1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qquickitemviewtransition_p.h"
5#include <QtQuick/qquickitem.h>
6#include <QtQuick/private/qquicktransition_p.h>
7#include <QtQuick/private/qquicktransitionmanager_p_p.h>
8
9QT_BEGIN_NAMESPACE
10
11static QList<int> qquickitemviewtransition_emptyIndexes = QList<int>();
12static QList<QObject *> qquickitemviewtransition_emptyTargets = QList<QObject *>();
13
14
15class QQuickItemViewTransitionJob : public QQuickTransitionManager
16{
17public:
18 QQuickItemViewTransitionJob();
19 ~QQuickItemViewTransitionJob();
20
21 void startTransition(QQuickItemViewTransitionableItem *item, int index, QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, const QPointF &to, bool isTargetItem);
22
23 QQuickItemViewTransitioner *m_transitioner;
24 QQuickItemViewTransitionableItem *m_item;
25 QPointF m_toPos;
26 QQuickItemViewTransitioner::TransitionType m_type;
27 bool m_isTarget;
28
29protected:
30 void finished() override;
31};
32
33
34QQuickItemViewTransitionJob::QQuickItemViewTransitionJob()
35 : m_transitioner(nullptr)
36 , m_item(nullptr)
37 , m_type(QQuickItemViewTransitioner::NoTransition)
38 , m_isTarget(false)
39{
40}
41
42QQuickItemViewTransitionJob::~QQuickItemViewTransitionJob()
43{
44 if (m_transitioner)
45 m_transitioner->runningJobs.remove(value: this);
46}
47
48void QQuickItemViewTransitionJob::startTransition(QQuickItemViewTransitionableItem *item, int index, QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, const QPointF &to, bool isTargetItem)
49{
50 if (type == QQuickItemViewTransitioner::NoTransition)
51 return;
52 if (!item) {
53 qWarning(msg: "startTransition(): invalid item");
54 return;
55 }
56 if (!transitioner) {
57 qWarning(msg: "startTransition(): invalid transitioner");
58 return;
59 }
60
61 QQuickTransition *trans = transitioner->transitionObject(type, asTarget: isTargetItem);
62 if (!trans) {
63 qWarning(msg: "QQuickItemView: invalid view transition!");
64 return;
65 }
66
67 m_item = item;
68 m_transitioner = transitioner;
69 m_toPos = to;
70 m_type = type;
71 m_isTarget = isTargetItem;
72
73 QQuickViewTransitionAttached *attached =
74 static_cast<QQuickViewTransitionAttached*>(qmlAttachedPropertiesObject<QQuickViewTransitionAttached>(obj: trans));
75 if (attached) {
76 attached->m_index = index;
77 attached->m_item = item->item;
78 attached->m_destination = to;
79 attached->m_targetIndexes = m_transitioner->targetIndexes(type);
80 attached->m_targetItems = m_transitioner->targetItems(type);
81 emit attached->indexChanged();
82 emit attached->itemChanged();
83 emit attached->destinationChanged();
84 emit attached->targetIndexesChanged();
85 emit attached->targetItemsChanged();
86 }
87
88 QQuickStateOperation::ActionList actions;
89 actions << QQuickStateAction(item->item, QLatin1String("x"), QVariant(to.x()));
90 actions << QQuickStateAction(item->item, QLatin1String("y"), QVariant(to.y()));
91
92 actions[0].fromValue = item->itemX();
93 actions[1].fromValue = item->itemY();
94 m_transitioner->runningJobs << this;
95 QQuickTransitionManager::transition(actions, transition: trans, defaultTarget: item->item);
96}
97
98void QQuickItemViewTransitionJob::finished()
99{
100 QQuickTransitionManager::finished();
101
102 if (m_transitioner) {
103 RETURN_IF_DELETED(m_transitioner->finishedTransition(this, m_item));
104 m_transitioner = nullptr;
105 }
106
107 m_item = nullptr;
108 m_toPos.setX(0);
109 m_toPos.setY(0);
110 m_type = QQuickItemViewTransitioner::NoTransition;
111 m_isTarget = false;
112}
113
114
115QQuickItemViewTransitioner::QQuickItemViewTransitioner()
116 : populateTransition(nullptr)
117 , addTransition(nullptr), addDisplacedTransition(nullptr)
118 , moveTransition(nullptr), moveDisplacedTransition(nullptr)
119 , removeTransition(nullptr), removeDisplacedTransition(nullptr)
120 , displacedTransition(nullptr)
121 , changeListener(nullptr)
122 , usePopulateTransition(false)
123{
124}
125
126QQuickItemViewTransitioner::~QQuickItemViewTransitioner()
127{
128 typedef QSet<QQuickItemViewTransitionJob *>::iterator JobIt;
129
130 for (JobIt it = runningJobs.begin(), end = runningJobs.end(); it != end; ++it)
131 (*it)->m_transitioner = nullptr;
132}
133
134bool QQuickItemViewTransitioner::canTransition(QQuickItemViewTransitioner::TransitionType type, bool asTarget) const
135{
136 if (!asTarget
137 && type != NoTransition && type != PopulateTransition
138 && displacedTransition && displacedTransition->enabled()) {
139 return true;
140 }
141
142 switch (type) {
143 case NoTransition:
144 break;
145 case PopulateTransition:
146 return usePopulateTransition
147 && populateTransition && populateTransition->enabled();
148 case AddTransition:
149 if (asTarget)
150 return addTransition && addTransition->enabled();
151 else
152 return addDisplacedTransition && addDisplacedTransition->enabled();
153 case MoveTransition:
154 if (asTarget)
155 return moveTransition && moveTransition->enabled();
156 else
157 return moveDisplacedTransition && moveDisplacedTransition->enabled();
158 case RemoveTransition:
159 if (asTarget)
160 return removeTransition && removeTransition->enabled();
161 else
162 return removeDisplacedTransition && removeDisplacedTransition->enabled();
163 }
164 return false;
165}
166
167void QQuickItemViewTransitioner::transitionNextReposition(QQuickItemViewTransitionableItem *item, QQuickItemViewTransitioner::TransitionType type, bool isTarget)
168{
169 item->setNextTransition(type, isTargetItem: isTarget);
170}
171
172void QQuickItemViewTransitioner::addToTargetLists(QQuickItemViewTransitioner::TransitionType type, QQuickItemViewTransitionableItem *item, int index)
173{
174 switch (type) {
175 case NoTransition:
176 break;
177 case PopulateTransition:
178 case AddTransition:
179 addTransitionIndexes << index;
180 addTransitionTargets << item->item;
181 break;
182 case MoveTransition:
183 moveTransitionIndexes << index;
184 moveTransitionTargets << item->item;
185 break;
186 case RemoveTransition:
187 removeTransitionIndexes << index;
188 removeTransitionTargets << item->item;
189 break;
190 }
191}
192
193void QQuickItemViewTransitioner::resetTargetLists()
194{
195 addTransitionIndexes.clear();
196 addTransitionTargets.clear();
197
198 removeTransitionIndexes.clear();
199 removeTransitionTargets.clear();
200
201 moveTransitionIndexes.clear();
202 moveTransitionTargets.clear();
203}
204
205QQuickTransition *QQuickItemViewTransitioner::transitionObject(QQuickItemViewTransitioner::TransitionType type, bool asTarget) const
206{
207 if (type == QQuickItemViewTransitioner::NoTransition)
208 return nullptr;
209
210 if (type == PopulateTransition)
211 asTarget = true; // no separate displaced transition
212
213 QQuickTransition *trans = nullptr;
214 switch (type) {
215 case NoTransition:
216 break;
217 case PopulateTransition:
218 trans = populateTransition;
219 break;
220 case AddTransition:
221 trans = asTarget ? addTransition : addDisplacedTransition;
222 break;
223 case MoveTransition:
224 trans = asTarget ? moveTransition : moveDisplacedTransition;
225 break;
226 case RemoveTransition:
227 trans = asTarget ? removeTransition : removeDisplacedTransition;
228 break;
229 }
230
231 if (!asTarget && (!trans || !trans->enabled()))
232 trans = displacedTransition;
233 if (trans && trans->enabled())
234 return trans;
235 return nullptr;
236}
237
238const QList<int> &QQuickItemViewTransitioner::targetIndexes(QQuickItemViewTransitioner::TransitionType type) const
239{
240 switch (type) {
241 case NoTransition:
242 break;
243 case PopulateTransition:
244 case AddTransition:
245 return addTransitionIndexes;
246 case MoveTransition:
247 return moveTransitionIndexes;
248 case RemoveTransition:
249 return removeTransitionIndexes;
250 }
251
252 return qquickitemviewtransition_emptyIndexes;
253}
254
255const QList<QObject *> &QQuickItemViewTransitioner::targetItems(QQuickItemViewTransitioner::TransitionType type) const
256{
257 switch (type) {
258 case NoTransition:
259 break;
260 case PopulateTransition:
261 case AddTransition:
262 return addTransitionTargets;
263 case MoveTransition:
264 return moveTransitionTargets;
265 case RemoveTransition:
266 return removeTransitionTargets;
267 }
268
269 return qquickitemviewtransition_emptyTargets;
270}
271
272void QQuickItemViewTransitioner::finishedTransition(QQuickItemViewTransitionJob *job, QQuickItemViewTransitionableItem *item)
273{
274 if (!runningJobs.contains(value: job))
275 return;
276 runningJobs.remove(value: job);
277 if (item) {
278 item->finishedTransition();
279 if (changeListener)
280 changeListener->viewItemTransitionFinished(item);
281 }
282}
283
284
285QQuickItemViewTransitionableItem::QQuickItemViewTransitionableItem(QQuickItem *i)
286 : item(i)
287 , transition(nullptr)
288 , nextTransitionType(QQuickItemViewTransitioner::NoTransition)
289 , isTransitionTarget(false)
290 , nextTransitionToSet(false)
291 , nextTransitionFromSet(false)
292 , lastMovedToSet(false)
293 , prepared(false)
294{
295}
296
297QQuickItemViewTransitionableItem::~QQuickItemViewTransitionableItem()
298{
299 delete transition;
300}
301
302qreal QQuickItemViewTransitionableItem::itemX() const
303{
304 if (nextTransitionType != QQuickItemViewTransitioner::NoTransition)
305 return nextTransitionToSet ? nextTransitionTo.x() : item->x();
306 else if (transition && transition->isRunning())
307 return transition->m_toPos.x();
308 else
309 return item->x();
310}
311
312qreal QQuickItemViewTransitionableItem::itemY() const
313{
314 // If item is transitioning to some pos, return that dest pos.
315 // If item was redirected to some new pos before the current transition finished,
316 // return that new pos.
317 if (nextTransitionType != QQuickItemViewTransitioner::NoTransition)
318 return nextTransitionToSet ? nextTransitionTo.y() : item->y();
319 else if (transition && transition->isRunning())
320 return transition->m_toPos.y();
321 else
322 return item->y();
323}
324
325void QQuickItemViewTransitionableItem::moveTo(const QPointF &pos, bool immediate)
326{
327 if (!nextTransitionFromSet && nextTransitionType != QQuickItemViewTransitioner::NoTransition) {
328 nextTransitionFrom = item->position();
329 nextTransitionFromSet = true;
330 }
331
332 lastMovedTo = pos;
333 lastMovedToSet = true;
334
335 if (immediate || !transitionScheduledOrRunning()) {
336 if (immediate)
337 stopTransition();
338 item->setPosition(pos);
339 } else {
340 nextTransitionTo = pos;
341 nextTransitionToSet = true;
342 }
343}
344
345bool QQuickItemViewTransitionableItem::transitionScheduledOrRunning() const
346{
347 return (transition && transition->isRunning())
348 || nextTransitionType != QQuickItemViewTransitioner::NoTransition;
349}
350
351bool QQuickItemViewTransitionableItem::transitionRunning() const
352{
353 return (transition && transition->isRunning());
354}
355
356bool QQuickItemViewTransitionableItem::isPendingRemoval() const
357{
358 if (nextTransitionType == QQuickItemViewTransitioner::RemoveTransition)
359 return isTransitionTarget;
360 if (transition && transition->isRunning() && transition->m_type == QQuickItemViewTransitioner::RemoveTransition)
361 return transition->m_isTarget;
362 return false;
363}
364
365bool QQuickItemViewTransitionableItem::prepareTransition(QQuickItemViewTransitioner *transitioner, int index, const QRectF &viewBounds)
366{
367 if (nextTransitionType == QQuickItemViewTransitioner::NoTransition)
368 return false;
369
370 if (isTransitionTarget) {
371 // If item is not already moving somewhere, set it to not move anywhere.
372 // This ensures that removed targets don't transition to the default (0,0) and that
373 // items set for other transition types only transition if they actually move somewhere.
374 if (!nextTransitionToSet)
375 moveTo(pos: item->position());
376 } else {
377 // don't start displaced transitions that don't move anywhere
378 if (!nextTransitionToSet || (nextTransitionFromSet && nextTransitionFrom == nextTransitionTo)) {
379 clearCurrentScheduledTransition();
380 return false;
381 }
382 }
383
384 bool doTransition = false;
385
386 // For move transitions (both target and displaced) and displaced transitions of other
387 // types, only run the transition if the item is actually moving to another position.
388 switch (nextTransitionType) {
389 case QQuickItemViewTransitioner::NoTransition:
390 {
391 return false;
392 }
393 case QQuickItemViewTransitioner::PopulateTransition:
394 {
395 doTransition = viewBounds.intersects(r: QRectF(nextTransitionTo.x(), nextTransitionTo.y(), item->width(), item->height()));
396 break;
397 }
398 case QQuickItemViewTransitioner::AddTransition:
399 case QQuickItemViewTransitioner::RemoveTransition:
400 if (viewBounds.isNull()) {
401 if (isTransitionTarget)
402 doTransition = true;
403 else
404 doTransition = transitionWillChangePosition();
405 } else if (isTransitionTarget) {
406 // For Add targets, do transition if item is moving into visible area
407 // For Remove targets, do transition if item is currently in visible area
408 doTransition = (nextTransitionType == QQuickItemViewTransitioner::AddTransition)
409 ? viewBounds.intersects(r: QRectF(nextTransitionTo.x(), nextTransitionTo.y(), item->width(), item->height()))
410 : viewBounds.intersects(r: QRectF(item->x(), item->y(), item->width(), item->height()));
411 } else {
412 // do transition if moving from or into visible area
413 if (viewBounds.intersects(r: QRectF(item->x(), item->y(), item->width(), item->height()))
414 || viewBounds.intersects(r: QRectF(nextTransitionTo.x(), nextTransitionTo.y(), item->width(), item->height()))) {
415 doTransition = transitionWillChangePosition();
416 }
417 }
418 break;
419 case QQuickItemViewTransitioner::MoveTransition:
420 // do transition if moving from or into visible area
421 if (transitionWillChangePosition()) {
422 doTransition = viewBounds.isNull()
423 || viewBounds.intersects(r: QRectF(item->x(), item->y(), item->width(), item->height()))
424 || viewBounds.intersects(r: QRectF(nextTransitionTo.x(), nextTransitionTo.y(), item->width(), item->height()));
425 }
426 break;
427 }
428
429 if (doTransition) {
430 // add item to target lists even if canTransition() is false for a target transition,
431 // since the target lists still need to be filled for displaced transitions
432 if (isTransitionTarget)
433 transitioner->addToTargetLists(type: nextTransitionType, item: this, index);
434 doTransition = transitioner->canTransition(type: nextTransitionType, asTarget: isTransitionTarget);
435 }
436
437 if (!doTransition) {
438 // if transition type is not valid, the previous transition still has to be
439 // canceled so that the item can move immediately to the right position
440 item->setPosition(nextTransitionTo);
441 ACTION_IF_DELETED(this, stopTransition(), return false);
442 }
443
444 prepared = true;
445 return doTransition;
446}
447
448void QQuickItemViewTransitionableItem::startTransition(QQuickItemViewTransitioner *transitioner, int index)
449{
450 if (nextTransitionType == QQuickItemViewTransitioner::NoTransition)
451 return;
452
453 if (!prepared) {
454 qWarning(msg: "QQuickViewItem::prepareTransition() not called!");
455 return;
456 }
457
458 if (!transition || transition->m_type != nextTransitionType || transition->m_isTarget != isTransitionTarget) {
459 if (transition)
460 RETURN_IF_DELETED(transition->cancel());
461 delete transition;
462 transition = new QQuickItemViewTransitionJob;
463 }
464
465 RETURN_IF_DELETED(transition->startTransition(this, index, transitioner, nextTransitionType, nextTransitionTo, isTransitionTarget));
466 clearCurrentScheduledTransition();
467}
468
469void QQuickItemViewTransitionableItem::completeTransition(QQuickTransition *quickTransition)
470{
471 if (nextTransitionType == QQuickItemViewTransitioner::NoTransition)
472 return;
473
474 if (!prepared) {
475 qWarning(msg: "QQuickViewItem::prepareTransition() not called!");
476 return;
477 }
478
479 if (!item) {
480 qWarning(msg: "No target for transition!");
481 return;
482 }
483
484 if (!transition || transition->m_type != nextTransitionType || transition->m_isTarget != isTransitionTarget) {
485 if (transition)
486 RETURN_IF_DELETED(transition->cancel());
487 delete transition;
488 transition = new QQuickItemViewTransitionJob;
489 }
490
491 QQuickStateOperation::ActionList actions; // not used
492 QList<QQmlProperty> after; // not used
493 QScopedPointer<QQuickTransitionInstance> instance(
494 quickTransition->prepare(actions, after, end: transition, defaultTarget: item));
495 RETURN_IF_DELETED(instance->complete());
496
497 clearCurrentScheduledTransition();
498}
499
500void QQuickItemViewTransitionableItem::setNextTransition(QQuickItemViewTransitioner::TransitionType type, bool isTargetItem)
501{
502 // Don't reset nextTransitionToSet - once it is set, it cannot be changed
503 // until the animation finishes since the itemX() and itemY() may be used
504 // to calculate positions for transitions for other items in the view.
505 nextTransitionType = type;
506 isTransitionTarget = isTargetItem;
507
508 if (!nextTransitionFromSet && lastMovedToSet) {
509 nextTransitionFrom = lastMovedTo;
510 nextTransitionFromSet = true;
511 }
512}
513
514bool QQuickItemViewTransitionableItem::transitionWillChangePosition() const
515{
516 if (transitionRunning() && transition->m_toPos != nextTransitionTo)
517 return true;
518 if (!nextTransitionFromSet)
519 return false;
520 return nextTransitionTo != nextTransitionFrom;
521}
522
523void QQuickItemViewTransitionableItem::resetNextTransitionPos()
524{
525 nextTransitionToSet = false;
526 nextTransitionTo = QPointF();
527}
528
529void QQuickItemViewTransitionableItem::finishedTransition()
530{
531 resetNextTransitionPos();
532}
533
534void QQuickItemViewTransitionableItem::clearCurrentScheduledTransition()
535{
536 // Just clear the current scheduled transition - don't touch the nextTransitionTo
537 // which may have already been set for a previously scheduled transition
538
539 nextTransitionType = QQuickItemViewTransitioner::NoTransition;
540 isTransitionTarget = false;
541 prepared = false;
542 nextTransitionFromSet = false;
543}
544
545void QQuickItemViewTransitionableItem::stopTransition()
546{
547 if (transition)
548 RETURN_IF_DELETED(transition->cancel());
549 delete transition;
550 transition = nullptr;
551 clearCurrentScheduledTransition();
552 resetNextTransitionPos();
553}
554
555
556QQuickViewTransitionAttached::QQuickViewTransitionAttached(QObject *parent)
557 : QObject(parent), m_index(-1)
558{
559}
560/*!
561 \qmltype ViewTransition
562 \instantiates QQuickViewTransitionAttached
563 \inqmlmodule QtQuick
564 \ingroup qtquick-transitions-animations
565 \brief Specifies items under transition in a view.
566
567 With ListView and GridView, it is possible to specify transitions that should be applied whenever
568 the items in the view change as a result of modifications to the view's model. They both have the
569 following properties that can be set to the appropriate transitions to be run for various
570 operations:
571
572 \list
573 \li \c populate - the transition to apply to the items created initially for the view, or when the model changes
574 \li \c add - the transition to apply to items that are added to the view after it has been created
575 \li \c remove - the transition to apply to items that are removed from the view
576 \li \c move - the transition to apply to items that are moved within the view (i.e. as a result
577 of a move operation in the model)
578 \li \c displaced - the generic transition to be applied to any items that are displaced by an
579 add, move or remove operation
580 \li \c addDisplaced, \c removeDisplaced and \c moveDisplaced - the transitions to be applied when
581 items are displaced by add, move, or remove operations, respectively (these override the
582 generic displaced transition if specified)
583 \endlist
584
585 For the \l Row, \l Column, \l Grid and \l Flow positioner types, which operate with collections of child
586 items rather than data models, the following properties are used instead:
587
588 \list
589 \li \c populate - the transition to apply to items that have been added to the positioner at the
590 time of its creation
591 \li \c add - the transition to apply to items that are added to
592 or reparented to the positioner, or items that have become \l {Item::}{visible}
593 \li \c move - the transition to apply to items that have moved within the positioner, including
594 when they are displaced due to the addition or removal of other items, or when items are otherwise
595 rearranged within the positioner, or when items are repositioned due to the resizing of other
596 items in the positioner
597 \endlist
598
599 View transitions have access to a ViewTransition attached property that
600 provides details of the items that are under transition and the operation that triggered the
601 transition. Since view transitions are run once per item, these details can be used to customize
602 each transition for each individual item.
603
604 The ViewTransition attached property provides the following properties specific to the item to
605 which the transition is applied:
606
607 \list
608 \li ViewTransition.item - the item that is under transition
609 \li ViewTransition.index - the index of this item
610 \li ViewTransition.destination - the (x,y) point to which this item is moving for the relevant view operation
611 \endlist
612
613 In addition, ViewTransition provides properties specific to the items which are the target
614 of the operation that triggered the transition:
615
616 \list
617 \li ViewTransition.targetIndexes - the indexes of the target items
618 \li ViewTransition.targetItems - the target items themselves
619 \endlist
620
621 (Note that for the \l Row, \l Column, \l Grid and \l Flow positioner types, the \c move transition only
622 provides these two additional details when the transition is triggered by the addition of items
623 to a positioner.)
624
625 View transitions can be written without referring to any of the attributes listed
626 above. These attributes merely provide extra details that are useful for customising view
627 transitions.
628
629 Following is an introduction to view transitions and the ways in which the ViewTransition
630 attached property can be used to augment view transitions.
631
632
633 \section2 View Transitions: a Simple Example
634
635 Here is a basic example of the use of view transitions. The view below specifies transitions for
636 the \c add and \c displaced properties, which will be run when items are added to the view:
637
638 \snippet qml/viewtransitions/viewtransitions-basic.qml 0
639
640 When the space key is pressed, adding an item to the model, the new item will fade in and
641 increase in scale over 400 milliseconds as it is added to the view. Also, any item that is
642 displaced by the addition of a new item will animate to its new position in the view over
643 400 milliseconds, as specified by the \c displaced transition.
644
645 If five items were inserted in succession at index 0, the effect would be this:
646
647 \image viewtransitions-basic.gif
648
649 Notice that the NumberAnimation objects above do not need to specify a \c target to animate
650 the appropriate item. Also, the NumberAnimation in the \c addTransition does not need to specify
651 the \c to value to move the item to its correct position in the view. This is because the view
652 implicitly sets the \c target and \c to values with the correct item and final item position
653 values if these properties are not explicitly defined.
654
655 At its simplest, a view transition may just animate an item to its new position following a
656 view operation, just as the \c displaced transition does above, or animate some item properties,
657 as in the \c add transition above. Additionally, a view transition may make use of the
658 ViewTransition attached property to customize animation behavior for different items. Following
659 are some examples of how this can be achieved.
660
661
662 \section2 Using the ViewTransition Attached Property
663
664 As stated, the various ViewTransition properties provide details specific to the individual item
665 being transitioned as well as the operation that triggered the transition. In the animation above,
666 five items are inserted in succession at index 0. When the fifth and final insertion takes place,
667 adding "Item 4" to the view, the \c add transition is run once (for the inserted item) and the
668 \c displaced transition is run four times (once for each of the four existing items in the view).
669
670 At this point, if we examined the \c displaced transition that was run for the bottom displaced
671 item ("Item 0"), the ViewTransition property values provided to this transition would be as follows:
672
673 \table
674 \header
675 \li Property
676 \li Value
677 \li Explanation
678 \row
679 \li ViewTransition.item
680 \li "Item 0" delegate instance
681 \li The "Item 0" \l Rectangle object itself
682 \row
683 \li ViewTransition.index
684 \li \c int value of 4
685 \li The index of "Item 0" within the model following the add operation
686 \row
687 \li ViewTransition.destination
688 \li \l point value of (0, 120)
689 \li The position that "Item 0" is moving to
690 \row
691 \li ViewTransition.targetIndexes
692 \li \c int array, just contains the integer "0" (zero)
693 \li The index of "Item 4", the new item added to the view
694 \row
695 \li ViewTransition.targetItems
696 \li object array, just contains the "Item 4" delegate instance
697 \li The "Item 4" \l Rectangle object - the new item added to the view
698 \endtable
699
700 The ViewTransition.targetIndexes and ViewTransition.targetItems lists provide the items and
701 indexes of all delegate instances that are the targets of the relevant operation. For an add
702 operation, these are all the items that are added into the view; for a remove, these are all
703 the items removed from the view, and so on. (Note these lists will only contain references to
704 items that have been created within the view or its cached items; targets that are not within
705 the visible area of the view or within the item cache will not be accessible.)
706
707 So, while the ViewTransition.item, ViewTransition.index and ViewTransition.destination values
708 vary for each individual transition that is run, the ViewTransition.targetIndexes and
709 ViewTransition.targetItems values are the same for every \c add and \c displaced transition
710 that is triggered by a particular add operation.
711
712
713 \section3 Delaying Animations Based on Index
714
715 Since each view transition is run once for each item affected by the transition, the ViewTransition
716 properties can be used within a transition to define custom behavior for each item's transition.
717 For example, the ListView in the previous example could use this information to create a ripple-type
718 effect on the movement of the displaced items.
719
720 This can be achieved by modifying the \c displaced transition so that it delays the animation of
721 each displaced item based on the difference between its index (provided by ViewTransition.index)
722 and the first removed index (provided by ViewTransition.targetIndexes):
723
724 \snippet qml/viewtransitions/viewtransitions-delayedbyindex.qml 0
725
726 Each displaced item delays its animation by an additional 100 milliseconds, producing a subtle
727 ripple-type effect when items are displaced by the add, like this:
728
729 \image viewtransitions-delayedbyindex.gif
730
731
732 \section3 Animating Items to Intermediate Positions
733
734 The ViewTransition.item property gives a reference to the item to which the transition is being
735 applied. This can be used to access any of the item's attributes, custom \c property values,
736 and so on.
737
738 Below is a modification of the \c displaced transition from the previous example. It adds a
739 ParallelAnimation with nested NumberAnimation objects that reference ViewTransition.item to access
740 each item's \c x and \c y values at the start of their transitions. This allows each item to
741 animate to an intermediate position relative to its starting point for the transition, before
742 animating to its final position in the view:
743
744 \snippet qml/viewtransitions/viewtransitions-intermediatemove.qml 0
745
746 Now, a displaced item will first move to a position of (20, 50) relative to its starting
747 position, and then to its final, correct position in the view:
748
749 \image viewtransitions-intermediatemove.gif
750
751 Since the final NumberAnimation does not specify a \c to value, the view implicitly sets this
752 value to the item's final position in the view, and so this last animation will move this item
753 to the correct place. If the transition requires the final position of the item for some calculation,
754 this is accessible through ViewTransition.destination.
755
756 Instead of using multiple NumberAnimations, you could use a PathAnimation to animate an item over
757 a curved path. For example, the \c add transition in the previous example could be augmented with
758 a PathAnimation as follows: to animate newly added items along a path:
759
760 \snippet qml/viewtransitions/viewtransitions-pathanim.qml 0
761
762 This animates newly added items along a path. Notice that each path is specified relative to
763 each item's final destination point, so that items inserted at different indexes start their
764 paths from different positions:
765
766 \image viewtransitions-pathanim.gif
767
768
769 \section2 Handling Interrupted Animations
770
771 A view transition may be interrupted at any time if a different view transition needs to be
772 applied while the original transition is in progress. For example, say Item A is inserted at index 0
773 and undergoes an "add" transition; then, Item B is inserted at index 0 in quick succession before
774 Item A's transition has finished. Since Item B is inserted before Item A, it will displace Item
775 A, causing the view to interrupt Item A's "add" transition mid-way and start a "displaced"
776 transition on Item A instead.
777
778 For simple animations that simply animate an item's movement to its final destination, this
779 interruption is unlikely to require additional consideration. However, if a transition changes other
780 properties, this interruption may cause unwanted side effects. Consider the first example on this
781 page, repeated below for convenience:
782
783 \snippet qml/viewtransitions/viewtransitions-basic.qml 0
784
785 If multiple items are added in rapid succession, without waiting for a previous transition
786 to finish, this is the result:
787
788 \image viewtransitions-interruptedbad.gif
789
790 Each newly added item undergoes an \c add transition, but before the transition can finish,
791 another item is added, displacing the previously added item. Because of this, the \c add
792 transition on the previously added item is interrupted and a \c displaced transition is
793 started on the item instead. Due to the interruption, the \c opacity and \c scale animations
794 have not completed, thus producing items with opacity and scale that are below 1.0.
795
796 To fix this, the \c displaced transition should additionally ensure the item properties are
797 set to the end values specified in the \c add transition, effectively resetting these values
798 whenever an item is displaced. In this case, it means setting the item opacity and scale to 1.0:
799
800 \snippet qml/viewtransitions/viewtransitions-interruptedgood.qml 0
801
802 Now, when an item's \c add transition is interrupted, its opacity and scale are animated to 1.0
803 upon displacement, avoiding the erroneous visual effects from before:
804
805 \image viewtransitions-interruptedgood.gif
806
807 The same principle applies to any combination of view transitions. An added item may be moved
808 before its add transition finishes, or a moved item may be removed before its moved transition
809 finishes, and so on; so, the rule of thumb is that every transition should handle the same set of
810 properties.
811
812
813 \section2 Restrictions Regarding ScriptAction
814
815 When a view transition is initialized, any property bindings that refer to the ViewTransition
816 attached property are evaluated in preparation for the transition. Due to the nature of the
817 internal construction of a view transition, the attributes of the ViewTransition attached
818 property are only valid for the relevant item when the transition is initialized, and may not be
819 valid when the transition is actually run.
820
821 Therefore, a ScriptAction within a view transition should not refer to the ViewTransition
822 attached property, as it may not refer to the expected values at the time that the ScriptAction
823 is actually invoked. Consider the following example:
824
825 \snippet qml/viewtransitions/viewtransitions-scriptactionbad.qml 0
826
827 When the space key is pressed, three items are moved from index 5 to index 1. For each moved
828 item, the \c moveTransition sequence presumably animates the item's color to "yellow", then
829 animates it to its final position, then changes the item color back to "lightsteelblue" using a
830 ScriptAction. However, when run, the transition does not produce the intended result:
831
832 \image viewtransitions-scriptactionbad.gif
833
834 Only the last moved item is returned to the "lightsteelblue" color; the others remain yellow. This
835 is because the ScriptAction is not run until after the transition has already been initialized, by
836 which time the ViewTransition.item value has changed to refer to a different item; the item that
837 the script had intended to refer to is not the one held by ViewTransition.item at the time the
838 ScriptAction is actually invoked.
839
840 In this instance, to avoid this issue, the view could set the property using a PropertyAction
841 instead:
842
843 \snippet qml/viewtransitions/viewtransitions-scriptactiongood.qml 0
844
845 When the transition is initialized, the PropertyAction \c target will be set to the respective
846 ViewTransition.item for the transition and will later run with the correct item target as
847 expected.
848 */
849
850/*!
851 \qmlattachedproperty int QtQuick::ViewTransition::index
852
853 This attached property holds the index of the item that is being
854 transitioned.
855
856 Note that if the item is being moved, this property holds the index that
857 the item is moving to, not from.
858*/
859
860/*!
861 \qmlattachedproperty item QtQuick::ViewTransition::item
862
863 This attached property holds the item that is being transitioned.
864
865 \warning This item should not be kept and referred to outside of the transition
866 as it may become invalid as the view changes.
867*/
868
869/*!
870 \qmlattachedproperty point QtQuick::ViewTransition::destination
871
872 This attached property holds the final destination position for the transitioned
873 item within the view.
874
875 This property value is a \l point with \c x and \c y properties.
876*/
877
878/*!
879 \qmlattachedproperty list QtQuick::ViewTransition::targetIndexes
880
881 This attached property holds a list of the indexes of the items in view
882 that are the target of the relevant operation.
883
884 The targets are the items that are the subject of the operation. For
885 an add operation, these are the items being added; for a remove, these
886 are the items being removed; for a move, these are the items being
887 moved.
888
889 For example, if the transition was triggered by an insert operation
890 that added two items at index 1 and 2, this targetIndexes list would
891 have the value [1,2].
892
893 \note The targetIndexes list only contains the indexes of items that are actually
894 in view, or will be in the view once the relevant operation completes.
895
896 \sa QtQuick::ViewTransition::targetItems
897*/
898
899/*!
900 \qmlattachedproperty list QtQuick::ViewTransition::targetItems
901
902 This attached property holds the list of items in view that are the
903 target of the relevant operation.
904
905 The targets are the items that are the subject of the operation. For
906 an add operation, these are the items being added; for a remove, these
907 are the items being removed; for a move, these are the items being
908 moved.
909
910 For example, if the transition was triggered by an insert operation
911 that added two items at index 1 and 2, this targetItems list would
912 contain these two items.
913
914 \note The targetItems list only contains items that are actually
915 in view, or will be in the view once the relevant operation completes.
916
917 \warning The objects in this list should not be kept and referred to
918 outside of the transition as the items may become invalid. The targetItems
919 are only valid when the Transition is initially created; this also means
920 they should not be used by ScriptAction objects in the Transition, which are
921 not evaluated until the transition is run.
922
923 \sa QtQuick::ViewTransition::targetIndexes
924*/
925QQmlListProperty<QObject> QQuickViewTransitionAttached::targetItems()
926{
927 return QQmlListProperty<QObject>(this, &m_targetItems);
928}
929
930QQuickViewTransitionAttached *QQuickViewTransitionAttached::qmlAttachedProperties(QObject *obj)
931{
932 return new QQuickViewTransitionAttached(obj);
933}
934
935QT_END_NAMESPACE
936
937#include "moc_qquickitemviewtransition_p.cpp"
938

source code of qtdeclarative/src/quick/items/qquickitemviewtransition.cpp