refilled items should be moved immediately
[profile/ivi/qtdeclarative.git] / tests / auto / quick / qquickgridview / tst_qquickgridview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtTest/QtTest>
43 #include <QtCore/qstringlistmodel.h>
44 #include <QtQuick/qquickview.h>
45 #include <QtQml/qqmlengine.h>
46 #include <QtQml/qqmlcomponent.h>
47 #include <QtQml/qqmlcontext.h>
48 #include <QtQml/qqmlexpression.h>
49 #include <QtQml/qqmlincubator.h>
50 #include <QtQuick/private/qquickitem_p.h>
51 #include <QtQuick/private/qquickgridview_p.h>
52 #include <QtQuick/private/qquicktext_p.h>
53 #include <QtQuick/private/qquickvisualitemmodel_p.h>
54 #include <QtQml/private/qquicklistmodel_p.h>
55 #include <QtQml/private/qlistmodelinterface_p.h>
56 #include "../../shared/util.h"
57 #include "../shared/viewtestutil.h"
58 #include "../shared/visualtestutil.h"
59 #include <QtGui/qguiapplication.h>
60
61 Q_DECLARE_METATYPE(Qt::LayoutDirection)
62 Q_DECLARE_METATYPE(QQuickGridView::Flow)
63
64 using namespace QQuickViewTestUtil;
65 using namespace QQuickVisualTestUtil;
66
67 class tst_QQuickGridView : public QQmlDataTest
68 {
69     Q_OBJECT
70 public:
71     tst_QQuickGridView();
72
73 private slots:
74     void items();
75     void changed();
76     void inserted();
77     void inserted_more();
78     void inserted_more_data();
79     void insertBeforeVisible();
80     void insertBeforeVisible_data();
81     void removed();
82     void removed_more();
83     void removed_more_data();
84     void addOrRemoveBeforeVisible();
85     void addOrRemoveBeforeVisible_data();
86     void clear();
87     void moved();
88     void moved_data();
89     void multipleChanges();
90     void multipleChanges_data();
91     void swapWithFirstItem();
92     void changeFlow();
93     void currentIndex();
94     void noCurrentIndex();
95     void defaultValues();
96     void properties();
97     void propertyChanges();
98     void componentChanges();
99     void modelChanges();
100     void positionViewAtIndex();
101     void positionViewAtIndex_rightToLeft();
102     void mirroring();
103     void snapping();
104     void resetModel();
105     void enforceRange();
106     void enforceRange_rightToLeft();
107     void QTBUG_8456();
108     void manualHighlight();
109     void footer();
110     void footer_data();
111     void header();
112     void header_data();
113     void resizeViewAndRepaint();
114     void changeColumnCount();
115     void indexAt_itemAt_data();
116     void indexAt_itemAt();
117     void onAdd();
118     void onAdd_data();
119     void onRemove();
120     void onRemove_data();
121     void columnCount();
122     void margins();
123     void creationContext();
124     void snapToRow_data();
125     void snapToRow();
126     void snapOneRow_data();
127     void snapOneRow();
128     void unaligned();
129     void cacheBuffer();
130     void asynchronous();
131     void unrequestedVisibility();
132
133     void populateTransitions();
134     void populateTransitions_data();
135     void addTransitions();
136     void addTransitions_data();
137     void moveTransitions();
138     void moveTransitions_data();
139     void removeTransitions();
140     void removeTransitions_data();
141     void displacedTransitions();
142     void displacedTransitions_data();
143     void multipleTransitions();
144     void multipleTransitions_data();
145
146 private:
147     QList<int> toIntList(const QVariantList &list);
148     void matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes);
149     void matchItemsAndIndexes(const QVariantMap &items, const QaimModel &model, const QList<int> &expectedIndexes);
150     void matchItemLists(const QVariantList &itemLists, const QList<QQuickItem *> &expectedItems);
151 };
152
153 tst_QQuickGridView::tst_QQuickGridView()
154 {
155 }
156
157 void tst_QQuickGridView::items()
158 {
159     QQuickView *canvas = createView();
160
161     QaimModel model;
162     model.addItem("Fred", "12345");
163     model.addItem("John", "2345");
164     model.addItem("Bob", "54321");
165     model.addItem("Billy", "22345");
166     model.addItem("Sam", "2945");
167     model.addItem("Ben", "04321");
168     model.addItem("Jim", "0780");
169
170     QQmlContext *ctxt = canvas->rootContext();
171     ctxt->setContextProperty("testModel", &model);
172     ctxt->setContextProperty("testRightToLeft", QVariant(false));
173     ctxt->setContextProperty("testTopToBottom", QVariant(false));
174
175     canvas->setSource(testFileUrl("gridview1.qml"));
176     qApp->processEvents();
177
178     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
179     QTRY_VERIFY(gridview != 0);
180
181     QQuickItem *contentItem = gridview->contentItem();
182     QTRY_VERIFY(contentItem != 0);
183
184     QTRY_COMPARE(gridview->count(), model.count());
185     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
186     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
187
188     for (int i = 0; i < model.count(); ++i) {
189         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
190         QTRY_VERIFY(name != 0);
191         QTRY_COMPARE(name->text(), model.name(i));
192         QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
193         QTRY_VERIFY(number != 0);
194         QTRY_COMPARE(number->text(), model.number(i));
195     }
196
197     // set an empty model and confirm that items are destroyed
198     QaimModel model2;
199     ctxt->setContextProperty("testModel", &model2);
200
201     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
202     QTRY_VERIFY(itemCount == 0);
203
204     delete canvas;
205 }
206
207 void tst_QQuickGridView::changed()
208 {
209     QQuickView *canvas = createView();
210
211     QaimModel model;
212     model.addItem("Fred", "12345");
213     model.addItem("John", "2345");
214     model.addItem("Bob", "54321");
215     model.addItem("Billy", "22345");
216     model.addItem("Sam", "2945");
217     model.addItem("Ben", "04321");
218     model.addItem("Jim", "0780");
219
220     QQmlContext *ctxt = canvas->rootContext();
221     ctxt->setContextProperty("testModel", &model);
222     ctxt->setContextProperty("testRightToLeft", QVariant(false));
223     ctxt->setContextProperty("testTopToBottom", QVariant(false));
224
225     canvas->setSource(testFileUrl("gridview1.qml"));
226     qApp->processEvents();
227
228     QQuickFlickable *gridview = findItem<QQuickFlickable>(canvas->rootObject(), "grid");
229     QTRY_VERIFY(gridview != 0);
230
231     QQuickItem *contentItem = gridview->contentItem();
232     QTRY_VERIFY(contentItem != 0);
233
234     model.modifyItem(1, "Will", "9876");
235     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
236     QTRY_VERIFY(name != 0);
237     QTRY_COMPARE(name->text(), model.name(1));
238     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
239     QTRY_VERIFY(number != 0);
240     QTRY_COMPARE(number->text(), model.number(1));
241
242     delete canvas;
243 }
244
245 void tst_QQuickGridView::inserted()
246 {
247     QQuickView *canvas = createView();
248     canvas->show();
249
250     QaimModel model;
251     model.addItem("Fred", "12345");
252     model.addItem("John", "2345");
253     model.addItem("Bob", "54321");
254
255     QQmlContext *ctxt = canvas->rootContext();
256     ctxt->setContextProperty("testModel", &model);
257     ctxt->setContextProperty("testRightToLeft", QVariant(false));
258     ctxt->setContextProperty("testTopToBottom", QVariant(false));
259
260     canvas->setSource(testFileUrl("gridview1.qml"));
261     qApp->processEvents();
262
263     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
264     QTRY_VERIFY(gridview != 0);
265
266     QQuickItem *contentItem = gridview->contentItem();
267     QTRY_VERIFY(contentItem != 0);
268
269     model.insertItem(1, "Will", "9876");
270
271     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
272     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
273
274     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
275     QTRY_VERIFY(name != 0);
276     QTRY_COMPARE(name->text(), model.name(1));
277     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
278     QTRY_VERIFY(number != 0);
279     QTRY_COMPARE(number->text(), model.number(1));
280
281     // Checks that onAdd is called
282     int added = canvas->rootObject()->property("added").toInt();
283     QTRY_COMPARE(added, 1);
284
285     // Confirm items positioned correctly
286     for (int i = 0; i < model.count(); ++i) {
287         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
288         QTRY_COMPARE(item->x(), (i%3)*80.0);
289         QTRY_COMPARE(item->y(), (i/3)*60.0);
290     }
291
292     model.insertItem(0, "Foo", "1111"); // zero index, and current item
293
294     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
295
296     name = findItem<QQuickText>(contentItem, "textName", 0);
297     QTRY_VERIFY(name != 0);
298     QTRY_COMPARE(name->text(), model.name(0));
299     number = findItem<QQuickText>(contentItem, "textNumber", 0);
300     QTRY_VERIFY(number != 0);
301     QTRY_COMPARE(number->text(), model.number(0));
302
303     QTRY_COMPARE(gridview->currentIndex(), 1);
304
305     // Confirm items positioned correctly
306     for (int i = 0; i < model.count(); ++i) {
307         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
308         QTRY_VERIFY(item->x() == (i%3)*80);
309         QTRY_VERIFY(item->y() == (i/3)*60);
310     }
311
312     for (int i = model.count(); i < 30; ++i)
313         model.insertItem(i, "Hello", QString::number(i));
314
315     gridview->setContentY(120);
316
317     // Insert item outside visible area
318     model.insertItem(1, "Hello", "1324");
319
320     QTRY_VERIFY(gridview->contentY() == 120);
321
322     delete canvas;
323 }
324
325 void tst_QQuickGridView::inserted_more()
326 {
327     QFETCH(qreal, contentY);
328     QFETCH(int, insertIndex);
329     QFETCH(int, insertCount);
330     QFETCH(qreal, itemsOffsetAfterMove);
331
332     QaimModel model;
333     for (int i = 0; i < 30; i++)
334         model.addItem("Item" + QString::number(i), "");
335
336     QQuickView *canvas = createView();
337     QQmlContext *ctxt = canvas->rootContext();
338     ctxt->setContextProperty("testModel", &model);
339     ctxt->setContextProperty("testRightToLeft", QVariant(false));
340     ctxt->setContextProperty("testTopToBottom", QVariant(false));
341
342     canvas->setSource(testFileUrl("gridview1.qml"));
343     canvas->show();
344     qApp->processEvents();
345
346     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
347     QTRY_VERIFY(gridview != 0);
348     QQuickItem *contentItem = gridview->contentItem();
349     QTRY_VERIFY(contentItem != 0);
350
351     gridview->setContentY(contentY);
352     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
353
354     QList<QPair<QString, QString> > newData;
355     for (int i=0; i<insertCount; i++)
356         newData << qMakePair(QString("value %1").arg(i), QString::number(i));
357     model.insertItems(insertIndex, newData);
358     QTRY_COMPARE(gridview->property("count").toInt(), model.count());
359
360     // check visibleItems.first() is in correct position
361     QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper", 0);
362     QVERIFY(item0);
363     QCOMPARE(item0->y(), 0.0);
364
365     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
366     int firstVisibleIndex = -1;
367     for (int i=0; i<items.count(); i++) {
368         if (items[i]->y() >= contentY) {
369             QQmlExpression e(qmlContext(items[i]), items[i], "index");
370             firstVisibleIndex = e.evaluate().toInt();
371             break;
372         }
373     }
374     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
375
376     // Confirm items positioned correctly and indexes correct
377     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
378     QQuickText *name;
379     QQuickText *number;
380     for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
381         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
382         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
383
384         QCOMPARE(item->x(), (i%3)*80.0);
385         QCOMPARE(item->y(), (i/3)*60.0 + itemsOffsetAfterMove);
386
387         name = findItem<QQuickText>(contentItem, "textName", i);
388         QVERIFY(name != 0);
389         QCOMPARE(name->text(), model.name(i));
390         number = findItem<QQuickText>(contentItem, "textNumber", i);
391         QVERIFY(number != 0);
392         QCOMPARE(number->text(), model.number(i));
393     }
394
395     delete canvas;
396 }
397
398 void tst_QQuickGridView::inserted_more_data()
399 {
400     QTest::addColumn<qreal>("contentY");
401     QTest::addColumn<int>("insertIndex");
402     QTest::addColumn<int>("insertCount");
403     QTest::addColumn<qreal>("itemsOffsetAfterMove");
404
405     QTest::newRow("add 1, before visible items")
406             << 120.0     // show 6-23
407             << 5 << 1
408             << 0.0;   // insert 1 above first visible, grid is rearranged; first visible moves forward within its row
409                       // new 1st visible item is at 0
410
411     QTest::newRow("add 2, before visible items")
412             << 120.0     // show 6-23
413             << 5 << 2
414             << 0.0;   // insert 2 above first visible, grid is rearranged; first visible moves forward within its row
415
416     QTest::newRow("add 3, before visible items")
417             << 120.0     // show 6-23
418             << 5 << 3
419             << -60.0;   // insert 3 (1 row) above first visible in negative pos, first visible does not move
420
421     QTest::newRow("add 5, before visible items")
422             << 120.0     // show 6-23
423             << 5 << 5
424             << -60.0;   // insert 1 row + 2 items above first visible, 1 row added at negative pos,
425                         // grid is rearranged and first visible moves forward within its row
426
427     QTest::newRow("add 6, before visible items")
428             << 120.0     // show 6-23
429             << 5 << 6
430             << -60.0 * 2;   // insert 2 rows above first visible in negative pos, first visible does not move
431
432
433
434    QTest::newRow("add 1, at start of visible, content at start")
435             << 0.0
436             << 0 << 1
437             << 0.0;
438
439     QTest::newRow("add multiple, at start of visible, content at start")
440             << 0.0
441             << 0 << 3
442             << 0.0;
443
444     QTest::newRow("add 1, at start of visible, content not at start")
445             << 120.0     // show 6-23
446             << 6 << 1
447             << 0.0;
448
449     QTest::newRow("add multiple, at start of visible, content not at start")
450             << 120.0     // show 6-23
451             << 6 << 3
452             << 0.0;
453
454
455     QTest::newRow("add 1, at end of visible, content at start")
456             << 0.0
457             << 17 << 1
458             << 0.0;
459
460     QTest::newRow("add 1, at end of visible, content at start")
461             << 0.0
462             << 17 << 3
463             << 0.0;
464
465     QTest::newRow("add 1, at end of visible, content not at start")
466             << 120.0     // show 6-23
467             << 23 << 1
468             << 0.0;
469
470     QTest::newRow("add multiple, at end of visible, content not at start")
471             << 120.0     // show 6-23
472             << 23 << 3
473             << 0.0;
474
475
476     QTest::newRow("add 1, after visible, content at start")
477             << 0.0
478             << 20 << 1
479             << 0.0;
480
481     QTest::newRow("add 1, after visible, content at start")
482             << 0.0
483             << 20 << 3
484             << 0.0;
485
486     QTest::newRow("add 1, after visible, content not at start")
487             << 120.0     // show 6-23
488             << 24 << 1
489             << 0.0;
490
491     QTest::newRow("add multiple, after visible, content not at start")
492             << 120.0     // show 6-23
493             << 24 << 3
494             << 0.0;
495 }
496
497 void tst_QQuickGridView::insertBeforeVisible()
498 {
499     QFETCH(int, insertIndex);
500     QFETCH(int, insertCount);
501     QFETCH(int, cacheBuffer);
502
503     QQuickText *name;
504     QQuickView *canvas = createView();
505
506     QaimModel model;
507     for (int i = 0; i < 30; i++)
508         model.addItem("Item" + QString::number(i), "");
509
510     QQmlContext *ctxt = canvas->rootContext();
511     ctxt->setContextProperty("testModel", &model);
512     ctxt->setContextProperty("testRightToLeft", QVariant(false));
513     ctxt->setContextProperty("testTopToBottom", QVariant(false));
514     canvas->setSource(testFileUrl("gridview1.qml"));
515     canvas->show();
516     qApp->processEvents();
517
518     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
519     QTRY_VERIFY(gridview != 0);
520     QQuickItem *contentItem = gridview->contentItem();
521     QTRY_VERIFY(contentItem != 0);
522
523     gridview->setCacheBuffer(cacheBuffer);
524     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
525
526     // trigger a refill (not just setting contentY) so that the visibleItems grid is updated
527     int firstVisibleIndex = 12;     // move to an index where the top item is not visible
528     gridview->setContentY(firstVisibleIndex/3 * 60.0);
529     gridview->setCurrentIndex(firstVisibleIndex);
530     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
531
532     QTRY_COMPARE(gridview->currentIndex(), firstVisibleIndex);
533     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", firstVisibleIndex);
534     QVERIFY(item);
535     QCOMPARE(item->y(), gridview->contentY());
536
537     QList<QPair<QString, QString> > newData;
538     for (int i=0; i<insertCount; i++)
539         newData << qMakePair(QString("value %1").arg(i), QString::number(i));
540     model.insertItems(insertIndex, newData);
541     QTRY_COMPARE(gridview->property("count").toInt(), model.count());
542
543     // now, moving to the top of the view should position the inserted items correctly
544     int itemsOffsetAfterMove = (insertCount / 3) * -60.0;
545     gridview->setCurrentIndex(0);
546     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
547     QTRY_COMPARE(gridview->currentIndex(), 0);
548     QTRY_COMPARE(gridview->contentY(), 0.0 + itemsOffsetAfterMove);
549
550     // Confirm items positioned correctly and indexes correct
551     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
552     for (int i = 0; i < model.count() && i < itemCount; ++i) {
553         item = findItem<QQuickItem>(contentItem, "wrapper", i);
554         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
555         QCOMPARE(item->x(), (i%3)*80.0);
556         QCOMPARE(item->y(), (i/3)*60.0 + itemsOffsetAfterMove);
557         name = findItem<QQuickText>(contentItem, "textName", i);
558         QVERIFY(name != 0);
559         QTRY_COMPARE(name->text(), model.name(i));
560     }
561
562     delete canvas;
563 }
564
565 void tst_QQuickGridView::insertBeforeVisible_data()
566 {
567     QTest::addColumn<int>("insertIndex");
568     QTest::addColumn<int>("insertCount");
569     QTest::addColumn<int>("cacheBuffer");
570
571     QTest::newRow("insert 1 at 0, 0 buffer") << 0 << 1 << 0;
572     QTest::newRow("insert 1 at 0, 100 buffer") << 0 << 1 << 100;
573     QTest::newRow("insert 1 at 0, 500 buffer") << 0 << 1 << 500;
574
575     QTest::newRow("insert 1 at 1, 0 buffer") << 1 << 1 << 0;
576     QTest::newRow("insert 1 at 1, 100 buffer") << 1 << 1 << 100;
577     QTest::newRow("insert 1 at 1, 500 buffer") << 1 << 1 << 500;
578
579     QTest::newRow("insert multiple at 0, 0 buffer") << 0 << 6 << 0;
580     QTest::newRow("insert multiple at 0, 100 buffer") << 0 << 6 << 100;
581     QTest::newRow("insert multiple at 0, 500 buffer") << 0 << 6 << 500;
582
583     QTest::newRow("insert multiple at 1, 0 buffer") << 1 << 6 << 0;
584     QTest::newRow("insert multiple at 1, 100 buffer") << 1 << 6 << 100;
585     QTest::newRow("insert multiple at 1, 500 buffer") << 1 << 6 << 500;
586 }
587
588 void tst_QQuickGridView::removed()
589 {
590     QQuickView *canvas = createView();
591     canvas->show();
592
593     QaimModel model;
594     for (int i = 0; i < 40; i++)
595         model.addItem("Item" + QString::number(i), "");
596
597     QQmlContext *ctxt = canvas->rootContext();
598     ctxt->setContextProperty("testModel", &model);
599     ctxt->setContextProperty("testRightToLeft", QVariant(false));
600     ctxt->setContextProperty("testTopToBottom", QVariant(false));
601
602     canvas->setSource(testFileUrl("gridview1.qml"));
603     qApp->processEvents();
604
605     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
606     QTRY_VERIFY(gridview != 0);
607     QQuickItem *contentItem = gridview->contentItem();
608     QTRY_VERIFY(contentItem != 0);
609     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
610
611     model.removeItem(1);
612     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
613
614     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
615     QTRY_VERIFY(name != 0);
616     QTRY_COMPARE(name->text(), model.name(1));
617     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
618     QTRY_VERIFY(number != 0);
619     QTRY_COMPARE(number->text(), model.number(1));
620
621
622     // Checks that onRemove is called
623     QString removed = canvas->rootObject()->property("removed").toString();
624     QTRY_COMPARE(removed, QString("Item1"));
625
626     // Confirm items positioned correctly
627     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
628     for (int i = 0; i < model.count() && i < itemCount; ++i) {
629         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
630         if (!item) qWarning() << "Item" << i << "not found";
631         QTRY_VERIFY(item->x() == (i%3)*80);
632         QTRY_VERIFY(item->y() == (i/3)*60);
633     }
634
635     // Remove first item (which is the current item);
636     model.removeItem(0);
637     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
638
639     name = findItem<QQuickText>(contentItem, "textName", 0);
640     QTRY_VERIFY(name != 0);
641     QTRY_COMPARE(name->text(), model.name(0));
642     number = findItem<QQuickText>(contentItem, "textNumber", 0);
643     QTRY_VERIFY(number != 0);
644     QTRY_COMPARE(number->text(), model.number(0));
645
646
647     // Confirm items positioned correctly
648     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
649     for (int i = 0; i < model.count() && i < itemCount; ++i) {
650         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
651         if (!item) qWarning() << "Item" << i << "not found";
652         QTRY_VERIFY(item->x() == (i%3)*80);
653         QTRY_VERIFY(item->y() == (i/3)*60);
654     }
655
656     // Remove items not visible
657     model.removeItem(25);
658     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
659
660     // Confirm items positioned correctly
661     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
662     for (int i = 0; i < model.count() && i < itemCount; ++i) {
663         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
664         if (!item) qWarning() << "Item" << i << "not found";
665         QTRY_VERIFY(item->x() == (i%3)*80);
666         QTRY_VERIFY(item->y() == (i/3)*60);
667     }
668
669     // Remove items before visible
670     gridview->setContentY(120);
671     gridview->setCurrentIndex(10);
672
673     // Setting currentIndex above shouldn't cause view to scroll
674     QTRY_COMPARE(gridview->contentY(), 120.0);
675
676     model.removeItem(1);
677     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
678
679     // Confirm items positioned correctly
680     for (int i = 6; i < 18; ++i) {
681         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
682         if (!item) qWarning() << "Item" << i << "not found";
683         QTRY_VERIFY(item->x() == (i%3)*80);
684         QTRY_VERIFY(item->y() == (i/3)*60);
685     }
686
687     // Remove currentIndex
688     QQuickItem *oldCurrent = gridview->currentItem();
689     model.removeItem(9);
690     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
691
692     QTRY_COMPARE(gridview->currentIndex(), 9);
693     QTRY_VERIFY(gridview->currentItem() != oldCurrent);
694
695     gridview->setContentY(0);
696     // let transitions settle.
697     QTest::qWait(300);
698
699     // Confirm items positioned correctly
700     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
701     for (int i = 0; i < model.count() && i < itemCount; ++i) {
702         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
703         QTRY_VERIFY(item->x() == (i%3)*80);
704         QTRY_VERIFY(item->y() == (i/3)*60);
705     }
706
707     // remove item outside current view.
708     gridview->setCurrentIndex(32);
709     gridview->setContentY(240);
710
711     model.removeItem(30);
712     QTRY_VERIFY(gridview->currentIndex() == 31);
713
714     // remove current item beyond visible items.
715     gridview->setCurrentIndex(20);
716     gridview->setContentY(0);
717     model.removeItem(20);
718
719     QTRY_COMPARE(gridview->currentIndex(), 20);
720     QTRY_VERIFY(gridview->currentItem() != 0);
721
722     // remove item before current, but visible
723     gridview->setCurrentIndex(8);
724     gridview->setContentY(240);
725     oldCurrent = gridview->currentItem();
726     model.removeItem(6);
727
728     QTRY_COMPARE(gridview->currentIndex(), 7);
729     QTRY_VERIFY(gridview->currentItem() == oldCurrent);
730
731     delete canvas;
732 }
733
734 void tst_QQuickGridView::removed_more()
735 {
736     QFETCH(qreal, contentY);
737     QFETCH(int, removeIndex);
738     QFETCH(int, removeCount);
739     QFETCH(qreal, itemsOffsetAfterMove);
740     QFETCH(QString, firstVisible);
741
742     QQuickText *name;
743     QQuickText *number;
744     QQuickView *canvas = createView();
745
746     QaimModel model;
747     for (int i = 0; i < 30; i++)
748         model.addItem("Item" + QString::number(i), "");
749
750     QQmlContext *ctxt = canvas->rootContext();
751     ctxt->setContextProperty("testModel", &model);
752     ctxt->setContextProperty("testRightToLeft", QVariant(false));
753     ctxt->setContextProperty("testTopToBottom", QVariant(false));
754     canvas->setSource(testFileUrl("gridview1.qml"));
755     canvas->show();
756     qApp->processEvents();
757
758     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
759     QTRY_VERIFY(gridview != 0);
760     QQuickItem *contentItem = gridview->contentItem();
761     QTRY_VERIFY(contentItem != 0);
762
763     gridview->setContentY(contentY);
764     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
765
766     model.removeItems(removeIndex, removeCount);
767     QTRY_COMPARE(gridview->property("count").toInt(), model.count());
768
769     QString firstName;
770     int firstVisibleIndex = -1;
771     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
772     for (int i=0; i<items.count(); i++) {
773         if (items[i]->y() >= contentY) {
774             QQmlExpression e(qmlContext(items[i]), items[i], "index");
775             firstVisibleIndex = e.evaluate().toInt();
776             QQmlExpression en(qmlContext(items[i]), items[i], "name");
777             firstName = en.evaluate().toString();
778             break;
779         }
780     }
781     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
782     QCOMPARE(firstName, firstVisible);
783
784     // Confirm items positioned correctly and indexes correct
785     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
786     for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
787         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
788         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
789
790         QTRY_COMPARE(item->x(), (i%3)*80.0);
791         QTRY_COMPARE(item->y(), (i/3)*60.0 + itemsOffsetAfterMove);
792
793         name = findItem<QQuickText>(contentItem, "textName", i);
794         QVERIFY(name != 0);
795         QTRY_COMPARE(name->text(), model.name(i));
796         number = findItem<QQuickText>(contentItem, "textNumber", i);
797         QVERIFY(number != 0);
798         QTRY_COMPARE(number->text(), model.number(i));
799     }
800
801     delete canvas;
802 }
803
804 void tst_QQuickGridView::removed_more_data()
805 {
806     QTest::addColumn<qreal>("contentY");
807     QTest::addColumn<int>("removeIndex");
808     QTest::addColumn<int>("removeCount");
809     QTest::addColumn<qreal>("itemsOffsetAfterMove");
810     QTest::addColumn<QString>("firstVisible");
811
812     QTest::newRow("remove 1, before visible items")
813             << 120.0     // show 6-23
814             << 2 << 1
815             << 0.0 << "Item7";
816
817     QTest::newRow("remove 1, before visible position")
818             << 120.0     // show 6-23
819             << 3 << 1
820             << 0.0 << "Item7";
821
822     QTest::newRow("remove multiple, all before visible items")
823             << 120.0
824             << 1 << 3
825             << 60.0 << "Item6";    // removed top row, slide down by 1 row
826
827     QTest::newRow("remove multiple, all before visible items, remove item 0")
828             << 120.0
829             << 0 << 4
830             << 60.0 << "Item7";    // removed top row, slide down by 1 row
831
832     QTest::newRow("remove multiple rows, all before visible items")
833             << 240.0     // show 12-29
834             << 1 << 7
835             << 120.0 << "Item13";
836
837     QTest::newRow("remove one row before visible, content y not on item border")
838             << 100.0
839             << 0 << 3
840             << 60.0 << "Item6"; // 1 row removed
841
842     QTest::newRow("remove mix of visible/non-visible")
843             << 120.0     // show 6-23
844             << 2 << 3
845             << 60.0 << "Item6"; // 1 row removed
846
847
848     // remove 3,4,5 before the visible pos, first row moves down to just before the visible pos,
849     // items 6,7 are removed from view, item 8 slides up to original pos of item 6 (120px)
850     QTest::newRow("remove multiple, mix of items from before and within visible items")
851             << 120.0
852             << 3 << 5
853             << 60.0 << "Item8";    // adjust for the 1 row removed before the visible
854
855     QTest::newRow("remove multiple, mix of items from before and within visible items, remove item 0")
856             << 120.0
857             << 0 << 8
858             << 60.0 * 2 << "Item8";    // adjust for the 2 rows removed before the visible
859
860
861     QTest::newRow("remove 1, from start of visible, content at start")
862             << 0.0
863             << 0 << 1
864             << 0.0 << "Item1";
865
866     QTest::newRow("remove multiple, from start of visible, content at start")
867             << 0.0
868             << 0 << 3
869             << 0.0 << "Item3";
870
871     QTest::newRow("remove 1, from start of visible, content not at start")
872             << 120.0     // show 6-23
873             << 4 << 1
874             << 0.0 << "Item7";
875
876     QTest::newRow("remove multiple, from start of visible, content not at start")
877             << 120.0     // show 6-23
878             << 4 << 3
879             << 0.0 << "Item9";
880
881
882     QTest::newRow("remove 1, from middle of visible, content at start")
883             << 0.0
884             << 10 << 1
885             << 0.0 << "Item0";
886
887     QTest::newRow("remove multiple, from middle of visible, content at start")
888             << 0.0
889             << 10 << 5
890             << 0.0 << "Item0";
891
892     QTest::newRow("remove 1, from middle of visible, content not at start")
893             << 120.0     // show 6-23
894             << 10 << 1
895             << 0.0 << "Item6";
896
897     QTest::newRow("remove multiple, from middle of visible, content not at start")
898             << 120.0     // show 6-23
899             << 10 << 5
900             << 0.0 << "Item6";
901
902
903     QTest::newRow("remove 1, after visible, content at start")
904             << 0.0
905             << 16 << 1
906             << 0.0 << "Item0";
907
908     QTest::newRow("remove multiple, after visible, content at start")
909             << 0.0
910             << 16 << 5
911             << 0.0 << "Item0";
912
913     QTest::newRow("remove 1, after visible, content not at start")
914             << 120.0     // show 6-23
915             << 16+4 << 1
916             << 0.0 << "Item6";
917
918     QTest::newRow("remove multiple, after visible, content not at start")
919             << 120.0     // show 6-23
920             << 16+4 << 5
921             << 0.0 << "Item6";
922
923     QTest::newRow("remove multiple, mix of items from within and after visible items")
924             << 120.0     // show 6-23
925             << 20 << 5
926             << 0.0 << "Item6";
927 }
928
929 void tst_QQuickGridView::addOrRemoveBeforeVisible()
930 {
931     // QTBUG-21588: ensure re-layout is done on grid after adding or removing
932     // items from before the visible area
933
934     QFETCH(bool, doAdd);
935     QFETCH(qreal, newTopContentY);
936
937     QQuickView *canvas = createView();
938     canvas->show();
939
940     QaimModel model;
941     for (int i = 0; i < 30; i++)
942         model.addItem("Item" + QString::number(i), "");
943
944     QQmlContext *ctxt = canvas->rootContext();
945     ctxt->setContextProperty("testModel", &model);
946     ctxt->setContextProperty("testRightToLeft", QVariant(false));
947     ctxt->setContextProperty("testTopToBottom", QVariant(false));
948     canvas->setSource(testFileUrl("gridview1.qml"));
949
950     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
951     QTRY_VERIFY(gridview != 0);
952     QQuickItem *contentItem = gridview->contentItem();
953     QTRY_VERIFY(contentItem != 0);
954
955     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 0);
956     QTRY_COMPARE(name->text(), QString("Item0"));
957
958     gridview->setCurrentIndex(0);
959     qApp->processEvents();
960     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
961
962     // scroll down until item 0 is no longer drawn
963     // (bug not triggered if we just move using content y, since that doesn't
964     // refill and change the visible items)
965     gridview->setCurrentIndex(24);
966     qApp->processEvents();
967
968     QTRY_COMPARE(gridview->currentIndex(), 24);
969     QTRY_COMPARE(gridview->contentY(), 220.0);
970
971     QTest::qWait(100);  // wait for refill to complete
972     QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 0));  // 0 shouldn't be visible
973
974     if (doAdd) {
975         model.insertItem(0, "New Item", "New Item number");
976         QTRY_COMPARE(gridview->count(), 31);
977     } else {
978         model.removeItem(0);
979         QTRY_COMPARE(gridview->count(), 29);
980     }
981
982     // scroll back up and item 0 should be gone
983     gridview->setCurrentIndex(0);
984     qApp->processEvents();
985     QTRY_COMPARE(gridview->currentIndex(), 0);
986     QTRY_COMPARE(gridview->contentY(), newTopContentY);
987
988     name = findItem<QQuickText>(contentItem, "textName", 0);
989     if (doAdd)
990         QCOMPARE(name->text(), QString("New Item"));
991     else
992         QCOMPARE(name->text(), QString("Item1"));
993
994     // Confirm items positioned correctly
995     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
996     for (int i = 0; i < model.count() && i < itemCount; ++i) {
997         QTRY_VERIFY(findItem<QQuickItem>(contentItem, "wrapper", i));
998         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
999         QTRY_VERIFY(item->x() == (i%3)*80);
1000         QTRY_VERIFY(item->y() == (i/3)*60 + newTopContentY);
1001     }
1002
1003     delete canvas;
1004 }
1005
1006 void tst_QQuickGridView::addOrRemoveBeforeVisible_data()
1007 {
1008     QTest::addColumn<bool>("doAdd");
1009     QTest::addColumn<qreal>("newTopContentY");
1010
1011     QTest::newRow("add") << true << -60.0;
1012     QTest::newRow("remove") << false << -60.0;
1013 }
1014
1015 void tst_QQuickGridView::clear()
1016 {
1017     QQuickView *canvas = createView();
1018
1019     QaimModel model;
1020     for (int i = 0; i < 30; i++)
1021         model.addItem("Item" + QString::number(i), "");
1022
1023     QQmlContext *ctxt = canvas->rootContext();
1024     ctxt->setContextProperty("testModel", &model);
1025     ctxt->setContextProperty("testRightToLeft", QVariant(false));
1026     ctxt->setContextProperty("testTopToBottom", QVariant(false));
1027
1028     canvas->setSource(testFileUrl("gridview1.qml"));
1029     canvas->show();
1030     qApp->processEvents();
1031
1032     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1033     QVERIFY(gridview != 0);
1034     QQuickItem *contentItem = gridview->contentItem();
1035     QVERIFY(contentItem != 0);
1036     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1037
1038     model.clear();
1039
1040     QVERIFY(gridview->count() == 0);
1041     QVERIFY(gridview->currentItem() == 0);
1042     QVERIFY(gridview->contentY() == 0);
1043     QVERIFY(gridview->currentIndex() == -1);
1044
1045     // confirm sanity when adding an item to cleared list
1046     model.addItem("New", "1");
1047     QTRY_COMPARE(gridview->count(), 1);
1048     QVERIFY(gridview->currentItem() != 0);
1049     QVERIFY(gridview->currentIndex() == 0);
1050
1051     delete canvas;
1052 }
1053
1054 void tst_QQuickGridView::moved()
1055 {
1056     QFETCH(qreal, contentY);
1057     QFETCH(int, from);
1058     QFETCH(int, to);
1059     QFETCH(int, count);
1060     QFETCH(qreal, itemsOffsetAfterMove);
1061
1062     QQuickText *name;
1063     QQuickText *number;
1064     QScopedPointer<QQuickView> canvas(createView());
1065
1066     QaimModel model;
1067     for (int i = 0; i < 30; i++)
1068         model.addItem("Item" + QString::number(i), "");
1069
1070     QQmlContext *ctxt = canvas->rootContext();
1071     ctxt->setContextProperty("testModel", &model);
1072     ctxt->setContextProperty("testRightToLeft", QVariant(false));
1073     ctxt->setContextProperty("testTopToBottom", QVariant(false));
1074
1075     canvas->setSource(testFileUrl("gridview1.qml"));
1076     canvas->show();
1077     qApp->processEvents();
1078
1079     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1080     QTRY_VERIFY(gridview != 0);
1081     QQuickItem *contentItem = gridview->contentItem();
1082     QTRY_VERIFY(contentItem != 0);
1083     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1084
1085     QQuickItem *currentItem = gridview->currentItem();
1086     QTRY_VERIFY(currentItem != 0);
1087
1088     if (contentY != 0) {
1089         gridview->setContentY(contentY);
1090         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1091     }
1092
1093     model.moveItems(from, to, count);
1094     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1095
1096     // Confirm items positioned correctly and indexes correct
1097     int firstVisibleIndex = qCeil(contentY / 60.0) * 3;
1098     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1099     for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
1100         if (i >= firstVisibleIndex + 18)    // index has moved out of view
1101             continue;
1102         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1103         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1104
1105         QTRY_COMPARE(item->x(), (i%3)*80.0);
1106         QTRY_COMPARE(item->y(), (i/3)*60.0 + itemsOffsetAfterMove);
1107
1108         name = findItem<QQuickText>(contentItem, "textName", i);
1109         QVERIFY(name != 0);
1110         QTRY_COMPARE(name->text(), model.name(i));
1111         number = findItem<QQuickText>(contentItem, "textNumber", i);
1112         QVERIFY(number != 0);
1113         QTRY_COMPARE(number->text(), model.number(i));
1114
1115         // current index should have been updated
1116         if (item == currentItem)
1117             QTRY_COMPARE(gridview->currentIndex(), i);
1118     }
1119 }
1120
1121 void tst_QQuickGridView::moved_data()
1122 {
1123     QTest::addColumn<qreal>("contentY");
1124     QTest::addColumn<int>("from");
1125     QTest::addColumn<int>("to");
1126     QTest::addColumn<int>("count");
1127     QTest::addColumn<qreal>("itemsOffsetAfterMove");
1128
1129     // model starts with 30 items, each 80x60, in area 240x320
1130     // 18 items should be visible at a time
1131
1132     // The first visible item should not move upwards and out of the view
1133     // if items are moved/removed before it.
1134
1135
1136     QTest::newRow("move 1 forwards, within visible items")
1137             << 0.0
1138             << 1 << 8 << 1
1139             << 0.0;
1140
1141     QTest::newRow("move 1 forwards, from non-visible -> visible")
1142             << 120.0     // show 6-23
1143             << 1 << 23 << 1
1144             << 0.0;
1145
1146     QTest::newRow("move 1 forwards, from non-visible -> visible (move first item)")
1147             << 120.0     // // show 6-23
1148             << 0 << 6 << 1
1149             << 0.0;
1150
1151     QTest::newRow("move 1 forwards, from visible -> non-visible")
1152             << 0.0
1153             << 1 << 20 << 1
1154             << 0.0;
1155
1156     QTest::newRow("move 1 forwards, from visible -> non-visible (move first item)")
1157             << 0.0
1158             << 0 << 20 << 1
1159             << 0.0;
1160
1161
1162     QTest::newRow("move 1 backwards, within visible items")
1163             << 0.0
1164             << 10 << 5 << 1
1165             << 0.0;
1166
1167     QTest::newRow("move 1 backwards, within visible items (to first index)")
1168             << 0.0
1169             << 10 << 0 << 1
1170             << 0.0;
1171
1172     QTest::newRow("move 1 backwards, from non-visible -> visible")
1173             << 0.0
1174             << 28 << 8 << 1
1175             << 0.0;
1176
1177     QTest::newRow("move 1 backwards, from non-visible -> visible (move last item)")
1178             << 0.0
1179             << 29 << 14 << 1
1180             << 0.0;
1181
1182     QTest::newRow("move 1 backwards, from visible -> non-visible")
1183             << 120.0     // show 6-23
1184             << 7 << 1 << 1
1185             << 0.0;     // only 1 item moved back, so items shift accordingly and first row doesn't move
1186
1187     QTest::newRow("move 1 backwards, from visible -> non-visible (move first item)")
1188             << 120.0     // show 6-23
1189             << 7 << 0 << 1
1190             << 0.0;     // only 1 item moved back, so items shift accordingly and first row doesn't move
1191
1192
1193     QTest::newRow("move multiple forwards, within visible items")
1194             << 0.0
1195             << 0 << 5 << 3
1196             << 0.0;
1197
1198     QTest::newRow("move multiple backwards, within visible items (move first item)")
1199             << 0.0
1200             << 10 << 0 << 3
1201             << 0.0;
1202
1203     QTest::newRow("move multiple forwards, before visible items")
1204             << 120.0     // show 6-23
1205             << 3 << 4 << 3      // 3, 4, 5 move to after 6
1206             << 60.0;      // row of 3,4,5 has moved down
1207
1208     QTest::newRow("move multiple forwards, from non-visible -> visible")
1209             << 120.0     // show 6-23
1210             << 1 << 6 << 3
1211             << 60.0; // 1st row (it's above visible area) disappears, 0 drops down 1 row, first visible item (6) stays where it is
1212
1213     QTest::newRow("move multiple forwards, from non-visible -> visible (move first item)")
1214             << 120.0     // show 6-23
1215             << 0 << 6 << 3
1216             << 60.0;    // top row moved and shifted to below 3rd row, all items should shift down by 1 row
1217
1218     QTest::newRow("move multiple forwards, mix of non-visible/visible")
1219             << 120.0
1220             << 3 << 16 << 6
1221             << 60.0;    // top two rows removed, third row is now the first visible
1222
1223     QTest::newRow("move multiple forwards, to bottom of view")
1224             << 0.0
1225             << 5 << 13 << 5
1226             << 0.0;
1227
1228     QTest::newRow("move multiple forwards, to bottom of view, first row -> last")
1229             << 0.0
1230             << 0 << 15 << 3
1231             << 0.0;
1232
1233     QTest::newRow("move multiple forwards, to bottom of view, content y not 0")
1234             << 120.0
1235             << 5+4 << 13+4 << 5
1236             << 0.0;
1237
1238     QTest::newRow("move multiple forwards, from visible -> non-visible")
1239             << 0.0
1240             << 1 << 16 << 3
1241             << 0.0;
1242
1243     QTest::newRow("move multiple forwards, from visible -> non-visible (move first item)")
1244             << 0.0
1245             << 0 << 16 << 3
1246             << 0.0;
1247
1248
1249     QTest::newRow("move multiple backwards, within visible items")
1250             << 0.0
1251             << 4 << 1 << 3
1252             << 0.0;
1253
1254     QTest::newRow("move multiple backwards, from non-visible -> visible")
1255             << 0.0
1256             << 20 << 4 << 3
1257             << 0.0;
1258
1259     QTest::newRow("move multiple backwards, from non-visible -> visible (move last item)")
1260             << 0.0
1261             << 27 << 10 << 3
1262             << 0.0;
1263
1264     QTest::newRow("move multiple backwards, from visible -> non-visible")
1265             << 120.0     // show 6-23
1266             << 16 << 1 << 3
1267             << -60.0;   // to minimize movement, items are added above visible area, all items move up by 1 row
1268
1269     QTest::newRow("move multiple backwards, from visible -> non-visible (move first item)")
1270             << 120.0     // show 6-23
1271             << 16 << 0 << 3
1272             << -60.0;   // 16,17,18 move to above item 0, all items move up by 1 row
1273 }
1274
1275 void tst_QQuickGridView::multipleChanges()
1276 {
1277     QFETCH(int, startCount);
1278     QFETCH(QList<ListChange>, changes);
1279     QFETCH(int, newCount);
1280     QFETCH(int, newCurrentIndex);
1281
1282     QQuickView *canvas = createView();
1283
1284     QaimModel model;
1285     for (int i = 0; i < startCount; i++)
1286         model.addItem("Item" + QString::number(i), "");
1287
1288     QQmlContext *ctxt = canvas->rootContext();
1289     ctxt->setContextProperty("testModel", &model);
1290     ctxt->setContextProperty("testRightToLeft", QVariant(false));
1291     ctxt->setContextProperty("testTopToBottom", QVariant(false));
1292
1293     canvas->setSource(testFileUrl("gridview1.qml"));
1294     canvas->show();
1295     qApp->processEvents();
1296
1297     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1298     QTRY_VERIFY(gridview != 0);
1299     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1300
1301     for (int i=0; i<changes.count(); i++) {
1302         switch (changes[i].type) {
1303             case ListChange::Inserted:
1304             {
1305                 QList<QPair<QString, QString> > items;
1306                 for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
1307                     items << qMakePair(QString("new item " + j), QString::number(j));
1308                 model.insertItems(changes[i].index, items);
1309                 break;
1310             }
1311             case ListChange::Removed:
1312                 model.removeItems(changes[i].index, changes[i].count);
1313                 QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1314                 break;
1315             case ListChange::Moved:
1316                 model.moveItems(changes[i].index, changes[i].to, changes[i].count);
1317                 QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1318                 break;
1319             case ListChange::SetCurrent:
1320                 gridview->setCurrentIndex(changes[i].index);
1321                 QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1322                 break;
1323             case ListChange::SetContentY:
1324                 gridview->setContentY(changes[i].pos);
1325                 QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1326                 break;
1327         }
1328     }
1329
1330     QTRY_COMPARE(gridview->count(), newCount);
1331     QCOMPARE(gridview->count(), model.count());
1332     QTRY_COMPARE(gridview->currentIndex(), newCurrentIndex);
1333
1334     QQuickText *name;
1335     QQuickText *number;
1336     QQuickItem *contentItem = gridview->contentItem();
1337     QTRY_VERIFY(contentItem != 0);
1338     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1339     for (int i=0; i < model.count() && i < itemCount; ++i) {
1340         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1341         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1342         name = findItem<QQuickText>(contentItem, "textName", i);
1343         QVERIFY(name != 0);
1344         QTRY_COMPARE(name->text(), model.name(i));
1345         number = findItem<QQuickText>(contentItem, "textNumber", i);
1346         QVERIFY(number != 0);
1347         QTRY_COMPARE(number->text(), model.number(i));
1348     }
1349
1350     delete canvas;
1351 }
1352
1353 void tst_QQuickGridView::multipleChanges_data()
1354 {
1355     QTest::addColumn<int>("startCount");
1356     QTest::addColumn<QList<ListChange> >("changes");
1357     QTest::addColumn<int>("newCount");
1358     QTest::addColumn<int>("newCurrentIndex");
1359
1360     QList<ListChange> changes;
1361
1362     for (int i=1; i<30; i++)
1363         changes << ListChange::remove(0);
1364     QTest::newRow("remove all but 1, first->last") << 30 << changes << 1 << 0;
1365
1366     changes << ListChange::remove(0);
1367     QTest::newRow("remove all") << 30 << changes << 0 << -1;
1368
1369     changes.clear();
1370     changes << ListChange::setCurrent(29);
1371     for (int i=29; i>0; i--)
1372         changes << ListChange::remove(i);
1373     QTest::newRow("remove last (current) -> first") << 30 << changes << 1 << 0;
1374
1375     QTest::newRow("remove then insert at 0") << 10 << (QList<ListChange>()
1376             << ListChange::remove(0, 1)
1377             << ListChange::insert(0, 1)
1378             ) << 10 << 1;
1379
1380     QTest::newRow("remove then insert at non-zero index") << 10 << (QList<ListChange>()
1381             << ListChange::setCurrent(2)
1382             << ListChange::remove(2, 1)
1383             << ListChange::insert(2, 1)
1384             ) << 10 << 3;
1385
1386     QTest::newRow("remove current then insert below it") << 10 << (QList<ListChange>()
1387             << ListChange::setCurrent(1)
1388             << ListChange::remove(1, 3)
1389             << ListChange::insert(2, 2)
1390             ) << 9 << 1;
1391
1392     QTest::newRow("remove current index then move it down") << 10 << (QList<ListChange>()
1393             << ListChange::setCurrent(2)
1394             << ListChange::remove(1, 3)
1395             << ListChange::move(1, 5, 1)
1396             ) << 7 << 5;
1397
1398     QTest::newRow("remove current index then move it up") << 10 << (QList<ListChange>()
1399             << ListChange::setCurrent(5)
1400             << ListChange::remove(4, 3)
1401             << ListChange::move(4, 1, 1)
1402             ) << 7 << 1;
1403
1404
1405     QTest::newRow("insert multiple times") << 0 << (QList<ListChange>()
1406             << ListChange::insert(0, 2)
1407             << ListChange::insert(0, 4)
1408             << ListChange::insert(0, 6)
1409             ) << 12 << 10;
1410
1411     QTest::newRow("insert multiple times with current index changes") << 0 << (QList<ListChange>()
1412             << ListChange::insert(0, 2)
1413             << ListChange::insert(0, 4)
1414             << ListChange::insert(0, 6)
1415             << ListChange::setCurrent(3)
1416             << ListChange::insert(3, 2)
1417             ) << 14 << 5;
1418
1419     QTest::newRow("insert and remove all") << 0 << (QList<ListChange>()
1420             << ListChange::insert(0, 30)
1421             << ListChange::remove(0, 30)
1422             ) << 0 << -1;
1423
1424     QTest::newRow("insert and remove current") << 30 << (QList<ListChange>()
1425             << ListChange::insert(1)
1426             << ListChange::setCurrent(1)
1427             << ListChange::remove(1)
1428             ) << 30 << 1;
1429
1430     QTest::newRow("insert before 0, then remove cross section of new and old items") << 10 << (QList<ListChange>()
1431             << ListChange::insert(0, 10)
1432             << ListChange::remove(5, 10)
1433             ) << 10 << 5;
1434
1435     QTest::newRow("insert multiple, then move new items to end") << 10 << (QList<ListChange>()
1436             << ListChange::insert(0, 3)
1437             << ListChange::move(0, 10, 3)
1438             ) << 13 << 0;
1439
1440     QTest::newRow("insert multiple, then move new and some old items to end") << 10 << (QList<ListChange>()
1441             << ListChange::insert(0, 3)
1442             << ListChange::move(0, 8, 5)
1443             ) << 13 << 11;
1444
1445     QTest::newRow("insert multiple at end, then move new and some old items to start") << 10 << (QList<ListChange>()
1446             << ListChange::setCurrent(9)
1447             << ListChange::insert(10, 3)
1448             << ListChange::move(8, 0, 5)
1449             ) << 13 << 1;
1450
1451
1452     QTest::newRow("move back and forth to same index") << 10 << (QList<ListChange>()
1453             << ListChange::setCurrent(1)
1454             << ListChange::move(1, 2, 2)
1455             << ListChange::move(2, 1, 2)
1456             ) << 10 << 1;
1457
1458     QTest::newRow("move forwards then back") << 10 << (QList<ListChange>()
1459             << ListChange::setCurrent(2)
1460             << ListChange::move(1, 2, 3)
1461             << ListChange::move(3, 0, 5)
1462             ) << 10 << 0;
1463
1464     QTest::newRow("move current, then remove it") << 10 << (QList<ListChange>()
1465             << ListChange::setCurrent(5)
1466             << ListChange::move(5, 0, 1)
1467             << ListChange::remove(0)
1468             ) << 9 << 0;
1469
1470     QTest::newRow("move current, then insert before it") << 10 << (QList<ListChange>()
1471             << ListChange::setCurrent(5)
1472             << ListChange::move(5, 0, 1)
1473             << ListChange::insert(0)
1474             ) << 11 << 1;
1475
1476     QTest::newRow("move multiple, then remove them") << 10 << (QList<ListChange>()
1477             << ListChange::setCurrent(1)
1478             << ListChange::move(5, 1, 3)
1479             << ListChange::remove(1, 3)
1480             ) << 7 << 1;
1481
1482     QTest::newRow("move multiple, then insert before them") << 10 << (QList<ListChange>()
1483             << ListChange::setCurrent(5)
1484             << ListChange::move(5, 1, 3)
1485             << ListChange::insert(1, 5)
1486             ) << 15 << 6;
1487
1488     QTest::newRow("move multiple, then insert after them") << 10 << (QList<ListChange>()
1489             << ListChange::setCurrent(3)
1490             << ListChange::move(0, 1, 2)
1491             << ListChange::insert(3, 5)
1492             ) << 15 << 8;
1493
1494
1495     QTest::newRow("clear current") << 0 << (QList<ListChange>()
1496             << ListChange::insert(0, 5)
1497             << ListChange::setCurrent(-1)
1498             << ListChange::remove(0, 5)
1499             << ListChange::insert(0, 5)
1500             ) << 5 << -1;
1501 }
1502
1503
1504 void tst_QQuickGridView::swapWithFirstItem()
1505 {
1506     // QTBUG_9697
1507     QQuickView *canvas = createView();
1508
1509     QaimModel model;
1510     for (int i = 0; i < 30; i++)
1511         model.addItem("Item" + QString::number(i), "");
1512
1513     QQmlContext *ctxt = canvas->rootContext();
1514     ctxt->setContextProperty("testModel", &model);
1515     ctxt->setContextProperty("testRightToLeft", QVariant(false));
1516     ctxt->setContextProperty("testTopToBottom", QVariant(false));
1517
1518     canvas->setSource(testFileUrl("gridview1.qml"));
1519     canvas->show();
1520     qApp->processEvents();
1521
1522     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1523     QTRY_VERIFY(gridview != 0);
1524
1525     // ensure content position is stable
1526     gridview->setContentY(0);
1527     model.moveItem(10, 0);
1528     QTRY_VERIFY(gridview->contentY() == 0);
1529
1530     delete canvas;
1531 }
1532
1533 void tst_QQuickGridView::currentIndex()
1534 {
1535     QaimModel model;
1536     for (int i = 0; i < 60; i++)
1537         model.addItem("Item" + QString::number(i), QString::number(i));
1538
1539     QQuickView *canvas = new QQuickView(0);
1540     canvas->setGeometry(0,0,240,320);
1541     canvas->show();
1542
1543     QQmlContext *ctxt = canvas->rootContext();
1544     ctxt->setContextProperty("testModel", &model);
1545
1546     QString filename(testFile("gridview-initCurrent.qml"));
1547     canvas->setSource(QUrl::fromLocalFile(filename));
1548
1549     qApp->processEvents();
1550
1551     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1552     QVERIFY(gridview != 0);
1553     QTRY_VERIFY(!QQuickItemPrivate::get(gridview)->polishScheduled);
1554
1555     QQuickItem *contentItem = gridview->contentItem();
1556     QVERIFY(contentItem != 0);
1557
1558     // current item should be third item
1559     QCOMPARE(gridview->currentIndex(), 35);
1560     QCOMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 35));
1561     QCOMPARE(gridview->currentItem()->y(), gridview->highlightItem()->y());
1562     QCOMPARE(gridview->contentY(), 400.0);
1563
1564     gridview->moveCurrentIndexRight();
1565     QCOMPARE(gridview->currentIndex(), 36);
1566     gridview->moveCurrentIndexDown();
1567     QCOMPARE(gridview->currentIndex(), 39);
1568     gridview->moveCurrentIndexUp();
1569     QCOMPARE(gridview->currentIndex(), 36);
1570     gridview->moveCurrentIndexLeft();
1571     QCOMPARE(gridview->currentIndex(), 35);
1572
1573     // wait until motion stops
1574     QTRY_VERIFY(gridview->verticalVelocity() == 0.0);
1575
1576     // no wrap
1577     gridview->setCurrentIndex(0);
1578     QCOMPARE(gridview->currentIndex(), 0);
1579     // confirm that the velocity is updated
1580     QTRY_VERIFY(gridview->verticalVelocity() != 0.0);
1581
1582     gridview->moveCurrentIndexUp();
1583     QCOMPARE(gridview->currentIndex(), 0);
1584
1585     gridview->moveCurrentIndexLeft();
1586     QCOMPARE(gridview->currentIndex(), 0);
1587
1588     gridview->setCurrentIndex(model.count()-1);
1589     QCOMPARE(gridview->currentIndex(), model.count()-1);
1590
1591     gridview->moveCurrentIndexRight();
1592     QCOMPARE(gridview->currentIndex(), model.count()-1);
1593
1594     gridview->moveCurrentIndexDown();
1595     QCOMPARE(gridview->currentIndex(), model.count()-1);
1596
1597     // with wrap
1598     gridview->setWrapEnabled(true);
1599
1600     gridview->setCurrentIndex(0);
1601     QCOMPARE(gridview->currentIndex(), 0);
1602
1603     gridview->moveCurrentIndexLeft();
1604     QCOMPARE(gridview->currentIndex(), model.count()-1);
1605
1606     qApp->processEvents();
1607     QTRY_COMPARE(gridview->contentY(), 880.0);
1608
1609     gridview->moveCurrentIndexRight();
1610     QCOMPARE(gridview->currentIndex(), 0);
1611
1612     QTRY_COMPARE(gridview->contentY(), 0.0);
1613
1614
1615     // footer should become visible if it is out of view, and then current index moves to the first row
1616     canvas->rootObject()->setProperty("showFooter", true);
1617     QTRY_VERIFY(gridview->footerItem());
1618     gridview->setCurrentIndex(model.count()-3);
1619     QTRY_VERIFY(gridview->footerItem()->y() > gridview->contentY() + gridview->height());
1620     gridview->setCurrentIndex(model.count()-2);
1621     QTRY_COMPARE(gridview->contentY() + gridview->height(), (60.0 * model.count()/3) + gridview->footerItem()->height());
1622     canvas->rootObject()->setProperty("showFooter", false);
1623
1624     // header should become visible if it is out of view, and then current index moves to the last row
1625     canvas->rootObject()->setProperty("showHeader", true);
1626     QTRY_VERIFY(gridview->headerItem());
1627     gridview->setCurrentIndex(3);
1628     QTRY_VERIFY(gridview->headerItem()->y() + gridview->headerItem()->height() < gridview->contentY());
1629     gridview->setCurrentIndex(1);
1630     QTRY_COMPARE(gridview->contentY(), -gridview->headerItem()->height());
1631     canvas->rootObject()->setProperty("showHeader", false);
1632
1633
1634     // Test keys
1635     canvas->requestActivateWindow();
1636     QTest::qWaitForWindowShown(canvas);
1637     QTRY_VERIFY(qGuiApp->focusWindow() == canvas);
1638
1639     gridview->setCurrentIndex(0);
1640
1641     QTest::keyClick(canvas, Qt::Key_Down);
1642     QCOMPARE(gridview->currentIndex(), 3);
1643
1644     QTest::keyClick(canvas, Qt::Key_Up);
1645     QCOMPARE(gridview->currentIndex(), 0);
1646
1647     // hold down Key_Down
1648     for (int i=0; i<(model.count() / 3) - 1; i++) {
1649         QTest::simulateEvent(canvas, true, Qt::Key_Down, Qt::NoModifier, "", true);
1650         QTRY_COMPARE(gridview->currentIndex(), i*3 + 3);
1651     }
1652     QTest::keyRelease(canvas, Qt::Key_Down);
1653     QTRY_COMPARE(gridview->currentIndex(), 57);
1654     QTRY_COMPARE(gridview->contentY(), 880.0);
1655
1656     // hold down Key_Up
1657     for (int i=(model.count() / 3) - 1; i > 0; i--) {
1658         QTest::simulateEvent(canvas, true, Qt::Key_Up, Qt::NoModifier, "", true);
1659         QTRY_COMPARE(gridview->currentIndex(), i*3 - 3);
1660     }
1661     QTest::keyRelease(canvas, Qt::Key_Up);
1662     QTRY_COMPARE(gridview->currentIndex(), 0);
1663     QTRY_COMPARE(gridview->contentY(), 0.0);
1664
1665
1666     gridview->setFlow(QQuickGridView::TopToBottom);
1667
1668     canvas->requestActivateWindow();
1669     QTest::qWaitForWindowShown(canvas);
1670     QVERIFY(qGuiApp->focusWindow() == canvas);
1671     qApp->processEvents();
1672
1673     QTest::keyClick(canvas, Qt::Key_Right);
1674     QCOMPARE(gridview->currentIndex(), 5);
1675
1676     QTest::keyClick(canvas, Qt::Key_Left);
1677     QCOMPARE(gridview->currentIndex(), 0);
1678
1679     QTest::keyClick(canvas, Qt::Key_Down);
1680     QCOMPARE(gridview->currentIndex(), 1);
1681
1682     QTest::keyClick(canvas, Qt::Key_Up);
1683     QCOMPARE(gridview->currentIndex(), 0);
1684
1685     // hold down Key_Right
1686     for (int i=0; i<(model.count() / 5) - 1; i++) {
1687         QTest::simulateEvent(canvas, true, Qt::Key_Right, Qt::NoModifier, "", true);
1688         QTRY_COMPARE(gridview->currentIndex(), i*5 + 5);
1689     }
1690
1691     QTest::keyRelease(canvas, Qt::Key_Right);
1692     QTRY_COMPARE(gridview->currentIndex(), 55);
1693     QTRY_COMPARE(gridview->contentX(), 720.0);
1694
1695     // hold down Key_Left
1696     for (int i=(model.count() / 5) - 1; i > 0; i--) {
1697         QTest::simulateEvent(canvas, true, Qt::Key_Left, Qt::NoModifier, "", true);
1698         QTRY_COMPARE(gridview->currentIndex(), i*5 - 5);
1699     }
1700     QTest::keyRelease(canvas, Qt::Key_Left);
1701     QTRY_COMPARE(gridview->currentIndex(), 0);
1702     QTRY_COMPARE(gridview->contentX(), 0.0);
1703
1704
1705     // turn off auto highlight
1706     gridview->setHighlightFollowsCurrentItem(false);
1707     QVERIFY(gridview->highlightFollowsCurrentItem() == false);
1708     QVERIFY(gridview->highlightItem());
1709     qreal hlPosX = gridview->highlightItem()->x();
1710     qreal hlPosY = gridview->highlightItem()->y();
1711
1712     gridview->setCurrentIndex(5);
1713     QTRY_COMPARE(gridview->highlightItem()->x(), hlPosX);
1714     QTRY_COMPARE(gridview->highlightItem()->y(), hlPosY);
1715
1716     // insert item before currentIndex
1717     gridview->setCurrentIndex(28);
1718     model.insertItem(0, "Foo", "1111");
1719     QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29);
1720
1721     // check removing highlight by setting currentIndex to -1;
1722     gridview->setCurrentIndex(-1);
1723
1724     QCOMPARE(gridview->currentIndex(), -1);
1725     QVERIFY(!gridview->highlightItem());
1726     QVERIFY(!gridview->currentItem());
1727
1728     gridview->setHighlightFollowsCurrentItem(true);
1729
1730     gridview->setFlow(QQuickGridView::LeftToRight);
1731     gridview->setLayoutDirection(Qt::RightToLeft);
1732
1733     canvas->requestActivateWindow();
1734     QTest::qWaitForWindowShown(canvas);
1735     QTRY_VERIFY(qGuiApp->focusWindow() == canvas);
1736     qApp->processEvents();
1737
1738     gridview->setCurrentIndex(35);
1739
1740     QTest::keyClick(canvas, Qt::Key_Right);
1741     QCOMPARE(gridview->currentIndex(), 34);
1742
1743     QTest::keyClick(canvas, Qt::Key_Down);
1744     QCOMPARE(gridview->currentIndex(), 37);
1745
1746     QTest::keyClick(canvas, Qt::Key_Up);
1747     QCOMPARE(gridview->currentIndex(), 34);
1748
1749     QTest::keyClick(canvas, Qt::Key_Left);
1750     QCOMPARE(gridview->currentIndex(), 35);
1751
1752
1753     // turn off auto highlight
1754     gridview->setHighlightFollowsCurrentItem(false);
1755     QVERIFY(gridview->highlightFollowsCurrentItem() == false);
1756     QVERIFY(gridview->highlightItem());
1757     hlPosX = gridview->highlightItem()->x();
1758     hlPosY = gridview->highlightItem()->y();
1759
1760     gridview->setCurrentIndex(5);
1761     QTRY_COMPARE(gridview->highlightItem()->x(), hlPosX);
1762     QTRY_COMPARE(gridview->highlightItem()->y(), hlPosY);
1763
1764     // insert item before currentIndex
1765     gridview->setCurrentIndex(28);
1766     model.insertItem(0, "Foo", "1111");
1767     QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29);
1768
1769     // check removing highlight by setting currentIndex to -1;
1770     gridview->setCurrentIndex(-1);
1771
1772     QCOMPARE(gridview->currentIndex(), -1);
1773     QVERIFY(!gridview->highlightItem());
1774     QVERIFY(!gridview->currentItem());
1775
1776     // moving currentItem out of view should make it invisible
1777     gridview->setCurrentIndex(0);
1778     QTRY_VERIFY(gridview->currentItem()->isVisible());
1779     gridview->setContentY(200);
1780     QTRY_VERIFY(!gridview->currentItem()->isVisible());
1781
1782     delete canvas;
1783 }
1784
1785 void tst_QQuickGridView::noCurrentIndex()
1786 {
1787     QaimModel model;
1788     for (int i = 0; i < 60; i++)
1789         model.addItem("Item" + QString::number(i), QString::number(i));
1790
1791     QQuickView *canvas = new QQuickView(0);
1792     canvas->setGeometry(0,0,240,320);
1793
1794     QQmlContext *ctxt = canvas->rootContext();
1795     ctxt->setContextProperty("testModel", &model);
1796
1797     QString filename(testFile("gridview-noCurrent.qml"));
1798     canvas->setSource(QUrl::fromLocalFile(filename));
1799     canvas->show();
1800     qApp->processEvents();
1801
1802     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1803     QVERIFY(gridview != 0);
1804     QQuickItem *contentItem = gridview->contentItem();
1805     QVERIFY(contentItem != 0);
1806     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1807
1808     // current index should be -1
1809     QCOMPARE(gridview->currentIndex(), -1);
1810     QVERIFY(!gridview->currentItem());
1811     QVERIFY(!gridview->highlightItem());
1812     QCOMPARE(gridview->contentY(), 0.0);
1813
1814     gridview->setCurrentIndex(5);
1815     QCOMPARE(gridview->currentIndex(), 5);
1816     QVERIFY(gridview->currentItem());
1817     QVERIFY(gridview->highlightItem());
1818
1819     delete canvas;
1820 }
1821
1822 void tst_QQuickGridView::changeFlow()
1823 {
1824     QQuickView *canvas = createView();
1825
1826     QaimModel model;
1827     for (int i = 0; i < 30; i++)
1828         model.addItem("Item" + QString::number(i), QString::number(i));
1829
1830     QQmlContext *ctxt = canvas->rootContext();
1831     ctxt->setContextProperty("testModel", &model);
1832     ctxt->setContextProperty("testRightToLeft", QVariant(false));
1833     ctxt->setContextProperty("testTopToBottom", QVariant(false));
1834
1835     canvas->setSource(testFileUrl("gridview1.qml"));
1836     qApp->processEvents();
1837
1838     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1839     QTRY_VERIFY(gridview != 0);
1840
1841     QQuickItem *contentItem = gridview->contentItem();
1842     QTRY_VERIFY(contentItem != 0);
1843
1844     // Confirm items positioned correctly and indexes correct
1845     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1846     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1847         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1848         if (!item) qWarning() << "Item" << i << "not found";
1849         QTRY_VERIFY(item);
1850         QTRY_COMPARE(item->x(), qreal((i%3)*80));
1851         QTRY_COMPARE(item->y(), qreal((i/3)*60));
1852         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
1853         QTRY_VERIFY(name != 0);
1854         QTRY_COMPARE(name->text(), model.name(i));
1855         QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
1856         QTRY_VERIFY(number != 0);
1857         QTRY_COMPARE(number->text(), model.number(i));
1858     }
1859
1860     ctxt->setContextProperty("testTopToBottom", QVariant(true));
1861
1862     // Confirm items positioned correctly and indexes correct
1863     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1864     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1865         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1866         if (!item) qWarning() << "Item" << i << "not found";
1867         QTRY_VERIFY(item);
1868         QTRY_COMPARE(item->x(), qreal((i/5)*80));
1869         QTRY_COMPARE(item->y(), qreal((i%5)*60));
1870         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
1871         QTRY_VERIFY(name != 0);
1872         QTRY_COMPARE(name->text(), model.name(i));
1873         QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
1874         QTRY_VERIFY(number != 0);
1875         QTRY_COMPARE(number->text(), model.number(i));
1876     }
1877
1878     ctxt->setContextProperty("testRightToLeft", QVariant(true));
1879
1880     // Confirm items positioned correctly and indexes correct
1881     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1882     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1883         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1884         if (!item) qWarning() << "Item" << i << "not found";
1885         QTRY_VERIFY(item);
1886         QTRY_COMPARE(item->x(), qreal(-(i/5)*80 - item->width()));
1887         QTRY_COMPARE(item->y(), qreal((i%5)*60));
1888         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
1889         QTRY_VERIFY(name != 0);
1890         QTRY_COMPARE(name->text(), model.name(i));
1891         QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
1892         QTRY_VERIFY(number != 0);
1893         QTRY_COMPARE(number->text(), model.number(i));
1894     }
1895     gridview->setContentX(100);
1896     QTRY_COMPARE(gridview->contentX(), 100.);
1897     ctxt->setContextProperty("testTopToBottom", QVariant(false));
1898     QTRY_COMPARE(gridview->contentX(), 0.);
1899
1900     // Confirm items positioned correctly and indexes correct
1901     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1902     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1903         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1904         if (!item) qWarning() << "Item" << i << "not found";
1905         QTRY_VERIFY(item);
1906         QTRY_COMPARE(item->x(), qreal(240 - (i%3+1)*80));
1907         QTRY_COMPARE(item->y(), qreal((i/3)*60));
1908         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
1909         QTRY_VERIFY(name != 0);
1910         QTRY_COMPARE(name->text(), model.name(i));
1911         QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
1912         QTRY_VERIFY(number != 0);
1913         QTRY_COMPARE(number->text(), model.number(i));
1914     }
1915
1916     delete canvas;
1917 }
1918
1919 void tst_QQuickGridView::defaultValues()
1920 {
1921     QQmlEngine engine;
1922     QQmlComponent c(&engine, testFileUrl("gridview3.qml"));
1923     QQuickGridView *obj = qobject_cast<QQuickGridView*>(c.create());
1924
1925     QTRY_VERIFY(obj != 0);
1926     QTRY_VERIFY(obj->model() == QVariant());
1927     QTRY_VERIFY(obj->delegate() == 0);
1928     QTRY_COMPARE(obj->currentIndex(), -1);
1929     QTRY_VERIFY(obj->currentItem() == 0);
1930     QTRY_COMPARE(obj->count(), 0);
1931     QTRY_VERIFY(obj->highlight() == 0);
1932     QTRY_VERIFY(obj->highlightItem() == 0);
1933     QTRY_COMPARE(obj->highlightFollowsCurrentItem(), true);
1934     QTRY_VERIFY(obj->flow() == 0);
1935     QTRY_COMPARE(obj->isWrapEnabled(), false);
1936     QTRY_COMPARE(obj->cacheBuffer(), 0);
1937     QTRY_COMPARE(obj->cellWidth(), qreal(100)); //### Should 100 be the default?
1938     QTRY_COMPARE(obj->cellHeight(), qreal(100));
1939     delete obj;
1940 }
1941
1942 void tst_QQuickGridView::properties()
1943 {
1944     QQmlEngine engine;
1945     QQmlComponent c(&engine, testFileUrl("gridview2.qml"));
1946     QQuickGridView *obj = qobject_cast<QQuickGridView*>(c.create());
1947
1948     QTRY_VERIFY(obj != 0);
1949     QTRY_VERIFY(obj->model() != QVariant());
1950     QTRY_VERIFY(obj->delegate() != 0);
1951     QTRY_COMPARE(obj->currentIndex(), 0);
1952     QTRY_VERIFY(obj->currentItem() != 0);
1953     QTRY_COMPARE(obj->count(), 4);
1954     QTRY_VERIFY(obj->highlight() != 0);
1955     QTRY_VERIFY(obj->highlightItem() != 0);
1956     QTRY_COMPARE(obj->highlightFollowsCurrentItem(), false);
1957     QTRY_VERIFY(obj->flow() == 0);
1958     QTRY_COMPARE(obj->isWrapEnabled(), true);
1959     QTRY_COMPARE(obj->cacheBuffer(), 200);
1960     QTRY_COMPARE(obj->cellWidth(), qreal(100));
1961     QTRY_COMPARE(obj->cellHeight(), qreal(100));
1962     delete obj;
1963 }
1964
1965 void tst_QQuickGridView::propertyChanges()
1966 {
1967     QQuickView *canvas = createView();
1968     QTRY_VERIFY(canvas);
1969     canvas->setSource(testFileUrl("propertychangestest.qml"));
1970
1971     QQuickGridView *gridView = canvas->rootObject()->findChild<QQuickGridView*>("gridView");
1972     QTRY_VERIFY(gridView);
1973
1974     QSignalSpy keyNavigationWrapsSpy(gridView, SIGNAL(keyNavigationWrapsChanged()));
1975     QSignalSpy cacheBufferSpy(gridView, SIGNAL(cacheBufferChanged()));
1976     QSignalSpy layoutSpy(gridView, SIGNAL(layoutDirectionChanged()));
1977     QSignalSpy flowSpy(gridView, SIGNAL(flowChanged()));
1978
1979     QTRY_COMPARE(gridView->isWrapEnabled(), true);
1980     QTRY_COMPARE(gridView->cacheBuffer(), 10);
1981     QTRY_COMPARE(gridView->flow(), QQuickGridView::LeftToRight);
1982
1983     gridView->setWrapEnabled(false);
1984     gridView->setCacheBuffer(3);
1985     gridView->setFlow(QQuickGridView::TopToBottom);
1986
1987     QTRY_COMPARE(gridView->isWrapEnabled(), false);
1988     QTRY_COMPARE(gridView->cacheBuffer(), 3);
1989     QTRY_COMPARE(gridView->flow(), QQuickGridView::TopToBottom);
1990
1991     QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
1992     QTRY_COMPARE(cacheBufferSpy.count(),1);
1993     QTRY_COMPARE(flowSpy.count(),1);
1994
1995     gridView->setWrapEnabled(false);
1996     gridView->setCacheBuffer(3);
1997     gridView->setFlow(QQuickGridView::TopToBottom);
1998
1999     QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
2000     QTRY_COMPARE(cacheBufferSpy.count(),1);
2001     QTRY_COMPARE(flowSpy.count(),1);
2002
2003     gridView->setFlow(QQuickGridView::LeftToRight);
2004     QTRY_COMPARE(gridView->flow(), QQuickGridView::LeftToRight);
2005
2006     gridView->setWrapEnabled(true);
2007     gridView->setCacheBuffer(5);
2008     gridView->setLayoutDirection(Qt::RightToLeft);
2009
2010     QTRY_COMPARE(gridView->isWrapEnabled(), true);
2011     QTRY_COMPARE(gridView->cacheBuffer(), 5);
2012     QTRY_COMPARE(gridView->layoutDirection(), Qt::RightToLeft);
2013
2014     QTRY_COMPARE(keyNavigationWrapsSpy.count(),2);
2015     QTRY_COMPARE(cacheBufferSpy.count(),2);
2016     QTRY_COMPARE(layoutSpy.count(),1);
2017     QTRY_COMPARE(flowSpy.count(),2);
2018
2019     gridView->setWrapEnabled(true);
2020     gridView->setCacheBuffer(5);
2021     gridView->setLayoutDirection(Qt::RightToLeft);
2022
2023     QTRY_COMPARE(keyNavigationWrapsSpy.count(),2);
2024     QTRY_COMPARE(cacheBufferSpy.count(),2);
2025     QTRY_COMPARE(layoutSpy.count(),1);
2026     QTRY_COMPARE(flowSpy.count(),2);
2027
2028     gridView->setFlow(QQuickGridView::TopToBottom);
2029     QTRY_COMPARE(gridView->flow(), QQuickGridView::TopToBottom);
2030     QTRY_COMPARE(flowSpy.count(),3);
2031
2032     gridView->setFlow(QQuickGridView::TopToBottom);
2033     QTRY_COMPARE(flowSpy.count(),3);
2034
2035     delete canvas;
2036 }
2037
2038 void tst_QQuickGridView::componentChanges()
2039 {
2040     QQuickView *canvas = createView();
2041     QTRY_VERIFY(canvas);
2042     canvas->setSource(testFileUrl("propertychangestest.qml"));
2043
2044     QQuickGridView *gridView = canvas->rootObject()->findChild<QQuickGridView*>("gridView");
2045     QTRY_VERIFY(gridView);
2046
2047     QQmlComponent component(canvas->engine());
2048     component.setData("import QtQuick 1.0; Rectangle { color: \"blue\"; }", QUrl::fromLocalFile(""));
2049
2050     QQmlComponent delegateComponent(canvas->engine());
2051     delegateComponent.setData("import QtQuick 1.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
2052
2053     QSignalSpy highlightSpy(gridView, SIGNAL(highlightChanged()));
2054     QSignalSpy delegateSpy(gridView, SIGNAL(delegateChanged()));
2055     QSignalSpy headerSpy(gridView, SIGNAL(headerChanged()));
2056     QSignalSpy footerSpy(gridView, SIGNAL(footerChanged()));
2057
2058     gridView->setHighlight(&component);
2059     gridView->setDelegate(&delegateComponent);
2060     gridView->setHeader(&component);
2061     gridView->setFooter(&component);
2062
2063     QTRY_COMPARE(gridView->highlight(), &component);
2064     QTRY_COMPARE(gridView->delegate(), &delegateComponent);
2065     QTRY_COMPARE(gridView->header(), &component);
2066     QTRY_COMPARE(gridView->footer(), &component);
2067
2068     QTRY_COMPARE(highlightSpy.count(),1);
2069     QTRY_COMPARE(delegateSpy.count(),1);
2070     QTRY_COMPARE(headerSpy.count(),1);
2071     QTRY_COMPARE(footerSpy.count(),1);
2072
2073     gridView->setHighlight(&component);
2074     gridView->setDelegate(&delegateComponent);
2075     gridView->setHeader(&component);
2076     gridView->setFooter(&component);
2077
2078     QTRY_COMPARE(highlightSpy.count(),1);
2079     QTRY_COMPARE(delegateSpy.count(),1);
2080     QTRY_COMPARE(headerSpy.count(),1);
2081     QTRY_COMPARE(footerSpy.count(),1);
2082
2083     delete canvas;
2084 }
2085
2086 void tst_QQuickGridView::modelChanges()
2087 {
2088     QQuickView *canvas = createView();
2089     QTRY_VERIFY(canvas);
2090     canvas->setSource(testFileUrl("propertychangestest.qml"));
2091
2092     QQuickGridView *gridView = canvas->rootObject()->findChild<QQuickGridView*>("gridView");
2093     QTRY_VERIFY(gridView);
2094
2095     QQuickListModel *alternateModel = canvas->rootObject()->findChild<QQuickListModel*>("alternateModel");
2096     QTRY_VERIFY(alternateModel);
2097     QVariant modelVariant = QVariant::fromValue<QObject *>(alternateModel);
2098     QSignalSpy modelSpy(gridView, SIGNAL(modelChanged()));
2099
2100     gridView->setModel(modelVariant);
2101     QTRY_COMPARE(gridView->model(), modelVariant);
2102     QTRY_COMPARE(modelSpy.count(),1);
2103
2104     gridView->setModel(modelVariant);
2105     QTRY_COMPARE(modelSpy.count(),1);
2106
2107     gridView->setModel(QVariant());
2108     QTRY_COMPARE(modelSpy.count(),2);
2109     delete canvas;
2110 }
2111
2112 void tst_QQuickGridView::positionViewAtIndex()
2113 {
2114     QQuickView *canvas = createView();
2115
2116     QaimModel model;
2117     for (int i = 0; i < 40; i++)
2118         model.addItem("Item" + QString::number(i), "");
2119
2120     QQmlContext *ctxt = canvas->rootContext();
2121     ctxt->setContextProperty("testModel", &model);
2122     ctxt->setContextProperty("testRightToLeft", QVariant(false));
2123     ctxt->setContextProperty("testTopToBottom", QVariant(false));
2124
2125     canvas->setSource(testFileUrl("gridview1.qml"));
2126     canvas->show();
2127     qApp->processEvents();
2128
2129     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2130     QTRY_VERIFY(gridview != 0);
2131     QQuickItem *contentItem = gridview->contentItem();
2132     QTRY_VERIFY(contentItem != 0);
2133     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
2134
2135     // Confirm items positioned correctly
2136     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2137     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2138         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2139         if (!item) qWarning() << "Item" << i << "not found";
2140         QTRY_VERIFY(item);
2141         QTRY_COMPARE(item->x(), (i%3)*80.);
2142         QTRY_COMPARE(item->y(), (i/3)*60.);
2143     }
2144
2145     // Position on a currently visible item
2146     gridview->positionViewAtIndex(4, QQuickGridView::Beginning);
2147     QTRY_COMPARE(gridview->indexAt(120, 90), 4);
2148     QTRY_COMPARE(gridview->contentY(), 60.);
2149
2150     // Confirm items positioned correctly
2151     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2152     for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
2153         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2154         if (!item) qWarning() << "Item" << i << "not found";
2155         QTRY_VERIFY(item);
2156         QTRY_COMPARE(item->x(), (i%3)*80.);
2157         QTRY_COMPARE(item->y(), (i/3)*60.);
2158     }
2159
2160     // Position on an item beyond the visible items
2161     gridview->positionViewAtIndex(21, QQuickGridView::Beginning);
2162     QTRY_COMPARE(gridview->indexAt(40, 450), 21);
2163     QTRY_COMPARE(gridview->contentY(), 420.);
2164
2165     // Confirm items positioned correctly
2166     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2167     for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
2168         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2169         if (!item) qWarning() << "Item" << i << "not found";
2170         QTRY_VERIFY(item);
2171         QTRY_COMPARE(item->x(), (i%3)*80.);
2172         QTRY_COMPARE(item->y(), (i/3)*60.);
2173     }
2174
2175     // Position on an item that would leave empty space if positioned at the top
2176     gridview->positionViewAtIndex(31, QQuickGridView::Beginning);
2177     QTRY_COMPARE(gridview->indexAt(120, 630), 31);
2178     QTRY_COMPARE(gridview->contentY(), 520.);
2179
2180     // Confirm items positioned correctly
2181     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2182     for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
2183         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2184         if (!item) qWarning() << "Item" << i << "not found";
2185         QTRY_VERIFY(item);
2186         QTRY_COMPARE(item->x(), (i%3)*80.);
2187         QTRY_COMPARE(item->y(), (i/3)*60.);
2188     }
2189
2190     // Position at the beginning again
2191     gridview->positionViewAtIndex(0, QQuickGridView::Beginning);
2192     QTRY_COMPARE(gridview->indexAt(0, 0), 0);
2193     QTRY_COMPARE(gridview->indexAt(40, 30), 0);
2194     QTRY_COMPARE(gridview->indexAt(80, 60), 4);
2195     QTRY_COMPARE(gridview->contentY(), 0.);
2196
2197     // Confirm items positioned correctly
2198     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2199     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2200         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2201         if (!item) qWarning() << "Item" << i << "not found";
2202         QTRY_VERIFY(item);
2203         QTRY_COMPARE(item->x(), (i%3)*80.);
2204         QTRY_COMPARE(item->y(), (i/3)*60.);
2205     }
2206
2207     // Position at End
2208     gridview->positionViewAtIndex(30, QQuickGridView::End);
2209     QTRY_COMPARE(gridview->contentY(), 340.);
2210
2211     // Position in Center
2212     gridview->positionViewAtIndex(15, QQuickGridView::Center);
2213     QTRY_COMPARE(gridview->contentY(), 170.);
2214
2215     // Ensure at least partially visible
2216     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2217     QTRY_COMPARE(gridview->contentY(), 170.);
2218
2219     gridview->setContentY(302);
2220     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2221     QTRY_COMPARE(gridview->contentY(), 302.);
2222
2223     gridview->setContentY(360);
2224     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2225     QTRY_COMPARE(gridview->contentY(), 300.);
2226
2227     gridview->setContentY(60);
2228     gridview->positionViewAtIndex(20, QQuickGridView::Visible);
2229     QTRY_COMPARE(gridview->contentY(), 60.);
2230
2231     gridview->setContentY(20);
2232     gridview->positionViewAtIndex(20, QQuickGridView::Visible);
2233     QTRY_COMPARE(gridview->contentY(), 100.);
2234
2235     // Ensure completely visible
2236     gridview->setContentY(120);
2237     gridview->positionViewAtIndex(20, QQuickGridView::Contain);
2238     QTRY_COMPARE(gridview->contentY(), 120.);
2239
2240     gridview->setContentY(302);
2241     gridview->positionViewAtIndex(15, QQuickGridView::Contain);
2242     QTRY_COMPARE(gridview->contentY(), 300.);
2243
2244     gridview->setContentY(60);
2245     gridview->positionViewAtIndex(20, QQuickGridView::Contain);
2246     QTRY_COMPARE(gridview->contentY(), 100.);
2247
2248     // Test for Top To Bottom layout
2249     ctxt->setContextProperty("testTopToBottom", QVariant(true));
2250
2251     // Confirm items positioned correctly
2252     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2253     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2254         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2255         if (!item) qWarning() << "Item" << i << "not found";
2256         QTRY_VERIFY(item);
2257         QTRY_COMPARE(item->x(), (i/5)*80.);
2258         QTRY_COMPARE(item->y(), (i%5)*60.);
2259     }
2260
2261     // Position at End
2262     gridview->positionViewAtIndex(30, QQuickGridView::End);
2263     QTRY_COMPARE(gridview->contentX(), 320.);
2264     QTRY_COMPARE(gridview->contentY(), 0.);
2265
2266     // Position in Center
2267     gridview->positionViewAtIndex(15, QQuickGridView::Center);
2268     QTRY_COMPARE(gridview->contentX(), 160.);
2269
2270     // Ensure at least partially visible
2271     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2272     QTRY_COMPARE(gridview->contentX(), 160.);
2273
2274     gridview->setContentX(170);
2275     gridview->positionViewAtIndex(25, QQuickGridView::Visible);
2276     QTRY_COMPARE(gridview->contentX(), 170.);
2277
2278     gridview->positionViewAtIndex(30, QQuickGridView::Visible);
2279     QTRY_COMPARE(gridview->contentX(), 320.);
2280
2281     gridview->setContentX(170);
2282     gridview->positionViewAtIndex(25, QQuickGridView::Contain);
2283     QTRY_COMPARE(gridview->contentX(), 240.);
2284
2285     // positionViewAtBeginning
2286     gridview->positionViewAtBeginning();
2287     QTRY_COMPARE(gridview->contentX(), 0.);
2288
2289     gridview->setContentX(80);
2290     canvas->rootObject()->setProperty("showHeader", true);
2291     gridview->positionViewAtBeginning();
2292     QTRY_COMPARE(gridview->contentX(), -30.);
2293
2294     // positionViewAtEnd
2295     gridview->positionViewAtEnd();
2296     QTRY_COMPARE(gridview->contentX(), 400.);   // 8*80 - 240   (8 columns)
2297
2298     gridview->setContentX(80);
2299     canvas->rootObject()->setProperty("showFooter", true);
2300     gridview->positionViewAtEnd();
2301     QTRY_COMPARE(gridview->contentX(), 430.);
2302
2303     // set current item to outside visible view, position at beginning
2304     // and ensure highlight moves to current item
2305     gridview->setCurrentIndex(6);
2306     gridview->positionViewAtBeginning();
2307     QTRY_COMPARE(gridview->contentX(), -30.);
2308     QVERIFY(gridview->highlightItem());
2309     QCOMPARE(gridview->highlightItem()->x(), 80.);
2310
2311     delete canvas;
2312 }
2313
2314 void tst_QQuickGridView::snapping()
2315 {
2316     QQuickView *canvas = createView();
2317
2318     QaimModel model;
2319     for (int i = 0; i < 40; i++)
2320         model.addItem("Item" + QString::number(i), "");
2321
2322     QQmlContext *ctxt = canvas->rootContext();
2323     ctxt->setContextProperty("testModel", &model);
2324     ctxt->setContextProperty("testTopToBottom", QVariant(false));
2325     ctxt->setContextProperty("testRightToLeft", QVariant(false));
2326
2327     canvas->setSource(testFileUrl("gridview1.qml"));
2328     qApp->processEvents();
2329
2330     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2331     QTRY_VERIFY(gridview != 0);
2332
2333     gridview->setHeight(220);
2334     QCOMPARE(gridview->height(), 220.);
2335
2336     gridview->positionViewAtIndex(12, QQuickGridView::Visible);
2337     QCOMPARE(gridview->contentY(), 80.);
2338
2339     gridview->setContentY(0);
2340     QCOMPARE(gridview->contentY(), 0.);
2341
2342     gridview->setSnapMode(QQuickGridView::SnapToRow);
2343     QCOMPARE(gridview->snapMode(), QQuickGridView::SnapToRow);
2344
2345     gridview->positionViewAtIndex(12, QQuickGridView::Visible);
2346     QCOMPARE(gridview->contentY(), 60.);
2347
2348     gridview->positionViewAtIndex(15, QQuickGridView::End);
2349     QCOMPARE(gridview->contentY(), 120.);
2350
2351     delete canvas;
2352
2353 }
2354
2355 void tst_QQuickGridView::mirroring()
2356 {
2357     QQuickView *canvasA = createView();
2358     canvasA->setSource(testFileUrl("mirroring.qml"));
2359     QQuickGridView *gridviewA = findItem<QQuickGridView>(canvasA->rootObject(), "view");
2360     QTRY_VERIFY(gridviewA != 0);
2361
2362     QQuickView *canvasB = createView();
2363     canvasB->setSource(testFileUrl("mirroring.qml"));
2364     QQuickGridView *gridviewB = findItem<QQuickGridView>(canvasB->rootObject(), "view");
2365     QTRY_VERIFY(gridviewA != 0);
2366     qApp->processEvents();
2367
2368     QList<QString> objectNames;
2369     objectNames << "item1" << "item2"; // << "item3"
2370
2371     gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
2372     gridviewB->setProperty("layoutDirection", Qt::RightToLeft);
2373     QCOMPARE(gridviewA->layoutDirection(), gridviewA->effectiveLayoutDirection());
2374
2375     // LTR != RTL
2376     foreach (const QString objectName, objectNames)
2377         QVERIFY(findItem<QQuickItem>(gridviewA, objectName)->x() != findItem<QQuickItem>(gridviewB, objectName)->x());
2378
2379     gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
2380     gridviewB->setProperty("layoutDirection", Qt::LeftToRight);
2381
2382     // LTR == LTR
2383     foreach (const QString objectName, objectNames)
2384         QCOMPARE(findItem<QQuickItem>(gridviewA, objectName)->x(), findItem<QQuickItem>(gridviewB, objectName)->x());
2385
2386     QVERIFY(gridviewB->layoutDirection() == gridviewB->effectiveLayoutDirection());
2387     QQuickItemPrivate::get(gridviewB)->setLayoutMirror(true);
2388     QVERIFY(gridviewB->layoutDirection() != gridviewB->effectiveLayoutDirection());
2389
2390     // LTR != LTR+mirror
2391     foreach (const QString objectName, objectNames)
2392         QVERIFY(findItem<QQuickItem>(gridviewA, objectName)->x() != findItem<QQuickItem>(gridviewB, objectName)->x());
2393
2394     gridviewA->setProperty("layoutDirection", Qt::RightToLeft);
2395
2396     // RTL == LTR+mirror
2397     foreach (const QString objectName, objectNames)
2398         QCOMPARE(findItem<QQuickItem>(gridviewA, objectName)->x(), findItem<QQuickItem>(gridviewB, objectName)->x());
2399
2400     gridviewB->setProperty("layoutDirection", Qt::RightToLeft);
2401
2402     // RTL != RTL+mirror
2403     foreach (const QString objectName, objectNames)
2404         QVERIFY(findItem<QQuickItem>(gridviewA, objectName)->x() != findItem<QQuickItem>(gridviewB, objectName)->x());
2405
2406     gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
2407
2408     // LTR == RTL+mirror
2409     foreach (const QString objectName, objectNames)
2410         QCOMPARE(findItem<QQuickItem>(gridviewA, objectName)->x(), findItem<QQuickItem>(gridviewB, objectName)->x());
2411
2412     delete canvasA;
2413     delete canvasB;
2414 }
2415
2416 void tst_QQuickGridView::positionViewAtIndex_rightToLeft()
2417 {
2418     QQuickView *canvas = createView();
2419
2420     QaimModel model;
2421     for (int i = 0; i < 40; i++)
2422         model.addItem("Item" + QString::number(i), "");
2423
2424     QQmlContext *ctxt = canvas->rootContext();
2425     ctxt->setContextProperty("testModel", &model);
2426     ctxt->setContextProperty("testTopToBottom", QVariant(true));
2427     ctxt->setContextProperty("testRightToLeft", QVariant(true));
2428
2429     canvas->setSource(testFileUrl("gridview1.qml"));
2430     qApp->processEvents();
2431
2432     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2433     QTRY_VERIFY(gridview != 0);
2434
2435     QQuickItem *contentItem = gridview->contentItem();
2436     QTRY_VERIFY(contentItem != 0);
2437
2438     // Confirm items positioned correctly
2439     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2440     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2441         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2442         if (!item) qWarning() << "Item" << i << "not found";
2443         QTRY_VERIFY(item);
2444         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
2445         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2446     }
2447
2448     // Position on a currently visible item
2449     gridview->positionViewAtIndex(6, QQuickGridView::Beginning);
2450     QTRY_COMPARE(gridview->contentX(), -320.);
2451
2452     // Confirm items positioned correctly
2453     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2454     for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
2455         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2456         if (!item) qWarning() << "Item" << i << "not found";
2457         QTRY_VERIFY(item);
2458         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
2459         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2460     }
2461
2462     // Position on an item beyond the visible items
2463     gridview->positionViewAtIndex(21, QQuickGridView::Beginning);
2464     QTRY_COMPARE(gridview->contentX(), -560.);
2465
2466     // Confirm items positioned correctly
2467     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2468     for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
2469         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2470         if (!item) qWarning() << "Item" << i << "not found";
2471         QTRY_VERIFY(item);
2472         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
2473         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2474     }
2475
2476     // Position on an item that would leave empty space if positioned at the top
2477     gridview->positionViewAtIndex(31, QQuickGridView::Beginning);
2478     QTRY_COMPARE(gridview->contentX(), -640.);
2479
2480     // Confirm items positioned correctly
2481     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2482     for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
2483         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2484         if (!item) qWarning() << "Item" << i << "not found";
2485         QTRY_VERIFY(item);
2486         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
2487         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2488     }
2489
2490     // Position at the beginning again
2491     gridview->positionViewAtIndex(0, QQuickGridView::Beginning);
2492     QTRY_COMPARE(gridview->contentX(), -240.);
2493
2494     // Confirm items positioned correctly
2495     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2496     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2497         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2498         if (!item) qWarning() << "Item" << i << "not found";
2499         QTRY_VERIFY(item);
2500         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
2501         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2502     }
2503
2504     // Position at End
2505     gridview->positionViewAtIndex(30, QQuickGridView::End);
2506     QTRY_COMPARE(gridview->contentX(), -560.);
2507
2508     // Position in Center
2509     gridview->positionViewAtIndex(15, QQuickGridView::Center);
2510     QTRY_COMPARE(gridview->contentX(), -400.);
2511
2512     // Ensure at least partially visible
2513     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2514     QTRY_COMPARE(gridview->contentX(), -400.);
2515
2516     gridview->setContentX(-555.);
2517     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2518     QTRY_COMPARE(gridview->contentX(), -555.);
2519
2520     gridview->setContentX(-239);
2521     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2522     QTRY_COMPARE(gridview->contentX(), -320.);
2523
2524     gridview->setContentX(-239);
2525     gridview->positionViewAtIndex(20, QQuickGridView::Visible);
2526     QTRY_COMPARE(gridview->contentX(), -400.);
2527
2528     gridview->setContentX(-640);
2529     gridview->positionViewAtIndex(20, QQuickGridView::Visible);
2530     QTRY_COMPARE(gridview->contentX(), -560.);
2531
2532     // Ensure completely visible
2533     gridview->setContentX(-400);
2534     gridview->positionViewAtIndex(20, QQuickGridView::Contain);
2535     QTRY_COMPARE(gridview->contentX(), -400.);
2536
2537     gridview->setContentX(-315);
2538     gridview->positionViewAtIndex(15, QQuickGridView::Contain);
2539     QTRY_COMPARE(gridview->contentX(), -320.);
2540
2541     gridview->setContentX(-640);
2542     gridview->positionViewAtIndex(20, QQuickGridView::Contain);
2543     QTRY_COMPARE(gridview->contentX(), -560.);
2544
2545     delete canvas;
2546 }
2547
2548 void tst_QQuickGridView::resetModel()
2549 {
2550     QQuickView *canvas = createView();
2551
2552     QStringList strings;
2553     strings << "one" << "two" << "three";
2554     QStringListModel model(strings);
2555
2556     QQmlContext *ctxt = canvas->rootContext();
2557     ctxt->setContextProperty("testModel", &model);
2558
2559     canvas->setSource(testFileUrl("displaygrid.qml"));
2560     canvas->show();
2561     qApp->processEvents();
2562
2563     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2564     QTRY_VERIFY(gridview != 0);
2565     QQuickItem *contentItem = gridview->contentItem();
2566     QTRY_VERIFY(contentItem != 0);
2567     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
2568
2569     QTRY_COMPARE(gridview->count(), model.rowCount());
2570
2571     for (int i = 0; i < model.rowCount(); ++i) {
2572         QQuickText *display = findItem<QQuickText>(contentItem, "displayText", i);
2573         QTRY_VERIFY(display != 0);
2574         QTRY_COMPARE(display->text(), strings.at(i));
2575     }
2576
2577     strings.clear();
2578     strings << "four" << "five" << "six" << "seven";
2579     model.setStringList(strings);
2580
2581     QTRY_COMPARE(gridview->count(), model.rowCount());
2582
2583     for (int i = 0; i < model.rowCount(); ++i) {
2584         QQuickText *display = findItem<QQuickText>(contentItem, "displayText", i);
2585         QTRY_VERIFY(display != 0);
2586         QTRY_COMPARE(display->text(), strings.at(i));
2587     }
2588
2589     delete canvas;
2590 }
2591
2592 void tst_QQuickGridView::enforceRange()
2593 {
2594     QQuickView *canvas = createView();
2595
2596     QaimModel model;
2597     for (int i = 0; i < 30; i++)
2598         model.addItem("Item" + QString::number(i), "");
2599
2600     QQmlContext *ctxt = canvas->rootContext();
2601     ctxt->setContextProperty("testModel", &model);
2602     ctxt->setContextProperty("testRightToLeft", QVariant(false));
2603     ctxt->setContextProperty("testTopToBottom", QVariant(false));
2604
2605     canvas->setSource(testFileUrl("gridview-enforcerange.qml"));
2606     canvas->show();
2607     qApp->processEvents();
2608     QVERIFY(canvas->rootObject() != 0);
2609
2610     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2611     QTRY_VERIFY(gridview != 0);
2612
2613     QTRY_COMPARE(gridview->preferredHighlightBegin(), 100.0);
2614     QTRY_COMPARE(gridview->preferredHighlightEnd(), 100.0);
2615     QTRY_COMPARE(gridview->highlightRangeMode(), QQuickGridView::StrictlyEnforceRange);
2616     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
2617
2618     QQuickItem *contentItem = gridview->contentItem();
2619     QTRY_VERIFY(contentItem != 0);
2620
2621     // view should be positioned at the top of the range.
2622     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
2623     QTRY_VERIFY(item);
2624     QTRY_COMPARE(gridview->contentY(), -100.0);
2625
2626     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 0);
2627     QTRY_VERIFY(name != 0);
2628     QTRY_COMPARE(name->text(), model.name(0));
2629     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 0);
2630     QTRY_VERIFY(number != 0);
2631     QTRY_COMPARE(number->text(), model.number(0));
2632
2633     // Check currentIndex is updated when contentItem moves
2634     gridview->setContentY(0);
2635     QTRY_COMPARE(gridview->currentIndex(), 2);
2636
2637     gridview->setCurrentIndex(5);
2638     QTRY_COMPARE(gridview->contentY(), 100.);
2639
2640     QaimModel model2;
2641     for (int i = 0; i < 5; i++)
2642         model2.addItem("Item" + QString::number(i), "");
2643
2644     ctxt->setContextProperty("testModel", &model2);
2645     QCOMPARE(gridview->count(), 5);
2646
2647     delete canvas;
2648 }
2649
2650 void tst_QQuickGridView::enforceRange_rightToLeft()
2651 {
2652     QQuickView *canvas = createView();
2653
2654     QaimModel model;
2655     for (int i = 0; i < 30; i++)
2656         model.addItem("Item" + QString::number(i), "");
2657
2658     QQmlContext *ctxt = canvas->rootContext();
2659     ctxt->setContextProperty("testModel", &model);
2660     ctxt->setContextProperty("testRightToLeft", QVariant(true));
2661     ctxt->setContextProperty("testTopToBottom", QVariant(true));
2662
2663     canvas->setSource(testFileUrl("gridview-enforcerange.qml"));
2664     qApp->processEvents();
2665     QVERIFY(canvas->rootObject() != 0);
2666
2667     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2668     QTRY_VERIFY(gridview != 0);
2669
2670     QCOMPARE(gridview->preferredHighlightBegin(), 100.0);
2671     QCOMPARE(gridview->preferredHighlightEnd(), 100.0);
2672     QCOMPARE(gridview->highlightRangeMode(), QQuickGridView::StrictlyEnforceRange);
2673
2674     QQuickItem *contentItem = gridview->contentItem();
2675     QVERIFY(contentItem != 0);
2676
2677     // view should be positioned at the top of the range.
2678     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
2679     QVERIFY(item);
2680     QTRY_COMPARE(gridview->contentX(), -140.);
2681     QTRY_COMPARE(gridview->contentY(), 0.0);
2682
2683     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 0);
2684     QTRY_VERIFY(name != 0);
2685     QTRY_COMPARE(name->text(), model.name(0));
2686     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 0);
2687     QTRY_VERIFY(number != 0);
2688     QTRY_COMPARE(number->text(), model.number(0));
2689
2690     // Check currentIndex is updated when contentItem moves
2691     gridview->setContentX(-240);
2692     QTRY_COMPARE(gridview->currentIndex(), 3);
2693
2694     gridview->setCurrentIndex(7);
2695     QTRY_COMPARE(gridview->contentX(), -340.);
2696     QTRY_COMPARE(gridview->contentY(), 0.0);
2697
2698     QaimModel model2;
2699     for (int i = 0; i < 5; i++)
2700         model2.addItem("Item" + QString::number(i), "");
2701
2702     ctxt->setContextProperty("testModel", &model2);
2703     QCOMPARE(gridview->count(), 5);
2704
2705     delete canvas;
2706 }
2707
2708 void tst_QQuickGridView::QTBUG_8456()
2709 {
2710     QQuickView *canvas = createView();
2711
2712     canvas->setSource(testFileUrl("setindex.qml"));
2713     qApp->processEvents();
2714
2715     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2716     QTRY_VERIFY(gridview != 0);
2717
2718     QTRY_COMPARE(gridview->currentIndex(), 0);
2719
2720     delete canvas;
2721 }
2722
2723 void tst_QQuickGridView::manualHighlight()
2724 {
2725     QQuickView *canvas = createView();
2726
2727     QString filename(testFile("manual-highlight.qml"));
2728     canvas->setSource(QUrl::fromLocalFile(filename));
2729
2730     qApp->processEvents();
2731
2732     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2733     QTRY_VERIFY(gridview != 0);
2734
2735     QQuickItem *contentItem = gridview->contentItem();
2736     QTRY_VERIFY(contentItem != 0);
2737
2738     QTRY_COMPARE(gridview->currentIndex(), 0);
2739     QTRY_COMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
2740     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
2741     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
2742
2743     gridview->setCurrentIndex(2);
2744
2745     QTRY_COMPARE(gridview->currentIndex(), 2);
2746     QTRY_COMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 2));
2747     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
2748     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
2749
2750     gridview->positionViewAtIndex(8, QQuickGridView::Contain);
2751
2752     QTRY_COMPARE(gridview->currentIndex(), 2);
2753     QTRY_COMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 2));
2754     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
2755     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
2756
2757     gridview->setFlow(QQuickGridView::TopToBottom);
2758     QTRY_COMPARE(gridview->flow(), QQuickGridView::TopToBottom);
2759
2760     gridview->setCurrentIndex(0);
2761     QTRY_COMPARE(gridview->currentIndex(), 0);
2762     QTRY_COMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
2763     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
2764     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
2765
2766     delete canvas;
2767 }
2768
2769
2770 void tst_QQuickGridView::footer()
2771 {
2772     QFETCH(QQuickGridView::Flow, flow);
2773     QFETCH(Qt::LayoutDirection, layoutDirection);
2774     QFETCH(QPointF, initialFooterPos);
2775     QFETCH(QPointF, changedFooterPos);
2776     QFETCH(QPointF, initialContentPos);
2777     QFETCH(QPointF, changedContentPos);
2778     QFETCH(QPointF, firstDelegatePos);
2779     QFETCH(QPointF, resizeContentPos);
2780
2781     QQuickView *canvas = createView();
2782     canvas->show();
2783
2784     QaimModel model;
2785     for (int i = 0; i < 7; i++)
2786         model.addItem("Item" + QString::number(i), "");
2787
2788     QQmlContext *ctxt = canvas->rootContext();
2789     ctxt->setContextProperty("testModel", &model);
2790
2791     canvas->setSource(testFileUrl("footer.qml"));
2792     qApp->processEvents();
2793
2794     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2795     QTRY_VERIFY(gridview != 0);
2796     gridview->setFlow(flow);
2797     gridview->setLayoutDirection(layoutDirection);
2798
2799     QQuickItem *contentItem = gridview->contentItem();
2800     QTRY_VERIFY(contentItem != 0);
2801
2802     QQuickText *footer = findItem<QQuickText>(contentItem, "footer");
2803     QVERIFY(footer);
2804
2805     QVERIFY(footer == gridview->footerItem());
2806
2807     QCOMPARE(footer->pos(), initialFooterPos);
2808     QCOMPARE(footer->width(), 100.);
2809     QCOMPARE(footer->height(), 30.);
2810     QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
2811
2812     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
2813     QVERIFY(item);
2814     QCOMPARE(item->pos(), firstDelegatePos);
2815
2816     if (flow == QQuickGridView::LeftToRight) {
2817         // shrink by one row
2818         model.removeItem(2);
2819         QTRY_COMPARE(footer->y(), initialFooterPos.y() - gridview->cellHeight());
2820     } else {
2821         // shrink by one column
2822         model.removeItem(2);
2823         model.removeItem(3);
2824         if (layoutDirection == Qt::LeftToRight)
2825             QTRY_COMPARE(footer->x(), initialFooterPos.x() - gridview->cellWidth());
2826         else
2827             QTRY_COMPARE(footer->x(), initialFooterPos.x() + gridview->cellWidth());
2828     }
2829
2830     // remove all items
2831     model.clear();
2832
2833     QPointF posWhenNoItems(0, 0);
2834     if (layoutDirection == Qt::RightToLeft)
2835         posWhenNoItems.setX(flow == QQuickGridView::LeftToRight ? gridview->width() - footer->width() : -footer->width());
2836     QTRY_COMPARE(footer->pos(), posWhenNoItems);
2837
2838     // if header is present, it's at a negative pos, so the footer should not move
2839     canvas->rootObject()->setProperty("showHeader", true);
2840     QVERIFY(findItem<QQuickItem>(contentItem, "header") != 0);
2841     QTRY_COMPARE(footer->pos(), posWhenNoItems);
2842     canvas->rootObject()->setProperty("showHeader", false);
2843
2844     // add 30 items
2845     for (int i = 0; i < 30; i++)
2846         model.addItem("Item" + QString::number(i), "");
2847
2848     QSignalSpy footerItemSpy(gridview, SIGNAL(footerItemChanged()));
2849     QMetaObject::invokeMethod(canvas->rootObject(), "changeFooter");
2850
2851     QCOMPARE(footerItemSpy.count(), 1);
2852
2853     footer = findItem<QQuickText>(contentItem, "footer");
2854     QVERIFY(!footer);
2855     footer = findItem<QQuickText>(contentItem, "footer2");
2856     QVERIFY(footer);
2857
2858     QVERIFY(footer == gridview->footerItem());
2859
2860     QCOMPARE(footer->pos(), changedFooterPos);
2861     QCOMPARE(footer->width(), 50.);
2862     QCOMPARE(footer->height(), 20.);
2863     QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), changedContentPos);
2864
2865     item = findItem<QQuickItem>(contentItem, "wrapper", 0);
2866     QVERIFY(item);
2867     QCOMPARE(item->pos(), firstDelegatePos);
2868
2869     gridview->positionViewAtEnd();
2870     footer->setHeight(10);
2871     footer->setWidth(40);
2872     QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), resizeContentPos);
2873
2874     delete canvas;
2875 }
2876
2877 void tst_QQuickGridView::footer_data()
2878 {
2879     QTest::addColumn<QQuickGridView::Flow>("flow");
2880     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
2881     QTest::addColumn<QPointF>("initialFooterPos");
2882     QTest::addColumn<QPointF>("changedFooterPos");
2883     QTest::addColumn<QPointF>("initialContentPos");
2884     QTest::addColumn<QPointF>("changedContentPos");
2885     QTest::addColumn<QPointF>("firstDelegatePos");
2886     QTest::addColumn<QPointF>("resizeContentPos");
2887
2888     // footer1 = 100 x 30
2889     // footer2 = 50 x 20
2890     // cells = 80 * 60
2891     // view width = 240
2892     // view height = 320
2893
2894     // footer below items, bottom left
2895     QTest::newRow("flow left to right") << QQuickGridView::LeftToRight << Qt::LeftToRight
2896         << QPointF(0, 3 * 60)  // 180 = height of 3 rows (cell height is 60)
2897         << QPointF(0, 10 * 60)  // 30 items = 10 rows
2898         << QPointF(0, 0)
2899         << QPointF(0, 0)
2900         << QPointF(0, 0)
2901         << QPointF(0, 10 * 60 - 320 + 10);
2902
2903     // footer below items, bottom right
2904     QTest::newRow("flow left to right, layout right to left") << QQuickGridView::LeftToRight << Qt::RightToLeft
2905         << QPointF(240 - 100, 3 * 60)
2906         << QPointF((240 - 100) + 50, 10 * 60)     // 50 = width diff between old and new footers
2907         << QPointF(0, 0)
2908         << QPointF(0, 0)
2909         << QPointF(240 - 80, 0)
2910         << QPointF(0, 10 * 60 - 320 + 10);
2911
2912     // footer to right of items
2913     QTest::newRow("flow top to bottom, layout left to right") << QQuickGridView::TopToBottom << Qt::LeftToRight
2914         << QPointF(2 * 80, 0)      // 2 columns, cell width 80
2915         << QPointF(6 * 80, 0)      // 30 items = 6 columns
2916         << QPointF(0, 0)
2917         << QPointF(0, 0)
2918         << QPointF(0, 0)
2919         << QPointF(6 * 80 - 240 + 40, 0);
2920
2921     // footer to left of items
2922     QTest::newRow("flow top to bottom, layout right to left") << QQuickGridView::TopToBottom << Qt::RightToLeft
2923         << QPointF(-(2 * 80) - 100, 0)
2924         << QPointF(-(6 * 80) - 50, 0)     // 50 = new footer width
2925         << QPointF(-240, 0)
2926         << QPointF(-240, 0)    // unchanged, footer change doesn't change content pos
2927         << QPointF(-80, 0)
2928         << QPointF(-(6 * 80) - 40, 0);
2929 }
2930
2931 void tst_QQuickGridView::header()
2932 {
2933     QFETCH(QQuickGridView::Flow, flow);
2934     QFETCH(Qt::LayoutDirection, layoutDirection);
2935     QFETCH(QPointF, initialHeaderPos);
2936     QFETCH(QPointF, changedHeaderPos);
2937     QFETCH(QPointF, initialContentPos);
2938     QFETCH(QPointF, changedContentPos);
2939     QFETCH(QPointF, firstDelegatePos);
2940     QFETCH(QPointF, resizeContentPos);
2941
2942     QaimModel model;
2943     for (int i = 0; i < 30; i++)
2944         model.addItem("Item" + QString::number(i), "");
2945
2946     QQuickView *canvas = createView();
2947     canvas->rootContext()->setContextProperty("testModel", &model);
2948     canvas->rootContext()->setContextProperty("initialViewWidth", 240);
2949     canvas->rootContext()->setContextProperty("initialViewHeight", 320);
2950     canvas->setSource(testFileUrl("header.qml"));
2951     canvas->show();
2952     qApp->processEvents();
2953
2954     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2955     QTRY_VERIFY(gridview != 0);
2956     gridview->setFlow(flow);
2957     gridview->setLayoutDirection(layoutDirection);
2958     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
2959
2960     QQuickItem *contentItem = gridview->contentItem();
2961     QTRY_VERIFY(contentItem != 0);
2962
2963     QQuickText *header = findItem<QQuickText>(contentItem, "header");
2964     QVERIFY(header);
2965
2966     QVERIFY(header == gridview->headerItem());
2967
2968     QCOMPARE(header->pos(), initialHeaderPos);
2969     QCOMPARE(header->width(), 100.);
2970     QCOMPARE(header->height(), 30.);
2971     QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
2972
2973     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
2974     QVERIFY(item);
2975     QCOMPARE(item->pos(), firstDelegatePos);
2976
2977     model.clear();
2978     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
2979     QCOMPARE(header->pos(), initialHeaderPos); // header should stay where it is
2980
2981     for (int i = 0; i < 30; i++)
2982         model.addItem("Item" + QString::number(i), "");
2983
2984     QSignalSpy headerItemSpy(gridview, SIGNAL(headerItemChanged()));
2985     QMetaObject::invokeMethod(canvas->rootObject(), "changeHeader");
2986
2987     QCOMPARE(headerItemSpy.count(), 1);
2988
2989     header = findItem<QQuickText>(contentItem, "header");
2990     QVERIFY(!header);
2991     header = findItem<QQuickText>(contentItem, "header2");
2992     QVERIFY(header);
2993
2994     QVERIFY(header == gridview->headerItem());
2995
2996     QCOMPARE(header->pos(), changedHeaderPos);
2997     QCOMPARE(header->width(), 50.);
2998     QCOMPARE(header->height(), 20.);
2999     QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), changedContentPos);
3000
3001     item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3002     QVERIFY(item);
3003     QCOMPARE(item->pos(), firstDelegatePos);
3004
3005     header->setHeight(10);
3006     header->setWidth(40);
3007     QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), resizeContentPos);
3008
3009     delete canvas;
3010
3011
3012     // QTBUG-21207 header should become visible if view resizes from initial empty size
3013
3014     canvas = createView();
3015     canvas->rootContext()->setContextProperty("testModel", &model);
3016     canvas->rootContext()->setContextProperty("initialViewWidth", 240);
3017     canvas->rootContext()->setContextProperty("initialViewHeight", 320);
3018     canvas->setSource(testFileUrl("header.qml"));
3019     canvas->show();
3020     qApp->processEvents();
3021
3022     gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3023     QTRY_VERIFY(gridview != 0);
3024     gridview->setFlow(flow);
3025     gridview->setLayoutDirection(layoutDirection);
3026     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3027
3028     gridview->setWidth(240);
3029     gridview->setHeight(320);
3030     QTRY_COMPARE(gridview->headerItem()->pos(), initialHeaderPos);
3031     QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
3032
3033     delete canvas;
3034 }
3035
3036 void tst_QQuickGridView::header_data()
3037 {
3038     QTest::addColumn<QQuickGridView::Flow>("flow");
3039     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3040     QTest::addColumn<QPointF>("initialHeaderPos");
3041     QTest::addColumn<QPointF>("changedHeaderPos");
3042     QTest::addColumn<QPointF>("initialContentPos");
3043     QTest::addColumn<QPointF>("changedContentPos");
3044     QTest::addColumn<QPointF>("firstDelegatePos");
3045     QTest::addColumn<QPointF>("resizeContentPos");
3046
3047     // header1 = 100 x 30
3048     // header2 = 50 x 20
3049     // cells = 80 x 60
3050     // view width = 240
3051
3052     // header above items, top left
3053     QTest::newRow("flow left to right") << QQuickGridView::LeftToRight << Qt::LeftToRight
3054         << QPointF(0, -30)
3055         << QPointF(0, -20)
3056         << QPointF(0, -30)
3057         << QPointF(0, -20)
3058         << QPointF(0, 0)
3059         << QPointF(0, -10);
3060
3061     // header above items, top right
3062     QTest::newRow("flow left to right, layout right to left") << QQuickGridView::LeftToRight << Qt::RightToLeft
3063         << QPointF(240 - 100, -30)
3064         << QPointF((240 - 100) + 50, -20)     // 50 = width diff between old and new headers
3065         << QPointF(0, -30)
3066         << QPointF(0, -20)
3067         << QPointF(160, 0)
3068         << QPointF(0, -10);
3069
3070     // header to left of items
3071     QTest::newRow("flow top to bottom, layout left to right") << QQuickGridView::TopToBottom << Qt::LeftToRight
3072         << QPointF(-100, 0)
3073         << QPointF(-50, 0)
3074         << QPointF(-100, 0)
3075         << QPointF(-50, 0)
3076         << QPointF(0, 0)
3077         << QPointF(-40, 0);
3078
3079     // header to right of items
3080     QTest::newRow("flow top to bottom, layout right to left") << QQuickGridView::TopToBottom << Qt::RightToLeft
3081         << QPointF(0, 0)
3082         << QPointF(0, 0)
3083         << QPointF(-(240 - 100), 0)
3084         << QPointF(-(240 - 50), 0)
3085         << QPointF(-80, 0)
3086         << QPointF(-(240 - 40), 0);
3087 }
3088
3089 void tst_QQuickGridView::resizeViewAndRepaint()
3090 {
3091     QQuickView *canvas = createView();
3092     canvas->show();
3093
3094     QaimModel model;
3095     for (int i = 0; i < 40; i++)
3096         model.addItem("Item" + QString::number(i), "");
3097
3098     QQmlContext *ctxt = canvas->rootContext();
3099     ctxt->setContextProperty("testModel", &model);
3100     ctxt->setContextProperty("initialWidth", 240);
3101     ctxt->setContextProperty("initialHeight", 100);
3102
3103     canvas->setSource(testFileUrl("resizeview.qml"));
3104     canvas->show();
3105     qApp->processEvents();
3106
3107     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3108     QTRY_VERIFY(gridview != 0);
3109     QQuickItem *contentItem = gridview->contentItem();
3110     QTRY_VERIFY(contentItem != 0);
3111     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3112
3113     // item at index 10 should not be currently visible
3114     QVERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 10));
3115
3116     gridview->setHeight(320);
3117     QTRY_VERIFY(findItem<QQuickItem>(contentItem, "wrapper", 10));
3118
3119     gridview->setHeight(100);
3120     QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 10));
3121
3122     // Ensure we handle -ve sizes
3123     gridview->setHeight(-100);
3124     QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper", false).count(), 3);
3125
3126     gridview->setCacheBuffer(120);
3127     QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper", false).count(), 9);
3128
3129     // ensure items in cache become visible
3130     gridview->setHeight(120);
3131     QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper", false).count(), 15);
3132
3133     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3134     for (int i = 0; i < model.count() && i < itemCount; ++i) {
3135         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3136         if (!item) qWarning() << "Item" << i << "not found";
3137         QTRY_VERIFY(item);
3138         QTRY_COMPARE(item->x(), qreal((i%3)*80));
3139         QTRY_COMPARE(item->y(), qreal((i/3)*60));
3140         QCOMPARE(item->isVisible(), i < 9); // inside view visible, outside not visible
3141     }
3142
3143     // ensure items outside view become invisible
3144     gridview->setHeight(60);
3145     QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper", false).count(), 12);
3146
3147     itemCount = findItems<QQuickItem>(contentItem, "wrapper", false).count();
3148     for (int i = 0; i < model.count() && i < itemCount; ++i) {
3149         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3150         if (!item) qWarning() << "Item" << i << "not found";
3151         QTRY_VERIFY(item);
3152         QTRY_COMPARE(item->x(), qreal((i%3)*80));
3153         QTRY_COMPARE(item->y(), qreal((i/3)*60));
3154         QCOMPARE(item->isVisible(), i < 6); // inside view visible, outside not visible
3155     }
3156
3157     delete canvas;
3158 }
3159
3160 void tst_QQuickGridView::changeColumnCount()
3161 {
3162     QmlListModel model;
3163     for (int i = 0; i < 40; i++)
3164         model.addItem("Item" + QString::number(i), "");
3165
3166     QQuickView *canvas = createView();
3167     QQmlContext *ctxt = canvas->rootContext();
3168     ctxt->setContextProperty("testModel", &model);
3169     ctxt->setContextProperty("initialWidth", 100);
3170     ctxt->setContextProperty("initialHeight", 320);
3171     canvas->setSource(testFileUrl("resizeview.qml"));
3172     canvas->show();
3173     qApp->processEvents();
3174
3175     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3176     QTRY_VERIFY(gridview != 0);
3177     QQuickItem *contentItem = gridview->contentItem();
3178     QTRY_VERIFY(contentItem != 0);
3179     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3180
3181     // a single column of 6 items are visible
3182     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3183     QCOMPARE(itemCount, 6);
3184     for (int i = 0; i < model.count() && i < itemCount; ++i) {
3185         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3186         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
3187         QCOMPARE(item->x(), 0.0);
3188         QCOMPARE(item->y(), qreal(i*60));
3189     }
3190
3191     // now 6x3 grid is visible, plus 1 extra below for refill
3192     gridview->setWidth(240);
3193     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3194     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3195     QCOMPARE(itemCount, 6*3 + 1);
3196     for (int i = 0; i < model.count() && i < itemCount; ++i) {
3197         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3198         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
3199         QCOMPARE(item->x(), qreal((i%3)*80));
3200         QCOMPARE(item->y(), qreal((i/3)*60));
3201     }
3202
3203     // back to single column
3204     gridview->setWidth(100);
3205     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3206     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3207     QCOMPARE(itemCount, 6);
3208     for (int i = 0; i < model.count() && i < itemCount; ++i) {
3209         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3210         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
3211         QCOMPARE(item->x(), 0.0);
3212         QCOMPARE(item->y(), qreal(i*60));
3213     }
3214
3215     delete canvas;
3216 }
3217
3218 void tst_QQuickGridView::indexAt_itemAt_data()
3219 {
3220     QTest::addColumn<qreal>("x");
3221     QTest::addColumn<qreal>("y");
3222     QTest::addColumn<int>("index");
3223
3224     QTest::newRow("Item 0 - 0, 0") << 0. << 0. << 0;
3225     QTest::newRow("Item 0 - 79, 59") << 79. << 59. << 0;
3226     QTest::newRow("Item 1 - 80, 0") << 80. << 0. << 1;
3227     QTest::newRow("Item 3 - 0, 60") << 0. << 60. << 3;
3228     QTest::newRow("No Item - 240, 0") << 240. << 0. << -1;
3229 }
3230
3231 void tst_QQuickGridView::indexAt_itemAt()
3232 {
3233     QFETCH(qreal, x);
3234     QFETCH(qreal, y);
3235     QFETCH(int, index);
3236
3237     QQuickView *canvas = createView();
3238
3239     QaimModel model;
3240     model.addItem("Fred", "12345");
3241     model.addItem("John", "2345");
3242     model.addItem("Bob", "54321");
3243     model.addItem("Billy", "22345");
3244     model.addItem("Sam", "2945");
3245     model.addItem("Ben", "04321");
3246     model.addItem("Jim", "0780");
3247
3248     QQmlContext *ctxt = canvas->rootContext();
3249     ctxt->setContextProperty("testModel", &model);
3250     ctxt->setContextProperty("testRightToLeft", QVariant(false));
3251     ctxt->setContextProperty("testTopToBottom", QVariant(false));
3252
3253     canvas->setSource(testFileUrl("gridview1.qml"));
3254     qApp->processEvents();
3255
3256     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3257     QTRY_VERIFY(gridview != 0);
3258
3259     QQuickItem *contentItem = gridview->contentItem();
3260     QTRY_VERIFY(contentItem != 0);
3261
3262     QTRY_COMPARE(gridview->count(), model.count());
3263
3264     QQuickItem *item = 0;
3265     if (index >= 0) {
3266         item = findItem<QQuickItem>(contentItem, "wrapper", index);
3267         QVERIFY(item);
3268     }
3269     QCOMPARE(gridview->indexAt(x, y), index);
3270     QVERIFY(gridview->itemAt(x, y) == item);
3271
3272     delete canvas;
3273 }
3274
3275 void tst_QQuickGridView::onAdd()
3276 {
3277     QFETCH(int, initialItemCount);
3278     QFETCH(int, itemsToAdd);
3279
3280     const int delegateWidth = 50;
3281     const int delegateHeight = 100;
3282     QaimModel model;
3283     QQuickView *canvas = createView();
3284     canvas->setGeometry(0,0,5 * delegateWidth, 5 * delegateHeight); // just ensure all items fit
3285
3286     // these initial items should not trigger GridView.onAdd
3287     for (int i=0; i<initialItemCount; i++)
3288         model.addItem("dummy value", "dummy value");
3289
3290     QQmlContext *ctxt = canvas->rootContext();
3291     ctxt->setContextProperty("testModel", &model);
3292     ctxt->setContextProperty("delegateWidth", delegateWidth);
3293     ctxt->setContextProperty("delegateHeight", delegateHeight);
3294     canvas->setSource(testFileUrl("attachedSignals.qml"));
3295
3296     QObject *object = canvas->rootObject();
3297     object->setProperty("width", canvas->width());
3298     object->setProperty("height", canvas->height());
3299     qApp->processEvents();
3300
3301     QList<QPair<QString, QString> > items;
3302     for (int i=0; i<itemsToAdd; i++)
3303         items << qMakePair(QString("value %1").arg(i), QString::number(i));
3304     model.addItems(items);
3305
3306     QTRY_COMPARE(model.count(), qobject_cast<QQuickGridView*>(canvas->rootObject())->count());
3307     qApp->processEvents();
3308
3309     QVariantList result = object->property("addedDelegates").toList();
3310     QTRY_COMPARE(result.count(), items.count());
3311     for (int i=0; i<items.count(); i++)
3312         QCOMPARE(result[i].toString(), items[i].first);
3313
3314     delete canvas;
3315 }
3316
3317 void tst_QQuickGridView::onAdd_data()
3318 {
3319     QTest::addColumn<int>("initialItemCount");
3320     QTest::addColumn<int>("itemsToAdd");
3321
3322     QTest::newRow("0, add 1") << 0 << 1;
3323     QTest::newRow("0, add 2") << 0 << 2;
3324     QTest::newRow("0, add 10") << 0 << 10;
3325
3326     QTest::newRow("1, add 1") << 1 << 1;
3327     QTest::newRow("1, add 2") << 1 << 2;
3328     QTest::newRow("1, add 10") << 1 << 10;
3329
3330     QTest::newRow("5, add 1") << 5 << 1;
3331     QTest::newRow("5, add 2") << 5 << 2;
3332     QTest::newRow("5, add 10") << 5 << 10;
3333 }
3334
3335 void tst_QQuickGridView::onRemove()
3336 {
3337     QFETCH(int, initialItemCount);
3338     QFETCH(int, indexToRemove);
3339     QFETCH(int, removeCount);
3340
3341     const int delegateWidth = 50;
3342     const int delegateHeight = 100;
3343     QaimModel model;
3344     for (int i=0; i<initialItemCount; i++)
3345         model.addItem(QString("value %1").arg(i), "dummy value");
3346
3347     QQuickView *canvas = createView();
3348     QQmlContext *ctxt = canvas->rootContext();
3349     ctxt->setContextProperty("testModel", &model);
3350     ctxt->setContextProperty("delegateWidth", delegateWidth);
3351     ctxt->setContextProperty("delegateHeight", delegateHeight);
3352     canvas->setSource(testFileUrl("attachedSignals.qml"));
3353     QObject *object = canvas->rootObject();
3354
3355     model.removeItems(indexToRemove, removeCount);
3356     QTRY_COMPARE(model.count(), qobject_cast<QQuickGridView*>(canvas->rootObject())->count());
3357     QCOMPARE(object->property("removedDelegateCount"), QVariant(removeCount));
3358
3359     delete canvas;
3360 }
3361
3362 void tst_QQuickGridView::onRemove_data()
3363 {
3364     QTest::addColumn<int>("initialItemCount");
3365     QTest::addColumn<int>("indexToRemove");
3366     QTest::addColumn<int>("removeCount");
3367
3368     QTest::newRow("remove first") << 1 << 0 << 1;
3369     QTest::newRow("two items, remove first") << 2 << 0 << 1;
3370     QTest::newRow("two items, remove last") << 2 << 1 << 1;
3371     QTest::newRow("two items, remove all") << 2 << 0 << 2;
3372
3373     QTest::newRow("four items, remove first") << 4 << 0 << 1;
3374     QTest::newRow("four items, remove 0-2") << 4 << 0 << 2;
3375     QTest::newRow("four items, remove 1-3") << 4 << 1 << 2;
3376     QTest::newRow("four items, remove 2-4") << 4 << 2 << 2;
3377     QTest::newRow("four items, remove last") << 4 << 3 << 1;
3378     QTest::newRow("four items, remove all") << 4 << 0 << 4;
3379
3380     QTest::newRow("ten items, remove 1-8") << 10 << 0 << 8;
3381     QTest::newRow("ten items, remove 2-7") << 10 << 2 << 5;
3382     QTest::newRow("ten items, remove 4-10") << 10 << 4 << 6;
3383 }
3384
3385 void tst_QQuickGridView::columnCount()
3386 {
3387     QQuickView canvas;
3388     canvas.setSource(testFileUrl("gridview4.qml"));
3389     canvas.show();
3390     canvas.requestActivateWindow();
3391     QTest::qWaitForWindowShown(&canvas);
3392
3393     QQuickGridView *view = qobject_cast<QQuickGridView*>(canvas.rootObject());
3394
3395     QCOMPARE(view->cellWidth(), qreal(405)/qreal(9));
3396     QCOMPARE(view->cellHeight(), qreal(100));
3397
3398     QList<QQuickItem*> items = findItems<QQuickItem>(view, "delegate");
3399     QCOMPARE(items.size(), 18);
3400     QCOMPARE(items.at(8)->y(), qreal(0));
3401     QCOMPARE(items.at(9)->y(), qreal(100));
3402 }
3403
3404 void tst_QQuickGridView::margins()
3405 {
3406     {
3407         QQuickView *canvas = createView();
3408
3409         QaimModel model;
3410         for (int i = 0; i < 40; i++)
3411             model.addItem("Item" + QString::number(i), "");
3412
3413         QQmlContext *ctxt = canvas->rootContext();
3414         ctxt->setContextProperty("testModel", &model);
3415         ctxt->setContextProperty("testRightToLeft", QVariant(false));
3416
3417         canvas->setSource(testFileUrl("margins.qml"));
3418         canvas->show();
3419         qApp->processEvents();
3420
3421         QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3422         QTRY_VERIFY(gridview != 0);
3423         QQuickItem *contentItem = gridview->contentItem();
3424         QTRY_VERIFY(contentItem != 0);
3425         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3426
3427         QCOMPARE(gridview->contentX(), -30.);
3428         QCOMPARE(gridview->xOrigin(), 0.);
3429
3430         // check end bound
3431         gridview->positionViewAtEnd();
3432         qreal pos = gridview->contentX();
3433         gridview->setContentX(pos + 80);
3434         gridview->returnToBounds();
3435         QTRY_COMPARE(gridview->contentX(), pos + 50);
3436
3437         // remove item before visible and check that left margin is maintained
3438         // and xOrigin is updated
3439         gridview->setContentX(200);
3440         model.removeItems(0, 4);
3441         QTest::qWait(100);
3442         gridview->setContentX(-50);
3443         gridview->returnToBounds();
3444         QCOMPARE(gridview->xOrigin(), 100.);
3445         QTRY_COMPARE(gridview->contentX(), 70.);
3446
3447         // reduce left margin
3448         gridview->setLeftMargin(20);
3449         QCOMPARE(gridview->xOrigin(), 100.);
3450         QTRY_COMPARE(gridview->contentX(), 80.);
3451
3452         // check end bound
3453         gridview->positionViewAtEnd();
3454         QCOMPARE(gridview->xOrigin(), 0.); // positionViewAtEnd() resets origin
3455         pos = gridview->contentX();
3456         gridview->setContentX(pos + 80);
3457         gridview->returnToBounds();
3458         QTRY_COMPARE(gridview->contentX(), pos + 50);
3459
3460         // reduce right margin
3461         pos = gridview->contentX();
3462         gridview->setRightMargin(40);
3463         QCOMPARE(gridview->xOrigin(), 0.);
3464         QTRY_COMPARE(gridview->contentX(), pos-10);
3465
3466         delete canvas;
3467     }
3468     {
3469         //RTL
3470         QQuickView *canvas = createView();
3471         canvas->show();
3472
3473         QaimModel model;
3474         for (int i = 0; i < 40; i++)
3475             model.addItem("Item" + QString::number(i), "");
3476
3477         QQmlContext *ctxt = canvas->rootContext();
3478         ctxt->setContextProperty("testModel", &model);
3479         ctxt->setContextProperty("testRightToLeft", QVariant(true));
3480
3481         canvas->setSource(testFileUrl("margins.qml"));
3482         qApp->processEvents();
3483
3484         QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3485         QTRY_VERIFY(gridview != 0);
3486
3487         QQuickItem *contentItem = gridview->contentItem();
3488         QTRY_VERIFY(contentItem != 0);
3489
3490         QCOMPARE(gridview->contentX(), -240+30.);
3491         QCOMPARE(gridview->xOrigin(), 0.);
3492
3493         // check end bound
3494         gridview->positionViewAtEnd();
3495         qreal pos = gridview->contentX();
3496         gridview->setContentX(pos - 80);
3497         gridview->returnToBounds();
3498         QTRY_COMPARE(gridview->contentX(), pos - 50);
3499
3500         // remove item before visible and check that left margin is maintained
3501         // and xOrigin is updated
3502         gridview->setContentX(-400);
3503         model.removeItems(0, 4);
3504         QTest::qWait(100);
3505         gridview->setContentX(-240+50);
3506         gridview->returnToBounds();
3507         QCOMPARE(gridview->xOrigin(), -100.);
3508         QTRY_COMPARE(gridview->contentX(), -240-70.);
3509
3510         // reduce left margin (i.e. right side due to RTL)
3511         pos = gridview->contentX();
3512         gridview->setLeftMargin(20);
3513         QCOMPARE(gridview->xOrigin(), -100.);
3514         QTRY_COMPARE(gridview->contentX(), -240-80.);
3515
3516         // check end bound
3517         gridview->positionViewAtEnd();
3518         QCOMPARE(gridview->xOrigin(), 0.); // positionViewAtEnd() resets origin
3519         pos = gridview->contentX();
3520         gridview->setContentX(pos - 80);
3521         gridview->returnToBounds();
3522         QTRY_COMPARE(gridview->contentX(), pos - 50);
3523
3524         // reduce right margin (i.e. left side due to RTL)
3525         pos = gridview->contentX();
3526         gridview->setRightMargin(40);
3527         QCOMPARE(gridview->xOrigin(), 0.);
3528         QTRY_COMPARE(gridview->contentX(), pos+10);
3529
3530         delete canvas;
3531     }
3532 }
3533
3534 void tst_QQuickGridView::creationContext()
3535 {
3536     QQuickView canvas;
3537     canvas.setGeometry(0,0,240,320);
3538     canvas.setSource(testFileUrl("creationContext.qml"));
3539     qApp->processEvents();
3540
3541     QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
3542     QVERIFY(rootItem);
3543     QVERIFY(rootItem->property("count").toInt() > 0);
3544
3545     QQuickItem *item;
3546     QVERIFY(item = rootItem->findChild<QQuickItem *>("listItem"));
3547     QCOMPARE(item->property("text").toString(), QString("Hello!"));
3548     QVERIFY(item = rootItem->findChild<QQuickItem *>("header"));
3549     QCOMPARE(item->property("text").toString(), QString("Hello!"));
3550     QVERIFY(item = rootItem->findChild<QQuickItem *>("footer"));
3551     QCOMPARE(item->property("text").toString(), QString("Hello!"));
3552 }
3553
3554 void tst_QQuickGridView::snapToRow_data()
3555 {
3556     QTest::addColumn<QQuickGridView::Flow>("flow");
3557     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3558     QTest::addColumn<int>("highlightRangeMode");
3559     QTest::addColumn<QPoint>("flickStart");
3560     QTest::addColumn<QPoint>("flickEnd");
3561     QTest::addColumn<qreal>("snapAlignment");
3562     QTest::addColumn<qreal>("endExtent");
3563     QTest::addColumn<qreal>("startExtent");
3564
3565     QTest::newRow("vertical, left to right") << QQuickGridView::LeftToRight << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
3566         << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 1200.0 << 0.0;
3567
3568     QTest::newRow("horizontal, left to right") << QQuickGridView::TopToBottom << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
3569         << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 1200.0 << 0.0;
3570
3571     QTest::newRow("horizontal, right to left") << QQuickGridView::TopToBottom << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
3572         << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -1200.0 - 240.0 << -240.0;
3573
3574     QTest::newRow("vertical, left to right, enforce range") << QQuickGridView::LeftToRight << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
3575         << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 1340.0 << -20.0;
3576
3577     QTest::newRow("horizontal, left to right, enforce range") << QQuickGridView::TopToBottom << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
3578         << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 1340.0 << -20.0;
3579
3580     QTest::newRow("horizontal, right to left, enforce range") << QQuickGridView::TopToBottom << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
3581         << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -1200.0 - 240.0 - 140.0 << -220.0;
3582 }
3583
3584 void tst_QQuickGridView::snapToRow()
3585 {
3586     QFETCH(QQuickGridView::Flow, flow);
3587     QFETCH(Qt::LayoutDirection, layoutDirection);
3588     QFETCH(int, highlightRangeMode);
3589     QFETCH(QPoint, flickStart);
3590     QFETCH(QPoint, flickEnd);
3591     QFETCH(qreal, snapAlignment);
3592     QFETCH(qreal, endExtent);
3593     QFETCH(qreal, startExtent);
3594
3595     QQuickView *canvas = createView();
3596
3597     canvas->setSource(testFileUrl("snapToRow.qml"));
3598     canvas->show();
3599     qApp->processEvents();
3600
3601     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3602     QTRY_VERIFY(gridview != 0);
3603
3604     gridview->setFlow(flow);
3605     gridview->setLayoutDirection(layoutDirection);
3606     gridview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
3607     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3608
3609     QQuickItem *contentItem = gridview->contentItem();
3610     QTRY_VERIFY(contentItem != 0);
3611
3612     // confirm that a flick hits an item boundary
3613     flick(canvas, flickStart, flickEnd, 180);
3614     QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
3615     if (flow == QQuickGridView::LeftToRight)
3616         QCOMPARE(qreal(fmod(gridview->contentY(),80.0)), snapAlignment);
3617     else
3618         QCOMPARE(qreal(fmod(gridview->contentX(),80.0)), snapAlignment);
3619
3620     // flick to end
3621     do {
3622         flick(canvas, flickStart, flickEnd, 180);
3623         QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
3624     } while (flow == QQuickGridView::LeftToRight
3625            ? !gridview->isAtYEnd()
3626            : layoutDirection == Qt::LeftToRight ? !gridview->isAtXEnd() : !gridview->isAtXBeginning());
3627
3628     if (flow == QQuickGridView::LeftToRight)
3629         QCOMPARE(gridview->contentY(), endExtent);
3630     else
3631         QCOMPARE(gridview->contentX(), endExtent);
3632
3633     // flick to start
3634     do {
3635         flick(canvas, flickEnd, flickStart, 180);
3636         QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
3637     } while (flow == QQuickGridView::LeftToRight
3638            ? !gridview->isAtYBeginning()
3639            : layoutDirection == Qt::LeftToRight ? !gridview->isAtXBeginning() : !gridview->isAtXEnd());
3640
3641     if (flow == QQuickGridView::LeftToRight)
3642         QCOMPARE(gridview->contentY(), startExtent);
3643     else
3644         QCOMPARE(gridview->contentX(), startExtent);
3645
3646     delete canvas;
3647 }
3648
3649 void tst_QQuickGridView::snapOneRow_data()
3650 {
3651     QTest::addColumn<QQuickGridView::Flow>("flow");
3652     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3653     QTest::addColumn<int>("highlightRangeMode");
3654     QTest::addColumn<QPoint>("flickStart");
3655     QTest::addColumn<QPoint>("flickEnd");
3656     QTest::addColumn<qreal>("snapAlignment");
3657     QTest::addColumn<qreal>("endExtent");
3658     QTest::addColumn<qreal>("startExtent");
3659
3660     QTest::newRow("vertical, left to right") << QQuickGridView::LeftToRight << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
3661         << QPoint(20, 200) << QPoint(20, 20) << 100.0 << 360.0 << 0.0;
3662
3663     QTest::newRow("horizontal, left to right") << QQuickGridView::TopToBottom << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
3664         << QPoint(200, 20) << QPoint(20, 20) << 100.0 << 360.0 << 0.0;
3665
3666     QTest::newRow("horizontal, right to left") << QQuickGridView::TopToBottom << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
3667         << QPoint(20, 20) << QPoint(200, 20) << -340.0 << -360.0 - 240.0 << -240.0;
3668
3669     QTest::newRow("vertical, left to right, enforce range") << QQuickGridView::LeftToRight << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
3670         << QPoint(20, 200) << QPoint(20, 20) << 100.0 << 460.0 << -20.0;
3671
3672     QTest::newRow("horizontal, left to right, enforce range") << QQuickGridView::TopToBottom << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
3673         << QPoint(200, 20) << QPoint(20, 20) << 100.0 << 460.0 << -20.0;
3674
3675     QTest::newRow("horizontal, right to left, enforce range") << QQuickGridView::TopToBottom << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
3676         << QPoint(20, 20) << QPoint(200, 20) << -340.0 << -360.0 - 240.0 - 100.0 << -220.0;
3677 }
3678
3679 void tst_QQuickGridView::snapOneRow()
3680 {
3681     QFETCH(QQuickGridView::Flow, flow);
3682     QFETCH(Qt::LayoutDirection, layoutDirection);
3683     QFETCH(int, highlightRangeMode);
3684     QFETCH(QPoint, flickStart);
3685     QFETCH(QPoint, flickEnd);
3686     QFETCH(qreal, snapAlignment);
3687     QFETCH(qreal, endExtent);
3688     QFETCH(qreal, startExtent);
3689
3690     QQuickView *canvas = createView();
3691
3692     canvas->setSource(testFileUrl("snapOneRow.qml"));
3693     canvas->show();
3694     qApp->processEvents();
3695
3696     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3697     QTRY_VERIFY(gridview != 0);
3698
3699     gridview->setFlow(flow);
3700     gridview->setLayoutDirection(layoutDirection);
3701     gridview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
3702     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3703
3704     QQuickItem *contentItem = gridview->contentItem();
3705     QTRY_VERIFY(contentItem != 0);
3706
3707     QSignalSpy currentIndexSpy(gridview, SIGNAL(currentIndexChanged()));
3708
3709     // confirm that a flick hits next row boundary
3710     flick(canvas, flickStart, flickEnd, 180);
3711     QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
3712     if (flow == QQuickGridView::LeftToRight)
3713         QCOMPARE(gridview->contentY(), snapAlignment);
3714     else
3715         QCOMPARE(gridview->contentX(), snapAlignment);
3716
3717     if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
3718         QCOMPARE(gridview->currentIndex(), 2);
3719         QCOMPARE(currentIndexSpy.count(), 1);
3720     }
3721
3722     // flick to end
3723     do {
3724         flick(canvas, flickStart, flickEnd, 180);
3725         QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
3726     } while (flow == QQuickGridView::LeftToRight
3727            ? !gridview->isAtYEnd()
3728            : layoutDirection == Qt::LeftToRight ? !gridview->isAtXEnd() : !gridview->isAtXBeginning());
3729
3730     if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
3731         QCOMPARE(gridview->currentIndex(), 8);
3732         QCOMPARE(currentIndexSpy.count(), 4);
3733     }
3734
3735     if (flow == QQuickGridView::LeftToRight)
3736         QCOMPARE(gridview->contentY(), endExtent);
3737     else
3738         QCOMPARE(gridview->contentX(), endExtent);
3739
3740     // flick to start
3741     do {
3742         flick(canvas, flickEnd, flickStart, 180);
3743         QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
3744     } while (flow == QQuickGridView::LeftToRight
3745            ? !gridview->isAtYBeginning()
3746            : layoutDirection == Qt::LeftToRight ? !gridview->isAtXBeginning() : !gridview->isAtXEnd());
3747
3748     if (flow == QQuickGridView::LeftToRight)
3749         QCOMPARE(gridview->contentY(), startExtent);
3750     else
3751         QCOMPARE(gridview->contentX(), startExtent);
3752
3753     if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
3754         QCOMPARE(gridview->currentIndex(), 0);
3755         QCOMPARE(currentIndexSpy.count(), 8);
3756     }
3757
3758     delete canvas;
3759 }
3760
3761
3762 void tst_QQuickGridView::unaligned()
3763 {
3764     QQuickView *canvas = createView();
3765     canvas->show();
3766
3767     QaimModel model;
3768     for (int i = 0; i < 10; i++)
3769         model.addItem("Item" + QString::number(i), "");
3770
3771     QQmlContext *ctxt = canvas->rootContext();
3772     ctxt->setContextProperty("testModel", &model);
3773
3774     canvas->setSource(testFileUrl("unaligned.qml"));
3775     qApp->processEvents();
3776
3777     QQuickGridView *gridview = qobject_cast<QQuickGridView*>(canvas->rootObject());
3778     QVERIFY(gridview != 0);
3779
3780     QQuickItem *contentItem = gridview->contentItem();
3781     QVERIFY(contentItem != 0);
3782
3783     for (int i = 0; i < 10; ++i) {
3784         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3785         if (!item) qWarning() << "Item" << i << "not found";
3786         QVERIFY(item);
3787         QCOMPARE(item->x(), qreal((i%9)*gridview->cellWidth()));
3788         QCOMPARE(item->y(), qreal((i/9)*gridview->cellHeight()));
3789     }
3790
3791     // appending
3792     for (int i = 10; i < 18; ++i) {
3793         model.addItem("Item" + QString::number(i), "");
3794         QQuickItem *item = 0;
3795         QTRY_VERIFY(item = findItem<QQuickItem>(contentItem, "wrapper", i));
3796         QCOMPARE(item->x(), qreal((i%9)*gridview->cellWidth()));
3797         QCOMPARE(item->y(), qreal((i/9)*gridview->cellHeight()));
3798     }
3799
3800     // inserting
3801     for (int i = 0; i < 10; ++i) {
3802         model.insertItem(i, "Item" + QString::number(i), "");
3803         QQuickItem *item = 0;
3804         QTRY_VERIFY(item = findItem<QQuickItem>(contentItem, "wrapper", i));
3805         QCOMPARE(item->x(), qreal((i%9)*gridview->cellWidth()));
3806         QCOMPARE(item->y(), qreal((i/9)*gridview->cellHeight()));
3807     }
3808
3809     // removing
3810     model.removeItems(7, 10);
3811     QTRY_COMPARE(model.count(), gridview->count());
3812     for (int i = 0; i < 18; ++i) {
3813         QQuickItem *item = 0;
3814         QTRY_VERIFY(item = findItem<QQuickItem>(contentItem, "wrapper", i));
3815         QCOMPARE(item->x(), qreal(i%9)*gridview->cellWidth());
3816         QCOMPARE(item->y(), qreal(i/9)*gridview->cellHeight());
3817     }
3818
3819     delete canvas;
3820 }
3821
3822 void tst_QQuickGridView::populateTransitions()
3823 {
3824     QFETCH(bool, staticallyPopulate);
3825     QFETCH(bool, dynamicallyPopulate);
3826     QFETCH(bool, usePopulateTransition);
3827
3828     QPointF transitionFrom(-50, -50);
3829     QPointF transitionVia(100, 100);
3830     QaimModel model_transitionFrom;
3831     QaimModel model_transitionVia;
3832
3833     QaimModel model;
3834     if (staticallyPopulate) {
3835         for (int i = 0; i < 30; i++)
3836             model.addItem("item" + QString::number(i), "");
3837     }
3838
3839     QQuickView *canvas = createView();
3840     canvas->rootContext()->setContextProperty("testModel", &model);
3841     canvas->rootContext()->setContextProperty("usePopulateTransition", usePopulateTransition);
3842     canvas->rootContext()->setContextProperty("dynamicallyPopulate", dynamicallyPopulate);
3843     canvas->rootContext()->setContextProperty("transitionFrom", transitionFrom);
3844     canvas->rootContext()->setContextProperty("transitionVia", transitionVia);
3845     canvas->rootContext()->setContextProperty("model_transitionFrom", &model_transitionFrom);
3846     canvas->rootContext()->setContextProperty("model_transitionVia", &model_transitionVia);
3847     canvas->setSource(testFileUrl("populateTransitions.qml"));
3848     canvas->show();
3849
3850     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3851     QVERIFY(gridview);
3852     QQuickItem *contentItem = gridview->contentItem();
3853     QVERIFY(contentItem);
3854
3855     if (staticallyPopulate || dynamicallyPopulate) {
3856         // check the populate transition is run
3857         if (usePopulateTransition) {
3858             QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), 19);
3859         } else {
3860             QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3861             QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), 0);
3862         }
3863         QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 0);
3864     } else {
3865         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3866     }
3867
3868     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3869     if (usePopulateTransition)
3870         QCOMPARE(itemCount, gridview->property("countPopulateTransitions").toInt());
3871     for (int i=0; i < model.count() && i < itemCount; ++i) {
3872         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3873         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
3874         QCOMPARE(item->x(), (i%3)*80.0);
3875         QCOMPARE(item->y(), (i/3)*60.0);
3876         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
3877         QVERIFY(name != 0);
3878         QTRY_COMPARE(name->text(), model.name(i));
3879     }
3880
3881     // add an item and check this is done with add transition, not populate
3882     model.insertItem(0, "another item", "");
3883     QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 1);
3884     QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(),
3885                  (usePopulateTransition && (staticallyPopulate || dynamicallyPopulate)) ? 19 : 0);
3886
3887     // clear the model
3888     canvas->rootContext()->setContextProperty("testModel", QVariant());
3889     QTRY_COMPARE(gridview->count(), 0);
3890     QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper").count(), 0);
3891     gridview->setProperty("countPopulateTransitions", 0);
3892     gridview->setProperty("countAddTransitions", 0);
3893
3894     // set to a valid model and check populate transition is run a second time
3895     model.clear();
3896     for (int i = 0; i < 30; i++)
3897         model.addItem("item" + QString::number(i), "");
3898     canvas->rootContext()->setContextProperty("testModel", &model);
3899     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3900
3901     QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), usePopulateTransition ? 19 : 0);
3902     QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 0);
3903
3904     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3905     if (usePopulateTransition)
3906         QCOMPARE(itemCount, gridview->property("countPopulateTransitions").toInt());
3907     for (int i=0; i < model.count() && i < itemCount; ++i) {
3908         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3909         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
3910         QCOMPARE(item->x(), (i%3)*80.0);
3911         QCOMPARE(item->y(), (i/3)*60.0);
3912         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
3913         QVERIFY(name != 0);
3914         QTRY_COMPARE(name->text(), model.name(i));
3915     }
3916
3917     // reset model and check populate transition is run again
3918     gridview->setProperty("countPopulateTransitions", 0);
3919     gridview->setProperty("countAddTransitions", 0);
3920     model.reset();
3921     QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), usePopulateTransition ? 19 : 0);
3922     QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 0);
3923
3924     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3925     if (usePopulateTransition)
3926         QCOMPARE(itemCount, gridview->property("countPopulateTransitions").toInt());
3927     for (int i=0; i < model.count() && i < itemCount; ++i) {
3928         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3929         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
3930         QCOMPARE(item->x(), (i%3)*80.0);
3931         QCOMPARE(item->y(), (i/3)*60.0);
3932         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
3933         QVERIFY(name != 0);
3934         QTRY_COMPARE(name->text(), model.name(i));
3935     }
3936
3937     delete canvas;
3938 }
3939
3940 void tst_QQuickGridView::populateTransitions_data()
3941 {
3942     QTest::addColumn<bool>("staticallyPopulate");
3943     QTest::addColumn<bool>("dynamicallyPopulate");
3944     QTest::addColumn<bool>("usePopulateTransition");
3945
3946     QTest::newRow("static") << true << false << true;
3947     QTest::newRow("static, no populate") << true << false << false;
3948
3949     QTest::newRow("dynamic") << false << true << true;
3950     QTest::newRow("dynamic, no populate") << false << true << false;
3951
3952     QTest::newRow("empty to start with") << false << false << true;
3953     QTest::newRow("empty to start with, no populate") << false << false << false;
3954 }
3955
3956 void tst_QQuickGridView::addTransitions()
3957 {
3958     QFETCH(int, initialItemCount);
3959     QFETCH(bool, shouldAnimateTargets);
3960     QFETCH(qreal, contentY);
3961     QFETCH(int, insertionIndex);
3962     QFETCH(int, insertionCount);
3963     QFETCH(ListRange, expectedDisplacedIndexes);
3964
3965     // added items should start here
3966     QPointF targetItems_transitionFrom(-50, -50);
3967
3968     // displaced items should pass through this point
3969     QPointF displacedItems_transitionVia(100, 100);
3970
3971     QaimModel model;
3972     for (int i = 0; i < initialItemCount; i++)
3973         model.addItem("Original item" + QString::number(i), "");
3974     QaimModel model_targetItems_transitionFrom;
3975     QaimModel model_displacedItems_transitionVia;
3976
3977     QQuickView *canvas = createView();
3978     QQmlContext *ctxt = canvas->rootContext();
3979     ctxt->setContextProperty("testModel", &model);
3980     ctxt->setContextProperty("model_targetItems_transitionFrom", &model_targetItems_transitionFrom);
3981     ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
3982     ctxt->setContextProperty("targetItems_transitionFrom", targetItems_transitionFrom);
3983     ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
3984     canvas->setSource(testFileUrl("addTransitions.qml"));
3985     canvas->show();
3986
3987     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3988     QTRY_VERIFY(gridview != 0);
3989     QQuickItem *contentItem = gridview->contentItem();
3990     QVERIFY(contentItem != 0);
3991     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3992
3993     if (contentY != 0) {
3994         gridview->setContentY(contentY);
3995         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3996     }
3997
3998     QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
3999
4000     // only target items that will become visible should be animated
4001     QList<QPair<QString, QString> > newData;
4002     QList<QPair<QString, QString> > expectedTargetData;
4003     QList<int> targetIndexes;
4004     if (shouldAnimateTargets) {
4005         for (int i=insertionIndex; i<insertionIndex+insertionCount; i++) {
4006             newData << qMakePair(QString("New item %1").arg(i), QString(""));
4007
4008             // last visible item is the first item of the row beneath the view
4009             if (i >= (contentY / 60)*3 && i < qCeil((contentY + gridview->height()) / 60.0)*3) {
4010                 expectedTargetData << newData.last();
4011                 targetIndexes << i;
4012             }
4013         }
4014         QVERIFY(expectedTargetData.count() > 0);
4015     }
4016
4017     // start animation
4018     if (!newData.isEmpty()) {
4019         model.insertItems(insertionIndex, newData);
4020         QTRY_COMPARE(model.count(), gridview->count());
4021     }
4022
4023     QList<QQuickItem *> targetItems = findItems<QQuickItem>(contentItem, "wrapper", targetIndexes);
4024
4025     if (shouldAnimateTargets) {
4026         QTRY_COMPARE(gridview->property("targetTransitionsDone").toInt(), expectedTargetData.count());
4027         QTRY_COMPARE(gridview->property("displaceTransitionsDone").toInt(),
4028                      expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0);
4029
4030         // check the target and displaced items were animated
4031         model_targetItems_transitionFrom.matchAgainst(expectedTargetData, "wasn't animated from target 'from' pos", "shouldn't have been animated from target 'from' pos");
4032         model_displacedItems_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with displaced anim", "shouldn't have been animated with displaced anim");
4033
4034         // check attached properties
4035         matchItemsAndIndexes(gridview->property("targetTrans_items").toMap(), model, targetIndexes);
4036         matchIndexLists(gridview->property("targetTrans_targetIndexes").toList(), targetIndexes);
4037         matchItemLists(gridview->property("targetTrans_targetItems").toList(), targetItems);
4038         if (expectedDisplacedIndexes.isValid()) {
4039             // adjust expectedDisplacedIndexes to their final values after the move
4040             QList<int> displacedIndexes = adjustIndexesForAddDisplaced(expectedDisplacedIndexes.indexes, insertionIndex, insertionCount);
4041             matchItemsAndIndexes(gridview->property("displacedTrans_items").toMap(), model, displacedIndexes);
4042             matchIndexLists(gridview->property("displacedTrans_targetIndexes").toList(), targetIndexes);
4043             matchItemLists(gridview->property("displacedTrans_targetItems").toList(), targetItems);
4044         }
4045     } else {
4046         QTRY_COMPARE(model_targetItems_transitionFrom.count(), 0);
4047         QTRY_COMPARE(model_displacedItems_transitionVia.count(), 0);
4048     }
4049
4050     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
4051     int firstVisibleIndex = -1;
4052     for (int i=0; i<items.count(); i++) {
4053         if (items[i]->y() >= contentY) {
4054             QQmlExpression e(qmlContext(items[i]), items[i], "index");
4055             firstVisibleIndex = e.evaluate().toInt();
4056             break;
4057         }
4058     }
4059     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
4060
4061     // verify all items moved to the correct final positions
4062     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
4063     for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
4064         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
4065         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
4066         QCOMPARE(item->x(), (i%3)*80.0);
4067         QCOMPARE(item->y(), (i/3)*60.0);
4068         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
4069         QVERIFY(name != 0);
4070         QCOMPARE(name->text(), model.name(i));
4071     }
4072
4073     delete canvas;
4074 }
4075
4076 void tst_QQuickGridView::addTransitions_data()
4077 {
4078     QTest::addColumn<int>("initialItemCount");
4079     QTest::addColumn<qreal>("contentY");
4080     QTest::addColumn<bool>("shouldAnimateTargets");
4081     QTest::addColumn<int>("insertionIndex");
4082     QTest::addColumn<int>("insertionCount");
4083     QTest::addColumn<ListRange>("expectedDisplacedIndexes");
4084
4085     // if inserting a full row before visible index, items don't appear or animate in, even if there are > 1 new items
4086     QTest::newRow("insert 1, just before start")
4087             << 30 << 20.0 << false
4088             << 0 << 1 << ListRange();
4089     QTest::newRow("insert 1, way before start")
4090             << 30 << 20.0 << false
4091             << 0 << 1 << ListRange();
4092     QTest::newRow("insert multiple, just before start")
4093             << 30 << 100.0 << false
4094             << 0 << 3 << ListRange();
4095     QTest::newRow("insert multiple (< 1 row), just before start")
4096             << 30 << 100.0 << false
4097             << 0 << 2 << ListRange();
4098     QTest::newRow("insert multiple, way before start")
4099             << 30 << 100.0 << false
4100             << 0 << 3 << ListRange();
4101
4102     QTest::newRow("insert 1 at start")
4103             << 30 << 0.0 << true
4104             << 0 << 1 << ListRange(0, 17);
4105     QTest::newRow("insert multiple at start")
4106             << 30 << 0.0 << true
4107             << 0 << 3 << ListRange(0, 17);
4108     QTest::newRow("insert multiple (> 1 row) at start")
4109             << 30 << 0.0 << true
4110             << 0 << 5 << ListRange(0, 17);
4111     QTest::newRow("insert 1 at start, content y not 0")
4112             << 30 << 60.0 << true  // first visible is index 3
4113             << 3 << 1 << ListRange(0 + 3, 17 + 3);
4114     QTest::newRow("insert multiple at start, content y not 0")
4115             << 30 << 60.0 << true    // first visible is index 3
4116             << 3 << 3 << ListRange(0 + 3, 17 + 3);
4117     QTest::newRow("insert multiple (> 1 row) at start, content y not 0")
4118             << 30 << 60.0 << true    // first visible is index 3
4119             << 3 << 5 << ListRange(0 + 3, 17 + 3);
4120
4121     QTest::newRow("insert 1 at start, to empty grid")
4122             << 0 << 0.0 << true
4123             << 0 << 1 << ListRange();
4124     QTest::newRow("insert multiple at start, to empty grid")
4125             << 0 << 0.0 << true
4126             << 0 << 3 << ListRange();
4127
4128     QTest::newRow("insert 1 at middle")
4129             << 30 << 0.0 << true
4130             << 7 << 1 << ListRange(7, 17);
4131     QTest::newRow("insert multiple at middle")
4132             << 30 << 0.0 << true
4133             << 7 << 3 << ListRange(7, 17);
4134     QTest::newRow("insert multiple (> 1 row) at middle")
4135             << 30 << 0.0 << true
4136             << 7 << 5 << ListRange(7, 17);
4137
4138     QTest::newRow("insert 1 at bottom")
4139             << 30 << 0.0 << true
4140             << 17 << 1 << ListRange(17, 17);
4141     QTest::newRow("insert multiple at bottom")
4142             << 30 << 0.0 << true
4143             << 17 << 3 << ListRange(17, 17);
4144     QTest::newRow("insert 1 at bottom, content y not 0")
4145             << 30 << 20.0 * 3 << true
4146             << 17 + 3 << 1 << ListRange(17 + 3, 17 + 3);
4147     QTest::newRow("insert multiple at bottom, content y not 0")
4148             << 30 << 20.0 * 3 << true
4149             << 17 + 3 << 3 << ListRange(17 + 3, 17 + 3);
4150
4151
4152     // items added after the last visible will not be animated in, since they
4153     // do not appear in the final view
4154     QTest::newRow("insert 1 after end")
4155             << 30 << 0.0 << false
4156             << 18 << 1 << ListRange();
4157     QTest::newRow("insert multiple after end")
4158             << 30 << 0.0 << false
4159             << 18 << 3 << ListRange();
4160 }
4161
4162 void tst_QQuickGridView::moveTransitions()
4163 {
4164     QFETCH(int, initialItemCount);
4165     QFETCH(qreal, contentY);
4166     QFETCH(qreal, itemsOffsetAfterMove);
4167     QFETCH(int, moveFrom);
4168     QFETCH(int, moveTo);
4169     QFETCH(int, moveCount);
4170     QFETCH(ListRange, expectedDisplacedIndexes);
4171
4172     // target and displaced items should pass through these points
4173     QPointF targetItems_transitionVia(-50, 50);
4174     QPointF displacedItems_transitionVia(100, 100);
4175
4176     QaimModel model;
4177     for (int i = 0; i < initialItemCount; i++)
4178         model.addItem("Original item" + QString::number(i), "");
4179     QaimModel model_targetItems_transitionVia;
4180     QaimModel model_displacedItems_transitionVia;
4181
4182     QQuickView *canvas = createView();
4183     QQmlContext *ctxt = canvas->rootContext();
4184     ctxt->setContextProperty("testModel", &model);
4185     ctxt->setContextProperty("model_targetItems_transitionVia", &model_targetItems_transitionVia);
4186     ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
4187     ctxt->setContextProperty("targetItems_transitionVia", targetItems_transitionVia);
4188     ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
4189     canvas->setSource(testFileUrl("moveTransitions.qml"));
4190     canvas->show();
4191
4192     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
4193     QTRY_VERIFY(gridview != 0);
4194     QQuickItem *contentItem = gridview->contentItem();
4195     QVERIFY(contentItem != 0);
4196     QQuickText *name;
4197
4198     if (contentY != 0) {
4199         gridview->setContentY(contentY);
4200         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4201     }
4202
4203     QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
4204
4205     // Items moving to *or* from visible positions should be animated.
4206     // Otherwise, they should not be animated.
4207     QList<QPair<QString, QString> > expectedTargetData;
4208     QList<int> targetIndexes;
4209     for (int i=moveFrom; i<moveFrom+moveCount; i++) {
4210         int toIndex = moveTo + (i - moveFrom);
4211         int firstVisibleIndex = (contentY / 60) * 3;
4212         int lastVisibleIndex = (qCeil((contentY + gridview->height()) / 60.0)*3) - 1;
4213         if ((i >= firstVisibleIndex && i <= lastVisibleIndex)
4214                 || (toIndex >= firstVisibleIndex && toIndex <= lastVisibleIndex)) {
4215             expectedTargetData << qMakePair(model.name(i), model.number(i));
4216             targetIndexes << i;
4217         }
4218     }
4219     // ViewTransition.index provides the indices that items are moving to, not from
4220     targetIndexes = adjustIndexesForMove(targetIndexes, moveFrom, moveTo, moveCount);
4221
4222     // start animation
4223     model.moveItems(moveFrom, moveTo, moveCount);
4224
4225     QTRY_COMPARE(gridview->property("targetTransitionsDone").toInt(), expectedTargetData.count());
4226     QTRY_COMPARE(gridview->property("displaceTransitionsDone").toInt(),
4227                  expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0);
4228
4229     QList<QQuickItem *> targetItems = findItems<QQuickItem>(contentItem, "wrapper", targetIndexes);
4230
4231     // check the target and displaced items were animated
4232     model_targetItems_transitionVia.matchAgainst(expectedTargetData, "wasn't animated from target 'from' pos", "shouldn't have been animated from target 'from' pos");
4233     model_displacedItems_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with displaced anim", "shouldn't have been animated with displaced anim");
4234
4235     // check attached properties
4236     matchItemsAndIndexes(gridview->property("targetTrans_items").toMap(), model, targetIndexes);
4237     matchIndexLists(gridview->property("targetTrans_targetIndexes").toList(), targetIndexes);
4238     matchItemLists(gridview->property("targetTrans_targetItems").toList(), targetItems);
4239     if (expectedDisplacedIndexes.isValid()) {
4240         // adjust expectedDisplacedIndexes to their final values after the move
4241         QList<int> displacedIndexes = adjustIndexesForMove(expectedDisplacedIndexes.indexes, moveFrom, moveTo, moveCount);
4242         matchItemsAndIndexes(gridview->property("displacedTrans_items").toMap(), model, displacedIndexes);
4243         matchIndexLists(gridview->property("displacedTrans_targetIndexes").toList(), targetIndexes);
4244         matchItemLists(gridview->property("displacedTrans_targetItems").toList(), targetItems);
4245     }
4246
4247     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
4248     int firstVisibleIndex = -1;
4249     for (int i=0; i<items.count(); i++) {
4250         if (items[i]->y() >= contentY) {
4251             QQmlExpression e(qmlContext(items[i]), items[i], "index");
4252             firstVisibleIndex = e.evaluate().toInt();
4253             break;
4254         }
4255     }
4256     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
4257
4258     // verify all items moved to the correct final positions
4259     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
4260     for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
4261         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
4262         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
4263         QCOMPARE(item->x(), (i%3)*80.0);
4264         QCOMPARE(item->y(), (i/3)*60.0 + itemsOffsetAfterMove);
4265         name = findItem<QQuickText>(contentItem, "textName", i);
4266         QVERIFY(name != 0);
4267         QTRY_COMPARE(name->text(), model.name(i));
4268     }
4269
4270     delete canvas;
4271 }
4272
4273 void tst_QQuickGridView::moveTransitions_data()
4274 {
4275     QTest::addColumn<int>("initialItemCount");
4276     QTest::addColumn<qreal>("contentY");
4277     QTest::addColumn<qreal>("itemsOffsetAfterMove");
4278     QTest::addColumn<int>("moveFrom");
4279     QTest::addColumn<int>("moveTo");
4280     QTest::addColumn<int>("moveCount");
4281     QTest::addColumn<ListRange>("expectedDisplacedIndexes");
4282
4283     QTest::newRow("move from above view, outside visible items, move 1") << 30 << 120.0 << 0.0
4284             << 1 << 10 << 1 << ListRange(6, 10);
4285     QTest::newRow("move from above view, outside visible items, move 1 (first item)") << 30 << 120.0 << 0.0
4286             << 0 << 10 << 1 << ListRange(6, 10);
4287     QTest::newRow("move from above view, outside visible items, move multiple") << 30 << 120.0 << 60.0
4288             << 1 << 10 << 3 << ListRange(13, 23);
4289     QTest::newRow("move from above view, mix of visible/non-visible") << 30 << 120.0 << 60.0
4290             << 1 << 10 << 6 << (ListRange(7, 15) + ListRange(16, 23));
4291     QTest::newRow("move from above view, mix of visible/non-visible (move first)") << 30 << 120.0 << 120.0
4292             << 0 << 10 << 6 << ListRange(16, 23);
4293
4294     QTest::newRow("move within view, move 1 down") << 30 << 0.0 << 0.0
4295             << 1 << 10 << 1 << ListRange(2, 10);
4296     QTest::newRow("move within view, move 1 down, move first item") << 30 << 0.0 << 0.0
4297             << 0 << 10 << 1 << ListRange(1, 10);
4298     QTest::newRow("move within view, move 1 down, move first item, contentY not 0") << 30 << 120.0 << 0.0
4299             << 0+6 << 10+6 << 1 << ListRange(1+6, 10+6);
4300     QTest::newRow("move within view, move 1 down, to last item") << 30 << 0.0 << 0.0
4301             << 10 << 17 << 1 << ListRange(11, 17);
4302     QTest::newRow("move within view, move first->last") << 30 << 0.0 << 0.0
4303             << 0 << 17 << 1 << ListRange(1, 17);
4304
4305     QTest::newRow("move within view, move multiple down") << 30 << 0.0 << 0.0
4306             << 1 << 10 << 3 << ListRange(4, 12);
4307     QTest::newRow("move within view, move multiple down, move first item") << 30 << 0.0 << 0.0
4308             << 0 << 10 << 3 << ListRange(3, 12);
4309     QTest::newRow("move within view, move multiple down, move first item, contentY not 0") << 30 << 60.0 << 0.0
4310             << 0+3 << 10+3 << 3 << ListRange(3+3, 12+3);
4311     QTest::newRow("move within view, move multiple down, displace last item") << 30 << 0.0 << 0.0
4312             << 5 << 15 << 3 << ListRange(8, 17);
4313     QTest::newRow("move within view, move multiple down, move first->last") << 30 << 0.0 << 0.0
4314             << 0 << 15 << 3 << ListRange(3, 17);
4315
4316     QTest::newRow("move within view, move 1 up") << 30 << 0.0 << 0.0
4317             << 10 << 1 << 1 << ListRange(1, 9);
4318     QTest::newRow("move within view, move 1 up, move to first index") << 30 << 0.0 << 0.0
4319             << 10 << 0 << 1 << ListRange(0, 9);
4320     QTest::newRow("move within view, move 1 up, move to first index, contentY not 0") << 30 << 120.0 << 0.0
4321             << 10+6 << 0+6 << 1 << ListRange(0+6, 9+6);
4322     QTest::newRow("move within view, move 1 up, move to first index, contentY not on item border") << 30 << 80.0 << 0.0
4323             << 10+3 << 0+3 << 1 << ListRange(0+3, 9+3);
4324     QTest::newRow("move within view, move 1 up, move last item") << 30 << 0.0 << 0.0
4325             << 17 << 10 << 1 << ListRange(10, 16);
4326     QTest::newRow("move within view, move 1 up, move last->first") << 30 << 0.0 << 0.0
4327             << 17 << 0 << 1 << ListRange(0, 16);
4328
4329     QTest::newRow("move within view, move multiple up") << 30 << 0.0 << 0.0
4330             << 10 << 1 << 3 << ListRange(1, 9);
4331     QTest::newRow("move within view, move multiple (> 1 row) up") << 30 << 0.0 << 0.0
4332             << 10 << 1 << 5 << ListRange(1, 9);
4333     QTest::newRow("move within view, move multiple up, move to first index") << 30 << 0.0 << 0.0
4334             << 10 << 0 << 3 << ListRange(0, 9);
4335     QTest::newRow("move within view, move multiple up, move to first index, contentY not 0") << 30 << 60.0 << 0.0
4336             << 10+3 << 0+3 << 3 << ListRange(0+3, 9+3);
4337     QTest::newRow("move within view, move multiple up (> 1 row), move to first index, contentY not on border") << 30 << 80.0 << 0.0
4338             << 10+3 << 0+3 << 5 << ListRange(0+3, 9+3);
4339     QTest::newRow("move within view, move multiple up, move last item") << 30 << 0.0 << 0.0
4340             << 15 << 5 << 3 << ListRange(5, 14);
4341     QTest::newRow("move within view, move multiple up, move last->first") << 30 << 0.0 << 0.0
4342             << 15 << 0 << 3 << ListRange(0, 14);
4343
4344     QTest::newRow("move from below view, move 1 up") << 30 << 0.0 << 0.0
4345             << 20 << 5 << 1 << ListRange(5, 17);
4346     QTest::newRow("move from below view, move 1 up, move to top") << 30 << 0.0 << 0.0
4347             << 20 << 0 << 1 << ListRange(0, 17);
4348     QTest::newRow("move from below view, move 1 up, move to top, contentY not 0") << 30 << 60.0 << 0.0
4349             << 25 << 3 << 1 << ListRange(0+3, 17+3);
4350     QTest::newRow("move from below view, move multiple (> 1 row) up") << 30 << 0.0 << 0.0
4351             << 20 << 5 << 5 << ListRange(5, 17);
4352     QTest::newRow("move from below view, move multiple up, move to top") << 30 << 0.0 << 0.0
4353             << 20 << 0 << 3 << ListRange(0, 17);
4354     QTest::newRow("move from below view, move multiple up, move to top, contentY not 0") << 30 << 60.0 << 0.0
4355             << 25 << 3 << 3 << ListRange(0+3, 17+3);
4356
4357     QTest::newRow("move from below view, move 1 up, move to bottom") << 30 << 0.0 << 0.0
4358             << 20 << 17 << 1 << ListRange(17, 17);
4359     QTest::newRow("move from below view, move 1 up, move to bottom, contentY not 0") << 30 << 60.0 << 0.0
4360             << 25 << 17+3 << 1 << ListRange(17+3, 17+3);
4361     QTest::newRow("move from below view, move multiple up, move to to bottom") << 30 << 0.0 << 0.0
4362             << 20 << 17 << 3 << ListRange(17, 17);
4363     QTest::newRow("move from below view, move multiple up, move to bottom, contentY not 0") << 30 << 60.0 << 0.0
4364             << 25 << 17+3 << 3 << ListRange(17+3, 17+3);
4365 }
4366
4367 void tst_QQuickGridView::removeTransitions()
4368 {
4369     QFETCH(int, initialItemCount);
4370     QFETCH(bool, shouldAnimateTargets);
4371     QFETCH(qreal, contentY);
4372     QFETCH(int, removalIndex);
4373     QFETCH(int, removalCount);
4374     QFETCH(ListRange, expectedDisplacedIndexes);
4375
4376     // added items should end here
4377     QPointF targetItems_transitionTo(-50, -50);
4378
4379     // displaced items should pass through this points
4380     QPointF displacedItems_transitionVia(100, 100);
4381
4382     QaimModel model;
4383     for (int i = 0; i < initialItemCount; i++)
4384         model.addItem("Original item" + QString::number(i), "");
4385     QaimModel model_targetItems_transitionTo;
4386     QaimModel model_displacedItems_transitionVia;
4387
4388     QQuickView *canvas = createView();
4389     QQmlContext *ctxt = canvas->rootContext();
4390     ctxt->setContextProperty("testModel", &model);
4391     ctxt->setContextProperty("model_targetItems_transitionTo", &model_targetItems_transitionTo);
4392     ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
4393     ctxt->setContextProperty("targetItems_transitionTo", targetItems_transitionTo);
4394     ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
4395     canvas->setSource(testFileUrl("removeTransitions.qml"));
4396     canvas->show();
4397
4398     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
4399     QTRY_VERIFY(gridview != 0);
4400     QQuickItem *contentItem = gridview->contentItem();
4401     QVERIFY(contentItem != 0);
4402     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4403
4404     if (contentY != 0) {
4405         gridview->setContentY(contentY);
4406         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4407     }
4408
4409     QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
4410
4411     // only target items that are visible should be animated
4412     QList<QPair<QString, QString> > expectedTargetData;
4413     QList<int> targetIndexes;
4414     if (shouldAnimateTargets) {
4415         for (int i=removalIndex; i<removalIndex+removalCount; i++) {
4416             int firstVisibleIndex = (contentY / 60.0)*3;
4417             int lastVisibleIndex = (qCeil((contentY + gridview->height()) / 60.0)*3) - 1;
4418             if (i >= firstVisibleIndex && i <= lastVisibleIndex) {
4419                 expectedTargetData << qMakePair(model.name(i), model.number(i));
4420                 targetIndexes << i;
4421             }
4422         }
4423         QVERIFY(expectedTargetData.count() > 0);
4424     }
4425
4426     // calculate targetItems and expectedTargets before model changes
4427     QList<QQuickItem *> targetItems = findItems<QQuickItem>(contentItem, "wrapper", targetIndexes);
4428     QVariantMap expectedTargets;
4429     for (int i=0; i<targetIndexes.count(); i++)
4430         expectedTargets[model.name(targetIndexes[i])] = targetIndexes[i];
4431
4432     // start animation
4433     model.removeItems(removalIndex, removalCount);
4434     QTRY_COMPARE(model.count(), gridview->count());
4435
4436     if (shouldAnimateTargets || expectedDisplacedIndexes.isValid()) {
4437         QTRY_COMPARE(gridview->property("targetTransitionsDone").toInt(), expectedTargetData.count());
4438         QTRY_COMPARE(gridview->property("displaceTransitionsDone").toInt(),
4439                      expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0);
4440
4441         // check the target and displaced items were animated
4442         model_targetItems_transitionTo.matchAgainst(expectedTargetData, "wasn't animated to target 'to' pos", "shouldn't have been animated to target 'to' pos");
4443         model_displacedItems_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with displaced anim", "shouldn't have been animated with displaced anim");
4444
4445         // check attached properties
4446         QCOMPARE(gridview->property("targetTrans_items").toMap(), expectedTargets);
4447         matchIndexLists(gridview->property("targetTrans_targetIndexes").toList(), targetIndexes);
4448         matchItemLists(gridview->property("targetTrans_targetItems").toList(), targetItems);
4449         if (expectedDisplacedIndexes.isValid()) {
4450             // adjust expectedDisplacedIndexes to their final values after the move
4451             QList<int> displacedIndexes = adjustIndexesForRemoveDisplaced(expectedDisplacedIndexes.indexes, removalIndex, removalCount);
4452             matchItemsAndIndexes(gridview->property("displacedTrans_items").toMap(), model, displacedIndexes);
4453             matchIndexLists(gridview->property("displacedTrans_targetIndexes").toList(), targetIndexes);
4454             matchItemLists(gridview->property("displacedTrans_targetItems").toList(), targetItems);
4455         }
4456     } else {
4457         QTRY_COMPARE(model_targetItems_transitionTo.count(), 0);
4458         QTRY_COMPARE(model_displacedItems_transitionVia.count(), 0);
4459     }
4460
4461     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
4462     int itemCount = items.count();
4463     int firstVisibleIndex = -1;
4464     for (int i=0; i<items.count(); i++) {
4465         QQmlExpression e(qmlContext(items[i]), items[i], "index");
4466         int index = e.evaluate().toInt();
4467         if (firstVisibleIndex < 0 && items[i]->y() >= contentY)
4468             firstVisibleIndex = index;
4469         else if (index < 0)
4470             itemCount--;    // exclude deleted items
4471     }
4472     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
4473
4474     // verify all items moved to the correct final positions
4475     for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
4476         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
4477         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
4478         QCOMPARE(item->x(), (i%3)*80.0);
4479         QCOMPARE(item->y(), contentY + ((i-firstVisibleIndex)/3) * 60.0);
4480         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
4481         QVERIFY(name != 0);
4482         QTRY_COMPARE(name->text(), model.name(i));
4483     }
4484
4485     delete canvas;
4486 }
4487
4488 void tst_QQuickGridView::removeTransitions_data()
4489 {
4490     QTest::addColumn<int>("initialItemCount");
4491     QTest::addColumn<qreal>("contentY");
4492     QTest::addColumn<bool>("shouldAnimateTargets");
4493     QTest::addColumn<int>("removalIndex");
4494     QTest::addColumn<int>("removalCount");
4495     QTest::addColumn<ListRange>("expectedDisplacedIndexes");
4496
4497     // All items that are visible following the remove operation should be animated.
4498     // Remove targets that are outside of the view should not be animated.
4499
4500     // For a GridView, removing any number of items other than a full row before the start
4501     // should displace all items in the view
4502     QTest::newRow("remove 1 before start")
4503             << 30 << 120.0 << false
4504             << 2 << 1 << ListRange(6, 24);    // 6-24 are displaced
4505     QTest::newRow("remove 1 row, before start")
4506             << 30 << 120.0 << false
4507             << 3 << 3 << ListRange();
4508     QTest::newRow("remove between 1-2 rows, before start")
4509             << 30 << 120.0 << false
4510             << 0 << 5 << ListRange(6, 25);
4511     QTest::newRow("remove 2 rows, before start")
4512             << 30 << 120.0 << false
4513             << 0 << 6 << ListRange();
4514     QTest::newRow("remove mix of before and after start")
4515             << 30 << 60.0 << true
4516             << 2 << 3 << ListRange(5, 23);  // 5-23 are displaced into view
4517
4518
4519     QTest::newRow("remove 1 from start")
4520             << 30 << 0.0 << true
4521             << 0 << 1 << ListRange(1, 18);  // 1-18 are displaced into view
4522     QTest::newRow("remove multiple from start")
4523             << 30 << 0.0 << true
4524             << 0 << 3 << ListRange(3, 20);  // 3-18 are displaced into view
4525     QTest::newRow("remove 1 from start, content y not 0")
4526             << 30 << 60.0 << true
4527             << 3 << 1 << ListRange(1 + 3, 18 + 3);
4528     QTest::newRow("remove multiple from start, content y not 0")
4529             << 30 << 60.0 << true
4530             << 3 << 3 << ListRange(3 + 3, 20 + 3);
4531
4532
4533     QTest::newRow("remove 1 from middle")
4534             << 30 << 0.0 << true
4535             << 5 << 1 << ListRange(6, 18);
4536     QTest::newRow("remove multiple from middle")
4537             << 30 << 0.0 << true
4538             << 5 << 3 << ListRange(8, 20);
4539
4540
4541     QTest::newRow("remove 1 from bottom")
4542             << 30 << 0.0 << true
4543             << 17 << 1 << ListRange(18, 18);
4544     QTest::newRow("remove multiple (1 row) from bottom")
4545             << 30 << 0.0 << true
4546             << 15 << 3 << ListRange(18, 20);
4547     QTest::newRow("remove multiple (> 1 row) from bottom")
4548             << 30 << 0.0 << true
4549             << 15 << 5 << ListRange(20, 22);
4550     QTest::newRow("remove 1 from bottom, content y not 0")
4551             << 30 << 60.0 << true
4552             << 17 + 3 << 1 << ListRange(18 + 3, 18 + 3);
4553     QTest::newRow("remove multiple (1 row) from bottom, content y not 0")
4554             << 30 << 60.0 << true
4555             << 15 + 3 << 3 << ListRange(18 + 3, 20 + 3);
4556
4557
4558     QTest::newRow("remove 1 after end")
4559             << 30 << 0.0 << false
4560             << 18 << 1 << ListRange();
4561     QTest::newRow("remove multiple after end")
4562             << 30 << 0.0 << false
4563             << 18 << 3 << ListRange();
4564 }
4565
4566 void tst_QQuickGridView::displacedTransitions()
4567 {
4568     QFETCH(bool, useDisplaced);
4569     QFETCH(bool, displacedEnabled);
4570     QFETCH(bool, useAddDisplaced);
4571     QFETCH(bool, addDisplacedEnabled);
4572     QFETCH(bool, useMoveDisplaced);
4573     QFETCH(bool, moveDisplacedEnabled);
4574     QFETCH(bool, useRemoveDisplaced);
4575     QFETCH(bool, removeDisplacedEnabled);
4576     QFETCH(ListChange, change);
4577     QFETCH(ListRange, expectedDisplacedIndexes);
4578
4579     QaimModel model;
4580     for (int i = 0; i < 30; i++)
4581         model.addItem("Original item" + QString::number(i), "");
4582     QaimModel model_displaced_transitionVia;
4583     QaimModel model_addDisplaced_transitionVia;
4584     QaimModel model_moveDisplaced_transitionVia;
4585     QaimModel model_removeDisplaced_transitionVia;
4586
4587     QPointF displaced_transitionVia(-50, -100);
4588     QPointF addDisplaced_transitionVia(-150, 100);
4589     QPointF moveDisplaced_transitionVia(50, -100);
4590     QPointF removeDisplaced_transitionVia(150, 100);
4591
4592     QQuickView *canvas = createView();
4593     QQmlContext *ctxt = canvas->rootContext();
4594     ctxt->setContextProperty("testModel", &model);
4595     ctxt->setContextProperty("model_displaced_transitionVia", &model_displaced_transitionVia);
4596     ctxt->setContextProperty("model_addDisplaced_transitionVia", &model_addDisplaced_transitionVia);
4597     ctxt->setContextProperty("model_moveDisplaced_transitionVia", &model_moveDisplaced_transitionVia);
4598     ctxt->setContextProperty("model_removeDisplaced_transitionVia", &model_removeDisplaced_transitionVia);
4599     ctxt->setContextProperty("displaced_transitionVia", displaced_transitionVia);
4600     ctxt->setContextProperty("addDisplaced_transitionVia", addDisplaced_transitionVia);
4601     ctxt->setContextProperty("moveDisplaced_transitionVia", moveDisplaced_transitionVia);
4602     ctxt->setContextProperty("removeDisplaced_transitionVia", removeDisplaced_transitionVia);
4603     ctxt->setContextProperty("useDisplaced", useDisplaced);
4604     ctxt->setContextProperty("displacedEnabled", displacedEnabled);
4605     ctxt->setContextProperty("useAddDisplaced", useAddDisplaced);
4606     ctxt->setContextProperty("addDisplacedEnabled", addDisplacedEnabled);
4607     ctxt->setContextProperty("useMoveDisplaced", useMoveDisplaced);
4608     ctxt->setContextProperty("moveDisplacedEnabled", moveDisplacedEnabled);
4609     ctxt->setContextProperty("useRemoveDisplaced", useRemoveDisplaced);
4610     ctxt->setContextProperty("removeDisplacedEnabled", removeDisplacedEnabled);
4611     canvas->setSource(testFileUrl("displacedTransitions.qml"));
4612     canvas->show();
4613     qApp->processEvents();
4614
4615     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
4616     QTRY_VERIFY(gridview != 0);
4617     QQuickItem *contentItem = gridview->contentItem();
4618     QVERIFY(contentItem != 0);
4619     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4620
4621     QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
4622     gridview->setProperty("displaceTransitionsDone", false);
4623
4624     switch (change.type) {
4625         case ListChange::Inserted:
4626         {
4627             QList<QPair<QString, QString> > targetItemData;
4628             for (int i=change.index; i<change.index + change.count; ++i)
4629                 targetItemData << qMakePair(QString("new item %1").arg(i), QString::number(i));
4630             model.insertItems(change.index, targetItemData);
4631             QTRY_COMPARE(model.count(), gridview->count());
4632             break;
4633         }
4634         case ListChange::Removed:
4635             model.removeItems(change.index, change.count);
4636             QTRY_COMPARE(model.count(), gridview->count());
4637             break;
4638         case ListChange::Moved:
4639             model.moveItems(change.index, change.to, change.count);
4640             QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4641             break;
4642         case ListChange::SetCurrent:
4643         case ListChange::SetContentY:
4644             break;
4645     }
4646
4647     QVariantList resultTargetIndexes = gridview->property("displacedTargetIndexes").toList();
4648     QVariantList resultTargetItems = gridview->property("displacedTargetItems").toList();
4649
4650     if ((useDisplaced && displacedEnabled)
4651             || (useAddDisplaced && addDisplacedEnabled)
4652             || (useMoveDisplaced && moveDisplacedEnabled)
4653             || (useRemoveDisplaced && removeDisplacedEnabled)) {
4654         QTRY_VERIFY(gridview->property("displaceTransitionsDone").toBool());
4655
4656         // check the correct number of target items and indexes were received
4657         QCOMPARE(resultTargetIndexes.count(), expectedDisplacedIndexes.count());
4658         for (int i=0; i<resultTargetIndexes.count(); i++)
4659             QCOMPARE(resultTargetIndexes[i].value<QList<int> >().count(), change.count);
4660         QCOMPARE(resultTargetItems.count(), expectedDisplacedIndexes.count());
4661         for (int i=0; i<resultTargetItems.count(); i++)
4662             QCOMPARE(resultTargetItems[i].toList().count(), change.count);
4663     } else {
4664         QCOMPARE(resultTargetIndexes.count(), 0);
4665         QCOMPARE(resultTargetItems.count(), 0);
4666     }
4667
4668     if (change.type == ListChange::Inserted && useAddDisplaced && addDisplacedEnabled)
4669         model_addDisplaced_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with add displaced", "shouldn't have been animated with add displaced");
4670     else
4671         QCOMPARE(model_addDisplaced_transitionVia.count(), 0);
4672     if (change.type == ListChange::Moved && useMoveDisplaced && moveDisplacedEnabled)
4673         model_moveDisplaced_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with move displaced", "shouldn't have been animated with move displaced");
4674     else
4675         QCOMPARE(model_moveDisplaced_transitionVia.count(), 0);
4676     if (change.type == ListChange::Removed && useRemoveDisplaced && removeDisplacedEnabled)
4677         model_removeDisplaced_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with remove displaced", "shouldn't have been animated with remove displaced");
4678     else
4679         QCOMPARE(model_removeDisplaced_transitionVia.count(), 0);
4680
4681     if (useDisplaced && displacedEnabled
4682             && ( (change.type == ListChange::Inserted && (!useAddDisplaced || !addDisplacedEnabled))
4683                  || (change.type == ListChange::Moved && (!useMoveDisplaced || !moveDisplacedEnabled))
4684                  || (change.type == ListChange::Removed && (!useRemoveDisplaced || !removeDisplacedEnabled))) ) {
4685         model_displaced_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with generic displaced", "shouldn't have been animated with generic displaced");
4686     } else {
4687         QCOMPARE(model_displaced_transitionVia.count(), 0);
4688     }
4689
4690     // verify all items moved to the correct final positions
4691     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
4692     for (int i=0; i < model.count() && i < items.count(); ++i) {
4693         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
4694         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
4695         QCOMPARE(item->x(), (i%3)*80.0);
4696         QCOMPARE(item->y(), (i/3)*60.0);
4697         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
4698         QVERIFY(name != 0);
4699         QTRY_COMPARE(name->text(), model.name(i));
4700     }
4701
4702     delete canvas;
4703 }
4704
4705 void tst_QQuickGridView::displacedTransitions_data()
4706 {
4707     QTest::addColumn<bool>("useDisplaced");
4708     QTest::addColumn<bool>("displacedEnabled");
4709     QTest::addColumn<bool>("useAddDisplaced");
4710     QTest::addColumn<bool>("addDisplacedEnabled");
4711     QTest::addColumn<bool>("useMoveDisplaced");
4712     QTest::addColumn<bool>("moveDisplacedEnabled");
4713     QTest::addColumn<bool>("useRemoveDisplaced");
4714     QTest::addColumn<bool>("removeDisplacedEnabled");
4715     QTest::addColumn<ListChange>("change");
4716     QTest::addColumn<ListRange>("expectedDisplacedIndexes");
4717
4718     QTest::newRow("no displaced transitions at all")
4719             << false << false
4720             << false << false
4721             << false << false
4722             << false << false
4723             << ListChange::insert(0, 1) << ListRange(0, 17);
4724
4725     QTest::newRow("just displaced")
4726             << true << true
4727             << false << false
4728             << false << false
4729             << false << false
4730             << ListChange::insert(0, 1) << ListRange(0, 17);
4731
4732     QTest::newRow("just displaced (not enabled)")
4733             << true << false
4734             << false << false
4735             << false << false
4736             << false << false
4737             << ListChange::insert(0, 1) << ListRange(0, 17);
4738
4739     QTest::newRow("displaced + addDisplaced")
4740             << true << true
4741             << true << true
4742             << false << false
4743             << false << false
4744             << ListChange::insert(0, 1) << ListRange(0, 17);
4745
4746     QTest::newRow("displaced + addDisplaced (not enabled)")
4747             << true << true
4748             << true << false
4749             << false << false
4750             << false << false
4751             << ListChange::insert(0, 1) << ListRange(0, 17);
4752
4753     QTest::newRow("displaced + moveDisplaced")
4754             << true << true
4755             << false << false
4756             << true << true
4757             << false << false
4758             << ListChange::move(0, 10, 1) << ListRange(1, 10);
4759
4760     QTest::newRow("displaced + moveDisplaced (not enabled)")
4761             << true << true
4762             << false << false
4763             << true << false
4764             << false << false
4765             << ListChange::move(0, 10, 1) << ListRange(1, 10);
4766
4767     QTest::newRow("displaced + removeDisplaced")
4768             << true << true
4769             << false << false
4770             << false << false
4771             << true << true
4772             << ListChange::remove(0, 1) << ListRange(1, 18);
4773
4774     QTest::newRow("displaced + removeDisplaced (not enabled)")
4775             << true << true
4776             << false << false
4777             << false << false
4778             << true << false
4779             << ListChange::remove(0, 1) << ListRange(1, 18);
4780
4781
4782     QTest::newRow("displaced + add, should use generic displaced for a remove")
4783             << true << true
4784             << true << true
4785             << false << false
4786             << true << false
4787             << ListChange::remove(0, 1) << ListRange(1, 18);
4788 }
4789
4790 void tst_QQuickGridView::multipleTransitions()
4791 {
4792     // Tests that if you interrupt a transition in progress with another action that
4793     // cancels the previous transition, the resulting items are still placed correctly.
4794
4795     QFETCH(int, initialCount);
4796     QFETCH(qreal, contentY);
4797     QFETCH(QList<ListChange>, changes);
4798     QFETCH(bool, enableAddTransitions);
4799     QFETCH(bool, enableMoveTransitions);
4800     QFETCH(bool, enableRemoveTransitions);
4801     QFETCH(bool, rippleAddDisplaced);
4802
4803     // add transitions on the left, moves on the right
4804     QPointF addTargets_transitionFrom(-50, -50);
4805     QPointF addDisplaced_transitionFrom(-50, 50);
4806     QPointF moveTargets_transitionFrom(50, -50);
4807     QPointF moveDisplaced_transitionFrom(50, 50);
4808     QPointF removeTargets_transitionTo(-100, 300);
4809     QPointF removeDisplaced_transitionFrom(100, 300);
4810
4811     QmlListModel model;
4812     for (int i = 0; i < initialCount; i++)
4813         model.addItem("Original item" + QString::number(i), "");
4814
4815     QQuickView *canvas = createView();
4816     QQmlContext *ctxt = canvas->rootContext();
4817     ctxt->setContextProperty("testModel", &model);
4818     ctxt->setContextProperty("addTargets_transitionFrom", addTargets_transitionFrom);
4819     ctxt->setContextProperty("addDisplaced_transitionFrom", addDisplaced_transitionFrom);
4820     ctxt->setContextProperty("moveTargets_transitionFrom", moveTargets_transitionFrom);
4821     ctxt->setContextProperty("moveDisplaced_transitionFrom", moveDisplaced_transitionFrom);
4822     ctxt->setContextProperty("removeTargets_transitionTo", removeTargets_transitionTo);
4823     ctxt->setContextProperty("removeDisplaced_transitionFrom", removeDisplaced_transitionFrom);
4824     ctxt->setContextProperty("enableAddTransitions", enableAddTransitions);
4825     ctxt->setContextProperty("enableMoveTransitions", enableMoveTransitions);
4826     ctxt->setContextProperty("enableRemoveTransitions", enableRemoveTransitions);
4827     ctxt->setContextProperty("rippleAddDisplaced", rippleAddDisplaced);
4828     canvas->setSource(testFileUrl("multipleTransitions.qml"));
4829     canvas->show();
4830     QTest::qWaitForWindowShown(canvas);
4831
4832     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
4833     QTRY_VERIFY(gridview != 0);
4834     QQuickItem *contentItem = gridview->contentItem();
4835     QVERIFY(contentItem != 0);
4836     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4837
4838     if (contentY != 0) {
4839         gridview->setContentY(contentY);
4840         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4841     }
4842
4843     int timeBetweenActions = canvas->rootObject()->property("timeBetweenActions").toInt();
4844
4845     for (int i=0; i<changes.count(); i++) {
4846         switch (changes[i].type) {
4847             case ListChange::Inserted:
4848             {
4849                 QList<QPair<QString, QString> > targetItems;
4850                 for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
4851                     targetItems << qMakePair(QString("new item %1").arg(j), QString::number(j));
4852                 model.insertItems(changes[i].index, targetItems);
4853                 QTRY_COMPARE(model.count(), gridview->count());
4854                 if (i == changes.count() - 1) {
4855                     QTRY_VERIFY(!gridview->property("runningAddTargets").toBool());
4856                     QTRY_VERIFY(!gridview->property("runningAddDisplaced").toBool());
4857                 } else {
4858                     QTest::qWait(timeBetweenActions);
4859                 }
4860                 break;
4861             }
4862             case ListChange::Removed:
4863                 model.removeItems(changes[i].index, changes[i].count);
4864                 QTRY_COMPARE(model.count(), gridview->count());
4865                 if (i == changes.count() - 1) {
4866                     QTRY_VERIFY(!gridview->property("runningRemoveTargets").toBool());
4867                     QTRY_VERIFY(!gridview->property("runningRemoveDisplaced").toBool());
4868                 } else {
4869                     QTest::qWait(timeBetweenActions);
4870                 }
4871                 break;
4872             case ListChange::Moved:
4873                 model.moveItems(changes[i].index, changes[i].to, changes[i].count);
4874                 QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4875                 if (i == changes.count() - 1) {
4876                     QTRY_VERIFY(!gridview->property("runningMoveTargets").toBool());
4877                     QTRY_VERIFY(!gridview->property("runningMoveDisplaced").toBool());
4878                 } else {
4879                     QTest::qWait(timeBetweenActions);
4880                 }
4881                 break;
4882             case ListChange::SetCurrent:
4883                 gridview->setCurrentIndex(changes[i].index);
4884                 QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4885                 break;
4886             case ListChange::SetContentY:
4887                 gridview->setContentY(changes[i].pos);
4888                 QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4889                 break;
4890         }
4891     }
4892     QCOMPARE(gridview->count(), model.count());
4893
4894     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
4895     int firstVisibleIndex = -1;
4896     for (int i=0; i<items.count(); i++) {
4897         if (items[i]->y() >= contentY) {
4898             QQmlExpression e(qmlContext(items[i]), items[i], "index");
4899             firstVisibleIndex = e.evaluate().toInt();
4900             break;
4901         }
4902     }
4903     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
4904
4905     // verify all items moved to the correct final positions
4906     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
4907     for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
4908         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
4909         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
4910         QTRY_COMPARE(item->x(), (i%3)*80.0);
4911         QTRY_COMPARE(item->y(), (i/3)*60.0);
4912         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
4913         QVERIFY(name != 0);
4914         QTRY_COMPARE(name->text(), model.name(i));
4915     }
4916
4917     delete canvas;
4918 }
4919
4920 void tst_QQuickGridView::multipleTransitions_data()
4921 {
4922     QTest::addColumn<int>("initialCount");
4923     QTest::addColumn<qreal>("contentY");
4924     QTest::addColumn<QList<ListChange> >("changes");
4925     QTest::addColumn<bool>("enableAddTransitions");
4926     QTest::addColumn<bool>("enableMoveTransitions");
4927     QTest::addColumn<bool>("enableRemoveTransitions");
4928     QTest::addColumn<bool>("rippleAddDisplaced");
4929
4930     // the added item and displaced items should move to final dest correctly
4931     QTest::newRow("add item, then move it immediately") << 10 << 0.0 << (QList<ListChange>()
4932              << ListChange::insert(0, 1)
4933              << ListChange::move(0, 3, 1)
4934              )
4935              << true << true << true << false;
4936
4937     // items affected by the add should change from move to add transition
4938     QTest::newRow("move, then insert item before the moved item") << 20 << 0.0 << (QList<ListChange>()
4939             << ListChange::move(1, 10, 3)
4940             << ListChange::insert(0, 1)
4941             )
4942             << true << true << true << false;
4943
4944     // items should be placed correctly if you trigger a transition then refill for that index
4945     QTest::newRow("add at 0, flick down, flick back to top and add at 0 again") << 20 << 0.0 << (QList<ListChange>()
4946             << ListChange::insert(0, 1)
4947             << ListChange::setContentY(160.0)
4948             << ListChange::setContentY(0.0)
4949             << ListChange::insert(0, 1)
4950             )
4951             << true << true << true << false;
4952
4953     QTest::newRow("insert then remove same index, with ripple effect on add displaced") << 20 << 0.0 << (QList<ListChange>()
4954             << ListChange::insert(1, 1)
4955             << ListChange::remove(1, 1)
4956             )
4957             << true << true << true << true;
4958
4959     // if item is removed while undergoing a displaced transition, all other items should end up at their correct positions,
4960     // even if a remove-displace transition is not present to re-animate them
4961     QTest::newRow("insert then remove, with remove disabled") << 20 << 0.0 << (QList<ListChange>()
4962             << ListChange::insert(0, 1)
4963             << ListChange::remove(2, 1)
4964             )
4965             << true << true << false << false;
4966
4967     // if last item is not flush with the edge of the view, it should still be refilled in correctly after a
4968     // remove has changed the position of where it will move to
4969     QTest::newRow("insert twice then remove, with remove disabled") << 20 << 0.0 << (QList<ListChange>()
4970             << ListChange::setContentY(-10.0)
4971             << ListChange::insert(0, 1)
4972             << ListChange::insert(0, 1)
4973             << ListChange::remove(2, 1)
4974             )
4975             << true << true << false << false;
4976 }
4977
4978 void tst_QQuickGridView::cacheBuffer()
4979 {
4980     QQuickView *canvas = createView();
4981
4982     QaimModel model;
4983     for (int i = 0; i < 90; i++)
4984         model.addItem("Item" + QString::number(i), "");
4985
4986     QQmlContext *ctxt = canvas->rootContext();
4987     ctxt->setContextProperty("testModel", &model);
4988     ctxt->setContextProperty("testRightToLeft", QVariant(false));
4989     ctxt->setContextProperty("testTopToBottom", QVariant(false));
4990
4991     canvas->setSource(testFileUrl("gridview1.qml"));
4992     canvas->show();
4993     qApp->processEvents();
4994
4995     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
4996     QVERIFY(gridview != 0);
4997
4998     QQuickItem *contentItem = gridview->contentItem();
4999     QVERIFY(contentItem != 0);
5000     QVERIFY(gridview->delegate() != 0);
5001     QVERIFY(gridview->model() != 0);
5002
5003     // Confirm items positioned correctly
5004     int itemCount = findItems<QQuickItem>(contentItem, "wrapper", false).count();
5005     for (int i = 0; i < model.count() && i < itemCount; ++i) {
5006         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
5007         QTRY_COMPARE(item->x(), (i%3)*80.0);
5008         QTRY_COMPARE(item->y(), (i/3)*60.0);
5009     }
5010
5011     QQmlIncubationController controller;
5012     canvas->engine()->setIncubationController(&controller);
5013
5014     canvas->rootObject()->setProperty("cacheBuffer", 200);
5015     QTRY_VERIFY(gridview->cacheBuffer() == 200);
5016
5017     // items will be created one at a time
5018     for (int i = itemCount; i < qMin(itemCount+9,model.count()); ++i) {
5019         QVERIFY(findItem<QQuickItem>(gridview, "wrapper", i) == 0);
5020         QQuickItem *item = 0;
5021         while (!item) {
5022             bool b = false;
5023             controller.incubateWhile(&b);
5024             item = findItem<QQuickItem>(gridview, "wrapper", i);
5025         }
5026     }
5027
5028     {
5029         bool b = true;
5030         controller.incubateWhile(&b);
5031     }
5032
5033     int newItemCount = 0;
5034     newItemCount = findItems<QQuickItem>(contentItem, "wrapper", false).count();
5035
5036     // Confirm items positioned correctly
5037     for (int i = 0; i < model.count() && i < newItemCount; ++i) {
5038         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
5039         QVERIFY(item);
5040         QTRY_COMPARE(item->x(), (i%3)*80.0);
5041         QTRY_COMPARE(item->y(), (i/3)*60.0);
5042     }
5043
5044     // move view and confirm items in view are visible immediately and outside are created async
5045     gridview->setContentY(300);
5046
5047     for (int i = 15; i < 34; ++i) { // 34 due to staggered item creation
5048         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
5049         QVERIFY(item);
5050         QTRY_COMPARE(item->x(), (i%3)*80.0);
5051         QTRY_COMPARE(item->y(), (i/3)*60.0);
5052     }
5053
5054     QVERIFY(findItem<QQuickItem>(gridview, "wrapper", 34) == 0);
5055
5056     // ensure buffered items are created
5057     for (int i = 34; i < qMin(44,model.count()); ++i) {
5058         QQuickItem *item = 0;
5059         while (!item) {
5060             qGuiApp->processEvents(); // allow refill to happen
5061             bool b = false;
5062             controller.incubateWhile(&b);
5063             item = findItem<QQuickItem>(gridview, "wrapper", i);
5064         }
5065     }
5066
5067     {
5068         bool b = true;
5069         controller.incubateWhile(&b);
5070     }
5071
5072     delete canvas;
5073 }
5074
5075 void tst_QQuickGridView::asynchronous()
5076 {
5077     QQuickView *canvas = createView();
5078     canvas->show();
5079     QQmlIncubationController controller;
5080     canvas->engine()->setIncubationController(&controller);
5081
5082     canvas->setSource(testFile("asyncloader.qml"));
5083
5084     QQuickItem *rootObject = qobject_cast<QQuickItem*>(canvas->rootObject());
5085     QVERIFY(rootObject);
5086
5087     QQuickGridView *gridview = 0;
5088     while (!gridview) {
5089         bool b = false;
5090         controller.incubateWhile(&b);
5091         gridview = rootObject->findChild<QQuickGridView*>("view");
5092     }
5093
5094     // items will be created one at a time
5095     for (int i = 0; i < 12; ++i) {
5096         QVERIFY(findItem<QQuickItem>(gridview, "wrapper", i) == 0);
5097         QQuickItem *item = 0;
5098         while (!item) {
5099             bool b = false;
5100             controller.incubateWhile(&b);
5101             item = findItem<QQuickItem>(gridview, "wrapper", i);
5102         }
5103     }
5104
5105     {
5106         bool b = true;
5107         controller.incubateWhile(&b);
5108     }
5109
5110     // verify positioning
5111     QQuickItem *contentItem = gridview->contentItem();
5112     for (int i = 0; i < 12; ++i) {
5113         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
5114         if (!item) qWarning() << "Item" << i << "not found";
5115         QVERIFY(item->x() == (i%3)*100);
5116         QVERIFY(item->y() == (i/3)*100);
5117     }
5118
5119     delete canvas;
5120 }
5121
5122 void tst_QQuickGridView::unrequestedVisibility()
5123 {
5124     QaimModel model;
5125     for (int i = 0; i < 30; i++)
5126         model.addItem("Item" + QString::number(i), QString::number(i));
5127
5128     QQuickView *canvas = new QQuickView(0);
5129     canvas->setGeometry(0,0,240,320);
5130
5131     QQmlContext *ctxt = canvas->rootContext();
5132     ctxt->setContextProperty("testModel", &model);
5133     ctxt->setContextProperty("testWrap", QVariant(false));
5134
5135     canvas->setSource(testFileUrl("unrequestedItems.qml"));
5136
5137     canvas->show();
5138
5139     qApp->processEvents();
5140
5141     QQuickGridView *leftview = findItem<QQuickGridView>(canvas->rootObject(), "leftGrid");
5142     QTRY_VERIFY(leftview != 0);
5143
5144     QQuickGridView *rightview = findItem<QQuickGridView>(canvas->rootObject(), "rightGrid");
5145     QTRY_VERIFY(rightview != 0);
5146
5147     QQuickItem *leftContent = leftview->contentItem();
5148     QTRY_VERIFY(leftContent != 0);
5149
5150     QQuickItem *rightContent = rightview->contentItem();
5151     QTRY_VERIFY(rightContent != 0);
5152
5153     rightview->setCurrentIndex(12);
5154
5155     QTRY_COMPARE(leftview->contentY(), 0.0);
5156     QTRY_COMPARE(rightview->contentY(), 240.0);
5157
5158     QQuickItem *item;
5159
5160     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
5161     QCOMPARE(item->isVisible(), true);
5162     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
5163     QCOMPARE(item->isVisible(), false);
5164
5165     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
5166     QCOMPARE(item->isVisible(), false);
5167     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
5168     QCOMPARE(item->isVisible(), true);
5169
5170     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 9));
5171     QCOMPARE(item->isVisible(), true);
5172     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 10));
5173     QCOMPARE(item->isVisible(), false);
5174     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 3));
5175     QCOMPARE(item->isVisible(), false);
5176     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 4));
5177     QCOMPARE(item->isVisible(), true);
5178
5179     rightview->setCurrentIndex(0);
5180
5181     QTRY_COMPARE(leftview->contentY(), 0.0);
5182     QTRY_COMPARE(rightview->contentY(), 0.0);
5183
5184     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
5185     QCOMPARE(item->isVisible(), true);
5186     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
5187     QTRY_COMPARE(item->isVisible(), true);
5188
5189     QVERIFY(!findItem<QQuickItem>(leftContent, "wrapper", 11));
5190     QVERIFY(!findItem<QQuickItem>(rightContent, "wrapper", 11));
5191
5192     leftview->setCurrentIndex(12);
5193
5194     QTRY_COMPARE(leftview->contentY(), 240.0);
5195     QTRY_COMPARE(rightview->contentY(), 0.0);
5196
5197     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
5198     QTRY_COMPARE(item->isVisible(), false);
5199     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
5200     QCOMPARE(item->isVisible(), true);
5201
5202     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
5203     QCOMPARE(item->isVisible(), true);
5204     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
5205     QCOMPARE(item->isVisible(), false);
5206
5207     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
5208     QCOMPARE(item->isVisible(), false);
5209     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
5210     QCOMPARE(item->isVisible(), true);
5211     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
5212     QCOMPARE(item->isVisible(), true);
5213     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
5214     QCOMPARE(item->isVisible(), false);
5215
5216     // move a non-visible item into view
5217     model.moveItems(10, 9, 1);
5218     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
5219
5220     QTRY_VERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
5221     QCOMPARE(item->isVisible(), false);
5222     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
5223     QCOMPARE(item->isVisible(), true);
5224
5225     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
5226     QCOMPARE(item->isVisible(), true);
5227     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
5228     QCOMPARE(item->isVisible(), false);
5229
5230     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
5231     QCOMPARE(item->isVisible(), false);
5232     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
5233     QCOMPARE(item->isVisible(), true);
5234     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
5235     QCOMPARE(item->isVisible(), true);
5236     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
5237     QCOMPARE(item->isVisible(), false);
5238
5239     // move a visible item out of view
5240     model.moveItems(5, 3, 1);
5241     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
5242
5243     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
5244     QCOMPARE(item->isVisible(), false);
5245     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
5246     QCOMPARE(item->isVisible(), true);
5247     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
5248     QCOMPARE(item->isVisible(), true);
5249     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
5250     QCOMPARE(item->isVisible(), false);
5251
5252     // move a non-visible item into view
5253     model.moveItems(3, 5, 1);
5254     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
5255
5256     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
5257     QCOMPARE(item->isVisible(), false);
5258     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
5259     QCOMPARE(item->isVisible(), true);
5260     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
5261     QCOMPARE(item->isVisible(), true);
5262     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
5263     QCOMPARE(item->isVisible(), false);
5264
5265     // move a visible item out of view
5266     model.moveItems(9, 10, 1);
5267     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
5268
5269     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
5270     QCOMPARE(item->isVisible(), false);
5271     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
5272     QCOMPARE(item->isVisible(), true);
5273     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
5274     QCOMPARE(item->isVisible(), true);
5275     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
5276     QCOMPARE(item->isVisible(), false);
5277
5278     // move a non-visible item into view
5279     model.moveItems(10, 9, 1);
5280     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
5281
5282     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
5283     QCOMPARE(item->isVisible(), false);
5284     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
5285     QCOMPARE(item->isVisible(), true);
5286     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
5287     QCOMPARE(item->isVisible(), true);
5288     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
5289     QCOMPARE(item->isVisible(), false);
5290
5291     delete canvas;
5292 }
5293
5294 QList<int> tst_QQuickGridView::toIntList(const QVariantList &list)
5295 {
5296     QList<int> ret;
5297     bool ok = true;
5298     for (int i=0; i<list.count(); i++) {
5299         ret << list[i].toInt(&ok);
5300         if (!ok)
5301             qWarning() << "tst_QQuickGridView::toIntList(): not a number:" << list[i];
5302     }
5303
5304     return ret;
5305 }
5306
5307 void tst_QQuickGridView::matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes)
5308 {
5309     for (int i=0; i<indexLists.count(); i++) {
5310         QSet<int> current = indexLists[i].value<QList<int> >().toSet();
5311         if (current != expectedIndexes.toSet())
5312             qDebug() << "Cannot match actual targets" << current << "with expected" << expectedIndexes;
5313         QCOMPARE(current, expectedIndexes.toSet());
5314     }
5315 }
5316
5317 void tst_QQuickGridView::matchItemsAndIndexes(const QVariantMap &items, const QaimModel &model, const QList<int> &expectedIndexes)
5318 {
5319     for (QVariantMap::const_iterator it = items.begin(); it != items.end(); ++it) {
5320         QVERIFY(it.value().type() == QVariant::Int);
5321         QString name = it.key();
5322         int itemIndex = it.value().toInt();
5323         QVERIFY2(expectedIndexes.contains(itemIndex), QTest::toString(QString("Index %1 not found in expectedIndexes").arg(itemIndex)));
5324         if (model.name(itemIndex) != name)
5325             qDebug() << itemIndex;
5326         QCOMPARE(model.name(itemIndex), name);
5327     }
5328     QCOMPARE(items.count(), expectedIndexes.count());
5329 }
5330
5331 void tst_QQuickGridView::matchItemLists(const QVariantList &itemLists, const QList<QQuickItem *> &expectedItems)
5332 {
5333     for (int i=0; i<itemLists.count(); i++) {
5334         QVariantList current = itemLists[i].toList();
5335         for (int j=0; j<current.count(); j++) {
5336             QQuickItem *o = qobject_cast<QQuickItem*>(current[j].value<QObject*>());
5337             QVERIFY2(o, QTest::toString(QString("Invalid actual item at %1").arg(j)));
5338             QVERIFY2(expectedItems.contains(o), QTest::toString(QString("Cannot match item %1").arg(j)));
5339         }
5340         QCOMPARE(current.count(), expectedItems.count());
5341     }
5342 }
5343
5344 QTEST_MAIN(tst_QQuickGridView)
5345
5346 #include "tst_qquickgridview.moc"
5347