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#include "private/qparallelanimationgroupjob_p.h"
5#include "private/qanimationjobutil_p.h"
6
7QT_BEGIN_NAMESPACE
8
9QParallelAnimationGroupJob::QParallelAnimationGroupJob()
10 : QAnimationGroupJob()
11 , m_previousLoop(0)
12 , m_previousCurrentTime(0)
13{
14}
15
16QParallelAnimationGroupJob::~QParallelAnimationGroupJob()
17{
18}
19
20int QParallelAnimationGroupJob::duration() const
21{
22 int ret = 0;
23
24 for (const QAbstractAnimationJob *animation : m_children) {
25 int currentDuration = animation->totalDuration();
26 if (currentDuration == -1)
27 return -1; // Undetermined length
28 ret = qMax(a: ret, b: currentDuration);
29 }
30
31 return ret;
32}
33
34void QParallelAnimationGroupJob::updateCurrentTime(int /*currentTime*/)
35{
36 if (m_children.isEmpty())
37 return;
38
39 if (m_currentLoop > m_previousLoop) {
40 // simulate completion of the loop
41 int dura = duration();
42 if (dura < 0) {
43 // For an uncontrolled parallel group, we need to simulate the end of running animations.
44 // As uncontrolled animation finish time is already reset for this next loop, we pick the
45 // longest of the known stop times.
46 for (QAbstractAnimationJob *animation : m_children) {
47 int currentDuration = animation->totalDuration();
48 if (currentDuration >= 0)
49 dura = qMax(a: dura, b: currentDuration);
50 }
51 }
52 if (dura > 0) {
53 for (QAbstractAnimationJob *animation : m_children) {
54 if (!animation->isStopped())
55 RETURN_IF_DELETED(animation->setCurrentTime(dura)); // will stop
56 }
57 }
58 } else if (m_currentLoop < m_previousLoop) {
59 // simulate completion of the loop seeking backwards
60 for (QAbstractAnimationJob *animation : m_children) {
61 //we need to make sure the animation is in the right state
62 //and then rewind it
63 applyGroupState(animation);
64 RETURN_IF_DELETED(animation->setCurrentTime(0));
65 animation->stop();
66 }
67 }
68
69 // finally move into the actual time of the current loop
70 for (QAbstractAnimationJob *animation : m_children) {
71 const int dura = animation->totalDuration();
72 //if the loopcount is bigger we should always start all animations
73 if (m_currentLoop > m_previousLoop
74 //if we're at the end of the animation, we need to start it if it wasn't already started in this loop
75 //this happens in Backward direction where not all animations are started at the same time
76 || shouldAnimationStart(animation, startIfAtEnd: m_previousCurrentTime > dura /*startIfAtEnd*/)) {
77 applyGroupState(animation);
78 }
79
80 if (animation->state() == state()) {
81 RETURN_IF_DELETED(animation->setCurrentTime(m_currentTime));
82 if (dura > 0 && m_currentTime > dura)
83 animation->stop();
84 }
85 }
86 m_previousLoop = m_currentLoop;
87 m_previousCurrentTime = m_currentTime;
88}
89
90void QParallelAnimationGroupJob::updateState(QAbstractAnimationJob::State newState,
91 QAbstractAnimationJob::State oldState)
92{
93 QAnimationGroupJob::updateState(newState, oldState);
94
95 switch (newState) {
96 case Stopped:
97 for (QAbstractAnimationJob *animation : m_children)
98 animation->stop();
99 break;
100 case Paused:
101 for (QAbstractAnimationJob *animation : m_children)
102 if (animation->isRunning())
103 animation->pause();
104 break;
105 case Running:
106 for (QAbstractAnimationJob *animation : m_children) {
107 if (oldState == Stopped) {
108 animation->stop();
109 m_previousLoop = m_direction == Forward ? 0 : m_loopCount - 1;
110 }
111 RETURN_IF_DELETED(resetUncontrolledAnimationFinishTime(animation));
112 animation->setDirection(m_direction);
113 if (shouldAnimationStart(animation, startIfAtEnd: oldState == Stopped))
114 RETURN_IF_DELETED(animation->start());
115 }
116 break;
117 }
118}
119
120bool QParallelAnimationGroupJob::shouldAnimationStart(QAbstractAnimationJob *animation, bool startIfAtEnd) const
121{
122 const int dura = animation->totalDuration();
123
124 if (dura == -1)
125 return uncontrolledAnimationFinishTime(anim: animation) == -1;
126
127 if (startIfAtEnd)
128 return m_currentTime <= dura;
129 if (m_direction == Forward)
130 return m_currentTime < dura;
131 else //direction == Backward
132 return m_currentTime && m_currentTime <= dura;
133}
134
135void QParallelAnimationGroupJob::applyGroupState(QAbstractAnimationJob *animation)
136{
137 switch (m_state)
138 {
139 case Running:
140 animation->start();
141 break;
142 case Paused:
143 animation->pause();
144 break;
145 case Stopped:
146 default:
147 break;
148 }
149}
150
151void QParallelAnimationGroupJob::updateDirection(QAbstractAnimationJob::Direction direction)
152{
153 //we need to update the direction of the current animation
154 if (!isStopped()) {
155 for (QAbstractAnimationJob *animation : m_children) {
156 animation->setDirection(direction);
157 }
158 } else {
159 if (direction == Forward) {
160 m_previousLoop = 0;
161 m_previousCurrentTime = 0;
162 } else {
163 // Looping backwards with loopCount == -1 does not really work well...
164 m_previousLoop = (m_loopCount == -1 ? 0 : m_loopCount - 1);
165 m_previousCurrentTime = duration();
166 }
167 }
168}
169
170void QParallelAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimationJob *animation)
171{
172 Q_ASSERT(animation && (animation->duration() == -1 || animation->loopCount() < 0));
173 int uncontrolledRunningCount = 0;
174
175 for (QAbstractAnimationJob *child : m_children) {
176 if (child == animation) {
177 setUncontrolledAnimationFinishTime(anim: animation, time: animation->currentTime());
178 } else if (child->duration() == -1 || child->loopCount() < 0) {
179 if (uncontrolledAnimationFinishTime(anim: child) == -1)
180 ++uncontrolledRunningCount;
181 }
182 }
183
184 if (uncontrolledRunningCount > 0)
185 return;
186
187 int maxDuration = 0;
188 bool running = false;
189 for (QAbstractAnimationJob *job : m_children) {
190 if (job->state() == Running)
191 running = true;
192 maxDuration = qMax(a: maxDuration, b: job->totalDuration());
193 }
194
195 setUncontrolledAnimationFinishTime(anim: this, time: qMax(a: maxDuration + m_currentLoopStartTime, b: currentTime()));
196
197 if (!running
198 && ((m_direction == Forward && m_currentLoop == m_loopCount -1)
199 || (m_direction == Backward && m_currentLoop == 0))) {
200 stop();
201 }
202}
203
204void QParallelAnimationGroupJob::debugAnimation(QDebug d) const
205{
206 d << "ParallelAnimationGroupJob(" << Qt::hex << (const void *) this << Qt::dec << ")";
207
208 debugChildren(d);
209}
210
211QT_END_NAMESPACE
212
213

source code of qtdeclarative/src/qml/animations/qparallelanimationgroupjob.cpp