From 2c6966c775fec86e3c4265cd3a5b204e7e5e183a Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Tue, 29 Oct 2019 09:45:54 -0500 Subject: Allow AnimatedSprite to finish on the last frame [ChangeLog][AnimatedSprite] Add finishBehavior to allow a sprite to finish on the last frame. Task-number: QTBUG-59090 Change-Id: Id45e879cdc4905f43e2ac3cb2529181390d47aab Reviewed-by: Mitch Curtis --- src/quick/items/qquickanimatedsprite.cpp | 35 +++++++++++++++++++++- src/quick/items/qquickanimatedsprite_p.h | 11 ++++++- src/quick/items/qquickanimatedsprite_p_p.h | 5 ++-- .../qquickanimatedsprite/data/finishBehavior.qml | 18 +++++++++++ .../tst_qquickanimatedsprite.cpp | 26 ++++++++++++++++ 5 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 tests/auto/quick/qquickanimatedsprite/data/finishBehavior.qml diff --git a/src/quick/items/qquickanimatedsprite.cpp b/src/quick/items/qquickanimatedsprite.cpp index 21c09f898d..b285fe56ed 100644 --- a/src/quick/items/qquickanimatedsprite.cpp +++ b/src/quick/items/qquickanimatedsprite.cpp @@ -262,6 +262,19 @@ QT_BEGIN_NAMESPACE */ +/*! + \qmlproperty enumeration QtQuick::AnimatedSprite::finishBehavior + + The behavior when the animation finishes on its own. + + \value FinishAtInitialFrame + When the animation finishes it returns to the initial frame. + This is the default behavior. + + \value FinishAtFinalFrame + When the animation finishes it stays on the final frame. +*/ + /*! \qmlmethod int QtQuick::AnimatedSprite::restart() @@ -381,6 +394,12 @@ int QQuickAnimatedSprite::currentFrame() const return d->m_curFrame; } +QQuickAnimatedSprite::FinishBehavior QQuickAnimatedSprite::finishBehavior() const +{ + Q_D(const QQuickAnimatedSprite); + return d->m_finishBehavior; +} + bool QQuickAnimatedSprite::isCurrentFrameChangedConnected() { IS_SIGNAL_CONNECTED(this, QQuickAnimatedSprite, currentFrameChanged, (int)); @@ -704,6 +723,16 @@ void QQuickAnimatedSprite::setCurrentFrame(int arg) //TODO-C: Probably only work } } +void QQuickAnimatedSprite::setFinishBehavior(FinishBehavior arg) +{ + Q_D(QQuickAnimatedSprite); + + if (d->m_finishBehavior != arg) { + d->m_finishBehavior = arg; + Q_EMIT finishBehaviorChanged(arg); + } +} + void QQuickAnimatedSprite::createEngine() { Q_D(QQuickAnimatedSprite); @@ -838,7 +867,11 @@ void QQuickAnimatedSprite::prepareNextFrame(QSGSpriteNode *node) progress = 0; } if (d->m_loops > 0 && d->m_curLoop >= d->m_loops) { - frameAt = 0; + if (d->m_finishBehavior == FinishAtInitialFrame) + frameAt = 0; + else + frameAt = frameCount() - 1; + d->m_curFrame = frameAt; d->m_running = false; emit runningChanged(false); emit finished(); diff --git a/src/quick/items/qquickanimatedsprite_p.h b/src/quick/items/qquickanimatedsprite_p.h index 30f64e9def..c28b6ce3af 100644 --- a/src/quick/items/qquickanimatedsprite_p.h +++ b/src/quick/items/qquickanimatedsprite_p.h @@ -92,6 +92,7 @@ class Q_AUTOTEST_EXPORT QQuickAnimatedSprite : public QQuickItem Q_PROPERTY(int loops READ loops WRITE setLoops NOTIFY loopsChanged) Q_PROPERTY(bool paused READ paused WRITE setPaused NOTIFY pausedChanged) Q_PROPERTY(int currentFrame READ currentFrame WRITE setCurrentFrame NOTIFY currentFrameChanged) + Q_PROPERTY(FinishBehavior finishBehavior READ finishBehavior WRITE setFinishBehavior NOTIFY finishBehaviorChanged REVISION 15) QML_NAMED_ELEMENT(AnimatedSprite) public: @@ -101,6 +102,12 @@ public: }; Q_ENUM(LoopParameters) + enum FinishBehavior { + FinishAtInitialFrame, + FinishAtFinalFrame + }; + Q_ENUM(FinishBehavior) + bool running() const; bool interpolate() const; QUrl source() const; @@ -116,6 +123,7 @@ public: int loops() const; bool paused() const; int currentFrame() const; + FinishBehavior finishBehavior() const; Q_SIGNALS: @@ -135,6 +143,7 @@ Q_SIGNALS: void frameDurationChanged(int arg); void loopsChanged(int arg); void currentFrameChanged(int arg); + Q_REVISION(15) void finishBehaviorChanged(FinishBehavior arg); Q_REVISION(12) void finished(); @@ -163,7 +172,7 @@ public Q_SLOTS: void resetFrameDuration(); void setLoops(int arg); void setCurrentFrame(int arg); - + void setFinishBehavior(FinishBehavior arg); private Q_SLOTS: void createEngine(); diff --git a/src/quick/items/qquickanimatedsprite_p_p.h b/src/quick/items/qquickanimatedsprite_p_p.h index 3610e58861..fb8faefbee 100644 --- a/src/quick/items/qquickanimatedsprite_p_p.h +++ b/src/quick/items/qquickanimatedsprite_p_p.h @@ -57,11 +57,10 @@ QT_REQUIRE_CONFIG(quick_sprite); #include "qquickitem_p.h" #include "qquicksprite_p.h" +#include "qquickanimatedsprite_p.h" QT_BEGIN_NAMESPACE -class QQuickAnimatedSprite; - class QQuickAnimatedSpritePrivate : public QQuickItemPrivate { Q_DECLARE_PUBLIC(QQuickAnimatedSprite) @@ -78,6 +77,7 @@ public: , m_loops(-1) , m_curLoop(0) , m_pauseOffset(0) + , m_finishBehavior(QQuickAnimatedSprite::FinishAtInitialFrame) { } @@ -93,6 +93,7 @@ public: int m_loops; int m_curLoop; int m_pauseOffset; + QQuickAnimatedSprite::FinishBehavior m_finishBehavior; }; QT_END_NAMESPACE diff --git a/tests/auto/quick/qquickanimatedsprite/data/finishBehavior.qml b/tests/auto/quick/qquickanimatedsprite/data/finishBehavior.qml new file mode 100644 index 0000000000..13a0ef4622 --- /dev/null +++ b/tests/auto/quick/qquickanimatedsprite/data/finishBehavior.qml @@ -0,0 +1,18 @@ +import QtQuick 2.15 + +Rectangle { + color: "black" + width: 320 + height: 320 + + AnimatedSprite { + objectName: "sprite" + loops: 1 + source: "squarefacesprite.png" + frameCount: 6 + frameDuration: 64 + width: 160 + height: 160 + finishBehavior: AnimatedSprite.FinishAtFinalFrame + } +} diff --git a/tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp b/tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp index b5366e2bb9..9f616c56e2 100644 --- a/tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp +++ b/tests/auto/quick/qquickanimatedsprite/tst_qquickanimatedsprite.cpp @@ -57,6 +57,7 @@ private slots: void test_changeSourceToSmallerImgKeepingBigFrameSize(); void test_infiniteLoops(); void test_implicitSize(); + void test_finishBehavior(); }; void tst_qquickanimatedsprite::initTestCase() @@ -428,6 +429,31 @@ void tst_qquickanimatedsprite::test_infiniteLoops() QCOMPARE(finishedSpy.count(), 0); } +void tst_qquickanimatedsprite::test_finishBehavior() +{ + QQuickView window; + window.setSource(testFileUrl("finishBehavior.qml")); + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + QVERIFY(window.rootObject()); + + QQuickAnimatedSprite* sprite = window.rootObject()->findChild("sprite"); + QVERIFY(sprite); + + QTRY_VERIFY(sprite->running()); + + // correctly stops at last frame + QSignalSpy finishedSpy(sprite, SIGNAL(finished())); + QVERIFY(finishedSpy.wait(2000)); + QCOMPARE(sprite->running(), false); + QCOMPARE(sprite->currentFrame(), 5); + + // correctly starts a second time + sprite->start(); + QTRY_VERIFY(sprite->running()); + QTRY_COMPARE(sprite->currentFrame(), 5); +} + QTEST_MAIN(tst_qquickanimatedsprite) #include "tst_qquickanimatedsprite.moc" -- cgit v1.2.3