diff --git a/autotests/CMakeLists.txt b/autotests/CMakeLists.txt index d475ac3ff..747e8e095 100644 --- a/autotests/CMakeLists.txt +++ b/autotests/CMakeLists.txt @@ -45,6 +45,7 @@ PLASMA_UNIT_TESTS( pluginloadertest framesvgtest iconitemtest + themetest # plasmoidpackagetest ) diff --git a/autotests/data/icons/test-theme-two/apps/22/tst-plasma-framework-test-icon.svg b/autotests/data/icons/test-theme-two/apps/22/tst-plasma-framework-test-icon.svg new file mode 100644 index 000000000..8739af5a6 --- /dev/null +++ b/autotests/data/icons/test-theme-two/apps/22/tst-plasma-framework-test-icon.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff --git a/autotests/data/icons/test-theme-two/index.theme b/autotests/data/icons/test-theme-two/index.theme new file mode 100644 index 000000000..65f3190b1 --- /dev/null +++ b/autotests/data/icons/test-theme-two/index.theme @@ -0,0 +1,28 @@ +[Icon Theme] +Name=Unittest Theme two + +DisplayDepth=32 + +DesktopDefault=48 +DesktopSizes=16,22,32,48,64,128,256 +ToolbarDefault=22 +ToolbarSizes=16,22,32,48 +MainToolbarDefault=22 +MainToolbarSizes=16,22,32,48 +SmallDefault=16 +SmallSizes=16,22,32,48 +PanelDefault=32 +PanelSizes=16,22,32,48,64,128,256 +DialogDefault=32 +DialogSizes=16,22,32,48,64,128,256 + +########## Directories +########## ordered by category and alphabetically + +Directories=apps/22 + +[apps/22] +Size=22 +Context=Applications +Type=Fixed + diff --git a/autotests/themetest.cpp b/autotests/themetest.cpp new file mode 100644 index 000000000..2165bef9a --- /dev/null +++ b/autotests/themetest.cpp @@ -0,0 +1,99 @@ +/******************************************************************************** +* Copyright 2016 Marco Martin * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Library General Public * +* License as published by the Free Software Foundation; either * +* version 2 of the License, or (at your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +* Library General Public License for more details. * +* * +* You should have received a copy of the GNU Library General Public License * +* along with this library; see the file COPYING.LIB. If not, write to * +* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * +* Boston, MA 02110-1301, USA. * +*********************************************************************************/ + +#include "themetest.h" +#include +#include + +#include +#include + +#define QLSEP QLatin1Char('_') +#define CACHE_ID_WITH_SIZE(size, id, devicePixelRatio) QString::number(int(size.width())) % QLSEP % QString::number(int(size.height())) % QLSEP % id % QLSEP % QLSEP % QString::number(int(devicePixelRatio)) + +void ThemeTest::initTestCase() +{ + // make our theme in search path + qputenv("XDG_DATA_DIRS", + qgetenv("XDG_DATA_DIRS") + ":" + QFINDTESTDATA("data").toLocal8Bit()); + + // set default icon theme to test-theme + QStandardPaths::setTestModeEnabled(true); + + QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); + + if(!QDir(configPath).mkpath(QStringLiteral("."))) { + qFatal("Failed to create test configuration directory."); + } + + QFile::remove(configPath); + + QIcon::setThemeSearchPaths(QStringList()<theme(); + QString iconPath; + if (iconTheme) { + iconPath = iconTheme->iconPath(QLatin1String("konversation.svg"), 48, KIconLoader::MatchBest); + } + + m_svg->setImagePath(iconPath); + QVERIFY(m_svg->isValid()); + + m_svg->pixmap(); //trigger the SVG being loaded + + QString cacheId = CACHE_ID_WITH_SIZE(QSize(48, 48), iconPath, m_svg->devicePixelRatio()) % QLSEP % QString::number(m_svg->colorGroup()); + + QPixmap result; + QVERIFY(m_svg->theme()->findInCache(cacheId, result)); + + QSignalSpy spy(m_svg, SIGNAL(repaintNeeded())); + QVERIFY(spy.isValid()); + + KConfigGroup cg(KSharedConfig::openConfig("kdeglobals"), "Icons"); + cg.writeEntry("Theme", "test-theme-two"); + cg.sync(); + // KIconloader needs changesto be emitted manually, ouch. + for (int i=0; i < KIconLoader::LastGroup; i++) { + KIconLoader::emitChange(KIconLoader::Group(i)); + } + + spy.wait(); + //Svg emitting repaintNeeded when something changes in KIconLoader means the svg loaded an icon + QVERIFY(spy.count() == 1); + + QVERIFY(!m_svg->theme()->findInCache(cacheId, result)); +} + +QTEST_MAIN(ThemeTest) + diff --git a/autotests/themetest.h b/autotests/themetest.h new file mode 100644 index 000000000..b6042c027 --- /dev/null +++ b/autotests/themetest.h @@ -0,0 +1,43 @@ +/****************************************************************************** +* Copyright 2016 Marco Martin * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of the GNU Library General Public * +* License as published by the Free Software Foundation; either * +* version 2 of the License, or (at your option) any later version. * +* * +* This library is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * +* Library General Public License for more details. * +* * +* You should have received a copy of the GNU Library General Public License * +* along with this library; see the file COPYING.LIB. If not, write to * +* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * +* Boston, MA 02110-1301, USA. * +*******************************************************************************/ +#ifndef THEMETEST_H +#define THEMETEST_H + +#include + +#include "plasma/theme.h" +#include "plasma/svg.h" + +class ThemeTest : public QObject +{ + Q_OBJECT + +public Q_SLOTS: + void initTestCase(); + void cleanupTestCase(); + +private Q_SLOTS: + void loadSvgIcon(); + +private: + Plasma::Svg *m_svg; +}; + +#endif + diff --git a/src/plasma/private/theme_p.cpp b/src/plasma/private/theme_p.cpp index 98bccabc2..fd70dbed9 100644 --- a/src/plasma/private/theme_p.cpp +++ b/src/plasma/private/theme_p.cpp @@ -30,6 +30,8 @@ #include #include +#include +#include namespace Plasma { @@ -120,6 +122,11 @@ ThemePrivate::ThemePrivate(QObject *parent) // ... but also remove/recreate cycles, like KConfig does it connect(KDirWatch::self(), &KDirWatch::created, this, &ThemePrivate::settingsFileChanged); + QObject::connect(KIconLoader::global(), &KIconLoader::iconChanged, + this, [this]() { + scheduleThemeChangeNotification(PixmapCache|SvgElementsCache); + }); + connect(KWindowSystem::self(), &KWindowSystem::compositingChanged, this, &ThemePrivate::compositingChanged); } @@ -172,6 +179,8 @@ bool ThemePrivate::useCache() } if (isRegularTheme) { themeMetadataPath = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QLatin1Literal(PLASMA_RELATIVE_DATA_INSTALL_DIR "/desktoptheme/") % themeName % QLatin1Literal("/metadata.desktop")); + const auto *iconTheme = KIconLoader::global()->theme(); + iconThemeMetadataPath = iconTheme->dir() + "index.theme"; Q_ASSERT(!themeMetadataPath.isEmpty() || themeName.isEmpty()); const QString cacheFileBase = cacheFile + QLatin1String("*.kcache"); @@ -194,6 +203,10 @@ bool ThemePrivate::useCache() QObject::connect(KDirWatch::self(), SIGNAL(dirty(QString)), this, SLOT(settingsFileChanged(QString)), Qt::UniqueConnection); + + if (!iconThemeMetadataPath.isEmpty()) { + KDirWatch::self()->addFile(iconThemeMetadataPath); + } } // now we check for, and remove if necessary, old caches @@ -220,7 +233,10 @@ bool ThemePrivate::useCache() if (!cacheFilePath.isEmpty()) { const QFileInfo cacheFileInfo(cacheFilePath); const QFileInfo metadataFileInfo(themeMetadataPath); - cachesTooOld = cacheFileInfo.lastModified().toTime_t() < metadataFileInfo.lastModified().toTime_t(); + const QFileInfo iconThemeMetadataFileInfo(iconThemeMetadataPath); + + cachesTooOld = (cacheFileInfo.lastModified().toTime_t() < metadataFileInfo.lastModified().toTime_t()) || + (cacheFileInfo.lastModified().toTime_t() < iconThemeMetadataFileInfo.lastModified().toTime_t()); } } @@ -248,6 +264,18 @@ bool ThemePrivate::useCache() const QString svgElementsFile = QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation) + '/' + svgElementsFileName; svgElementsCache = KSharedConfig::openConfig(svgElementsFile); + QString currentIconThemePath; + const auto *iconTheme = KIconLoader::global()->theme(); + if (iconTheme) { + currentIconThemePath = iconTheme->dir(); + } + KConfigGroup globalGroup(svgElementsCache, QLatin1String("Global")); + const QString oldIconThemePath = globalGroup.readEntry("currentIconThemePath", QString()); + if (oldIconThemePath != currentIconThemePath) { + discardCache(PixmapCache | SvgElementsCache); + globalGroup.writeEntry("currentIconThemePath", currentIconThemePath); + svgElementsCache = KSharedConfig::openConfig(svgElementsFile); + } } return cacheTheme; diff --git a/src/plasma/private/theme_p.h b/src/plasma/private/theme_p.h index 69a893497..d2246deab 100644 --- a/src/plasma/private/theme_p.h +++ b/src/plasma/private/theme_p.h @@ -140,6 +140,7 @@ public: CacheTypes cachesToDiscard; QString themeVersion; QString themeMetadataPath; + QString iconThemeMetadataPath; bool locolor : 1; bool compositingActive : 1; diff --git a/src/plasma/svg.cpp b/src/plasma/svg.cpp index 6946537c5..4538ad0ac 100644 --- a/src/plasma/svg.cpp +++ b/src/plasma/svg.cpp @@ -36,6 +36,8 @@ #include #include #include +#include +#include #include "applet.h" #include "package.h" @@ -182,8 +184,15 @@ bool SvgPrivate::setImagePath(const QString &imagePath) //length of file:// actualPath = actualPath.mid(7); } - const bool isThemed = !QDir::isAbsolutePath(actualPath); + bool isThemed = !QDir::isAbsolutePath(actualPath); + bool inIconTheme = false; + + //an absolute path.. let's try if this actually an *icon* theme + if (!isThemed) { + const auto *iconTheme = KIconLoader::global()->theme(); + isThemed = inIconTheme = iconTheme && actualPath.startsWith(iconTheme->dir()); + } // lets check to see if we're already set to this file if (isThemed == themed && ((themed && themePath == actualPath) || @@ -216,7 +225,11 @@ bool SvgPrivate::setImagePath(const QString &imagePath) emit q->fromCurrentThemeChanged(fromCurrentTheme); } - if (themed) { + if (inIconTheme) { + themePath = actualPath; + path = actualPath; + QObject::connect(actualTheme(), SIGNAL(themeChanged()), q, SLOT(themeChanged())); + } else if (themed) { themePath = actualPath; path = actualTheme()->imagePath(themePath); themeFailed = path.isEmpty();