48 The class defines the functions for the functionality shared by |
48 The class defines the functions for the functionality shared by |
49 all animations. By inheriting this class, you can create custom |
49 all animations. By inheriting this class, you can create custom |
50 animations that plug into the rest of the animation framework. |
50 animations that plug into the rest of the animation framework. |
51 |
51 |
52 The progress of an animation is given by its current time |
52 The progress of an animation is given by its current time |
53 (currentTime()), which is measured in milliseconds from the start |
53 (currentLoopTime()), which is measured in milliseconds from the start |
54 of the animation (0) to its end (duration()). The value is updated |
54 of the animation (0) to its end (duration()). The value is updated |
55 automatically while the animation is running. It can also be set |
55 automatically while the animation is running. It can also be set |
56 directly with setCurrentTime(). |
56 directly with setCurrentTime(). |
57 |
57 |
58 At any point an animation is in one of three states: |
58 At any point an animation is in one of three states: |
155 #include <QtCore/qpointer.h> |
155 #include <QtCore/qpointer.h> |
156 |
156 |
157 #ifndef QT_NO_ANIMATION |
157 #ifndef QT_NO_ANIMATION |
158 |
158 |
159 #define DEFAULT_TIMER_INTERVAL 16 |
159 #define DEFAULT_TIMER_INTERVAL 16 |
160 |
|
161 #ifdef Q_WS_WIN |
|
162 /// Fix for Qt 4.7 |
|
163 //on windows if you're currently dragging a widget an inner eventloop was started by the system |
|
164 //to make sure that this timer is getting fired, we need to make sure to use the system timers |
|
165 //that will send a WM_TIMER event. We do that by settings the timer interval to 11 |
|
166 //It is 16 because QEventDispatcherWin32Private::registerTimer specifically checks if the interval |
|
167 //is greater than 11 to determine if it should use a system timer (or the multimedia timer). |
|
168 #define STARTSTOP_TIMER_DELAY 16 |
|
169 #else |
|
170 #define STARTSTOP_TIMER_DELAY 0 |
160 #define STARTSTOP_TIMER_DELAY 0 |
171 #endif |
|
172 |
|
173 |
161 |
174 QT_BEGIN_NAMESPACE |
162 QT_BEGIN_NAMESPACE |
175 |
163 |
176 Q_GLOBAL_STATIC(QThreadStorage<QUnifiedTimer *>, unifiedTimer) |
164 Q_GLOBAL_STATIC(QThreadStorage<QUnifiedTimer *>, unifiedTimer) |
177 |
165 |
178 QUnifiedTimer::QUnifiedTimer() : |
166 QUnifiedTimer::QUnifiedTimer() : |
179 QObject(), lastTick(0), timingInterval(DEFAULT_TIMER_INTERVAL), |
167 QObject(), lastTick(0), timingInterval(DEFAULT_TIMER_INTERVAL), |
180 currentAnimationIdx(0), consistentTiming(false), isPauseTimerActive(false), |
168 currentAnimationIdx(0), consistentTiming(false), slowMode(false), |
181 runningLeafAnimations(0) |
169 isPauseTimerActive(false), runningLeafAnimations(0) |
182 { |
170 { |
183 } |
171 } |
184 |
172 |
185 QUnifiedTimer *QUnifiedTimer::instance() |
173 QUnifiedTimer *QUnifiedTimer::instance() |
186 { |
174 { |
192 inst = unifiedTimer()->localData(); |
180 inst = unifiedTimer()->localData(); |
193 } |
181 } |
194 return inst; |
182 return inst; |
195 } |
183 } |
196 |
184 |
197 void QUnifiedTimer::ensureTimerUpdate(QAbstractAnimation *animation) |
185 void QUnifiedTimer::ensureTimerUpdate() |
198 { |
186 { |
199 if (isPauseTimerActive) { |
187 if (isPauseTimerActive) |
200 updateAnimationsTime(); |
188 updateAnimationsTime(); |
201 } else { |
|
202 // this code is needed when ensureTimerUpdate is called from setState because we update |
|
203 // the currentTime when an animation starts running (otherwise we could remove it) |
|
204 animation->setCurrentTime(animation->currentTime()); |
|
205 } |
|
206 } |
189 } |
207 |
190 |
208 void QUnifiedTimer::updateAnimationsTime() |
191 void QUnifiedTimer::updateAnimationsTime() |
209 { |
192 { |
210 // ignore consistentTiming in case the pause timer is active |
193 // ignore consistentTiming in case the pause timer is active |
211 const int delta = (consistentTiming && !isPauseTimerActive) ? |
194 int delta = (consistentTiming && !isPauseTimerActive) ? |
212 timingInterval : time.elapsed() - lastTick; |
195 timingInterval : time.elapsed() - lastTick; |
|
196 if (slowMode) |
|
197 delta /= 5; |
213 lastTick = time.elapsed(); |
198 lastTick = time.elapsed(); |
214 |
199 |
215 //we make sure we only call update time if the time has actually changed |
200 //we make sure we only call update time if the time has actually changed |
216 //it might happen in some cases that the time doesn't change because events are delayed |
201 //it might happen in some cases that the time doesn't change because events are delayed |
217 //when the CPU load is high |
202 //when the CPU load is high |
228 |
213 |
229 void QUnifiedTimer::restartAnimationTimer() |
214 void QUnifiedTimer::restartAnimationTimer() |
230 { |
215 { |
231 if (runningLeafAnimations == 0 && !runningPauseAnimations.isEmpty()) { |
216 if (runningLeafAnimations == 0 && !runningPauseAnimations.isEmpty()) { |
232 int closestTimeToFinish = closestPauseAnimationTimeToFinish(); |
217 int closestTimeToFinish = closestPauseAnimationTimeToFinish(); |
|
218 if (closestTimeToFinish < 0) { |
|
219 qDebug() << runningPauseAnimations; |
|
220 qDebug() << closestPauseAnimationTimeToFinish(); |
|
221 } |
233 animationTimer.start(closestTimeToFinish, this); |
222 animationTimer.start(closestTimeToFinish, this); |
234 isPauseTimerActive = true; |
223 isPauseTimerActive = true; |
235 } else if (!animationTimer.isActive() || isPauseTimerActive) { |
224 } else if (!animationTimer.isActive() || isPauseTimerActive) { |
236 animationTimer.start(timingInterval, this); |
225 animationTimer.start(timingInterval, this); |
237 isPauseTimerActive = false; |
226 isPauseTimerActive = false; |
238 } |
227 } |
239 } |
228 } |
240 |
229 |
241 void QUnifiedTimer::timerEvent(QTimerEvent *event) |
230 void QUnifiedTimer::timerEvent(QTimerEvent *event) |
242 { |
231 { |
243 if (event->timerId() == startStopAnimationTimer.timerId()) { |
232 //in the case of consistent timing we make sure the orders in which events come is always the same |
|
233 //for that purpose we do as if the startstoptimer would always fire before the animation timer |
|
234 if ((consistentTiming && startStopAnimationTimer.isActive()) || |
|
235 event->timerId() == startStopAnimationTimer.timerId()) { |
244 startStopAnimationTimer.stop(); |
236 startStopAnimationTimer.stop(); |
245 |
237 |
246 //we transfer the waiting animations into the "really running" state |
238 //we transfer the waiting animations into the "really running" state |
247 animations += animationsToStart; |
239 animations += animationsToStart; |
248 animationsToStart.clear(); |
240 animationsToStart.clear(); |
302 void QUnifiedTimer::registerRunningAnimation(QAbstractAnimation *animation) |
296 void QUnifiedTimer::registerRunningAnimation(QAbstractAnimation *animation) |
303 { |
297 { |
304 if (QAbstractAnimationPrivate::get(animation)->isGroup) |
298 if (QAbstractAnimationPrivate::get(animation)->isGroup) |
305 return; |
299 return; |
306 |
300 |
307 if (QAbstractAnimationPrivate::get(animation)->isPause) |
301 if (QAbstractAnimationPrivate::get(animation)->isPause) { |
|
302 if (animation->duration() == -1) |
|
303 qDebug() << "toto"; |
308 runningPauseAnimations << animation; |
304 runningPauseAnimations << animation; |
309 else |
305 } else |
310 runningLeafAnimations++; |
306 runningLeafAnimations++; |
311 } |
307 } |
312 |
308 |
313 void QUnifiedTimer::unregisterRunningAnimation(QAbstractAnimation *animation) |
309 void QUnifiedTimer::unregisterRunningAnimation(QAbstractAnimation *animation) |
314 { |
310 { |
328 for (int i = 0; i < runningPauseAnimations.size(); ++i) { |
324 for (int i = 0; i < runningPauseAnimations.size(); ++i) { |
329 QAbstractAnimation *animation = runningPauseAnimations.at(i); |
325 QAbstractAnimation *animation = runningPauseAnimations.at(i); |
330 int timeToFinish; |
326 int timeToFinish; |
331 |
327 |
332 if (animation->direction() == QAbstractAnimation::Forward) |
328 if (animation->direction() == QAbstractAnimation::Forward) |
333 timeToFinish = animation->duration() - animation->currentTime(); |
329 timeToFinish = animation->duration() - animation->currentLoopTime(); |
334 else |
330 else |
335 timeToFinish = animation->currentTime(); |
331 timeToFinish = animation->currentLoopTime(); |
336 |
332 |
337 if (timeToFinish < closestTimeToFinish) |
333 if (timeToFinish < closestTimeToFinish) |
338 closestTimeToFinish = timeToFinish; |
334 closestTimeToFinish = timeToFinish; |
339 } |
335 } |
340 return closestTimeToFinish; |
336 return closestTimeToFinish; |
362 } |
358 } |
363 |
359 |
364 state = newState; |
360 state = newState; |
365 QWeakPointer<QAbstractAnimation> guard(q); |
361 QWeakPointer<QAbstractAnimation> guard(q); |
366 |
362 |
367 q->updateState(oldState, newState); |
363 //(un)registration of the animation must always happen before calls to |
368 if (!guard) |
364 //virtual function (updateState) to ensure a correct state of the timer |
369 return; |
365 bool isTopLevel = !group || group->state() == QAbstractAnimation::Stopped; |
370 |
366 if (oldState == QAbstractAnimation::Running) { |
371 //this is to be safe if updateState changes the state |
367 if (newState == QAbstractAnimation::Paused && hasRegisteredTimer) |
372 if (state == oldState) |
368 QUnifiedTimer::instance()->ensureTimerUpdate(); |
|
369 //the animation, is not running any more |
|
370 QUnifiedTimer::instance()->unregisterAnimation(q); |
|
371 } else if (newState == QAbstractAnimation::Running) { |
|
372 QUnifiedTimer::instance()->registerAnimation(q, isTopLevel); |
|
373 } |
|
374 |
|
375 q->updateState(newState, oldState); |
|
376 if (!guard || newState != state) //this is to be safe if updateState changes the state |
373 return; |
377 return; |
374 |
378 |
375 // Notify state change |
379 // Notify state change |
376 emit q->stateChanged(oldState, newState); |
380 emit q->stateChanged(newState, oldState); |
377 if (!guard) |
381 if (!guard || newState != state) //this is to be safe if updateState changes the state |
378 return; |
382 return; |
379 |
383 |
380 switch (state) { |
384 switch (state) { |
381 case QAbstractAnimation::Paused: |
385 case QAbstractAnimation::Paused: |
382 if (hasRegisteredTimer) |
|
383 // currentTime needs to be updated if pauseTimer is active |
|
384 QUnifiedTimer::instance()->ensureTimerUpdate(q); |
|
385 if (!guard) |
|
386 return; |
|
387 //here we're sure that we were in running state before and that the |
|
388 //animation is currently registered |
|
389 QUnifiedTimer::instance()->unregisterAnimation(q); |
|
390 break; |
386 break; |
391 case QAbstractAnimation::Running: |
387 case QAbstractAnimation::Running: |
392 { |
388 { |
393 bool isTopLevel = !group || group->state() == QAbstractAnimation::Stopped; |
|
394 QUnifiedTimer::instance()->registerAnimation(q, isTopLevel); |
|
395 |
389 |
396 // this ensures that the value is updated now that the animation is running |
390 // this ensures that the value is updated now that the animation is running |
397 if (oldState == QAbstractAnimation::Stopped) { |
391 if (oldState == QAbstractAnimation::Stopped) { |
398 if (isTopLevel) |
392 if (isTopLevel) { |
399 // currentTime needs to be updated if pauseTimer is active |
393 // currentTime needs to be updated if pauseTimer is active |
400 QUnifiedTimer::instance()->ensureTimerUpdate(q); |
394 QUnifiedTimer::instance()->ensureTimerUpdate(); |
|
395 q->setCurrentTime(totalCurrentTime); |
|
396 } |
401 } |
397 } |
402 } |
398 } |
403 break; |
399 break; |
404 case QAbstractAnimation::Stopped: |
400 case QAbstractAnimation::Stopped: |
405 // Leave running state. |
401 // Leave running state. |
406 int dura = q->duration(); |
402 int dura = q->duration(); |
407 if (!guard) |
|
408 return; |
|
409 |
403 |
410 if (deleteWhenStopped) |
404 if (deleteWhenStopped) |
411 q->deleteLater(); |
405 q->deleteLater(); |
412 |
|
413 if (oldState == QAbstractAnimation::Running) |
|
414 QUnifiedTimer::instance()->unregisterAnimation(q); |
|
415 |
406 |
416 if (dura == -1 || loopCount < 0 |
407 if (dura == -1 || loopCount < 0 |
417 || (oldDirection == QAbstractAnimation::Forward && (oldCurrentTime * (oldCurrentLoop + 1)) == (dura * loopCount)) |
408 || (oldDirection == QAbstractAnimation::Forward && (oldCurrentTime * (oldCurrentLoop + 1)) == (dura * loopCount)) |
418 || (oldDirection == QAbstractAnimation::Backward && oldCurrentTime == 0)) { |
409 || (oldDirection == QAbstractAnimation::Backward && oldCurrentTime == 0)) { |
419 emit q->finished(); |
410 emit q->finished(); |
556 } |
547 } |
557 |
548 |
558 // the commands order below is important: first we need to setCurrentTime with the old direction, |
549 // the commands order below is important: first we need to setCurrentTime with the old direction, |
559 // then update the direction on this and all children and finally restart the pauseTimer if needed |
550 // then update the direction on this and all children and finally restart the pauseTimer if needed |
560 if (d->hasRegisteredTimer) |
551 if (d->hasRegisteredTimer) |
561 QUnifiedTimer::instance()->ensureTimerUpdate(this); |
552 QUnifiedTimer::instance()->ensureTimerUpdate(); |
562 |
553 |
563 d->direction = direction; |
554 d->direction = direction; |
564 updateDirection(direction); |
555 updateDirection(direction); |
565 |
556 |
566 if (d->hasRegisteredTimer) |
557 if (d->hasRegisteredTimer) |
655 return -1; |
646 return -1; |
656 return dura * loopcount; |
647 return dura * loopcount; |
657 } |
648 } |
658 |
649 |
659 /*! |
650 /*! |
|
651 Returns the current time inside the current loop. It can go from 0 to duration(). |
|
652 |
|
653 \sa duration(), currentTime |
|
654 */ |
|
655 |
|
656 int QAbstractAnimation::currentLoopTime() const |
|
657 { |
|
658 Q_D(const QAbstractAnimation); |
|
659 return d->currentTime; |
|
660 } |
|
661 |
|
662 /*! |
660 \property QAbstractAnimation::currentTime |
663 \property QAbstractAnimation::currentTime |
661 \brief the current time and progress of the animation |
664 \brief the current time and progress of the animation |
662 |
665 |
663 This property describes the animation's current time. You can change the |
666 This property describes the animation's current time. You can change the |
664 current time by calling setCurrentTime, or you can call start() and let |
667 current time by calling setCurrentTime, or you can call start() and let |
665 the animation run, setting the current time automatically as the animation |
668 the animation run, setting the current time automatically as the animation |
666 progresses. |
669 progresses. |
667 |
670 |
668 The animation's current time starts at 0, and ends at duration(). If the |
671 The animation's current time starts at 0, and ends at totalDuration(). |
669 animation's loopCount is larger than 1, the current time will rewind and |
672 |
670 start at 0 again for the consecutive loops. If the animation has a pause. |
673 \sa loopCount, currentLoopTime() |
671 currentTime will also include the duration of the pause. |
|
672 |
|
673 \sa loopCount |
|
674 */ |
674 */ |
675 int QAbstractAnimation::currentTime() const |
675 int QAbstractAnimation::currentTime() const |
676 { |
676 { |
677 Q_D(const QAbstractAnimation); |
677 Q_D(const QAbstractAnimation); |
678 return d->currentTime; |
678 return d->totalCurrentTime; |
679 } |
679 } |
680 void QAbstractAnimation::setCurrentTime(int msecs) |
680 void QAbstractAnimation::setCurrentTime(int msecs) |
681 { |
681 { |
682 Q_D(QAbstractAnimation); |
682 Q_D(QAbstractAnimation); |
683 msecs = qMax(msecs, 0); |
683 msecs = qMax(msecs, 0); |
747 /*! |
747 /*! |
748 Stops the animation. When the animation is stopped, it emits the stateChanged() |
748 Stops the animation. When the animation is stopped, it emits the stateChanged() |
749 signal, and state() returns Stopped. The current time is not changed. |
749 signal, and state() returns Stopped. The current time is not changed. |
750 |
750 |
751 If the animation stops by itself after reaching the end (i.e., |
751 If the animation stops by itself after reaching the end (i.e., |
752 currentTime() == duration() and currentLoop() > loopCount() - 1), the |
752 currentLoopTime() == duration() and currentLoop() > loopCount() - 1), the |
753 finished() signal is emitted. |
753 finished() signal is emitted. |
754 |
754 |
755 \sa start(), state() |
755 \sa start(), state() |
756 */ |
756 */ |
757 void QAbstractAnimation::stop() |
757 void QAbstractAnimation::stop() |
819 This virtual function is called by QAbstractAnimation when the state |
834 This virtual function is called by QAbstractAnimation when the state |
820 of the animation is changed from \a oldState to \a newState. |
835 of the animation is changed from \a oldState to \a newState. |
821 |
836 |
822 \sa start(), stop(), pause(), resume() |
837 \sa start(), stop(), pause(), resume() |
823 */ |
838 */ |
824 void QAbstractAnimation::updateState(QAbstractAnimation::State oldState, |
839 void QAbstractAnimation::updateState(QAbstractAnimation::State newState, |
825 QAbstractAnimation::State newState) |
840 QAbstractAnimation::State oldState) |
826 { |
841 { |
827 Q_UNUSED(oldState); |
842 Q_UNUSED(oldState); |
828 Q_UNUSED(newState); |
843 Q_UNUSED(newState); |
829 } |
844 } |
830 |
845 |