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