aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/qquickitemviewtransition.cpp
blob: 823269bfc11bd8224b58d2e2f8be10c630497631 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
/****************************************************************************
**
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
** Contact: http://www.qt-project.org/
**
** This file is part of the QtQml module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** GNU Lesser General Public License Usage
** This file may be used under the terms of the GNU Lesser General Public
** License version 2.1 as published by the Free Software Foundation and
** appearing in the file LICENSE.LGPL included in the packaging of this
** file. Please review the following information to ensure the GNU Lesser
** General Public License version 2.1 requirements will be met:
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain additional
** rights. These rights are described in the Nokia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU General
** Public License version 3.0 as published by the Free Software Foundation
** and appearing in the file LICENSE.GPL included in the packaging of this
** file. Please review the following information to ensure the GNU General
** Public License version 3.0 requirements will be met:
** http://www.gnu.org/copyleft/gpl.html.
**
** Other Usage
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
**
**
**
**
**
** $QT_END_LICENSE$
**
****************************************************************************/

#include "qquickitemviewtransition_p.h"
#include <QtQuick/qquickitem.h>
#include <QtQuick/private/qquicktransition_p.h>

QT_BEGIN_NAMESPACE

static QList<int> qquickitemviewtransition_emptyIndexes = QList<int>();
static QList<QObject *> qquickitemviewtransition_emptyTargets = QList<QObject *>();


class QQuickItemViewTransitionJob : public QQuickTransitionManager
{
public:
    QQuickItemViewTransitionJob();
    ~QQuickItemViewTransitionJob();

    void startTransition(QQuickItemViewTransitionableItem *item, int index, QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, const QPointF &to, bool isTargetItem);

    QQuickItemViewTransitioner *m_transitioner;
    QQuickItemViewTransitionableItem *m_item;
    QPointF m_toPos;
    QQuickItemViewTransitioner::TransitionType m_type;
    bool m_isTarget;
    bool *m_wasDeleted;

protected:
    virtual void finished();
};


QQuickItemViewTransitionJob::QQuickItemViewTransitionJob()
    : m_transitioner(0)
    , m_item(0)
    , m_type(QQuickItemViewTransitioner::NoTransition)
    , m_isTarget(false)
    , m_wasDeleted(0)
{
}

QQuickItemViewTransitionJob::~QQuickItemViewTransitionJob()
{
    if (m_wasDeleted)
        *m_wasDeleted = true;
    if (m_transitioner)
        m_transitioner->runningJobs.remove(this);
}

void QQuickItemViewTransitionJob::startTransition(QQuickItemViewTransitionableItem *item, int index, QQuickItemViewTransitioner *transitioner, QQuickItemViewTransitioner::TransitionType type, const QPointF &to, bool isTargetItem)
{
    if (type == QQuickItemViewTransitioner::NoTransition)
        return;
    if (!item) {
        qWarning("startTransition(): invalid item");
        return;
    }
    if (!transitioner) {
        qWarning("startTransition(): invalid transitioner");
        return;
    }

    QQuickTransition *trans = transitioner->transitionObject(type, isTargetItem);
    if (!trans) {
        qWarning("QQuickItemView: invalid view transition!");
        return;
    }

    m_item = item;
    m_transitioner = transitioner;
    m_toPos = to;
    m_type = type;
    m_isTarget = isTargetItem;

    QQuickViewTransitionAttached *attached =
            static_cast<QQuickViewTransitionAttached*>(qmlAttachedPropertiesObject<QQuickViewTransitionAttached>(trans));
    if (attached) {
        attached->m_index = index;
        attached->m_item = item->item;
        attached->m_destination = to;
        attached->m_targetIndexes = m_transitioner->targetIndexes(type);
        attached->m_targetItems = m_transitioner->targetItems(type);
        emit attached->indexChanged();
        emit attached->itemChanged();
        emit attached->destinationChanged();
        emit attached->targetIndexesChanged();
        emit attached->targetItemsChanged();
    }

    QQuickStateOperation::ActionList actions;
    actions << QQuickAction(item->item, QLatin1String("x"), QVariant(to.x()));
    actions << QQuickAction(item->item, QLatin1String("y"), QVariant(to.y()));

    m_transitioner->runningJobs << this;
    QQuickTransitionManager::transition(actions, trans, item->item);
}

