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

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