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"