diff --git a/src/declarativeimports/core/iconitem.cpp b/src/declarativeimports/core/iconitem.cpp index 9f345fe3b..3b5049aef 100644 --- a/src/declarativeimports/core/iconitem.cpp +++ b/src/declarativeimports/core/iconitem.cpp @@ -1,707 +1,815 @@ /* * Copyright 2012 Marco Martin * Copyright 2014 David Edmundson * * 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 #include #include #include #include "fadingnode_p.h" #include #include "theme.h" #include "units.h" +class IconItemSource +{ +public: + explicit IconItemSource(IconItem *iconItem) : m_iconItem(iconItem) {} + virtual ~IconItemSource() {} + + virtual bool isValid() const = 0; + virtual const QSize size() const = 0; + virtual QPixmap pixmap(const QSize &size) = 0; + +protected: + QQuickWindow *window() + { + return m_iconItem->window(); + } + +private: + IconItem *m_iconItem; +}; + +class NullSource : public IconItemSource +{ +public: + explicit NullSource(IconItem *iconItem) : IconItemSource(iconItem) {} + + bool isValid() const override + { + return false; + } + + const QSize size() const override + { + return QSize(); + } + + QPixmap pixmap(const QSize &size) override + { + Q_UNUSED(size) + return QPixmap(); + } +}; + +class QIconSource : public IconItemSource +{ +public: + explicit QIconSource(const QIcon &icon, IconItem *iconItem) : IconItemSource(iconItem) + { + m_icon = icon; + } + + bool isValid() const override + { + return !m_icon.isNull(); + } + + const QSize size() const override + { + return QSize(); + } + + QPixmap pixmap(const QSize &size) override + { + KIconLoader::global()->setCustomPalette(Plasma::Theme().palette()); + QPixmap result = m_icon.pixmap(window(), size); + KIconLoader::global()->resetPalette(); + return result; + } + +private: + QIcon m_icon; +}; + +class QImageSource : public IconItemSource +{ +public: + explicit QImageSource(const QImage &imageIcon, IconItem *iconItem) : IconItemSource(iconItem) + { + m_imageIcon = imageIcon; + } + + bool isValid() const override + { + return !m_imageIcon.isNull(); + } + + const QSize size() const override + { + const QSize s = m_imageIcon.size(); + if (s.isValid()) { + return s; + } + + return QSize(); + } + + QPixmap pixmap(const QSize &size) override + { + Q_UNUSED(size) + return QPixmap::fromImage(m_imageIcon); + } + +private: + QImage m_imageIcon; +}; + +class SvgSource : public IconItemSource +{ +public: + explicit SvgSource(const QString &sourceString, IconItem* iconItem) : IconItemSource(iconItem) + { + m_svgIcon = new Plasma::Svg(iconItem); + m_svgIcon->setColorGroup(iconItem->colorGroup()); + m_svgIcon->setStatus(iconItem->status()); + m_svgIcon->setDevicePixelRatio(devicePixelRatio()); + QObject::connect(m_svgIcon, &Plasma::Svg::repaintNeeded, iconItem, &IconItem::schedulePixmapUpdate); + QObject::connect(iconItem, &IconItem::statusChanged, m_svgIcon, [=] { + if (m_svgIcon) { + m_svgIcon->setStatus(iconItem->status()); + } + }); + QObject::connect(iconItem, &IconItem::colorGroupChanged, m_svgIcon, [=] { + if (m_svgIcon) { + m_svgIcon->setColorGroup(iconItem->colorGroup()); + } + }); + + if (iconItem->usesPlasmaTheme()) { + //try as a svg icon from plasma theme + m_svgIcon->setImagePath(QLatin1String("icons/") + sourceString.section(QLatin1Char('-'), 0, 0)); + m_svgIcon->setContainsMultipleImages(true); + } + + //success? + if (iconItem->usesPlasmaTheme() && m_svgIcon->isValid() && m_svgIcon->hasElement(sourceString)) { + m_svgIconName = sourceString; + //ok, svg not available from the plasma theme + } else { + //try to load from iconloader an svg with Plasma::Svg + const auto *iconTheme = KIconLoader::global()->theme(); + QString iconPath; + if (iconTheme) { + iconPath = iconTheme->iconPath(sourceString + QLatin1String(".svg"), qMin(iconItem->width(), iconItem->height()), KIconLoader::MatchBest); + if (iconPath.isEmpty()) { + iconPath = iconTheme->iconPath(sourceString + QLatin1String(".svgz"), qMin(iconItem->width(), iconItem->height()), KIconLoader::MatchBest); + } + } else { + qWarning() << "KIconLoader has no theme set"; + } + + if (!iconPath.isEmpty()) { + m_svgIcon->setImagePath(iconPath); + m_svgIconName = sourceString; + } else { + //fail, cleanup + delete m_svgIcon; + } + } + } + + bool isValid() const override + { + return m_svgIcon; + } + + const QSize size() const override + { + QSize s; + if (m_svgIcon) { // FIXME: Check Svg::isValid()? Considered expensive by apidox. + //resize() resets the icon to its implicit size, specified + m_svgIcon->resize(); + + //plasma theme icon, where one file contains multiple images + if (m_svgIcon->hasElement(m_svgIconName)) { + s = m_svgIcon->elementSize(m_svgIconName); + //normal icon: one image per file, page size is icon size + } else { + s = m_svgIcon->size(); + } + } + return s; + } + + QPixmap pixmap(const QSize &size) override + { + m_svgIcon->setDevicePixelRatio(devicePixelRatio()); + m_svgIcon->resize(size); + if (!m_svgIconName.isEmpty() && m_svgIcon->hasElement(m_svgIconName)) { + return m_svgIcon->pixmap(m_svgIconName); + } else if (!m_svgIconName.isEmpty()) { + const auto *iconTheme = KIconLoader::global()->theme(); + if (iconTheme) { + QString iconPath = iconTheme->iconPath(m_svgIconName + QLatin1String(".svg"), size.width(), KIconLoader::MatchBest); + if (iconPath.isEmpty()) { + iconPath = iconTheme->iconPath(m_svgIconName + QLatin1String(".svgz"), size.width(), KIconLoader::MatchBest); + } + + if (!iconPath.isEmpty()) { + m_svgIcon->setImagePath(iconPath); + } + } else { + qWarning() << "KIconLoader has no theme set"; + } + + return m_svgIcon->pixmap(); + } + + return QPixmap(); + } + +private: + qreal devicePixelRatio() + { + return window() ? window()->devicePixelRatio() : qApp->devicePixelRatio(); + } + + QPointer m_svgIcon; + QString m_svgIconName; +}; + IconItem::IconItem(QQuickItem *parent) : QQuickItem(parent), - m_svgIcon(nullptr), + m_iconItemSource(new NullSource(this)), m_status(Plasma::Svg::Normal), m_active(false), m_animated(true), m_usesPlasmaTheme(true), m_roundToIconSize(true), m_textureChanged(false), m_sizeChanged(false), m_allowNextAnimation(false), m_blockNextAnimation(false), m_implicitHeightSetByUser(false), m_implicitWidthSetByUser(false), m_colorGroup(Plasma::Theme::NormalColorGroup), m_animValue(0) { m_animation = new QPropertyAnimation(this); connect(m_animation, &QPropertyAnimation::valueChanged, this, &IconItem::valueChanged); connect(m_animation, &QPropertyAnimation::finished, this, &IconItem::animationFinished); m_animation->setTargetObject(this); m_animation->setEasingCurve(QEasingCurve::InOutQuad); m_animation->setDuration(250); //FIXME from theme setFlag(ItemHasContents, true); connect(KIconLoader::global(), &KIconLoader::iconLoaderSettingsChanged, this, &IconItem::updateImplicitSize); connect(this, &IconItem::implicitWidthChanged, this, &IconItem::implicitWidthChanged2); connect(this, &IconItem::implicitHeightChanged, this, &IconItem::implicitHeightChanged2); updateImplicitSize(); } IconItem::~IconItem() { } void IconItem::updateImplicitSize() { - if (!m_imageIcon.isNull()) { - const QSize &s = m_imageIcon.size(); - - if (s.isValid()) { - if (!m_implicitWidthSetByUser && !m_implicitHeightSetByUser) { - setImplicitSize(s.width(), s.height()); - } else if (!m_implicitWidthSetByUser) { - setImplicitWidth(s.width()); - } else if (!m_implicitHeightSetByUser) { - setImplicitHeight(s.height()); - } + if (m_iconItemSource->isValid()) { + const QSize s = m_iconItemSource->size(); - return; - } - } else if (m_svgIcon) { // FIXME: Check Svg::isValid()? Considered expensive by apidox. - //resize() resets the icon to its implicit size, specified - m_svgIcon->resize(); - QSize s; - const QString &sourceString = m_source.toString(); - //plasma theme icon, where one file contains multiple images - if (m_svgIcon->hasElement(sourceString)) { - s = m_svgIcon->elementSize(sourceString); - //normal icon: one image per file, page size is icon size - } else { - s = m_svgIcon->size(); - } if (s.isValid()) { if (!m_implicitWidthSetByUser && !m_implicitHeightSetByUser) { setImplicitSize(s.width(), s.height()); } else if (!m_implicitWidthSetByUser) { setImplicitWidth(s.width()); } else if (!m_implicitHeightSetByUser) { setImplicitHeight(s.height()); } return; } } // Fall back to initializing implicit size to the Dialog size. const int implicitSize = KIconLoader::global()->currentSize(KIconLoader::Dialog); if (!m_implicitWidthSetByUser && !m_implicitHeightSetByUser) { setImplicitSize(implicitSize, implicitSize); } else if (!m_implicitWidthSetByUser) { setImplicitWidth(implicitSize); } else if (!m_implicitHeightSetByUser) { setImplicitHeight(implicitSize); } } void IconItem::setSource(const QVariant &source) { if (source == m_source) { return; } disconnect(KIconLoader::global(), &KIconLoader::iconChanged, this, &IconItem::iconLoaderIconChanged); const bool oldValid = isValid(); m_source = source; QString sourceString = source.toString(); // If the QIcon was created with QIcon::fromTheme(), try to load it as svg if (source.canConvert() && !source.value().name().isEmpty()) { sourceString = source.value().name(); } if (!sourceString.isEmpty()) { // If a file:// URL or a absolute path is passed, take the image pointed by that from disk QString localFile; if (sourceString.startsWith(QLatin1String("file:"))) { localFile = QUrl(sourceString).toLocalFile(); } else if (sourceString.startsWith(QLatin1Char('/'))) { localFile = sourceString; } if (!localFile.isEmpty()) { if (sourceString.endsWith(QLatin1String(".svg")) || sourceString.endsWith(QLatin1String(".svgz"))) { - m_icon = QIcon(localFile); - m_imageIcon = QImage(); + QIcon icon = QIcon(localFile); + m_iconItemSource.reset(new QIconSource(icon, this)); } else { - m_icon = QIcon(); - m_imageIcon = QImage(localFile); + QImage imageIcon = QImage(localFile); + m_iconItemSource.reset(new QImageSource(imageIcon, this)); } - m_svgIconName.clear(); - delete m_svgIcon; - m_svgIcon = nullptr; } else { - if (!m_svgIcon) { - m_svgIcon = new Plasma::Svg(this); - m_svgIcon->setColorGroup(m_colorGroup); - m_svgIcon->setStatus(m_status); - m_svgIcon->setDevicePixelRatio((window() ? window()->devicePixelRatio() : qApp->devicePixelRatio())); - connect(m_svgIcon, &Plasma::Svg::repaintNeeded, this, &IconItem::schedulePixmapUpdate); - } + m_iconItemSource.reset(new SvgSource(sourceString, this)); - if (m_usesPlasmaTheme) { - //try as a svg icon from plasma theme - m_svgIcon->setImagePath(QLatin1String("icons/") + sourceString.section(QLatin1Char('-'), 0, 0)); - m_svgIcon->setContainsMultipleImages(true); - } - - //success? - if (m_usesPlasmaTheme && m_svgIcon->isValid() && m_svgIcon->hasElement(sourceString)) { - m_icon = QIcon(); - m_imageIcon = QImage(); - m_svgIconName = sourceString; - //ok, svg not available from the plasma theme - } else { - //try to load from iconloader an svg with Plasma::Svg - const auto *iconTheme = KIconLoader::global()->theme(); - QString iconPath; - if (iconTheme) { - iconPath = iconTheme->iconPath(sourceString + QLatin1String(".svg"), qMin(width(), height()), KIconLoader::MatchBest); - if (iconPath.isEmpty()) { - iconPath = iconTheme->iconPath(sourceString + QLatin1String(".svgz"), qMin(width(), height()), KIconLoader::MatchBest); - } - } else { - qWarning() << "KIconLoader has no theme set"; + if (!m_iconItemSource->isValid()) { + //if we started with a QIcon use that. + QIcon icon = source.value(); + if (icon.isNull()) { + icon = QIcon::fromTheme(sourceString); } + m_iconItemSource.reset(new QIconSource(icon, this)); - if (!iconPath.isEmpty()) { - m_svgIcon->setImagePath(iconPath); - m_svgIconName = sourceString; - //fail, use QIcon - } else { - //if we started with a QIcon use that. - m_icon = source.value(); - if (m_icon.isNull()) { - m_icon = QIcon::fromTheme(sourceString); - } - - //since QIcon is rendered by KIconLoader, watch for when its configuration changes now and reload as needed. - connect(KIconLoader::global(), &KIconLoader::iconChanged, this, &IconItem::iconLoaderIconChanged); - - m_svgIconName.clear(); - delete m_svgIcon; - m_svgIcon = nullptr; - m_imageIcon = QImage(); - } + //since QIcon is rendered by KIconLoader, watch for when its configuration changes now and reload as needed. + connect(KIconLoader::global(), &KIconLoader::iconChanged, this, &IconItem::iconLoaderIconChanged); } } } else if (source.canConvert()) { - m_icon = source.value(); - m_imageIcon = QImage(); - m_svgIconName.clear(); - delete m_svgIcon; - m_svgIcon = nullptr; + m_iconItemSource.reset(new QIconSource(source.value(), this)); } else if (source.canConvert()) { - m_icon = QIcon(); - m_imageIcon = source.value(); - m_svgIconName.clear(); - delete m_svgIcon; - m_svgIcon = nullptr; + m_iconItemSource.reset(new QImageSource(source.value(), this)); } else { - m_icon = QIcon(); - m_imageIcon = QImage(); - m_svgIconName.clear(); - delete m_svgIcon; - m_svgIcon = nullptr; + m_iconItemSource.reset(new NullSource(this)); } if (width() > 0 && height() > 0) { schedulePixmapUpdate(); } updateImplicitSize(); emit sourceChanged(); if (isValid() != oldValid) { emit validChanged(); } } QVariant IconItem::source() const { return m_source; } void IconItem::setColorGroup(Plasma::Theme::ColorGroup group) { if (m_colorGroup == group) { return; } m_colorGroup = group; - - if (m_svgIcon) { - m_svgIcon->setColorGroup(group); - } - emit colorGroupChanged(); } Plasma::Theme::ColorGroup IconItem::colorGroup() const { return m_colorGroup; } void IconItem::setOverlays(const QStringList &overlays) { if (overlays == m_overlays) { return; } m_overlays = overlays; schedulePixmapUpdate(); emit overlaysChanged(); } QStringList IconItem::overlays() const { return m_overlays; } bool IconItem::isActive() const { return m_active; } void IconItem::setActive(bool active) { if (m_active == active) { return; } m_active = active; if (isComponentComplete()) { m_allowNextAnimation = true; schedulePixmapUpdate(); } emit activeChanged(); } bool IconItem::isAnimated() const { return m_animated; } void IconItem::setAnimated(bool animated) { if (m_animated == animated) { return; } m_animated = animated; emit animatedChanged(); } bool IconItem::usesPlasmaTheme() const { return m_usesPlasmaTheme; } void IconItem::setUsesPlasmaTheme(bool usesPlasmaTheme) { if (m_usesPlasmaTheme == usesPlasmaTheme) { return; } m_usesPlasmaTheme = usesPlasmaTheme; // Reload icon with new settings const QVariant src = m_source; m_source.clear(); setSource(src); update(); emit usesPlasmaThemeChanged(); } bool IconItem::roundToIconSize() const { return m_roundToIconSize; } void IconItem::setRoundToIconSize(bool roundToIconSize) { if (m_roundToIconSize == roundToIconSize) { return; } const QSize oldPaintedSize = paintedSize(); m_roundToIconSize = roundToIconSize; emit roundToIconSizeChanged(); if (oldPaintedSize != paintedSize()) { emit paintedSizeChanged(); } schedulePixmapUpdate(); } bool IconItem::isValid() const { - return !m_icon.isNull() || m_svgIcon || !m_imageIcon.isNull(); + return m_iconItemSource->isValid(); } int IconItem::paintedWidth() const { return paintedSize(boundingRect().size()).width(); } int IconItem::paintedHeight() const { return paintedSize(boundingRect().size()).height(); } QSize IconItem::paintedSize(const QSizeF &containerSize) const { const QSize &actualContainerSize = (containerSize.isValid() ? containerSize : boundingRect().size()).toSize(); const QSize paintedSize = m_iconPixmap.size().scaled(actualContainerSize, Qt::KeepAspectRatio); const int width = paintedSize.width(); const int height = paintedSize.height(); if (width == height) { if (m_roundToIconSize) { return QSize(Units::roundToIconSize(width), Units::roundToIconSize(height)); } else { return QSize(width, height); } } // if we don't have a square image, we still want it to be rounded to icon size // but we cannot just blindly round both as we might erroneously change a 50x45 image to be 48x32 // instead, round the bigger of the two and then downscale the smaller with the ratio if (width > height) { const int roundedWidth = m_roundToIconSize ? Units::roundToIconSize(width) : width; return QSize(roundedWidth, qRound(height * (roundedWidth / static_cast(width)))); } else { const int roundedHeight = m_roundToIconSize ? Units::roundToIconSize(height) : height; return QSize(qRound(width * (roundedHeight / static_cast(height))), roundedHeight); } } void IconItem::setStatus(Plasma::Svg::Status status) { if (m_status == status) { return; } m_status = status; - if (m_svgIcon) { - m_svgIcon->setStatus(status); - } emit statusChanged(); } Plasma::Svg::Status IconItem::status() const { return m_status; } void IconItem::setImplicitHeight2(int height) { m_implicitHeightSetByUser = true; setImplicitHeight(height); emit implicitHeightChanged2(); } void IconItem::setImplicitWidth2(int width) { m_implicitWidthSetByUser = true; setImplicitWidth(width); emit implicitWidthChanged2(); } void IconItem::updatePolish() { QQuickItem::updatePolish(); loadPixmap(); } QSGNode* IconItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *updatePaintNodeData) { Q_UNUSED(updatePaintNodeData) if (m_iconPixmap.isNull() || width() == 0.0 || height() == 0.0) { delete oldNode; return nullptr; } if (m_animation->state() == QAbstractAnimation::Running) { FadingNode *animatingNode = dynamic_cast(oldNode); if (!animatingNode || m_textureChanged) { delete oldNode; QSGTexture *source = window()->createTextureFromImage(m_oldIconPixmap.toImage(), QQuickWindow::TextureCanUseAtlas); source->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest); QSGTexture *target = window()->createTextureFromImage(m_iconPixmap.toImage(), QQuickWindow::TextureCanUseAtlas); target->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest); animatingNode = new FadingNode(source, target); m_sizeChanged = true; m_textureChanged = false; } animatingNode->setProgress(m_animValue); if (m_sizeChanged) { const QSize newSize = paintedSize(); const QRect destRect(QPointF(boundingRect().center() - QPointF(newSize.width(), newSize.height()) / 2).toPoint(), newSize); animatingNode->setRect(destRect); m_sizeChanged = false; } return animatingNode; } else { ManagedTextureNode *textureNode = dynamic_cast(oldNode); if (!textureNode || m_textureChanged) { delete oldNode; textureNode = new ManagedTextureNode; textureNode->setTexture(QSharedPointer(window()->createTextureFromImage(m_iconPixmap.toImage(), QQuickWindow::TextureCanUseAtlas))); m_sizeChanged = true; m_textureChanged = false; } textureNode->setFiltering(smooth() ? QSGTexture::Linear : QSGTexture::Nearest); if (m_sizeChanged) { const QSize newSize = paintedSize(); const QRect destRect(QPointF(boundingRect().center() - QPointF(newSize.width(), newSize.height()) / 2).toPoint(), newSize); textureNode->setRect(destRect); m_sizeChanged = false; } return textureNode; } } void IconItem::valueChanged(const QVariant &value) { m_animValue = value.toReal(); update(); } void IconItem::onEnabledChanged() { m_allowNextAnimation = true; schedulePixmapUpdate(); } void IconItem::animationFinished() { m_oldIconPixmap = QPixmap(); m_textureChanged = true; update(); } void IconItem::iconLoaderIconChanged(int group) { Q_UNUSED(group); schedulePixmapUpdate(); } void IconItem::windowVisibleChanged(bool visible) { if (visible) { m_blockNextAnimation = true; } } void IconItem::schedulePixmapUpdate() { polish(); } void IconItem::loadPixmap() { if (!isComponentComplete()) { return; } int size = qMin(qRound(width()), qRound(height())); if (m_roundToIconSize) { size = Units::roundToIconSize(size); } //final pixmap to paint QPixmap result; if (size <= 0) { m_iconPixmap = QPixmap(); m_animation->stop(); update(); return; - } else if (m_svgIcon) { - m_svgIcon->setDevicePixelRatio(window() ? window()->devicePixelRatio() : qApp->devicePixelRatio()); - m_svgIcon->resize(size, size); - if (!m_svgIconName.isEmpty() && m_svgIcon->hasElement(m_svgIconName)) { - result = m_svgIcon->pixmap(m_svgIconName); - } else if (!m_svgIconName.isEmpty()) { - const auto *iconTheme = KIconLoader::global()->theme(); - if (iconTheme) { - QString iconPath = iconTheme->iconPath(m_svgIconName + QLatin1String(".svg"), size, KIconLoader::MatchBest); - if (iconPath.isEmpty()) { - iconPath = iconTheme->iconPath(m_svgIconName + QLatin1String(".svgz"), size, KIconLoader::MatchBest); - } - - if (!iconPath.isEmpty()) { - m_svgIcon->setImagePath(iconPath); - } - } else { - qWarning() << "KIconLoader has no theme set"; - } - - result = m_svgIcon->pixmap(); - } - } else if (!m_icon.isNull()) { - KIconLoader::global()->setCustomPalette(Plasma::Theme().palette()); - result = m_icon.pixmap(window(), QSize(size, size)); - KIconLoader::global()->resetPalette(); - } else if (!m_imageIcon.isNull()) { - result = QPixmap::fromImage(m_imageIcon); + } + if (m_iconItemSource->isValid()) { + result = m_iconItemSource->pixmap(QSize(size, size)); } else { m_iconPixmap = QPixmap(); m_animation->stop(); update(); return; } // Strangely KFileItem::overlays() returns empty string-values, so // we need to check first whether an overlay must be drawn at all. // It is more efficient to do it here, as KIconLoader::drawOverlays() // assumes that an overlay will be drawn and has some additional // setup time. for (const QString &overlay : qAsConst(m_overlays)) { if (!overlay.isEmpty()) { // There is at least one overlay, draw all overlays above m_pixmap // and cancel the check KIconLoader::global()->drawOverlays(m_overlays, result, KIconLoader::Desktop); break; } } 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); } const QSize oldPaintedSize = paintedSize(); m_oldIconPixmap = m_iconPixmap; m_iconPixmap = result; m_textureChanged = true; if (oldPaintedSize != paintedSize()) { emit paintedSizeChanged(); } //don't animate initial setting bool animated = (m_animated || m_allowNextAnimation) && !m_oldIconPixmap.isNull() && !m_sizeChanged && !m_blockNextAnimation; if (QQuickWindow::sceneGraphBackend() == QLatin1String("software")) { animated = false; } if (animated) { m_animValue = 0.0; m_animation->setStartValue((qreal)0); m_animation->setEndValue((qreal)1); m_animation->start(); m_allowNextAnimation = false; } else { m_animValue = 1.0; m_animation->stop(); m_blockNextAnimation = false; } update(); } void IconItem::itemChange(ItemChange change, const ItemChangeData &value) { if (change == ItemVisibleHasChanged && value.boolValue) { m_blockNextAnimation = true; } else if (change == ItemEnabledHasChanged) { onEnabledChanged(); } else if (change == ItemSceneChange && value.window) { if (m_window) { disconnect(m_window.data(), &QWindow::visibleChanged, this, &IconItem::windowVisibleChanged); } m_window = value.window; if (m_window) { connect(m_window.data(), &QWindow::visibleChanged, this, &IconItem::windowVisibleChanged); } schedulePixmapUpdate(); } QQuickItem::itemChange(change, value); } void IconItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { if (newGeometry.size() != oldGeometry.size()) { m_sizeChanged = true; if (newGeometry.width() > 0 && newGeometry.height() > 0) { schedulePixmapUpdate(); } else { update(); } if (paintedSize(oldGeometry.size()) != paintedSize(newGeometry.size())) { emit paintedSizeChanged(); } } QQuickItem::geometryChanged(newGeometry, oldGeometry); } void IconItem::componentComplete() { QQuickItem::componentComplete(); schedulePixmapUpdate(); } diff --git a/src/declarativeimports/core/iconitem.h b/src/declarativeimports/core/iconitem.h index 848c07739..c9daddc74 100644 --- a/src/declarativeimports/core/iconitem.h +++ b/src/declarativeimports/core/iconitem.h @@ -1,229 +1,231 @@ /* * Copyright 2012 Marco Martin * Copyright 2014 David Edmundson * * 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 #include #include #include #include class QPropertyAnimation; +class IconItemSource; +class SvgSource; /** * @class IconItem * @short Displays an icon, either from the standard QIcon system or where applicable from the theme SVG files */ class IconItem : public QQuickItem { Q_OBJECT /** * Sets the icon to be displayed. Source can be one of: * - iconName (as a string) * - URL * - QImage * - QPixmap * - QIcon * * When passing an icon name (or a QIcon with an icon name set) it will: * - load the plasma variant if usesPlasmaTheme is set and exists * - otherwise try to load the icon as an SVG so colorscopes apply * - load the icon as normal */ Q_PROPERTY(QVariant source READ source WRITE setSource NOTIFY sourceChanged) /** * Specifies the color group to use for this icon * This only applies to icons loaded from the plasma theme */ Q_PROPERTY(Plasma::Theme::ColorGroup colorGroup READ colorGroup WRITE setColorGroup NOTIFY colorGroupChanged) /** * Specifies the overlay(s) for this icon */ Q_PROPERTY(QStringList overlays READ overlays WRITE setOverlays NOTIFY overlaysChanged) /** * Apply a visual indication that this icon is active. * Typically used to indicate that it is hovered */ Q_PROPERTY(bool active READ isActive WRITE setActive NOTIFY activeChanged) /** * Sets the image in a selected status. * Svgs can be colored with system color themes, if the status is selected, * the TextColor will become HighlightedText color and BackgroundColor * will become HighlightColor, making the svg graphics (for instance an icon) * will look correct together selected text * @see Plasma::Svg::status * @since 5.23 */ Q_PROPERTY(Plasma::Svg::Status status READ status WRITE setStatus NOTIFY statusChanged) /** * If set, icon will blend when the source is changed */ Q_PROPERTY(bool animated READ isAnimated WRITE setAnimated NOTIFY animatedChanged) /** * If set, icon will try and use icons from the Plasma theme if possible */ Q_PROPERTY(bool usesPlasmaTheme READ usesPlasmaTheme WRITE setUsesPlasmaTheme NOTIFY usesPlasmaThemeChanged) /** * If set, icon will round the painted size to defined icon sizes. Default is true. */ Q_PROPERTY(bool roundToIconSize READ roundToIconSize WRITE setRoundToIconSize NOTIFY roundToIconSizeChanged) /** * True if a valid icon is set. False otherwise. */ Q_PROPERTY(bool valid READ isValid NOTIFY validChanged) /** * The width of the icon that is actually painted * Icons are drawn at standard icon sizes (eg. 16,32,64) centered within the item */ Q_PROPERTY(int paintedWidth READ paintedWidth NOTIFY paintedSizeChanged) /** * The height of the icon actually being drawn. * Icons are drawn at standard icon sizes (eg. 16,32,64) centered within the item */ Q_PROPERTY(int paintedHeight READ paintedHeight NOTIFY paintedSizeChanged) Q_PROPERTY(int implicitHeight READ implicitHeight WRITE setImplicitHeight2 NOTIFY implicitHeightChanged2) Q_PROPERTY(int implicitWidth READ implicitWidth WRITE setImplicitWidth2 NOTIFY implicitWidthChanged2) public: explicit IconItem(QQuickItem *parent = nullptr); ~IconItem() override; void setSource(const QVariant &source); QVariant source() const; void setColorGroup(Plasma::Theme::ColorGroup group); Plasma::Theme::ColorGroup colorGroup() const; void setOverlays(const QStringList &overlays); QStringList overlays() const; bool isActive() const; void setActive(bool active); bool isAnimated() const; void setAnimated(bool animated); bool usesPlasmaTheme() const; void setUsesPlasmaTheme(bool usesPlasmaTheme); bool roundToIconSize() const; void setRoundToIconSize(bool roundToIconSize); bool isValid() const; int paintedWidth() const; int paintedHeight() const; void setStatus(Plasma::Svg::Status status); Plasma::Svg::Status status() const; void setImplicitHeight2(int height); void setImplicitWidth2(int height); void updatePolish() override; QSGNode* updatePaintNode(QSGNode * oldNode, UpdatePaintNodeData * updatePaintNodeData) override; void itemChange(ItemChange change, const ItemChangeData &value) override; void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override; void componentComplete() override; Q_SIGNALS: void overlaysChanged(); void activeChanged(); void sourceChanged(); void animatedChanged(); void usesPlasmaThemeChanged(); void roundToIconSizeChanged(); void validChanged(); void colorGroupChanged(); void paintedSizeChanged(); void statusChanged(); void implicitHeightChanged2(); void implicitWidthChanged2(); private Q_SLOTS: void schedulePixmapUpdate(); void animationFinished(); void valueChanged(const QVariant &value); void onEnabledChanged(); void iconLoaderIconChanged(int group); void windowVisibleChanged(bool visible); private: void loadPixmap(); QSize paintedSize(const QSizeF &containerSize = QSizeF()) const; void updateImplicitSize(); //all the ways we can set an source. Only one of them will be valid - QIcon m_icon; - Plasma::Svg *m_svgIcon; - QString m_svgIconName; - QImage m_imageIcon; + QScopedPointer m_iconItemSource; //this contains the raw variant it was passed QVariant m_source; Plasma::Svg::Status m_status; bool m_active; bool m_animated; bool m_usesPlasmaTheme; bool m_roundToIconSize; bool m_textureChanged; bool m_sizeChanged; bool m_allowNextAnimation; bool m_blockNextAnimation; bool m_implicitHeightSetByUser; bool m_implicitWidthSetByUser; QPixmap m_iconPixmap; QPixmap m_oldIconPixmap; QStringList m_overlays; Plasma::Theme::ColorGroup m_colorGroup; //animation on pixmap change QPropertyAnimation *m_animation; qreal m_animValue; QPointer m_window; + + // to access schedulePixmapUpdate private slot + friend class SvgSource; }; #endif