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