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"