1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtQml module of the Qt Toolkit.
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.
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.
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.
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.
40 ****************************************************************************/
42 #include "qquicktimeline_p_p.h"
47 #include <QWaitCondition>
49 #include <QCoreApplication>
50 #include <QEasingCurve>
56 Update(QQuickTimeLineValue *_g, qreal _v)
58 Update(const QQuickTimeLineCallback &_e)
59 : g(0), v(0), e(_e) {}
61 QQuickTimeLineValue *g;
63 QQuickTimeLineCallback e;
66 struct QQuickTimeLinePrivate
68 QQuickTimeLinePrivate(QQuickTimeLine *);
81 Op(Type t, int l, qreal v, qreal v2, int o,
82 const QQuickTimeLineCallback &ev = QQuickTimeLineCallback(), const QEasingCurve &es = QEasingCurve())
83 : type(t), length(l), value(v), value2(v2), order(o), event(ev),
86 : type(o.type), length(o.length), value(o.value), value2(o.value2),
87 order(o.order), event(o.event), easing(o.easing) {}
88 Op &operator=(const Op &o) {
89 type = o.type; length = o.length; value = o.value;
90 value2 = o.value2; order = o.order; event = o.event;
101 QQuickTimeLineCallback event;
106 TimeLine() : length(0), consumedOpLength(0), base(0.) {}
109 int consumedOpLength;
115 typedef QHash<QQuickTimeLineObject *, TimeLine> Ops;
119 void add(QQuickTimeLineObject &, const Op &);
120 qreal value(const Op &op, int time, qreal base, bool *) const;
129 QQuickTimeLine::SyncMode syncMode;
131 QList<QPair<int, Update> > *updateQueue;
134 QQuickTimeLinePrivate::QQuickTimeLinePrivate(QQuickTimeLine *parent)
135 : length(0), syncPoint(0), q(parent), clockRunning(false), prevTime(0), order(0), syncMode(QQuickTimeLine::LocalSync), syncAdj(0), updateQueue(0)
139 void QQuickTimeLinePrivate::add(QQuickTimeLineObject &g, const Op &o)
141 if (g._t && g._t != q) {
142 qWarning() << "QQuickTimeLine: Cannot modify a QQuickTimeLineValue owned by"
143 << "another timeline.";
148 Ops::Iterator iter = ops.find(&g);
149 if (iter == ops.end()) {
150 iter = ops.insert(&g, TimeLine());
152 q->pause(g, syncPoint);
154 if (!iter->ops.isEmpty() &&
155 o.type == Op::Pause &&
156 iter->ops.last().type == Op::Pause) {
157 iter->ops.last().length += o.length;
158 iter->length += o.length;
161 iter->length += o.length;
164 if (iter->length > length)
165 length = iter->length;
172 if (syncMode == QQuickTimeLine::LocalSync) {
179 if (syncMode == QQuickTimeLine::LocalSync) {
188 qreal QQuickTimeLinePrivate::value(const Op &op, int time, qreal base, bool *changed) const
191 Q_ASSERT(time <= op.length);
203 } else if (time == (op.length)) {
206 qreal delta = op.value - base;
207 qreal pTime = (qreal)(time) / (qreal)op.length;
208 if (op.easing.type() == QEasingCurve::Linear)
209 return base + delta * pTime;
211 return base + delta * op.easing.valueForProgress(pTime);
216 } else if (time == (op.length)) {
217 return base + op.value;
219 qreal delta = op.value;
220 qreal pTime = (qreal)(time) / (qreal)op.length;
221 if (op.easing.type() == QEasingCurve::Linear)
222 return base + delta * pTime;
224 return base + delta * op.easing.valueForProgress(pTime);
230 qreal t = (qreal)(time) / 1000.0f;
231 qreal delta = op.value * t + 0.5f * op.value2 * t * t;
234 case Op::AccelDistance:
237 } else if (time == (op.length)) {
238 return base + op.value2;
240 qreal t = (qreal)(time) / 1000.0f;
241 qreal accel = -1.0f * 1000.0f * op.value / (qreal)op.length;
242 qreal delta = op.value * t + 0.5f * accel * t * t;
247 op.event.d0(op.event.d1);
257 \class QQuickTimeLine
258 \brief The QQuickTimeLine class provides a timeline for controlling animations.
260 QQuickTimeLine is similar to QTimeLine except:
262 \li It updates QQuickTimeLineValue instances directly, rather than maintaining a single
265 For example, the following animates a simple value over 200 milliseconds:
267 QQuickTimeLineValue v(<starting value>);
269 tl.move(v, 100., 200);
273 If your program needs to know when values are changed, it can either
274 connect to the QQuickTimeLine's updated() signal, or inherit from QQuickTimeLineValue
275 and reimplement the QQuickTimeLineValue::setValue() method.
277 \li Supports multiple QQuickTimeLineValue, arbitrary start and end values and allows
278 animations to be strung together for more complex effects.
280 For example, the following animation moves the x and y coordinates of
281 an object from wherever they are to the position (100, 100) in 50
282 milliseconds and then further animates them to (100, 200) in 50
286 QQuickTimeLineValue x(<starting value>);
287 QQuickTimeLineValue y(<starting value>);
292 tl.move(x, 100., 50);
293 tl.move(y, 100., 50);
294 tl.move(y, 200., 50);
297 \li All QQuickTimeLine instances share a single, synchronized clock.
299 Actions scheduled within the same event loop tick are scheduled
300 synchronously against each other, regardless of the wall time between the
301 scheduling. Synchronized scheduling applies both to within the same
302 QQuickTimeLine and across separate QQuickTimeLine's within the same process.
306 Currently easing functions are not supported.
311 Construct a new QQuickTimeLine with the specified \a parent.
313 QQuickTimeLine::QQuickTimeLine(QObject *parent)
316 d = new QQuickTimeLinePrivate(this);
320 Destroys the time line. Any inprogress animations are canceled, but not
323 QQuickTimeLine::~QQuickTimeLine()
325 for (QQuickTimeLinePrivate::Ops::Iterator iter = d->ops.begin();
326 iter != d->ops.end();
334 \enum QQuickTimeLine::SyncMode
338 Return the timeline's synchronization mode.
340 QQuickTimeLine::SyncMode QQuickTimeLine::syncMode() const
346 Set the timeline's synchronization mode to \a syncMode.
348 void QQuickTimeLine::setSyncMode(SyncMode syncMode)
350 d->syncMode = syncMode;
354 Pause \a obj for \a time milliseconds.
356 void QQuickTimeLine::pause(QQuickTimeLineObject &obj, int time)
358 if (time <= 0) return;
359 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::Pause, time, 0., 0., d->order++);
364 Execute the \a event.
366 void QQuickTimeLine::callback(const QQuickTimeLineCallback &callback)
368 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::Execute, 0, 0, 0., d->order++, callback);
369 d->add(*callback.callbackObject(), op);
373 Set the \a value of \a timeLineValue.
375 void QQuickTimeLine::set(QQuickTimeLineValue &timeLineValue, qreal value)
377 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::Set, 0, value, 0., d->order++);
378 d->add(timeLineValue, op);
382 Decelerate \a timeLineValue from the starting \a velocity to zero at the
383 given \a acceleration rate. Although the \a acceleration is technically
384 a deceleration, it should always be positive. The QQuickTimeLine will ensure
385 that the deceleration is in the opposite direction to the initial velocity.
387 int QQuickTimeLine::accel(QQuickTimeLineValue &timeLineValue, qreal velocity, qreal acceleration)
389 if (acceleration == 0.0f)
392 if ((velocity > 0.0f) == (acceleration > 0.0f))
393 acceleration = acceleration * -1.0f;
395 int time = static_cast<int>(-1000 * velocity / acceleration);
397 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++);
398 d->add(timeLineValue, op);
406 Decelerate \a timeLineValue from the starting \a velocity to zero at the
407 given \a acceleration rate over a maximum distance of maxDistance.
409 If necessary, QQuickTimeLine will reduce the acceleration to ensure that the
410 entire operation does not require a move of more than \a maxDistance.
411 \a maxDistance should always be positive.
413 int QQuickTimeLine::accel(QQuickTimeLineValue &timeLineValue, qreal velocity, qreal acceleration, qreal maxDistance)
415 if (maxDistance == 0.0f || acceleration == 0.0f)
418 Q_ASSERT(acceleration > 0.0f && maxDistance > 0.0f);
420 qreal maxAccel = (velocity * velocity) / (2.0f * maxDistance);
421 if (maxAccel > acceleration)
422 acceleration = maxAccel;
424 if ((velocity > 0.0f) == (acceleration > 0.0f))
425 acceleration = acceleration * -1.0f;
427 int time = static_cast<int>(-1000 * velocity / acceleration);
429 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++);
430 d->add(timeLineValue, op);
436 Decelerate \a timeLineValue from the starting \a velocity to zero over the given
437 \a distance. This is like accel(), but the QQuickTimeLine calculates the exact
440 \a distance should be positive.
442 int QQuickTimeLine::accelDistance(QQuickTimeLineValue &timeLineValue, qreal velocity, qreal distance)
444 if (distance == 0.0f || velocity == 0.0f)
447 Q_ASSERT((distance >= 0.0f) == (velocity >= 0.0f));
449 int time = static_cast<int>(1000 * (2.0f * distance) / velocity);
451 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::AccelDistance, time, velocity, distance, d->order++);
452 d->add(timeLineValue, op);
458 Linearly change the \a timeLineValue from its current value to the given
459 \a destination value over \a time milliseconds.
461 void QQuickTimeLine::move(QQuickTimeLineValue &timeLineValue, qreal destination, int time)
463 if (time <= 0) return;
464 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++);
465 d->add(timeLineValue, op);
469 Change the \a timeLineValue from its current value to the given \a destination
470 value over \a time milliseconds using the \a easing curve.
472 void QQuickTimeLine::move(QQuickTimeLineValue &timeLineValue, qreal destination, const QEasingCurve &easing, int time)
474 if (time <= 0) return;
475 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++, QQuickTimeLineCallback(), easing);
476 d->add(timeLineValue, op);
480 Linearly change the \a timeLineValue from its current value by the \a change amount
481 over \a time milliseconds.
483 void QQuickTimeLine::moveBy(QQuickTimeLineValue &timeLineValue, qreal change, int time)
485 if (time <= 0) return;
486 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++);
487 d->add(timeLineValue, op);
491 Change the \a timeLineValue from its current value by the \a change amount over
492 \a time milliseconds using the \a easing curve.
494 void QQuickTimeLine::moveBy(QQuickTimeLineValue &timeLineValue, qreal change, const QEasingCurve &easing, int time)
496 if (time <= 0) return;
497 QQuickTimeLinePrivate::Op op(QQuickTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++, QQuickTimeLineCallback(), easing);
498 d->add(timeLineValue, op);
502 Cancel (but don't complete) all scheduled actions for \a timeLineValue.
504 void QQuickTimeLine::reset(QQuickTimeLineValue &timeLineValue)
506 if (!timeLineValue._t)
508 if (timeLineValue._t != this) {
509 qWarning() << "QQuickTimeLine: Cannot reset a QQuickTimeLineValue owned by another timeline.";
512 remove(&timeLineValue);
513 timeLineValue._t = 0;
516 int QQuickTimeLine::duration() const
522 Synchronize the end point of \a timeLineValue to the endpoint of \a syncTo
523 within this timeline.
525 Following operations on \a timeLineValue in this timeline will be scheduled after
526 all the currently scheduled actions on \a syncTo are complete. In
527 pseudo-code this is equivalent to:
529 QQuickTimeLine::pause(timeLineValue, min(0, length_of(syncTo) - length_of(timeLineValue)))
532 void QQuickTimeLine::sync(QQuickTimeLineValue &timeLineValue, QQuickTimeLineValue &syncTo)
534 QQuickTimeLinePrivate::Ops::Iterator iter = d->ops.find(&syncTo);
535 if (iter == d->ops.end())
537 int length = iter->length;
539 iter = d->ops.find(&timeLineValue);
540 if (iter == d->ops.end()) {
541 pause(timeLineValue, length);
543 int glength = iter->length;
544 pause(timeLineValue, length - glength);
549 Synchronize the end point of \a timeLineValue to the endpoint of the longest
550 action cursrently scheduled in the timeline.
552 In pseudo-code, this is equivalent to:
554 QQuickTimeLine::pause(timeLineValue, length_of(timeline) - length_of(timeLineValue))
557 void QQuickTimeLine::sync(QQuickTimeLineValue &timeLineValue)
559 QQuickTimeLinePrivate::Ops::Iterator iter = d->ops.find(&timeLineValue);
560 if (iter == d->ops.end()) {
561 pause(timeLineValue, d->length);
563 pause(timeLineValue, d->length - iter->length);
568 Synchronize all currently and future scheduled values in this timeline to
569 the longest action currently scheduled.
573 value1->setValue(0.);
574 value2->setValue(0.);
575 value3->setValue(0.);
578 tl.move(value1, 10, 200);
579 tl.move(value2, 10, 100);
581 tl.move(value2, 20, 100);
582 tl.move(value3, 20, 100);
588 \header \li \li 0ms \li 50ms \li 100ms \li 150ms \li 200ms \li 250ms \li 300ms
589 \row \li value1 \li 0 \li 2.5 \li 5.0 \li 7.5 \li 10 \li 10 \li 10
590 \row \li value2 \li 0 \li 5.0 \li 10.0 \li 10.0 \li 10.0 \li 15.0 \li 20.0
591 \row \li value2 \li 0 \li 0 \li 0 \li 0 \li 0 \li 10.0 \li 20.0
595 /*void QQuickTimeLine::sync()
597 for (QQuickTimeLinePrivate::Ops::Iterator iter = d->ops.begin();
598 iter != d->ops.end();
600 pause(*iter.key(), d->length - iter->length);
601 d->syncPoint = d->length;
609 void QQuickTimeLine::setSyncPoint(int sp)
619 int QQuickTimeLine::syncPoint() const
625 Returns true if the timeline is active. An active timeline is one where
626 QQuickTimeLineValue actions are still pending.
628 bool QQuickTimeLine::isActive() const
630 return !d->ops.isEmpty();
634 Completes the timeline. All queued actions are played to completion, and then discarded. For example,
636 QQuickTimeLineValue v(0.);
638 tl.move(v, 100., 1000.);
645 void QQuickTimeLine::complete()
647 d->advance(d->length);
651 Resets the timeline. All queued actions are discarded and QQuickTimeLineValue's retain their current value. For example,
653 QQuickTimeLineValue v(0.);
655 tl.move(v, 100., 1000.);
662 void QQuickTimeLine::clear()
664 for (QQuickTimeLinePrivate::Ops::ConstIterator iter = d->ops.begin(); iter != d->ops.end(); ++iter)
669 //XXX need stop here?
672 int QQuickTimeLine::time() const
678 \fn void QQuickTimeLine::updated()
680 Emitted each time the timeline modifies QQuickTimeLineValues. Even if multiple
681 QQuickTimeLineValues are changed, this signal is only emitted once for each clock tick.
684 void QQuickTimeLine::updateCurrentTime(int v)
686 if (d->syncAdj == -1)
690 int timeChanged = v - d->prevTime;
696 d->advance(timeChanged);
699 // Do we need to stop the clock?
700 if (d->ops.isEmpty()) {
703 d->clockRunning = false;
705 } /*else if (pauseTime > 0) {
706 GfxClock::cancelClock();
708 GfxClock::pauseFor(pauseTime);
710 d->clockRunning = false;
711 }*/ else if (/*!GfxClock::isActive()*/ state() != Running) {
714 d->clockRunning = true;
720 bool operator<(const QPair<int, Update> &lhs,
721 const QPair<int, Update> &rhs)
723 return lhs.first < rhs.first;
726 int QQuickTimeLinePrivate::advance(int t)
730 // XXX - surely there is a more efficient way?
733 // Minimal advance time
735 for (Ops::Iterator iter = ops.begin(); iter != ops.end(); ++iter) {
736 TimeLine &tl = *iter;
737 Op &op = tl.ops.first();
738 int length = op.length - tl.consumedOpLength;
740 if (length < advanceTime) {
741 advanceTime = length;
742 if (advanceTime == 0)
748 // Process until then. A zero length advance time will only process
750 QList<QPair<int, Update> > updates;
752 for (Ops::Iterator iter = ops.begin(); iter != ops.end(); ) {
753 QQuickTimeLineValue *v = static_cast<QQuickTimeLineValue *>(iter.key());
754 TimeLine &tl = *iter;
755 Q_ASSERT(!tl.ops.isEmpty());
758 Op &op = tl.ops.first();
759 if (advanceTime == 0 && op.length != 0)
762 if (tl.consumedOpLength == 0 &&
763 op.type != Op::Pause &&
764 op.type != Op::Execute)
765 tl.base = v->value();
767 if ((tl.consumedOpLength + advanceTime) == op.length) {
768 if (op.type == Op::Execute) {
769 updates << qMakePair(op.order, Update(op.event));
771 bool changed = false;
772 qreal val = value(op, op.length, tl.base, &changed);
774 updates << qMakePair(op.order, Update(v, val));
776 tl.length -= qMin(advanceTime, tl.length);
777 tl.consumedOpLength = 0;
778 tl.ops.removeFirst();
780 tl.consumedOpLength += advanceTime;
781 bool changed = false;
782 qreal val = value(op, tl.consumedOpLength, tl.base, &changed);
784 updates << qMakePair(op.order, Update(v, val));
785 tl.length -= qMin(advanceTime, tl.length);
789 } while(!tl.ops.isEmpty() && advanceTime == 0 && tl.ops.first().length == 0);
792 if (tl.ops.isEmpty()) {
793 iter = ops.erase(iter);
796 if (tl.ops.first().type == Op::Pause && pauseTime != 0) {
797 int opPauseTime = tl.ops.first().length - tl.consumedOpLength;
798 if (pauseTime == -1 || opPauseTime < pauseTime)
799 pauseTime = opPauseTime;
807 length -= qMin(length, advanceTime);
808 syncPoint -= advanceTime;
810 qSort(updates.begin(), updates.end());
811 updateQueue = &updates;
812 for (int ii = 0; ii < updates.count(); ++ii) {
813 const Update &v = updates.at(ii).second;
826 void QQuickTimeLine::remove(QQuickTimeLineObject *v)
828 QQuickTimeLinePrivate::Ops::Iterator iter = d->ops.find(v);
829 Q_ASSERT(iter != d->ops.end());
831 int len = iter->length;
833 if (len == d->length) {
834 // We need to recalculate the length
836 for (QQuickTimeLinePrivate::Ops::Iterator iter = d->ops.begin();
837 iter != d->ops.end();
840 if (iter->length > d->length)
841 d->length = iter->length;
845 if (d->ops.isEmpty()) {
847 d->clockRunning = false;
848 } else if (/*!GfxClock::isActive()*/ state() != Running) {
851 d->clockRunning = true;
853 if (d->syncMode == QQuickTimeLine::LocalSync) {
861 if (d->updateQueue) {
862 for (int ii = 0; ii < d->updateQueue->count(); ++ii) {
863 if (d->updateQueue->at(ii).second.g == v ||
864 d->updateQueue->at(ii).second.e.callbackObject() == v) {
865 d->updateQueue->removeAt(ii);
876 \class QQuickTimeLineValue
877 \brief The QQuickTimeLineValue class provides a value that can be modified by QQuickTimeLine.
881 \fn QQuickTimeLineValue::QQuickTimeLineValue(qreal value = 0)
883 Construct a new QQuickTimeLineValue with an initial \a value.
887 \fn qreal QQuickTimeLineValue::value() const
889 Return the current value.
893 \fn void QQuickTimeLineValue::setValue(qreal value)
895 Set the current \a value.
899 \fn QQuickTimeLine *QQuickTimeLineValue::timeLine() const
901 If a QQuickTimeLine is operating on this value, return a pointer to it,
902 otherwise return null.
906 QQuickTimeLineObject::QQuickTimeLineObject()
911 QQuickTimeLineObject::~QQuickTimeLineObject()
919 QQuickTimeLineCallback::QQuickTimeLineCallback()
920 : d0(0), d1(0), d2(0)
924 QQuickTimeLineCallback::QQuickTimeLineCallback(QQuickTimeLineObject *b, Callback f, void *d)
925 : d0(f), d1(d), d2(b)
929 QQuickTimeLineCallback::QQuickTimeLineCallback(const QQuickTimeLineCallback &o)
930 : d0(o.d0), d1(o.d1), d2(o.d2)
934 QQuickTimeLineCallback &QQuickTimeLineCallback::operator=(const QQuickTimeLineCallback &o)
942 QQuickTimeLineObject *QQuickTimeLineCallback::callbackObject() const