Kirigami2

basictheme.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 "basictheme_p.h"
9 
10 #include "styleselector_p.h"
11 #include <QFile>
12 #include <QGuiApplication>
13 
14 #include "loggingcategory.h"
15 
16 namespace Kirigami
17 {
18 class CompatibilityThemeDefinition : public BasicThemeDefinition
19 {
20  Q_OBJECT
21 public:
22  CompatibilityThemeDefinition(QObject *component, QObject *parent = nullptr)
23  : BasicThemeDefinition(parent)
24  {
25  m_object = component;
26 
27  connect(m_object, SIGNAL(textColorChanged()), this, SLOT(syncFromQml()));
28  connect(m_object, SIGNAL(disabledTextColorChanged()), this, SLOT(syncFromQml()));
29  connect(m_object, SIGNAL(highlightColorChanged()), this, SLOT(syncFromQml()));
30  connect(m_object, SIGNAL(highlightedTextColorChanged()), this, SLOT(syncFromQml()));
31  connect(m_object, SIGNAL(backgroundColorChanged()), this, SLOT(syncFromQml()));
32  connect(m_object, SIGNAL(alternateBackgroundColorChanged()), this, SLOT(syncFromQml()));
33  connect(m_object, SIGNAL(linkColorChanged()), this, SLOT(syncFromQml()));
34  connect(m_object, SIGNAL(visitedLinkColorChanged()), this, SLOT(syncFromQml()));
35  connect(m_object, SIGNAL(buttonTextColorChanged()), this, SLOT(syncFromQml()));
36  connect(m_object, SIGNAL(buttonBackgroundColorChanged()), this, SLOT(syncFromQml()));
37  connect(m_object, SIGNAL(buttonAlternateBackgroundColorChanged()), this, SLOT(syncFromQml()));
38  connect(m_object, SIGNAL(buttonHoverColorChanged()), this, SLOT(syncFromQml()));
39  connect(m_object, SIGNAL(buttonFocusColorChanged()), this, SLOT(syncFromQml()));
40  connect(m_object, SIGNAL(viewTextColorChanged()), this, SLOT(syncFromQml()));
41  connect(m_object, SIGNAL(viewBackgroundColorChanged()), this, SLOT(syncFromQml()));
42  connect(m_object, SIGNAL(viewAlternateBackgroundColorChanged()), this, SLOT(syncFromQml()));
43  connect(m_object, SIGNAL(viewHoverColorChanged()), this, SLOT(syncFromQml()));
44  connect(m_object, SIGNAL(viewFocusColorChanged()), this, SLOT(syncFromQml()));
45  connect(m_object, SIGNAL(complementaryTextColorChanged()), this, SLOT(syncFromQml()));
46  connect(m_object, SIGNAL(complementaryBackgroundColorChanged()), this, SLOT(syncFromQml()));
47  connect(m_object, SIGNAL(complementaryAlternateBackgroundColorChanged()), this, SLOT(syncFromQml()));
48  connect(m_object, SIGNAL(complementaryHoverColorChanged()), this, SLOT(syncFromQml()));
49  connect(m_object, SIGNAL(complementaryFocusColorChanged()), this, SLOT(syncFromQml()));
50  }
51 
52  void syncToQml(PlatformTheme *object) override
53  {
54  BasicThemeDefinition::syncToQml(object);
55 
56  QMetaObject::invokeMethod(m_object, "__propagateColorSet", Q_ARG(QVariant, QVariant::fromValue(object->parent())), Q_ARG(QVariant, object->colorSet()));
58  "__propagateTextColor",
59  Q_ARG(QVariant, QVariant::fromValue(object->parent())),
60  Q_ARG(QVariant, object->textColor()));
62  "__propagateBackgroundColor",
63  Q_ARG(QVariant, QVariant::fromValue(object->parent())),
64  Q_ARG(QVariant, object->backgroundColor()));
66  "__propagatePrimaryColor",
67  Q_ARG(QVariant, QVariant::fromValue(object->parent())),
68  Q_ARG(QVariant, object->highlightColor()));
70  "__propagateAccentColor",
71  Q_ARG(QVariant, QVariant::fromValue(object->parent())),
72  Q_ARG(QVariant, object->highlightColor()));
73  }
74 
75  Q_SLOT void syncFromQml()
76  {
77  textColor = m_object->property("textColor").value<QColor>();
78  disabledTextColor = m_object->property("disabledTextColor").value<QColor>();
79  highlightColor = m_object->property("highlightColor").value<QColor>();
80  highlightedTextColor = m_object->property("highlightedTextColor").value<QColor>();
81  backgroundColor = m_object->property("backgroundColor").value<QColor>();
82  alternateBackgroundColor = m_object->property("alternateBackgroundColor").value<QColor>();
83  linkColor = m_object->property("linkColor").value<QColor>();
84  visitedLinkColor = m_object->property("visitedLinkColor").value<QColor>();
85  buttonTextColor = m_object->property("buttonTextColor").value<QColor>();
86  buttonBackgroundColor = m_object->property("buttonBackgroundColor").value<QColor>();
87  buttonAlternateBackgroundColor = m_object->property("buttonAlternateBackgroundColor").value<QColor>();
88  buttonHoverColor = m_object->property("buttonHoverColor").value<QColor>();
89  buttonFocusColor = m_object->property("buttonFocusColor").value<QColor>();
90  viewTextColor = m_object->property("viewTextColor").value<QColor>();
91  viewBackgroundColor = m_object->property("viewBackgroundColor").value<QColor>();
92  viewAlternateBackgroundColor = m_object->property("viewAlternateBackgroundColor").value<QColor>();
93  viewHoverColor = m_object->property("viewHoverColor").value<QColor>();
94  viewFocusColor = m_object->property("viewFocusColor").value<QColor>();
95  complementaryTextColor = m_object->property("complementaryTextColor").value<QColor>();
96  complementaryBackgroundColor = m_object->property("complementaryBackgroundColor").value<QColor>();
97  complementaryAlternateBackgroundColor = m_object->property("complementaryAlternateBackgroundColor").value<QColor>();
98  complementaryHoverColor = m_object->property("complementaryHoverColor").value<QColor>();
99  complementaryFocusColor = m_object->property("complementaryFocusColor").value<QColor>();
100 
101  Q_EMIT changed();
102  }
103 
104 private:
105  QObject *m_object;
106 };
107 
108 BasicThemeDefinition::BasicThemeDefinition(QObject *parent)
109  : QObject(parent)
110 {
111  defaultFont = qGuiApp->font();
112 
113  smallFont = qGuiApp->font();
114  smallFont.setPointSize(smallFont.pointSize() - 2);
115 }
116 
117 void BasicThemeDefinition::syncToQml(PlatformTheme *object)
118 {
119  auto item = qobject_cast<QQuickItem *>(object->parent());
120  if (item && qmlAttachedPropertiesObject<PlatformTheme>(item, false) == object) {
121  Q_EMIT sync(item);
122  }
123 }
124 
125 BasicThemeInstance::BasicThemeInstance(QObject *parent)
126  : QObject(parent)
127 {
128 }
129 
130 BasicThemeDefinition &BasicThemeInstance::themeDefinition(QQmlEngine *engine)
131 {
132  if (m_themeDefinition) {
133  return *m_themeDefinition;
134  }
135 
136  auto componentUrl = StyleSelector::componentUrl(QStringLiteral("Theme.qml"));
137  QString path{componentUrl.toLocalFile()};
138  if (path.isEmpty() && componentUrl.scheme() == QLatin1String("qrc")) {
139  path = QLatin1Char(':') + componentUrl.path();
140  }
141  QFile themeFile{path};
142  if (themeFile.open(QIODevice::ReadOnly)) {
143  auto data = themeFile.readAll();
144 
145  // Before Kirigami 5.80, custom Theme files would be registered as a
146  // "Theme" singleton that would then be proxied by BasicTheme. This has
147  // changed with the Theme singleton being an instance of BasicTheme.
148  // However, this means that old theme files fail to load because
149  // QQmlComponent complains about "pragma Singleton". To workaround this,
150  // we remove the pragma here, as everything else should still work
151  // correctly.
152  // TODO KF6: Remove this and rely on all Theme files not being singletons.
153  data.replace("\npragma Singleton\n", "");
154 
155  QQmlComponent component(engine);
156  component.setData(data, componentUrl);
157  auto result = component.create();
158 
159  if (!result) {
160  const auto errors = component.errors();
161  for (auto error : errors) {
162  qCWarning(KirigamiLog) << error.toString();
163  }
164 
165  qCWarning(KirigamiLog) << "Invalid Theme file, using default Basic theme.";
166  m_themeDefinition = std::make_unique<BasicThemeDefinition>();
167  } else if (qobject_cast<BasicThemeDefinition *>(result)) {
168  m_themeDefinition.reset(qobject_cast<BasicThemeDefinition *>(result));
169  } else {
170  qCWarning(KirigamiLog) << "Warning: Theme implementations should use Kirigami.BasicThemeDefinition for its root item";
171  m_themeDefinition = std::make_unique<CompatibilityThemeDefinition>(result);
172  }
173  } else {
174  qCDebug(KirigamiLog) << "No Theme file found, using default Basic theme";
175  m_themeDefinition = std::make_unique<BasicThemeDefinition>();
176  }
177 
178  connect(m_themeDefinition.get(), &BasicThemeDefinition::changed, this, &BasicThemeInstance::onDefinitionChanged);
179 
180  return *m_themeDefinition;
181 }
182 
183 void BasicThemeInstance::onDefinitionChanged()
184 {
185  for (auto watcher : std::as_const(watchers)) {
186  watcher->sync();
187  }
188 }
189 
190 Q_GLOBAL_STATIC(BasicThemeInstance, basicThemeInstance)
191 
192 BasicTheme::BasicTheme(QObject *parent)
193  : PlatformTheme(parent)
194 {
195  basicThemeInstance()->watchers.append(this);
196 
197  sync();
198 }
199 
200 BasicTheme::~BasicTheme()
201 {
202  basicThemeInstance()->watchers.removeOne(this);
203 }
204 
205 void BasicTheme::sync()
206 {
207  auto &definition = basicThemeInstance()->themeDefinition(qmlEngine(parent()));
208 
209  switch (colorSet()) {
210  case BasicTheme::Button:
211  setTextColor(tint(definition.buttonTextColor));
212  setBackgroundColor(tint(definition.buttonBackgroundColor));
213  setAlternateBackgroundColor(tint(definition.buttonAlternateBackgroundColor));
214  setHoverColor(tint(definition.buttonHoverColor));
215  setFocusColor(tint(definition.buttonFocusColor));
216  break;
217  case BasicTheme::View:
218  setTextColor(tint(definition.viewTextColor));
219  setBackgroundColor(tint(definition.viewBackgroundColor));
220  setAlternateBackgroundColor(tint(definition.viewAlternateBackgroundColor));
221  setHoverColor(tint(definition.viewHoverColor));
222  setFocusColor(tint(definition.viewFocusColor));
223  break;
224  case BasicTheme::Selection:
225  setTextColor(tint(definition.selectionTextColor));
226  setBackgroundColor(tint(definition.selectionBackgroundColor));
227  setAlternateBackgroundColor(tint(definition.selectionAlternateBackgroundColor));
228  setHoverColor(tint(definition.selectionHoverColor));
229  setFocusColor(tint(definition.selectionFocusColor));
230  break;
231  case BasicTheme::Tooltip:
232  setTextColor(tint(definition.tooltipTextColor));
233  setBackgroundColor(tint(definition.tooltipBackgroundColor));
234  setAlternateBackgroundColor(tint(definition.tooltipAlternateBackgroundColor));
235  setHoverColor(tint(definition.tooltipHoverColor));
236  setFocusColor(tint(definition.tooltipFocusColor));
237  break;
238  case BasicTheme::Complementary:
239  setTextColor(tint(definition.complementaryTextColor));
240  setBackgroundColor(tint(definition.complementaryBackgroundColor));
241  setAlternateBackgroundColor(tint(definition.complementaryAlternateBackgroundColor));
242  setHoverColor(tint(definition.complementaryHoverColor));
243  setFocusColor(tint(definition.complementaryFocusColor));
244  break;
245  case BasicTheme::Window:
246  default:
247  setTextColor(tint(definition.textColor));
248  setBackgroundColor(tint(definition.backgroundColor));
249  setAlternateBackgroundColor(tint(definition.alternateBackgroundColor));
250  setHoverColor(tint(definition.hoverColor));
251  setFocusColor(tint(definition.focusColor));
252  break;
253  }
254 
255  setDisabledTextColor(tint(definition.disabledTextColor));
256  setHighlightColor(tint(definition.highlightColor));
257  setHighlightedTextColor(tint(definition.highlightedTextColor));
258  setActiveTextColor(tint(definition.activeTextColor));
259  setActiveBackgroundColor(tint(definition.activeBackgroundColor));
260  setLinkColor(tint(definition.linkColor));
261  setLinkBackgroundColor(tint(definition.linkBackgroundColor));
262  setVisitedLinkColor(tint(definition.visitedLinkColor));
263  setVisitedLinkBackgroundColor(tint(definition.visitedLinkBackgroundColor));
264  setNegativeTextColor(tint(definition.negativeTextColor));
265  setNegativeBackgroundColor(tint(definition.negativeBackgroundColor));
266  setNeutralTextColor(tint(definition.neutralTextColor));
267  setNeutralBackgroundColor(tint(definition.neutralBackgroundColor));
268  setPositiveTextColor(tint(definition.positiveTextColor));
269  setPositiveBackgroundColor(tint(definition.positiveBackgroundColor));
270 
271  setDefaultFont(definition.defaultFont);
272  setSmallFont(definition.smallFont);
273 }
274 
275 bool BasicTheme::event(QEvent *event)
276 {
277  if (event->type() == PlatformThemeEvents::DataChangedEvent::type) {
278  sync();
279  }
280 
281  if (event->type() == PlatformThemeEvents::ColorSetChangedEvent::type) {
282  sync();
283  }
284 
285  if (event->type() == PlatformThemeEvents::ColorGroupChangedEvent::type) {
286  sync();
287  }
288 
289  if (event->type() == PlatformThemeEvents::ColorChangedEvent::type) {
290  basicThemeInstance()->themeDefinition(qmlEngine(parent())).syncToQml(this);
291  }
292 
293  if (event->type() == PlatformThemeEvents::FontChangedEvent::type) {
294  basicThemeInstance()->themeDefinition(qmlEngine(parent())).syncToQml(this);
295  }
296 
297  return PlatformTheme::event(event);
298 }
299 
300 QColor BasicTheme::tint(const QColor &color)
301 {
302  switch (colorGroup()) {
303  case PlatformTheme::Inactive:
304  return QColor::fromHsvF(color.hueF(), color.saturationF() * 0.5, color.valueF());
305  case PlatformTheme::Disabled:
306  return QColor::fromHsvF(color.hueF(), color.saturationF() * 0.5, color.valueF() * 0.8);
307  default:
308  return color;
309  }
310 }
311 
312 }
313 
314 #include "basictheme.moc"
QVariant fromValue(const T &value)
KGUIADDONS_EXPORT QColor tint(const QColor &base, const QColor &color, qreal amount=0.3)
Q_GLOBAL_STATIC(Internal::StaticControl, s_instance) class ControlPrivate
int value() const const
AKONADI_CALENDAR_EXPORT KCalendarCore::Event::Ptr event(const Akonadi::Item &item)
QByteArray & replace(int pos, int len, const char *after)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
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)
qreal hueF() const const
qreal saturationF() const const
QByteArray readAll()
QColor fromHsvF(qreal h, qreal s, qreal v, qreal a)
qreal valueF() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Jan 29 2023 04:11:03 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.