Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qkdetheme.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 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// Qt-Security score:significant reason:default
4
5#include "qkdetheme_p.h"
6#include <qpa/qplatformtheme_p.h>
7#include <qpa/qplatformfontdatabase.h>
8#include <qpa/qplatformdialoghelper.h>
9#include <QPalette>
10#include <qpa/qwindowsysteminterface.h>
12#if QT_CONFIG(dbus) && QT_CONFIG(systemtrayicon)
13#include <private/qdbustrayicon_p.h>
14#endif
15#include <private/qdbusplatformmenu_p.h>
16#include <private/qdbusmenubar_p.h>
17#include <QSettings>
18#include <QStandardPaths>
19
21
22using namespace Qt::StringLiterals;
23
24Q_STATIC_LOGGING_CATEGORY(lcQpaThemeKde, "qt.qpa.theme.kde")
25
27{
28
29public:
39
72
73 QKdeThemePrivate(const QStringList &kdeDirs, int kdeVersion);
74 ~QKdeThemePrivate() { clearResources(); }
75
76 static QString kdeGlobals(const QString &kdeDir, int kdeVersion)
77 {
78 if (kdeVersion > 4)
79 return kdeDir + "/kdeglobals"_L1;
80 return kdeDir + "/share/config/kdeglobals"_L1;
81 }
82
83 void refresh();
84 static QVariant readKdeSetting(KdeSetting s, const QStringList &kdeDirs, int kdeVersion, QHash<QString, QSettings*> &settings);
86 void clearKdeSettings() const;
87 static void readKdeSystemPalette(const QStringList &kdeDirs, int kdeVersion, QHash<QString, QSettings*> &kdeSettings, QPalette *pal);
88 static QFont *kdeFont(const QVariant &fontValue);
89 static QStringList kdeIconThemeSearchPaths(const QStringList &kdeDirs);
90
92 const int kdeVersion;
93
99 bool singleClick = true;
104 int startDragTime = 500;
105 int cursorBlinkRate = 1000;
110 void updateColorScheme(const QString &themeName);
111 bool hasRequestedColorScheme() const { return m_requestedColorScheme != Qt::ColorScheme::Unknown
112 && m_requestedColorScheme != m_colorScheme; }
113
114private:
115 mutable QHash<QString, QSettings *> kdeSettings;
116#if QT_CONFIG(dbus)
118 bool initDbus();
121 const QVariant &value);
123#endif // QT_CONFIG(dbus)
124 void clearResources();
125};
126
127#if QT_CONFIG(dbus)
128void QKdeThemePrivate::settingChangedHandler(QDBusListener::Provider provider,
129 QDBusListener::Setting setting,
130 const QVariant &value)
131{
132 if (provider != QDBusListener::Provider::Kde)
133 return;
134
135 switch (setting) {
136 case QDBusListener::Setting::ColorScheme:
137 qCDebug(lcQpaThemeKde) << "KDE color theme changed to:" << value.value<Qt::ColorScheme>();
138 break;
139 case QDBusListener::Setting::Theme:
140 qCDebug(lcQpaThemeKde) << "KDE global theme changed to:" << value.toString();
141 break;
142 case QDBusListener::Setting::ApplicationStyle:
143 qCDebug(lcQpaThemeKde) << "KDE application style changed to:" << value.toString();
144 break;
145 case QDBusListener::Setting::Contrast:
146 qCDebug(lcQpaThemeKde) << "KDE contrast setting changed to: "
147 << value.value<Qt::ContrastPreference>();
148 break;
149 case QDBusListener::Setting::Motion:
150 qCDebug(lcQpaThemeKde) << "KDE motion setting changed to: "
151 << value.value<Qt::MotionPreference>();
152 break;
153 }
154
155 refresh();
156}
157
158void QKdeThemePrivate::clearResources()
159{
160 qDeleteAll(fonts, fonts + QPlatformTheme::NFonts);
161 std::fill(fonts, fonts + QPlatformTheme::NFonts, static_cast<QFont *>(nullptr));
162 systemPalette.reset();
163}
164
165bool QKdeThemePrivate::initDbus()
166{
167 dbus.reset(new QDBusListener());
168 Q_ASSERT(dbus);
169
170 // Wrap slot in a lambda to avoid inheriting QKdeThemePrivate from QObject
171 auto wrapper = [this](QDBusListener::Provider provider,
172 QDBusListener::Setting setting,
173 const QVariant &value) {
174 settingChangedHandler(provider, setting, value);
175 };
176
177 return QObject::connect(dbus.get(), &QDBusListener::settingChanged, dbus.get(), wrapper);
178}
179#endif // QT_CONFIG(dbus)
180
181QKdeThemePrivate::QKdeThemePrivate(const QStringList &kdeDirs, int kdeVersion)
182 : kdeDirs(kdeDirs), kdeVersion(kdeVersion)
183{
184 std::fill(fonts, fonts + QPlatformTheme::NFonts, static_cast<QFont *>(nullptr));
185#if QT_CONFIG(dbus)
186 initDbus();
187#endif // QT_CONFIG(dbus)
188}
189
191{
192 switch (type) {
194 return QLatin1StringView();
196 return QLatin1StringView("KDE/");
198 return QLatin1StringView();
200 return QLatin1StringView("Colors:");
202 return QLatin1StringView("Icons/");
204 return QLatin1StringView("ToolbarIcons/");
206 return QLatin1StringView("Toolbar style/");
207 }
208 // GCC 8.x does not treat __builtin_unreachable() as constexpr
209# if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
210 // NOLINTNEXTLINE(qt-use-unreachable-return): Triggers on Clang, breaking GCC 8
211 Q_UNREACHABLE();
212# endif
213 return {};
214}
215
217{
218#define CASE(s, type) case QKdeThemePrivate::KdeSetting::s:
219 return QKdeThemePrivate::KdeSettingType::type
220
221 switch (setting) {
234 CASE(Font, Root);
235 CASE(Fixed, Root);
252 };
253 // GCC 8.x does not treat __builtin_unreachable() as constexpr
254# if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
255 // NOLINTNEXTLINE(qt-use-unreachable-return): Triggers on Clang, breaking GCC 8
256 Q_UNREACHABLE();
257# endif
259}
260#undef CASE
261
263{
264 switch (setting) {
266 return QLatin1StringView("widgetStyle");
268 return QLatin1StringView("ColorScheme");
270 return QLatin1StringView("SingleClick");
272 return QLatin1StringView("ShowIconsOnPushButtons");
274 return QLatin1StringView("Theme");
276 return QLatin1StringView("Size");
278 return QLatin1StringView("ToolButtonStyle");
280 return QLatin1StringView("WheelScrollLines");
282 return QLatin1StringView("DoubleClickInterval");
284 return QLatin1StringView("StartDragDist");
286 return QLatin1StringView("StartDragTime");
288 return QLatin1StringView("CursorBlinkRate");
290 return QLatin1StringView("font");
292 return QLatin1StringView("fixed");
294 return QLatin1StringView("menuFont");
296 return QLatin1StringView("toolBarFont");
298 return QLatin1StringView("Button/BackgroundNormal");
300 return QLatin1StringView("Window/BackgroundNormal");
302 return QLatin1StringView("View/ForegroundNormal");
304 return QLatin1StringView("View/ForegroundInactive");
306 return QLatin1StringView("Window/ForegroundNormal");
308 return QLatin1StringView("View/BackgroundNormal");
310 return QLatin1StringView("Selection/BackgroundNormal");
312 return QLatin1StringView("Selection/ForegroundNormal");
314 return QLatin1StringView("View/BackgroundAlternate");
316 return QLatin1StringView("Button/ForegroundNormal");
318 return QLatin1StringView("View/ForegroundLink");
320 return QLatin1StringView("View/ForegroundVisited");
322 return QLatin1StringView("Tooltip/BackgroundNormal");
324 return QLatin1StringView("Tooltip/ForegroundNormal");
325 };
326 // GCC 8.x does not treat __builtin_unreachable() as constexpr
327# if !defined(Q_CC_GNU_ONLY) || (Q_CC_GNU >= 900)
328 // NOLINTNEXTLINE(qt-use-unreachable-return): Triggers on Clang, breaking GCC 8
329 Q_UNREACHABLE();
330# endif
331 return {};
332}
333
335{
336 clearResources();
338
339 toolButtonStyle = Qt::ToolButtonTextBesideIcon;
340 toolBarIconSize = 0;
341 styleNames.clear();
342 if (kdeVersion >= 5)
343 styleNames << QStringLiteral("breeze");
344 styleNames << QStringLiteral("Oxygen") << QStringLiteral("Fusion") << QStringLiteral("windows");
345 if (kdeVersion >= 5)
346 iconFallbackThemeName = iconThemeName = QStringLiteral("breeze");
347 else
348 iconFallbackThemeName = iconThemeName = QStringLiteral("oxygen");
349
350 systemPalette.reset(new QPalette(QPalette()));
351 readKdeSystemPalette(kdeDirs, kdeVersion, kdeSettings, systemPalette.get());
352
353 const QVariant styleValue = readKdeSetting(KdeSetting::WidgetStyle);
354 if (styleValue.isValid()) {
355 const QString style = styleValue.toString();
356 if (style != styleNames.front())
357 styleNames.push_front(style);
358 }
359
360 const QVariant colorScheme = readKdeSetting(KdeSetting::ColorScheme);
361
362 updateColorScheme(colorScheme.toString());
363
364 const QVariant singleClickValue = readKdeSetting(KdeSetting::SingleClick);
365 if (singleClickValue.isValid())
366 singleClick = singleClickValue.toBool();
367 else if (kdeVersion >= 6) // Plasma 6 defaults to double-click
368 singleClick = false;
369 else // earlier version to single-click
370 singleClick = true;
371
372 const QVariant showIconsOnPushButtonsValue = readKdeSetting(KdeSetting::ShowIconsOnPushButtons);
373 if (showIconsOnPushButtonsValue.isValid())
374 showIconsOnPushButtons = showIconsOnPushButtonsValue.toBool();
375
376 const QVariant themeValue = readKdeSetting(KdeSetting::IconTheme);
377 if (themeValue.isValid())
378 iconThemeName = themeValue.toString();
379
380 const QVariant toolBarIconSizeValue = readKdeSetting(KdeSetting::ToolBarIconSize);
381 if (toolBarIconSizeValue.isValid())
382 toolBarIconSize = toolBarIconSizeValue.toInt();
383
384 const QVariant toolbarStyleValue = readKdeSetting(KdeSetting::ToolButtonStyle);
385 if (toolbarStyleValue.isValid()) {
386 const QString toolBarStyle = toolbarStyleValue.toString();
387 if (toolBarStyle == "TextBesideIcon"_L1)
388 toolButtonStyle = Qt::ToolButtonTextBesideIcon;
389 else if (toolBarStyle == "TextOnly"_L1)
390 toolButtonStyle = Qt::ToolButtonTextOnly;
391 else if (toolBarStyle == "TextUnderIcon"_L1)
392 toolButtonStyle = Qt::ToolButtonTextUnderIcon;
393 }
394
395 const QVariant wheelScrollLinesValue = readKdeSetting(KdeSetting::WheelScrollLines);
396 if (wheelScrollLinesValue.isValid())
397 wheelScrollLines = wheelScrollLinesValue.toInt();
398
399 const QVariant doubleClickIntervalValue = readKdeSetting(KdeSetting::DoubleClickInterval);
400 if (doubleClickIntervalValue.isValid())
401 doubleClickInterval = doubleClickIntervalValue.toInt();
402
403 const QVariant startDragDistValue = readKdeSetting(KdeSetting::StartDragDistance);
404 if (startDragDistValue.isValid())
405 startDragDist = startDragDistValue.toInt();
406
407 const QVariant startDragTimeValue = readKdeSetting(KdeSetting::StartDragTime);
408 if (startDragTimeValue.isValid())
409 startDragTime = startDragTimeValue.toInt();
410
411 const QVariant cursorBlinkRateValue = readKdeSetting(KdeSetting::CursorBlinkRate);
412 if (cursorBlinkRateValue.isValid()) {
413 cursorBlinkRate = cursorBlinkRateValue.toInt();
414 cursorBlinkRate = cursorBlinkRate > 0 ? qBound(200, cursorBlinkRate, 2000) : 0;
415 }
416
417 // Read system font, ignore 'smallestReadableFont'
418 if (QFont *systemFont = kdeFont(readKdeSetting(KdeSetting::Font)))
419 fonts[QPlatformTheme::SystemFont] = systemFont;
420 else
421 fonts[QPlatformTheme::SystemFont] = new QFont(QLatin1StringView(QGenericUnixTheme::defaultSystemFontNameC),
422 QGenericUnixTheme::defaultSystemFontSize);
423
424 if (QFont *fixedFont = kdeFont(readKdeSetting(KdeSetting::Fixed))) {
425 fonts[QPlatformTheme::FixedFont] = fixedFont;
426 } else {
427 fixedFont = new QFont(QLatin1StringView(QGenericUnixTheme::defaultFixedFontNameC),
428 QGenericUnixTheme::defaultSystemFontSize);
429 fixedFont->setStyleHint(QFont::TypeWriter);
430 fonts[QPlatformTheme::FixedFont] = fixedFont;
431 }
432
433 if (QFont *menuFont = kdeFont(readKdeSetting(KdeSetting::MenuFont))) {
434 fonts[QPlatformTheme::MenuFont] = menuFont;
435 fonts[QPlatformTheme::MenuBarFont] = new QFont(*menuFont);
436 }
437
438 if (QFont *toolBarFont = kdeFont(readKdeSetting(KdeSetting::ToolBarFont)))
439 fonts[QPlatformTheme::ToolButtonFont] = toolBarFont;
440
441 QWindowSystemInterface::handleThemeChange();
442
443 qCDebug(lcQpaFonts) << "default fonts: system" << fonts[QPlatformTheme::SystemFont]
444 << "fixed" << fonts[QPlatformTheme::FixedFont];
445 qDeleteAll(kdeSettings);
446}
447
448QVariant QKdeThemePrivate::readKdeSetting(KdeSetting s, const QStringList &kdeDirs, int kdeVersion, QHash<QString, QSettings*> &kdeSettings)
449{
450 for (const QString &kdeDir : kdeDirs) {
451 QSettings *settings = kdeSettings.value(kdeDir);
452 if (!settings) {
453 const QString kdeGlobalsPath = kdeGlobals(kdeDir, kdeVersion);
454 if (QFileInfo(kdeGlobalsPath).isReadable()) {
455 settings = new QSettings(kdeGlobalsPath, QSettings::IniFormat);
456 kdeSettings.insert(kdeDir, settings);
457 }
458 }
459 if (settings) {
460 const QString key = settingsPrefix(settingsType(s)) + settingsKey(s);
461 const QVariant value = settings->value(key);
462 if (value.isValid())
463 return value;
464 }
465 }
466 return QVariant();
467}
468
470{
471 return readKdeSetting(s, kdeDirs, kdeVersion, kdeSettings);
472}
473
475{
476 kdeSettings.clear();
477}
478
479// Reads the color from the KDE configuration, and store it in the
480// palette with the given color role if found.
481static inline bool kdeColor(QPalette *pal, QPalette::ColorRole role, const QVariant &value)
482{
483 if (!value.isValid())
484 return false;
485 const QStringList values = value.toStringList();
486 if (values.size() != 3)
487 return false;
488 pal->setBrush(role, QColor(values.at(0).toInt(), values.at(1).toInt(), values.at(2).toInt()));
489 return true;
490}
491
492void QKdeThemePrivate::readKdeSystemPalette(const QStringList &kdeDirs, int kdeVersion, QHash<QString, QSettings*> &kdeSettings, QPalette *pal)
493{
494 if (!kdeColor(pal, QPalette::Button, readKdeSetting(KdeSetting::ButtonBackground, kdeDirs, kdeVersion, kdeSettings))) {
495 // kcolorscheme.cpp: SetDefaultColors
496 const QColor defaultWindowBackground(214, 210, 208);
497 const QColor defaultButtonBackground(223, 220, 217);
498 *pal = QPalette(defaultButtonBackground, defaultWindowBackground);
499 return;
500 }
501
502 kdeColor(pal, QPalette::Window, readKdeSetting(KdeSetting::WindowBackground, kdeDirs, kdeVersion, kdeSettings));
503 kdeColor(pal, QPalette::Text, readKdeSetting(KdeSetting::ViewForeground, kdeDirs, kdeVersion, kdeSettings));
504 kdeColor(pal, QPalette::WindowText, readKdeSetting(KdeSetting::WindowForeground, kdeDirs, kdeVersion, kdeSettings));
505 kdeColor(pal, QPalette::Base, readKdeSetting(KdeSetting::ViewBackground, kdeDirs, kdeVersion, kdeSettings));
506 kdeColor(pal, QPalette::Highlight, readKdeSetting(KdeSetting::SelectionBackground, kdeDirs, kdeVersion, kdeSettings));
507 kdeColor(pal, QPalette::HighlightedText, readKdeSetting(KdeSetting::SelectionForeground, kdeDirs, kdeVersion, kdeSettings));
508 kdeColor(pal, QPalette::AlternateBase, readKdeSetting(KdeSetting::ViewBackgroundAlternate, kdeDirs, kdeVersion, kdeSettings));
509 kdeColor(pal, QPalette::ButtonText, readKdeSetting(KdeSetting::ButtonForeground, kdeDirs, kdeVersion, kdeSettings));
510 kdeColor(pal, QPalette::Link, readKdeSetting(KdeSetting::ViewForegroundLink, kdeDirs, kdeVersion, kdeSettings));
511 kdeColor(pal, QPalette::LinkVisited, readKdeSetting(KdeSetting::ViewForegroundVisited, kdeDirs, kdeVersion, kdeSettings));
512 kdeColor(pal, QPalette::ToolTipBase, readKdeSetting(KdeSetting::TooltipBackground, kdeDirs, kdeVersion, kdeSettings));
513 kdeColor(pal, QPalette::ToolTipText, readKdeSetting(KdeSetting::TooltipForeground, kdeDirs, kdeVersion, kdeSettings));
514 kdeColor(pal, QPalette::PlaceholderText, readKdeSetting(KdeSetting::ViewForegroundInactive, kdeDirs, kdeVersion, kdeSettings));
515
516 // The above code sets _all_ color roles to "normal" colors. In KDE, the disabled
517 // color roles are calculated by applying various effects described in kdeglobals.
518 // We use a bit simpler approach here, similar logic than in qt_palette_from_color().
519 const QColor button = pal->color(QPalette::Button);
520 int h, s, v;
521 button.getHsv(&h, &s, &v);
522
523 const QBrush whiteBrush = QBrush(Qt::white);
524 const QBrush buttonBrush = QBrush(button);
525 const QBrush buttonBrushDark = QBrush(button.darker(v > 128 ? 200 : 50));
526 const QBrush buttonBrushDark150 = QBrush(button.darker(v > 128 ? 150 : 75));
527 const QBrush buttonBrushLight150 = QBrush(button.lighter(v > 128 ? 150 : 75));
528 const QBrush buttonBrushLight = QBrush(button.lighter(v > 128 ? 200 : 50));
529
530 pal->setBrush(QPalette::Disabled, QPalette::WindowText, buttonBrushDark);
531 pal->setBrush(QPalette::Disabled, QPalette::ButtonText, buttonBrushDark);
532 pal->setBrush(QPalette::Disabled, QPalette::Button, buttonBrush);
533 pal->setBrush(QPalette::Disabled, QPalette::Text, buttonBrushDark);
534 pal->setBrush(QPalette::Disabled, QPalette::BrightText, whiteBrush);
535 pal->setBrush(QPalette::Disabled, QPalette::Base, buttonBrush);
536 pal->setBrush(QPalette::Disabled, QPalette::Window, buttonBrush);
537 pal->setBrush(QPalette::Disabled, QPalette::Highlight, buttonBrushDark150);
538 pal->setBrush(QPalette::Disabled, QPalette::HighlightedText, buttonBrushLight150);
539
540 // set calculated colors for all groups
541 pal->setBrush(QPalette::Light, buttonBrushLight);
542 pal->setBrush(QPalette::Midlight, buttonBrushLight150);
543 pal->setBrush(QPalette::Mid, buttonBrushDark150);
544 pal->setBrush(QPalette::Dark, buttonBrushDark);
545}
546
547/*!
548 \class QKdeTheme
549 \brief QKdeTheme is a theme implementation for the KDE desktop (version 4 or higher).
550 \since 5.0
551 \internal
552 \ingroup qpa
553*/
554
555const char *QKdeTheme::name = "kde";
556
557QKdeTheme::QKdeTheme(const QStringList& kdeDirs, int kdeVersion)
558 : QGenericUnixTheme(new QKdeThemePrivate(kdeDirs, kdeVersion))
559{
560 d_func()->refresh();
561}
562
563QKdeTheme::~QKdeTheme()
564 = default;
565
566QFont *QKdeThemePrivate::kdeFont(const QVariant &fontValue)
567{
568 if (fontValue.isValid()) {
569 // Read font value: Might be a QStringList as KDE stores fonts without quotes.
570 // Also retrieve the family for the constructor since we cannot use the
571 // default constructor of QFont, which accesses QGuiApplication::systemFont()
572 // causing recursion.
573 QString fontDescription;
574 QString fontFamily;
575 if (fontValue.userType() == QMetaType::QStringList) {
576 const QStringList list = fontValue.toStringList();
577 if (!list.isEmpty()) {
578 fontFamily = list.first();
579 fontDescription = list.join(u',');
580 }
581 } else {
582 fontDescription = fontFamily = fontValue.toString();
583 }
584 if (!fontDescription.isEmpty()) {
585 QFont font(fontFamily);
586 if (font.fromString(fontDescription))
587 return new QFont(std::move(font));
588 }
589 }
590 return nullptr;
591}
592
593
594QStringList QKdeThemePrivate::kdeIconThemeSearchPaths(const QStringList &kdeDirs)
595{
596 QStringList paths = QGenericUnixTheme::xdgIconThemePaths();
597 const QString iconPath = QStringLiteral("/share/icons");
598 for (const QString &candidate : kdeDirs) {
599 const QFileInfo fi(candidate + iconPath);
600 if (fi.isDir())
601 paths.append(fi.absoluteFilePath());
602 }
603 return paths;
604}
605
606QVariant QKdeTheme::themeHint(QPlatformTheme::ThemeHint hint) const
607{
608 Q_D(const QKdeTheme);
609 switch (hint) {
610 case QPlatformTheme::UseFullScreenForPopupMenu:
611 return QVariant(true);
612 case QPlatformTheme::DialogButtonBoxButtonsHaveIcons:
613 return QVariant(d->showIconsOnPushButtons);
614 case QPlatformTheme::DialogButtonBoxLayout:
615 return QVariant(QPlatformDialogHelper::KdeLayout);
616 case QPlatformTheme::ToolButtonStyle:
617 return QVariant(d->toolButtonStyle);
618 case QPlatformTheme::ToolBarIconSize:
619 return QVariant(d->toolBarIconSize);
620 case QPlatformTheme::SystemIconThemeName:
621 return QVariant(d->iconThemeName);
622 case QPlatformTheme::SystemIconFallbackThemeName:
623 return QVariant(d->iconFallbackThemeName);
624 case QPlatformTheme::IconThemeSearchPaths:
625 return QVariant(d->kdeIconThemeSearchPaths(d->kdeDirs));
626 case QPlatformTheme::IconPixmapSizes:
627 return QVariant::fromValue(availableXdgFileIconSizes());
628 case QPlatformTheme::StyleNames:
629 return QVariant(d->styleNames);
630 case QPlatformTheme::KeyboardScheme:
631 return QVariant(int(KdeKeyboardScheme));
632 case QPlatformTheme::ItemViewActivateItemOnSingleClick:
633 return QVariant(d->singleClick);
634 case QPlatformTheme::WheelScrollLines:
635 return QVariant(d->wheelScrollLines);
636 case QPlatformTheme::MouseDoubleClickInterval:
637 return QVariant(d->doubleClickInterval);
638 case QPlatformTheme::StartDragTime:
639 return QVariant(d->startDragTime);
640 case QPlatformTheme::StartDragDistance:
641 return QVariant(d->startDragDist);
642 case QPlatformTheme::CursorFlashTime:
643 return QVariant(d->cursorBlinkRate);
644 case QPlatformTheme::UiEffects:
645 return QVariant(int(HoverEffect));
646 case QPlatformTheme::MouseCursorTheme:
647 return QVariant(mouseCursorTheme());
648 case QPlatformTheme::MouseCursorSize:
649 return QVariant(mouseCursorSize());
650 case QPlatformTheme::PreferFileIconFromTheme:
651 return true;
652 default:
653 break;
654 }
655 return QPlatformTheme::themeHint(hint);
656}
657
658QIcon QKdeTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions) const
659{
660#if QT_CONFIG(mimetype)
661 return xdgFileIcon(fileInfo);
662#else
663 Q_UNUSED(fileInfo);
664 return QIcon();
665#endif
666}
667
668/*!
669 \internal
670 \reimp
671 \brief QKdeTheme::requestColorScheme Programmatically request a color scheme
672 If \a scheme is \c Dark or \c Light, \a scheme is applied to the application,
673 independently from the current KDE theme.
674 If \a scheme is \c Unknown, the current KDE theme's color scheme will be applied instead.
675 This is the default behavior.
676
677 \note
678 A KDE theme is considered either \c Dark or \c Light. When the requested color scheme
679 doesn't match the current KDE theme, a default \c Dark or \c Light fusion palette
680 is used instead.
681 \sa QKdeThemePrivate::hasRequestedColorScheme
682*/
683
684/*!
685 \internal
686 \brief QKdeThemePrivate::hasRequestedColorScheme Check if fusion palette fallback is necessary.
687 This internal helper function returns true, if
688 \list
689 \li a color scheme has been programmatically requested, and
690 \li the requested color scheme differs from the current KDE theme's color scheme.
691 \endlist
692 \sa QKdeTheme:requestColorScheme
693*/
694
695void QKdeTheme::requestColorScheme(Qt::ColorScheme scheme)
696{
697 Q_D(QKdeTheme);
698 if (d->m_requestedColorScheme == scheme)
699 return;
700 qCDebug(lcQpaThemeKde) << scheme << "has been requested. Theme supports color scheme:"
701 << d->m_colorScheme;
702 d->m_requestedColorScheme = scheme;
703 d->refresh();
704}
705
706Qt::ColorScheme QKdeTheme::colorScheme() const
707{
708 Q_D(const QKdeTheme);
709#ifdef QT_DEBUG
710 if (d->hasRequestedColorScheme()) {
711 qCDebug(lcQpaThemeKde) << "Reuqested color scheme" << d->m_requestedColorScheme
712 << "differs from theme color scheme" << d->m_colorScheme;
713 }
714#endif
715 return d->hasRequestedColorScheme() ? d->m_requestedColorScheme
716 : d->m_colorScheme;
717}
718
719/*!
720 \internal
721 \brief QKdeTheme::updateColorScheme - guess and set a color scheme for unix themes.
722 KDE themes do not have a color scheme property.
723 The key words "dark" or "light" are usually part of the theme name.
724 This is, however, not a mandatory convention.
725
726 If \param themeName contains a valid key word, the respective color scheme is set.
727 If it doesn't, the color scheme is heuristically determined by comparing text and base color
728 of the system palette.
729 */
730void QKdeThemePrivate::updateColorScheme(const QString &themeName)
731{
732 if (themeName.contains(QLatin1StringView("light"), Qt::CaseInsensitive)) {
733 m_colorScheme = Qt::ColorScheme::Light;
734 return;
735 }
736 if (themeName.contains(QLatin1StringView("dark"), Qt::CaseInsensitive)) {
737 m_colorScheme = Qt::ColorScheme::Dark;
738 return;
739 }
740
741 m_colorScheme = colorSchemeFromPalette();
742}
743
744Qt::ColorScheme QKdeThemePrivate::colorSchemeFromPalette() const
745{
746 if (!systemPalette)
747 return Qt::ColorScheme::Unknown;
748 if (systemPalette->text().color().lightness() < systemPalette->base().color().lightness())
749 return Qt::ColorScheme::Light;
750 if (systemPalette->text().color().lightness() > systemPalette->base().color().lightness())
751 return Qt::ColorScheme::Dark;
752 return Qt::ColorScheme::Unknown;
753}
754
755const QPalette *QKdeTheme::palette(Palette type) const
756{
757 Q_D(const QKdeTheme);
758 if (d->hasRequestedColorScheme()) {
759 qCDebug(lcQpaThemeKde) << "Current KDE theme doesn't support reuqested color scheme"
760 << d->m_requestedColorScheme << "Falling back to fusion palette:"
761 << type;
762 return QPlatformTheme::palette(type);
763 }
764
765 return d->systemPalette.get();
766}
767
768const QFont *QKdeTheme::font(Font type) const
769{
770 Q_D(const QKdeTheme);
771 return d->fonts[type];
772}
773
774QPlatformTheme *QKdeTheme::createKdeTheme()
775{
776 const QByteArray kdeVersionBA = qgetenv("KDE_SESSION_VERSION");
777 const int kdeVersion = kdeVersionBA.toInt();
778 if (kdeVersion < 4)
779 return nullptr;
780
781 if (kdeVersion > 4)
782 // Plasma 5 follows XDG spec
783 // but uses the same config file format:
784 return new QKdeTheme(QStandardPaths::standardLocations(QStandardPaths::GenericConfigLocation), kdeVersion);
785
786 // Determine KDE prefixes in the following priority order:
787 // - KDEHOME and KDEDIRS environment variables
788 // - ~/.kde(<version>)
789 // - read prefixes from /etc/kde<version>rc
790 // - fallback to /etc/kde<version>
791
792 QStringList kdeDirs;
793 const QString kdeHomePathVar = qEnvironmentVariable("KDEHOME");
794 if (!kdeHomePathVar.isEmpty())
795 kdeDirs += kdeHomePathVar;
796
797 const QString kdeDirsVar = qEnvironmentVariable("KDEDIRS");
798 if (!kdeDirsVar.isEmpty())
799 kdeDirs += kdeDirsVar.split(u':', Qt::SkipEmptyParts);
800
801 const QString kdeVersionHomePath = QDir::homePath() + "/.kde"_L1 + QLatin1StringView(kdeVersionBA);
802 if (QFileInfo(kdeVersionHomePath).isDir())
803 kdeDirs += kdeVersionHomePath;
804
805 const QString kdeHomePath = QDir::homePath() + "/.kde"_L1;
806 if (QFileInfo(kdeHomePath).isDir())
807 kdeDirs += kdeHomePath;
808
809 const QString kdeRcPath = "/etc/kde"_L1 + QLatin1StringView(kdeVersionBA) + "rc"_L1;
810 if (QFileInfo(kdeRcPath).isReadable()) {
811 QSettings kdeSettings(kdeRcPath, QSettings::IniFormat);
812 kdeSettings.beginGroup(QStringLiteral("Directories-default"));
813 kdeDirs += kdeSettings.value(QStringLiteral("prefixes")).toStringList();
814 }
815
816 const QString kdeVersionPrefix = "/etc/kde"_L1 + QLatin1StringView(kdeVersionBA);
817 if (QFileInfo(kdeVersionPrefix).isDir())
818 kdeDirs += kdeVersionPrefix;
819
820 kdeDirs.removeDuplicates();
821 if (kdeDirs.isEmpty()) {
822 qWarning("Unable to determine KDE dirs");
823 return nullptr;
824 }
825
826 return new QKdeTheme(kdeDirs, kdeVersion);
827}
828
829#if QT_CONFIG(dbus)
830QPlatformMenuBar *QKdeTheme::createPlatformMenuBar() const
831{
832 if (isDBusGlobalMenuAvailable())
833 return new QDBusMenuBar();
834 return nullptr;
835}
836#endif
837
838#if QT_CONFIG(dbus) && QT_CONFIG(systemtrayicon)
839QPlatformSystemTrayIcon *QKdeTheme::createPlatformSystemTrayIcon() const
840{
841 if (shouldUseDBusTray())
842 return new QDBusTrayIcon();
843 return nullptr;
844}
845#endif
846
847QT_END_NAMESPACE
static QFont * kdeFont(const QVariant &fontValue)
static QStringList kdeIconThemeSearchPaths(const QStringList &kdeDirs)
std::unique_ptr< QPalette > systemPalette
void updateColorScheme(const QString &themeName)
QKdeTheme::updateColorScheme - guess and set a color scheme for unix themes. KDE themes do not have a...
bool hasRequestedColorScheme() const
QStringList styleNames
Definition qkdetheme.cpp:96
QString iconThemeName
Definition qkdetheme.cpp:94
void clearKdeSettings() const
const QStringList kdeDirs
Definition qkdetheme.cpp:91
static QString kdeGlobals(const QString &kdeDir, int kdeVersion)
Definition qkdetheme.cpp:76
bool showIconsOnPushButtons
Qt::ColorScheme m_colorScheme
Qt::ColorScheme m_requestedColorScheme
QString iconFallbackThemeName
Definition qkdetheme.cpp:95
QVariant readKdeSetting(KdeSetting s) const
static QVariant readKdeSetting(KdeSetting s, const QStringList &kdeDirs, int kdeVersion, QHash< QString, QSettings * > &settings)
const int kdeVersion
Definition qkdetheme.cpp:92
QFont * fonts[QPlatformTheme::NFonts]
QKdeThemePrivate(const QStringList &kdeDirs, int kdeVersion)
static void readKdeSystemPalette(const QStringList &kdeDirs, int kdeVersion, QHash< QString, QSettings * > &kdeSettings, QPalette *pal)
Combined button and popup list for selecting options.
#define CASE(E, member)
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
static constexpr QLatin1StringView settingsKey(QKdeThemePrivate::KdeSetting setting)
static constexpr QKdeThemePrivate::KdeSettingType settingsType(QKdeThemePrivate::KdeSetting setting)
static constexpr QLatin1StringView settingsPrefix(QKdeThemePrivate::KdeSettingType type)
static bool kdeColor(QPalette *pal, QPalette::ColorRole role, const QVariant &value)