void QQuickItemViewTransitionJob::finished()
{
    QQuickTransitionManager::finished();

    if (m_transitioner) {
        bool deleted = false;
        m_wasDeleted = &deleted;
        m_transitioner->finishedTransition(this, m_item);
        if (deleted)
            return;
        m_wasDeleted = 0;

        m_transitioner = 0;
    }

    m_item = 0;
    m_toPos.setX(0);
    m_toPos.setY(0);
    m_type = QQuickItemViewTransitioner::NoTransition;
    m_isTarget = false;
}


QQuickItemViewTransitioner::QQuickItemViewTransitioner()
    : populateTransition(0)
    , addTransition(0), addDisplacedTransition(0)
    , moveTransition(0), moveDisplacedTransition(0)
    , removeTransition(0), removeDisplacedTransition(0)
    , displacedTransition(0)
    , changeListener(0)
    , usePopulateTransition(false)
{
}

QQuickItemViewTransitioner::~QQuickItemViewTransitioner()
{
    for (QSet<QQuickItemViewTransitionJob *>::iterator it = runningJobs.begin(); it != runningJobs.end(); ++it)
        (*it)->m_transitioner = 0;
}

bool QQuickItemViewTransitioner::canTransition(QQuickItemViewTransitioner::TransitionType type, bool asTarget) const
{
    if (!asTarget
            && type != NoTransition && type != PopulateTransition
            && displacedTransition && displacedTransition->enabled()) {
        return true;
    }

    switch (type) {
    case NoTransition:
        break;
    case PopulateTransition:
        return usePopulateTransition
                && populateTransition && populateTransition->enabled();
    case AddTransition:
        if (usePopulateTransition)
            return false;
        if (asTarget)
            return addTransition && addTransition->enabled();
        else
            return addDisplacedTransition && addDisplacedTransition->enabled();
    case MoveTransition:
        if (asTarget)
            return moveTransition && moveTransition->enabled();
        else
            return moveDisplacedTransition && moveDisplacedTransition->enabled();
    case RemoveTransition:
        if (asTarget)
            return removeTransition && removeTransition->enabled();
        else
            return removeDisplacedTransition && removeDisplacedTransition->enabled();
    }
    return false;
}

void QQuickItemViewTransitioner::transitionNextReposition(QQuickItemViewTransitionableItem *item, QQuickItemViewTransitioner::TransitionType type, bool isTarget)
{
    item->setNextTransition(type, isTarget);
}

void QQuickItemViewTransitioner::addToTargetLists(QQuickItemViewTransitioner::TransitionType type, QQuickItemViewTransitionableItem *item, int index)
{
    switch (type) {
    case NoTransition:
        break;
    case PopulateTransition:
    case AddTransition:
        addTransitionIndexes << index;
        addTransitionTargets << item->item;
        break;
    case MoveTransition:
        moveTransitionIndexes << index;
        moveTransitionTargets << item->item;
        break;
    case RemoveTransition:
        removeTransitionIndexes << index;
        removeTransitionTargets << item->item;
        break;
    }
}

void QQuickItemViewTransitioner::resetTargetLists()
{
    addTransitionIndexes.clear();
    addTransitionTargets.clear();

    removeTransitionIndexes.clear();
    removeTransitionTargets.clear();

    moveTransitionIndexes.clear();
    moveTransitionTargets.clear();
}

