MauiKit Controls

platformtheme.cpp
1/*
2 * SPDX-FileCopyrightText: 2017 by Marco Martin <mart@kde.org>
3 * SPDX-FileCopyrightText: 2021 Arjen Hiemstra <ahiemstra@heimr.nl>
4 *
5 * SPDX-License-Identifier: LGPL-2.0-or-later
6 */
7
8#include "platformtheme.h"
9#include "basictheme_p.h"
10#include <QDebug>
11#include <QDir>
12#include <QGuiApplication>
13#include <QPointer>
14#include <QQmlContext>
15#include <QQmlEngine>
16#include <QQuickStyle>
17#include <QQuickWindow>
18
19#include <array>
20#include <cinttypes>
21#include <functional>
22#include <memory>
23#include <unordered_map>
24
25namespace Maui
26{
27template<>
28QEvent::Type PlatformThemeEvents::DataChangedEvent::type = QEvent::None;
29template<>
30QEvent::Type PlatformThemeEvents::ColorSetChangedEvent::type = QEvent::None;
31template<>
32QEvent::Type PlatformThemeEvents::ColorGroupChangedEvent::type = QEvent::None;
33template<>
34QEvent::Type PlatformThemeEvents::ColorChangedEvent::type = QEvent::None;
35template<>
36QEvent::Type PlatformThemeEvents::FontChangedEvent::type = QEvent::None;
37
38// Initialize event types.
39// We want to avoid collisions with application event types so we should use
40// registerEventType for generating the event types. Unfortunately, that method
41// is not constexpr so we need to call it somewhere during application startup.
42// This struct handles that.
43struct TypeInitializer {
44 TypeInitializer()
45 {
46 PlatformThemeEvents::DataChangedEvent::type = QEvent::Type(QEvent::registerEventType());
47 PlatformThemeEvents::ColorSetChangedEvent::type = QEvent::Type(QEvent::registerEventType());
48 PlatformThemeEvents::ColorGroupChangedEvent::type = QEvent::Type(QEvent::registerEventType());
49 PlatformThemeEvents::ColorChangedEvent::type = QEvent::Type(QEvent::registerEventType());
50 PlatformThemeEvents::FontChangedEvent::type = QEvent::Type(QEvent::registerEventType());
51 }
52};
53static TypeInitializer initializer;
54
55// This class encapsulates the actual data of the Theme object. It may be shared
56// among several instances of PlatformTheme, to ensure that the memory usage of
57// PlatformTheme stays low.
58class PlatformThemeData : public QObject
59{
61
62public:
63 // An enum for all colors in PlatformTheme.
64 // This is used so we can have a QHash of local overrides in the
65 // PlatformTheme, which avoids needing to store all these colors in
66 // PlatformTheme even when they're not used.
67 enum ColorRole {
68 TextColor,
69 DisabledTextColor,
70 HighlightedTextColor,
71 ActiveTextColor,
72 LinkColor,
73 VisitedLinkColor,
74 NegativeTextColor,
75 NeutralTextColor,
76 PositiveTextColor,
77 BackgroundColor,
78 AlternateBackgroundColor,
79 HighlightColor,
80 ActiveBackgroundColor,
81 LinkBackgroundColor,
82 VisitedLinkBackgroundColor,
83 NegativeBackgroundColor,
84 NeutralBackgroundColor,
85 PositiveBackgroundColor,
86 FocusColor,
87 HoverColor,
88
89 // This should always be the last item. It indicates how many items
90 // there are and is used for the storage array below.
91 ColorRoleCount,
92 };
93
94 using ColorMap = std::unordered_map<std::underlying_type<ColorRole>::type, QColor>;
95
96 // Which PlatformTheme instance "owns" this data object. Only the owner is
97 // allowed to make changes to data.
99
101 PlatformTheme::ColorGroup colorGroup = PlatformTheme::Active;
102
103 std::array<QColor, ColorRoleCount> colors;
104
105 QPalette palette;
106
107 // A list of PlatformTheme instances that want to be notified when the data
108 // changes. This is used instead of signal/slots as this way we only store
109 // a little bit of data and that data is shared among instances, whereas
110 // signal/slots turn out to have a pretty large memory overhead per instance.
111 using Watcher = PlatformTheme *;
112 QVector<Watcher> watchers;
113
114 inline void setColorSet(PlatformTheme *sender, PlatformTheme::ColorSet set)
115 {
116 if (sender != owner || colorSet == set) {
117 return;
118 }
119
120 auto oldValue = colorSet;
121
122 colorSet = set;
123
124 notifyWatchers<PlatformTheme::ColorSet>(sender, oldValue, set);
125 }
126
127 inline void setColorGroup(PlatformTheme *sender, PlatformTheme::ColorGroup group)
128 {
129 if (sender != owner || colorGroup == group) {
130 return;
131 }
132
133 auto oldValue = colorGroup;
134
135 colorGroup = group;
137
138 notifyWatchers<PlatformTheme::ColorGroup>(sender, oldValue, group);
139 }
140
141 inline void setColor(PlatformTheme *sender, ColorRole role, const QColor &color)
142 {
143 if (sender != owner || colors[role] == color) {
144 return;
145 }
146
147 auto oldValue = colors[role];
148
149 colors[role] = color;
150 updatePalette(palette, colors);
151
152 notifyWatchers<QColor>(sender, oldValue, colors[role]);
153 }
154
155 inline void addChangeWatcher(PlatformTheme *object)
156 {
157 watchers.append(object);
158 }
159
160 inline void removeChangeWatcher(PlatformTheme *object)
161 {
162 watchers.removeOne(object);
163 }
164
165 template<typename T>
166 inline void notifyWatchers(PlatformTheme *sender, const T &oldValue, const T &newValue)
167 {
168 for (auto object : std::as_const(watchers)) {
169 PlatformThemeEvents::PropertyChangedEvent<T> event(sender, oldValue, newValue);
171 }
172 }
173
174 // Update a palette from a list of colors.
175 inline static void updatePalette(QPalette &palette, const std::array<QColor, ColorRoleCount> &colors)
176 {
177 for (std::size_t i = 0; i < colors.size(); ++i) {
178 setPaletteColor(palette, ColorRole(i), colors.at(i));
179 }
180 }
181
182 // Update a palette from a hash of colors.
183 inline static void updatePalette(QPalette &palette, const ColorMap &colors)
184 {
185 for (auto entry : colors) {
186 setPaletteColor(palette, ColorRole(entry.first), entry.second);
187 }
188 }
189
190 inline static void setPaletteColor(QPalette &palette, ColorRole role, const QColor &color)
191 {
192 switch (role) {
193 case TextColor:
194 palette.setColor(QPalette::Text, color);
195 palette.setColor(QPalette::WindowText, color);
196 palette.setColor(QPalette::ButtonText, color);
197 break;
198 case BackgroundColor:
199 palette.setColor(QPalette::Window, color);
200 palette.setColor(QPalette::Base, color);
201 palette.setColor(QPalette::Button, color);
202 break;
203 case AlternateBackgroundColor:
204 palette.setColor(QPalette::AlternateBase, color);
205 break;
206 case HighlightColor:
207 palette.setColor(QPalette::Highlight, color);
208 break;
209 case HighlightedTextColor:
210 palette.setColor(QPalette::HighlightedText, color);
211 break;
212 case LinkColor:
213 palette.setColor(QPalette::Link, color);
214 break;
215 case VisitedLinkColor:
216 palette.setColor(QPalette::LinkVisited, color);
217 break;
218
219 default:
220 break;
221 }
222 }
223};
224
225class PlatformThemePrivate
226{
227public:
228 PlatformThemePrivate()
229 : inherit(true)
230 , supportsIconColoring(false)
231 , pendingColorChange(false)
232 , pendingChildUpdate(false)
233 , colorSet(PlatformTheme::Window)
234 , colorGroup(PlatformTheme::Active)
235 {
236 }
237
238 inline QColor color(const PlatformTheme *theme, PlatformThemeData::ColorRole color) const
239 {
240 if (!data) {
241 return QColor{};
242 }
243
244 QColor value = data->colors.at(color);
245
246 if (data->owner != theme && localOverrides) {
247 auto itr = localOverrides->find(color);
248 if (itr != localOverrides->end()) {
249 value = itr->second;
250 }
251 }
252
253 return value;
254 }
255
256 inline void setColor(PlatformTheme *theme, PlatformThemeData::ColorRole color, const QColor &value)
257 {
258 if (!localOverrides) {
259 localOverrides = std::make_unique<PlatformThemeData::ColorMap>();
260 }
261
262 if (!value.isValid()) {
263 // Invalid color, assume we are resetting the value.
264 auto itr = localOverrides->find(color);
265 if (itr != localOverrides->end()) {
266 localOverrides->erase(itr);
267
268 if (data) {
269 // TODO: Find a better way to determine "default" color.
270 // Right now this sets the color to transparent to force a
271 // color change and relies on the style-specific subclass to
272 // handle resetting the actual color.
273 data->setColor(theme, color, Qt::transparent);
274 }
275
276 emitCompressedColorChanged(theme);
277 }
278
279 return;
280 }
281
282 auto itr = localOverrides->find(color);
283 if (itr != localOverrides->end() && itr->second == value && (data && data->owner != theme)) {
284 return;
285 }
286
287 (*localOverrides)[color] = value;
288
289 if (data) {
290 data->setColor(theme, color, value);
291 }
292
293 emitCompressedColorChanged(theme);
294 }
295
296 inline void setDataColor(PlatformTheme *theme, PlatformThemeData::ColorRole color, const QColor &value)
297 {
298 // Only set color if we have no local override of the color.
299 // This is done because colorSet/colorGroup changes will trigger most
300 // subclasses to reevaluate and reset the colors, breaking any local
301 // overrides we have.
302 if (localOverrides) {
303 auto itr = localOverrides->find(color);
304 if (itr != localOverrides->end()) {
305 return;
306 }
307 }
308
309 if (data) {
310 data->setColor(theme, color, value);
311 }
312 }
313
314 inline void emitCompressedColorChanged(PlatformTheme *theme)
315 {
316 if (pendingColorChange) {
317 return;
318 }
319
320 pendingColorChange = true;
321 QMetaObject::invokeMethod(theme, &PlatformTheme::emitColorChanged, Qt::QueuedConnection);
322 }
323
324 inline void queueChildUpdate(PlatformTheme *theme)
325 {
326 if (pendingChildUpdate) {
327 return;
328 }
329
330 pendingChildUpdate = true;
332 theme,
333 [this, theme]() {
334 pendingChildUpdate = false;
335 theme->updateChildren(theme->parent());
336 },
338 }
339
340 /*
341 * Please note that there is no q pointer. This is intentional, as it avoids
342 * having to store that information for each instance of PlatformTheme,
343 * saving us 8 bytes per instance. Instead, we pass the theme object as
344 * first parameter of each method. This is a little uglier but essentially
345 * works the same without needing memory.
346 */
347
348 // An instance of the data object. This is potentially shared with many
349 // instances of PlatformTheme.
350 std::shared_ptr<PlatformThemeData> data;
351 // Used to store color overrides of inherited data. This is created on
352 // demand and will only exist if we actually have local overrides.
353 std::unique_ptr<PlatformThemeData::ColorMap> localOverrides;
354
355 bool inherit : 1;
356 bool supportsIconColoring : 1; // TODO KF6: Remove in favour of virtual method
357 bool pendingColorChange : 1;
358 bool pendingChildUpdate : 1;
359
360 // Note: We use these to store local values of PlatformTheme::ColorSet and
361 // PlatformTheme::ColorGroup. While these are standard enums and thus 32
362 // bits they only contain a few items so we store the value in only 4 bits
363 // to save space.
364 uint8_t colorSet : 4;
365 uint8_t colorGroup : 4;
366
367 // Ensure the above assumption holds. Should this static assert fail, the
368 // bit size above needs to be adjusted.
369 static_assert(PlatformTheme::ColorGroupCount <= 16, "PlatformTheme::ColorGroup contains more elements than can be stored in PlatformThemePrivate");
370 static_assert(PlatformTheme::ColorSetCount <= 16, "PlatformTheme::ColorSet contains more elements than can be stored in PlatformThemePrivate");
371
372};
373
374
375PlatformTheme::PlatformTheme(QObject *parent)
376 : QObject(parent)
377 , d(new PlatformThemePrivate)
378{
379 if (QQuickItem *item = qobject_cast<QQuickItem *>(parent)) {
380 connect(item, &QQuickItem::windowChanged, this, &PlatformTheme::update);
381 connect(item, &QQuickItem::parentChanged, this, &PlatformTheme::update);
382 }
383
384 update();
385}
386
387PlatformTheme::~PlatformTheme()
388{
389 if (d->data) {
390 d->data->removeChangeWatcher(this);
391 }
392
393 delete d;
394}
395
396void PlatformTheme::setColorSet(PlatformTheme::ColorSet colorSet)
397{
398 d->colorSet = colorSet;
399
400 if (d->data) {
401 d->data->setColorSet(this, colorSet);
402 }
403}
404
405PlatformTheme::ColorSet PlatformTheme::colorSet() const
406{
407 return d->data ? d->data->colorSet : Window;
408}
409
410void PlatformTheme::setColorGroup(PlatformTheme::ColorGroup colorGroup)
411{
412 d->colorGroup = colorGroup;
413
414 if (d->data) {
415 d->data->setColorGroup(this, colorGroup);
416 }
417}
418
419PlatformTheme::ColorGroup PlatformTheme::colorGroup() const
420{
421 return d->data ? d->data->colorGroup : Active;
422}
423
424bool PlatformTheme::inherit() const
425{
426 return d->inherit;
427}
428
429void PlatformTheme::setInherit(bool inherit)
430{
431 if (inherit == d->inherit) {
432 return;
433 }
434
435 d->inherit = inherit;
436 update();
437
438 Q_EMIT inheritChanged(inherit);
439}
440
441QColor PlatformTheme::textColor() const
442{
443 return d->color(this, PlatformThemeData::TextColor);
444}
445
446QColor PlatformTheme::disabledTextColor() const
447{
448 return d->color(this, PlatformThemeData::DisabledTextColor);
449}
450
451QColor PlatformTheme::highlightColor() const
452{
453 return d->color(this, PlatformThemeData::HighlightColor);
454}
455
456QColor PlatformTheme::highlightedTextColor() const
457{
458 return d->color(this, PlatformThemeData::HighlightedTextColor);
459}
460
461QColor PlatformTheme::backgroundColor() const
462{
463 return d->color(this, PlatformThemeData::BackgroundColor);
464}
465
466QColor PlatformTheme::alternateBackgroundColor() const
467{
468 return d->color(this, PlatformThemeData::AlternateBackgroundColor);
469}
470
471QColor PlatformTheme::activeTextColor() const
472{
473 return d->color(this, PlatformThemeData::ActiveTextColor);
474}
475
476QColor PlatformTheme::activeBackgroundColor() const
477{
478 return d->color(this, PlatformThemeData::ActiveBackgroundColor);
479}
480
481QColor PlatformTheme::linkColor() const
482{
483 return d->color(this, PlatformThemeData::LinkColor);
484}
485
486QColor PlatformTheme::linkBackgroundColor() const
487{
488 return d->color(this, PlatformThemeData::LinkBackgroundColor);
489}
490
491QColor PlatformTheme::visitedLinkColor() const
492{
493 return d->color(this, PlatformThemeData::VisitedLinkColor);
494}
495
496QColor PlatformTheme::visitedLinkBackgroundColor() const
497{
498 return d->color(this, PlatformThemeData::VisitedLinkBackgroundColor);
499}
500
501QColor PlatformTheme::negativeTextColor() const
502{
503 return d->color(this, PlatformThemeData::NegativeTextColor);
504}
505
506QColor PlatformTheme::negativeBackgroundColor() const
507{
508 return d->color(this, PlatformThemeData::NegativeBackgroundColor);
509}
510
511QColor PlatformTheme::neutralTextColor() const
512{
513 return d->color(this, PlatformThemeData::NeutralTextColor);
514}
515
516QColor PlatformTheme::neutralBackgroundColor() const
517{
518 return d->color(this, PlatformThemeData::NeutralBackgroundColor);
519}
520
521QColor PlatformTheme::positiveTextColor() const
522{
523 return d->color(this, PlatformThemeData::PositiveTextColor);
524}
525
526QColor PlatformTheme::positiveBackgroundColor() const
527{
528 return d->color(this, PlatformThemeData::PositiveBackgroundColor);
529}
530
531QColor PlatformTheme::focusColor() const
532{
533 return d->color(this, PlatformThemeData::FocusColor);
534}
535
536QColor PlatformTheme::hoverColor() const
537{
538 return d->color(this, PlatformThemeData::HoverColor);
539}
540
541// setters for theme implementations
542void PlatformTheme::setTextColor(const QColor &color)
543{
544 d->setDataColor(this, PlatformThemeData::TextColor, color);
545}
546
547void PlatformTheme::setDisabledTextColor(const QColor &color)
548{
549 d->setDataColor(this, PlatformThemeData::DisabledTextColor, color);
550}
551
552void PlatformTheme::setBackgroundColor(const QColor &color)
553{
554 d->setDataColor(this, PlatformThemeData::BackgroundColor, color);
555}
556
557void PlatformTheme::setAlternateBackgroundColor(const QColor &color)
558{
559 d->setDataColor(this, PlatformThemeData::AlternateBackgroundColor, color);
560}
561
562void PlatformTheme::setHighlightColor(const QColor &color)
563{
564 d->setDataColor(this, PlatformThemeData::HighlightColor, color);
565}
566
567void PlatformTheme::setHighlightedTextColor(const QColor &color)
568{
569 d->setDataColor(this, PlatformThemeData::HighlightedTextColor, color);
570}
571
572void PlatformTheme::setActiveTextColor(const QColor &color)
573{
574 d->setDataColor(this, PlatformThemeData::ActiveTextColor, color);
575}
576
577void PlatformTheme::setActiveBackgroundColor(const QColor &color)
578{
579 d->setDataColor(this, PlatformThemeData::ActiveBackgroundColor, color);
580}
581
582void PlatformTheme::setLinkColor(const QColor &color)
583{
584 d->setDataColor(this, PlatformThemeData::LinkColor, color);
585}
586
587void PlatformTheme::setLinkBackgroundColor(const QColor &color)
588{
589 d->setDataColor(this, PlatformThemeData::LinkBackgroundColor, color);
590}
591
592void PlatformTheme::setVisitedLinkColor(const QColor &color)
593{
594 d->setDataColor(this, PlatformThemeData::VisitedLinkColor, color);
595}
596
597void PlatformTheme::setVisitedLinkBackgroundColor(const QColor &color)
598{
599 d->setDataColor(this, PlatformThemeData::VisitedLinkBackgroundColor, color);
600}
601
602void PlatformTheme::setNegativeTextColor(const QColor &color)
603{
604 d->setDataColor(this, PlatformThemeData::NegativeTextColor, color);
605}
606
607void PlatformTheme::setNegativeBackgroundColor(const QColor &color)
608{
609 d->setDataColor(this, PlatformThemeData::NegativeBackgroundColor, color);
610}
611
612void PlatformTheme::setNeutralTextColor(const QColor &color)
613{
614 d->setDataColor(this, PlatformThemeData::NeutralTextColor, color);
615}
616
617void PlatformTheme::setNeutralBackgroundColor(const QColor &color)
618{
619 d->setDataColor(this, PlatformThemeData::NeutralBackgroundColor, color);
620}
621
622void PlatformTheme::setPositiveTextColor(const QColor &color)
623{
624 d->setDataColor(this, PlatformThemeData::PositiveTextColor, color);
625}
626
627void PlatformTheme::setPositiveBackgroundColor(const QColor &color)
628{
629 d->setDataColor(this, PlatformThemeData::PositiveBackgroundColor, color);
630}
631
632void PlatformTheme::setHoverColor(const QColor &color)
633{
634 d->setDataColor(this, PlatformThemeData::HoverColor, color);
635}
636
637void PlatformTheme::setFocusColor(const QColor &color)
638{
639 d->setDataColor(this, PlatformThemeData::FocusColor, color);
640}
641
642// setters for QML clients
643void PlatformTheme::setCustomTextColor(const QColor &color)
644{
645 d->setColor(this, PlatformThemeData::TextColor, color);
646}
647
648void PlatformTheme::setCustomDisabledTextColor(const QColor &color)
649{
650 d->setColor(this, PlatformThemeData::DisabledTextColor, color);
651}
652
653void PlatformTheme::setCustomBackgroundColor(const QColor &color)
654{
655 d->setColor(this, PlatformThemeData::BackgroundColor, color);
656}
657
658void PlatformTheme::setCustomAlternateBackgroundColor(const QColor &color)
659{
660 d->setColor(this, PlatformThemeData::AlternateBackgroundColor, color);
661}
662
663void PlatformTheme::setCustomHighlightColor(const QColor &color)
664{
665 d->setColor(this, PlatformThemeData::HighlightColor, color);
666}
667
668void PlatformTheme::setCustomHighlightedTextColor(const QColor &color)
669{
670 d->setColor(this, PlatformThemeData::HighlightedTextColor, color);
671}
672
673void PlatformTheme::setCustomActiveTextColor(const QColor &color)
674{
675 d->setColor(this, PlatformThemeData::ActiveTextColor, color);
676}
677
678void PlatformTheme::setCustomActiveBackgroundColor(const QColor &color)
679{
680 d->setColor(this, PlatformThemeData::ActiveBackgroundColor, color);
681}
682
683void PlatformTheme::setCustomLinkColor(const QColor &color)
684{
685 d->setColor(this, PlatformThemeData::LinkColor, color);
686}
687
688void PlatformTheme::setCustomLinkBackgroundColor(const QColor &color)
689{
690 d->setColor(this, PlatformThemeData::LinkBackgroundColor, color);
691}
692
693void PlatformTheme::setCustomVisitedLinkColor(const QColor &color)
694{
695 d->setColor(this, PlatformThemeData::TextColor, color);
696}
697
698void PlatformTheme::setCustomVisitedLinkBackgroundColor(const QColor &color)
699{
700 d->setColor(this, PlatformThemeData::VisitedLinkBackgroundColor, color);
701}
702
703void PlatformTheme::setCustomNegativeTextColor(const QColor &color)
704{
705 d->setColor(this, PlatformThemeData::NegativeTextColor, color);
706}
707
708void PlatformTheme::setCustomNegativeBackgroundColor(const QColor &color)
709{
710 d->setColor(this, PlatformThemeData::NegativeBackgroundColor, color);
711}
712
713void PlatformTheme::setCustomNeutralTextColor(const QColor &color)
714{
715 d->setColor(this, PlatformThemeData::NeutralTextColor, color);
716}
717
718void PlatformTheme::setCustomNeutralBackgroundColor(const QColor &color)
719{
720 d->setColor(this, PlatformThemeData::NeutralBackgroundColor, color);
721}
722
723void PlatformTheme::setCustomPositiveTextColor(const QColor &color)
724{
725 d->setColor(this, PlatformThemeData::PositiveTextColor, color);
726}
727
728void PlatformTheme::setCustomPositiveBackgroundColor(const QColor &color)
729{
730 d->setColor(this, PlatformThemeData::PositiveBackgroundColor, color);
731}
732
733void PlatformTheme::setCustomHoverColor(const QColor &color)
734{
735 d->setColor(this, PlatformThemeData::HoverColor, color);
736}
737
738void PlatformTheme::setCustomFocusColor(const QColor &color)
739{
740 d->setColor(this, PlatformThemeData::FocusColor, color);
741}
742
743QPalette PlatformTheme::palette() const
744{
745 if (!d->data) {
746 return QPalette{};
747 }
748
749 auto palette = d->data->palette;
750
751 if (d->localOverrides) {
752 PlatformThemeData::updatePalette(palette, *d->localOverrides);
753 }
754
755 return palette;
756}
757
758QIcon PlatformTheme::iconFromTheme(const QString &name, const QColor &customColor)
759{
760 Q_UNUSED(customColor);
761 QIcon icon = QIcon::fromTheme(name);
762 return icon;
763}
764
765bool PlatformTheme::supportsIconColoring() const
766{
767 return d->supportsIconColoring;
768}
769
770void PlatformTheme::setSupportsIconColoring(bool support)
771{
772 d->supportsIconColoring = support;
773}
774
775PlatformTheme *PlatformTheme::qmlAttachedProperties(QObject *object)
776{
777 return new BasicTheme(object);
778}
779
780bool PlatformTheme::event(QEvent *event)
781{
782 if (event->type() == PlatformThemeEvents::DataChangedEvent::type) {
783 auto changeEvent = static_cast<PlatformThemeEvents::DataChangedEvent *>(event);
784
785 if (changeEvent->sender != this) {
786 return false;
787 }
788
789 if (changeEvent->oldValue) {
790 changeEvent->oldValue->removeChangeWatcher(this);
791 }
792
793 if (changeEvent->newValue) {
794 auto data = changeEvent->newValue;
795 data->addChangeWatcher(this);
796
797 Q_EMIT colorSetChanged(data->colorSet);
798 Q_EMIT colorGroupChanged(data->colorGroup);
799
800 d->emitCompressedColorChanged(this);
801 }
802
803 return true;
804 }
805
806 if (event->type() == PlatformThemeEvents::ColorSetChangedEvent::type) {
807 if (d->data) {
808 Q_EMIT colorSetChanged(d->data->colorSet);
809 }
810 return true;
811 }
812
813 if (event->type() == PlatformThemeEvents::ColorGroupChangedEvent::type) {
814 if (d->data) {
815 Q_EMIT colorGroupChanged(d->data->colorGroup);
816 }
817 return true;
818 }
819
820 if (event->type() == PlatformThemeEvents::ColorChangedEvent::type) {
821 d->emitCompressedColorChanged(this);
822 return true;
823 }
824
825 if (event->type() == PlatformThemeEvents::FontChangedEvent::type) {
826 if (d->data) {
827 // Q_EMIT defaultFontChanged(d->data->defaultFont);
828 // Q_EMIT smallFontChanged(d->data->smallFont);
829 }
830 return true;
831 }
832
833 return QObject::event(event);
834}
835
836void PlatformTheme::update()
837{
838 d->queueChildUpdate(this);
839
840 auto oldData = d->data;
841
842 if (d->inherit) {
843 QObject *candidate = parent();
844 while (true) {
845 candidate = determineParent(candidate);
846 if (!candidate) {
847 break;
848 }
849
850 auto t = static_cast<PlatformTheme *>(qmlAttachedPropertiesObject<PlatformTheme>(candidate, false));
851 if (t && t->d->data && t->d->data->owner == t) {
852 if (d->data == t->d->data) {
853 // Inheritance is already correct, do nothing.
854 return;
855 }
856
857 d->data = t->d->data;
858
859 PlatformThemeEvents::DataChangedEvent event{this, oldData, t->d->data};
860 QCoreApplication::sendEvent(this, &event);
861
862 return;
863 }
864 }
865 } else if (d->data->owner != this) {
866 // Inherit has changed and we no longer want to inherit, clear the data
867 // so it is recreated below.
868 d->data = nullptr;
869 }
870
871 if (!d->data) {
872 d->data = std::make_shared<PlatformThemeData>();
873 d->data->owner = this;
874 d->data->setColorSet(this, static_cast<ColorSet>(d->colorSet));
875 d->data->setColorGroup(this, static_cast<ColorGroup>(d->colorGroup));
876 }
877
878 if (d->localOverrides) {
879 for (auto entry : *d->localOverrides) {
880 d->data->setColor(this, PlatformThemeData::ColorRole(entry.first), entry.second);
881 }
882 }
883
884 PlatformThemeEvents::DataChangedEvent event{this, oldData, d->data};
885 QCoreApplication::sendEvent(this, &event);
886}
887
888void PlatformTheme::updateChildren(QObject *object)
889{
890 if (!object) {
891 return;
892 }
893
894 const auto children = object->children();
895 for (auto child : children) {
896 auto t = static_cast<PlatformTheme *>(qmlAttachedPropertiesObject<PlatformTheme>(child, false));
897 if (t) {
898 t->update();
899 } else {
900 updateChildren(child);
901 }
902 }
903}
904
905void PlatformTheme::emitColorChanged()
906{
907 if (d->data) {
908 Q_EMIT paletteChanged(d->data->palette);
909 }
910
911 Q_EMIT colorsChanged();
912 d->pendingColorChange = false;
913}
914
915// We sometimes set theme properties on non-visual objects. However, if an item
916// has a visual and a non-visual parent that are different, we should prefer the
917// visual parent, so we need to apply some extra logic.
918QObject *PlatformTheme::determineParent(QObject *object)
919{
920 if (!object) {
921 return nullptr;
922 }
923
924 auto item = qobject_cast<QQuickItem *>(object);
925 if (item) {
926 return item->parentItem();
927 } else {
928 return object->parent();
929 }
930}
931
932}
933
934#include "platformtheme.moc"
@ Window
Color set for item views, usually the lightest of all.
@ ColorSetCount
Color set to be used by heading areas of applications, such as toolbars.
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
void update(Part *part, const QByteArray &data, qint64 dataSize)
bool isValid() const const
bool sendEvent(QObject *receiver, QEvent *event)
int registerEventType(int hint)
QIcon fromTheme(const QString &name)
void append(QList< T > &&value)
bool removeOne(const AT &t)
bool invokeMethod(QObject *context, Functor &&function, FunctorReturnType *ret)
Q_OBJECTQ_OBJECT
virtual bool event(QEvent *e)
QObject * sender() const const
void setColor(ColorGroup group, ColorRole role, const QColor &color)
void setCurrentColorGroup(ColorGroup cg)
void parentChanged(QQuickItem *)
void windowChanged(QQuickWindow *window)
QueuedConnection
transparent
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 17 2024 11:56:16 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.