File indexing completed on 2024-05-05 04:46:58

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 class QNetworkReply;
0017 namespace Maui
0018 {
0019 class PlatformTheme;
0020 }
0021 
0022 /**
0023  * Class for rendering an icon in UI.
0024  */
0025 class Icon : public QQuickItem
0026 {
0027     Q_OBJECT
0028     QML_NAMED_ELEMENT(PrivateIcon)
0029 
0030     /**
0031      * The source of this icon. An `Icon` can pull from:
0032      *
0033      * * The icon theme:
0034      * @include icon/IconThemeSource.qml
0035      * * The filesystem:
0036      * @include icon/FilesystemSource.qml
0037      * * Remote URIs:
0038      * @include icon/InternetSource.qml
0039      * * Custom providers:
0040      * @include icon/CustomSource.qml
0041      * * Your application's bundled resources:
0042      * @include icon/ResourceSource.qml
0043      *
0044      * @note See https://doc.qt.io/qt-5/qtquickcontrols2-icons.html for how to
0045      * bundle icon themes in your application to refer to them by name instead of
0046      * by resource URL.
0047      *
0048      * @note Use `fallback` to provide a fallback theme name for icons.
0049      *
0050      * @note Cuttlefish is a KDE application that lets you view all the icons that
0051      * you can use for your application. It offers a number of useful features such
0052      * as previews of their appearance across different installed themes, previews
0053      * at different sizes, and more. You might find it a useful tool when deciding
0054      * on which icons to use in your application.
0055      */
0056     Q_PROPERTY(QVariant source READ source WRITE setSource NOTIFY sourceChanged)
0057 
0058     /**
0059      * The name of a fallback icon to load from the icon theme when the `source`
0060      * cannot be found. The default fallback icon is `"unknown"`.
0061      *
0062      * @include icon/Fallback.qml
0063      *
0064      * @note This will only be loaded if source is unavailable (e.g. it doesn't exist, or network issues have prevented loading).
0065      */
0066     Q_PROPERTY(QString fallback READ fallback WRITE setFallback NOTIFY fallbackChanged)
0067 
0068     /**
0069      * The name of an icon from the icon theme to show while the icon set in `source` is
0070      * being loaded. This is primarily relevant for remote sources, or those using slow-
0071      * loading image providers. The default temporary icon is `"image-x-icon"`
0072      *
0073      * @note This will only be loaded if the source is a type which can be so long-loading
0074      * that a temporary image makes sense (e.g. a remote image, or from an ImageProvider
0075      * of the type QQmlImageProviderBase::ImageResponse)
0076      *
0077      * @since 5.15
0078      */
0079     Q_PROPERTY(QString placeholder READ placeholder WRITE setPlaceholder NOTIFY placeholderChanged)
0080 
0081     /**
0082      * Whether this icon will use the QIcon::Active mode when drawing the icon,
0083      * resulting in a graphical effect being applied to the icon to indicate that
0084      * it is currently active.
0085      *
0086      * This is typically used to indicate when an item is being hovered or pressed.
0087      *
0088      * @image html icon/active.png
0089      *
0090      * The color differences under the default KDE color palette, Breeze. Note
0091      * that a dull highlight background is typically displayed behind active icons and
0092      * it is recommended to add one if you are creating a custom component.
0093      */
0094     Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged)
0095 
0096     /**
0097      * Whether this icon's `source` is valid and it is being used.
0098      */
0099     Q_PROPERTY(bool valid READ valid NOTIFY validChanged)
0100 
0101     /**
0102      * Whether this icon will use the QIcon::Selected mode when drawing the icon,
0103      * resulting in a graphical effect being applied to the icon to indicate that
0104      * it is currently selected.
0105      *
0106      * This is typically used to indicate when a list item is currently selected.
0107      *
0108      * @image html icon/selected.png
0109      *
0110      * The color differences under the default KDE color palette, Breeze. Note
0111      * that a blue background is typically displayed behind selected elements.
0112      */
0113     Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectedChanged)
0114 
0115     /**
0116      * Whether this icon will be treated as a mask. When an icon is being used
0117      * as a mask, all non-transparent colors are replaced with the color provided in the Icon's
0118      * @link Icon::color color @endlink property.
0119      *
0120      * @see color
0121      */
0122     Q_PROPERTY(bool isMask READ isMask WRITE setIsMask NOTIFY isMaskChanged)
0123 
0124     /**
0125      * The color to use when drawing this icon when `isMask` is enabled.
0126      * If this property is not set or is `Qt::transparent`, the icon will use
0127      * the text or the selected text color, depending on if `selected` is set to
0128      * true.
0129      */
0130     Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged)
0131 
0132     /**
0133      * Whether the icon is correctly loaded, is asynchronously loading or there was an error.
0134      * Note that image loading will not be initiated until the item is shown, so if the Icon is not visible,
0135      * it can only have Null or Loading states.
0136      * @since 5.15
0137      */
0138     Q_PROPERTY(Icon::Status status READ status NOTIFY statusChanged)
0139 
0140     /**
0141      * The width of the painted area measured in pixels. This will be smaller than or
0142      * equal to the width of the area taken up by the Item itself. This can be 0.
0143      *
0144      * @since 5.15
0145      */
0146     Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedAreaChanged)
0147 
0148     /**
0149      * The height of the painted area measured in pixels. This will be smaller than or
0150      * equal to the height of the area taken up by the Item itself. This can be 0.
0151      *
0152      * @since 5.15
0153      */
0154     Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedAreaChanged)
0155 public:
0156     enum Status {
0157         Null = 0, /// No icon has been set
0158         Ready, /// The icon loaded correctly
0159         Loading, // The icon is being loaded, but not ready yet
0160         Error, /// There was an error while loading the icon, for instance a non existent themed name, or an invalid url
0161     };
0162     Q_ENUM(Status)
0163 
0164     Icon(QQuickItem *parent = nullptr);
0165     ~Icon() override;
0166 
0167     void setSource(const QVariant &source);
0168     QVariant source() const;
0169 
0170     void setActive(bool active = true);
0171     bool active() const;
0172 
0173     bool valid() const;
0174 
0175     void setSelected(bool selected = true);
0176     bool selected() const;
0177 
0178     void setIsMask(bool mask);
0179     bool isMask() const;
0180 
0181     void setColor(const QColor &color);
0182     QColor color() const;
0183 
0184     QString fallback() const;
0185     void setFallback(const QString &fallback);
0186 
0187     QString placeholder() const;
0188     void setPlaceholder(const QString &placeholder);
0189 
0190     Status status() const;
0191 
0192     qreal paintedWidth() const;
0193     qreal paintedHeight() const;
0194 
0195     QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data) override;
0196 
0197 public Q_SLOTS:
0198     void refresh();
0199 
0200 Q_SIGNALS:
0201     void sourceChanged();
0202     void activeChanged();
0203     void validChanged();
0204     void selectedChanged();
0205     void isMaskChanged();
0206     void colorChanged();
0207     void fallbackChanged(const QString &fallback);
0208     void placeholderChanged(const QString &placeholder);
0209     void statusChanged();
0210     void paintedAreaChanged();
0211 
0212 protected:
0213 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
0214     void geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) override;
0215 #else
0216     void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
0217 #endif
0218     QImage findIcon(const QSize &size);
0219     void handleFinished(QNetworkReply *reply);
0220     void handleRedirect(QNetworkReply *reply);
0221     QIcon::Mode iconMode() const;
0222     bool guessMonochrome(const QImage &img);
0223     void setStatus(Status status);
0224     void updatePolish() override;
0225     void updatePaintedGeometry();
0226     void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override;
0227 
0228 private:
0229     Maui::PlatformTheme *m_theme = nullptr;
0230     QPointer<QNetworkReply> m_networkReply;
0231     QHash<int, bool> m_monochromeHeuristics;
0232     QVariant m_source;
0233     Status m_status = Null;
0234     bool m_changed;
0235     bool m_active;
0236     bool m_selected;
0237     bool m_isMask;
0238     bool m_isMaskHeuristic = false;
0239     QImage m_loadedImage;
0240     QColor m_color = Qt::transparent;
0241     QString m_fallback = QStringLiteral("unknown");
0242     QString m_placeholder = QStringLiteral("image-png");
0243     qreal m_paintedWidth = 0.0;
0244     qreal m_paintedHeight = 0.0;
0245 
0246     QImage m_icon;
0247 
0248 };