diff options
author | Leonardo Sobral Cunha <leo.cunha@nokia.com> | 2009-10-06 17:00:34 (GMT) |
---|---|---|
committer | Leonardo Sobral Cunha <leo.cunha@nokia.com> | 2009-10-07 15:37:47 (GMT) |
commit | cd32bfdd74a9615d28670a197296821d7cf2844d (patch) | |
tree | 2701c2e340c91ad4895c0e6014035abdc14104a2 /src/corelib/animation/qabstractanimation.cpp | |
parent | 232212660535bddee054f3c60b7e8a9dfcc8c582 (diff) | |
download | Qt-cd32bfdd74a9615d28670a197296821d7cf2844d.zip Qt-cd32bfdd74a9615d28670a197296821d7cf2844d.tar.gz Qt-cd32bfdd74a9615d28670a197296821d7cf2844d.tar.bz2 |
Avoids timer ticks when there are only pause animations running
When there are only pause animations running, the timer will stop and
restart when the closest pause animation finishes. While there are only
pause animations running, there are no additional timer ticks, but
if there is at least one animation running that is not a group or a pause,
then the global animation timer will restore it's update interval.
Includes a new auto-test for the QPauseAnimation class.
Task-number: QT-941
Reviewed-by: thierry
Reviewed-by: janarve
Diffstat (limited to 'src/corelib/animation/qabstractanimation.cpp')
-rw-r--r-- | src/corelib/animation/qabstractanimation.cpp | 187 |
1 files changed, 147 insertions, 40 deletions
diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index a4a8853..9e50784 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -144,6 +144,7 @@ #include "qabstractanimation.h" #include "qanimationgroup.h" + #include <QtCore/qdebug.h> #include "qabstractanimation_p.h" @@ -176,7 +177,8 @@ Q_GLOBAL_STATIC(QThreadStorage<QUnifiedTimer *>, unifiedTimer) QUnifiedTimer::QUnifiedTimer() : QObject(), lastTick(0), timingInterval(DEFAULT_TIMER_INTERVAL), - currentAnimationIdx(0), consistentTiming(false) + currentAnimationIdx(0), consistentTiming(false), isPauseTimerActive(false), + runningLeafAnimations(0) { } @@ -192,50 +194,96 @@ QUnifiedTimer *QUnifiedTimer::instance() return inst; } +void QUnifiedTimer::ensureTimerUpdate(QAbstractAnimation *animation) +{ + if (isPauseTimerActive) { + updateAnimationsTime(); + } else { + // this code is needed when ensureTimerUpdate is called from setState because we update + // the currentTime when an animation starts running (otherwise we could remove it) + animation->setCurrentTime(animation->currentTime()); + } +} + +void QUnifiedTimer::updateAnimationsTime() +{ + // this is simply the time we last received a tick + const int oldLastTick = lastTick; + // ignore consistentTiming in case the pause timer is active + if (consistentTiming && !isPauseTimerActive) + lastTick = oldLastTick + timingInterval; + else + lastTick = time.elapsed(); + const int delta = lastTick - oldLastTick; + + //we make sure we only call update time if the time has actually changed + //it might happen in some cases that the time doesn't change because events are delayed + //when the CPU load is high + if (delta) { + for (currentAnimationIdx = 0; currentAnimationIdx < animations.count(); ++currentAnimationIdx) { + QAbstractAnimation *animation = animations.at(currentAnimationIdx); + int elapsed = QAbstractAnimationPrivate::get(animation)->totalCurrentTime + + (animation->direction() == QAbstractAnimation::Forward ? delta : -delta); + animation->setCurrentTime(elapsed); + } + currentAnimationIdx = 0; + } +} + +void QUnifiedTimer::restartAnimationTimer() +{ + if (runningLeafAnimations == 0 && !runningPauseAnimations.isEmpty()) { + int closestTimeToFinish = closestPauseAnimationTimeToFinish(); + animationTimer.start(closestTimeToFinish, this); + isPauseTimerActive = true; + } else if (!animationTimer.isActive() || isPauseTimerActive) { + animationTimer.start(timingInterval, this); + isPauseTimerActive = false; + } +} + void QUnifiedTimer::timerEvent(QTimerEvent *event) { if (event->timerId() == startStopAnimationTimer.timerId()) { startStopAnimationTimer.stop(); + //we transfer the waiting animations into the "really running" state animations += animationsToStart; animationsToStart.clear(); if (animations.isEmpty()) { animationTimer.stop(); - } else if (!animationTimer.isActive()) { - animationTimer.start(timingInterval, this); - lastTick = 0; - time.start(); - } - } else if (event->timerId() == animationTimer.timerId()) { - //this is simply the time we last received a tick - const int oldLastTick = lastTick; - lastTick = consistentTiming ? oldLastTick + timingInterval : time.elapsed(); - - //we make sure we only call update time if the time has actually changed - //it might happen in some cases that the time doesn't change because events are delayed - //when the CPU load is high - if (const int delta = lastTick - oldLastTick) { - for (currentAnimationIdx = 0; currentAnimationIdx < animations.count(); ++currentAnimationIdx) { - QAbstractAnimation *animation = animations.at(currentAnimationIdx); - int elapsed = QAbstractAnimationPrivate::get(animation)->totalCurrentTime - + (animation->direction() == QAbstractAnimation::Forward ? delta : -delta); - animation->setCurrentTime(elapsed); + isPauseTimerActive = false; + // invalidate the start reference time + time = QTime(); + } else { + restartAnimationTimer(); + if (!time.isValid()) { + lastTick = 0; + time.start(); } - currentAnimationIdx = 0; } + } else if (event->timerId() == animationTimer.timerId()) { + // update current time on all top level animations + updateAnimationsTime(); + restartAnimationTimer(); } } -void QUnifiedTimer::registerAnimation(QAbstractAnimation *animation) +void QUnifiedTimer::registerAnimation(QAbstractAnimation *animation, bool isTopLevel) { - Q_ASSERT(!QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer); - QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer = true; - animationsToStart << animation; - startStopAnimationTimer.start(STARTSTOP_TIMER_DELAY, this); + registerRunningAnimation(animation); + if (isTopLevel) { + Q_ASSERT(!QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer); + QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer = true; + animationsToStart << animation; + startStopAnimationTimer.start(STARTSTOP_TIMER_DELAY, this); + } } void QUnifiedTimer::unregisterAnimation(QAbstractAnimation *animation) { + unregisterRunningAnimation(animation); + if (!QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer) return; @@ -253,6 +301,46 @@ void QUnifiedTimer::unregisterAnimation(QAbstractAnimation *animation) QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer = false; } +void QUnifiedTimer::registerRunningAnimation(QAbstractAnimation *animation) +{ + if (QAbstractAnimationPrivate::get(animation)->isGroup) + return; + + if (QAbstractAnimationPrivate::get(animation)->isPause) + runningPauseAnimations << animation; + else + runningLeafAnimations++; +} + +void QUnifiedTimer::unregisterRunningAnimation(QAbstractAnimation *animation) +{ + if (QAbstractAnimationPrivate::get(animation)->isGroup) + return; + + if (QAbstractAnimationPrivate::get(animation)->isPause) + runningPauseAnimations.removeOne(animation); + else + runningLeafAnimations--; +} + +int QUnifiedTimer::closestPauseAnimationTimeToFinish() +{ + int closestTimeToFinish = INT_MAX; + for (int i = 0; i < runningPauseAnimations.size(); ++i) { + QAbstractAnimation *animation = runningPauseAnimations.at(i); + int timeToFinish; + + if (animation->direction() == QAbstractAnimation::Forward) + timeToFinish = animation->totalDuration() - QAbstractAnimationPrivate::get(animation)->totalCurrentTime; + else + timeToFinish = QAbstractAnimationPrivate::get(animation)->totalCurrentTime; + + if (timeToFinish < closestTimeToFinish) + closestTimeToFinish = timeToFinish; + } + return closestTimeToFinish; +} + void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState) { Q_Q(QAbstractAnimation); @@ -270,7 +358,7 @@ void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState) //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) ? + totalCurrentTime = currentTime = (direction == QAbstractAnimation::Forward) ? 0 : (loopCount == -1 ? q->duration() : q->totalDuration()); } @@ -292,23 +380,31 @@ void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState) switch (state) { case QAbstractAnimation::Paused: - q->setCurrentTime(currentTime); + if (hasRegisteredTimer) + // currentTime needs to be updated if pauseTimer is active + QUnifiedTimer::instance()->ensureTimerUpdate(q); if (!guard) return; QUnifiedTimer::instance()->unregisterAnimation(q); break; case QAbstractAnimation::Running: - // this ensures that the value is updated now that the animation is running - if (oldState == QAbstractAnimation::Stopped) { - q->setCurrentTime(currentTime); - if (!guard) - return; - } + { + bool isTopLevel = !group || group->state() == QAbstractAnimation::Stopped; + + // this ensures that the value is updated now that the animation is running + if (oldState == QAbstractAnimation::Stopped) { + if (isTopLevel) + // currentTime needs to be updated if pauseTimer is active + QUnifiedTimer::instance()->ensureTimerUpdate(q); + if (!guard) + return; + } - if (state == QAbstractAnimation::Running) { - // Register timer if our parent is not running - if (!group || group->state() == QAbstractAnimation::Stopped) - QUnifiedTimer::instance()->registerAnimation(q); + // test needed in case we stop in the setCurrentTime inside ensureTimerUpdate (zero duration) + if (state == QAbstractAnimation::Running) { + // register timer if our parent is not running + QUnifiedTimer::instance()->registerAnimation(q, isTopLevel); + } } break; case QAbstractAnimation::Stopped: @@ -453,7 +549,6 @@ void QAbstractAnimation::setDirection(Direction direction) if (d->direction == direction) return; - d->direction = direction; if (state() == Stopped) { if (direction == Backward) { d->currentTime = duration(); @@ -463,7 +558,19 @@ void QAbstractAnimation::setDirection(Direction direction) d->currentLoop = 0; } } + + // the commands order below is important: first we need to setCurrentTime with the old direction, + // then update the direction on this and all children and finally restart the pauseTimer if needed + if (d->hasRegisteredTimer) + QUnifiedTimer::instance()->ensureTimerUpdate(this); + + d->direction = direction; updateDirection(direction); + + if (d->hasRegisteredTimer) + // needed to update the timer interval in case of a pause animation + QUnifiedTimer::instance()->restartAnimationTimer(); + emit directionChanged(direction); } @@ -660,7 +767,7 @@ void QAbstractAnimation::stop() /*! Pauses the animation. When the animation is paused, state() returns Paused. - The value of currentTime will remain unchanged until resume() or start() + The value of currentTime will remain unchanged until resume() or start() is called. If you want to continue from the current time, call resume(). \sa start(), state(), resume() |