Add generic "displaced" transition property
authorBea Lam <bea.lam@nokia.com>
Wed, 22 Feb 2012 07:23:47 +0000 (17:23 +1000)
committerQt by Nokia <qt-info@nokia.com>
Wed, 29 Feb 2012 00:20:00 +0000 (01:20 +0100)
This is the default displaced transition that will be applied if addDisplaced,
removeDisplaced or moveDisplaced are not specified (or are disabled).

Change-Id: I9356036dc93bd9cb26e64e0b1769228113b74273
Reviewed-by: Martin Jones <martin.jones@nokia.com>
18 files changed:
doc/src/snippets/declarative/viewtransitions/viewtransitions-basic.qml
doc/src/snippets/declarative/viewtransitions/viewtransitions-delayedbyindex.qml
doc/src/snippets/declarative/viewtransitions/viewtransitions-intermediatemove.qml
doc/src/snippets/declarative/viewtransitions/viewtransitions-interruptedgood.qml
doc/src/snippets/declarative/viewtransitions/viewtransitions-pathanim.qml
doc/src/snippets/declarative/viewtransitions/viewtransitions-scriptactionbad.qml
doc/src/snippets/declarative/viewtransitions/viewtransitions-scriptactiongood.qml
src/quick/items/qquickgridview.cpp
src/quick/items/qquickitemview.cpp
src/quick/items/qquickitemview_p.h
src/quick/items/qquickitemviewtransition.cpp
src/quick/items/qquickitemviewtransition_p.h
src/quick/items/qquicklistview.cpp
tests/auto/qtquick2/qquickgridview/data/displacedTransitions.qml [new file with mode: 0644]
tests/auto/qtquick2/qquickgridview/tst_qquickgridview.cpp
tests/auto/qtquick2/qquicklistview/data/displacedTransitions.qml [new file with mode: 0644]
tests/auto/qtquick2/qquicklistview/tst_qquicklistview.cpp
tests/auto/qtquick2/shared/viewtestutil.h

index 8a05491..cb94acb 100644 (file)
@@ -60,7 +60,7 @@ ListView {
         NumberAnimation { property: "scale"; from: 0; to: 1.0; duration: 400 }
     }
 
