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