Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Toggle main menu visibility
Loading...
Searching...
No Matches
qsequentialanimationgroup.cpp
Go to the documentation of this file.
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
// Qt-Security score:significant reason:default
4
5
/*!
6
\class QSequentialAnimationGroup
7
\inmodule QtCore
8
\brief The QSequentialAnimationGroup class provides a sequential group of animations.
9
\since 4.6
10
\ingroup animation
11
12
QSequentialAnimationGroup is a QAnimationGroup that runs its
13
animations in sequence, i.e., it starts one animation after
14
another has finished playing. The animations are played in the
15
order they are added to the group (using
16
\l{QAnimationGroup::}{addAnimation()} or
17
\l{QAnimationGroup::}{insertAnimation()}). The animation group
18
finishes when its last animation has finished.
19
20
At each moment there is at most one animation that is active in
21
the group; it is returned by currentAnimation(). An empty group
22
has no current animation.
23
24
A sequential animation group can be treated as any other
25
animation, i.e., it can be started, stopped, and added to other
26
groups. You can also call addPause() or insertPause() to add a
27
pause to a sequential animation group.
28
29
\snippet code/src_corelib_animation_qsequentialanimationgroup.cpp 0
30
31
In this example, \c anim1 and \c anim2 are two already set up
32
\l{QPropertyAnimation}s.
33
34
\sa QAnimationGroup, QAbstractAnimation, {The Animation Framework}
35
*/
36
37
#
include
"qsequentialanimationgroup.h"
38
#
include
"qsequentialanimationgroup_p.h"
39
40
#
include
"qpauseanimation.h"
41
42
#
include
<
QtCore
/
qdebug
.
h
>
43
#
include
<
QtCore
/
qnumeric
.
h
>
44
45
QT_BEGIN_NAMESPACE
46
47
namespace
{
48
49
/*!
50
\internal
51
Returns INT_MAX in case of overflow
52
*/
53
int
addSaturating
(
int
a,
int
b)
54
{
55
int
r;
56
if
(qAddOverflow(a, b, &r))
57
return
std::numeric_limits<
int
>::max();
58
return
r;
59
}
60
61
}
// namespace
62
63
typedef
QList
<
QAbstractAnimation
*>::
ConstIterator
AnimationListConstIt
;
64
65
bool
QSequentialAnimationGroupPrivate::atEnd()
const
66
{
67
// we try to detect if we're at the end of the group
68
//this is true if the following conditions are true:
69
// 1. we're in the last loop
70
// 2. the direction is forward
71
// 3. the current animation is the last one
72
// 4. the current animation has reached its end
73
const
int
animTotalCurrentTime = QAbstractAnimationPrivate::get(currentAnimation)->totalCurrentTime;
74
return
(currentLoop == loopCount - 1
75
&& direction == QAbstractAnimation::Forward
76
&& currentAnimation == animations.last()
77
&& animTotalCurrentTime == animationActualTotalDuration(currentAnimationIndex));
78
}
79
80
int
QSequentialAnimationGroupPrivate::animationActualTotalDuration(
int
index)
const
81
{
82
QAbstractAnimation *anim = animations.at(index);
83
int
ret = anim->totalDuration();
84
if
(ret == -1 && actualDuration.size() > index)
85
ret = actualDuration.at(index);
//we can try the actual duration there
86
return
ret;
87
}
88
89
QSequentialAnimationGroupPrivate::AnimationIndex QSequentialAnimationGroupPrivate::indexForCurrentTime()
const
90
{
91
Q_ASSERT(!animations.isEmpty());
92
93
AnimationIndex ret;
94
int
duration = 0;
95
96
for
(
int
i = 0; i < animations.size(); ++i) {
97
duration = animationActualTotalDuration(i);
98
99
// 'animation' is the current animation if one of these reasons is true:
100
// 1. it's duration is undefined
101
// 2. it ends after msecs
102
// 3. it is the last animation (this can happen in case there is at least 1 uncontrolled animation)
103
// 4. it ends exactly in msecs and the direction is backwards
104
const
int
offsetPlusDuration = addSaturating(ret.timeOffset, duration);
105
if
(duration == -1 || currentTime < offsetPlusDuration
106
|| (currentTime == offsetPlusDuration && direction == QAbstractAnimation::Backward)) {
107
ret.index = i;
108
return
ret;
109
}
110
111
// 'animation' has a non-null defined duration and is not the one at time 'msecs'.
112
ret.timeOffset = offsetPlusDuration;
113
}
114
115
// this can only happen when one of those conditions is true:
116
// 1. the duration of the group is undefined and we passed its actual duration
117
// 2. there are only 0-duration animations in the group
118
ret.timeOffset -= duration;
119
ret.index = animations.size() - 1;
120
return
ret;
121
}
122
123
void
QSequentialAnimationGroupPrivate::restart()
124
{
125
// restarting the group by making the first/last animation the current one
126
if
(direction == QAbstractAnimation::Forward) {
127
lastLoop = 0;
128
if
(currentAnimationIndex == 0)
129
activateCurrentAnimation();
130
else
131
setCurrentAnimation(0);
132
}
else
{
// direction == QAbstractAnimation::Backward
133
lastLoop = loopCount - 1;
134
int
index = animations.size() - 1;
135
if
(currentAnimationIndex == index)
136
activateCurrentAnimation();
137
else
138
setCurrentAnimation(index);
139
}
140
}
141
142
/*!
143
\internal
144
This manages advancing the execution of a group running forwards (time has gone forward),
145
which is the same behaviour for rewinding the execution of a group running backwards
146
(time has gone backward).
147
*/
148
void
QSequentialAnimationGroupPrivate::advanceForwards(
const
AnimationIndex &newAnimationIndex)
149
{
150
if
(lastLoop < currentLoop) {
151
// we need to fast forward to the end
152
for
(
int
i = currentAnimationIndex; i < animations.size(); ++i) {
153
QAbstractAnimation *anim = animations.at(i);
154
setCurrentAnimation(i,
true
);
155
anim->setCurrentTime(animationActualTotalDuration(i));
156
}
157
// this will make sure the current animation is reset to the beginning
158
if
(animations.size() == 1)
159
// we need to force activation because setCurrentAnimation will have no effect
160
activateCurrentAnimation();
161
else
162
setCurrentAnimation(0,
true
);
163
}
164
165
// and now we need to fast forward from the current position to
166
for
(
int
i = currentAnimationIndex; i < newAnimationIndex.index; ++i) {
//### WRONG,
167
QAbstractAnimation *anim = animations.at(i);
168
setCurrentAnimation(i,
true
);
169
anim->setCurrentTime(animationActualTotalDuration(i));
170
}
171
// setting the new current animation will happen later
172
}
173
174
/*!
175
\internal
176
This manages rewinding the execution of a group running forwards (time has gone forward),
177
which is the same behaviour for advancing the execution of a group running backwards
178
(time has gone backward).
179
*/
180
void
QSequentialAnimationGroupPrivate::rewindForwards(
const
AnimationIndex &newAnimationIndex)
181
{
182
if
(lastLoop > currentLoop) {
183
// we need to fast rewind to the beginning
184
for
(
int
i = currentAnimationIndex; i >= 0 ; --i) {
185
QAbstractAnimation *anim = animations.at(i);
186
setCurrentAnimation(i,
true
);
187
anim->setCurrentTime(0);
188
}
189
// this will make sure the current animation is reset to the end
190
if
(animations.size() == 1)
191
// we need to force activation because setCurrentAnimation will have no effect
192
activateCurrentAnimation();
193
else
194
setCurrentAnimation(animations.size() - 1,
true
);
195
}
196
197
// and now we need to fast rewind from the current position to
198
for
(
int
i = currentAnimationIndex; i > newAnimationIndex.index; --i) {
199
QAbstractAnimation *anim = animations.at(i);
200
setCurrentAnimation(i,
true
);
201
anim->setCurrentTime(0);
202
}
203
// setting the new current animation will happen later
204
}
205
206
/*!
207
\fn QSequentialAnimationGroup::currentAnimationChanged(QAbstractAnimation *current)
208
209
QSequentialAnimationGroup emits this signal when currentAnimation
210
has been changed. \a current is the current animation.
211
212
\sa currentAnimation()
213
*/
214
215
216
/*!
217
Constructs a QSequentialAnimationGroup.
218
\a parent is passed to QObject's constructor.
219
*/
220
QSequentialAnimationGroup::QSequentialAnimationGroup(QObject *parent)
221
: QAnimationGroup(*
new
QSequentialAnimationGroupPrivate, parent)
222
{
223
}
224
225
/*!
226
\internal
227
*/
228
QSequentialAnimationGroup::QSequentialAnimationGroup(QSequentialAnimationGroupPrivate &dd,
229
QObject *parent)
230
: QAnimationGroup(dd, parent)
231
{
232
}
233
234
/*!
235
Destroys the animation group. It will also destroy all its animations.
236
*/
237
QSequentialAnimationGroup::~QSequentialAnimationGroup()
238
{
239
}
240
241
/*!
242
Adds a pause of \a msecs to this animation group.
243
The pause is considered as a special type of animation, thus
244
\l{QAnimationGroup::animationCount()}{animationCount} will be
245
increased by one.
246
247
\sa insertPause(), QAnimationGroup::addAnimation()
248
*/
249
QPauseAnimation *QSequentialAnimationGroup::addPause(
int
msecs)
250
{
251
QPauseAnimation *pause =
new
QPauseAnimation(msecs);
252
addAnimation(pause);
253
return
pause;
254
}
255
256
/*!
257
Inserts a pause of \a msecs milliseconds at \a index in this animation
258
group.
259
260
\sa addPause(), QAnimationGroup::insertAnimation()
261
*/
262
QPauseAnimation *QSequentialAnimationGroup::insertPause(
int
index,
int
msecs)
263
{
264
Q_D(
const
QSequentialAnimationGroup);
265
266
if
(index < 0 || index > d->animations.size()) {
267
qWarning(
"QSequentialAnimationGroup::insertPause: index is out of bounds"
);
268
return
nullptr
;
269
}
270
271
QPauseAnimation *pause =
new
QPauseAnimation(msecs);
272
insertAnimation(index, pause);
273
return
pause;
274
}
275
276
277
/*!
278
\property QSequentialAnimationGroup::currentAnimation
279
\brief the animation in the current time.
280
*/
281
QAbstractAnimation *QSequentialAnimationGroup::currentAnimation()
const
282
{
283
Q_D(
const
QSequentialAnimationGroup);
284
return
d->currentAnimation;
285
}
286
287
QBindable<QAbstractAnimation *> QSequentialAnimationGroup::bindableCurrentAnimation()
const
288
{
289
return
&d_func()->currentAnimation;
290
}
291
292
/*!
293
\reimp
294
*/
295
int
QSequentialAnimationGroup::duration()
const
296
{
297
Q_D(
const
QSequentialAnimationGroup);
298
int
ret = 0;
299
300
for
(AnimationListConstIt it = d->animations.constBegin(), cend = d->animations.constEnd(); it != cend; ++it) {
301
const
int
currentDuration = (*it)->totalDuration();
302
if
(currentDuration == -1)
303
return
-1;
// Undetermined length
304
305
if
(qAddOverflow(ret, currentDuration, &ret))
306
return
std::numeric_limits<
int
>::max();
307
}
308
309
return
ret;
310
}
311
312
/*!
313
\reimp
314
*/
315
void
QSequentialAnimationGroup::updateCurrentTime(
int
currentTime)
316
{
317
Q_D(QSequentialAnimationGroup);
318
if
(!d->currentAnimation)
319
return
;
320
321
const
QSequentialAnimationGroupPrivate::AnimationIndex newAnimationIndex = d->indexForCurrentTime();
322
323
// remove unneeded animations from actualDuration list
324
while
(newAnimationIndex.index < d->actualDuration.size())
325
d->actualDuration.removeLast();
326
327
// newAnimationIndex.index is the new current animation
328
if
(d->lastLoop < d->currentLoop
329
|| (d->lastLoop == d->currentLoop && d->currentAnimationIndex < newAnimationIndex.index)) {
330
// advancing with forward direction is the same as rewinding with backwards direction
331
d->advanceForwards(newAnimationIndex);
332
}
else
if
(d->lastLoop > d->currentLoop
333
|| (d->lastLoop == d->currentLoop && d->currentAnimationIndex > newAnimationIndex.index)) {
334
// rewinding with forward direction is the same as advancing with backwards direction
335
d->rewindForwards(newAnimationIndex);
336
}
337
338
d->setCurrentAnimation(newAnimationIndex.index);
339
340
const
int
newCurrentTime = currentTime - newAnimationIndex.timeOffset;
341
342
if
(d->currentAnimation) {
343
d->currentAnimation->setCurrentTime(newCurrentTime);
344
if
(d->atEnd()) {
345
//we make sure that we don't exceed the duration here
346
const
int
diff =
347
QAbstractAnimationPrivate::get(d->currentAnimation)->totalCurrentTime - newCurrentTime;
348
d->currentTime = addSaturating(d->currentTime, diff);
349
stop();
350
}
351
}
else
{
352
//the only case where currentAnimation could be null
353
//is when all animations have been removed
354
Q_ASSERT(d->animations.isEmpty());
355
d->currentTime = 0;
356
stop();
357
}
358
359
d->lastLoop = d->currentLoop;
360
}
361
362
/*!
363
\reimp
364
*/
365
void
QSequentialAnimationGroup::updateState(QAbstractAnimation::State newState,
366
QAbstractAnimation::State oldState)
367
{
368
Q_D(QSequentialAnimationGroup);
369
QAnimationGroup::updateState(newState, oldState);
370
371
if
(!d->currentAnimation)
372
return
;
373
374
switch
(newState) {
375
case
Stopped:
376
d->currentAnimation->stop();
377
break
;
378
case
Paused:
379
if
(oldState == d->currentAnimation->state()
380
&& oldState == QSequentialAnimationGroup::Running) {
381
d->currentAnimation->pause();
382
}
383
else
384
d->restart();
385
break
;
386
case
Running:
387
if
(oldState == d->currentAnimation->state()
388
&& oldState == QSequentialAnimationGroup::Paused)
389
d->currentAnimation->start();
390
else
391
d->restart();
392
break
;
393
}
394
}
395
396
/*!
397
\reimp
398
*/
399
void
QSequentialAnimationGroup::updateDirection(QAbstractAnimation::Direction direction)
400
{
401
Q_D(QSequentialAnimationGroup);
402
// we need to update the direction of the current animation
403
if
(state() != Stopped && d->currentAnimation)
404
d->currentAnimation->setDirection(direction);
405
}
406
407
/*!
408
\reimp
409
*/
410
bool
QSequentialAnimationGroup::event(QEvent *event)
411
{
412
return
QAnimationGroup::event(event);
413
}
414
415
void
QSequentialAnimationGroupPrivate::setCurrentAnimation(
int
index,
bool
intermediate)
416
{
417
Q_Q(QSequentialAnimationGroup);
418
// currentAnimation.removeBindingUnlessInWrapper()
419
// is not necessary here, since it is read only
420
421
index = qMin(index, animations.size() - 1);
422
423
if
(index == -1) {
424
Q_ASSERT(animations.isEmpty());
425
currentAnimationIndex = -1;
426
currentAnimation =
nullptr
;
427
return
;
428
}
429
430
// need these two checks below because this func can be called after the current animation
431
// has been removed
432
if
(index == currentAnimationIndex && animations.at(index) == currentAnimation)
433
return
;
434
435
// stop the old current animation
436
if
(currentAnimation)
437
currentAnimation->stop();
438
439
currentAnimationIndex = index;
440
currentAnimation = animations.at(index);
441
442
emit q->currentAnimationChanged(currentAnimation);
443
444
activateCurrentAnimation(intermediate);
445
}
446
447
void
QSequentialAnimationGroupPrivate::activateCurrentAnimation(
bool
intermediate)
448
{
449
if
(!currentAnimation || state == QSequentialAnimationGroup::Stopped)
450
return
;
451
452
currentAnimation->stop();
453
454
// we ensure the direction is consistent with the group's direction
455
currentAnimation->setDirection(direction);
456
457
// connects to the finish signal of uncontrolled animations
458
if
(currentAnimation->totalDuration() == -1)
459
connectUncontrolledAnimation(currentAnimation);
460
461
currentAnimation->start();
462
if
(!intermediate && state == QSequentialAnimationGroup::Paused)
463
currentAnimation->pause();
464
}
465
466
void
QSequentialAnimationGroupPrivate::_q_uncontrolledAnimationFinished()
467
{
468
Q_Q(QSequentialAnimationGroup);
469
Q_ASSERT(qobject_cast<QAbstractAnimation *>(q->sender()) == currentAnimation);
470
471
// we trust the duration returned by the animation
472
while
(actualDuration.size() < (currentAnimationIndex + 1))
473
actualDuration.append(-1);
474
actualDuration[currentAnimationIndex] = currentAnimation->currentTime();
475
476
disconnectUncontrolledAnimation(currentAnimation);
477
478
if
((direction == QAbstractAnimation::Forward && currentAnimation == animations.last())
479
|| (direction == QAbstractAnimation::Backward && currentAnimationIndex == 0)) {
480
// we don't handle looping of a group with undefined duration
481
q->stop();
482
}
else
if
(direction == QAbstractAnimation::Forward) {
483
// set the current animation to be the next one
484
setCurrentAnimation(currentAnimationIndex + 1);
485
}
else
{
486
// set the current animation to be the previous one
487
setCurrentAnimation(currentAnimationIndex - 1);
488
}
489
}
490
491
/*!
492
\internal
493
This method is called whenever an animation is added to
494
the group at index \a index.
495
Note: We only support insertion after the current animation
496
*/
497
void
QSequentialAnimationGroupPrivate::animationInsertedAt(qsizetype index)
498
{
499
if
(currentAnimation ==
nullptr
) {
500
setCurrentAnimation(0);
// initialize the current animation
501
Q_ASSERT(currentAnimation);
502
}
503
504
if
(currentAnimationIndex == index
505
&& currentAnimation->currentTime() == 0 && currentAnimation->currentLoop() == 0) {
506
//in this case we simply insert an animation before the current one has actually started
507
setCurrentAnimation(index);
508
}
509
510
//we update currentAnimationIndex in case it has changed (the animation pointer is still valid)
511
currentAnimationIndex = animations.indexOf(currentAnimation);
512
513
if
(index < currentAnimationIndex || currentLoop != 0) {
514
qWarning(
"QSequentialGroup::insertAnimation only supports to add animations after the current one."
);
515
return
;
//we're not affected because it is added after the current one
516
}
517
}
518
519
/*!
520
\internal
521
This method is called whenever an animation is removed from
522
the group at index \a index. The animation is no more listed when this
523
method is called.
524
*/
525
void
QSequentialAnimationGroupPrivate::animationRemoved(qsizetype index, QAbstractAnimation *anim)
526
{
527
Q_Q(QSequentialAnimationGroup);
528
QAnimationGroupPrivate::animationRemoved(index, anim);
529
530
if
(!currentAnimation)
531
return
;
532
533
if
(actualDuration.size() > index)
534
actualDuration.removeAt(index);
535
536
const
qsizetype currentIndex = animations.indexOf(currentAnimation);
537
if
(currentIndex == -1) {
538
//we're removing the current animation
539
540
disconnectUncontrolledAnimation(currentAnimation);
541
542
if
(index < animations.size())
543
setCurrentAnimation(index);
//let's try to take the next one
544
else
if
(index > 0)
545
setCurrentAnimation(index - 1);
546
else
// case all animations were removed
547
setCurrentAnimation(-1);
548
}
else
if
(currentAnimationIndex > index) {
549
currentAnimationIndex--;
550
}
551
552
// duration of the previous animations up to the current animation
553
currentTime = 0;
554
for
(qsizetype i = 0; i < currentAnimationIndex; ++i) {
555
const
int
current = animationActualTotalDuration(i);
556
currentTime = addSaturating(currentTime, current);
557
}
558
559
if
(currentIndex != -1) {
560
//the current animation is not the one being removed
561
//so we add its current time to the current time of this group
562
const
int
currAnimTotalTime =
563
QAbstractAnimationPrivate::get(currentAnimation)->totalCurrentTime;
564
currentTime = addSaturating(currentTime, currAnimTotalTime);
565
}
566
567
//let's also update the total current time
568
totalCurrentTime = addSaturating(currentTime, q->totalDuration());
569
}
570
571
QT_END_NAMESPACE
572
573
#
include
"moc_qsequentialanimationgroup.cpp"
QT_BEGIN_NAMESPACE
Combined button and popup list for selecting options.
Definition
qsequentialanimationgroup.cpp:47
QT_BEGIN_NAMESPACE::addSaturating
int addSaturating(int a, int b)
Definition
qsequentialanimationgroup.cpp:53
AnimationListConstIt
QList< QAbstractAnimation * >::ConstIterator AnimationListConstIt
Definition
qsequentialanimationgroup.cpp:63
qtbase
src
corelib
animation
qsequentialanimationgroup.cpp
Generated on
for Qt by
1.18.0