235c29f5a42c3155e86ab37a7f1b49677b6d8bc7
[profile/ivi/qtdeclarative.git] / tests / auto / quick / qquickgridview / tst_qquickgridview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the test suite of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtTest/QtTest>
43 #include <QtCore/qstringlistmodel.h>
44 #include <QtQuick/qquickview.h>
45 #include <QtQml/qqmlengine.h>
46 #include <QtQml/qqmlcomponent.h>
47 #include <QtQml/qqmlcontext.h>
48 #include <QtQml/qqmlexpression.h>
49 #include <QtQml/qqmlincubator.h>
50 #include <QtQml/qqmlcontext.h>
51 #include <QtQuick/private/qquickitem_p.h>
52 #include <QtQuick/private/qquickgridview_p.h>
53 #include <QtQuick/private/qquicktext_p.h>
54 #include <QtQuick/private/qquickvisualitemmodel_p.h>
55 #include <QtQml/private/qquicklistmodel_p.h>
56 #include <QtQml/private/qlistmodelinterface_p.h>
57 #include "../../shared/util.h"
58 #include "../shared/viewtestutil.h"
59 #include "../shared/visualtestutil.h"
60 #include <QtGui/qguiapplication.h>
61
62 Q_DECLARE_METATYPE(QQuickGridView::Flow)
63 Q_DECLARE_METATYPE(Qt::LayoutDirection)
64 Q_DECLARE_METATYPE(QQuickItemView::VerticalLayoutDirection)
65 Q_DECLARE_METATYPE(Qt::Key)
66
67 using namespace QQuickViewTestUtil;
68 using namespace QQuickVisualTestUtil;
69
70 #define SHARE_VIEWS
71
72 class tst_QQuickGridView : public QQmlDataTest
73 {
74     Q_OBJECT
75 public:
76     tst_QQuickGridView();
77
78 private slots:
79     void init();
80     void items();
81     void changed();
82     void inserted_basic();
83     void inserted_defaultLayout(QQuickGridView::Flow flow = QQuickGridView::FlowLeftToRight, Qt::LayoutDirection horizLayout = Qt::LeftToRight, QQuickItemView::VerticalLayoutDirection verticalLayout = QQuickItemView::TopToBottom);
84     void inserted_defaultLayout_data();
85     void insertBeforeVisible();
86     void insertBeforeVisible_data();
87     void removed_basic();
88     void removed_defaultLayout(QQuickGridView::Flow flow = QQuickGridView::FlowLeftToRight, Qt::LayoutDirection horizLayout = Qt::LeftToRight, QQuickItemView::VerticalLayoutDirection verticalLayout = QQuickItemView::TopToBottom);
89     void removed_defaultLayout_data();
90     void addOrRemoveBeforeVisible();
91     void addOrRemoveBeforeVisible_data();
92     void clear();
93     void moved_defaultLayout(QQuickGridView::Flow flow = QQuickGridView::FlowLeftToRight, Qt::LayoutDirection horizLayout = Qt::LeftToRight, QQuickItemView::VerticalLayoutDirection verticalLayout = QQuickItemView::TopToBottom);
94     void moved_defaultLayout_data();
95     void multipleChanges_condensed() { multipleChanges(true); }
96     void multipleChanges_condensed_data() { multipleChanges_data(); }
97     void multipleChanges_uncondensed() { multipleChanges(false); }
98     void multipleChanges_uncondensed_data() { multipleChanges_data(); }
99     void swapWithFirstItem();
100     void changeFlow();
101     void currentIndex();
102     void noCurrentIndex();
103     void keyNavigation();
104     void keyNavigation_data();
105     void defaultValues();
106     void properties();
107     void propertyChanges();
108     void componentChanges();
109     void modelChanges();
110     void positionViewAtIndex();
111     void positionViewAtIndex_rightToLeft();
112     void mirroring();
113     void snapping();
114     void resetModel();
115     void enforceRange();
116     void enforceRange_rightToLeft();
117     void QTBUG_8456();
118     void manualHighlight();
119     void footer();
120     void footer_data();
121     void initialZValues();
122     void header();
123     void header_data();
124     void extents();
125     void extents_data();
126     void resetModel_headerFooter();
127     void resizeViewAndRepaint();
128     void resizeGrid();
129     void resizeGrid_data();
130     void changeColumnCount();
131     void indexAt_itemAt_data();
132     void indexAt_itemAt();
133     void onAdd();
134     void onAdd_data();
135     void onRemove();
136     void onRemove_data();
137     void columnCount();
138     void margins();
139     void creationContext();
140     void snapToRow_data();
141     void snapToRow();
142     void snapOneRow_data();
143     void snapOneRow();
144     void unaligned();
145     void cacheBuffer();
146     void asynchronous();
147     void unrequestedVisibility();
148
149     void populateTransitions();
150     void populateTransitions_data();
151     void addTransitions();
152     void addTransitions_data();
153     void moveTransitions();
154     void moveTransitions_data();
155     void removeTransitions();
156     void removeTransitions_data();
157     void displacedTransitions();
158     void displacedTransitions_data();
159     void multipleTransitions();
160     void multipleTransitions_data();
161     void multipleDisplaced();
162
163     void inserted_leftToRight_RtL_TtB();
164     void inserted_leftToRight_RtL_TtB_data();
165     void inserted_leftToRight_LtR_BtT();
166     void inserted_leftToRight_LtR_BtT_data();
167     void inserted_leftToRight_RtL_BtT();
168     void inserted_leftToRight_RtL_BtT_data();
169     void inserted_topToBottom_LtR_TtB();
170     void inserted_topToBottom_LtR_TtB_data();
171     void inserted_topToBottom_RtL_TtB();
172     void inserted_topToBottom_RtL_TtB_data();
173     void inserted_topToBottom_LtR_BtT();
174     void inserted_topToBottom_LtR_BtT_data();
175     void inserted_topToBottom_RtL_BtT();
176     void inserted_topToBottom_RtL_BtT_data();
177
178     void removed_leftToRight_RtL_TtB();
179     void removed_leftToRight_RtL_TtB_data();
180     void removed_leftToRight_LtR_BtT();
181     void removed_leftToRight_LtR_BtT_data();
182     void removed_leftToRight_RtL_BtT();
183     void removed_leftToRight_RtL_BtT_data();
184     void removed_topToBottom_LtR_TtB();
185     void removed_topToBottom_LtR_TtB_data();
186     void removed_topToBottom_RtL_TtB();
187     void removed_topToBottom_RtL_TtB_data();
188     void removed_topToBottom_LtR_BtT();
189     void removed_topToBottom_LtR_BtT_data();
190     void removed_topToBottom_RtL_BtT();
191     void removed_topToBottom_RtL_BtT_data();
192
193     void moved_leftToRight_RtL_TtB();
194     void moved_leftToRight_RtL_TtB_data();
195     void moved_leftToRight_LtR_BtT();
196     void moved_leftToRight_LtR_BtT_data();
197     void moved_leftToRight_RtL_BtT();
198     void moved_leftToRight_RtL_BtT_data();
199     void moved_topToBottom_LtR_TtB();
200     void moved_topToBottom_LtR_TtB_data();
201     void moved_topToBottom_RtL_TtB();
202     void moved_topToBottom_RtL_TtB_data();
203     void moved_topToBottom_LtR_BtT();
204     void moved_topToBottom_LtR_BtT_data();
205     void moved_topToBottom_RtL_BtT();
206     void moved_topToBottom_RtL_BtT_data();
207
208 private:
209     QList<int> toIntList(const QVariantList &list);
210     void matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes);
211     void matchItemsAndIndexes(const QVariantMap &items, const QaimModel &model, const QList<int> &expectedIndexes);
212     void matchItemLists(const QVariantList &itemLists, const QList<QQuickItem *> &expectedItems);
213
214     void multipleChanges(bool condensed);
215     void multipleChanges_data();
216
217     QPointF expectedItemPos(QQuickGridView *grid, int index, qreal rowOffset = 0) {
218         qreal x;
219         qreal y;
220         if (grid->flow() == QQuickGridView::FlowLeftToRight) {
221             int columns = grid->width() / grid->cellWidth();
222             x = (index % columns) * grid->cellWidth();
223             y = (index / columns) * grid->cellHeight();
224             if (grid->effectiveLayoutDirection() == Qt::RightToLeft) {
225                 int col = (index % columns) * grid->cellWidth();
226                 x = grid->cellWidth() * (columns - 1) - col;
227             }
228
229             qreal offset = grid->cellHeight() * rowOffset;
230             if (grid->verticalLayoutDirection() == QQuickItemView::TopToBottom)
231                 y += offset;
232             else
233                 y = -grid->cellHeight() - y - offset;
234         } else {
235             int rows = grid->height() / grid->cellHeight();
236             x = (index / rows) * grid->cellWidth();
237             y = (index % rows) * grid->cellHeight();
238             if (grid->effectiveLayoutDirection() == Qt::RightToLeft)
239                 x = -x - grid->cellWidth();
240
241             qreal offset = grid->cellWidth() * rowOffset;
242             if (grid->effectiveLayoutDirection() == Qt::RightToLeft)
243                 x -= offset;
244             else
245                 x += offset;
246             if (grid->verticalLayoutDirection() == QQuickItemView::BottomToTop)
247                 y = -grid->cellHeight() - y;
248         }
249         return QPointF(x, y);
250     }
251
252     // Sets contentY (or contentX in TopToBottom flow) according to given row offset
253     // (in LeftToRight flow) or col offset (in TopToBottom).
254     bool setContentPos(QQuickGridView *gridview, qreal rowOrColOffset) {
255         bool contentPosChanged = (rowOrColOffset != 0);
256         qreal contentOffset = gridview->flow() == QQuickGridView::FlowLeftToRight
257                 ? rowOrColOffset * gridview->cellHeight()
258                 : rowOrColOffset * gridview->cellWidth();
259
260         if (gridview->flow() == QQuickGridView::FlowLeftToRight) {
261             if (gridview->verticalLayoutDirection() == QQuickItemView::BottomToTop)
262                 contentOffset = -gridview->height() - contentOffset;
263         } else {
264             if (gridview->effectiveLayoutDirection() == Qt::RightToLeft)
265                 contentOffset = -gridview->width() - contentOffset;
266         }
267         if (gridview->flow() == QQuickGridView::FlowLeftToRight)
268             gridview->setContentY(contentOffset);
269         else
270             gridview->setContentX(contentOffset);
271         return contentPosChanged;
272     }
273
274 #ifdef SHARE_VIEWS
275     QQuickView *getView() {
276         if (m_view) {
277             if (QString(QTest::currentTestFunction()) != testForView) {
278                 delete m_view;
279                 m_view = 0;
280             } else {
281                 m_view->setSource(QUrl());
282                 return m_view;
283             }
284         }
285
286         testForView = QTest::currentTestFunction();
287         m_view = createView();
288         return m_view;
289     }
290     void releaseView(QQuickView *view) {
291         Q_ASSERT(view == m_view);
292         m_view->setSource(QUrl());
293     }
294 #else
295     QQuickView *getView() {
296         return createView();
297     }
298     void releaseView(QQuickView *view) {
299         delete view;
300     }
301 #endif
302
303     QQuickView *m_view;
304     QString testForView;
305 };
306
307 tst_QQuickGridView::tst_QQuickGridView() : m_view(0)
308 {
309 }
310
311 void tst_QQuickGridView::init()
312 {
313 #ifdef SHARE_VIEWS
314     if (m_view && QString(QTest::currentTestFunction()) != testForView) {
315         testForView = QString();
316         delete m_view;
317         m_view = 0;
318     }
319 #endif
320 }
321
322 void tst_QQuickGridView::items()
323 {
324     QQuickView *canvas = createView();
325
326     QaimModel model;
327     model.addItem("Fred", "12345");
328     model.addItem("John", "2345");
329     model.addItem("Bob", "54321");
330     model.addItem("Billy", "22345");
331     model.addItem("Sam", "2945");
332     model.addItem("Ben", "04321");
333     model.addItem("Jim", "0780");
334
335     QQmlContext *ctxt = canvas->rootContext();
336     ctxt->setContextProperty("testModel", &model);
337
338     canvas->setSource(testFileUrl("gridview1.qml"));
339     qApp->processEvents();
340
341     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
342     QTRY_VERIFY(gridview != 0);
343
344     QQuickItem *contentItem = gridview->contentItem();
345     QTRY_VERIFY(contentItem != 0);
346
347     QTRY_COMPARE(gridview->count(), model.count());
348     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
349     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
350
351     for (int i = 0; i < model.count(); ++i) {
352         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
353         QTRY_VERIFY(name != 0);
354         QTRY_COMPARE(name->text(), model.name(i));
355         QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
356         QTRY_VERIFY(number != 0);
357         QTRY_COMPARE(number->text(), model.number(i));
358     }
359
360     // set an empty model and confirm that items are destroyed
361     QaimModel model2;
362     ctxt->setContextProperty("testModel", &model2);
363
364     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
365     QTRY_VERIFY(itemCount == 0);
366
367     delete canvas;
368 }
369
370 void tst_QQuickGridView::changed()
371 {
372     QQuickView *canvas = createView();
373
374     QaimModel model;
375     model.addItem("Fred", "12345");
376     model.addItem("John", "2345");
377     model.addItem("Bob", "54321");
378     model.addItem("Billy", "22345");
379     model.addItem("Sam", "2945");
380     model.addItem("Ben", "04321");
381     model.addItem("Jim", "0780");
382
383     QQmlContext *ctxt = canvas->rootContext();
384     ctxt->setContextProperty("testModel", &model);
385
386     canvas->setSource(testFileUrl("gridview1.qml"));
387     qApp->processEvents();
388
389     QQuickFlickable *gridview = findItem<QQuickFlickable>(canvas->rootObject(), "grid");
390     QTRY_VERIFY(gridview != 0);
391
392     QQuickItem *contentItem = gridview->contentItem();
393     QTRY_VERIFY(contentItem != 0);
394
395     model.modifyItem(1, "Will", "9876");
396     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
397     QTRY_VERIFY(name != 0);
398     QTRY_COMPARE(name->text(), model.name(1));
399     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
400     QTRY_VERIFY(number != 0);
401     QTRY_COMPARE(number->text(), model.number(1));
402
403     delete canvas;
404 }
405
406 void tst_QQuickGridView::inserted_basic()
407 {
408     QQuickView *canvas = createView();
409     canvas->show();
410
411     QaimModel model;
412     model.addItem("Fred", "12345");
413     model.addItem("John", "2345");
414     model.addItem("Bob", "54321");
415
416     canvas->rootContext()->setContextProperty("testModel", &model);
417     canvas->setSource(testFileUrl("gridview1.qml"));
418     qApp->processEvents();
419
420     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
421     QTRY_VERIFY(gridview != 0);
422
423     QQuickItem *contentItem = gridview->contentItem();
424     QTRY_VERIFY(contentItem != 0);
425
426     model.insertItem(1, "Will", "9876");
427
428     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
429     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
430
431     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
432     QTRY_VERIFY(name != 0);
433     QTRY_COMPARE(name->text(), model.name(1));
434     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
435     QTRY_VERIFY(number != 0);
436     QTRY_COMPARE(number->text(), model.number(1));
437
438     // Checks that onAdd is called
439     int added = canvas->rootObject()->property("added").toInt();
440     QTRY_COMPARE(added, 1);
441
442     // Confirm items positioned correctly
443     for (int i = 0; i < model.count(); ++i) {
444         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
445         QTRY_COMPARE(item->x(), (i%3)*80.0);
446         QTRY_COMPARE(item->y(), (i/3)*60.0);
447     }
448
449     model.insertItem(0, "Foo", "1111"); // zero index, and current item
450
451     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
452
453     name = findItem<QQuickText>(contentItem, "textName", 0);
454     QTRY_VERIFY(name != 0);
455     QTRY_COMPARE(name->text(), model.name(0));
456     number = findItem<QQuickText>(contentItem, "textNumber", 0);
457     QTRY_VERIFY(number != 0);
458     QTRY_COMPARE(number->text(), model.number(0));
459
460     QTRY_COMPARE(gridview->currentIndex(), 1);
461
462     // Confirm items positioned correctly
463     for (int i = 0; i < model.count(); ++i) {
464         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
465         QTRY_VERIFY(item->x() == (i%3)*80);
466         QTRY_VERIFY(item->y() == (i/3)*60);
467     }
468
469     for (int i = model.count(); i < 30; ++i)
470         model.insertItem(i, "Hello", QString::number(i));
471
472     gridview->setContentY(120);
473
474     // Insert item outside visible area
475     model.insertItem(1, "Hello", "1324");
476
477     QTRY_VERIFY(gridview->contentY() == 120);
478
479     delete canvas;
480 }
481
482 void tst_QQuickGridView::inserted_defaultLayout(QQuickGridView::Flow flow,
483                                        Qt::LayoutDirection horizLayout,
484                                        QQuickItemView::VerticalLayoutDirection verticalLayout)
485 {
486     QFETCH(qreal, contentYRowOffset);
487     QFETCH(int, insertIndex);
488     QFETCH(int, insertCount);
489     QFETCH(int, insertIndex_ttb);
490     QFETCH(int, insertCount_ttb);
491     QFETCH(qreal, rowOffsetAfterMove);
492
493     QaimModel model;
494     for (int i = 0; i < 30; i++)
495         model.addItem("Item" + QString::number(i), "");
496
497     QQuickView *canvas = getView();
498     QQmlContext *ctxt = canvas->rootContext();
499     ctxt->setContextProperty("testModel", &model);
500     ctxt->setContextProperty("testTopToBottom", flow == QQuickGridView::FlowTopToBottom);
501     ctxt->setContextProperty("testRightToLeft", horizLayout == Qt::RightToLeft);
502     ctxt->setContextProperty("testBottomToTop", verticalLayout == QQuickGridView::BottomToTop);
503     canvas->setSource(testFileUrl("layouts.qml"));
504     canvas->show();
505     qApp->processEvents();
506
507     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
508     QTRY_VERIFY(gridview != 0);
509     QQuickItem *contentItem = gridview->contentItem();
510     QTRY_VERIFY(contentItem != 0);
511
512     if (flow == QQuickGridView::FlowTopToBottom) {
513         insertIndex = insertIndex_ttb;
514         insertCount = insertCount_ttb;
515     }
516     if (setContentPos(gridview, contentYRowOffset))
517         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
518
519     QList<QPair<QString, QString> > newData;
520     for (int i=0; i<insertCount; i++)
521         newData << qMakePair(QString("value %1").arg(i), QString::number(i));
522     model.insertItems(insertIndex, newData);
523     QTRY_COMPARE(gridview->property("count").toInt(), model.count());
524
525     // check visibleItems.first() is in correct position
526     QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper", 0);
527     QVERIFY(item0);
528     QPointF firstPos(0, 0);
529     if (horizLayout == Qt::RightToLeft)
530         firstPos.rx() = flow == QQuickGridView::FlowLeftToRight ? gridview->width() - gridview->cellWidth() : -gridview->cellWidth();
531     if (verticalLayout == QQuickItemView::BottomToTop)
532         firstPos.ry() -= gridview->cellHeight();
533     QCOMPARE(item0->pos(), firstPos);
534
535     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
536     int firstVisibleIndex = -1;
537     for (int i=0; i<items.count(); i++) {
538         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
539         if (item && item->isVisible()) {
540             firstVisibleIndex = i;
541             break;
542         }
543     }
544     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
545
546     // Confirm items positioned correctly and indexes correct
547     for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) {
548         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
549         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
550         QCOMPARE(item->pos(), expectedItemPos(gridview, i, rowOffsetAfterMove));
551         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
552         QVERIFY(name != 0);
553         QCOMPARE(name->text(), model.name(i));
554     }
555
556     releaseView(canvas);
557 }
558
559 void tst_QQuickGridView::inserted_defaultLayout_data()
560 {
561     QTest::addColumn<qreal>("contentYRowOffset");
562     QTest::addColumn<int>("insertIndex");
563     QTest::addColumn<int>("insertCount");
564     QTest::addColumn<int>("insertIndex_ttb");
565     QTest::addColumn<int>("insertCount_ttb");
566     QTest::addColumn<qreal>("rowOffsetAfterMove");
567
568     QTest::newRow("add 1, before visible items")
569             << 2.0     // show 6-23
570             << 5 << 1
571                << 9 << 1
572             << 0.0;   // insert 1 above first visible, grid is rearranged; first visible moves forward within its row
573                       // new 1st visible item is at 0
574
575     QTest::newRow("add 2, before visible items")
576             << 2.0     // show 6-23
577             << 5 << 2
578                << 9 << 2
579             << 0.0;   // insert 2 above first visible, grid is rearranged; first visible moves forward within its row
580
581     QTest::newRow("add 3, before visible items")
582             << 2.0     // show 6-23
583             << 5 << 3
584                << 9 << 5
585             << -1.0;   // insert 3 (1 row) above first visible in negative pos, first visible does not move
586
587     QTest::newRow("add 5, before visible items")
588             << 2.0     // show 6-23
589             << 5 << 5
590                << 9 << 7
591             << -1.0;   // insert 1 row + 2 items above first visible, 1 row added at negative pos,
592                         // grid is rearranged and first visible moves forward within its row
593
594     QTest::newRow("add 6, before visible items")
595             << 2.0     // show 6-23
596             << 5 << 6
597                << 9 << 10
598             << -1.0 * 2;   // insert 2 rows above first visible in negative pos, first visible does not move
599
600
601
602    QTest::newRow("add 1, at start of visible, content at start")
603             << 0.0
604             << 0 << 1
605                << 0 << 1
606             << 0.0;
607
608     QTest::newRow("add multiple, at start of visible, content at start")
609             << 0.0
610             << 0 << 3
611                << 0 << 5
612             << 0.0;
613
614     QTest::newRow("add 1, at start of visible, content not at start")
615             << 2.0     // show 6-23
616             << 6 << 1
617                << 10 << 1
618             << 0.0;
619
620     QTest::newRow("add multiple, at start of visible, content not at start")
621             << 2.0     // show 6-23
622             << 6 << 3
623                << 10 << 5
624             << 0.0;
625
626
627     QTest::newRow("add 1, at end of visible, content at start")
628             << 0.0
629             << 17 << 1
630                << 14 << 1
631             << 0.0;
632
633     QTest::newRow("add row, at end of visible, content at start")
634             << 0.0
635             << 17 << 3
636                << 14 << 5
637             << 0.0;
638
639     QTest::newRow("add 1, at end of visible, content not at start")
640             << 2.0     // show 6-23
641             << 17+6 << 1
642                << 14+10 << 1
643             << 0.0;
644
645     QTest::newRow("add multiple, at end of visible, content not at start")
646             << 2.0     // show 6-23
647             << 17+6 << 3
648                << 14+10 << 5
649             << 0.0;
650
651
652     QTest::newRow("add 1, after visible, content at start")
653             << 0.0
654             << 20 << 1
655                << 18 << 1
656             << 0.0;
657
658     QTest::newRow("add row, after visible, content at start")
659             << 0.0
660             << 20 << 3
661                << 18 << 5
662             << 0.0;
663
664     QTest::newRow("add 1, after visible, content not at start")
665             << 2.0     // show 6-23
666             << 20+6 << 1
667                << 18+10 << 1
668             << 0.0;
669
670     QTest::newRow("add multiple, after visible, content not at start")
671             << 2.0     // show 6-23
672             << 20+6 << 3
673                << 18+10 << 3
674             << 0.0;
675 }
676
677 void tst_QQuickGridView::insertBeforeVisible()
678 {
679     QFETCH(int, insertIndex);
680     QFETCH(int, insertCount);
681     QFETCH(int, cacheBuffer);
682
683     QQuickText *name;
684     QQuickView *canvas = getView();
685
686     QaimModel model;
687     for (int i = 0; i < 30; i++)
688         model.addItem("Item" + QString::number(i), "");
689
690     QQmlContext *ctxt = canvas->rootContext();
691     ctxt->setContextProperty("testModel", &model);
692     canvas->setSource(testFileUrl("gridview1.qml"));
693     canvas->show();
694     qApp->processEvents();
695
696     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
697     QTRY_VERIFY(gridview != 0);
698     QQuickItem *contentItem = gridview->contentItem();
699     QTRY_VERIFY(contentItem != 0);
700
701     gridview->setCacheBuffer(cacheBuffer);
702     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
703
704     // trigger a refill (not just setting contentY) so that the visibleItems grid is updated
705     int firstVisibleIndex = 12;     // move to an index where the top item is not visible
706     gridview->setContentY(firstVisibleIndex/3 * 60.0);
707     gridview->setCurrentIndex(firstVisibleIndex);
708     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
709
710     QTRY_COMPARE(gridview->currentIndex(), firstVisibleIndex);
711     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", firstVisibleIndex);
712     QVERIFY(item);
713     QCOMPARE(item->y(), gridview->contentY());
714
715     QList<QPair<QString, QString> > newData;
716     for (int i=0; i<insertCount; i++)
717         newData << qMakePair(QString("value %1").arg(i), QString::number(i));
718     model.insertItems(insertIndex, newData);
719     QTRY_COMPARE(gridview->property("count").toInt(), model.count());
720
721     // now, moving to the top of the view should position the inserted items correctly
722     int itemsOffsetAfterMove = (insertCount / 3) * -60.0;
723     gridview->setCurrentIndex(0);
724     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
725     QTRY_COMPARE(gridview->currentIndex(), 0);
726     QTRY_COMPARE(gridview->contentY(), 0.0 + itemsOffsetAfterMove);
727
728     // Confirm items positioned correctly and indexes correct
729     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
730     for (int i = 0; i < model.count() && i < itemCount; ++i) {
731         item = findItem<QQuickItem>(contentItem, "wrapper", i);
732         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
733         QCOMPARE(item->x(), (i%3)*80.0);
734         QCOMPARE(item->y(), (i/3)*60.0 + itemsOffsetAfterMove);
735         name = findItem<QQuickText>(contentItem, "textName", i);
736         QVERIFY(name != 0);
737         QTRY_COMPARE(name->text(), model.name(i));
738     }
739
740     releaseView(canvas);
741 }
742
743 void tst_QQuickGridView::insertBeforeVisible_data()
744 {
745     QTest::addColumn<int>("insertIndex");
746     QTest::addColumn<int>("insertCount");
747     QTest::addColumn<int>("cacheBuffer");
748
749     QTest::newRow("insert 1 at 0, 0 buffer") << 0 << 1 << 0;
750     QTest::newRow("insert 1 at 0, 100 buffer") << 0 << 1 << 100;
751     QTest::newRow("insert 1 at 0, 500 buffer") << 0 << 1 << 500;
752
753     QTest::newRow("insert 1 at 1, 0 buffer") << 1 << 1 << 0;
754     QTest::newRow("insert 1 at 1, 100 buffer") << 1 << 1 << 100;
755     QTest::newRow("insert 1 at 1, 500 buffer") << 1 << 1 << 500;
756
757     QTest::newRow("insert multiple at 0, 0 buffer") << 0 << 6 << 0;
758     QTest::newRow("insert multiple at 0, 100 buffer") << 0 << 6 << 100;
759     QTest::newRow("insert multiple at 0, 500 buffer") << 0 << 6 << 500;
760
761     QTest::newRow("insert multiple at 1, 0 buffer") << 1 << 6 << 0;
762     QTest::newRow("insert multiple at 1, 100 buffer") << 1 << 6 << 100;
763     QTest::newRow("insert multiple at 1, 500 buffer") << 1 << 6 << 500;
764 }
765
766 void tst_QQuickGridView::removed_basic()
767 {
768     QQuickView *canvas = createView();
769     canvas->show();
770
771     QaimModel model;
772     for (int i = 0; i < 40; i++)
773         model.addItem("Item" + QString::number(i), "");
774
775     canvas->rootContext()->setContextProperty("testModel", &model);
776     canvas->setSource(testFileUrl("gridview1.qml"));
777     qApp->processEvents();
778
779     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
780     QTRY_VERIFY(gridview != 0);
781     QQuickItem *contentItem = gridview->contentItem();
782     QTRY_VERIFY(contentItem != 0);
783     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
784
785     model.removeItem(1);
786     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
787
788     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
789     QTRY_VERIFY(name != 0);
790     QTRY_COMPARE(name->text(), model.name(1));
791     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
792     QTRY_VERIFY(number != 0);
793     QTRY_COMPARE(number->text(), model.number(1));
794
795
796     // Checks that onRemove is called
797     QString removed = canvas->rootObject()->property("removed").toString();
798     QTRY_COMPARE(removed, QString("Item1"));
799
800     // Confirm items positioned correctly
801     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
802     for (int i = 0; i < model.count() && i < itemCount; ++i) {
803         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
804         if (!item) qWarning() << "Item" << i << "not found";
805         QTRY_VERIFY(item->x() == (i%3)*80);
806         QTRY_VERIFY(item->y() == (i/3)*60);
807     }
808
809     // Remove first item (which is the current item);
810     model.removeItem(0);
811     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
812
813     name = findItem<QQuickText>(contentItem, "textName", 0);
814     QTRY_VERIFY(name != 0);
815     QTRY_COMPARE(name->text(), model.name(0));
816     number = findItem<QQuickText>(contentItem, "textNumber", 0);
817     QTRY_VERIFY(number != 0);
818     QTRY_COMPARE(number->text(), model.number(0));
819
820
821     // Confirm items positioned correctly
822     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
823     for (int i = 0; i < model.count() && i < itemCount; ++i) {
824         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
825         if (!item) qWarning() << "Item" << i << "not found";
826         QTRY_VERIFY(item->x() == (i%3)*80);
827         QTRY_VERIFY(item->y() == (i/3)*60);
828     }
829
830     // Remove items not visible
831     model.removeItem(25);
832     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
833
834     // Confirm items positioned correctly
835     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
836     for (int i = 0; i < model.count() && i < itemCount; ++i) {
837         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
838         if (!item) qWarning() << "Item" << i << "not found";
839         QTRY_VERIFY(item->x() == (i%3)*80);
840         QTRY_VERIFY(item->y() == (i/3)*60);
841     }
842
843     // Remove items before visible
844     gridview->setContentY(120);
845     gridview->setCurrentIndex(10);
846
847     // Setting currentIndex above shouldn't cause view to scroll
848     QTRY_COMPARE(gridview->contentY(), 120.0);
849
850     model.removeItem(1);
851     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
852
853     // Confirm items positioned correctly
854     for (int i = 6; i < 18; ++i) {
855         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
856         if (!item) qWarning() << "Item" << i << "not found";
857         QTRY_VERIFY(item->x() == (i%3)*80);
858         QTRY_VERIFY(item->y() == (i/3)*60);
859     }
860
861     // Remove currentIndex
862     QQuickItem *oldCurrent = gridview->currentItem();
863     model.removeItem(9);
864     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
865
866     QTRY_COMPARE(gridview->currentIndex(), 9);
867     QTRY_VERIFY(gridview->currentItem() != oldCurrent);
868
869     gridview->setContentY(0);
870     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
871
872     // Confirm items positioned correctly
873     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
874     for (int i = 0; i < model.count() && i < itemCount; ++i) {
875         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
876         QTRY_VERIFY(item->x() == (i%3)*80);
877         QTRY_VERIFY(item->y() == (i/3)*60);
878     }
879
880     // remove item outside current view.
881     gridview->setCurrentIndex(32);
882     gridview->setContentY(240);
883
884     model.removeItem(30);
885     QTRY_VERIFY(gridview->currentIndex() == 31);
886
887     // remove current item beyond visible items.
888     gridview->setCurrentIndex(20);
889     gridview->setContentY(0);
890     model.removeItem(20);
891
892     QTRY_COMPARE(gridview->currentIndex(), 20);
893     QTRY_VERIFY(gridview->currentItem() != 0);
894
895     // remove item before current, but visible
896     gridview->setCurrentIndex(8);
897     gridview->setContentY(240);
898     oldCurrent = gridview->currentItem();
899     model.removeItem(6);
900
901     QTRY_COMPARE(gridview->currentIndex(), 7);
902     QTRY_VERIFY(gridview->currentItem() == oldCurrent);
903
904     delete canvas;
905 }
906
907 void tst_QQuickGridView::removed_defaultLayout(QQuickGridView::Flow flow,
908                                                Qt::LayoutDirection horizLayout,
909                                                QQuickItemView::VerticalLayoutDirection verticalLayout)
910 {
911     QFETCH(qreal, contentYRowOffset);
912     QFETCH(int, removeIndex);
913     QFETCH(int, removeCount);
914     QFETCH(int, removeIndex_ttb);
915     QFETCH(int, removeCount_ttb);
916     QFETCH(qreal, rowOffsetAfterMove);
917     QFETCH(QString, firstVisible);
918     QFETCH(QString, firstVisible_ttb);
919
920     QQuickView *canvas = getView();
921
922     QaimModel model;
923     for (int i = 0; i < 30; i++)
924         model.addItem("Item" + QString::number(i), "");
925
926     QQmlContext *ctxt = canvas->rootContext();
927     ctxt->setContextProperty("testModel", &model);
928     ctxt->setContextProperty("testTopToBottom", flow == QQuickGridView::FlowTopToBottom);
929     ctxt->setContextProperty("testRightToLeft", horizLayout == Qt::RightToLeft);
930     ctxt->setContextProperty("testBottomToTop", verticalLayout == QQuickGridView::BottomToTop);
931     canvas->setSource(testFileUrl("layouts.qml"));
932     canvas->show();
933     qApp->processEvents();
934
935     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
936     QTRY_VERIFY(gridview != 0);
937     QQuickItem *contentItem = gridview->contentItem();
938     QTRY_VERIFY(contentItem != 0);
939
940     if (flow == QQuickGridView::FlowTopToBottom) {
941         removeIndex = removeIndex_ttb;
942         removeCount = removeCount_ttb;
943         firstVisible = firstVisible_ttb;
944     }
945     if (setContentPos(gridview, contentYRowOffset))
946         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
947
948     model.removeItems(removeIndex, removeCount);
949     QTRY_COMPARE(gridview->property("count").toInt(), model.count());
950
951     QString firstName;
952     int firstVisibleIndex = -1;
953     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
954     QRectF viewRect(gridview->contentX(), gridview->contentY(), gridview->width(), gridview->height());
955     for (int i=0; i<items.count(); i++) {
956         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
957         if (item) {
958             QRectF itemRect(item->x(), item->y(), item->width(), item->height());
959             if (item->isVisible() && viewRect.intersects(itemRect)) {
960                 firstVisibleIndex = i;
961                 QQmlExpression en(qmlContext(item), item, "name");
962                 firstName = en.evaluate().toString();
963                 break;
964             }
965         }
966     }
967     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
968     QCOMPARE(firstName, firstVisible);
969
970     // Confirm items positioned correctly and indexes correct
971     for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) {
972         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
973         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
974         QCOMPARE(item->pos(), expectedItemPos(gridview, i, rowOffsetAfterMove));
975         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
976         QVERIFY(name != 0);
977         QTRY_COMPARE(name->text(), model.name(i));
978     }
979
980     releaseView(canvas);
981 }
982
983 void tst_QQuickGridView::removed_defaultLayout_data()
984 {
985     QTest::addColumn<qreal>("contentYRowOffset");
986     QTest::addColumn<int>("removeIndex");
987     QTest::addColumn<int>("removeCount");
988     QTest::addColumn<int>("removeIndex_ttb");
989     QTest::addColumn<int>("removeCount_ttb");
990     QTest::addColumn<qreal>("rowOffsetAfterMove");
991     QTest::addColumn<QString>("firstVisible");
992     QTest::addColumn<QString>("firstVisible_ttb");
993
994     QTest::newRow("remove 1, before visible items")
995             << 2.0     // show 6-23
996             << 2 << 1
997                << 4 << 1
998             << 0.0 << "Item7" << "Item11";
999
1000     QTest::newRow("remove 1, before visible position")
1001             << 2.0     // show 6-23
1002             << 3 << 1
1003                << 5 << 1
1004             << 0.0 << "Item7" << "Item11";
1005
1006     QTest::newRow("remove multiple (1 row), all before visible items")
1007             << 2.0
1008             << 1 << 3
1009                << 1 << 5
1010             << 1.0 << "Item6" << "Item10";    // removed top row, slide down by 1 row
1011
1012     QTest::newRow("remove multiple, all before visible items, remove item 0")
1013             << 2.0
1014             << 0 << 4
1015                << 0 << 6
1016             << 1.0 << "Item7" << "Item11";    // removed top row, slide down by 1 row
1017
1018     QTest::newRow("remove multiple rows, all before visible items")
1019             << 4.0     // show 12-29
1020             << 1 << 7
1021                << 1 << 12
1022             << 2.0 << "Item13" << "Item17";
1023
1024     QTest::newRow("remove one row before visible, content y not on item border")
1025             << 1.5
1026             << 0 << 3
1027                << 0 << 5
1028             << 1.0 << "Item3" << "Item5"; // 1 row removed
1029
1030     QTest::newRow("remove mix of visible/non-visible")
1031             << 2.0     // show 6-23
1032             << 2 << 3
1033                << 4 << 3
1034             << 1.0 << "Item6" << "Item8"; // 1 row removed
1035
1036
1037     // remove 3,4,5 before the visible pos, first row moves down to just before the visible pos,
1038     // items 6,7 are removed from view, item 8 slides up to original pos of item 6 (120px)
1039     QTest::newRow("remove multiple, mix of items from before and within visible items")
1040             << 2.0
1041             << 3 << 5
1042                 << 5 << 7
1043             << 1.0 << "Item8" << "Item12";    // adjust for the 1 row removed before the visible
1044
1045     QTest::newRow("remove multiple, mix of items from before and within visible items, remove item 0")
1046             << 2.0
1047             << 0 << 8
1048                << 0 << 12
1049             << 1.0 * 2 << "Item8" << "Item12";    // adjust for the 2 rows removed before the visible
1050
1051
1052     QTest::newRow("remove 1, from start of visible, content at start")
1053             << 0.0
1054             << 0 << 1
1055                << 0 << 1
1056             << 0.0 << "Item1" << "Item1";
1057
1058     QTest::newRow("remove multiple, from start of visible, content at start")
1059             << 0.0
1060             << 0 << 3
1061                << 0 << 5
1062             << 0.0 << "Item3" << "Item5";
1063
1064     QTest::newRow("remove 1, from start of visible, content not at start")
1065             << 2.0     // show 6-23
1066             << 4 << 1
1067                << 7 << 1
1068             << 0.0 << "Item7" << "Item11";
1069
1070     QTest::newRow("remove multiple, from start of visible, content not at start")
1071             << 2.0     // show 6-23
1072             << 4 << 3
1073                << 7 << 5
1074             << 0.0 << "Item9" << "Item15";
1075
1076
1077     QTest::newRow("remove 1, from middle of visible, content at start")
1078             << 0.0
1079             << 10 << 1
1080                << 12 << 1
1081             << 0.0 << "Item0" << "Item0";
1082
1083     QTest::newRow("remove multiple, from middle of visible, content at start")
1084             << 0.0
1085             << 10 << 5
1086                << 12 << 5
1087             << 0.0 << "Item0" << "Item0";
1088
1089     QTest::newRow("remove 1, from middle of visible, content not at start")
1090             << 2.0     // show 6-23
1091             << 10 << 1
1092                << 12 << 1
1093             << 0.0 << "Item6" << "Item10";
1094
1095     QTest::newRow("remove multiple, from middle of visible, content not at start")
1096             << 2.0     // show 6-23
1097             << 10 << 5
1098                << 12 << 7
1099             << 0.0 << "Item6" << "Item10";
1100
1101
1102     QTest::newRow("remove 1, after visible, content at start")
1103             << 0.0
1104             << 16 << 1
1105                << 15 << 1
1106             << 0.0 << "Item0" << "Item0";
1107
1108     QTest::newRow("remove multiple, after visible, content at start")
1109             << 0.0
1110             << 16 << 5
1111                << 15 << 7
1112             << 0.0 << "Item0" << "Item0";
1113
1114     QTest::newRow("remove 1, after visible, content not at start")
1115             << 2.0     // show 6-23
1116             << 16+4 << 1
1117                << 15+10 << 1
1118             << 0.0 << "Item6" << "Item10";
1119
1120     QTest::newRow("remove multiple, after visible, content not at start")
1121             << 2.0     // show 6-23
1122             << 16+4 << 5
1123                << 15+10 << 7
1124             << 0.0 << "Item6" << "Item10";
1125
1126     QTest::newRow("remove multiple, mix of items from within and after visible items")
1127             << 2.0     // show 6-23
1128             << 20 << 5
1129                << 22 << 7
1130             << 0.0 << "Item6" << "Item10";
1131 }
1132
1133 void tst_QQuickGridView::addOrRemoveBeforeVisible()
1134 {
1135     // QTBUG-21588: ensure re-layout is done on grid after adding or removing
1136     // items from before the visible area
1137
1138     QFETCH(bool, doAdd);
1139     QFETCH(qreal, newTopContentY);
1140
1141     QQuickView *canvas = getView();
1142     canvas->show();
1143
1144     QaimModel model;
1145     for (int i = 0; i < 30; i++)
1146         model.addItem("Item" + QString::number(i), "");
1147
1148     canvas->rootContext()->setContextProperty("testModel", &model);
1149     canvas->setSource(testFileUrl("gridview1.qml"));
1150
1151     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1152     QTRY_VERIFY(gridview != 0);
1153     QQuickItem *contentItem = gridview->contentItem();
1154     QTRY_VERIFY(contentItem != 0);
1155
1156     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 0);
1157     QTRY_COMPARE(name->text(), QString("Item0"));
1158
1159     gridview->setCurrentIndex(0);
1160     qApp->processEvents();
1161     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1162
1163     // scroll down until item 0 is no longer drawn
1164     // (bug not triggered if we just move using content y, since that doesn't
1165     // refill and change the visible items)
1166     gridview->setCurrentIndex(24);
1167     qApp->processEvents();
1168
1169     QTRY_COMPARE(gridview->currentIndex(), 24);
1170     QTRY_COMPARE(gridview->contentY(), 220.0);
1171
1172     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1173     QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 0));  // 0 shouldn't be visible
1174
1175     if (doAdd) {
1176         model.insertItem(0, "New Item", "New Item number");
1177         QTRY_COMPARE(gridview->count(), 31);
1178     } else {
1179         model.removeItem(0);
1180         QTRY_COMPARE(gridview->count(), 29);
1181     }
1182
1183     // scroll back up and item 0 should be gone
1184     gridview->setCurrentIndex(0);
1185     qApp->processEvents();
1186     QTRY_COMPARE(gridview->currentIndex(), 0);
1187     QTRY_COMPARE(gridview->contentY(), newTopContentY);
1188
1189     name = findItem<QQuickText>(contentItem, "textName", 0);
1190     if (doAdd)
1191         QCOMPARE(name->text(), QString("New Item"));
1192     else
1193         QCOMPARE(name->text(), QString("Item1"));
1194
1195     // Confirm items positioned correctly
1196     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1197     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1198         QTRY_VERIFY(findItem<QQuickItem>(contentItem, "wrapper", i));
1199         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1200         QTRY_VERIFY(item->x() == (i%3)*80);
1201         QTRY_VERIFY(item->y() == (i/3)*60 + newTopContentY);
1202     }
1203
1204     releaseView(canvas);
1205 }
1206
1207 void tst_QQuickGridView::addOrRemoveBeforeVisible_data()
1208 {
1209     QTest::addColumn<bool>("doAdd");
1210     QTest::addColumn<qreal>("newTopContentY");
1211
1212     QTest::newRow("add") << true << -60.0;
1213     QTest::newRow("remove") << false << -60.0;
1214 }
1215
1216 void tst_QQuickGridView::clear()
1217 {
1218     QQuickView *canvas = createView();
1219
1220     QaimModel model;
1221     for (int i = 0; i < 30; i++)
1222         model.addItem("Item" + QString::number(i), "");
1223
1224     canvas->rootContext()->setContextProperty("testModel", &model);
1225     canvas->setSource(testFileUrl("gridview1.qml"));
1226     canvas->show();
1227     qApp->processEvents();
1228
1229     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1230     QVERIFY(gridview != 0);
1231     QQuickItem *contentItem = gridview->contentItem();
1232     QVERIFY(contentItem != 0);
1233     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1234
1235     model.clear();
1236
1237     QVERIFY(gridview->count() == 0);
1238     QVERIFY(gridview->currentItem() == 0);
1239     QVERIFY(gridview->contentY() == 0);
1240     QVERIFY(gridview->currentIndex() == -1);
1241
1242     // confirm sanity when adding an item to cleared list
1243     model.addItem("New", "1");
1244     QTRY_COMPARE(gridview->count(), 1);
1245     QVERIFY(gridview->currentItem() != 0);
1246     QVERIFY(gridview->currentIndex() == 0);
1247
1248     delete canvas;
1249 }
1250
1251 void tst_QQuickGridView::moved_defaultLayout(QQuickGridView::Flow flow,
1252                                              Qt::LayoutDirection horizLayout,
1253                                              QQuickItemView::VerticalLayoutDirection verticalLayout)
1254 {
1255     QFETCH(qreal, contentYRowOffset);
1256     QFETCH(int, from);
1257     QFETCH(int, to);
1258     QFETCH(int, count);
1259     QFETCH(int, from_ttb);
1260     QFETCH(int, to_ttb);
1261     QFETCH(int, count_ttb);
1262     QFETCH(qreal, rowOffsetAfterMove);
1263
1264     QQuickView *canvas = getView();
1265
1266     QaimModel model;
1267     for (int i = 0; i < 30; i++)
1268         model.addItem("Item" + QString::number(i), "");
1269
1270     QQmlContext *ctxt = canvas->rootContext();
1271     ctxt->setContextProperty("testModel", &model);
1272     ctxt->setContextProperty("testTopToBottom", flow == QQuickGridView::FlowTopToBottom);
1273     ctxt->setContextProperty("testRightToLeft", horizLayout == Qt::RightToLeft);
1274     ctxt->setContextProperty("testBottomToTop", verticalLayout == QQuickGridView::BottomToTop);
1275     canvas->setSource(testFileUrl("layouts.qml"));
1276     canvas->show();
1277     qApp->processEvents();
1278
1279     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1280     QTRY_VERIFY(gridview != 0);
1281     QQuickItem *contentItem = gridview->contentItem();
1282     QTRY_VERIFY(contentItem != 0);
1283     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1284
1285     QQuickItem *currentItem = gridview->currentItem();
1286     QTRY_VERIFY(currentItem != 0);
1287
1288     if (flow == QQuickGridView::FlowTopToBottom) {
1289         from = from_ttb;
1290         to = to_ttb;
1291         count = count_ttb;
1292     }
1293     if (setContentPos(gridview, contentYRowOffset))
1294         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1295
1296     model.moveItems(from, to, count);
1297     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1298
1299     // Confirm items positioned correctly and indexes correct
1300     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
1301     int firstVisibleIndex = -1;
1302     for (int i=0; i<items.count(); i++) {
1303         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1304         if (item && item->isVisible()) {
1305             firstVisibleIndex = i;
1306             break;
1307         }
1308     }
1309     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
1310
1311     for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) {
1312         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1313         if (!item &&
1314                 ( (flow == QQuickGridView::FlowLeftToRight && i >= firstVisibleIndex + (3*6))
1315                 || flow == QQuickGridView::FlowTopToBottom && i >= firstVisibleIndex + (5*3) ) ) {
1316             continue;   // index has moved out of view
1317         }
1318         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1319         QCOMPARE(item->pos(), expectedItemPos(gridview, i, rowOffsetAfterMove));
1320         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
1321         QVERIFY(name != 0);
1322         QTRY_COMPARE(name->text(), model.name(i));
1323
1324         // current index should have been updated
1325         if (item == currentItem)
1326             QTRY_COMPARE(gridview->currentIndex(), i);
1327     }
1328
1329     releaseView(canvas);
1330 }
1331
1332 void tst_QQuickGridView::moved_defaultLayout_data()
1333 {
1334     QTest::addColumn<qreal>("contentYRowOffset");
1335     QTest::addColumn<int>("from");
1336     QTest::addColumn<int>("to");
1337     QTest::addColumn<int>("count");
1338     QTest::addColumn<int>("from_ttb");
1339     QTest::addColumn<int>("to_ttb");
1340     QTest::addColumn<int>("count_ttb");
1341     QTest::addColumn<qreal>("rowOffsetAfterMove");
1342
1343     // model starts with 30 items, each 80x60, in area 240x320
1344     // 18 items should be visible at a time
1345
1346     // The first visible item should not move upwards and out of the view
1347     // if items are moved/removed before it.
1348
1349
1350     QTest::newRow("move 1 forwards, within visible items")
1351             << 0.0
1352             << 1 << 8 << 1
1353                << 2 << 12 << 1
1354             << 0.0;
1355
1356     QTest::newRow("move 1 forwards, from non-visible -> visible")
1357             << 2.0     // show 6-23
1358             << 1 << 8+6 << 1
1359                << 2 << 12+10 << 1
1360             << 0.0;
1361
1362     QTest::newRow("move 1 forwards, from non-visible -> visible (move first item)")
1363             << 2.0     // // show 6-23
1364             << 0 << 6 << 1
1365                << 0 << 10 << 1
1366             << 0.0;
1367
1368     QTest::newRow("move 1 forwards, from visible -> non-visible")
1369             << 0.0
1370             << 1 << 20 << 1
1371                << 1 << 20 << 1
1372             << 0.0;
1373
1374     QTest::newRow("move 1 forwards, from visible -> non-visible (move first item)")
1375             << 0.0
1376             << 0 << 20 << 1
1377                << 0 << 20 << 1
1378             << 0.0;
1379
1380
1381     QTest::newRow("move 1 backwards, within visible items")
1382             << 0.0
1383             << 10 << 5 << 1
1384                << 10 << 5 << 1
1385             << 0.0;
1386
1387     QTest::newRow("move 1 backwards, within visible items (to first index)")
1388             << 0.0
1389             << 10 << 0 << 1
1390                << 10 << 0 << 1
1391             << 0.0;
1392
1393     QTest::newRow("move 1 backwards, from non-visible -> visible")
1394             << 0.0
1395             << 28 << 8 << 1
1396                << 28 << 8 << 1
1397             << 0.0;
1398
1399     QTest::newRow("move 1 backwards, from non-visible -> visible (move last item)")
1400             << 0.0
1401             << 29 << 14 << 1
1402                << 29 << 14 << 1
1403             << 0.0;
1404
1405     QTest::newRow("move 1 backwards, from visible -> non-visible")
1406             << 2.0     // show 6-23
1407             << 7 << 1 << 1
1408                << 10 << 1 << 1
1409             << 0.0;     // only 1 item moved back, so items shift accordingly and first row doesn't move
1410
1411     QTest::newRow("move 1 backwards, from visible -> non-visible (move first item)")
1412             << 2.0     // show 6-23
1413             << 7 << 0 << 1
1414                << 10 << 0 << 1
1415             << 0.0;     // only 1 item moved back, so items shift accordingly and first row doesn't move
1416
1417
1418     QTest::newRow("move multiple forwards, within visible items")
1419             << 0.0
1420             << 0 << 5 << 3
1421                << 0 << 7 << 5
1422             << 0.0;
1423
1424     QTest::newRow("move multiple backwards, within visible items (move first item)")
1425             << 0.0
1426             << 10 << 0 << 3
1427                << 12 << 0 << 5
1428             << 0.0;
1429
1430     QTest::newRow("move multiple forwards, before visible items")
1431             << 2.0     // show 6-23
1432             << 3 << 4 << 3      // 3, 4, 5 move to after 6
1433                << 5 << 6 << 5
1434             << 1.0;      // row of 3,4,5 has moved down
1435
1436     QTest::newRow("move multiple forwards, from non-visible -> visible")
1437             << 2.0     // show 6-23
1438             << 1 << 6 << 3
1439                << 1 << 10 << 5
1440             << 1.0; // 1st row (it's above visible area) disappears, 0 drops down 1 row, first visible item (6) stays where it is
1441
1442     QTest::newRow("move multiple forwards, from non-visible -> visible (move first item)")
1443             << 2.0     // show 6-23
1444             << 0 << 6 << 3
1445                << 0 << 10 << 5
1446             << 1.0;    // top row moved and shifted to below 3rd row, all items should shift down by 1 row
1447
1448     QTest::newRow("move multiple forwards, mix of non-visible/visible")
1449             << 2.0
1450             << 3 << 16 << 6
1451                << 5 << 18 << 10
1452             << 1.0;    // top two rows removed, third row is now the first visible
1453
1454     QTest::newRow("move multiple forwards, to bottom of view")
1455             << 0.0
1456             << 5 << 13 << 5
1457                << 1 << 8 << 6
1458             << 0.0;
1459
1460     QTest::newRow("move multiple forwards, to bottom of view, first row -> last")
1461             << 0.0
1462             << 0 << 15 << 3
1463                << 0 << 10 << 5
1464             << 0.0;
1465
1466     QTest::newRow("move multiple forwards, to bottom of view, content y not 0")
1467             << 2.0
1468             << 5+4 << 13+4 << 5
1469                << 11 << 19 << 6
1470             << 0.0;
1471
1472     QTest::newRow("move multiple forwards, from visible -> non-visible")
1473             << 0.0
1474             << 1 << 16 << 3
1475                << 1 << 18 << 5
1476             << 0.0;
1477
1478     QTest::newRow("move multiple forwards, from visible -> non-visible (move first item)")
1479             << 0.0
1480             << 0 << 16 << 3
1481                << 1 << 18 << 5
1482             << 0.0;
1483
1484
1485     QTest::newRow("move multiple backwards, within visible items")
1486             << 0.0
1487             << 4 << 1 << 3
1488                << 7 << 1 << 5
1489             << 0.0;
1490
1491     QTest::newRow("move multiple backwards, from non-visible -> visible")
1492             << 0.0
1493             << 20 << 4 << 3
1494                << 20 << 4 << 5
1495             << 0.0;
1496
1497     QTest::newRow("move multiple backwards, from non-visible -> visible (move last item)")
1498             << 0.0
1499             << 27 << 10 << 3
1500                << 25 << 8 << 5
1501             << 0.0;
1502
1503     QTest::newRow("move multiple backwards, from visible -> non-visible")
1504             << 2.0     // show 6-23
1505             << 16 << 1 << 3
1506                << 17 << 1 << 5
1507             << -1.0;   // to minimize movement, items are added above visible area, all items move up by 1 row
1508
1509     QTest::newRow("move multiple backwards, from visible -> non-visible (move first item)")
1510             << 2.0     // show 6-23
1511             << 16 << 0 << 3
1512                << 17 << 0 << 5
1513             << -1.0;   // 16,17,18 move to above item 0, all items move up by 1 row
1514 }
1515
1516 void tst_QQuickGridView::multipleChanges(bool condensed)
1517 {
1518     QFETCH(int, startCount);
1519     QFETCH(QList<ListChange>, changes);
1520     QFETCH(int, newCount);
1521     QFETCH(int, newCurrentIndex);
1522
1523     QQuickView *canvas = getView();
1524
1525     QaimModel model;
1526     for (int i = 0; i < startCount; i++)
1527         model.addItem("Item" + QString::number(i), "");
1528
1529     canvas->rootContext()->setContextProperty("testModel", &model);
1530     canvas->setSource(testFileUrl("gridview1.qml"));
1531     canvas->show();
1532     qApp->processEvents();
1533
1534     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1535     QTRY_VERIFY(gridview != 0);
1536     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1537
1538     for (int i=0; i<changes.count(); i++) {
1539         switch (changes[i].type) {
1540             case ListChange::Inserted:
1541             {
1542                 QList<QPair<QString, QString> > items;
1543                 for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
1544                     items << qMakePair(QString("new item " + j), QString::number(j));
1545                 model.insertItems(changes[i].index, items);
1546                 break;
1547             }
1548             case ListChange::Removed:
1549                 model.removeItems(changes[i].index, changes[i].count);
1550                 break;
1551             case ListChange::Moved:
1552                 model.moveItems(changes[i].index, changes[i].to, changes[i].count);
1553                 break;
1554             case ListChange::SetCurrent:
1555                 gridview->setCurrentIndex(changes[i].index);
1556                 break;
1557             case ListChange::SetContentY:
1558                 gridview->setContentY(changes[i].pos);
1559                 break;
1560         }
1561         if (condensed) {
1562             QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1563         }
1564     }
1565     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1566
1567     QCOMPARE(gridview->count(), newCount);
1568     QCOMPARE(gridview->count(), model.count());
1569     QCOMPARE(gridview->currentIndex(), newCurrentIndex);
1570
1571     QQuickText *name;
1572     QQuickText *number;
1573     QQuickItem *contentItem = gridview->contentItem();
1574     QTRY_VERIFY(contentItem != 0);
1575     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1576     for (int i=0; i < model.count() && i < itemCount; ++i) {
1577         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1578         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1579         name = findItem<QQuickText>(contentItem, "textName", i);
1580         QVERIFY(name != 0);
1581         QTRY_COMPARE(name->text(), model.name(i));
1582         number = findItem<QQuickText>(contentItem, "textNumber", i);
1583         QVERIFY(number != 0);
1584         QTRY_COMPARE(number->text(), model.number(i));
1585     }
1586
1587     releaseView(canvas);
1588 }
1589
1590 void tst_QQuickGridView::multipleChanges_data()
1591 {
1592     QTest::addColumn<int>("startCount");
1593     QTest::addColumn<QList<ListChange> >("changes");
1594     QTest::addColumn<int>("newCount");
1595     QTest::addColumn<int>("newCurrentIndex");
1596
1597     QList<ListChange> changes;
1598
1599     for (int i=1; i<30; i++)
1600         changes << ListChange::remove(0);
1601     QTest::newRow("remove all but 1, first->last") << 30 << changes << 1 << 0;
1602
1603     changes << ListChange::remove(0);
1604     QTest::newRow("remove all") << 30 << changes << 0 << -1;
1605
1606     changes.clear();
1607     changes << ListChange::setCurrent(29);
1608     for (int i=29; i>0; i--)
1609         changes << ListChange::remove(i);
1610     QTest::newRow("remove last (current) -> first") << 30 << changes << 1 << 0;
1611
1612     QTest::newRow("remove then insert at 0") << 10 << (QList<ListChange>()
1613             << ListChange::remove(0, 1)
1614             << ListChange::insert(0, 1)
1615             ) << 10 << 1;
1616
1617     QTest::newRow("remove then insert at non-zero index") << 10 << (QList<ListChange>()
1618             << ListChange::setCurrent(2)
1619             << ListChange::remove(2, 1)
1620             << ListChange::insert(2, 1)
1621             ) << 10 << 3;
1622
1623     QTest::newRow("remove current then insert below it") << 10 << (QList<ListChange>()
1624             << ListChange::setCurrent(1)
1625             << ListChange::remove(1, 3)
1626             << ListChange::insert(2, 2)
1627             ) << 9 << 1;
1628
1629     QTest::newRow("remove current index then move it down") << 10 << (QList<ListChange>()
1630             << ListChange::setCurrent(2)
1631             << ListChange::remove(1, 3)
1632             << ListChange::move(1, 5, 1)
1633             ) << 7 << 5;
1634
1635     QTest::newRow("remove current index then move it up") << 10 << (QList<ListChange>()
1636             << ListChange::setCurrent(5)
1637             << ListChange::remove(4, 3)
1638             << ListChange::move(4, 1, 1)
1639             ) << 7 << 1;
1640
1641
1642     QTest::newRow("insert multiple times") << 0 << (QList<ListChange>()
1643             << ListChange::insert(0, 2)
1644             << ListChange::insert(0, 4)
1645             << ListChange::insert(0, 6)
1646             ) << 12 << 10;
1647
1648     QTest::newRow("insert multiple times with current index changes") << 0 << (QList<ListChange>()
1649             << ListChange::insert(0, 2)
1650             << ListChange::insert(0, 4)
1651             << ListChange::insert(0, 6)
1652             << ListChange::setCurrent(3)
1653             << ListChange::insert(3, 2)
1654             ) << 14 << 5;
1655
1656     QTest::newRow("insert and remove all") << 0 << (QList<ListChange>()
1657             << ListChange::insert(0, 30)
1658             << ListChange::remove(0, 30)
1659             ) << 0 << -1;
1660
1661     QTest::newRow("insert and remove current") << 30 << (QList<ListChange>()
1662             << ListChange::insert(1)
1663             << ListChange::setCurrent(1)
1664             << ListChange::remove(1)
1665             ) << 30 << 1;
1666
1667     QTest::newRow("insert before 0, then remove cross section of new and old items") << 10 << (QList<ListChange>()
1668             << ListChange::insert(0, 10)
1669             << ListChange::remove(5, 10)
1670             ) << 10 << 5;
1671
1672     QTest::newRow("insert multiple, then move new items to end") << 10 << (QList<ListChange>()
1673             << ListChange::insert(0, 3)
1674             << ListChange::move(0, 10, 3)
1675             ) << 13 << 0;
1676
1677     QTest::newRow("insert multiple, then move new and some old items to end") << 10 << (QList<ListChange>()
1678             << ListChange::insert(0, 3)
1679             << ListChange::move(0, 8, 5)
1680             ) << 13 << 11;
1681
1682     QTest::newRow("insert multiple at end, then move new and some old items to start") << 10 << (QList<ListChange>()
1683             << ListChange::setCurrent(9)
1684             << ListChange::insert(10, 3)
1685             << ListChange::move(8, 0, 5)
1686             ) << 13 << 1;
1687
1688
1689     QTest::newRow("move back and forth to same index") << 10 << (QList<ListChange>()
1690             << ListChange::setCurrent(1)
1691             << ListChange::move(1, 2, 2)
1692             << ListChange::move(2, 1, 2)
1693             ) << 10 << 1;
1694
1695     QTest::newRow("move forwards then back") << 10 << (QList<ListChange>()
1696             << ListChange::setCurrent(2)
1697             << ListChange::move(1, 2, 3)
1698             << ListChange::move(3, 0, 5)
1699             ) << 10 << 0;
1700
1701     QTest::newRow("move current, then remove it") << 10 << (QList<ListChange>()
1702             << ListChange::setCurrent(5)
1703             << ListChange::move(5, 0, 1)
1704             << ListChange::remove(0)
1705             ) << 9 << 0;
1706
1707     QTest::newRow("move current, then insert before it") << 10 << (QList<ListChange>()
1708             << ListChange::setCurrent(5)
1709             << ListChange::move(5, 0, 1)
1710             << ListChange::insert(0)
1711             ) << 11 << 1;
1712
1713     QTest::newRow("move multiple, then remove them") << 10 << (QList<ListChange>()
1714             << ListChange::setCurrent(1)
1715             << ListChange::move(5, 1, 3)
1716             << ListChange::remove(1, 3)
1717             ) << 7 << 1;
1718
1719     QTest::newRow("move multiple, then insert before them") << 10 << (QList<ListChange>()
1720             << ListChange::setCurrent(5)
1721             << ListChange::move(5, 1, 3)
1722             << ListChange::insert(1, 5)
1723             ) << 15 << 6;
1724
1725     QTest::newRow("move multiple, then insert after them") << 10 << (QList<ListChange>()
1726             << ListChange::setCurrent(3)
1727             << ListChange::move(0, 1, 2)
1728             << ListChange::insert(3, 5)
1729             ) << 15 << 8;
1730
1731
1732     QTest::newRow("clear current") << 0 << (QList<ListChange>()
1733             << ListChange::insert(0, 5)
1734             << ListChange::setCurrent(-1)
1735             << ListChange::remove(0, 5)
1736             << ListChange::insert(0, 5)
1737             ) << 5 << -1;
1738
1739     QTest::newRow("remove, scroll") << 30 << (QList<ListChange>()
1740             << ListChange::remove(20, 5)
1741             << ListChange::setContentY(20)
1742             ) << 25 << 0;
1743
1744     QTest::newRow("insert, scroll") << 10 << (QList<ListChange>()
1745             << ListChange::insert(9, 5)
1746             << ListChange::setContentY(20)
1747             ) << 15 << 0;
1748
1749     QTest::newRow("move, scroll") << 20 << (QList<ListChange>()
1750             << ListChange::move(15, 8, 3)
1751             << ListChange::setContentY(0)
1752             ) << 20 << 0;
1753
1754     QTest::newRow("clear, insert, scroll") << 30 << (QList<ListChange>()
1755             << ListChange::setContentY(20)
1756             << ListChange::remove(0, 30)
1757             << ListChange::insert(0, 2)
1758             << ListChange::setContentY(0)
1759             ) << 2 << 0;
1760 }
1761
1762
1763 void tst_QQuickGridView::swapWithFirstItem()
1764 {
1765     // QTBUG_9697
1766     QQuickView *canvas = createView();
1767
1768     QaimModel model;
1769     for (int i = 0; i < 30; i++)
1770         model.addItem("Item" + QString::number(i), "");
1771
1772     canvas->rootContext()->setContextProperty("testModel", &model);
1773     canvas->setSource(testFileUrl("gridview1.qml"));
1774     canvas->show();
1775     qApp->processEvents();
1776
1777     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1778     QTRY_VERIFY(gridview != 0);
1779
1780     // ensure content position is stable
1781     gridview->setContentY(0);
1782     model.moveItem(10, 0);
1783     QTRY_VERIFY(gridview->contentY() == 0);
1784
1785     delete canvas;
1786 }
1787
1788 void tst_QQuickGridView::currentIndex()
1789 {
1790     QaimModel model;
1791     for (int i = 0; i < 60; i++)
1792         model.addItem("Item" + QString::number(i), QString::number(i));
1793
1794     QQuickView *canvas = new QQuickView(0);
1795     canvas->setGeometry(0,0,240,320);
1796     canvas->show();
1797
1798     QQmlContext *ctxt = canvas->rootContext();
1799     ctxt->setContextProperty("testModel", &model);
1800
1801     QString filename(testFile("gridview-initCurrent.qml"));
1802     canvas->setSource(QUrl::fromLocalFile(filename));
1803
1804     qApp->processEvents();
1805
1806     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1807     QVERIFY(gridview != 0);
1808     QTRY_VERIFY(!QQuickItemPrivate::get(gridview)->polishScheduled);
1809
1810     QQuickItem *contentItem = gridview->contentItem();
1811     QVERIFY(contentItem != 0);
1812
1813     // current item should be third item
1814     QCOMPARE(gridview->currentIndex(), 35);
1815     QCOMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 35));
1816     QCOMPARE(gridview->currentItem()->y(), gridview->highlightItem()->y());
1817     QCOMPARE(gridview->contentY(), 400.0);
1818
1819     gridview->setCurrentIndex(0);
1820     QCOMPARE(gridview->currentIndex(), 0);
1821     // confirm that the velocity is updated
1822     QTRY_VERIFY(gridview->verticalVelocity() != 0.0);
1823
1824     // footer should become visible if it is out of view, and then current index moves to the first row
1825     canvas->rootObject()->setProperty("showFooter", true);
1826     QTRY_VERIFY(gridview->footerItem());
1827     gridview->setCurrentIndex(model.count()-3);
1828     QTRY_VERIFY(gridview->footerItem()->y() > gridview->contentY() + gridview->height());
1829     gridview->setCurrentIndex(model.count()-2);
1830     QTRY_COMPARE(gridview->contentY() + gridview->height(), (60.0 * model.count()/3) + gridview->footerItem()->height());
1831     canvas->rootObject()->setProperty("showFooter", false);
1832
1833     // header should become visible if it is out of view, and then current index moves to the last row
1834     canvas->rootObject()->setProperty("showHeader", true);
1835     QTRY_VERIFY(gridview->headerItem());
1836     gridview->setCurrentIndex(3);
1837     QTRY_VERIFY(gridview->headerItem()->y() + gridview->headerItem()->height() < gridview->contentY());
1838     gridview->setCurrentIndex(1);
1839     QTRY_COMPARE(gridview->contentY(), -gridview->headerItem()->height());
1840     canvas->rootObject()->setProperty("showHeader", false);
1841
1842     // turn off auto highlight
1843     gridview->setHighlightFollowsCurrentItem(false);
1844     QVERIFY(gridview->highlightFollowsCurrentItem() == false);
1845     QVERIFY(gridview->highlightItem());
1846     qreal hlPosX = gridview->highlightItem()->x();
1847     qreal hlPosY = gridview->highlightItem()->y();
1848
1849     gridview->setCurrentIndex(5);
1850     QTRY_COMPARE(gridview->highlightItem()->x(), hlPosX);
1851     QTRY_COMPARE(gridview->highlightItem()->y(), hlPosY);
1852
1853     // insert item before currentIndex
1854     gridview->setCurrentIndex(28);
1855     model.insertItem(0, "Foo", "1111");
1856     QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29);
1857
1858     // check removing highlight by setting currentIndex to -1;
1859     gridview->setCurrentIndex(-1);
1860
1861     QCOMPARE(gridview->currentIndex(), -1);
1862     QVERIFY(!gridview->highlightItem());
1863     QVERIFY(!gridview->currentItem());
1864
1865     // moving currentItem out of view should make it invisible
1866     gridview->setCurrentIndex(0);
1867     QTRY_VERIFY(gridview->currentItem()->isVisible());
1868     gridview->setContentY(200);
1869     QTRY_VERIFY(!gridview->currentItem()->isVisible());
1870
1871     delete canvas;
1872 }
1873
1874 void tst_QQuickGridView::noCurrentIndex()
1875 {
1876     QaimModel model;
1877     for (int i = 0; i < 60; i++)
1878         model.addItem("Item" + QString::number(i), QString::number(i));
1879
1880     QQuickView *canvas = new QQuickView(0);
1881     canvas->setGeometry(0,0,240,320);
1882
1883     QQmlContext *ctxt = canvas->rootContext();
1884     ctxt->setContextProperty("testModel", &model);
1885
1886     QString filename(testFile("gridview-noCurrent.qml"));
1887     canvas->setSource(QUrl::fromLocalFile(filename));
1888     canvas->show();
1889     qApp->processEvents();
1890
1891     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1892     QVERIFY(gridview != 0);
1893     QQuickItem *contentItem = gridview->contentItem();
1894     QVERIFY(contentItem != 0);
1895     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1896
1897     // current index should be -1
1898     QCOMPARE(gridview->currentIndex(), -1);
1899     QVERIFY(!gridview->currentItem());
1900     QVERIFY(!gridview->highlightItem());
1901     QCOMPARE(gridview->contentY(), 0.0);
1902
1903     gridview->setCurrentIndex(5);
1904     QCOMPARE(gridview->currentIndex(), 5);
1905     QVERIFY(gridview->currentItem());
1906     QVERIFY(gridview->highlightItem());
1907
1908     delete canvas;
1909 }
1910
1911 void tst_QQuickGridView::keyNavigation()
1912 {
1913     QFETCH(QQuickGridView::Flow, flow);
1914     QFETCH(Qt::LayoutDirection, layoutDirection);
1915     QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
1916     QFETCH(Qt::Key, forwardsKey);
1917     QFETCH(Qt::Key, backwardsKey);
1918     QFETCH(QPointF, contentPosAtFirstItem);
1919     QFETCH(QPointF, contentPosAtLastItem);
1920     QFETCH(int, indexRightOf7);
1921     QFETCH(int, indexLeftOf7);
1922     QFETCH(int, indexUpFrom7);
1923     QFETCH(int, indexDownFrom7);
1924
1925     QmlListModel model;
1926     for (int i = 0; i < 18; i++)
1927         model.addItem("Item" + QString::number(i), "");
1928
1929     QQuickView *canvas = getView();
1930     canvas->rootContext()->setContextProperty("testModel", &model);
1931     canvas->setSource(testFileUrl("gridview1.qml"));
1932     canvas->show();
1933     qApp->processEvents();
1934
1935     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
1936     QTRY_VERIFY(gridview != 0);
1937     gridview->setFlow(flow);
1938     gridview->setLayoutDirection(layoutDirection);
1939     gridview->setVerticalLayoutDirection(verticalLayoutDirection);
1940     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
1941
1942     canvas->requestActivateWindow();
1943     QTRY_VERIFY(qGuiApp->focusWindow() == canvas);
1944     QCOMPARE(gridview->currentIndex(), 0);
1945
1946     QTest::keyClick(canvas, forwardsKey);
1947     QCOMPARE(gridview->currentIndex(), 1);
1948
1949     QTest::keyClick(canvas, backwardsKey);
1950     QCOMPARE(gridview->currentIndex(), 0);
1951
1952     gridview->setCurrentIndex(7);
1953     gridview->moveCurrentIndexRight();
1954     QCOMPARE(gridview->currentIndex(), indexRightOf7);
1955     gridview->moveCurrentIndexLeft();
1956     QCOMPARE(gridview->currentIndex(), 7);
1957     gridview->moveCurrentIndexLeft();
1958     QCOMPARE(gridview->currentIndex(), indexLeftOf7);
1959     gridview->moveCurrentIndexRight();
1960     QCOMPARE(gridview->currentIndex(), 7);
1961     gridview->moveCurrentIndexUp();
1962     QCOMPARE(gridview->currentIndex(), indexUpFrom7);
1963     gridview->moveCurrentIndexDown();
1964     QCOMPARE(gridview->currentIndex(), 7);
1965     gridview->moveCurrentIndexDown();
1966     QCOMPARE(gridview->currentIndex(), indexDownFrom7);
1967
1968     gridview->setCurrentIndex(7);
1969     QTest::keyClick(canvas, Qt::Key_Right);
1970     QCOMPARE(gridview->currentIndex(), indexRightOf7);
1971     QTest::keyClick(canvas, Qt::Key_Left);
1972     QCOMPARE(gridview->currentIndex(), 7);
1973     QTest::keyClick(canvas, Qt::Key_Left);
1974     QCOMPARE(gridview->currentIndex(), indexLeftOf7);
1975     QTest::keyClick(canvas, Qt::Key_Right);
1976     QCOMPARE(gridview->currentIndex(), 7);
1977     QTest::keyClick(canvas, Qt::Key_Up);
1978     QCOMPARE(gridview->currentIndex(), indexUpFrom7);
1979     QTest::keyClick(canvas, Qt::Key_Down);
1980     QCOMPARE(gridview->currentIndex(), 7);
1981     QTest::keyClick(canvas, Qt::Key_Down);
1982     QCOMPARE(gridview->currentIndex(), indexDownFrom7);
1983
1984     // hold down a key to go forwards
1985     gridview->setCurrentIndex(0);
1986     for (int i=0; i<model.count()-1; i++) {
1987 //        QTest::qWait(500);
1988         QTest::simulateEvent(canvas, true, forwardsKey, Qt::NoModifier, "", true);
1989         QTRY_COMPARE(gridview->currentIndex(), i+1);
1990     }
1991     QTest::keyRelease(canvas, forwardsKey);
1992     QTRY_COMPARE(gridview->currentIndex(), model.count()-1);
1993     QTRY_COMPARE(gridview->contentX(), contentPosAtLastItem.x());
1994     QTRY_COMPARE(gridview->contentY(), contentPosAtLastItem.y());
1995
1996     // hold down a key to go backwards
1997     for (int i=model.count()-1; i > 0; i--) {
1998         QTest::simulateEvent(canvas, true, backwardsKey, Qt::NoModifier, "", true);
1999         QTRY_COMPARE(gridview->currentIndex(), i-1);
2000     }
2001     QTest::keyRelease(canvas, backwardsKey);
2002     QTRY_COMPARE(gridview->currentIndex(), 0);
2003     QTRY_COMPARE(gridview->contentX(), contentPosAtFirstItem.x());
2004     QTRY_COMPARE(gridview->contentY(), contentPosAtFirstItem.y());
2005
2006     // no wrap
2007     QVERIFY(!gridview->isWrapEnabled());
2008     QTest::keyClick(canvas, forwardsKey);
2009     QCOMPARE(gridview->currentIndex(), 1);
2010     QTest::keyClick(canvas, backwardsKey);
2011     QCOMPARE(gridview->currentIndex(), 0);
2012
2013     QTest::keyClick(canvas, backwardsKey);
2014     QCOMPARE(gridview->currentIndex(), 0);
2015
2016     // with wrap
2017     gridview->setWrapEnabled(true);
2018     QVERIFY(gridview->isWrapEnabled());
2019
2020     QTest::keyClick(canvas, backwardsKey);
2021     QCOMPARE(gridview->currentIndex(), model.count()-1);
2022     QTRY_COMPARE(gridview->contentX(), contentPosAtLastItem.x());
2023     QTRY_COMPARE(gridview->contentY(), contentPosAtLastItem.y());
2024
2025     QTest::keyClick(canvas, forwardsKey);
2026     QCOMPARE(gridview->currentIndex(), 0);
2027     QTRY_COMPARE(gridview->contentX(), contentPosAtFirstItem.x());
2028     QTRY_COMPARE(gridview->contentY(), contentPosAtFirstItem.y());
2029
2030     // Test key press still accepted when position wraps to same index.
2031     canvas->rootObject()->setProperty("lastKey", 0);
2032     model.removeItems(1, model.count() - 1);
2033
2034     QTest::keyClick(canvas, backwardsKey);
2035     QCOMPARE(canvas->rootObject()->property("lastKey").toInt(), 0);
2036
2037     QTest::keyClick(canvas, forwardsKey);
2038     QCOMPARE(canvas->rootObject()->property("lastKey").toInt(), 0);
2039
2040     // Test key press not accepted at limits when wrap is disabled.
2041     gridview->setWrapEnabled(false);
2042
2043     QTest::keyClick(canvas, backwardsKey);
2044     QCOMPARE(canvas->rootObject()->property("lastKey").toInt(), int(backwardsKey));
2045
2046     QTest::keyClick(canvas, forwardsKey);
2047     QCOMPARE(canvas->rootObject()->property("lastKey").toInt(), int(forwardsKey));
2048
2049     releaseView(canvas);
2050 }
2051
2052 void tst_QQuickGridView::keyNavigation_data()
2053 {
2054     QTest::addColumn<QQuickGridView::Flow>("flow");
2055     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
2056     QTest::addColumn<QQuickItemView::VerticalLayoutDirection>("verticalLayoutDirection");
2057     QTest::addColumn<Qt::Key>("forwardsKey");
2058     QTest::addColumn<Qt::Key>("backwardsKey");
2059     QTest::addColumn<QPointF>("contentPosAtFirstItem");
2060     QTest::addColumn<QPointF>("contentPosAtLastItem");
2061     QTest::addColumn<int>("indexRightOf7");
2062     QTest::addColumn<int>("indexLeftOf7");
2063     QTest::addColumn<int>("indexUpFrom7");
2064     QTest::addColumn<int>("indexDownFrom7");
2065
2066     QTest::newRow("LeftToRight, LtR, TtB")
2067             << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::TopToBottom
2068             << Qt::Key_Right << Qt::Key_Left
2069             << QPointF(0, 0)
2070             << QPointF(0, 40)
2071             << 8 << 6 << 4 << 10;
2072
2073     QTest::newRow("LeftToRight, RtL, TtB")
2074             << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::TopToBottom
2075             << Qt::Key_Left << Qt::Key_Right
2076             << QPointF(0, 0)
2077             << QPointF(0, 40)
2078             << 6 << 8 << 4 << 10;
2079
2080     QTest::newRow("LeftToRight, LtR, BtT")
2081             << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::BottomToTop
2082             << Qt::Key_Right << Qt::Key_Left
2083             << QPointF(0, -320)
2084             << QPointF(0, -360)
2085             << 8 << 6 << 10 << 4;
2086
2087     QTest::newRow("LeftToRight, RtL, BtT")
2088             << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::BottomToTop
2089             << Qt::Key_Left << Qt::Key_Right
2090             << QPointF(0, -320)
2091             << QPointF(0, -360)
2092             << 6 << 8 << 10 << 4;
2093
2094     QTest::newRow("TopToBottom, LtR, TtB")
2095             << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::TopToBottom
2096             << Qt::Key_Down << Qt::Key_Up
2097             << QPointF(0, 0)
2098             << QPointF(80, 0)
2099             << 12 << 2 << 6 << 8;
2100
2101     QTest::newRow("TopToBottom, RtL, TtB")
2102             << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::TopToBottom
2103             << Qt::Key_Down << Qt::Key_Up
2104             << QPointF(-240, 0)
2105             << QPointF(-320, 0)
2106             << 2 << 12 << 6 << 8;
2107
2108     QTest::newRow("TopToBottom, LtR, BtT")
2109             << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::BottomToTop
2110             << Qt::Key_Up << Qt::Key_Down
2111             << QPointF(0, -320)
2112             << QPointF(80, -320)
2113             << 12 << 2 << 8 << 6;
2114
2115     QTest::newRow("TopToBottom, RtL, BtT")
2116             << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::BottomToTop
2117             << Qt::Key_Up << Qt::Key_Down
2118             << QPointF(-240, -320)
2119             << QPointF(-320, -320)
2120             << 2 << 12 << 8 << 6;
2121 }
2122
2123 void tst_QQuickGridView::changeFlow()
2124 {
2125     QQuickView *canvas = createView();
2126
2127     QaimModel model;
2128     for (int i = 0; i < 30; i++)
2129         model.addItem("Item" + QString::number(i), QString::number(i));
2130
2131     QQmlContext *ctxt = canvas->rootContext();
2132     ctxt->setContextProperty("testModel", &model);
2133     ctxt->setContextProperty("testRightToLeft", QVariant(false));
2134     ctxt->setContextProperty("testTopToBottom", QVariant(false));
2135     ctxt->setContextProperty("testBottomToTop", QVariant(false));
2136
2137     canvas->setSource(testFileUrl("layouts.qml"));
2138     qApp->processEvents();
2139
2140     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2141     QTRY_VERIFY(gridview != 0);
2142
2143     QQuickItem *contentItem = gridview->contentItem();
2144     QTRY_VERIFY(contentItem != 0);
2145
2146     // Confirm items positioned correctly and indexes correct
2147     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2148     for (int i = 0; i < model.count() && i < itemCount; ++i) {
2149         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2150         if (!item) qWarning() << "Item" << i << "not found";
2151         QTRY_VERIFY(item);
2152         QTRY_COMPARE(item->x(), qreal((i%3)*80));
2153         QTRY_COMPARE(item->y(), qreal((i/3)*60));
2154         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
2155         QTRY_VERIFY(name != 0);
2156         QTRY_COMPARE(name->text(), model.name(i));
2157         QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
2158         QTRY_VERIFY(number != 0);
2159         QTRY_COMPARE(number->text(), model.number(i));
2160     }
2161
2162     ctxt->setContextProperty("testTopToBottom", QVariant(true));
2163
2164     // Confirm items positioned correctly and indexes correct
2165     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2166     for (int i = 0; i < model.count() && i < itemCount; ++i) {
2167         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2168         if (!item) qWarning() << "Item" << i << "not found";
2169         QTRY_VERIFY(item);
2170         QTRY_COMPARE(item->x(), qreal((i/5)*80));
2171         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2172         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
2173         QTRY_VERIFY(name != 0);
2174         QTRY_COMPARE(name->text(), model.name(i));
2175         QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
2176         QTRY_VERIFY(number != 0);
2177         QTRY_COMPARE(number->text(), model.number(i));
2178     }
2179
2180     ctxt->setContextProperty("testRightToLeft", QVariant(true));
2181
2182     // Confirm items positioned correctly and indexes correct
2183     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2184     for (int i = 0; i < model.count() && i < itemCount; ++i) {
2185         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2186         if (!item) qWarning() << "Item" << i << "not found";
2187         QTRY_VERIFY(item);
2188         QTRY_COMPARE(item->x(), qreal(-(i/5)*80 - item->width()));
2189         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2190         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
2191         QTRY_VERIFY(name != 0);
2192         QTRY_COMPARE(name->text(), model.name(i));
2193         QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
2194         QTRY_VERIFY(number != 0);
2195         QTRY_COMPARE(number->text(), model.number(i));
2196     }
2197     gridview->setContentX(100);
2198     QTRY_COMPARE(gridview->contentX(), 100.);
2199     ctxt->setContextProperty("testTopToBottom", QVariant(false));
2200     QTRY_COMPARE(gridview->contentX(), 0.);
2201
2202     // Confirm items positioned correctly and indexes correct
2203     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2204     for (int i = 0; i < model.count() && i < itemCount; ++i) {
2205         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2206         if (!item) qWarning() << "Item" << i << "not found";
2207         QTRY_VERIFY(item);
2208         QTRY_COMPARE(item->x(), qreal(240 - (i%3+1)*80));
2209         QTRY_COMPARE(item->y(), qreal((i/3)*60));
2210         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
2211         QTRY_VERIFY(name != 0);
2212         QTRY_COMPARE(name->text(), model.name(i));
2213         QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
2214         QTRY_VERIFY(number != 0);
2215         QTRY_COMPARE(number->text(), model.number(i));
2216     }
2217
2218     delete canvas;
2219 }
2220
2221 void tst_QQuickGridView::defaultValues()
2222 {
2223     QQmlEngine engine;
2224     QQmlComponent c(&engine, testFileUrl("gridview3.qml"));
2225     QQuickGridView *obj = qobject_cast<QQuickGridView*>(c.create());
2226
2227     QTRY_VERIFY(obj != 0);
2228     QTRY_VERIFY(obj->model() == QVariant());
2229     QTRY_VERIFY(obj->delegate() == 0);
2230     QTRY_COMPARE(obj->currentIndex(), -1);
2231     QTRY_VERIFY(obj->currentItem() == 0);
2232     QTRY_COMPARE(obj->count(), 0);
2233     QTRY_VERIFY(obj->highlight() == 0);
2234     QTRY_VERIFY(obj->highlightItem() == 0);
2235     QTRY_COMPARE(obj->highlightFollowsCurrentItem(), true);
2236     QTRY_VERIFY(obj->flow() == 0);
2237     QTRY_COMPARE(obj->isWrapEnabled(), false);
2238     QTRY_COMPARE(obj->cacheBuffer(), 0);
2239     QTRY_COMPARE(obj->cellWidth(), qreal(100)); //### Should 100 be the default?
2240     QTRY_COMPARE(obj->cellHeight(), qreal(100));
2241     delete obj;
2242 }
2243
2244 void tst_QQuickGridView::properties()
2245 {
2246     QQmlEngine engine;
2247     QQmlComponent c(&engine, testFileUrl("gridview2.qml"));
2248     QQuickGridView *obj = qobject_cast<QQuickGridView*>(c.create());
2249
2250     QTRY_VERIFY(obj != 0);
2251     QTRY_VERIFY(obj->model() != QVariant());
2252     QTRY_VERIFY(obj->delegate() != 0);
2253     QTRY_COMPARE(obj->currentIndex(), 0);
2254     QTRY_VERIFY(obj->currentItem() != 0);
2255     QTRY_COMPARE(obj->count(), 4);
2256     QTRY_VERIFY(obj->highlight() != 0);
2257     QTRY_VERIFY(obj->highlightItem() != 0);
2258     QTRY_COMPARE(obj->highlightFollowsCurrentItem(), false);
2259     QTRY_VERIFY(obj->flow() == 0);
2260     QTRY_COMPARE(obj->isWrapEnabled(), true);
2261     QTRY_COMPARE(obj->cacheBuffer(), 200);
2262     QTRY_COMPARE(obj->cellWidth(), qreal(100));
2263     QTRY_COMPARE(obj->cellHeight(), qreal(100));
2264     delete obj;
2265 }
2266
2267 void tst_QQuickGridView::propertyChanges()
2268 {
2269     QQuickView *canvas = createView();
2270     QTRY_VERIFY(canvas);
2271     canvas->setSource(testFileUrl("propertychangestest.qml"));
2272
2273     QQuickGridView *gridView = canvas->rootObject()->findChild<QQuickGridView*>("gridView");
2274     QTRY_VERIFY(gridView);
2275
2276     QSignalSpy keyNavigationWrapsSpy(gridView, SIGNAL(keyNavigationWrapsChanged()));
2277     QSignalSpy cacheBufferSpy(gridView, SIGNAL(cacheBufferChanged()));
2278     QSignalSpy layoutSpy(gridView, SIGNAL(layoutDirectionChanged()));
2279     QSignalSpy flowSpy(gridView, SIGNAL(flowChanged()));
2280
2281     QTRY_COMPARE(gridView->isWrapEnabled(), true);
2282     QTRY_COMPARE(gridView->cacheBuffer(), 10);
2283     QTRY_COMPARE(gridView->flow(), QQuickGridView::FlowLeftToRight);
2284
2285     gridView->setWrapEnabled(false);
2286     gridView->setCacheBuffer(3);
2287     gridView->setFlow(QQuickGridView::FlowTopToBottom);
2288
2289     QTRY_COMPARE(gridView->isWrapEnabled(), false);
2290     QTRY_COMPARE(gridView->cacheBuffer(), 3);
2291     QTRY_COMPARE(gridView->flow(), QQuickGridView::FlowTopToBottom);
2292
2293     QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
2294     QTRY_COMPARE(cacheBufferSpy.count(),1);
2295     QTRY_COMPARE(flowSpy.count(),1);
2296
2297     gridView->setWrapEnabled(false);
2298     gridView->setCacheBuffer(3);
2299     gridView->setFlow(QQuickGridView::FlowTopToBottom);
2300
2301     QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
2302     QTRY_COMPARE(cacheBufferSpy.count(),1);
2303     QTRY_COMPARE(flowSpy.count(),1);
2304
2305     gridView->setFlow(QQuickGridView::FlowLeftToRight);
2306     QTRY_COMPARE(gridView->flow(), QQuickGridView::FlowLeftToRight);
2307
2308     gridView->setWrapEnabled(true);
2309     gridView->setCacheBuffer(5);
2310     gridView->setLayoutDirection(Qt::RightToLeft);
2311
2312     QTRY_COMPARE(gridView->isWrapEnabled(), true);
2313     QTRY_COMPARE(gridView->cacheBuffer(), 5);
2314     QTRY_COMPARE(gridView->layoutDirection(), Qt::RightToLeft);
2315
2316     QTRY_COMPARE(keyNavigationWrapsSpy.count(),2);
2317     QTRY_COMPARE(cacheBufferSpy.count(),2);
2318     QTRY_COMPARE(layoutSpy.count(),1);
2319     QTRY_COMPARE(flowSpy.count(),2);
2320
2321     gridView->setWrapEnabled(true);
2322     gridView->setCacheBuffer(5);
2323     gridView->setLayoutDirection(Qt::RightToLeft);
2324
2325     QTRY_COMPARE(keyNavigationWrapsSpy.count(),2);
2326     QTRY_COMPARE(cacheBufferSpy.count(),2);
2327     QTRY_COMPARE(layoutSpy.count(),1);
2328     QTRY_COMPARE(flowSpy.count(),2);
2329
2330     gridView->setFlow(QQuickGridView::FlowTopToBottom);
2331     QTRY_COMPARE(gridView->flow(), QQuickGridView::FlowTopToBottom);
2332     QTRY_COMPARE(flowSpy.count(),3);
2333
2334     gridView->setFlow(QQuickGridView::FlowTopToBottom);
2335     QTRY_COMPARE(flowSpy.count(),3);
2336
2337     delete canvas;
2338 }
2339
2340 void tst_QQuickGridView::componentChanges()
2341 {
2342     QQuickView *canvas = createView();
2343     QTRY_VERIFY(canvas);
2344     canvas->setSource(testFileUrl("propertychangestest.qml"));
2345
2346     QQuickGridView *gridView = canvas->rootObject()->findChild<QQuickGridView*>("gridView");
2347     QTRY_VERIFY(gridView);
2348
2349     QQmlComponent component(canvas->engine());
2350     component.setData("import QtQuick 2.0; Rectangle { color: \"blue\"; }", QUrl::fromLocalFile(""));
2351
2352     QQmlComponent delegateComponent(canvas->engine());
2353     delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
2354
2355     QSignalSpy highlightSpy(gridView, SIGNAL(highlightChanged()));
2356     QSignalSpy delegateSpy(gridView, SIGNAL(delegateChanged()));
2357     QSignalSpy headerSpy(gridView, SIGNAL(headerChanged()));
2358     QSignalSpy footerSpy(gridView, SIGNAL(footerChanged()));
2359     QSignalSpy headerItemSpy(gridView, SIGNAL(headerItemChanged()));
2360     QSignalSpy footerItemSpy(gridView, SIGNAL(footerItemChanged()));
2361
2362     gridView->setHighlight(&component);
2363     gridView->setDelegate(&delegateComponent);
2364     gridView->setHeader(&component);
2365     gridView->setFooter(&component);
2366
2367     QTRY_COMPARE(gridView->highlight(), &component);
2368     QTRY_COMPARE(gridView->delegate(), &delegateComponent);
2369     QTRY_COMPARE(gridView->header(), &component);
2370     QTRY_COMPARE(gridView->footer(), &component);
2371
2372     QVERIFY(gridView->headerItem());
2373     QVERIFY(gridView->footerItem());
2374
2375     QTRY_COMPARE(highlightSpy.count(),1);
2376     QTRY_COMPARE(delegateSpy.count(),1);
2377     QTRY_COMPARE(headerSpy.count(),1);
2378     QTRY_COMPARE(footerSpy.count(),1);
2379     QTRY_COMPARE(headerItemSpy.count(),1);
2380     QTRY_COMPARE(footerItemSpy.count(),1);
2381
2382     gridView->setHighlight(&component);
2383     gridView->setDelegate(&delegateComponent);
2384     gridView->setHeader(&component);
2385     gridView->setFooter(&component);
2386
2387     QTRY_COMPARE(highlightSpy.count(),1);
2388     QTRY_COMPARE(delegateSpy.count(),1);
2389     QTRY_COMPARE(headerSpy.count(),1);
2390     QTRY_COMPARE(footerSpy.count(),1);
2391     QTRY_COMPARE(headerItemSpy.count(),1);
2392     QTRY_COMPARE(footerItemSpy.count(),1);
2393
2394     delete canvas;
2395 }
2396
2397 void tst_QQuickGridView::modelChanges()
2398 {
2399     QQuickView *canvas = createView();
2400     QTRY_VERIFY(canvas);
2401     canvas->setSource(testFileUrl("propertychangestest.qml"));
2402
2403     QQuickGridView *gridView = canvas->rootObject()->findChild<QQuickGridView*>("gridView");
2404     QTRY_VERIFY(gridView);
2405
2406     QQuickListModel *alternateModel = canvas->rootObject()->findChild<QQuickListModel*>("alternateModel");
2407     QTRY_VERIFY(alternateModel);
2408     QVariant modelVariant = QVariant::fromValue<QObject *>(alternateModel);
2409     QSignalSpy modelSpy(gridView, SIGNAL(modelChanged()));
2410
2411     gridView->setModel(modelVariant);
2412     QTRY_COMPARE(gridView->model(), modelVariant);
2413     QTRY_COMPARE(modelSpy.count(),1);
2414
2415     gridView->setModel(modelVariant);
2416     QTRY_COMPARE(modelSpy.count(),1);
2417
2418     gridView->setModel(QVariant());
2419     QTRY_COMPARE(modelSpy.count(),2);
2420     delete canvas;
2421 }
2422
2423 void tst_QQuickGridView::positionViewAtIndex()
2424 {
2425     QQuickView *canvas = createView();
2426
2427     QaimModel model;
2428     for (int i = 0; i < 40; i++)
2429         model.addItem("Item" + QString::number(i), "");
2430
2431     QQmlContext *ctxt = canvas->rootContext();
2432     ctxt->setContextProperty("testModel", &model);
2433     ctxt->setContextProperty("testRightToLeft", QVariant(false));
2434     ctxt->setContextProperty("testTopToBottom", QVariant(false));
2435     ctxt->setContextProperty("testBottomToTop", QVariant(false));
2436
2437     canvas->setSource(testFileUrl("layouts.qml"));
2438     canvas->show();
2439     qApp->processEvents();
2440
2441     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2442     QTRY_VERIFY(gridview != 0);
2443     QQuickItem *contentItem = gridview->contentItem();
2444     QTRY_VERIFY(contentItem != 0);
2445     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
2446
2447     // Confirm items positioned correctly
2448     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2449     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2450         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2451         if (!item) qWarning() << "Item" << i << "not found";
2452         QTRY_VERIFY(item);
2453         QTRY_COMPARE(item->x(), (i%3)*80.);
2454         QTRY_COMPARE(item->y(), (i/3)*60.);
2455     }
2456
2457     // Position on a currently visible item
2458     gridview->positionViewAtIndex(4, QQuickGridView::Beginning);
2459     QTRY_COMPARE(gridview->indexAt(120, 90), 4);
2460     QTRY_COMPARE(gridview->contentY(), 60.);
2461
2462     // Confirm items positioned correctly
2463     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2464     for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
2465         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2466         if (!item) qWarning() << "Item" << i << "not found";
2467         QTRY_VERIFY(item);
2468         QTRY_COMPARE(item->x(), (i%3)*80.);
2469         QTRY_COMPARE(item->y(), (i/3)*60.);
2470     }
2471
2472     // Position on an item beyond the visible items
2473     gridview->positionViewAtIndex(21, QQuickGridView::Beginning);
2474     QTRY_COMPARE(gridview->indexAt(40, 450), 21);
2475     QTRY_COMPARE(gridview->contentY(), 420.);
2476
2477     // Confirm items positioned correctly
2478     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2479     for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
2480         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2481         if (!item) qWarning() << "Item" << i << "not found";
2482         QTRY_VERIFY(item);
2483         QTRY_COMPARE(item->x(), (i%3)*80.);
2484         QTRY_COMPARE(item->y(), (i/3)*60.);
2485     }
2486
2487     // Position on an item that would leave empty space if positioned at the top
2488     gridview->positionViewAtIndex(31, QQuickGridView::Beginning);
2489     QTRY_COMPARE(gridview->indexAt(120, 630), 31);
2490     QTRY_COMPARE(gridview->contentY(), 520.);
2491
2492     // Confirm items positioned correctly
2493     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2494     for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
2495         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2496         if (!item) qWarning() << "Item" << i << "not found";
2497         QTRY_VERIFY(item);
2498         QTRY_COMPARE(item->x(), (i%3)*80.);
2499         QTRY_COMPARE(item->y(), (i/3)*60.);
2500     }
2501
2502     // Position at the beginning again
2503     gridview->positionViewAtIndex(0, QQuickGridView::Beginning);
2504     QTRY_COMPARE(gridview->indexAt(0, 0), 0);
2505     QTRY_COMPARE(gridview->indexAt(40, 30), 0);
2506     QTRY_COMPARE(gridview->indexAt(80, 60), 4);
2507     QTRY_COMPARE(gridview->contentY(), 0.);
2508
2509     // Confirm items positioned correctly
2510     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2511     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2512         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2513         if (!item) qWarning() << "Item" << i << "not found";
2514         QTRY_VERIFY(item);
2515         QTRY_COMPARE(item->x(), (i%3)*80.);
2516         QTRY_COMPARE(item->y(), (i/3)*60.);
2517     }
2518
2519     // Position at End
2520     gridview->positionViewAtIndex(30, QQuickGridView::End);
2521     QTRY_COMPARE(gridview->contentY(), 340.);
2522
2523     // Position in Center
2524     gridview->positionViewAtIndex(15, QQuickGridView::Center);
2525     QTRY_COMPARE(gridview->contentY(), 170.);
2526
2527     // Ensure at least partially visible
2528     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2529     QTRY_COMPARE(gridview->contentY(), 170.);
2530
2531     gridview->setContentY(302);
2532     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2533     QTRY_COMPARE(gridview->contentY(), 302.);
2534
2535     gridview->setContentY(360);
2536     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2537     QTRY_COMPARE(gridview->contentY(), 300.);
2538
2539     gridview->setContentY(60);
2540     gridview->positionViewAtIndex(20, QQuickGridView::Visible);
2541     QTRY_COMPARE(gridview->contentY(), 60.);
2542
2543     gridview->setContentY(20);
2544     gridview->positionViewAtIndex(20, QQuickGridView::Visible);
2545     QTRY_COMPARE(gridview->contentY(), 100.);
2546
2547     // Ensure completely visible
2548     gridview->setContentY(120);
2549     gridview->positionViewAtIndex(20, QQuickGridView::Contain);
2550     QTRY_COMPARE(gridview->contentY(), 120.);
2551
2552     gridview->setContentY(302);
2553     gridview->positionViewAtIndex(15, QQuickGridView::Contain);
2554     QTRY_COMPARE(gridview->contentY(), 300.);
2555
2556     gridview->setContentY(60);
2557     gridview->positionViewAtIndex(20, QQuickGridView::Contain);
2558     QTRY_COMPARE(gridview->contentY(), 100.);
2559
2560     // Test for Top To Bottom layout
2561     ctxt->setContextProperty("testTopToBottom", QVariant(true));
2562
2563     // Confirm items positioned correctly
2564     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2565     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2566         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2567         if (!item) qWarning() << "Item" << i << "not found";
2568         QTRY_VERIFY(item);
2569         QTRY_COMPARE(item->x(), (i/5)*80.);
2570         QTRY_COMPARE(item->y(), (i%5)*60.);
2571     }
2572
2573     // Position at End
2574     gridview->positionViewAtIndex(30, QQuickGridView::End);
2575     QTRY_COMPARE(gridview->contentX(), 320.);
2576     QTRY_COMPARE(gridview->contentY(), 0.);
2577
2578     // Position in Center
2579     gridview->positionViewAtIndex(15, QQuickGridView::Center);
2580     QTRY_COMPARE(gridview->contentX(), 160.);
2581
2582     // Ensure at least partially visible
2583     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2584     QTRY_COMPARE(gridview->contentX(), 160.);
2585
2586     gridview->setContentX(170);
2587     gridview->positionViewAtIndex(25, QQuickGridView::Visible);
2588     QTRY_COMPARE(gridview->contentX(), 170.);
2589
2590     gridview->positionViewAtIndex(30, QQuickGridView::Visible);
2591     QTRY_COMPARE(gridview->contentX(), 320.);
2592
2593     gridview->setContentX(170);
2594     gridview->positionViewAtIndex(25, QQuickGridView::Contain);
2595     QTRY_COMPARE(gridview->contentX(), 240.);
2596
2597     // positionViewAtBeginning
2598     gridview->positionViewAtBeginning();
2599     QTRY_COMPARE(gridview->contentX(), 0.);
2600
2601     gridview->setContentX(80);
2602     canvas->rootObject()->setProperty("showHeader", true);
2603     gridview->positionViewAtBeginning();
2604     QTRY_COMPARE(gridview->contentX(), -30.);
2605
2606     // positionViewAtEnd
2607     gridview->positionViewAtEnd();
2608     QTRY_COMPARE(gridview->contentX(), 400.);   // 8*80 - 240   (8 columns)
2609
2610     gridview->setContentX(80);
2611     canvas->rootObject()->setProperty("showFooter", true);
2612     gridview->positionViewAtEnd();
2613     QTRY_COMPARE(gridview->contentX(), 430.);
2614
2615     // set current item to outside visible view, position at beginning
2616     // and ensure highlight moves to current item
2617     gridview->setCurrentIndex(6);
2618     gridview->positionViewAtBeginning();
2619     QTRY_COMPARE(gridview->contentX(), -30.);
2620     QVERIFY(gridview->highlightItem());
2621     QCOMPARE(gridview->highlightItem()->x(), 80.);
2622
2623     delete canvas;
2624 }
2625
2626 void tst_QQuickGridView::snapping()
2627 {
2628     QQuickView *canvas = createView();
2629
2630     QaimModel model;
2631     for (int i = 0; i < 40; i++)
2632         model.addItem("Item" + QString::number(i), "");
2633
2634     canvas->rootContext()->setContextProperty("testModel", &model);
2635     canvas->setSource(testFileUrl("gridview1.qml"));
2636     qApp->processEvents();
2637
2638     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2639     QTRY_VERIFY(gridview != 0);
2640
2641     gridview->setHeight(220);
2642     QCOMPARE(gridview->height(), 220.);
2643
2644     gridview->positionViewAtIndex(12, QQuickGridView::Visible);
2645     QCOMPARE(gridview->contentY(), 80.);
2646
2647     gridview->setContentY(0);
2648     QCOMPARE(gridview->contentY(), 0.);
2649
2650     gridview->setSnapMode(QQuickGridView::SnapToRow);
2651     QCOMPARE(gridview->snapMode(), QQuickGridView::SnapToRow);
2652
2653     gridview->positionViewAtIndex(12, QQuickGridView::Visible);
2654     QCOMPARE(gridview->contentY(), 60.);
2655
2656     gridview->positionViewAtIndex(15, QQuickGridView::End);
2657     QCOMPARE(gridview->contentY(), 120.);
2658
2659     delete canvas;
2660
2661 }
2662
2663 void tst_QQuickGridView::mirroring()
2664 {
2665     QQuickView *canvasA = createView();
2666     canvasA->setSource(testFileUrl("mirroring.qml"));
2667     QQuickGridView *gridviewA = findItem<QQuickGridView>(canvasA->rootObject(), "view");
2668     QTRY_VERIFY(gridviewA != 0);
2669
2670     QQuickView *canvasB = createView();
2671     canvasB->setSource(testFileUrl("mirroring.qml"));
2672     QQuickGridView *gridviewB = findItem<QQuickGridView>(canvasB->rootObject(), "view");
2673     QTRY_VERIFY(gridviewA != 0);
2674     qApp->processEvents();
2675
2676     QList<QString> objectNames;
2677     objectNames << "item1" << "item2"; // << "item3"
2678
2679     gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
2680     gridviewB->setProperty("layoutDirection", Qt::RightToLeft);
2681     QCOMPARE(gridviewA->layoutDirection(), gridviewA->effectiveLayoutDirection());
2682
2683     // LTR != RTL
2684     foreach (const QString objectName, objectNames)
2685         QVERIFY(findItem<QQuickItem>(gridviewA, objectName)->x() != findItem<QQuickItem>(gridviewB, objectName)->x());
2686
2687     gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
2688     gridviewB->setProperty("layoutDirection", Qt::LeftToRight);
2689
2690     // LTR == LTR
2691     foreach (const QString objectName, objectNames)
2692         QCOMPARE(findItem<QQuickItem>(gridviewA, objectName)->x(), findItem<QQuickItem>(gridviewB, objectName)->x());
2693
2694     QVERIFY(gridviewB->layoutDirection() == gridviewB->effectiveLayoutDirection());
2695     QQuickItemPrivate::get(gridviewB)->setLayoutMirror(true);
2696     QVERIFY(gridviewB->layoutDirection() != gridviewB->effectiveLayoutDirection());
2697
2698     // LTR != LTR+mirror
2699     foreach (const QString objectName, objectNames)
2700         QVERIFY(findItem<QQuickItem>(gridviewA, objectName)->x() != findItem<QQuickItem>(gridviewB, objectName)->x());
2701
2702     gridviewA->setProperty("layoutDirection", Qt::RightToLeft);
2703
2704     // RTL == LTR+mirror
2705     foreach (const QString objectName, objectNames)
2706         QCOMPARE(findItem<QQuickItem>(gridviewA, objectName)->x(), findItem<QQuickItem>(gridviewB, objectName)->x());
2707
2708     gridviewB->setProperty("layoutDirection", Qt::RightToLeft);
2709
2710     // RTL != RTL+mirror
2711     foreach (const QString objectName, objectNames)
2712         QVERIFY(findItem<QQuickItem>(gridviewA, objectName)->x() != findItem<QQuickItem>(gridviewB, objectName)->x());
2713
2714     gridviewA->setProperty("layoutDirection", Qt::LeftToRight);
2715
2716     // LTR == RTL+mirror
2717     foreach (const QString objectName, objectNames)
2718         QCOMPARE(findItem<QQuickItem>(gridviewA, objectName)->x(), findItem<QQuickItem>(gridviewB, objectName)->x());
2719
2720     delete canvasA;
2721     delete canvasB;
2722 }
2723
2724 void tst_QQuickGridView::positionViewAtIndex_rightToLeft()
2725 {
2726     QQuickView *canvas = createView();
2727
2728     QaimModel model;
2729     for (int i = 0; i < 40; i++)
2730         model.addItem("Item" + QString::number(i), "");
2731
2732     QQmlContext *ctxt = canvas->rootContext();
2733     ctxt->setContextProperty("testModel", &model);
2734     ctxt->setContextProperty("testTopToBottom", QVariant(true));
2735     ctxt->setContextProperty("testRightToLeft", QVariant(true));
2736     ctxt->setContextProperty("testBottomToTop", QVariant(false));
2737
2738     canvas->setSource(testFileUrl("layouts.qml"));
2739     qApp->processEvents();
2740
2741     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2742     QTRY_VERIFY(gridview != 0);
2743
2744     QQuickItem *contentItem = gridview->contentItem();
2745     QTRY_VERIFY(contentItem != 0);
2746
2747     // Confirm items positioned correctly
2748     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2749     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2750         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2751         if (!item) qWarning() << "Item" << i << "not found";
2752         QTRY_VERIFY(item);
2753         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
2754         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2755     }
2756
2757     // Position on a currently visible item
2758     gridview->positionViewAtIndex(6, QQuickGridView::Beginning);
2759     QTRY_COMPARE(gridview->contentX(), -320.);
2760
2761     // Confirm items positioned correctly
2762     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2763     for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
2764         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2765         if (!item) qWarning() << "Item" << i << "not found";
2766         QTRY_VERIFY(item);
2767         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
2768         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2769     }
2770
2771     // Position on an item beyond the visible items
2772     gridview->positionViewAtIndex(21, QQuickGridView::Beginning);
2773     QTRY_COMPARE(gridview->contentX(), -560.);
2774
2775     // Confirm items positioned correctly
2776     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2777     for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
2778         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2779         if (!item) qWarning() << "Item" << i << "not found";
2780         QTRY_VERIFY(item);
2781         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
2782         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2783     }
2784
2785     // Position on an item that would leave empty space if positioned at the top
2786     gridview->positionViewAtIndex(31, QQuickGridView::Beginning);
2787     QTRY_COMPARE(gridview->contentX(), -640.);
2788
2789     // Confirm items positioned correctly
2790     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2791     for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
2792         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2793         if (!item) qWarning() << "Item" << i << "not found";
2794         QTRY_VERIFY(item);
2795         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
2796         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2797     }
2798
2799     // Position at the beginning again
2800     gridview->positionViewAtIndex(0, QQuickGridView::Beginning);
2801     QTRY_COMPARE(gridview->contentX(), -240.);
2802
2803     // Confirm items positioned correctly
2804     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2805     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2806         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2807         if (!item) qWarning() << "Item" << i << "not found";
2808         QTRY_VERIFY(item);
2809         QTRY_COMPARE(item->x(), qreal(-(i/5)*80-item->width()));
2810         QTRY_COMPARE(item->y(), qreal((i%5)*60));
2811     }
2812
2813     // Position at End
2814     gridview->positionViewAtIndex(30, QQuickGridView::End);
2815     QTRY_COMPARE(gridview->contentX(), -560.);
2816
2817     // Position in Center
2818     gridview->positionViewAtIndex(15, QQuickGridView::Center);
2819     QTRY_COMPARE(gridview->contentX(), -400.);
2820
2821     // Ensure at least partially visible
2822     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2823     QTRY_COMPARE(gridview->contentX(), -400.);
2824
2825     gridview->setContentX(-555.);
2826     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2827     QTRY_COMPARE(gridview->contentX(), -555.);
2828
2829     gridview->setContentX(-239);
2830     gridview->positionViewAtIndex(15, QQuickGridView::Visible);
2831     QTRY_COMPARE(gridview->contentX(), -320.);
2832
2833     gridview->setContentX(-239);
2834     gridview->positionViewAtIndex(20, QQuickGridView::Visible);
2835     QTRY_COMPARE(gridview->contentX(), -400.);
2836
2837     gridview->setContentX(-640);
2838     gridview->positionViewAtIndex(20, QQuickGridView::Visible);
2839     QTRY_COMPARE(gridview->contentX(), -560.);
2840
2841     // Ensure completely visible
2842     gridview->setContentX(-400);
2843     gridview->positionViewAtIndex(20, QQuickGridView::Contain);
2844     QTRY_COMPARE(gridview->contentX(), -400.);
2845
2846     gridview->setContentX(-315);
2847     gridview->positionViewAtIndex(15, QQuickGridView::Contain);
2848     QTRY_COMPARE(gridview->contentX(), -320.);
2849
2850     gridview->setContentX(-640);
2851     gridview->positionViewAtIndex(20, QQuickGridView::Contain);
2852     QTRY_COMPARE(gridview->contentX(), -560.);
2853
2854     delete canvas;
2855 }
2856
2857 void tst_QQuickGridView::resetModel()
2858 {
2859     QQuickView *canvas = createView();
2860
2861     QStringList strings;
2862     strings << "one" << "two" << "three";
2863     QStringListModel model(strings);
2864
2865     QQmlContext *ctxt = canvas->rootContext();
2866     ctxt->setContextProperty("testModel", &model);
2867
2868     canvas->setSource(testFileUrl("displaygrid.qml"));
2869     canvas->show();
2870     qApp->processEvents();
2871
2872     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2873     QTRY_VERIFY(gridview != 0);
2874     QQuickItem *contentItem = gridview->contentItem();
2875     QTRY_VERIFY(contentItem != 0);
2876     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
2877
2878     QTRY_COMPARE(gridview->count(), model.rowCount());
2879
2880     for (int i = 0; i < model.rowCount(); ++i) {
2881         QQuickText *display = findItem<QQuickText>(contentItem, "displayText", i);
2882         QTRY_VERIFY(display != 0);
2883         QTRY_COMPARE(display->text(), strings.at(i));
2884     }
2885
2886     strings.clear();
2887     strings << "four" << "five" << "six" << "seven";
2888     model.setStringList(strings);
2889
2890     QTRY_COMPARE(gridview->count(), model.rowCount());
2891
2892     for (int i = 0; i < model.rowCount(); ++i) {
2893         QQuickText *display = findItem<QQuickText>(contentItem, "displayText", i);
2894         QTRY_VERIFY(display != 0);
2895         QTRY_COMPARE(display->text(), strings.at(i));
2896     }
2897
2898     delete canvas;
2899 }
2900
2901 void tst_QQuickGridView::enforceRange()
2902 {
2903     QQuickView *canvas = createView();
2904
2905     QaimModel model;
2906     for (int i = 0; i < 30; i++)
2907         model.addItem("Item" + QString::number(i), "");
2908
2909     QQmlContext *ctxt = canvas->rootContext();
2910     ctxt->setContextProperty("testModel", &model);
2911     ctxt->setContextProperty("testRightToLeft", QVariant(false));
2912     ctxt->setContextProperty("testTopToBottom", QVariant(false));
2913
2914     canvas->setSource(testFileUrl("gridview-enforcerange.qml"));
2915     canvas->show();
2916     qApp->processEvents();
2917     QVERIFY(canvas->rootObject() != 0);
2918
2919     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2920     QTRY_VERIFY(gridview != 0);
2921
2922     QTRY_COMPARE(gridview->preferredHighlightBegin(), 100.0);
2923     QTRY_COMPARE(gridview->preferredHighlightEnd(), 100.0);
2924     QTRY_COMPARE(gridview->highlightRangeMode(), QQuickGridView::StrictlyEnforceRange);
2925     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
2926
2927     QQuickItem *contentItem = gridview->contentItem();
2928     QTRY_VERIFY(contentItem != 0);
2929
2930     // view should be positioned at the top of the range.
2931     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
2932     QTRY_VERIFY(item);
2933     QTRY_COMPARE(gridview->contentY(), -100.0);
2934
2935     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 0);
2936     QTRY_VERIFY(name != 0);
2937     QTRY_COMPARE(name->text(), model.name(0));
2938     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 0);
2939     QTRY_VERIFY(number != 0);
2940     QTRY_COMPARE(number->text(), model.number(0));
2941
2942     // Check currentIndex is updated when contentItem moves
2943     gridview->setContentY(0);
2944     QTRY_COMPARE(gridview->currentIndex(), 2);
2945
2946     gridview->setCurrentIndex(5);
2947     QTRY_COMPARE(gridview->contentY(), 100.);
2948
2949     QaimModel model2;
2950     for (int i = 0; i < 5; i++)
2951         model2.addItem("Item" + QString::number(i), "");
2952
2953     ctxt->setContextProperty("testModel", &model2);
2954     QCOMPARE(gridview->count(), 5);
2955
2956     delete canvas;
2957 }
2958
2959 void tst_QQuickGridView::enforceRange_rightToLeft()
2960 {
2961     QQuickView *canvas = createView();
2962
2963     QaimModel model;
2964     for (int i = 0; i < 30; i++)
2965         model.addItem("Item" + QString::number(i), "");
2966
2967     QQmlContext *ctxt = canvas->rootContext();
2968     ctxt->setContextProperty("testModel", &model);
2969     ctxt->setContextProperty("testRightToLeft", QVariant(true));
2970     ctxt->setContextProperty("testTopToBottom", QVariant(true));
2971
2972     canvas->setSource(testFileUrl("gridview-enforcerange.qml"));
2973     qApp->processEvents();
2974     QVERIFY(canvas->rootObject() != 0);
2975
2976     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
2977     QTRY_VERIFY(gridview != 0);
2978
2979     QCOMPARE(gridview->preferredHighlightBegin(), 100.0);
2980     QCOMPARE(gridview->preferredHighlightEnd(), 100.0);
2981     QCOMPARE(gridview->highlightRangeMode(), QQuickGridView::StrictlyEnforceRange);
2982
2983     QQuickItem *contentItem = gridview->contentItem();
2984     QVERIFY(contentItem != 0);
2985
2986     // view should be positioned at the top of the range.
2987     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
2988     QVERIFY(item);
2989     QTRY_COMPARE(gridview->contentX(), -140.);
2990     QTRY_COMPARE(gridview->contentY(), 0.0);
2991
2992     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 0);
2993     QTRY_VERIFY(name != 0);
2994     QTRY_COMPARE(name->text(), model.name(0));
2995     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 0);
2996     QTRY_VERIFY(number != 0);
2997     QTRY_COMPARE(number->text(), model.number(0));
2998
2999     // Check currentIndex is updated when contentItem moves
3000     gridview->setContentX(-240);
3001     QTRY_COMPARE(gridview->currentIndex(), 3);
3002
3003     gridview->setCurrentIndex(7);
3004     QTRY_COMPARE(gridview->contentX(), -340.);
3005     QTRY_COMPARE(gridview->contentY(), 0.0);
3006
3007     QaimModel model2;
3008     for (int i = 0; i < 5; i++)
3009         model2.addItem("Item" + QString::number(i), "");
3010
3011     ctxt->setContextProperty("testModel", &model2);
3012     QCOMPARE(gridview->count(), 5);
3013
3014     delete canvas;
3015 }
3016
3017 void tst_QQuickGridView::QTBUG_8456()
3018 {
3019     QQuickView *canvas = createView();
3020
3021     canvas->setSource(testFileUrl("setindex.qml"));
3022     qApp->processEvents();
3023
3024     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3025     QTRY_VERIFY(gridview != 0);
3026
3027     QTRY_COMPARE(gridview->currentIndex(), 0);
3028
3029     delete canvas;
3030 }
3031
3032 void tst_QQuickGridView::manualHighlight()
3033 {
3034     QQuickView *canvas = createView();
3035
3036     QString filename(testFile("manual-highlight.qml"));
3037     canvas->setSource(QUrl::fromLocalFile(filename));
3038
3039     qApp->processEvents();
3040
3041     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3042     QTRY_VERIFY(gridview != 0);
3043
3044     QQuickItem *contentItem = gridview->contentItem();
3045     QTRY_VERIFY(contentItem != 0);
3046
3047     QTRY_COMPARE(gridview->currentIndex(), 0);
3048     QTRY_COMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
3049     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
3050     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
3051
3052     gridview->setCurrentIndex(2);
3053
3054     QTRY_COMPARE(gridview->currentIndex(), 2);
3055     QTRY_COMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 2));
3056     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
3057     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
3058
3059     gridview->positionViewAtIndex(8, QQuickGridView::Contain);
3060
3061     QTRY_COMPARE(gridview->currentIndex(), 2);
3062     QTRY_COMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 2));
3063     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
3064     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
3065
3066     gridview->setFlow(QQuickGridView::FlowTopToBottom);
3067     QTRY_COMPARE(gridview->flow(), QQuickGridView::FlowTopToBottom);
3068
3069     gridview->setCurrentIndex(0);
3070     QTRY_COMPARE(gridview->currentIndex(), 0);
3071     QTRY_COMPARE(gridview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
3072     QTRY_COMPARE(gridview->highlightItem()->y() - 5, gridview->currentItem()->y());
3073     QTRY_COMPARE(gridview->highlightItem()->x() - 5, gridview->currentItem()->x());
3074
3075     delete canvas;
3076 }
3077
3078
3079 void tst_QQuickGridView::footer()
3080 {
3081     QFETCH(QQuickGridView::Flow, flow);
3082     QFETCH(Qt::LayoutDirection, layoutDirection);
3083     QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
3084     QFETCH(QPointF, initialFooterPos);
3085     QFETCH(QPointF, changedFooterPos);
3086     QFETCH(QPointF, initialContentPos);
3087     QFETCH(QPointF, firstDelegatePos);
3088     QFETCH(QPointF, resizeContentPos);
3089
3090     QQuickView *canvas = getView();
3091     canvas->show();
3092
3093     QaimModel model;
3094     for (int i = 0; i < 7; i++)
3095         model.addItem("Item" + QString::number(i), "");
3096
3097     QQmlContext *ctxt = canvas->rootContext();
3098     ctxt->setContextProperty("testModel", &model);
3099     canvas->setSource(testFileUrl("footer.qml"));
3100     qApp->processEvents();
3101
3102     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3103     QTRY_VERIFY(gridview != 0);
3104     gridview->setFlow(flow);
3105     gridview->setLayoutDirection(layoutDirection);
3106     gridview->setVerticalLayoutDirection(verticalLayoutDirection);
3107     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3108
3109     QQuickItem *contentItem = gridview->contentItem();
3110     QTRY_VERIFY(contentItem != 0);
3111
3112     QQuickText *footer = findItem<QQuickText>(contentItem, "footer");
3113     QVERIFY(footer);
3114     QVERIFY(footer == gridview->footerItem());
3115
3116     QCOMPARE(footer->pos(), initialFooterPos);
3117     QCOMPARE(footer->width(), 100.);
3118     QCOMPARE(footer->height(), 30.);
3119     QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
3120
3121     if (flow == QQuickGridView::FlowLeftToRight)
3122         QCOMPARE(gridview->contentHeight(), (model.count()+2) / 3 * 60. + footer->height());
3123     else
3124         QCOMPARE(gridview->contentWidth(), (model.count()+3) / 5 * 80. + footer->width());
3125
3126     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3127     QVERIFY(item);
3128     QCOMPARE(item->pos(), firstDelegatePos);
3129
3130     if (flow == QQuickGridView::FlowLeftToRight) {
3131         // shrink by one row
3132         model.removeItem(2);
3133         if (verticalLayoutDirection == QQuickItemView::TopToBottom)
3134             QTRY_COMPARE(footer->y(), initialFooterPos.y() - gridview->cellHeight());
3135         else
3136             QTRY_COMPARE(footer->y(), initialFooterPos.y() + gridview->cellHeight());
3137     } else {
3138         // shrink by one column
3139         model.removeItem(2);
3140         model.removeItem(3);
3141         if (layoutDirection == Qt::LeftToRight)
3142             QTRY_COMPARE(footer->x(), initialFooterPos.x() - gridview->cellWidth());
3143         else
3144             QTRY_COMPARE(footer->x(), initialFooterPos.x() + gridview->cellWidth());
3145     }
3146
3147     // remove all items
3148     model.clear();
3149
3150     QPointF posWhenNoItems(0, 0);
3151     if (layoutDirection == Qt::RightToLeft)
3152         posWhenNoItems.setX(flow == QQuickGridView::FlowLeftToRight ? gridview->width() - footer->width() : -footer->width());
3153     if (verticalLayoutDirection == QQuickItemView::BottomToTop)
3154         posWhenNoItems.setY(-footer->height());
3155     QTRY_COMPARE(footer->pos(), posWhenNoItems);
3156
3157     // if header is toggled, it shouldn't affect the footer position
3158     canvas->rootObject()->setProperty("showHeader", true);
3159     QVERIFY(findItem<QQuickItem>(contentItem, "header") != 0);
3160     QTRY_COMPARE(footer->pos(), posWhenNoItems);
3161     canvas->rootObject()->setProperty("showHeader", false);
3162
3163     // add 30 items
3164     for (int i = 0; i < 30; i++)
3165         model.addItem("Item" + QString::number(i), "");
3166
3167     QSignalSpy footerItemSpy(gridview, SIGNAL(footerItemChanged()));
3168     QMetaObject::invokeMethod(canvas->rootObject(), "changeFooter");
3169
3170     QCOMPARE(footerItemSpy.count(), 1);
3171
3172     footer = findItem<QQuickText>(contentItem, "footer");
3173     QVERIFY(!footer);
3174     footer = findItem<QQuickText>(contentItem, "footer2");
3175     QVERIFY(footer);
3176     QVERIFY(footer == gridview->footerItem());
3177
3178     QCOMPARE(footer->pos(), changedFooterPos);
3179     QCOMPARE(footer->width(), 50.);
3180     QCOMPARE(footer->height(), 20.);
3181
3182     // changing the footer shouldn't change the content pos
3183     QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
3184
3185     item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3186     QVERIFY(item);
3187     QCOMPARE(item->pos(), firstDelegatePos);
3188
3189     gridview->positionViewAtEnd();
3190     footer->setHeight(10);
3191     footer->setWidth(40);
3192     QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), resizeContentPos);
3193
3194     releaseView(canvas);
3195 }
3196
3197 void tst_QQuickGridView::footer_data()
3198 {
3199     QTest::addColumn<QQuickGridView::Flow>("flow");
3200     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3201     QTest::addColumn<QQuickItemView::VerticalLayoutDirection>("verticalLayoutDirection");
3202     QTest::addColumn<QPointF>("initialFooterPos");
3203     QTest::addColumn<QPointF>("changedFooterPos");
3204     QTest::addColumn<QPointF>("initialContentPos");
3205     QTest::addColumn<QPointF>("firstDelegatePos");
3206     QTest::addColumn<QPointF>("resizeContentPos");
3207
3208     // footer1 = 100 x 30
3209     // footer2 = 50 x 20
3210     // cells = 80 * 60
3211     // view width = 240
3212     // view height = 320
3213
3214     // footer below items, bottom left
3215     QTest::newRow("LeftToRight, LtR, TtB")
3216         << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::TopToBottom
3217         << QPointF(0, 3 * 60)  // 180 = height of 3 rows (cell height is 60)
3218         << QPointF(0, 10 * 60)  // 30 items = 10 rows
3219         << QPointF(0, 0)
3220         << QPointF(0, 0)
3221         << QPointF(0, (10 * 60) - 320 + 10);
3222
3223     // footer below items, bottom right
3224     QTest::newRow("LeftToRight, RtL, TtB")
3225         << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::TopToBottom
3226         << QPointF(240 - 100, 3 * 60)
3227         << QPointF((240 - 100) + 50, 10 * 60)     // 50 = width diff between old and new footers
3228         << QPointF(0, 0)
3229         << QPointF(240 - 80, 0)
3230         << QPointF(0, (10 * 60) - 320 + 10);
3231
3232     // footer above items, top left
3233     QTest::newRow("LeftToRight, LtR, BtT")
3234         << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::BottomToTop
3235         << QPointF(0, -(3 * 60) - 30)
3236         << QPointF(0, -(10 * 60) - 20)
3237         << QPointF(0, -320)
3238         << QPointF(0, -60)
3239         << QPointF(0, -(10 * 60) - 10);
3240
3241     // footer above items, top right
3242     QTest::newRow("LeftToRight, RtL, BtT")
3243         << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::BottomToTop
3244         << QPointF(240 - 100, -(3 * 60) - 30)
3245         << QPointF((240 - 100) + 50, -(10 * 60) - 20)
3246         << QPointF(0, -320)
3247         << QPointF(240 - 80, -60)
3248         << QPointF(0, -(10 * 60) - 10);
3249
3250
3251     // footer to right of items, bottom right
3252     QTest::newRow("TopToBottom, LtR, TtB")
3253         << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::TopToBottom
3254         << QPointF(2 * 80, 0)      // 2 columns, cell width 80
3255         << QPointF(6 * 80, 0)      // 30 items = 6 columns
3256         << QPointF(0, 0)
3257         << QPointF(0, 0)
3258         << QPointF((6 * 80) - 240 + 40, 0);
3259
3260     // footer to left of items, bottom right
3261     QTest::newRow("TopToBottom, RtL, TtB")
3262         << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::TopToBottom
3263         << QPointF(-(2 * 80) - 100, 0)
3264         << QPointF(-(6 * 80) - 50, 0)     // 50 = new footer width
3265         << QPointF(-240, 0)
3266         << QPointF(-80, 0)
3267         << QPointF(-(6 * 80) - 40, 0);
3268
3269     // footer to right of items, top right
3270     QTest::newRow("TopToBottom, LtR, BtT")
3271         << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::BottomToTop
3272         << QPointF(2 * 80, -30)
3273         << QPointF(6 * 80, -20)
3274         << QPointF(0, -320)
3275         << QPointF(0, -60)
3276         << QPointF((6 * 80) - 240 + 40, -320);
3277
3278     // footer to left of items, top left
3279     QTest::newRow("TopToBottom, RtL, BtT")
3280         << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::BottomToTop
3281         << QPointF(-(2 * 80) - 100, -30)
3282         << QPointF(-(6 * 80) - 50, -20)
3283         << QPointF(-240, -320)
3284         << QPointF(-80, -60)
3285         << QPointF(-(6 * 80) - 40, -320);
3286 }
3287
3288 void tst_QQuickGridView::initialZValues()
3289 {
3290     QQuickView *canvas = createView();
3291     canvas->setSource(testFileUrl("initialZValues.qml"));
3292     qApp->processEvents();
3293
3294     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3295     QTRY_VERIFY(gridview != 0);
3296     QQuickItem *contentItem = gridview->contentItem();
3297     QTRY_VERIFY(contentItem != 0);
3298
3299     QVERIFY(gridview->headerItem());
3300     QTRY_COMPARE(gridview->headerItem()->z(), gridview->property("initialZ").toReal());
3301
3302     QVERIFY(gridview->footerItem());
3303     QTRY_COMPARE(gridview->footerItem()->z(), gridview->property("initialZ").toReal());
3304
3305     delete canvas;
3306 }
3307
3308 void tst_QQuickGridView::header()
3309 {
3310     QFETCH(QQuickGridView::Flow, flow);
3311     QFETCH(Qt::LayoutDirection, layoutDirection);
3312     QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
3313     QFETCH(QPointF, initialHeaderPos);
3314     QFETCH(QPointF, changedHeaderPos);
3315     QFETCH(QPointF, initialContentPos);
3316     QFETCH(QPointF, changedContentPos);
3317     QFETCH(QPointF, firstDelegatePos);
3318     QFETCH(QPointF, resizeContentPos);
3319
3320     QaimModel model;
3321     for (int i = 0; i < 30; i++)
3322         model.addItem("Item" + QString::number(i), "");
3323
3324     QQuickView *canvas = getView();
3325     canvas->rootContext()->setContextProperty("testModel", &model);
3326     canvas->rootContext()->setContextProperty("initialViewWidth", 240);
3327     canvas->rootContext()->setContextProperty("initialViewHeight", 320);
3328     canvas->setSource(testFileUrl("header.qml"));
3329     canvas->show();
3330     qApp->processEvents();
3331
3332     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3333     QTRY_VERIFY(gridview != 0);
3334     gridview->setFlow(flow);
3335     gridview->setLayoutDirection(layoutDirection);
3336     gridview->setVerticalLayoutDirection(verticalLayoutDirection);
3337     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3338
3339     QQuickItem *contentItem = gridview->contentItem();
3340     QTRY_VERIFY(contentItem != 0);
3341
3342     QQuickText *header = findItem<QQuickText>(contentItem, "header");
3343     QVERIFY(header);
3344     QVERIFY(header == gridview->headerItem());
3345
3346     QCOMPARE(header->pos(), initialHeaderPos);
3347     QCOMPARE(header->width(), 100.);
3348     QCOMPARE(header->height(), 30.);
3349     QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
3350
3351     if (flow == QQuickGridView::FlowLeftToRight)
3352         QCOMPARE(gridview->contentHeight(), (model.count()+2) / 3 * 60. + header->height());
3353     else
3354         QCOMPARE(gridview->contentWidth(), (model.count()+3) / 5 * 80. + header->width());
3355
3356     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3357     QVERIFY(item);
3358     QCOMPARE(item->pos(), firstDelegatePos);
3359
3360     model.clear();
3361     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3362     QCOMPARE(header->pos(), initialHeaderPos); // header should stay where it is
3363
3364     for (int i = 0; i < 30; i++)
3365         model.addItem("Item" + QString::number(i), "");
3366
3367     QSignalSpy headerItemSpy(gridview, SIGNAL(headerItemChanged()));
3368     QMetaObject::invokeMethod(canvas->rootObject(), "changeHeader");
3369
3370     QCOMPARE(headerItemSpy.count(), 1);
3371
3372     header = findItem<QQuickText>(contentItem, "header");
3373     QVERIFY(!header);
3374     header = findItem<QQuickText>(contentItem, "header2");
3375     QVERIFY(header);
3376
3377     QVERIFY(header == gridview->headerItem());
3378
3379     QCOMPARE(header->pos(), changedHeaderPos);
3380     QCOMPARE(header->width(), 50.);
3381     QCOMPARE(header->height(), 20.);
3382     QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), changedContentPos);
3383
3384     item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3385     QVERIFY(item);
3386     QCOMPARE(item->pos(), firstDelegatePos);
3387
3388     header->setHeight(10);
3389     header->setWidth(40);
3390     QTRY_COMPARE(QPointF(gridview->contentX(), gridview->contentY()), resizeContentPos);
3391
3392     releaseView(canvas);
3393
3394
3395     // QTBUG-21207 header should become visible if view resizes from initial empty size
3396
3397     canvas = getView();
3398     canvas->rootContext()->setContextProperty("testModel", &model);
3399     canvas->rootContext()->setContextProperty("initialViewWidth", 240);
3400     canvas->rootContext()->setContextProperty("initialViewHeight", 320);
3401     canvas->setSource(testFileUrl("header.qml"));
3402     canvas->show();
3403     qApp->processEvents();
3404
3405     gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3406     QTRY_VERIFY(gridview != 0);
3407     gridview->setFlow(flow);
3408     gridview->setLayoutDirection(layoutDirection);
3409     gridview->setVerticalLayoutDirection(verticalLayoutDirection);
3410     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3411
3412     gridview->setWidth(240);
3413     gridview->setHeight(320);
3414     QTRY_COMPARE(gridview->headerItem()->pos(), initialHeaderPos);
3415     QCOMPARE(QPointF(gridview->contentX(), gridview->contentY()), initialContentPos);
3416
3417     releaseView(canvas);
3418 }
3419
3420 void tst_QQuickGridView::header_data()
3421 {
3422     QTest::addColumn<QQuickGridView::Flow>("flow");
3423     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3424     QTest::addColumn<QQuickItemView::VerticalLayoutDirection>("verticalLayoutDirection");
3425     QTest::addColumn<QPointF>("initialHeaderPos");
3426     QTest::addColumn<QPointF>("changedHeaderPos");
3427     QTest::addColumn<QPointF>("initialContentPos");
3428     QTest::addColumn<QPointF>("changedContentPos");
3429     QTest::addColumn<QPointF>("firstDelegatePos");
3430     QTest::addColumn<QPointF>("resizeContentPos");
3431
3432     // header1 = 100 x 30
3433     // header2 = 50 x 20
3434     // cells = 80 x 60
3435     // view width = 240
3436
3437     // header above items, top left
3438     QTest::newRow("LeftToRight, LtR, TtB")
3439         << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::TopToBottom
3440         << QPointF(0, -30)
3441         << QPointF(0, -20)
3442         << QPointF(0, -30)
3443         << QPointF(0, -20)
3444         << QPointF(0, 0)
3445         << QPointF(0, -10);
3446
3447     // header above items, top right
3448     QTest::newRow("LeftToRight, RtL, TtB")
3449         << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::TopToBottom
3450         << QPointF(240 - 100, -30)
3451         << QPointF((240 - 100) + 50, -20)     // 50 = width diff between old and new headers
3452         << QPointF(0, -30)
3453         << QPointF(0, -20)
3454         << QPointF(160, 0)
3455         << QPointF(0, -10);
3456
3457     // header below items, bottom left
3458     QTest::newRow("LeftToRight, LtR, BtT")
3459         << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::BottomToTop
3460         << QPointF(0, 0)
3461         << QPointF(0, 0)
3462         << QPointF(0, -320 + 30)
3463         << QPointF(0, -320 + 20)
3464         << QPointF(0, -60)
3465         << QPointF(0, -320 + 10);
3466
3467     // header above items, top right
3468     QTest::newRow("LeftToRight, RtL, BtT")
3469         << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::BottomToTop
3470         << QPointF(240 - 100, 0)
3471         << QPointF((240 - 100) + 50, 0)
3472         << QPointF(0, -320 + 30)
3473         << QPointF(0, -320 + 20)
3474         << QPointF(160, -60)
3475         << QPointF(0, -320 + 10);
3476
3477
3478     // header to left of items, bottom left
3479     QTest::newRow("TopToBottom, LtR, TtB")
3480         << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::TopToBottom
3481         << QPointF(-100, 0)
3482         << QPointF(-50, 0)
3483         << QPointF(-100, 0)
3484         << QPointF(-50, 0)
3485         << QPointF(0, 0)
3486         << QPointF(-40, 0);
3487
3488     // header to right of items, bottom right
3489     QTest::newRow("TopToBottom, RtL, TtB")
3490         << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::TopToBottom
3491         << QPointF(0, 0)
3492         << QPointF(0, 0)
3493         << QPointF(-(240 - 100), 0)
3494         << QPointF(-(240 - 50), 0)
3495         << QPointF(-80, 0)
3496         << QPointF(-(240 - 40), 0);
3497
3498     // header to left of items, top left
3499     QTest::newRow("TopToBottom, LtR, BtT")
3500         << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::BottomToTop
3501         << QPointF(-100, -30)
3502         << QPointF(-50, -20)
3503         << QPointF(-100, -320)
3504         << QPointF(-50, -320)
3505         << QPointF(0, -60)
3506         << QPointF(-40, -320);
3507
3508     // header to right of items, top right
3509     QTest::newRow("TopToBottom, RtL, BtT")
3510         << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::BottomToTop
3511         << QPointF(0, -30)
3512         << QPointF(0, -20)
3513         << QPointF(-(240 - 100), -320)
3514         << QPointF(-(240 - 50), -320)
3515         << QPointF(-80, -60)
3516         << QPointF(-(240 - 40), -320);
3517 }
3518
3519 class GVAccessor : public QQuickGridView
3520 {
3521 public:
3522     qreal minY() const { return minYExtent(); }
3523     qreal maxY() const { return maxYExtent(); }
3524     qreal minX() const { return minXExtent(); }
3525     qreal maxX() const { return maxXExtent(); }
3526 };
3527
3528 void tst_QQuickGridView::extents()
3529 {
3530     QFETCH(QQuickGridView::Flow, flow);
3531     QFETCH(Qt::LayoutDirection, layoutDirection);
3532     QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
3533     QFETCH(QPointF, headerPos);
3534     QFETCH(QPointF, footerPos);
3535     QFETCH(QPointF, minPos);
3536     QFETCH(QPointF, maxPos);
3537     QFETCH(QPointF, origin_empty);
3538     QFETCH(QPointF, origin_nonEmpty);
3539
3540     QQuickView *canvas = getView();
3541
3542     QmlListModel model;
3543     QQmlContext *ctxt = canvas->rootContext();
3544     ctxt->setContextProperty("testModel", &model);
3545     canvas->setSource(testFileUrl("headerfooter.qml"));
3546     canvas->show();
3547     qApp->processEvents();
3548
3549     QQuickGridView *gridview = qobject_cast<QQuickGridView*>(canvas->rootObject());
3550     QTRY_VERIFY(gridview != 0);
3551     gridview->setFlow(flow);
3552     gridview->setLayoutDirection(layoutDirection);
3553     gridview->setVerticalLayoutDirection(verticalLayoutDirection);
3554     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3555
3556     QQuickItem *contentItem = gridview->contentItem();
3557     QTRY_VERIFY(contentItem != 0);
3558
3559     QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
3560     QVERIFY(header);
3561     QCOMPARE(header->pos(), headerPos);
3562
3563     QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
3564     QVERIFY(footer);
3565     QCOMPARE(footer->pos(), footerPos);
3566
3567     QCOMPARE(static_cast<GVAccessor*>(gridview)->minX(), minPos.x());
3568     QCOMPARE(static_cast<GVAccessor*>(gridview)->minY(), minPos.y());
3569     QCOMPARE(static_cast<GVAccessor*>(gridview)->maxX(), maxPos.x());
3570     QCOMPARE(static_cast<GVAccessor*>(gridview)->maxY(), maxPos.y());
3571
3572     QCOMPARE(gridview->xOrigin(), origin_empty.x());
3573     QCOMPARE(gridview->yOrigin(), origin_empty.y());
3574     for (int i=0; i<30; i++)
3575         model.addItem("Item" + QString::number(i), "");
3576     QTRY_COMPARE(gridview->count(), model.count());
3577     QCOMPARE(gridview->xOrigin(), origin_nonEmpty.x());
3578     QCOMPARE(gridview->yOrigin(), origin_nonEmpty.y());
3579
3580     releaseView(canvas);
3581 }
3582
3583 void tst_QQuickGridView::extents_data()
3584 {
3585     QTest::addColumn<QQuickGridView::Flow>("flow");
3586     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3587     QTest::addColumn<QQuickItemView::VerticalLayoutDirection>("verticalLayoutDirection");
3588     QTest::addColumn<QPointF>("headerPos");
3589     QTest::addColumn<QPointF>("footerPos");
3590     QTest::addColumn<QPointF>("minPos");
3591     QTest::addColumn<QPointF>("maxPos");
3592     QTest::addColumn<QPointF>("origin_empty");
3593     QTest::addColumn<QPointF>("origin_nonEmpty");
3594
3595     // header is 240x20 (or 20x320 in TopToBottom)
3596     // footer is 240x30 (or 30x320 in TopToBottom)
3597     // grid has 10 rows in LeftToRight mode and 6 columns in TopToBottom
3598
3599     QTest::newRow("LeftToRight, LtR, TtB")
3600             << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::TopToBottom
3601             << QPointF(0, -20) << QPointF(0, 0)
3602             << QPointF(0, 20) << QPointF(240, 20)
3603             << QPointF(0, -20) << QPointF(0, -20);
3604
3605     QTest::newRow("LeftToRight, RtL, TtB")
3606             << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::TopToBottom
3607             << QPointF(0, -20) << QPointF(0, 0)
3608             << QPointF(0, 20) << QPointF(240, 20)
3609             << QPointF(0, -20) << QPointF(0, -20);
3610
3611     QTest::newRow("LeftToRight, LtR, BtT")
3612             << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::BottomToTop
3613             << QPointF(0, 0) << QPointF(0, -30)
3614             << QPointF(0, 320 - 20) << QPointF(240, 320 - 20)  // content flow is reversed
3615             << QPointF(0, -30) << QPointF(0, (-60.0 * 10) - 30);
3616
3617     QTest::newRow("LeftToRight, RtL, BtT")
3618             << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::BottomToTop
3619             << QPointF(0, 0) << QPointF(0, -30)
3620             << QPointF(0, 320 - 20) << QPointF(240, 320 - 20)  // content flow is reversed
3621             << QPointF(0, -30) << QPointF(0, (-60.0 * 10) - 30);
3622
3623
3624     QTest::newRow("TopToBottom, LtR, TtB")
3625             << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::TopToBottom
3626             << QPointF(-20, 0) << QPointF(0, 0)
3627             << QPointF(20, 0) << QPointF(20, 320)
3628             << QPointF(-20, 0) << QPointF(-20, 0);
3629
3630     QTest::newRow("TopToBottom, RtL, TtB")
3631             << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::TopToBottom
3632             << QPointF(0, 0) << QPointF(-30, 0)
3633             << QPointF(240 - 20, 0) << QPointF(240 - 20, 320)  // content flow is reversed
3634             << QPointF(-30, 0) << QPointF((-80.0 * 6) - 30, 0);
3635
3636     QTest::newRow("TopToBottom, LtR, BtT")
3637             << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::BottomToTop
3638             << QPointF(-20, -320) << QPointF(0, -320)
3639             << QPointF(20, 0) << QPointF(20, 320)
3640             << QPointF(-20, 0) << QPointF(-20, 0);
3641
3642     QTest::newRow("TopToBottom, RtL, BtT")
3643             << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::BottomToTop
3644             << QPointF(0, -320) << QPointF(-30, -320)
3645             << QPointF(240 - 20, 0) << QPointF(240 - 20, 320)  // content flow is reversed
3646             << QPointF(-30, 0) << QPointF((-80.0 * 6) - 30, 0);
3647 }
3648
3649 void tst_QQuickGridView::resetModel_headerFooter()
3650 {
3651     // Resetting a model shouldn't crash in views with header/footer
3652
3653     QQuickView *canvas = createView();
3654
3655     QaimModel model;
3656     for (int i = 0; i < 6; i++)
3657         model.addItem("Item" + QString::number(i), "");
3658     QQmlContext *ctxt = canvas->rootContext();
3659     ctxt->setContextProperty("testModel", &model);
3660
3661     canvas->setSource(testFileUrl("headerfooter.qml"));
3662     qApp->processEvents();
3663
3664     QQuickGridView *gridview = qobject_cast<QQuickGridView*>(canvas->rootObject());
3665     QTRY_VERIFY(gridview != 0);
3666
3667     QQuickItem *contentItem = gridview->contentItem();
3668     QTRY_VERIFY(contentItem != 0);
3669
3670     QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
3671     QVERIFY(header);
3672     QCOMPARE(header->y(), -header->height());
3673
3674     QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
3675     QVERIFY(footer);
3676     QCOMPARE(footer->y(), 60.*2);
3677
3678     model.reset();
3679
3680     header = findItem<QQuickItem>(contentItem, "header");
3681     QVERIFY(header);
3682     QCOMPARE(header->y(), -header->height());
3683
3684     footer = findItem<QQuickItem>(contentItem, "footer");
3685     QVERIFY(footer);
3686     QCOMPARE(footer->y(), 60.*2);
3687
3688     delete canvas;
3689 }
3690
3691 void tst_QQuickGridView::resizeViewAndRepaint()
3692 {
3693     QQuickView *canvas = createView();
3694     canvas->show();
3695
3696     QaimModel model;
3697     for (int i = 0; i < 40; i++)
3698         model.addItem("Item" + QString::number(i), "");
3699
3700     QQmlContext *ctxt = canvas->rootContext();
3701     ctxt->setContextProperty("testModel", &model);
3702     ctxt->setContextProperty("initialWidth", 240);
3703     ctxt->setContextProperty("initialHeight", 100);
3704
3705     canvas->setSource(testFileUrl("resizeview.qml"));
3706     canvas->show();
3707     qApp->processEvents();
3708
3709     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3710     QTRY_VERIFY(gridview != 0);
3711     QQuickItem *contentItem = gridview->contentItem();
3712     QTRY_VERIFY(contentItem != 0);
3713     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3714
3715     // item at index 10 should not be currently visible
3716     QVERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 10));
3717
3718     gridview->setHeight(320);
3719     QTRY_VERIFY(findItem<QQuickItem>(contentItem, "wrapper", 10));
3720
3721     gridview->setHeight(100);
3722     QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 10));
3723
3724     // Ensure we handle -ve sizes
3725     gridview->setHeight(-100);
3726     QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper", false).count(), 3);
3727
3728     gridview->setCacheBuffer(120);
3729     QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper", false).count(), 9);
3730
3731     // ensure items in cache become visible
3732     gridview->setHeight(120);
3733     QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper", false).count(), 15);
3734
3735     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3736     for (int i = 0; i < model.count() && i < itemCount; ++i) {
3737         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3738         if (!item) qWarning() << "Item" << i << "not found";
3739         QTRY_VERIFY(item);
3740         QTRY_COMPARE(item->x(), qreal((i%3)*80));
3741         QTRY_COMPARE(item->y(), qreal((i/3)*60));
3742         QCOMPARE(item->isVisible(), i < 9); // inside view visible, outside not visible
3743     }
3744
3745     // ensure items outside view become invisible
3746     gridview->setHeight(60);
3747     QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper", false).count(), 12);
3748
3749     itemCount = findItems<QQuickItem>(contentItem, "wrapper", false).count();
3750     for (int i = 0; i < model.count() && i < itemCount; ++i) {
3751         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3752         if (!item) qWarning() << "Item" << i << "not found";
3753         QTRY_VERIFY(item);
3754         QTRY_COMPARE(item->x(), qreal((i%3)*80));
3755         QTRY_COMPARE(item->y(), qreal((i/3)*60));
3756         QCOMPARE(item->isVisible(), i < 6); // inside view visible, outside not visible
3757     }
3758
3759     delete canvas;
3760 }
3761
3762 void tst_QQuickGridView::resizeGrid()
3763 {
3764     QFETCH(QQuickGridView::Flow, flow);
3765     QFETCH(Qt::LayoutDirection, layoutDirection);
3766     QFETCH(QQuickItemView::VerticalLayoutDirection, verticalLayoutDirection);
3767     QFETCH(QPointF, initialContentPos);
3768     QFETCH(QPointF, firstItemPos);
3769
3770     QaimModel model;
3771     for (int i = 0; i < 30; i++)
3772         model.addItem("Item" + QString::number(i), "");
3773
3774     QQuickView *canvas = getView();
3775     QQmlContext *ctxt = canvas->rootContext();
3776     ctxt->setContextProperty("testModel", &model);
3777     ctxt->setContextProperty("testTopToBottom", flow == QQuickGridView::FlowTopToBottom);
3778     ctxt->setContextProperty("testRightToLeft", layoutDirection == Qt::RightToLeft);
3779     ctxt->setContextProperty("testBottomToTop", verticalLayoutDirection == QQuickGridView::BottomToTop);
3780     canvas->setSource(testFileUrl("resizegrid.qml"));
3781     canvas->show();
3782     qApp->processEvents();
3783
3784     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3785     QTRY_VERIFY(gridview != 0);
3786     QQuickItem *contentItem = gridview->contentItem();
3787     QTRY_VERIFY(contentItem != 0);
3788
3789     // set the width to slightly larger than 3 items across, to test
3790     // items are aligned correctly in right-to-left
3791     canvas->rootObject()->setWidth(260);
3792     canvas->rootObject()->setHeight(320);
3793     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3794
3795     QCOMPARE(gridview->contentX(), initialContentPos.x());
3796     QCOMPARE(gridview->contentY(), initialContentPos.y());
3797
3798     QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper", 0);
3799     QVERIFY(item0);
3800     QCOMPARE(item0->pos(), firstItemPos);
3801
3802     // Confirm items positioned correctly and indexes correct
3803     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
3804     QVERIFY(items.count() >= 18 && items.count() <= 21);
3805     for (int i = 0; i < model.count() && i < items.count(); ++i) {
3806         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3807         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
3808         QCOMPARE(item->pos(), expectedItemPos(gridview, i, 0));
3809         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
3810         QVERIFY(name != 0);
3811         QCOMPARE(name->text(), model.name(i));
3812     }
3813
3814     // change from 3x5 grid to 4x7
3815     canvas->rootObject()->setWidth(canvas->rootObject()->width() + 80);
3816     canvas->rootObject()->setHeight(canvas->rootObject()->height() + 60*2);
3817     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3818
3819     // other than in LeftToRight+RightToLeft layout, the first item should not move
3820     // if view is resized
3821     QCOMPARE(findItem<QQuickItem>(contentItem, "wrapper", 0), item0);
3822     if (flow == QQuickGridView::FlowLeftToRight && layoutDirection == Qt::RightToLeft)
3823         firstItemPos.rx() += 80;
3824     QCOMPARE(item0->pos(), firstItemPos);
3825
3826     QPointF newContentPos = initialContentPos;
3827     if (flow == QQuickGridView::FlowTopToBottom && layoutDirection == Qt::RightToLeft)
3828         newContentPos.rx() -= 80.0;
3829     if (verticalLayoutDirection == QQuickItemView::BottomToTop)
3830         newContentPos.ry() -= 60.0 * 2;
3831     QCOMPARE(gridview->contentX(), newContentPos.x());
3832     QCOMPARE(gridview->contentY(), newContentPos.y());
3833
3834     // Confirm items positioned correctly and indexes correct
3835     items = findItems<QQuickItem>(contentItem, "wrapper");
3836     QVERIFY(items.count() >= 28);
3837     for (int i = 0; i < model.count() && i < items.count(); ++i) {
3838         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3839         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
3840         QCOMPARE(item->pos(), expectedItemPos(gridview, i, 0));
3841         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
3842         QVERIFY(name != 0);
3843         QCOMPARE(name->text(), model.name(i));
3844     }
3845
3846     releaseView(canvas);
3847 }
3848
3849 void tst_QQuickGridView::resizeGrid_data()
3850 {
3851     QTest::addColumn<QQuickGridView::Flow>("flow");
3852     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3853     QTest::addColumn<QQuickItemView::VerticalLayoutDirection>("verticalLayoutDirection");
3854     QTest::addColumn<QPointF>("initialContentPos");
3855     QTest::addColumn<QPointF>("firstItemPos");
3856
3857     // Initial view width is 260, so in LeftToRight + right-to-left mode the
3858     // content x should be -20
3859
3860     QTest::newRow("LeftToRight, LtR, TtB")
3861             << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::TopToBottom
3862             << QPointF(0, 0)
3863             << QPointF(0, 0);
3864
3865     QTest::newRow("LeftToRight, RtL, TtB")
3866             << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::TopToBottom
3867             << QPointF(-20.0, 0)
3868             << QPointF(80.0 * 2, 0);
3869
3870     QTest::newRow("LeftToRight, LtR, BtT")
3871             << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << QQuickItemView::BottomToTop
3872             << QPointF(0, -320)
3873             << QPointF(0, -60.0);
3874
3875     QTest::newRow("LeftToRight, RtL, BtT")
3876             << QQuickGridView::FlowLeftToRight << Qt::RightToLeft << QQuickItemView::BottomToTop
3877             << QPointF(-20.0, -320)
3878             << QPointF(80.0 * 2, -60.0);
3879
3880
3881     QTest::newRow("TopToBottom, LtR, TtB")
3882             << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::TopToBottom
3883             << QPointF(0, 0)
3884             << QPointF(0, 0);
3885
3886     QTest::newRow("TopToBottom, RtL, TtB")
3887             << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::TopToBottom
3888             << QPointF(-260, 0)
3889             << QPointF(-80.0, 0);
3890
3891     QTest::newRow("TopToBottom, LtR, BtT")
3892             << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << QQuickItemView::BottomToTop
3893             << QPointF(0, -320)
3894             << QPointF(0, -60.0);
3895
3896     QTest::newRow("TopToBottom, RtL, BtT")
3897             << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << QQuickItemView::BottomToTop
3898             << QPointF(-260, -320)
3899             << QPointF(-80.0, -60.0);
3900 }
3901
3902
3903 void tst_QQuickGridView::changeColumnCount()
3904 {
3905     QmlListModel model;
3906     for (int i = 0; i < 40; i++)
3907         model.addItem("Item" + QString::number(i), "");
3908
3909     QQuickView *canvas = createView();
3910     QQmlContext *ctxt = canvas->rootContext();
3911     ctxt->setContextProperty("testModel", &model);
3912     ctxt->setContextProperty("initialWidth", 100);
3913     ctxt->setContextProperty("initialHeight", 320);
3914     canvas->setSource(testFileUrl("resizeview.qml"));
3915     canvas->show();
3916     qApp->processEvents();
3917
3918     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3919     QTRY_VERIFY(gridview != 0);
3920     QQuickItem *contentItem = gridview->contentItem();
3921     QTRY_VERIFY(contentItem != 0);
3922     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3923
3924     // a single column of 6 items are visible
3925     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3926     QCOMPARE(itemCount, 6);
3927     for (int i = 0; i < model.count() && i < itemCount; ++i) {
3928         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3929         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
3930         QCOMPARE(item->x(), 0.0);
3931         QCOMPARE(item->y(), qreal(i*60));
3932     }
3933
3934     // now 6x3 grid is visible, plus 1 extra below for refill
3935     gridview->setWidth(240);
3936     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3937     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3938     QCOMPARE(itemCount, 6*3 + 1);
3939     for (int i = 0; i < model.count() && i < itemCount; ++i) {
3940         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3941         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
3942         QCOMPARE(item->x(), qreal((i%3)*80));
3943         QCOMPARE(item->y(), qreal((i/3)*60));
3944     }
3945
3946     // back to single column
3947     gridview->setWidth(100);
3948     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
3949     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3950     QCOMPARE(itemCount, 6);
3951     for (int i = 0; i < model.count() && i < itemCount; ++i) {
3952         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3953         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
3954         QCOMPARE(item->x(), 0.0);
3955         QCOMPARE(item->y(), qreal(i*60));
3956     }
3957
3958     delete canvas;
3959 }
3960
3961 void tst_QQuickGridView::indexAt_itemAt_data()
3962 {
3963     QTest::addColumn<qreal>("x");
3964     QTest::addColumn<qreal>("y");
3965     QTest::addColumn<int>("index");
3966
3967     QTest::newRow("Item 0 - 0, 0") << 0. << 0. << 0;
3968     QTest::newRow("Item 0 - 79, 59") << 79. << 59. << 0;
3969     QTest::newRow("Item 1 - 80, 0") << 80. << 0. << 1;
3970     QTest::newRow("Item 3 - 0, 60") << 0. << 60. << 3;
3971     QTest::newRow("No Item - 240, 0") << 240. << 0. << -1;
3972 }
3973
3974 void tst_QQuickGridView::indexAt_itemAt()
3975 {
3976     QFETCH(qreal, x);
3977     QFETCH(qreal, y);
3978     QFETCH(int, index);
3979
3980     QQuickView *canvas = getView();
3981
3982     QaimModel model;
3983     model.addItem("Fred", "12345");
3984     model.addItem("John", "2345");
3985     model.addItem("Bob", "54321");
3986     model.addItem("Billy", "22345");
3987     model.addItem("Sam", "2945");
3988     model.addItem("Ben", "04321");
3989     model.addItem("Jim", "0780");
3990
3991     canvas->rootContext()->setContextProperty("testModel", &model);
3992     canvas->setSource(testFileUrl("gridview1.qml"));
3993     qApp->processEvents();
3994
3995     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
3996     QTRY_VERIFY(gridview != 0);
3997
3998     QQuickItem *contentItem = gridview->contentItem();
3999     QTRY_VERIFY(contentItem != 0);
4000
4001     QTRY_COMPARE(gridview->count(), model.count());
4002
4003     QQuickItem *item = 0;
4004     if (index >= 0) {
4005         item = findItem<QQuickItem>(contentItem, "wrapper", index);
4006         QVERIFY(item);
4007     }
4008     QCOMPARE(gridview->indexAt(x, y), index);
4009     QVERIFY(gridview->itemAt(x, y) == item);
4010
4011     releaseView(canvas);
4012 }
4013
4014 void tst_QQuickGridView::onAdd()
4015 {
4016     QFETCH(int, initialItemCount);
4017     QFETCH(int, itemsToAdd);
4018
4019     const int delegateWidth = 50;
4020     const int delegateHeight = 100;
4021     QaimModel model;
4022     QQuickView *canvas = getView();
4023     canvas->setGeometry(0,0,5 * delegateWidth, 5 * delegateHeight); // just ensure all items fit
4024
4025     // these initial items should not trigger GridView.onAdd
4026     for (int i=0; i<initialItemCount; i++)
4027         model.addItem("dummy value", "dummy value");
4028
4029     QQmlContext *ctxt = canvas->rootContext();
4030     ctxt->setContextProperty("testModel", &model);
4031     ctxt->setContextProperty("delegateWidth", delegateWidth);
4032     ctxt->setContextProperty("delegateHeight", delegateHeight);
4033     canvas->setSource(testFileUrl("attachedSignals.qml"));
4034
4035     QObject *object = canvas->rootObject();
4036     object->setProperty("width", canvas->width());
4037     object->setProperty("height", canvas->height());
4038     qApp->processEvents();
4039
4040     QList<QPair<QString, QString> > items;
4041     for (int i=0; i<itemsToAdd; i++)
4042         items << qMakePair(QString("value %1").arg(i), QString::number(i));
4043     model.addItems(items);
4044
4045     QTRY_COMPARE(model.count(), qobject_cast<QQuickGridView*>(canvas->rootObject())->count());
4046     qApp->processEvents();
4047
4048     QVariantList result = object->property("addedDelegates").toList();
4049     QTRY_COMPARE(result.count(), items.count());
4050     for (int i=0; i<items.count(); i++)
4051         QCOMPARE(result[i].toString(), items[i].first);
4052
4053     releaseView(canvas);
4054 }
4055
4056 void tst_QQuickGridView::onAdd_data()
4057 {
4058     QTest::addColumn<int>("initialItemCount");
4059     QTest::addColumn<int>("itemsToAdd");
4060
4061     QTest::newRow("0, add 1") << 0 << 1;
4062     QTest::newRow("0, add 2") << 0 << 2;
4063     QTest::newRow("0, add 10") << 0 << 10;
4064
4065     QTest::newRow("1, add 1") << 1 << 1;
4066     QTest::newRow("1, add 2") << 1 << 2;
4067     QTest::newRow("1, add 10") << 1 << 10;
4068
4069     QTest::newRow("5, add 1") << 5 << 1;
4070     QTest::newRow("5, add 2") << 5 << 2;
4071     QTest::newRow("5, add 10") << 5 << 10;
4072 }
4073
4074 void tst_QQuickGridView::onRemove()
4075 {
4076     QFETCH(int, initialItemCount);
4077     QFETCH(int, indexToRemove);
4078     QFETCH(int, removeCount);
4079
4080     const int delegateWidth = 50;
4081     const int delegateHeight = 100;
4082     QaimModel model;
4083     for (int i=0; i<initialItemCount; i++)
4084         model.addItem(QString("value %1").arg(i), "dummy value");
4085
4086     QQuickView *canvas = getView();
4087     QQmlContext *ctxt = canvas->rootContext();
4088     ctxt->setContextProperty("testModel", &model);
4089     ctxt->setContextProperty("delegateWidth", delegateWidth);
4090     ctxt->setContextProperty("delegateHeight", delegateHeight);
4091     canvas->setSource(testFileUrl("attachedSignals.qml"));
4092     QObject *object = canvas->rootObject();
4093
4094     model.removeItems(indexToRemove, removeCount);
4095     QTRY_COMPARE(model.count(), qobject_cast<QQuickGridView*>(canvas->rootObject())->count());
4096     QCOMPARE(object->property("removedDelegateCount"), QVariant(removeCount));
4097
4098     releaseView(canvas);
4099 }
4100
4101 void tst_QQuickGridView::onRemove_data()
4102 {
4103     QTest::addColumn<int>("initialItemCount");
4104     QTest::addColumn<int>("indexToRemove");
4105     QTest::addColumn<int>("removeCount");
4106
4107     QTest::newRow("remove first") << 1 << 0 << 1;
4108     QTest::newRow("two items, remove first") << 2 << 0 << 1;
4109     QTest::newRow("two items, remove last") << 2 << 1 << 1;
4110     QTest::newRow("two items, remove all") << 2 << 0 << 2;
4111
4112     QTest::newRow("four items, remove first") << 4 << 0 << 1;
4113     QTest::newRow("four items, remove 0-2") << 4 << 0 << 2;
4114     QTest::newRow("four items, remove 1-3") << 4 << 1 << 2;
4115     QTest::newRow("four items, remove 2-4") << 4 << 2 << 2;
4116     QTest::newRow("four items, remove last") << 4 << 3 << 1;
4117     QTest::newRow("four items, remove all") << 4 << 0 << 4;
4118
4119     QTest::newRow("ten items, remove 1-8") << 10 << 0 << 8;
4120     QTest::newRow("ten items, remove 2-7") << 10 << 2 << 5;
4121     QTest::newRow("ten items, remove 4-10") << 10 << 4 << 6;
4122 }
4123
4124 void tst_QQuickGridView::columnCount()
4125 {
4126     QQuickView canvas;
4127     canvas.setSource(testFileUrl("gridview4.qml"));
4128     canvas.show();
4129     canvas.requestActivateWindow();
4130     QTest::qWaitForWindowShown(&canvas);
4131
4132     QQuickGridView *view = qobject_cast<QQuickGridView*>(canvas.rootObject());
4133
4134     QCOMPARE(view->cellWidth(), qreal(405)/qreal(9));
4135     QCOMPARE(view->cellHeight(), qreal(100));
4136
4137     QList<QQuickItem*> items = findItems<QQuickItem>(view, "delegate");
4138     QCOMPARE(items.size(), 18);
4139     QCOMPARE(items.at(8)->y(), qreal(0));
4140     QCOMPARE(items.at(9)->y(), qreal(100));
4141 }
4142
4143 void tst_QQuickGridView::margins()
4144 {
4145     {
4146         QQuickView *canvas = createView();
4147
4148         QaimModel model;
4149         for (int i = 0; i < 40; i++)
4150             model.addItem("Item" + QString::number(i), "");
4151
4152         QQmlContext *ctxt = canvas->rootContext();
4153         ctxt->setContextProperty("testModel", &model);
4154         ctxt->setContextProperty("testRightToLeft", QVariant(false));
4155
4156         canvas->setSource(testFileUrl("margins.qml"));
4157         canvas->show();
4158         qApp->processEvents();
4159
4160         QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
4161         QTRY_VERIFY(gridview != 0);
4162         QQuickItem *contentItem = gridview->contentItem();
4163         QTRY_VERIFY(contentItem != 0);
4164         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4165
4166         QCOMPARE(gridview->contentX(), -30.);
4167         QCOMPARE(gridview->xOrigin(), 0.);
4168
4169         // check end bound
4170         gridview->positionViewAtEnd();
4171         qreal pos = gridview->contentX();
4172         gridview->setContentX(pos + 80);
4173         gridview->returnToBounds();
4174         QTRY_COMPARE(gridview->contentX(), pos + 50);
4175
4176         // remove item before visible and check that left margin is maintained
4177         // and xOrigin is updated
4178         gridview->setContentX(200);
4179         model.removeItems(0, 4);
4180         QTest::qWait(100);
4181         gridview->setContentX(-50);
4182         gridview->returnToBounds();
4183         QCOMPARE(gridview->xOrigin(), 100.);
4184         QTRY_COMPARE(gridview->contentX(), 70.);
4185
4186         // reduce left margin
4187         gridview->setLeftMargin(20);
4188         QCOMPARE(gridview->xOrigin(), 100.);
4189         QTRY_COMPARE(gridview->contentX(), 80.);
4190
4191         // check end bound
4192         gridview->positionViewAtEnd();
4193         QCOMPARE(gridview->xOrigin(), 0.); // positionViewAtEnd() resets origin
4194         pos = gridview->contentX();
4195         gridview->setContentX(pos + 80);
4196         gridview->returnToBounds();
4197         QTRY_COMPARE(gridview->contentX(), pos + 50);
4198
4199         // reduce right margin
4200         pos = gridview->contentX();
4201         gridview->setRightMargin(40);
4202         QCOMPARE(gridview->xOrigin(), 0.);
4203         QTRY_COMPARE(gridview->contentX(), pos-10);
4204
4205         delete canvas;
4206     }
4207     {
4208         //RTL
4209         QQuickView *canvas = createView();
4210         canvas->show();
4211
4212         QaimModel model;
4213         for (int i = 0; i < 40; i++)
4214             model.addItem("Item" + QString::number(i), "");
4215
4216         QQmlContext *ctxt = canvas->rootContext();
4217         ctxt->setContextProperty("testModel", &model);
4218         ctxt->setContextProperty("testRightToLeft", QVariant(true));
4219
4220         canvas->setSource(testFileUrl("margins.qml"));
4221         qApp->processEvents();
4222
4223         QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
4224         QTRY_VERIFY(gridview != 0);
4225
4226         QQuickItem *contentItem = gridview->contentItem();
4227         QTRY_VERIFY(contentItem != 0);
4228
4229         QTRY_COMPARE(gridview->contentX(), -240+50.);
4230         QTRY_COMPARE(gridview->xOrigin(), -100. * 10);
4231
4232         // check end bound
4233         gridview->positionViewAtEnd();
4234         qreal pos = gridview->contentX();
4235         gridview->setContentX(pos - 80);
4236         gridview->returnToBounds();
4237         QTRY_COMPARE(gridview->contentX(), pos - 30);
4238
4239         // remove item before visible and check that left margin is maintained
4240         // and xOrigin is updated
4241         gridview->setContentX(-400);
4242         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4243         model.removeItems(0, 4);
4244         QTRY_COMPARE(model.count(), gridview->count());
4245         gridview->setContentX(-240+50);
4246         gridview->returnToBounds();
4247         QCOMPARE(gridview->xOrigin(), -1000.);
4248         QTRY_COMPARE(gridview->contentX(), -240-50.);
4249
4250         // reduce right margin
4251         pos = gridview->contentX();
4252         gridview->setRightMargin(40);
4253         QCOMPARE(gridview->xOrigin(), -1000.);
4254         QTRY_COMPARE(gridview->contentX(), -240-100 + 40.);
4255
4256         // check end bound
4257         gridview->positionViewAtEnd();
4258         QCOMPARE(gridview->xOrigin(), -900.); // positionViewAtEnd() resets origin
4259         pos = gridview->contentX();
4260         gridview->setContentX(pos - 80);
4261         gridview->returnToBounds();
4262         QTRY_COMPARE(gridview->contentX(), pos - 30);
4263
4264         // reduce left margin
4265         pos = gridview->contentX();
4266         gridview->setLeftMargin(20);
4267         QCOMPARE(gridview->xOrigin(), -900.);
4268         QTRY_COMPARE(gridview->contentX(), pos+10);
4269
4270         delete canvas;
4271     }
4272 }
4273
4274 void tst_QQuickGridView::creationContext()
4275 {
4276     QQuickView canvas;
4277     canvas.setGeometry(0,0,240,320);
4278     canvas.setSource(testFileUrl("creationContext.qml"));
4279     qApp->processEvents();
4280
4281     QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
4282     QVERIFY(rootItem);
4283     QVERIFY(rootItem->property("count").toInt() > 0);
4284
4285     QQuickItem *item;
4286     QVERIFY(item = findItem<QQuickItem>(rootItem, "listItem"));
4287     QCOMPARE(item->property("text").toString(), QString("Hello!"));
4288     QVERIFY(item = rootItem->findChild<QQuickItem *>("header"));
4289     QCOMPARE(item->property("text").toString(), QString("Hello!"));
4290     QVERIFY(item = rootItem->findChild<QQuickItem *>("footer"));
4291     QCOMPARE(item->property("text").toString(), QString("Hello!"));
4292 }
4293
4294 void tst_QQuickGridView::snapToRow_data()
4295 {
4296     QTest::addColumn<QQuickGridView::Flow>("flow");
4297     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
4298     QTest::addColumn<int>("highlightRangeMode");
4299     QTest::addColumn<QPoint>("flickStart");
4300     QTest::addColumn<QPoint>("flickEnd");
4301     QTest::addColumn<qreal>("snapAlignment");
4302     QTest::addColumn<qreal>("endExtent");
4303     QTest::addColumn<qreal>("startExtent");
4304
4305     QTest::newRow("vertical, left to right") << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4306         << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 800.0 << 0.0;
4307
4308     QTest::newRow("horizontal, left to right") << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4309         << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 800.0 << 0.0;
4310
4311     QTest::newRow("horizontal, right to left") << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
4312         << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -800.0 - 240.0 << -240.0;
4313
4314     QTest::newRow("vertical, left to right, enforce range") << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4315         << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 940.0 << -20.0;
4316
4317     QTest::newRow("horizontal, left to right, enforce range") << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4318         << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 940.0 << -20.0;
4319
4320     QTest::newRow("horizontal, right to left, enforce range") << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
4321         << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -800.0 - 240.0 - 140.0 << -220.0;
4322 }
4323
4324 void tst_QQuickGridView::snapToRow()
4325 {
4326     QFETCH(QQuickGridView::Flow, flow);
4327     QFETCH(Qt::LayoutDirection, layoutDirection);
4328     QFETCH(int, highlightRangeMode);
4329     QFETCH(QPoint, flickStart);
4330     QFETCH(QPoint, flickEnd);
4331     QFETCH(qreal, snapAlignment);
4332     QFETCH(qreal, endExtent);
4333     QFETCH(qreal, startExtent);
4334
4335     QQuickView *canvas = getView();
4336
4337     canvas->setSource(testFileUrl("snapToRow.qml"));
4338     canvas->show();
4339     qApp->processEvents();
4340
4341     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
4342     QTRY_VERIFY(gridview != 0);
4343
4344     gridview->setFlow(flow);
4345     gridview->setLayoutDirection(layoutDirection);
4346     gridview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
4347     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4348
4349     QQuickItem *contentItem = gridview->contentItem();
4350     QTRY_VERIFY(contentItem != 0);
4351
4352     // confirm that a flick hits an item boundary
4353     flick(canvas, flickStart, flickEnd, 180);
4354     QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
4355     if (flow == QQuickGridView::FlowLeftToRight)
4356         QCOMPARE(qreal(fmod(gridview->contentY(),80.0)), snapAlignment);
4357     else
4358         QCOMPARE(qreal(fmod(gridview->contentX(),80.0)), snapAlignment);
4359
4360     // flick to end
4361     do {
4362         flick(canvas, flickStart, flickEnd, 180);
4363         QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
4364     } while (flow == QQuickGridView::FlowLeftToRight
4365            ? !gridview->isAtYEnd()
4366            : layoutDirection == Qt::LeftToRight ? !gridview->isAtXEnd() : !gridview->isAtXBeginning());
4367
4368     if (flow == QQuickGridView::FlowLeftToRight)
4369         QCOMPARE(gridview->contentY(), endExtent);
4370     else
4371         QCOMPARE(gridview->contentX(), endExtent);
4372
4373     // flick to start
4374     do {
4375         flick(canvas, flickEnd, flickStart, 180);
4376         QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
4377     } while (flow == QQuickGridView::FlowLeftToRight
4378            ? !gridview->isAtYBeginning()
4379            : layoutDirection == Qt::LeftToRight ? !gridview->isAtXBeginning() : !gridview->isAtXEnd());
4380
4381     if (flow == QQuickGridView::FlowLeftToRight)
4382         QCOMPARE(gridview->contentY(), startExtent);
4383     else
4384         QCOMPARE(gridview->contentX(), startExtent);
4385
4386     releaseView(canvas);
4387 }
4388
4389 void tst_QQuickGridView::snapOneRow_data()
4390 {
4391     QTest::addColumn<QQuickGridView::Flow>("flow");
4392     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
4393     QTest::addColumn<int>("highlightRangeMode");
4394     QTest::addColumn<QPoint>("flickStart");
4395     QTest::addColumn<QPoint>("flickEnd");
4396     QTest::addColumn<qreal>("snapAlignment");
4397     QTest::addColumn<qreal>("endExtent");
4398     QTest::addColumn<qreal>("startExtent");
4399
4400     QTest::newRow("vertical, left to right") << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4401         << QPoint(20, 200) << QPoint(20, 20) << 100.0 << 240.0 << 0.0;
4402
4403     QTest::newRow("horizontal, left to right") << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4404         << QPoint(200, 20) << QPoint(20, 20) << 100.0 << 240.0 << 0.0;
4405
4406     QTest::newRow("horizontal, right to left") << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
4407         << QPoint(20, 20) << QPoint(200, 20) << -340.0 << -240.0 - 240.0 << -240.0;
4408
4409     QTest::newRow("vertical, left to right, enforce range") << QQuickGridView::FlowLeftToRight << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4410         << QPoint(20, 200) << QPoint(20, 20) << 100.0 << 340.0 << -20.0;
4411
4412     QTest::newRow("horizontal, left to right, enforce range") << QQuickGridView::FlowTopToBottom << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4413         << QPoint(200, 20) << QPoint(20, 20) << 100.0 << 340.0 << -20.0;
4414
4415     QTest::newRow("horizontal, right to left, enforce range") << QQuickGridView::FlowTopToBottom << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
4416         << QPoint(20, 20) << QPoint(200, 20) << -340.0 << -240.0 - 240.0 - 100.0 << -220.0;
4417 }
4418
4419 void tst_QQuickGridView::snapOneRow()
4420 {
4421     QFETCH(QQuickGridView::Flow, flow);
4422     QFETCH(Qt::LayoutDirection, layoutDirection);
4423     QFETCH(int, highlightRangeMode);
4424     QFETCH(QPoint, flickStart);
4425     QFETCH(QPoint, flickEnd);
4426     QFETCH(qreal, snapAlignment);
4427     QFETCH(qreal, endExtent);
4428     QFETCH(qreal, startExtent);
4429
4430     QQuickView *canvas = getView();
4431
4432     canvas->setSource(testFileUrl("snapOneRow.qml"));
4433     canvas->show();
4434     qApp->processEvents();
4435
4436     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
4437     QTRY_VERIFY(gridview != 0);
4438
4439     gridview->setFlow(flow);
4440     gridview->setLayoutDirection(layoutDirection);
4441     gridview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
4442     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4443
4444     QQuickItem *contentItem = gridview->contentItem();
4445     QTRY_VERIFY(contentItem != 0);
4446
4447     QSignalSpy currentIndexSpy(gridview, SIGNAL(currentIndexChanged()));
4448
4449     // confirm that a flick hits next row boundary
4450     flick(canvas, flickStart, flickEnd, 180);
4451     QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
4452     if (flow == QQuickGridView::FlowLeftToRight)
4453         QCOMPARE(gridview->contentY(), snapAlignment);
4454     else
4455         QCOMPARE(gridview->contentX(), snapAlignment);
4456
4457     if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4458         QCOMPARE(gridview->currentIndex(), 2);
4459         QCOMPARE(currentIndexSpy.count(), 1);
4460     }
4461
4462     // flick to end
4463     do {
4464         flick(canvas, flickStart, flickEnd, 180);
4465         QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
4466     } while (flow == QQuickGridView::FlowLeftToRight
4467            ? !gridview->isAtYEnd()
4468            : layoutDirection == Qt::LeftToRight ? !gridview->isAtXEnd() : !gridview->isAtXBeginning());
4469
4470     if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4471         QCOMPARE(gridview->currentIndex(), 6);
4472         QCOMPARE(currentIndexSpy.count(), 3);
4473     }
4474
4475     if (flow == QQuickGridView::FlowLeftToRight)
4476         QCOMPARE(gridview->contentY(), endExtent);
4477     else
4478         QCOMPARE(gridview->contentX(), endExtent);
4479
4480     // flick to start
4481     do {
4482         flick(canvas, flickEnd, flickStart, 180);
4483         QTRY_VERIFY(gridview->isMoving() == false); // wait until it stops
4484     } while (flow == QQuickGridView::FlowLeftToRight
4485            ? !gridview->isAtYBeginning()
4486            : layoutDirection == Qt::LeftToRight ? !gridview->isAtXBeginning() : !gridview->isAtXEnd());
4487
4488     if (flow == QQuickGridView::FlowLeftToRight)
4489         QCOMPARE(gridview->contentY(), startExtent);
4490     else
4491         QCOMPARE(gridview->contentX(), startExtent);
4492
4493     if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4494         QCOMPARE(gridview->currentIndex(), 0);
4495         QCOMPARE(currentIndexSpy.count(), 6);
4496     }
4497
4498     releaseView(canvas);
4499 }
4500
4501
4502 void tst_QQuickGridView::unaligned()
4503 {
4504     QQuickView *canvas = createView();
4505     canvas->show();
4506
4507     QaimModel model;
4508     for (int i = 0; i < 10; i++)
4509         model.addItem("Item" + QString::number(i), "");
4510
4511     QQmlContext *ctxt = canvas->rootContext();
4512     ctxt->setContextProperty("testModel", &model);
4513
4514     canvas->setSource(testFileUrl("unaligned.qml"));
4515     qApp->processEvents();
4516
4517     QQuickGridView *gridview = qobject_cast<QQuickGridView*>(canvas->rootObject());
4518     QVERIFY(gridview != 0);
4519
4520     QQuickItem *contentItem = gridview->contentItem();
4521     QVERIFY(contentItem != 0);
4522
4523     for (int i = 0; i < 10; ++i) {
4524         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
4525         if (!item) qWarning() << "Item" << i << "not found";
4526         QVERIFY(item);
4527         QCOMPARE(item->x(), qreal((i%9)*gridview->cellWidth()));
4528         QCOMPARE(item->y(), qreal((i/9)*gridview->cellHeight()));
4529     }
4530
4531     // appending
4532     for (int i = 10; i < 18; ++i) {
4533         model.addItem("Item" + QString::number(i), "");
4534         QQuickItem *item = 0;
4535         QTRY_VERIFY(item = findItem<QQuickItem>(contentItem, "wrapper", i));
4536         QCOMPARE(item->x(), qreal((i%9)*gridview->cellWidth()));
4537         QCOMPARE(item->y(), qreal((i/9)*gridview->cellHeight()));
4538     }
4539
4540     // inserting
4541     for (int i = 0; i < 10; ++i) {
4542         model.insertItem(i, "Item" + QString::number(i), "");
4543         QQuickItem *item = 0;
4544         QTRY_VERIFY(item = findItem<QQuickItem>(contentItem, "wrapper", i));
4545         QCOMPARE(item->x(), qreal((i%9)*gridview->cellWidth()));
4546         QCOMPARE(item->y(), qreal((i/9)*gridview->cellHeight()));
4547     }
4548
4549     // removing
4550     model.removeItems(7, 10);
4551     QTRY_COMPARE(model.count(), gridview->count());
4552     for (int i = 0; i < 18; ++i) {
4553         QQuickItem *item = 0;
4554         QTRY_VERIFY(item = findItem<QQuickItem>(contentItem, "wrapper", i));
4555         QCOMPARE(item->x(), qreal(i%9)*gridview->cellWidth());
4556         QCOMPARE(item->y(), qreal(i/9)*gridview->cellHeight());
4557     }
4558
4559     delete canvas;
4560 }
4561
4562 void tst_QQuickGridView::populateTransitions()
4563 {
4564     QFETCH(bool, staticallyPopulate);
4565     QFETCH(bool, dynamicallyPopulate);
4566     QFETCH(bool, usePopulateTransition);
4567
4568     QPointF transitionFrom(-50, -50);
4569     QPointF transitionVia(100, 100);
4570     QaimModel model_transitionFrom;
4571     QaimModel model_transitionVia;
4572
4573     QaimModel model;
4574     if (staticallyPopulate) {
4575         for (int i = 0; i < 30; i++)
4576             model.addItem("item" + QString::number(i), "");
4577     }
4578
4579     QQuickView *canvas = getView();
4580     canvas->rootContext()->setContextProperty("testModel", &model);
4581     canvas->rootContext()->setContextProperty("usePopulateTransition", usePopulateTransition);
4582     canvas->rootContext()->setContextProperty("dynamicallyPopulate", dynamicallyPopulate);
4583     canvas->rootContext()->setContextProperty("transitionFrom", transitionFrom);
4584     canvas->rootContext()->setContextProperty("transitionVia", transitionVia);
4585     canvas->rootContext()->setContextProperty("model_transitionFrom", &model_transitionFrom);
4586     canvas->rootContext()->setContextProperty("model_transitionVia", &model_transitionVia);
4587     canvas->setSource(testFileUrl("populateTransitions.qml"));
4588     canvas->show();
4589
4590     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
4591     QVERIFY(gridview);
4592     QQuickItem *contentItem = gridview->contentItem();
4593     QVERIFY(contentItem);
4594
4595     // check the populate transition is run
4596     if (staticallyPopulate && usePopulateTransition) {
4597         QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), 18);
4598         QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 0);
4599     } else if (dynamicallyPopulate) {
4600         QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), 0);
4601         QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 18);
4602     } else {
4603         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4604         QCOMPARE(gridview->property("countPopulateTransitions").toInt(), 0);
4605         QCOMPARE(gridview->property("countAddTransitions").toInt(), 0);
4606     }
4607
4608     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
4609     for (int i=0; i < model.count() && i < itemCount; ++i) {
4610         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
4611         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
4612         QTRY_COMPARE(item->x(), (i%3)*80.0);
4613         QTRY_COMPARE(item->y(), (i/3)*60.0);
4614         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
4615         QVERIFY(name != 0);
4616         QTRY_COMPARE(name->text(), model.name(i));
4617     }
4618
4619     gridview->setProperty("countPopulateTransitions", 0);
4620     gridview->setProperty("countAddTransitions", 0);
4621
4622     // add an item and check this is done with add transition, not populate
4623     model.insertItem(0, "another item", "");
4624     QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 1);
4625     QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), 0);
4626
4627     // clear the model
4628     canvas->rootContext()->setContextProperty("testModel", QVariant());
4629     QTRY_COMPARE(gridview->count(), 0);
4630     QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper").count(), 0);
4631     gridview->setProperty("countPopulateTransitions", 0);
4632     gridview->setProperty("countAddTransitions", 0);
4633
4634     // set to a valid model and check populate transition is run a second time
4635     model.clear();
4636     for (int i = 0; i < 30; i++)
4637         model.addItem("item" + QString::number(i), "");
4638     canvas->rootContext()->setContextProperty("testModel", &model);
4639     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4640
4641     QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), usePopulateTransition ? 18 : 0);
4642     QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 0);
4643
4644     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
4645     for (int i=0; i < model.count() && i < itemCount; ++i) {
4646         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
4647         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
4648         QTRY_COMPARE(item->x(), (i%3)*80.0);
4649         QTRY_COMPARE(item->y(), (i/3)*60.0);
4650         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
4651         QVERIFY(name != 0);
4652         QTRY_COMPARE(name->text(), model.name(i));
4653     }
4654
4655     // reset model and check populate transition is run again
4656     gridview->setProperty("countPopulateTransitions", 0);
4657     gridview->setProperty("countAddTransitions", 0);
4658     model.reset();
4659     QTRY_COMPARE(gridview->property("countPopulateTransitions").toInt(), usePopulateTransition ? 18 : 0);
4660     QTRY_COMPARE(gridview->property("countAddTransitions").toInt(), 0);
4661
4662     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
4663     for (int i=0; i < model.count() && i < itemCount; ++i) {
4664         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
4665         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
4666         QTRY_COMPARE(item->x(), (i%3)*80.0);
4667         QTRY_COMPARE(item->y(), (i/3)*60.0);
4668         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
4669         QVERIFY(name != 0);
4670         QTRY_COMPARE(name->text(), model.name(i));
4671     }
4672
4673     releaseView(canvas);
4674 }
4675
4676 void tst_QQuickGridView::populateTransitions_data()
4677 {
4678     QTest::addColumn<bool>("staticallyPopulate");
4679     QTest::addColumn<bool>("dynamicallyPopulate");
4680     QTest::addColumn<bool>("usePopulateTransition");
4681
4682     QTest::newRow("static") << true << false << true;
4683     QTest::newRow("static, no populate") << true << false << false;
4684
4685     QTest::newRow("dynamic") << false << true << true;
4686     QTest::newRow("dynamic, no populate") << false << true << false;
4687
4688     QTest::newRow("empty to start with") << false << false << true;
4689     QTest::newRow("empty to start with, no populate") << false << false << false;
4690 }
4691
4692 void tst_QQuickGridView::addTransitions()
4693 {
4694     QFETCH(int, initialItemCount);
4695     QFETCH(bool, shouldAnimateTargets);
4696     QFETCH(qreal, contentYRowOffset);
4697     QFETCH(int, insertionIndex);
4698     QFETCH(int, insertionCount);
4699     QFETCH(ListRange, expectedDisplacedIndexes);
4700
4701     // added items should start here
4702     QPointF targetItems_transitionFrom(-50, -50);
4703
4704     // displaced items should pass through this point
4705     QPointF displacedItems_transitionVia(100, 100);
4706
4707     QaimModel model;
4708     for (int i = 0; i < initialItemCount; i++)
4709         model.addItem("Original item" + QString::number(i), "");
4710     QaimModel model_targetItems_transitionFrom;
4711     QaimModel model_displacedItems_transitionVia;
4712
4713     QQuickView *canvas = getView();
4714     QQmlContext *ctxt = canvas->rootContext();
4715     ctxt->setContextProperty("testModel", &model);
4716     ctxt->setContextProperty("model_targetItems_transitionFrom", &model_targetItems_transitionFrom);
4717     ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
4718     ctxt->setContextProperty("targetItems_transitionFrom", targetItems_transitionFrom);
4719     ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
4720     canvas->setSource(testFileUrl("addTransitions.qml"));
4721     canvas->show();
4722
4723     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
4724     QTRY_VERIFY(gridview != 0);
4725     QQuickItem *contentItem = gridview->contentItem();
4726     QVERIFY(contentItem != 0);
4727     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4728
4729     if (contentYRowOffset != 0) {
4730         gridview->setContentY(contentYRowOffset * 60.0);
4731         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4732     }
4733
4734     QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
4735
4736     // only target items that will become visible should be animated
4737     QList<QPair<QString, QString> > newData;
4738     QList<QPair<QString, QString> > expectedTargetData;
4739     QList<int> targetIndexes;
4740     if (shouldAnimateTargets) {
4741         for (int i=insertionIndex; i<insertionIndex+insertionCount; i++) {
4742             newData << qMakePair(QString("New item %1").arg(i), QString(""));
4743
4744             // last visible item is the first item of the row beneath the view
4745             if (i >= (gridview->contentY() / 60)*3 && i < qCeil((gridview->contentY() + gridview->height()) / 60.0)*3) {
4746                 expectedTargetData << newData.last();
4747                 targetIndexes << i;
4748             }
4749         }
4750         QVERIFY(expectedTargetData.count() > 0);
4751     }
4752
4753     // start animation
4754     if (!newData.isEmpty()) {
4755         model.insertItems(insertionIndex, newData);
4756         QTRY_COMPARE(model.count(), gridview->count());
4757     }
4758
4759     QList<QQuickItem *> targetItems = findItems<QQuickItem>(contentItem, "wrapper", targetIndexes);
4760
4761     if (shouldAnimateTargets) {
4762         QTRY_COMPARE(gridview->property("targetTransitionsDone").toInt(), expectedTargetData.count());
4763         QTRY_COMPARE(gridview->property("displaceTransitionsDone").toInt(),
4764                      expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0);
4765
4766         // check the target and displaced items were animated
4767         model_targetItems_transitionFrom.matchAgainst(expectedTargetData, "wasn't animated from target 'from' pos", "shouldn't have been animated from target 'from' pos");
4768         model_displacedItems_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with displaced anim", "shouldn't have been animated with displaced anim");
4769
4770         // check attached properties
4771         matchItemsAndIndexes(gridview->property("targetTrans_items").toMap(), model, targetIndexes);
4772         matchIndexLists(gridview->property("targetTrans_targetIndexes").toList(), targetIndexes);
4773         matchItemLists(gridview->property("targetTrans_targetItems").toList(), targetItems);
4774         if (expectedDisplacedIndexes.isValid()) {
4775             // adjust expectedDisplacedIndexes to their final values after the move
4776             QList<int> displacedIndexes = adjustIndexesForAddDisplaced(expectedDisplacedIndexes.indexes, insertionIndex, insertionCount);
4777             matchItemsAndIndexes(gridview->property("displacedTrans_items").toMap(), model, displacedIndexes);
4778             matchIndexLists(gridview->property("displacedTrans_targetIndexes").toList(), targetIndexes);
4779             matchItemLists(gridview->property("displacedTrans_targetItems").toList(), targetItems);
4780         }
4781     } else {
4782         QTRY_COMPARE(model_targetItems_transitionFrom.count(), 0);
4783         QTRY_COMPARE(model_displacedItems_transitionVia.count(), 0);
4784     }
4785
4786     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
4787     int firstVisibleIndex = -1;
4788     for (int i=0; i<items.count(); i++) {
4789         if (items[i]->y() >= gridview->contentY()) {
4790             QQmlExpression e(qmlContext(items[i]), items[i], "index");
4791             firstVisibleIndex = e.evaluate().toInt();
4792             break;
4793         }
4794     }
4795     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
4796
4797     // verify all items moved to the correct final positions
4798     for (int i = firstVisibleIndex; i < model.count() && i < items.count(); ++i) {
4799         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
4800         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
4801         QCOMPARE(item->x(), (i%3)*80.0);
4802         QCOMPARE(item->y(), (i/3)*60.0);
4803         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
4804         QVERIFY(name != 0);
4805         QCOMPARE(name->text(), model.name(i));
4806     }
4807
4808     releaseView(canvas);
4809 }
4810
4811 void tst_QQuickGridView::addTransitions_data()
4812 {
4813     QTest::addColumn<int>("initialItemCount");
4814     QTest::addColumn<qreal>("contentYRowOffset");
4815     QTest::addColumn<bool>("shouldAnimateTargets");
4816     QTest::addColumn<int>("insertionIndex");
4817     QTest::addColumn<int>("insertionCount");
4818     QTest::addColumn<ListRange>("expectedDisplacedIndexes");
4819
4820     // if inserting a full row before visible index, items don't appear or animate in, even if there are > 1 new items
4821     QTest::newRow("insert 1, just before start")
4822             << 30 << 1.0 << false
4823             << 0 << 1 << ListRange();
4824     QTest::newRow("insert 1, way before start")
4825             << 30 << 1.0 << false
4826             << 0 << 1 << ListRange();
4827     QTest::newRow("insert multiple, just before start")
4828             << 30 << 1.0 << false
4829             << 0 << 3 << ListRange();
4830     QTest::newRow("insert multiple (< 1 row), just before start")
4831             << 30 << 1.0 << false
4832             << 0 << 2 << ListRange();
4833     QTest::newRow("insert multiple, way before start")
4834             << 30 << 3.0 << false
4835             << 0 << 3 << ListRange();
4836
4837     QTest::newRow("insert 1 at start")
4838             << 30 << 0.0 << true
4839             << 0 << 1 << ListRange(0, 17);
4840     QTest::newRow("insert multiple at start")
4841             << 30 << 0.0 << true
4842             << 0 << 3 << ListRange(0, 17);
4843     QTest::newRow("insert multiple (> 1 row) at start")
4844             << 30 << 0.0 << true
4845             << 0 << 5 << ListRange(0, 17);
4846     QTest::newRow("insert 1 at start, content y not 0")
4847             << 30 << 1.0 << true  // first visible is index 3
4848             << 3 << 1 << ListRange(0 + 3, 17 + 3);
4849     QTest::newRow("insert multiple at start, content y not 0")
4850             << 30 << 1.0 << true    // first visible is index 3
4851             << 3 << 3 << ListRange(0 + 3, 17 + 3);
4852     QTest::newRow("insert multiple (> 1 row) at start, content y not 0")
4853             << 30 << 1.0 << true    // first visible is index 3
4854             << 3 << 5 << ListRange(0 + 3, 17 + 3);
4855
4856     QTest::newRow("insert 1 at start, to empty grid")
4857             << 0 << 0.0 << true
4858             << 0 << 1 << ListRange();
4859     QTest::newRow("insert multiple at start, to empty grid")
4860             << 0 << 0.0 << true
4861             << 0 << 3 << ListRange();
4862
4863     QTest::newRow("insert 1 at middle")
4864             << 30 << 0.0 << true
4865             << 7 << 1 << ListRange(7, 17);
4866     QTest::newRow("insert multiple at middle")
4867             << 30 << 0.0 << true
4868             << 7 << 3 << ListRange(7, 17);
4869     QTest::newRow("insert multiple (> 1 row) at middle")
4870             << 30 << 0.0 << true
4871             << 7 << 5 << ListRange(7, 17);
4872
4873     QTest::newRow("insert 1 at bottom")
4874             << 30 << 0.0 << true
4875             << 17 << 1 << ListRange(17, 17);
4876     QTest::newRow("insert multiple at bottom")
4877             << 30 << 0.0 << true
4878             << 17 << 3 << ListRange(17, 17);
4879     QTest::newRow("insert 1 at bottom, content y not 0")
4880             << 30 << 1.0 << true
4881             << 17 + 3 << 1 << ListRange(17 + 3, 17 + 3);
4882     QTest::newRow("insert multiple at bottom, content y not 0")
4883             << 30 << 1.0 << true
4884             << 17 + 3 << 3 << ListRange(17 + 3, 17 + 3);
4885
4886
4887     // items added after the last visible will not be animated in, since they
4888     // do not appear in the final view
4889     QTest::newRow("insert 1 after end")
4890             << 30 << 0.0 << false
4891             << 18 << 1 << ListRange();
4892     QTest::newRow("insert multiple after end")
4893             << 30 << 0.0 << false
4894             << 18 << 3 << ListRange();
4895 }
4896
4897 void tst_QQuickGridView::moveTransitions()
4898 {
4899     QFETCH(int, initialItemCount);
4900     QFETCH(qreal, contentYRowOffset);
4901     QFETCH(qreal, rowOffsetAfterMove);
4902     QFETCH(int, moveFrom);
4903     QFETCH(int, moveTo);
4904     QFETCH(int, moveCount);
4905     QFETCH(ListRange, expectedDisplacedIndexes);
4906
4907     // target and displaced items should pass through these points
4908     QPointF targetItems_transitionVia(-50, 50);
4909     QPointF displacedItems_transitionVia(100, 100);
4910
4911     QaimModel model;
4912     for (int i = 0; i < initialItemCount; i++)
4913         model.addItem("Original item" + QString::number(i), "");
4914     QaimModel model_targetItems_transitionVia;
4915     QaimModel model_displacedItems_transitionVia;
4916
4917     QQuickView *canvas = getView();
4918     QQmlContext *ctxt = canvas->rootContext();
4919     ctxt->setContextProperty("testModel", &model);
4920     ctxt->setContextProperty("model_targetItems_transitionVia", &model_targetItems_transitionVia);
4921     ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
4922     ctxt->setContextProperty("targetItems_transitionVia", targetItems_transitionVia);
4923     ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
4924     canvas->setSource(testFileUrl("moveTransitions.qml"));
4925     canvas->show();
4926
4927     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
4928     QTRY_VERIFY(gridview != 0);
4929     QQuickItem *contentItem = gridview->contentItem();
4930     QVERIFY(contentItem != 0);
4931     QQuickText *name;
4932
4933     if (contentYRowOffset != 0) {
4934         gridview->setContentY(contentYRowOffset * 60.0);
4935         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
4936     }
4937
4938     QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
4939
4940     // Items moving to *or* from visible positions should be animated.
4941     // Otherwise, they should not be animated.
4942     QList<QPair<QString, QString> > expectedTargetData;
4943     QList<int> targetIndexes;
4944     for (int i=moveFrom; i<moveFrom+moveCount; i++) {
4945         int toIndex = moveTo + (i - moveFrom);
4946         int firstVisibleIndex = (gridview->contentY() / 60) * 3;
4947         int lastVisibleIndex = (qCeil((gridview->contentY() + gridview->height()) / 60.0)*3) - 1;
4948         if ((i >= firstVisibleIndex && i <= lastVisibleIndex)
4949                 || (toIndex >= firstVisibleIndex && toIndex <= lastVisibleIndex)) {
4950             expectedTargetData << qMakePair(model.name(i), model.number(i));
4951             targetIndexes << i;
4952         }
4953     }
4954     // ViewTransition.index provides the indices that items are moving to, not from
4955     targetIndexes = adjustIndexesForMove(targetIndexes, moveFrom, moveTo, moveCount);
4956
4957     // start animation
4958     model.moveItems(moveFrom, moveTo, moveCount);
4959
4960     QTRY_COMPARE(gridview->property("targetTransitionsDone").toInt(), expectedTargetData.count());
4961     QTRY_COMPARE(gridview->property("displaceTransitionsDone").toInt(),
4962                  expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0);
4963
4964     QList<QQuickItem *> targetItems = findItems<QQuickItem>(contentItem, "wrapper", targetIndexes);
4965
4966     // check the target and displaced items were animated
4967     model_targetItems_transitionVia.matchAgainst(expectedTargetData, "wasn't animated from target 'from' pos", "shouldn't have been animated from target 'from' pos");
4968     model_displacedItems_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with displaced anim", "shouldn't have been animated with displaced anim");
4969
4970     // check attached properties
4971     matchItemsAndIndexes(gridview->property("targetTrans_items").toMap(), model, targetIndexes);
4972     matchIndexLists(gridview->property("targetTrans_targetIndexes").toList(), targetIndexes);
4973     matchItemLists(gridview->property("targetTrans_targetItems").toList(), targetItems);
4974     if (expectedDisplacedIndexes.isValid()) {
4975         // adjust expectedDisplacedIndexes to their final values after the move
4976         QList<int> displacedIndexes = adjustIndexesForMove(expectedDisplacedIndexes.indexes, moveFrom, moveTo, moveCount);
4977         matchItemsAndIndexes(gridview->property("displacedTrans_items").toMap(), model, displacedIndexes);
4978         matchIndexLists(gridview->property("displacedTrans_targetIndexes").toList(), targetIndexes);
4979         matchItemLists(gridview->property("displacedTrans_targetItems").toList(), targetItems);
4980     }
4981
4982     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
4983     int firstVisibleIndex = -1;
4984     for (int i=0; i<items.count(); i++) {
4985         if (items[i]->y() >= gridview->contentY()) {
4986             QQmlExpression e(qmlContext(items[i]), items[i], "index");
4987             firstVisibleIndex = e.evaluate().toInt();
4988             break;
4989         }
4990     }
4991     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
4992
4993     // verify all items moved to the correct final positions
4994     qreal pixelOffset = 60 * rowOffsetAfterMove;
4995     for (int i=firstVisibleIndex; i < model.count() && i < items.count(); ++i) {
4996         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
4997         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
4998         QCOMPARE(item->x(), (i%3)*80.0);
4999         QCOMPARE(item->y(), (i/3)*60.0 + pixelOffset);
5000         name = findItem<QQuickText>(contentItem, "textName", i);
5001         QVERIFY(name != 0);
5002         QTRY_COMPARE(name->text(), model.name(i));
5003     }
5004
5005     releaseView(canvas);
5006 }
5007
5008 void tst_QQuickGridView::moveTransitions_data()
5009 {
5010     QTest::addColumn<int>("initialItemCount");
5011     QTest::addColumn<qreal>("contentYRowOffset");
5012     QTest::addColumn<qreal>("rowOffsetAfterMove");
5013     QTest::addColumn<int>("moveFrom");
5014     QTest::addColumn<int>("moveTo");
5015     QTest::addColumn<int>("moveCount");
5016     QTest::addColumn<ListRange>("expectedDisplacedIndexes");
5017
5018     QTest::newRow("move from above view, outside visible items, move 1")
5019             << 30 << 2.0 << 0.0
5020             << 1 << 10 << 1 << ListRange(6, 10);
5021     QTest::newRow("move from above view, outside visible items, move 1 (first item)")
5022             << 30 << 2.0 << 0.0
5023             << 0 << 10 << 1 << ListRange(6, 10);
5024     QTest::newRow("move from above view, outside visible items, move multiple")
5025             << 30 << 2.0 << 1.0
5026             << 1 << 10 << 3 << ListRange(13, 23);
5027     QTest::newRow("move from above view, mix of visible/non-visible")
5028             << 30 << 2.0 << 1.0
5029             << 1 << 10 << 6 << (ListRange(7, 15) + ListRange(16, 23));
5030     QTest::newRow("move from above view, mix of visible/non-visible (move first)")
5031             << 30 << 2.0 << 2.0
5032             << 0 << 10 << 6 << ListRange(16, 23);
5033
5034     QTest::newRow("move within view, move 1 down")
5035             << 30 << 0.0 << 0.0
5036             << 1 << 10 << 1 << ListRange(2, 10);
5037     QTest::newRow("move within view, move 1 down, move first item")
5038             << 30 << 0.0 << 0.0
5039             << 0 << 10 << 1 << ListRange(1, 10);
5040     QTest::newRow("move within view, move 1 down, move first item, contentY not 0")
5041             << 30 << 2.0 << 0.0
5042             << 0+6 << 10+6 << 1 << ListRange(1+6, 10+6);
5043     QTest::newRow("move within view, move 1 down, to last item")
5044             << 30 << 0.0 << 0.0
5045             << 10 << 17 << 1 << ListRange(11, 17);
5046     QTest::newRow("move within view, move first->last")
5047             << 30 << 0.0 << 0.0
5048             << 0 << 17 << 1 << ListRange(1, 17);
5049
5050     QTest::newRow("move within view, move multiple down")
5051             << 30 << 0.0 << 0.0
5052             << 1 << 10 << 3 << ListRange(4, 12);
5053     QTest::newRow("move within view, move multiple down, move first item")
5054             << 30 << 0.0 << 0.0
5055             << 0 << 10 << 3 << ListRange(3, 12);
5056     QTest::newRow("move within view, move multiple down, move first item, contentY not 0")
5057             << 30 << 1.0 << 0.0
5058             << 0+3 << 10+3 << 3 << ListRange(3+3, 12+3);
5059     QTest::newRow("move within view, move multiple down, displace last item")
5060             << 30 << 0.0 << 0.0
5061             << 5 << 15 << 3 << ListRange(8, 17);
5062     QTest::newRow("move within view, move multiple down, move first->last")
5063             << 30 << 0.0 << 0.0
5064             << 0 << 15 << 3 << ListRange(3, 17);
5065
5066     QTest::newRow("move within view, move 1 up")
5067             << 30 << 0.0 << 0.0
5068             << 10 << 1 << 1 << ListRange(1, 9);
5069     QTest::newRow("move within view, move 1 up, move to first index")
5070             << 30 << 0.0 << 0.0
5071             << 10 << 0 << 1 << ListRange(0, 9);
5072     QTest::newRow("move within view, move 1 up, move to first index, contentY not 0")
5073             << 30 << 2.0 << 0.0
5074             << 10+6 << 0+6 << 1 << ListRange(0+6, 9+6);
5075     QTest::newRow("move within view, move 1 up, move to first index, contentY not on item border")
5076             << 30 << 1.5 << 0.0
5077             << 10+3 << 0+3 << 1 << ListRange(0+3, 9+3);
5078     QTest::newRow("move within view, move 1 up, move last item")
5079             << 30 << 0.0 << 0.0
5080             << 17 << 10 << 1 << ListRange(10, 16);
5081     QTest::newRow("move within view, move 1 up, move last->first")
5082             << 30 << 0.0 << 0.0
5083             << 17 << 0 << 1 << ListRange(0, 16);
5084
5085     QTest::newRow("move within view, move multiple up")
5086             << 30 << 0.0 << 0.0
5087             << 10 << 1 << 3 << ListRange(1, 9);
5088     QTest::newRow("move within view, move multiple (> 1 row) up")
5089             << 30 << 0.0 << 0.0
5090             << 10 << 1 << 5 << ListRange(1, 9);
5091     QTest::newRow("move within view, move multiple up, move to first index")
5092             << 30 << 0.0 << 0.0
5093             << 10 << 0 << 3 << ListRange(0, 9);
5094     QTest::newRow("move within view, move multiple up, move to first index, contentY not 0")
5095             << 30 << 1.0 << 0.0
5096             << 10+3 << 0+3 << 3 << ListRange(0+3, 9+3);
5097     QTest::newRow("move within view, move multiple up (> 1 row), move to first index, contentY not on border")
5098             << 30 << 1.5 << 0.0
5099             << 10+3 << 0+3 << 5 << ListRange(0+3, 9+3);
5100     QTest::newRow("move within view, move multiple up, move last item")
5101             << 30 << 0.0 << 0.0
5102             << 15 << 5 << 3 << ListRange(5, 14);
5103     QTest::newRow("move within view, move multiple up, move last->first")
5104             << 30 << 0.0 << 0.0
5105             << 15 << 0 << 3 << ListRange(0, 14);
5106
5107     QTest::newRow("move from below view, move 1 up")
5108             << 30 << 0.0 << 0.0
5109             << 20 << 5 << 1 << ListRange(5, 17);
5110     QTest::newRow("move from below view, move 1 up, move to top")
5111             << 30 << 0.0 << 0.0
5112             << 20 << 0 << 1 << ListRange(0, 17);
5113     QTest::newRow("move from below view, move 1 up, move to top, contentY not 0")
5114             << 30 << 1.0 << 0.0
5115             << 25 << 3 << 1 << ListRange(0+3, 17+3);
5116     QTest::newRow("move from below view, move multiple (> 1 row) up")
5117             << 30 << 0.0 << 0.0
5118             << 20 << 5 << 5 << ListRange(5, 17);
5119     QTest::newRow("move from below view, move multiple up, move to top")
5120             << 30 << 0.0 << 0.0
5121             << 20 << 0 << 3 << ListRange(0, 17);
5122     QTest::newRow("move from below view, move multiple up, move to top, contentY not 0")
5123             << 30 << 1.0 << 0.0
5124             << 25 << 3 << 3 << ListRange(0+3, 17+3);
5125
5126     QTest::newRow("move from below view, move 1 up, move to bottom")
5127             << 30 << 0.0 << 0.0
5128             << 20 << 17 << 1 << ListRange(17, 17);
5129     QTest::newRow("move from below view, move 1 up, move to bottom, contentY not 0")
5130             << 30 << 1.0 << 0.0
5131             << 25 << 17+3 << 1 << ListRange(17+3, 17+3);
5132     QTest::newRow("move from below view, move multiple up, move to to bottom")
5133             << 30 << 0.0 << 0.0
5134             << 20 << 17 << 3 << ListRange(17, 17);
5135     QTest::newRow("move from below view, move multiple up, move to bottom, contentY not 0")
5136             << 30 << 1.0 << 0.0
5137             << 25 << 17+3 << 3 << ListRange(17+3, 17+3);
5138 }
5139
5140 void tst_QQuickGridView::removeTransitions()
5141 {
5142     QFETCH(int, initialItemCount);
5143     QFETCH(bool, shouldAnimateTargets);
5144     QFETCH(qreal, contentYRowOffset);
5145     QFETCH(int, removalIndex);
5146     QFETCH(int, removalCount);
5147     QFETCH(ListRange, expectedDisplacedIndexes);
5148
5149     // added items should end here
5150     QPointF targetItems_transitionTo(-50, -50);
5151
5152     // displaced items should pass through this points
5153     QPointF displacedItems_transitionVia(100, 100);
5154
5155     QaimModel model;
5156     for (int i = 0; i < initialItemCount; i++)
5157         model.addItem("Original item" + QString::number(i), "");
5158     QaimModel model_targetItems_transitionTo;
5159     QaimModel model_displacedItems_transitionVia;
5160
5161     QQuickView *canvas = getView();
5162     QQmlContext *ctxt = canvas->rootContext();
5163     ctxt->setContextProperty("testModel", &model);
5164     ctxt->setContextProperty("model_targetItems_transitionTo", &model_targetItems_transitionTo);
5165     ctxt->setContextProperty("model_displacedItems_transitionVia", &model_displacedItems_transitionVia);
5166     ctxt->setContextProperty("targetItems_transitionTo", targetItems_transitionTo);
5167     ctxt->setContextProperty("displacedItems_transitionVia", displacedItems_transitionVia);
5168     canvas->setSource(testFileUrl("removeTransitions.qml"));
5169     canvas->show();
5170
5171     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
5172     QTRY_VERIFY(gridview != 0);
5173     QQuickItem *contentItem = gridview->contentItem();
5174     QVERIFY(contentItem != 0);
5175     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
5176
5177     if (contentYRowOffset != 0) {
5178         gridview->setContentY(contentYRowOffset * 60.0);
5179         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
5180     }
5181
5182     QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
5183
5184     // only target items that are visible should be animated
5185     QList<QPair<QString, QString> > expectedTargetData;
5186     QList<int> targetIndexes;
5187     if (shouldAnimateTargets) {
5188         for (int i=removalIndex; i<removalIndex+removalCount; i++) {
5189             int firstVisibleIndex = (gridview->contentY() / 60.0)*3;
5190             int lastVisibleIndex = (qCeil((gridview->contentY() + gridview->height()) / 60.0)*3) - 1;
5191             if (i >= firstVisibleIndex && i <= lastVisibleIndex) {
5192                 expectedTargetData << qMakePair(model.name(i), model.number(i));
5193                 targetIndexes << i;
5194             }
5195         }
5196         QVERIFY(expectedTargetData.count() > 0);
5197     }
5198
5199     // calculate targetItems and expectedTargets before model changes
5200     QList<QQuickItem *> targetItems = findItems<QQuickItem>(contentItem, "wrapper", targetIndexes);
5201     QVariantMap expectedTargets;
5202     for (int i=0; i<targetIndexes.count(); i++)
5203         expectedTargets[model.name(targetIndexes[i])] = targetIndexes[i];
5204
5205     // start animation
5206     model.removeItems(removalIndex, removalCount);
5207     QTRY_COMPARE(model.count(), gridview->count());
5208
5209     if (shouldAnimateTargets || expectedDisplacedIndexes.isValid()) {
5210         QTRY_COMPARE(gridview->property("targetTransitionsDone").toInt(), expectedTargetData.count());
5211         QTRY_COMPARE(gridview->property("displaceTransitionsDone").toInt(),
5212                      expectedDisplacedIndexes.isValid() ? expectedDisplacedIndexes.count() : 0);
5213
5214         // check the target and displaced items were animated
5215         model_targetItems_transitionTo.matchAgainst(expectedTargetData, "wasn't animated to target 'to' pos", "shouldn't have been animated to target 'to' pos");
5216         model_displacedItems_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with displaced anim", "shouldn't have been animated with displaced anim");
5217
5218         // check attached properties
5219         QCOMPARE(gridview->property("targetTrans_items").toMap(), expectedTargets);
5220         matchIndexLists(gridview->property("targetTrans_targetIndexes").toList(), targetIndexes);
5221         matchItemLists(gridview->property("targetTrans_targetItems").toList(), targetItems);
5222         if (expectedDisplacedIndexes.isValid()) {
5223             // adjust expectedDisplacedIndexes to their final values after the move
5224             QList<int> displacedIndexes = adjustIndexesForRemoveDisplaced(expectedDisplacedIndexes.indexes, removalIndex, removalCount);
5225             matchItemsAndIndexes(gridview->property("displacedTrans_items").toMap(), model, displacedIndexes);
5226             matchIndexLists(gridview->property("displacedTrans_targetIndexes").toList(), targetIndexes);
5227             matchItemLists(gridview->property("displacedTrans_targetItems").toList(), targetItems);
5228         }
5229     } else {
5230         QTRY_COMPARE(model_targetItems_transitionTo.count(), 0);
5231         QTRY_COMPARE(model_displacedItems_transitionVia.count(), 0);
5232     }
5233
5234     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
5235     int itemCount = items.count();
5236     int firstVisibleIndex = -1;
5237     for (int i=0; i<items.count(); i++) {
5238         QQmlExpression e(qmlContext(items[i]), items[i], "index");
5239         int index = e.evaluate().toInt();
5240         if (firstVisibleIndex < 0 && items[i]->y() >= gridview->contentY())
5241             firstVisibleIndex = index;
5242         else if (index < 0)
5243             itemCount--;    // exclude deleted items
5244     }
5245     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
5246
5247     // verify all items moved to the correct final positions
5248     for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
5249         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
5250         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
5251         QCOMPARE(item->x(), (i%3)*80.0);
5252         QCOMPARE(item->y(), gridview->contentY() + ((i-firstVisibleIndex)/3) * 60.0);
5253         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
5254         QVERIFY(name != 0);
5255         QTRY_COMPARE(name->text(), model.name(i));
5256     }
5257
5258     releaseView(canvas);
5259 }
5260
5261 void tst_QQuickGridView::removeTransitions_data()
5262 {
5263     QTest::addColumn<int>("initialItemCount");
5264     QTest::addColumn<qreal>("contentYRowOffset");
5265     QTest::addColumn<bool>("shouldAnimateTargets");
5266     QTest::addColumn<int>("removalIndex");
5267     QTest::addColumn<int>("removalCount");
5268     QTest::addColumn<ListRange>("expectedDisplacedIndexes");
5269
5270     // All items that are visible following the remove operation should be animated.
5271     // Remove targets that are outside of the view should not be animated.
5272
5273     // For a GridView, removing any number of items other than a full row before the start
5274     // should displace all items in the view
5275     QTest::newRow("remove 1 before start")
5276             << 30 << 2.0 << false
5277             << 2 << 1 << ListRange(6, 24);    // 6-24 are displaced
5278     QTest::newRow("remove 1 row, before start")
5279             << 30 << 2.0 << false
5280             << 3 << 3 << ListRange();
5281     QTest::newRow("remove between 1-2 rows, before start")
5282             << 30 << 2.0 << false
5283             << 0 << 5 << ListRange(6, 25);
5284     QTest::newRow("remove 2 rows, before start")
5285             << 30 << 2.0 << false
5286             << 0 << 6 << ListRange();
5287     QTest::newRow("remove mix of before and after start")
5288             << 30 << 1.0 << true
5289             << 2 << 3 << ListRange(5, 23);  // 5-23 are displaced into view
5290
5291
5292     QTest::newRow("remove 1 from start")
5293             << 30 << 0.0 << true
5294             << 0 << 1 << ListRange(1, 18);  // 1-18 are displaced into view
5295     QTest::newRow("remove multiple from start")
5296             << 30 << 0.0 << true
5297             << 0 << 3 << ListRange(3, 20);  // 3-18 are displaced into view
5298     QTest::newRow("remove 1 from start, content y not 0")
5299             << 30 << 1.0 << true
5300             << 3 << 1 << ListRange(1 + 3, 18 + 3);
5301     QTest::newRow("remove multiple from start, content y not 0")
5302             << 30 << 1.0 << true
5303             << 3 << 3 << ListRange(3 + 3, 20 + 3);
5304
5305
5306     QTest::newRow("remove 1 from middle")
5307             << 30 << 0.0 << true
5308             << 5 << 1 << ListRange(6, 18);
5309     QTest::newRow("remove multiple from middle")
5310             << 30 << 0.0 << true
5311             << 5 << 3 << ListRange(8, 20);
5312
5313
5314     QTest::newRow("remove 1 from bottom")
5315             << 30 << 0.0 << true
5316             << 17 << 1 << ListRange(18, 18);
5317     QTest::newRow("remove multiple (1 row) from bottom")
5318             << 30 << 0.0 << true
5319             << 15 << 3 << ListRange(18, 20);
5320     QTest::newRow("remove multiple (> 1 row) from bottom")
5321             << 30 << 0.0 << true
5322             << 15 << 5 << ListRange(20, 22);
5323     QTest::newRow("remove 1 from bottom, content y not 0")
5324             << 30 << 1.0 << true
5325             << 17 + 3 << 1 << ListRange(18 + 3, 18 + 3);
5326     QTest::newRow("remove multiple (1 row) from bottom, content y not 0")
5327             << 30 << 1.0 << true
5328             << 15 + 3 << 3 << ListRange(18 + 3, 20 + 3);
5329
5330
5331     QTest::newRow("remove 1 after end")
5332             << 30 << 0.0 << false
5333             << 18 << 1 << ListRange();
5334     QTest::newRow("remove multiple after end")
5335             << 30 << 0.0 << false
5336             << 18 << 3 << ListRange();
5337 }
5338
5339 void tst_QQuickGridView::displacedTransitions()
5340 {
5341     QFETCH(bool, useDisplaced);
5342     QFETCH(bool, displacedEnabled);
5343     QFETCH(bool, useAddDisplaced);
5344     QFETCH(bool, addDisplacedEnabled);
5345     QFETCH(bool, useMoveDisplaced);
5346     QFETCH(bool, moveDisplacedEnabled);
5347     QFETCH(bool, useRemoveDisplaced);
5348     QFETCH(bool, removeDisplacedEnabled);
5349     QFETCH(ListChange, change);
5350     QFETCH(ListRange, expectedDisplacedIndexes);
5351
5352     QaimModel model;
5353     for (int i = 0; i < 30; i++)
5354         model.addItem("Original item" + QString::number(i), "");
5355     QaimModel model_displaced_transitionVia;
5356     QaimModel model_addDisplaced_transitionVia;
5357     QaimModel model_moveDisplaced_transitionVia;
5358     QaimModel model_removeDisplaced_transitionVia;
5359
5360     QPointF displaced_transitionVia(-50, -100);
5361     QPointF addDisplaced_transitionVia(-150, 100);
5362     QPointF moveDisplaced_transitionVia(50, -100);
5363     QPointF removeDisplaced_transitionVia(150, 100);
5364
5365     QQuickView *canvas = getView();
5366     QQmlContext *ctxt = canvas->rootContext();
5367     ctxt->setContextProperty("testModel", &model);
5368     ctxt->setContextProperty("model_displaced_transitionVia", &model_displaced_transitionVia);
5369     ctxt->setContextProperty("model_addDisplaced_transitionVia", &model_addDisplaced_transitionVia);
5370     ctxt->setContextProperty("model_moveDisplaced_transitionVia", &model_moveDisplaced_transitionVia);
5371     ctxt->setContextProperty("model_removeDisplaced_transitionVia", &model_removeDisplaced_transitionVia);
5372     ctxt->setContextProperty("displaced_transitionVia", displaced_transitionVia);
5373     ctxt->setContextProperty("addDisplaced_transitionVia", addDisplaced_transitionVia);
5374     ctxt->setContextProperty("moveDisplaced_transitionVia", moveDisplaced_transitionVia);
5375     ctxt->setContextProperty("removeDisplaced_transitionVia", removeDisplaced_transitionVia);
5376     ctxt->setContextProperty("useDisplaced", useDisplaced);
5377     ctxt->setContextProperty("displacedEnabled", displacedEnabled);
5378     ctxt->setContextProperty("useAddDisplaced", useAddDisplaced);
5379     ctxt->setContextProperty("addDisplacedEnabled", addDisplacedEnabled);
5380     ctxt->setContextProperty("useMoveDisplaced", useMoveDisplaced);
5381     ctxt->setContextProperty("moveDisplacedEnabled", moveDisplacedEnabled);
5382     ctxt->setContextProperty("useRemoveDisplaced", useRemoveDisplaced);
5383     ctxt->setContextProperty("removeDisplacedEnabled", removeDisplacedEnabled);
5384     canvas->setSource(testFileUrl("displacedTransitions.qml"));
5385     canvas->show();
5386     qApp->processEvents();
5387
5388     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
5389     QTRY_VERIFY(gridview != 0);
5390     QQuickItem *contentItem = gridview->contentItem();
5391     QVERIFY(contentItem != 0);
5392     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
5393
5394     QList<QPair<QString,QString> > expectedDisplacedValues = expectedDisplacedIndexes.getModelDataValues(model);
5395     gridview->setProperty("displaceTransitionsDone", false);
5396
5397     switch (change.type) {
5398         case ListChange::Inserted:
5399         {
5400             QList<QPair<QString, QString> > targetItemData;
5401             for (int i=change.index; i<change.index + change.count; ++i)
5402                 targetItemData << qMakePair(QString("new item %1").arg(i), QString::number(i));
5403             model.insertItems(change.index, targetItemData);
5404             QTRY_COMPARE(model.count(), gridview->count());
5405             break;
5406         }
5407         case ListChange::Removed:
5408             model.removeItems(change.index, change.count);
5409             QTRY_COMPARE(model.count(), gridview->count());
5410             break;
5411         case ListChange::Moved:
5412             model.moveItems(change.index, change.to, change.count);
5413             QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
5414             break;
5415         case ListChange::SetCurrent:
5416         case ListChange::SetContentY:
5417             break;
5418     }
5419
5420     QVariantList resultTargetIndexes = gridview->property("displacedTargetIndexes").toList();
5421     QVariantList resultTargetItems = gridview->property("displacedTargetItems").toList();
5422
5423     if ((useDisplaced && displacedEnabled)
5424             || (useAddDisplaced && addDisplacedEnabled)
5425             || (useMoveDisplaced && moveDisplacedEnabled)
5426             || (useRemoveDisplaced && removeDisplacedEnabled)) {
5427         QTRY_VERIFY(gridview->property("displaceTransitionsDone").toBool());
5428
5429         // check the correct number of target items and indexes were received
5430         QCOMPARE(resultTargetIndexes.count(), expectedDisplacedIndexes.count());
5431         for (int i=0; i<resultTargetIndexes.count(); i++)
5432             QCOMPARE(resultTargetIndexes[i].value<QList<int> >().count(), change.count);
5433         QCOMPARE(resultTargetItems.count(), expectedDisplacedIndexes.count());
5434         for (int i=0; i<resultTargetItems.count(); i++)
5435             QCOMPARE(resultTargetItems[i].toList().count(), change.count);
5436     } else {
5437         QCOMPARE(resultTargetIndexes.count(), 0);
5438         QCOMPARE(resultTargetItems.count(), 0);
5439     }
5440
5441     if (change.type == ListChange::Inserted && useAddDisplaced && addDisplacedEnabled)
5442         model_addDisplaced_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with add displaced", "shouldn't have been animated with add displaced");
5443     else
5444         QCOMPARE(model_addDisplaced_transitionVia.count(), 0);
5445     if (change.type == ListChange::Moved && useMoveDisplaced && moveDisplacedEnabled)
5446         model_moveDisplaced_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with move displaced", "shouldn't have been animated with move displaced");
5447     else
5448         QCOMPARE(model_moveDisplaced_transitionVia.count(), 0);
5449     if (change.type == ListChange::Removed && useRemoveDisplaced && removeDisplacedEnabled)
5450         model_removeDisplaced_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with remove displaced", "shouldn't have been animated with remove displaced");
5451     else
5452         QCOMPARE(model_removeDisplaced_transitionVia.count(), 0);
5453
5454     if (useDisplaced && displacedEnabled
5455             && ( (change.type == ListChange::Inserted && (!useAddDisplaced || !addDisplacedEnabled))
5456                  || (change.type == ListChange::Moved && (!useMoveDisplaced || !moveDisplacedEnabled))
5457                  || (change.type == ListChange::Removed && (!useRemoveDisplaced || !removeDisplacedEnabled))) ) {
5458         model_displaced_transitionVia.matchAgainst(expectedDisplacedValues, "wasn't animated with generic displaced", "shouldn't have been animated with generic displaced");
5459     } else {
5460         QCOMPARE(model_displaced_transitionVia.count(), 0);
5461     }
5462
5463     // verify all items moved to the correct final positions
5464     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
5465     for (int i=0; i < model.count() && i < items.count(); ++i) {
5466         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
5467         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
5468         QCOMPARE(item->x(), (i%3)*80.0);
5469         QCOMPARE(item->y(), (i/3)*60.0);
5470         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
5471         QVERIFY(name != 0);
5472         QTRY_COMPARE(name->text(), model.name(i));
5473     }
5474
5475     releaseView(canvas);
5476 }
5477
5478 void tst_QQuickGridView::displacedTransitions_data()
5479 {
5480     QTest::addColumn<bool>("useDisplaced");
5481     QTest::addColumn<bool>("displacedEnabled");
5482     QTest::addColumn<bool>("useAddDisplaced");
5483     QTest::addColumn<bool>("addDisplacedEnabled");
5484     QTest::addColumn<bool>("useMoveDisplaced");
5485     QTest::addColumn<bool>("moveDisplacedEnabled");
5486     QTest::addColumn<bool>("useRemoveDisplaced");
5487     QTest::addColumn<bool>("removeDisplacedEnabled");
5488     QTest::addColumn<ListChange>("change");
5489     QTest::addColumn<ListRange>("expectedDisplacedIndexes");
5490
5491     QTest::newRow("no displaced transitions at all")
5492             << false << false
5493             << false << false
5494             << false << false
5495             << false << false
5496             << ListChange::insert(0, 1) << ListRange(0, 17);
5497
5498     QTest::newRow("just displaced")
5499             << true << true
5500             << false << false
5501             << false << false
5502             << false << false
5503             << ListChange::insert(0, 1) << ListRange(0, 17);
5504
5505     QTest::newRow("just displaced (not enabled)")
5506             << true << false
5507             << false << false
5508             << false << false
5509             << false << false
5510             << ListChange::insert(0, 1) << ListRange(0, 17);
5511
5512     QTest::newRow("displaced + addDisplaced")
5513             << true << true
5514             << true << true
5515             << false << false
5516             << false << false
5517             << ListChange::insert(0, 1) << ListRange(0, 17);
5518
5519     QTest::newRow("displaced + addDisplaced (not enabled)")
5520             << true << true
5521             << true << false
5522             << false << false
5523             << false << false
5524             << ListChange::insert(0, 1) << ListRange(0, 17);
5525
5526     QTest::newRow("displaced + moveDisplaced")
5527             << true << true
5528             << false << false
5529             << true << true
5530             << false << false
5531             << ListChange::move(0, 10, 1) << ListRange(1, 10);
5532
5533     QTest::newRow("displaced + moveDisplaced (not enabled)")
5534             << true << true
5535             << false << false
5536             << true << false
5537             << false << false
5538             << ListChange::move(0, 10, 1) << ListRange(1, 10);
5539
5540     QTest::newRow("displaced + removeDisplaced")
5541             << true << true
5542             << false << false
5543             << false << false
5544             << true << true
5545             << ListChange::remove(0, 1) << ListRange(1, 18);
5546
5547     QTest::newRow("displaced + removeDisplaced (not enabled)")
5548             << true << true
5549             << false << false
5550             << false << false
5551             << true << false
5552             << ListChange::remove(0, 1) << ListRange(1, 18);
5553
5554
5555     QTest::newRow("displaced + add, should use generic displaced for a remove")
5556             << true << true
5557             << true << true
5558             << false << false
5559             << true << false
5560             << ListChange::remove(0, 1) << ListRange(1, 18);
5561 }
5562
5563 void tst_QQuickGridView::multipleTransitions()
5564 {
5565     // Tests that if you interrupt a transition in progress with another action that
5566     // cancels the previous transition, the resulting items are still placed correctly.
5567
5568     QFETCH(int, initialCount);
5569     QFETCH(qreal, contentY);
5570     QFETCH(QList<ListChange>, changes);
5571     QFETCH(bool, enableAddTransitions);
5572     QFETCH(bool, enableMoveTransitions);
5573     QFETCH(bool, enableRemoveTransitions);
5574     QFETCH(bool, rippleAddDisplaced);
5575
5576     // add transitions on the left, moves on the right
5577     QPointF addTargets_transitionFrom(-50, -50);
5578     QPointF addDisplaced_transitionFrom(-50, 50);
5579     QPointF moveTargets_transitionFrom(50, -50);
5580     QPointF moveDisplaced_transitionFrom(50, 50);
5581     QPointF removeTargets_transitionTo(-100, 300);
5582     QPointF removeDisplaced_transitionFrom(100, 300);
5583
5584     QmlListModel model;
5585     for (int i = 0; i < initialCount; i++)
5586         model.addItem("Original item" + QString::number(i), "");
5587
5588     QQuickView *canvas = getView();
5589     QQmlContext *ctxt = canvas->rootContext();
5590     ctxt->setContextProperty("testModel", &model);
5591     ctxt->setContextProperty("addTargets_transitionFrom", addTargets_transitionFrom);
5592     ctxt->setContextProperty("addDisplaced_transitionFrom", addDisplaced_transitionFrom);
5593     ctxt->setContextProperty("moveTargets_transitionFrom", moveTargets_transitionFrom);
5594     ctxt->setContextProperty("moveDisplaced_transitionFrom", moveDisplaced_transitionFrom);
5595     ctxt->setContextProperty("removeTargets_transitionTo", removeTargets_transitionTo);
5596     ctxt->setContextProperty("removeDisplaced_transitionFrom", removeDisplaced_transitionFrom);
5597     ctxt->setContextProperty("enableAddTransitions", enableAddTransitions);
5598     ctxt->setContextProperty("enableMoveTransitions", enableMoveTransitions);
5599     ctxt->setContextProperty("enableRemoveTransitions", enableRemoveTransitions);
5600     ctxt->setContextProperty("rippleAddDisplaced", rippleAddDisplaced);
5601     canvas->setSource(testFileUrl("multipleTransitions.qml"));
5602     canvas->show();
5603     QTest::qWaitForWindowShown(canvas);
5604
5605     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
5606     QTRY_VERIFY(gridview != 0);
5607     QQuickItem *contentItem = gridview->contentItem();
5608     QVERIFY(contentItem != 0);
5609     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
5610
5611     if (contentY != 0) {
5612         gridview->setContentY(contentY);
5613         QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
5614     }
5615
5616     int timeBetweenActions = canvas->rootObject()->property("timeBetweenActions").toInt();
5617
5618     for (int i=0; i<changes.count(); i++) {
5619         switch (changes[i].type) {
5620             case ListChange::Inserted:
5621             {
5622                 QList<QPair<QString, QString> > targetItems;
5623                 for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
5624                     targetItems << qMakePair(QString("new item %1").arg(j), QString::number(j));
5625                 model.insertItems(changes[i].index, targetItems);
5626                 QTRY_COMPARE(model.count(), gridview->count());
5627                 if (i == changes.count() - 1) {
5628                     QTRY_VERIFY(!gridview->property("runningAddTargets").toBool());
5629                     QTRY_VERIFY(!gridview->property("runningAddDisplaced").toBool());
5630                 } else {
5631                     QTest::qWait(timeBetweenActions);
5632                 }
5633                 break;
5634             }
5635             case ListChange::Removed:
5636                 model.removeItems(changes[i].index, changes[i].count);
5637                 QTRY_COMPARE(model.count(), gridview->count());
5638                 if (i == changes.count() - 1) {
5639                     QTRY_VERIFY(!gridview->property("runningRemoveTargets").toBool());
5640                     QTRY_VERIFY(!gridview->property("runningRemoveDisplaced").toBool());
5641                 } else {
5642                     QTest::qWait(timeBetweenActions);
5643                 }
5644                 break;
5645             case ListChange::Moved:
5646                 model.moveItems(changes[i].index, changes[i].to, changes[i].count);
5647                 QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
5648                 if (i == changes.count() - 1) {
5649                     QTRY_VERIFY(!gridview->property("runningMoveTargets").toBool());
5650                     QTRY_VERIFY(!gridview->property("runningMoveDisplaced").toBool());
5651                 } else {
5652                     QTest::qWait(timeBetweenActions);
5653                 }
5654                 break;
5655             case ListChange::SetCurrent:
5656                 gridview->setCurrentIndex(changes[i].index);
5657                 QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
5658                 break;
5659             case ListChange::SetContentY:
5660                 gridview->setContentY(changes[i].pos);
5661                 QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
5662                 break;
5663         }
5664     }
5665     QCOMPARE(gridview->count(), model.count());
5666
5667     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
5668     int firstVisibleIndex = -1;
5669     for (int i=0; i<items.count(); i++) {
5670         if (items[i]->y() >= contentY) {
5671             QQmlExpression e(qmlContext(items[i]), items[i], "index");
5672             firstVisibleIndex = e.evaluate().toInt();
5673             break;
5674         }
5675     }
5676     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
5677
5678     // verify all items moved to the correct final positions
5679     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
5680     for (int i=firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
5681         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
5682         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
5683         QTRY_COMPARE(item->x(), (i%3)*80.0);
5684         QTRY_COMPARE(item->y(), (i/3)*60.0);
5685         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
5686         QVERIFY(name != 0);
5687         QTRY_COMPARE(name->text(), model.name(i));
5688     }
5689
5690     releaseView(canvas);
5691 }
5692
5693 void tst_QQuickGridView::multipleTransitions_data()
5694 {
5695     QTest::addColumn<int>("initialCount");
5696     QTest::addColumn<qreal>("contentY");
5697     QTest::addColumn<QList<ListChange> >("changes");
5698     QTest::addColumn<bool>("enableAddTransitions");
5699     QTest::addColumn<bool>("enableMoveTransitions");
5700     QTest::addColumn<bool>("enableRemoveTransitions");
5701     QTest::addColumn<bool>("rippleAddDisplaced");
5702
5703     // the added item and displaced items should move to final dest correctly
5704     QTest::newRow("add item, then move it immediately") << 10 << 0.0 << (QList<ListChange>()
5705              << ListChange::insert(0, 1)
5706              << ListChange::move(0, 3, 1)
5707              )
5708              << true << true << true << false;
5709
5710     // items affected by the add should change from move to add transition
5711     QTest::newRow("move, then insert item before the moved item") << 20 << 0.0 << (QList<ListChange>()
5712             << ListChange::move(1, 10, 3)
5713             << ListChange::insert(0, 1)
5714             )
5715             << true << true << true << false;
5716
5717     // items should be placed correctly if you trigger a transition then refill for that index
5718     QTest::newRow("add at 0, flick down, flick back to top and add at 0 again") << 20 << 0.0 << (QList<ListChange>()
5719             << ListChange::insert(0, 1)
5720             << ListChange::setContentY(160.0)
5721             << ListChange::setContentY(0.0)
5722             << ListChange::insert(0, 1)
5723             )
5724             << true << true << true << false;
5725
5726     QTest::newRow("insert then remove same index, with ripple effect on add displaced") << 20 << 0.0 << (QList<ListChange>()
5727             << ListChange::insert(1, 1)
5728             << ListChange::remove(1, 1)
5729             )
5730             << true << true << true << true;
5731
5732     // if item is removed while undergoing a displaced transition, all other items should end up at their correct positions,
5733     // even if a remove-displace transition is not present to re-animate them
5734     QTest::newRow("insert then remove, with remove disabled") << 20 << 0.0 << (QList<ListChange>()
5735             << ListChange::insert(0, 1)
5736             << ListChange::remove(2, 1)
5737             )
5738             << true << true << false << false;
5739
5740     // if last item is not flush with the edge of the view, it should still be refilled in correctly after a
5741     // remove has changed the position of where it will move to
5742     QTest::newRow("insert twice then remove, with remove disabled") << 20 << 0.0 << (QList<ListChange>()
5743             << ListChange::setContentY(-10.0)
5744             << ListChange::insert(0, 1)
5745             << ListChange::insert(0, 1)
5746             << ListChange::remove(2, 1)
5747             )
5748             << true << true << false << false;
5749 }
5750
5751 void tst_QQuickGridView::multipleDisplaced()
5752 {
5753     // multiple move() operations should only restart displace transitions for items that
5754     // moved from previously set positions, and not those that have moved from their current
5755     // item positions (which may e.g. still be changing from easing bounces in the last transition)
5756
5757     QmlListModel model;
5758     for (int i = 0; i < 30; i++)
5759         model.addItem("Original item" + QString::number(i), "");
5760
5761     QQuickView *canvas = createView();
5762     QQmlContext *ctxt = canvas->rootContext();
5763     ctxt->setContextProperty("testModel", &model);
5764     canvas->setSource(testFileUrl("multipleDisplaced.qml"));
5765     canvas->show();
5766     QTest::qWaitForWindowShown(canvas);
5767
5768     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
5769     QTRY_VERIFY(gridview != 0);
5770     QQuickItem *contentItem = gridview->contentItem();
5771     QVERIFY(contentItem != 0);
5772     QTRY_COMPARE(QQuickItemPrivate::get(gridview)->polishScheduled, false);
5773
5774     model.moveItems(12, 8, 1);
5775     QTest::qWait(canvas->rootObject()->property("duration").toInt() / 2);
5776     model.moveItems(8, 3, 1);
5777     QTRY_VERIFY(gridview->property("displaceTransitionsDone").toBool());
5778
5779     QVariantMap transitionsStarted = gridview->property("displaceTransitionsStarted").toMap();
5780     foreach (const QString &name, transitionsStarted.keys()) {
5781         QVERIFY2(transitionsStarted[name] == 1,
5782                  QTest::toString(QString("%1 was displaced %2 times").arg(name).arg(transitionsStarted[name].toInt())));
5783     }
5784
5785     // verify all items moved to the correct final positions
5786     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
5787     for (int i=0; i < model.count() && i < items.count(); ++i) {
5788         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
5789         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
5790         QTRY_COMPARE(item->x(), (i%3)*80.0);
5791         QTRY_COMPARE(item->y(), (i/3)*60.0);
5792         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
5793         QVERIFY(name != 0);
5794         QTRY_COMPARE(name->text(), model.name(i));
5795     }
5796
5797     delete canvas;
5798 }
5799
5800 void tst_QQuickGridView::cacheBuffer()
5801 {
5802     QQuickView *canvas = createView();
5803
5804     QaimModel model;
5805     for (int i = 0; i < 90; i++)
5806         model.addItem("Item" + QString::number(i), "");
5807
5808     canvas->rootContext()->setContextProperty("testModel", &model);
5809     canvas->setSource(testFileUrl("gridview1.qml"));
5810     canvas->show();
5811     qApp->processEvents();
5812
5813     QQuickGridView *gridview = findItem<QQuickGridView>(canvas->rootObject(), "grid");
5814     QVERIFY(gridview != 0);
5815
5816     QQuickItem *contentItem = gridview->contentItem();
5817     QVERIFY(contentItem != 0);
5818     QVERIFY(gridview->delegate() != 0);
5819     QVERIFY(gridview->model() != 0);
5820
5821     // Confirm items positioned correctly
5822     int itemCount = findItems<QQuickItem>(contentItem, "wrapper", false).count();
5823     for (int i = 0; i < model.count() && i < itemCount; ++i) {
5824         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
5825         QTRY_COMPARE(item->x(), (i%3)*80.0);
5826         QTRY_COMPARE(item->y(), (i/3)*60.0);
5827     }
5828
5829     QQmlIncubationController controller;
5830     canvas->engine()->setIncubationController(&controller);
5831
5832     canvas->rootObject()->setProperty("cacheBuffer", 200);
5833     QTRY_VERIFY(gridview->cacheBuffer() == 200);
5834
5835     // items will be created one at a time
5836     for (int i = itemCount; i < qMin(itemCount+9,model.count()); ++i) {
5837         QVERIFY(findItem<QQuickItem>(gridview, "wrapper", i) == 0);
5838         QQuickItem *item = 0;
5839         while (!item) {
5840             bool b = false;
5841             controller.incubateWhile(&b);
5842             item = findItem<QQuickItem>(gridview, "wrapper", i);
5843         }
5844     }
5845
5846     {
5847         bool b = true;
5848         controller.incubateWhile(&b);
5849     }
5850
5851     int newItemCount = 0;
5852     newItemCount = findItems<QQuickItem>(contentItem, "wrapper", false).count();
5853
5854     // Confirm items positioned correctly
5855     for (int i = 0; i < model.count() && i < newItemCount; ++i) {
5856         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
5857         QVERIFY(item);
5858         QTRY_COMPARE(item->x(), (i%3)*80.0);
5859         QTRY_COMPARE(item->y(), (i/3)*60.0);
5860     }
5861
5862     // move view and confirm items in view are visible immediately and outside are created async
5863     gridview->setContentY(300);
5864
5865     for (int i = 15; i < 34; ++i) { // 34 due to staggered item creation
5866         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
5867         QVERIFY(item);
5868         QTRY_COMPARE(item->x(), (i%3)*80.0);
5869         QTRY_COMPARE(item->y(), (i/3)*60.0);
5870     }
5871
5872     QVERIFY(findItem<QQuickItem>(gridview, "wrapper", 34) == 0);
5873
5874     // ensure buffered items are created
5875     for (int i = 34; i < qMin(44,model.count()); ++i) {
5876         QQuickItem *item = 0;
5877         while (!item) {
5878             qGuiApp->processEvents(); // allow refill to happen
5879             bool b = false;
5880             controller.incubateWhile(&b);
5881             item = findItem<QQuickItem>(gridview, "wrapper", i);
5882         }
5883     }
5884
5885     {
5886         bool b = true;
5887         controller.incubateWhile(&b);
5888     }
5889
5890     delete canvas;
5891 }
5892
5893 void tst_QQuickGridView::asynchronous()
5894 {
5895     QQuickView *canvas = createView();
5896     canvas->show();
5897     QQmlIncubationController controller;
5898     canvas->engine()->setIncubationController(&controller);
5899
5900     canvas->setSource(testFileUrl("asyncloader.qml"));
5901
5902     QQuickItem *rootObject = qobject_cast<QQuickItem*>(canvas->rootObject());
5903     QVERIFY(rootObject);
5904
5905     QQuickGridView *gridview = 0;
5906     while (!gridview) {
5907         bool b = false;
5908         controller.incubateWhile(&b);
5909         gridview = rootObject->findChild<QQuickGridView*>("view");
5910     }
5911
5912     // items will be created one at a time
5913     for (int i = 0; i < 12; ++i) {
5914         QVERIFY(findItem<QQuickItem>(gridview, "wrapper", i) == 0);
5915         QQuickItem *item = 0;
5916         while (!item) {
5917             bool b = false;
5918             controller.incubateWhile(&b);
5919             item = findItem<QQuickItem>(gridview, "wrapper", i);
5920         }
5921     }
5922
5923     {
5924         bool b = true;
5925         controller.incubateWhile(&b);
5926     }
5927
5928     // verify positioning
5929     QQuickItem *contentItem = gridview->contentItem();
5930     for (int i = 0; i < 12; ++i) {
5931         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
5932         if (!item) qWarning() << "Item" << i << "not found";
5933         QVERIFY(item->x() == (i%3)*100);
5934         QVERIFY(item->y() == (i/3)*100);
5935     }
5936
5937     delete canvas;
5938 }
5939
5940 void tst_QQuickGridView::unrequestedVisibility()
5941 {
5942     QaimModel model;
5943     for (int i = 0; i < 30; i++)
5944         model.addItem("Item" + QString::number(i), QString::number(i));
5945
5946     QQuickView *canvas = new QQuickView(0);
5947     canvas->setGeometry(0,0,240,320);
5948
5949     QQmlContext *ctxt = canvas->rootContext();
5950     ctxt->setContextProperty("testModel", &model);
5951     ctxt->setContextProperty("testWrap", QVariant(false));
5952
5953     canvas->setSource(testFileUrl("unrequestedItems.qml"));
5954
5955     canvas->show();
5956
5957     qApp->processEvents();
5958
5959     QQuickGridView *leftview = findItem<QQuickGridView>(canvas->rootObject(), "leftGrid");
5960     QTRY_VERIFY(leftview != 0);
5961
5962     QQuickGridView *rightview = findItem<QQuickGridView>(canvas->rootObject(), "rightGrid");
5963     QTRY_VERIFY(rightview != 0);
5964
5965     QQuickItem *leftContent = leftview->contentItem();
5966     QTRY_VERIFY(leftContent != 0);
5967
5968     QQuickItem *rightContent = rightview->contentItem();
5969     QTRY_VERIFY(rightContent != 0);
5970
5971     rightview->setCurrentIndex(12);
5972
5973     QTRY_COMPARE(leftview->contentY(), 0.0);
5974     QTRY_COMPARE(rightview->contentY(), 240.0);
5975
5976     QQuickItem *item;
5977
5978     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
5979     QCOMPARE(item->isVisible(), true);
5980     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
5981     QCOMPARE(item->isVisible(), false);
5982
5983     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
5984     QCOMPARE(item->isVisible(), false);
5985     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
5986     QCOMPARE(item->isVisible(), true);
5987
5988     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 9));
5989     QCOMPARE(item->isVisible(), true);
5990     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 10));
5991     QCOMPARE(item->isVisible(), false);
5992     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 3));
5993     QCOMPARE(item->isVisible(), false);
5994     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 4));
5995     QCOMPARE(item->isVisible(), true);
5996
5997     rightview->setCurrentIndex(0);
5998
5999     QTRY_COMPARE(leftview->contentY(), 0.0);
6000     QTRY_COMPARE(rightview->contentY(), 0.0);
6001
6002     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
6003     QCOMPARE(item->isVisible(), true);
6004     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
6005     QTRY_COMPARE(item->isVisible(), true);
6006
6007     QVERIFY(!findItem<QQuickItem>(leftContent, "wrapper", 11));
6008     QVERIFY(!findItem<QQuickItem>(rightContent, "wrapper", 11));
6009
6010     leftview->setCurrentIndex(12);
6011
6012     QTRY_COMPARE(leftview->contentY(), 240.0);
6013     QTRY_COMPARE(rightview->contentY(), 0.0);
6014
6015     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
6016     QTRY_COMPARE(item->isVisible(), false);
6017     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
6018     QCOMPARE(item->isVisible(), true);
6019
6020     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
6021     QCOMPARE(item->isVisible(), true);
6022     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
6023     QCOMPARE(item->isVisible(), false);
6024
6025     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
6026     QCOMPARE(item->isVisible(), false);
6027     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
6028     QCOMPARE(item->isVisible(), true);
6029     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
6030     QCOMPARE(item->isVisible(), true);
6031     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
6032     QCOMPARE(item->isVisible(), false);
6033
6034     // move a non-visible item into view
6035     model.moveItems(10, 9, 1);
6036     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
6037
6038     QTRY_VERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
6039     QCOMPARE(item->isVisible(), false);
6040     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
6041     QCOMPARE(item->isVisible(), true);
6042
6043     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 11));
6044     QCOMPARE(item->isVisible(), true);
6045     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 11));
6046     QCOMPARE(item->isVisible(), false);
6047
6048     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
6049     QCOMPARE(item->isVisible(), false);
6050     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
6051     QCOMPARE(item->isVisible(), true);
6052     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
6053     QCOMPARE(item->isVisible(), true);
6054     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
6055     QCOMPARE(item->isVisible(), false);
6056
6057     // move a visible item out of view
6058     model.moveItems(5, 3, 1);
6059     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
6060
6061     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
6062     QCOMPARE(item->isVisible(), false);
6063     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
6064     QCOMPARE(item->isVisible(), true);
6065     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
6066     QCOMPARE(item->isVisible(), true);
6067     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
6068     QCOMPARE(item->isVisible(), false);
6069
6070     // move a non-visible item into view
6071     model.moveItems(3, 5, 1);
6072     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
6073
6074     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
6075     QCOMPARE(item->isVisible(), false);
6076     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
6077     QCOMPARE(item->isVisible(), true);
6078     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
6079     QCOMPARE(item->isVisible(), true);
6080     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
6081     QCOMPARE(item->isVisible(), false);
6082
6083     // move a visible item out of view
6084     model.moveItems(9, 10, 1);
6085     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
6086
6087     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
6088     QCOMPARE(item->isVisible(), false);
6089     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
6090     QCOMPARE(item->isVisible(), true);
6091     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
6092     QCOMPARE(item->isVisible(), true);
6093     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
6094     QCOMPARE(item->isVisible(), false);
6095
6096     // move a non-visible item into view
6097     model.moveItems(10, 9, 1);
6098     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
6099
6100     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
6101     QCOMPARE(item->isVisible(), false);
6102     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
6103     QCOMPARE(item->isVisible(), true);
6104     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 9));
6105     QCOMPARE(item->isVisible(), true);
6106     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 10));
6107     QCOMPARE(item->isVisible(), false);
6108
6109     delete canvas;
6110 }
6111
6112
6113 void tst_QQuickGridView::inserted_leftToRight_RtL_TtB()
6114 {
6115     inserted_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::RightToLeft);
6116 }
6117
6118 void tst_QQuickGridView::inserted_leftToRight_RtL_TtB_data()
6119 {
6120     inserted_defaultLayout_data();
6121 }
6122
6123 void tst_QQuickGridView::inserted_topToBottom_LtR_TtB()
6124 {
6125     inserted_defaultLayout(QQuickGridView::FlowTopToBottom);
6126 }
6127
6128 void tst_QQuickGridView::inserted_topToBottom_LtR_TtB_data()
6129 {
6130     inserted_defaultLayout_data();
6131 }
6132
6133 void tst_QQuickGridView::inserted_topToBottom_RtL_TtB()
6134 {
6135     inserted_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::RightToLeft);
6136 }
6137
6138 void tst_QQuickGridView::inserted_topToBottom_RtL_TtB_data()
6139 {
6140     inserted_defaultLayout_data();
6141 }
6142
6143 void tst_QQuickGridView::inserted_leftToRight_LtR_BtT()
6144 {
6145     inserted_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::LeftToRight, QQuickItemView::BottomToTop);
6146 }
6147
6148 void tst_QQuickGridView::inserted_leftToRight_LtR_BtT_data()
6149 {
6150     inserted_defaultLayout_data();
6151 }
6152
6153 void tst_QQuickGridView::inserted_leftToRight_RtL_BtT()
6154 {
6155     inserted_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::RightToLeft, QQuickItemView::BottomToTop);
6156 }
6157
6158 void tst_QQuickGridView::inserted_leftToRight_RtL_BtT_data()
6159 {
6160     inserted_defaultLayout_data();
6161 }
6162
6163 void tst_QQuickGridView::inserted_topToBottom_LtR_BtT()
6164 {
6165     inserted_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::LeftToRight, QQuickItemView::BottomToTop);
6166 }
6167
6168 void tst_QQuickGridView::inserted_topToBottom_LtR_BtT_data()
6169 {
6170     inserted_defaultLayout_data();
6171 }
6172
6173 void tst_QQuickGridView::inserted_topToBottom_RtL_BtT()
6174 {
6175     inserted_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::RightToLeft, QQuickItemView::BottomToTop);
6176 }
6177
6178 void tst_QQuickGridView::inserted_topToBottom_RtL_BtT_data()
6179 {
6180     inserted_defaultLayout_data();
6181 }
6182
6183
6184 void tst_QQuickGridView::removed_leftToRight_RtL_TtB()
6185 {
6186     removed_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::RightToLeft);
6187 }
6188
6189 void tst_QQuickGridView::removed_leftToRight_RtL_TtB_data()
6190 {
6191     removed_defaultLayout_data();
6192 }
6193
6194 void tst_QQuickGridView::removed_topToBottom_LtR_TtB()
6195 {
6196     removed_defaultLayout(QQuickGridView::FlowTopToBottom);
6197 }
6198
6199 void tst_QQuickGridView::removed_topToBottom_LtR_TtB_data()
6200 {
6201     removed_defaultLayout_data();
6202 }
6203
6204 void tst_QQuickGridView::removed_topToBottom_RtL_TtB()
6205 {
6206     removed_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::RightToLeft);
6207 }
6208
6209 void tst_QQuickGridView::removed_topToBottom_RtL_TtB_data()
6210 {
6211     removed_defaultLayout_data();
6212 }
6213
6214 void tst_QQuickGridView::removed_leftToRight_LtR_BtT()
6215 {
6216     removed_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::LeftToRight, QQuickItemView::BottomToTop);
6217 }
6218
6219 void tst_QQuickGridView::removed_leftToRight_LtR_BtT_data()
6220 {
6221     removed_defaultLayout_data();
6222 }
6223
6224 void tst_QQuickGridView::removed_leftToRight_RtL_BtT()
6225 {
6226     removed_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::RightToLeft, QQuickItemView::BottomToTop);
6227 }
6228
6229 void tst_QQuickGridView::removed_leftToRight_RtL_BtT_data()
6230 {
6231     removed_defaultLayout_data();
6232 }
6233
6234 void tst_QQuickGridView::removed_topToBottom_LtR_BtT()
6235 {
6236     removed_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::LeftToRight, QQuickItemView::BottomToTop);
6237 }
6238
6239 void tst_QQuickGridView::removed_topToBottom_LtR_BtT_data()
6240 {
6241     removed_defaultLayout_data();
6242 }
6243
6244 void tst_QQuickGridView::removed_topToBottom_RtL_BtT()
6245 {
6246     removed_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::RightToLeft, QQuickItemView::BottomToTop);
6247 }
6248
6249 void tst_QQuickGridView::removed_topToBottom_RtL_BtT_data()
6250 {
6251     removed_defaultLayout_data();
6252 }
6253
6254
6255 void tst_QQuickGridView::moved_leftToRight_RtL_TtB()
6256 {
6257     moved_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::RightToLeft);
6258 }
6259
6260 void tst_QQuickGridView::moved_leftToRight_RtL_TtB_data()
6261 {
6262     moved_defaultLayout_data();
6263 }
6264
6265 void tst_QQuickGridView::moved_topToBottom_LtR_TtB()
6266 {
6267     moved_defaultLayout(QQuickGridView::FlowTopToBottom);
6268 }
6269
6270 void tst_QQuickGridView::moved_topToBottom_LtR_TtB_data()
6271 {
6272     moved_defaultLayout_data();
6273 }
6274
6275 void tst_QQuickGridView::moved_topToBottom_RtL_TtB()
6276 {
6277     moved_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::RightToLeft);
6278 }
6279
6280 void tst_QQuickGridView::moved_topToBottom_RtL_TtB_data()
6281 {
6282     moved_defaultLayout_data();
6283 }
6284
6285 void tst_QQuickGridView::moved_leftToRight_LtR_BtT()
6286 {
6287     moved_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::LeftToRight, QQuickItemView::BottomToTop);
6288 }
6289
6290 void tst_QQuickGridView::moved_leftToRight_LtR_BtT_data()
6291 {
6292     moved_defaultLayout_data();
6293 }
6294
6295 void tst_QQuickGridView::moved_leftToRight_RtL_BtT()
6296 {
6297     moved_defaultLayout(QQuickGridView::FlowLeftToRight, Qt::RightToLeft, QQuickItemView::BottomToTop);
6298 }
6299
6300 void tst_QQuickGridView::moved_leftToRight_RtL_BtT_data()
6301 {
6302     moved_defaultLayout_data();
6303 }
6304
6305 void tst_QQuickGridView::moved_topToBottom_LtR_BtT()
6306 {
6307     moved_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::LeftToRight, QQuickItemView::BottomToTop);
6308 }
6309
6310 void tst_QQuickGridView::moved_topToBottom_LtR_BtT_data()
6311 {
6312     moved_defaultLayout_data();
6313 }
6314
6315 void tst_QQuickGridView::moved_topToBottom_RtL_BtT()
6316 {
6317     moved_defaultLayout(QQuickGridView::FlowTopToBottom, Qt::RightToLeft, QQuickItemView::BottomToTop);
6318 }
6319
6320 void tst_QQuickGridView::moved_topToBottom_RtL_BtT_data()
6321 {
6322     moved_defaultLayout_data();
6323 }
6324
6325
6326 QList<int> tst_QQuickGridView::toIntList(const QVariantList &list)
6327 {
6328     QList<int> ret;
6329     bool ok = true;
6330     for (int i=0; i<list.count(); i++) {
6331         ret << list[i].toInt(&ok);
6332         if (!ok)
6333             qWarning() << "tst_QQuickGridView::toIntList(): not a number:" << list[i];
6334     }
6335
6336     return ret;
6337 }
6338
6339 void tst_QQuickGridView::matchIndexLists(const QVariantList &indexLists, const QList<int> &expectedIndexes)
6340 {
6341     for (int i=0; i<indexLists.count(); i++) {
6342         QSet<int> current = indexLists[i].value<QList<int> >().toSet();
6343         if (current != expectedIndexes.toSet())
6344             qDebug() << "Cannot match actual targets" << current << "with expected" << expectedIndexes;
6345         QCOMPARE(current, expectedIndexes.toSet());
6346     }
6347 }
6348
6349 void tst_QQuickGridView::matchItemsAndIndexes(const QVariantMap &items, const QaimModel &model, const QList<int> &expectedIndexes)
6350 {
6351     for (QVariantMap::const_iterator it = items.begin(); it != items.end(); ++it) {
6352         QVERIFY(it.value().type() == QVariant::Int);
6353         QString name = it.key();
6354         int itemIndex = it.value().toInt();
6355         QVERIFY2(expectedIndexes.contains(itemIndex), QTest::toString(QString("Index %1 not found in expectedIndexes").arg(itemIndex)));
6356         if (model.name(itemIndex) != name)
6357             qDebug() << itemIndex;
6358         QCOMPARE(model.name(itemIndex), name);
6359     }
6360     QCOMPARE(items.count(), expectedIndexes.count());
6361 }
6362
6363 void tst_QQuickGridView::matchItemLists(const QVariantList &itemLists, const QList<QQuickItem *> &expectedItems)
6364 {
6365     for (int i=0; i<itemLists.count(); i++) {
6366         QVariantList current = itemLists[i].toList();
6367         for (int j=0; j<current.count(); j++) {
6368             QQuickItem *o = qobject_cast<QQuickItem*>(current[j].value<QObject*>());
6369             QVERIFY2(o, QTest::toString(QString("Invalid actual item at %1").arg(j)));
6370             QVERIFY2(expectedItems.contains(o), QTest::toString(QString("Cannot match item %1").arg(j)));
6371         }
6372         QCOMPARE(current.count(), expectedItems.count());
6373     }
6374 }
6375
6376 QTEST_MAIN(tst_QQuickGridView)
6377
6378 #include "tst_qquickgridview.moc"
6379