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

source code of qtdeclarative/src/quick/util/qquicktimeline.cpp