1 | /****************************************************************************
|
---|
2 | **
|
---|
3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
---|
4 | ** All rights reserved.
|
---|
5 | ** Contact: Nokia Corporation ([email protected])
|
---|
6 | **
|
---|
7 | ** This file is part of the QtCore module of the Qt Toolkit.
|
---|
8 | **
|
---|
9 | ** $QT_BEGIN_LICENSE:LGPL$
|
---|
10 | ** Commercial Usage
|
---|
11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
---|
12 | ** accordance with the Qt Commercial License Agreement provided with the
|
---|
13 | ** Software or, alternatively, in accordance with the terms contained in
|
---|
14 | ** a written agreement between you and Nokia.
|
---|
15 | **
|
---|
16 | ** GNU Lesser General Public License Usage
|
---|
17 | ** Alternatively, this file may be used under the terms of the GNU Lesser
|
---|
18 | ** General Public License version 2.1 as published by the Free Software
|
---|
19 | ** Foundation and appearing in the file LICENSE.LGPL included in the
|
---|
20 | ** packaging of this file. Please review the following information to
|
---|
21 | ** ensure the GNU Lesser General Public License version 2.1 requirements
|
---|
22 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
---|
23 | **
|
---|
24 | ** In addition, as a special exception, Nokia gives you certain additional
|
---|
25 | ** rights. These rights are described in the Nokia Qt LGPL Exception
|
---|
26 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
---|
27 | **
|
---|
28 | ** GNU General Public License Usage
|
---|
29 | ** Alternatively, this file may be used under the terms of the GNU
|
---|
30 | ** General Public License version 3.0 as published by the Free Software
|
---|
31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
32 | ** packaging of this file. Please review the following information to
|
---|
33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
---|
34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
---|
35 | **
|
---|
36 | ** If you have questions regarding the use of this file, please contact
|
---|
37 | ** Nokia at [email protected].
|
---|
38 | ** $QT_END_LICENSE$
|
---|
39 | **
|
---|
40 | ****************************************************************************/
|
---|
41 |
|
---|
42 | /*!
|
---|
43 | \class QParallelAnimationGroup
|
---|
44 | \brief The QParallelAnimationGroup class provides a parallel group of animations.
|
---|
45 | \since 4.6
|
---|
46 | \ingroup animation
|
---|
47 |
|
---|
48 | QParallelAnimationGroup--a \l{QAnimationGroup}{container for
|
---|
49 | animations}--starts all its animations when it is
|
---|
50 | \l{QAbstractAnimation::start()}{started} itself, i.e., runs all
|
---|
51 | animations in parallel. The animation group finishes when the
|
---|
52 | longest lasting animation has finished.
|
---|
53 |
|
---|
54 | You can treat QParallelAnimation as any other QAbstractAnimation,
|
---|
55 | e.g., pause, resume, or add it to other animation groups.
|
---|
56 |
|
---|
57 | \code
|
---|
58 | QParallelAnimationGroup *group = new QParallelAnimationGroup;
|
---|
59 | group->addAnimation(anim1);
|
---|
60 | group->addAnimation(anim2);
|
---|
61 |
|
---|
62 | group->start();
|
---|
63 | \endcode
|
---|
64 |
|
---|
65 | In this example, \c anim1 and \c anim2 are two
|
---|
66 | \l{QPropertyAnimation}s that have already been set up.
|
---|
67 |
|
---|
68 | \sa QAnimationGroup, QPropertyAnimation, {The Animation Framework}
|
---|
69 | */
|
---|
70 |
|
---|
71 |
|
---|
72 | #include "qparallelanimationgroup.h"
|
---|
73 | #include "qparallelanimationgroup_p.h"
|
---|
74 | //#define QANIMATION_DEBUG
|
---|
75 |
|
---|
76 | #ifndef QT_NO_ANIMATION
|
---|
77 |
|
---|
78 | QT_BEGIN_NAMESPACE
|
---|
79 |
|
---|
80 | /*!
|
---|
81 | Constructs a QParallelAnimationGroup.
|
---|
82 | \a parent is passed to QObject's constructor.
|
---|
83 | */
|
---|
84 | QParallelAnimationGroup::QParallelAnimationGroup(QObject *parent)
|
---|
85 | : QAnimationGroup(*new QParallelAnimationGroupPrivate, parent)
|
---|
86 | {
|
---|
87 | }
|
---|
88 |
|
---|
89 | /*!
|
---|
90 | \internal
|
---|
91 | */
|
---|
92 | QParallelAnimationGroup::QParallelAnimationGroup(QParallelAnimationGroupPrivate &dd,
|
---|
93 | QObject *parent)
|
---|
94 | : QAnimationGroup(dd, parent)
|
---|
95 | {
|
---|
96 | }
|
---|
97 |
|
---|
98 | /*!
|
---|
99 | Destroys the animation group. It will also destroy all its animations.
|
---|
100 | */
|
---|
101 | QParallelAnimationGroup::~QParallelAnimationGroup()
|
---|
102 | {
|
---|
103 | }
|
---|
104 |
|
---|
105 | /*!
|
---|
106 | \reimp
|
---|
107 | */
|
---|
108 | int QParallelAnimationGroup::duration() const
|
---|
109 | {
|
---|
110 | Q_D(const QParallelAnimationGroup);
|
---|
111 | int ret = 0;
|
---|
112 |
|
---|
113 | for (int i = 0; i < d->animations.size(); ++i) {
|
---|
114 | QAbstractAnimation *animation = d->animations.at(i);
|
---|
115 | const int currentDuration = animation->totalDuration();
|
---|
116 | if (currentDuration == -1)
|
---|
117 | return -1; // Undetermined length
|
---|
118 |
|
---|
119 | ret = qMax(ret, currentDuration);
|
---|
120 | }
|
---|
121 |
|
---|
122 | return ret;
|
---|
123 | }
|
---|
124 |
|
---|
125 | /*!
|
---|
126 | \reimp
|
---|
127 | */
|
---|
128 | void QParallelAnimationGroup::updateCurrentTime(int currentTime)
|
---|
129 | {
|
---|
130 | Q_D(QParallelAnimationGroup);
|
---|
131 | if (d->animations.isEmpty())
|
---|
132 | return;
|
---|
133 |
|
---|
134 | if (d->currentLoop > d->lastLoop) {
|
---|
135 | // simulate completion of the loop
|
---|
136 | int dura = duration();
|
---|
137 | if (dura > 0) {
|
---|
138 | for (int i = 0; i < d->animations.size(); ++i) {
|
---|
139 | QAbstractAnimation *animation = d->animations.at(i);
|
---|
140 | if (animation->state() != QAbstractAnimation::Stopped)
|
---|
141 | d->animations.at(i)->setCurrentTime(dura); // will stop
|
---|
142 | }
|
---|
143 | }
|
---|
144 | } else if (d->currentLoop < d->lastLoop) {
|
---|
145 | // simulate completion of the loop seeking backwards
|
---|
146 | for (int i = 0; i < d->animations.size(); ++i) {
|
---|
147 | QAbstractAnimation *animation = d->animations.at(i);
|
---|
148 | //we need to make sure the animation is in the right state
|
---|
149 | //and then rewind it
|
---|
150 | d->applyGroupState(animation);
|
---|
151 | animation->setCurrentTime(0);
|
---|
152 | animation->stop();
|
---|
153 | }
|
---|
154 | }
|
---|
155 |
|
---|
156 | #ifdef QANIMATION_DEBUG
|
---|
157 | qDebug("QParallellAnimationGroup %5d: setCurrentTime(%d), loop:%d, last:%d, timeFwd:%d, lastcurrent:%d, %d",
|
---|
158 | __LINE__, d->currentTime, d->currentLoop, d->lastLoop, timeFwd, d->lastCurrentTime, state());
|
---|
159 | #endif
|
---|
160 | // finally move into the actual time of the current loop
|
---|
161 | for (int i = 0; i < d->animations.size(); ++i) {
|
---|
162 | QAbstractAnimation *animation = d->animations.at(i);
|
---|
163 | const int dura = animation->totalDuration();
|
---|
164 | //if the loopcount is bigger we should always start all animations
|
---|
165 | if (d->currentLoop > d->lastLoop
|
---|
166 | //if we're at the end of the animation, we need to start it if it wasn't already started in this loop
|
---|
167 | //this happens in Backward direction where not all animations are started at the same time
|
---|
168 | || d->shouldAnimationStart(animation, d->lastCurrentTime > dura /*startIfAtEnd*/)) {
|
---|
169 | d->applyGroupState(animation);
|
---|
170 | }
|
---|
171 |
|
---|
172 | if (animation->state() == state()) {
|
---|
173 | animation->setCurrentTime(currentTime);
|
---|
174 | if (dura > 0 && currentTime > dura)
|
---|
175 | animation->stop();
|
---|
176 | }
|
---|
177 | }
|
---|
178 | d->lastLoop = d->currentLoop;
|
---|
179 | d->lastCurrentTime = currentTime;
|
---|
180 | }
|
---|
181 |
|
---|
182 | /*!
|
---|
183 | \reimp
|
---|
184 | */
|
---|
185 | void QParallelAnimationGroup::updateState(QAbstractAnimation::State newState,
|
---|
186 | QAbstractAnimation::State oldState)
|
---|
187 | {
|
---|
188 | Q_D(QParallelAnimationGroup);
|
---|
189 | QAnimationGroup::updateState(newState, oldState);
|
---|
190 |
|
---|
191 | switch (newState) {
|
---|
192 | case Stopped:
|
---|
193 | for (int i = 0; i < d->animations.size(); ++i)
|
---|
194 | d->animations.at(i)->stop();
|
---|
195 | d->disconnectUncontrolledAnimations();
|
---|
196 | break;
|
---|
197 | case Paused:
|
---|
198 | for (int i = 0; i < d->animations.size(); ++i)
|
---|
199 | if (d->animations.at(i)->state() == Running)
|
---|
200 | d->animations.at(i)->pause();
|
---|
201 | break;
|
---|
202 | case Running:
|
---|
203 | d->connectUncontrolledAnimations();
|
---|
204 | for (int i = 0; i < d->animations.size(); ++i) {
|
---|
205 | QAbstractAnimation *animation = d->animations.at(i);
|
---|
206 | if (oldState == Stopped)
|
---|
207 | animation->stop();
|
---|
208 | animation->setDirection(d->direction);
|
---|
209 | if (d->shouldAnimationStart(animation, oldState == Stopped))
|
---|
210 | animation->start();
|
---|
211 | }
|
---|
212 | break;
|
---|
213 | }
|
---|
214 | }
|
---|
215 |
|
---|
216 | void QParallelAnimationGroupPrivate::_q_uncontrolledAnimationFinished()
|
---|
217 | {
|
---|
218 | Q_Q(QParallelAnimationGroup);
|
---|
219 |
|
---|
220 | QAbstractAnimation *animation = qobject_cast<QAbstractAnimation *>(q->sender());
|
---|
221 | Q_ASSERT(animation);
|
---|
222 |
|
---|
223 | int uncontrolledRunningCount = 0;
|
---|
224 | if (animation->duration() == -1 || animation->loopCount() < 0) {
|
---|
225 | QHash<QAbstractAnimation *, int>::iterator it = uncontrolledFinishTime.begin();
|
---|
226 | while (it != uncontrolledFinishTime.end()) {
|
---|
227 | if (it.key() == animation) {
|
---|
228 | *it = animation->currentTime();
|
---|
229 | }
|
---|
230 | if (it.value() == -1)
|
---|
231 | ++uncontrolledRunningCount;
|
---|
232 | ++it;
|
---|
233 | }
|
---|
234 | }
|
---|
235 |
|
---|
236 | if (uncontrolledRunningCount > 0)
|
---|
237 | return;
|
---|
238 |
|
---|
239 | int maxDuration = 0;
|
---|
240 | for (int i = 0; i < animations.size(); ++i)
|
---|
241 | maxDuration = qMax(maxDuration, animations.at(i)->totalDuration());
|
---|
242 |
|
---|
243 | if (currentTime >= maxDuration)
|
---|
244 | q->stop();
|
---|
245 | }
|
---|
246 |
|
---|
247 | void QParallelAnimationGroupPrivate::disconnectUncontrolledAnimations()
|
---|
248 | {
|
---|
249 | QHash<QAbstractAnimation *, int>::iterator it = uncontrolledFinishTime.begin();
|
---|
250 | while (it != uncontrolledFinishTime.end()) {
|
---|
251 | disconnectUncontrolledAnimation(it.key());
|
---|
252 | ++it;
|
---|
253 | }
|
---|
254 |
|
---|
255 | uncontrolledFinishTime.clear();
|
---|
256 | }
|
---|
257 |
|
---|
258 | void QParallelAnimationGroupPrivate::connectUncontrolledAnimations()
|
---|
259 | {
|
---|
260 | for (int i = 0; i < animations.size(); ++i) {
|
---|
261 | QAbstractAnimation *animation = animations.at(i);
|
---|
262 | if (animation->duration() == -1 || animation->loopCount() < 0) {
|
---|
263 | uncontrolledFinishTime[animation] = -1;
|
---|
264 | connectUncontrolledAnimation(animation);
|
---|
265 | }
|
---|
266 | }
|
---|
267 | }
|
---|
268 |
|
---|
269 | bool QParallelAnimationGroupPrivate::shouldAnimationStart(QAbstractAnimation *animation, bool startIfAtEnd) const
|
---|
270 | {
|
---|
271 | const int dura = animation->totalDuration();
|
---|
272 | if (dura == -1)
|
---|
273 | return !isUncontrolledAnimationFinished(animation);
|
---|
274 | if (startIfAtEnd)
|
---|
275 | return currentTime <= dura;
|
---|
276 | if (direction == QAbstractAnimation::Forward)
|
---|
277 | return currentTime < dura;
|
---|
278 | else //direction == QAbstractAnimation::Backward
|
---|
279 | return currentTime && currentTime <= dura;
|
---|
280 | }
|
---|
281 |
|
---|
282 | void QParallelAnimationGroupPrivate::applyGroupState(QAbstractAnimation *animation)
|
---|
283 | {
|
---|
284 | switch (state)
|
---|
285 | {
|
---|
286 | case QAbstractAnimation::Running:
|
---|
287 | animation->start();
|
---|
288 | break;
|
---|
289 | case QAbstractAnimation::Paused:
|
---|
290 | animation->pause();
|
---|
291 | break;
|
---|
292 | case QAbstractAnimation::Stopped:
|
---|
293 | default:
|
---|
294 | break;
|
---|
295 | }
|
---|
296 | }
|
---|
297 |
|
---|
298 |
|
---|
299 | bool QParallelAnimationGroupPrivate::isUncontrolledAnimationFinished(QAbstractAnimation *anim) const
|
---|
300 | {
|
---|
301 | return uncontrolledFinishTime.value(anim, -1) >= 0;
|
---|
302 | }
|
---|
303 |
|
---|
304 | void QParallelAnimationGroupPrivate::animationRemoved(int index, QAbstractAnimation *anim)
|
---|
305 | {
|
---|
306 | QAnimationGroupPrivate::animationRemoved(index, anim);
|
---|
307 | disconnectUncontrolledAnimation(anim);
|
---|
308 | uncontrolledFinishTime.remove(anim);
|
---|
309 | }
|
---|
310 |
|
---|
311 | /*!
|
---|
312 | \reimp
|
---|
313 | */
|
---|
314 | void QParallelAnimationGroup::updateDirection(QAbstractAnimation::Direction direction)
|
---|
315 | {
|
---|
316 | Q_D(QParallelAnimationGroup);
|
---|
317 | //we need to update the direction of the current animation
|
---|
318 | if (state() != Stopped) {
|
---|
319 | for (int i = 0; i < d->animations.size(); ++i) {
|
---|
320 | QAbstractAnimation *animation = d->animations.at(i);
|
---|
321 | animation->setDirection(direction);
|
---|
322 | }
|
---|
323 | } else {
|
---|
324 | if (direction == Forward) {
|
---|
325 | d->lastLoop = 0;
|
---|
326 | d->lastCurrentTime = 0;
|
---|
327 | } else {
|
---|
328 | // Looping backwards with loopCount == -1 does not really work well...
|
---|
329 | d->lastLoop = (d->loopCount == -1 ? 0 : d->loopCount - 1);
|
---|
330 | d->lastCurrentTime = duration();
|
---|
331 | }
|
---|
332 | }
|
---|
333 | }
|
---|
334 |
|
---|
335 | /*!
|
---|
336 | \reimp
|
---|
337 | */
|
---|
338 | bool QParallelAnimationGroup::event(QEvent *event)
|
---|
339 | {
|
---|
340 | return QAnimationGroup::event(event);
|
---|
341 | }
|
---|
342 |
|
---|
343 | QT_END_NAMESPACE
|
---|
344 |
|
---|
345 | #include "moc_qparallelanimationgroup.cpp"
|
---|
346 |
|
---|
347 | #endif //QT_NO_ANIMATION
|
---|