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