summaryrefslogtreecommitdiffstats
path: root/src/gui/platform/unix/qgenericunixthemes.cpp
diff options
context:
space:
mode:
authorAxel Spoerl <axel.spoerl@qt.io>2022-08-26 07:34:52 +0200
committerAxel Spoerl <axel.spoerl@qt.io>2022-08-30 20:36:14 +0200
commit223fdc7d52665b2fab24c0624b969bfdab067a2f (patch)
tree8463a3e34ff51a678d6d3761f4261c93fb9677bd /src/gui/platform/unix/qgenericunixthemes.cpp
parent51f8eaa3283daf78f32caa2f9db08860c3f7be9d (diff)
Make QKdeTheme aware of runtime theme changes
When the KDE theme gets changed programatically or by the user in KDE settings, Qt applications were not notified during run time. The KDE theme was read during startup only, when the QApplication's palette was constructed. This patch implements a DBus connection to the SettingChanged signal. QKdeTheme is notified about KDE theme changes, which trigger an application palette reconstruction and all subsequent QEvents. The implementation reacts to changes the in KDE settings "widgetStyle" and "ColorTheme". The application palette is updated only if the underlying settings change results in a palette modification. Fixes: QTBUG-103093 Pick-to: 6.4 6.3 6.2 Change-Id: If0ec0f0ba515ef3dcf9924283d3a818ac7d24a7b Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Diffstat (limited to 'src/gui/platform/unix/qgenericunixthemes.cpp')
-rw-r--r--src/gui/platform/unix/qgenericunixthemes.cpp153
1 files changed, 148 insertions, 5 deletions
diff --git a/src/gui/platform/unix/qgenericunixthemes.cpp b/src/gui/platform/unix/qgenericunixthemes.cpp
index 62ef0636c7..1efd759bcf 100644
--- a/src/gui/platform/unix/qgenericunixthemes.cpp
+++ b/src/gui/platform/unix/qgenericunixthemes.cpp
@@ -41,6 +41,9 @@
#include <algorithm>
QT_BEGIN_NAMESPACE
+#ifndef QT_NO_DBUS
+Q_LOGGING_CATEGORY(lcQpaThemeDBus, "qt.qpa.theme.dbus")
+#endif
using namespace Qt::StringLiterals;
@@ -98,7 +101,92 @@ static bool isDBusGlobalMenuAvailable()
static bool dbusGlobalMenuAvailable = checkDBusGlobalMenuAvailable();
return dbusGlobalMenuAvailable;
}
-#endif
+
+/*!
+ * \internal
+ * The QGenericUnixThemeDBusListener class listens to the SettingChanged DBus signal
+ * and translates it into the QDbusSettingType enum.
+ * Upon construction, it logs success/failure of the DBus connection.
+ *
+ * The signal settingChanged delivers the normalized setting type and the new value as a string.
+ * It is emitted on known setting types only.
+ */
+
+class QGenericUnixThemeDBusListener : public QObject
+{
+ Q_OBJECT
+
+public:
+ QGenericUnixThemeDBusListener(const QString &service, const QString &path, const QString &interface, const QString &signal);
+
+ enum class SettingType {
+ KdeGlobalTheme,
+ KdeApplicationStyle,
+ Unknown
+ };
+ Q_ENUM(SettingType)
+
+ static SettingType toSettingType(const QString &location, const QString &key);
+
+private Q_SLOTS:
+ void onSettingChanged(const QString &location, const QString &key, const QDBusVariant &value);
+
+Q_SIGNALS:
+ void settingChanged(QGenericUnixThemeDBusListener::SettingType type, const QString &value);
+
+};
+
+QGenericUnixThemeDBusListener::QGenericUnixThemeDBusListener(const QString &service,
+ const QString &path, const QString &interface, const QString &signal)
+{
+ QDBusConnection dbus = QDBusConnection::sessionBus();
+ const bool dBusRunning = dbus.isConnected();
+ bool dBusSignalConnected = false;
+#define LOG service << path << interface << signal;
+
+ if (dBusRunning) {
+ qRegisterMetaType<QDBusVariant>();
+ dBusSignalConnected = dbus.connect(service, path, interface, signal, this,
+ SLOT(onSettingChanged(QString,QString,QDBusVariant)));
+ }
+
+ if (dBusSignalConnected) {
+ // Connection successful
+ qCDebug(lcQpaThemeDBus) << LOG;
+ } else {
+ if (dBusRunning) {
+ // DBus running, but connection failed
+ qCWarning(lcQpaThemeDBus) << "DBus connection failed:" << LOG;
+ } else {
+ // DBus not running
+ qCWarning(lcQpaThemeDBus) << "Session DBus not running.";
+ }
+ qCWarning(lcQpaThemeDBus) << "Application will not react to KDE setting changes.\n"
+ << "Check your DBus installation.";
+ }
+#undef LOG
+}
+
+QGenericUnixThemeDBusListener::SettingType QGenericUnixThemeDBusListener::toSettingType(
+ const QString &location, const QString &key)
+{
+ if (location == QLatin1StringView("org.kde.kdeglobals.KDE")
+ && key == QLatin1StringView("widgetStyle"))
+ return SettingType::KdeApplicationStyle;
+ if (location == QLatin1StringView("org.kde.kdeglobals.General")
+ && key == QLatin1StringView("ColorScheme"))
+ return SettingType::KdeGlobalTheme;
+ return SettingType::Unknown;
+}
+
+void QGenericUnixThemeDBusListener::onSettingChanged(const QString &location, const QString &key, const QDBusVariant &value)
+{
+ const SettingType type = toSettingType(location, key);
+ if (type != SettingType::Unknown)
+ emit settingChanged(type, value.variant().toString());
+}
+
+#endif //QT_NO_DBUS
class QGenericUnixThemePrivate : public QPlatformThemePrivate
{
@@ -231,11 +319,9 @@ static QIcon xdgFileIcon(const QFileInfo &fileInfo)
#if QT_CONFIG(settings)
class QKdeThemePrivate : public QPlatformThemePrivate
{
+
public:
- QKdeThemePrivate(const QStringList &kdeDirs, int kdeVersion)
- : kdeDirs(kdeDirs)
- , kdeVersion(kdeVersion)
- { }
+ QKdeThemePrivate(const QStringList &kdeDirs, int kdeVersion);
static QString kdeGlobals(const QString &kdeDir, int kdeVersion)
{
@@ -266,8 +352,59 @@ public:
int startDragDist = 10;
int startDragTime = 500;
int cursorBlinkRate = 1000;
+
+#ifndef QT_NO_DBUS
+private:
+ std::unique_ptr<QGenericUnixThemeDBusListener> dbus;
+ bool initDbus();
+ void settingChangedHandler(QGenericUnixThemeDBusListener::SettingType type, const QString &value);
+#endif // QT_NO_DBUS
};
+#ifndef QT_NO_DBUS
+void QKdeThemePrivate::settingChangedHandler(QGenericUnixThemeDBusListener::SettingType type, const QString &value)
+{
+ switch (type) {
+ case QGenericUnixThemeDBusListener::SettingType::KdeGlobalTheme:
+ qCDebug(lcQpaThemeDBus) << "KDE global theme changed to:" << value;
+ break;
+ case QGenericUnixThemeDBusListener::SettingType::KdeApplicationStyle:
+ qCDebug(lcQpaThemeDBus) << "KDE application style changed to:" << value;
+ break;
+ case QGenericUnixThemeDBusListener::SettingType::Unknown:
+ Q_UNREACHABLE();
+ }
+
+ refresh();
+}
+
+bool QKdeThemePrivate::initDbus()
+{
+ constexpr QLatin1StringView service("");
+ constexpr QLatin1StringView path("/org/freedesktop/portal/desktop");
+ constexpr QLatin1StringView interface("org.freedesktop.portal.Settings");
+ constexpr QLatin1StringView signal("SettingChanged");
+
+ dbus.reset(new QGenericUnixThemeDBusListener(service, path, interface, signal));
+ Q_ASSERT(dbus);
+
+ // Wrap slot in a lambda to avoid inheriting QKdeThemePrivate from QObject
+ auto wrapper = [this](QGenericUnixThemeDBusListener::SettingType type, const QString &value) {
+ settingChangedHandler(type, value);
+ };
+
+ return QObject::connect(dbus.get(), &QGenericUnixThemeDBusListener::settingChanged, wrapper);
+}
+#endif // QT_NO_DBUS
+
+QKdeThemePrivate::QKdeThemePrivate(const QStringList &kdeDirs, int kdeVersion)
+ : kdeDirs(kdeDirs), kdeVersion(kdeVersion)
+{
+#ifndef QT_NO_DBUS
+ initDbus();
+#endif // QT_NO_DBUS
+}
+
void QKdeThemePrivate::refresh()
{
resources.clear();
@@ -368,6 +505,8 @@ void QKdeThemePrivate::refresh()
if (QFont *toolBarFont = kdeFont(readKdeSetting(QStringLiteral("toolBarFont"), kdeDirs, kdeVersion, kdeSettings)))
resources.fonts[QPlatformTheme::ToolButtonFont] = toolBarFont;
+ QWindowSystemInterface::handleThemeChange();
+
qCDebug(lcQpaFonts) << "default fonts: system" << resources.fonts[QPlatformTheme::SystemFont]
<< "fixed" << resources.fonts[QPlatformTheme::FixedFont];
qDeleteAll(kdeSettings);
@@ -855,3 +994,7 @@ QStringList QGenericUnixTheme::themeNames()
}
QT_END_NAMESPACE
+
+#ifndef QT_NO_DBUS
+#include "qgenericunixthemes.moc"
+#endif // QT_NO_DBUS