Revision control
++ // Copyright (C) 2025 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++
++ #include <QtQuick/private/qquickpalette_p.h>
++ #include <QtQuick/private/qquickwindow_p.h>
++ #include <QtQuick/private/qquickstategroup_p.h>
++ #include <QtQuick/private/qquickpropertychanges_p.h>
++
++ #include "qqstylekit_p.h"
++ #include "qqstylekitreader_p.h"
++ #include "qqstylekitpropertyresolver_p.h"
++ #include "qqstylekitstyle_p.h"
++
++ QT_BEGIN_NAMESPACE
++
++ /*!
++ \qmltype StyleReader
++ \inqmlmodule Qt.labs.StyleKit
++ \inherits ControlStyleProperties
++ \brief Reads properties from the active Style for a specific control.
++
++ StyleReader is the bridge between a control in \l {Qt Quick Controls}
++ and the \l {StyleKit::style}{active style}. It exposes all the style
++ properties — \l {ControlStyleProperties::background}{backgrounds},
++ \l {ControlStyleProperties::indicator}{indicators},
++ \l {ControlStyleProperties::handle}{handles},
++ \l {ControlStyleProperties::text}{text},
++ \l {ControlStyleProperties::padding}{padding},
++ and more — that a control, and its delegates, should bind to.
++ All built-in \c StyleKit controls use a StyleReader
++ internally for this purpose.
++
++ The state properties — \l hovered, \l pressed, \l focused, \l checked,
++ \l highlighted, etc. — tell StyleReader which state the control is in.
++ It uses these to resolve and read the correct property values from
++ the \l Style, taking \l {Theme}{Themes}, \l {StyleVariation}{StyleVariations},
++ \l {StylableControls}{fallback types}, and property propagation
++ into account.
++
++ When implementing a \l CustomControl, you can follow the same approach as
++ the built-in controls:
++
++ \snippet StyleReaderSnippets.qml custom control
++
++ \note The style properties inherited from \l ControlStyleProperties do not map
++ directly to the \l Style. Instead, they reflect a cache of potentially interpolated
++ values during an active \l {ControlStyle::}{transition}. Writing to them will \e not
++ update the corresponding properties in the \l Style and may interfere with ongoing
++ transitions. Write directly to the \l Style when you need to change a style property.
++
++ \labs
++
++ \sa CustomControl, Style, {StyleVariation::controlType}{StyleVariation.controlType},
++ {StyleKit Property Resolution}
++ */
++
++ /*!
++ \qmlproperty enumeration StyleReader::controlType
++
++ Identifies which control type in the \l Style this reader reads
++ properties from. This can either be set to one of the predefined values
++ below, or to a \l {CustomControl::controlType}{custom control type} defined in the
++ \l Style.
++
++ \value StyleReader.BusyIndicator \l {BusyIndicator}
++ \value StyleReader.Button \l {Button}
++ \value StyleReader.CheckBox \l {CheckBox}
++ \value StyleReader.CheckDelegate \l {CheckDelegate}
++ \value StyleReader.ComboBox \l {ComboBox}
++ \value StyleReader.DelayButton \l {DelayButton}
++ \value StyleReader.Dial \l {Dial}
++ \value StyleReader.Dialog \l {Dialog}
++ \value StyleReader.DialogButtonBox \l {DialogButtonBox}
++ \value StyleReader.Drawer \l {Drawer}
++ \value StyleReader.Frame \l {Frame}
++ \value StyleReader.GroupBox \l {GroupBox}
++ \value StyleReader.HeaderViewDelegate \l {HorizontalHeaderViewDelegate} and \l {VerticalHeaderViewDelegate}
++ \value StyleReader.ItemDelegate \l {ItemDelegate}
++ \value StyleReader.Label \l {Label}
++ \value StyleReader.Menu \l {Menu}
++ \value StyleReader.MenuBar \l {MenuBar}
++ \value StyleReader.MenuBarItem \l {MenuBarItem}
++ \value StyleReader.MenuItem \l {MenuItem}
++ \value StyleReader.MenuSeparator \l {MenuSeparator}
++ \value StyleReader.Page \l {Page}
++ \value StyleReader.PageIndicator \l {PageIndicator}
++ \value StyleReader.Pane \l {Pane}
++ \value StyleReader.Popup \l {Popup}
++ \value StyleReader.ProgressBar \l {ProgressBar}
++ \value StyleReader.RadioButton \l {RadioButton}
++ \value StyleReader.RadioDelegate \l {RadioDelegate}
++ \value StyleReader.RoundButton \l {RoundButton}
++ \value StyleReader.ScrollBar \l {ScrollBar}
++ \value StyleReader.ScrollView \l {ScrollView}
++ \value StyleReader.SearchField \l {SearchField}
++ \value StyleReader.Slider \l {Slider}
++ \value StyleReader.SpinBox \l {SpinBox}
++ \value StyleReader.SwipeDelegate \l {SwipeDelegate}
++ \value StyleReader.Switch \l {Switch}
++ \value StyleReader.SwitchDelegate \l {SwitchDelegate}
++ \value StyleReader.TabBar \l {TabBar}
++ \value StyleReader.TabButton \l {TabButton}
++ \value StyleReader.TableViewDelegate \l {TableViewDelegate}
++ \value StyleReader.TextArea \l {TextArea}
++ \value StyleReader.TextField \l {TextField}
++ \value StyleReader.ToolBar \l {ToolBar}
++ \value StyleReader.ToolButton \l {ToolButton}
++ \value StyleReader.ToolSeparator \l {ToolSeparator}
++ \value StyleReader.ToolTip \l {ToolTip}
++ \value StyleReader.TreeViewDelegate \l {TreeViewDelegate}
++ \value StyleReader.Tumbler \l {Tumbler}
++
++ \sa {StyleVariation::controlType}{StyleVariation.controlType}
++ */
++
++ /*!
++ \qmlproperty bool StyleReader::enabled
++
++ Whether the control is enabled.
++
++ Bind this to the control's \l {Item::enabled}{enabled} property.
++
++ The default value is \c true.
++
++ \sa {ControlStateStyle::disabled}{ControlStateStyle.disabled}
++ */
++
++ /*!
++ \qmlproperty bool StyleReader::hovered
++
++ Whether the control is hovered.
++
++ Bind this to the control's \l {Control::hovered}{hovered} property.
++
++ The default value is \c false.
++
++ \sa {ControlStateStyle::hovered}{ControlStateStyle.hovered}
++ */
++
++ /*!
++ \qmlproperty bool StyleReader::pressed
++
++ Whether the control is pressed.
++
++ Bind this to the control's \l {AbstractButton::pressed}{pressed} property.
++
++ The default value is \c false.
++
++ \sa {ControlStateStyle::pressed}{ControlStateStyle.pressed}
++ */
++
++ /*!
++ \qmlproperty bool StyleReader::focused
++
++ Whether the control has active focus.
++
++ Bind this to the control's \l {Item::activeFocus}{activeFocus} property.
++
++ The default value is \c false.
++
++ \sa {ControlStateStyle::focused}{ControlStateStyle.focused}
++ */
++
++ /*!
++ \qmlproperty bool StyleReader::checked
++
++ Whether the control is checked.
++
++ Bind this to the control's \l {AbstractButton::checked}{checked} property.
++
++ The default value is \c false.
++
++ \sa {ControlStateStyle::checked}{ControlStateStyle.checked}
++ */
++
++ /*!
++ \qmlproperty bool StyleReader::highlighted
++
++ Whether the control is highlighted.
++
++ Bind this to the control's \l {Button::highlighted}{highlighted} property.
++
++ The default value is \c false.
++
++ \sa {ControlStateStyle::highlighted}{ControlStateStyle.highlighted}
++ */
++
++ /*!
++ \qmlproperty bool StyleReader::vertical
++
++ Whether the control is oriented vertically.
++
++ Bind this to the control's \l {Slider::orientation}{orientation}.
++
++ The default value is \c false.
++
++ \sa {ControlStateStyle::vertical}{ControlStateStyle.vertical}
++ */
++
++ /*!
++ \qmlproperty palette StyleReader::palette
++
++ The palette of the control. StyleKit uses this to resolve color properties
++ that bind the \l {Style::}{palette} in the \l Style.
++
++ Bind this to the control's \l {Control::palette}{palette} property.
++
++ \sa {Style::palette}{Style.palette}
++ */
++
++ /*!
++ \qmlproperty font StyleReader::font
++ \readonly
++
++ The effective font for this control type, as defined by the
++ \l {AbstractStyle::fonts}{style}. This also takes into account any font
++ overrides set in the \l {ControlStyle::}{text} properties of the style.
++
++ Bind the control's \l {Control::font}{font} property to this property.
++
++ \note Unlike properties such as \l hovered, \l pressed and \l palette — which
++ is forwarded from the control to the StyleReader — \l font is an output. Bind the
++ control's \l {Control::font}{font} property to this value, not the other way around.
++ */
++
++ /*!
++ \qmlproperty ControlStyleProperties StyleReader::global
++ \readonly
++
++ Provides direct access to the style properties, bypassing any ongoing
++ \l {ControlStyle::}{transition}.
++
++ While a state transition is ongoing, style properties read from a StyleReader
++ may return interpolated values. By prepending \c global to the property path,
++ you bypass the transition and get the end-state values immediately.
++
++ For example, when transitioning from \l hovered to \l pressed, \c {background.color}
++ may return an interpolated value between the \l {DelegateStyle::}{color} in the
++ \l {ControlStateStyle::}{hovered} state and the color in the
++ \l {ControlStateStyle::}{pressed} state. \c {global.background.color}, on the
++ other hand, returns the color in the pressed state directly.
++
++ \sa {StyleKit::transitionsEnabled}{transitionsEnabled}
++ */
++
++ using namespace Qt::StringLiterals;
++
++ static constexpr QLatin1StringView kAlternate1 = "A1"_L1;
++ static constexpr QLatin1StringView kAlternate2 = "A2"_L1;
++
++ static quint64 textFontOverridesSignature(const QQStyleKitTextProperties *t)
++ {
++ if (!t)
++ return 0;
++
++ quint64 sig = 0;
++
++ // bit 0: bold defined, bit 1: bold value
++ if (t->isDefined(QQSK::Property::Bold)) {
++ sig |= (quint64(1) << 0);
++ if (t->styleProperty<bool>(QQSK::Property::Bold))
++ sig |= (quint64(1) << 1);
++ }
++ // bit 2: italic defined, bit 3: italic value
++ if (t->isDefined(QQSK::Property::Italic)) {
++ sig |= (quint64(1) << 2);
++ if (t->styleProperty<bool>(QQSK::Property::Italic))
++ sig |= (quint64(1) << 3);
++ }
++ // bit 4: pointSize defined, bits 5..: quantized pointSize
++ if (t->isDefined(QQSK::Property::PointSize)) {
++ sig |= (quint64(1) << 4);
++ const qreal ps = t->styleProperty<qreal>(QQSK::Property::PointSize);
++ // 64-bit signature: 5 bits used, 59 bits available for pointSize
++ constexpr int payloadBits = 64 - 5;
++ const qint64 maxQ = (quint64(1) << payloadBits) - 1;
++ const quint64 q = quint64(qBound<qint64>(0, qRound64(ps * 64.0), maxQ));
++ sig |= (q << 5);
++ }
++ return sig;
++ }
++
++ QList<QQStyleKitReader *> QQStyleKitReader::s_allReaders;
++ QQStyleKitReader::PropertyChangesComponents QQStyleKitReader::s_propertyChangesComponents;
++
++ QQStyleKitReader::QQStyleKitReader(QObject *parent)
++ : QQStyleKitControlProperties(QQSK::PropertyGroup::Control, parent)
++ , m_dontEmitChangedSignals(false)
++ , m_effectiveVariationsDirty(true)
++ , m_transitionsEnabled(true)
++ , m_completed(false)
++ , m_global(QQStyleKitControlProperties(QQSK::PropertyGroup::GlobalFlag, this))
++ {
++ }
++
++ QQStyleKitReader::~QQStyleKitReader()
++ {
++ s_allReaders.removeOne(this);
++ }
++
++ QQuickStateGroup *QQStyleKitReader::stateGroup()
++ {
++ if (m_stateGroup)
++ return m_stateGroup;
++
++ /* Lazy create a QQuickStateGroup as soon as we have delegates
++ * that needs to be "tracked". That is, the user of this StyleKitReader
++ * has read one or more properties, so we need to check and emit changes for
++ * those properties whenever our state changes. */
++ const auto *stylePtr = style();
++ Q_ASSERT(stylePtr);
++
++ m_stateGroup = new QQuickStateGroup(this);
++
++ // Add two states that we can alternate between
++ auto statesProp = m_stateGroup->statesProperty();
++ QQuickState *alternate1 = new QQuickState(m_stateGroup);
++ QQuickState *alternate2 = new QQuickState(m_stateGroup);
++ alternate1->setName(kAlternate1);
++ alternate2->setName(kAlternate2);
++ m_stateGroup->statesProperty().append(&statesProp, alternate1);
++ m_stateGroup->statesProperty().append(&statesProp, alternate2);
++
++ QQmlComponent *controlComp = createControlChangesComponent();
++ instantiatePropertyChanges(controlComp);
++
++ return m_stateGroup;
++ }
++
++ QQmlComponent *QQStyleKitReader::createControlChangesComponent() const
++ {
++ QQmlEngine *engine = qmlEngine(style());
++ auto key = PropertyChangesComponents::key_type{engine, u"control"_s};
++ if (auto r = s_propertyChangesComponents.value(key, nullptr))
++ return r.data();
++
++ const QString qmlControlCode = QString::fromUtf8(R"(
++ import QtQuick
++ PropertyChanges {
++ spacing: global.spacing
++ padding: global.padding
++ leftPadding: global.leftPadding
++ rightPadding: global.rightPadding
++ topPadding: global.topPadding
++ bottomPadding: global.bottomPadding
++ text.color: global.text.color
++ text.alignment: global.text.alignment
++ text.bold: global.text.bold
++ text.italic: global.text.italic
++ text.pointSize: global.text.pointSize
++ text.padding: global.text.padding
++ text.leftPadding: global.text.leftPadding
++ text.rightPadding: global.text.rightPadding
++ text.topPadding: global.text.topPadding
++ text.bottomPadding: global.text.bottomPadding
++ }
++ )");
++
++ // TODO: cache propertyName to component!
++ QQmlComponent *component = new QQmlComponent(engine, engine);
++ component->setData(qmlControlCode.toUtf8(), QUrl());
++ Q_ASSERT_X(!component->isError(), __FUNCTION__, component->errorString().toUtf8().constData());
++ s_propertyChangesComponents.insert(key, component);
++ QObject::connect(engine, &QObject::destroyed, engine, [key = std::move(key)] {
++ s_propertyChangesComponents.remove(key);
++ });
++ return component;
++ }
++
++ QQmlComponent *QQStyleKitReader::createDelegateChangesComponent(const QString &delegateName) const
++ {
++ QQmlEngine *engine = qmlEngine(style());
++ auto key = PropertyChangesComponents::key_type{engine, delegateName};
++ if (auto r = s_propertyChangesComponents.value(key, nullptr))
++ return r.data();
++
++ static const QString qmlTemplateCode = QString::fromUtf8(R"(
++ import QtQuick
++ PropertyChanges { $ {
++ width: global.$.width
++ height: global.$.height
++ visible: global.$.visible
++ color: global.$.color
++ gradient: global.$.gradient
++ radius: global.$.radius
++ topLeftRadius: global.$.topLeftRadius
++ topRightRadius: global.$.topRightRadius
++ bottomLeftRadius: global.$.bottomLeftRadius
++ bottomRightRadius: global.$.bottomRightRadius
++ margins: global.$.margins
++ alignment: global.$.alignment
++ leftMargin: global.$.leftMargin
++ rightMargin: global.$.rightMargin
++ topMargin: global.$.topMargin
++ bottomMargin: global.$.bottomMargin
++ scale: global.$.scale
++ rotation: global.$.rotation
++ opacity: global.$.opacity
++ border.color: global.$.border.color
++ border.width: global.$.border.width
++ shadow.color: global.$.shadow.color
++ shadow.scale: global.$.shadow.scale
++ shadow.blur: global.$.shadow.blur
++ shadow.visible: global.$.shadow.visible
++ shadow.opacity: global.$.shadow.opacity
++ shadow.verticalOffset: global.$.shadow.verticalOffset
++ shadow.horizontalOffset: global.$.shadow.horizontalOffset
++ shadow.delegate: global.$.shadow.delegate
++ image.source: global.$.image.source
++ image.color: global.$.image.color
++ image.fillMode: global.$.image.fillMode
++ delegate: global.$.delegate
++ data: global.$.data
++ }}
++ )");
++
++ QString substitutedCode = qmlTemplateCode;
++ substitutedCode.replace('$'_L1, delegateName);
++ QQmlComponent *component = new QQmlComponent(engine, engine);
++ component->setData(substitutedCode.toUtf8(), QUrl());
++ Q_ASSERT_X(!component->isError(), __FUNCTION__, component->errorString().toUtf8().constData());
++ s_propertyChangesComponents.insert(key, component);
++ QObject::connect(engine, &QObject::destroyed, engine, [key = std::move(key)] {
++ s_propertyChangesComponents.remove(key);
++ });
++ return component;
++ }
++
++ void QQStyleKitReader::instantiatePropertyChanges(QQmlComponent *comp)
++ {
++ QObject *obj = comp->create(qmlContext(this));
++ auto *propertyChanges = qobject_cast<QQuickPropertyChanges *>(obj);
++ Q_ASSERT(propertyChanges);
++
++ // setter for the "target" property is called setObject
++ propertyChanges->setObject(this);
++ /* set "explicit" to true, meaning that the StyleProperties shouldn't
++ * create bindings, but do one-off assignments. Bindings are not needed
++ * here since it's the state changes of this StyleKitReader that
++ * drives the property changes. The properties cannot change outside of
++ * a state change (or, if they do, it will trigger a full update equal
++ * to a theme change) */
++ propertyChanges->setIsExplicit(true);
++ /* We don't need to ever restore the properties back to default, since
++ * the group state will never be reset back to an empty string. This will
++ * hopefully avoid the generation of restore structures inside the StateGroup. */
++ propertyChanges->setRestoreEntryValues(false);
++
++ /* Add the new PropertyChanges object to both states, A1 and A2 */
++ for (QQuickState *state : stateGroup()->states()) {
++ auto changesProp = state->changes();
++ changesProp.append(&changesProp, propertyChanges);
++ }
++ }
++
++ void QQStyleKitReader::maybeTrackDelegates()
++ {
++ forEachUsedDelegate(
++ [this](QQStyleKitDelegateProperties *delegate, QQSK::Delegate type, const QString &delegatePath){
++ if (m_trackedDelegates.testFlag(type)) {
++ // We're already tracking the delegate. So nothing needs to be done.
++ return;
++ }
++ if (!delegate->visible()) {
++ /* As an optimization, if the delegate is hidden, we don't track it. Most
++ * controls set background.visible to false, for example. If this, for
++ * whatever reason, is not wanted, set opacity to 0 instead. */
++ return;
++ }
++ /* Invariant: The application has read one or more properties for the given delegate
++ * from the Style, but we don't yet have a PropertyChanges object that can track
++ * changes to it (and run transitions). So we create one now. By lazy creating them this
++ * way, we avoid creating PropertyChanges for all the different delegates that a control
++ * _may_ have. Instead, we only track changes for the delegates it actually uses. */
++ m_trackedDelegates.setFlag(type);
++ QQmlComponent *comp = createDelegateChangesComponent(delegatePath);
++ instantiatePropertyChanges(comp);
++ });
++ }
++
++ void QQStyleKitReader::updateControl(bool applyStateChange)
++ {
++ const QQStyleKitStyle *currentStyle = style();
++ if (!m_completed || !currentStyle || !currentStyle->loaded())
++ return;
++
++ if (applyStateChange) {
++ /* Alternate between two states to trigger a state change. The state group
++ * will, upon changing state, take care of reading the updated property values,
++ * compare them against the current ones in the local storage, and emit changes
++ * (possibly using a transition) if changed. Since the new state might change the
++ * transition, we need to update it first before we do the state change, so that
++ * it takes effect.
++ * If we have skipped tracking some delegates because they are hidden, we need to
++ * check again if this is still the case for the current state. Otherwise, we now
++ * need to track them. Untracked delegates are not backed by PropertyChanges objects,
++ * and hence, will not update when we do a state swap below.
++ * Note that the first time this function is called after start-up, none of the
++ * delegates are yet tracked, and therefore will be created now. */
++
++ maybeTrackDelegates();
++
++ auto transitionProp = stateGroup()->transitionsProperty();
++ const int transitionCountInStateGroup = transitionProp.count(&transitionProp);
++ const bool enabled = m_transitionsEnabled && QQStyleKit::qmlAttachedProperties()->transitionsEnabled();
++ QQuickTransition *transitionInStyle = enabled ? transition() : nullptr;
++ QQuickTransition *transitionInStateGroup =
++ transitionCountInStateGroup > 0 ? transitionProp.at(&transitionProp, 0) : nullptr;
++ if (transitionInStyle != transitionInStateGroup) {
++ transitionProp.clear(&transitionProp);
++ if (transitionInStyle)
++ transitionProp.append(&transitionProp, transitionInStyle);
++ }
++
++ switch (m_alternateState) {
++ case AlternateState::Alternate1:
++ m_alternateState = AlternateState::Alternate2;
++ stateGroup()->setState(kAlternate2);
++ break;
++ case AlternateState::Alternate2:
++ m_alternateState = AlternateState::Alternate1;
++ stateGroup()->setState(kAlternate1);
++ break;
++ default:
++ Q_UNREACHABLE();
++ }
++ } else {
++ // Since no state change occurs, the property values in
++ // the local storage no longer reflect this StyleReader's
++ // current state. Clear it to avoid returning stale values
++ // from subsequent property reads.
++ clearLocalStorage();
++ }
++
++ auto textOverrideSig = textFontOverridesSignature(global()->text());
++ if (m_lastTextFontOverridesSignature != textOverrideSig)
++ m_fontDirty = true;
++ m_lastTextFontOverridesSignature = textOverrideSig;
++ rebuildEffectiveFont();
++ }
++
++ void QQStyleKitReader::resetReadersForStyle(const QQStyleKitStyle *style)
++ {
++ for (QQStyleKitReader *reader : s_allReaders) {
++ if (reader->style() == style) {
++ reader->m_effectiveVariationsDirty = true;
++ reader->m_fontDirty = true;
++ reader->clearLocalStorage();
++ reader->rebuildEffectivePalette();
++ reader->rebuildEffectiveFont();
++ reader->emitChangedForAllStyleProperties(EmitFlag::AllProperties);
++ }
++ }
++ }
++
++ void QQStyleKitReader::populateLocalStorage()
++ {
++ if (!m_completed) {
++ /* The local storage is used for implementing transitions and to ensure that we
++ * only emit changed signals for properties that actually change during a state
++ * change. During start-up, however, all properties are considered dirty, so we
++ * can delay populating the storage until the first time we get a state change
++ * after completed, to improve start-up performance. */
++ return;
++ }
++
++ if (!m_storage.isEmpty())
++ return;
++ const auto *stylePtr = style();
++ if (!stylePtr || !stylePtr->loaded())
++ return;
++
++ /* The local storage is empty, which is typically the case after an
++ * operation that should perform without a transition, such as a theme
++ * change or a change to a property value in the Style itself.
++ * Doing a transition in that case is unwanted and slow (since the
++ * operation typically affect all controls), so we short-cut the process by
++ * emitting changed signals directly for all the properties instead.
++ * Since that will render the local storage out-of-sync, we clear it at the
++ * same time to signal that it's 'dirty'. Which is why we need to sync it back
++ * up again now.
++ * Syncing the local storage before changing state (even if the values that
++ * end up in the storage should be exactly the same as those already
++ * showing), has the upshot that we can compare after the state change which
++ * properties has changed, which will limit the amount of changed signals we
++ * then need to emit. */
++ m_dontEmitChangedSignals = true;
++ updateControl();
++ m_dontEmitChangedSignals = false;
++ }
++
++ void QQStyleKitReader::clearLocalStorage()
++ {
++ /* Clear all the local property overrides that has been set on this reader. Such
++ * overrides are typically interpolated values set by a transition during a state
++ * change. By clearing them, the controls will end up reading the property values
++ * directly from the Style instead. */
++ m_storage.clear();
++ }
++
++ QQSK::State QQStyleKitReader::controlState() const
++ {
++ QQSK::State effectiveState = m_state;
++
++ if (!enabled()) {
++ // Some states are not valid if the control is disabled
++ effectiveState &= ~(QQSK::StateFlag::Pressed |
++ QQSK::StateFlag::Hovered |
++ QQSK::StateFlag::Highlighted |
++ QQSK::StateFlag::Focused |
++ QQSK::StateFlag::Hovered);
++ }
++
++ if (effectiveState == QQSK::StateFlag::Unspecified)
++ effectiveState.setFlag(QQSK::StateFlag::Normal);
++
++ return effectiveState;
++ }
++
++ QVariant QQStyleKitReader::readStyleProperty(PropertyStorageId key) const
++ {
++ return m_storage.value(key);
++ }
++
++ void QQStyleKitReader::writeStyleProperty(PropertyStorageId key, const QVariant &value)
++ {
++ m_storage.insert(key, value);
++ }
++
++ bool QQStyleKitReader::dontEmitChangedSignals() const
++ {
++ return m_dontEmitChangedSignals;
++ }
++
++ QQStyleKitExtendableControlType QQStyleKitReader::controlType() const
++ {
++ return m_type;
++ }
++
++ void QQStyleKitReader::setControlType(QQStyleKitExtendableControlType type)
++ {
++ if (m_type == type)
++ return;
++
++ m_type = type;
++ populateLocalStorage();
++ emit controlTypeChanged();
++ updateControl();
++ }
++
++ #ifdef QT_DEBUG
++ QQStyleKitReader::ControlType QQStyleKitReader::typeAsControlType() const
++ {
++ /* Note: m_type is of type int to support extending the list
++ * of possible types from the Style itself. This function
++ * is here to for debugging purposes */
++ return ControlType(m_type);
++ }
++ #endif
++
++ bool QQStyleKitReader::hovered() const
++ {
++ return m_state.testFlag(QQSK::StateFlag::Hovered);
++ }
++
++ void QQStyleKitReader::setHovered(bool hovered)
++ {
++ if (hovered == QQStyleKitReader::hovered())
++ return;
++
++ populateLocalStorage();
++ m_state.setFlag(QQSK::StateFlag::Hovered, hovered);
++ emit hoveredChanged();
++ updateControl();
++ }
++
++ bool QQStyleKitReader::enabled() const
++ {
++ return !m_state.testFlag(QQSK::StateFlag::Disabled);
++ }
++
++ void QQStyleKitReader::setEnabled(bool enabled)
++ {
++ if (enabled == QQStyleKitReader::enabled())
++ return;
++
++ populateLocalStorage();
++ m_state.setFlag(QQSK::StateFlag::Disabled, !enabled);
++ emit enabledChanged();
++ updateControl();
++ }
++
++ bool QQStyleKitReader::focused() const
++ {
++ return m_state.testFlag(QQSK::StateFlag::Focused);
++ }
++
++ void QQStyleKitReader::setFocused(bool focused)
++ {
++ if (focused == QQStyleKitReader::focused())
++ return;
++
++ populateLocalStorage();
++ m_state.setFlag(QQSK::StateFlag::Focused, focused);
++ emit focusedChanged();
++ updateControl();
++ }
++
++ bool QQStyleKitReader::checked() const
++ {
++ return m_state.testFlag(QQSK::StateFlag::Checked);
++ }
++
++ void QQStyleKitReader::setChecked(bool checked)
++ {
++ if (checked == QQStyleKitReader::checked())
++ return;
++
++ populateLocalStorage();
++ m_state.setFlag(QQSK::StateFlag::Checked, checked);
++ emit checkedChanged();
++ updateControl();
++ }
++
++ bool QQStyleKitReader::pressed() const
++ {
++ return m_state.testFlag(QQSK::StateFlag::Pressed);
++ }
++
++ void QQStyleKitReader::setPressed(bool pressed)
++ {
++ if (pressed == QQStyleKitReader::pressed())
++ return;
++
++ populateLocalStorage();
++ m_state.setFlag(QQSK::StateFlag::Pressed, pressed);
++ emit pressedChanged();
++ updateControl();
++ }
++
++ bool QQStyleKitReader::vertical() const
++ {
++ return m_state.testFlag(QQSK::StateFlag::Vertical);
++ }
++
++ void QQStyleKitReader::setVertical(bool vertical)
++ {
++ if (vertical == QQStyleKitReader::vertical())
++ return;
++
++ populateLocalStorage();
++ m_state.setFlag(QQSK::StateFlag::Vertical, vertical);
++ emit verticalChanged();
++ updateControl();
++ }
++
++ bool QQStyleKitReader::highlighted() const
++ {
++ return m_state.testFlag(QQSK::StateFlag::Highlighted);
++ }
++
++ void QQStyleKitReader::setHighlighted(bool highlighted)
++ {
++ if (highlighted == QQStyleKitReader::highlighted())
++ return;
++
++ populateLocalStorage();
++ m_state.setFlag(QQSK::StateFlag::Highlighted, highlighted);
++ emit highlightedChanged();
++ updateControl();
++ }
++
++ void QQStyleKitReader::setControlTypeAndState(QQStyleKitExtendableControlType controlType, QQSK::State flags,
++ bool applyStateChange)
++ {
++ // Apply type and states in one shot to avoid calling
++ // populateLocalStorage() + updateControl() once per individual setter
++ const bool typeChanged = m_type != controlType;
++ const QQSK::State stateChanged = m_state ^ flags;
++
++ if (!typeChanged && !stateChanged)
++ return;
++
++ if (applyStateChange)
++ populateLocalStorage();
++
++ m_type = controlType;
++ m_state = flags;
++
++ updateControl(applyStateChange);
++ }
++
++ QObject *QQStyleKitReader::target() const
++ {
++ return m_target.data();
++ }
++
++ void QQStyleKitReader::setTarget(QObject *target)
++ {
++ /* A reader can optionally set a target object which represents
++ the current object that is being styled, ie: using this StyleReader
++ to read properties from the style. It is needed so that the widget style using
++ the StyleReader is able to post StyleAnimationUpdate events to the correct
++ target during a transition. */
++ m_target = target;
++ }
++
++ void QQStyleKitReader::setCompleted(bool completed)
++ {
++ const QQStyleKitStyle *currentStyle = style();
++ if (currentStyle && currentStyle->loaded()) {
++ clearLocalStorage();
++ rebuildEffectiveFont();
++ rebuildEffectivePalette();
++
++ /* Some style properties were read (e.g. by a Qt Quick Control) before this
++ * StyleReader was completed. Those reads were premature: the values depend not
++ * only on the style and theme, but also on the StyleReader's own state (such as
++ * its controlType, checked state, and so on), which may not have been fully
++ * initialized at that time. Now that all of it is in place, emit change signals
++ * for all properties to force the bindings to re-read them. */
++ emitChangedForAllStyleProperties(EmitFlag::AllProperties);
++ }
++
++ // From now on, we need to be notified when the style or theme changes
++ s_allReaders.append(this);
++
++ m_completed = completed;
++ }
++
++ bool QQStyleKitReader::transitionsEnabled() const
++ {
++ return m_transitionsEnabled;
++ }
++
++ void QQStyleKitReader::setTransitionsEnabled(bool enabled)
++ {
++ if (m_transitionsEnabled == enabled)
++ return;
++ m_transitionsEnabled = enabled;
++ }
++
++ QQStyleKitStyle *QQStyleKitReader::explicitStyle() const
++ {
++ return m_explicitStyle.data();
++ }
++
++ void QQStyleKitReader::setExplicitStyle(QQStyleKitStyle *style)
++ {
++ if (m_explicitStyle == style)
++ return;
++ m_explicitStyle = style;
++
++ if (!m_explicitStyle || !m_explicitStyle->loaded()) {
++ clearLocalStorage();
++ return;
++ }
++
++ m_effectiveVariationsDirty = true;
++ m_fontDirty = true;
++ clearLocalStorage();
++ rebuildEffectivePalette();
++ rebuildEffectiveFont();
++ emitChangedForAllStyleProperties(EmitFlag::AllProperties);
++ }
++
++ QQuickPalette *QQStyleKitReader::palette() const
++ {
++ return m_palette.data();
++ }
++
++ void QQStyleKitReader::setPalette(QQuickPalette *palette)
++ {
++ if (m_palette == palette)
++ return;
++
++ if (m_palette)
++ QObject::disconnect(m_palette, nullptr, this, nullptr);
++
++ m_palette = palette;
++ emit paletteChanged();
++
++ if (m_palette) {
++ // changed signal will be triggered when any role changes
++ QObject::connect(m_palette, &QQuickPalette::changed,
++ this, &QQStyleKitReader::onPaletteChanged);
++ }
++
++ onPaletteChanged();
++ }
++
++ QPalette QQStyleKitReader::effectivePalette() const
++ {
++ return m_effectivePalette;
++ }
++
++ void QQStyleKitReader::onPaletteChanged()
++ {
++ const QQStyleKitStyle *currentStyle = style();
++ if (!m_completed || !currentStyle || !currentStyle->loaded())
++ return;
++
++ if (rebuildEffectivePalette()) {
++ clearLocalStorage();
++ emitChangedForAllStyleProperties(EmitFlag::Colors);
++ }
++ }
++
++ bool QQStyleKitReader::rebuildEffectivePalette()
++ {
++ auto mergedPalette = style()->paletteForControlType(this->controlType());
++ const auto stylePaletteResolveMask = mergedPalette.resolveMask();
++ if (m_palette) {
++ // The control palette takes precedence over the style palette
++ const auto controlPalette = m_palette->toQPalette();
++ mergedPalette = controlPalette.resolve(mergedPalette);
++ // Explicitly set the resolve mask to make sure it is not lost during the resolve operation
++ // when the control palette has a resolveMask of 0
++ mergedPalette.setResolveMask(stylePaletteResolveMask | controlPalette.resolveMask());
++ }
++ if (m_effectivePalette == mergedPalette)
++ return false;
++
++ m_effectivePalette = mergedPalette;
++ return true;
++ }
++
++ QFont QQStyleKitReader::font() const
++ {
++ return m_font;
++ }
++
++ bool QQStyleKitReader::rebuildEffectiveFont()
++ {
++ const QQStyleKitStyle *currentStyle = style();
++ if (!currentStyle || !currentStyle->loaded())
++ return false;
++
++ if (!m_fontDirty)
++ return false;
++
++ QFont font = currentStyle->fontForControlType(controlType());
++ const QQStyleKitTextProperties *textProps = global()->text();
++ if (textProps) {
++ if (textProps->isDefined(QQSK::Property::Bold))
++ font.setBold(textProps->bold());
++ if (textProps->isDefined(QQSK::Property::Italic))
++ font.setItalic(textProps->italic());
++ if (textProps->isDefined(QQSK::Property::PointSize))
++ font.setPointSizeF(textProps->pointSize());
++ }
++
++ if (m_font == font)
++ return false;
++
++ m_font = font;
++ emit fontChanged();
++ return true;
++ }
++
++ QQStyleKitControlProperties *QQStyleKitReader::global() const
++ {
++ return &const_cast<QQStyleKitReader *>(this)->m_global;
++ }
++
++ void QQStyleKitReader::componentComplete()
++ {
++ setCompleted(true);
++ }
++
++ QT_END_NAMESPACE
++
++ #include "moc_qqstylekitreader_p.cpp"