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

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