diff options
author | Ulf Hermann <[email protected]> | 2025-06-24 14:39:07 +0200 |
---|---|---|
committer | Ulf Hermann <[email protected]> | 2025-06-30 11:32:12 +0200 |
commit | cee83328857674343ac9ff0a5016ee677347a537 (patch) | |
tree | 2481d54b4bc0188d4e6ffc689e4c4be83d0ead8d | |
parent | 57325020f65665d91e63dc300d674a8e8dc411f1 (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.cpp | 67 | ||||
-rw-r--r-- | tests/auto/quick/qquickanimations/data/animationInstantiator.qml | 40 | ||||
-rw-r--r-- | tests/auto/quick/qquickanimations/tst_qquickanimations.cpp | 11 |
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" |