aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorUlf Hermann <ulf.hermann@qt.io>2021-02-03 14:24:14 +0100
committerUlf Hermann <ulf.hermann@qt.io>2021-02-05 15:20:02 +0100
commit4938984f9a779192264757a06e6ca555fc8f5e91 (patch)
treed4d1684298c889a3775f5dd19added3a23e24371 /src
parent680f28b08f65ad38c8d5498b5738231b2a2779a3 (diff)
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 <fabian.kosmale@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/qml/CMakeLists.txt1
-rw-r--r--src/qml/animations/qabstractanimationjob.cpp2
-rw-r--r--src/qml/animations/qabstractanimationjob_p.h7
-rw-r--r--src/qml/animations/qanimationgroupjob.cpp58
-rw-r--r--src/qml/animations/qanimationgroupjob_p.h19
-rw-r--r--src/qml/animations/qcontinuinganimationgroupjob.cpp18
-rw-r--r--src/qml/animations/qparallelanimationgroupjob.cpp24
-rw-r--r--src/qml/animations/qsequentialanimationgroupjob.cpp73
-rw-r--r--src/qml/animations/qsequentialanimationgroupjob_p.h6
-rw-r--r--src/qml/qml/ftw/qdoubleendedlist_p.h292
-rw-r--r--src/quick/util/qquickanimatorcontroller.cpp9
-rw-r--r--src/quick/util/qquickanimatorjob.cpp2
12 files changed, 389 insertions, 122 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);
}