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