Refactor qgeneericunixthemes_p.h/cpp

Header and implementation file of QGenericUnixTheme contained several
helper functions and classes. It also contained the implementation of
QGnomeTheme and QKdeTheme, both of which are not generic.

Split qgenericunixthemes_p.h/cpp up into separate files.
Group all helpers as static member functions in QGenericUnixTheme.
Inherit QGnomeTheme and QKdeTheme from QGenericUnixTheme.

Task-number: QTBUG-132929
Change-Id: Idfa6198a2b7f669edd009dc165375a9b2960fcad
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Reviewed-by: Piotr Wierciński <piotr.wiercinski@qt.io>
This commit is contained in:
Axel Spoerl 2025-02-24 21:33:14 +01:00
parent 3bbc9e29ef
commit 53fb13456f
14 changed files with 1103 additions and 938 deletions

View File

@ -1042,9 +1042,16 @@ qt_internal_extend_target(Gui CONDITION TARGET Qt::DBus AND UNIX AND (QT_FEATURE
Qt::DBus
)
qt_internal_extend_target(Gui CONDITION UNIX AND QT_FEATURE_settings AND QT_FEATURE_dbus AND (QT_FEATURE_xcb OR QT_FEATURE_wayland)
SOURCES
platform/unix/qkdetheme_p.h platform/unix/qkdetheme.cpp
platform/unix/qdbuslistener_p.h platform/unix/qdbuslistener.cpp
)
qt_internal_extend_target(Gui CONDITION UNIX AND (QT_FEATURE_xcb OR NOT MACOS) AND (QT_FEATURE_xcb OR NOT UIKIT)
SOURCES
platform/unix/qgenericunixthemes.cpp platform/unix/qgenericunixthemes_p.h
platform/unix/qgenericunixtheme.cpp platform/unix/qgenericunixtheme_p.h
platform/unix/qgnometheme_p.h platform/unix/qgnometheme.cpp
)
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)

View File

@ -0,0 +1,233 @@
// 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 "qdbuslistener_p.h"
#include <private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformservices.h>
#include <private/qdbustrayicon_p.h>
QT_BEGIN_NAMESPACE
using namespace Qt::StringLiterals;
Q_STATIC_LOGGING_CATEGORY(lcQpaThemeDBus, "qt.qpa.theme.dbus")
/*!
\internal
The QDBusListener class listens to the SettingChanged DBus signal
and translates it into combinations of the enums \c Provider and \c Setting.
Upon construction, it logs success/failure of the DBus connection.
The signal settingChanged delivers the normalized setting type and the new value as a string.
It is emitted on known setting types only.
*/
QDBusListener::QDBusListener(const QString &service,
const QString &path, const QString &interface, const QString &signal)
{
init (service, path, interface, signal);
}
QDBusListener::QDBusListener()
{
const auto service = u""_s;
const auto path = u"/org/freedesktop/portal/desktop"_s;
const auto interface = u"org.freedesktop.portal.Settings"_s;
const auto signal = u"SettingChanged"_s;
init (service, path, interface, signal);
}
namespace {
namespace JsonKeys {
constexpr auto dbusLocation() { return "DBusLocation"_L1; }
constexpr auto dbusKey() { return "DBusKey"_L1; }
constexpr auto provider() { return "Provider"_L1; }
constexpr auto setting() { return "Setting"_L1; }
constexpr auto dbusSignals() { return "DbusSignals"_L1; }
constexpr auto root() { return "Q_L1.qpa.DBusSignals"_L1; }
} // namespace JsonKeys
}
void QDBusListener::init(const QString &service, const QString &path,
const QString &interface, const QString &signal)
{
QDBusConnection dbus = QDBusConnection::sessionBus();
const bool dBusRunning = dbus.isConnected();
bool dBusSignalConnected = false;
#define LOG service << path << interface << signal;
if (dBusRunning) {
populateSignalMap();
qRegisterMetaType<QDBusVariant>();
dBusSignalConnected = dbus.connect(service, path, interface, signal, this,
SLOT(onSettingChanged(QString,QString,QDBusVariant)));
}
if (dBusSignalConnected) {
// Connection successful
qCDebug(lcQpaThemeDBus) << LOG;
} else {
if (dBusRunning) {
// DBus running, but connection failed
qCWarning(lcQpaThemeDBus) << "DBus connection failed:" << LOG;
} else {
// DBus not running
qCWarning(lcQpaThemeDBus) << "Session DBus not running.";
}
qCWarning(lcQpaThemeDBus) << "Application will not react to setting changes.\n"
<< "Check your DBus installation.";
}
#undef LOG
}
void QDBusListener::loadJson(const QString &fileName)
{
Q_ASSERT(!fileName.isEmpty());
#define CHECK(cond, warning)\
if (!cond) {\
qCWarning(lcQpaThemeDBus) << fileName << warning << "Falling back to default.";\
return;\
}
#define PARSE(var, enumeration, string)\
enumeration var;\
{\
bool success;\
const int val = QMetaEnum::fromType<enumeration>().keyToValue(string.toLatin1(), &success);\
CHECK(success, "Parse Error: Invalid value" << string << "for" << #var);\
var = static_cast<enumeration>(val);\
}
QFile file(fileName);
CHECK(file.exists(), fileName << "doesn't exist.");
CHECK(file.open(QIODevice::ReadOnly), "could not be opened for reading.");
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
CHECK((error.error == QJsonParseError::NoError), error.errorString());
CHECK(doc.isObject(), "Parse Error: Expected root object" << JsonKeys::root());
const QJsonObject &root = doc.object();
CHECK(root.contains(JsonKeys::root()), "Parse Error: Expectned root object" << JsonKeys::root());
CHECK(root[JsonKeys::root()][JsonKeys::dbusSignals()].isArray(), "Parse Error: Expected array" << JsonKeys::dbusSignals());
const QJsonArray &sigs = root[JsonKeys::root()][JsonKeys::dbusSignals()].toArray();
CHECK((sigs.count() > 0), "Parse Error: Found empty array" << JsonKeys::dbusSignals());
for (auto sig = sigs.constBegin(); sig != sigs.constEnd(); ++sig) {
CHECK(sig->isObject(), "Parse Error: Expected object array" << JsonKeys::dbusSignals());
const QJsonObject &obj = sig->toObject();
CHECK(obj.contains(JsonKeys::dbusLocation()), "Parse Error: Expected key" << JsonKeys::dbusLocation());
CHECK(obj.contains(JsonKeys::dbusKey()), "Parse Error: Expected key" << JsonKeys::dbusKey());
CHECK(obj.contains(JsonKeys::provider()), "Parse Error: Expected key" << JsonKeys::provider());
CHECK(obj.contains(JsonKeys::setting()), "Parse Error: Expected key" << JsonKeys::setting());
const QString &location = obj[JsonKeys::dbusLocation()].toString();
const QString &key = obj[JsonKeys::dbusKey()].toString();
const QString &providerString = obj[JsonKeys::provider()].toString();
const QString &settingString = obj[JsonKeys::setting()].toString();
PARSE(provider, Provider, providerString);
PARSE(setting, Setting, settingString);
const DBusKey dkey(location, key);
CHECK (!m_signalMap.contains(dkey), "Duplicate key" << location << key);
m_signalMap.insert(dkey, ChangeSignal(provider, setting));
}
#undef PARSE
#undef CHECK
if (m_signalMap.count() > 0)
qCInfo(lcQpaThemeDBus) << "Successfully imported" << fileName;
else
qCWarning(lcQpaThemeDBus) << "No data imported from" << fileName << "falling back to default.";
#ifdef QT_DEBUG
const int count = m_signalMap.count();
if (count == 0)
return;
qCDebug(lcQpaThemeDBus) << "Listening to" << count << "signals:";
for (auto it = m_signalMap.constBegin(); it != m_signalMap.constEnd(); ++it) {
qDebug() << it.key().key << it.key().location << "mapped to"
<< it.value().provider << it.value().setting;
}
#endif
}
void QDBusListener::saveJson(const QString &fileName) const
{
Q_ASSERT(!m_signalMap.isEmpty());
Q_ASSERT(!fileName.isEmpty());
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly)) {
qCWarning(lcQpaThemeDBus) << fileName << "could not be opened for writing.";
return;
}
QJsonArray sigs;
for (auto sig = m_signalMap.constBegin(); sig != m_signalMap.constEnd(); ++sig) {
const DBusKey &dkey = sig.key();
const ChangeSignal &csig = sig.value();
QJsonObject obj;
obj[JsonKeys::dbusLocation()] = dkey.location;
obj[JsonKeys::dbusKey()] = dkey.key;
obj[JsonKeys::provider()] = QLatin1StringView(QMetaEnum::fromType<Provider>()
.valueToKey(static_cast<int>(csig.provider)));
obj[JsonKeys::setting()] = QLatin1StringView(QMetaEnum::fromType<Setting>()
.valueToKey(static_cast<int>(csig.setting)));
sigs.append(obj);
}
QJsonObject obj;
obj[JsonKeys::dbusSignals()] = sigs;
QJsonObject root;
root[JsonKeys::root()] = obj;
QJsonDocument doc(root);
file.write(doc.toJson());
file.close();
}
void QDBusListener::populateSignalMap()
{
m_signalMap.clear();
const QString &loadJsonFile = qEnvironmentVariable("QT_QPA_DBUS_SIGNALS");
if (!loadJsonFile.isEmpty())
loadJson(loadJsonFile);
if (!m_signalMap.isEmpty())
return;
m_signalMap.insert(DBusKey("org.kde.kdeglobals.KDE"_L1, "widgetStyle"_L1),
ChangeSignal(Provider::Kde, Setting::ApplicationStyle));
m_signalMap.insert(DBusKey("org.kde.kdeglobals.General"_L1, "ColorScheme"_L1),
ChangeSignal(Provider::Kde, Setting::Theme));
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),
ChangeSignal(Provider::Gnome, Setting::ColorScheme));
const QString &saveJsonFile = qEnvironmentVariable("QT_QPA_DBUS_SIGNALS_SAVE");
if (!saveJsonFile.isEmpty())
saveJson(saveJsonFile);
}
std::optional<QDBusListener::ChangeSignal>
QDBusListener::findSignal(const QString &location, const QString &key) const
{
const DBusKey dkey(location, key);
std::optional<QDBusListener::ChangeSignal> ret;
if (m_signalMap.contains(dkey))
ret.emplace(m_signalMap.value(dkey));
return ret;
}
void QDBusListener::onSettingChanged(const QString &location, const QString &key, const QDBusVariant &value)
{
auto sig = findSignal(location, key);
if (!sig.has_value())
return;
emit settingChanged(sig.value().provider, sig.value().setting, value.variant().toString());
}
QT_END_NAMESPACE

