Make a group animation dirty when a member changed
Some properties of one of the member animations in a group animation can be changed after the group animation initialized. 1. If the group animation is already proceeding, they will affect the next loop. 2. If the group animation started but does not proceed, they will affect the current loop Fixes: QTBUG-110589 Fixes: QTBUG-61282 Fixes: QTBUG-95840 Pick-to: 6.6 6.5 Change-Id: I018105bdd75dd5bd7c24e9126853cd79c8f0123b Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io>
This commit is contained in:
parent
9831b38fc5
commit
0e69268b49
|
@ -165,6 +165,27 @@ QQmlProperty QQuickAbstractAnimationPrivate::createProperty(QObject *obj, const
|
|||
return prop;
|
||||
}
|
||||
|
||||
void QQuickAbstractAnimationPrivate::animationGroupDirty()
|
||||
{
|
||||
Q_ASSERT(group != nullptr);
|
||||
if (!componentComplete)
|
||||
return;
|
||||
|
||||
auto *animGroupPriv = static_cast<QQuickAnimationGroupPrivate *>(QQuickAnimationGroupPrivate::get(group));
|
||||
if (animGroupPriv->running && !animGroupPriv->animationDirty) {
|
||||
animGroupPriv->animationDirty = true;
|
||||
|
||||
if (group->currentTime() == 0) {
|
||||
// restart if the animation didn't proceed yet.
|
||||
animGroupPriv->restartFromCurrentLoop();
|
||||
}
|
||||
}
|
||||
|
||||
// check the animationGroup is one of another animationGroup members
|
||||
if (animGroupPriv->group)
|
||||
animGroupPriv->animationGroupDirty();
|
||||
}
|
||||
|
||||
/*!
|
||||
\qmlsignal QtQuick::Animation::started()
|
||||
|
||||
|
@ -696,6 +717,8 @@ void QQuickPauseAnimation::setDuration(int duration)
|
|||
return;
|
||||
d->duration = duration;
|
||||
emit durationChanged(duration);
|
||||
if (d->group)
|
||||
d->animationGroupDirty();
|
||||
}
|
||||
|
||||
QAbstractAnimationJob* QQuickPauseAnimation::transition(QQuickStateActions &actions,
|
||||
|
@ -1076,6 +1099,8 @@ void QQuickPropertyAction::setTargetObject(QObject *o)
|
|||
return;
|
||||
d->target = o;
|
||||
emit targetChanged();
|
||||
if (d->group)
|
||||
d->animationGroupDirty();
|
||||
}
|
||||
|
||||
QString QQuickPropertyAction::property() const
|
||||
|
@ -1091,6 +1116,8 @@ void QQuickPropertyAction::setProperty(const QString &n)
|
|||
return;
|
||||
d->propertyName = n;
|
||||
emit propertyChanged();
|
||||
if (d->group)
|
||||
d->animationGroupDirty();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -1121,6 +1148,8 @@ void QQuickPropertyAction::setProperties(const QString &p)
|
|||
return;
|
||||
d->properties = p;
|
||||
emit propertiesChanged(p);
|
||||
if (d->group)
|
||||
d->animationGroupDirty();
|
||||
}
|
||||
|
||||
QQmlListProperty<QObject> QQuickPropertyAction::targets()
|
||||
|
@ -1722,6 +1751,36 @@ void QQuickAnimationGroupPrivate::removeLast_animation(QQmlListProperty<QQuickAb
|
|||
q->d_func()->animations.last()->setGroup(nullptr);
|
||||
}
|
||||
|
||||
void QQuickAnimationGroupPrivate::restartFromCurrentLoop()
|
||||
{
|
||||
Q_Q(QQuickAnimationGroup);
|
||||
if (!animationDirty)
|
||||
return;
|
||||
|
||||
animationDirty = false;
|
||||
|
||||
Q_ASSERT(animationInstance);
|
||||
const int currentLoop = animationInstance->currentLoop();
|
||||
|
||||
QSignalBlocker signalBlocker(q);
|
||||
q->stop();
|
||||
q->start();
|
||||
|
||||
Q_ASSERT(animationInstance);
|
||||
// Restarting adjusts animationInstance's loopCount
|
||||
// Since we just want to start it from this loop,
|
||||
// it will be restored again.
|
||||
if (loopCount != -1)
|
||||
animationInstance->setLoopCount(loopCount - currentLoop);
|
||||
}
|
||||
|
||||
void QQuickAnimationGroupPrivate::animationCurrentLoopChanged(QAbstractAnimationJob *)
|
||||
{
|
||||
if (!animationDirty)
|
||||
return;
|
||||
restartFromCurrentLoop();
|
||||
}
|
||||
|
||||
QQuickAnimationGroup::~QQuickAnimationGroup()
|
||||
{
|
||||
Q_D(QQuickAnimationGroup);
|
||||
|
@ -2135,6 +2194,8 @@ void QQuickPropertyAnimation::setDuration(int duration)
|
|||
if (d->componentComplete && d->running)
|
||||
d->ourPropertiesDirty = true;
|
||||
emit durationChanged(duration);
|
||||
if (d->group)
|
||||
d->animationGroupDirty();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -2164,6 +2225,8 @@ void QQuickPropertyAnimation::setFrom(const QVariant &f)
|
|||
if (d->componentComplete && d->running)
|
||||
d->ourPropertiesDirty = true;
|
||||
emit fromChanged();
|
||||
if (d->group)
|
||||
d->animationGroupDirty();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -2193,6 +2256,8 @@ void QQuickPropertyAnimation::setTo(const QVariant &t)
|
|||
if (d->componentComplete && d->running)
|
||||
d->ourPropertiesDirty = true;
|
||||
emit toChanged();
|
||||
if (d->group)
|
||||
d->animationGroupDirty();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
@ -2425,6 +2490,8 @@ void QQuickPropertyAnimation::setEasing(const QEasingCurve &e)
|
|||
if (d->componentComplete && d->running)
|
||||
d->ourPropertiesDirty = true;
|
||||
emit easingChanged(e);
|
||||
if (d->group)
|
||||
d->animationGroupDirty();
|
||||
}
|
||||
|
||||
QObject *QQuickPropertyAnimation::target() const
|
||||
|
@ -2440,6 +2507,8 @@ void QQuickPropertyAnimation::setTargetObject(QObject *o)
|
|||
return;
|
||||
d->target = o;
|
||||
emit targetChanged();
|
||||
if (d->group)
|
||||
d->animationGroupDirty();
|
||||
}
|
||||
|
||||
QString QQuickPropertyAnimation::property() const
|
||||
|
@ -2455,6 +2524,8 @@ void QQuickPropertyAnimation::setProperty(const QString &n)
|
|||
return;
|
||||
d->propertyName = n;
|
||||
emit propertyChanged();
|
||||
if (d->group)
|
||||
d->animationGroupDirty();
|
||||
}
|
||||
|
||||
QString QQuickPropertyAnimation::properties() const
|
||||
|
@ -2471,6 +2542,8 @@ void QQuickPropertyAnimation::setProperties(const QString &prop)
|
|||
|
||||
d->properties = prop;
|
||||
emit propertiesChanged(prop);
|
||||
if (d->group)
|
||||
d->animationGroupDirty();
|
||||
}
|
||||
|
||||
/*!
|
||||
|
|
|
@ -164,6 +164,7 @@ public:
|
|||
QAbstractAnimationJob* animationInstance;
|
||||
|
||||
static QQmlProperty createProperty(QObject *obj, const QString &str, QObject *infoObj, QString *errorMessage = nullptr);
|
||||
void animationGroupDirty();
|
||||
};
|
||||
|
||||
class QQuickPauseAnimationPrivate : public QQuickAbstractAnimationPrivate
|
||||
|
@ -217,7 +218,7 @@ class QQuickAnimationGroupPrivate : public QQuickAbstractAnimationPrivate
|
|||
Q_DECLARE_PUBLIC(QQuickAnimationGroup)
|
||||
public:
|
||||
QQuickAnimationGroupPrivate()
|
||||
: QQuickAbstractAnimationPrivate() {}
|
||||
: QQuickAbstractAnimationPrivate(), animationDirty(false) {}
|
||||
|
||||
static void append_animation(QQmlListProperty<QQuickAbstractAnimation> *list, QQuickAbstractAnimation *role);
|
||||
static QQuickAbstractAnimation *at_animation(QQmlListProperty<QQuickAbstractAnimation> *list, qsizetype index);
|
||||
|
@ -227,6 +228,10 @@ public:
|
|||
QQuickAbstractAnimation *role);
|
||||
static void removeLast_animation(QQmlListProperty<QQuickAbstractAnimation> *list);
|
||||
QList<QQuickAbstractAnimation *> animations;
|
||||
|
||||
void restartFromCurrentLoop();
|
||||
void animationCurrentLoopChanged(QAbstractAnimationJob *job) override;
|
||||
bool animationDirty: 1;
|
||||
};
|
||||
|
||||
class Q_QUICK_PRIVATE_EXPORT QQuickPropertyAnimationPrivate : public QQuickAbstractAnimationPrivate
|
||||
|
|
|
@ -0,0 +1,92 @@
|
|||
import QtQuick
|
||||
|
||||
Rectangle {
|
||||
width: 300
|
||||
height: 300
|
||||
|
||||
// test SequentialAnimation
|
||||
Rectangle {
|
||||
id: line0
|
||||
y: 100
|
||||
width: parent.width
|
||||
height: 2
|
||||
color: "blue"
|
||||
}
|
||||
Rectangle {
|
||||
id: target0
|
||||
objectName: "target0"
|
||||
y: 100
|
||||
anchors.verticalCenter: line0.verticalCenter
|
||||
height: line0.height * 5
|
||||
width: height
|
||||
color: "red"
|
||||
radius: height/2
|
||||
|
||||
property bool onFinishedCalled : false;
|
||||
|
||||
SequentialAnimation {
|
||||
id: seqAnim0
|
||||
objectName: "seqAnim0"
|
||||
loops: 2
|
||||
running: true
|
||||
NumberAnimation {
|
||||
id: anim0
|
||||
target: target0
|
||||
property: "x"
|
||||
from: 0
|
||||
to: 50
|
||||
duration: 500
|
||||
}
|
||||
Component.onCompleted: anim0.to = 290
|
||||
onFinished: target0.onFinishedCalled = true
|
||||
}
|
||||
}
|
||||
|
||||
// test ParallelAnimation
|
||||
Rectangle {
|
||||
id: line1
|
||||
y: 200
|
||||
width: parent.width
|
||||
height: 2
|
||||
color: "blue"
|
||||
}
|
||||
Rectangle {
|
||||
id: target1
|
||||
objectName: "target1"
|
||||
anchors.verticalCenter: line1.verticalCenter
|
||||
height: line1.height * 5
|
||||
width: height
|
||||
color: "yellow"
|
||||
radius: height/2
|
||||
|
||||
property bool onFinishedCalled : false;
|
||||
|
||||
ParallelAnimation {
|
||||
id: parAnim0
|
||||
objectName: "parAnim0"
|
||||
loops: 2
|
||||
running: true
|
||||
NumberAnimation {
|
||||
id: anim1
|
||||
target: target1
|
||||
property: "x"
|
||||
from: 0
|
||||
to: 50
|
||||
duration: 500
|
||||
}
|
||||
Component.onCompleted: anim1.to = 290
|
||||
onFinished: target1.onFinishedCalled = true
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 400
|
||||
running: true
|
||||
onTriggered: {
|
||||
seqAnim0.pause()
|
||||
parAnim0.pause()
|
||||
anim0.to = 140
|
||||
anim1.to = 140
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
import QtQuick
|
||||
|
||||
Rectangle {
|
||||
width: 300
|
||||
height: 300
|
||||
|
||||
// test ParallelAnimation in SequentialAnimation
|
||||
Rectangle {
|
||||
id: line0
|
||||
y: 100
|
||||
width: parent.width
|
||||
height: 2
|
||||
color: "blue"
|
||||
}
|
||||
Rectangle {
|
||||
id: target0
|
||||
objectName: "target0"
|
||||
y: 100
|
||||
anchors.verticalCenter: line0.verticalCenter
|
||||
height: line0.height * 5
|
||||
width: height
|
||||
color: "red"
|
||||
radius: height/2
|
||||
|
||||
property bool onFinishedCalled : false;
|
||||
|
||||
SequentialAnimation {
|
||||
id: seqAnim0
|
||||
objectName: "seqAnim0"
|
||||
loops: 2
|
||||
running: true
|
||||
ParallelAnimation {
|
||||
NumberAnimation {
|
||||
id: anim0
|
||||
target: target0
|
||||
property: "x"
|
||||
from: 0
|
||||
to: 50
|
||||
duration: 500
|
||||
}
|
||||
}
|
||||
Component.onCompleted: anim0.to = 290
|
||||
onFinished: target0.onFinishedCalled = true
|
||||
}
|
||||
}
|
||||
|
||||
// test SequentialAnimation in ParallelAnimation
|
||||
Rectangle {
|
||||
id: line1
|
||||
y: 200
|
||||
width: parent.width
|
||||
height: 2
|
||||
color: "blue"
|
||||
}
|
||||
Rectangle {
|
||||
id: target1
|
||||
objectName: "target1"
|
||||
anchors.verticalCenter: line1.verticalCenter
|
||||
height: line1.height * 5
|
||||
width: height
|
||||
color: "yellow"
|
||||
radius: height/2
|
||||
|
||||
property bool onFinishedCalled : false;
|
||||
|
||||
ParallelAnimation {
|
||||
id: parAnim0
|
||||
objectName: "parAnim0"
|
||||
loops: 2
|
||||
running: true
|
||||
SequentialAnimation {
|
||||
NumberAnimation {
|
||||
id: anim1
|
||||
target: target1
|
||||
property: "x"
|
||||
from: 0
|
||||
to: 50
|
||||
duration: 500
|
||||
}
|
||||
}
|
||||
Component.onCompleted: anim1.to = 290
|
||||
onFinished: target1.onFinishedCalled = true
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 400
|
||||
running: true
|
||||
onTriggered: {
|
||||
seqAnim0.pause()
|
||||
parAnim0.pause()
|
||||
anim0.to = 140
|
||||
anim1.to = 140
|
||||
}
|
||||
}
|
||||
}
|
|
@ -98,6 +98,8 @@ private slots:
|
|||
void infiniteLoopsWithoutFrom();
|
||||
void frameAnimation1();
|
||||
void frameAnimation2();
|
||||
void restartAnimationGroupWhenDirty();
|
||||
void restartNestedAnimationGroupWhenDirty();
|
||||
};
|
||||
|
||||
#define QTIMED_COMPARE(lhs, rhs) do { \
|
||||
|
@ -2212,6 +2214,65 @@ void tst_qquickanimations::frameAnimation2()
|
|||
QVERIFY(frameAnimation->currentFrame() > 3);
|
||||
}
|
||||
|
||||
//QTBUG-110589
|
||||
void tst_qquickanimations::restartAnimationGroupWhenDirty()
|
||||
{
|
||||
QQmlEngine engine;
|
||||
QQmlComponent c(&engine, testFileUrl("restartAnimationGroupWhenDirty.qml"));
|
||||
QScopedPointer<QObject> obj(c.create());
|
||||
auto *root = qobject_cast<QQuickRectangle*>(obj.data());
|
||||
QVERIFY2(root, qPrintable(c.errorString()));
|
||||
|
||||
QQuickSequentialAnimation *seqAnim0 = root->findChild<QQuickSequentialAnimation*>("seqAnim0");
|
||||
QVERIFY(seqAnim0);
|
||||
QQuickRectangle *target0 = root->findChild<QQuickRectangle*>("target0");
|
||||
QVERIFY(target0);
|
||||
QQuickParallelAnimation *parAnim0 = root->findChild<QQuickParallelAnimation*>("parAnim0");
|
||||
QVERIFY(parAnim0);
|
||||
QQuickRectangle *target1 = root->findChild<QQuickRectangle*>("target1");
|
||||
QVERIFY(target1);
|
||||
|
||||
QTRY_VERIFY(seqAnim0->isPaused());
|
||||
QTRY_VERIFY(parAnim0->isPaused());
|
||||
QTRY_VERIFY(target0->x() > 140);
|
||||
QTRY_VERIFY(target1->x() > 140);
|
||||
seqAnim0->resume();
|
||||
parAnim0->resume();
|
||||
QTRY_VERIFY(target0->property("onFinishedCalled").value<bool>());
|
||||
QTRY_VERIFY(target1->property("onFinishedCalled").value<bool>());
|
||||
QTRY_COMPARE(target0->x(), 140);
|
||||
QTRY_COMPARE(target1->x(), 140);
|
||||
}
|
||||
|
||||
//QTBUG-95840
|
||||
void tst_qquickanimations::restartNestedAnimationGroupWhenDirty()
|
||||
{
|
||||
QQmlEngine engine;
|
||||
QQmlComponent c(&engine, testFileUrl("restartNestedAnimationGroupWhenDirty.qml"));
|
||||
QScopedPointer<QObject> obj(c.create());
|
||||
auto *root = qobject_cast<QQuickRectangle*>(obj.data());
|
||||
QVERIFY2(root, qPrintable(c.errorString()));
|
||||
|
||||
QQuickSequentialAnimation *seqAnim0 = root->findChild<QQuickSequentialAnimation*>("seqAnim0");
|
||||
QVERIFY(seqAnim0);
|
||||
QQuickRectangle *target0 = root->findChild<QQuickRectangle*>("target0");
|
||||
QVERIFY(target0);
|
||||
QQuickParallelAnimation *parAnim0 = root->findChild<QQuickParallelAnimation*>("parAnim0");
|
||||
QVERIFY(parAnim0);
|
||||
QQuickRectangle *target1 = root->findChild<QQuickRectangle*>("target1");
|
||||
QVERIFY(target1);
|
||||
|
||||
QTRY_VERIFY(seqAnim0->isPaused());
|
||||
QTRY_VERIFY(parAnim0->isPaused());
|
||||
QTRY_VERIFY(target0->x() > 140);
|
||||
QTRY_VERIFY(target1->x() > 140);
|
||||
seqAnim0->resume();
|
||||
parAnim0->resume();
|
||||
QTRY_VERIFY(target0->property("onFinishedCalled").value<bool>());
|
||||
QTRY_VERIFY(target1->property("onFinishedCalled").value<bool>());
|
||||
QTRY_COMPARE(target0->x(), 140);
|
||||
QTRY_COMPARE(target1->x(), 140);
|
||||
}
|
||||
QTEST_MAIN(tst_qquickanimations)
|
||||
|
||||
#include "tst_qquickanimations.moc"
|
||||
|
|
Loading…
Reference in New Issue