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: &QAnimationDriver::started, context: this, slot: &QDefaultAnimationDriver::startTimer);
860 connect(sender: this, signal: &QAnimationDriver::stopped, context: this, slot: &QDefaultAnimationDriver::stopTimer);
861}
862
863QDefaultAnimationDriver::~QDefaultAnimationDriver()
864{
865 disconnect(sender: this, signal: &QAnimationDriver::started, receiver: this, slot: &QDefaultAnimationDriver::startTimer);
866 disconnect(sender: this, signal: &QAnimationDriver::stopped, receiver: this, slot: &QDefaultAnimationDriver::stopTimer);
867}
868
869void QDefaultAnimationDriver::timerEvent(QTimerEvent *e)
870{
871 Q_ASSERT(e->timerId() == m_timer.timerId());
872 Q_UNUSED(e); // if the assertions are disabled
873 advance();
874}
875
876void QDefaultAnimationDriver::startTimer()
877{
878 // always use a precise timer to drive animations
879 m_timer.start(msec: m_unified_timer->timingInterval, timerType: Qt::PreciseTimer, obj: this);
880}
881
882void QDefaultAnimationDriver::stopTimer()
883{
884 m_timer.stop();
885}
886
887QAnimationDriverPrivate::QAnimationDriverPrivate()
888 = default;
889
890QAnimationDriverPrivate::~QAnimationDriverPrivate()
891 = default;
892
893QAbstractAnimationTimer::QAbstractAnimationTimer()
894 = default;
895
896QAbstractAnimationTimer::~QAbstractAnimationTimer()
897 = default;
898
899QAbstractAnimationPrivate::QAbstractAnimationPrivate()
900 = default;
901
902QAbstractAnimationPrivate::~QAbstractAnimationPrivate() { }
903
904void QAbstractAnimationPrivate::setState(QAbstractAnimation::State newState)
905{
906 Q_Q(QAbstractAnimation);
907 const QAbstractAnimation::State oldState = state.valueBypassingBindings();
908 if (oldState == newState)
909 return;
910
911 if (loopCount == 0)
912 return;
913
914 int oldCurrentTime = currentTime;
915 int oldCurrentLoop = currentLoop;
916 QAbstractAnimation::Direction oldDirection = direction;
917
918 // check if we should Rewind
919 if ((newState == QAbstractAnimation::Paused || newState == QAbstractAnimation::Running)
920 && oldState == QAbstractAnimation::Stopped) {
921 const int oldTotalCurrentTime = totalCurrentTime;
922 //here we reset the time if needed
923 //we don't call setCurrentTime because this might change the way the animation
924 //behaves: changing the state or changing the current value
925 totalCurrentTime = currentTime = (direction == QAbstractAnimation::Forward) ?
926 0 : (loopCount == -1 ? q->duration() : q->totalDuration());
927 if (totalCurrentTime != oldTotalCurrentTime)
928 totalCurrentTime.notify();
929 }
930
931 state.setValueBypassingBindings(newState);
932 QPointer<QAbstractAnimation> guard(q);
933
934 //(un)registration of the animation must always happen before calls to
935 //virtual function (updateState) to ensure a correct state of the timer
936 bool isTopLevel = !group || group->state() == QAbstractAnimation::Stopped;
937 if (oldState == QAbstractAnimation::Running) {
938 if (newState == QAbstractAnimation::Paused && hasRegisteredTimer)
939 QAnimationTimer::ensureTimerUpdate();
940 //the animation, is not running any more
941 QAnimationTimer::unregisterAnimation(animation: q);
942 } else if (newState == QAbstractAnimation::Running) {
943 QAnimationTimer::registerAnimation(animation: q, isTopLevel);
944 }
945
946 q->updateState(newState, oldState);
947 //this is to be safe if updateState changes the state
948 if (!guard || newState != state.valueBypassingBindings())
949 return;
950
951 // Notify state change
952 state.notify();
953 emit q->stateChanged(newState, oldState);
954 //this is to be safe if updateState changes the state
955 if (!guard || newState != state.valueBypassingBindings())
956 return;
957
958 switch (state) {
959 case QAbstractAnimation::Paused:
960 break;
961 case QAbstractAnimation::Running:
962 {
963
964 // this ensures that the value is updated now that the animation is running
965 if (oldState == QAbstractAnimation::Stopped) {
966 if (isTopLevel) {
967 // currentTime needs to be updated if pauseTimer is active
968 QAnimationTimer::ensureTimerUpdate();
969 q->setCurrentTime(totalCurrentTime);
970 }
971 }
972 }
973 break;
974 case QAbstractAnimation::Stopped:
975 // Leave running state.
976 int dura = q->duration();
977
978 if (deleteWhenStopped)
979 q->deleteLater();
980
981 if (dura == -1 || loopCount < 0
982 || (oldDirection == QAbstractAnimation::Forward && (oldCurrentTime * (oldCurrentLoop + 1)) == (dura * loopCount))
983 || (oldDirection == QAbstractAnimation::Backward && oldCurrentTime == 0)) {
984 emit q->finished();
985 }
986 break;
987 }
988}
989
990/*!
991 Constructs the QAbstractAnimation base class, and passes \a parent to
992 QObject's constructor.
993
994 \sa QVariantAnimation, QAnimationGroup
995*/
996QAbstractAnimation::QAbstractAnimation(QObject *parent)
997 : QObject(*new QAbstractAnimationPrivate, nullptr)
998{
999 // Allow auto-add on reparent
1000 setParent(parent);
1001}
1002
1003/*!
1004 \internal
1005*/
1006QAbstractAnimation::QAbstractAnimation(QAbstractAnimationPrivate &dd, QObject *parent)
1007 : QObject(dd, nullptr)
1008{
1009 // Allow auto-add on reparent
1010 setParent(parent);
1011}
1012
1013/*!
1014 Stops the animation if it's running, then destroys the
1015 QAbstractAnimation. If the animation is part of a QAnimationGroup, it is
1016 automatically removed before it's destroyed.
1017*/
1018QAbstractAnimation::~QAbstractAnimation()
1019{
1020 Q_D(QAbstractAnimation);
1021 //we can't call stop here. Otherwise we get pure virtual calls
1022 if (d->state != Stopped) {
1023 QAbstractAnimation::State oldState = d->state;
1024 d->state = Stopped;
1025 d->state.notify();
1026 emit stateChanged(newState: d->state, oldState);
1027 if (oldState == QAbstractAnimation::Running)
1028 QAnimationTimer::unregisterAnimation(animation: this);
1029 }
1030 if (d->group)
1031 d->group->removeAnimation(animation: this);
1032}
1033
1034/*!
1035 \property QAbstractAnimation::state
1036 \brief state of the animation.
1037
1038 This property describes the current state of the animation. When the
1039 animation state changes, QAbstractAnimation emits the stateChanged()
1040 signal.
1041
1042 \note State updates might cause updates of the currentTime property,
1043 which, in turn, can cancel its bindings. So be careful when setting
1044 bindings to the currentTime property, when you expect the state of the
1045 animation to change.
1046*/
1047QAbstractAnimation::State QAbstractAnimation::state() const
1048{
1049 Q_D(const QAbstractAnimation);
1050 return d->state;
1051}
1052
1053QBindable<QAbstractAnimation::State> QAbstractAnimation::bindableState() const
1054{
1055 Q_D(const QAbstractAnimation);
1056 return &d->state;
1057}
1058
1059/*!
1060 If this animation is part of a QAnimationGroup, this function returns a
1061 pointer to the group; otherwise, it returns \nullptr.
1062
1063 \sa QAnimationGroup::addAnimation()
1064*/
1065QAnimationGroup *QAbstractAnimation::group() const
1066{
1067 Q_D(const QAbstractAnimation);
1068 return d->group;
1069}
1070
1071/*!
1072 \enum QAbstractAnimation::State
1073
1074 This enum describes the state of the animation.
1075
1076 \value Stopped The animation is not running. This is the initial state
1077 of QAbstractAnimation, and the state QAbstractAnimation reenters when finished. The current
1078 time remain unchanged until either setCurrentTime() is
1079 called, or the animation is started by calling start().
1080
1081 \value Paused The animation is paused (i.e., temporarily
1082 suspended). Calling resume() will resume animation activity.
1083
1084 \value Running The animation is running. While control is in the event
1085 loop, QAbstractAnimation will update its current time at regular intervals,
1086 calling updateCurrentTime() when appropriate.
1087
1088 \sa state(), stateChanged()
1089*/
1090
1091/*!
1092 \enum QAbstractAnimation::Direction
1093
1094 This enum describes the direction of the animation when in \l Running state.
1095
1096 \value Forward The current time of the animation increases with time (i.e.,
1097 moves from 0 and towards the end / duration).
1098
1099 \value Backward The current time of the animation decreases with time (i.e.,
1100 moves from the end / duration and towards 0).
1101
1102 \sa direction
1103*/
1104
1105/*!
1106 \property QAbstractAnimation::direction
1107 \brief the direction of the animation when it is in \l Running
1108 state.
1109
1110 This direction indicates whether the time moves from 0 towards the
1111 animation duration, or from the value of the duration and towards 0 after
1112 start() has been called.
1113
1114 By default, this property is set to \l Forward.
1115*/
1116QAbstractAnimation::Direction QAbstractAnimation::direction() const
1117{
1118 Q_D(const QAbstractAnimation);
1119 return d->direction;
1120}
1121void QAbstractAnimation::setDirection(Direction direction)
1122{
1123 Q_D(QAbstractAnimation);
1124 if (d->direction == direction) {
1125 d->direction.removeBindingUnlessInWrapper();
1126 return;
1127 }
1128
1129 const QScopedPropertyUpdateGroup guard;
1130 const int oldCurrentLoop = d->currentLoop;
1131 if (state() == Stopped) {
1132 if (direction == Backward) {
1133 d->currentTime = duration();
1134 d->currentLoop = d->loopCount - 1;
1135 } else {
1136 d->currentTime = 0;
1137 d->currentLoop = 0;
1138 }
1139 }
1140
1141 // the commands order below is important: first we need to setCurrentTime with the old direction,
1142 // then update the direction on this and all children and finally restart the pauseTimer if needed
1143 if (d->hasRegisteredTimer)
1144 QAnimationTimer::ensureTimerUpdate();
1145
1146 d->direction = direction;
1147 updateDirection(direction);
1148
1149 if (d->hasRegisteredTimer)
1150 // needed to update the timer interval in case of a pause animation
1151 QAnimationTimer::updateAnimationTimer();
1152
1153 if (d->currentLoop != oldCurrentLoop)
1154 d->currentLoop.notify();
1155 d->direction.notify();
1156}
1157
1158QBindable<QAbstractAnimation::Direction> QAbstractAnimation::bindableDirection()
1159{
1160 Q_D(QAbstractAnimation);
1161 return &d->direction;
1162}
1163
1164/*!
1165 \property QAbstractAnimation::duration
1166 \brief the duration of the animation.
1167
1168 If the duration is -1, it means that the duration is undefined.
1169 In this case, loopCount is ignored.
1170*/
1171
1172/*!
1173 \property QAbstractAnimation::loopCount
1174 \brief the loop count of the animation
1175
1176 This property describes the loop count of the animation as an integer.
1177 By default this value is 1, indicating that the animation
1178 should run once only, and then stop. By changing it you can let the
1179 animation loop several times. With a value of 0, the animation will not
1180 run at all, and with a value of -1, the animation will loop forever
1181 until stopped.
1182 It is not supported to have loop on an animation that has an undefined
1183 duration. It will only run once.
1184*/
1185int QAbstractAnimation::loopCount() const
1186{
1187 Q_D(const QAbstractAnimation);
1188 return d->loopCount;
1189}
1190void QAbstractAnimation::setLoopCount(int loopCount)
1191{
1192 Q_D(QAbstractAnimation);
1193 d->loopCount = loopCount;
1194}
1195
1196QBindable<int> QAbstractAnimation::bindableLoopCount()
1197{
1198 Q_D(QAbstractAnimation);
1199 return &d->loopCount;
1200}
1201
1202/*!
1203 \property QAbstractAnimation::currentLoop
1204 \brief the current loop of the animation
1205
1206 This property describes the current loop of the animation. By default,
1207 the animation's loop count is 1, and so the current loop will
1208 always be 0. If the loop count is 2 and the animation runs past its
1209 duration, it will automatically rewind and restart at current time 0, and
1210 current loop 1, and so on.
1211
1212 When the current loop changes, QAbstractAnimation emits the
1213 currentLoopChanged() signal.
1214*/
1215int QAbstractAnimation::currentLoop() const
1216{
1217 Q_D(const QAbstractAnimation);
1218 return d->currentLoop;
1219}
1220
1221QBindable<int> QAbstractAnimation::bindableCurrentLoop() const
1222{
1223 Q_D(const QAbstractAnimation);
1224 return &d->currentLoop;
1225}
1226
1227/*!
1228 \fn virtual int QAbstractAnimation::duration() const = 0
1229
1230 This pure virtual function returns the duration of the animation, and
1231 defines for how long QAbstractAnimation should update the current
1232 time. This duration is local, and does not include the loop count.
1233
1234 A return value of -1 indicates that the animation has no defined duration;
1235 the animation should run forever until stopped. This is useful for
1236 animations that are not time driven, or where you cannot easily predict
1237 its duration (e.g., event driven audio playback in a game).
1238
1239 If the animation is a parallel QAnimationGroup, the duration will be the longest
1240 duration of all its animations. If the animation is a sequential QAnimationGroup,
1241 the duration will be the sum of the duration of all its animations.
1242 \sa loopCount
1243*/
1244
1245/*!
1246 Returns the total and effective duration of the animation, including the
1247 loop count.
1248
1249 \sa duration(), currentTime
1250*/
1251int QAbstractAnimation::totalDuration() const
1252{
1253 int dura = duration();
1254 if (dura <= 0)
1255 return dura;
1256 int loopcount = loopCount();
1257 if (loopcount < 0)
1258 return -1;
1259 return dura * loopcount;
1260}
1261
1262/*!
1263 Returns the current time inside the current loop. It can go from 0 to duration().
1264
1265 \sa duration(), currentTime
1266*/
1267
1268int QAbstractAnimation::currentLoopTime() const
1269{
1270 Q_D(const QAbstractAnimation);
1271 return d->currentTime;
1272}
1273
1274/*!
1275 \property QAbstractAnimation::currentTime
1276 \brief the current time and progress of the animation
1277
1278 This property describes the animation's current time. You can change the
1279 current time by calling setCurrentTime, or you can call start() and let
1280 the animation run, setting the current time automatically as the animation
1281 progresses.
1282
1283 The animation's current time starts at 0, and ends at totalDuration().
1284
1285 \note You can bind other properties to currentTime, but it is not
1286 recommended setting bindings to it. As animation progresses, the currentTime
1287 is updated automatically, which cancels its bindings.
1288
1289 \sa loopCount, currentLoopTime()
1290 */
1291int QAbstractAnimation::currentTime() const
1292{
1293 Q_D(const QAbstractAnimation);
1294 return d->totalCurrentTime;
1295}
1296
1297QBindable<int> QAbstractAnimation::bindableCurrentTime()
1298{
1299 Q_D(QAbstractAnimation);
1300 return &d->totalCurrentTime;
1301}
1302
1303void QAbstractAnimation::setCurrentTime(int msecs)
1304{
1305 Q_D(QAbstractAnimation);
1306 msecs = qMax(a: msecs, b: 0);
1307
1308 // Calculate new time and loop.
1309 const int dura = duration();
1310 const int totalLoopCount = d->loopCount;
1311 const int totalDura = dura <= 0 ? dura : ((totalLoopCount < 0) ? -1 : dura * totalLoopCount);
1312 if (totalDura != -1)
1313 msecs = qMin(a: totalDura, b: msecs);
1314
1315 d->totalCurrentTime.removeBindingUnlessInWrapper();
1316
1317 const int oldCurrentTime = d->totalCurrentTime.valueBypassingBindings();
1318 d->totalCurrentTime.setValueBypassingBindings(msecs);
1319
1320 QAbstractAnimation::Direction currentDirection = d->direction;
1321
1322 // Update new values.
1323 const int oldLoop = d->currentLoop.valueBypassingBindings();
1324 int newCurrentLoop = (dura <= 0) ? 0 : (msecs / dura);
1325 if (newCurrentLoop == totalLoopCount) {
1326 //we're at the end
1327 d->currentTime = qMax(a: 0, b: dura);
1328 newCurrentLoop = qMax(a: 0, b: totalLoopCount - 1);
1329 } else {
1330 if (currentDirection == Forward) {
1331 d->currentTime = (dura <= 0) ? msecs : (msecs % dura);
1332 } else {
1333 d->currentTime = (dura <= 0) ? msecs : ((msecs - 1) % dura) + 1;
1334 if (d->currentTime == dura)
1335 newCurrentLoop = newCurrentLoop - 1;
1336 }
1337 }
1338 d->currentLoop.setValueBypassingBindings(newCurrentLoop);
1339
1340 // this is a virtual function, so it can update the properties as well
1341 updateCurrentTime(currentTime: d->currentTime);
1342
1343 // read the property values again
1344 newCurrentLoop = d->currentLoop.valueBypassingBindings();
1345 currentDirection = d->direction;
1346 const int newTotalCurrentTime = d->totalCurrentTime.valueBypassingBindings();
1347
1348 if (newCurrentLoop != oldLoop)
1349 d->currentLoop.notify();
1350
1351 /* Notify before calling stop: As seen in tst_QSequentialAnimationGroup::clear
1352 * we might delete the animation when stop is called. Thus after stop no member
1353 * of the object must be used anymore.
1354 */
1355 if (oldCurrentTime != newTotalCurrentTime)
1356 d->totalCurrentTime.notify();
1357 // All animations are responsible for stopping the animation when their
1358 // own end state is reached; in this case the animation is time driven,
1359 // and has reached the end.
1360 if ((currentDirection == Forward && newTotalCurrentTime == totalDura)
1361 || (currentDirection == Backward && newTotalCurrentTime == 0)) {
1362 stop();
1363 }
1364}
1365
1366/*!
1367 Starts the animation. The \a policy argument says whether or not the
1368 animation should be deleted when it's done. When the animation starts, the
1369 stateChanged() signal is emitted, and state() returns Running. When control
1370 reaches the event loop, the animation will run by itself, periodically
1371 calling updateCurrentTime() as the animation progresses.
1372
1373 If the animation is currently stopped or has already reached the end,
1374 calling start() will rewind the animation and start again from the beginning.
1375 When the animation reaches the end, the animation will either stop, or
1376 if the loop level is more than 1, it will rewind and continue from the beginning.
1377
1378 If the animation is already running, this function does nothing.
1379
1380 \sa stop(), state()
1381*/
1382void QAbstractAnimation::start(DeletionPolicy policy)
1383{
1384 Q_D(QAbstractAnimation);
1385 if (d->state.valueBypassingBindings() == Running)
1386 return;
1387 d->deleteWhenStopped = policy;
1388 d->setState(Running);
1389}
1390
1391/*!
1392 Stops the animation. When the animation is stopped, it emits the stateChanged()
1393 signal, and state() returns Stopped. The current time is not changed.
1394
1395 If the animation stops by itself after reaching the end (i.e.,
1396 currentLoopTime() == duration() and currentLoop() > loopCount() - 1), the
1397 finished() signal is emitted.
1398
1399 \sa start(), state()
1400 */
1401void QAbstractAnimation::stop()
1402{
1403 Q_D(QAbstractAnimation);
1404
1405 if (d->state.valueBypassingBindings() == Stopped)
1406 return;
1407
1408 d->setState(Stopped);
1409}
1410
1411/*!
1412 Pauses the animation. When the animation is paused, state() returns Paused.
1413 The value of currentTime will remain unchanged until resume() or start()
1414 is called. If you want to continue from the current time, call resume().
1415
1416 \sa start(), state(), resume()
1417 */
1418void QAbstractAnimation::pause()
1419{
1420 Q_D(QAbstractAnimation);
1421 if (d->state.valueBypassingBindings() == Stopped) {
1422 qWarning(msg: "QAbstractAnimation::pause: Cannot pause a stopped animation");
1423 return;
1424 }
1425
1426 d->setState(Paused);
1427}
1428
1429/*!
1430 Resumes the animation after it was paused. When the animation is resumed,
1431 it emits the resumed() and stateChanged() signals. The currenttime is not
1432 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

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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