~loic.molinari/+junk/qtdeclarative-shadereffectsource-changes

« back to all changes in this revision

Viewing changes to src/qml/animations/qsequentialanimationgroupjob.cpp

  • Committer: Loïc Molinari
  • Date: 2012-04-21 17:59:51 UTC
  • Revision ID: loic.molinari@canonical.com-20120421175951-bqx68caaf5zrp76l
Initial import

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/****************************************************************************
 
2
**
 
3
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
 
4
** Contact: http://www.qt-project.org/
 
5
**
 
6
** This file is part of the QtQml module of the Qt Toolkit.
 
7
**
 
8
** $QT_BEGIN_LICENSE:LGPL$
 
9
** GNU Lesser General Public License Usage
 
10
** This file may be used under the terms of the GNU Lesser General Public
 
11
** License version 2.1 as published by the Free Software Foundation and
 
12
** appearing in the file LICENSE.LGPL included in the packaging of this
 
13
** file. Please review the following information to ensure the GNU Lesser
 
14
** General Public License version 2.1 requirements will be met:
 
15
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
 
16
**
 
17
** In addition, as a special exception, Nokia gives you certain additional
 
18
** rights. These rights are described in the Nokia Qt LGPL Exception
 
19
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
 
20
**
 
21
** GNU General Public License Usage
 
22
** Alternatively, this file may be used under the terms of the GNU General
 
23
** Public License version 3.0 as published by the Free Software Foundation
 
24
** and appearing in the file LICENSE.GPL included in the packaging of this
 
25
** file. Please review the following information to ensure the GNU General
 
26
** Public License version 3.0 requirements will be met:
 
27
** http://www.gnu.org/copyleft/gpl.html.
 
28
**
 
29
** Other Usage
 
30
** Alternatively, this file may be used in accordance with the terms and
 
31
** conditions contained in a signed written agreement between you and Nokia.
 
32
**
 
33
**
 
34
**
 
35
**
 
36
**
 
37
**
 
38
** $QT_END_LICENSE$
 
39
**
 
40
****************************************************************************/
 
41
 
 
42
#include "private/qsequentialanimationgroupjob_p.h"
 
43
#include "private/qpauseanimationjob_p.h"
 
44
#include "private/qanimationjobutil_p.h"
 
45
 
 
46
QT_BEGIN_NAMESPACE
 
47
 
 
48
QSequentialAnimationGroupJob::QSequentialAnimationGroupJob()
 
49
    : QAnimationGroupJob()
 
50
    , m_currentAnimation(0)
 
51
    , m_previousLoop(0)
 
52
{
 
53
}
 
54
 
 
55
QSequentialAnimationGroupJob::~QSequentialAnimationGroupJob()
 
56
{
 
57
}
 
58
 
 
59
bool QSequentialAnimationGroupJob::atEnd() const
 
60
{
 
61
    // we try to detect if we're at the end of the group
 
62
    //this is true if the following conditions are true:
 
63
    // 1. we're in the last loop
 
64
    // 2. the direction is forward
 
65
    // 3. the current animation is the last one
 
66
    // 4. the current animation has reached its end
 
67
    const int animTotalCurrentTime = m_currentAnimation->currentTime();
 
68
    return (m_currentLoop == m_loopCount - 1
 
69
        && m_direction == Forward
 
70
        && !m_currentAnimation->nextSibling()
 
71
        && animTotalCurrentTime == animationActualTotalDuration(m_currentAnimation));
 
72
}
 
73
 
 
74
int QSequentialAnimationGroupJob::animationActualTotalDuration(QAbstractAnimationJob *anim) const
 
75
{
 
76
    int ret = anim->totalDuration();
 
77
    if (ret == -1)
 
78
        ret = uncontrolledAnimationFinishTime(anim); //we can try the actual duration there
 
79
    return ret;
 
80
}
 
81
 
 
82
QSequentialAnimationGroupJob::AnimationIndex QSequentialAnimationGroupJob::indexForCurrentTime() const
 
