File indexing completed on 2024-05-12 04:46:58

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