QQuickTransition *QQuickItemViewTransitioner::transitionObject(QQuickItemViewTransitioner::TransitionType type, bool asTarget)
{
    if (type == QQuickItemViewTransitioner::NoTransition)
        return 0;

    if (type == PopulateTransition)
        asTarget = true;    // no separate displaced transition

    QQuickTransition *trans = 0;
    switch (type) {
    case NoTransition:
        break;
    case PopulateTransition:
        trans = populateTransition;
        break;
    case AddTransition:
        trans = asTarget ? addTransition : addDisplacedTransition;
        break;
    case MoveTransition:
        trans = asTarget ? moveTransition : moveDisplacedTransition;
        break;
    case RemoveTransition:
        trans = asTarget ? removeTransition : removeDisplacedTransition;
        break;
    }

    if (!asTarget && (!trans || !trans->enabled()))
        trans = displacedTransition;
    if (trans && trans->enabled())
        return trans;
    return 0;
}

const QList<int> &QQuickItemViewTransitioner::targetIndexes(QQuickItemViewTransitioner::TransitionType type) const
{
    switch (type) {
    case NoTransition:
        break;
    case PopulateTransition:
    case AddTransition:
        return addTransitionIndexes;
    case MoveTransition:
        return moveTransitionIndexes;
    case RemoveTransition:
        return removeTransitionIndexes;
    }

    return qquickitemviewtransition_emptyIndexes;
}

const QList<QObject *> &QQuickItemViewTransitioner::targetItems(QQuickItemViewTransitioner::TransitionType type) const
{
    switch (type) {
    case NoTransition:
        break;
    case PopulateTransition:
    case AddTransition:
        return addTransitionTargets;
    case MoveTransition:
        return moveTransitionTargets;
    case RemoveTransition:
        return removeTransitionTargets;
    }

    return qquickitemviewtransition_emptyTargets;
}

void QQuickItemViewTransitioner::finishedTransition(QQuickItemViewTransitionJob *job, QQuickItemViewTransitionableItem *item)
{
    if (!runningJobs.contains(job))
        return;
    runningJobs.remove(job);
    if (item) {
        item->finishedTransition();
        if (changeListener)
            changeListener->viewItemTransitionFinished(item);
    }
}


QQuickItemViewTransitionableItem::QQuickItemViewTransitionableItem(QQuickItem *i)
    : item(i)
    , transition(0)
    , nextTransitionType(QQuickItemViewTransitioner::NoTransition)
    , isTransitionTarget(false)
    , nextTransitionToSet(false)
    , nextTransitionFromSet(false)
    , lastMovedToSet(false)
    , prepared(false)
{
}

QQuickItemViewTransitionableItem::~QQuickItemViewTransitionableItem()
{
    delete transition;
}

qreal QQuickItemViewTransitionableItem::itemX() const
{
    if (nextTransitionType != QQuickItemViewTransitioner::NoTransition)
        return nextTransitionToSet ? nextTransitionTo.x() : item->x();
    else if (transition && transition->isRunning())
        return transition->m_toPos.x();
    else
        return item->x();
}

qreal QQuickItemViewTransitionableItem::itemY() const
{
    // If item is transitioning to some pos, return that dest pos.
    // If item was redirected to some new pos before the current transition finished,
    // return that new pos.
    if (nextTransitionType != QQuickItemViewTransitioner::NoTransition)
        return nextTransitionToSet ? nextTransitionTo.y() : item->y();
    else if (transition && transition->isRunning())
        return transition->m_toPos.y();
    else
        return item->y();
}

void QQuickItemViewTransitionableItem::moveTo(const QPointF &pos, bool immediate)
{
    if (!nextTransitionFromSet && nextTransitionType != QQuickItemViewTransitioner::NoTransition) {
        nextTransitionFrom = item->pos();
        nextTransitionFromSet = true;
    }

    lastMovedTo = pos;
    lastMovedToSet = true;

    if (immediate || !transitionScheduledOrRunning()) {
        if (immediate)
            stopTransition();
        item->setPos(pos);
    } else {
        nextTransitionTo = pos;
        nextTransitionToSet = true;
    }
}

bool QQuickItemViewTransitionableItem::transitionScheduledOrRunning() const
{
    return (transition && transition->isRunning())
            || nextTransitionType != QQuickItemViewTransitioner::NoTransition;
}

bool QQuickItemViewTransitionableItem::transitionRunning() const
{
    return (transition && transition->isRunning());
}

