Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qiconloader.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3#ifndef QT_NO_ICON
4#include <private/qiconloader_p.h>
5
6#include <private/qguiapplication_p.h>
7#include <private/qicon_p.h>
8
9#include <QtGui/QIconEnginePlugin>
10#include <QtGui/QPixmapCache>
11#include <qpa/qplatformtheme.h>
12#include <QtGui/QIconEngine>
13#include <QtGui/QPalette>
14#include <QtCore/qmath.h>
15#include <QtCore/QList>
16#include <QtCore/QDir>
17#include <QtCore/qloggingcategory.h>
18#if QT_CONFIG(settings)
19#include <QtCore/QSettings>
20#endif
21#include <QtGui/QPainter>
22
23#include <private/qhexstring_p.h>
24
26
27Q_LOGGING_CATEGORY(lcIconLoader, "qt.gui.icon.loader")
28
29using namespace Qt::StringLiterals;
30
31Q_GLOBAL_STATIC(QIconLoader, iconLoaderInstance)
32
33/* Theme to use in last resort, if the theme does not have the icon, neither the parents */
35{
37 const QVariant themeHint = theme->themeHint(QPlatformTheme::SystemIconFallbackThemeName);
38 if (themeHint.isValid())
39 return themeHint.toString();
40 }
41 return QString();
42}
43
45 m_themeKey(1), m_supportsSvg(false), m_initialized(false)
46{
47}
48
49static inline QString systemThemeName()
50{
51 const auto override = qgetenv("QT_QPA_SYSTEM_ICON_THEME");
52 if (!override.isEmpty())
53 return QString::fromLocal8Bit(override);
55 const QVariant themeHint = theme->themeHint(QPlatformTheme::SystemIconThemeName);
56 if (themeHint.isValid())
57 return themeHint.toString();
58 }
59 return QString();
60}
61
63{
65 const QVariant themeHint = theme->themeHint(QPlatformTheme::IconThemeSearchPaths);
66 if (themeHint.isValid())
67 return themeHint.toStringList();
68 }
69 return QStringList();
70}
71
73{
75 const QVariant themeHint = theme->themeHint(QPlatformTheme::IconFallbackSearchPaths);
76 if (themeHint.isValid())
77 return themeHint.toStringList();
78 }
79 return QStringList();
80}
81
83
85{
86 if (!m_initialized) {
88 return; // it's too early: try again later (QTBUG-74252)
89 m_initialized = true;
90 m_systemTheme = systemThemeName();
91
92 if (m_systemTheme.isEmpty())
93 m_systemTheme = systemFallbackThemeName();
94 if (qt_iconEngineFactoryLoader()->keyMap().key("svg"_L1, -1) != -1)
95 m_supportsSvg = true;
96
97 qCDebug(lcIconLoader) << "Initialized icon loader with system theme"
98 << m_systemTheme << "and SVG support" << m_supportsSvg;
99 }
100}
101
113{
114 iconLoaderInstance()->ensureInitialized();
115 return iconLoaderInstance();
116}
117
118// Queries the system theme and invalidates existing
119// icons if the theme has changed.
121{
122 const QString currentSystemTheme = m_systemTheme;
123 m_systemTheme = systemThemeName();
124 if (m_systemTheme.isEmpty())
125 m_systemTheme = systemFallbackThemeName();
126 if (m_systemTheme != currentSystemTheme)
127 qCDebug(lcIconLoader) << "Updated system theme to" << m_systemTheme;
128 // Invalidate even if the system theme name hasn't changed, as the
129 // theme itself may have changed its underlying icon lookup logic.
130 if (!hasUserTheme())
132}
133
135{
136 // Invalidating the key here will result in QThemeIconEngine
137 // recreating the actual engine the next time the icon is used.
138 // We don't need to clear the QIcon cache itself.
139 m_themeKey++;
140}
141
143{
144 return m_userTheme.isEmpty() ? m_systemTheme : m_userTheme;
145}
146
148{
149 if (m_userTheme == themeName)
150 return;
151
152 qCDebug(lcIconLoader) << "Setting user theme name to" << themeName;
153
154 const bool hadUserTheme = hasUserTheme();
155 m_userTheme = themeName;
156 // if we cleared the user theme, then reset search paths as well,
157 // otherwise we'll keep looking in the user-defined search paths for
158 // a system-provide theme, which will never work.
159 if (!hasUserTheme() && hadUserTheme)
162}
163
165{
166 return m_userFallbackTheme.isEmpty() ? systemFallbackThemeName() : m_userFallbackTheme;
167}
168
170{
171 qCDebug(lcIconLoader) << "Setting fallback theme name to" << themeName;
172 m_userFallbackTheme = themeName;
174}
175
177{
178 qCDebug(lcIconLoader) << "Setting theme search path to" << searchPaths;
179 m_iconDirs = searchPaths;
180 themeList.clear();
182}
183
185{
186 if (m_iconDirs.isEmpty()) {
187 m_iconDirs = systemIconSearchPaths();
188 // Always add resource directory as search path
189 m_iconDirs.append(":/icons"_L1);
190 }
191 return m_iconDirs;
192}
193
195{
196 qCDebug(lcIconLoader) << "Setting fallback search path to" << searchPaths;
197 m_fallbackDirs = searchPaths;
199}
200
202{
203 if (m_fallbackDirs.isEmpty()) {
204 m_fallbackDirs = systemFallbackSearchPaths();
205 }
206 return m_fallbackDirs;
207}
208
217{
218public:
219 explicit QIconCacheGtkReader(const QString &themeDir);
220 QList<const char *> lookup(QStringView);
221 bool isValid() const { return m_isValid; }
222private:
223 QFile m_file;
224 const unsigned char *m_data;
225 quint64 m_size;
226 bool m_isValid;
227
228 quint16 read16(uint offset)
229 {
230 if (offset > m_size - 2 || (offset & 0x1)) {
231 m_isValid = false;
232 return 0;
233 }
234 return m_data[offset+1] | m_data[offset] << 8;
235 }
236 quint32 read32(uint offset)
237 {
238 if (offset > m_size - 4 || (offset & 0x3)) {
239 m_isValid = false;
240 return 0;
241 }
242 return m_data[offset+3] | m_data[offset+2] << 8
243 | m_data[offset+1] << 16 | m_data[offset] << 24;
244 }
245};
246
247
249 : m_isValid(false)
250{
251 QFileInfo info(dirName + "/icon-theme.cache"_L1);
252 if (!info.exists() || info.lastModified(QTimeZone::UTC) < QFileInfo(dirName).lastModified(QTimeZone::UTC))
253 return;
254 m_file.setFileName(info.absoluteFilePath());
255 if (!m_file.open(QFile::ReadOnly))
256 return;
257 m_size = m_file.size();
258 m_data = m_file.map(0, m_size);
259 if (!m_data)
260 return;
261 if (read16(0) != 1) // VERSION_MAJOR
262 return;
263
264 m_isValid = true;
265
266 // Check that all the directories are older than the cache
267 const QDateTime lastModified = info.lastModified(QTimeZone::UTC);
268 quint32 dirListOffset = read32(8);
269 quint32 dirListLen = read32(dirListOffset);
270 for (uint i = 0; i < dirListLen; ++i) {
271 quint32 offset = read32(dirListOffset + 4 + 4 * i);
272 if (!m_isValid || offset >= m_size || lastModified < QFileInfo(dirName + u'/'
273 + QString::fromUtf8(reinterpret_cast<const char*>(m_data + offset))).lastModified(QTimeZone::UTC)) {
274 m_isValid = false;
275 return;
276 }
277 }
278}
279
280static quint32 icon_name_hash(const char *p)
281{
282 quint32 h = static_cast<signed char>(*p);
283 for (p += 1; *p != '\0'; p++)
284 h = (h << 5) - h + *p;
285 return h;
286}
287
294{
295 QList<const char *> ret;
296 if (!isValid() || name.isEmpty())
297 return ret;
298
299 QByteArray nameUtf8 = name.toUtf8();
300 quint32 hash = icon_name_hash(nameUtf8);
301
302 quint32 hashOffset = read32(4);
303 quint32 hashBucketCount = read32(hashOffset);
304
305 if (!isValid() || hashBucketCount == 0) {
306 m_isValid = false;
307 return ret;
308 }
309
310 quint32 bucketIndex = hash % hashBucketCount;
311 quint32 bucketOffset = read32(hashOffset + 4 + bucketIndex * 4);
312 while (bucketOffset > 0 && bucketOffset <= m_size - 12) {
313 quint32 nameOff = read32(bucketOffset + 4);
314 if (nameOff < m_size && strcmp(reinterpret_cast<const char*>(m_data + nameOff), nameUtf8) == 0) {
315 quint32 dirListOffset = read32(8);
316 quint32 dirListLen = read32(dirListOffset);
317
318 quint32 listOffset = read32(bucketOffset+8);
319 quint32 listLen = read32(listOffset);
320
321 if (!m_isValid || listOffset + 4 + 8 * listLen > m_size) {
322 m_isValid = false;
323 return ret;
324 }
325
326 ret.reserve(listLen);
327 for (uint j = 0; j < listLen && m_isValid; ++j) {
328 quint32 dirIndex = read16(listOffset + 4 + 8 * j);
329 quint32 o = read32(dirListOffset + 4 + dirIndex*4);
330 if (!m_isValid || dirIndex >= dirListLen || o >= m_size) {
331 m_isValid = false;
332 return ret;
333 }
334 ret.append(reinterpret_cast<const char*>(m_data) + o);
335 }
336 return ret;
337 }
338 bucketOffset = read32(bucketOffset);
339 }
340 return ret;
341}
342
344 : m_valid(false)
345{
346 QFile themeIndex;
347
348 const QStringList iconDirs = QIcon::themeSearchPaths();
349 for ( int i = 0 ; i < iconDirs.size() ; ++i) {
350 QDir iconDir(iconDirs[i]);
351 QString themeDir = iconDir.path() + u'/' + themeName;
352 QFileInfo themeDirInfo(themeDir);
353
354 if (themeDirInfo.isDir()) {
355 m_contentDirs << themeDir;
356 m_gtkCaches << QSharedPointer<QIconCacheGtkReader>::create(themeDir);
357 }
358
359 if (!m_valid) {
360 themeIndex.setFileName(themeDir + "/index.theme"_L1);
361 m_valid = themeIndex.exists();
362 qCDebug(lcIconLoader) << "Probing theme file at" << themeIndex.fileName() << m_valid;
363 }
364 }
365#if QT_CONFIG(settings)
366 if (m_valid) {
367 const QSettings indexReader(themeIndex.fileName(), QSettings::IniFormat);
368 const QStringList keys = indexReader.allKeys();
369 for (const QString &key : keys) {
370 if (key.endsWith("/Size"_L1)) {
371 // Note the QSettings ini-format does not accept
372 // slashes in key names, hence we have to cheat
373 if (int size = indexReader.value(key).toInt()) {
374 QString directoryKey = key.left(key.size() - 5);
375 QIconDirInfo dirInfo(directoryKey);
376 dirInfo.size = size;
377 QString type = indexReader.value(directoryKey + "/Type"_L1).toString();
378
379 if (type == "Fixed"_L1)
380 dirInfo.type = QIconDirInfo::Fixed;
381 else if (type == "Scalable"_L1)
383 else
385
386 dirInfo.threshold = indexReader.value(directoryKey +
387 "/Threshold"_L1,
388 2).toInt();
389
390 dirInfo.minSize = indexReader.value(directoryKey + "/MinSize"_L1, size).toInt();
391
392 dirInfo.maxSize = indexReader.value(directoryKey + "/MaxSize"_L1, size).toInt();
393
394 dirInfo.scale = indexReader.value(directoryKey + "/Scale"_L1, 1).toInt();
395
396 const QString context = indexReader.value(directoryKey + "/Context"_L1).toString();
397 dirInfo.context = [context]() {
398 if (context == "Applications"_L1)
400 else if (context == "MimeTypes"_L1)
402 else
404 }();
405
406 m_keyList.append(dirInfo);
407 }
408 }
409 }
410
411 // Parent themes provide fallbacks for missing icons
412 m_parents = indexReader.value("Icon Theme/Inherits"_L1).toStringList();
413 m_parents.removeAll(QString());
414 }
415#endif // settings
416}
417
419{
420 // Respect explicitly declared parents
421 QStringList result = m_parents;
422
423 // Ensure a default fallback for all themes
424 const QString fallback = QIconLoader::instance()->fallbackThemeName();
425 if (!fallback.isEmpty())
426 result.append(fallback);
427
428 // Ensure that all themes fall back to hicolor as the last theme
429 result.removeAll("hicolor"_L1);
430 result.append("hicolor"_L1);
431
432 return result;
433}
434
435QDebug operator<<(QDebug debug, const std::unique_ptr<QIconLoaderEngineEntry> &entry)
436{
437 QDebugStateSaver saver(debug);
438 debug.noquote() << entry->filename;
439 return debug;
440}
441
442QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName,
443 const QString &iconName,
444 QStringList &visited,
445 DashRule rule) const
446{
447 qCDebug(lcIconLoader) << "Finding icon" << iconName << "in theme" << themeName
448 << "skipping" << visited;
449
452
453 // Used to protect against potential recursions
454 visited << themeName;
455
456 QIconTheme &theme = themeList[themeName];
457 if (!theme.isValid()) {
459 if (!theme.isValid()) {
460 qCDebug(lcIconLoader) << "Theme" << themeName << "not found";
461 return info;
462 }
463 }
464
465 const QStringList contentDirs = theme.contentDirs();
466
467 QStringView iconNameFallback(iconName);
468 bool searchingGenericFallback = m_iconName.length() > iconName.length();
469
470 // Iterate through all icon's fallbacks in current theme
471 if (info.entries.empty()) {
472 const QString svgIconName = iconNameFallback + ".svg"_L1;
473 const QString pngIconName = iconNameFallback + ".png"_L1;
474
475 // Add all relevant files
476 for (int i = 0; i < contentDirs.size(); ++i) {
477 QList<QIconDirInfo> subDirs = theme.keyList();
478
479 // Try to reduce the amount of subDirs by looking in the GTK+ cache in order to save
480 // a massive amount of file stat (especially if the icon is not there)
481 auto cache = theme.m_gtkCaches.at(i);
482 if (cache->isValid()) {
483 const auto result = cache->lookup(iconNameFallback);
484 if (cache->isValid()) {
485 const QList<QIconDirInfo> subDirsCopy = subDirs;
486 subDirs.clear();
487 subDirs.reserve(result.size());
488 for (const char *s : result) {
490 auto it = std::find_if(subDirsCopy.cbegin(), subDirsCopy.cend(),
491 [&](const QIconDirInfo &info) {
492 return info.path == path; } );
493 if (it != subDirsCopy.cend()) {
494 subDirs.append(*it);
495 }
496 }
497 }
498 }
499
500 QString contentDir = contentDirs.at(i) + u'/';
501 for (int j = 0; j < subDirs.size() ; ++j) {
502 const QIconDirInfo &dirInfo = subDirs.at(j);
503 if (searchingGenericFallback &&
506 continue;
507
508 const QString subDir = contentDir + dirInfo.path + u'/';
509 const QString pngPath = subDir + pngIconName;
510 if (QFile::exists(pngPath)) {
511 auto iconEntry = std::make_unique<PixmapEntry>();
512 iconEntry->dir = dirInfo;
513 iconEntry->filename = pngPath;
514 // Notice we ensure that pixmap entries always come before
515 // scalable to preserve search order afterwards
516 info.entries.insert(info.entries.begin(), std::move(iconEntry));
517 } else if (m_supportsSvg) {
518 const QString svgPath = subDir + svgIconName;
519 if (QFile::exists(svgPath)) {
520 auto iconEntry = std::make_unique<ScalableEntry>();
521 iconEntry->dir = dirInfo;
522 iconEntry->filename = svgPath;
523 info.entries.push_back(std::move(iconEntry));
524 }
525 }
526 }
527 }
528
529 if (!info.entries.empty()) {
530 info.iconName = iconNameFallback.toString();
531 }
532 }
533
534 if (info.entries.empty()) {
535 const QStringList parents = theme.parents();
536 qCDebug(lcIconLoader) << "Did not find matching icons in theme;"
537 << "trying parent themes" << parents
538 << "skipping visited" << visited;
539
540 // Search recursively through inherited themes
541 for (int i = 0 ; i < parents.size() ; ++i) {
542
543 const QString parentTheme = parents.at(i).trimmed();
544
545 if (!visited.contains(parentTheme)) // guard against recursion
546 info = findIconHelper(parentTheme, iconName, visited, QIconLoader::NoFallBack);
547
548 if (!info.entries.empty()) // success
549 break;
550 }
551 }
552
553 if (rule == QIconLoader::FallBack && info.entries.empty()) {
554 // If it's possible - find next fallback for the icon
555 const int indexOfDash = iconNameFallback.lastIndexOf(u'-');
556 if (indexOfDash != -1) {
557 qCDebug(lcIconLoader) << "Did not find matching icons in all themes;"
558 << "trying dash fallback";
559 iconNameFallback.truncate(indexOfDash);
560 QStringList _visited;
561 info = findIconHelper(themeName, iconNameFallback.toString(), _visited, QIconLoader::FallBack);
562 }
563 }
564
565 return info;
566}
567
568QThemeIconInfo QIconLoader::lookupFallbackIcon(const QString &iconName) const
569{
570 qCDebug(lcIconLoader) << "Looking up fallback icon" << iconName;
571
573
574 const QString pngIconName = iconName + ".png"_L1;
575 const QString xpmIconName = iconName + ".xpm"_L1;
576 const QString svgIconName = iconName + ".svg"_L1;
577
578 const auto searchPaths = QIcon::fallbackSearchPaths();
579 for (const QString &iconDir: searchPaths) {
580 QDir currentDir(iconDir);
581 std::unique_ptr<QIconLoaderEngineEntry> iconEntry;
582 if (currentDir.exists(pngIconName)) {
583 iconEntry = std::make_unique<PixmapEntry>();
584 iconEntry->dir.type = QIconDirInfo::Fallback;
585 iconEntry->filename = currentDir.filePath(pngIconName);
586 } else if (currentDir.exists(xpmIconName)) {
587 iconEntry = std::make_unique<PixmapEntry>();
588 iconEntry->dir.type = QIconDirInfo::Fallback;
589 iconEntry->filename = currentDir.filePath(xpmIconName);
590 } else if (m_supportsSvg &&
591 currentDir.exists(svgIconName)) {
592 iconEntry = std::make_unique<ScalableEntry>();
593 iconEntry->dir.type = QIconDirInfo::Fallback;
594 iconEntry->filename = currentDir.filePath(svgIconName);
595 }
596 if (iconEntry) {
597 info.entries.push_back(std::move(iconEntry));
598 break;
599 }
600 }
601
602 if (!info.entries.empty())
603 info.iconName = iconName;
604
605 return info;
606}
607
609{
610 qCDebug(lcIconLoader) << "Loading icon" << name;
611
612 m_iconName = name;
613 QThemeIconInfo iconInfo;
614 QStringList visitedThemes;
615 if (!themeName().isEmpty())
616 iconInfo = findIconHelper(themeName(), name, visitedThemes, QIconLoader::FallBack);
617
618 if (iconInfo.entries.empty() && !fallbackThemeName().isEmpty())
619 iconInfo = findIconHelper(fallbackThemeName(), name, visitedThemes, QIconLoader::FallBack);
620
621 if (iconInfo.entries.empty())
622 iconInfo = lookupFallbackIcon(name);
623
624 qCDebug(lcIconLoader) << "Resulting icon entries" << iconInfo.entries;
625 return iconInfo;
626}
627
628#ifndef QT_NO_DEBUG_STREAM
630{
631 QDebugStateSaver saver(debug);
632 debug.nospace();
633 if (engine) {
634 debug.noquote() << engine->key() << "(";
635 debug << static_cast<const void *>(engine);
636 if (!engine->isNull())
637 debug.quote() << ", " << engine->iconName();
638 else
639 debug << ", null";
640 debug << ")";
641 } else {
642 debug << "QIconEngine(nullptr)";
643 }
644 return debug;
645}
646#endif
647
649{
650 qCDebug(lcIconLoader) << "Resolving icon engine for icon" << iconName;
651
652 std::unique_ptr<QIconEngine> iconEngine;
653 if (hasUserTheme())
654 iconEngine.reset(new QIconLoaderEngine(iconName));
655 if (!iconEngine || iconEngine->isNull()) {
656 qCDebug(lcIconLoader) << "Icon is not available from theme or fallback theme.";
657 if (auto *platformTheme = QGuiApplicationPrivate::platformTheme()) {
658 qCDebug(lcIconLoader) << "Trying platform engine.";
659 std::unique_ptr<QIconEngine> themeEngine(platformTheme->createIconEngine(iconName));
660 if (themeEngine && !themeEngine->isNull()) {
661 iconEngine = std::move(themeEngine);
662 qCDebug(lcIconLoader) << "Icon provided by platform engine.";
663 }
664 }
665 }
666 // We need to maintain the invariant that the QIcon has a valid engine
667 if (!iconEngine)
668 iconEngine.reset(new QIconLoaderEngine(iconName));
669
670 qCDebug(lcIconLoader) << "Resulting engine" << iconEngine.get();
671 return iconEngine.release();
672}
673
691 , m_iconName(iconName)
692{
693}
694
697 , m_iconName(other.m_iconName)
698{
699}
700
702{
703 // Although we proxy the underlying engine, that's an implementation
704 // detail, so from the point of view of QIcon, and in terms of
705 // serialization, we are the one and only theme icon engine.
706 return u"QThemeIconEngine"_s;
707}
708
710{
711 return new QThemeIconEngine(*this);
712}
713
715 in >> m_iconName;
716 return true;
717}
718
720{
721 out << m_iconName;
722 return true;
723}
724
726{
727 const auto *iconLoader = QIconLoader::instance();
728 auto mostRecentThemeKey = iconLoader->themeKey();
729 if (mostRecentThemeKey != m_themeKey) {
730 qCDebug(lcIconLoader) << "Theme key" << mostRecentThemeKey << "is different"
731 << "than cached key" << m_themeKey << "for icon" << m_iconName;
732 m_proxiedEngine.reset(iconLoader->iconEngine(m_iconName));
733 m_themeKey = mostRecentThemeKey;
734 }
735 return m_proxiedEngine.get();
736}
737
750 : m_iconName(iconName)
751 , m_info(QIconLoader::instance()->loadIcon(m_iconName))
752{
753}
754
756
758{
759 Q_UNREACHABLE();
760 return nullptr; // Cannot be cloned
761}
762
763bool QIconLoaderEngine::hasIcon() const
764{
765 return !(m_info.entries.empty());
766}
767
770{
771 QSize pixmapSize = rect.size() * painter->device()->devicePixelRatio();
772 painter->drawPixmap(rect, pixmap(pixmapSize, mode, state));
773}
774
775/*
776 * This algorithm is defined by the freedesktop spec:
777 * http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
778 */
779static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize, int iconscale)
780{
781 if (dir.scale != iconscale)
782 return false;
783
784 if (dir.type == QIconDirInfo::Fixed) {
785 return dir.size == iconsize;
786
787 } else if (dir.type == QIconDirInfo::Scalable) {
788 return iconsize <= dir.maxSize &&
789 iconsize >= dir.minSize;
790
791 } else if (dir.type == QIconDirInfo::Threshold) {
792 return iconsize >= dir.size - dir.threshold &&
793 iconsize <= dir.size + dir.threshold;
794 } else if (dir.type == QIconDirInfo::Fallback) {
795 return true;
796 }
797
798 Q_ASSERT(1); // Not a valid value
799 return false;
800}
801
802/*
803 * This algorithm is defined by the freedesktop spec:
804 * http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html
805 */
806static int directorySizeDistance(const QIconDirInfo &dir, int iconsize, int iconscale)
807{
808 const int scaledIconSize = iconsize * iconscale;
809 if (dir.type == QIconDirInfo::Fixed) {
810 return qAbs(dir.size * dir.scale - scaledIconSize);
811
812 } else if (dir.type == QIconDirInfo::Scalable) {
813 if (scaledIconSize < dir.minSize * dir.scale)
814 return dir.minSize * dir.scale - scaledIconSize;
815 else if (scaledIconSize > dir.maxSize * dir.scale)
816 return scaledIconSize - dir.maxSize * dir.scale;
817 else
818 return 0;
819
820 } else if (dir.type == QIconDirInfo::Threshold) {
821 if (scaledIconSize < (dir.size - dir.threshold) * dir.scale)
822 return dir.minSize * dir.scale - scaledIconSize;
823 else if (scaledIconSize > (dir.size + dir.threshold) * dir.scale)
824 return scaledIconSize - dir.maxSize * dir.scale;
825 else return 0;
826 } else if (dir.type == QIconDirInfo::Fallback) {
827 return 0;
828 }
829
830 Q_ASSERT(1); // Not a valid value
831 return INT_MAX;
832}
833
835{
836 int iconsize = qMin(size.width(), size.height());
837
838 // Note that m_info.entries are sorted so that png-files
839 // come first
840
841 // Search for exact matches first
842 for (const auto &entry : info.entries) {
843 if (directoryMatchesSize(entry->dir, iconsize, scale)) {
844 return entry.get();
845 }
846 }
847
848 // Find the minimum distance icon
849 int minimalSize = INT_MAX;
851 for (const auto &entry : info.entries) {
852 int distance = directorySizeDistance(entry->dir, iconsize, scale);
853 if (distance < minimalSize) {
854 minimalSize = distance;
855 closestMatch = entry.get();
856 }
857 }
858 return closestMatch;
859}
860
861/*
862 * Returns the actual icon size. For scalable svg's this is equivalent
863 * to the requested size. Otherwise the closest match is returned but
864 * we can never return a bigger size than the requested size.
865 *
866 */
869{
870 Q_UNUSED(mode);
872
874 if (entry) {
875 const QIconDirInfo &dir = entry->dir;
876 if (dir.type == QIconDirInfo::Scalable) {
877 return size;
878 } else if (dir.type == QIconDirInfo::Fallback) {
879 return QIcon(entry->filename).actualSize(size, mode, state);
880 } else {
881 int result = qMin<int>(dir.size * dir.scale, qMin(size.width(), size.height()));
882 return QSize(result, result);
883 }
884 }
885 return QSize(0, 0);
886}
887
889{
891
892 // Ensure that basePixmap is lazily initialized before generating the
893 // key, otherwise the cache key is not unique
894 if (basePixmap.isNull())
896
897 QSize actualSize = basePixmap.size();
898 // If the size of the best match we have (basePixmap) is larger than the
899 // requested size, we downscale it to match.
900 if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
901 actualSize.scale(size, Qt::KeepAspectRatio);
902
903 QString key = "$qt_theme_"_L1
904 % HexString<qint64>(basePixmap.cacheKey())
905 % HexString<int>(mode)
906 % HexString<qint64>(QGuiApplication::palette().cacheKey())
907 % HexString<int>(actualSize.width())
908 % HexString<int>(actualSize.height());
909
910 QPixmap cachedPixmap;
911 if (QPixmapCache::find(key, &cachedPixmap)) {
912 return cachedPixmap;
913 } else {
914 if (basePixmap.size() != actualSize)
916 else
917 cachedPixmap = basePixmap;
918 if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp))
919 cachedPixmap = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(guiApp))->applyQIconStyleHelper(mode, cachedPixmap);
920 QPixmapCache::insert(key, cachedPixmap);
921 }
922 return cachedPixmap;
923}
924
926{
927 if (svgIcon.isNull())
929
930 // Bypass QIcon API, as that will scale by device pixel ratio of the
931 // highest DPR screen since we're not passing on any QWindow.
932 if (QIconEngine *engine = svgIcon.data_ptr() ? svgIcon.data_ptr()->engine : nullptr)
933 return engine->pixmap(size, mode, state);
934
935 return QPixmap();
936}
937
940{
942 if (entry)
943 return entry->pixmap(size, mode, state);
944
945 return QPixmap();
946}
947
949{
950 return u"QIconLoaderEngine"_s;
951}
952
954{
955 return m_info.iconName;
956}
957
959{
960 return m_info.entries.empty();
961}
962
964{
965 const int integerScale = qCeil(scale);
966 QIconLoaderEngineEntry *entry = entryForSize(m_info, size / integerScale, integerScale);
967 return entry ? entry->pixmap(size, mode, state) : QPixmap();
968}
969
971{
972 Q_UNUSED(mode);
974
975 const qsizetype N = qsizetype(m_info.entries.size());
976 QList<QSize> sizes;
977 sizes.reserve(N);
978
979 // Gets all sizes from the DirectoryInfo entries
980 for (const auto &entry : m_info.entries) {
981 if (entry->dir.type == QIconDirInfo::Fallback) {
982 sizes.append(QIcon(entry->filename).availableSizes());
983 } else {
984 int size = entry->dir.size;
985 sizes.append(QSize(size, size));
986 }
987 }
988 return sizes;
989}
990
992
993#endif //QT_NO_ICON
NSData * m_data
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore\reentrant
Definition qdatastream.h:46
\inmodule QtCore\reentrant
Definition qdatetime.h:283
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qdir.h:20
QString path() const
Returns the path.
Definition qdir.cpp:653
uchar * map(qint64 offset, qint64 size, MemoryMapFlags flags=NoOptions)
Maps size bytes of the file into memory starting at offset.
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
void setFileName(const QString &name)
Sets the name of the file.
Definition qfile.cpp:302
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qfile.cpp:351
qint64 size() const override
\reimp
Definition qfile.cpp:1179
static QPlatformTheme * platformTheme()
\macro qGuiApp
static QPalette palette()
Returns the current application palette.
void clear() noexcept(std::is_nothrow_destructible< Node >::value)
Removes all items from the hash and frees up all memory used by it.
Definition qhash.h:951
QIconCacheGtkReader(const QString &themeDir)
QList< const char * > lookup(QStringView)
The QIconEngine class provides an abstract base class for QIcon renderers.
Definition qiconengine.h:15
virtual bool isNull()
An icon engine based on icon entries collected by QIconLoader.
QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state) override
Returns the actual size of the icon the engine provides for the requested size, mode and state.
static Q_GUI_EXPORT QIconLoaderEngineEntry * entryForSize(const QThemeIconInfo &info, const QSize &size, int scale=1)
bool isNull() override
QString key() const override
\variable QIconEngine::ScaledPixmapArgument::size
QPixmap scaledPixmap(const QSize &size, QIcon::Mode mode, QIcon::State state, qreal scale) override
QIconLoaderEngine(const QString &iconName=QString())
QString iconName() override
QList< QSize > availableSizes(QIcon::Mode mode, QIcon::State state) override
void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state) override
Uses the given painter to paint the icon with the required mode and state into the rectangle rect.
QIconEngine * clone() const override
Reimplement this method to return a clone of this icon engine.
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override
Returns the icon as a pixmap with the required size, mode, and state.
QThemeIconInfo loadIcon(const QString &iconName) const
void setFallbackThemeName(const QString &themeName)
bool hasUserTheme() const
QStringList fallbackSearchPaths() const
void ensureInitialized()
void setFallbackSearchPaths(const QStringList &searchPaths)
void updateSystemTheme()
QIconDirInfo dirInfo(int dirindex)
void setThemeSearchPath(const QStringList &searchPaths)
static QIconLoader * instance()
QString fallbackThemeName() const
QStringList themeSearchPaths() const
QIconTheme theme()
QIconEngine * iconEngine(const QString &iconName) const
QString themeName() const
void setThemeName(const QString &themeName)
void invalidateKey()
QIconEngine * engine
Definition qicon_p.h:39
QStringList contentDirs()
QStringList parents() const
QList< QIconDirInfo > keyList()
QList< QSharedPointer< QIconCacheGtkReader > > m_gtkCaches
The QIcon class provides scalable icons in different modes and states.
Definition qicon.h:20
static QStringList fallbackSearchPaths()
Definition qicon.cpp:1213
bool isNull() const
Returns true if the icon is empty; otherwise returns false.
Definition qicon.cpp:1019
Mode
This enum type describes the mode for which a pixmap is intended to be used.
Definition qicon.h:22
QList< QSize > availableSizes(Mode mode=Normal, State state=Off) const
Definition qicon.cpp:1144
State
This enum describes the state for which a pixmap is intended to be used.
Definition qicon.h:23
DataPtr & data_ptr()
Definition qicon.h:266
static QStringList themeSearchPaths()
Definition qicon.cpp:1193
QSize actualSize(const QSize &size, Mode mode=Normal, State state=Off) const
Returns the actual size of the icon for the requested size, mode, and state.
Definition qicon.cpp:926
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
void append(parameter_type t)
Definition qlist.h:458
static QObjectPrivate * get(QObject *o)
Definition qobject_p.h:150
qreal devicePixelRatio() const
The QPainter class performs low-level painting on widgets and other paint devices.
Definition qpainter.h:46
QPaintDevice * device() const
Returns the paint device on which this painter is currently painting, or \nullptr if the painter is n...
void drawPixmap(const QRectF &targetRect, const QPixmap &pixmap, const QRectF &sourceRect)
Draws the rectangular portion source of the given pixmap into the given target in the paint device.
static bool find(const QString &key, QPixmap *pixmap)
Looks for a cached pixmap associated with the given key in the cache.
static bool insert(const QString &key, const QPixmap &pixmap)
Inserts a copy of the pixmap pixmap associated with the key into the cache.
Returns a copy of the pixmap that is transformed using the given transformation transform and transfo...
Definition qpixmap.h:27
QPixmap scaled(int w, int h, Qt::AspectRatioMode aspectMode=Qt::IgnoreAspectRatio, Qt::TransformationMode mode=Qt::FastTransformation) const
Definition qpixmap.h:78
QSize size() const
Returns the size of the pixmap.
Definition qpixmap.cpp:493
bool load(const QString &fileName, const char *format=nullptr, Qt::ImageConversionFlags flags=Qt::AutoColor)
Loads a pixmap from the file with the given fileName.
Definition qpixmap.cpp:704
bool isNull() const
Returns true if this is a null pixmap; otherwise returns false.
Definition qpixmap.cpp:456
qint64 cacheKey() const
Returns a number that identifies this QPixmap.
Definition qpixmap.cpp:884
The QPlatformTheme class allows customizing the UI based on themes.
\inmodule QtCore\reentrant
Definition qrect.h:30
const_iterator cend() const noexcept
Definition qset.h:142
\inmodule QtCore
Definition qsettings.h:30
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:133
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:130
void scale(int w, int h, Qt::AspectRatioMode mode) noexcept
Scales the size to a rectangle with the given width and height, according to the specified mode:
Definition qsize.h:145
constexpr bool isNull() const noexcept
Returns true if both the width and height is 0; otherwise returns false.
Definition qsize.h:121
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString left(qsizetype n) const &
Definition qstring.h:363
static QString fromLocal8Bit(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5949
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
qsizetype size() const noexcept
Returns the number of characters in this string.
Definition qstring.h:186
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
qsizetype length() const noexcept
Returns the number of characters in this string.
Definition qstring.h:191
A named-based icon engine for providing theme icons.
QThemeIconEngine(const QString &iconName=QString())
QString key() const override
\variable QIconEngine::ScaledPixmapArgument::size
bool write(QDataStream &out) const override
Writes the contents of this engine to the QDataStream out.
bool read(QDataStream &in) override
Reads icon engine contents from the QDataStream in.
QIconEngine * clone() const override
Reimplement this method to return a clone of this icon engine.
QIconEngine * proxiedEngine() const override
\inmodule QtCore
Definition qvariant.h:65
bool isValid() const
Returns true if the storage type of this variant is not QMetaType::UnknownType; otherwise returns fal...
Definition qvariant.h:714
QString toString() const
Returns the variant as a QString if the variant has a userType() including, but not limited to:
QStringList toStringList() const
Returns the variant as a QStringList if the variant has userType() \l QMetaType::QStringList,...
QHash< int, QWidget * > hash
[35multi]
QCache< int, Employee > cache
[0]
QSet< QString >::iterator it
rect
[4]
else opt state
[0]
Combined button and popup list for selecting options.
Definition qcompare.h:63
@ SmoothTransformation
@ KeepAspectRatio
@ IgnoreAspectRatio
static void * context
QList< QString > QStringList
Constructs a string list that contains the given string, str.
#define qApp
DBusConnection const char * rule
#define Q_GLOBAL_STATIC(TYPE, NAME,...)
static QStringList systemIconSearchPaths()
QFactoryLoader * qt_iconEngineFactoryLoader()
QDebug operator<<(QDebug debug, const std::unique_ptr< QIconLoaderEngineEntry > &entry)
static QStringList systemFallbackSearchPaths()
static int directorySizeDistance(const QIconDirInfo &dir, int iconsize, int iconscale)
static QString systemThemeName()
static QString systemFallbackThemeName()
static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize, int iconscale)
static quint32 icon_name_hash(const char *p)
static int closestMatch(QRgb pixel, const QList< QRgb > &clut)
Definition qimage.cpp:2260
static QByteArray cacheKey(Args &&...args)
#define Q_LOGGING_CATEGORY(name,...)
#define qCDebug(category,...)
return ret
int qCeil(T v)
Definition qmath.h:36
constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:40
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
static QString themeName()
GLenum mode
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLsizei GLsizei GLfloat distance
GLenum type
GLenum GLuint GLintptr offset
GLuint name
GLfloat GLfloat GLfloat GLfloat h
GLdouble s
[6]
Definition qopenglext.h:235
GLuint entry
GLuint GLsizei const GLuint const GLintptr const GLsizeiptr * sizes
GLuint in
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLenum GLenum GLenum GLenum GLenum scale
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:50
unsigned short quint16
Definition qtypes.h:48
unsigned long long quint64
Definition qtypes.h:61
ptrdiff_t qsizetype
Definition qtypes.h:165
unsigned int uint
Definition qtypes.h:34
double qreal
Definition qtypes.h:187
static const struct @450 keyMap[]
QStringList keys
QTextStream out(stdout)
[7]
QSharedPointer< T > other(t)
[5]
QString dir
[11]
widget render & pixmap
QPainter painter(this)
[7]
QHostInfo info
[0]
QJSEngine engine
[0]
QPixmap basePixmap
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override
Context context
QThemeIconEntries entries
QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state) override