aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/animations/qabstractanimationjob.cpp35
-rw-r--r--src/qml/animations/qabstractanimationjob_p.h8
-rw-r--r--src/quick/util/qquickanimationcontroller.cpp92
-rw-r--r--src/quick/util/qquickanimationcontroller_p.h2
-rwxr-xr-xtests/auto/quick/qquickanimationcontroller/data/tst_completion.qml43
5 files changed, 174 insertions, 6 deletions
diff --git a/src/qml/animations/qabstractanimationjob.cpp b/src/qml/animations/qabstractanimationjob.cpp
index 285937db2a..52ec00e7e0 100644
--- a/src/qml/animations/qabstractanimationjob.cpp
+++ b/src/qml/animations/qabstractanimationjob.cpp
@@ -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
diff --git a/src/qml/animations/qabstractanimationjob_p.h b/src/qml/animations/qabstractanimationjob_p.h
index 47869826ec..e889a12aed 100644
--- a/src/qml/animations/qabstractanimationjob_p.h
+++ b/src/qml/animations/qabstractanimationjob_p.h
@@ -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
diff --git a/src/quick/util/qquickanimationcontroller.cpp b/src/quick/util/qquickanimationcontroller.cpp
index 2b5d174caa..52a58339a6 100644
--- a/src/quick/util/qquickanimationcontroller.cpp
+++ b/src/quick/util/qquickanimationcontroller.cpp
@@ -46,12 +46,15 @@
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
diff --git a/src/quick/util/qquickanimationcontroller_p.h b/src/quick/util/qquickanimationcontroller_p.h
index 2b2b2a84ec..2813bf2bd9 100644
--- a/src/quick/util/qquickanimationcontroller_p.h
+++ b/src/quick/util/qquickanimationcontroller_p.h
@@ -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
index 0000000000..92e252fc4c
--- /dev/null
+++ b/tests/auto/quick/qquickanimationcontroller/data/tst_completion.qml
@@ -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