bool QQuickItemViewTransitionableItem::isPendingRemoval() const
{
    if (nextTransitionType == QQuickItemViewTransitioner::RemoveTransition)
        return isTransitionTarget;
    if (transition && transition->isRunning() && transition->m_type == QQuickItemViewTransitioner::RemoveTransition)
        return transition->m_isTarget;
    return false;
}

bool QQuickItemViewTransitionableItem::prepareTransition(QQuickItemViewTransitioner *transitioner, int index, const QRectF &viewBounds)
{
    if (nextTransitionType == QQuickItemViewTransitioner::NoTransition)
        return false;

    if (isTransitionTarget) {
        // If item is not already moving somewhere, set it to not move anywhere.
        // This ensures that removed targets don't transition to the default (0,0) and that
        // items set for other transition types only transition if they actually move somewhere.
        if (!nextTransitionToSet)
            moveTo(item->pos());
    } else {
        // don't start displaced transitions that don't move anywhere
        if (!nextTransitionToSet || (nextTransitionFromSet && nextTransitionFrom == nextTransitionTo)) {
            clearCurrentScheduledTransition();
            return false;
        }
    }

    bool doTransition = false;

    // For move transitions (both target and displaced) and displaced transitions of other
    // types, only run the transition if the item is actually moving to another position.
    switch (nextTransitionType) {
    case QQuickItemViewTransitioner::NoTransition:
    {
        return false;
    }
    case QQuickItemViewTransitioner::PopulateTransition:
    {
        doTransition = true;
        break;
    }
    case QQuickItemViewTransitioner::AddTransition:
    case QQuickItemViewTransitioner::RemoveTransition:
        if (viewBounds.isNull()) {
            if (isTransitionTarget)
                doTransition = true;
            else
                doTransition = transitionWillChangePosition();
        } else if (isTransitionTarget) {
            // For Add targets, do transition if item is moving into visible area
            // For Remove targets, do transition if item is currently in visible area
            doTransition = (nextTransitionType == QQuickItemViewTransitioner::AddTransition)
                    ? viewBounds.intersects(QRectF(nextTransitionTo.x(), nextTransitionTo.y(), item->width(), item->height()))
                    : viewBounds.intersects(QRectF(item->x(), item->y(), item->width(), item->height()));
        } else {
            // do transition if moving from or into visible area
            if (viewBounds.intersects(QRectF(item->x(), item->y(), item->width(), item->height()))
                    || viewBounds.intersects(QRectF(nextTransitionTo.x(), nextTransitionTo.y(), item->width(), item->height()))) {
                doTransition = transitionWillChangePosition();
            }
        }
        break;
    case QQuickItemViewTransitioner::MoveTransition:
        // do transition if moving from or into visible area
        if (transitionWillChangePosition()) {
            doTransition = viewBounds.isNull()
                    || viewBounds.intersects(QRectF(item->x(), item->y(), item->width(), item->height()))
                    || viewBounds.intersects(QRectF(nextTransitionTo.x(), nextTransitionTo.y(), item->width(), item->height()));
        }
        break;
    }

    if (doTransition) {
        // add item to target lists even if canTransition() is false for a target transition,
        // since the target lists still need to be filled for displaced transitions
        if (isTransitionTarget)
            transitioner->addToTargetLists(nextTransitionType, this, index);
        doTransition = transitioner->canTransition(nextTransitionType, isTransitionTarget);
    }

    if (!doTransition) {
        // if transition type is not valid, the previous transition still has to be
        // canceled so that the item can move immediately to the right position
        item->setPos(nextTransitionTo);
        stopTransition();
    }

    prepared = true;
    return doTransition;
}

void QQuickItemViewTransitionableItem::startTransition(QQuickItemViewTransitioner *transitioner, int index)
{
    if (nextTransitionType == QQuickItemViewTransitioner::NoTransition)
        return;

    if (!prepared) {
        qWarning("QQuickViewItem::prepareTransition() not called!");
        return;
    }

    if (!transition || transition->m_type != nextTransitionType || transition->m_isTarget != isTransitionTarget) {
        delete transition;
        transition = new QQuickItemViewTransitionJob;
    }

    transition->startTransition(this, index, transitioner, nextTransitionType, nextTransitionTo, isTransitionTarget);
    clearCurrentScheduledTransition();
}

