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 reply = *watcher; + if (Q_LIKELY(reply.isValid())) + m_contrast = static_cast(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(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 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(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(value.variant().toUInt())); - QWindowSystemInterface::handleThemeChange(); + if (group == appearanceInterface) { + if (key == colorSchemeKey) { + colorScheme = colorSchemeFromXdgPref(static_cast(value.variant().toUInt())); + QWindowSystemInterface::handleThemeChange(); + } else if (key == contrastKey) { + contrast = static_cast(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 reply = QDBusConnection::sessionBus().call(message); if (reply.isValid()) { - const QDBusVariant dbusVariant = qvariant_cast(reply.value()); - const QXdgDesktopPortalThemePrivate::XdgColorschemePref xdgPref = static_cast(dbusVariant.variant().toUInt()); - d->colorScheme = QXdgDesktopPortalThemePrivate::colorSchemeFromXdgPref(xdgPref); + const QMap settingsMap = qvariant_cast>(reply.value()); + if (!settingsMap.isEmpty()) { + const auto xdgColorSchemePref = static_cast(settingsMap.value(appearanceInterface).value(colorSchemeKey).toUInt()); + d->colorScheme = QXdgDesktopPortalThemePrivate::colorSchemeFromXdgPref(xdgColorSchemePref); + d->contrast = static_cast(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,