diff options
author | Jan Grulich <jgrulich@redhat.com> | 2024-03-22 12:12:51 +0100 |
---|---|---|
committer | Jan Grulich <jgrulich@redhat.com> | 2024-04-19 11:13:31 +0000 |
commit | d0b4e8a601bc2f81ddb23c124acc99defa26c02f (patch) | |
tree | 039d39983295756b993a2c220f612c579417689e /src/plugins/platformthemes | |
parent | 96fcb4ef84bb7486221f04e53be8ee7218385178 (diff) |
QGtk3Theme: Add support for xdg-desktop-portal to get color scheme
Use xdg-desktop-portal to get color scheme used by GNOME. Recent GNOME
versions use this as primary way to switch between light and dark theme.
Make this a preferred way to get color scheme and fallback to previous
methods in case xdg-desktop-portal cannot be used. Also update app theme
on runtime when color scheme changes, not only when theme is changed.
[ChangeLog][Platform Specific Changes][Linux] Add support for
xdg-desktop-portal to get color scheme in QGtk3Theme.
Fixes: QTBUG-116197
Change-Id: Ib3ffad405bc795ed6f4de4af411efc45721662b9
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Axel Spoerl <axel.spoerl@qt.io>
Reviewed-by: Santhosh Kumar <santhosh.kumar.selvaraj@qt.io>
Diffstat (limited to 'src/plugins/platformthemes')
5 files changed, 206 insertions, 9 deletions
diff --git a/src/plugins/platformthemes/gtk3/CMakeLists.txt b/src/plugins/platformthemes/gtk3/CMakeLists.txt index becfcccd35..6d3c7bf3a2 100644 --- a/src/plugins/platformthemes/gtk3/CMakeLists.txt +++ b/src/plugins/platformthemes/gtk3/CMakeLists.txt @@ -35,6 +35,13 @@ qt_internal_add_plugin(QGtk3ThemePlugin Qt::GuiPrivate ) +qt_internal_extend_target(QGtk3ThemePlugin CONDITION QT_FEATURE_dbus + SOURCES + qgtk3portalinterface.cpp + LIBRARIES + Qt::DBus +) + qt_internal_extend_target(QGtk3ThemePlugin CONDITION QT_FEATURE_xlib LIBRARIES X11::X11 diff --git a/src/plugins/platformthemes/gtk3/qgtk3portalinterface.cpp b/src/plugins/platformthemes/gtk3/qgtk3portalinterface.cpp new file mode 100644 index 0000000000..1ffdda74fa --- /dev/null +++ b/src/plugins/platformthemes/gtk3/qgtk3portalinterface.cpp @@ -0,0 +1,123 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qgtk3portalinterface_p.h" +#include "qgtk3storage_p.h" + +#include <QtDBus/QDBusArgument> +#include <QtDBus/QDBusConnection> +#include <QtDBus/QDBusMessage> +#include <QtDBus/QDBusPendingCall> +#include <QtDBus/QDBusPendingCallWatcher> +#include <QtDBus/QDBusPendingReply> +#include <QtDBus/QDBusVariant> +#include <QtDBus/QtDBus> + +QT_BEGIN_NAMESPACE + +Q_LOGGING_CATEGORY(lcQGtk3PortalInterface, "qt.qpa.gtk"); + +using namespace Qt::StringLiterals; + +static constexpr QLatin1StringView appearanceInterface("org.freedesktop.appearance"); +static constexpr QLatin1StringView colorSchemeKey("color-scheme"); + +const QDBusArgument &operator>>(const QDBusArgument &argument, QMap<QString, QVariantMap> &map) +{ + argument.beginMap(); + map.clear(); + + while (!argument.atEnd()) { + QString key; + QVariantMap value; + argument.beginMapEntry(); + argument >> key >> value; + argument.endMapEntry(); + map.insert(key, value); + } + + argument.endMap(); + return argument; +} + +QGtk3PortalInterface::QGtk3PortalInterface(QGtk3Storage *s) + : m_storage(s) { + qRegisterMetaType<QDBusVariant>(); + qDBusRegisterMetaType<QMap<QString, QVariantMap>>(); + + queryColorScheme(); + + if (!s) { + qCDebug(lcQGtk3PortalInterface) << "QGtk3PortalInterface instantiated without QGtk3Storage." + << "No reaction to runtime theme changes."; + } +} + +Qt::ColorScheme QGtk3PortalInterface::colorScheme() const +{ + return m_colorScheme; +} + +void QGtk3PortalInterface::queryColorScheme() { + QDBusConnection connection = QDBusConnection::sessionBus(); + QDBusMessage message = QDBusMessage::createMethodCall( + "org.freedesktop.portal.Desktop"_L1, + "/org/freedesktop/portal/desktop"_L1, + "org.freedesktop.portal.Settings"_L1, "ReadAll"_L1); + message << QStringList{ appearanceInterface }; + + QDBusPendingCall pendingCall = connection.asyncCall(message); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this); + QObject::connect( + watcher, &QDBusPendingCallWatcher::finished, this, + [this](QDBusPendingCallWatcher *watcher) { + QDBusPendingReply<QMap<QString, QVariantMap>> reply = *watcher; + if (reply.isValid()) { + QMap<QString, QVariantMap> settings = reply.value(); + if (!settings.isEmpty()) { + settingChanged(appearanceInterface, colorSchemeKey, + QDBusVariant(settings.value(appearanceInterface).value(colorSchemeKey))); + } + } else { + qCDebug(lcQGtk3PortalInterface) << "Failed to query org.freedesktop.portal.Settings: " + << reply.error().message(); + } + watcher->deleteLater(); + }); + + QDBusConnection::sessionBus().connect( + "org.freedesktop.portal.Desktop"_L1, "/org/freedesktop/portal/desktop"_L1, + "org.freedesktop.portal.Settings"_L1, "SettingChanged"_L1, this, + SLOT(settingChanged(QString, QString, QDBusVariant))); +} + +void QGtk3PortalInterface::settingChanged(const QString &group, const QString &key, + const QDBusVariant &value) +{ + if (group == appearanceInterface && key == colorSchemeKey) { + const uint colorScheme = value.variant().toUInt(); + // From org.freedesktop.portal.Settings.xml + // "1" - Prefer dark appearance + Qt::ColorScheme newColorScheme = colorScheme == 1 ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light; + if (m_colorScheme != newColorScheme) { + m_colorScheme = newColorScheme; + if (m_storage) + m_storage->handleThemeChange(); + } + } +} + +QT_END_NAMESPACE + +#include "moc_qgtk3portalinterface_p.cpp" diff --git a/src/plugins/platformthemes/gtk3/qgtk3portalinterface_p.h b/src/plugins/platformthemes/gtk3/qgtk3portalinterface_p.h new file mode 100644 index 0000000000..25a5f58ab1 --- /dev/null +++ b/src/plugins/platformthemes/gtk3/qgtk3portalinterface_p.h @@ -0,0 +1,49 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QGTK3PORTALINTERFACE_H +#define QGTK3PORTALINTERFACE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/QObject> +#include <QtCore/QLoggingCategory> + +QT_BEGIN_NAMESPACE + +class QDBusVariant; +class QGtk3Storage; + +Q_DECLARE_LOGGING_CATEGORY(lcQGtk3PortalInterface); + +class QGtk3PortalInterface : public QObject +{ + Q_OBJECT +public: + QGtk3PortalInterface(QGtk3Storage *s); + ~QGtk3PortalInterface() = default; + + Qt::ColorScheme colorScheme() const; + +private Q_SLOTS: + void settingChanged(const QString &group, const QString &key, + const QDBusVariant &value); +private: + void queryColorScheme(); + + Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown; + QGtk3Storage *m_storage = nullptr; +}; + +QT_END_NAMESPACE + +#endif // QGTK3PORTALINTERFACE_H diff --git a/src/plugins/platformthemes/gtk3/qgtk3storage.cpp b/src/plugins/platformthemes/gtk3/qgtk3storage.cpp index 90c0282651..2877b28590 100644 --- a/src/plugins/platformthemes/gtk3/qgtk3storage.cpp +++ b/src/plugins/platformthemes/gtk3/qgtk3storage.cpp @@ -21,6 +21,9 @@ QT_BEGIN_NAMESPACE QGtk3Storage::QGtk3Storage() { m_interface.reset(new QGtk3Interface(this)); +#if QT_CONFIG(dbus) + m_portalInterface.reset(new QGtk3PortalInterface(this)); +#endif populateMap(); } @@ -273,7 +276,6 @@ void QGtk3Storage::clear() */ void QGtk3Storage::handleThemeChange() { - clear(); populateMap(); QWindowSystemInterface::handleThemeChange(); } @@ -331,21 +333,32 @@ void QGtk3Storage::populateMap() static QString m_themeName; // Distiguish initialization, theme change or call without theme change + Qt::ColorScheme newColorScheme = Qt::ColorScheme::Unknown; const QString newThemeName = themeName(); - if (m_themeName == newThemeName) + +#if QT_CONFIG(dbus) + // Prefer color scheme we get from xdg-desktop-portal as this is what GNOME + // relies on these days + newColorScheme = m_portalInterface->colorScheme(); +#endif + + if (newColorScheme == Qt::ColorScheme::Unknown) { + // Derive color scheme from theme name + newColorScheme = newThemeName.contains("dark"_L1, Qt::CaseInsensitive) + ? Qt::ColorScheme::Dark : m_interface->colorSchemeByColors(); + } + + if (m_themeName == newThemeName && m_colorScheme == newColorScheme) return; clear(); - // Derive color scheme from theme name - m_colorScheme = newThemeName.contains("dark"_L1, Qt::CaseInsensitive) - ? Qt::ColorScheme::Dark : m_interface->colorSchemeByColors(); - if (m_themeName.isEmpty()) { - qCDebug(lcQGtk3Interface) << "GTK theme initialized:" << newThemeName << m_colorScheme; + qCDebug(lcQGtk3Interface) << "GTK theme initialized:" << newThemeName << newColorScheme; } else { - qCDebug(lcQGtk3Interface) << "GTK theme changed to:" << newThemeName << m_colorScheme; + qCDebug(lcQGtk3Interface) << "GTK theme changed to:" << newThemeName << newColorScheme; } + m_colorScheme = newColorScheme; m_themeName = newThemeName; // create standard mapping or load from Json file? diff --git a/src/plugins/platformthemes/gtk3/qgtk3storage_p.h b/src/plugins/platformthemes/gtk3/qgtk3storage_p.h index 37c5bf57ff..45192263a9 100644 --- a/src/plugins/platformthemes/gtk3/qgtk3storage_p.h +++ b/src/plugins/platformthemes/gtk3/qgtk3storage_p.h @@ -16,6 +16,9 @@ // #include "qgtk3interface_p.h" +#if QT_CONFIG(dbus) +#include "qgtk3portalinterface_p.h" +#endif #include <QtCore/QJsonDocument> #include <QtCore/QCache> @@ -205,7 +208,9 @@ private: PaletteMap m_palettes; std::unique_ptr<QGtk3Interface> m_interface; - +#if QT_CONFIG(dbus) + std::unique_ptr<QGtk3PortalInterface> m_portalInterface; +#endif Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown; |