Implement appearance detection generic unix themes

Detect appearance and reimplement QPlatformTheme::appearance() in
KDE and Gnome themes.

Task-number: QTBUG-106381
Change-Id: Id65ea1e47696fbfb87db5ed194300d652e0bbe66
Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
Axel Spoerl 2022-09-16 16:16:05 +02:00
parent c986c046b4
commit ae22bff97e
2 changed files with 135 additions and 11 deletions

View File

@ -1,4 +1,4 @@
// Copyright (C) 2016 The Qt Company Ltd. // Copyright (C) 2022 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 // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qgenericunixthemes_p.h" #include "qgenericunixthemes_p.h"
@ -122,6 +122,7 @@ public:
enum class SettingType { enum class SettingType {
KdeGlobalTheme, KdeGlobalTheme,
KdeApplicationStyle, KdeApplicationStyle,
GtkTheme,
Unknown Unknown
}; };
Q_ENUM(SettingType) Q_ENUM(SettingType)
@ -161,7 +162,7 @@ QGenericUnixThemeDBusListener::QGenericUnixThemeDBusListener(const QString &serv
// DBus not running // DBus not running
qCWarning(lcQpaThemeDBus) << "Session DBus not running."; qCWarning(lcQpaThemeDBus) << "Session DBus not running.";
} }
qCWarning(lcQpaThemeDBus) << "Application will not react to KDE setting changes.\n" qCWarning(lcQpaThemeDBus) << "Application will not react to setting changes.\n"
<< "Check your DBus installation."; << "Check your DBus installation.";
} }
#undef LOG #undef LOG
@ -176,6 +177,9 @@ QGenericUnixThemeDBusListener::SettingType QGenericUnixThemeDBusListener::toSett
if (location == QLatin1StringView("org.kde.kdeglobals.General") if (location == QLatin1StringView("org.kde.kdeglobals.General")
&& key == QLatin1StringView("ColorScheme")) && key == QLatin1StringView("ColorScheme"))
return SettingType::KdeGlobalTheme; return SettingType::KdeGlobalTheme;
if (location == QLatin1StringView("org.gnome.desktop.interface")
&& key == QLatin1StringView("gtk-theme"))
return SettingType::GtkTheme;
return SettingType::Unknown; return SettingType::Unknown;
} }
@ -352,6 +356,8 @@ public:
int startDragDist = 10; int startDragDist = 10;
int startDragTime = 500; int startDragTime = 500;
int cursorBlinkRate = 1000; int cursorBlinkRate = 1000;
Qt::Appearance m_appearance = Qt::Appearance::Unknown;
void updateAppearance(const QString &themeName);
#ifndef QT_NO_DBUS #ifndef QT_NO_DBUS
private: private:
@ -371,6 +377,8 @@ void QKdeThemePrivate::settingChangedHandler(QGenericUnixThemeDBusListener::Sett
case QGenericUnixThemeDBusListener::SettingType::KdeApplicationStyle: case QGenericUnixThemeDBusListener::SettingType::KdeApplicationStyle:
qCDebug(lcQpaThemeDBus) << "KDE application style changed to:" << value; qCDebug(lcQpaThemeDBus) << "KDE application style changed to:" << value;
break; break;
case QGenericUnixThemeDBusListener::SettingType::GtkTheme:
return; // KDE can change GTK2 / GTK3 themes. Ignored here, handled in GnomeTheme
case QGenericUnixThemeDBusListener::SettingType::Unknown: case QGenericUnixThemeDBusListener::SettingType::Unknown:
Q_UNREACHABLE(); Q_UNREACHABLE();
} }
@ -380,10 +388,10 @@ void QKdeThemePrivate::settingChangedHandler(QGenericUnixThemeDBusListener::Sett
bool QKdeThemePrivate::initDbus() bool QKdeThemePrivate::initDbus()
{ {
constexpr QLatin1StringView service(""); static constexpr QLatin1StringView service("");
constexpr QLatin1StringView path("/org/freedesktop/portal/desktop"); static constexpr QLatin1StringView path("/org/freedesktop/portal/desktop");
constexpr QLatin1StringView interface("org.freedesktop.portal.Settings"); static constexpr QLatin1StringView interface("org.freedesktop.portal.Settings");
constexpr QLatin1StringView signal("SettingChanged"); static constexpr QLatin1StringView signal("SettingChanged");
dbus.reset(new QGenericUnixThemeDBusListener(service, path, interface, signal)); dbus.reset(new QGenericUnixThemeDBusListener(service, path, interface, signal));
Q_ASSERT(dbus); Q_ASSERT(dbus);
@ -434,6 +442,14 @@ void QKdeThemePrivate::refresh()
styleNames.push_front(style); styleNames.push_front(style);
} }
const QVariant colorScheme = readKdeSetting(QStringLiteral("ColorScheme"), kdeDirs,
kdeVersion, kdeSettings);
if (colorScheme.isValid())
updateAppearance(colorScheme.toString());
else
m_appearance = Qt::Appearance::Unknown;
const QVariant singleClickValue = readKdeSetting(QStringLiteral("KDE/SingleClick"), kdeDirs, kdeVersion, kdeSettings); const QVariant singleClickValue = readKdeSetting(QStringLiteral("KDE/SingleClick"), kdeDirs, kdeVersion, kdeSettings);
if (singleClickValue.isValid()) if (singleClickValue.isValid())
singleClick = singleClickValue.toBool(); singleClick = singleClickValue.toBool();
@ -711,6 +727,48 @@ QIcon QKdeTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions
#endif #endif
} }
Qt::Appearance QKdeTheme::appearance() const
{
return d_func()->m_appearance;
}
/*!
\internal
\brief QKdeTheme::setAppearance - guess and set appearance for unix themes.
KDE themes do not have an appearance property.
The key words "dark" or "light" should be part of the theme name.
This is, however, not a mandatory convention.
If \param themeName contains a key word, the respective appearance is set.
If it doesn't, the appearance is heuristically determined by comparing text and base color
of the system palette.
*/
void QKdeThemePrivate::updateAppearance(const QString &themeName)
{
if (themeName.contains(QLatin1StringView("light"), Qt::CaseInsensitive)) {
m_appearance = Qt::Appearance::Light;
return;
}
if (themeName.contains(QLatin1StringView("dark"), Qt::CaseInsensitive)) {
m_appearance = Qt::Appearance::Dark;
return;
}
if (systemPalette) {
if (systemPalette->text().color().lightness() < systemPalette->base().color().lightness()) {
m_appearance = Qt::Appearance::Light;
return;
}
if (systemPalette->text().color().lightness() > systemPalette->base().color().lightness()) {
m_appearance = Qt::Appearance::Dark;
return;
}
}
m_appearance = Qt::Appearance::Unknown;
}
const QPalette *QKdeTheme::palette(Palette type) const const QPalette *QKdeTheme::palette(Palette type) const
{ {
Q_D(const QKdeTheme); Q_D(const QKdeTheme);
@ -811,8 +869,8 @@ const char *QGnomeTheme::name = "gnome";
class QGnomeThemePrivate : public QPlatformThemePrivate class QGnomeThemePrivate : public QPlatformThemePrivate
{ {
public: public:
QGnomeThemePrivate() : systemFont(nullptr), fixedFont(nullptr) {} QGnomeThemePrivate();
~QGnomeThemePrivate() { delete systemFont; delete fixedFont; } ~QGnomeThemePrivate();
void configureFonts(const QString &gtkFontName) const void configureFonts(const QString &gtkFontName) const
{ {
@ -827,10 +885,68 @@ public:
qCDebug(lcQpaFonts) << "default fonts: system" << systemFont << "fixed" << fixedFont; qCDebug(lcQpaFonts) << "default fonts: system" << systemFont << "fixed" << fixedFont;
} }
mutable QFont *systemFont; mutable QFont *systemFont = nullptr;
mutable QFont *fixedFont; mutable QFont *fixedFont = nullptr;
#ifndef QT_NO_DBUS
Qt::Appearance m_appearance = Qt::Appearance::Unknown;
private:
std::unique_ptr<QGenericUnixThemeDBusListener> dbus;
bool initDbus();
void updateAppearance(const QString &themeName);
#endif // QT_NO_DBUS
}; };
QGnomeThemePrivate::QGnomeThemePrivate()
{
#ifndef QT_NO_DBUS
initDbus();
#endif // QT_NO_DBUS
}
QGnomeThemePrivate::~QGnomeThemePrivate()
{
if (systemFont)
delete systemFont;
if (fixedFont)
delete fixedFont;
}
#ifndef QT_NO_DBUS
bool QGnomeThemePrivate::initDbus()
{
static constexpr QLatin1StringView service("");
static constexpr QLatin1StringView path("/org/freedesktop/portal/desktop");
static constexpr QLatin1StringView interface("org.freedesktop.portal.Settings");
static constexpr QLatin1StringView signal("SettingChanged");
dbus.reset(new QGenericUnixThemeDBusListener(service, path, interface, signal));
Q_ASSERT(dbus);
// Wrap slot in a lambda to avoid inheriting QGnomeThemePrivate from QObject
auto wrapper = [this](QGenericUnixThemeDBusListener::SettingType type, const QString &value) {
if (type == QGenericUnixThemeDBusListener::SettingType::GtkTheme)
updateAppearance(value);
};
return QObject::connect(dbus.get(), &QGenericUnixThemeDBusListener::settingChanged, wrapper);
}
void QGnomeThemePrivate::updateAppearance(const QString &themeName)
{
const auto oldAppearance = m_appearance;
if (themeName.contains(QLatin1StringView("light"), Qt::CaseInsensitive)) {
m_appearance = Qt::Appearance::Light;
} else if (themeName.contains(QLatin1StringView("dark"), Qt::CaseInsensitive)) {
m_appearance = Qt::Appearance::Dark;
} else {
m_appearance = Qt::Appearance::Unknown;
}
if (oldAppearance != m_appearance)
QWindowSystemInterface::handleThemeChange();
}
#endif // QT_NO_DBUS
QGnomeTheme::QGnomeTheme() QGnomeTheme::QGnomeTheme()
: QPlatformTheme(new QGnomeThemePrivate()) : QPlatformTheme(new QGnomeThemePrivate())
{ {
@ -910,6 +1026,12 @@ QPlatformMenuBar *QGnomeTheme::createPlatformMenuBar() const
return new QDBusMenuBar(); return new QDBusMenuBar();
return nullptr; return nullptr;
} }
Qt::Appearance QGnomeTheme::appearance() const
{
return d_func()->m_appearance;
}
#endif #endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON) #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)

View File

@ -77,6 +77,7 @@ public:
QPlatformTheme::IconOptions iconOptions = { }) const override; QPlatformTheme::IconOptions iconOptions = { }) const override;
const QPalette *palette(Palette type = SystemPalette) const override; const QPalette *palette(Palette type = SystemPalette) const override;
Qt::Appearance appearance() const override;
const QFont *font(Font type) const override; const QFont *font(Font type) const override;
#ifndef QT_NO_DBUS #ifndef QT_NO_DBUS
@ -106,6 +107,7 @@ public:
virtual QString gtkFontName() const; virtual QString gtkFontName() const;
#ifndef QT_NO_DBUS #ifndef QT_NO_DBUS
QPlatformMenuBar *createPlatformMenuBar() const override; QPlatformMenuBar *createPlatformMenuBar() const override;
Qt::Appearance appearance() const override;
#endif #endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON) #if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override; QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override;