PlasmaCore.IconItem

remove the old private IconLoader
this takes care of rendering at the proper size, using a Svg when needed and animation when
This commit is contained in:
Marco Martin 2012-11-26 20:49:16 +01:00
parent b353f6e7fb
commit ed4504f731
13 changed files with 452 additions and 159 deletions

View File

@ -13,6 +13,7 @@ set(corebindings_SRCS
dialog.cpp dialog.cpp
tooltip.cpp tooltip.cpp
dataenginebindings.cpp dataenginebindings.cpp
iconitem.cpp
) )
INCLUDE_DIRECTORIES( INCLUDE_DIRECTORIES(

View File

@ -38,6 +38,7 @@
#include "svgitem.h" #include "svgitem.h"
#include "theme.h" #include "theme.h"
#include "dialog.h" #include "dialog.h"
#include "iconitem.h"
#include "tooltip.h" #include "tooltip.h"
#include "dataenginebindings_p.h" #include "dataenginebindings_p.h"
@ -93,6 +94,7 @@ void CoreBindingsPlugin::registerTypes(const char *uri)
qRegisterMetaType<Plasma::QueryMatch *>("QueryMatch"); qRegisterMetaType<Plasma::QueryMatch *>("QueryMatch");
qmlRegisterType<QDeclarativePropertyMap>(); qmlRegisterType<QDeclarativePropertyMap>();
qmlRegisterType<IconItem>(uri, 0, 1, "IconItem");
/*qmlRegisterInterface<Plasma::DataSource>("DataSource"); /*qmlRegisterInterface<Plasma::DataSource>("DataSource");
qRegisterMetaType<Plasma::DataSource*>("DataSource");*/ qRegisterMetaType<Plasma::DataSource*>("DataSource");*/

View File

@ -0,0 +1,325 @@
/*
* Copyright 2012 Marco Martin <mart@kde.org>
*
* 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 <KIcon>
#include <KIconLoader>
#include <KIconEffect>
#include <QPainter>
#include <QPropertyAnimation>
#include <QStyleOptionGraphicsItem>
#include <Plasma/PaintUtils>
#include <Plasma/Svg>
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<QIcon>()) {
m_icon = source.value<QIcon>();
m_imageIcon = QImage();
m_pixmapIcon = QPixmap();
delete m_svgIcon;
m_svgIcon = 0;
} else if (source.canConvert<QString>()) {
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<QPixmap>()) {
m_icon = QIcon();
m_imageIcon = QImage();
m_pixmapIcon = source.value<QPixmap>();
delete m_svgIcon;
m_svgIcon = 0;
} else if (source.canConvert<QImage>()) {
m_icon = QIcon();
m_imageIcon = source.value<QImage>();
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"

View File

@ -0,0 +1,107 @@
/*
* Copyright 2012 Marco Martin <mart@kde.org>
*
* 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 <QDeclarativeItem>
#include <QPixmap>
#include <QVariant>
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<QPixmap> m_iconPixmaps;
//animation on pixmap change
QPropertyAnimation *m_animation;
qreal m_animValue;
};
#endif

View File

@ -65,7 +65,6 @@ install(FILES qml/ToolButton.qml DESTINATION ${PLUGIN_INSTALL_DIR}/platformimpor
#Now install the private stuff! #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/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/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/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) install(FILES qml/private/ScrollBarDelegate.qml DESTINATION ${PLUGIN_INSTALL_DIR}/platformimports/touch/org/kde/plasma/components/private)

View File

@ -40,7 +40,6 @@
import QtQuick 1.1 import QtQuick 1.1
import org.kde.plasma.core 0.1 as PlasmaCore import org.kde.plasma.core 0.1 as PlasmaCore
import "private" as Private
Item { Item {
id: root id: root
@ -52,7 +51,7 @@ Item {
property bool checkable: false property bool checkable: false
property alias font: textArea.font 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) implicitHeight: Math.max(theme.smallIconSize, textArea.paintedHeight + 6)
width: Math.max(implicitWidth, parent.width) width: Math.max(implicitWidth, parent.width)
@ -64,12 +63,12 @@ Item {
internal.separatorItem.destroy() internal.separatorItem.destroy()
} }
} }
property alias icon: iconLoader.source property alias icon: iconItem.source
enabled: !separator enabled: !separator
Private.IconLoader { PlasmaCore.IconItem {
id: iconLoader id: iconItem
width: parent.height width: parent.height
anchors { anchors {
verticalCenter: parent.verticalCenter verticalCenter: parent.verticalCenter

View File

@ -21,7 +21,6 @@ import QtQuick 1.1
import org.kde.plasma.core 0.1 as PlasmaCore import org.kde.plasma.core 0.1 as PlasmaCore
import org.kde.qtextracomponents 0.1 import org.kde.qtextracomponents 0.1
import "EditBubble.js" as EditBubbleHelper import "EditBubble.js" as EditBubbleHelper
import "private" as Private
Item { Item {
id: textField id: textField
@ -172,7 +171,7 @@ Item {
} }
Private.IconLoader { PlasmaCore.IconItem {
parent: mouseEventListener // reparent to the MouseFilter for MouseArea to work parent: mouseEventListener // reparent to the MouseFilter for MouseArea to work
id: clearButton id: clearButton
source: "edit-clear-locationbar-rtl" source: "edit-clear-locationbar-rtl"

View File

@ -223,7 +223,7 @@ Item {
bottomMargin: surfaceNormal.margins.bottom bottomMargin: surfaceNormal.margins.bottom
} }
Private.IconLoader { PlasmaCore.IconItem {
id: icon id: icon
anchors { anchors {
@ -231,8 +231,9 @@ Item {
left: label.text.length > 0 ? parent.left : undefined left: label.text.length > 0 ? parent.left : undefined
horizontalCenter: label.text.length > 0 ? undefined : parent.horizontalCenter horizontalCenter: label.text.length > 0 ? undefined : parent.horizontalCenter
} }
height: roundToStandardSize(parent.height) active: shadow.hasOverState && mouse.containsMouse
width: height height: parent.height
width: parent.height
} }
Text { Text {

View File

@ -42,7 +42,6 @@
import QtQuick 1.1 import QtQuick 1.1
import org.kde.plasma.core 0.1 as PlasmaCore import org.kde.plasma.core 0.1 as PlasmaCore
import "private" as Private
import "." 0.1 import "." 0.1
@ -163,7 +162,7 @@ Dialog {
} }
} }
Private.IconLoader { PlasmaCore.IconItem {
id: titleAreaIcon id: titleAreaIcon
width: theme.iconSizeSmall width: theme.iconSizeSmall
height: theme.iconSizeSmall height: theme.iconSizeSmall

View File

@ -76,7 +76,7 @@ Signals:
import QtQuick 1.1 import QtQuick 1.1
import "private/AppManager.js" as Utils import "private/AppManager.js" as Utils
import "private" as Private import org.kde.plasma.core 0.1 as PlasmaCore
Item { Item {
id: root id: root
@ -154,7 +154,7 @@ Item {
color: root.checked ? theme.buttonTextColor : theme.textColor color: root.checked ? theme.buttonTextColor : theme.textColor
} }
Private.IconLoader { PlasmaCore.IconItem {
id: imageLoader id: imageLoader
implicitWidth: internal.portrait ? Math.max(theme.smallIconSize, root.height - (label.text ? label.height : 0)) : Math.max(theme.smallIconSize, root.height) implicitWidth: internal.portrait ? Math.max(theme.smallIconSize, root.height - (label.text ? label.height : 0)) : Math.max(theme.smallIconSize, root.height)

View File

@ -314,7 +314,7 @@ FocusScope {
Keys.forwardTo: textField Keys.forwardTo: textField
} }
Private.IconLoader { PlasmaCore.IconItem {
id: clearButton id: clearButton
source: "edit-clear-locationbar-rtl" source: "edit-clear-locationbar-rtl"
height: Math.max(textInput.height, theme.smallIconSize) height: Math.max(textInput.height, theme.smallIconSize)

View File

@ -351,7 +351,7 @@ Item {
bottomMargin: delegate.margins.bottom bottomMargin: delegate.margins.bottom
} }
Private.IconLoader { PlasmaCore.IconItem {
id: icon id: icon
anchors { anchors {
@ -359,8 +359,9 @@ Item {
left: label.text ? parent.left : undefined left: label.text ? parent.left : undefined
horizontalCenter: label.text ? undefined : parent.horizontalCenter horizontalCenter: label.text ? undefined : parent.horizontalCenter
} }
height: roundToStandardSize(parent.height) height: parent.height
width: height width: parent.height
active: delegate.item.hasOverState && mouse.containsMouse
} }
Text { Text {

View File

@ -1,140 +0,0 @@
/*
* Copyright (C) 2011 by Marco MArtin <mart@kde.org>
*
* 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
}
}
}
}