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