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