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 | |
7 | QT_BEGIN_NAMESPACE |
8 | |
9 | QParallelAnimationGroupJob::QParallelAnimationGroupJob() |
10 | : QAnimationGroupJob() |
11 | , m_previousLoop(0) |
12 | , m_previousCurrentTime(0) |
13 | { |
14 | } |
15 | |
16 | QParallelAnimationGroupJob::~QParallelAnimationGroupJob() |
17 | { |
18 | } |
19 | |
20 | int 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 | |
34 | void 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 | |
90 | void 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 | |
120 | bool 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 | |
135 | void 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 | |
151 | void 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 | |
170 | void 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 | |
204 | void QParallelAnimationGroupJob::debugAnimation(QDebug d) const |
205 | { |
206 | d << "ParallelAnimationGroupJob(" << Qt::hex << (const void *) this << Qt::dec << ")" ; |
207 | |
208 | debugChildren(d); |
209 | } |
210 | |
211 | QT_END_NAMESPACE |
212 | |
213 | |