diff options
Diffstat (limited to 'src/corelib/animation')
-rw-r--r-- | src/corelib/animation/qabstractanimation.cpp | 204 | ||||
-rw-r--r-- | src/corelib/animation/qabstractanimation_p.h | 46 | ||||
-rw-r--r-- | src/corelib/animation/qanimationgroup_p.h | 4 | ||||
-rw-r--r-- | src/corelib/animation/qparallelanimationgroup.cpp | 80 | ||||
-rw-r--r-- | src/corelib/animation/qparallelanimationgroup_p.h | 2 | ||||
-rw-r--r-- | src/corelib/animation/qpauseanimation.cpp | 1 | ||||
-rw-r--r-- | src/corelib/animation/qpropertyanimation.cpp | 4 |
7 files changed, 253 insertions, 88 deletions
diff --git a/src/corelib/animation/qabstractanimation.cpp b/src/corelib/animation/qabstractanimation.cpp index f92c22d..c775a00 100644 --- a/src/corelib/animation/qabstractanimation.cpp +++ b/src/corelib/animation/qabstractanimation.cpp @@ -83,7 +83,7 @@ updateCurrentTime(). The duration() function lets you report a duration for the animation (as discussed above). The animation framework calls updateCurrentTime() when current time has changed. - By reimplementing this function, you can track the animation + By reimplementing this function, you can track the animation progress. Note that neither the interval between calls nor the number of calls to this function are defined; though, it will normally be 60 updates per second. @@ -144,6 +144,7 @@ #include "qabstractanimation.h" #include "qanimationgroup.h" + #include <QtCore/qdebug.h> #include "qabstractanimation_p.h" @@ -162,9 +163,9 @@ //on windows if you're currently dragging a widget an inner eventloop was started by the system //to make sure that this timer is getting fired, we need to make sure to use the system timers //that will send a WM_TIMER event. We do that by settings the timer interval to 11 - //It is 11 because QEventDispatcherWin32Private::registerTimer specifically checks if the interval - //is greater than 10 to determine if it should use a system timer (or the multimedia timer). -#define STARTSTOP_TIMER_DELAY 11 + //It is 16 because QEventDispatcherWin32Private::registerTimer specifically checks if the interval + //is greater than 11 to determine if it should use a system timer (or the multimedia timer). +#define STARTSTOP_TIMER_DELAY 16 #else #define STARTSTOP_TIMER_DELAY 0 #endif @@ -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,51 +194,94 @@ 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() +{ + // ignore consistentTiming in case the pause timer is active + const int delta = (consistentTiming && !isPauseTimerActive) ? + timingInterval : time.elapsed() - lastTick; + lastTick = 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 (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) { - if (animations.contains(animation) || animationsToStart.contains(animation)) - return; - animationsToStart << animation; - startStopAnimationTimer.start(STARTSTOP_TIMER_DELAY, this); // we delay the check if we should start/stop the global timer + 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) { - Q_ASSERT(animations.count(animation) + animationsToStart.count(animation) <= 1); + unregisterRunningAnimation(animation); + + if (!QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer) + return; int idx = animations.indexOf(animation); if (idx != -1) { @@ -244,12 +289,54 @@ void QUnifiedTimer::unregisterAnimation(QAbstractAnimation *animation) // this is needed if we unregister an animation while its running if (idx <= currentAnimationIdx) --currentAnimationIdx; + + if (animations.isEmpty()) + startStopAnimationTimer.start(STARTSTOP_TIMER_DELAY, this); } else { animationsToStart.removeOne(animation); } - startStopAnimationTimer.start(STARTSTOP_TIMER_DELAY, this); // we delay the check if we should start/stop the global timer + 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) { @@ -268,7 +355,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()); } @@ -290,22 +377,31 @@ void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState) switch (state) { case QAbstractAnimation::Paused: + 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; + } - // Register timer if our parent is not running. - if (state == QAbstractAnimation::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); } - } else { - //new state is paused - QUnifiedTimer::instance()->unregisterAnimation(q); } break; case QAbstractAnimation::Stopped: @@ -326,7 +422,6 @@ void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState) } break; } - } /*! @@ -451,7 +546,6 @@ void QAbstractAnimation::setDirection(Direction direction) if (d->direction == direction) return; - d->direction = direction; if (state() == Stopped) { if (direction == Backward) { d->currentTime = duration(); @@ -461,7 +555,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); } @@ -658,7 +764,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 20868f9..bef0499 100644 --- a/src/corelib/animation/qabstractanimation_p.h +++ b/src/corelib/animation/qabstractanimation_p.h @@ -70,11 +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) { } @@ -88,7 +91,6 @@ public: QAbstractAnimation::State state; QAbstractAnimation::Direction direction; - bool deleteWhenStopped; void setState(QAbstractAnimation::State state); int totalCurrentTime; @@ -96,6 +98,11 @@ public: int loopCount; int currentLoop; + bool deleteWhenStopped; + bool hasRegisteredTimer; + bool isPause; + bool isGroup; + QAnimationGroup *group; private: @@ -112,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); } @@ -131,19 +138,46 @@ public: */ void setConsistentTiming(bool consistent) { consistentTiming = consistent; } - int elapsedTime() const { return lastTick; } + /* + 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: - QBasicTimer animationTimer, startStopAnimationTimer; + // timer used for all active (running) animations + QBasicTimer animationTimer; + // 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/qparallelanimationgroup.cpp b/src/corelib/animation/qparallelanimationgroup.cpp index 5b7fd22..0a04c14 100644 --- a/src/corelib/animation/qparallelanimationgroup.cpp +++ b/src/corelib/animation/qparallelanimationgroup.cpp @@ -136,20 +136,23 @@ void QParallelAnimationGroup::updateCurrentTime(int currentTime) int dura = duration(); if (dura > 0) { for (int i = 0; i < d->animations.size(); ++i) { - d->animations.at(i)->setCurrentTime(dura); // will stop + QAbstractAnimation *animation = d->animations.at(i); + if (animation->state() != QAbstractAnimation::Stopped) + d->animations.at(i)->setCurrentTime(dura); // will stop } } } else if (d->currentLoop < d->lastLoop) { // simulate completion of the loop seeking backwards for (int i = 0; i < d->animations.size(); ++i) { QAbstractAnimation *animation = d->animations.at(i); + //we need to make sure the animation is in the right state + //and then rewind it + d->applyGroupState(animation); animation->setCurrentTime(0); animation->stop(); } } - bool timeFwd = ((d->currentLoop == d->lastLoop && currentTime >= d->lastCurrentTime) - || d->currentLoop > d->lastLoop); #ifdef QANIMATION_DEBUG qDebug("QParallellAnimationGroup %5d: setCurrentTime(%d), loop:%d, last:%d, timeFwd:%d, lastcurrent:%d, %d", __LINE__, d->currentTime, d->currentLoop, d->lastLoop, timeFwd, d->lastCurrentTime, state()); @@ -158,34 +161,19 @@ void QParallelAnimationGroup::updateCurrentTime(int currentTime) for (int i = 0; i < d->animations.size(); ++i) { QAbstractAnimation *animation = d->animations.at(i); const int dura = animation->totalDuration(); - if (dura == -1 && d->isUncontrolledAnimationFinished(animation)) - continue; - if (dura == -1 || (currentTime <= dura && dura != 0) - || (dura == 0 && d->currentLoop != d->lastLoop)) { - switch (state()) { - case Running: - animation->start(); - break; - case Paused: - animation->pause(); - break; - case Stopped: - default: - break; - } + //if the loopcount is bigger we should always start all animations + if (d->currentLoop > d->lastLoop + //if we're at the end of the animation, we need to start it if it wasn't already started in this loop + //this happens in Backward direction where not all animations are started at the same time + || d->shouldAnimationStart(animation, d->lastCurrentTime > dura /*startIfAtEnd*/)) { + d->applyGroupState(animation); } - if (dura <= 0) { - if (dura == -1) - animation->setCurrentTime(currentTime); - continue; + if (animation->state() == state()) { + animation->setCurrentTime(currentTime); + if (dura > 0 && currentTime > dura) + animation->stop(); } - - if ((timeFwd && d->lastCurrentTime <= dura) - || (!timeFwd && d->currentTime <= dura)) - animation->setCurrentTime(currentTime); - if (currentTime > dura) - animation->stop(); } d->lastLoop = d->currentLoop; d->lastCurrentTime = currentTime; @@ -208,7 +196,8 @@ void QParallelAnimationGroup::updateState(QAbstractAnimation::State oldState, break; case Paused: for (int i = 0; i < d->animations.size(); ++i) - d->animations.at(i)->pause(); + if (d->animations.at(i)->state() == Running) + d->animations.at(i)->pause(); break; case Running: d->connectUncontrolledAnimations(); @@ -217,7 +206,8 @@ void QParallelAnimationGroup::updateState(QAbstractAnimation::State oldState, if (oldState == Stopped) animation->stop(); animation->setDirection(d->direction); - animation->start(); + if (d->shouldAnimationStart(animation, oldState == Stopped)) + animation->start(); } break; } @@ -280,6 +270,36 @@ void QParallelAnimationGroupPrivate::connectUncontrolledAnimations() } } +bool QParallelAnimationGroupPrivate::shouldAnimationStart(QAbstractAnimation *animation, bool startIfAtEnd) const +{ + const int dura = animation->totalDuration(); + if (dura == -1) + return !isUncontrolledAnimationFinished(animation); + if (startIfAtEnd) + return currentTime <= dura; + if (direction == QAbstractAnimation::Forward) + return currentTime < dura; + else //direction == QAbstractAnimation::Backward + return currentTime && currentTime <= dura; +} + +void QParallelAnimationGroupPrivate::applyGroupState(QAbstractAnimation *animation) +{ + switch (state) + { + case QAbstractAnimation::Running: + animation->start(); + break; + case QAbstractAnimation::Paused: + animation->pause(); + break; + case QAbstractAnimation::Stopped: + default: + break; + } +} + + bool QParallelAnimationGroupPrivate::isUncontrolledAnimationFinished(QAbstractAnimation *anim) const { return uncontrolledFinishTime.value(anim, -1) >= 0; diff --git a/src/corelib/animation/qparallelanimationgroup_p.h b/src/corelib/animation/qparallelanimationgroup_p.h index 8e1fb34..fa0ef95 100644 --- a/src/corelib/animation/qparallelanimationgroup_p.h +++ b/src/corelib/animation/qparallelanimationgroup_p.h @@ -74,6 +74,8 @@ public: int lastLoop; int lastCurrentTime; + bool shouldAnimationStart(QAbstractAnimation *animation, bool startIfAtEnd) const; + void applyGroupState(QAbstractAnimation *animation); bool isUncontrolledAnimationFinished(QAbstractAnimation *anim) const; void connectUncontrolledAnimations(); void disconnectUncontrolledAnimations(); 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; diff --git a/src/corelib/animation/qpropertyanimation.cpp b/src/corelib/animation/qpropertyanimation.cpp index b64d7df..4742e54 100644 --- a/src/corelib/animation/qpropertyanimation.cpp +++ b/src/corelib/animation/qpropertyanimation.cpp @@ -132,9 +132,9 @@ void QPropertyAnimationPrivate::updateProperty(const QVariant &newValue) } if (newValue.userType() == propertyType) { - //no conversion is needed, we directly call the QObject::qt_metacall + //no conversion is needed, we directly call the QMetaObject::metacall void *data = const_cast<void*>(newValue.constData()); - targetValue->qt_metacall(QMetaObject::WriteProperty, propertyIndex, &data); + QMetaObject::metacall(targetValue, QMetaObject::WriteProperty, propertyIndex, &data); } else { targetValue->setProperty(propertyName.constData(), newValue); } |