Port QAbstractAnimation to the new property system

Task-number: QTBUG-85520
Change-Id: I2710c314b8c32b03fccb826fd78482ee7095fdda
Reviewed-by: Andreas Buhr <andreas.buhr@qt.io>
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Sona Kurazyan 2020-12-17 17:33:18 +01:00
parent 31f90e99b8
commit a67002dc5b
5 changed files with 189 additions and 16 deletions

View File

@ -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();
}
/*!

View File

@ -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;

View File

@ -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;

View File

@ -7,4 +7,6 @@
qt_internal_add_test(tst_qabstractanimation
SOURCES
tst_qabstractanimation.cpp
LIBRARIES
Qt::TestPrivate
)

View File

@ -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)