83
{
 
84
    Q_ASSERT(firstChild());
 
85
 
 
86
    AnimationIndex ret;
 
87
    QAbstractAnimationJob *anim = 0;
 
88
    int duration = 0;
 
89
 
 
90
    for (anim = firstChild(); anim; anim = anim->nextSibling()) {
 
91
        duration = animationActualTotalDuration(anim);
 
92
 
 
93
        // 'animation' is the current animation if one of these reasons is true:
 
94
        // 1. it's duration is undefined
 
95
        // 2. it ends after msecs
 
96
        // 3. it is the last animation (this can happen in case there is at least 1 uncontrolled animation)
 
97
        // 4. it ends exactly in msecs and the direction is backwards
 
98
        if (duration == -1 || m_currentTime < (ret.timeOffset + duration)
 
99
            || (m_currentTime == (ret.timeOffset + duration) && m_direction == QAbstractAnimationJob::Backward)) {
 
100
            ret.animation = anim;
 
101
            return ret;
 
102
        }
 
103
 
 
104
        if (anim == m_currentAnimation)
 
105
            ret.afterCurrent = true;
 
106
 
 
107
        // 'animation' has a non-null defined duration and is not the one at time 'msecs'.
 
108
        ret.timeOffset += duration;
 
109
    }
 
110
 
 
111
    // this can only happen when one of those conditions is true:
 
112
    // 1. the duration of the group is undefined and we passed its actual duration
 
113
    // 2. there are only 0-duration animations in the group
 
114
    ret.timeOffset -= duration;
 
115
    ret.animation = lastChild();
 
116
    return ret;
 
117
}
 
118
 
 
119
void QSequentialAnimationGroupJob::restart()
 
120
{
 
121
    // restarting the group by making the first/last animation the current one
 
122
    if (m_direction == Forward) {
 
123
        m_previousLoop = 0;
 
124
        if (m_currentAnimation == firstChild())
 
125
            activateCurrentAnimation();
 
126
        else
 
127
            setCurrentAnimation(firstChild());
 
128
    }
 
129
    else { // direction == Backward
 
130
        m_previousLoop = m_loopCount - 1;
 
131
        if (m_currentAnimation == lastChild())
 
132
            activateCurrentAnimation();
 
133
        else
 
134
            setCurrentAnimation(lastChild());
 
135
    }
 
136
}
 
137
 
 
138
void QSequentialAnimationGroupJob::advanceForwards(const AnimationIndex &newAnimationIndex)
 
139
{
 
140
    if (m_previousLoop < m_currentLoop) {
 
141
        // we need to fast forward to the end
 
142
        for (QAbstractAnimationJob *anim = m_currentAnimation; anim; anim = anim->nextSibling()) {
 
143
            setCurrentAnimation(anim, true);
 
144
            RETURN_IF_DELETED(anim->setCurrentTime(animationActualTotalDuration(anim)));
 
145
        }
 
146
        // this will make sure the current animation is reset to the beginning
 
147
        if (firstChild() && !firstChild()->nextSibling())   //count == 1
 
148
            // we need to force activation because setCurrentAnimation will have no effect
 
149
            activateCurrentAnimation();
 
150
        else
 
151
            setCurrentAnimation(firstChild(), true);
 
152
    }
 
153
 
 
154
    // and now we need to fast forward from the current position to
 
155
    for (QAbstractAnimationJob *anim = m_currentAnimation; anim && anim != newAnimationIndex.animation; anim = anim->nextSibling()) {     //### WRONG,
 
156
        setCurrentAnimation(anim, true);
 
157
        RETURN_IF_DELETED(anim->setCurrentTime(animationActualTotalDuration(anim)));
 
158
    }
 
159
    // setting the new current animation will happen later
 
160
}
 
161
 
 
162
void QSequentialAnimationGroupJob::rewindForwards(const AnimationIndex &newAnimationIndex)
 
163
{
 
164
    if (m_previousLoop > m_currentLoop) {
 
165
        // we need to fast rewind to the beginning
 
166
        for (QAbstractAnimationJob *anim = m_currentAnimation; anim; anim = anim->previousSibling()) {
 
167
            setCurrentAnimation(anim, true);
 
168
            RETURN_IF_DELETED(anim->setCurrentTime(0));
 
169
        }
 
170
        // this will make sure the current animation is reset to the end
 
171
        if (lastChild() && !lastChild()->previousSibling())   //count == 1
 
172
            // we need to force activation because setCurrentAnimation will have no effect
 
173
            activateCurrentAnimation();
 
174
        else {
 
175
            setCurrentAnimation(lastChild(), true);
 
176
        }
 
177
    }
 
178
 
 
179
    // and now we need to fast rewind from the current position to
 
180
    for (QAbstractAnimationJob *anim = m_currentAnimation; anim && anim != newAnimationIndex.animation; anim = anim->previousSibling()) {
 
181
        setCurrentAnimation(anim, true);
 
182
        RETURN_IF_DELETED(anim->setCurrentTime(0));
 
183
    }
 
184
    // setting the new current animation will happen later
 
185
}
 