void QQuickItemViewTransitionableItem::setNextTransition(QQuickItemViewTransitioner::TransitionType type, bool isTargetItem)
{
    // Don't reset nextTransitionToSet - once it is set, it cannot be changed
    // until the animation finishes since the itemX() and itemY() may be used
    // to calculate positions for transitions for other items in the view.
    nextTransitionType = type;
    isTransitionTarget = isTargetItem;

    if (!nextTransitionFromSet && lastMovedToSet) {
        nextTransitionFrom = lastMovedTo;
        nextTransitionFromSet = true;
    }
}

bool QQuickItemViewTransitionableItem::transitionWillChangePosition() const
{
    if (transitionRunning() && transition->m_toPos != nextTransitionTo)
        return true;
    if (!nextTransitionFromSet)
        return false;
    return nextTransitionTo != nextTransitionFrom;
}

void QQuickItemViewTransitionableItem::resetNextTransitionPos()
{
    nextTransitionToSet = false;
    nextTransitionTo = QPointF();
}

void QQuickItemViewTransitionableItem::finishedTransition()
{
    resetNextTransitionPos();
}

void QQuickItemViewTransitionableItem::clearCurrentScheduledTransition()
{
    // Just clear the current scheduled transition - don't touch the nextTransitionTo
    // which may have already been set for a previously scheduled transition

    nextTransitionType = QQuickItemViewTransitioner::NoTransition;
    isTransitionTarget = false;
    prepared = false;
    nextTransitionFromSet = false;
}

void QQuickItemViewTransitionableItem::stopTransition()
{
    if (transition)
        transition->cancel();
    clearCurrentScheduledTransition();
    resetNextTransitionPos();
}


