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

source code of qtbase/src/corelib/animation/qabstractanimation.cpp