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