From 4938984f9a779192264757a06e6ca555fc8f5e91 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 3 Feb 2021 14:24:14 +0100 Subject: Use a QDoubleEndedList for the children of animation group jobs This way it's fundamentally impossible to add the same animation job to two different group jobs. The pointers are not exposed anymore and no one can re-order the jobs. Task-number: QTBUG-90401 Change-Id: Iebff4b64960c853915dd32714acd144fc5cdc00d Reviewed-by: Fabian Kosmale --- src/qml/CMakeLists.txt | 1 + src/qml/animations/qabstractanimationjob.cpp | 2 - src/qml/animations/qabstractanimationjob_p.h | 7 +- src/qml/animations/qanimationgroupjob.cpp | 58 ++-- src/qml/animations/qanimationgroupjob_p.h | 19 +- .../animations/qcontinuinganimationgroupjob.cpp | 18 +- src/qml/animations/qparallelanimationgroupjob.cpp | 24 +- .../animations/qsequentialanimationgroupjob.cpp | 73 +++--- .../animations/qsequentialanimationgroupjob_p.h | 6 +- src/qml/qml/ftw/qdoubleendedlist_p.h | 292 +++++++++++++++++++++ src/quick/util/qquickanimatorcontroller.cpp | 9 +- src/quick/util/qquickanimatorjob.cpp | 2 +- 12 files changed, 389 insertions(+), 122 deletions(-) create mode 100644 src/qml/qml/ftw/qdoubleendedlist_p.h (limited to 'src') diff --git a/src/qml/CMakeLists.txt b/src/qml/CMakeLists.txt index f59f1d8f23..39c50e5050 100644 --- a/src/qml/CMakeLists.txt +++ b/src/qml/CMakeLists.txt @@ -205,6 +205,7 @@ qt_internal_add_module(Qml parser/qqmljskeywords_p.h parser/qqmljslexer.cpp parser/qqmljslexer_p.h qml/ftw/qbitfield_p.h + qml/ftw/qdoubleendedlist_p.h qml/ftw/qfieldlist_p.h qml/ftw/qfinitestack_p.h qml/ftw/qflagpointer_p.h diff --git a/src/qml/animations/qabstractanimationjob.cpp b/src/qml/animations/qabstractanimationjob.cpp index a04b8b6e9b..9e82b63771 100644 --- a/src/qml/animations/qabstractanimationjob.cpp +++ b/src/qml/animations/qabstractanimationjob.cpp @@ -259,8 +259,6 @@ QAbstractAnimationJob::QAbstractAnimationJob() , m_currentLoop(0) , m_uncontrolledFinishTime(-1) , m_currentLoopStartTime(0) - , m_nextSibling(nullptr) - , m_previousSibling(nullptr) , m_hasRegisteredTimer(false) , m_isPause(false) , m_isGroup(false) diff --git a/src/qml/animations/qabstractanimationjob_p.h b/src/qml/animations/qabstractanimationjob_p.h index 9490070246..c7514f4583 100644 --- a/src/qml/animations/qabstractanimationjob_p.h +++ b/src/qml/animations/qabstractanimationjob_p.h @@ -53,6 +53,7 @@ #include #include +#include #include #include #include @@ -65,7 +66,7 @@ class QAnimationGroupJob; class QAnimationJobChangeListener; class QQmlAnimationTimer; -class Q_QML_PRIVATE_EXPORT QAbstractAnimationJob +class Q_QML_PRIVATE_EXPORT QAbstractAnimationJob : public QInheritedListNode { Q_DISABLE_COPY(QAbstractAnimationJob) public: @@ -124,8 +125,6 @@ public: void addAnimationChangeListener(QAnimationJobChangeListener *listener, QAbstractAnimationJob::ChangeTypes); void removeAnimationChangeListener(QAnimationJobChangeListener *listener, QAbstractAnimationJob::ChangeTypes); - QAbstractAnimationJob *nextSibling() const { return m_nextSibling; } - QAbstractAnimationJob *previousSibling() const { return m_previousSibling; } bool isGroup() const { return m_isGroup; } bool isRenderThreadJob() const { return m_isRenderThreadJob; } @@ -173,8 +172,6 @@ protected: }; std::vector changeListeners; - QAbstractAnimationJob *m_nextSibling; - QAbstractAnimationJob *m_previousSibling; QQmlAnimationTimer *m_timer = nullptr; bool m_hasRegisteredTimer:1; diff --git a/src/qml/animations/qanimationgroupjob.cpp b/src/qml/animations/qanimationgroupjob.cpp index 60b3003d0a..3b6c9123bf 100644 --- a/src/qml/animations/qanimationgroupjob.cpp +++ b/src/qml/animations/qanimationgroupjob.cpp @@ -42,7 +42,6 @@ QT_BEGIN_NAMESPACE QAnimationGroupJob::QAnimationGroupJob() - : QAbstractAnimationJob(), m_firstChild(nullptr), m_lastChild(nullptr) { m_isGroup = true; } @@ -51,29 +50,14 @@ void QAnimationGroupJob::ungroupChild(QAbstractAnimationJob *animation) { Q_ASSERT(animation); Q_ASSERT(animation->m_group == this); - QAbstractAnimationJob *prev = animation->previousSibling(); - QAbstractAnimationJob *next = animation->nextSibling(); - - if (prev) - prev->m_nextSibling = next; - else - m_firstChild = next; - - if (next) - next->m_previousSibling = prev; - else - m_lastChild = prev; - - animation->m_previousSibling = nullptr; - animation->m_nextSibling = nullptr; - + m_children.remove(animation); animation->m_group = nullptr; } void QAnimationGroupJob::handleAnimationRemoved(QAbstractAnimationJob *animation) { resetUncontrolledAnimationFinishTime(animation); - if (!firstChild()) { + if (m_children.isEmpty()) { m_currentTime = 0; stop(); } @@ -81,7 +65,7 @@ void QAnimationGroupJob::handleAnimationRemoved(QAbstractAnimationJob *animation QAnimationGroupJob::~QAnimationGroupJob() { - while (QAbstractAnimationJob *animation = firstChild()) { + while (QAbstractAnimationJob *animation = m_children.first()) { ungroupChild(animation); handleAnimationRemoved(animation); delete animation; @@ -90,7 +74,7 @@ QAnimationGroupJob::~QAnimationGroupJob() void QAnimationGroupJob::topLevelAnimationLoopChanged() { - for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) + for (QAbstractAnimationJob *animation : m_children) animation->fireTopLevelAnimationLoopChanged(); } @@ -99,15 +83,9 @@ void QAnimationGroupJob::appendAnimation(QAbstractAnimationJob *animation) if (QAnimationGroupJob *oldGroup = animation->m_group) oldGroup->removeAnimation(animation); - Q_ASSERT(!animation->previousSibling() && !animation->nextSibling()); - - if (m_lastChild) - m_lastChild->m_nextSibling = animation; - else - m_firstChild = animation; - animation->m_previousSibling = m_lastChild; - m_lastChild = animation; + Q_ASSERT(!animation->isInList()); + m_children.append(animation); animation->m_group = this; animationInserted(animation); } @@ -117,40 +95,34 @@ void QAnimationGroupJob::prependAnimation(QAbstractAnimationJob *animation) if (QAnimationGroupJob *oldGroup = animation->m_group) oldGroup->removeAnimation(animation); - Q_ASSERT(!previousSibling() && !nextSibling()); - - if (m_firstChild) - m_firstChild->m_previousSibling = animation; - else - m_lastChild = animation; - animation->m_nextSibling = m_firstChild; - m_firstChild = animation; + Q_ASSERT(!animation->isInList()); + m_children.prepend(animation); animation->m_group = this; animationInserted(animation); } void QAnimationGroupJob::removeAnimation(QAbstractAnimationJob *animation) { - QAbstractAnimationJob *prev = animation->previousSibling(); - QAbstractAnimationJob *next = animation->nextSibling(); + QAbstractAnimationJob *prev = m_children.prev(animation); + QAbstractAnimationJob *next = m_children.next(animation); ungroupChild(animation); animationRemoved(animation, prev, next); } void QAnimationGroupJob::clear() { - while (QAbstractAnimationJob *child = firstChild()) { + while (QAbstractAnimationJob *child = m_children.first()) { removeAnimation(child); delete child; } - m_firstChild = nullptr; - m_lastChild = nullptr; + + Q_ASSERT(m_children.isEmpty()); } void QAnimationGroupJob::resetUncontrolledAnimationsFinishTime() { - for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) { + for (QAbstractAnimationJob *animation : m_children) { if (animation->duration() == -1 || animation->loopCount() < 0) { resetUncontrolledAnimationFinishTime(animation); } @@ -185,7 +157,7 @@ void QAnimationGroupJob::debugChildren(QDebug d) const ++indentLevel; QByteArray ind(indentLevel, ' '); - for (QAbstractAnimationJob *child = firstChild(); child; child = child->nextSibling()) + for (const QAbstractAnimationJob *child : m_children) d << "\n" << ind.constData() << child; } diff --git a/src/qml/animations/qanimationgroupjob_p.h b/src/qml/animations/qanimationgroupjob_p.h index 6a0941db65..3733fc6b8a 100644 --- a/src/qml/animations/qanimationgroupjob_p.h +++ b/src/qml/animations/qanimationgroupjob_p.h @@ -51,7 +51,8 @@ // We mean it. // -#include "private/qabstractanimationjob_p.h" +#include +#include #include QT_REQUIRE_CONFIG(qml_animation); @@ -62,6 +63,8 @@ class Q_QML_PRIVATE_EXPORT QAnimationGroupJob : public QAbstractAnimationJob { Q_DISABLE_COPY(QAnimationGroupJob) public: + using Children = QDoubleEndedList; + QAnimationGroupJob(); ~QAnimationGroupJob() override; @@ -69,8 +72,8 @@ public: void prependAnimation(QAbstractAnimationJob *animation); void removeAnimation(QAbstractAnimationJob *animation); - QAbstractAnimationJob *firstChild() const { return m_firstChild; } - QAbstractAnimationJob *lastChild() const { return m_lastChild; } + Children *children() { return &m_children; } + const Children *children() const { return &m_children; } virtual void clear(); @@ -85,18 +88,18 @@ protected: //TODO: confirm location of these (should any be moved into QAbstractAnimationJob?) void resetUncontrolledAnimationsFinishTime(); void resetUncontrolledAnimationFinishTime(QAbstractAnimationJob *anim); - int uncontrolledAnimationFinishTime(QAbstractAnimationJob *anim) const { return anim->m_uncontrolledFinishTime; } + int uncontrolledAnimationFinishTime(const QAbstractAnimationJob *anim) const + { + return anim->m_uncontrolledFinishTime; + } void setUncontrolledAnimationFinishTime(QAbstractAnimationJob *anim, int time); void debugChildren(QDebug d) const; -private: void ungroupChild(QAbstractAnimationJob *animation); void handleAnimationRemoved(QAbstractAnimationJob *animation); - //definition - QAbstractAnimationJob *m_firstChild = nullptr; - QAbstractAnimationJob *m_lastChild = nullptr; + Children m_children; }; QT_END_NAMESPACE diff --git a/src/qml/animations/qcontinuinganimationgroupjob.cpp b/src/qml/animations/qcontinuinganimationgroupjob.cpp index 88c0e9e60e..7d24d8ce55 100644 --- a/src/qml/animations/qcontinuinganimationgroupjob.cpp +++ b/src/qml/animations/qcontinuinganimationgroupjob.cpp @@ -52,9 +52,8 @@ QContinuingAnimationGroupJob::~QContinuingAnimationGroupJob() void QContinuingAnimationGroupJob::updateCurrentTime(int /*currentTime*/) { - Q_ASSERT(firstChild()); - - for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) { + Q_ASSERT(!m_children.isEmpty()); + for (QAbstractAnimationJob *animation : m_children) { if (animation->state() == state()) { RETURN_IF_DELETED(animation->setCurrentTime(m_currentTime)); } @@ -68,20 +67,20 @@ void QContinuingAnimationGroupJob::updateState(QAbstractAnimationJob::State newS switch (newState) { case Stopped: - for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) + for (QAbstractAnimationJob *animation : m_children) animation->stop(); break; case Paused: - for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) + for (QAbstractAnimationJob *animation : m_children) if (animation->isRunning()) animation->pause(); break; case Running: - if (!firstChild()) { + if (m_children.isEmpty()) { stop(); return; } - for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) { + for (QAbstractAnimationJob *animation : m_children) { resetUncontrolledAnimationFinishTime(animation); animation->setDirection(m_direction); animation->start(); @@ -93,9 +92,8 @@ void QContinuingAnimationGroupJob::updateState(QAbstractAnimationJob::State newS void QContinuingAnimationGroupJob::updateDirection(QAbstractAnimationJob::Direction direction) { if (!isStopped()) { - for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) { + for (QAbstractAnimationJob *animation : m_children) animation->setDirection(direction); - } } } @@ -104,7 +102,7 @@ void QContinuingAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimat Q_ASSERT(animation && (animation->duration() == -1)); int uncontrolledRunningCount = 0; - for (QAbstractAnimationJob *child = firstChild(); child; child = child->nextSibling()) { + for (QAbstractAnimationJob *child : m_children) { if (child == animation) setUncontrolledAnimationFinishTime(animation, animation->currentTime()); else if (uncontrolledAnimationFinishTime(child) == -1) diff --git a/src/qml/animations/qparallelanimationgroupjob.cpp b/src/qml/animations/qparallelanimationgroupjob.cpp index 420a934ba2..4da4a06af2 100644 --- a/src/qml/animations/qparallelanimationgroupjob.cpp +++ b/src/qml/animations/qparallelanimationgroupjob.cpp @@ -57,7 +57,7 @@ int QParallelAnimationGroupJob::duration() const { int ret = 0; - for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) { + for (const QAbstractAnimationJob *animation : m_children) { int currentDuration = animation->totalDuration(); if (currentDuration == -1) return -1; // Undetermined length @@ -69,7 +69,7 @@ int QParallelAnimationGroupJob::duration() const void QParallelAnimationGroupJob::updateCurrentTime(int /*currentTime*/) { - if (!firstChild()) + if (m_children.isEmpty()) return; if (m_currentLoop > m_previousLoop) { @@ -79,21 +79,21 @@ void QParallelAnimationGroupJob::updateCurrentTime(int /*currentTime*/) // For an uncontrolled parallel group, we need to simulate the end of running animations. // As uncontrolled animation finish time is already reset for this next loop, we pick the // longest of the known stop times. - for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) { + for (QAbstractAnimationJob *animation : m_children) { int currentDuration = animation->totalDuration(); if (currentDuration >= 0) dura = qMax(dura, currentDuration); } } if (dura > 0) { - for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) { + for (QAbstractAnimationJob *animation : m_children) { if (!animation->isStopped()) RETURN_IF_DELETED(animation->setCurrentTime(dura)); // will stop } } } else if (m_currentLoop < m_previousLoop) { // simulate completion of the loop seeking backwards - for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) { + for (QAbstractAnimationJob *animation : m_children) { //we need to make sure the animation is in the right state //and then rewind it applyGroupState(animation); @@ -103,7 +103,7 @@ void QParallelAnimationGroupJob::updateCurrentTime(int /*currentTime*/) } // finally move into the actual time of the current loop - for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) { + for (QAbstractAnimationJob *animation : m_children) { const int dura = animation->totalDuration(); //if the loopcount is bigger we should always start all animations if (m_currentLoop > m_previousLoop @@ -130,16 +130,16 @@ void QParallelAnimationGroupJob::updateState(QAbstractAnimationJob::State newSta switch (newState) { case Stopped: - for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) + for (QAbstractAnimationJob *animation : m_children) animation->stop(); break; case Paused: - for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) + for (QAbstractAnimationJob *animation : m_children) if (animation->isRunning()) animation->pause(); break; case Running: - for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) { + for (QAbstractAnimationJob *animation : m_children) { if (oldState == Stopped) { animation->stop(); m_previousLoop = m_direction == Forward ? 0 : m_loopCount - 1; @@ -188,7 +188,7 @@ void QParallelAnimationGroupJob::updateDirection(QAbstractAnimationJob::Directio { //we need to update the direction of the current animation if (!isStopped()) { - for (QAbstractAnimationJob *animation = firstChild(); animation; animation = animation->nextSibling()) { + for (QAbstractAnimationJob *animation : m_children) { animation->setDirection(direction); } } else { @@ -208,7 +208,7 @@ void QParallelAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimatio Q_ASSERT(animation && (animation->duration() == -1 || animation->loopCount() < 0)); int uncontrolledRunningCount = 0; - for (QAbstractAnimationJob *child = firstChild(); child; child = child->nextSibling()) { + for (QAbstractAnimationJob *child : m_children) { if (child == animation) { setUncontrolledAnimationFinishTime(animation, animation->currentTime()); } else if (child->duration() == -1 || child->loopCount() < 0) { @@ -222,7 +222,7 @@ void QParallelAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimatio int maxDuration = 0; bool running = false; - for (QAbstractAnimationJob *job = firstChild(); job; job = job->nextSibling()) { + for (QAbstractAnimationJob *job : m_children) { if (job->state() == Running) running = true; maxDuration = qMax(maxDuration, job->totalDuration()); diff --git a/src/qml/animations/qsequentialanimationgroupjob.cpp b/src/qml/animations/qsequentialanimationgroupjob.cpp index 1d19bbf79d..2da6c79e29 100644 --- a/src/qml/animations/qsequentialanimationgroupjob.cpp +++ b/src/qml/animations/qsequentialanimationgroupjob.cpp @@ -66,11 +66,12 @@ bool QSequentialAnimationGroupJob::atEnd() const const int animTotalCurrentTime = m_currentAnimation->currentTime(); return (m_currentLoop == m_loopCount - 1 && m_direction == Forward - && !m_currentAnimation->nextSibling() + && !m_children.next(m_currentAnimation) && animTotalCurrentTime == animationActualTotalDuration(m_currentAnimation)); } -int QSequentialAnimationGroupJob::animationActualTotalDuration(QAbstractAnimationJob *anim) const +int QSequentialAnimationGroupJob::animationActualTotalDuration( + const QAbstractAnimationJob *anim) const { int ret = anim->totalDuration(); if (ret == -1) { @@ -84,13 +85,12 @@ int QSequentialAnimationGroupJob::animationActualTotalDuration(QAbstractAnimatio QSequentialAnimationGroupJob::AnimationIndex QSequentialAnimationGroupJob::indexForCurrentTime() const { - Q_ASSERT(firstChild()); + Q_ASSERT(!m_children.isEmpty()); AnimationIndex ret; - QAbstractAnimationJob *anim = nullptr; int duration = 0; - for (anim = firstChild(); anim; anim = anim->nextSibling()) { + for (const QAbstractAnimationJob *anim : m_children) { duration = animationActualTotalDuration(anim); // 'animation' is the current animation if one of these reasons is true: @@ -116,7 +116,7 @@ QSequentialAnimationGroupJob::AnimationIndex QSequentialAnimationGroupJob::index // 1. the duration of the group is undefined and we passed its actual duration // 2. there are only 0-duration animations in the group ret.timeOffset -= duration; - ret.animation = lastChild(); + ret.animation = m_children.last(); return ret; } @@ -125,17 +125,17 @@ void QSequentialAnimationGroupJob::restart() // restarting the group by making the first/last animation the current one if (m_direction == Forward) { m_previousLoop = 0; - if (m_currentAnimation == firstChild()) + if (m_currentAnimation == m_children.first()) activateCurrentAnimation(); else - setCurrentAnimation(firstChild()); + setCurrentAnimation(m_children.first()); } else { // direction == Backward m_previousLoop = m_loopCount - 1; - if (m_currentAnimation == lastChild()) + if (m_currentAnimation == m_children.last()) activateCurrentAnimation(); else - setCurrentAnimation(lastChild()); + setCurrentAnimation(m_children.last()); } } @@ -143,21 +143,22 @@ void QSequentialAnimationGroupJob::advanceForwards(const AnimationIndex &newAnim { if (m_previousLoop < m_currentLoop) { // we need to fast forward to the end - for (QAbstractAnimationJob *anim = m_currentAnimation; anim; anim = anim->nextSibling()) { + for (QAbstractAnimationJob *anim = m_currentAnimation; anim; anim = m_children.next(anim)) { RETURN_IF_DELETED(setCurrentAnimation(anim, true)); RETURN_IF_DELETED(anim->setCurrentTime(animationActualTotalDuration(anim))); } // this will make sure the current animation is reset to the beginning - if (firstChild() && !firstChild()->nextSibling()) { //count == 1 + if (m_children.count() == 1) { // we need to force activation because setCurrentAnimation will have no effect RETURN_IF_DELETED(activateCurrentAnimation()); } else { - RETURN_IF_DELETED(setCurrentAnimation(firstChild(), true)); + RETURN_IF_DELETED(setCurrentAnimation(m_children.first(), true)); } } // and now we need to fast forward from the current position to - for (QAbstractAnimationJob *anim = m_currentAnimation; anim && anim != newAnimationIndex.animation; anim = anim->nextSibling()) { //### WRONG, + for (QAbstractAnimationJob *anim = m_currentAnimation; + anim && anim != newAnimationIndex.animation; anim = m_children.next(anim)) { //### WRONG, RETURN_IF_DELETED(setCurrentAnimation(anim, true)); RETURN_IF_DELETED(anim->setCurrentTime(animationActualTotalDuration(anim))); } @@ -168,21 +169,23 @@ void QSequentialAnimationGroupJob::rewindForwards(const AnimationIndex &newAnima { if (m_previousLoop > m_currentLoop) { // we need to fast rewind to the beginning - for (QAbstractAnimationJob *anim = m_currentAnimation; anim; anim = anim->previousSibling()) { + for (QAbstractAnimationJob *anim = m_currentAnimation; anim; + anim = m_children.prev(anim)) { RETURN_IF_DELETED(setCurrentAnimation(anim, true)); RETURN_IF_DELETED(anim->setCurrentTime(0)); } // this will make sure the current animation is reset to the end - if (lastChild() && !lastChild()->previousSibling()) { //count == 1 + if (m_children.count() == 1) { //count == 1 // we need to force activation because setCurrentAnimation will have no effect RETURN_IF_DELETED(activateCurrentAnimation()); } else { - RETURN_IF_DELETED(setCurrentAnimation(lastChild(), true)); + RETURN_IF_DELETED(setCurrentAnimation(m_children.last(), true)); } } // and now we need to fast rewind from the current position to - for (QAbstractAnimationJob *anim = m_currentAnimation; anim && anim != newAnimationIndex.animation; anim = anim->previousSibling()) { + for (QAbstractAnimationJob *anim = m_currentAnimation; + anim && anim != newAnimationIndex.animation; anim = m_children.prev(anim)) { RETURN_IF_DELETED(setCurrentAnimation(anim, true)); RETURN_IF_DELETED(anim->setCurrentTime(0)); } @@ -193,7 +196,7 @@ int QSequentialAnimationGroupJob::duration() const { int ret = 0; - for (QAbstractAnimationJob *anim = firstChild(); anim; anim = anim->nextSibling()) { + for (const QAbstractAnimationJob *anim : m_children) { const int currentDuration = anim->totalDuration(); if (currentDuration == -1) return -1; // Undetermined length @@ -246,7 +249,7 @@ void QSequentialAnimationGroupJob::updateCurrentTime(int currentTime) } else { //the only case where currentAnimation could be null //is when all animations have been removed - Q_ASSERT(!firstChild()); + Q_ASSERT(m_children.isEmpty()); m_currentTime = 0; RETURN_IF_DELETED(stop()); } @@ -288,10 +291,11 @@ void QSequentialAnimationGroupJob::updateDirection(QAbstractAnimationJob::Direct m_currentAnimation->setDirection(direction); } -void QSequentialAnimationGroupJob::setCurrentAnimation(QAbstractAnimationJob *anim, bool intermediate) +void QSequentialAnimationGroupJob::setCurrentAnimation( + const QAbstractAnimationJob *anim, bool intermediate) { if (!anim) { - Q_ASSERT(!firstChild()); + Q_ASSERT(m_children.isEmpty()); m_currentAnimation = nullptr; return; } @@ -303,7 +307,12 @@ void QSequentialAnimationGroupJob::setCurrentAnimation(QAbstractAnimationJob *an if (m_currentAnimation) m_currentAnimation->stop(); - m_currentAnimation = anim; + // Assert that the animation passed as argument is actually part of this group ... + Q_ASSERT(m_children.contains(anim)); + + // ... as then this const_cast is just a shortcut for looking up the non-const + // pointer in the linked list of jobs. + m_currentAnimation = const_cast(anim); activateCurrentAnimation(intermediate); } @@ -337,10 +346,10 @@ void QSequentialAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimat int totalTime = currentTime(); if (m_direction == Forward) { // set the current animation to be the next one - if (m_currentAnimation->nextSibling()) - RETURN_IF_DELETED(setCurrentAnimation(m_currentAnimation->nextSibling())); + if (auto *anim = m_children.next(m_currentAnimation)) + RETURN_IF_DELETED(setCurrentAnimation(anim)); - for (QAbstractAnimationJob *a = animation->nextSibling(); a; a = a->nextSibling()) { + for (QAbstractAnimationJob *a = m_children.next(animation); a; a = m_children.next(a)) { int dur = a->duration(); if (dur == -1) { totalTime = -1; @@ -352,10 +361,10 @@ void QSequentialAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimat } else { // set the current animation to be the previous one - if (m_currentAnimation->previousSibling()) - RETURN_IF_DELETED(setCurrentAnimation(m_currentAnimation->previousSibling())); + if (auto *anim = m_children.prev(m_currentAnimation)) + RETURN_IF_DELETED(setCurrentAnimation(anim)); - for (QAbstractAnimationJob *a = animation->previousSibling(); a; a = a->previousSibling()) { + for (QAbstractAnimationJob *a = m_children.prev(animation); a; a = m_children.prev(a)) { int dur = a->duration(); if (dur == -1) { totalTime = -1; @@ -374,9 +383,9 @@ void QSequentialAnimationGroupJob::uncontrolledAnimationFinished(QAbstractAnimat void QSequentialAnimationGroupJob::animationInserted(QAbstractAnimationJob *anim) { if (m_currentAnimation == nullptr) - RETURN_IF_DELETED(setCurrentAnimation(firstChild())); // initialize the current animation + RETURN_IF_DELETED(setCurrentAnimation(m_children.first())); // initialize the current animation - if (m_currentAnimation == anim->nextSibling() + if (m_currentAnimation == m_children.next(anim) && m_currentAnimation->currentTime() == 0 && m_currentAnimation->currentLoop() == 0) { //in this case we simply insert the animation before the current one has actually started RETURN_IF_DELETED(setCurrentAnimation(anim)); @@ -407,7 +416,7 @@ void QSequentialAnimationGroupJob::animationRemoved(QAbstractAnimationJob *anim, // duration of the previous animations up to the current animation m_currentTime = 0; - for (QAbstractAnimationJob *job = firstChild(); job; job = job->nextSibling()) { + for (QAbstractAnimationJob *job : m_children) { if (job == m_currentAnimation) break; m_currentTime += animationActualTotalDuration(job); diff --git a/src/qml/animations/qsequentialanimationgroupjob_p.h b/src/qml/animations/qsequentialanimationgroupjob_p.h index 34e8fe1e08..8c58862a50 100644 --- a/src/qml/animations/qsequentialanimationgroupjob_p.h +++ b/src/qml/animations/qsequentialanimationgroupjob_p.h @@ -85,13 +85,13 @@ private: // Note that the index semantic is slightly different depending on the direction. bool afterCurrent = false; //whether animation is before or after m_currentAnimation //TODO: make enum Before/After/Same int timeOffset = 0; // time offset when the animation at index starts. - QAbstractAnimationJob *animation = nullptr; //points to the animation at timeOffset + const QAbstractAnimationJob *animation = nullptr; //points to the animation at timeOffset }; - int animationActualTotalDuration(QAbstractAnimationJob *anim) const; + int animationActualTotalDuration(const QAbstractAnimationJob *anim) const; AnimationIndex indexForCurrentTime() const; - void setCurrentAnimation(QAbstractAnimationJob *anim, bool intermediate = false); + void setCurrentAnimation(const QAbstractAnimationJob *anim, bool intermediate = false); void activateCurrentAnimation(bool intermediate = false); void animationInserted(QAbstractAnimationJob *anim) override; diff --git a/src/qml/qml/ftw/qdoubleendedlist_p.h b/src/qml/qml/ftw/qdoubleendedlist_p.h new file mode 100644 index 0000000000..33fe97502d --- /dev/null +++ b/src/qml/qml/ftw/qdoubleendedlist_p.h @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QDOUBLEENDEDLIST_P_H +#define QDOUBLEENDEDLIST_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include + +QT_BEGIN_NAMESPACE + +class QInheritedListNode +{ +public: + ~QInheritedListNode() { remove(); } + bool isInList() const + { + Q_ASSERT((m_prev && m_next) || (!m_prev && !m_next)); + return m_prev != nullptr; + } + +private: + template + friend class QDoubleEndedList; + + void remove() + { + Q_ASSERT((m_prev && m_next) || (!m_prev && !m_next)); + if (!m_prev) + return; + + m_prev->m_next = m_next; + m_next->m_prev = m_prev; + m_prev = nullptr; + m_next = nullptr; + } + + QInheritedListNode *m_next = nullptr; + QInheritedListNode *m_prev = nullptr; +}; + +template +class QDoubleEndedList +{ +public: + QDoubleEndedList() + { + m_head.m_next = &m_head; + m_head.m_prev = &m_head; + assertHeadConsistent(); + } + + ~QDoubleEndedList() + { + assertHeadConsistent(); + while (!isEmpty()) + m_head.m_next->remove(); + assertHeadConsistent(); + } + + bool isEmpty() const + { + assertHeadConsistent(); + return m_head.m_next == &m_head; + } + + void prepend(N *n) + { + assertHeadConsistent(); + QInheritedListNode *nnode = n; + nnode->remove(); + + nnode->m_next = m_head.m_next ? m_head.m_next : &m_head; + nnode->m_next->m_prev = nnode; + + m_head.m_next = nnode; + nnode->m_prev = &m_head; + assertHeadConsistent(); + } + + void append(N *n) + { + assertHeadConsistent(); + QInheritedListNode *nnode = n; + nnode->remove(); + + nnode->m_prev = m_head.m_prev ? m_head.m_prev : &m_head; + nnode->m_prev->m_next = nnode; + + m_head.m_prev = nnode; + nnode->m_next = &m_head; + assertHeadConsistent(); + } + + void remove(N *n) { + Q_ASSERT(contains(n)); + QInheritedListNode *nnode = n; + nnode->remove(); + assertHeadConsistent(); + } + + bool contains(const N *n) const + { + assertHeadConsistent(); + for (const QInheritedListNode *nnode = m_head.m_next; + nnode != &m_head; nnode = nnode->m_next) { + if (nnode == n) + return true; + } + + return false; + } + + template + class base_iterator { + public: + T *operator*() const { return QDoubleEndedList::nodeToN(m_node); } + T *operator->() const { return QDoubleEndedList::nodeToN(m_node); } + + bool operator==(const base_iterator &other) const { return other.m_node == m_node; } + bool operator!=(const base_iterator &other) const { return other.m_node != m_node; } + + base_iterator &operator++() + { + m_node = m_node->m_next; + return *this; + } + + base_iterator operator++(int) + { + const base_iterator self(m_node); + m_node = m_node->m_next; + return self; + } + + private: + friend class QDoubleEndedList; + + base_iterator(Node *node) : m_node(node) + { + Q_ASSERT(m_node != nullptr); + } + + Node *m_node = nullptr; + }; + + using iterator = base_iterator; + using const_iterator = base_iterator; + + const N *first() const { return checkedNodeToN(m_head.m_next); } + N *first() { return checkedNodeToN(m_head.m_next); } + + const N *last() const { return checkedNodeToN(m_head.m_prev); } + N *last() { return checkedNodeToN(m_head.m_prev); } + + const N *next(const N *current) const + { + Q_ASSERT(contains(current)); + const QInheritedListNode *nnode = current; + return checkedNodeToN(nnode->m_next); + } + + N *next(N *current) + { + Q_ASSERT(contains(current)); + const QInheritedListNode *nnode = current; + return checkedNodeToN(nnode->m_next); + } + + const N *prev(const N *current) const + { + Q_ASSERT(contains(current)); + const QInheritedListNode *nnode = current; + return checkedNodeToN(nnode->m_prev); + } + + N *prev(N *current) + { + Q_ASSERT(contains(current)); + const QInheritedListNode *nnode = current; + return checkedNodeToN(nnode->m_prev); + } + + iterator begin() + { + assertHeadConsistent(); + return iterator(m_head.m_next); + } + + iterator end() + { + assertHeadConsistent(); + return iterator(&m_head); + } + + const_iterator begin() const + { + assertHeadConsistent(); + return const_iterator(m_head.m_next); + } + + const_iterator end() const + { + assertHeadConsistent(); + return const_iterator(&m_head); + } + + qsizetype count() const + { + assertHeadConsistent(); + qsizetype result = 0; + for (const auto *node = m_head.m_next; node != &m_head; node = node->m_next) + ++result; + return result; + } + +private: + static N *nodeToN(QInheritedListNode *node) + { + return static_cast(node); + } + + static const N *nodeToN(const QInheritedListNode *node) + { + return static_cast(node); + } + + N *checkedNodeToN(QInheritedListNode *node) const + { + assertHeadConsistent(); + return (!node || node == &m_head) ? nullptr : nodeToN(node); + } + + void assertHeadConsistent() const + { + Q_ASSERT(m_head.m_next != nullptr); + Q_ASSERT(m_head.m_prev != nullptr); + Q_ASSERT(m_head.m_next != &m_head || m_head.m_prev == &m_head); + } + + QInheritedListNode m_head; +}; + +QT_END_NAMESPACE + +#endif // QDOUBLEENDEDLIST_P_H diff --git a/src/quick/util/qquickanimatorcontroller.cpp b/src/quick/util/qquickanimatorcontroller.cpp index 9b60fe7ae0..e0fd8537a7 100644 --- a/src/quick/util/qquickanimatorcontroller.cpp +++ b/src/quick/util/qquickanimatorcontroller.cpp @@ -65,8 +65,7 @@ static void qquickanimator_invalidate_jobs(QAbstractAnimationJob *job) if (job->isRenderThreadJob()) { static_cast(job)->invalidate(); } else if (job->isGroup()) { - QAnimationGroupJob *g = static_cast(job); - for (QAbstractAnimationJob *a = g->firstChild(); a; a = a->nextSibling()) + for (QAbstractAnimationJob *a : *static_cast(job)->children()) qquickanimator_invalidate_jobs(a); } } @@ -115,8 +114,7 @@ static void qquickanimator_sync_before_start(QAbstractAnimationJob *job) if (job->isRenderThreadJob()) { static_cast(job)->preSync(); } else if (job->isGroup()) { - QAnimationGroupJob *g = static_cast(job); - for (QAbstractAnimationJob *a = g->firstChild(); a; a = a->nextSibling()) + for (QAbstractAnimationJob *a : *static_cast(job)->children()) qquickanimator_sync_before_start(a); } } @@ -195,8 +193,7 @@ void QQuickAnimatorController::start_helper(QAbstractAnimationJob *job) j->addAnimationChangeListener(this, QAbstractAnimationJob::StateChange); j->initialize(this); } else if (job->isGroup()) { - QAnimationGroupJob *g = static_cast(job); - for (QAbstractAnimationJob *a = g->firstChild(); a; a = a->nextSibling()) + for (QAbstractAnimationJob *a : *static_cast(job)->children()) start_helper(a); } } diff --git a/src/quick/util/qquickanimatorjob.cpp b/src/quick/util/qquickanimatorjob.cpp index 93d4e9a8b5..e18e31c023 100644 --- a/src/quick/util/qquickanimatorjob.cpp +++ b/src/quick/util/qquickanimatorjob.cpp @@ -243,7 +243,7 @@ static void qquick_syncback_helper(QAbstractAnimationJob *job) } else if (job->isGroup()) { QAnimationGroupJob *g = static_cast(job); - for (QAbstractAnimationJob *a = g->firstChild(); a; a = a->nextSibling()) + for (QAbstractAnimationJob *a : *g->children()) qquick_syncback_helper(a); } -- cgit v1.2.3