--- /dev/null
+INCLUDEPATH += $$PWD
+
+HEADERS += \
+ $$PWD/qabstractanimationjob_p.h \
+ $$PWD/qanimationgroupjob_p.h \
+ $$PWD/qsequentialanimationgroupjob_p.h \
+ $$PWD/qparallelanimationgroupjob_p.h \
+ $$PWD/qpauseanimationjob_p.h
+
+SOURCES += \
+ $$PWD/qabstractanimationjob.cpp \
+ $$PWD/qanimationgroupjob.cpp \
+ $$PWD/qsequentialanimationgroupjob.cpp \
+ $$PWD/qparallelanimationgroupjob.cpp \
+ $$PWD/qpauseanimationjob.cpp
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtCore/qthreadstorage.h>
+
+#include "private/qabstractanimationjob_p.h"
+#include "private/qanimationgroupjob_p.h"
+
+#define DEFAULT_TIMER_INTERVAL 16
+
+QT_BEGIN_NAMESPACE
+
+#ifndef QT_NO_THREAD
+Q_GLOBAL_STATIC(QThreadStorage<QDeclarativeAnimationTimer *>, animationTimer)
+#endif
+
+QDeclarativeAnimationTimer::QDeclarativeAnimationTimer() :
+ QAbstractAnimationTimer(), lastTick(0), lastDelta(0),
+ currentAnimationIdx(0), insideTick(false),
+ startAnimationPending(false), stopTimerPending(false),
+ runningLeafAnimations(0)
+{
+}
+
+QDeclarativeAnimationTimer *QDeclarativeAnimationTimer::instance(bool create)
+{
+ QDeclarativeAnimationTimer *inst;
+#ifndef QT_NO_THREAD
+ if (create && !animationTimer()->hasLocalData()) {
+ inst = new QDeclarativeAnimationTimer;
+ animationTimer()->setLocalData(inst);
+ } else {
+ inst = animationTimer() ? animationTimer()->localData() : 0;
+ }
+#else
+ static QAnimationTimer unifiedTimer;
+ inst = &unifiedTimer;
+#endif
+ return inst;
+}
+
+QDeclarativeAnimationTimer *QDeclarativeAnimationTimer::instance()
+{
+ return instance(true);
+}
+
+void QDeclarativeAnimationTimer::ensureTimerUpdate()
+{
+ QDeclarativeAnimationTimer *inst = QDeclarativeAnimationTimer::instance(false);
+ QUnifiedTimer *instU = QUnifiedTimer::instance(false);
+ if (instU && inst && inst->isPaused)
+ instU->updateAnimationTimers(-1);
+}
+
+void QDeclarativeAnimationTimer::updateAnimationsTime(qint64 delta)
+{
+ //setCurrentTime can get this called again while we're the for loop. At least with pauseAnimations
+ if (insideTick)
+ return;
+
+ lastTick += delta;
+ lastDelta = delta;
+
+ //we make sure we only call update time if the time has actually changed
+ //it might happen in some cases that the time doesn't change because events are delayed
+ //when the CPU load is high
+ if (delta) {
+ insideTick = true;
+ for (currentAnimationIdx = 0; currentAnimationIdx < animations.count(); ++currentAnimationIdx) {
+ QAbstractAnimationJob *animation = animations.at(currentAnimationIdx);
+ int elapsed = animation->m_totalCurrentTime
+ + (animation->direction() == QAbstractAnimationJob::Forward ? delta : -delta);
+ animation->setCurrentTime(elapsed);
+ }
+ insideTick = false;
+ currentAnimationIdx = 0;
+ }
+}
+
+void QDeclarativeAnimationTimer::updateAnimationTimer()
+{
+ QDeclarativeAnimationTimer *inst = QDeclarativeAnimationTimer::instance(false);
+ if (inst)
+ inst->restartAnimationTimer();
+}
+
+void QDeclarativeAnimationTimer::restartAnimationTimer()
+{
+ if (runningLeafAnimations == 0 && !runningPauseAnimations.isEmpty())
+ QUnifiedTimer::pauseAnimationTimer(this, closestPauseAnimationTimeToFinish());
+ else if (isPaused)
+ QUnifiedTimer::resumeAnimationTimer(this);
+ else if (!isRegistered)
+ QUnifiedTimer::startAnimationTimer(this);
+}
+
+void QDeclarativeAnimationTimer::startAnimations()
+{
+ startAnimationPending = false;
+ //force timer to update, which prevents large deltas for our newly added animations
+ if (!animations.isEmpty())
+ QUnifiedTimer::instance()->updateAnimationTimers(-1);
+
+ //we transfer the waiting animations into the "really running" state
+ animations += animationsToStart;
+ animationsToStart.clear();
+ if (!animations.isEmpty())
+ restartAnimationTimer();
+}
+
+void QDeclarativeAnimationTimer::stopTimer()
+{
+ stopTimerPending = false;
+ if (animations.isEmpty()) {
+ QUnifiedTimer::resumeAnimationTimer(this);
+ QUnifiedTimer::stopAnimationTimer(this);
+ // invalidate the start reference time
+ lastTick = 0;
+ lastDelta = 0;
+ }
+}
+
+void QDeclarativeAnimationTimer::registerAnimation(QAbstractAnimationJob *animation, bool isTopLevel)
+{
+ QDeclarativeAnimationTimer *inst = instance(true); //we create the instance if needed
+ inst->registerRunningAnimation(animation);
+ if (isTopLevel) {
+ Q_ASSERT(!animation->m_hasRegisteredTimer);
+ animation->m_hasRegisteredTimer = true;
+ inst->animationsToStart << animation;
+ if (!inst->startAnimationPending) {
+ inst->startAnimationPending = true;
+ QMetaObject::invokeMethod(inst, "startAnimations", Qt::QueuedConnection);
+ }
+ }
+}
+
+void QDeclarativeAnimationTimer::unregisterAnimation(QAbstractAnimationJob *animation)
+{
+ QDeclarativeAnimationTimer *inst = QDeclarativeAnimationTimer::instance(false);
+ if (inst) {
+ //at this point the unified timer should have been created
+ //but it might also have been already destroyed in case the application is shutting down
+
+ inst->unregisterRunningAnimation(animation);
+
+ if (!animation->m_hasRegisteredTimer)
+ return;
+
+ int idx = inst->animations.indexOf(animation);
+ if (idx != -1) {
+ inst->animations.removeAt(idx);
+ // this is needed if we unregister an animation while its running
+ if (idx <= inst->currentAnimationIdx)
+ --inst->currentAnimationIdx;
+
+ if (inst->animations.isEmpty() && !inst->stopTimerPending) {
+ inst->stopTimerPending = true;
+ QMetaObject::invokeMethod(inst, "stopTimer", Qt::QueuedConnection);
+ }
+ } else {
+ inst->animationsToStart.removeOne(animation);
+ }
+ }
+ animation->m_hasRegisteredTimer = false;
+}
+
+void QDeclarativeAnimationTimer::registerRunningAnimation(QAbstractAnimationJob *animation)
+{
+ if (animation->m_isGroup)
+ return;
+
+ if (animation->m_isPause) {
+ runningPauseAnimations << animation;
+ } else
+ runningLeafAnimations++;
+}
+
+void QDeclarativeAnimationTimer::unregisterRunningAnimation(QAbstractAnimationJob *animation)
+{
+ if (animation->m_isGroup)
+ return;
+
+ if (animation->m_isPause)
+ runningPauseAnimations.removeOne(animation);
+ else
+ runningLeafAnimations--;
+ Q_ASSERT(runningLeafAnimations >= 0);
+}
+
+int QDeclarativeAnimationTimer::closestPauseAnimationTimeToFinish()
+{
+ int closestTimeToFinish = INT_MAX;
+ for (int i = 0; i < runningPauseAnimations.size(); ++i) {
+ QAbstractAnimationJob *animation = runningPauseAnimations.at(i);
+ int timeToFinish;
+
+ if (animation->direction() == QAbstractAnimationJob::Forward)
+ timeToFinish = animation->duration() - animation->currentLoopTime();
+ else
+ timeToFinish = animation->currentLoopTime();
+
+ if (timeToFinish < closestTimeToFinish)
+ closestTimeToFinish = timeToFinish;
+ }
+ return closestTimeToFinish;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+QAbstractAnimationJob::QAbstractAnimationJob()
+ : m_isPause(false)
+ , m_isGroup(false)
+ , m_loopCount(1)
+ , m_group(0)
+ , m_direction(QAbstractAnimationJob::Forward)
+ , m_state(QAbstractAnimationJob::Stopped)
+ , m_totalCurrentTime(0)
+ , m_currentTime(0)
+ , m_currentLoop(0)
+ , m_hasRegisteredTimer(false)
+ , m_uncontrolledFinishTime(-1)
+ , m_wasDeleted(0)
+ , m_nextSibling(0)
+ , m_previousSibling(0)
+{
+}
+
+QAbstractAnimationJob::~QAbstractAnimationJob()
+{
+ if (m_wasDeleted)
+ *m_wasDeleted = true;
+
+ //we can't call stop here. Otherwise we get pure virtual calls
+ if (m_state != Stopped) {
+ State oldState = m_state;
+ m_state = Stopped;
+ stateChanged(oldState, m_state);
+ if (oldState == Running)
+ QDeclarativeAnimationTimer::unregisterAnimation(this);
+ }
+
+ if (m_group)
+ m_group->removeAnimation(this);
+}
+
+void QAbstractAnimationJob::setState(QAbstractAnimationJob::State newState)
+{
+ if (m_state == newState)
+ return;
+
+ if (m_loopCount == 0)
+ return;
+
+ State oldState = m_state;
+ int oldCurrentTime = m_currentTime;
+ int oldCurrentLoop = m_currentLoop;
+ Direction oldDirection = m_direction;
+
+ // check if we should Rewind
+ if ((newState == Paused || newState == Running) && oldState == Stopped) {
+ //here we reset the time if needed
+ //we don't call setCurrentTime because this might change the way the animation
+ //behaves: changing the state or changing the current value
+ m_totalCurrentTime = m_currentTime = (m_direction == Forward) ?
+ 0 : (m_loopCount == -1 ? duration() : totalDuration());
+ }
+
+ m_state = newState;
+ //(un)registration of the animation must always happen before calls to
+ //virtual function (updateState) to ensure a correct state of the timer
+ bool isTopLevel = !m_group || m_group->isStopped();
+ if (oldState == Running) {
+ if (newState == Paused && m_hasRegisteredTimer)
+ QDeclarativeAnimationTimer::ensureTimerUpdate();
+ //the animation, is not running any more
+ QDeclarativeAnimationTimer::unregisterAnimation(this);
+ } else if (newState == Running) {
+ QDeclarativeAnimationTimer::registerAnimation(this, isTopLevel);
+ }
+
+ //starting an animation qualifies as a top level loop change
+ if (newState == Running && oldState == Stopped && !m_group)
+ topLevelAnimationLoopChanged();
+
+ bool wasDeleted = false;
+ m_wasDeleted = &wasDeleted;
+ updateState(newState, oldState);
+ if (wasDeleted)
+ return;
+ m_wasDeleted = 0;
+
+ if (newState != m_state) //this is to be safe if updateState changes the state
+ return;
+
+ // Notify state change
+ stateChanged(newState, oldState);
+ if (newState != m_state) //this is to be safe if updateState changes the state
+ return;
+
+ switch (m_state) {
+ case Paused:
+ break;
+ case Running:
+ {
+ // this ensures that the value is updated now that the animation is running
+ if (oldState == Stopped) {
+ if (isTopLevel) {
+ // currentTime needs to be updated if pauseTimer is active
+ QDeclarativeAnimationTimer::ensureTimerUpdate();
+ setCurrentTime(m_totalCurrentTime);
+ }
+ }
+ }
+ break;
+ case Stopped:
+ // Leave running state.
+ int dura = duration();
+
+ if (dura == -1 || m_loopCount < 0
+ || (oldDirection == Forward && (oldCurrentTime * (oldCurrentLoop + 1)) == (dura * m_loopCount))
+ || (oldDirection == Backward && oldCurrentTime == 0)) {
+ finished();
+ }
+ break;
+ }
+}
+
+void QAbstractAnimationJob::setDirection(Direction direction)
+{
+ if (m_direction == direction)
+ return;
+
+ if (m_state == Stopped) {
+ if (m_direction == Backward) {
+ m_currentTime = duration();
+ m_currentLoop = m_loopCount - 1;
+ } else {
+ m_currentTime = 0;
+ m_currentLoop = 0;
+ }
+ }
+
+ // the commands order below is important: first we need to setCurrentTime with the old direction,
+ // then update the direction on this and all children and finally restart the pauseTimer if needed
+ if (m_hasRegisteredTimer)
+ QDeclarativeAnimationTimer::ensureTimerUpdate();
+
+ m_direction = direction;
+ updateDirection(direction);
+
+ if (m_hasRegisteredTimer)
+ // needed to update the timer interval in case of a pause animation
+ QDeclarativeAnimationTimer::updateAnimationTimer();
+}
+
+void QAbstractAnimationJob::setLoopCount(int loopCount)
+{
+ m_loopCount = loopCount;
+}
+
+int QAbstractAnimationJob::totalDuration() const
+{
+ int dura = duration();
+ if (dura <= 0)
+ return dura;
+ int loopcount = loopCount();
+ if (loopcount < 0)
+ return -1;
+ return dura * loopcount;
+}
+
+void QAbstractAnimationJob::setCurrentTime(int msecs)
+{
+ msecs = qMax(msecs, 0);
+ // Calculate new time and loop.
+ int dura = duration();
+ int totalDura = dura <= 0 ? dura : ((m_loopCount < 0) ? -1 : dura * m_loopCount);
+ if (totalDura != -1)
+ msecs = qMin(totalDura, msecs);
+ m_totalCurrentTime = msecs;
+
+ // Update new values.
+ int oldLoop = m_currentLoop;
+ m_currentLoop = ((dura <= 0) ? 0 : (msecs / dura));
+ if (m_currentLoop == m_loopCount) {
+ //we're at the end
+ m_currentTime = qMax(0, dura);
+ m_currentLoop = qMax(0, m_loopCount - 1);
+ } else {
+ if (m_direction == Forward) {
+ m_currentTime = (dura <= 0) ? msecs : (msecs % dura);
+ } else {
+ m_currentTime = (dura <= 0) ? msecs : ((msecs - 1) % dura) + 1;
+ if (m_currentTime == dura)
+ --m_currentLoop;
+ }
+ }
+
+ if (m_currentLoop != oldLoop && !m_group) //### verify Running as well?
+ topLevelAnimationLoopChanged();
+
+ updateCurrentTime(m_currentTime);
+
+ if (m_currentLoop != oldLoop)
+ currentLoopChanged(m_currentLoop);
+
+ // All animations are responsible for stopping the animation when their
+ // own end state is reached; in this case the animation is time driven,
+ // and has reached the end.
+ if ((m_direction == Forward && m_totalCurrentTime == totalDura)
+ || (m_direction == Backward && m_totalCurrentTime == 0)) {
+ stop();
+ }
+}
+
+void QAbstractAnimationJob::start()
+{
+ if (m_state == Running)
+ return;
+ setState(Running);
+}
+
+void QAbstractAnimationJob::stop()
+{
+ if (m_state == Stopped)
+ return;
+ setState(Stopped);
+}
+
+void QAbstractAnimationJob::pause()
+{
+ if (m_state == Stopped) {
+ qWarning("QAbstractAnimationJob::pause: Cannot pause a stopped animation");
+ return;
+ }
+
+ setState(Paused);
+}
+
+void QAbstractAnimationJob::resume()
+{
+ if (m_state != Paused) {
+ qWarning("QAbstractAnimationJob::resume: "
+ "Cannot resume an animation that is not paused");
+ return;
+ }
+ setState(Running);
+}
+
+void QAbstractAnimationJob::updateState(QAbstractAnimationJob::State newState,
+ QAbstractAnimationJob::State oldState)
+{
+ Q_UNUSED(oldState);
+ Q_UNUSED(newState);
+}
+
+void QAbstractAnimationJob::updateDirection(QAbstractAnimationJob::Direction direction)
+{
+ Q_UNUSED(direction);
+}
+
+void QAbstractAnimationJob::finished()
+{
+ //TODO: update this code so it is valid to delete the animation in animationFinished
+ for (int i = 0; i < changeListeners.count(); ++i) {
+ const QAbstractAnimationJob::ChangeListener &change = changeListeners.at(i);
+ if (change.types & QAbstractAnimationJob::Completion)
+ change.listener->animationFinished(this);
+ }
+
+ if (m_group && (duration() == -1 || loopCount() < 0)) {
+ //this is an uncontrolled animation, need to notify the group animation we are finished
+ m_group->uncontrolledAnimationFinished(this);
+ }
+}
+
+void QAbstractAnimationJob::stateChanged(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState)
+{
+ for (int i = 0; i < changeListeners.count(); ++i) {
+ const QAbstractAnimationJob::ChangeListener &change = changeListeners.at(i);
+ if (change.types & QAbstractAnimationJob::StateChange)
+ change.listener->animationStateChanged(this, newState, oldState);
+ }
+}
+
+void QAbstractAnimationJob::currentLoopChanged(int currentLoop)
+{
+ Q_UNUSED(currentLoop);
+ for (int i = 0; i < changeListeners.count(); ++i) {
+ const QAbstractAnimationJob::ChangeListener &change = changeListeners.at(i);
+ if (change.types & QAbstractAnimationJob::CurrentLoop)
+ change.listener->animationCurrentLoopChanged(this);
+ }
+}
+
+void QAbstractAnimationJob::addAnimationChangeListener(QAnimation2ChangeListener *listener, QAbstractAnimationJob::ChangeTypes changes)
+{
+ changeListeners.append(ChangeListener(listener, changes));
+}
+
+void QAbstractAnimationJob::removeAnimationChangeListener(QAnimation2ChangeListener *listener, QAbstractAnimationJob::ChangeTypes changes)
+{
+ changeListeners.removeOne(ChangeListener(listener, changes));
+}
+
+
+QT_END_NAMESPACE
+
+//#include "moc_qabstractanimation2_p.cpp"
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QABSTRACTANIMATIONJOB_P_H
+#define QABSTRACTANIMATIONJOB_P_H
+
+#include <QtCore/QObject>
+#include <QtCore/private/qabstractanimation_p.h>
+#include "private/qpodvector_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QAnimationGroupJob;
+class QAnimation2ChangeListener;
+class Q_DECLARATIVE_EXPORT QAbstractAnimationJob
+{
+ Q_DISABLE_COPY(QAbstractAnimationJob)
+public:
+ enum Direction {
+ Forward,
+ Backward
+ };
+
+ enum State {
+ Stopped,
+ Paused,
+ Running
+ };
+
+ QAbstractAnimationJob();
+ virtual ~QAbstractAnimationJob();
+
+ //definition
+ inline QAnimationGroupJob *group() const {return m_group;}
+
+ inline int loopCount() const {return m_loopCount;}
+ void setLoopCount(int loopCount);
+
+ int totalDuration() const;
+ virtual int duration() const {return 0;}
+
+ inline QAbstractAnimationJob::Direction direction() const {return m_direction;}
+ void setDirection(QAbstractAnimationJob::Direction direction);
+
+ //state
+ inline int currentTime() const {return m_totalCurrentTime;}
+ inline int currentLoopTime() const {return m_currentTime;}
+ inline int currentLoop() const {return m_currentLoop;}
+ inline QAbstractAnimationJob::State state() const {return m_state;}
+ inline bool isRunning() { return m_state == Running; }
+ inline bool isStopped() { return m_state == Stopped; }
+ inline bool isPaused() { return m_state == Paused; }
+
+ void setCurrentTime(int msecs);
+
+ void start();
+ void pause();
+ void resume();
+ void stop();
+
+ enum ChangeType {
+ Completion = 0x01,
+ StateChange = 0x02,
+ CurrentLoop = 0x04
+ };
+ Q_DECLARE_FLAGS(ChangeTypes, ChangeType)
+
+ void addAnimationChangeListener(QAnimation2ChangeListener *listener, QAbstractAnimationJob::ChangeTypes);
+ void removeAnimationChangeListener(QAnimation2ChangeListener *listener, QAbstractAnimationJob::ChangeTypes);
+
+ QAbstractAnimationJob *nextSibling() const { return m_nextSibling; }
+ QAbstractAnimationJob *previousSibling() const { return m_previousSibling; }
+
+protected:
+ virtual void updateCurrentTime(int) {}
+ virtual void updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState);
+ virtual void updateDirection(QAbstractAnimationJob::Direction direction);
+ virtual void topLevelAnimationLoopChanged() {}
+
+ void setState(QAbstractAnimationJob::State state);
+
+ void finished();
+ void stateChanged(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState);
+ void currentLoopChanged(int currentLoop);
+ void directionChanged(QAbstractAnimationJob::Direction);
+
+ //definition
+ bool m_isPause;
+ bool m_isGroup;
+ int m_loopCount;
+ QAnimationGroupJob *m_group;
+ QAbstractAnimationJob::Direction m_direction;
+
+ //state
+ QAbstractAnimationJob::State m_state;
+ int m_totalCurrentTime;
+ int m_currentTime;
+ int m_currentLoop;
+ bool m_hasRegisteredTimer;
+ //records the finish time for an uncontrolled animation (used by animation groups)
+ int m_uncontrolledFinishTime;
+ bool *m_wasDeleted;
+
+ struct ChangeListener {
+ ChangeListener(QAnimation2ChangeListener *l, QAbstractAnimationJob::ChangeTypes t) : listener(l), types(t) {}
+ QAnimation2ChangeListener *listener;
+ QAbstractAnimationJob::ChangeTypes types;
+ bool operator==(const ChangeListener &other) const { return listener == other.listener && types == other.types; }
+ };
+ QPODVector<ChangeListener,4> changeListeners;
+
+ QAbstractAnimationJob *m_nextSibling;
+ QAbstractAnimationJob *m_previousSibling;
+
+ friend class QDeclarativeAnimationTimer;
+ friend class QAnimationGroupJob;
+};
+
+class Q_AUTOTEST_EXPORT QAnimation2ChangeListener
+{
+public:
+ virtual void animationFinished(QAbstractAnimationJob *) {}
+ virtual void animationStateChanged(QAbstractAnimationJob *, QAbstractAnimationJob::State, QAbstractAnimationJob::State) {}
+ virtual void animationCurrentLoopChanged(QAbstractAnimationJob *) {}
+};
+
+class Q_DECLARATIVE_EXPORT QDeclarativeAnimationTimer : public QAbstractAnimationTimer
+{
+ Q_OBJECT
+private:
+ QDeclarativeAnimationTimer();
+
+public:
+ static QDeclarativeAnimationTimer *instance();
+ static QDeclarativeAnimationTimer *instance(bool create);
+
+ static void registerAnimation(QAbstractAnimationJob *animation, bool isTopLevel);
+ static void unregisterAnimation(QAbstractAnimationJob *animation);
+
+ /*
+ this is used for updating the currentTime of all animations in case the pause
+ timer is active or, otherwise, only of the animation passed as parameter.
+ */
+ static void ensureTimerUpdate();
+
+ /*
+ this will evaluate the need of restarting the pause timer in case there is still
+ some pause animations running.
+ */
+ static void updateAnimationTimer();
+
+ void restartAnimationTimer();
+ void updateAnimationsTime(qint64 timeStep);
+
+ int currentDelta() { return lastDelta; }
+
+ //useful for profiling/debugging
+ int runningAnimationCount() { return animations.count(); }
+
+private Q_SLOTS:
+ void startAnimations();
+ void stopTimer();
+
+private:
+ qint64 lastTick;
+ int lastDelta;
+ int currentAnimationIdx;
+ bool insideTick;
+ bool startAnimationPending;
+ bool stopTimerPending;
+
+ QList<QAbstractAnimationJob*> animations, animationsToStart;
+
+ // this is the count of running animations that are not a group neither a pause animation
+ int runningLeafAnimations;
+ QList<QAbstractAnimationJob*> runningPauseAnimations;
+
+ void registerRunningAnimation(QAbstractAnimationJob *animation);
+ void unregisterRunningAnimation(QAbstractAnimationJob *animation);
+
+ int closestPauseAnimationTimeToFinish();
+};
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(QAbstractAnimationJob::ChangeTypes)
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QABSTRACTANIMATIONJOB_P_H
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qanimationgroupjob_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QAnimationGroupJob::QAnimationGroupJob()
+ : QAbstractAnimationJob(), m_firstChild(0), m_lastChild(0)
+{
+ m_isGroup = true;
+}
+
+QAnimationGroupJob::~QAnimationGroupJob()
+{
+ while (firstChild() != 0)
+ delete firstChild();
+}
+
+void QAnimationGroupJob::topLevelAnimationLoopChanged()
+{
+ for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling())
+ animation->topLevelAnimationLoopChanged();
+}
+
+void QAnimationGroupJob::appendAnimation(QAbstractAnimationJob *animation)
+{
+ if (QAnimationGroupJob *oldGroup = animation->m_group)
+ oldGroup->removeAnimation(animation);
+
+ Q_ASSERT(!animation->previousSibling() && !animation->nextSibling());
+
+ if (m_lastChild)
+ m_lastChild->m_nextSibling = animation;
+ else
+ m_firstChild = animation;
+ animation->m_previousSibling = m_lastChild;
+ m_lastChild = animation;
+
+ animation->m_group = this;
+ animationInserted(animation);
+}
+
+void QAnimationGroupJob::prependAnimation(QAbstractAnimationJob *animation)
+{
+ if (QAnimationGroupJob *oldGroup = animation->m_group)
+ oldGroup->removeAnimation(animation);
+
+ Q_ASSERT(!previousSibling() && !nextSibling());
+
+ if (m_firstChild)
+ m_firstChild->m_previousSibling = animation;
+ else
+ m_lastChild = animation;
+ animation->m_nextSibling = m_firstChild;
+ m_firstChild = animation;
+
+ animation->m_group = this;
+ animationInserted(animation);
+}
+
+void QAnimationGroupJob::removeAnimation(QAbstractAnimationJob *animation)
+{
+ Q_ASSERT(animation);
+ Q_ASSERT(animation->m_group == this);
+ QAbstractAnimationJob *prev = animation->previousSibling();
+ QAbstractAnimationJob *next = animation->nextSibling();
+
+ if (prev)
+ prev->m_nextSibling = next;
+ else
+ m_firstChild = next;
+
+ if (next)
+ next->m_previousSibling = prev;
+ else
+ m_lastChild = prev;
+
+ animation->m_previousSibling = 0;
+ animation->m_nextSibling = 0;
+
+ animation->m_group = 0;
+ animationRemoved(animation, prev, next);
+}
+
+void QAnimationGroupJob::clear()
+{
+ //### should this remove and delete, or just remove?
+ while (firstChild() != 0)
+ delete firstChild(); //removeAnimation(firstChild());
+}
+
+void QAnimationGroupJob::resetUncontrolledAnimationsFinishTime()
+{
+ for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) {
+ if (animation->duration() == -1 || animation->loopCount() < 0) {
+ resetUncontrolledAnimationFinishTime(animation);
+ }
+ }
+}
+
+void QAnimationGroupJob::resetUncontrolledAnimationFinishTime(QAbstractAnimationJob *anim)
+{
+ setUncontrolledAnimationFinishTime(anim, -1);
+}
+
+void QAnimationGroupJob::setUncontrolledAnimationFinishTime(QAbstractAnimationJob *anim, int time)
+{
+ anim->m_uncontrolledFinishTime = time;
+}
+
+void QAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimationJob *animation)
+{
+ Q_UNUSED(animation);
+}
+
+void QAnimationGroupJob::animationRemoved(QAbstractAnimationJob* anim, QAbstractAnimationJob* , QAbstractAnimationJob* )
+{
+ resetUncontrolledAnimationFinishTime(anim);
+ if (!firstChild()) {
+ m_currentTime = 0;
+ stop();
+ }
+}
+
+QT_END_NAMESPACE
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QANIMATIONGROUPJOB_P_H
+#define QANIMATIONGROUPJOB_P_H
+
+#include "private/qabstractanimationjob_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class Q_DECLARATIVE_EXPORT QAnimationGroupJob : public QAbstractAnimationJob
+{
+ Q_DISABLE_COPY(QAnimationGroupJob)
+public:
+ QAnimationGroupJob();
+ ~QAnimationGroupJob();
+
+ void appendAnimation(QAbstractAnimationJob *animation);
+ void prependAnimation(QAbstractAnimationJob *animation);
+ void removeAnimation(QAbstractAnimationJob *animation);
+
+ QAbstractAnimationJob *firstChild() const { return m_firstChild; }
+ QAbstractAnimationJob *lastChild() const { return m_lastChild; }
+
+ void clear();
+
+ //called by QAbstractAnimationJob
+ virtual void uncontrolledAnimationFinished(QAbstractAnimationJob *animation);
+protected:
+ void topLevelAnimationLoopChanged();
+
+ virtual void animationInserted(QAbstractAnimationJob*) { }
+ virtual void animationRemoved(QAbstractAnimationJob*, QAbstractAnimationJob*, QAbstractAnimationJob*);
+
+ //TODO: confirm location of these (should any be moved into QAbstractAnimationJob?)
+ void resetUncontrolledAnimationsFinishTime();
+ void resetUncontrolledAnimationFinishTime(QAbstractAnimationJob *anim);
+ int uncontrolledAnimationFinishTime(QAbstractAnimationJob *anim) const { return anim->m_uncontrolledFinishTime; }
+ void setUncontrolledAnimationFinishTime(QAbstractAnimationJob *anim, int time);
+
+private:
+ //definition
+ QAbstractAnimationJob *m_firstChild;
+ QAbstractAnimationJob *m_lastChild;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif //QANIMATIONGROUPJOB_P_H
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qparallelanimationgroupjob_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QParallelAnimationGroupJob::QParallelAnimationGroupJob()
+ : QAnimationGroupJob()
+ , m_previousLoop(0)
+ , m_previousCurrentTime(0)
+{
+}
+
+QParallelAnimationGroupJob::~QParallelAnimationGroupJob()
+{
+}
+
+int QParallelAnimationGroupJob::duration() const
+{
+ int ret = 0;
+
+ for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) {
+ int currentDuration = animation->totalDuration();
+ //this takes care of the case where a parallel animation group has controlled and uncontrolled
+ //animations, and the uncontrolled stop before the controlled
+ if (currentDuration == -1)
+ currentDuration = uncontrolledAnimationFinishTime(animation);
+ if (currentDuration == -1)
+ return -1; // Undetermined length
+
+ ret = qMax(ret, currentDuration);
+ }
+
+ return ret;
+}
+
+void QParallelAnimationGroupJob::updateCurrentTime(int /*currentTime*/)
+{
+ if (!firstChild())
+ return;
+
+ if (m_currentLoop > m_previousLoop) {
+ // simulate completion of the loop
+ int dura = duration();
+ if (dura > 0) {
+ for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) {
+ if (!animation->isStopped())
+ animation->setCurrentTime(dura); // will stop
+ }
+ }
+ } else if (m_currentLoop < m_previousLoop) {
+ // simulate completion of the loop seeking backwards
+ for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) {
+ //we need to make sure the animation is in the right state
+ //and then rewind it
+ applyGroupState(animation);
+ animation->setCurrentTime(0);
+ animation->stop();
+ }
+ }
+
+ // finally move into the actual time of the current loop
+ for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) {
+ const int dura = animation->totalDuration();
+ //if the loopcount is bigger we should always start all animations
+ if (m_currentLoop > m_previousLoop
+ //if we're at the end of the animation, we need to start it if it wasn't already started in this loop
+ //this happens in Backward direction where not all animations are started at the same time
+ || shouldAnimationStart(animation, m_previousCurrentTime > dura /*startIfAtEnd*/)) {
+ applyGroupState(animation);
+ }
+
+ if (animation->state() == state()) {
+ animation->setCurrentTime(m_currentTime);
+ if (dura > 0 && m_currentTime > dura)
+ animation->stop();
+ }
+ }
+ m_previousLoop = m_currentLoop;
+ m_previousCurrentTime = m_currentTime;
+}
+
+void QParallelAnimationGroupJob::updateState(QAbstractAnimationJob::State newState,
+ QAbstractAnimationJob::State oldState)
+{
+ QAnimationGroupJob::updateState(newState, oldState);
+
+ switch (newState) {
+ case Stopped:
+ for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling())
+ animation->stop();
+ break;
+ case Paused:
+ for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling())
+ if (animation->isRunning())
+ animation->pause();
+ break;
+ case Running:
+ for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) {
+ if (oldState == Stopped)
+ animation->stop();
+ resetUncontrolledAnimationFinishTime(animation);
+ animation->setDirection(m_direction);
+ if (shouldAnimationStart(animation, oldState == Stopped))
+ animation->start();
+ }
+ break;
+ }
+}
+
+bool QParallelAnimationGroupJob::shouldAnimationStart(QAbstractAnimationJob *animation, bool startIfAtEnd) const
+{
+ const int dura = animation->totalDuration();
+
+ if (dura == -1)
+ return uncontrolledAnimationFinishTime(animation) == -1;
+
+ if (startIfAtEnd)
+ return m_currentTime <= dura;
+ if (m_direction == Forward)
+ return m_currentTime < dura;
+ else //direction == Backward
+ return m_currentTime && m_currentTime <= dura;
+}
+
+void QParallelAnimationGroupJob::applyGroupState(QAbstractAnimationJob *animation)
+{
+ switch (m_state)
+ {
+ case Running:
+ animation->start();
+ break;
+ case Paused:
+ animation->pause();
+ break;
+ case Stopped:
+ default:
+ break;
+ }
+}
+
+void QParallelAnimationGroupJob::updateDirection(QAbstractAnimationJob::Direction direction)
+{
+ //we need to update the direction of the current animation
+ if (!isStopped()) {
+ for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) {
+ animation->setDirection(direction);
+ }
+ } else {
+ if (direction == Forward) {
+ m_previousLoop = 0;
+ m_previousCurrentTime = 0;
+ } else {
+ // Looping backwards with loopCount == -1 does not really work well...
+ m_previousLoop = (m_loopCount == -1 ? 0 : m_loopCount - 1);
+ m_previousCurrentTime = duration();
+ }
+ }
+}
+
+void QParallelAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimationJob *animation)
+{
+ Q_ASSERT(animation && animation->duration() == -1 || animation->loopCount() < 0);
+ int uncontrolledRunningCount = 0;
+
+ for (QAbstractAnimationJob *child = firstChild(); child; child = child->nextSibling()) {
+ if (child == animation) {
+ setUncontrolledAnimationFinishTime(animation, animation->currentTime());
+ } else if (child->duration() == -1 || child->loopCount() < 0) {
+ if (uncontrolledAnimationFinishTime(child) == -1)
+ ++uncontrolledRunningCount;
+ }
+ }
+
+ if (uncontrolledRunningCount > 0)
+ return;
+
+ int maxDuration = 0;
+ for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling())
+ maxDuration = qMax(maxDuration, animation->totalDuration());
+
+ if (m_currentTime >= maxDuration)
+ stop();
+}
+
+QT_END_NAMESPACE
+
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPARALLELANIMATIONGROUPJOB_P_H
+#define QPARALLELANIMATIONGROUPJOB_P_H
+
+#include "private/qanimationgroupjob_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class Q_DECLARATIVE_EXPORT QParallelAnimationGroupJob : public QAnimationGroupJob
+{
+ Q_DISABLE_COPY(QParallelAnimationGroupJob)
+public:
+ QParallelAnimationGroupJob();
+ ~QParallelAnimationGroupJob();
+
+ int duration() const;
+
+protected:
+ void updateCurrentTime(int currentTime);
+ void updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState);
+ void updateDirection(QAbstractAnimationJob::Direction direction);
+ void uncontrolledAnimationFinished(QAbstractAnimationJob *animation);
+
+private:
+ bool shouldAnimationStart(QAbstractAnimationJob *animation, bool startIfAtEnd) const;
+ void applyGroupState(QAbstractAnimationJob *animation);
+
+ //state
+ int m_previousLoop;
+ int m_previousCurrentTime;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPARALLELANIMATIONGROUPJOB_P_H
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qpauseanimationjob_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QPauseAnimationJob::QPauseAnimationJob(int duration)
+ : QAbstractAnimationJob()
+ , m_duration(duration)
+{
+ m_isPause = true;
+}
+
+QPauseAnimationJob::~QPauseAnimationJob()
+{
+}
+
+int QPauseAnimationJob::duration() const
+{
+ return m_duration;
+}
+
+void QPauseAnimationJob::setDuration(int msecs)
+{
+ m_duration = msecs;
+}
+
+void QPauseAnimationJob::updateCurrentTime(int)
+{
+}
+
+QT_END_NAMESPACE
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QPAUSEANIMATIONJOB_P_H
+#define QPAUSEANIMATIONJOB_P_H
+
+#include <private/qanimationgroupjob_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class Q_DECLARATIVE_EXPORT QPauseAnimationJob : public QAbstractAnimationJob
+{
+ Q_DISABLE_COPY(QPauseAnimationJob)
+public:
+ explicit QPauseAnimationJob(int duration = 250);
+ ~QPauseAnimationJob();
+
+ int duration() const;
+ void setDuration(int msecs);
+
+protected:
+ void updateCurrentTime(int);
+
+private:
+ //definition
+ int m_duration;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif // QPAUSEANIMATIONJOB_P_H
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "private/qsequentialanimationgroupjob_p.h"
+#include "private/qpauseanimationjob_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QSequentialAnimationGroupJob::QSequentialAnimationGroupJob()
+ : QAnimationGroupJob()
+ , m_currentAnimation(0)
+ , m_previousLoop(0)
+{
+}
+
+QSequentialAnimationGroupJob::~QSequentialAnimationGroupJob()
+{
+}
+
+bool QSequentialAnimationGroupJob::atEnd() const
+{
+ // we try to detect if we're at the end of the group
+ //this is true if the following conditions are true:
+ // 1. we're in the last loop
+ // 2. the direction is forward
+ // 3. the current animation is the last one
+ // 4. the current animation has reached its end
+ const int animTotalCurrentTime = m_currentAnimation->currentTime();
+ return (m_currentLoop == m_loopCount - 1
+ && m_direction == Forward
+ && !m_currentAnimation->nextSibling()
+ && animTotalCurrentTime == animationActualTotalDuration(m_currentAnimation));
+}
+
+int QSequentialAnimationGroupJob::animationActualTotalDuration(QAbstractAnimationJob *anim) const
+{
+ int ret = anim->totalDuration();
+ if (ret == -1)
+ ret = uncontrolledAnimationFinishTime(anim); //we can try the actual duration there
+ return ret;
+}
+
+QSequentialAnimationGroupJob::AnimationIndex QSequentialAnimationGroupJob::indexForCurrentTime() const
+{
+ Q_ASSERT(firstChild());
+
+ AnimationIndex ret;
+ QAbstractAnimationJob *anim = 0;
+ int duration = 0;
+
+ for (anim = firstChild(); anim; anim = anim->nextSibling()) {
+ duration = animationActualTotalDuration(anim);
+
+ // 'animation' is the current animation if one of these reasons is true:
+ // 1. it's duration is undefined
+ // 2. it ends after msecs
+ // 3. it is the last animation (this can happen in case there is at least 1 uncontrolled animation)
+ // 4. it ends exactly in msecs and the direction is backwards
+ if (duration == -1 || m_currentTime < (ret.timeOffset + duration)
+ || (m_currentTime == (ret.timeOffset + duration) && m_direction == QAbstractAnimationJob::Backward)) {
+ ret.animation = anim;
+ return ret;
+ }
+
+ if (anim == m_currentAnimation)
+ ret.afterCurrent = true;
+
+ // 'animation' has a non-null defined duration and is not the one at time 'msecs'.
+ ret.timeOffset += duration;
+ }
+
+ // this can only happen when one of those conditions is true:
+ // 1. the duration of the group is undefined and we passed its actual duration
+ // 2. there are only 0-duration animations in the group
+ ret.timeOffset -= duration;
+ ret.animation = lastChild();
+ return ret;
+}
+
+void QSequentialAnimationGroupJob::restart()
+{
+ // restarting the group by making the first/last animation the current one
+ if (m_direction == Forward) {
+ m_previousLoop = 0;
+ if (m_currentAnimation == firstChild())
+ activateCurrentAnimation();
+ else
+ setCurrentAnimation(firstChild());
+ }
+ else { // direction == Backward
+ m_previousLoop = m_loopCount - 1;
+ if (m_currentAnimation == lastChild())
+ activateCurrentAnimation();
+ else
+ setCurrentAnimation(lastChild());
+ }
+}
+
+void QSequentialAnimationGroupJob::advanceForwards(const AnimationIndex &newAnimationIndex)
+{
+ if (m_previousLoop < m_currentLoop) {
+ // we need to fast forward to the end
+ for (QAbstractAnimationJob *anim = m_currentAnimation; anim; anim = anim->nextSibling()) {
+ setCurrentAnimation(anim, true);
+ anim->setCurrentTime(animationActualTotalDuration(anim));
+ }
+ // this will make sure the current animation is reset to the beginning
+ if (firstChild() && !firstChild()->nextSibling()) //count == 1
+ // we need to force activation because setCurrentAnimation will have no effect
+ activateCurrentAnimation();
+ else
+ setCurrentAnimation(firstChild(), true);
+ }
+
+ // and now we need to fast forward from the current position to
+ for (QAbstractAnimationJob *anim = m_currentAnimation; anim && anim != newAnimationIndex.animation; anim = anim->nextSibling()) { //### WRONG,
+ setCurrentAnimation(anim, true);
+ anim->setCurrentTime(animationActualTotalDuration(anim));
+ }
+ // setting the new current animation will happen later
+}
+
+void QSequentialAnimationGroupJob::rewindForwards(const AnimationIndex &newAnimationIndex)
+{
+ if (m_previousLoop > m_currentLoop) {
+ // we need to fast rewind to the beginning
+ for (QAbstractAnimationJob *anim = m_currentAnimation; anim; anim = anim->previousSibling()) {
+ setCurrentAnimation(anim, true);
+ anim->setCurrentTime(0);
+ }
+ // this will make sure the current animation is reset to the end
+ if (lastChild() && !lastChild()->previousSibling()) //count == 1
+ // we need to force activation because setCurrentAnimation will have no effect
+ activateCurrentAnimation();
+ else {
+ setCurrentAnimation(lastChild(), true);
+ }
+ }
+
+ // and now we need to fast rewind from the current position to
+ for (QAbstractAnimationJob *anim = m_currentAnimation; anim && anim != newAnimationIndex.animation; anim = anim->previousSibling()) {
+ setCurrentAnimation(anim, true);
+ anim->setCurrentTime(0);
+ }
+ // setting the new current animation will happen later
+}
+
+int QSequentialAnimationGroupJob::duration() const
+{
+ int ret = 0;
+
+ for (QAbstractAnimationJob *anim = firstChild(); anim; anim = anim->nextSibling()) {
+ const int currentDuration = anim->totalDuration();
+ if (currentDuration == -1)
+ return -1; // Undetermined length
+
+ ret += currentDuration;
+ }
+
+ return ret;
+}
+
+void QSequentialAnimationGroupJob::updateCurrentTime(int currentTime)
+{
+ if (!m_currentAnimation)
+ return;
+
+ const QSequentialAnimationGroupJob::AnimationIndex newAnimationIndex = indexForCurrentTime();
+
+ // newAnimationIndex.index is the new current animation
+ if (m_previousLoop < m_currentLoop
+ || (m_previousLoop == m_currentLoop && m_currentAnimation != newAnimationIndex.animation && newAnimationIndex.afterCurrent)) {
+ // advancing with forward direction is the same as rewinding with backwards direction
+ advanceForwards(newAnimationIndex);
+ } else if (m_previousLoop > m_currentLoop
+ || (m_previousLoop == m_currentLoop && m_currentAnimation != newAnimationIndex.animation && !newAnimationIndex.afterCurrent)) {
+ // rewinding with forward direction is the same as advancing with backwards direction
+ rewindForwards(newAnimationIndex);
+ }
+
+ setCurrentAnimation(newAnimationIndex.animation);
+
+ const int newCurrentTime = currentTime - newAnimationIndex.timeOffset;
+
+ if (m_currentAnimation) {
+ m_currentAnimation->setCurrentTime(newCurrentTime);
+ if (atEnd()) {
+ //we make sure that we don't exceed the duration here
+ m_currentTime += m_currentAnimation->currentTime() - newCurrentTime;
+ stop();
+ }
+ } else {
+ //the only case where currentAnimation could be null
+ //is when all animations have been removed
+ Q_ASSERT(!firstChild());
+ m_currentTime = 0;
+ stop();
+ }
+
+ m_previousLoop = m_currentLoop;
+}
+
+void QSequentialAnimationGroupJob::updateState(QAbstractAnimationJob::State newState,
+ QAbstractAnimationJob::State oldState)
+{
+ QAnimationGroupJob::updateState(newState, oldState);
+
+ if (!m_currentAnimation)
+ return;
+
+ switch (newState) {
+ case Stopped:
+ m_currentAnimation->stop();
+ break;
+ case Paused:
+ if (oldState == m_currentAnimation->state() && oldState == Running)
+ m_currentAnimation->pause();
+ else
+ restart();
+ break;
+ case Running:
+ if (oldState == m_currentAnimation->state() && oldState == Paused)
+ m_currentAnimation->start();
+ else
+ restart();
+ break;
+ }
+}
+
+void QSequentialAnimationGroupJob::updateDirection(QAbstractAnimationJob::Direction direction)
+{
+ // we need to update the direction of the current animation
+ if (!isStopped() && m_currentAnimation)
+ m_currentAnimation->setDirection(direction);
+}
+
+void QSequentialAnimationGroupJob::setCurrentAnimation(QAbstractAnimationJob *anim, bool intermediate)
+{
+ if (!anim) {
+ Q_ASSERT(!firstChild());
+ m_currentAnimation = 0;
+ return;
+ }
+
+ if (anim == m_currentAnimation)
+ return;
+
+ // stop the old current animation
+ if (m_currentAnimation)
+ m_currentAnimation->stop();
+
+ m_currentAnimation = anim;
+
+ activateCurrentAnimation(intermediate);
+}
+
+void QSequentialAnimationGroupJob::activateCurrentAnimation(bool intermediate)
+{
+ if (!m_currentAnimation || isStopped())
+ return;
+
+ m_currentAnimation->stop();
+
+ // we ensure the direction is consistent with the group's direction
+ m_currentAnimation->setDirection(m_direction);
+
+ // reset the finish time of the animation if it is uncontrolled
+ if (m_currentAnimation->totalDuration() == -1)
+ resetUncontrolledAnimationFinishTime(m_currentAnimation);
+
+ m_currentAnimation->start();
+ if (!intermediate && isPaused())
+ m_currentAnimation->pause();
+}
+
+void QSequentialAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimationJob *animation)
+{
+ Q_ASSERT(animation == m_currentAnimation);
+
+ setUncontrolledAnimationFinishTime(m_currentAnimation, m_currentAnimation->currentTime());
+
+ if ((m_direction == Forward && m_currentAnimation == lastChild())
+ || (m_direction == Backward && m_currentAnimation == firstChild())) {
+ // we don't handle looping of a group with undefined duration
+ stop();
+ } else if (m_direction == Forward) {
+ // set the current animation to be the next one
+ setCurrentAnimation(m_currentAnimation->nextSibling());
+ } else {
+ // set the current animation to be the previous one
+ setCurrentAnimation(m_currentAnimation->previousSibling());
+ }
+}
+
+void QSequentialAnimationGroupJob::animationInserted(QAbstractAnimationJob *anim)
+{
+ if (m_currentAnimation == 0)
+ setCurrentAnimation(firstChild()); // initialize the current animation
+
+ if (m_currentAnimation == anim->nextSibling()
+ && m_currentAnimation->currentTime() == 0 && m_currentAnimation->currentLoop() == 0) {
+ //in this case we simply insert the animation before the current one has actually started
+ setCurrentAnimation(anim);
+ }
+
+// TODO
+// if (index < m_currentAnimationIndex || m_currentLoop != 0) {
+// qWarning("QSequentialGroup::insertAnimation only supports to add animations after the current one.");
+// return; //we're not affected because it is added after the current one
+// }
+}
+
+void QSequentialAnimationGroupJob::animationRemoved(QAbstractAnimationJob *anim, QAbstractAnimationJob *prev, QAbstractAnimationJob *next)
+{
+ QAnimationGroupJob::animationRemoved(anim, prev, next);
+
+ Q_ASSERT(m_currentAnimation); // currentAnimation should always be set
+
+ bool removingCurrent = anim == m_currentAnimation;
+ if (removingCurrent) {
+ if (next)
+ setCurrentAnimation(next); //let's try to take the next one
+ else if (prev)
+ setCurrentAnimation(prev);
+ else// case all animations were removed
+ setCurrentAnimation(0);
+ }
+
+ // duration of the previous animations up to the current animation
+ m_currentTime = 0;
+ for (QAbstractAnimationJob *anim = firstChild(); anim; anim = anim->nextSibling()) {
+ if (anim == m_currentAnimation)
+ break;
+ m_currentTime += animationActualTotalDuration(anim);
+
+ }
+
+ if (!removingCurrent) {
+ //the current animation is not the one being removed
+ //so we add its current time to the current time of this group
+ m_currentTime += m_currentAnimation->currentTime();
+ }
+
+ //let's also update the total current time
+ m_totalCurrentTime = m_currentTime + m_loopCount * duration();
+}
+
+QT_END_NAMESPACE
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSEQUENTIALANIMATIONGROUPJOB_P_H
+#define QSEQUENTIALANIMATIONGROUPJOB_P_H
+
+#include <private/qanimationgroupjob_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QPauseAnimationJob;
+class Q_DECLARATIVE_EXPORT QSequentialAnimationGroupJob : public QAnimationGroupJob
+{
+ Q_DISABLE_COPY(QSequentialAnimationGroupJob)
+public:
+ QSequentialAnimationGroupJob();
+ ~QSequentialAnimationGroupJob();
+
+ int duration() const;
+
+ QAbstractAnimationJob *currentAnimation() const { return m_currentAnimation; }
+
+protected:
+ void updateCurrentTime(int);
+ void updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState);
+ void updateDirection(QAbstractAnimationJob::Direction direction);
+ void uncontrolledAnimationFinished(QAbstractAnimationJob *animation);
+
+private:
+ struct AnimationIndex
+ {
+ AnimationIndex() : afterCurrent(false), timeOffset(0), animation(0) {}
+ // AnimationIndex points to the animation at timeOffset, skipping 0 duration animations.
+ // Note that the index semantic is slightly different depending on the direction.
+ bool afterCurrent; //whether animation is before or after m_currentAnimation //TODO: make enum Before/After/Same
+ int timeOffset; // time offset when the animation at index starts.
+ QAbstractAnimationJob *animation; //points to the animation at timeOffset
+ };
+
+ int animationActualTotalDuration(QAbstractAnimationJob *anim) const;
+ AnimationIndex indexForCurrentTime() const;
+
+ void setCurrentAnimation(QAbstractAnimationJob *anim, bool intermediate = false);
+ void activateCurrentAnimation(bool intermediate = false);
+
+ void animationInserted(QAbstractAnimationJob *anim);
+ void animationRemoved(QAbstractAnimationJob *anim,QAbstractAnimationJob*,QAbstractAnimationJob*);
+
+ bool atEnd() const;
+
+ void restart();
+
+ // handle time changes
+ void rewindForwards(const AnimationIndex &newAnimationIndex);
+ void advanceForwards(const AnimationIndex &newAnimationIndex);
+
+ //state
+ QAbstractAnimationJob *m_currentAnimation;
+ int m_previousLoop;
+};
+
+QT_END_NAMESPACE
+
+QT_END_HEADER
+
+#endif //QSEQUENTIALANIMATIONGROUPJOB_P_H
include(util/util.pri)
include(qml/qml.pri)
include(debugger/debugger.pri)
+include(animations/animations.pri)
\ No newline at end of file
#include <QtDeclarative/qdeclarativeinfo.h>
#include <QtCore/qmath.h>
-#include <QtCore/qsequentialanimationgroup.h>
-#include <QtCore/qparallelanimationgroup.h>
+#include "private/qsequentialanimationgroupjob_p.h"
+#include "private/qparallelanimationgroupjob_p.h"
#include <QtGui/qtransform.h>
QT_BEGIN_NAMESPACE
QQuickParentAnimation::QQuickParentAnimation(QObject *parent)
: QDeclarativeAnimationGroup(*(new QQuickParentAnimationPrivate), parent)
{
- Q_D(QQuickParentAnimation);
- d->topLevelGroup = new QSequentialAnimationGroup;
- QDeclarative_setParent_noEvent(d->topLevelGroup, this);
-
- d->startAction = new QActionAnimation;
- QDeclarative_setParent_noEvent(d->startAction, d->topLevelGroup);
- d->topLevelGroup->addAnimation(d->startAction);
-
- d->ag = new QParallelAnimationGroup;
- QDeclarative_setParent_noEvent(d->ag, d->topLevelGroup);
- d->topLevelGroup->addAnimation(d->ag);
-
- d->endAction = new QActionAnimation;
- QDeclarative_setParent_noEvent(d->endAction, d->topLevelGroup);
- d->topLevelGroup->addAnimation(d->endAction);
}
QQuickParentAnimation::~QQuickParentAnimation()
}
}
-void QQuickParentAnimation::transition(QDeclarativeStateActions &actions,
+QAbstractAnimationJob* QQuickParentAnimation::transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction)
{
}
}
+ QSequentialAnimationGroupJob *topLevelGroup = new QSequentialAnimationGroupJob;
+ QActionAnimation *viaAction = d->via ? new QActionAnimation : 0;
+ QActionAnimation *targetAction = new QActionAnimation;
+ //we'll assume the common case by far is to have children, and always create ag
+ QParallelAnimationGroupJob *ag = new QParallelAnimationGroupJob;
+
if (data->actions.count()) {
- if (direction == QDeclarativeAbstractAnimation::Forward) {
- d->startAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped);
- d->endAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped);
+ if (d->via)
+ viaAction->setAnimAction(viaData);
+ targetAction->setAnimAction(data);
+
+ //take care of any child animations
+ bool valid = d->defaultProperty.isValid();
+ QAbstractAnimationJob* anim;
+ for (int ii = 0; ii < d->animations.count(); ++ii) {
+ if (valid)
+ d->animations.at(ii)->setDefaultTarget(d->defaultProperty);
+ anim = d->animations.at(ii)->transition(actions, modified, direction);
+ ag->appendAnimation(anim);
+ }
+
+ //TODO: simplify/clarify logic
+ bool forwards = direction == QDeclarativeAbstractAnimation::Forward;
+ if (forwards) {
+ topLevelGroup->appendAnimation(d->via ? viaAction : targetAction);
+ topLevelGroup->appendAnimation(ag);
+ if (d->via)
+ topLevelGroup->appendAnimation(targetAction);
} else {
- d->endAction->setAnimAction(d->via ? viaData : data, QActionAnimation::DeleteWhenStopped);
- d->startAction->setAnimAction(d->via ? data : 0, QActionAnimation::DeleteWhenStopped);
+ if (d->via)
+ topLevelGroup->appendAnimation(targetAction);
+ topLevelGroup->appendAnimation(ag);
+ topLevelGroup->appendAnimation(d->via ? viaAction : targetAction);
}
} else {
delete data;
delete viaData;
}
- //take care of any child animations
- bool valid = d->defaultProperty.isValid();
- for (int ii = 0; ii < d->animations.count(); ++ii) {
- if (valid)
- d->animations.at(ii)->setDefaultTarget(d->defaultProperty);
- d->animations.at(ii)->transition(actions, modified, direction);
- }
-
-}
-
-QAbstractAnimation *QQuickParentAnimation::qtAnimation()
-{
- Q_D(QQuickParentAnimation);
- return d->topLevelGroup;
+ return initInstance(topLevelGroup);
}
/*!
QQuickAnchorAnimation::QQuickAnchorAnimation(QObject *parent)
: QDeclarativeAbstractAnimation(*(new QQuickAnchorAnimationPrivate), parent)
{
- Q_D(QQuickAnchorAnimation);
- d->va = new QDeclarativeBulkValueAnimator;
- QDeclarative_setParent_noEvent(d->va, this);
}
QQuickAnchorAnimation::~QQuickAnchorAnimation()
{
}
-QAbstractAnimation *QQuickAnchorAnimation::qtAnimation()
-{
- Q_D(QQuickAnchorAnimation);
- return d->va;
-}
-
/*!
\qmlproperty list<Item> QtQuick2::AnchorAnimation::targets
The items to reanchor.
int QQuickAnchorAnimation::duration() const
{
Q_D(const QQuickAnchorAnimation);
- return d->va->duration();
+ return d->duration;
}
void QQuickAnchorAnimation::setDuration(int duration)
}
Q_D(QQuickAnchorAnimation);
- if (d->va->duration() == duration)
+ if (d->duration == duration)
return;
- d->va->setDuration(duration);
+ d->duration = duration;
emit durationChanged(duration);
}
QEasingCurve QQuickAnchorAnimation::easing() const
{
Q_D(const QQuickAnchorAnimation);
- return d->va->easingCurve();
+ return d->easing;
}
void QQuickAnchorAnimation::setEasing(const QEasingCurve &e)
{
Q_D(QQuickAnchorAnimation);
- if (d->va->easingCurve() == e)
+ if (d->easing == e)
return;
- d->va->setEasingCurve(e);
+ d->easing = e;
emit easingChanged(e);
}
-void QQuickAnchorAnimation::transition(QDeclarativeStateActions &actions,
+QAbstractAnimationJob* QQuickAnchorAnimation::transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction)
{
QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater;
data->interpolatorType = QMetaType::QReal;
data->interpolator = d->interpolator;
-
data->reverse = direction == Backward ? true : false;
data->fromSourced = false;
data->fromDefined = false;
}
}
+ QDeclarativeBulkValueAnimator *animator = new QDeclarativeBulkValueAnimator;
if (data->actions.count()) {
- if (!d->rangeIsSet) {
- d->va->setStartValue(qreal(0));
- d->va->setEndValue(qreal(1));
- d->rangeIsSet = true;
- }
- d->va->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped);
- d->va->setFromSourcedValue(&data->fromSourced);
+ animator->setAnimValue(data);
+ animator->setFromSourcedValue(&data->fromSourced);
} else {
delete data;
}
+
+ return initInstance(animator);
}
/*!
QQuickPathAnimation::QQuickPathAnimation(QObject *parent)
: QDeclarativeAbstractAnimation(*(new QQuickPathAnimationPrivate), parent)
{
- Q_D(QQuickPathAnimation);
- d->pa = new QDeclarativeBulkValueAnimator;
- QDeclarative_setParent_noEvent(d->pa, this);
}
QQuickPathAnimation::~QQuickPathAnimation()
{
+ Q_D(QQuickPathAnimation);
+ QHash<QQuickItem*, QQuickPathAnimationAnimator* >::iterator it;
+ for (it = d->activeAnimations.begin(); it != d->activeAnimations.end(); ++it) {
+ it.value()->clearTemplate();
+ }
}
/*!
int QQuickPathAnimation::duration() const
{
Q_D(const QQuickPathAnimation);
- return d->pa->duration();
+ return d->duration;
}
void QQuickPathAnimation::setDuration(int duration)
}
Q_D(QQuickPathAnimation);
- if (d->pa->duration() == duration)
+ if (d->duration == duration)
return;
- d->pa->setDuration(duration);
+ d->duration = duration;
emit durationChanged(duration);
}
QEasingCurve QQuickPathAnimation::easing() const
{
Q_D(const QQuickPathAnimation);
- return d->pa->easingCurve();
+ return d->easingCurve;
}
void QQuickPathAnimation::setEasing(const QEasingCurve &e)
{
Q_D(QQuickPathAnimation);
- if (d->pa->easingCurve() == e)
+ if (d->easingCurve == e)
return;
- d->pa->setEasingCurve(e);
+ d->easingCurve = e;
emit easingChanged(e);
}
emit endRotationChanged(d->endRotation);
}
-
-QAbstractAnimation *QQuickPathAnimation::qtAnimation()
-{
- Q_D(QQuickPathAnimation);
- return d->pa;
-}
-
-void QQuickPathAnimation::transition(QDeclarativeStateActions &actions,
+QAbstractAnimationJob* QQuickPathAnimation::transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction)
{
Q_D(QQuickPathAnimation);
- QQuickPathAnimationUpdater *data = new QQuickPathAnimationUpdater;
+
+ QQuickPathAnimationUpdater prevData;
+ bool havePrevData = false;
+ if (d->activeAnimations.contains(d->target)) {
+ havePrevData = true;
+ prevData = *d->activeAnimations[d->target]->pathUpdater();
+ }
+
+ QList<QQuickItem*> keys = d->activeAnimations.keys();
+ foreach (QQuickItem *item, keys) {
+ QQuickPathAnimationAnimator *anim = d->activeAnimations.value(item);
+ if (anim->state() == QAbstractAnimationJob::Stopped) {
+ anim->clearTemplate();
+ d->activeAnimations.remove(item);
+ }
+ }
+
+ QQuickPathAnimationUpdater *data = new QQuickPathAnimationUpdater();
+ QQuickPathAnimationAnimator *pa = new QQuickPathAnimationAnimator(d);
+
+ d->activeAnimations[d->target] = pa;
data->orientation = d->orientation;
data->anchorPoint = d->anchorPoint;
- data->entryInterval = duration() ? qreal(d->entryDuration) / duration() : qreal(0);
- data->exitInterval = duration() ? qreal(d->exitDuration) / duration() : qreal(0);
+ data->entryInterval = d->duration ? qreal(d->entryDuration) / d->duration : qreal(0);
+ data->exitInterval = d->duration ? qreal(d->exitDuration) / d->duration : qreal(0);
data->endRotation = d->endRotation;
data->reverse = direction == Backward ? true : false;
data->fromSourced = false;
data->target = d->target;
data->path = d->path;
data->path->invalidateSequentialHistory();
- if (!d->rangeIsSet) {
- d->pa->setStartValue(qreal(0));
- d->pa->setEndValue(qreal(1));
- d->rangeIsSet = true;
- }
- /*
- NOTE: The following block relies on the fact that the previous value hasn't
- yet been deleted, and has the same target, etc, which may be a bit fragile.
- */
- if (d->pa->getAnimValue()) {
- QQuickPathAnimationUpdater *prevData = static_cast<QQuickPathAnimationUpdater*>(d->pa->getAnimValue());
+ if (havePrevData) {
// get the original start angle that was used (so we can exactly reverse).
- data->startRotation = prevData->startRotation;
+ data->startRotation = prevData.startRotation;
// treat interruptions specially, otherwise we end up with strange paths
- if ((data->reverse || prevData->reverse) && prevData->currentV > 0 && prevData->currentV < 1) {
- if (!data->fromDefined && !data->toDefined && !prevData->painterPath.isEmpty()) {
- QPointF pathPos = QDeclarativePath::sequentialPointAt(prevData->painterPath, prevData->pathLength, prevData->attributePoints, prevData->prevBez, prevData->currentV);
- if (!prevData->anchorPoint.isNull())
- pathPos -= prevData->anchorPoint;
+ if ((data->reverse || prevData.reverse) && prevData.currentV > 0 && prevData.currentV < 1) {
+ if (!data->fromDefined && !data->toDefined && !prevData.painterPath.isEmpty()) {
+ QPointF pathPos = QDeclarativePath::sequentialPointAt(prevData.painterPath, prevData.pathLength, prevData.attributePoints, prevData.prevBez, prevData.currentV);
+ if (!prevData.anchorPoint.isNull())
+ pathPos -= prevData.anchorPoint;
if (pathPos == data->target->pos()) { //only treat as interruption if we interrupted ourself
- data->painterPath = prevData->painterPath;
+ data->painterPath = prevData.painterPath;
data->toDefined = data->fromDefined = data->fromSourced = true;
data->prevBez.isValid = false;
- data->interruptStart = prevData->currentV;
- data->startRotation = prevData->startRotation;
- data->pathLength = prevData->pathLength;
- data->attributePoints = prevData->attributePoints;
+ data->interruptStart = prevData.currentV;
+ data->startRotation = prevData.startRotation;
+ data->pathLength = prevData.pathLength;
+ data->attributePoints = prevData.attributePoints;
}
}
}
}
- d->pa->setFromSourcedValue(&data->fromSourced);
- d->pa->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped);
+ pa->setFromSourcedValue(&data->fromSourced);
+ pa->setAnimValue(data);
} else {
- d->pa->setFromSourcedValue(0);
- d->pa->setAnimValue(0, QAbstractAnimation::DeleteWhenStopped);
+ pa->setFromSourcedValue(0);
+ pa->setAnimValue(0);
+ delete pa;
delete data;
}
+
+ pa->setDuration(d->duration);
+ pa->setEasingCurve(d->easingCurve);
+ return initInstance(pa);
}
void QQuickPathAnimationUpdater::setValue(qreal v)
}
}
- //### could cache properties rather than reconstructing each time
- QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("x")), currentPos.x(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
- QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("y")), currentPos.y(), QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
+ target->setPos(currentPos);
//adjust angle according to orientation
if (!fixed) {
else if (v > exitStart)
angle = endRotation * (v - exitStart) / exitInterval + angle * (exitInterval - (v - exitStart)) / exitInterval;
}
- QDeclarativePropertyPrivate::write(QDeclarativeProperty(target, QStringLiteral("rotation")), angle, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
+ target->setRotation(angle);
}
/*
}
}
+QQuickPathAnimationAnimator::QQuickPathAnimationAnimator(QQuickPathAnimationPrivate *priv)
+ : animationTemplate(priv)
+{
+}
+
+QQuickPathAnimationAnimator::~QQuickPathAnimationAnimator()
+{
+ if (animationTemplate && pathUpdater()) {
+ QHash<QQuickItem*, QQuickPathAnimationAnimator* >::iterator it =
+ animationTemplate->activeAnimations.find(pathUpdater()->target);
+ if (it != animationTemplate->activeAnimations.end() && it.value() == this)
+ animationTemplate->activeAnimations.erase(it);
+ }
+}
+
QT_END_NAMESPACE
#include <QtQuick/private/qdeclarativeanimation_p.h>
-#include <QtCore/qabstractanimation.h>
-
QT_BEGIN_HEADER
QT_BEGIN_NAMESPACE
class QQuickParentAnimationPrivate;
-class QQuickParentAnimation : public QDeclarativeAnimationGroup
+class Q_QUICK_PRIVATE_EXPORT QQuickParentAnimation : public QDeclarativeAnimationGroup
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickParentAnimation)
void viaChanged();
protected:
- virtual void transition(QDeclarativeStateActions &actions,
+ virtual QAbstractAnimationJob* transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction);
- virtual QAbstractAnimation *qtAnimation();
};
class QQuickAnchorAnimationPrivate;
-class QQuickAnchorAnimation : public QDeclarativeAbstractAnimation
+class Q_QUICK_PRIVATE_EXPORT QQuickAnchorAnimation : public QDeclarativeAbstractAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickAnchorAnimation)
void easingChanged(const QEasingCurve&);
protected:
- virtual void transition(QDeclarativeStateActions &actions,
+ virtual QAbstractAnimationJob* transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction);
- virtual QAbstractAnimation *qtAnimation();
};
class QQuickItem;
class QDeclarativePath;
class QQuickPathAnimationPrivate;
-class Q_AUTOTEST_EXPORT QQuickPathAnimation : public QDeclarativeAbstractAnimation
+class Q_QUICK_PRIVATE_EXPORT QQuickPathAnimation : public QDeclarativeAbstractAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QQuickPathAnimation)
void setEndRotation(qreal);
protected:
- virtual void transition(QDeclarativeStateActions &actions,
+ virtual QAbstractAnimationJob* transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction);
- virtual QAbstractAnimation *qtAnimation();
-
Q_SIGNALS:
void durationChanged(int);
void easingChanged(const QEasingCurve &);
{
Q_DECLARE_PUBLIC(QQuickParentAnimation)
public:
- QQuickParentAnimationPrivate()
- : QDeclarativeAnimationGroupPrivate(), target(0), newParent(0),
- via(0), topLevelGroup(0), startAction(0), endAction(0) {}
+ QQuickParentAnimationPrivate()
+ : QDeclarativeAnimationGroupPrivate(), target(0), newParent(0), via(0) {}
QQuickItem *target;
QQuickItem *newParent;
QQuickItem *via;
- QSequentialAnimationGroup *topLevelGroup;
- QActionAnimation *startAction;
- QActionAnimation *endAction;
-
QPointF computeTransformOrigin(QQuickItem::TransformOrigin origin, qreal width, qreal height) const;
};
{
Q_DECLARE_PUBLIC(QQuickAnchorAnimation)
public:
- QQuickAnchorAnimationPrivate() : rangeIsSet(false), va(0),
- interpolator(QVariantAnimationPrivate::getInterpolator(QMetaType::QReal)) {}
+ QQuickAnchorAnimationPrivate() : interpolator(QVariantAnimationPrivate::getInterpolator(QMetaType::QReal)), duration(250) {}
- bool rangeIsSet;
- QDeclarativeBulkValueAnimator *va;
QVariantAnimation::Interpolator interpolator;
+ int duration;
+ QEasingCurve easing;
QList<QQuickItem*> targets;
};
entryInterval(0), exitInterval(0) {}
~QQuickPathAnimationUpdater() {}
- void setValue(qreal v);
+ void setValue(qreal v);
QDeclarativePath *path;
QDeclarativeNullableValue<qreal> startRotation;
};
+class QQuickPathAnimationPrivate;
+class QQuickPathAnimationAnimator : public QDeclarativeBulkValueAnimator
+{
+public:
+ QQuickPathAnimationAnimator(QQuickPathAnimationPrivate * = 0);
+ ~QQuickPathAnimationAnimator();
+
+ void clearTemplate() { animationTemplate = 0; }
+
+ QQuickPathAnimationUpdater *pathUpdater() { return static_cast<QQuickPathAnimationUpdater*>(getAnimValue()); }
+private:
+ QQuickPathAnimationPrivate *animationTemplate;
+};
+
class QQuickPathAnimationPrivate : public QDeclarativeAbstractAnimationPrivate
{
Q_DECLARE_PUBLIC(QQuickPathAnimation)
public:
QQuickPathAnimationPrivate() : path(0), target(0),
- rangeIsSet(false), orientation(QQuickPathAnimation::Fixed), entryDuration(0), exitDuration(0), pa(0) {}
+ orientation(QQuickPathAnimation::Fixed), entryDuration(0), exitDuration(0), duration(250) {}
QDeclarativePath *path;
QQuickItem *target;
- bool rangeIsSet;
QQuickPathAnimation::Orientation orientation;
QPointF anchorPoint;
qreal entryDuration;
qreal exitDuration;
QDeclarativeNullableValue<qreal> endRotation;
- QDeclarativeBulkValueAnimator *pa;
+ int duration;
+ QEasingCurve easingCurve;
+ QHash<QQuickItem*, QQuickPathAnimationAnimator* > activeAnimations;
};
, snapMode(QQuickGridView::NoSnap)
, highlightXAnimator(0), highlightYAnimator(0)
{}
+ ~QQuickGridViewPrivate()
+ {
+ delete highlightXAnimator;
+ delete highlightYAnimator;
+ }
};
Qt::Orientation QQuickGridViewPrivate::layoutOrientation() const
FxGridItemSG *newHighlight = new FxGridItemSG(item, q, true);
if (autoHighlight)
resetHighlightPosition();
- highlightXAnimator = new QSmoothedAnimation(q);
+ highlightXAnimator = new QSmoothedAnimation;
highlightXAnimator->target = QDeclarativeProperty(item, QLatin1String("x"));
highlightXAnimator->userDuration = highlightMoveDuration;
- highlightYAnimator = new QSmoothedAnimation(q);
+ highlightYAnimator = new QSmoothedAnimation;
highlightYAnimator->target = QDeclarativeProperty(item, QLatin1String("y"));
highlightYAnimator->userDuration = highlightMoveDuration;
, sectionCriteria(0), currentSectionItem(0), nextSectionItem(0)
, overshootDist(0.0), correctFlick(false), inFlickCorrection(false)
{}
+ ~QQuickListViewPrivate() {
+ delete highlightPosAnimator;
+ delete highlightSizeAnimator;
+ }
friend class QQuickViewSection;
};
newHighlight->setPosition(static_cast<FxListItemSG*>(currentItem)->itemPosition());
}
const QLatin1String posProp(orient == QQuickListView::Vertical ? "y" : "x");
- highlightPosAnimator = new QSmoothedAnimation(q);
+ highlightPosAnimator = new QSmoothedAnimation;
highlightPosAnimator->target = QDeclarativeProperty(item, posProp);
highlightPosAnimator->velocity = highlightMoveSpeed;
highlightPosAnimator->userDuration = highlightMoveDuration;
const QLatin1String sizeProp(orient == QQuickListView::Vertical ? "height" : "width");
- highlightSizeAnimator = new QSmoothedAnimation(q);
+ highlightSizeAnimator = new QSmoothedAnimation;
highlightSizeAnimator->velocity = highlightResizeSpeed;
highlightSizeAnimator->userDuration = highlightResizeDuration;
highlightSizeAnimator->target = QDeclarativeProperty(item, sizeProp);
QQuickParticlePainter(parent), m_fade(true), m_delegate(0)
{
setFlag(QQuickItem::ItemHasContents);
- clock = new Clock(this, this);
+ clock = new Clock(this);
clock->start();
}
+QQuickItemParticle::~QQuickItemParticle()
+{
+ delete clock;
+}
void QQuickItemParticle::freeze(QQuickItem* item)
{
Q_PROPERTY(QDeclarativeComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged)
public:
explicit QQuickItemParticle(QQuickItem *parent = 0);
+ ~QQuickItemParticle();
bool fade() const { return m_fade; }
#include <qvariant.h>
#include <qcolor.h>
#include <qfile.h>
-#include <QParallelAnimationGroup>
-#include <QSequentialAnimationGroup>
+#include "private/qparallelanimationgroupjob_p.h"
+#include "private/qsequentialanimationgroupjob_p.h"
#include <QtCore/qset.h>
#include <QtCore/qrect.h>
#include <QtCore/qpoint.h>
#include <QtCore/qsize.h>
#include <QtCore/qmath.h>
-#include <private/qvariantanimation_p.h>
-
QT_BEGIN_NAMESPACE
/*!
QDeclarativeAbstractAnimation::~QDeclarativeAbstractAnimation()
{
+ Q_D(QDeclarativeAbstractAnimation);
+ delete d->animationInstance;
}
QDeclarativeAbstractAnimation::QDeclarativeAbstractAnimation(QDeclarativeAbstractAnimationPrivate &dd, QObject *parent)
{
}
+QAbstractAnimationJob* QDeclarativeAbstractAnimation::qtAnimation()
+{
+ Q_D(QDeclarativeAbstractAnimation);
+ return d->animationInstance;
+}
+
/*!
\qmlproperty bool QtQuick2::Animation::running
This property holds whether the animation is currently running.
QDeclarativeStateActions actions;
QDeclarativeProperties properties;
- q->transition(actions, properties, QDeclarativeAbstractAnimation::Forward);
- q->qtAnimation()->start();
- if (q->qtAnimation()->state() == QAbstractAnimation::Stopped) {
+ QAbstractAnimationJob *oldInstance = animationInstance;
+ animationInstance = q->transition(actions, properties, QDeclarativeAbstractAnimation::Forward);
+ if (oldInstance != animationInstance) {
+ animationInstance->addAnimationChangeListener(this, QAbstractAnimationJob::Completion);
+ if (oldInstance)
+ delete oldInstance;
+ }
+ animationInstance->start();
+ if (animationInstance->isStopped()) {
running = false;
emit q->completed();
}
else if (!d->registered) {
d->registered = true;
QDeclarativeEnginePrivate *engPriv = QDeclarativeEnginePrivate::get(qmlEngine(this));
- engPriv->registerFinalizeCallback(this, this->metaObject()->indexOfSlot("componentFinalized()"));
+ static int finalizedIdx = -1;
+ if (finalizedIdx < 0)
+ finalizedIdx = metaObject()->indexOfSlot("componentFinalized()");
+ engPriv->registerFinalizeCallback(this, finalizedIdx);
}
return;
}
if (d->running) {
bool supressStart = false;
if (d->alwaysRunToEnd && d->loopCount != 1
- && qtAnimation()->state() == QAbstractAnimation::Running) {
+ && d->animationInstance && d->animationInstance->isRunning()) {
//we've restarted before the final loop finished; restore proper loop count
if (d->loopCount == -1)
- qtAnimation()->setLoopCount(d->loopCount);
+ d->animationInstance->setLoopCount(d->loopCount);
else
- qtAnimation()->setLoopCount(qtAnimation()->currentLoop() + d->loopCount);
+ d->animationInstance->setLoopCount(d->animationInstance->currentLoop() + d->loopCount);
supressStart = true; //we want the animation to continue, rather than restart
}
-
- if (!d->connectedTimeLine) {
- FAST_CONNECT(qtAnimation(), SIGNAL(finished()), this, SLOT(timelineComplete()))
- d->connectedTimeLine = true;
- }
if (!supressStart)
d->commence();
emit started();
} else {
- if (d->alwaysRunToEnd) {
- if (d->loopCount != 1)
- qtAnimation()->setLoopCount(qtAnimation()->currentLoop()+1); //finish the current loop
- } else
- qtAnimation()->stop();
-
+ if (d->animationInstance) {
+ if (d->alwaysRunToEnd) {
+ if (d->loopCount != 1)
+ d->animationInstance->setLoopCount(d->animationInstance->currentLoop()+1); //finish the current loop
+ } else {
+ d->animationInstance->stop();
+ }
+ }
emit completed();
}
d->paused = p;
- if (!d->componentComplete)
+ if (!d->componentComplete || !d->animationInstance)
return;
if (d->paused)
- qtAnimation()->pause();
+ d->animationInstance->pause();
else
- qtAnimation()->resume();
+ d->animationInstance->resume();
emit pausedChanged(d->paused);
}
return;
d->loopCount = loops;
- qtAnimation()->setLoopCount(loops);
emit loopCountChanged(loops);
}
+int QDeclarativeAbstractAnimation::duration() const
+{
+ Q_D(const QDeclarativeAbstractAnimation);
+ return d->animationInstance ? d->animationInstance->duration() : 0;
+}
int QDeclarativeAbstractAnimation::currentTime()
{
- return qtAnimation()->currentLoopTime();
+ Q_D(QDeclarativeAbstractAnimation);
+ return d->animationInstance ? d->animationInstance->currentLoopTime() : 0;
}
void QDeclarativeAbstractAnimation::setCurrentTime(int time)
{
- qtAnimation()->setCurrentTime(time);
+ Q_D(QDeclarativeAbstractAnimation);
+ if (d->animationInstance)
+ d->animationInstance->setCurrentTime(time);
+ //TODO save value for start?
}
QDeclarativeAnimationGroup *QDeclarativeAbstractAnimation::group() const
*/
void QDeclarativeAbstractAnimation::complete()
{
- if (isRunning()) {
- qtAnimation()->setCurrentTime(qtAnimation()->duration());
+ Q_D(QDeclarativeAbstractAnimation);
+ if (isRunning() && d->animationInstance) {
+ d->animationInstance->setCurrentTime(d->animationInstance->duration());
}
}
d->disableUserControl = true;
}
-void QDeclarativeAbstractAnimation::transition(QDeclarativeStateActions &actions,
+void QDeclarativeAbstractAnimation::setEnableUserControl()
+{
+ Q_D(QDeclarativeAbstractAnimation);
+ d->disableUserControl = false;
+
+}
+
+bool QDeclarativeAbstractAnimation::userControlDisabled() const
+{
+ Q_D(const QDeclarativeAbstractAnimation);
+ return d->disableUserControl;
+}
+
+QAbstractAnimationJob* QDeclarativeAbstractAnimation::initInstance(QAbstractAnimationJob *animation)
+{
+ Q_D(QDeclarativeAbstractAnimation);
+ animation->setLoopCount(d->loopCount);
+ return animation;
+}
+
+QAbstractAnimationJob* QDeclarativeAbstractAnimation::transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction)
{
Q_UNUSED(actions);
Q_UNUSED(modified);
Q_UNUSED(direction);
+ return 0;
}
-void QDeclarativeAbstractAnimation::timelineComplete()
+void QDeclarativeAbstractAnimationPrivate::animationFinished(QAbstractAnimationJob*)
{
- Q_D(QDeclarativeAbstractAnimation);
- setRunning(false);
- if (d->alwaysRunToEnd && d->loopCount != 1) {
+ Q_Q(QDeclarativeAbstractAnimation);
+ q->setRunning(false);
+ if (alwaysRunToEnd && loopCount != 1) {
//restore the proper loopCount for the next run
- qtAnimation()->setLoopCount(d->loopCount);
+ animationInstance->setLoopCount(loopCount);
}
}
QDeclarativePauseAnimation::QDeclarativePauseAnimation(QObject *parent)
: QDeclarativeAbstractAnimation(*(new QDeclarativePauseAnimationPrivate), parent)
{
- Q_D(QDeclarativePauseAnimation);
- d->init();
}
QDeclarativePauseAnimation::~QDeclarativePauseAnimation()
{
}
-void QDeclarativePauseAnimationPrivate::init()
-{
- Q_Q(QDeclarativePauseAnimation);
- pa = new QPauseAnimation;
- QDeclarative_setParent_noEvent(pa, q);
-}
-
/*!
\qmlproperty int QtQuick2::PauseAnimation::duration
This property holds the duration of the pause in milliseconds
int QDeclarativePauseAnimation::duration() const
{
Q_D(const QDeclarativePauseAnimation);
- return d->pa->duration();
+ return d->duration;
}
void QDeclarativePauseAnimation::setDuration(int duration)
}
Q_D(QDeclarativePauseAnimation);
- if (d->pa->duration() == duration)
+ if (d->duration == duration)
return;
- d->pa->setDuration(duration);
+ d->duration = duration;
emit durationChanged(duration);
}
-QAbstractAnimation *QDeclarativePauseAnimation::qtAnimation()
+QAbstractAnimationJob* QDeclarativePauseAnimation::transition(QDeclarativeStateActions &actions,
+ QDeclarativeProperties &modified,
+ TransitionDirection direction)
{
Q_D(QDeclarativePauseAnimation);
- return d->pa;
+ Q_UNUSED(actions);
+ Q_UNUSED(modified);
+ Q_UNUSED(direction);
+
+ return initInstance(new QPauseAnimationJob(d->duration));
}
/*!
{
Q_D(QDeclarativePropertyAnimation);
d->interpolatorType = QMetaType::QColor;
- d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType);
d->defaultToInterpolatorType = true;
+ d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType);
}
QDeclarativeColorAnimation::~QDeclarativeColorAnimation()
QDeclarativePropertyAnimation::setTo(t);
}
+QActionAnimation::QActionAnimation()
+ : QAbstractAnimationJob(), animAction(0)
+{
+}
+
+QActionAnimation::QActionAnimation(QAbstractAnimationAction *action)
+ : QAbstractAnimationJob(), animAction(action)
+{
+}
+
+QActionAnimation::~QActionAnimation()
+{
+ delete animAction;
+}
+
+int QActionAnimation::duration() const
+{
+ return 0;
+}
+
+void QActionAnimation::setAnimAction(QAbstractAnimationAction *action)
+{
+ if (isRunning())
+ stop();
+ animAction = action;
+}
+
+void QActionAnimation::updateCurrentTime(int)
+{
+}
+void QActionAnimation::updateState(State newState, State oldState)
+{
+ Q_UNUSED(oldState);
+
+ if (newState == Running) {
+ if (animAction) {
+ animAction->doAction();
+ }
+ }
+}
/*!
\qmlclass ScriptAction QDeclarativeScriptAction
QDeclarativeScriptAction::QDeclarativeScriptAction(QObject *parent)
:QDeclarativeAbstractAnimation(*(new QDeclarativeScriptActionPrivate), parent)
{
- Q_D(QDeclarativeScriptAction);
- d->init();
}
QDeclarativeScriptAction::~QDeclarativeScriptAction()
{
}
-void QDeclarativeScriptActionPrivate::init()
-{
- Q_Q(QDeclarativeScriptAction);
- rsa = new QActionAnimation(&proxy);
- QDeclarative_setParent_noEvent(rsa, q);
-}
+QDeclarativeScriptActionPrivate::QDeclarativeScriptActionPrivate()
+ : QDeclarativeAbstractAnimationPrivate(), hasRunScriptScript(false), reversing(false){}
/*!
\qmlproperty script QtQuick2::ScriptAction::script
d->name = name;
}
+QAbstractAnimationAction* QDeclarativeScriptActionPrivate::createAction()
+{
+ return new Proxy(this);
+}
+
void QDeclarativeScriptActionPrivate::execute()
{
Q_Q(QDeclarativeScriptAction);
}
}
-void QDeclarativeScriptAction::transition(QDeclarativeStateActions &actions,
+QAbstractAnimationJob* QDeclarativeScriptAction::transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction)
{
break; //only match one (names should be unique)
}
}
+ return initInstance(new QActionAnimation(d->createAction()));
}
-QAbstractAnimation *QDeclarativeScriptAction::qtAnimation()
-{
- Q_D(QDeclarativeScriptAction);
- return d->rsa;
-}
-
-
-
/*!
\qmlclass PropertyAction QDeclarativePropertyAction
\inqmlmodule QtQuick 2
QDeclarativePropertyAction::QDeclarativePropertyAction(QObject *parent)
: QDeclarativeAbstractAnimation(*(new QDeclarativePropertyActionPrivate), parent)
{
- Q_D(QDeclarativePropertyAction);
- d->init();
}
QDeclarativePropertyAction::~QDeclarativePropertyAction()
{
}
-void QDeclarativePropertyActionPrivate::init()
-{
- Q_Q(QDeclarativePropertyAction);
- spa = new QActionAnimation;
- QDeclarative_setParent_noEvent(spa, q);
-}
-
QObject *QDeclarativePropertyAction::target() const
{
Q_D(const QDeclarativePropertyAction);
}
}
-QAbstractAnimation *QDeclarativePropertyAction::qtAnimation()
-{
- Q_D(QDeclarativePropertyAction);
- return d->spa;
-}
-
-void QDeclarativePropertyAction::transition(QDeclarativeStateActions &actions,
+QAbstractAnimationJob* QDeclarativePropertyAction::transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction)
{
}
}
+ QActionAnimation *action = new QActionAnimation;
if (data->actions.count()) {
- d->spa->setAnimAction(data, QAbstractAnimation::DeleteWhenStopped);
+ action->setAnimAction(data);
} else {
delete data;
}
+ return initInstance(action);
}
/*!
{
Q_D(QDeclarativePropertyAnimation);
d->interpolatorType = QMetaType::QVector3D;
- d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType);
d->defaultToInterpolatorType = true;
+ d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType);
}
QDeclarativeVector3dAnimation::~QDeclarativeVector3dAnimation()
d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType);
break;
}
-
emit directionChanged();
}
QDeclarativeAnimationGroup *q = qobject_cast<QDeclarativeAnimationGroup *>(list->object);
if (q) {
a->setGroup(q);
- // This is an optimization for the parenting that already occurs via addAnimation
- QDeclarative_setParent_noEvent(a->qtAnimation(), q->d_func()->ag);
- q->d_func()->ag->addAnimation(a->qtAnimation());
}
}
if (q) {
while (q->d_func()->animations.count()) {
QDeclarativeAbstractAnimation *firstAnim = q->d_func()->animations.at(0);
- QDeclarative_setParent_noEvent(firstAnim->qtAnimation(), 0);
- q->d_func()->ag->removeAnimation(firstAnim->qtAnimation());
firstAnim->setGroup(0);
}
}
QDeclarativeSequentialAnimation::QDeclarativeSequentialAnimation(QObject *parent) :
QDeclarativeAnimationGroup(parent)
{
- Q_D(QDeclarativeAnimationGroup);
- d->ag = new QSequentialAnimationGroup;
- QDeclarative_setParent_noEvent(d->ag, this);
}
QDeclarativeSequentialAnimation::~QDeclarativeSequentialAnimation()
{
}
-QAbstractAnimation *QDeclarativeSequentialAnimation::qtAnimation()
-{
- Q_D(QDeclarativeAnimationGroup);
- return d->ag;
-}
-
-void QDeclarativeSequentialAnimation::transition(QDeclarativeStateActions &actions,
+QAbstractAnimationJob* QDeclarativeSequentialAnimation::transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction)
{
Q_D(QDeclarativeAnimationGroup);
+ QSequentialAnimationGroupJob *ag = new QSequentialAnimationGroupJob;
+
int inc = 1;
int from = 0;
if (direction == Backward) {
}
bool valid = d->defaultProperty.isValid();
+ QAbstractAnimationJob* anim;
for (int ii = from; ii < d->animations.count() && ii >= 0; ii += inc) {
if (valid)
d->animations.at(ii)->setDefaultTarget(d->defaultProperty);
- d->animations.at(ii)->transition(actions, modified, direction);
+ anim = d->animations.at(ii)->transition(actions, modified, direction);
+ inc == -1 ? ag->prependAnimation(anim) : ag->appendAnimation(anim);
}
+
+ return initInstance(ag);
}
QDeclarativeParallelAnimation::QDeclarativeParallelAnimation(QObject *parent) :
QDeclarativeAnimationGroup(parent)
{
- Q_D(QDeclarativeAnimationGroup);
- d->ag = new QParallelAnimationGroup;
- QDeclarative_setParent_noEvent(d->ag, this);
}
QDeclarativeParallelAnimation::~QDeclarativeParallelAnimation()
{
}
-QAbstractAnimation *QDeclarativeParallelAnimation::qtAnimation()
-{
- Q_D(QDeclarativeAnimationGroup);
- return d->ag;
-}
-
-void QDeclarativeParallelAnimation::transition(QDeclarativeStateActions &actions,
+QAbstractAnimationJob* QDeclarativeParallelAnimation::transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction)
{
Q_D(QDeclarativeAnimationGroup);
+ QParallelAnimationGroupJob *ag = new QParallelAnimationGroupJob;
+
bool valid = d->defaultProperty.isValid();
+ QAbstractAnimationJob* anim;
for (int ii = 0; ii < d->animations.count(); ++ii) {
if (valid)
d->animations.at(ii)->setDefaultTarget(d->defaultProperty);
- d->animations.at(ii)->transition(actions, modified, direction);
+ anim = d->animations.at(ii)->transition(actions, modified, direction);
+ ag->appendAnimation(anim);
}
+ return initInstance(ag);
}
-
-
//convert a variant from string type to another animatable type
void QDeclarativePropertyAnimationPrivate::convertVariant(QVariant &variant, int type)
{
}
}
+QDeclarativeBulkValueAnimator::QDeclarativeBulkValueAnimator()
+ : QAbstractAnimationJob(), animValue(0), fromSourced(0), m_duration(250)
+{
+}
+
+QDeclarativeBulkValueAnimator::~QDeclarativeBulkValueAnimator()
+{
+ delete animValue;
+}
+
+void QDeclarativeBulkValueAnimator::setAnimValue(QDeclarativeBulkValueUpdater *value)
+{
+ if (isRunning())
+ stop();
+ animValue = value;
+}
+
+void QDeclarativeBulkValueAnimator::updateCurrentTime(int currentTime)
+{
+ if (isStopped())
+ return;
+
+ const qreal progress = easing.valueForProgress(((m_duration == 0) ? qreal(1) : qreal(currentTime) / qreal(m_duration)));
+
+ if (animValue)
+ animValue->setValue(progress);
+}
+
+void QDeclarativeBulkValueAnimator::topLevelAnimationLoopChanged()
+{
+ //check for new from every top-level loop (when the top level animation is started and all subsequent loops)
+ if (fromSourced)
+ *fromSourced = false;
+}
+
/*!
\qmlclass PropertyAnimation QDeclarativePropertyAnimation
\inqmlmodule QtQuick 2
QDeclarativePropertyAnimation::QDeclarativePropertyAnimation(QObject *parent)
: QDeclarativeAbstractAnimation(*(new QDeclarativePropertyAnimationPrivate), parent)
{
- Q_D(QDeclarativePropertyAnimation);
- d->init();
}
QDeclarativePropertyAnimation::QDeclarativePropertyAnimation(QDeclarativePropertyAnimationPrivate &dd, QObject *parent)
: QDeclarativeAbstractAnimation(dd, parent)
{
- Q_D(QDeclarativePropertyAnimation);
- d->init();
}
QDeclarativePropertyAnimation::~QDeclarativePropertyAnimation()
{
}
-void QDeclarativePropertyAnimationPrivate::init()
-{
- Q_Q(QDeclarativePropertyAnimation);
- va = new QDeclarativeBulkValueAnimator;
- QDeclarative_setParent_noEvent(va, q);
-}
-
/*!
\qmlproperty int QtQuick2::PropertyAnimation::duration
This property holds the duration of the animation, in milliseconds.
int QDeclarativePropertyAnimation::duration() const
{
Q_D(const QDeclarativePropertyAnimation);
- return d->va->duration();
+ return d->duration;
}
void QDeclarativePropertyAnimation::setDuration(int duration)
}
Q_D(QDeclarativePropertyAnimation);
- if (d->va->duration() == duration)
+ if (d->duration == duration)
return;
- d->va->setDuration(duration);
+ d->duration = duration;
emit durationChanged(duration);
}
QEasingCurve QDeclarativePropertyAnimation::easing() const
{
Q_D(const QDeclarativePropertyAnimation);
- return d->va->easingCurve();
+ return d->easing;
}
void QDeclarativePropertyAnimation::setEasing(const QEasingCurve &e)
{
Q_D(QDeclarativePropertyAnimation);
- if (d->va->easingCurve() == e)
+ if (d->easing == e)
return;
- d->va->setEasingCurve(e);
+ d->easing = e;
emit easingChanged(e);
}
return QDeclarativeListProperty<QObject>(this, d->exclude);
}
-QAbstractAnimation *QDeclarativePropertyAnimation::qtAnimation()
-{
- Q_D(QDeclarativePropertyAnimation);
- return d->va;
-}
-
void QDeclarativeAnimationPropertyUpdater::setValue(qreal v)
{
bool deleted = false;
wasDeleted = &deleted;
- if (reverse) //QVariantAnimation sends us 1->0 when reversed, but we are expecting 0->1
+ if (reverse)
v = 1 - v;
for (int ii = 0; ii < actions.count(); ++ii) {
QDeclarativeAction &action = actions[ii];
- if (v == 1.)
+ if (v == 1.) {
QDeclarativePropertyPrivate::write(action.property, action.toValue, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
- else {
+ } else {
if (!fromSourced && !fromDefined) {
action.fromValue = action.property.read();
- if (interpolatorType)
+ if (interpolatorType) {
QDeclarativePropertyAnimationPrivate::convertVariant(action.fromValue, interpolatorType);
+ }
}
if (!interpolatorType) {
int propType = action.property.propertyType();
fromSourced = true;
}
-void QDeclarativePropertyAnimation::transition(QDeclarativeStateActions &actions,
- QDeclarativeProperties &modified,
- TransitionDirection direction)
+QDeclarativeStateActions QDeclarativePropertyAnimation::createTransitionActions(QDeclarativeStateActions &actions,
+ QDeclarativeProperties &modified)
{
Q_D(QDeclarativePropertyAnimation);
+ QDeclarativeStateActions newActions;
QStringList props = d->properties.isEmpty() ? QStringList() : d->properties.split(QLatin1Char(','));
for (int ii = 0; ii < props.count(); ++ii)
props << d->defaultProperties.split(QLatin1Char(','));
}
- QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater;
- data->interpolatorType = d->interpolatorType;
- data->interpolator = d->interpolator;
- data->reverse = direction == Backward ? true : false;
- data->fromSourced = false;
- data->fromDefined = d->fromIsDefined;
-
bool hasExplicit = false;
//an explicit animation has been specified
if (d->toIsDefined) {
}
myAction.toValue = d->to;
d->convertVariant(myAction.toValue, d->interpolatorType ? d->interpolatorType : myAction.property.propertyType());
- data->actions << myAction;
+ newActions << myAction;
hasExplicit = true;
for (int ii = 0; ii < actions.count(); ++ii) {
QDeclarativeAction &action = actions[ii];
modified << action.property;
- data->actions << myAction;
+ newActions << myAction;
action.fromValue = myAction.toValue;
}
}
-
- if (data->actions.count()) {
- if (!d->rangeIsSet) {
- d->va->setStartValue(qreal(0));
- d->va->setEndValue(qreal(1));
- d->rangeIsSet = true;
- }
- d->va->setAnimValue(data, QAbstractAnimation::DeleteWhenStopped);
- d->va->setFromSourcedValue(&data->fromSourced);
- d->actions = &data->actions;
- } else {
- delete data;
- d->va->setFromSourcedValue(0); //clear previous data
- d->va->setAnimValue(0, QAbstractAnimation::DeleteWhenStopped); //clear previous data
- d->actions = 0;
- }
+ return newActions;
}
+QAbstractAnimationJob* QDeclarativePropertyAnimation::transition(QDeclarativeStateActions &actions,
+ QDeclarativeProperties &modified,
+ TransitionDirection direction)
+{
+ Q_D(QDeclarativePropertyAnimation);
-QDeclarativeScriptActionPrivate::QDeclarativeScriptActionPrivate()
- : QDeclarativeAbstractAnimationPrivate(), hasRunScriptScript(false), reversing(false), proxy(this), rsa(0) {}
+ QDeclarativeStateActions dataActions = createTransitionActions(actions, modified);
+
+ QDeclarativeBulkValueAnimator *animator = new QDeclarativeBulkValueAnimator;
+ animator->setDuration(d->duration);
+ animator->setEasingCurve(d->easing);
+
+ if (!dataActions.isEmpty()) {
+ QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater;
+ data->interpolatorType = d->interpolatorType;
+ data->interpolator = d->interpolator;
+ data->reverse = direction == Backward ? true : false;
+ data->fromSourced = false;
+ data->fromDefined = d->fromIsDefined;
+ data->actions = dataActions;
+ animator->setAnimValue(data);
+ animator->setFromSourcedValue(&data->fromSourced);
+ d->actions = &data->actions; //remove this?
+ }
+ return initInstance(animator);
+}
QT_END_NAMESPACE
#include <QtCore/qvariant.h>
#include <QtCore/qeasingcurve.h>
-#include <QtCore/QAbstractAnimation>
+#include "private/qabstractanimationjob_p.h"
#include <QtGui/qcolor.h>
QT_BEGIN_HEADER
int loops() const;
void setLoops(int);
+ int duration() const;
int currentTime();
void setCurrentTime(int);
void setDefaultTarget(const QDeclarativeProperty &);
void setDisableUserControl();
-
+ void setEnableUserControl();
+ bool userControlDisabled() const;
void classBegin();
void componentComplete();
protected:
QDeclarativeAbstractAnimation(QDeclarativeAbstractAnimationPrivate &dd, QObject *parent);
+ QAbstractAnimationJob* initInstance(QAbstractAnimationJob *animation);
public:
enum TransitionDirection { Forward, Backward };
- virtual void transition(QDeclarativeStateActions &actions,
+ virtual QAbstractAnimationJob* transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction);
- virtual QAbstractAnimation *qtAnimation() = 0;
+ QAbstractAnimationJob* qtAnimation();
private Q_SLOTS:
- void timelineComplete();
void componentFinalized();
private:
virtual void setTarget(const QDeclarativeProperty &);
void notifyRunningChanged(bool running);
friend class QDeclarativeBehavior;
-
-
+ friend class QDeclarativeBehaviorPrivate;
};
class QDeclarativePauseAnimationPrivate;
-class Q_AUTOTEST_EXPORT QDeclarativePauseAnimation : public QDeclarativeAbstractAnimation
+class Q_QUICK_PRIVATE_EXPORT QDeclarativePauseAnimation : public QDeclarativeAbstractAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QDeclarativePauseAnimation)
void durationChanged(int);
protected:
- virtual QAbstractAnimation *qtAnimation();
+ QAbstractAnimationJob* transition(QDeclarativeStateActions &actions,
+ QDeclarativeProperties &modified,
+ TransitionDirection direction);
};
class QDeclarativeScriptActionPrivate;
void setStateChangeScriptName(const QString &);
protected:
- virtual void transition(QDeclarativeStateActions &actions,
+ virtual QAbstractAnimationJob* transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction);
- virtual QAbstractAnimation *qtAnimation();
};
class QDeclarativePropertyActionPrivate;
-class QDeclarativePropertyAction : public QDeclarativeAbstractAnimation
+class Q_QUICK_PRIVATE_EXPORT QDeclarativePropertyAction : public QDeclarativeAbstractAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QDeclarativePropertyAction)
void propertyChanged();
protected:
- virtual void transition(QDeclarativeStateActions &actions,
+ virtual QAbstractAnimationJob* transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction);
- virtual QAbstractAnimation *qtAnimation();
};
class QDeclarativePropertyAnimationPrivate;
-class Q_AUTOTEST_EXPORT QDeclarativePropertyAnimation : public QDeclarativeAbstractAnimation
+class Q_QUICK_PRIVATE_EXPORT QDeclarativePropertyAnimation : public QDeclarativeAbstractAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation)
QDeclarativeListProperty<QObject> exclude();
protected:
+ QDeclarativeStateActions createTransitionActions(QDeclarativeStateActions &actions,
+ QDeclarativeProperties &modified);
+
QDeclarativePropertyAnimation(QDeclarativePropertyAnimationPrivate &dd, QObject *parent);
- virtual void transition(QDeclarativeStateActions &actions,
+ virtual QAbstractAnimationJob* transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction);
- virtual QAbstractAnimation *qtAnimation();
-
Q_SIGNALS:
void durationChanged(int);
void fromChanged(QVariant);
void propertyChanged();
};
-class Q_AUTOTEST_EXPORT QDeclarativeColorAnimation : public QDeclarativePropertyAnimation
+class Q_QUICK_PRIVATE_EXPORT QDeclarativeColorAnimation : public QDeclarativePropertyAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation)
void setTo(const QColor &);
};
-class Q_AUTOTEST_EXPORT QDeclarativeNumberAnimation : public QDeclarativePropertyAnimation
+class Q_QUICK_PRIVATE_EXPORT QDeclarativeNumberAnimation : public QDeclarativePropertyAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation)
void init();
};
-class Q_AUTOTEST_EXPORT QDeclarativeVector3dAnimation : public QDeclarativePropertyAnimation
+class Q_QUICK_PRIVATE_EXPORT QDeclarativeVector3dAnimation : public QDeclarativePropertyAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation)
};
class QDeclarativeRotationAnimationPrivate;
-class Q_AUTOTEST_EXPORT QDeclarativeRotationAnimation : public QDeclarativePropertyAnimation
+class Q_QUICK_PRIVATE_EXPORT QDeclarativeRotationAnimation : public QDeclarativePropertyAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QDeclarativeRotationAnimation)
};
class QDeclarativeAnimationGroupPrivate;
-class Q_AUTOTEST_EXPORT QDeclarativeAnimationGroup : public QDeclarativeAbstractAnimation
+class Q_QUICK_PRIVATE_EXPORT QDeclarativeAnimationGroup : public QDeclarativeAbstractAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QDeclarativeAnimationGroup)
virtual ~QDeclarativeSequentialAnimation();
protected:
- virtual void transition(QDeclarativeStateActions &actions,
+ virtual QAbstractAnimationJob* transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction);
- virtual QAbstractAnimation *qtAnimation();
};
-class QDeclarativeParallelAnimation : public QDeclarativeAnimationGroup
+class Q_QUICK_PRIVATE_EXPORT QDeclarativeParallelAnimation : public QDeclarativeAnimationGroup
{
Q_OBJECT
Q_DECLARE_PRIVATE(QDeclarativeAnimationGroup)
virtual ~QDeclarativeParallelAnimation();
protected:
- virtual void transition(QDeclarativeStateActions &actions,
+ virtual QAbstractAnimationJob* transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction);
- virtual QAbstractAnimation *qtAnimation();
};
**
****************************************************************************/
-#ifndef QDECLARATIVEANIMATION_P_H
-#define QDECLARATIVEANIMATION_P_H
+#ifndef QDECLARATIVEANIMATION2_P_H
+#define QDECLARATIVEANIMATION2_P_H
//
// W A R N I N G
#include <qdeclarative.h>
#include <qdeclarativecontext.h>
-#include <QtCore/QPauseAnimation>
-#include <QtCore/QVariantAnimation>
-#include <QtCore/QAnimationGroup>
+#include <private/qvariantanimation_p.h>
+#include "private/qpauseanimationjob_p.h"
#include <QDebug>
#include <private/qobject_p.h>
-#include <private/qvariantanimation_p.h>
+#include "private/qanimationgroupjob_p.h"
+#include <QDebug>
+
+#include <private/qobject_p.h>
+
QT_BEGIN_NAMESPACE
class QAnimationActionProxy : public QAbstractAnimationAction
{
public:
- QAnimationActionProxy(T *p) : m_p(p) {}
- virtual void doAction() { (m_p->*method)(); }
+ QAnimationActionProxy(T *instance) : m_instance(instance) {}
+ virtual void doAction() { (m_instance->*method)(); }
private:
- T *m_p;
+ T *m_instance;
};
//performs an action of type QAbstractAnimationAction
-class Q_AUTOTEST_EXPORT QActionAnimation : public QAbstractAnimation
+class Q_AUTOTEST_EXPORT QActionAnimation : public QAbstractAnimationJob
{
- Q_OBJECT
+ Q_DISABLE_COPY(QActionAnimation)
public:
- QActionAnimation(QObject *parent = 0) : QAbstractAnimation(parent), animAction(0), policy(KeepWhenStopped) {}
- QActionAnimation(QAbstractAnimationAction *action, QObject *parent = 0)
- : QAbstractAnimation(parent), animAction(action), policy(KeepWhenStopped) {}
- ~QActionAnimation() { if (policy == DeleteWhenStopped) { delete animAction; animAction = 0; } }
- virtual int duration() const { return 0; }
- void setAnimAction(QAbstractAnimationAction *action, DeletionPolicy p)
- {
- if (state() == Running)
- stop();
- if (policy == DeleteWhenStopped)
- delete animAction;
- animAction = action;
- policy = p;
- }
+ QActionAnimation();
+
+ QActionAnimation(QAbstractAnimationAction *action);
+ ~QActionAnimation();
+
+ virtual int duration() const;
+ void setAnimAction(QAbstractAnimationAction *action);
+
protected:
- virtual void updateCurrentTime(int) {}
-
- virtual void updateState(State newState, State /*oldState*/)
- {
- if (newState == Running) {
- if (animAction) {
- animAction->doAction();
- if (state() == Stopped && policy == DeleteWhenStopped) {
- delete animAction;
- animAction = 0;
- }
- }
- }
- }
+ virtual void updateCurrentTime(int);
+ virtual void updateState(State newState, State oldState);
private:
QAbstractAnimationAction *animAction;
- DeletionPolicy policy;
};
class QDeclarativeBulkValueUpdater
};
//animates QDeclarativeBulkValueUpdater (assumes start and end values will be reals or compatible)
-class Q_AUTOTEST_EXPORT QDeclarativeBulkValueAnimator : public QVariantAnimation
+class Q_AUTOTEST_EXPORT QDeclarativeBulkValueAnimator : public QAbstractAnimationJob
{
- Q_OBJECT
+ Q_DISABLE_COPY(QDeclarativeBulkValueAnimator)
public:
- QDeclarativeBulkValueAnimator(QObject *parent = 0) : QVariantAnimation(parent), animValue(0), fromSourced(0), policy(KeepWhenStopped) {}
- ~QDeclarativeBulkValueAnimator() { if (policy == DeleteWhenStopped) { delete animValue; animValue = 0; } }
- void setAnimValue(QDeclarativeBulkValueUpdater *value, DeletionPolicy p)
- {
- if (state() == Running)
- stop();
- if (policy == DeleteWhenStopped)
- delete animValue;
- animValue = value;
- policy = p;
- }
- QDeclarativeBulkValueUpdater *getAnimValue() const
- {
- return animValue;
- }
- void setFromSourcedValue(bool *value)
- {
- fromSourced = value;
- }
+ QDeclarativeBulkValueAnimator();
+ ~QDeclarativeBulkValueAnimator();
+
+ void setAnimValue(QDeclarativeBulkValueUpdater *value);
+ QDeclarativeBulkValueUpdater *getAnimValue() const { return animValue; }
+
+ void setFromSourcedValue(bool *value) { fromSourced = value; }
+
+ int duration() const { return m_duration; }
+ void setDuration(int msecs) { m_duration = msecs; }
+
+ QEasingCurve easingCurve() const { return easing; }
+ void setEasingCurve(const QEasingCurve &curve) { easing = curve; }
+
protected:
- virtual void updateCurrentValue(const QVariant &value)
- {
- if (state() == QAbstractAnimation::Stopped)
- return;
-
- if (animValue)
- animValue->setValue(value.toReal());
- }
- virtual void updateState(State newState, State oldState)
- {
- QVariantAnimation::updateState(newState, oldState);
- if (newState == Running) {
- //check for new from every loop
- if (fromSourced)
- *fromSourced = false;
- }
- }
+ void updateCurrentTime(int currentTime);
+ void topLevelAnimationLoopChanged();
private:
QDeclarativeBulkValueUpdater *animValue;
bool *fromSourced;
- DeletionPolicy policy;
+ int m_duration;
+ QEasingCurve easing;
};
//an animation that just gives a tick
template<class T, void (T::*method)(int)>
-class QTickAnimationProxy : public QAbstractAnimation
+class QTickAnimationProxy : public QAbstractAnimationJob
{
- //Q_OBJECT //doesn't work with templating
+ Q_DISABLE_COPY(QTickAnimationProxy)
public:
- QTickAnimationProxy(T *p, QObject *parent = 0) : QAbstractAnimation(parent), m_p(p) {}
+ QTickAnimationProxy(T *instance) : QAbstractAnimationJob(), m_instance(instance) {}
virtual int duration() const { return -1; }
protected:
- virtual void updateCurrentTime(int msec) { (m_p->*method)(msec); }
+ virtual void updateCurrentTime(int msec) { (m_instance->*method)(msec); }
private:
- T *m_p;
+ T *m_instance;
};
-class QDeclarativeAbstractAnimationPrivate : public QObjectPrivate
+class QDeclarativeAbstractAnimationPrivate : public QObjectPrivate, public QAnimation2ChangeListener
{
Q_DECLARE_PUBLIC(QDeclarativeAbstractAnimation)
public:
QDeclarativeAbstractAnimationPrivate()
: running(false), paused(false), alwaysRunToEnd(false),
- connectedTimeLine(false), componentComplete(true),
+ /*connectedTimeLine(false), */componentComplete(true),
avoidPropertyValueSourceStart(false), disableUserControl(false),
- registered(false), loopCount(1), group(0) {}
+ registered(false), loopCount(1), group(0), animationInstance(0) {}
bool running:1;
bool paused:1;
bool alwaysRunToEnd:1;
- bool connectedTimeLine:1;
+ //bool connectedTimeLine:1;
bool componentComplete:1;
bool avoidPropertyValueSourceStart:1;
bool disableUserControl:1;
int loopCount;
void commence();
+ virtual void animationFinished(QAbstractAnimationJob *);
QDeclarativeProperty defaultProperty;
QDeclarativeAnimationGroup *group;
+ QAbstractAnimationJob* animationInstance;
static QDeclarativeProperty createProperty(QObject *obj, const QString &str, QObject *infoObj);
};
Q_DECLARE_PUBLIC(QDeclarativePauseAnimation)
public:
QDeclarativePauseAnimationPrivate()
- : QDeclarativeAbstractAnimationPrivate(), pa(0) {}
+ : QDeclarativeAbstractAnimationPrivate(), duration(250) {}
- void init();
-
- QPauseAnimation *pa;
+ int duration;
};
class QDeclarativeScriptActionPrivate : public QDeclarativeAbstractAnimationPrivate
public:
QDeclarativeScriptActionPrivate();
- void init();
-
QDeclarativeScriptString script;
QString name;
QDeclarativeScriptString runScriptScript;
bool reversing;
void execute();
-
- QAnimationActionProxy<QDeclarativeScriptActionPrivate,
- &QDeclarativeScriptActionPrivate::execute> proxy;
- QActionAnimation *rsa;
+ QAbstractAnimationAction* createAction();
+ typedef QAnimationActionProxy<QDeclarativeScriptActionPrivate,
+ &QDeclarativeScriptActionPrivate::execute> Proxy;
};
class QDeclarativePropertyActionPrivate : public QDeclarativeAbstractAnimationPrivate
Q_DECLARE_PUBLIC(QDeclarativePropertyAction)
public:
QDeclarativePropertyActionPrivate()
- : QDeclarativeAbstractAnimationPrivate(), target(0), spa(0) {}
-
- void init();
+ : QDeclarativeAbstractAnimationPrivate(), target(0) {}
QObject *target;
QString propertyName;
QList<QObject *> exclude;
QDeclarativeNullableValue<QVariant> value;
-
- QActionAnimation *spa;
};
class QDeclarativeAnimationGroupPrivate : public QDeclarativeAbstractAnimationPrivate
Q_DECLARE_PUBLIC(QDeclarativeAnimationGroup)
public:
QDeclarativeAnimationGroupPrivate()
- : QDeclarativeAbstractAnimationPrivate(), ag(0) {}
+ : QDeclarativeAbstractAnimationPrivate() {}
static void append_animation(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list, QDeclarativeAbstractAnimation *role);
static void clear_animation(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list);
QList<QDeclarativeAbstractAnimation *> animations;
- QAnimationGroup *ag;
};
class QDeclarativePropertyAnimationPrivate : public QDeclarativeAbstractAnimationPrivate
public:
QDeclarativePropertyAnimationPrivate()
: QDeclarativeAbstractAnimationPrivate(), target(0), fromSourced(false), fromIsDefined(false), toIsDefined(false),
- rangeIsSet(false), defaultToInterpolatorType(0), interpolatorType(0), interpolator(0), va(0), actions(0) {}
-
- void init();
+ defaultToInterpolatorType(0), interpolatorType(0), interpolator(0), duration(250), actions(0) {}
QVariant from;
QVariant to;
bool fromSourced;
bool fromIsDefined:1;
bool toIsDefined:1;
- bool rangeIsSet:1;
bool defaultToInterpolatorType:1;
int interpolatorType;
QVariantAnimation::Interpolator interpolator;
-
- QDeclarativeBulkValueAnimator *va;
+ int duration;
+ QEasingCurve easing;
// for animations that don't use the QDeclarativeBulkValueAnimator
QDeclarativeStateActions *actions;
class Q_AUTOTEST_EXPORT QDeclarativeAnimationPropertyUpdater : public QDeclarativeBulkValueUpdater
{
public:
+ QDeclarativeAnimationPropertyUpdater() : prevInterpolatorType(0), wasDeleted(0) {}
+ ~QDeclarativeAnimationPropertyUpdater() { if (wasDeleted) *wasDeleted = true; }
+
+ void setValue(qreal v);
+
QDeclarativeStateActions actions;
int interpolatorType; //for Number/ColorAnimation
- int prevInterpolatorType; //for generic
QVariantAnimation::Interpolator interpolator;
+ int prevInterpolatorType; //for generic
bool reverse;
bool fromSourced;
bool fromDefined;
bool *wasDeleted;
- QDeclarativeAnimationPropertyUpdater() : prevInterpolatorType(0), wasDeleted(0) {}
- ~QDeclarativeAnimationPropertyUpdater() { if (wasDeleted) *wasDeleted = true; }
- void setValue(qreal v);
};
QT_END_NAMESPACE
-#endif // QDECLARATIVEANIMATION_P_H
+#endif // QDECLARATIVEANIMATION2_P_H
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qdeclarativeanimationcontroller_p.h"
+#include <QtDeclarative/qdeclarativeinfo.h>
+#include <private/qdeclarativeengine_p.h>
+
+QT_BEGIN_NAMESPACE
+
+
+class QDeclarativeAnimationControllerPrivate : public QObjectPrivate
+{
+ Q_DECLARE_PUBLIC(QDeclarativeAnimationController)
+public:
+ QDeclarativeAnimationControllerPrivate()
+ : progress(0.0), animation(0), animationInstance(0), finalized(false) {}
+
+ qreal progress;
+ QDeclarativeAbstractAnimation *animation;
+ QAbstractAnimationJob *animationInstance;
+ bool finalized:1;
+
+};
+
+/*!
+ \qmlclass AnimationController QDeclarativeAnimationController
+ \inqmlmodule QtQuick 2
+ \ingroup qml-animation-transition
+ \brief The AnimationController element allows you to control animations manually.
+
+ Normally animations are driven by an internal timer, but the AnimationController
+ allows the given \a animation to be driven by a \a progress value explicitly.
+*/
+
+
+QDeclarativeAnimationController::QDeclarativeAnimationController(QObject *parent)
+: QObject(*(new QDeclarativeAnimationControllerPrivate), parent)
+{
+}
+
+QDeclarativeAnimationController::~QDeclarativeAnimationController()
+{
+ Q_D(QDeclarativeAnimationController);
+ delete d->animationInstance;
+}
+
+/*!
+ \qmlproperty real QtQuick2::AnimationController::progress
+ This property holds the animation progress value.
+
+ The valid \c progress value is 0.0 to 1.0, setting values less than 0 will be converted to 0,
+ setting values great than 1 will be converted to 1.
+*/
+qreal QDeclarativeAnimationController::progress() const
+{
+ Q_D(const QDeclarativeAnimationController);
+ return d->progress;
+}
+
+void QDeclarativeAnimationController::setProgress(qreal progress)
+{
+ Q_D(QDeclarativeAnimationController);
+ progress = qBound(qreal(0), progress, qreal(1));
+
+ if (progress != d->progress) {
+ d->progress = progress;
+ updateProgress();
+ emit progressChanged();
+ }
+}
+
+/*!
+ \qmlproperty real QtQuick2::AnimationController::animation
+ \default
+
+ This property holds the animation to be controlled by the AnimationController.
+
+ Note:An animation controlled by AnimationController will always have its
+ \c running and \c paused properties set to true. It can not be manually
+ started or stopped (much like an animation in a Behavior can not be manually started or stopped).
+*/
+QDeclarativeAbstractAnimation *QDeclarativeAnimationController::animation() const
+{
+ Q_D(const QDeclarativeAnimationController);
+ return d->animation;
+}
+
+void QDeclarativeAnimationController::classBegin()
+{
+ QDeclarativeEnginePrivate *engPriv = QDeclarativeEnginePrivate::get(qmlEngine(this));
+ engPriv->registerFinalizeCallback(this, this->metaObject()->indexOfSlot("componentFinalized()"));
+}
+
+
+void QDeclarativeAnimationController::setAnimation(QDeclarativeAbstractAnimation *animation)
+{
+ Q_D(QDeclarativeAnimationController);
+
+ if (animation != d->animation) {
+ if (animation) {
+ if (animation->userControlDisabled()) {
+ qmlInfo(this) << "QDeclarativeAnimationController::setAnimation: the animation is controlled by others, can't be used in AnimationController.";
+ return;
+ }
+ animation->setDisableUserControl();
+ }
+
+ if (d->animation)
+ d->animation->setEnableUserControl();
+
+ d->animation = animation;
+ reload();
+ emit animationChanged();
+ }
+}
+
+/*!
+ \qmlmethod QtQuick2::AnimationController::reload()
+ \brief Reloads the animation properties.
+
+ If the animation properties changed, calling this method to reload the animation definations.
+*/
+void QDeclarativeAnimationController::reload()
+{
+ Q_D(QDeclarativeAnimationController);
+ if (!d->finalized)
+ return;
+
+ if (!d->animation) {
+ d->animationInstance = 0;
+ } else {
+ QDeclarativeStateActions actions;
+ QDeclarativeProperties properties;
+ QAbstractAnimationJob *oldInstance = d->animationInstance;
+ d->animationInstance = d->animation->transition(actions, properties, QDeclarativeAbstractAnimation::Forward);
+ if (oldInstance && oldInstance != d->animationInstance)
+ delete oldInstance;
+ d->animationInstance->setLoopCount(1);
+ d->animationInstance->start();
+ d->animationInstance->pause();
+ updateProgress();
+ }
+}
+
+void QDeclarativeAnimationController::updateProgress()
+{
+ Q_D(QDeclarativeAnimationController);
+ if (!d->animationInstance)
+ return;
+
+ d->animationInstance->start();
+ QDeclarativeAnimationTimer::unregisterAnimation(d->animationInstance);
+ d->animationInstance->setCurrentTime(d->progress * d->animationInstance->duration());
+}
+
+void QDeclarativeAnimationController::componentFinalized()
+{
+ Q_D(QDeclarativeAnimationController);
+ d->finalized = true;
+ reload();
+}
+
+
+QT_END_NAMESPACE
+
+
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QDECLARATIVEANIMATIONCONTROLLER_H
+#define QDECLARATIVEANIMATIONCONTROLLER_H
+
+#include <qdeclarative.h>
+#include "qdeclarativeanimation_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QDeclarativeAnimationControllerPrivate;
+class Q_AUTOTEST_EXPORT QDeclarativeAnimationController : public QObject, public QDeclarativeParserStatus
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QDeclarativeAnimationController)
+ Q_CLASSINFO("DefaultProperty", "animation")
+
+ Q_PROPERTY(qreal progress READ progress WRITE setProgress NOTIFY progressChanged)
+ Q_PROPERTY(QDeclarativeAbstractAnimation *animation READ animation WRITE setAnimation NOTIFY animationChanged)
+
+public:
+ QDeclarativeAnimationController(QObject *parent=0);
+ ~QDeclarativeAnimationController();
+
+ qreal progress() const;
+ void setProgress(qreal progress);
+
+ QDeclarativeAbstractAnimation *animation() const;
+ void setAnimation(QDeclarativeAbstractAnimation *animation);
+
+ void classBegin();
+ void componentComplete() {}
+Q_SIGNALS:
+ void progressChanged();
+ void animationChanged();
+public Q_SLOTS:
+ void reload();
+private Q_SLOTS:
+ void componentFinalized();
+ void updateProgress();
+};
+
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QDeclarativeAnimationController)
+
+QT_END_HEADER
+
+#endif // QDECLARATIVEANIMATIONCONTROLLER_H
#include <private/qdeclarativeproperty_p.h>
#include <private/qdeclarativeguard_p.h>
#include <private/qdeclarativeengine_p.h>
+#include <private/qabstractanimationjob_p.h>
+#include <private/qdeclarativetransition_p.h>
#include <private/qobject_p.h>
QT_BEGIN_NAMESPACE
-class QDeclarativeBehaviorPrivate : public QObjectPrivate
+class QDeclarativeBehaviorPrivate : public QObjectPrivate, public QAnimation2ChangeListener
{
Q_DECLARE_PUBLIC(QDeclarativeBehavior)
public:
- QDeclarativeBehaviorPrivate() : animation(0), enabled(true), finalized(false)
+ QDeclarativeBehaviorPrivate() : animation(0), animationInstance(0), enabled(true), finalized(false)
, blockRunningChanged(false) {}
+ virtual void animationStateChanged(QAbstractAnimationJob *, QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState);
+
QDeclarativeProperty property;
QVariant targetValue;
QDeclarativeGuard<QDeclarativeAbstractAnimation> animation;
+ QAbstractAnimationJob *animationInstance;
bool enabled;
bool finalized;
bool blockRunningChanged;
QDeclarativeBehavior::~QDeclarativeBehavior()
{
+ Q_D(QDeclarativeBehavior);
+ delete d->animationInstance;
}
/*!
if (d->animation) {
d->animation->setDefaultTarget(d->property);
d->animation->setDisableUserControl();
- FAST_CONNECT(d->animation->qtAnimation(),
- SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)),
- this,
- SLOT(qtAnimationStateChanged(QAbstractAnimation::State,QAbstractAnimation::State)))
}
}
-void QDeclarativeBehavior::qtAnimationStateChanged(QAbstractAnimation::State newState,QAbstractAnimation::State)
+void QDeclarativeBehaviorPrivate::animationStateChanged(QAbstractAnimationJob *, QAbstractAnimationJob::State newState,QAbstractAnimationJob::State)
{
- Q_D(QDeclarativeBehavior);
- if (!d->blockRunningChanged)
- d->animation->notifyRunningChanged(newState == QAbstractAnimation::Running);
+ if (!blockRunningChanged)
+ animation->notifyRunningChanged(newState == QAbstractAnimationJob::Running);
}
-
/*!
\qmlproperty bool QtQuick2::Behavior::enabled
const QVariant ¤tValue = d->property.read();
d->targetValue = value;
- if (d->animation->qtAnimation()->duration() != -1
- && d->animation->qtAnimation()->state() != QAbstractAnimation::Stopped) {
+ if (d->animationInstance && d->animationInstance->duration() != -1
+ && !d->animationInstance->isStopped()) {
d->blockRunningChanged = true;
- d->animation->qtAnimation()->stop();
+ d->animationInstance->stop();
}
QDeclarativeStateOperation::ActionList actions;
actions << action;
QList<QDeclarativeProperty> after;
- d->animation->transition(actions, after, QDeclarativeAbstractAnimation::Forward);
- d->animation->qtAnimation()->start();
+ QAbstractAnimationJob *prev = d->animationInstance;
+ d->animationInstance = d->animation->transition(actions, after, QDeclarativeAbstractAnimation::Forward);
+ if (d->animationInstance != prev) {
+ d->animationInstance->addAnimationChangeListener(d, QAbstractAnimationJob::StateChange);
+ if (prev)
+ delete prev;
+ }
+ d->animationInstance->start();
d->blockRunningChanged = false;
if (!after.contains(d->property))
QDeclarativePropertyPrivate::write(d->property, value, QDeclarativePropertyPrivate::BypassInterceptor | QDeclarativePropertyPrivate::DontRemoveBinding);
d->animation->setDefaultTarget(property);
QDeclarativeEnginePrivate *engPriv = QDeclarativeEnginePrivate::get(qmlEngine(this));
- engPriv->registerFinalizeCallback(this, this->metaObject()->indexOfSlot("componentFinalized()"));
+ static int finalizedIdx = -1;
+ if (finalizedIdx < 0)
+ finalizedIdx = metaObject()->indexOfSlot("componentFinalized()");
+ engPriv->registerFinalizeCallback(this, finalizedIdx);
}
void QDeclarativeBehavior::componentFinalized()
#include <private/qdeclarativepropertyvalueinterceptor_p.h>
#include <qdeclarative.h>
-#include <QtCore/QAbstractAnimation>
QT_BEGIN_HEADER
private Q_SLOTS:
void componentFinalized();
- void qtAnimationStateChanged(QAbstractAnimation::State,QAbstractAnimation::State);
};
QT_END_NAMESPACE
QT_BEGIN_NAMESPACE
-QSmoothedAnimation::QSmoothedAnimation(QObject *parent)
- : QAbstractAnimation(parent), to(0), velocity(200), userDuration(-1), maximumEasingTime(-1),
+
+QSmoothedAnimationTimer::QSmoothedAnimationTimer(QSmoothedAnimation *animation, QObject *parent)
+ : QTimer(parent)
+ , m_animation(animation)
+{
+ connect(this, SIGNAL(timeout()), this, SLOT(stopAnimation()));
+}
+
+QSmoothedAnimationTimer::~QSmoothedAnimationTimer()
+{
+}
+
+void QSmoothedAnimationTimer::stopAnimation()
+{
+ m_animation->stop();
+}
+
+QSmoothedAnimation::QSmoothedAnimation(QDeclarativeSmoothedAnimationPrivate *priv)
+ : QAbstractAnimationJob(), to(0), velocity(200), userDuration(-1), maximumEasingTime(-1),
reversingMode(QDeclarativeSmoothedAnimation::Eased), initialVelocity(0),
- trackVelocity(0), initialValue(0), invert(false), finalDuration(-1), lastTime(0)
+ trackVelocity(0), initialValue(0), invert(false), finalDuration(-1), lastTime(0),
+ useDelta(false), delayedStopTimer(new QSmoothedAnimationTimer(this)), animationTemplate(priv)
+{
+ delayedStopTimer->setInterval(DELAY_STOP_TIMER_INTERVAL);
+ delayedStopTimer->setSingleShot(true);
+}
+
+QSmoothedAnimation::~QSmoothedAnimation()
{
+ delete delayedStopTimer;
+ if (animationTemplate) {
+ if (target.object()) {
+ QHash<QDeclarativeProperty, QSmoothedAnimation* >::iterator it =
+ animationTemplate->activeAnimations.find(target);
+ if (it != animationTemplate->activeAnimations.end() && it.value() == this)
+ animationTemplate->activeAnimations.erase(it);
+ } else {
+ //target is no longer valid, need to search linearly
+ QHash<QDeclarativeProperty, QSmoothedAnimation* >::iterator it;
+ for (it = animationTemplate->activeAnimations.begin(); it != animationTemplate->activeAnimations.end(); ++it) {
+ if (it.value() == this) {
+ animationTemplate->activeAnimations.erase(it);
+ break;
+ }
+ }
+ }
+ }
}
void QSmoothedAnimation::restart()
{
initialVelocity = trackVelocity;
- if (state() != QAbstractAnimation::Running)
- start();
- else
+ if (isRunning())
init();
+ else
+ start();
}
-void QSmoothedAnimation::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State /*oldState*/)
+void QSmoothedAnimation::prepareForRestart()
{
- if (newState == QAbstractAnimation::Running)
+ initialVelocity = trackVelocity;
+ if (isRunning()) {
+ //we are joining a new wrapper group while running, our times need to be restarted
+ useDelta = true;
init();
+ lastTime = 0;
+ } else {
+ useDelta = false;
+ //we'll be started when the group starts, which will force an init()
+ }
}
-void QSmoothedAnimation::timerEvent(QTimerEvent *event)
+void QSmoothedAnimation::updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State /*oldState*/)
{
- if (event->timerId() == delayedStopTimer.timerId()) {
- delayedStopTimer.stop();
- stop();
- } else {
- QAbstractAnimation::timerEvent(event);
- }
+ if (newState == QAbstractAnimationJob::Running)
+ init();
}
void QSmoothedAnimation::delayedStop()
{
- if (!delayedStopTimer.isActive())
- delayedStopTimer.start(DELAY_STOP_TIMER_INTERVAL, this);
+ if (!delayedStopTimer->isActive())
+ delayedStopTimer->start();
}
int QSmoothedAnimation::duration() const
void QSmoothedAnimation::updateCurrentTime(int t)
{
- qreal time_seconds = qreal(t - lastTime) / 1000.;
+ qreal time_seconds = useDelta ? qreal(QDeclarativeAnimationTimer::instance()->currentDelta()) / 1000. : qreal(t - lastTime) / 1000.;
+ if (useDelta)
+ useDelta = false;
qreal value = easeFollow(time_seconds);
value *= (invert? -1.0: 1.0);
return;
}
- if (delayedStopTimer.isActive())
- delayedStopTimer.stop();
+ if (delayedStopTimer->isActive())
+ delayedStopTimer->stop();
initialValue = target.read().toReal();
lastTime = this->currentTime();
QDeclarativeSmoothedAnimation::~QDeclarativeSmoothedAnimation()
{
+
}
QDeclarativeSmoothedAnimationPrivate::QDeclarativeSmoothedAnimationPrivate()
- : wrapperGroup(new QParallelAnimationGroup), anim(new QSmoothedAnimation)
+ : anim(0)
{
- Q_Q(QDeclarativeSmoothedAnimation);
- QDeclarative_setParent_noEvent(wrapperGroup, q);
- QDeclarative_setParent_noEvent(anim, q);
+ anim = new QSmoothedAnimation;
+}
+
+QDeclarativeSmoothedAnimationPrivate::~QDeclarativeSmoothedAnimationPrivate()
+{
+ delete anim;
+ QHash<QDeclarativeProperty, QSmoothedAnimation* >::iterator it;
+ for (it = activeAnimations.begin(); it != activeAnimations.end(); ++it) {
+ it.value()->clearTemplate();
+ }
}
void QDeclarativeSmoothedAnimationPrivate::updateRunningAnimations()
}
}
-QAbstractAnimation* QDeclarativeSmoothedAnimation::qtAnimation()
-{
- Q_D(QDeclarativeSmoothedAnimation);
- return d->wrapperGroup;
-}
-
-void QDeclarativeSmoothedAnimation::transition(QDeclarativeStateActions &actions,
+QAbstractAnimationJob* QDeclarativeSmoothedAnimation::transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction)
{
+ Q_UNUSED(direction);
Q_D(QDeclarativeSmoothedAnimation);
- QDeclarativeNumberAnimation::transition(actions, modified, direction);
-
- if (!d->actions)
- return;
- QSet<QAbstractAnimation*> anims;
- for (int i = 0; i < d->actions->size(); i++) {
- QSmoothedAnimation *ease;
- bool needsRestart;
- if (!d->activeAnimations.contains((*d->actions)[i].property)) {
- ease = new QSmoothedAnimation();
- d->wrapperGroup->addAnimation(ease);
- d->activeAnimations.insert((*d->actions)[i].property, ease);
- needsRestart = false;
- } else {
- ease = d->activeAnimations.value((*d->actions)[i].property);
- needsRestart = true;
+ QDeclarativeStateActions dataActions = QDeclarativePropertyAnimation::createTransitionActions(actions, modified);
+
+ QParallelAnimationGroupJob *wrapperGroup = new QParallelAnimationGroupJob();
+
+ if (!dataActions.isEmpty()) {
+ QSet<QAbstractAnimationJob*> anims;
+ for (int i = 0; i < dataActions.size(); i++) {
+ QSmoothedAnimation *ease;
+ bool isActive;
+ if (!d->activeAnimations.contains(dataActions[i].property)) {
+ ease = new QSmoothedAnimation(d);
+ d->activeAnimations.insert(dataActions[i].property, ease);
+ ease->target = dataActions[i].property;
+ isActive = false;
+ } else {
+ ease = d->activeAnimations.value(dataActions[i].property);
+ isActive = true;
+ }
+ wrapperGroup->appendAnimation(initInstance(ease));
+
+ ease->to = dataActions[i].toValue.toReal();
+
+ // copying public members from main value holder animation
+ ease->maximumEasingTime = d->anim->maximumEasingTime;
+ ease->reversingMode = d->anim->reversingMode;
+ ease->velocity = d->anim->velocity;
+ ease->userDuration = d->anim->userDuration;
+
+ ease->initialVelocity = ease->trackVelocity;
+
+ if (isActive)
+ ease->prepareForRestart();
+ anims.insert(ease);
}
- ease->target = (*d->actions)[i].property;
- ease->to = (*d->actions)[i].toValue.toReal();
-
- // copying public members from main value holder animation
- ease->maximumEasingTime = d->anim->maximumEasingTime;
- ease->reversingMode = d->anim->reversingMode;
- ease->velocity = d->anim->velocity;
- ease->userDuration = d->anim->userDuration;
-
- ease->initialVelocity = ease->trackVelocity;
-
- if (needsRestart)
- ease->init();
- anims.insert(ease);
- }
- for (int i = d->wrapperGroup->animationCount() - 1; i >= 0 ; --i) {
- if (!anims.contains(d->wrapperGroup->animationAt(i))) {
- QSmoothedAnimation *ease = static_cast<QSmoothedAnimation*>(d->wrapperGroup->animationAt(i));
- d->activeAnimations.remove(ease->target);
- d->wrapperGroup->takeAnimation(i);
- delete ease;
+ foreach (QSmoothedAnimation *ease, d->activeAnimations.values()){
+ if (!anims.contains(ease)) {
+ ease->clearTemplate();
+ d->activeAnimations.remove(ease->target);
+ }
}
}
+ return wrapperGroup;
}
/*!
class QDeclarativeProperty;
class QDeclarativeSmoothedAnimationPrivate;
-class Q_AUTOTEST_EXPORT QDeclarativeSmoothedAnimation : public QDeclarativeNumberAnimation
+class Q_QUICK_PRIVATE_EXPORT QDeclarativeSmoothedAnimation : public QDeclarativeNumberAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QDeclarativeSmoothedAnimation)
int maximumEasingTime() const;
void setMaximumEasingTime(int);
- virtual void transition(QDeclarativeStateActions &actions,
+ virtual QAbstractAnimationJob* transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction);
- QAbstractAnimation* qtAnimation();
-
Q_SIGNALS:
void velocityChanged();
void reversingModeChanged();
**
****************************************************************************/
-#ifndef QDECLARATIVESMOOTHEDANIMATION_P_H
-#define QDECLARATIVESMOOTHEDANIMATION_P_H
+#ifndef QDECLARATIVESMOOTHEDANIMATION2_P_H
+#define QDECLARATIVESMOOTHEDANIMATION2_P_H
//
// W A R N I N G
#include "qdeclarativeanimation_p_p.h"
-#include <qparallelanimationgroup.h>
+#include "private/qparallelanimationgroupjob_p.h"
#include <private/qobject_p.h>
#include <QBasicTimer>
QT_BEGIN_NAMESPACE
+class QSmoothedAnimation;
+class QSmoothedAnimationTimer : public QTimer
+{
+ Q_OBJECT
+public:
+ explicit QSmoothedAnimationTimer(QSmoothedAnimation *animation, QObject *parent = 0);
+ ~QSmoothedAnimationTimer();
+public Q_SLOTS:
+ void stopAnimation();
+private:
+ QSmoothedAnimation *m_animation;
+};
-class Q_AUTOTEST_EXPORT QSmoothedAnimation : public QAbstractAnimation
+class QDeclarativeSmoothedAnimationPrivate;
+class Q_AUTOTEST_EXPORT QSmoothedAnimation : public QAbstractAnimationJob
{
+ Q_DISABLE_COPY(QSmoothedAnimation)
public:
- QSmoothedAnimation(QObject *parent=0);
+ QSmoothedAnimation(QDeclarativeSmoothedAnimationPrivate * = 0);
+ ~QSmoothedAnimation();
qreal to;
qreal velocity;
int userDuration;
void restart();
void init();
+ void prepareForRestart();
+ void clearTemplate() { animationTemplate = 0; }
+
protected:
virtual void updateCurrentTime(int);
- virtual void updateState(QAbstractAnimation::State, QAbstractAnimation::State);
- virtual void timerEvent(QTimerEvent *);
+ virtual void updateState(QAbstractAnimationJob::State, QAbstractAnimationJob::State);
private:
qreal easeFollow(qreal);
qreal s; // Total s
int lastTime;
+ bool useDelta;
bool recalc();
void delayedStop();
-
- QBasicTimer delayedStopTimer;
+ QSmoothedAnimationTimer *delayedStopTimer;
+ QDeclarativeSmoothedAnimationPrivate *animationTemplate;
};
class QDeclarativeSmoothedAnimationPrivate : public QDeclarativePropertyAnimationPrivate
Q_DECLARE_PUBLIC(QDeclarativeSmoothedAnimation)
public:
QDeclarativeSmoothedAnimationPrivate();
+ ~QDeclarativeSmoothedAnimationPrivate();
void updateRunningAnimations();
- QParallelAnimationGroup *wrapperGroup;
QSmoothedAnimation *anim;
QHash<QDeclarativeProperty, QSmoothedAnimation*> activeAnimations;
};
QT_END_NAMESPACE
-#endif // QDECLARATIVESMOOTHEDANIMATION_P_H
+#endif // QDECLARATIVESMOOTHEDANIMATION2_P_H
#include "qdeclarativeanimation_p_p.h"
#include <private/qdeclarativeproperty_p.h>
+#include "private/qparallelanimationgroupjob_p.h"
#include <QtCore/qdebug.h>
#include <limits.h>
#include <math.h>
-QT_BEGIN_NAMESPACE
+#define DELAY_STOP_TIMER_INTERVAL 32
+QT_BEGIN_NAMESPACE
-class QDeclarativeSpringAnimationPrivate : public QDeclarativePropertyAnimationPrivate
+class QDeclarativeSpringAnimationPrivate;
+class Q_AUTOTEST_EXPORT QSpringAnimation : public QAbstractAnimationJob
{
- Q_DECLARE_PUBLIC(QDeclarativeSpringAnimation)
+ Q_DISABLE_COPY(QSpringAnimation)
public:
-
-
- struct SpringAnimation {
- SpringAnimation()
- : currentValue(0), to(0), velocity(0), start(0), duration(0) {}
- qreal currentValue;
- qreal to;
- qreal velocity;
- int start;
- int duration;
+ QSpringAnimation(QDeclarativeSpringAnimationPrivate * = 0);
+
+ ~QSpringAnimation();
+ int duration() const;
+ void restart();
+ void init();
+
+ qreal currentValue;
+ qreal to;
+ qreal velocity;
+ int startTime;
+ int dura;
+ int lastTime;
+ int stopTime;
+ enum Mode {
+ Track,
+ Velocity,
+ Spring
};
- QHash<QDeclarativeProperty, SpringAnimation> activeAnimations;
+ Mode mode;
+ QDeclarativeProperty target;
- qreal maxVelocity;
qreal velocityms;
- int lastTime;
+ qreal maxVelocity;
qreal mass;
qreal spring;
qreal damping;
bool useMass : 1;
bool haveModulus : 1;
+ bool useDelta : 1;
+ typedef QHash<QDeclarativeProperty, QSpringAnimation*> ActiveAnimationHash;
- enum Mode {
- Track,
- Velocity,
- Spring
- };
- Mode mode;
+ void clearTemplate() { animationTemplate = 0; }
+protected:
+ virtual void updateCurrentTime(int time);
+ virtual void updateState(QAbstractAnimationJob::State, QAbstractAnimationJob::State);
+
+private:
+ QDeclarativeSpringAnimationPrivate *animationTemplate;
+};
+
+class QDeclarativeSpringAnimationPrivate : public QDeclarativePropertyAnimationPrivate
+{
+ Q_DECLARE_PUBLIC(QDeclarativeSpringAnimation)
+public:
QDeclarativeSpringAnimationPrivate()
- : maxVelocity(0), velocityms(0), lastTime(0)
- , mass(1.0), spring(0.), damping(0.), epsilon(0.01)
- , modulus(0.0), useMass(false), haveModulus(false)
- , mode(Track), clock(0)
- { }
-
- void tick(int time);
- bool animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed);
+ : QDeclarativePropertyAnimationPrivate()
+ , velocityms(0)
+ , maxVelocity(0)
+ , mass(1.0)
+ , spring(0.)
+ , damping(0.)
+ , epsilon(0.01)
+ , modulus(0.0)
+ , useMass(false)
+ , haveModulus(false)
+ , mode(QSpringAnimation::Track)
+ { elapsed.start(); }
+
void updateMode();
+ qreal velocityms;
+ qreal maxVelocity;
+ qreal mass;
+ qreal spring;
+ qreal damping;
+ qreal epsilon;
+ qreal modulus;
+
+ bool useMass : 1;
+ bool haveModulus : 1;
+ QSpringAnimation::Mode mode;
- typedef QTickAnimationProxy<QDeclarativeSpringAnimationPrivate, &QDeclarativeSpringAnimationPrivate::tick> Clock;
- Clock *clock;
+ QSpringAnimation::ActiveAnimationHash activeAnimations;
+ QElapsedTimer elapsed;
};
-void QDeclarativeSpringAnimationPrivate::tick(int time)
+QSpringAnimation::QSpringAnimation(QDeclarativeSpringAnimationPrivate *priv)
+ : QAbstractAnimationJob()
+ , currentValue(0)
+ , to(0)
+ , velocity(0)
+ , startTime(0)
+ , dura(0)
+ , lastTime(0)
+ , stopTime(-1)
+ , mode(Track)
+ , velocityms(0)
+ , maxVelocity(0)
+ , mass(1.0)
+ , spring(0.)
+ , damping(0.)
+ , epsilon(0.01)
+ , modulus(0.0)
+ , useMass(false)
+ , haveModulus(false)
+ , useDelta(false)
+ , animationTemplate(priv)
+{
+}
+
+QSpringAnimation::~QSpringAnimation()
+{
+ if (animationTemplate) {
+ if (target.object()) {
+ QSpringAnimation::ActiveAnimationHash::iterator it =
+ animationTemplate->activeAnimations.find(target);
+ if (it != animationTemplate->activeAnimations.end() && it.value() == this)
+ animationTemplate->activeAnimations.erase(it);
+ } else {
+ //target is no longer valid, need to search linearly
+ QSpringAnimation::ActiveAnimationHash::iterator it;
+ for (it = animationTemplate->activeAnimations.begin(); it != animationTemplate->activeAnimations.end(); ++it) {
+ if (it.value() == this) {
+ animationTemplate->activeAnimations.erase(it);
+ break;
+ }
+ }
+ }
+ }
+}
+
+int QSpringAnimation::duration() const
+{
+ return -1;
+}
+
+void QSpringAnimation::restart()
+{
+ if (isRunning() || (stopTime != -1 && (animationTemplate->elapsed.elapsed() - stopTime) < DELAY_STOP_TIMER_INTERVAL)) {
+ useDelta = true;
+ init();
+ lastTime = 0;
+ } else {
+ useDelta = false;
+ //init() will be triggered when group starts
+ }
+}
+
+void QSpringAnimation::init()
+{
+ lastTime = startTime = 0;
+ stopTime = -1;
+}
+
+void QSpringAnimation::updateCurrentTime(int time)
{
if (mode == Track) {
- clock->stop();
+ stop();
return;
}
- int elapsed = time - lastTime;
+
+ int elapsed = useDelta ? QDeclarativeAnimationTimer::instance()->currentDelta() : time - lastTime;
+ if (useDelta) {
+ startTime = time - elapsed;
+ useDelta = false;
+ }
+
if (!elapsed)
return;
+ int count = elapsed / 16;
+
if (mode == Spring) {
if (elapsed < 16) // capped at 62fps.
return;
- int count = elapsed / 16;
lastTime = time - (elapsed - count * 16);
} else {
lastTime = time;
}
- QMutableHashIterator<QDeclarativeProperty, SpringAnimation> it(activeAnimations);
- while (it.hasNext()) {
- it.next();
- if (animate(it.key(), it.value(), elapsed))
- it.remove();
- }
-
- if (activeAnimations.isEmpty())
- clock->stop();
-}
-
-bool QDeclarativeSpringAnimationPrivate::animate(const QDeclarativeProperty &property, SpringAnimation &animation, int elapsed)
-{
- qreal srcVal = animation.to;
+ qreal srcVal = to;
- bool stop = false;
+ bool stopped = false;
if (haveModulus) {
- animation.currentValue = fmod(animation.currentValue, modulus);
+ currentValue = fmod(currentValue, modulus);
srcVal = fmod(srcVal, modulus);
}
if (mode == Spring) {
// Real men solve the spring DEs using RK4.
// We'll do something much simpler which gives a result that looks fine.
- int count = elapsed / 16;
for (int i = 0; i < count; ++i) {
- qreal diff = srcVal - animation.currentValue;
+ qreal diff = srcVal - currentValue;
if (haveModulus && qAbs(diff) > modulus / 2) {
if (diff < 0)
diff += modulus;
diff -= modulus;
}
if (useMass)
- animation.velocity = animation.velocity + (spring * diff - damping * animation.velocity) / mass;
+ velocity = velocity + (spring * diff - damping * velocity) / mass;
else
- animation.velocity = animation.velocity + spring * diff - damping * animation.velocity;
+ velocity = velocity + spring * diff - damping * velocity;
if (maxVelocity > 0.) {
// limit velocity
- if (animation.velocity > maxVelocity)
- animation.velocity = maxVelocity;
- else if (animation.velocity < -maxVelocity)
- animation.velocity = -maxVelocity;
+ if (velocity > maxVelocity)
+ velocity = maxVelocity;
+ else if (velocity < -maxVelocity)
+ velocity = -maxVelocity;
}
- animation.currentValue += animation.velocity * 16.0 / 1000.0;
+ currentValue += velocity * 16.0 / 1000.0;
if (haveModulus) {
- animation.currentValue = fmod(animation.currentValue, modulus);
- if (animation.currentValue < 0.0)
- animation.currentValue += modulus;
+ currentValue = fmod(currentValue, modulus);
+ if (currentValue < 0.0)
+ currentValue += modulus;
}
}
- if (qAbs(animation.velocity) < epsilon && qAbs(srcVal - animation.currentValue) < epsilon) {
- animation.velocity = 0.0;
- animation.currentValue = srcVal;
- stop = true;
+ if (qAbs(velocity) < epsilon && qAbs(srcVal - currentValue) < epsilon) {
+ velocity = 0.0;
+ currentValue = srcVal;
+ stopped = true;
}
} else {
qreal moveBy = elapsed * velocityms;
- qreal diff = srcVal - animation.currentValue;
+ qreal diff = srcVal - currentValue;
if (haveModulus && qAbs(diff) > modulus / 2) {
if (diff < 0)
diff += modulus;
diff -= modulus;
}
if (diff > 0) {
- animation.currentValue += moveBy;
+ currentValue += moveBy;
if (haveModulus)
- animation.currentValue = fmod(animation.currentValue, modulus);
+ currentValue = fmod(currentValue, modulus);
} else {
- animation.currentValue -= moveBy;
- if (haveModulus && animation.currentValue < 0.0)
- animation.currentValue = fmod(animation.currentValue, modulus) + modulus;
+ currentValue -= moveBy;
+ if (haveModulus && currentValue < 0.0)
+ currentValue = fmod(currentValue, modulus) + modulus;
}
- if (lastTime - animation.start >= animation.duration) {
- animation.currentValue = animation.to;
- stop = true;
+ if (lastTime - startTime >= dura) {
+ currentValue = to;
+ stopped = true;
}
}
- qreal old_to = animation.to;
+ qreal old_to = to;
- QDeclarativePropertyPrivate::write(property, animation.currentValue,
+ QDeclarativePropertyPrivate::write(target, currentValue,
QDeclarativePropertyPrivate::BypassInterceptor |
QDeclarativePropertyPrivate::DontRemoveBinding);
- return (stop && old_to == animation.to); // do not stop if we got restarted
+ if (stopped && old_to == to) { // do not stop if we got restarted
+ stopTime = animationTemplate->elapsed.elapsed();
+ stop();
+ }
+}
+
+void QSpringAnimation::updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State /*oldState*/)
+{
+ if (newState == QAbstractAnimationJob::Running)
+ init();
}
void QDeclarativeSpringAnimationPrivate::updateMode()
{
if (spring == 0. && maxVelocity == 0.)
- mode = Track;
+ mode = QSpringAnimation::Track;
else if (spring > 0.)
- mode = Spring;
+ mode = QSpringAnimation::Spring;
else {
- mode = Velocity;
- QHash<QDeclarativeProperty, SpringAnimation>::iterator it;
+ mode = QSpringAnimation::Velocity;
+ QSpringAnimation::ActiveAnimationHash::iterator it;
for (it = activeAnimations.begin(); it != activeAnimations.end(); ++it) {
- SpringAnimation &animation = *it;
- animation.start = lastTime;
- qreal dist = qAbs(animation.currentValue - animation.to);
+ QSpringAnimation *animation = *it;
+ animation->startTime = animation->lastTime;
+ qreal dist = qAbs(animation->currentValue - animation->to);
if (haveModulus && dist > modulus / 2)
dist = modulus - fmod(dist, modulus);
- animation.duration = dist / velocityms;
+ animation->dura = dist / velocityms;
}
}
}
QDeclarativeSpringAnimation::QDeclarativeSpringAnimation(QObject *parent)
: QDeclarativeNumberAnimation(*(new QDeclarativeSpringAnimationPrivate),parent)
{
- Q_D(QDeclarativeSpringAnimation);
- d->clock = new QDeclarativeSpringAnimationPrivate::Clock(d, this);
}
QDeclarativeSpringAnimation::~QDeclarativeSpringAnimation()
{
+ Q_D(QDeclarativeSpringAnimation);
+ QSpringAnimation::ActiveAnimationHash::iterator it;
+ for (it = d->activeAnimations.begin(); it != d->activeAnimations.end(); ++it) {
+ it.value()->clearTemplate();
+ }
}
/*!
}
}
-void QDeclarativeSpringAnimation::transition(QDeclarativeStateActions &actions,
- QDeclarativeProperties &modified,
- TransitionDirection direction)
+QAbstractAnimationJob* QDeclarativeSpringAnimation::transition(QDeclarativeStateActions &actions,
+ QDeclarativeProperties &modified,
+ TransitionDirection direction)
{
Q_D(QDeclarativeSpringAnimation);
Q_UNUSED(direction);
- if (d->clock->state() != QAbstractAnimation::Running) {
- d->lastTime = 0;
- }
-
- QDeclarativeNumberAnimation::transition(actions, modified, direction);
-
- if (!d->actions)
- return;
+ QParallelAnimationGroupJob *wrapperGroup = new QParallelAnimationGroupJob();
+
+ QDeclarativeStateActions dataActions = QDeclarativeNumberAnimation::createTransitionActions(actions, modified);
+ if (!dataActions.isEmpty()) {
+ QSet<QAbstractAnimationJob*> anims;
+ for (int i = 0; i < dataActions.size(); ++i) {
+ QSpringAnimation *animation;
+ bool needsRestart = false;
+ const QDeclarativeProperty &property = dataActions.at(i).property;
+ if (d->activeAnimations.contains(property)) {
+ animation = d->activeAnimations[property];
+ needsRestart = true;
+ } else {
+ animation = new QSpringAnimation(d);
+ d->activeAnimations.insert(property, animation);
+ animation->target = property;
+ }
+ wrapperGroup->appendAnimation(initInstance(animation));
+
+ animation->to = dataActions.at(i).toValue.toReal();
+ animation->startTime = 0;
+ animation->velocityms = d->velocityms;
+ animation->mass = d->mass;
+ animation->spring = d->spring;
+ animation->damping = d->damping;
+ animation->epsilon = d->epsilon;
+ animation->modulus = d->modulus;
+ animation->useMass = d->useMass;
+ animation->haveModulus = d->haveModulus;
+ animation->mode = d->mode;
+ animation->dura = -1;
+ animation->maxVelocity = d->maxVelocity;
- if (!d->actions->isEmpty()) {
- for (int i = 0; i < d->actions->size(); ++i) {
- const QDeclarativeProperty &property = d->actions->at(i).property;
- QDeclarativeSpringAnimationPrivate::SpringAnimation &animation
- = d->activeAnimations[property];
- animation.to = d->actions->at(i).toValue.toReal();
- animation.start = d->lastTime;
if (d->fromIsDefined)
- animation.currentValue = d->actions->at(i).fromValue.toReal();
+ animation->currentValue = dataActions.at(i).fromValue.toReal();
else
- animation.currentValue = property.read().toReal();
- if (d->mode == QDeclarativeSpringAnimationPrivate::Velocity) {
- qreal dist = qAbs(animation.currentValue - animation.to);
+ animation->currentValue = property.read().toReal();
+ if (animation->mode == QSpringAnimation::Velocity) {
+ qreal dist = qAbs(animation->currentValue - animation->to);
if (d->haveModulus && dist > d->modulus / 2)
dist = d->modulus - fmod(dist, d->modulus);
- animation.duration = dist / d->velocityms;
+ animation->dura = dist / animation->velocityms;
+ }
+
+ if (needsRestart)
+ animation->restart();
+ anims.insert(animation);
+ }
+ foreach (QSpringAnimation *anim, d->activeAnimations.values()){
+ if (!anims.contains(anim)) {
+ anim->clearTemplate();
+ d->activeAnimations.remove(anim->target);
}
}
}
-}
-
-
-QAbstractAnimation *QDeclarativeSpringAnimation::qtAnimation()
-{
- Q_D(QDeclarativeSpringAnimation);
- return d->clock;
+ return wrapperGroup;
}
QT_END_NAMESPACE
QT_BEGIN_NAMESPACE
class QDeclarativeSpringAnimationPrivate;
-class Q_AUTOTEST_EXPORT QDeclarativeSpringAnimation : public QDeclarativeNumberAnimation
+class Q_QUICK_PRIVATE_EXPORT QDeclarativeSpringAnimation : public QDeclarativeNumberAnimation
{
Q_OBJECT
Q_DECLARE_PRIVATE(QDeclarativeSpringAnimation)
qreal modulus() const;
void setModulus(qreal modulus);
- virtual void transition(QDeclarativeStateActions &actions,
+ virtual QAbstractAnimationJob* transition(QDeclarativeStateActions &actions,
QDeclarativeProperties &modified,
TransitionDirection direction);
-protected:
- virtual QAbstractAnimation *qtAnimation();
-
Q_SIGNALS:
void modulusChanged();
void massChanged();
Construct a new QDeclarativeTimeLine with the specified \a parent.
*/
QDeclarativeTimeLine::QDeclarativeTimeLine(QObject *parent)
-: QAbstractAnimation(parent)
+ : QObject(parent)
{
d = new QDeclarativeTimeLinePrivate(this);
}
//
#include <QtCore/QObject>
-#include <QtCore/QAbstractAnimation>
+#include "private/qabstractanimationjob_p.h"
QT_BEGIN_NAMESPACE
class QDeclarativeTimeLineCallback;
struct QDeclarativeTimeLinePrivate;
class QDeclarativeTimeLineObject;
-class Q_AUTOTEST_EXPORT QDeclarativeTimeLine : public QAbstractAnimation
+class Q_AUTOTEST_EXPORT QDeclarativeTimeLine : public QObject, QAbstractAnimationJob
{
Q_OBJECT
public:
#include "qdeclarativetimer_p.h"
#include <QtCore/qcoreapplication.h>
-#include <QtCore/qpauseanimation.h>
+#include "private/qpauseanimationjob_p.h"
#include <qdebug.h>
#include <private/qobject_p.h>
-class QDeclarativeTimerPrivate : public QObjectPrivate
+class QDeclarativeTimerPrivate : public QObjectPrivate, public QAnimation2ChangeListener
{
Q_DECLARE_PUBLIC(QDeclarativeTimer)
public:
QDeclarativeTimerPrivate()
: interval(1000), running(false), repeating(false), triggeredOnStart(false)
, classBegun(false), componentComplete(false), firstTick(true) {}
+
+ virtual void animationFinished(QAbstractAnimationJob *);
+ virtual void animationCurrentLoopChanged(QAbstractAnimationJob *) { Q_Q(QDeclarativeTimer); q->ticked(); }
+
int interval;
- QPauseAnimation pause;
+ QPauseAnimationJob pause;
bool running : 1;
bool repeating : 1;
bool triggeredOnStart : 1;
: QObject(*(new QDeclarativeTimerPrivate), parent)
{
Q_D(QDeclarativeTimer);
- connect(&d->pause, SIGNAL(currentLoopChanged(int)), this, SLOT(ticked()));
- connect(&d->pause, SIGNAL(finished()), this, SLOT(finished()));
+ d->pause.addAnimationChangeListener(d, QAbstractAnimationJob::Completion | QAbstractAnimationJob::CurrentLoop);
d->pause.setLoopCount(1);
d->pause.setDuration(d->interval);
}
d->firstTick = false;
}
-void QDeclarativeTimer::finished()
+void QDeclarativeTimerPrivate::animationFinished(QAbstractAnimationJob *)
{
- Q_D(QDeclarativeTimer);
- if (d->repeating || !d->running)
+ Q_Q(QDeclarativeTimer);
+ if (repeating || !running)
return;
- d->running = false;
- d->firstTick = false;
- emit triggered();
- emit runningChanged();
+ running = false;
+ firstTick = false;
+ emit q->triggered();
+ emit q->runningChanged();
}
QT_END_NAMESPACE
#include <qdeclarative.h>
#include <QtCore/qobject.h>
-#include <QtCore/qabstractanimation.h>
#include <private/qtquickglobal_p.h>
private Q_SLOTS:
void ticked();
- void finished();
};
QT_END_NAMESPACE
#include "qdeclarativeanimation_p_p.h"
#include "qdeclarativetransitionmanager_p_p.h"
-#include <QParallelAnimationGroup>
+#include "private/qparallelanimationgroupjob_p.h"
QT_BEGIN_NAMESPACE
\sa {QML Animation and Transitions}, {declarative/animation/states}{states example}, {qmlstates}{States}, {QtDeclarative}
*/
+QDeclarativeTransitionInstance::QDeclarativeTransitionInstance()
+ : m_anim(0)
+{
+}
+
+QDeclarativeTransitionInstance::~QDeclarativeTransitionInstance()
+{
+ delete m_anim;
+}
+
+void QDeclarativeTransitionInstance::start()
+{
+ if (m_anim)
+ m_anim->start();
+}
+
+void QDeclarativeTransitionInstance::stop()
+{
+ if (m_anim)
+ m_anim->stop();
+}
+
+bool QDeclarativeTransitionInstance::isRunning() const
+{
+ return m_anim && m_anim->state() == QAbstractAnimationJob::Running;
+}
+
+
//ParallelAnimationWrapper allows us to do a "callback" when the animation finishes, rather than connecting
//and disconnecting signals and slots frequently
-class ParallelAnimationWrapper : public QParallelAnimationGroup
+class ParallelAnimationWrapper : public QParallelAnimationGroupJob
{
- Q_OBJECT
public:
- ParallelAnimationWrapper(QObject *parent = 0) : QParallelAnimationGroup(parent) {}
- QDeclarativeTransitionPrivate *trans;
+ ParallelAnimationWrapper() : QParallelAnimationGroupJob() {}
+ QDeclarativeTransitionManager *manager;
+
protected:
- virtual void updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState);
+ virtual void updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState);
};
class QDeclarativeTransitionPrivate : public QObjectPrivate
public:
QDeclarativeTransitionPrivate()
: fromState(QLatin1String("*")), toState(QLatin1String("*")),
- reversed(false), reversible(false), enabled(true), manager(0)
+ reversed(false), reversible(false), enabled(true)
{
- group.trans = this;
}
QString fromState;
bool reversed;
bool reversible;
bool enabled;
- ParallelAnimationWrapper group;
- QDeclarativeTransitionManager *manager;
- void complete()
- {
- manager->complete();
- }
static void append_animation(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list, QDeclarativeAbstractAnimation *a);
static int animation_count(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list);
static QDeclarativeAbstractAnimation* animation_at(QDeclarativeListProperty<QDeclarativeAbstractAnimation> *list, int pos);
{
QDeclarativeTransition *q = static_cast<QDeclarativeTransition *>(list->object);
q->d_func()->animations.append(a);
- q->d_func()->group.addAnimation(a->qtAnimation());
a->setDisableUserControl();
}
QDeclarativeTransition *q = static_cast<QDeclarativeTransition *>(list->object);
while (q->d_func()->animations.count()) {
QDeclarativeAbstractAnimation *firstAnim = q->d_func()->animations.at(0);
- q->d_func()->group.removeAnimation(firstAnim->qtAnimation());
q->d_func()->animations.removeAll(firstAnim);
}
}
-void ParallelAnimationWrapper::updateState(QAbstractAnimation::State newState, QAbstractAnimation::State oldState)
+void ParallelAnimationWrapper::updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState)
{
- QParallelAnimationGroup::updateState(newState, oldState);
+ QParallelAnimationGroupJob::updateState(newState, oldState);
if (newState == Stopped && (duration() == -1
- || (direction() == QAbstractAnimation::Forward && currentLoopTime() == duration())
- || (direction() == QAbstractAnimation::Backward && currentLoopTime() == 0)))
+ || (direction() == QAbstractAnimationJob::Forward && currentLoopTime() == duration())
+ || (direction() == QAbstractAnimationJob::Backward && currentLoopTime() == 0)))
{
- trans->complete();
+ manager->complete();
}
}
-
QDeclarativeTransition::QDeclarativeTransition(QObject *parent)
: QObject(*(new QDeclarativeTransitionPrivate), parent)
{
QDeclarativeTransition::~QDeclarativeTransition()
{
-}
-
-void QDeclarativeTransition::stop()
-{
Q_D(QDeclarativeTransition);
- d->group.stop();
}
void QDeclarativeTransition::setReversed(bool r)
d->reversed = r;
}
-void QDeclarativeTransition::prepare(QDeclarativeStateOperation::ActionList &actions,
+QDeclarativeTransitionInstance *QDeclarativeTransition::prepare(QDeclarativeStateOperation::ActionList &actions,
QList<QDeclarativeProperty> &after,
QDeclarativeTransitionManager *manager)
{
qmlExecuteDeferred(this);
- if (d->reversed) {
- for (int ii = d->animations.count() - 1; ii >= 0; --ii) {
- d->animations.at(ii)->transition(actions, after, QDeclarativeAbstractAnimation::Backward);
- }
- } else {
- for (int ii = 0; ii < d->animations.count(); ++ii) {
- d->animations.at(ii)->transition(actions, after, QDeclarativeAbstractAnimation::Forward);
- }
+ ParallelAnimationWrapper *group = new ParallelAnimationWrapper();
+ group->manager = manager;
+
+ QDeclarativeAbstractAnimation::TransitionDirection direction = d->reversed ? QDeclarativeAbstractAnimation::Backward : QDeclarativeAbstractAnimation::Forward;
+ int start = d->reversed ? d->animations.count() - 1 : 0;
+ int end = d->reversed ? -1 : d->animations.count();
+
+ QAbstractAnimationJob *anim = 0;
+ for (int i = start; i != end;) {
+ anim = d->animations.at(i)->transition(actions, after, direction);
+ if (anim)
+ d->reversed ? group->prependAnimation(anim) : group->appendAnimation(anim);
+ d->reversed ? --i : ++i;
}
- d->manager = manager;
- d->group.setDirection(d->reversed ? QAbstractAnimation::Backward : QAbstractAnimation::Forward);
- d->group.start();
+ group->setDirection(d->reversed ? QAbstractAnimationJob::Backward : QAbstractAnimationJob::Forward);
+
+ QDeclarativeTransitionInstance *wrapper = new QDeclarativeTransitionInstance;
+ wrapper->m_anim = group;
+ return wrapper;
}
/*!
QT_END_NAMESPACE
-#include <qdeclarativetransition.moc>
+//#include <qdeclarativetransition.moc>
#define QDECLARATIVETRANSITION_H
#include "qdeclarativestate_p.h"
-
#include <qdeclarative.h>
#include <QtCore/qobject.h>
class QDeclarativeAbstractAnimation;
class QDeclarativeTransitionPrivate;
class QDeclarativeTransitionManager;
+class QDeclarativeTransition;
+class QAbstractAnimationJob;
+
+class Q_QUICK_EXPORT QDeclarativeTransitionInstance
+{
+public:
+ QDeclarativeTransitionInstance();
+ ~QDeclarativeTransitionInstance();
+
+ void start();
+ void stop();
+
+ bool isRunning() const;
+
+private:
+ QAbstractAnimationJob *m_anim;
+ friend class QDeclarativeTransition;
+};
+
class Q_QUICK_EXPORT QDeclarativeTransition : public QObject
{
Q_OBJECT
QDeclarativeListProperty<QDeclarativeAbstractAnimation> animations();
- void prepare(QDeclarativeStateOperation::ActionList &actions,
+ QDeclarativeTransitionInstance *prepare(QDeclarativeStateOperation::ActionList &actions,
QList<QDeclarativeProperty> &after,
QDeclarativeTransitionManager *end);
void setReversed(bool r);
- void stop();
Q_SIGNALS:
void fromChanged();
#include "qdeclarativetransition_p.h"
#include "qdeclarativestate_p_p.h"
-#include "qdeclarativestate_p.h"
#include <private/qdeclarativebinding_p.h>
#include <private/qdeclarativeglobal_p.h>
{
public:
QDeclarativeTransitionManagerPrivate()
- : state(0) {}
+ : state(0), transitionInstance(0) {}
void applyBindings();
typedef QList<QDeclarativeSimpleAction> SimpleActionList;
QDeclarativeState *state;
- QDeclarativeGuard<QDeclarativeTransition> transition;
+ QDeclarativeTransitionInstance *transitionInstance;
QDeclarativeStateOperation::ActionList bindingsList;
SimpleActionList completeList;
};
QDeclarativeTransitionManager::~QDeclarativeTransitionManager()
{
+ delete d->transitionInstance;
delete d; d = 0;
}
+bool QDeclarativeTransitionManager::isRunning() const
+{
+ return d->transitionInstance && d->transitionInstance->isRunning();
+}
+
void QDeclarativeTransitionManager::complete()
{
d->applyBindings();
if (d->state)
static_cast<QDeclarativeStatePrivate*>(QObjectPrivate::get(d->state))->complete();
+
+ finished();
}
void QDeclarativeTransitionManagerPrivate::applyBindings()
bindingsList.clear();
}
+void QDeclarativeTransitionManager::finished()
+{
+}
+
void QDeclarativeTransitionManager::transition(const QList<QDeclarativeAction> &list,
QDeclarativeTransition *transition)
{
if (transition) {
QList<QDeclarativeProperty> touched;
- d->transition = transition;
- d->transition->prepare(applyList, touched, this);
+ QDeclarativeTransitionInstance *oldInstance = d->transitionInstance;
+ d->transitionInstance = transition->prepare(applyList, touched, this);
+ d->transitionInstance->start();
+ if (oldInstance && oldInstance != d->transitionInstance)
+ delete oldInstance;
// Modify the action list to remove actions handled in the transition
for (int ii = 0; ii < applyList.count(); ++ii) {
void QDeclarativeTransitionManager::cancel()
{
- if (d->transition) {
- // ### this could potentially trigger a complete in rare circumstances
- d->transition->stop();
- d->transition = 0;
- }
+ if (d->transitionInstance && d->transitionInstance->isRunning())
+ d->transitionInstance->stop();
for(int i = 0; i < d->bindingsList.count(); ++i) {
QDeclarativeAction action = d->bindingsList[i];
//
#include "qdeclarativestateoperations_p.h"
+#include "qdeclarativeanimation_p.h"
QT_BEGIN_NAMESPACE
QDeclarativeTransitionManager();
~QDeclarativeTransitionManager();
+ bool isRunning() const;
+
void transition(const QList<QDeclarativeAction> &, QDeclarativeTransition *transition);
void cancel();
+protected:
+ virtual void finished();
+
private:
Q_DISABLE_COPY(QDeclarativeTransitionManager)
QDeclarativeTransitionManagerPrivate *d;
void setState(QDeclarativeState *);
friend class QDeclarativeState;
- friend class QDeclarativeTransitionPrivate;
+ friend class ParallelAnimationWrapper;
};
QT_END_NAMESPACE
#include "qdeclarativetransition_p.h"
#include <qdeclarativeinfo.h>
#include <private/qdeclarativetypenotavailable_p.h>
+#include <private/qdeclarativeanimationcontroller_p.h>
#include <QtCore/qcoreapplication.h>
#include <QtGui/QInputPanel>
qmlRegisterType<QDeclarativeScriptAction>("QtQuick",2,0,"ScriptAction");
qmlRegisterType<QDeclarativeSequentialAnimation>("QtQuick",2,0,"SequentialAnimation");
qmlRegisterType<QDeclarativeSpringAnimation>("QtQuick",2,0,"SpringAnimation");
+ qmlRegisterType<QDeclarativeAnimationController>("QtQuick",2,0,"AnimationController");
qmlRegisterType<QDeclarativeStateChangeScript>("QtQuick",2,0,"StateChangeScript");
qmlRegisterType<QDeclarativeStateGroup>("QtQuick",2,0,"StateGroup");
qmlRegisterType<QDeclarativeState>("QtQuick",2,0,"State");
$$PWD/qdeclarativesystempalette.cpp \
$$PWD/qdeclarativespringanimation.cpp \
$$PWD/qdeclarativesmoothedanimation.cpp \
+ $$PWD/qdeclarativeanimationcontroller.cpp \
$$PWD/qdeclarativestate.cpp\
$$PWD/qdeclarativetransitionmanager.cpp \
$$PWD/qdeclarativestateoperations.cpp \
$$PWD/qdeclarativeanimation_p_p.h \
$$PWD/qdeclarativesystempalette_p.h \
$$PWD/qdeclarativespringanimation_p.h \
+ $$PWD/qdeclarativeanimationcontroller_p.h \
$$PWD/qdeclarativesmoothedanimation_p.h \
$$PWD/qdeclarativesmoothedanimation_p_p.h \
$$PWD/qdeclarativestate_p.h\
$$PWD/qdeclarativechangeset_p.h \
$$PWD/qdeclarativelistcompositor_p.h \
$$PWD/qdeclarativepathinterpolator_p.h \
- $$PWD/qdeclarativesvgparser_p.h
+ $$PWD/qdeclarativesvgparser_p.h
\ No newline at end of file
--- /dev/null
+TEMPLATE=subdirs
+SUBDIRS=\
+ qabstractanimationjob \
+ qanimationgroupjob \
+ qparallelanimationgroupjob \
+ qpauseanimationjob \
+ qsequentialanimationgroupjob
--- /dev/null
+CONFIG += testcase parallel_test
+macx:CONFIG -= app_bundle
+TARGET = tst_qabstractanimationjob
+QT = core-private declarative-private testlib
+SOURCES = tst_qabstractanimationjob.cpp
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include <QtDeclarative/private/qabstractanimationjob_p.h>
+#include <QtDeclarative/private/qanimationgroupjob_p.h>
+#include <QtTest>
+
+class tst_QAbstractAnimationJob : public QObject
+{
+ Q_OBJECT
+private slots:
+ void construction();
+ void destruction();
+ void currentLoop();
+ void currentLoopTime();
+ void currentTime();
+ void direction();
+ void group();
+ void loopCount();
+ void state();
+ void totalDuration();
+ void avoidJumpAtStart();
+ void avoidJumpAtStartWithStop();
+ void avoidJumpAtStartWithRunning();
+};
+
+class TestableQAbstractAnimation : public QAbstractAnimationJob
+{
+public:
+ TestableQAbstractAnimation() : m_duration(10) {}
+ virtual ~TestableQAbstractAnimation() {};
+
+ int duration() const { return m_duration; }
+ virtual void updateCurrentTime(int) {}
+
+ void setDuration(int duration) { m_duration = duration; }
+private:
+ int m_duration;
+};
+
+class DummyQAnimationGroup : public QAnimationGroupJob
+{
+public:
+ int duration() const { return 10; }
+ virtual void updateCurrentTime(int) {}
+};
+
+void tst_QAbstractAnimationJob::construction()
+{
+ TestableQAbstractAnimation anim;
+}
+
+void tst_QAbstractAnimationJob::destruction()
+{
+ TestableQAbstractAnimation *anim = new TestableQAbstractAnimation;
+ delete anim;
+}
+
+void tst_QAbstractAnimationJob::currentLoop()
+{
+ TestableQAbstractAnimation anim;
+ QCOMPARE(anim.currentLoop(), 0);
+}
+
+void tst_QAbstractAnimationJob::currentLoopTime()
+{
+ TestableQAbstractAnimation anim;
+ QCOMPARE(anim.currentLoopTime(), 0);
+}
+
+void tst_QAbstractAnimationJob::currentTime()
+{
+ TestableQAbstractAnimation anim;
+ QCOMPARE(anim.currentTime(), 0);
+ anim.setCurrentTime(10);
+ QCOMPARE(anim.currentTime(), 10);
+}
+
+void tst_QAbstractAnimationJob::direction()
+{
+ TestableQAbstractAnimation anim;
+ QCOMPARE(anim.direction(), QAbstractAnimationJob::Forward);
+ anim.setDirection(QAbstractAnimationJob::Backward);
+ QCOMPARE(anim.direction(), QAbstractAnimationJob::Backward);
+ anim.setDirection(QAbstractAnimationJob::Forward);
+ QCOMPARE(anim.direction(), QAbstractAnimationJob::Forward);
+}
+
+void tst_QAbstractAnimationJob::group()
+{
+ TestableQAbstractAnimation *anim = new TestableQAbstractAnimation;
+ DummyQAnimationGroup group;
+ group.appendAnimation(anim);
+ QCOMPARE(anim->group(), &group);
+}
+
+void tst_QAbstractAnimationJob::loopCount()
+{
+ TestableQAbstractAnimation anim;
+ QCOMPARE(anim.loopCount(), 1);
+ anim.setLoopCount(10);
+ QCOMPARE(anim.loopCount(), 10);
+}
+
+void tst_QAbstractAnimationJob::state()
+{
+ TestableQAbstractAnimation anim;
+ QCOMPARE(anim.state(), QAbstractAnimationJob::Stopped);
+}
+
+void tst_QAbstractAnimationJob::totalDuration()
+{
+ TestableQAbstractAnimation anim;
+ QCOMPARE(anim.duration(), 10);
+ anim.setLoopCount(5);
+ QCOMPARE(anim.totalDuration(), 50);
+}
+
+void tst_QAbstractAnimationJob::avoidJumpAtStart()
+{
+ TestableQAbstractAnimation anim;
+ anim.setDuration(1000);
+
+ /*
+ the timer shouldn't actually start until we hit the event loop,
+ so the sleep should have no effect
+ */
+ anim.start();
+ QTest::qSleep(300);
+ QCoreApplication::processEvents();
+ QVERIFY(anim.currentTime() < 50);
+}
+
+void tst_QAbstractAnimationJob::avoidJumpAtStartWithStop()
+{
+ TestableQAbstractAnimation anim;
+ anim.setDuration(1000);
+
+ TestableQAbstractAnimation anim2;
+ anim2.setDuration(1000);
+
+ TestableQAbstractAnimation anim3;
+ anim3.setDuration(1000);
+
+ anim.start();
+ QTest::qWait(300);
+ anim.stop();
+
+ /*
+ same test as avoidJumpAtStart, but after there is a
+ running animation that is stopped
+ */
+ anim2.start();
+ QTest::qSleep(300);
+ anim3.start();
+ QCoreApplication::processEvents();
+ QVERIFY(anim2.currentTime() < 50);
+ QVERIFY(anim3.currentTime() < 50);
+}
+
+void tst_QAbstractAnimationJob::avoidJumpAtStartWithRunning()
+{
+ TestableQAbstractAnimation anim;
+ anim.setDuration(2000);
+
+ TestableQAbstractAnimation anim2;
+ anim2.setDuration(1000);
+
+ TestableQAbstractAnimation anim3;
+ anim3.setDuration(1000);
+
+ anim.start();
+ QTest::qWait(300); //make sure timer has started
+
+ /*
+ same test as avoidJumpAtStart, but with an
+ existing running animation
+ */
+ anim2.start();
+ QTest::qSleep(300); //force large delta for next tick
+ anim3.start();
+ QCoreApplication::processEvents();
+ QVERIFY(anim2.currentTime() < 50);
+ QVERIFY(anim3.currentTime() < 50);
+}
+
+
+QTEST_MAIN(tst_QAbstractAnimationJob)
+
+#include "tst_qabstractanimationjob.moc"
--- /dev/null
+CONFIG += testcase parallel_test
+macx:CONFIG -= app_bundle
+TARGET = tst_qanimationgroupjob
+QT = core-private declarative-private testlib
+SOURCES = tst_qanimationgroupjob.cpp
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDeclarative/private/qanimationgroupjob_p.h>
+#include <QtDeclarative/private/qsequentialanimationgroupjob_p.h>
+#include <QtDeclarative/private/qparallelanimationgroupjob_p.h>
+
+Q_DECLARE_METATYPE(QAbstractAnimationJob::State)
+
+class tst_QAnimationGroupJob : public QObject
+{
+ Q_OBJECT
+public Q_SLOTS:
+ void initTestCase();
+
+private slots:
+ void construction();
+ void emptyGroup();
+ void setCurrentTime();
+ void addChildTwice();
+};
+
+void tst_QAnimationGroupJob::initTestCase()
+{
+ qRegisterMetaType<QAbstractAnimationJob::State>("QAbstractAnimationJob::State");
+}
+
+void tst_QAnimationGroupJob::construction()
+{
+ QSequentialAnimationGroupJob animationgroup;
+}
+
+class TestableGenericAnimation : public QAbstractAnimationJob
+{
+public:
+ TestableGenericAnimation(int duration = 250) : m_duration(duration) {}
+ int duration() const { return m_duration; }
+
+private:
+ int m_duration;
+};
+
+class UncontrolledAnimation : public QObject, public QAbstractAnimationJob
+{
+ Q_OBJECT
+public:
+ UncontrolledAnimation()
+ : id(0)
+ {
+ }
+
+ int duration() const { return -1; /* not time driven */ }
+
+protected:
+ void timerEvent(QTimerEvent *event)
+ {
+ if (event->timerId() == id)
+ stop();
+ }
+
+ void updateRunning(bool running)
+ {
+ if (running) {
+ id = startTimer(500);
+ } else {
+ killTimer(id);
+ id = 0;
+ }
+ }
+
+private:
+ int id;
+};
+
+class StateChangeListener: public QAnimation2ChangeListener
+{
+public:
+ virtual void animationStateChanged(QAbstractAnimationJob *, QAbstractAnimationJob::State newState, QAbstractAnimationJob::State)
+ {
+ states << newState;
+ }
+
+ int count()
+ {
+ return states.count();
+ }
+
+ QList<QAbstractAnimationJob::State> states;
+};
+
+void tst_QAnimationGroupJob::emptyGroup()
+{
+ QSequentialAnimationGroupJob group;
+ StateChangeListener groupStateChangedSpy;
+ group.addAnimationChangeListener(&groupStateChangedSpy, QAbstractAnimationJob::StateChange);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ group.start();
+
+ QCOMPARE(groupStateChangedSpy.count(), 2);
+
+ QCOMPARE(groupStateChangedSpy.states.at(0), QAnimationGroupJob::Running);
+ QCOMPARE(groupStateChangedSpy.states.at(1), QAnimationGroupJob::Stopped);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+
+ QTest::ignoreMessage(QtWarningMsg, "QAbstractAnimationJob::pause: Cannot pause a stopped animation");
+ group.pause();
+
+ QCOMPARE(groupStateChangedSpy.count(), 2);
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+
+ group.start();
+
+ QCOMPARE(groupStateChangedSpy.states.at(2),
+ QAnimationGroupJob::Running);
+ QCOMPARE(groupStateChangedSpy.states.at(3),
+ QAnimationGroupJob::Stopped);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+
+ group.stop();
+
+ QCOMPARE(groupStateChangedSpy.count(), 4);
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+}
+
+void tst_QAnimationGroupJob::setCurrentTime()
+{
+ // was originally sequence operating on same object/property
+ QSequentialAnimationGroupJob *sequence = new QSequentialAnimationGroupJob();
+ QAbstractAnimationJob *a1_s_o1 = new TestableGenericAnimation;
+ QAbstractAnimationJob *a2_s_o1 = new TestableGenericAnimation;
+ QAbstractAnimationJob *a3_s_o1 = new TestableGenericAnimation;
+ a2_s_o1->setLoopCount(3);
+ sequence->appendAnimation(a1_s_o1);
+ sequence->appendAnimation(a2_s_o1);
+ sequence->appendAnimation(a3_s_o1);
+
+ // was originally sequence operating on different object/properties
+ QAnimationGroupJob *sequence2 = new QSequentialAnimationGroupJob();
+ QAbstractAnimationJob *a1_s_o2 = new TestableGenericAnimation;
+ QAbstractAnimationJob *a1_s_o3 = new TestableGenericAnimation;
+ sequence2->appendAnimation(a1_s_o2);
+ sequence2->appendAnimation(a1_s_o3);
+
+ // was originally parallel operating on different object/properties
+ QAnimationGroupJob *parallel = new QParallelAnimationGroupJob();
+ QAbstractAnimationJob *a1_p_o1 = new TestableGenericAnimation;
+ QAbstractAnimationJob *a1_p_o2 = new TestableGenericAnimation;
+ QAbstractAnimationJob *a1_p_o3 = new TestableGenericAnimation;
+ a1_p_o2->setLoopCount(3);
+ parallel->appendAnimation(a1_p_o1);
+ parallel->appendAnimation(a1_p_o2);
+ parallel->appendAnimation(a1_p_o3);
+
+ QAbstractAnimationJob *notTimeDriven = new UncontrolledAnimation;
+ QCOMPARE(notTimeDriven->totalDuration(), -1);
+
+ QAbstractAnimationJob *loopsForever = new TestableGenericAnimation;
+ loopsForever->setLoopCount(-1);
+ QCOMPARE(loopsForever->totalDuration(), -1);
+
+ QParallelAnimationGroupJob group;
+ group.appendAnimation(sequence);
+ group.appendAnimation(sequence2);
+ group.appendAnimation(parallel);
+ group.appendAnimation(notTimeDriven);
+ group.appendAnimation(loopsForever);
+
+ // Current time = 1
+ group.setCurrentTime(1);
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(sequence->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(sequence2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(parallel->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_p_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_p_o2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_p_o3->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(notTimeDriven->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(loopsForever->state(), QAnimationGroupJob::Stopped);
+
+ QCOMPARE(group.currentLoopTime(), 1);
+ QCOMPARE(sequence->currentLoopTime(), 1);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 1);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 0);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 1);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 0);
+ QCOMPARE(a1_p_o1->currentLoopTime(), 1);
+ QCOMPARE(a1_p_o2->currentLoopTime(), 1);
+ QCOMPARE(a1_p_o3->currentLoopTime(), 1);
+ QCOMPARE(notTimeDriven->currentLoopTime(), 1);
+ QCOMPARE(loopsForever->currentLoopTime(), 1);
+
+ // Current time = 250
+ group.setCurrentTime(250);
+ QCOMPARE(group.currentLoopTime(), 250);
+ QCOMPARE(sequence->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 0);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 0);
+ QCOMPARE(a1_p_o1->currentLoopTime(), 250);
+ QCOMPARE(a1_p_o2->currentLoopTime(), 0);
+ QCOMPARE(a1_p_o2->currentLoop(), 1);
+ QCOMPARE(a1_p_o3->currentLoopTime(), 250);
+ QCOMPARE(notTimeDriven->currentLoopTime(), 250);
+ QCOMPARE(loopsForever->currentLoopTime(), 0);
+ QCOMPARE(loopsForever->currentLoop(), 1);
+ QCOMPARE(sequence->currentAnimation(), a2_s_o1);
+
+ // Current time = 251
+ group.setCurrentTime(251);
+ QCOMPARE(group.currentLoopTime(), 251);
+ QCOMPARE(sequence->currentLoopTime(), 251);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 1);
+ QCOMPARE(a2_s_o1->currentLoop(), 0);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 0);
+ QCOMPARE(sequence2->currentLoopTime(), 251);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 1);
+ QCOMPARE(a1_p_o1->currentLoopTime(), 250);
+ QCOMPARE(a1_p_o2->currentLoopTime(), 1);
+ QCOMPARE(a1_p_o2->currentLoop(), 1);
+ QCOMPARE(a1_p_o3->currentLoopTime(), 250);
+ QCOMPARE(notTimeDriven->currentLoopTime(), 251);
+ QCOMPARE(loopsForever->currentLoopTime(), 1);
+ QCOMPARE(sequence->currentAnimation(), a2_s_o1);
+}
+
+void tst_QAnimationGroupJob::addChildTwice()
+{
+ QAbstractAnimationJob *subGroup;
+ QAbstractAnimationJob *subGroup2;
+ QAnimationGroupJob *parent = new QSequentialAnimationGroupJob();
+
+ subGroup = new QAbstractAnimationJob;
+ parent->appendAnimation(subGroup);
+ parent->appendAnimation(subGroup);
+ QVERIFY(parent->firstChild() && !parent->firstChild()->nextSibling());
+
+ parent->clear();
+
+ QVERIFY(!parent->firstChild());
+
+ // adding the same item twice to a group will remove the item from its current position
+ // and append it to the end
+ subGroup = new QAbstractAnimationJob;
+ parent->appendAnimation(subGroup);
+ subGroup2 = new QAbstractAnimationJob;
+ parent->appendAnimation(subGroup2);
+
+ QCOMPARE(parent->firstChild(), subGroup);
+ QCOMPARE(parent->lastChild(), subGroup2);
+
+ parent->appendAnimation(subGroup);
+
+ QCOMPARE(parent->firstChild(), subGroup2);
+ QCOMPARE(parent->lastChild(), subGroup);
+
+ delete parent;
+}
+
+QTEST_MAIN(tst_QAnimationGroupJob)
+#include "tst_qanimationgroupjob.moc"
--- /dev/null
+CONFIG += testcase
+macx:CONFIG -= app_bundle
+TARGET = tst_qparallelanimationgroupjob
+QT = core-private gui declarative-private testlib
+SOURCES = tst_qparallelanimationgroupjob.cpp
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDeclarative/private/qparallelanimationgroupjob_p.h>
+
+Q_DECLARE_METATYPE(QAbstractAnimationJob::State)
+
+class tst_QParallelAnimationGroupJob : public QObject
+{
+ Q_OBJECT
+public Q_SLOTS:
+ void initTestCase();
+
+private slots:
+ void construction();
+ void setCurrentTime();
+ void stateChanged();
+ void clearGroup();
+ void propagateGroupUpdateToChildren();
+ void updateChildrenWithRunningGroup();
+ void deleteChildrenWithRunningGroup();
+ void startChildrenWithStoppedGroup();
+ void stopGroupWithRunningChild();
+ void startGroupWithRunningChild();
+ void zeroDurationAnimation();
+ void stopUncontrolledAnimations();
+ void loopCount_data();
+ void loopCount();
+ void addAndRemoveDuration();
+ void pauseResume();
+
+ void crashWhenRemovingUncontrolledAnimation();
+};
+
+void tst_QParallelAnimationGroupJob::initTestCase()
+{
+ qRegisterMetaType<QAbstractAnimationJob::State>("QAbstractAnimationJob::State");
+#if defined(Q_OS_MAC) || defined(Q_OS_WINCE)
+ // give the mac/wince app start event queue time to clear
+ QTest::qWait(1000);
+#endif
+}
+
+void tst_QParallelAnimationGroupJob::construction()
+{
+ QParallelAnimationGroupJob animationgroup;
+}
+
+class TestAnimation : public QAbstractAnimationJob
+{
+public:
+ TestAnimation(int duration = 250) : m_duration(duration) {}
+ int duration() const { return m_duration; }
+
+private:
+ int m_duration;
+};
+
+class UncontrolledAnimation : public QObject, public QAbstractAnimationJob
+{
+ Q_OBJECT
+public:
+ UncontrolledAnimation()
+ : id(0)
+ {
+ }
+
+ int duration() const { return -1; /* not time driven */ }
+
+protected:
+ void timerEvent(QTimerEvent *event)
+ {
+ if (event->timerId() == id)
+ stop();
+ }
+
+ void updateRunning(bool running)
+ {
+ if (running) {
+ id = startTimer(500);
+ } else {
+ killTimer(id);
+ id = 0;
+ }
+ }
+
+private:
+ int id;
+};
+
+class StateChangeListener: public QAnimation2ChangeListener
+{
+public:
+ virtual void animationStateChanged(QAbstractAnimationJob *, QAbstractAnimationJob::State newState, QAbstractAnimationJob::State)
+ {
+ states << newState;
+ }
+
+ void clear() { states.clear(); }
+ int count() { return states.count(); }
+
+ QList<QAbstractAnimationJob::State> states;
+};
+
+class FinishedListener: public QAnimation2ChangeListener
+{
+public:
+ FinishedListener() : m_count(0) {}
+
+ virtual void animationFinished(QAbstractAnimationJob *) { ++m_count; }
+ void clear() { m_count = 0; }
+ int count() { return m_count; }
+
+private:
+ int m_count;
+};
+
+void tst_QParallelAnimationGroupJob::setCurrentTime()
+{
+ // originally was parallel operating on different object/properties
+ QAnimationGroupJob *parallel = new QParallelAnimationGroupJob();
+ TestAnimation *a1_p_o1 = new TestAnimation;
+ TestAnimation *a1_p_o2 = new TestAnimation;
+ TestAnimation *a1_p_o3 = new TestAnimation;
+ a1_p_o2->setLoopCount(3);
+ parallel->appendAnimation(a1_p_o1);
+ parallel->appendAnimation(a1_p_o2);
+ parallel->appendAnimation(a1_p_o3);
+
+ UncontrolledAnimation *notTimeDriven = new UncontrolledAnimation;
+ QCOMPARE(notTimeDriven->totalDuration(), -1);
+
+ TestAnimation *loopsForever = new TestAnimation;
+ loopsForever->setLoopCount(-1);
+ QCOMPARE(loopsForever->totalDuration(), -1);
+
+ QParallelAnimationGroupJob group;
+ group.appendAnimation(parallel);
+ group.appendAnimation(notTimeDriven);
+ group.appendAnimation(loopsForever);
+
+ // Current time = 1
+ group.setCurrentTime(1);
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(parallel->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_p_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_p_o2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_p_o3->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(notTimeDriven->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(loopsForever->state(), QAnimationGroupJob::Stopped);
+
+ QCOMPARE(group.currentLoopTime(), 1);
+ QCOMPARE(a1_p_o1->currentLoopTime(), 1);
+ QCOMPARE(a1_p_o2->currentLoopTime(), 1);
+ QCOMPARE(a1_p_o3->currentLoopTime(), 1);
+ QCOMPARE(notTimeDriven->currentLoopTime(), 1);
+ QCOMPARE(loopsForever->currentLoopTime(), 1);
+
+ // Current time = 250
+ group.setCurrentTime(250);
+ QCOMPARE(group.currentLoopTime(), 250);
+ QCOMPARE(a1_p_o1->currentLoopTime(), 250);
+ QCOMPARE(a1_p_o2->currentLoopTime(), 0);
+ QCOMPARE(a1_p_o2->currentLoop(), 1);
+ QCOMPARE(a1_p_o3->currentLoopTime(), 250);
+ QCOMPARE(notTimeDriven->currentLoopTime(), 250);
+ QCOMPARE(loopsForever->currentLoopTime(), 0);
+ QCOMPARE(loopsForever->currentLoop(), 1);
+
+ // Current time = 251
+ group.setCurrentTime(251);
+ QCOMPARE(group.currentLoopTime(), 251);
+ QCOMPARE(a1_p_o1->currentLoopTime(), 250);
+ QCOMPARE(a1_p_o2->currentLoopTime(), 1);
+ QCOMPARE(a1_p_o2->currentLoop(), 1);
+ QCOMPARE(a1_p_o3->currentLoopTime(), 250);
+ QCOMPARE(notTimeDriven->currentLoopTime(), 251);
+ QCOMPARE(loopsForever->currentLoopTime(), 1);
+}
+
+void tst_QParallelAnimationGroupJob::stateChanged()
+{
+ //this ensures that the correct animations are started when starting the group
+ TestAnimation *anim1 = new TestAnimation(1000);
+ TestAnimation *anim2 = new TestAnimation(2000);
+ TestAnimation *anim3 = new TestAnimation(3000);
+ TestAnimation *anim4 = new TestAnimation(3000);
+
+ QParallelAnimationGroupJob group;
+ group.appendAnimation(anim1);
+ group.appendAnimation(anim2);
+ group.appendAnimation(anim3);
+ group.appendAnimation(anim4);
+
+ StateChangeListener spy1;
+ anim1->addAnimationChangeListener(&spy1, QAbstractAnimationJob::StateChange);
+ StateChangeListener spy2;
+ anim2->addAnimationChangeListener(&spy2, QAbstractAnimationJob::StateChange);
+ StateChangeListener spy3;
+ anim3->addAnimationChangeListener(&spy3, QAbstractAnimationJob::StateChange);
+ StateChangeListener spy4;
+ anim4->addAnimationChangeListener(&spy4, QAbstractAnimationJob::StateChange);
+
+ //first; let's start forward
+ group.start();
+ //all the animations should be started
+ QCOMPARE(spy1.count(), 1);
+ QCOMPARE(spy1.states.last(), TestAnimation::Running);
+ QCOMPARE(spy2.count(), 1);
+ QCOMPARE(spy2.states.last(), TestAnimation::Running);
+ QCOMPARE(spy3.count(), 1);
+ QCOMPARE(spy3.states.last(), TestAnimation::Running);
+ QCOMPARE(spy4.count(), 1);
+ QCOMPARE(spy4.states.last(), TestAnimation::Running);
+
+ group.setCurrentTime(1500); //anim1 should be finished
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(spy1.count(), 2);
+ QCOMPARE(spy1.states.last(), TestAnimation::Stopped);
+ QCOMPARE(spy2.count(), 1); //no change
+ QCOMPARE(spy3.count(), 1); //no change
+ QCOMPARE(spy4.count(), 1); //no change
+
+ group.setCurrentTime(2500); //anim2 should be finished
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(spy1.count(), 2); //no change
+ QCOMPARE(spy2.count(), 2);
+ QCOMPARE(spy2.states.last(), TestAnimation::Stopped);
+ QCOMPARE(spy3.count(), 1); //no change
+ QCOMPARE(spy4.count(), 1); //no change
+
+ group.setCurrentTime(3500); //everything should be finished
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(spy1.count(), 2); //no change
+ QCOMPARE(spy2.count(), 2); //no change
+ QCOMPARE(spy3.count(), 2);
+ QCOMPARE(spy3.states.last(), TestAnimation::Stopped);
+ QCOMPARE(spy4.count(), 2);
+ QCOMPARE(spy4.states.last(), TestAnimation::Stopped);
+
+ //cleanup
+ spy1.clear();
+ spy2.clear();
+ spy3.clear();
+ spy4.clear();
+
+ //now let's try to reverse that
+ group.setDirection(QAbstractAnimationJob::Backward);
+ group.start();
+
+ //only anim3 and anim4 should be started
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(spy1.count(), 0);
+ QCOMPARE(spy2.count(), 0);
+ QCOMPARE(spy3.count(), 1);
+ QCOMPARE(spy3.states.last(), TestAnimation::Running);
+ QCOMPARE(spy4.count(), 1);
+ QCOMPARE(spy4.states.last(), TestAnimation::Running);
+
+ group.setCurrentTime(1500); //anim2 should be started
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(spy1.count(), 0); //no change
+ QCOMPARE(spy2.count(), 1);
+ QCOMPARE(spy2.states.last(), TestAnimation::Running);
+ QCOMPARE(spy3.count(), 1); //no change
+ QCOMPARE(spy4.count(), 1); //no change
+
+ group.setCurrentTime(500); //anim1 is finally also started
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(spy1.count(), 1);
+ QCOMPARE(spy1.states.last(), TestAnimation::Running);
+ QCOMPARE(spy2.count(), 1); //no change
+ QCOMPARE(spy3.count(), 1); //no change
+ QCOMPARE(spy4.count(), 1); //no change
+
+ group.setCurrentTime(0); //everything should be stopped
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(spy1.count(), 2);
+ QCOMPARE(spy1.states.last(), TestAnimation::Stopped);
+ QCOMPARE(spy2.count(), 2);
+ QCOMPARE(spy2.states.last(), TestAnimation::Stopped);
+ QCOMPARE(spy3.count(), 2);
+ QCOMPARE(spy3.states.last(), TestAnimation::Stopped);
+ QCOMPARE(spy4.count(), 2);
+ QCOMPARE(spy4.states.last(), TestAnimation::Stopped);
+}
+
+void tst_QParallelAnimationGroupJob::clearGroup()
+{
+ QParallelAnimationGroupJob group;
+ static const int animationCount = 10;
+
+ for (int i = 0; i < animationCount; ++i) {
+ group.appendAnimation(new QParallelAnimationGroupJob);
+ }
+
+ int count = 0;
+ for (QAbstractAnimationJob *anim = group.firstChild(); anim; anim = anim->nextSibling())
+ ++count;
+ QCOMPARE(count, animationCount);
+
+ group.clear();
+
+ QVERIFY(!group.firstChild() && !group.lastChild());
+ QCOMPARE(group.currentLoopTime(), 0);
+}
+
+void tst_QParallelAnimationGroupJob::propagateGroupUpdateToChildren()
+{
+ // this test verifies if group state changes are updating its children correctly
+ QParallelAnimationGroupJob group;
+
+ TestAnimation anim1(100);
+ TestAnimation anim2(200);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped);
+
+ group.appendAnimation(&anim1);
+ group.appendAnimation(&anim2);
+
+ group.start();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Running);
+
+ group.pause();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Paused);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Paused);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Paused);
+
+ group.stop();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped);
+}
+
+void tst_QParallelAnimationGroupJob::updateChildrenWithRunningGroup()
+{
+ // assert that its possible to modify a child's state directly while their group is running
+ QParallelAnimationGroupJob group;
+
+ TestAnimation anim(200);
+
+ StateChangeListener groupStateChangedSpy;
+ group.addAnimationChangeListener(&groupStateChangedSpy, QAbstractAnimationJob::StateChange);
+ StateChangeListener childStateChangedSpy;
+ anim.addAnimationChangeListener(&childStateChangedSpy, QAbstractAnimationJob::StateChange);
+
+ QCOMPARE(groupStateChangedSpy.count(), 0);
+ QCOMPARE(childStateChangedSpy.count(), 0);
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim.state(), QAnimationGroupJob::Stopped);
+
+ group.appendAnimation(&anim);
+
+ group.start();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim.state(), QAnimationGroupJob::Running);
+
+ QCOMPARE(groupStateChangedSpy.count(), 1);
+ QCOMPARE(childStateChangedSpy.count(), 1);
+
+ QCOMPARE(groupStateChangedSpy.states.at(0), QAnimationGroupJob::Running);
+ QCOMPARE(childStateChangedSpy.states.at(0), QAnimationGroupJob::Running);
+
+ // starting directly a running child will not have any effect
+ anim.start();
+
+ QCOMPARE(groupStateChangedSpy.count(), 1);
+ QCOMPARE(childStateChangedSpy.count(), 1);
+
+ anim.pause();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim.state(), QAnimationGroupJob::Paused);
+
+ // in the animation stops directly, the group will still be running
+ anim.stop();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim.state(), QAnimationGroupJob::Stopped);
+
+ //cleanup
+ group.removeAnimationChangeListener(&groupStateChangedSpy, QAbstractAnimationJob::StateChange);
+ anim.removeAnimationChangeListener(&childStateChangedSpy, QAbstractAnimationJob::StateChange);
+}
+
+void tst_QParallelAnimationGroupJob::deleteChildrenWithRunningGroup()
+{
+ // test if children can be activated when their group is stopped
+ QParallelAnimationGroupJob group;
+
+ TestAnimation *anim1 = new TestAnimation(200);
+ group.appendAnimation(anim1);
+
+ QCOMPARE(group.duration(), anim1->duration());
+
+ group.start();
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim1->state(), QAnimationGroupJob::Running);
+
+ QTest::qWait(80);
+ QVERIFY(group.currentLoopTime() > 0);
+
+ delete anim1;
+ QVERIFY(!group.firstChild());
+ QCOMPARE(group.duration(), 0);
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(group.currentLoopTime(), 0); //that's the invariant
+}
+
+void tst_QParallelAnimationGroupJob::startChildrenWithStoppedGroup()
+{
+ // test if children can be activated when their group is stopped
+ QParallelAnimationGroupJob group;
+
+ TestAnimation anim1(200);
+ TestAnimation anim2(200);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped);
+
+ group.appendAnimation(&anim1);
+ group.appendAnimation(&anim2);
+
+ group.stop();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped);
+
+ anim1.start();
+ anim2.start();
+ anim2.pause();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Paused);
+}
+
+void tst_QParallelAnimationGroupJob::stopGroupWithRunningChild()
+{
+ // children that started independently will not be affected by a group stop
+ QParallelAnimationGroupJob group;
+
+ TestAnimation anim1(200);
+ TestAnimation anim2(200);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped);
+
+ group.appendAnimation(&anim1);
+ group.appendAnimation(&anim2);
+
+ anim1.start();
+ anim2.start();
+ anim2.pause();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Paused);
+
+ group.stop();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Paused);
+
+ anim1.stop();
+ anim2.stop();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped);
+}
+
+void tst_QParallelAnimationGroupJob::startGroupWithRunningChild()
+{
+ // as the group has precedence over its children, starting a group will restart all the children
+ QParallelAnimationGroupJob group;
+
+ TestAnimation anim1(200);
+ TestAnimation anim2(200);
+
+ StateChangeListener stateChangedSpy1;
+ anim1.addAnimationChangeListener(&stateChangedSpy1, QAbstractAnimationJob::StateChange);
+ StateChangeListener stateChangedSpy2;
+ anim2.addAnimationChangeListener(&stateChangedSpy2, QAbstractAnimationJob::StateChange);
+
+ QCOMPARE(stateChangedSpy1.count(), 0);
+ QCOMPARE(stateChangedSpy2.count(), 0);
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped);
+
+ group.appendAnimation(&anim1);
+ group.appendAnimation(&anim2);
+
+ anim1.start();
+ anim2.start();
+ anim2.pause();
+
+ QCOMPARE(stateChangedSpy1.states.at(0), QAnimationGroupJob::Running);
+ QCOMPARE(stateChangedSpy2.states.at(0), QAnimationGroupJob::Running);
+ QCOMPARE(stateChangedSpy2.states.at(1), QAnimationGroupJob::Paused);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Paused);
+
+ group.start();
+
+ QCOMPARE(stateChangedSpy1.count(), 3);
+ QCOMPARE(stateChangedSpy1.states.at(1), QAnimationGroupJob::Stopped);
+ QCOMPARE(stateChangedSpy1.states.at(2), QAnimationGroupJob::Running);
+
+ QCOMPARE(stateChangedSpy2.count(), 4);
+ QCOMPARE(stateChangedSpy2.states.at(2), QAnimationGroupJob::Stopped);
+ QCOMPARE(stateChangedSpy2.states.at(3), QAnimationGroupJob::Running);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Running);
+
+ //cleanup
+ anim1.removeAnimationChangeListener(&stateChangedSpy1, QAbstractAnimationJob::StateChange);
+ anim2.removeAnimationChangeListener(&stateChangedSpy2, QAbstractAnimationJob::StateChange);
+}
+
+void tst_QParallelAnimationGroupJob::zeroDurationAnimation()
+{
+ QParallelAnimationGroupJob group;
+
+ TestAnimation anim1(0);
+ TestAnimation anim2(100);
+ TestAnimation anim3(10);
+
+ StateChangeListener stateChangedSpy1;
+ anim1.addAnimationChangeListener(&stateChangedSpy1, QAbstractAnimationJob::StateChange);
+ FinishedListener finishedSpy1;
+ anim1.addAnimationChangeListener(&finishedSpy1, QAbstractAnimationJob::Completion);
+
+ StateChangeListener stateChangedSpy2;
+ anim2.addAnimationChangeListener(&stateChangedSpy2, QAbstractAnimationJob::StateChange);
+ FinishedListener finishedSpy2;
+ anim2.addAnimationChangeListener(&finishedSpy2, QAbstractAnimationJob::Completion);
+
+ StateChangeListener stateChangedSpy3;
+ anim3.addAnimationChangeListener(&stateChangedSpy3, QAbstractAnimationJob::StateChange);
+ FinishedListener finishedSpy3;
+ anim3.addAnimationChangeListener(&finishedSpy3, QAbstractAnimationJob::Completion);
+
+ group.appendAnimation(&anim1);
+ group.appendAnimation(&anim2);
+ group.appendAnimation(&anim3);
+ QCOMPARE(stateChangedSpy1.count(), 0);
+ group.start();
+ QCOMPARE(stateChangedSpy1.count(), 2);
+ QCOMPARE(finishedSpy1.count(), 1);
+ QCOMPARE(stateChangedSpy1.states.at(0), QAnimationGroupJob::Running);
+ QCOMPARE(stateChangedSpy1.states.at(1), QAnimationGroupJob::Stopped);
+
+ QCOMPARE(stateChangedSpy2.count(), 1);
+ QCOMPARE(finishedSpy2.count(), 0);
+ QCOMPARE(stateChangedSpy1.states.at(0), QAnimationGroupJob::Running);
+
+ QCOMPARE(stateChangedSpy3.count(), 1);
+ QCOMPARE(finishedSpy3.count(), 0);
+ QCOMPARE(stateChangedSpy3.states.at(0), QAnimationGroupJob::Running);
+
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim3.state(), QAnimationGroupJob::Running);
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+
+ group.stop();
+ group.setLoopCount(4);
+ stateChangedSpy1.clear();
+ stateChangedSpy2.clear();
+ stateChangedSpy3.clear();
+
+ group.start();
+ QCOMPARE(stateChangedSpy1.count(), 2);
+ QCOMPARE(stateChangedSpy2.count(), 1);
+ QCOMPARE(stateChangedSpy3.count(), 1);
+ group.setCurrentTime(50);
+ QCOMPARE(stateChangedSpy1.count(), 2);
+ QCOMPARE(stateChangedSpy2.count(), 1);
+ QCOMPARE(stateChangedSpy3.count(), 2);
+ group.setCurrentTime(150);
+ QCOMPARE(stateChangedSpy1.count(), 4);
+ QCOMPARE(stateChangedSpy2.count(), 3);
+ QCOMPARE(stateChangedSpy3.count(), 4);
+ group.setCurrentTime(50);
+ QCOMPARE(stateChangedSpy1.count(), 6);
+ QCOMPARE(stateChangedSpy2.count(), 5);
+ QCOMPARE(stateChangedSpy3.count(), 6);
+
+ //cleanup
+ anim1.removeAnimationChangeListener(&stateChangedSpy1, QAbstractAnimationJob::StateChange);
+ anim1.removeAnimationChangeListener(&finishedSpy1, QAbstractAnimationJob::Completion);
+ anim2.removeAnimationChangeListener(&stateChangedSpy2, QAbstractAnimationJob::StateChange);
+ anim2.removeAnimationChangeListener(&finishedSpy2, QAbstractAnimationJob::Completion);
+ anim3.removeAnimationChangeListener(&stateChangedSpy3, QAbstractAnimationJob::StateChange);
+ anim3.removeAnimationChangeListener(&finishedSpy3, QAbstractAnimationJob::Completion);
+}
+
+void tst_QParallelAnimationGroupJob::stopUncontrolledAnimations()
+{
+ QParallelAnimationGroupJob group;
+
+ TestAnimation anim1(0);
+
+ UncontrolledAnimation notTimeDriven;
+ QCOMPARE(notTimeDriven.totalDuration(), -1);
+
+ TestAnimation loopsForever(100);
+ loopsForever.setLoopCount(-1);
+
+ StateChangeListener stateChangedSpy;
+ anim1.addAnimationChangeListener(&stateChangedSpy, QAbstractAnimationJob::StateChange);
+
+ group.appendAnimation(&anim1);
+ group.appendAnimation(¬TimeDriven);
+ group.appendAnimation(&loopsForever);
+
+ group.start();
+
+ QCOMPARE(stateChangedSpy.count(), 2);
+ QCOMPARE(stateChangedSpy.states.at(0), QAnimationGroupJob::Running);
+ QCOMPARE(stateChangedSpy.states.at(1), QAnimationGroupJob::Stopped);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(notTimeDriven.state(), QAnimationGroupJob::Running);
+ QCOMPARE(loopsForever.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped);
+
+ notTimeDriven.stop();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(notTimeDriven.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(loopsForever.state(), QAnimationGroupJob::Running);
+
+ loopsForever.stop();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(notTimeDriven.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(loopsForever.state(), QAnimationGroupJob::Stopped);
+}
+
+struct AnimState {
+ AnimState(int time = -1) : time(time), state(-1) {}
+ AnimState(int time, int state) : time(time), state(state) {}
+ int time;
+ int state;
+};
+
+#define Running QAbstractAnimationJob::Running
+#define Stopped QAbstractAnimationJob::Stopped
+
+Q_DECLARE_METATYPE(AnimState)
+void tst_QParallelAnimationGroupJob::loopCount_data()
+{
+ QTest::addColumn<bool>("directionBackward");
+ QTest::addColumn<int>("setLoopCount");
+ QTest::addColumn<int>("initialGroupTime");
+ QTest::addColumn<int>("currentGroupTime");
+ QTest::addColumn<AnimState>("expected1");
+ QTest::addColumn<AnimState>("expected2");
+ QTest::addColumn<AnimState>("expected3");
+
+ // D U R A T I O N
+ // 100 60*2 0
+ // direction = Forward
+ QTest::newRow("50") << false << 3 << 0 << 50 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped);
+ QTest::newRow("100") << false << 3 << 0 << 100 << AnimState(100 ) << AnimState( 40, Running) << AnimState( 0, Stopped);
+ QTest::newRow("110") << false << 3 << 0 << 110 << AnimState(100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
+ QTest::newRow("120") << false << 3 << 0 << 120 << AnimState( 0, Running) << AnimState( 0, Running) << AnimState( 0, Stopped);
+
+ QTest::newRow("170") << false << 3 << 0 << 170 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped);
+ QTest::newRow("220") << false << 3 << 0 << 220 << AnimState(100 ) << AnimState( 40, Running) << AnimState( 0, Stopped);
+ QTest::newRow("230") << false << 3 << 0 << 230 << AnimState(100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
+ QTest::newRow("240") << false << 3 << 0 << 240 << AnimState( 0, Running) << AnimState( 0, Running) << AnimState( 0, Stopped);
+
+ QTest::newRow("290") << false << 3 << 0 << 290 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped);
+ QTest::newRow("340") << false << 3 << 0 << 340 << AnimState(100 ) << AnimState( 40, Running) << AnimState( 0, Stopped);
+ QTest::newRow("350") << false << 3 << 0 << 350 << AnimState(100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
+ QTest::newRow("360") << false << 3 << 0 << 360 << AnimState(100, Stopped) << AnimState( 60 ) << AnimState( 0, Stopped);
+
+ QTest::newRow("410") << false << 3 << 0 << 410 << AnimState(100, Stopped) << AnimState( 60, Stopped) << AnimState( 0, Stopped);
+ QTest::newRow("460") << false << 3 << 0 << 460 << AnimState(100, Stopped) << AnimState( 60, Stopped) << AnimState( 0, Stopped);
+ QTest::newRow("470") << false << 3 << 0 << 470 << AnimState(100, Stopped) << AnimState( 60, Stopped) << AnimState( 0, Stopped);
+ QTest::newRow("480") << false << 3 << 0 << 480 << AnimState(100, Stopped) << AnimState( 60, Stopped) << AnimState( 0, Stopped);
+
+ // direction = Forward, rewind
+ QTest::newRow("120-110") << false << 3 << 120 << 110 << AnimState( 0, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
+ QTest::newRow("120-50") << false << 3 << 120 << 50 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped);
+ QTest::newRow("120-0") << false << 3 << 120 << 0 << AnimState( 0, Running) << AnimState( 0, Running) << AnimState( 0, Stopped);
+ QTest::newRow("300-110") << false << 3 << 300 << 110 << AnimState( 0, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
+ QTest::newRow("300-50") << false << 3 << 300 << 50 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped);
+ QTest::newRow("300-0") << false << 3 << 300 << 0 << AnimState( 0, Running) << AnimState( 0, Running) << AnimState( 0, Stopped);
+ QTest::newRow("115-105") << false << 3 << 115 << 105 << AnimState( 42, Stopped) << AnimState( 45, Running) << AnimState( 0, Stopped);
+
+ // direction = Backward
+ QTest::newRow("b120-120") << true << 3 << 120 << 120 << AnimState( 42, Stopped) << AnimState( 60, Running) << AnimState( 0, Stopped);
+ QTest::newRow("b120-110") << true << 3 << 120 << 110 << AnimState( 42, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
+ QTest::newRow("b120-100") << true << 3 << 120 << 100 << AnimState(100, Running) << AnimState( 40, Running) << AnimState( 0, Stopped);
+ QTest::newRow("b120-50") << true << 3 << 120 << 50 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped);
+ QTest::newRow("b120-0") << true << 3 << 120 << 0 << AnimState( 0, Stopped) << AnimState( 0, Stopped) << AnimState( 0, Stopped);
+ QTest::newRow("b360-170") << true << 3 << 360 << 170 << AnimState( 50, Running) << AnimState( 50, Running) << AnimState( 0, Stopped);
+ QTest::newRow("b360-220") << true << 3 << 360 << 220 << AnimState(100, Running) << AnimState( 40, Running) << AnimState( 0, Stopped);
+ QTest::newRow("b360-210") << true << 3 << 360 << 210 << AnimState( 90, Running) << AnimState( 30, Running) << AnimState( 0, Stopped);
+ QTest::newRow("b360-120") << true << 3 << 360 << 120 << AnimState( 0, Stopped) << AnimState( 60, Running) << AnimState( 0, Stopped);
+
+ // rewind, direction = Backward
+ QTest::newRow("b50-110") << true << 3 << 50 << 110 << AnimState(100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
+ QTest::newRow("b50-120") << true << 3 << 50 << 120 << AnimState(100, Stopped) << AnimState( 60, Running) << AnimState( 0, Stopped);
+ QTest::newRow("b50-140") << true << 3 << 50 << 140 << AnimState( 20, Running) << AnimState( 20, Running) << AnimState( 0, Stopped);
+ QTest::newRow("b50-240") << true << 3 << 50 << 240 << AnimState(100, Stopped) << AnimState( 60, Running) << AnimState( 0, Stopped);
+ QTest::newRow("b50-260") << true << 3 << 50 << 260 << AnimState( 20, Running) << AnimState( 20, Running) << AnimState( 0, Stopped);
+ QTest::newRow("b50-350") << true << 3 << 50 << 350 << AnimState(100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
+
+ // infinite looping
+ QTest::newRow("inf1220") << false << -1 << 0 << 1220 << AnimState( 20, Running) << AnimState( 20, Running) << AnimState( 0, Stopped);
+ QTest::newRow("inf1310") << false << -1 << 0 << 1310 << AnimState( 100, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
+ // infinite looping, direction = Backward (will only loop once)
+ QTest::newRow("b.inf120-120") << true << -1 << 120 << 120 << AnimState( 42, Stopped) << AnimState( 60, Running) << AnimState( 0, Stopped);
+ QTest::newRow("b.inf120-20") << true << -1 << 120 << 20 << AnimState( 20, Running) << AnimState( 20, Running) << AnimState( 0, Stopped);
+ QTest::newRow("b.inf120-110") << true << -1 << 120 << 110 << AnimState( 42, Stopped) << AnimState( 50, Running) << AnimState( 0, Stopped);
+
+
+}
+
+void tst_QParallelAnimationGroupJob::loopCount()
+{
+ QFETCH(bool, directionBackward);
+ QFETCH(int, setLoopCount);
+ QFETCH(int, initialGroupTime);
+ QFETCH(int, currentGroupTime);
+ QFETCH(AnimState, expected1);
+ QFETCH(AnimState, expected2);
+ QFETCH(AnimState, expected3);
+
+ QParallelAnimationGroupJob group;
+
+ TestAnimation anim1(100);
+ TestAnimation anim2(60); //total 120
+ anim2.setLoopCount(2);
+ TestAnimation anim3(0);
+
+ group.appendAnimation(&anim1);
+ group.appendAnimation(&anim2);
+ group.appendAnimation(&anim3);
+
+ group.setLoopCount(setLoopCount);
+ if (initialGroupTime >= 0)
+ group.setCurrentTime(initialGroupTime);
+ if (directionBackward)
+ group.setDirection(QAbstractAnimationJob::Backward);
+
+ group.start();
+ if (initialGroupTime >= 0)
+ group.setCurrentTime(initialGroupTime);
+
+ anim1.setCurrentTime(42); // 42 is "untouched"
+ anim2.setCurrentTime(42);
+
+ group.setCurrentTime(currentGroupTime);
+
+ QCOMPARE(anim1.currentLoopTime(), expected1.time);
+ QCOMPARE(anim2.currentLoopTime(), expected2.time);
+ QCOMPARE(anim3.currentLoopTime(), expected3.time);
+
+ if (expected1.state >=0)
+ QCOMPARE(int(anim1.state()), expected1.state);
+ if (expected2.state >=0)
+ QCOMPARE(int(anim2.state()), expected2.state);
+ if (expected3.state >=0)
+ QCOMPARE(int(anim3.state()), expected3.state);
+
+}
+
+void tst_QParallelAnimationGroupJob::addAndRemoveDuration()
+{
+ QParallelAnimationGroupJob group;
+ QCOMPARE(group.duration(), 0);
+ TestAnimation *test = new TestAnimation(250); // 0, duration = 250;
+ group.appendAnimation(test);
+ QCOMPARE(test->group(), static_cast<QAnimationGroupJob*>(&group));
+ QCOMPARE(test->duration(), 250);
+ QCOMPARE(group.duration(), 250);
+
+ TestAnimation *test2 = new TestAnimation(750); // 1
+ group.appendAnimation(test2);
+ QCOMPARE(test2->group(), static_cast<QAnimationGroupJob*>(&group));
+ QCOMPARE(group.duration(), 750);
+
+ TestAnimation *test3 = new TestAnimation(500); // 2
+ group.appendAnimation(test3);
+ QCOMPARE(test3->group(), static_cast<QAnimationGroupJob*>(&group));
+ QCOMPARE(group.duration(), 750);
+
+ group.removeAnimation(test2); // remove the one with duration = 750
+ delete test2;
+ QCOMPARE(group.duration(), 500);
+
+ group.removeAnimation(test3); // remove the one with duration = 500
+ delete test3;
+ QCOMPARE(group.duration(), 250);
+
+ group.removeAnimation(test); // remove the last one (with duration = 250)
+ QCOMPARE(test->group(), static_cast<QAnimationGroupJob*>(0));
+ QCOMPARE(group.duration(), 0);
+ delete test;
+}
+
+void tst_QParallelAnimationGroupJob::pauseResume()
+{
+ QParallelAnimationGroupJob group;
+ TestAnimation *anim = new TestAnimation(250); // 0, duration = 250;
+ group.appendAnimation(anim);
+ StateChangeListener spy;
+ anim->addAnimationChangeListener(&spy, QAbstractAnimationJob::StateChange);
+ QCOMPARE(group.duration(), 250);
+ group.start();
+ QTest::qWait(100);
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim->state(), QAnimationGroupJob::Running);
+ QCOMPARE(spy.count(), 1);
+ spy.clear();
+ const int currentTime = group.currentLoopTime();
+ QCOMPARE(anim->currentLoopTime(), currentTime);
+
+ group.pause();
+ QCOMPARE(group.state(), QAnimationGroupJob::Paused);
+ QCOMPARE(group.currentLoopTime(), currentTime);
+ QCOMPARE(anim->state(), QAnimationGroupJob::Paused);
+ QCOMPARE(anim->currentLoopTime(), currentTime);
+ QCOMPARE(spy.count(), 1);
+ spy.clear();
+
+ group.resume();
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(group.currentLoopTime(), currentTime);
+ QCOMPARE(anim->state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim->currentLoopTime(), currentTime);
+ QCOMPARE(spy.count(), 1);
+
+ group.stop();
+ spy.clear();
+ group.appendAnimation(new TestAnimation(500));
+ group.start();
+ QCOMPARE(spy.count(), 1); //the animation should have been started
+ QCOMPARE(spy.states.at(0), TestAnimation::Running);
+ group.setCurrentTime(250); //end of first animation
+ QCOMPARE(spy.count(), 2); //the animation should have been stopped
+ QCOMPARE(spy.states.at(1), TestAnimation::Stopped);
+ group.pause();
+ QCOMPARE(spy.count(), 2); //this shouldn't have changed
+ group.resume();
+ QCOMPARE(spy.count(), 2); //this shouldn't have changed
+}
+
+// This is a regression test for QTBUG-8910, where a crash occurred when the
+// last animation was removed from a group.
+void tst_QParallelAnimationGroupJob::crashWhenRemovingUncontrolledAnimation()
+{
+ QParallelAnimationGroupJob group;
+ TestAnimation *anim = new TestAnimation;
+ anim->setLoopCount(-1);
+ TestAnimation *anim2 = new TestAnimation;
+ anim2->setLoopCount(-1);
+ group.appendAnimation(anim);
+ group.appendAnimation(anim2);
+ group.start();
+ delete anim;
+ // it would crash here because the internals of the group would still have a reference to anim
+ delete anim2;
+}
+
+
+QTEST_MAIN(tst_QParallelAnimationGroupJob)
+#include "tst_qparallelanimationgroupjob.moc"
--- /dev/null
+CONFIG += testcase
+macx:CONFIG -= app_bundle
+TARGET = tst_qpauseanimationjob
+QT = core-private gui-private declarative-private testlib
+SOURCES = tst_qpauseanimationjob.cpp
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+
+#include <QtDeclarative/private/qpauseanimationjob_p.h>
+#include <QtDeclarative/private/qsequentialanimationgroupjob_p.h>
+#include <QtDeclarative/private/qparallelanimationgroupjob_p.h>
+
+#ifdef Q_OS_WIN
+static const char winTimerError[] = "On windows, consistent timing is not working properly due to bad timer resolution";
+#endif
+
+class TestablePauseAnimation : public QPauseAnimationJob
+{
+public:
+ TestablePauseAnimation()
+ : m_updateCurrentTimeCount(0)
+ {
+ }
+
+ TestablePauseAnimation(int duration)
+ : QPauseAnimationJob(duration), m_updateCurrentTimeCount(0)
+ {
+ }
+
+ int m_updateCurrentTimeCount;
+protected:
+ void updateCurrentTime(int currentTime)
+ {
+ QPauseAnimationJob::updateCurrentTime(currentTime);
+ ++m_updateCurrentTimeCount;
+ }
+};
+
+class TestableGenericAnimation : public QAbstractAnimationJob
+{
+public:
+ TestableGenericAnimation(int duration = 250) : m_duration(duration) {}
+ int duration() const { return m_duration; }
+
+private:
+ int m_duration;
+};
+
+class EnableConsistentTiming
+{
+public:
+ EnableConsistentTiming()
+ {
+ QUnifiedTimer *timer = QUnifiedTimer::instance();
+ timer->setConsistentTiming(true);
+ }
+ ~EnableConsistentTiming()
+ {
+ QUnifiedTimer *timer = QUnifiedTimer::instance();
+ timer->setConsistentTiming(false);
+ }
+};
+
+class tst_QPauseAnimationJob : public QObject
+{
+ Q_OBJECT
+public Q_SLOTS:
+ void initTestCase();
+
+private slots:
+ void changeDirectionWhileRunning();
+ void noTimerUpdates_data();
+ void noTimerUpdates();
+ void multiplePauseAnimations();
+ void pauseAndPropertyAnimations();
+ void pauseResume();
+ void sequentialPauseGroup();
+ void sequentialGroupWithPause();
+ void multipleSequentialGroups();
+ void zeroDuration();
+};
+
+void tst_QPauseAnimationJob::initTestCase()
+{
+// qRegisterMetaType<QAbstractAnimationJob::State>("QAbstractAnimationJob::State");
+}
+
+void tst_QPauseAnimationJob::changeDirectionWhileRunning()
+{
+ EnableConsistentTiming enabled;
+
+ TestablePauseAnimation animation;
+ animation.setDuration(400);
+ animation.start();
+ QTest::qWait(100);
+ QVERIFY(animation.state() == QAbstractAnimationJob::Running);
+ animation.setDirection(QAbstractAnimationJob::Backward);
+ QTest::qWait(animation.totalDuration() + 50);
+ QVERIFY(animation.state() == QAbstractAnimationJob::Stopped);
+}
+
+void tst_QPauseAnimationJob::noTimerUpdates_data()
+{
+ QTest::addColumn<int>("duration");
+ QTest::addColumn<int>("loopCount");
+
+ QTest::newRow("0") << 200 << 1;
+ QTest::newRow("1") << 160 << 1;
+ QTest::newRow("2") << 160 << 2;
+ QTest::newRow("3") << 200 << 3;
+}
+
+void tst_QPauseAnimationJob::noTimerUpdates()
+{
+ EnableConsistentTiming enabled;
+
+ QFETCH(int, duration);
+ QFETCH(int, loopCount);
+
+ TestablePauseAnimation animation;
+ animation.setDuration(duration);
+ animation.setLoopCount(loopCount);
+ animation.start();
+ QTest::qWait(animation.totalDuration() + 100);
+
+#ifdef Q_OS_WIN
+ if (animation.state() != QAbstractAnimationJob::Stopped)
+ QEXPECT_FAIL("", winTimerError, Abort);
+#endif
+
+ QVERIFY(animation.state() == QAbstractAnimationJob::Stopped);
+ const int expectedLoopCount = 1 + loopCount;
+
+#ifdef Q_OS_WIN
+ if (animation.m_updateCurrentTimeCount != expectedLoopCount)
+ QEXPECT_FAIL("", winTimerError, Abort);
+#endif
+ QCOMPARE(animation.m_updateCurrentTimeCount, expectedLoopCount);
+}
+
+void tst_QPauseAnimationJob::multiplePauseAnimations()
+{
+ EnableConsistentTiming enabled;
+
+ TestablePauseAnimation animation;
+ animation.setDuration(200);
+
+ TestablePauseAnimation animation2;
+ animation2.setDuration(800);
+
+ animation.start();
+ animation2.start();
+ QTest::qWait(animation.totalDuration() + 100);
+
+#ifdef Q_OS_WIN
+ if (animation.state() != QAbstractAnimationJob::Stopped)
+ QEXPECT_FAIL("", winTimerError, Abort);
+#endif
+ QVERIFY(animation.state() == QAbstractAnimationJob::Stopped);
+
+#ifdef Q_OS_WIN
+ if (animation2.state() != QAbstractAnimationJob::Running)
+ QEXPECT_FAIL("", winTimerError, Abort);
+#endif
+ QVERIFY(animation2.state() == QAbstractAnimationJob::Running);
+
+#ifdef Q_OS_WIN
+ if (animation.m_updateCurrentTimeCount != 2)
+ QEXPECT_FAIL("", winTimerError, Abort);
+#endif
+ QCOMPARE(animation.m_updateCurrentTimeCount, 2);
+
+#ifdef Q_OS_WIN
+ if (animation2.m_updateCurrentTimeCount != 2)
+ QEXPECT_FAIL("", winTimerError, Abort);
+#endif
+ QCOMPARE(animation2.m_updateCurrentTimeCount, 2);
+
+ QTest::qWait(550);
+
+#ifdef Q_OS_WIN
+ if (animation2.state() != QAbstractAnimationJob::Stopped)
+ QEXPECT_FAIL("", winTimerError, Abort);
+#endif
+ QVERIFY(animation2.state() == QAbstractAnimationJob::Stopped);
+
+#ifdef Q_OS_WIN
+ if (animation2.m_updateCurrentTimeCount != 3)
+ QEXPECT_FAIL("", winTimerError, Abort);
+#endif
+ QCOMPARE(animation2.m_updateCurrentTimeCount, 3);
+}
+
+void tst_QPauseAnimationJob::pauseAndPropertyAnimations()
+{
+ EnableConsistentTiming enabled;
+
+ TestablePauseAnimation pause;
+ pause.setDuration(200);
+
+ TestableGenericAnimation animation;
+
+ pause.start();
+
+ QTest::qWait(100);
+ animation.start();
+
+ QVERIFY(animation.state() == QAbstractAnimationJob::Running);
+ QVERIFY(pause.state() == QAbstractAnimationJob::Running);
+ QCOMPARE(pause.m_updateCurrentTimeCount, 2);
+
+ QTest::qWait(animation.totalDuration() + 100);
+
+#ifdef Q_OS_WIN
+ if (animation.state() != QAbstractAnimationJob::Stopped)
+ QEXPECT_FAIL("", winTimerError, Abort);
+#endif
+ QVERIFY(animation.state() == QAbstractAnimationJob::Stopped);
+ QVERIFY(pause.state() == QAbstractAnimationJob::Stopped);
+ QVERIFY(pause.m_updateCurrentTimeCount > 3);
+}
+
+void tst_QPauseAnimationJob::pauseResume()
+{
+ TestablePauseAnimation animation;
+ animation.setDuration(400);
+ animation.start();
+ QVERIFY(animation.state() == QAbstractAnimationJob::Running);
+ QTest::qWait(200);
+ animation.pause();
+ QVERIFY(animation.state() == QAbstractAnimationJob::Paused);
+ animation.start();
+ QTest::qWait(300);
+ QVERIFY(animation.state() == QAbstractAnimationJob::Stopped);
+
+#ifdef Q_OS_WIN
+ if (animation.m_updateCurrentTimeCount != 3)
+ QEXPECT_FAIL("", winTimerError, Abort);
+#endif
+ QCOMPARE(animation.m_updateCurrentTimeCount, 3);
+}
+
+void tst_QPauseAnimationJob::sequentialPauseGroup()
+{
+ QSequentialAnimationGroupJob group;
+
+ TestablePauseAnimation animation1(200);
+ group.appendAnimation(&animation1);
+ TestablePauseAnimation animation2(200);
+ group.appendAnimation(&animation2);
+ TestablePauseAnimation animation3(200);
+ group.appendAnimation(&animation3);
+
+ group.start();
+ QCOMPARE(animation1.m_updateCurrentTimeCount, 1);
+ QCOMPARE(animation2.m_updateCurrentTimeCount, 0);
+ QCOMPARE(animation3.m_updateCurrentTimeCount, 0);
+
+ QVERIFY(group.state() == QAbstractAnimationJob::Running);
+ QVERIFY(animation1.state() == QAbstractAnimationJob::Running);
+ QVERIFY(animation2.state() == QAbstractAnimationJob::Stopped);
+ QVERIFY(animation3.state() == QAbstractAnimationJob::Stopped);
+
+ group.setCurrentTime(250);
+ QCOMPARE(animation1.m_updateCurrentTimeCount, 2);
+ QCOMPARE(animation2.m_updateCurrentTimeCount, 1);
+ QCOMPARE(animation3.m_updateCurrentTimeCount, 0);
+
+ QVERIFY(group.state() == QAbstractAnimationJob::Running);
+ QVERIFY(animation1.state() == QAbstractAnimationJob::Stopped);
+ QCOMPARE((QAbstractAnimationJob*)&animation2, group.currentAnimation());
+ QVERIFY(animation2.state() == QAbstractAnimationJob::Running);
+ QVERIFY(animation3.state() == QAbstractAnimationJob::Stopped);
+
+ group.setCurrentTime(500);
+ QCOMPARE(animation1.m_updateCurrentTimeCount, 2);
+ QCOMPARE(animation2.m_updateCurrentTimeCount, 2);
+ QCOMPARE(animation3.m_updateCurrentTimeCount, 1);
+
+ QVERIFY(group.state() == QAbstractAnimationJob::Running);
+ QVERIFY(animation1.state() == QAbstractAnimationJob::Stopped);
+ QVERIFY(animation2.state() == QAbstractAnimationJob::Stopped);
+ QCOMPARE((QAbstractAnimationJob*)&animation3, group.currentAnimation());
+ QVERIFY(animation3.state() == QAbstractAnimationJob::Running);
+
+ group.setCurrentTime(750);
+
+ QVERIFY(group.state() == QAbstractAnimationJob::Stopped);
+ QVERIFY(animation1.state() == QAbstractAnimationJob::Stopped);
+ QVERIFY(animation2.state() == QAbstractAnimationJob::Stopped);
+ QVERIFY(animation3.state() == QAbstractAnimationJob::Stopped);
+
+ QCOMPARE(animation1.m_updateCurrentTimeCount, 2);
+ QCOMPARE(animation2.m_updateCurrentTimeCount, 2);
+ QCOMPARE(animation3.m_updateCurrentTimeCount, 2);
+}
+
+void tst_QPauseAnimationJob::sequentialGroupWithPause()
+{
+ QSequentialAnimationGroupJob group;
+
+ TestableGenericAnimation animation;
+ group.appendAnimation(&animation);
+
+ TestablePauseAnimation pause;
+ pause.setDuration(250);
+ group.appendAnimation(&pause);
+
+ group.start();
+
+ QVERIFY(group.state() == QAbstractAnimationJob::Running);
+ QVERIFY(animation.state() == QAbstractAnimationJob::Running);
+ QVERIFY(pause.state() == QAbstractAnimationJob::Stopped);
+
+ group.setCurrentTime(300);
+
+ QVERIFY(group.state() == QAbstractAnimationJob::Running);
+ QVERIFY(animation.state() == QAbstractAnimationJob::Stopped);
+ QCOMPARE((QAbstractAnimationJob*)&pause, group.currentAnimation());
+ QVERIFY(pause.state() == QAbstractAnimationJob::Running);
+
+ group.setCurrentTime(600);
+
+ QVERIFY(group.state() == QAbstractAnimationJob::Stopped);
+ QVERIFY(animation.state() == QAbstractAnimationJob::Stopped);
+ QVERIFY(pause.state() == QAbstractAnimationJob::Stopped);
+
+ QCOMPARE(pause.m_updateCurrentTimeCount, 2);
+}
+
+void tst_QPauseAnimationJob::multipleSequentialGroups()
+{
+ EnableConsistentTiming enabled;
+
+ QParallelAnimationGroupJob group;
+ group.setLoopCount(2);
+
+ QSequentialAnimationGroupJob subgroup1;
+ group.appendAnimation(&subgroup1);
+
+ TestableGenericAnimation animation(300);
+ subgroup1.appendAnimation(&animation);
+
+ TestablePauseAnimation pause(200);
+ subgroup1.appendAnimation(&pause);
+
+ QSequentialAnimationGroupJob subgroup2;
+ group.appendAnimation(&subgroup2);
+
+ TestableGenericAnimation animation2(200);
+ subgroup2.appendAnimation(&animation2);
+
+ TestablePauseAnimation pause2(250);
+ subgroup2.appendAnimation(&pause2);
+
+ QSequentialAnimationGroupJob subgroup3;
+ group.appendAnimation(&subgroup3);
+
+ TestablePauseAnimation pause3(400);
+ subgroup3.appendAnimation(&pause3);
+
+ TestableGenericAnimation animation3(200);
+ subgroup3.appendAnimation(&animation3);
+
+ QSequentialAnimationGroupJob subgroup4;
+ group.appendAnimation(&subgroup4);
+
+ TestablePauseAnimation pause4(310);
+ subgroup4.appendAnimation(&pause4);
+
+ TestablePauseAnimation pause5(60);
+ subgroup4.appendAnimation(&pause5);
+
+ group.start();
+
+ QVERIFY(group.state() == QAbstractAnimationJob::Running);
+ QVERIFY(subgroup1.state() == QAbstractAnimationJob::Running);
+ QVERIFY(subgroup2.state() == QAbstractAnimationJob::Running);
+ QVERIFY(subgroup3.state() == QAbstractAnimationJob::Running);
+ QVERIFY(subgroup4.state() == QAbstractAnimationJob::Running);
+
+ // This is a pretty long animation so it tends to get rather out of sync
+ // when using the consistent timer, so run for an extra half second for good
+ // measure...
+ QTest::qWait(group.totalDuration() + 500);
+
+#ifdef Q_OS_WIN
+ if (group.state() != QAbstractAnimationJob::Stopped)
+ QEXPECT_FAIL("", winTimerError, Abort);
+#endif
+ QVERIFY(group.state() == QAbstractAnimationJob::Stopped);
+
+#ifdef Q_OS_WIN
+ if (subgroup1.state() != QAbstractAnimationJob::Stopped)
+ QEXPECT_FAIL("", winTimerError, Abort);
+#endif
+ QVERIFY(subgroup1.state() == QAbstractAnimationJob::Stopped);
+
+#ifdef Q_OS_WIN
+ if (subgroup2.state() != QAbstractAnimationJob::Stopped)
+ QEXPECT_FAIL("", winTimerError, Abort);
+#endif
+ QVERIFY(subgroup2.state() == QAbstractAnimationJob::Stopped);
+
+#ifdef Q_OS_WIN
+ if (subgroup3.state() != QAbstractAnimationJob::Stopped)
+ QEXPECT_FAIL("", winTimerError, Abort);
+#endif
+ QVERIFY(subgroup3.state() == QAbstractAnimationJob::Stopped);
+
+#ifdef Q_OS_WIN
+ if (subgroup4.state() != QAbstractAnimationJob::Stopped)
+ QEXPECT_FAIL("", winTimerError, Abort);
+#endif
+ QVERIFY(subgroup4.state() == QAbstractAnimationJob::Stopped);
+
+#ifdef Q_OS_WIN
+ if (pause5.m_updateCurrentTimeCount != 4)
+ QEXPECT_FAIL("", winTimerError, Abort);
+#endif
+ QCOMPARE(pause5.m_updateCurrentTimeCount, 4);
+}
+
+void tst_QPauseAnimationJob::zeroDuration()
+{
+ TestablePauseAnimation animation;
+ animation.setDuration(0);
+ animation.start();
+ QTest::qWait(animation.totalDuration() + 100);
+ QVERIFY(animation.state() == QAbstractAnimationJob::Stopped);
+ QCOMPARE(animation.m_updateCurrentTimeCount, 1);
+}
+
+QTEST_MAIN(tst_QPauseAnimationJob)
+#include "tst_qpauseanimationjob.moc"
--- /dev/null
+CONFIG += testcase parallel_test
+macx:CONFIG -= app_bundle
+TARGET = tst_qsequentialanimationgroupjob
+QT = core-private declarative-private testlib
+SOURCES = tst_qsequentialanimationgroupjob.cpp
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <QtTest/QtTest>
+#include <QtDeclarative/private/qsequentialanimationgroupjob_p.h>
+#include <QtDeclarative/private/qparallelanimationgroupjob_p.h>
+#include <QtDeclarative/private/qpauseanimationjob_p.h>
+
+Q_DECLARE_METATYPE(QAbstractAnimationJob::State)
+Q_DECLARE_METATYPE(QAbstractAnimationJob*)
+
+class tst_QSequentialAnimationGroupJob : public QObject
+{
+ Q_OBJECT
+public Q_SLOTS:
+ void initTestCase();
+
+private slots:
+ void construction();
+ void setCurrentTime();
+ void setCurrentTimeWithUncontrolledAnimation();
+ void seekingForwards();
+ void seekingBackwards();
+ void pauseAndResume();
+ void restart();
+ void looping();
+ void startDelay();
+ void clearGroup();
+ void groupWithZeroDurationAnimations();
+ void propagateGroupUpdateToChildren();
+ void updateChildrenWithRunningGroup();
+ void deleteChildrenWithRunningGroup();
+ void startChildrenWithStoppedGroup();
+ void stopGroupWithRunningChild();
+ void startGroupWithRunningChild();
+ void zeroDurationAnimation();
+ void stopUncontrolledAnimations();
+ void finishWithUncontrolledAnimation();
+ void addRemoveAnimation();
+ void currentAnimation();
+ void currentAnimationWithZeroDuration();
+ void insertAnimation();
+ void clear();
+ void pauseResume();
+};
+
+void tst_QSequentialAnimationGroupJob::initTestCase()
+{
+ qRegisterMetaType<QAbstractAnimationJob::State>("QAbstractAnimationJob::State");
+ qRegisterMetaType<QAbstractAnimationJob*>("QAbstractAnimationJob*");
+}
+
+void tst_QSequentialAnimationGroupJob::construction()
+{
+ QSequentialAnimationGroupJob animationgroup;
+}
+
+class TestAnimation : public QAbstractAnimationJob
+{
+public:
+ TestAnimation(int duration = 250) : m_duration(duration) {}
+ int duration() const { return m_duration; }
+
+private:
+ int m_duration;
+};
+
+class TestValueAnimation : public TestAnimation
+{
+public:
+ TestValueAnimation(int duration = 250)
+ : TestAnimation(duration), start(0), end(0), value(0) {}
+
+ void updateCurrentTime(int msecs)
+ {
+ if (msecs >= duration())
+ value = end;
+ else
+ value = start + (end - start) * (qreal(msecs) / duration());
+ }
+
+ qreal start, end;
+ qreal value;
+};
+
+class UncontrolledAnimation : public QObject, public QAbstractAnimationJob
+{
+ Q_OBJECT
+public:
+ int duration() const { return -1; /* not time driven */ }
+
+protected:
+ void updateCurrentTime(int currentTime)
+ {
+ if (currentTime >= 250)
+ stop();
+ }
+};
+
+class StateChangeListener: public QAnimation2ChangeListener
+{
+public:
+ virtual void animationStateChanged(QAbstractAnimationJob *, QAbstractAnimationJob::State newState, QAbstractAnimationJob::State)
+ {
+ states << newState;
+ }
+
+ void clear() { states.clear(); }
+ int count() const { return states.count(); }
+
+ QList<QAbstractAnimationJob::State> states;
+};
+
+class FinishedListener: public QAnimation2ChangeListener
+{
+public:
+ FinishedListener() : m_count(0) {}
+
+ virtual void animationFinished(QAbstractAnimationJob *) { ++m_count; }
+ void clear() { m_count = 0; }
+ int count() { return m_count; }
+
+private:
+ int m_count;
+};
+
+void tst_QSequentialAnimationGroupJob::setCurrentTime()
+{
+ // sequence operating on same object/property
+ QAnimationGroupJob *sequence = new QSequentialAnimationGroupJob();
+ TestAnimation *a1_s_o1 = new TestAnimation;
+ TestAnimation *a2_s_o1 = new TestAnimation;
+ TestAnimation *a3_s_o1 = new TestAnimation;
+ a2_s_o1->setLoopCount(3);
+ sequence->appendAnimation(a1_s_o1);
+ sequence->appendAnimation(a2_s_o1);
+ sequence->appendAnimation(a3_s_o1);
+
+ // sequence operating on different object/properties
+ QAnimationGroupJob *sequence2 = new QSequentialAnimationGroupJob();
+ TestAnimation *a1_s_o2 = new TestAnimation;
+ TestAnimation *a1_s_o3 = new TestAnimation;
+ sequence2->appendAnimation(a1_s_o2);
+ sequence2->appendAnimation(a1_s_o3);
+
+ QSequentialAnimationGroupJob group;
+ group.appendAnimation(sequence);
+ group.appendAnimation(sequence2);
+
+ // Current time = 1
+ group.setCurrentTime(1);
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(sequence->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(sequence2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped);
+
+ QCOMPARE(group.currentLoopTime(), 1);
+ QCOMPARE(sequence->currentLoopTime(), 1);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 1);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 0);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 0);
+
+ // Current time = 250
+ group.setCurrentTime(250);
+ QCOMPARE(group.currentLoopTime(), 250);
+ QCOMPARE(sequence->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 0);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 0);
+
+ // Current time = 251
+ group.setCurrentTime(251);
+ QCOMPARE(group.currentLoopTime(), 251);
+ QCOMPARE(sequence->currentLoopTime(), 251);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 1);
+ QCOMPARE(a2_s_o1->currentLoop(), 0);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 0);
+ QCOMPARE(sequence2->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 0);
+
+ // Current time = 750
+ group.setCurrentTime(750);
+ QCOMPARE(group.currentLoopTime(), 750);
+ QCOMPARE(sequence->currentLoopTime(), 750);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 0);
+ QCOMPARE(a2_s_o1->currentLoop(), 2);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 0);
+ QCOMPARE(sequence2->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 0);
+
+ // Current time = 1000
+ group.setCurrentTime(1000);
+ QCOMPARE(group.currentLoopTime(), 1000);
+ QCOMPARE(sequence->currentLoopTime(), 1000);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoop(), 2);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 0);
+ QCOMPARE(sequence2->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 0);
+
+ // Current time = 1010
+ group.setCurrentTime(1010);
+ QCOMPARE(group.currentLoopTime(), 1010);
+ QCOMPARE(sequence->currentLoopTime(), 1010);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoop(), 2);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 10);
+ QCOMPARE(sequence2->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 0);
+
+ // Current time = 1250
+ group.setCurrentTime(1250);
+ QCOMPARE(group.currentLoopTime(), 1250);
+ QCOMPARE(sequence->currentLoopTime(), 1250);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoop(), 2);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 250);
+ QCOMPARE(sequence2->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 0);
+
+ // Current time = 1500
+ group.setCurrentTime(1500);
+ QCOMPARE(group.currentLoopTime(), 1500);
+ QCOMPARE(sequence->currentLoopTime(), 1250);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoop(), 2);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 250);
+ QCOMPARE(sequence2->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 0);
+
+ // Current time = 1750
+ group.setCurrentTime(1750);
+ QCOMPARE(group.currentLoopTime(), 1750);
+ QCOMPARE(sequence->currentLoopTime(), 1250);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoop(), 2);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 250);
+ QCOMPARE(sequence2->currentLoopTime(), 500);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 250);
+
+ // Current time = 2000
+ group.setCurrentTime(2000);
+ QCOMPARE(group.currentLoopTime(), 1750);
+ QCOMPARE(sequence->currentLoopTime(), 1250);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoop(), 2);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 250);
+ QCOMPARE(sequence2->currentLoopTime(), 500);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 250);
+}
+
+void tst_QSequentialAnimationGroupJob::setCurrentTimeWithUncontrolledAnimation()
+{
+ // sequence operating on different object/properties
+ QAnimationGroupJob *sequence = new QSequentialAnimationGroupJob();
+ TestAnimation *a1_s_o1 = new TestAnimation;
+ TestAnimation *a1_s_o2 = new TestAnimation;
+ sequence->appendAnimation(a1_s_o1);
+ sequence->appendAnimation(a1_s_o2);
+
+ UncontrolledAnimation *notTimeDriven = new UncontrolledAnimation;
+ QCOMPARE(notTimeDriven->totalDuration(), -1);
+
+ TestAnimation *loopsForever = new TestAnimation;
+ loopsForever->setLoopCount(-1);
+ QCOMPARE(loopsForever->totalDuration(), -1);
+
+ QSequentialAnimationGroupJob group;
+ group.appendAnimation(sequence);
+ group.appendAnimation(notTimeDriven);
+ group.appendAnimation(loopsForever);
+ group.start();
+ group.pause(); // this allows the group to listen for the finish signal of its children
+
+ // Current time = 1
+ group.setCurrentTime(1);
+ QCOMPARE(group.state(), QAnimationGroupJob::Paused);
+ QCOMPARE(sequence->state(), QAnimationGroupJob::Paused);
+ QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Paused);
+ QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(notTimeDriven->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(loopsForever->state(), QAnimationGroupJob::Stopped);
+
+ QCOMPARE(group.currentLoopTime(), 1);
+ QCOMPARE(sequence->currentLoopTime(), 1);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 1);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 0);
+ QCOMPARE(notTimeDriven->currentLoopTime(), 0);
+ QCOMPARE(loopsForever->currentLoopTime(), 0);
+
+ // Current time = 250
+ group.setCurrentTime(250);
+ QCOMPARE(group.currentLoopTime(), 250);
+ QCOMPARE(sequence->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 0);
+ QCOMPARE(notTimeDriven->currentLoopTime(), 0);
+ QCOMPARE(loopsForever->currentLoopTime(), 0);
+
+ // Current time = 500
+ group.setCurrentTime(500);
+ QCOMPARE(group.currentLoopTime(), 500);
+ QCOMPARE(sequence->currentLoopTime(), 500);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 250);
+ QCOMPARE(notTimeDriven->currentLoopTime(), 0);
+ QCOMPARE(loopsForever->currentLoopTime(), 0);
+ QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob *>(notTimeDriven));
+
+ // Current time = 505
+ group.setCurrentTime(505);
+ QCOMPARE(group.currentLoopTime(), 505);
+ QCOMPARE(sequence->currentLoopTime(), 500);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 250);
+ QCOMPARE(notTimeDriven->currentLoopTime(), 5);
+ QCOMPARE(loopsForever->currentLoopTime(), 0);
+ QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob *>(notTimeDriven));
+ QCOMPARE(sequence->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(notTimeDriven->state(), QAnimationGroupJob::Paused);
+ QCOMPARE(loopsForever->state(), QAnimationGroupJob::Stopped);
+
+ // Current time = 750 (end of notTimeDriven animation)
+ group.setCurrentTime(750);
+ QCOMPARE(group.currentLoopTime(), 750);
+ QCOMPARE(sequence->currentLoopTime(), 500);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 250);
+ QCOMPARE(notTimeDriven->currentLoopTime(), 250);
+ QCOMPARE(loopsForever->currentLoopTime(), 0);
+ QCOMPARE(group.currentAnimation(), loopsForever);
+ QCOMPARE(sequence->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(notTimeDriven->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(loopsForever->state(), QAnimationGroupJob::Paused);
+
+ // Current time = 800 (as notTimeDriven was finished at 750, loopsforever should still run)
+ group.setCurrentTime(800);
+ QCOMPARE(group.currentLoopTime(), 800);
+ QCOMPARE(group.currentAnimation(), loopsForever);
+ QCOMPARE(sequence->currentLoopTime(), 500);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 250);
+ QCOMPARE(notTimeDriven->currentLoopTime(), 250);
+ QCOMPARE(loopsForever->currentLoopTime(), 50);
+
+ loopsForever->stop(); // this should stop the group
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(sequence->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(notTimeDriven->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(loopsForever->state(), QAnimationGroupJob::Stopped);
+}
+
+void tst_QSequentialAnimationGroupJob::seekingForwards()
+{
+
+ // sequence operating on same object/property
+ QAnimationGroupJob *sequence = new QSequentialAnimationGroupJob;
+ TestAnimation *a1_s_o1 = new TestAnimation;
+ TestAnimation *a2_s_o1 = new TestAnimation;
+ TestAnimation *a3_s_o1 = new TestAnimation;
+ a2_s_o1->setLoopCount(3);
+ sequence->appendAnimation(a1_s_o1);
+ sequence->appendAnimation(a2_s_o1);
+ sequence->appendAnimation(a3_s_o1);
+
+ // sequence operating on different object/properties
+ QAnimationGroupJob *sequence2 = new QSequentialAnimationGroupJob;
+ TestAnimation *a1_s_o2 = new TestAnimation;
+ TestAnimation *a1_s_o3 = new TestAnimation;
+ sequence2->appendAnimation(a1_s_o2);
+ sequence2->appendAnimation(a1_s_o3);
+
+ QSequentialAnimationGroupJob group;
+ group.appendAnimation(sequence);
+ group.appendAnimation(sequence2);
+
+ // Current time = 1
+ group.setCurrentTime(1);
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(sequence->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(sequence2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o3->state(), QAnimationGroupJob::Stopped);
+
+ QCOMPARE(group.currentLoopTime(), 1);
+ QCOMPARE(sequence->currentLoopTime(), 1);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 1);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 0);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 0);
+ QCOMPARE(sequence2->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 0);
+
+ // Current time = 1500
+ group.setCurrentTime(1500);
+ QCOMPARE(group.currentLoopTime(), 1500);
+ QCOMPARE(sequence->currentLoopTime(), 1250);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoop(), 2);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 250);
+ QCOMPARE(sequence2->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 0);
+
+ // this will restart the group
+ group.start();
+ group.pause();
+ QCOMPARE(group.state(), QAnimationGroupJob::Paused);
+ QCOMPARE(sequence->state(), QAnimationGroupJob::Paused);
+ QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Paused);
+ QCOMPARE(sequence2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o3->state(), QAnimationGroupJob::Stopped);
+
+ // Current time = 1750
+ group.setCurrentTime(1750);
+ QCOMPARE(group.currentLoopTime(), 1750);
+ QCOMPARE(sequence->currentLoopTime(), 1250);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoop(), 2);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 250);
+ QCOMPARE(sequence2->currentLoopTime(), 500);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 250);
+}
+
+void tst_QSequentialAnimationGroupJob::seekingBackwards()
+{
+ // sequence operating on same object/property
+ QAnimationGroupJob *sequence = new QSequentialAnimationGroupJob();
+ TestAnimation *a1_s_o1 = new TestAnimation;
+ TestAnimation *a2_s_o1 = new TestAnimation;
+ TestAnimation *a3_s_o1 = new TestAnimation;
+ a2_s_o1->setLoopCount(3);
+ sequence->appendAnimation(a1_s_o1);
+ sequence->appendAnimation(a2_s_o1);
+ sequence->appendAnimation(a3_s_o1);
+
+ // sequence operating on different object/properties
+ QAnimationGroupJob *sequence2 = new QSequentialAnimationGroupJob();
+ TestAnimation *a1_s_o2 = new TestAnimation;
+ TestAnimation *a1_s_o3 = new TestAnimation;
+ sequence2->appendAnimation(a1_s_o2);
+ sequence2->appendAnimation(a1_s_o3);
+
+ QSequentialAnimationGroupJob group;
+ group.appendAnimation(sequence);
+ group.appendAnimation(sequence2);
+
+ group.start();
+
+ // Current time = 1600
+ group.setCurrentTime(1600);
+ QCOMPARE(group.currentLoopTime(), 1600);
+ QCOMPARE(sequence->currentLoopTime(), 1250);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoop(), 2);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 250);
+ QCOMPARE(sequence2->currentLoopTime(), 350);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 100);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(sequence->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(sequence2->state(), QAnimationGroupJob::Running);
+ QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o3->state(), QAnimationGroupJob::Running);
+
+ // Seeking backwards, current time = 1
+ group.setCurrentTime(1);
+ QCOMPARE(group.currentLoopTime(), 1);
+ QCOMPARE(sequence->currentLoopTime(), 1);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 1);
+
+ QEXPECT_FAIL("", "rewinding in nested groups is considered as a restart from the children,"
+ "hence they don't reset from their current animation", Continue);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 0);
+ QEXPECT_FAIL("", "rewinding in nested groups is considered as a restart from the children,"
+ "hence they don't reset from their current animation", Continue);
+ QCOMPARE(a2_s_o1->currentLoop(), 0);
+ QEXPECT_FAIL("", "rewinding in nested groups is considered as a restart from the children,"
+ "hence they don't reset from their current animation", Continue);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 0);
+ QCOMPARE(sequence2->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 0);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 0);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(sequence->state(), QAnimationGroupJob::Running);
+ QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Running);
+ QCOMPARE(sequence2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o3->state(), QAnimationGroupJob::Stopped);
+
+ // Current time = 2000
+ group.setCurrentTime(2000);
+ QCOMPARE(group.currentLoopTime(), 1750);
+ QCOMPARE(sequence->currentLoopTime(), 1250);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoop(), 2);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 250);
+ QCOMPARE(sequence2->currentLoopTime(), 500);
+ QCOMPARE(a1_s_o2->currentLoopTime(), 250);
+ QCOMPARE(a1_s_o3->currentLoopTime(), 250);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(sequence->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(sequence2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1_s_o3->state(), QAnimationGroupJob::Stopped);
+}
+
+typedef QList<QAbstractAnimationJob::State> StateList;
+
+static bool compareStates(const StateChangeListener& spy, const StateList &expectedStates)
+{
+ bool equals = true;
+ for (int i = 0; i < qMax(expectedStates.count(), spy.count()); ++i) {
+ if (i >= spy.count() || i >= expectedStates.count()) {
+ equals = false;
+ break;
+ }
+ QAbstractAnimationJob::State st = expectedStates.at(i);
+ QAbstractAnimationJob::State actual = spy.states.at(i);
+ if (equals && actual != st) {
+ equals = false;
+ break;
+ }
+ }
+ if (!equals) {
+ const char *stateStrings[] = {"Stopped", "Paused", "Running"};
+ QString e,a;
+ for (int i = 0; i < qMax(expectedStates.count(), spy.count()); ++i) {
+ if (i < expectedStates.count()) {
+ int exp = int(expectedStates.at(i));
+ if (!e.isEmpty())
+ e += QLatin1String(", ");
+ e += QLatin1String(stateStrings[exp]);
+ }
+ if (i < spy.count()) {
+ QAbstractAnimationJob::State actual = spy.states.at(i);
+ if (!a.isEmpty())
+ a += QLatin1String(", ");
+ if (int(actual) >= 0 && int(actual) <= 2) {
+ a += QLatin1String(stateStrings[int(actual)]);
+ } else {
+ a += QLatin1String("NaN");
+ }
+ }
+
+ }
+ qDebug("\n"
+ "expected (count == %d): %s\n"
+ "actual (count == %d): %s\n", expectedStates.count(), qPrintable(e), spy.count(), qPrintable(a));
+ }
+ return equals;
+}
+
+void tst_QSequentialAnimationGroupJob::pauseAndResume()
+{
+ // sequence operating on same object/property
+ QAnimationGroupJob *sequence = new QSequentialAnimationGroupJob();
+ TestAnimation *a1_s_o1 = new TestAnimation;
+ TestAnimation *a2_s_o1 = new TestAnimation;
+ TestAnimation *a3_s_o1 = new TestAnimation;
+ a2_s_o1->setLoopCount(2);
+ sequence->appendAnimation(a1_s_o1);
+ sequence->appendAnimation(a2_s_o1);
+ sequence->appendAnimation(a3_s_o1);
+ sequence->setLoopCount(2);
+
+ StateChangeListener a1StateChangedSpy;
+ a1_s_o1->addAnimationChangeListener(&a1StateChangedSpy, QAbstractAnimationJob::StateChange);
+ StateChangeListener seqStateChangedSpy;
+ sequence->addAnimationChangeListener(&seqStateChangedSpy, QAbstractAnimationJob::StateChange);
+
+ QSequentialAnimationGroupJob group;
+ group.appendAnimation(sequence);
+
+ group.start();
+ group.pause();
+
+ // Current time = 1751
+ group.setCurrentTime(1751);
+ QCOMPARE(group.currentLoopTime(), 1751);
+ QCOMPARE(sequence->currentLoopTime(), 751);
+ QCOMPARE(sequence->currentLoop(), 1);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoop(), 1);
+ QCOMPARE(a3_s_o1->currentLoop(), 0);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 1);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Paused);
+ QCOMPARE(sequence->state(), QAnimationGroupJob::Paused);
+ QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a2_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a3_s_o1->state(), QAnimationGroupJob::Paused);
+
+ QCOMPARE(a1StateChangedSpy.count(), 5); // Running,Paused,Stopped,Running,Stopped
+ QCOMPARE(seqStateChangedSpy.count(), 2); // Running,Paused
+
+ QVERIFY(compareStates(a1StateChangedSpy, (StateList() << QAbstractAnimationJob::Running
+ << QAbstractAnimationJob::Paused
+ << QAbstractAnimationJob::Stopped
+ << QAbstractAnimationJob::Running
+ << QAbstractAnimationJob::Stopped)));
+
+ //### is this the same test as compareStates test above?
+ QCOMPARE(a1StateChangedSpy.states.at(0), QAnimationGroupJob::Running);
+ QCOMPARE(a1StateChangedSpy.states.at(1), QAnimationGroupJob::Paused);
+ QCOMPARE(a1StateChangedSpy.states.at(2), QAnimationGroupJob::Stopped);
+ QCOMPARE(a1StateChangedSpy.states.at(3), QAnimationGroupJob::Running);
+ QCOMPARE(a1StateChangedSpy.states.at(4), QAnimationGroupJob::Stopped);
+
+ QCOMPARE(seqStateChangedSpy.states.at(0), QAnimationGroupJob::Running);
+ QCOMPARE(seqStateChangedSpy.states.at(1), QAnimationGroupJob::Paused);
+
+ group.resume();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(sequence->state(), QAnimationGroupJob::Running);
+ QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a2_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a3_s_o1->state(), QAnimationGroupJob::Running);
+
+ QVERIFY(group.currentLoopTime() >= 1751);
+ QVERIFY(sequence->currentLoopTime() >= 751);
+ QCOMPARE(sequence->currentLoop(), 1);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoop(), 1);
+ QCOMPARE(a3_s_o1->currentLoop(), 0);
+ QVERIFY(a3_s_o1->currentLoopTime() >= 1);
+
+ QCOMPARE(seqStateChangedSpy.count(), 3); // Running,Paused,Running
+ QCOMPARE(seqStateChangedSpy.states.at(2), QAnimationGroupJob::Running);
+
+ group.pause();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Paused);
+ QCOMPARE(sequence->state(), QAnimationGroupJob::Paused);
+ QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a2_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a3_s_o1->state(), QAnimationGroupJob::Paused);
+
+ QVERIFY(group.currentLoopTime() >= 1751);
+ QVERIFY(sequence->currentLoopTime() >= 751);
+ QCOMPARE(sequence->currentLoop(), 1);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoop(), 1);
+ QCOMPARE(a3_s_o1->currentLoop(), 0);
+ QVERIFY(a3_s_o1->currentLoopTime() >= 1);
+
+ QCOMPARE(seqStateChangedSpy.count(), 4); // Running,Paused,Running,Paused
+ QCOMPARE(seqStateChangedSpy.states.at(3), QAnimationGroupJob::Paused);
+
+ group.stop();
+
+ QCOMPARE(seqStateChangedSpy.count(), 5); // Running,Paused,Running,Paused,Stopped
+ QCOMPARE(seqStateChangedSpy.states.at(4), QAnimationGroupJob::Stopped);
+}
+
+void tst_QSequentialAnimationGroupJob::restart()
+{
+ // originally was sequence operating on same object/property
+ QAnimationGroupJob *sequence = new QSequentialAnimationGroupJob();
+ //### no equivilant signal
+ //QSignalSpy seqCurrentAnimChangedSpy(sequence, SIGNAL(currentAnimationChanged(QAbstractAnimationJob*)));
+
+ StateChangeListener seqStateChangedSpy;
+ sequence->addAnimationChangeListener(&seqStateChangedSpy, QAbstractAnimationJob::StateChange);
+
+ TestAnimation *anims[3];
+ StateChangeListener *animsStateChanged[3];
+
+ for (int i = 0; i < 3; i++) {
+ anims[i] = new TestAnimation(100);
+ animsStateChanged[i] = new StateChangeListener;
+ anims[i]->addAnimationChangeListener(animsStateChanged[i], QAbstractAnimationJob::StateChange);
+ }
+
+ anims[1]->setLoopCount(2);
+ sequence->appendAnimation(anims[0]);
+ sequence->appendAnimation(anims[1]);
+ sequence->appendAnimation(anims[2]);
+ sequence->setLoopCount(2);
+
+ QSequentialAnimationGroupJob group;
+ group.appendAnimation(sequence);
+
+ group.start();
+
+ QTest::qWait(500);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+
+ QTest::qWait(300);
+ QTRY_COMPARE(group.state(), QAnimationGroupJob::Stopped);
+
+ for (int i = 0; i < 3; i++) {
+ QCOMPARE(animsStateChanged[i]->count(), 4);
+ QCOMPARE(animsStateChanged[i]->states.at(0), QAnimationGroupJob::Running);
+ QCOMPARE(animsStateChanged[i]->states.at(1), QAnimationGroupJob::Stopped);
+ QCOMPARE(animsStateChanged[i]->states.at(2), QAnimationGroupJob::Running);
+ QCOMPARE(animsStateChanged[i]->states.at(3), QAnimationGroupJob::Stopped);
+ }
+
+ QCOMPARE(seqStateChangedSpy.count(), 2);
+ QCOMPARE(seqStateChangedSpy.states.at(0), QAnimationGroupJob::Running);
+ QCOMPARE(seqStateChangedSpy.states.at(1), QAnimationGroupJob::Stopped);
+
+ //QCOMPARE(seqCurrentAnimChangedSpy.count(), 6);
+ //for(int i=0; i<seqCurrentAnimChangedSpy.count(); i++)
+ // QCOMPARE(static_cast<QAbstractAnimationJob*>(anims[i%3]), qVariantValue<QAbstractAnimationJob*>(seqCurrentAnimChangedSpy.at(i).at(0)));
+
+ group.start();
+
+ QCOMPARE(animsStateChanged[0]->count(), 5);
+ QCOMPARE(animsStateChanged[1]->count(), 4);
+ QCOMPARE(animsStateChanged[2]->count(), 4);
+ QCOMPARE(seqStateChangedSpy.count(), 3);
+}
+
+void tst_QSequentialAnimationGroupJob::looping()
+{
+ // originally was sequence operating on same object/property
+ QSequentialAnimationGroupJob *sequence = new QSequentialAnimationGroupJob();
+ QAbstractAnimationJob *a1_s_o1 = new TestAnimation;
+ QAbstractAnimationJob *a2_s_o1 = new TestAnimation;
+ QAbstractAnimationJob *a3_s_o1 = new TestAnimation;
+
+ StateChangeListener a1Spy;
+ a1_s_o1->addAnimationChangeListener(&a1Spy, QAbstractAnimationJob::StateChange);
+ StateChangeListener a2Spy;
+ a2_s_o1->addAnimationChangeListener(&a2Spy, QAbstractAnimationJob::StateChange);
+ StateChangeListener a3Spy;
+ a3_s_o1->addAnimationChangeListener(&a3Spy, QAbstractAnimationJob::StateChange);
+ StateChangeListener seqSpy;
+ sequence->addAnimationChangeListener(&seqSpy, QAbstractAnimationJob::StateChange);
+
+ a2_s_o1->setLoopCount(2);
+ sequence->appendAnimation(a1_s_o1);
+ sequence->appendAnimation(a2_s_o1);
+ sequence->appendAnimation(a3_s_o1);
+ sequence->setLoopCount(2);
+
+ QSequentialAnimationGroupJob group;
+ StateChangeListener groupSpy;
+ group.addAnimationChangeListener(&groupSpy, QAbstractAnimationJob::StateChange);
+
+ group.appendAnimation(sequence);
+ group.setLoopCount(2);
+
+ group.start();
+ group.pause();
+
+ // Current time = 1750
+ group.setCurrentTime(1750);
+ QCOMPARE(group.currentLoopTime(), 1750);
+ QCOMPARE(sequence->currentLoopTime(), 750);
+ QCOMPARE(sequence->currentLoop(), 1);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoop(), 1);
+ // this animation is at the beginning because it is the current one inside sequence
+ QCOMPARE(a3_s_o1->currentLoop(), 0);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 0);
+ QCOMPARE(sequence->currentAnimation(), a3_s_o1);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Paused);
+ QCOMPARE(sequence->state(), QAnimationGroupJob::Paused);
+ QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a2_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a3_s_o1->state(), QAnimationGroupJob::Paused);
+
+ QCOMPARE(a1Spy.count(), 5); // Running,Paused,Stopped,Running,Stopped
+ QVERIFY(compareStates(a1Spy, (StateList() << QAbstractAnimationJob::Running
+ << QAbstractAnimationJob::Paused
+ << QAbstractAnimationJob::Stopped
+ << QAbstractAnimationJob::Running
+ << QAbstractAnimationJob::Stopped)));
+
+ QCOMPARE(a2Spy.count(), 4); // Running,Stopped,Running,Stopped
+ QVERIFY(compareStates(a3Spy, (StateList() << QAbstractAnimationJob::Running
+ << QAbstractAnimationJob::Stopped
+ << QAbstractAnimationJob::Running
+ << QAbstractAnimationJob::Paused)));
+
+ QCOMPARE(seqSpy.count(), 2); // Running,Paused
+ QCOMPARE(groupSpy.count(), 2); // Running,Paused
+
+ // Looping, current time = duration + 1
+ group.setCurrentTime(group.duration() + 1);
+ QCOMPARE(group.currentLoopTime(), 1);
+ QCOMPARE(group.currentLoop(), 1);
+ QCOMPARE(sequence->currentLoopTime(), 1);
+ QCOMPARE(sequence->currentLoop(), 0);
+ QCOMPARE(a1_s_o1->currentLoopTime(), 1);
+ QCOMPARE(a2_s_o1->currentLoopTime(), 250);
+ QCOMPARE(a2_s_o1->currentLoop(), 1);
+ // this animation is at the end because it was run on the previous loop
+ QCOMPARE(a3_s_o1->currentLoop(), 0);
+ QCOMPARE(a3_s_o1->currentLoopTime(), 250);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Paused);
+ QCOMPARE(sequence->state(), QAnimationGroupJob::Paused);
+ QCOMPARE(a1_s_o1->state(), QAnimationGroupJob::Paused);
+ QCOMPARE(a2_s_o1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a3_s_o1->state(), QAnimationGroupJob::Stopped);
+
+ QCOMPARE(a1Spy.count(), 7); // Running,Paused,Stopped,Running,Stopped,Running,Stopped
+ QCOMPARE(a2Spy.count(), 4); // Running, Stopped, Running, Stopped
+ QVERIFY(compareStates(a3Spy, (StateList() << QAbstractAnimationJob::Running
+ << QAbstractAnimationJob::Stopped
+ << QAbstractAnimationJob::Running
+ << QAbstractAnimationJob::Paused
+ << QAbstractAnimationJob::Stopped)));
+ QVERIFY(compareStates(seqSpy, (StateList() << QAbstractAnimationJob::Running
+ << QAbstractAnimationJob::Paused
+ << QAbstractAnimationJob::Stopped
+ << QAbstractAnimationJob::Running
+ << QAbstractAnimationJob::Paused)));
+ QCOMPARE(groupSpy.count(), 2);
+
+ //cleanup
+ a1_s_o1->removeAnimationChangeListener(&a1Spy, QAbstractAnimationJob::StateChange);
+ a2_s_o1->removeAnimationChangeListener(&a2Spy, QAbstractAnimationJob::StateChange);
+ a3_s_o1->removeAnimationChangeListener(&a3Spy, QAbstractAnimationJob::StateChange);
+ sequence->removeAnimationChangeListener(&seqSpy, QAbstractAnimationJob::StateChange);
+ group.removeAnimationChangeListener(&groupSpy, QAbstractAnimationJob::StateChange);
+}
+
+void tst_QSequentialAnimationGroupJob::startDelay()
+{
+ QSequentialAnimationGroupJob group;
+ group.appendAnimation(new QPauseAnimationJob(250));
+ group.appendAnimation(new QPauseAnimationJob(125));
+ QCOMPARE(group.totalDuration(), 375);
+
+ group.start();
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+
+ QTest::qWait(500);
+
+ QTRY_COMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QVERIFY(group.currentLoopTime() == 375);
+}
+
+void tst_QSequentialAnimationGroupJob::clearGroup()
+{
+ QSequentialAnimationGroupJob group;
+
+ static const int animationCount = 20;
+
+ for (int i = 0; i < animationCount/2; ++i) {
+ QSequentialAnimationGroupJob *subGroup = new QSequentialAnimationGroupJob;
+ group.appendAnimation(subGroup);
+ group.appendAnimation(new QPauseAnimationJob(100));
+ subGroup->appendAnimation(new QPauseAnimationJob(10));
+ }
+
+ int count = 0;
+ for (QAbstractAnimationJob *anim = group.firstChild(); anim; anim = anim->nextSibling())
+ ++count;
+ QCOMPARE(count, animationCount);
+
+ group.clear();
+
+ QVERIFY(!group.firstChild() && !group.lastChild());
+ QCOMPARE(group.currentLoopTime(), 0);
+}
+
+void tst_QSequentialAnimationGroupJob::groupWithZeroDurationAnimations()
+{
+ QSequentialAnimationGroupJob group;
+
+ TestValueAnimation *a1 = new TestValueAnimation(0);
+ a1->start = 42;
+ a1->end = 43;
+ group.appendAnimation(a1);
+
+ //this should just run fine and change nothing
+ group.setCurrentTime(0);
+ QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(a1));
+
+ TestValueAnimation *a2 = new TestValueAnimation(500);
+ a2->start = 13;
+ a2->end = 31;
+ group.appendAnimation(a2);
+
+ TestValueAnimation *a3 = new TestValueAnimation(0);
+ a3->start = 43;
+ a3->end = 44;
+ group.appendAnimation(a3);
+
+ TestValueAnimation *a4 = new TestValueAnimation(250);
+ a4->start = 13;
+ a4->end = 75;
+ group.appendAnimation(a4);
+
+ TestValueAnimation *a5 = new TestValueAnimation(0);
+ a5->start = 42;
+ a5->end = 12;
+ group.appendAnimation(a5);
+
+ QCOMPARE((int)a1->value, 43); //### is this actually the behavior we want?
+ QCOMPARE((int)a2->value, 0);
+ QCOMPARE((int)a3->value, 0);
+ QCOMPARE((int)a4->value, 0);
+ QCOMPARE((int)a5->value, 0);
+
+ group.start();
+
+ QCOMPARE((int)a1->value, 43); //### is this actually the behavior we want?
+ QCOMPARE((int)a2->value, 13);
+ QCOMPARE((int)a3->value, 0);
+ QCOMPARE((int)a4->value, 0);
+ QCOMPARE((int)a5->value, 0);
+
+ QTest::qWait(100);
+
+ QCOMPARE((int)a1->value, 43);
+ QVERIFY(a2->value > 13 && a2->value < 31);
+ QCOMPARE((int)a3->value, 0);
+ QCOMPARE((int)a4->value, 0);
+ QCOMPARE((int)a5->value, 0);
+
+ QTest::qWait(500);
+
+ QTRY_COMPARE((int)a3->value, 44);
+ QCOMPARE((int)a1->value, 43);
+ QCOMPARE((int)a2->value, 31);
+ //QCOMPARE((int)a4->value, 36);
+ QCOMPARE((int)a5->value, 0);
+ QCOMPARE(a1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a3->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a4->state(), QAnimationGroupJob::Running);
+ QCOMPARE(a5->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QTest::qWait(500);
+
+ QTRY_COMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE((int)a1->value, 43);
+ QCOMPARE((int)a2->value, 31);
+ QCOMPARE((int)a3->value, 44);
+ QCOMPARE((int)a4->value, 75);
+ QCOMPARE((int)a5->value, 12);
+ QCOMPARE(a1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a2->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a3->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a4->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(a5->state(), QAnimationGroupJob::Stopped);
+}
+
+void tst_QSequentialAnimationGroupJob::propagateGroupUpdateToChildren()
+{
+ // this test verifies if group state changes are updating its children correctly
+ QSequentialAnimationGroupJob group;
+
+ TestAnimation anim1(100);
+ TestAnimation anim2(200);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped);
+
+ group.appendAnimation(&anim1);
+ group.appendAnimation(&anim2);
+
+ group.start();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped);
+
+ group.pause();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Paused);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Paused);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped);
+
+ group.stop();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped);
+}
+
+void tst_QSequentialAnimationGroupJob::updateChildrenWithRunningGroup()
+{
+ // assert that its possible to modify a child's state directly while their group is running
+ QSequentialAnimationGroupJob group;
+
+ TestAnimation anim(200);
+
+ StateChangeListener groupStateChangedSpy;
+ group.addAnimationChangeListener(&groupStateChangedSpy, QAbstractAnimationJob::StateChange);
+ StateChangeListener childStateChangedSpy;
+ anim.addAnimationChangeListener(&childStateChangedSpy, QAbstractAnimationJob::StateChange);
+
+ QCOMPARE(groupStateChangedSpy.count(), 0);
+ QCOMPARE(childStateChangedSpy.count(), 0);
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim.state(), QAnimationGroupJob::Stopped);
+
+ group.appendAnimation(&anim);
+
+ group.start();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim.state(), QAnimationGroupJob::Running);
+
+ QCOMPARE(groupStateChangedSpy.count(), 1);
+ QCOMPARE(childStateChangedSpy.count(), 1);
+
+ QCOMPARE(groupStateChangedSpy.states.at(0), QAnimationGroupJob::Running);
+ QCOMPARE(childStateChangedSpy.states.at(0), QAnimationGroupJob::Running);
+
+ // starting directly a running child will not have any effect
+ anim.start();
+
+ QCOMPARE(groupStateChangedSpy.count(), 1);
+ QCOMPARE(childStateChangedSpy.count(), 1);
+
+ anim.pause();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim.state(), QAnimationGroupJob::Paused);
+
+ // in the animation stops directly, the group will still be running
+ anim.stop();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim.state(), QAnimationGroupJob::Stopped);
+
+ //cleanup
+ group.removeAnimationChangeListener(&groupStateChangedSpy, QAbstractAnimationJob::StateChange);
+ anim.removeAnimationChangeListener(&childStateChangedSpy, QAbstractAnimationJob::StateChange);
+}
+
+void tst_QSequentialAnimationGroupJob::deleteChildrenWithRunningGroup()
+{
+ // test if children can be activated when their group is stopped
+ QSequentialAnimationGroupJob group;
+
+ TestAnimation *anim1 = new TestAnimation(200);
+ group.appendAnimation(anim1);
+
+ QCOMPARE(group.duration(), anim1->duration());
+
+ group.start();
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim1->state(), QAnimationGroupJob::Running);
+
+ QTest::qWait(100);
+ QTRY_VERIFY(group.currentLoopTime() > 0);
+
+ delete anim1;
+ QVERIFY(!group.firstChild());
+ QCOMPARE(group.duration(), 0);
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(group.currentLoopTime(), 0); //that's the invariant
+}
+
+void tst_QSequentialAnimationGroupJob::startChildrenWithStoppedGroup()
+{
+ // test if children can be activated when their group is stopped
+ QSequentialAnimationGroupJob group;
+
+ TestAnimation anim1(200);
+ TestAnimation anim2(200);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped);
+
+ group.appendAnimation(&anim1);
+ group.appendAnimation(&anim2);
+
+ group.stop();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped);
+
+ anim1.start();
+ anim2.start();
+ anim2.pause();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Paused);
+}
+
+void tst_QSequentialAnimationGroupJob::stopGroupWithRunningChild()
+{
+ // children that started independently will not be affected by a group stop
+ QSequentialAnimationGroupJob group;
+
+ TestAnimation anim1(200);
+ TestAnimation anim2(200);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped);
+
+ group.appendAnimation(&anim1);
+ group.appendAnimation(&anim2);
+
+ anim1.start();
+ anim2.start();
+ anim2.pause();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Paused);
+
+ group.stop();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Paused);
+
+ anim1.stop();
+ anim2.stop();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2.state(), QAnimationGroupJob::Stopped);
+}
+
+void tst_QSequentialAnimationGroupJob::startGroupWithRunningChild()
+{
+ // as the group has precedence over its children, starting a group will restart all the children
+ QSequentialAnimationGroupJob group;
+
+ TestAnimation *anim1 = new TestAnimation(200);
+ TestAnimation *anim2 = new TestAnimation(200);
+
+ StateChangeListener stateChangedSpy1;
+ anim1->addAnimationChangeListener(&stateChangedSpy1, QAbstractAnimationJob::StateChange);
+ StateChangeListener stateChangedSpy2;
+ anim2->addAnimationChangeListener(&stateChangedSpy2, QAbstractAnimationJob::StateChange);
+
+ QCOMPARE(stateChangedSpy1.count(), 0);
+ QCOMPARE(stateChangedSpy2.count(), 0);
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2->state(), QAnimationGroupJob::Stopped);
+
+ group.appendAnimation(anim1);
+ group.appendAnimation(anim2);
+
+ anim1->start();
+ anim2->start();
+ anim2->pause();
+
+ QVERIFY(compareStates(stateChangedSpy1, (StateList() << QAbstractAnimationJob::Running)));
+
+ QVERIFY(compareStates(stateChangedSpy2, (StateList() << QAbstractAnimationJob::Running
+ << QAbstractAnimationJob::Paused)));
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1->state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim2->state(), QAnimationGroupJob::Paused);
+
+ group.start();
+
+ QVERIFY(compareStates(stateChangedSpy1, (StateList() << QAbstractAnimationJob::Running
+ << QAbstractAnimationJob::Stopped
+ << QAbstractAnimationJob::Running)));
+ QVERIFY(compareStates(stateChangedSpy2, (StateList() << QAbstractAnimationJob::Running
+ << QAbstractAnimationJob::Paused)));
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim1->state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim2->state(), QAnimationGroupJob::Paused);
+
+ QTest::qWait(300);
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2->state(), QAnimationGroupJob::Running);
+
+ QCOMPARE(stateChangedSpy2.count(), 4);
+ QCOMPARE(stateChangedSpy2.states.at(2), QAnimationGroupJob::Stopped);
+ QCOMPARE(stateChangedSpy2.states.at(3), QAnimationGroupJob::Running);
+
+ group.stop();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2->state(), QAnimationGroupJob::Stopped);
+
+ anim1->removeAnimationChangeListener(&stateChangedSpy1, QAbstractAnimationJob::StateChange);
+ anim2->removeAnimationChangeListener(&stateChangedSpy2, QAbstractAnimationJob::StateChange);
+}
+
+void tst_QSequentialAnimationGroupJob::zeroDurationAnimation()
+{
+ QSequentialAnimationGroupJob group;
+
+ TestAnimation *anim1 = new TestAnimation(0);
+ TestAnimation *anim2 = new TestAnimation(100);
+ TestValueAnimation *anim3 = new TestValueAnimation(0);
+ anim3->end = 100;
+
+ StateChangeListener stateChangedSpy;
+ anim1->addAnimationChangeListener(&stateChangedSpy, QAbstractAnimationJob::StateChange);
+
+ group.appendAnimation(anim1);
+ group.appendAnimation(anim2);
+ group.appendAnimation(anim3);
+ group.setLoopCount(2);
+ group.start();
+
+ QCOMPARE(stateChangedSpy.count(), 2);
+ QCOMPARE(stateChangedSpy.states.at(0), QAnimationGroupJob::Running);
+ QCOMPARE(stateChangedSpy.states.at(1), QAnimationGroupJob::Stopped);
+
+ QCOMPARE(anim1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2->state(), QAnimationGroupJob::Running);
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+
+ //now let's try to seek to the next loop
+ group.setCurrentTime(group.duration() + 1);
+ QCOMPARE(anim1->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim2->state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim3->state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ //TODO: test that anim3 was run
+ QCOMPARE(anim3->value, qreal(100)); //anim3 should have been run
+
+ anim1->removeAnimationChangeListener(&stateChangedSpy, QAbstractAnimationJob::StateChange);
+}
+
+void tst_QSequentialAnimationGroupJob::stopUncontrolledAnimations()
+{
+ QSequentialAnimationGroupJob group;
+
+ UncontrolledAnimation notTimeDriven;
+ QCOMPARE(notTimeDriven.totalDuration(), -1);
+
+ TestAnimation loopsForever(100);
+ loopsForever.setLoopCount(-1);
+
+ group.appendAnimation(¬TimeDriven);
+ group.appendAnimation(&loopsForever);
+
+ group.start();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(notTimeDriven.state(), QAnimationGroupJob::Running);
+ QCOMPARE(loopsForever.state(), QAnimationGroupJob::Stopped);
+
+ notTimeDriven.stop();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(notTimeDriven.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(loopsForever.state(), QAnimationGroupJob::Running);
+
+ loopsForever.stop();
+
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(notTimeDriven.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(loopsForever.state(), QAnimationGroupJob::Stopped);
+}
+
+void tst_QSequentialAnimationGroupJob::finishWithUncontrolledAnimation()
+{
+ //1st case:
+ //first we test a group with one uncontrolled animation
+ QSequentialAnimationGroupJob group;
+ UncontrolledAnimation notTimeDriven;
+ group.appendAnimation(¬TimeDriven);
+ FinishedListener spy;
+ group.addAnimationChangeListener(&spy, QAbstractAnimationJob::Completion);
+
+ group.start();
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(notTimeDriven.state(), QAnimationGroupJob::Running);
+ QCOMPARE(group.currentLoopTime(), 0);
+ QCOMPARE(notTimeDriven.currentLoopTime(), 0);
+
+ QTest::qWait(300); //wait for the end of notTimeDriven
+ QTRY_COMPARE(notTimeDriven.state(), QAnimationGroupJob::Stopped);
+ const int actualDuration = notTimeDriven.currentLoopTime();
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(group.currentLoopTime(), actualDuration);
+ QCOMPARE(spy.count(), 1);
+
+ //2nd case:
+ // lets make sure the seeking will work again
+ spy.clear();
+ TestAnimation anim;
+ group.appendAnimation(&anim);
+ StateChangeListener animStateChangedSpy;
+ anim.addAnimationChangeListener(&animStateChangedSpy, QAbstractAnimationJob::StateChange);
+
+ group.setCurrentTime(300);
+ QCOMPARE(group.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(notTimeDriven.currentLoopTime(), actualDuration);
+ QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(&anim));
+
+ //3rd case:
+ //now let's add a perfectly defined animation at the end
+ QCOMPARE(animStateChangedSpy.count(), 0);
+ group.start();
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(notTimeDriven.state(), QAnimationGroupJob::Running);
+ QCOMPARE(group.currentLoopTime(), 0);
+ QCOMPARE(notTimeDriven.currentLoopTime(), 0);
+
+ QCOMPARE(animStateChangedSpy.count(), 0);
+
+ QTest::qWait(300); //wait for the end of notTimeDriven
+ QTRY_COMPARE(notTimeDriven.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim.state(), QAnimationGroupJob::Running);
+ QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(&anim));
+ QCOMPARE(animStateChangedSpy.count(), 1);
+ QTest::qWait(300); //wait for the end of anim
+
+ QTRY_COMPARE(anim.state(), QAnimationGroupJob::Stopped);
+ QCOMPARE(anim.currentLoopTime(), anim.duration());
+
+ //we should simply be at the end
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(animStateChangedSpy.count(), 2);
+ QCOMPARE(group.currentLoopTime(), notTimeDriven.currentLoopTime() + anim.currentLoopTime());
+
+ //cleanup
+ group.removeAnimationChangeListener(&spy, QAbstractAnimationJob::Completion);
+ anim.removeAnimationChangeListener(&animStateChangedSpy, QAbstractAnimationJob::StateChange);
+}
+
+void tst_QSequentialAnimationGroupJob::addRemoveAnimation()
+{
+ //this test is specific to the sequential animation group
+ QSequentialAnimationGroupJob group;
+
+ QCOMPARE(group.duration(), 0);
+ QCOMPARE(group.currentLoopTime(), 0);
+ QAbstractAnimationJob *anim1 = new TestAnimation;
+ group.appendAnimation(anim1);
+ QCOMPARE(group.duration(), 250);
+ QCOMPARE(group.currentLoopTime(), 0);
+ QCOMPARE(group.currentAnimation(), anim1);
+
+ //let's append an animation
+ QAbstractAnimationJob *anim2 = new TestAnimation;
+ group.appendAnimation(anim2);
+ QCOMPARE(group.duration(), 500);
+ QCOMPARE(group.currentLoopTime(), 0);
+ QCOMPARE(group.currentAnimation(), anim1);
+
+ //let's prepend an animation
+ QAbstractAnimationJob *anim0 = new TestAnimation;
+ group.prependAnimation(anim0);
+ QCOMPARE(group.duration(), 750);
+ QCOMPARE(group.currentLoopTime(), 0);
+ QCOMPARE(group.currentAnimation(), anim0); //anim0 has become the new currentAnimation
+
+ group.setCurrentTime(300); //anim0 | anim1 | anim2
+ QCOMPARE(group.currentLoopTime(), 300);
+ QCOMPARE(group.currentAnimation(), anim1);
+ QCOMPARE(anim1->currentLoopTime(), 50);
+
+ group.removeAnimation(anim0); //anim1 | anim2
+ QCOMPARE(group.currentLoopTime(), 50);
+ QCOMPARE(group.currentAnimation(), anim1);
+ QCOMPARE(anim1->currentLoopTime(), 50);
+
+ group.setCurrentTime(0);
+ group.prependAnimation(anim0); //anim0 | anim1 | anim2
+ group.setCurrentTime(300);
+ QCOMPARE(group.currentLoopTime(), 300);
+ QCOMPARE(group.currentAnimation(), anim1);
+ QCOMPARE(anim1->currentLoopTime(), 50);
+
+ group.removeAnimation(anim1); //anim0 | anim2
+ QCOMPARE(group.currentLoopTime(), 250);
+ QCOMPARE(group.currentAnimation(), anim2);
+ QCOMPARE(anim0->currentLoopTime(), 250);
+}
+
+void tst_QSequentialAnimationGroupJob::currentAnimation()
+{
+ QSequentialAnimationGroupJob group;
+ QVERIFY(group.currentAnimation() == 0);
+
+ TestAnimation anim(0);
+ group.appendAnimation(&anim);
+ QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(&anim));
+}
+
+void tst_QSequentialAnimationGroupJob::currentAnimationWithZeroDuration()
+{
+ QSequentialAnimationGroupJob group;
+ QVERIFY(group.currentAnimation() == 0);
+
+ TestAnimation zero1(0);
+ TestAnimation zero2(0);
+
+ TestAnimation anim;
+
+ TestAnimation zero3(0);
+ TestAnimation zero4(0);
+
+ group.appendAnimation(&zero1);
+ group.appendAnimation(&zero2);
+ group.appendAnimation(&anim);
+ group.appendAnimation(&zero3);
+ group.appendAnimation(&zero4);
+
+ QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(&zero1));
+
+ group.setCurrentTime(0);
+ QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(&anim));
+
+ group.setCurrentTime(group.duration());
+ QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(&zero4));
+
+ group.setDirection(QAbstractAnimationJob::Backward);
+
+ group.setCurrentTime(0);
+ QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(&zero1));
+
+ group.setCurrentTime(group.duration());
+ QCOMPARE(group.currentAnimation(), static_cast<QAbstractAnimationJob*>(&anim));
+}
+
+void tst_QSequentialAnimationGroupJob::insertAnimation()
+{
+ QSequentialAnimationGroupJob group;
+ group.setLoopCount(2);
+ TestAnimation *anim = new TestAnimation;
+ group.appendAnimation(anim);
+ QCOMPARE(group.duration(), anim->duration());
+ group.setCurrentTime(300);
+ QCOMPARE(group.currentLoop(), 1);
+
+ //this will crash if the sequential group calls duration on the created animation
+ group.appendAnimation(new TestAnimation);
+}
+
+class ClearFinishedListener: public QAnimation2ChangeListener
+{
+public:
+ ClearFinishedListener(QSequentialAnimationGroupJob *g) : group(g) {}
+
+ virtual void animationFinished(QAbstractAnimationJob *)
+ {
+ group->clear();
+ }
+
+ QSequentialAnimationGroupJob *group;
+};
+
+class RefillFinishedListener: public QAnimation2ChangeListener
+{
+public:
+ RefillFinishedListener(QSequentialAnimationGroupJob *g) : group(g) {}
+
+ virtual void animationFinished(QAbstractAnimationJob *)
+ {
+ group->stop();
+ group->clear();
+ group->appendAnimation(new TestAnimation);
+ group->start();
+ }
+
+ QSequentialAnimationGroupJob *group;
+};
+
+void tst_QSequentialAnimationGroupJob::clear()
+{
+ QSKIP("deleting an animation when finished is not currently supported");
+ QSequentialAnimationGroupJob group;
+ TestAnimation *anim1 = new TestAnimation;
+ group.appendAnimation(anim1);
+ ClearFinishedListener clearListener(&group);
+ anim1->addAnimationChangeListener(&clearListener, QAbstractAnimationJob::Completion);
+
+ TestAnimation *anim2 = new TestAnimation;
+ group.appendAnimation(anim2);
+ QCOMPARE(group.firstChild(), anim1);
+ QCOMPARE(group.lastChild(), anim2);
+
+ group.start();
+ QTest::qWait(anim1->duration() + 100);
+ QTRY_VERIFY(!group.firstChild());
+ QCOMPARE(group.state(), QAbstractAnimationJob::Stopped);
+ QCOMPARE(group.currentLoopTime(), 0);
+
+ anim1 = new TestAnimation;
+ group.appendAnimation(anim1);
+ RefillFinishedListener refillListener(&group);
+ anim1->addAnimationChangeListener(&refillListener, QAbstractAnimationJob::Completion);
+ group.start();
+ QTest::qWait(anim1->duration() + 100);
+ QTRY_COMPARE(group.state(), QAbstractAnimationJob::Running);
+}
+
+void tst_QSequentialAnimationGroupJob::pauseResume()
+{
+ QParallelAnimationGroupJob group;
+ TestAnimation *anim = new TestAnimation;
+ group.appendAnimation(anim);
+ StateChangeListener spy;
+ anim->addAnimationChangeListener(&spy, QAbstractAnimationJob::StateChange);
+ QCOMPARE(group.duration(), 250);
+ group.start();
+ QTest::qWait(100);
+ QTRY_COMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim->state(), QAnimationGroupJob::Running);
+ QCOMPARE(spy.count(), 1);
+ spy.clear();
+ const int currentTime = group.currentLoopTime();
+ QCOMPARE(anim->currentLoopTime(), currentTime);
+
+ group.pause();
+ QCOMPARE(group.state(), QAnimationGroupJob::Paused);
+ QCOMPARE(group.currentLoopTime(), currentTime);
+ QCOMPARE(anim->state(), QAnimationGroupJob::Paused);
+ QCOMPARE(anim->currentLoopTime(), currentTime);
+ QCOMPARE(spy.count(), 1);
+ spy.clear();
+
+ group.resume();
+ QCOMPARE(group.state(), QAnimationGroupJob::Running);
+ QCOMPARE(group.currentLoopTime(), currentTime);
+ QCOMPARE(anim->state(), QAnimationGroupJob::Running);
+ QCOMPARE(anim->currentLoopTime(), currentTime);
+ QCOMPARE(spy.count(), 1);
+
+ anim->removeAnimationChangeListener(&spy, QAbstractAnimationJob::StateChange);
+}
+
+QTEST_MAIN(tst_QSequentialAnimationGroupJob)
+#include "tst_qsequentialanimationgroupjob.moc"
qmlplugindump
PRIVATETESTS += \
+ animation \
qdeclarativebinding \
qdeclarativechangeset \
qdeclarativeconnection \
--- /dev/null
+import QtQuick 2.0
+import QtTest 1.0
+
+Rectangle {
+ id:container
+ width:50
+ height:50
+
+ Rectangle {id:rect; x:0; y:0; color:"red"; width:10; height:10}
+ AnimationController {
+ id:numberAnimationcontroller
+ progress:1
+ animation: NumberAnimation {target: rect; property: "x"; from:0; to:40; duration: 1000}
+ }
+
+ TestCase {
+ name:"AnimationController"
+ when:windowShown
+ function test_numberAnimation() {
+ numberAnimationcontroller.progress = 0;
+ compare(rect.x, 0);
+ numberAnimationcontroller.progress = 0.5;
+ compare(rect.x, 20);
+
+ // <=0 -> 0
+ numberAnimationcontroller.progress = -1;
+ compare(rect.x, 0);
+
+ //>=1 -> 1
+ numberAnimationcontroller.progress = 1.1;
+ compare(rect.x, 40);
+
+ //make sure the progress can be set backward
+ numberAnimationcontroller.progress = 0.5;
+ compare(rect.x, 20);
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+QT += core-private gui-private declarative-private
+TEMPLATE=app
+TARGET=tst_qdeclarativeanimationcontroller
+
+CONFIG += warn_on qmltestcase
+SOURCES += tst_qdeclarativeanimationcontroller.cpp
+
+importFiles.files = data
+importFiles.path = .
+DEPLOYMENT += importFiles
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <QtQuickTest/quicktest.h>
+QUICK_TEST_MAIN(qdeclarativeanimationcontroller)
rect.setPos(QPointF(0,0));
animation.start();
- animation.pause();
QVERIFY(animation.isRunning());
+ animation.pause();
QVERIFY(animation.isPaused());
animation.setCurrentTime(125);
QVERIFY(animation.currentTime() == 125);
QVERIFY(rect);
QDeclarativeAbstractAnimation *myAnim = rect->findChild<QDeclarativeAbstractAnimation*>("MyAnim");
- QVERIFY(myAnim && myAnim->qtAnimation());
- QVERIFY(myAnim->qtAnimation()->state() == QAbstractAnimation::Stopped);
+ QVERIFY(myAnim && !myAnim->qtAnimation());
+ //QVERIFY(myAnim->qtAnimation()->state() == QAbstractAnimationJob::Stopped);
}
}
QVERIFY(rect);
QDeclarativeAbstractAnimation *myAnim = rect->findChild<QDeclarativeAbstractAnimation*>("MyAnim");
- QVERIFY(myAnim && myAnim->qtAnimation());
- QVERIFY(myAnim->qtAnimation()->state() == QAbstractAnimation::Stopped);
+ QVERIFY(myAnim && !myAnim->qtAnimation());
+ //QVERIFY(myAnim->qtAnimation()->state() == QAbstractAnimationJob::Stopped);
}
{
QVERIFY(rect);
QDeclarativeAbstractAnimation *myAnim = rect->findChild<QDeclarativeAbstractAnimation*>("MyAnim");
- QVERIFY(myAnim && myAnim->qtAnimation());
- QVERIFY(myAnim->qtAnimation()->state() == QAbstractAnimation::Stopped);
+ QVERIFY(myAnim && !myAnim->qtAnimation());
+ //QVERIFY(myAnim->qtAnimation()->state() == QAbstractAnimationJob::Stopped);
}
}
QDeclarativeAbstractAnimation *anim = rect->findChild<QDeclarativeAbstractAnimation*>("animation");
QVERIFY(anim != 0);
- QTRY_COMPARE(anim->qtAnimation()->state(), QAbstractAnimation::Stopped);
+ QTRY_COMPARE(anim->qtAnimation()->state(), QAbstractAnimationJob::Stopped);
}
//QTBUG-16736
QVERIFY(rect.x() != qreal(200));
QTest::qWait(800);
QTIMED_COMPARE(rect.x(), qreal(200));
- QCOMPARE(static_cast<QDeclarativeAbstractAnimation*>(&animation)->qtAnimation()->state(), QAbstractAnimation::Stopped);
+ QCOMPARE(static_cast<QDeclarativeAbstractAnimation*>(&animation)->qtAnimation()->state(), QAbstractAnimationJob::Stopped);
}
//QTBUG-20227
QQuickRectangle *rect = qobject_cast<QQuickRectangle*>(c.create());
QVERIFY(rect != 0);
QDeclarativeAbstractAnimation *anim = rect->findChild<QDeclarativeAbstractAnimation*>("animation");
- QVERIFY(anim->qtAnimation()->state() == QAbstractAnimation::Paused);
+ QVERIFY(anim->qtAnimation()->state() == QAbstractAnimationJob::Paused);
delete rect;
}
QDeclarativeComponent c(&engine, testFileUrl("pauseBug.qml"));
QDeclarativeAbstractAnimation *anim = qobject_cast<QDeclarativeAbstractAnimation*>(c.create());
QVERIFY(anim != 0);
- QCOMPARE(anim->qtAnimation()->state(), QAbstractAnimation::Paused);
+ QCOMPARE(anim->qtAnimation()->state(), QAbstractAnimationJob::Paused);
QCOMPARE(anim->isPaused(), true);
QCOMPARE(anim->isRunning(), true);
QVERIFY(rect);
QDeclarativeAbstractAnimation *myAnim = rect->findChild<QDeclarativeAbstractAnimation*>("MyAnim");
- QVERIFY(myAnim && myAnim->qtAnimation());
- QVERIFY(myAnim->qtAnimation()->state() == QAbstractAnimation::Stopped);
+ QVERIFY(myAnim && !myAnim->qtAnimation());
delete rect;
}
--- /dev/null
+import QtQuick 2.0
+
+Rectangle {
+ width: 300; height: 300;
+ Rectangle {
+ objectName: "rect"
+ color: "red"
+ width: 60; height: 60;
+ x: 100; y: 100;
+ }
+ SmoothedAnimation { objectName: "anim"}
+}
\ No newline at end of file
#include <qtest.h>
#include <QtDeclarative/qdeclarativeengine.h>
#include <QtDeclarative/qdeclarativecomponent.h>
-#include <private/qdeclarativesmoothedanimation_p.h>
+#include <QtQuick/private/qdeclarativesmoothedanimation_p.h>
#include <QtQuick/private/qquickrectangle_p.h>
#include <private/qdeclarativevaluetype_p.h>
#include "../../shared/util.h"
void tst_qdeclarativesmoothedanimation::simpleAnimation()
{
- QQuickRectangle rect;
- QDeclarativeSmoothedAnimation animation;
- animation.setTarget(&rect);
- animation.setProperty("x");
- animation.setTo(200);
- animation.setDuration(250);
- QVERIFY(animation.target() == &rect);
- QVERIFY(animation.property() == "x");
- QVERIFY(animation.to() == 200);
- animation.start();
- QVERIFY(animation.isRunning());
- QTest::qWait(animation.duration());
- QTRY_COMPARE(rect.x(), qreal(200));
-
- rect.setX(0);
- animation.start();
- animation.pause();
- QVERIFY(animation.isRunning());
- QVERIFY(animation.isPaused());
- animation.setCurrentTime(125);
- QVERIFY(animation.currentTime() == 125);
- QCOMPARE(rect.x(), qreal(100));
+ QDeclarativeEngine engine;
+ QDeclarativeComponent c(&engine, testFileUrl("simpleanimation.qml"));
+ QObject *obj = c.create();
+ QVERIFY(obj);
+
+ QQuickRectangle *rect = obj->findChild<QQuickRectangle*>("rect");
+ QVERIFY(rect);
+
+ QDeclarativeSmoothedAnimation *animation = obj->findChild<QDeclarativeSmoothedAnimation*>("anim");
+ QVERIFY(animation);
+
+ animation->setTarget(rect);
+ animation->setProperty("x");
+ animation->setTo(200);
+ animation->setDuration(250);
+ QVERIFY(animation->target() == rect);
+ QVERIFY(animation->property() == "x");
+ QVERIFY(animation->to() == 200);
+ animation->start();
+ QVERIFY(animation->isRunning());
+ QTest::qWait(animation->duration());
+ QTRY_COMPARE(rect->x(), qreal(200));
+ QTest::qWait(100); //smoothed animation doesn't report stopped until delayed timer fires
+
+ QVERIFY(!animation->isRunning());
+ rect->setX(0);
+ animation->start();
+ QVERIFY(animation->isRunning());
+ animation->pause();
+ QVERIFY(animation->isRunning());
+ QVERIFY(animation->isPaused());
+ animation->setCurrentTime(125);
+ QVERIFY(animation->currentTime() == 125);
+ QCOMPARE(rect->x(), qreal(100));
}
void tst_qdeclarativesmoothedanimation::valueSource()
import QtQuick 2.0
-SpringAnimation {
- to: 1.44; velocity: 0.9
- spring: 1.0; damping: 0.5
- epsilon: 0.25; modulus: 360.0
- mass: 2.0;
- running: true;
+Item {
+ Item {
+ id: item
+ }
+
+ SpringAnimation {
+ target: item; property: "x"
+ to: 1.44; velocity: 0.9
+ spring: 1.0; damping: 0.5
+ epsilon: 0.25; modulus: 360.0
+ mass: 2.0;
+ running: true;
+ }
}
{
QDeclarativeEngine engine;
QDeclarativeComponent c(&engine, testFileUrl("springanimation2.qml"));
- QDeclarativeSpringAnimation *obj = qobject_cast<QDeclarativeSpringAnimation*>(c.create());
+ QObject *root = c.create();
+
+ QDeclarativeSpringAnimation *obj = root->findChild<QDeclarativeSpringAnimation*>();
QVERIFY(obj != 0);
--- /dev/null
+TEMPLATE = app
+TARGET = tst_animation
+QT += declarative
+macx:CONFIG -= app_bundle
+
+SOURCES += tst_animation.cpp
+
+DEFINES += SRCDIR=\\\"$$PWD\\\"
+
+QT += testlib core-private gui-private declarative-private quick-private v8-private
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+
+ParallelAnimation {
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+ NumberAnimation {}
+}
--- /dev/null
+/****************************************************************************
+**
+** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/
+**
+** This file is part of the test suite of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt 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.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qtest.h>
+#include <QDeclarativeEngine>
+#include <QDeclarativeComponent>
+#include <private/qdeclarativemetatype_p.h>
+#include <private/qdeclarativeanimation_p_p.h>
+#include <QDeclarativeContext>
+
+class tst_animation : public QObject
+{
+ Q_OBJECT
+public:
+ tst_animation();
+
+private slots:
+ void abstractAnimation();
+ void bulkValueAnimator();
+ void propertyUpdater();
+
+ void animationtree_qml();
+
+ void animationelements_data();
+ void animationelements();
+
+ void numberAnimation();
+ void numberAnimationStarted();
+ void numberAnimationMultipleTargets();
+ void numberAnimationEmpty();
+
+private:
+ QDeclarativeEngine engine;
+};
+
+tst_animation::tst_animation()
+{
+}
+
+inline QUrl TEST_FILE(const QString &filename)
+{
+ return QUrl::fromLocalFile(QLatin1String(SRCDIR) + QLatin1String("/data/") + filename);
+}
+
+void tst_animation::abstractAnimation()
+{
+ QBENCHMARK {
+ QAbstractAnimationJob *animation = new QAbstractAnimationJob;
+ delete animation;
+ }
+}
+
+void tst_animation::bulkValueAnimator()
+{
+ QBENCHMARK {
+ QDeclarativeBulkValueAnimator *animator = new QDeclarativeBulkValueAnimator;
+ delete animator;
+ }
+}
+
+void tst_animation::propertyUpdater()
+{
+ QBENCHMARK {
+ QDeclarativeAnimationPropertyUpdater *updater = new QDeclarativeAnimationPropertyUpdater;
+ delete updater;
+ }
+}
+
+void tst_animation::animationtree_qml()
+{
+ QDeclarativeComponent component(&engine, TEST_FILE("animation.qml"));
+ QObject *obj = component.create();
+ delete obj;
+
+ QBENCHMARK {
+ QObject *obj = component.create();
+ delete obj;
+ }
+}
+
+void tst_animation::animationelements_data()
+{
+ QTest::addColumn<QString>("type");
+
+ QSet<QString> types = QDeclarativeMetaType::qmlTypeNames().toSet();
+ foreach (const QString &type, types) {
+ if (type.contains(QLatin1String("Animation")))
+ QTest::newRow(type.toLatin1()) << type;
+ }
+
+ QTest::newRow("QtQuick/Behavior") << "QtQuick/Behavior";
+ QTest::newRow("QtQuick/Transition") << "QtQuick/Transition";
+}
+
+void tst_animation::animationelements()
+{
+ QFETCH(QString, type);
+ QDeclarativeType *t = QDeclarativeMetaType::qmlType(type, 2, 0);
+ if (!t || !t->isCreatable())
+ QSKIP("Non-creatable type");
+
+ QBENCHMARK {
+ QObject *obj = t->create();
+ delete obj;
+ }
+}
+
+void tst_animation::numberAnimation()
+{
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 2.0\nItem { Rectangle { id: rect; NumberAnimation { target: rect; property: \"x\"; to: 100; duration: 500; easing.type: Easing.InOutQuad } } }", QUrl());
+
+ QObject *obj = component.create();
+ delete obj;
+
+ QBENCHMARK {
+ QObject *obj = component.create();
+ delete obj;
+ }
+}
+
+void tst_animation::numberAnimationStarted()
+{
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 2.0\nItem { Rectangle { id: rect; NumberAnimation { target: rect; property: \"x\"; to: 100; duration: 500; easing.type: Easing.InOutQuad; running: true; paused: true } } }", QUrl());
+
+ QObject *obj = component.create();
+ delete obj;
+
+ QBENCHMARK {
+ QObject *obj = component.create();
+ delete obj;
+ }
+}
+
+void tst_animation::numberAnimationMultipleTargets()
+{
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 2.0\nItem { Rectangle { id: rect; NumberAnimation { target: rect; properties: \"x,y,z,width,height,implicitWidth,implicitHeight\"; to: 100; duration: 500; easing.type: Easing.InOutQuad; running: true; paused: true } } }", QUrl());
+
+ QObject *obj = component.create();
+ delete obj;
+
+ QBENCHMARK {
+ QObject *obj = component.create();
+ delete obj;
+ }
+}
+
+void tst_animation::numberAnimationEmpty()
+{
+ QDeclarativeComponent component(&engine);
+ component.setData("import QtQuick 2.0\nNumberAnimation { }", QUrl());
+
+ QObject *obj = component.create();
+ delete obj;
+
+ QBENCHMARK {
+ QObject *obj = component.create();
+ delete obj;
+ }
+}
+
+QTEST_MAIN(tst_animation)
+
+#include "tst_animation.moc"