diff options
author | Oliver Eftevaag <[email protected]> | 2025-02-20 18:27:50 +0100 |
---|---|---|
committer | Oliver Eftevaag <[email protected]> | 2025-03-28 17:44:39 +0100 |
commit | 4ac89dad78772ce90649b9846efa17319deba28f (patch) | |
tree | e7a2b51b6d7a19e56b8efdb39f8629437ca67601 | |
parent | 8c943392ae2ae554e496f11d9a3dd6c2c2e762e3 (diff) |
Add virtual member QPlatformTheme::contrastPreference()
This function can be overridden by individual platform themes, in order
to read contrast settings from the platform's system settings, and
report the result back to Qt.
This information is relevant for our styles, and can be used to
determine color palette values, and additional elements like outline
thickness for controls/widgets.
Currently only the Windows, macOS, Gnome and Flatpak themes support this
feature.
Task-number: QTBUG-133595
Change-Id: I3aff519aa7f07c8b2fdcc1e7fb35ec719ab8efcc
Reviewed-by: Tor Arne Vestbø <[email protected]>
-rw-r--r-- | src/corelib/global/qnamespace.h | 6 | ||||
-rw-r--r-- | src/gui/kernel/qplatformtheme.cpp | 5 | ||||
-rw-r--r-- | src/gui/kernel/qplatformtheme.h | 1 | ||||
-rw-r--r-- | src/gui/platform/unix/qdbuslistener.cpp | 5 | ||||
-rw-r--r-- | src/gui/platform/unix/qdbuslistener_p.h | 3 | ||||
-rw-r--r-- | src/gui/platform/unix/qgnometheme.cpp | 57 | ||||
-rw-r--r-- | src/gui/platform/unix/qgnometheme_p.h | 3 | ||||
-rw-r--r-- | src/gui/platform/unix/qkdetheme.cpp | 14 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoatheme.h | 1 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qcocoatheme.mm | 6 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowstheme.cpp | 6 | ||||
-rw-r--r-- | src/plugins/platforms/windows/qwindowstheme.h | 1 | ||||
-rw-r--r-- | src/plugins/platformthemes/gtk3/qgtk3theme.cpp | 2 | ||||
-rw-r--r-- | src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp | 35 | ||||
-rw-r--r-- | src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.h | 1 |
15 files changed, 125 insertions, 21 deletions
diff --git a/src/corelib/global/qnamespace.h b/src/corelib/global/qnamespace.h index f079a2fc83c..71ec627461c 100644 --- a/src/corelib/global/qnamespace.h +++ b/src/corelib/global/qnamespace.h @@ -54,6 +54,11 @@ namespace Qt { Dark, }; + enum class ContrastPreference { + NoPreference, + HighContrast, + }; + enum MouseButton { NoButton = 0x00000000, LeftButton = 0x00000001, @@ -1786,6 +1791,7 @@ namespace Qt { Q_ENUM_NS(CursorShape) Q_ENUM_NS(GlobalColor) Q_ENUM_NS(ColorScheme) + Q_ENUM_NS(ContrastPreference) Q_ENUM_NS(AspectRatioMode) Q_ENUM_NS(TransformationMode) Q_FLAG_NS(ImageConversionFlags) diff --git a/src/gui/kernel/qplatformtheme.cpp b/src/gui/kernel/qplatformtheme.cpp index d2a1625bf71..6f840916448 100644 --- a/src/gui/kernel/qplatformtheme.cpp +++ b/src/gui/kernel/qplatformtheme.cpp @@ -484,6 +484,11 @@ void QPlatformTheme::requestColorScheme(Qt::ColorScheme scheme) } } +Qt::ContrastPreference QPlatformTheme::contrastPreference() const +{ + return Qt::ContrastPreference::NoPreference; +} + /*! \internal \brief Return a color palette for type \a type. diff --git a/src/gui/kernel/qplatformtheme.h b/src/gui/kernel/qplatformtheme.h index a81d0624db3..c39d1cdcded 100644 --- a/src/gui/kernel/qplatformtheme.h +++ b/src/gui/kernel/qplatformtheme.h @@ -319,6 +319,7 @@ public: virtual QKeySequence standardButtonShortcut(int button) const; #endif virtual void requestColorScheme(Qt::ColorScheme scheme); + virtual Qt::ContrastPreference contrastPreference() const; static QVariant defaultThemeHint(ThemeHint hint); static QString defaultStandardButtonText(int button); diff --git a/src/gui/platform/unix/qdbuslistener.cpp b/src/gui/platform/unix/qdbuslistener.cpp index 213d190fa6b..3c8b6f741b2 100644 --- a/src/gui/platform/unix/qdbuslistener.cpp +++ b/src/gui/platform/unix/qdbuslistener.cpp @@ -209,6 +209,9 @@ void QDBusListener::populateSignalMap() m_signalMap.insert(DBusKey("org.freedesktop.appearance"_L1, "color-scheme"_L1), ChangeSignal(Provider::Gnome, Setting::ColorScheme)); + m_signalMap.insert(DBusKey("org.freedesktop.appearance"_L1, "contrast"_L1), + ChangeSignal(Provider::Gnome, Setting::Contrast)); + const QString &saveJsonFile = qEnvironmentVariable("QT_QPA_DBUS_SIGNALS_SAVE"); if (!saveJsonFile.isEmpty()) saveJson(saveJsonFile); @@ -232,6 +235,6 @@ void QDBusListener::onSettingChanged(const QString &location, const QString &key if (!sig.has_value()) return; - emit settingChanged(sig.value().provider, sig.value().setting, value.variant().toString()); + emit settingChanged(sig.value().provider, sig.value().setting, value.variant()); } QT_END_NAMESPACE diff --git a/src/gui/platform/unix/qdbuslistener_p.h b/src/gui/platform/unix/qdbuslistener_p.h index cde62266177..9e06d20d839 100644 --- a/src/gui/platform/unix/qdbuslistener_p.h +++ b/src/gui/platform/unix/qdbuslistener_p.h @@ -38,6 +38,7 @@ public: Theme, ApplicationStyle, ColorScheme, + Contrast, }; Q_ENUM(Setting) @@ -51,7 +52,7 @@ private Q_SLOTS: Q_SIGNALS: void settingChanged(QDBusListener::Provider provider, QDBusListener::Setting setting, - const QString &value); + const QVariant &value); private: struct DBusKey diff --git a/src/gui/platform/unix/qgnometheme.cpp b/src/gui/platform/unix/qgnometheme.cpp index 0ed01b5cf72..bce0eca0629 100644 --- a/src/gui/platform/unix/qgnometheme.cpp +++ b/src/gui/platform/unix/qgnometheme.cpp @@ -15,6 +15,10 @@ QT_BEGIN_NAMESPACE +#ifndef QT_NO_DBUS +Q_STATIC_LOGGING_CATEGORY(lcQpaThemeGnome, "qt.qpa.theme.gnome") +#endif // QT_NO_DBUS + /*! \class QGnomeTheme \brief QGnomeTheme is a theme implementation for the Gnome desktop. @@ -27,7 +31,30 @@ const char *QGnomeTheme::name = "gnome"; QGnomeThemePrivate::QGnomeThemePrivate() { #ifndef QT_NO_DBUS - initDbus(); + QDBusMessage message = QDBusMessage::createMethodCall(QLatin1String("org.freedesktop.portal.Desktop"), + QLatin1String("/org/freedesktop/portal/desktop"), + QLatin1String("org.freedesktop.portal.Settings"), + QLatin1String("ReadOne")); + static constexpr QLatin1String appearanceNamespace("org.freedesktop.appearance"); + static constexpr QLatin1String contrastKey("contrast"); + + message << appearanceNamespace << contrastKey; + + QDBusConnection dbus = QDBusConnection::sessionBus(); + if (!dbus.isConnected()) + qCWarning(lcQpaThemeGnome) << "dbus connection failed. Last error: " << dbus.lastError(); + + QDBusPendingCall pendingCall = dbus.asyncCall(message); + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(pendingCall); + QObject::connect(watcher, &QDBusPendingCallWatcher::finished, watcher, [this](QDBusPendingCallWatcher *watcher) { + if (!watcher->isError()) { + QDBusPendingReply<QVariant> reply = *watcher; + if (Q_LIKELY(reply.isValid())) + m_contrast = static_cast<Qt::ContrastPreference>(reply.value().toUInt()); + } + watcher->deleteLater(); + initDbus(); + }); #endif // QT_NO_DBUS } QGnomeThemePrivate::~QGnomeThemePrivate() @@ -60,14 +87,22 @@ bool QGnomeThemePrivate::initDbus() // Wrap slot in a lambda to avoid inheriting QGnomeThemePrivate from QObject auto wrapper = [this](QDBusListener::Provider provider, QDBusListener::Setting setting, - const QString &value) { + const QVariant &value) { if (provider != QDBusListener::Provider::Gnome && provider != QDBusListener::Provider::Gtk) { return; } - if (setting == QDBusListener::Setting::Theme) - updateColorScheme(value); + switch (setting) { + case QDBusListener::Setting::Theme: + updateColorScheme(value.toString()); + break; + case QDBusListener::Setting::Contrast: + updateHighContrast(static_cast<Qt::ContrastPreference>(value.toUInt())); + break; + default: + break; + } }; return QObject::connect(dbus.get(), &QDBusListener::settingChanged, dbus.get(), wrapper); @@ -87,6 +122,15 @@ void QGnomeThemePrivate::updateColorScheme(const QString &themeName) if (oldColorScheme != m_colorScheme) QWindowSystemInterface::handleThemeChange(); } + +void QGnomeThemePrivate::updateHighContrast(Qt::ContrastPreference contrast) +{ + if (m_contrast == contrast) + return; + m_contrast = contrast; + QWindowSystemInterface::handleThemeChange(); +} + #endif // QT_NO_DBUS QGnomeTheme::QGnomeTheme() @@ -181,6 +225,11 @@ Qt::ColorScheme QGnomeTheme::colorScheme() const return d_func()->m_colorScheme; } +Qt::ContrastPreference QGnomeTheme::contrastPreference() const +{ + return d_func()->m_contrast; +} + #endif #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON) diff --git a/src/gui/platform/unix/qgnometheme_p.h b/src/gui/platform/unix/qgnometheme_p.h index 310af45ecb5..490aa2a4b16 100644 --- a/src/gui/platform/unix/qgnometheme_p.h +++ b/src/gui/platform/unix/qgnometheme_p.h @@ -41,6 +41,7 @@ public: #ifndef QT_NO_DBUS QPlatformMenuBar *createPlatformMenuBar() const override; Qt::ColorScheme colorScheme() const override; + Qt::ContrastPreference contrastPreference() const override; #endif #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON) QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override; @@ -62,10 +63,12 @@ public: #ifndef QT_NO_DBUS Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown; + Qt::ContrastPreference m_contrast = Qt::ContrastPreference::NoPreference; private: std::unique_ptr<QDBusListener> dbus; bool initDbus(); void updateColorScheme(const QString &themeName); + void updateHighContrast(Qt::ContrastPreference contrast); #endif // QT_NO_DBUS }; diff --git a/src/gui/platform/unix/qkdetheme.cpp b/src/gui/platform/unix/qkdetheme.cpp index 07df7124430..af6edbc3f8f 100644 --- a/src/gui/platform/unix/qkdetheme.cpp +++ b/src/gui/platform/unix/qkdetheme.cpp @@ -114,7 +114,7 @@ private: bool initDbus(); void settingChangedHandler(QDBusListener::Provider provider, QDBusListener::Setting setting, - const QString &value); + const QVariant &value); Qt::ColorScheme colorSchemeFromPalette() const; #endif // QT_NO_DBUS void clearResources(); @@ -123,21 +123,23 @@ private: #ifndef QT_NO_DBUS void QKdeThemePrivate::settingChangedHandler(QDBusListener::Provider provider, QDBusListener::Setting setting, - const QString &value) + const QVariant &value) { if (provider != QDBusListener::Provider::Kde) return; switch (setting) { case QDBusListener::Setting::ColorScheme: - qCDebug(lcQpaThemeKde) << "KDE color theme changed to:" << value; + qCDebug(lcQpaThemeKde) << "KDE color theme changed to:" << value.toUInt(); break; case QDBusListener::Setting::Theme: - qCDebug(lcQpaThemeKde) << "KDE global theme changed to:" << value; + qCDebug(lcQpaThemeKde) << "KDE global theme changed to:" << value.toString(); break; case QDBusListener::Setting::ApplicationStyle: - qCDebug(lcQpaThemeKde) << "KDE application style changed to:" << value; + qCDebug(lcQpaThemeKde) << "KDE application style changed to:" << value.toString(); break; + case QDBusListener::Setting::Contrast: + qCDebug(lcQpaThemeKde) << "KDE contrast setting changed to: " << static_cast<Qt::ContrastPreference>(value.toUInt()); } refresh(); @@ -158,7 +160,7 @@ bool QKdeThemePrivate::initDbus() // Wrap slot in a lambda to avoid inheriting QKdeThemePrivate from QObject auto wrapper = [this](QDBusListener::Provider provider, QDBusListener::Setting setting, - const QString &value) { + const QVariant &value) { settingChangedHandler(provider, setting, value); }; diff --git a/src/plugins/platforms/cocoa/qcocoatheme.h b/src/plugins/platforms/cocoa/qcocoatheme.h index 97e0f633a79..986901362b3 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.h +++ b/src/plugins/platforms/cocoa/qcocoatheme.h @@ -39,6 +39,7 @@ public: QVariant themeHint(ThemeHint hint) const override; Qt::ColorScheme colorScheme() const override; + Qt::ContrastPreference contrastPreference() const override; QString standardButtonText(int button) const override; QKeySequence standardButtonShortcut(int button) const override; diff --git a/src/plugins/platforms/cocoa/qcocoatheme.mm b/src/plugins/platforms/cocoa/qcocoatheme.mm index 507c5d775f9..2ce621c27aa 100644 --- a/src/plugins/platforms/cocoa/qcocoatheme.mm +++ b/src/plugins/platforms/cocoa/qcocoatheme.mm @@ -508,6 +508,12 @@ void QCocoaTheme::updateColorScheme() m_colorScheme = qt_mac_applicationIsInDarkMode() ? Qt::ColorScheme::Dark : Qt::ColorScheme::Light; } +Qt::ContrastPreference QCocoaTheme::contrastPreference() const +{ + return NSWorkspace.sharedWorkspace.accessibilityDisplayShouldIncreaseContrast ? Qt::ContrastPreference::HighContrast + : Qt::ContrastPreference::NoPreference; +} + QString QCocoaTheme::standardButtonText(int button) const { return button == QPlatformDialogHelper::Discard ? diff --git a/src/plugins/platforms/windows/qwindowstheme.cpp b/src/plugins/platforms/windows/qwindowstheme.cpp index 7881720d338..fd09fc5f00d 100644 --- a/src/plugins/platforms/windows/qwindowstheme.cpp +++ b/src/plugins/platforms/windows/qwindowstheme.cpp @@ -597,6 +597,12 @@ void QWindowsTheme::requestColorScheme(Qt::ColorScheme scheme) handleSettingsChanged(); } +Qt::ContrastPreference QWindowsTheme::contrastPreference() const +{ + return queryHighContrast() ? Qt::ContrastPreference::HighContrast + : Qt::ContrastPreference::NoPreference; +} + void QWindowsTheme::handleSettingsChanged() { const auto oldColorScheme = s_colorScheme; diff --git a/src/plugins/platforms/windows/qwindowstheme.h b/src/plugins/platforms/windows/qwindowstheme.h index a89fb1e5bd1..5901b14dce2 100644 --- a/src/plugins/platforms/windows/qwindowstheme.h +++ b/src/plugins/platforms/windows/qwindowstheme.h @@ -33,6 +33,7 @@ public: Qt::ColorScheme colorScheme() const override; void requestColorScheme(Qt::ColorScheme scheme) override; + Qt::ContrastPreference contrastPreference() const override; static void handleSettingsChanged(); diff --git a/src/plugins/platformthemes/gtk3/qgtk3theme.cpp b/src/plugins/platformthemes/gtk3/qgtk3theme.cpp index 25decb235b7..93aa9f33eb7 100644 --- a/src/plugins/platformthemes/gtk3/qgtk3theme.cpp +++ b/src/plugins/platformthemes/gtk3/qgtk3theme.cpp @@ -233,7 +233,7 @@ const QPalette *QGtk3Theme::palette(Palette type) const #ifdef QT_DEBUG if (m_requestedColorScheme != Qt::ColorScheme::Unknown && m_requestedColorScheme != m_storage->colorScheme()) { - qCDebug(lcQGtk3Interface) << "Current KDE theme doesn't support reuqested color scheme" + qCDebug(lcQGtk3Interface) << "Current KDE theme doesn't support requested color scheme" << m_requestedColorScheme << "Falling back to fusion palette."; return QPlatformTheme::palette(type); } diff --git a/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp b/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp index 2b3fb2b9494..951c1833106 100644 --- a/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp +++ b/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.cpp @@ -20,6 +20,10 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; +static constexpr QLatin1StringView appearanceInterface("org.freedesktop.appearance"); +static constexpr QLatin1StringView colorSchemeKey("color-scheme"); +static constexpr QLatin1StringView contrastKey("contrast"); + class QXdgDesktopPortalThemePrivate : public QObject { Q_OBJECT @@ -67,9 +71,14 @@ public Q_SLOTS: void settingChanged(const QString &group, const QString &key, const QDBusVariant &value) { - if (group == "org.freedesktop.appearance"_L1 && key == "color-scheme"_L1) { - colorScheme = colorSchemeFromXdgPref(static_cast<XdgColorschemePref>(value.variant().toUInt())); - QWindowSystemInterface::handleThemeChange(); + if (group == appearanceInterface) { + if (key == colorSchemeKey) { + colorScheme = colorSchemeFromXdgPref(static_cast<XdgColorschemePref>(value.variant().toUInt())); + QWindowSystemInterface::handleThemeChange(); + } else if (key == contrastKey) { + contrast = static_cast<Qt::ContrastPreference>(value.variant().toUInt()); + QWindowSystemInterface::handleThemeChange(); + } } } @@ -77,6 +86,7 @@ public: QPlatformTheme *baseTheme = nullptr; uint fileChooserPortalVersion = 0; Qt::ColorScheme colorScheme = Qt::ColorScheme::Unknown; + Qt::ContrastPreference contrast = Qt::ContrastPreference::NoPreference; }; QXdgDesktopPortalTheme::QXdgDesktopPortalTheme() @@ -123,15 +133,18 @@ QXdgDesktopPortalTheme::QXdgDesktopPortalTheme() message = QDBusMessage::createMethodCall("org.freedesktop.portal.Desktop"_L1, "/org/freedesktop/portal/desktop"_L1, "org.freedesktop.portal.Settings"_L1, - "Read"_L1); - message << "org.freedesktop.appearance"_L1 << "color-scheme"_L1; + "ReadAll"_L1); + message << appearanceInterface; // this must not be asyncCall() because we have to set appearance now QDBusReply<QVariant> reply = QDBusConnection::sessionBus().call(message); if (reply.isValid()) { - const QDBusVariant dbusVariant = qvariant_cast<QDBusVariant>(reply.value()); - const QXdgDesktopPortalThemePrivate::XdgColorschemePref xdgPref = static_cast<QXdgDesktopPortalThemePrivate::XdgColorschemePref>(dbusVariant.variant().toUInt()); - d->colorScheme = QXdgDesktopPortalThemePrivate::colorSchemeFromXdgPref(xdgPref); + const QMap<QString, QVariantMap> settingsMap = qvariant_cast<QMap<QString, QVariantMap>>(reply.value()); + if (!settingsMap.isEmpty()) { + const auto xdgColorSchemePref = static_cast<QXdgDesktopPortalThemePrivate::XdgColorschemePref>(settingsMap.value(appearanceInterface).value(colorSchemeKey).toUInt()); + d->colorScheme = QXdgDesktopPortalThemePrivate::colorSchemeFromXdgPref(xdgColorSchemePref); + d->contrast = static_cast<Qt::ContrastPreference>(settingsMap.value(appearanceInterface).value(contrastKey).toUInt()); + } } QDBusConnection::sessionBus().connect( @@ -225,6 +238,12 @@ Qt::ColorScheme QXdgDesktopPortalTheme::colorScheme() const return d->colorScheme; } +Qt::ContrastPreference QXdgDesktopPortalTheme::contrastPreference() const +{ + Q_D(const QXdgDesktopPortalTheme); + return d->contrast; +} + QPixmap QXdgDesktopPortalTheme::standardPixmap(StandardPixmap sp, const QSizeF &size) const { Q_D(const QXdgDesktopPortalTheme); diff --git a/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.h b/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.h index 19329f53a4e..ca2f83fddb9 100644 --- a/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.h +++ b/src/plugins/platformthemes/xdgdesktopportal/qxdgdesktopportaltheme.h @@ -35,6 +35,7 @@ public: QVariant themeHint(ThemeHint hint) const override; Qt::ColorScheme colorScheme() const override; + Qt::ContrastPreference contrastPreference() const override; QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const override; QIcon fileIcon(const QFileInfo &fileInfo, |