Kirigami2

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

KDE's Doxygen guidelines are available online.