1// Copyright (C) 2020 The Qt Company Ltd.
2// Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qicon.h"
6#include "qicon_p.h"
7#include "qiconengine.h"
8#include "qiconengineplugin.h"
9#include "qimagereader.h"
10#include "private/qfactoryloader_p.h"
11#include "private/qiconloader_p.h"
12#include "qpainter.h"
13#include "qfileinfo.h"
14#if QT_CONFIG(mimetype)
15#include <qmimedatabase.h>
16#include <qmimetype.h>
17#endif
18#include "qpixmapcache.h"
19#include "qvariant.h"
20#include "qcache.h"
21#include "qdebug.h"
22#include "qdir.h"
23#include "qpalette.h"
24#include "qmath.h"
25
26#include "private/qhexstring_p.h"
27#include "private/qguiapplication_p.h"
28#include "private/qoffsetstringarray_p.h"
29#include "qpa/qplatformtheme.h"
30
31#ifndef QT_NO_ICON
32QT_BEGIN_NAMESPACE
33
34using namespace Qt::StringLiterals;
35// Convenience class providing a bool read() function.
36namespace {
37class ImageReader
38{
39public:
40 ImageReader(const QString &fileName) : m_reader(fileName), m_atEnd(false) { }
41
42 QByteArray format() const { return m_reader.format(); }
43 bool supportsReadSize() const { return m_reader.supportsOption(option: QImageIOHandler::Size); }
44 QSize size() const { return m_reader.size(); }
45 bool jumpToNextImage() { return m_reader.jumpToNextImage(); }
46 void jumpToImage(int index) { m_reader.jumpToImage(imageNumber: index); }
47
48 bool read(QImage *image)
49 {
50 if (m_atEnd)
51 return false;
52 *image = m_reader.read();
53 if (!image->size().isValid()) {
54 m_atEnd = true;
55 return false;
56 }
57 m_atEnd = !m_reader.jumpToNextImage();
58 return true;
59 }
60
61private:
62 QImageReader m_reader;
63 bool m_atEnd;
64};
65} // namespace
66
67/*!
68 \enum QIcon::Mode
69
70 This enum type describes the mode for which a pixmap is intended
71 to be used. The currently defined modes are:
72
73 \value Normal
74 Display the pixmap when the user is
75 not interacting with the icon, but the
76 functionality represented by the icon is available.
77 \value Disabled
78 Display the pixmap when the
79 functionality represented by the icon is not available.
80 \value Active
81 Display the pixmap when the
82 functionality represented by the icon is available and
83 the user is interacting with the icon, for example, moving the
84 mouse over it or clicking it.
85 \value Selected
86 Display the pixmap when the item represented by the icon is
87 selected.
88*/
89
90/*!
91 \enum QIcon::State
92
93 This enum describes the state for which a pixmap is intended to be
94 used. The \e state can be:
95
96 \value Off Display the pixmap when the widget is in an "off" state
97 \value On Display the pixmap when the widget is in an "on" state
98*/
99
100static int nextSerialNumCounter()
101{
102 Q_CONSTINIT static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0);
103 return 1 + serial.fetchAndAddRelaxed(valueToAdd: 1);
104}
105
106static void qt_cleanup_icon_cache();
107namespace {
108 struct IconCache : public QCache<QString, QIcon>
109 {
110 IconCache()
111 {
112 // ### note: won't readd if QApplication is re-created!
113 qAddPostRoutine(qt_cleanup_icon_cache);
114 }
115 };
116}
117
118Q_GLOBAL_STATIC(IconCache, qtIconCache)
119
120static void qt_cleanup_icon_cache()
121{
122 qtIconCache()->clear();
123}
124
125QIconPrivate::QIconPrivate(QIconEngine *e)
126 : engine(e), ref(1),
127 serialNum(nextSerialNumCounter()),
128 detach_no(0),
129 is_mask(false)
130{
131}
132
133void QIconPrivate::clearIconCache()
134{
135 qt_cleanup_icon_cache();
136}
137
138/*! \internal
139 Computes the displayDevicePixelRatio for a pixmap.
140
141 If displayDevicePixelRatio is 1.0 the reurned value is 1.0, always.
142
143 For a displayDevicePixelRatio of 2.0 the returned value will be between
144 1.0 and 2.0, depending on requestedSize and actualsize:
145 * If actualsize < requestedSize : 1.0 (not enough pixels for a normal-dpi pixmap)
146 * If actualsize == requestedSize * 2.0 : 2.0 (enough pixels for a high-dpi pixmap)
147 * else : a scaled value between 1.0 and 2.0. (pixel count is between normal-dpi and high-dpi)
148*/
149qreal QIconPrivate::pixmapDevicePixelRatio(qreal displayDevicePixelRatio, const QSize &requestedSize, const QSize &actualSize)
150{
151 QSize targetSize = requestedSize * displayDevicePixelRatio;
152 if ((actualSize.width() == targetSize.width() && actualSize.height() <= targetSize.height()) ||
153 (actualSize.width() <= targetSize.width() && actualSize.height() == targetSize.height())) {
154 // Correctly scaled for dpr, just having different aspect ratio
155 return displayDevicePixelRatio;
156 }
157 qreal scale = 0.5 * (qreal(actualSize.width()) / qreal(targetSize.width()) +
158 qreal(actualSize.height() / qreal(targetSize.height())));
159 return qMax(a: qreal(1.0), b: displayDevicePixelRatio *scale);
160}
161
162QPixmapIconEngine::QPixmapIconEngine()
163{
164}
165
166QPixmapIconEngine::QPixmapIconEngine(const QPixmapIconEngine &other)
167 : QIconEngine(other), pixmaps(other.pixmaps)
168{
169}
170
171QPixmapIconEngine::~QPixmapIconEngine()
172{
173}
174
175void QPixmapIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
176{
177 auto paintDevice = painter->device();
178 qreal dpr = paintDevice ? paintDevice->devicePixelRatio() : qApp->devicePixelRatio();
179 QPixmap px = scaledPixmap(size: rect.size(), mode, state, scale: dpr);
180 painter->drawPixmap(r: rect, pm: px);
181}
182
183static inline qint64 area(const QSize &s) { return qint64(s.width()) * s.height(); }
184
185// Returns the smallest of the two that is still larger than or equal to size.
186// Pixmaps at the correct scale are preferred, pixmaps at lower scale are
187// used as fallbacks. We assume that the pixmap set is complete, in the sense
188// that no 2x pixmap is going to be a better match than a 3x pixmap for the the
189// target scale of 3 (It's OK if 3x pixmaps are missing - we'll fall back to
190// the 2x pixmaps then.)
191static QPixmapIconEngineEntry *bestSizeScaleMatch(const QSize &size, qreal scale, QPixmapIconEngineEntry *pa, QPixmapIconEngineEntry *pb)
192{
193 const auto scaleA = pa->pixmap.devicePixelRatio();
194 const auto scaleB = pb->pixmap.devicePixelRatio();
195 // scale: we can only differentiate on scale if the scale differs
196 if (scaleA != scaleB) {
197
198 // Score the pixmaps: 0 is an exact scale match, positive
199 // scores have more detail than requested, negative scores
200 // have less detail than requested.
201 qreal ascore = scaleA - scale;
202 qreal bscore = scaleB - scale;
203
204 // always prefer positive scores to prevent upscaling
205 if ((ascore < 0) != (bscore < 0))
206 return bscore < 0 ? pa : pb;
207 // Take the one closest to 0
208 return (qAbs(t: ascore) < qAbs(t: bscore)) ? pa : pb;
209 }
210
211 qint64 s = area(s: size * scale);
212 if (pa->size == QSize() && pa->pixmap.isNull()) {
213 pa->pixmap = QPixmap(pa->fileName);
214 pa->size = pa->pixmap.size();
215 }
216 qint64 a = area(s: pa->size);
217 if (pb->size == QSize() && pb->pixmap.isNull()) {
218 pb->pixmap = QPixmap(pb->fileName);
219 pb->size = pb->pixmap.size();
220 }
221 qint64 b = area(s: pb->size);
222 qint64 res = a;
223 if (qMin(a,b) >= s)
224 res = qMin(a,b);
225 else
226 res = qMax(a,b);
227 if (res == a)
228 return pa;
229 return pb;
230}
231
232QPixmapIconEngineEntry *QPixmapIconEngine::tryMatch(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state)
233{
234 QPixmapIconEngineEntry *pe = nullptr;
235 for (auto &entry : pixmaps) {
236 if (entry.mode == mode && entry.state == state) {
237 if (pe)
238 pe = bestSizeScaleMatch(size, scale, pa: &entry, pb: pe);
239 else
240 pe = &entry;
241 }
242 }
243 return pe;
244}
245
246
247QPixmapIconEngineEntry *QPixmapIconEngine::bestMatch(const QSize &size, qreal scale, QIcon::Mode mode, QIcon::State state)
248{
249 QPixmapIconEngineEntry *pe = tryMatch(size, scale, mode, state);
250 while (!pe){
251 QIcon::State oppositeState = (state == QIcon::On) ? QIcon::Off : QIcon::On;
252 if (mode == QIcon::Disabled || mode == QIcon::Selected) {
253 QIcon::Mode oppositeMode = (mode == QIcon::Disabled) ? QIcon::Selected : QIcon::Disabled;
254 if ((pe = tryMatch(size, scale, mode: QIcon::Normal, state)))
255 break;
256 if ((pe = tryMatch(size, scale, mode: QIcon::Active, state)))
257 break;
258 if ((pe = tryMatch(size, scale, mode, state: oppositeState)))
259 break;
260 if ((pe = tryMatch(size, scale, mode: QIcon::Normal, state: oppositeState)))
261 break;
262 if ((pe = tryMatch(size, scale, mode: QIcon::Active, state: oppositeState)))
263 break;
264 if ((pe = tryMatch(size, scale, mode: oppositeMode, state)))
265 break;
266 if ((pe = tryMatch(size, scale, mode: oppositeMode, state: oppositeState)))
267 break;
268 } else {
269 QIcon::Mode oppositeMode = (mode == QIcon::Normal) ? QIcon::Active : QIcon::Normal;
270 if ((pe = tryMatch(size, scale, mode: oppositeMode, state)))
271 break;
272 if ((pe = tryMatch(size, scale, mode, state: oppositeState)))
273 break;
274 if ((pe = tryMatch(size, scale, mode: oppositeMode, state: oppositeState)))
275 break;
276 if ((pe = tryMatch(size, scale, mode: QIcon::Disabled, state)))
277 break;
278 if ((pe = tryMatch(size, scale, mode: QIcon::Selected, state)))
279 break;
280 if ((pe = tryMatch(size, scale, mode: QIcon::Disabled, state: oppositeState)))
281 break;
282 if ((pe = tryMatch(size, scale, mode: QIcon::Selected, state: oppositeState)))
283 break;
284 }
285
286 if (!pe)
287 return pe;
288 }
289
290 if (pe->pixmap.isNull()) {
291 // delay-load the image
292 ImageReader imageReader(pe->fileName);
293 QImage image, prevImage;
294 const QSize realSize = size * scale;
295 bool fittingImageFound = false;
296 if (imageReader.supportsReadSize()) {
297 // find the image with the best size without loading the entire image
298 do {
299 fittingImageFound = imageReader.size() == realSize;
300 } while (!fittingImageFound && imageReader.jumpToNextImage());
301 }
302 if (!fittingImageFound) {
303 imageReader.jumpToImage(index: 0);
304 while (imageReader.read(image: &image) && image.size() != realSize)
305 prevImage = image;
306 if (image.isNull())
307 image = prevImage;
308 } else {
309 imageReader.read(image: &image);
310 }
311 if (!image.isNull()) {
312 pe->pixmap.convertFromImage(img: image);
313 if (!pe->pixmap.isNull()) {
314 pe->size = pe->pixmap.size();
315 pe->pixmap.setDevicePixelRatio(scale);
316 }
317 }
318 if (!pe->size.isValid()) {
319 removePixmapEntry(pe);
320 pe = nullptr;
321 }
322 }
323
324 return pe;
325}
326
327QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
328{
329 return scaledPixmap(size, mode, state, scale: 1.0);
330}
331
332QPixmap QPixmapIconEngine::scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale)
333{
334 QPixmap pm;
335 QPixmapIconEngineEntry *pe = bestMatch(size, scale, mode, state);
336 if (pe)
337 pm = pe->pixmap;
338 else
339 return pm;
340
341 if (pm.isNull()) {
342 removePixmapEntry(pe);
343 if (pixmaps.isEmpty())
344 return pm;
345 return scaledPixmap(size, mode, state, scale);
346 }
347
348 const auto actualSize = adjustSize(expectedSize: size * scale, size: pm.size());
349 const auto calculatedDpr = QIconPrivate::pixmapDevicePixelRatio(displayDevicePixelRatio: scale, requestedSize: size, actualSize);
350 QString key = "qt_"_L1
351 % HexString<quint64>(pm.cacheKey())
352 % HexString<quint8>(pe->mode)
353 % HexString<quint64>(QGuiApplication::palette().cacheKey())
354 % HexString<uint>(actualSize.width())
355 % HexString<uint>(actualSize.height())
356 % HexString<quint16>(qRound(d: calculatedDpr * 1000));
357
358 if (mode == QIcon::Active) {
359 if (QPixmapCache::find(key: key % HexString<quint8>(mode), pixmap: &pm))
360 return pm; // horray
361 if (QPixmapCache::find(key: key % HexString<quint8>(QIcon::Normal), pixmap: &pm)) {
362 QPixmap active = pm;
363 if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp))
364 active = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(o: guiApp))->applyQIconStyleHelper(QIcon::Active, basePixmap: pm);
365 if (pm.cacheKey() == active.cacheKey())
366 return pm;
367 }
368 }
369
370 if (!QPixmapCache::find(key: key % HexString<quint8>(mode), pixmap: &pm)) {
371 if (pm.size() != actualSize)
372 pm = pm.scaled(s: actualSize, aspectMode: Qt::IgnoreAspectRatio, mode: Qt::SmoothTransformation);
373 if (pe->mode != mode && mode != QIcon::Normal) {
374 QPixmap generated = pm;
375 if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp))
376 generated = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(o: guiApp))->applyQIconStyleHelper(mode, basePixmap: pm);
377 if (!generated.isNull())
378 pm = generated;
379 }
380 pm.setDevicePixelRatio(calculatedDpr);
381 QPixmapCache::insert(key: key % HexString<quint8>(mode), pixmap: pm);
382 }
383 return pm;
384}
385
386QSize QPixmapIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
387{
388 QSize actualSize;
389
390 // The returned actual size is the size in device independent pixels,
391 // so we limit the search to scale 1 and assume that e.g. @2x versions
392 // does not proviode extra actual sizes not also provided by the 1x versions.
393 qreal scale = 1;
394
395 if (QPixmapIconEngineEntry *pe = bestMatch(size, scale, mode, state))
396 actualSize = pe->size;
397
398 return adjustSize(expectedSize: size, size: actualSize);
399}
400
401QList<QSize> QPixmapIconEngine::availableSizes(QIcon::Mode mode, QIcon::State state)
402{
403 QList<QSize> sizes;
404 for (QPixmapIconEngineEntry &pe : pixmaps) {
405 if (pe.mode != mode || pe.state != state)
406 continue;
407 if (pe.size.isEmpty() && pe.pixmap.isNull()) {
408 pe.pixmap = QPixmap(pe.fileName);
409 pe.size = pe.pixmap.size();
410 }
411 if (!pe.size.isEmpty() && !sizes.contains(t: pe.size))
412 sizes.push_back(t: pe.size);
413 }
414 return sizes;
415}
416
417void QPixmapIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state)
418{
419 if (!pixmap.isNull()) {
420 QPixmapIconEngineEntry *pe = tryMatch(size: pixmap.size() / pixmap.devicePixelRatio(),
421 scale: pixmap.devicePixelRatio(), mode, state);
422 if (pe && pe->size == pixmap.size() && pe->pixmap.devicePixelRatio() == pixmap.devicePixelRatio()) {
423 pe->pixmap = pixmap;
424 pe->fileName.clear();
425 } else {
426 pixmaps += QPixmapIconEngineEntry(pixmap, mode, state);
427 }
428 }
429}
430
431// Read out original image depth as set by ICOReader
432static inline int origIcoDepth(const QImage &image)
433{
434 const QString s = image.text(QStringLiteral("_q_icoOrigDepth"));
435 return s.isEmpty() ? 32 : s.toInt();
436}
437
438static inline int findBySize(const QList<QImage> &images, const QSize &size)
439{
440 for (qsizetype i = 0; i < images.size(); ++i) {
441 if (images.at(i).size() == size)
442 return i;
443 }
444 return -1;
445}
446
447void QPixmapIconEngine::addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state)
448{
449 if (fileName.isEmpty())
450 return;
451 const QString abs = fileName.startsWith(c: u':') ? fileName : QFileInfo(fileName).absoluteFilePath();
452 const bool ignoreSize = !size.isValid();
453 ImageReader imageReader(abs);
454 const QByteArray format = imageReader.format();
455 if (format.isEmpty()) // Device failed to open or unsupported format.
456 return;
457 QImage image;
458 if (format != "ico") {
459 if (ignoreSize) { // No size specified: Add all images.
460 if (imageReader.supportsReadSize()) {
461 do {
462 pixmaps += QPixmapIconEngineEntry(abs, imageReader.size(), mode, state);
463 } while (imageReader.jumpToNextImage());
464 } else {
465 while (imageReader.read(image: &image))
466 pixmaps += QPixmapIconEngineEntry(abs, image, mode, state);
467 }
468 } else {
469 pixmaps += QPixmapIconEngineEntry(abs, size, mode, state);
470 }
471 return;
472 }
473 // Special case for reading Windows ".ico" files. Historically (QTBUG-39287),
474 // these files may contain low-resolution images. As this information is lost,
475 // ICOReader sets the original format as an image text key value. Read all matching
476 // images into a list trying to find the highest quality per size.
477 QList<QImage> icoImages;
478 while (imageReader.read(image: &image)) {
479 if (ignoreSize || image.size() == size) {
480 const int position = findBySize(images: icoImages, size: image.size());
481 if (position >= 0) { // Higher quality available? -> replace.
482 if (origIcoDepth(image) > origIcoDepth(image: icoImages.at(i: position)))
483 icoImages[position] = image;
484 } else {
485 icoImages.append(t: image);
486 }
487 }
488 }
489 for (const QImage &i : std::as_const(t&: icoImages))
490 pixmaps += QPixmapIconEngineEntry(abs, i, mode, state);
491 if (icoImages.isEmpty() && !ignoreSize) // Add placeholder with the filename and empty pixmap for the size.
492 pixmaps += QPixmapIconEngineEntry(abs, size, mode, state);
493}
494
495bool QPixmapIconEngine::isNull()
496{
497 return pixmaps.isEmpty();
498}
499
500QString QPixmapIconEngine::key() const
501{
502 return "QPixmapIconEngine"_L1;
503}
504
505QIconEngine *QPixmapIconEngine::clone() const
506{
507 return new QPixmapIconEngine(*this);
508}
509
510bool QPixmapIconEngine::read(QDataStream &in)
511{
512 int num_entries;
513 QPixmap pm;
514 QString fileName;
515 QSize sz;
516 uint mode;
517 uint state;
518
519 in >> num_entries;
520 for (int i=0; i < num_entries; ++i) {
521 if (in.atEnd()) {
522 pixmaps.clear();
523 return false;
524 }
525 in >> pm;
526 in >> fileName;
527 in >> sz;
528 in >> mode;
529 in >> state;
530 if (pm.isNull()) {
531 addFile(fileName, size: sz, mode: QIcon::Mode(mode), state: QIcon::State(state));
532 } else {
533 QPixmapIconEngineEntry pe(fileName, sz, QIcon::Mode(mode), QIcon::State(state));
534 pe.pixmap = pm;
535 pixmaps += pe;
536 }
537 }
538 return true;
539}
540
541bool QPixmapIconEngine::write(QDataStream &out) const
542{
543 int num_entries = pixmaps.size();
544 out << num_entries;
545 for (int i=0; i < num_entries; ++i) {
546 if (pixmaps.at(i).pixmap.isNull())
547 out << QPixmap(pixmaps.at(i).fileName);
548 else
549 out << pixmaps.at(i).pixmap;
550 out << pixmaps.at(i).fileName;
551 out << pixmaps.at(i).size;
552 out << (uint) pixmaps.at(i).mode;
553 out << (uint) pixmaps.at(i).state;
554 }
555 return true;
556}
557
558Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, iceLoader,
559 (QIconEngineFactoryInterface_iid, "/iconengines"_L1, Qt::CaseInsensitive))
560
561QFactoryLoader *qt_iconEngineFactoryLoader()
562{
563 return iceLoader();
564}
565
566
567/*!
568 \class QIcon
569
570 \brief The QIcon class provides scalable icons in different modes
571 and states.
572
573 \ingroup painting
574 \ingroup shared
575 \inmodule QtGui
576
577 A QIcon can generate smaller, larger, active, and disabled pixmaps
578 from the set of pixmaps it is given. Such pixmaps are used by Qt
579 UI components to show an icon representing a particular action.
580
581 \section1 Creating an icon from image files
582
583 The simplest way to construct a QIcon is to create one from one or
584 several image files or resources. For example:
585
586 \snippet code/src_gui_image_qicon.cpp 0
587
588 QIcon can store several images for different states, and Qt will
589 select the image that is the closest match for the action's current
590 state.
591
592 \snippet code/src_gui_image_qicon.cpp addFile
593
594 Qt will generate the required icon styles and sizes when needed,
595 e.g. the pixmap for the QIcon::Disabled state might be generated by
596 graying out one of the provided pixmaps.
597
598 To clear the icon, simply set a null icon in its place:
599
600 \snippet code/src_gui_image_qicon.cpp 1
601
602 Use the QImageReader::supportedImageFormats() and
603 QImageWriter::supportedImageFormats() functions to retrieve a
604 complete list of the supported file formats.
605
606 \section1 Creating an icon from a theme or icon library
607
608 The most convenient way to construct an icon is by using the
609 \l{QIcon::}{fromTheme()} factory function. Qt implements access to
610 the native icon library on platforms that support the
611 \l {Freedesktop Icon Theme Specification}. Since Qt 6.7, Qt also
612 provides access to the native icon library on macOS, iOS, and
613 Windows 10 and 11. On Android, Qt can access icons from the Material
614 design system as long as the
615 \l{https://github.com/google/material-design-icons/tree/master/font}
616 {MaterialIcons-Regular} font is available on the system, or bundled
617 as a resource at \c{:/qt-project.org/icons/MaterialIcons-Regular.ttf}
618 with the application.
619
620 \snippet code/src_gui_image_qicon.cpp fromTheme
621
622 Applications can use the same theming specification to provide
623 their own icon library. See below for an example theme description
624 and the corresponding directory structure for the image files.
625 Icons from an application-provided theme take precedence over the
626 native icon library.
627
628 \section1 Icon Engines
629
630 Internally, QIcon instantiates an \l {QIconEngine} {icon engine}
631 backend to handle and render the icon images. The type of icon
632 engine is determined by the first file or pixmap or theme added to a
633 QIcon object. Additional files or pixmaps will then be handled by
634 the same engine.
635
636 Icon engines differ in the way they handle and render icons. The
637 default pixmap-based engine only deals with fixed images, while the
638 QtSvg module provides an icon engine that can re-render the provided
639 vector graphics files at the requested size for better quality. The
640 theme icon engines will typically only provide images from native
641 platform icon library, and ignore any added files or pixmaps.
642
643 In addition, it is possible to provide custom icon engines. This
644 allows applications to customize every aspect of generated
645 icons. With QIconEnginePlugin it is possible to register different
646 icon engines for different file suffixes, making it possible for
647 third parties to provide additional icon engines to those included
648 with Qt.
649
650 \section1 Making Classes that Use QIcon
651
652 If you write your own widgets that have an option to set a small
653 pixmap, consider allowing a QIcon to be set for that pixmap. The
654 Qt class QToolButton is an example of such a widget.
655
656 Provide a method to set a QIcon, and paint the QIcon with
657 \l{QIcon::}{paint}, choosing the appropriate parameters based
658 on the current state of your widget. For example:
659
660 \snippet code/src_gui_image_qicon.cpp 2
661
662 When you retrieve a pixmap using pixmap(QSize, Mode, State), and no
663 pixmap for this given size, mode and state has been added with
664 addFile() or addPixmap(), then QIcon will generate one on the
665 fly. This pixmap generation happens in a QIconEngine. The default
666 engine scales pixmaps down if required, but never up, and it uses
667 the current style to calculate a disabled appearance.
668
669 You might also make use of the \c Active mode, perhaps making your
670 widget \c Active when the mouse is over the widget (see \l
671 QWidget::enterEvent()), while the mouse is pressed pending the
672 release that will activate the function, or when it is the currently
673 selected item. If the widget can be toggled, the "On" mode might be
674 used to draw a different icon.
675
676 \image icon.png QIcon
677
678 \note QIcon needs a QGuiApplication instance before the icon is created.
679
680 \section1 High DPI Icons
681
682 Icons that are provided by the native icon library are usually based
683 on vector graphics, and will automatically be rendered in the appropriate
684 resolution.
685
686 When providing your own image files via \l addFile(), then QIcon will
687 use Qt's \l {High Resolution Versions of Images}{"@nx" high DPI syntax}.
688 This is useful if you have your own custom directory structure and do not
689 use follow \l {Freedesktop Icon Theme Specification}.
690
691 When providing an application theme, then you need to follow the Icon Theme
692 Specification to specify which files to use for different resolutions.
693 To make QIcon use the high DPI version of an image, add an additional entry
694 to the appropriate \c index.theme file:
695
696 \badcode
697 [Icon Theme]
698 Name=Test
699 Comment=Test Theme
700
701 Directories=32x32/actions,32x32@2/actions
702
703 [32x32/actions]
704 Size=32
705 Context=Actions
706 Type=Fixed
707
708 # High DPI version of the entry above.
709 [32x32@2/actions]
710 Size=32
711 Scale=2
712 Type=Fixed
713 \endcode
714
715 Your icon theme directory would then look something like this:
716
717 \badcode
718 ├── 32x32
719 │ └── actions
720 │ └── appointment-new.png
721 ├── 32x32@2
722 │ └── actions
723 │ └── appointment-new.png
724 └── index.theme
725 \endcode
726*/
727
728
729/*!
730 Constructs a null icon.
731*/
732QIcon::QIcon() noexcept
733 : d(nullptr)
734{
735}
736
737/*!
738 Constructs an icon from a \a pixmap.
739 */
740QIcon::QIcon(const QPixmap &pixmap)
741 :d(nullptr)
742{
743 addPixmap(pixmap);
744}
745
746/*!
747 Constructs a copy of \a other. This is very fast.
748*/
749QIcon::QIcon(const QIcon &other)
750 :d(other.d)
751{
752 if (d)
753 d->ref.ref();
754}
755
756/*!
757 \fn QIcon::QIcon(QIcon &&other)
758
759 Move-constructs a QIcon instance, making it point to the same object
760 that \a other was pointing to.
761*/
762
763/*!
764 Constructs an icon from the file with the given \a fileName. The
765 file will be loaded on demand.
766
767 If \a fileName contains a relative path (e.g. the filename only)
768 the relevant file must be found relative to the runtime working
769 directory.
770
771 The file name can refer to an actual file on disk or to
772 one of the application's embedded resources. See the
773 \l{resources.html}{Resource System} overview for details on how to
774 embed images and other resource files in the application's
775 executable.
776
777 Use the QImageReader::supportedImageFormats() and
778 QImageWriter::supportedImageFormats() functions to retrieve a
779 complete list of the supported file formats.
780*/
781QIcon::QIcon(const QString &fileName)
782 : d(nullptr)
783{
784 addFile(fileName);
785}
786
787
788/*!
789 Creates an icon with a specific icon \a engine. The icon takes
790 ownership of the engine.
791*/
792QIcon::QIcon(QIconEngine *engine)
793 :d(new QIconPrivate(engine))
794{
795}
796
797/*!
798 Destroys the icon.
799*/
800QIcon::~QIcon()
801{
802 if (d && !d->ref.deref())
803 delete d;
804}
805
806/*!
807 Assigns the \a other icon to this icon and returns a reference to
808 this icon.
809*/
810QIcon &QIcon::operator=(const QIcon &other)
811{
812 if (other.d)
813 other.d->ref.ref();
814 if (d && !d->ref.deref())
815 delete d;
816 d = other.d;
817 return *this;
818}
819
820/*!
821 \fn QIcon &QIcon::operator=(QIcon &&other)
822
823 Move-assigns \a other to this QIcon instance.
824
825 \since 5.2
826*/
827
828/*!
829 \fn void QIcon::swap(QIcon &other)
830
831 Swaps icon \a other with this icon. This operation is very
832 fast and never fails.
833*/
834
835/*!
836 Returns the icon as a QVariant.
837*/
838QIcon::operator QVariant() const
839{
840 return QVariant::fromValue(value: *this);
841}
842
843/*!
844 Returns a number that identifies the contents of this QIcon
845 object. Distinct QIcon objects can have the same key if
846 they refer to the same contents.
847
848 The cacheKey() will change when the icon is altered via
849 addPixmap() or addFile().
850
851 Cache keys are mostly useful in conjunction with caching.
852
853 \sa QPixmap::cacheKey()
854*/
855qint64 QIcon::cacheKey() const
856{
857 if (!d)
858 return 0;
859 return (((qint64) d->serialNum) << 32) | ((qint64) (d->detach_no));
860}
861
862/*!
863 Returns a pixmap with the requested \a size, \a mode, and \a
864 state, generating one if necessary. The pixmap might be smaller than
865 requested, but never larger, unless the device-pixel ratio of the returned
866 pixmap is larger than 1.
867
868 \sa actualSize(), paint()
869*/
870QPixmap QIcon::pixmap(const QSize &size, Mode mode, State state) const
871{
872 if (!d)
873 return QPixmap();
874 const qreal dpr = -1; // don't know target dpr
875 return pixmap(size, devicePixelRatio: dpr, mode, state);
876}
877
878/*!
879 \fn QPixmap QIcon::pixmap(int w, int h, Mode mode = Normal, State state = Off) const
880
881 \overload
882
883 Returns a pixmap of size QSize(\a w, \a h). The pixmap might be smaller than
884 requested, but never larger, unless the device-pixel ratio of the returned
885 pixmap is larger than 1.
886*/
887
888/*!
889 \fn QPixmap QIcon::pixmap(int extent, Mode mode = Normal, State state = Off) const
890
891 \overload
892
893 Returns a pixmap of size QSize(\a extent, \a extent). The pixmap might be smaller
894 than requested, but never larger, unless the device-pixel ratio of the returned
895 pixmap is larger than 1.
896*/
897
898/*!
899 \overload
900 \since 6.0
901
902 Returns a pixmap with the requested \a size, \a devicePixelRatio, \a mode, and \a
903 state, generating one with the given \a mode and \a state if necessary. The pixmap
904 might be smaller than requested, but never larger, unless the device-pixel ratio
905 of the returned pixmap is larger than 1.
906
907 \note Prior to Qt 6.8 this function wronlgy passed the device dependent pixmap size to
908 QIconEngine::scaledPixmap(), since Qt 6.8 it's the device independent size (not scaled
909 with the \a devicePixelRatio).
910
911 \sa actualSize(), paint()
912*/
913QPixmap QIcon::pixmap(const QSize &size, qreal devicePixelRatio, Mode mode, State state) const
914{
915 if (!d)
916 return QPixmap();
917
918 // Use the global devicePixelRatio if the caller does not know the target dpr
919 if (devicePixelRatio == -1)
920 devicePixelRatio = qApp->devicePixelRatio();
921
922 // Handle the simple normal-dpi case
923 if (!(devicePixelRatio > 1.0)) {
924 QPixmap pixmap = d->engine->pixmap(size, mode, state);
925 pixmap.setDevicePixelRatio(1.0);
926 return pixmap;
927 }
928
929 // Try get a pixmap that is big enough to be displayed at device pixel resolution.
930 QPixmap pixmap = d->engine->scaledPixmap(size, mode, state, scale: devicePixelRatio);
931 pixmap.setDevicePixelRatio(d->pixmapDevicePixelRatio(displayDevicePixelRatio: devicePixelRatio, requestedSize: size, actualSize: pixmap.size()));
932 return pixmap;
933}
934
935#if QT_DEPRECATED_SINCE(6, 0)
936/*!
937 \since 5.1
938 \deprecated [6.0] Use pixmap(size, devicePixelRatio) instead.
939
940 Returns a pixmap with the requested \a window \a size, \a mode, and \a
941 state, generating one if necessary.
942
943 The pixmap can be smaller than the requested size. If \a window is on
944 a high-dpi display the pixmap can be larger. In that case it will have
945 a devicePixelRatio larger than 1.
946
947 \sa actualSize(), paint()
948*/
949
950QPixmap QIcon::pixmap(QWindow *window, const QSize &size, Mode mode, State state) const
951{
952 if (!d)
953 return QPixmap();
954
955 qreal devicePixelRatio = window ? window->devicePixelRatio() : qApp->devicePixelRatio();
956 return pixmap(size, devicePixelRatio, mode, state);
957}
958#endif
959
960
961/*! Returns the actual size of the icon for the requested \a size, \a
962 mode, and \a state. The result might be smaller than requested, but
963 never larger. The returned size is in device-independent pixels (This
964 is relevant for high-dpi pixmaps.)
965
966 \sa pixmap(), paint()
967*/
968QSize QIcon::actualSize(const QSize &size, Mode mode, State state) const
969{
970 if (!d)
971 return QSize();
972
973 const qreal devicePixelRatio = qApp->devicePixelRatio();
974
975 // Handle the simple normal-dpi case:
976 if (!(devicePixelRatio > 1.0))
977 return d->engine->actualSize(size, mode, state);
978
979 const QSize actualSize = d->engine->actualSize(size: size * devicePixelRatio, mode, state);
980 return actualSize / d->pixmapDevicePixelRatio(displayDevicePixelRatio: devicePixelRatio, requestedSize: size, actualSize);
981}
982
983#if QT_DEPRECATED_SINCE(6, 0)
984/*!
985 \since 5.1
986 \deprecated [6.0] Use actualSize(size) instead.
987
988 Returns the actual size of the icon for the requested \a window \a size, \a
989 mode, and \a state.
990
991 The pixmap can be smaller than the requested size. The returned size
992 is in device-independent pixels (This is relevant for high-dpi pixmaps.)
993
994 \sa actualSize(), pixmap(), paint()
995*/
996
997QSize QIcon::actualSize(QWindow *window, const QSize &size, Mode mode, State state) const
998{
999 if (!d)
1000 return QSize();
1001
1002 qreal devicePixelRatio = window ? window->devicePixelRatio() : qApp->devicePixelRatio();
1003
1004 // Handle the simple normal-dpi case:
1005 if (!(devicePixelRatio > 1.0))
1006 return d->engine->actualSize(size, mode, state);
1007
1008 QSize actualSize = d->engine->actualSize(size: size * devicePixelRatio, mode, state);
1009 return actualSize / d->pixmapDevicePixelRatio(displayDevicePixelRatio: devicePixelRatio, requestedSize: size, actualSize);
1010}
1011#endif
1012
1013/*!
1014 Uses the \a painter to paint the icon with specified \a alignment,
1015 required \a mode, and \a state into the rectangle \a rect.
1016
1017 \sa actualSize(), pixmap()
1018*/
1019void QIcon::paint(QPainter *painter, const QRect &rect, Qt::Alignment alignment, Mode mode, State state) const
1020{
1021 if (!d || !painter)
1022 return;
1023
1024 // Copy of QStyle::alignedRect
1025 const QSize size = d->engine->actualSize(size: rect.size(), mode, state);
1026 alignment = QGuiApplicationPrivate::visualAlignment(direction: painter->layoutDirection(), alignment);
1027 int x = rect.x();
1028 int y = rect.y();
1029 int w = size.width();
1030 int h = size.height();
1031 if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter)
1032 y += rect.size().height()/2 - h/2;
1033 else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom)
1034 y += rect.size().height() - h;
1035 if ((alignment & Qt::AlignRight) == Qt::AlignRight)
1036 x += rect.size().width() - w;
1037 else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter)
1038 x += rect.size().width()/2 - w/2;
1039 QRect alignedRect(x, y, w, h);
1040
1041 d->engine->paint(painter, rect: alignedRect, mode, state);
1042}
1043
1044/*!
1045 \fn void QIcon::paint(QPainter *painter, int x, int y, int w, int h, Qt::Alignment alignment,
1046 Mode mode, State state) const
1047
1048 \overload
1049
1050 Paints the icon into the rectangle QRect(\a x, \a y, \a w, \a h).
1051*/
1052
1053/*!
1054 Returns \c true if the icon is empty; otherwise returns \c false.
1055
1056 An icon is empty if it has neither a pixmap nor a filename.
1057
1058 Note: Even a non-null icon might not be able to create valid
1059 pixmaps, eg. if the file does not exist or cannot be read.
1060*/
1061bool QIcon::isNull() const
1062{
1063 return !d || d->engine->isNull();
1064}
1065
1066/*!\internal
1067 */
1068bool QIcon::isDetached() const
1069{
1070 return !d || d->ref.loadRelaxed() == 1;
1071}
1072
1073/*! \internal
1074 */
1075void QIcon::detach()
1076{
1077 if (d) {
1078 if (d->engine->isNull()) {
1079 if (!d->ref.deref())
1080 delete d;
1081 d = nullptr;
1082 return;
1083 } else if (d->ref.loadRelaxed() != 1) {
1084 QIconPrivate *x = new QIconPrivate(d->engine->clone());
1085 if (!d->ref.deref())
1086 delete d;
1087 d = x;
1088 }
1089 ++d->detach_no;
1090 }
1091}
1092
1093/*!
1094 Adds \a pixmap to the icon, as a specialization for \a mode and
1095 \a state.
1096
1097 Custom icon engines are free to ignore additionally added
1098 pixmaps.
1099
1100 \sa addFile()
1101*/
1102void QIcon::addPixmap(const QPixmap &pixmap, Mode mode, State state)
1103{
1104 if (pixmap.isNull())
1105 return;
1106 detach();
1107 if (!d)
1108 d = new QIconPrivate(new QPixmapIconEngine);
1109 d->engine->addPixmap(pixmap, mode, state);
1110}
1111
1112static QIconEngine *iconEngineFromSuffix(const QString &fileName, const QString &suffix)
1113{
1114 if (!suffix.isEmpty()) {
1115 const int index = iceLoader()->indexOf(needle: suffix);
1116 if (index != -1) {
1117 if (QIconEnginePlugin *factory = qobject_cast<QIconEnginePlugin*>(object: iceLoader()->instance(index))) {
1118 return factory->create(filename: fileName);
1119 }
1120 }
1121 }
1122 return nullptr;
1123}
1124
1125/*! Adds an image from the file with the given \a fileName to the
1126 icon, as a specialization for \a size, \a mode and \a state. The
1127 file will be loaded on demand. Note: custom icon engines are free
1128 to ignore additionally added pixmaps.
1129
1130 If \a fileName contains a relative path (e.g. the filename only)
1131 the relevant file must be found relative to the runtime working
1132 directory.
1133
1134 The file name can refer to an actual file on disk or to
1135 one of the application's embedded resources. See the
1136 \l{resources.html}{Resource System} overview for details on how to
1137 embed images and other resource files in the application's
1138 executable.
1139
1140 Use the QImageReader::supportedImageFormats() and
1141 QImageWriter::supportedImageFormats() functions to retrieve a
1142 complete list of the supported file formats.
1143
1144 If a high resolution version of the image exists (identified by
1145 the suffix \c @2x on the base name), it is automatically loaded
1146 and added with the \e{device pixel ratio} set to a value of 2.
1147 This can be disabled by setting the environment variable
1148 \c QT_HIGHDPI_DISABLE_2X_IMAGE_LOADING (see QImageReader).
1149
1150 \note When you add a non-empty filename to a QIcon, the icon becomes
1151 non-null, even if the file doesn't exist or points to a corrupt file.
1152
1153 \sa addPixmap(), QPixmap::devicePixelRatio()
1154 */
1155void QIcon::addFile(const QString &fileName, const QSize &size, Mode mode, State state)
1156{
1157 if (fileName.isEmpty())
1158 return;
1159 detach();
1160 bool alreadyAdded = false;
1161 if (!d) {
1162
1163 QFileInfo info(fileName);
1164 QString suffix = info.suffix();
1165#if QT_CONFIG(mimetype)
1166 if (suffix.isEmpty())
1167 suffix = QMimeDatabase().mimeTypeForFile(fileInfo: info).preferredSuffix(); // determination from contents
1168#endif // mimetype
1169 QIconEngine *engine = iconEngineFromSuffix(fileName, suffix);
1170 if (engine)
1171 alreadyAdded = !engine->isNull();
1172 d = new QIconPrivate(engine ? engine : new QPixmapIconEngine);
1173 }
1174 if (!alreadyAdded)
1175 d->engine->addFile(fileName, size, mode, state);
1176
1177 // Check if a "@Nx" file exists and add it.
1178 QString atNxFileName = qt_findAtNxFile(baseFileName: fileName, qApp->devicePixelRatio());
1179 if (atNxFileName != fileName)
1180 d->engine->addFile(fileName: atNxFileName, size, mode, state);
1181}
1182
1183/*!
1184 Returns a list of available icon sizes for the specified \a mode and
1185 \a state.
1186*/
1187QList<QSize> QIcon::availableSizes(Mode mode, State state) const
1188{
1189 if (!d || !d->engine)
1190 return QList<QSize>();
1191 return d->engine->availableSizes(mode, state);
1192}
1193
1194/*!
1195 Returns the name used to create the icon, if available.
1196
1197 Depending on the way the icon was created, it may have an associated
1198 name. This is the case for icons created with fromTheme().
1199
1200 \sa fromTheme(), QIconEngine::iconName()
1201*/
1202QString QIcon::name() const
1203{
1204 if (!d || !d->engine)
1205 return QString();
1206 return d->engine->iconName();
1207}
1208
1209/*!
1210 Sets the search paths for icon themes to \a paths.
1211
1212 The content of \a paths should follow the theme format
1213 documented by setThemeName().
1214
1215 \sa themeSearchPaths(), fromTheme(), setThemeName()
1216*/
1217void QIcon::setThemeSearchPaths(const QStringList &paths)
1218{
1219 QIconLoader::instance()->setThemeSearchPath(paths);
1220}
1221
1222/*!
1223 Returns the search paths for icon themes.
1224
1225 The default search paths will be defined by the platform.
1226 All platforms will also have the resource directory \c{:\icons} as a fallback.
1227
1228 \sa setThemeSearchPaths(), fromTheme(), setThemeName()
1229*/
1230QStringList QIcon::themeSearchPaths()
1231{
1232 return QIconLoader::instance()->themeSearchPaths();
1233}
1234
1235/*!
1236 \since 5.11
1237
1238 Returns the fallback search paths for icons.
1239
1240 The fallback search paths are consulted for standalone
1241 icon files if the \l{themeName()}{current icon theme}
1242 or \l{fallbackThemeName()}{fallback icon theme} do
1243 not provide results for an icon lookup.
1244
1245 If not set, the fallback search paths will be defined
1246 by the platform.
1247
1248 \sa setFallbackSearchPaths(), themeSearchPaths()
1249*/
1250QStringList QIcon::fallbackSearchPaths()
1251{
1252 return QIconLoader::instance()->fallbackSearchPaths();
1253}
1254
1255/*!
1256 \since 5.11
1257
1258 Sets the fallback search paths for icons to \a paths.
1259
1260 The fallback search paths are consulted for standalone
1261 icon files if the \l{themeName()}{current icon theme}
1262 or \l{fallbackThemeName()}{fallback icon theme} do
1263 not provide results for an icon lookup.
1264
1265 For example:
1266
1267 \snippet code/src_gui_image_qicon.cpp 5
1268
1269 \sa fallbackSearchPaths(), setThemeSearchPaths()
1270*/
1271void QIcon::setFallbackSearchPaths(const QStringList &paths)
1272{
1273 QIconLoader::instance()->setFallbackSearchPaths(paths);
1274}
1275
1276/*!
1277 Sets the current icon theme to \a name.
1278
1279 The theme will be will be looked up in themeSearchPaths().
1280
1281 At the moment the only supported icon theme format is the
1282 \l{Freedesktop Icon Theme Specification}. The \a name should
1283 correspond to a directory name in the themeSearchPath()
1284 containing an \c index.theme file describing its contents.
1285
1286 \sa themeSearchPaths(), themeName(),
1287 {Freedesktop Icon Theme Specification}
1288*/
1289void QIcon::setThemeName(const QString &name)
1290{
1291 QIconLoader::instance()->setThemeName(name);
1292}
1293
1294/*!
1295 Returns the name of the current icon theme.
1296
1297 If not set, the current icon theme will be defined by the
1298 platform.
1299
1300 \note Platform icon themes are only implemented on
1301 \l{Freedesktop} based systems at the moment, and the
1302 icon theme depends on your desktop settings.
1303
1304 \sa setThemeName(), themeSearchPaths(), fromTheme(),
1305 hasThemeIcon()
1306*/
1307QString QIcon::themeName()
1308{
1309 return QIconLoader::instance()->themeName();
1310}
1311
1312/*!
1313 \since 5.12
1314
1315 Returns the name of the fallback icon theme.
1316
1317 If not set, the fallback icon theme will be defined by the
1318 platform.
1319
1320 \note Platform fallback icon themes are only implemented on
1321 \l{Freedesktop} based systems at the moment, and the
1322 icon theme depends on your desktop settings.
1323
1324 \sa setFallbackThemeName(), themeName()
1325*/
1326QString QIcon::fallbackThemeName()
1327{
1328 return QIconLoader::instance()->fallbackThemeName();
1329}
1330
1331/*!
1332 \since 5.12
1333
1334 Sets the fallback icon theme to \a name.
1335
1336 The fallback icon theme is consulted for icons not provided by
1337 the \l{themeName()}{current icon theme}, or if the \l{themeName()}
1338 {current icon theme} does not exist.
1339
1340 The \a name should correspond to theme in the same format
1341 as documented by setThemeName(), and will be looked up
1342 in themeSearchPaths().
1343
1344 \note Fallback icon themes should be set before creating
1345 QGuiApplication, to ensure correct initialization.
1346
1347 \sa fallbackThemeName(), themeSearchPaths(), themeName()
1348*/
1349void QIcon::setFallbackThemeName(const QString &name)
1350{
1351 QIconLoader::instance()->setFallbackThemeName(name);
1352}
1353
1354/*!
1355 Returns the QIcon corresponding to \a name in the
1356 \l{themeName()}{current icon theme}.
1357
1358 If the current theme does not provide an icon for \a name,
1359 the \l{fallbackThemeName()}{fallback icon theme} is consulted,
1360 before falling back to looking up standalone icon files in the
1361 \l{QIcon::fallbackSearchPaths()}{fallback icon search path}.
1362 Finally, the platform's native icon library is consulted.
1363
1364 To fetch an icon from the current icon theme:
1365
1366 \snippet code/src_gui_image_qicon.cpp fromTheme
1367
1368 If an \l{themeName()}{icon theme} has not been explicitly
1369 set via setThemeName() a platform defined icon theme will
1370 be used.
1371
1372 \sa themeName(), fallbackThemeName(), setThemeName(), themeSearchPaths(), fallbackSearchPaths(),
1373 {Freedesktop Icon Naming Specification}
1374*/
1375QIcon QIcon::fromTheme(const QString &name)
1376{
1377
1378 if (QIcon *cachedIcon = qtIconCache()->object(key: name))
1379 return *cachedIcon;
1380
1381 if (QDir::isAbsolutePath(path: name))
1382 return QIcon(name);
1383
1384 QIcon icon(new QThemeIconEngine(name));
1385 qtIconCache()->insert(key: name, object: new QIcon(icon));
1386 return icon;
1387}
1388
1389/*!
1390 \overload
1391
1392 Returns the QIcon corresponding to \a name in the
1393 \l{themeName()}{current icon theme}.
1394
1395 If the current theme does not provide an icon for \a name,
1396 the \l{fallbackThemeName()}{fallback icon theme} is consulted,
1397 before falling back to looking up standalone icon files in the
1398 \l{QIcon::fallbackSearchPaths()}{fallback icon search path}.
1399 Finally, the platform's native icon library is consulted.
1400
1401 If no icon is found \a fallback is returned.
1402
1403 This is useful to provide a guaranteed fallback, regardless of
1404 whether the current set of icon themes and fallbacks paths
1405 support the requested icon.
1406
1407 For example:
1408
1409 \snippet code/src_gui_image_qicon.cpp 4
1410
1411 \sa fallbackThemeName(), fallbackSearchPaths()
1412*/
1413QIcon QIcon::fromTheme(const QString &name, const QIcon &fallback)
1414{
1415 QIcon icon = fromTheme(name);
1416
1417 if (icon.isNull() || icon.availableSizes().isEmpty())
1418 return fallback;
1419
1420 return icon;
1421}
1422
1423/*!
1424 Returns \c true if there is an icon available for \a name in the
1425 current icon theme or any of the fallbacks, as described by
1426 fromTheme(), otherwise returns \c false.
1427
1428 \sa themeSearchPaths(), fromTheme(), setThemeName()
1429*/
1430bool QIcon::hasThemeIcon(const QString &name)
1431{
1432 QIcon icon = fromTheme(name);
1433
1434 return icon.name() == name;
1435}
1436
1437static constexpr auto themeIconMapping = qOffsetStringArray(
1438 strings: "address-book-new",
1439 strings: "application-exit",
1440 strings: "appointment-new",
1441 strings: "call-start",
1442 strings: "call-stop",
1443 strings: "contact-new",
1444 strings: "document-new",
1445 strings: "document-open",
1446 strings: "document-open-recent",
1447 strings: "document-page-setup",
1448 strings: "document-print",
1449 strings: "document-print-preview",
1450 strings: "document-properties",
1451 strings: "document-revert",
1452 strings: "document-save",
1453 strings: "document-save-as",
1454 strings: "document-send",
1455 strings: "edit-clear",
1456 strings: "edit-copy",
1457 strings: "edit-cut",
1458 strings: "edit-delete",
1459 strings: "edit-find",
1460 strings: "edit-paste",
1461 strings: "edit-redo",
1462 strings: "edit-select-all",
1463 strings: "edit-undo",
1464 strings: "folder-new",
1465 strings: "format-indent-less",
1466 strings: "format-indent-more",
1467 strings: "format-justify-center",
1468 strings: "format-justify-fill",
1469 strings: "format-justify-left",
1470 strings: "format-justify-right",
1471 strings: "format-text-direction-ltr",
1472 strings: "format-text-direction-rtl",
1473 strings: "format-text-bold",
1474 strings: "format-text-italic",
1475 strings: "format-text-underline",
1476 strings: "format-text-strikethrough",
1477 strings: "go-down",
1478 strings: "go-home",
1479 strings: "go-next",
1480 strings: "go-previous",
1481 strings: "go-up",
1482 strings: "help-about",
1483 strings: "help-faq",
1484 strings: "insert-image",
1485 strings: "insert-link",
1486 strings: "insert-text",
1487 strings: "list-add",
1488 strings: "list-remove",
1489 strings: "mail-forward",
1490 strings: "mail-mark-important",
1491 strings: "mail-mark-read",
1492 strings: "mail-mark-unread",
1493 strings: "mail-message-new",
1494 strings: "mail-reply-all",
1495 strings: "mail-reply-sender",
1496 strings: "mail-send",
1497 strings: "media-eject",
1498 strings: "media-playback-pause",
1499 strings: "media-playback-start",
1500 strings: "media-playback-stop",
1501 strings: "media-record",
1502 strings: "media-seek-backward",
1503 strings: "media-seek-forward",
1504 strings: "media-skip-backward",
1505 strings: "media-skip-forward",
1506 strings: "object-rotate-left",
1507 strings: "object-rotate-right",
1508 strings: "process-stop",
1509 strings: "system-lock-screen",
1510 strings: "system-log-out",
1511 strings: "system-search",
1512 strings: "system-reboot",
1513 strings: "system-shutdown",
1514 strings: "tools-check-spelling",
1515 strings: "view-fullscreen",
1516 strings: "view-refresh",
1517 strings: "view-restore",
1518 strings: "window-close",
1519 strings: "window-new",
1520 strings: "zoom-fit-best",
1521 strings: "zoom-in",
1522 strings: "zoom-out",
1523
1524 strings: "audio-card",
1525 strings: "audio-input-microphone",
1526 strings: "battery",
1527 strings: "camera-photo",
1528 strings: "camera-video",
1529 strings: "camera-web",
1530 strings: "computer",
1531 strings: "drive-harddisk",
1532 strings: "drive-optical",
1533 strings: "input-gaming",
1534 strings: "input-keyboard",
1535 strings: "input-mouse",
1536 strings: "input-tablet",
1537 strings: "media-flash",
1538 strings: "media-optical",
1539 strings: "media-tape",
1540 strings: "multimedia-player",
1541 strings: "network-wired",
1542 strings: "network-wireless",
1543 strings: "phone",
1544 strings: "printer",
1545 strings: "scanner",
1546 strings: "video-display",
1547
1548 strings: "appointment-missed",
1549 strings: "appointment-soon",
1550 strings: "audio-volume-high",
1551 strings: "audio-volume-low",
1552 strings: "audio-volume-medium",
1553 strings: "audio-volume-muted",
1554 strings: "battery-caution",
1555 strings: "battery-low",
1556 strings: "dialog-error",
1557 strings: "dialog-information",
1558 strings: "dialog-password",
1559 strings: "dialog-question",
1560 strings: "dialog-warning",
1561 strings: "folder-drag-accept",
1562 strings: "folder-open",
1563 strings: "folder-visiting",
1564 strings: "image-loading",
1565 strings: "image-missing",
1566 strings: "mail-attachment",
1567 strings: "mail-unread",
1568 strings: "mail-read",
1569 strings: "mail-replied",
1570 strings: "media-playlist-repeat",
1571 strings: "media-playlist-shuffle",
1572 strings: "network-offline",
1573 strings: "printer-printing",
1574 strings: "security-high",
1575 strings: "security-low",
1576 strings: "software-update-available",
1577 strings: "software-update-urgent",
1578 strings: "sync-error",
1579 strings: "sync-synchronizing",
1580 strings: "user-available",
1581 strings: "user-offline",
1582 strings: "weather-clear",
1583 strings: "weather-clear-night",
1584 strings: "weather-few-clouds",
1585 strings: "weather-few-clouds-night",
1586 strings: "weather-fog",
1587 strings: "weather-showers",
1588 strings: "weather-snow",
1589 strings: "weather-storm"
1590);
1591static_assert(QIcon::ThemeIcon::NThemeIcons == QIcon::ThemeIcon(themeIconMapping.count()));
1592
1593static constexpr QLatin1StringView themeIconName(QIcon::ThemeIcon icon)
1594{
1595 using ThemeIconIndex = std::underlying_type_t<QIcon::ThemeIcon>;
1596 const auto index = static_cast<ThemeIconIndex>(icon);
1597 Q_ASSERT(index < themeIconMapping.count());
1598 return QLatin1StringView(themeIconMapping.viewAt(index));
1599}
1600
1601/*!
1602 \enum QIcon::ThemeIcon
1603 \since 6.7
1604
1605 This enum provides access to icons that are provided by most
1606 icon theme implementations.
1607
1608 \value AddressBookNew The icon for the action to create a new address book.
1609 \value ApplicationExit The icon for exiting an application.
1610 \value AppointmentNew The icon for the action to create a new appointment.
1611 \value CallStart The icon for initiating or accepting a call.
1612 \value CallStop The icon for stopping a current call.
1613 \value ContactNew The icon for the action to create a new contact.
1614 \value DocumentNew The icon for the action to create a new document.
1615 \value DocumentOpen The icon for the action to open a document.
1616 \value DocumentOpenRecent The icon for the action to open a document that was recently opened.
1617 \value DocumentPageSetup The icon for the \e{page setup} action.
1618 \value DocumentPrint The icon for the \e{print} action.
1619 \value DocumentPrintPreview The icon for the \e{print preview} action.
1620 \value DocumentProperties The icon for the action to view the properties of a document.
1621 \value DocumentRevert The icon for the action of reverting to a previous version of a document.
1622 \value DocumentSave The icon for the \e{save} action.
1623 \value DocumentSaveAs The icon for the \e{save as} action.
1624 \value DocumentSend The icon for the \e{send} action.
1625 \value EditClear The icon for the \e{clear} action.
1626 \value EditCopy The icon for the \e{copy} action.
1627 \value EditCut The icon for the \e{cut} action.
1628 \value EditDelete The icon for the \e{delete} action.
1629 \value EditFind The icon for the \e{find} action.
1630 \value EditPaste The icon for the \e{paste} action.
1631 \value EditRedo The icon for the \e{redo} action.
1632 \value EditSelectAll The icon for the \e{select all} action.
1633 \value EditUndo The icon for the \e{undo} action.
1634 \value FolderNew The icon for creating a new folder.
1635 \value FormatIndentLess The icon for the \e{decrease indent formatting} action.
1636 \value FormatIndentMore The icon for the \e{increase indent formatting} action.
1637 \value FormatJustifyCenter The icon for the \e{center justification formatting} action.
1638 \value FormatJustifyFill The icon for the \e{fill justification formatting} action.
1639 \value FormatJustifyLeft The icon for the \e{left justification formatting} action.
1640 \value FormatJustifyRight The icon for the \e{right justification} action.
1641 \value FormatTextDirectionLtr The icon for the \e{left-to-right text formatting} action.
1642 \value FormatTextDirectionRtl The icon for the \e{right-to-left formatting} action.
1643 \value FormatTextBold The icon for the \e{bold text formatting} action.
1644 \value FormatTextItalic The icon for the \e{italic text formatting} action.
1645 \value FormatTextUnderline The icon for the \e{underlined text formatting} action.
1646 \value FormatTextStrikethrough The icon for the \e{strikethrough text formatting} action.
1647 \value GoDown The icon for the \e{go down in a list} action.
1648 \value GoHome The icon for the \e{go to home location} action.
1649 \value GoNext The icon for the \e{go to the next item in a list} action.
1650 \value GoPrevious The icon for the \e{go to the previous item in a list} action.
1651 \value GoUp The icon for the \e{go up in a list} action.
1652 \value HelpAbout The icon for the \e{About} item in the Help menu.
1653 \value HelpFaq The icon for the \e{FAQ} item in the Help menu.
1654 \value InsertImage The icon for the \e{insert image} action of an application.
1655 \value InsertLink The icon for the \e{insert link} action of an application.
1656 \value InsertText The icon for the \e{insert text} action of an application.
1657 \value ListAdd The icon for the \e{add to list} action.
1658 \value ListRemove The icon for the \e{remove from list} action.
1659 \value MailForward The icon for the \e{forward} action.
1660 \value MailMarkImportant The icon for the \e{mark as important} action.
1661 \value MailMarkRead The icon for the \e{mark as read} action.
1662 \value MailMarkUnread The icon for the \e{mark as unread} action.
1663 \value MailMessageNew The icon for the \e{compose new mail} action.
1664 \value MailReplyAll The icon for the \e{reply to all} action.
1665 \value MailReplySender The icon for the \e{reply to sender} action.
1666 \value MailSend The icon for the \e{send} action.
1667 \value MediaEject The icon for the \e{eject} action of a media player or file manager.
1668 \value MediaPlaybackPause The icon for the \e{pause} action of a media player.
1669 \value MediaPlaybackStart The icon for the \e{start playback} action of a media player.
1670 \value MediaPlaybackStop The icon for the \e{stop} action of a media player.
1671 \value MediaRecord The icon for the \e{record} action of a media application.
1672 \value MediaSeekBackward The icon for the \e{seek backward} action of a media player.
1673 \value MediaSeekForward The icon for the \e{seek forward} action of a media player.
1674 \value MediaSkipBackward The icon for the \e{skip backward} action of a media player.
1675 \value MediaSkipForward The icon for the \e{skip forward} action of a media player.
1676 \value ObjectRotateLeft The icon for the \e{rotate left} action performed on an object.
1677 \value ObjectRotateRight The icon for the \e{rotate right} action performed on an object.
1678 \value ProcessStop The icon for the \e{stop action in applications with} actions that
1679 may take a while to process, such as web page loading in a browser.
1680 \value SystemLockScreen The icon for the \e{lock screen} action.
1681 \value SystemLogOut The icon for the \e{log out} action.
1682 \value SystemSearch The icon for the \e{search} action.
1683 \value SystemReboot The icon for the \e{reboot} action.
1684 \value SystemShutdown The icon for the \e{shutdown} action.
1685 \value ToolsCheckSpelling The icon for the \e{check spelling} action.
1686 \value ViewFullscreen The icon for the \e{fullscreen} action.
1687 \value ViewRefresh The icon for the \e{refresh} action.
1688 \value ViewRestore The icon for leaving the fullscreen view.
1689 \value WindowClose The icon for the \e{close window} action.
1690 \value WindowNew The icon for the \e{new window} action.
1691 \value ZoomFitBest The icon for the \e{best fit} action.
1692 \value ZoomIn The icon for the \e{zoom in} action.
1693 \value ZoomOut The icon for the \e{zoom out} action.
1694
1695 \value AudioCard The icon for the audio rendering device.
1696 \value AudioInputMicrophone The icon for the microphone audio input device.
1697 \value Battery The icon for the system battery device.
1698 \value CameraPhoto The icon for a digital still camera devices.
1699 \value CameraVideo The icon for a video camera device.
1700 \value CameraWeb The icon for a web camera device.
1701 \value Computer The icon for the computing device as a whole.
1702 \value DriveHarddisk The icon for hard disk drives.
1703 \value DriveOptical The icon for optical media drives such as CD and DVD.
1704 \value InputGaming The icon for the gaming input device.
1705 \value InputKeyboard The icon for the keyboard input device.
1706 \value InputMouse The icon for the mousing input device.
1707 \value InputTablet The icon for graphics tablet input devices.
1708 \value MediaFlash The icon for flash media, such as a memory stick.
1709 \value MediaOptical The icon for physical optical media such as CD and DVD.
1710 \value MediaTape The icon for generic physical tape media.
1711 \value MultimediaPlayer The icon for generic multimedia playing devices.
1712 \value NetworkWired The icon for wired network connections.
1713 \value NetworkWireless The icon for wireless network connections.
1714 \value Phone The icon for phone devices.
1715 \value Printer The icon for a printer device.
1716 \value Scanner The icon for a scanner device.
1717 \value VideoDisplay The icon for the monitor that video gets displayed on.
1718
1719 \value AppointmentMissed The icon for when an appointment was missed.
1720 \value AppointmentSoon The icon for when an appointment will occur soon.
1721 \value AudioVolumeHigh The icon used to indicate high audio volume.
1722 \value AudioVolumeLow The icon used to indicate low audio volume.
1723 \value AudioVolumeMedium The icon used to indicate medium audio volume.
1724 \value AudioVolumeMuted The icon used to indicate the muted state for audio playback.
1725 \value BatteryCaution The icon used when the battery is below 40%.
1726 \value BatteryLow The icon used when the battery is below 20%.
1727 \value DialogError The icon used when a dialog is opened to explain an error
1728 condition to the user.
1729 \value DialogInformation The icon used when a dialog is opened to give information to the
1730 user that may be pertinent to the requested action.
1731 \value DialogPassword The icon used when a dialog requesting the authentication
1732 credentials for a user is opened.
1733 \value DialogQuestion The icon used when a dialog is opened to ask a simple question
1734 to the user.
1735 \value DialogWarning The icon used when a dialog is opened to warn the user of
1736 impending issues with the requested action.
1737 \value FolderDragAccept The icon used for a folder while an acceptable object is being
1738 dragged onto it.
1739 \value FolderOpen The icon used for folders, while their contents are being displayed
1740 within the same window.
1741 \value FolderVisiting The icon used for folders, while their contents are being displayed
1742 in another window.
1743 \value ImageLoading The icon used while another image is being loaded.
1744 \value ImageMissing The icon used when another image could not be loaded.
1745 \value MailAttachment The icon for a message that contains attachments.
1746 \value MailUnread The icon for an unread message.
1747 \value MailRead The icon for a read message.
1748 \value MailReplied The icon for a message that has been replied to.
1749 \value MediaPlaylistRepeat The icon for the repeat mode of a media player.
1750 \value MediaPlaylistShuffle The icon for the shuffle mode of a media player.
1751 \value NetworkOffline The icon used to indicate that the device is not connected to the
1752 network.
1753 \value PrinterPrinting The icon used while a print job is successfully being spooled to a
1754 printing device.
1755 \value SecurityHigh The icon used to indicate that the security level of an item is
1756 known to be high.
1757 \value SecurityLow The icon used to indicate that the security level of an item is
1758 known to be low.
1759 \value SoftwareUpdateAvailable The icon used to indicate that an update is available.
1760 \value SoftwareUpdateUrgent The icon used to indicate that an urgent update is available.
1761 \value SyncError The icon used when an error occurs while attempting to synchronize
1762 data across devices.
1763 \value SyncSynchronizing The icon used while data is successfully synchronizing across
1764 devices.
1765 \value UserAvailable The icon used to indicate that a user is available.
1766 \value UserOffline The icon used to indicate that a user is not available.
1767 \value WeatherClear The icon used to indicate that the sky is clear.
1768 \value WeatherClearNight The icon used to indicate that the sky is clear
1769 during the night.
1770 \value WeatherFewClouds The icon used to indicate that the sky is partly cloudy.
1771 \value WeatherFewCloudsNight The icon used to indicate that the sky is partly cloudy
1772 during the night.
1773 \value WeatherFog The icon used to indicate that the weather is foggy.
1774 \value WeatherShowers The icon used to indicate that rain showers are occurring.
1775 \value WeatherSnow The icon used to indicate that snow is falling.
1776 \value WeatherStorm The icon used to indicate that the weather is stormy.
1777
1778 \omitvalue NThemeIcons
1779
1780 \sa {QIcon#Creating an icon from a theme or icon library},
1781 fromTheme()
1782*/
1783
1784/*!
1785 \since 6.7
1786 \overload
1787
1788 Returns \c true if there is an icon available for \a icon in the
1789 current icon theme or any of the fallbacks, as described by
1790 fromTheme(), otherwise returns \c false.
1791
1792 \sa fromTheme()
1793*/
1794bool QIcon::hasThemeIcon(QIcon::ThemeIcon icon)
1795{
1796 return hasThemeIcon(name: themeIconName(icon));
1797}
1798
1799/*!
1800 \fn QIcon QIcon::fromTheme(QIcon::ThemeIcon icon)
1801 \fn QIcon QIcon::fromTheme(QIcon::ThemeIcon icon, const QIcon &fallback)
1802 \since 6.7
1803 \overload
1804
1805 Returns the QIcon corresponding to \a icon in the
1806 \l{themeName()}{current icon theme}.
1807
1808 If the current theme does not provide an icon for \a icon,
1809 the \l{fallbackThemeName()}{fallback icon theme} is consulted,
1810 before falling back to looking up standalone icon files in the
1811 \l{QIcon::fallbackSearchPaths()}{fallback icon search path}.
1812 Finally, the platform's native icon library is consulted.
1813
1814 If no icon is found and a \a fallback is provided, \a fallback is
1815 returned. This is useful to provide a guaranteed fallback, regardless
1816 of whether the current set of icon themes and fallbacks paths
1817 support the requested icon.
1818
1819 If no icon is found and no \a fallback is provided, a default
1820 constructed, empty QIcon is returned.
1821*/
1822QIcon QIcon::fromTheme(QIcon::ThemeIcon icon)
1823{
1824 return fromTheme(name: themeIconName(icon));
1825}
1826
1827QIcon QIcon::fromTheme(QIcon::ThemeIcon icon, const QIcon &fallback)
1828{
1829 return fromTheme(name: themeIconName(icon), fallback);
1830}
1831
1832/*!
1833 \since 5.6
1834
1835 Indicate that this icon is a mask image(boolean \a isMask), and hence can
1836 potentially be modified based on where it's displayed.
1837 \sa isMask()
1838*/
1839void QIcon::setIsMask(bool isMask)
1840{
1841 if (isMask == (d && d->is_mask))
1842 return;
1843
1844 detach();
1845 if (!d)
1846 d = new QIconPrivate(new QPixmapIconEngine);
1847 d->is_mask = isMask;
1848}
1849
1850/*!
1851 \since 5.6
1852
1853 Returns \c true if this icon has been marked as a mask image.
1854 Certain platforms render mask icons differently (for example,
1855 menu icons on \macos).
1856
1857 \sa setIsMask()
1858*/
1859bool QIcon::isMask() const
1860{
1861 if (!d)
1862 return false;
1863 return d->is_mask;
1864}
1865
1866/*****************************************************************************
1867 QIcon stream functions
1868 *****************************************************************************/
1869#if !defined(QT_NO_DATASTREAM)
1870/*!
1871 \fn QDataStream &operator<<(QDataStream &stream, const QIcon &icon)
1872 \relates QIcon
1873
1874 Writes the given \a icon to the given \a stream as a PNG
1875 image. If the icon contains more than one image, all images will
1876 be written to the stream. Note that writing the stream to a file
1877 will not produce a valid image file.
1878*/
1879
1880QDataStream &operator<<(QDataStream &s, const QIcon &icon)
1881{
1882 if (s.version() >= QDataStream::Qt_4_3) {
1883 if (icon.isNull()) {
1884 s << QString();
1885 } else {
1886 s << icon.d->engine->key();
1887 icon.d->engine->write(out&: s);
1888 }
1889 } else if (s.version() == QDataStream::Qt_4_2) {
1890 if (icon.isNull()) {
1891 s << 0;
1892 } else {
1893 QPixmapIconEngine *engine = static_cast<QPixmapIconEngine *>(icon.d->engine);
1894 int num_entries = engine->pixmaps.size();
1895 s << num_entries;
1896 for (int i=0; i < num_entries; ++i) {
1897 s << engine->pixmaps.at(i).pixmap;
1898 s << engine->pixmaps.at(i).fileName;
1899 s << engine->pixmaps.at(i).size;
1900 s << (uint) engine->pixmaps.at(i).mode;
1901 s << (uint) engine->pixmaps.at(i).state;
1902 }
1903 }
1904 } else {
1905 s << QPixmap(icon.pixmap(w: 22,h: 22));
1906 }
1907 return s;
1908}
1909
1910/*!
1911 \fn QDataStream &operator>>(QDataStream &stream, QIcon &icon)
1912 \relates QIcon
1913
1914 Reads an image, or a set of images, from the given \a stream into
1915 the given \a icon.
1916*/
1917
1918QDataStream &operator>>(QDataStream &s, QIcon &icon)
1919{
1920 if (s.version() >= QDataStream::Qt_4_3) {
1921 icon = QIcon();
1922 QString key;
1923 s >> key;
1924 if (key == "QPixmapIconEngine"_L1) {
1925 icon.d = new QIconPrivate(new QPixmapIconEngine);
1926 icon.d->engine->read(in&: s);
1927 } else if (key == "QIconLoaderEngine"_L1 || key == "QThemeIconEngine"_L1) {
1928 icon.d = new QIconPrivate(new QThemeIconEngine);
1929 icon.d->engine->read(in&: s);
1930 } else {
1931 const int index = iceLoader()->indexOf(needle: key);
1932 if (index != -1) {
1933 if (QIconEnginePlugin *factory = qobject_cast<QIconEnginePlugin*>(object: iceLoader()->instance(index))) {
1934 if (QIconEngine *engine= factory->create()) {
1935 icon.d = new QIconPrivate(engine);
1936 engine->read(in&: s);
1937 } // factory
1938 } // instance
1939 } // index
1940 }
1941 } else if (s.version() == QDataStream::Qt_4_2) {
1942 icon = QIcon();
1943 int num_entries;
1944 QPixmap pm;
1945 QString fileName;
1946 QSize sz;
1947 uint mode;
1948 uint state;
1949
1950 s >> num_entries;
1951 for (int i=0; i < num_entries; ++i) {
1952 s >> pm;
1953 s >> fileName;
1954 s >> sz;
1955 s >> mode;
1956 s >> state;
1957 if (pm.isNull())
1958 icon.addFile(fileName, size: sz, mode: QIcon::Mode(mode), state: QIcon::State(state));
1959 else
1960 icon.addPixmap(pixmap: pm, mode: QIcon::Mode(mode), state: QIcon::State(state));
1961 }
1962 } else {
1963 QPixmap pm;
1964 s >> pm;
1965 icon.addPixmap(pixmap: pm);
1966 }
1967 return s;
1968}
1969
1970#endif //QT_NO_DATASTREAM
1971
1972#ifndef QT_NO_DEBUG_STREAM
1973QDebug operator<<(QDebug dbg, const QIcon &i)
1974{
1975 QDebugStateSaver saver(dbg);
1976 dbg.resetFormat();
1977 dbg.nospace();
1978 dbg << "QIcon(";
1979 if (i.isNull()) {
1980 dbg << "null";
1981 } else {
1982 if (!i.name().isEmpty())
1983 dbg << i.name() << ',';
1984 dbg << "availableSizes[normal,Off]=" << i.availableSizes()
1985 << ",cacheKey=" << Qt::showbase << Qt::hex << i.cacheKey() << Qt::dec << Qt::noshowbase;
1986 }
1987 dbg << ')';
1988 return dbg;
1989}
1990#endif
1991
1992/*!
1993 \fn DataPtr &QIcon::data_ptr()
1994 \internal
1995*/
1996
1997/*!
1998 \typedef QIcon::DataPtr
1999 \internal
2000*/
2001
2002/*!
2003 \internal
2004 \since 5.6
2005 Attempts to find a suitable @Nx file for the given \a targetDevicePixelRatio
2006 Returns the \a baseFileName if no such file was found.
2007
2008 Given base foo.png and a target dpr of 2.5, this function will look for
2009 foo@3x.png, then foo@2x, then fall back to foo.png if not found.
2010
2011 \a sourceDevicePixelRatio will be set to the value of N if the argument is
2012 not \nullptr
2013*/
2014QString qt_findAtNxFile(const QString &baseFileName, qreal targetDevicePixelRatio,
2015 qreal *sourceDevicePixelRatio)
2016{
2017 if (targetDevicePixelRatio <= 1.0)
2018 return baseFileName;
2019
2020 static bool disableNxImageLoading = !qEnvironmentVariableIsEmpty(varName: "QT_HIGHDPI_DISABLE_2X_IMAGE_LOADING");
2021 if (disableNxImageLoading)
2022 return baseFileName;
2023
2024 int dotIndex = baseFileName.lastIndexOf(c: u'.');
2025 if (dotIndex == -1) { /* no dot */
2026 dotIndex = baseFileName.size(); /* append */
2027 } else if (dotIndex >= 2 && baseFileName[dotIndex - 1] == u'9'
2028 && baseFileName[dotIndex - 2] == u'.') {
2029 // If the file has a .9.* (9-patch image) extension, we must ensure that the @nx goes before it.
2030 dotIndex -= 2;
2031 }
2032
2033 QString atNxfileName = baseFileName;
2034 atNxfileName.insert(i: dotIndex, s: "@2x"_L1);
2035 // Check for @Nx, ..., @3x, @2x file versions,
2036 for (int n = qMin(a: qCeil(v: targetDevicePixelRatio), b: 9); n > 1; --n) {
2037 atNxfileName[dotIndex + 1] = QLatin1Char('0' + n);
2038 if (QFile::exists(fileName: atNxfileName)) {
2039 if (sourceDevicePixelRatio)
2040 *sourceDevicePixelRatio = n;
2041 return atNxfileName;
2042 }
2043 }
2044
2045 return baseFileName;
2046}
2047
2048QT_END_NAMESPACE
2049#endif //QT_NO_ICON
2050

source code of qtbase/src/gui/image/qicon.cpp