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

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