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