diff options
5 files changed, 189 insertions, 16 deletions
diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index d5d75e8ab3..18dab48e5a 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -930,14 +930,17 @@ void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState) // check if we should Rewind if ((newState == QAbstractAnimation::Paused || newState == QAbstractAnimation::Running) && oldState == QAbstractAnimation::Stopped) { + const int oldTotalCurrentTime = totalCurrentTime; //here we reset the time if needed //we don't call setCurrentTime because this might change the way the animation //behaves: changing the state or changing the current value totalCurrentTime = currentTime = (direction == QAbstractAnimation::Forward) ? 0 : (loopCount == -1 ? q->duration() : q->totalDuration()); + if (totalCurrentTime != oldTotalCurrentTime) + totalCurrentTime.notify(); } - state = newState; + state.setValueBypassingBindings(newState); QPointer<QAbstractAnimation> guard(q); //(un)registration of the animation must always happen before calls to @@ -957,6 +960,7 @@ void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState) return; // Notify state change + state.notify(); emit q->stateChanged(newState, oldState); if (!guard || newState != state) //this is to be safe if updateState changes the state return; @@ -1028,6 +1032,7 @@ QAbstractAnimation::~QAbstractAnimation() if (d->state != Stopped) { QAbstractAnimation::State oldState = d->state; d->state = Stopped; + d->state.notify(); emit stateChanged(d->state, oldState); if (oldState == QAbstractAnimation::Running) QAnimationTimer::unregisterAnimation(this); @@ -1043,6 +1048,11 @@ QAbstractAnimation::~QAbstractAnimation() This property describes the current state of the animation. When the animation state changes, QAbstractAnimation emits the stateChanged() signal. + + \note State updates might cause updates of the currentTime property, + which, in turn, can cancel its bindings. So be careful when setting + bindings to the currentTime property, when you expect the state of the + animation to change. */ QAbstractAnimation::State QAbstractAnimation::state() const { @@ -1050,6 +1060,12 @@ QAbstractAnimation::State QAbstractAnimation::state() const return d->state; } +QBindable<QAbstractAnimation::State> QAbstractAnimation::bindableState() const +{ + Q_D(const QAbstractAnimation); + return &d->state; +} + /*! If this animation is part of a QAnimationGroup, this function returns a pointer to the group; otherwise, it returns \nullptr. @@ -1115,9 +1131,13 @@ QAbstractAnimation::Direction QAbstractAnimation::direction() const void QAbstractAnimation::setDirection(Direction direction) { Q_D(QAbstractAnimation); - if (d->direction == direction) + if (d->direction == direction) { + d->direction.removeBindingUnlessInWrapper(); return; + } + Qt::beginPropertyUpdateGroup(); + const int oldCurrentLoop = d->currentLoop; if (state() == Stopped) { if (direction == Backward) { d->currentTime = duration(); @@ -1140,7 +1160,16 @@ void QAbstractAnimation::setDirection(Direction direction) // needed to update the timer interval in case of a pause animation QAnimationTimer::updateAnimationTimer(); - emit directionChanged(direction); + if (d->currentLoop != oldCurrentLoop) + d->currentLoop.notify(); + d->direction.notify(); + Qt::endPropertyUpdateGroup(); +} + +QBindable<QAbstractAnimation::Direction> QAbstractAnimation::bindableDirection() +{ + Q_D(QAbstractAnimation); + return &d->direction; } /*! @@ -1175,6 +1204,12 @@ void QAbstractAnimation::setLoopCount(int loopCount) d->loopCount = loopCount; } +QBindable<int> QAbstractAnimation::bindableLoopCount() +{ + Q_D(QAbstractAnimation); + return &d->loopCount; +} + /*! \property QAbstractAnimation::currentLoop \brief the current loop of the animation @@ -1194,6 +1229,12 @@ int QAbstractAnimation::currentLoop() const return d->currentLoop; } +QBindable<int> QAbstractAnimation::bindableCurrentLoop() const +{ + Q_D(const QAbstractAnimation); + return &d->currentLoop; +} + /*! \fn virtual int QAbstractAnimation::duration() const = 0 @@ -1252,6 +1293,10 @@ int QAbstractAnimation::currentLoopTime() const The animation's current time starts at 0, and ends at totalDuration(). + \note You can bind other properties to currentTime, but it is not + recommended setting bindings to it. As animation progresses, the currentTime + is updated automatically, which cancels its bindings. + \sa loopCount, currentLoopTime() */ int QAbstractAnimation::currentTime() const @@ -1259,6 +1304,13 @@ int QAbstractAnimation::currentTime() const Q_D(const QAbstractAnimation); return d->totalCurrentTime; } + +QBindable<int> QAbstractAnimation::bindableCurrentTime() +{ + Q_D(QAbstractAnimation); + return &d->totalCurrentTime; +} + void QAbstractAnimation::setCurrentTime(int msecs) { Q_D(QAbstractAnimation); @@ -1269,6 +1321,8 @@ void QAbstractAnimation::setCurrentTime(int msecs) int totalDura = dura <= 0 ? dura : ((d->loopCount < 0) ? -1 : dura * d->loopCount); if (totalDura != -1) msecs = qMin(totalDura, msecs); + + const int oldCurrentTime = d->totalCurrentTime; d->totalCurrentTime = msecs; // Update new values. @@ -1284,13 +1338,13 @@ void QAbstractAnimation::setCurrentTime(int msecs) } else { d->currentTime = (dura <= 0) ? msecs : ((msecs - 1) % dura) + 1; if (d->currentTime == dura) - --d->currentLoop; + d->currentLoop = d->currentLoop - 1; } } updateCurrentTime(d->currentTime); if (d->currentLoop != oldLoop) - emit currentLoopChanged(d->currentLoop); + d->currentLoop.notify(); // All animations are responsible for stopping the animation when their // own end state is reached; in this case the animation is time driven, @@ -1299,6 +1353,8 @@ void QAbstractAnimation::setCurrentTime(int msecs) || (d->direction == Backward && d->totalCurrentTime == 0)) { stop(); } + if (oldCurrentTime != d->totalCurrentTime) + d->totalCurrentTime.notify(); } /*! diff --git a/src/corelib/animation/qabstractanimation.h b/src/corelib/animation/qabstractanimation.h index 2bb1d52582..3165c2b049 100644 --- a/src/corelib/animation/qabstractanimation.h +++ b/src/corelib/animation/qabstractanimation.h @@ -55,11 +55,13 @@ class Q_CORE_EXPORT QAbstractAnimation : public QObject { Q_OBJECT - Q_PROPERTY(State state READ state NOTIFY stateChanged) - Q_PROPERTY(int loopCount READ loopCount WRITE setLoopCount) - Q_PROPERTY(int currentTime READ currentTime WRITE setCurrentTime) - Q_PROPERTY(int currentLoop READ currentLoop NOTIFY currentLoopChanged) - Q_PROPERTY(Direction direction READ direction WRITE setDirection NOTIFY directionChanged) + Q_PROPERTY(State state READ state NOTIFY stateChanged BINDABLE bindableState) + Q_PROPERTY(int loopCount READ loopCount WRITE setLoopCount BINDABLE bindableLoopCount) + Q_PROPERTY(int currentTime READ currentTime WRITE setCurrentTime BINDABLE bindableCurrentTime) + Q_PROPERTY(int currentLoop READ currentLoop NOTIFY currentLoopChanged + BINDABLE bindableCurrentLoop) + Q_PROPERTY(Direction direction READ direction WRITE setDirection NOTIFY directionChanged + BINDABLE bindableDirection) Q_PROPERTY(int duration READ duration) public: @@ -85,18 +87,25 @@ public: virtual ~QAbstractAnimation(); State state() const; + QBindable<QAbstractAnimation::State> bindableState() const; QAnimationGroup *group() const; Direction direction() const; void setDirection(Direction direction); + QBindable<Direction> bindableDirection(); int currentTime() const; + QBindable<int> bindableCurrentTime(); + int currentLoopTime() const; int loopCount() const; void setLoopCount(int loopCount); + QBindable<int> bindableLoopCount(); + int currentLoop() const; + QBindable<int> bindableCurrentLoop() const; virtual int duration() const = 0; int totalDuration() const; diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h index 9debb64424..6a94d2bb8a 100644 --- a/src/corelib/animation/qabstractanimation_p.h +++ b/src/corelib/animation/qabstractanimation_p.h @@ -56,6 +56,7 @@ #include <QtCore/qtimer.h> #include <QtCore/qelapsedtimer.h> #include <private/qobject_p.h> +#include <private/qproperty_p.h> #include <qabstractanimation.h> QT_REQUIRE_CONFIG(animation); @@ -74,14 +75,30 @@ public: return q->d_func(); } - QAbstractAnimation::State state = QAbstractAnimation::Stopped; - QAbstractAnimation::Direction direction = QAbstractAnimation::Forward; + Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QAbstractAnimationPrivate, QAbstractAnimation::State, + state, QAbstractAnimation::Stopped) void setState(QAbstractAnimation::State state); - int totalCurrentTime = 0; + void setDirection(QAbstractAnimation::Direction direction) + { + q_func()->setDirection(direction); + } + void emitDirectionChanged() { emit q_func()->directionChanged(direction); } + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QAbstractAnimationPrivate, QAbstractAnimation::Direction, + direction, &QAbstractAnimationPrivate::setDirection, + &QAbstractAnimationPrivate::emitDirectionChanged, + QAbstractAnimation::Forward) + + void setCurrentTime(int msecs) { q_func()->setCurrentTime(msecs); } + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QAbstractAnimationPrivate, int, totalCurrentTime, + &QAbstractAnimationPrivate::setCurrentTime, 0) int currentTime = 0; - int loopCount = 1; - int currentLoop = 0; + + Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QAbstractAnimationPrivate, int, loopCount, 1) + + void emitCurrentLoopChanged() { emit q_func()->currentLoopChanged(currentLoop); } + Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QAbstractAnimationPrivate, int, currentLoop, nullptr, + &QAbstractAnimationPrivate::emitCurrentLoopChanged, 0) bool deleteWhenStopped = false; bool hasRegisteredTimer = false; diff --git a/tests/auto/corelib/animation/qabstractanimation/CMakeLists.txt b/tests/auto/corelib/animation/qabstractanimation/CMakeLists.txt index 7db3ef9462..1ab378546d 100644 --- a/tests/auto/corelib/animation/qabstractanimation/CMakeLists.txt +++ b/tests/auto/corelib/animation/qabstractanimation/CMakeLists.txt @@ -7,4 +7,6 @@ qt_internal_add_test(tst_qabstractanimation SOURCES tst_qabstractanimation.cpp + LIBRARIES + Qt::TestPrivate ) diff --git a/tests/auto/corelib/animation/qabstractanimation/tst_qabstractanimation.cpp b/tests/auto/corelib/animation/qabstractanimation/tst_qabstractanimation.cpp index b9cf91444f..65e57e2d84 100644 --- a/tests/auto/corelib/animation/qabstractanimation/tst_qabstractanimation.cpp +++ b/tests/auto/corelib/animation/qabstractanimation/tst_qabstractanimation.cpp @@ -30,6 +30,7 @@ #include <QtCore/qabstractanimation.h> #include <QtCore/qanimationgroup.h> #include <QTest> +#include <QtTest/private/qpropertytesthelper_p.h> class tst_QAbstractAnimation : public QObject { @@ -48,6 +49,11 @@ private slots: void avoidJumpAtStart(); void avoidJumpAtStartWithStop(); void avoidJumpAtStartWithRunning(); + void stateBinding(); + void loopCountBinding(); + void currentTimeBinding(); + void currentLoopBinding(); + void directionBinding(); }; class TestableQAbstractAnimation : public QAbstractAnimation @@ -56,7 +62,7 @@ class TestableQAbstractAnimation : public QAbstractAnimation public: TestableQAbstractAnimation() : m_duration(10) {} - virtual ~TestableQAbstractAnimation() {}; + virtual ~TestableQAbstractAnimation() override { } int duration() const override { return m_duration; } virtual void updateCurrentTime(int) override {} @@ -228,6 +234,89 @@ void tst_QAbstractAnimation::avoidJumpAtStartWithRunning() QVERIFY(anim3.currentTime() < 50); } +void tst_QAbstractAnimation::stateBinding() +{ + TestableQAbstractAnimation animation; + QTestPrivate::testReadOnlyPropertyBasics(animation, QAbstractAnimation::Stopped, + QAbstractAnimation::Running, "state", + [&] { animation.start(); }); +} + +void tst_QAbstractAnimation::loopCountBinding() +{ + TestableQAbstractAnimation animation; + QTestPrivate::testReadWritePropertyBasics(animation, 42, 43, "loopCount"); +} + +void tst_QAbstractAnimation::currentTimeBinding() +{ + TestableQAbstractAnimation animation; + + QProperty<int> currentTimeProperty; + animation.bindableCurrentTime().setBinding(Qt::makePropertyBinding(currentTimeProperty)); + QCOMPARE(animation.currentTime(), currentTimeProperty); + + // This should cancel the binding + animation.start(); + + currentTimeProperty = 5; + QVERIFY(animation.currentTime() != currentTimeProperty); + + QTestPrivate::testReadWritePropertyBasics(animation, 6, 7, "currentTime"); +} + +void tst_QAbstractAnimation::currentLoopBinding() +{ + TestableQAbstractAnimation animation; + + QTestPrivate::testReadOnlyPropertyBasics(animation, 0, 3, "currentLoop", [&] { + // Trigger an update of currentLoop + animation.setLoopCount(4); + // This brings us to the end of the animation, so currentLoop should be loopCount - 1 + animation.setCurrentTime(42); + }); +} + +void tst_QAbstractAnimation::directionBinding() +{ + TestableQAbstractAnimation animation; + QTestPrivate::testReadWritePropertyBasics(animation, QAbstractAnimation::Backward, + QAbstractAnimation::Forward, "direction"); + + // setDirection() may trigger a currentLoop update. Make sure the observers + // are notified about direction and currentLoop changes only after a consistent + // state is reached. + QProperty<int> currLoopObserver; + currLoopObserver.setBinding([&] { return animation.currentLoop(); }); + + QProperty<QAbstractAnimation::Direction> directionObserver; + directionObserver.setBinding([&] { return animation.direction(); }); + + animation.setLoopCount(10); + + bool currentLoopChanged = false; + auto currentLoopHandler = animation.bindableCurrentLoop().onValueChanged([&] { + QVERIFY(!currentLoopChanged); + QCOMPARE(currLoopObserver, 9); + QCOMPARE(directionObserver, QAbstractAnimation::Backward); + currentLoopChanged = true; + }); + + bool directionChanged = false; + auto directionHandler = animation.bindableDirection().onValueChanged([&] { + QVERIFY(!directionChanged); + QCOMPARE(currLoopObserver, 9); + QCOMPARE(directionObserver, QAbstractAnimation::Backward); + directionChanged = true; + }); + + QCOMPARE(animation.direction(), QAbstractAnimation::Forward); + // This will set currentLoop to 9 + animation.setDirection(QAbstractAnimation::Backward); + + QVERIFY(currentLoopChanged); + QVERIFY(directionChanged); +} QTEST_MAIN(tst_QAbstractAnimation) |