186
 
 
187
int QSequentialAnimationGroupJob::duration() const
 
188
{
 
189
    int ret = 0;
 
190
 
 
191
    for (QAbstractAnimationJob *anim = firstChild(); anim; anim = anim->nextSibling()) {
 
192
        const int currentDuration = anim->totalDuration();
 
193
        if (currentDuration == -1)
 
194
            return -1; // Undetermined length
 
195
 
 
196
        ret += currentDuration;
 
197
    }
 
198
 
 
199
    return ret;
 
200
}
 
201
 
 
202
void QSequentialAnimationGroupJob::updateCurrentTime(int currentTime)
 
203
{
 
204
    if (!m_currentAnimation)
 
205
        return;
 
206
 
 
207
    const QSequentialAnimationGroupJob::AnimationIndex newAnimationIndex = indexForCurrentTime();
 
208
 
 
209
    // newAnimationIndex.index is the new current animation
 
210
    if (m_previousLoop < m_currentLoop
 
211
        || (m_previousLoop == m_currentLoop && m_currentAnimation != newAnimationIndex.animation && newAnimationIndex.afterCurrent)) {
 
212
            // advancing with forward direction is the same as rewinding with backwards direction
 
213
            RETURN_IF_DELETED(advanceForwards(newAnimationIndex));
 
214
    } else if (m_previousLoop > m_currentLoop
 
215
        || (m_previousLoop == m_currentLoop && m_currentAnimation != newAnimationIndex.animation && !newAnimationIndex.afterCurrent)) {
 
216
            // rewinding with forward direction is the same as advancing with backwards direction
 
217
            RETURN_IF_DELETED(rewindForwards(newAnimationIndex));
 
218
    }
 
219
 
 
220
    setCurrentAnimation(newAnimationIndex.animation);
 
221
 
 
222
    const int newCurrentTime = currentTime - newAnimationIndex.timeOffset;
 
223
 
 
224
    if (m_currentAnimation) {
 
225
        RETURN_IF_DELETED(m_currentAnimation->setCurrentTime(newCurrentTime));
 
226
        if (atEnd()) {
 
227
            //we make sure that we don't exceed the duration here
 
228
            m_currentTime += m_currentAnimation->currentTime() - newCurrentTime;
 
229
            stop();
 
230
        }
 
231
    } else {
 
232
        //the only case where currentAnimation could be null
 
233
        //is when all animations have been removed
 
234
        Q_ASSERT(!firstChild());
 
235
        m_currentTime = 0;
 
236
        stop();
 
237
    }
 
238
 
 
239
    m_previousLoop = m_currentLoop;
 
240
}
 
241
 
 
242
void QSequentialAnimationGroupJob::updateState(QAbstractAnimationJob::State newState,
 
243
                                            QAbstractAnimationJob::State oldState)
 
244
{
 
245
    QAnimationGroupJob::updateState(newState, oldState);
 
246
 
 
247
    if (!m_currentAnimation)
 
248
        return;
 
249
 
 
250
    switch (newState) {
 
251
    case Stopped:
 
252
        m_currentAnimation->stop();
 
253
        break;
 
254
    case Paused:
 
255
        if (oldState == m_currentAnimation->state() && oldState == Running)
 
256
            m_currentAnimation->pause();
 
257
        else
 
258
            restart();
 
259
        break;
 
260
    case Running:
 
261
        if (oldState == m_currentAnimation->state() && oldState == Paused)
 
262
            m_currentAnimation->start();
 
263
        else
 
264
            restart();
 
265
        break;
 
266
    }
 
267
}
 
268
 
 
269
void QSequentialAnimationGroupJob::updateDirection(QAbstractAnimationJob::Direction direction)
 
270
{
 
271
    // we need to update the direction of the current animation
 
272
    if (!isStopped() && m_currentAnimation)
 
273
        m_currentAnimation->setDirection(direction);
 
274
}
 
275
 
 
276
void QSequentialAnimationGroupJob::setCurrentAnimation(QAbstractAnimationJob *anim, bool intermediate)
 
277
{
 
278
    if (!anim) {
 
279
        Q_ASSERT(!firstChild());
 
280
        m_currentAnimation = 0;
 
281
        return;
 
282
    }
 
283
 
 
284
    if (anim == m_currentAnimation)
 
285
        return;
 
286
 
 
287
    // stop the old current animation
 
288
    if (m_currentAnimation)
 
289
        m_currentAnimation->stop();
 
290
 
 
291
    m_currentAnimation = anim;
 
292
 
 
293
    activateCurrentAnimation(intermediate);
 
294
}
 
