5#include "private/qsequentialanimationgroupjob_p.h"
6#include "private/qpauseanimationjob_p.h"
7#include "private/qanimationjobutil_p.h"
9#include <QtCore/qnumeric.h>
18
19
20
21int addSaturating(
int a,
int b)
24 if (qAddOverflow(a, b, &r))
25 return std::numeric_limits<
int>::max();
31QSequentialAnimationGroupJob::QSequentialAnimationGroupJob()
32 : QAnimationGroupJob()
33 , m_currentAnimation(
nullptr)
38QSequentialAnimationGroupJob::~QSequentialAnimationGroupJob()
42bool QSequentialAnimationGroupJob::atEnd()
const
51 const int animTotalCurrentTime = m_currentAnimation->currentTime();
52 return (m_currentLoop == m_loopCount - 1
53 && m_direction == Forward
54 && !m_children.next(m_currentAnimation)
55 && animTotalCurrentTime == animationActualTotalDuration(m_currentAnimation));
58int QSequentialAnimationGroupJob::animationActualTotalDuration(
59 const QAbstractAnimationJob *anim)
const
61 int ret = anim->totalDuration();
63 int done = uncontrolledAnimationFinishTime(anim);
65 if (done >= 0 && (anim->loopCount() - 1 == anim->currentLoop() || anim->state() == Stopped))
71QSequentialAnimationGroupJob::AnimationIndex QSequentialAnimationGroupJob::indexForCurrentTime()
const
73 Q_ASSERT(!m_children.isEmpty());
78 for (
const QAbstractAnimationJob *anim : m_children) {
79 duration = animationActualTotalDuration(anim);
86 const int offsetPlusDuration = addSaturating(ret.timeOffset, duration);
87 if (duration == -1 || m_currentTime < offsetPlusDuration
88 || (m_currentTime == offsetPlusDuration && m_direction == QAbstractAnimationJob::Backward)) {
93 if (anim == m_currentAnimation) {
94 ret.afterCurrent =
true;
98 ret.timeOffset = offsetPlusDuration;
104 ret.timeOffset -= duration;
105 ret.animation = m_children.last();
109void QSequentialAnimationGroupJob::restart()
112 if (m_direction == Forward) {
114 if (m_currentAnimation == m_children.first())
115 activateCurrentAnimation();
117 setCurrentAnimation(m_children.first());
120 m_previousLoop = m_loopCount - 1;
121 if (m_currentAnimation == m_children.last())
122 activateCurrentAnimation();
124 setCurrentAnimation(m_children.last());
128void QSequentialAnimationGroupJob::advanceForwards(
const AnimationIndex &newAnimationIndex)
130 if (m_previousLoop < m_currentLoop) {
132 for (QAbstractAnimationJob *anim = m_currentAnimation; anim; anim = m_children.next(anim)) {
133 RETURN_IF_DELETED(setCurrentAnimation(anim,
true));
134 RETURN_IF_DELETED(anim->setCurrentTime(animationActualTotalDuration(anim)));
137 if (m_children.count() == 1) {
139 RETURN_IF_DELETED(activateCurrentAnimation());
141 RETURN_IF_DELETED(setCurrentAnimation(m_children.first(),
true));
146 for (QAbstractAnimationJob *anim = m_currentAnimation;
147 anim && anim != newAnimationIndex.animation; anim = m_children.next(anim)) {
148 RETURN_IF_DELETED(setCurrentAnimation(anim,
true));
149 RETURN_IF_DELETED(anim->setCurrentTime(animationActualTotalDuration(anim)));
154void QSequentialAnimationGroupJob::rewindForwards(
const AnimationIndex &newAnimationIndex)
156 if (m_previousLoop > m_currentLoop) {
158 for (QAbstractAnimationJob *anim = m_currentAnimation; anim;
159 anim = m_children.prev(anim)) {
160 RETURN_IF_DELETED(setCurrentAnimation(anim,
true));
161 RETURN_IF_DELETED(anim->setCurrentTime(0));
164 if (m_children.count() == 1) {
166 RETURN_IF_DELETED(activateCurrentAnimation());
168 RETURN_IF_DELETED(setCurrentAnimation(m_children.last(),
true));
173 for (QAbstractAnimationJob *anim = m_currentAnimation;
174 anim && anim != newAnimationIndex.animation; anim = m_children.prev(anim)) {
175 RETURN_IF_DELETED(setCurrentAnimation(anim,
true));
176 RETURN_IF_DELETED(anim->setCurrentTime(0));
181int QSequentialAnimationGroupJob::duration()
const
185 for (
const QAbstractAnimationJob *anim : m_children) {
186 const int currentDuration = anim->totalDuration();
187 if (currentDuration == -1)
190 if (qAddOverflow(ret, currentDuration, &ret))
191 return std::numeric_limits<
int>::max();
197void QSequentialAnimationGroupJob::clear()
200 QAnimationGroupJob::clear();
203 Q_ASSERT(m_currentAnimation ==
nullptr);
206void QSequentialAnimationGroupJob::updateCurrentTime(
int currentTime)
208 if (!m_currentAnimation)
211 const QSequentialAnimationGroupJob::AnimationIndex newAnimationIndex = indexForCurrentTime();
214 if (m_previousLoop < m_currentLoop
215 || (m_previousLoop == m_currentLoop && m_currentAnimation != newAnimationIndex.animation && newAnimationIndex.afterCurrent)) {
217 RETURN_IF_DELETED(advanceForwards(newAnimationIndex));
219 }
else if (m_previousLoop > m_currentLoop
220 || (m_previousLoop == m_currentLoop && m_currentAnimation != newAnimationIndex.animation && !newAnimationIndex.afterCurrent)) {
222 RETURN_IF_DELETED(rewindForwards(newAnimationIndex));
225 RETURN_IF_DELETED(setCurrentAnimation(newAnimationIndex.animation));
227 const int newCurrentTime = currentTime - newAnimationIndex.timeOffset;
229 if (m_currentAnimation) {
230 RETURN_IF_DELETED(m_currentAnimation->setCurrentTime(newCurrentTime));
233 const int diff = m_currentAnimation->currentTime() - newCurrentTime;
234 m_currentTime = addSaturating(m_currentTime, diff);
235 RETURN_IF_DELETED(stop());
240 Q_ASSERT(m_children.isEmpty());
242 RETURN_IF_DELETED(stop());
245 m_previousLoop = m_currentLoop;
248void QSequentialAnimationGroupJob::updateState(QAbstractAnimationJob::State newState,
249 QAbstractAnimationJob::State oldState)
251 QAnimationGroupJob::updateState(newState, oldState);
253 if (!m_currentAnimation)
258 m_currentAnimation->stop();
261 if (oldState == m_currentAnimation->state() && oldState == Running)
262 m_currentAnimation->pause();
267 if (oldState == m_currentAnimation->state() && oldState == Paused)
268 m_currentAnimation->start();
275void QSequentialAnimationGroupJob::updateDirection(QAbstractAnimationJob::Direction direction)
278 if (!isStopped() && m_currentAnimation)
279 m_currentAnimation->setDirection(direction);
282void QSequentialAnimationGroupJob::setCurrentAnimation(
283 const QAbstractAnimationJob *anim,
bool intermediate)
286 Q_ASSERT(m_children.isEmpty());
287 m_currentAnimation =
nullptr;
291 if (anim == m_currentAnimation)
295 if (m_currentAnimation)
296 m_currentAnimation->stop();
299 Q_ASSERT(m_children.contains(anim));
303 m_currentAnimation =
const_cast<QAbstractAnimationJob *>(anim);
305 activateCurrentAnimation(intermediate);
308void QSequentialAnimationGroupJob::activateCurrentAnimation(
bool intermediate)
310 if (!m_currentAnimation || isStopped())
313 m_currentAnimation->stop();
316 m_currentAnimation->setDirection(m_direction);
319 if (m_currentAnimation->totalDuration() == -1)
320 resetUncontrolledAnimationFinishTime(m_currentAnimation);
322 RETURN_IF_DELETED(m_currentAnimation->start());
323 if (!intermediate && isPaused())
324 m_currentAnimation->pause();
327void QSequentialAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimationJob *animation)
330 Q_ASSERT(animation == m_currentAnimation);
332 setUncontrolledAnimationFinishTime(m_currentAnimation, m_currentAnimation->currentTime());
334 int totalTime = currentTime();
335 if (m_direction == Forward) {
337 if (
auto *anim = m_children.next(m_currentAnimation))
338 RETURN_IF_DELETED(setCurrentAnimation(anim));
340 for (QAbstractAnimationJob *a = m_children.next(animation); a; a = m_children.next(a)) {
341 int dur = a->duration();
352 if (
auto *anim = m_children.prev(m_currentAnimation))
353 RETURN_IF_DELETED(setCurrentAnimation(anim));
355 for (QAbstractAnimationJob *a = m_children.prev(animation); a; a = m_children.prev(a)) {
356 int dur = a->duration();
366 setUncontrolledAnimationFinishTime(
this, totalTime);
371void QSequentialAnimationGroupJob::animationInserted(QAbstractAnimationJob *anim)
373 if (m_currentAnimation ==
nullptr)
374 RETURN_IF_DELETED(setCurrentAnimation(m_children.first()));
376 if (m_currentAnimation == m_children.next(anim)
377 && m_currentAnimation->currentTime() == 0 && m_currentAnimation->currentLoop() == 0) {
379 RETURN_IF_DELETED(setCurrentAnimation(anim));
389void QSequentialAnimationGroupJob::animationRemoved(QAbstractAnimationJob *anim, QAbstractAnimationJob *prev, QAbstractAnimationJob *next)
391 QAnimationGroupJob::animationRemoved(anim, prev, next);
393 Q_ASSERT(m_currentAnimation);
395 bool removingCurrent = anim == m_currentAnimation;
396 if (removingCurrent) {
398 RETURN_IF_DELETED(setCurrentAnimation(next));
400 RETURN_IF_DELETED(setCurrentAnimation(prev));
402 RETURN_IF_DELETED(setCurrentAnimation(
nullptr));
407 for (QAbstractAnimationJob *job : m_children) {
408 if (job == m_currentAnimation)
410 m_currentTime = addSaturating(m_currentTime, animationActualTotalDuration(job));
414 if (!removingCurrent) {
417 m_currentTime = addSaturating(m_currentTime, m_currentAnimation->currentTime());
421 m_totalCurrentTime = addSaturating(m_currentTime, totalDuration());
424void QSequentialAnimationGroupJob::debugAnimation(QDebug d)
const
426 d <<
"SequentialAnimationGroupJob(" << Qt::hex << (
const void *)
this << Qt::dec <<
")" <<
"currentAnimation:" << (
void *)m_currentAnimation;