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