diff options
16 files changed, 408 insertions, 146 deletions
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 <private/qtqmlglobal_p.h> #include <private/qanimationjobutil_p.h> +#include <private/qdoubleendedlist_p.h> #include <QtCore/QObject> #include <QtCore/private/qabstractanimation_p.h> #include <vector> @@ -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<ChangeListener> 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 <QtQml/private/qabstractanimationjob_p.h> +#include <QtQml/private/qdoubleendedlist_p.h> #include <QtCore/qdebug.h> 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<QAbstractAnimationJob>; + 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<QAbstractAnimationJob *>(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 <QtCore/qglobal.h> + +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<class N> + 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 N> +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<typename T, typename Node> + class base_iterator { + public: + T *operator*() const { return QDoubleEndedList<N>::nodeToN(m_node); } + T *operator->() const { return QDoubleEndedList<N>::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<N>; + + base_iterator(Node *node) : m_node(node) + { + Q_ASSERT(m_node != nullptr); + } + + Node *m_node = nullptr; + }; + + using iterator = base_iterator<N, QInheritedListNode>; + using const_iterator = base_iterator<const N, const QInheritedListNode>; + + 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<N *>(node); + } + + static const N *nodeToN(const QInheritedListNode *node) + { + return static_cast<const N *>(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<QQuickAnimatorJob *>(job)->invalidate(); } else if (job->isGroup()) { - QAnimationGroupJob *g = static_cast<QAnimationGroupJob *>(job); - for (QAbstractAnimationJob *a = g->firstChild(); a; a = a->nextSibling()) + for (QAbstractAnimationJob *a : *static_cast<QAnimationGroupJob *>(job)->children()) qquickanimator_invalidate_jobs(a); } } @@ -115,8 +114,7 @@ static void qquickanimator_sync_before_start(QAbstractAnimationJob *job) if (job->isRenderThreadJob()) { static_cast<QQuickAnimatorJob *>(job)->preSync(); } else if (job->isGroup()) { - QAnimationGroupJob *g = static_cast<QAnimationGroupJob *>(job); - for (QAbstractAnimationJob *a = g->firstChild(); a; a = a->nextSibling()) + for (QAbstractAnimationJob *a : *static_cast<QAnimationGroupJob *>(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<QAnimationGroupJob *>(job); - for (QAbstractAnimationJob *a = g->firstChild(); a; a = a->nextSibling()) + for (QAbstractAnimationJob *a : *static_cast<QAnimationGroupJob *>(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<QAnimationGroupJob *>(job); - for (QAbstractAnimationJob *a = g->firstChild(); a; a = a->nextSibling()) + for (QAbstractAnimationJob *a : *g->children()) qquick_syncback_helper(a); } diff --git a/tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp b/tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp index 6bd8c2a2e0..ceb8cc707f 100644 --- a/tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp +++ b/tests/auto/qml/animation/qanimationgroupjob/tst_qanimationgroupjob.cpp @@ -266,14 +266,15 @@ void tst_QAnimationGroupJob::addChildTwice() subGroup = new QAbstractAnimationJob; parent->appendAnimation(subGroup); parent->appendAnimation(subGroup); - QVERIFY(parent->firstChild()); - QVERIFY(!parent->firstChild()->nextSibling()); - QVERIFY(!parent->firstChild()->previousSibling()); + QVERIFY(!parent->children()->isEmpty()); + QCOMPARE(parent->children()->count(), 1); + QVERIFY(!parent->children()->next(parent->children()->first())); + QVERIFY(!parent->children()->prev(parent->children()->last())); parent->clear(); QCOMPARE(parent->currentAnimation(), nullptr); - QVERIFY(!parent->firstChild()); + QVERIFY(parent->children()->isEmpty()); // adding the same item twice to a group will remove the item from its current position // and append it to the end @@ -282,13 +283,13 @@ void tst_QAnimationGroupJob::addChildTwice() subGroup2 = new QAbstractAnimationJob; parent->appendAnimation(subGroup2); - QCOMPARE(parent->firstChild(), subGroup); - QCOMPARE(parent->lastChild(), subGroup2); + QCOMPARE(parent->children()->first(), subGroup); + QCOMPARE(parent->children()->last(), subGroup2); parent->appendAnimation(subGroup); - QCOMPARE(parent->firstChild(), subGroup2); - QCOMPARE(parent->lastChild(), subGroup); + QCOMPARE(parent->children()->first(), subGroup2); + QCOMPARE(parent->children()->last(), subGroup); delete parent; } diff --git a/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp b/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp index 8c4c461813..a22c94e4c4 100644 --- a/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp +++ b/tests/auto/qml/animation/qparallelanimationgroupjob/tst_qparallelanimationgroupjob.cpp @@ -321,14 +321,11 @@ void tst_QParallelAnimationGroupJob::clearGroup() group.appendAnimation(new QParallelAnimationGroupJob); } - int count = 0; - for (QAbstractAnimationJob *anim = group.firstChild(); anim; anim = anim->nextSibling()) - ++count; - QCOMPARE(count, animationCount); + QCOMPARE(group.children()->count(), animationCount); group.clear(); - QVERIFY(!group.firstChild() && !group.lastChild()); + QVERIFY(group.children()->isEmpty()); QCOMPARE(group.currentLoopTime(), 0); } @@ -435,7 +432,7 @@ void tst_QParallelAnimationGroupJob::deleteChildrenWithRunningGroup() QTRY_VERIFY(group.currentLoopTime() > 0); delete anim1; - QVERIFY(!group.firstChild()); + QVERIFY(group.children()->isEmpty()); QCOMPARE(group.duration(), 0); QCOMPARE(group.state(), QAnimationGroupJob::Stopped); QCOMPARE(group.currentLoopTime(), 0); //that's the invariant diff --git a/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp b/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp index 2712ccde67..e8872ebc58 100644 --- a/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp +++ b/tests/auto/qml/animation/qsequentialanimationgroupjob/tst_qsequentialanimationgroupjob.cpp @@ -933,14 +933,11 @@ void tst_QSequentialAnimationGroupJob::clearGroup() subGroup->appendAnimation(new QPauseAnimationJob(10)); } - int count = 0; - for (QAbstractAnimationJob *anim = group.firstChild(); anim; anim = anim->nextSibling()) - ++count; - QCOMPARE(count, animationCount); + QCOMPARE(group.children()->count(), animationCount); group.clear(); - QVERIFY(!group.firstChild() && !group.lastChild()); + QVERIFY(group.children()->isEmpty()); QCOMPARE(group.currentLoopTime(), 0); } @@ -1131,7 +1128,7 @@ void tst_QSequentialAnimationGroupJob::deleteChildrenWithRunningGroup() QTRY_VERIFY(group.currentLoopTime() > 0); delete anim1; - QVERIFY(!group.firstChild()); + QVERIFY(group.children()->isEmpty()); QCOMPARE(group.duration(), 0); QCOMPARE(group.state(), QAnimationGroupJob::Stopped); QCOMPARE(group.currentLoopTime(), 0); //that's the invariant @@ -1568,12 +1565,12 @@ void tst_QSequentialAnimationGroupJob::clear() TestAnimation *anim2 = new TestAnimation; group.appendAnimation(anim2); - QCOMPARE(group.firstChild(), anim1); - QCOMPARE(group.lastChild(), anim2); + QCOMPARE(group.children()->first(), anim1); + QCOMPARE(group.children()->last(), anim2); group.start(); QTest::qWait(anim1->duration() + 100); - QTRY_VERIFY(!group.firstChild()); + QTRY_VERIFY(group.children()->isEmpty()); QCOMPARE(group.state(), QAbstractAnimationJob::Stopped); QCOMPARE(group.currentLoopTime(), 0); diff --git a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp index fe366d0f34..f903256d49 100644 --- a/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp +++ b/tests/auto/quick/qquickanimations/tst_qquickanimations.cpp @@ -1543,7 +1543,7 @@ void tst_qquickanimations::loopingBug() QVERIFY(anim != nullptr); QCOMPARE(anim->qtAnimation()->totalDuration(), 300); QCOMPARE(anim->isRunning(), true); - QTRY_COMPARE(static_cast<QAnimationGroupJob*>(anim->qtAnimation())->firstChild()->currentLoop(), 2); + QTRY_COMPARE(static_cast<QAnimationGroupJob*>(anim->qtAnimation())->children()->first()->currentLoop(), 2); QTRY_COMPARE(anim->isRunning(), false); QQuickRectangle *rect = obj->findChild<QQuickRectangle*>(); |