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