From ce3dee765c858a0b573d468ef8fee6b838e576d1 Mon Sep 17 00:00:00 2001 From: Michael Brasser <michael.brasser@nokia.com> Date: Fri, 3 Feb 2012 12:26:37 +1000 Subject: [PATCH] Add and use new animation backend. The new backend improves performance, and allows us to create multiple running animation jobs from a single Transition. It is based off of the existing Qt animation framework. Change-Id: Id1d0162f6e5c65bf31267f3f9f2042c354375d57 Reviewed-by: Yunqiao Yin <charles.yin@nokia.com> --- src/declarative/animations/animations.pri | 15 + .../animations/qabstractanimationjob.cpp | 548 ++++++ .../animations/qabstractanimationjob_p.h | 232 +++ .../animations/qanimationgroupjob.cpp | 164 ++ .../animations/qanimationgroupjob_p.h | 93 + .../animations/qparallelanimationgroupjob.cpp | 226 +++ .../animations/qparallelanimationgroupjob_p.h | 81 + .../animations/qpauseanimationjob.cpp | 71 + .../animations/qpauseanimationjob_p.h | 75 + .../qsequentialanimationgroupjob.cpp | 386 ++++ .../qsequentialanimationgroupjob_p.h | 108 ++ src/declarative/declarative.pro | 1 + src/quick/items/qquickanimation.cpp | 231 +-- src/quick/items/qquickanimation_p.h | 18 +- src/quick/items/qquickanimation_p_p.h | 39 +- src/quick/items/qquickgridview.cpp | 9 +- src/quick/items/qquicklistview.cpp | 8 +- src/quick/particles/qquickitemparticle.cpp | 6 +- src/quick/particles/qquickitemparticle_p.h | 1 + src/quick/util/qdeclarativeanimation.cpp | 409 +++-- src/quick/util/qdeclarativeanimation_p.h | 56 +- src/quick/util/qdeclarativeanimation_p_p.h | 188 +- .../util/qdeclarativeanimationcontroller.cpp | 204 +++ .../util/qdeclarativeanimationcontroller_p.h | 92 + src/quick/util/qdeclarativebehavior.cpp | 44 +- src/quick/util/qdeclarativebehavior_p.h | 2 - .../util/qdeclarativesmoothedanimation.cpp | 189 +- .../util/qdeclarativesmoothedanimation_p.h | 6 +- .../util/qdeclarativesmoothedanimation_p_p.h | 40 +- .../util/qdeclarativespringanimation.cpp | 370 ++-- .../util/qdeclarativespringanimation_p.h | 7 +- src/quick/util/qdeclarativetimeline.cpp | 2 +- src/quick/util/qdeclarativetimeline_p_p.h | 4 +- src/quick/util/qdeclarativetimer.cpp | 27 +- src/quick/util/qdeclarativetimer_p.h | 2 - src/quick/util/qdeclarativetransition.cpp | 100 +- src/quick/util/qdeclarativetransition_p.h | 23 +- .../util/qdeclarativetransitionmanager.cpp | 31 +- .../util/qdeclarativetransitionmanager_p_p.h | 8 +- src/quick/util/qdeclarativeutilmodule.cpp | 2 + src/quick/util/util.pri | 4 +- .../auto/declarative/animation/animation.pro | 7 + .../qabstractanimationjob.pro | 5 + .../tst_qabstractanimationjob.cpp | 229 +++ .../qanimationgroupjob/qanimationgroupjob.pro | 5 + .../tst_qanimationgroupjob.cpp | 310 ++++ .../qparallelanimationgroupjob.pro | 5 + .../tst_qparallelanimationgroupjob.cpp | 931 ++++++++++ .../qpauseanimationjob/qpauseanimationjob.pro | 5 + .../tst_qpauseanimationjob.cpp | 470 +++++ .../qsequentialanimationgroupjob.pro | 5 + .../tst_qsequentialanimationgroupjob.cpp | 1617 +++++++++++++++++ tests/auto/declarative/declarative.pro | 1 + .../data/tst_numberanimation.qml | 38 + .../qdeclarativeanimationcontroller.pro | 10 + .../tst_qdeclarativeanimationcontroller.cpp | 42 + .../tst_qdeclarativeanimations.cpp | 22 +- .../tst_qdeclarativebehaviors.cpp | 3 +- .../data/simpleanimation.qml | 12 + .../tst_qdeclarativesmoothedanimation.cpp | 58 +- .../data/springanimation2.qml | 19 +- .../tst_qdeclarativespringanimation.cpp | 4 +- .../declarative/animation/animation.pro | 10 + .../declarative/animation/data/animation.qml | 75 + .../declarative/animation/tst_animation.cpp | 204 +++ 65 files changed, 7407 insertions(+), 802 deletions(-) create mode 100644 src/declarative/animations/animations.pri create mode 100644 src/declarative/animations/qabstractanimationjob.cpp create mode 100644 src/declarative/animations/qabstractanimationjob_p.h create mode 100644 src/declarative/animations/qanimationgroupjob.cpp create mode 100644 src/declarative/animations/qanimationgroupjob_p.h create mode 100644 src/declarative/animations/qparallelanimationgroupjob.cpp create mode 100644 src/declarative/animations/qparallelanimationgroupjob_p.h create mode 100644 src/declarative/animations/qpauseanimationjob.cpp create mode 100644 src/declarative/animations/qpauseanimationjob_p.h create mode 100644 src/declarative/animations/qsequentialanimationgroupjob.cpp create mode 100644 src/declarative/animations/qsequentialanimationgroupjob_p.h create mode 100644 src/quick/util/qdeclarativeanimationcontroller.cpp create mode 100644 src/quick/util/qdeclarativeanimationcontroller_p.h create mode 100644 tests/auto/declarative/animation/animation.pro create mode 100644 tests/auto/declarative/animation/qabstractanimationjob/qabstractanimationjob.pro create mode 100644 tests/auto/declarative/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp create mode 100644 tests/auto/declarative/animation/qanimationgroupjob/qanimationgroupjob.pro create mode 100644 tests/auto/declarative/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp create mode 100644 tests/auto/declarative/animation/qparallelanimationgroupjob/qparallelanimationgroupjob.pro create mode 100644 tests/auto/declarative/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp create mode 100644 tests/auto/declarative/animation/qpauseanimationjob/qpauseanimationjob.pro create mode 100644 tests/auto/declarative/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp create mode 100644 tests/auto/declarative/animation/qsequentialanimationgroupjob/qsequentialanimationgroupjob.pro create mode 100644 tests/auto/declarative/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp create mode 100644 tests/auto/qtquick2/qdeclarativeanimationcontroller/data/tst_numberanimation.qml create mode 100644 tests/auto/qtquick2/qdeclarativeanimationcontroller/qdeclarativeanimationcontroller.pro create mode 100644 tests/auto/qtquick2/qdeclarativeanimationcontroller/tst_qdeclarativeanimationcontroller.cpp create mode 100644 tests/auto/qtquick2/qdeclarativesmoothedanimation/data/simpleanimation.qml create mode 100644 tests/benchmarks/declarative/animation/animation.pro create mode 100644 tests/benchmarks/declarative/animation/data/animation.qml create mode 100644 tests/benchmarks/declarative/animation/tst_animation.cpp diff --git a/src/declarative/animations/animations.pri b/src/declarative/animations/animations.pri new file mode 100644 index 0000000000..240ee96dce --- /dev/null +++ b/src/declarative/animations/animations.pri @@ -0,0 +1,15 @@ +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 diff --git a/src/declarative/animations/qabstractanimationjob.cpp b/src/declarative/animations/qabstractanimationjob.cpp new file mode 100644 index 0000000000..a796016bb1 --- /dev/null +++ b/src/declarative/animations/qabstractanimationjob.cpp @@ -0,0 +1,548 @@ +/**************************************************************************** +** +** 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" diff --git a/src/declarative/animations/qabstractanimationjob_p.h b/src/declarative/animations/qabstractanimationjob_p.h new file mode 100644 index 0000000000..dcbc749a05 --- /dev/null +++ b/src/declarative/animations/qabstractanimationjob_p.h @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** 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 diff --git a/src/declarative/animations/qanimationgroupjob.cpp b/src/declarative/animations/qanimationgroupjob.cpp new file mode 100644 index 0000000000..7e26f9778d --- /dev/null +++ b/src/declarative/animations/qanimationgroupjob.cpp @@ -0,0 +1,164 @@ +/**************************************************************************** +** +** 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 diff --git a/src/declarative/animations/qanimationgroupjob_p.h b/src/declarative/animations/qanimationgroupjob_p.h new file mode 100644 index 0000000000..d1917a5249 --- /dev/null +++ b/src/declarative/animations/qanimationgroupjob_p.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** 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 diff --git a/src/declarative/animations/qparallelanimationgroupjob.cpp b/src/declarative/animations/qparallelanimationgroupjob.cpp new file mode 100644 index 0000000000..ff38be382d --- /dev/null +++ b/src/declarative/animations/qparallelanimationgroupjob.cpp @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** 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 + diff --git a/src/declarative/animations/qparallelanimationgroupjob_p.h b/src/declarative/animations/qparallelanimationgroupjob_p.h new file mode 100644 index 0000000000..42a96b50d5 --- /dev/null +++ b/src/declarative/animations/qparallelanimationgroupjob_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** 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 diff --git a/src/declarative/animations/qpauseanimationjob.cpp b/src/declarative/animations/qpauseanimationjob.cpp new file mode 100644 index 0000000000..c362f5ab58 --- /dev/null +++ b/src/declarative/animations/qpauseanimationjob.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** 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 diff --git a/src/declarative/animations/qpauseanimationjob_p.h b/src/declarative/animations/qpauseanimationjob_p.h new file mode 100644 index 0000000000..d4af832577 --- /dev/null +++ b/src/declarative/animations/qpauseanimationjob_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** 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 diff --git a/src/declarative/animations/qsequentialanimationgroupjob.cpp b/src/declarative/animations/qsequentialanimationgroupjob.cpp new file mode 100644 index 0000000000..f186f390af --- /dev/null +++ b/src/declarative/animations/qsequentialanimationgroupjob.cpp @@ -0,0 +1,386 @@ +/**************************************************************************** +** +** 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 diff --git a/src/declarative/animations/qsequentialanimationgroupjob_p.h b/src/declarative/animations/qsequentialanimationgroupjob_p.h new file mode 100644 index 0000000000..4c1fb2d55a --- /dev/null +++ b/src/declarative/animations/qsequentialanimationgroupjob_p.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** 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 diff --git a/src/declarative/declarative.pro b/src/declarative/declarative.pro index ca81a4a3d3..6c544bfc3f 100644 --- a/src/declarative/declarative.pro +++ b/src/declarative/declarative.pro @@ -31,3 +31,4 @@ HEADERS += qtdeclarativeversion.h include(util/util.pri) include(qml/qml.pri) include(debugger/debugger.pri) +include(animations/animations.pri) \ No newline at end of file diff --git a/src/quick/items/qquickanimation.cpp b/src/quick/items/qquickanimation.cpp index 3c9e93d4fd..baf1c8dc45 100644 --- a/src/quick/items/qquickanimation.cpp +++ b/src/quick/items/qquickanimation.cpp @@ -48,8 +48,8 @@ #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 @@ -95,21 +95,6 @@ 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() @@ -219,7 +204,7 @@ QPointF QQuickParentAnimationPrivate::computeTransformOrigin(QQuickItem::Transfo } } -void QQuickParentAnimation::transition(QDeclarativeStateActions &actions, +QAbstractAnimationJob* QQuickParentAnimation::transition(QDeclarativeStateActions &actions, QDeclarativeProperties &modified, TransitionDirection direction) { @@ -385,33 +370,46 @@ void QQuickParentAnimation::transition(QDeclarativeStateActions &actions, } } + 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); } /*! @@ -442,21 +440,12 @@ QAbstractAnimation *QQuickParentAnimation::qtAnimation() 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. @@ -479,7 +468,7 @@ QDeclarativeListProperty<QQuickItem> QQuickAnchorAnimation::targets() int QQuickAnchorAnimation::duration() const { Q_D(const QQuickAnchorAnimation); - return d->va->duration(); + return d->duration; } void QQuickAnchorAnimation::setDuration(int duration) @@ -490,9 +479,9 @@ 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); } @@ -517,20 +506,20 @@ void QQuickAnchorAnimation::setDuration(int 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) { @@ -539,7 +528,6 @@ void QQuickAnchorAnimation::transition(QDeclarativeStateActions &actions, QDeclarativeAnimationPropertyUpdater *data = new QDeclarativeAnimationPropertyUpdater; data->interpolatorType = QMetaType::QReal; data->interpolator = d->interpolator; - data->reverse = direction == Backward ? true : false; data->fromSourced = false; data->fromDefined = false; @@ -552,17 +540,15 @@ void QQuickAnchorAnimation::transition(QDeclarativeStateActions &actions, } } + 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); } /*! @@ -593,13 +579,15 @@ void QQuickAnchorAnimation::transition(QDeclarativeStateActions &actions, 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(); + } } /*! @@ -611,7 +599,7 @@ QQuickPathAnimation::~QQuickPathAnimation() int QQuickPathAnimation::duration() const { Q_D(const QQuickPathAnimation); - return d->pa->duration(); + return d->duration; } void QQuickPathAnimation::setDuration(int duration) @@ -622,9 +610,9 @@ 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); } @@ -645,16 +633,16 @@ void QQuickPathAnimation::setDuration(int 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); } @@ -833,24 +821,37 @@ void QQuickPathAnimation::setEndRotation(qreal rotation) 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; @@ -879,46 +880,41 @@ void QQuickPathAnimation::transition(QDeclarativeStateActions &actions, 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) @@ -955,9 +951,7 @@ 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) { @@ -1009,7 +1003,7 @@ void QQuickPathAnimationUpdater::setValue(qreal v) 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); } /* @@ -1024,4 +1018,19 @@ void QQuickPathAnimationUpdater::setValue(qreal v) } } +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 diff --git a/src/quick/items/qquickanimation_p.h b/src/quick/items/qquickanimation_p.h index e16c6fb0a9..924fe5d35f 100644 --- a/src/quick/items/qquickanimation_p.h +++ b/src/quick/items/qquickanimation_p.h @@ -47,14 +47,12 @@ #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) @@ -82,14 +80,13 @@ Q_SIGNALS: 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) @@ -114,16 +111,15 @@ Q_SIGNALS: 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) @@ -179,11 +175,9 @@ public: 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 &); diff --git a/src/quick/items/qquickanimation_p_p.h b/src/quick/items/qquickanimation_p_p.h index 576abbac99..2e6157c140 100644 --- a/src/quick/items/qquickanimation_p_p.h +++ b/src/quick/items/qquickanimation_p_p.h @@ -65,18 +65,13 @@ class QQuickParentAnimationPrivate : public QDeclarativeAnimationGroupPrivate { 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; }; @@ -84,12 +79,11 @@ class QQuickAnchorAnimationPrivate : public QDeclarativeAbstractAnimationPrivate { 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; }; @@ -102,7 +96,7 @@ public: entryInterval(0), exitInterval(0) {} ~QQuickPathAnimationUpdater() {} - void setValue(qreal v); + void setValue(qreal v); QDeclarativePath *path; @@ -129,22 +123,37 @@ public: 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; }; diff --git a/src/quick/items/qquickgridview.cpp b/src/quick/items/qquickgridview.cpp index 80ab2ac55a..522c09ae54 100644 --- a/src/quick/items/qquickgridview.cpp +++ b/src/quick/items/qquickgridview.cpp @@ -217,6 +217,11 @@ public: , snapMode(QQuickGridView::NoSnap) , highlightXAnimator(0), highlightYAnimator(0) {} + ~QQuickGridViewPrivate() + { + delete highlightXAnimator; + delete highlightYAnimator; + } }; Qt::Orientation QQuickGridViewPrivate::layoutOrientation() const @@ -639,10 +644,10 @@ void QQuickGridViewPrivate::createHighlight() 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; diff --git a/src/quick/items/qquicklistview.cpp b/src/quick/items/qquicklistview.cpp index 129db0c3cf..424edc5843 100644 --- a/src/quick/items/qquicklistview.cpp +++ b/src/quick/items/qquicklistview.cpp @@ -168,6 +168,10 @@ public: , sectionCriteria(0), currentSectionItem(0), nextSectionItem(0) , overshootDist(0.0), correctFlick(false), inFlickCorrection(false) {} + ~QQuickListViewPrivate() { + delete highlightPosAnimator; + delete highlightSizeAnimator; + } friend class QQuickViewSection; }; @@ -787,13 +791,13 @@ void QQuickListViewPrivate::createHighlight() 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); diff --git a/src/quick/particles/qquickitemparticle.cpp b/src/quick/particles/qquickitemparticle.cpp index 9c0e4624c1..db9e3d1519 100644 --- a/src/quick/particles/qquickitemparticle.cpp +++ b/src/quick/particles/qquickitemparticle.cpp @@ -103,10 +103,14 @@ QQuickItemParticle::QQuickItemParticle(QQuickItem *parent) : 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) { diff --git a/src/quick/particles/qquickitemparticle_p.h b/src/quick/particles/qquickitemparticle_p.h index 8bc01b6009..df104a5644 100644 --- a/src/quick/particles/qquickitemparticle_p.h +++ b/src/quick/particles/qquickitemparticle_p.h @@ -59,6 +59,7 @@ class QQuickItemParticle : public QQuickParticlePainter Q_PROPERTY(QDeclarativeComponent* delegate READ delegate WRITE setDelegate NOTIFY delegateChanged) public: explicit QQuickItemParticle(QQuickItem *parent = 0); + ~QQuickItemParticle(); bool fade() const { return m_fade; } diff --git a/src/quick/util/qdeclarativeanimation.cpp b/src/quick/util/qdeclarativeanimation.cpp index 2e09fc8d9d..9f6421b657 100644 --- a/src/quick/util/qdeclarativeanimation.cpp +++ b/src/quick/util/qdeclarativeanimation.cpp @@ -59,16 +59,14 @@ #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 /*! @@ -90,6 +88,8 @@ QDeclarativeAbstractAnimation::QDeclarativeAbstractAnimation(QObject *parent) QDeclarativeAbstractAnimation::~QDeclarativeAbstractAnimation() { + Q_D(QDeclarativeAbstractAnimation); + delete d->animationInstance; } QDeclarativeAbstractAnimation::QDeclarativeAbstractAnimation(QDeclarativeAbstractAnimationPrivate &dd, QObject *parent) @@ -97,6 +97,12 @@ QDeclarativeAbstractAnimation::QDeclarativeAbstractAnimation(QDeclarativeAbstrac { } +QAbstractAnimationJob* QDeclarativeAbstractAnimation::qtAnimation() +{ + Q_D(QDeclarativeAbstractAnimation); + return d->animationInstance; +} + /*! \qmlproperty bool QtQuick2::Animation::running This property holds whether the animation is currently running. @@ -155,10 +161,16 @@ void QDeclarativeAbstractAnimationPrivate::commence() 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(); } @@ -187,7 +199,10 @@ void QDeclarativeAbstractAnimation::setRunning(bool r) 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; } @@ -204,29 +219,26 @@ void QDeclarativeAbstractAnimation::setRunning(bool r) 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(); } @@ -264,13 +276,13 @@ void QDeclarativeAbstractAnimation::setPaused(bool p) 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); } @@ -371,19 +383,27 @@ void QDeclarativeAbstractAnimation::setLoops(int loops) 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 @@ -503,8 +523,9 @@ void QDeclarativeAbstractAnimation::restart() */ void QDeclarativeAbstractAnimation::complete() { - if (isRunning()) { - qtAnimation()->setCurrentTime(qtAnimation()->duration()); + Q_D(QDeclarativeAbstractAnimation); + if (isRunning() && d->animationInstance) { + d->animationInstance->setCurrentTime(d->animationInstance->duration()); } } @@ -539,22 +560,43 @@ void QDeclarativeAbstractAnimation::setDisableUserControl() 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); } } @@ -582,21 +624,12 @@ void QDeclarativeAbstractAnimation::timelineComplete() 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 @@ -606,7 +639,7 @@ void QDeclarativePauseAnimationPrivate::init() int QDeclarativePauseAnimation::duration() const { Q_D(const QDeclarativePauseAnimation); - return d->pa->duration(); + return d->duration; } void QDeclarativePauseAnimation::setDuration(int duration) @@ -617,16 +650,22 @@ 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)); } /*! @@ -663,8 +702,8 @@ QDeclarativeColorAnimation::QDeclarativeColorAnimation(QObject *parent) { Q_D(QDeclarativePropertyAnimation); d->interpolatorType = QMetaType::QColor; - d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); d->defaultToInterpolatorType = true; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); } QDeclarativeColorAnimation::~QDeclarativeColorAnimation() @@ -731,7 +770,47 @@ void QDeclarativeColorAnimation::setTo(const QColor &t) 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 @@ -764,20 +843,14 @@ void QDeclarativeColorAnimation::setTo(const QColor &t) 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 @@ -817,6 +890,11 @@ void QDeclarativeScriptAction::setStateChangeScriptName(const QString &name) d->name = name; } +QAbstractAnimationAction* QDeclarativeScriptActionPrivate::createAction() +{ + return new Proxy(this); +} + void QDeclarativeScriptActionPrivate::execute() { Q_Q(QDeclarativeScriptAction); @@ -833,7 +911,7 @@ void QDeclarativeScriptActionPrivate::execute() } } -void QDeclarativeScriptAction::transition(QDeclarativeStateActions &actions, +QAbstractAnimationJob* QDeclarativeScriptAction::transition(QDeclarativeStateActions &actions, QDeclarativeProperties &modified, TransitionDirection direction) { @@ -853,16 +931,9 @@ void QDeclarativeScriptAction::transition(QDeclarativeStateActions &actions, 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 @@ -906,21 +977,12 @@ QAbstractAnimation *QDeclarativeScriptAction::qtAnimation() 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); @@ -1023,13 +1085,7 @@ void QDeclarativePropertyAction::setValue(const QVariant &v) } } -QAbstractAnimation *QDeclarativePropertyAction::qtAnimation() -{ - Q_D(QDeclarativePropertyAction); - return d->spa; -} - -void QDeclarativePropertyAction::transition(QDeclarativeStateActions &actions, +QAbstractAnimationJob* QDeclarativePropertyAction::transition(QDeclarativeStateActions &actions, QDeclarativeProperties &modified, TransitionDirection direction) { @@ -1117,11 +1173,13 @@ void QDeclarativePropertyAction::transition(QDeclarativeStateActions &actions, } } + QActionAnimation *action = new QActionAnimation; if (data->actions.count()) { - d->spa->setAnimAction(data, QAbstractAnimation::DeleteWhenStopped); + action->setAnimAction(data); } else { delete data; } + return initInstance(action); } /*! @@ -1258,8 +1316,8 @@ QDeclarativeVector3dAnimation::QDeclarativeVector3dAnimation(QObject *parent) { Q_D(QDeclarativePropertyAnimation); d->interpolatorType = QMetaType::QVector3D; - d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); d->defaultToInterpolatorType = true; + d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); } QDeclarativeVector3dAnimation::~QDeclarativeVector3dAnimation() @@ -1503,7 +1561,6 @@ void QDeclarativeRotationAnimation::setDirection(QDeclarativeRotationAnimation:: d->interpolator = QVariantAnimationPrivate::getInterpolator(d->interpolatorType); break; } - emit directionChanged(); } @@ -1524,9 +1581,6 @@ void QDeclarativeAnimationGroupPrivate::append_animation(QDeclarativeListPropert 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()); } } @@ -1536,8 +1590,6 @@ void QDeclarativeAnimationGroupPrivate::clear_animation(QDeclarativeListProperty 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); } } @@ -1592,27 +1644,20 @@ QDeclarativeListProperty<QDeclarativeAbstractAnimation> QDeclarativeAnimationGro 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) { @@ -1621,11 +1666,15 @@ void QDeclarativeSequentialAnimation::transition(QDeclarativeStateActions &actio } 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); } @@ -1661,36 +1710,30 @@ void QDeclarativeSequentialAnimation::transition(QDeclarativeStateActions &actio 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) { @@ -1744,6 +1787,41 @@ void QDeclarativePropertyAnimationPrivate::convertVariant(QVariant &variant, int } } +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 @@ -1809,28 +1887,17 @@ void QDeclarativePropertyAnimationPrivate::convertVariant(QVariant &variant, int 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. @@ -1840,7 +1907,7 @@ void QDeclarativePropertyAnimationPrivate::init() int QDeclarativePropertyAnimation::duration() const { Q_D(const QDeclarativePropertyAnimation); - return d->va->duration(); + return d->duration; } void QDeclarativePropertyAnimation::setDuration(int duration) @@ -1851,9 +1918,9 @@ 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); } @@ -2122,16 +2189,16 @@ void QDeclarativePropertyAnimation::setTo(const QVariant &t) 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); } @@ -2288,28 +2355,23 @@ QDeclarativeListProperty<QObject> QDeclarativePropertyAnimation::exclude() 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(); @@ -2328,11 +2390,11 @@ void QDeclarativeAnimationPropertyUpdater::setValue(qreal v) 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) @@ -2356,13 +2418,6 @@ void QDeclarativePropertyAnimation::transition(QDeclarativeStateActions &actions 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) { @@ -2377,7 +2432,7 @@ void QDeclarativePropertyAnimation::transition(QDeclarativeStateActions &actions } 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]; @@ -2420,31 +2475,39 @@ void QDeclarativePropertyAnimation::transition(QDeclarativeStateActions &actions 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 diff --git a/src/quick/util/qdeclarativeanimation_p.h b/src/quick/util/qdeclarativeanimation_p.h index 7cf6bbe35e..5b69c5fb29 100644 --- a/src/quick/util/qdeclarativeanimation_p.h +++ b/src/quick/util/qdeclarativeanimation_p.h @@ -51,7 +51,7 @@ #include <QtCore/qvariant.h> #include <QtCore/qeasingcurve.h> -#include <QtCore/QAbstractAnimation> +#include "private/qabstractanimationjob_p.h" #include <QtGui/qcolor.h> QT_BEGIN_HEADER @@ -89,6 +89,7 @@ public: int loops() const; void setLoops(int); + int duration() const; int currentTime(); void setCurrentTime(int); @@ -98,7 +99,8 @@ public: void setDefaultTarget(const QDeclarativeProperty &); void setDisableUserControl(); - + void setEnableUserControl(); + bool userControlDisabled() const; void classBegin(); void componentComplete(); @@ -120,27 +122,26 @@ public Q_SLOTS: 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) @@ -158,7 +159,9 @@ Q_SIGNALS: void durationChanged(int); protected: - virtual QAbstractAnimation *qtAnimation(); + QAbstractAnimationJob* transition(QDeclarativeStateActions &actions, + QDeclarativeProperties &modified, + TransitionDirection direction); }; class QDeclarativeScriptActionPrivate; @@ -181,14 +184,13 @@ public: 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) @@ -226,14 +228,13 @@ Q_SIGNALS: 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) @@ -277,12 +278,13 @@ public: 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); @@ -293,7 +295,7 @@ Q_SIGNALS: void propertyChanged(); }; -class Q_AUTOTEST_EXPORT QDeclarativeColorAnimation : public QDeclarativePropertyAnimation +class Q_QUICK_PRIVATE_EXPORT QDeclarativeColorAnimation : public QDeclarativePropertyAnimation { Q_OBJECT Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation) @@ -311,7 +313,7 @@ public: 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) @@ -336,7 +338,7 @@ private: void init(); }; -class Q_AUTOTEST_EXPORT QDeclarativeVector3dAnimation : public QDeclarativePropertyAnimation +class Q_QUICK_PRIVATE_EXPORT QDeclarativeVector3dAnimation : public QDeclarativePropertyAnimation { Q_OBJECT Q_DECLARE_PRIVATE(QDeclarativePropertyAnimation) @@ -356,7 +358,7 @@ public: }; class QDeclarativeRotationAnimationPrivate; -class Q_AUTOTEST_EXPORT QDeclarativeRotationAnimation : public QDeclarativePropertyAnimation +class Q_QUICK_PRIVATE_EXPORT QDeclarativeRotationAnimation : public QDeclarativePropertyAnimation { Q_OBJECT Q_DECLARE_PRIVATE(QDeclarativeRotationAnimation) @@ -385,7 +387,7 @@ Q_SIGNALS: }; class QDeclarativeAnimationGroupPrivate; -class Q_AUTOTEST_EXPORT QDeclarativeAnimationGroup : public QDeclarativeAbstractAnimation +class Q_QUICK_PRIVATE_EXPORT QDeclarativeAnimationGroup : public QDeclarativeAbstractAnimation { Q_OBJECT Q_DECLARE_PRIVATE(QDeclarativeAnimationGroup) @@ -414,13 +416,12 @@ public: 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) @@ -430,10 +431,9 @@ public: virtual ~QDeclarativeParallelAnimation(); protected: - virtual void transition(QDeclarativeStateActions &actions, + virtual QAbstractAnimationJob* transition(QDeclarativeStateActions &actions, QDeclarativeProperties &modified, TransitionDirection direction); - virtual QAbstractAnimation *qtAnimation(); }; diff --git a/src/quick/util/qdeclarativeanimation_p_p.h b/src/quick/util/qdeclarativeanimation_p_p.h index 53408e1e8a..5bcd70472d 100644 --- a/src/quick/util/qdeclarativeanimation_p_p.h +++ b/src/quick/util/qdeclarativeanimation_p_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef QDECLARATIVEANIMATION_P_H -#define QDECLARATIVEANIMATION_P_H +#ifndef QDECLARATIVEANIMATION2_P_H +#define QDECLARATIVEANIMATION2_P_H // // W A R N I N G @@ -60,13 +60,16 @@ #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 @@ -85,51 +88,32 @@ template<class T, void (T::*method)()> 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 @@ -140,83 +124,64 @@ public: }; //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; @@ -225,10 +190,12 @@ public: int loopCount; void commence(); + virtual void animationFinished(QAbstractAnimationJob *); QDeclarativeProperty defaultProperty; QDeclarativeAnimationGroup *group; + QAbstractAnimationJob* animationInstance; static QDeclarativeProperty createProperty(QObject *obj, const QString &str, QObject *infoObj); }; @@ -238,11 +205,9 @@ class QDeclarativePauseAnimationPrivate : public QDeclarativeAbstractAnimationPr Q_DECLARE_PUBLIC(QDeclarativePauseAnimation) public: QDeclarativePauseAnimationPrivate() - : QDeclarativeAbstractAnimationPrivate(), pa(0) {} + : QDeclarativeAbstractAnimationPrivate(), duration(250) {} - void init(); - - QPauseAnimation *pa; + int duration; }; class QDeclarativeScriptActionPrivate : public QDeclarativeAbstractAnimationPrivate @@ -251,8 +216,6 @@ class QDeclarativeScriptActionPrivate : public QDeclarativeAbstractAnimationPriv public: QDeclarativeScriptActionPrivate(); - void init(); - QDeclarativeScriptString script; QString name; QDeclarativeScriptString runScriptScript; @@ -260,10 +223,9 @@ public: bool reversing; void execute(); - - QAnimationActionProxy<QDeclarativeScriptActionPrivate, - &QDeclarativeScriptActionPrivate::execute> proxy; - QActionAnimation *rsa; + QAbstractAnimationAction* createAction(); + typedef QAnimationActionProxy<QDeclarativeScriptActionPrivate, + &QDeclarativeScriptActionPrivate::execute> Proxy; }; class QDeclarativePropertyActionPrivate : public QDeclarativeAbstractAnimationPrivate @@ -271,9 +233,7 @@ class QDeclarativePropertyActionPrivate : public QDeclarativeAbstractAnimationPr Q_DECLARE_PUBLIC(QDeclarativePropertyAction) public: QDeclarativePropertyActionPrivate() - : QDeclarativeAbstractAnimationPrivate(), target(0), spa(0) {} - - void init(); + : QDeclarativeAbstractAnimationPrivate(), target(0) {} QObject *target; QString propertyName; @@ -282,8 +242,6 @@ public: QList<QObject *> exclude; QDeclarativeNullableValue<QVariant> value; - - QActionAnimation *spa; }; class QDeclarativeAnimationGroupPrivate : public QDeclarativeAbstractAnimationPrivate @@ -291,12 +249,11 @@ class QDeclarativeAnimationGroupPrivate : public QDeclarativeAbstractAnimationPr 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 @@ -305,9 +262,7 @@ class QDeclarativePropertyAnimationPrivate : public QDeclarativeAbstractAnimatio 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; @@ -322,12 +277,11 @@ public: 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; @@ -348,19 +302,21 @@ public: 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 diff --git a/src/quick/util/qdeclarativeanimationcontroller.cpp b/src/quick/util/qdeclarativeanimationcontroller.cpp new file mode 100644 index 0000000000..3901a65cb6 --- /dev/null +++ b/src/quick/util/qdeclarativeanimationcontroller.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** 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 + + diff --git a/src/quick/util/qdeclarativeanimationcontroller_p.h b/src/quick/util/qdeclarativeanimationcontroller_p.h new file mode 100644 index 0000000000..d4000b53d9 --- /dev/null +++ b/src/quick/util/qdeclarativeanimationcontroller_p.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** 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 diff --git a/src/quick/util/qdeclarativebehavior.cpp b/src/quick/util/qdeclarativebehavior.cpp index daed2e806d..726c39fa9f 100644 --- a/src/quick/util/qdeclarativebehavior.cpp +++ b/src/quick/util/qdeclarativebehavior.cpp @@ -47,21 +47,26 @@ #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; @@ -102,6 +107,8 @@ QDeclarativeBehavior::QDeclarativeBehavior(QObject *parent) QDeclarativeBehavior::~QDeclarativeBehavior() { + Q_D(QDeclarativeBehavior); + delete d->animationInstance; } /*! @@ -129,22 +136,16 @@ void QDeclarativeBehavior::setAnimation(QDeclarativeAbstractAnimation *animation 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 @@ -187,10 +188,10 @@ void QDeclarativeBehavior::write(const QVariant &value) 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; @@ -201,8 +202,14 @@ void QDeclarativeBehavior::write(const QVariant &value) 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); @@ -216,7 +223,10 @@ void QDeclarativeBehavior::setTarget(const QDeclarativeProperty &property) 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() diff --git a/src/quick/util/qdeclarativebehavior_p.h b/src/quick/util/qdeclarativebehavior_p.h index 5b26aa90d3..c95304dce7 100644 --- a/src/quick/util/qdeclarativebehavior_p.h +++ b/src/quick/util/qdeclarativebehavior_p.h @@ -46,7 +46,6 @@ #include <private/qdeclarativepropertyvalueinterceptor_p.h> #include <qdeclarative.h> -#include <QtCore/QAbstractAnimation> QT_BEGIN_HEADER @@ -83,7 +82,6 @@ Q_SIGNALS: private Q_SLOTS: void componentFinalized(); - void qtAnimationStateChanged(QAbstractAnimation::State,QAbstractAnimation::State); }; QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativesmoothedanimation.cpp b/src/quick/util/qdeclarativesmoothedanimation.cpp index 52e870a3be..9b61561fef 100644 --- a/src/quick/util/qdeclarativesmoothedanimation.cpp +++ b/src/quick/util/qdeclarativesmoothedanimation.cpp @@ -57,42 +57,88 @@ 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 @@ -196,7 +242,9 @@ qreal QSmoothedAnimation::easeFollow(qreal time_seconds) 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); @@ -212,8 +260,8 @@ void QSmoothedAnimation::init() return; } - if (delayedStopTimer.isActive()) - delayedStopTimer.stop(); + if (delayedStopTimer->isActive()) + delayedStopTimer->stop(); initialValue = target.read().toReal(); lastTime = this->currentTime(); @@ -312,14 +360,22 @@ QDeclarativeSmoothedAnimation::QDeclarativeSmoothedAnimation(QObject *parent) 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() @@ -333,59 +389,56 @@ 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; } /*! diff --git a/src/quick/util/qdeclarativesmoothedanimation_p.h b/src/quick/util/qdeclarativesmoothedanimation_p.h index 02acf205d9..d065a20516 100644 --- a/src/quick/util/qdeclarativesmoothedanimation_p.h +++ b/src/quick/util/qdeclarativesmoothedanimation_p.h @@ -53,7 +53,7 @@ QT_BEGIN_NAMESPACE 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) @@ -81,11 +81,9 @@ public: 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(); diff --git a/src/quick/util/qdeclarativesmoothedanimation_p_p.h b/src/quick/util/qdeclarativesmoothedanimation_p_p.h index 07d8b2b271..9afb6506a7 100644 --- a/src/quick/util/qdeclarativesmoothedanimation_p_p.h +++ b/src/quick/util/qdeclarativesmoothedanimation_p_p.h @@ -39,8 +39,8 @@ ** ****************************************************************************/ -#ifndef QDECLARATIVESMOOTHEDANIMATION_P_H -#define QDECLARATIVESMOOTHEDANIMATION_P_H +#ifndef QDECLARATIVESMOOTHEDANIMATION2_P_H +#define QDECLARATIVESMOOTHEDANIMATION2_P_H // // W A R N I N G @@ -58,18 +58,33 @@ #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; @@ -86,10 +101,12 @@ public: 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); @@ -112,11 +129,12 @@ private: qreal s; // Total s int lastTime; + bool useDelta; bool recalc(); void delayedStop(); - - QBasicTimer delayedStopTimer; + QSmoothedAnimationTimer *delayedStopTimer; + QDeclarativeSmoothedAnimationPrivate *animationTemplate; }; class QDeclarativeSmoothedAnimationPrivate : public QDeclarativePropertyAnimationPrivate @@ -124,13 +142,13 @@ class QDeclarativeSmoothedAnimationPrivate : public QDeclarativePropertyAnimatio 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 diff --git a/src/quick/util/qdeclarativespringanimation.cpp b/src/quick/util/qdeclarativespringanimation.cpp index 27ef5bd62e..40408bb112 100644 --- a/src/quick/util/qdeclarativespringanimation.cpp +++ b/src/quick/util/qdeclarativespringanimation.cpp @@ -43,6 +43,7 @@ #include "qdeclarativeanimation_p_p.h" #include <private/qdeclarativeproperty_p.h> +#include "private/qparallelanimationgroupjob_p.h" #include <QtCore/qdebug.h> @@ -51,29 +52,39 @@ #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; @@ -82,75 +93,161 @@ public: 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; @@ -158,31 +255,31 @@ bool QDeclarativeSpringAnimationPrivate::animate(const QDeclarativeProperty &pro 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; @@ -190,45 +287,54 @@ bool QDeclarativeSpringAnimationPrivate::animate(const QDeclarativeProperty &pro 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; } } } @@ -264,12 +370,15 @@ void QDeclarativeSpringAnimationPrivate::updateMode() 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(); + } } /*! @@ -415,48 +524,69 @@ void QDeclarativeSpringAnimation::setMass(qreal mass) } } -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 diff --git a/src/quick/util/qdeclarativespringanimation_p.h b/src/quick/util/qdeclarativespringanimation_p.h index 169cd89e9d..21515d087b 100644 --- a/src/quick/util/qdeclarativespringanimation_p.h +++ b/src/quick/util/qdeclarativespringanimation_p.h @@ -52,7 +52,7 @@ QT_BEGIN_HEADER 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) @@ -87,13 +87,10 @@ public: 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(); diff --git a/src/quick/util/qdeclarativetimeline.cpp b/src/quick/util/qdeclarativetimeline.cpp index b38f361e88..a33fdd5104 100644 --- a/src/quick/util/qdeclarativetimeline.cpp +++ b/src/quick/util/qdeclarativetimeline.cpp @@ -311,7 +311,7 @@ qreal QDeclarativeTimeLinePrivate::value(const Op &op, int time, qreal base, boo Construct a new QDeclarativeTimeLine with the specified \a parent. */ QDeclarativeTimeLine::QDeclarativeTimeLine(QObject *parent) -: QAbstractAnimation(parent) + : QObject(parent) { d = new QDeclarativeTimeLinePrivate(this); } diff --git a/src/quick/util/qdeclarativetimeline_p_p.h b/src/quick/util/qdeclarativetimeline_p_p.h index 2f82bd75ae..421e2e5976 100644 --- a/src/quick/util/qdeclarativetimeline_p_p.h +++ b/src/quick/util/qdeclarativetimeline_p_p.h @@ -54,7 +54,7 @@ // #include <QtCore/QObject> -#include <QtCore/QAbstractAnimation> +#include "private/qabstractanimationjob_p.h" QT_BEGIN_NAMESPACE @@ -63,7 +63,7 @@ class QDeclarativeTimeLineValue; class QDeclarativeTimeLineCallback; struct QDeclarativeTimeLinePrivate; class QDeclarativeTimeLineObject; -class Q_AUTOTEST_EXPORT QDeclarativeTimeLine : public QAbstractAnimation +class Q_AUTOTEST_EXPORT QDeclarativeTimeLine : public QObject, QAbstractAnimationJob { Q_OBJECT public: diff --git a/src/quick/util/qdeclarativetimer.cpp b/src/quick/util/qdeclarativetimer.cpp index e4ec54f7eb..f54e9b305a 100644 --- a/src/quick/util/qdeclarativetimer.cpp +++ b/src/quick/util/qdeclarativetimer.cpp @@ -42,7 +42,7 @@ #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> @@ -51,15 +51,19 @@ QT_BEGIN_NAMESPACE -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; @@ -111,8 +115,7 @@ QDeclarativeTimer::QDeclarativeTimer(QObject *parent) : 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); } @@ -310,15 +313,15 @@ void QDeclarativeTimer::ticked() 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 diff --git a/src/quick/util/qdeclarativetimer_p.h b/src/quick/util/qdeclarativetimer_p.h index 8cec031a12..e400b3dda3 100644 --- a/src/quick/util/qdeclarativetimer_p.h +++ b/src/quick/util/qdeclarativetimer_p.h @@ -45,7 +45,6 @@ #include <qdeclarative.h> #include <QtCore/qobject.h> -#include <QtCore/qabstractanimation.h> #include <private/qtquickglobal_p.h> @@ -101,7 +100,6 @@ private: private Q_SLOTS: void ticked(); - void finished(); }; QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativetransition.cpp b/src/quick/util/qdeclarativetransition.cpp index 12966b53f7..2a05dd20d3 100644 --- a/src/quick/util/qdeclarativetransition.cpp +++ b/src/quick/util/qdeclarativetransition.cpp @@ -48,7 +48,7 @@ #include "qdeclarativeanimation_p_p.h" #include "qdeclarativetransitionmanager_p_p.h" -#include <QParallelAnimationGroup> +#include "private/qparallelanimationgroupjob_p.h" QT_BEGIN_NAMESPACE @@ -96,16 +96,44 @@ 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 @@ -114,9 +142,8 @@ 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; @@ -124,13 +151,7 @@ public: 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); @@ -142,7 +163,6 @@ void QDeclarativeTransitionPrivate::append_animation(QDeclarativeListProperty<QD { QDeclarativeTransition *q = static_cast<QDeclarativeTransition *>(list->object); q->d_func()->animations.append(a); - q->d_func()->group.addAnimation(a->qtAnimation()); a->setDisableUserControl(); } @@ -163,37 +183,30 @@ void QDeclarativeTransitionPrivate::clear_animations(QDeclarativeListProperty<QD 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) @@ -202,7 +215,7 @@ 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) { @@ -210,19 +223,26 @@ void QDeclarativeTransition::prepare(QDeclarativeStateOperation::ActionList &act 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; } /*! @@ -389,4 +409,4 @@ QDeclarativeListProperty<QDeclarativeAbstractAnimation> QDeclarativeTransition:: QT_END_NAMESPACE -#include <qdeclarativetransition.moc> +//#include <qdeclarativetransition.moc> diff --git a/src/quick/util/qdeclarativetransition_p.h b/src/quick/util/qdeclarativetransition_p.h index a4f6dc46a5..64fd45b725 100644 --- a/src/quick/util/qdeclarativetransition_p.h +++ b/src/quick/util/qdeclarativetransition_p.h @@ -43,7 +43,6 @@ #define QDECLARATIVETRANSITION_H #include "qdeclarativestate_p.h" - #include <qdeclarative.h> #include <QtCore/qobject.h> @@ -55,6 +54,25 @@ QT_BEGIN_NAMESPACE 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 @@ -86,12 +104,11 @@ public: 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(); diff --git a/src/quick/util/qdeclarativetransitionmanager.cpp b/src/quick/util/qdeclarativetransitionmanager.cpp index 5542548937..a2de7dbae6 100644 --- a/src/quick/util/qdeclarativetransitionmanager.cpp +++ b/src/quick/util/qdeclarativetransitionmanager.cpp @@ -43,7 +43,6 @@ #include "qdeclarativetransition_p.h" #include "qdeclarativestate_p_p.h" -#include "qdeclarativestate_p.h" #include <private/qdeclarativebinding_p.h> #include <private/qdeclarativeglobal_p.h> @@ -59,12 +58,12 @@ class QDeclarativeTransitionManagerPrivate { 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; }; @@ -81,9 +80,15 @@ void QDeclarativeTransitionManager::setState(QDeclarativeState *s) QDeclarativeTransitionManager::~QDeclarativeTransitionManager() { + delete d->transitionInstance; delete d; d = 0; } +bool QDeclarativeTransitionManager::isRunning() const +{ + return d->transitionInstance && d->transitionInstance->isRunning(); +} + void QDeclarativeTransitionManager::complete() { d->applyBindings(); @@ -97,6 +102,8 @@ void QDeclarativeTransitionManager::complete() if (d->state) static_cast<QDeclarativeStatePrivate*>(QObjectPrivate::get(d->state))->complete(); + + finished(); } void QDeclarativeTransitionManagerPrivate::applyBindings() @@ -116,6 +123,10 @@ void QDeclarativeTransitionManagerPrivate::applyBindings() bindingsList.clear(); } +void QDeclarativeTransitionManager::finished() +{ +} + void QDeclarativeTransitionManager::transition(const QList<QDeclarativeAction> &list, QDeclarativeTransition *transition) { @@ -196,8 +207,11 @@ void QDeclarativeTransitionManager::transition(const QList<QDeclarativeAction> & 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) { @@ -257,11 +271,8 @@ void QDeclarativeTransitionManager::transition(const QList<QDeclarativeAction> & 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]; diff --git a/src/quick/util/qdeclarativetransitionmanager_p_p.h b/src/quick/util/qdeclarativetransitionmanager_p_p.h index adbde004df..4e5d1a9617 100644 --- a/src/quick/util/qdeclarativetransitionmanager_p_p.h +++ b/src/quick/util/qdeclarativetransitionmanager_p_p.h @@ -54,6 +54,7 @@ // #include "qdeclarativestateoperations_p.h" +#include "qdeclarativeanimation_p.h" QT_BEGIN_NAMESPACE @@ -65,10 +66,15 @@ public: 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; @@ -77,7 +83,7 @@ private: void setState(QDeclarativeState *); friend class QDeclarativeState; - friend class QDeclarativeTransitionPrivate; + friend class ParallelAnimationWrapper; }; QT_END_NAMESPACE diff --git a/src/quick/util/qdeclarativeutilmodule.cpp b/src/quick/util/qdeclarativeutilmodule.cpp index 80f517c9a1..1841a8c94f 100644 --- a/src/quick/util/qdeclarativeutilmodule.cpp +++ b/src/quick/util/qdeclarativeutilmodule.cpp @@ -59,6 +59,7 @@ #include "qdeclarativetransition_p.h" #include <qdeclarativeinfo.h> #include <private/qdeclarativetypenotavailable_p.h> +#include <private/qdeclarativeanimationcontroller_p.h> #include <QtCore/qcoreapplication.h> #include <QtGui/QInputPanel> @@ -83,6 +84,7 @@ void QDeclarativeUtilModule::defineModule() 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"); diff --git a/src/quick/util/util.pri b/src/quick/util/util.pri index 9c8964bfef..d933459596 100644 --- a/src/quick/util/util.pri +++ b/src/quick/util/util.pri @@ -6,6 +6,7 @@ SOURCES += \ $$PWD/qdeclarativesystempalette.cpp \ $$PWD/qdeclarativespringanimation.cpp \ $$PWD/qdeclarativesmoothedanimation.cpp \ + $$PWD/qdeclarativeanimationcontroller.cpp \ $$PWD/qdeclarativestate.cpp\ $$PWD/qdeclarativetransitionmanager.cpp \ $$PWD/qdeclarativestateoperations.cpp \ @@ -34,6 +35,7 @@ HEADERS += \ $$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\ @@ -56,4 +58,4 @@ HEADERS += \ $$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 diff --git a/tests/auto/declarative/animation/animation.pro b/tests/auto/declarative/animation/animation.pro new file mode 100644 index 0000000000..a9c0cee309 --- /dev/null +++ b/tests/auto/declarative/animation/animation.pro @@ -0,0 +1,7 @@ +TEMPLATE=subdirs +SUBDIRS=\ + qabstractanimationjob \ + qanimationgroupjob \ + qparallelanimationgroupjob \ + qpauseanimationjob \ + qsequentialanimationgroupjob diff --git a/tests/auto/declarative/animation/qabstractanimationjob/qabstractanimationjob.pro b/tests/auto/declarative/animation/qabstractanimationjob/qabstractanimationjob.pro new file mode 100644 index 0000000000..db8649bbb9 --- /dev/null +++ b/tests/auto/declarative/animation/qabstractanimationjob/qabstractanimationjob.pro @@ -0,0 +1,5 @@ +CONFIG += testcase parallel_test +macx:CONFIG -= app_bundle +TARGET = tst_qabstractanimationjob +QT = core-private declarative-private testlib +SOURCES = tst_qabstractanimationjob.cpp diff --git a/tests/auto/declarative/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp b/tests/auto/declarative/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp new file mode 100644 index 0000000000..772605d494 --- /dev/null +++ b/tests/auto/declarative/animation/qabstractanimationjob/tst_qabstractanimationjob.cpp @@ -0,0 +1,229 @@ +/**************************************************************************** +** +** 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" diff --git a/tests/auto/declarative/animation/qanimationgroupjob/qanimationgroupjob.pro b/tests/auto/declarative/animation/qanimationgroupjob/qanimationgroupjob.pro new file mode 100644 index 0000000000..b9c9d2921d --- /dev/null +++ b/tests/auto/declarative/animation/qanimationgroupjob/qanimationgroupjob.pro @@ -0,0 +1,5 @@ +CONFIG += testcase parallel_test +macx:CONFIG -= app_bundle +TARGET = tst_qanimationgroupjob +QT = core-private declarative-private testlib +SOURCES = tst_qanimationgroupjob.cpp diff --git a/tests/auto/declarative/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp b/tests/auto/declarative/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp new file mode 100644 index 0000000000..196f917a1f --- /dev/null +++ b/tests/auto/declarative/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp @@ -0,0 +1,310 @@ +/**************************************************************************** +** +** 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" diff --git a/tests/auto/declarative/animation/qparallelanimationgroupjob/qparallelanimationgroupjob.pro b/tests/auto/declarative/animation/qparallelanimationgroupjob/qparallelanimationgroupjob.pro new file mode 100644 index 0000000000..332fa4554b --- /dev/null +++ b/tests/auto/declarative/animation/qparallelanimationgroupjob/qparallelanimationgroupjob.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +macx:CONFIG -= app_bundle +TARGET = tst_qparallelanimationgroupjob +QT = core-private gui declarative-private testlib +SOURCES = tst_qparallelanimationgroupjob.cpp diff --git a/tests/auto/declarative/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp b/tests/auto/declarative/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp new file mode 100644 index 0000000000..52c0ff284e --- /dev/null +++ b/tests/auto/declarative/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp @@ -0,0 +1,931 @@ +/**************************************************************************** +** +** 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" diff --git a/tests/auto/declarative/animation/qpauseanimationjob/qpauseanimationjob.pro b/tests/auto/declarative/animation/qpauseanimationjob/qpauseanimationjob.pro new file mode 100644 index 0000000000..d2b48a6c74 --- /dev/null +++ b/tests/auto/declarative/animation/qpauseanimationjob/qpauseanimationjob.pro @@ -0,0 +1,5 @@ +CONFIG += testcase +macx:CONFIG -= app_bundle +TARGET = tst_qpauseanimationjob +QT = core-private gui-private declarative-private testlib +SOURCES = tst_qpauseanimationjob.cpp diff --git a/tests/auto/declarative/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp b/tests/auto/declarative/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp new file mode 100644 index 0000000000..97be325017 --- /dev/null +++ b/tests/auto/declarative/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp @@ -0,0 +1,470 @@ +/**************************************************************************** +** +** 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" diff --git a/tests/auto/declarative/animation/qsequentialanimationgroupjob/qsequentialanimationgroupjob.pro b/tests/auto/declarative/animation/qsequentialanimationgroupjob/qsequentialanimationgroupjob.pro new file mode 100644 index 0000000000..914fc3ca62 --- /dev/null +++ b/tests/auto/declarative/animation/qsequentialanimationgroupjob/qsequentialanimationgroupjob.pro @@ -0,0 +1,5 @@ +CONFIG += testcase parallel_test +macx:CONFIG -= app_bundle +TARGET = tst_qsequentialanimationgroupjob +QT = core-private declarative-private testlib +SOURCES = tst_qsequentialanimationgroupjob.cpp diff --git a/tests/auto/declarative/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp b/tests/auto/declarative/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp new file mode 100644 index 0000000000..895e4f849e --- /dev/null +++ b/tests/auto/declarative/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp @@ -0,0 +1,1617 @@ +/**************************************************************************** +** +** 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" diff --git a/tests/auto/declarative/declarative.pro b/tests/auto/declarative/declarative.pro index 6780a87f55..7288da27ba 100644 --- a/tests/auto/declarative/declarative.pro +++ b/tests/auto/declarative/declarative.pro @@ -27,6 +27,7 @@ PUBLICTESTS += \ qmlplugindump PRIVATETESTS += \ + animation \ qdeclarativebinding \ qdeclarativechangeset \ qdeclarativeconnection \ diff --git a/tests/auto/qtquick2/qdeclarativeanimationcontroller/data/tst_numberanimation.qml b/tests/auto/qtquick2/qdeclarativeanimationcontroller/data/tst_numberanimation.qml new file mode 100644 index 0000000000..7c4496b206 --- /dev/null +++ b/tests/auto/qtquick2/qdeclarativeanimationcontroller/data/tst_numberanimation.qml @@ -0,0 +1,38 @@ +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 diff --git a/tests/auto/qtquick2/qdeclarativeanimationcontroller/qdeclarativeanimationcontroller.pro b/tests/auto/qtquick2/qdeclarativeanimationcontroller/qdeclarativeanimationcontroller.pro new file mode 100644 index 0000000000..52cafc33a6 --- /dev/null +++ b/tests/auto/qtquick2/qdeclarativeanimationcontroller/qdeclarativeanimationcontroller.pro @@ -0,0 +1,10 @@ +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 diff --git a/tests/auto/qtquick2/qdeclarativeanimationcontroller/tst_qdeclarativeanimationcontroller.cpp b/tests/auto/qtquick2/qdeclarativeanimationcontroller/tst_qdeclarativeanimationcontroller.cpp new file mode 100644 index 0000000000..744f92b99d --- /dev/null +++ b/tests/auto/qtquick2/qdeclarativeanimationcontroller/tst_qdeclarativeanimationcontroller.cpp @@ -0,0 +1,42 @@ +/**************************************************************************** +** +** 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) diff --git a/tests/auto/qtquick2/qdeclarativeanimations/tst_qdeclarativeanimations.cpp b/tests/auto/qtquick2/qdeclarativeanimations/tst_qdeclarativeanimations.cpp index 1340b514f9..3151a99407 100644 --- a/tests/auto/qtquick2/qdeclarativeanimations/tst_qdeclarativeanimations.cpp +++ b/tests/auto/qtquick2/qdeclarativeanimations/tst_qdeclarativeanimations.cpp @@ -130,8 +130,8 @@ void tst_qdeclarativeanimations::simpleProperty() rect.setPos(QPointF(0,0)); animation.start(); - animation.pause(); QVERIFY(animation.isRunning()); + animation.pause(); QVERIFY(animation.isPaused()); animation.setCurrentTime(125); QVERIFY(animation.currentTime() == 125); @@ -887,8 +887,8 @@ void tst_qdeclarativeanimations::propertyValueSourceDefaultStart() 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); } } @@ -906,8 +906,8 @@ void tst_qdeclarativeanimations::dontStart() 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); } { @@ -921,8 +921,8 @@ void tst_qdeclarativeanimations::dontStart() 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); } } @@ -1086,7 +1086,7 @@ void tst_qdeclarativeanimations::doubleRegistrationBug() 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 @@ -1110,7 +1110,7 @@ void tst_qdeclarativeanimations::alwaysRunToEndRestartBug() 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 @@ -1134,7 +1134,7 @@ void tst_qdeclarativeanimations::pauseBindingBug() 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; } @@ -1147,7 +1147,7 @@ void tst_qdeclarativeanimations::pauseBug() 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); diff --git a/tests/auto/qtquick2/qdeclarativebehaviors/tst_qdeclarativebehaviors.cpp b/tests/auto/qtquick2/qdeclarativebehaviors/tst_qdeclarativebehaviors.cpp index 085f4000f0..c1e60f1560 100644 --- a/tests/auto/qtquick2/qdeclarativebehaviors/tst_qdeclarativebehaviors.cpp +++ b/tests/auto/qtquick2/qdeclarativebehaviors/tst_qdeclarativebehaviors.cpp @@ -335,8 +335,7 @@ void tst_qdeclarativebehaviors::dontStart() QVERIFY(rect); QDeclarativeAbstractAnimation *myAnim = rect->findChild<QDeclarativeAbstractAnimation*>("MyAnim"); - QVERIFY(myAnim && myAnim->qtAnimation()); - QVERIFY(myAnim->qtAnimation()->state() == QAbstractAnimation::Stopped); + QVERIFY(myAnim && !myAnim->qtAnimation()); delete rect; } diff --git a/tests/auto/qtquick2/qdeclarativesmoothedanimation/data/simpleanimation.qml b/tests/auto/qtquick2/qdeclarativesmoothedanimation/data/simpleanimation.qml new file mode 100644 index 0000000000..b2be63ec94 --- /dev/null +++ b/tests/auto/qtquick2/qdeclarativesmoothedanimation/data/simpleanimation.qml @@ -0,0 +1,12 @@ +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 diff --git a/tests/auto/qtquick2/qdeclarativesmoothedanimation/tst_qdeclarativesmoothedanimation.cpp b/tests/auto/qtquick2/qdeclarativesmoothedanimation/tst_qdeclarativesmoothedanimation.cpp index 5ab3e96e06..f60955c58e 100644 --- a/tests/auto/qtquick2/qdeclarativesmoothedanimation/tst_qdeclarativesmoothedanimation.cpp +++ b/tests/auto/qtquick2/qdeclarativesmoothedanimation/tst_qdeclarativesmoothedanimation.cpp @@ -41,7 +41,7 @@ #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" @@ -120,28 +120,40 @@ void tst_qdeclarativesmoothedanimation::disabled() 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() diff --git a/tests/auto/qtquick2/qdeclarativespringanimation/data/springanimation2.qml b/tests/auto/qtquick2/qdeclarativespringanimation/data/springanimation2.qml index 172cc57ca8..9f72e51533 100644 --- a/tests/auto/qtquick2/qdeclarativespringanimation/data/springanimation2.qml +++ b/tests/auto/qtquick2/qdeclarativespringanimation/data/springanimation2.qml @@ -1,9 +1,16 @@ 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; + } } diff --git a/tests/auto/qtquick2/qdeclarativespringanimation/tst_qdeclarativespringanimation.cpp b/tests/auto/qtquick2/qdeclarativespringanimation/tst_qdeclarativespringanimation.cpp index 15a3263eed..64956d7753 100644 --- a/tests/auto/qtquick2/qdeclarativespringanimation/tst_qdeclarativespringanimation.cpp +++ b/tests/auto/qtquick2/qdeclarativespringanimation/tst_qdeclarativespringanimation.cpp @@ -88,7 +88,9 @@ void tst_qdeclarativespringanimation::values() { 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); diff --git a/tests/benchmarks/declarative/animation/animation.pro b/tests/benchmarks/declarative/animation/animation.pro new file mode 100644 index 0000000000..fc72040a3d --- /dev/null +++ b/tests/benchmarks/declarative/animation/animation.pro @@ -0,0 +1,10 @@ +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 diff --git a/tests/benchmarks/declarative/animation/data/animation.qml b/tests/benchmarks/declarative/animation/data/animation.qml new file mode 100644 index 0000000000..c48f5ccbe3 --- /dev/null +++ b/tests/benchmarks/declarative/animation/data/animation.qml @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** 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 {} +} diff --git a/tests/benchmarks/declarative/animation/tst_animation.cpp b/tests/benchmarks/declarative/animation/tst_animation.cpp new file mode 100644 index 0000000000..6273d9740e --- /dev/null +++ b/tests/benchmarks/declarative/animation/tst_animation.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** 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" -- GitLab