diff options
| author | Doris Verria <doris.verria@qt.io> | 2026-09-02 15:35:11 +0200 |
|---|---|---|
| committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2026-09-17 11:43:49 +0000 |
| commit | af552ce0d3ac5984131b3161b365bc19816f1e52 (patch) | |
| tree | 99830e422089523aeda3fed3f4275986439de29a | |
| parent | 9758f457b3ee8c83db62d7729529d63e78ece916 (diff) | |
QStylekitStyle: Don't always populate local cache when updating control
Widgets query the style for size hints way before any drawing. Also
a lot of style queries for font and palette information as well as
reader setups happen at polish() time before any draw calls. During
this time, the reader was unecessarily flipping states and populating
the local storage needed for potential transitions. This is unecessary
work at this time, since we don't need to run transitions before
drawing and we can get the property values directly from the style
without needing to populate the local storage.
To avoid this, make it possible to call updateControl with a parameter
to be able to configure if the reader should update the local storage
or not and call it as needed from the widget style.
This optimization cuts startup time and paint time considerably
(50% - 65%).
Change-Id: If84c734ce8572843652506ca2b53e80afcaec6a7
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
(cherry picked from commit ff61a68f75536d3148445038f087a4316358eed3)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
(cherry picked from commit 8ff33d8f7970154f4ca9578d62ae3826aa3801c3)
| -rw-r--r-- | src/labs/stylekit/qqstylekitreader.cpp | 107 | ||||
| -rw-r--r-- | src/labs/stylekit/qqstylekitreader_p.h | 5 | ||||
| -rw-r--r-- | src/labs/stylekit/widgets/qstylekitstyle.cpp | 14 |
3 files changed, 61 insertions, 65 deletions
diff --git a/src/labs/stylekit/qqstylekitreader.cpp b/src/labs/stylekit/qqstylekitreader.cpp index e691d07308..2ae13093a5 100644 --- a/src/labs/stylekit/qqstylekitreader.cpp +++ b/src/labs/stylekit/qqstylekitreader.cpp @@ -473,50 +473,58 @@ void QQStyleKitReader::maybeTrackDelegates() }); } -void QQStyleKitReader::updateControl() +void QQStyleKitReader::updateControl(bool applyStateChange) { const QQStyleKitStyle *currentStyle = style(); if (!m_completed || !currentStyle || !currentStyle->loaded()) return; - /* 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); - } + 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(); + 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()); @@ -757,7 +765,8 @@ void QQStyleKitReader::setHighlighted(bool highlighted) updateControl(); } -void QQStyleKitReader::setControlTypeAndState(QQStyleKitExtendableControlType controlType, QQSK::State flags) +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 @@ -767,27 +776,13 @@ void QQStyleKitReader::setControlTypeAndState(QQStyleKitExtendableControlType co if (!typeChanged && !stateChanged) return; - populateLocalStorage(); + if (applyStateChange) + populateLocalStorage(); m_type = controlType; m_state = flags; - if (typeChanged) - emit controlTypeChanged(); - - auto emitChanged = [this, &stateChanged](QQSK::StateFlag flag, void (QQStyleKitReader::*signal)()) { - if (stateChanged.testFlag(flag)) - (this->*signal)(); - }; - emitChanged(QQSK::StateFlag::Hovered, &QQStyleKitReader::hoveredChanged); - emitChanged(QQSK::StateFlag::Disabled, &QQStyleKitReader::enabledChanged); - emitChanged(QQSK::StateFlag::Focused, &QQStyleKitReader::focusedChanged); - emitChanged(QQSK::StateFlag::Checked, &QQStyleKitReader::checkedChanged); - emitChanged(QQSK::StateFlag::Pressed, &QQStyleKitReader::pressedChanged); - emitChanged(QQSK::StateFlag::Vertical, &QQStyleKitReader::verticalChanged); - emitChanged(QQSK::StateFlag::Highlighted, &QQStyleKitReader::highlightedChanged); - - updateControl(); + updateControl(applyStateChange); } QObject *QQStyleKitReader::target() const diff --git a/src/labs/stylekit/qqstylekitreader_p.h b/src/labs/stylekit/qqstylekitreader_p.h index 782a6c4ca1..660a77f852 100644 --- a/src/labs/stylekit/qqstylekitreader_p.h +++ b/src/labs/stylekit/qqstylekitreader_p.h @@ -155,7 +155,8 @@ public: QQSK::State controlState() const; - void setControlTypeAndState(QQStyleKitExtendableControlType controlType, QQSK::State flags); + void setControlTypeAndState(QQStyleKitExtendableControlType controlType, QQSK::State flags, + bool applyStateChange = true); QObject *target() const; void setTarget(QObject *target); @@ -193,7 +194,7 @@ protected: void componentComplete() override; private: - void updateControl(); + void updateControl(bool applyStateChange = true); void populateLocalStorage(); bool dontEmitChangedSignals() const; diff --git a/src/labs/stylekit/widgets/qstylekitstyle.cpp b/src/labs/stylekit/widgets/qstylekitstyle.cpp index 1bff745e68..76d0d8e0b6 100644 --- a/src/labs/stylekit/widgets/qstylekitstyle.cpp +++ b/src/labs/stylekit/widgets/qstylekitstyle.cpp @@ -763,7 +763,7 @@ void QStyleKitStylePrivate::refreshStyleFont(QWidget *widget) opt.initFrom(targetWidget); const QQSK::State currentState = resolvedStateFor(controlType, opt.state, targetWidget); - shared->setControlTypeAndState(controlType, currentState); + shared->setControlTypeAndState(controlType, currentState, false); setStyleFont(widget, shared->font()); } @@ -825,12 +825,12 @@ void QStyleKitStylePrivate::refreshStylePalette(QWidget *widget) QPalette stylePalette; if (isWindow) { // Windows draw their own background using the QPalette::Window role - shared->setControlTypeAndState(controlType, currentState); + shared->setControlTypeAndState(controlType, currentState, false); if (const auto *bg = shared->global()->background(); bg && bg->isDefined(QQSK::Property::Color)) { stylePalette.setColor(QPalette::Window, bg->color()); } - shared->setControlTypeAndState(controlType, QQSK::StateFlag::Disabled); + shared->setControlTypeAndState(controlType, QQSK::StateFlag::Disabled, false); if (const auto *dbg = shared->global()->background(); dbg && dbg->isDefined(QQSK::Property::Color)) { stylePalette.setColor(QPalette::Disabled, QPalette::Window, dbg->color()); @@ -838,13 +838,13 @@ void QStyleKitStylePrivate::refreshStylePalette(QWidget *widget) } else { // The remaining text-based widgets use the QPalette::Text/WindowText roles for their // foreground color - shared->setControlTypeAndState(controlType, currentState); + shared->setControlTypeAndState(controlType, currentState, false); if (const auto *text = shared->global()->text(); text && text->isDefined(QQSK::Property::Color)) { stylePalette.setColor(QPalette::Text, text->color()); stylePalette.setColor(QPalette::WindowText, text->color()); } - shared->setControlTypeAndState(controlType, QQSK::StateFlag::Disabled); + shared->setControlTypeAndState(controlType, QQSK::StateFlag::Disabled, false); if (const auto *dt = shared->global()->text(); dt && dt->isDefined(QQSK::Property::Color)) { stylePalette.setColor(QPalette::Disabled, QPalette::Text, dt->color()); @@ -1098,7 +1098,7 @@ QStyleKitStylePrivate::metricsFor(QQStyleKitReader::ControlType type, QQSK::Stat auto *reader = ensureSharedReader(); Q_ASSERT(reader); - reader->setControlTypeAndState(type, state); + reader->setControlTypeAndState(type, state, false); return *metricsCache.insert({ type, state }, metricsForReader(reader)); } @@ -1283,7 +1283,7 @@ QStyleKitStylePrivate::resolveLayout(QQStyleKitReader::ControlType type, QStyle: // metricsFor() sets the sharedReader to (type, resolvedState) on cache miss. // On hit it didn't, so make sure staticProps / staticFont reflect the state // and type of the caller - reader->setControlTypeAndState(type, resolvedState); + reader->setControlTypeAndState(type, resolvedState, false); out.staticProps = reader->global(); out.staticFont = reader->font(); return out; |
