summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Volkov <a.volkov@rusbitech.ru>2017-12-20 16:03:42 +0300
committerAlexander Volkov <a.volkov@rusbitech.ru>2018-01-15 10:12:22 +0000
commit85aa0fd041fbaa258f089d86f227311e53f6206a (patch)
treebafaed0633d4034232af4a9d725d009e0c2a7155
parent537af273026eaf0a18d2151ad6928b7797d7955b (diff)
Introduce QIcon::fallbackSearchPaths()
... that will be used if an icon can't be found in the current theme. The Icon Theme Specification https://standards.freedesktop.org/icon-theme-spec/latest/ar01s05.html states that unthemed icons must be searched in the base directories, i.e. /usr/share/icons, ... But in practice unthemed icons are installed into /usr/share/pixmaps and this dir is not used as a base dir for icon themes. So it's better to explicitly specify fallback dirs to avoid needless access to the filesystem. Also some KDE application install their own unthemed icons (into /usr/share/<appname>/pics), that can't be found by QIconLoader. With this change it would be possible for them to specify dirs with unthemed icons and thus be displayed correctly in non-KDE environments. [ChangeLog][QtGui][QIcon] Added fallbackSearchPaths() that will be used to find icons missing in the current icon theme. Change-Id: I0dc55ba958b29356a3b0a2123d6b8faa24d4c91e Task-number: QTBUG-33123 Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io> Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
-rw-r--r--src/gui/doc/snippets/code/src_gui_image_qicon.cpp3
-rw-r--r--src/gui/image/qicon.cpp35
-rw-r--r--src/gui/image/qicon.h3
-rw-r--r--src/gui/image/qiconloader.cpp88
-rw-r--r--src/gui/image/qiconloader_p.h7
-rw-r--r--src/gui/kernel/qplatformtheme.cpp2
-rw-r--r--src/gui/kernel/qplatformtheme.h3
-rw-r--r--src/platformsupport/themes/genericunix/qgenericunixthemes.cpp8
-rw-r--r--src/platformsupport/themes/genericunix/qgenericunixthemes_p.h1
-rw-r--r--tests/auto/gui/image/qicon/fallback_icons/red.pngbin0 -> 105 bytes
-rw-r--r--tests/auto/gui/image/qicon/qicon.pro2
-rw-r--r--tests/auto/gui/image/qicon/tst_qicon.cpp11
-rw-r--r--tests/auto/gui/image/qicon/tst_qicon.qrc1
13 files changed, 155 insertions, 9 deletions
diff --git a/src/gui/doc/snippets/code/src_gui_image_qicon.cpp b/src/gui/doc/snippets/code/src_gui_image_qicon.cpp
index f472494e4a..faad6574a7 100644
--- a/src/gui/doc/snippets/code/src_gui_image_qicon.cpp
+++ b/src/gui/doc/snippets/code/src_gui_image_qicon.cpp
@@ -79,3 +79,6 @@ void MyWidget::drawIcon(QPainter *painter, QPoint pos)
QIcon undoicon = QIcon::fromTheme("edit-undo", QIcon(":/undo.png"));
//! [4]
+//! [5]
+ QIcon::setFallbackSearchPaths(QIcon::fallbackSearchPaths() << "my/search/path");
+//! [5]
diff --git a/src/gui/image/qicon.cpp b/src/gui/image/qicon.cpp
index 9b2e96d4b0..32fa9e75ac 100644
--- a/src/gui/image/qicon.cpp
+++ b/src/gui/image/qicon.cpp
@@ -1158,6 +1158,36 @@ QStringList QIcon::themeSearchPaths()
}
/*!
+ \since 5.11
+
+ Returns the fallback search paths for icons.
+
+ The default value will depend on the platform.
+
+ \sa setFallbackSearchPaths(), themeSearchPaths()
+*/
+QStringList QIcon::fallbackSearchPaths()
+{
+ return QIconLoader::instance()->fallbackSearchPaths();
+}
+
+/*!
+ \since 5.11
+
+ Sets the fallback search paths for icons to \a paths.
+
+ \note To add some path without replacing existing ones:
+
+ \snippet code/src_gui_image_qicon.cpp 5
+
+ \sa fallbackSearchPaths(), setThemeSearchPaths()
+*/
+void QIcon::setFallbackSearchPaths(const QStringList &paths)
+{
+ QIconLoader::instance()->setFallbackSearchPaths(paths);
+}
+
+/*!
\since 4.6
Sets the current icon theme to \a name.
@@ -1216,7 +1246,10 @@ QString QIcon::themeName()
the lookup. These caches can be generated using gtk-update-icon-cache:
\l{https://developer.gnome.org/gtk3/stable/gtk-update-icon-cache.html}.
- \sa themeName(), setThemeName(), themeSearchPaths()
+ \note If an icon can't be found in the current theme, then it will be
+ searched in fallbackSearchPaths() as an unthemed icon.
+
+ \sa themeName(), setThemeName(), themeSearchPaths(), fallbackSearchPaths()
*/
QIcon QIcon::fromTheme(const QString &name)
{
diff --git a/src/gui/image/qicon.h b/src/gui/image/qicon.h
index 4832455c9f..653ba6fda4 100644
--- a/src/gui/image/qicon.h
+++ b/src/gui/image/qicon.h
@@ -118,6 +118,9 @@ public:
static QStringList themeSearchPaths();
static void setThemeSearchPaths(const QStringList &searchpath);
+ static QStringList fallbackSearchPaths();
+ static void setFallbackSearchPaths(const QStringList &paths);
+
static QString themeName();
static void setThemeName(const QString &path);
diff --git a/src/gui/image/qiconloader.cpp b/src/gui/image/qiconloader.cpp
index 3cb6f46bd6..1ea4f1340b 100644
--- a/src/gui/image/qiconloader.cpp
+++ b/src/gui/image/qiconloader.cpp
@@ -95,6 +95,16 @@ static inline QStringList systemIconSearchPaths()
return QStringList();
}
+static inline QStringList systemFallbackSearchPaths()
+{
+ if (const QPlatformTheme *theme = QGuiApplicationPrivate::platformTheme()) {
+ const QVariant themeHint = theme->themeHint(QPlatformTheme::IconFallbackSearchPaths);
+ if (themeHint.isValid())
+ return themeHint.toStringList();
+ }
+ return QStringList();
+}
+
extern QFactoryLoader *qt_iconEngineFactoryLoader(); // qicon.cpp
void QIconLoader::ensureInitialized()
@@ -158,6 +168,20 @@ QStringList QIconLoader::themeSearchPaths() const
return m_iconDirs;
}
+void QIconLoader::setFallbackSearchPaths(const QStringList &searchPaths)
+{
+ m_fallbackDirs = searchPaths;
+ invalidateKey();
+}
+
+QStringList QIconLoader::fallbackSearchPaths() const
+{
+ if (m_fallbackDirs.isEmpty()) {
+ m_fallbackDirs = systemFallbackSearchPaths();
+ }
+ return m_fallbackDirs;
+}
+
/*!
\internal
Helper class that reads and looks up into the icon-theme.cache generated with
@@ -481,11 +505,54 @@ QThemeIconInfo QIconLoader::findIconHelper(const QString &themeName,
return info;
}
+QThemeIconInfo QIconLoader::lookupFallbackIcon(const QString &iconName) const
+{
+ QThemeIconInfo info;
+
+ const QString pngIconName = iconName + QLatin1String(".png");
+ const QString xpmIconName = iconName + QLatin1String(".xpm");
+ const QString svgIconName = iconName + QLatin1String(".svg");
+
+ const auto searchPaths = QIcon::fallbackSearchPaths();
+ for (const QString &iconDir: searchPaths) {
+ QDir currentDir(iconDir);
+ if (currentDir.exists(pngIconName)) {
+ PixmapEntry *iconEntry = new PixmapEntry;
+ iconEntry->dir.type = QIconDirInfo::Fallback;
+ iconEntry->filename = currentDir.filePath(pngIconName);
+ info.entries.append(iconEntry);
+ break;
+ } else if (currentDir.exists(xpmIconName)) {
+ PixmapEntry *iconEntry = new PixmapEntry;
+ iconEntry->dir.type = QIconDirInfo::Fallback;
+ iconEntry->filename = currentDir.filePath(xpmIconName);
+ info.entries.append(iconEntry);
+ break;
+ } else if (m_supportsSvg &&
+ currentDir.exists(svgIconName)) {
+ ScalableEntry *iconEntry = new ScalableEntry;
+ iconEntry->dir.type = QIconDirInfo::Fallback;
+ iconEntry->filename = currentDir.filePath(svgIconName);
+ info.entries.append(iconEntry);
+ break;
+ }
+ }
+
+ if (!info.entries.isEmpty())
+ info.iconName = iconName;
+
+ return info;
+}
+
QThemeIconInfo QIconLoader::loadIcon(const QString &name) const
{
if (!themeName().isEmpty()) {
QStringList visited;
- return findIconHelper(themeName(), name, visited);
+ const QThemeIconInfo iconInfo = findIconHelper(themeName(), name, visited);
+ if (!iconInfo.entries.isEmpty())
+ return iconInfo;
+
+ return lookupFallbackIcon(name);
}
return QThemeIconInfo();
@@ -573,6 +640,8 @@ static bool directoryMatchesSize(const QIconDirInfo &dir, int iconsize, int icon
} else if (dir.type == QIconDirInfo::Threshold) {
return iconsize >= dir.size - dir.threshold &&
iconsize <= dir.size + dir.threshold;
+ } else if (dir.type == QIconDirInfo::Fallback) {
+ return true;
}
Q_ASSERT(1); // Not a valid value
@@ -603,6 +672,8 @@ static int directorySizeDistance(const QIconDirInfo &dir, int iconsize, int icon
else if (scaledIconSize > (dir.size + dir.threshold) * dir.scale)
return scaledIconSize - dir.maxSize * dir.scale;
else return 0;
+ } else if (dir.type == QIconDirInfo::Fallback) {
+ return 0;
}
Q_ASSERT(1); // Not a valid value
@@ -657,9 +728,11 @@ QSize QIconLoaderEngine::actualSize(const QSize &size, QIcon::Mode mode,
QIconLoaderEngineEntry *entry = entryForSize(m_info, size);
if (entry) {
const QIconDirInfo &dir = entry->dir;
- if (dir.type == QIconDirInfo::Scalable)
+ if (dir.type == QIconDirInfo::Scalable) {
return size;
- else {
+ } else if (dir.type == QIconDirInfo::Fallback) {
+ return QIcon(entry->filename).actualSize(size, mode, state);
+ } else {
int result = qMin<int>(dir.size, qMin(size.width(), size.height()));
return QSize(result, result);
}
@@ -745,8 +818,13 @@ void QIconLoaderEngine::virtual_hook(int id, void *data)
// Gets all sizes from the DirectoryInfo entries
for (int i = 0; i < N; ++i) {
- int size = m_info.entries.at(i)->dir.size;
- sizes.append(QSize(size, size));
+ const QIconLoaderEngineEntry *entry = m_info.entries.at(i);
+ if (entry->dir.type == QIconDirInfo::Fallback) {
+ sizes.append(QIcon(entry->filename).availableSizes());
+ } else {
+ int size = entry->dir.size;
+ sizes.append(QSize(size, size));
+ }
}
arg.sizes.swap(sizes); // commit
}
diff --git a/src/gui/image/qiconloader_p.h b/src/gui/image/qiconloader_p.h
index 0007437ee9..746e871fb1 100644
--- a/src/gui/image/qiconloader_p.h
+++ b/src/gui/image/qiconloader_p.h
@@ -69,7 +69,7 @@ class QIconLoader;
struct QIconDirInfo
{
- enum Type { Fixed, Scalable, Threshold };
+ enum Type { Fixed, Scalable, Threshold, Fallback };
QIconDirInfo(const QString &_path = QString()) :
path(_path),
size(0),
@@ -180,6 +180,8 @@ public:
QIconTheme theme() { return themeList.value(themeName()); }
void setThemeSearchPath(const QStringList &searchPaths);
QStringList themeSearchPaths() const;
+ void setFallbackSearchPaths(const QStringList &searchPaths);
+ QStringList fallbackSearchPaths() const;
QIconDirInfo dirInfo(int dirindex);
static QIconLoader *instance();
void updateSystemTheme();
@@ -191,6 +193,8 @@ private:
QThemeIconInfo findIconHelper(const QString &themeName,
const QString &iconName,
QStringList &visited) const;
+ QThemeIconInfo lookupFallbackIcon(const QString &iconName) const;
+
uint m_themeKey;
bool m_supportsSvg;
bool m_initialized;
@@ -199,6 +203,7 @@ private:
mutable QString m_systemTheme;
mutable QStringList m_iconDirs;
mutable QHash <QString, QIconTheme> themeList;
+ mutable QStringList m_fallbackDirs;
};
QT_END_NAMESPACE
diff --git a/src/gui/kernel/qplatformtheme.cpp b/src/gui/kernel/qplatformtheme.cpp
index c8ba86bc9a..1856952805 100644
--- a/src/gui/kernel/qplatformtheme.cpp
+++ b/src/gui/kernel/qplatformtheme.cpp
@@ -516,6 +516,8 @@ QVariant QPlatformTheme::defaultThemeHint(ThemeHint hint)
return QVariant(QString());
case QPlatformTheme::IconThemeSearchPaths:
return QVariant(QStringList());
+ case QPlatformTheme::IconFallbackSearchPaths:
+ return QVariant(QStringList());
case QPlatformTheme::StyleNames:
return QVariant(QStringList());
case QPlatformTheme::ShowShortcutsInContextMenus:
diff --git a/src/gui/kernel/qplatformtheme.h b/src/gui/kernel/qplatformtheme.h
index f4ff418db6..87873d446f 100644
--- a/src/gui/kernel/qplatformtheme.h
+++ b/src/gui/kernel/qplatformtheme.h
@@ -116,7 +116,8 @@ public:
MouseDoubleClickDistance,
WheelScrollLines,
TouchDoubleTapDistance,
- ShowShortcutsInContextMenus
+ ShowShortcutsInContextMenus,
+ IconFallbackSearchPaths
};
enum DialogType {
diff --git a/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp b/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp
index 5eac0a3584..63a860f251 100644
--- a/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp
+++ b/src/platformsupport/themes/genericunix/qgenericunixthemes.cpp
@@ -178,6 +178,12 @@ QStringList QGenericUnixTheme::xdgIconThemePaths()
paths.append(xdgIconsDir.absoluteFilePath());
}
+ return paths;
+}
+
+QStringList QGenericUnixTheme::iconFallbackPaths()
+{
+ QStringList paths;
const QFileInfo pixmapsIconsDir(QStringLiteral("/usr/share/pixmaps"));
if (pixmapsIconsDir.isDir())
paths.append(pixmapsIconsDir.absoluteFilePath());
@@ -210,6 +216,8 @@ QVariant QGenericUnixTheme::themeHint(ThemeHint hint) const
return QVariant(QString(QStringLiteral("hicolor")));
case QPlatformTheme::IconThemeSearchPaths:
return xdgIconThemePaths();
+ case QPlatformTheme::IconFallbackSearchPaths:
+ return iconFallbackPaths();
case QPlatformTheme::DialogButtonBoxButtonsHaveIcons:
return QVariant(true);
case QPlatformTheme::StyleNames: {
diff --git a/src/platformsupport/themes/genericunix/qgenericunixthemes_p.h b/src/platformsupport/themes/genericunix/qgenericunixthemes_p.h
index 186e5a7dff..865a624694 100644
--- a/src/platformsupport/themes/genericunix/qgenericunixthemes_p.h
+++ b/src/platformsupport/themes/genericunix/qgenericunixthemes_p.h
@@ -85,6 +85,7 @@ public:
QVariant themeHint(ThemeHint hint) const override;
static QStringList xdgIconThemePaths();
+ static QStringList iconFallbackPaths();
#ifndef QT_NO_DBUS
QPlatformMenuBar *createPlatformMenuBar() const override;
#endif
diff --git a/tests/auto/gui/image/qicon/fallback_icons/red.png b/tests/auto/gui/image/qicon/fallback_icons/red.png
new file mode 100644
index 0000000000..4a843e744f
--- /dev/null
+++ b/tests/auto/gui/image/qicon/fallback_icons/red.png
Binary files differ
diff --git a/tests/auto/gui/image/qicon/qicon.pro b/tests/auto/gui/image/qicon/qicon.pro
index b3c60bf32b..c96f0555ad 100644
--- a/tests/auto/gui/image/qicon/qicon.pro
+++ b/tests/auto/gui/image/qicon/qicon.pro
@@ -6,4 +6,4 @@ qtHaveModule(widgets): QT += widgets
SOURCES += tst_qicon.cpp
RESOURCES = tst_qicon.qrc tst_qicon.cpp
-TESTDATA += icons/* second_icons/* *.png *.svg *.svgz
+TESTDATA += icons/* second_icons/* fallback_icons/* *.png *.svg *.svgz
diff --git a/tests/auto/gui/image/qicon/tst_qicon.cpp b/tests/auto/gui/image/qicon/tst_qicon.cpp
index bf8f7ade9e..b1a4e4312f 100644
--- a/tests/auto/gui/image/qicon/tst_qicon.cpp
+++ b/tests/auto/gui/image/qicon/tst_qicon.cpp
@@ -554,6 +554,11 @@ void tst_QIcon::fromTheme()
QCOMPARE(firstSearchPath, QIcon::themeSearchPaths()[0]);
QCOMPARE(secondSearchPath, QIcon::themeSearchPaths()[1]);
+ QString fallbackSearchPath = QStringLiteral(":/fallback_icons");
+ QIcon::setFallbackSearchPaths(QStringList() << fallbackSearchPath);
+ QCOMPARE(QIcon::fallbackSearchPaths().size(), 1);
+ QCOMPARE(fallbackSearchPath, QIcon::fallbackSearchPaths().at(0));
+
QString themeName("testtheme");
QIcon::setThemeName(themeName);
QCOMPARE(QIcon::themeName(), themeName);
@@ -580,6 +585,12 @@ void tst_QIcon::fromTheme()
QVERIFY(QIcon::hasThemeIcon("address-book-new"));
QVERIFY(!abIcon.availableSizes().isEmpty());
+ // Test icon from fallback path
+ QIcon fallbackIcon = QIcon::fromTheme("red");
+ QVERIFY(!fallbackIcon.isNull());
+ QVERIFY(QIcon::hasThemeIcon("red"));
+ QCOMPARE(fallbackIcon.availableSizes().size(), 1);
+
// Test non existing icon
QIcon noIcon = QIcon::fromTheme("broken-icon");
QVERIFY(noIcon.isNull());
diff --git a/tests/auto/gui/image/qicon/tst_qicon.qrc b/tests/auto/gui/image/qicon/tst_qicon.qrc
index 3c8fbba7c2..4b347ec88c 100644
--- a/tests/auto/gui/image/qicon/tst_qicon.qrc
+++ b/tests/auto/gui/image/qicon/tst_qicon.qrc
@@ -6,6 +6,7 @@
<file>./icons/testtheme/16x16/actions/appointment-new.png</file>
<file>./icons/testtheme/22x22/actions/appointment-new.png</file>
<file>./second_icons/testtheme/32x32/actions/appointment-new.png</file>
+<file>./fallback_icons/red.png</file>
<file>./icons/testtheme/index.theme</file>
<file>./icons/testtheme/scalable/actions/svg-only.svg</file>
<file>./icons/themeparent/16x16/actions/address-book-new.png</file>