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
qqstylekitvariation.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
8
10
11/*!
12 \qmltype StyleVariation
13 \inqmlmodule Qt.labs.StyleKit
14 \inherits AbstractStylableControls
15 \brief Defines alternative styling for specific controls.
16
17
18 A StyleVariation lets you define alternative styling that can be applied
19 to specific controls in the application (\e{instance variations}),
20 or to all descendants of a particular parent control type
21 (\e{type variations}). For example, you can give controls in a sidebar a
22 compact appearance, or make buttons inside all \l {ToolBar}{toolbars} look
23 different from buttons elsewhere.
24
25 \section2 Instance Variations
26
27 Instance variations are activated by setting the \l {StyleVariation::}{variations}
28 attached property on any item in the application — all StyleKit controls that are
29 children or descendants of that item will receive the alternative styling. Variations
30 can also be set at multiple levels of the hierarchy simultaneously; when they conflict,
31 the variation closest to the control takes precedence.
32
33 Instance variations are defined in the \l Style or \l Theme that they affect:
34
35 \snippet InstanceVariationSnippets.qml instance variations in style
36
37 And they are applied from the application using the
38 \l {StyleVariation::}{variations} attached property:
39
40 \snippet InstanceVariationSnippets.qml apply instance variation
41
42 Variation names referred to from the application that are not defined in the active
43 style (or theme) are silently ignored — they act as hints that the style author can
44 choose to implement or leave unhandled.
45
46 \section2 Type Variations
47
48 Type variations are assigned to the \l {ControlStyle::}{variations} property of a
49 specific \l ControlStyle, without requiring a \l name. Unlike instance variations —
50 which require the application to explicitly opt in — type variations are applied
51 automatically to all controls of a given type whenever they are descendants of the
52 specified parent control type.
53
54 The following snippet shows how to make all \l {Button}{Buttons} inside a \l Frame look
55 distinct from buttons elsewhere:
56
57 \snippet TypeVariationSnippets.qml frame with variation
58
59 Because \l {AbstractStylableControls::}{groupBox} falls back to
60 \l {AbstractStylableControls::}{frame} in the style hierarchy, type variations
61 set on \c frame are automatically inherited by \c groupBox as well. To opt out,
62 reset the variations for the subtype.
63
64 \section2 Propagation order
65
66 A StyleVariation is local to the \l Style or \l Theme where it is defined. A Theme
67 cannot shadow an entire StyleVariation defined in the Style — only individual
68 properties within it can be overridden, just like any other style property.
69
70 For example, if both the \l Style and the active \l Theme define \c {frame.variations},
71 both type variation arrays take effect for a \l Frame. The resolution order for
72 individual properties is: the Theme's variation properties take precedence over the
73 Theme's direct properties, which take precedence over the Style's variation properties,
74 which take precedence over the Style's direct properties.
75
76 The same applies to instance variations. A StyleVariation defined in a \l Theme takes
77 precedence over the Theme's direct properties. If a StyleVariation with the same
78 \l name is also defined in the \l Style, both take effect, following the same
79 resolution order as described above.
80
81 In the following snippet, a button with \c {StyleVariation.variations: ["alert"]}
82 will be red in the light theme and cyan in the dark theme, with a 4-pixel border in
83 both. Because the dark theme overrides \c {button.background.radius} to be \c 6,
84 that property takes precedence over the \c {background.radius} set in the Style's
85 variation. As a result, the button's radius ends up being \c 6 in the dark theme, but \c 0
86 in the light theme:
87
88 \snippet VariationPropagation.qml propagation
89
90 \labs
91
92 \sa Style, Theme, {ControlStyle::variations}{ControlStyle.variations},
93 {StyleVariation::variations}{StyleVariation.variations}
94*/
95
96/*!
97 \qmlproperty string StyleVariation::name
98
99 The name of this variation.
100
101 The name identifies this variation when used as an
102 \l {Instance Variations}{instance variation}.
103
104 \sa {StyleVariation::variations}{variations} {Instance Variations}
105*/
106
107/*!
108 \qmlattachedproperty list<string> StyleVariation::variations
109
110 This property holds the list of \l{StyleVariation}{instance variations}
111 to activate for a \l Control, and its descendant controls.
112
113 If multiple variations in the list sets the same property, the first
114 one in the list takes precedence.
115
116 If variations are set on both a parent item and a descendant control, both
117 sets apply, with the control's own variations taking precedence
118 over those inherited from the parent.
119
120 \snippet InstanceVariationSnippets.qml apply instance variation
121
122 \sa name
123*/
124
125/*!
126 \qmlattachedproperty int StyleVariation::controlType
127
128 This property identifies the \l {StyleReader::controlType}{control type} of
129 the item it is attached to.
130
131 StyleKit uses it to resolve \l {Type Variations}{type variations} for descendant
132 controls — if a parent item's \c controlType matches a control type that has
133 \l {ControlStyle::}{variations} defined in the \l Style, those variations apply
134 to all descendant controls.
135
136 Built-in controls set this property automatically. Custom controls
137 must set it explicitly to participate in type variation resolution.
138
139 \sa {ControlStyle::variations}{ControlStyle.variations},
140 {StyleReader::controlType}{StyleReader.controlType}
141*/
142
147
149{
151
152 /* Whenever there is a Style or Theme change, the list of StyleVariations that affects a
153 * StyleReader needs to be rebuilt. And in order to know which StyleVariations that should
154 * be taken into consideration, we need to know which Style each StyleVariation belongs to. */
155 bool styleOrThemeFound = false;
156 for (QObject *current = parent(); current; current = current->parent()) {
157 if (auto *styleOrTheme = qobject_cast<QQStyleKitStyleAndThemeBase *>(current)) {
158 styleOrTheme->m_styleVariations.append(this);
159 styleOrThemeFound = true;
160 break;
161 }
162 }
163 if (!styleOrThemeFound)
164 qmlWarning(this) << "A StyleVariation needs to be a descendant of the Style it belongs to!";
165}
166
167QQStyleKitVariationAttached *QQStyleKitVariation::qmlAttachedProperties(QObject *object)
168{
169 return new QQStyleKitVariationAttached(object);
170}
171
173{
174 return m_name;
175}
176
177void QQStyleKitVariation::setName(const QString &name)
178{
179 if (m_name == name)
180 return;
181
182 m_name = name;
183 emit nameChanged();
184}
185
186QQStyleKitVariationAttached::QQStyleKitVariationAttached(QObject *parent)
187 : QObject(parent)
188{
189}
190
192{
193 return m_variations;
194}
195
196void QQStyleKitVariationAttached::setVariations(const QStringList &variations)
197{
198 if (m_variations == variations)
199 return;
200
201 m_variations = variations;
202 emit variationsChanged();
203}
204
209
210void QQStyleKitVariationAttached::setControlType(QQStyleKitExtendableControlType type)
211{
212 if (m_controlType == type)
213 return;
214
215 m_controlType = type;
216 emit controlTypeChanged();
217}
218
219void QQStyleKitVariation::resetVariationsForStyle(QQStyleKitStyle *style)
220{
221 /* The usage context stored in a StyleVariation tells which Style or Theme uses it.
222 * After a theme change, we therefore need to rebuild this context list, since the
223 * old theme needs to be removed, and the new theme might need to be added.
224 * Since we also need to update which variations will now be effective for the existing
225 * StyleReaders, we simply clear the context here, and leave it to QQStyleKitPropertyResolver
226 * to rebuild both the effective variations and the context in one pass later.
227 * (QQStyleKitReader::resetReadersForStyle() will set the m_effectiveVariationsDirty
228 * flag, which will trigger the rebuild).
229 *
230 * As an optimization, we also update m_hasVariations to reflect whether any variations
231 * exist at all. If not, variations don't need to be taken into consideration during
232 * style property look-up. */
233 style->m_hasVariations = false;
234
235 for (QQStyleKitStyle *current = style; current; current = current->fallbackStyle()) {
236 if (!current->m_styleVariations.isEmpty()) {
237 style->m_hasVariations = true;
238 for (QQStyleKitVariation *variation : current->m_styleVariations)
239 variation->m_usageContext.clear();
240 }
241
242 if (QQStyleKitTheme *theme = current->theme()) {
243 if (!theme->m_styleVariations.isEmpty()) {
244 style->m_hasVariations = true;
245 for (QQStyleKitVariation *variation : theme->m_styleVariations)
246 variation->m_usageContext.clear();
247 }
248 }
249 }
250}
251
252QT_END_NAMESPACE
253
254#include "moc_qqstylekitvariation_p.cpp"
QObject * parent
Definition qobject.h:74
\inmodule QtCore
Definition qobject.h:106
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
void setControlType(QQStyleKitExtendableControlType type)
QQStyleKitExtendableControlType controlType() const
void setVariations(const QStringList &variations)
void componentComplete() override
Invoked after the root component that caused this instantiation has completed construction.
void setName(const QString &name)
Combined button and popup list for selecting options.