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