aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMitch Curtis <mitch.curtis@qt.io>2018-03-07 14:57:29 +0100
committerMitch Curtis <mitch.curtis@qt.io>2018-03-09 09:11:31 +0000
commit31e64606f253663f8ee2032b94521afd1804ab11 (patch)
tree92484cb64b16db769cf0cd37470245e670fd22ae
parent5446be855becab2499611f2bc2280b74160f2e75 (diff)
Animation: add finished() signal
Currently, Animation has a stopped() signal. This is executed even if the animation is stopped manually. To react to an animation finishing naturally, you currently have to do the following: onRunningChanged: if (!running) doStuff() This patch adds a dedicated signal: onFinished: doStuff() [ChangeLog][QtQuick][Animation] Added finished() signal to Animation as a convenient way to react to an animation finishing naturally. Change-Id: I8765d3e8e2b7bf7ef66a6acb69feafb43e9619d3 Reviewed-by: J-P Nurmi <jpnurmi@qt.io> Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
-rw-r--r--src/quick/util/qquickanimation.cpp22
-rw-r--r--src/quick/util/qquickanimation_p.h1
-rw-r--r--src/quick/util/qquickutilmodule.cpp3
-rw-r--r--tests/auto/quick/qquickanimations/data/finished.qml95
-rw-r--r--tests/auto/quick/qquickanimations/tst_qquickanimations.cpp74
5 files changed, 195 insertions, 0 deletions
diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp
index a89f237499..9bbf70b3a4 100644
--- a/src/quick/util/qquickanimation.cpp
+++ b/src/quick/util/qquickanimation.cpp
@@ -234,6 +234,27 @@ QQmlProperty QQuickAbstractAnimationPrivate::createProperty(QObject *obj, const
The corresponding handler is \c onStopped.
*/
+/*!
+ \qmlsignal QtQuick::Animation::finished()
+ \since 5.12
+
+ This signal is emitted when the animation has finished naturally.
+
+ It is not emitted when \l running is set to \c false, nor for animations whose
+ \l loops property is set to \c Animation.Infinite.
+
+ In addition, it is only emitted for top-level, standalone animations. It
+ will not be emitted for animations in a Behavior or Transition, or
+ animations that are part of an animation group.
+
+ If \l alwaysRunToEnd is true, this signal will not be emitted until the
+ animation has completed its current iteration.
+
+ The corresponding handler is \c onFinished.
+
+ \sa stopped(), started(), running
+*/
+
void QQuickAbstractAnimation::setRunning(bool r)
{
Q_D(QQuickAbstractAnimation);
@@ -656,6 +677,7 @@ void QQuickAbstractAnimationPrivate::animationFinished(QAbstractAnimationJob*)
if (loopCount != 1)
animationInstance->setLoopCount(loopCount);
}
+ emit q->finished();
}
QQuickAbstractAnimation::ThreadingModel QQuickAbstractAnimation::threadingModel() const
diff --git a/src/quick/util/qquickanimation_p.h b/src/quick/util/qquickanimation_p.h
index 2293f2597f..746cb938bd 100644
--- a/src/quick/util/qquickanimation_p.h
+++ b/src/quick/util/qquickanimation_p.h
@@ -126,6 +126,7 @@ Q_SIGNALS:
void pausedChanged(bool);
void alwaysRunToEndChanged(bool);
void loopCountChanged(int);
+ Q_REVISION(12) void finished();
public Q_SLOTS:
void restart();
diff --git a/src/quick/util/qquickutilmodule.cpp b/src/quick/util/qquickutilmodule.cpp
index b2e5b84cf4..31d4d4c437 100644
--- a/src/quick/util/qquickutilmodule.cpp
+++ b/src/quick/util/qquickutilmodule.cpp
@@ -132,4 +132,7 @@ void QQuickUtilModule::defineModule()
qmlRegisterType<QQuickShortcut,9>("QtQuick", 2, 9, "Shortcut");
#endif
+
+ qmlRegisterUncreatableType<QQuickAbstractAnimation, 12>("QtQuick", 2, 12, "Animation",
+ QQuickAbstractAnimation::tr("Animation is an abstract class"));
}
diff --git a/tests/auto/quick/qquickanimations/data/finished.qml b/tests/auto/quick/qquickanimations/data/finished.qml
new file mode 100644
index 0000000000..a18b321501
--- /dev/null
+++ b/tests/auto/quick/qquickanimations/data/finished.qml
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:GPL-EXCEPT$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.12
+
+Item {
+ id: root
+ width: 400
+ height: 400
+
+ property bool finishedUsableInQml: false
+
+ property alias simpleTopLevelAnimation: simpleTopLevelAnimation
+ property real foo: 0
+
+ property alias transitionRect: transitionRect
+ property alias transition: transition
+ property alias animationWithinTransition: animationWithinTransition
+
+ property real bar: 0
+ property alias animationWithinBehavior: animationWithinBehavior
+ property alias behavior: behavior
+
+ NumberAnimation {
+ id: simpleTopLevelAnimation
+ target: root
+ property: "foo"
+ from: 0
+ to: 1
+ duration: 10
+
+ onFinished: finishedUsableInQml = true
+ }
+
+ Rectangle {
+ id: transitionRect
+ color: "green"
+ width: 50
+ height: 50
+ anchors.centerIn: parent
+
+ states: State {
+ name: "go"
+ }
+ transitions: Transition {
+ id: transition
+ to: "go"
+ SequentialAnimation {
+ NumberAnimation {
+ id: animationWithinTransition
+ duration: 10
+ property: "foo"
+ from: 1
+ to: 2
+ }
+ }
+ }
+ }
+
+ Behavior on bar {
+ id: behavior
+ NumberAnimation {
+ id: animationWithinBehavior
+ duration: 10
+ property: "bar"
+ from: 0
+ to: 1
+ }
+ }
+}
diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
index de86bb16db..3cfe03a376 100644
--- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
+++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
@@ -105,6 +105,7 @@ private slots:
void pathSvgAnimation();
void pathLineUnspecifiedXYBug();
void unsetAnimatorProxyJobWindow();
+ void finished();
};
#define QTIMED_COMPARE(lhs, rhs) do { \
@@ -1612,6 +1613,79 @@ void tst_qquickanimations::unsetAnimatorProxyJobWindow()
QCOMPARE(proxy.job().data(), job);
}
+void tst_qquickanimations::finished()
+{
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("finished.qml"));
+ QScopedPointer<QObject> root(component.create());
+ QVERIFY(root);
+
+ // Test that finished() is emitted for a simple top-level animation.
+ // (Each test is in its own block so that we can reuse the nice signal names :))
+ {
+ QQuickAbstractAnimation *simpleTopLevelAnimation
+ = root->property("simpleTopLevelAnimation").value<QQuickAbstractAnimation*>();
+ QVERIFY(simpleTopLevelAnimation);
+
+ QSignalSpy stoppedSpy(simpleTopLevelAnimation, SIGNAL(stopped()));
+ QVERIFY(stoppedSpy.isValid());
+
+ QSignalSpy finishedSpy(simpleTopLevelAnimation, SIGNAL(finished()));
+ QVERIFY(finishedSpy.isValid());
+
+ QVERIFY(simpleTopLevelAnimation->setProperty("running", QVariant(true)));
+ QTRY_COMPARE(stoppedSpy.count(), 1);
+ QCOMPARE(finishedSpy.count(), 1);
+
+ // Test that the signal is properly revisioned and hence accessible from QML.
+ QCOMPARE(root->property("finishedUsableInQml").toBool(), true);
+ }
+
+ // Test that finished() is not emitted for animations within a Transition.
+ {
+ QObject *transition = root->property("transition").value<QObject*>();
+ QVERIFY(transition);
+
+ QSignalSpy runningChangedSpy(transition, SIGNAL(runningChanged()));
+ QVERIFY(runningChangedSpy.isValid());
+
+ QQuickAbstractAnimation *animationWithinTransition
+ = root->property("animationWithinTransition").value<QQuickAbstractAnimation*>();
+ QVERIFY(animationWithinTransition);
+
+ QSignalSpy stoppedSpy(animationWithinTransition, SIGNAL(stopped()));
+ QVERIFY(stoppedSpy.isValid());
+
+ QSignalSpy finishedSpy(animationWithinTransition, SIGNAL(finished()));
+ QVERIFY(finishedSpy.isValid());
+
+ QObject *transitionRect = root->property("transitionRect").value<QObject*>();
+ QVERIFY(transitionRect);
+ QVERIFY(transitionRect->setProperty("state", QVariant(QLatin1String("go"))));
+ QTRY_COMPARE(runningChangedSpy.count(), 1);
+ QCOMPARE(stoppedSpy.count(), 0);
+ QCOMPARE(finishedSpy.count(), 0);
+ }
+
+ // Test that finished() is not emitted for animations within a Behavior.
+ {
+ QQuickAbstractAnimation *animationWithinBehavior
+ = root->property("animationWithinBehavior").value<QQuickAbstractAnimation*>();
+ QVERIFY(animationWithinBehavior);
+
+ QSignalSpy stoppedSpy(animationWithinBehavior, SIGNAL(stopped()));
+ QVERIFY(stoppedSpy.isValid());
+
+ QSignalSpy finishedSpy(animationWithinBehavior, SIGNAL(finished()));
+ QVERIFY(finishedSpy.isValid());
+
+ QVERIFY(root->setProperty("bar", QVariant(1.0)));
+ QTRY_COMPARE(root->property("bar").toReal(), 1.0);
+ QCOMPARE(stoppedSpy.count(), 0);
+ QCOMPARE(finishedSpy.count(), 0);
+ }
+}
+
QTEST_MAIN(tst_qquickanimations)
#include "tst_qquickanimations.moc"