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 | |
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')
-rw-r--r-- | src/corelib/animation/qabstractanimation.cpp | 187 | ||||
-rw-r--r-- | src/corelib/animation/qabstractanimation_p.h | 41 | ||||
-rw-r--r-- | src/corelib/animation/qanimationgroup_p.h | 4 | ||||
-rw-r--r-- | src/corelib/animation/qpauseanimation.cpp | 1 |
4 files changed, 186 insertions, 47 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() diff --git a/src/corelib/animation/qabstractanimation_p.h b/src/corelib/animation/qabstractanimation_p.h index 8066d32..bef0499 100644 --- a/src/corelib/animation/qabstractanimation_p.h +++ b/src/corelib/animation/qabstractanimation_p.h @@ -70,12 +70,14 @@ public: QAbstractAnimationPrivate() : state(QAbstractAnimation::Stopped), direction(QAbstractAnimation::Forward), - deleteWhenStopped(false), totalCurrentTime(0), currentTime(0), loopCount(1), currentLoop(0), + deleteWhenStopped(false), hasRegisteredTimer(false), + isPause(false), + isGroup(false), group(0) { } @@ -89,7 +91,6 @@ public: QAbstractAnimation::State state; QAbstractAnimation::Direction direction; - bool deleteWhenStopped; void setState(QAbstractAnimation::State state); int totalCurrentTime; @@ -97,7 +98,10 @@ public: int loopCount; int currentLoop; + bool deleteWhenStopped; bool hasRegisteredTimer; + bool isPause; + bool isGroup; QAnimationGroup *group; @@ -115,14 +119,14 @@ public: //XXX this is needed by dui static Q_CORE_EXPORT QUnifiedTimer *instance(); - void registerAnimation(QAbstractAnimation *animation); + void registerAnimation(QAbstractAnimation *animation, bool isTopLevel); void unregisterAnimation(QAbstractAnimation *animation); //defines the timing interval. Default is DEFAULT_TIMER_INTERVAL void setTimingInterval(int interval) { timingInterval = interval; - if (animationTimer.isActive()) { + if (animationTimer.isActive() && !isPauseTimerActive) { //we changed the timing interval animationTimer.start(timingInterval, this); } @@ -134,21 +138,46 @@ public: */ void setConsistentTiming(bool consistent) { consistentTiming = consistent; } + /* + this is used for updating the currentTime of all animations in case the pause + timer is active or, otherwise, only of the animation passed as parameter. + */ + void ensureTimerUpdate(QAbstractAnimation *animation); + + /* + this will evaluate the need of restarting the pause timer in case there is still + some pause animations running. + */ + void restartAnimationTimer(); protected: void timerEvent(QTimerEvent *); private: - // timer used for all active animations + // timer used for all active (running) animations QBasicTimer animationTimer; - // timer used to delay the check if we should start/stop the global timer + // timer used to delay the check if we should start/stop the animation timer QBasicTimer startStopAnimationTimer; + QTime time; int lastTick; int timingInterval; int currentAnimationIdx; bool consistentTiming; + // bool to indicate that only pause animations are active + bool isPauseTimerActive; + QList<QAbstractAnimation*> animations, animationsToStart; + + // this is the count of running animations that are not a group neither a pause animation + int runningLeafAnimations; + QList<QAbstractAnimation*> runningPauseAnimations; + + void registerRunningAnimation(QAbstractAnimation *animation); + void unregisterRunningAnimation(QAbstractAnimation *animation); + + void updateAnimationsTime(); + int closestPauseAnimationTimeToFinish(); }; QT_END_NAMESPACE diff --git a/src/corelib/animation/qanimationgroup_p.h b/src/corelib/animation/qanimationgroup_p.h index 45eab58..bb1cfb3 100644 --- a/src/corelib/animation/qanimationgroup_p.h +++ b/src/corelib/animation/qanimationgroup_p.h @@ -68,7 +68,9 @@ class QAnimationGroupPrivate : public QAbstractAnimationPrivate Q_DECLARE_PUBLIC(QAnimationGroup) public: QAnimationGroupPrivate() - { } + { + isGroup = true; + } virtual void animationInsertedAt(int index) { Q_UNUSED(index) }; virtual void animationRemovedAt(int index); diff --git a/src/corelib/animation/qpauseanimation.cpp b/src/corelib/animation/qpauseanimation.cpp index 2fd12aa..d90f001 100644 --- a/src/corelib/animation/qpauseanimation.cpp +++ b/src/corelib/animation/qpauseanimation.cpp @@ -75,6 +75,7 @@ class QPauseAnimationPrivate : public QAbstractAnimationPrivate public: QPauseAnimationPrivate() : QAbstractAnimationPrivate(), duration(0) { + isPause = true; } int duration; |