diff options
author | Piotr Mikolajczyk <piotr.mikolajczyk@qt.io> | 2020-10-06 10:27:20 +0200 |
---|---|---|
committer | Piotr Mikolajczyk <piotr.mikolajczyk@qt.io> | 2020-10-13 11:57:03 +0200 |
commit | d66d0540dc323e6a536b952acedcfda70cd90c0c (patch) | |
tree | c23de7945f4b104e345afa31a398e5846eb5b872 | |
parent | aba1bf6fca24ec6ee21ffc4417ef05d5d2cfdfeb (diff) |
Fix alwaysRunToEnd==true prevented complex Anim from stopping
AnimatorProxyJob would not forward loopCount to the controlled job causing
the sequential or parallel animation to go infinitely after attempt to stop
Task-number: QTBUG-82890
Pick-to: 5.15
Change-Id: I6a1ca787f06789064e05407bbe9ae5e5861f24d5
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
7 files changed, 177 insertions, 0 deletions
diff --git a/src/qml/animations/qabstractanimationjob.cpp b/src/qml/animations/qabstractanimationjob.cpp index 82f3e53d68..f3c12bce3f 100644 --- a/src/qml/animations/qabstractanimationjob.cpp +++ b/src/qml/animations/qabstractanimationjob.cpp @@ -416,7 +416,10 @@ void QAbstractAnimationJob::setDirection(Direction direction) void QAbstractAnimationJob::setLoopCount(int loopCount) { + if (m_loopCount == loopCount) + return; m_loopCount = loopCount; + updateLoopCount(loopCount); } int QAbstractAnimationJob::totalDuration() const diff --git a/src/qml/animations/qabstractanimationjob_p.h b/src/qml/animations/qabstractanimationjob_p.h index d046ce9def..9490070246 100644 --- a/src/qml/animations/qabstractanimationjob_p.h +++ b/src/qml/animations/qabstractanimationjob_p.h @@ -134,6 +134,7 @@ public: SelfDeletable m_selfDeletable; protected: virtual void updateCurrentTime(int) {} + virtual void updateLoopCount(int) {} virtual void updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState); virtual void updateDirection(QAbstractAnimationJob::Direction direction); virtual void topLevelAnimationLoopChanged() {} diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp index 1c2472dc49..f196005561 100644 --- a/src/quick/util/qquickanimatorjob.cpp +++ b/src/quick/util/qquickanimatorjob.cpp @@ -118,6 +118,11 @@ QQuickAnimatorProxyJob::QQuickAnimatorProxyJob(QAbstractAnimationJob *job, QObje } } +void QQuickAnimatorProxyJob::updateLoopCount(int loopCount) +{ + m_job->setLoopCount(loopCount); +} + QQuickAnimatorProxyJob::~QQuickAnimatorProxyJob() { if (m_job && m_controller) @@ -138,6 +143,10 @@ void QQuickAnimatorProxyJob::updateCurrentTime(int) if (m_internalState != State_Running) return; + // Copy current loop number from the job + // we could make currentLoop() virtual but it would be less efficient + m_currentLoop = m_job->currentLoop(); + // A proxy which is being ticked should be associated with a window, (see // setWindow() below). If we get here when there is no more controller we // have a problem. diff --git a/src/quick/util/qquickanimatorjob_p.h b/src/quick/util/qquickanimatorjob_p.h index 0ec1253118..c79c5341bc 100644 --- a/src/quick/util/qquickanimatorjob_p.h +++ b/src/quick/util/qquickanimatorjob_p.h @@ -86,6 +86,7 @@ public: protected: void updateCurrentTime(int) override; + void updateLoopCount(int) override; void updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState) override; void debugAnimation(QDebug d) const override; diff --git a/tests/auto/quick/qquickanimations/data/alwaysRunToEndInSequentialAnimationBug.qml b/tests/auto/quick/qquickanimations/data/alwaysRunToEndInSequentialAnimationBug.qml new file mode 100644 index 0000000000..a775e5d263 --- /dev/null +++ b/tests/auto/quick/qquickanimations/data/alwaysRunToEndInSequentialAnimationBug.qml @@ -0,0 +1,59 @@ +import QtQuick 2.15 +import QtQuick.Window 2.15 + +Rectangle { + width: 100 + height: 100 + visible: true + color: "blue" + property bool onFinishedCalled : false; + property bool onStoppedCalled : false; + property int loopsMade: 0; + + Rectangle { + id: whiteRect + objectName: "whiteRect" + anchors.fill: parent + color: "white" + opacity: 1 + + } + + property var seqAnim : SequentialAnimation { + loops: Animation.Infinite + alwaysRunToEnd: true + NumberAnimation { + target: whiteRect + properties: "opacity" + from: 1; + to: 0.1; + duration: 500 + } + NumberAnimation { + target: whiteRect + properties: "opacity" + from: 0.1; + to: 1; + duration: 500 + } + ScriptAction { + script: loopsMade++ + } + + onFinished: { + whiteRect.opacity = 1 + onFinishedCalled = true + } + + onStopped : { + whiteRect.opacity = 1 + onStoppedCalled = true + } + + onStarted:{ + onFinishedCalled = false + onStoppedCalled = false + loopsMade = 0 + } + } +} diff --git a/tests/auto/quick/qquickanimations/qquickanimations.pro b/tests/auto/quick/qquickanimations/qquickanimations.pro index 6998c023db..85a77ae620 100644 --- a/tests/auto/quick/qquickanimations/qquickanimations.pro +++ b/tests/auto/quick/qquickanimations/qquickanimations.pro @@ -12,6 +12,7 @@ QT += core-private gui-private qml-private quick-private testlib qmlmodels-priv DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 OTHER_FILES += \ + data/alwaysRunToEndInSequentialAnimationBug.qml \ data/animationJobSelfDestructionBug.qml\ data/attached.qml \ data/badproperty1.qml \ diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp index 8580a2dc1b..fe366d0f34 100644 --- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp +++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp @@ -115,6 +115,7 @@ private slots: void animationJobSelfDestruction(); void fastFlickingBug(); void opacityAnimationFromZero(); + void alwaysRunToEndInSequentialAnimationBug(); }; #define QTIMED_COMPARE(lhs, rhs) do { \ @@ -1895,6 +1896,108 @@ void tst_qquickanimations::opacityAnimationFromZero() QTRY_VERIFY(!img.isNull() && img.pixel(100, 100) > qRgb(10, 10, 10)); } +void tst_qquickanimations::alwaysRunToEndInSequentialAnimationBug() +{ + QQuickView view(QUrl::fromLocalFile("data/alwaysRunToEndInSequentialAnimationBug.qml")); + view.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QVERIFY(view.rootObject()); + QObject *root = view.rootObject(); + QQuickAbstractAnimation* seqAnim = root->property("seqAnim").value<QQuickAbstractAnimation *>(); + QObject *whiteRect = root->findChild<QObject*>("whiteRect"); + + // + // Tesing the SequentialAnimation with alwaysRunToEnd = true + // + + // + // Stopping in the first loop + + QVERIFY(whiteRect->property("opacity").value<qreal>() == 1.0); + seqAnim->start(); + QTRY_VERIFY(seqAnim->isRunning() == true); + + seqAnim->stop(); + QTRY_VERIFY(seqAnim->isRunning() == false); + QTRY_VERIFY(!root->property("onStoppedCalled").value<bool>()); + QTRY_VERIFY(!root->property("onFinishedCalled").value<bool>()); + + //The animation should still be running + QTest::qWait(500); + QTRY_VERIFY(whiteRect->property("opacity").value<qreal>() != 1.0); + + QTest::qWait(500); + + //The animation should now be stopped - after reaching its cycle end + QTRY_COMPARE(whiteRect->property("opacity").value<qreal>(), 1.0); + QTRY_VERIFY(root->property("onStoppedCalled").value<bool>()); + QTRY_VERIFY(root->property("onFinishedCalled").value<bool>()); + + // + // Stopping in the second loop + + QVERIFY(whiteRect->property("opacity").value<qreal>() == 1.0); + seqAnim->start(); + QTRY_VERIFY(seqAnim->isRunning() == true); + + QTRY_COMPARE(root->property("loopsMade").value<int>(), 1); // Wait for cycle no 2 to start + + seqAnim->stop(); + QTRY_VERIFY(seqAnim->isRunning() == false); + QTRY_VERIFY(!root->property("onStoppedCalled").value<bool>()); + QTRY_VERIFY(!root->property("onFinishedCalled").value<bool>()); + + //The animation should still be running + QTest::qWait(500); + QTRY_VERIFY(whiteRect->property("opacity").value<qreal>() != 1.0); + + QTest::qWait(500); + + //The animation should now be stopped - after reaching its cycle end + QTRY_COMPARE(whiteRect->property("opacity").value<qreal>(), 1.0); + QTRY_VERIFY(root->property("onStoppedCalled").value<bool>()); + QTRY_VERIFY(root->property("onFinishedCalled").value<bool>()); + + + // + // Tesing the SequentialAnimation with alwaysRunToEnd = false + // + + // + // Stopping in the first loop + + seqAnim->setProperty("alwaysRunToEnd", false); + QVERIFY(whiteRect->property("opacity").value<qreal>() == 1.0); + + seqAnim->start(); + QTRY_VERIFY(seqAnim->isRunning() == true); + + QTest::qWait(50); + seqAnim->stop(); + //The animation should be stopped immediately + QVERIFY(seqAnim->isRunning() == false); + QVERIFY(root->property("onStoppedCalled").value<bool>()); + QVERIFY(root->property("onFinishedCalled").value<bool>()); + QCOMPARE(whiteRect->property("opacity").value<qreal>(), 1.0); + + // + // Stopping in the second loop + QVERIFY(whiteRect->property("opacity").value<qreal>() == 1.0); + seqAnim->start(); + QTRY_VERIFY(seqAnim->isRunning() == true); + + QTRY_COMPARE(root->property("loopsMade").value<int>(), 1); // Wait for cycle no 2 to start + + QTest::qWait(50); + seqAnim->stop(); + //The animation should be stopped immediately + QVERIFY(seqAnim->isRunning() == false); + QVERIFY(root->property("onStoppedCalled").value<bool>()); + QVERIFY(root->property("onFinishedCalled").value<bool>()); + QCOMPARE(whiteRect->property("opacity").value<qreal>(),1.0); +} + QTEST_MAIN(tst_qquickanimations) #include "tst_qquickanimations.moc" |