1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQuick module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qquicktransition_p.h"
41
42#include "qquickstate_p.h"
43#include "qquickstate_p_p.h"
44#include "qquickstatechangescript_p.h"
45#include "qquickanimation_p.h"
46#include "qquickanimation_p_p.h"
47#include "qquicktransitionmanager_p_p.h"
48
49#include <private/qquickanimatorjob_p.h>
50
51#include "private/qparallelanimationgroupjob_p.h"
52
53QT_BEGIN_NAMESPACE
54
55/*!
56 \qmltype Transition
57 \instantiates QQuickTransition
58 \inqmlmodule QtQuick
59 \ingroup qtquick-transitions-animations
60 \brief Defines animated transitions that occur on state changes.
61
62 A Transition defines the animations to be applied when a \l State change occurs.
63
64 For example, the following \l Rectangle has two states: the default state, and
65 an added "moved" state. In the "moved state, the rectangle's position changes
66 to (50, 50). The added Transition specifies that when the rectangle
67 changes between the default and the "moved" state, any changes
68 to the \c x and \c y properties should be animated, using an \c Easing.InOutQuad.
69
70 \snippet qml/transition.qml 0
71
72 Notice the example does not require \l{PropertyAnimation::}{to} and
73 \l{PropertyAnimation::}{from} values for the NumberAnimation. As a convenience,
74 these properties are automatically set to the values of \c x and \c y before
75 and after the state change; the \c from values are provided by
76 the current values of \c x and \c y, and the \c to values are provided by
77 the PropertyChanges object. If you wish, you can provide \l{PropertyAnimation::}{to} and
78 \l{PropertyAnimation::}{from} values anyway to override the default values.
79
80 By default, a Transition's animations are applied for any state change in the
81 parent item. The Transition \l {Transition::}{from} and \l {Transition::}{to}
82 values can be set to restrict the animations to only be applied when changing
83 from one particular state to another.
84
85 To define multiple transitions, specify \l Item::transitions as a list:
86
87 \snippet qml/transitions-list.qml list of transitions
88
89 If multiple Transitions are specified, only a single (best-matching) Transition will be applied for any particular
90 state change. In the example above, when changing to \c state1, the first transition will be used, rather
91 than the more generic second transition.
92
93 If a state change has a Transition that matches the same property as a
94 \l Behavior, the Transition animation overrides the \l Behavior for that
95 state change.
96
97 \sa {Animation and Transitions in Qt Quick}, {Qt Quick Examples - Animation#States}{States example}, {Qt Quick States}, {Qt QML}
98*/
99
100//ParallelAnimationWrapper allows us to do a "callback" when the animation finishes, rather than connecting
101//and disconnecting signals and slots frequently
102class ParallelAnimationWrapper : public QParallelAnimationGroupJob
103{
104public:
105 ParallelAnimationWrapper() : QParallelAnimationGroupJob() {}
106 QQuickTransitionManager *manager;
107
108protected:
109 void updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState) override;
110};
111
112class QQuickTransitionPrivate : public QObjectPrivate
113{
114 Q_DECLARE_PUBLIC(QQuickTransition)
115public:
116 QQuickTransitionPrivate()
117 : fromState(QLatin1String("*")), toState(QLatin1String("*"))
118 , runningInstanceCount(0), state(QAbstractAnimationJob::Stopped)
119 , reversed(false), reversible(false), enabled(true)
120 {
121 }
122
123 static QQuickTransitionPrivate *get(QQuickTransition *q) { return q->d_func(); }
124 void animationStateChanged(QAbstractAnimationJob::State newState);
125
126 QString fromState;
127 QString toState;
128 quint32 runningInstanceCount;
129 QAbstractAnimationJob::State state;
130 bool reversed;
131 bool reversible;
132 bool enabled;
133protected:
134
135 static void append_animation(QQmlListProperty<QQuickAbstractAnimation> *list, QQuickAbstractAnimation *a);
136 static int animation_count(QQmlListProperty<QQuickAbstractAnimation> *list);
137 static QQuickAbstractAnimation* animation_at(QQmlListProperty<QQuickAbstractAnimation> *list, int pos);
138 static void clear_animations(QQmlListProperty<QQuickAbstractAnimation> *list);
139 QList<QQuickAbstractAnimation *> animations;
140};
141
142void QQuickTransitionPrivate::append_animation(QQmlListProperty<QQuickAbstractAnimation> *list, QQuickAbstractAnimation *a)
143{
144 QQuickTransition *q = static_cast<QQuickTransition *>(list->object);
145 q->d_func()->animations.append(a);
146 a->setDisableUserControl();
147}
148
149int QQuickTransitionPrivate::animation_count(QQmlListProperty<QQuickAbstractAnimation> *list)
150{
151 QQuickTransition *q = static_cast<QQuickTransition *>(list->object);
152 return q->d_func()->animations.count();
153}
154
155QQuickAbstractAnimation* QQuickTransitionPrivate::animation_at(QQmlListProperty<QQuickAbstractAnimation> *list, int pos)
156{
157 QQuickTransition *q = static_cast<QQuickTransition *>(list->object);
158 return q->d_func()->animations.at(pos);
159}
160
161void QQuickTransitionPrivate::clear_animations(QQmlListProperty<QQuickAbstractAnimation> *list)
162{
163 QQuickTransition *q = static_cast<QQuickTransition *>(list->object);
164 while (q->d_func()->animations.count()) {
165 QQuickAbstractAnimation *firstAnim = q->d_func()->animations.at(0);
166 q->d_func()->animations.removeAll(firstAnim);
167 }
168}
169
170void QQuickTransitionInstance::animationStateChanged(QAbstractAnimationJob *, QAbstractAnimationJob::State newState, QAbstractAnimationJob::State)
171{
172 if (!m_transition)
173 return;
174
175 QQuickTransitionPrivate *transition = QQuickTransitionPrivate::get(m_transition);
176 transition->animationStateChanged(newState);
177}
178
179void QQuickTransitionPrivate::animationStateChanged(QAbstractAnimationJob::State newState)
180{
181 Q_Q(QQuickTransition);
182
183 if (newState == QAbstractAnimationJob::Running) {
184 runningInstanceCount++;
185 if (runningInstanceCount == 1)
186 emit q->runningChanged();
187 } else if (newState == QAbstractAnimationJob::Stopped) {
188 runningInstanceCount--;
189 if (runningInstanceCount == 0)
190 emit q->runningChanged();
191 }
192}
193
194void ParallelAnimationWrapper::updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState)
195{
196 QParallelAnimationGroupJob::updateState(newState, oldState);
197 if (newState == Stopped && (duration() == -1
198 || (direction() == QAbstractAnimationJob::Forward && currentLoopTime() == duration())
199 || (direction() == QAbstractAnimationJob::Backward && currentLoopTime() == 0)))
200 {
201 manager->complete();
202 }
203}
204
205QQuickTransitionInstance::QQuickTransitionInstance(QQuickTransition *transition, QAbstractAnimationJob *anim)
206 : m_transition(transition)
207 , m_anim(anim)
208{
209 anim->addAnimationChangeListener(this, QAbstractAnimationJob::StateChange);
210}
211
212QQuickTransitionInstance::~QQuickTransitionInstance()
213{
214 removeStateChangeListener();
215 delete m_anim;
216}
217
218void QQuickTransitionInstance::start()
219{
220 if (m_anim)
221 m_anim->start();
222}
223
224void QQuickTransitionInstance::stop()
225{
226 if (m_anim)
227 m_anim->stop();
228}
229
230bool QQuickTransitionInstance::isRunning() const
231{
232 return m_anim && m_anim->state() == QAbstractAnimationJob::Running;
233}
234
235QQuickTransition::QQuickTransition(QObject *parent)
236 : QObject(*(new QQuickTransitionPrivate), parent)
237{
238}
239
240QQuickTransition::~QQuickTransition()
241{
242}
243
244void QQuickTransition::setReversed(bool r)
245{
246 Q_D(QQuickTransition);
247 d->reversed = r;
248}
249
250QQuickTransitionInstance *QQuickTransition::prepare(QQuickStateOperation::ActionList &actions,
251 QList<QQmlProperty> &after,
252 QQuickTransitionManager *manager,
253 QObject *defaultTarget)
254{
255 Q_D(QQuickTransition);
256
257 qmlExecuteDeferred(this);
258
259 ParallelAnimationWrapper *group = new ParallelAnimationWrapper();
260 group->manager = manager;
261
262 QQuickAbstractAnimation::TransitionDirection direction = d->reversed ? QQuickAbstractAnimation::Backward : QQuickAbstractAnimation::Forward;
263 int start = d->reversed ? d->animations.count() - 1 : 0;
264 int end = d->reversed ? -1 : d->animations.count();
265
266 QAbstractAnimationJob *anim = nullptr;
267 for (int i = start; i != end;) {
268 anim = d->animations.at(i)->transition(actions, after, direction, defaultTarget);
269 if (anim) {
270 if (d->animations.at(i)->threadingModel() == QQuickAbstractAnimation::RenderThread)
271 anim = new QQuickAnimatorProxyJob(anim, d->animations.at(i));
272 d->reversed ? group->prependAnimation(anim) : group->appendAnimation(anim);
273 }
274 d->reversed ? --i : ++i;
275 }
276
277 group->setDirection(d->reversed ? QAbstractAnimationJob::Backward : QAbstractAnimationJob::Forward);
278
279 QQuickTransitionInstance *wrapper = new QQuickTransitionInstance(this, group);
280 return wrapper;
281}
282
283/*!
284 \qmlproperty string QtQuick::Transition::from
285 \qmlproperty string QtQuick::Transition::to
286
287 These properties indicate the state changes that trigger the transition.
288
289 The default values for these properties is "*" (that is, any state).
290
291 For example, the following transition has not set the \c to and \c from
292 properties, so the animation is always applied when changing between
293 the two states (i.e. when the mouse is pressed and released).
294
295 \snippet qml/transition-from-to.qml 0
296
297 If the transition was changed to this:
298
299 \snippet qml/transition-from-to-modified.qml modified transition
300
301 The animation would only be applied when changing from the default state to
302 the "brighter" state (i.e. when the mouse is pressed, but not on release).
303
304 Multiple \c to and \c from values can be set by using a comma-separated string.
305
306 \sa reversible
307*/
308QString QQuickTransition::fromState() const
309{
310 Q_D(const QQuickTransition);
311 return d->fromState;
312}
313
314void QQuickTransition::setFromState(const QString &f)
315{
316 Q_D(QQuickTransition);
317 if (f == d->fromState)
318 return;
319
320 d->fromState = f;
321 emit fromChanged();
322}
323
324/*!
325 \qmlproperty bool QtQuick::Transition::reversible
326 This property holds whether the transition should be automatically reversed when the conditions that triggered this transition are reversed.
327
328 The default value is false.
329
330 By default, transitions run in parallel and are applied to all state
331 changes if the \l from and \l to states have not been set. In this
332 situation, the transition is automatically applied when a state change
333 is reversed, and it is not necessary to set this property to reverse
334 the transition.
335
336 However, if a SequentialAnimation is used, or if the \l from or \l to
337 properties have been set, this property will need to be set to reverse
338 a transition when a state change is reverted. For example, the following
339 transition applies a sequential animation when the mouse is pressed,
340 and reverses the sequence of the animation when the mouse is released:
341
342 \snippet qml/transition-reversible.qml 0
343
344 If the transition did not set the \c to and \c reversible values, then
345 on the mouse release, the transition would play the PropertyAnimation
346 before the ColorAnimation instead of reversing the sequence.
347*/
348bool QQuickTransition::reversible() const
349{
350 Q_D(const QQuickTransition);
351 return d->reversible;
352}
353
354void QQuickTransition::setReversible(bool r)
355{
356 Q_D(QQuickTransition);
357 if (r == d->reversible)
358 return;
359
360 d->reversible = r;
361 emit reversibleChanged();
362}
363
364QString QQuickTransition::toState() const
365{
366 Q_D(const QQuickTransition);
367 return d->toState;
368}
369
370void QQuickTransition::setToState(const QString &t)
371{
372 Q_D(QQuickTransition);
373 if (t == d->toState)
374 return;
375
376 d->toState = t;
377 emit toChanged();
378}
379
380/*!
381 \qmlproperty bool QtQuick::Transition::enabled
382
383 This property holds whether the Transition will be run when moving
384 from the \c from state to the \c to state.
385
386 By default a Transition is enabled.
387
388 Note that in some circumstances disabling a Transition may cause an
389 alternative Transition to be used in its place. In the following
390 example, although the first Transition has been set to animate changes
391 from "state1" to "state2", this transition has been disabled by setting
392 \c enabled to \c false, so any such state change will actually be animated
393 by the second Transition instead.
394
395 \qml
396 Item {
397 states: [
398 State { name: "state1" },
399 State { name: "state2" }
400 ]
401 transitions: [
402 Transition { from: "state1"; to: "state2"; enabled: false },
403 Transition {
404 // ...
405 }
406 ]
407 }
408 \endqml
409*/
410
411bool QQuickTransition::enabled() const
412{
413 Q_D(const QQuickTransition);
414 return d->enabled;
415}
416
417void QQuickTransition::setEnabled(bool enabled)
418{
419 Q_D(QQuickTransition);
420 if (d->enabled == enabled)
421 return;
422 d->enabled = enabled;
423 emit enabledChanged();
424}
425
426/*!
427 \qmlproperty bool QtQuick::Transition::running
428
429 This property holds whether the transition is currently running.
430
431 This property is read only.
432*/
433bool QQuickTransition::running() const
434{
435 Q_D(const QQuickTransition);
436 return d->runningInstanceCount;
437}
438
439
440/*!
441 \qmlproperty list<Animation> QtQuick::Transition::animations
442 \default
443
444 This property holds a list of the animations to be run for this transition.
445
446 \snippet ../qml/dynamicscene/dynamicscene.qml top-level transitions
447
448 The top-level animations are run in parallel. To run them sequentially,
449 define them within a SequentialAnimation:
450
451 \snippet qml/transition-reversible.qml sequential animations
452*/
453QQmlListProperty<QQuickAbstractAnimation> QQuickTransition::animations()
454{
455 Q_D(QQuickTransition);
456 return QQmlListProperty<QQuickAbstractAnimation>(this, &d->animations, QQuickTransitionPrivate::append_animation,
457 QQuickTransitionPrivate::animation_count,
458 QQuickTransitionPrivate::animation_at,
459 QQuickTransitionPrivate::clear_animations);
460}
461
462QT_END_NAMESPACE
463
464//#include <qquicktransition.moc>
465
466#include "moc_qquicktransition_p.cpp"
467