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
qqstylekitreader.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 <QtQuick/private/qquickpalette_p.h>
5#include <QtQuick/private/qquickwindow_p.h>
6#include <QtQuick/private/qquickstategroup_p.h>
7#include <QtQuick/private/qquickpropertychanges_p.h>
8
9#include "qqstylekit_p.h"
13
15
16/*!
17 \qmltype StyleReader
18 \inqmlmodule Qt.labs.StyleKit
19 \inherits ControlStyleProperties
20 \brief Reads properties from the active Style for a specific control.
21
22 StyleReader is the bridge between a control in \l {Qt Quick Controls}
23 and the \l {StyleKit::style}{active style}. It exposes all the style
24 properties — \l {ControlStyleProperties::background}{backgrounds},
25 \l {ControlStyleProperties::indicator}{indicators},
26 \l {ControlStyleProperties::handle}{handles},
27 \l {ControlStyleProperties::text}{text},
28 \l {ControlStyleProperties::padding}{padding},
29 and more — that a control, and its delegates, should bind to.
30 All built-in \c StyleKit controls use a StyleReader
31 internally for this purpose.
32
33 The state properties — \l hovered, \l pressed, \l focused, \l checked,
34 \l highlighted, etc. — tell StyleReader which state the control is in.
35 It uses these to resolve and read the correct property values from
36 the \l Style, taking \l {Theme}{Themes}, \l {StyleVariation}{StyleVariations},
37 \l {StylableControls}{fallback types}, and property propagation
38 into account.
39
40 When implementing a \l CustomControl, you can follow the same approach as
41 the built-in controls:
42
43 \snippet StyleReaderSnippets.qml custom control
44
45 \note The style properties inherited from \l ControlStyleProperties do not map
46 directly to the \l Style. Instead, they reflect a cache of potentially interpolated
47 values during an active \l {ControlStyle::}{transition}. Writing to them will \e not
48 update the corresponding properties in the \l Style and may interfere with ongoing
49 transitions. Write directly to the \l Style when you need to change a style property.
50
51 \labs
52
53 \sa CustomControl, Style, {StyleVariation::controlType}{StyleVariation.controlType},
54 {StyleKit Property Resolution}
55*/
56
57/*!
58 \qmlproperty enumeration StyleReader::controlType
59
60 Identifies which control type in the \l Style this reader reads
61 properties from. This can either be set to one of the predefined values
62 below, or to a \l {CustomControl::controlType}{custom control type} defined in the
63 \l Style.
64
65 \value StyleReader.BusyIndicator \l {BusyIndicator}
66 \value StyleReader.Button \l {Button}
67 \value StyleReader.CheckBox \l {CheckBox}
68 \value StyleReader.CheckDelegate \l {CheckDelegate}
69 \value StyleReader.ComboBox \l {ComboBox}
70 \value StyleReader.DelayButton \l {DelayButton}
71 \value StyleReader.Dial \l {Dial}
72 \value StyleReader.Dialog \l {Dialog}
73 \value StyleReader.DialogButtonBox \l {DialogButtonBox}
74 \value StyleReader.Drawer \l {Drawer}
75 \value StyleReader.Frame \l {Frame}
76 \value StyleReader.GroupBox \l {GroupBox}
77 \value StyleReader.HorizontalHeaderViewDelegate \l {HorizontalHeaderViewDelegate}
78 \value StyleReader.ItemDelegate \l {ItemDelegate}
79 \value StyleReader.Label \l {Label}
80 \value StyleReader.Menu \l {Menu}
81 \value StyleReader.MenuBar \l {MenuBar}
82 \value StyleReader.MenuBarItem \l {MenuBarItem}
83 \value StyleReader.MenuItem \l {MenuItem}
84 \value StyleReader.MenuSeparator \l {MenuSeparator}
85 \value StyleReader.Page \l {Page}
86 \value StyleReader.PageIndicator \l {PageIndicator}
87 \value StyleReader.Pane \l {Pane}
88 \value StyleReader.Popup \l {Popup}
89 \value StyleReader.ProgressBar \l {ProgressBar}
90 \value StyleReader.RadioButton \l {RadioButton}
91 \value StyleReader.RadioDelegate \l {RadioDelegate}
92 \value StyleReader.RoundButton \l {RoundButton}
93 \value StyleReader.ScrollBar \l {ScrollBar}
94 \value StyleReader.ScrollView \l {ScrollView}
95 \value StyleReader.SearchField \l {SearchField}
96 \value StyleReader.Slider \l {Slider}
97 \value StyleReader.SpinBox \l {SpinBox}
98 \value StyleReader.SwipeDelegate \l {SwipeDelegate}
99 \value StyleReader.Switch \l {Switch}
100 \value StyleReader.SwitchDelegate \l {SwitchDelegate}
101 \value StyleReader.TabBar \l {TabBar}
102 \value StyleReader.TabButton \l {TabButton}
103 \value StyleReader.TableViewDelegate \l {TableViewDelegate}
104 \value StyleReader.TextArea \l {TextArea}
105 \value StyleReader.TextField \l {TextField}
106 \value StyleReader.ToolBar \l {ToolBar}
107 \value StyleReader.ToolButton \l {ToolButton}
108 \value StyleReader.ToolSeparator \l {ToolSeparator}
109 \value StyleReader.ToolTip \l {ToolTip}
110 \value StyleReader.TreeViewDelegate \l {TreeViewDelegate}
111 \value StyleReader.Tumbler \l {Tumbler}
112 \value StyleReader.VerticalHeaderViewDelegate \l {VerticalHeaderViewDelegate}
113
114 \sa {StyleVariation::controlType}{StyleVariation.controlType}
115*/
116
117/*!
118 \qmlproperty bool StyleReader::enabled
119
120 Whether the control is enabled.
121
122 Bind this to the control's \l {Item::enabled}{enabled} property.
123
124 The default value is \c true.
125
126 \sa {ControlStateStyle::disabled}{ControlStateStyle.disabled}
127*/
128
129/*!
130 \qmlproperty bool StyleReader::hovered
131
132 Whether the control is hovered.
133
134 Bind this to the control's \l {Control::hovered}{hovered} property.
135
136 The default value is \c false.
137
138 \sa {ControlStateStyle::hovered}{ControlStateStyle.hovered}
139*/
140
141/*!
142 \qmlproperty bool StyleReader::pressed
143
144 Whether the control is pressed.
145
146 Bind this to the control's \l {AbstractButton::pressed}{pressed} property.
147
148 The default value is \c false.
149
150 \sa {ControlStateStyle::pressed}{ControlStateStyle.pressed}
151*/
152
153/*!
154 \qmlproperty bool StyleReader::focused
155
156 Whether the control has active focus.
157
158 Bind this to the control's \l {Item::activeFocus}{activeFocus} property.
159
160 The default value is \c false.
161
162 \sa {ControlStateStyle::focused}{ControlStateStyle.focused}
163*/
164
165/*!
166 \qmlproperty bool StyleReader::checked
167
168 Whether the control is checked.
169
170 Bind this to the control's \l {AbstractButton::checked}{checked} property.
171
172 The default value is \c false.
173
174 \sa {ControlStateStyle::checked}{ControlStateStyle.checked}
175*/
176
177/*!
178 \qmlproperty bool StyleReader::highlighted
179
180 Whether the control is highlighted.
181
182 Bind this to the control's \l {Button::highlighted}{highlighted} property.
183
184 The default value is \c false.
185
186 \sa {ControlStateStyle::highlighted}{ControlStateStyle.highlighted}
187*/
188
189/*!
190 \qmlproperty bool StyleReader::vertical
191
192 Whether the control is oriented vertically.
193
194 Bind this to the control's \l {Slider::orientation}{orientation}.
195
196 The default value is \c false.
197
198 \sa {ControlStateStyle::vertical}{ControlStateStyle.vertical}
199*/
200
201/*!
202 \qmlproperty palette StyleReader::palette
203
204 The palette of the control. StyleKit uses this to resolve color properties
205 that bind the \l {Style::}{palette} in the \l Style.
206
207 Bind this to the control's \l {Control::palette}{palette} property.
208
209 \sa {Style::palette}{Style.palette}
210*/
211
212/*!
213 \qmlproperty font StyleReader::font
214 \readonly
215
216 The effective font for this control type, as defined by the
217 \l {AbstractStyle::fonts}{style}. This also takes into account any font
218 overrides set in the \l {ControlStyle::}{text} properties of the style.
219
220 Bind the control's \l {Control::font}{font} property to this property.
221
222 \note Unlike properties such as \l hovered, \l pressed and \l palette — which
223 is forwarded from the control to the StyleReader — \l font is an output. Bind the
224 control's \l {Control::font}{font} property to this value, not the other way around.
225*/
226
227/*!
228 \qmlproperty ControlStyleProperties StyleReader::global
229 \readonly
230
231 Provides direct access to the style properties, bypassing any ongoing
232 \l {ControlStyle::}{transition}.
233
234 While a state transition is ongoing, style properties read from a StyleReader
235 may return interpolated values. By prepending \c global to the property path,
236 you bypass the transition and get the end-state values immediately.
237
238 For example, when transitioning from \l hovered to \l pressed, \c {background.color}
239 may return an interpolated value between the \l {DelegateStyle::}{color} in the
240 \l {ControlStateStyle::}{hovered} state and the color in the
241 \l {ControlStateStyle::}{pressed} state. \c {global.background.color}, on the
242 other hand, returns the color in the pressed state directly.
243
244 \sa {StyleKit::transitionsEnabled}{transitionsEnabled}
245*/
246
247using namespace Qt::StringLiterals;
248
249static constexpr QLatin1StringView kAlternate1 = "A1"_L1;
250static constexpr QLatin1StringView kAlternate2 = "A2"_L1;
251
253{
254 if (!t)
255 return 0;
256
257 quint64 sig = 0;
258
259 // bit 0: bold defined, bit 1: bold value
260 if (t->isDefined(QQSK::Property::Bold)) {
261 sig |= (quint64(1) << 0);
262 if (t->styleProperty<bool>(QQSK::Property::Bold))
263 sig |= (quint64(1) << 1);
264 }
265 // bit 2: italic defined, bit 3: italic value
266 if (t->isDefined(QQSK::Property::Italic)) {
267 sig |= (quint64(1) << 2);
268 if (t->styleProperty<bool>(QQSK::Property::Italic))
269 sig |= (quint64(1) << 3);
270 }
271 // bit 4: pointSize defined, bits 5..: quantized pointSize
272 if (t->isDefined(QQSK::Property::PointSize)) {
273 sig |= (quint64(1) << 4);
274 const qreal ps = t->styleProperty<qreal>(QQSK::Property::PointSize);
275 // 64-bit signature: 5 bits used, 59 bits available for pointSize
276 constexpr int payloadBits = 64 - 5;
277 const qint64 maxQ = (quint64(1) << payloadBits) - 1;
278 const quint64 q = quint64(qBound<qint64>(0, qRound64(ps * 64.0), maxQ));
279 sig |= (q << 5);
280 }
281 return sig;
282}
283
284QList<QQStyleKitReader *> QQStyleKitReader::s_allReaders;
285QQStyleKitReader::PropertyChangesComponents QQStyleKitReader::s_propertyChangesComponents;
286
287QQStyleKitReader::QQStyleKitReader(QObject *parent)
288 : QQStyleKitControlProperties(QQSK::PropertyGroup::Control, parent)
289 , m_dontEmitChangedSignals(false)
290 , m_effectiveVariationsDirty(true)
291 , m_transitionsEnabled(true)
292 , m_completed(false)
293 , m_global(QQStyleKitControlProperties(QQSK::PropertyGroup::GlobalFlag, this))
294{
295}
296
297QQStyleKitReader::~QQStyleKitReader()
298{
299 s_allReaders.removeOne(this);
300}
301
302QQuickStateGroup *QQStyleKitReader::stateGroup()
303{
304 if (m_stateGroup)
305 return m_stateGroup;
306
307 /* Lazy create a QQuickStateGroup as soon as we have delegates
308 * that needs to be "tracked". That is, the user of this StyleKitReader
309 * has read one or more properties, so we need to check and emit changes for
310 * those properties whenever our state changes. */
311 const auto *stylePtr = style();
312 Q_ASSERT(stylePtr);
313
314 m_stateGroup = new QQuickStateGroup(this);
315
316 // Add two states that we can alternate between
317 auto statesProp = m_stateGroup->statesProperty();
318 QQuickState *alternate1 = new QQuickState(m_stateGroup);
319 QQuickState *alternate2 = new QQuickState(m_stateGroup);
320 alternate1->setName(kAlternate1);
321 alternate2->setName(kAlternate2);
322 m_stateGroup->statesProperty().append(&statesProp, alternate1);
323 m_stateGroup->statesProperty().append(&statesProp, alternate2);
324
325 QQmlComponent *controlComp = createControlChangesComponent();
326 instantiatePropertyChanges(controlComp);
327
328 return m_stateGroup;
329}
330
331QQmlComponent *QQStyleKitReader::createControlChangesComponent() const
332{
333 QQmlEngine *engine = qmlEngine(style());
334 auto key = PropertyChangesComponents::key_type{engine, u"control"_s};
335 if (auto r = s_propertyChangesComponents.value(key, nullptr))
336 return r.data();
337
338 const QString qmlControlCode = QString::fromUtf8(R"(
339 import QtQuick
340 PropertyChanges {
341 spacing: global.spacing
342 padding: global.padding
343 leftPadding: global.leftPadding
344 rightPadding: global.rightPadding
345 topPadding: global.topPadding
346 bottomPadding: global.bottomPadding
347 text.color: global.text.color
348 text.alignment: global.text.alignment
349 text.bold: global.text.bold
350 text.italic: global.text.italic
351 text.pointSize: global.text.pointSize
352 text.padding: global.text.padding
353 text.leftPadding: global.text.leftPadding
354 text.rightPadding: global.text.rightPadding
355 text.topPadding: global.text.topPadding
356 text.bottomPadding: global.text.bottomPadding
357 }
358 )");
359
360 // TODO: cache propertyName to component!
361 QQmlComponent *component = new QQmlComponent(engine, engine);
362 component->setData(qmlControlCode.toUtf8(), QUrl());
363 Q_ASSERT_X(!component->isError(), __FUNCTION__, component->errorString().toUtf8().constData());
364 s_propertyChangesComponents.insert(key, component);
365 QObject::connect(engine, &QObject::destroyed, engine, [key = std::move(key)] {
366 s_propertyChangesComponents.remove(key);
367 });
368 return component;
369}
370
371QQmlComponent *QQStyleKitReader::createDelegateChangesComponent(const QString &delegateName) const
372{
373 QQmlEngine *engine = qmlEngine(style());
374 auto key = PropertyChangesComponents::key_type{engine, delegateName};
375 if (auto r = s_propertyChangesComponents.value(key, nullptr))
376 return r.data();
377
378 static const QString qmlTemplateCode = QString::fromUtf8(R"(
379 import QtQuick
380 PropertyChanges { $ {
381 width: global.$.width
382 height: global.$.height
383 visible: global.$.visible
384 color: global.$.color
385 gradient: global.$.gradient
386 radius: global.$.radius
387 topLeftRadius: global.$.topLeftRadius
388 topRightRadius: global.$.topRightRadius
389 bottomLeftRadius: global.$.bottomLeftRadius
390 bottomRightRadius: global.$.bottomRightRadius
391 margins: global.$.margins
392 alignment: global.$.alignment
393 leftMargin: global.$.leftMargin
394 rightMargin: global.$.rightMargin
395 topMargin: global.$.topMargin
396 bottomMargin: global.$.bottomMargin
397 scale: global.$.scale
398 rotation: global.$.rotation
399 opacity: global.$.opacity
400 border.color: global.$.border.color
401 border.width: global.$.border.width
402 shadow.color: global.$.shadow.color
403 shadow.scale: global.$.shadow.scale
404 shadow.blur: global.$.shadow.blur
405 shadow.visible: global.$.shadow.visible
406 shadow.opacity: global.$.shadow.opacity
407 shadow.verticalOffset: global.$.shadow.verticalOffset
408 shadow.horizontalOffset: global.$.shadow.horizontalOffset
409 shadow.delegate: global.$.shadow.delegate
410 image.source: global.$.image.source
411 image.color: global.$.image.color
412 image.fillMode: global.$.image.fillMode
413 delegate: global.$.delegate
414 data: global.$.data
415 }}
416 )");
417
418 QString substitutedCode = qmlTemplateCode;
419 substitutedCode.replace('$'_L1, delegateName);
420 QQmlComponent *component = new QQmlComponent(engine, engine);
421 component->setData(substitutedCode.toUtf8(), QUrl());
422 Q_ASSERT_X(!component->isError(), __FUNCTION__, component->errorString().toUtf8().constData());
423 s_propertyChangesComponents.insert(key, component);
424 QObject::connect(engine, &QObject::destroyed, engine, [key = std::move(key)] {
425 s_propertyChangesComponents.remove(key);
426 });
427 return component;
428}
429
430void QQStyleKitReader::instantiatePropertyChanges(QQmlComponent *comp)
431{
432 QObject *obj = comp->create(qmlContext(this));
433 auto *propertyChanges = qobject_cast<QQuickPropertyChanges *>(obj);
434 Q_ASSERT(propertyChanges);
435
436 // setter for the "target" property is called setObject
437 propertyChanges->setObject(this);
438 /* set "explicit" to true, meaning that the StyleProperties shouldn't
439 * create bindings, but do one-off assignments. Bindings are not needed
440 * here since it's the state changes of this StyleKitReader that
441 * drives the property changes. The properties cannot change outside of
442 * a state change (or, if they do, it will trigger a full update equal
443 * to a theme change) */
444 propertyChanges->setIsExplicit(true);
445 /* We don't need to ever restore the properties back to default, since
446 * the group state will never be reset back to an empty string. This will
447 * hopefully avoid the generation of restore structures inside the StateGroup. */
448 propertyChanges->setRestoreEntryValues(false);
449
450 /* Add the new PropertyChanges object to both states, A1 and A2 */
451 for (QQuickState *state : stateGroup()->states()) {
452 auto changesProp = state->changes();
453 changesProp.append(&changesProp, propertyChanges);
454 }
455}
456
457void QQStyleKitReader::maybeTrackDelegates()
458{
459 forEachUsedDelegate(
460 [this](QQStyleKitDelegateProperties *delegate, QQSK::Delegate type, const QString &delegatePath){
461 if (m_trackedDelegates.testFlag(type)) {
462 // We're already tracking the delegate. So nothing needs to be done.
463 return;
464 }
465 if (!delegate->visible()) {
466 /* As an optimization, if the delegate is hidden, we don't track it. Most
467 * controls set background.visible to false, for example. If this, for
468 * whatever reason, is not wanted, set opacity to 0 instead. */
469 return;
470 }
471 /* Invariant: The application has read one or more properties for the given delegate
472 * from the Style, but we don't yet have a PropertyChanges object that can track
473 * changes to it (and run transitions). So we create one now. By lazy creating them this
474 * way, we avoid creating PropertyChanges for all the different delegates that a control
475 * _may_ have. Instead, we only track changes for the delegates it actually uses. */
476 m_trackedDelegates.setFlag(type);
477 QQmlComponent *comp = createDelegateChangesComponent(delegatePath);
478 instantiatePropertyChanges(comp);
479 });
480}
481
482void QQStyleKitReader::updateControl(bool applyStateChange)
483{
484 const QQStyleKitStyle *currentStyle = style();
485 if (!m_completed || !currentStyle || !currentStyle->loaded())
486 return;
487
488 if (applyStateChange) {
489 /* Alternate between two states to trigger a state change. The state group
490 * will, upon changing state, take care of reading the updated property values,
491 * compare them against the current ones in the local storage, and emit changes
492 * (possibly using a transition) if changed. Since the new state might change the
493 * transition, we need to update it first before we do the state change, so that
494 * it takes effect.
495 * If we have skipped tracking some delegates because they are hidden, we need to
496 * check again if this is still the case for the current state. Otherwise, we now
497 * need to track them. Untracked delegates are not backed by PropertyChanges objects,
498 * and hence, will not update when we do a state swap below.
499 * Note that the first time this function is called after start-up, none of the
500 * delegates are yet tracked, and therefore will be created now. */
501
502 maybeTrackDelegates();
503
504 auto transitionProp = stateGroup()->transitionsProperty();
505 const int transitionCountInStateGroup = transitionProp.count(&transitionProp);
506 const bool enabled = m_transitionsEnabled && QQStyleKit::qmlAttachedProperties()->transitionsEnabled();
507 QQuickTransition *transitionInStyle = enabled ? transition() : nullptr;
508 QQuickTransition *transitionInStateGroup =
509 transitionCountInStateGroup > 0 ? transitionProp.at(&transitionProp, 0) : nullptr;
510 if (transitionInStyle != transitionInStateGroup) {
511 transitionProp.clear(&transitionProp);
512 if (transitionInStyle)
513 transitionProp.append(&transitionProp, transitionInStyle);
514 }
515
516 switch (m_alternateState) {
517 case AlternateState::Alternate1:
518 m_alternateState = AlternateState::Alternate2;
519 stateGroup()->setState(kAlternate2);
520 break;
521 case AlternateState::Alternate2:
522 m_alternateState = AlternateState::Alternate1;
523 stateGroup()->setState(kAlternate1);
524 break;
525 default:
526 Q_UNREACHABLE();
527 }
528 } else {
529 // Since no state change occurs, the property values in
530 // the local storage no longer reflect this StyleReader's
531 // current state. Clear it to avoid returning stale values
532 // from subsequent property reads.
533 clearLocalStorage();
534 }
536 auto textOverrideSig = textFontOverridesSignature(global()->text());
537 if (m_lastTextFontOverridesSignature != textOverrideSig)
538 m_fontDirty = true;
539 m_lastTextFontOverridesSignature = textOverrideSig;
540 rebuildEffectiveFont();
541}
542
543void QQStyleKitReader::resetReadersForStyle(const QQStyleKitStyle *style)
545 for (QQStyleKitReader *reader : s_allReaders) {
546 if (reader->style() == style) {
547 reader->m_effectiveVariationsDirty = true;
548 reader->m_fontDirty = true;
549 reader->clearLocalStorage();
550 reader->rebuildEffectivePalette();
551 reader->rebuildEffectiveFont();
552 reader->emitChangedForAllStyleProperties(EmitFlag::AllProperties);
553 }
554 }
555}
556
557void QQStyleKitReader::populateLocalStorage()
558{
559 if (!m_completed) {
560 /* The local storage is used for implementing transitions and to ensure that we
561 * only emit changed signals for properties that actually change during a state
562 * change. During start-up, however, all properties are considered dirty, so we
563 * can delay populating the storage until the first time we get a state change
564 * after completed, to improve start-up performance. */
565 return;
566 }
567
568 if (!m_storage.isEmpty())
569 return;
570 const auto *stylePtr = style();
571 if (!stylePtr || !stylePtr->loaded())
572 return;
573
574 /* The local storage is empty, which is typically the case after an
575 * operation that should perform without a transition, such as a theme
576 * change or a change to a property value in the Style itself.
577 * Doing a transition in that case is unwanted and slow (since the
578 * operation typically affect all controls), so we short-cut the process by
579 * emitting changed signals directly for all the properties instead.
580 * Since that will render the local storage out-of-sync, we clear it at the
581 * same time to signal that it's 'dirty'. Which is why we need to sync it back
582 * up again now.
583 * Syncing the local storage before changing state (even if the values that
584 * end up in the storage should be exactly the same as those already
585 * showing), has the upshot that we can compare after the state change which
586 * properties has changed, which will limit the amount of changed signals we
587 * then need to emit. */
588 m_dontEmitChangedSignals = true;
589 updateControl();
590 m_dontEmitChangedSignals = false;
591}
592
593void QQStyleKitReader::clearLocalStorage()
594{
595 /* Clear all the local property overrides that has been set on this reader. Such
596 * overrides are typically interpolated values set by a transition during a state
597 * change. By clearing them, the controls will end up reading the property values
598 * directly from the Style instead. */
599 m_storage.clear();
600}
601
602QQSK::State QQStyleKitReader::controlState() const
603{
604 QQSK::State effectiveState = m_state;
605
606 if (!enabled()) {
607 // Some states are not valid if the control is disabled
608 effectiveState &= ~(QQSK::StateFlag::Pressed |
609 QQSK::StateFlag::Hovered |
610 QQSK::StateFlag::Highlighted |
611 QQSK::StateFlag::Focused |
612 QQSK::StateFlag::Hovered);
613 }
614
615 if (effectiveState == QQSK::StateFlag::Unspecified)
616 effectiveState.setFlag(QQSK::StateFlag::Normal);
617
618 return effectiveState;
619}
621QVariant QQStyleKitReader::readStyleProperty(PropertyStorageId key) const
622{
623 return m_storage.value(key);
624}
626void QQStyleKitReader::writeStyleProperty(PropertyStorageId key, const QVariant &value)
627{
628 m_storage.insert(key, value);
629}
630
631bool QQStyleKitReader::dontEmitChangedSignals() const
632{
633 return m_dontEmitChangedSignals;
634}
635
636QQStyleKitExtendableControlType QQStyleKitReader::controlType() const
637{
638 return m_type;
639}
640
641void QQStyleKitReader::setControlType(QQStyleKitExtendableControlType type)
642{
643 if (m_type == type)
644 return;
645
646 m_type = type;
647 populateLocalStorage();
648 emit controlTypeChanged();
649 updateControl();
650}
651
652#ifdef QT_DEBUG
653QQStyleKitReader::ControlType QQStyleKitReader::typeAsControlType() const
654{
655 /* Note: m_type is of type int to support extending the list
656 * of possible types from the Style itself. This function
657 * is here to for debugging purposes */
658 return ControlType(m_type);
659}
660#endif
661
662bool QQStyleKitReader::hovered() const
663{
664 return m_state.testFlag(QQSK::StateFlag::Hovered);
665}
666
667void QQStyleKitReader::setHovered(bool hovered)
669 if (hovered == QQStyleKitReader::hovered())
670 return;
671
672 populateLocalStorage();
673 m_state.setFlag(QQSK::StateFlag::Hovered, hovered);
674 emit hoveredChanged();
675 updateControl();
676}
677
678bool QQStyleKitReader::enabled() const
679{
680 return !m_state.testFlag(QQSK::StateFlag::Disabled);
681}
682
683void QQStyleKitReader::setEnabled(bool enabled)
685 if (enabled == QQStyleKitReader::enabled())
686 return;
687
688 populateLocalStorage();
689 m_state.setFlag(QQSK::StateFlag::Disabled, !enabled);
690 emit enabledChanged();
691 updateControl();
692}
693
694bool QQStyleKitReader::focused() const
695{
696 return m_state.testFlag(QQSK::StateFlag::Focused);
697}
698
699void QQStyleKitReader::setFocused(bool focused)
701 if (focused == QQStyleKitReader::focused())
702 return;
703
704 populateLocalStorage();
705 m_state.setFlag(QQSK::StateFlag::Focused, focused);
706 emit focusedChanged();
707 updateControl();
708}
709
710bool QQStyleKitReader::checked() const
711{
712 return m_state.testFlag(QQSK::StateFlag::Checked);
713}
714
715void QQStyleKitReader::setChecked(bool checked)
717 if (checked == QQStyleKitReader::checked())
718 return;
719
720 populateLocalStorage();
721 m_state.setFlag(QQSK::StateFlag::Checked, checked);
722 emit checkedChanged();
723 updateControl();
724}
725
726bool QQStyleKitReader::pressed() const
727{
728 return m_state.testFlag(QQSK::StateFlag::Pressed);
729}
730
731void QQStyleKitReader::setPressed(bool pressed)
732{
733 if (pressed == QQStyleKitReader::pressed())
734 return;
735
736 populateLocalStorage();
737 m_state.setFlag(QQSK::StateFlag::Pressed, pressed);
738 emit pressedChanged();
739 updateControl();
740}
742bool QQStyleKitReader::vertical() const
743{
744 return m_state.testFlag(QQSK::StateFlag::Vertical);
745}
746
747void QQStyleKitReader::setVertical(bool vertical)
748{
749 if (vertical == QQStyleKitReader::vertical())
750 return;
752 populateLocalStorage();
753 m_state.setFlag(QQSK::StateFlag::Vertical, vertical);
754 emit verticalChanged();
755 updateControl();
756}
757
758bool QQStyleKitReader::highlighted() const
759{
760 return m_state.testFlag(QQSK::StateFlag::Highlighted);
761}
762
763void QQStyleKitReader::setHighlighted(bool highlighted)
764{
765 if (highlighted == QQStyleKitReader::highlighted())
766 return;
767
768 populateLocalStorage();
769 m_state.setFlag(QQSK::StateFlag::Highlighted, highlighted);
770 emit highlightedChanged();
771 updateControl();
772}
773
774void QQStyleKitReader::setControlTypeAndState(QQStyleKitExtendableControlType controlType, QQSK::State flags,
775 bool applyStateChange)
776{
777 // Apply type and states in one shot to avoid calling
778 // populateLocalStorage() + updateControl() once per individual setter
779 const bool typeChanged = m_type != controlType;
780 const QQSK::State stateChanged = m_state ^ flags;
781
782 if (!typeChanged && !stateChanged)
783 return;
784
785 if (applyStateChange)
786 populateLocalStorage();
787
788 m_type = controlType;
789 m_state = flags;
790
791 updateControl(applyStateChange);
792}
793
794QObject *QQStyleKitReader::target() const
795{
796 return m_target.data();
797}
798
799void QQStyleKitReader::setTarget(QObject *target)
800{
801 /* A reader can optionally set a target object which represents
802 the current object that is being styled, ie: using this StyleReader
803 to read properties from the style. It is needed so that the widget style using
804 the StyleReader is able to post StyleAnimationUpdate events to the correct
805 target during a transition. */
806 m_target = target;
807}
808
809void QQStyleKitReader::setCompleted(bool completed)
811 const QQStyleKitStyle *currentStyle = style();
812 if (currentStyle && currentStyle->loaded()) {
813 clearLocalStorage();
814 rebuildEffectiveFont();
815 rebuildEffectivePalette();
816
817 /* Some style properties were read (e.g. by a Qt Quick Control) before this
818 * StyleReader was completed. Those reads were premature: the values depend not
819 * only on the style and theme, but also on the StyleReader's own state (such as
820 * its controlType, checked state, and so on), which may not have been fully
821 * initialized at that time. Now that all of it is in place, emit change signals
822 * for all properties to force the bindings to re-read them. */
823 emitChangedForAllStyleProperties(EmitFlag::AllProperties);
824 }
825
826 // From now on, we need to be notified when the style or theme changes
827 s_allReaders.append(this);
828
829 m_completed = completed;
830}
831
832bool QQStyleKitReader::transitionsEnabled() const
833{
834 return m_transitionsEnabled;
836
837void QQStyleKitReader::setTransitionsEnabled(bool enabled)
838{
839 if (m_transitionsEnabled == enabled)
840 return;
841 m_transitionsEnabled = enabled;
842}
843
844QQStyleKitStyle *QQStyleKitReader::explicitStyle() const
845{
846 return m_explicitStyle.data();
847}
848
849void QQStyleKitReader::setExplicitStyle(QQStyleKitStyle *style)
850{
851 if (m_explicitStyle == style)
852 return;
853 m_explicitStyle = style;
854
855 if (!m_explicitStyle || !m_explicitStyle->loaded()) {
856 clearLocalStorage();
857 return;
858 }
859
860 m_effectiveVariationsDirty = true;
861 m_fontDirty = true;
862 clearLocalStorage();
863 rebuildEffectivePalette();
864 rebuildEffectiveFont();
865 emitChangedForAllStyleProperties(EmitFlag::AllProperties);
866}
867
868QQuickPalette *QQStyleKitReader::palette() const
869{
870 return m_palette.data();
872
873void QQStyleKitReader::setPalette(QQuickPalette *palette)
874{
875 if (m_palette == palette)
876 return;
877
878 if (m_palette)
879 QObject::disconnect(m_palette, nullptr, this, nullptr);
880
881 m_palette = palette;
882 emit paletteChanged();
883
884 if (m_palette) {
885 // changed signal will be triggered when any role changes
886 QObject::connect(m_palette, &QQuickPalette::changed,
887 this, &QQStyleKitReader::onPaletteChanged);
888 }
889
890 onPaletteChanged();
891}
892
893QPalette QQStyleKitReader::effectivePalette() const
894{
895 return m_effectivePalette;
896}
897
898void QQStyleKitReader::onPaletteChanged()
899{
900 const QQStyleKitStyle *currentStyle = style();
901 if (!m_completed || !currentStyle || !currentStyle->loaded())
902 return;
903
904 if (rebuildEffectivePalette()) {
905 clearLocalStorage();
906 emitChangedForAllStyleProperties(EmitFlag::Colors);
907 }
908}
910bool QQStyleKitReader::rebuildEffectivePalette()
911{
912 auto mergedPalette = style()->paletteForControlType(this->controlType());
913 const auto stylePaletteResolveMask = mergedPalette.resolveMask();
914 if (m_palette) {
915 // The control palette takes precedence over the style palette
916 const auto controlPalette = m_palette->toQPalette();
917 mergedPalette = controlPalette.resolve(mergedPalette);
918 // Explicitly set the resolve mask to make sure it is not lost during the resolve operation
919 // when the control palette has a resolveMask of 0
920 mergedPalette.setResolveMask(stylePaletteResolveMask | controlPalette.resolveMask());
921 }
922 if (m_effectivePalette == mergedPalette)
923 return false;
924
925 m_effectivePalette = mergedPalette;
926 return true;
927}
928
929QFont QQStyleKitReader::font() const
930{
931 return m_font;
932}
933
934bool QQStyleKitReader::rebuildEffectiveFont()
935{
936 const QQStyleKitStyle *currentStyle = style();
937 if (!currentStyle || !currentStyle->loaded())
938 return false;
939
940 if (!m_fontDirty)
941 return false;
942
943 QFont font = currentStyle->fontForControlType(controlType());
944 const QQStyleKitTextProperties *textProps = global()->text();
945 if (textProps) {
946 if (textProps->isDefined(QQSK::Property::Bold))
947 font.setBold(textProps->bold());
948 if (textProps->isDefined(QQSK::Property::Italic))
949 font.setItalic(textProps->italic());
950 if (textProps->isDefined(QQSK::Property::PointSize))
951 font.setPointSizeF(textProps->pointSize());
952 }
953
954 if (m_font == font)
955 return false;
956
957 m_font = font;
958 emit fontChanged();
959 return true;
960}
961
962QQStyleKitControlProperties *QQStyleKitReader::global() const
963{
964 return &const_cast<QQStyleKitReader *>(this)->m_global;
965}
966
967void QQStyleKitReader::componentComplete()
968{
969 setCompleted(true);
970}
971
972QT_END_NAMESPACE
973
974#include "moc_qqstylekitreader_p.cpp"
Combined button and popup list for selecting options.
static quint64 textFontOverridesSignature(const QQStyleKitTextProperties *t)
static constexpr QLatin1StringView kAlternate1
static constexpr QLatin1StringView kAlternate2