View File

@ -0,0 +1,88 @@
// 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 QDBUSLISTENER_P_H
#define QDBUSLISTENER_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 <qpa/qplatformtheme.h>
#include <private/qflatmap_p.h>
#include <QDBusVariant>
QT_BEGIN_NAMESPACE
class QDBusListener : public QObject
{
Q_OBJECT
public:
enum class Provider {
Kde,
Gtk,
Gnome,
};
Q_ENUM(Provider)
enum class Setting {
Theme,
ApplicationStyle,
ColorScheme,
};
Q_ENUM(Setting)
QDBusListener();
QDBusListener(const QString &service, const QString &path,
const QString &interface, const QString &signal);
private Q_SLOTS:
void onSettingChanged(const QString &location, const QString &key, const QDBusVariant &value);
Q_SIGNALS:
void settingChanged(QDBusListener::Provider provider,
QDBusListener::Setting setting,
const QString &value);
private:
struct DBusKey
{
QString location;
QString key;
DBusKey(const QString &loc, const QString &k) : location(loc), key(k) {};
bool operator<(const DBusKey &other) const
{
return location + key < other.location + other.key;
}
};
struct ChangeSignal
{
Provider provider;
Setting setting;
ChangeSignal(Provider p, Setting s) : provider(p), setting(s) {}
ChangeSignal() {}
};
QFlatMap <DBusKey, ChangeSignal> m_signalMap;
void init(const QString &service, const QString &path,
const QString &interface, const QString &signal);
std::optional<ChangeSignal> findSignal(const QString &location, const QString &key) const;
void populateSignalMap();
void loadJson(const QString &fileName);
void saveJson(const QString &fileName) const;
};
QT_END_NAMESPACE
#endif // QDBUSLISTENER_P_H

View File

@ -0,0 +1,297 @@
// 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
#include "qgenericunixtheme_p.h"
#include "qgnometheme_p.h"
#include <QPalette>
#include <QFont>
#include <QGuiApplication>
#include <QDir>
#include <QFileInfo>
#include <QFile>
#include <QDebug>
#include <QHash>
#include <QLoggingCategory>
#include <QVariant>
#include <QStandardPaths>
#include <QStringList>
#if QT_CONFIG(mimetype)
#include <QMimeDatabase>
#endif
#if QT_CONFIG(settings)
#include <QSettings>
#ifndef QT_NO_DBUS
#include "qkdetheme_p.h"
#endif
#endif
#include <qpa/qplatformfontdatabase.h> // lcQpaFonts
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformservices.h>
#include <qpa/qplatformdialoghelper.h>
#include <qpa/qplatformtheme_p.h>
#ifndef QT_NO_DBUS
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonParseError>
#ifndef QT_NO_SYSTEMTRAYICON
#include <private/qdbustrayicon_p.h>
#include <private/qdbusmenubar_p.h>
#endif
#endif
#include <private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
#include <QtCore/QStandardPaths>
#ifndef QT_NO_DBUS
#include <private/qdbustrayicon_p.h>
#endif
#if QT_CONFIG(mimetype)
#include <QtCore/QMimeDatabase>
#include <QtCore/QMimeData>
#endif
QT_BEGIN_NAMESPACE
Q_DECLARE_LOGGING_CATEGORY(qLcTray)
using namespace Qt::StringLiterals;
const char *QGenericUnixTheme::name = "generic";
QGenericUnixThemePrivate::QGenericUnixThemePrivate()
: QPlatformThemePrivate()
, systemFont(QLatin1StringView(QGenericUnixTheme::defaultSystemFontNameC),
QGenericUnixTheme::defaultSystemFontSize)
, fixedFont(QLatin1StringView(QGenericUnixTheme::defaultFixedFontNameC),
systemFont.pointSize())
{
fixedFont.setStyleHint(QFont::TypeWriter);
qCDebug(lcQpaFonts) << "default fonts: system" << systemFont << "fixed" << fixedFont;
}
QGenericUnixTheme::QGenericUnixTheme(QGenericUnixThemePrivate *p)
: QPlatformTheme(p)
{}
QGenericUnixTheme::QGenericUnixTheme()
: QPlatformTheme(new QGenericUnixThemePrivate())
{}
const QFont *QGenericUnixTheme::font(Font type) const
{
Q_D(const QGenericUnixTheme);
switch (type) {
case QPlatformTheme::SystemFont:
return &d->systemFont;
case QPlatformTheme::FixedFont:
return &d->fixedFont;
default:
return nullptr;
}
}
#ifndef QT_NO_DBUS
QPlatformMenuBar *QGenericUnixTheme::createPlatformMenuBar() const
{
if (isDBusGlobalMenuAvailable())
return new QDBusMenuBar();
return nullptr;
}
#endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
QPlatformSystemTrayIcon *QGenericUnixTheme::createPlatformSystemTrayIcon() const
{
if (shouldUseDBusTray())
return new QDBusTrayIcon();
return nullptr;
}
#endif
QVariant QGenericUnixTheme::themeHint(ThemeHint hint) const
{
switch (hint) {
case QPlatformTheme::SystemIconFallbackThemeName:
return QVariant(QString(QStringLiteral("hicolor")));
case QPlatformTheme::IconThemeSearchPaths:
return xdgIconThemePaths();
case QPlatformTheme::IconFallbackSearchPaths:
return iconFallbackPaths();
case QPlatformTheme::DialogButtonBoxButtonsHaveIcons:
return QVariant(true);
case QPlatformTheme::StyleNames: {
QStringList styleNames;
styleNames << QStringLiteral("Fusion") << QStringLiteral("Windows");
return QVariant(styleNames);
}
case QPlatformTheme::KeyboardScheme:
return QVariant(int(X11KeyboardScheme));
case QPlatformTheme::UiEffects:
return QVariant(int(HoverEffect));
case QPlatformTheme::MouseCursorTheme:
return QVariant(mouseCursorTheme());
case QPlatformTheme::MouseCursorSize:
return QVariant(mouseCursorSize());
case QPlatformTheme::PreferFileIconFromTheme:
return true;
default:
break;
}
return QPlatformTheme::themeHint(hint);
}
QStringList QGenericUnixTheme::themeNames()
{
QStringList result;
if (QGuiApplication::desktopSettingsAware()) {
const QByteArray desktopEnvironment = QGuiApplicationPrivate::platformIntegration()->services()->desktopEnvironment();
QList<QByteArray> gtkBasedEnvironments;
gtkBasedEnvironments << "GNOME"
<< "X-CINNAMON"
<< "PANTHEON"
<< "UNITY"
<< "MATE"
<< "XFCE"
<< "LXDE";
const QList<QByteArray> desktopNames = desktopEnvironment.split(':');
for (const QByteArray &desktopName : desktopNames) {
if (desktopEnvironment == "KDE") {
#if !defined QT_NO_DBUS && QT_CONFIG(settings)
result.push_back(QLatin1StringView(QKdeTheme::name));
#endif
} else if (gtkBasedEnvironments.contains(desktopName)) {
// prefer the GTK3 theme implementation with native dialogs etc.
result.push_back(QStringLiteral("gtk3"));
// fallback to the generic Gnome theme if loading the GTK3 theme fails
result.push_back(QLatin1StringView(QGnomeTheme::name));
} else {
// unknown, but lowercase the name (our standard practice) and
// remove any "x-" prefix
QString s = QString::fromLatin1(desktopName.toLower());
result.push_back(s.startsWith("x-"_L1) ? s.mid(2) : s);
}
}
} // desktopSettingsAware
result.append(QLatin1StringView(QGenericUnixTheme::name));
return result;
}
/*!
\internal
\brief Creates a UNIX theme according to the given theme \a name
*/
QPlatformTheme *QGenericUnixTheme::createUnixTheme(const QString &name)
{
if (name == QLatin1StringView(QGenericUnixTheme::name))
return new QGenericUnixTheme;
#if !defined QT_NO_DBUS && QT_CONFIG(settings)
if (name == QLatin1StringView(QKdeTheme::name))
return QKdeTheme::createKdeTheme();
#endif
if (name == QLatin1StringView(QGnomeTheme::name))
return new QGnomeTheme;
return nullptr;
}
// Helper to return the icon theme paths from XDG.
QStringList QGenericUnixTheme::xdgIconThemePaths()
{
QStringList paths;
// Add home directory first in search path
const QFileInfo homeIconDir(QDir::homePath() + "/.icons"_L1);
if (homeIconDir.isDir())
paths.prepend(homeIconDir.absoluteFilePath());
paths.append(QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
QStringLiteral("icons"),
QStandardPaths::LocateDirectory));
return paths;
}
QStringList QGenericUnixTheme::iconFallbackPaths()
{
QStringList paths;
const QFileInfo pixmapsIconsDir(QStringLiteral("/usr/share/pixmaps"));
if (pixmapsIconsDir.isDir())
paths.append(pixmapsIconsDir.absoluteFilePath());
return paths;
}
QString QGenericUnixTheme::mouseCursorTheme()
{
static QString themeName = qEnvironmentVariable("XCURSOR_THEME");
return themeName;
}
QSize QGenericUnixTheme::mouseCursorSize()
{
constexpr int defaultCursorSize = 24;
static const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE");
static const int s = xCursorSize > 0 ? xCursorSize : defaultCursorSize;
return QSize(s, s);
}
#ifndef QT_NO_DBUS
static bool checkDBusGlobalMenuAvailable()
{
const QDBusConnection connection = QDBusConnection::sessionBus();
static const QString registrarService = QStringLiteral("com.canonical.AppMenu.Registrar");
if (const auto iface = connection.interface())
return iface->isServiceRegistered(registrarService);
return false;
}
bool QGenericUnixTheme::isDBusGlobalMenuAvailable()
{
static bool dbusGlobalMenuAvailable = checkDBusGlobalMenuAvailable();
return dbusGlobalMenuAvailable;
}
#endif
#if QT_CONFIG(mimetype)
QIcon QGenericUnixTheme::xdgFileIcon(const QFileInfo &fileInfo)
{
QMimeDatabase mimeDatabase;
QMimeType mimeType = mimeDatabase.mimeTypeForFile(fileInfo);
if (!mimeType.isValid())
return QIcon();
const QString &iconName = mimeType.iconName();
if (!iconName.isEmpty()) {
const QIcon icon = QIcon::fromTheme(iconName);
if (!icon.isNull())
return icon;
}
const QString &genericIconName = mimeType.genericIconName();
return genericIconName.isEmpty() ? QIcon() : QIcon::fromTheme(genericIconName);
}
#endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
bool QGenericUnixTheme::shouldUseDBusTray()
{
// There's no other tray implementation to fallback to on non-X11
// and QDBusTrayIcon can register the icon on the fly after creation
if (QGuiApplication::platformName() != "xcb"_L1)
return true;
const bool result = QDBusMenuConnection().isWatcherRegistered();
qCDebug(qLcTray) << "D-Bus tray available:" << result;
return result;
}
#endif
// Helper functions for implementing QPlatformTheme::fileIcon() for XDG icon themes.
QList<QSize> QGenericUnixTheme::availableXdgFileIconSizes()
{
return QIcon::fromTheme(QStringLiteral("inode-directory")).availableSizes();
}
QT_END_NAMESPACE

