Add completeToBeginning()/completeToEnd() to animation controller
authorCharles Yin <charles.yin@nokia.com>
Thu, 17 May 2012 04:43:43 +0000 (14:43 +1000)
committerQt by Nokia <qt-info@nokia.com>
Wed, 23 May 2012 03:28:00 +0000 (05:28 +0200)
Change-Id: I1abac96754cc82c8e0e00c58a27c09b68c5075c1
Reviewed-by: Michael Brasser <michael.brasser@nokia.com>
src/qml/animations/qabstractanimationjob.cpp
src/qml/animations/qabstractanimationjob_p.h
src/quick/util/qquickanimationcontroller.cpp
src/quick/util/qquickanimationcontroller_p.h
tests/auto/quick/qquickanimationcontroller/data/tst_completion.qml [new file with mode: 0755]

index 285937d..52ec00e 100644 (file)
@@ -271,6 +271,8 @@ QAbstractAnimationJob::QAbstractAnimationJob()
     , m_isPause(false)
     , m_isGroup(false)
     , m_disableUserControl(false)
+    , m_hasCurrentTimeChangeListeners(false)
+
 {
 }
 
@@ -449,7 +451,7 @@ void QAbstractAnimationJob::setCurrentTime(int msecs)
     RETURN_IF_DELETED(updateCurrentTime(m_currentTime));
 
     if (m_currentLoop != oldLoop)
-        currentLoopChanged(m_currentLoop);
+        currentLoopChanged();
 
     // All animations are responsible for stopping the animation when their
     // own end state is reached; in this case the animation is time driven,
@@ -458,6 +460,9 @@ void QAbstractAnimationJob::setCurrentTime(int msecs)
         || (m_direction == Backward && m_totalCurrentTime == 0)) {
         stop();
     }
+
+    if (m_hasCurrentTimeChangeListeners)
+        currentTimeChanged(m_currentTime);
 }
 
 void QAbstractAnimationJob::start()
@@ -549,9 +554,8 @@ void QAbstractAnimationJob::stateChanged(QAbstractAnimationJob::State newState,
     }
 }
 
-void QAbstractAnimationJob::currentLoopChanged(int currentLoop)
+void QAbstractAnimationJob::currentLoopChanged()
 {
-    Q_UNUSED(currentLoop);
     for (int i = 0; i < changeListeners.count(); ++i) {
         const QAbstractAnimationJob::ChangeListener &change = changeListeners.at(i);
         if (change.types & QAbstractAnimationJob::CurrentLoop) {
@@ -560,14 +564,39 @@ void QAbstractAnimationJob::currentLoopChanged(int currentLoop)
     }
 }
 
+void QAbstractAnimationJob::currentTimeChanged(int currentTime)
+{
+    Q_ASSERT(m_hasCurrentTimeChangeListeners);
+
+    for (int i = 0; i < changeListeners.count(); ++i) {
+        const QAbstractAnimationJob::ChangeListener &change = changeListeners.at(i);
+        if (change.types & QAbstractAnimationJob::CurrentTime) {
+           RETURN_IF_DELETED(change.listener->animationCurrentTimeChanged(this, currentTime));
+        }
+    }
+}
+
 void QAbstractAnimationJob::addAnimationChangeListener(QAnimationJobChangeListener *listener, QAbstractAnimationJob::ChangeTypes changes)
 {
+    if (changes & QAbstractAnimationJob::CurrentTime)
+        m_hasCurrentTimeChangeListeners = true;
+
     changeListeners.append(ChangeListener(listener, changes));
 }
 
 void QAbstractAnimationJob::removeAnimationChangeListener(QAnimationJobChangeListener *listener, QAbstractAnimationJob::ChangeTypes changes)
 {
+    m_hasCurrentTimeChangeListeners = false;
+
     changeListeners.removeOne(ChangeListener(listener, changes));
+
+    for (int i = 0; i < changeListeners.count(); ++i) {
+        const QAbstractAnimationJob::ChangeListener &change = changeListeners.at(i);
+        if (change.types & QAbstractAnimationJob::CurrentTime) {
+            m_hasCurrentTimeChangeListeners = true;
+            break;
+        }
+    }
 }
 
 QT_END_NAMESPACE
index 4786982..e889a12 100644 (file)
@@ -107,7 +107,8 @@ public:
     enum ChangeType {
         Completion = 0x01,
         StateChange = 0x02,
-        CurrentLoop = 0x04
+        CurrentLoop = 0x04,
+        CurrentTime = 0x08
     };
     Q_DECLARE_FLAGS(ChangeTypes, ChangeType)
 
@@ -126,8 +127,9 @@ protected:
 
     void finished();
     void stateChanged(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState);
-    void currentLoopChanged(int currentLoop);
+    void currentLoopChanged();
     void directionChanged(QAbstractAnimationJob::Direction);
+    void currentTimeChanged(int currentTime);
 
     //definition
     int m_loopCount;
@@ -158,6 +160,7 @@ protected:
     bool m_isPause:1;
     bool m_isGroup:1;
     bool m_disableUserControl:1;
+    bool m_hasCurrentTimeChangeListeners:1;
 
     friend class QQmlAnimationTimer;
     friend class QAnimationGroupJob;
@@ -169,6 +172,7 @@ public:
     virtual void animationFinished(QAbstractAnimationJob *) {}
     virtual void animationStateChanged(QAbstractAnimationJob *, QAbstractAnimationJob::State, QAbstractAnimationJob::State) {}
     virtual void animationCurrentLoopChanged(QAbstractAnimationJob *) {}
+    virtual void animationCurrentTimeChanged(QAbstractAnimationJob *, int) {}
 };
 
 class Q_QML_PRIVATE_EXPORT QQmlAnimationTimer : public QAbstractAnimationTimer
