Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqstylekitstyle.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqstylekit_p.h"
10
11#include <QtQuickTemplates2/private/qquickdeferredexecute_p_p.h>
12#include <QtQml/private/qqmllist_p.h>
13
14#include <QtGui/QGuiApplication>
15#include <QtGui/QStyleHints>
16
18
19static const QString kSystem = "System"_L1;
20static const QString kLight = "Light"_L1;
21static const QString kDark = "Dark"_L1;
22
23QQStyleKitStyle::QQStyleKitStyle(QObject *parent)
25 , m_paletteProxy(new QQuickPalette(this))
26 , m_themeName(kSystem)
27{
28}
29
31{
32 if (m_theme)
33 m_theme->deleteLater();
34}
35
36QQuickPalette *QQStyleKitStyle::palette() const
37{
38 return m_paletteProxy;
39}
40
42{
43 return m_light;
44}
45
47{
48 if (!m_fallbackStyle) {
49 auto *self = const_cast<QQStyleKitStyle *>(this);
50 self->executeFallbackStyle();
51 }
52 return m_fallbackStyle;
53}
54
56{
57 if (m_fallbackStyle == fallbackStyle)
58 return;
59
60 m_fallbackStyle = fallbackStyle;
61 emit fallbackStyleChanged();
62
63 if (palettes())
64 palettes()->setFallbackPalette(m_fallbackStyle ? m_fallbackStyle->palettes() : nullptr);
65
66 if (fonts())
67 fonts()->setFallbackFont(m_fallbackStyle ? m_fallbackStyle->fonts() : nullptr);
68
69}
70
71void QQStyleKitStyle::setLight(QQmlComponent *lightTheme)
72{
73 if (m_light == lightTheme)
74 return;
75
76 m_light = lightTheme;
77
78 emit lightChanged();
79}
80
82{
83 return m_dark;
84}
85
86void QQStyleKitStyle::setDark(QQmlComponent *darkTheme)
87{
88 if (m_dark == darkTheme)
89 return;
90
91 m_dark = darkTheme;
92
93 emit darkChanged();
94}
95
97{
98 return m_theme;
99}
100
101QList<QObject *> QQStyleKitStyle::customThemesAsList()
102{
103 QList<QObject *> list;
104 for (auto *customTheme : customThemes())
105 list.append(customTheme);
106 return list;
107}
108
110{
111 QList<QQStyleKitCustomTheme *> list;
112 for (auto *obj : children()) {
113 if (auto *customTheme = qobject_cast<QQStyleKitCustomTheme *>(obj))
114 list.append(customTheme);
115 }
116 return list;
117}
118
119void QQStyleKitStyle::parseThemes()
120{
121 m_themeNames = QStringList({kSystem, kLight, kDark});
122
123 for (auto *customTheme : customThemes()) {
124 const QString name = customTheme->name();
125 if (name.isEmpty())
126 continue;
127 m_themeNames << name;
128 m_customThemeNames << name;
129 }
130
131 emit themeNamesChanged();
132 emit customThemeNamesChanged();
133}
134
136{
137 return m_themeName;
138}
139
141{
142 return m_themeNames;
143}
144
146{
147 return m_customThemeNames;
148}
149
150void QQStyleKitStyle::setThemeName(const QString &themeName)
151{
152 if (m_themeName == themeName)
153 return;
154
155 m_themeName = themeName;
156 recreateTheme();
157
158 emit themeNameChanged();
159}
160
161void QQStyleKitStyle::recreateTheme()
162{
163 QString effectiveThemeName;
164 QQmlComponent *effectiveThemeComponent = nullptr;
165
166 if (QString::compare(m_themeName, kSystem, Qt::CaseInsensitive) == 0) {
167 const auto scheme = QGuiApplication::styleHints()->colorScheme();
168 if (scheme == Qt::ColorScheme::Light) {
169 effectiveThemeName = kLight;
170 effectiveThemeComponent = m_light;
171 }
172 else if (scheme == Qt::ColorScheme::Dark) {
173 effectiveThemeName =kDark;
174 effectiveThemeComponent = m_dark;
175 }
176 } else if (QString::compare(m_themeName, kLight, Qt::CaseInsensitive) == 0) {
177 effectiveThemeName = kLight;
178 effectiveThemeComponent = m_light;
179 } else if (QString::compare(m_themeName,kDark, Qt::CaseInsensitive) == 0) {
180 effectiveThemeName =kDark;
181 effectiveThemeComponent = m_dark;
182 } else if (!m_themeName.isEmpty()){
183 for (auto *customTheme : customThemes()) {
184 if (QString::compare(m_themeName, customTheme->name(), Qt::CaseInsensitive) == 0) {
185 effectiveThemeName = customTheme->name();
186 effectiveThemeComponent = customTheme->theme();
187 break;
188 }
189 }
190 if (effectiveThemeName.isEmpty())
191 qmlWarning(this) << "No theme found with name:" << m_themeName;
192 else if (!effectiveThemeComponent)
193 qmlWarning(this) << "Custom theme '" << effectiveThemeName << "' has no theme component set";
194 }
195
196 if (m_effectiveThemeName == effectiveThemeName) {
197 // Switching theme name from e.g "System" to "Light" might not
198 // actually change the currently effective theme.
199 emit themeNameChanged();
200 return;
201 }
202
203 if (m_theme) {
204 m_theme->deleteLater();
205 m_theme = nullptr;
206 }
207
208 m_currentThemeComponent = effectiveThemeComponent;
209
210 if (effectiveThemeComponent) {
211 if (effectiveThemeComponent->status() != QQmlComponent::Ready) {
212 qmlWarning(this) << "failed to create theme '" << effectiveThemeName << "': " << effectiveThemeComponent->errorString();
213 } else {
214 /* The 'createThemeInsideStyle' JS function is a work-around since we haven't found
215 * a way to instantiate a Theme inside the context of a Style from c++. Doing so is
216 * needed in order to allow custom style properties to be added as children of a
217 * Style, and at the same time, be able to access them from within a Theme. For
218 * this to work, the Style also needs to set 'pragma ComponentBehavior: Bound'. */
219 QVariant themeAsVariant;
220 QMetaObject::invokeMethod(this, "createThemeInsideStyle", Qt::DirectConnection,
221 qReturnArg(themeAsVariant), QVariant::fromValue(effectiveThemeComponent));
222 m_theme = qvariant_cast<QQStyleKitTheme *>(themeAsVariant);
223
224 if (!m_theme || !effectiveThemeComponent->errorString().isEmpty()) {
225 qmlWarning(this) << "failed to create theme '" << effectiveThemeName << "': " << effectiveThemeComponent->errorString();
226 } else {
227 m_theme->setParent(this);
228 }
229 }
230 }
231
232 if (!m_theme) {
233 // We always require a theme, even if it's empty
234 m_theme = new QQStyleKitTheme(this);
235 m_theme->setObjectName("<empty theme>"_L1);
236 m_theme->m_completed = true;
237 } else {
238 m_theme->setParent(this);
239 }
240
241 if (m_theme->fonts())
242 m_theme->fonts()->setFallbackFont(fonts());
243 if (m_theme->palettes())
244 m_theme->palettes()->setFallbackPalette(palettes());
245 if (this == current()) {
246 m_theme->updateThemePalettes();
247 m_theme->updateThemeFonts();
249 }
250
251 emit themeChanged();
252}
253
255{
256 return QQStyleKit::qmlAttachedProperties()->style();
257}
258
259QPalette QQStyleKitStyle::paletteForControlType(QQStyleKitExtendableControlType type) const
260{
261 return m_theme->paletteForControlType(type);
262}
263
264QFont QQStyleKitStyle::fontForControlType(QQStyleKitExtendableControlType type) const
265{
266 return m_theme->fontForControlType(type);
267}
268
270{
271 /* Before both the style and theme has completed loading
272 * we return false. This can be used to avoid unnecessary
273 * property reads when we anyway have to do a full update
274 * in the end. */
275 if (!m_completed)
276 return false;
277 if (!m_theme || !m_theme->m_completed)
278 return false;
279
280 return true;
281}
282
283void QQStyleKitStyle::executeFallbackStyle(bool complete)
284{
285 if (m_fallbackStyle.wasExecuted())
286 return;
287
288 const QString name = "fallbackStyle"_L1;
289 if (!m_fallbackStyle || complete)
290 quickBeginDeferred(this, name, m_fallbackStyle);
291 if (complete)
292 quickCompleteDeferred(this, name, m_fallbackStyle);
293}
294
295void QQStyleKitStyle::syncFromQPalette(const QPalette &palette)
296{
297 if (m_isUpdatingPalette)
298 return;
299 QScopedValueRollback<bool> rb(m_isUpdatingPalette, true);
300 if (palette == m_effectivePalette)
301 return;
302 m_effectivePalette = palette;
303 m_paletteProxy->fromQPalette(m_effectivePalette);
304 emit paletteChanged();
305}
306
307QPalette QQStyleKitStyle::effectivePalette() const
308{
309 return m_effectivePalette;
310}
311
313{
315
316 /* It's important to set m_completed before creating the theme, otherwise
317 * styleAndThemeFinishedLoading() will still be false, which will e.g cause
318 * property reads to return early from QQStyleKitPropertyResolver */
319 m_completed = true;
320
321 executeFallbackStyle(true);
322 parseThemes();
323 recreateTheme();
324}
325
326QT_END_NAMESPACE
327
328#include "moc_qqstylekitstyle_p.cpp"
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
QQStyleKitPalette * palettes()
QQStyleKitFont * fonts()
QList< QQStyleKitCustomTheme * > customThemes() const
QStringList themeNames() const
QQmlComponent * dark() const
QQmlComponent * light() const
QStringList customThemeNames() const
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
QQStyleKitStyle * fallbackStyle() const
QQStyleKitTheme * theme() const
static QQStyleKitStyle * current()
QQuickPalette * palette() const
void setThemeName(const QString &themeName)
void setDark(QQmlComponent *darkTheme)
QPalette paletteForControlType(QQStyleKitExtendableControlType type) const
QFont fontForControlType(QQStyleKitExtendableControlType type) const
void setLight(QQmlComponent *lightTheme)
QString themeName() const
void setFallbackStyle(QQStyleKitStyle *fallbackStyle)
Combined button and popup list for selecting options.
static const QString kDark
static const QString kLight
static QT_BEGIN_NAMESPACE const QString kSystem