View File

@ -0,0 +1,85 @@
// Copyright (C) 2020 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 QGENERICUNIXTHEMES_H
#define QGENERICUNIXTHEMES_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 <qpa/qplatformtheme.h>
#include <qpa/qplatformtheme_p.h>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtGui/QFont>
#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
class QGenericUnixTheme;
class QGenericUnixThemePrivate : public QPlatformThemePrivate
{
public:
QGenericUnixThemePrivate();
const QFont systemFont;
QFont fixedFont;
};
class Q_GUI_EXPORT QGenericUnixTheme : public QPlatformTheme
{
Q_DECLARE_PRIVATE(QGenericUnixTheme)
protected:
QGenericUnixTheme(QGenericUnixThemePrivate *p);
public:
QGenericUnixTheme();
const QFont *font(Font type) const override;
QVariant themeHint(ThemeHint hint) const override;
#ifndef QT_NO_DBUS
QPlatformMenuBar *createPlatformMenuBar() const override;
#endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override;
#endif
static const char *name;
// Default system font, corresponding to the value returned by 4.8 for
// XRender/FontConfig which we can now assume as default.
static constexpr char defaultSystemFontNameC[] = "Sans Serif";
static constexpr char defaultFixedFontNameC[] = "monospace";
enum { defaultSystemFontSize = 9 };
// Helpers
static QStringList xdgIconThemePaths();
static QPlatformTheme *createUnixTheme(const QString &name);
static QStringList themeNames();
protected:
static QStringList iconFallbackPaths();
static bool isDBusGlobalMenuAvailable();
static QString mouseCursorTheme();
static QSize mouseCursorSize();
static QList<QSize> availableXdgFileIconSizes();
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
static bool shouldUseDBusTray();
#endif
#if QT_CONFIG(mimetype)
static QIcon xdgFileIcon(const QFileInfo &fileInfo);
#endif
};
QT_END_NAMESPACE
#endif // QGENERICUNIXTHEMES_H

View File

@ -1,123 +0,0 @@
// Copyright (C) 2020 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 QGENERICUNIXTHEMES_H
#define QGENERICUNIXTHEMES_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 <qpa/qplatformtheme.h>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtGui/QFont>
#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
class ResourceHelper
{
public:
ResourceHelper();
~ResourceHelper() { clear(); }
void clear();
QPalette *palettes[QPlatformTheme::NPalettes];
QFont *fonts[QPlatformTheme::NFonts];
};
class QGenericUnixThemePrivate;
class Q_GUI_EXPORT QGenericUnixTheme : public QPlatformTheme
{
Q_DECLARE_PRIVATE(QGenericUnixTheme)
public:
QGenericUnixTheme();
static QPlatformTheme *createUnixTheme(const QString &name);
static QStringList themeNames();
const QFont *font(Font type) const override;
QVariant themeHint(ThemeHint hint) const override;
static QStringList xdgIconThemePaths();
static QStringList iconFallbackPaths();
#ifndef QT_NO_DBUS
QPlatformMenuBar *createPlatformMenuBar() const override;
#endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override;
#endif
static const char *name;
};
#if QT_CONFIG(settings)
class QKdeThemePrivate;
class QKdeTheme : public QPlatformTheme
{
Q_DECLARE_PRIVATE(QKdeTheme)
public:
QKdeTheme(const QStringList& kdeDirs, int kdeVersion);
static QPlatformTheme *createKdeTheme();
QVariant themeHint(ThemeHint hint) const override;
QIcon fileIcon(const QFileInfo &fileInfo,
QPlatformTheme::IconOptions iconOptions = { }) const override;
const QPalette *palette(Palette type = SystemPalette) const override;
Qt::ColorScheme colorScheme() const override;
const QFont *font(Font type) const override;
#ifndef QT_NO_DBUS
QPlatformMenuBar *createPlatformMenuBar() const override;
#endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override;
#endif
static const char *name;
};
#endif // settings
class QGnomeThemePrivate;
class Q_GUI_EXPORT QGnomeTheme : public QPlatformTheme
{
Q_DECLARE_PRIVATE(QGnomeTheme)
public:
QGnomeTheme();
QVariant themeHint(ThemeHint hint) const override;
QIcon fileIcon(const QFileInfo &fileInfo,
QPlatformTheme::IconOptions = { }) const override;
const QFont *font(Font type) const override;
QString standardButtonText(int button) const override;
virtual QString gtkFontName() const;
#ifndef QT_NO_DBUS
QPlatformMenuBar *createPlatformMenuBar() const override;
Qt::ColorScheme colorScheme() const override;
#endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override;
#endif
static const char *name;
};
QPlatformTheme *qt_createUnixTheme();
QT_END_NAMESPACE
#endif // QGENERICUNIXTHEMES_H

