Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qgenericunixthemes.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QPalette>
7#include <QFont>
8#include <QGuiApplication>
9#include <QDir>
10#include <QFileInfo>
11#include <QFile>
12#include <QDebug>
13#include <QHash>
14#include <QLoggingCategory>
15#include <QVariant>
16#include <QStandardPaths>
17#include <QStringList>
18#if QT_CONFIG(mimetype)
19#include <QMimeDatabase>
20#endif
21#if QT_CONFIG(settings)
22#include <QSettings>
23#endif
24
25#include <qpa/qplatformfontdatabase.h> // lcQpaFonts
26#include <qpa/qplatformintegration.h>
27#include <qpa/qplatformservices.h>
28#include <qpa/qplatformdialoghelper.h>
29#include <qpa/qplatformtheme_p.h>
30
31#include <private/qguiapplication_p.h>
32#ifndef QT_NO_DBUS
33#include <QDBusConnectionInterface>
34#include <private/qdbusplatformmenu_p.h>
35#include <private/qdbusmenubar_p.h>
36#include <private/qflatmap_p.h>
37#include <QJsonDocument>
38#include <QJsonArray>
39#include <QJsonObject>
40#include <QJsonValue>
41#include <QJsonParseError>
42#endif
43#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
44#include <private/qdbustrayicon_p.h>
45#endif
46
47#include <algorithm>
48
50#ifndef QT_NO_DBUS
51Q_LOGGING_CATEGORY(lcQpaThemeDBus, "qt.qpa.theme.dbus")
52#endif
53
54using namespace Qt::StringLiterals;
55
57
59{
60 std::fill(palettes, palettes + QPlatformTheme::NPalettes, static_cast<QPalette *>(nullptr));
61 std::fill(fonts, fonts + QPlatformTheme::NFonts, static_cast<QFont *>(nullptr));
62}
63
71
72const char *QGenericUnixTheme::name = "generic";
73
74// Default system font, corresponding to the value returned by 4.8 for
75// XRender/FontConfig which we can now assume as default.
76static const char defaultSystemFontNameC[] = "Sans Serif";
77static const char defaultFixedFontNameC[] = "monospace";
79
80#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
81static bool isDBusTrayAvailable() {
82 static bool dbusTrayAvailable = false;
83 static bool dbusTrayAvailableKnown = false;
84 if (!dbusTrayAvailableKnown) {
86 if (conn.isWatcherRegistered())
87 dbusTrayAvailable = true;
88 dbusTrayAvailableKnown = true;
89 qCDebug(qLcTray) << "D-Bus tray available:" << dbusTrayAvailable;
90 }
91 return dbusTrayAvailable;
92}
93#endif
94
96{
97 static QString themeName = qEnvironmentVariable("XCURSOR_THEME");
98 return themeName;
99}
100
102{
103 constexpr int defaultCursorSize = 24;
104 static const int xCursorSize = qEnvironmentVariableIntValue("XCURSOR_SIZE");
105 static const int s = xCursorSize > 0 ? xCursorSize : defaultCursorSize;
106 return QSize(s, s);
107}
108
109#ifndef QT_NO_DBUS
111{
113 static const QString registrarService = QStringLiteral("com.canonical.AppMenu.Registrar");
114 if (const auto iface = connection.interface())
115 return iface->isServiceRegistered(registrarService);
116 return false;
117}
118
120{
121 static bool dbusGlobalMenuAvailable = checkDBusGlobalMenuAvailable();
122 return dbusGlobalMenuAvailable;
123}
124
136{
138
139public:
140
141 enum class Provider {
142 Kde,
143 Gtk,
144 Gnome,
145 };
147
148 enum class Setting {
149 Theme,
152 };
154
156 QGenericUnixThemeDBusListener(const QString &service, const QString &path,
157 const QString &interface, const QString &signal);
158
159private Q_SLOTS:
160 void onSettingChanged(const QString &location, const QString &key, const QDBusVariant &value);
161
165 const QString &value);
166
167private:
168 struct DBusKey
169 {
171 QString key;
172 DBusKey(const QString &loc, const QString &k) : location(loc), key(k) {};
173 bool operator<(const DBusKey &other) const
174 {
175 return location + key < other.location + other.key;
176 }
177 };
178
179 struct ChangeSignal
180 {
181 Provider provider;
182 Setting setting;
183 ChangeSignal(Provider p, Setting s) : provider(p), setting(s) {}
184 ChangeSignal() {}
185 };
186
187 // Json keys
188 static constexpr QLatin1StringView s_dbusLocation = QLatin1StringView("DBusLocation");
189 static constexpr QLatin1StringView s_dbusKey = QLatin1StringView("DBusKey");
190 static constexpr QLatin1StringView s_provider = QLatin1StringView("Provider");
191 static constexpr QLatin1StringView s_setting = QLatin1StringView("Setting");
192 static constexpr QLatin1StringView s_signals = QLatin1StringView("DbusSignals");
193 static constexpr QLatin1StringView s_root = QLatin1StringView("Qt.qpa.DBusSignals");
194
195 QFlatMap <DBusKey, ChangeSignal> m_signalMap;
196
197 void init(const QString &service, const QString &path,
198 const QString &interface, const QString &signal);
199
200 std::optional<ChangeSignal> findSignal(const QString &location, const QString &key) const;
201 void populateSignalMap();
202 void loadJson(const QString &fileName);
203 void saveJson(const QString &fileName) const;
204};
205
207 const QString &path, const QString &interface, const QString &signal)
208{
209 init (service, path, interface, signal);
210}
211
213{
214 static constexpr QLatin1StringView service("");
215 static constexpr QLatin1StringView path("/org/freedesktop/portal/desktop");
216 static constexpr QLatin1StringView interface("org.freedesktop.portal.Settings");
217 static constexpr QLatin1StringView signal("SettingChanged");
218
219 init (service, path, interface, signal);
220}
221
222void QGenericUnixThemeDBusListener::init(const QString &service, const QString &path,
223 const QString &interface, const QString &signal)
224{
226 const bool dBusRunning = dbus.isConnected();
227 bool dBusSignalConnected = false;
228#define LOG service << path << interface << signal;
229
230 if (dBusRunning) {
231 populateSignalMap();
232 qRegisterMetaType<QDBusVariant>();
233 dBusSignalConnected = dbus.connect(service, path, interface, signal, this,
234 SLOT(onSettingChanged(QString,QString,QDBusVariant)));
235 }
236
237 if (dBusSignalConnected) {
238 // Connection successful
239 qCDebug(lcQpaThemeDBus) << LOG;
240 } else {
241 if (dBusRunning) {
242 // DBus running, but connection failed
243 qCWarning(lcQpaThemeDBus) << "DBus connection failed:" << LOG;
244 } else {
245 // DBus not running
246 qCWarning(lcQpaThemeDBus) << "Session DBus not running.";
247 }
248 qCWarning(lcQpaThemeDBus) << "Application will not react to setting changes.\n"
249 << "Check your DBus installation.";
250 }
251#undef LOG
252}
253
254void QGenericUnixThemeDBusListener::loadJson(const QString &fileName)
255{
256 Q_ASSERT(!fileName.isEmpty());
257#define CHECK(cond, warning)\
258 if (!cond) {\
259 qCWarning(lcQpaThemeDBus) << fileName << warning << "Falling back to default.";\
260 return;\
261 }
262
263#define PARSE(var, enumeration, string)\
264 enumeration var;\
265 {\
266 bool success;\
267 const int val = QMetaEnum::fromType<enumeration>().keyToValue(string.toLatin1(), &success);\
268 CHECK(success, "Parse Error: Invalid value" << string << "for" << #var);\
269 var = static_cast<enumeration>(val);\
270 }
271
273 CHECK(file.exists(), fileName << "doesn't exist.");
274 CHECK(file.open(QIODevice::ReadOnly), "could not be opened for reading.");
275
278 CHECK((error.error == QJsonParseError::NoError), error.errorString());
279 CHECK(doc.isObject(), "Parse Error: Expected root object" << s_root);
280
281 const QJsonObject &root = doc.object();
282 CHECK(root.contains(s_root), "Parse Error: Expected root object" << s_root);
283 CHECK(root[s_root][s_signals].isArray(), "Parse Error: Expected array" << s_signals);
284
285 const QJsonArray &sigs = root[s_root][s_signals].toArray();
286 CHECK((sigs.count() > 0), "Parse Error: Found empty array" << s_signals);
287
288 for (auto sig = sigs.constBegin(); sig != sigs.constEnd(); ++sig) {
289 CHECK(sig->isObject(), "Parse Error: Expected object array" << s_signals);
290 const QJsonObject &obj = sig->toObject();
291 CHECK(obj.contains(s_dbusLocation), "Parse Error: Expected key" << s_dbusLocation);
292 CHECK(obj.contains(s_dbusKey), "Parse Error: Expected key" << s_dbusKey);
293 CHECK(obj.contains(s_provider), "Parse Error: Expected key" << s_provider);
294 CHECK(obj.contains(s_setting), "Parse Error: Expected key" << s_setting);
295 const QString &location = obj[s_dbusLocation].toString();
296 const QString &key = obj[s_dbusKey].toString();
297 const QString &providerString = obj[s_provider].toString();
298 const QString &settingString = obj[s_setting].toString();
299 PARSE(provider, Provider, providerString);
300 PARSE(setting, Setting, settingString);
301 const DBusKey dkey(location, key);
302 CHECK (!m_signalMap.contains(dkey), "Duplicate key" << location << key);
303 m_signalMap.insert(dkey, ChangeSignal(provider, setting));
304 }
305#undef PARSE
306#undef CHECK
307
308 if (m_signalMap.count() > 0)
309 qCInfo(lcQpaThemeDBus) << "Successfully imported" << fileName;
310 else
311 qCWarning(lcQpaThemeDBus) << "No data imported from" << fileName << "falling back to default.";
312
313#ifdef QT_DEBUG
314 const int count = m_signalMap.count();
315 if (count == 0)
316 return;
317
318 qCDebug(lcQpaThemeDBus) << "Listening to" << count << "signals:";
319 for (auto it = m_signalMap.constBegin(); it != m_signalMap.constEnd(); ++it) {
320 qDebug() << it.key().key << it.key().location << "mapped to"
321 << it.value().provider << it.value().setting;
322 }
323
324#endif
325}
326
327void QGenericUnixThemeDBusListener::saveJson(const QString &fileName) const
328{
329 Q_ASSERT(!m_signalMap.isEmpty());
330 Q_ASSERT(!fileName.isEmpty());
333 qCWarning(lcQpaThemeDBus) << fileName << "could not be opened for writing.";
334 return;
335 }
336
337 QJsonArray sigs;
338 for (auto sig = m_signalMap.constBegin(); sig != m_signalMap.constEnd(); ++sig) {
339 const DBusKey &dkey = sig.key();
340 const ChangeSignal &csig = sig.value();
342 obj[s_dbusLocation] = dkey.location;
343 obj[s_dbusKey] = dkey.key;
344 obj[s_provider] = QLatin1StringView(QMetaEnum::fromType<Provider>()
345 .valueToKey(static_cast<int>(csig.provider)));
346 obj[s_setting] = QLatin1StringView(QMetaEnum::fromType<Setting>()
347 .valueToKey(static_cast<int>(csig.setting)));
348 sigs.append(obj);
349 }
351 obj[s_signals] = sigs;
352 QJsonObject root;
353 root[s_root] = obj;
354 QJsonDocument doc(root);
355 file.write(doc.toJson());
356 file.close();
357}
358
359void QGenericUnixThemeDBusListener::populateSignalMap()
360{
361 m_signalMap.clear();
362 const QString &loadJsonFile = qEnvironmentVariable("QT_QPA_DBUS_SIGNALS");
363 if (!loadJsonFile.isEmpty())
364 loadJson(loadJsonFile);
365 if (!m_signalMap.isEmpty())
366 return;
367
368 m_signalMap.insert(DBusKey("org.kde.kdeglobals.KDE"_L1, "widgetStyle"_L1),
370
371 m_signalMap.insert(DBusKey("org.kde.kdeglobals.General"_L1, "ColorScheme"_L1),
372 ChangeSignal(Provider::Kde, Setting::Theme));
373
374 m_signalMap.insert(DBusKey("org.gnome.desktop.interface"_L1, "gtk-theme"_L1),
375 ChangeSignal(Provider::Gtk, Setting::Theme));
376
377 m_signalMap.insert(DBusKey("org.freedesktop.appearance"_L1, "color-scheme"_L1),
379
380 const QString &saveJsonFile = qEnvironmentVariable("QT_QPA_DBUS_SIGNALS_SAVE");
381 if (!saveJsonFile.isEmpty())
382 saveJson(saveJsonFile);
383}
384
385std::optional<QGenericUnixThemeDBusListener::ChangeSignal>
386 QGenericUnixThemeDBusListener::findSignal(const QString &location, const QString &key) const
387{
388 const DBusKey dkey(location, key);
389 std::optional<QGenericUnixThemeDBusListener::ChangeSignal> ret;
390 if (m_signalMap.contains(dkey))
391 ret.emplace(m_signalMap.value(dkey));
392
393 return ret;
394}
395
396void QGenericUnixThemeDBusListener::onSettingChanged(const QString &location, const QString &key, const QDBusVariant &value)
397{
398 auto sig = findSignal(location, key);
399 if (!sig.has_value())
400 return;
401
402 emit settingChanged(sig.value().provider, sig.value().setting, value.variant().toString());
403}
404
405#endif //QT_NO_DBUS
406
422
427
429{
430 Q_D(const QGenericUnixTheme);
431 switch (type) {
433 return &d->systemFont;
435 return &d->fixedFont;
436 default:
437 return nullptr;
438 }
439}
440
441// Helper to return the icon theme paths from XDG.
443{
445 // Add home directory first in search path
446 const QFileInfo homeIconDir(QDir::homePath() + "/.icons"_L1);
447 if (homeIconDir.isDir())
448 paths.prepend(homeIconDir.absoluteFilePath());
449
451 QStringLiteral("icons"),
453
454 return paths;
455}
456
458{
460 const QFileInfo pixmapsIconsDir(QStringLiteral("/usr/share/pixmaps"));
461 if (pixmapsIconsDir.isDir())
462 paths.append(pixmapsIconsDir.absoluteFilePath());
463
464 return paths;
465}
466
467#ifndef QT_NO_DBUS
469{
471 return new QDBusMenuBar();
472 return nullptr;
473}
474#endif
475
476#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
478{
480 return new QDBusTrayIcon();
481 return nullptr;
482}
483#endif
484
514
515// Helper functions for implementing QPlatformTheme::fileIcon() for XDG icon themes.
516static QList<QSize> availableXdgFileIconSizes()
517{
518 return QIcon::fromTheme(QStringLiteral("inode-directory")).availableSizes();
519}
520
521#if QT_CONFIG(mimetype)
522static QIcon xdgFileIcon(const QFileInfo &fileInfo)
523{
524 QMimeDatabase mimeDatabase;
525 QMimeType mimeType = mimeDatabase.mimeTypeForFile(fileInfo);
526 if (!mimeType.isValid())
527 return QIcon();
528 const QString &iconName = mimeType.iconName();
529 if (!iconName.isEmpty()) {
530 const QIcon icon = QIcon::fromTheme(iconName);
531 if (!icon.isNull())
532 return icon;
533 }
534 const QString &genericIconName = mimeType.genericIconName();
535 return genericIconName.isEmpty() ? QIcon() : QIcon::fromTheme(genericIconName);
536}
537#endif
538
539#if QT_CONFIG(settings)
540class QKdeThemePrivate : public QPlatformThemePrivate
541{
542
543public:
544 enum class KdeSettingType {
545 Root,
546 KDE,
547 Icons,
548 ToolBarIcons,
549 ToolBarStyle,
550 Fonts,
551 Colors,
552 };
553
554 enum class KdeSetting {
555 WidgetStyle,
557 SingleClick,
558 ShowIconsOnPushButtons,
559 IconTheme,
560 ToolBarIconSize,
562 WheelScrollLines,
563 DoubleClickInterval,
564 StartDragDistance,
565 StartDragTime,
566 CursorBlinkRate,
567 Font,
568 Fixed,
569 MenuFont,
570 ToolBarFont,
571 ButtonBackground,
572 WindowBackground,
573 ViewForeground,
574 WindowForeground,
575 ViewBackground,
576 SelectionBackground,
577 SelectionForeground,
578 ViewBackgroundAlternate,
579 ButtonForeground,
580 ViewForegroundLink,
581 ViewForegroundVisited,
582 TooltipBackground,
583 TooltipForeground,
584 };
585
586 QKdeThemePrivate(const QStringList &kdeDirs, int kdeVersion);
587
588 static QString kdeGlobals(const QString &kdeDir, int kdeVersion)
589 {
590 if (kdeVersion > 4)
591 return kdeDir + "/kdeglobals"_L1;
592 return kdeDir + "/share/config/kdeglobals"_L1;
593 }
594
595 void refresh();
596 static QVariant readKdeSetting(KdeSetting s, const QStringList &kdeDirs, int kdeVersion, QHash<QString, QSettings*> &settings);
597 QVariant readKdeSetting(KdeSetting s) const;
598 void clearKdeSettings() const;
599 static void readKdeSystemPalette(const QStringList &kdeDirs, int kdeVersion, QHash<QString, QSettings*> &kdeSettings, QPalette *pal);
600 static QFont *kdeFont(const QVariant &fontValue);
601 static QStringList kdeIconThemeSearchPaths(const QStringList &kdeDirs);
602
603 const QStringList kdeDirs;
604 const int kdeVersion;
605
606 ResourceHelper resources;
607 QString iconThemeName;
608 QString iconFallbackThemeName;
610 int toolButtonStyle = Qt::ToolButtonTextBesideIcon;
611 int toolBarIconSize = 0;
612 bool singleClick = true;
613 bool showIconsOnPushButtons = true;
614 int wheelScrollLines = 3;
615 int doubleClickInterval = 400;
616 int startDragDist = 10;
617 int startDragTime = 500;
618 int cursorBlinkRate = 1000;
620 void updateColorScheme(const QString &themeName);
621
622private:
623 mutable QHash<QString, QSettings *> kdeSettings;
624#ifndef QT_NO_DBUS
625 std::unique_ptr<QGenericUnixThemeDBusListener> dbus;
626 bool initDbus();
627 void settingChangedHandler(QGenericUnixThemeDBusListener::Provider provider,
629 const QString &value);
630#endif // QT_NO_DBUS
631};
632
633#ifndef QT_NO_DBUS
634void QKdeThemePrivate::settingChangedHandler(QGenericUnixThemeDBusListener::Provider provider,
636 const QString &value)
637{
639 return;
640
641 switch (setting) {
643 qCDebug(lcQpaThemeDBus) << "KDE color theme changed to:" << value;
644 break;
646 qCDebug(lcQpaThemeDBus) << "KDE global theme changed to:" << value;
647 break;
649 qCDebug(lcQpaThemeDBus) << "KDE application style changed to:" << value;
650 break;
651 }
652
653 refresh();
654}
655
656bool QKdeThemePrivate::initDbus()
657{
659 Q_ASSERT(dbus);
660
661 // Wrap slot in a lambda to avoid inheriting QKdeThemePrivate from QObject
664 const QString &value) {
665 settingChangedHandler(provider, setting, value);
666 };
667
669}
670#endif // QT_NO_DBUS
671
672QKdeThemePrivate::QKdeThemePrivate(const QStringList &kdeDirs, int kdeVersion)
673 : kdeDirs(kdeDirs), kdeVersion(kdeVersion)
674{
675#ifndef QT_NO_DBUS
676 initDbus();
677#endif // QT_NO_DBUS
678}
679
680static constexpr QLatin1StringView settingsPrefix(QKdeThemePrivate::KdeSettingType type)
681{
682 switch (type) {
683 case QKdeThemePrivate::KdeSettingType::Root:
684 return QLatin1StringView();
685 case QKdeThemePrivate::KdeSettingType::KDE:
686 return QLatin1StringView("KDE/");
687 case QKdeThemePrivate::KdeSettingType::Fonts:
688 return QLatin1StringView();
689 case QKdeThemePrivate::KdeSettingType::Colors:
690 return QLatin1StringView("Colors:");
691 case QKdeThemePrivate::KdeSettingType::Icons:
692 return QLatin1StringView("Icons/");
693 case QKdeThemePrivate::KdeSettingType::ToolBarIcons:
694 return QLatin1StringView("ToolbarIcons/");
695 case QKdeThemePrivate::KdeSettingType::ToolBarStyle:
696 return QLatin1StringView("Toolbar style/");
697 }
698 Q_UNREACHABLE_RETURN(QLatin1StringView());
699}
700
701static constexpr QKdeThemePrivate::KdeSettingType settingsType(QKdeThemePrivate::KdeSetting setting)
702{
703#define CASE(s, type) case QKdeThemePrivate::KdeSetting::s:\
704 return QKdeThemePrivate::KdeSettingType::type
705
706 switch (setting) {
707 CASE(WidgetStyle, Root);
708 CASE(ColorScheme, Root);
709 CASE(SingleClick, KDE);
710 CASE(ShowIconsOnPushButtons, KDE);
711 CASE(IconTheme, Icons);
712 CASE(ToolBarIconSize, ToolBarIcons);
713 CASE(ToolButtonStyle, ToolBarStyle);
714 CASE(WheelScrollLines, KDE);
715 CASE(DoubleClickInterval, KDE);
716 CASE(StartDragDistance, KDE);
717 CASE(StartDragTime, KDE);
718 CASE(CursorBlinkRate, KDE);
719 CASE(Font, Root);
720 CASE(Fixed, Root);
721 CASE(MenuFont, Root);
722 CASE(ToolBarFont, Root);
723 CASE(ButtonBackground, Colors);
724 CASE(WindowBackground, Colors);
725 CASE(ViewForeground, Colors);
726 CASE(WindowForeground, Colors);
727 CASE(ViewBackground, Colors);
728 CASE(SelectionBackground, Colors);
729 CASE(SelectionForeground, Colors);
730 CASE(ViewBackgroundAlternate, Colors);
731 CASE(ButtonForeground, Colors);
732 CASE(ViewForegroundLink, Colors);
733 CASE(ViewForegroundVisited, Colors);
734 CASE(TooltipBackground, Colors);
735 CASE(TooltipForeground, Colors);
736 };
737 Q_UNREACHABLE_RETURN(QKdeThemePrivate::KdeSettingType::Root);
738}
739#undef CASE
740
741static constexpr QLatin1StringView settingsKey(QKdeThemePrivate::KdeSetting setting)
742{
743 switch (setting) {
744 case QKdeThemePrivate::KdeSetting::WidgetStyle:
745 return QLatin1StringView("widgetStyle");
746 case QKdeThemePrivate::KdeSetting::ColorScheme:
747 return QLatin1StringView("ColorScheme");
748 case QKdeThemePrivate::KdeSetting::SingleClick:
749 return QLatin1StringView("SingleClick");
750 case QKdeThemePrivate::KdeSetting::ShowIconsOnPushButtons:
751 return QLatin1StringView("ShowIconsOnPushButtons");
752 case QKdeThemePrivate::KdeSetting::IconTheme:
753 return QLatin1StringView("Theme");
754 case QKdeThemePrivate::KdeSetting::ToolBarIconSize:
755 return QLatin1StringView("Size");
756 case QKdeThemePrivate::KdeSetting::ToolButtonStyle:
757 return QLatin1StringView("ToolButtonStyle");
758 case QKdeThemePrivate::KdeSetting::WheelScrollLines:
759 return QLatin1StringView("WheelScrollLines");
760 case QKdeThemePrivate::KdeSetting::DoubleClickInterval:
761 return QLatin1StringView("DoubleClickInterval");
762 case QKdeThemePrivate::KdeSetting::StartDragDistance:
763 return QLatin1StringView("StartDragDist");
764 case QKdeThemePrivate::KdeSetting::StartDragTime:
765 return QLatin1StringView("StartDragTime");
766 case QKdeThemePrivate::KdeSetting::CursorBlinkRate:
767 return QLatin1StringView("CursorBlinkRate");
768 case QKdeThemePrivate::KdeSetting::Font:
769 return QLatin1StringView("font");
770 case QKdeThemePrivate::KdeSetting::Fixed:
771 return QLatin1StringView("fixed");
772 case QKdeThemePrivate::KdeSetting::MenuFont:
773 return QLatin1StringView("menuFont");
774 case QKdeThemePrivate::KdeSetting::ToolBarFont:
775 return QLatin1StringView("toolBarFont");
776 case QKdeThemePrivate::KdeSetting::ButtonBackground:
777 return QLatin1StringView("Button/BackgroundNormal");
778 case QKdeThemePrivate::KdeSetting::WindowBackground:
779 return QLatin1StringView("Window/BackgroundNormal");
780 case QKdeThemePrivate::KdeSetting::ViewForeground:
781 return QLatin1StringView("View/ForegroundNormal");
782 case QKdeThemePrivate::KdeSetting::WindowForeground:
783 return QLatin1StringView("Window/ForegroundNormal");
784 case QKdeThemePrivate::KdeSetting::ViewBackground:
785 return QLatin1StringView("View/BackgroundNormal");
786 case QKdeThemePrivate::KdeSetting::SelectionBackground:
787 return QLatin1StringView("Selection/BackgroundNormal");
788 case QKdeThemePrivate::KdeSetting::SelectionForeground:
789 return QLatin1StringView("Selection/ForegroundNormal");
790 case QKdeThemePrivate::KdeSetting::ViewBackgroundAlternate:
791 return QLatin1StringView("View/BackgroundAlternate");
792 case QKdeThemePrivate::KdeSetting::ButtonForeground:
793 return QLatin1StringView("Button/ForegroundNormal");
794 case QKdeThemePrivate::KdeSetting::ViewForegroundLink:
795 return QLatin1StringView("View/ForegroundLink");
796 case QKdeThemePrivate::KdeSetting::ViewForegroundVisited:
797 return QLatin1StringView("View/ForegroundVisited");
798 case QKdeThemePrivate::KdeSetting::TooltipBackground:
799 return QLatin1StringView("Tooltip/BackgroundNormal");
800 case QKdeThemePrivate::KdeSetting::TooltipForeground:
801 return QLatin1StringView("Tooltip/ForegroundNormal");
802 };
803 Q_UNREACHABLE_RETURN(QLatin1StringView());
804}
805
806void QKdeThemePrivate::refresh()
807{
808 resources.clear();
809 clearKdeSettings();
810
811 toolButtonStyle = Qt::ToolButtonTextBesideIcon;
812 toolBarIconSize = 0;
813 styleNames.clear();
814 if (kdeVersion >= 5)
815 styleNames << QStringLiteral("breeze");
816 styleNames << QStringLiteral("Oxygen") << QStringLiteral("Fusion") << QStringLiteral("windows");
817 if (kdeVersion >= 5)
818 iconFallbackThemeName = iconThemeName = QStringLiteral("breeze");
819 else
820 iconFallbackThemeName = iconThemeName = QStringLiteral("oxygen");
821
822 QPalette systemPalette = QPalette();
823 readKdeSystemPalette(kdeDirs, kdeVersion, kdeSettings, &systemPalette);
824 resources.palettes[QPlatformTheme::SystemPalette] = new QPalette(systemPalette);
825 //## TODO tooltip color
826
827 const QVariant styleValue = readKdeSetting(KdeSetting::WidgetStyle);
828 if (styleValue.isValid()) {
829 const QString style = styleValue.toString();
830 if (style != styleNames.front())
831 styleNames.push_front(style);
832 }
833
834 const QVariant colorScheme = readKdeSetting(KdeSetting::ColorScheme);
835
836 if (colorScheme.isValid())
837 updateColorScheme(colorScheme.toString());
838 else
839 m_colorScheme = Qt::ColorScheme::Unknown;
840
841 const QVariant singleClickValue = readKdeSetting(KdeSetting::SingleClick);
842 if (singleClickValue.isValid())
843 singleClick = singleClickValue.toBool();
844
845 const QVariant showIconsOnPushButtonsValue = readKdeSetting(KdeSetting::ShowIconsOnPushButtons);
846 if (showIconsOnPushButtonsValue.isValid())
847 showIconsOnPushButtons = showIconsOnPushButtonsValue.toBool();
848
849 const QVariant themeValue = readKdeSetting(KdeSetting::IconTheme);
850 if (themeValue.isValid())
851 iconThemeName = themeValue.toString();
852
853 const QVariant toolBarIconSizeValue = readKdeSetting(KdeSetting::ToolBarIconSize);
854 if (toolBarIconSizeValue.isValid())
855 toolBarIconSize = toolBarIconSizeValue.toInt();
856
857 const QVariant toolbarStyleValue = readKdeSetting(KdeSetting::ToolButtonStyle);
858 if (toolbarStyleValue.isValid()) {
859 const QString toolBarStyle = toolbarStyleValue.toString();
860 if (toolBarStyle == "TextBesideIcon"_L1)
861 toolButtonStyle = Qt::ToolButtonTextBesideIcon;
862 else if (toolBarStyle == "TextOnly"_L1)
863 toolButtonStyle = Qt::ToolButtonTextOnly;
864 else if (toolBarStyle == "TextUnderIcon"_L1)
865 toolButtonStyle = Qt::ToolButtonTextUnderIcon;
866 }
867
868 const QVariant wheelScrollLinesValue = readKdeSetting(KdeSetting::WheelScrollLines);
869 if (wheelScrollLinesValue.isValid())
870 wheelScrollLines = wheelScrollLinesValue.toInt();
871
872 const QVariant doubleClickIntervalValue = readKdeSetting(KdeSetting::DoubleClickInterval);
873 if (doubleClickIntervalValue.isValid())
874 doubleClickInterval = doubleClickIntervalValue.toInt();
875
876 const QVariant startDragDistValue = readKdeSetting(KdeSetting::StartDragDistance);
877 if (startDragDistValue.isValid())
878 startDragDist = startDragDistValue.toInt();
879
880 const QVariant startDragTimeValue = readKdeSetting(KdeSetting::StartDragTime);
881 if (startDragTimeValue.isValid())
882 startDragTime = startDragTimeValue.toInt();
883
884 const QVariant cursorBlinkRateValue = readKdeSetting(KdeSetting::CursorBlinkRate);
885 if (cursorBlinkRateValue.isValid()) {
886 cursorBlinkRate = cursorBlinkRateValue.toInt();
887 cursorBlinkRate = cursorBlinkRate > 0 ? qBound(200, cursorBlinkRate, 2000) : 0;
888 }
889
890 // Read system font, ignore 'smallestReadableFont'
891 if (QFont *systemFont = kdeFont(readKdeSetting(KdeSetting::Font)))
892 resources.fonts[QPlatformTheme::SystemFont] = systemFont;
893 else
895
896 if (QFont *fixedFont = kdeFont(readKdeSetting(KdeSetting::Fixed))) {
897 resources.fonts[QPlatformTheme::FixedFont] = fixedFont;
898 } else {
900 fixedFont->setStyleHint(QFont::TypeWriter);
901 resources.fonts[QPlatformTheme::FixedFont] = fixedFont;
902 }
903
904 if (QFont *menuFont = kdeFont(readKdeSetting(KdeSetting::MenuFont))) {
905 resources.fonts[QPlatformTheme::MenuFont] = menuFont;
906 resources.fonts[QPlatformTheme::MenuBarFont] = new QFont(*menuFont);
907 }
908
909 if (QFont *toolBarFont = kdeFont(readKdeSetting(KdeSetting::ToolBarFont)))
910 resources.fonts[QPlatformTheme::ToolButtonFont] = toolBarFont;
911
913
914 qCDebug(lcQpaFonts) << "default fonts: system" << resources.fonts[QPlatformTheme::SystemFont]
915 << "fixed" << resources.fonts[QPlatformTheme::FixedFont];
916 qDeleteAll(kdeSettings);
917}
918
919QVariant QKdeThemePrivate::readKdeSetting(KdeSetting s, const QStringList &kdeDirs, int kdeVersion, QHash<QString, QSettings*> &kdeSettings)
920{
921 for (const QString &kdeDir : kdeDirs) {
922 QSettings *settings = kdeSettings.value(kdeDir);
923 if (!settings) {
924 const QString kdeGlobalsPath = kdeGlobals(kdeDir, kdeVersion);
925 if (QFileInfo(kdeGlobalsPath).isReadable()) {
926 settings = new QSettings(kdeGlobalsPath, QSettings::IniFormat);
927 kdeSettings.insert(kdeDir, settings);
928 }
929 }
930 if (settings) {
931 const QString key = settingsPrefix(settingsType(s)) + settingsKey(s);
932 const QVariant value = settings->value(key);
933 if (value.isValid())
934 return value;
935 }
936 }
937 return QVariant();
938}
939
940QVariant QKdeThemePrivate::readKdeSetting(KdeSetting s) const
941{
942 return readKdeSetting(s, kdeDirs, kdeVersion, kdeSettings);
943}
944
945void QKdeThemePrivate::clearKdeSettings() const
946{
947 kdeSettings.clear();
948}
949
950// Reads the color from the KDE configuration, and store it in the
951// palette with the given color role if found.
952static inline bool kdeColor(QPalette *pal, QPalette::ColorRole role, const QVariant &value)
953{
954 if (!value.isValid())
955 return false;
956 const QStringList values = value.toStringList();
957 if (values.size() != 3)
958 return false;
959 pal->setBrush(role, QColor(values.at(0).toInt(), values.at(1).toInt(), values.at(2).toInt()));
960 return true;
961}
962
963void QKdeThemePrivate::readKdeSystemPalette(const QStringList &kdeDirs, int kdeVersion, QHash<QString, QSettings*> &kdeSettings, QPalette *pal)
964{
965 if (!kdeColor(pal, QPalette::Button, readKdeSetting(KdeSetting::ButtonBackground, kdeDirs, kdeVersion, kdeSettings))) {
966 // kcolorscheme.cpp: SetDefaultColors
967 const QColor defaultWindowBackground(214, 210, 208);
968 const QColor defaultButtonBackground(223, 220, 217);
969 *pal = QPalette(defaultButtonBackground, defaultWindowBackground);
970 return;
971 }
972
973 kdeColor(pal, QPalette::Window, readKdeSetting(KdeSetting::WindowBackground, kdeDirs, kdeVersion, kdeSettings));
974 kdeColor(pal, QPalette::Text, readKdeSetting(KdeSetting::ViewForeground, kdeDirs, kdeVersion, kdeSettings));
975 kdeColor(pal, QPalette::WindowText, readKdeSetting(KdeSetting::WindowForeground, kdeDirs, kdeVersion, kdeSettings));
976 kdeColor(pal, QPalette::Base, readKdeSetting(KdeSetting::ViewBackground, kdeDirs, kdeVersion, kdeSettings));
977 kdeColor(pal, QPalette::Highlight, readKdeSetting(KdeSetting::SelectionBackground, kdeDirs, kdeVersion, kdeSettings));
978 kdeColor(pal, QPalette::HighlightedText, readKdeSetting(KdeSetting::SelectionForeground, kdeDirs, kdeVersion, kdeSettings));
979 kdeColor(pal, QPalette::AlternateBase, readKdeSetting(KdeSetting::ViewBackgroundAlternate, kdeDirs, kdeVersion, kdeSettings));
980 kdeColor(pal, QPalette::ButtonText, readKdeSetting(KdeSetting::ButtonForeground, kdeDirs, kdeVersion, kdeSettings));
981 kdeColor(pal, QPalette::Link, readKdeSetting(KdeSetting::ViewForegroundLink, kdeDirs, kdeVersion, kdeSettings));
982 kdeColor(pal, QPalette::LinkVisited, readKdeSetting(KdeSetting::ViewForegroundVisited, kdeDirs, kdeVersion, kdeSettings));
983 kdeColor(pal, QPalette::ToolTipBase, readKdeSetting(KdeSetting::TooltipBackground, kdeDirs, kdeVersion, kdeSettings));
984 kdeColor(pal, QPalette::ToolTipText, readKdeSetting(KdeSetting::TooltipForeground, kdeDirs, kdeVersion, kdeSettings));
985
986 // The above code sets _all_ color roles to "normal" colors. In KDE, the disabled
987 // color roles are calculated by applying various effects described in kdeglobals.
988 // We use a bit simpler approach here, similar logic than in qt_palette_from_color().
989 const QColor button = pal->color(QPalette::Button);
990 int h, s, v;
991 button.getHsv(&h, &s, &v);
992
993 const QBrush whiteBrush = QBrush(Qt::white);
994 const QBrush buttonBrush = QBrush(button);
995 const QBrush buttonBrushDark = QBrush(button.darker(v > 128 ? 200 : 50));
996 const QBrush buttonBrushDark150 = QBrush(button.darker(v > 128 ? 150 : 75));
997 const QBrush buttonBrushLight150 = QBrush(button.lighter(v > 128 ? 150 : 75));
998 const QBrush buttonBrushLight = QBrush(button.lighter(v > 128 ? 200 : 50));
999
1000 pal->setBrush(QPalette::Disabled, QPalette::WindowText, buttonBrushDark);
1001 pal->setBrush(QPalette::Disabled, QPalette::ButtonText, buttonBrushDark);
1002 pal->setBrush(QPalette::Disabled, QPalette::Button, buttonBrush);
1003 pal->setBrush(QPalette::Disabled, QPalette::Text, buttonBrushDark);
1005 pal->setBrush(QPalette::Disabled, QPalette::Base, buttonBrush);
1006 pal->setBrush(QPalette::Disabled, QPalette::Window, buttonBrush);
1007 pal->setBrush(QPalette::Disabled, QPalette::Highlight, buttonBrushDark150);
1008 pal->setBrush(QPalette::Disabled, QPalette::HighlightedText, buttonBrushLight150);
1009
1010 // set calculated colors for all groups
1011 pal->setBrush(QPalette::Light, buttonBrushLight);
1012 pal->setBrush(QPalette::Midlight, buttonBrushLight150);
1013 pal->setBrush(QPalette::Mid, buttonBrushDark150);
1014 pal->setBrush(QPalette::Dark, buttonBrushDark);
1015}
1016
1025const char *QKdeTheme::name = "kde";
1026
1027QKdeTheme::QKdeTheme(const QStringList& kdeDirs, int kdeVersion)
1028 : QPlatformTheme(new QKdeThemePrivate(kdeDirs,kdeVersion))
1029{
1030 d_func()->refresh();
1031}
1032
1033QFont *QKdeThemePrivate::kdeFont(const QVariant &fontValue)
1034{
1035 if (fontValue.isValid()) {
1036 // Read font value: Might be a QStringList as KDE stores fonts without quotes.
1037 // Also retrieve the family for the constructor since we cannot use the
1038 // default constructor of QFont, which accesses QGuiApplication::systemFont()
1039 // causing recursion.
1040 QString fontDescription;
1041 QString fontFamily;
1042 if (fontValue.userType() == QMetaType::QStringList) {
1043 const QStringList list = fontValue.toStringList();
1044 if (!list.isEmpty()) {
1045 fontFamily = list.first();
1046 fontDescription = list.join(u',');
1047 }
1048 } else {
1049 fontDescription = fontFamily = fontValue.toString();
1050 }
1051 if (!fontDescription.isEmpty()) {
1052 QFont font(fontFamily);
1053 if (font.fromString(fontDescription))
1054 return new QFont(font);
1055 }
1056 }
1057 return nullptr;
1058}
1059
1060
1061QStringList QKdeThemePrivate::kdeIconThemeSearchPaths(const QStringList &kdeDirs)
1062{
1064 const QString iconPath = QStringLiteral("/share/icons");
1065 for (const QString &candidate : kdeDirs) {
1066 const QFileInfo fi(candidate + iconPath);
1067 if (fi.isDir())
1068 paths.append(fi.absoluteFilePath());
1069 }
1070 return paths;
1071}
1072
1073QVariant QKdeTheme::themeHint(QPlatformTheme::ThemeHint hint) const
1074{
1075 Q_D(const QKdeTheme);
1076 switch (hint) {
1078 return QVariant(true);
1080 return QVariant(d->showIconsOnPushButtons);
1084 return QVariant(d->toolButtonStyle);
1086 return QVariant(d->toolBarIconSize);
1088 return QVariant(d->iconThemeName);
1090 return QVariant(d->iconFallbackThemeName);
1092 return QVariant(d->kdeIconThemeSearchPaths(d->kdeDirs));
1096 return QVariant(d->styleNames);
1098 return QVariant(int(KdeKeyboardScheme));
1100 return QVariant(d->singleClick);
1102 return QVariant(d->wheelScrollLines);
1104 return QVariant(d->doubleClickInterval);
1106 return QVariant(d->startDragTime);
1108 return QVariant(d->startDragDist);
1110 return QVariant(d->cursorBlinkRate);
1112 return QVariant(int(HoverEffect));
1114 return QVariant(mouseCursorTheme());
1116 return QVariant(mouseCursorSize());
1117 default:
1118 break;
1119 }
1121}
1122
1123QIcon QKdeTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions) const
1124{
1125#if QT_CONFIG(mimetype)
1126 return xdgFileIcon(fileInfo);
1127#else
1128 Q_UNUSED(fileInfo);
1129 return QIcon();
1130#endif
1131}
1132
1133Qt::ColorScheme QKdeTheme::colorScheme() const
1134{
1135 return d_func()->m_colorScheme;
1136}
1137
1149void QKdeThemePrivate::updateColorScheme(const QString &themeName)
1150{
1151 if (themeName.contains(QLatin1StringView("light"), Qt::CaseInsensitive)) {
1152 m_colorScheme = Qt::ColorScheme::Light;
1153 return;
1154 }
1155 if (themeName.contains(QLatin1StringView("dark"), Qt::CaseInsensitive)) {
1156 m_colorScheme = Qt::ColorScheme::Dark;
1157 return;
1158 }
1159
1160 if (systemPalette) {
1161 if (systemPalette->text().color().lightness() < systemPalette->base().color().lightness()) {
1162 m_colorScheme = Qt::ColorScheme::Light;
1163 return;
1164 }
1165 if (systemPalette->text().color().lightness() > systemPalette->base().color().lightness()) {
1166 m_colorScheme = Qt::ColorScheme::Dark;
1167 return;
1168 }
1169 }
1170
1171 m_colorScheme = Qt::ColorScheme::Unknown;
1172}
1173
1174const QPalette *QKdeTheme::palette(Palette type) const
1175{
1176 Q_D(const QKdeTheme);
1177 return d->resources.palettes[type];
1178}
1179
1180const QFont *QKdeTheme::font(Font type) const
1181{
1182 Q_D(const QKdeTheme);
1183 return d->resources.fonts[type];
1184}
1185
1186QPlatformTheme *QKdeTheme::createKdeTheme()
1187{
1188 const QByteArray kdeVersionBA = qgetenv("KDE_SESSION_VERSION");
1189 const int kdeVersion = kdeVersionBA.toInt();
1190 if (kdeVersion < 4)
1191 return nullptr;
1192
1193 if (kdeVersion > 4)
1194 // Plasma 5 follows XDG spec
1195 // but uses the same config file format:
1197
1198 // Determine KDE prefixes in the following priority order:
1199 // - KDEHOME and KDEDIRS environment variables
1200 // - ~/.kde(<version>)
1201 // - read prefixes from /etc/kde<version>rc
1202 // - fallback to /etc/kde<version>
1203
1204 QStringList kdeDirs;
1205 const QString kdeHomePathVar = QFile::decodeName(qgetenv("KDEHOME"));
1206 if (!kdeHomePathVar.isEmpty())
1207 kdeDirs += kdeHomePathVar;
1208
1209 const QString kdeDirsVar = QFile::decodeName(qgetenv("KDEDIRS"));
1210 if (!kdeDirsVar.isEmpty())
1211 kdeDirs += kdeDirsVar.split(u':', Qt::SkipEmptyParts);
1212
1213 const QString kdeVersionHomePath = QDir::homePath() + "/.kde"_L1 + QLatin1StringView(kdeVersionBA);
1214 if (QFileInfo(kdeVersionHomePath).isDir())
1215 kdeDirs += kdeVersionHomePath;
1216
1217 const QString kdeHomePath = QDir::homePath() + "/.kde"_L1;
1218 if (QFileInfo(kdeHomePath).isDir())
1219 kdeDirs += kdeHomePath;
1220
1221 const QString kdeRcPath = "/etc/kde"_L1 + QLatin1StringView(kdeVersionBA) + "rc"_L1;
1222 if (QFileInfo(kdeRcPath).isReadable()) {
1223 QSettings kdeSettings(kdeRcPath, QSettings::IniFormat);
1224 kdeSettings.beginGroup(QStringLiteral("Directories-default"));
1225 kdeDirs += kdeSettings.value(QStringLiteral("prefixes")).toStringList();
1226 }
1227
1228 const QString kdeVersionPrefix = "/etc/kde"_L1 + QLatin1StringView(kdeVersionBA);
1229 if (QFileInfo(kdeVersionPrefix).isDir())
1230 kdeDirs += kdeVersionPrefix;
1231
1232 kdeDirs.removeDuplicates();
1233 if (kdeDirs.isEmpty()) {
1234 qWarning("Unable to determine KDE dirs");
1235 return nullptr;
1236 }
1237
1238 return new QKdeTheme(kdeDirs, kdeVersion);
1239}
1240
1241#ifndef QT_NO_DBUS
1242QPlatformMenuBar *QKdeTheme::createPlatformMenuBar() const
1243{
1245 return new QDBusMenuBar();
1246 return nullptr;
1247}
1248#endif
1249
1250#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
1251QPlatformSystemTrayIcon *QKdeTheme::createPlatformSystemTrayIcon() const
1252{
1253 if (isDBusTrayAvailable())
1254 return new QDBusTrayIcon();
1255 return nullptr;
1256}
1257#endif
1258
1259#endif // settings
1260
1269const char *QGnomeTheme::name = "gnome";
1270
1272{
1273public:
1276
1277 void configureFonts(const QString &gtkFontName) const
1278 {
1279 Q_ASSERT(!systemFont);
1280 const int split = gtkFontName.lastIndexOf(QChar::Space);
1281 float size = QStringView{gtkFontName}.mid(split + 1).toFloat();
1282 QString fontName = gtkFontName.left(split);
1283
1284 systemFont = new QFont(fontName, size);
1285 fixedFont = new QFont(QLatin1StringView(defaultFixedFontNameC), systemFont->pointSize());
1286 fixedFont->setStyleHint(QFont::TypeWriter);
1287 qCDebug(lcQpaFonts) << "default fonts: system" << systemFont << "fixed" << fixedFont;
1288 }
1289
1290 mutable QFont *systemFont = nullptr;
1291 mutable QFont *fixedFont = nullptr;
1292
1293#ifndef QT_NO_DBUS
1295private:
1296 std::unique_ptr<QGenericUnixThemeDBusListener> dbus;
1297 bool initDbus();
1298 void updateColorScheme(const QString &themeName);
1299#endif // QT_NO_DBUS
1300};
1301
1303{
1304#ifndef QT_NO_DBUS
1305 initDbus();
1306#endif // QT_NO_DBUS
1307}
1309{
1310 if (systemFont)
1311 delete systemFont;
1312 if (fixedFont)
1313 delete fixedFont;
1314}
1315
1316#ifndef QT_NO_DBUS
1317bool QGnomeThemePrivate::initDbus()
1318{
1320 Q_ASSERT(dbus);
1321
1322 // Wrap slot in a lambda to avoid inheriting QGnomeThemePrivate from QObject
1323 auto wrapper = [this](QGenericUnixThemeDBusListener::Provider provider,
1325 const QString &value) {
1328 return;
1329 }
1330
1332 updateColorScheme(value);
1333 };
1334
1336}
1337
1338void QGnomeThemePrivate::updateColorScheme(const QString &themeName)
1339{
1340 const auto oldColorScheme = m_colorScheme;
1341 if (themeName.contains(QLatin1StringView("light"), Qt::CaseInsensitive)) {
1342 m_colorScheme = Qt::ColorScheme::Light;
1343 } else if (themeName.contains(QLatin1StringView("dark"), Qt::CaseInsensitive)) {
1344 m_colorScheme = Qt::ColorScheme::Dark;
1345 } else {
1346 m_colorScheme = Qt::ColorScheme::Unknown;
1347 }
1348
1349 if (oldColorScheme != m_colorScheme)
1351}
1352#endif // QT_NO_DBUS
1353
1358
1360{
1361 switch (hint) {
1363 return QVariant(true);
1367 return QVariant(QStringLiteral("Adwaita"));
1369 return QVariant(QStringLiteral("gnome"));
1376 styleNames << QStringLiteral("Fusion") << QStringLiteral("windows");
1377 return QVariant(styleNames);
1378 }
1380 return QVariant(int(GnomeKeyboardScheme));
1382 return QVariant(QChar(0x2022));
1384 return QVariant(int(HoverEffect));
1386 return QVariant::fromValue(
1389 return true;
1391 return QVariant(mouseCursorTheme());
1393 return QVariant(mouseCursorSize());
1394 default:
1395 break;
1396 }
1398}
1399
1400QIcon QGnomeTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions) const
1401{
1402#if QT_CONFIG(mimetype)
1403 return xdgFileIcon(fileInfo);
1404#else
1405 Q_UNUSED(fileInfo);
1406 return QIcon();
1407#endif
1408}
1409
1411{
1412 Q_D(const QGnomeTheme);
1413 if (!d->systemFont)
1414 d->configureFonts(gtkFontName());
1415 switch (type) {
1417 return d->systemFont;
1419 return d->fixedFont;
1420 default:
1421 return nullptr;
1422 }
1423}
1424
1429
1430#ifndef QT_NO_DBUS
1432{
1434 return new QDBusMenuBar();
1435 return nullptr;
1436}
1437
1439{
1440 return d_func()->m_colorScheme;
1441}
1442
1443#endif
1444
1445#if !defined(QT_NO_DBUS) && !defined(QT_NO_SYSTEMTRAYICON)
1447{
1448 if (isDBusTrayAvailable())
1449 return new QDBusTrayIcon();
1450 return nullptr;
1451}
1452#endif
1453
1455{
1456 switch (button) {
1458 return QCoreApplication::translate("QGnomeTheme", "&OK");
1460 return QCoreApplication::translate("QGnomeTheme", "&Save");
1462 return QCoreApplication::translate("QGnomeTheme", "&Cancel");
1464 return QCoreApplication::translate("QGnomeTheme", "&Close");
1466 return QCoreApplication::translate("QGnomeTheme", "Close without Saving");
1467 default:
1468 break;
1469 }
1471}
1472
1478{
1480 return new QGenericUnixTheme;
1481#if QT_CONFIG(settings)
1482 if (name == QLatin1StringView(QKdeTheme::name))
1483 if (QPlatformTheme *kdeTheme = QKdeTheme::createKdeTheme())
1484 return kdeTheme;
1485#endif
1487 return new QGnomeTheme;
1488 return nullptr;
1489}
1490
1492{
1495 const QByteArray desktopEnvironment = QGuiApplicationPrivate::platformIntegration()->services()->desktopEnvironment();
1496 QList<QByteArray> gtkBasedEnvironments;
1497 gtkBasedEnvironments << "GNOME"
1498 << "X-CINNAMON"
1499 << "UNITY"
1500 << "MATE"
1501 << "XFCE"
1502 << "LXDE";
1503 const QList<QByteArray> desktopNames = desktopEnvironment.split(':');
1504 for (const QByteArray &desktopName : desktopNames) {
1505 if (desktopEnvironment == "KDE") {
1506#if QT_CONFIG(settings)
1507 result.push_back(QLatin1StringView(QKdeTheme::name));
1508#endif
1509 } else if (gtkBasedEnvironments.contains(desktopName)) {
1510 // prefer the GTK3 theme implementation with native dialogs etc.
1511 result.push_back(QStringLiteral("gtk3"));
1512 // fallback to the generic Gnome theme if loading the GTK3 theme fails
1514 } else {
1515 // unknown, but lowercase the name (our standard practice) and
1516 // remove any "x-" prefix
1517 QString s = QString::fromLatin1(desktopName.toLower());
1518 result.push_back(s.startsWith("x-"_L1) ? s.mid(2) : s);
1519 }
1520 }
1521 } // desktopSettingsAware
1523 return result;
1524}
1525
1527
1528#ifndef QT_NO_DBUS
1529#include "qgenericunixthemes.moc"
1530#endif // QT_NO_DBUS
\inmodule QtGui
Definition qbrush.h:30
const QColor & color() const
Returns the brush color.
Definition qbrush.h:121
\inmodule QtCore
Definition qbytearray.h:57
QList< QByteArray > split(char sep) const
Splits the byte array into subarrays wherever sep occurs, and returns the list of those arrays.
\inmodule QtCore
The QColor class provides colors based on RGB, HSV or CMYK values.
Definition qcolor.h:31
int lightness() const noexcept
Definition qcolor.cpp:1860
static QString translate(const char *context, const char *key, const char *disambiguation=nullptr, int n=-1)
\threadsafe
\inmodule QtDBus
static QDBusConnection sessionBus()
Returns a QDBusConnection object opened with the session bus.
\inmodule QtDBus
static QString homePath()
Returns the absolute path of the user's home directory.
Definition qdir.cpp:2103
void close() override
Calls QFileDevice::flush() and closes the file.
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
static QString decodeName(const QByteArray &localFileName)
This does the reverse of QFile::encodeName() using localFileName.
Definition qfile.h:162
bool exists() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qfile.cpp:351
bool isEmpty() const noexcept
Definition qflatmap_p.h:579
size_type count() const noexcept
Definition qflatmap_p.h:576
bool contains(const Key &key) const
Definition qflatmap_p.h:625
const_iterator constBegin() const
Definition qflatmap_p.h:772
const_iterator constEnd() const
Definition qflatmap_p.h:776
std::pair< iterator, bool > insert(const Key &key, const T &value)
Definition qflatmap_p.h:678
T value(const Key &key, const T &defaultValue) const
Definition qflatmap_p.h:636
void clear()
Definition qflatmap_p.h:591
\reentrant
Definition qfont.h:22
@ TypeWriter
Definition qfont.h:28
bool fromString(const QString &)
Sets this font to match the description descrip.
Definition qfont.cpp:2190
void setStyleHint(StyleHint, StyleStrategy=PreferDefault)
Sets the style hint and strategy to hint and strategy, respectively.
Definition qfont.cpp:1505
void settingChanged(QGenericUnixThemeDBusListener::Provider provider, QGenericUnixThemeDBusListener::Setting setting, const QString &value)
static const char * name
QVariant themeHint(ThemeHint hint) const override
static QStringList iconFallbackPaths()
const QFont * font(Font type) const override
QPlatformMenuBar * createPlatformMenuBar() const override
static QPlatformTheme * createUnixTheme(const QString &name)
Creates a UNIX theme according to the detected desktop environment.
QPlatformSystemTrayIcon * createPlatformSystemTrayIcon() const override
Factory function for QSystemTrayIcon.
static QStringList xdgIconThemePaths()
static QStringList themeNames()
void configureFonts(const QString &gtkFontName) const
QGnomeTheme is a theme implementation for the Gnome desktop.
QPlatformMenuBar * createPlatformMenuBar() const override
virtual QString gtkFontName() const
QVariant themeHint(ThemeHint hint) const override
QPlatformSystemTrayIcon * createPlatformSystemTrayIcon() const override
Factory function for QSystemTrayIcon.
QIcon fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions={ }) const override
Return an icon for fileInfo, observing iconOptions.
Qt::ColorScheme colorScheme() const override
QString standardButtonText(int button) const override
Returns the text of a standard button.
static const char * name
const QFont * font(Font type) const override
static QPlatformIntegration * platformIntegration()
static bool desktopSettingsAware()
Returns true if Qt is set to use the system's standard colors, fonts, etc.; otherwise returns false.
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
The QIcon class provides scalable icons in different modes and states.
Definition qicon.h:20
bool isNull() const
Returns true if the icon is empty; otherwise returns false.
Definition qicon.cpp:1019
static QIcon fromTheme(const QString &name)
Definition qicon.cpp:1344
\inmodule QtCore\reentrant
Definition qjsonarray.h:18
const_iterator constBegin() const
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the first item in the array.
Definition qjsonarray.h:258
const_iterator constEnd() const
Returns a const \l{STL-style iterators}{STL-style iterator} pointing to the imaginary item after the ...
Definition qjsonarray.h:262
qsizetype count() const
Same as size().
Definition qjsonarray.h:42
void append(const QJsonValue &value)
Inserts value at the end of the array.
\inmodule QtCore\reentrant
QByteArray toJson(JsonFormat format=Indented) const
QJsonObject object() const
Returns the QJsonObject contained in the document.
bool isObject() const
Returns true if the document contains an object.
static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error=nullptr)
Parses json as a UTF-8 encoded JSON document, and creates a QJsonDocument from it.
\inmodule QtCore\reentrant
Definition qjsonobject.h:20
bool contains(const QString &key) const
Returns true if the object contains key key.
bool isEmpty() const noexcept
Definition qlist.h:401
T & first()
Definition qlist.h:645
void push_back(parameter_type t)
Definition qlist.h:675
\inmodule QtCore
\inmodule QtCore
Definition qmimetype.h:25
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
The QPalette class contains color groups for each widget state.
Definition qpalette.h:19
const QBrush & text() const
Returns the text foreground brush of the current color group.
Definition qpalette.h:88
const QColor & color(ColorGroup cg, ColorRole cr) const
Returns the color in the specified color group, used for the given color role.
Definition qpalette.h:67
void setBrush(ColorRole cr, const QBrush &brush)
Sets the brush for the given color role to the specified brush for all groups in the palette.
Definition qpalette.h:151
@ Disabled
Definition qpalette.h:49
const QBrush & base() const
Returns the base brush of the current color group.
Definition qpalette.h:89
@ HighlightedText
Definition qpalette.h:53
@ ToolTipBase
Definition qpalette.h:57
@ BrightText
Definition qpalette.h:52
@ AlternateBase
Definition qpalette.h:55
@ ButtonText
Definition qpalette.h:52
@ WindowText
Definition qpalette.h:51
@ Highlight
Definition qpalette.h:53
@ ToolTipText
Definition qpalette.h:57
@ Midlight
Definition qpalette.h:51
@ LinkVisited
Definition qpalette.h:54
The QPlatformTheme class allows customizing the UI based on themes.
virtual QVariant themeHint(ThemeHint hint) const
virtual QString standardButtonText(int button) const
Returns the text of a standard button.
ThemeHint
This enum describes the available theme hints.
@ DialogButtonBoxButtonsHaveIcons
@ ItemViewActivateItemOnSingleClick
\inmodule QtCore
Definition qsettings.h:30
QVariant value(QAnyStringView key, const QVariant &defaultValue) const
Returns the value for setting key.
\inmodule QtCore
Definition qsize.h:25
static QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options=LocateFile)
[0]
static QStringList standardLocations(StandardLocation type)
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString left(qsizetype n) const &
Definition qstring.h:363
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:296
static QString fromLatin1(QByteArrayView ba)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5871
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
float toFloat(bool *ok=nullptr) const
Returns the string converted to a float value.
Definition qstring.cpp:7950
\inmodule QtCore
Definition qvariant.h:65
bool isValid() const
Returns true if the storage type of this variant is not QMetaType::UnknownType; otherwise returns fal...
Definition qvariant.h:714
QString toString() const
Returns the variant as a QString if the variant has a userType() including, but not limited to:
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:536
static void handleThemeChange(QWindow *window=nullptr)
QPalette * palettes[QPlatformTheme::NPalettes]
QFont * fonts[QPlatformTheme::NFonts]
QPushButton * button
[2]
qDeleteAll(list.begin(), list.end())
QSet< QString >::iterator it
auto signal
Combined button and popup list for selecting options.
ColorScheme
Definition qnamespace.h:50
@ white
Definition qnamespace.h:31
@ Key_Select
@ Key_Return
Definition qnamespace.h:667
@ Key_Enter
Definition qnamespace.h:668
@ Key_Space
Definition qnamespace.h:513
@ CaseInsensitive
@ SkipEmptyParts
Definition qnamespace.h:128
ToolButtonStyle
@ ToolButtonTextOnly
@ ToolButtonTextUnderIcon
@ ToolButtonTextBesideIcon
#define CASE(Enum, Size)
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char * interface
DBusConnection const char DBusError * error
DBusConnection * connection
Colors
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
const char * mimeType
@ defaultSystemFontSize
#define LOG
static bool isDBusGlobalMenuAvailable()
#define CHECK(cond, warning)
static const char defaultFixedFontNameC[]
static const char defaultSystemFontNameC[]
static QString mouseCursorTheme()
static bool isDBusTrayAvailable()
static QList< QSize > availableXdgFileIconSizes()
#define PARSE(var, enumeration, string)
static bool checkDBusGlobalMenuAvailable()
static QSize mouseCursorSize()
#define qDebug
[1]
Definition qlogging.h:164
#define qWarning
Definition qlogging.h:166
#define Q_LOGGING_CATEGORY(name,...)
#define qCInfo(category,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
return ret
constexpr const T & qBound(const T &min, const T &val, const T &max)
Definition qminmax.h:44
#define SLOT(a)
Definition qobjectdefs.h:52
static QString themeName()
GLint location
GLenum GLsizei GLsizei GLint * values
[15]
GLsizei const GLfloat * v
[13]
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLenum GLsizei count
GLenum type
GLsizei const GLuint * paths
GLenum const void * fontName
GLuint name
GLfloat GLfloat GLfloat GLfloat h
GLhandleARB obj
[2]
GLdouble s
[6]
Definition qopenglext.h:235
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static void split(QT_FT_Vector *b)
#define QStringLiteral(str)
static QT_BEGIN_NAMESPACE QVariant hint(QPlatformIntegration::StyleHint h)
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
return device isReadable() ? device -> read(static_cast< char * >(buf), size) :-1
#define Q_ENUM(x)
#define Q_OBJECT
#define Q_SLOTS
#define Q_SIGNALS
#define emit
#define Q_UNUSED(x)
static QStringList styleNames()
QList< int > list
[14]
QFile file
[0]
QSettings settings("MySoft", "Star Runner")
[0]
QSharedPointer< T > other(t)
[5]
void dbus()
[0-0]
\inmodule QtCore\reentrant
void wrapper()