Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qabstractanimation.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5/*!
6 \class QAbstractAnimation
7 \inmodule QtCore
8 \ingroup animation
9 \brief The QAbstractAnimation class is the base of all animations.
10 \since 4.6
11
12 The class defines the functions for the functionality shared by
13 all animations. By inheriting this class, you can create custom
14 animations that plug into the rest of the animation framework.
15
16 The progress of an animation is given by its current time
17 (currentLoopTime()), which is measured in milliseconds from the start
18 of the animation (0) to its end (duration()). The value is updated
19 automatically while the animation is running. It can also be set
20 directly with setCurrentTime().
21
22 At any point an animation is in one of three states:
23 \l{QAbstractAnimation::}{Running},
24 \l{QAbstractAnimation::}{Stopped}, or
25 \l{QAbstractAnimation::}{Paused}--as defined by the
26 \l{QAbstractAnimation::}{State} enum. The current state can be
27 changed by calling start(), stop(), pause(), or resume(). An
28 animation will always reset its \l{currentTime()}{current time}
29 when it is started. If paused, it will continue with the same
30 current time when resumed. When an animation is stopped, it cannot
31 be resumed, but will keep its current time (until started again).
32 QAbstractAnimation will emit stateChanged() whenever its state
33 changes.
34
35 An animation can loop any number of times by setting the loopCount
36 property. When an animation's current time reaches its duration(),
37 it will reset the current time and keep running. A loop count of 1
38 (the default value) means that the animation will run one time.
39 Note that a duration of -1 means that the animation will run until
40 stopped; the current time will increase indefinitely. When the
41 current time equals duration() and the animation is in its
42 final loop, the \l{QAbstractAnimation::}{Stopped} state is
43 entered, and the finished() signal is emitted.
44
45 QAbstractAnimation provides pure virtual functions used by
46 subclasses to track the progress of the animation: duration() and
47 updateCurrentTime(). The duration() function lets you report a
48 duration for the animation (as discussed above). The animation
49 framework calls updateCurrentTime() when current time has changed.
50 By reimplementing this function, you can track the animation
51 progress. Note that neither the interval between calls nor the
52 number of calls to this function are defined; though, it will
53 normally be 60 updates per second.
54
55 By reimplementing updateState(), you can track the animation's
56 state changes, which is particularly useful for animations that
57 are not driven by time.
58
59 \sa QVariantAnimation, QPropertyAnimation, QAnimationGroup, {The Animation Framework}
60*/
61
62/*!
63 \enum QAbstractAnimation::DeletionPolicy
64
65 \value KeepWhenStopped The animation will not be deleted when stopped.
66 \value DeleteWhenStopped The animation will be automatically deleted when
67 stopped.
68*/
69
70/*!
71 \fn void QAbstractAnimation::finished()
72
73 QAbstractAnimation emits this signal after the animation has stopped and
74 has reached the end.
75
76 This signal is emitted after stateChanged().
77
78 \sa stateChanged()
79*/
80
81/*!
82 \fn void QAbstractAnimation::stateChanged(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
83
84 QAbstractAnimation emits this signal whenever the state of the animation has
85 changed from \a oldState to \a newState. This signal is emitted after the virtual
86 updateState() function is called.
87
88 \sa updateState()
89*/
90
91/*!
92 \fn void QAbstractAnimation::currentLoopChanged(int currentLoop)
93
94 QAbstractAnimation emits this signal whenever the current loop
95 changes. \a currentLoop is the current loop.
96
97 \sa currentLoop(), loopCount()
98*/
99
100/*!
101 \fn void QAbstractAnimation::directionChanged(QAbstractAnimation::Direction newDirection);
102
103 QAbstractAnimation emits this signal whenever the direction has been
104 changed. \a newDirection is the new direction.
105
106 \sa direction
107*/
108
110#include "qanimationgroup.h"
111
112#include <QtCore/qdebug.h>
113
115
116#include <QtCore/qmath.h>
117#include <QtCore/qcoreevent.h>
118#include <QtCore/qpointer.h>
119#include <QtCore/qscopedvaluerollback.h>
120
121#define DEFAULT_TIMER_INTERVAL 16
122#define PAUSE_TIMER_COARSE_THRESHOLD 2000
123
125
128
129/*!
130 \class QAbstractAnimationTimer
131 \inmodule QtCore
132 \brief QAbstractAnimationTimer is the base class for animation timers.
133 \internal
134
135 Subclass QAbstractAnimationTimer to provide an animation timer that is run by
136 QUnifiedTimer and can in turn be used to run any number of animations.
137
138 Currently two subclasses have been implemented: QAnimationTimer to drive the Qt C++
139 animation framework (QAbstractAnimation and subclasses) and QDeclarativeAnimationTimer
140 to drive the Qt QML animation framework.
141*/
142
143/*!
144 \fn virtual void QAbstractAnimationTimer::updateAnimationsTime(qint64 delta) = 0;
145 \internal
146
147 This pure virtual function is called when the animation timer needs to update
148 the current time for all animations it is running.
149*/
150
151/*!
152 \fn virtual void QAbstractAnimationTimer::restartAnimationTimer() = 0;
153 \internal
154
155 This pure virtual function restarts the animation timer as needed.
156
157 Classes implementing this function may choose to pause or resume the timer
158 as appropriate, or conditionally restart it.
159*/
160
161/*!
162 \fn virtual qsizetype QAbstractAnimationTimer::runningAnimationCount() = 0;
163 \internal
164
165 This pure virtual function returns the number of animations the timer is running.
166 This information is useful for profiling.
167*/
168
169/*!
170 \class QUnifiedTimer
171 \inmodule QtCore
172 \brief QUnifiedTimer provides a unified timing mechanism for animations in Qt C++ and QML.
173 \internal
174
175 QUnifiedTimer allows animations run by Qt to share a single timer. This keeps animations
176 visually in sync, as well as being more efficient than running numerous timers.
177
178 QUnifiedTimer drives animations indirectly, via QAbstractAnimationTimer.
179*/
180
181QUnifiedTimer::QUnifiedTimer() :
182 QObject(), defaultDriver(this), lastTick(0), timingInterval(DEFAULT_TIMER_INTERVAL),
183 currentAnimationIdx(0), insideTick(false), insideRestart(false), consistentTiming(false),
184 startTimersPending(false), stopTimerPending(false), allowNegativeDelta(false),
185 speedModifier(1), profilerCallback(nullptr),
186 driverStartTime(0), temporalDrift(0)
187{
188 time.invalidate();
189 driver = &defaultDriver;
190}
191
192QUnifiedTimer::~QUnifiedTimer()
193 = default;
194
195QUnifiedTimer *QUnifiedTimer::instance(bool create)
196{
197 QUnifiedTimer *inst;
198 static thread_local std::unique_ptr<QUnifiedTimer> unifiedTimer;
199 if (create && !unifiedTimer) {
200 inst = new QUnifiedTimer;
201 unifiedTimer.reset(inst);
202 } else {
203 inst = unifiedTimer.get();
204 }
205 return inst;
206}
207
208QUnifiedTimer *QUnifiedTimer::instance()
209{
210 return instance(true);
211}
212
213void QUnifiedTimer::maybeUpdateAnimationsToCurrentTime()
214{
215 if (elapsed() - lastTick > 50)
216 updateAnimationTimers();
217}
218
219qint64 QUnifiedTimer::elapsed() const
220{
221 if (driver->isRunning())
222 return driverStartTime + driver->elapsed();
223 else if (time.isValid())
224 return time.elapsed() + temporalDrift;
225
226 // Reaching here would normally indicate that the function is called
227 // under the wrong circumstances as neither pauses nor actual animations
228 // are running and there should be no need to query for elapsed().
229 return 0;
230}
231
232void QUnifiedTimer::startAnimationDriver()
233{
234 if (driver->isRunning()) {
235 qWarning("QUnifiedTimer::startAnimationDriver: driver is already running...");
236 return;
237 }
238 // Set the start time to the currently elapsed() value before starting.
239 // This means we get the animation system time including the temporal drift
240 // which is what we want.
241 driverStartTime = elapsed();
242 driver->start();
243}
244
245void QUnifiedTimer::stopAnimationDriver()
246{
247 if (!driver->isRunning()) {
248 qWarning("QUnifiedTimer::stopAnimationDriver: driver is not running");
249 return;
250 }
251 // Update temporal drift. Since the driver is running, elapsed() will
252 // return the total animation time in driver-time. Subtract the current
253 // wall time to get the delta.
254 temporalDrift = elapsed() - time.elapsed();
255 driver->stop();
256}
257
258void QUnifiedTimer::updateAnimationTimers()
259{
260 //setCurrentTime can get this called again while we're the for loop. At least with pauseAnimations
261 if (insideTick)
262 return;
263
264 const qint64 totalElapsed = elapsed();
265
266 // ignore consistentTiming in case the pause timer is active
267 qint64 delta = (consistentTiming && !pauseTimer.isActive()) ?
268 timingInterval : totalElapsed - lastTick;
269 // Don't use qFuzzyCompare because this won't be set frequently enough
270 // that floating point error can accumulate.
271 if (speedModifier != 1) {
272 if (speedModifier > 0)
273 delta = qRound(delta * speedModifier);
274 else
275 delta = 0;
276 }
277
278 lastTick = totalElapsed;
279
280 //we make sure we only call update time if the time has actually advanced
281 //* it might happen in some cases that the time doesn't change because events are delayed
282 // when the CPU load is high
283 //* it might happen in some cases that the delta is negative because the animation driver
284 // advances faster than time.elapsed()
285 if (delta != 0 && (allowNegativeDelta || delta > 0)) {
286 QScopedValueRollback<bool> guard(insideTick, true);
287 if (profilerCallback)
288 profilerCallback(delta);
289 for (currentAnimationIdx = 0; currentAnimationIdx < animationTimers.size(); ++currentAnimationIdx) {
290 QAbstractAnimationTimer *animation = animationTimers.at(currentAnimationIdx);
291 animation->updateAnimationsTime(delta);
292 }
293 currentAnimationIdx = 0;
294 }
295}
296
297qsizetype QUnifiedTimer::runningAnimationCount() const
298{
299 qsizetype count = 0;
300 for (const QAbstractAnimationTimer *timer : animationTimers)
301 count += timer->runningAnimationCount();
302 return count;
303}
304
305void QUnifiedTimer::registerProfilerCallback(void (*cb)(qint64))
306{
307 profilerCallback = cb;
308}
309
310void QUnifiedTimer::localRestart()
311{
312 if (insideRestart)
313 return;
314
315 if (!pausedAnimationTimers.isEmpty() && (animationTimers.size() + animationTimersToStart.size() == pausedAnimationTimers.size())) {
316 driver->stop();
317 int closestTimeToFinish = closestPausedAnimationTimerTimeToFinish();
318 // use a precise timer if the pause will be short
319 Qt::TimerType timerType = closestTimeToFinish < PAUSE_TIMER_COARSE_THRESHOLD ? Qt::PreciseTimer : Qt::CoarseTimer;
320 pauseTimer.start(closestTimeToFinish, timerType, this);
321 } else if (!driver->isRunning()) {
322 if (pauseTimer.isActive())
323 pauseTimer.stop();
324 startAnimationDriver();
325 }
326
327}
328
329void QUnifiedTimer::restart()
330{
331 {
332 QScopedValueRollback<bool> guard(insideRestart, true);
333 for (int i = 0; i < animationTimers.size(); ++i)
334 animationTimers.at(i)->restartAnimationTimer();
335 }
336
337 localRestart();
338}
339
340void QUnifiedTimer::setTimingInterval(int interval)
341{
342 timingInterval = interval;
343
344 if (driver->isRunning() && !pauseTimer.isActive()) {
345 //we changed the timing interval
346 stopAnimationDriver();
347 startAnimationDriver();
348 }
349}
350
351void QUnifiedTimer::startTimers()
352{
353 startTimersPending = false;
354
355 //we transfer the waiting animations into the "really running" state
356 animationTimers += animationTimersToStart;
357 animationTimersToStart.clear();
358 if (!animationTimers.isEmpty()) {
359 if (!time.isValid()) {
360 lastTick = 0;
361 time.start();
362 temporalDrift = 0;
363 driverStartTime = 0;
364 }
365 localRestart();
366 }
367}
368
369void QUnifiedTimer::stopTimer()
370{
371 stopTimerPending = false;
372 if (animationTimers.isEmpty()) {
373 stopAnimationDriver();
374 pauseTimer.stop();
375 // invalidate the start reference time
376 time.invalidate();
377 }
378}
379
380void QUnifiedTimer::timerEvent(QTimerEvent *event)
381{
382 //in the case of consistent timing we make sure the order in which events come is always the same
383 //for that purpose we do as if the startstoptimer would always fire before the animation timer
384 if (consistentTiming) {
385 if (stopTimerPending)
386 stopTimer();
387 if (startTimersPending)
388 startTimers();
389 }
390
391 if (event->id() == pauseTimer.id()) {
392 // update current time on all timers
393 updateAnimationTimers();
394 restart();
395 }
396}
397
398void QUnifiedTimer::startAnimationTimer(QAbstractAnimationTimer *timer)
399{
400 if (timer->isRegistered)
401 return;
402 timer->isRegistered = true;
403
404 QUnifiedTimer *inst = instance(true); //we create the instance if needed
405 inst->animationTimersToStart << timer;
406 if (!inst->startTimersPending) {
407 inst->startTimersPending = true;
408 QMetaObject::invokeMethod(inst, "startTimers", Qt::QueuedConnection);
409 }
410}
411
412void QUnifiedTimer::stopAnimationTimer(QAbstractAnimationTimer *timer)
413{
414 QUnifiedTimer *inst = QUnifiedTimer::instance(false);
415 if (inst) {
416 //at this point the unified timer should have been created
417 //but it might also have been already destroyed in case the application is shutting down
418
419 if (!timer->isRegistered)
420 return;
421 timer->isRegistered = false;
422
423 int idx = inst->animationTimers.indexOf(timer);
424 if (idx != -1) {
425 inst->animationTimers.removeAt(idx);
426 // this is needed if we unregister an animation while its running
427 if (idx <= inst->currentAnimationIdx)
428 --inst->currentAnimationIdx;
429
430 if (inst->animationTimers.isEmpty() && !inst->stopTimerPending) {
431 inst->stopTimerPending = true;
432 QMetaObject::invokeMethod(inst, "stopTimer", Qt::QueuedConnection);
433 }
434 } else {
435 inst->animationTimersToStart.removeOne(timer);
436 }
437 }
438}
439
440void QUnifiedTimer::pauseAnimationTimer(QAbstractAnimationTimer *timer, int duration)
441{
442 QUnifiedTimer *inst = QUnifiedTimer::instance();
443 if (!timer->isRegistered)
444 inst->startAnimationTimer(timer);
445
446 bool timerWasPaused = timer->isPaused;
447 timer->isPaused = true;
448 timer->pauseDuration = duration;
449 if (!timerWasPaused)
450 inst->pausedAnimationTimers << timer;
451 inst->localRestart();
452}
453
454void QUnifiedTimer::resumeAnimationTimer(QAbstractAnimationTimer *timer)
455{
456 if (!timer->isPaused)
457 return;
458
459 timer->isPaused = false;
460 QUnifiedTimer *inst = QUnifiedTimer::instance();
461 inst->pausedAnimationTimers.removeOne(timer);
462 inst->localRestart();
463}
464
465int QUnifiedTimer::closestPausedAnimationTimerTimeToFinish()
466{
467 int closestTimeToFinish = INT_MAX;
468 for (TimerListConstIt it = pausedAnimationTimers.constBegin(), cend = pausedAnimationTimers.constEnd(); it != cend; ++it) {
469 const int timeToFinish = (*it)->pauseDuration;
470 if (timeToFinish < closestTimeToFinish)
471 closestTimeToFinish = timeToFinish;
472 }
473 return closestTimeToFinish;
474}
475
476void QUnifiedTimer::installAnimationDriver(QAnimationDriver *d)
477{
478 if (driver != &defaultDriver) {
479 qWarning("QUnifiedTimer: animation driver already installed...");
480 return;
481 }
482
483 bool running = driver->isRunning();
484 if (running)
485 stopAnimationDriver();
486 driver = d;
487 if (driver)
488 allowNegativeDelta = driver->property("allowNegativeDelta").toBool();
489 if (running)
490 startAnimationDriver();
491}
492
493void QUnifiedTimer::uninstallAnimationDriver(QAnimationDriver *d)
494{
495 if (driver != d) {
496 qWarning("QUnifiedTimer: trying to uninstall a driver that is not installed...");
497 return;
498 }
499
500 bool running = driver->isRunning();
501 if (running)
502 stopAnimationDriver();
503 driver = &defaultDriver;
504 allowNegativeDelta = false;
505 if (running)
506 startAnimationDriver();
507}
508
509/*!
510 Returns \c true if \a d is the currently installed animation driver
511 and is not the default animation driver (which can never be uninstalled).
512*/
513bool QUnifiedTimer::canUninstallAnimationDriver(QAnimationDriver *d)
514{
515 return d == driver && driver != &defaultDriver;
516}
517
518QAnimationTimer::QAnimationTimer() :
519 QAbstractAnimationTimer(), lastTick(0),
520 currentAnimationIdx(0), insideTick(false),
521 startAnimationPending(false), stopTimerPending(false),
522 runningLeafAnimations(0)
523{
524}
525
527 = default;
528
530{
531 QAnimationTimer *inst;
532#if QT_CONFIG(thread)
533 static thread_local std::unique_ptr<QAnimationTimer> animationTimer;
534 if (create && !animationTimer) {
535 inst = new QAnimationTimer;
536 animationTimer.reset(inst);
537 } else {
538 inst = animationTimer.get();
539 }
540#else
541 Q_UNUSED(create);
542 static QAnimationTimer animationTimer;
543 inst = &animationTimer;
544#endif
545 return inst;
546}
547
552
554{
556 QUnifiedTimer *instU = QUnifiedTimer::instance(false);
557 if (instU && inst && inst->isPaused)
558 instU->updateAnimationTimers();
559}
560
562{
563 //setCurrentTime can get this called again while we're the for loop. At least with pauseAnimations
564 if (insideTick)
565 return;
566
567 lastTick += delta;
568
569 //we make sure we only call update time if the time has actually changed
570 //it might happen in some cases that the time doesn't change because events are delayed
571 //when the CPU load is high
572 if (delta) {
573 QScopedValueRollback<bool> guard(insideTick, true);
574 for (currentAnimationIdx = 0; currentAnimationIdx < animations.size(); ++currentAnimationIdx) {
575 QAbstractAnimation *animation = animations.at(currentAnimationIdx);
576 int elapsed = QAbstractAnimationPrivate::get(animation)->totalCurrentTime
577 + (animation->direction() == QAbstractAnimation::Forward ? delta : -delta);
578 animation->setCurrentTime(elapsed);
579 }
580 currentAnimationIdx = 0;
581 }
582}
583
590
592{
593 if (runningLeafAnimations == 0 && !runningPauseAnimations.isEmpty())
594 QUnifiedTimer::pauseAnimationTimer(this, closestPauseAnimationTimeToFinish());
595 else if (isPaused)
596 QUnifiedTimer::resumeAnimationTimer(this);
597 else if (!isRegistered)
598 QUnifiedTimer::startAnimationTimer(this);
599}
600
601void QAnimationTimer::startAnimations()
602{
603 if (!startAnimationPending)
604 return;
605 startAnimationPending = false;
606
607 //force timer to update, which prevents large deltas for our newly added animations
608 QUnifiedTimer::instance()->maybeUpdateAnimationsToCurrentTime();
609
610 //we transfer the waiting animations into the "really running" state
611 animations += animationsToStart;
612 animationsToStart.clear();
613 if (!animations.isEmpty())
615}
616
617void QAnimationTimer::stopTimer()
618{
619 stopTimerPending = false;
620 bool pendingStart = startAnimationPending && animationsToStart.size() > 0;
621 if (animations.isEmpty() && !pendingStart) {
622 QUnifiedTimer::resumeAnimationTimer(this);
623 QUnifiedTimer::stopAnimationTimer(this);
624 // invalidate the start reference time
625 lastTick = 0;
626 }
627}
628
629void QAnimationTimer::registerAnimation(QAbstractAnimation *animation, bool isTopLevel)
630{
631 QAnimationTimer *inst = instance(true); //we create the instance if needed
632 inst->registerRunningAnimation(animation);
633 if (isTopLevel) {
634 Q_ASSERT(!QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer);
635 QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer = true;
636 inst->animationsToStart << animation;
637 if (!inst->startAnimationPending) {
638 inst->startAnimationPending = true;
639 QMetaObject::invokeMethod(inst, "startAnimations", Qt::QueuedConnection);
640 }
641 }
642}
643
644void QAnimationTimer::unregisterAnimation(QAbstractAnimation *animation)
645{
647 if (inst) {
648 //at this point the unified timer should have been created
649 //but it might also have been already destroyed in case the application is shutting down
650
651 inst->unregisterRunningAnimation(animation);
652
653 if (!QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer)
654 return;
655
656 int idx = inst->animations.indexOf(animation);
657 if (idx != -1) {
658 inst->animations.removeAt(idx);
659 // this is needed if we unregister an animation while its running
660 if (idx <= inst->currentAnimationIdx)
661 --inst->currentAnimationIdx;
662
663 if (inst->animations.isEmpty() && !inst->stopTimerPending) {
664 inst->stopTimerPending = true;
665 QMetaObject::invokeMethod(inst, "stopTimer", Qt::QueuedConnection);
666 }
667 } else {
668 inst->animationsToStart.removeOne(animation);
669 }
670 }
671 QAbstractAnimationPrivate::get(animation)->hasRegisteredTimer = false;
672}
673
674void QAnimationTimer::registerRunningAnimation(QAbstractAnimation *animation)
675{
676 if (QAbstractAnimationPrivate::get(animation)->isGroup)
677 return;
678
679 if (QAbstractAnimationPrivate::get(animation)->isPause) {
680 runningPauseAnimations << animation;
681 } else
682 runningLeafAnimations++;
683}
684
685void QAnimationTimer::unregisterRunningAnimation(QAbstractAnimation *animation)
686{
687 if (QAbstractAnimationPrivate::get(animation)->isGroup)
688 return;
689
690 if (QAbstractAnimationPrivate::get(animation)->isPause)
691 runningPauseAnimations.removeOne(animation);
692 else
693 runningLeafAnimations--;
694 Q_ASSERT(runningLeafAnimations >= 0);
695}
696
697int QAnimationTimer::closestPauseAnimationTimeToFinish()
698{
699 int closestTimeToFinish = INT_MAX;
700 for (AnimationListConstIt it = runningPauseAnimations.constBegin(), cend = runningPauseAnimations.constEnd(); it != cend; ++it) {
701 const QAbstractAnimation *animation = *it;
702 int timeToFinish;
703
704 if (animation->direction() == QAbstractAnimation::Forward)
705 timeToFinish = animation->duration() - animation->currentLoopTime();
706 else
707 timeToFinish = animation->currentLoopTime();
708
709 if (timeToFinish < closestTimeToFinish)
710 closestTimeToFinish = timeToFinish;
711 }
712 return closestTimeToFinish;
713}
714
715/*!
716 \class QAnimationDriver
717 \inmodule QtCore
718
719 \brief The QAnimationDriver class is used to exchange the mechanism that drives animations.
720
721 The default animation system is driven by a timer that fires at regular intervals.
722 In some scenarios, it is better to drive the animation based on other synchronization
723 mechanisms, such as the vertical refresh rate of the screen.
724
725 \internal
726 */
727
728QAnimationDriver::QAnimationDriver(QObject *parent)
729 : QObject(*(new QAnimationDriverPrivate), parent)
730{
731}
732
733QAnimationDriver::QAnimationDriver(QAnimationDriverPrivate &dd, QObject *parent)
734 : QObject(dd, parent)
735{
736}
737
738QAnimationDriver::~QAnimationDriver()
739{
740 QUnifiedTimer *timer = QUnifiedTimer::instance(false);
741 if (timer && timer->canUninstallAnimationDriver(this))
742 uninstall();
743}
744
745/*!
746 Advances the animation. This function should be continuously called by
747 the driver subclasses while the animation is running.
748
749 The calculation of the new current time will use elapsed() in combination
750 with the internal time offsets of the animation system.
751 */
752
753void QAnimationDriver::advanceAnimation()
754{
755 QUnifiedTimer *instance = QUnifiedTimer::instance();
756
757 // update current time on all top level animations
758 instance->updateAnimationTimers();
759 instance->restart();
760}
761
762
763
764/*!
765 Advances the animation. This function should be continuously called
766 by the driver while the animation is running.
767 */
768
769void QAnimationDriver::advance()
770{
771 advanceAnimation();
772}
773
774
775
776/*!
777 Installs this animation driver. The animation driver is thread local and
778 will only apply for the thread its installed in.
779 */
780
781void QAnimationDriver::install()
782{
783 QUnifiedTimer *timer = QUnifiedTimer::instance(true);
784 timer->installAnimationDriver(this);
785}
786
787
788
789/*!
790 Uninstalls this animation driver.
791 */
792
793void QAnimationDriver::uninstall()
794{
795 QUnifiedTimer *timer = QUnifiedTimer::instance(true);
796 timer->uninstallAnimationDriver(this);
797}
798
799bool QAnimationDriver::isRunning() const
800{
801 return d_func()->running;
802}
803
804
805void QAnimationDriver::start()
806{
807 Q_D(QAnimationDriver);
808 if (!d->running) {
809 d->running = true;
810 d->timer.start();
811 emit started();
812 }
813}
814
815
816void QAnimationDriver::stop()
817{
818 Q_D(QAnimationDriver);
819 if (d->running) {
820 d->running = false;
821 emit stopped();
822 }
823}
824
825
826/*!
827 \fn qint64 QAnimationDriver::elapsed() const
828
829 Returns the number of milliseconds since the animations was started.
830 */
831
832qint64 QAnimationDriver::elapsed() const
833{
834 Q_D(const QAnimationDriver);
835 return d->running ? d->timer.elapsed() : 0;
836}
837
838/*!
839 \fn QAnimationDriver::started()
840
841 This signal is emitted by the animation framework to notify the driver
842 that continuous animation has started.
843
844 \internal
845 */
846
847/*!
848 \fn QAnimationDriver::stopped()
849
850 This signal is emitted by the animation framework to notify the driver
851 that continuous animation has stopped.
852
853 \internal
854 */
855
856/*!
857 The default animation driver just spins the timer...
858 */
859QDefaultAnimationDriver::QDefaultAnimationDriver(QUnifiedTimer *timer)
860 : QAnimationDriver(nullptr), m_unified_timer(timer)
861{
862 connect(this, &QAnimationDriver::started, this, &QDefaultAnimationDriver::startTimer);
863 connect(this, &QAnimationDriver::stopped, this, &QDefaultAnimationDriver::stopTimer);
864}
865
867{
868 disconnect(this, &QAnimationDriver::started, this, &QDefaultAnimationDriver::startTimer);
869 disconnect(this, &QAnimationDriver::stopped, this, &QDefaultAnimationDriver::stopTimer);
870}
871
873{
874 Q_ASSERT(e->id() == m_timer.id());
875 Q_UNUSED(e); // if the assertions are disabled
876 advance();
877}
878
879void QDefaultAnimationDriver::startTimer()
880{
881 // always use a precise timer to drive animations
882 m_timer.start(m_unified_timer->timingInterval, Qt::PreciseTimer, this);
883}
884
885void QDefaultAnimationDriver::stopTimer()
886{
887 m_timer.stop();
888}
889
890QAnimationDriverPrivate::QAnimationDriverPrivate()
891 = default;
892
893QAnimationDriverPrivate::~QAnimationDriverPrivate()
894 = default;
895
896QAbstractAnimationTimer::QAbstractAnimationTimer()
897 = default;
898
899QAbstractAnimationTimer::~QAbstractAnimationTimer()
900 = default;
901
902QAbstractAnimationPrivate::QAbstractAnimationPrivate()
903 = default;
904
905QAbstractAnimationPrivate::~QAbstractAnimationPrivate() { }
906
907void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState)
908{
909 Q_Q(QAbstractAnimation);
910 const QAbstractAnimation::State oldState = state.valueBypassingBindings();
911 if (oldState == newState)
912 return;
913
914 if (loopCount == 0)
915 return;
916
917 int oldCurrentTime = currentTime;
918 int oldCurrentLoop = currentLoop;
919 QAbstractAnimation::Direction oldDirection = direction;
920
921 // check if we should Rewind
922 if ((newState == QAbstractAnimation::Paused || newState == QAbstractAnimation::Running)
923 && oldState == QAbstractAnimation::Stopped) {
924 const int oldTotalCurrentTime = totalCurrentTime;
925 //here we reset the time if needed
926 //we don't call setCurrentTime because this might change the way the animation
927 //behaves: changing the state or changing the current value
928 totalCurrentTime = currentTime = (direction == QAbstractAnimation::Forward) ?
929 0 : (loopCount == -1 ? q->duration() : q->totalDuration());
930 if (totalCurrentTime != oldTotalCurrentTime)
931 totalCurrentTime.notify();
932 }
933
934 state.setValueBypassingBindings(newState);
935 QPointer<QAbstractAnimation> guard(q);
936
937 //(un)registration of the animation must always happen before calls to
938 //virtual function (updateState) to ensure a correct state of the timer
939 bool isTopLevel = !group || group->state() == QAbstractAnimation::Stopped;
940 if (oldState == QAbstractAnimation::Running) {
941 if (newState == QAbstractAnimation::Paused && hasRegisteredTimer)
942 QAnimationTimer::ensureTimerUpdate();
943 //the animation, is not running any more
944 QAnimationTimer::unregisterAnimation(q);
945 } else if (newState == QAbstractAnimation::Running) {
946 QAnimationTimer::registerAnimation(q, isTopLevel);
947 }
948
949 q->updateState(newState, oldState);
950 //this is to be safe if updateState changes the state
951 if (!guard || newState != state.valueBypassingBindings())
952 return;
953
954 // Notify state change
955 state.notify();
956 emit q->stateChanged(newState, oldState);
957 //this is to be safe if updateState changes the state
958 if (!guard || newState != state.valueBypassingBindings())
959 return;
960
961 switch (state) {
962 case QAbstractAnimation::Paused:
963 break;
964 case QAbstractAnimation::Running:
965 {
966
967 // this ensures that the value is updated now that the animation is running
968 if (oldState == QAbstractAnimation::Stopped) {
969 if (isTopLevel) {
970 // currentTime needs to be updated if pauseTimer is active
971 QAnimationTimer::ensureTimerUpdate();
972 q->setCurrentTime(totalCurrentTime);
973 }
974 }
975 }
976 break;
977 case QAbstractAnimation::Stopped:
978 // Leave running state.
979 int dura = q->duration();
980
981 if (deleteWhenStopped)
982 q->deleteLater();
983
984 if (dura == -1 || loopCount < 0
985 || (oldDirection == QAbstractAnimation::Forward && (oldCurrentTime * (oldCurrentLoop + 1)) == (dura * loopCount))
986 || (oldDirection == QAbstractAnimation::Backward && oldCurrentTime == 0)) {
987 emit q->finished();
988 }
989 break;
990 }
991}
992
993/*!
994 Constructs the QAbstractAnimation base class, and passes \a parent to
995 QObject's constructor.
996
997 \sa QVariantAnimation, QAnimationGroup
998*/
999QAbstractAnimation::QAbstractAnimation(QObject *parent)
1000 : QObject(*new QAbstractAnimationPrivate, nullptr)
1001{
1002 // Allow auto-add on reparent
1003 setParent(parent);
1004}
1005
1006/*!
1007 \internal
1008*/
1009QAbstractAnimation::QAbstractAnimation(QAbstractAnimationPrivate &dd, QObject *parent)
1010 : QObject(dd, nullptr)
1011{
1012 // Allow auto-add on reparent
1013 setParent(parent);
1014}
1015
1016/*!
1017 Stops the animation if it's running, then destroys the
1018 QAbstractAnimation. If the animation is part of a QAnimationGroup, it is
1019 automatically removed before it's destroyed.
1020*/
1021QAbstractAnimation::~QAbstractAnimation()
1022{
1023 Q_D(QAbstractAnimation);
1024 //we can't call stop here. Otherwise we get pure virtual calls
1025 if (d->state != Stopped) {
1026 QAbstractAnimation::State oldState = d->state;
1027 d->state = Stopped;
1028 d->state.notify();
1029 emit stateChanged(d->state, oldState);
1030 if (oldState == QAbstractAnimation::Running)
1031 QAnimationTimer::unregisterAnimation(this);
1032 }
1033 if (d->group)
1034 d->group->removeAnimation(this);
1035}
1036
1037/*!
1038 \property QAbstractAnimation::state
1039 \brief state of the animation.
1040
1041 This property describes the current state of the animation. When the
1042 animation state changes, QAbstractAnimation emits the stateChanged()
1043 signal.
1044
1045 \note State updates might cause updates of the currentTime property,
1046 which, in turn, can cancel its bindings. So be careful when setting
1047 bindings to the currentTime property, when you expect the state of the
1048 animation to change.
1049*/
1050QAbstractAnimation::State QAbstractAnimation::state() const
1051{
1052 Q_D(const QAbstractAnimation);
1053 return d->state;
1054}
1055
1056QBindable<QAbstractAnimation::State> QAbstractAnimation::bindableState() const
1057{
1058 Q_D(const QAbstractAnimation);
1059 return &d->state;
1060}
1061
1062/*!
1063 If this animation is part of a QAnimationGroup, this function returns a
1064 pointer to the group; otherwise, it returns \nullptr.
1065
1066 \sa QAnimationGroup::addAnimation()
1067*/
1068QAnimationGroup *QAbstractAnimation::group() const
1069{
1070 Q_D(const QAbstractAnimation);
1071 return d->group;
1072}
1073
1074/*!
1075 \enum QAbstractAnimation::State
1076
1077 This enum describes the state of the animation.
1078
1079 \value Stopped The animation is not running. This is the initial state
1080 of QAbstractAnimation, and the state QAbstractAnimation reenters when finished. The current
1081 time remain unchanged until either setCurrentTime() is
1082 called, or the animation is started by calling start().
1083
1084 \value Paused The animation is paused (i.e., temporarily
1085 suspended). Calling resume() will resume animation activity.
1086
1087 \value Running The animation is running. While control is in the event
1088 loop, QAbstractAnimation will update its current time at regular intervals,
1089 calling updateCurrentTime() when appropriate.
1090
1091 \sa state(), stateChanged()
1092*/
1093
1094/*!
1095 \enum QAbstractAnimation::Direction
1096
1097 This enum describes the direction of the animation when in \l Running state.
1098
1099 \value Forward The current time of the animation increases with time (i.e.,
1100 moves from 0 and towards the end / duration).
1101
1102 \value Backward The current time of the animation decreases with time (i.e.,
1103 moves from the end / duration and towards 0).
1104
1105 \sa direction
1106*/
1107
1108/*!
1109 \property QAbstractAnimation::direction
1110 \brief the direction of the animation when it is in \l Running
1111 state.
1112
1113 This direction indicates whether the time moves from 0 towards the
1114 animation duration, or from the value of the duration and towards 0 after
1115 start() has been called.
1116
1117 By default, this property is set to \l Forward.
1118*/
1119QAbstractAnimation::Direction QAbstractAnimation::direction() const
1120{
1121 Q_D(const QAbstractAnimation);
1122 return d->direction;
1123}
1124void QAbstractAnimation::setDirection(Direction direction)
1125{
1126 Q_D(QAbstractAnimation);
1127 if (d->direction == direction) {
1128 d->direction.removeBindingUnlessInWrapper();
1129 return;
1130 }
1131
1132 const QScopedPropertyUpdateGroup guard;
1133 const int oldCurrentLoop = d->currentLoop;
1134 if (state() == Stopped) {
1135 if (direction == Backward) {
1136 d->currentTime = duration();
1137 d->currentLoop = d->loopCount - 1;
1138 } else {
1139 d->currentTime = 0;
1140 d->currentLoop = 0;
1141 }
1142 }
1143
1144 // the commands order below is important: first we need to setCurrentTime with the old direction,
1145 // then update the direction on this and all children and finally restart the pauseTimer if needed
1146 if (d->hasRegisteredTimer)
1147 QAnimationTimer::ensureTimerUpdate();
1148
1149 d->direction = direction;
1150 updateDirection(direction);
1151
1152 if (d->hasRegisteredTimer)
1153 // needed to update the timer interval in case of a pause animation
1154 QAnimationTimer::updateAnimationTimer();
1155
1156 if (d->currentLoop != oldCurrentLoop)
1157 d->currentLoop.notify();
1158 d->direction.notify();
1159}
1160
1161QBindable<QAbstractAnimation::Direction> QAbstractAnimation::bindableDirection()
1162{
1163 Q_D(QAbstractAnimation);
1164 return &d->direction;
1165}
1166
1167/*!
1168 \property QAbstractAnimation::duration
1169 \brief the duration of the animation.
1170
1171 If the duration is -1, it means that the duration is undefined.
1172 In this case, loopCount is ignored.
1173*/
1174
1175/*!
1176 \property QAbstractAnimation::loopCount
1177 \brief the loop count of the animation
1178
1179 This property describes the loop count of the animation as an integer.
1180 By default this value is 1, indicating that the animation
1181 should run once only, and then stop. By changing it you can let the
1182 animation loop several times. With a value of 0, the animation will not
1183 run at all, and with a value of -1, the animation will loop forever
1184 until stopped.
1185 It is not supported to have loop on an animation that has an undefined
1186 duration. It will only run once.
1187*/
1188int QAbstractAnimation::loopCount() const
1189{
1190 Q_D(const QAbstractAnimation);
1191 return d->loopCount;
1192}
1193void QAbstractAnimation::setLoopCount(int loopCount)
1194{
1195 Q_D(QAbstractAnimation);
1196 d->loopCount = loopCount;
1197}
1198
1199QBindable<int> QAbstractAnimation::bindableLoopCount()
1200{
1201 Q_D(QAbstractAnimation);
1202 return &d->loopCount;
1203}
1204
1205/*!
1206 \property QAbstractAnimation::currentLoop
1207 \brief the current loop of the animation
1208
1209 This property describes the current loop of the animation. By default,
1210 the animation's loop count is 1, and so the current loop will
1211 always be 0. If the loop count is 2 and the animation runs past its
1212 duration, it will automatically rewind and restart at current time 0, and
1213 current loop 1, and so on.
1214
1215 When the current loop changes, QAbstractAnimation emits the
1216 currentLoopChanged() signal.
1217*/
1218int QAbstractAnimation::currentLoop() const
1219{
1220 Q_D(const QAbstractAnimation);
1221 return d->currentLoop;
1222}
1223
1224QBindable<int> QAbstractAnimation::bindableCurrentLoop() const
1225{
1226 Q_D(const QAbstractAnimation);
1227 return &d->currentLoop;
1228}
1229
1230/*!
1231 \fn virtual int QAbstractAnimation::duration() const = 0
1232
1233 This pure virtual function returns the duration of the animation, and
1234 defines for how long QAbstractAnimation should update the current
1235 time. This duration is local, and does not include the loop count.
1236
1237 A return value of -1 indicates that the animation has no defined duration;
1238 the animation should run forever until stopped. This is useful for
1239 animations that are not time driven, or where you cannot easily predict
1240 its duration (e.g., event driven audio playback in a game).
1241
1242 If the animation is a parallel QAnimationGroup, the duration will be the longest
1243 duration of all its animations. If the animation is a sequential QAnimationGroup,
1244 the duration will be the sum of the duration of all its animations.
1245 \sa loopCount
1246*/
1247
1248/*!
1249 Returns the total and effective duration of the animation, including the
1250 loop count.
1251
1252 \sa duration(), currentTime
1253*/
1254int QAbstractAnimation::totalDuration() const
1255{
1256 int dura = duration();
1257 if (dura <= 0)
1258 return dura;
1259 int loopcount = loopCount();
1260 if (loopcount < 0)
1261 return -1;
1262 return dura * loopcount;
1263}
1264
1265/*!
1266 Returns the current time inside the current loop. It can go from 0 to duration().
1267
1268 \sa duration(), currentTime
1269*/
1270
1271int QAbstractAnimation::currentLoopTime() const
1272{
1273 Q_D(const QAbstractAnimation);
1274 return d->currentTime;
1275}
1276
1277/*!
1278 \property QAbstractAnimation::currentTime
1279 \brief the current time and progress of the animation
1280
1281 This property describes the animation's current time. You can change the
1282 current time by calling setCurrentTime(), or you can call start() and let
1283 the animation run, setting the current time automatically as the animation
1284 progresses.
1285
1286 The animation's current time starts at 0, and ends at totalDuration().
1287
1288 \note You can bind other properties to currentTime, but it is not
1289 recommended setting bindings to it. As animation progresses, the currentTime
1290 is updated automatically, which cancels its bindings.
1291
1292 \sa loopCount, currentLoopTime()
1293 */
1294int QAbstractAnimation::currentTime() const
1295{
1296 Q_D(const QAbstractAnimation);
1297 return d->totalCurrentTime;
1298}
1299
1300QBindable<int> QAbstractAnimation::bindableCurrentTime()
1301{
1302 Q_D(QAbstractAnimation);
1303 return &d->totalCurrentTime;
1304}
1305
1306void QAbstractAnimation::setCurrentTime(int msecs)
1307{
1308 Q_D(QAbstractAnimation);
1309 msecs = qMax(msecs, 0);
1310
1311 // Calculate new time and loop.
1312 const int dura = duration();
1313 const int totalLoopCount = d->loopCount;
1314 const int totalDura = dura <= 0 ? dura : ((totalLoopCount < 0) ? -1 : dura * totalLoopCount);
1315 if (totalDura != -1)
1316 msecs = qMin(totalDura, msecs);
1317
1318 d->totalCurrentTime.removeBindingUnlessInWrapper();
1319
1320 const int oldCurrentTime = d->totalCurrentTime.valueBypassingBindings();
1321 d->totalCurrentTime.setValueBypassingBindings(msecs);
1322
1323 QAbstractAnimation::Direction currentDirection = d->direction;
1324
1325 // Update new values.
1326 const int oldLoop = d->currentLoop.valueBypassingBindings();
1327 int newCurrentLoop = (dura <= 0) ? 0 : (msecs / dura);
1328 if (newCurrentLoop == totalLoopCount) {
1329 //we're at the end
1330 d->currentTime = qMax(0, dura);
1331 newCurrentLoop = qMax(0, totalLoopCount - 1);
1332 } else {
1333 if (currentDirection == Forward) {
1334 d->currentTime = (dura <= 0) ? msecs : (msecs % dura);
1335 } else {
1336 d->currentTime = (dura <= 0) ? msecs : ((msecs - 1) % dura) + 1;
1337 if (d->currentTime == dura)
1338 newCurrentLoop = newCurrentLoop - 1;
1339 }
1340 }
1341 d->currentLoop.setValueBypassingBindings(newCurrentLoop);
1342
1343 // this is a virtual function, so it can update the properties as well
1344 updateCurrentTime(d->currentTime);
1345
1346 // read the property values again
1347 newCurrentLoop = d->currentLoop.valueBypassingBindings();
1348 currentDirection = d->direction;
1349 const int newTotalCurrentTime = d->totalCurrentTime.valueBypassingBindings();
1350
1351 if (newCurrentLoop != oldLoop)
1352 d->currentLoop.notify();
1353
1354 /* Notify before calling stop: As seen in tst_QSequentialAnimationGroup::clear
1355 * we might delete the animation when stop is called. Thus after stop no member
1356 * of the object must be used anymore.
1357 */
1358 if (oldCurrentTime != newTotalCurrentTime)
1359 d->totalCurrentTime.notify();
1360 // All animations are responsible for stopping the animation when their
1361 // own end state is reached; in this case the animation is time driven,
1362 // and has reached the end.
1363 if ((currentDirection == Forward && newTotalCurrentTime == totalDura)
1364 || (currentDirection == Backward && newTotalCurrentTime == 0)) {
1365 stop();
1366 }
1367}
1368
1369/*!
1370 Starts the animation. The \a policy argument says whether or not the
1371 animation should be deleted when it's done. When the animation starts, the
1372 stateChanged() signal is emitted, and state() returns Running. When control
1373 reaches the event loop, the animation will run by itself, periodically
1374 calling updateCurrentTime() as the animation progresses.
1375
1376 If the animation is currently stopped or has already reached the end,
1377 calling start() will rewind the animation and start again from the beginning.
1378 When the animation reaches the end, the animation will either stop, or
1379 if the loop level is more than 1, it will rewind and continue from the beginning.
1380
1381 If the animation is already running, this function does nothing.
1382
1383 \sa stop(), state()
1384*/
1385void QAbstractAnimation::start(DeletionPolicy policy)
1386{
1387 Q_D(QAbstractAnimation);
1388 if (d->state.valueBypassingBindings() == Running)
1389 return;
1390 d->deleteWhenStopped = policy;
1391 d->setState(Running);
1392}
1393
1394/*!
1395 Stops the animation. When the animation is stopped, it emits the stateChanged()
1396 signal, and state() returns Stopped. The current time is not changed.
1397
1398 If the animation stops by itself after reaching the end (i.e.,
1399 currentLoopTime() == duration() and currentLoop() > loopCount() - 1), the
1400 finished() signal is emitted.
1401
1402 \sa start(), state()
1403 */
1404void QAbstractAnimation::stop()
1405{
1406 Q_D(QAbstractAnimation);
1407
1408 if (d->state.valueBypassingBindings() == Stopped)
1409 return;
1410
1411 d->setState(Stopped);
1412}
1413
1414/*!
1415 Pauses the animation. When the animation is paused, state() returns Paused.
1416 The value of currentTime will remain unchanged until resume() or start()
1417 is called. If you want to continue from the current time, call resume().
1418
1419 \sa start(), state(), resume()
1420 */
1421void QAbstractAnimation::pause()
1422{
1423 Q_D(QAbstractAnimation);
1424 if (d->state.valueBypassingBindings() == Stopped) {
1425 qWarning("QAbstractAnimation::pause: Cannot pause a stopped animation");
1426 return;
1427 }
1428
1429 d->setState(Paused);
1430}
1431
1432/*!
1433 Resumes the animation after it was paused. When the animation is resumed,
1434 it emits the stateChanged() signal. The currentTime property is not changed.
1435
1436 \sa start(), pause(), state()
1437 */
1438void QAbstractAnimation::resume()
1439{
1440 Q_D(QAbstractAnimation);
1441 if (d->state.valueBypassingBindings() != Paused) {
1442 qWarning("QAbstractAnimation::resume: "
1443 "Cannot resume an animation that is not paused");
1444 return;
1445 }
1446
1447 d->setState(Running);
1448}
1449
1450/*!
1451 If \a paused is true, the animation is paused.
1452 If \a paused is false, the animation is resumed.
1453
1454 \sa state(), pause(), resume()
1455*/
1456void QAbstractAnimation::setPaused(bool paused)
1457{
1458 if (paused)
1459 pause();
1460 else
1461 resume();
1462}
1463
1464
1465/*!
1466 \reimp
1467*/
1468bool QAbstractAnimation::event(QEvent *event)
1469{
1470 return QObject::event(event);
1471}
1472
1473/*!
1474 \fn virtual void QAbstractAnimation::updateCurrentTime(int currentTime) = 0;
1475
1476 This pure virtual function is called every time the animation's
1477 \a currentTime changes.
1478
1479 \sa updateState()
1480*/
1481
1482/*!
1483 This virtual function is called by QAbstractAnimation when the state
1484 of the animation is changed from \a oldState to \a newState.
1485
1486 \sa start(), stop(), pause(), resume()
1487*/
1488void QAbstractAnimation::updateState(QAbstractAnimation::State newState,
1489 QAbstractAnimation::State oldState)
1490{
1491 Q_UNUSED(oldState);
1492 Q_UNUSED(newState);
1493}
1494
1495/*!
1496 This virtual function is called by QAbstractAnimation when the direction
1497 of the animation is changed. The \a direction argument is the new direction.
1498
1499 \sa setDirection(), direction()
1500*/
1501void QAbstractAnimation::updateDirection(QAbstractAnimation::Direction direction)
1502{
1503 Q_UNUSED(direction);
1504}
1505
1506
1507QT_END_NAMESPACE
1508
1509#include "moc_qabstractanimation.cpp"
1510#include "moc_qabstractanimation_p.cpp"
static void unregisterAnimation(QAbstractAnimation *animation)
static QAnimationTimer * instance(bool create)
static void updateAnimationTimer()
void updateAnimationsTime(qint64 delta) override
~QAnimationTimer() override
void restartAnimationTimer() override
static void ensureTimerUpdate()
static void registerAnimation(QAbstractAnimation *animation, bool isTopLevel)
static QAnimationTimer * instance()
void timerEvent(QTimerEvent *e) override
This event handler can be reimplemented in a subclass to receive timer events for the object.
QList< QAbstractAnimation * >::ConstIterator AnimationListConstIt
#define DEFAULT_TIMER_INTERVAL
QT_BEGIN_NAMESPACE typedef QList< QAbstractAnimationTimer * >::ConstIterator TimerListConstIt
#define PAUSE_TIMER_COARSE_THRESHOLD