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
tooltip.cpp
dataenginebindings.cpp
iconitem.cpp
)
INCLUDE_DIRECTORIES(

View File

@ -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<Plasma::QueryMatch *>("QueryMatch");
qmlRegisterType<QDeclarativePropertyMap>();
qmlRegisterType<IconItem>(uri, 0, 1, "IconItem");
/*qmlRegisterInterface<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!
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)

View File

@ -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

View File

@ -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"

View File

@ -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 {

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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 {

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
}
}
}
}