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