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 QAnimationGroup |
42 | \inmodule QtCore |
43 | \brief The QAnimationGroup class is an abstract base class for groups of animations. |
44 | \since 4.6 |
45 | \ingroup animation |
46 | |
47 | An animation group is a container for animations (subclasses of |
48 | QAbstractAnimation). A group is usually responsible for managing |
49 | the \l{QAbstractAnimation::State}{state} of its animations, i.e., |
50 | it decides when to start, stop, resume, and pause them. Currently, |
51 | Qt provides two such groups: QParallelAnimationGroup and |
52 | QSequentialAnimationGroup. Look up their class descriptions for |
53 | details. |
54 | |
55 | Since QAnimationGroup inherits from QAbstractAnimation, you can |
56 | combine groups, and easily construct complex animation graphs. |
57 | You can query QAbstractAnimation for the group it belongs to |
58 | (using the \l{QAbstractAnimation::}{group()} function). |
59 | |
60 | To start a top-level animation group, you simply use the |
61 | \l{QAbstractAnimation::}{start()} function from |
62 | QAbstractAnimation. By a top-level animation group, we think of a |
63 | group that itself is not contained within another group. Starting |
64 | sub groups directly is not supported, and may lead to unexpected |
65 | behavior. |
66 | |
67 | \omit OK, we'll put in a snippet on this here \endomit |
68 | |
69 | QAnimationGroup provides methods for adding and retrieving |
70 | animations. Besides that, you can remove animations by calling |
71 | \l removeAnimation(), and clear the animation group by calling |
72 | clear(). You may keep track of changes in the group's |
73 | animations by listening to QEvent::ChildAdded and |
74 | QEvent::ChildRemoved events. |
75 | |
76 | \omit OK, let's find a snippet here as well. \endomit |
77 | |
78 | QAnimationGroup takes ownership of the animations it manages, and |
79 | ensures that they are deleted when the animation group is deleted. |
80 | |
81 | \sa QAbstractAnimation, QVariantAnimation, {The Animation Framework} |
82 | */ |
83 | |
84 | #include "qanimationgroup.h" |
85 | #include <QtCore/qdebug.h> |
86 | #include <QtCore/qcoreevent.h> |
87 | #include "qanimationgroup_p.h" |
88 | |
89 | #include <algorithm> |
90 | |
91 | QT_BEGIN_NAMESPACE |
92 | |
93 | |
94 | /*! |
95 | Constructs a QAnimationGroup. |
96 | \a parent is passed to QObject's constructor. |
97 | */ |
98 | QAnimationGroup::QAnimationGroup(QObject *parent) |
99 | : QAbstractAnimation(*new QAnimationGroupPrivate, parent) |
100 | { |
101 | } |
102 | |
103 | /*! |
104 | \internal |
105 | */ |
106 | QAnimationGroup::QAnimationGroup(QAnimationGroupPrivate &dd, QObject *parent) |
107 | : QAbstractAnimation(dd, parent) |
108 | { |
109 | } |
110 | |
111 | /*! |
112 | Destroys the animation group. It will also destroy all its animations. |
113 | */ |
114 | QAnimationGroup::~QAnimationGroup() |
115 | { |
116 | Q_D(QAnimationGroup); |
117 | // We need to clear the animations now while we are still a valid QAnimationGroup. |
118 | // If we wait until ~QObject() the QAbstractAnimation's pointer back to us would |
119 | // point to a QObject, not a valid QAnimationGroup. |
120 | d->clear(onDestruction: true); |
121 | } |
122 | |
123 | /*! |
124 | Returns a pointer to the animation at \a index in this group. This |
125 | function is useful when you need access to a particular animation. \a |
126 | index is between 0 and animationCount() - 1. |
127 | |
128 | \sa animationCount(), indexOfAnimation() |
129 | */ |
130 | QAbstractAnimation *QAnimationGroup::animationAt(int index) const |
131 | { |
132 | Q_D(const QAnimationGroup); |
133 | |
134 | if (index < 0 || index >= d->animations.size()) { |
135 | qWarning(msg: "QAnimationGroup::animationAt: index is out of bounds" ); |
136 | return nullptr; |
137 | } |
138 | |
139 | return d->animations.at(i: index); |
140 | } |
141 | |
142 | |
143 | /*! |
144 | Returns the number of animations managed by this group. |
145 | |
146 | \sa indexOfAnimation(), addAnimation(), animationAt() |
147 | */ |
148 | int QAnimationGroup::animationCount() const |
149 | { |
150 | Q_D(const QAnimationGroup); |
151 | return d->animations.size(); |
152 | } |
153 | |
154 | /*! |
155 | Returns the index of \a animation. The returned index can be passed |
156 | to the other functions that take an index as an argument. |
157 | |
158 | \sa insertAnimation(), animationAt(), takeAnimation() |
159 | */ |
160 | int QAnimationGroup::indexOfAnimation(QAbstractAnimation *animation) const |
161 | { |
162 | Q_D(const QAnimationGroup); |
163 | return d->animations.indexOf(t: animation); |
164 | } |
165 | |
166 | /*! |
167 | Adds \a animation to this group. This will call insertAnimation with |
168 | index equals to animationCount(). |
169 | |
170 | \note The group takes ownership of the animation. |
171 | |
172 | \sa removeAnimation() |
173 | */ |
174 | void QAnimationGroup::addAnimation(QAbstractAnimation *animation) |
175 | { |
176 | Q_D(QAnimationGroup); |
177 | insertAnimation(index: d->animations.count(), animation); |
178 | } |
179 | |
180 | /*! |
181 | Inserts \a animation into this animation group at \a index. |
182 | If \a index is 0 the animation is inserted at the beginning. |
183 | If \a index is animationCount(), the animation is inserted at the end. |
184 | |
185 | \note The group takes ownership of the animation. |
186 | |
187 | \sa takeAnimation(), addAnimation(), indexOfAnimation(), removeAnimation() |
188 | */ |
189 | void QAnimationGroup::insertAnimation(int index, QAbstractAnimation *animation) |
190 | { |
191 | Q_D(QAnimationGroup); |
192 | |
193 | if (index < 0 || index > d->animations.size()) { |
194 | qWarning(msg: "QAnimationGroup::insertAnimation: index is out of bounds" ); |
195 | return; |
196 | } |
197 | |
198 | if (QAnimationGroup *oldGroup = animation->group()) { |
199 | oldGroup->removeAnimation(animation); |
200 | // ensure we don't insert out of bounds if oldGroup == this |
201 | index = qMin(a: index, b: d->animations.size()); |
202 | } |
203 | |
204 | d->animations.insert(i: index, t: animation); |
205 | QAbstractAnimationPrivate::get(q: animation)->group = this; |
206 | // this will make sure that ChildAdded event is sent to 'this' |
207 | animation->setParent(this); |
208 | d->animationInsertedAt(index); |
209 | } |
210 | |
211 | /*! |
212 | Removes \a animation from this group. The ownership of \a animation is |
213 | transferred to the caller. |
214 | |
215 | \sa takeAnimation(), insertAnimation(), addAnimation() |
216 | */ |
217 | void QAnimationGroup::removeAnimation(QAbstractAnimation *animation) |
218 | { |
219 | Q_D(QAnimationGroup); |
220 | |
221 | if (!animation) { |
222 | qWarning(msg: "QAnimationGroup::remove: cannot remove null animation" ); |
223 | return; |
224 | } |
225 | int index = d->animations.indexOf(t: animation); |
226 | if (index == -1) { |
227 | qWarning(msg: "QAnimationGroup::remove: animation is not part of this group" ); |
228 | return; |
229 | } |
230 | |
231 | takeAnimation(index); |
232 | } |
233 | |
234 | /*! |
235 | Returns the animation at \a index and removes it from the animation group. |
236 | |
237 | \note The ownership of the animation is transferred to the caller. |
238 | |
239 | \sa removeAnimation(), addAnimation(), insertAnimation(), indexOfAnimation() |
240 | */ |
241 | QAbstractAnimation *QAnimationGroup::takeAnimation(int index) |
242 | { |
243 | Q_D(QAnimationGroup); |
244 | if (index < 0 || index >= d->animations.size()) { |
245 | qWarning(msg: "QAnimationGroup::takeAnimation: no animation at index %d" , index); |
246 | return nullptr; |
247 | } |
248 | QAbstractAnimation *animation = d->animations.at(i: index); |
249 | QAbstractAnimationPrivate::get(q: animation)->group = nullptr; |
250 | // ### removing from list before doing setParent to avoid inifinite recursion |
251 | // in ChildRemoved event |
252 | d->animations.removeAt(i: index); |
253 | animation->setParent(nullptr); |
254 | d->animationRemoved(index, animation); |
255 | return animation; |
256 | } |
257 | |
258 | /*! |
259 | Removes and deletes all animations in this animation group, and resets the current |
260 | time to 0. |
261 | |
262 | \sa addAnimation(), removeAnimation() |
263 | */ |
264 | void QAnimationGroup::clear() |
265 | { |
266 | Q_D(QAnimationGroup); |
267 | d->clear(onDestruction: false); |
268 | } |
269 | |
270 | /*! |
271 | \reimp |
272 | */ |
273 | bool QAnimationGroup::event(QEvent *event) |
274 | { |
275 | Q_D(QAnimationGroup); |
276 | if (event->type() == QEvent::ChildAdded) { |
277 | QChildEvent *childEvent = static_cast<QChildEvent *>(event); |
278 | if (QAbstractAnimation *a = qobject_cast<QAbstractAnimation *>(object: childEvent->child())) { |
279 | if (a->group() != this) |
280 | addAnimation(animation: a); |
281 | } |
282 | } else if (event->type() == QEvent::ChildRemoved) { |
283 | QChildEvent *childEvent = static_cast<QChildEvent *>(event); |
284 | // You can only rely on the child being a QObject because in the QEvent::ChildRemoved |
285 | // case it might be called from the destructor. Casting down to QAbstractAnimation then |
286 | // entails undefined behavior, so compare items as QObjects (which std::find does internally): |
287 | const QList<QAbstractAnimation *>::const_iterator it |
288 | = std::find(first: d->animations.cbegin(), last: d->animations.cend(), val: childEvent->child()); |
289 | if (it != d->animations.cend()) |
290 | takeAnimation(index: it - d->animations.cbegin()); |
291 | } |
292 | return QAbstractAnimation::event(event); |
293 | } |
294 | |
295 | void QAnimationGroupPrivate::clear(bool onDestruction) |
296 | { |
297 | const QList<QAbstractAnimation *> animationsCopy = animations; // taking a copy |
298 | animations.clear(); |
299 | // Clearing backwards so the indices doesn't change while we remove animations. |
300 | for (int i = animationsCopy.count() - 1; i >= 0; --i) { |
301 | QAbstractAnimation *animation = animationsCopy.at(i); |
302 | animation->setParent(nullptr); |
303 | QAbstractAnimationPrivate::get(q: animation)->group = nullptr; |
304 | // If we are in ~QAnimationGroup() it is not safe to called the virtual |
305 | // animationRemoved method, which can still be a method in a |
306 | // QAnimationGroupPrivate derived class that assumes q_ptr is still |
307 | // a valid derived class of QAnimationGroup. |
308 | if (!onDestruction) |
309 | animationRemoved(i, animation); |
310 | delete animation; |
311 | } |
312 | } |
313 | |
314 | void QAnimationGroupPrivate::animationRemoved(int index, QAbstractAnimation *) |
315 | { |
316 | Q_Q(QAnimationGroup); |
317 | Q_UNUSED(index); |
318 | if (animations.isEmpty()) { |
319 | currentTime = 0; |
320 | q->stop(); |
321 | } |
322 | } |
323 | |
324 | QT_END_NAMESPACE |
325 | |
326 | #include "moc_qanimationgroup.cpp" |
327 | |