QGnomeTheme, QGtk3Theme: Refactor and Simplify DBus Interactions
This patch refactors the DBus integration in both QGnomeTheme and QGtk3Theme to centralize and simplify the portal and settings access logic. Previously, the codebase contained duplicated and scattered DBus logic for querying GNOME/GTK appearance settings, such as color scheme and contrast, which were implemented separately in both QGnomeTheme and QGtk3Theme. The patch introduces a new QGnomePortalInterface class which encapsulates all DBus interactions related to GNOME/GTK appearance settings. The old DBus interface logic is removed from QGtk3Theme and QGnomeTheme, and replaced with calls to the unified QGnomePortalInterface. The update also ensures signal-based updates for theme and appearance changes via the new interface. Change-Id: I5440f7ac00f956b846b18bd890113af0044482f0 Reviewed-by: Oliver Eftevaag <oliver.eftevaag@qt.io> Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
parent
933338f947
commit
2fe9eed3fd
@ -1059,6 +1059,11 @@ qt_internal_extend_target(Gui CONDITION UNIX AND (QT_FEATURE_xcb OR NOT MACOS) A
|
||||
platform/unix/qgnometheme_p.h platform/unix/qgnometheme.cpp
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Gui CONDITION UNIX AND QT_FEATURE_dbus AND (QT_FEATURE_xcb OR QT_FEATURE_wayland)
|
||||
SOURCES
|
||||
platform/unix/qgnomeportalinterface.cpp platform/unix/qgnomeportalinterface_p.h
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Gui CONDITION TARGET Qt::DBus AND UNIX AND (QT_FEATURE_xcb OR NOT MACOS) AND (QT_FEATURE_xcb OR NOT UIKIT)
|
||||
SOURCES
|
||||
platform/unix/dbusmenu/qdbusmenuadaptor.cpp platform/unix/dbusmenu/qdbusmenuadaptor_p.h
|
||||
@ -1068,6 +1073,7 @@ qt_internal_extend_target(Gui CONDITION TARGET Qt::DBus AND UNIX AND (QT_FEATURE
|
||||
platform/unix/dbusmenu/qdbusmenutypes.cpp platform/unix/dbusmenu/qdbusmenutypes_p.h
|
||||
platform/unix/dbusmenu/qdbusplatformmenu.cpp platform/unix/dbusmenu/qdbusplatformmenu_p.h
|
||||
platform/unix/qdbuslistener_p.h platform/unix/qdbuslistener.cpp
|
||||
platform/unix/qdbussettings_p.h platform/unix/qdbussettings.cpp
|
||||
)
|
||||
|
||||
qt_internal_extend_target(Gui CONDITION QT_FEATURE_systemtrayicon AND TARGET Qt::DBus AND UNIX AND (QT_FEATURE_xcb OR NOT MACOS) AND (QT_FEATURE_xcb OR NOT UIKIT)
|
||||
|
@ -2,6 +2,7 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
||||
|
||||
#include "qdbuslistener_p.h"
|
||||
#include "qdbussettings_p.h"
|
||||
#include <private/qguiapplication_p.h>
|
||||
#include <qpa/qplatformintegration.h>
|
||||
#include <qpa/qplatformservices.h>
|
||||
@ -48,38 +49,6 @@ constexpr auto setting() { return "Setting"_L1; }
|
||||
constexpr auto dbusSignals() { return "DbusSignals"_L1; }
|
||||
constexpr auto root() { return "Q_L1.qpa.DBusSignals"_L1; }
|
||||
} // namespace JsonKeys
|
||||
|
||||
namespace XdgSettings {
|
||||
// XDG Desktop Portal Settings (Preferred)
|
||||
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html
|
||||
constexpr auto contrastNamespace = "org.freedesktop.appearance"_L1;
|
||||
constexpr auto contrastKey = "contrast"_L1;
|
||||
// XDG portal provides the contrast preference value as uint:
|
||||
// 0 for no-preference, and, 1 for high-contrast.
|
||||
Qt::ContrastPreference convertContrastPreference(const QVariant &value)
|
||||
{
|
||||
if (!value.isValid())
|
||||
return Qt::ContrastPreference::NoPreference;
|
||||
return static_cast<Qt::ContrastPreference>(value.toUInt());
|
||||
}
|
||||
} // namespace XdgSettings
|
||||
|
||||
namespace GSettings {
|
||||
// GNOME Destop Settings (Alternative)
|
||||
// https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2069
|
||||
// https://gitlab.gnome.org/GNOME/gsettings-desktop-schemas/-/commit/0e97f1f571c495184f80d875c68f241261a50e30
|
||||
constexpr auto contrastNamespace = "org.gnome.desktop.a11y.interface"_L1;
|
||||
constexpr auto contrastKey = "high-contrast"_L1;
|
||||
// GSetting provides the contrast value as boolean:
|
||||
// true for enabled high-contrast, and, false for disabled high-contrast.
|
||||
Qt::ContrastPreference convertContrastPreference(const QVariant &value)
|
||||
{
|
||||
if (!value.isValid())
|
||||
return Qt::ContrastPreference::NoPreference;
|
||||
return value.toBool() ? Qt::ContrastPreference::HighContrast
|
||||
: Qt::ContrastPreference::NoPreference;
|
||||
}
|
||||
} // namespace GSettings
|
||||
} // namespace
|
||||
|
||||
void QDBusListener::init(const QString &service, const QString &path,
|
||||
@ -237,14 +206,15 @@ void QDBusListener::populateSignalMap()
|
||||
m_signalMap.insert(DBusKey("org.gnome.desktop.interface"_L1, "gtk-theme"_L1),
|
||||
ChangeSignal(Provider::Gtk, Setting::Theme));
|
||||
|
||||
m_signalMap.insert(DBusKey("org.freedesktop.appearance"_L1, "color-scheme"_L1),
|
||||
using namespace QDBusSettings;
|
||||
m_signalMap.insert(DBusKey(XdgSettings::AppearanceNamespace, XdgSettings::ColorSchemeKey),
|
||||
ChangeSignal(Provider::Gnome, Setting::ColorScheme));
|
||||
|
||||
m_signalMap.insert(DBusKey(XdgSettings::contrastNamespace, XdgSettings::contrastKey),
|
||||
m_signalMap.insert(DBusKey(XdgSettings::AppearanceNamespace, XdgSettings::ContrastKey),
|
||||
ChangeSignal(Provider::Gnome, Setting::Contrast));
|
||||
// Alternative solution if XDG desktop portal setting is not accessible,
|
||||
// e.g. when using the XDG portal version 1.
|
||||
m_signalMap.insert(DBusKey(GSettings::contrastNamespace, GSettings::contrastKey),
|
||||
m_signalMap.insert(DBusKey(GnomeSettings::AllyNamespace, GnomeSettings::ContrastKey),
|
||||
ChangeSignal(Provider::Gnome, Setting::Contrast));
|
||||
|
||||
const QString &saveJsonFile = qEnvironmentVariable("QT_QPA_DBUS_SIGNALS_SAVE");
|
||||
@ -274,13 +244,17 @@ void QDBusListener::onSettingChanged(const QString &location, const QString &key
|
||||
QVariant settingValue = value.variant();
|
||||
|
||||
switch (setting) {
|
||||
case Setting::ColorScheme:
|
||||
settingValue.setValue(QDBusSettings::XdgSettings::convertColorScheme(settingValue));
|
||||
break;
|
||||
case Setting::Contrast:
|
||||
using namespace QDBusSettings;
|
||||
// To unify the value, it's necessary to convert the DBus value to Qt::ContrastPreference.
|
||||
// Then the users of the value don't need to parse the raw value.
|
||||
if (key == XdgSettings::contrastKey)
|
||||
if (key == XdgSettings::ContrastKey)
|
||||
settingValue.setValue(XdgSettings::convertContrastPreference(settingValue));
|
||||
else if (key == GSettings::contrastKey)
|
||||
settingValue.setValue(GSettings::convertContrastPreference(settingValue));
|
||||
else if (key == GnomeSettings::ContrastKey)
|
||||
settingValue.setValue(GnomeSettings::convertContrastPreference(settingValue));
|
||||
else
|
||||
Q_UNREACHABLE_IMPL();
|
||||
break;
|
||||
|
47
src/gui/platform/unix/qdbussettings.cpp
Normal file
47
src/gui/platform/unix/qdbussettings.cpp
Normal file
@ -0,0 +1,47 @@
|
||||
// Copyright (C) 2025 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
|
||||
|
||||
#include "qdbussettings_p.h"
|
||||
#include <QtCore/qvariant.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QDBusSettings::XdgSettings {
|
||||
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html
|
||||
enum class ColorScheme : uint { NoPreference, PreferDark, PreferLight };
|
||||
} // namespace QDBusSettings::XdgSettings
|
||||
|
||||
Qt::ContrastPreference QDBusSettings::XdgSettings::convertContrastPreference(const QVariant &value)
|
||||
{
|
||||
// XDG portal provides the contrast preference value as uint:
|
||||
// 0 for no-preference, and, 1 for high-contrast.
|
||||
if (!value.isValid())
|
||||
return Qt::ContrastPreference::NoPreference;
|
||||
return static_cast<Qt::ContrastPreference>(value.toUInt());
|
||||
}
|
||||
|
||||
Qt::ColorScheme QDBusSettings::XdgSettings::convertColorScheme(const QVariant &value)
|
||||
{
|
||||
switch (ColorScheme{ value.toUInt() }) {
|
||||
case ColorScheme::NoPreference:
|
||||
return Qt::ColorScheme::Unknown;
|
||||
case ColorScheme::PreferDark:
|
||||
return Qt::ColorScheme::Dark;
|
||||
case ColorScheme::PreferLight:
|
||||
return Qt::ColorScheme::Light;
|
||||
}
|
||||
Q_UNREACHABLE_RETURN(Qt::ColorScheme::Unknown);
|
||||
}
|
||||
|
||||
Qt::ContrastPreference
|
||||
QDBusSettings::GnomeSettings::convertContrastPreference(const QVariant &value)
|
||||
{
|
||||
// GSetting provides the contrast value as boolean:
|
||||
// true for enabled high-contrast, and, false for disabled high-contrast.
|
||||
if (!value.isValid())
|
||||
return Qt::ContrastPreference::NoPreference;
|
||||
return value.toBool() ? Qt::ContrastPreference::HighContrast
|
||||
: Qt::ContrastPreference::NoPreference;
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
46
src/gui/platform/unix/qdbussettings_p.h
Normal file
46
src/gui/platform/unix/qdbussettings_p.h
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright (C) 2025 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 QDBUSSETTINGS_P_H
|
||||
#define QDBUSSETTINGS_P_H
|
||||
|
||||
#include <QtCore/qnamespace.h>
|
||||
#include <QtCore/qstring.h>
|
||||
QT_REQUIRE_CONFIG(dbus);
|
||||
|
||||
//
|
||||
// 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.
|
||||
//
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
namespace QDBusSettings {
|
||||
// XDG Desktop Portal Settings (Preferred)
|
||||
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html
|
||||
namespace XdgSettings {
|
||||
static constexpr QLatin1StringView AppearanceNamespace{ "org.freedesktop.appearance" };
|
||||
static constexpr QLatin1StringView ColorSchemeKey{ "color-scheme" };
|
||||
static constexpr QLatin1StringView ContrastKey{ "contrast" };
|
||||
Qt::ContrastPreference convertContrastPreference(const QVariant &value);
|
||||
Qt::ColorScheme convertColorScheme(const QVariant &value);
|
||||
} // namespace XdgSettings
|
||||
|
||||
namespace GnomeSettings {
|
||||
// GNOME Destop Settings (Alternative)
|
||||
// https://gitlab.gnome.org/GNOME/gnome-shell/-/merge_requests/2069
|
||||
// https://gitlab.gnome.org/GNOME/gsettings-desktop-schemas/-/commit/0e97f1f571c495184f80d875c68f241261a50e30
|
||||
static constexpr QLatin1StringView AllyNamespace{ "org.gnome.desktop.a11y.interface" };
|
||||
static constexpr QLatin1StringView ContrastKey{ "high-contrast" };
|
||||
Qt::ContrastPreference convertContrastPreference(const QVariant &value);
|
||||
} // namespace GnomeSettings
|
||||
} // namespace QDBusSettings
|
||||
|
||||
QT_END_NAMESPACE
|
||||
#endif // QDBUSSETTINGS_P_H
|
175
src/gui/platform/unix/qgnomeportalinterface.cpp
Normal file
175
src/gui/platform/unix/qgnomeportalinterface.cpp
Normal file
@ -0,0 +1,175 @@
|
||||
// Copyright (C) 2025 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
|
||||
|
||||
#include "qgnomeportalinterface_p.h"
|
||||
#include "qdbussettings_p.h"
|
||||
#include <private/qdbusplatformmenu_p.h>
|
||||
#include <QtDBus/QDBusMessage>
|
||||
#include <QtDBus/QDBusPendingCall>
|
||||
#include <QtDBus/QDBusReply>
|
||||
#include <QtDBus/QDBusVariant>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#if QT_CONFIG(dbus)
|
||||
Q_STATIC_LOGGING_CATEGORY(lcQpaThemeGnome, "qt.qpa.theme.gnome")
|
||||
#endif // QT_CONFIG(dbus)
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
QGnomePortalInterface::QGnomePortalInterface(QObject *parent)
|
||||
: QObject(parent), m_dbus{ new QDBusListener }
|
||||
{
|
||||
QObject::connect(m_dbus.get(), &QDBusListener::settingChanged, this,
|
||||
&QGnomePortalInterface::dbusSettingChanged);
|
||||
|
||||
querySettings();
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
\brief QGnomePortalInterface::colorScheme
|
||||
This is a getter method for the color-scheme loaded from the DBus portal.
|
||||
\param fallback indicates the fallback color-scheme.
|
||||
\return returns the cached color-scheme. If the color-scheme was not loaded
|
||||
it returns the \a fallback color-scheme.
|
||||
*/
|
||||
Qt::ColorScheme QGnomePortalInterface::colorScheme(Qt::ColorScheme fallback) const
|
||||
{
|
||||
if (!m_colorScheme.has_value())
|
||||
return fallback;
|
||||
return m_colorScheme.value();
|
||||
}
|
||||
|
||||
/*!
|
||||
\internal
|
||||
\brief QGnomePortalInterface::contrastPreference
|
||||
This is a getter method for the contrast-preference loaded from the DBus portal.
|
||||
\param fallback indicates the fallback contrast-preference.
|
||||
\return returns the cached contrast-preference if it was loaded. Otherwise,
|
||||
returns the \a fallback contrast-preference.
|
||||
*/
|
||||
Qt::ContrastPreference
|
||||
QGnomePortalInterface::contrastPreference(Qt::ContrastPreference fallback) const
|
||||
{
|
||||
if (!m_contrast.has_value())
|
||||
return fallback;
|
||||
return m_contrast.value();
|
||||
}
|
||||
|
||||
void QGnomePortalInterface::querySettings()
|
||||
{
|
||||
QDBusConnection dbus = QDBusConnection::sessionBus();
|
||||
if (!dbus.isConnected()) {
|
||||
qCWarning(lcQpaThemeGnome) << "dbus connection failed. Last error: " << dbus.lastError();
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr auto method = "ReadAll"_L1;
|
||||
auto message = QDBusMessage::createMethodCall(s_service, s_path, s_interface, method);
|
||||
|
||||
message << QStringList{ QDBusSettings::XdgSettings::AppearanceNamespace,
|
||||
QDBusSettings::GnomeSettings::AllyNamespace };
|
||||
|
||||
QDBusPendingCall pendingCall = dbus.asyncCall(message);
|
||||
QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall, this);
|
||||
|
||||
auto onWatcherFinished = [&](QDBusPendingCallWatcher *watcher) {
|
||||
const QDBusMessage reply = watcher->reply();
|
||||
if (QDBusMessage::ErrorMessage == reply.type()) {
|
||||
qCWarning(lcQpaThemeGnome)
|
||||
<< "dbus reply error: [" << reply.errorName() << "]" << reply.errorMessage();
|
||||
watcher->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
const auto convertXdgColorScheme = [](const QVariant &value) {
|
||||
using namespace QDBusSettings::XdgSettings;
|
||||
return QVariant::fromValue(convertColorScheme(value));
|
||||
};
|
||||
|
||||
constexpr auto XdgContrastKey = QDBusSettings::XdgSettings::ContrastKey;
|
||||
const auto convertXdgContrast = [](const QVariant &value) {
|
||||
using namespace QDBusSettings::XdgSettings;
|
||||
return QVariant::fromValue(convertContrastPreference(value));
|
||||
};
|
||||
|
||||
constexpr auto GnomeContrastKey = QDBusSettings::GnomeSettings::ContrastKey;
|
||||
const auto convrtGnomeContrast = [](const QVariant &value) {
|
||||
using namespace QDBusSettings::GnomeSettings;
|
||||
return QVariant::fromValue(convertContrastPreference(value));
|
||||
};
|
||||
|
||||
const QVariantList &args = reply.arguments();
|
||||
for (const QVariant &arg_ : args) {
|
||||
const QMap<QString, QVariantMap> arg = qdbus_cast<QMap<QString, QVariantMap>>(arg_);
|
||||
for (auto aIt = arg.begin(); aIt != arg.end(); ++aIt) {
|
||||
const QString &namespace_ = aIt.key();
|
||||
const QVariantMap &settings = aIt.value();
|
||||
for (auto sIt = settings.begin(); sIt != settings.end(); ++sIt) {
|
||||
const QString &key = sIt.key();
|
||||
const QVariant &value = sIt.value();
|
||||
if ((key == QDBusSettings::XdgSettings::ColorSchemeKey)
|
||||
&& (namespace_ == QDBusSettings::XdgSettings::AppearanceNamespace))
|
||||
dbusSettingChanged(QDBusListener::Provider::Gnome,
|
||||
QDBusListener::Setting::ColorScheme,
|
||||
convertXdgColorScheme(value));
|
||||
else if ((key == XdgContrastKey)
|
||||
&& (namespace_ == QDBusSettings::XdgSettings::AppearanceNamespace))
|
||||
dbusSettingChanged(QDBusListener::Provider::Gnome,
|
||||
QDBusListener::Setting::Contrast,
|
||||
convertXdgContrast(value));
|
||||
else if ((key == GnomeContrastKey)
|
||||
&& (namespace_ == QDBusSettings::GnomeSettings::AllyNamespace))
|
||||
dbusSettingChanged(QDBusListener::Provider::Gnome,
|
||||
QDBusListener::Setting::Contrast,
|
||||
convrtGnomeContrast(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
watcher->deleteLater();
|
||||
};
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, onWatcherFinished);
|
||||
}
|
||||
|
||||
void QGnomePortalInterface::updateColorScheme(Qt::ColorScheme colorScheme)
|
||||
{
|
||||
if (m_colorScheme.has_value() && (m_colorScheme.value() == colorScheme))
|
||||
return;
|
||||
m_colorScheme = colorScheme;
|
||||
emit colorSchemeChanged(colorScheme);
|
||||
}
|
||||
|
||||
void QGnomePortalInterface::updateContrast(Qt::ContrastPreference contrast)
|
||||
{
|
||||
if (m_contrast.has_value() && (m_contrast.value() == contrast))
|
||||
return;
|
||||
m_contrast = contrast;
|
||||
emit contrastChanged(contrast);
|
||||
}
|
||||
|
||||
void QGnomePortalInterface::dbusSettingChanged(QDBusListener::Provider provider,
|
||||
QDBusListener::Setting setting,
|
||||
const QVariant &value)
|
||||
{
|
||||
if (provider != QDBusListener::Provider::Gnome && provider != QDBusListener::Provider::Gtk)
|
||||
return;
|
||||
|
||||
switch (setting) {
|
||||
case QDBusListener::Setting::ColorScheme:
|
||||
updateColorScheme(value.value<Qt::ColorScheme>());
|
||||
break;
|
||||
case QDBusListener::Setting::Contrast:
|
||||
updateContrast(value.value<Qt::ContrastPreference>());
|
||||
break;
|
||||
case QDBusListener::Setting::Theme:
|
||||
emit themeNameChanged(value.toString());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "moc_qgnomeportalinterface_p.cpp"
|
63
src/gui/platform/unix/qgnomeportalinterface_p.h
Normal file
63
src/gui/platform/unix/qgnomeportalinterface_p.h
Normal file
@ -0,0 +1,63 @@
|
||||
// Copyright (C) 2025 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 QGNOMEPORTALINTERFACE_P_H
|
||||
#define QGNOMEPORTALINTERFACE_P_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/qtconfigmacros.h>
|
||||
QT_REQUIRE_CONFIG(dbus);
|
||||
|
||||
#include <QtCore/QObject>
|
||||
#include <QtGui/private/qdbuslistener_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class Q_GUI_EXPORT QGnomePortalInterface : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QGnomePortalInterface(QObject *parent = nullptr);
|
||||
~QGnomePortalInterface() = default;
|
||||
|
||||
Qt::ColorScheme colorScheme(Qt::ColorScheme fallback = Qt::ColorScheme::Unknown) const;
|
||||
Qt::ContrastPreference contrastPreference(
|
||||
Qt::ContrastPreference fallback = Qt::ContrastPreference::NoPreference) const;
|
||||
|
||||
private:
|
||||
void querySettings();
|
||||
void updateColorScheme(Qt::ColorScheme colorScheme);
|
||||
void updateContrast(Qt::ContrastPreference contrast);
|
||||
|
||||
Q_SIGNALS:
|
||||
void colorSchemeChanged(Qt::ColorScheme);
|
||||
void contrastChanged(Qt::ContrastPreference);
|
||||
void themeNameChanged(const QString &themeName);
|
||||
|
||||
private Q_SLOTS:
|
||||
void dbusSettingChanged(QDBusListener::Provider, QDBusListener::Setting, const QVariant &value);
|
||||
|
||||
private:
|
||||
mutable uint m_version = 0; // cached version value
|
||||
std::optional<Qt::ColorScheme> m_colorScheme;
|
||||
std::optional<Qt::ContrastPreference> m_contrast;
|
||||
std::unique_ptr<QDBusListener> m_dbus;
|
||||
static constexpr QLatin1StringView s_service{ "org.freedesktop.portal.Desktop" };
|
||||
static constexpr QLatin1StringView s_path{ "/org/freedesktop/portal/desktop" };
|
||||
static constexpr QLatin1StringView s_interface{ "org.freedesktop.portal.Settings" };
|
||||
};
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#endif // QGNOMEPORTALINTERFACE_P_H
|
@ -5,163 +5,13 @@
|
||||
#include <qpa/qplatformdialoghelper.h>
|
||||
#include <qpa/qplatformfontdatabase.h>
|
||||
#if QT_CONFIG(dbus)
|
||||
#include "qdbuslistener_p.h"
|
||||
#include <private/qdbustrayicon_p.h>
|
||||
#include <private/qdbustrayicon_p.h>
|
||||
#include <private/qdbusplatformmenu_p.h>
|
||||
#include <private/qdbusmenubar_p.h>
|
||||
#include <QtDBus/QDBusMessage>
|
||||
#include <QtDBus/QDBusPendingCall>
|
||||
#include <QtDBus/QDBusReply>
|
||||
#include <QtDBus/QDBusVariant>
|
||||
# include <private/qdbustrayicon_p.h>
|
||||
# include <private/qdbusmenubar_p.h>
|
||||
#endif
|
||||
#include <qpa/qwindowsysteminterface.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#if QT_CONFIG(dbus)
|
||||
Q_STATIC_LOGGING_CATEGORY(lcQpaThemeGnome, "qt.qpa.theme.gnome")
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
namespace {
|
||||
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html
|
||||
enum class XDG_ColorScheme : uint { NoPreference, PreferDark, PreferLight };
|
||||
|
||||
constexpr Qt::ColorScheme convertColorScheme(XDG_ColorScheme colorScheme)
|
||||
{
|
||||
switch (colorScheme) {
|
||||
case XDG_ColorScheme::NoPreference:
|
||||
return Qt::ColorScheme::Unknown;
|
||||
case XDG_ColorScheme::PreferDark:
|
||||
return Qt::ColorScheme::Dark;
|
||||
case XDG_ColorScheme::PreferLight:
|
||||
return Qt::ColorScheme::Light;
|
||||
default:
|
||||
Q_UNREACHABLE_RETURN(Qt::ColorScheme::Unknown);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr XDG_ColorScheme convertColorScheme(Qt::ColorScheme colorScheme)
|
||||
{
|
||||
switch (colorScheme) {
|
||||
case Qt::ColorScheme::Unknown:
|
||||
return XDG_ColorScheme::NoPreference;
|
||||
case Qt::ColorScheme::Light:
|
||||
return XDG_ColorScheme::PreferLight;
|
||||
case Qt::ColorScheme::Dark:
|
||||
return XDG_ColorScheme::PreferDark;
|
||||
default:
|
||||
Q_UNREACHABLE_RETURN(XDG_ColorScheme::NoPreference);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
class DBusInterface
|
||||
{
|
||||
DBusInterface() = delete;
|
||||
|
||||
constexpr static auto Service = "org.freedesktop.portal.Desktop"_L1;
|
||||
constexpr static auto Path = "/org/freedesktop/portal/desktop"_L1;
|
||||
|
||||
public:
|
||||
static inline QVariant query(QLatin1StringView interface, QLatin1StringView method,
|
||||
QLatin1StringView name_space, QLatin1StringView key);
|
||||
static inline uint queryPortalVersion();
|
||||
static inline QLatin1StringView readOneMethod();
|
||||
static inline std::optional<Qt::ColorScheme> queryColorScheme();
|
||||
static inline std::optional<Qt::ContrastPreference> queryContrast();
|
||||
};
|
||||
|
||||
QVariant DBusInterface::query(QLatin1StringView interface, QLatin1StringView method,
|
||||
QLatin1StringView name_space, QLatin1StringView key)
|
||||
{
|
||||
QDBusConnection dbus = QDBusConnection::sessionBus();
|
||||
if (dbus.isConnected()) {
|
||||
QDBusMessage message = QDBusMessage::createMethodCall(
|
||||
DBusInterface::Service, DBusInterface::Path, interface, method);
|
||||
message << name_space << key;
|
||||
|
||||
QDBusReply<QVariant> reply = dbus.call(message);
|
||||
if (Q_LIKELY(reply.isValid()))
|
||||
return reply.value();
|
||||
} else {
|
||||
qCWarning(lcQpaThemeGnome) << "dbus connection failed. Last error: " << dbus.lastError();
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
uint DBusInterface::queryPortalVersion()
|
||||
{
|
||||
constexpr auto interface = "org.freedesktop.DBus.Properties"_L1;
|
||||
constexpr auto method = "Get"_L1;
|
||||
constexpr auto name_space = "org.freedesktop.portal.Settings"_L1;
|
||||
constexpr auto key = "version"_L1;
|
||||
|
||||
static uint version = 0; // cached version value
|
||||
|
||||
if (version == 0) {
|
||||
QVariant reply = query(interface, method, name_space, key);
|
||||
if (reply.isValid())
|
||||
version = reply.toUInt(); // caches the value for the next calls
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
QLatin1StringView DBusInterface::readOneMethod()
|
||||
{
|
||||
// Based on the documentation on flatpak:
|
||||
// https://flatpak.github.io/xdg-desktop-portal/docs/doc-org.freedesktop.portal.Settings.html
|
||||
// The method name "Read" has changed to "ReadOne" since version 2.
|
||||
const uint version = queryPortalVersion();
|
||||
if (version == 1)
|
||||
return "Read"_L1;
|
||||
return "ReadOne"_L1;
|
||||
}
|
||||
|
||||
std::optional<Qt::ColorScheme> DBusInterface::queryColorScheme()
|
||||
{
|
||||
constexpr auto interface = "org.freedesktop.portal.Settings"_L1;
|
||||
constexpr auto name_space = "org.freedesktop.appearance"_L1;
|
||||
constexpr auto key = "color-scheme"_L1;
|
||||
const auto method = readOneMethod();
|
||||
|
||||
QVariant reply = query(interface, method, name_space, key);
|
||||
if (reply.isValid())
|
||||
return convertColorScheme(
|
||||
XDG_ColorScheme{ reply.value<QDBusVariant>().variant().toUInt() });
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
std::optional<Qt::ContrastPreference> DBusInterface::queryContrast()
|
||||
{
|
||||
constexpr auto interface = "org.freedesktop.portal.Settings"_L1;
|
||||
const auto method = readOneMethod();
|
||||
|
||||
constexpr auto namespace_xdg_portal = "org.freedesktop.appearance"_L1;
|
||||
constexpr auto key_xdg_portal = "contrast"_L1;
|
||||
QVariant reply = query(interface, method, namespace_xdg_portal, key_xdg_portal);
|
||||
if (reply.isValid())
|
||||
return static_cast<Qt::ContrastPreference>(reply.toUInt());
|
||||
|
||||
// Fall back to desktop-specific methods (GSettings for GNOME)
|
||||
constexpr auto namespace_gsettings = "org.gnome.desktop.a11y.interface"_L1;
|
||||
constexpr auto key_gsettings = "high-contrast"_L1;
|
||||
reply = query(interface, method, namespace_gsettings, key_gsettings);
|
||||
if (reply.isValid())
|
||||
return reply.toBool() ? Qt::ContrastPreference::HighContrast
|
||||
: Qt::ContrastPreference::NoPreference;
|
||||
|
||||
return {};
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#endif // QT_CONFIG(dbus)
|
||||
|
||||
/*!
|
||||
\class QGnomeTheme
|
||||
\brief QGnomeTheme is a theme implementation for the Gnome desktop.
|
||||
@ -174,13 +24,8 @@ const char *QGnomeTheme::name = "gnome";
|
||||
QGnomeThemePrivate::QGnomeThemePrivate()
|
||||
{
|
||||
#if QT_CONFIG(dbus)
|
||||
initDbus();
|
||||
|
||||
if (auto value = DBusInterface::queryColorScheme(); value.has_value())
|
||||
updateColorScheme(value.value());
|
||||
|
||||
if (auto value = DBusInterface::queryContrast(); value.has_value())
|
||||
updateHighContrast(value.value());
|
||||
QObject::connect(&m_gnomePortal, &QGnomePortalInterface::themeNameChanged, &m_gnomePortal,
|
||||
[this](const QString &themeName) { m_themeName = themeName; });
|
||||
#endif // QT_CONFIG(dbus)
|
||||
}
|
||||
|
||||
@ -205,43 +50,15 @@ void QGnomeThemePrivate::configureFonts(const QString >kFontName) const
|
||||
qCDebug(lcQpaFonts) << "default fonts: system" << systemFont << "fixed" << fixedFont;
|
||||
}
|
||||
|
||||
#if QT_CONFIG(dbus)
|
||||
bool QGnomeThemePrivate::initDbus()
|
||||
{
|
||||
dbus.reset(new QDBusListener());
|
||||
Q_ASSERT(dbus);
|
||||
|
||||
// Wrap slot in a lambda to avoid inheriting QGnomeThemePrivate from QObject
|
||||
auto wrapper = [this](QDBusListener::Provider provider,
|
||||
QDBusListener::Setting setting,
|
||||
const QVariant &value) {
|
||||
if (provider != QDBusListener::Provider::Gnome
|
||||
&& provider != QDBusListener::Provider::Gtk) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (setting) {
|
||||
case QDBusListener::Setting::ColorScheme:
|
||||
updateColorScheme(convertColorScheme(XDG_ColorScheme{ value.toUInt() }));
|
||||
break;
|
||||
case QDBusListener::Setting::Theme:
|
||||
m_themeName = value.toString();
|
||||
break;
|
||||
case QDBusListener::Setting::Contrast:
|
||||
updateHighContrast(value.value<Qt::ContrastPreference>());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
return QObject::connect(dbus.get(), &QDBusListener::settingChanged, dbus.get(), wrapper);
|
||||
}
|
||||
|
||||
Qt::ColorScheme QGnomeThemePrivate::colorScheme() const
|
||||
{
|
||||
if (m_colorScheme != Qt::ColorScheme::Unknown)
|
||||
return m_colorScheme;
|
||||
if (hasRequestedColorScheme())
|
||||
return m_requestedColorScheme;
|
||||
|
||||
#if QT_CONFIG(dbus)
|
||||
if (Qt::ColorScheme colorScheme = m_gnomePortal.colorScheme();
|
||||
colorScheme != Qt::ColorScheme::Unknown)
|
||||
return colorScheme;
|
||||
|
||||
// If the color scheme is set to Unknown by mistake or is not set at all,
|
||||
// then maybe the theme name contains a hint about the color scheme.
|
||||
@ -251,31 +68,31 @@ Qt::ColorScheme QGnomeThemePrivate::colorScheme() const
|
||||
return Qt::ColorScheme::Light;
|
||||
else if (m_themeName.contains(QLatin1StringView("dark"), Qt::CaseInsensitive))
|
||||
return Qt::ColorScheme::Dark;
|
||||
else
|
||||
return Qt::ColorScheme::Unknown;
|
||||
}
|
||||
|
||||
void QGnomeThemePrivate::updateColorScheme(Qt::ColorScheme colorScheme)
|
||||
{
|
||||
if (m_colorScheme == colorScheme)
|
||||
return;
|
||||
m_colorScheme = colorScheme;
|
||||
QWindowSystemInterface::handleThemeChange();
|
||||
}
|
||||
|
||||
void QGnomeThemePrivate::updateHighContrast(Qt::ContrastPreference contrast)
|
||||
{
|
||||
if (m_contrast == contrast)
|
||||
return;
|
||||
m_contrast = contrast;
|
||||
QWindowSystemInterface::handleThemeChange();
|
||||
}
|
||||
|
||||
#endif // QT_CONFIG(dbus)
|
||||
|
||||
// Fallback to Unknown if no color scheme is set or detected
|
||||
return Qt::ColorScheme::Unknown;
|
||||
}
|
||||
|
||||
bool QGnomeThemePrivate::hasRequestedColorScheme() const
|
||||
{
|
||||
return m_requestedColorScheme != Qt::ColorScheme::Unknown;
|
||||
}
|
||||
|
||||
QGnomeTheme::QGnomeTheme()
|
||||
: QGenericUnixTheme(new QGnomeThemePrivate())
|
||||
{
|
||||
#if QT_CONFIG(dbus)
|
||||
Q_D(QGnomeTheme);
|
||||
|
||||
QGnomePortalInterface *portal = &d->m_gnomePortal;
|
||||
|
||||
QObject::connect(portal, &QGnomePortalInterface::colorSchemeChanged, portal,
|
||||
[this](Qt::ColorScheme colorScheme) { updateColorScheme(colorScheme); });
|
||||
|
||||
QObject::connect(portal, &QGnomePortalInterface::contrastChanged, portal,
|
||||
[this](Qt::ContrastPreference contrast) { updateHighContrast(contrast); });
|
||||
#endif // QT_CONFIG(dbus)
|
||||
}
|
||||
|
||||
QVariant QGnomeTheme::themeHint(QPlatformTheme::ThemeHint hint) const
|
||||
@ -352,7 +169,38 @@ QString QGnomeTheme::gtkFontName() const
|
||||
.arg(defaultSystemFontSize);
|
||||
}
|
||||
|
||||
void QGnomeTheme::requestColorScheme(Qt::ColorScheme scheme)
|
||||
{
|
||||
Q_D(QGnomeTheme);
|
||||
if (d->m_requestedColorScheme == scheme)
|
||||
return;
|
||||
QPlatformTheme::requestColorScheme(scheme);
|
||||
d->m_requestedColorScheme = scheme;
|
||||
QWindowSystemInterface::handleThemeChange();
|
||||
}
|
||||
|
||||
Qt::ColorScheme QGnomeTheme::colorScheme() const
|
||||
{
|
||||
Q_D(const QGnomeTheme);
|
||||
if (auto colorScheme = d->colorScheme(); colorScheme != Qt::ColorScheme::Unknown)
|
||||
return colorScheme;
|
||||
// If the color scheme is not set or detected, fall back to the default
|
||||
return QPlatformTheme::colorScheme();
|
||||
}
|
||||
|
||||
#if QT_CONFIG(dbus)
|
||||
void QGnomeTheme::updateColorScheme(Qt::ColorScheme colorScheme)
|
||||
{
|
||||
Q_UNUSED(colorScheme);
|
||||
QWindowSystemInterface::handleThemeChange();
|
||||
}
|
||||
|
||||
void QGnomeTheme::updateHighContrast(Qt::ContrastPreference contrast)
|
||||
{
|
||||
Q_UNUSED(contrast);
|
||||
QWindowSystemInterface::handleThemeChange();
|
||||
}
|
||||
|
||||
QPlatformMenuBar *QGnomeTheme::createPlatformMenuBar() const
|
||||
{
|
||||
if (isDBusGlobalMenuAvailable())
|
||||
@ -360,26 +208,21 @@ QPlatformMenuBar *QGnomeTheme::createPlatformMenuBar() const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Qt::ColorScheme QGnomeTheme::colorScheme() const
|
||||
{
|
||||
return d_func()->colorScheme();
|
||||
}
|
||||
|
||||
Qt::ContrastPreference QGnomeTheme::contrastPreference() const
|
||||
{
|
||||
return d_func()->m_contrast;
|
||||
Q_D(const QGnomeTheme);
|
||||
return d->m_gnomePortal.contrastPreference();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if QT_CONFIG(dbus) && QT_CONFIG(systemtrayicon)
|
||||
# if QT_CONFIG(systemtrayicon)
|
||||
QPlatformSystemTrayIcon *QGnomeTheme::createPlatformSystemTrayIcon() const
|
||||
{
|
||||
if (shouldUseDBusTray())
|
||||
return new QDBusTrayIcon();
|
||||
return nullptr;
|
||||
}
|
||||
#endif
|
||||
# endif // QT_CONFIG(systemtrayicon)
|
||||
#endif // QT_CONFIG(dbus)
|
||||
|
||||
QString QGnomeTheme::standardButtonText(int button) const
|
||||
{
|
||||
|
@ -19,17 +19,19 @@
|
||||
#include <qpa/qplatformtheme.h>
|
||||
#include <qpa/qplatformtheme_p.h>
|
||||
#include <QtGui/QFont>
|
||||
#if QT_CONFIG(dbus)
|
||||
# include "qgnomeportalinterface_p.h"
|
||||
#endif // QT_CONFIG(dbus)
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QGnomeThemePrivate;
|
||||
#if QT_CONFIG(dbus)
|
||||
class QDBusListener;
|
||||
class QDBusPendingCallWatcher;
|
||||
#endif
|
||||
|
||||
class Q_GUI_EXPORT QGnomeTheme : public QGenericUnixTheme
|
||||
{
|
||||
protected:
|
||||
Q_DECLARE_PRIVATE(QGnomeTheme)
|
||||
|
||||
public:
|
||||
QGnomeTheme();
|
||||
QVariant themeHint(ThemeHint hint) const override;
|
||||
@ -39,47 +41,51 @@ public:
|
||||
QString standardButtonText(int button) const override;
|
||||
|
||||
virtual QString gtkFontName() const;
|
||||
|
||||
virtual void requestColorScheme(Qt::ColorScheme) override;
|
||||
virtual Qt::ColorScheme colorScheme() const override;
|
||||
|
||||
#if QT_CONFIG(dbus)
|
||||
protected:
|
||||
virtual void updateColorScheme(Qt::ColorScheme);
|
||||
virtual void updateHighContrast(Qt::ContrastPreference);
|
||||
|
||||
public:
|
||||
QPlatformMenuBar *createPlatformMenuBar() const override;
|
||||
Qt::ColorScheme colorScheme() const override;
|
||||
Qt::ContrastPreference contrastPreference() const override;
|
||||
#endif
|
||||
#if QT_CONFIG(dbus) && QT_CONFIG(systemtrayicon)
|
||||
|
||||
# if QT_CONFIG(systemtrayicon)
|
||||
QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override;
|
||||
#endif
|
||||
# endif // QT_CONFIG(systemtrayicon)
|
||||
#endif // QT_CONFIG(dbus)
|
||||
|
||||
static const char *name;
|
||||
};
|
||||
|
||||
class QGnomeThemePrivate : public QGenericUnixThemePrivate
|
||||
class Q_GUI_EXPORT QGnomeThemePrivate : public QGenericUnixThemePrivate
|
||||
{
|
||||
friend QGnomeTheme;
|
||||
|
||||
public:
|
||||
QGnomeThemePrivate();
|
||||
~QGnomeThemePrivate();
|
||||
|
||||
void configureFonts(const QString >kFontName) const;
|
||||
|
||||
Qt::ColorScheme colorScheme() const;
|
||||
bool hasRequestedColorScheme() const;
|
||||
|
||||
private:
|
||||
mutable QFont *systemFont = nullptr;
|
||||
mutable QFont *fixedFont = nullptr;
|
||||
|
||||
Qt::ColorScheme m_requestedColorScheme = Qt::ColorScheme::Unknown;
|
||||
|
||||
#if QT_CONFIG(dbus)
|
||||
Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown;
|
||||
Qt::ContrastPreference m_contrast = Qt::ContrastPreference::NoPreference;
|
||||
private:
|
||||
std::unique_ptr<QDBusListener> dbus;
|
||||
std::unique_ptr<QDBusPendingCallWatcher> pendingCallWatcher;
|
||||
QGnomePortalInterface m_gnomePortal;
|
||||
QString m_themeName;
|
||||
|
||||
public:
|
||||
Qt::ColorScheme colorScheme() const;
|
||||
|
||||
private:
|
||||
bool initDbus();
|
||||
void updateColorScheme(Qt::ColorScheme colorScheme);
|
||||
void updateHighContrast(Qt::ContrastPreference contrast);
|
||||
#endif // QT_CONFIG(dbus)
|
||||
};
|
||||
|
||||
|
||||
QT_END_NAMESPACE
|
||||
#endif // QGNOMETHEME_P_H
|
||||
|
@ -130,7 +130,7 @@ void QKdeThemePrivate::settingChangedHandler(QDBusListener::Provider provider,
|
||||
|
||||
switch (setting) {
|
||||
case QDBusListener::Setting::ColorScheme:
|
||||
qCDebug(lcQpaThemeKde) << "KDE color theme changed to:" << value.toUInt();
|
||||
qCDebug(lcQpaThemeKde) << "KDE color theme changed to:" << value.value<Qt::ColorScheme>();
|
||||
break;
|
||||
case QDBusListener::Setting::Theme:
|
||||
qCDebug(lcQpaThemeKde) << "KDE global theme changed to:" << value.toString();
|
||||
|
@ -37,8 +37,6 @@ qt_internal_add_plugin(QGtk3ThemePlugin
|
||||
)
|
||||
|
||||
qt_internal_extend_target(QGtk3ThemePlugin CONDITION QT_FEATURE_dbus
|
||||
SOURCES
|
||||
qgtk3portalinterface.cpp
|
||||
LIBRARIES
|
||||
Qt::DBus
|
||||
)
|
||||
|
@ -1,123 +0,0 @@
|
||||
// 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"
|
@ -1,49 +0,0 @@
|
||||
// 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
|
@ -15,6 +15,9 @@
|
||||
#include "qgtk3json_p.h"
|
||||
#include "qgtk3storage_p.h"
|
||||
#include <qpa/qwindowsysteminterface.h>
|
||||
#if QT_CONFIG(dbus)
|
||||
# include <QtGui/private/qgnomeportalinterface_p.h>
|
||||
#endif
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
@ -22,11 +25,13 @@ QGtk3Storage::QGtk3Storage()
|
||||
{
|
||||
m_interface.reset(new QGtk3Interface(this));
|
||||
#if QT_CONFIG(dbus)
|
||||
m_portalInterface.reset(new QGtk3PortalInterface(this));
|
||||
m_portalInterface.reset(new QGnomePortalInterface);
|
||||
#endif
|
||||
populateMap();
|
||||
}
|
||||
|
||||
QGtk3Storage::~QGtk3Storage() { }
|
||||
|
||||
/*!
|
||||
\internal
|
||||
\enum QGtk3Storage::SourceType
|
||||
|
@ -16,9 +16,6 @@
|
||||
//
|
||||
|
||||
#include "qgtk3interface_p.h"
|
||||
#if QT_CONFIG(dbus)
|
||||
#include "qgtk3portalinterface_p.h"
|
||||
#endif
|
||||
|
||||
#include <QtCore/QJsonDocument>
|
||||
#include <QtCore/QCache>
|
||||
@ -30,11 +27,17 @@
|
||||
#include <private/qflatmap_p.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#if QT_CONFIG(dbus)
|
||||
class QGnomePortalInterface;
|
||||
#endif
|
||||
|
||||
class QGtk3Storage
|
||||
{
|
||||
Q_GADGET
|
||||
public:
|
||||
QGtk3Storage();
|
||||
~QGtk3Storage();
|
||||
|
||||
// Enum documented in cpp file. Please keep it in line with updates made here.
|
||||
enum class SourceType {
|
||||
@ -242,7 +245,7 @@ private:
|
||||
|
||||
std::unique_ptr<QGtk3Interface> m_interface;
|
||||
#if QT_CONFIG(dbus)
|
||||
std::unique_ptr<QGtk3PortalInterface> m_portalInterface;
|
||||
std::unique_ptr<QGnomePortalInterface> m_portalInterface;
|
||||
#endif
|
||||
|
||||
Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown;
|
||||
|
@ -163,29 +163,34 @@ QString QGtk3Theme::gtkFontName() const
|
||||
return QGnomeTheme::gtkFontName();
|
||||
}
|
||||
|
||||
void QGtk3Theme::requestColorScheme(Qt::ColorScheme scheme)
|
||||
{
|
||||
if (m_requestedColorScheme == scheme)
|
||||
return;
|
||||
qCDebug(lcQGtk3Interface) << scheme << "has been requested. Theme supports color scheme:"
|
||||
<< m_storage->colorScheme();
|
||||
QPlatformTheme::requestColorScheme(scheme);
|
||||
m_requestedColorScheme = scheme;
|
||||
m_storage->handleThemeChange();
|
||||
}
|
||||
|
||||
Qt::ColorScheme QGtk3Theme::colorScheme() const
|
||||
{
|
||||
Q_ASSERT(m_storage);
|
||||
|
||||
Q_D(const QGnomeTheme);
|
||||
const Qt::ColorScheme colorScheme = d->colorScheme();
|
||||
const bool hasRequestedColorScheme = d->hasRequestedColorScheme();
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
if (m_requestedColorScheme != Qt::ColorScheme::Unknown
|
||||
&& m_requestedColorScheme != m_storage->colorScheme()) {
|
||||
qCDebug(lcQGtk3Interface) << "Requested color scheme" << m_requestedColorScheme
|
||||
if (hasRequestedColorScheme && colorScheme != m_storage->colorScheme()) {
|
||||
qCDebug(lcQGtk3Interface) << "Requested color scheme" << colorScheme
|
||||
<< "differs from theme color scheme" << m_storage->colorScheme();
|
||||
}
|
||||
#endif
|
||||
return m_requestedColorScheme == Qt::ColorScheme::Unknown ? m_storage->colorScheme()
|
||||
: m_requestedColorScheme;
|
||||
|
||||
return hasRequestedColorScheme ? colorScheme : m_storage->colorScheme();
|
||||
}
|
||||
|
||||
void QGtk3Theme::requestColorScheme(Qt::ColorScheme scheme)
|
||||
{
|
||||
const Qt::ColorScheme oldColorScheme = colorScheme();
|
||||
QGnomeTheme::requestColorScheme(scheme);
|
||||
if (oldColorScheme == colorScheme())
|
||||
return;
|
||||
qCDebug(lcQGtk3Interface) << scheme << "has been requested. Theme supports color scheme:"
|
||||
<< m_storage->colorScheme();
|
||||
m_storage->handleThemeChange();
|
||||
QWindowSystemInterface::sendWindowSystemEvents(QEventLoop::AllEvents);
|
||||
}
|
||||
|
||||
bool QGtk3Theme::usePlatformNativeDialog(DialogType type) const
|
||||
@ -234,18 +239,22 @@ bool QGtk3Theme::useNativeFileDialog()
|
||||
const QPalette *QGtk3Theme::palette(Palette type) const
|
||||
{
|
||||
Q_ASSERT(m_storage);
|
||||
|
||||
Q_D(const QGnomeTheme);
|
||||
const Qt::ColorScheme colorScheme = d->colorScheme();
|
||||
const bool hasRequestedColorScheme = d->hasRequestedColorScheme();
|
||||
|
||||
#ifdef QT_DEBUG
|
||||
if (m_requestedColorScheme != Qt::ColorScheme::Unknown
|
||||
&& m_requestedColorScheme != m_storage->colorScheme()) {
|
||||
if (hasRequestedColorScheme && colorScheme != m_storage->colorScheme()) {
|
||||
qCDebug(lcQGtk3Interface) << "Current KDE theme doesn't support requested color scheme"
|
||||
<< m_requestedColorScheme << "Falling back to fusion palette.";
|
||||
<< colorScheme << "Falling back to fusion palette.";
|
||||
return QPlatformTheme::palette(type);
|
||||
}
|
||||
#endif
|
||||
|
||||
return (m_requestedColorScheme != Qt::ColorScheme::Unknown
|
||||
&& m_requestedColorScheme != m_storage->colorScheme())
|
||||
? QPlatformTheme::palette(type) : m_storage->palette(type);
|
||||
return (hasRequestedColorScheme && colorScheme != m_storage->colorScheme())
|
||||
? QPlatformTheme::palette(type)
|
||||
: m_storage->palette(type);
|
||||
}
|
||||
|
||||
QPixmap QGtk3Theme::standardPixmap(StandardPixmap sp, const QSizeF &size) const
|
||||
@ -268,4 +277,14 @@ QIcon QGtk3Theme::fileIcon(const QFileInfo &fileInfo,
|
||||
return m_storage->fileIcon(fileInfo);
|
||||
}
|
||||
|
||||
#if QT_CONFIG(dbus)
|
||||
void QGtk3Theme::updateColorScheme(Qt::ColorScheme newColorScheme)
|
||||
{
|
||||
if (newColorScheme == colorScheme())
|
||||
QGnomeTheme::updateColorScheme(newColorScheme);
|
||||
else
|
||||
m_storage->handleThemeChange();
|
||||
}
|
||||
#endif // QT_CONFIG(dbus)
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
@ -31,8 +31,11 @@ public:
|
||||
QPlatformTheme::IconOptions iconOptions = { }) const override;
|
||||
|
||||
static const char *name;
|
||||
|
||||
private:
|
||||
Qt::ColorScheme m_requestedColorScheme = Qt::ColorScheme::Unknown;
|
||||
#if QT_CONFIG(dbus)
|
||||
void updateColorScheme(Qt::ColorScheme) override;
|
||||
#endif // QT_CONFIG(dbus)
|
||||
static bool useNativeFileDialog();
|
||||
std::unique_ptr<QGtk3Storage> m_storage;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user