index 2b5d174..52a5833 100644 (file)
 QT_BEGIN_NAMESPACE
 
 
-class QQuickAnimationControllerPrivate : public QObjectPrivate
+class QQuickAnimationControllerPrivate : public QObjectPrivate, QAnimationJobChangeListener
 {
     Q_DECLARE_PUBLIC(QQuickAnimationController)
 public:
     QQuickAnimationControllerPrivate()
         : progress(0.0), animation(0), animationInstance(0), finalized(false) {}
+    virtual void animationFinished(QAbstractAnimationJob *job);
+    virtual void animationCurrentTimeChanged(QAbstractAnimationJob *job, int currentTime);
+
 
     qreal progress;
     QQuickAbstractAnimation *animation;
@@ -60,6 +63,34 @@ public:
 
 };
 
+void QQuickAnimationControllerPrivate::animationFinished(QAbstractAnimationJob *job)
+{
+    Q_Q(QQuickAnimationController);
+    Q_ASSERT(animationInstance && animationInstance == job);
+
+    animationInstance->removeAnimationChangeListener(this, QAbstractAnimationJob::Completion | QAbstractAnimationJob::CurrentTime);
+
+    if (animationInstance->direction() == QAbstractAnimationJob::Forward && progress != 1) {
+        progress = 1;
+        emit q->progressChanged();
+    } else if (animationInstance->direction() == QAbstractAnimationJob::Backward && progress != 0) {
+        progress = 0;
+        emit q->progressChanged();
+    }
+
+}
+
+void QQuickAnimationControllerPrivate::animationCurrentTimeChanged(QAbstractAnimationJob *job, int currentTime)
+{
+    Q_Q(QQuickAnimationController);
+    Q_ASSERT(animationInstance && animationInstance == job);
+    const qreal newProgress = currentTime * 1.0 / animationInstance->duration();
+    if (progress != newProgress) {
+        progress = newProgress;
+        emit q->progressChanged();
+    }
+}
+
 /*!
     \qmlclass AnimationController QQuickAnimationController
     \inqmlmodule QtQuick 2
@@ -200,6 +231,65 @@ void QQuickAnimationController::componentFinalized()
     reload();
 }
 
+/*!
+    \qmlmethod QtQuick2::AnimationController::completeToBeginning()
+    \brief Finishes running the controlled animation in a backwards direction.
+
+    After calling this method, the animation runs normally from the current progress point
+    in a backwards direction to the beginning state.
+
+    The animation controller's progress value will be automatically updated while the animation is running.
+
+    \sa completeToEnd(), progress()
+*/
+void QQuickAnimationController::completeToBeginning()
+{
+    Q_D(QQuickAnimationController);
+    if (!d->animationInstance)
+        return;
+
+    if (d->progress == 0)
+        return;
+
+    d->animationInstance->addAnimationChangeListener(d, QAbstractAnimationJob::Completion | QAbstractAnimationJob::CurrentTime);
+    d->animationInstance->setDirection(QAbstractAnimationJob::Backward);
+
+    //Disable and then enable user control to trigger the animation instance's state change
+    d->animationInstance->setDisableUserControl();
+    d->animationInstance->setEnableUserControl();
+    d->animationInstance->start();
+}
+
+/*!
+    \qmlmethod QtQuick2::AnimationController::completeToEnd()
+    \brief Finishes running the controlled animation in a forwards direction.
+
+    After calling this method, the animation runs normally from the current progress point
+    in a forwards direction to the end state.
+
+    The animation controller's progress value will be automatically updated while the animation is running.
+
+    \sa completeToBeginning(), progress()
+*/
+void QQuickAnimationController::completeToEnd()
+{
+    Q_D(QQuickAnimationController);
+    if (!d->animationInstance)
+        return;
+
+    if (d->progress == 1)
+        return;
+
+    d->animationInstance->addAnimationChangeListener(d, QAbstractAnimationJob::Completion | QAbstractAnimationJob::CurrentTime);
+    d->animationInstance->setDirection(QAbstractAnimationJob::Forward);
+
+    //Disable and then enable user control to trigger the animation instance's state change
+    d->animationInstance->setDisableUserControl();
+    d->animationInstance->setEnableUserControl();
+    d->animationInstance->start();
+}
+
+
 
 QT_END_NAMESPACE
 
