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)
90 Transition will be applied for any particular state change. In the
91 example above, if the Rectangle enters a state other than \c "middleRight"
92 or \c "bottomLeft", the third Transition will be carried out, meaning the
93 icon will be moved to the starting point.
94
95 If a state change has a Transition that matches the same property as a
96 \l Behavior, the Transition animation overrides the \l Behavior for that
97 state change.
98
99 \sa {Animation and Transitions in Qt Quick}, {Qt Quick Examples - Animation#States}{States example}, {Qt Quick States}, {Qt QML}
100*/
101
102//ParallelAnimationWrapper allows us to do a "callback" when the animation finishes, rather than connecting
103//and disconnecting signals and slots frequently
104class ParallelAnimationWrapper : public QParallelAnimationGroupJob
105{
106public:
107 ParallelAnimationWrapper() : QParallelAnimationGroupJob() {}
108 QQuickTransitionManager *manager;
109
110protected:
111 void updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState) override;
112};
113
114class QQuickTransitionPrivate : public QObjectPrivate
115{
116 Q_DECLARE_PUBLIC(QQuickTransition)
117public:
118 QQuickTransitionPrivate()
119 : fromState(QLatin1String("*")), toState(QLatin1String("*"))
120 , runningInstanceCount(0), state(QAbstractAnimationJob::Stopped)
121 , reversed(false), reversible(false), enabled(true)
122 {
123 }
124
125 static QQuickTransitionPrivate *get(QQuickTransition *q) { return q->d_func(); }
126 void animationStateChanged(QAbstractAnimationJob::State newState);
127
128 QString fromState;
129 QString toState;
130 quint32 runningInstanceCount;
131 QAbstractAnimationJob::State state;
132 bool reversed;
133 bool reversible;
134 bool enabled;
135protected:
136
137 static void append_animation(QQmlListProperty<QQuickAbstractAnimation> *list, QQuickAbstractAnimation *a);
138 static int animation_count(QQmlListProperty<QQuickAbstractAnimation> *list);
139 static QQuickAbstractAnimation* animation_at(QQmlListProperty<QQuickAbstractAnimation> *list, int pos);
140 static void clear_animations(QQmlListProperty<QQuickAbstractAnimation> *list);
141 QList<QQuickAbstractAnimation *> animations;
142};
143
144void QQuickTransitionPrivate::append_animation(QQmlListProperty<QQuickAbstractAnimation> *list, QQuickAbstractAnimation *a)
145{
146 QQuickTransition *q = static_cast<QQuickTransition *>(list->object);
147 q->d_func()->animations.append(t: a);
148 a->setDisableUserControl();
149}
150
151int QQuickTransitionPrivate::animation_count(QQmlListProperty<QQuickAbstractAnimation> *list)
152{
153 QQuickTransition *q = static_cast<QQuickTransition *>(list->object);
154 return q->d_func()->animations.count();
155}
156
157QQuickAbstractAnimation* QQuickTransitionPrivate::animation_at(QQmlListProperty<QQuickAbstractAnimation> *list, int pos)
158{
159 QQuickTransition *q = static_cast<QQuickTransition *>(list->object);
160 return q->d_func()->animations.at(i: pos);
161}
162
163void QQuickTransitionPrivate::clear_animations(QQmlListProperty<QQuickAbstractAnimation> *list)
164{
165 QQuickTransition *q = static_cast<QQuickTransition *>(list->object);
166 while (q->d_func()->animations.count()) {
167 QQuickAbstractAnimation *firstAnim = q->d_func()->animations.at(i: 0);
168 q->d_func()->animations.removeAll(t: firstAnim);
169 }
170}
171
172void QQuickTransitionInstance::animationStateChanged(QAbstractAnimationJob *, QAbstractAnimationJob::State newState, QAbstractAnimationJob::State)
173{
174 if (!m_transition)
175 return;
176
177 QQuickTransitionPrivate *transition = QQuickTransitionPrivate::get(q: m_transition);
178 transition->animationStateChanged(newState);
179}
180
181void QQuickTransitionPrivate::animationStateChanged(QAbstractAnimationJob::State newState)
182{
183 Q_Q(QQuickTransition);
184
185 if (newState == QAbstractAnimationJob::Running) {
186 runningInstanceCount++;
187 if (runningInstanceCount == 1)
188 emit q->runningChanged();
189 } else if (newState == QAbstractAnimationJob::Stopped) {
190 runningInstanceCount--;
191 if (runningInstanceCount == 0)
192 emit q->runningChanged();
193 }
194}
195
196void ParallelAnimationWrapper::updateState(QAbstractAnimationJob::State newState, QAbstractAnimationJob::State oldState)
197{
198 QParallelAnimationGroupJob::updateState(newState, oldState);
199 if (newState == Stopped && (duration() == -1
200 || (direction() == QAbstractAnimationJob::Forward && currentLoopTime() == duration())
201 || (direction() == QAbstractAnimationJob::Backward && currentLoopTime() == 0)))
202 {
203 manager->complete();
204 }
205}
206
207QQuickTransitionInstance::QQuickTransitionInstance(QQuickTransition *transition, QAbstractAnimationJob *anim)
208 : m_transition(transition)
209 , m_anim(anim)
210{
211 anim->addAnimationChangeListener(listener: this, QAbstractAnimationJob::StateChange);
212}
213
214QQuickTransitionInstance::~QQuickTransitionInstance()
215{
216 removeStateChangeListener();
217 delete m_anim;
218}
219
220void QQuickTransitionInstance::start()
221{
222 if (m_anim)
223 m_anim->start();
224}
225
226void QQuickTransitionInstance::stop()
227{
228 if (m_anim)
229 m_anim->stop();
230}
231
232bool QQuickTransitionInstance::isRunning() const
233{
234 return m_anim && m_anim->state() == QAbstractAnimationJob::Running;
235}
236
237QQuickTransition::QQuickTransition(QObject *parent)
238 : QObject(*(new QQuickTransitionPrivate), parent)
239{
240}
241
242QQuickTransition::~QQuickTransition()
243{
244}
245
246void QQuickTransition::setReversed(bool r)
247{
248 Q_D(QQuickTransition);
249 d->reversed = r;
250}
251
252QQuickTransitionInstance *QQuickTransition::prepare(QQuickStateOperation::ActionList &actions,
253 QList<QQmlProperty> &after,
254 QQuickTransitionManager *manager,
255 QObject *defaultTarget)
256{
257 Q_D(QQuickTransition);
258
259 qmlExecuteDeferred(this);
260
261 ParallelAnimationWrapper *group = new ParallelAnimationWrapper();
262 group->manager = manager;
263
264 QQuickAbstractAnimation::TransitionDirection direction = d->reversed ? QQuickAbstractAnimation::Backward : QQuickAbstractAnimation::Forward;
265 int start = d->reversed ? d->animations.count() - 1 : 0;
266 int end = d->reversed ? -1 : d->animations.count();
267
268 QAbstractAnimationJob *anim = nullptr;
269 for (int i = start; i != end;) {
270 anim = d->animations.at(i)->transition(actions, modified&: after, direction, defaultTarget);
271 if (anim) {
272 if (d->animations.at(i)->threadingModel() == QQuickAbstractAnimation::RenderThread)
273 anim = new QQuickAnimatorProxyJob(anim, d->animations.at(i));
274 d->reversed ? group->prependAnimation(animation: anim) : group->appendAnimation(animation: anim);
275 }
276 d->reversed ? --i : ++i;
277 }
278
279 group->setDirection(d->reversed ? QAbstractAnimationJob::Backward : QAbstractAnimationJob::Forward);
280
281 QQuickTransitionInstance *wrapper = new QQuickTransitionInstance(this, group);
282 return wrapper;
283}
284
285/*!
286 \qmlproperty string QtQuick::Transition::from
287 \qmlproperty string QtQuick::Transition::to
288
289 These properties indicate the state changes that trigger the transition.
290
291 The default values for these properties is "*" (that is, any state).
292
293 For example, the following transition has not set the \c to and \c from
294 properties, so the animation is always applied when changing between
295 the two states (i.e. when the mouse is pressed and released).
296
297 \snippet qml/transition-from-to.qml 0
298
299 If the transition was changed to this:
300
301 \snippet qml/transition-from-to-modified.qml modified transition
302
303 The animation would only be applied when changing from the default state
304 to the "brighter" state (i.e. when the mouse is pressed, but not on release).
305
306 Multiple \c to and \c from values can be set by using a comma-separated
307 string.
308
309 \sa reversible
310*/
311QString QQuickTransition::fromState() const
312{
313 Q_D(const QQuickTransition);
314 return d->fromState;
315}
316
317void QQuickTransition::setFromState(const QString &f)
318{
319 Q_D(QQuickTransition);
320 if (f == d->fromState)
321 return;
322
323 d->fromState = f;
324 emit fromChanged();
325}
326
327/*!
328 \qmlproperty bool QtQuick::Transition::reversible
329 This property holds whether the transition should be automatically
330 reversed when the conditions that triggered this transition are reversed.
331
332 The default value is false.
333
334 By default, transitions run in parallel and are applied to all state
335 changes if the \l from and \l to states have not been set. In this
336 situation, the transition is automatically applied when a state change
337 is reversed, and it is not necessary to set this property to reverse
338 the transition.
339
340 However, if a SequentialAnimation is used, or if the \l from or \l to
341 properties have been set, this property will need to be set to reverse
342 a transition when a state change is reverted. For example, the following
343 transition applies a sequential animation when the mouse is pressed,
344 and reverses the sequence of the animation when the mouse is released:
345
346 \snippet qml/transition-reversible.qml 0
347
348 If the transition did not set the \c to and \c reversible values, then
349 on the mouse release, the transition would play the PropertyAnimation
350 before the ColorAnimation instead of reversing the sequence.
351*/
352bool QQuickTransition::reversible() const
353{
354 Q_D(const QQuickTransition);
355 return d->reversible;
356}
357
358void QQuickTransition::setReversible(bool r)
359{
360 Q_D(QQuickTransition);
361 if (r == d->reversible)
362 return;
363
364 d->reversible = r;
365 emit reversibleChanged();
366}
367
368QString QQuickTransition::toState() const
369{
370 Q_D(const QQuickTransition);
371 return d->toState;
372}
373
374void QQuickTransition::setToState(const QString &t)
375{
376 Q_D(QQuickTransition);
377 if (t == d->toState)
378 return;
379
380 d->toState = t;
381 emit toChanged();
382}
383
384/*!
385 \qmlproperty bool QtQuick::Transition::enabled
386
387 This property holds whether the Transition will be run when moving
388 from the \c from state to the \c to state.
389
390 By default a Transition is enabled.
391
392 Note that in some circumstances disabling a Transition may cause an
393 alternative Transition to be used in its place. In the following
394 example, although the first Transition has been set to animate changes
395 from "state1" to "state2", this transition has been disabled by setting
396 \c enabled to \c false, so any such state change will actually be animated
397 by the second Transition instead.
398
399 \qml
400 Item {
401 states: [
402 State { name: "state1" },
403 State { name: "state2" }
404 ]
405 transitions: [
406 Transition { from: "state1"; to: "state2"; enabled: false },
407 Transition {
408 // ...
409 }
410 ]
411 }
412 \endqml
413*/
414
415bool QQuickTransition::enabled() const
416{
417 Q_D(const QQuickTransition);
418 return d->enabled;
419}
420
421void QQuickTransition::setEnabled(bool enabled)
422{
423 Q_D(QQuickTransition);
424 if (d->enabled == enabled)
425 return;
426 d->enabled = enabled;
427 emit enabledChanged();
428}
429
430/*!
431 \qmlproperty bool QtQuick::Transition::running
432
433 This property holds whether the transition is currently running.
434
435 This property is read only.
436*/
437bool QQuickTransition::running() const
438{
439 Q_D(const QQuickTransition);
440 return d->runningInstanceCount;
441}
442
443
444/*!
445 \qmlproperty list<Animation> QtQuick::Transition::animations
446 \default
447
448 This property holds a list of the animations to be run for this transition.
449
450 \snippet ../qml/dynamicscene/dynamicscene.qml top-level transitions
451
452 The top-level animations are run in parallel. To run them sequentially,
453 define them within a SequentialAnimation:
454
455 \snippet qml/transition-reversible.qml sequential animations
456*/
457QQmlListProperty<QQuickAbstractAnimation> QQuickTransition::animations()
458{
459 Q_D(QQuickTransition);
460 return QQmlListProperty<QQuickAbstractAnimation>(this, &d->animations, QQuickTransitionPrivate::append_animation,
461 QQuickTransitionPrivate::animation_count,
462 QQuickTransitionPrivate::animation_at,
463 QQuickTransitionPrivate::clear_animations);
464}
465
466QT_END_NAMESPACE
467
468//#include <qquicktransition.moc>
469
470#include "moc_qquicktransition_p.cpp"
471

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