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