-    addDisplaced: Transition {
+    displaced: Transition {
         NumberAnimation { properties: "x,y"; duration: 400; easing.type: Easing.OutBounce }
     }
 
index 79d00d2..84c4848 100644 (file)
@@ -60,12 +60,12 @@ ListView {
     }
 
 //! [0]
-    addDisplaced: Transition {
-        id: addDispTrans
+    displaced: Transition {
+        id: dispTrans
         SequentialAnimation {
             PauseAnimation {
-                duration: (addDispTrans.ViewTransition.index -
-                        addDispTrans.ViewTransition.targetIndexes[0]) * 100
+                duration: (dispTrans.ViewTransition.index -
+                        dispTrans.ViewTransition.targetIndexes[0]) * 100
             }
             NumberAnimation { properties: "x,y"; duration: 400; easing.type: Easing.OutBounce }
         }
index a43d3a8..89353b4 100644 (file)
@@ -60,20 +60,20 @@ ListView {
     }
 
 //! [0]
-    addDisplaced: Transition {
-        id: addDispTrans
+    displaced: Transition {
+        id: dispTrans
         SequentialAnimation {
             PauseAnimation {
-                duration: (addDispTrans.ViewTransition.index -
-                        addDispTrans.ViewTransition.targetIndexes[0]) * 100
+                duration: (dispTrans.ViewTransition.index -
+                        dispTrans.ViewTransition.targetIndexes[0]) * 100
             }
             ParallelAnimation {
                 NumberAnimation {
-                    property: "x"; to: addDispTrans.ViewTransition.item.x + 20
+                    property: "x"; to: dispTrans.ViewTransition.item.x + 20
                     easing.type: Easing.OutQuad
                 }
                 NumberAnimation {
-                    property: "y"; to: addDispTrans.ViewTransition.item.y + 50
+                    property: "y"; to: dispTrans.ViewTransition.item.y + 50
                     easing.type: Easing.OutQuad
                 }
             }
index 1355268..0644caa 100644 (file)
@@ -60,7 +60,7 @@ ListView {
     }
 
 //! [0]
-    addDisplaced: Transition {
+    displaced: Transition {
         NumberAnimation { properties: "x,y"; duration: 400; easing.type: Easing.OutBounce }
 
         // ensure opacity and scale values return to 1.0
index 3d075c4..4b16857 100644 (file)
@@ -76,20 +76,20 @@ ListView {
     }
 //! [0]
 
-    addDisplaced: Transition {
-        id: addDispTrans
+    displaced: Transition {
+        id: dispTrans
         SequentialAnimation {
             PauseAnimation {
-                duration: (addDispTrans.ViewTransition.index -
-                        addDispTrans.ViewTransition.targetIndexes[0]) * 100
+                duration: (dispTrans.ViewTransition.index -
+                        dispTrans.ViewTransition.targetIndexes[0]) * 100
             }
             ParallelAnimation {
                 NumberAnimation {
-                    property: "x"; to: addDispTrans.ViewTransition.item.x + 20
+                    property: "x"; to: dispTrans.ViewTransition.item.x + 20
                     easing.type: Easing.OutQuad
                 }
                 NumberAnimation {
-                    property: "y"; to: addDispTrans.ViewTransition.item.y + 50
+                    property: "y"; to: dispTrans.ViewTransition.item.y + 50
                     easing.type: Easing.OutQuad
                 }
             }
index 0334835..0e7d1e8 100644 (file)
@@ -70,7 +70,7 @@ ListView {
         }
     }
 
-    moveDisplaced: Transition {
+    displaced: Transition {
         NumberAnimation { properties: "x,y"; duration: 400; easing.type: Easing.OutBounce }
     }
 
index eda5c35..7fa7e48 100644 (file)
@@ -73,7 +73,7 @@ ListView {
     }
 //! [0]
 
-    moveDisplaced: Transition {
+    displaced: Transition {
         NumberAnimation { properties: "x,y"; duration: 400; easing.type: Easing.OutBounce }
     }
 
index fbce0af..8b9f805 100644 (file)
@@ -1634,8 +1634,8 @@ void QQuickGridView::setSnapMode(SnapMode mode)
     Whenever an item is added to the above view, the item will be animated from the position (100,100)
     to its final x,y position within the view, over one second. The transition only applies to
     the new items that are added to the view; it does not apply to the items below that are
-    displaced by the addition of the new items. To animate the displaced items, set the \l
-    addDisplaced property.
+    displaced by the addition of the new items. To animate the displaced items, set the \l displaced
+    or \l addDisplaced properties.
 
     For more details and examples on how to use view transitions, see the ViewTransition
     documentation.
@@ -1671,9 +1671,10 @@ void QQuickGridView::setSnapMode(SnapMode mode)
     the new item that has been added to the view; to animate the added items, set the \l add
     property.
 
-    If an item is displaced by multiple types of operations at the same time, it is not
-    defined as to whether the addDisplaced, moveDisplaced or removeDisplaced transition
-    will be applied.
+    If an item is displaced by multiple types of operations at the same time, it is not defined as to
+    whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
+    if it is not necessary to specify different transitions depending on whether an item is displaced
+    by an add, move or remove operation, consider setting the \l displaced property instead.
 
     For more details and examples on how to use view transitions, see the ViewTransition
     documentation.
@@ -1682,7 +1683,7 @@ void QQuickGridView::setSnapMode(SnapMode mode)
     populated, or when the view's \l model changes. In those cases, the \l populate transition is
     applied instead.
 
-    \sa add, populate, ViewTransition
+    \sa displaced, add, populate, ViewTransition
 */
 /*!
     \qmlproperty Transition QtQuick2::GridView::move
@@ -1705,7 +1706,7 @@ void QQuickGridView::setSnapMode(SnapMode mode)
     respective items in the view will be animated to their new positions in the view over one
     second. The transition only applies to the items that are the subject of the move operation
     in the model; it does not apply to items below them that are displaced by the move operation.
-    To animate the displaced items, set the \l moveDisplaced property.
+    To animate the displaced items, set the \l displaced or \l moveDisplaced properties.
 
     For more details and examples on how to use view transitions, see the ViewTransition
     documentation.
@@ -1738,14 +1739,15 @@ void QQuickGridView::setSnapMode(SnapMode mode)
     the items that are the actual subjects of the move operation; to animate the moved items, set
     the \l move property.
 
-    If an item is displaced by multiple types of operations at the same time, it is not
-    defined as to whether the addDisplaced, moveDisplaced or removeDisplaced transition
-    will be applied.
+    If an item is displaced by multiple types of operations at the same time, it is not defined as to
+    whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
+    if it is not necessary to specify different transitions depending on whether an item is displaced
+    by an add, move or remove operation, consider setting the \l displaced property instead.
 
     For more details and examples on how to use view transitions, see the ViewTransition
     documentation.
 
-    \sa move, ViewTransition
+    \sa displaced, move, ViewTransition
 */
 
 /*!
@@ -1770,8 +1772,8 @@ void QQuickGridView::setSnapMode(SnapMode mode)
     Whenever an item is removed from the above view, the item will be animated to the position (100,100)
     over one second, and in parallel will also change its opacity to 0. The transition
     only applies to the items that are removed from the view; it does not apply to the items below
-    them that are displaced by the removal of the items. To animate the displaced items, set the \l
-    removeDisplaced property.
+    them that are displaced by the removal of the items. To animate the displaced items, set the
+    \l displaced or \l removeDisplaced properties.
 
     Note that by the time the transition is applied, the item has already been removed from the
     model; any references to the model data for the removed index will not be valid.
@@ -1809,15 +1811,53 @@ void QQuickGridView::setSnapMode(SnapMode mode)
     the item that has actually been removed from the view; to animate the removed items, set the
     \l remove property.
 
-    If an item is displaced by multiple types of operations at the same time, it is not
-    defined as to whether the addDisplaced, moveDisplaced or removeDisplaced transition
-    will be applied.
+    If an item is displaced by multiple types of operations at the same time, it is not defined as to
+    whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
+    if it is not necessary to specify different transitions depending on whether an item is displaced
+    by an add, move or remove operation, consider setting the \l displaced property instead.
 
     For more details and examples on how to use view transitions, see the ViewTransition
     documentation.
 
-    \sa remove, ViewTransition
+    \sa displaced, remove, ViewTransition
 */
+
+/*!
+    \qmlproperty Transition QtQuick2::GridView::displaced
+    This property holds the generic transition to apply to items that have been displaced by
+    any model operation that affects the view.
+
+    This is a convenience for specifying a generic transition for items that are displaced
+    by add, move or remove operations, without having to specify the individual addDisplaced,
+    moveDisplaced and removeDisplaced properties. For example, here is a view that specifies
+    a displaced transition:
+
+    \code
+    GridView {
+        ...
+        displaced: Transition {
+            NumberAnimation { properties: "x,y"; duration: 1000 }
+        }
+    }
+    \endcode
+
+    When any item is added, moved or removed within the above view, the items below it are
+    displaced, causing them to move down (or sideways, if horizontally orientated) within the
+    view. As this displacement occurs, the items' movement to their new x,y positions within
+    the view will be animated by a NumberAnimation over one second, as specified.
+
+    If a view specifies this generic displaced transition as well as a specific addDisplaced,
+    moveDisplaced or removeDisplaced transition, the more specific transition will be used
+    instead of the generic displaced transition when the relevant operation occurs, providing that
+    the more specific transition has not been disabled (by setting \l {Transition::enabled}{enabled}
+    to false). If it has indeed been disabled, the generic displaced transition is applied instead.
+
+    For more details and examples on how to use view transitions, see the ViewTransition
+    documentation.
+
+    \sa addDisplaced, moveDisplaced, removeDisplaced, ViewTransition
+*/
+
 void QQuickGridView::viewportMoved()
 {
     Q_D(QQuickGridView);
index 481a0d4..600b72d 100644 (file)
@@ -705,6 +705,22 @@ void QQuickItemView::setRemoveDisplacedTransition(QDeclarativeTransition *transi
     }
 }
 
+QDeclarativeTransition *QQuickItemView::displacedTransition() const
+{
+    Q_D(const QQuickItemView);
+    return d->transitioner ? d->transitioner->displacedTransition : 0;
+}
+
+void QQuickItemView::setDisplacedTransition(QDeclarativeTransition *transition)
+{
+    Q_D(QQuickItemView);
+    d->createTransitioner();
+    if (d->transitioner->displacedTransition != transition) {
+        d->transitioner->displacedTransition = transition;
+        emit displacedTransitionChanged();
+    }
+}
+
 void QQuickItemViewPrivate::positionViewAtIndex(int index, int mode)
 {
     Q_Q(QQuickItemView);
index 63262f3..69a3d4a 100644 (file)
@@ -83,6 +83,7 @@ class Q_AUTOTEST_EXPORT QQuickItemView : public QQuickFlickable
     Q_PROPERTY(QDeclarativeTransition *moveDisplaced READ moveDisplacedTransition WRITE setMoveDisplacedTransition NOTIFY moveDisplacedTransitionChanged)
     Q_PROPERTY(QDeclarativeTransition *remove READ removeTransition WRITE setRemoveTransition NOTIFY removeTransitionChanged)
     Q_PROPERTY(QDeclarativeTransition *removeDisplaced READ removeDisplacedTransition WRITE setRemoveDisplacedTransition NOTIFY removeDisplacedTransitionChanged)
+    Q_PROPERTY(QDeclarativeTransition *displaced READ displacedTransition WRITE setDisplacedTransition NOTIFY displacedTransitionChanged)
 
     Q_PROPERTY(QDeclarativeComponent *highlight READ highlight WRITE setHighlight NOTIFY highlightChanged)
     Q_PROPERTY(QQuickItem *highlightItem READ highlightItem NOTIFY highlightItemChanged)
@@ -151,6 +152,9 @@ public:
     QDeclarativeTransition *removeDisplacedTransition() const;
     void setRemoveDisplacedTransition(QDeclarativeTransition *transition);
 
+    QDeclarativeTransition *displacedTransition() const;
+    void setDisplacedTransition(QDeclarativeTransition *transition);
+
     QDeclarativeComponent *highlight() const;
     void setHighlight(QDeclarativeComponent *);
 
@@ -211,6 +215,7 @@ signals:
     void moveDisplacedTransitionChanged();
     void removeTransitionChanged();
     void removeDisplacedTransitionChanged();
+    void displacedTransitionChanged();
 
     void highlightChanged();
     void highlightItemChanged();
index 3e3a99f..0f092b7 100644 (file)
@@ -90,13 +90,25 @@ void QQuickItemViewTransitionJob::startTransition(QQuickViewItem *item, QQuickIt
         trans = m_transitioner->populateTransition;
         break;
     case QQuickItemViewTransitioner::AddTransition:
-        trans = isTargetItem ? m_transitioner->addTransition : m_transitioner->addDisplacedTransition;
+        if (isTargetItem)
+            trans = m_transitioner->addTransition;
+        else
+            trans = (m_transitioner->addDisplacedTransition && m_transitioner->addDisplacedTransition->enabled()) ?
+                        m_transitioner->addDisplacedTransition : m_transitioner->displacedTransition;
         break;
     case QQuickItemViewTransitioner::MoveTransition:
-        trans = isTargetItem ? m_transitioner->moveTransition : m_transitioner->moveDisplacedTransition;
+        if (isTargetItem)
+            trans = m_transitioner->moveTransition;
+        else
+            trans = (m_transitioner->moveDisplacedTransition && m_transitioner->moveDisplacedTransition->enabled()) ?
+                        m_transitioner->moveDisplacedTransition : m_transitioner->displacedTransition;
         break;
     case QQuickItemViewTransitioner::RemoveTransition:
-        trans = isTargetItem ? m_transitioner->removeTransition : m_transitioner->removeDisplacedTransition;
+        if (isTargetItem)
+            trans = m_transitioner->removeTransition;
+        else
+            trans = (m_transitioner->removeDisplacedTransition && m_transitioner->removeDisplacedTransition->enabled()) ?
+                        m_transitioner->removeDisplacedTransition : m_transitioner->displacedTransition;
         break;
     }
 
@@ -169,6 +181,7 @@ QQuickItemViewTransitioner::QQuickItemViewTransitioner()
     , addTransition(0), addDisplacedTransition(0)
     , moveTransition(0), moveDisplacedTransition(0)
     , removeTransition(0), removeDisplacedTransition(0)
+    , displacedTransition(0)
     , changeListener(0)
     , usePopulateTransition(false)
 {
@@ -176,6 +189,12 @@ QQuickItemViewTransitioner::QQuickItemViewTransitioner()
 
 bool QQuickItemViewTransitioner::canTransition(QQuickItemViewTransitioner::TransitionType type, bool asTarget) const
 {
+    if (!asTarget
+            && type != QQuickItemViewTransitioner::NoTransition && type != QQuickItemViewTransitioner::PopulateTransition
+            && displacedTransition && displacedTransition->enabled()) {
+        return true;
+    }
+
     switch (type) {
     case QQuickItemViewTransitioner::NoTransition:
         break;
@@ -428,11 +447,16 @@ QQuickViewTransitionAttached::QQuickViewTransitionAttached(QObject *parent)
     operations:
 
     \list
-    \o \c add and \c addDisplaced - the transitions to run when items are added to the view
-    \o \c remove and \c removeDisplaced - the transitions to run when items are removed from the view
-    \o \c move and \c moveDisplaced - the transitions to run when items are moved within the view
-       (i.e. as a result of a move operation in the model)
     \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
 
     Such view transitions additionally have access to a ViewTransition attached property that
@@ -468,14 +492,14 @@ QQuickViewTransitionAttached::QQuickViewTransitionAttached(QObject *parent)
     \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 addDisplaced properties, which will be run when items are added to the view:
+    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 addDisplaced transition.
+    400 milliseconds, as specified by the \c displaced transition.
 
     If five items were inserted in succession at index 0, the effect would be this:
 
@@ -488,7 +512,7 @@ QQuickViewTransitionAttached::QQuickViewTransitionAttached(QObject *parent)
     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 addDisplaced transition does above, or animate some item properties,
+    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.
@@ -500,9 +524,9 @@ QQuickViewTransitionAttached::QQuickViewTransitionAttached(QObject *parent)
     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 addDisplaced transition is run four times (once for each of the four existing items in the view).
+    \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 addDisplaced transition that was run for the bottom displaced
+    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
@@ -541,7 +565,7 @@ QQuickViewTransitionAttached::QQuickViewTransitionAttached(QObject *parent)
 
     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 addDisplaced transition
+    ViewTransition.targetItems values are the same for every \c add and \c displaced transition
     that is triggered by a particular add operation.
 
 
@@ -552,7 +576,7 @@ QQuickViewTransitionAttached::QQuickViewTransitionAttached(QObject *parent)
     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 addDisplaced transition so that it delays the animation of
+    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):
 
@@ -570,7 +594,7 @@ QQuickViewTransitionAttached::QQuickViewTransitionAttached(QObject *parent)
     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 addDisplaced transition from the previous example. It adds a
+    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
@@ -607,7 +631,7 @@ QQuickViewTransitionAttached::QQuickViewTransitionAttached(QObject *parent)
     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 an "addDisplaced"
+    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
@@ -624,11 +648,11 @@ QQuickViewTransitionAttached::QQuickViewTransitionAttached(QObject *parent)
 
     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 an \c addDisplaced transition is
+    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 addDisplaced transition should additionally ensure the item properties are
+    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:
 
index c388bed..8dd2f86 100644 (file)
@@ -99,6 +99,7 @@ public:
     QDeclarativeTransition *moveDisplacedTransition;
     QDeclarativeTransition *removeTransition;
     QDeclarativeTransition *removeDisplacedTransition;
+    QDeclarativeTransition *displacedTransition;
 
 private:
     friend class QQuickItemViewTransitionJob;
index 2190952..876859e 100644 (file)
@@ -2301,8 +2301,8 @@ void QQuickListView::setSnapMode(SnapMode mode)
     Whenever an item is added to the above view, the item will be animated from the position (100,100)
     to its final x,y position within the view, over one second. The transition only applies to
     the new items that are added to the view; it does not apply to the items below that are
-    displaced by the addition of the new items. To animate the displaced items, set the \l
-    addDisplaced property.
+    displaced by the addition of the new items. To animate the displaced items, set the \l displaced
+    or \l addDisplaced properties.
 
     For more details and examples on how to use view transitions, see the ViewTransition
     documentation.
@@ -2338,9 +2338,10 @@ void QQuickListView::setSnapMode(SnapMode mode)
     the new item that has been added to the view; to animate the added items, set the \l add
     property.
 
-    If an item is displaced by multiple types of operations at the same time, it is not
-    defined as to whether the addDisplaced, moveDisplaced or removeDisplaced transition
-    will be applied.
+    If an item is displaced by multiple types of operations at the same time, it is not defined as to
+    whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
+    if it is not necessary to specify different transitions depending on whether an item is displaced
+    by an add, move or remove operation, consider setting the \l displaced property instead.
 
     For more details and examples on how to use view transitions, see the ViewTransition
     documentation.
@@ -2349,7 +2350,7 @@ void QQuickListView::setSnapMode(SnapMode mode)
     populated, or when the view's \l model changes. In those cases, the \l populate transition is
     applied instead.
 
-    \sa add, populate, ViewTransition
+    \sa displaced, add, populate, ViewTransition
 */
 
 /*!
@@ -2373,7 +2374,7 @@ void QQuickListView::setSnapMode(SnapMode mode)
     respective items in the view will be animated to their new positions in the view over one
     second. The transition only applies to the items that are the subject of the move operation
     in the model; it does not apply to items below them that are displaced by the move operation.
-    To animate the displaced items, set the \l moveDisplaced property.
+    To animate the displaced items, set the \l displaced or \l moveDisplaced properties.
 
     For more details and examples on how to use view transitions, see the ViewTransition
     documentation.
@@ -2406,14 +2407,15 @@ void QQuickListView::setSnapMode(SnapMode mode)
     the items that are the actual subjects of the move operation; to animate the moved items, set
     the \l move property.
 
-    If an item is displaced by multiple types of operations at the same time, it is not
-    defined as to whether the addDisplaced, moveDisplaced or removeDisplaced transition
-    will be applied.
+    If an item is displaced by multiple types of operations at the same time, it is not defined as to
+    whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
+    if it is not necessary to specify different transitions depending on whether an item is displaced
+    by an add, move or remove operation, consider setting the \l displaced property instead.
 
     For more details and examples on how to use view transitions, see the ViewTransition
     documentation.
 
-    \sa move, ViewTransition
+    \sa displaced, move, ViewTransition
 */
 
 /*!
@@ -2438,8 +2440,8 @@ void QQuickListView::setSnapMode(SnapMode mode)
     Whenever an item is removed from the above view, the item will be animated to the position (100,100)
     over one second, and in parallel will also change its opacity to 0. The transition
     only applies to the items that are removed from the view; it does not apply to the items below
-    them that are displaced by the removal of the items. To animate the displaced items, set the \l
-    removeDisplaced property.
+    them that are displaced by the removal of the items. To animate the displaced items, set the
+    \l displaced or \l removeDisplaced properties.
 
     Note that by the time the transition is applied, the item has already been removed from the
     model; any references to the model data for the removed index will not be valid.
@@ -2477,16 +2479,52 @@ void QQuickListView::setSnapMode(SnapMode mode)
     the item that has actually been removed from the view; to animate the removed items, set the
     \l remove property.
 
-    If an item is displaced by multiple types of operations at the same time, it is not
-    defined as to whether the addDisplaced, moveDisplaced or removeDisplaced transition
-    will be applied.
+    If an item is displaced by multiple types of operations at the same time, it is not defined as to
+    whether the addDisplaced, moveDisplaced or removeDisplaced transition will be applied. Additionally,
+    if it is not necessary to specify different transitions depending on whether an item is displaced
+    by an add, move or remove operation, consider setting the \l displaced property instead.
 
     For more details and examples on how to use view transitions, see the ViewTransition
     documentation.
 
-    \sa remove, ViewTransition
+    \sa displaced, remove, ViewTransition
 */
 
+/*!
+    \qmlproperty Transition QtQuick2::ListView::displaced
+    This property holds the generic transition to apply to items that have been displaced by
+    any model operation that affects the view.
+
+    This is a convenience for specifying the generic transition to be applied to any items
+    that are displaced by an add, move or remove operation, without having to specify the
+    individual addDisplaced, moveDisplaced and removeDisplaced properties. For example, here
+    is a view that specifies a displaced transition:
+
+    \code
+    ListView {
+        ...
+        displaced: Transition {
+            NumberAnimation { properties: "x,y"; duration: 1000 }
+        }
+    }
+    \endcode
+
+    When any item is added, moved or removed within the above view, the items below it are
+    displaced, causing them to move down (or sideways, if horizontally orientated) within the
+    view. As this displacement occurs, the items' movement to their new x,y positions within
+    the view will be animated by a NumberAnimation over one second, as specified.
+
+    If a view specifies this generic displaced transition as well as a specific addDisplaced,
+    moveDisplaced or removeDisplaced transition, the more specific transition will be used
+    instead of the generic displaced transition when the relevant operation occurs, providing that
+    the more specific transition has not been disabled (by setting \l {Transition::enabled}{enabled}
+    to false). If it has indeed been disabled, the generic displaced transition is applied instead.
+
+    For more details and examples on how to use view transitions, see the ViewTransition
+    documentation.
+
+    \sa addDisplaced, moveDisplaced, removeDisplaced, ViewTransition
+*/
 
 void QQuickListView::viewportMoved()
 {
diff --git a/tests/auto/qtquick2/qquickgridview/data/displacedTransitions.qml b/tests/auto/qtquick2/qquickgridview/data/displacedTransitions.qml
new file mode 100644 (file)
index 0000000..d9353c0
--- /dev/null
@@ -0,0 +1,138 @@
+import QtQuick 2.0
+
+Rectangle {
+    id: root
+    width: 500
+    height: 600
+
+    property int duration: 10
+    property int count: grid.count
+
+    Component {
+        id: myDelegate
+        Rectangle {
+            id: wrapper
+
+            property string nameData: name
+
+            objectName: "wrapper"
+            width: 80
+            height: 60
+            Text {
+                text: index
+            }
+            Text {
+                x: 40
+                text: wrapper.x + ", " + wrapper.y
+            }
+            Text {
+                y: 20
+                id: textName
+                objectName: "textName"
+                text: name
+            }
+            Text {
+                y: 40
+                id: textNumber
+                objectName: "textNumber"
+                text: number
+            }
+            color: GridView.isCurrentItem ? "lightsteelblue" : "white"
+
+            onXChanged: checkPos()
+            onYChanged: checkPos()
+
+            function checkPos() {
+                if (Qt.point(x, y) == displaced_transitionVia)
+                    model_displaced_transitionVia.addItem(name, "")
+                if (Qt.point(x, y) == addDisplaced_transitionVia)
+                    model_addDisplaced_transitionVia.addItem(name, "")
+                if (Qt.point(x, y) == moveDisplaced_transitionVia)
+                    model_moveDisplaced_transitionVia.addItem(name, "")
+                if (Qt.point(x, y) == removeDisplaced_transitionVia)
+                    model_removeDisplaced_transitionVia.addItem(name, "")
+            }
+        }
+    }
+
+    GridView {
+        id: grid
+
+        property int targetTransitionsDone
+        property int displaceTransitionsDone
+
+        objectName: "grid"
+        focus: true
+        anchors.centerIn: parent
+        width: 240
+        height: 320
+        cellWidth: 80
+        cellHeight: 60
+        model: testModel
+        delegate: myDelegate
+
+        displaced: useDisplaced ? displaced : null
+        addDisplaced: useAddDisplaced ? addDisplaced : null
+        moveDisplaced: useMoveDisplaced ? moveDisplaced : null
+        removeDisplaced: useRemoveDisplaced ? removeDisplaced : null
+
+        Transition {
+            id: displaced
+            enabled: displacedEnabled
+            SequentialAnimation {
+                ParallelAnimation {
+                    NumberAnimation { properties: "x"; to: displaced_transitionVia.x; duration: root.duration }
+                    NumberAnimation { properties: "y"; to: displaced_transitionVia.y; duration: root.duration }
+                }
+                NumberAnimation { properties: "x,y"; duration: root.duration }
+                PropertyAction { target: grid; property: "displaceTransitionsDone"; value: true }
+            }
+        }
+
+        Transition {
+            id: addDisplaced
+            enabled: addDisplacedEnabled
+            SequentialAnimation {
+                ParallelAnimation {
+                    NumberAnimation { properties: "x"; to: addDisplaced_transitionVia.x; duration: root.duration }
+                    NumberAnimation { properties: "y"; to: addDisplaced_transitionVia.y; duration: root.duration }
+                }
+                NumberAnimation { properties: "x,y"; duration: root.duration }
+                PropertyAction { target: grid; property: "displaceTransitionsDone"; value: true }
+            }
+        }
+
+        Transition {
+            id: moveDisplaced
+            enabled: moveDisplacedEnabled
+            SequentialAnimation {
+                ParallelAnimation {
+                    NumberAnimation { properties: "x"; to: moveDisplaced_transitionVia.x; duration: root.duration }
+                    NumberAnimation { properties: "y"; to: moveDisplaced_transitionVia.y; duration: root.duration }
+                }
+                NumberAnimation { properties: "x,y"; duration: root.duration }
+                PropertyAction { target: grid; property: "displaceTransitionsDone"; value: true }
+            }
+        }
+
+        Transition {
+            id: removeDisplaced
+            enabled: removeDisplacedEnabled
+            SequentialAnimation {
+                ParallelAnimation {
+                    NumberAnimation { properties: "x"; to: removeDisplaced_transitionVia.x; duration: root.duration }
+                    NumberAnimation { properties: "y"; to: removeDisplaced_transitionVia.y; duration: root.duration }
+                }
+                NumberAnimation { properties: "x,y"; duration: root.duration }
+                PropertyAction { target: grid; property: "displaceTransitionsDone"; value: true }
+            }
+        }
+    }
+
+    Rectangle {
+        anchors.fill: grid
+        color: "lightsteelblue"
+        opacity: 0.2
+    }
+}
+
index 5765a67..6d755a6 100644 (file)
@@ -138,6 +138,8 @@ private slots:
     void moveTransitions_data();
     void removeTransitions();
     void removeTransitions_data();
+    void displacedTransitions();
+    void displacedTransitions_data();
     void multipleTransitions();
     void multipleTransitions_data();
 
@@ -4557,6 +4559,215 @@ void tst_QQuickGridView::removeTransitions_data()
             << 18 << 3 << ListRange();
 }
 
+void tst_QQuickGridView::displacedTransitions()
+{
+    QFETCH(bool, useDisplaced);
+    QFETCH(bool, displacedEnabled);
+    QFETCH(bool, useAddDisplaced);
+    QFETCH(bool, addDisplacedEnabled);
+    QFETCH(bool, useMoveDisplaced);
+    QFETCH(bool, moveDisplacedEnabled);
+    QFETCH(bool, useRemoveDisplaced);
+    QFETCH(bool, removeDisplacedEnabled);
+    QFETCH(ListChange, change);
+    QFETCH(ListRange, expectedDisplacedIndexes);
+
+    QaimModel model;
+    for (int i = 0; i < 30; i++)
+        model.addItem("Original item" + QString::number(i), "");
+    QaimModel model_displaced_transitionVia;
+    QaimModel model_addDisplaced_transitionVia;
+    QaimModel model_moveDisplaced_transitionVia;
+    QaimModel model_removeDisplaced_transitionVia;
+
+    QPointF displaced_transitionVia(-50, -100);
+    QPointF addDisplaced_transitionVia(-150, 100);
+    QPointF moveDisplaced_transitionVia(50, -100);
+    QPointF removeDisplaced_transitionVia(150, 100);
+
+    QQuickView *canvas = createView();
+    QDeclarativeContext *ctxt = canvas->rootContext();
+    ctxt->setContextProperty("testModel", &model);
+    ctxt->setContextProperty("model_displaced_transitionVia", &model_displaced_transitionVia);
+    ctxt->setContextProperty("model_addDisplaced_transitionVia", &model_addDisplaced_transitionVia);
+    ctxt->setContextProperty("model_moveDisplaced_transitionVia", &model_moveDisplaced_transitionVia);
+    ctxt->setContextProperty("model_removeDisplaced_transitionVia", &model_removeDisplaced_transitionVia);
+    ctxt->setContextProperty("displaced_transitionVia", displaced_transitionVia);
+    ctxt->setContextProperty("addDisplaced_transitionVia", addDisplaced_transitionVia);
+    ctxt->setContextProperty("moveDisplaced_transitionVia", moveDisplaced_transitionVia);
+    ctxt->setContextProperty("removeDisplaced_transitionVia", removeDisplaced_transitionVia);
+    ctxt->setContextProperty("useDisplaced", useDisplaced);
+    ctxt->setContextProperty("displacedEnabled", displacedEnabled);
+    ctxt->setContextProperty("useAddDisplaced", useAddDisplaced);
+    ctxt->setContextProperty("addDisplacedEnabled", addDisplacedEnabled);
+    ctxt->setContextProperty("useMoveDisplaced", useMoveDisplaced);
+    ctxt->setContextProperty("moveDisplacedEnabled", moveDisplacedEnabled);
+    ctxt->setContextProperty("useRemoveDisplaced", useRemoveDisplaced);
+    ctxt->setContextProperty("removeDisplacedEnabled", removeDisplacedEnabled);
+    canvas->setSource(testFileUrl("displacedTransitions.qml"));
+    canvas->show();
+    qApp->processEvents();
+
+    QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
+    QTRY_VERIFY(gridview != 0);
+    QQuickItem *contentItem = gridview->contentItem();
+    QVERIFY(contentItem != 0);
+    QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+
+    QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
+    gridview->setProperty("displaceTransitionsDone", false);
+
+    switch (change.type) {
+        case ListChange::Inserted:
+        {
+            QList<QPair<QString, QString> > targetItemData;
+            for (int i=change.index; i<change.index + change.count; ++i)
+                targetItemData << qMakePair(QString("new item %1").arg(i), QString::number(i));
+            model.insertItems(change.index, targetItemData);
+            QTRY_COMPARE(model.count(), gridview->count());
+            break;
+        }
+        case ListChange::Removed:
+            model.removeItems(change.index, change.count);
+            QTRY_COMPARE(model.count(), gridview->count());
+            break;
+        case ListChange::Moved:
+            model.moveItems(change.index, change.to, change.count);
+            QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+            break;
+        case ListChange::SetCurrent:
+        case ListChange::SetContentY:
+            break;
+    }
+    if ((useDisplaced && displacedEnabled)
+            || (useAddDisplaced && addDisplacedEnabled)
+            || (useMoveDisplaced && moveDisplacedEnabled)
+            || (useRemoveDisplaced && removeDisplacedEnabled)) {
+        QTRY_VERIFY(gridview->property("displaceTransitionsDone").toBool());
+    }
+
+    if (change.type == ListChange::Inserted && useAddDisplaced && addDisplacedEnabled)
+        model_addDisplaced_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with add displaced", "shouldn't have been animated with add displaced");
+    else
+        QCOMPARE(model_addDisplaced_transitionVia.count(), 0);
+    if (change.type == ListChange::Moved && useMoveDisplaced && moveDisplacedEnabled)
+        model_moveDisplaced_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with move displaced", "shouldn't have been animated with move displaced");
+    else
+        QCOMPARE(model_moveDisplaced_transitionVia.count(), 0);
+    if (change.type == ListChange::Removed && useRemoveDisplaced && removeDisplacedEnabled)
+        model_removeDisplaced_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with remove displaced", "shouldn't have been animated with remove displaced");
+    else
+        QCOMPARE(model_removeDisplaced_transitionVia.count(), 0);
+
+    if (useDisplaced && displacedEnabled
+            && ( (change.type == ListChange::Inserted && (!useAddDisplaced || !addDisplacedEnabled))
+                 || (change.type == ListChange::Moved && (!useMoveDisplaced || !moveDisplacedEnabled))
+                 || (change.type == ListChange::Removed && (!useRemoveDisplaced || !removeDisplacedEnabled))) ) {
+        model_displaced_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with generic displaced", "shouldn't have been animated with generic displaced");
+    } else {
+        QCOMPARE(model_displaced_transitionVia.count(), 0);
+    }
+
+    // verify all items moved to the correct final positions
+    QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
+    for (int i=0; i < model.count() && i < items.count(); ++i) {
+        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+        QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
+        QCOMPARE(item->x(), (i%3)*80.0);
+        QCOMPARE(item->y(), (i/3)*60.0);
+        QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
+        QVERIFY(name != 0);
+        QTRY_COMPARE(name->text(), model.name(i));
+    }
+
+    delete canvas;
+}
+
+void tst_QQuickGridView::displacedTransitions_data()
+{
+    QTest::addColumn<bool>("useDisplaced");
+    QTest::addColumn<bool>("displacedEnabled");
+    QTest::addColumn<bool>("useAddDisplaced");
+    QTest::addColumn<bool>("addDisplacedEnabled");
+    QTest::addColumn<bool>("useMoveDisplaced");
+    QTest::addColumn<bool>("moveDisplacedEnabled");
+    QTest::addColumn<bool>("useRemoveDisplaced");
+    QTest::addColumn<bool>("removeDisplacedEnabled");
+    QTest::addColumn<ListChange>("change");
+    QTest::addColumn<ListRange>("expectedDisplacedIndexes");
+
+    QTest::newRow("no displaced transitions at all")
+            << false << false
+            << false << false
+            << false << false
+            << false << false
+            << ListChange::insert(0, 1) << ListRange(0, 17);
+
+    QTest::newRow("just displaced")
+            << true << true
+            << false << false
+            << false << false
+            << false << false
+            << ListChange::insert(0, 1) << ListRange(0, 17);
+
+    QTest::newRow("just displaced (not enabled)")
+            << true << false
+            << false << false
+            << false << false
+            << false << false
+            << ListChange::insert(0, 1) << ListRange(0, 17);
+
+    QTest::newRow("displaced + addDisplaced")
+            << true << true
+            << true << true
+            << false << false
+            << false << false
+            << ListChange::insert(0, 1) << ListRange(0, 17);
+
+    QTest::newRow("displaced + addDisplaced (not enabled)")
+            << true << true
+            << true << false
+            << false << false
+            << false << false
+            << ListChange::insert(0, 1) << ListRange(0, 17);
+
+    QTest::newRow("displaced + moveDisplaced")
+            << true << true
+            << false << false
+            << true << true
+            << false << false
+            << ListChange::move(0, 10, 1) << ListRange(1, 10);
+
+    QTest::newRow("displaced + moveDisplaced (not enabled)")
+            << true << true
+            << false << false
+            << true << false
+            << false << false
+            << ListChange::move(0, 10, 1) << ListRange(1, 10);
+
+    QTest::newRow("displaced + removeDisplaced")
+            << true << true
+            << false << false
+            << false << false
+            << true << true
+            << ListChange::remove(0, 1) << ListRange(1, 18);
+
+    QTest::newRow("displaced + removeDisplaced (not enabled)")
+            << true << true
+            << false << false
+            << false << false
+            << true << false
+            << ListChange::remove(0, 1) << ListRange(1, 18);
+
+
+    QTest::newRow("displaced + add, should use generic displaced for a remove")
+            << true << true
+            << true << true
+            << false << false
+            << true << false
+            << ListChange::remove(0, 1) << ListRange(1, 18);
+}
+
 void tst_QQuickGridView::multipleTransitions()
 {
     // Tests that if you interrupt a transition in progress with another action that
diff --git a/tests/auto/qtquick2/qquicklistview/data/displacedTransitions.qml b/tests/auto/qtquick2/qquicklistview/data/displacedTransitions.qml
new file mode 100644 (file)
index 0000000..cc7892e
--- /dev/null
@@ -0,0 +1,128 @@
+import QtQuick 2.0
+
+Rectangle {
+    id: root
+    width: 500
+    height: 600
+
+    property int duration: 10
+    property int count: list.count
+
+    Component {
+        id: myDelegate
+        Rectangle {
+            id: wrapper
+
+            property string nameData: name
+
+            objectName: "wrapper"
+            height: 20
+            width: 240
+            Text { text: index }
+            Text {
+                x: 30
+                id: textName
+                objectName: "textName"
+                text: name
+            }
+            Text {
+                x: 200
+                text: wrapper.y
+            }
+            color: ListView.isCurrentItem ? "lightsteelblue" : "white"
+
+            onXChanged: checkPos()
+            onYChanged: checkPos()
+
+            function checkPos() {
+                if (Qt.point(x, y) == displaced_transitionVia)
+                    model_displaced_transitionVia.addItem(name, "")
+                if (Qt.point(x, y) == addDisplaced_transitionVia)
+                    model_addDisplaced_transitionVia.addItem(name, "")
+                if (Qt.point(x, y) == moveDisplaced_transitionVia)
+                    model_moveDisplaced_transitionVia.addItem(name, "")
+                if (Qt.point(x, y) == removeDisplaced_transitionVia)
+                    model_removeDisplaced_transitionVia.addItem(name, "")
+            }
+        }
+    }
+
+    ListView {
+        id: list
+
+        property int targetTransitionsDone
+        property int displaceTransitionsDone
+
+        objectName: "list"
+        focus: true
+        anchors.centerIn: parent
+        width: 240
+        height: 320
+        model: testModel
+        delegate: myDelegate
+
+        displaced: useDisplaced ? displaced : null
+        addDisplaced: useAddDisplaced ? addDisplaced : null
+        moveDisplaced: useMoveDisplaced ? moveDisplaced : null
+        removeDisplaced: useRemoveDisplaced ? removeDisplaced : null
+
+        Transition {
+            id: displaced
+            enabled: displacedEnabled
+            SequentialAnimation {
+                ParallelAnimation {
+                    NumberAnimation { properties: "x"; to: displaced_transitionVia.x; duration: root.duration }
+                    NumberAnimation { properties: "y"; to: displaced_transitionVia.y; duration: root.duration }
+                }
+                NumberAnimation { properties: "x,y"; duration: root.duration }
+                PropertyAction { target: list; property: "displaceTransitionsDone"; value: true }
+            }
+        }
+
+        Transition {
+            id: addDisplaced
+            enabled: addDisplacedEnabled
+            SequentialAnimation {
+                ParallelAnimation {
+                    NumberAnimation { properties: "x"; to: addDisplaced_transitionVia.x; duration: root.duration }
+                    NumberAnimation { properties: "y"; to: addDisplaced_transitionVia.y; duration: root.duration }
+                }
+                NumberAnimation { properties: "x,y"; duration: root.duration }
+                PropertyAction { target: list; property: "displaceTransitionsDone"; value: true }
+            }
+        }
+
+        Transition {
+            id: moveDisplaced
+            enabled: moveDisplacedEnabled
+            SequentialAnimation {
+                ParallelAnimation {
+                    NumberAnimation { properties: "x"; to: moveDisplaced_transitionVia.x; duration: root.duration }
+                    NumberAnimation { properties: "y"; to: moveDisplaced_transitionVia.y; duration: root.duration }
+                }
+                NumberAnimation { properties: "x,y"; duration: root.duration }
+                PropertyAction { target: list; property: "displaceTransitionsDone"; value: true }
+            }
+        }
+
+        Transition {
+            id: removeDisplaced
+            enabled: removeDisplacedEnabled
+            SequentialAnimation {
+                ParallelAnimation {
+                    NumberAnimation { properties: "x"; to: removeDisplaced_transitionVia.x; duration: root.duration }
+                    NumberAnimation { properties: "y"; to: removeDisplaced_transitionVia.y; duration: root.duration }
+                }
+                NumberAnimation { properties: "x,y"; duration: root.duration }
+                PropertyAction { target: list; property: "displaceTransitionsDone"; value: true }
+            }
+        }
+    }
+
+    Rectangle {
+        anchors.fill: list
+        color: "lightsteelblue"
+        opacity: 0.2
+    }
+}
+
index 04da3f7..a834f1a 100644 (file)
@@ -179,6 +179,8 @@ private slots:
     void moveTransitions_data();
     void removeTransitions();
     void removeTransitions_data();
+    void displacedTransitions();
+    void displacedTransitions_data();
     void multipleTransitions();
     void multipleTransitions_data();
 
@@ -5564,6 +5566,217 @@ void tst_QQuickListView::removeTransitions_data()
             << 17 << 3 << ListRange();
 }
 
+void tst_QQuickListView::displacedTransitions()
+{
+    QFETCH(bool, useDisplaced);
+    QFETCH(bool, displacedEnabled);
+    QFETCH(bool, useAddDisplaced);
+    QFETCH(bool, addDisplacedEnabled);
+    QFETCH(bool, useMoveDisplaced);
+    QFETCH(bool, moveDisplacedEnabled);
+    QFETCH(bool, useRemoveDisplaced);
+    QFETCH(bool, removeDisplacedEnabled);
+    QFETCH(ListChange, change);
+    QFETCH(ListRange, expectedDisplacedIndexes);
+
+    QaimModel model;
+    for (int i = 0; i < 30; i++)
+        model.addItem("Original item" + QString::number(i), "");
+    QaimModel model_displaced_transitionVia;
+    QaimModel model_addDisplaced_transitionVia;
+    QaimModel model_moveDisplaced_transitionVia;
+    QaimModel model_removeDisplaced_transitionVia;
+
+    QPointF displaced_transitionVia(-50, -100);
+    QPointF addDisplaced_transitionVia(-150, 100);
+    QPointF moveDisplaced_transitionVia(50, -100);
+    QPointF removeDisplaced_transitionVia(150, 100);
+
+    QQuickView *canvas = createView();
+    QDeclarativeContext *ctxt = canvas->rootContext();
+    TestObject *testObject = new TestObject(canvas);
+    ctxt->setContextProperty("testModel", &model);
+    ctxt->setContextProperty("testObject", testObject);
+    ctxt->setContextProperty("model_displaced_transitionVia", &model_displaced_transitionVia);
+    ctxt->setContextProperty("model_addDisplaced_transitionVia", &model_addDisplaced_transitionVia);
+    ctxt->setContextProperty("model_moveDisplaced_transitionVia", &model_moveDisplaced_transitionVia);
+    ctxt->setContextProperty("model_removeDisplaced_transitionVia", &model_removeDisplaced_transitionVia);
+    ctxt->setContextProperty("displaced_transitionVia", displaced_transitionVia);
+    ctxt->setContextProperty("addDisplaced_transitionVia", addDisplaced_transitionVia);
+    ctxt->setContextProperty("moveDisplaced_transitionVia", moveDisplaced_transitionVia);
+    ctxt->setContextProperty("removeDisplaced_transitionVia", removeDisplaced_transitionVia);
+    ctxt->setContextProperty("useDisplaced", useDisplaced);
+    ctxt->setContextProperty("displacedEnabled", displacedEnabled);
+    ctxt->setContextProperty("useAddDisplaced", useAddDisplaced);
+    ctxt->setContextProperty("addDisplacedEnabled", addDisplacedEnabled);
+    ctxt->setContextProperty("useMoveDisplaced", useMoveDisplaced);
+    ctxt->setContextProperty("moveDisplacedEnabled", moveDisplacedEnabled);
+    ctxt->setContextProperty("useRemoveDisplaced", useRemoveDisplaced);
+    ctxt->setContextProperty("removeDisplacedEnabled", removeDisplacedEnabled);
+    canvas->setSource(testFileUrl("displacedTransitions.qml"));
+    canvas->show();
+    qApp->processEvents();
+
+    QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
+    QTRY_VERIFY(listview != 0);
+    QQuickItem *contentItem = listview->contentItem();
+    QVERIFY(contentItem != 0);
+    QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+
+    QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
+    listview->setProperty("displaceTransitionsDone", false);
+
+    switch (change.type) {
+        case ListChange::Inserted:
+        {
+            QList<QPair<QString, QString> > targetItemData;
+            for (int i=change.index; i<change.index + change.count; ++i)
+                targetItemData << qMakePair(QString("new item %1").arg(i), QString::number(i));
+            model.insertItems(change.index, targetItemData);
+            QTRY_COMPARE(model.count(), listview->count());
+            break;
+        }
+        case ListChange::Removed:
+            model.removeItems(change.index, change.count);
+            QTRY_COMPARE(model.count(), listview->count());
+            break;
+        case ListChange::Moved:
+            model.moveItems(change.index, change.to, change.count);
+            QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+            break;
+        case ListChange::SetCurrent:
+        case ListChange::SetContentY:
+            break;
+    }
+    if ((useDisplaced && displacedEnabled)
+            || (useAddDisplaced && addDisplacedEnabled)
+            || (useMoveDisplaced && moveDisplacedEnabled)
+            || (useRemoveDisplaced && removeDisplacedEnabled)) {
+        QTRY_VERIFY(listview->property("displaceTransitionsDone").toBool());
+    }
+
+    if (change.type == ListChange::Inserted && useAddDisplaced && addDisplacedEnabled)
+        model_addDisplaced_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with add displaced", "shouldn't have been animated with add displaced");
+    else
+        QCOMPARE(model_addDisplaced_transitionVia.count(), 0);
+    if (change.type == ListChange::Moved && useMoveDisplaced && moveDisplacedEnabled)
+        model_moveDisplaced_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with move displaced", "shouldn't have been animated with move displaced");
+    else
+        QCOMPARE(model_moveDisplaced_transitionVia.count(), 0);
+    if (change.type == ListChange::Removed && useRemoveDisplaced && removeDisplacedEnabled)
+        model_removeDisplaced_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with remove displaced", "shouldn't have been animated with remove displaced");
+    else
+        QCOMPARE(model_removeDisplaced_transitionVia.count(), 0);
+
+    if (useDisplaced && displacedEnabled
+            && ( (change.type == ListChange::Inserted && (!useAddDisplaced || !addDisplacedEnabled))
+                 || (change.type == ListChange::Moved && (!useMoveDisplaced || !moveDisplacedEnabled))
+                 || (change.type == ListChange::Removed && (!useRemoveDisplaced || !removeDisplacedEnabled))) ) {
+        model_displaced_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with generic displaced", "shouldn't have been animated with generic displaced");
+    } else {
+        QCOMPARE(model_displaced_transitionVia.count(), 0);
+    }
+
+    // verify all items moved to the correct final positions
+    QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
+    for (int i=0; i < model.count() && i < items.count(); ++i) {
+        QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+        QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
+        QCOMPARE(item->x(), 0.0);
+        QCOMPARE(item->y(), i * 20.0);
+        QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
+        QVERIFY(name != 0);
+        QTRY_COMPARE(name->text(), model.name(i));
+    }
+
+    delete canvas;
+}
+
+void tst_QQuickListView::displacedTransitions_data()
+{
+    QTest::addColumn<bool>("useDisplaced");
+    QTest::addColumn<bool>("displacedEnabled");
+    QTest::addColumn<bool>("useAddDisplaced");
+    QTest::addColumn<bool>("addDisplacedEnabled");
+    QTest::addColumn<bool>("useMoveDisplaced");
+    QTest::addColumn<bool>("moveDisplacedEnabled");
+    QTest::addColumn<bool>("useRemoveDisplaced");
+    QTest::addColumn<bool>("removeDisplacedEnabled");
+    QTest::addColumn<ListChange>("change");
+    QTest::addColumn<ListRange>("expectedDisplacedIndexes");
+
+    QTest::newRow("no displaced transitions at all")
+            << false << false
+            << false << false
+            << false << false
+            << false << false
+            << ListChange::insert(0, 1) << ListRange(0, 15);
+
+    QTest::newRow("just displaced")
+            << true << true
+            << false << false
+            << false << false
+            << false << false
+            << ListChange::insert(0, 1) << ListRange(0, 15);
+
+    QTest::newRow("just displaced (not enabled)")
+            << true << false
+            << false << false
+            << false << false
+            << false << false
+            << ListChange::insert(0, 1) << ListRange(0, 15);
+
+    QTest::newRow("displaced + addDisplaced")
+            << true << true
+            << true << true
+            << false << false
+            << false << false
+            << ListChange::insert(0, 1) << ListRange(0, 15);
+
+    QTest::newRow("displaced + addDisplaced (not enabled)")
+            << true << true
+            << true << false
+            << false << false
+            << false << false
+            << ListChange::insert(0, 1) << ListRange(0, 15);
+
+    QTest::newRow("displaced + moveDisplaced")
+            << true << true
+            << false << false
+            << true << true
+            << false << false
+            << ListChange::move(0, 10, 1) << ListRange(1, 10);
+
+    QTest::newRow("displaced + moveDisplaced (not enabled)")
+            << true << true
+            << false << false
+            << true << false
+            << false << false
+            << ListChange::move(0, 10, 1) << ListRange(1, 10);
+
+    QTest::newRow("displaced + removeDisplaced")
+            << true << true
+            << false << false
+            << false << false
+            << true << true
+            << ListChange::remove(0, 1) << ListRange(1, 16);
+
+    QTest::newRow("displaced + removeDisplaced (not enabled)")
+            << true << true
+            << false << false
+            << false << false
+            << true << false
+            << ListChange::remove(0, 1) << ListRange(1, 16);
+
+
+    QTest::newRow("displaced + add, should use generic displaced for a remove")
+            << true << true
+            << true << true
+            << false << false
+            << true << false
+            << ListChange::remove(0, 1) << ListRange(1, 16);
+}
+
 void tst_QQuickListView::multipleTransitions()
 {
     QSKIP("QTBUG-24523");
index 71fd506..ebee178 100644 (file)
@@ -174,6 +174,7 @@ namespace QQuickViewTestUtil
     };
 }
 
+Q_DECLARE_METATYPE(QQuickViewTestUtil::ListChange)
 Q_DECLARE_METATYPE(QList<QQuickViewTestUtil::ListChange>)
 Q_DECLARE_METATYPE(QQuickViewTestUtil::ListRange)