QQuickViewTransitionAttached::QQuickViewTransitionAttached(QObject *parent)
    : QObject(parent), m_item(0), m_index(-1)
{
}
/*!
    \qmlclass ViewTransition QQuickViewTransitionAttached
    \inqmlmodule QtQuick 2
    \ingroup qml-view-elements
    \brief The ViewTransition attached property provides details on items under transition in a view.

    With ListView and GridView, it is possible to specify transitions that should be applied whenever
    the items in the view change as a result of modifications to the view's model. They both have the
    following properties that can be set to the appropriate transitions to be run for various
    operations:

    \list
    \o \c populate - the transition to run when a view is created, or when the model changes
    \o \c add - the transition to apply to items that are added to the view
    \o \c remove - the transition to apply to items that are removed from the view
    \o \c move - the transition to apply to items that are moved within the view (i.e. as a result
       of a move operation in the model)
    \o \c displaced - the generic transition to be applied to any items that are displaced by an
       add, move or remove operation
    \o \c addDisplaced, \c removeDisplaced and \c moveDisplaced - the transitions to be applied when
       items are displaced by add, move, or remove operations, respectively (these override the
       generic displaced transition if specified)
    \endlist

    For the \l Row, \l Column, \l Grid and \l Flow positioner elements, which operate with collections of child
    items rather than data models, the following properties are used instead:

    \list
    \o \c add - the transition to apply to items that are created for the positioner, added to
       or reparented to the positioner, or items that have become \l {Item::}{visible}
    \o \c move - the transition to apply to items that have moved within the positioner, including
       when they are displaced due to the addition or removal of other items, or when items are otherwise
       rearranged within the positioner, or when items are repositioned due to the resizing of other
       items in the positioner
    \endlist

    View transitions have access to a ViewTransition attached property that
    provides details of the items that are under transition and the operation that triggered the
    transition. Since view transitions are run once per item, these details can be used to customise
    each transition for each individual item.

    The ViewTransition attached property provides the following properties specific to the item to
    which the transition is applied:

    \list
    \o ViewTransition.item - the item that is under transition
    \o ViewTransition.index - the index of this item
    \o ViewTransition.destination - the (x,y) point to which this item is moving for the relevant view operation
    \endlist

    In addition, ViewTransition provides properties specific to the items which are the target
    of the operation that triggered the transition:

    \list
    \o ViewTransition.targetIndexes - the indexes of the target items
    \o ViewTransition.targetItems - the target items themselves
    \endlist

    (Note that for the \l Row, \l Column, \l Grid and \l Flow positioner elements, the \c move transition only
    provides these two additional details when the transition is triggered by the addition of items
    to a positioner.)

    View transitions can be written without referring to any of the attributes listed
    above. These attributes merely provide extra details that are useful for customising view
    transitions.

    Following is an introduction to view transitions and the ways in which the ViewTransition
    attached property can be used to augment view transitions.


    \section2 View transitions: a simple example

    Here is a basic example of the use of view transitions. The view below specifies transitions for
    the \c add and \c displaced properties, which will be run when items are added to the view:

    \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-basic.qml 0

    When the space key is pressed, adding an item to the model, the new item will fade in and
    increase in scale over 400 milliseconds as it is added to the view. Also, any item that is
    displaced by the addition of a new item will animate to its new position in the view over
    400 milliseconds, as specified by the \c displaced transition.

    If five items were inserted in succession at index 0, the effect would be this:

    \image viewtransitions-basic.gif

    Notice that the NumberAnimation objects above do not need to specify a \c target to animate
    the appropriate item. Also, the NumberAnimation in the \c addTransition does not need to specify
    the \c to value to move the item to its correct position in the view. This is because the view
    implicitly sets the \c target and \c to values with the correct item and final item position
    values if these properties are not explicitly defined.

    At its simplest, a view transition may just animate an item to its new position following a
    view operation, just as the \c displaced transition does above, or animate some item properties,
    as in the \c add transition above. Additionally, a view transition may make use of the
    ViewTransition attached property to customise animation behavior for different items. Following
    are some examples of how this can be achieved.


    \section2 Using the ViewTransition attached property

    As stated, the various ViewTransition properties provide details specific to the individual item
    being transitioned as well as the operation that triggered the transition. In the animation above,
    five items are inserted in succession at index 0. When the fifth and final insertion takes place,
    adding "Item 4" to the view, the \c add transition is run once (for the inserted item) and the
    \c displaced transition is run four times (once for each of the four existing items in the view).

    At this point, if we examined the \c displaced transition that was run for the bottom displaced
    item ("Item 0"), the ViewTransition property values provided to this transition would be as follows:

    \table
    \header
        \o Property
        \o Value
        \o Explanation
    \row
        \o ViewTransition.item
        \o "Item 0" delegate instance
        \o The "Item 0" \l Rectangle object itself
    \row
        \o ViewTransition.index
        \o \c int value of 4
        \o The index of "Item 0" within the model following the add operation
    \row
        \o ViewTransition.destination
        \o \l point value of (0, 120)
        \o The position that "Item 0" is moving to
    \row
        \o ViewTransition.targetIndexes
        \o \c int array, just contains the integer "0" (zero)
        \o The index of "Item 4", the new item added to the view
    \row
        \o ViewTransition.targetItems
        \o object array, just contains the "Item 4" delegate instance
        \o The "Item 4" \l Rectangle object - the new item added to the view
    \endtable

    The ViewTransition.targetIndexes and ViewTransition.targetItems lists provide the items and
    indexes of all delegate instances that are the targets of the relevant operation. For an add
    operation, these are all the items that are added into the view; for a remove, these are all
    the items removed from the view, and so on. (Note these lists will only contain references to
    items that have been created within the view or its cached items; targets that are not within
    the visible area of the view or within the item cache will not be accessible.)

    So, while the ViewTransition.item, ViewTransition.index and ViewTransition.destination values
    vary for each individual transition that is run, the ViewTransition.targetIndexes and
    ViewTransition.targetItems values are the same for every \c add and \c displaced transition
    that is triggered by a particular add operation.


    \section3 Delaying animations based on index

    Since each view transition is run once for each item affected by the transition, the ViewTransition
    properties can be used within a transition to define custom behavior for each item's transition.
    For example, the ListView in the previous example could use this information to create a ripple-type
    effect on the movement of the displaced items.

    This can be achieved by modifying the \c displaced transition so that it delays the animation of
    each displaced item based on the difference between its index (provided by ViewTransition.index)
    and the first removed index (provided by ViewTransition.targetIndexes):

    \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-delayedbyindex.qml 0

    Each displaced item delays its animation by an additional 100 milliseconds, producing a subtle
    ripple-type effect when items are displaced by the add, like this:

    \image viewtransitions-delayedbyindex.gif


    \section3 Animating items to intermediate positions

    The ViewTransition.item property gives a reference to the item to which the transition is being
    applied. This can be used to access any of the item's attributes, custom \c property values,
    and so on.

    Below is a modification of the \c displaced transition from the previous example. It adds a
    ParallelAnimation with nested NumberAnimation objects that reference ViewTransition.item to access
    each item's \c x and \c y values at the start of their transitions. This allows each item to
    animate to an intermediate position relative to its starting point for the transition, before
    animating to its final position in the view:

    \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-intermediatemove.qml 0

    Now, a displaced item will first move to a position of (20, 50) relative to its starting
    position, and then to its final, correct position in the view:

    \image viewtransitions-intermediatemove.gif

    Since the final NumberAnimation does not specify a \c to value, the view implicitly sets this
    value to the item's final position in the view, and so this last animation will move this item
    to the correct place. If the transition requires the final position of the item for some calculation,
    this is accessible through ViewTransition.destination.

    Instead of using multiple NumberAnimations, you could use a PathAnimation to animate an item over
    a curved path. For example, the \c add transition in the previous example could be augmented with
    a PathAnimation as follows: to animate newly added items along a path:

    \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-pathanim.qml 0

    This animates newly added items along a path. Notice that each path is specified relative to
    each item's final destination point, so that items inserted at different indexes start their
    paths from different positions:

    \image viewtransitions-pathanim.gif


    \section2 Handling interrupted animations

    A view transition may be interrupted at any time if a different view transition needs to be
    applied while the original transition is in progress. For example, say Item A is inserted at index 0
    and undergoes an "add" transition; then, Item B is inserted at index 0 in quick succession before
    Item A's transition has finished. Since Item B is inserted before Item A, it will displace Item
    A, causing the view to interrupt Item A's "add" transition mid-way and start a "displaced"
    transition on Item A instead.

    For simple animations that simply animate an item's movement to its final destination, this
    interruption is unlikely to require additional consideration. However, if a transition changes other
    properties, this interruption may cause unwanted side effects. Consider the first example on this
    page, repeated below for convenience:

    \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-basic.qml 0

    If multiple items are added in rapid succession, without waiting for a previous transition
    to finish, this is the result:

    \image viewtransitions-interruptedbad.gif

    Each newly added item undergoes an \c add transition, but before the transition can finish,
    another item is added, displacing the previously added item. Because of this, the \c add
    transition on the previously added item is interrupted and a \c displaced transition is
    started on the item instead. Due to the interruption, the \c opacity and \c scale animations
    have not completed, thus producing items with opacity and scale that are below 1.0.

    To fix this, the \c displaced transition should additionally ensure the item properties are
    set to the end values specified in the \c add transition, effectively resetting these values
    whenever an item is displaced. In this case, it means setting the item opacity and scale to 1.0:

    \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-interruptedgood.qml 0

    Now, when an item's \c add transition is interrupted, its opacity and scale are animated to 1.0
    upon displacement, avoiding the erroneous visual effects from before:

    \image viewtransitions-interruptedgood.gif

    The same principle applies to any combination of view transitions. An added item may be moved
    before its add transition finishes, or a moved item may be removed before its moved transition
    finishes, and so on; so, the rule of thumb is that every transition should handle the same set of
    properties.


    \section2 Restrictions regarding ScriptAction

    When a view transition is initialized, any property bindings that refer to the ViewTransition
    attached property are evaluated in preparation for the transition. Due to the nature of the
    internal construction of a view transition, the attributes of the ViewTransition attached
    property are only valid for the relevant item when the transition is initialized, and may not be
    valid when the transition is actually run.

    Therefore, a ScriptAction within a view transition should not refer to the ViewTransition
    attached property, as it may not refer to the expected values at the time that the ScriptAction
    is actually invoked. Consider the following example:

    \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-scriptactionbad.qml 0

    When the space key is pressed, three items are moved from index 5 to index 1. For each moved
    item, the \c moveTransition sequence presumably animates the item's color to "yellow", then
    animates it to its final position, then changes the item color back to "lightsteelblue" using a
    ScriptAction. However, when run, the transition does not produce the intended result:

    \image viewtransitions-scriptactionbad.gif

    Only the last moved item is returned to the "lightsteelblue" color; the others remain yellow. This
    is because the ScriptAction is not run until after the transition has already been initialized, by
    which time the ViewTransition.item value has changed to refer to a different item; the item that
    the script had intended to refer to is not the one held by ViewTransition.item at the time the
    ScriptAction is actually invoked.

    In this instance, to avoid this issue, the view could set the property using a PropertyAction
    instead:

    \snippet doc/src/snippets/declarative/viewtransitions/viewtransitions-scriptactiongood.qml 0

    When the transition is initialized, the PropertyAction \c target will be set to the respective
    ViewTransition.item for the transition and will later run with the correct item target as
    expected.
  */

