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