mirror of
https://bitbucket.org/smil3y/katie.git
synced 2025-02-27 04:13:08 +00:00
948 lines
28 KiB
C++
948 lines
28 KiB
C++
/****************************************************************************
|
|
**
|
|
** Copyright (C) 2015 The Qt Company Ltd.
|
|
** Contact: http://www.qt.io/licensing/
|
|
**
|
|
** This file is part of the QtDeclarative module of the Qt Toolkit.
|
|
**
|
|
** $QT_BEGIN_LICENSE:LGPL$
|
|
** Commercial License Usage
|
|
** Licensees holding valid commercial Qt licenses may use this file in
|
|
** accordance with the commercial license agreement provided with the
|
|
** Software or, alternatively, in accordance with the terms contained in
|
|
** a written agreement between you and The Qt Company. For licensing terms
|
|
** and conditions see http://www.qt.io/terms-conditions. For further
|
|
** information use the contact form at http://www.qt.io/contact-us.
|
|
**
|
|
** GNU Lesser General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU Lesser
|
|
** General Public License version 2.1 or version 3 as published by the Free
|
|
** Software Foundation and appearing in the file LICENSE.LGPLv21 and
|
|
** LICENSE.LGPLv3 included in the packaging of this file. Please review the
|
|
** following information to ensure the GNU Lesser General Public License
|
|
** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
|
|
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|
**
|
|
** As a special exception, The Qt Company gives you certain additional
|
|
** rights. These rights are described in The Qt Company LGPL Exception
|
|
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|
**
|
|
** GNU General Public License Usage
|
|
** Alternatively, this file may be used under the terms of the GNU
|
|
** General Public License version 3.0 as published by the Free Software
|
|
** Foundation and appearing in the file LICENSE.GPL included in the
|
|
** packaging of this file. Please review the following information to
|
|
** ensure the GNU General Public License version 3.0 requirements will be
|
|
** met: http://www.gnu.org/copyleft/gpl.html.
|
|
**
|
|
** $QT_END_LICENSE$
|
|
**
|
|
****************************************************************************/
|
|
|
|
#include "qdeclarativetimeline_p_p.h"
|
|
|
|
#include <QDebug>
|
|
#include <QMutex>
|
|
#include <QThread>
|
|
#include <QWaitCondition>
|
|
#include <QEvent>
|
|
#include <QCoreApplication>
|
|
#include <QEasingCurve>
|
|
#include <QDateTime>
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
struct Update {
|
|
Update(QDeclarativeTimeLineValue *_g, qreal _v)
|
|
: g(_g), v(_v) {}
|
|
Update(const QDeclarativeTimeLineCallback &_e)
|
|
: g(0), v(0), e(_e) {}
|
|
|
|
QDeclarativeTimeLineValue *g;
|
|
qreal v;
|
|
QDeclarativeTimeLineCallback e;
|
|
};
|
|
|
|
struct QDeclarativeTimeLinePrivate
|
|
{
|
|
QDeclarativeTimeLinePrivate(QDeclarativeTimeLine *);
|
|
|
|
struct Op {
|
|
enum Type {
|
|
Pause,
|
|
Set,
|
|
Move,
|
|
MoveBy,
|
|
Accel,
|
|
AccelDistance,
|
|
Execute
|
|
};
|
|
Op() {}
|
|
Op(Type t, int l, qreal v, qreal v2, int o,
|
|
const QDeclarativeTimeLineCallback &ev = QDeclarativeTimeLineCallback(), const QEasingCurve &es = QEasingCurve())
|
|
: type(t), length(l), value(v), value2(v2), order(o), event(ev),
|
|
easing(es) {}
|
|
Op(const Op &o)
|
|
: type(o.type), length(o.length), value(o.value), value2(o.value2),
|
|
order(o.order), event(o.event), easing(o.easing) {}
|
|
Op &operator=(const Op &o) {
|
|
type = o.type; length = o.length; value = o.value;
|
|
value2 = o.value2; order = o.order; event = o.event;
|
|
easing = o.easing;
|
|
return *this;
|
|
}
|
|
|
|
Type type;
|
|
int length;
|
|
qreal value;
|
|
qreal value2;
|
|
|
|
int order;
|
|
QDeclarativeTimeLineCallback event;
|
|
QEasingCurve easing;
|
|
};
|
|
struct TimeLine
|
|
{
|
|
TimeLine() : length(0), consumedOpLength(0), base(0.) {}
|
|
QList<Op> ops;
|
|
int length;
|
|
int consumedOpLength;
|
|
qreal base;
|
|
};
|
|
|
|
int length;
|
|
int syncPoint;
|
|
typedef QHash<QDeclarativeTimeLineObject *, TimeLine> Ops;
|
|
Ops ops;
|
|
QDeclarativeTimeLine *q;
|
|
|
|
void add(QDeclarativeTimeLineObject &, const Op &);
|
|
qreal value(const Op &op, int time, qreal base, bool *) const;
|
|
|
|
int advance(int);
|
|
|
|
bool clockRunning;
|
|
int prevTime;
|
|
|
|
int order;
|
|
|
|
QDeclarativeTimeLine::SyncMode syncMode;
|
|
int syncAdj;
|
|
QList<QPair<int, Update> > *updateQueue;
|
|
};
|
|
|
|
QDeclarativeTimeLinePrivate::QDeclarativeTimeLinePrivate(QDeclarativeTimeLine *parent)
|
|
: length(0), syncPoint(0), q(parent), clockRunning(false), prevTime(0), order(0), syncMode(QDeclarativeTimeLine::LocalSync), syncAdj(0), updateQueue(0)
|
|
{
|
|
}
|
|
|
|
void QDeclarativeTimeLinePrivate::add(QDeclarativeTimeLineObject &g, const Op &o)
|
|
{
|
|
if (g._t && g._t != q) {
|
|
qWarning() << "QDeclarativeTimeLine: Cannot modify a QDeclarativeTimeLineValue owned by"
|
|
<< "another timeline.";
|
|
return;
|
|
}
|
|
g._t = q;
|
|
|
|
Ops::Iterator iter = ops.find(&g);
|
|
if (iter == ops.end()) {
|
|
iter = ops.insert(&g, TimeLine());
|
|
if (syncPoint > 0)
|
|
q->pause(g, syncPoint);
|
|
}
|
|
if (!iter->ops.isEmpty() &&
|
|
o.type == Op::Pause &&
|
|
iter->ops.last().type == Op::Pause) {
|
|
iter->ops.last().length += o.length;
|
|
iter->length += o.length;
|
|
} else {
|
|
iter->ops.append(o);
|
|
iter->length += o.length;
|
|
}
|
|
|
|
if (iter->length > length)
|
|
length = iter->length;
|
|
|
|
if (!clockRunning) {
|
|
q->stop();
|
|
prevTime = 0;
|
|
clockRunning = true;
|
|
|
|
if (syncMode == QDeclarativeTimeLine::LocalSync) {
|
|
syncAdj = -1;
|
|
} else {
|
|
syncAdj = 0;
|
|
}
|
|
q->start();
|
|
/* q->tick(0);
|
|
if (syncMode == QDeclarativeTimeLine::LocalSync) {
|
|
syncAdj = -1;
|
|
} else {
|
|
syncAdj = 0;
|
|
}
|
|
*/
|
|
}
|
|
}
|
|
|
|
qreal QDeclarativeTimeLinePrivate::value(const Op &op, int time, qreal base, bool *changed) const
|
|
{
|
|
Q_ASSERT(time >= 0);
|
|
Q_ASSERT(time <= op.length);
|
|
*changed = true;
|
|
|
|
switch(op.type) {
|
|
case Op::Pause:
|
|
*changed = false;
|
|
return base;
|
|
case Op::Set:
|
|
return op.value;
|
|
case Op::Move:
|
|
if (time == 0) {
|
|
return base;
|
|
} else if (time == (op.length)) {
|
|
return op.value;
|
|
} else {
|
|
qreal delta = op.value - base;
|
|
qreal pTime = (qreal)(time) / (qreal)op.length;
|
|
if (op.easing.type() == QEasingCurve::Linear)
|
|
return base + delta * pTime;
|
|
else
|
|
return base + delta * op.easing.valueForProgress(pTime);
|
|
}
|
|
case Op::MoveBy:
|
|
if (time == 0) {
|
|
return base;
|
|
} else if (time == (op.length)) {
|
|
return base + op.value;
|
|
} else {
|
|
qreal delta = op.value;
|
|
qreal pTime = (qreal)(time) / (qreal)op.length;
|
|
if (op.easing.type() == QEasingCurve::Linear)
|
|
return base + delta * pTime;
|
|
else
|
|
return base + delta * op.easing.valueForProgress(pTime);
|
|
}
|
|
case Op::Accel:
|
|
if (time == 0) {
|
|
return base;
|
|
} else {
|
|
qreal t = (qreal)(time) / 1000.0f;
|
|
qreal delta = op.value * t + 0.5f * op.value2 * t * t;
|
|
return base + delta;
|
|
}
|
|
case Op::AccelDistance:
|
|
if (time == 0) {
|
|
return base;
|
|
} else if (time == (op.length)) {
|
|
return base + op.value2;
|
|
} else {
|
|
qreal t = (qreal)(time) / 1000.0f;
|
|
qreal accel = -1.0f * 1000.0f * op.value / (qreal)op.length;
|
|
qreal delta = op.value * t + 0.5f * accel * t * t;
|
|
return base + delta;
|
|
|
|
}
|
|
case Op::Execute:
|
|
op.event.d0(op.event.d1);
|
|
*changed = false;
|
|
return -1;
|
|
}
|
|
|
|
return base;
|
|
}
|
|
|
|
/*!
|
|
\internal
|
|
\class QDeclarativeTimeLine
|
|
\brief The QDeclarativeTimeLine class provides a timeline for controlling animations.
|
|
|
|
QDeclarativeTimeLine is similar to QTimeLine except:
|
|
\list
|
|
\i It updates QDeclarativeTimeLineValue instances directly, rather than maintaining a single
|
|
current value.
|
|
|
|
For example, the following animates a simple value over 200 milliseconds:
|
|
\code
|
|
QDeclarativeTimeLineValue v(<starting value>);
|
|
QDeclarativeTimeLine tl;
|
|
tl.move(v, 100., 200);
|
|
tl.start()
|
|
\endcode
|
|
|
|
If your program needs to know when values are changed, it can either
|
|
connect to the QDeclarativeTimeLine's updated() signal, or inherit from QDeclarativeTimeLineValue
|
|
and reimplement the QDeclarativeTimeLineValue::setValue() method.
|
|
|
|
\i Supports multiple QDeclarativeTimeLineValue, arbitrary start and end values and allows
|
|
animations to be strung together for more complex effects.
|
|
|
|
For example, the following animation moves the x and y coordinates of
|
|
an object from wherever they are to the position (100, 100) in 50
|
|
milliseconds and then further animates them to (100, 200) in 50
|
|
milliseconds:
|
|
|
|
\code
|
|
QDeclarativeTimeLineValue x(<starting value>);
|
|
QDeclarativeTimeLineValue y(<starting value>);
|
|
|
|
QDeclarativeTimeLine tl;
|
|
tl.start();
|
|
|
|
tl.move(x, 100., 50);
|
|
tl.move(y, 100., 50);
|
|
tl.move(y, 200., 50);
|
|
\endcode
|
|
|
|
\i All QDeclarativeTimeLine instances share a single, synchronized clock.
|
|
|
|
Actions scheduled within the same event loop tick are scheduled
|
|
synchronously against each other, regardless of the wall time between the
|
|
scheduling. Synchronized scheduling applies both to within the same
|
|
QDeclarativeTimeLine and across separate QDeclarativeTimeLine's within the same process.
|
|
|
|
\endlist
|
|
|
|
Currently easing functions are not supported.
|
|
*/
|
|
|
|
|
|
/*!
|
|
Construct a new QDeclarativeTimeLine with the specified \a parent.
|
|
*/
|
|
QDeclarativeTimeLine::QDeclarativeTimeLine(QObject *parent)
|
|
: QAbstractAnimation(parent)
|
|
{
|
|
d = new QDeclarativeTimeLinePrivate(this);
|
|
}
|
|
|
|
/*!
|
|
Destroys the time line. Any inprogress animations are canceled, but not
|
|
completed.
|
|
*/
|
|
QDeclarativeTimeLine::~QDeclarativeTimeLine()
|
|
{
|
|
for (QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.begin();
|
|
iter != d->ops.end();
|
|
++iter)
|
|
iter.key()->_t = 0;
|
|
|
|
delete d; d = 0;
|
|
}
|
|
|
|
/*!
|
|
\enum QDeclarativeTimeLine::SyncMode
|
|
*/
|
|
|
|
/*!
|
|
Return the timeline's synchronization mode.
|
|
*/
|
|
QDeclarativeTimeLine::SyncMode QDeclarativeTimeLine::syncMode() const
|
|
{
|
|
return d->syncMode;
|
|
}
|
|
|
|
/*!
|
|
Set the timeline's synchronization mode to \a syncMode.
|
|
*/
|
|
void QDeclarativeTimeLine::setSyncMode(SyncMode syncMode)
|
|
{
|
|
d->syncMode = syncMode;
|
|
}
|
|
|
|
/*!
|
|
Pause \a obj for \a time milliseconds.
|
|
*/
|
|
void QDeclarativeTimeLine::pause(QDeclarativeTimeLineObject &obj, int time)
|
|
{
|
|
if (time <= 0) return;
|
|
QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Pause, time, 0., 0., d->order++);
|
|
d->add(obj, op);
|
|
}
|
|
|
|
/*!
|
|
Execute the \a event.
|
|
*/
|
|
void QDeclarativeTimeLine::callback(const QDeclarativeTimeLineCallback &callback)
|
|
{
|
|
QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Execute, 0, 0, 0., d->order++, callback);
|
|
d->add(*callback.callbackObject(), op);
|
|
}
|
|
|
|
/*!
|
|
Set the \a value of \a timeLineValue.
|
|
*/
|
|
void QDeclarativeTimeLine::set(QDeclarativeTimeLineValue &timeLineValue, qreal value)
|
|
{
|
|
QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Set, 0, value, 0., d->order++);
|
|
d->add(timeLineValue, op);
|
|
}
|
|
|
|
/*!
|
|
Decelerate \a timeLineValue from the starting \a velocity to zero at the
|
|
given \a acceleration rate. Although the \a acceleration is technically
|
|
a deceleration, it should always be positive. The QDeclarativeTimeLine will ensure
|
|
that the deceleration is in the opposite direction to the initial velocity.
|
|
*/
|
|
int QDeclarativeTimeLine::accel(QDeclarativeTimeLineValue &timeLineValue, qreal velocity, qreal acceleration)
|
|
{
|
|
if (acceleration == 0.0f)
|
|
return -1;
|
|
|
|
if ((velocity > 0.0f) == (acceleration > 0.0f))
|
|
acceleration = acceleration * -1.0f;
|
|
|
|
int time = static_cast<int>(-1000 * velocity / acceleration);
|
|
|
|
QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++);
|
|
d->add(timeLineValue, op);
|
|
|
|
return time;
|
|
}
|
|
|
|
/*!
|
|
\overload
|
|
|
|
Decelerate \a timeLineValue from the starting \a velocity to zero at the
|
|
given \a acceleration rate over a maximum distance of maxDistance.
|
|
|
|
If necessary, QDeclarativeTimeLine will reduce the acceleration to ensure that the
|
|
entire operation does not require a move of more than \a maxDistance.
|
|
\a maxDistance should always be positive.
|
|
*/
|
|
int QDeclarativeTimeLine::accel(QDeclarativeTimeLineValue &timeLineValue, qreal velocity, qreal acceleration, qreal maxDistance)
|
|
{
|
|
if (maxDistance == 0.0f || acceleration == 0.0f)
|
|
return -1;
|
|
|
|
Q_ASSERT(acceleration > 0.0f && maxDistance > 0.0f);
|
|
|
|
qreal maxAccel = (velocity * velocity) / (2.0f * maxDistance);
|
|
if (maxAccel > acceleration)
|
|
acceleration = maxAccel;
|
|
|
|
if ((velocity > 0.0f) == (acceleration > 0.0f))
|
|
acceleration = acceleration * -1.0f;
|
|
|
|
int time = static_cast<int>(-1000 * velocity / acceleration);
|
|
|
|
QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Accel, time, velocity, acceleration, d->order++);
|
|
d->add(timeLineValue, op);
|
|
|
|
return time;
|
|
}
|
|
|
|
/*!
|
|
Decelerate \a timeLineValue from the starting \a velocity to zero over the given
|
|
\a distance. This is like accel(), but the QDeclarativeTimeLine calculates the exact
|
|
deceleration to use.
|
|
|
|
\a distance should be positive.
|
|
*/
|
|
int QDeclarativeTimeLine::accelDistance(QDeclarativeTimeLineValue &timeLineValue, qreal velocity, qreal distance)
|
|
{
|
|
if (distance == 0.0f || velocity == 0.0f)
|
|
return -1;
|
|
|
|
Q_ASSERT((distance >= 0.0f) == (velocity >= 0.0f));
|
|
|
|
int time = static_cast<int>(1000 * (2.0f * distance) / velocity);
|
|
|
|
QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::AccelDistance, time, velocity, distance, d->order++);
|
|
d->add(timeLineValue, op);
|
|
|
|
return time;
|
|
}
|
|
|
|
/*!
|
|
Linearly change the \a timeLineValue from its current value to the given
|
|
\a destination value over \a time milliseconds.
|
|
*/
|
|
void QDeclarativeTimeLine::move(QDeclarativeTimeLineValue &timeLineValue, qreal destination, int time)
|
|
{
|
|
if (time <= 0) return;
|
|
QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++);
|
|
d->add(timeLineValue, op);
|
|
}
|
|
|
|
/*!
|
|
Change the \a timeLineValue from its current value to the given \a destination
|
|
value over \a time milliseconds using the \a easing curve.
|
|
*/
|
|
void QDeclarativeTimeLine::move(QDeclarativeTimeLineValue &timeLineValue, qreal destination, const QEasingCurve &easing, int time)
|
|
{
|
|
if (time <= 0) return;
|
|
QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::Move, time, destination, 0.0f, d->order++, QDeclarativeTimeLineCallback(), easing);
|
|
d->add(timeLineValue, op);
|
|
}
|
|
|
|
/*!
|
|
Linearly change the \a timeLineValue from its current value by the \a change amount
|
|
over \a time milliseconds.
|
|
*/
|
|
void QDeclarativeTimeLine::moveBy(QDeclarativeTimeLineValue &timeLineValue, qreal change, int time)
|
|
{
|
|
if (time <= 0) return;
|
|
QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++);
|
|
d->add(timeLineValue, op);
|
|
}
|
|
|
|
/*!
|
|
Change the \a timeLineValue from its current value by the \a change amount over
|
|
\a time milliseconds using the \a easing curve.
|
|
*/
|
|
void QDeclarativeTimeLine::moveBy(QDeclarativeTimeLineValue &timeLineValue, qreal change, const QEasingCurve &easing, int time)
|
|
{
|
|
if (time <= 0) return;
|
|
QDeclarativeTimeLinePrivate::Op op(QDeclarativeTimeLinePrivate::Op::MoveBy, time, change, 0.0f, d->order++, QDeclarativeTimeLineCallback(), easing);
|
|
d->add(timeLineValue, op);
|
|
}
|
|
|
|
/*!
|
|
Cancel (but don't complete) all scheduled actions for \a timeLineValue.
|
|
*/
|
|
void QDeclarativeTimeLine::reset(QDeclarativeTimeLineValue &timeLineValue)
|
|
{
|
|
if (!timeLineValue._t)
|
|
return;
|
|
if (timeLineValue._t != this) {
|
|
qWarning() << "QDeclarativeTimeLine: Cannot reset a QDeclarativeTimeLineValue owned by another timeline.";
|
|
return;
|
|
}
|
|
remove(&timeLineValue);
|
|
timeLineValue._t = 0;
|
|
}
|
|
|
|
int QDeclarativeTimeLine::duration() const
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/*!
|
|
Synchronize the end point of \a timeLineValue to the endpoint of \a syncTo
|
|
within this timeline.
|
|
|
|
Following operations on \a timeLineValue in this timeline will be scheduled after
|
|
all the currently scheduled actions on \a syncTo are complete. In
|
|
pseudo-code this is equivalent to:
|
|
\code
|
|
QDeclarativeTimeLine::pause(timeLineValue, min(0, length_of(syncTo) - length_of(timeLineValue)))
|
|
\endcode
|
|
*/
|
|
void QDeclarativeTimeLine::sync(QDeclarativeTimeLineValue &timeLineValue, QDeclarativeTimeLineValue &syncTo)
|
|
{
|
|
QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.find(&syncTo);
|
|
if (iter == d->ops.end())
|
|
return;
|
|
int length = iter->length;
|
|
|
|
iter = d->ops.find(&timeLineValue);
|
|
if (iter == d->ops.end()) {
|
|
pause(timeLineValue, length);
|
|
} else {
|
|
int glength = iter->length;
|
|
pause(timeLineValue, length - glength);
|
|
}
|
|
}
|
|
|
|
/*!
|
|
Synchronize the end point of \a timeLineValue to the endpoint of the longest
|
|
action cursrently scheduled in the timeline.
|
|
|
|
In pseudo-code, this is equivalent to:
|
|
\code
|
|
QDeclarativeTimeLine::pause(timeLineValue, length_of(timeline) - length_of(timeLineValue))
|
|
\endcode
|
|
*/
|
|
void QDeclarativeTimeLine::sync(QDeclarativeTimeLineValue &timeLineValue)
|
|
{
|
|
QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.find(&timeLineValue);
|
|
if (iter == d->ops.end()) {
|
|
pause(timeLineValue, d->length);
|
|
} else {
|
|
pause(timeLineValue, d->length - iter->length);
|
|
}
|
|
}
|
|
|
|
/*
|
|
Synchronize all currently and future scheduled values in this timeline to
|
|
the longest action currently scheduled.
|
|
|
|
For example:
|
|
\code
|
|
value1->setValue(0.);
|
|
value2->setValue(0.);
|
|
value3->setValue(0.);
|
|
QDeclarativeTimeLine tl;
|
|
...
|
|
tl.move(value1, 10, 200);
|
|
tl.move(value2, 10, 100);
|
|
tl.sync();
|
|
tl.move(value2, 20, 100);
|
|
tl.move(value3, 20, 100);
|
|
\endcode
|
|
|
|
will result in:
|
|
|
|
\table
|
|
\header \o \o 0ms \o 50ms \o 100ms \o 150ms \o 200ms \o 250ms \o 300ms
|
|
\row \o value1 \o 0 \o 2.5 \o 5.0 \o 7.5 \o 10 \o 10 \o 10
|
|
\row \o value2 \o 0 \o 5.0 \o 10.0 \o 10.0 \o 10.0 \o 15.0 \o 20.0
|
|
\row \o value2 \o 0 \o 0 \o 0 \o 0 \o 0 \o 10.0 \o 20.0
|
|
\endtable
|
|
*/
|
|
|
|
/*void QDeclarativeTimeLine::sync()
|
|
{
|
|
for (QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.begin();
|
|
iter != d->ops.end();
|
|
++iter)
|
|
pause(*iter.key(), d->length - iter->length);
|
|
d->syncPoint = d->length;
|
|
}*/
|
|
|
|
/*!
|
|
\internal
|
|
|
|
Temporary hack.
|
|
*/
|
|
void QDeclarativeTimeLine::setSyncPoint(int sp)
|
|
{
|
|
d->syncPoint = sp;
|
|
}
|
|
|
|
/*!
|
|
\internal
|
|
|
|
Temporary hack.
|
|
*/
|
|
int QDeclarativeTimeLine::syncPoint() const
|
|
{
|
|
return d->syncPoint;
|
|
}
|
|
|
|
/*!
|
|
Returns true if the timeline is active. An active timeline is one where
|
|
QDeclarativeTimeLineValue actions are still pending.
|
|
*/
|
|
bool QDeclarativeTimeLine::isActive() const
|
|
{
|
|
return !d->ops.isEmpty();
|
|
}
|
|
|
|
/*!
|
|
Completes the timeline. All queued actions are played to completion, and then discarded. For example,
|
|
\code
|
|
QDeclarativeTimeLineValue v(0.);
|
|
QDeclarativeTimeLine tl;
|
|
tl.move(v, 100., 1000.);
|
|
// 500 ms passes
|
|
// v.value() == 50.
|
|
tl.complete();
|
|
// v.value() == 100.
|
|
\endcode
|
|
*/
|
|
void QDeclarativeTimeLine::complete()
|
|
{
|
|
d->advance(d->length);
|
|
}
|
|
|
|
/*!
|
|
Resets the timeline. All queued actions are discarded and QDeclarativeTimeLineValue's retain their current value. For example,
|
|
\code
|
|
QDeclarativeTimeLineValue v(0.);
|
|
QDeclarativeTimeLine tl;
|
|
tl.move(v, 100., 1000.);
|
|
// 500 ms passes
|
|
// v.value() == 50.
|
|
tl.clear();
|
|
// v.value() == 50.
|
|
\endcode
|
|
*/
|
|
void QDeclarativeTimeLine::clear()
|
|
{
|
|
for (QDeclarativeTimeLinePrivate::Ops::ConstIterator iter = d->ops.begin(); iter != d->ops.end(); ++iter)
|
|
iter.key()->_t = 0;
|
|
d->ops.clear();
|
|
d->length = 0;
|
|
d->syncPoint = 0;
|
|
//XXX need stop here?
|
|
}
|
|
|
|
int QDeclarativeTimeLine::time() const
|
|
{
|
|
return d->prevTime;
|
|
}
|
|
|
|
/*!
|
|
\fn void QDeclarativeTimeLine::updated()
|
|
|
|
Emitted each time the timeline modifies QDeclarativeTimeLineValues. Even if multiple
|
|
QDeclarativeTimeLineValues are changed, this signal is only emitted once for each clock tick.
|
|
*/
|
|
|
|
void QDeclarativeTimeLine::updateCurrentTime(int v)
|
|
{
|
|
if (d->syncAdj == -1)
|
|
d->syncAdj = v;
|
|
v -= d->syncAdj;
|
|
|
|
int timeChanged = v - d->prevTime;
|
|
#if 0
|
|
if (!timeChanged)
|
|
return;
|
|
#endif
|
|
d->prevTime = v;
|
|
d->advance(timeChanged);
|
|
emit updated();
|
|
|
|
// Do we need to stop the clock?
|
|
if (d->ops.isEmpty()) {
|
|
stop();
|
|
d->prevTime = 0;
|
|
d->clockRunning = false;
|
|
emit completed();
|
|
} /*else if (pauseTime > 0) {
|
|
GfxClock::cancelClock();
|
|
d->prevTime = 0;
|
|
GfxClock::pauseFor(pauseTime);
|
|
d->syncAdj = 0;
|
|
d->clockRunning = false;
|
|
}*/ else if (/*!GfxClock::isActive()*/ state() != Running) {
|
|
stop();
|
|
d->prevTime = 0;
|
|
d->clockRunning = true;
|
|
d->syncAdj = 0;
|
|
start();
|
|
}
|
|
}
|
|
|
|
bool operator<(const QPair<int, Update> &lhs,
|
|
const QPair<int, Update> &rhs)
|
|
{
|
|
return lhs.first < rhs.first;
|
|
}
|
|
|
|
int QDeclarativeTimeLinePrivate::advance(int t)
|
|
{
|
|
int pauseTime = -1;
|
|
|
|
// XXX - surely there is a more efficient way?
|
|
do {
|
|
pauseTime = -1;
|
|
// Minimal advance time
|
|
int advanceTime = t;
|
|
for (Ops::Iterator iter = ops.begin(); iter != ops.end(); ++iter) {
|
|
TimeLine &tl = *iter;
|
|
Op &op = tl.ops.first();
|
|
int length = op.length - tl.consumedOpLength;
|
|
|
|
if (length < advanceTime) {
|
|
advanceTime = length;
|
|
if (advanceTime == 0)
|
|
break;
|
|
}
|
|
}
|
|
t -= advanceTime;
|
|
|
|
// Process until then. A zero length advance time will only process
|
|
// sets.
|
|
QList<QPair<int, Update> > updates;
|
|
|
|
for (Ops::Iterator iter = ops.begin(); iter != ops.end(); ) {
|
|
QDeclarativeTimeLineValue *v = static_cast<QDeclarativeTimeLineValue *>(iter.key());
|
|
TimeLine &tl = *iter;
|
|
Q_ASSERT(!tl.ops.isEmpty());
|
|
|
|
do {
|
|
Op &op = tl.ops.first();
|
|
if (advanceTime == 0 && op.length != 0)
|
|
continue;
|
|
|
|
if (tl.consumedOpLength == 0 &&
|
|
op.type != Op::Pause &&
|
|
op.type != Op::Execute)
|
|
tl.base = v->value();
|
|
|
|
if ((tl.consumedOpLength + advanceTime) == op.length) {
|
|
if (op.type == Op::Execute) {
|
|
updates << qMakePair(op.order, Update(op.event));
|
|
} else {
|
|
bool changed = false;
|
|
qreal val = value(op, op.length, tl.base, &changed);
|
|
if (changed)
|
|
updates << qMakePair(op.order, Update(v, val));
|
|
}
|
|
tl.length -= qMin(advanceTime, tl.length);
|
|
tl.consumedOpLength = 0;
|
|
tl.ops.removeFirst();
|
|
} else {
|
|
tl.consumedOpLength += advanceTime;
|
|
bool changed = false;
|
|
qreal val = value(op, tl.consumedOpLength, tl.base, &changed);
|
|
if (changed)
|
|
updates << qMakePair(op.order, Update(v, val));
|
|
tl.length -= qMin(advanceTime, tl.length);
|
|
break;
|
|
}
|
|
|
|
} while(!tl.ops.isEmpty() && advanceTime == 0 && tl.ops.first().length == 0);
|
|
|
|
|
|
if (tl.ops.isEmpty()) {
|
|
iter = ops.erase(iter);
|
|
v->_t = 0;
|
|
} else {
|
|
if (tl.ops.first().type == Op::Pause && pauseTime != 0) {
|
|
int opPauseTime = tl.ops.first().length - tl.consumedOpLength;
|
|
if (pauseTime == -1 || opPauseTime < pauseTime)
|
|
pauseTime = opPauseTime;
|
|
} else {
|
|
pauseTime = 0;
|
|
}
|
|
++iter;
|
|
}
|
|
}
|
|
|
|
length -= qMin(length, advanceTime);
|
|
syncPoint -= advanceTime;
|
|
|
|
qSort(updates.begin(), updates.end());
|
|
updateQueue = &updates;
|
|
for (int ii = 0; ii < updates.count(); ++ii) {
|
|
const Update &v = updates.at(ii).second;
|
|
if (v.g) {
|
|
v.g->setValue(v.v);
|
|
} else {
|
|
v.e.d0(v.e.d1);
|
|
}
|
|
}
|
|
updateQueue = 0;
|
|
} while(t);
|
|
|
|
return pauseTime;
|
|
}
|
|
|
|
void QDeclarativeTimeLine::remove(QDeclarativeTimeLineObject *v)
|
|
{
|
|
QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.find(v);
|
|
Q_ASSERT(iter != d->ops.end());
|
|
|
|
int len = iter->length;
|
|
d->ops.erase(iter);
|
|
if (len == d->length) {
|
|
// We need to recalculate the length
|
|
d->length = 0;
|
|
for (QDeclarativeTimeLinePrivate::Ops::Iterator iter = d->ops.begin();
|
|
iter != d->ops.end();
|
|
++iter) {
|
|
|
|
if (iter->length > d->length)
|
|
d->length = iter->length;
|
|
|
|
}
|
|
}
|
|
if (d->ops.isEmpty()) {
|
|
stop();
|
|
d->clockRunning = false;
|
|
} else if (/*!GfxClock::isActive()*/ state() != Running) {
|
|
stop();
|
|
d->prevTime = 0;
|
|
d->clockRunning = true;
|
|
|
|
if (d->syncMode == QDeclarativeTimeLine::LocalSync) {
|
|
d->syncAdj = -1;
|
|
} else {
|
|
d->syncAdj = 0;
|
|
}
|
|
start();
|
|
}
|
|
|
|
if (d->updateQueue) {
|
|
for (int ii = 0; ii < d->updateQueue->count(); ++ii) {
|
|
if (d->updateQueue->at(ii).second.g == v ||
|
|
d->updateQueue->at(ii).second.e.callbackObject() == v) {
|
|
d->updateQueue->removeAt(ii);
|
|
--ii;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
/*!
|
|
\internal
|
|
\class QDeclarativeTimeLineValue
|
|
\brief The QDeclarativeTimeLineValue class provides a value that can be modified by QDeclarativeTimeLine.
|
|
*/
|
|
|
|
/*!
|
|
\fn QDeclarativeTimeLineValue::QDeclarativeTimeLineValue(qreal value = 0)
|
|
|
|
Construct a new QDeclarativeTimeLineValue with an initial \a value.
|
|
*/
|
|
|
|
/*!
|
|
\fn qreal QDeclarativeTimeLineValue::value() const
|
|
|
|
Return the current value.
|
|
*/
|
|
|
|
/*!
|
|
\fn void QDeclarativeTimeLineValue::setValue(qreal value)
|
|
|
|
Set the current \a value.
|
|
*/
|
|
|
|
/*!
|
|
\fn QDeclarativeTimeLine *QDeclarativeTimeLineValue::timeLine() const
|
|
|
|
If a QDeclarativeTimeLine is operating on this value, return a pointer to it,
|
|
otherwise return null.
|
|
*/
|
|
|
|
|
|
QDeclarativeTimeLineObject::QDeclarativeTimeLineObject()
|
|
: _t(0)
|
|
{
|
|
}
|
|
|
|
QDeclarativeTimeLineObject::~QDeclarativeTimeLineObject()
|
|
{
|
|
if (_t) {
|
|
_t->remove(this);
|
|
_t = 0;
|
|
}
|
|
}
|
|
|
|
QDeclarativeTimeLineCallback::QDeclarativeTimeLineCallback()
|
|
: d0(0), d1(0), d2(0)
|
|
{
|
|
}
|
|
|
|
QDeclarativeTimeLineCallback::QDeclarativeTimeLineCallback(QDeclarativeTimeLineObject *b, Callback f, void *d)
|
|
: d0(f), d1(d), d2(b)
|
|
{
|
|
}
|
|
|
|
QDeclarativeTimeLineCallback::QDeclarativeTimeLineCallback(const QDeclarativeTimeLineCallback &o)
|
|
: d0(o.d0), d1(o.d1), d2(o.d2)
|
|
{
|
|
}
|
|
|
|
QDeclarativeTimeLineCallback &QDeclarativeTimeLineCallback::operator=(const QDeclarativeTimeLineCallback &o)
|
|
{
|
|
d0 = o.d0;
|
|
d1 = o.d1;
|
|
d2 = o.d2;
|
|
return *this;
|
|
}
|
|
|
|
QDeclarativeTimeLineObject *QDeclarativeTimeLineCallback::callbackObject() const
|
|
{
|
|
return d2;
|
|
}
|
|
|
|
QT_END_NAMESPACE
|
|
#include <moc_qdeclarativetimeline_p_p.h>
|