View File

@ -0,0 +1,214 @@
// 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 "qgnometheme_p.h"
#include <qpa/qplatformdialoghelper.h>
#include <qpa/qplatformfontdatabase.h>
#ifndef QT_NO_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>
#endif
#include <qpa/qwindowsysteminterface.h>
QT_BEGIN_NAMESPACE
/*!
\class QGnomeTheme
\brief QGnomeTheme is a theme implementation for the Gnome desktop.
\since 5.0
\internal
\ingroup qpa
*/
const char *QGnomeTheme::name = "gnome";
QGnomeThemePrivate::QGnomeThemePrivate()
{
#ifndef QT_NO_DBUS
initDbus();
#endif // QT_NO_DBUS
}
QGnomeThemePrivate::~QGnomeThemePrivate()
{
if (systemFont)
delete systemFont;
if (fixedFont)
delete fixedFont;
}
void QGnomeThemePrivate::configureFonts(const QString &gtkFontName) const
{
Q_ASSERT(!systemFont);
const int split = gtkFontName.lastIndexOf(QChar::Space);
float size = QStringView{gtkFontName}.mid(split + 1).toFloat();
QString fontName = gtkFontName.left(split);
systemFont = new QFont(fontName, size);
fixedFont = new QFont(QLatin1StringView(QGenericUnixTheme::defaultFixedFontNameC), systemFont->pointSize());
fixedFont->setStyleHint(QFont::TypeWriter);
qCDebug(lcQpaFonts) << "default fonts: system" << systemFont << "fixed" << fixedFont;
}
#ifndef QT_NO_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 QString &value) {
if (provider != QDBusListener::Provider::Gnome
&& provider != QDBusListener::Provider::Gtk) {
return;
}
if (setting == QDBusListener::Setting::Theme)
updateColorScheme(value);
};
return QObject::connect(dbus.get(), &QDBusListener::settingChanged, dbus.get(), wrapper);
}
void QGnomeThemePrivate::updateColorScheme(const QString &themeName)
{
const auto oldColorScheme = m_colorScheme;
if (themeName.contains(QLatin1StringView("light"), Qt::CaseInsensitive)) {
m_colorScheme = Qt::ColorScheme::Light;
} else if (themeName.contains(QLatin1StringView("dark"), Qt::CaseInsensitive)) {
m_colorScheme = Qt::ColorScheme::Dark;
} else {
m_colorScheme = Qt::ColorScheme::Unknown;
}
if (oldColorScheme != m_colorScheme)
QWindowSystemInterface::handleThemeChange();
}
#endif // QT_NO_DBUS
QGnomeTheme::QGnomeTheme()
: QGenericUnixTheme(new QGnomeThemePrivate())
{
}
QVariant QGnomeTheme::themeHint(QPlatformTheme::ThemeHint hint) const
{
switch (hint) {
case QPlatformTheme::DialogButtonBoxButtonsHaveIcons:
return QVariant(true);
case QPlatformTheme::DialogButtonBoxLayout:
return QVariant(QPlatformDialogHelper::GnomeLayout);
case QPlatformTheme::SystemIconThemeName:
return QVariant(QStringLiteral("Adwaita"));
case QPlatformTheme::SystemIconFallbackThemeName:
return QVariant(QStringLiteral("gnome"));
case QPlatformTheme::IconThemeSearchPaths:
return QVariant(xdgIconThemePaths());
case QPlatformTheme::IconPixmapSizes:
return QVariant::fromValue(availableXdgFileIconSizes());
case QPlatformTheme::StyleNames: {
QStringList styleNames;
styleNames << QStringLiteral("Fusion") << QStringLiteral("windows");
return QVariant(styleNames);
}
case QPlatformTheme::KeyboardScheme:
return QVariant(int(GnomeKeyboardScheme));
case QPlatformTheme::PasswordMaskCharacter:
return QVariant(QChar(0x2022));
case QPlatformTheme::UiEffects:
return QVariant(int(HoverEffect));
case QPlatformTheme::ButtonPressKeys:
return QVariant::fromValue(
QList<Qt::Key>({ Qt::Key_Space, Qt::Key_Return, Qt::Key_Enter, Qt::Key_Select }));
case QPlatformTheme::PreselectFirstFileInDirectory:
return true;
case QPlatformTheme::MouseCursorTheme:
return QVariant(mouseCursorTheme());
case QPlatformTheme::MouseCursorSize:
return QVariant(mouseCursorSize());
case QPlatformTheme::PreferFileIconFromTheme:
return true;
default:
break;
}
return QPlatformTheme::themeHint(hint);
}
QIcon QGnomeTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions) const
{
#if QT_CONFIG(mimetype)
return xdgFileIcon(fileInfo);
#else
Q_UNUSED(fileInfo);
return QIcon();
#endif
}
const QFont *QGnomeTheme::font(Font type) const
{
Q_D(const QGnomeTheme);
if (!d->systemFont)
d->configureFonts(gtkFontName());
switch (type) {
case QPlatformTheme::SystemFont:
return d->systemFont;
case QPlatformTheme::FixedFont:
return d->fixedFont;
default:
return nullptr;
}
}
QString QGnomeTheme::gtkFontName() const
{
return QStringLiteral("%1 %2").arg(QLatin1StringView(defaultSystemFontNameC))
.arg(defaultSystemFontSize);
}
#ifndef QT_NO_DBUS
QPlatformMenuBar *QGnomeTheme::createPlatformMenuBar() const
{
if (isDBusGlobalMenuAvailable())
return new QDBusMenuBar();
return nullptr;
}
Qt::ColorScheme QGnomeTheme::colorScheme() const
{
return d_func()->m_colorScheme;
}
#endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
QPlatformSystemTrayIcon *QGnomeTheme::createPlatformSystemTrayIcon() const
{
if (shouldUseDBusTray())
return new QDBusTrayIcon();
return nullptr;
}
#endif
QString QGnomeTheme::standardButtonText(int button) const
{
switch (button) {
case QPlatformDialogHelper::Ok:
return QCoreApplication::translate("QGnomeTheme", "&OK");
case QPlatformDialogHelper::Save:
return QCoreApplication::translate("QGnomeTheme", "&Save");
case QPlatformDialogHelper::Cancel:
return QCoreApplication::translate("QGnomeTheme", "&Cancel");
case QPlatformDialogHelper::Close:
return QCoreApplication::translate("QGnomeTheme", "&Close");
case QPlatformDialogHelper::Discard:
return QCoreApplication::translate("QGnomeTheme", "Close without Saving");
default:
break;
}
return QPlatformTheme::standardButtonText(button);
}
QT_END_NAMESPACE

View File

@ -0,0 +1,74 @@
// 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 QGNOMETHEME_P_H
#define QGNOMETHEME_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 "qgenericunixtheme_p.h"
#include <qpa/qplatformtheme.h>
#include <qpa/qplatformtheme_p.h>
#include <QtGui/QFont>
QT_BEGIN_NAMESPACE
class QGnomeThemePrivate;
#ifndef QT_NO_DBUS
class QDBusListener;
#endif
class Q_GUI_EXPORT QGnomeTheme : public QGenericUnixTheme
{
Q_DECLARE_PRIVATE(QGnomeTheme)
public:
QGnomeTheme();
QVariant themeHint(ThemeHint hint) const override;
QIcon fileIcon(const QFileInfo &fileInfo,
QPlatformTheme::IconOptions = { }) const override;
const QFont *font(Font type) const override;
QString standardButtonText(int button) const override;
virtual QString gtkFontName() const;
#ifndef QT_NO_DBUS
QPlatformMenuBar *createPlatformMenuBar() const override;
Qt::ColorScheme colorScheme() const override;
#endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override;
#endif
static const char *name;
};
class QGnomeThemePrivate : public QGenericUnixThemePrivate
{
public:
QGnomeThemePrivate();
~QGnomeThemePrivate();
void configureFonts(const QString &gtkFontName) const;
mutable QFont *systemFont = nullptr;
mutable QFont *fixedFont = nullptr;
#ifndef QT_NO_DBUS
Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown;
private:
std::unique_ptr<QDBusListener> dbus;
bool initDbus();
void updateColorScheme(const QString &themeName);
#endif // QT_NO_DBUS
};
QT_END_NAMESPACE
#endif // QGNOMETHEME_P_H

View File

