diff --git a/declarativeimports/core/CMakeLists.txt b/declarativeimports/core/CMakeLists.txt index bf4625c58..5c41fd0db 100644 --- a/declarativeimports/core/CMakeLists.txt +++ b/declarativeimports/core/CMakeLists.txt @@ -13,6 +13,7 @@ set(corebindings_SRCS dialog.cpp tooltip.cpp dataenginebindings.cpp + iconitem.cpp ) INCLUDE_DIRECTORIES( diff --git a/declarativeimports/core/corebindingsplugin.cpp b/declarativeimports/core/corebindingsplugin.cpp index 4f907a9b5..169fd35ee 100644 --- a/declarativeimports/core/corebindingsplugin.cpp +++ b/declarativeimports/core/corebindingsplugin.cpp @@ -38,6 +38,7 @@ #include "svgitem.h" #include "theme.h" #include "dialog.h" +#include "iconitem.h" #include "tooltip.h" #include "dataenginebindings_p.h" @@ -93,6 +94,7 @@ void CoreBindingsPlugin::registerTypes(const char *uri) qRegisterMetaType("QueryMatch"); qmlRegisterType(); + qmlRegisterType(uri, 0, 1, "IconItem"); /*qmlRegisterInterface("DataSource"); qRegisterMetaType("DataSource");*/ diff --git a/declarativeimports/core/iconitem.cpp b/declarativeimports/core/iconitem.cpp new file mode 100644 index 000000000..fdd371b4f --- /dev/null +++ b/declarativeimports/core/iconitem.cpp @@ -0,0 +1,325 @@ +/* + * Copyright 2012 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "iconitem.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +IconItem::IconItem(QDeclarativeItem *parent) + : QDeclarativeItem(parent), + m_svgIcon(0), + m_smooth(false), + m_active(false), + m_animValue(0) +{ + m_animation = new QPropertyAnimation(this); + connect(m_animation, SIGNAL(valueChanged(QVariant)), + this, SLOT(valueChanged(QVariant))); + connect(m_animation, SIGNAL(finished()), + this, SLOT(animationFinished())); + m_animation->setTargetObject(this); + m_animation->setEasingCurve(QEasingCurve::InOutQuad); + m_animation->setDuration(250); + + setFlag(QGraphicsItem::ItemHasNoContents, false); + + connect(KIconLoader::global(), SIGNAL(iconLoaderSettingsChanged()), + this, SLOT(implicitWidthChanged())); + connect(KIconLoader::global(), SIGNAL(iconLoaderSettingsChanged()), + this, SLOT(implicitHeightChanged())); + + + //initialize implicit size to the Dialog size + setImplicitWidth(KIconLoader::global()->currentSize(KIconLoader::Dialog)); + setImplicitHeight(KIconLoader::global()->currentSize(KIconLoader::Dialog)); +} + + +IconItem::~IconItem() +{ +} + +void IconItem::setSource(const QVariant &source) +{ + if (source == m_source) { + return; + } + + m_source = source; + + if (source.canConvert()) { + m_icon = source.value(); + m_imageIcon = QImage(); + m_pixmapIcon = QPixmap(); + delete m_svgIcon; + m_svgIcon = 0; + + } else if (source.canConvert()) { + m_svgIcon = new Plasma::Svg(this); + //try as a svg toolbar icon + m_svgIcon->setImagePath("toolbar-icons/" + source.toString().split("-").first()); + + //try as a svg normal icon (like systray) + if (!m_svgIcon->isValid()) { + m_svgIcon->setImagePath("icons/" + source.toString().split("-").first()); + } + m_svgIcon->setContainsMultipleImages(true); + + //success? + if (m_svgIcon->isValid()) { + m_icon = QIcon(); + + //ok, svg not available + } else { + m_icon = KIcon(source.toString()); + delete m_svgIcon; + m_svgIcon = 0; + } + + m_imageIcon = QImage(); + m_pixmapIcon = QPixmap(); + + } else if (source.canConvert()) { + m_icon = QIcon(); + m_imageIcon = QImage(); + m_pixmapIcon = source.value(); + delete m_svgIcon; + m_svgIcon = 0; + + } else if (source.canConvert()) { + m_icon = QIcon(); + m_imageIcon = source.value(); + m_pixmapIcon = QPixmap(); + delete m_svgIcon; + m_svgIcon = 0; + + } else { + m_icon = QIcon(); + m_imageIcon = QImage(); + m_pixmapIcon = QPixmap(); + delete m_svgIcon; + m_svgIcon = 0; + } + + loadPixmap(); + + emit sourceChanged(); + emit validChanged(); +} + +QVariant IconItem::source() const +{ + return m_source; +} + +bool IconItem::isActive() const +{ + return m_active; +} + +void IconItem::setActive(bool active) +{ + if (m_active == active) { + return; + } + + m_active = active; + loadPixmap(); + emit activeChanged(); +} + +void IconItem::setImplicitWidth(qreal width) +{ + if (implicitWidth() == width) { + return; + } + + QDeclarativeItem::setImplicitWidth(width); + + emit implicitWidthChanged(); +} + +qreal IconItem::implicitWidth() const +{ + return QDeclarativeItem::implicitWidth(); +} + +void IconItem::setImplicitHeight(qreal height) +{ + if (implicitHeight() == height) { + return; + } + + QDeclarativeItem::setImplicitHeight(height); + + emit implicitHeightChanged(); +} + +qreal IconItem::implicitHeight() const +{ + return QDeclarativeItem::implicitHeight(); +} + +void IconItem::setSmooth(const bool smooth) +{ + if (smooth == m_smooth) { + return; + } + m_smooth = smooth; + update(); +} + +bool IconItem::smooth() const +{ + return m_smooth; +} + +bool IconItem::isValid() const +{ + return !m_iconPixmaps.isEmpty(); +} + +void IconItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) +{ + Q_UNUSED(option); + Q_UNUSED(widget); + + if (m_iconPixmaps.isEmpty()) { + return; + } + painter->save(); + painter->setRenderHint(QPainter::Antialiasing, m_smooth); + painter->setRenderHint(QPainter::SmoothPixmapTransform, m_smooth); + + const QRect destRect(QPointF(boundingRect().center() - QPointF(m_iconPixmaps.first().width()/2, m_iconPixmaps.first().height()/2)).toPoint(), + m_iconPixmaps.first().size()); + + if (m_animation->state() == QAbstractAnimation::Running) { + QPixmap result = m_iconPixmaps.first(); + result = Plasma::PaintUtils::transition(result, + m_iconPixmaps.last(), m_animValue); + painter->drawPixmap(destRect, result); + //simpler logic for just paint + } else { + painter->drawPixmap(destRect, m_iconPixmaps.first()); + } + + painter->restore(); +} + +void IconItem::animationFinished() +{ + while (m_iconPixmaps.count() > 1) { + m_iconPixmaps.pop_front(); + } +} + +void IconItem::valueChanged(const QVariant &value) +{ + m_animValue = value.toReal(); + update(); +} + +void IconItem::loadPixmap() +{ + int size = qMin(width(), height()); + + + //FIXME: Heuristic: allow 24x24 for icons/ that are in the systray(ugly) + if (m_svgIcon && m_svgIcon->imagePath().contains("icons/") && + size > KIconLoader::SizeSmallMedium && + size < KIconLoader::SizeMedium) { + size = 24; + + //if size is less than 16, leave as is + } else if (size < KIconLoader::SizeSmall) { + //do nothing + } else if (size < KIconLoader::SizeSmallMedium) { + size = KIconLoader::SizeSmall; + } else if (size < KIconLoader::SizeMedium) { + size = KIconLoader::SizeSmallMedium; + } else if (size < KIconLoader::SizeLarge) { + size = KIconLoader::SizeMedium; + } else if (size < KIconLoader::SizeHuge) { + size = KIconLoader::SizeLarge; + //if size is more than 64, leave as is + } + + + + //final pixmap to paint + QPixmap result; + if (m_svgIcon) { + m_svgIcon->resize(size, size); + result = m_svgIcon->pixmap(m_source.toString()); + } else if (!m_source.isNull()) { + result = m_icon.pixmap(QSize(size, size)); + } else if (!m_pixmapIcon.isNull()) { + result = m_pixmapIcon; + } else if (!m_imageIcon.isNull()) { + result = QPixmap::fromImage(m_imageIcon); + } else { + m_iconPixmaps.clear(); + return; + } + + if (!isEnabled()) { + result = KIconLoader::global()->iconEffect()->apply(result, KIconLoader::Desktop, KIconLoader::DisabledState); + } else if (m_active) { + result = KIconLoader::global()->iconEffect()->apply(result, KIconLoader::Desktop, KIconLoader::ActiveState); + } + + //this happen only when loadPixmap has been called when an anim is running + while (m_iconPixmaps.count() > 1) { + m_iconPixmaps.pop_front(); + } + + m_iconPixmaps << result; + //if there is only one image, don't animate + //if an animation was already running, immediate transition, to not overload + if (m_animation->state() == QAbstractAnimation::Running) { + m_animation->stop(); + } else if (m_iconPixmaps.count() > 1) { + m_animation->setStartValue((qreal)0); + m_animation->setEndValue((qreal)1); + m_animation->start(); + } + update(); +} + +void IconItem::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + if (newGeometry.size() != oldGeometry.size()) { + m_iconPixmaps.clear(); + loadPixmap(); + } + + QDeclarativeItem::geometryChanged(newGeometry, oldGeometry); +} + +#include "iconitem.moc" diff --git a/declarativeimports/core/iconitem.h b/declarativeimports/core/iconitem.h new file mode 100644 index 000000000..b5c14b078 --- /dev/null +++ b/declarativeimports/core/iconitem.h @@ -0,0 +1,107 @@ +/* + * Copyright 2012 Marco Martin + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as + * published by the Free Software Foundation; either version 2, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details + * + * You should have received a copy of the GNU Library General Public + * License along with this program; if not, write to the + * Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef ICONITEM_H +#define ICONITEM_H + +#include +#include +#include + +class QPropertyAnimation; + +namespace Plasma { + class Svg; +} + +class IconItem : public QDeclarativeItem +{ + Q_OBJECT + + Q_PROPERTY(QVariant source READ source WRITE setSource NOTIFY sourceChanged) + Q_PROPERTY(bool smooth READ smooth WRITE setSmooth NOTIFY smoothChanged) + Q_PROPERTY(qreal implicitWidth READ implicitWidth WRITE setImplicitWidth NOTIFY implicitWidthChanged) + Q_PROPERTY(qreal implicitHeight READ implicitHeight WRITE setImplicitHeight NOTIFY implicitHeightChanged) + Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged) + Q_PROPERTY(bool valid READ isValid NOTIFY validChanged) + +public: + + IconItem(QDeclarativeItem *parent=0); + ~IconItem(); + + void setSource(const QVariant &source); + QVariant source() const; + + bool isActive() const; + void setActive(bool active); + + void setImplicitWidth(qreal width); + qreal implicitWidth() const; + void setImplicitHeight(qreal height); + qreal implicitHeight() const; + + void setSmooth(const bool smooth); + bool smooth() const; + + bool isValid() const; + + void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + + void geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry); + +Q_SIGNALS: + void activeChanged(); + void sourceChanged(); + void smoothChanged(); + void validChanged(); + void implicitWidthChanged(); + void implicitHeightChanged(); + +private Q_SLOTS: + void animationFinished(); + void valueChanged(const QVariant &value); + +private: + void loadPixmap(); + + //all the ways we can set an source. Only one of them will be valid + QIcon m_icon; + Plasma::Svg *m_svgIcon; + QPixmap m_pixmapIcon; + QImage m_imageIcon; + //this contains the raw variant it was passed + QVariant m_source; + + QSizeF m_implicitSize; + + bool m_smooth; + bool m_active; + + //This list contains at most 2 sources, when a pixmap transition is due, + //a new pixmap is queued, the old one is removed when the animation finishes + QList m_iconPixmaps; + + //animation on pixmap change + QPropertyAnimation *m_animation; + qreal m_animValue; +}; + +#endif diff --git a/declarativeimports/plasmacomponents/CMakeLists.txt b/declarativeimports/plasmacomponents/CMakeLists.txt index 589c0471f..249b02607 100644 --- a/declarativeimports/plasmacomponents/CMakeLists.txt +++ b/declarativeimports/plasmacomponents/CMakeLists.txt @@ -65,7 +65,6 @@ install(FILES qml/ToolButton.qml DESTINATION ${PLUGIN_INSTALL_DIR}/platformimpor #Now install the private stuff! install(FILES qml/private/DualStateButton.qml DESTINATION ${PLUGIN_INSTALL_DIR}/platformimports/touch/org/kde/plasma/components/private) -install(FILES qml/private/IconLoader.qml DESTINATION ${PLUGIN_INSTALL_DIR}/platformimports/touch/org/kde/plasma/components/private) install(FILES qml/private/PageStack.js DESTINATION ${PLUGIN_INSTALL_DIR}/platformimports/touch/org/kde/plasma/components/private) install(FILES qml/private/TabGroup.js DESTINATION ${PLUGIN_INSTALL_DIR}/platformimports/touch/org/kde/plasma/components/private) install(FILES qml/private/ScrollBarDelegate.qml DESTINATION ${PLUGIN_INSTALL_DIR}/platformimports/touch/org/kde/plasma/components/private) diff --git a/declarativeimports/plasmacomponents/platformcomponents/touch/MenuItem.qml b/declarativeimports/plasmacomponents/platformcomponents/touch/MenuItem.qml index 5720d072e..0ec88eda1 100644 --- a/declarativeimports/plasmacomponents/platformcomponents/touch/MenuItem.qml +++ b/declarativeimports/plasmacomponents/platformcomponents/touch/MenuItem.qml @@ -40,7 +40,6 @@ import QtQuick 1.1 import org.kde.plasma.core 0.1 as PlasmaCore -import "private" as Private Item { id: root @@ -52,7 +51,7 @@ Item { property bool checkable: false property alias font: textArea.font - implicitWidth: textArea.paintedWidth + iconLoader.width*2 + 6 + implicitWidth: textArea.paintedWidth + iconItem.width*2 + 6 implicitHeight: Math.max(theme.smallIconSize, textArea.paintedHeight + 6) width: Math.max(implicitWidth, parent.width) @@ -64,12 +63,12 @@ Item { internal.separatorItem.destroy() } } - property alias icon: iconLoader.source + property alias icon: iconItem.source enabled: !separator - Private.IconLoader { - id: iconLoader + PlasmaCore.IconItem { + id: iconItem width: parent.height anchors { verticalCenter: parent.verticalCenter diff --git a/declarativeimports/plasmacomponents/platformcomponents/touch/TextField.qml b/declarativeimports/plasmacomponents/platformcomponents/touch/TextField.qml index 3a1c3f77b..d5d1e8450 100644 --- a/declarativeimports/plasmacomponents/platformcomponents/touch/TextField.qml +++ b/declarativeimports/plasmacomponents/platformcomponents/touch/TextField.qml @@ -21,7 +21,6 @@ import QtQuick 1.1 import org.kde.plasma.core 0.1 as PlasmaCore import org.kde.qtextracomponents 0.1 import "EditBubble.js" as EditBubbleHelper -import "private" as Private Item { id: textField @@ -172,7 +171,7 @@ Item { } - Private.IconLoader { + PlasmaCore.IconItem { parent: mouseEventListener // reparent to the MouseFilter for MouseArea to work id: clearButton source: "edit-clear-locationbar-rtl" diff --git a/declarativeimports/plasmacomponents/qml/Button.qml b/declarativeimports/plasmacomponents/qml/Button.qml index 4bdd9a2c6..2d5277c76 100644 --- a/declarativeimports/plasmacomponents/qml/Button.qml +++ b/declarativeimports/plasmacomponents/qml/Button.qml @@ -223,7 +223,7 @@ Item { bottomMargin: surfaceNormal.margins.bottom } - Private.IconLoader { + PlasmaCore.IconItem { id: icon anchors { @@ -231,8 +231,9 @@ Item { left: label.text.length > 0 ? parent.left : undefined horizontalCenter: label.text.length > 0 ? undefined : parent.horizontalCenter } - height: roundToStandardSize(parent.height) - width: height + active: shadow.hasOverState && mouse.containsMouse + height: parent.height + width: parent.height } Text { diff --git a/declarativeimports/plasmacomponents/qml/CommonDialog.qml b/declarativeimports/plasmacomponents/qml/CommonDialog.qml index 1db6fa84b..048405929 100644 --- a/declarativeimports/plasmacomponents/qml/CommonDialog.qml +++ b/declarativeimports/plasmacomponents/qml/CommonDialog.qml @@ -42,7 +42,6 @@ import QtQuick 1.1 import org.kde.plasma.core 0.1 as PlasmaCore -import "private" as Private import "." 0.1 @@ -163,7 +162,7 @@ Dialog { } } - Private.IconLoader { + PlasmaCore.IconItem { id: titleAreaIcon width: theme.iconSizeSmall height: theme.iconSizeSmall diff --git a/declarativeimports/plasmacomponents/qml/TabButton.qml b/declarativeimports/plasmacomponents/qml/TabButton.qml index 8d0e1ca0f..68cb69df9 100644 --- a/declarativeimports/plasmacomponents/qml/TabButton.qml +++ b/declarativeimports/plasmacomponents/qml/TabButton.qml @@ -76,7 +76,7 @@ Signals: import QtQuick 1.1 import "private/AppManager.js" as Utils -import "private" as Private +import org.kde.plasma.core 0.1 as PlasmaCore Item { id: root @@ -154,7 +154,7 @@ Item { color: root.checked ? theme.buttonTextColor : theme.textColor } - Private.IconLoader { + PlasmaCore.IconItem { id: imageLoader implicitWidth: internal.portrait ? Math.max(theme.smallIconSize, root.height - (label.text ? label.height : 0)) : Math.max(theme.smallIconSize, root.height) diff --git a/declarativeimports/plasmacomponents/qml/TextField.qml b/declarativeimports/plasmacomponents/qml/TextField.qml index 148ddc8a0..d19e7d886 100644 --- a/declarativeimports/plasmacomponents/qml/TextField.qml +++ b/declarativeimports/plasmacomponents/qml/TextField.qml @@ -314,7 +314,7 @@ FocusScope { Keys.forwardTo: textField } - Private.IconLoader { + PlasmaCore.IconItem { id: clearButton source: "edit-clear-locationbar-rtl" height: Math.max(textInput.height, theme.smallIconSize) diff --git a/declarativeimports/plasmacomponents/qml/ToolButton.qml b/declarativeimports/plasmacomponents/qml/ToolButton.qml index ffb25bd5f..594067d88 100644 --- a/declarativeimports/plasmacomponents/qml/ToolButton.qml +++ b/declarativeimports/plasmacomponents/qml/ToolButton.qml @@ -351,7 +351,7 @@ Item { bottomMargin: delegate.margins.bottom } - Private.IconLoader { + PlasmaCore.IconItem { id: icon anchors { @@ -359,8 +359,9 @@ Item { left: label.text ? parent.left : undefined horizontalCenter: label.text ? undefined : parent.horizontalCenter } - height: roundToStandardSize(parent.height) - width: height + height: parent.height + width: parent.height + active: delegate.item.hasOverState && mouse.containsMouse } Text { diff --git a/declarativeimports/plasmacomponents/qml/private/IconLoader.qml b/declarativeimports/plasmacomponents/qml/private/IconLoader.qml deleted file mode 100644 index 422a38434..000000000 --- a/declarativeimports/plasmacomponents/qml/private/IconLoader.qml +++ /dev/null @@ -1,140 +0,0 @@ -/* -* Copyright (C) 2011 by Marco MArtin -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU Library General Public License as -* published by the Free Software Foundation; either version 2, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU Library General Public License for more details -* -* You should have received a copy of the GNU Library General Public -* License along with this program; if not, write to the -* Free Software Foundation, Inc., -* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ - -/**Documented API -Inherits: - Item - -Imports: - QtQuick 1.1 - org.kde.plasma.core - org.kde.qtextracomponents - -Description: - TODO i need more info here - -Properties: - bool valid: - Returns if the icon is valid or not. - - string source: - Returns the dir,in which the icon exists. -**/ - -import QtQuick 1.1 -import org.kde.plasma.core 0.1 as PlasmaCore -import org.kde.qtextracomponents 0.1 - -Item { - id: root - - property bool valid: false - - property variant source - - onSourceChanged: { - //is it a qicon? - if (typeof source != "string") { - imageLoader.sourceComponent = iconComponent - valid = true - return - } else if (source == "") { - imageLoader.sourceComponent = null - valid = false - return - } - - svgIcon.imagePath = "toolbar-icons/"+root.source.split("-")[0] - if (!svgIcon.isValid() || !svgIcon.hasElement(root.source)) { - svgIcon.imagePath = "icons/"+root.source.split("-")[0] - } - - if (svgIcon.isValid() && svgIcon.hasElement(root.source)) { - imageLoader.sourceComponent = svgComponent - } else if ((root.source.indexOf(".") == -1 && root.source.indexOf(":") == -1)) { - imageLoader.sourceComponent = iconComponent - } else { - imageLoader.sourceComponent = imageComponent - } - valid = true - } - - implicitWidth: theme.smallIconSize - implicitHeight: theme.smallIconSize - - PlasmaCore.Svg { - id: svgIcon - } - - function roundToStandardSize(size) - { - if (size >= theme.enormousIconSize) { - return theme.enormousIconSize - } else if (size >= theme.hugeIconSize) { - return theme.hugeIconSize - } else if (size >= theme.largeIconSize) { - return theme.largeIconSize - } else if (size >= theme.mediumIconSize) { - return theme.mediumIconSize - } else if (size >= theme.smallMediumIconSize) { - return theme.smallMediumIconSize - } else { - return theme.smallIconSize - } - } - - Loader { - id: imageLoader - anchors.fill: parent - - Component { - id: svgComponent - - PlasmaCore.SvgItem { - svg: svgIcon - elementId: root.source - anchors.fill: parent - smooth: true - } - } - - Component { - id: iconComponent - - QIconItem { - icon: (typeof source == "string") ? QIcon(root.source) : root.source - smooth: true - anchors.fill: parent - } - } - - Component { - id: imageComponent - - Image { - source: root.source - sourceSize.width: width - sourceSize.height: height - fillMode: Image.PreserveAspectFit - smooth: true - anchors.fill: parent - } - } - } -}