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