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