File indexing completed on 2024-05-05 03:56:27

0001 /*
0002  *  SPDX-FileCopyrightText: 2017 by Marco Martin <mart@kde.org>
0003  *  SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl>
0004  *
0005  *  SPDX-License-Identifier: LGPL-2.0-or-later
0006  */
0007 
0008 #include "platformtheme.h"
0009 #include "basictheme_p.h"
0010 #include "platformpluginfactory.h"
0011 #include <QDebug>
0012 #include <QDir>
0013 #include <QGuiApplication>
0014 #include <QPluginLoader>
0015 #include <QPointer>
0016 #include <QQmlContext>
0017 #include <QQmlEngine>
0018 #include <QQuickStyle>
0019 #include <QQuickWindow>
0020 
0021 #include <array>
0022 #include <cinttypes>
0023 #include <functional>
0024 #include <memory>
0025 #include <unordered_map>
0026 
0027 namespace Kirigami
0028 {
0029 namespace Platform
0030 {
0031 template<>
0032 KIRIGAMIPLATFORM_EXPORT QEvent::Type PlatformThemeEvents::DataChangedEvent::type = QEvent::None;
0033 template<>
0034 KIRIGAMIPLATFORM_EXPORT QEvent::Type PlatformThemeEvents::ColorSetChangedEvent::type = QEvent::None;
0035 template<>
0036 KIRIGAMIPLATFORM_EXPORT QEvent::Type PlatformThemeEvents::ColorGroupChangedEvent::type = QEvent::None;
0037 template<>
0038 KIRIGAMIPLATFORM_EXPORT QEvent::Type PlatformThemeEvents::ColorChangedEvent::type = QEvent::None;
0039 template<>
0040 KIRIGAMIPLATFORM_EXPORT QEvent::Type PlatformThemeEvents::FontChangedEvent::type = QEvent::None;
0041 
0042 // Initialize event types.
0043 // We want to avoid collisions with application event types so we should use
0044 // registerEventType for generating the event types. Unfortunately, that method
0045 // is not constexpr so we need to call it somewhere during application startup.
0046 // This struct handles that.
0047 struct TypeInitializer {
0048     TypeInitializer()
0049     {
0050         PlatformThemeEvents::DataChangedEvent::type = QEvent::Type(QEvent::registerEventType());
0051         PlatformThemeEvents::ColorSetChangedEvent::type = QEvent::Type(QEvent::registerEventType());
0052         PlatformThemeEvents::ColorGroupChangedEvent::type = QEvent::Type(QEvent::registerEventType());
0053         PlatformThemeEvents::ColorChangedEvent::type = QEvent::Type(QEvent::registerEventType());
0054         PlatformThemeEvents::FontChangedEvent::type = QEvent::Type(QEvent::registerEventType());
0055     }
0056 };
0057 static TypeInitializer initializer;
0058 
0059 // This class encapsulates the actual data of the Theme object. It may be shared
0060 // among several instances of PlatformTheme, to ensure that the memory usage of
0061 // PlatformTheme stays low.
0062 class PlatformThemeData : public QObject
0063 {
0064     Q_OBJECT
0065 
0066 public:
0067     // An enum for all colors in PlatformTheme.
0068     // This is used so we can have a QHash of local overrides in the
0069     // PlatformTheme, which avoids needing to store all these colors in
0070     // PlatformTheme even when they're not used.
0071     enum ColorRole {
0072         TextColor,
0073         DisabledTextColor,
0074         HighlightedTextColor,
0075         ActiveTextColor,
0076         LinkColor,
0077         VisitedLinkColor,
0078         NegativeTextColor,
0079         NeutralTextColor,
0080         PositiveTextColor,
0081         BackgroundColor,
0082         AlternateBackgroundColor,
0083         HighlightColor,
0084         ActiveBackgroundColor,
0085         LinkBackgroundColor,
0086         VisitedLinkBackgroundColor,
0087         NegativeBackgroundColor,
0088         NeutralBackgroundColor,
0089         PositiveBackgroundColor,
0090         FocusColor,
0091         HoverColor,
0092 
0093         // This should always be the last item. It indicates how many items
0094         // there are and is used for the storage array below.
0095         ColorRoleCount,
0096     };
0097 
0098     using ColorMap = std::unordered_map<std::underlying_type<ColorRole>::type, QColor>;
0099 
0100     // Which PlatformTheme instance "owns" this data object. Only the owner is
0101     // allowed to make changes to data.
0102     QPointer<PlatformTheme> owner;
0103 
0104     PlatformTheme::ColorSet colorSet = PlatformTheme::Window;
0105     PlatformTheme::ColorGroup colorGroup = PlatformTheme::Active;
0106 
0107     std::array<QColor, ColorRoleCount> colors;
0108 
0109     QFont defaultFont;
0110     QFont smallFont;
0111 
0112     QPalette palette;
0113 
0114     // A list of PlatformTheme instances that want to be notified when the data
0115     // changes. This is used instead of signal/slots as this way we only store
0116     // a little bit of data and that data is shared among instances, whereas
0117     // signal/slots turn out to have a pretty large memory overhead per instance.
0118     using Watcher = PlatformTheme *;
0119     QList<Watcher> watchers;
0120 
0121     inline void setColorSet(PlatformTheme *sender, PlatformTheme::ColorSet set)
0122     {
0123         if (sender != owner || colorSet == set) {
0124             return;
0125         }
0126 
0127         auto oldValue = colorSet;
0128 
0129         colorSet = set;
0130 
0131         notifyWatchers<PlatformTheme::ColorSet>(sender, oldValue, set);
0132     }
0133 
0134     inline void setColorGroup(PlatformTheme *sender, PlatformTheme::ColorGroup group)
0135     {
0136         if (sender != owner || colorGroup == group) {
0137             return;
0138         }
0139 
0140         auto oldValue = colorGroup;
0141 
0142         colorGroup = group;
0143         palette.setCurrentColorGroup(QPalette::ColorGroup(group));
0144 
0145         notifyWatchers<PlatformTheme::ColorGroup>(sender, oldValue, group);
0146     }
0147 
0148     inline void setColor(PlatformTheme *sender, ColorRole role, const QColor &color)
0149     {
0150         if (sender != owner || colors[role] == color) {
0151             return;
0152         }
0153 
0154         auto oldValue = colors[role];
0155 
0156         colors[role] = color;
0157         updatePalette(palette, colors);
0158 
0159         notifyWatchers<QColor>(sender, oldValue, colors[role]);
0160     }
0161 
0162     inline void setDefaultFont(PlatformTheme *sender, const QFont &font)
0163     {
0164         if (sender != owner || font == defaultFont) {
0165             return;
0166         }
0167 
0168         auto oldValue = defaultFont;
0169 
0170         defaultFont = font;
0171 
0172         notifyWatchers<QFont>(sender, oldValue, font);
0173     }
0174 
0175     inline void setSmallFont(PlatformTheme *sender, const QFont &font)
0176     {
0177         if (sender != owner || font == smallFont) {
0178             return;
0179         }
0180 
0181         auto oldValue = smallFont;
0182 
0183         smallFont = font;
0184 
0185         notifyWatchers<QFont>(sender, oldValue, smallFont);
0186     }
0187 
0188     inline void addChangeWatcher(PlatformTheme *object)
0189     {
0190         watchers.append(object);
0191     }
0192 
0193     inline void removeChangeWatcher(PlatformTheme *object)
0194     {
0195         watchers.removeOne(object);
0196     }
0197 
0198     template<typename T>
0199     inline void notifyWatchers(PlatformTheme *sender, const T &oldValue, const T &newValue)
0200     {
0201         for (auto object : std::as_const(watchers)) {
0202             PlatformThemeEvents::PropertyChangedEvent<T> event(sender, oldValue, newValue);
0203             QCoreApplication::sendEvent(object, &event);
0204         }
0205     }
0206 
0207     // Update a palette from a list of colors.
0208     inline static void updatePalette(QPalette &palette, const std::array<QColor, ColorRoleCount> &colors)
0209     {
0210         for (std::size_t i = 0; i < colors.size(); ++i) {
0211             setPaletteColor(palette, ColorRole(i), colors.at(i));
0212         }
0213     }
0214 
0215     // Update a palette from a hash of colors.
0216     inline static void updatePalette(QPalette &palette, const ColorMap &colors)
0217     {
0218         for (auto entry : colors) {
0219             setPaletteColor(palette, ColorRole(entry.first), entry.second);
0220         }
0221     }
0222 
0223     inline static void setPaletteColor(QPalette &palette, ColorRole role, const QColor &color)
0224     {
0225         switch (role) {
0226         case TextColor:
0227             palette.setColor(QPalette::Text, color);
0228             palette.setColor(QPalette::WindowText, color);
0229             palette.setColor(QPalette::ButtonText, color);
0230             break;
0231         case BackgroundColor:
0232             palette.setColor(QPalette::Window, color);
0233             palette.setColor(QPalette::Base, color);
0234             palette.setColor(QPalette::Button, color);
0235             break;
0236         case AlternateBackgroundColor:
0237             palette.setColor(QPalette::AlternateBase, color);
0238             break;
0239         case HighlightColor:
0240             palette.setColor(QPalette::Highlight, color);
0241             break;
0242         case HighlightedTextColor:
0243             palette.setColor(QPalette::HighlightedText, color);
0244             break;
0245         case LinkColor:
0246             palette.setColor(QPalette::Link, color);
0247             break;
0248         case VisitedLinkColor:
0249             palette.setColor(QPalette::LinkVisited, color);
0250             break;
0251 
0252         default:
0253             break;
0254         }
0255     }
0256 };
0257 
0258 class PlatformThemePrivate
0259 {
0260 public:
0261     PlatformThemePrivate()
0262         : inherit(true)
0263         , supportsIconColoring(false)
0264         , pendingColorChange(false)
0265         , pendingChildUpdate(false)
0266         , useAlternateBackgroundColor(false)
0267         , colorSet(PlatformTheme::Window)
0268         , colorGroup(PlatformTheme::Active)
0269     {
0270     }
0271 
0272     inline QColor color(const PlatformTheme *theme, PlatformThemeData::ColorRole color) const
0273     {
0274         if (!data) {
0275             return QColor{};
0276         }
0277 
0278         QColor value = data->colors.at(color);
0279 
0280         if (data->owner != theme && localOverrides) {
0281             auto itr = localOverrides->find(color);
0282             if (itr != localOverrides->end()) {
0283                 value = itr->second;
0284             }
0285         }
0286 
0287         return value;
0288     }
0289 
0290     inline void setColor(PlatformTheme *theme, PlatformThemeData::ColorRole color, const QColor &value)
0291     {
0292         if (!localOverrides) {
0293             localOverrides = std::make_unique<PlatformThemeData::ColorMap>();
0294         }
0295 
0296         if (!value.isValid()) {
0297             // Invalid color, assume we are resetting the value.
0298             auto itr = localOverrides->find(color);
0299             if (itr != localOverrides->end()) {
0300                 localOverrides->erase(itr);
0301 
0302                 if (data) {
0303                     // TODO: Find a better way to determine "default" color.
0304                     // Right now this sets the color to transparent to force a
0305                     // color change and relies on the style-specific subclass to
0306                     // handle resetting the actual color.
0307                     data->setColor(theme, color, Qt::transparent);
0308                 }
0309 
0310                 emitCompressedColorChanged(theme);
0311             }
0312 
0313             return;
0314         }
0315 
0316         auto itr = localOverrides->find(color);
0317         if (itr != localOverrides->end() && itr->second == value && (data && data->owner != theme)) {
0318             return;
0319         }
0320 
0321         (*localOverrides)[color] = value;
0322 
0323         if (data) {
0324             data->setColor(theme, color, value);
0325         }
0326 
0327         emitCompressedColorChanged(theme);
0328     }
0329 
0330     inline void setDataColor(PlatformTheme *theme, PlatformThemeData::ColorRole color, const QColor &value)
0331     {
0332         // Only set color if we have no local override of the color.
0333         // This is done because colorSet/colorGroup changes will trigger most
0334         // subclasses to reevaluate and reset the colors, breaking any local
0335         // overrides we have.
0336         if (localOverrides) {
0337             auto itr = localOverrides->find(color);
0338             if (itr != localOverrides->end()) {
0339                 return;
0340             }
0341         }
0342 
0343         if (data) {
0344             data->setColor(theme, color, value);
0345         }
0346     }
0347 
0348     inline void emitCompressedColorChanged(PlatformTheme *theme)
0349     {
0350         if (pendingColorChange) {
0351             return;
0352         }
0353 
0354         pendingColorChange = true;
0355         QMetaObject::invokeMethod(theme, &PlatformTheme::emitColorChanged, Qt::QueuedConnection);
0356     }
0357 
0358     inline void queueChildUpdate(PlatformTheme *theme)
0359     {
0360         if (pendingChildUpdate) {
0361             return;
0362         }
0363 
0364         pendingChildUpdate = true;
0365         QMetaObject::invokeMethod(
0366             theme,
0367             [this, theme]() {
0368                 pendingChildUpdate = false;
0369                 theme->updateChildren(theme->parent());
0370             },
0371             Qt::QueuedConnection);
0372     }
0373 
0374     /*
0375      * Please note that there is no q pointer. This is intentional, as it avoids
0376      * having to store that information for each instance of PlatformTheme,
0377      * saving us 8 bytes per instance. Instead, we pass the theme object as
0378      * first parameter of each method. This is a little uglier but essentially
0379      * works the same without needing memory.
0380      */
0381 
0382     // An instance of the data object. This is potentially shared with many
0383     // instances of PlatformTheme.
0384     std::shared_ptr<PlatformThemeData> data;
0385     // Used to store color overrides of inherited data. This is created on
0386     // demand and will only exist if we actually have local overrides.
0387     std::unique_ptr<PlatformThemeData::ColorMap> localOverrides;
0388 
0389     bool inherit : 1;
0390     bool supportsIconColoring : 1; // TODO KF6: Remove in favour of virtual method
0391     bool pendingColorChange : 1;
0392     bool pendingChildUpdate : 1;
0393     bool useAlternateBackgroundColor : 1;
0394 
0395     // Note: We use these to store local values of PlatformTheme::ColorSet and
0396     // PlatformTheme::ColorGroup. While these are standard enums and thus 32
0397     // bits they only contain a few items so we store the value in only 4 bits
0398     // to save space.
0399     uint8_t colorSet : 4;
0400     uint8_t colorGroup : 4;
0401 
0402     // Ensure the above assumption holds. Should this static assert fail, the
0403     // bit size above needs to be adjusted.
0404     static_assert(PlatformTheme::ColorGroupCount <= 16, "PlatformTheme::ColorGroup contains more elements than can be stored in PlatformThemePrivate");
0405     static_assert(PlatformTheme::ColorSetCount <= 16, "PlatformTheme::ColorSet contains more elements than can be stored in PlatformThemePrivate");
0406 
0407     inline static PlatformPluginFactory *s_pluginFactory = nullptr;
0408 };
0409 
0410 PlatformTheme::PlatformTheme(QObject *parent)
0411     : QObject(parent)
0412     , d(new PlatformThemePrivate)
0413 {
0414     if (QQuickItem *item = qobject_cast<QQuickItem *>(parent)) {
0415         connect(item, &QQuickItem::windowChanged, this, &PlatformTheme::update);
0416         connect(item, &QQuickItem::parentChanged, this, &PlatformTheme::update);
0417     }
0418 
0419     update();
0420 }
0421 
0422 PlatformTheme::~PlatformTheme()
0423 {
0424     if (d->data) {
0425         d->data->removeChangeWatcher(this);
0426     }
0427 
0428     delete d;
0429 }
0430 
0431 void PlatformTheme::setColorSet(PlatformTheme::ColorSet colorSet)
0432 {
0433     d->colorSet = colorSet;
0434 
0435     if (d->data) {
0436         d->data->setColorSet(this, colorSet);
0437     }
0438 }
0439 
0440 PlatformTheme::ColorSet PlatformTheme::colorSet() const
0441 {
0442     return d->data ? d->data->colorSet : Window;
0443 }
0444 
0445 void PlatformTheme::setColorGroup(PlatformTheme::ColorGroup colorGroup)
0446 {
0447     d->colorGroup = colorGroup;
0448 
0449     if (d->data) {
0450         d->data->setColorGroup(this, colorGroup);
0451     }
0452 }
0453 
0454 PlatformTheme::ColorGroup PlatformTheme::colorGroup() const
0455 {
0456     return d->data ? d->data->colorGroup : Active;
0457 }
0458 
0459 bool PlatformTheme::inherit() const
0460 {
0461     return d->inherit;
0462 }
0463 
0464 void PlatformTheme::setInherit(bool inherit)
0465 {
0466     if (inherit == d->inherit) {
0467         return;
0468     }
0469 
0470     d->inherit = inherit;
0471     update();
0472 
0473     Q_EMIT inheritChanged(inherit);
0474 }
0475 
0476 QColor PlatformTheme::textColor() const
0477 {
0478     return d->color(this, PlatformThemeData::TextColor);
0479 }
0480 
0481 QColor PlatformTheme::disabledTextColor() const
0482 {
0483     return d->color(this, PlatformThemeData::DisabledTextColor);
0484 }
0485 
0486 QColor PlatformTheme::highlightColor() const
0487 {
0488     return d->color(this, PlatformThemeData::HighlightColor);
0489 }
0490 
0491 QColor PlatformTheme::highlightedTextColor() const
0492 {
0493     return d->color(this, PlatformThemeData::HighlightedTextColor);
0494 }
0495 
0496 QColor PlatformTheme::backgroundColor() const
0497 {
0498     return d->color(this, PlatformThemeData::BackgroundColor);
0499 }
0500 
0501 QColor PlatformTheme::alternateBackgroundColor() const
0502 {
0503     return d->color(this, PlatformThemeData::AlternateBackgroundColor);
0504 }
0505 
0506 QColor PlatformTheme::activeTextColor() const
0507 {
0508     return d->color(this, PlatformThemeData::ActiveTextColor);
0509 }
0510 
0511 QColor PlatformTheme::activeBackgroundColor() const
0512 {
0513     return d->color(this, PlatformThemeData::ActiveBackgroundColor);
0514 }
0515 
0516 QColor PlatformTheme::linkColor() const
0517 {
0518     return d->color(this, PlatformThemeData::LinkColor);
0519 }
0520 
0521 QColor PlatformTheme::linkBackgroundColor() const
0522 {
0523     return d->color(this, PlatformThemeData::LinkBackgroundColor);
0524 }
0525 
0526 QColor PlatformTheme::visitedLinkColor() const
0527 {
0528     return d->color(this, PlatformThemeData::VisitedLinkColor);
0529 }
0530 
0531 QColor PlatformTheme::visitedLinkBackgroundColor() const
0532 {
0533     return d->color(this, PlatformThemeData::VisitedLinkBackgroundColor);
0534 }
0535 
0536 QColor PlatformTheme::negativeTextColor() const
0537 {
0538     return d->color(this, PlatformThemeData::NegativeTextColor);
0539 }
0540 
0541 QColor PlatformTheme::negativeBackgroundColor() const
0542 {
0543     return d->color(this, PlatformThemeData::NegativeBackgroundColor);
0544 }
0545 
0546 QColor PlatformTheme::neutralTextColor() const
0547 {
0548     return d->color(this, PlatformThemeData::NeutralTextColor);
0549 }
0550 
0551 QColor PlatformTheme::neutralBackgroundColor() const
0552 {
0553     return d->color(this, PlatformThemeData::NeutralBackgroundColor);
0554 }
0555 
0556 QColor PlatformTheme::positiveTextColor() const
0557 {
0558     return d->color(this, PlatformThemeData::PositiveTextColor);
0559 }
0560 
0561 QColor PlatformTheme::positiveBackgroundColor() const
0562 {
0563     return d->color(this, PlatformThemeData::PositiveBackgroundColor);
0564 }
0565 
0566 QColor PlatformTheme::focusColor() const
0567 {
0568     return d->color(this, PlatformThemeData::FocusColor);
0569 }
0570 
0571 QColor PlatformTheme::hoverColor() const
0572 {
0573     return d->color(this, PlatformThemeData::HoverColor);
0574 }
0575 
0576 // setters for theme implementations
0577 void PlatformTheme::setTextColor(const QColor &color)
0578 {
0579     d->setDataColor(this, PlatformThemeData::TextColor, color);
0580 }
0581 
0582 void PlatformTheme::setDisabledTextColor(const QColor &color)
0583 {
0584     d->setDataColor(this, PlatformThemeData::DisabledTextColor, color);
0585 }
0586 
0587 void PlatformTheme::setBackgroundColor(const QColor &color)
0588 {
0589     d->setDataColor(this, PlatformThemeData::BackgroundColor, color);
0590 }
0591 
0592 void PlatformTheme::setAlternateBackgroundColor(const QColor &color)
0593 {
0594     d->setDataColor(this, PlatformThemeData::AlternateBackgroundColor, color);
0595 }
0596 
0597 void PlatformTheme::setHighlightColor(const QColor &color)
0598 {
0599     d->setDataColor(this, PlatformThemeData::HighlightColor, color);
0600 }
0601 
0602 void PlatformTheme::setHighlightedTextColor(const QColor &color)
0603 {
0604     d->setDataColor(this, PlatformThemeData::HighlightedTextColor, color);
0605 }
0606 
0607 void PlatformTheme::setActiveTextColor(const QColor &color)
0608 {
0609     d->setDataColor(this, PlatformThemeData::ActiveTextColor, color);
0610 }
0611 
0612 void PlatformTheme::setActiveBackgroundColor(const QColor &color)
0613 {
0614     d->setDataColor(this, PlatformThemeData::ActiveBackgroundColor, color);
0615 }
0616 
0617 void PlatformTheme::setLinkColor(const QColor &color)
0618 {
0619     d->setDataColor(this, PlatformThemeData::LinkColor, color);
0620 }
0621 
0622 void PlatformTheme::setLinkBackgroundColor(const QColor &color)
0623 {
0624     d->setDataColor(this, PlatformThemeData::LinkBackgroundColor, color);
0625 }
0626 
0627 void PlatformTheme::setVisitedLinkColor(const QColor &color)
0628 {
0629     d->setDataColor(this, PlatformThemeData::VisitedLinkColor, color);
0630 }
0631 
0632 void PlatformTheme::setVisitedLinkBackgroundColor(const QColor &color)
0633 {
0634     d->setDataColor(this, PlatformThemeData::VisitedLinkBackgroundColor, color);
0635 }
0636 
0637 void PlatformTheme::setNegativeTextColor(const QColor &color)
0638 {
0639     d->setDataColor(this, PlatformThemeData::NegativeTextColor, color);
0640 }
0641 
0642 void PlatformTheme::setNegativeBackgroundColor(const QColor &color)
0643 {
0644     d->setDataColor(this, PlatformThemeData::NegativeBackgroundColor, color);
0645 }
0646 
0647 void PlatformTheme::setNeutralTextColor(const QColor &color)
0648 {
0649     d->setDataColor(this, PlatformThemeData::NeutralTextColor, color);
0650 }
0651 
0652 void PlatformTheme::setNeutralBackgroundColor(const QColor &color)
0653 {
0654     d->setDataColor(this, PlatformThemeData::NeutralBackgroundColor, color);
0655 }
0656 
0657 void PlatformTheme::setPositiveTextColor(const QColor &color)
0658 {
0659     d->setDataColor(this, PlatformThemeData::PositiveTextColor, color);
0660 }
0661 
0662 void PlatformTheme::setPositiveBackgroundColor(const QColor &color)
0663 {
0664     d->setDataColor(this, PlatformThemeData::PositiveBackgroundColor, color);
0665 }
0666 
0667 void PlatformTheme::setHoverColor(const QColor &color)
0668 {
0669     d->setDataColor(this, PlatformThemeData::HoverColor, color);
0670 }
0671 
0672 void PlatformTheme::setFocusColor(const QColor &color)
0673 {
0674     d->setDataColor(this, PlatformThemeData::FocusColor, color);
0675 }
0676 
0677 QFont PlatformTheme::defaultFont() const
0678 {
0679     return d->data ? d->data->defaultFont : QFont{};
0680 }
0681 
0682 void PlatformTheme::setDefaultFont(const QFont &font)
0683 {
0684     if (d->data) {
0685         d->data->setDefaultFont(this, font);
0686     }
0687 }
0688 
0689 QFont PlatformTheme::smallFont() const
0690 {
0691     return d->data ? d->data->smallFont : QFont{};
0692 }
0693 
0694 void PlatformTheme::setSmallFont(const QFont &font)
0695 {
0696     if (d->data) {
0697         d->data->setSmallFont(this, font);
0698     }
0699 }
0700 
0701 qreal PlatformTheme::frameContrast() const
0702 {
0703     // This value must be kept in sync with
0704     // the value from Breeze Qt Widget theme.
0705     // See: https://invent.kde.org/plasma/breeze/-/blob/master/kstyle/breezemetrics.h?ref_type=heads#L162
0706     return 0.20;
0707 }
0708 
0709 qreal PlatformTheme::lightFrameContrast() const
0710 {
0711     // This can be utilized to return full contrast
0712     // if high contrast accessibility setting is enabled
0713     return frameContrast() / 2.0;
0714 }
0715 
0716 // setters for QML clients
0717 void PlatformTheme::setCustomTextColor(const QColor &color)
0718 {
0719     d->setColor(this, PlatformThemeData::TextColor, color);
0720 }
0721 
0722 void PlatformTheme::setCustomDisabledTextColor(const QColor &color)
0723 {
0724     d->setColor(this, PlatformThemeData::DisabledTextColor, color);
0725 }
0726 
0727 void PlatformTheme::setCustomBackgroundColor(const QColor &color)
0728 {
0729     d->setColor(this, PlatformThemeData::BackgroundColor, color);
0730 }
0731 
0732 void PlatformTheme::setCustomAlternateBackgroundColor(const QColor &color)
0733 {
0734     d->setColor(this, PlatformThemeData::AlternateBackgroundColor, color);
0735 }
0736 
0737 void PlatformTheme::setCustomHighlightColor(const QColor &color)
0738 {
0739     d->setColor(this, PlatformThemeData::HighlightColor, color);
0740 }
0741 
0742 void PlatformTheme::setCustomHighlightedTextColor(const QColor &color)
0743 {
0744     d->setColor(this, PlatformThemeData::HighlightedTextColor, color);
0745 }
0746 
0747 void PlatformTheme::setCustomActiveTextColor(const QColor &color)
0748 {
0749     d->setColor(this, PlatformThemeData::ActiveTextColor, color);
0750 }
0751 
0752 void PlatformTheme::setCustomActiveBackgroundColor(const QColor &color)
0753 {
0754     d->setColor(this, PlatformThemeData::ActiveBackgroundColor, color);
0755 }
0756 
0757 void PlatformTheme::setCustomLinkColor(const QColor &color)
0758 {
0759     d->setColor(this, PlatformThemeData::LinkColor, color);
0760 }
0761 
0762 void PlatformTheme::setCustomLinkBackgroundColor(const QColor &color)
0763 {
0764     d->setColor(this, PlatformThemeData::LinkBackgroundColor, color);
0765 }
0766 
0767 void PlatformTheme::setCustomVisitedLinkColor(const QColor &color)
0768 {
0769     d->setColor(this, PlatformThemeData::TextColor, color);
0770 }
0771 
0772 void PlatformTheme::setCustomVisitedLinkBackgroundColor(const QColor &color)
0773 {
0774     d->setColor(this, PlatformThemeData::VisitedLinkBackgroundColor, color);
0775 }
0776 
0777 void PlatformTheme::setCustomNegativeTextColor(const QColor &color)
0778 {
0779     d->setColor(this, PlatformThemeData::NegativeTextColor, color);
0780 }
0781 
0782 void PlatformTheme::setCustomNegativeBackgroundColor(const QColor &color)
0783 {
0784     d->setColor(this, PlatformThemeData::NegativeBackgroundColor, color);
0785 }
0786 
0787 void PlatformTheme::setCustomNeutralTextColor(const QColor &color)
0788 {
0789     d->setColor(this, PlatformThemeData::NeutralTextColor, color);
0790 }
0791 
0792 void PlatformTheme::setCustomNeutralBackgroundColor(const QColor &color)
0793 {
0794     d->setColor(this, PlatformThemeData::NeutralBackgroundColor, color);
0795 }
0796 
0797 void PlatformTheme::setCustomPositiveTextColor(const QColor &color)
0798 {
0799     d->setColor(this, PlatformThemeData::PositiveTextColor, color);
0800 }
0801 
0802 void PlatformTheme::setCustomPositiveBackgroundColor(const QColor &color)
0803 {
0804     d->setColor(this, PlatformThemeData::PositiveBackgroundColor, color);
0805 }
0806 
0807 void PlatformTheme::setCustomHoverColor(const QColor &color)
0808 {
0809     d->setColor(this, PlatformThemeData::HoverColor, color);
0810 }
0811 
0812 void PlatformTheme::setCustomFocusColor(const QColor &color)
0813 {
0814     d->setColor(this, PlatformThemeData::FocusColor, color);
0815 }
0816 
0817 bool PlatformTheme::useAlternateBackgroundColor() const
0818 {
0819     return d->useAlternateBackgroundColor;
0820 }
0821 
0822 void PlatformTheme::setUseAlternateBackgroundColor(bool alternate)
0823 {
0824     if (alternate == d->useAlternateBackgroundColor) {
0825         return;
0826     }
0827 
0828     d->useAlternateBackgroundColor = alternate;
0829     Q_EMIT useAlternateBackgroundColorChanged(alternate);
0830 }
0831 
0832 QPalette PlatformTheme::palette() const
0833 {
0834     if (!d->data) {
0835         return QPalette{};
0836     }
0837 
0838     auto palette = d->data->palette;
0839 
0840     if (d->localOverrides) {
0841         PlatformThemeData::updatePalette(palette, *d->localOverrides);
0842     }
0843 
0844     return palette;
0845 }
0846 
0847 QIcon PlatformTheme::iconFromTheme(const QString &name, const QColor &customColor)
0848 {
0849     Q_UNUSED(customColor);
0850     QIcon icon = QIcon::fromTheme(name);
0851     return icon;
0852 }
0853 
0854 bool PlatformTheme::supportsIconColoring() const
0855 {
0856     return d->supportsIconColoring;
0857 }
0858 
0859 void PlatformTheme::setSupportsIconColoring(bool support)
0860 {
0861     d->supportsIconColoring = support;
0862 }
0863 
0864 PlatformTheme *PlatformTheme::qmlAttachedProperties(QObject *object)
0865 {
0866     QQmlEngine *engine = qmlEngine(object);
0867     QString pluginName;
0868 
0869     if (engine) {
0870         pluginName = engine->property("_kirigamiTheme").toString();
0871     }
0872 
0873     auto plugin = PlatformPluginFactory::findPlugin(pluginName);
0874     if (!plugin && !pluginName.isEmpty()) {
0875         plugin = PlatformPluginFactory::findPlugin();
0876     }
0877 
0878     if (plugin) {
0879         if (auto theme = plugin->createPlatformTheme(object)) {
0880             return theme;
0881         }
0882     }
0883 
0884     return new BasicTheme(object);
0885 }
0886 
0887 bool PlatformTheme::event(QEvent *event)
0888 {
0889     if (event->type() == PlatformThemeEvents::DataChangedEvent::type) {
0890         auto changeEvent = static_cast<PlatformThemeEvents::DataChangedEvent *>(event);
0891 
0892         if (changeEvent->sender != this) {
0893             return false;
0894         }
0895 
0896         if (changeEvent->oldValue) {
0897             changeEvent->oldValue->removeChangeWatcher(this);
0898         }
0899 
0900         if (changeEvent->newValue) {
0901             auto data = changeEvent->newValue;
0902             data->addChangeWatcher(this);
0903 
0904             Q_EMIT colorSetChanged(data->colorSet);
0905             Q_EMIT colorGroupChanged(data->colorGroup);
0906             Q_EMIT defaultFontChanged(data->defaultFont);
0907             Q_EMIT smallFontChanged(data->smallFont);
0908             d->emitCompressedColorChanged(this);
0909         }
0910 
0911         return true;
0912     }
0913 
0914     if (event->type() == PlatformThemeEvents::ColorSetChangedEvent::type) {
0915         if (d->data) {
0916             Q_EMIT colorSetChanged(d->data->colorSet);
0917         }
0918         return true;
0919     }
0920 
0921     if (event->type() == PlatformThemeEvents::ColorGroupChangedEvent::type) {
0922         if (d->data) {
0923             Q_EMIT colorGroupChanged(d->data->colorGroup);
0924         }
0925         return true;
0926     }
0927 
0928     if (event->type() == PlatformThemeEvents::ColorChangedEvent::type) {
0929         d->emitCompressedColorChanged(this);
0930         return true;
0931     }
0932 
0933     if (event->type() == PlatformThemeEvents::FontChangedEvent::type) {
0934         if (d->data) {
0935             Q_EMIT defaultFontChanged(d->data->defaultFont);
0936             Q_EMIT smallFontChanged(d->data->smallFont);
0937         }
0938         return true;
0939     }
0940 
0941     return QObject::event(event);
0942 }
0943 
0944 void PlatformTheme::update()
0945 {
0946     d->queueChildUpdate(this);
0947 
0948     auto oldData = d->data;
0949 
0950     if (d->inherit) {
0951         QObject *candidate = parent();
0952         while (true) {
0953             candidate = determineParent(candidate);
0954             if (!candidate) {
0955                 break;
0956             }
0957 
0958             auto t = static_cast<PlatformTheme *>(qmlAttachedPropertiesObject<PlatformTheme>(candidate, false));
0959             if (t && t->d->data && t->d->data->owner == t) {
0960                 if (d->data == t->d->data) {
0961                     // Inheritance is already correct, do nothing.
0962                     return;
0963                 }
0964 
0965                 d->data = t->d->data;
0966 
0967                 PlatformThemeEvents::DataChangedEvent event{this, oldData, t->d->data};
0968                 QCoreApplication::sendEvent(this, &event);
0969 
0970                 return;
0971             }
0972         }
0973     } else if (d->data->owner != this) {
0974         // Inherit has changed and we no longer want to inherit, clear the data
0975         // so it is recreated below.
0976         d->data = nullptr;
0977     }
0978 
0979     if (!d->data) {
0980         d->data = std::make_shared<PlatformThemeData>();
0981         d->data->owner = this;
0982         d->data->setColorSet(this, static_cast<ColorSet>(d->colorSet));
0983         d->data->setColorGroup(this, static_cast<ColorGroup>(d->colorGroup));
0984     }
0985 
0986     if (d->localOverrides) {
0987         for (auto entry : *d->localOverrides) {
0988             d->data->setColor(this, PlatformThemeData::ColorRole(entry.first), entry.second);
0989         }
0990     }
0991 
0992     PlatformThemeEvents::DataChangedEvent event{this, oldData, d->data};
0993     QCoreApplication::sendEvent(this, &event);
0994 }
0995 
0996 void PlatformTheme::updateChildren(QObject *object)
0997 {
0998     if (!object) {
0999         return;
1000     }
1001 
1002     const auto children = object->children();
1003     for (auto child : children) {
1004         auto t = static_cast<PlatformTheme *>(qmlAttachedPropertiesObject<PlatformTheme>(child, false));
1005         if (t) {
1006             t->update();
1007         } else {
1008             updateChildren(child);
1009         }
1010     }
1011 }
1012 
1013 void PlatformTheme::emitColorChanged()
1014 {
1015     if (d->data) {
1016         Q_EMIT paletteChanged(d->data->palette);
1017     }
1018 
1019     Q_EMIT colorsChanged();
1020     d->pendingColorChange = false;
1021 }
1022 
1023 // We sometimes set theme properties on non-visual objects. However, if an item
1024 // has a visual and a non-visual parent that are different, we should prefer the
1025 // visual parent, so we need to apply some extra logic.
1026 QObject *PlatformTheme::determineParent(QObject *object)
1027 {
1028     if (!object) {
1029         return nullptr;
1030     }
1031 
1032     auto item = qobject_cast<QQuickItem *>(object);
1033     if (item) {
1034         return item->parentItem();
1035     } else {
1036         return object->parent();
1037     }
1038 }
1039 
1040 }
1041 }
1042 
1043 #include "moc_platformtheme.cpp"
1044 #include "platformtheme.moc"