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