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 |
32 | QT_BEGIN_NAMESPACE |
33 | |
34 | using namespace Qt::StringLiterals; |
35 | // Convenience class providing a bool read() function. |
36 | namespace { |
37 | class ImageReader |
38 | { |
39 | public: |
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 | |
61 | private: |
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 | |
100 | static int nextSerialNumCounter() |
101 | { |
102 | Q_CONSTINIT static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0); |
103 | return 1 + serial.fetchAndAddRelaxed(valueToAdd: 1); |
104 | } |
105 | |
106 | static void qt_cleanup_icon_cache(); |
107 | namespace { |
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 | |
118 | Q_GLOBAL_STATIC(IconCache, qtIconCache) |
119 | |
120 | static void qt_cleanup_icon_cache() |
121 | { |
122 | qtIconCache()->clear(); |
123 | } |
124 | |
125 | QIconPrivate::QIconPrivate(QIconEngine *e) |
126 | : engine(e), ref(1), |
127 | serialNum(nextSerialNumCounter()), |
128 | detach_no(0), |
129 | is_mask(false) |
130 | { |
131 | } |
132 | |
133 | void 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 | */ |
149 | qreal 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 | |
162 | QPixmapIconEngine::QPixmapIconEngine() |
163 | { |
164 | } |
165 | |
166 | QPixmapIconEngine::QPixmapIconEngine(const QPixmapIconEngine &other) |
167 | : QIconEngine(other), pixmaps(other.pixmaps) |
168 | { |
169 | } |
170 | |
171 | QPixmapIconEngine::~QPixmapIconEngine() |
172 | { |
173 | } |
174 | |
175 | void 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 | |
183 | static 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.) |
191 | static 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 | |
232 | QPixmapIconEngineEntry *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 | |
247 | QPixmapIconEngineEntry *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 | |
327 | QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) |
328 | { |
329 | return scaledPixmap(size, mode, state, scale: 1.0); |
330 | } |
331 | |
332 | QPixmap 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 | |
386 | QSize 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 | |
401 | QList<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 | |
417 | void 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 |
432 | static 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 | |
438 | static 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 | |
447 | void 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 | |
495 | bool QPixmapIconEngine::isNull() |
496 | { |
497 | return pixmaps.isEmpty(); |
498 | } |
499 | |
500 | QString QPixmapIconEngine::key() const |
501 | { |
502 | return "QPixmapIconEngine"_L1 ; |
503 | } |
504 | |
505 | QIconEngine *QPixmapIconEngine::clone() const |
506 | { |
507 | return new QPixmapIconEngine(*this); |
508 | } |
509 | |
510 | bool 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 | |
541 | bool 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 | |
558 | Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, iceLoader, |
559 | (QIconEngineFactoryInterface_iid, "/iconengines"_L1 , Qt::CaseInsensitive)) |
560 | |
561 | QFactoryLoader *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 | */ |
732 | QIcon::QIcon() noexcept |
733 | : d(nullptr) |
734 | { |
735 | } |
736 | |
737 | /*! |
738 | Constructs an icon from a \a pixmap. |
739 | */ |
740 | QIcon::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 | */ |
749 | QIcon::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 | */ |
781 | QIcon::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 | */ |
792 | QIcon::QIcon(QIconEngine *engine) |
793 | :d(new QIconPrivate(engine)) |
794 | { |
795 | } |
796 | |
797 | /*! |
798 | Destroys the icon. |
799 | */ |
800 | QIcon::~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 | */ |
810 | QIcon &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 | */ |
838 | QIcon::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 | */ |
855 | qint64 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 | */ |
870 | QPixmap 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 | */ |
913 | QPixmap 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 | |
950 | QPixmap 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 | */ |
968 | QSize 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 | |
997 | QSize 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 | */ |
1019 | void 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 | */ |
1061 | bool QIcon::isNull() const |
1062 | { |
1063 | return !d || d->engine->isNull(); |
1064 | } |
1065 | |
1066 | /*!\internal |
1067 | */ |
1068 | bool QIcon::isDetached() const |
1069 | { |
1070 | return !d || d->ref.loadRelaxed() == 1; |
1071 | } |
1072 | |
1073 | /*! \internal |
1074 | */ |
1075 | void 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 | */ |
1102 | void 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 | |
1112 | static 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 | */ |
1155 | void 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 | */ |
1187 | QList<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 | */ |
1202 | QString 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 | */ |
1217 | void 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 | */ |
1230 | QStringList 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 | */ |
1250 | QStringList 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 | */ |
1271 | void 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 | */ |
1289 | void 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 | */ |
1307 | QString 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 | */ |
1326 | QString 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 | */ |
1349 | void 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 | */ |
1375 | QIcon 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 | */ |
1413 | QIcon 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 | */ |
1430 | bool QIcon::hasThemeIcon(const QString &name) |
1431 | { |
1432 | QIcon icon = fromTheme(name); |
1433 | |
1434 | return icon.name() == name; |
1435 | } |
1436 | |
1437 | static 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 | ); |
1591 | static_assert(QIcon::ThemeIcon::NThemeIcons == QIcon::ThemeIcon(themeIconMapping.count())); |
1592 | |
1593 | static 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 | */ |
1794 | bool 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 | */ |
1822 | QIcon QIcon::fromTheme(QIcon::ThemeIcon icon) |
1823 | { |
1824 | return fromTheme(name: themeIconName(icon)); |
1825 | } |
1826 | |
1827 | QIcon 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 | */ |
1839 | void 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 | */ |
1859 | bool 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 | |
1880 | QDataStream &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 | |
1918 | QDataStream &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 |
1973 | QDebug 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 | */ |
2014 | QString 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 | |
2048 | QT_END_NAMESPACE |
2049 | #endif //QT_NO_ICON |
2050 | |