aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/animations/qabstractanimationjob.cpp69
-rw-r--r--src/qml/animations/qabstractanimationjob_p.h3
-rw-r--r--src/qml/animations/qanimationgroupjob.cpp2
-rw-r--r--src/qml/animations/qparallelanimationgroupjob.cpp13
-rw-r--r--src/qml/animations/qsequentialanimationgroupjob.cpp43
-rw-r--r--src/quick/util/qquickanimation.cpp1
-rw-r--r--src/quick/util/qquickanimatorjob.cpp1
-rw-r--r--tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp44
-rw-r--r--tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp45
9 files changed, 191 insertions, 30 deletions
diff --git a/src/qml/animations/qabstractanimationjob.cpp b/src/qml/animations/qabstractanimationjob.cpp
index da961763ed..d9e3aff5a4 100644
--- a/src/qml/animations/qabstractanimationjob.cpp
+++ b/src/qml/animations/qabstractanimationjob.cpp
@@ -269,6 +269,7 @@ QAbstractAnimationJob::QAbstractAnimationJob()
, m_currentTime(0)
, m_currentLoop(0)
, m_uncontrolledFinishTime(-1)
+ , m_currentLoopStartTime(0)
, m_nextSibling(0)
, m_previousSibling(0)
, m_wasDeleted(0)
@@ -304,6 +305,14 @@ QAbstractAnimationJob::~QAbstractAnimationJob()
m_group->removeAnimation(this);
}
+void QAbstractAnimationJob::fireTopLevelAnimationLoopChanged()
+{
+ m_uncontrolledFinishTime = -1;
+ if (m_group)
+ m_currentLoopStartTime = 0;
+ topLevelAnimationLoopChanged();
+}
+
void QAbstractAnimationJob::setState(QAbstractAnimationJob::State newState)
{
if (m_state == newState)
@@ -324,6 +333,11 @@ void QAbstractAnimationJob::setState(QAbstractAnimationJob::State newState)
//behaves: changing the state or changing the current value
m_totalCurrentTime = m_currentTime = (m_direction == Forward) ?
0 : (m_loopCount == -1 ? duration() : totalDuration());
+
+ // Reset uncontrolled finish time and currentLoopStartTime for this run.
+ m_uncontrolledFinishTime = -1;
+ if (!m_group)
+ m_currentLoopStartTime = m_totalCurrentTime;
}
m_state = newState;
@@ -341,7 +355,7 @@ void QAbstractAnimationJob::setState(QAbstractAnimationJob::State newState)
//starting an animation qualifies as a top level loop change
if (newState == Running && oldState == Stopped && !m_group)
- topLevelAnimationLoopChanged();
+ fireTopLevelAnimationLoopChanged();
RETURN_IF_DELETED(updateState(newState, oldState));
@@ -430,30 +444,49 @@ void QAbstractAnimationJob::setCurrentTime(int msecs)
msecs = qMax(msecs, 0);
// Calculate new time and loop.
int dura = duration();
- int totalDura = dura <= 0 ? dura : ((m_loopCount < 0) ? -1 : dura * m_loopCount);
- if (totalDura != -1)
- msecs = qMin(totalDura, msecs);
- m_totalCurrentTime = msecs;
-
- // Update new values.
+ int totalDura;
int oldLoop = m_currentLoop;
- m_currentLoop = ((dura <= 0) ? 0 : (msecs / dura));
- if (m_currentLoop == m_loopCount) {
- //we're at the end
- m_currentTime = qMax(0, dura);
- m_currentLoop = qMax(0, m_loopCount - 1);
+
+ if (dura < 0 && m_direction == Forward) {
+ totalDura = -1;
+ if (m_uncontrolledFinishTime >= 0 && msecs >= m_uncontrolledFinishTime) {
+ msecs = m_uncontrolledFinishTime;
+ if (m_currentLoop == m_loopCount - 1) {
+ totalDura = m_uncontrolledFinishTime;
+ } else {
+ ++m_currentLoop;
+ m_currentLoopStartTime = msecs;
+ m_uncontrolledFinishTime = -1;
+ }
+ }
+ m_totalCurrentTime = msecs;
+ m_currentTime = msecs - m_currentLoopStartTime;
} else {
- if (m_direction == Forward) {
- m_currentTime = (dura <= 0) ? msecs : (msecs % dura);
+ totalDura = dura <= 0 ? dura : ((m_loopCount < 0) ? -1 : dura * m_loopCount);
+ if (totalDura != -1)
+ msecs = qMin(totalDura, msecs);
+ m_totalCurrentTime = msecs;
+
+ // Update new values.
+ m_currentLoop = ((dura <= 0) ? 0 : (msecs / dura));
+ if (m_currentLoop == m_loopCount) {
+ //we're at the end
+ m_currentTime = qMax(0, dura);
+ m_currentLoop = qMax(0, m_loopCount - 1);
} else {
- m_currentTime = (dura <= 0) ? msecs : ((msecs - 1) % dura) + 1;
- if (m_currentTime == dura)
- --m_currentLoop;
+ if (m_direction == Forward) {
+ m_currentTime = (dura <= 0) ? msecs : (msecs % dura);
+ } else {
+ m_currentTime = (dura <= 0) ? msecs : ((msecs - 1) % dura) + 1;
+ if (m_currentTime == dura)
+ --m_currentLoop;
+ }
}
}
+
if (m_currentLoop != oldLoop && !m_group) //### verify Running as well?
- topLevelAnimationLoopChanged();
+ fireTopLevelAnimationLoopChanged();
RETURN_IF_DELETED(updateCurrentTime(m_currentTime));
diff --git a/src/qml/animations/qabstractanimationjob_p.h b/src/qml/animations/qabstractanimationjob_p.h
index 95a238405c..d50bc4d849 100644
--- a/src/qml/animations/qabstractanimationjob_p.h
+++ b/src/qml/animations/qabstractanimationjob_p.h
@@ -123,6 +123,8 @@ protected:
virtual void updateDirection(QAbstractAnimationJob::Direction direction);
virtual void topLevelAnimationLoopChanged() {}
+ void fireTopLevelAnimationLoopChanged();
+
void setState(QAbstractAnimationJob::State state);
void finished();
@@ -143,6 +145,7 @@ protected:
int m_currentLoop;
//records the finish time for an uncontrolled animation (used by animation groups)
int m_uncontrolledFinishTime;
+ int m_currentLoopStartTime; // used together with m_uncontrolledFinishTime
struct ChangeListener {
ChangeListener(QAnimationJobChangeListener *l, QAbstractAnimationJob::ChangeTypes t) : listener(l), types(t) {}
diff --git a/src/qml/animations/qanimationgroupjob.cpp b/src/qml/animations/qanimationgroupjob.cpp
index 716beb9ce1..afdf07639b 100644
--- a/src/qml/animations/qanimationgroupjob.cpp
+++ b/src/qml/animations/qanimationgroupjob.cpp
@@ -57,7 +57,7 @@ QAnimationGroupJob::~QAnimationGroupJob()
void QAnimationGroupJob::topLevelAnimationLoopChanged()
{
for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling())
- animation->topLevelAnimationLoopChanged();
+ animation->fireTopLevelAnimationLoopChanged();
}
void QAnimationGroupJob::appendAnimation(QAbstractAnimationJob *animation)
diff --git a/src/qml/animations/qparallelanimationgroupjob.cpp b/src/qml/animations/qparallelanimationgroupjob.cpp
index c49bb873b5..43dda478f7 100644
--- a/src/qml/animations/qparallelanimationgroupjob.cpp
+++ b/src/qml/animations/qparallelanimationgroupjob.cpp
@@ -216,11 +216,20 @@ void QParallelAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimatio
return;
int maxDuration = 0;
- for (QAbstractAnimationJob *job = firstChild(); job; job = job->nextSibling())
+ bool running = false;
+ for (QAbstractAnimationJob *job = firstChild(); job; job = job->nextSibling()) {
+ if (job->state() == Running)
+ running = true;
maxDuration = qMax(maxDuration, job->totalDuration());
+ }
+
+ setUncontrolledAnimationFinishTime(this, qMax(maxDuration, currentTime()));
- if (m_currentTime >= maxDuration)
+ if (!running
+ && ((m_direction == Forward && m_currentLoop == m_loopCount -1)
+ || m_direction == Backward && m_currentLoop == 0)) {
stop();
+ }
}
QT_END_NAMESPACE
diff --git a/src/qml/animations/qsequentialanimationgroupjob.cpp b/src/qml/animations/qsequentialanimationgroupjob.cpp
index b82e1850f7..41a83cefa2 100644
--- a/src/qml/animations/qsequentialanimationgroupjob.cpp
+++ b/src/qml/animations/qsequentialanimationgroupjob.cpp
@@ -64,6 +64,7 @@ bool QSequentialAnimationGroupJob::atEnd() const
// 2. the direction is forward
// 3. the current animation is the last one
// 4. the current animation has reached its end
+
const int animTotalCurrentTime = m_currentAnimation->currentTime();
return (m_currentLoop == m_loopCount - 1
&& m_direction == Forward
@@ -101,8 +102,9 @@ QSequentialAnimationGroupJob::AnimationIndex QSequentialAnimationGroupJob::index
return ret;
}
- if (anim == m_currentAnimation)
+ if (anim == m_currentAnimation) {
ret.afterCurrent = true;
+ }
// 'animation' has a non-null defined duration and is not the one at time 'msecs'.
ret.timeOffset += duration;
@@ -211,6 +213,7 @@ void QSequentialAnimationGroupJob::updateCurrentTime(int currentTime)
|| (m_previousLoop == m_currentLoop && m_currentAnimation != newAnimationIndex.animation && newAnimationIndex.afterCurrent)) {
// advancing with forward direction is the same as rewinding with backwards direction
RETURN_IF_DELETED(advanceForwards(newAnimationIndex));
+
} else if (m_previousLoop > m_currentLoop
|| (m_previousLoop == m_currentLoop && m_currentAnimation != newAnimationIndex.animation && !newAnimationIndex.afterCurrent)) {
// rewinding with forward direction is the same as advancing with backwards direction
@@ -319,17 +322,41 @@ void QSequentialAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimat
setUncontrolledAnimationFinishTime(m_currentAnimation, m_currentAnimation->currentTime());
- if ((m_direction == Forward && m_currentAnimation == lastChild())
- || (m_direction == Backward && m_currentAnimation == firstChild())) {
- // we don't handle looping of a group with undefined duration
- stop();
- } else if (m_direction == Forward) {
+ int totalTime = currentTime();
+ if (m_direction == Forward) {
// set the current animation to be the next one
- setCurrentAnimation(m_currentAnimation->nextSibling());
+ if (m_currentAnimation->nextSibling())
+ setCurrentAnimation(m_currentAnimation->nextSibling());
+
+ for (QAbstractAnimationJob *a = animation->nextSibling(); a; a = a->nextSibling()) {
+ int dur = a->duration();
+ if (dur == -1) {
+ totalTime = -1;
+ break;
+ } else {
+ totalTime += dur;
+ }
+ }
+
} else {
// set the current animation to be the previous one
- setCurrentAnimation(m_currentAnimation->previousSibling());
+ if (m_currentAnimation->previousSibling())
+ setCurrentAnimation(m_currentAnimation->previousSibling());
+
+ for (QAbstractAnimationJob *a = animation->previousSibling(); a; a = a->previousSibling()) {
+ int dur = a->duration();
+ if (dur == -1) {
+ totalTime = -1;
+ break;
+ } else {
+ totalTime += dur;
+ }
+ }
}
+ if (totalTime >= 0)
+ setUncontrolledAnimationFinishTime(this, totalTime);
+ if (atEnd())
+ stop();
}
void QSequentialAnimationGroupJob::animationInserted(QAbstractAnimationJob *anim)
diff --git a/src/quick/util/qquickanimation.cpp b/src/quick/util/qquickanimation.cpp
index 60953528f6..06b4e83c0d 100644
--- a/src/quick/util/qquickanimation.cpp
+++ b/src/quick/util/qquickanimation.cpp
@@ -1921,6 +1921,7 @@ void QQuickBulkValueAnimator::topLevelAnimationLoopChanged()
//check for new from every top-level loop (when the top level animation is started and all subsequent loops)
if (fromSourced)
*fromSourced = false;
+ QAbstractAnimationJob::topLevelAnimationLoopChanged();
}
/*!
diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp
index 3270faa652..ea8da018b0 100644
--- a/src/quick/util/qquickanimatorjob.cpp
+++ b/src/quick/util/qquickanimatorjob.cpp
@@ -185,7 +185,6 @@ bool QQuickAnimatorProxyJob::event(QEvent *e)
if ((uint) e->type() == QQuickAnimatorController::AnimationFinished) {
// Update the duration of this proxy to the current time and stop it so
// that parent animations can progress gracefully
- m_duration = m_currentTime;
stop();
return true;
}
diff --git a/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp b/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp
index 3fed59ea20..149f034903 100644
--- a/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp
+++ b/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp
@@ -64,6 +64,7 @@ private slots:
void startGroupWithRunningChild();
void zeroDurationAnimation();
void stopUncontrolledAnimations();
+ void uncontrolledWithLoops();
void loopCount_data();
void loopCount();
void addAndRemoveDuration();
@@ -779,6 +780,9 @@ void tst_QParallelAnimationGroupJob::loopCount_data()
}
+#undef Stopped
+#undef Running
+
void tst_QParallelAnimationGroupJob::loopCount()
{
QFETCH(bool, directionBackward);
@@ -926,6 +930,46 @@ void tst_QParallelAnimationGroupJob::crashWhenRemovingUncontrolledAnimation()
delete anim2;
}
+void tst_QParallelAnimationGroupJob::uncontrolledWithLoops()
+{
+ QParallelAnimationGroupJob group;
+
+ TestAnimation *plain = new TestAnimation(100);
+ TestAnimation *loopsForever = new TestAnimation();
+ UncontrolledAnimation *notTimeBased = new UncontrolledAnimation();
+
+ loopsForever->setLoopCount(-1);
+
+ group.appendAnimation(plain);
+ group.appendAnimation(loopsForever);
+ group.appendAnimation(notTimeBased);
+
+ StateChangeListener listener;
+ group.addAnimationChangeListener(&listener, QAbstractAnimationJob::CurrentLoop);
+ group.setLoopCount(2);
+
+ group.start();
+
+ QCOMPARE(group.currentLoop(), 0);
+ QCOMPARE(group.state(), QAbstractAnimationJob::Running);
+ QCOMPARE(plain->state(), QAbstractAnimationJob::Running);
+ QCOMPARE(loopsForever->state(), QAbstractAnimationJob::Running);
+ QCOMPARE(notTimeBased->state(), QAbstractAnimationJob::Running);
+
+ loopsForever->stop();
+ notTimeBased->stop();
+
+ QTRY_COMPARE(group.currentLoop(), 1);
+ QCOMPARE(group.state(), QAbstractAnimationJob::Running);
+ QCOMPARE(loopsForever->state(), QAbstractAnimationJob::Running);
+ QCOMPARE(notTimeBased->state(), QAbstractAnimationJob::Running);
+
+ loopsForever->stop();
+ notTimeBased->stop();
+
+ QTRY_COMPARE(group.state(), QAbstractAnimationJob::Stopped);
+}
+
QTEST_MAIN(tst_QParallelAnimationGroupJob)
#include "tst_qparallelanimationgroupjob.moc"
diff --git a/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp b/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp
index bb626293aa..591e8f2665 100644
--- a/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp
+++ b/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp
@@ -73,6 +73,7 @@ private slots:
void startGroupWithRunningChild();
void zeroDurationAnimation();
void stopUncontrolledAnimations();
+ void uncontrolledWithLoops();
void finishWithUncontrolledAnimation();
void addRemoveAnimation();
void currentAnimation();
@@ -1613,5 +1614,49 @@ void tst_QSequentialAnimationGroupJob::pauseResume()
anim->removeAnimationChangeListener(&spy, QAbstractAnimationJob::StateChange);
}
+
+void tst_QSequentialAnimationGroupJob::uncontrolledWithLoops()
+{
+ QSequentialAnimationGroupJob group;
+
+ TestAnimation *plain = new TestAnimation(100);
+ TestAnimation *loopsForever = new TestAnimation();
+ UncontrolledAnimation *notTimeBased = new UncontrolledAnimation();
+
+ loopsForever->setLoopCount(-1);
+
+ group.appendAnimation(plain);
+ group.appendAnimation(loopsForever);
+ group.appendAnimation(notTimeBased);
+
+ StateChangeListener listener;
+ group.addAnimationChangeListener(&listener, QAbstractAnimationJob::CurrentLoop);
+ group.setLoopCount(2);
+
+ group.start();
+
+ QCOMPARE(group.currentLoop(), 0);
+ QCOMPARE(group.state(), QAbstractAnimationJob::Running);
+ QTRY_COMPARE(plain->state(), QAbstractAnimationJob::Running);
+
+ QTRY_COMPARE(loopsForever->state(), QAbstractAnimationJob::Running);
+ loopsForever->stop();
+ QTRY_COMPARE(notTimeBased->state(), QAbstractAnimationJob::Running);
+ QTRY_COMPARE(notTimeBased->state(), QAbstractAnimationJob::Stopped); // Stops on its own after 250ms
+
+ QTRY_COMPARE(group.currentLoop(), 1);
+
+ QCOMPARE(group.state(), QAbstractAnimationJob::Running);
+ QTRY_COMPARE(plain->state(), QAbstractAnimationJob::Running);
+ QTRY_COMPARE(plain->state(), QAbstractAnimationJob::Stopped);
+ QTRY_COMPARE(loopsForever->state(), QAbstractAnimationJob::Running);
+ loopsForever->stop();
+ QTRY_COMPARE(notTimeBased->state(), QAbstractAnimationJob::Running);
+ QTRY_COMPARE(notTimeBased->state(), QAbstractAnimationJob::Stopped);
+
+ QTRY_COMPARE(group.state(), QAbstractAnimationJob::Stopped);
+}
+
+
QTEST_MAIN(tst_QSequentialAnimationGroupJob)
#include "tst_qsequentialanimationgroupjob.moc"