@ -1,59 +1,22 @@
// Copyright (C) 2022 The Qt Company Ltd.
// 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 "qgenericunixthemes_p.h"
#include <QPalette>
#include <QFont>
#include <QGuiApplication>
#include <QDir>
#include <QFileInfo>
#include <QFile>
#include <QDebug>
#include <QHash>
#include <QLoggingCategory>
#include <QVariant>
#include <QStandardPaths>
#include <QStringList>
#if QT_CONFIG(mimetype)
#include <QMimeDatabase>
#endif
#if QT_CONFIG(settings)
#include <QSettings>
#endif
#include <qpa/qplatformfontdatabase.h> // lcQpaFonts
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformservices.h>
#include <qpa/qplatformdialoghelper.h>
#include "qkdetheme_p.h"
#include <qpa/qplatformtheme_p.h>
#include <private/qguiapplication_p.h>
#ifndef QT_NO_DBUS
#include <QDBusConnectionInterface>
#include <qpa/qplatformfontdatabase.h>
#include <qpa/qplatformdialoghelper.h>
#include <QPalette>
#include <qpa/qwindowsysteminterface.h>
#include "qdbuslistener_p.h"
#include <private/qdbustrayicon_p.h>
#include <private/qdbusplatformmenu_p.h>
#include <private/qdbusmenubar_p.h>
#include <private/qflatmap_p.h>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QJsonValue>
#include <QJsonParseError>
#endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
#include <private/qdbustrayicon_p.h>
#endif
#include <algorithm>
QT_BEGIN_NAMESPACE
#ifndef QT_NO_DBUS
Q_STATIC_LOGGING_CATEGORY(lcQpaThemeDBus, "qt.qpa.theme.dbus")
#endif
using namespace Qt::StringLiterals;
Q_DECLARE_LOGGING_CATEGORY(qLcTray)
Q_STATIC_LOGGING_CATEGORY(lcQpaThemeKde, "qt.qpa.theme.kde")
ResourceHelper::ResourceHelper()
{
@ -69,477 +32,7 @@ void ResourceHelper::clear()
std::fill(fonts, fonts + QPlatformTheme::NFonts, static_cast<QFont *>(nullptr));
}
const char *QGenericUnixTheme::name = "generic";
// Default system font, corresponding to the value returned by 4.8 for
// XRender/FontConfig which we can now assume as default.
static const char defaultSystemFontNameC[] = "Sans Serif";
static const char defaultFixedFontNameC[] = "monospace";
enum { defaultSystemFontSize = 9 };
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
static bool shouldUseDBusTray() {
// There's no other tray implementation to fallback to on non-X11
// and QDBusTrayIcon can register the icon on the fly after creation
if (QGuiApplication::platformName() != "xcb"_L1)
return true;
const bool result = QDBusMenuConnection().isWatcherRegistered();
qCDebug(qLcTray) << "D-Bus tray available:" << result;
return result;
}
#endif
static QString mouseCursorTheme()
{
static QString themeName = qEnvironmentVariable("XCURSOR_THEME");
return themeName;
}
static QSize mouseCursorSize()
{
constexpr int defaultCursorSize = 24;
static const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE");
static const int s = xCursorSize > 0 ? xCursorSize : defaultCursorSize;
return QSize(s, s);
}
#ifndef QT_NO_DBUS
static bool checkDBusGlobalMenuAvailable()
{
const QDBusConnection connection = QDBusConnection::sessionBus();
static const QString registrarService = QStringLiteral("com.canonical.AppMenu.Registrar");
if (const auto iface = connection.interface())
return iface->isServiceRegistered(registrarService);
return false;
}
static bool isDBusGlobalMenuAvailable()
{
static bool dbusGlobalMenuAvailable = checkDBusGlobalMenuAvailable();
return dbusGlobalMenuAvailable;
}
/*!
* \internal
* The QGenericUnixThemeDBusListener class listens to the SettingChanged DBus signal
* and translates it into combinations of the enums \c Provider and \c Setting.
* Upon construction, it logs success/failure of the DBus connection.
*
* The signal settingChanged delivers the normalized setting type and the new value as a string.
* It is emitted on known setting types only.
*/
class QGenericUnixThemeDBusListener : public QObject
{
Q_OBJECT
public:
enum class Provider {
Kde,
Gtk,
Gnome,
};
Q_ENUM(Provider)
enum class Setting {
Theme,
ApplicationStyle,
ColorScheme,
};
Q_ENUM(Setting)
QGenericUnixThemeDBusListener();
QGenericUnixThemeDBusListener(const QString &service, const QString &path,
const QString &interface, const QString &signal);
private Q_SLOTS:
void onSettingChanged(const QString &location, const QString &key, const QDBusVariant &value);
Q_SIGNALS:
void settingChanged(QGenericUnixThemeDBusListener::Provider provider,
QGenericUnixThemeDBusListener::Setting setting,
const QString &value);
private:
struct DBusKey
{
QString location;
QString key;
DBusKey(const QString &loc, const QString &k) : location(loc), key(k) {};
bool operator<(const DBusKey &other) const
{
return location + key < other.location + other.key;
}
};
struct ChangeSignal
{
Provider provider;
Setting setting;
ChangeSignal(Provider p, Setting s) : provider(p), setting(s) {}
ChangeSignal() {}
};
QFlatMap <DBusKey, ChangeSignal> m_signalMap;
void init(const QString &service, const QString &path,
const QString &interface, const QString &signal);
std::optional<ChangeSignal> findSignal(const QString &location, const QString &key) const;
void populateSignalMap();
void loadJson(const QString &fileName);
void saveJson(const QString &fileName) const;
};
namespace {
namespace JsonKeys {
constexpr auto dbusLocation() { return "DBusLocation"_L1; }
constexpr auto dbusKey() { return "DBusKey"_L1; }
constexpr auto provider() { return "Provider"_L1; }
constexpr auto setting() { return "Setting"_L1; }
constexpr auto dbusSignals() { return "DbusSignals"_L1; }
constexpr auto root() { return "Qt.qpa.DBusSignals"_L1; }
} // namespace JsonKeys
}
QGenericUnixThemeDBusListener::QGenericUnixThemeDBusListener(const QString &service,
const QString &path, const QString &interface, const QString &signal)
{
init (service, path, interface, signal);
}
QGenericUnixThemeDBusListener::QGenericUnixThemeDBusListener()
{
const auto service = u""_s;
const auto path = u"/org/freedesktop/portal/desktop"_s;
const auto interface = u"org.freedesktop.portal.Settings"_s;
const auto signal = u"SettingChanged"_s;
init (service, path, interface, signal);
}
void QGenericUnixThemeDBusListener::init(const QString &service, const QString &path,
const QString &interface, const QString &signal)
{
QDBusConnection dbus = QDBusConnection::sessionBus();
const bool dBusRunning = dbus.isConnected();
bool dBusSignalConnected = false;
#define LOG service << path << interface << signal;
if (dBusRunning) {
populateSignalMap();
qRegisterMetaType<QDBusVariant>();
dBusSignalConnected = dbus.connect(service, path, interface, signal, this,
SLOT(onSettingChanged(QString,QString,QDBusVariant)));
}
if (dBusSignalConnected) {
// Connection successful
qCDebug(lcQpaThemeDBus) << LOG;
} else {
if (dBusRunning) {
// DBus running, but connection failed
qCWarning(lcQpaThemeDBus) << "DBus connection failed:" << LOG;
} else {
// DBus not running
qCWarning(lcQpaThemeDBus) << "Session DBus not running.";
}
qCWarning(lcQpaThemeDBus) << "Application will not react to setting changes.\n"
<< "Check your DBus installation.";
}
#undef LOG
}
void QGenericUnixThemeDBusListener::loadJson(const QString &fileName)
{
Q_ASSERT(!fileName.isEmpty());
#define CHECK(cond, warning)\
if (!cond) {\
qCWarning(lcQpaThemeDBus) << fileName << warning << "Falling back to default.";\
return;\
}
#define PARSE(var, enumeration, string)\
enumeration var;\
{\
bool success;\
const int val = QMetaEnum::fromType<enumeration>().keyToValue(string.toLatin1(), &success);\
CHECK(success, "Parse Error: Invalid value" << string << "for" << #var);\
var = static_cast<enumeration>(val);\
}
QFile file(fileName);
CHECK(file.exists(), fileName << "doesn't exist.");
CHECK(file.open(QIODevice::ReadOnly), "could not be opened for reading.");
QJsonParseError error;
QJsonDocument doc = QJsonDocument::fromJson(file.readAll(), &error);
CHECK((error.error == QJsonParseError::NoError), error.errorString());
CHECK(doc.isObject(), "Parse Error: Expected root object" << JsonKeys::root());
const QJsonObject &root = doc.object();
CHECK(root.contains(JsonKeys::root()), "Parse Error: Expected root object" << JsonKeys::root());
CHECK(root[JsonKeys::root()][JsonKeys::dbusSignals()].isArray(), "Parse Error: Expected array" << JsonKeys::dbusSignals());
const QJsonArray &sigs = root[JsonKeys::root()][JsonKeys::dbusSignals()].toArray();
CHECK((sigs.count() > 0), "Parse Error: Found empty array" << JsonKeys::dbusSignals());
for (auto sig = sigs.constBegin(); sig != sigs.constEnd(); ++sig) {
CHECK(sig->isObject(), "Parse Error: Expected object array" << JsonKeys::dbusSignals());
const QJsonObject &obj = sig->toObject();
CHECK(obj.contains(JsonKeys::dbusLocation()), "Parse Error: Expected key" << JsonKeys::dbusLocation());
CHECK(obj.contains(JsonKeys::dbusKey()), "Parse Error: Expected key" << JsonKeys::dbusKey());
CHECK(obj.contains(JsonKeys::provider()), "Parse Error: Expected key" << JsonKeys::provider());
CHECK(obj.contains(JsonKeys::setting()), "Parse Error: Expected key" << JsonKeys::setting());
const QString &location = obj[JsonKeys::dbusLocation()].toString();
const QString &key = obj[JsonKeys::dbusKey()].toString();
const QString &providerString = obj[JsonKeys::provider()].toString();
const QString &settingString = obj[JsonKeys::setting()].toString();
PARSE(provider, Provider, providerString);
PARSE(setting, Setting, settingString);
const DBusKey dkey(location, key);
CHECK (!m_signalMap.contains(dkey), "Duplicate key" << location << key);
m_signalMap.insert(dkey, ChangeSignal(provider, setting));
}
#undef PARSE
#undef CHECK
if (m_signalMap.count() > 0)
qCInfo(lcQpaThemeDBus) << "Successfully imported" << fileName;
else
qCWarning(lcQpaThemeDBus) << "No data imported from" << fileName << "falling back to default.";
#ifdef QT_DEBUG
const int count = m_signalMap.count();
if (count == 0)
return;
qCDebug(lcQpaThemeDBus) << "Listening to" << count << "signals:";
for (auto it = m_signalMap.constBegin(); it != m_signalMap.constEnd(); ++it) {
qDebug() << it.key().key << it.key().location << "mapped to"
<< it.value().provider << it.value().setting;
}
#endif
}
void QGenericUnixThemeDBusListener::saveJson(const QString &fileName) const
{
Q_ASSERT(!m_signalMap.isEmpty());
Q_ASSERT(!fileName.isEmpty());
QFile file(fileName);
if (!file.open(QIODevice::WriteOnly)) {
qCWarning(lcQpaThemeDBus) << fileName << "could not be opened for writing.";
return;
}
QJsonArray sigs;
for (auto sig = m_signalMap.constBegin(); sig != m_signalMap.constEnd(); ++sig) {
const DBusKey &dkey = sig.key();
const ChangeSignal &csig = sig.value();
QJsonObject obj;
obj[JsonKeys::dbusLocation()] = dkey.location;
obj[JsonKeys::dbusKey()] = dkey.key;
obj[JsonKeys::provider()] = QLatin1StringView(QMetaEnum::fromType<Provider>()
.valueToKey(static_cast<int>(csig.provider)));
obj[JsonKeys::setting()] = QLatin1StringView(QMetaEnum::fromType<Setting>()
.valueToKey(static_cast<int>(csig.setting)));
sigs.append(obj);
}
QJsonObject obj;
obj[JsonKeys::dbusSignals()] = sigs;
QJsonObject root;
root[JsonKeys::root()] = obj;
QJsonDocument doc(root);
file.write(doc.toJson());
file.close();
}
void QGenericUnixThemeDBusListener::populateSignalMap()
{
m_signalMap.clear();
const QString &loadJsonFile = qEnvironmentVariable("QT_QPA_DBUS_SIGNALS");
if (!loadJsonFile.isEmpty())
loadJson(loadJsonFile);
if (!m_signalMap.isEmpty())
return;
m_signalMap.insert(DBusKey("org.kde.kdeglobals.KDE"_L1, "widgetStyle"_L1),
ChangeSignal(Provider::Kde, Setting::ApplicationStyle));
m_signalMap.insert(DBusKey("org.kde.kdeglobals.General"_L1, "ColorScheme"_L1),
ChangeSignal(Provider::Kde, Setting::Theme));
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),
ChangeSignal(Provider::Gnome, Setting::ColorScheme));
const QString &saveJsonFile = qEnvironmentVariable("QT_QPA_DBUS_SIGNALS_SAVE");
if (!saveJsonFile.isEmpty())
saveJson(saveJsonFile);
}
std::optional<QGenericUnixThemeDBusListener::ChangeSignal>
QGenericUnixThemeDBusListener::findSignal(const QString &location, const QString &key) const
{
const DBusKey dkey(location, key);
std::optional<QGenericUnixThemeDBusListener::ChangeSignal> ret;
if (m_signalMap.contains(dkey))
ret.emplace(m_signalMap.value(dkey));
return ret;
}
void QGenericUnixThemeDBusListener::onSettingChanged(const QString &location, const QString &key, const QDBusVariant &value)
{
auto sig = findSignal(location, key);
if (!sig.has_value())
return;
emit settingChanged(sig.value().provider, sig.value().setting, value.variant().toString());
}
#endif //QT_NO_DBUS
class QGenericUnixThemePrivate : public QPlatformThemePrivate
{
public:
QGenericUnixThemePrivate()
: QPlatformThemePrivate()
, systemFont(QLatin1StringView(defaultSystemFontNameC), defaultSystemFontSize)
, fixedFont(QLatin1StringView(defaultFixedFontNameC), systemFont.pointSize())
{
fixedFont.setStyleHint(QFont::TypeWriter);
qCDebug(lcQpaFonts) << "default fonts: system" << systemFont << "fixed" << fixedFont;
}
const QFont systemFont;
QFont fixedFont;
};
QGenericUnixTheme::QGenericUnixTheme()
: QPlatformTheme(new QGenericUnixThemePrivate())
{
}
const QFont *QGenericUnixTheme::font(Font type) const
{
Q_D(const QGenericUnixTheme);
switch (type) {
case QPlatformTheme::SystemFont:
return &d->systemFont;
case QPlatformTheme::FixedFont:
return &d->fixedFont;
default:
return nullptr;
}
}
// Helper to return the icon theme paths from XDG.
QStringList QGenericUnixTheme::xdgIconThemePaths()
{
QStringList paths;
// Add home directory first in search path
const QFileInfo homeIconDir(QDir::homePath() + "/.icons"_L1);
if (homeIconDir.isDir())
paths.prepend(homeIconDir.absoluteFilePath());
paths.append(QStandardPaths::locateAll(QStandardPaths::GenericDataLocation,
QStringLiteral("icons"),
QStandardPaths::LocateDirectory));
return paths;
}
QStringList QGenericUnixTheme::iconFallbackPaths()
{
QStringList paths;
const QFileInfo pixmapsIconsDir(QStringLiteral("/usr/share/pixmaps"));
if (pixmapsIconsDir.isDir())
paths.append(pixmapsIconsDir.absoluteFilePath());
return paths;
}
#ifndef QT_NO_DBUS
QPlatformMenuBar *QGenericUnixTheme::createPlatformMenuBar() const
{
if (isDBusGlobalMenuAvailable())
return new QDBusMenuBar();
return nullptr;
}
#endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
QPlatformSystemTrayIcon *QGenericUnixTheme::createPlatformSystemTrayIcon() const
{
if (shouldUseDBusTray())
return new QDBusTrayIcon();
return nullptr;
}
#endif
QVariant QGenericUnixTheme::themeHint(ThemeHint hint) const
{
switch (hint) {
case QPlatformTheme::SystemIconFallbackThemeName:
return QVariant(QString(QStringLiteral("hicolor")));
case QPlatformTheme::IconThemeSearchPaths:
return xdgIconThemePaths();
case QPlatformTheme::IconFallbackSearchPaths:
return iconFallbackPaths();
case QPlatformTheme::DialogButtonBoxButtonsHaveIcons:
return QVariant(true);
case QPlatformTheme::StyleNames: {
QStringList styleNames;
styleNames << QStringLiteral("Fusion") << QStringLiteral("Windows");
return QVariant(styleNames);
}
case QPlatformTheme::KeyboardScheme:
return QVariant(int(X11KeyboardScheme));
case QPlatformTheme::UiEffects:
return QVariant(int(HoverEffect));
case QPlatformTheme::MouseCursorTheme:
return QVariant(mouseCursorTheme());
case QPlatformTheme::MouseCursorSize:
return QVariant(mouseCursorSize());
case QPlatformTheme::PreferFileIconFromTheme:
return true;
default:
break;
}
return QPlatformTheme::themeHint(hint);
}
// Helper functions for implementing QPlatformTheme::fileIcon() for XDG icon themes.
static QList<QSize> availableXdgFileIconSizes()
{
return QIcon::fromTheme(QStringLiteral("inode-directory")).availableSizes();
}
#if QT_CONFIG(mimetype)
static QIcon xdgFileIcon(const QFileInfo &fileInfo)
{
QMimeDatabase mimeDatabase;
QMimeType mimeType = mimeDatabase.mimeTypeForFile(fileInfo);
if (!mimeType.isValid())
return QIcon();
const QString &iconName = mimeType.iconName();
if (!iconName.isEmpty()) {
const QIcon icon = QIcon::fromTheme(iconName);
if (!icon.isNull())
return icon;
}
const QString &genericIconName = mimeType.genericIconName();
return genericIconName.isEmpty() ? QIcon() : QIcon::fromTheme(genericIconName);
}
#endif
#if QT_CONFIG(settings)
class QKdeThemePrivate : public QPlatformThemePrivate
class QKdeThemePrivate : public QGenericUnixThemePrivate
{
public:
@ -624,31 +117,31 @@ public:
private:
mutable QHash<QString, QSettings *> kdeSettings;
#ifndef QT_NO_DBUS
std::unique_ptr<QGenericUnixThemeDBusListener> dbus;
std::unique_ptr<QDBusListener> dbus;
bool initDbus();
void settingChangedHandler(QGenericUnixThemeDBusListener::Provider provider,
QGenericUnixThemeDBusListener::Setting setting,
void settingChangedHandler(QDBusListener::Provider provider,
QDBusListener::Setting setting,
const QString &value);
#endif // QT_NO_DBUS
};
#ifndef QT_NO_DBUS
void QKdeThemePrivate::settingChangedHandler(QGenericUnixThemeDBusListener::Provider provider,
QGenericUnixThemeDBusListener::Setting setting,
void QKdeThemePrivate::settingChangedHandler(QDBusListener::Provider provider,
QDBusListener::Setting setting,
const QString &value)
{
if (provider != QGenericUnixThemeDBusListener::Provider::Kde)
if (provider != QDBusListener::Provider::Kde)
return;
switch (setting) {
case QGenericUnixThemeDBusListener::Setting::ColorScheme:
qCDebug(lcQpaThemeDBus) << "KDE color theme changed to:" << value;
case QDBusListener::Setting::ColorScheme:
qCDebug(lcQpaThemeKde) << "KDE color theme changed to:" << value;
break;
case QGenericUnixThemeDBusListener::Setting::Theme:
qCDebug(lcQpaThemeDBus) << "KDE global theme changed to:" << value;
case QDBusListener::Setting::Theme:
qCDebug(lcQpaThemeKde) << "KDE global theme changed to:" << value;
break;
case QGenericUnixThemeDBusListener::Setting::ApplicationStyle:
qCDebug(lcQpaThemeDBus) << "KDE application style changed to:" << value;
case QDBusListener::Setting::ApplicationStyle:
qCDebug(lcQpaThemeKde) << "KDE application style changed to:" << value;
break;
}
@ -657,17 +150,17 @@ void QKdeThemePrivate::settingChangedHandler(QGenericUnixThemeDBusListener::Prov
bool QKdeThemePrivate::initDbus()
{
dbus.reset(new QGenericUnixThemeDBusListener());
dbus.reset(new QDBusListener());
Q_ASSERT(dbus);
// Wrap slot in a lambda to avoid inheriting QKdeThemePrivate from QObject
auto wrapper = [this](QGenericUnixThemeDBusListener::Provider provider,
QGenericUnixThemeDBusListener::Setting setting,
auto wrapper = [this](QDBusListener::Provider provider,
QDBusListener::Setting setting,
const QString &value) {
settingChangedHandler(provider, setting, value);
};
return QObject::connect(dbus.get(), &QGenericUnixThemeDBusListener::settingChanged, dbus.get(), wrapper);
return QObject::connect(dbus.get(), &QDBusListener::settingChanged, dbus.get(), wrapper);
}
#endif // QT_NO_DBUS
@ -909,12 +402,14 @@ void QKdeThemePrivate::refresh()
if (QFont *systemFont = kdeFont(readKdeSetting(KdeSetting::Font)))
resources.fonts[QPlatformTheme::SystemFont] = systemFont;
else
resources.fonts[QPlatformTheme::SystemFont] = new QFont(QLatin1StringView(defaultSystemFontNameC), defaultSystemFontSize);
resources.fonts[QPlatformTheme::SystemFont] = new QFont(QLatin1StringView(QGenericUnixTheme::defaultSystemFontNameC),
QGenericUnixTheme::defaultSystemFontSize);
if (QFont *fixedFont = kdeFont(readKdeSetting(KdeSetting::Fixed))) {
resources.fonts[QPlatformTheme::FixedFont] = fixedFont;
} else {
fixedFont = new QFont(QLatin1StringView(defaultFixedFontNameC), defaultSystemFontSize);
fixedFont = new QFont(QLatin1StringView(QGenericUnixTheme::defaultFixedFontNameC),
QGenericUnixTheme::defaultSystemFontSize);
fixedFont->setStyleHint(QFont::TypeWriter);
resources.fonts[QPlatformTheme::FixedFont] = fixedFont;
}
@ -1043,7 +538,7 @@ void QKdeThemePrivate::readKdeSystemPalette(const QStringList &kdeDirs, int kdeV
const char *QKdeTheme::name = "kde";
QKdeTheme::QKdeTheme(const QStringList& kdeDirs, int kdeVersion)
: QPlatformTheme(new QKdeThemePrivate(kdeDirs,kdeVersion))
: QGenericUnixTheme(new QKdeThemePrivate(kdeDirs, kdeVersion))
{
d_func()->refresh();
}
@ -1276,278 +771,4 @@ QPlatformSystemTrayIcon *QKdeTheme::createPlatformSystemTrayIcon() const
}
#endif
#endif // settings
/*!
\class QGnomeTheme
\brief QGnomeTheme is a theme implementation for the Gnome desktop.
\since 5.0
\internal
\ingroup qpa
*/
const char *QGnomeTheme::name = "gnome";
class QGnomeThemePrivate : public QPlatformThemePrivate
{
public:
QGnomeThemePrivate();
~QGnomeThemePrivate();
void configureFonts(const QString &gtkFontName) const
{
Q_ASSERT(!systemFont);
const int split = gtkFontName.lastIndexOf(QChar::Space);
float size = QStringView{gtkFontName}.mid(split + 1).toFloat();
QString fontName = gtkFontName.left(split);
systemFont = new QFont(fontName, size);
fixedFont = new QFont(QLatin1StringView(defaultFixedFontNameC), systemFont->pointSize());
fixedFont->setStyleHint(QFont::TypeWriter);
qCDebug(lcQpaFonts) << "default fonts: system" << systemFont << "fixed" << fixedFont;
}
mutable QFont *systemFont = nullptr;
mutable QFont *fixedFont = nullptr;
#ifndef QT_NO_DBUS
Qt::ColorScheme m_colorScheme = Qt::ColorScheme::Unknown;
private:
std::unique_ptr<QGenericUnixThemeDBusListener> dbus;
bool initDbus();
void updateColorScheme(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()
{
dbus.reset(new QGenericUnixThemeDBusListener());
Q_ASSERT(dbus);
// Wrap slot in a lambda to avoid inheriting QGnomeThemePrivate from QObject
auto wrapper = [this](QGenericUnixThemeDBusListener::Provider provider,
QGenericUnixThemeDBusListener::Setting setting,
const QString &value) {
if (provider != QGenericUnixThemeDBusListener::Provider::Gnome
&& provider != QGenericUnixThemeDBusListener::Provider::Gtk) {
return;
}
if (setting == QGenericUnixThemeDBusListener::Setting::Theme)
updateColorScheme(value);
};
return QObject::connect(dbus.get(), &QGenericUnixThemeDBusListener::settingChanged, dbus.get(), wrapper);
}
void QGnomeThemePrivate::updateColorScheme(const QString &themeName)
{
const auto oldColorScheme = m_colorScheme;
if (themeName.contains(QLatin1StringView("light"), Qt::CaseInsensitive)) {
m_colorScheme = Qt::ColorScheme::Light;
} else if (themeName.contains(QLatin1StringView("dark"), Qt::CaseInsensitive)) {
m_colorScheme = Qt::ColorScheme::Dark;
} else {
m_colorScheme = Qt::ColorScheme::Unknown;
}
if (oldColorScheme != m_colorScheme)
QWindowSystemInterface::handleThemeChange();
}
#endif // QT_NO_DBUS
QGnomeTheme::QGnomeTheme()
: QPlatformTheme(new QGnomeThemePrivate())
{
}
QVariant QGnomeTheme::themeHint(QPlatformTheme::ThemeHint hint) const
{
switch (hint) {
case QPlatformTheme::DialogButtonBoxButtonsHaveIcons:
return QVariant(true);
case QPlatformTheme::DialogButtonBoxLayout:
return QVariant(QPlatformDialogHelper::GnomeLayout);
case QPlatformTheme::SystemIconThemeName:
return QVariant(QStringLiteral("Adwaita"));
case QPlatformTheme::SystemIconFallbackThemeName:
return QVariant(QStringLiteral("gnome"));
case QPlatformTheme::IconThemeSearchPaths:
return QVariant(QGenericUnixTheme::xdgIconThemePaths());
case QPlatformTheme::IconPixmapSizes:
return QVariant::fromValue(availableXdgFileIconSizes());
case QPlatformTheme::StyleNames: {
QStringList styleNames;
styleNames << QStringLiteral("Fusion") << QStringLiteral("windows");
return QVariant(styleNames);
}
case QPlatformTheme::KeyboardScheme:
return QVariant(int(GnomeKeyboardScheme));
case QPlatformTheme::PasswordMaskCharacter:
return QVariant(QChar(0x2022));
case QPlatformTheme::UiEffects:
return QVariant(int(HoverEffect));
case QPlatformTheme::ButtonPressKeys:
return QVariant::fromValue(
QList<Qt::Key>({ Qt::Key_Space, Qt::Key_Return, Qt::Key_Enter, Qt::Key_Select }));
case QPlatformTheme::PreselectFirstFileInDirectory:
return true;
case QPlatformTheme::MouseCursorTheme:
return QVariant(mouseCursorTheme());
case QPlatformTheme::MouseCursorSize:
return QVariant(mouseCursorSize());
case QPlatformTheme::PreferFileIconFromTheme:
return true;
default:
break;
}
return QPlatformTheme::themeHint(hint);
}
QIcon QGnomeTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions) const
{
#if QT_CONFIG(mimetype)
return xdgFileIcon(fileInfo);
#else
Q_UNUSED(fileInfo);
return QIcon();
#endif
}
const QFont *QGnomeTheme::font(Font type) const
{
Q_D(const QGnomeTheme);
if (!d->systemFont)
d->configureFonts(gtkFontName());
switch (type) {
case QPlatformTheme::SystemFont:
return d->systemFont;
case QPlatformTheme::FixedFont:
return d->fixedFont;
default:
return nullptr;
}
}
QString QGnomeTheme::gtkFontName() const
{
return QStringLiteral("%1 %2").arg(QLatin1StringView(defaultSystemFontNameC)).arg(defaultSystemFontSize);
}
#ifndef QT_NO_DBUS
QPlatformMenuBar *QGnomeTheme::createPlatformMenuBar() const
{
if (isDBusGlobalMenuAvailable())
return new QDBusMenuBar();
return nullptr;
}
Qt::ColorScheme QGnomeTheme::colorScheme() const
{
return d_func()->m_colorScheme;
}
#endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
QPlatformSystemTrayIcon *QGnomeTheme::createPlatformSystemTrayIcon() const
{
if (shouldUseDBusTray())
return new QDBusTrayIcon();
return nullptr;
}
#endif
QString QGnomeTheme::standardButtonText(int button) const
{
switch (button) {
case QPlatformDialogHelper::Ok:
return QCoreApplication::translate("QGnomeTheme", "&OK");
case QPlatformDialogHelper::Save:
return QCoreApplication::translate("QGnomeTheme", "&Save");
case QPlatformDialogHelper::Cancel:
return QCoreApplication::translate("QGnomeTheme", "&Cancel");
case QPlatformDialogHelper::Close:
return QCoreApplication::translate("QGnomeTheme", "&Close");
case QPlatformDialogHelper::Discard:
return QCoreApplication::translate("QGnomeTheme", "Close without Saving");
default:
break;
}
return QPlatformTheme::standardButtonText(button);
}
/*!
\brief Creates a UNIX theme according to the detected desktop environment.
*/
QPlatformTheme *QGenericUnixTheme::createUnixTheme(const QString &name)
{
if (name == QLatin1StringView(QGenericUnixTheme::name))
return new QGenericUnixTheme;
#if QT_CONFIG(settings)
if (name == QLatin1StringView(QKdeTheme::name))
if (QPlatformTheme *kdeTheme = QKdeTheme::createKdeTheme())
return kdeTheme;
#endif
if (name == QLatin1StringView(QGnomeTheme::name))
return new QGnomeTheme;
return nullptr;
}
QStringList QGenericUnixTheme::themeNames()
{
QStringList result;
if (QGuiApplication::desktopSettingsAware()) {
const QByteArray desktopEnvironment = QGuiApplicationPrivate::platformIntegration()->services()->desktopEnvironment();
QList<QByteArray> gtkBasedEnvironments;
gtkBasedEnvironments << "GNOME"
<< "X-CINNAMON"
<< "PANTHEON"
<< "UNITY"
<< "MATE"
<< "XFCE"
<< "LXDE";
const QList<QByteArray> desktopNames = desktopEnvironment.split(':');
for (const QByteArray &desktopName : desktopNames) {
if (desktopEnvironment == "KDE") {
#if QT_CONFIG(settings)
result.push_back(QLatin1StringView(QKdeTheme::name));
#endif
} else if (gtkBasedEnvironments.contains(desktopName)) {
// prefer the GTK3 theme implementation with native dialogs etc.
result.push_back(QStringLiteral("gtk3"));
// fallback to the generic Gnome theme if loading the GTK3 theme fails
result.push_back(QLatin1StringView(QGnomeTheme::name));
} else {
// unknown, but lowercase the name (our standard practice) and
// remove any "x-" prefix
QString s = QString::fromLatin1(desktopName.toLower());
result.push_back(s.startsWith("x-"_L1) ? s.mid(2) : s);
}
}
} // desktopSettingsAware
result.append(QLatin1StringView(QGenericUnixTheme::name));
return result;
}
QT_END_NAMESPACE
#ifndef QT_NO_DBUS
#include "qgenericunixthemes.moc"
#endif // QT_NO_DBUS

View File

@ -0,0 +1,68 @@
// 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 QKDETHEME_P_H
#define QKDETHEME_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 "qgenericunixtheme_p.h"
#include <qpa/qplatformtheme.h>
#include <QtCore/QString>
#include <QtCore/QStringList>
#include <QtGui/QFont>
#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
class ResourceHelper
{
public:
ResourceHelper();
~ResourceHelper() { clear(); }
void clear();
QPalette *palettes[QPlatformTheme::NPalettes];
QFont *fonts[QPlatformTheme::NFonts];
};
class QKdeThemePrivate;
class Q_GUI_EXPORT QKdeTheme : public QGenericUnixTheme
{
Q_DECLARE_PRIVATE(QKdeTheme)
public:
QKdeTheme(const QStringList& kdeDirs, int kdeVersion);
static QPlatformTheme *createKdeTheme();
QVariant themeHint(ThemeHint hint) const override;
QIcon fileIcon(const QFileInfo &fileInfo,
QPlatformTheme::IconOptions iconOptions = { }) const override;
const QPalette *palette(Palette type = SystemPalette) const override;
Qt::ColorScheme colorScheme() const override;
//void requestColorScheme(Qt::ColorScheme scheme) override;
const QFont *font(Font type) const override;
#ifndef QT_NO_DBUS
QPlatformMenuBar *createPlatformMenuBar() const override;
#endif
#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override;
#endif
static const char *name;
};
QT_END_NAMESPACE
#endif // QKDETHEME_P_H

View File

@ -32,7 +32,7 @@
#endif
#include <QtGui/private/qgenericunixfontdatabase_p.h>
#include <QtGui/private/qgenericunixthemes_p.h>
#include <QtGui/private/qgenericunixtheme_p.h>
#include <QtGui/private/qgenericunixeventdispatcher_p.h>
#include <QtFbSupport/private/qfbvthandler_p.h>
#ifndef QT_NO_OPENGL

View File

@ -13,7 +13,7 @@
#include <QtGui/private/qwindow_p.h>
#include <QtGui/private/qgenericunixeventdispatcher_p.h>
#include <QtGui/private/qgenericunixfontdatabase_p.h>
#include <QtGui/private/qgenericunixthemes_p.h>
#include <QtGui/private/qgenericunixtheme_p.h>
#include <qpa/qplatformservices.h>
#include <QtFbSupport/private/qfbvthandler_p.h>

View File

@ -43,7 +43,8 @@
#endif
#include <qpa/qplatforminputcontextfactory_p.h>
#include <private/qgenericunixthemes_p.h>
#include <private/qgenericunixtheme_p.h>
#include <private/qkdetheme_p.h>
#include <qpa/qplatforminputcontext.h>
#include <QtGui/QOpenGLContext>

View File

@ -5,7 +5,7 @@
#define QGTK3THEME_H
#include <private/qtguiglobal_p.h>
#include <private/qgenericunixthemes_p.h>
#include <private/qgnometheme_p.h>
#include "qgtk3storage_p.h"
QT_BEGIN_NAMESPACE