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 * \qmltype Icon
33 * \inqmlmodule org.kde.kirigami.primitives
34 *
35 * \brief Class for rendering an icon supporting many different sources.
36 *
37 * \l source is the most important property, and determines where to get the
38 * icon from. It can be a FreeDesktop-compatible icon name, a URL, a local
39 * image file, or a bundled resource. Use \l fallback to specify the name of an
40 * icon from the current icon theme to display if the requested icon is not
41 * found.
42 */
43class Icon : public QQuickItem
44{
45 Q_OBJECT
46 QML_ELEMENT
47
48 /*!
49 * \qmlproperty var Icon::source
50 *
51 * The source of this icon. An Icon can pull from:
52 * \list
53 * \li The icon theme:
54 * \quotefile icon/IconThemeSource.qml
55 * \li The filesystem:
56 * \quotefile icon/FilesystemSource.qml
57 * \li Remote URIs:
58 * \quotefile icon/InternetSource.qml
59 * \li Custom providers:
60 * \quotefile icon/CustomSource.qml
61 * \li Your application's bundled resources:
62 * \quotefile icon/ResourceSource.qml
63 * \endlist
64 *
65 * \note See https://doc.qt.io/qt-5/qtquickcontrols2-icons.html for how to
66 * bundle icon themes in your application to refer to them by name instead of
67 * by resource URL.
68 *
69 * \note Use fallback to provide a fallback icon from the current icon theme.
70 *
71 * \note Cuttlefish is a KDE application that lets you view all the icons that
72 * you can use for your application. It offers a number of useful features such
73 * as previews of their appearance across different installed themes, previews
74 * at different sizes, and more. You might find it a useful tool when deciding
75 * on which icons to use in your application.
76 */
77 Q_PROPERTY(QVariant source READ source WRITE setSource NOTIFY sourceChanged FINAL)
78
79 /*!
80 * \qmlproperty string Icon::fallback
81 *
82 * The name of a fallback icon to load from the icon theme when the source
83 * cannot be found. The default fallback icon is \c "unknown".
84 *
85 * \quotefile icon/Fallback.qml
86 *
87 * \note This will only be loaded if source is unavailable (e.g. it doesn't exist, or network issues have prevented loading).
88 */
89 Q_PROPERTY(QString fallback READ fallback WRITE setFallback NOTIFY fallbackChanged FINAL)
90
91 /*!
92 * \qmlproperty string Icon::placeholder
93 *
94 * The name of an icon from the icon theme to show while the icon set in \l source is
95 * being loaded. This is primarily relevant for remote sources, or those using slow-
96 * loading image providers. The default temporary icon is \c "image-x-icon"
97 *
98 * \note This will only be loaded if the source is a type which can be so long-loading
99 * that a temporary image makes sense (e.g. a remote image, or from an ImageProvider
100 * of the type QQmlImageProviderBase::ImageResponse)
101 *
102 * \since 5.15
103 */
104 Q_PROPERTY(QString placeholder READ placeholder WRITE setPlaceholder NOTIFY placeholderChanged FINAL)
105
106 /*!
107 * \qmlproperty bool Icon::active
108 *
109 * Whether this icon will use the QIcon::Active mode when drawing the icon,
110 * resulting in a graphical effect being applied to the icon to indicate that
111 * it is currently active.
112 *
113 * This is typically used to indicate when an item is being hovered or pressed.
114 *
115 * \image icon/active.png
116 *
117 * The color differences under the default KDE color palette, Breeze. Note
118 * that a dull highlight background is typically displayed behind active icons and
119 * it is recommended to add one if you are creating a custom component.
120 *
121 * The default is \c false.
122 */
123 Q_PROPERTY(bool active READ active WRITE setActive NOTIFY activeChanged FINAL)
124
125 /*!
126 * \qmlproperty bool Icon::valid
127 *
128 * Whether this icon's source is valid and it is being used.
129 */
130 Q_PROPERTY(bool valid READ valid NOTIFY validChanged FINAL)
131
132 /*!
133 * \qmlproperty bool Icon::selected
134 *
135 * Whether this icon will use the QIcon::Selected mode when drawing the icon,
136 * resulting in a graphical effect being applied to the icon to indicate that
137 * it is currently selected.
138 *
139 * This is typically used to indicate when a list item is currently selected.
140 *
141 * \image icon/selected.png
142 *
143 * The color differences under the default KDE color palette, Breeze. Note
144 * that a blue background is typically displayed behind selected elements.
145 *
146 * The default is \c false.
147 */
148 Q_PROPERTY(bool selected READ selected WRITE setSelected NOTIFY selectedChanged FINAL)
149
150 /*!
151 * \qmlproperty bool Icon::isMask
152 *
153 * Whether this icon will be treated as a mask. When an icon is being used
154 * as a mask, all non-transparent colors are replaced with the color provided in the Icon's
155 * color property.
156 *
157 * The default is \c false.
158 *
159 * \sa color
160 */
161 Q_PROPERTY(bool isMask READ isMask WRITE setIsMask NOTIFY isMaskChanged FINAL)
162
163 /*!
164 * \qmlproperty color Icon::color
165 *
166 * The color to draw this icon in when \l isMask is true.
167 * If this property is not set or is \c Qt::transparent, the icon will use
168 * the text or the selected text color, depending on if selected is set to
169 * true. This property has no effect if \l isMask is \c false.
170 */
171 Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY colorChanged FINAL)
172
173 /*!
174 * \qmlproperty enumeration Icon::status
175 *
176 * \qmlenumeratorsfrom Icon::Status
177 *
178 * Whether the icon is correctly loaded, is asynchronously loading, or there was an error.
179 * Note that image loading will not be initiated until the item is shown, so if the Icon is not visible,
180 * it can only have Null or Loading states.
181 * \since 5.15
182 */
183 Q_PROPERTY(Icon::Status status READ status NOTIFY statusChanged FINAL)
184
185 /*!
186 * \qmlproperty real Icon::paintedWidth
187 *
188 * The width of the painted area measured in pixels. This will be smaller than or
189 * equal to the width of the area taken up by the Item itself. This can be 0.
190 *
191 * \since 5.15
192 */
193 Q_PROPERTY(qreal paintedWidth READ paintedWidth NOTIFY paintedAreaChanged FINAL)
194
195 /*!
196 * \qmlproperty real Icon::paintedHeight
197 *
198 * The height of the painted area measured in pixels. This will be smaller than or
199 * equal to the height of the area taken up by the Item itself. This can be 0.
200 *
201 * \since 5.15
202 */
203 Q_PROPERTY(qreal paintedHeight READ paintedHeight NOTIFY paintedAreaChanged FINAL)
204
205 /*!
206 * \qmlproperty bool Icon::animated
207 *
208 * If set, icon will blend when the source is changed.
209 */
210 Q_PROPERTY(bool animated READ isAnimated WRITE setAnimated NOTIFY animatedChanged FINAL)
211
212 /*!
213 * \qmlproperty bool Icon::roundToIconSize
214 *
215 * If set, icon will round the painted size to the nearest standard icon size.
216 *
217 * The default is \c true.
218 */
219 Q_PROPERTY(bool roundToIconSize READ roundToIconSize WRITE setRoundToIconSize NOTIFY roundToIconSizeChanged FINAL)
220
221public:
222 /*!
223 * \value Null No icon has been set
224 * \value Ready The icon loaded correctly
225 * \value Loading The icon is being loaded, but not ready yet
226 * \value Error There was an error while loading the icon, for instance a non existent themed name, or an invalid url
227 */
228 enum Status {
229 Null = 0,
230 Ready,
231 Loading,
232 Error,
233 };
234 Q_ENUM(Status)
235
236 Icon(QQuickItem *parent = nullptr);
237 ~Icon() override;
238
239 void componentComplete() override;
240
241 void setSource(const QVariant &source);
242 QVariant source() const;
243
244 void setActive(bool active = true);
245 bool active() const;
246
247 bool valid() const;
248
249 void setSelected(bool selected = true);
250 bool selected() const;
251
252 void setIsMask(bool mask);
253 bool isMask() const;
254
255 void setColor(const QColor &color);
256 QColor color() const;
257
258 QString fallback() const;
259 void setFallback(const QString &fallback);
260
261 QString placeholder() const;
262 void setPlaceholder(const QString &placeholder);
263
264 Status status() const;
265
266 qreal paintedWidth() const;
267 qreal paintedHeight() const;
268
269 bool isAnimated() const;
270 void setAnimated(bool animated);
271
272 bool roundToIconSize() const;
273 void setRoundToIconSize(bool roundToIconSize);
274
275 QSGNode *updatePaintNode(QSGNode *node, UpdatePaintNodeData *data) override;
276
277Q_SIGNALS:
278 void sourceChanged();
279 void activeChanged();
280 void validChanged();
281 void selectedChanged();
282 void isMaskChanged();
283 void colorChanged();
284 void fallbackChanged(const QString &fallback);
285 void placeholderChanged(const QString &placeholder);
286 void statusChanged();
287 void paintedAreaChanged();
288 void animatedChanged();
289 void roundToIconSizeChanged();
290
291protected:
292 void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override;
293 QImage findIcon(const QSize &size);
294 void handleFinished(QNetworkReply *reply);
295 void handleRedirect(QNetworkReply *reply);
296 bool guessMonochrome(const QImage &img);
297 void setStatus(Status status);
298 void updatePolish() override;
299 void updatePaintedGeometry();
300 void updateIsMaskHeuristic(const QString &iconSource);
301 void itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value) override;
302
303private:
304 void valueChanged(const QVariant &value);
305 void windowVisibleChanged(bool visible);
306 QSGNode *createSubtree(qreal initialOpacity);
307 void updateSubtree(QSGNode *node, qreal opacity);
308 QSize iconSizeHint() const;
309 inline QImage iconPixmap(const QIcon &icon) const;
310 QIcon loadFromTheme(const QString &iconName) const;
311 QRectF calculateNodeRect();
312 bool isSoftwareRendering() const;
313
314 Kirigami::Platform::PlatformTheme *m_theme = nullptr;
315 Kirigami::Platform::Units *m_units = nullptr;
316 QPointer<QNetworkReply> m_networkReply;
317 QHash<int, bool> m_monochromeHeuristics;
318 QVariant m_source;
319 qreal m_devicePixelRatio = 1.0;
320 Status m_status = Null;
321 bool m_textureChanged = false;
322 bool m_sizeChanged = false;
323 bool m_active;
324 bool m_selected;
325 bool m_isMask;
326 bool m_isMaskHeuristic = false;
327 QImage m_loadedImage;
328 QColor m_color = Qt::transparent;
329 QString m_fallback = QStringLiteral("unknown");
330 QString m_placeholder = QStringLiteral("image-png");
331 QSizeF m_paintedSize;
332
333 QImage m_oldIcon;
334 QImage m_icon;
335
336 // animation on image change
337 QPropertyAnimation *m_animation = nullptr;
338 qreal m_animValue = 1.0;
339 bool m_animated = false;
340 bool m_roundToIconSize = true;
341 bool m_blockNextAnimation = false;
342 QPointer<QQuickWindow> m_window;
343};
344

source code of kirigami/src/primitives/icon.h