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