summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorBea Lam <bea.lam@nokia.com>2012-02-09 17:59:44 +1000
committerQt by Nokia <qt-info@nokia.com>2012-02-16 08:30:27 +0100
commit2df9abf7047afbe20b19d156ac37b58d9b047575 (patch)
tree0d4e13362d12f978d8d0c7871e161fd2f0fddeaa /tests
parent7127120b68ec08296b6e2980d1c9ae1a34e5f28d (diff)
Built-in transition support for ListView & GridView
ListView and GridView can now be assigned transitions to be run when: - Populating the view (when initially setting the model / resetting) - Adding items - Removing items - Moving items The ViewTransition attached object can be used from within a transition declaration to access various information about the items that are being transitioned. Task-number: QTBUG-21504 Change-Id: Ie5c75ea511c8b15acc3f06fccf19abe34d3677f9 Reviewed-by: Martin Jones <martin.jones@nokia.com>
Diffstat (limited to 'tests')
-rw-r--r--tests/auto/qtquick2/qquickgridview/data/addTransitions.qml129
-rw-r--r--tests/auto/qtquick2/qquickgridview/data/moveTransitions.qml143
-rw-r--r--tests/auto/qtquick2/qquickgridview/data/multipleTransitions.qml123
-rw-r--r--tests/auto/qtquick2/qquickgridview/data/populateTransitions.qml103
-rw-r--r--tests/auto/qtquick2/qquickgridview/data/removeTransitions.qml146
-rw-r--r--tests/auto/qtquick2/qquickgridview/tst_qquickgridview.cpp977
-rw-r--r--tests/auto/qtquick2/qquicklistview/data/addTransitions.qml134
-rw-r--r--tests/auto/qtquick2/qquicklistview/data/moveTransitions.qml141
-rw-r--r--tests/auto/qtquick2/qquicklistview/data/multipleTransitions.qml121
-rw-r--r--tests/auto/qtquick2/qquicklistview/data/populateTransitions.qml102
-rw-r--r--tests/auto/qtquick2/qquicklistview/data/removeTransitions.qml144
-rw-r--r--tests/auto/qtquick2/qquicklistview/tst_qquicklistview.cpp950
-rw-r--r--tests/auto/qtquick2/shared/viewtestutil.cpp148
-rw-r--r--tests/auto/qtquick2/shared/viewtestutil.h48
-rw-r--r--tests/auto/qtquick2/shared/visualtestutil.h10
15 files changed, 3407 insertions, 12 deletions
diff --git a/tests/auto/qtquick2/qquickgridview/data/addTransitions.qml b/tests/auto/qtquick2/qquickgridview/data/addTransitions.qml
new file mode 100644
index 0000000000..faea02a50d
--- /dev/null
+++ b/tests/auto/qtquick2/qquickgridview/data/addTransitions.qml
@@ -0,0 +1,129 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: root
+ width: 550
+ 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
+ border.width: 1
+ Column {
+ Text { text: index }
+ Text {
+ text: wrapper.x + ", " + wrapper.y
+ }
+ Text {
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ }
+ color: GridView.isCurrentItem ? "lightsteelblue" : "white"
+
+ onXChanged: checkPos()
+ onYChanged: checkPos()
+
+ function checkPos() {
+ if (Qt.point(x, y) == targetItems_transitionFrom)
+ model_targetItems_transitionFrom.addItem(name, "")
+ if (Qt.point(x, y) == displacedItems_transitionVia)
+ model_displacedItems_transitionVia.addItem(name, "")
+ }
+ }
+ }
+
+ GridView {
+ id: grid
+
+ property int targetTransitionsDone
+ property int displaceTransitionsDone
+
+ property var targetTrans_items: new Object()
+ property var targetTrans_targetIndexes: new Array()
+ property var targetTrans_targetItems: new Array()
+
+ property var displacedTrans_items: new Object()
+ property var displacedTrans_targetIndexes: new Array()
+ property var displacedTrans_targetItems: new Array()
+
+ objectName: "grid"
+ width: 240
+ height: 320
+ cellWidth: 80
+ cellHeight: 60
+ anchors.centerIn: parent
+ model: testModel
+ delegate: myDelegate
+
+ // for QDeclarativeListProperty types
+ function copyList(propList) {
+ var temp = new Array()
+ for (var i=0; i<propList.length; i++)
+ temp.push(propList[i])
+ return temp
+ }
+
+ add: Transition {
+ id: targetTransition
+
+ SequentialAnimation {
+ ScriptAction {
+ script: {
+ grid.targetTrans_items[targetTransition.ViewTransition.item.nameData] = targetTransition.ViewTransition.index
+ grid.targetTrans_targetIndexes.push(targetTransition.ViewTransition.targetIndexes)
+ grid.targetTrans_targetItems.push(grid.copyList(targetTransition.ViewTransition.targetItems))
+ }
+ }
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; from: targetItems_transitionFrom.x; duration: root.duration }
+ NumberAnimation { properties: "y"; from: targetItems_transitionFrom.y; duration: root.duration }
+ }
+
+ ScriptAction { script: grid.targetTransitionsDone += 1 }
+ }
+ }
+
+ addDisplaced: Transition {
+ id: displaced
+
+ SequentialAnimation {
+ ScriptAction {
+ script: {
+ grid.displacedTrans_items[displaced.ViewTransition.item.nameData] = displaced.ViewTransition.index
+ grid.displacedTrans_targetIndexes.push(displaced.ViewTransition.targetIndexes)
+ grid.displacedTrans_targetItems.push(grid.copyList(displaced.ViewTransition.targetItems))
+ }
+ }
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; duration: root.duration; to: displacedItems_transitionVia.x }
+ NumberAnimation { properties: "y"; duration: root.duration; to: displacedItems_transitionVia.y }
+ }
+ NumberAnimation { properties: "x,y"; duration: root.duration }
+
+ ScriptAction { script: grid.displaceTransitionsDone += 1 }
+ }
+
+ }
+ }
+
+ Rectangle {
+ anchors.fill: grid
+ color: "lightsteelblue"
+ opacity: 0.2
+ }
+}
+
+
diff --git a/tests/auto/qtquick2/qquickgridview/data/moveTransitions.qml b/tests/auto/qtquick2/qquickgridview/data/moveTransitions.qml
new file mode 100644
index 0000000000..3599dcfea0
--- /dev/null
+++ b/tests/auto/qtquick2/qquickgridview/data/moveTransitions.qml
@@ -0,0 +1,143 @@
+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
+ border.width: 1
+ Column {
+ Text { text: index }
+ Text {
+ text: wrapper.x + ", " + wrapper.y
+ }
+ Text {
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ }
+ color: GridView.isCurrentItem ? "lightsteelblue" : "white"
+
+ onXChanged: checkPos()
+ onYChanged: checkPos()
+
+ function checkPos() {
+ if (Qt.point(x, y) == targetItems_transitionVia)
+ model_targetItems_transitionVia.addItem(name, "")
+ if (Qt.point(x, y) == displacedItems_transitionVia)
+ model_displacedItems_transitionVia.addItem(name, "")
+ }
+ }
+ }
+
+ GridView {
+ id: grid
+
+ property int targetTransitionsDone
+ property int displaceTransitionsDone
+
+ property var targetTrans_items: new Object()
+ property var targetTrans_targetIndexes: new Array()
+ property var targetTrans_targetItems: new Array()
+
+ property var displacedTrans_items: new Object()
+ property var displacedTrans_targetIndexes: new Array()
+ property var displacedTrans_targetItems: new Array()
+
+ objectName: "grid"
+ width: 240
+ height: 320
+ cellWidth: 80
+ cellHeight: 60
+ anchors.centerIn: parent
+ model: testModel
+ delegate: myDelegate
+
+ // for QDeclarativeListProperty types
+ function copyList(propList) {
+ var temp = new Array()
+ for (var i=0; i<propList.length; i++)
+ temp.push(propList[i])
+ return temp
+ }
+
+ move: Transition {
+ id: targetTransition
+
+ SequentialAnimation {
+ ScriptAction {
+ script: {
+ grid.targetTrans_items[targetTransition.ViewTransition.item.nameData] = targetTransition.ViewTransition.index
+ grid.targetTrans_targetIndexes.push(targetTransition.ViewTransition.targetIndexes)
+ grid.targetTrans_targetItems.push(grid.copyList(targetTransition.ViewTransition.targetItems))
+ }
+ }
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; to: targetItems_transitionVia.x; duration: root.duration }
+ NumberAnimation { properties: "y"; to: targetItems_transitionVia.y; duration: root.duration }
+ }
+
+ NumberAnimation { properties: "x,y"; duration: root.duration }
+
+ ScriptAction { script: grid.targetTransitionsDone += 1 }
+ }
+ }
+
+ moveDisplaced: Transition {
+ id: displaced
+
+ SequentialAnimation {
+ ScriptAction {
+ script: {
+ grid.displacedTrans_items[displaced.ViewTransition.item.nameData] = displaced.ViewTransition.index
+ grid.displacedTrans_targetIndexes.push(displaced.ViewTransition.targetIndexes)
+ grid.displacedTrans_targetItems.push(grid.copyList(displaced.ViewTransition.targetItems))
+ }
+ }
+ ParallelAnimation {
+ NumberAnimation {
+ properties: "x"; duration: root.duration
+ to: displacedItems_transitionVia.x
+ }
+ NumberAnimation {
+ properties: "y"; duration: root.duration
+ to: displacedItems_transitionVia.y
+ }
+ }
+ NumberAnimation { properties: "x,y"; duration: root.duration }
+
+ ScriptAction { script: grid.displaceTransitionsDone += 1 }
+ }
+
+ }
+ }
+
+ Rectangle {
+ anchors.fill: grid
+ color: "lightsteelblue"
+ opacity: 0.2
+ }
+
+ Rectangle {
+ anchors.bottom: parent.bottom
+ width: 20; height: 20
+ color: "white"
+ NumberAnimation on x { loops: Animation.Infinite; from: 0; to: 300; duration: 10000 }
+ }
+}
+
+
diff --git a/tests/auto/qtquick2/qquickgridview/data/multipleTransitions.qml b/tests/auto/qtquick2/qquickgridview/data/multipleTransitions.qml
new file mode 100644
index 0000000000..45b86e22cf
--- /dev/null
+++ b/tests/auto/qtquick2/qquickgridview/data/multipleTransitions.qml
@@ -0,0 +1,123 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: root
+ width: 500
+ height: 600
+
+ // time to pause between each add, remove, etc.
+ // (obviously, must be less than 'duration' value to actually test that
+ // interrupting transitions will still produce the correct result)
+ property int timeBetweenActions: duration / 2
+
+ property int duration: 100
+
+ property int count: grid.count
+
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ objectName: "wrapper"
+ width: 80
+ height: 60
+ border.width: 1
+ Column {
+ Text { text: index }
+ Text {
+ text: wrapper.x + ", " + wrapper.y
+ }
+ Text {
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ }
+ color: GridView.isCurrentItem ? "lightsteelblue" : "white"
+ }
+ }
+
+ GridView {
+ id: grid
+
+ property bool populateDone
+
+ property bool runningAddTargets: false
+ property bool runningAddDisplaced: false
+ property bool runningMoveTargets: false
+ property bool runningMoveDisplaced: false
+
+ objectName: "grid"
+ width: 240
+ height: 320
+ cellWidth: 80
+ cellHeight: 60
+ anchors.centerIn: parent
+ model: testModel
+ delegate: myDelegate
+
+ add: Transition {
+ id: addTargets
+ SequentialAnimation {
+ ScriptAction { script: grid.runningAddTargets = true }
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; from: addTargets_transitionFrom.x; duration: root.duration }
+ NumberAnimation { properties: "y"; from: addTargets_transitionFrom.y; duration: root.duration }
+ }
+ ScriptAction { script: grid.runningAddTargets = false }
+ }
+ }
+
+ addDisplaced: Transition {
+ id: addDisplaced
+ SequentialAnimation {
+ ScriptAction { script: grid.runningAddDisplaced = true }
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; from: addDisplaced_transitionFrom.x; duration: root.duration }
+ NumberAnimation { properties: "y"; from: addDisplaced_transitionFrom.y; duration: root.duration }
+ }
+ ScriptAction { script: grid.runningAddDisplaced = false }
+ }
+ }
+
+ move: Transition {
+ id: moveTargets
+ SequentialAnimation {
+ ScriptAction { script: grid.runningMoveTargets = true }
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; from: moveTargets_transitionFrom.x; duration: root.duration }
+ NumberAnimation { properties: "y"; from: moveTargets_transitionFrom.y; duration: root.duration }
+ }
+ ScriptAction { script: grid.runningMoveTargets = false }
+ }
+ }
+
+ moveDisplaced: Transition {
+ id: moveDisplaced
+ SequentialAnimation {
+ ScriptAction { script: grid.runningMoveDisplaced = true }
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; from: moveDisplaced_transitionFrom.x; duration: root.duration }
+ NumberAnimation { properties: "y"; from: moveDisplaced_transitionFrom.y; duration: root.duration }
+ }
+ ScriptAction { script: grid.runningMoveDisplaced = false }
+ }
+ }
+ }
+
+ Rectangle {
+ anchors.fill: grid
+ color: "lightsteelblue"
+ opacity: 0.2
+ }
+
+ Rectangle {
+ anchors.bottom: parent.bottom
+ width: 20; height: 20
+ color: "white"
+ NumberAnimation on x { loops: Animation.Infinite; from: 0; to: 300; duration: 100000 }
+ }
+}
+
+
+
diff --git a/tests/auto/qtquick2/qquickgridview/data/populateTransitions.qml b/tests/auto/qtquick2/qquickgridview/data/populateTransitions.qml
new file mode 100644
index 0000000000..c12d5ac39d
--- /dev/null
+++ b/tests/auto/qtquick2/qquickgridview/data/populateTransitions.qml
@@ -0,0 +1,103 @@
+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
+ objectName: "wrapper"
+ width: 80
+ height: 60
+ border.width: 1
+ Column {
+ Text { text: index }
+ Text {
+ text: wrapper.x + ", " + wrapper.y
+ }
+ Text {
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ }
+ color: GridView.isCurrentItem ? "lightsteelblue" : "white"
+
+ onXChanged: checkPos()
+ onYChanged: checkPos()
+
+ function checkPos() {
+ if (Qt.point(x, y) == transitionFrom)
+ model_transitionFrom.addItem(name, "")
+ if (Qt.point(x, y) == transitionVia)
+ model_transitionVia.addItem(name, "")
+ }
+ }
+ }
+
+ GridView {
+ id: grid
+
+ property int countPopulateTransitions
+ property int countAddTransitions
+
+ objectName: "grid"
+ focus: true
+ anchors.centerIn: parent
+ width: 240
+ height: 320
+ cellWidth: 80
+ cellHeight: 60
+ model: testModel
+ delegate: myDelegate
+
+ populate: usePopulateTransition ? popTransition : null
+
+ add: Transition {
+ SequentialAnimation {
+ ScriptAction { script: grid.countAddTransitions += 1 }
+ NumberAnimation { properties: "x,y"; duration: root.duration }
+ }
+ }
+ }
+
+ Transition {
+ id: popTransition
+ SequentialAnimation {
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; from: transitionFrom.x; to: transitionVia.x; duration: root.duration }
+ NumberAnimation { properties: "y"; from: transitionFrom.y; to: transitionVia.y; duration: root.duration }
+ }
+ NumberAnimation { properties: "x,y"; duration: root.duration }
+ ScriptAction { script: grid.countPopulateTransitions += 1 }
+ }
+ }
+
+ Rectangle {
+ anchors.fill: grid
+ color: "lightsteelblue"
+ opacity: 0.2
+ }
+
+ Component.onCompleted: {
+ if (dynamicallyPopulate) {
+ for (var i=0; i<30; i++)
+ testModel.addItem("item " + i, "")
+ }
+ }
+
+ Rectangle {
+ anchors.bottom: parent.bottom
+ width: 20; height: 20
+ color: "white"
+ NumberAnimation on x { loops: Animation.Infinite; from: 0; to: 300; duration: 100000 }
+ }
+}
+
+
diff --git a/tests/auto/qtquick2/qquickgridview/data/removeTransitions.qml b/tests/auto/qtquick2/qquickgridview/data/removeTransitions.qml
new file mode 100644
index 0000000000..b07a03580a
--- /dev/null
+++ b/tests/auto/qtquick2/qquickgridview/data/removeTransitions.qml
@@ -0,0 +1,146 @@
+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
+ border.width: 1
+ Column {
+ Text { text: index }
+ Text {
+ text: wrapper.x + ", " + wrapper.y
+ }
+ Text {
+ id: textName
+ objectName: "textName"
+ text: name
+ }
+ }
+ color: GridView.isCurrentItem ? "lightsteelblue" : "white"
+
+ onXChanged: checkPos()
+ onYChanged: checkPos()
+
+ function checkPos() {
+ if (Qt.point(x, y) == targetItems_transitionTo) {
+ model_targetItems_transitionTo.addItem(nameData, "") // name is invalid once model removes the item
+ }
+ if (Qt.point(x, y) == displacedItems_transitionVia) {
+ model_displacedItems_transitionVia.addItem(name, "")
+ }
+ }
+ }
+ }
+
+ GridView {
+ id: grid
+
+ property int targetTransitionsDone
+ property int displaceTransitionsDone
+
+ property var targetTrans_items: new Object()
+ property var targetTrans_targetIndexes: new Array()
+ property var targetTrans_targetItems: new Array()
+
+ property var displacedTrans_items: new Object()
+ property var displacedTrans_targetIndexes: new Array()
+ property var displacedTrans_targetItems: new Array()
+
+ objectName: "grid"
+ width: 240
+ height: 320
+ cellWidth: 80
+ cellHeight: 60
+ anchors.centerIn: parent
+ model: testModel
+ delegate: myDelegate
+
+ // for QDeclarativeListProperty types
+ function copyList(propList) {
+ var temp = new Array()
+ for (var i=0; i<propList.length; i++)
+ temp.push(propList[i])
+ return temp
+ }
+
+ remove: Transition {
+ id: targetTransition
+
+ SequentialAnimation {
+ ScriptAction {
+ script: {
+ grid.targetTrans_items[targetTransition.ViewTransition.item.nameData] = targetTransition.ViewTransition.index
+ grid.targetTrans_targetIndexes.push(targetTransition.ViewTransition.targetIndexes)
+ grid.targetTrans_targetItems.push(grid.copyList(targetTransition.ViewTransition.targetItems))
+ }
+ }
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; to: targetItems_transitionTo.x; duration: root.duration }
+ NumberAnimation { properties: "y"; to: targetItems_transitionTo.y; duration: root.duration }
+ }
+ ScriptAction { script: grid.targetTransitionsDone += 1 }
+
+ // delay deleting this item so that it stays valid for the tests
+ // (this doesn't delay the test itself)
+ PauseAnimation { duration: 10000 }
+ }
+ }
+
+ removeDisplaced: Transition {
+ id: displaced
+
+ SequentialAnimation {
+ ScriptAction {
+ script: {
+ grid.displacedTrans_items[displaced.ViewTransition.item.nameData] = displaced.ViewTransition.index
+ grid.displacedTrans_targetIndexes.push(displaced.ViewTransition.targetIndexes)
+ grid.displacedTrans_targetItems.push(grid.copyList(displaced.ViewTransition.targetItems))
+ }
+ }
+ ParallelAnimation {
+ NumberAnimation {
+ properties: "x"; duration: root.duration
+ to: displacedItems_transitionVia.x
+ }
+ NumberAnimation {
+ properties: "y"; duration: root.duration
+ to: displacedItems_transitionVia.y
+ }
+ }
+ NumberAnimation { properties: "x,y"; duration: root.duration }
+
+ ScriptAction { script: grid.displaceTransitionsDone += 1 }
+ }
+
+ }
+ }
+
+ Rectangle {
+ anchors.fill: grid
+ color: "lightsteelblue"
+ opacity: 0.2
+ }
+
+ Rectangle {
+ anchors.bottom: parent.bottom
+ width: 20; height: 20
+ color: "white"
+ NumberAnimation on x { loops: Animation.Infinite; from: 0; to: 300; duration: 10000 }
+ }
+}
+
+
diff --git a/tests/auto/qtquick2/qquickgridview/tst_qquickgridview.cpp b/tests/auto/qtquick2/qquickgridview/tst_qquickgridview.cpp
index e6b27f808c..5d60e916ae 100644
--- a/tests/auto/qtquick2/qquickgridview/tst_qquickgridview.cpp
+++ b/tests/auto/qtquick2/qquickgridview/tst_qquickgridview.cpp
@@ -50,7 +50,9 @@
#include <QtQuick/private/qquickitem_p.h>
#include <QtQuick/private/qquickgridview_p.h>
#include <QtQuick/private/qquicktext_p.h>
+#include <QtQuick/private/qquickvisualitemmodel_p.h>
#include <QtDeclarative/private/qdeclarativelistmodel_p.h>
+#include <QtDeclarative/private/qlistmodelinterface_p.h>
#include "../../shared/util.h"
#include "../shared/viewtestutil.h"
#include "../shared/visualtestutil.h"
@@ -127,6 +129,23 @@ private slots:
void cacheBuffer();
void asynchronous();
void unrequestedVisibility();
+
+ void populateTransitions();
+ void populateTransitions_data();
+ void addTransitions();
+ void addTransitions_data();
+ void moveTransitions();
+ void moveTransitions_data();
+ void removeTransitions();
+ void removeTransitions_data();
+ void multipleTransitions();
+ void multipleTransitions_data();
+
+private:
+ QList<int> toIntList(const QVariantList &list);
+ void matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes);
+ void matchItemsAndIndexes(const QVariantMap &items, const QaimModel &model, const QList<int> &expectedIndexes);
+ void matchItemLists(const QVariantList &itemLists, const QList<QQuickItem *> &expectedItems);
};
tst_QQuickGridView::tst_QQuickGridView()
@@ -500,13 +519,15 @@ void tst_QQuickGridView::insertBeforeVisible()
QTRY_VERIFY(contentItem != 0);
gridview->setCacheBuffer(cacheBuffer);
+ QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
// trigger a refill (not just setting contentY) so that the visibleItems grid is updated
int firstVisibleIndex = 12; // move to an index where the top item is not visible
gridview->setContentY(firstVisibleIndex/3 * 60.0);
gridview->setCurrentIndex(firstVisibleIndex);
-
qApp->processEvents();
+ QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+
QTRY_COMPARE(gridview->currentIndex(), firstVisibleIndex);
QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", firstVisibleIndex);
QVERIFY(item);
@@ -521,6 +542,7 @@ void tst_QQuickGridView::insertBeforeVisible()
// now, moving to the top of the view should position the inserted items correctly
int itemsOffsetAfterMove = (insertCount / 3) * -60.0;
gridview->setCurrentIndex(0);
+ QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
QTRY_COMPARE(gridview->currentIndex(), 0);
QTRY_COMPARE(gridview->contentY(), 0.0 + itemsOffsetAfterMove);
@@ -1283,12 +1305,19 @@ void tst_QQuickGridView::multipleChanges()
}
case ListChange::Removed:
model.removeItems(changes[i].index, changes[i].count);
+ QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
break;
case ListChange::Moved:
model.moveItems(changes[i].index, changes[i].to, changes[i].count);
+ QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
break;
case ListChange::SetCurrent:
gridview->setCurrentIndex(changes[i].index);
+ QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+ break;
+ case ListChange::SetContentY:
+ gridview->setContentY(changes[i].pos);
+ QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
break;
}
}
@@ -2005,10 +2034,10 @@ void tst_QQuickGridView::componentChanges()
QTRY_VERIFY(gridView);
QDeclarativeComponent component(canvas->engine());
- component.setData("import QtQuick 2.0; Rectangle { color: \"blue\"; }", QUrl::fromLocalFile(""));
+ component.setData("import QtQuick 1.0; Rectangle { color: \"blue\"; }", QUrl::fromLocalFile(""));
QDeclarativeComponent delegateComponent(canvas->engine());
- delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
+ delegateComponent.setData("import QtQuick 1.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
QSignalSpy highlightSpy(gridView, SIGNAL(highlightChanged()));
QSignalSpy delegateSpy(gridView, SIGNAL(delegateChanged()));
@@ -3769,6 +3798,898 @@ void tst_QQuickGridView::unaligned()
delete canvas;
}
+void tst_QQuickGridView::populateTransitions()
+{
+ QFETCH(bool, staticallyPopulate);
+ QFETCH(bool, dynamicallyPopulate);
+ QFETCH(bool, usePopulateTransition);
+
+ QPointF transitionFrom(-50, -50);
+ QPointF transitionVia(100, 100);
+ QaimModel model_transitionFrom;
+ QaimModel model_transitionVia;
+
+ QaimModel model;
+ if (staticallyPopulate) {
+ for (int i = 0; i < 30; i++)
+ model.addItem("item" + QString::number(i), "");
+ }
+
+ QQuickView *canvas = createView();
+ canvas->rootContext()->setContextProperty("testModel", &model);
+ canvas->rootContext()->setContextProperty("usePopulateTransition", usePopulateTransition);
+ canvas->rootContext()->setContextProperty("dynamicallyPopulate", dynamicallyPopulate);
+ canvas->rootContext()->setContextProperty("transitionFrom", transitionFrom);
+ canvas->rootContext()->setContextProperty("transitionVia", transitionVia);
+ canvas->rootContext()->setContextProperty("model_transitionFrom", &model_transitionFrom);
+ canvas->rootContext()->setContextProperty("model_transitionVia", &model_transitionVia);
+ canvas->setSource(testFileUrl("populateTransitions.qml"));
+ canvas->show();
+
+ QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
+ QVERIFY(gridview);
+ QQuickItem *contentItem = gridview->contentItem();
+ QVERIFY(contentItem);
+
+ if (staticallyPopulate || dynamicallyPopulate) {
+ // check the populate transition is run
+ if (usePopulateTransition) {
+ QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), 19);
+ } else {
+ QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+ QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), 0);
+ }
+ QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 0);
+ } else {
+ QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+ }
+
+ int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
+ if (usePopulateTransition)
+ QCOMPARE(itemCount, gridview->property("countPopulateTransitions").toInt());
+ for (int i=0; i < model.count() && i < itemCount; ++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));
+ }
+
+ // add an item and check this is done with add transition, not populate
+ model.insertItem(0, "another item", "");
+ QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 1);
+ QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(),
+ (usePopulateTransition && (staticallyPopulate || dynamicallyPopulate)) ? 19 : 0);
+
+ // clear the model
+ canvas->rootContext()->setContextProperty("testModel", QVariant());
+ QTRY_COMPARE(gridview->count(), 0);
+ QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper").count(), 0);
+ gridview->setProperty("countPopulateTransitions", 0);
+ gridview->setProperty("countAddTransitions", 0);
+
+ // set to a valid model and check populate transition is run a second time
+ model.clear();
+ for (int i = 0; i < 30; i++)
+ model.addItem("item" + QString::number(i), "");
+ canvas->rootContext()->setContextProperty("testModel", &model);
+ QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+
+ QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), usePopulateTransition ? 19 : 0);
+ QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 0);
+
+ itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
+ if (usePopulateTransition)
+ QCOMPARE(itemCount, gridview->property("countPopulateTransitions").toInt());
+ for (int i=0; i < model.count() && i < itemCount; ++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));
+ }
+
+ // reset model and check populate transition is run again
+ gridview->setProperty("countPopulateTransitions", 0);
+ gridview->setProperty("countAddTransitions", 0);
+ model.reset();
+ QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), usePopulateTransition ? 19 : 0);
+ QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 0);
+
+ itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
+ if (usePopulateTransition)
+ QCOMPARE(itemCount, gridview->property("countPopulateTransitions").toInt());
+ for (int i=0; i < model.count() && i < itemCount; ++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::populateTransitions_data()
+{
+ QTest::addColumn<bool>("staticallyPopulate");
+ QTest::addColumn<bool>("dynamicallyPopulate");
+ QTest::addColumn<bool>("usePopulateTransition");
+
+ QTest::newRow("static") << true << false << true;
+ QTest::newRow("static, no populate") << true << false << false;
+
+ QTest::newRow("dynamic") << false << true << true;
+ QTest::newRow("dynamic, no populate") << false << true << false;
+
+ QTest::newRow("empty to start with") << false << false << true;
+ QTest::newRow("empty to start with, no populate") << false << false << false;
+}
+
+void tst_QQuickGridView::addTransitions()
+{
+ QFETCH(int, initialItemCount);
+ QFETCH(bool, shouldAnimateTargets);
+ QFETCH(qreal, contentY);
+ QFETCH(int, insertionIndex);
+ QFETCH(int, insertionCount);
+ QFETCH(ListRange, expectedDisplacedIndexes);
+
+ // added items should start here
+ QPointF targetItems_transitionFrom(-50, -50);
+
+ // displaced items should pass through this point
+ QPointF displacedItems_transitionVia(100, 100);
+
+ QaimModel model;
+ for (int i = 0; i < initialItemCount; i++)
+ model.addItem("Original item" + QString::number(i), "");
+ QaimModel model_targetItems_transitionFrom;
+ QaimModel model_displacedItems_transitionVia;
+
+ QQuickView *canvas = createView();
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("model_targetItems_transitionFrom", &model_targetItems_transitionFrom);
+ ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
+ ctxt->setContextProperty("targetItems_transitionFrom", targetItems_transitionFrom);
+ ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
+ canvas->setSource(testFileUrl("addTransitions.qml"));
+ canvas->show();
+
+ 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);
+
+ if (contentY != 0) {
+ gridview->setContentY(contentY);
+ QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+ }
+
+ QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
+
+ // only target items that will become visible should be animated
+ QList<QPair<QString, QString> > newData;
+ QList<QPair<QString, QString> > expectedTargetData;
+ QList<int> targetIndexes;
+ if (shouldAnimateTargets) {
+ for (int i=insertionIndex; i<insertionIndex+insertionCount; i++) {
+ newData << qMakePair(QString("New item %1").arg(i), QString(""));
+
+ // last visible item is the first item of the row beneath the view
+ if (i >= (contentY / 60)*3 && i < qCeil((contentY + gridview->height()) / 60.0)*3) {
+ expectedTargetData << newData.last();
+ targetIndexes << i;
+ }
+ }
+ QVERIFY(expectedTargetData.count() > 0);
+ }
+
+ // start animation
+ if (!newData.isEmpty()) {
+ model.insertItems(insertionIndex, newData);
+ QTRY_COMPARE(model.count(), gridview->count());
+ }
+
+ QList<QQuickItem *> targetItems = findItems<QQuickItem>(contentItem, "wrapper", targetIndexes);
+
+ if (shouldAnimateTargets) {
+ QTRY_COMPARE(gridview->property("targetTransitionsDone").toInt(), expectedTargetData.count());
+ QTRY_COMPARE(gridview->property("displaceTransitionsDone").toInt(),
+ expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0);
+
+ // check the target and displaced items were animated
+ model_targetItems_transitionFrom.matchAgainst(expectedTargetData, "wasn't animated from target 'from' pos", "shouldn't have been animated from target 'from' pos");
+ model_displacedItems_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with displaced anim", "shouldn't have been animated with displaced anim");
+
+ // check attached properties
+ matchItemsAndIndexes(gridview->property("targetTrans_items").toMap(), model, targetIndexes);
+ matchIndexLists(gridview->property("targetTrans_targetIndexes").toList(), targetIndexes);
+ matchItemLists(gridview->property("targetTrans_targetItems").toList(), targetItems);
+ if (expectedDisplacedIndexes.isValid()) {
+ // adjust expectedDisplacedIndexes to their final values after the move
+ QList<int> displacedIndexes = adjustIndexesForAddDisplaced(expectedDisplacedIndexes.indexes, insertionIndex, insertionCount);
+ matchItemsAndIndexes(gridview->property("displacedTrans_items").toMap(), model, displacedIndexes);
+ matchIndexLists(gridview->property("displacedTrans_targetIndexes").toList(), targetIndexes);
+ matchItemLists(gridview->property("displacedTrans_targetItems").toList(), targetItems);
+ }
+ } else {
+ QTRY_COMPARE(model_targetItems_transitionFrom.count(), 0);
+ QTRY_COMPARE(model_displacedItems_transitionVia.count(), 0);
+ }
+
+ QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
+ int firstVisibleIndex = -1;
+ for (int i=0; i<items.count(); i++) {
+ if (items[i]->y() >= contentY) {
+ QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
+ firstVisibleIndex = e.evaluate().toInt();
+ break;
+ }
+ }
+ QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
+
+ // verify all items moved to the correct final positions
+ int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
+ for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++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);
+ QCOMPARE(name->text(), model.name(i));
+ }
+
+ delete canvas;
+}
+
+void tst_QQuickGridView::addTransitions_data()
+{
+ QTest::addColumn<int>("initialItemCount");
+ QTest::addColumn<qreal>("contentY");
+ QTest::addColumn<bool>("shouldAnimateTargets");
+ QTest::addColumn<int>("insertionIndex");
+ QTest::addColumn<int>("insertionCount");
+ QTest::addColumn<ListRange>("expectedDisplacedIndexes");
+
+ // if inserting a full row before visible index, items don't appear or animate in, even if there are > 1 new items
+ QTest::newRow("insert 1, just before start")
+ << 30 << 20.0 << false
+ << 0 << 1 << ListRange();
+ QTest::newRow("insert 1, way before start")
+ << 30 << 20.0 << false
+ << 0 << 1 << ListRange();
+ QTest::newRow("insert multiple, just before start")
+ << 30 << 100.0 << false
+ << 0 << 3 << ListRange();
+ QTest::newRow("insert multiple (< 1 row), just before start")
+ << 30 << 100.0 << false
+ << 0 << 2 << ListRange();
+ QTest::newRow("insert multiple, way before start")
+ << 30 << 100.0 << false
+ << 0 << 3 << ListRange();
+
+ QTest::newRow("insert 1 at start")
+ << 30 << 0.0 << true
+ << 0 << 1 << ListRange(0, 17);
+ QTest::newRow("insert multiple at start")
+ << 30 << 0.0 << true
+ << 0 << 3 << ListRange(0, 17);
+ QTest::newRow("insert multiple (> 1 row) at start")
+ << 30 << 0.0 << true
+ << 0 << 5 << ListRange(0, 17);
+ QTest::newRow("insert 1 at start, content y not 0")
+ << 30 << 60.0 << true // first visible is index 3
+ << 3 << 1 << ListRange(0 + 3, 17 + 3);
+ QTest::newRow("insert multiple at start, content y not 0")
+ << 30 << 60.0 << true // first visible is index 3
+ << 3 << 3 << ListRange(0 + 3, 17 + 3);
+ QTest::newRow("insert multiple (> 1 row) at start, content y not 0")
+ << 30 << 60.0 << true // first visible is index 3
+ << 3 << 5 << ListRange(0 + 3, 17 + 3);
+
+ QTest::newRow("insert 1 at start, to empty grid")
+ << 0 << 0.0 << true
+ << 0 << 1 << ListRange();
+ QTest::newRow("insert multiple at start, to empty grid")
+ << 0 << 0.0 << true
+ << 0 << 3 << ListRange();
+
+ QTest::newRow("insert 1 at middle")
+ << 30 << 0.0 << true
+ << 7 << 1 << ListRange(7, 17);
+ QTest::newRow("insert multiple at middle")
+ << 30 << 0.0 << true
+ << 7 << 3 << ListRange(7, 17);
+ QTest::newRow("insert multiple (> 1 row) at middle")
+ << 30 << 0.0 << true
+ << 7 << 5 << ListRange(7, 17);
+
+ QTest::newRow("insert 1 at bottom")
+ << 30 << 0.0 << true
+ << 17 << 1 << ListRange(17, 17);
+ QTest::newRow("insert multiple at bottom")
+ << 30 << 0.0 << true
+ << 17 << 3 << ListRange(17, 17);
+ QTest::newRow("insert 1 at bottom, content y not 0")
+ << 30 << 20.0 * 3 << true
+ << 17 + 3 << 1 << ListRange(17 + 3, 17 + 3);
+ QTest::newRow("insert multiple at bottom, content y not 0")
+ << 30 << 20.0 * 3 << true
+ << 17 + 3 << 3 << ListRange(17 + 3, 17 + 3);
+
+
+ // items added after the last visible will not be animated in, since they
+ // do not appear in the final view
+ QTest::newRow("insert 1 after end")
+ << 30 << 0.0 << false
+ << 18 << 1 << ListRange();
+ QTest::newRow("insert multiple after end")
+ << 30 << 0.0 << false
+ << 18 << 3 << ListRange();
+}
+
+void tst_QQuickGridView::moveTransitions()
+{
+ QFETCH(int, initialItemCount);
+ QFETCH(qreal, contentY);
+ QFETCH(qreal, itemsOffsetAfterMove);
+ QFETCH(int, moveFrom);
+ QFETCH(int, moveTo);
+ QFETCH(int, moveCount);
+ QFETCH(ListRange, expectedDisplacedIndexes);
+
+ // target and displaced items should pass through these points
+ QPointF targetItems_transitionVia(-50, 50);
+ QPointF displacedItems_transitionVia(100, 100);
+
+ QaimModel model;
+ for (int i = 0; i < initialItemCount; i++)
+ model.addItem("Original item" + QString::number(i), "");
+ QaimModel model_targetItems_transitionVia;
+ QaimModel model_displacedItems_transitionVia;
+
+ QQuickView *canvas = createView();
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("model_targetItems_transitionVia", &model_targetItems_transitionVia);
+ ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
+ ctxt->setContextProperty("targetItems_transitionVia", targetItems_transitionVia);
+ ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
+ canvas->setSource(testFileUrl("moveTransitions.qml"));
+ canvas->show();
+
+ QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
+ QTRY_VERIFY(gridview != 0);
+ QQuickItem *contentItem = gridview->contentItem();
+ QVERIFY(contentItem != 0);
+ QQuickText *name;
+
+ if (contentY != 0) {
+ gridview->setContentY(contentY);
+ QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+ }
+
+ QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
+
+ // Items moving to *or* from visible positions should be animated.
+ // Otherwise, they should not be animated.
+ QList<QPair<QString, QString> > expectedTargetData;
+ QList<int> targetIndexes;
+ for (int i=moveFrom; i<moveFrom+moveCount; i++) {
+ int toIndex = moveTo + (i - moveFrom);
+ int firstVisibleIndex = (contentY / 60) * 3;
+ int lastVisibleIndex = (qCeil((contentY + gridview->height()) / 60.0)*3) - 1;
+ if ((i >= firstVisibleIndex && i <= lastVisibleIndex)
+ || (toIndex >= firstVisibleIndex && toIndex <= lastVisibleIndex)) {
+ expectedTargetData << qMakePair(model.name(i), model.number(i));
+ targetIndexes << i;
+ }
+ }
+ // ViewTransition.index provides the indices that items are moving to, not from
+ targetIndexes = adjustIndexesForMove(targetIndexes, moveFrom, moveTo, moveCount);
+
+ // start animation
+ model.moveItems(moveFrom, moveTo, moveCount);
+
+ QTRY_COMPARE(gridview->property("targetTransitionsDone").toInt(), expectedTargetData.count());
+ QTRY_COMPARE(gridview->property("displaceTransitionsDone").toInt(),
+ expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0);
+
+ QList<QQuickItem *> targetItems = findItems<QQuickItem>(contentItem, "wrapper", targetIndexes);
+
+ // check the target and displaced items were animated
+ model_targetItems_transitionVia.matchAgainst(expectedTargetData, "wasn't animated from target 'from' pos", "shouldn't have been animated from target 'from' pos");
+ model_displacedItems_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with displaced anim", "shouldn't have been animated with displaced anim");
+
+ // check attached properties
+ matchItemsAndIndexes(gridview->property("targetTrans_items").toMap(), model, targetIndexes);
+ matchIndexLists(gridview->property("targetTrans_targetIndexes").toList(), targetIndexes);
+ matchItemLists(gridview->property("targetTrans_targetItems").toList(), targetItems);
+ if (expectedDisplacedIndexes.isValid()) {
+ // adjust expectedDisplacedIndexes to their final values after the move
+ QList<int> displacedIndexes = adjustIndexesForMove(expectedDisplacedIndexes.indexes, moveFrom, moveTo, moveCount);
+ matchItemsAndIndexes(gridview->property("displacedTrans_items").toMap(), model, displacedIndexes);
+ matchIndexLists(gridview->property("displacedTrans_targetIndexes").toList(), targetIndexes);
+ matchItemLists(gridview->property("displacedTrans_targetItems").toList(), targetItems);
+ }
+
+ QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
+ int firstVisibleIndex = -1;
+ for (int i=0; i<items.count(); i++) {
+ if (items[i]->y() >= contentY) {
+ QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
+ firstVisibleIndex = e.evaluate().toInt();
+ break;
+ }
+ }
+ QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
+
+ // verify all items moved to the correct final positions
+ int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
+ for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++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 + itemsOffsetAfterMove);
+ name = findItem<QQuickText>(contentItem, "textName", i);
+ QVERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ }
+
+ delete canvas;
+}
+
+void tst_QQuickGridView::moveTransitions_data()
+{
+ QTest::addColumn<int>("initialItemCount");
+ QTest::addColumn<qreal>("contentY");
+ QTest::addColumn<qreal>("itemsOffsetAfterMove");
+ QTest::addColumn<int>("moveFrom");
+ QTest::addColumn<int>("moveTo");
+ QTest::addColumn<int>("moveCount");
+ QTest::addColumn<ListRange>("expectedDisplacedIndexes");
+
+ QTest::newRow("move from above view, outside visible items, move 1") << 30 << 120.0 << 0.0
+ << 1 << 10 << 1 << ListRange(6, 10);
+ QTest::newRow("move from above view, outside visible items, move 1 (first item)") << 30 << 120.0 << 0.0
+ << 0 << 10 << 1 << ListRange(6, 10);
+ QTest::newRow("move from above view, outside visible items, move multiple") << 30 << 120.0 << 60.0
+ << 1 << 10 << 3 << ListRange(13, 23);
+ QTest::newRow("move from above view, mix of visible/non-visible") << 30 << 120.0 << 60.0
+ << 1 << 10 << 6 << (ListRange(7, 15) + ListRange(16, 23));
+ QTest::newRow("move from above view, mix of visible/non-visible (move first)") << 30 << 120.0 << 120.0
+ << 0 << 10 << 6 << ListRange(16, 23);
+
+ QTest::newRow("move within view, move 1 down") << 30 << 0.0 << 0.0
+ << 1 << 10 << 1 << ListRange(2, 10);
+ QTest::newRow("move within view, move 1 down, move first item") << 30 << 0.0 << 0.0
+ << 0 << 10 << 1 << ListRange(1, 10);
+ QTest::newRow("move within view, move 1 down, move first item, contentY not 0") << 30 << 120.0 << 0.0
+ << 0+6 << 10+6 << 1 << ListRange(1+6, 10+6);
+ QTest::newRow("move within view, move 1 down, to last item") << 30 << 0.0 << 0.0
+ << 10 << 17 << 1 << ListRange(11, 17);
+ QTest::newRow("move within view, move first->last") << 30 << 0.0 << 0.0
+ << 0 << 17 << 1 << ListRange(1, 17);
+
+ QTest::newRow("move within view, move multiple down") << 30 << 0.0 << 0.0
+ << 1 << 10 << 3 << ListRange(4, 12);
+ QTest::newRow("move within view, move multiple down, move first item") << 30 << 0.0 << 0.0
+ << 0 << 10 << 3 << ListRange(3, 12);
+ QTest::newRow("move within view, move multiple down, move first item, contentY not 0") << 30 << 60.0 << 0.0
+ << 0+3 << 10+3 << 3 << ListRange(3+3, 12+3);
+ QTest::newRow("move within view, move multiple down, displace last item") << 30 << 0.0 << 0.0
+ << 5 << 15 << 3 << ListRange(8, 17);
+ QTest::newRow("move within view, move multiple down, move first->last") << 30 << 0.0 << 0.0
+ << 0 << 15 << 3 << ListRange(3, 17);
+
+ QTest::newRow("move within view, move 1 up") << 30 << 0.0 << 0.0
+ << 10 << 1 << 1 << ListRange(1, 9);
+ QTest::newRow("move within view, move 1 up, move to first index") << 30 << 0.0 << 0.0
+ << 10 << 0 << 1 << ListRange(0, 9);
+ QTest::newRow("move within view, move 1 up, move to first index, contentY not 0") << 30 << 120.0 << 0.0
+ << 10+6 << 0+6 << 1 << ListRange(0+6, 9+6);
+ QTest::newRow("move within view, move 1 up, move to first index, contentY not on item border") << 30 << 80.0 << 0.0
+ << 10+3 << 0+3 << 1 << ListRange(0+3, 9+3);
+ QTest::newRow("move within view, move 1 up, move last item") << 30 << 0.0 << 0.0
+ << 17 << 10 << 1 << ListRange(10, 16);
+ QTest::newRow("move within view, move 1 up, move last->first") << 30 << 0.0 << 0.0
+ << 17 << 0 << 1 << ListRange(0, 16);
+
+ QTest::newRow("move within view, move multiple up") << 30 << 0.0 << 0.0
+ << 10 << 1 << 3 << ListRange(1, 9);
+ QTest::newRow("move within view, move multiple (> 1 row) up") << 30 << 0.0 << 0.0
+ << 10 << 1 << 5 << ListRange(1, 9);
+ QTest::newRow("move within view, move multiple up, move to first index") << 30 << 0.0 << 0.0
+ << 10 << 0 << 3 << ListRange(0, 9);
+ QTest::newRow("move within view, move multiple up, move to first index, contentY not 0") << 30 << 60.0 << 0.0
+ << 10+3 << 0+3 << 3 << ListRange(0+3, 9+3);
+ QTest::newRow("move within view, move multiple up (> 1 row), move to first index, contentY not on border") << 30 << 80.0 << 0.0
+ << 10+3 << 0+3 << 5 << ListRange(0+3, 9+3);
+ QTest::newRow("move within view, move multiple up, move last item") << 30 << 0.0 << 0.0
+ << 15 << 5 << 3 << ListRange(5, 14);
+ QTest::newRow("move within view, move multiple up, move last->first") << 30 << 0.0 << 0.0
+ << 15 << 0 << 3 << ListRange(0, 14);
+
+ QTest::newRow("move from below view, move 1 up") << 30 << 0.0 << 0.0
+ << 20 << 5 << 1 << ListRange(5, 17);
+ QTest::newRow("move from below view, move 1 up, move to top") << 30 << 0.0 << 0.0
+ << 20 << 0 << 1 << ListRange(0, 17);
+ QTest::newRow("move from below view, move 1 up, move to top, contentY not 0") << 30 << 60.0 << 0.0
+ << 25 << 3 << 1 << ListRange(0+3, 17+3);
+ QTest::newRow("move from below view, move multiple (> 1 row) up") << 30 << 0.0 << 0.0
+ << 20 << 5 << 5 << ListRange(5, 17);
+ QTest::newRow("move from below view, move multiple up, move to top") << 30 << 0.0 << 0.0
+ << 20 << 0 << 3 << ListRange(0, 17);
+ QTest::newRow("move from below view, move multiple up, move to top, contentY not 0") << 30 << 60.0 << 0.0
+ << 25 << 3 << 3 << ListRange(0+3, 17+3);
+
+ QTest::newRow("move from below view, move 1 up, move to bottom") << 30 << 0.0 << 0.0
+ << 20 << 17 << 1 << ListRange(17, 17);
+ QTest::newRow("move from below view, move 1 up, move to bottom, contentY not 0") << 30 << 60.0 << 0.0
+ << 25 << 17+3 << 1 << ListRange(17+3, 17+3);
+ QTest::newRow("move from below view, move multiple up, move to to bottom") << 30 << 0.0 << 0.0
+ << 20 << 17 << 3 << ListRange(17, 17);
+ QTest::newRow("move from below view, move multiple up, move to bottom, contentY not 0") << 30 << 60.0 << 0.0
+ << 25 << 17+3 << 3 << ListRange(17+3, 17+3);
+}
+
+void tst_QQuickGridView::removeTransitions()
+{
+ QFETCH(int, initialItemCount);
+ QFETCH(bool, shouldAnimateTargets);
+ QFETCH(qreal, contentY);
+ QFETCH(int, removalIndex);
+ QFETCH(int, removalCount);
+ QFETCH(ListRange, expectedDisplacedIndexes);
+
+ // added items should end here
+ QPointF targetItems_transitionTo(-50, -50);
+
+ // displaced items should pass through this points
+ QPointF displacedItems_transitionVia(100, 100);
+
+ QaimModel model;
+ for (int i = 0; i < initialItemCount; i++)
+ model.addItem("Original item" + QString::number(i), "");
+ QaimModel model_targetItems_transitionTo;
+ QaimModel model_displacedItems_transitionVia;
+
+ QQuickView *canvas = createView();
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("model_targetItems_transitionTo", &model_targetItems_transitionTo);
+ ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
+ ctxt->setContextProperty("targetItems_transitionTo", targetItems_transitionTo);
+ ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
+ canvas->setSource(testFileUrl("removeTransitions.qml"));
+ canvas->show();
+
+ 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);
+
+ if (contentY != 0) {
+ gridview->setContentY(contentY);
+ QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+ }
+
+ QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
+
+ // only target items that are visible should be animated
+ QList<QPair<QString, QString> > expectedTargetData;
+ QList<int> targetIndexes;
+ if (shouldAnimateTargets) {
+ for (int i=removalIndex; i<removalIndex+removalCount; i++) {
+ int firstVisibleIndex = (contentY / 60.0)*3;
+ int lastVisibleIndex = (qCeil((contentY + gridview->height()) / 60.0)*3) - 1;
+ if (i >= firstVisibleIndex && i <= lastVisibleIndex) {
+ expectedTargetData << qMakePair(model.name(i), model.number(i));
+ targetIndexes << i;
+ }
+ }
+ QVERIFY(expectedTargetData.count() > 0);
+ }
+
+ // calculate targetItems and expectedTargets before model changes
+ QList<QQuickItem *> targetItems = findItems<QQuickItem>(contentItem, "wrapper", targetIndexes);
+ QVariantMap expectedTargets;
+ for (int i=0; i<targetIndexes.count(); i++)
+ expectedTargets[model.name(targetIndexes[i])] = targetIndexes[i];
+
+ // start animation
+ model.removeItems(removalIndex, removalCount);
+ QTRY_COMPARE(model.count(), gridview->count());
+
+ if (shouldAnimateTargets || expectedDisplacedIndexes.isValid()) {
+ QTRY_COMPARE(gridview->property("targetTransitionsDone").toInt(), expectedTargetData.count());
+ QTRY_COMPARE(gridview->property("displaceTransitionsDone").toInt(),
+ expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0);
+
+ // check the target and displaced items were animated
+ model_targetItems_transitionTo.matchAgainst(expectedTargetData, "wasn't animated to target 'to' pos", "shouldn't have been animated to target 'to' pos");
+ model_displacedItems_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with displaced anim", "shouldn't have been animated with displaced anim");
+
+ // check attached properties
+ QCOMPARE(gridview->property("targetTrans_items").toMap(), expectedTargets);
+ matchIndexLists(gridview->property("targetTrans_targetIndexes").toList(), targetIndexes);
+ matchItemLists(gridview->property("targetTrans_targetItems").toList(), targetItems);
+ if (expectedDisplacedIndexes.isValid()) {
+ // adjust expectedDisplacedIndexes to their final values after the move
+ QList<int> displacedIndexes = adjustIndexesForRemoveDisplaced(expectedDisplacedIndexes.indexes, removalIndex, removalCount);
+ matchItemsAndIndexes(gridview->property("displacedTrans_items").toMap(), model, displacedIndexes);
+ matchIndexLists(gridview->property("displacedTrans_targetIndexes").toList(), targetIndexes);
+ matchItemLists(gridview->property("displacedTrans_targetItems").toList(), targetItems);
+ }
+ } else {
+ QTRY_COMPARE(model_targetItems_transitionTo.count(), 0);
+ QTRY_COMPARE(model_displacedItems_transitionVia.count(), 0);
+ }
+
+ QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
+ int itemCount = items.count();
+ int firstVisibleIndex = -1;
+ for (int i=0; i<items.count(); i++) {
+ QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
+ int index = e.evaluate().toInt();
+ if (firstVisibleIndex < 0 && items[i]->y() >= contentY)
+ firstVisibleIndex = index;
+ else if (index < 0)
+ itemCount--; // exclude deleted items
+ }
+ QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
+
+ // verify all items moved to the correct final positions
+ for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++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(), contentY + ((i-firstVisibleIndex)/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::removeTransitions_data()
+{
+ QTest::addColumn<int>("initialItemCount");
+ QTest::addColumn<qreal>("contentY");
+ QTest::addColumn<bool>("shouldAnimateTargets");
+ QTest::addColumn<int>("removalIndex");
+ QTest::addColumn<int>("removalCount");
+ QTest::addColumn<ListRange>("expectedDisplacedIndexes");
+
+ // All items that are visible following the remove operation should be animated.
+ // Remove targets that are outside of the view should not be animated.
+
+ // For a GridView, removing any number of items other than a full row before the start
+ // should displace all items in the view
+ QTest::newRow("remove 1 before start")
+ << 30 << 120.0 << false
+ << 2 << 1 << ListRange(6, 24); // 6-24 are displaced
+ QTest::newRow("remove 1 row, before start")
+ << 30 << 120.0 << false
+ << 3 << 3 << ListRange();
+ QTest::newRow("remove between 1-2 rows, before start")
+ << 30 << 120.0 << false
+ << 0 << 5 << ListRange(6, 25);
+ QTest::newRow("remove 2 rows, before start")
+ << 30 << 120.0 << false
+ << 0 << 6 << ListRange();
+ QTest::newRow("remove mix of before and after start")
+ << 30 << 60.0 << true
+ << 2 << 3 << ListRange(5, 23); // 5-23 are displaced into view
+
+
+ QTest::newRow("remove 1 from start")
+ << 30 << 0.0 << true
+ << 0 << 1 << ListRange(1, 18); // 1-18 are displaced into view
+ QTest::newRow("remove multiple from start")
+ << 30 << 0.0 << true
+ << 0 << 3 << ListRange(3, 20); // 3-18 are displaced into view
+ QTest::newRow("remove 1 from start, content y not 0")
+ << 30 << 60.0 << true
+ << 3 << 1 << ListRange(1 + 3, 18 + 3);
+ QTest::newRow("remove multiple from start, content y not 0")
+ << 30 << 60.0 << true
+ << 3 << 3 << ListRange(3 + 3, 20 + 3);
+
+
+ QTest::newRow("remove 1 from middle")
+ << 30 << 0.0 << true
+ << 5 << 1 << ListRange(6, 18);
+ QTest::newRow("remove multiple from middle")
+ << 30 << 0.0 << true
+ << 5 << 3 << ListRange(8, 20);
+
+
+ QTest::newRow("remove 1 from bottom")
+ << 30 << 0.0 << true
+ << 17 << 1 << ListRange(18, 18);
+ QTest::newRow("remove multiple (1 row) from bottom")
+ << 30 << 0.0 << true
+ << 15 << 3 << ListRange(18, 20);
+ QTest::newRow("remove multiple (> 1 row) from bottom")
+ << 30 << 0.0 << true
+ << 15 << 5 << ListRange(20, 22);
+ QTest::newRow("remove 1 from bottom, content y not 0")
+ << 30 << 60.0 << true
+ << 17 + 3 << 1 << ListRange(18 + 3, 18 + 3);
+ QTest::newRow("remove multiple (1 row) from bottom, content y not 0")
+ << 30 << 60.0 << true
+ << 15 + 3 << 3 << ListRange(18 + 3, 20 + 3);
+
+
+ QTest::newRow("remove 1 after end")
+ << 30 << 0.0 << false
+ << 18 << 1 << ListRange();
+ QTest::newRow("remove multiple after end")
+ << 30 << 0.0 << false
+ << 18 << 3 << ListRange();
+}
+
+void tst_QQuickGridView::multipleTransitions()
+{
+ // Tests that if you interrupt a transition in progress with another action that
+ // cancels the previous transition, the resulting items are still placed correctly.
+
+ QFETCH(int, initialCount);
+ QFETCH(qreal, contentY);
+ QFETCH(QList<ListChange>, changes);
+
+ // add transitions on the left, moves on the right
+ QPointF addTargets_transitionFrom(-50, -50);
+ QPointF addDisplaced_transitionFrom(-50, 50);
+ QPointF moveTargets_transitionFrom(50, -50);
+ QPointF moveDisplaced_transitionFrom(50, 50);
+
+ QmlListModel model;
+ for (int i = 0; i < initialCount; i++)
+ model.addItem("Original item" + QString::number(i), "");
+
+ QQuickView *canvas = createView();
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("addTargets_transitionFrom", addTargets_transitionFrom);
+ ctxt->setContextProperty("addDisplaced_transitionFrom", addDisplaced_transitionFrom);
+ ctxt->setContextProperty("moveTargets_transitionFrom", moveTargets_transitionFrom);
+ ctxt->setContextProperty("moveDisplaced_transitionFrom", moveDisplaced_transitionFrom);
+ canvas->setSource(testFileUrl("multipleTransitions.qml"));
+ canvas->show();
+
+ 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);
+
+ int timeBetweenActions = canvas->rootObject()->property("timeBetweenActions").toInt();
+
+ QList<QPair<QString, QString> > targetItems;
+ for (int i=0; i<changes.count(); i++) {
+ switch (changes[i].type) {
+ case ListChange::Inserted:
+ {
+ for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
+ targetItems << qMakePair(QString("new item %1").arg(j), QString::number(j));
+ model.insertItems(changes[i].index, targetItems);
+ QTRY_COMPARE(model.count(), gridview->count());
+ QTRY_VERIFY(gridview->property("runningAddTargets").toBool());
+ QTRY_VERIFY(gridview->property("runningAddDisplaced").toBool());
+ if (i == changes.count() - 1) {
+ QTRY_VERIFY(!gridview->property("runningAddTargets").toBool());
+ QTRY_VERIFY(!gridview->property("runningAddDisplaced").toBool());
+ } else {
+ QTest::qWait(timeBetweenActions);
+ }
+ break;
+ }
+ case ListChange::Removed:
+ for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
+ targetItems << qMakePair(model.name(i), model.number(i));
+ model.removeItems(changes[i].index, changes[i].count);
+ QTRY_COMPARE(model.count(), gridview->count());
+ QTRY_VERIFY(gridview->property("runningRemoveTargets").toBool());
+ QTRY_VERIFY(gridview->property("runningRemoveDisplaced").toBool());
+ if (i == changes.count() - 1) {
+ QTRY_VERIFY(!gridview->property("runningRemoveTargets").toBool());
+ QTRY_VERIFY(!gridview->property("runningRemoveDisplaced").toBool());
+ } else {
+ QTest::qWait(timeBetweenActions);
+ }
+ break;
+ case ListChange::Moved:
+ for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
+ targetItems << qMakePair(model.name(i), model.number(i));
+ model.moveItems(changes[i].index, changes[i].to, changes[i].count);
+ QTRY_VERIFY(gridview->property("runningMoveTargets").toBool());
+ QTRY_VERIFY(gridview->property("runningMoveDisplaced").toBool());
+ if (i == changes.count() - 1) {
+ QTRY_VERIFY(!gridview->property("runningMoveTargets").toBool());
+ QTRY_VERIFY(!gridview->property("runningMoveDisplaced").toBool());
+ } else {
+ QTest::qWait(timeBetweenActions);
+ }
+ break;
+ case ListChange::SetCurrent:
+ gridview->setCurrentIndex(changes[i].index);
+ QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+ break;
+ case ListChange::SetContentY:
+ gridview->setContentY(changes[i].pos);
+ QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
+ break;
+ }
+ }
+ QCOMPARE(gridview->count(), model.count());
+
+ QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
+ int firstVisibleIndex = -1;
+ for (int i=0; i<items.count(); i++) {
+ if (items[i]->y() >= contentY) {
+ QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
+ firstVisibleIndex = e.evaluate().toInt();
+ break;
+ }
+ }
+ QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
+
+ // verify all items moved to the correct final positions
+ int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
+ for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++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::multipleTransitions_data()
+{
+ QTest::addColumn<int>("initialCount");
+ QTest::addColumn<qreal>("contentY");
+ QTest::addColumn<QList<ListChange> >("changes");
+
+ // the added item and displaced items should move to final dest correctly
+ QTest::newRow("add item, then move it immediately") << 10 << 0.0 << (QList<ListChange>()
+ << ListChange::insert(0, 1)
+ << ListChange::move(0, 3, 1)
+ );
+
+ // items affected by the add should change from move to add transition
+ QTest::newRow("move, then insert item before the moved item") << 20 << 0.0 << (QList<ListChange>()
+ << ListChange::move(1, 10, 3)
+ << ListChange::insert(0, 1)
+ );
+
+ // items should be placed correctly if you trigger a transition then refill for that index
+ QTest::newRow("add at 0, flick down, flick back to top and add at 0 again") << 20 << 0.0 << (QList<ListChange>()
+ << ListChange::insert(0, 1)
+ << ListChange::setContentY(160.0)
+ << ListChange::setContentY(0.0)
+ << ListChange::insert(0, 1)
+ );
+}
+
void tst_QQuickGridView::cacheBuffer()
{
QQuickView *canvas = createView();
@@ -4085,6 +5006,56 @@ void tst_QQuickGridView::unrequestedVisibility()
delete canvas;
}
+QList<int> tst_QQuickGridView::toIntList(const QVariantList &list)
+{
+ QList<int> ret;
+ bool ok = true;
+ for (int i=0; i<list.count(); i++) {
+ ret << list[i].toInt(&ok);
+ if (!ok)
+ qWarning() << "tst_QQuickGridView::toIntList(): not a number:" << list[i];
+ }
+
+ return ret;
+}
+
+void tst_QQuickGridView::matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes)
+{
+ for (int i=0; i<indexLists.count(); i++) {
+ QSet<int> current = indexLists[i].value<QList<int> >().toSet();
+ if (current != expectedIndexes.toSet())
+ qDebug() << "Cannot match actual targets" << current << "with expected" << expectedIndexes;
+ QCOMPARE(current, expectedIndexes.toSet());
+ }
+}
+
+void tst_QQuickGridView::matchItemsAndIndexes(const QVariantMap &items, const QaimModel &model, const QList<int> &expectedIndexes)
+{
+ for (QVariantMap::const_iterator it = items.begin(); it != items.end(); ++it) {
+ QVERIFY(it.value().type() == QVariant::Int);
+ QString name = it.key();
+ int itemIndex = it.value().toInt();
+ QVERIFY2(expectedIndexes.contains(itemIndex), QTest::toString(QString("Index %1 not found in expectedIndexes").arg(itemIndex)));
+ if (model.name(itemIndex) != name)
+ qDebug() << itemIndex;
+ QCOMPARE(model.name(itemIndex), name);
+ }
+ QCOMPARE(items.count(), expectedIndexes.count());
+}
+
+void tst_QQuickGridView::matchItemLists(const QVariantList &itemLists, const QList<QQuickItem *> &expectedItems)
+{
+ for (int i=0; i<itemLists.count(); i++) {
+ QVariantList current = itemLists[i].toList();
+ for (int j=0; j<current.count(); j++) {
+ QQuickItem *o = qobject_cast<QQuickItem*>(current[j].value<QObject*>());
+ QVERIFY2(o, QTest::toString(QString("Invalid actual item at %1").arg(j)));
+ QVERIFY2(expectedItems.contains(o), QTest::toString(QString("Cannot match item %1").arg(j)));
+ }
+ QCOMPARE(current.count(), expectedItems.count());
+ }
+}
+
QTEST_MAIN(tst_QQuickGridView)
#include "tst_qquickgridview.moc"
diff --git a/tests/auto/qtquick2/qquicklistview/data/addTransitions.qml b/tests/auto/qtquick2/qquicklistview/data/addTransitions.qml
new file mode 100644
index 0000000000..ff90ead8a6
--- /dev/null
+++ b/tests/auto/qtquick2/qquicklistview/data/addTransitions.qml
@@ -0,0 +1,134 @@
+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) == targetItems_transitionFrom)
+ model_targetItems_transitionFrom.addItem(name, "")
+ if (Qt.point(x, y) == displacedItems_transitionVia)
+ model_displacedItems_transitionVia.addItem(name, "")
+ }
+ }
+ }
+
+ ListView {
+ id: list
+
+ property int targetTransitionsDone
+ property int displaceTransitionsDone
+
+ property var targetTrans_items: new Object()
+ property var targetTrans_targetIndexes: new Array()
+ property var targetTrans_targetItems: new Array()
+
+ property var displacedTrans_items: new Object()
+ property var displacedTrans_targetIndexes: new Array()
+ property var displacedTrans_targetItems: new Array()
+
+ objectName: "list"
+ focus: true
+ anchors.centerIn: parent
+ width: 240
+ height: 320
+ model: testModel
+ delegate: myDelegate
+
+ // for QDeclarativeListProperty types
+ function copyList(propList) {
+ var temp = new Array()
+ for (var i=0; i<propList.length; i++)
+ temp.push(propList[i])
+ return temp
+ }
+
+ add: Transition {
+ id: targetTransition
+
+ SequentialAnimation {
+ ScriptAction {
+ script: {
+ list.targetTrans_items[targetTransition.ViewTransition.item.nameData] = targetTransition.ViewTransition.index
+ list.targetTrans_targetIndexes.push(targetTransition.ViewTransition.targetIndexes)
+ list.targetTrans_targetItems.push(list.copyList(targetTransition.ViewTransition.targetItems))
+ }
+ }
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; from: targetItems_transitionFrom.x; duration: root.duration }
+ NumberAnimation { properties: "y"; from: targetItems_transitionFrom.y; duration: root.duration }
+ }
+
+ ScriptAction { script: list.targetTransitionsDone += 1 }
+ }
+ }
+
+ addDisplaced: Transition {
+ id: displaced
+
+ SequentialAnimation {
+ ScriptAction {
+ script: {
+ list.displacedTrans_items[displaced.ViewTransition.item.nameData] = displaced.ViewTransition.index
+ list.displacedTrans_targetIndexes.push(displaced.ViewTransition.targetIndexes)
+ list.displacedTrans_targetItems.push(list.copyList(displaced.ViewTransition.targetItems))
+ }
+ }
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; duration: root.duration; to: displacedItems_transitionVia.x }
+ NumberAnimation { properties: "y"; duration: root.duration; to: displacedItems_transitionVia.y }
+ }
+ NumberAnimation { properties: "x,y"; duration: root.duration }
+
+ ScriptAction { script: list.displaceTransitionsDone += 1 }
+ }
+
+ }
+ }
+
+ Rectangle {
+ anchors.fill: list
+ color: "lightsteelblue"
+ opacity: 0.2
+ }
+
+ // XXX will it pass without these if I just wait for polish?
+ // check all of these tests - if not, then mark this bit with the bug number!
+ Rectangle {
+ anchors.bottom: parent.bottom
+ width: 20; height: 20
+ color: "white"
+ NumberAnimation on x { loops: Animation.Infinite; from: 0; to: 300; duration: 100000 }
+ }
+}
+
diff --git a/tests/auto/qtquick2/qquicklistview/data/moveTransitions.qml b/tests/auto/qtquick2/qquicklistview/data/moveTransitions.qml
new file mode 100644
index 0000000000..744db3110e
--- /dev/null
+++ b/tests/auto/qtquick2/qquicklistview/data/moveTransitions.qml
@@ -0,0 +1,141 @@
+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) == targetItems_transitionVia)
+ model_targetItems_transitionVia.addItem(name, "")
+ if (Qt.point(x, y) == displacedItems_transitionVia)
+ model_displacedItems_transitionVia.addItem(name, "")
+ }
+ }
+ }
+
+ ListView {
+ id: list
+
+ property int targetTransitionsDone
+ property int displaceTransitionsDone
+
+ property var targetTrans_items: new Object()
+ property var targetTrans_targetIndexes: new Array()
+ property var targetTrans_targetItems: new Array()
+
+ property var displacedTrans_items: new Object()
+ property var displacedTrans_targetIndexes: new Array()
+ property var displacedTrans_targetItems: new Array()
+
+ objectName: "list"
+ focus: true
+ anchors.centerIn: parent
+ width: 240
+ height: 320
+ model: testModel
+ delegate: myDelegate
+
+ // for QDeclarativeListProperty types
+ function copyList(propList) {
+ var temp = new Array()
+ for (var i=0; i<propList.length; i++)
+ temp.push(propList[i])
+ return temp
+ }
+
+ move: Transition {
+ id: targetTransition
+
+ SequentialAnimation {
+ ScriptAction {
+ script: {
+ list.targetTrans_items[targetTransition.ViewTransition.item.nameData] = targetTransition.ViewTransition.index
+ list.targetTrans_targetIndexes.push(targetTransition.ViewTransition.targetIndexes)
+ list.targetTrans_targetItems.push(list.copyList(targetTransition.ViewTransition.targetItems))
+ }
+ }
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; to: targetItems_transitionVia.x; duration: root.duration }
+ NumberAnimation { properties: "y"; to: targetItems_transitionVia.y; duration: root.duration }
+ }
+
+ NumberAnimation { properties: "x,y"; duration: root.duration }
+
+ ScriptAction { script: list.targetTransitionsDone += 1 }
+ }
+ }
+
+ moveDisplaced: Transition {
+ id: displaced
+
+ SequentialAnimation {
+ ScriptAction {
+ script: {
+ list.displacedTrans_items[displaced.ViewTransition.item.nameData] = displaced.ViewTransition.index
+ list.displacedTrans_targetIndexes.push(displaced.ViewTransition.targetIndexes)
+ list.displacedTrans_targetItems.push(list.copyList(displaced.ViewTransition.targetItems))
+ }
+ }
+ ParallelAnimation {
+ NumberAnimation {
+ properties: "x"; duration: root.duration
+ to: displacedItems_transitionVia.x
+ }
+ NumberAnimation {
+ properties: "y"; duration: root.duration
+ to: displacedItems_transitionVia.y
+ }
+ }
+ NumberAnimation { properties: "x,y"; duration: root.duration }
+
+ ScriptAction { script: list.displaceTransitionsDone += 1 }
+ }
+
+ }
+ }
+
+ Rectangle {
+ anchors.fill: list
+ color: "lightsteelblue"
+ opacity: 0.2
+ }
+
+ Rectangle {
+ anchors.bottom: parent.bottom
+ width: 20; height: 20
+ color: "white"
+ NumberAnimation on x { loops: Animation.Infinite; from: 0; to: 300; duration: 10000 }
+ }
+}
+
+
diff --git a/tests/auto/qtquick2/qquicklistview/data/multipleTransitions.qml b/tests/auto/qtquick2/qquicklistview/data/multipleTransitions.qml
new file mode 100644
index 0000000000..50ffbc53c3
--- /dev/null
+++ b/tests/auto/qtquick2/qquicklistview/data/multipleTransitions.qml
@@ -0,0 +1,121 @@
+import QtQuick 2.0
+
+Rectangle {
+ id: root
+ width: 500
+ height: 600
+
+ // time to pause between each add, remove, etc.
+ // (obviously, must be less than 'duration' value to actually test that
+ // interrupting transitions will still produce the correct result)
+ property int timeBetweenActions: duration / 2
+
+ property int duration: 100
+
+ property int count: list.count
+
+ Component {
+ id: myDelegate
+ Rectangle {
+ id: wrapper
+ 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"
+ }
+ }
+
+ ListView {
+ id: list
+
+ property bool populateDone
+
+ property bool runningAddTargets: false
+ property bool runningAddDisplaced: false
+ property bool runningMoveTargets: false
+ property bool runningMoveDisplaced: false
+
+ objectName: "list"
+ focus: true
+ anchors.centerIn: parent
+ width: 240
+ height: 320
+ model: testModel
+ delegate: myDelegate
+
+ add: Transition {
+ id: addTargets
+ SequentialAnimation {
+ ScriptAction { script: list.runningAddTargets = true }
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; from: addTargets_transitionFrom.x; duration: root.duration }
+ NumberAnimation { properties: "y"; from: addTargets_transitionFrom.y; duration: root.duration }
+ }
+ ScriptAction { script: list.runningAddTargets = false }
+ }
+ }
+
+ addDisplaced: Transition {
+ id: addDisplaced
+ SequentialAnimation {
+ ScriptAction { script: list.runningAddDisplaced = true }
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; from: addDisplaced_transitionFrom.x; duration: root.duration }
+ NumberAnimation { properties: "y"; from: addDisplaced_transitionFrom.y; duration: root.duration }
+ }
+ ScriptAction { script: list.runningAddDisplaced = false }
+ }
+ }
+
+ move: Transition {
+ id: moveTargets
+ SequentialAnimation {
+ ScriptAction { script: list.runningMoveTargets = true }
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; from: moveTargets_transitionFrom.x; duration: root.duration }
+ NumberAnimation { properties: "y"; from: moveTargets_transitionFrom.y; duration: root.duration }
+ }
+ ScriptAction { script: list.runningMoveTargets = false }
+ }
+ }
+
+ moveDisplaced: Transition {
+ id: moveDisplaced
+ SequentialAnimation {
+ ScriptAction { script: list.runningMoveDisplaced = true }
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; from: moveDisplaced_transitionFrom.x; duration: root.duration }
+ NumberAnimation { properties: "y"; from: moveDisplaced_transitionFrom.y; duration: root.duration }
+ }
+ ScriptAction { script: list.runningMoveDisplaced = false }
+ }
+ }
+ }
+
+ Rectangle {
+ anchors.fill: list
+ color: "lightsteelblue"
+ opacity: 0.2
+ }
+
+ Rectangle {
+ anchors.bottom: parent.bottom
+ width: 20; height: 20
+ color: "white"
+ NumberAnimation on x { loops: Animation.Infinite; from: 0; to: 300; duration: 100000 }
+ }
+}
+
+
+
diff --git a/tests/auto/qtquick2/qquicklistview/data/populateTransitions.qml b/tests/auto/qtquick2/qquicklistview/data/populateTransitions.qml
new file mode 100644
index 0000000000..0994e0943d
--- /dev/null
+++ b/tests/auto/qtquick2/qquicklistview/data/populateTransitions.qml
@@ -0,0 +1,102 @@
+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
+ 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) == transitionFrom)
+ model_transitionFrom.addItem(name, "")
+ if (Qt.point(x, y) == transitionVia) {
+ model_transitionVia.addItem(name, "")
+ }
+ }
+ }
+ }
+
+ ListView {
+ id: list
+
+ property int countPopulateTransitions
+ property int countAddTransitions
+
+ objectName: "list"
+ focus: true
+ anchors.centerIn: parent
+ width: 240
+ height: 320
+ model: testModel
+ delegate: myDelegate
+
+ populate: usePopulateTransition ? popTransition : null
+
+ add: Transition {
+ SequentialAnimation {
+ ScriptAction { script: list.countAddTransitions += 1 }
+ NumberAnimation { properties: "x,y"; duration: root.duration }
+ }
+ }
+ }
+
+ Transition {
+ id: popTransition
+ SequentialAnimation {
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; from: transitionFrom.x; to: transitionVia.x; duration: root.duration }
+ NumberAnimation { properties: "y"; from: transitionFrom.y; to: transitionVia.y; duration: root.duration }
+ }
+ NumberAnimation { properties: "x,y"; duration: root.duration }
+ ScriptAction { script: list.countPopulateTransitions += 1 }
+ }
+ }
+
+
+ Rectangle {
+ anchors.fill: list
+ color: "lightsteelblue"
+ opacity: 0.2
+ }
+
+ Component.onCompleted: {
+ if (dynamicallyPopulate) {
+ for (var i=0; i<30; i++)
+ testModel.addItem("item " + i, "")
+ }
+ }
+
+ Rectangle {
+ anchors.bottom: parent.bottom
+ width: 20; height: 20
+ color: "white"
+ NumberAnimation on x { loops: Animation.Infinite; from: 0; to: 300; duration: 100000 }
+ }
+}
+
+
diff --git a/tests/auto/qtquick2/qquicklistview/data/removeTransitions.qml b/tests/auto/qtquick2/qquicklistview/data/removeTransitions.qml
new file mode 100644
index 0000000000..95f76f0200
--- /dev/null
+++ b/tests/auto/qtquick2/qquicklistview/data/removeTransitions.qml
@@ -0,0 +1,144 @@
+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) == targetItems_transitionTo) {
+ model_targetItems_transitionTo.addItem(nameData, "") // name is invalid once model removes the item
+ }
+ if (Qt.point(x, y) == displacedItems_transitionVia) {
+ model_displacedItems_transitionVia.addItem(name, "")
+ }
+ }
+ }
+ }
+
+ ListView {
+ id: list
+
+ property int targetTransitionsDone
+ property int displaceTransitionsDone
+
+ property var targetTrans_items: new Object()
+ property var targetTrans_targetIndexes: new Array()
+ property var targetTrans_targetItems: new Array()
+
+ property var displacedTrans_items: new Object()
+ property var displacedTrans_targetIndexes: new Array()
+ property var displacedTrans_targetItems: new Array()
+
+ objectName: "list"
+ focus: true
+ anchors.centerIn: parent
+ width: 240
+ height: 320
+ model: testModel
+ delegate: myDelegate
+
+ // for QDeclarativeListProperty types
+ function copyList(propList) {
+ var temp = new Array()
+ for (var i=0; i<propList.length; i++)
+ temp.push(propList[i])
+ return temp
+ }
+
+ remove: Transition {
+ id: targetTransition
+
+ SequentialAnimation {
+ ScriptAction {
+ script: {
+ list.targetTrans_items[targetTransition.ViewTransition.item.nameData] = targetTransition.ViewTransition.index
+ list.targetTrans_targetIndexes.push(targetTransition.ViewTransition.targetIndexes)
+ list.targetTrans_targetItems.push(list.copyList(targetTransition.ViewTransition.targetItems))
+ }
+ }
+ ParallelAnimation {
+ NumberAnimation { properties: "x"; to: targetItems_transitionTo.x; duration: root.duration }
+ NumberAnimation { properties: "y"; to: targetItems_transitionTo.y; duration: root.duration }
+ }
+ ScriptAction { script: list.targetTransitionsDone += 1 }
+
+ // delay deleting this item so that it stays valid for the tests
+ // (this doesn't delay the test itself)
+ PauseAnimation { duration: 10000 }
+ }
+ }
+
+ removeDisplaced: Transition {
+ id: displaced
+
+ SequentialAnimation {
+ ScriptAction {
+ script: {
+ list.displacedTrans_items[displaced.ViewTransition.item.nameData] = displaced.ViewTransition.index
+ list.displacedTrans_targetIndexes.push(displaced.ViewTransition.targetIndexes)
+ list.displacedTrans_targetItems.push(list.copyList(displaced.ViewTransition.targetItems))
+ }
+ }
+ ParallelAnimation {
+ NumberAnimation {
+ properties: "x"; duration: root.duration
+ to: displacedItems_transitionVia.x
+ }
+ NumberAnimation {
+ properties: "y"; duration: root.duration
+ to: displacedItems_transitionVia.y
+ }
+ }
+ NumberAnimation { properties: "x,y"; duration: root.duration }
+
+ ScriptAction { script: list.displaceTransitionsDone += 1 }
+ }
+
+ }
+ }
+
+ Rectangle {
+ anchors.fill: list
+ color: "lightsteelblue"
+ opacity: 0.2
+ }
+
+ Rectangle {
+ anchors.bottom: parent.bottom
+ width: 20; height: 20
+ color: "white"
+ NumberAnimation on x { loops: Animation.Infinite; from: 0; to: 300; duration: 10000 }
+ }
+}
+
+
diff --git a/tests/auto/qtquick2/qquicklistview/tst_qquicklistview.cpp b/tests/auto/qtquick2/qquicklistview/tst_qquicklistview.cpp
index e809f95eed..393dd09d36 100644
--- a/tests/auto/qtquick2/qquicklistview/tst_qquicklistview.cpp
+++ b/tests/auto/qtquick2/qquicklistview/tst_qquicklistview.cpp
@@ -51,7 +51,6 @@
#include <QtQuick/private/qquicktext_p.h>
#include <QtQuick/private/qquickvisualitemmodel_p.h>
#include <QtDeclarative/private/qdeclarativelistmodel_p.h>
-#include <QtQuick/private/qdeclarativechangeset_p.h>
#include "../../shared/util.h"
#include "../shared/viewtestutil.h"
#include "../shared/visualtestutil.h"
@@ -171,6 +170,17 @@ private slots:
void asynchronous();
void unrequestedVisibility();
+ void populateTransitions();
+ void populateTransitions_data();
+ void addTransitions();
+ void addTransitions_data();
+ void moveTransitions();
+ void moveTransitions_data();
+ void removeTransitions();
+ void removeTransitions_data();
+ void multipleTransitions();
+ void multipleTransitions_data();
+
private:
template <class T> void items(const QUrl &source, bool forceLayout);
template <class T> void changed(const QUrl &source, bool forceLayout);
@@ -182,6 +192,11 @@ private:
template <class T> void clear(const QUrl &source);
template <class T> void sections(const QUrl &source);
+ QList<int> toIntList(const QVariantList &list);
+ void matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes);
+ void matchItemsAndIndexes(const QVariantMap &items, const QaimModel &model, const QList<int> &expectedIndexes);
+ void matchItemLists(const QVariantList &itemLists, const QList<QQuickItem *> &expectedItems);
+
void inserted_more_data();
void removed_more_data();
void moved_data();
@@ -1356,18 +1371,26 @@ void tst_QQuickListView::multipleChanges()
{
QList<QPair<QString, QString> > items;
for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
- items << qMakePair(QString("new item " + j), QString::number(j));
+ items << qMakePair(QString("new item %1").arg(j), QString::number(j));
model.insertItems(changes[i].index, items);
+ QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
break;
}
case ListChange::Removed:
model.removeItems(changes[i].index, changes[i].count);
+ QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
break;
case ListChange::Moved:
model.moveItems(changes[i].index, changes[i].to, changes[i].count);
+ QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
break;
case ListChange::SetCurrent:
listview->setCurrentIndex(changes[i].index);
+ QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+ break;
+ case ListChange::SetContentY:
+ listview->setContentY(changes[i].pos);
+ QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
break;
}
}
@@ -4729,6 +4752,929 @@ void tst_QQuickListView::unrequestedVisibility()
delete canvas;
}
+void tst_QQuickListView::populateTransitions()
+{
+ QFETCH(bool, staticallyPopulate);
+ QFETCH(bool, dynamicallyPopulate);
+ QFETCH(bool, usePopulateTransition);
+
+ QPointF transitionFrom(-50, -50);
+ QPointF transitionVia(100, 100);
+ QaimModel model_transitionFrom;
+ QaimModel model_transitionVia;
+
+ QaimModel model;
+ if (staticallyPopulate) {
+ for (int i = 0; i < 30; i++)
+ model.addItem("item" + QString::number(i), "");
+ }
+
+ QQuickView *canvas = createView();
+ canvas->rootContext()->setContextProperty("testModel", &model);
+ canvas->rootContext()->setContextProperty("testObject", new TestObject(canvas->rootContext()));
+ canvas->rootContext()->setContextProperty("usePopulateTransition", usePopulateTransition);
+ canvas->rootContext()->setContextProperty("dynamicallyPopulate", dynamicallyPopulate);
+ canvas->rootContext()->setContextProperty("transitionFrom", transitionFrom);
+ canvas->rootContext()->setContextProperty("transitionVia", transitionVia);
+ canvas->rootContext()->setContextProperty("model_transitionFrom", &model_transitionFrom);
+ canvas->rootContext()->setContextProperty("model_transitionVia", &model_transitionVia);
+ canvas->setSource(testFileUrl("populateTransitions.qml"));
+ canvas->show();
+
+ QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
+ QVERIFY(listview);
+ QQuickItem *contentItem = listview->contentItem();
+ QVERIFY(contentItem);
+
+ if (staticallyPopulate || dynamicallyPopulate) {
+ // check the populate transition is run
+ if (usePopulateTransition) {
+ QTRY_COMPARE(listview->property("countPopulateTransitions").toInt(), 17);
+ } else {
+ QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+ QTRY_COMPARE(listview->property("countPopulateTransitions").toInt(), 0);
+ }
+ QTRY_COMPARE(listview->property("countAddTransitions").toInt(), 0);
+ } else {
+ QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+ }
+
+ int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
+ if (usePopulateTransition)
+ QCOMPARE(itemCount, listview->property("countPopulateTransitions").toInt());
+ for (int i=0; i < model.count() && i < itemCount; ++i) {
+ QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+ QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
+ QTRY_COMPARE(item->x(), 0.0);
+ QTRY_COMPARE(item->y(), i*20.0);
+ QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
+ QVERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ }
+
+ // add an item and check this is done with add trantion, not populate
+ model.insertItem(0, "another item", "");
+ QTRY_COMPARE(listview->property("countAddTransitions").toInt(), 1);
+ QTRY_COMPARE(listview->property("countPopulateTransitions").toInt(),
+ (usePopulateTransition && (staticallyPopulate || dynamicallyPopulate)) ? 17 : 0);
+
+ // clear the model
+ canvas->rootContext()->setContextProperty("testModel", QVariant());
+ QTRY_COMPARE(listview->count(), 0);
+ QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper").count(), 0);
+ listview->setProperty("countPopulateTransitions", 0);
+ listview->setProperty("countAddTransitions", 0);
+
+ // set to a valid model and check populate transition is run a second time
+ model.clear();
+ for (int i = 0; i < 30; i++)
+ model.addItem("item" + QString::number(i), "");
+ canvas->rootContext()->setContextProperty("testModel", &model);
+ QTRY_COMPARE(listview->property("countPopulateTransitions").toInt(), usePopulateTransition ? 17 : 0);
+ QTRY_COMPARE(listview->property("countAddTransitions").toInt(), 0);
+
+ itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
+ if (usePopulateTransition)
+ QCOMPARE(itemCount, listview->property("countPopulateTransitions").toInt());
+ for (int i=0; i < model.count() && i < itemCount; ++i) {
+ QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+ QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
+ QTRY_COMPARE(item->x(), 0.0);
+ QTRY_COMPARE(item->y(), i*20.0);
+ QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
+ QVERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ }
+
+ // reset model and check populate transition is run again
+ listview->setProperty("countPopulateTransitions", 0);
+ listview->setProperty("countAddTransitions", 0);
+ model.reset();
+ QTRY_COMPARE(listview->property("countPopulateTransitions").toInt(), usePopulateTransition ? 17 : 0);
+ QTRY_COMPARE(listview->property("countAddTransitions").toInt(), 0);
+
+ itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
+ if (usePopulateTransition)
+ QCOMPARE(itemCount, listview->property("countPopulateTransitions").toInt());
+ for (int i=0; i < model.count() && i < itemCount; ++i) {
+ QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+ QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
+ QTRY_COMPARE(item->x(), 0.0);
+ QTRY_COMPARE(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::populateTransitions_data()
+{
+ QTest::addColumn<bool>("staticallyPopulate");
+ QTest::addColumn<bool>("dynamicallyPopulate");
+ QTest::addColumn<bool>("usePopulateTransition");
+
+ QTest::newRow("static") << true << false << true;
+ QTest::newRow("static, no populate") << true << false << false;
+
+ QTest::newRow("dynamic") << false << true << true;
+ QTest::newRow("dynamic, no populate") << false << true << false;
+
+ QTest::newRow("empty to start with") << false << false << true;
+ QTest::newRow("empty to start with, no populate") << false << false << false;
+}
+
+void tst_QQuickListView::addTransitions()
+{
+ QFETCH(int, initialItemCount);
+ QFETCH(bool, shouldAnimateTargets);
+ QFETCH(qreal, contentY);
+ QFETCH(int, insertionIndex);
+ QFETCH(int, insertionCount);
+ QFETCH(ListRange, expectedDisplacedIndexes);
+
+ // added items should start here
+ QPointF targetItems_transitionFrom(-50, -50);
+
+ // displaced items should pass through this point
+ QPointF displacedItems_transitionVia(100, 100);
+
+ QaimModel model;
+ for (int i = 0; i < initialItemCount; i++)
+ model.addItem("Original item" + QString::number(i), "");
+ QaimModel model_targetItems_transitionFrom;
+ QaimModel model_displacedItems_transitionVia;
+
+ QQuickView *canvas = createView();
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("model_targetItems_transitionFrom", &model_targetItems_transitionFrom);
+ ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
+ ctxt->setContextProperty("targetItems_transitionFrom", targetItems_transitionFrom);
+ ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
+ ctxt->setContextProperty("testObject", testObject);
+ canvas->setSource(testFileUrl("addTransitions.qml"));
+ canvas->show();
+
+ 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);
+
+ if (contentY != 0) {
+ listview->setContentY(contentY);
+ QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+ }
+
+ QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
+
+ // only target items that will become visible should be animated
+ QList<QPair<QString, QString> > newData;
+ QList<QPair<QString, QString> > expectedTargetData;
+ QList<int> targetIndexes;
+ if (shouldAnimateTargets) {
+ for (int i=insertionIndex; i<insertionIndex+insertionCount; i++) {
+ newData << qMakePair(QString("New item %1").arg(i), QString(""));
+
+ if (i >= contentY / 20 && i < (contentY + listview->height()) / 20) { // only grab visible items
+ expectedTargetData << newData.last();
+ targetIndexes << i;
+ }
+ }
+ QVERIFY(expectedTargetData.count() > 0);
+ }
+
+ // start animation
+ if (!newData.isEmpty()) {
+ model.insertItems(insertionIndex, newData);
+ QTRY_COMPARE(model.count(), listview->count());
+ }
+
+ QList<QQuickItem *> targetItems = findItems<QQuickItem>(contentItem, "wrapper", targetIndexes);
+
+ if (shouldAnimateTargets) {
+ QTRY_COMPARE(listview->property("targetTransitionsDone").toInt(), expectedTargetData.count());
+ QTRY_COMPARE(listview->property("displaceTransitionsDone").toInt(),
+ expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0);
+
+ // check the target and displaced items were animated
+ model_targetItems_transitionFrom.matchAgainst(expectedTargetData, "wasn't animated from target 'from' pos", "shouldn't have been animated from target 'from' pos");
+ model_displacedItems_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with displaced anim", "shouldn't have been animated with displaced anim");
+
+ // check attached properties
+ matchItemsAndIndexes(listview->property("targetTrans_items").toMap(), model, targetIndexes);
+ matchIndexLists(listview->property("targetTrans_targetIndexes").toList(), targetIndexes);
+ matchItemLists(listview->property("targetTrans_targetItems").toList(), targetItems);
+ if (expectedDisplacedIndexes.isValid()) {
+ // adjust expectedDisplacedIndexes to their final values after the move
+ QList<int> displacedIndexes = adjustIndexesForAddDisplaced(expectedDisplacedIndexes.indexes, insertionIndex, insertionCount);
+ matchItemsAndIndexes(listview->property("displacedTrans_items").toMap(), model, displacedIndexes);
+ matchIndexLists(listview->property("displacedTrans_targetIndexes").toList(), targetIndexes);
+ matchItemLists(listview->property("displacedTrans_targetItems").toList(), targetItems);
+ }
+
+ } else {
+ QTRY_COMPARE(model_targetItems_transitionFrom.count(), 0);
+ QTRY_COMPARE(model_displacedItems_transitionVia.count(), 0);
+ }
+
+ QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
+ int firstVisibleIndex = -1;
+ int itemCount = items.count();
+ for (int i=0; i<items.count(); i++) {
+ if (items[i]->y() >= contentY) {
+ QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
+ firstVisibleIndex = e.evaluate().toInt();
+ break;
+ }
+ }
+ QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
+
+ // verify all items moved to the correct final positions
+ for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
+ QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+ QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
+ QTRY_COMPARE(item->y(), i*20.0);
+ QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
+ QVERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ }
+
+ delete canvas;
+ delete testObject;
+}
+
+void tst_QQuickListView::addTransitions_data()
+{
+ QTest::addColumn<int>("initialItemCount");
+ QTest::addColumn<qreal>("contentY");
+ QTest::addColumn<bool>("shouldAnimateTargets");
+ QTest::addColumn<int>("insertionIndex");
+ QTest::addColumn<int>("insertionCount");
+ QTest::addColumn<ListRange>("expectedDisplacedIndexes");
+
+ // if inserting before visible index, items should not appear or animate in, even if there are > 1 new items
+ QTest::newRow("insert 1, just before start")
+ << 30 << 20.0 << false
+ << 0 << 1 << ListRange();
+ QTest::newRow("insert 1, way before start")
+ << 30 << 20.0 << false
+ << 0 << 1 << ListRange();
+ QTest::newRow("insert multiple, just before start")
+ << 30 << 100.0 << false
+ << 0 << 3 << ListRange();
+ QTest::newRow("insert multiple, way before start")
+ << 30 << 100.0 << false
+ << 0 << 3 << ListRange();
+
+ QTest::newRow("insert 1 at start")
+ << 30 << 0.0 << true
+ << 0 << 1 << ListRange(0, 15);
+ QTest::newRow("insert multiple at start")
+ << 30 << 0.0 << true
+ << 0 << 3 << ListRange(0, 15);
+ QTest::newRow("insert 1 at start, content y not 0")
+ << 30 << 40.0 << true // first visible is index 2, so translate the displaced indexes by 2
+ << 2 << 1 << ListRange(0 + 2, 15 + 2);
+ QTest::newRow("insert multiple at start, content y not 0")
+ << 30 << 40.0 << true // first visible is index 2
+ << 2 << 3 << ListRange(0 + 2, 15 + 2);
+
+ QTest::newRow("insert 1 at start, to empty list")
+ << 0 << 0.0 << true
+ << 0 << 1 << ListRange();
+ QTest::newRow("insert multiple at start, to empty list")
+ << 0 << 0.0 << true
+ << 0 << 3 << ListRange();
+
+ QTest::newRow("insert 1 at middle")
+ << 30 << 0.0 << true
+ << 5 << 1 << ListRange(5, 15);
+ QTest::newRow("insert multiple at middle")
+ << 30 << 0.0 << true
+ << 5 << 3 << ListRange(5, 15);
+
+ QTest::newRow("insert 1 at bottom")
+ << 30 << 0.0 << true
+ << 15 << 1 << ListRange(15, 15);
+ QTest::newRow("insert multiple at bottom")
+ << 30 << 0.0 << true
+ << 15 << 3 << ListRange(15, 15);
+ QTest::newRow("insert 1 at bottom, content y not 0")
+ << 30 << 20.0 * 3 << true
+ << 15 + 3 << 1 << ListRange(15 + 3, 15 + 3);
+ QTest::newRow("insert multiple at bottom, content y not 0")
+ << 30 << 20.0 * 3 << true
+ << 15 + 3 << 3 << ListRange(15 + 3, 15 + 3);
+
+ // items added after the last visible will not be animated in, since they
+ // do not appear in the final view
+ QTest::newRow("insert 1 after end")
+ << 30 << 0.0 << false
+ << 17 << 1 << ListRange();
+ QTest::newRow("insert multiple after end")
+ << 30 << 0.0 << false
+ << 17 << 3 << ListRange();
+}
+
+void tst_QQuickListView::moveTransitions()
+{
+ QFETCH(int, initialItemCount);
+ QFETCH(qreal, contentY);
+ QFETCH(qreal, itemsOffsetAfterMove);
+ QFETCH(int, moveFrom);
+ QFETCH(int, moveTo);
+ QFETCH(int, moveCount);
+ QFETCH(ListRange, expectedDisplacedIndexes);
+
+ // target and displaced items should pass through these points
+ QPointF targetItems_transitionVia(-50, 50);
+ QPointF displacedItems_transitionVia(100, 100);
+
+ QaimModel model;
+ for (int i = 0; i < initialItemCount; i++)
+ model.addItem("Original item" + QString::number(i), "");
+ QaimModel model_targetItems_transitionVia;
+ QaimModel model_displacedItems_transitionVia;
+
+ QQuickView *canvas = createView();
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("model_targetItems_transitionVia", &model_targetItems_transitionVia);
+ ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
+ ctxt->setContextProperty("targetItems_transitionVia", targetItems_transitionVia);
+ ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
+ ctxt->setContextProperty("testObject", testObject);
+ canvas->setSource(testFileUrl("moveTransitions.qml"));
+ canvas->show();
+
+ QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
+ QTRY_VERIFY(listview != 0);
+ QQuickItem *contentItem = listview->contentItem();
+ QVERIFY(contentItem != 0);
+ QQuickText *name;
+
+ if (contentY != 0) {
+ listview->setContentY(contentY);
+ QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+ }
+
+ QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
+
+ // Items moving to *or* from visible positions should be animated.
+ // Otherwise, they should not be animated.
+ QList<QPair<QString, QString> > expectedTargetData;
+ QList<int> targetIndexes;
+ for (int i=moveFrom; i<moveFrom+moveCount; i++) {
+ int toIndex = moveTo + (i - moveFrom);
+ if (i <= (contentY + listview->height()) / 20
+ || toIndex < (contentY + listview->height()) / 20) {
+ expectedTargetData << qMakePair(model.name(i), model.number(i));
+ targetIndexes << i;
+ }
+ }
+ // ViewTransition.index provides the indices that items are moving to, not from
+ targetIndexes = adjustIndexesForMove(targetIndexes, moveFrom, moveTo, moveCount);
+
+ // start animation
+ model.moveItems(moveFrom, moveTo, moveCount);
+
+ QTRY_COMPARE(listview->property("targetTransitionsDone").toInt(), expectedTargetData.count());
+ QTRY_COMPARE(listview->property("displaceTransitionsDone").toInt(),
+ expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0);
+
+ QList<QQuickItem *> targetItems = findItems<QQuickItem>(contentItem, "wrapper", targetIndexes);
+
+ // check the target and displaced items were animated
+ model_targetItems_transitionVia.matchAgainst(expectedTargetData, "wasn't animated from target 'from' pos", "shouldn't have been animated from target 'from' pos");
+ model_displacedItems_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with displaced anim", "shouldn't have been animated with displaced anim");
+
+ // check attached properties
+ matchItemsAndIndexes(listview->property("targetTrans_items").toMap(), model, targetIndexes);
+ matchIndexLists(listview->property("targetTrans_targetIndexes").toList(), targetIndexes);
+ matchItemLists(listview->property("targetTrans_targetItems").toList(), targetItems);
+ if (expectedDisplacedIndexes.isValid()) {
+ // adjust expectedDisplacedIndexes to their final values after the move
+ QList<int> displacedIndexes = adjustIndexesForMove(expectedDisplacedIndexes.indexes, moveFrom, moveTo, moveCount);
+ matchItemsAndIndexes(listview->property("displacedTrans_items").toMap(), model, displacedIndexes);
+ matchIndexLists(listview->property("displacedTrans_targetIndexes").toList(), targetIndexes);
+ matchItemLists(listview->property("displacedTrans_targetItems").toList(), targetItems);
+ }
+
+ QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
+ int firstVisibleIndex = -1;
+ for (int i=0; i<items.count(); i++) {
+ if (items[i]->y() >= contentY) {
+ QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
+ firstVisibleIndex = e.evaluate().toInt();
+ break;
+ }
+ }
+ QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
+
+ // verify all items moved to the correct final positions
+ int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
+ for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
+ QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+ QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
+ QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove);
+ name = findItem<QQuickText>(contentItem, "textName", i);
+ QVERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ }
+
+ delete canvas;
+ delete testObject;
+}
+
+void tst_QQuickListView::moveTransitions_data()
+{
+ QTest::addColumn<int>("initialItemCount");
+ QTest::addColumn<qreal>("contentY");
+ QTest::addColumn<qreal>("itemsOffsetAfterMove");
+ QTest::addColumn<int>("moveFrom");
+ QTest::addColumn<int>("moveTo");
+ QTest::addColumn<int>("moveCount");
+ QTest::addColumn<ListRange>("expectedDisplacedIndexes");
+
+ // when removing from above the visible, all items shift down depending on how many
+ // items have been removed from above the visible
+ QTest::newRow("move from above view, outside visible items, move 1") << 30 << 4*20.0 << 20.0
+ << 1 << 10 << 1 << ListRange(11, 15+4);
+ QTest::newRow("move from above view, outside visible items, move 1 (first item)") << 30 << 4*20.0 << 20.0
+ << 0 << 10 << 1 << ListRange(11, 15+4);
+ QTest::newRow("move from above view, outside visible items, move multiple") << 30 << 4*20.0 << 2*20.0
+ << 1 << 10 << 2 << ListRange(12, 15+4);
+ QTest::newRow("move from above view, outside visible items, move multiple (first item)") << 30 << 4*20.0 << 3*20.0
+ << 0 << 10 << 3 << ListRange(13, 15+4);
+ QTest::newRow("move from above view, mix of visible/non-visible") << 30 << 4*20.0 << 3*20.0
+ << 1 << 10 << 5 << ListRange(6, 14) + ListRange(15, 15+4);
+ QTest::newRow("move from above view, mix of visible/non-visible (move first)") << 30 << 4*20.0 << 4*20.0
+ << 0 << 10 << 5 << ListRange(5, 14) + ListRange(15, 15+4);
+
+ QTest::newRow("move within view, move 1 down") << 30 << 0.0 << 0.0
+ << 1 << 10 << 1 << ListRange(2, 10);
+ QTest::newRow("move within view, move 1 down, move first item") << 30 << 0.0 << 0.0
+ << 0 << 10 << 1 << ListRange(1, 10);
+ QTest::newRow("move within view, move 1 down, move first item, contentY not 0") << 30 << 4*20.0 << 0.0
+ << 0+4 << 10+4 << 1 << ListRange(1+4, 10+4);
+ QTest::newRow("move within view, move 1 down, to last item") << 30 << 0.0 << 0.0
+ << 10 << 15 << 1 << ListRange(11, 15);
+ QTest::newRow("move within view, move first->last") << 30 << 0.0 << 0.0
+ << 0 << 15 << 1 << ListRange(1, 15);
+
+ QTest::newRow("move within view, move multiple down") << 30 << 0.0 << 0.0
+ << 1 << 10 << 3 << ListRange(4, 12);
+ QTest::newRow("move within view, move multiple down, move first item") << 30 << 0.0 << 0.0
+ << 0 << 10 << 3 << ListRange(3, 12);
+ QTest::newRow("move within view, move multiple down, move first item, contentY not 0") << 30 << 4*20.0 << 0.0
+ << 0+4 << 10+4 << 3 << ListRange(3+4, 12+4);
+ QTest::newRow("move within view, move multiple down, displace last item") << 30 << 0.0 << 0.0
+ << 5 << 13 << 3 << ListRange(8, 15);
+ QTest::newRow("move within view, move multiple down, move first->last") << 30 << 0.0 << 0.0
+ << 0 << 13 << 3 << ListRange(3, 15);
+
+ QTest::newRow("move within view, move 1 up") << 30 << 0.0 << 0.0
+ << 10 << 1 << 1 << ListRange(1, 9);
+ QTest::newRow("move within view, move 1 up, move to first index") << 30 << 0.0 << 0.0
+ << 10 << 0 << 1 << ListRange(0, 9);
+ QTest::newRow("move within view, move 1 up, move to first index, contentY not 0") << 30 << 4*20.0 << 0.0
+ << 10+4 << 0+4 << 1 << ListRange(0+4, 9+4);
+ QTest::newRow("move within view, move 1 up, move to first index, contentY not on item border") << 30 << 4*20.0 - 10 << 0.0
+ << 10+4 << 0+4 << 1 << ListRange(0+4, 9+4);
+ QTest::newRow("move within view, move 1 up, move last item") << 30 << 0.0 << 0.0
+ << 15 << 10 << 1 << ListRange(10, 14);
+ QTest::newRow("move within view, move 1 up, move last->first") << 30 << 0.0 << 0.0
+ << 15 << 0 << 1 << ListRange(0, 14);
+
+ QTest::newRow("move within view, move multiple up") << 30 << 0.0 << 0.0
+ << 10 << 1 << 3 << ListRange(1, 9);
+ QTest::newRow("move within view, move multiple up, move to first index") << 30 << 0.0 << 0.0
+ << 10 << 0 << 3 << ListRange(0, 9);
+ QTest::newRow("move within view, move multiple up, move to first index, contentY not 0") << 30 << 4*20.0 << 0.0
+ << 10+4 << 0+4 << 3 << ListRange(0+4, 9+4);
+ QTest::newRow("move within view, move multiple up, move last item") << 30 << 0.0 << 0.0
+ << 13 << 5 << 3 << ListRange(5, 12);
+ QTest::newRow("move within view, move multiple up, move last->first") << 30 << 0.0 << 0.0
+ << 13 << 0 << 3 << ListRange(0, 12);
+
+ QTest::newRow("move from below view, move 1 up, move to top") << 30 << 0.0 << 0.0
+ << 20 << 0 << 1 << ListRange(0, 15);
+ QTest::newRow("move from below view, move 1 up, move to top, contentY not 0") << 30 << 4*20.0 << 0.0
+ << 25 << 4 << 1 << ListRange(0+4, 15+4);
+ QTest::newRow("move from below view, move multiple up, move to top") << 30 << 0.0 << 0.0
+ << 20 << 0 << 3 << ListRange(0, 15);
+ QTest::newRow("move from below view, move multiple up, move to top, contentY not 0") << 30 << 4*20.0 << 0.0
+ << 25 << 4 << 3 << ListRange(0+4, 15+4);
+
+ QTest::newRow("move from below view, move 1 up, move to bottom") << 30 << 0.0 << 0.0
+ << 20 << 15 << 1 << ListRange(15, 15);
+ QTest::newRow("move from below view, move 1 up, move to bottom, contentY not 0") << 30 << 4*20.0 << 0.0
+ << 25 << 15+4 << 1 << ListRange(15+4, 15+4);
+ QTest::newRow("move from below view, move multiple up, move to to bottom") << 30 << 0.0 << 0.0
+ << 20 << 15 << 3 << ListRange(15, 15);
+ QTest::newRow("move from below view, move multiple up, move to bottom, contentY not 0") << 30 << 4*20.0 << 0.0
+ << 25 << 15+4 << 3 << ListRange(15+4, 15+4);
+}
+
+void tst_QQuickListView::removeTransitions()
+{
+ QFETCH(int, initialItemCount);
+ QFETCH(bool, shouldAnimateTargets);
+ QFETCH(qreal, contentY);
+ QFETCH(int, removalIndex);
+ QFETCH(int, removalCount);
+ QFETCH(ListRange, expectedDisplacedIndexes);
+
+ // added items should end here
+ QPointF targetItems_transitionTo(-50, -50);
+
+ // displaced items should pass through this points
+ QPointF displacedItems_transitionVia(100, 100);
+
+ QaimModel model;
+ for (int i = 0; i < initialItemCount; i++)
+ model.addItem("Original item" + QString::number(i), "");
+ QaimModel model_targetItems_transitionTo;
+ QaimModel model_displacedItems_transitionVia;
+
+ QQuickView *canvas = createView();
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("model_targetItems_transitionTo", &model_targetItems_transitionTo);
+ ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
+ ctxt->setContextProperty("targetItems_transitionTo", targetItems_transitionTo);
+ ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
+ ctxt->setContextProperty("testObject", testObject);
+ canvas->setSource(testFileUrl("removeTransitions.qml"));
+ canvas->show();
+
+ 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);
+
+ if (contentY != 0) {
+ listview->setContentY(contentY);
+ QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+ }
+
+ QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
+
+ // only target items that are visible should be animated
+ QList<QPair<QString, QString> > expectedTargetData;
+ QList<int> targetIndexes;
+ if (shouldAnimateTargets) {
+ for (int i=removalIndex; i<removalIndex+removalCount; i++) {
+ if (i >= contentY / 20 && i < (contentY + listview->height()) / 20) {
+ expectedTargetData << qMakePair(model.name(i), model.number(i));
+ targetIndexes << i;
+ }
+ }
+ QVERIFY(expectedTargetData.count() > 0);
+ }
+
+ // calculate targetItems and expectedTargets before model changes
+ QList<QQuickItem *> targetItems = findItems<QQuickItem>(contentItem, "wrapper", targetIndexes);
+ QVariantMap expectedTargets;
+ for (int i=0; i<targetIndexes.count(); i++)
+ expectedTargets[model.name(targetIndexes[i])] = targetIndexes[i];
+
+ // start animation
+ model.removeItems(removalIndex, removalCount);
+ QTRY_COMPARE(model.count(), listview->count());
+
+ if (shouldAnimateTargets) {
+ QTRY_COMPARE(listview->property("targetTransitionsDone").toInt(), expectedTargetData.count());
+ QTRY_COMPARE(listview->property("displaceTransitionsDone").toInt(),
+ expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0);
+
+ // check the target and displaced items were animated
+ model_targetItems_transitionTo.matchAgainst(expectedTargetData, "wasn't animated to target 'to' pos", "shouldn't have been animated to target 'to' pos");
+ model_displacedItems_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with displaced anim", "shouldn't have been animated with displaced anim");
+
+ // check attached properties
+ QCOMPARE(listview->property("targetTrans_items").toMap(), expectedTargets);
+ matchIndexLists(listview->property("targetTrans_targetIndexes").toList(), targetIndexes);
+ matchItemLists(listview->property("targetTrans_targetItems").toList(), targetItems);
+ if (expectedDisplacedIndexes.isValid()) {
+ // adjust expectedDisplacedIndexes to their final values after the move
+ QList<int> displacedIndexes = adjustIndexesForRemoveDisplaced(expectedDisplacedIndexes.indexes, removalIndex, removalCount);
+ matchItemsAndIndexes(listview->property("displacedTrans_items").toMap(), model, displacedIndexes);
+ matchIndexLists(listview->property("displacedTrans_targetIndexes").toList(), targetIndexes);
+ matchItemLists(listview->property("displacedTrans_targetItems").toList(), targetItems);
+ }
+ } else {
+ QTRY_COMPARE(model_targetItems_transitionTo.count(), 0);
+ QTRY_COMPARE(model_displacedItems_transitionVia.count(), 0);
+ }
+
+ QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
+ int firstVisibleIndex = -1;
+ int itemCount = items.count();
+
+ for (int i=0; i<items.count(); i++) {
+ QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
+ int index = e.evaluate().toInt();
+ if (firstVisibleIndex < 0 && items[i]->y() >= contentY)
+ firstVisibleIndex = index;
+ if (index < 0)
+ itemCount--; // exclude deleted items
+ }
+ QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
+
+ // verify all items moved to the correct final positions
+ for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++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(), contentY + (i-firstVisibleIndex) * 20.0);
+ QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
+ QVERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ }
+
+ delete canvas;
+ delete testObject;
+}
+
+void tst_QQuickListView::removeTransitions_data()
+{
+ QTest::addColumn<int>("initialItemCount");
+ QTest::addColumn<qreal>("contentY");
+ QTest::addColumn<bool>("shouldAnimateTargets");
+ QTest::addColumn<int>("removalIndex");
+ QTest::addColumn<int>("removalCount");
+ QTest::addColumn<ListRange>("expectedDisplacedIndexes");
+
+ // All items that are visible following the remove operation should be animated.
+ // Remove targets that are outside of the view should not be animated.
+
+ QTest::newRow("remove 1 before start")
+ << 30 << 20.0 * 3 << false
+ << 2 << 1 << ListRange();
+ QTest::newRow("remove multiple, all before start")
+ << 30 << 20.0 * 3 << false
+ << 0 << 3 << ListRange();
+ QTest::newRow("remove mix of before and after start")
+ << 30 << 20.0 * 3 << true
+ << 2 << 3 << ListRange(5, 20); // 5-20 are visible after the remove
+
+ QTest::newRow("remove 1 from start")
+ << 30 << 0.0 << true
+ << 0 << 1 << ListRange(1, 16); // 1-16 are visible after the remove
+ QTest::newRow("remove multiple from start")
+ << 30 << 0.0 << true
+ << 0 << 3 << ListRange(3, 18); // 3-18 are visible after the remove
+ QTest::newRow("remove 1 from start, content y not 0")
+ << 30 << 20.0 * 2 << true // first visible is index 2, so translate the displaced indexes by 2
+ << 2 << 1 << ListRange(1 + 2, 16 + 2);
+ QTest::newRow("remove multiple from start, content y not 0")
+ << 30 << 20.0 * 2 << true // first visible is index 2
+ << 2 << 3 << ListRange(3 + 2, 18 + 2);
+
+ QTest::newRow("remove 1 from middle")
+ << 30 << 0.0 << true
+ << 5 << 1 << ListRange(6, 16);
+ QTest::newRow("remove multiple from middle")
+ << 30 << 0.0 << true
+ << 5 << 3 << ListRange(8, 18);
+
+
+ QTest::newRow("remove 1 from bottom")
+ << 30 << 0.0 << true
+ << 15 << 1 << ListRange(16, 16);
+
+ // remove 15, 16, 17
+ // 15 will animate as the target item, 16 & 17 won't be animated since they are outside
+ // the view, and 18 will be animated as the displaced item to replace the last item
+ QTest::newRow("remove multiple from bottom")
+ << 30 << 0.0 << true
+ << 15 << 3 << ListRange(18, 18);
+
+ QTest::newRow("remove 1 from bottom, content y not 0")
+ << 30 << 20.0 * 2 << true
+ << 15 + 2 << 1 << ListRange(16 + 2, 16 + 2);
+ QTest::newRow("remove multiple from bottom, content y not 0")
+ << 30 << 20.0 * 2 << true
+ << 15 + 2 << 3 << ListRange(18 + 2, 18 + 2);
+
+
+ QTest::newRow("remove 1 after end")
+ << 30 << 0.0 << false
+ << 17 << 1 << ListRange();
+ QTest::newRow("remove multiple after end")
+ << 30 << 0.0 << false
+ << 17 << 3 << ListRange();
+}
+
+void tst_QQuickListView::multipleTransitions()
+{
+ // Tests that if you interrupt a transition in progress with another action that
+ // cancels the previous transition, the resulting items are still placed correctly.
+
+ QFETCH(int, initialCount);
+ QFETCH(qreal, contentY);
+ QFETCH(QList<ListChange>, changes);
+
+ // add transitions on the left, moves on the right
+ QPointF addTargets_transitionFrom(-50, -50);
+ QPointF addDisplaced_transitionFrom(-50, 50);
+ QPointF moveTargets_transitionFrom(50, -50);
+ QPointF moveDisplaced_transitionFrom(50, 50);
+
+ QmlListModel model;
+ for (int i = 0; i < initialCount; i++)
+ model.addItem("Original item" + QString::number(i), "");
+
+ QQuickView *canvas = createView();
+ QDeclarativeContext *ctxt = canvas->rootContext();
+ TestObject *testObject = new TestObject;
+ ctxt->setContextProperty("testModel", &model);
+ ctxt->setContextProperty("testObject", testObject);
+ ctxt->setContextProperty("addTargets_transitionFrom", addTargets_transitionFrom);
+ ctxt->setContextProperty("addDisplaced_transitionFrom", addDisplaced_transitionFrom);
+ ctxt->setContextProperty("moveTargets_transitionFrom", moveTargets_transitionFrom);
+ ctxt->setContextProperty("moveDisplaced_transitionFrom", moveDisplaced_transitionFrom);
+ canvas->setSource(testFileUrl("multipleTransitions.qml"));
+ canvas->show();
+
+ 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);
+
+ int timeBetweenActions = canvas->rootObject()->property("timeBetweenActions").toInt();
+
+ QList<QPair<QString, QString> > targetItems;
+ for (int i=0; i<changes.count(); i++) {
+ switch (changes[i].type) {
+ case ListChange::Inserted:
+ {
+ for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
+ targetItems << qMakePair(QString("new item %1").arg(j), QString::number(j));
+ model.insertItems(changes[i].index, targetItems);
+ QTRY_COMPARE(model.count(), listview->count());
+ QTRY_VERIFY(listview->property("runningAddTargets").toBool());
+ QTRY_VERIFY(listview->property("runningAddDisplaced").toBool());
+ if (i == changes.count() - 1) {
+ QTRY_VERIFY(!listview->property("runningAddTargets").toBool());
+ QTRY_VERIFY(!listview->property("runningAddDisplaced").toBool());
+ } else {
+ QTest::qWait(timeBetweenActions);
+ }
+ break;
+ }
+ case ListChange::Removed:
+ for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
+ targetItems << qMakePair(model.name(i), model.number(i));
+ model.removeItems(changes[i].index, changes[i].count);
+ QTRY_COMPARE(model.count(), listview->count());
+ QTRY_VERIFY(listview->property("runningRemoveTargets").toBool());
+ QTRY_VERIFY(listview->property("runningRemoveDisplaced").toBool());
+ if (i == changes.count() - 1) {
+ QTRY_VERIFY(!listview->property("runningRemoveTargets").toBool());
+ QTRY_VERIFY(!listview->property("runningRemoveDisplaced").toBool());
+ } else {
+ QTest::qWait(timeBetweenActions);
+ }
+ break;
+ case ListChange::Moved:
+ for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
+ targetItems << qMakePair(model.name(i), model.number(i));
+ model.moveItems(changes[i].index, changes[i].to, changes[i].count);
+ QTRY_VERIFY(listview->property("runningMoveTargets").toBool());
+ QTRY_VERIFY(listview->property("runningMoveDisplaced").toBool());
+ if (i == changes.count() - 1) {
+ QTRY_VERIFY(!listview->property("runningMoveTargets").toBool());
+ QTRY_VERIFY(!listview->property("runningMoveDisplaced").toBool());
+ } else {
+ QTest::qWait(timeBetweenActions);
+ }
+ break;
+ case ListChange::SetCurrent:
+ listview->setCurrentIndex(changes[i].index);
+ break;
+ case ListChange::SetContentY:
+ listview->setContentY(changes[i].pos);
+ QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
+ break;
+ }
+ }
+ QCOMPARE(listview->count(), model.count());
+
+ QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
+ int firstVisibleIndex = -1;
+ for (int i=0; i<items.count(); i++) {
+ if (items[i]->y() >= contentY) {
+ QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
+ firstVisibleIndex = e.evaluate().toInt();
+ break;
+ }
+ }
+ QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
+
+ // verify all items moved to the correct final positions
+ int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
+ for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
+ QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
+ QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
+ QTRY_COMPARE(item->x(), 0.0);
+ QTRY_COMPARE(item->y(), i*20.0);
+ QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
+ QVERIFY(name != 0);
+ QTRY_COMPARE(name->text(), model.name(i));
+ }
+
+ delete canvas;
+ delete testObject;
+}
+
+void tst_QQuickListView::multipleTransitions_data()
+{
+ QTest::addColumn<int>("initialCount");
+ QTest::addColumn<qreal>("contentY");
+ QTest::addColumn<QList<ListChange> >("changes");
+
+ // the added item and displaced items should move to final dest correctly
+ QTest::newRow("add item, then move it immediately") << 10 << 0.0 << (QList<ListChange>()
+ << ListChange::insert(0, 1)
+ << ListChange::move(0, 3, 1)
+ );
+
+ // items affected by the add should change from move to add transition
+ QTest::newRow("move, then insert item before the moved item") << 20 << 0.0 << (QList<ListChange>()
+ << ListChange::move(1, 10, 3)
+ << ListChange::insert(0, 1)
+ );
+
+ // items should be placed correctly if you trigger a transition then refill for that index
+ QTest::newRow("add at 0, flick down, flick back to top and add at 0 again") << 20 << 0.0 << (QList<ListChange>()
+ << ListChange::insert(0, 1)
+ << ListChange::setContentY(80.0)
+ << ListChange::setContentY(0.0)
+ << ListChange::insert(0, 1)
+ );
+}
+
+QList<int> tst_QQuickListView::toIntList(const QVariantList &list)
+{
+ QList<int> ret;
+ bool ok = true;
+ for (int i=0; i<list.count(); i++) {
+ ret << list[i].toInt(&ok);
+ if (!ok)
+ qWarning() << "tst_QQuickListView::toIntList(): not a number:" << list[i];
+ }
+
+ return ret;
+}
+
+void tst_QQuickListView::matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes)
+{
+ for (int i=0; i<indexLists.count(); i++) {
+ QSet<int> current = indexLists[i].value<QList<int> >().toSet();
+ if (current != expectedIndexes.toSet())
+ qDebug() << "Cannot match actual targets" << current << "with expected" << expectedIndexes;
+ QCOMPARE(current, expectedIndexes.toSet());
+ }
+}
+
+void tst_QQuickListView::matchItemsAndIndexes(const QVariantMap &items, const QaimModel &model, const QList<int> &expectedIndexes)
+{
+ for (QVariantMap::const_iterator it = items.begin(); it != items.end(); ++it) {
+ QVERIFY(it.value().type() == QVariant::Int);
+ QString name = it.key();
+ int itemIndex = it.value().toInt();
+ QVERIFY2(expectedIndexes.contains(itemIndex), QTest::toString(QString("Index %1 not found in expectedIndexes").arg(itemIndex)));
+ if (model.name(itemIndex) != name)
+ qDebug() << itemIndex;
+ QCOMPARE(model.name(itemIndex), name);
+ }
+ QCOMPARE(items.count(), expectedIndexes.count());
+}
+
+void tst_QQuickListView::matchItemLists(const QVariantList &itemLists, const QList<QQuickItem *> &expectedItems)
+{
+ for (int i=0; i<itemLists.count(); i++) {
+ QVERIFY(itemLists[i].type() == QVariant::List);
+ QVariantList current = itemLists[i].toList();
+ for (int j=0; j<current.count(); j++) {
+ QQuickItem *o = qobject_cast<QQuickItem*>(current[j].value<QObject*>());
+ QVERIFY2(o, QTest::toString(QString("Invalid actual item at %1").arg(j)));
+ QVERIFY2(expectedItems.contains(o), QTest::toString(QString("Cannot match item %1").arg(j)));
+ }
+ QCOMPARE(current.count(), expectedItems.count());
+ }
+}
+
QTEST_MAIN(tst_QQuickListView)
diff --git a/tests/auto/qtquick2/shared/viewtestutil.cpp b/tests/auto/qtquick2/shared/viewtestutil.cpp
index ed2066d6aa..eeef23001e 100644
--- a/tests/auto/qtquick2/shared/viewtestutil.cpp
+++ b/tests/auto/qtquick2/shared/viewtestutil.cpp
@@ -99,6 +99,53 @@ void QQuickViewTestUtil::flick(QQuickView *canvas, const QPoint &from, const QPo
QTest::qWait(50);
}
+QList<int> QQuickViewTestUtil::adjustIndexesForAddDisplaced(const QList<int> &indexes, int index, int count)
+{
+ QList<int> result;
+ for (int i=0; i<indexes.count(); i++) {
+ int num = indexes[i];
+ if (num >= index) {
+ num += count;
+ }
+ result << num;
+ }
+ return result;
+}
+
+QList<int> QQuickViewTestUtil::adjustIndexesForMove(const QList<int> &indexes, int from, int to, int count)
+{
+ QList<int> result;
+ for (int i=0; i<indexes.count(); i++) {
+ int num = indexes[i];
+ if (from < to) {
+ if (num >= from && num < from + count)
+ num += (to - from); // target
+ else if (num >= from && num < to + count)
+ num -= count; // displaced
+ } else if (from > to) {
+ if (num >= from && num < from + count)
+ num -= (from - to); // target
+ else if (num >= to && num < from + count)
+ num += count; // displaced
+ }
+ result << num;
+ }
+ return result;
+}
+
+QList<int> QQuickViewTestUtil::adjustIndexesForRemoveDisplaced(const QList<int> &indexes, int index, int count)
+{
+ QList<int> result;
+ for (int i=0; i<indexes.count(); i++) {
+ int num = indexes[i];
+ if (num >= index)
+ num -= count;
+ result << num;
+ }
+ return result;
+}
+
+
QQuickViewTestUtil::QmlListModel::QmlListModel(QObject *parent)
: QListModelInterface(parent)
{
@@ -228,6 +275,17 @@ void QQuickViewTestUtil::QmlListModel::clear() {
emit itemsRemoved(0, count);
}
+void QQuickViewTestUtil::QmlListModel::matchAgainst(const QList<QPair<QString, QString> > &other, const QString &error1, const QString &error2) {
+ for (int i=0; i<other.count(); i++) {
+ QVERIFY2(list.contains(other[i]),
+ QTest::toString(other[i].first + " " + other[i].second + " " + error1));
+ }
+ for (int i=0; i<list.count(); i++) {
+ QVERIFY2(other.contains(list[i]),
+ QTest::toString(list[i].first + " " + list[i].second + " " + error2));
+ }
+}
+
QQuickViewTestUtil::QaimModel::QaimModel(QObject *parent)
: QAbstractListModel(parent)
@@ -343,3 +401,93 @@ void QQuickViewTestUtil::QaimModel::clear()
emit endRemoveRows();
}
+void QQuickViewTestUtil::QaimModel::reset()
+{
+ emit beginResetModel();
+ emit endResetModel();
+}
+
+void QQuickViewTestUtil::QaimModel::matchAgainst(const QList<QPair<QString, QString> > &other, const QString &error1, const QString &error2) {
+ for (int i=0; i<other.count(); i++) {
+ QVERIFY2(list.contains(other[i]),
+ QTest::toString(other[i].first + " " + other[i].second + " " + error1));
+ }
+ for (int i=0; i<list.count(); i++) {
+ QVERIFY2(other.contains(list[i]),
+ QTest::toString(list[i].first + " " + list[i].second + " " + error2));
+ }
+}
+
+
+
+QQuickViewTestUtil::ListRange::ListRange()
+ : valid(false)
+{
+}
+
+QQuickViewTestUtil::ListRange::ListRange(const ListRange &other)
+ : valid(other.valid)
+{
+ indexes = other.indexes;
+}
+
+QQuickViewTestUtil::ListRange::ListRange(int start, int end)
+ : valid(true)
+{
+ for (int i=start; i<=end; i++)
+ indexes << i;
+}
+
+QQuickViewTestUtil::ListRange::~ListRange()
+{
+}
+
+QQuickViewTestUtil::ListRange QQuickViewTestUtil::ListRange::operator+(const ListRange &other) const
+{
+ if (other == *this)
+ return *this;
+ ListRange a(*this);
+ a.indexes.append(other.indexes);
+ return a;
+}
+
+bool QQuickViewTestUtil::ListRange::operator==(const ListRange &other) const
+{
+ return indexes.toSet() == other.indexes.toSet();
+}
+
+bool QQuickViewTestUtil::ListRange::operator!=(const ListRange &other) const
+{
+ return !(*this == other);
+}
+
+bool QQuickViewTestUtil::ListRange::isValid() const
+{
+ return valid;
+}
+
+int QQuickViewTestUtil::ListRange::count() const
+{
+ return indexes.count();
+}
+
+QList<QPair<QString,QString> > QQuickViewTestUtil::ListRange::getModelDataValues(const QmlListModel &model)
+{
+ QList<QPair<QString,QString> > data;
+ if (!valid)
+ return data;
+ for (int i=0; i<indexes.count(); i++)
+ data.append(qMakePair(model.name(indexes[i]), model.number(indexes[i])));
+ return data;
+}
+
+QList<QPair<QString,QString> > QQuickViewTestUtil::ListRange::getModelDataValues(const QaimModel &model)
+{
+ QList<QPair<QString,QString> > data;
+ if (!valid)
+ return data;
+ for (int i=0; i<indexes.count(); i++)
+ data.append(qMakePair(model.name(indexes[i]), model.number(indexes[i])));
+ return data;
+}
+
diff --git a/tests/auto/qtquick2/shared/viewtestutil.h b/tests/auto/qtquick2/shared/viewtestutil.h
index 98b0dbbf92..71fd5065df 100644
--- a/tests/auto/qtquick2/shared/viewtestutil.h
+++ b/tests/auto/qtquick2/shared/viewtestutil.h
@@ -55,16 +55,22 @@ namespace QQuickViewTestUtil
void flick(QQuickView *canvas, const QPoint &from, const QPoint &to, int duration);
+ QList<int> adjustIndexesForAddDisplaced(const QList<int> &indexes, int index, int count);
+ QList<int> adjustIndexesForMove(const QList<int> &indexes, int from, int to, int count);
+ QList<int> adjustIndexesForRemoveDisplaced(const QList<int> &indexes, int index, int count);
+
struct ListChange {
- enum { Inserted, Removed, Moved, SetCurrent } type;
+ enum { Inserted, Removed, Moved, SetCurrent, SetContentY } type;
int index;
int count;
int to; // Move
+ qreal pos; // setContentY
- static ListChange insert(int index, int count = 1) { ListChange c = { Inserted, index, count, -1 }; return c; }
- static ListChange remove(int index, int count = 1) { ListChange c = { Removed, index, count, -1 }; return c; }
- static ListChange move(int index, int to, int count) { ListChange c = { Moved, index, count, to }; return c; }
- static ListChange setCurrent(int index) { ListChange c = { SetCurrent, index, -1, -1 }; return c; }
+ static ListChange insert(int index, int count = 1) { ListChange c = { Inserted, index, count, -1, 0.0 }; return c; }
+ static ListChange remove(int index, int count = 1) { ListChange c = { Removed, index, count, -1, 0.0 }; return c; }
+ static ListChange move(int index, int to, int count) { ListChange c = { Moved, index, count, to, 0.0 }; return c; }
+ static ListChange setCurrent(int index) { ListChange c = { SetCurrent, index, -1, -1, 0.0 }; return c; }
+ static ListChange setContentY(qreal pos) { ListChange c = { SetContentY, -1, -1, -1, pos }; return c; }
};
class QmlListModel : public QListModelInterface
@@ -87,7 +93,7 @@ namespace QQuickViewTestUtil
QVariant data(int index, int role) const;
QHash<int, QVariant> data(int index, const QList<int> &roles) const;
- void addItem(const QString &name, const QString &number);
+ Q_INVOKABLE void addItem(const QString &name, const QString &number);
void insertItem(int index, const QString &name, const QString &number);
void insertItems(int index, const QList<QPair<QString, QString> > &items);
@@ -101,6 +107,8 @@ namespace QQuickViewTestUtil
void clear();
+ void matchAgainst(const QList<QPair<QString, QString> > &other, const QString &error1, const QString &error2);
+
private:
QList<QPair<QString,QString> > list;
};
@@ -120,7 +128,7 @@ namespace QQuickViewTestUtil
QString name(int index) const;
QString number(int index) const;
- void addItem(const QString &name, const QString &number);
+ Q_INVOKABLE void addItem(const QString &name, const QString &number);
void addItems(const QList<QPair<QString, QString> > &items);
void insertItem(int index, const QString &name, const QString &number);
void insertItems(int index, const QList<QPair<QString, QString> > &items);
@@ -134,13 +142,39 @@ namespace QQuickViewTestUtil
void modifyItem(int idx, const QString &name, const QString &number);
void clear();
+ void reset();
+
+ void matchAgainst(const QList<QPair<QString, QString> > &other, const QString &error1, const QString &error2);
private:
QList<QPair<QString,QString> > list;
};
+ class ListRange
+ {
+ public:
+ ListRange();
+ ListRange(const ListRange &other);
+ ListRange(int start, int end);
+
+ ~ListRange();
+
+ ListRange operator+(const ListRange &other) const;
+ bool operator==(const ListRange &other) const;
+ bool operator!=(const ListRange &other) const;
+
+ bool isValid() const;
+ int count() const;
+
+ QList<QPair<QString,QString> > getModelDataValues(const QmlListModel &model);
+ QList<QPair<QString,QString> > getModelDataValues(const QaimModel &model);
+
+ QList<int> indexes;
+ bool valid;
+ };
}
Q_DECLARE_METATYPE(QList<QQuickViewTestUtil::ListChange>)
+Q_DECLARE_METATYPE(QQuickViewTestUtil::ListRange)
#endif // QQUICKVIEWTESTUTIL_H
diff --git a/tests/auto/qtquick2/shared/visualtestutil.h b/tests/auto/qtquick2/shared/visualtestutil.h
index ceed4b04a7..09bb03c002 100644
--- a/tests/auto/qtquick2/shared/visualtestutil.h
+++ b/tests/auto/qtquick2/shared/visualtestutil.h
@@ -97,6 +97,16 @@ namespace QQuickVisualTestUtil
return items;
}
+
+ template<typename T>
+ QList<T*> findItems(QQuickItem *parent, const QString &objectName, const QList<int> &indexes)
+ {
+ QList<T*> items;
+ for (int i=0; i<indexes.count(); i++)
+ items << qobject_cast<QQuickItem*>(findItem<T>(parent, objectName, indexes[i]));
+ return items;
+ }
+
}
#endif // QQUICKVISUALTESTUTIL_H