diff options
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" |