295
 
 
296
void QSequentialAnimationGroupJob::activateCurrentAnimation(bool intermediate)
 
297
{
 
298
    if (!m_currentAnimation || isStopped())
 
299
        return;
 
300
 
 
301
    m_currentAnimation->stop();
 
302
 
 
303
    // we ensure the direction is consistent with the group's direction
 
304
    m_currentAnimation->setDirection(m_direction);
 
305
 
 
306
    // reset the finish time of the animation if it is uncontrolled
 
307
    if (m_currentAnimation->totalDuration() == -1)
 
308
        resetUncontrolledAnimationFinishTime(m_currentAnimation);
 
309
 
 
310
    m_currentAnimation->start();
 
311
    if (!intermediate && isPaused())
 
312
        m_currentAnimation->pause();
 
313
}
 
314
 
 
315
void QSequentialAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimationJob *animation)
 
316
{
 
317
    Q_UNUSED(animation);
 
318
    Q_ASSERT(animation == m_currentAnimation);
 
319
 
 
320
    setUncontrolledAnimationFinishTime(m_currentAnimation, m_currentAnimation->currentTime());
 
321
 
 
322
    if ((m_direction == Forward && m_currentAnimation == lastChild())
 
323
        || (m_direction == Backward && m_currentAnimation == firstChild())) {
 
324
        // we don't handle looping of a group with undefined duration
 
325
        stop();
 
326
    } else if (m_direction == Forward) {
 
327
        // set the current animation to be the next one
 
328
        setCurrentAnimation(m_currentAnimation->nextSibling());
 
329
    } else {
 
330
        // set the current animation to be the previous one
 
331
        setCurrentAnimation(m_currentAnimation->previousSibling());
 
332
    }
 
333
}
 
334
 
 
335
void QSequentialAnimationGroupJob::animationInserted(QAbstractAnimationJob *anim)
 
336
{
 
337
    if (m_currentAnimation == 0)
 
338
        setCurrentAnimation(firstChild()); // initialize the current animation
 
339
 
 
340
    if (m_currentAnimation == anim->nextSibling()
 
341
        && m_currentAnimation->currentTime() == 0 && m_currentAnimation->currentLoop() == 0) {
 
342
            //in this case we simply insert the animation before the current one has actually started
 
343
            setCurrentAnimation(anim);
 
344
    }
 
345
 
 
346
//    TODO
 
347
//    if (index < m_currentAnimationIndex || m_currentLoop != 0) {
 
348
//        qWarning("QSequentialGroup::insertAnimation only supports to add animations after the current one.");
 
349
//        return; //we're not affected because it is added after the current one
 
350
//    }
 
351
}
 
352
 
 
353
void QSequentialAnimationGroupJob::animationRemoved(QAbstractAnimationJob *anim, QAbstractAnimationJob *prev, QAbstractAnimationJob *next)
 
354
{
 
355
    QAnimationGroupJob::animationRemoved(anim, prev, next);
 
356
 
 
357
    Q_ASSERT(m_currentAnimation); // currentAnimation should always be set
 
358
 
 
359
    bool removingCurrent = anim == m_currentAnimation;
 
360
    if (removingCurrent) {
 
361
        if (next)
 
362
            setCurrentAnimation(next); //let's try to take the next one
 
363
        else if (prev)
 
364
            setCurrentAnimation(prev);
 
365
        else// case all animations were removed
 
366
            setCurrentAnimation(0);
 
367
    }
 
368
 
 
369
    // duration of the previous animations up to the current animation
 
370
    m_currentTime = 0;
 
371
    for (QAbstractAnimationJob *anim = firstChild(); anim; anim = anim->nextSibling()) {
 
372
        if (anim == m_currentAnimation)
 
373
            break;
 
374
        m_currentTime += animationActualTotalDuration(anim);
 
375
 
 
376
    }
 
377
 
 
378
    if (!removingCurrent) {
 
379
        //the current animation is not the one being removed
 
380
        //so we add its current time to the current time of this group
 
381
        m_currentTime += m_currentAnimation->currentTime();
 
382
    }
 
383
 
 
384
    //let's also update the total current time
 
385
    m_totalCurrentTime = m_currentTime + m_loopCount * duration();
 
386
}
 
387
 
 
388
QT_END_NAMESPACE