File indexing completed on 2024-04-28 07:44:48

0001 /*
0002  *  SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
0003  *  SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
0004  *  SPDX-FileCopyrightText: 2020 Carson Black <uhhadd@gmail.com>
0005  *
0006  *  SPDX-License-Identifier: LGPL-2.0-or-later
0007  */
0008 
0009 #pragma once
0010 
0011 #include <QIcon>
0012 #include <QPointer>
0013 #include <QQuickItem>
0014 #include <QVariant>
0015 
0016 #include <QQmlEngine>
0017 
0018 class QNetworkReply;
0019 class QQuickWindow;
0020 class QPropertyAnimation;
0021 
0022 namespace Kirigami
0023 {
0024 namespace Platform
0025 {
0026 class PlatformTheme;
0027 class Units;
0028 }
0029 }
0030 
0031 /**
0032  * Class for rendering an icon in UI.
0033  */
0034 class Icon : public QQuickItem
0035 {
0036     Q_OBJECT
0037     QML_ELEMENT
0038 
0039     /**
0040      * The source of this icon. An `Icon` can pull from:
0041      *
0042      * * The icon theme:
0043      * @include icon/IconThemeSource.qml
0044      * * The filesystem:
0045      * @include icon/FilesystemSource.qml
0046      * * Remote URIs:
0047      * @include icon/InternetSource.qml
0048      * * Custom providers:
0049      * @include icon/CustomSource.qml
0050      * * Your application's bundled resources:
0051      * @include icon/ResourceSource.qml
0052      *
0053      * @note See https://doc.qt.io/qt-5/qtquickcontrols2-icons.html for how to
0054      * bundle icon themes in your application to refer to them by name instead of
0055      * by resource URL.
0056      *
0057      * @note Use `fallback` to provide a fallback theme name for icons.
0058      *
0059      * @note Cuttlefish is a KDE application that lets you view all the icons that
0060      * you can use for your application. It offers a number of useful features such
0061      * as previews of their appearance across different installed themes, previews
0062      * at different sizes, and more. You might find it a useful tool when deciding
0063      * on which icons to use in your application.
0064      */
0065     Q_PROPERTY(QVariant source READ source WRITE setSource NOTIFY sourceChanged FINAL)
0066 
0067     /**
0068      * The name of a fallback icon to load from the icon theme when the `source`
0069      * cannot be found. The default fallback icon is `"unknown"`.
0070      *
0071      * @include icon/Fallback.qml
0072      *
0073      * @note This will only be loaded if source is unavailable (e.g. it doesn't exist, or network issues have prevented loading).
0074      */
0075     Q_PROPERTY(QString fallback READ fallback WRITE setFallback NOTIFY fallbackChanged FINAL)
0076 
0077     /**
0078      * The name of an icon from the icon theme to show while the icon set in `source` is
0079      * being loaded. This is primarily relevant for remote sources, or those using slow-
0080      * loading image providers. The default temporary icon is `"image-x-icon"`
0081      *
0082      * @note This will only be loaded if the source is a type which can be so long-loading
0083      * that a temporary image makes sense (e.g. a remote image, or from an ImageProvider
0084      * of the type QQmlImageProviderBase::ImageResponse)
0085      *
0086      * @since 5.15
0087      */
0088     Q_PROPERTY(QString placeholder READ placeholder WRITE setPlaceholder NOTIFY placeholderChanged FINAL)
0089 
0090     /**
0091      * Whether this icon will use the QIcon::Active mode when drawing the icon,
0092      * resulting in a graphical effect being applied to the icon to indicate that
0093      * it is currently active.
0094      *
0095      * This is typically used to indicate when an item is being hovered or pressed.
0096      *
0097      * @image html icon/active.png
0098      *
0099      * The color differences under the default KDE color palette, Breeze. Note
0100      * that a dull highlight background is typically displayed behind active icons and
0101      * it is recommended to add one if you are creating a custom component.
0102      */
0103     Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged FINAL)
0104 
0105     /**
0106      * Whether this icon's `source` is valid and it is being used.
0107      */
0108     Q_PROPERTY(bool valid READ valid NOTIFY validChanged FINAL)
0109 
0110     /**
0111      * Whether this icon will use the QIcon::Selected mode when drawing the icon,
0112      * resulting in a graphical effect being applied to the icon to indicate that
0113      * it is currently selected.
0114      *
0115      * This is typically used to indicate when a list item is currently selected.
0116      *
0117      * @image html icon/selected.png
0118      *
0119      * The color differences under the default KDE color palette, Breeze. Note
0120      * that a blue background is typically displayed behind selected elements.
0121      */
0122     Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectedChanged FINAL)
0123 
0124     /**
0125      * Whether this icon will be treated as a mask. When an icon is being used
0126      * as a mask, all non-transparent colors are replaced with the color provided in the Icon's
0127      * @link Icon::color color @endlink property.
0128      *
0129      * @see color
0130      */
0131     Q_PROPERTY(bool isMask READ isMask WRITE setIsMask NOTIFY isMaskChanged FINAL)
0132 
0133     /**
0134      * The color to use when drawing this icon when `isMask` is enabled.
0135      * If this property is not set or is `Qt::transparent`, the icon will use
0136      * the text or the selected text color, depending on if `selected` is set to
0137      * true.
0138      */
0139     Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL)
0140 
0141     /**
0142      * Whether the icon is correctly loaded, is asynchronously loading or there was an error.
0143      * Note that image loading will not be initiated until the item is shown, so if the Icon is not visible,
0144      * it can only have Null or Loading states.
0145      * @since 5.15
0146      */
0147     Q_PROPERTY(Icon::Status status READ status NOTIFY statusChanged FINAL)
0148 
0149     /**
0150      * The width of the painted area measured in pixels. This will be smaller than or
0151      * equal to the width of the area taken up by the Item itself. This can be 0.
0152      *
0153      * @since 5.15
0154      */
0155     Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedAreaChanged FINAL)
0156 
0157     /**
0158      * The height of the painted area measured in pixels. This will be smaller than or
0159      * equal to the height of the area taken up by the Item itself. This can be 0.
0160      *
0161      * @since 5.15
0162      */
0163     Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedAreaChanged FINAL)
0164 
0165     /**
0166      * If set, icon will blend when the source is changed
0167      */
0168     Q_PROPERTY(bool animated READ isAnimated WRITE setAnimated NOTIFY animatedChanged FINAL)
0169 
0170     /**
0171      * If set, icon will round the painted size to defined icon sizes. Default is true.
0172      */
0173     Q_PROPERTY(bool roundToIconSize READ roundToIconSize WRITE setRoundToIconSize NOTIFY roundToIconSizeChanged FINAL)
0174 
0175 public:
0176     enum Status {
0177         Null = 0, /// No icon has been set
0178         Ready, /// The icon loaded correctly
0179         Loading, // The icon is being loaded, but not ready yet
0180         Error, /// There was an error while loading the icon, for instance a non existent themed name, or an invalid url
0181     };
0182     Q_ENUM(Status)
0183 
0184     Icon(QQuickItem *parent = nullptr);
0185     ~Icon() override;
0186 
0187     void componentComplete() override;
0188 
0189     void setSource(const QVariant &source);
0190     QVariant source() const;
0191 
0192     void setActive(bool active = true);
0193     bool active() const;
0194 
0195     bool valid() const;
0196 
0197     void setSelected(bool selected = true);
0198     bool selected() const;
0199 
0200     void setIsMask(bool mask);
0201     bool isMask() const;
0202 
0203     void setColor(const QColor &color);
0204     QColor color() const;
0205 
0206     QString fallback() const;
0207     void setFallback(const QString &fallback);
0208 
0209     QString placeholder() const;
0210     void setPlaceholder(const QString &placeholder);
0211 
0212     Status status() const;
0213 
0214     qreal paintedWidth() const;
0215     qreal paintedHeight() const;
0216 
0217     bool isAnimated() const;
0218     void setAnimated(bool animated);
0219 
0220     bool roundToIconSize() const;
0221     void setRoundToIconSize(bool roundToIconSize);
0222 
0223     QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data) override;
0224 
0225 Q_SIGNALS:
0226     void sourceChanged();
0227     void activeChanged();
0228     void validChanged();
0229     void selectedChanged();
0230     void isMaskChanged();
0231     void colorChanged();
0232     void fallbackChanged(const QString &fallback);
0233     void placeholderChanged(const QString &placeholder);
0234     void statusChanged();
0235     void paintedAreaChanged();
0236     void animatedChanged();
0237     void roundToIconSizeChanged();
0238 
0239 protected:
0240     void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
0241     QImage findIcon(const QSize &size);
0242     void handleFinished(QNetworkReply *reply);
0243     void handleRedirect(QNetworkReply *reply);
0244     QIcon::Mode iconMode() const;
0245     bool guessMonochrome(const QImage &img);
0246     void setStatus(Status status);
0247     void updatePolish() override;
0248     void updatePaintedGeometry();
0249     void updateIsMaskHeuristic(const QString &iconSource);
0250     void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override;
0251 
0252 private:
0253     void valueChanged(const QVariant &value);
0254     void windowVisibleChanged(bool visible);
0255     QSGNode *createSubtree(qreal initialOpacity);
0256     void updateSubtree(QSGNode *node, qreal opacity);
0257     QSize iconSizeHint() const;
0258     inline QImage iconPixmap(const QIcon &icon) const;
0259 
0260     Kirigami::Platform::PlatformTheme *m_theme = nullptr;
0261     Kirigami::Platform::Units *m_units = nullptr;
0262     QPointer<QNetworkReply> m_networkReply;
0263     QHash<int, bool> m_monochromeHeuristics;
0264     QVariant m_source;
0265     qreal m_devicePixelRatio = 1.0;
0266     Status m_status = Null;
0267     bool m_textureChanged = false;
0268     bool m_sizeChanged = false;
0269     bool m_active;
0270     bool m_selected;
0271     bool m_isMask;
0272     bool m_isMaskHeuristic = false;
0273     QImage m_loadedImage;
0274     QColor m_color = Qt::transparent;
0275     QString m_fallback = QStringLiteral("unknown");
0276     QString m_placeholder = QStringLiteral("image-png");
0277     QSizeF m_paintedSize;
0278 
0279     QImage m_oldIcon;
0280     QImage m_icon;
0281 
0282     // animation on image change
0283     QPropertyAnimation *m_animation = nullptr;
0284     qreal m_animValue = 1.0;
0285     bool m_animated = false;
0286     bool m_roundToIconSize = true;
0287     bool m_allowNextAnimation = false;
0288     bool m_blockNextAnimation = false;
0289     QPointer<QQuickWindow> m_window;
0290 };