diff options
author | Fabian Kosmale <fabian.kosmale@qt.io> | 2022-02-07 15:48:30 +0100 |
---|---|---|
committer | Qt Cherry-pick Bot <cherrypick_bot@qt-project.org> | 2022-02-08 18:35:08 +0000 |
commit | cb4594718a709c50cc8da0237cef6cd0567e5295 (patch) | |
tree | 99cadb3d788fe98dac016a7fb6fa202748c4e8cb | |
parent | cd60a5182e273d99ef66b77a555cea2571d034a9 (diff) |
Correctly handle QQuickState::when
64c1fbe96c68b1286a70242ff4922be140128cb2 fixed that one could not assign
a constant value to QQuickState::when. However, as alluded to by a
comment in QQState's code, this caused state oscillation, breaking clean
transitions between two states.
The reason for this issue is that as soon as once of the when condition
changes, we check the when condition of all states. However, in the case
of logically exclusive cases we can run into the issue that the binding
of the second state has not been re-evaluated yet. Thus, we might end up
with all states being false, even though the second one becomes true
immediately afterwards.
To avoid this issue, we force the reevaluation of the binding. However,
instead of doing this by using a QQmlBinding property (with all the
tooling and runtime issues that it would entail), we keep using a normal
property. We then ask the engine for the binding, and if it exists, we
reevaluate it to obtain the current value.
Fixes: QTBUG-86695
Change-Id: Id10a3549935794b93b29450b0f5dfb6d1df691a1
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
(cherry picked from commit a8c729d83979fb0b9939044d246e73b1d578e65b)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
-rw-r--r-- | src/quick/util/qquickstategroup.cpp | 10 | ||||
-rw-r--r-- | tests/auto/quick/qquickstates/data/noStateOsciallation.qml | 22 | ||||
-rw-r--r-- | tests/auto/quick/qquickstates/tst_qquickstates.cpp | 15 |
3 files changed, 46 insertions, 1 deletions
diff --git a/src/quick/util/qquickstategroup.cpp b/src/quick/util/qquickstategroup.cpp index d664b67485..781d2cf1fb 100644 --- a/src/quick/util/qquickstategroup.cpp +++ b/src/quick/util/qquickstategroup.cpp @@ -376,7 +376,15 @@ bool QQuickStateGroupPrivate::updateAutoState() QQuickState *state = states.at(ii); if (state->isWhenKnown()) { if (state->isNamed()) { - if (state->when()) { + bool whenValue = state->when(); + const QQmlProperty whenProp(state, u"when"_qs); + const auto potentialWhenBinding = QQmlAnyBinding::ofProperty(whenProp); + Q_ASSERT(!potentialWhenBinding.isUntypedPropertyBinding()); + // if there is a binding, the value in when might not be up-to-date at this point + // so we manually reevaluate the binding + if (auto abstractBinding = dynamic_cast<QQmlBinding *>( potentialWhenBinding.asAbstractBinding())) + whenValue = abstractBinding->evaluate().toBool(); + if (whenValue) { qCDebug(lcStates) << "Setting auto state due to expression"; if (currentState != state->name()) { q->setState(state->name()); diff --git a/tests/auto/quick/qquickstates/data/noStateOsciallation.qml b/tests/auto/quick/qquickstates/data/noStateOsciallation.qml new file mode 100644 index 0000000000..f0d7aeeb6d --- /dev/null +++ b/tests/auto/quick/qquickstates/data/noStateOsciallation.qml @@ -0,0 +1,22 @@ +import QtQuick 2.15 + +Item { + id: root + property int number: 2 + property int stateChangeCounter: 0 + + Item { + id: item + onStateChanged: ++stateChangeCounter + states: [ + State { + name: "n1" + when: root.number === 1 + }, + State { + name: "n2" + when: root.number === 2 + } + ] + } +} diff --git a/tests/auto/quick/qquickstates/tst_qquickstates.cpp b/tests/auto/quick/qquickstates/tst_qquickstates.cpp index 176a377ed4..2397cf4f06 100644 --- a/tests/auto/quick/qquickstates/tst_qquickstates.cpp +++ b/tests/auto/quick/qquickstates/tst_qquickstates.cpp @@ -199,6 +199,7 @@ private slots: void revertListMemoryLeak(); void duplicateStateName(); void trivialWhen(); + void noStateOsciallation(); void parentChangeCorrectReversal(); void revertNullObjectBinding(); void bindableProperties(); @@ -1715,6 +1716,20 @@ void tst_qquickstates::trivialWhen() QVERIFY(root); } +void tst_qquickstates::noStateOsciallation() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("noStateOsciallation.qml")); + QScopedPointer<QObject> root {component.create()}; + QVERIFY(root); + // set to 1 on initial transition from "" to "n2" + QCOMPARE(root->property("stateChangeCounter").toInt(), 1); + root->setProperty("number", 1); + // setting number to 1 changes directly from "n2" to "n1" + // without any intermediate transition to "" + QCOMPARE(root->property("stateChangeCounter").toInt(), 2); +} + void tst_qquickstates::parentChangeCorrectReversal() { QQmlEngine engine; |