Show header/footer if current index is set to first/last item or row
[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 <QtGui/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 <QtOpenGL/QGLShaderProgram>
56
57 #ifdef Q_OS_SYMBIAN
58 // In Symbian OS test data is located in applications private dir
59 #define SRCDIR "."
60 #endif
61
62 Q_DECLARE_METATYPE(Qt::LayoutDirection)
63 Q_DECLARE_METATYPE(QSGGridView::Flow)
64
65 class tst_QSGGridView : public QObject
66 {
67     Q_OBJECT
68 public:
69     tst_QSGGridView();
70
71 private slots:
72     void initTestCase();
73     void cleanupTestCase();
74     void items();
75     void changed();
76     void inserted();
77     void removed();
78     void clear();
79     void moved();
80     void moved_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 indexAt();
104     void onAdd();
105     void onAdd_data();
106     void onRemove();
107     void onRemove_data();
108     void testQtQuick11Attributes();
109     void testQtQuick11Attributes_data();
110
111 private:
112     QSGView *createView();
113     template<typename T>
114     T *findItem(QSGItem *parent, const QString &id, int index=-1);
115     template<typename T>
116     QList<T*> findItems(QSGItem *parent, const QString &objectName);
117     void dumpTree(QSGItem *parent, int depth = 0);
118 };
119
120 template<typename T>
121 void tst_qsggridview_move(int from, int to, int n, T *items)
122 {
123     if (n == 1) {
124         items->move(from, to);
125     } else {
126         T replaced;
127         int i=0;
128         typename T::ConstIterator it=items->begin(); it += from+n;
129         for (; i<to-from; ++i,++it)
130             replaced.append(*it);
131         i=0;
132         it=items->begin(); it += from;
133         for (; i<n; ++i,++it)
134             replaced.append(*it);
135         typename T::ConstIterator f=replaced.begin();
136         typename T::Iterator t=items->begin(); t += from;
137         for (; f != replaced.end(); ++f, ++t)
138             *t = *f;
139     }
140 }
141
142 void tst_QSGGridView::initTestCase()
143 {
144     QSGView canvas;
145     if (!QGLShaderProgram::hasOpenGLShaderPrograms(canvas.context()))
146         QSKIP("QSGGridView needs OpenGL 2.0", SkipAll);
147 }
148
149 void tst_QSGGridView::cleanupTestCase()
150 {
151
152 }
153 class TestModel : public QAbstractListModel
154 {
155 public:
156     enum Roles { Name = Qt::UserRole+1, Number = Qt::UserRole+2 };
157
158     TestModel(QObject *parent=0) : QAbstractListModel(parent) {
159         QHash<int, QByteArray> roles;
160         roles[Name] = "name";
161         roles[Number] = "number";
162         setRoleNames(roles);
163     }
164
165     int rowCount(const QModelIndex &parent=QModelIndex()) const { Q_UNUSED(parent); return list.count(); }
166     QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const {
167         QVariant rv;
168         if (role == Name)
169             rv = list.at(index.row()).first;
170         else if (role == Number)
171             rv = list.at(index.row()).second;
172
173         return rv;
174     }
175
176     int count() const { return rowCount(); }
177     QString name(int index) const { return list.at(index).first; }
178     QString number(int index) const { return list.at(index).second; }
179
180     void addItem(const QString &name, const QString &number) {
181         emit beginInsertRows(QModelIndex(), list.count(), list.count());
182         list.append(QPair<QString,QString>(name, number));
183         emit endInsertRows();
184     }
185
186     void addItems(const QList<QPair<QString, QString> > &items) {
187         emit beginInsertRows(QModelIndex(), list.count(), list.count()+items.count()-1);
188         for (int i=0; i<items.count(); i++)
189             list.append(QPair<QString,QString>(items[i].first, items[i].second));
190         emit endInsertRows();
191     }
192
193     void insertItem(int index, const QString &name, const QString &number) {
194         emit beginInsertRows(QModelIndex(), index, index);
195         list.insert(index, QPair<QString,QString>(name, number));
196         emit endInsertRows();
197     }
198
199     void removeItem(int index) {
200         emit beginRemoveRows(QModelIndex(), index, index);
201         list.removeAt(index);
202         emit endRemoveRows();
203     }
204
205     void removeItems(int index, int count) {
206         emit beginRemoveRows(QModelIndex(), index, index+count-1);
207         while (count--)
208             list.removeAt(index);
209         emit endRemoveRows();
210     }
211
212     void moveItem(int from, int to) {
213         emit beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
214         list.move(from, to);
215         emit endMoveRows();
216     }
217
218     void moveItems(int from, int to, int count) {
219         emit beginMoveRows(QModelIndex(), from, from+count-1, QModelIndex(), to > from ? to+count : to);
220         tst_qsggridview_move(from, to, count, &list);
221         emit endMoveRows();
222     }
223
224     void modifyItem(int idx, const QString &name, const QString &number) {
225         list[idx] = QPair<QString,QString>(name, number);
226         emit dataChanged(index(idx,0), index(idx,0));
227     }
228
229     void clear() {
230         int count = list.count();
231         emit beginRemoveRows(QModelIndex(), 0, count-1);
232         list.clear();
233         emit endRemoveRows();
234     }
235
236
237 private:
238     QList<QPair<QString,QString> > list;
239 };
240
241 tst_QSGGridView::tst_QSGGridView()
242 {
243 }
244
245 void tst_QSGGridView::items()
246 {
247     QSGView *canvas = createView();
248
249     TestModel model;
250     model.addItem("Fred", "12345");
251     model.addItem("John", "2345");
252     model.addItem("Bob", "54321");
253     model.addItem("Billy", "22345");
254     model.addItem("Sam", "2945");
255     model.addItem("Ben", "04321");
256     model.addItem("Jim", "0780");
257
258     QDeclarativeContext *ctxt = canvas->rootContext();
259     ctxt->setContextProperty("testModel", &model);
260     ctxt->setContextProperty("testRightToLeft", QVariant(false));
261     ctxt->setContextProperty("testTopToBottom", QVariant(false));
262
263     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
264     qApp->processEvents();
265
266     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
267     QTRY_VERIFY(gridview != 0);
268
269     QSGItem *contentItem = gridview->contentItem();
270     QTRY_VERIFY(contentItem != 0);
271
272     QTRY_COMPARE(gridview->count(), model.count());
273     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
274     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
275
276     for (int i = 0; i < model.count(); ++i) {
277         QSGText *name = findItem<QSGText>(contentItem, "textName", i);
278         QTRY_VERIFY(name != 0);
279         QTRY_COMPARE(name->text(), model.name(i));
280         QSGText *number = findItem<QSGText>(contentItem, "textNumber", i);
281         QTRY_VERIFY(number != 0);
282         QTRY_COMPARE(number->text(), model.number(i));
283     }
284
285     // set an empty model and confirm that items are destroyed
286     TestModel model2;
287     ctxt->setContextProperty("testModel", &model2);
288
289     int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
290     QTRY_VERIFY(itemCount == 0);
291
292     delete canvas;
293 }
294
295 void tst_QSGGridView::changed()
296 {
297     QSGView *canvas = createView();
298
299     TestModel model;
300     model.addItem("Fred", "12345");
301     model.addItem("John", "2345");
302     model.addItem("Bob", "54321");
303     model.addItem("Billy", "22345");
304     model.addItem("Sam", "2945");
305     model.addItem("Ben", "04321");
306     model.addItem("Jim", "0780");
307
308     QDeclarativeContext *ctxt = canvas->rootContext();
309     ctxt->setContextProperty("testModel", &model);
310     ctxt->setContextProperty("testRightToLeft", QVariant(false));
311     ctxt->setContextProperty("testTopToBottom", QVariant(false));
312
313     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
314     qApp->processEvents();
315
316     QSGFlickable *gridview = findItem<QSGFlickable>(canvas->rootObject(), "grid");
317     QTRY_VERIFY(gridview != 0);
318
319     QSGItem *contentItem = gridview->contentItem();
320     QTRY_VERIFY(contentItem != 0);
321
322     model.modifyItem(1, "Will", "9876");
323     QSGText *name = findItem<QSGText>(contentItem, "textName", 1);
324     QTRY_VERIFY(name != 0);
325     QTRY_COMPARE(name->text(), model.name(1));
326     QSGText *number = findItem<QSGText>(contentItem, "textNumber", 1);
327     QTRY_VERIFY(number != 0);
328     QTRY_COMPARE(number->text(), model.number(1));
329
330     delete canvas;
331 }
332
333 void tst_QSGGridView::inserted()
334 {
335     QSGView *canvas = createView();
336     canvas->show();
337
338     TestModel model;
339     model.addItem("Fred", "12345");
340     model.addItem("John", "2345");
341     model.addItem("Bob", "54321");
342
343     QDeclarativeContext *ctxt = canvas->rootContext();
344     ctxt->setContextProperty("testModel", &model);
345     ctxt->setContextProperty("testRightToLeft", QVariant(false));
346     ctxt->setContextProperty("testTopToBottom", QVariant(false));
347
348     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
349     qApp->processEvents();
350
351     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
352     QTRY_VERIFY(gridview != 0);
353
354     QSGItem *contentItem = gridview->contentItem();
355     QTRY_VERIFY(contentItem != 0);
356
357     model.insertItem(1, "Will", "9876");
358     QCOMPARE(canvas->rootObject()->property("count").toInt(), model.count());
359
360     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
361
362     QSGText *name = findItem<QSGText>(contentItem, "textName", 1);
363     QTRY_VERIFY(name != 0);
364     QTRY_COMPARE(name->text(), model.name(1));
365     QSGText *number = findItem<QSGText>(contentItem, "textNumber", 1);
366     QTRY_VERIFY(number != 0);
367     QTRY_COMPARE(number->text(), model.number(1));
368
369     // Checks that onAdd is called
370     int added = canvas->rootObject()->property("added").toInt();
371     QTRY_COMPARE(added, 1);
372
373     // Confirm items positioned correctly
374     for (int i = 0; i < model.count(); ++i) {
375         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
376         QTRY_COMPARE(item->x(), (i%3)*80.0);
377         QTRY_COMPARE(item->y(), (i/3)*60.0);
378     }
379
380     model.insertItem(0, "Foo", "1111"); // zero index, and current item
381
382     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
383
384     name = findItem<QSGText>(contentItem, "textName", 0);
385     QTRY_VERIFY(name != 0);
386     QTRY_COMPARE(name->text(), model.name(0));
387     number = findItem<QSGText>(contentItem, "textNumber", 0);
388     QTRY_VERIFY(number != 0);
389     QTRY_COMPARE(number->text(), model.number(0));
390
391     QTRY_COMPARE(gridview->currentIndex(), 1);
392
393     // Confirm items positioned correctly
394     for (int i = 0; i < model.count(); ++i) {
395         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
396         QTRY_VERIFY(item->x() == (i%3)*80);
397         QTRY_VERIFY(item->y() == (i/3)*60);
398     }
399
400     for (int i = model.count(); i < 30; ++i)
401         model.insertItem(i, "Hello", QString::number(i));
402
403     gridview->setContentY(120);
404
405     // Insert item outside visible area
406     model.insertItem(1, "Hello", "1324");
407
408     QTRY_VERIFY(gridview->contentY() == 120);
409
410     delete canvas;
411 }
412
413 void tst_QSGGridView::removed()
414 {
415     QSGView *canvas = createView();
416     canvas->show();
417
418     TestModel model;
419     for (int i = 0; i < 40; i++)
420         model.addItem("Item" + QString::number(i), "");
421
422     QDeclarativeContext *ctxt = canvas->rootContext();
423     ctxt->setContextProperty("testModel", &model);
424     ctxt->setContextProperty("testRightToLeft", QVariant(false));
425     ctxt->setContextProperty("testTopToBottom", QVariant(false));
426
427     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
428     qApp->processEvents();
429
430     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
431     QTRY_VERIFY(gridview != 0);
432
433     QSGItem *contentItem = gridview->contentItem();
434     QTRY_VERIFY(contentItem != 0);
435
436     model.removeItem(1);
437     QCOMPARE(canvas->rootObject()->property("count").toInt(), model.count());
438
439     QSGText *name = findItem<QSGText>(contentItem, "textName", 1);
440     QTRY_VERIFY(name != 0);
441     QTRY_COMPARE(name->text(), model.name(1));
442     QSGText *number = findItem<QSGText>(contentItem, "textNumber", 1);
443     QTRY_VERIFY(number != 0);
444     QTRY_COMPARE(number->text(), model.number(1));
445
446     // Checks that onRemove is called
447     QString removed = canvas->rootObject()->property("removed").toString();
448     QTRY_COMPARE(removed, QString("Item1"));
449
450     // Confirm items positioned correctly
451     int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
452     for (int i = 0; i < model.count() && i < itemCount; ++i) {
453         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
454         if (!item) qWarning() << "Item" << i << "not found";
455         QTRY_VERIFY(item);
456         QTRY_VERIFY(item->x() == (i%3)*80);
457         QTRY_VERIFY(item->y() == (i/3)*60);
458     }
459
460     // Remove first item (which is the current item);
461     model.removeItem(0);
462
463     name = findItem<QSGText>(contentItem, "textName", 0);
464     QTRY_VERIFY(name != 0);
465     QTRY_COMPARE(name->text(), model.name(0));
466     number = findItem<QSGText>(contentItem, "textNumber", 0);
467     QTRY_VERIFY(number != 0);
468     QTRY_COMPARE(number->text(), model.number(0));
469
470     // Confirm items positioned correctly
471     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
472     for (int i = 0; i < model.count() && i < itemCount; ++i) {
473         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
474         if (!item) qWarning() << "Item" << i << "not found";
475         QTRY_VERIFY(item);
476         QTRY_VERIFY(item->x() == (i%3)*80);
477         QTRY_VERIFY(item->y() == (i/3)*60);
478     }
479
480     // Remove items not visible
481     model.removeItem(25);
482
483     // Confirm items positioned correctly
484     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
485     for (int i = 0; i < model.count() && i < itemCount; ++i) {
486         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
487         if (!item) qWarning() << "Item" << i << "not found";
488         QTRY_VERIFY(item);
489         QTRY_VERIFY(item->x() == (i%3)*80);
490         QTRY_VERIFY(item->y() == (i/3)*60);
491     }
492
493     // Remove items before visible
494     gridview->setContentY(120);
495     gridview->setCurrentIndex(10);
496
497     // Setting currentIndex above shouldn't cause view to scroll
498     QTRY_COMPARE(gridview->contentY(), 120.0);
499
500     model.removeItem(1);
501
502     // Confirm items positioned correctly
503     for (int i = 6; i < 18; ++i) {
504         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
505         if (!item) qWarning() << "Item" << i << "not found";
506         QTRY_VERIFY(item);
507         QTRY_VERIFY(item->x() == (i%3)*80);
508         QTRY_VERIFY(item->y() == (i/3)*60);
509     }
510
511     // Remove currentIndex
512     QSGItem *oldCurrent = gridview->currentItem();
513     model.removeItem(9);
514
515     QTRY_COMPARE(gridview->currentIndex(), 9);
516     QTRY_VERIFY(gridview->currentItem() != oldCurrent);
517
518     gridview->setContentY(0);
519     // let transitions settle.
520     QTest::qWait(100);
521
522     // Confirm items positioned correctly
523     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
524     for (int i = 0; i < model.count() && i < itemCount; ++i) {
525         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
526         if (!item) qWarning() << "Item" << i << "not found";
527         QTRY_VERIFY(item);
528         QTRY_VERIFY(item->x() == (i%3)*80);
529         QTRY_VERIFY(item->y() == (i/3)*60);
530     }
531
532     // remove item outside current view.
533     gridview->setCurrentIndex(32);
534     gridview->setContentY(240);
535
536     model.removeItem(30);
537     QTRY_VERIFY(gridview->currentIndex() == 31);
538
539     // remove current item beyond visible items.
540     gridview->setCurrentIndex(20);
541     gridview->setContentY(0);
542     model.removeItem(20);
543
544     QTRY_COMPARE(gridview->currentIndex(), 20);
545     QTRY_VERIFY(gridview->currentItem() != 0);
546
547     // remove item before current, but visible
548     gridview->setCurrentIndex(8);
549     gridview->setContentY(240);
550     oldCurrent = gridview->currentItem();
551     model.removeItem(6);
552
553     QTRY_COMPARE(gridview->currentIndex(), 7);
554     QTRY_VERIFY(gridview->currentItem() == oldCurrent);
555
556     delete canvas;
557 }
558
559 void tst_QSGGridView::clear()
560 {
561     QSGView *canvas = createView();
562
563     TestModel model;
564     for (int i = 0; i < 30; i++)
565         model.addItem("Item" + QString::number(i), "");
566
567     QDeclarativeContext *ctxt = canvas->rootContext();
568     ctxt->setContextProperty("testModel", &model);
569     ctxt->setContextProperty("testRightToLeft", QVariant(false));
570     ctxt->setContextProperty("testTopToBottom", QVariant(false));
571
572     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
573     qApp->processEvents();
574
575     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
576     QVERIFY(gridview != 0);
577
578     QSGItem *contentItem = gridview->contentItem();
579     QVERIFY(contentItem != 0);
580
581     model.clear();
582
583     QVERIFY(gridview->count() == 0);
584     QVERIFY(gridview->currentItem() == 0);
585     QVERIFY(gridview->contentY() == 0);
586     QVERIFY(gridview->currentIndex() == -1);
587
588     // confirm sanity when adding an item to cleared list
589     model.addItem("New", "1");
590     QVERIFY(gridview->count() == 1);
591     QVERIFY(gridview->currentItem() != 0);
592     QVERIFY(gridview->currentIndex() == 0);
593
594     delete canvas;
595 }
596
597 void tst_QSGGridView::moved()
598 {
599     QFETCH(qreal, contentY);
600     QFETCH(int, from);
601     QFETCH(int, to);
602     QFETCH(int, count);
603     QFETCH(qreal, itemsOffsetAfterMove);
604
605     QSGText *name;
606     QSGText *number;
607     QSGView *canvas = createView();
608     canvas->show();
609
610     TestModel model;
611     for (int i = 0; i < 30; i++)
612         model.addItem("Item" + QString::number(i), "");
613
614     QDeclarativeContext *ctxt = canvas->rootContext();
615     ctxt->setContextProperty("testModel", &model);
616     ctxt->setContextProperty("testRightToLeft", QVariant(false));
617     ctxt->setContextProperty("testTopToBottom", QVariant(false));
618
619     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
620     qApp->processEvents();
621
622     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
623     QTRY_VERIFY(gridview != 0);
624
625     QSGItem *contentItem = gridview->contentItem();
626     QTRY_VERIFY(contentItem != 0);
627
628     QSGItem *currentItem = gridview->currentItem();
629     QTRY_VERIFY(currentItem != 0);
630
631     gridview->setContentY(contentY);
632     model.moveItems(from, to, count);
633
634     // Confirm items positioned correctly and indexes correct
635     int firstVisibleIndex = qCeil(contentY / 60.0) * 3;
636     int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
637
638     for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
639         if (i >= firstVisibleIndex + 18)    // index has moved out of view
640             continue;
641         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
642         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
643
644         QTRY_COMPARE(item->x(), (i%3)*80.0);
645         QTRY_COMPARE(item->y(), (i/3)*60.0 + itemsOffsetAfterMove);
646
647         name = findItem<QSGText>(contentItem, "textName", i);
648         QVERIFY(name != 0);
649         QTRY_COMPARE(name->text(), model.name(i));
650         number = findItem<QSGText>(contentItem, "textNumber", i);
651         QVERIFY(number != 0);
652         QTRY_COMPARE(number->text(), model.number(i));
653
654         // current index should have been updated
655         if (item == currentItem)
656             QTRY_COMPARE(gridview->currentIndex(), i);
657     }
658
659     delete canvas;
660 }
661
662 void tst_QSGGridView::moved_data()
663 {
664     QTest::addColumn<qreal>("contentY");
665     QTest::addColumn<int>("from");
666     QTest::addColumn<int>("to");
667     QTest::addColumn<int>("count");
668     QTest::addColumn<qreal>("itemsOffsetAfterMove");
669
670     // model starts with 30 items, each 80x60, in area 240x320
671     // 18 items should be visible at a time
672
673     QTest::newRow("move 1 forwards, within visible items")
674             << 0.0
675             << 1 << 8 << 1
676             << 0.0;
677
678     QTest::newRow("move 1 forwards, from non-visible -> visible")
679             << 120.0     // show 6-23
680             << 1 << 23 << 1
681             << 0.0;
682
683     QTest::newRow("move 1 forwards, from non-visible -> visible (move first item)")
684             << 120.0     // // show 6-23
685             << 0 << 6 << 1
686             << 0.0;
687
688     QTest::newRow("move 1 forwards, from visible -> non-visible")
689             << 0.0
690             << 1 << 20 << 1
691             << 0.0;
692
693     QTest::newRow("move 1 forwards, from visible -> non-visible (move first item)")
694             << 0.0
695             << 0 << 20 << 1
696             << 0.0;
697
698
699     QTest::newRow("move 1 backwards, within visible items")
700             << 0.0
701             << 10 << 5 << 1
702             << 0.0;
703
704     QTest::newRow("move 1 backwards, from non-visible -> visible")
705             << 0.0
706             << 28 << 8 << 1
707             << 0.0;
708
709     QTest::newRow("move 1 backwards, from non-visible -> visible (move last item)")
710             << 0.0
711             << 29 << 14 << 1
712             << 0.0;
713
714     QTest::newRow("move 1 backwards, from visible -> non-visible")
715             << 120.0     // show 6-23
716             << 7 << 1 << 1
717             << 60.0 * 2;   // this results in a forward movement that removes 6 items (2 rows)
718
719     QTest::newRow("move 1 backwards, from visible -> non-visible (move first item)")
720             << 120.0     // show 6-23
721             << 7 << 0 << 1
722             << 60.0 * 2;     // first visible item moved, so all items shift 2 rows down with it
723
724
725     QTest::newRow("move multiple forwards, within visible items")
726             << 0.0
727             << 0 << 5 << 3
728             << 60.0;    // moved 3 items (i.e. 1 row) down
729
730     QTest::newRow("move multiple forwards, from non-visible -> visible")
731             << 120.0     // show 6-23
732             << 1 << 6 << 3
733             << 60.0;    // top row moved, all items should shift down by 1 row
734
735     QTest::newRow("move multiple forwards, from non-visible -> visible (move first item)")
736             << 120.0     // show 6-23
737             << 0 << 6 << 3
738             << 60.0;    // top row moved, all items should shift down by 1 row
739
740     QTest::newRow("move multiple forwards, from visible -> non-visible")
741             << 0.0
742             << 1 << 16 << 3
743             << 0.0;
744
745     QTest::newRow("move multiple forwards, from visible -> non-visible (move first item)")
746             << 0.0
747             << 0 << 16 << 3
748             << 60.0;
749
750
751     QTest::newRow("move multiple backwards, within visible items")
752             << 0.0
753             << 4 << 1 << 3
754             << 0.0;
755
756     QTest::newRow("move multiple backwards, from non-visible -> visible")
757             << 0.0
758             << 20 << 4 << 3
759             << 0.0;
760
761     QTest::newRow("move multiple backwards, from non-visible -> visible (move last item)")
762             << 0.0
763             << 27 << 10 << 3
764             << 0.0;
765
766     QTest::newRow("move multiple backwards, from visible -> non-visible")
767             << 120.0     // show 6-23
768             << 16 << 1 << 3
769             << 60.0 * 5;   // this results in a forward movement that removes 15 items (5 rows)
770
771     QTest::newRow("move multiple backwards, from visible -> non-visible (move first item)")
772             << 120.0     // show 6-23
773             << 16 << 0 << 3
774             << 60.0 * 5;    // this results in a forward movement that removes 16 items (5 rows)
775 }
776
777 void tst_QSGGridView::swapWithFirstItem()
778 {
779     // QTBUG_9697
780     QSGView *canvas = createView();
781     canvas->show();
782
783     TestModel model;
784     for (int i = 0; i < 30; i++)
785         model.addItem("Item" + QString::number(i), "");
786
787     QDeclarativeContext *ctxt = canvas->rootContext();
788     ctxt->setContextProperty("testModel", &model);
789     ctxt->setContextProperty("testRightToLeft", QVariant(false));
790     ctxt->setContextProperty("testTopToBottom", QVariant(false));
791
792     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
793     qApp->processEvents();
794
795     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
796     QTRY_VERIFY(gridview != 0);
797
798     // ensure content position is stable
799     gridview->setContentY(0);
800     model.moveItem(10, 0);
801     QTRY_VERIFY(gridview->contentY() == 0);
802
803     delete canvas;
804 }
805
806 void tst_QSGGridView::currentIndex()
807 {
808     TestModel model;
809     for (int i = 0; i < 60; i++)
810         model.addItem("Item" + QString::number(i), QString::number(i));
811
812     QSGView *canvas = new QSGView(0);
813     canvas->setFixedSize(240,320);
814     canvas->show();
815
816     QDeclarativeContext *ctxt = canvas->rootContext();
817     ctxt->setContextProperty("testModel", &model);
818
819     QString filename(SRCDIR "/data/gridview-initCurrent.qml");
820     canvas->setSource(QUrl::fromLocalFile(filename));
821
822     qApp->processEvents();
823
824     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
825     QVERIFY(gridview != 0);
826
827     QSGItem *contentItem = gridview->contentItem();
828     QVERIFY(contentItem != 0);
829
830     // current item should be third item
831     QCOMPARE(gridview->currentIndex(), 35);
832     QCOMPARE(gridview->currentItem(), findItem<QSGItem>(contentItem, "wrapper", 35));
833     QCOMPARE(gridview->currentItem()->y(), gridview->highlightItem()->y());
834     QCOMPARE(gridview->contentY(), 400.0);
835
836     gridview->moveCurrentIndexRight();
837     QCOMPARE(gridview->currentIndex(), 36);
838     gridview->moveCurrentIndexDown();
839     QCOMPARE(gridview->currentIndex(), 39);
840     gridview->moveCurrentIndexUp();
841     QCOMPARE(gridview->currentIndex(), 36);
842     gridview->moveCurrentIndexLeft();
843     QCOMPARE(gridview->currentIndex(), 35);
844
845     // no wrap
846     gridview->setCurrentIndex(0);
847     QCOMPARE(gridview->currentIndex(), 0);
848     // confirm that the velocity is updated
849     QTRY_VERIFY(gridview->verticalVelocity() != 0.0);
850
851     gridview->moveCurrentIndexUp();
852     QCOMPARE(gridview->currentIndex(), 0);
853
854     gridview->moveCurrentIndexLeft();
855     QCOMPARE(gridview->currentIndex(), 0);
856
857     gridview->setCurrentIndex(model.count()-1);
858     QCOMPARE(gridview->currentIndex(), model.count()-1);
859
860     gridview->moveCurrentIndexRight();
861     QCOMPARE(gridview->currentIndex(), model.count()-1);
862
863     gridview->moveCurrentIndexDown();
864     QCOMPARE(gridview->currentIndex(), model.count()-1);
865
866     // with wrap
867     gridview->setWrapEnabled(true);
868
869     gridview->setCurrentIndex(0);
870     QCOMPARE(gridview->currentIndex(), 0);
871
872     gridview->moveCurrentIndexLeft();
873     QCOMPARE(gridview->currentIndex(), model.count()-1);
874
875     qApp->processEvents();
876     QTRY_COMPARE(gridview->contentY(), 880.0);
877
878     gridview->moveCurrentIndexRight();
879     QCOMPARE(gridview->currentIndex(), 0);
880
881     QTRY_COMPARE(gridview->contentY(), 0.0);
882
883
884     // footer should become visible if it is out of view, and then current index moves to the first row
885     canvas->rootObject()->setProperty("showFooter", true);
886     QTRY_VERIFY(gridview->footerItem());
887     gridview->setCurrentIndex(model.count()-3);
888     QTRY_VERIFY(gridview->footerItem()->y() > gridview->contentY() + gridview->height());
889     gridview->setCurrentIndex(model.count()-2);
890     QTRY_COMPARE(gridview->contentY() + gridview->height(), (60.0 * model.count()/3) + gridview->footerItem()->height());
891     canvas->rootObject()->setProperty("showFooter", false);
892
893     // header should become visible if it is out of view, and then current index moves to the last row
894     canvas->rootObject()->setProperty("showHeader", true);
895     QTRY_VERIFY(gridview->headerItem());
896     gridview->setCurrentIndex(3);
897     QTRY_VERIFY(gridview->headerItem()->y() + gridview->headerItem()->height() < gridview->contentY());
898     gridview->setCurrentIndex(1);
899     QTRY_COMPARE(gridview->contentY(), -gridview->headerItem()->height());
900     canvas->rootObject()->setProperty("showHeader", false);
901
902
903     // Test keys
904     qApp->setActiveWindow(canvas);
905 #ifdef Q_WS_X11
906     // to be safe and avoid failing setFocus with window managers
907     qt_x11_wait_for_window_manager(canvas);
908 #endif
909     QTRY_VERIFY(canvas->hasFocus());
910     qApp->processEvents();
911
912     gridview->setCurrentIndex(0);
913
914     QTest::keyClick(canvas, Qt::Key_Down);
915     QCOMPARE(gridview->currentIndex(), 3);
916
917     QTest::keyClick(canvas, Qt::Key_Up);
918     QCOMPARE(gridview->currentIndex(), 0);
919
920     // hold down Key_Down
921     for (int i=0; i<(model.count() / 3) - 1; i++) {
922         QTest::simulateEvent(canvas, true, Qt::Key_Down, Qt::NoModifier, "", true);
923         QTRY_COMPARE(gridview->currentIndex(), i*3 + 3);
924     }
925     QTest::keyRelease(canvas, Qt::Key_Down);
926     QTRY_COMPARE(gridview->currentIndex(), 57);
927     QTRY_COMPARE(gridview->contentY(), 880.0);
928
929     // hold down Key_Up
930     for (int i=(model.count() / 3) - 1; i > 0; i--) {
931         QTest::simulateEvent(canvas, true, Qt::Key_Up, Qt::NoModifier, "", true);
932         QTRY_COMPARE(gridview->currentIndex(), i*3 - 3);
933     }
934     QTest::keyRelease(canvas, Qt::Key_Up);
935     QTRY_COMPARE(gridview->currentIndex(), 0);
936     QTRY_COMPARE(gridview->contentY(), 0.0);
937
938
939     gridview->setFlow(QSGGridView::TopToBottom);
940
941     qApp->setActiveWindow(canvas);
942 #ifdef Q_WS_X11
943     // to be safe and avoid failing setFocus with window managers
944     qt_x11_wait_for_window_manager(canvas);
945 #endif
946     QTRY_VERIFY(canvas->hasFocus());
947     qApp->processEvents();
948
949     QTest::keyClick(canvas, Qt::Key_Right);
950     QCOMPARE(gridview->currentIndex(), 5);
951
952     QTest::keyClick(canvas, Qt::Key_Left);
953     QCOMPARE(gridview->currentIndex(), 0);
954
955     QTest::keyClick(canvas, Qt::Key_Down);
956     QCOMPARE(gridview->currentIndex(), 1);
957
958     QTest::keyClick(canvas, Qt::Key_Up);
959     QCOMPARE(gridview->currentIndex(), 0);
960
961     // hold down Key_Right
962     for (int i=0; i<(model.count() / 5) - 1; i++) {
963         QTest::simulateEvent(canvas, true, Qt::Key_Right, Qt::NoModifier, "", true);
964         QTRY_COMPARE(gridview->currentIndex(), i*5 + 5);
965     }
966     QTest::keyRelease(canvas, Qt::Key_Right);
967     QTRY_COMPARE(gridview->currentIndex(), 55);
968     QTRY_COMPARE(gridview->contentX(), 720.0);
969
970     // hold down Key_Left
971     for (int i=(model.count() / 5) - 1; i > 0; i--) {
972         QTest::simulateEvent(canvas, true, Qt::Key_Left, Qt::NoModifier, "", true);
973         QTRY_COMPARE(gridview->currentIndex(), i*5 - 5);
974     }
975     QTest::keyRelease(canvas, Qt::Key_Left);
976     QTRY_COMPARE(gridview->currentIndex(), 0);
977     QTRY_COMPARE(gridview->contentX(), 0.0);
978
979
980     // turn off auto highlight
981     gridview->setHighlightFollowsCurrentItem(false);
982     QVERIFY(gridview->highlightFollowsCurrentItem() == false);
983     QVERIFY(gridview->highlightItem());
984     qreal hlPosX = gridview->highlightItem()->x();
985     qreal hlPosY = gridview->highlightItem()->y();
986
987     gridview->setCurrentIndex(5);
988     QTRY_COMPARE(gridview->highlightItem()->x(), hlPosX);
989     QTRY_COMPARE(gridview->highlightItem()->y(), hlPosY);
990
991     // insert item before currentIndex
992     gridview->setCurrentIndex(28);
993     model.insertItem(0, "Foo", "1111");
994     QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29);
995
996     // check removing highlight by setting currentIndex to -1;
997     gridview->setCurrentIndex(-1);
998
999     QCOMPARE(gridview->currentIndex(), -1);
1000     QVERIFY(!gridview->highlightItem());
1001     QVERIFY(!gridview->currentItem());
1002
1003     gridview->setHighlightFollowsCurrentItem(true);
1004
1005     gridview->setFlow(QSGGridView::LeftToRight);
1006     gridview->setLayoutDirection(Qt::RightToLeft);
1007
1008     qApp->setActiveWindow(canvas);
1009 #ifdef Q_WS_X11
1010     // to be safe and avoid failing setFocus with window managers
1011     qt_x11_wait_for_window_manager(canvas);
1012 #endif
1013     QTRY_VERIFY(canvas->hasFocus());
1014     qApp->processEvents();
1015
1016     gridview->setCurrentIndex(35);
1017
1018     QTest::keyClick(canvas, Qt::Key_Right);
1019     QCOMPARE(gridview->currentIndex(), 34);
1020
1021     QTest::keyClick(canvas, Qt::Key_Down);
1022     QCOMPARE(gridview->currentIndex(), 37);
1023
1024     QTest::keyClick(canvas, Qt::Key_Up);
1025     QCOMPARE(gridview->currentIndex(), 34);
1026
1027     QTest::keyClick(canvas, Qt::Key_Left);
1028     QCOMPARE(gridview->currentIndex(), 35);
1029
1030
1031     // turn off auto highlight
1032     gridview->setHighlightFollowsCurrentItem(false);
1033     QVERIFY(gridview->highlightFollowsCurrentItem() == false);
1034     QVERIFY(gridview->highlightItem());
1035     hlPosX = gridview->highlightItem()->x();
1036     hlPosY = gridview->highlightItem()->y();
1037
1038     gridview->setCurrentIndex(5);
1039     QTRY_COMPARE(gridview->highlightItem()->x(), hlPosX);
1040     QTRY_COMPARE(gridview->highlightItem()->y(), hlPosY);
1041
1042     // insert item before currentIndex
1043     gridview->setCurrentIndex(28);
1044     model.insertItem(0, "Foo", "1111");
1045     QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29);
1046
1047     // check removing highlight by setting currentIndex to -1;
1048     gridview->setCurrentIndex(-1);
1049
1050     QCOMPARE(gridview->currentIndex(), -1);
1051     QVERIFY(!gridview->highlightItem());
1052     QVERIFY(!gridview->currentItem());
1053
1054     delete canvas;
1055 }
1056
1057 void tst_QSGGridView::noCurrentIndex()
1058 {
1059     TestModel model;
1060     for (int i = 0; i < 60; i++)
1061         model.addItem("Item" + QString::number(i), QString::number(i));
1062
1063     QSGView *canvas = new QSGView(0);
1064     canvas->setFixedSize(240,320);
1065
1066     QDeclarativeContext *ctxt = canvas->rootContext();
1067     ctxt->setContextProperty("testModel", &model);
1068
1069     QString filename(SRCDIR "/data/gridview-noCurrent.qml");
1070     canvas->setSource(QUrl::fromLocalFile(filename));
1071
1072     qApp->processEvents();
1073
1074     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
1075     QVERIFY(gridview != 0);
1076
1077     QSGItem *contentItem = gridview->contentItem();
1078     QVERIFY(contentItem != 0);
1079
1080     // current index should be -1
1081     QCOMPARE(gridview->currentIndex(), -1);
1082     QVERIFY(!gridview->currentItem());
1083     QVERIFY(!gridview->highlightItem());
1084     QCOMPARE(gridview->contentY(), 0.0);
1085
1086     gridview->setCurrentIndex(5);
1087     QCOMPARE(gridview->currentIndex(), 5);
1088     QVERIFY(gridview->currentItem());
1089     QVERIFY(gridview->highlightItem());
1090
1091     delete canvas;
1092 }
1093
1094 void tst_QSGGridView::changeFlow()
1095 {
1096     QSGView *canvas = createView();
1097
1098     TestModel model;
1099     for (int i = 0; i < 30; i++)
1100         model.addItem("Item" + QString::number(i), QString::number(i));
1101
1102     QDeclarativeContext *ctxt = canvas->rootContext();
1103     ctxt->setContextProperty("testModel", &model);
1104     ctxt->setContextProperty("testRightToLeft", QVariant(false));
1105     ctxt->setContextProperty("testTopToBottom", QVariant(false));
1106
1107     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
1108     qApp->processEvents();
1109
1110     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
1111     QTRY_VERIFY(gridview != 0);
1112
1113     QSGItem *contentItem = gridview->contentItem();
1114     QTRY_VERIFY(contentItem != 0);
1115
1116     // Confirm items positioned correctly and indexes correct
1117     int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
1118     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1119         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
1120         if (!item) qWarning() << "Item" << i << "not found";
1121         QTRY_VERIFY(item);
1122         QTRY_COMPARE(item->x(), qreal((i%3)*80));
1123         QTRY_COMPARE(item->y(), qreal((i/3)*60));
1124         QSGText *name = findItem<QSGText>(contentItem, "textName", i);
1125         QTRY_VERIFY(name != 0);
1126         QTRY_COMPARE(name->text(), model.name(i));
1127         QSGText *number = findItem<QSGText>(contentItem, "textNumber", i);
1128         QTRY_VERIFY(number != 0);
1129         QTRY_COMPARE(number->text(), model.number(i));
1130     }
1131
1132     ctxt->setContextProperty("testTopToBottom", QVariant(true));
1133
1134     // Confirm items positioned correctly and indexes correct
1135     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
1136     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1137         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
1138         if (!item) qWarning() << "Item" << i << "not found";
1139         QTRY_VERIFY(item);
1140         QTRY_COMPARE(item->x(), qreal((i/5)*80));
1141         QTRY_COMPARE(item->y(), qreal((i%5)*60));
1142         QSGText *name = findItem<QSGText>(contentItem, "textName", i);
1143         QTRY_VERIFY(name != 0);
1144         QTRY_COMPARE(name->text(), model.name(i));
1145         QSGText *number = findItem<QSGText>(contentItem, "textNumber", i);
1146         QTRY_VERIFY(number != 0);
1147         QTRY_COMPARE(number->text(), model.number(i));
1148     }
1149
1150     ctxt->setContextProperty("testRightToLeft", QVariant(true));
1151
1152     // Confirm items positioned correctly and indexes correct
1153     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
1154     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1155         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
1156         if (!item) qWarning() << "Item" << i << "not found";
1157         QTRY_VERIFY(item);
1158         QTRY_COMPARE(item->x(), qreal(-(i/5)*80 - item->width()));
1159         QTRY_COMPARE(item->y(), qreal((i%5)*60));
1160         QSGText *name = findItem<QSGText>(contentItem, "textName", i);
1161         QTRY_VERIFY(name != 0);
1162         QTRY_COMPARE(name->text(), model.name(i));
1163         QSGText *number = findItem<QSGText>(contentItem, "textNumber", i);
1164         QTRY_VERIFY(number != 0);
1165         QTRY_COMPARE(number->text(), model.number(i));
1166     }
1167     gridview->setContentX(100);
1168     QTRY_COMPARE(gridview->contentX(), 100.);
1169     ctxt->setContextProperty("testTopToBottom", QVariant(false));
1170     QTRY_COMPARE(gridview->contentX(), 0.);
1171
1172     // Confirm items positioned correctly and indexes correct
1173     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
1174     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1175         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
1176         if (!item) qWarning() << "Item" << i << "not found";
1177         QTRY_VERIFY(item);
1178         QTRY_COMPARE(item->x(), qreal(240 - (i%3+1)*80));
1179         QTRY_COMPARE(item->y(), qreal((i/3)*60));
1180         QSGText *name = findItem<QSGText>(contentItem, "textName", i);
1181         QTRY_VERIFY(name != 0);
1182         QTRY_COMPARE(name->text(), model.name(i));
1183         QSGText *number = findItem<QSGText>(contentItem, "textNumber", i);
1184         QTRY_VERIFY(number != 0);
1185         QTRY_COMPARE(number->text(), model.number(i));
1186     }
1187
1188     delete canvas;
1189 }
1190
1191 void tst_QSGGridView::defaultValues()
1192 {
1193     QDeclarativeEngine engine;
1194     QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/gridview3.qml"));
1195     QSGGridView *obj = qobject_cast<QSGGridView*>(c.create());
1196
1197     QTRY_VERIFY(obj != 0);
1198     QTRY_VERIFY(obj->model() == QVariant());
1199     QTRY_VERIFY(obj->delegate() == 0);
1200     QTRY_COMPARE(obj->currentIndex(), -1);
1201     QTRY_VERIFY(obj->currentItem() == 0);
1202     QTRY_COMPARE(obj->count(), 0);
1203     QTRY_VERIFY(obj->highlight() == 0);
1204     QTRY_VERIFY(obj->highlightItem() == 0);
1205     QTRY_COMPARE(obj->highlightFollowsCurrentItem(), true);
1206     QTRY_VERIFY(obj->flow() == 0);
1207     QTRY_COMPARE(obj->isWrapEnabled(), false);
1208     QTRY_COMPARE(obj->cacheBuffer(), 0);
1209     QTRY_COMPARE(obj->cellWidth(), 100); //### Should 100 be the default?
1210     QTRY_COMPARE(obj->cellHeight(), 100);
1211     delete obj;
1212 }
1213
1214 void tst_QSGGridView::properties()
1215 {
1216     QDeclarativeEngine engine;
1217     QDeclarativeComponent c(&engine, QUrl::fromLocalFile(SRCDIR "/data/gridview2.qml"));
1218     QSGGridView *obj = qobject_cast<QSGGridView*>(c.create());
1219
1220     QTRY_VERIFY(obj != 0);
1221     QTRY_VERIFY(obj->model() != QVariant());
1222     QTRY_VERIFY(obj->delegate() != 0);
1223     QTRY_COMPARE(obj->currentIndex(), 0);
1224     QTRY_VERIFY(obj->currentItem() != 0);
1225     QTRY_COMPARE(obj->count(), 4);
1226     QTRY_VERIFY(obj->highlight() != 0);
1227     QTRY_VERIFY(obj->highlightItem() != 0);
1228     QTRY_COMPARE(obj->highlightFollowsCurrentItem(), false);
1229     QTRY_VERIFY(obj->flow() == 0);
1230     QTRY_COMPARE(obj->isWrapEnabled(), true);
1231     QTRY_COMPARE(obj->cacheBuffer(), 200);
1232     QTRY_COMPARE(obj->cellWidth(), 100);
1233     QTRY_COMPARE(obj->cellHeight(), 100);
1234     delete obj;
1235 }
1236
1237 void tst_QSGGridView::propertyChanges()
1238 {
1239     QSGView *canvas = createView();
1240     QTRY_VERIFY(canvas);
1241     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychangestest.qml"));
1242
1243     QSGGridView *gridView = canvas->rootObject()->findChild<QSGGridView*>("gridView");
1244     QTRY_VERIFY(gridView);
1245
1246     QSignalSpy keyNavigationWrapsSpy(gridView, SIGNAL(keyNavigationWrapsChanged()));
1247     QSignalSpy cacheBufferSpy(gridView, SIGNAL(cacheBufferChanged()));
1248     QSignalSpy layoutSpy(gridView, SIGNAL(layoutDirectionChanged()));
1249     QSignalSpy flowSpy(gridView, SIGNAL(flowChanged()));
1250
1251     QTRY_COMPARE(gridView->isWrapEnabled(), true);
1252     QTRY_COMPARE(gridView->cacheBuffer(), 10);
1253     QTRY_COMPARE(gridView->flow(), QSGGridView::LeftToRight);
1254
1255     gridView->setWrapEnabled(false);
1256     gridView->setCacheBuffer(3);
1257     gridView->setFlow(QSGGridView::TopToBottom);
1258
1259     QTRY_COMPARE(gridView->isWrapEnabled(), false);
1260     QTRY_COMPARE(gridView->cacheBuffer(), 3);
1261     QTRY_COMPARE(gridView->flow(), QSGGridView::TopToBottom);
1262
1263     QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
1264     QTRY_COMPARE(cacheBufferSpy.count(),1);
1265     QTRY_COMPARE(flowSpy.count(),1);
1266
1267     gridView->setWrapEnabled(false);
1268     gridView->setCacheBuffer(3);
1269     gridView->setFlow(QSGGridView::TopToBottom);
1270
1271     QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
1272     QTRY_COMPARE(cacheBufferSpy.count(),1);
1273     QTRY_COMPARE(flowSpy.count(),1);
1274
1275     gridView->setFlow(QSGGridView::LeftToRight);
1276     QTRY_COMPARE(gridView->flow(), QSGGridView::LeftToRight);
1277
1278     gridView->setWrapEnabled(true);
1279     gridView->setCacheBuffer(5);
1280     gridView->setLayoutDirection(Qt::RightToLeft);
1281
1282     QTRY_COMPARE(gridView->isWrapEnabled(), true);
1283     QTRY_COMPARE(gridView->cacheBuffer(), 5);
1284     QTRY_COMPARE(gridView->layoutDirection(), Qt::RightToLeft);
1285
1286     QTRY_COMPARE(keyNavigationWrapsSpy.count(),2);
1287     QTRY_COMPARE(cacheBufferSpy.count(),2);
1288     QTRY_COMPARE(layoutSpy.count(),1);
1289     QTRY_COMPARE(flowSpy.count(),2);
1290
1291     gridView->setWrapEnabled(true);
1292     gridView->setCacheBuffer(5);
1293     gridView->setLayoutDirection(Qt::RightToLeft);
1294
1295     QTRY_COMPARE(keyNavigationWrapsSpy.count(),2);
1296     QTRY_COMPARE(cacheBufferSpy.count(),2);
1297     QTRY_COMPARE(layoutSpy.count(),1);
1298     QTRY_COMPARE(flowSpy.count(),2);
1299
1300     gridView->setFlow(QSGGridView::TopToBottom);
1301     QTRY_COMPARE(gridView->flow(), QSGGridView::TopToBottom);
1302     QTRY_COMPARE(flowSpy.count(),3);
1303
1304     gridView->setFlow(QSGGridView::TopToBottom);
1305     QTRY_COMPARE(flowSpy.count(),3);
1306
1307     delete canvas;
1308 }
1309
1310 void tst_QSGGridView::componentChanges()
1311 {
1312     QSGView *canvas = createView();
1313     QTRY_VERIFY(canvas);
1314     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychangestest.qml"));
1315
1316     QSGGridView *gridView = canvas->rootObject()->findChild<QSGGridView*>("gridView");
1317     QTRY_VERIFY(gridView);
1318
1319     QDeclarativeComponent component(canvas->engine());
1320     component.setData("import QtQuick 1.0; Rectangle { color: \"blue\"; }", QUrl::fromLocalFile(""));
1321
1322     QDeclarativeComponent delegateComponent(canvas->engine());
1323     delegateComponent.setData("import QtQuick 1.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
1324
1325     QSignalSpy highlightSpy(gridView, SIGNAL(highlightChanged()));
1326     QSignalSpy delegateSpy(gridView, SIGNAL(delegateChanged()));
1327     QSignalSpy headerSpy(gridView, SIGNAL(headerChanged()));
1328     QSignalSpy footerSpy(gridView, SIGNAL(footerChanged()));
1329
1330     gridView->setHighlight(&component);
1331     gridView->setDelegate(&delegateComponent);
1332     gridView->setHeader(&component);
1333     gridView->setFooter(&component);
1334
1335     QTRY_COMPARE(gridView->highlight(), &component);
1336     QTRY_COMPARE(gridView->delegate(), &delegateComponent);
1337     QTRY_COMPARE(gridView->header(), &component);
1338     QTRY_COMPARE(gridView->footer(), &component);
1339
1340     QTRY_COMPARE(highlightSpy.count(),1);
1341     QTRY_COMPARE(delegateSpy.count(),1);
1342     QTRY_COMPARE(headerSpy.count(),1);
1343     QTRY_COMPARE(footerSpy.count(),1);
1344
1345     gridView->setHighlight(&component);
1346     gridView->setDelegate(&delegateComponent);
1347     gridView->setHeader(&component);
1348     gridView->setFooter(&component);
1349
1350     QTRY_COMPARE(highlightSpy.count(),1);
1351     QTRY_COMPARE(delegateSpy.count(),1);
1352     QTRY_COMPARE(headerSpy.count(),1);
1353     QTRY_COMPARE(footerSpy.count(),1);
1354
1355     delete canvas;
1356 }
1357
1358 void tst_QSGGridView::modelChanges()
1359 {
1360     QSGView *canvas = createView();
1361     QTRY_VERIFY(canvas);
1362     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/propertychangestest.qml"));
1363
1364     QSGGridView *gridView = canvas->rootObject()->findChild<QSGGridView*>("gridView");
1365     QTRY_VERIFY(gridView);
1366
1367     QDeclarativeListModel *alternateModel = canvas->rootObject()->findChild<QDeclarativeListModel*>("alternateModel");
1368     QTRY_VERIFY(alternateModel);
1369     QVariant modelVariant = QVariant::fromValue<QObject *>(alternateModel);
1370     QSignalSpy modelSpy(gridView, SIGNAL(modelChanged()));
1371
1372     gridView->setModel(modelVariant);
1373     QTRY_COMPARE(gridView->model(), modelVariant);
1374     QTRY_COMPARE(modelSpy.count(),1);
1375
1376     gridView->setModel(modelVariant);
1377     QTRY_COMPARE(modelSpy.count(),1);
1378
1379     gridView->setModel(QVariant());
1380     QTRY_COMPARE(modelSpy.count(),2);
1381     delete canvas;
1382 }
1383
1384 void tst_QSGGridView::positionViewAtIndex()
1385 {
1386     QSGView *canvas = createView();
1387
1388     TestModel model;
1389     for (int i = 0; i < 40; i++)
1390         model.addItem("Item" + QString::number(i), "");
1391
1392     QDeclarativeContext *ctxt = canvas->rootContext();
1393     ctxt->setContextProperty("testModel", &model);
1394     ctxt->setContextProperty("testRightToLeft", QVariant(false));
1395     ctxt->setContextProperty("testTopToBottom", QVariant(false));
1396
1397     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
1398     qApp->processEvents();
1399
1400     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
1401     QTRY_VERIFY(gridview != 0);
1402
1403     QSGItem *contentItem = gridview->contentItem();
1404     QTRY_VERIFY(contentItem != 0);
1405
1406     // Confirm items positioned correctly
1407     int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
1408     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
1409         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
1410         if (!item) qWarning() << "Item" << i << "not found";
1411         QTRY_VERIFY(item);
1412         QTRY_COMPARE(item->x(), (i%3)*80.);
1413         QTRY_COMPARE(item->y(), (i/3)*60.);
1414     }
1415
1416     // Position on a currently visible item
1417     gridview->positionViewAtIndex(4, QSGGridView::Beginning);
1418     QTRY_COMPARE(gridview->indexAt(120, 90), 4);
1419     QTRY_COMPARE(gridview->contentY(), 60.);
1420
1421     // Confirm items positioned correctly
1422     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
1423     for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
1424         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
1425         if (!item) qWarning() << "Item" << i << "not found";
1426         QTRY_VERIFY(item);
1427         QTRY_COMPARE(item->x(), (i%3)*80.);
1428         QTRY_COMPARE(item->y(), (i/3)*60.);
1429     }
1430
1431     // Position on an item beyond the visible items
1432     gridview->positionViewAtIndex(21, QSGGridView::Beginning);
1433     QTRY_COMPARE(gridview->indexAt(40, 450), 21);
1434     QTRY_COMPARE(gridview->contentY(), 420.);
1435
1436     // Confirm items positioned correctly
1437     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
1438     for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
1439         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
1440         if (!item) qWarning() << "Item" << i << "not found";
1441         QTRY_VERIFY(item);
1442         QTRY_COMPARE(item->x(), (i%3)*80.);
1443         QTRY_COMPARE(item->y(), (i/3)*60.);
1444     }
1445
1446     // Position on an item that would leave empty space if positioned at the top
1447     gridview->positionViewAtIndex(31, QSGGridView::Beginning);
1448     QTRY_COMPARE(gridview->indexAt(120, 630), 31);
1449     QTRY_COMPARE(gridview->contentY(), 520.);
1450
1451     // Confirm items positioned correctly
1452     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
1453     for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
1454         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
1455         if (!item) qWarning() << "Item" << i << "not found";
1456         QTRY_VERIFY(item);
1457         QTRY_COMPARE(item->x(), (i%3)*80.);
1458         QTRY_COMPARE(item->y(), (i/3)*60.);
1459     }
1460
1461     // Position at the beginning again
1462     gridview->positionViewAtIndex(0, QSGGridView::Beginning);
1463     QTRY_COMPARE(gridview->indexAt(0, 0), 0);
1464     QTRY_COMPARE(gridview->indexAt(40, 30), 0);
1465     QTRY_COMPARE(gridview->indexAt(80, 60), 4);
1466     QTRY_COMPARE(gridview->contentY(), 0.);
1467
1468     // Confirm items positioned correctly
1469     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
1470     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
1471         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
1472         if (!item) qWarning() << "Item" << i << "not found";
1473         QTRY_VERIFY(item);
1474         QTRY_COMPARE(item->x(), (i%3)*80.);
1475         QTRY_COMPARE(item->y(), (i/3)*60.);
1476     }
1477
1478     // Position at End
1479     gridview->positionViewAtIndex(30, QSGGridView::End);
1480     QTRY_COMPARE(gridview->contentY(), 340.);
1481
1482     // Position in Center
1483     gridview->positionViewAtIndex(15, QSGGridView::Center);
1484     QTRY_COMPARE(gridview->contentY(), 170.);
1485
1486     // Ensure at least partially visible
1487     gridview->positionViewAtIndex(15, QSGGridView::Visible);
1488     QTRY_COMPARE(gridview->contentY(), 170.);
1489
1490     gridview->setContentY(302);
1491     gridview->positionViewAtIndex(15, QSGGridView::Visible);
1492     QTRY_COMPARE(gridview->contentY(), 302.);
1493
1494     gridview->setContentY(360);
1495     gridview->positionViewAtIndex(15, QSGGridView::Visible);
1496     QTRY_COMPARE(gridview->contentY(), 300.);
1497
1498     gridview->setContentY(60);
1499     gridview->positionViewAtIndex(20, QSGGridView::Visible);
1500     QTRY_COMPARE(gridview->contentY(), 60.);
1501
1502     gridview->setContentY(20);
1503     gridview->positionViewAtIndex(20, QSGGridView::Visible);
1504     QTRY_COMPARE(gridview->contentY(), 100.);
1505
1506     // Ensure completely visible
1507     gridview->setContentY(120);
1508     gridview->positionViewAtIndex(20, QSGGridView::Contain);
1509     QTRY_COMPARE(gridview->contentY(), 120.);
1510
1511     gridview->setContentY(302);
1512     gridview->positionViewAtIndex(15, QSGGridView::Contain);
1513     QTRY_COMPARE(gridview->contentY(), 300.);
1514
1515     gridview->setContentY(60);
1516     gridview->positionViewAtIndex(20, QSGGridView::Contain);
1517     QTRY_COMPARE(gridview->contentY(), 100.);
1518
1519     // Test for Top To Bottom layout
1520     ctxt->setContextProperty("testTopToBottom", QVariant(true));
1521
1522     // Confirm items positioned correctly
1523     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
1524     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
1525         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
1526         if (!item) qWarning() << "Item" << i << "not found";
1527         QTRY_VERIFY(item);
1528         QTRY_COMPARE(item->x(), (i/5)*80.);
1529         QTRY_COMPARE(item->y(), (i%5)*60.);
1530     }
1531
1532     // Position at End
1533     gridview->positionViewAtIndex(30, QSGGridView::End);
1534     QTRY_COMPARE(gridview->contentX(), 320.);
1535     QTRY_COMPARE(gridview->contentY(), 0.);
1536
1537     // Position in Center
1538     gridview->positionViewAtIndex(15, QSGGridView::Center);
1539     QTRY_COMPARE(gridview->contentX(), 160.);
1540
1541     // Ensure at least partially visible
1542     gridview->positionViewAtIndex(15, QSGGridView::Visible);
1543     QTRY_COMPARE(gridview->contentX(), 160.);
1544
1545     gridview->setContentX(170);
1546     gridview->positionViewAtIndex(25, QSGGridView::Visible);
1547     QTRY_COMPARE(gridview->contentX(), 170.);
1548
1549     gridview->positionViewAtIndex(30, QSGGridView::Visible);
1550     QTRY_COMPARE(gridview->contentX(), 320.);
1551
1552     gridview->setContentX(170);
1553     gridview->positionViewAtIndex(25, QSGGridView::Contain);
1554     QTRY_COMPARE(gridview->contentX(), 240.);
1555
1556     // positionViewAtBeginning
1557     gridview->positionViewAtBeginning();
1558     QTRY_COMPARE(gridview->contentX(), 0.);
1559
1560     gridview->setContentX(80);
1561     canvas->rootObject()->setProperty("showHeader", true);
1562     gridview->positionViewAtBeginning();
1563     QTRY_COMPARE(gridview->contentX(), -30.);
1564
1565     // positionViewAtEnd
1566     gridview->positionViewAtEnd();
1567     QTRY_COMPARE(gridview->contentX(), 400.);   // 8*80 - 240   (8 columns)
1568
1569     gridview->setContentX(80);
1570     canvas->rootObject()->setProperty("showFooter", true);
1571     gridview->positionViewAtEnd();
1572     QTRY_COMPARE(gridview->contentX(), 430.);
1573
1574     // set current item to outside visible view, position at beginning
1575     // and ensure highlight moves to current item
1576     gridview->setCurrentIndex(6);
1577     gridview->positionViewAtBeginning();
1578     QTRY_COMPARE(gridview->contentX(), -30.);
1579     QVERIFY(gridview->highlightItem());
1580     QCOMPARE(gridview->highlightItem()->x(), 80.);
1581
1582     delete canvas;
1583 }
1584
1585 void tst_QSGGridView::snapping()
1586 {
1587     QSGView *canvas = createView();
1588
1589     TestModel model;
1590     for (int i = 0; i < 40; i++)
1591         model.addItem("Item" + QString::number(i), "");
1592
1593     QDeclarativeContext *ctxt = canvas->rootContext();
1594     ctxt->setContextProperty("testModel", &model);
1595     ctxt->setContextProperty("testTopToBottom", QVariant(false));
1596     ctxt->setContextProperty("testRightToLeft", QVariant(false));
1597
1598     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
1599     qApp->processEvents();
1600
1601     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
1602     QTRY_VERIFY(gridview != 0);
1603
1604     gridview->setHeight(220);
1605     QCOMPARE(gridview->height(), 220.);
1606
1607     gridview->positionViewAtIndex(12, QSGGridView::Visible);
1608     QCOMPARE(gridview->contentY(), 80.);
1609
1610     gridview->setContentY(0);
1611     QCOMPARE(gridview->contentY(), 0.);
1612
1613     gridview->setSnapMode(QSGGridView::SnapToRow);
1614     QCOMPARE(gridview->snapMode(), QSGGridView::SnapToRow);
1615
1616     gridview->positionViewAtIndex(12, QSGGridView::Visible);
1617     QCOMPARE(gridview->contentY(), 60.);
1618
1619     gridview->positionViewAtIndex(15, QSGGridView::End);
1620     QCOMPARE(gridview->contentY(), 120.);
1621
1622     delete canvas;
1623
1624 }
1625
1626 void tst_QSGGridView::mirroring()
1627 {
1628     QSGView *canvasA = createView();
1629     canvasA->setSource(QUrl::fromLocalFile(SRCDIR "/data/mirroring.qml"));
1630     QSGGridView *gridviewA = findItem<QSGGridView>(canvasA->rootObject(), "view");
1631     QTRY_VERIFY(gridviewA != 0);
1632
1633     QSGView *canvasB = createView();
1634     canvasB->setSource(QUrl::fromLocalFile(SRCDIR "/data/mirroring.qml"));
1635     QSGGridView *gridviewB = findItem<QSGGridView>(canvasB->rootObject(), "view");
1636     QTRY_VERIFY(gridviewA != 0);
1637     qApp->processEvents();
1638
1639     QList<QString> objectNames;
1640     objectNames << "item1" << "item2"; // << "item3"
1641
1642     gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
1643     gridviewB->setProperty("layoutDirection", Qt::RightToLeft);
1644     QCOMPARE(gridviewA->layoutDirection(), gridviewA->effectiveLayoutDirection());
1645
1646     // LTR != RTL
1647     foreach(const QString objectName, objectNames)
1648         QVERIFY(findItem<QSGItem>(gridviewA, objectName)->x() != findItem<QSGItem>(gridviewB, objectName)->x());
1649
1650     gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
1651     gridviewB->setProperty("layoutDirection", Qt::LeftToRight);
1652
1653     // LTR == LTR
1654     foreach(const QString objectName, objectNames)
1655         QCOMPARE(findItem<QSGItem>(gridviewA, objectName)->x(), findItem<QSGItem>(gridviewB, objectName)->x());
1656
1657     QVERIFY(gridviewB->layoutDirection() == gridviewB->effectiveLayoutDirection());
1658     QSGItemPrivate::get(gridviewB)->setLayoutMirror(true);
1659     QVERIFY(gridviewB->layoutDirection() != gridviewB->effectiveLayoutDirection());
1660
1661     // LTR != LTR+mirror
1662     foreach(const QString objectName, objectNames)
1663         QVERIFY(findItem<QSGItem>(gridviewA, objectName)->x() != findItem<QSGItem>(gridviewB, objectName)->x());
1664
1665     gridviewA->setProperty("layoutDirection", Qt::RightToLeft);
1666
1667     // RTL == LTR+mirror
1668     foreach(const QString objectName, objectNames)
1669         QCOMPARE(findItem<QSGItem>(gridviewA, objectName)->x(), findItem<QSGItem>(gridviewB, objectName)->x());
1670
1671     gridviewB->setProperty("layoutDirection", Qt::RightToLeft);
1672
1673     // RTL != RTL+mirror
1674     foreach(const QString objectName, objectNames)
1675         QVERIFY(findItem<QSGItem>(gridviewA, objectName)->x() != findItem<QSGItem>(gridviewB, objectName)->x());
1676
1677     gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
1678
1679     // LTR == RTL+mirror
1680     foreach(const QString objectName, objectNames)
1681         QCOMPARE(findItem<QSGItem>(gridviewA, objectName)->x(), findItem<QSGItem>(gridviewB, objectName)->x());
1682
1683     delete canvasA;
1684     delete canvasB;
1685 }
1686
1687 void tst_QSGGridView::positionViewAtIndex_rightToLeft()
1688 {
1689     QSGView *canvas = createView();
1690
1691     TestModel model;
1692     for (int i = 0; i < 40; i++)
1693         model.addItem("Item" + QString::number(i), "");
1694
1695     QDeclarativeContext *ctxt = canvas->rootContext();
1696     ctxt->setContextProperty("testModel", &model);
1697     ctxt->setContextProperty("testTopToBottom", QVariant(true));
1698     ctxt->setContextProperty("testRightToLeft", QVariant(true));
1699
1700     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
1701     qApp->processEvents();
1702
1703     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
1704     QTRY_VERIFY(gridview != 0);
1705
1706     QSGItem *contentItem = gridview->contentItem();
1707     QTRY_VERIFY(contentItem != 0);
1708
1709     // Confirm items positioned correctly
1710     int itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
1711     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
1712         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
1713         if (!item) qWarning() << "Item" << i << "not found";
1714         QTRY_VERIFY(item);
1715         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
1716         QTRY_COMPARE(item->y(), qreal((i%5)*60));
1717     }
1718
1719     // Position on a currently visible item
1720     gridview->positionViewAtIndex(6, QSGGridView::Beginning);
1721     QTRY_COMPARE(gridview->contentX(), -320.);
1722
1723     // Confirm items positioned correctly
1724     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
1725     for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
1726         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
1727         if (!item) qWarning() << "Item" << i << "not found";
1728         QTRY_VERIFY(item);
1729         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
1730         QTRY_COMPARE(item->y(), qreal((i%5)*60));
1731     }
1732
1733     // Position on an item beyond the visible items
1734     gridview->positionViewAtIndex(21, QSGGridView::Beginning);
1735     QTRY_COMPARE(gridview->contentX(), -560.);
1736
1737     // Confirm items positioned correctly
1738     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
1739     for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
1740         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
1741         if (!item) qWarning() << "Item" << i << "not found";
1742         QTRY_VERIFY(item);
1743         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
1744         QTRY_COMPARE(item->y(), qreal((i%5)*60));
1745     }
1746
1747     // Position on an item that would leave empty space if positioned at the top
1748     gridview->positionViewAtIndex(31, QSGGridView::Beginning);
1749     QTRY_COMPARE(gridview->contentX(), -640.);
1750
1751     // Confirm items positioned correctly
1752     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
1753     for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
1754         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
1755         if (!item) qWarning() << "Item" << i << "not found";
1756         QTRY_VERIFY(item);
1757         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
1758         QTRY_COMPARE(item->y(), qreal((i%5)*60));
1759     }
1760
1761     // Position at the beginning again
1762     gridview->positionViewAtIndex(0, QSGGridView::Beginning);
1763     QTRY_COMPARE(gridview->contentX(), -240.);
1764
1765     // Confirm items positioned correctly
1766     itemCount = findItems<QSGItem>(contentItem, "wrapper").count();
1767     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
1768         QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", i);
1769         if (!item) qWarning() << "Item" << i << "not found";
1770         QTRY_VERIFY(item);
1771         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
1772         QTRY_COMPARE(item->y(), qreal((i%5)*60));
1773     }
1774
1775     // Position at End
1776     gridview->positionViewAtIndex(30, QSGGridView::End);
1777     QTRY_COMPARE(gridview->contentX(), -560.);
1778
1779     // Position in Center
1780     gridview->positionViewAtIndex(15, QSGGridView::Center);
1781     QTRY_COMPARE(gridview->contentX(), -400.);
1782
1783     // Ensure at least partially visible
1784     gridview->positionViewAtIndex(15, QSGGridView::Visible);
1785     QTRY_COMPARE(gridview->contentX(), -400.);
1786
1787     gridview->setContentX(-555.);
1788     gridview->positionViewAtIndex(15, QSGGridView::Visible);
1789     QTRY_COMPARE(gridview->contentX(), -555.);
1790
1791     gridview->setContentX(-239);
1792     gridview->positionViewAtIndex(15, QSGGridView::Visible);
1793     QTRY_COMPARE(gridview->contentX(), -320.);
1794
1795     gridview->setContentX(-239);
1796     gridview->positionViewAtIndex(20, QSGGridView::Visible);
1797     QTRY_COMPARE(gridview->contentX(), -400.);
1798
1799     gridview->setContentX(-640);
1800     gridview->positionViewAtIndex(20, QSGGridView::Visible);
1801     QTRY_COMPARE(gridview->contentX(), -560.);
1802
1803     // Ensure completely visible
1804     gridview->setContentX(-400);
1805     gridview->positionViewAtIndex(20, QSGGridView::Contain);
1806     QTRY_COMPARE(gridview->contentX(), -400.);
1807
1808     gridview->setContentX(-315);
1809     gridview->positionViewAtIndex(15, QSGGridView::Contain);
1810     QTRY_COMPARE(gridview->contentX(), -320.);
1811
1812     gridview->setContentX(-640);
1813     gridview->positionViewAtIndex(20, QSGGridView::Contain);
1814     QTRY_COMPARE(gridview->contentX(), -560.);
1815
1816     delete canvas;
1817 }
1818
1819 void tst_QSGGridView::resetModel()
1820 {
1821     QSGView *canvas = createView();
1822
1823     QStringList strings;
1824     strings << "one" << "two" << "three";
1825     QStringListModel model(strings);
1826
1827     QDeclarativeContext *ctxt = canvas->rootContext();
1828     ctxt->setContextProperty("testModel", &model);
1829
1830     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/displaygrid.qml"));
1831     qApp->processEvents();
1832
1833     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
1834     QTRY_VERIFY(gridview != 0);
1835
1836     QSGItem *contentItem = gridview->contentItem();
1837     QTRY_VERIFY(contentItem != 0);
1838
1839     QTRY_COMPARE(gridview->count(), model.rowCount());
1840
1841     for (int i = 0; i < model.rowCount(); ++i) {
1842         QSGText *display = findItem<QSGText>(contentItem, "displayText", i);
1843         QTRY_VERIFY(display != 0);
1844         QTRY_COMPARE(display->text(), strings.at(i));
1845     }
1846
1847     strings.clear();
1848     strings << "four" << "five" << "six" << "seven";
1849     model.setStringList(strings);
1850
1851     QTRY_COMPARE(gridview->count(), model.rowCount());
1852
1853     for (int i = 0; i < model.rowCount(); ++i) {
1854         QSGText *display = findItem<QSGText>(contentItem, "displayText", i);
1855         QTRY_VERIFY(display != 0);
1856         QTRY_COMPARE(display->text(), strings.at(i));
1857     }
1858
1859     delete canvas;
1860 }
1861
1862 void tst_QSGGridView::enforceRange()
1863 {
1864     QSGView *canvas = createView();
1865
1866     TestModel model;
1867     for (int i = 0; i < 30; i++)
1868         model.addItem("Item" + QString::number(i), "");
1869
1870     QDeclarativeContext *ctxt = canvas->rootContext();
1871     ctxt->setContextProperty("testModel", &model);
1872     ctxt->setContextProperty("testRightToLeft", QVariant(false));
1873     ctxt->setContextProperty("testTopToBottom", QVariant(false));
1874
1875     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview-enforcerange.qml"));
1876     qApp->processEvents();
1877     QVERIFY(canvas->rootObject() != 0);
1878
1879     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
1880     QTRY_VERIFY(gridview != 0);
1881
1882     QTRY_COMPARE(gridview->preferredHighlightBegin(), 100.0);
1883     QTRY_COMPARE(gridview->preferredHighlightEnd(), 100.0);
1884     QTRY_COMPARE(gridview->highlightRangeMode(), QSGGridView::StrictlyEnforceRange);
1885
1886     QSGItem *contentItem = gridview->contentItem();
1887     QTRY_VERIFY(contentItem != 0);
1888
1889     // view should be positioned at the top of the range.
1890     QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", 0);
1891     QTRY_VERIFY(item);
1892     QTRY_COMPARE(gridview->contentY(), -100.0);
1893
1894     QSGText *name = findItem<QSGText>(contentItem, "textName", 0);
1895     QTRY_VERIFY(name != 0);
1896     QTRY_COMPARE(name->text(), model.name(0));
1897     QSGText *number = findItem<QSGText>(contentItem, "textNumber", 0);
1898     QTRY_VERIFY(number != 0);
1899     QTRY_COMPARE(number->text(), model.number(0));
1900
1901     // Check currentIndex is updated when contentItem moves
1902     gridview->setContentY(0);
1903     QTRY_COMPARE(gridview->currentIndex(), 2);
1904
1905     gridview->setCurrentIndex(5);
1906     QTRY_COMPARE(gridview->contentY(), 100.);
1907
1908     TestModel model2;
1909     for (int i = 0; i < 5; i++)
1910         model2.addItem("Item" + QString::number(i), "");
1911
1912     ctxt->setContextProperty("testModel", &model2);
1913     QCOMPARE(gridview->count(), 5);
1914
1915     delete canvas;
1916 }
1917
1918 void tst_QSGGridView::enforceRange_rightToLeft()
1919 {
1920     QSGView *canvas = createView();
1921
1922     TestModel model;
1923     for (int i = 0; i < 30; i++)
1924         model.addItem("Item" + QString::number(i), "");
1925
1926     QDeclarativeContext *ctxt = canvas->rootContext();
1927     ctxt->setContextProperty("testModel", &model);
1928     ctxt->setContextProperty("testRightToLeft", QVariant(true));
1929     ctxt->setContextProperty("testTopToBottom", QVariant(true));
1930
1931     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview-enforcerange.qml"));
1932     qApp->processEvents();
1933     QVERIFY(canvas->rootObject() != 0);
1934
1935     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
1936     QTRY_VERIFY(gridview != 0);
1937
1938     QTRY_COMPARE(gridview->preferredHighlightBegin(), 100.0);
1939     QTRY_COMPARE(gridview->preferredHighlightEnd(), 100.0);
1940     QTRY_COMPARE(gridview->highlightRangeMode(), QSGGridView::StrictlyEnforceRange);
1941
1942     QSGItem *contentItem = gridview->contentItem();
1943     QTRY_VERIFY(contentItem != 0);
1944
1945     // view should be positioned at the top of the range.
1946     QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", 0);
1947     QTRY_VERIFY(item);
1948     QTRY_COMPARE(gridview->contentX(), -100.);
1949     QTRY_COMPARE(gridview->contentY(), 0.0);
1950
1951     QSGText *name = findItem<QSGText>(contentItem, "textName", 0);
1952     QTRY_VERIFY(name != 0);
1953     QTRY_COMPARE(name->text(), model.name(0));
1954     QSGText *number = findItem<QSGText>(contentItem, "textNumber", 0);
1955     QTRY_VERIFY(number != 0);
1956     QTRY_COMPARE(number->text(), model.number(0));
1957
1958     // Check currentIndex is updated when contentItem moves
1959     gridview->setContentX(-200);
1960     QTRY_COMPARE(gridview->currentIndex(), 3);
1961
1962     gridview->setCurrentIndex(7);
1963     QTRY_COMPARE(gridview->contentX(), -300.);
1964     QTRY_COMPARE(gridview->contentY(), 0.0);
1965
1966     TestModel model2;
1967     for (int i = 0; i < 5; i++)
1968         model2.addItem("Item" + QString::number(i), "");
1969
1970     ctxt->setContextProperty("testModel", &model2);
1971     QCOMPARE(gridview->count(), 5);
1972
1973     delete canvas;
1974 }
1975
1976 void tst_QSGGridView::QTBUG_8456()
1977 {
1978     QSGView *canvas = createView();
1979
1980     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/setindex.qml"));
1981     qApp->processEvents();
1982
1983     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
1984     QTRY_VERIFY(gridview != 0);
1985
1986     QTRY_COMPARE(gridview->currentIndex(), 0);
1987
1988     delete canvas;
1989 }
1990
1991 void tst_QSGGridView::manualHighlight()
1992 {
1993     QSGView *canvas = createView();
1994
1995     QString filename(SRCDIR "/data/manual-highlight.qml");
1996     canvas->setSource(QUrl::fromLocalFile(filename));
1997
1998     qApp->processEvents();
1999
2000     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
2001     QTRY_VERIFY(gridview != 0);
2002
2003     QSGItem *contentItem = gridview->contentItem();
2004     QTRY_VERIFY(contentItem != 0);
2005
2006     QTRY_COMPARE(gridview->currentIndex(), 0);
2007     QTRY_COMPARE(gridview->currentItem(), findItem<QSGItem>(contentItem, "wrapper", 0));
2008     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
2009     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
2010
2011     gridview->setCurrentIndex(2);
2012
2013     QTRY_COMPARE(gridview->currentIndex(), 2);
2014     QTRY_COMPARE(gridview->currentItem(), findItem<QSGItem>(contentItem, "wrapper", 2));
2015     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
2016     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
2017
2018     gridview->positionViewAtIndex(8, QSGGridView::Contain);
2019
2020     QTRY_COMPARE(gridview->currentIndex(), 2);
2021     QTRY_COMPARE(gridview->currentItem(), findItem<QSGItem>(contentItem, "wrapper", 2));
2022     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
2023     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
2024
2025     gridview->setFlow(QSGGridView::TopToBottom);
2026     QTRY_COMPARE(gridview->flow(), QSGGridView::TopToBottom);
2027
2028     gridview->setCurrentIndex(0);
2029     QTRY_COMPARE(gridview->currentIndex(), 0);
2030     QTRY_COMPARE(gridview->currentItem(), findItem<QSGItem>(contentItem, "wrapper", 0));
2031     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
2032     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
2033
2034     delete canvas;
2035 }
2036
2037
2038 void tst_QSGGridView::footer()
2039 {
2040     QFETCH(QSGGridView::Flow, flow);
2041     QFETCH(Qt::LayoutDirection, layoutDirection);
2042     QFETCH(QPointF, initialFooterPos);
2043     QFETCH(QPointF, changedFooterPos);
2044     QFETCH(QPointF, initialContentPos);
2045     QFETCH(QPointF, changedContentPos);
2046     QFETCH(QPointF, firstDelegatePos);
2047
2048     QSGView *canvas = createView();
2049     canvas->show();
2050
2051     TestModel model;
2052     for (int i = 0; i < 7; i++)
2053         model.addItem("Item" + QString::number(i), "");
2054
2055     QDeclarativeContext *ctxt = canvas->rootContext();
2056     ctxt->setContextProperty("testModel", &model);
2057
2058     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/footer.qml"));
2059     qApp->processEvents();
2060
2061     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
2062     QTRY_VERIFY(gridview != 0);
2063     gridview->setFlow(flow);
2064     gridview->setLayoutDirection(layoutDirection);
2065
2066     QSGItem *contentItem = gridview->contentItem();
2067     QTRY_VERIFY(contentItem != 0);
2068
2069     QSGText *footer = findItem<QSGText>(contentItem, "footer");
2070     QVERIFY(footer);
2071
2072     QVERIFY(footer == gridview->footerItem());
2073
2074     QCOMPARE(footer->pos(), initialFooterPos);
2075     QCOMPARE(footer->width(), 100.);
2076     QCOMPARE(footer->height(), 30.);
2077     QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
2078
2079     QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", 0);
2080     QVERIFY(item);
2081     QCOMPARE(item->pos(), firstDelegatePos);
2082
2083     if (flow == QSGGridView::LeftToRight) {
2084         // shrink by one row
2085         model.removeItem(2);
2086         QTRY_COMPARE(footer->y(), initialFooterPos.y() - gridview->cellHeight());
2087     } else {
2088         // shrink by one column
2089         model.removeItem(2);
2090         model.removeItem(3);
2091         if (layoutDirection == Qt::LeftToRight)
2092             QTRY_COMPARE(footer->x(), initialFooterPos.x() - gridview->cellWidth());
2093         else
2094             QTRY_COMPARE(footer->x(), initialFooterPos.x() + gridview->cellWidth());
2095     }
2096
2097     // remove all items
2098     model.clear();
2099
2100     QPointF posWhenNoItems(0, 0);
2101     if (layoutDirection == Qt::RightToLeft)
2102         posWhenNoItems.setX(flow == QSGGridView::LeftToRight ? gridview->width() - footer->width() : -footer->width());
2103     QTRY_COMPARE(footer->pos(), posWhenNoItems);
2104
2105     // if header is present, it's at a negative pos, so the footer should not move
2106     canvas->rootObject()->setProperty("showHeader", true);
2107     QVERIFY(findItem<QSGItem>(contentItem, "header") != 0);
2108     QTRY_COMPARE(footer->pos(), posWhenNoItems);
2109     canvas->rootObject()->setProperty("showHeader", false);
2110
2111     // add 30 items
2112     for (int i = 0; i < 30; i++)
2113         model.addItem("Item" + QString::number(i), "");
2114
2115     QSignalSpy footerItemSpy(gridview, SIGNAL(footerItemChanged()));
2116     QMetaObject::invokeMethod(canvas->rootObject(), "changeFooter");
2117
2118     QCOMPARE(footerItemSpy.count(), 1);
2119
2120     footer = findItem<QSGText>(contentItem, "footer");
2121     QVERIFY(!footer);
2122     footer = findItem<QSGText>(contentItem, "footer2");
2123     QVERIFY(footer);
2124
2125     QVERIFY(footer == gridview->footerItem());
2126
2127     QCOMPARE(footer->pos(), changedFooterPos);
2128     QCOMPARE(footer->width(), 50.);
2129     QCOMPARE(footer->height(), 20.);
2130     QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), changedContentPos);
2131
2132     item = findItem<QSGItem>(contentItem, "wrapper", 0);
2133     QVERIFY(item);
2134     QCOMPARE(item->pos(), firstDelegatePos);
2135
2136     delete canvas;
2137 }
2138
2139 void tst_QSGGridView::footer_data()
2140 {
2141     QTest::addColumn<QSGGridView::Flow>("flow");
2142     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
2143     QTest::addColumn<QPointF>("initialFooterPos");
2144     QTest::addColumn<QPointF>("changedFooterPos");
2145     QTest::addColumn<QPointF>("initialContentPos");
2146     QTest::addColumn<QPointF>("changedContentPos");
2147     QTest::addColumn<QPointF>("firstDelegatePos");
2148
2149     // footer1 = 100 x 30
2150     // footer2 = 100 x 20
2151     // cells = 80 * 60
2152     // view width = 240
2153
2154     // footer below items, bottom left
2155     QTest::newRow("flow left to right") << QSGGridView::LeftToRight << Qt::LeftToRight
2156         << QPointF(0, 3 * 60)  // 180 = height of 3 rows (cell height is 60)
2157         << QPointF(0, 10 * 60)  // 30 items = 10 rows
2158         << QPointF(0, 0)
2159         << QPointF(0, 0)
2160         << QPointF(0, 0);
2161
2162     // footer below items, bottom right
2163     QTest::newRow("flow left to right, layout right to left") << QSGGridView::LeftToRight << Qt::RightToLeft
2164         << QPointF(240 - 100, 3 * 60)
2165         << QPointF((240 - 100) + 50, 10 * 60)     // 50 = width diff between old and new footers
2166         << QPointF(0, 0)
2167         << QPointF(0, 0)
2168         << QPointF(240 - 80, 0);
2169
2170     // footer to right of items
2171     QTest::newRow("flow top to bottom, layout left to right") << QSGGridView::TopToBottom << Qt::LeftToRight
2172         << QPointF(2 * 80, 0)      // 2 columns, cell width 80
2173         << QPointF(6 * 80, 0)      // 30 items = 6 columns
2174         << QPointF(0, 0)
2175         << QPointF(0, 0)
2176         << QPointF(0, 0);
2177
2178     // footer to left of items
2179     QTest::newRow("flow top to bottom, layout right to left") << QSGGridView::TopToBottom << Qt::RightToLeft
2180         << QPointF(-(2 * 80) - 100, 0)
2181         << QPointF(-(6 * 80) - 50, 0)     // 50 = new footer width
2182         << QPointF(-240, 0)
2183         << QPointF(-240, 0)    // unchanged, footer change doesn't change content pos
2184         << QPointF(-80, 0);
2185 }
2186
2187 void tst_QSGGridView::header()
2188 {
2189     QFETCH(QSGGridView::Flow, flow);
2190     QFETCH(Qt::LayoutDirection, layoutDirection);
2191     QFETCH(QPointF, initialHeaderPos);
2192     QFETCH(QPointF, changedHeaderPos);
2193     QFETCH(QPointF, initialContentPos);
2194     QFETCH(QPointF, changedContentPos);
2195     QFETCH(QPointF, firstDelegatePos);
2196
2197     QSGView *canvas = createView();
2198
2199     TestModel model;
2200     for (int i = 0; i < 30; i++)
2201         model.addItem("Item" + QString::number(i), "");
2202
2203     QDeclarativeContext *ctxt = canvas->rootContext();
2204     ctxt->setContextProperty("testModel", &model);
2205
2206     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/header.qml"));
2207     qApp->processEvents();
2208
2209     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
2210     QTRY_VERIFY(gridview != 0);
2211     gridview->setFlow(flow);
2212     gridview->setLayoutDirection(layoutDirection);
2213
2214     QSGItem *contentItem = gridview->contentItem();
2215     QTRY_VERIFY(contentItem != 0);
2216
2217     QSGText *header = findItem<QSGText>(contentItem, "header");
2218     QVERIFY(header);
2219
2220     QVERIFY(header == gridview->headerItem());
2221
2222     QCOMPARE(header->pos(), initialHeaderPos);
2223     QCOMPARE(header->width(), 100.);
2224     QCOMPARE(header->height(), 30.);
2225     QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
2226
2227     QSGItem *item = findItem<QSGItem>(contentItem, "wrapper", 0);
2228     QVERIFY(item);
2229     QCOMPARE(item->pos(), firstDelegatePos);
2230
2231     model.clear();
2232     QCOMPARE(header->pos(), initialHeaderPos); // header should stay where it is
2233
2234     for (int i = 0; i < 30; i++)
2235         model.addItem("Item" + QString::number(i), "");
2236
2237     QSignalSpy headerItemSpy(gridview, SIGNAL(headerItemChanged()));
2238     QMetaObject::invokeMethod(canvas->rootObject(), "changeHeader");
2239
2240     QCOMPARE(headerItemSpy.count(), 1);
2241
2242     header = findItem<QSGText>(contentItem, "header");
2243     QVERIFY(!header);
2244     header = findItem<QSGText>(contentItem, "header2");
2245     QVERIFY(header);
2246
2247     QVERIFY(header == gridview->headerItem());
2248
2249     QCOMPARE(header->pos(), changedHeaderPos);
2250     QCOMPARE(header->width(), 50.);
2251     QCOMPARE(header->height(), 20.);
2252     QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), changedContentPos);
2253
2254     item = findItem<QSGItem>(contentItem, "wrapper", 0);
2255     QVERIFY(item);
2256     QCOMPARE(item->pos(), firstDelegatePos);
2257
2258     delete canvas;
2259 }
2260
2261 void tst_QSGGridView::header_data()
2262 {
2263     QTest::addColumn<QSGGridView::Flow>("flow");
2264     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
2265     QTest::addColumn<QPointF>("initialHeaderPos");
2266     QTest::addColumn<QPointF>("changedHeaderPos");
2267     QTest::addColumn<QPointF>("initialContentPos");
2268     QTest::addColumn<QPointF>("changedContentPos");
2269     QTest::addColumn<QPointF>("firstDelegatePos");
2270
2271     // header1 = 100 x 30
2272     // header2 = 100 x 20
2273     // cells = 80 x 60
2274     // view width = 240
2275
2276     // header above items, top left
2277     QTest::newRow("flow left to right") << QSGGridView::LeftToRight << Qt::LeftToRight
2278         << QPointF(0, -30)
2279         << QPointF(0, -20)
2280         << QPointF(0, -30)
2281         << QPointF(0, -20)
2282         << QPointF(0, 0);
2283
2284     // header above items, top right
2285     QTest::newRow("flow left to right, layout right to left") << QSGGridView::LeftToRight << Qt::RightToLeft
2286         << QPointF(240 - 100, -30)
2287         << QPointF((240 - 100) + 50, -20)     // 50 = width diff between old and new headers
2288         << QPointF(0, -30)
2289         << QPointF(0, -20)
2290         << QPointF(160, 0);
2291
2292     // header to left of items
2293     QTest::newRow("flow top to bottom, layout left to right") << QSGGridView::TopToBottom << Qt::LeftToRight
2294         << QPointF(-100, 0)
2295         << QPointF(-50, 0)
2296         << QPointF(-100, 0)
2297         << QPointF(-50, 0)
2298         << QPointF(0, 0);
2299
2300     // header to right of items
2301     QTest::newRow("flow top to bottom, layout right to left") << QSGGridView::TopToBottom << Qt::RightToLeft
2302         << QPointF(0, 0)
2303         << QPointF(0, 0)
2304         << QPointF(-(240 - 100), 0)
2305         << QPointF(-(240 - 50), 0)
2306         << QPointF(-80, 0);
2307 }
2308
2309 void tst_QSGGridView::indexAt()
2310 {
2311     QSGView *canvas = createView();
2312
2313     TestModel model;
2314     model.addItem("Fred", "12345");
2315     model.addItem("John", "2345");
2316     model.addItem("Bob", "54321");
2317     model.addItem("Billy", "22345");
2318     model.addItem("Sam", "2945");
2319     model.addItem("Ben", "04321");
2320     model.addItem("Jim", "0780");
2321
2322     QDeclarativeContext *ctxt = canvas->rootContext();
2323     ctxt->setContextProperty("testModel", &model);
2324     ctxt->setContextProperty("testRightToLeft", QVariant(false));
2325     ctxt->setContextProperty("testTopToBottom", QVariant(false));
2326
2327     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/gridview1.qml"));
2328     qApp->processEvents();
2329
2330     QSGGridView *gridview = findItem<QSGGridView>(canvas->rootObject(), "grid");
2331     QTRY_VERIFY(gridview != 0);
2332
2333     QSGItem *contentItem = gridview->contentItem();
2334     QTRY_VERIFY(contentItem != 0);
2335
2336     QTRY_COMPARE(gridview->count(), model.count());
2337
2338     QCOMPARE(gridview->indexAt(0, 0), 0);
2339     QCOMPARE(gridview->indexAt(79, 59), 0);
2340     QCOMPARE(gridview->indexAt(80, 0), 1);
2341     QCOMPARE(gridview->indexAt(0, 60), 3);
2342     QCOMPARE(gridview->indexAt(240, 0), -1);
2343
2344     delete canvas;
2345 }
2346
2347 void tst_QSGGridView::onAdd()
2348 {
2349     QFETCH(int, initialItemCount);
2350     QFETCH(int, itemsToAdd);
2351
2352     const int delegateWidth = 50;
2353     const int delegateHeight = 100;
2354     TestModel model;
2355     QSGView *canvas = createView();
2356     canvas->setFixedSize(5 * delegateWidth, 5 * delegateHeight); // just ensure all items fit
2357
2358     // these initial items should not trigger GridView.onAdd
2359     for (int i=0; i<initialItemCount; i++)
2360         model.addItem("dummy value", "dummy value");
2361
2362     QDeclarativeContext *ctxt = canvas->rootContext();
2363     ctxt->setContextProperty("testModel", &model);
2364     ctxt->setContextProperty("delegateWidth", delegateWidth);
2365     ctxt->setContextProperty("delegateHeight", delegateHeight);
2366     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/attachedSignals.qml"));
2367
2368     QObject *object = canvas->rootObject();
2369     object->setProperty("width", canvas->width());
2370     object->setProperty("height", canvas->height());
2371     qApp->processEvents();
2372
2373     QList<QPair<QString, QString> > items;
2374     for (int i=0; i<itemsToAdd; i++)
2375         items << qMakePair(QString("value %1").arg(i), QString::number(i));
2376     model.addItems(items);
2377
2378     qApp->processEvents();
2379
2380     QVariantList result = object->property("addedDelegates").toList();
2381     QCOMPARE(result.count(), items.count());
2382     for (int i=0; i<items.count(); i++)
2383         QCOMPARE(result[i].toString(), items[i].first);
2384
2385     delete canvas;
2386 }
2387
2388 void tst_QSGGridView::onAdd_data()
2389 {
2390     QTest::addColumn<int>("initialItemCount");
2391     QTest::addColumn<int>("itemsToAdd");
2392
2393     QTest::newRow("0, add 1") << 0 << 1;
2394     QTest::newRow("0, add 2") << 0 << 2;
2395     QTest::newRow("0, add 10") << 0 << 10;
2396
2397     QTest::newRow("1, add 1") << 1 << 1;
2398     QTest::newRow("1, add 2") << 1 << 2;
2399     QTest::newRow("1, add 10") << 1 << 10;
2400
2401     QTest::newRow("5, add 1") << 5 << 1;
2402     QTest::newRow("5, add 2") << 5 << 2;
2403     QTest::newRow("5, add 10") << 5 << 10;
2404 }
2405
2406 void tst_QSGGridView::onRemove()
2407 {
2408     QFETCH(int, initialItemCount);
2409     QFETCH(int, indexToRemove);
2410     QFETCH(int, removeCount);
2411
2412     const int delegateWidth = 50;
2413     const int delegateHeight = 100;
2414     TestModel model;
2415     for (int i=0; i<initialItemCount; i++)
2416         model.addItem(QString("value %1").arg(i), "dummy value");
2417
2418     QSGView *canvas = createView();
2419     QDeclarativeContext *ctxt = canvas->rootContext();
2420     ctxt->setContextProperty("testModel", &model);
2421     ctxt->setContextProperty("delegateWidth", delegateWidth);
2422     ctxt->setContextProperty("delegateHeight", delegateHeight);
2423     canvas->setSource(QUrl::fromLocalFile(SRCDIR "/data/attachedSignals.qml"));
2424     QObject *object = canvas->rootObject();
2425
2426     qApp->processEvents();
2427
2428     model.removeItems(indexToRemove, removeCount);
2429     qApp->processEvents();
2430     QCOMPARE(object->property("removedDelegateCount"), QVariant(removeCount));
2431
2432     delete canvas;
2433 }
2434
2435 void tst_QSGGridView::onRemove_data()
2436 {
2437     QTest::addColumn<int>("initialItemCount");
2438     QTest::addColumn<int>("indexToRemove");
2439     QTest::addColumn<int>("removeCount");
2440
2441     QTest::newRow("remove first") << 1 << 0 << 1;
2442     QTest::newRow("two items, remove first") << 2 << 0 << 1;
2443     QTest::newRow("two items, remove last") << 2 << 1 << 1;
2444     QTest::newRow("two items, remove all") << 2 << 0 << 2;
2445
2446     QTest::newRow("four items, remove first") << 4 << 0 << 1;
2447     QTest::newRow("four items, remove 0-2") << 4 << 0 << 2;
2448     QTest::newRow("four items, remove 1-3") << 4 << 1 << 2;
2449     QTest::newRow("four items, remove 2-4") << 4 << 2 << 2;
2450     QTest::newRow("four items, remove last") << 4 << 3 << 1;
2451     QTest::newRow("four items, remove all") << 4 << 0 << 4;
2452
2453     QTest::newRow("ten items, remove 1-8") << 10 << 0 << 8;
2454     QTest::newRow("ten items, remove 2-7") << 10 << 2 << 5;
2455     QTest::newRow("ten items, remove 4-10") << 10 << 4 << 6;
2456 }
2457
2458 void tst_QSGGridView::testQtQuick11Attributes()
2459 {
2460     QFETCH(QString, code);
2461     QFETCH(QString, warning);
2462     QFETCH(QString, error);
2463
2464     QDeclarativeEngine engine;
2465     QObject *obj;
2466
2467     QDeclarativeComponent valid(&engine);
2468     valid.setData("import QtQuick 1.1; GridView { " + code.toUtf8() + " }", QUrl(""));
2469     obj = valid.create();
2470     QVERIFY(obj);
2471     QVERIFY(valid.errorString().isEmpty());
2472     delete obj;
2473
2474     QDeclarativeComponent invalid(&engine);
2475     invalid.setData("import QtQuick 1.0; GridView { " + code.toUtf8() + " }", QUrl(""));
2476     QTest::ignoreMessage(QtWarningMsg, warning.toUtf8());
2477     obj = invalid.create();
2478     QCOMPARE(invalid.errorString(), error);
2479     delete obj;
2480 }
2481
2482 void tst_QSGGridView::testQtQuick11Attributes_data()
2483 {
2484     QTest::addColumn<QString>("code");
2485     QTest::addColumn<QString>("warning");
2486     QTest::addColumn<QString>("error");
2487
2488     QTest::newRow("positionViewAtBeginning") << "Component.onCompleted: positionViewAtBeginning()"
2489         << "<Unknown File>:1: ReferenceError: Can't find variable: positionViewAtBeginning"
2490         << "";
2491
2492     QTest::newRow("positionViewAtEnd") << "Component.onCompleted: positionViewAtEnd()"
2493         << "<Unknown File>:1: ReferenceError: Can't find variable: positionViewAtEnd"
2494         << "";
2495 }
2496
2497 QSGView *tst_QSGGridView::createView()
2498 {
2499     QSGView *canvas = new QSGView(0);
2500     canvas->setFixedSize(240,320);
2501
2502     return canvas;
2503 }
2504
2505 /*
2506    Find an item with the specified objectName.  If index is supplied then the
2507    item must also evaluate the {index} expression equal to index
2508 */
2509 template<typename T>
2510 T *tst_QSGGridView::findItem(QSGItem *parent, const QString &objectName, int index)
2511 {
2512     const QMetaObject &mo = T::staticMetaObject;
2513     //qDebug() << parent->childItems().count() << "children";
2514     for (int i = 0; i < parent->childItems().count(); ++i) {
2515         QSGItem *item = qobject_cast<QSGItem*>(parent->childItems().at(i));
2516         if(!item)
2517             continue;
2518         //qDebug() << "try" << item;
2519         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
2520             if (index != -1) {
2521                 QDeclarativeContext *context = QDeclarativeEngine::contextForObject(item);
2522                 if (context) {
2523                     if (context->contextProperty("index").toInt() == index) {
2524                         return static_cast<T*>(item);
2525                     }
2526                 }
2527             } else {
2528                 return static_cast<T*>(item);
2529             }
2530         }
2531         item = findItem<T>(item, objectName, index);
2532         if (item)
2533             return static_cast<T*>(item);
2534     }
2535
2536     return 0;
2537 }
2538
2539 template<typename T>
2540 QList<T*> tst_QSGGridView::findItems(QSGItem *parent, const QString &objectName)
2541 {
2542     QList<T*> items;
2543     const QMetaObject &mo = T::staticMetaObject;
2544     //qDebug() << parent->childItems().count() << "children";
2545     for (int i = 0; i < parent->childItems().count(); ++i) {
2546         QSGItem *item = qobject_cast<QSGItem*>(parent->childItems().at(i));
2547         if(!item)
2548             continue;
2549         //qDebug() << "try" << item;
2550         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
2551             items.append(static_cast<T*>(item));
2552             //qDebug() << " found:" << item;
2553         }
2554         items += findItems<T>(item, objectName);
2555     }
2556
2557     return items;
2558 }
2559
2560 void tst_QSGGridView::dumpTree(QSGItem *parent, int depth)
2561 {
2562     static QString padding("                       ");
2563     for (int i = 0; i < parent->childItems().count(); ++i) {
2564         QSGItem *item = qobject_cast<QSGItem*>(parent->childItems().at(i));
2565         if(!item)
2566             continue;
2567         QDeclarativeContext *context = QDeclarativeEngine::contextForObject(item);
2568         qDebug() << padding.left(depth*2) << item << (context ? context->contextProperty("index").toInt() : -1);
2569         dumpTree(item, depth+1);
2570     }
2571 }
2572
2573
2574 QTEST_MAIN(tst_QSGGridView)
2575
2576 #include "tst_qsggridview.moc"
2577