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