Update copyright year in license headers.
[profile/ivi/qtdeclarative.git] / tests / auto / qtquick2 / qquickgridview / tst_qquickgridview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the test suite of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
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 <QtDeclarative/qdeclarativeengine.h>
46 #include <QtDeclarative/qdeclarativecomponent.h>
47 #include <QtDeclarative/qdeclarativecontext.h>
48 #include <QtDeclarative/qdeclarativeexpression.h>
49 #include <QtDeclarative/qdeclarativeincubator.h>
50 #include <QtQuick/private/qquickitem_p.h>
51 #include <QtDeclarative/private/qlistmodelinterface_p.h>
52 #include <QtQuick/private/qquickgridview_p.h>
53 #include <QtQuick/private/qquicktext_p.h>
54 #include <QtDeclarative/private/qdeclarativelistmodel_p.h>
55 #include "../../shared/util.h"
56 #include <QtGui/qguiapplication.h>
57
58 Q_DECLARE_METATYPE(Qt::LayoutDirection)
59 Q_DECLARE_METATYPE(QQuickGridView::Flow)
60
61 class tst_QQuickGridView : public QDeclarativeDataTest
62 {
63     Q_OBJECT
64 public:
65     tst_QQuickGridView();
66
67 private slots:
68     void items();
69     void changed();
70     void inserted();
71     void inserted_more();
72     void inserted_more_data();
73     void insertBeforeVisible();
74     void insertBeforeVisible_data();
75     void removed();
76     void addOrRemoveBeforeVisible();
77     void addOrRemoveBeforeVisible_data();
78     void clear();
79     void moved();
80     void moved_data();
81     void multipleChanges();
82     void multipleChanges_data();
83     void swapWithFirstItem();
84     void changeFlow();
85     void currentIndex();
86     void noCurrentIndex();
87     void defaultValues();
88     void properties();
89     void propertyChanges();
90     void componentChanges();
91     void modelChanges();
92     void positionViewAtIndex();
93     void positionViewAtIndex_rightToLeft();
94     void mirroring();
95     void snapping();
96     void resetModel();
97     void enforceRange();
98     void enforceRange_rightToLeft();
99     void QTBUG_8456();
100     void manualHighlight();
101     void footer();
102     void footer_data();
103     void header();
104     void header_data();
105     void resizeViewAndRepaint();
106     void indexAt();
107     void onAdd();
108     void onAdd_data();
109     void onRemove();
110     void onRemove_data();
111     void columnCount();
112     void margins();
113     void creationContext();
114     void snapToRow_data();
115     void snapToRow();
116     void snapOneRow_data();
117     void snapOneRow();
118     void unaligned();
119     void cacheBuffer();
120     void asynchronous();
121     void unrequestedVisibility();
122
123 private:
124     QQuickView *createView();
125     void flick(QQuickView *canvas, const QPoint &from, const QPoint &to, int duration);
126     template<typename T>
127     T *findItem(QQuickItem *parent, const QString &id, int index=-1);
128     template<typename T>
129     QList<T*> findItems(QQuickItem *parent, const QString &objectName);
130     void dumpTree(QQuickItem *parent, int depth = 0);
131 };
132
133 template<typename T>
134 void tst_qquickgridview_move(int from, int to, int n, T *items)
135 {
136     if (from > to) {
137         // Only move forwards - flip if backwards moving
138         int tfrom = from;
139         int tto = to;
140         from = tto;
141         to = tto+n;
142         n = tfrom-tto;
143     }
144
145     T replaced;
146     int i=0;
147     typename T::ConstIterator it=items->begin(); it += from+n;
148     for (; i<to-from; ++i,++it)
149         replaced.append(*it);
150     i=0;
151     it=items->begin(); it += from;
152     for (; i<n; ++i,++it)
153         replaced.append(*it);
154     typename T::ConstIterator f=replaced.begin();
155     typename T::Iterator t=items->begin(); t += from;
156     for (; f != replaced.end(); ++f, ++t)
157         *t = *f;
158 }
159
160 class TestModel : public QAbstractListModel
161 {
162 public:
163     enum Roles { Name = Qt::UserRole+1, Number = Qt::UserRole+2 };
164
165     TestModel(QObject *parent=0) : QAbstractListModel(parent) {
166         QHash<int, QByteArray> roles;
167         roles[Name] = "name";
168         roles[Number] = "number";
169         setRoleNames(roles);
170     }
171
172     int rowCount(const QModelIndex &parent=QModelIndex()) const { Q_UNUSED(parent); return list.count(); }
173     QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const {
174         QVariant rv;
175         if (role == Name)
176             rv = list.at(index.row()).first;
177         else if (role == Number)
178             rv = list.at(index.row()).second;
179
180         return rv;
181     }
182
183     int count() const { return rowCount(); }
184     QString name(int index) const { return list.at(index).first; }
185     QString number(int index) const { return list.at(index).second; }
186
187     void addItem(const QString &name, const QString &number) {
188         emit beginInsertRows(QModelIndex(), list.count(), list.count());
189         list.append(QPair<QString,QString>(name, number));
190         emit endInsertRows();
191     }
192
193     void addItems(const QList<QPair<QString, QString> > &items) {
194         emit beginInsertRows(QModelIndex(), list.count(), list.count()+items.count()-1);
195         for (int i=0; i<items.count(); i++)
196             list.append(QPair<QString,QString>(items[i].first, items[i].second));
197         emit endInsertRows();
198     }
199
200     void insertItem(int index, const QString &name, const QString &number) {
201         emit beginInsertRows(QModelIndex(), index, index);
202         list.insert(index, QPair<QString,QString>(name, number));
203         emit endInsertRows();
204     }
205
206     void insertItems(int index, const QList<QPair<QString, QString> > &items) {
207         emit beginInsertRows(QModelIndex(), index, index + items.count() - 1);
208         for (int i=0; i<items.count(); i++)
209             list.insert(index + i, QPair<QString,QString>(items[i].first, items[i].second));
210         emit endInsertRows();
211     }
212
213     void removeItem(int index) {
214         emit beginRemoveRows(QModelIndex(), index, index);
215         list.removeAt(index);
216         emit endRemoveRows();
217     }
218
219     void removeItems(int index, int count) {
220         emit beginRemoveRows(QModelIndex(), index, index+count-1);
221         while (count--)
222             list.removeAt(index);
223         emit endRemoveRows();
224     }
225
226     void moveItem(int from, int to) {
227         emit beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
228         list.move(from, to);
229         emit endMoveRows();
230     }
231
232     void moveItems(int from, int to, int count) {
233         emit beginMoveRows(QModelIndex(), from, from+count-1, QModelIndex(), to > from ? to+count : to);
234         tst_qquickgridview_move(from, to, count, &list);
235         emit endMoveRows();
236     }
237
238     void modifyItem(int idx, const QString &name, const QString &number) {
239         list[idx] = QPair<QString,QString>(name, number);
240         emit dataChanged(index(idx,0), index(idx,0));
241     }
242
243     void clear() {
244         int count = list.count();
245         emit beginRemoveRows(QModelIndex(), 0, count-1);
246         list.clear();
247         emit endRemoveRows();
248     }
249
250
251 private:
252     QList<QPair<QString,QString> > list;
253 };
254
255 tst_QQuickGridView::tst_QQuickGridView()
256 {
257 }
258
259 void tst_QQuickGridView::items()
260 {
261     QQuickView *canvas = createView();
262
263     TestModel model;
264     model.addItem("Fred", "12345");
265     model.addItem("John", "2345");
266     model.addItem("Bob", "54321");
267     model.addItem("Billy", "22345");
268     model.addItem("Sam", "2945");
269     model.addItem("Ben", "04321");
270     model.addItem("Jim", "0780");
271
272     QDeclarativeContext *ctxt = canvas->rootContext();
273     ctxt->setContextProperty("testModel", &model);
274     ctxt->setContextProperty("testRightToLeft", QVariant(false));
275     ctxt->setContextProperty("testTopToBottom", QVariant(false));
276
277     canvas->setSource(testFileUrl("gridview1.qml"));
278     qApp->processEvents();
279
280     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
281     QTRY_VERIFY(gridview != 0);
282
283     QQuickItem *contentItem = gridview->contentItem();
284     QTRY_VERIFY(contentItem != 0);
285
286     QTRY_COMPARE(gridview->count(), model.count());
287     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
288     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
289
290     for (int i = 0; i < model.count(); ++i) {
291         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
292         QTRY_VERIFY(name != 0);
293         QTRY_COMPARE(name->text(), model.name(i));
294         QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
295         QTRY_VERIFY(number != 0);
296         QTRY_COMPARE(number->text(), model.number(i));
297     }
298
299     // set an empty model and confirm that items are destroyed
300     TestModel model2;
301     ctxt->setContextProperty("testModel", &model2);
302
303     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
304     QTRY_VERIFY(itemCount == 0);
305
306     delete canvas;
307 }
308
309 void tst_QQuickGridView::changed()
310 {
311     QQuickView *canvas = createView();
312
313     TestModel model;
314     model.addItem("Fred", "12345");
315     model.addItem("John", "2345");
316     model.addItem("Bob", "54321");
317     model.addItem("Billy", "22345");
318     model.addItem("Sam", "2945");
319     model.addItem("Ben", "04321");
320     model.addItem("Jim", "0780");
321
322     QDeclarativeContext *ctxt = canvas->rootContext();
323     ctxt->setContextProperty("testModel", &model);
324     ctxt->setContextProperty("testRightToLeft", QVariant(false));
325     ctxt->setContextProperty("testTopToBottom", QVariant(false));
326
327     canvas->setSource(testFileUrl("gridview1.qml"));
328     qApp->processEvents();
329
330     QQuickFlickable *gridview = findItem<QQuickFlickable>(canvas->rootObject(), "grid");
331     QTRY_VERIFY(gridview != 0);
332
333     QQuickItem *contentItem = gridview->contentItem();
334     QTRY_VERIFY(contentItem != 0);
335
336     model.modifyItem(1, "Will", "9876");
337     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
338     QTRY_VERIFY(name != 0);
339     QTRY_COMPARE(name->text(), model.name(1));
340     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
341     QTRY_VERIFY(number != 0);
342     QTRY_COMPARE(number->text(), model.number(1));
343
344     delete canvas;
345 }
346
347 void tst_QQuickGridView::inserted()
348 {
349     QQuickView *canvas = createView();
350     canvas->show();
351
352     TestModel model;
353     model.addItem("Fred", "12345");
354     model.addItem("John", "2345");
355     model.addItem("Bob", "54321");
356
357     QDeclarativeContext *ctxt = canvas->rootContext();
358     ctxt->setContextProperty("testModel", &model);
359     ctxt->setContextProperty("testRightToLeft", QVariant(false));
360     ctxt->setContextProperty("testTopToBottom", QVariant(false));
361
362     canvas->setSource(testFileUrl("gridview1.qml"));
363     qApp->processEvents();
364
365     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
366     QTRY_VERIFY(gridview != 0);
367
368     QQuickItem *contentItem = gridview->contentItem();
369     QTRY_VERIFY(contentItem != 0);
370
371     model.insertItem(1, "Will", "9876");
372
373     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
374     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
375
376     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
377     QTRY_VERIFY(name != 0);
378     QTRY_COMPARE(name->text(), model.name(1));
379     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
380     QTRY_VERIFY(number != 0);
381     QTRY_COMPARE(number->text(), model.number(1));
382
383     // Checks that onAdd is called
384     int added = canvas->rootObject()->property("added").toInt();
385     QTRY_COMPARE(added, 1);
386
387     // Confirm items positioned correctly
388     for (int i = 0; i < model.count(); ++i) {
389         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
390         QTRY_COMPARE(item->x(), (i%3)*80.0);
391         QTRY_COMPARE(item->y(), (i/3)*60.0);
392     }
393
394     model.insertItem(0, "Foo", "1111"); // zero index, and current item
395
396     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
397
398     name = findItem<QQuickText>(contentItem, "textName", 0);
399     QTRY_VERIFY(name != 0);
400     QTRY_COMPARE(name->text(), model.name(0));
401     number = findItem<QQuickText>(contentItem, "textNumber", 0);
402     QTRY_VERIFY(number != 0);
403     QTRY_COMPARE(number->text(), model.number(0));
404
405     QTRY_COMPARE(gridview->currentIndex(), 1);
406
407     // Confirm items positioned correctly
408     for (int i = 0; i < model.count(); ++i) {
409         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
410         QTRY_VERIFY(item->x() == (i%3)*80);
411         QTRY_VERIFY(item->y() == (i/3)*60);
412     }
413
414     for (int i = model.count(); i < 30; ++i)
415         model.insertItem(i, "Hello", QString::number(i));
416
417     gridview->setContentY(120);
418
419     // Insert item outside visible area
420     model.insertItem(1, "Hello", "1324");
421
422     QTRY_VERIFY(gridview->contentY() == 120);
423
424     delete canvas;
425 }
426
427 void tst_QQuickGridView::inserted_more()
428 {
429     QFETCH(qreal, contentY);
430     QFETCH(int, insertIndex);
431     QFETCH(int, insertCount);
432     QFETCH(qreal, itemsOffsetAfterMove);
433
434     QQuickText *name;
435     QQuickText *number;
436     QQuickView *canvas = createView();
437     canvas->show();
438
439     TestModel model;
440     for (int i = 0; i < 30; i++)
441         model.addItem("Item" + QString::number(i), "");
442
443     QDeclarativeContext *ctxt = canvas->rootContext();
444     ctxt->setContextProperty("testModel", &model);
445     ctxt->setContextProperty("testRightToLeft", QVariant(false));
446     ctxt->setContextProperty("testTopToBottom", QVariant(false));
447
448     canvas->setSource(testFileUrl("gridview1.qml"));
449     qApp->processEvents();
450
451     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
452     QTRY_VERIFY(gridview != 0);
453     QQuickItem *contentItem = gridview->contentItem();
454     QTRY_VERIFY(contentItem != 0);
455
456     gridview->setContentY(contentY);
457
458     QList<QPair<QString, QString> > newData;
459     for (int i=0; i<insertCount; i++)
460         newData << qMakePair(QString("value %1").arg(i), QString::number(i));
461     model.insertItems(insertIndex, newData);
462     QTRY_COMPARE(gridview->property("count").toInt(), model.count());
463
464     // check visibleItems.first() is in correct position
465     QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper", 0);
466     QVERIFY(item0);
467     QCOMPARE(item0->y(), itemsOffsetAfterMove);
468
469     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
470     int firstVisibleIndex = -1;
471     for (int i=0; i<items.count(); i++) {
472         if (items[i]->y() >= contentY) {
473             QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
474             firstVisibleIndex = e.evaluate().toInt();
475             break;
476         }
477     }
478     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
479
480     // Confirm items positioned correctly and indexes correct
481     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
482     for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
483         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
484         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
485
486         QCOMPARE(item->x(), (i%3)*80.0);
487         QCOMPARE(item->y(), (i/3)*60.0 + itemsOffsetAfterMove);
488
489         name = findItem<QQuickText>(contentItem, "textName", i);
490         QVERIFY(name != 0);
491         QCOMPARE(name->text(), model.name(i));
492         number = findItem<QQuickText>(contentItem, "textNumber", i);
493         QVERIFY(number != 0);
494         QCOMPARE(number->text(), model.number(i));
495     }
496
497     delete canvas;
498 }
499
500 void tst_QQuickGridView::inserted_more_data()
501 {
502     QTest::addColumn<qreal>("contentY");
503     QTest::addColumn<int>("insertIndex");
504     QTest::addColumn<int>("insertCount");
505     QTest::addColumn<qreal>("itemsOffsetAfterMove");
506
507     QTest::newRow("add 1, before visible items")
508             << 120.0     // show 6-23
509             << 5 << 1
510             << 0.0;   // insert 1 above first visible, grid is rearranged; first visible moves forward within its row
511                       // new 1st visible item is at 0
512
513     QTest::newRow("add 2, before visible items")
514             << 120.0     // show 6-23
515             << 5 << 2
516             << 0.0;   // insert 2 above first visible, grid is rearranged; first visible moves forward within its row
517
518     QTest::newRow("add 3, before visible items")
519             << 120.0     // show 6-23
520             << 5 << 3
521             << -60.0;   // insert 3 (1 row) above first visible in negative pos, first visible does not move
522
523     QTest::newRow("add 5, before visible items")
524             << 120.0     // show 6-23
525             << 5 << 5
526             << -60.0;   // insert 1 row + 2 items above first visible, 1 row added at negative pos,
527                         // grid is rearranged and first visible moves forward within its row
528
529     QTest::newRow("add 6, before visible items")
530             << 120.0     // show 6-23
531             << 5 << 6
532             << -60.0 * 2;   // insert 2 rows above first visible in negative pos, first visible does not move
533
534
535
536    QTest::newRow("add 1, at start of visible, content at start")
537             << 0.0
538             << 0 << 1
539             << 0.0;
540
541     QTest::newRow("add multiple, at start of visible, content at start")
542             << 0.0
543             << 0 << 3
544             << 0.0;
545
546     QTest::newRow("add 1, at start of visible, content not at start")
547             << 120.0     // show 6-23
548             << 6 << 1
549             << 0.0;
550
551     QTest::newRow("add multiple, at start of visible, content not at start")
552             << 120.0     // show 6-23
553             << 6 << 3
554             << 0.0;
555
556
557     QTest::newRow("add 1, at end of visible, content at start")
558             << 0.0
559             << 17 << 1
560             << 0.0;
561
562     QTest::newRow("add 1, at end of visible, content at start")
563             << 0.0
564             << 17 << 3
565             << 0.0;
566
567     QTest::newRow("add 1, at end of visible, content not at start")
568             << 120.0     // show 6-23
569             << 23 << 1
570             << 0.0;
571
572     QTest::newRow("add multiple, at end of visible, content not at start")
573             << 120.0     // show 6-23
574             << 23 << 3
575             << 0.0;
576
577
578     QTest::newRow("add 1, after visible, content at start")
579             << 0.0
580             << 20 << 1
581             << 0.0;
582
583     QTest::newRow("add 1, after visible, content at start")
584             << 0.0
585             << 20 << 3
586             << 0.0;
587
588     QTest::newRow("add 1, after visible, content not at start")
589             << 120.0     // show 6-23
590             << 24 << 1
591             << 0.0;
592
593     QTest::newRow("add multiple, after visible, content not at start")
594             << 120.0     // show 6-23
595             << 24 << 3
596             << 0.0;
597 }
598
599 void tst_QQuickGridView::insertBeforeVisible()
600 {
601     QFETCH(int, insertIndex);
602     QFETCH(int, insertCount);
603     QFETCH(int, cacheBuffer);
604
605     QQuickText *name;
606     QQuickView *canvas = createView();
607     canvas->show();
608
609     TestModel model;
610     for (int i = 0; i < 30; i++)
611         model.addItem("Item" + QString::number(i), "");
612
613     QDeclarativeContext *ctxt = canvas->rootContext();
614     ctxt->setContextProperty("testModel", &model);
615     ctxt->setContextProperty("testRightToLeft", QVariant(false));
616     ctxt->setContextProperty("testTopToBottom", QVariant(false));
617     canvas->setSource(testFileUrl("gridview1.qml"));
618     qApp->processEvents();
619
620     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
621     QTRY_VERIFY(gridview != 0);
622     QQuickItem *contentItem = gridview->contentItem();
623     QTRY_VERIFY(contentItem != 0);
624
625     gridview->setCacheBuffer(cacheBuffer);
626
627     // trigger a refill (not just setting contentY) so that the visibleItems grid is updated
628     int firstVisibleIndex = 20;     // move to an index where the top item is not visible
629     gridview->setContentY(firstVisibleIndex * 20.0);
630     gridview->setCurrentIndex(firstVisibleIndex);
631     qApp->processEvents();
632     QTRY_COMPARE(gridview->currentIndex(), firstVisibleIndex);
633     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", firstVisibleIndex);
634     QVERIFY(item);
635     QCOMPARE(item->y(), gridview->contentY());
636
637     QList<QPair<QString, QString> > newData;
638     for (int i=0; i<insertCount; i++)
639         newData << qMakePair(QString("value %1").arg(i), QString::number(i));
640     model.insertItems(insertIndex, newData);
641     QTRY_COMPARE(gridview->property("count").toInt(), model.count());
642
643     // now, moving to the top of the view should position the inserted items correctly
644     int itemsOffsetAfterMove = (insertCount / 3) * -60.0;
645     gridview->setCurrentIndex(0);
646     QTRY_COMPARE(gridview->currentIndex(), 0);
647     QTRY_COMPARE(gridview->contentY(), 0.0 + itemsOffsetAfterMove);
648
649     // Confirm items positioned correctly and indexes correct
650     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
651     for (int i = 0; i < model.count() && i < itemCount; ++i) {
652         item = findItem<QQuickItem>(contentItem, "wrapper", i);
653         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
654         QCOMPARE(item->x(), (i%3)*80.0);
655         QCOMPARE(item->y(), (i/3)*60.0 + itemsOffsetAfterMove);
656         name = findItem<QQuickText>(contentItem, "textName", i);
657         QVERIFY(name != 0);
658         QTRY_COMPARE(name->text(), model.name(i));
659     }
660
661     delete canvas;
662 }
663
664 void tst_QQuickGridView::insertBeforeVisible_data()
665 {
666     QTest::addColumn<int>("insertIndex");
667     QTest::addColumn<int>("insertCount");
668     QTest::addColumn<int>("cacheBuffer");
669
670     QTest::newRow("insert 1 at 0, 0 buffer") << 0 << 1 << 0;
671     QTest::newRow("insert 1 at 0, 100 buffer") << 0 << 1 << 100;
672     QTest::newRow("insert 1 at 0, 500 buffer") << 0 << 1 << 500;
673
674     QTest::newRow("insert 1 at 1, 0 buffer") << 1 << 1 << 0;
675     QTest::newRow("insert 1 at 1, 100 buffer") << 1 << 1 << 100;
676     QTest::newRow("insert 1 at 1, 500 buffer") << 1 << 1 << 500;
677
678     QTest::newRow("insert multiple at 0, 0 buffer") << 0 << 6 << 0;
679     QTest::newRow("insert multiple at 0, 100 buffer") << 0 << 6 << 100;
680     QTest::newRow("insert multiple at 0, 500 buffer") << 0 << 6 << 500;
681
682     QTest::newRow("insert multiple at 1, 0 buffer") << 1 << 6 << 0;
683     QTest::newRow("insert multiple at 1, 100 buffer") << 1 << 6 << 100;
684     QTest::newRow("insert multiple at 1, 500 buffer") << 1 << 6 << 500;
685 }
686
687 void tst_QQuickGridView::removed()
688 {
689     QQuickView *canvas = createView();
690     canvas->show();
691
692     TestModel model;
693     for (int i = 0; i < 40; i++)
694         model.addItem("Item" + QString::number(i), "");
695
696     QDeclarativeContext *ctxt = canvas->rootContext();
697     ctxt->setContextProperty("testModel", &model);
698     ctxt->setContextProperty("testRightToLeft", QVariant(false));
699     ctxt->setContextProperty("testTopToBottom", QVariant(false));
700
701     canvas->setSource(testFileUrl("gridview1.qml"));
702     qApp->processEvents();
703
704     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
705     QTRY_VERIFY(gridview != 0);
706
707     QQuickItem *contentItem = gridview->contentItem();
708     QTRY_VERIFY(contentItem != 0);
709
710     model.removeItem(1);
711     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
712
713     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
714     QTRY_VERIFY(name != 0);
715     QTRY_COMPARE(name->text(), model.name(1));
716     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
717     QTRY_VERIFY(number != 0);
718     QTRY_COMPARE(number->text(), model.number(1));
719
720
721     // Checks that onRemove is called
722     QString removed = canvas->rootObject()->property("removed").toString();
723     QTRY_COMPARE(removed, QString("Item1"));
724
725     // Confirm items positioned correctly
726     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
727     for (int i = 0; i < model.count() && i < itemCount; ++i) {
728         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
729         if (!item) qWarning() << "Item" << i << "not found";
730         QTRY_VERIFY(item->x() == (i%3)*80);
731         QTRY_VERIFY(item->y() == (i/3)*60);
732     }
733
734     // Remove first item (which is the current item);
735     model.removeItem(0);
736     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
737
738     name = findItem<QQuickText>(contentItem, "textName", 0);
739     QTRY_VERIFY(name != 0);
740     QTRY_COMPARE(name->text(), model.name(0));
741     number = findItem<QQuickText>(contentItem, "textNumber", 0);
742     QTRY_VERIFY(number != 0);
743     QTRY_COMPARE(number->text(), model.number(0));
744
745
746     // Confirm items positioned correctly
747     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
748     for (int i = 0; i < model.count() && i < itemCount; ++i) {
749         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
750         if (!item) qWarning() << "Item" << i << "not found";
751         QTRY_VERIFY(item->x() == (i%3)*80);
752         QTRY_VERIFY(item->y() == (i/3)*60);
753     }
754
755     // Remove items not visible
756     model.removeItem(25);
757     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
758
759     // Confirm items positioned correctly
760     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
761     for (int i = 0; i < model.count() && i < itemCount; ++i) {
762         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
763         if (!item) qWarning() << "Item" << i << "not found";
764         QTRY_VERIFY(item->x() == (i%3)*80);
765         QTRY_VERIFY(item->y() == (i/3)*60);
766     }
767
768     // Remove items before visible
769     gridview->setContentY(120);
770     gridview->setCurrentIndex(10);
771
772     // Setting currentIndex above shouldn't cause view to scroll
773     QTRY_COMPARE(gridview->contentY(), 120.0);
774
775     model.removeItem(1);
776     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
777
778     // Confirm items positioned correctly
779     for (int i = 6; i < 18; ++i) {
780         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
781         if (!item) qWarning() << "Item" << i << "not found";
782         QTRY_VERIFY(item->x() == (i%3)*80);
783         QTRY_VERIFY(item->y() == (i/3)*60);
784     }
785
786     // Remove currentIndex
787     QQuickItem *oldCurrent = gridview->currentItem();
788     model.removeItem(9);
789     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
790
791     QTRY_COMPARE(gridview->currentIndex(), 9);
792     QTRY_VERIFY(gridview->currentItem() != oldCurrent);
793
794     gridview->setContentY(0);
795     // let transitions settle.
796     QTest::qWait(300);
797
798     // Confirm items positioned correctly
799     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
800     for (int i = 0; i < model.count() && i < itemCount; ++i) {
801         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
802         QTRY_VERIFY(item->x() == (i%3)*80);
803         QTRY_VERIFY(item->y() == (i/3)*60);
804     }
805
806     // remove item outside current view.
807     gridview->setCurrentIndex(32);
808     gridview->setContentY(240);
809
810     model.removeItem(30);
811     QTRY_VERIFY(gridview->currentIndex() == 31);
812
813     // remove current item beyond visible items.
814     gridview->setCurrentIndex(20);
815     gridview->setContentY(0);
816     model.removeItem(20);
817
818     QTRY_COMPARE(gridview->currentIndex(), 20);
819     QTRY_VERIFY(gridview->currentItem() != 0);
820
821     // remove item before current, but visible
822     gridview->setCurrentIndex(8);
823     gridview->setContentY(240);
824     oldCurrent = gridview->currentItem();
825     model.removeItem(6);
826
827     QTRY_COMPARE(gridview->currentIndex(), 7);
828     QTRY_VERIFY(gridview->currentItem() == oldCurrent);
829
830     delete canvas;
831 }
832
833 void tst_QQuickGridView::addOrRemoveBeforeVisible()
834 {
835     // QTBUG-21588: ensure re-layout is done on grid after adding or removing
836     // items from before the visible area
837
838     QFETCH(bool, doAdd);
839     QFETCH(qreal, newTopContentY);
840
841     QQuickView *canvas = createView();
842     canvas->show();
843
844     TestModel model;
845     for (int i = 0; i < 30; i++)
846         model.addItem("Item" + QString::number(i), "");
847
848     QDeclarativeContext *ctxt = canvas->rootContext();
849     ctxt->setContextProperty("testModel", &model);
850     ctxt->setContextProperty("testRightToLeft", QVariant(false));
851     ctxt->setContextProperty("testTopToBottom", QVariant(false));
852     canvas->setSource(testFileUrl("gridview1.qml"));
853
854     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
855     QTRY_VERIFY(gridview != 0);
856     QQuickItem *contentItem = gridview->contentItem();
857     QTRY_VERIFY(contentItem != 0);
858
859     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 0);
860     QTRY_COMPARE(name->text(), QString("Item0"));
861
862     gridview->setCurrentIndex(0);
863     qApp->processEvents();
864
865     // scroll down until item 0 is no longer drawn
866     // (bug not triggered if we just move using content y, since that doesn't
867     // refill and change the visible items)
868     gridview->setCurrentIndex(24);
869     qApp->processEvents();
870
871     QTRY_COMPARE(gridview->currentIndex(), 24);
872     QTRY_COMPARE(gridview->contentY(), 220.0);
873
874     QTest::qWait(100);  // wait for refill to complete
875     QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 0));  // 0 shouldn't be visible
876
877     if (doAdd) {
878         model.insertItem(0, "New Item", "New Item number");
879         QTRY_COMPARE(gridview->count(), 31);
880     } else {
881         model.removeItem(0);
882         QTRY_COMPARE(gridview->count(), 29);
883     }
884
885     // scroll back up and item 0 should be gone
886     gridview->setCurrentIndex(0);
887     qApp->processEvents();
888     QTRY_COMPARE(gridview->currentIndex(), 0);
889     QTRY_COMPARE(gridview->contentY(), newTopContentY);
890
891     name = findItem<QQuickText>(contentItem, "textName", 0);
892     if (doAdd)
893         QCOMPARE(name->text(), QString("New Item"));
894     else
895         QCOMPARE(name->text(), QString("Item1"));
896
897     // Confirm items positioned correctly
898     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
899     for (int i = 0; i < model.count() && i < itemCount; ++i) {
900         QTRY_VERIFY(findItem<QQuickItem>(contentItem, "wrapper", i));
901         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
902         QTRY_VERIFY(item->x() == (i%3)*80);
903         QTRY_VERIFY(item->y() == (i/3)*60 + newTopContentY);
904     }
905
906     delete canvas;
907 }
908
909 void tst_QQuickGridView::addOrRemoveBeforeVisible_data()
910 {
911     QTest::addColumn<bool>("doAdd");
912     QTest::addColumn<qreal>("newTopContentY");
913
914     QTest::newRow("add") << true << -60.0;
915     QTest::newRow("remove") << false << 0.0;
916 }
917
918 void tst_QQuickGridView::clear()
919 {
920     QQuickView *canvas = createView();
921
922     TestModel model;
923     for (int i = 0; i < 30; i++)
924         model.addItem("Item" + QString::number(i), "");
925
926     QDeclarativeContext *ctxt = canvas->rootContext();
927     ctxt->setContextProperty("testModel", &model);
928     ctxt->setContextProperty("testRightToLeft", QVariant(false));
929     ctxt->setContextProperty("testTopToBottom", QVariant(false));
930
931     canvas->setSource(testFileUrl("gridview1.qml"));
932     qApp->processEvents();
933
934     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
935     QVERIFY(gridview != 0);
936
937     QQuickItem *contentItem = gridview->contentItem();
938     QVERIFY(contentItem != 0);
939
940     model.clear();
941
942     QVERIFY(gridview->count() == 0);
943     QVERIFY(gridview->currentItem() == 0);
944     QVERIFY(gridview->contentY() == 0);
945     QVERIFY(gridview->currentIndex() == -1);
946
947     // confirm sanity when adding an item to cleared list
948     model.addItem("New", "1");
949     QTRY_COMPARE(gridview->count(), 1);
950     QVERIFY(gridview->currentItem() != 0);
951     QVERIFY(gridview->currentIndex() == 0);
952
953     delete canvas;
954 }
955
956 void tst_QQuickGridView::moved()
957 {
958     QFETCH(qreal, contentY);
959     QFETCH(int, from);
960     QFETCH(int, to);
961     QFETCH(int, count);
962     QFETCH(qreal, itemsOffsetAfterMove);
963
964     QQuickText *name;
965     QQuickText *number;
966     QScopedPointer<QQuickView> canvas(createView());
967     canvas->show();
968
969     TestModel model;
970     for (int i = 0; i < 30; i++)
971         model.addItem("Item" + QString::number(i), "");
972
973     QDeclarativeContext *ctxt = canvas->rootContext();
974     ctxt->setContextProperty("testModel", &model);
975     ctxt->setContextProperty("testRightToLeft", QVariant(false));
976     ctxt->setContextProperty("testTopToBottom", QVariant(false));
977
978     canvas->setSource(testFileUrl("gridview1.qml"));
979     qApp->processEvents();
980
981     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
982     QTRY_VERIFY(gridview != 0);
983
984     QQuickItem *contentItem = gridview->contentItem();
985     QTRY_VERIFY(contentItem != 0);
986
987     QQuickItem *currentItem = gridview->currentItem();
988     QTRY_VERIFY(currentItem != 0);
989
990     gridview->setContentY(contentY);
991     model.moveItems(from, to, count);
992
993     // wait for items to move
994     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
995
996     // Confirm items positioned correctly and indexes correct
997     int firstVisibleIndex = qCeil(contentY / 60.0) * 3;
998     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
999     for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
1000         if (i >= firstVisibleIndex + 18)    // index has moved out of view
1001             continue;
1002         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1003         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1004
1005         QTRY_COMPARE(item->x(), (i%3)*80.0);
1006         QTRY_COMPARE(item->y(), (i/3)*60.0 + itemsOffsetAfterMove);
1007
1008         name = findItem<QQuickText>(contentItem, "textName", i);
1009         QVERIFY(name != 0);
1010         QTRY_COMPARE(name->text(), model.name(i));
1011         number = findItem<QQuickText>(contentItem, "textNumber", i);
1012         QVERIFY(number != 0);
1013         QTRY_COMPARE(number->text(), model.number(i));
1014
1015         // current index should have been updated
1016         if (item == currentItem)
1017             QTRY_COMPARE(gridview->currentIndex(), i);
1018     }
1019 }
1020
1021 void tst_QQuickGridView::moved_data()
1022 {
1023     QTest::addColumn<qreal>("contentY");
1024     QTest::addColumn<int>("from");
1025     QTest::addColumn<int>("to");
1026     QTest::addColumn<int>("count");
1027     QTest::addColumn<qreal>("itemsOffsetAfterMove");
1028
1029     // model starts with 30 items, each 80x60, in area 240x320
1030     // 18 items should be visible at a time
1031
1032     QTest::newRow("move 1 forwards, within visible items")
1033             << 0.0
1034             << 1 << 8 << 1
1035             << 0.0;
1036
1037     QTest::newRow("move 1 forwards, from non-visible -> visible")
1038             << 120.0     // show 6-23
1039             << 1 << 23 << 1
1040             << 0.0;     // only 1 item was removed from the 1st row, so it doesn't move down
1041
1042     QTest::newRow("move 1 forwards, from non-visible -> visible (move first item)")
1043             << 120.0     // // show 6-23
1044             << 0 << 6 << 1
1045             << 0.0;     // only 1 item was removed from the 1st row, so it doesn't move down
1046
1047     QTest::newRow("move 1 forwards, from visible -> non-visible")
1048             << 0.0
1049             << 1 << 20 << 1
1050             << 0.0;
1051
1052     QTest::newRow("move 1 forwards, from visible -> non-visible (move first item)")
1053             << 0.0
1054             << 0 << 20 << 1
1055             << 0.0;
1056
1057
1058     QTest::newRow("move 1 backwards, within visible items")
1059             << 0.0
1060             << 10 << 5 << 1
1061             << 0.0;
1062
1063     QTest::newRow("move 1 backwards, within visible items (to first index)")
1064             << 0.0
1065             << 10 << 0 << 1
1066             << 0.0;
1067
1068     QTest::newRow("move 1 backwards, from non-visible -> visible")
1069             << 0.0
1070             << 28 << 8 << 1
1071             << 0.0;
1072
1073     QTest::newRow("move 1 backwards, from non-visible -> visible (move last item)")
1074             << 0.0
1075             << 29 << 14 << 1
1076             << 0.0;
1077
1078     QTest::newRow("move 1 backwards, from visible -> non-visible")
1079             << 120.0     // show 6-23
1080             << 7 << 1 << 1
1081             << 0.0;     // only 1 item moved back, so items shift accordingly and first row doesn't move
1082
1083     QTest::newRow("move 1 backwards, from visible -> non-visible (move first item)")
1084             << 120.0     // show 6-23
1085             << 7 << 0 << 1
1086             << 0.0;     // only 1 item moved back, so items shift accordingly and first row doesn't move
1087
1088
1089     QTest::newRow("move multiple forwards, within visible items")
1090             << 0.0
1091             << 0 << 5 << 3
1092             << 0.0;
1093
1094     QTest::newRow("move multiple backwards, within visible items (move first item)")
1095             << 0.0
1096             << 10 << 0 << 3
1097             << 0.0;
1098
1099     QTest::newRow("move multiple forwards, before visible items")
1100             << 120.0     // show 6-23
1101             << 3 << 4 << 3      // 3, 4, 5 move to after 6
1102             << 60.0;      // row of 3,4,5 has moved down
1103
1104     QTest::newRow("move multiple forwards, from non-visible -> visible")
1105             << 120.0     // show 6-23
1106             << 1 << 6 << 3
1107             << 60.0; // 1st row (it's above visible area) disappears, 0 drops down 1 row, first visible item (6) stays where it is
1108
1109     QTest::newRow("move multiple forwards, from non-visible -> visible (move first item)")
1110             << 120.0     // show 6-23
1111             << 0 << 6 << 3
1112             << 60.0;    // top row moved and shifted to below 3rd row, all items should shift down by 1 row
1113
1114     QTest::newRow("move multiple forwards, from visible -> non-visible")
1115             << 0.0
1116             << 1 << 16 << 3
1117             << 0.0;
1118
1119     QTest::newRow("move multiple forwards, from visible -> non-visible (move first item)")
1120             << 0.0
1121             << 0 << 16 << 3
1122             << 0.0;
1123
1124
1125     QTest::newRow("move multiple backwards, within visible items")
1126             << 0.0
1127             << 4 << 1 << 3
1128             << 0.0;
1129
1130     QTest::newRow("move multiple backwards, from non-visible -> visible")
1131             << 0.0
1132             << 20 << 4 << 3
1133             << 0.0;
1134
1135     QTest::newRow("move multiple backwards, from non-visible -> visible (move last item)")
1136             << 0.0
1137             << 27 << 10 << 3
1138             << 0.0;
1139
1140     QTest::newRow("move multiple backwards, from visible -> non-visible")
1141             << 120.0     // show 6-23
1142             << 16 << 1 << 3
1143             << -60.0;   // to minimize movement, items are added above visible area, all items move up by 1 row
1144
1145     QTest::newRow("move multiple backwards, from visible -> non-visible (move first item)")
1146             << 120.0     // show 6-23
1147             << 16 << 0 << 3
1148             << -60.0;   // 16,17,18 move to above item 0, all items move up by 1 row
1149 }
1150
1151 struct ListChange {
1152     enum { Inserted, Removed, Moved, SetCurrent } type;
1153     int index;
1154     int count;
1155     int to;     // Move
1156
1157     static ListChange insert(int index, int count = 1) { ListChange c = { Inserted, index, count, -1 }; return c; }
1158     static ListChange remove(int index, int count = 1) { ListChange c = { Removed, index, count, -1 }; return c; }
1159     static ListChange move(int index, int to, int count) { ListChange c = { Moved, index, count, to }; return c; }
1160     static ListChange setCurrent(int index) { ListChange c = { SetCurrent, index, -1, -1 }; return c; }
1161 };
1162 Q_DECLARE_METATYPE(QList<ListChange>)
1163
1164 void tst_QQuickGridView::multipleChanges()
1165 {
1166     QFETCH(int, startCount);
1167     QFETCH(QList<ListChange>, changes);
1168     QFETCH(int, newCount);
1169     QFETCH(int, newCurrentIndex);
1170
1171     QQuickView *canvas = createView();
1172     canvas->show();
1173
1174     TestModel model;
1175     for (int i = 0; i < startCount; i++)
1176         model.addItem("Item" + QString::number(i), "");
1177
1178     QDeclarativeContext *ctxt = canvas->rootContext();
1179     ctxt->setContextProperty("testModel", &model);
1180     ctxt->setContextProperty("testRightToLeft", QVariant(false));
1181     ctxt->setContextProperty("testTopToBottom", QVariant(false));
1182
1183     canvas->setSource(testFileUrl("gridview1.qml"));
1184     qApp->processEvents();
1185
1186     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1187     QTRY_VERIFY(gridview != 0);
1188
1189     for (int i=0; i<changes.count(); i++) {
1190         switch (changes[i].type) {
1191             case ListChange::Inserted:
1192             {
1193                 QList<QPair<QString, QString> > items;
1194                 for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
1195                     items << qMakePair(QString("new item " + j), QString::number(j));
1196                 model.insertItems(changes[i].index, items);
1197                 break;
1198             }
1199             case ListChange::Removed:
1200                 model.removeItems(changes[i].index, changes[i].count);
1201                 break;
1202             case ListChange::Moved:
1203                 model.moveItems(changes[i].index, changes[i].to, changes[i].count);
1204                 break;
1205             case ListChange::SetCurrent:
1206                 gridview->setCurrentIndex(changes[i].index);
1207                 break;
1208         }
1209     }
1210
1211     QTRY_COMPARE(gridview->count(), newCount);
1212     QCOMPARE(gridview->count(), model.count());
1213     QTRY_COMPARE(gridview->currentIndex(), newCurrentIndex);
1214
1215     QQuickText *name;
1216     QQuickText *number;
1217     QQuickItem *contentItem = gridview->contentItem();
1218     QTRY_VERIFY(contentItem != 0);
1219     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1220     for (int i=0; i < model.count() && i < itemCount; ++i) {
1221         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1222         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1223         name = findItem<QQuickText>(contentItem, "textName", i);
1224         QVERIFY(name != 0);
1225         QTRY_COMPARE(name->text(), model.name(i));
1226         number = findItem<QQuickText>(contentItem, "textNumber", i);
1227         QVERIFY(number != 0);
1228         QTRY_COMPARE(number->text(), model.number(i));
1229     }
1230
1231     delete canvas;
1232 }
1233
1234 void tst_QQuickGridView::multipleChanges_data()
1235 {
1236     QTest::addColumn<int>("startCount");
1237     QTest::addColumn<QList<ListChange> >("changes");
1238     QTest::addColumn<int>("newCount");
1239     QTest::addColumn<int>("newCurrentIndex");
1240
1241     QList<ListChange> changes;
1242
1243     for (int i=1; i<30; i++)
1244         changes << ListChange::remove(0);
1245     QTest::newRow("remove all but 1, first->last") << 30 << changes << 1 << 0;
1246
1247     changes << ListChange::remove(0);
1248     QTest::newRow("remove all") << 30 << changes << 0 << -1;
1249
1250     changes.clear();
1251     changes << ListChange::setCurrent(29);
1252     for (int i=29; i>0; i--)
1253         changes << ListChange::remove(i);
1254     QTest::newRow("remove last (current) -> first") << 30 << changes << 1 << 0;
1255
1256     QTest::newRow("remove then insert at 0") << 10 << (QList<ListChange>()
1257             << ListChange::remove(0, 1)
1258             << ListChange::insert(0, 1)
1259             ) << 10 << 1;
1260
1261     QTest::newRow("remove then insert at non-zero index") << 10 << (QList<ListChange>()
1262             << ListChange::setCurrent(2)
1263             << ListChange::remove(2, 1)
1264             << ListChange::insert(2, 1)
1265             ) << 10 << 3;
1266
1267     QTest::newRow("remove current then insert below it") << 10 << (QList<ListChange>()
1268             << ListChange::setCurrent(1)
1269             << ListChange::remove(1, 3)
1270             << ListChange::insert(2, 2)
1271             ) << 9 << 1;
1272
1273     QTest::newRow("remove current index then move it down") << 10 << (QList<ListChange>()
1274             << ListChange::setCurrent(2)
1275             << ListChange::remove(1, 3)
1276             << ListChange::move(1, 5, 1)
1277             ) << 7 << 5;
1278
1279     QTest::newRow("remove current index then move it up") << 10 << (QList<ListChange>()
1280             << ListChange::setCurrent(5)
1281             << ListChange::remove(4, 3)
1282             << ListChange::move(4, 1, 1)
1283             ) << 7 << 1;
1284
1285
1286     QTest::newRow("insert multiple times") << 0 << (QList<ListChange>()
1287             << ListChange::insert(0, 2)
1288             << ListChange::insert(0, 4)
1289             << ListChange::insert(0, 6)
1290             ) << 12 << 10;
1291
1292     QTest::newRow("insert multiple times with current index changes") << 0 << (QList<ListChange>()
1293             << ListChange::insert(0, 2)
1294             << ListChange::insert(0, 4)
1295             << ListChange::insert(0, 6)
1296             << ListChange::setCurrent(3)
1297             << ListChange::insert(3, 2)
1298             ) << 14 << 5;
1299
1300     QTest::newRow("insert and remove all") << 0 << (QList<ListChange>()
1301             << ListChange::insert(0, 30)
1302             << ListChange::remove(0, 30)
1303             ) << 0 << -1;
1304
1305     QTest::newRow("insert and remove current") << 30 << (QList<ListChange>()
1306             << ListChange::insert(1)
1307             << ListChange::setCurrent(1)
1308             << ListChange::remove(1)
1309             ) << 30 << 1;
1310
1311     QTest::newRow("insert before 0, then remove cross section of new and old items") << 10 << (QList<ListChange>()
1312             << ListChange::insert(0, 10)
1313             << ListChange::remove(5, 10)
1314             ) << 10 << 5;
1315
1316     QTest::newRow("insert multiple, then move new items to end") << 10 << (QList<ListChange>()
1317             << ListChange::insert(0, 3)
1318             << ListChange::move(0, 10, 3)
1319             ) << 13 << 0;
1320
1321     QTest::newRow("insert multiple, then move new and some old items to end") << 10 << (QList<ListChange>()
1322             << ListChange::insert(0, 3)
1323             << ListChange::move(0, 8, 5)
1324             ) << 13 << 11;
1325
1326     QTest::newRow("insert multiple at end, then move new and some old items to start") << 10 << (QList<ListChange>()
1327             << ListChange::setCurrent(9)
1328             << ListChange::insert(10, 3)
1329             << ListChange::move(8, 0, 5)
1330             ) << 13 << 1;
1331
1332
1333     QTest::newRow("move back and forth to same index") << 10 << (QList<ListChange>()
1334             << ListChange::setCurrent(1)
1335             << ListChange::move(1, 2, 2)
1336             << ListChange::move(2, 1, 2)
1337             ) << 10 << 1;
1338
1339     QTest::newRow("move forwards then back") << 10 << (QList<ListChange>()
1340             << ListChange::setCurrent(2)
1341             << ListChange::move(1, 2, 3)
1342             << ListChange::move(3, 0, 5)
1343             ) << 10 << 0;
1344
1345     QTest::newRow("move current, then remove it") << 10 << (QList<ListChange>()
1346             << ListChange::setCurrent(5)
1347             << ListChange::move(5, 0, 1)
1348             << ListChange::remove(0)
1349             ) << 9 << 0;
1350
1351     QTest::newRow("move current, then insert before it") << 10 << (QList<ListChange>()
1352             << ListChange::setCurrent(5)
1353             << ListChange::move(5, 0, 1)
1354             << ListChange::insert(0)
1355             ) << 11 << 1;
1356
1357     QTest::newRow("move multiple, then remove them") << 10 << (QList<ListChange>()
1358             << ListChange::setCurrent(1)
1359             << ListChange::move(5, 1, 3)
1360             << ListChange::remove(1, 3)
1361             ) << 7 << 1;
1362
1363     QTest::newRow("move multiple, then insert before them") << 10 << (QList<ListChange>()
1364             << ListChange::setCurrent(5)
1365             << ListChange::move(5, 1, 3)
1366             << ListChange::insert(1, 5)
1367             ) << 15 << 6;
1368
1369     QTest::newRow("move multiple, then insert after them") << 10 << (QList<ListChange>()
1370             << ListChange::setCurrent(3)
1371             << ListChange::move(0, 1, 2)
1372             << ListChange::insert(3, 5)
1373             ) << 15 << 8;
1374
1375
1376     QTest::newRow("clear current") << 0 << (QList<ListChange>()
1377             << ListChange::insert(0, 5)
1378             << ListChange::setCurrent(-1)
1379             << ListChange::remove(0, 5)
1380             << ListChange::insert(0, 5)
1381             ) << 5 << -1;
1382 }
1383
1384
1385 void tst_QQuickGridView::swapWithFirstItem()
1386 {
1387     // QTBUG_9697
1388     QQuickView *canvas = createView();
1389     canvas->show();
1390
1391     TestModel model;
1392     for (int i = 0; i < 30; i++)
1393         model.addItem("Item" + QString::number(i), "");
1394
1395     QDeclarativeContext *ctxt = canvas->rootContext();
1396     ctxt->setContextProperty("testModel", &model);
1397     ctxt->setContextProperty("testRightToLeft", QVariant(false));
1398     ctxt->setContextProperty("testTopToBottom", QVariant(false));
1399
1400     canvas->setSource(testFileUrl("gridview1.qml"));
1401     qApp->processEvents();
1402
1403     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1404     QTRY_VERIFY(gridview != 0);
1405
1406     // ensure content position is stable
1407     gridview->setContentY(0);
1408     model.moveItem(10, 0);
1409     QTRY_VERIFY(gridview->contentY() == 0);
1410
1411     delete canvas;
1412 }
1413
1414 void tst_QQuickGridView::currentIndex()
1415 {
1416     TestModel model;
1417     for (int i = 0; i < 60; i++)
1418         model.addItem("Item" + QString::number(i), QString::number(i));
1419
1420     QQuickView *canvas = new QQuickView(0);
1421     canvas->setGeometry(0,0,240,320);
1422     canvas->show();
1423
1424     QDeclarativeContext *ctxt = canvas->rootContext();
1425     ctxt->setContextProperty("testModel", &model);
1426
1427     QString filename(testFile("gridview-initCurrent.qml"));
1428     canvas->setSource(QUrl::fromLocalFile(filename));
1429
1430     qApp->processEvents();
1431
1432     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1433     QVERIFY(gridview != 0);
1434     QTRY_VERIFY(!QQuickItemPrivate::get(gridview)->polishScheduled);
1435
1436     QQuickItem *contentItem = gridview->contentItem();
1437     QVERIFY(contentItem != 0);
1438
1439     // current item should be third item
1440     QCOMPARE(gridview->currentIndex(), 35);
1441     QCOMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 35));
1442     QCOMPARE(gridview->currentItem()->y(), gridview->highlightItem()->y());
1443     QCOMPARE(gridview->contentY(), 400.0);
1444
1445     gridview->moveCurrentIndexRight();
1446     QCOMPARE(gridview->currentIndex(), 36);
1447     gridview->moveCurrentIndexDown();
1448     QCOMPARE(gridview->currentIndex(), 39);
1449     gridview->moveCurrentIndexUp();
1450     QCOMPARE(gridview->currentIndex(), 36);
1451     gridview->moveCurrentIndexLeft();
1452     QCOMPARE(gridview->currentIndex(), 35);
1453
1454     // wait until motion stops
1455     QTRY_VERIFY(gridview->verticalVelocity() == 0.0);
1456
1457     // no wrap
1458     gridview->setCurrentIndex(0);
1459     QCOMPARE(gridview->currentIndex(), 0);
1460     // confirm that the velocity is updated
1461     QTRY_VERIFY(gridview->verticalVelocity() != 0.0);
1462
1463     gridview->moveCurrentIndexUp();
1464     QCOMPARE(gridview->currentIndex(), 0);
1465
1466     gridview->moveCurrentIndexLeft();
1467     QCOMPARE(gridview->currentIndex(), 0);
1468
1469     gridview->setCurrentIndex(model.count()-1);
1470     QCOMPARE(gridview->currentIndex(), model.count()-1);
1471
1472     gridview->moveCurrentIndexRight();
1473     QCOMPARE(gridview->currentIndex(), model.count()-1);
1474
1475     gridview->moveCurrentIndexDown();
1476     QCOMPARE(gridview->currentIndex(), model.count()-1);
1477
1478     // with wrap
1479     gridview->setWrapEnabled(true);
1480
1481     gridview->setCurrentIndex(0);
1482     QCOMPARE(gridview->currentIndex(), 0);
1483
1484     gridview->moveCurrentIndexLeft();
1485     QCOMPARE(gridview->currentIndex(), model.count()-1);
1486
1487     qApp->processEvents();
1488     QTRY_COMPARE(gridview->contentY(), 880.0);
1489
1490     gridview->moveCurrentIndexRight();
1491     QCOMPARE(gridview->currentIndex(), 0);
1492
1493     QTRY_COMPARE(gridview->contentY(), 0.0);
1494
1495
1496     // footer should become visible if it is out of view, and then current index moves to the first row
1497     canvas->rootObject()->setProperty("showFooter", true);
1498     QTRY_VERIFY(gridview->footerItem());
1499     gridview->setCurrentIndex(model.count()-3);
1500     QTRY_VERIFY(gridview->footerItem()->y() > gridview->contentY() + gridview->height());
1501     gridview->setCurrentIndex(model.count()-2);
1502     QTRY_COMPARE(gridview->contentY() + gridview->height(), (60.0 * model.count()/3) + gridview->footerItem()->height());
1503     canvas->rootObject()->setProperty("showFooter", false);
1504
1505     // header should become visible if it is out of view, and then current index moves to the last row
1506     canvas->rootObject()->setProperty("showHeader", true);
1507     QTRY_VERIFY(gridview->headerItem());
1508     gridview->setCurrentIndex(3);
1509     QTRY_VERIFY(gridview->headerItem()->y() + gridview->headerItem()->height() < gridview->contentY());
1510     gridview->setCurrentIndex(1);
1511     QTRY_COMPARE(gridview->contentY(), -gridview->headerItem()->height());
1512     canvas->rootObject()->setProperty("showHeader", false);
1513
1514
1515     // Test keys
1516     canvas->requestActivateWindow();
1517     QTest::qWaitForWindowShown(canvas);
1518     QTRY_VERIFY(qGuiApp->focusWindow() == canvas);
1519
1520     gridview->setCurrentIndex(0);
1521
1522     QTest::keyClick(canvas, Qt::Key_Down);
1523     QCOMPARE(gridview->currentIndex(), 3);
1524
1525     QTest::keyClick(canvas, Qt::Key_Up);
1526     QCOMPARE(gridview->currentIndex(), 0);
1527
1528     // hold down Key_Down
1529     for (int i=0; i<(model.count() / 3) - 1; i++) {
1530         QTest::simulateEvent(canvas, true, Qt::Key_Down, Qt::NoModifier, "", true);
1531         QTRY_COMPARE(gridview->currentIndex(), i*3 + 3);
1532     }
1533     QTest::keyRelease(canvas, Qt::Key_Down);
1534     QTRY_COMPARE(gridview->currentIndex(), 57);
1535     QTRY_COMPARE(gridview->contentY(), 880.0);
1536
1537     // hold down Key_Up
1538     for (int i=(model.count() / 3) - 1; i > 0; i--) {
1539         QTest::simulateEvent(canvas, true, Qt::Key_Up, Qt::NoModifier, "", true);
1540         QTRY_COMPARE(gridview->currentIndex(), i*3 - 3);
1541     }
1542     QTest::keyRelease(canvas, Qt::Key_Up);
1543     QTRY_COMPARE(gridview->currentIndex(), 0);
1544     QTRY_COMPARE(gridview->contentY(), 0.0);
1545
1546
1547     gridview->setFlow(QQuickGridView::TopToBottom);
1548
1549     canvas->requestActivateWindow();
1550     QTest::qWaitForWindowShown(canvas);
1551     QVERIFY(qGuiApp->focusWindow() == canvas);
1552     qApp->processEvents();
1553
1554     QTest::keyClick(canvas, Qt::Key_Right);
1555     QCOMPARE(gridview->currentIndex(), 5);
1556
1557     QTest::keyClick(canvas, Qt::Key_Left);
1558     QCOMPARE(gridview->currentIndex(), 0);
1559
1560     QTest::keyClick(canvas, Qt::Key_Down);
1561     QCOMPARE(gridview->currentIndex(), 1);
1562
1563     QTest::keyClick(canvas, Qt::Key_Up);
1564     QCOMPARE(gridview->currentIndex(), 0);
1565
1566     // hold down Key_Right
1567     for (int i=0; i<(model.count() / 5) - 1; i++) {
1568         QTest::simulateEvent(canvas, true, Qt::Key_Right, Qt::NoModifier, "", true);
1569         QTRY_COMPARE(gridview->currentIndex(), i*5 + 5);
1570     }
1571
1572     QTest::keyRelease(canvas, Qt::Key_Right);
1573     QTRY_COMPARE(gridview->currentIndex(), 55);
1574     QTRY_COMPARE(gridview->contentX(), 720.0);
1575
1576     // hold down Key_Left
1577     for (int i=(model.count() / 5) - 1; i > 0; i--) {
1578         QTest::simulateEvent(canvas, true, Qt::Key_Left, Qt::NoModifier, "", true);
1579         QTRY_COMPARE(gridview->currentIndex(), i*5 - 5);
1580     }
1581     QTest::keyRelease(canvas, Qt::Key_Left);
1582     QTRY_COMPARE(gridview->currentIndex(), 0);
1583     QTRY_COMPARE(gridview->contentX(), 0.0);
1584
1585
1586     // turn off auto highlight
1587     gridview->setHighlightFollowsCurrentItem(false);
1588     QVERIFY(gridview->highlightFollowsCurrentItem() == false);
1589     QVERIFY(gridview->highlightItem());
1590     qreal hlPosX = gridview->highlightItem()->x();
1591     qreal hlPosY = gridview->highlightItem()->y();
1592
1593     gridview->setCurrentIndex(5);
1594     QTRY_COMPARE(gridview->highlightItem()->x(), hlPosX);
1595     QTRY_COMPARE(gridview->highlightItem()->y(), hlPosY);
1596
1597     // insert item before currentIndex
1598     gridview->setCurrentIndex(28);
1599     model.insertItem(0, "Foo", "1111");
1600     QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29);
1601
1602     // check removing highlight by setting currentIndex to -1;
1603     gridview->setCurrentIndex(-1);
1604
1605     QCOMPARE(gridview->currentIndex(), -1);
1606     QVERIFY(!gridview->highlightItem());
1607     QVERIFY(!gridview->currentItem());
1608
1609     gridview->setHighlightFollowsCurrentItem(true);
1610
1611     gridview->setFlow(QQuickGridView::LeftToRight);
1612     gridview->setLayoutDirection(Qt::RightToLeft);
1613
1614     canvas->requestActivateWindow();
1615     QTest::qWaitForWindowShown(canvas);
1616     QTRY_VERIFY(qGuiApp->focusWindow() == canvas);
1617     qApp->processEvents();
1618
1619     gridview->setCurrentIndex(35);
1620
1621     QTest::keyClick(canvas, Qt::Key_Right);
1622     QCOMPARE(gridview->currentIndex(), 34);
1623
1624     QTest::keyClick(canvas, Qt::Key_Down);
1625     QCOMPARE(gridview->currentIndex(), 37);
1626
1627     QTest::keyClick(canvas, Qt::Key_Up);
1628     QCOMPARE(gridview->currentIndex(), 34);
1629
1630     QTest::keyClick(canvas, Qt::Key_Left);
1631     QCOMPARE(gridview->currentIndex(), 35);
1632
1633
1634     // turn off auto highlight
1635     gridview->setHighlightFollowsCurrentItem(false);
1636     QVERIFY(gridview->highlightFollowsCurrentItem() == false);
1637     QVERIFY(gridview->highlightItem());
1638     hlPosX = gridview->highlightItem()->x();
1639     hlPosY = gridview->highlightItem()->y();
1640
1641     gridview->setCurrentIndex(5);
1642     QTRY_COMPARE(gridview->highlightItem()->x(), hlPosX);
1643     QTRY_COMPARE(gridview->highlightItem()->y(), hlPosY);
1644
1645     // insert item before currentIndex
1646     gridview->setCurrentIndex(28);
1647     model.insertItem(0, "Foo", "1111");
1648     QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29);
1649
1650     // check removing highlight by setting currentIndex to -1;
1651     gridview->setCurrentIndex(-1);
1652
1653     QCOMPARE(gridview->currentIndex(), -1);
1654     QVERIFY(!gridview->highlightItem());
1655     QVERIFY(!gridview->currentItem());
1656
1657     delete canvas;
1658 }
1659
1660 void tst_QQuickGridView::noCurrentIndex()
1661 {
1662     TestModel model;
1663     for (int i = 0; i < 60; i++)
1664         model.addItem("Item" + QString::number(i), QString::number(i));
1665
1666     QQuickView *canvas = new QQuickView(0);
1667     canvas->setGeometry(0,0,240,320);
1668
1669     QDeclarativeContext *ctxt = canvas->rootContext();
1670     ctxt->setContextProperty("testModel", &model);
1671
1672     QString filename(testFile("gridview-noCurrent.qml"));
1673     canvas->setSource(QUrl::fromLocalFile(filename));
1674
1675     qApp->processEvents();
1676
1677     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1678     QVERIFY(gridview != 0);
1679
1680     QQuickItem *contentItem = gridview->contentItem();
1681     QVERIFY(contentItem != 0);
1682
1683     // current index should be -1
1684     QCOMPARE(gridview->currentIndex(), -1);
1685     QVERIFY(!gridview->currentItem());
1686     QVERIFY(!gridview->highlightItem());
1687     QCOMPARE(gridview->contentY(), 0.0);
1688
1689     gridview->setCurrentIndex(5);
1690     QCOMPARE(gridview->currentIndex(), 5);
1691     QVERIFY(gridview->currentItem());
1692     QVERIFY(gridview->highlightItem());
1693
1694     delete canvas;
1695 }
1696
1697 void tst_QQuickGridView::changeFlow()
1698 {
1699     QQuickView *canvas = createView();
1700
1701     TestModel model;
1702     for (int i = 0; i < 30; i++)
1703         model.addItem("Item" + QString::number(i), QString::number(i));
1704
1705     QDeclarativeContext *ctxt = canvas->rootContext();
1706     ctxt->setContextProperty("testModel", &model);
1707     ctxt->setContextProperty("testRightToLeft", QVariant(false));
1708     ctxt->setContextProperty("testTopToBottom", QVariant(false));
1709
1710     canvas->setSource(testFileUrl("gridview1.qml"));
1711     qApp->processEvents();
1712
1713     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1714     QTRY_VERIFY(gridview != 0);
1715
1716     QQuickItem *contentItem = gridview->contentItem();
1717     QTRY_VERIFY(contentItem != 0);
1718
1719     // Confirm items positioned correctly and indexes correct
1720     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1721     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1722         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1723         if (!item) qWarning() << "Item" << i << "not found";
1724         QTRY_VERIFY(item);
1725         QTRY_COMPARE(item->x(), qreal((i%3)*80));
1726         QTRY_COMPARE(item->y(), qreal((i/3)*60));
1727         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
1728         QTRY_VERIFY(name != 0);
1729         QTRY_COMPARE(name->text(), model.name(i));
1730         QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
1731         QTRY_VERIFY(number != 0);
1732         QTRY_COMPARE(number->text(), model.number(i));
1733     }
1734
1735     ctxt->setContextProperty("testTopToBottom", QVariant(true));
1736
1737     // Confirm items positioned correctly and indexes correct
1738     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1739     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1740         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1741         if (!item) qWarning() << "Item" << i << "not found";
1742         QTRY_VERIFY(item);
1743         QTRY_COMPARE(item->x(), qreal((i/5)*80));
1744         QTRY_COMPARE(item->y(), qreal((i%5)*60));
1745         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
1746         QTRY_VERIFY(name != 0);
1747         QTRY_COMPARE(name->text(), model.name(i));
1748         QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
1749         QTRY_VERIFY(number != 0);
1750         QTRY_COMPARE(number->text(), model.number(i));
1751     }
1752
1753     ctxt->setContextProperty("testRightToLeft", QVariant(true));
1754
1755     // Confirm items positioned correctly and indexes correct
1756     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1757     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1758         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1759         if (!item) qWarning() << "Item" << i << "not found";
1760         QTRY_VERIFY(item);
1761         QTRY_COMPARE(item->x(), qreal(-(i/5)*80 - item->width()));
1762         QTRY_COMPARE(item->y(), qreal((i%5)*60));
1763         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
1764         QTRY_VERIFY(name != 0);
1765         QTRY_COMPARE(name->text(), model.name(i));
1766         QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
1767         QTRY_VERIFY(number != 0);
1768         QTRY_COMPARE(number->text(), model.number(i));
1769     }
1770     gridview->setContentX(100);
1771     QTRY_COMPARE(gridview->contentX(), 100.);
1772     ctxt->setContextProperty("testTopToBottom", QVariant(false));
1773     QTRY_COMPARE(gridview->contentX(), 0.);
1774
1775     // Confirm items positioned correctly and indexes correct
1776     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1777     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1778         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1779         if (!item) qWarning() << "Item" << i << "not found";
1780         QTRY_VERIFY(item);
1781         QTRY_COMPARE(item->x(), qreal(240 - (i%3+1)*80));
1782         QTRY_COMPARE(item->y(), qreal((i/3)*60));
1783         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
1784         QTRY_VERIFY(name != 0);
1785         QTRY_COMPARE(name->text(), model.name(i));
1786         QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
1787         QTRY_VERIFY(number != 0);
1788         QTRY_COMPARE(number->text(), model.number(i));
1789     }
1790
1791     delete canvas;
1792 }
1793
1794 void tst_QQuickGridView::defaultValues()
1795 {
1796     QDeclarativeEngine engine;
1797     QDeclarativeComponent c(&engine, testFileUrl("gridview3.qml"));
1798     QQuickGridView *obj = qobject_cast<QQuickGridView*>(c.create());
1799
1800     QTRY_VERIFY(obj != 0);
1801     QTRY_VERIFY(obj->model() == QVariant());
1802     QTRY_VERIFY(obj->delegate() == 0);
1803     QTRY_COMPARE(obj->currentIndex(), -1);
1804     QTRY_VERIFY(obj->currentItem() == 0);
1805     QTRY_COMPARE(obj->count(), 0);
1806     QTRY_VERIFY(obj->highlight() == 0);
1807     QTRY_VERIFY(obj->highlightItem() == 0);
1808     QTRY_COMPARE(obj->highlightFollowsCurrentItem(), true);
1809     QTRY_VERIFY(obj->flow() == 0);
1810     QTRY_COMPARE(obj->isWrapEnabled(), false);
1811     QTRY_COMPARE(obj->cacheBuffer(), 0);
1812     QTRY_COMPARE(obj->cellWidth(), qreal(100)); //### Should 100 be the default?
1813     QTRY_COMPARE(obj->cellHeight(), qreal(100));
1814     delete obj;
1815 }
1816
1817 void tst_QQuickGridView::properties()
1818 {
1819     QDeclarativeEngine engine;
1820     QDeclarativeComponent c(&engine, testFileUrl("gridview2.qml"));
1821     QQuickGridView *obj = qobject_cast<QQuickGridView*>(c.create());
1822
1823     QTRY_VERIFY(obj != 0);
1824     QTRY_VERIFY(obj->model() != QVariant());
1825     QTRY_VERIFY(obj->delegate() != 0);
1826     QTRY_COMPARE(obj->currentIndex(), 0);
1827     QTRY_VERIFY(obj->currentItem() != 0);
1828     QTRY_COMPARE(obj->count(), 4);
1829     QTRY_VERIFY(obj->highlight() != 0);
1830     QTRY_VERIFY(obj->highlightItem() != 0);
1831     QTRY_COMPARE(obj->highlightFollowsCurrentItem(), false);
1832     QTRY_VERIFY(obj->flow() == 0);
1833     QTRY_COMPARE(obj->isWrapEnabled(), true);
1834     QTRY_COMPARE(obj->cacheBuffer(), 200);
1835     QTRY_COMPARE(obj->cellWidth(), qreal(100));
1836     QTRY_COMPARE(obj->cellHeight(), qreal(100));
1837     delete obj;
1838 }
1839
1840 void tst_QQuickGridView::propertyChanges()
1841 {
1842     QQuickView *canvas = createView();
1843     QTRY_VERIFY(canvas);
1844     canvas->setSource(testFileUrl("propertychangestest.qml"));
1845
1846     QQuickGridView *gridView = canvas->rootObject()->findChild<QQuickGridView*>("gridView");
1847     QTRY_VERIFY(gridView);
1848
1849     QSignalSpy keyNavigationWrapsSpy(gridView, SIGNAL(keyNavigationWrapsChanged()));
1850     QSignalSpy cacheBufferSpy(gridView, SIGNAL(cacheBufferChanged()));
1851     QSignalSpy layoutSpy(gridView, SIGNAL(layoutDirectionChanged()));
1852     QSignalSpy flowSpy(gridView, SIGNAL(flowChanged()));
1853
1854     QTRY_COMPARE(gridView->isWrapEnabled(), true);
1855     QTRY_COMPARE(gridView->cacheBuffer(), 10);
1856     QTRY_COMPARE(gridView->flow(), QQuickGridView::LeftToRight);
1857
1858     gridView->setWrapEnabled(false);
1859     gridView->setCacheBuffer(3);
1860     gridView->setFlow(QQuickGridView::TopToBottom);
1861
1862     QTRY_COMPARE(gridView->isWrapEnabled(), false);
1863     QTRY_COMPARE(gridView->cacheBuffer(), 3);
1864     QTRY_COMPARE(gridView->flow(), QQuickGridView::TopToBottom);
1865
1866     QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
1867     QTRY_COMPARE(cacheBufferSpy.count(),1);
1868     QTRY_COMPARE(flowSpy.count(),1);
1869
1870     gridView->setWrapEnabled(false);
1871     gridView->setCacheBuffer(3);
1872     gridView->setFlow(QQuickGridView::TopToBottom);
1873
1874     QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
1875     QTRY_COMPARE(cacheBufferSpy.count(),1);
1876     QTRY_COMPARE(flowSpy.count(),1);
1877
1878     gridView->setFlow(QQuickGridView::LeftToRight);
1879     QTRY_COMPARE(gridView->flow(), QQuickGridView::LeftToRight);
1880
1881     gridView->setWrapEnabled(true);
1882     gridView->setCacheBuffer(5);
1883     gridView->setLayoutDirection(Qt::RightToLeft);
1884
1885     QTRY_COMPARE(gridView->isWrapEnabled(), true);
1886     QTRY_COMPARE(gridView->cacheBuffer(), 5);
1887     QTRY_COMPARE(gridView->layoutDirection(), Qt::RightToLeft);
1888
1889     QTRY_COMPARE(keyNavigationWrapsSpy.count(),2);
1890     QTRY_COMPARE(cacheBufferSpy.count(),2);
1891     QTRY_COMPARE(layoutSpy.count(),1);
1892     QTRY_COMPARE(flowSpy.count(),2);
1893
1894     gridView->setWrapEnabled(true);
1895     gridView->setCacheBuffer(5);
1896     gridView->setLayoutDirection(Qt::RightToLeft);
1897
1898     QTRY_COMPARE(keyNavigationWrapsSpy.count(),2);
1899     QTRY_COMPARE(cacheBufferSpy.count(),2);
1900     QTRY_COMPARE(layoutSpy.count(),1);
1901     QTRY_COMPARE(flowSpy.count(),2);
1902
1903     gridView->setFlow(QQuickGridView::TopToBottom);
1904     QTRY_COMPARE(gridView->flow(), QQuickGridView::TopToBottom);
1905     QTRY_COMPARE(flowSpy.count(),3);
1906
1907     gridView->setFlow(QQuickGridView::TopToBottom);
1908     QTRY_COMPARE(flowSpy.count(),3);
1909
1910     delete canvas;
1911 }
1912
1913 void tst_QQuickGridView::componentChanges()
1914 {
1915     QQuickView *canvas = createView();
1916     QTRY_VERIFY(canvas);
1917     canvas->setSource(testFileUrl("propertychangestest.qml"));
1918
1919     QQuickGridView *gridView = canvas->rootObject()->findChild<QQuickGridView*>("gridView");
1920     QTRY_VERIFY(gridView);
1921
1922     QDeclarativeComponent component(canvas->engine());
1923     component.setData("import QtQuick 2.0; Rectangle { color: \"blue\"; }", QUrl::fromLocalFile(""));
1924
1925     QDeclarativeComponent delegateComponent(canvas->engine());
1926     delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
1927
1928     QSignalSpy highlightSpy(gridView, SIGNAL(highlightChanged()));
1929     QSignalSpy delegateSpy(gridView, SIGNAL(delegateChanged()));
1930     QSignalSpy headerSpy(gridView, SIGNAL(headerChanged()));
1931     QSignalSpy footerSpy(gridView, SIGNAL(footerChanged()));
1932
1933     gridView->setHighlight(&component);
1934     gridView->setDelegate(&delegateComponent);
1935     gridView->setHeader(&component);
1936     gridView->setFooter(&component);
1937
1938     QTRY_COMPARE(gridView->highlight(), &component);
1939     QTRY_COMPARE(gridView->delegate(), &delegateComponent);
1940     QTRY_COMPARE(gridView->header(), &component);
1941     QTRY_COMPARE(gridView->footer(), &component);
1942
1943     QTRY_COMPARE(highlightSpy.count(),1);
1944     QTRY_COMPARE(delegateSpy.count(),1);
1945     QTRY_COMPARE(headerSpy.count(),1);
1946     QTRY_COMPARE(footerSpy.count(),1);
1947
1948     gridView->setHighlight(&component);
1949     gridView->setDelegate(&delegateComponent);
1950     gridView->setHeader(&component);
1951     gridView->setFooter(&component);
1952
1953     QTRY_COMPARE(highlightSpy.count(),1);
1954     QTRY_COMPARE(delegateSpy.count(),1);
1955     QTRY_COMPARE(headerSpy.count(),1);
1956     QTRY_COMPARE(footerSpy.count(),1);
1957
1958     delete canvas;
1959 }
1960
1961 void tst_QQuickGridView::modelChanges()
1962 {
1963     QQuickView *canvas = createView();
1964     QTRY_VERIFY(canvas);
1965     canvas->setSource(testFileUrl("propertychangestest.qml"));
1966
1967     QQuickGridView *gridView = canvas->rootObject()->findChild<QQuickGridView*>("gridView");
1968     QTRY_VERIFY(gridView);
1969
1970     QDeclarativeListModel *alternateModel = canvas->rootObject()->findChild<QDeclarativeListModel*>("alternateModel");
1971     QTRY_VERIFY(alternateModel);
1972     QVariant modelVariant = QVariant::fromValue<QObject *>(alternateModel);
1973     QSignalSpy modelSpy(gridView, SIGNAL(modelChanged()));
1974
1975     gridView->setModel(modelVariant);
1976     QTRY_COMPARE(gridView->model(), modelVariant);
1977     QTRY_COMPARE(modelSpy.count(),1);
1978
1979     gridView->setModel(modelVariant);
1980     QTRY_COMPARE(modelSpy.count(),1);
1981
1982     gridView->setModel(QVariant());
1983     QTRY_COMPARE(modelSpy.count(),2);
1984     delete canvas;
1985 }
1986
1987 void tst_QQuickGridView::positionViewAtIndex()
1988 {
1989     QQuickView *canvas = createView();
1990
1991     TestModel model;
1992     for (int i = 0; i < 40; i++)
1993         model.addItem("Item" + QString::number(i), "");
1994
1995     QDeclarativeContext *ctxt = canvas->rootContext();
1996     ctxt->setContextProperty("testModel", &model);
1997     ctxt->setContextProperty("testRightToLeft", QVariant(false));
1998     ctxt->setContextProperty("testTopToBottom", QVariant(false));
1999
2000     canvas->setSource(testFileUrl("gridview1.qml"));
2001     qApp->processEvents();
2002
2003     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2004     QTRY_VERIFY(gridview != 0);
2005
2006     QQuickItem *contentItem = gridview->contentItem();
2007     QTRY_VERIFY(contentItem != 0);
2008
2009     // Confirm items positioned correctly
2010     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2011     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2012         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2013         if (!item) qWarning() << "Item" << i << "not found";
2014         QTRY_VERIFY(item);
2015         QTRY_COMPARE(item->x(), (i%3)*80.);
2016         QTRY_COMPARE(item->y(), (i/3)*60.);
2017     }
2018
2019     // Position on a currently visible item
2020     gridview->positionViewAtIndex(4, QQuickGridView::Beginning);
2021     QTRY_COMPARE(gridview->indexAt(120, 90), 4);
2022     QTRY_COMPARE(gridview->contentY(), 60.);
2023
2024     // Confirm items positioned correctly
2025     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2026     for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
2027         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2028         if (!item) qWarning() << "Item" << i << "not found";
2029         QTRY_VERIFY(item);
2030         QTRY_COMPARE(item->x(), (i%3)*80.);
2031         QTRY_COMPARE(item->y(), (i/3)*60.);
2032     }
2033
2034     // Position on an item beyond the visible items
2035     gridview->positionViewAtIndex(21, QQuickGridView::Beginning);
2036     QTRY_COMPARE(gridview->indexAt(40, 450), 21);
2037     QTRY_COMPARE(gridview->contentY(), 420.);
2038
2039     // Confirm items positioned correctly
2040     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2041     for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
2042         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2043         if (!item) qWarning() << "Item" << i << "not found";
2044         QTRY_VERIFY(item);
2045         QTRY_COMPARE(item->x(), (i%3)*80.);
2046         QTRY_COMPARE(item->y(), (i/3)*60.);
2047     }
2048
2049     // Position on an item that would leave empty space if positioned at the top
2050     gridview->positionViewAtIndex(31, QQuickGridView::Beginning);
2051     QTRY_COMPARE(gridview->indexAt(120, 630), 31);
2052     QTRY_COMPARE(gridview->contentY(), 520.);
2053
2054     // Confirm items positioned correctly
2055     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2056     for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
2057         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2058         if (!item) qWarning() << "Item" << i << "not found";
2059         QTRY_VERIFY(item);
2060         QTRY_COMPARE(item->x(), (i%3)*80.);
2061         QTRY_COMPARE(item->y(), (i/3)*60.);
2062     }
2063
2064     // Position at the beginning again
2065     gridview->positionViewAtIndex(0, QQuickGridView::Beginning);
2066     QTRY_COMPARE(gridview->indexAt(0, 0), 0);
2067     QTRY_COMPARE(gridview->indexAt(40, 30), 0);
2068     QTRY_COMPARE(gridview->indexAt(80, 60), 4);
2069     QTRY_COMPARE(gridview->contentY(), 0.);
2070
2071     // Confirm items positioned correctly
2072     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2073     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2074         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2075         if (!item) qWarning() << "Item" << i << "not found";
2076         QTRY_VERIFY(item);
2077         QTRY_COMPARE(item->x(), (i%3)*80.);
2078         QTRY_COMPARE(item->y(), (i/3)*60.);
2079     }
2080
2081     // Position at End
2082     gridview->positionViewAtIndex(30, QQuickGridView::End);
2083     QTRY_COMPARE(gridview->contentY(), 340.);
2084
2085     // Position in Center
2086     gridview->positionViewAtIndex(15, QQuickGridView::Center);
2087     QTRY_COMPARE(gridview->contentY(), 170.);
2088
2089     // Ensure at least partially visible
2090     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2091     QTRY_COMPARE(gridview->contentY(), 170.);
2092
2093     gridview->setContentY(302);
2094     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2095     QTRY_COMPARE(gridview->contentY(), 302.);
2096
2097     gridview->setContentY(360);
2098     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2099     QTRY_COMPARE(gridview->contentY(), 300.);
2100
2101     gridview->setContentY(60);
2102     gridview->positionViewAtIndex(20, QQuickGridView::Visible);
2103     QTRY_COMPARE(gridview->contentY(), 60.);
2104
2105     gridview->setContentY(20);
2106     gridview->positionViewAtIndex(20, QQuickGridView::Visible);
2107     QTRY_COMPARE(gridview->contentY(), 100.);
2108
2109     // Ensure completely visible
2110     gridview->setContentY(120);
2111     gridview->positionViewAtIndex(20, QQuickGridView::Contain);
2112     QTRY_COMPARE(gridview->contentY(), 120.);
2113
2114     gridview->setContentY(302);
2115     gridview->positionViewAtIndex(15, QQuickGridView::Contain);
2116     QTRY_COMPARE(gridview->contentY(), 300.);
2117
2118     gridview->setContentY(60);
2119     gridview->positionViewAtIndex(20, QQuickGridView::Contain);
2120     QTRY_COMPARE(gridview->contentY(), 100.);
2121
2122     // Test for Top To Bottom layout
2123     ctxt->setContextProperty("testTopToBottom", QVariant(true));
2124
2125     // Confirm items positioned correctly
2126     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2127     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2128         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2129         if (!item) qWarning() << "Item" << i << "not found";
2130         QTRY_VERIFY(item);
2131         QTRY_COMPARE(item->x(), (i/5)*80.);
2132         QTRY_COMPARE(item->y(), (i%5)*60.);
2133     }
2134
2135     // Position at End
2136     gridview->positionViewAtIndex(30, QQuickGridView::End);
2137     QTRY_COMPARE(gridview->contentX(), 320.);
2138     QTRY_COMPARE(gridview->contentY(), 0.);
2139
2140     // Position in Center
2141     gridview->positionViewAtIndex(15, QQuickGridView::Center);
2142     QTRY_COMPARE(gridview->contentX(), 160.);
2143
2144     // Ensure at least partially visible
2145     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2146     QTRY_COMPARE(gridview->contentX(), 160.);
2147
2148     gridview->setContentX(170);
2149     gridview->positionViewAtIndex(25, QQuickGridView::Visible);
2150     QTRY_COMPARE(gridview->contentX(), 170.);
2151
2152     gridview->positionViewAtIndex(30, QQuickGridView::Visible);
2153     QTRY_COMPARE(gridview->contentX(), 320.);
2154
2155     gridview->setContentX(170);
2156     gridview->positionViewAtIndex(25, QQuickGridView::Contain);
2157     QTRY_COMPARE(gridview->contentX(), 240.);
2158
2159     // positionViewAtBeginning
2160     gridview->positionViewAtBeginning();
2161     QTRY_COMPARE(gridview->contentX(), 0.);
2162
2163     gridview->setContentX(80);
2164     canvas->rootObject()->setProperty("showHeader", true);
2165     gridview->positionViewAtBeginning();
2166     QTRY_COMPARE(gridview->contentX(), -30.);
2167
2168     // positionViewAtEnd
2169     gridview->positionViewAtEnd();
2170     QTRY_COMPARE(gridview->contentX(), 400.);   // 8*80 - 240   (8 columns)
2171
2172     gridview->setContentX(80);
2173     canvas->rootObject()->setProperty("showFooter", true);
2174     gridview->positionViewAtEnd();
2175     QTRY_COMPARE(gridview->contentX(), 430.);
2176
2177     // set current item to outside visible view, position at beginning
2178     // and ensure highlight moves to current item
2179     gridview->setCurrentIndex(6);
2180     gridview->positionViewAtBeginning();
2181     QTRY_COMPARE(gridview->contentX(), -30.);
2182     QVERIFY(gridview->highlightItem());
2183     QCOMPARE(gridview->highlightItem()->x(), 80.);
2184
2185     delete canvas;
2186 }
2187
2188 void tst_QQuickGridView::snapping()
2189 {
2190     QQuickView *canvas = createView();
2191
2192     TestModel model;
2193     for (int i = 0; i < 40; i++)
2194         model.addItem("Item" + QString::number(i), "");
2195
2196     QDeclarativeContext *ctxt = canvas->rootContext();
2197     ctxt->setContextProperty("testModel", &model);
2198     ctxt->setContextProperty("testTopToBottom", QVariant(false));
2199     ctxt->setContextProperty("testRightToLeft", QVariant(false));
2200
2201     canvas->setSource(testFileUrl("gridview1.qml"));
2202     qApp->processEvents();
2203
2204     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2205     QTRY_VERIFY(gridview != 0);
2206
2207     gridview->setHeight(220);
2208     QCOMPARE(gridview->height(), 220.);
2209
2210     gridview->positionViewAtIndex(12, QQuickGridView::Visible);
2211     QCOMPARE(gridview->contentY(), 80.);
2212
2213     gridview->setContentY(0);
2214     QCOMPARE(gridview->contentY(), 0.);
2215
2216     gridview->setSnapMode(QQuickGridView::SnapToRow);
2217     QCOMPARE(gridview->snapMode(), QQuickGridView::SnapToRow);
2218
2219     gridview->positionViewAtIndex(12, QQuickGridView::Visible);
2220     QCOMPARE(gridview->contentY(), 60.);
2221
2222     gridview->positionViewAtIndex(15, QQuickGridView::End);
2223     QCOMPARE(gridview->contentY(), 120.);
2224
2225     delete canvas;
2226
2227 }
2228
2229 void tst_QQuickGridView::mirroring()
2230 {
2231     QQuickView *canvasA = createView();
2232     canvasA->setSource(testFileUrl("mirroring.qml"));
2233     QQuickGridView *gridviewA = findItem<QQuickGridView>(canvasA->rootObject(), "view");
2234     QTRY_VERIFY(gridviewA != 0);
2235
2236     QQuickView *canvasB = createView();
2237     canvasB->setSource(testFileUrl("mirroring.qml"));
2238     QQuickGridView *gridviewB = findItem<QQuickGridView>(canvasB->rootObject(), "view");
2239     QTRY_VERIFY(gridviewA != 0);
2240     qApp->processEvents();
2241
2242     QList<QString> objectNames;
2243     objectNames << "item1" << "item2"; // << "item3"
2244
2245     gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
2246     gridviewB->setProperty("layoutDirection", Qt::RightToLeft);
2247     QCOMPARE(gridviewA->layoutDirection(), gridviewA->effectiveLayoutDirection());
2248
2249     // LTR != RTL
2250     foreach (const QString objectName, objectNames)
2251         QVERIFY(findItem<QQuickItem>(gridviewA, objectName)->x() != findItem<QQuickItem>(gridviewB, objectName)->x());
2252
2253     gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
2254     gridviewB->setProperty("layoutDirection", Qt::LeftToRight);
2255
2256     // LTR == LTR
2257     foreach (const QString objectName, objectNames)
2258         QCOMPARE(findItem<QQuickItem>(gridviewA, objectName)->x(), findItem<QQuickItem>(gridviewB, objectName)->x());
2259
2260     QVERIFY(gridviewB->layoutDirection() == gridviewB->effectiveLayoutDirection());
2261     QQuickItemPrivate::get(gridviewB)->setLayoutMirror(true);
2262     QVERIFY(gridviewB->layoutDirection() != gridviewB->effectiveLayoutDirection());
2263
2264     // LTR != LTR+mirror
2265     foreach (const QString objectName, objectNames)
2266         QVERIFY(findItem<QQuickItem>(gridviewA, objectName)->x() != findItem<QQuickItem>(gridviewB, objectName)->x());
2267
2268     gridviewA->setProperty("layoutDirection", Qt::RightToLeft);
2269
2270     // RTL == LTR+mirror
2271     foreach (const QString objectName, objectNames)
2272         QCOMPARE(findItem<QQuickItem>(gridviewA, objectName)->x(), findItem<QQuickItem>(gridviewB, objectName)->x());
2273
2274     gridviewB->setProperty("layoutDirection", Qt::RightToLeft);
2275
2276     // RTL != RTL+mirror
2277     foreach (const QString objectName, objectNames)
2278         QVERIFY(findItem<QQuickItem>(gridviewA, objectName)->x() != findItem<QQuickItem>(gridviewB, objectName)->x());
2279
2280     gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
2281
2282     // LTR == RTL+mirror
2283     foreach (const QString objectName, objectNames)
2284         QCOMPARE(findItem<QQuickItem>(gridviewA, objectName)->x(), findItem<QQuickItem>(gridviewB, objectName)->x());
2285
2286     delete canvasA;
2287     delete canvasB;
2288 }
2289
2290 void tst_QQuickGridView::positionViewAtIndex_rightToLeft()
2291 {
2292     QQuickView *canvas = createView();
2293
2294     TestModel model;
2295     for (int i = 0; i < 40; i++)
2296         model.addItem("Item" + QString::number(i), "");
2297
2298     QDeclarativeContext *ctxt = canvas->rootContext();
2299     ctxt->setContextProperty("testModel", &model);
2300     ctxt->setContextProperty("testTopToBottom", QVariant(true));
2301     ctxt->setContextProperty("testRightToLeft", QVariant(true));
2302
2303     canvas->setSource(testFileUrl("gridview1.qml"));
2304     qApp->processEvents();
2305
2306     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2307     QTRY_VERIFY(gridview != 0);
2308
2309     QQuickItem *contentItem = gridview->contentItem();
2310     QTRY_VERIFY(contentItem != 0);
2311
2312     // Confirm items positioned correctly
2313     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2314     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2315         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2316         if (!item) qWarning() << "Item" << i << "not found";
2317         QTRY_VERIFY(item);
2318         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
2319         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2320     }
2321
2322     // Position on a currently visible item
2323     gridview->positionViewAtIndex(6, QQuickGridView::Beginning);
2324     QTRY_COMPARE(gridview->contentX(), -320.);
2325
2326     // Confirm items positioned correctly
2327     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2328     for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
2329         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2330         if (!item) qWarning() << "Item" << i << "not found";
2331         QTRY_VERIFY(item);
2332         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
2333         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2334     }
2335
2336     // Position on an item beyond the visible items
2337     gridview->positionViewAtIndex(21, QQuickGridView::Beginning);
2338     QTRY_COMPARE(gridview->contentX(), -560.);
2339
2340     // Confirm items positioned correctly
2341     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2342     for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
2343         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2344         if (!item) qWarning() << "Item" << i << "not found";
2345         QTRY_VERIFY(item);
2346         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
2347         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2348     }
2349
2350     // Position on an item that would leave empty space if positioned at the top
2351     gridview->positionViewAtIndex(31, QQuickGridView::Beginning);
2352     QTRY_COMPARE(gridview->contentX(), -640.);
2353
2354     // Confirm items positioned correctly
2355     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2356     for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
2357         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2358         if (!item) qWarning() << "Item" << i << "not found";
2359         QTRY_VERIFY(item);
2360         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
2361         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2362     }
2363
2364     // Position at the beginning again
2365     gridview->positionViewAtIndex(0, QQuickGridView::Beginning);
2366     QTRY_COMPARE(gridview->contentX(), -240.);
2367
2368     // Confirm items positioned correctly
2369     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2370     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2371         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2372         if (!item) qWarning() << "Item" << i << "not found";
2373         QTRY_VERIFY(item);
2374         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
2375         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2376     }
2377
2378     // Position at End
2379     gridview->positionViewAtIndex(30, QQuickGridView::End);
2380     QTRY_COMPARE(gridview->contentX(), -560.);
2381
2382     // Position in Center
2383     gridview->positionViewAtIndex(15, QQuickGridView::Center);
2384     QTRY_COMPARE(gridview->contentX(), -400.);
2385
2386     // Ensure at least partially visible
2387     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2388     QTRY_COMPARE(gridview->contentX(), -400.);
2389
2390     gridview->setContentX(-555.);
2391     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2392     QTRY_COMPARE(gridview->contentX(), -555.);
2393
2394     gridview->setContentX(-239);
2395     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2396     QTRY_COMPARE(gridview->contentX(), -320.);
2397
2398     gridview->setContentX(-239);
2399     gridview->positionViewAtIndex(20, QQuickGridView::Visible);
2400     QTRY_COMPARE(gridview->contentX(), -400.);
2401
2402     gridview->setContentX(-640);
2403     gridview->positionViewAtIndex(20, QQuickGridView::Visible);
2404     QTRY_COMPARE(gridview->contentX(), -560.);
2405
2406     // Ensure completely visible
2407     gridview->setContentX(-400);
2408     gridview->positionViewAtIndex(20, QQuickGridView::Contain);
2409     QTRY_COMPARE(gridview->contentX(), -400.);
2410
2411     gridview->setContentX(-315);
2412     gridview->positionViewAtIndex(15, QQuickGridView::Contain);
2413     QTRY_COMPARE(gridview->contentX(), -320.);
2414
2415     gridview->setContentX(-640);
2416     gridview->positionViewAtIndex(20, QQuickGridView::Contain);
2417     QTRY_COMPARE(gridview->contentX(), -560.);
2418
2419     delete canvas;
2420 }
2421
2422 void tst_QQuickGridView::resetModel()
2423 {
2424     QQuickView *canvas = createView();
2425
2426     QStringList strings;
2427     strings << "one" << "two" << "three";
2428     QStringListModel model(strings);
2429
2430     QDeclarativeContext *ctxt = canvas->rootContext();
2431     ctxt->setContextProperty("testModel", &model);
2432
2433     canvas->setSource(testFileUrl("displaygrid.qml"));
2434     qApp->processEvents();
2435
2436     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2437     QTRY_VERIFY(gridview != 0);
2438
2439     QQuickItem *contentItem = gridview->contentItem();
2440     QTRY_VERIFY(contentItem != 0);
2441
2442     QTRY_COMPARE(gridview->count(), model.rowCount());
2443
2444     for (int i = 0; i < model.rowCount(); ++i) {
2445         QQuickText *display = findItem<QQuickText>(contentItem, "displayText", i);
2446         QTRY_VERIFY(display != 0);
2447         QTRY_COMPARE(display->text(), strings.at(i));
2448     }
2449
2450     strings.clear();
2451     strings << "four" << "five" << "six" << "seven";
2452     model.setStringList(strings);
2453
2454     QTRY_COMPARE(gridview->count(), model.rowCount());
2455
2456     for (int i = 0; i < model.rowCount(); ++i) {
2457         QQuickText *display = findItem<QQuickText>(contentItem, "displayText", i);
2458         QTRY_VERIFY(display != 0);
2459         QTRY_COMPARE(display->text(), strings.at(i));
2460     }
2461
2462     delete canvas;
2463 }
2464
2465 void tst_QQuickGridView::enforceRange()
2466 {
2467     QQuickView *canvas = createView();
2468
2469     TestModel model;
2470     for (int i = 0; i < 30; i++)
2471         model.addItem("Item" + QString::number(i), "");
2472
2473     QDeclarativeContext *ctxt = canvas->rootContext();
2474     ctxt->setContextProperty("testModel", &model);
2475     ctxt->setContextProperty("testRightToLeft", QVariant(false));
2476     ctxt->setContextProperty("testTopToBottom", QVariant(false));
2477
2478     canvas->setSource(testFileUrl("gridview-enforcerange.qml"));
2479     qApp->processEvents();
2480     QVERIFY(canvas->rootObject() != 0);
2481
2482     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2483     QTRY_VERIFY(gridview != 0);
2484
2485     QTRY_COMPARE(gridview->preferredHighlightBegin(), 100.0);
2486     QTRY_COMPARE(gridview->preferredHighlightEnd(), 100.0);
2487     QTRY_COMPARE(gridview->highlightRangeMode(), QQuickGridView::StrictlyEnforceRange);
2488
2489     QQuickItem *contentItem = gridview->contentItem();
2490     QTRY_VERIFY(contentItem != 0);
2491
2492     // view should be positioned at the top of the range.
2493     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
2494     QTRY_VERIFY(item);
2495     QTRY_COMPARE(gridview->contentY(), -100.0);
2496
2497     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 0);
2498     QTRY_VERIFY(name != 0);
2499     QTRY_COMPARE(name->text(), model.name(0));
2500     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 0);
2501     QTRY_VERIFY(number != 0);
2502     QTRY_COMPARE(number->text(), model.number(0));
2503
2504     // Check currentIndex is updated when contentItem moves
2505     gridview->setContentY(0);
2506     QTRY_COMPARE(gridview->currentIndex(), 2);
2507
2508     gridview->setCurrentIndex(5);
2509     QTRY_COMPARE(gridview->contentY(), 100.);
2510
2511     TestModel model2;
2512     for (int i = 0; i < 5; i++)
2513         model2.addItem("Item" + QString::number(i), "");
2514
2515     ctxt->setContextProperty("testModel", &model2);
2516     QCOMPARE(gridview->count(), 5);
2517
2518     delete canvas;
2519 }
2520
2521 void tst_QQuickGridView::enforceRange_rightToLeft()
2522 {
2523     QQuickView *canvas = createView();
2524
2525     TestModel model;
2526     for (int i = 0; i < 30; i++)
2527         model.addItem("Item" + QString::number(i), "");
2528
2529     QDeclarativeContext *ctxt = canvas->rootContext();
2530     ctxt->setContextProperty("testModel", &model);
2531     ctxt->setContextProperty("testRightToLeft", QVariant(true));
2532     ctxt->setContextProperty("testTopToBottom", QVariant(true));
2533
2534     canvas->setSource(testFileUrl("gridview-enforcerange.qml"));
2535     qApp->processEvents();
2536     QVERIFY(canvas->rootObject() != 0);
2537
2538     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2539     QTRY_VERIFY(gridview != 0);
2540
2541     QCOMPARE(gridview->preferredHighlightBegin(), 100.0);
2542     QCOMPARE(gridview->preferredHighlightEnd(), 100.0);
2543     QCOMPARE(gridview->highlightRangeMode(), QQuickGridView::StrictlyEnforceRange);
2544
2545     QQuickItem *contentItem = gridview->contentItem();
2546     QVERIFY(contentItem != 0);
2547
2548     // view should be positioned at the top of the range.
2549     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
2550     QVERIFY(item);
2551     QTRY_COMPARE(gridview->contentX(), -140.);
2552     QTRY_COMPARE(gridview->contentY(), 0.0);
2553
2554     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 0);
2555     QTRY_VERIFY(name != 0);
2556     QTRY_COMPARE(name->text(), model.name(0));
2557     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 0);
2558     QTRY_VERIFY(number != 0);
2559     QTRY_COMPARE(number->text(), model.number(0));
2560
2561     // Check currentIndex is updated when contentItem moves
2562     gridview->setContentX(-240);
2563     QTRY_COMPARE(gridview->currentIndex(), 3);
2564
2565     gridview->setCurrentIndex(7);
2566     QTRY_COMPARE(gridview->contentX(), -340.);
2567     QTRY_COMPARE(gridview->contentY(), 0.0);
2568
2569     TestModel model2;
2570     for (int i = 0; i < 5; i++)
2571         model2.addItem("Item" + QString::number(i), "");
2572
2573     ctxt->setContextProperty("testModel", &model2);
2574     QCOMPARE(gridview->count(), 5);
2575
2576     delete canvas;
2577 }
2578
2579 void tst_QQuickGridView::QTBUG_8456()
2580 {
2581     QQuickView *canvas = createView();
2582
2583     canvas->setSource(testFileUrl("setindex.qml"));
2584     qApp->processEvents();
2585
2586     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2587     QTRY_VERIFY(gridview != 0);
2588
2589     QTRY_COMPARE(gridview->currentIndex(), 0);
2590
2591     delete canvas;
2592 }
2593
2594 void tst_QQuickGridView::manualHighlight()
2595 {
2596     QQuickView *canvas = createView();
2597
2598     QString filename(testFile("manual-highlight.qml"));
2599     canvas->setSource(QUrl::fromLocalFile(filename));
2600
2601     qApp->processEvents();
2602
2603     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2604     QTRY_VERIFY(gridview != 0);
2605
2606     QQuickItem *contentItem = gridview->contentItem();
2607     QTRY_VERIFY(contentItem != 0);
2608
2609     QTRY_COMPARE(gridview->currentIndex(), 0);
2610     QTRY_COMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
2611     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
2612     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
2613
2614     gridview->setCurrentIndex(2);
2615
2616     QTRY_COMPARE(gridview->currentIndex(), 2);
2617     QTRY_COMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 2));
2618     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
2619     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
2620
2621     gridview->positionViewAtIndex(8, QQuickGridView::Contain);
2622
2623     QTRY_COMPARE(gridview->currentIndex(), 2);
2624     QTRY_COMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 2));
2625     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
2626     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
2627
2628     gridview->setFlow(QQuickGridView::TopToBottom);
2629     QTRY_COMPARE(gridview->flow(), QQuickGridView::TopToBottom);
2630
2631     gridview->setCurrentIndex(0);
2632     QTRY_COMPARE(gridview->currentIndex(), 0);
2633     QTRY_COMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
2634     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
2635     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
2636
2637     delete canvas;
2638 }
2639
2640
2641 void tst_QQuickGridView::footer()
2642 {
2643     QFETCH(QQuickGridView::Flow, flow);
2644     QFETCH(Qt::LayoutDirection, layoutDirection);
2645     QFETCH(QPointF, initialFooterPos);
2646     QFETCH(QPointF, changedFooterPos);
2647     QFETCH(QPointF, initialContentPos);
2648     QFETCH(QPointF, changedContentPos);
2649     QFETCH(QPointF, firstDelegatePos);
2650     QFETCH(QPointF, resizeContentPos);
2651
2652     QQuickView *canvas = createView();
2653     canvas->show();
2654
2655     TestModel model;
2656     for (int i = 0; i < 7; i++)
2657         model.addItem("Item" + QString::number(i), "");
2658
2659     QDeclarativeContext *ctxt = canvas->rootContext();
2660     ctxt->setContextProperty("testModel", &model);
2661
2662     canvas->setSource(testFileUrl("footer.qml"));
2663     qApp->processEvents();
2664
2665     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2666     QTRY_VERIFY(gridview != 0);
2667     gridview->setFlow(flow);
2668     gridview->setLayoutDirection(layoutDirection);
2669
2670     QQuickItem *contentItem = gridview->contentItem();
2671     QTRY_VERIFY(contentItem != 0);
2672
2673     QQuickText *footer = findItem<QQuickText>(contentItem, "footer");
2674     QVERIFY(footer);
2675
2676     QVERIFY(footer == gridview->footerItem());
2677
2678     QCOMPARE(footer->pos(), initialFooterPos);
2679     QCOMPARE(footer->width(), 100.);
2680     QCOMPARE(footer->height(), 30.);
2681     QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
2682
2683     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
2684     QVERIFY(item);
2685     QCOMPARE(item->pos(), firstDelegatePos);
2686
2687     if (flow == QQuickGridView::LeftToRight) {
2688         // shrink by one row
2689         model.removeItem(2);
2690         QTRY_COMPARE(footer->y(), initialFooterPos.y() - gridview->cellHeight());
2691     } else {
2692         // shrink by one column
2693         model.removeItem(2);
2694         model.removeItem(3);
2695         if (layoutDirection == Qt::LeftToRight)
2696             QTRY_COMPARE(footer->x(), initialFooterPos.x() - gridview->cellWidth());
2697         else
2698             QTRY_COMPARE(footer->x(), initialFooterPos.x() + gridview->cellWidth());
2699     }
2700
2701     // remove all items
2702     model.clear();
2703
2704     QPointF posWhenNoItems(0, 0);
2705     if (layoutDirection == Qt::RightToLeft)
2706         posWhenNoItems.setX(flow == QQuickGridView::LeftToRight ? gridview->width() - footer->width() : -footer->width());
2707     QTRY_COMPARE(footer->pos(), posWhenNoItems);
2708
2709     // if header is present, it's at a negative pos, so the footer should not move
2710     canvas->rootObject()->setProperty("showHeader", true);
2711     QVERIFY(findItem<QQuickItem>(contentItem, "header") != 0);
2712     QTRY_COMPARE(footer->pos(), posWhenNoItems);
2713     canvas->rootObject()->setProperty("showHeader", false);
2714
2715     // add 30 items
2716     for (int i = 0; i < 30; i++)
2717         model.addItem("Item" + QString::number(i), "");
2718
2719     QSignalSpy footerItemSpy(gridview, SIGNAL(footerItemChanged()));
2720     QMetaObject::invokeMethod(canvas->rootObject(), "changeFooter");
2721
2722     QCOMPARE(footerItemSpy.count(), 1);
2723
2724     footer = findItem<QQuickText>(contentItem, "footer");
2725     QVERIFY(!footer);
2726     footer = findItem<QQuickText>(contentItem, "footer2");
2727     QVERIFY(footer);
2728
2729     QVERIFY(footer == gridview->footerItem());
2730
2731     QCOMPARE(footer->pos(), changedFooterPos);
2732     QCOMPARE(footer->width(), 50.);
2733     QCOMPARE(footer->height(), 20.);
2734     QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), changedContentPos);
2735
2736     item = findItem<QQuickItem>(contentItem, "wrapper", 0);
2737     QVERIFY(item);
2738     QCOMPARE(item->pos(), firstDelegatePos);
2739
2740     gridview->positionViewAtEnd();
2741     footer->setHeight(10);
2742     footer->setWidth(40);
2743     QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), resizeContentPos);
2744
2745     delete canvas;
2746 }
2747
2748 void tst_QQuickGridView::footer_data()
2749 {
2750     QTest::addColumn<QQuickGridView::Flow>("flow");
2751     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
2752     QTest::addColumn<QPointF>("initialFooterPos");
2753     QTest::addColumn<QPointF>("changedFooterPos");
2754     QTest::addColumn<QPointF>("initialContentPos");
2755     QTest::addColumn<QPointF>("changedContentPos");
2756     QTest::addColumn<QPointF>("firstDelegatePos");
2757     QTest::addColumn<QPointF>("resizeContentPos");
2758
2759     // footer1 = 100 x 30
2760     // footer2 = 50 x 20
2761     // cells = 80 * 60
2762     // view width = 240
2763     // view height = 320
2764
2765     // footer below items, bottom left
2766     QTest::newRow("flow left to right") << QQuickGridView::LeftToRight << Qt::LeftToRight
2767         << QPointF(0, 3 * 60)  // 180 = height of 3 rows (cell height is 60)
2768         << QPointF(0, 10 * 60)  // 30 items = 10 rows
2769         << QPointF(0, 0)
2770         << QPointF(0, 0)
2771         << QPointF(0, 0)
2772         << QPointF(0, 10 * 60 - 320 + 10);
2773
2774     // footer below items, bottom right
2775     QTest::newRow("flow left to right, layout right to left") << QQuickGridView::LeftToRight << Qt::RightToLeft
2776         << QPointF(240 - 100, 3 * 60)
2777         << QPointF((240 - 100) + 50, 10 * 60)     // 50 = width diff between old and new footers
2778         << QPointF(0, 0)
2779         << QPointF(0, 0)
2780         << QPointF(240 - 80, 0)
2781         << QPointF(0, 10 * 60 - 320 + 10);
2782
2783     // footer to right of items
2784     QTest::newRow("flow top to bottom, layout left to right") << QQuickGridView::TopToBottom << Qt::LeftToRight
2785         << QPointF(2 * 80, 0)      // 2 columns, cell width 80
2786         << QPointF(6 * 80, 0)      // 30 items = 6 columns
2787         << QPointF(0, 0)
2788         << QPointF(0, 0)
2789         << QPointF(0, 0)
2790         << QPointF(6 * 80 - 240 + 40, 0);
2791
2792     // footer to left of items
2793     QTest::newRow("flow top to bottom, layout right to left") << QQuickGridView::TopToBottom << Qt::RightToLeft
2794         << QPointF(-(2 * 80) - 100, 0)
2795         << QPointF(-(6 * 80) - 50, 0)     // 50 = new footer width
2796         << QPointF(-240, 0)
2797         << QPointF(-240, 0)    // unchanged, footer change doesn't change content pos
2798         << QPointF(-80, 0)
2799         << QPointF(-(6 * 80) - 40, 0);
2800 }
2801
2802 void tst_QQuickGridView::header()
2803 {
2804     QFETCH(QQuickGridView::Flow, flow);
2805     QFETCH(Qt::LayoutDirection, layoutDirection);
2806     QFETCH(QPointF, initialHeaderPos);
2807     QFETCH(QPointF, changedHeaderPos);
2808     QFETCH(QPointF, initialContentPos);
2809     QFETCH(QPointF, changedContentPos);
2810     QFETCH(QPointF, firstDelegatePos);
2811     QFETCH(QPointF, resizeContentPos);
2812
2813     TestModel model;
2814     for (int i = 0; i < 30; i++)
2815         model.addItem("Item" + QString::number(i), "");
2816
2817     QQuickView *canvas = createView();
2818     canvas->rootContext()->setContextProperty("testModel", &model);
2819     canvas->rootContext()->setContextProperty("initialViewWidth", 240);
2820     canvas->rootContext()->setContextProperty("initialViewHeight", 320);
2821     canvas->setSource(testFileUrl("header.qml"));
2822
2823     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2824     QTRY_VERIFY(gridview != 0);
2825     gridview->setFlow(flow);
2826     gridview->setLayoutDirection(layoutDirection);
2827
2828     QQuickItem *contentItem = gridview->contentItem();
2829     QTRY_VERIFY(contentItem != 0);
2830
2831     QQuickText *header = findItem<QQuickText>(contentItem, "header");
2832     QVERIFY(header);
2833
2834     QVERIFY(header == gridview->headerItem());
2835
2836     QCOMPARE(header->pos(), initialHeaderPos);
2837     QCOMPARE(header->width(), 100.);
2838     QCOMPARE(header->height(), 30.);
2839     QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
2840
2841     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
2842     QVERIFY(item);
2843     QCOMPARE(item->pos(), firstDelegatePos);
2844
2845     model.clear();
2846     QCOMPARE(header->pos(), initialHeaderPos); // header should stay where it is
2847
2848     for (int i = 0; i < 30; i++)
2849         model.addItem("Item" + QString::number(i), "");
2850
2851     QSignalSpy headerItemSpy(gridview, SIGNAL(headerItemChanged()));
2852     QMetaObject::invokeMethod(canvas->rootObject(), "changeHeader");
2853
2854     QCOMPARE(headerItemSpy.count(), 1);
2855
2856     header = findItem<QQuickText>(contentItem, "header");
2857     QVERIFY(!header);
2858     header = findItem<QQuickText>(contentItem, "header2");
2859     QVERIFY(header);
2860
2861     QVERIFY(header == gridview->headerItem());
2862
2863     QCOMPARE(header->pos(), changedHeaderPos);
2864     QCOMPARE(header->width(), 50.);
2865     QCOMPARE(header->height(), 20.);
2866     QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), changedContentPos);
2867
2868     item = findItem<QQuickItem>(contentItem, "wrapper", 0);
2869     QVERIFY(item);
2870     QCOMPARE(item->pos(), firstDelegatePos);
2871
2872     header->setHeight(10);
2873     header->setWidth(40);
2874     QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), resizeContentPos);
2875
2876     delete canvas;
2877
2878
2879     // QTBUG-21207 header should become visible if view resizes from initial empty size
2880
2881     canvas = createView();
2882     canvas->rootContext()->setContextProperty("testModel", &model);
2883     canvas->rootContext()->setContextProperty("initialViewWidth", 240);
2884     canvas->rootContext()->setContextProperty("initialViewHeight", 320);
2885     canvas->setSource(testFileUrl("header.qml"));
2886
2887     gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2888     QTRY_VERIFY(gridview != 0);
2889     gridview->setFlow(flow);
2890     gridview->setLayoutDirection(layoutDirection);
2891
2892     gridview->setWidth(240);
2893     gridview->setHeight(320);
2894     QTRY_COMPARE(gridview->headerItem()->pos(), initialHeaderPos);
2895     QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
2896
2897     delete canvas;
2898 }
2899
2900 void tst_QQuickGridView::header_data()
2901 {
2902     QTest::addColumn<QQuickGridView::Flow>("flow");
2903     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
2904     QTest::addColumn<QPointF>("initialHeaderPos");
2905     QTest::addColumn<QPointF>("changedHeaderPos");
2906     QTest::addColumn<QPointF>("initialContentPos");
2907     QTest::addColumn<QPointF>("changedContentPos");
2908     QTest::addColumn<QPointF>("firstDelegatePos");
2909     QTest::addColumn<QPointF>("resizeContentPos");
2910
2911     // header1 = 100 x 30
2912     // header2 = 50 x 20
2913     // cells = 80 x 60
2914     // view width = 240
2915
2916     // header above items, top left
2917     QTest::newRow("flow left to right") << QQuickGridView::LeftToRight << Qt::LeftToRight
2918         << QPointF(0, -30)
2919         << QPointF(0, -20)
2920         << QPointF(0, -30)
2921         << QPointF(0, -20)
2922         << QPointF(0, 0)
2923         << QPointF(0, -10);
2924
2925     // header above items, top right
2926     QTest::newRow("flow left to right, layout right to left") << QQuickGridView::LeftToRight << Qt::RightToLeft
2927         << QPointF(240 - 100, -30)
2928         << QPointF((240 - 100) + 50, -20)     // 50 = width diff between old and new headers
2929         << QPointF(0, -30)
2930         << QPointF(0, -20)
2931         << QPointF(160, 0)
2932         << QPointF(0, -10);
2933
2934     // header to left of items
2935     QTest::newRow("flow top to bottom, layout left to right") << QQuickGridView::TopToBottom << Qt::LeftToRight
2936         << QPointF(-100, 0)
2937         << QPointF(-50, 0)
2938         << QPointF(-100, 0)
2939         << QPointF(-50, 0)
2940         << QPointF(0, 0)
2941         << QPointF(-40, 0);
2942
2943     // header to right of items
2944     QTest::newRow("flow top to bottom, layout right to left") << QQuickGridView::TopToBottom << Qt::RightToLeft
2945         << QPointF(0, 0)
2946         << QPointF(0, 0)
2947         << QPointF(-(240 - 100), 0)
2948         << QPointF(-(240 - 50), 0)
2949         << QPointF(-80, 0)
2950         << QPointF(-(240 - 40), 0);
2951 }
2952
2953 void tst_QQuickGridView::resizeViewAndRepaint()
2954 {
2955     QQuickView *canvas = createView();
2956     canvas->show();
2957
2958     TestModel model;
2959     for (int i = 0; i < 40; i++)
2960         model.addItem("Item" + QString::number(i), "");
2961
2962     QDeclarativeContext *ctxt = canvas->rootContext();
2963     ctxt->setContextProperty("testModel", &model);
2964     ctxt->setContextProperty("initialHeight", 100);
2965
2966     canvas->setSource(testFileUrl("resizeview.qml"));
2967     qApp->processEvents();
2968
2969     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2970     QTRY_VERIFY(gridview != 0);
2971     QQuickItem *contentItem = gridview->contentItem();
2972     QTRY_VERIFY(contentItem != 0);
2973
2974     // item at index 10 should not be currently visible
2975     QVERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 10));
2976
2977     gridview->setHeight(320);
2978     QTRY_VERIFY(findItem<QQuickItem>(contentItem, "wrapper", 10));
2979
2980     gridview->setHeight(100);
2981     QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 10));
2982
2983     delete canvas;
2984 }
2985
2986 void tst_QQuickGridView::indexAt()
2987 {
2988     QQuickView *canvas = createView();
2989
2990     TestModel model;
2991     model.addItem("Fred", "12345");
2992     model.addItem("John", "2345");
2993     model.addItem("Bob", "54321");
2994     model.addItem("Billy", "22345");
2995     model.addItem("Sam", "2945");
2996     model.addItem("Ben", "04321");
2997     model.addItem("Jim", "0780");
2998
2999     QDeclarativeContext *ctxt = canvas->rootContext();
3000     ctxt->setContextProperty("testModel", &model);
3001     ctxt->setContextProperty("testRightToLeft", QVariant(false));
3002     ctxt->setContextProperty("testTopToBottom", QVariant(false));
3003
3004     canvas->setSource(testFileUrl("gridview1.qml"));
3005     qApp->processEvents();
3006
3007     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3008     QTRY_VERIFY(gridview != 0);
3009
3010     QQuickItem *contentItem = gridview->contentItem();
3011     QTRY_VERIFY(contentItem != 0);
3012
3013     QTRY_COMPARE(gridview->count(), model.count());
3014
3015     QCOMPARE(gridview->indexAt(0, 0), 0);
3016     QCOMPARE(gridview->indexAt(79, 59), 0);
3017     QCOMPARE(gridview->indexAt(80, 0), 1);
3018     QCOMPARE(gridview->indexAt(0, 60), 3);
3019     QCOMPARE(gridview->indexAt(240, 0), -1);
3020
3021     delete canvas;
3022 }
3023
3024 void tst_QQuickGridView::onAdd()
3025 {
3026     QFETCH(int, initialItemCount);
3027     QFETCH(int, itemsToAdd);
3028
3029     const int delegateWidth = 50;
3030     const int delegateHeight = 100;
3031     TestModel model;
3032     QQuickView *canvas = createView();
3033     canvas->setGeometry(0,0,5 * delegateWidth, 5 * delegateHeight); // just ensure all items fit
3034
3035     // these initial items should not trigger GridView.onAdd
3036     for (int i=0; i<initialItemCount; i++)
3037         model.addItem("dummy value", "dummy value");
3038
3039     QDeclarativeContext *ctxt = canvas->rootContext();
3040     ctxt->setContextProperty("testModel", &model);
3041     ctxt->setContextProperty("delegateWidth", delegateWidth);
3042     ctxt->setContextProperty("delegateHeight", delegateHeight);
3043     canvas->setSource(testFileUrl("attachedSignals.qml"));
3044
3045     QObject *object = canvas->rootObject();
3046     object->setProperty("width", canvas->width());
3047     object->setProperty("height", canvas->height());
3048     qApp->processEvents();
3049
3050     QList<QPair<QString, QString> > items;
3051     for (int i=0; i<itemsToAdd; i++)
3052         items << qMakePair(QString("value %1").arg(i), QString::number(i));
3053     model.addItems(items);
3054
3055     QTRY_COMPARE(model.count(), qobject_cast<QQuickGridView*>(canvas->rootObject())->count());
3056     qApp->processEvents();
3057
3058     QVariantList result = object->property("addedDelegates").toList();
3059     QTRY_COMPARE(result.count(), items.count());
3060     for (int i=0; i<items.count(); i++)
3061         QCOMPARE(result[i].toString(), items[i].first);
3062
3063     delete canvas;
3064 }
3065
3066 void tst_QQuickGridView::onAdd_data()
3067 {
3068     QTest::addColumn<int>("initialItemCount");
3069     QTest::addColumn<int>("itemsToAdd");
3070
3071     QTest::newRow("0, add 1") << 0 << 1;
3072     QTest::newRow("0, add 2") << 0 << 2;
3073     QTest::newRow("0, add 10") << 0 << 10;
3074
3075     QTest::newRow("1, add 1") << 1 << 1;
3076     QTest::newRow("1, add 2") << 1 << 2;
3077     QTest::newRow("1, add 10") << 1 << 10;
3078
3079     QTest::newRow("5, add 1") << 5 << 1;
3080     QTest::newRow("5, add 2") << 5 << 2;
3081     QTest::newRow("5, add 10") << 5 << 10;
3082 }
3083
3084 void tst_QQuickGridView::onRemove()
3085 {
3086     QFETCH(int, initialItemCount);
3087     QFETCH(int, indexToRemove);
3088     QFETCH(int, removeCount);
3089
3090     const int delegateWidth = 50;
3091     const int delegateHeight = 100;
3092     TestModel model;
3093     for (int i=0; i<initialItemCount; i++)
3094         model.addItem(QString("value %1").arg(i), "dummy value");
3095
3096     QQuickView *canvas = createView();
3097     QDeclarativeContext *ctxt = canvas->rootContext();
3098     ctxt->setContextProperty("testModel", &model);
3099     ctxt->setContextProperty("delegateWidth", delegateWidth);
3100     ctxt->setContextProperty("delegateHeight", delegateHeight);
3101     canvas->setSource(testFileUrl("attachedSignals.qml"));
3102     QObject *object = canvas->rootObject();
3103
3104     model.removeItems(indexToRemove, removeCount);
3105     QTRY_COMPARE(model.count(), qobject_cast<QQuickGridView*>(canvas->rootObject())->count());
3106     QCOMPARE(object->property("removedDelegateCount"), QVariant(removeCount));
3107
3108     delete canvas;
3109 }
3110
3111 void tst_QQuickGridView::onRemove_data()
3112 {
3113     QTest::addColumn<int>("initialItemCount");
3114     QTest::addColumn<int>("indexToRemove");
3115     QTest::addColumn<int>("removeCount");
3116
3117     QTest::newRow("remove first") << 1 << 0 << 1;
3118     QTest::newRow("two items, remove first") << 2 << 0 << 1;
3119     QTest::newRow("two items, remove last") << 2 << 1 << 1;
3120     QTest::newRow("two items, remove all") << 2 << 0 << 2;
3121
3122     QTest::newRow("four items, remove first") << 4 << 0 << 1;
3123     QTest::newRow("four items, remove 0-2") << 4 << 0 << 2;
3124     QTest::newRow("four items, remove 1-3") << 4 << 1 << 2;
3125     QTest::newRow("four items, remove 2-4") << 4 << 2 << 2;
3126     QTest::newRow("four items, remove last") << 4 << 3 << 1;
3127     QTest::newRow("four items, remove all") << 4 << 0 << 4;
3128
3129     QTest::newRow("ten items, remove 1-8") << 10 << 0 << 8;
3130     QTest::newRow("ten items, remove 2-7") << 10 << 2 << 5;
3131     QTest::newRow("ten items, remove 4-10") << 10 << 4 << 6;
3132 }
3133
3134 void tst_QQuickGridView::columnCount()
3135 {
3136     QQuickView canvas;
3137     canvas.setSource(testFileUrl("gridview4.qml"));
3138     canvas.show();
3139     canvas.requestActivateWindow();
3140     QTest::qWaitForWindowShown(&canvas);
3141
3142     QQuickGridView *view = qobject_cast<QQuickGridView*>(canvas.rootObject());
3143
3144     QCOMPARE(view->cellWidth(), qreal(405)/qreal(9));
3145     QCOMPARE(view->cellHeight(), qreal(100));
3146
3147     QList<QQuickItem*> items = findItems<QQuickItem>(view, "delegate");
3148     QCOMPARE(items.size(), 18);
3149     QCOMPARE(items.at(8)->y(), qreal(0));
3150     QCOMPARE(items.at(9)->y(), qreal(100));
3151 }
3152
3153 void tst_QQuickGridView::margins()
3154 {
3155     {
3156         QQuickView *canvas = createView();
3157         canvas->show();
3158
3159         TestModel model;
3160         for (int i = 0; i < 40; i++)
3161             model.addItem("Item" + QString::number(i), "");
3162
3163         QDeclarativeContext *ctxt = canvas->rootContext();
3164         ctxt->setContextProperty("testModel", &model);
3165         ctxt->setContextProperty("testRightToLeft", QVariant(false));
3166
3167         canvas->setSource(testFileUrl("margins.qml"));
3168         qApp->processEvents();
3169
3170         QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3171         QTRY_VERIFY(gridview != 0);
3172
3173         QQuickItem *contentItem = gridview->contentItem();
3174         QTRY_VERIFY(contentItem != 0);
3175
3176         QCOMPARE(gridview->contentX(), -30.);
3177         QCOMPARE(gridview->xOrigin(), 0.);
3178
3179         // check end bound
3180         gridview->positionViewAtEnd();
3181         qreal pos = gridview->contentX();
3182         gridview->setContentX(pos + 80);
3183         gridview->returnToBounds();
3184         QTRY_COMPARE(gridview->contentX(), pos + 50);
3185
3186         // remove item before visible and check that left margin is maintained
3187         // and xOrigin is updated
3188         gridview->setContentX(200);
3189         model.removeItems(0, 4);
3190         QTest::qWait(100);
3191         gridview->setContentX(-50);
3192         gridview->returnToBounds();
3193         QCOMPARE(gridview->xOrigin(), 100.);
3194         QTRY_COMPARE(gridview->contentX(), 70.);
3195
3196         // reduce left margin
3197         gridview->setLeftMargin(20);
3198         QCOMPARE(gridview->xOrigin(), 100.);
3199         QTRY_COMPARE(gridview->contentX(), 80.);
3200
3201         // check end bound
3202         gridview->positionViewAtEnd();
3203         QCOMPARE(gridview->xOrigin(), 0.); // positionViewAtEnd() resets origin
3204         pos = gridview->contentX();
3205         gridview->setContentX(pos + 80);
3206         gridview->returnToBounds();
3207         QTRY_COMPARE(gridview->contentX(), pos + 50);
3208
3209         // reduce right margin
3210         pos = gridview->contentX();
3211         gridview->setRightMargin(40);
3212         QCOMPARE(gridview->xOrigin(), 0.);
3213         QTRY_COMPARE(gridview->contentX(), pos-10);
3214
3215         delete canvas;
3216     }
3217     {
3218         //RTL
3219         QQuickView *canvas = createView();
3220         canvas->show();
3221
3222         TestModel model;
3223         for (int i = 0; i < 40; i++)
3224             model.addItem("Item" + QString::number(i), "");
3225
3226         QDeclarativeContext *ctxt = canvas->rootContext();
3227         ctxt->setContextProperty("testModel", &model);
3228         ctxt->setContextProperty("testRightToLeft", QVariant(true));
3229
3230         canvas->setSource(testFileUrl("margins.qml"));
3231         qApp->processEvents();
3232
3233         QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3234         QTRY_VERIFY(gridview != 0);
3235
3236         QQuickItem *contentItem = gridview->contentItem();
3237         QTRY_VERIFY(contentItem != 0);
3238
3239         QCOMPARE(gridview->contentX(), -240+30.);
3240         QCOMPARE(gridview->xOrigin(), 0.);
3241
3242         // check end bound
3243         gridview->positionViewAtEnd();
3244         qreal pos = gridview->contentX();
3245         gridview->setContentX(pos - 80);
3246         gridview->returnToBounds();
3247         QTRY_COMPARE(gridview->contentX(), pos - 50);
3248
3249         // remove item before visible and check that left margin is maintained
3250         // and xOrigin is updated
3251         gridview->setContentX(-400);
3252         model.removeItems(0, 4);
3253         QTest::qWait(100);
3254         gridview->setContentX(-240+50);
3255         gridview->returnToBounds();
3256         QCOMPARE(gridview->xOrigin(), -100.);
3257         QTRY_COMPARE(gridview->contentX(), -240-70.);
3258
3259         // reduce left margin (i.e. right side due to RTL)
3260         pos = gridview->contentX();
3261         gridview->setLeftMargin(20);
3262         QCOMPARE(gridview->xOrigin(), -100.);
3263         QTRY_COMPARE(gridview->contentX(), -240-80.);
3264
3265         // check end bound
3266         gridview->positionViewAtEnd();
3267         QCOMPARE(gridview->xOrigin(), 0.); // positionViewAtEnd() resets origin
3268         pos = gridview->contentX();
3269         gridview->setContentX(pos - 80);
3270         gridview->returnToBounds();
3271         QTRY_COMPARE(gridview->contentX(), pos - 50);
3272
3273         // reduce right margin (i.e. left side due to RTL)
3274         pos = gridview->contentX();
3275         gridview->setRightMargin(40);
3276         QCOMPARE(gridview->xOrigin(), 0.);
3277         QTRY_COMPARE(gridview->contentX(), pos+10);
3278
3279         delete canvas;
3280     }
3281 }
3282
3283 void tst_QQuickGridView::creationContext()
3284 {
3285     QQuickView canvas;
3286     canvas.setGeometry(0,0,240,320);
3287     canvas.setSource(testFileUrl("creationContext.qml"));
3288     qApp->processEvents();
3289
3290     QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
3291     QVERIFY(rootItem);
3292     QVERIFY(rootItem->property("count").toInt() > 0);
3293
3294     QQuickItem *item;
3295     QVERIFY(item = rootItem->findChild<QQuickItem *>("listItem"));
3296     QCOMPARE(item->property("text").toString(), QString("Hello!"));
3297     QVERIFY(item = rootItem->findChild<QQuickItem *>("header"));
3298     QCOMPARE(item->property("text").toString(), QString("Hello!"));
3299     QVERIFY(item = rootItem->findChild<QQuickItem *>("footer"));
3300     QCOMPARE(item->property("text").toString(), QString("Hello!"));
3301 }
3302
3303 void tst_QQuickGridView::snapToRow_data()
3304 {
3305     QTest::addColumn<QQuickGridView::Flow>("flow");
3306     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3307     QTest::addColumn<int>("highlightRangeMode");
3308     QTest::addColumn<QPoint>("flickStart");
3309     QTest::addColumn<QPoint>("flickEnd");
3310     QTest::addColumn<qreal>("snapAlignment");
3311     QTest::addColumn<qreal>("endExtent");
3312     QTest::addColumn<qreal>("startExtent");
3313
3314     QTest::newRow("vertical, left to right") << QQuickGridView::LeftToRight << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
3315         << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 1200.0 << 0.0;
3316
3317     QTest::newRow("horizontal, left to right") << QQuickGridView::TopToBottom << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
3318         << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 1200.0 << 0.0;
3319
3320     QTest::newRow("horizontal, right to left") << QQuickGridView::TopToBottom << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
3321         << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -1200.0 - 240.0 << -240.0;
3322
3323     QTest::newRow("vertical, left to right, enforce range") << QQuickGridView::LeftToRight << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
3324         << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 1340.0 << -20.0;
3325
3326     QTest::newRow("horizontal, left to right, enforce range") << QQuickGridView::TopToBottom << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
3327         << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 1340.0 << -20.0;
3328
3329     QTest::newRow("horizontal, right to left, enforce range") << QQuickGridView::TopToBottom << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
3330         << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -1200.0 - 240.0 - 140.0 << -220.0;
3331 }
3332
3333 void tst_QQuickGridView::snapToRow()
3334 {
3335     QFETCH(QQuickGridView::Flow, flow);
3336     QFETCH(Qt::LayoutDirection, layoutDirection);
3337     QFETCH(int, highlightRangeMode);
3338     QFETCH(QPoint, flickStart);
3339     QFETCH(QPoint, flickEnd);
3340     QFETCH(qreal, snapAlignment);
3341     QFETCH(qreal, endExtent);
3342     QFETCH(qreal, startExtent);
3343
3344     QQuickView *canvas = createView();
3345
3346     canvas->setSource(testFileUrl("snapToRow.qml"));
3347     canvas->show();
3348     qApp->processEvents();
3349
3350     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3351     QTRY_VERIFY(gridview != 0);
3352
3353     gridview->setFlow(flow);
3354     gridview->setLayoutDirection(layoutDirection);
3355     gridview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
3356
3357     QQuickItem *contentItem = gridview->contentItem();
3358     QTRY_VERIFY(contentItem != 0);
3359
3360     // confirm that a flick hits an item boundary
3361     flick(canvas, flickStart, flickEnd, 180);
3362     QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
3363     if (flow == QQuickGridView::LeftToRight)
3364         QCOMPARE(qreal(fmod(gridview->contentY(),80.0)), snapAlignment);
3365     else
3366         QCOMPARE(qreal(fmod(gridview->contentX(),80.0)), snapAlignment);
3367
3368     // flick to end
3369     do {
3370         flick(canvas, flickStart, flickEnd, 180);
3371         QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
3372     } while (flow == QQuickGridView::LeftToRight
3373            ? !gridview->isAtYEnd()
3374            : layoutDirection == Qt::LeftToRight ? !gridview->isAtXEnd() : !gridview->isAtXBeginning());
3375
3376     if (flow == QQuickGridView::LeftToRight)
3377         QCOMPARE(gridview->contentY(), endExtent);
3378     else
3379         QCOMPARE(gridview->contentX(), endExtent);
3380
3381     // flick to start
3382     do {
3383         flick(canvas, flickEnd, flickStart, 180);
3384         QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
3385     } while (flow == QQuickGridView::LeftToRight
3386            ? !gridview->isAtYBeginning()
3387            : layoutDirection == Qt::LeftToRight ? !gridview->isAtXBeginning() : !gridview->isAtXEnd());
3388
3389     if (flow == QQuickGridView::LeftToRight)
3390         QCOMPARE(gridview->contentY(), startExtent);
3391     else
3392         QCOMPARE(gridview->contentX(), startExtent);
3393
3394     delete canvas;
3395 }
3396
3397 void tst_QQuickGridView::snapOneRow_data()
3398 {
3399     QTest::addColumn<QQuickGridView::Flow>("flow");
3400     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3401     QTest::addColumn<int>("highlightRangeMode");
3402     QTest::addColumn<QPoint>("flickStart");
3403     QTest::addColumn<QPoint>("flickEnd");
3404     QTest::addColumn<qreal>("snapAlignment");
3405     QTest::addColumn<qreal>("endExtent");
3406     QTest::addColumn<qreal>("startExtent");
3407
3408     QTest::newRow("vertical, left to right") << QQuickGridView::LeftToRight << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
3409         << QPoint(20, 200) << QPoint(20, 20) << 100.0 << 360.0 << 0.0;
3410
3411     QTest::newRow("horizontal, left to right") << QQuickGridView::TopToBottom << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
3412         << QPoint(200, 20) << QPoint(20, 20) << 100.0 << 360.0 << 0.0;
3413
3414     QTest::newRow("horizontal, right to left") << QQuickGridView::TopToBottom << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
3415         << QPoint(20, 20) << QPoint(200, 20) << -340.0 << -360.0 - 240.0 << -240.0;
3416
3417     QTest::newRow("vertical, left to right, enforce range") << QQuickGridView::LeftToRight << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
3418         << QPoint(20, 200) << QPoint(20, 20) << 100.0 << 460.0 << -20.0;
3419
3420     QTest::newRow("horizontal, left to right, enforce range") << QQuickGridView::TopToBottom << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
3421         << QPoint(200, 20) << QPoint(20, 20) << 100.0 << 460.0 << -20.0;
3422
3423     QTest::newRow("horizontal, right to left, enforce range") << QQuickGridView::TopToBottom << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
3424         << QPoint(20, 20) << QPoint(200, 20) << -340.0 << -360.0 - 240.0 - 100.0 << -220.0;
3425 }
3426
3427 void tst_QQuickGridView::snapOneRow()
3428 {
3429     QFETCH(QQuickGridView::Flow, flow);
3430     QFETCH(Qt::LayoutDirection, layoutDirection);
3431     QFETCH(int, highlightRangeMode);
3432     QFETCH(QPoint, flickStart);
3433     QFETCH(QPoint, flickEnd);
3434     QFETCH(qreal, snapAlignment);
3435     QFETCH(qreal, endExtent);
3436     QFETCH(qreal, startExtent);
3437
3438     QQuickView *canvas = createView();
3439
3440     canvas->setSource(testFileUrl("snapOneRow.qml"));
3441     canvas->show();
3442     qApp->processEvents();
3443
3444     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3445     QTRY_VERIFY(gridview != 0);
3446
3447     gridview->setFlow(flow);
3448     gridview->setLayoutDirection(layoutDirection);
3449     gridview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
3450
3451     QQuickItem *contentItem = gridview->contentItem();
3452     QTRY_VERIFY(contentItem != 0);
3453
3454     QSignalSpy currentIndexSpy(gridview, SIGNAL(currentIndexChanged()));
3455
3456     // confirm that a flick hits next row boundary
3457     flick(canvas, flickStart, flickEnd, 180);
3458     QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
3459     if (flow == QQuickGridView::LeftToRight)
3460         QCOMPARE(gridview->contentY(), snapAlignment);
3461     else
3462         QCOMPARE(gridview->contentX(), snapAlignment);
3463
3464     if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
3465         QCOMPARE(gridview->currentIndex(), 2);
3466         QCOMPARE(currentIndexSpy.count(), 1);
3467     }
3468
3469     // flick to end
3470     do {
3471         flick(canvas, flickStart, flickEnd, 180);
3472         QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
3473     } while (flow == QQuickGridView::LeftToRight
3474            ? !gridview->isAtYEnd()
3475            : layoutDirection == Qt::LeftToRight ? !gridview->isAtXEnd() : !gridview->isAtXBeginning());
3476
3477     if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
3478         QCOMPARE(gridview->currentIndex(), 8);
3479         QCOMPARE(currentIndexSpy.count(), 4);
3480     }
3481
3482     if (flow == QQuickGridView::LeftToRight)
3483         QCOMPARE(gridview->contentY(), endExtent);
3484     else
3485         QCOMPARE(gridview->contentX(), endExtent);
3486
3487     // flick to start
3488     do {
3489         flick(canvas, flickEnd, flickStart, 180);
3490         QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
3491     } while (flow == QQuickGridView::LeftToRight
3492            ? !gridview->isAtYBeginning()
3493            : layoutDirection == Qt::LeftToRight ? !gridview->isAtXBeginning() : !gridview->isAtXEnd());
3494
3495     if (flow == QQuickGridView::LeftToRight)
3496         QCOMPARE(gridview->contentY(), startExtent);
3497     else
3498         QCOMPARE(gridview->contentX(), startExtent);
3499
3500     if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
3501         QCOMPARE(gridview->currentIndex(), 0);
3502         QCOMPARE(currentIndexSpy.count(), 8);
3503     }
3504
3505     delete canvas;
3506 }
3507
3508
3509 void tst_QQuickGridView::unaligned()
3510 {
3511     QQuickView *canvas = createView();
3512     canvas->show();
3513
3514     TestModel model;
3515     for (int i = 0; i < 10; i++)
3516         model.addItem("Item" + QString::number(i), "");
3517
3518     QDeclarativeContext *ctxt = canvas->rootContext();
3519     ctxt->setContextProperty("testModel", &model);
3520
3521     canvas->setSource(testFileUrl("unaligned.qml"));
3522     qApp->processEvents();
3523
3524     QQuickGridView *gridview = qobject_cast<QQuickGridView*>(canvas->rootObject());
3525     QVERIFY(gridview != 0);
3526
3527     QQuickItem *contentItem = gridview->contentItem();
3528     QVERIFY(contentItem != 0);
3529
3530     for (int i = 0; i < 10; ++i) {
3531         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3532         if (!item) qWarning() << "Item" << i << "not found";
3533         QVERIFY(item);
3534         QCOMPARE(item->x(), qreal((i%9)*gridview->cellWidth()));
3535         QCOMPARE(item->y(), qreal((i/9)*gridview->cellHeight()));
3536     }
3537
3538     // appending
3539     for (int i = 10; i < 18; ++i) {
3540         model.addItem("Item" + QString::number(i), "");
3541         QQuickItem *item = 0;
3542         QTRY_VERIFY(item = findItem<QQuickItem>(contentItem, "wrapper", i));
3543         QCOMPARE(item->x(), qreal((i%9)*gridview->cellWidth()));
3544         QCOMPARE(item->y(), qreal((i/9)*gridview->cellHeight()));
3545     }
3546
3547     // inserting
3548     for (int i = 0; i < 10; ++i) {
3549         model.insertItem(i, "Item" + QString::number(i), "");
3550         QQuickItem *item = 0;
3551         QTRY_VERIFY(item = findItem<QQuickItem>(contentItem, "wrapper", i));
3552         QCOMPARE(item->x(), qreal((i%9)*gridview->cellWidth()));
3553         QCOMPARE(item->y(), qreal((i/9)*gridview->cellHeight()));
3554     }
3555
3556     // removing
3557     model.removeItems(7, 10);
3558     QTRY_COMPARE(model.count(), gridview->count());
3559     for (int i = 0; i < 18; ++i) {
3560         QQuickItem *item = 0;
3561         QTRY_VERIFY(item = findItem<QQuickItem>(contentItem, "wrapper", i));
3562         QCOMPARE(item->x(), qreal(i%9)*gridview->cellWidth());
3563         QCOMPARE(item->y(), qreal(i/9)*gridview->cellHeight());
3564     }
3565
3566     delete canvas;
3567 }
3568
3569 QQuickView *tst_QQuickGridView::createView()
3570 {
3571     QQuickView *canvas = new QQuickView(0);
3572     canvas->setGeometry(0,0,240,320);
3573
3574     return canvas;
3575 }
3576
3577 void tst_QQuickGridView::flick(QQuickView *canvas, const QPoint &from, const QPoint &to, int duration)
3578 {
3579     const int pointCount = 5;
3580     QPoint diff = to - from;
3581
3582     // send press, five equally spaced moves, and release.
3583     QTest::mousePress(canvas, Qt::LeftButton, 0, from);
3584
3585     for (int i = 0; i < pointCount; ++i) {
3586         QMouseEvent mv(QEvent::MouseMove, from + (i+1)*diff/pointCount, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
3587         QApplication::sendEvent(canvas, &mv);
3588         QTest::qWait(duration/pointCount);
3589         QCoreApplication::processEvents();
3590     }
3591
3592     QTest::mouseRelease(canvas, Qt::LeftButton, 0, to);
3593 }
3594
3595 void tst_QQuickGridView::cacheBuffer()
3596 {
3597     QQuickView *canvas = createView();
3598
3599     TestModel model;
3600     for (int i = 0; i < 90; i++)
3601         model.addItem("Item" + QString::number(i), "");
3602
3603     QDeclarativeContext *ctxt = canvas->rootContext();
3604     ctxt->setContextProperty("testModel", &model);
3605     ctxt->setContextProperty("testRightToLeft", QVariant(false));
3606     ctxt->setContextProperty("testTopToBottom", QVariant(false));
3607
3608     canvas->setSource(testFileUrl("gridview1.qml"));
3609     canvas->show();
3610     qApp->processEvents();
3611
3612     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3613     QVERIFY(gridview != 0);
3614
3615     QQuickItem *contentItem = gridview->contentItem();
3616     QVERIFY(contentItem != 0);
3617     QVERIFY(gridview->delegate() != 0);
3618     QVERIFY(gridview->model() != 0);
3619
3620     // Confirm items positioned correctly
3621     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3622     for (int i = 0; i < model.count() && i < itemCount; ++i) {
3623         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3624         QTRY_COMPARE(item->x(), (i%3)*80.0);
3625         QTRY_COMPARE(item->y(), (i/3)*60.0);
3626     }
3627
3628     QDeclarativeIncubationController controller;
3629     canvas->engine()->setIncubationController(&controller);
3630
3631     canvas->rootObject()->setProperty("cacheBuffer", 200);
3632     QTRY_VERIFY(gridview->cacheBuffer() == 200);
3633
3634     // items will be created one at a time
3635     for (int i = itemCount; i < qMin(itemCount+9,model.count()); ++i) {
3636         QVERIFY(findItem<QQuickItem>(gridview, "wrapper", i) == 0);
3637         QQuickItem *item = 0;
3638         while (!item) {
3639             bool b = false;
3640             controller.incubateWhile(&b);
3641             item = findItem<QQuickItem>(gridview, "wrapper", i);
3642         }
3643     }
3644
3645     {
3646         bool b = true;
3647         controller.incubateWhile(&b);
3648     }
3649
3650     int newItemCount = 0;
3651     newItemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3652
3653     // Confirm items positioned correctly
3654     for (int i = 0; i < model.count() && i < newItemCount; ++i) {
3655         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3656         QVERIFY(item);
3657         QTRY_COMPARE(item->x(), (i%3)*80.0);
3658         QTRY_COMPARE(item->y(), (i/3)*60.0);
3659     }
3660
3661     // move view and confirm items in view are visible immediately and outside are created async
3662     gridview->setContentY(300);
3663
3664     for (int i = 15; i < 34; ++i) { // 34 due to staggered item creation
3665         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3666         QVERIFY(item);
3667         QTRY_COMPARE(item->x(), (i%3)*80.0);
3668         QTRY_COMPARE(item->y(), (i/3)*60.0);
3669     }
3670
3671     QVERIFY(findItem<QQuickItem>(gridview, "wrapper", 34) == 0);
3672
3673     // ensure buffered items are created
3674     for (int i = 34; i < qMin(44,model.count()); ++i) {
3675         QQuickItem *item = 0;
3676         while (!item) {
3677             qGuiApp->processEvents(); // allow refill to happen
3678             bool b = false;
3679             controller.incubateWhile(&b);
3680             item = findItem<QQuickItem>(gridview, "wrapper", i);
3681         }
3682     }
3683
3684     {
3685         bool b = true;
3686         controller.incubateWhile(&b);
3687     }
3688
3689     delete canvas;
3690 }
3691
3692 void tst_QQuickGridView::asynchronous()
3693 {
3694     QQuickView *canvas = createView();
3695     canvas->show();
3696     QDeclarativeIncubationController controller;
3697     canvas->engine()->setIncubationController(&controller);
3698
3699     canvas->setSource(testFile("asyncloader.qml"));
3700
3701     QQuickItem *rootObject = qobject_cast<QQuickItem*>(canvas->rootObject());
3702     QVERIFY(rootObject);
3703
3704     QQuickGridView *gridview = 0;
3705     while (!gridview) {
3706         bool b = false;
3707         controller.incubateWhile(&b);
3708         gridview = rootObject->findChild<QQuickGridView*>("view");
3709     }
3710
3711     // items will be created one at a time
3712     for (int i = 0; i < 12; ++i) {
3713         QVERIFY(findItem<QQuickItem>(gridview, "wrapper", i) == 0);
3714         QQuickItem *item = 0;
3715         while (!item) {
3716             bool b = false;
3717             controller.incubateWhile(&b);
3718             item = findItem<QQuickItem>(gridview, "wrapper", i);
3719         }
3720     }
3721
3722     {
3723         bool b = true;
3724         controller.incubateWhile(&b);
3725     }
3726
3727     // verify positioning
3728     QQuickItem *contentItem = gridview->contentItem();
3729     for (int i = 0; i < 12; ++i) {
3730         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3731         if (!item) qWarning() << "Item" << i << "not found";
3732         QVERIFY(item->x() == (i%3)*100);
3733         QVERIFY(item->y() == (i/3)*100);
3734     }
3735
3736     delete canvas;
3737 }
3738
3739 void tst_QQuickGridView::unrequestedVisibility()
3740 {
3741     TestModel model;
3742     for (int i = 0; i < 30; i++)
3743         model.addItem("Item" + QString::number(i), QString::number(i));
3744
3745     QQuickView *canvas = new QQuickView(0);
3746     canvas->setGeometry(0,0,240,320);
3747
3748     QDeclarativeContext *ctxt = canvas->rootContext();
3749     ctxt->setContextProperty("testModel", &model);
3750     ctxt->setContextProperty("testWrap", QVariant(false));
3751
3752     canvas->setSource(testFileUrl("unrequestedItems.qml"));
3753
3754     canvas->show();
3755
3756     qApp->processEvents();
3757
3758     QQuickGridView *leftview = findItem<QQuickGridView>(canvas->rootObject(), "leftGrid");
3759     QTRY_VERIFY(leftview != 0);
3760
3761     QQuickGridView *rightview = findItem<QQuickGridView>(canvas->rootObject(), "rightGrid");
3762     QTRY_VERIFY(rightview != 0);
3763
3764     QQuickItem *leftContent = leftview->contentItem();
3765     QTRY_VERIFY(leftContent != 0);
3766
3767     QQuickItem *rightContent = rightview->contentItem();
3768     QTRY_VERIFY(rightContent != 0);
3769
3770     rightview->setCurrentIndex(12);
3771
3772     QTRY_COMPARE(leftview->contentY(), 0.0);
3773     QTRY_COMPARE(rightview->contentY(), 240.0);
3774
3775     QQuickItem *item;
3776
3777     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
3778     QCOMPARE(item->isVisible(), true);
3779     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
3780     QCOMPARE(item->isVisible(), false);
3781
3782     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
3783     QCOMPARE(item->isVisible(), false);
3784     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
3785     QCOMPARE(item->isVisible(), true);
3786
3787     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 9));
3788     QCOMPARE(item->isVisible(), true);
3789     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 10));
3790     QCOMPARE(item->isVisible(), false);
3791     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 3));
3792     QCOMPARE(item->isVisible(), false);
3793     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 4));
3794     QCOMPARE(item->isVisible(), true);
3795
3796     rightview->setCurrentIndex(0);
3797
3798     QTRY_COMPARE(leftview->contentY(), 0.0);
3799     QTRY_COMPARE(rightview->contentY(), 0.0);
3800
3801     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
3802     QCOMPARE(item->isVisible(), true);
3803     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
3804     QTRY_COMPARE(item->isVisible(), true);
3805
3806     QVERIFY(!findItem<QQuickItem>(leftContent, "wrapper", 11));
3807     QVERIFY(!findItem<QQuickItem>(rightContent, "wrapper", 11));
3808
3809     leftview->setCurrentIndex(12);
3810
3811     QTRY_COMPARE(leftview->contentY(), 240.0);
3812     QTRY_COMPARE(rightview->contentY(), 0.0);
3813
3814     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
3815     QTRY_COMPARE(item->isVisible(), false);
3816     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
3817     QCOMPARE(item->isVisible(), true);
3818
3819     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
3820     QCOMPARE(item->isVisible(), true);
3821     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
3822     QCOMPARE(item->isVisible(), false);
3823
3824     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
3825     QCOMPARE(item->isVisible(), false);
3826     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
3827     QCOMPARE(item->isVisible(), true);
3828     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
3829     QCOMPARE(item->isVisible(), true);
3830     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
3831     QCOMPARE(item->isVisible(), false);
3832
3833     model.moveItems(19, 1, 1);
3834     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
3835
3836     QTRY_VERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
3837     QCOMPARE(item->isVisible(), false);
3838     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
3839     QCOMPARE(item->isVisible(), true);
3840
3841     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
3842     QCOMPARE(item->isVisible(), true);
3843     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
3844     QCOMPARE(item->isVisible(), false);
3845
3846     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
3847     QCOMPARE(item->isVisible(), false);
3848     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
3849     QCOMPARE(item->isVisible(), true);
3850     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
3851     QCOMPARE(item->isVisible(), true);
3852     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
3853     QCOMPARE(item->isVisible(), false);
3854
3855     model.moveItems(3, 4, 1);
3856     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
3857
3858     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
3859     QCOMPARE(item->isVisible(), false);
3860     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 2));
3861     QCOMPARE(item->isVisible(), true);
3862     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
3863     QCOMPARE(item->isVisible(), true);
3864     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
3865     QCOMPARE(item->isVisible(), false);
3866
3867     model.moveItems(4, 5, 1);
3868     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
3869
3870     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
3871     QCOMPARE(item->isVisible(), false);
3872     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 2));
3873     QCOMPARE(item->isVisible(), true);
3874     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
3875     QCOMPARE(item->isVisible(), true);
3876     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
3877     QCOMPARE(item->isVisible(), false);
3878
3879     model.moveItems(9, 10, 1);
3880     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
3881
3882     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
3883     QCOMPARE(item->isVisible(), false);
3884     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 2));
3885     QCOMPARE(item->isVisible(), true);
3886     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
3887     QCOMPARE(item->isVisible(), true);
3888     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
3889     QCOMPARE(item->isVisible(), false);
3890
3891     model.moveItems(10, 9, 1);
3892     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
3893
3894     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
3895     QCOMPARE(item->isVisible(), false);
3896     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 2));
3897     QCOMPARE(item->isVisible(), true);
3898     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
3899     QCOMPARE(item->isVisible(), true);
3900     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
3901     QCOMPARE(item->isVisible(), false);
3902
3903     delete canvas;
3904 }
3905
3906 /*
3907    Find an item with the specified objectName.  If index is supplied then the
3908    item must also evaluate the {index} expression equal to index
3909 */
3910 template<typename T>
3911 T *tst_QQuickGridView::findItem(QQuickItem *parent, const QString &objectName, int index)
3912 {
3913     const QMetaObject &mo = T::staticMetaObject;
3914     //qDebug() << parent->childItems().count() << "children";
3915     for (int i = 0; i < parent->childItems().count(); ++i) {
3916         QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
3917         if (!item)
3918             continue;
3919         //qDebug() << "try" << item;
3920         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
3921             if (index != -1) {
3922                 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(item);
3923                 if (context) {
3924                     if (context->contextProperty("index").toInt() == index) {
3925                         return static_cast<T*>(item);
3926                     }
3927                 }
3928             } else {
3929                 return static_cast<T*>(item);
3930             }
3931         }
3932         item = findItem<T>(item, objectName, index);
3933         if (item)
3934             return static_cast<T*>(item);
3935     }
3936
3937     return 0;
3938 }
3939
3940 template<typename T>
3941 QList<T*> tst_QQuickGridView::findItems(QQuickItem *parent, const QString &objectName)
3942 {
3943     QList<T*> items;
3944     const QMetaObject &mo = T::staticMetaObject;
3945     //qDebug() << parent->childItems().count() << "children";
3946     for (int i = 0; i < parent->childItems().count(); ++i) {
3947         QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
3948         if (!item)
3949             continue;
3950         //qDebug() << "try" << item;
3951         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
3952             items.append(static_cast<T*>(item));
3953             //qDebug() << " found:" << item;
3954         }
3955         items += findItems<T>(item, objectName);
3956     }
3957
3958     return items;
3959 }
3960
3961 void tst_QQuickGridView::dumpTree(QQuickItem *parent, int depth)
3962 {
3963     static QString padding("                       ");
3964     for (int i = 0; i < parent->childItems().count(); ++i) {
3965         QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
3966         if (!item)
3967             continue;
3968         QDeclarativeContext *context = QDeclarativeEngine::contextForObject(item);
3969         qDebug() << padding.left(depth*2) << item << (context ? context->contextProperty("index").toInt() : -1);
3970         dumpTree(item, depth+1);
3971     }
3972 }
3973
3974
3975 QTEST_MAIN(tst_QQuickGridView)
3976
3977 #include "tst_qquickgridview.moc"
3978