aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <[email protected]>2025-06-24 14:39:07 +0200
committerUlf Hermann <[email protected]>2025-06-30 11:32:12 +0200
commitcee83328857674343ac9ff0a5016ee677347a537 (patch)
tree2481d54b4bc0188d4e6ffc689e4c4be83d0ead8d
parent57325020f65665d91e63dc300d674a8e8dc411f1 (diff)
Fix handling of animations in QQuickTransition
We need to implement all the QQmlListProperty methods, and we need to guard against nullptr to avoid crashes. Pick-to: 6.10 6.9 6.8 6.5 Task-number: QTBUG-137554 Change-Id: I9a222085b4848a9f0ebe59d9e029cabdedc46512 Reviewed-by: Fabian Kosmale <[email protected]> Reviewed-by: Sami Shalayel <[email protected]>
-rw-r--r--src/quick/util/qquicktransition.cpp67
-rw-r--r--tests/auto/quick/qquickanimations/data/animationInstantiator.qml40
-rw-r--r--tests/auto/quick/qquickanimations/tst_qquickanimations.cpp11
3 files changed, 99 insertions, 19 deletions
diff --git a/src/quick/util/qquicktransition.cpp b/src/quick/util/qquicktransition.cpp
index 63707f4a4b..85b4615206 100644
--- a/src/quick/util/qquicktransition.cpp
+++ b/src/quick/util/qquicktransition.cpp
@@ -107,14 +107,20 @@ protected:
static qsizetype animation_count(QQmlListProperty<QQuickAbstractAnimation> *list);
static QQuickAbstractAnimation* animation_at(QQmlListProperty<QQuickAbstractAnimation> *list, qsizetype pos);
static void clear_animations(QQmlListProperty<QQuickAbstractAnimation> *list);
- QList<QQuickAbstractAnimation *> animations;
+ static void removeLast_animation(QQmlListProperty<QQuickAbstractAnimation> *list);
+ static void replace_animation(
+ QQmlListProperty<QQuickAbstractAnimation> *list, qsizetype pos,
+ QQuickAbstractAnimation *a);
+
+ QList<QPointer<QQuickAbstractAnimation>> animations;
};
void QQuickTransitionPrivate::append_animation(QQmlListProperty<QQuickAbstractAnimation> *list, QQuickAbstractAnimation *a)
{
QQuickTransition *q = static_cast<QQuickTransition *>(list->object);
q->d_func()->animations.append(a);
- a->setDisableUserControl();
+ if (a)
+ a->setDisableUserControl();
}
qsizetype QQuickTransitionPrivate::animation_count(QQmlListProperty<QQuickAbstractAnimation> *list)
@@ -132,10 +138,25 @@ QQuickAbstractAnimation* QQuickTransitionPrivate::animation_at(QQmlListProperty<
void QQuickTransitionPrivate::clear_animations(QQmlListProperty<QQuickAbstractAnimation> *list)
{
QQuickTransition *q = static_cast<QQuickTransition *>(list->object);
- while (q->d_func()->animations.size()) {
- QQuickAbstractAnimation *firstAnim = q->d_func()->animations.at(0);
- q->d_func()->animations.removeAll(firstAnim);
- }
+ q->d_func()->animations.clear();
+}
+
+void QQuickTransitionPrivate::removeLast_animation(QQmlListProperty<QQuickAbstractAnimation> *list)
+{
+ QQuickTransition *q = static_cast<QQuickTransition *>(list->object);
+ q->d_func()->animations.removeLast();
+}
+
+void QQuickTransitionPrivate::replace_animation(
+ QQmlListProperty<QQuickAbstractAnimation> *list, qsizetype pos, QQuickAbstractAnimation *a)
+{
+ QQuickTransition *q = static_cast<QQuickTransition *>(list->object);
+ QQuickTransitionPrivate *d = q->d_func();
+ if (d->animations.length() <= pos)
+ d->animations.resize(pos + 1, nullptr);
+ d->animations[pos] = a;
+ if (a)
+ a->setDisableUserControl();
}
void QQuickTransitionInstance::animationStateChanged(QAbstractAnimationJob *, QAbstractAnimationJob::State newState, QAbstractAnimationJob::State)
@@ -240,15 +261,19 @@ QQuickTransitionInstance *QQuickTransition::prepare(QQuickStateOperation::Action
int start = d->reversed ? d->animations.size() - 1 : 0;
int end = d->reversed ? -1 : d->animations.size();
- QAbstractAnimationJob *anim = nullptr;
- for (int i = start; i != end;) {
- anim = d->animations.at(i)->transition(actions, after, direction, defaultTarget);
- if (anim) {
- if (d->animations.at(i)->threadingModel() == QQuickAbstractAnimation::RenderThread)
- anim = new QQuickAnimatorProxyJob(anim, d->animations.at(i));
- d->reversed ? group->prependAnimation(anim) : group->appendAnimation(anim);
- }
- d->reversed ? --i : ++i;
+ for (int i = start; i != end; d->reversed ? --i : ++i) {
+ QQuickAbstractAnimation *anim = d->animations.at(i);
+ if (!anim)
+ continue;
+
+ QAbstractAnimationJob *job = anim->transition(actions, after, direction, defaultTarget);
+ if (!job)
+ continue;
+
+ if (anim->threadingModel() == QQuickAbstractAnimation::RenderThread)
+ job = new QQuickAnimatorProxyJob(job, anim);
+
+ d->reversed ? group->prependAnimation(job) : group->appendAnimation(job);
}
group->setDirection(d->reversed ? QAbstractAnimationJob::Backward : QAbstractAnimationJob::Forward);
@@ -434,10 +459,14 @@ bool QQuickTransition::running() const
QQmlListProperty<QQuickAbstractAnimation> QQuickTransition::animations()
{
Q_D(QQuickTransition);
- return QQmlListProperty<QQuickAbstractAnimation>(this, &d->animations, QQuickTransitionPrivate::append_animation,
- QQuickTransitionPrivate::animation_count,
- QQuickTransitionPrivate::animation_at,
- QQuickTransitionPrivate::clear_animations);
+ return QQmlListProperty<QQuickAbstractAnimation>(
+ this, &d->animations,
+ QQuickTransitionPrivate::append_animation,
+ QQuickTransitionPrivate::animation_count,
+ QQuickTransitionPrivate::animation_at,
+ QQuickTransitionPrivate::clear_animations,
+ QQuickTransitionPrivate::replace_animation,
+ QQuickTransitionPrivate::removeLast_animation);
}
QT_END_NAMESPACE
diff --git a/tests/auto/quick/qquickanimations/data/animationInstantiator.qml b/tests/auto/quick/qquickanimations/data/animationInstantiator.qml
new file mode 100644
index 0000000000..48206073d0
--- /dev/null
+++ b/tests/auto/quick/qquickanimations/data/animationInstantiator.qml
@@ -0,0 +1,40 @@
+import QtQuick
+
+Item {
+ id: root
+ property int v: 0
+
+ Transition {
+ id: transition
+ NumberAnimation {}
+ }
+
+ Instantiator {
+ id: instantiator
+
+ delegate: NumberAnimation {}
+
+ onObjectAdded: (index, object) => {
+ transition.animations.splice(index, 0, object)
+ }
+
+ onObjectRemoved: (index, object) => {
+ // NB: This is a logic error. The indices of other entries change when we remove one
+ // from the middle, but Instantiator will still report the old indices.
+ // We still want to do this, in order ot exercise the code that nulls dangling
+ // pointers.
+ transition.animations.splice(index, 1)
+ }
+ }
+
+ Timer {
+ interval: 2
+ running: true
+ repeat: true
+ onTriggered: {
+ instantiator.model = (++v % 2) ? 0 : 10
+ }
+ }
+}
+
+
diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
index 0a4c87e138..c6c844993a 100644
--- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
+++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp
@@ -107,6 +107,7 @@ private slots:
void restartNestedAnimationGroupWhenDirty();
void targetsDeletedNotRemoved();
void alwaysRunToEndSetFalseRestartBug();
+ void animationInstantiator();
};
#define QTIMED_COMPARE(lhs, rhs) do { \
@@ -2335,6 +2336,16 @@ void tst_qquickanimations::alwaysRunToEndSetFalseRestartBug()
QCOMPARE(sequential.isRunning(), false);
}
+void tst_qquickanimations::animationInstantiator()
+{
+ QQmlEngine engine;
+ QQmlComponent c(&engine, testFileUrl("animationInstantiator.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+ QScopedPointer<QObject> o(c.create());
+ QVERIFY(!o.isNull());
+ QTRY_VERIFY(o->property("v").toInt() > 10);
+}
+
QTEST_MAIN(tst_qquickanimations)
#include "tst_qquickanimations.moc"