index 2b2b2a8..2813bf2 100644 (file)
@@ -80,6 +80,8 @@ Q_SIGNALS:
     void animationChanged();
 public Q_SLOTS:
     void reload();
+    void completeToBeginning();
+    void completeToEnd();
 private Q_SLOTS:
     void componentFinalized();
     void updateProgress();
diff --git a/tests/auto/quick/qquickanimationcontroller/data/tst_completion.qml b/tests/auto/quick/qquickanimationcontroller/data/tst_completion.qml
new file mode 100755 (executable)
index 0000000..92e252f
--- /dev/null
@@ -0,0 +1,43 @@
+import QtQuick 2.0
+import QtTest 1.0
+
+Rectangle {
+  id:container
+  width:110
+  height:40
+
+  Rectangle {id:rect; x:0; y:0; color:"red"; width:10; height:10}
+  AnimationController {
+     id:ctrl
+     progress:0
+     animation: NumberAnimation {id:anim; target: rect; property:"x"; to:100; from:0; duration: 500}
+  }
+
+  TestCase {
+    name:"AnimationController"
+    when:windowShown
+    function test_complete() {
+      ctrl.progress = 0;
+      compare(rect.x, 0);
+      ctrl.progress = 0.5;
+      compare(rect.x, 50);
+
+      ctrl.completeToBeginning();
+      wait(200);
+      verify(ctrl.progress < 0.5 && ctrl.progress > 0);
+      wait(600);
+      compare(ctrl.progress, 0);
+      compare(rect.x, 0);
+
+      ctrl.progress = 0.5;
+      compare(rect.x, 50);
+
+      ctrl.completeToEnd();
+      wait(200);
+      verify(ctrl.progress > 0.5 && ctrl.progress < 1);
+      wait(600);
+      compare(ctrl.progress, 1);
+      compare(rect.x, 100);
+    }
+  }
+}
\ No newline at end of file