File indexing completed on 2024-05-12 15:42:38

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