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