1/*
2 * SPDX-FileCopyrightText: 2011 Marco Martin <mart@kde.org>
3 * SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
4 * SPDX-FileCopyrightText: 2020 Carson Black <uhhadd@gmail.com>
5 *
6 * SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8
9#pragma once
10
11#include <QIcon>
12#include <QPointer>
13#include <QQuickItem>
14#include <QVariant>
15
16#include <QQmlEngine>
17
18class QNetworkReply;
19class QQuickWindow;
20class QPropertyAnimation;
21
22namespace Kirigami
23{
24namespace Platform
25{
26class PlatformTheme;
27class Units;
28}
29}
30
31/**
32 * Class for rendering an icon in UI.
33 */
34class Icon : public QQuickItem
35{
36 Q_OBJECT
37 QML_ELEMENT
38
39 /**
40 * The source of this icon. An `Icon` can pull from:
41 *
42 * * The icon theme:
43 * @include icon/IconThemeSource.qml
44 * * The filesystem:
45 * @include icon/FilesystemSource.qml
46 * * Remote URIs:
47 * @include icon/InternetSource.qml
48 * * Custom providers:
49 * @include icon/CustomSource.qml
50 * * Your application's bundled resources:
51 * @include icon/ResourceSource.qml
52 *
53 * @note See https://doc.qt.io/qt-5/qtquickcontrols2-icons.html for how to
54 * bundle icon themes in your application to refer to them by name instead of
55 * by resource URL.
56 *
57 * @note Use `fallback` to provide a fallback theme name for icons.
58 *
59 * @note Cuttlefish is a KDE application that lets you view all the icons that
60 * you can use for your application. It offers a number of useful features such
61 * as previews of their appearance across different installed themes, previews
62 * at different sizes, and more. You might find it a useful tool when deciding
63 * on which icons to use in your application.
64 */
65 Q_PROPERTY(QVariant source READ source WRITE setSource NOTIFY sourceChanged FINAL)
66
67 /**
68 * The name of a fallback icon to load from the icon theme when the `source`
69 * cannot be found. The default fallback icon is `"unknown"`.
70 *
71 * @include icon/Fallback.qml
72 *
73 * @note This will only be loaded if source is unavailable (e.g. it doesn't exist, or network issues have prevented loading).
74 */
75 Q_PROPERTY(QString fallback READ fallback WRITE setFallback NOTIFY fallbackChanged FINAL)
76
77 /**
78 * The name of an icon from the icon theme to show while the icon set in `source` is
79 * being loaded. This is primarily relevant for remote sources, or those using slow-
80 * loading image providers. The default temporary icon is `"image-x-icon"`
81 *
82 * @note This will only be loaded if the source is a type which can be so long-loading
83 * that a temporary image makes sense (e.g. a remote image, or from an ImageProvider
84 * of the type QQmlImageProviderBase::ImageResponse)
85 *
86 * @since 5.15
87 */
88 Q_PROPERTY(QString placeholder READ placeholder WRITE setPlaceholder NOTIFY placeholderChanged FINAL)
89
90 /**
91 * Whether this icon will use the QIcon::Active mode when drawing the icon,
92 * resulting in a graphical effect being applied to the icon to indicate that
93 * it is currently active.
94 *
95 * This is typically used to indicate when an item is being hovered or pressed.
96 *
97 * @image html icon/active.png
98 *
99 * The color differences under the default KDE color palette, Breeze. Note
100 * that a dull highlight background is typically displayed behind active icons and
101 * it is recommended to add one if you are creating a custom component.
102 */
103 Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged FINAL)
104
105 /**
106 * Whether this icon's `source` is valid and it is being used.
107 */
108 Q_PROPERTY(bool valid READ valid NOTIFY validChanged FINAL)
109
110 /**
111 * Whether this icon will use the QIcon::Selected mode when drawing the icon,
112 * resulting in a graphical effect being applied to the icon to indicate that
113 * it is currently selected.
114 *
115 * This is typically used to indicate when a list item is currently selected.
116 *
117 * @image html icon/selected.png
118 *
119 * The color differences under the default KDE color palette, Breeze. Note
120 * that a blue background is typically displayed behind selected elements.
121 */
122 Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectedChanged FINAL)
123
124 /**
125 * Whether this icon will be treated as a mask. When an icon is being used
126 * as a mask, all non-transparent colors are replaced with the color provided in the Icon's
127 * @link Icon::color color @endlink property.
128 *
129 * @see color
130 */
131 Q_PROPERTY(bool isMask READ isMask WRITE setIsMask NOTIFY isMaskChanged FINAL)
132
133 /**
134 * The color to use when drawing this icon when `isMask` is enabled.
135 * If this property is not set or is `Qt::transparent`, the icon will use
136 * the text or the selected text color, depending on if `selected` is set to
137 * true.
138 */
139 Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL)
140
141 /**
142 * Whether the icon is correctly loaded, is asynchronously loading or there was an error.
143 * Note that image loading will not be initiated until the item is shown, so if the Icon is not visible,
144 * it can only have Null or Loading states.
145 * @since 5.15
146 */
147 Q_PROPERTY(Icon::Status status READ status NOTIFY statusChanged FINAL)
148
149 /**
150 * The width of the painted area measured in pixels. This will be smaller than or
151 * equal to the width of the area taken up by the Item itself. This can be 0.
152 *
153 * @since 5.15
154 */
155 Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedAreaChanged FINAL)
156
157 /**
158 * The height of the painted area measured in pixels. This will be smaller than or
159 * equal to the height of the area taken up by the Item itself. This can be 0.
160 *
161 * @since 5.15
162 */
163 Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedAreaChanged FINAL)
164
165 /**
166 * If set, icon will blend when the source is changed
167 */
168 Q_PROPERTY(bool animated READ isAnimated WRITE setAnimated NOTIFY animatedChanged FINAL)
169
170 /**
171 * If set, icon will round the painted size to defined icon sizes. Default is true.
172 */
173 Q_PROPERTY(bool roundToIconSize READ roundToIconSize WRITE setRoundToIconSize NOTIFY roundToIconSizeChanged FINAL)
174
175public:
176 enum Status {
177 Null = 0, /// No icon has been set
178 Ready, /// The icon loaded correctly
179 Loading, // The icon is being loaded, but not ready yet
180 Error, /// There was an error while loading the icon, for instance a non existent themed name, or an invalid url
181 };
182 Q_ENUM(Status)
183
184 Icon(QQuickItem *parent = nullptr);
185 ~Icon() override;
186
187 void componentComplete() override;
188
189 void setSource(const QVariant &source);
190 QVariant source() const;
191
192 void setActive(bool active = true);
193 bool active() const;
194
195 bool valid() const;
196
197 void setSelected(bool selected = true);
198 bool selected() const;
199
200 void setIsMask(bool mask);
201 bool isMask() const;
202
203 void setColor(const QColor &color);
204 QColor color() const;
205
206 QString fallback() const;
207 void setFallback(const QString &fallback);
208
209 QString placeholder() const;
210 void setPlaceholder(const QString &placeholder);
211
212 Status status() const;
213
214 qreal paintedWidth() const;
215 qreal paintedHeight() const;
216
217 bool isAnimated() const;
218 void setAnimated(bool animated);
219
220 bool roundToIconSize() const;
221 void setRoundToIconSize(bool roundToIconSize);
222
223 QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data) override;
224
225Q_SIGNALS:
226 void sourceChanged();
227 void activeChanged();
228 void validChanged();
229 void selectedChanged();
230 void isMaskChanged();
231 void colorChanged();
232 void fallbackChanged(const QString &fallback);
233 void placeholderChanged(const QString &placeholder);
234 void statusChanged();
235 void paintedAreaChanged();
236 void animatedChanged();
237 void roundToIconSizeChanged();
238
239protected:
240 void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
241 QImage findIcon(const QSize &size);
242 void handleFinished(QNetworkReply *reply);
243 void handleRedirect(QNetworkReply *reply);
244 QIcon::Mode iconMode() const;
245 bool guessMonochrome(const QImage &img);
246 void setStatus(Status status);
247 void updatePolish() override;
248 void updatePaintedGeometry();
249 void updateIsMaskHeuristic(const QString &iconSource);
250 void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override;
251
252private:
253 void valueChanged(const QVariant &value);
254 void windowVisibleChanged(bool visible);
255 QSGNode *createSubtree(qreal initialOpacity);
256 void updateSubtree(QSGNode *node, qreal opacity);
257 QSize iconSizeHint() const;
258 inline QImage iconPixmap(const QIcon &icon) const;
259
260 Kirigami::Platform::PlatformTheme *m_theme = nullptr;
261 Kirigami::Platform::Units *m_units = nullptr;
262 QPointer<QNetworkReply> m_networkReply;
263 QHash<int, bool> m_monochromeHeuristics;
264 QVariant m_source;
265 qreal m_devicePixelRatio = 1.0;
266 Status m_status = Null;
267 bool m_textureChanged = false;
268 bool m_sizeChanged = false;
269 bool m_active;
270 bool m_selected;
271 bool m_isMask;
272 bool m_isMaskHeuristic = false;
273 QImage m_loadedImage;
274 QColor m_color = Qt::transparent;
275 QString m_fallback = QStringLiteral("unknown");
276 QString m_placeholder = QStringLiteral("image-png");
277 QSizeF m_paintedSize;
278
279 QImage m_oldIcon;
280 QImage m_icon;
281
282 // animation on image change
283 QPropertyAnimation *m_animation = nullptr;
284 qreal m_animValue = 1.0;
285 bool m_animated = false;
286 bool m_roundToIconSize = true;
287 bool m_allowNextAnimation = false;
288 bool m_blockNextAnimation = false;
289 QPointer<QQuickWindow> m_window;
290};
291

source code of kirigami/src/icon.h