/*!
    \qmlattachedproperty list QtQuick2::ViewTransition::index

    This attached property holds the index of the item that is being
    transitioned.

    Note that if the item is being moved, this property holds the index that
    the item is moving to, not from.
*/

/*!
    \qmlattachedproperty list QtQuick2::ViewTransition::item

    This attached property holds the the item that is being transitioned.

    \warning This item should not be kept and referred to outside of the transition
    as it may become invalid as the view changes.
*/

/*!
    \qmlattachedproperty list QtQuick2::ViewTransition::destination

    This attached property holds the final destination position for the transitioned
    item within the view.

    This property value is a \l point with \c x and \c y properties.
*/

/*!
    \qmlattachedproperty list QtQuick2::ViewTransition::targetIndexes

    This attached property holds a list of the indexes of the items in view
    that are the target of the relevant operation.

    The targets are the items that are the subject of the operation. For
    an add operation, these are the items being added; for a remove, these
    are the items being removed; for a move, these are the items being
    moved.

    For example, if the transition was triggered by an insert operation
    that added two items at index 1 and 2, this targetIndexes list would
    have the value [1,2].

    \note The targetIndexes list only contains the indexes of items that are actually
    in view, or will be in the view once the relevant operation completes.

    \sa QtQuick2::ViewTransition::targetIndexes
*/

/*!
    \qmlattachedproperty list QtQuick2::ViewTransition::targetItems

    This attached property holds the list of items in view that are the
    target of the relevant operation.

    The targets are the items that are the subject of the operation. For
    an add operation, these are the items being added; for a remove, these
    are the items being removed; for a move, these are the items being
    moved.

    For example, if the transition was triggered by an insert operation
    that added two items at index 1 and 2, this targetItems list would
    contain these two items.

    \note The targetItems list only contains items that are actually
    in view, or will be in the view once the relevant operation completes.

    \warning The objects in this list should not be kept and referred to
    outside of the transition as the items may become invalid. The targetItems
    are only valid when the Transition is initially created; this also means
    they should not be used by ScriptAction objects in the Transition, which are
    not evaluated until the transition is run.

    \sa QtQuick2::ViewTransition::targetIndexes
*/
QQmlListProperty<QObject> QQuickViewTransitionAttached::targetItems()
{
    return QQmlListProperty<QObject>(this, m_targetItems);
}

QQuickViewTransitionAttached *QQuickViewTransitionAttached::qmlAttachedProperties(QObject *obj)
{
    return new QQuickViewTransitionAttached(obj);
}

QT_END_NAMESPACE