1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Gunnar Sletta <gunnar@sletta.org>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qquickanimatorcontroller_p.h"
6
7#include <private/qquickitem_p.h>
8#include <private/qsgrenderloop_p.h>
9
10#include <private/qanimationgroupjob_p.h>
11
12#include <QtGui/qscreen.h>
13
14#include <QtCore/qcoreapplication.h>
15
16QT_BEGIN_NAMESPACE
17
18QQuickAnimatorController::~QQuickAnimatorController()
19{
20}
21
22QQuickAnimatorController::QQuickAnimatorController(QQuickWindow *window)
23 : m_window(window)
24{
25}
26
27static void qquickanimator_invalidate_jobs(QAbstractAnimationJob *job)
28{
29 if (job->isRenderThreadJob()) {
30 static_cast<QQuickAnimatorJob *>(job)->invalidate();
31 } else if (job->isGroup()) {
32 for (QAbstractAnimationJob *a : *static_cast<QAnimationGroupJob *>(job)->children())
33 qquickanimator_invalidate_jobs(job: a);
34 }
35}
36
37void QQuickAnimatorController::windowNodesDestroyed()
38{
39 for (const QSharedPointer<QAbstractAnimationJob> &toStop : std::as_const(t&: m_rootsPendingStop)) {
40 qquickanimator_invalidate_jobs(job: toStop.data());
41 toStop->stop();
42 }
43 m_rootsPendingStop.clear();
44
45 // Clear animation roots and iterate over a temporary to avoid that job->stop()
46 // modifies the m_animationRoots and messes with our iteration
47 const auto roots = m_animationRoots;
48 m_animationRoots.clear();
49 for (const QSharedPointer<QAbstractAnimationJob> &job : roots) {
50 qquickanimator_invalidate_jobs(job: job.data());
51
52 // Stop it and add it to the list of pending start so it might get
53 // started later on.
54 job->stop();
55 m_rootsPendingStart.insert(value: job);
56 }
57}
58
59void QQuickAnimatorController::advance()
60{
61 bool running = false;
62 for (const QSharedPointer<QAbstractAnimationJob> &job : std::as_const(t&: m_animationRoots)) {
63 if (job->isRunning()) {
64 running = true;
65 break;
66 }
67 }
68
69 for (QQuickAnimatorJob *job : std::as_const(t&: m_runningAnimators))
70 job->commit();
71
72 if (running)
73 m_window->update();
74}
75
76static void qquickanimator_sync_before_start(QAbstractAnimationJob *job)
77{
78 if (job->isRenderThreadJob()) {
79 static_cast<QQuickAnimatorJob *>(job)->preSync();
80 } else if (job->isGroup()) {
81 for (QAbstractAnimationJob *a : *static_cast<QAnimationGroupJob *>(job)->children())
82 qquickanimator_sync_before_start(job: a);
83 }
84}
85
86void QQuickAnimatorController::beforeNodeSync()
87{
88 for (const QSharedPointer<QAbstractAnimationJob> &toStop : std::as_const(t&: m_rootsPendingStop)) {
89 toStop->stop();
90 m_animationRoots.remove(key: toStop.data());
91 }
92 m_rootsPendingStop.clear();
93
94
95 for (QQuickAnimatorJob *job : std::as_const(t&: m_runningAnimators))
96 job->preSync();
97
98 // Start pending jobs
99 for (const QSharedPointer<QAbstractAnimationJob> &job : std::as_const(t&: m_rootsPendingStart)) {
100 Q_ASSERT(!job->isRunning());
101
102 // We want to make sure that presync is called before
103 // updateAnimationTime is called the very first time, so before
104 // starting a tree of jobs, we go through it and call preSync on all
105 // its animators.
106 qquickanimator_sync_before_start(job: job.data());
107
108 // The start the job..
109 job->start();
110 m_animationRoots.insert(key: job.data(), value: job);
111 }
112 m_rootsPendingStart.clear();
113
114 // Issue an update directly on the window to force another render pass.
115 if (m_animationRoots.size())
116 m_window->update();
117}
118
119void QQuickAnimatorController::afterNodeSync()
120{
121 for (QQuickAnimatorJob *job : std::as_const(t&: m_runningAnimators))
122 job->postSync();
123}
124
125void QQuickAnimatorController::animationFinished(QAbstractAnimationJob *job)
126{
127 m_animationRoots.remove(key: job);
128}
129
130void QQuickAnimatorController::animationStateChanged(QAbstractAnimationJob *job,
131 QAbstractAnimationJob::State newState,
132 QAbstractAnimationJob::State oldState)
133{
134 Q_ASSERT(job->isRenderThreadJob());
135 QQuickAnimatorJob *animator = static_cast<QQuickAnimatorJob *>(job);
136 if (newState == QAbstractAnimationJob::Running) {
137 m_runningAnimators.insert(value: animator);
138 } else if (oldState == QAbstractAnimationJob::Running) {
139 animator->commit();
140 m_runningAnimators.remove(value: animator);
141 }
142}
143
144
145void QQuickAnimatorController::requestSync()
146{
147 // Force a "sync" pass as the newly started animation needs to sync properties from GUI.
148 m_window->maybeUpdate();
149}
150
151// All this is being executed on the GUI thread while the animator controller
152// is locked.
153void QQuickAnimatorController::start_helper(QAbstractAnimationJob *job)
154{
155 if (job->isRenderThreadJob()) {
156 QQuickAnimatorJob *j = static_cast<QQuickAnimatorJob *>(job);
157 j->addAnimationChangeListener(listener: this, QAbstractAnimationJob::StateChange);
158 j->initialize(controller: this);
159 } else if (job->isGroup()) {
160 for (QAbstractAnimationJob *a : *static_cast<QAnimationGroupJob *>(job)->children())
161 start_helper(job: a);
162 }
163}
164
165// Called by the proxy when it is time to kick off an animation job
166void QQuickAnimatorController::start(const QSharedPointer<QAbstractAnimationJob> &job)
167{
168 m_rootsPendingStart.insert(value: job);
169 m_rootsPendingStop.remove(value: job);
170 job->addAnimationChangeListener(listener: this, QAbstractAnimationJob::Completion);
171 start_helper(job: job.data());
172 requestSync();
173}
174
175
176// Called by the proxy when it is time to stop an animation job.
177void QQuickAnimatorController::cancel(const QSharedPointer<QAbstractAnimationJob> &job)
178{
179 m_rootsPendingStart.remove(value: job);
180 m_rootsPendingStop.insert(value: job);
181}
182
183
184QT_END_NAMESPACE
185
186#include "moc_qquickanimatorcontroller_p.cpp"
187

source code of qtdeclarative/src/quick/util/qquickanimatorcontroller.cpp