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 QtQuick 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#include "qquicktimeline_p_p.h"
41
42#include <QDebug>
43#include <QMutex>
44#include <QThread>
45#include <QWaitCondition>
46#include <QEvent>
47#include <QCoreApplication>
48#include <QEasingCurve>
49#include <QTime>
50#include <QtCore/private/qnumeric_p.h>
51
52#include <algorithm>
53
54QT_BEGIN_NAMESPACE
55
56struct Update {
57 Update(QQuickTimeLineValue *_g, qreal _v)
58 : g(_g), v(_v) {}
59 Update(const QQuickTimeLineCallback &_e)
60 : g(nullptr), v(0), e(_e) {}
61
62 QQuickTimeLineValue *g;
63 qreal v;
64 QQuickTimeLineCallback e;
65};
66
67struct QQuickTimeLinePrivate
68{
69 QQuickTimeLinePrivate(QQuickTimeLine *);
70
71 struct Op {
72 enum Type {
73 Pause,
74 Set,
75 Move,
76 MoveBy,
77 Accel,
78 AccelDistance,
79 Execute
80 };
81 Op() {}
82 Op(Type t, int l, qreal v, qreal v2, int o,
83 const QQuickTimeLineCallback &ev = QQuickTimeLineCallback(), const QEasingCurve &es = QEasingCurve())
84 : type(t), length(l), value(v), value2(v2), order(o), event(ev),
85 easing(es) {}
86 Op(const Op &o)
87 : type(o.type), length(o.length), value(o.value), value2(o.value2),
88 order(o.order), event(o.event), easing(o.easing) {}
89 Op &operator=(const Op &o) {
90 type = o.type; length = o.length; value = o.value;
91 value2 = o.value2; order = o.order; event = o.event;
92 easing = o.easing;
93 return *this;
94 }
95
96 Type type;
97 int length;
98 qreal value;
99 qreal value2;
100
101 int order;
102 QQuickTimeLineCallback event;
103 QEasingCurve easing;
104 };
105 struct TimeLine
106 {
107 TimeLine() {}
108 QList<Op> ops;
109 int length = 0;
110 int consumedOpLength = 0;
111 qreal base = 0.;
112 };
113
114 int length;
115 int syncPoint;
116 typedef QHash<QQuickTimeLineObject *, TimeLine> Ops;
117 Ops ops;
118 QQuickTimeLine *q;
119
120 void add(QQuickTimeLineObject &, const Op &);
121 qreal value(const Op &op, int time, qreal base, bool *) const;
122
123 int advance(int);
124
125 bool clockRunning;
126 int prevTime;
127
128 int order;
129
130 QQuickTimeLine::SyncMode syncMode;
131 int syncAdj;
132 QList<QPair<int, Update> > *updateQueue;
133};
134
135QQuickTimeLinePrivate::QQuickTimeLinePrivate(QQuickTimeLine *parent)
136: length(0), syncPoint(0), q(parent), clockRunning(false), prevTime(0), order(0), syncMode(QQuickTimeLine::LocalSync), syncAdj(0), updateQueue(nullptr)
137{
138}
139
140void QQuickTimeLinePrivate::add(QQuickTimeLineObject &g, const Op &o)
141{
142 if (g._t && g._t != q) {
143 qWarning() << "QQuickTimeLine: Cannot modify a QQuickTimeLineValue owned by"
144 << "another timeline.";
145 return;
146 }
147 g._t = q;
148
149 Ops::Iterator iter = ops.find(&g);
150 if (iter == ops.end()) {
151 iter = ops.insert(&g, TimeLine());
152 if (syncPoint > 0)
153 q->pause(g, syncPoint);
154 }
155 if (!iter->ops.isEmpty() &&
156 o.type == Op::Pause &&
157 iter->ops.constLast().type == Op::Pause) {
158 iter->ops.last().length += o.length;
159 iter->length += o.length;
160 } else {
161 iter->ops.append(o);
162 iter->length += o.length;
163 }
164
165 if (iter->length > length)
166 length = iter->length;
167
168 if (!clockRunning) {
169 q->stop();
170 prevTime = 0;
171 clockRunning = true;
172
173 if (syncMode == QQuickTimeLine::LocalSync) {
174 syncAdj = -1;
175 } else {
176 syncAdj = 0;
177 }
178 q->start();
179/* q->tick(0);
180 if (syncMode == QQuickTimeLine::LocalSync) {
181 syncAdj = -1;
182 } else {
183 syncAdj = 0;
184 }
185 */
186 }
187}
188
189qreal QQuickTimeLinePrivate::value(const Op &op, int time, qreal base, bool *changed) const
190{
191 Q_ASSERT(time >= 0);
192 Q_ASSERT(time <= op.length);
193 *changed = true;
194
195 switch(op.type) {
196 case Op::Pause:
197 *changed = false;
198 return base;
199 case Op::Set:
200 return op.value;
201 case Op::Move:
202 if (time == 0) {
203 return base;
204 } else if (time == (op.length)) {
205 return op.value;
206 } else {
207 qreal delta = op.value - base;
208 qreal pTime = (qreal)(time) / (qreal)op.length;
209 if (op.easing.type() == QEasingCurve::Linear)
210 return base + delta * pTime;
211 else
212 return base + delta * op.easing.valueForProgress(pTime);
213 }
214 case Op::MoveBy:
215 if (time == 0) {
216 return base;
217 } else if (time == (op.length)) {
218 return base + op.value;
219 } else {
220 qreal delta = op.value;
221 qreal pTime = (qreal)(time) / (qreal)op.length;
222 if (op.easing.type() == QEasingCurve::Linear)
223 return base + delta * pTime;
224 else
225 return base + delta * op.easing.valueForProgress(pTime);
226 }
227 case Op::Accel:
228 if (time == 0) {
229 return base;
230 } else {
231 qreal t = (qreal)(time) / 1000.0f;
232 qreal delta = op.value * t + 0.5f * op.value2 * t * t;
233 return base + delta;
234 }
235 case Op::AccelDistance:
236 if (time == 0) {
237 return base;
238 } else if (time == (op.length)) {
239 return base + op.value2;
240 } else {
241 qreal t = (qreal)(time) / 1000.0f;
242 qreal accel = -1.0f * 1000.0f * op.value / (qreal)op.length;
243 qreal delta = op.value * t + 0.5f * accel * t * t;
244 return base + delta;
245
246 }
247 case Op::Execute:
248 op.event.d0(op.event.d1);
249 *changed = false;
250 return -1;
251 }
252
253 return base;
254}
255
256/*!
257 \internal
258 \class QQuickTimeLine
259 \brief The QQuickTimeLine class provides a timeline for controlling animations.
260
261 QQuickTimeLine is similar to QTimeLine except:
262 \list
263 \li It updates QQuickTimeLineValue instances directly, rather than maintaining a single
264 current value.
265
266 For example, the following animates a simple value over 200 milliseconds:
267 \code
268 QQuickTimeLineValue v(<starting value>);
269 QQuickTimeLine tl;
270 tl.move(v, 100., 200);
271 tl.start()
272 \endcode
273
274 If your program needs to know when values are changed, it can either
275 connect to the QQuickTimeLine's updated() signal, or inherit from QQuickTimeLineValue
276 and reimplement the QQuickTimeLineValue::setValue() method.
277
278 \li Supports multiple QQuickTimeLineValue, arbitrary start and end values and allows
279 animations to be strung together for more complex effects.
280
281 For example, the following animation moves the x and y coordinates of
282 an object from wherever they are to the position (100, 100) in 50
283 milliseconds and then further animates them to (100, 200) in 50
284 milliseconds:
285
286 \code
287 QQuickTimeLineValue x(<starting value>);
288 QQuickTimeLineValue y(<starting value>);
289
290 QQuickTimeLine tl;
291 tl.start();
292
293 tl.move(x, 100., 50);
294 tl.move(y, 100., 50);
295 tl.move(y, 200., 50);
296 \endcode
297
298 \li All QQuickTimeLine instances share a single, synchronized clock.
299
300 Actions scheduled within the same event loop tick are scheduled
301 synchronously against each other, regardless of the wall time between the
302 scheduling. Synchronized scheduling applies both to within the same
303 QQuickTimeLine and across separate QQuickTimeLine's within the same process.
304
305 \endlist
306
307 Currently easing functions are not supported.
308*/
309
310
311/*!
312 Construct a new QQuickTimeLine with the specified \a parent.
313*/
314QQuickTimeLine::QQuickTimeLine(QObject *parent)
315 : QObject(parent)
316{
317 d = new QQuickTimeLinePrivate(this);
318}
319
320/*!
321 Destroys the time line. Any inprogress animations are canceled, but not
322 completed.
323*/
324QQuickTimeLine::~QQuickTimeLine()
325{
326 for (QQuickTimeLinePrivate::Ops::Iterator iter = d->ops.begin();
327 iter != d->ops.end();
328 ++iter)
329 iter.key()->_t = nullptr;
330
331 delete d; d = nullptr;
332}
333
334/*!
335 \enum QQuickTimeLine::SyncMode
336 */
337
338/*!
339 Return the timeline's synchronization mode.
340 */
341QQuickTimeLine::SyncMode QQuickTimeLine::syncMode() const
342{
343 return d->syncMode;
344}
345
346/*!
347 Set the timeline's synchronization mode to \a syncMode.
348 */
349void QQuickTimeLine::setSyncMode(SyncMode syncMode)
350{
351 d->syncMode = syncMode;
352}
353
354/*!
355 Pause \a obj for \a time milliseconds.
356*/
357void QQuickTimeLine::pause(QQuickTimeLineObject &obj, int time)
358{
359 if (time <= 0) return;
360 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::Pause, time, 0., 0., d->order++);
361 d->add(obj, op);
362}
363
364/*!
365 Execute the \a event.
366 */
367void QQuickTimeLine::callback(const QQuickTimeLineCallback &callback)
368{
369 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::Execute, 0, 0, 0., d->order++, callback);
370 d->add(*callback.callbackObject(), op);
371}
372
373/*!
374 Set the \a value of \a timeLineValue.
375*/
376void QQuickTimeLine::set(QQuickTimeLineValue &timeLineValue, qreal value)
377{
378 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::Set, 0, value, 0., d->order++);
379 d->add(timeLineValue, op);
380}
381
382/*!
383 Decelerate \a timeLineValue from the starting \a velocity to zero at the
384 given \a acceleration rate. Although the \a acceleration is technically
385 a deceleration, it should always be positive. The QQuickTimeLine will ensure
386 that the deceleration is in the opposite direction to the initial velocity.
387*/
388int QQuickTimeLine::accel(QQuickTimeLineValue &timeLineValue, qreal velocity, qreal acceleration)
389{
390 if (qFuzzyIsNull(acceleration) || qt_is_nan(acceleration))
391 return -1;
392
393 if ((velocity > 0.0f) == (acceleration > 0.0f))
394 acceleration = acceleration * -1.0f;
395
396 int time = static_cast<int>(-1000 * velocity / acceleration);
397 if (time <= 0) return -1;
398
399 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++);
400 d->add(timeLineValue, op);
401
402 return time;
403}
404
405/*!
406 \overload
407
408 Decelerate \a timeLineValue from the starting \a velocity to zero at the
409 given \a acceleration rate over a maximum distance of maxDistance.
410
411 If necessary, QQuickTimeLine will reduce the acceleration to ensure that the
412 entire operation does not require a move of more than \a maxDistance.
413 \a maxDistance should always be positive.
414*/
415int QQuickTimeLine::accel(QQuickTimeLineValue &timeLineValue, qreal velocity, qreal acceleration, qreal maxDistance)
416{
417 if (qFuzzyIsNull(maxDistance) || qt_is_nan(maxDistance) || qFuzzyIsNull(acceleration) || qt_is_nan(acceleration))
418 return -1;
419
420 Q_ASSERT(acceleration > 0.0f && maxDistance > 0.0f);
421
422 qreal maxAccel = (velocity * velocity) / (2.0f * maxDistance);
423 if (maxAccel > acceleration)
424 acceleration = maxAccel;
425
426 if ((velocity > 0.0f) == (acceleration > 0.0f))
427 acceleration = acceleration * -1.0f;
428
429 int time = static_cast<int>(-1000 * velocity / acceleration);
430 if (time <= 0) return -1;
431
432 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++);
433 d->add(timeLineValue, op);
434
435 return time;
436}
437
438/*!
439 Decelerate \a timeLineValue from the starting \a velocity to zero over the given
440 \a distance. This is like accel(), but the QQuickTimeLine calculates the exact
441 deceleration to use.
442
443 \a distance should be positive.
444*/
445int QQuickTimeLine::accelDistance(QQuickTimeLineValue &timeLineValue, qreal velocity, qreal distance)
446{
447 if (qFuzzyIsNull(distance) || qt_is_nan(distance) || qFuzzyIsNull(velocity) || qt_is_nan(velocity))
448 return -1;
449
450 Q_ASSERT((distance >= 0.0f) == (velocity >= 0.0f));
451
452 int time = static_cast<int>(1000 * (2.0f * distance) / velocity);
453 if (time <= 0) return -1;
454
455 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::AccelDistance, time, velocity, distance, d->order++);
456 d->add(timeLineValue, op);
457
458 return time;
459}
460
461/*!
462 Linearly change the \a timeLineValue from its current value to the given
463 \a destination value over \a time milliseconds.
464*/
465void QQuickTimeLine::move(QQuickTimeLineValue &timeLineValue, qreal destination, int time)
466{
467 if (time <= 0) return;
468 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++);
469 d->add(timeLineValue, op);
470}
471
472/*!
473 Change the \a timeLineValue from its current value to the given \a destination
474 value over \a time milliseconds using the \a easing curve.
475 */
476void QQuickTimeLine::move(QQuickTimeLineValue &timeLineValue, qreal destination, const QEasingCurve &easing, int time)
477{
478 if (time <= 0) return;
479 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++, QQuickTimeLineCallback(), easing);
480 d->add(timeLineValue, op);
481}
482
483/*!
484 Linearly change the \a timeLineValue from its current value by the \a change amount
485 over \a time milliseconds.
486*/
487void QQuickTimeLine::moveBy(QQuickTimeLineValue &timeLineValue, qreal change, int time)
488{
489 if (time <= 0) return;
490 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++);
491 d->add(timeLineValue, op);
492}
493
494/*!
495 Change the \a timeLineValue from its current value by the \a change amount over
496 \a time milliseconds using the \a easing curve.
497 */
498void QQuickTimeLine::moveBy(QQuickTimeLineValue &timeLineValue, qreal change, const QEasingCurve &easing, int time)
499{
500 if (time <= 0) return;
501 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++, QQuickTimeLineCallback(), easing);
502 d->add(timeLineValue, op);
503}
504
505/*!
506 Cancel (but don't complete) all scheduled actions for \a timeLineValue.
507*/
508void QQuickTimeLine::reset(QQuickTimeLineValue &timeLineValue)
509{
510 if (!timeLineValue._t)
511 return;
512 if (timeLineValue._t != this) {
513 qWarning() << "QQuickTimeLine: Cannot reset a QQuickTimeLineValue owned by another timeline.";
514 return;
515 }
516 remove(&timeLineValue);
517 timeLineValue._t = nullptr;
518}
519
520int QQuickTimeLine::duration() const
521{
522 return -1;
523}
524
525/*!
526 Synchronize the end point of \a timeLineValue to the endpoint of \a syncTo
527 within this timeline.
528
529 Following operations on \a timeLineValue in this timeline will be scheduled after
530 all the currently scheduled actions on \a syncTo are complete. In
531 pseudo-code this is equivalent to:
532 \code
533 QQuickTimeLine::pause(timeLineValue, min(0, length_of(syncTo) - length_of(timeLineValue)))
534 \endcode
535*/
536void QQuickTimeLine::sync(QQuickTimeLineValue &timeLineValue, QQuickTimeLineValue &syncTo)
537{
538 QQuickTimeLinePrivate::Ops::Iterator iter = d->ops.find(&syncTo);
539 if (iter == d->ops.end())
540 return;
541 int length = iter->length;
542
543 iter = d->ops.find(&timeLineValue);
544 if (iter == d->ops.end()) {
545 pause(timeLineValue, length);
546 } else {
547 int glength = iter->length;
548 pause(timeLineValue, length - glength);
549 }
550}
551
552/*!
553 Synchronize the end point of \a timeLineValue to the endpoint of the longest
554 action cursrently scheduled in the timeline.
555
556 In pseudo-code, this is equivalent to:
557 \code
558 QQuickTimeLine::pause(timeLineValue, length_of(timeline) - length_of(timeLineValue))
559 \endcode
560*/
561void QQuickTimeLine::sync(QQuickTimeLineValue &timeLineValue)
562{
563 QQuickTimeLinePrivate::Ops::Iterator iter = d->ops.find(&timeLineValue);
564 if (iter == d->ops.end()) {
565 pause(timeLineValue, d->length);
566 } else {
567 pause(timeLineValue, d->length - iter->length);
568 }
569}
570
571/*
572 Synchronize all currently and future scheduled values in this timeline to
573 the longest action currently scheduled.
574
575 For example:
576 \code
577 value1->setValue(0.);
578 value2->setValue(0.);
579 value3->setValue(0.);
580 QQuickTimeLine tl;
581 ...
582 tl.move(value1, 10, 200);
583 tl.move(value2, 10, 100);
584 tl.sync();
585 tl.move(value2, 20, 100);
586 tl.move(value3, 20, 100);
587 \endcode
588
589 will result in:
590
591 \table
592 \header \li \li 0ms \li 50ms \li 100ms \li 150ms \li 200ms \li 250ms \li 300ms
593 \row \li value1 \li 0 \li 2.5 \li 5.0 \li 7.5 \li 10 \li 10 \li 10
594 \row \li value2 \li 0 \li 5.0 \li 10.0 \li 10.0 \li 10.0 \li 15.0 \li 20.0
595 \row \li value2 \li 0 \li 0 \li 0 \li 0 \li 0 \li 10.0 \li 20.0
596 \endtable
597*/
598
599/*void QQuickTimeLine::sync()
600{
601 for (QQuickTimeLinePrivate::Ops::Iterator iter = d->ops.begin();
602 iter != d->ops.end();
603 ++iter)
604 pause(*iter.key(), d->length - iter->length);
605 d->syncPoint = d->length;
606}*/
607
608/*!
609 \internal
610
611 Temporary hack.
612 */
613void QQuickTimeLine::setSyncPoint(int sp)
614{
615 d->syncPoint = sp;
616}
617
618/*!
619 \internal
620
621 Temporary hack.
622 */
623int QQuickTimeLine::syncPoint() const
624{
625 return d->syncPoint;
626}
627
628/*!
629 Returns true if the timeline is active. An active timeline is one where
630 QQuickTimeLineValue actions are still pending.
631*/
632bool QQuickTimeLine::isActive() const
633{
634 return !d->ops.isEmpty();
635}
636
637/*!
638 Completes the timeline. All queued actions are played to completion, and then discarded. For example,
639 \code
640 QQuickTimeLineValue v(0.);
641 QQuickTimeLine tl;
642 tl.move(v, 100., 1000.);
643 // 500 ms passes
644 // v.value() == 50.
645 tl.complete();
646 // v.value() == 100.
647 \endcode
648*/
649void QQuickTimeLine::complete()
650{
651 d->advance(d->length);
652}
653
654/*!
655 Resets the timeline. All queued actions are discarded and QQuickTimeLineValue's retain their current value. For example,
656 \code
657 QQuickTimeLineValue v(0.);
658 QQuickTimeLine tl;
659 tl.move(v, 100., 1000.);
660 // 500 ms passes
661 // v.value() == 50.
662 tl.clear();
663 // v.value() == 50.
664 \endcode
665*/
666void QQuickTimeLine::clear()
667{
668 for (QQuickTimeLinePrivate::Ops::const_iterator iter = d->ops.cbegin(), cend = d->ops.cend(); iter != cend; ++iter)
669 iter.key()->_t = nullptr;
670 d->ops.clear();
671 d->length = 0;
672 d->syncPoint = 0;
673 //XXX need stop here?
674}
675
676int QQuickTimeLine::time() const
677{
678 return d->prevTime;
679}
680
681/*!
682 \fn void QQuickTimeLine::updated()
683
684 Emitted each time the timeline modifies QQuickTimeLineValues. Even if multiple
685 QQuickTimeLineValues are changed, this signal is only emitted once for each clock tick.
686*/
687
688void QQuickTimeLine::updateCurrentTime(int v)
689{
690 if (d->syncAdj == -1)
691 d->syncAdj = v;
692 v -= d->syncAdj;
693
694 int timeChanged = v - d->prevTime;
695#if 0
696 if (!timeChanged)
697 return;
698#endif
699 d->prevTime = v;
700 d->advance(timeChanged);
701 emit updated();
702
703 // Do we need to stop the clock?
704 if (d->ops.isEmpty()) {
705 stop();
706 d->prevTime = 0;
707 d->clockRunning = false;
708 emit completed();
709 } /*else if (pauseTime > 0) {
710 GfxClock::cancelClock();
711 d->prevTime = 0;
712 GfxClock::pauseFor(pauseTime);
713 d->syncAdj = 0;
714 d->clockRunning = false;
715 }*/ else if (/*!GfxClock::isActive()*/ state() != Running) {
716 stop();
717 d->prevTime = 0;
718 d->clockRunning = true;
719 d->syncAdj = 0;
720 start();
721 }
722}
723
724void QQuickTimeLine::debugAnimation(QDebug d) const
725{
726 d << "QuickTimeLine(" << hex << (const void *) this << dec << ")";
727}
728
729bool operator<(const QPair<int, Update> &lhs,
730 const QPair<int, Update> &rhs)
731{
732 return lhs.first < rhs.first;
733}
734
735int QQuickTimeLinePrivate::advance(int t)
736{
737 int pauseTime = -1;
738
739 // XXX - surely there is a more efficient way?
740 do {
741 pauseTime = -1;
742 // Minimal advance time
743 int advanceTime = t;
744 for (Ops::const_iterator iter = ops.constBegin(), cend = ops.constEnd(); iter != cend; ++iter) {
745 const TimeLine &tl = *iter;
746 const Op &op = tl.ops.first();
747 int length = op.length - tl.consumedOpLength;
748
749 if (length < advanceTime) {
750 advanceTime = length;
751 if (advanceTime == 0)
752 break;
753 }
754 }
755 t -= advanceTime;
756
757 // Process until then. A zero length advance time will only process
758 // sets.
759 QList<QPair<int, Update> > updates;
760
761 for (Ops::Iterator iter = ops.begin(); iter != ops.end(); ) {
762 QQuickTimeLineValue *v = static_cast<QQuickTimeLineValue *>(iter.key());
763 TimeLine &tl = *iter;
764 Q_ASSERT(!tl.ops.isEmpty());
765
766 do {
767 Op &op = tl.ops.first();
768 if (advanceTime == 0 && op.length != 0)
769 continue;
770
771 if (tl.consumedOpLength == 0 &&
772 op.type != Op::Pause &&
773 op.type != Op::Execute)
774 tl.base = v->value();
775
776 if ((tl.consumedOpLength + advanceTime) == op.length) {
777 if (op.type == Op::Execute) {
778 updates << qMakePair(op.order, Update(op.event));
779 } else {
780 bool changed = false;
781 qreal val = value(op, op.length, tl.base, &changed);
782 if (changed)
783 updates << qMakePair(op.order, Update(v, val));
784 }
785 tl.length -= qMin(advanceTime, tl.length);
786 tl.consumedOpLength = 0;
787 tl.ops.removeFirst();
788 } else {
789 tl.consumedOpLength += advanceTime;
790 bool changed = false;
791 qreal val = value(op, tl.consumedOpLength, tl.base, &changed);
792 if (changed)
793 updates << qMakePair(op.order, Update(v, val));
794 tl.length -= qMin(advanceTime, tl.length);
795 break;
796 }
797
798 } while(!tl.ops.isEmpty() && advanceTime == 0 && tl.ops.first().length == 0);
799
800
801 if (tl.ops.isEmpty()) {
802 iter = ops.erase(iter);
803 v->_t = nullptr;
804 } else {
805 if (tl.ops.first().type == Op::Pause && pauseTime != 0) {
806 int opPauseTime = tl.ops.first().length - tl.consumedOpLength;
807 if (pauseTime == -1 || opPauseTime < pauseTime)
808 pauseTime = opPauseTime;
809 } else {
810 pauseTime = 0;
811 }
812 ++iter;
813 }
814 }
815
816 length -= qMin(length, advanceTime);
817 syncPoint -= advanceTime;
818
819 std::sort(updates.begin(), updates.end());
820 updateQueue = &updates;
821 for (int ii = 0; ii < updates.count(); ++ii) {
822 const Update &v = updates.at(ii).second;
823 if (v.g) {
824 v.g->setValue(v.v);
825 } else {
826 v.e.d0(v.e.d1);
827 }
828 }
829 updateQueue = nullptr;
830 } while(t);
831
832 return pauseTime;
833}
834
835void QQuickTimeLine::remove(QQuickTimeLineObject *v)
836{
837 QQuickTimeLinePrivate::Ops::Iterator iter = d->ops.find(v);
838 Q_ASSERT(iter != d->ops.end());
839
840 int len = iter->length;
841 d->ops.erase(iter);
842 if (len == d->length) {
843 // We need to recalculate the length
844 d->length = 0;
845 for (QQuickTimeLinePrivate::Ops::Iterator iter = d->ops.begin();
846 iter != d->ops.end();
847 ++iter) {
848
849 if (iter->length > d->length)
850 d->length = iter->length;
851
852 }
853 }
854 if (d->ops.isEmpty()) {
855 stop();
856 d->clockRunning = false;
857 } else if (state() != Running) { // was !GfxClock::isActive()
858 stop();
859 d->prevTime = 0;
860 d->clockRunning = true;
861
862 if (d->syncMode == QQuickTimeLine::LocalSync) {
863 d->syncAdj = -1;
864 } else {
865 d->syncAdj = 0;
866 }
867 start();
868 }
869
870 if (d->updateQueue) {
871 for (int ii = 0; ii < d->updateQueue->count(); ++ii) {
872 if (d->updateQueue->at(ii).second.g == v ||
873 d->updateQueue->at(ii).second.e.callbackObject() == v) {
874 d->updateQueue->removeAt(ii);
875 --ii;
876 }
877 }
878 }
879
880
881}
882
883/*!
884 \internal
885 \class QQuickTimeLineValue
886 \brief The QQuickTimeLineValue class provides a value that can be modified by QQuickTimeLine.
887*/
888
889/*!
890 \fn QQuickTimeLineValue::QQuickTimeLineValue(qreal value = 0)
891
892 Construct a new QQuickTimeLineValue with an initial \a value.
893*/
894
895/*!
896 \fn qreal QQuickTimeLineValue::value() const
897
898 Return the current value.
899*/
900
901/*!
902 \fn void QQuickTimeLineValue::setValue(qreal value)
903
904 Set the current \a value.
905*/
906
907/*!
908 \fn QQuickTimeLine *QQuickTimeLineValue::timeLine() const
909
910 If a QQuickTimeLine is operating on this value, return a pointer to it,
911 otherwise return null.
912*/
913
914
915QQuickTimeLineObject::QQuickTimeLineObject()
916: _t(nullptr)
917{
918}
919
920QQuickTimeLineObject::~QQuickTimeLineObject()
921{
922 if (_t) {
923 _t->remove(this);
924 _t = nullptr;
925 }
926}
927
928QQuickTimeLineCallback::QQuickTimeLineCallback()
929: d0(nullptr), d1(nullptr), d2(nullptr)
930{
931}
932
933QQuickTimeLineCallback::QQuickTimeLineCallback(QQuickTimeLineObject *b, Callback f, void *d)
934: d0(f), d1(d), d2(b)
935{
936}
937
938QQuickTimeLineCallback::QQuickTimeLineCallback(const QQuickTimeLineCallback &o)
939: d0(o.d0), d1(o.d1), d2(o.d2)
940{
941}
942
943QQuickTimeLineCallback &QQuickTimeLineCallback::operator=(const QQuickTimeLineCallback &o)
944{
945 d0 = o.d0;
946 d1 = o.d1;
947 d2 = o.d2;
948 return *this;
949}
950
951QQuickTimeLineObject *QQuickTimeLineCallback::callbackObject() const
952{
953 return d2;
954}
955
956QT_END_NAMESPACE
957