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