Update copyright year in license headers.
[profile/ivi/qtdeclarative.git] / tests / auto / qtquick2 / qquicklistview / tst_qquicklistview.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the test suite of the Qt Toolkit.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include <QtTest/QtTest>
43 #include <QtCore/QStringListModel>
44 #include <QtQuick/qquickview.h>
45 #include <QtDeclarative/qdeclarativeengine.h>
46 #include <QtDeclarative/qdeclarativecontext.h>
47 #include <QtDeclarative/qdeclarativeexpression.h>
48 #include <QtDeclarative/qdeclarativeincubator.h>
49 #include <QtQuick/private/qquickitem_p.h>
50 #include <QtQuick/private/qquicklistview_p.h>
51 #include <QtQuick/private/qquicktext_p.h>
52 #include <QtQuick/private/qquickvisualitemmodel_p.h>
53 #include <QtDeclarative/private/qdeclarativelistmodel_p.h>
54 #include <QtDeclarative/private/qlistmodelinterface_p.h>
55 #include <QtQuick/private/qdeclarativechangeset_p.h>
56 #include "../../shared/util.h"
57 #include "incrementalmodel.h"
58 #include <math.h>
59
60 Q_DECLARE_METATYPE(Qt::LayoutDirection)
61 Q_DECLARE_METATYPE(QQuickListView::Orientation)
62
63 class tst_QQuickListView : public QDeclarativeDataTest
64 {
65     Q_OBJECT
66 public:
67     tst_QQuickListView();
68
69 private slots:
70     // Test both QListModelInterface and QAbstractItemModel model types
71     void qListModelInterface_items();
72     void qListModelInterface_package_items();
73     void qAbstractItemModel_items();
74
75     void qListModelInterface_changed();
76     void qListModelInterface_package_changed();
77     void qAbstractItemModel_changed();
78
79     void qListModelInterface_inserted();
80     void qListModelInterface_inserted_more();
81     void qListModelInterface_inserted_more_data();
82     void qListModelInterface_package_inserted();
83     void qAbstractItemModel_inserted();
84     void qAbstractItemModel_inserted_more();
85     void qAbstractItemModel_inserted_more_data();
86
87     void qListModelInterface_removed();
88     void qListModelInterface_package_removed();
89     void qAbstractItemModel_removed();
90
91     void qListModelInterface_moved();
92     void qListModelInterface_moved_data();
93     void qListModelInterface_package_moved();
94     void qListModelInterface_package_moved_data();
95     void qAbstractItemModel_moved();
96     void qAbstractItemModel_moved_data();
97
98     void multipleChanges();
99     void multipleChanges_data();
100
101     void qListModelInterface_clear();
102     void qListModelInterface_package_clear();
103     void qAbstractItemModel_clear();
104
105     void insertBeforeVisible();
106     void insertBeforeVisible_data();
107     void swapWithFirstItem();
108     void itemList();
109     void currentIndex_delayedItemCreation();
110     void currentIndex_delayedItemCreation_data();
111     void currentIndex();
112     void noCurrentIndex();
113     void enforceRange();
114     void enforceRange_withoutHighlight();
115     void spacing();
116     void qListModelInterface_sections();
117     void qListModelInterface_package_sections();
118     void qAbstractItemModel_sections();
119     void sectionsPositioning();
120     void sectionsDelegate();
121     void cacheBuffer();
122     void positionViewAtIndex();
123     void resetModel();
124     void propertyChanges();
125     void componentChanges();
126     void modelChanges();
127     void manualHighlight();
128     void header();
129     void header_data();
130     void header_delayItemCreation();
131     void footer();
132     void footer_data();
133     void headerFooter();
134     void resizeView();
135     void resizeViewAndRepaint();
136     void sizeLessThan1();
137     void QTBUG_14821();
138     void resizeDelegate();
139     void resizeFirstDelegate();
140     void QTBUG_16037();
141     void indexAt();
142     void incrementalModel();
143     void onAdd();
144     void onAdd_data();
145     void onRemove();
146     void onRemove_data();
147     void rightToLeft();
148     void test_mirroring();
149     void margins();
150     void creationContext();
151     void snapToItem_data();
152     void snapToItem();
153     void snapOneItem_data();
154     void snapOneItem();
155
156     void QTBUG_9791();
157     void QTBUG_11105();
158     void QTBUG_21742();
159
160     void asynchronous();
161     void unrequestedVisibility();
162
163 private:
164     template <class T> void items(const QUrl &source, bool forceLayout);
165     template <class T> void changed(const QUrl &source, bool forceLayout);
166     template <class T> void inserted(const QUrl &source);
167     template <class T> void inserted_more();
168     template <class T> void removed(const QUrl &source, bool animated);
169     template <class T> void moved(const QUrl &source);
170     template <class T> void clear(const QUrl &source);
171     template <class T> void sections(const QUrl &source);
172     QQuickView *createView();
173     void flick(QQuickView *canvas, const QPoint &from, const QPoint &to, int duration);
174     QQuickItem *findVisibleChild(QQuickItem *parent, const QString &objectName);
175     template<typename T>
176     T *findItem(QQuickItem *parent, const QString &id, int index=-1);
177     template<typename T>
178     QList<T*> findItems(QQuickItem *parent, const QString &objectName, bool visibleOnly = true);
179     void dumpTree(QQuickItem *parent, int depth = 0);
180
181     void inserted_more_data();
182     void moved_data();
183 };
184
185 class TestObject : public QObject
186 {
187     Q_OBJECT
188
189     Q_PROPERTY(bool error READ error WRITE setError NOTIFY changedError)
190     Q_PROPERTY(bool animate READ animate NOTIFY changedAnim)
191     Q_PROPERTY(bool invalidHighlight READ invalidHighlight NOTIFY changedHl)
192     Q_PROPERTY(int cacheBuffer READ cacheBuffer NOTIFY changedCacheBuffer)
193
194 public:
195     TestObject(QObject *parent = 0)
196         : QObject(parent), mError(true), mAnimate(false), mInvalidHighlight(false)
197         , mCacheBuffer(0) {}
198
199     bool error() const { return mError; }
200     void setError(bool err) { mError = err; emit changedError(); }
201
202     bool animate() const { return mAnimate; }
203     void setAnimate(bool anim) { mAnimate = anim; emit changedAnim(); }
204
205     bool invalidHighlight() const { return mInvalidHighlight; }
206     void setInvalidHighlight(bool invalid) { mInvalidHighlight = invalid; emit changedHl(); }
207
208     int cacheBuffer() const { return mCacheBuffer; }
209     void setCacheBuffer(int buffer) { mCacheBuffer = buffer; emit changedCacheBuffer(); }
210
211 signals:
212     void changedError();
213     void changedAnim();
214     void changedHl();
215     void changedCacheBuffer();
216
217 public:
218     bool mError;
219     bool mAnimate;
220     bool mInvalidHighlight;
221     int mCacheBuffer;
222 };
223
224 template<typename T>
225 void tst_qquicklistview_move(int from, int to, int n, T *items)
226 {
227     if (from > to) {
228         // Only move forwards - flip if backwards moving
229         int tfrom = from;
230         int tto = to;
231         from = tto;
232         to = tto+n;
233         n = tfrom-tto;
234     }
235     if (n == 1) {
236         items->move(from, to);
237     } else {
238         T replaced;
239         int i=0;
240         typename T::ConstIterator it=items->begin(); it += from+n;
241         for (; i<to-from; ++i,++it)
242             replaced.append(*it);
243         i=0;
244         it=items->begin(); it += from;
245         for (; i<n; ++i,++it)
246             replaced.append(*it);
247         typename T::ConstIterator f=replaced.begin();
248         typename T::Iterator t=items->begin(); t += from;
249         for (; f != replaced.end(); ++f, ++t)
250             *t = *f;
251     }
252 }
253
254 class TestModel : public QListModelInterface
255 {
256     Q_OBJECT
257 public:
258     TestModel(QObject *parent = 0) : QListModelInterface(parent) {}
259     ~TestModel() {}
260
261     enum Roles { Name, Number };
262
263     QString name(int index) const { return list.at(index).first; }
264     QString number(int index) const { return list.at(index).second; }
265
266     int count() const { return list.count(); }
267
268     QList<int> roles() const { return QList<int>() << Name << Number; }
269     QString toString(int role) const {
270         switch (role) {
271         case Name:
272             return "name";
273         case Number:
274             return "number";
275         default:
276             return "";
277         }
278     }
279
280     QVariant data(int index, int role) const
281     {
282         if (role==0)
283             return list.at(index).first;
284         if (role==1)
285             return list.at(index).second;
286         return QVariant();
287     }
288     QHash<int, QVariant> data(int index, const QList<int> &roles) const {
289         QHash<int,QVariant> returnHash;
290
291         for (int i = 0; i < roles.size(); ++i) {
292             int role = roles.at(i);
293             QVariant info;
294             switch (role) {
295             case Name:
296                 info = list.at(index).first;
297                 break;
298             case Number:
299                 info = list.at(index).second;
300                 break;
301             default:
302                 break;
303             }
304             returnHash.insert(role, info);
305         }
306         return returnHash;
307     }
308
309     void addItem(const QString &name, const QString &number) {
310         list.append(QPair<QString,QString>(name, number));
311         emit itemsInserted(list.count()-1, 1);
312     }
313
314     void insertItem(int index, const QString &name, const QString &number) {
315         list.insert(index, QPair<QString,QString>(name, number));
316         emit itemsInserted(index, 1);
317     }
318
319     void insertItems(int index, const QList<QPair<QString, QString> > &items) {
320         for (int i=0; i<items.count(); i++)
321             list.insert(index + i, QPair<QString,QString>(items[i].first, items[i].second));
322         emit itemsInserted(index, items.count());
323     }
324
325     void removeItem(int index) {
326         list.removeAt(index);
327         emit itemsRemoved(index, 1);
328     }
329
330     void removeItems(int index, int count) {
331         int c = count;
332         while (c--)
333             list.removeAt(index);
334         emit itemsRemoved(index, count);
335     }
336
337     void moveItem(int from, int to) {
338         list.move(from, to);
339         emit itemsMoved(from, to, 1);
340     }
341
342     void moveItems(int from, int to, int count) {
343         tst_qquicklistview_move(from, to, count, &list);
344         emit itemsMoved(from, to, count);
345     }
346
347     void modifyItem(int index, const QString &name, const QString &number) {
348         list[index] = QPair<QString,QString>(name, number);
349         emit itemsChanged(index, 1, roles());
350     }
351
352     void clear() {
353         int count = list.count();
354         list.clear();
355         emit itemsRemoved(0, count);
356     }
357
358 private:
359     QList<QPair<QString,QString> > list;
360 };
361
362
363 class TestModel2 : public QAbstractListModel
364 {
365 public:
366     enum Roles { Name = Qt::UserRole+1, Number = Qt::UserRole+2 };
367
368     TestModel2(QObject *parent=0) : QAbstractListModel(parent) {
369         QHash<int, QByteArray> roles;
370         roles[Name] = "name";
371         roles[Number] = "number";
372         setRoleNames(roles);
373     }
374
375     int rowCount(const QModelIndex &parent=QModelIndex()) const { Q_UNUSED(parent); return list.count(); }
376     QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const {
377         QVariant rv;
378         if (role == Name)
379             rv = list.at(index.row()).first;
380         else if (role == Number)
381             rv = list.at(index.row()).second;
382
383         return rv;
384     }
385
386     int count() const { return rowCount(); }
387     QString name(int index) const { return list.at(index).first; }
388     QString number(int index) const { return list.at(index).second; }
389
390     void addItem(const QString &name, const QString &number) {
391         emit beginInsertRows(QModelIndex(), list.count(), list.count());
392         list.append(QPair<QString,QString>(name, number));
393         emit endInsertRows();
394     }
395
396     void addItems(const QList<QPair<QString, QString> > &items) {
397         emit beginInsertRows(QModelIndex(), list.count(), list.count()+items.count()-1);
398         for (int i=0; i<items.count(); i++)
399             list.append(QPair<QString,QString>(items[i].first, items[i].second));
400         emit endInsertRows();
401     }
402
403     void insertItem(int index, const QString &name, const QString &number) {
404         emit beginInsertRows(QModelIndex(), index, index);
405         list.insert(index, QPair<QString,QString>(name, number));
406         emit endInsertRows();
407     }
408
409     void insertItems(int index, const QList<QPair<QString, QString> > &items) {
410         emit beginInsertRows(QModelIndex(), index, index+items.count()-1);
411         for (int i=0; i<items.count(); i++)
412             list.insert(index + i, QPair<QString,QString>(items[i].first, items[i].second));
413         emit endInsertRows();
414     }
415
416     void removeItem(int index) {
417         emit beginRemoveRows(QModelIndex(), index, index);
418         list.removeAt(index);
419         emit endRemoveRows();
420     }
421
422     void removeItems(int index, int count) {
423         emit beginRemoveRows(QModelIndex(), index, index+count-1);
424         while (count--)
425             list.removeAt(index);
426         emit endRemoveRows();
427     }
428
429     void moveItem(int from, int to) {
430         emit beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
431         list.move(from, to);
432         emit endMoveRows();
433     }
434
435     void moveItems(int from, int to, int count) {
436         emit beginMoveRows(QModelIndex(), from, from+count-1, QModelIndex(), to > from ? to+count : to);
437         tst_qquicklistview_move(from, to, count, &list);
438         emit endMoveRows();
439     }
440
441     void modifyItem(int idx, const QString &name, const QString &number) {
442         list[idx] = QPair<QString,QString>(name, number);
443         emit dataChanged(index(idx,0), index(idx,0));
444     }
445
446     void clear() {
447         int count = list.count();
448         emit beginRemoveRows(QModelIndex(), 0, count-1);
449         list.clear();
450         emit endRemoveRows();
451     }
452
453 private:
454     QList<QPair<QString,QString> > list;
455 };
456
457 tst_QQuickListView::tst_QQuickListView()
458 {
459 }
460
461 template <class T>
462 void tst_QQuickListView::items(const QUrl &source, bool forceLayout)
463 {
464     QQuickView *canvas = createView();
465
466     T model;
467     model.addItem("Fred", "12345");
468     model.addItem("John", "2345");
469     model.addItem("Bob", "54321");
470
471     QDeclarativeContext *ctxt = canvas->rootContext();
472     ctxt->setContextProperty("testModel", &model);
473
474     TestObject *testObject = new TestObject;
475     ctxt->setContextProperty("testObject", testObject);
476
477     canvas->setSource(source);
478     qApp->processEvents();
479
480     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
481     QTRY_VERIFY(listview != 0);
482
483     QQuickItem *contentItem = listview->contentItem();
484     QTRY_VERIFY(contentItem != 0);
485
486     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
487     QTRY_VERIFY(testObject->error() == false);
488
489     QTRY_VERIFY(listview->highlightItem() != 0);
490     QTRY_COMPARE(listview->count(), model.count());
491     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
492     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
493
494     // current item should be first item
495     QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
496
497     for (int i = 0; i < model.count(); ++i) {
498         QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
499         QTRY_VERIFY(name != 0);
500         QTRY_COMPARE(name->text(), model.name(i));
501         QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
502         QTRY_VERIFY(number != 0);
503         QTRY_COMPARE(number->text(), model.number(i));
504     }
505
506     // switch to other delegate
507     testObject->setAnimate(true);
508     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
509     QTRY_VERIFY(testObject->error() == false);
510     QTRY_VERIFY(listview->currentItem());
511
512     // set invalid highlight
513     testObject->setInvalidHighlight(true);
514     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
515     QTRY_VERIFY(testObject->error() == false);
516     QTRY_VERIFY(listview->currentItem());
517     QTRY_VERIFY(listview->highlightItem() == 0);
518
519     // back to normal highlight
520     testObject->setInvalidHighlight(false);
521     QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
522     QTRY_VERIFY(testObject->error() == false);
523     QTRY_VERIFY(listview->currentItem());
524     QTRY_VERIFY(listview->highlightItem() != 0);
525
526     // set an empty model and confirm that items are destroyed
527     T model2;
528     ctxt->setContextProperty("testModel", &model2);
529
530     // Force a layout, necessary if ListView is completed before VisualDataModel.
531     if (forceLayout)
532         QCOMPARE(listview->property("count").toInt(), 0);
533
534     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
535     QTRY_VERIFY(itemCount == 0);
536
537     QTRY_COMPARE(listview->highlightResizeSpeed(), 1000.0);
538     QTRY_COMPARE(listview->highlightMoveSpeed(), 1000.0);
539
540     delete canvas;
541     delete testObject;
542 }
543
544
545 template <class T>
546 void tst_QQuickListView::changed(const QUrl &source, bool forceLayout)
547 {
548     QQuickView *canvas = createView();
549
550     T model;
551     model.addItem("Fred", "12345");
552     model.addItem("John", "2345");
553     model.addItem("Bob", "54321");
554
555     QDeclarativeContext *ctxt = canvas->rootContext();
556     ctxt->setContextProperty("testModel", &model);
557
558     TestObject *testObject = new TestObject;
559     ctxt->setContextProperty("testObject", testObject);
560
561     canvas->setSource(source);
562     qApp->processEvents();
563
564     QQuickFlickable *listview = findItem<QQuickFlickable>(canvas->rootObject(), "list");
565     QTRY_VERIFY(listview != 0);
566
567     QQuickItem *contentItem = listview->contentItem();
568     QTRY_VERIFY(contentItem != 0);
569
570     // Force a layout, necessary if ListView is completed before VisualDataModel.
571     if (forceLayout)
572         QCOMPARE(listview->property("count").toInt(), model.count());
573
574     model.modifyItem(1, "Will", "9876");
575     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
576     QTRY_VERIFY(name != 0);
577     QTRY_COMPARE(name->text(), model.name(1));
578     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
579     QTRY_VERIFY(number != 0);
580     QTRY_COMPARE(number->text(), model.number(1));
581
582     delete canvas;
583     delete testObject;
584 }
585
586 template <class T>
587 void tst_QQuickListView::inserted(const QUrl &source)
588 {
589     QQuickView *canvas = createView();
590     canvas->show();
591
592     T model;
593     model.addItem("Fred", "12345");
594     model.addItem("John", "2345");
595     model.addItem("Bob", "54321");
596
597     QDeclarativeContext *ctxt = canvas->rootContext();
598     ctxt->setContextProperty("testModel", &model);
599
600     TestObject *testObject = new TestObject;
601     ctxt->setContextProperty("testObject", testObject);
602
603     canvas->setSource(source);
604     //canvas->setSource(testFileUrl("listviewtest.qml")));
605     qApp->processEvents();
606
607     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
608     QTRY_VERIFY(listview != 0);
609
610     QQuickItem *contentItem = listview->contentItem();
611     QTRY_VERIFY(contentItem != 0);
612
613     model.insertItem(1, "Will", "9876");
614
615     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
616     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
617
618     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
619     QTRY_VERIFY(name != 0);
620     QTRY_COMPARE(name->text(), model.name(1));
621     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
622     QTRY_VERIFY(number != 0);
623     QTRY_COMPARE(number->text(), model.number(1));
624
625     // Confirm items positioned correctly
626     for (int i = 0; i < model.count(); ++i) {
627         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
628         QTRY_COMPARE(item->y(), i*20.0);
629     }
630
631     model.insertItem(0, "Foo", "1111"); // zero index, and current item
632
633     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
634     QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
635
636     name = findItem<QQuickText>(contentItem, "textName", 0);
637     QTRY_VERIFY(name != 0);
638     QTRY_COMPARE(name->text(), model.name(0));
639     number = findItem<QQuickText>(contentItem, "textNumber", 0);
640     QTRY_VERIFY(number != 0);
641     QTRY_COMPARE(number->text(), model.number(0));
642
643     QTRY_COMPARE(listview->currentIndex(), 1);
644
645     // Confirm items positioned correctly
646     for (int i = 0; i < model.count(); ++i) {
647         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
648         QTRY_COMPARE(item->y(), i*20.0);
649     }
650
651     for (int i = model.count(); i < 30; ++i)
652         model.insertItem(i, "Hello", QString::number(i));
653
654     listview->setContentY(80);
655
656     // Insert item outside visible area
657     model.insertItem(1, "Hello", "1324");
658
659     QTRY_VERIFY(listview->contentY() == 80);
660
661     // Confirm items positioned correctly
662     for (int i = 5; i < 5+15; ++i) {
663         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
664         if (!item) qWarning() << "Item" << i << "not found";
665         QTRY_VERIFY(item);
666         QTRY_COMPARE(item->y(), i*20.0 - 20.0);
667     }
668
669 //    QTRY_COMPARE(listview->contentItemHeight(), model.count() * 20.0);
670
671     // QTBUG-19675
672     model.clear();
673     model.insertItem(0, "Hello", "1234");
674     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
675
676     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
677     QVERIFY(item);
678     QCOMPARE(item->y(), 0.);
679     QVERIFY(listview->contentY() == 0);
680
681     delete canvas;
682     delete testObject;
683 }
684
685 template <class T>
686 void tst_QQuickListView::inserted_more()
687 {
688     QFETCH(qreal, contentY);
689     QFETCH(int, insertIndex);
690     QFETCH(int, insertCount);
691     QFETCH(qreal, itemsOffsetAfterMove);
692
693     QQuickText *name;
694     QQuickText *number;
695     QQuickView *canvas = createView();
696     canvas->show();
697
698     T model;
699     for (int i = 0; i < 30; i++)
700         model.addItem("Item" + QString::number(i), "");
701
702     QDeclarativeContext *ctxt = canvas->rootContext();
703     ctxt->setContextProperty("testModel", &model);
704
705     TestObject *testObject = new TestObject;
706     ctxt->setContextProperty("testObject", testObject);
707
708     canvas->setSource(testFileUrl("listviewtest.qml"));
709     qApp->processEvents();
710
711     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
712     QTRY_VERIFY(listview != 0);
713     QQuickItem *contentItem = listview->contentItem();
714     QTRY_VERIFY(contentItem != 0);
715
716     listview->setContentY(contentY);
717
718     QList<QPair<QString, QString> > newData;
719     for (int i=0; i<insertCount; i++)
720         newData << qMakePair(QString("value %1").arg(i), QString::number(i));
721     model.insertItems(insertIndex, newData);
722     QTRY_COMPARE(listview->property("count").toInt(), model.count());
723
724     // check visibleItems.first() is in correct position
725     QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper", 0);
726     QVERIFY(item0);
727     QCOMPARE(item0->y(), itemsOffsetAfterMove);
728
729     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
730     int firstVisibleIndex = -1;
731     for (int i=0; i<items.count(); i++) {
732         if (items[i]->y() >= contentY) {
733             QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
734             firstVisibleIndex = e.evaluate().toInt();
735             break;
736         }
737     }
738     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
739
740     // Confirm items positioned correctly and indexes correct
741     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
742     for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
743         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
744         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
745         QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove);
746         name = findItem<QQuickText>(contentItem, "textName", i);
747         QVERIFY(name != 0);
748         QTRY_COMPARE(name->text(), model.name(i));
749         number = findItem<QQuickText>(contentItem, "textNumber", i);
750         QVERIFY(number != 0);
751         QTRY_COMPARE(number->text(), model.number(i));
752     }
753
754     delete canvas;
755     delete testObject;
756 }
757
758 void tst_QQuickListView::inserted_more_data()
759 {
760     QTest::addColumn<qreal>("contentY");
761     QTest::addColumn<int>("insertIndex");
762     QTest::addColumn<int>("insertCount");
763     QTest::addColumn<qreal>("itemsOffsetAfterMove");
764
765     QTest::newRow("add 1, before visible items")
766             << 80.0     // show 4-19
767             << 3 << 1
768             << -20.0;   // insert above first visible i.e. 0 is at -20, first visible should not move
769
770     QTest::newRow("add multiple, before visible")
771             << 80.0     // show 4-19
772             << 3 << 3
773             << -20.0 * 3;   // again first visible should not move
774
775     QTest::newRow("add 1, at start of visible, content at start")
776             << 0.0
777             << 0 << 1
778             << 0.0;
779
780     QTest::newRow("add multiple, start of visible, content at start")
781             << 0.0
782             << 0 << 3
783             << 0.0;
784
785     QTest::newRow("add 1, at start of visible, content not at start")
786             << 80.0     // show 4-19
787             << 4 << 1
788             << 0.0;
789
790     QTest::newRow("add multiple, at start of visible, content not at start")
791             << 80.0     // show 4-19
792             << 4 << 3
793             << 0.0;
794
795
796     QTest::newRow("add 1, at end of visible, content at start")
797             << 0.0
798             << 15 << 1
799             << 0.0;
800
801     QTest::newRow("add 1, at end of visible, content at start")
802             << 0.0
803             << 15 << 3
804             << 0.0;
805
806     QTest::newRow("add 1, at end of visible, content not at start")
807             << 80.0     // show 4-19
808             << 19 << 1
809             << 0.0;
810
811     QTest::newRow("add multiple, at end of visible, content not at start")
812             << 80.0     // show 4-19
813             << 19 << 3
814             << 0.0;
815
816
817     QTest::newRow("add 1, after visible, content at start")
818             << 0.0
819             << 16 << 1
820             << 0.0;
821
822     QTest::newRow("add 1, after visible, content at start")
823             << 0.0
824             << 16 << 3
825             << 0.0;
826
827     QTest::newRow("add 1, after visible, content not at start")
828             << 80.0     // show 4-19
829             << 20 << 1
830             << 0.0;
831
832     QTest::newRow("add multiple, after visible, content not at start")
833             << 80.0     // show 4-19
834             << 20 << 3
835             << 0.0;
836 }
837
838 void tst_QQuickListView::insertBeforeVisible()
839 {
840     QFETCH(int, insertIndex);
841     QFETCH(int, insertCount);
842     QFETCH(int, cacheBuffer);
843
844     QQuickText *name;
845     QQuickView *canvas = createView();
846     canvas->show();
847
848     TestModel model;
849     for (int i = 0; i < 30; i++)
850         model.addItem("Item" + QString::number(i), "");
851
852     QDeclarativeContext *ctxt = canvas->rootContext();
853     ctxt->setContextProperty("testModel", &model);
854
855     TestObject *testObject = new TestObject;
856     ctxt->setContextProperty("testObject", testObject);
857
858     canvas->setSource(testFileUrl("listviewtest.qml"));
859     qApp->processEvents();
860
861     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
862     QTRY_VERIFY(listview != 0);
863     QQuickItem *contentItem = listview->contentItem();
864     QTRY_VERIFY(contentItem != 0);
865
866     listview->setCacheBuffer(cacheBuffer);
867
868     // trigger a refill (not just setting contentY) so that the visibleItems grid is updated
869     int firstVisibleIndex = 20;     // move to an index where the top item is not visible
870     listview->setContentY(firstVisibleIndex * 20.0);
871     listview->setCurrentIndex(firstVisibleIndex);
872     qApp->processEvents();
873     QTRY_COMPARE(listview->currentIndex(), firstVisibleIndex);
874     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", firstVisibleIndex);
875     QVERIFY(item);
876     QCOMPARE(item->y(), listview->contentY());
877
878     QList<QPair<QString, QString> > newData;
879     for (int i=0; i<insertCount; i++)
880         newData << qMakePair(QString("value %1").arg(i), QString::number(i));
881     model.insertItems(insertIndex, newData);
882     QTRY_COMPARE(listview->property("count").toInt(), model.count());
883
884     // now, moving to the top of the view should position the inserted items correctly
885     int itemsOffsetAfterMove = -(insertCount * 20);
886     listview->setCurrentIndex(0);
887     QTRY_COMPARE(listview->currentIndex(), 0);
888     QTRY_COMPARE(listview->contentY(), 0.0 + itemsOffsetAfterMove);
889
890     // Confirm items positioned correctly and indexes correct
891     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
892     for (int i = 0; i < model.count() && i < itemCount; ++i) {
893         item = findItem<QQuickItem>(contentItem, "wrapper", i);
894         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
895         QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove);
896         name = findItem<QQuickText>(contentItem, "textName", i);
897         QVERIFY(name != 0);
898         QTRY_COMPARE(name->text(), model.name(i));
899     }
900
901     delete canvas;
902     delete testObject;
903 }
904
905 void tst_QQuickListView::insertBeforeVisible_data()
906 {
907     QTest::addColumn<int>("insertIndex");
908     QTest::addColumn<int>("insertCount");
909     QTest::addColumn<int>("cacheBuffer");
910
911     QTest::newRow("insert 1 at 0, 0 buffer") << 0 << 1 << 0;
912     QTest::newRow("insert 1 at 0, 100 buffer") << 0 << 1 << 100;
913     QTest::newRow("insert 1 at 0, 500 buffer") << 0 << 1 << 500;
914
915     QTest::newRow("insert 1 at 1, 0 buffer") << 1 << 1 << 0;
916     QTest::newRow("insert 1 at 1, 100 buffer") << 1 << 1 << 100;
917     QTest::newRow("insert 1 at 1, 500 buffer") << 1 << 1 << 500;
918
919     QTest::newRow("insert multiple at 0, 0 buffer") << 0 << 3 << 0;
920     QTest::newRow("insert multiple at 0, 100 buffer") << 0 << 3 << 100;
921     QTest::newRow("insert multiple at 0, 500 buffer") << 0 << 3 << 500;
922
923     QTest::newRow("insert multiple at 1, 0 buffer") << 1 << 3 << 0;
924     QTest::newRow("insert multiple at 1, 100 buffer") << 1 << 3 << 100;
925     QTest::newRow("insert multiple at 1, 500 buffer") << 1 << 3 << 500;
926 }
927
928 template <class T>
929 void tst_QQuickListView::removed(const QUrl &source, bool /* animated */)
930 {
931     QQuickView *canvas = createView();
932
933     T model;
934     for (int i = 0; i < 50; i++)
935         model.addItem("Item" + QString::number(i), "");
936
937     QDeclarativeContext *ctxt = canvas->rootContext();
938     ctxt->setContextProperty("testModel", &model);
939
940     TestObject *testObject = new TestObject;
941     ctxt->setContextProperty("testObject", testObject);
942
943     canvas->setSource(source);
944     canvas->show();
945     qApp->processEvents();
946
947     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
948     QTRY_VERIFY(listview != 0);
949
950     QQuickItem *contentItem = listview->contentItem();
951     QTRY_VERIFY(contentItem != 0);
952
953     model.removeItem(1);
954     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
955
956     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
957     QTRY_VERIFY(name != 0);
958     QTRY_COMPARE(name->text(), model.name(1));
959     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
960     QTRY_VERIFY(number != 0);
961     QTRY_COMPARE(number->text(), model.number(1));
962
963     // Confirm items positioned correctly
964     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
965     for (int i = 0; i < model.count() && i < itemCount; ++i) {
966         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
967         if (!item) qWarning() << "Item" << i << "not found";
968         QTRY_VERIFY(item);
969         QTRY_VERIFY(item->y() == i*20);
970     }
971
972     // Remove first item (which is the current item);
973     model.removeItem(0);  // post: top item starts at 20
974     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
975
976     name = findItem<QQuickText>(contentItem, "textName", 0);
977     QTRY_VERIFY(name != 0);
978     QTRY_COMPARE(name->text(), model.name(0));
979     number = findItem<QQuickText>(contentItem, "textNumber", 0);
980     QTRY_VERIFY(number != 0);
981     QTRY_COMPARE(number->text(), model.number(0));
982
983     // Confirm items positioned correctly
984     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
985     for (int i = 0; i < model.count() && i < itemCount; ++i) {
986         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
987         if (!item) qWarning() << "Item" << i << "not found";
988         QTRY_VERIFY(item);
989         QTRY_COMPARE(item->y(),i*20.0 + 20.0);
990     }
991
992     // Remove items not visible
993     model.removeItem(18);
994     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
995
996     // Confirm items positioned correctly
997     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
998     for (int i = 0; i < model.count() && i < itemCount; ++i) {
999         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1000         if (!item) qWarning() << "Item" << i << "not found";
1001         QTRY_VERIFY(item);
1002         QTRY_COMPARE(item->y(),i*20.0+20.0);
1003     }
1004
1005     // Remove items before visible
1006     listview->setContentY(80);
1007     listview->setCurrentIndex(10);
1008
1009     model.removeItem(1); // post: top item will be at 40
1010     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
1011
1012     // Confirm items positioned correctly
1013     for (int i = 2; i < 18; ++i) {
1014         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1015         if (!item) qWarning() << "Item" << i << "not found";
1016         QTRY_VERIFY(item);
1017         QTRY_COMPARE(item->y(),40+i*20.0);
1018     }
1019
1020     // Remove current index
1021     QTRY_VERIFY(listview->currentIndex() == 9);
1022     QQuickItem *oldCurrent = listview->currentItem();
1023     model.removeItem(9);
1024
1025     QTRY_COMPARE(listview->currentIndex(), 9);
1026     QTRY_VERIFY(listview->currentItem() != oldCurrent);
1027
1028     listview->setContentY(40); // That's the top now
1029     // let transitions settle.
1030     QTest::qWait(300);
1031
1032     // Confirm items positioned correctly
1033     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1034     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1035         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1036         if (!item) qWarning() << "Item" << i << "not found";
1037         QTRY_VERIFY(item);
1038         QTRY_COMPARE(item->y(),40+i*20.0);
1039     }
1040
1041     // remove current item beyond visible items.
1042     listview->setCurrentIndex(20);
1043     listview->setContentY(40);
1044     model.removeItem(20);
1045
1046     QTRY_COMPARE(listview->currentIndex(), 20);
1047     QTRY_VERIFY(listview->currentItem() != 0);
1048
1049     // remove item before current, but visible
1050     listview->setCurrentIndex(8);
1051     oldCurrent = listview->currentItem();
1052     model.removeItem(6);
1053
1054     QTRY_COMPARE(listview->currentIndex(), 7);
1055     QTRY_VERIFY(listview->currentItem() == oldCurrent);
1056
1057     listview->setContentY(80);
1058     QTest::qWait(300);
1059
1060     // remove all visible items
1061     model.removeItems(1, 18);
1062     QTRY_COMPARE(listview->count() , model.count());
1063
1064     // Confirm items positioned correctly
1065     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1066     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
1067         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i+1);
1068         if (!item) qWarning() << "Item" << i+1 << "not found";
1069         QTRY_VERIFY(item);
1070         QTRY_COMPARE(item->y(),80+i*20.0);
1071     }
1072
1073     model.removeItems(1, 17);
1074     QTRY_COMPARE(listview->count() , model.count());
1075
1076     model.removeItems(2, 1);
1077     QTRY_COMPARE(listview->count() , model.count());
1078
1079     model.addItem("New", "1");
1080     QTRY_COMPARE(listview->count() , model.count());
1081
1082     QTRY_VERIFY(name = findItem<QQuickText>(contentItem, "textName", model.count()-1));
1083     QCOMPARE(name->text(), QString("New"));
1084
1085     // Add some more items so that we don't run out
1086     model.clear();
1087     for (int i = 0; i < 50; i++)
1088         model.addItem("Item" + QString::number(i), "");
1089
1090     // QTBUG-QTBUG-20575
1091     listview->setCurrentIndex(0);
1092     listview->setContentY(30);
1093     model.removeItem(0);
1094     QTRY_VERIFY(name = findItem<QQuickText>(contentItem, "textName", 0));
1095
1096     // QTBUG-19198 move to end and remove all visible items one at a time.
1097     listview->positionViewAtEnd();
1098     for (int i = 0; i < 18; ++i)
1099         model.removeItems(model.count() - 1, 1);
1100     QTRY_VERIFY(findItems<QQuickItem>(contentItem, "wrapper").count() > 16);
1101
1102     delete canvas;
1103     delete testObject;
1104 }
1105
1106 template <class T>
1107 void tst_QQuickListView::clear(const QUrl &source)
1108 {
1109     QQuickView *canvas = createView();
1110
1111     T model;
1112     for (int i = 0; i < 30; i++)
1113         model.addItem("Item" + QString::number(i), "");
1114
1115     QDeclarativeContext *ctxt = canvas->rootContext();
1116     ctxt->setContextProperty("testModel", &model);
1117
1118     TestObject *testObject = new TestObject;
1119     ctxt->setContextProperty("testObject", testObject);
1120
1121     canvas->setSource(source);
1122     qApp->processEvents();
1123
1124     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1125     QTRY_VERIFY(listview != 0);
1126
1127     QQuickItem *contentItem = listview->contentItem();
1128     QTRY_VERIFY(contentItem != 0);
1129
1130     model.clear();
1131
1132     QTRY_VERIFY(listview->count() == 0);
1133     QTRY_VERIFY(listview->currentItem() == 0);
1134     QTRY_VERIFY(listview->contentY() == 0);
1135     QVERIFY(listview->currentIndex() == -1);
1136
1137     // confirm sanity when adding an item to cleared list
1138     model.addItem("New", "1");
1139     QTRY_VERIFY(listview->count() == 1);
1140     QVERIFY(listview->currentItem() != 0);
1141     QVERIFY(listview->currentIndex() == 0);
1142
1143     delete canvas;
1144     delete testObject;
1145 }
1146
1147 template <class T>
1148 void tst_QQuickListView::moved(const QUrl &source)
1149 {
1150     QFETCH(qreal, contentY);
1151     QFETCH(int, from);
1152     QFETCH(int, to);
1153     QFETCH(int, count);
1154     QFETCH(qreal, itemsOffsetAfterMove);
1155
1156     QQuickText *name;
1157     QQuickText *number;
1158     QQuickView *canvas = createView();
1159     canvas->show();
1160
1161     T model;
1162     for (int i = 0; i < 30; i++)
1163         model.addItem("Item" + QString::number(i), "");
1164
1165     QDeclarativeContext *ctxt = canvas->rootContext();
1166     ctxt->setContextProperty("testModel", &model);
1167
1168     TestObject *testObject = new TestObject;
1169     ctxt->setContextProperty("testObject", testObject);
1170
1171     canvas->setSource(source);
1172     qApp->processEvents();
1173
1174     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1175     QTRY_VERIFY(listview != 0);
1176
1177     QQuickItem *contentItem = listview->contentItem();
1178     QTRY_VERIFY(contentItem != 0);
1179
1180     QQuickItem *currentItem = listview->currentItem();
1181     QTRY_VERIFY(currentItem != 0);
1182
1183     listview->setContentY(contentY);
1184     model.moveItems(from, to, count);
1185
1186     // wait for items to move
1187     QTest::qWait(100);
1188
1189     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
1190     int firstVisibleIndex = -1;
1191     for (int i=0; i<items.count(); i++) {
1192         if (items[i]->y() >= contentY) {
1193             QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
1194             firstVisibleIndex = e.evaluate().toInt();
1195             break;
1196         }
1197     }
1198     QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
1199
1200     // Confirm items positioned correctly and indexes correct
1201     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1202     for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
1203         if (i >= firstVisibleIndex + 16)    // index has moved out of view
1204             continue;
1205         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1206         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1207         QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove);
1208         name = findItem<QQuickText>(contentItem, "textName", i);
1209         QVERIFY(name != 0);
1210         QTRY_COMPARE(name->text(), model.name(i));
1211         number = findItem<QQuickText>(contentItem, "textNumber", i);
1212         QVERIFY(number != 0);
1213         QTRY_COMPARE(number->text(), model.number(i));
1214
1215         // current index should have been updated
1216         if (item == currentItem)
1217             QTRY_COMPARE(listview->currentIndex(), i);
1218     }
1219
1220     delete canvas;
1221     delete testObject;
1222 }
1223
1224 void tst_QQuickListView::moved_data()
1225 {
1226     QTest::addColumn<qreal>("contentY");
1227     QTest::addColumn<int>("from");
1228     QTest::addColumn<int>("to");
1229     QTest::addColumn<int>("count");
1230     QTest::addColumn<qreal>("itemsOffsetAfterMove");
1231
1232     // model starts with 30 items, each 20px high, in area 320px high
1233     // 16 items should be visible at a time
1234     // itemsOffsetAfterMove should be > 0 whenever items above the visible pos have moved
1235
1236     QTest::newRow("move 1 forwards, within visible items")
1237             << 0.0
1238             << 1 << 4 << 1
1239             << 0.0;
1240
1241     QTest::newRow("move 1 forwards, from non-visible -> visible")
1242             << 80.0     // show 4-19
1243             << 1 << 18 << 1
1244             << 20.0;    // removed 1 item above the first visible, so item 0 should drop down by 1 to minimize movement
1245
1246     QTest::newRow("move 1 forwards, from non-visible -> visible (move first item)")
1247             << 80.0     // show 4-19
1248             << 0 << 4 << 1
1249             << 20.0;    // first item has moved to below item4, everything drops down by size of 1 item
1250
1251     QTest::newRow("move 1 forwards, from visible -> non-visible")
1252             << 0.0
1253             << 1 << 16 << 1
1254             << 0.0;
1255
1256     QTest::newRow("move 1 forwards, from visible -> non-visible (move first item)")
1257             << 0.0
1258             << 0 << 16 << 1
1259             << 0.0;
1260
1261
1262     QTest::newRow("move 1 backwards, within visible items")
1263             << 0.0
1264             << 4 << 1 << 1
1265             << 0.0;
1266
1267     QTest::newRow("move 1 backwards, within visible items (to first index)")
1268             << 0.0
1269             << 4 << 0 << 1
1270             << 0.0;
1271
1272     QTest::newRow("move 1 backwards, from non-visible -> visible")
1273             << 0.0
1274             << 20 << 4 << 1
1275             << 0.0;
1276
1277     QTest::newRow("move 1 backwards, from non-visible -> visible (move last item)")
1278             << 0.0
1279             << 29 << 15 << 1
1280             << 0.0;
1281
1282     QTest::newRow("move 1 backwards, from visible -> non-visible")
1283             << 80.0     // show 4-19
1284             << 16 << 1 << 1
1285             << -20.0;   // to minimize movement, item 0 moves to -20, and other items do not move
1286
1287     QTest::newRow("move 1 backwards, from visible -> non-visible (move first item)")
1288             << 80.0     // show 4-19
1289             << 16 << 0 << 1
1290             << -20.0;   // to minimize movement, item 16 (now at 0) moves to -20, and other items do not move
1291
1292
1293     QTest::newRow("move multiple forwards, within visible items")
1294             << 0.0
1295             << 0 << 5 << 3
1296             << 0.0;
1297
1298     QTest::newRow("move multiple forwards, before visible items")
1299             << 140.0     // show 7-22
1300             << 4 << 5 << 3      // 4,5,6 move to below 7
1301             << 20.0 * 3;      // 4,5,6 moved down
1302
1303     QTest::newRow("move multiple forwards, from non-visible -> visible")
1304             << 80.0     // show 4-19
1305             << 1 << 5 << 3
1306             << 20.0 * 3;    // moving 3 from above the content y should adjust y positions accordingly
1307
1308     QTest::newRow("move multiple forwards, from non-visible -> visible (move first item)")
1309             << 80.0     // show 4-19
1310             << 0 << 5 << 3
1311             << 20.0 * 3;        // moving 3 from above the content y should adjust y positions accordingly
1312
1313     QTest::newRow("move multiple forwards, from visible -> non-visible")
1314             << 0.0
1315             << 1 << 16 << 3
1316             << 0.0;
1317
1318     QTest::newRow("move multiple forwards, from visible -> non-visible (move first item)")
1319             << 0.0
1320             << 0 << 16 << 3
1321             << 0.0;
1322
1323
1324     QTest::newRow("move multiple backwards, within visible items")
1325             << 0.0
1326             << 4 << 1 << 3
1327             << 0.0;
1328
1329     QTest::newRow("move multiple backwards, within visible items (move first item)")
1330             << 0.0
1331             << 10 << 0 << 3
1332             << 0.0;
1333
1334     QTest::newRow("move multiple backwards, from non-visible -> visible")
1335             << 0.0
1336             << 20 << 4 << 3
1337             << 0.0;
1338
1339     QTest::newRow("move multiple backwards, from non-visible -> visible (move last item)")
1340             << 0.0
1341             << 27 << 10 << 3
1342             << 0.0;
1343
1344     QTest::newRow("move multiple backwards, from visible -> non-visible")
1345             << 80.0     // show 4-19
1346             << 16 << 1 << 3
1347             << -20.0 * 3;   // to minimize movement, 0 moves by -60, and other items do not move
1348
1349     QTest::newRow("move multiple backwards, from visible -> non-visible (move first item)")
1350             << 80.0     // show 4-19
1351             << 16 << 0 << 3
1352             << -20.0 * 3;   // to minimize movement, 16,17,18 move to above item 0, and other items do not move
1353 }
1354
1355
1356 struct ListChange {
1357     enum { Inserted, Removed, Moved, SetCurrent } type;
1358     int index;
1359     int count;
1360     int to;     // Move
1361
1362     static ListChange insert(int index, int count = 1) { ListChange c = { Inserted, index, count, -1 }; return c; }
1363     static ListChange remove(int index, int count = 1) { ListChange c = { Removed, index, count, -1 }; return c; }
1364     static ListChange move(int index, int to, int count) { ListChange c = { Moved, index, count, to }; return c; }
1365     static ListChange setCurrent(int index) { ListChange c = { SetCurrent, index, -1, -1 }; return c; }
1366 };
1367 Q_DECLARE_METATYPE(QList<ListChange>)
1368
1369 void tst_QQuickListView::multipleChanges()
1370 {
1371     QFETCH(int, startCount);
1372     QFETCH(QList<ListChange>, changes);
1373     QFETCH(int, newCount);
1374     QFETCH(int, newCurrentIndex);
1375
1376     QQuickView *canvas = createView();
1377     canvas->show();
1378
1379     TestModel model;
1380     for (int i = 0; i < startCount; i++)
1381         model.addItem("Item" + QString::number(i), "");
1382
1383     QDeclarativeContext *ctxt = canvas->rootContext();
1384     ctxt->setContextProperty("testModel", &model);
1385
1386     TestObject *testObject = new TestObject;
1387     ctxt->setContextProperty("testObject", testObject);
1388
1389     canvas->setSource(testFileUrl("listviewtest.qml"));
1390     qApp->processEvents();
1391
1392     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1393     QTRY_VERIFY(listview != 0);
1394
1395     for (int i=0; i<changes.count(); i++) {
1396         switch (changes[i].type) {
1397             case ListChange::Inserted:
1398             {
1399                 QList<QPair<QString, QString> > items;
1400                 for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
1401                     items << qMakePair(QString("new item " + j), QString::number(j));
1402                 model.insertItems(changes[i].index, items);
1403                 break;
1404             }
1405             case ListChange::Removed:
1406                 model.removeItems(changes[i].index, changes[i].count);
1407                 break;
1408             case ListChange::Moved:
1409                 model.moveItems(changes[i].index, changes[i].to, changes[i].count);
1410                 break;
1411             case ListChange::SetCurrent:
1412                 listview->setCurrentIndex(changes[i].index);
1413                 break;
1414         }
1415     }
1416
1417     QTRY_COMPARE(listview->count(), newCount);
1418     QCOMPARE(listview->count(), model.count());
1419     QTRY_COMPARE(listview->currentIndex(), newCurrentIndex);
1420
1421     QQuickText *name;
1422     QQuickText *number;
1423     QQuickItem *contentItem = listview->contentItem();
1424     QTRY_VERIFY(contentItem != 0);
1425     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1426     for (int i=0; i < model.count() && i < itemCount; ++i) {
1427         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1428         QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1429         name = findItem<QQuickText>(contentItem, "textName", i);
1430         QVERIFY(name != 0);
1431         QTRY_COMPARE(name->text(), model.name(i));
1432         number = findItem<QQuickText>(contentItem, "textNumber", i);
1433         QVERIFY(number != 0);
1434         QTRY_COMPARE(number->text(), model.number(i));
1435     }
1436
1437     delete testObject;
1438     delete canvas;
1439 }
1440
1441 void tst_QQuickListView::multipleChanges_data()
1442 {
1443     QTest::addColumn<int>("startCount");
1444     QTest::addColumn<QList<ListChange> >("changes");
1445     QTest::addColumn<int>("newCount");
1446     QTest::addColumn<int>("newCurrentIndex");
1447
1448     QList<ListChange> changes;
1449
1450     for (int i=1; i<30; i++)
1451         changes << ListChange::remove(0);
1452     QTest::newRow("remove all but 1, first->last") << 30 << changes << 1 << 0;
1453
1454     changes << ListChange::remove(0);
1455     QTest::newRow("remove all") << 30 << changes << 0 << -1;
1456
1457     changes.clear();
1458     changes << ListChange::setCurrent(29);
1459     for (int i=29; i>0; i--)
1460         changes << ListChange::remove(i);
1461     QTest::newRow("remove last (current) -> first") << 30 << changes << 1 << 0;
1462
1463     QTest::newRow("remove then insert at 0") << 10 << (QList<ListChange>()
1464             << ListChange::remove(0, 1)
1465             << ListChange::insert(0, 1)
1466             ) << 10 << 1;
1467
1468     QTest::newRow("remove then insert at non-zero index") << 10 << (QList<ListChange>()
1469             << ListChange::setCurrent(2)
1470             << ListChange::remove(2, 1)
1471             << ListChange::insert(2, 1)
1472             ) << 10 << 3;
1473
1474     QTest::newRow("remove current then insert below it") << 10 << (QList<ListChange>()
1475             << ListChange::setCurrent(1)
1476             << ListChange::remove(1, 3)
1477             << ListChange::insert(2, 2)
1478             ) << 9 << 1;
1479
1480     QTest::newRow("remove current index then move it down") << 10 << (QList<ListChange>()
1481             << ListChange::setCurrent(2)
1482             << ListChange::remove(1, 3)
1483             << ListChange::move(1, 5, 1)
1484             ) << 7 << 5;
1485
1486     QTest::newRow("remove current index then move it up") << 10 << (QList<ListChange>()
1487             << ListChange::setCurrent(5)
1488             << ListChange::remove(4, 3)
1489             << ListChange::move(4, 1, 1)
1490             ) << 7 << 1;
1491
1492
1493     QTest::newRow("insert multiple times") << 0 << (QList<ListChange>()
1494             << ListChange::insert(0, 2)
1495             << ListChange::insert(0, 4)
1496             << ListChange::insert(0, 6)
1497             ) << 12 << 10;
1498
1499     QTest::newRow("insert multiple times with current index changes") << 0 << (QList<ListChange>()
1500             << ListChange::insert(0, 2)
1501             << ListChange::insert(0, 4)
1502             << ListChange::insert(0, 6)
1503             << ListChange::setCurrent(3)
1504             << ListChange::insert(3, 2)
1505             ) << 14 << 5;
1506
1507     QTest::newRow("insert and remove all") << 0 << (QList<ListChange>()
1508             << ListChange::insert(0, 30)
1509             << ListChange::remove(0, 30)
1510             ) << 0 << -1;
1511
1512     QTest::newRow("insert and remove current") << 30 << (QList<ListChange>()
1513             << ListChange::insert(1)
1514             << ListChange::setCurrent(1)
1515             << ListChange::remove(1)
1516             ) << 30 << 1;
1517
1518     QTest::newRow("insert before 0, then remove cross section of new and old items") << 10 << (QList<ListChange>()
1519             << ListChange::insert(0, 10)
1520             << ListChange::remove(5, 10)
1521             ) << 10 << 5;
1522
1523     QTest::newRow("insert multiple, then move new items to end") << 10 << (QList<ListChange>()
1524             << ListChange::insert(0, 3)
1525             << ListChange::move(0, 10, 3)
1526             ) << 13 << 0;
1527
1528     QTest::newRow("insert multiple, then move new and some old items to end") << 10 << (QList<ListChange>()
1529             << ListChange::insert(0, 3)
1530             << ListChange::move(0, 8, 5)
1531             ) << 13 << 11;
1532
1533     QTest::newRow("insert multiple at end, then move new and some old items to start") << 10 << (QList<ListChange>()
1534             << ListChange::setCurrent(9)
1535             << ListChange::insert(10, 3)
1536             << ListChange::move(8, 0, 5)
1537             ) << 13 << 1;
1538
1539
1540     QTest::newRow("move back and forth to same index") << 10 << (QList<ListChange>()
1541             << ListChange::setCurrent(1)
1542             << ListChange::move(1, 2, 2)
1543             << ListChange::move(2, 1, 2)
1544             ) << 10 << 1;
1545
1546     QTest::newRow("move forwards then back") << 10 << (QList<ListChange>()
1547             << ListChange::setCurrent(2)
1548             << ListChange::move(1, 2, 3)
1549             << ListChange::move(3, 0, 5)
1550             ) << 10 << 0;
1551
1552     QTest::newRow("move current, then remove it") << 10 << (QList<ListChange>()
1553             << ListChange::setCurrent(5)
1554             << ListChange::move(5, 0, 1)
1555             << ListChange::remove(0)
1556             ) << 9 << 0;
1557
1558     QTest::newRow("move current, then insert before it") << 10 << (QList<ListChange>()
1559             << ListChange::setCurrent(5)
1560             << ListChange::move(5, 0, 1)
1561             << ListChange::insert(0)
1562             ) << 11 << 1;
1563
1564     QTest::newRow("move multiple, then remove them") << 10 << (QList<ListChange>()
1565             << ListChange::setCurrent(1)
1566             << ListChange::move(5, 1, 3)
1567             << ListChange::remove(1, 3)
1568             ) << 7 << 1;
1569
1570     QTest::newRow("move multiple, then insert before them") << 10 << (QList<ListChange>()
1571             << ListChange::setCurrent(5)
1572             << ListChange::move(5, 1, 3)
1573             << ListChange::insert(1, 5)
1574             ) << 15 << 6;
1575
1576     QTest::newRow("move multiple, then insert after them") << 10 << (QList<ListChange>()
1577             << ListChange::setCurrent(3)
1578             << ListChange::move(0, 1, 2)
1579             << ListChange::insert(3, 5)
1580             ) << 15 << 8;
1581
1582
1583     QTest::newRow("clear current") << 0 << (QList<ListChange>()
1584             << ListChange::insert(0, 5)
1585             << ListChange::setCurrent(-1)
1586             << ListChange::remove(0, 5)
1587             << ListChange::insert(0, 5)
1588             ) << 5 << -1;
1589 }
1590
1591 void tst_QQuickListView::swapWithFirstItem()
1592 {
1593     QQuickView *canvas = createView();
1594     canvas->show();
1595
1596     TestModel model;
1597     for (int i = 0; i < 30; i++)
1598         model.addItem("Item" + QString::number(i), "");
1599
1600     QDeclarativeContext *ctxt = canvas->rootContext();
1601     ctxt->setContextProperty("testModel", &model);
1602
1603     TestObject *testObject = new TestObject;
1604     ctxt->setContextProperty("testObject", testObject);
1605
1606     canvas->setSource(testFileUrl("listviewtest.qml"));
1607     qApp->processEvents();
1608
1609     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1610     QTRY_VERIFY(listview != 0);
1611
1612     // ensure content position is stable
1613     listview->setContentY(0);
1614     model.moveItem(1, 0);
1615     QTRY_VERIFY(listview->contentY() == 0);
1616
1617     delete testObject;
1618     delete canvas;
1619 }
1620
1621 void tst_QQuickListView::enforceRange()
1622 {
1623     QQuickView *canvas = createView();
1624
1625     TestModel model;
1626     for (int i = 0; i < 30; i++)
1627         model.addItem("Item" + QString::number(i), "");
1628
1629     QDeclarativeContext *ctxt = canvas->rootContext();
1630     ctxt->setContextProperty("testModel", &model);
1631
1632     canvas->setSource(testFileUrl("listview-enforcerange.qml"));
1633     qApp->processEvents();
1634
1635     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1636     QTRY_VERIFY(listview != 0);
1637
1638     QTRY_COMPARE(listview->preferredHighlightBegin(), 100.0);
1639     QTRY_COMPARE(listview->preferredHighlightEnd(), 100.0);
1640     QTRY_COMPARE(listview->highlightRangeMode(), QQuickListView::StrictlyEnforceRange);
1641
1642     QQuickItem *contentItem = listview->contentItem();
1643     QTRY_VERIFY(contentItem != 0);
1644
1645     // view should be positioned at the top of the range.
1646     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
1647     QTRY_VERIFY(item);
1648     QTRY_COMPARE(listview->contentY(), -100.0);
1649
1650     QQuickText *name = findItem<QQuickText>(contentItem, "textName", 0);
1651     QTRY_VERIFY(name != 0);
1652     QTRY_COMPARE(name->text(), model.name(0));
1653     QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 0);
1654     QTRY_VERIFY(number != 0);
1655     QTRY_COMPARE(number->text(), model.number(0));
1656
1657     // Check currentIndex is updated when contentItem moves
1658     listview->setContentY(20);
1659
1660     QTRY_COMPARE(listview->currentIndex(), 6);
1661
1662     // change model
1663     TestModel model2;
1664     for (int i = 0; i < 5; i++)
1665         model2.addItem("Item" + QString::number(i), "");
1666
1667     ctxt->setContextProperty("testModel", &model2);
1668     QCOMPARE(listview->count(), 5);
1669
1670     delete canvas;
1671 }
1672
1673 void tst_QQuickListView::enforceRange_withoutHighlight()
1674 {
1675     // QTBUG-20287
1676     // If no highlight is set but StrictlyEnforceRange is used, the content should still move
1677     // to the correct position (i.e. to the next/previous item, not next/previous section)
1678     // when moving up/down via incrementCurrentIndex() and decrementCurrentIndex()
1679
1680     QQuickView *canvas = createView();
1681     canvas->show();
1682     QTest::qWait(200);
1683
1684     TestModel model;
1685     model.addItem("Item 0", "a");
1686     model.addItem("Item 1", "b");
1687     model.addItem("Item 2", "b");
1688     model.addItem("Item 3", "c");
1689
1690     QDeclarativeContext *ctxt = canvas->rootContext();
1691     ctxt->setContextProperty("testModel", &model);
1692
1693     canvas->setSource(testFileUrl("listview-enforcerange-nohighlight.qml"));
1694     qApp->processEvents();
1695
1696     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1697     QTRY_VERIFY(listview != 0);
1698
1699     qreal expectedPos = -100.0;
1700
1701     expectedPos += 10.0;    // scroll past 1st section's delegate (10px height)
1702     QTRY_COMPARE(listview->contentY(), expectedPos);
1703
1704     expectedPos += 20 + 10;     // scroll past 1st section and section delegate of 2nd section
1705     QTest::keyClick(canvas, Qt::Key_Down);
1706
1707     QTRY_COMPARE(listview->contentY(), expectedPos);
1708
1709     expectedPos += 20;     // scroll past 1st item of 2nd section
1710     QTest::keyClick(canvas, Qt::Key_Down);
1711     QTRY_COMPARE(listview->contentY(), expectedPos);
1712
1713     expectedPos += 20 + 10;     // scroll past 2nd item of 2nd section and section delegate of 3rd section
1714     QTest::keyClick(canvas, Qt::Key_Down);
1715     QTRY_COMPARE(listview->contentY(), expectedPos);
1716
1717     delete canvas;
1718 }
1719
1720 void tst_QQuickListView::spacing()
1721 {
1722     QQuickView *canvas = createView();
1723     canvas->show();
1724
1725     TestModel model;
1726     for (int i = 0; i < 30; i++)
1727         model.addItem("Item" + QString::number(i), "");
1728
1729     QDeclarativeContext *ctxt = canvas->rootContext();
1730     ctxt->setContextProperty("testModel", &model);
1731
1732     TestObject *testObject = new TestObject;
1733     ctxt->setContextProperty("testObject", testObject);
1734
1735     canvas->setSource(testFileUrl("listviewtest.qml"));
1736     qApp->processEvents();
1737
1738     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1739     QTRY_VERIFY(listview != 0);
1740
1741     QQuickItem *contentItem = listview->contentItem();
1742     QTRY_VERIFY(contentItem != 0);
1743
1744     // Confirm items positioned correctly
1745     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1746     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1747         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1748         if (!item) qWarning() << "Item" << i << "not found";
1749         QTRY_VERIFY(item);
1750         QTRY_VERIFY(item->y() == i*20);
1751     }
1752
1753     listview->setSpacing(10);
1754     QTRY_VERIFY(listview->spacing() == 10);
1755
1756     // Confirm items positioned correctly
1757     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1758     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1759         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1760         if (!item) qWarning() << "Item" << i << "not found";
1761         QTRY_VERIFY(item);
1762         QTRY_VERIFY(item->y() == i*30);
1763     }
1764
1765     listview->setSpacing(0);
1766
1767     // Confirm items positioned correctly
1768     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1769     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1770         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1771         if (!item) qWarning() << "Item" << i << "not found";
1772         QTRY_VERIFY(item);
1773         QTRY_COMPARE(item->y(), i*20.0);
1774     }
1775
1776     delete canvas;
1777     delete testObject;
1778 }
1779
1780 template <typename T>
1781 void tst_QQuickListView::sections(const QUrl &source)
1782 {
1783     QQuickView *canvas = createView();
1784     canvas->show();
1785
1786     T model;
1787     for (int i = 0; i < 30; i++)
1788         model.addItem("Item" + QString::number(i), QString::number(i/5));
1789
1790     QDeclarativeContext *ctxt = canvas->rootContext();
1791     ctxt->setContextProperty("testModel", &model);
1792
1793     canvas->setSource(source);
1794     qApp->processEvents();
1795
1796     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1797     QTRY_VERIFY(listview != 0);
1798
1799     QQuickItem *contentItem = listview->contentItem();
1800     QTRY_VERIFY(contentItem != 0);
1801
1802     // Confirm items positioned correctly
1803     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1804     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1805         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1806         QTRY_VERIFY(item);
1807         QTRY_COMPARE(item->y(), qreal(i*20 + ((i+4)/5) * 20));
1808         QQuickText *next = findItem<QQuickText>(item, "nextSection");
1809         QCOMPARE(next->text().toInt(), (i+1)/5);
1810     }
1811
1812     QSignalSpy currentSectionChangedSpy(listview, SIGNAL(currentSectionChanged()));
1813
1814     // Remove section boundary
1815     model.removeItem(5);
1816     QTRY_COMPARE(listview->count(), model.count());
1817
1818     // New section header created
1819     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 5);
1820     QTRY_VERIFY(item);
1821     QTRY_COMPARE(item->height(), 40.0);
1822
1823     model.insertItem(3, "New Item", "0");
1824     QTRY_COMPARE(listview->count(), model.count());
1825
1826     // Section header moved
1827     item = findItem<QQuickItem>(contentItem, "wrapper", 5);
1828     QTRY_VERIFY(item);
1829     QTRY_COMPARE(item->height(), 20.0);
1830
1831     item = findItem<QQuickItem>(contentItem, "wrapper", 6);
1832     QTRY_VERIFY(item);
1833     QTRY_COMPARE(item->height(), 40.0);
1834
1835     // insert item which will become a section header
1836     model.insertItem(6, "Replace header", "1");
1837     QTRY_COMPARE(listview->count(), model.count());
1838
1839     item = findItem<QQuickItem>(contentItem, "wrapper", 6);
1840     QTRY_VERIFY(item);
1841     QTRY_COMPARE(item->height(), 40.0);
1842
1843     item = findItem<QQuickItem>(contentItem, "wrapper", 7);
1844     QTRY_VERIFY(item);
1845     QTRY_COMPARE(item->height(), 20.0);
1846
1847     QTRY_COMPARE(listview->currentSection(), QString("0"));
1848
1849     listview->setContentY(140);
1850     QTRY_COMPARE(listview->currentSection(), QString("1"));
1851
1852     QTRY_COMPARE(currentSectionChangedSpy.count(), 1);
1853
1854     listview->setContentY(20);
1855     QTRY_COMPARE(listview->currentSection(), QString("0"));
1856
1857     QTRY_COMPARE(currentSectionChangedSpy.count(), 2);
1858
1859     item = findItem<QQuickItem>(contentItem, "wrapper", 1);
1860     QTRY_VERIFY(item);
1861     QTRY_COMPARE(item->height(), 20.0);
1862
1863     // check that headers change when item changes
1864     listview->setContentY(0);
1865     model.modifyItem(0, "changed", "2");
1866     QTest::qWait(300);
1867
1868     item = findItem<QQuickItem>(contentItem, "wrapper", 1);
1869     QTRY_VERIFY(item);
1870     QTRY_COMPARE(item->height(), 40.0);
1871
1872     delete canvas;
1873 }
1874
1875 void tst_QQuickListView::sectionsDelegate()
1876 {
1877     QQuickView *canvas = createView();
1878     canvas->show();
1879
1880     TestModel model;
1881     for (int i = 0; i < 30; i++)
1882         model.addItem("Item" + QString::number(i), QString::number(i/5));
1883
1884     QDeclarativeContext *ctxt = canvas->rootContext();
1885     ctxt->setContextProperty("testModel", &model);
1886
1887     canvas->setSource(testFileUrl("listview-sections_delegate.qml"));
1888     qApp->processEvents();
1889
1890     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1891     QTRY_VERIFY(listview != 0);
1892
1893     QQuickItem *contentItem = listview->contentItem();
1894     QTRY_VERIFY(contentItem != 0);
1895
1896     // Confirm items positioned correctly
1897     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1898     for (int i = 0; i < model.count() && i < itemCount; ++i) {
1899         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1900         QTRY_VERIFY(item);
1901         QTRY_COMPARE(item->y(), qreal(i*20 + ((i+5)/5) * 20));
1902         QQuickText *next = findItem<QQuickText>(item, "nextSection");
1903         QCOMPARE(next->text().toInt(), (i+1)/5);
1904     }
1905
1906     for (int i = 0; i < 3; ++i) {
1907         QQuickItem *item = findItem<QQuickItem>(contentItem, "sect_" + QString::number(i));
1908         QVERIFY(item);
1909         QTRY_COMPARE(item->y(), qreal(i*20*6));
1910     }
1911
1912     model.modifyItem(0, "One", "aaa");
1913     model.modifyItem(1, "Two", "aaa");
1914     model.modifyItem(2, "Three", "aaa");
1915     model.modifyItem(3, "Four", "aaa");
1916     model.modifyItem(4, "Five", "aaa");
1917     QTest::qWait(300);
1918
1919     for (int i = 0; i < 3; ++i) {
1920         QQuickItem *item = findItem<QQuickItem>(contentItem,
1921                 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
1922         QVERIFY(item);
1923         QTRY_COMPARE(item->y(), qreal(i*20*6));
1924     }
1925
1926     // remove section boundary
1927     model.removeItem(5);
1928     QTRY_COMPARE(listview->count(), model.count());
1929     for (int i = 0; i < 3; ++i) {
1930         QQuickItem *item = findItem<QQuickItem>(contentItem,
1931                 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
1932         QVERIFY(item);
1933     }
1934
1935     // QTBUG-17606
1936     QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "sect_1");
1937     QCOMPARE(items.count(), 1);
1938
1939     // QTBUG-17759
1940     model.modifyItem(0, "One", "aaa");
1941     model.modifyItem(1, "One", "aaa");
1942     model.modifyItem(2, "One", "aaa");
1943     model.modifyItem(3, "Four", "aaa");
1944     model.modifyItem(4, "Four", "aaa");
1945     model.modifyItem(5, "Four", "aaa");
1946     model.modifyItem(6, "Five", "aaa");
1947     model.modifyItem(7, "Five", "aaa");
1948     model.modifyItem(8, "Five", "aaa");
1949     model.modifyItem(9, "Two", "aaa");
1950     model.modifyItem(10, "Two", "aaa");
1951     model.modifyItem(11, "Two", "aaa");
1952     QTRY_COMPARE(findItems<QQuickItem>(contentItem, "sect_aaa").count(), 1);
1953     canvas->rootObject()->setProperty("sectionProperty", "name");
1954     // ensure view has settled.
1955     QTRY_COMPARE(findItems<QQuickItem>(contentItem, "sect_Four").count(), 1);
1956     for (int i = 0; i < 4; ++i) {
1957         QQuickItem *item = findItem<QQuickItem>(contentItem,
1958                 "sect_" + model.name(i*3));
1959         QVERIFY(item);
1960         QTRY_COMPARE(item->y(), qreal(i*20*4));
1961     }
1962
1963     // QTBUG-17769
1964     model.removeItems(10, 20);
1965     // ensure view has settled.
1966     QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper").count(), 10);
1967     // Drag view up beyond bounds
1968     QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(20,20));
1969     {
1970         QMouseEvent mv(QEvent::MouseMove, QPoint(20,0), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1971         QApplication::sendEvent(canvas, &mv);
1972     }
1973     {
1974         QMouseEvent mv(QEvent::MouseMove, QPoint(20,-50), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1975         QApplication::sendEvent(canvas, &mv);
1976     }
1977     {
1978         QMouseEvent mv(QEvent::MouseMove, QPoint(20,-200), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1979         QApplication::sendEvent(canvas, &mv);
1980     }
1981     QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(20,-200));
1982     // view should settle back at 0
1983     QTRY_COMPARE(listview->contentY(), 0.0);
1984
1985     delete canvas;
1986 }
1987
1988 void tst_QQuickListView::sectionsPositioning()
1989 {
1990     QQuickView *canvas = createView();
1991     canvas->show();
1992
1993     TestModel model;
1994     for (int i = 0; i < 30; i++)
1995         model.addItem("Item" + QString::number(i), QString::number(i/5));
1996
1997     QDeclarativeContext *ctxt = canvas->rootContext();
1998     ctxt->setContextProperty("testModel", &model);
1999
2000     canvas->setSource(testFileUrl("listview-sections_delegate.qml"));
2001     qApp->processEvents();
2002     canvas->rootObject()->setProperty("sectionPositioning", QVariant(int(QQuickViewSection::InlineLabels | QQuickViewSection::CurrentLabelAtStart | QQuickViewSection::NextLabelAtEnd)));
2003
2004     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2005     QTRY_VERIFY(listview != 0);
2006
2007     QQuickItem *contentItem = listview->contentItem();
2008     QTRY_VERIFY(contentItem != 0);
2009
2010     for (int i = 0; i < 3; ++i) {
2011         QQuickItem *item = findItem<QQuickItem>(contentItem, "sect_" + QString::number(i));
2012         QVERIFY(item);
2013         QTRY_COMPARE(item->y(), qreal(i*20*6));
2014     }
2015
2016     QQuickItem *topItem = findVisibleChild(contentItem, "sect_0"); // section header
2017     QVERIFY(topItem);
2018     QCOMPARE(topItem->y(), 0.);
2019
2020     QQuickItem *bottomItem = findVisibleChild(contentItem, "sect_3"); // section footer
2021     QVERIFY(bottomItem);
2022     QCOMPARE(bottomItem->y(), 300.);
2023
2024     // move down a little and check that section header is at top
2025     listview->setContentY(10);
2026     QCOMPARE(topItem->y(), 0.);
2027
2028     // push the top header up
2029     listview->setContentY(110);
2030     topItem = findVisibleChild(contentItem, "sect_0"); // section header
2031     QVERIFY(topItem);
2032     QCOMPARE(topItem->y(), 100.);
2033
2034     QQuickItem *item = findVisibleChild(contentItem, "sect_1");
2035     QVERIFY(item);
2036     QCOMPARE(item->y(), 120.);
2037
2038     bottomItem = findVisibleChild(contentItem, "sect_4"); // section footer
2039     QVERIFY(bottomItem);
2040     QCOMPARE(bottomItem->y(), 410.);
2041
2042     // Move past section 0
2043     listview->setContentY(120);
2044     topItem = findVisibleChild(contentItem, "sect_0"); // section header
2045     QVERIFY(!topItem);
2046
2047     // Push section footer down
2048     listview->setContentY(70);
2049     bottomItem = findVisibleChild(contentItem, "sect_4"); // section footer
2050     QVERIFY(bottomItem);
2051     QCOMPARE(bottomItem->y(), 380.);
2052
2053     // Change current section
2054     listview->setContentY(10);
2055     model.modifyItem(0, "One", "aaa");
2056     model.modifyItem(1, "Two", "aaa");
2057     model.modifyItem(2, "Three", "aaa");
2058     model.modifyItem(3, "Four", "aaa");
2059     model.modifyItem(4, "Five", "aaa");
2060     QTest::qWait(300);
2061
2062     QTRY_COMPARE(listview->currentSection(), QString("aaa"));
2063
2064     for (int i = 0; i < 3; ++i) {
2065         QQuickItem *item = findItem<QQuickItem>(contentItem,
2066                 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
2067         QVERIFY(item);
2068         QTRY_COMPARE(item->y(), qreal(i*20*6));
2069     }
2070
2071     topItem = findVisibleChild(contentItem, "sect_aaa"); // section header
2072     QVERIFY(topItem);
2073     QCOMPARE(topItem->y(), 10.);
2074
2075     // remove section boundary
2076     listview->setContentY(120);
2077     model.removeItem(5);
2078     QTRY_COMPARE(listview->count(), model.count());
2079     for (int i = 0; i < 3; ++i) {
2080         QQuickItem *item = findItem<QQuickItem>(contentItem,
2081                 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
2082         QVERIFY(item);
2083         QTRY_COMPARE(item->y(), qreal(i*20*6));
2084     }
2085
2086     QVERIFY(topItem = findVisibleChild(contentItem, "sect_1"));
2087     QTRY_COMPARE(topItem->y(), 120.);
2088
2089     // Change the next section
2090     listview->setContentY(0);
2091     bottomItem = findVisibleChild(contentItem, "sect_3"); // section footer
2092     QVERIFY(bottomItem);
2093     QTRY_COMPARE(bottomItem->y(), 300.);
2094
2095     model.modifyItem(14, "New", "new");
2096
2097     QTRY_VERIFY(bottomItem = findVisibleChild(contentItem, "sect_new")); // section footer
2098     QTRY_COMPARE(bottomItem->y(), 300.);
2099
2100     // Turn sticky footer off
2101     listview->setContentY(40);
2102     canvas->rootObject()->setProperty("sectionPositioning", QVariant(int(QQuickViewSection::InlineLabels | QQuickViewSection::CurrentLabelAtStart)));
2103     item = findVisibleChild(contentItem, "sect_new"); // inline label restored
2104     QVERIFY(item);
2105     QCOMPARE(item->y(), 360.);
2106
2107     // Turn sticky header off
2108     listview->setContentY(30);
2109     canvas->rootObject()->setProperty("sectionPositioning", QVariant(int(QQuickViewSection::InlineLabels)));
2110     item = findVisibleChild(contentItem, "sect_aaa"); // inline label restored
2111     QVERIFY(item);
2112     QCOMPARE(item->y(), 0.);
2113
2114     delete canvas;
2115 }
2116
2117 void tst_QQuickListView::currentIndex_delayedItemCreation()
2118 {
2119     QFETCH(bool, setCurrentToZero);
2120
2121     QQuickView *canvas = createView();
2122
2123     TestModel model;
2124
2125     // test currentIndexChanged() is emitted even if currentIndex = 0 on start up
2126     // (since the currentItem will have changed and that shares the same index)
2127     canvas->rootContext()->setContextProperty("setCurrentToZero", setCurrentToZero);
2128
2129     canvas->setSource(testFileUrl("fillModelOnComponentCompleted.qml"));
2130     qApp->processEvents();
2131
2132     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2133     QTRY_VERIFY(listview != 0);
2134
2135     QQuickItem *contentItem = listview->contentItem();
2136     QTRY_VERIFY(contentItem != 0);
2137
2138     QSignalSpy spy(listview, SIGNAL(currentItemChanged()));
2139     QCOMPARE(listview->currentIndex(), 0);
2140     QTRY_COMPARE(spy.count(), 1);
2141
2142     delete canvas;
2143 }
2144
2145 void tst_QQuickListView::currentIndex_delayedItemCreation_data()
2146 {
2147     QTest::addColumn<bool>("setCurrentToZero");
2148
2149     QTest::newRow("set to 0") << true;
2150     QTest::newRow("don't set to 0") << false;
2151 }
2152
2153 void tst_QQuickListView::currentIndex()
2154 {
2155     TestModel model;
2156     for (int i = 0; i < 30; i++)
2157         model.addItem("Item" + QString::number(i), QString::number(i));
2158
2159     QQuickView *canvas = new QQuickView(0);
2160     canvas->setGeometry(0,0,240,320);
2161
2162     QDeclarativeContext *ctxt = canvas->rootContext();
2163     ctxt->setContextProperty("testModel", &model);
2164     ctxt->setContextProperty("testWrap", QVariant(false));
2165
2166     QString filename(testFile("listview-initCurrent.qml"));
2167     canvas->setSource(QUrl::fromLocalFile(filename));
2168
2169     qApp->processEvents();
2170
2171     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2172     QTRY_VERIFY(listview != 0);
2173
2174     QQuickItem *contentItem = listview->contentItem();
2175     QTRY_VERIFY(contentItem != 0);
2176
2177     // current item should be 20th item at startup
2178     // and current item should be in view
2179     QCOMPARE(listview->currentIndex(), 20);
2180     QCOMPARE(listview->contentY(), 100.0);
2181     QCOMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 20));
2182     QCOMPARE(listview->highlightItem()->y(), listview->currentItem()->y());
2183
2184     // no wrap
2185     listview->setCurrentIndex(0);
2186     QCOMPARE(listview->currentIndex(), 0);
2187     // confirm that the velocity is updated
2188     QTRY_VERIFY(listview->verticalVelocity() != 0.0);
2189
2190     listview->incrementCurrentIndex();
2191     QCOMPARE(listview->currentIndex(), 1);
2192     listview->decrementCurrentIndex();
2193     QCOMPARE(listview->currentIndex(), 0);
2194
2195     listview->decrementCurrentIndex();
2196     QCOMPARE(listview->currentIndex(), 0);
2197
2198     // with wrap
2199     ctxt->setContextProperty("testWrap", QVariant(true));
2200     QVERIFY(listview->isWrapEnabled());
2201
2202     listview->decrementCurrentIndex();
2203     QCOMPARE(listview->currentIndex(), model.count()-1);
2204
2205     QTRY_COMPARE(listview->contentY(), 280.0);
2206
2207     listview->incrementCurrentIndex();
2208     QCOMPARE(listview->currentIndex(), 0);
2209
2210     QTRY_COMPARE(listview->contentY(), 0.0);
2211
2212
2213     // footer should become visible if it is out of view, and then current index is set to count-1
2214     canvas->rootObject()->setProperty("showFooter", true);
2215     QTRY_VERIFY(listview->footerItem());
2216     listview->setCurrentIndex(model.count()-2);
2217     QTRY_VERIFY(listview->footerItem()->y() > listview->contentY() + listview->height());
2218     listview->setCurrentIndex(model.count()-1);
2219     QTRY_COMPARE(listview->contentY() + listview->height(), (20.0 * model.count()) + listview->footerItem()->height());
2220     canvas->rootObject()->setProperty("showFooter", false);
2221
2222     // header should become visible if it is out of view, and then current index is set to 0
2223     canvas->rootObject()->setProperty("showHeader", true);
2224     QTRY_VERIFY(listview->headerItem());
2225     listview->setCurrentIndex(1);
2226     QTRY_VERIFY(listview->headerItem()->y() + listview->headerItem()->height() < listview->contentY());
2227     listview->setCurrentIndex(0);
2228     QTRY_COMPARE(listview->contentY(), -listview->headerItem()->height());
2229     canvas->rootObject()->setProperty("showHeader", false);
2230
2231
2232     // Test keys
2233     canvas->show();
2234     canvas->requestActivateWindow();
2235     QTest::qWaitForWindowShown(canvas);
2236     QTRY_VERIFY(qGuiApp->focusWindow() == canvas);
2237
2238     listview->setCurrentIndex(0);
2239
2240     QTest::keyClick(canvas, Qt::Key_Down);
2241     QCOMPARE(listview->currentIndex(), 1);
2242
2243     QTest::keyClick(canvas, Qt::Key_Up);
2244     QCOMPARE(listview->currentIndex(), 0);
2245
2246     // hold down Key_Down
2247     for (int i=0; i<model.count()-1; i++) {
2248         QTest::simulateEvent(canvas, true, Qt::Key_Down, Qt::NoModifier, "", true);
2249         QTRY_COMPARE(listview->currentIndex(), i+1);
2250     }
2251     QTest::keyRelease(canvas, Qt::Key_Down);
2252     QTRY_COMPARE(listview->currentIndex(), model.count()-1);
2253     QTRY_COMPARE(listview->contentY(), 280.0);
2254
2255     // hold down Key_Up
2256     for (int i=model.count()-1; i > 0; i--) {
2257         QTest::simulateEvent(canvas, true, Qt::Key_Up, Qt::NoModifier, "", true);
2258         QTRY_COMPARE(listview->currentIndex(), i-1);
2259     }
2260     QTest::keyRelease(canvas, Qt::Key_Up);
2261     QTRY_COMPARE(listview->currentIndex(), 0);
2262     QTRY_COMPARE(listview->contentY(), 0.0);
2263
2264
2265     // turn off auto highlight
2266     listview->setHighlightFollowsCurrentItem(false);
2267     QVERIFY(listview->highlightFollowsCurrentItem() == false);
2268
2269     QVERIFY(listview->highlightItem());
2270     qreal hlPos = listview->highlightItem()->y();
2271
2272     listview->setCurrentIndex(4);
2273     QTRY_COMPARE(listview->highlightItem()->y(), hlPos);
2274
2275     // insert item before currentIndex
2276     listview->setCurrentIndex(28);
2277     model.insertItem(0, "Foo", "1111");
2278     QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29);
2279
2280     // check removing highlight by setting currentIndex to -1;
2281     listview->setCurrentIndex(-1);
2282
2283     QCOMPARE(listview->currentIndex(), -1);
2284     QVERIFY(!listview->highlightItem());
2285     QVERIFY(!listview->currentItem());
2286
2287     delete canvas;
2288 }
2289
2290 void tst_QQuickListView::noCurrentIndex()
2291 {
2292     TestModel model;
2293     for (int i = 0; i < 30; i++)
2294         model.addItem("Item" + QString::number(i), QString::number(i));
2295
2296     QQuickView *canvas = new QQuickView(0);
2297     canvas->setGeometry(0,0,240,320);
2298
2299     QDeclarativeContext *ctxt = canvas->rootContext();
2300     ctxt->setContextProperty("testModel", &model);
2301
2302     QString filename(testFile("listview-noCurrent.qml"));
2303     canvas->setSource(QUrl::fromLocalFile(filename));
2304
2305     qApp->processEvents();
2306
2307     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2308     QTRY_VERIFY(listview != 0);
2309
2310     QQuickItem *contentItem = listview->contentItem();
2311     QTRY_VERIFY(contentItem != 0);
2312
2313     // current index should be -1 at startup
2314     // and we should not have a currentItem or highlightItem
2315     QCOMPARE(listview->currentIndex(), -1);
2316     QCOMPARE(listview->contentY(), 0.0);
2317     QVERIFY(!listview->highlightItem());
2318     QVERIFY(!listview->currentItem());
2319
2320     listview->setCurrentIndex(2);
2321     QCOMPARE(listview->currentIndex(), 2);
2322     QVERIFY(listview->highlightItem());
2323     QVERIFY(listview->currentItem());
2324
2325     delete canvas;
2326 }
2327
2328 void tst_QQuickListView::itemList()
2329 {
2330     QQuickView *canvas = createView();
2331
2332     canvas->setSource(testFileUrl("itemlist.qml"));
2333     qApp->processEvents();
2334
2335     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "view");
2336     QTRY_VERIFY(listview != 0);
2337
2338     QQuickItem *contentItem = listview->contentItem();
2339     QTRY_VERIFY(contentItem != 0);
2340
2341     QQuickVisualItemModel *model = canvas->rootObject()->findChild<QQuickVisualItemModel*>("itemModel");
2342     QTRY_VERIFY(model != 0);
2343
2344     QTRY_VERIFY(model->count() == 3);
2345     QTRY_COMPARE(listview->currentIndex(), 0);
2346
2347     QQuickItem *item = findItem<QQuickItem>(contentItem, "item1");
2348     QTRY_VERIFY(item);
2349     QTRY_COMPARE(item->x(), 0.0);
2350     QCOMPARE(item->height(), listview->height());
2351
2352     QQuickText *text = findItem<QQuickText>(contentItem, "text1");
2353     QTRY_VERIFY(text);
2354     QTRY_COMPARE(text->text(), QLatin1String("index: 0"));
2355
2356     listview->setCurrentIndex(2);
2357
2358     item = findItem<QQuickItem>(contentItem, "item3");
2359     QTRY_VERIFY(item);
2360     QTRY_COMPARE(item->x(), 480.0);
2361
2362     text = findItem<QQuickText>(contentItem, "text3");
2363     QTRY_VERIFY(text);
2364     QTRY_COMPARE(text->text(), QLatin1String("index: 2"));
2365
2366     delete canvas;
2367 }
2368
2369 void tst_QQuickListView::cacheBuffer()
2370 {
2371     QQuickView *canvas = createView();
2372
2373     TestModel model;
2374     for (int i = 0; i < 90; i++)
2375         model.addItem("Item" + QString::number(i), "");
2376
2377     QDeclarativeContext *ctxt = canvas->rootContext();
2378     ctxt->setContextProperty("testModel", &model);
2379
2380     TestObject *testObject = new TestObject;
2381     ctxt->setContextProperty("testObject", testObject);
2382
2383     canvas->setSource(testFileUrl("listviewtest.qml"));
2384     canvas->show();
2385     qApp->processEvents();
2386
2387     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2388     QTRY_VERIFY(listview != 0);
2389
2390     QQuickItem *contentItem = listview->contentItem();
2391     QTRY_VERIFY(contentItem != 0);
2392     QTRY_VERIFY(listview->delegate() != 0);
2393     QTRY_VERIFY(listview->model() != 0);
2394     QTRY_VERIFY(listview->highlight() != 0);
2395
2396     // Confirm items positioned correctly
2397     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2398     for (int i = 0; i < model.count() && i < itemCount; ++i) {
2399         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2400         if (!item) qWarning() << "Item" << i << "not found";
2401         QTRY_VERIFY(item);
2402         QTRY_VERIFY(item->y() == i*20);
2403     }
2404
2405     QDeclarativeIncubationController controller;
2406     canvas->engine()->setIncubationController(&controller);
2407
2408     testObject->setCacheBuffer(200);
2409     QTRY_VERIFY(listview->cacheBuffer() == 200);
2410
2411     // items will be created one at a time
2412     for (int i = itemCount; i < qMin(itemCount+10,model.count()); ++i) {
2413         QVERIFY(findItem<QQuickItem>(listview, "wrapper", i) == 0);
2414         QQuickItem *item = 0;
2415         while (!item) {
2416             bool b = false;
2417             controller.incubateWhile(&b);
2418             item = findItem<QQuickItem>(listview, "wrapper", i);
2419         }
2420     }
2421
2422     {
2423         bool b = true;
2424         controller.incubateWhile(&b);
2425     }
2426
2427     int newItemCount = 0;
2428     newItemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2429
2430     // Confirm items positioned correctly
2431     for (int i = 0; i < model.count() && i < newItemCount; ++i) {
2432         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2433         if (!item) qWarning() << "Item" << i << "not found";
2434         QTRY_VERIFY(item);
2435         QTRY_VERIFY(item->y() == i*20);
2436     }
2437
2438     // move view and confirm items in view are visible immediately and outside are created async
2439     listview->setContentY(300);
2440
2441     for (int i = 15; i < 32; ++i) {
2442         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2443         if (!item) qWarning() << "Item" << i << "not found";
2444         QVERIFY(item);
2445         QVERIFY(item->y() == i*20);
2446     }
2447
2448     QVERIFY(findItem<QQuickItem>(listview, "wrapper", 32) == 0);
2449
2450     // ensure buffered items are created
2451     for (int i = 32; i < qMin(41,model.count()); ++i) {
2452         QQuickItem *item = 0;
2453         while (!item) {
2454             qGuiApp->processEvents(); // allow refill to happen
2455             bool b = false;
2456             controller.incubateWhile(&b);
2457             item = findItem<QQuickItem>(listview, "wrapper", i);
2458         }
2459     }
2460
2461     {
2462         bool b = true;
2463         controller.incubateWhile(&b);
2464     }
2465
2466     delete canvas;
2467     delete testObject;
2468 }
2469
2470 void tst_QQuickListView::positionViewAtIndex()
2471 {
2472     QQuickView *canvas = createView();
2473
2474     TestModel model;
2475     for (int i = 0; i < 40; i++)
2476         model.addItem("Item" + QString::number(i), "");
2477
2478     QDeclarativeContext *ctxt = canvas->rootContext();
2479     ctxt->setContextProperty("testModel", &model);
2480
2481     TestObject *testObject = new TestObject;
2482     ctxt->setContextProperty("testObject", testObject);
2483
2484     canvas->setSource(testFileUrl("listviewtest.qml"));
2485     qApp->processEvents();
2486
2487     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2488     QTRY_VERIFY(listview != 0);
2489
2490     QQuickItem *contentItem = listview->contentItem();
2491     QTRY_VERIFY(contentItem != 0);
2492
2493     // Confirm items positioned correctly
2494     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2495     for (int i = 0; i < model.count() && i < itemCount; ++i) {
2496         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2497         if (!item) qWarning() << "Item" << i << "not found";
2498         QTRY_VERIFY(item);
2499         QTRY_COMPARE(item->y(), i*20.);
2500     }
2501
2502     // Position on a currently visible item
2503     listview->positionViewAtIndex(3, QQuickListView::Beginning);
2504     QTRY_COMPARE(listview->contentY(), 60.);
2505
2506     // Confirm items positioned correctly
2507     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2508     for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
2509         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2510         if (!item) qWarning() << "Item" << i << "not found";
2511         QTRY_VERIFY(item);
2512         QTRY_COMPARE(item->y(), i*20.);
2513     }
2514
2515     // Position on an item beyond the visible items
2516     listview->positionViewAtIndex(22, QQuickListView::Beginning);
2517     QTRY_COMPARE(listview->contentY(), 440.);
2518
2519     // Confirm items positioned correctly
2520     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2521     for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
2522         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2523         if (!item) qWarning() << "Item" << i << "not found";
2524         QTRY_VERIFY(item);
2525         QTRY_COMPARE(item->y(), i*20.);
2526     }
2527
2528     // Position on an item that would leave empty space if positioned at the top
2529     listview->positionViewAtIndex(28, QQuickListView::Beginning);
2530     QTRY_COMPARE(listview->contentY(), 480.);
2531
2532     // Confirm items positioned correctly
2533     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2534     for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
2535         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2536         if (!item) qWarning() << "Item" << i << "not found";
2537         QTRY_VERIFY(item);
2538         QTRY_COMPARE(item->y(), i*20.);
2539     }
2540
2541     // Position at the beginning again
2542     listview->positionViewAtIndex(0, QQuickListView::Beginning);
2543     QTRY_COMPARE(listview->contentY(), 0.);
2544
2545     // Confirm items positioned correctly
2546     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2547     for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2548         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2549         if (!item) qWarning() << "Item" << i << "not found";
2550         QTRY_VERIFY(item);
2551         QTRY_COMPARE(item->y(), i*20.);
2552     }
2553
2554     // Position at End using last index
2555     listview->positionViewAtIndex(model.count()-1, QQuickListView::End);
2556     QTRY_COMPARE(listview->contentY(), 480.);
2557
2558     // Confirm items positioned correctly
2559     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2560     for (int i = 24; i < model.count(); ++i) {
2561         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2562         if (!item) qWarning() << "Item" << i << "not found";
2563         QTRY_VERIFY(item);
2564         QTRY_COMPARE(item->y(), i*20.);
2565     }
2566
2567     // Position at End
2568     listview->positionViewAtIndex(20, QQuickListView::End);
2569     QTRY_COMPARE(listview->contentY(), 100.);
2570
2571     // Position in Center
2572     listview->positionViewAtIndex(15, QQuickListView::Center);
2573     QTRY_COMPARE(listview->contentY(), 150.);
2574
2575     // Ensure at least partially visible
2576     listview->positionViewAtIndex(15, QQuickListView::Visible);
2577     QTRY_COMPARE(listview->contentY(), 150.);
2578
2579     listview->setContentY(302);
2580     listview->positionViewAtIndex(15, QQuickListView::Visible);
2581     QTRY_COMPARE(listview->contentY(), 302.);
2582
2583     listview->setContentY(320);
2584     listview->positionViewAtIndex(15, QQuickListView::Visible);
2585     QTRY_COMPARE(listview->contentY(), 300.);
2586
2587     listview->setContentY(85);
2588     listview->positionViewAtIndex(20, QQuickListView::Visible);
2589     QTRY_COMPARE(listview->contentY(), 85.);
2590
2591     listview->setContentY(75);
2592     listview->positionViewAtIndex(20, QQuickListView::Visible);
2593     QTRY_COMPARE(listview->contentY(), 100.);
2594
2595     // Ensure completely visible
2596     listview->setContentY(120);
2597     listview->positionViewAtIndex(20, QQuickListView::Contain);
2598     QTRY_COMPARE(listview->contentY(), 120.);
2599
2600     listview->setContentY(302);
2601     listview->positionViewAtIndex(15, QQuickListView::Contain);
2602     QTRY_COMPARE(listview->contentY(), 300.);
2603
2604     listview->setContentY(85);
2605     listview->positionViewAtIndex(20, QQuickListView::Contain);
2606     QTRY_COMPARE(listview->contentY(), 100.);
2607
2608     // positionAtBeginnging
2609     listview->positionViewAtBeginning();
2610     QTRY_COMPARE(listview->contentY(), 0.);
2611
2612     listview->setContentY(80);
2613     canvas->rootObject()->setProperty("showHeader", true);
2614     listview->positionViewAtBeginning();
2615     QTRY_COMPARE(listview->contentY(), -30.);
2616
2617     // positionAtEnd
2618     listview->positionViewAtEnd();
2619     QTRY_COMPARE(listview->contentY(), 480.); // 40*20 - 320
2620
2621     listview->setContentY(80);
2622     canvas->rootObject()->setProperty("showFooter", true);
2623     listview->positionViewAtEnd();
2624     QTRY_COMPARE(listview->contentY(), 510.);
2625
2626     // set current item to outside visible view, position at beginning
2627     // and ensure highlight moves to current item
2628     listview->setCurrentIndex(1);
2629     listview->positionViewAtBeginning();
2630     QTRY_COMPARE(listview->contentY(), -30.);
2631     QVERIFY(listview->highlightItem());
2632     QCOMPARE(listview->highlightItem()->y(), 20.);
2633
2634     delete canvas;
2635     delete testObject;
2636 }
2637
2638 void tst_QQuickListView::resetModel()
2639 {
2640     QQuickView *canvas = createView();
2641
2642     QStringList strings;
2643     strings << "one" << "two" << "three";
2644     QStringListModel model(strings);
2645
2646     QDeclarativeContext *ctxt = canvas->rootContext();
2647     ctxt->setContextProperty("testModel", &model);
2648
2649     canvas->setSource(testFileUrl("displaylist.qml"));
2650     qApp->processEvents();
2651
2652     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2653     QTRY_VERIFY(listview != 0);
2654
2655     QQuickItem *contentItem = listview->contentItem();
2656     QTRY_VERIFY(contentItem != 0);
2657
2658     QTRY_COMPARE(listview->count(), model.rowCount());
2659
2660     for (int i = 0; i < model.rowCount(); ++i) {
2661         QQuickText *display = findItem<QQuickText>(contentItem, "displayText", i);
2662         QTRY_VERIFY(display != 0);
2663         QTRY_COMPARE(display->text(), strings.at(i));
2664     }
2665
2666     strings.clear();
2667     strings << "four" << "five" << "six" << "seven";
2668     model.setStringList(strings);
2669
2670     QTRY_COMPARE(listview->count(), model.rowCount());
2671
2672     for (int i = 0; i < model.rowCount(); ++i) {
2673         QQuickText *display = findItem<QQuickText>(contentItem, "displayText", i);
2674         QTRY_VERIFY(display != 0);
2675         QTRY_COMPARE(display->text(), strings.at(i));
2676     }
2677
2678     delete canvas;
2679 }
2680
2681 void tst_QQuickListView::propertyChanges()
2682 {
2683     QQuickView *canvas = createView();
2684     QTRY_VERIFY(canvas);
2685     canvas->setSource(testFileUrl("propertychangestest.qml"));
2686
2687     QQuickListView *listView = canvas->rootObject()->findChild<QQuickListView*>("listView");
2688     QTRY_VERIFY(listView);
2689
2690     QSignalSpy highlightFollowsCurrentItemSpy(listView, SIGNAL(highlightFollowsCurrentItemChanged()));
2691     QSignalSpy preferredHighlightBeginSpy(listView, SIGNAL(preferredHighlightBeginChanged()));
2692     QSignalSpy preferredHighlightEndSpy(listView, SIGNAL(preferredHighlightEndChanged()));
2693     QSignalSpy highlightRangeModeSpy(listView, SIGNAL(highlightRangeModeChanged()));
2694     QSignalSpy keyNavigationWrapsSpy(listView, SIGNAL(keyNavigationWrapsChanged()));
2695     QSignalSpy cacheBufferSpy(listView, SIGNAL(cacheBufferChanged()));
2696     QSignalSpy snapModeSpy(listView, SIGNAL(snapModeChanged()));
2697
2698     QTRY_COMPARE(listView->highlightFollowsCurrentItem(), true);
2699     QTRY_COMPARE(listView->preferredHighlightBegin(), 0.0);
2700     QTRY_COMPARE(listView->preferredHighlightEnd(), 0.0);
2701     QTRY_COMPARE(listView->highlightRangeMode(), QQuickListView::ApplyRange);
2702     QTRY_COMPARE(listView->isWrapEnabled(), true);
2703     QTRY_COMPARE(listView->cacheBuffer(), 10);
2704     QTRY_COMPARE(listView->snapMode(), QQuickListView::SnapToItem);
2705
2706     listView->setHighlightFollowsCurrentItem(false);
2707     listView->setPreferredHighlightBegin(1.0);
2708     listView->setPreferredHighlightEnd(1.0);
2709     listView->setHighlightRangeMode(QQuickListView::StrictlyEnforceRange);
2710     listView->setWrapEnabled(false);
2711     listView->setCacheBuffer(3);
2712     listView->setSnapMode(QQuickListView::SnapOneItem);
2713
2714     QTRY_COMPARE(listView->highlightFollowsCurrentItem(), false);
2715     QTRY_COMPARE(listView->preferredHighlightBegin(), 1.0);
2716     QTRY_COMPARE(listView->preferredHighlightEnd(), 1.0);
2717     QTRY_COMPARE(listView->highlightRangeMode(), QQuickListView::StrictlyEnforceRange);
2718     QTRY_COMPARE(listView->isWrapEnabled(), false);
2719     QTRY_COMPARE(listView->cacheBuffer(), 3);
2720     QTRY_COMPARE(listView->snapMode(), QQuickListView::SnapOneItem);
2721
2722     QTRY_COMPARE(highlightFollowsCurrentItemSpy.count(),1);
2723     QTRY_COMPARE(preferredHighlightBeginSpy.count(),1);
2724     QTRY_COMPARE(preferredHighlightEndSpy.count(),1);
2725     QTRY_COMPARE(highlightRangeModeSpy.count(),1);
2726     QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
2727     QTRY_COMPARE(cacheBufferSpy.count(),1);
2728     QTRY_COMPARE(snapModeSpy.count(),1);
2729
2730     listView->setHighlightFollowsCurrentItem(false);
2731     listView->setPreferredHighlightBegin(1.0);
2732     listView->setPreferredHighlightEnd(1.0);
2733     listView->setHighlightRangeMode(QQuickListView::StrictlyEnforceRange);
2734     listView->setWrapEnabled(false);
2735     listView->setCacheBuffer(3);
2736     listView->setSnapMode(QQuickListView::SnapOneItem);
2737
2738     QTRY_COMPARE(highlightFollowsCurrentItemSpy.count(),1);
2739     QTRY_COMPARE(preferredHighlightBeginSpy.count(),1);
2740     QTRY_COMPARE(preferredHighlightEndSpy.count(),1);
2741     QTRY_COMPARE(highlightRangeModeSpy.count(),1);
2742     QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
2743     QTRY_COMPARE(cacheBufferSpy.count(),1);
2744     QTRY_COMPARE(snapModeSpy.count(),1);
2745
2746     delete canvas;
2747 }
2748
2749 void tst_QQuickListView::componentChanges()
2750 {
2751     QQuickView *canvas = createView();
2752     QTRY_VERIFY(canvas);
2753     canvas->setSource(testFileUrl("propertychangestest.qml"));
2754
2755     QQuickListView *listView = canvas->rootObject()->findChild<QQuickListView*>("listView");
2756     QTRY_VERIFY(listView);
2757
2758     QDeclarativeComponent component(canvas->engine());
2759     component.setData("import QtQuick 2.0; Rectangle { color: \"blue\"; }", QUrl::fromLocalFile(""));
2760
2761     QDeclarativeComponent delegateComponent(canvas->engine());
2762     delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
2763
2764     QSignalSpy highlightSpy(listView, SIGNAL(highlightChanged()));
2765     QSignalSpy delegateSpy(listView, SIGNAL(delegateChanged()));
2766     QSignalSpy headerSpy(listView, SIGNAL(headerChanged()));
2767     QSignalSpy footerSpy(listView, SIGNAL(footerChanged()));
2768
2769     listView->setHighlight(&component);
2770     listView->setHeader(&component);
2771     listView->setFooter(&component);
2772     listView->setDelegate(&delegateComponent);
2773
2774     QTRY_COMPARE(listView->highlight(), &component);
2775     QTRY_COMPARE(listView->header(), &component);
2776     QTRY_COMPARE(listView->footer(), &component);
2777     QTRY_COMPARE(listView->delegate(), &delegateComponent);
2778
2779     QTRY_COMPARE(highlightSpy.count(),1);
2780     QTRY_COMPARE(delegateSpy.count(),1);
2781     QTRY_COMPARE(headerSpy.count(),1);
2782     QTRY_COMPARE(footerSpy.count(),1);
2783
2784     listView->setHighlight(&component);
2785     listView->setHeader(&component);
2786     listView->setFooter(&component);
2787     listView->setDelegate(&delegateComponent);
2788
2789     QTRY_COMPARE(highlightSpy.count(),1);
2790     QTRY_COMPARE(delegateSpy.count(),1);
2791     QTRY_COMPARE(headerSpy.count(),1);
2792     QTRY_COMPARE(footerSpy.count(),1);
2793
2794     delete canvas;
2795 }
2796
2797 void tst_QQuickListView::modelChanges()
2798 {
2799     QQuickView *canvas = createView();
2800     QTRY_VERIFY(canvas);
2801     canvas->setSource(testFileUrl("propertychangestest.qml"));
2802
2803     QQuickListView *listView = canvas->rootObject()->findChild<QQuickListView*>("listView");
2804     QTRY_VERIFY(listView);
2805
2806     QDeclarativeListModel *alternateModel = canvas->rootObject()->findChild<QDeclarativeListModel*>("alternateModel");
2807     QTRY_VERIFY(alternateModel);
2808     QVariant modelVariant = QVariant::fromValue<QObject *>(alternateModel);
2809     QSignalSpy modelSpy(listView, SIGNAL(modelChanged()));
2810
2811     listView->setModel(modelVariant);
2812     QTRY_COMPARE(listView->model(), modelVariant);
2813     QTRY_COMPARE(modelSpy.count(),1);
2814
2815     listView->setModel(modelVariant);
2816     QTRY_COMPARE(modelSpy.count(),1);
2817
2818     listView->setModel(QVariant());
2819     QTRY_COMPARE(modelSpy.count(),2);
2820
2821     delete canvas;
2822 }
2823
2824 void tst_QQuickListView::QTBUG_9791()
2825 {
2826     QQuickView *canvas = createView();
2827
2828     canvas->setSource(testFileUrl("strictlyenforcerange.qml"));
2829     qApp->processEvents();
2830
2831     QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
2832     QTRY_VERIFY(listview != 0);
2833
2834     QQuickItem *contentItem = listview->contentItem();
2835     QTRY_VERIFY(contentItem != 0);
2836     QTRY_VERIFY(listview->delegate() != 0);
2837     QTRY_VERIFY(listview->model() != 0);
2838
2839     QMetaObject::invokeMethod(listview, "fillModel");
2840     qApp->processEvents();
2841
2842     // Confirm items positioned correctly
2843     int itemCount = findItems<QQuickItem>(contentItem, "wrapper", false).count();
2844     QCOMPARE(itemCount, 3);
2845
2846     for (int i = 0; i < itemCount; ++i) {
2847         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2848         if (!item) qWarning() << "Item" << i << "not found";
2849         QTRY_VERIFY(item);
2850         QTRY_COMPARE(item->x(), i*300.0);
2851     }
2852
2853     // check that view is positioned correctly
2854     QTRY_COMPARE(listview->contentX(), 590.0);
2855
2856     delete canvas;
2857 }
2858
2859 void tst_QQuickListView::manualHighlight()
2860 {
2861     QQuickView *canvas = new QQuickView(0);
2862     canvas->setGeometry(0,0,240,320);
2863
2864     QString filename(testFile("manual-highlight.qml"));
2865     canvas->setSource(QUrl::fromLocalFile(filename));
2866
2867     qApp->processEvents();
2868
2869     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2870     QTRY_VERIFY(listview != 0);
2871
2872     QQuickItem *contentItem = listview->contentItem();
2873     QTRY_VERIFY(contentItem != 0);
2874
2875     QTRY_COMPARE(listview->currentIndex(), 0);
2876     QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
2877     QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
2878
2879     listview->setCurrentIndex(2);
2880
2881     QTRY_COMPARE(listview->currentIndex(), 2);
2882     QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 2));
2883     QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
2884
2885     // QTBUG-15972
2886     listview->positionViewAtIndex(3, QQuickListView::Contain);
2887
2888     QTRY_COMPARE(listview->currentIndex(), 2);
2889     QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 2));
2890     QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
2891
2892     delete canvas;
2893 }
2894
2895 void tst_QQuickListView::QTBUG_11105()
2896 {
2897     QQuickView *canvas = createView();
2898
2899     TestModel model;
2900     for (int i = 0; i < 30; i++)
2901         model.addItem("Item" + QString::number(i), "");
2902
2903     QDeclarativeContext *ctxt = canvas->rootContext();
2904     ctxt->setContextProperty("testModel", &model);
2905
2906     TestObject *testObject = new TestObject;
2907     ctxt->setContextProperty("testObject", testObject);
2908
2909     canvas->setSource(testFileUrl("listviewtest.qml"));
2910     qApp->processEvents();
2911
2912     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2913     QTRY_VERIFY(listview != 0);
2914
2915     QQuickItem *contentItem = listview->contentItem();
2916     QTRY_VERIFY(contentItem != 0);
2917
2918     // Confirm items positioned correctly
2919     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2920     for (int i = 0; i < model.count() && i < itemCount; ++i) {
2921         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2922         if (!item) qWarning() << "Item" << i << "not found";
2923         QTRY_VERIFY(item);
2924         QTRY_VERIFY(item->y() == i*20);
2925     }
2926
2927     listview->positionViewAtIndex(20, QQuickListView::Beginning);
2928     QCOMPARE(listview->contentY(), 280.);
2929
2930     TestModel model2;
2931     for (int i = 0; i < 5; i++)
2932         model2.addItem("Item" + QString::number(i), "");
2933
2934     ctxt->setContextProperty("testModel", &model2);
2935
2936     itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2937     QCOMPARE(itemCount, 5);
2938
2939     delete canvas;
2940     delete testObject;
2941 }
2942
2943 void tst_QQuickListView::header()
2944 {
2945     QFETCH(QQuickListView::Orientation, orientation);
2946     QFETCH(Qt::LayoutDirection, layoutDirection);
2947     QFETCH(QPointF, initialHeaderPos);
2948     QFETCH(QPointF, firstDelegatePos);
2949     QFETCH(QPointF, initialContentPos);
2950     QFETCH(QPointF, changedHeaderPos);
2951     QFETCH(QPointF, changedContentPos);
2952     QFETCH(QPointF, resizeContentPos);
2953
2954     TestModel model;
2955     for (int i = 0; i < 30; i++)
2956         model.addItem("Item" + QString::number(i), "");
2957
2958     QQuickView *canvas = createView();
2959     canvas->rootContext()->setContextProperty("testModel", &model);
2960     canvas->rootContext()->setContextProperty("initialViewWidth", 240);
2961     canvas->rootContext()->setContextProperty("initialViewHeight", 320);
2962     canvas->setSource(testFileUrl("header.qml"));
2963
2964     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2965     QTRY_VERIFY(listview != 0);
2966     listview->setOrientation(orientation);
2967     listview->setLayoutDirection(layoutDirection);
2968
2969     QQuickItem *contentItem = listview->contentItem();
2970     QTRY_VERIFY(contentItem != 0);
2971
2972     QQuickText *header = findItem<QQuickText>(contentItem, "header");
2973     QVERIFY(header);
2974
2975     QVERIFY(header == listview->headerItem());
2976
2977     QCOMPARE(header->width(), 100.);
2978     QCOMPARE(header->height(), 30.);
2979     QCOMPARE(header->pos(), initialHeaderPos);
2980     QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos);
2981
2982     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
2983     QVERIFY(item);
2984     QCOMPARE(item->pos(), firstDelegatePos);
2985
2986     model.clear();
2987     QCOMPARE(header->pos(), initialHeaderPos); // header should stay where it is
2988
2989     for (int i = 0; i < 30; i++)
2990         model.addItem("Item" + QString::number(i), "");
2991
2992     QSignalSpy headerItemSpy(listview, SIGNAL(headerItemChanged()));
2993     QMetaObject::invokeMethod(canvas->rootObject(), "changeHeader");
2994
2995     QCOMPARE(headerItemSpy.count(), 1);
2996
2997     header = findItem<QQuickText>(contentItem, "header");
2998     QVERIFY(!header);
2999     header = findItem<QQuickText>(contentItem, "header2");
3000     QVERIFY(header);
3001
3002     QVERIFY(header == listview->headerItem());
3003
3004     QCOMPARE(header->pos(), changedHeaderPos);
3005     QCOMPARE(header->width(), 50.);
3006     QCOMPARE(header->height(), 20.);
3007     QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), changedContentPos);
3008     QCOMPARE(item->pos(), firstDelegatePos);
3009
3010     delete canvas;
3011
3012
3013     // QTBUG-21207 header should become visible if view resizes from initial empty size
3014
3015     canvas = createView();
3016     canvas->rootContext()->setContextProperty("testModel", &model);
3017     canvas->rootContext()->setContextProperty("initialViewWidth", 0.0);
3018     canvas->rootContext()->setContextProperty("initialViewHeight", 0.0);
3019     canvas->setSource(testFileUrl("header.qml"));
3020
3021     listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3022     QTRY_VERIFY(listview != 0);
3023     listview->setOrientation(orientation);
3024     listview->setLayoutDirection(layoutDirection);
3025
3026     listview->setWidth(240);
3027     listview->setHeight(320);
3028     QTRY_COMPARE(listview->headerItem()->pos(), initialHeaderPos);
3029     QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos);
3030
3031
3032     delete canvas;
3033 }
3034
3035 void tst_QQuickListView::header_data()
3036 {
3037     QTest::addColumn<QQuickListView::Orientation>("orientation");
3038     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3039     QTest::addColumn<QPointF>("initialHeaderPos");
3040     QTest::addColumn<QPointF>("changedHeaderPos");
3041     QTest::addColumn<QPointF>("initialContentPos");
3042     QTest::addColumn<QPointF>("changedContentPos");
3043     QTest::addColumn<QPointF>("firstDelegatePos");
3044     QTest::addColumn<QPointF>("resizeContentPos");
3045
3046     // header1 = 100 x 30
3047     // header2 = 50 x 20
3048     // delegates = 240 x 20
3049     // view width = 240
3050
3051     // header above items, top left
3052     QTest::newRow("vertical, left to right") << QQuickListView::Vertical << Qt::LeftToRight
3053         << QPointF(0, -30)
3054         << QPointF(0, -20)
3055         << QPointF(0, -30)
3056         << QPointF(0, -20)
3057         << QPointF(0, 0)
3058         << QPointF(0, -10);
3059
3060     // header above items, top right
3061     QTest::newRow("vertical, layout right to left") << QQuickListView::Vertical << Qt::RightToLeft
3062         << QPointF(0, -30)
3063         << QPointF(0, -20)
3064         << QPointF(0, -30)
3065         << QPointF(0, -20)
3066         << QPointF(0, 0)
3067         << QPointF(0, -10);
3068
3069     // header to left of items
3070     QTest::newRow("horizontal, layout left to right") << QQuickListView::Horizontal << Qt::LeftToRight
3071         << QPointF(-100, 0)
3072         << QPointF(-50, 0)
3073         << QPointF(-100, 0)
3074         << QPointF(-50, 0)
3075         << QPointF(0, 0)
3076         << QPointF(-40, 0);
3077
3078     // header to right of items
3079     QTest::newRow("horizontal, layout right to left") << QQuickListView::Horizontal << Qt::RightToLeft
3080         << QPointF(0, 0)
3081         << QPointF(0, 0)
3082         << QPointF(-240 + 100, 0)
3083         << QPointF(-240 + 50, 0)
3084         << QPointF(-240, 0)
3085         << QPointF(-240 + 40, 0);
3086 }
3087
3088 void tst_QQuickListView::header_delayItemCreation()
3089 {
3090     QQuickView *canvas = createView();
3091
3092     TestModel model;
3093
3094     canvas->rootContext()->setContextProperty("setCurrentToZero", QVariant(false));
3095     canvas->setSource(testFileUrl("fillModelOnComponentCompleted.qml"));
3096     qApp->processEvents();
3097
3098     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3099     QTRY_VERIFY(listview != 0);
3100
3101     QQuickItem *contentItem = listview->contentItem();
3102     QTRY_VERIFY(contentItem != 0);
3103
3104     QQuickText *header = findItem<QQuickText>(contentItem, "header");
3105     QVERIFY(header);
3106     QCOMPARE(header->y(), -header->height());
3107
3108     QCOMPARE(listview->contentY(), -header->height());
3109
3110     model.clear();
3111     QTRY_COMPARE(header->y(), -header->height());
3112
3113     delete canvas;
3114 }
3115
3116 void tst_QQuickListView::footer()
3117 {
3118     QFETCH(QQuickListView::Orientation, orientation);
3119     QFETCH(Qt::LayoutDirection, layoutDirection);
3120     QFETCH(QPointF, initialFooterPos);
3121     QFETCH(QPointF, firstDelegatePos);
3122     QFETCH(QPointF, initialContentPos);
3123     QFETCH(QPointF, changedFooterPos);
3124     QFETCH(QPointF, changedContentPos);
3125     QFETCH(QPointF, resizeContentPos);
3126
3127     QQuickView *canvas = createView();
3128
3129     TestModel model;
3130     for (int i = 0; i < 3; i++)
3131         model.addItem("Item" + QString::number(i), "");
3132
3133     QDeclarativeContext *ctxt = canvas->rootContext();
3134     ctxt->setContextProperty("testModel", &model);
3135
3136     canvas->setSource(testFileUrl("footer.qml"));
3137     canvas->show();
3138     qApp->processEvents();
3139
3140     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3141     QTRY_VERIFY(listview != 0);
3142     listview->setOrientation(orientation);
3143     listview->setLayoutDirection(layoutDirection);
3144
3145     QQuickItem *contentItem = listview->contentItem();
3146     QTRY_VERIFY(contentItem != 0);
3147
3148     QQuickText *footer = findItem<QQuickText>(contentItem, "footer");
3149     QVERIFY(footer);
3150
3151     QVERIFY(footer == listview->footerItem());
3152
3153     QCOMPARE(footer->pos(), initialFooterPos);
3154     QCOMPARE(footer->width(), 100.);
3155     QCOMPARE(footer->height(), 30.);
3156     QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos);
3157
3158     QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3159     QVERIFY(item);
3160     QCOMPARE(item->pos(), firstDelegatePos);
3161
3162     // remove one item
3163     model.removeItem(1);
3164
3165     if (orientation == QQuickListView::Vertical) {
3166         QTRY_COMPARE(footer->y(), initialFooterPos.y() - 20);   // delegate height = 20
3167     } else {
3168         QTRY_COMPARE(footer->x(), layoutDirection == Qt::LeftToRight ?
3169                 initialFooterPos.x() - 40 : initialFooterPos.x() + 40);  // delegate width = 40
3170     }
3171
3172     // remove all items
3173     model.clear();
3174
3175     QPointF posWhenNoItems(0, 0);
3176     if (orientation == QQuickListView::Horizontal && layoutDirection == Qt::RightToLeft)
3177         posWhenNoItems.setX(-100);
3178     QTRY_COMPARE(footer->pos(), posWhenNoItems);
3179
3180     // if header is present, it's at a negative pos, so the footer should not move
3181     canvas->rootObject()->setProperty("showHeader", true);
3182     QTRY_COMPARE(footer->pos(), posWhenNoItems);
3183     canvas->rootObject()->setProperty("showHeader", false);
3184
3185     // add 30 items
3186     for (int i = 0; i < 30; i++)
3187         model.addItem("Item" + QString::number(i), "");
3188
3189     QSignalSpy footerItemSpy(listview, SIGNAL(footerItemChanged()));
3190     QMetaObject::invokeMethod(canvas->rootObject(), "changeFooter");
3191
3192     QCOMPARE(footerItemSpy.count(), 1);
3193
3194     footer = findItem<QQuickText>(contentItem, "footer");
3195     QVERIFY(!footer);
3196     footer = findItem<QQuickText>(contentItem, "footer2");
3197     QVERIFY(footer);
3198
3199     QVERIFY(footer == listview->footerItem());
3200
3201     QCOMPARE(footer->pos(), changedFooterPos);
3202     QCOMPARE(footer->width(), 50.);
3203     QCOMPARE(footer->height(), 20.);
3204     QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), changedContentPos);
3205
3206     item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3207     QVERIFY(item);
3208     QCOMPARE(item->pos(), firstDelegatePos);
3209
3210     listview->positionViewAtEnd();
3211     footer->setHeight(10);
3212     footer->setWidth(40);
3213     QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), resizeContentPos);
3214
3215     delete canvas;
3216 }
3217
3218 void tst_QQuickListView::footer_data()
3219 {
3220     QTest::addColumn<QQuickListView::Orientation>("orientation");
3221     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3222     QTest::addColumn<QPointF>("initialFooterPos");
3223     QTest::addColumn<QPointF>("changedFooterPos");
3224     QTest::addColumn<QPointF>("initialContentPos");
3225     QTest::addColumn<QPointF>("changedContentPos");
3226     QTest::addColumn<QPointF>("firstDelegatePos");
3227     QTest::addColumn<QPointF>("resizeContentPos");
3228
3229     // footer1 = 100 x 30
3230     // footer2 = 50 x 20
3231     // delegates = 40 x 20
3232     // view width = 240
3233     // view height = 320
3234
3235     // footer below items, bottom left
3236     QTest::newRow("vertical, layout left to right") << QQuickListView::Vertical << Qt::LeftToRight
3237         << QPointF(0, 3 * 20)
3238         << QPointF(0, 30 * 20)  // added 30 items
3239         << QPointF(0, 0)
3240         << QPointF(0, 0)
3241         << QPointF(0, 0)
3242         << QPointF(0, 30 * 20 - 320 + 10);
3243
3244     // footer below items, bottom right
3245     QTest::newRow("vertical, layout right to left") << QQuickListView::Vertical << Qt::RightToLeft
3246         << QPointF(0, 3 * 20)
3247         << QPointF(0, 30 * 20)
3248         << QPointF(0, 0)
3249         << QPointF(0, 0)
3250         << QPointF(0, 0)
3251         << QPointF(0, 30 * 20 - 320 + 10);
3252
3253     // footer to right of items
3254     QTest::newRow("horizontal, layout left to right") << QQuickListView::Horizontal << Qt::LeftToRight
3255         << QPointF(40 * 3, 0)
3256         << QPointF(40 * 30, 0)
3257         << QPointF(0, 0)
3258         << QPointF(0, 0)
3259         << QPointF(0, 0)
3260         << QPointF(40 * 30 - 240 + 40, 0);
3261
3262     // footer to left of items
3263     QTest::newRow("horizontal, layout right to left") << QQuickListView::Horizontal << Qt::RightToLeft
3264         << QPointF(-(40 * 3) - 100, 0)
3265         << QPointF(-(40 * 30) - 50, 0)     // 50 = new footer width
3266         << QPointF(-240, 0)
3267         << QPointF(-240, 0)
3268         << QPointF(-40, 0)
3269         << QPointF(-(40 * 30) - 40, 0);
3270 }
3271
3272 class LVAccessor : public QQuickListView
3273 {
3274 public:
3275     qreal minY() const { return minYExtent(); }
3276     qreal maxY() const { return maxYExtent(); }
3277     qreal minX() const { return minXExtent(); }
3278     qreal maxX() const { return maxXExtent(); }
3279 };
3280
3281 void tst_QQuickListView::headerFooter()
3282 {
3283     {
3284         // Vertical
3285         QQuickView *canvas = createView();
3286
3287         TestModel model;
3288         QDeclarativeContext *ctxt = canvas->rootContext();
3289         ctxt->setContextProperty("testModel", &model);
3290
3291         canvas->setSource(testFileUrl("headerfooter.qml"));
3292         qApp->processEvents();
3293
3294         QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3295         QTRY_VERIFY(listview != 0);
3296
3297         QQuickItem *contentItem = listview->contentItem();
3298         QTRY_VERIFY(contentItem != 0);
3299
3300         QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
3301         QVERIFY(header);
3302         QCOMPARE(header->y(), -header->height());
3303
3304         QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
3305         QVERIFY(footer);
3306         QCOMPARE(footer->y(), 0.);
3307
3308         QCOMPARE(static_cast<LVAccessor*>(listview)->minY(), header->height());
3309         QCOMPARE(static_cast<LVAccessor*>(listview)->maxY(), header->height());
3310
3311         delete canvas;
3312     }
3313     {
3314         // Horizontal
3315         QQuickView *canvas = createView();
3316
3317         TestModel model;
3318         QDeclarativeContext *ctxt = canvas->rootContext();
3319         ctxt->setContextProperty("testModel", &model);
3320
3321         canvas->setSource(testFileUrl("headerfooter.qml"));
3322         canvas->rootObject()->setProperty("horizontal", true);
3323         qApp->processEvents();
3324
3325         QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3326         QTRY_VERIFY(listview != 0);
3327
3328         QQuickItem *contentItem = listview->contentItem();
3329         QTRY_VERIFY(contentItem != 0);
3330
3331         QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
3332         QVERIFY(header);
3333         QCOMPARE(header->x(), -header->width());
3334
3335         QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
3336         QVERIFY(footer);
3337         QCOMPARE(footer->x(), 0.);
3338
3339         QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), header->width());
3340         QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), header->width());
3341
3342         delete canvas;
3343     }
3344     {
3345         // Horizontal RTL
3346         QQuickView *canvas = createView();
3347
3348         TestModel model;
3349         QDeclarativeContext *ctxt = canvas->rootContext();
3350         ctxt->setContextProperty("testModel", &model);
3351
3352         canvas->setSource(testFileUrl("headerfooter.qml"));
3353         canvas->rootObject()->setProperty("horizontal", true);
3354         canvas->rootObject()->setProperty("rtl", true);
3355         qApp->processEvents();
3356
3357         QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3358         QTRY_VERIFY(listview != 0);
3359
3360         QQuickItem *contentItem = listview->contentItem();
3361         QTRY_VERIFY(contentItem != 0);
3362
3363         QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
3364         QVERIFY(header);
3365         QCOMPARE(header->x(), 0.);
3366
3367         QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
3368         QVERIFY(footer);
3369         QCOMPARE(footer->x(), -footer->width());
3370
3371         QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), 240. - header->width());
3372         QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), 240. - header->width());
3373
3374         delete canvas;
3375     }
3376 }
3377
3378 void tst_QQuickListView::resizeView()
3379 {
3380     QQuickView *canvas = createView();
3381
3382     TestModel model;
3383     for (int i = 0; i < 40; i++)
3384         model.addItem("Item" + QString::number(i), "");
3385
3386     QDeclarativeContext *ctxt = canvas->rootContext();
3387     ctxt->setContextProperty("testModel", &model);
3388
3389     TestObject *testObject = new TestObject;
3390     ctxt->setContextProperty("testObject", testObject);
3391
3392     canvas->setSource(testFileUrl("listviewtest.qml"));
3393     qApp->processEvents();
3394
3395     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3396     QTRY_VERIFY(listview != 0);
3397
3398     QQuickItem *contentItem = listview->contentItem();
3399     QTRY_VERIFY(contentItem != 0);
3400
3401     // Confirm items positioned correctly
3402     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3403     for (int i = 0; i < model.count() && i < itemCount; ++i) {
3404         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3405         if (!item) qWarning() << "Item" << i << "not found";
3406         QTRY_VERIFY(item);
3407         QTRY_COMPARE(item->y(), i*20.);
3408     }
3409
3410     QVariant heightRatio;
3411     QMetaObject::invokeMethod(canvas->rootObject(), "heightRatio", Q_RETURN_ARG(QVariant, heightRatio));
3412     QCOMPARE(heightRatio.toReal(), 0.4);
3413
3414     listview->setHeight(200);
3415
3416     QMetaObject::invokeMethod(canvas->rootObject(), "heightRatio", Q_RETURN_ARG(QVariant, heightRatio));
3417     QCOMPARE(heightRatio.toReal(), 0.25);
3418
3419     delete canvas;
3420     delete testObject;
3421 }
3422
3423 void tst_QQuickListView::resizeViewAndRepaint()
3424 {
3425     QQuickView *canvas = createView();
3426     canvas->show();
3427
3428     TestModel model;
3429     for (int i = 0; i < 40; i++)
3430         model.addItem("Item" + QString::number(i), "");
3431
3432     QDeclarativeContext *ctxt = canvas->rootContext();
3433     ctxt->setContextProperty("testModel", &model);
3434     ctxt->setContextProperty("initialHeight", 100);
3435
3436     canvas->setSource(testFileUrl("resizeview.qml"));
3437     qApp->processEvents();
3438
3439     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3440     QTRY_VERIFY(listview != 0);
3441     QQuickItem *contentItem = listview->contentItem();
3442     QTRY_VERIFY(contentItem != 0);
3443
3444     // item at index 10 should not be currently visible
3445     QVERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 10));
3446
3447     listview->setHeight(320);
3448     QTRY_VERIFY(findItem<QQuickItem>(contentItem, "wrapper", 10));
3449
3450     listview->setHeight(100);
3451     QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 10));
3452
3453     delete canvas;
3454 }
3455
3456 void tst_QQuickListView::sizeLessThan1()
3457 {
3458     QQuickView *canvas = createView();
3459
3460     TestModel model;
3461     for (int i = 0; i < 30; i++)
3462         model.addItem("Item" + QString::number(i), "");
3463
3464     QDeclarativeContext *ctxt = canvas->rootContext();
3465     ctxt->setContextProperty("testModel", &model);
3466
3467     TestObject *testObject = new TestObject;
3468     ctxt->setContextProperty("testObject", testObject);
3469
3470     canvas->setSource(testFileUrl("sizelessthan1.qml"));
3471     qApp->processEvents();
3472
3473     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3474     QTRY_VERIFY(listview != 0);
3475
3476     QQuickItem *contentItem = listview->contentItem();
3477     QTRY_VERIFY(contentItem != 0);
3478
3479     // Confirm items positioned correctly
3480     int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3481     for (int i = 0; i < model.count() && i < itemCount; ++i) {
3482         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3483         if (!item) qWarning() << "Item" << i << "not found";
3484         QTRY_VERIFY(item);
3485         QTRY_COMPARE(item->y(), i*0.5);
3486     }
3487
3488     delete canvas;
3489     delete testObject;
3490 }
3491
3492 void tst_QQuickListView::QTBUG_14821()
3493 {
3494     QQuickView *canvas = createView();
3495
3496     canvas->setSource(testFileUrl("qtbug14821.qml"));
3497     qApp->processEvents();
3498
3499     QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3500     QVERIFY(listview != 0);
3501
3502     QQuickItem *contentItem = listview->contentItem();
3503     QVERIFY(contentItem != 0);
3504
3505     listview->decrementCurrentIndex();
3506     QCOMPARE(listview->currentIndex(), 99);
3507
3508     listview->incrementCurrentIndex();
3509     QCOMPARE(listview->currentIndex(), 0);
3510
3511     delete canvas;
3512 }
3513
3514 void tst_QQuickListView::resizeDelegate()
3515 {
3516     QQuickView *canvas = createView();
3517     canvas->show();
3518
3519     QStringList strings;
3520     for (int i = 0; i < 30; ++i)
3521         strings << QString::number(i);
3522     QStringListModel model(strings);
3523
3524     QDeclarativeContext *ctxt = canvas->rootContext();
3525     ctxt->setContextProperty("testModel", &model);
3526
3527     canvas->setSource(testFileUrl("displaylist.qml"));
3528     qApp->processEvents();
3529
3530     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3531     QVERIFY(listview != 0);
3532
3533     QQuickItem *contentItem = listview->contentItem();
3534     QVERIFY(contentItem != 0);
3535
3536     QCOMPARE(listview->count(), model.rowCount());
3537
3538     listview->setCurrentIndex(25);
3539     listview->setContentY(0);
3540     QTest::qWait(300);
3541
3542     for (int i = 0; i < 16; ++i) {
3543         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3544         QVERIFY(item != 0);
3545         QCOMPARE(item->y(), i*20.0);
3546     }
3547
3548     QCOMPARE(listview->currentItem()->y(), 500.0);
3549     QTRY_COMPARE(listview->highlightItem()->y(), 500.0);
3550
3551     canvas->rootObject()->setProperty("delegateHeight", 30);
3552     QTest::qWait(300);
3553
3554     for (int i = 0; i < 11; ++i) {
3555         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3556         QVERIFY(item != 0);
3557         QTRY_COMPARE(item->y(), i*30.0);
3558     }
3559
3560     QTRY_COMPARE(listview->currentItem()->y(), 750.0);
3561     QTRY_COMPARE(listview->highlightItem()->y(), 750.0);
3562
3563     listview->setCurrentIndex(1);
3564     listview->positionViewAtIndex(25, QQuickListView::Beginning);
3565     listview->positionViewAtIndex(5, QQuickListView::Beginning);
3566
3567     for (int i = 5; i < 16; ++i) {
3568         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3569         QVERIFY(item != 0);
3570         QCOMPARE(item->y(), i*30.0);
3571     }
3572
3573     QTRY_COMPARE(listview->currentItem()->y(), 30.0);
3574     QTRY_COMPARE(listview->highlightItem()->y(), 30.0);
3575
3576     canvas->rootObject()->setProperty("delegateHeight", 20);
3577     QTest::qWait(300);
3578
3579     for (int i = 5; i < 11; ++i) {
3580         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3581         QVERIFY(item != 0);
3582         QTRY_COMPARE(item->y(), 150 + (i-5)*20.0);
3583     }
3584
3585     QTRY_COMPARE(listview->currentItem()->y(), 70.0);
3586     QTRY_COMPARE(listview->highlightItem()->y(), 70.0);
3587
3588     delete canvas;
3589 }
3590
3591 void tst_QQuickListView::resizeFirstDelegate()
3592 {
3593     // QTBUG-20712: Content Y jumps constantly if first delegate height == 0
3594     // and other delegates have height > 0
3595
3596     QSKIP("Test unstable - QTBUG-22872");
3597
3598     QQuickView *canvas = createView();
3599     canvas->show();
3600
3601     // bug only occurs when all items in the model are visible
3602     TestModel model;
3603     for (int i = 0; i < 10; i++)
3604         model.addItem("Item" + QString::number(i), "");
3605
3606     QDeclarativeContext *ctxt = canvas->rootContext();
3607     ctxt->setContextProperty("testModel", &model);
3608
3609     TestObject *testObject = new TestObject;
3610     ctxt->setContextProperty("testObject", testObject);
3611
3612     canvas->setSource(testFileUrl("listviewtest.qml"));
3613     qApp->processEvents();
3614
3615     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3616     QVERIFY(listview != 0);
3617
3618     QQuickItem *contentItem = listview->contentItem();
3619     QVERIFY(contentItem != 0);
3620
3621     QQuickItem *item = 0;
3622     for (int i = 0; i < model.count(); ++i) {
3623         item = findItem<QQuickItem>(contentItem, "wrapper", i);
3624         QVERIFY(item != 0);
3625         QCOMPARE(item->y(), i*20.0);
3626     }
3627
3628     item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3629     item->setHeight(0);
3630
3631     // check the content y has not jumped up and down
3632     QCOMPARE(listview->contentY(), 0.0);
3633     QSignalSpy spy(listview, SIGNAL(contentYChanged()));
3634     QTest::qWait(100);
3635     QCOMPARE(spy.count(), 0);
3636
3637     for (int i = 1; i < model.count(); ++i) {
3638         item = findItem<QQuickItem>(contentItem, "wrapper", i);
3639         QVERIFY(item != 0);
3640         QTRY_COMPARE(item->y(), (i-1)*20.0);
3641     }
3642
3643
3644     // QTBUG-22014: refill doesn't clear items scrolling off the top of the
3645     // list if they follow a zero-sized delegate
3646
3647     for (int i = 0; i < 10; i++)
3648         model.addItem("Item" + QString::number(i), "");
3649
3650     item = findItem<QQuickItem>(contentItem, "wrapper", 1);
3651     QVERIFY(item);
3652     item->setHeight(0);
3653
3654     listview->setCurrentIndex(19);
3655     qApp->processEvents();
3656
3657     // items 0-2 should have been deleted
3658     for (int i=0; i<3; i++) {
3659         QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper", i));
3660     }
3661
3662     delete testObject;
3663     delete canvas;
3664 }
3665
3666 void tst_QQuickListView::QTBUG_16037()
3667 {
3668     QQuickView *canvas = createView();
3669     canvas->show();
3670
3671     canvas->setSource(testFileUrl("qtbug16037.qml"));
3672     qApp->processEvents();
3673
3674     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "listview");
3675     QTRY_VERIFY(listview != 0);
3676
3677     QVERIFY(listview->contentHeight() <= 0.0);
3678
3679     QMetaObject::invokeMethod(canvas->rootObject(), "setModel");
3680
3681     QTRY_COMPARE(listview->contentHeight(), 80.0);
3682
3683     delete canvas;
3684 }
3685
3686 void tst_QQuickListView::indexAt()
3687 {
3688     QQuickView *canvas = createView();
3689
3690     TestModel model;
3691     for (int i = 0; i < 30; i++)
3692         model.addItem("Item" + QString::number(i), "");
3693
3694     QDeclarativeContext *ctxt = canvas->rootContext();
3695     ctxt->setContextProperty("testModel", &model);
3696
3697     TestObject *testObject = new TestObject;
3698     ctxt->setContextProperty("testObject", testObject);
3699
3700     canvas->setSource(testFileUrl("listviewtest.qml"));
3701     qApp->processEvents();
3702
3703     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3704     QTRY_VERIFY(listview != 0);
3705
3706     QQuickItem *contentItem = listview->contentItem();
3707     QTRY_VERIFY(contentItem != 0);
3708
3709     QCOMPARE(listview->indexAt(0,0), 0);
3710     QCOMPARE(listview->indexAt(0,19), 0);
3711     QCOMPARE(listview->indexAt(239,19), 0);
3712     QCOMPARE(listview->indexAt(0,20), 1);
3713     QCOMPARE(listview->indexAt(240,20), -1);
3714
3715     delete canvas;
3716     delete testObject;
3717 }
3718
3719 void tst_QQuickListView::incrementalModel()
3720 {
3721     QQuickView *canvas = createView();
3722
3723     IncrementalModel model;
3724     QDeclarativeContext *ctxt = canvas->rootContext();
3725     ctxt->setContextProperty("testModel", &model);
3726
3727     canvas->setSource(testFileUrl("displaylist.qml"));
3728     qApp->processEvents();
3729
3730     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3731     QTRY_VERIFY(listview != 0);
3732
3733     QQuickItem *contentItem = listview->contentItem();
3734     QTRY_VERIFY(contentItem != 0);
3735
3736     QTRY_COMPARE(listview->count(), 20);
3737
3738     listview->positionViewAtIndex(10, QQuickListView::Beginning);
3739
3740     QTRY_COMPARE(listview->count(), 25);
3741
3742     delete canvas;
3743 }
3744
3745 void tst_QQuickListView::onAdd()
3746 {
3747     QFETCH(int, initialItemCount);
3748     QFETCH(int, itemsToAdd);
3749
3750     const int delegateHeight = 10;
3751     TestModel2 model;
3752
3753     // these initial items should not trigger ListView.onAdd
3754     for (int i=0; i<initialItemCount; i++)
3755         model.addItem("dummy value", "dummy value");
3756
3757     QQuickView *canvas = createView();
3758     canvas->setGeometry(0,0,200, delegateHeight * (initialItemCount + itemsToAdd));
3759     QDeclarativeContext *ctxt = canvas->rootContext();
3760     ctxt->setContextProperty("testModel", &model);
3761     ctxt->setContextProperty("delegateHeight", delegateHeight);
3762     canvas->setSource(testFileUrl("attachedSignals.qml"));
3763
3764     QObject *object = canvas->rootObject();
3765     object->setProperty("width", canvas->width());
3766     object->setProperty("height", canvas->height());
3767     qApp->processEvents();
3768
3769     QList<QPair<QString, QString> > items;
3770     for (int i=0; i<itemsToAdd; i++)
3771         items << qMakePair(QString("value %1").arg(i), QString::number(i));
3772     model.addItems(items);
3773     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
3774
3775     QVariantList result = object->property("addedDelegates").toList();
3776     QCOMPARE(result.count(), items.count());
3777     for (int i=0; i<items.count(); i++)
3778         QCOMPARE(result[i].toString(), items[i].first);
3779
3780     delete canvas;
3781 }
3782
3783 void tst_QQuickListView::onAdd_data()
3784 {
3785     QTest::addColumn<int>("initialItemCount");
3786     QTest::addColumn<int>("itemsToAdd");
3787
3788     QTest::newRow("0, add 1") << 0 << 1;
3789     QTest::newRow("0, add 2") << 0 << 2;
3790     QTest::newRow("0, add 10") << 0 << 10;
3791
3792     QTest::newRow("1, add 1") << 1 << 1;
3793     QTest::newRow("1, add 2") << 1 << 2;
3794     QTest::newRow("1, add 10") << 1 << 10;
3795
3796     QTest::newRow("5, add 1") << 5 << 1;
3797     QTest::newRow("5, add 2") << 5 << 2;
3798     QTest::newRow("5, add 10") << 5 << 10;
3799 }
3800
3801 void tst_QQuickListView::onRemove()
3802 {
3803     QFETCH(int, initialItemCount);
3804     QFETCH(int, indexToRemove);
3805     QFETCH(int, removeCount);
3806
3807     const int delegateHeight = 10;
3808     TestModel2 model;
3809     for (int i=0; i<initialItemCount; i++)
3810         model.addItem(QString("value %1").arg(i), "dummy value");
3811
3812     QQuickView *canvas = createView();
3813     QDeclarativeContext *ctxt = canvas->rootContext();
3814     ctxt->setContextProperty("testModel", &model);
3815     ctxt->setContextProperty("delegateHeight", delegateHeight);
3816     canvas->setSource(testFileUrl("attachedSignals.qml"));
3817     QObject *object = canvas->rootObject();
3818
3819     model.removeItems(indexToRemove, removeCount);
3820     QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
3821
3822     QCOMPARE(object->property("removedDelegateCount"), QVariant(removeCount));
3823
3824     delete canvas;
3825 }
3826
3827 void tst_QQuickListView::onRemove_data()
3828 {
3829     QTest::addColumn<int>("initialItemCount");
3830     QTest::addColumn<int>("indexToRemove");
3831     QTest::addColumn<int>("removeCount");
3832
3833     QTest::newRow("remove first") << 1 << 0 << 1;
3834     QTest::newRow("two items, remove first") << 2 << 0 << 1;
3835     QTest::newRow("two items, remove last") << 2 << 1 << 1;
3836     QTest::newRow("two items, remove all") << 2 << 0 << 2;
3837
3838     QTest::newRow("four items, remove first") << 4 << 0 << 1;
3839     QTest::newRow("four items, remove 0-2") << 4 << 0 << 2;
3840     QTest::newRow("four items, remove 1-3") << 4 << 1 << 2;
3841     QTest::newRow("four items, remove 2-4") << 4 << 2 << 2;
3842     QTest::newRow("four items, remove last") << 4 << 3 << 1;
3843     QTest::newRow("four items, remove all") << 4 << 0 << 4;
3844
3845     QTest::newRow("ten items, remove 1-8") << 10 << 0 << 8;
3846     QTest::newRow("ten items, remove 2-7") << 10 << 2 << 5;
3847     QTest::newRow("ten items, remove 4-10") << 10 << 4 << 6;
3848 }
3849
3850 void tst_QQuickListView::rightToLeft()
3851 {
3852     QQuickView *canvas = createView();
3853     canvas->setGeometry(0,0,640,320);
3854     canvas->setSource(testFileUrl("rightToLeft.qml"));
3855     qApp->processEvents();
3856
3857     QVERIFY(canvas->rootObject() != 0);
3858     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "view");
3859     QTRY_VERIFY(listview != 0);
3860
3861     QQuickItem *contentItem = listview->contentItem();
3862     QTRY_VERIFY(contentItem != 0);
3863
3864     QQuickVisualItemModel *model = canvas->rootObject()->findChild<QQuickVisualItemModel*>("itemModel");
3865     QTRY_VERIFY(model != 0);
3866
3867     QTRY_VERIFY(model->count() == 3);
3868     QTRY_COMPARE(listview->currentIndex(), 0);
3869
3870     // initial position at first item, right edge aligned
3871     QCOMPARE(listview->contentX(), -640.);
3872
3873     QQuickItem *item = findItem<QQuickItem>(contentItem, "item1");
3874     QTRY_VERIFY(item);
3875     QTRY_COMPARE(item->x(), -100.0);
3876     QCOMPARE(item->height(), listview->height());
3877
3878     QQuickText *text = findItem<QQuickText>(contentItem, "text1");
3879     QTRY_VERIFY(text);
3880     QTRY_COMPARE(text->text(), QLatin1String("index: 0"));
3881
3882     listview->setCurrentIndex(2);
3883
3884     item = findItem<QQuickItem>(contentItem, "item3");
3885     QTRY_VERIFY(item);
3886     QTRY_COMPARE(item->x(), -540.0);
3887
3888     text = findItem<QQuickText>(contentItem, "text3");
3889     QTRY_VERIFY(text);
3890     QTRY_COMPARE(text->text(), QLatin1String("index: 2"));
3891
3892     QCOMPARE(listview->contentX(), -640.);
3893
3894     // Ensure resizing maintains position relative to right edge
3895     qobject_cast<QQuickItem*>(canvas->rootObject())->setWidth(600);
3896     QTRY_COMPARE(listview->contentX(), -600.);
3897
3898     delete canvas;
3899 }
3900
3901 void tst_QQuickListView::test_mirroring()
3902 {
3903     QQuickView *canvasA = createView();
3904     canvasA->setSource(testFileUrl("rightToLeft.qml"));
3905     QQuickListView *listviewA = findItem<QQuickListView>(canvasA->rootObject(), "view");
3906     QTRY_VERIFY(listviewA != 0);
3907
3908     QQuickView *canvasB = createView();
3909     canvasB->setSource(testFileUrl("rightToLeft.qml"));
3910     QQuickListView *listviewB = findItem<QQuickListView>(canvasB->rootObject(), "view");
3911     QTRY_VERIFY(listviewA != 0);
3912     qApp->processEvents();
3913
3914     QList<QString> objectNames;
3915     objectNames << "item1" << "item2"; // << "item3"
3916
3917     listviewA->setProperty("layoutDirection", Qt::LeftToRight);
3918     listviewB->setProperty("layoutDirection", Qt::RightToLeft);
3919     QCOMPARE(listviewA->layoutDirection(), listviewA->effectiveLayoutDirection());
3920
3921     // LTR != RTL
3922     foreach (const QString objectName, objectNames)
3923         QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x());
3924
3925     listviewA->setProperty("layoutDirection", Qt::LeftToRight);
3926     listviewB->setProperty("layoutDirection", Qt::LeftToRight);
3927
3928     // LTR == LTR
3929     foreach (const QString objectName, objectNames)
3930         QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x());
3931
3932     QVERIFY(listviewB->layoutDirection() == listviewB->effectiveLayoutDirection());
3933     QQuickItemPrivate::get(listviewB)->setLayoutMirror(true);
3934     QVERIFY(listviewB->layoutDirection() != listviewB->effectiveLayoutDirection());
3935
3936     // LTR != LTR+mirror
3937     foreach (const QString objectName, objectNames)
3938         QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x());
3939
3940     listviewA->setProperty("layoutDirection", Qt::RightToLeft);
3941
3942     // RTL == LTR+mirror
3943     foreach (const QString objectName, objectNames)
3944         QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x());
3945
3946     listviewB->setProperty("layoutDirection", Qt::RightToLeft);
3947
3948     // RTL != RTL+mirror
3949     foreach (const QString objectName, objectNames)
3950         QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x());
3951
3952     listviewA->setProperty("layoutDirection", Qt::LeftToRight);
3953
3954     // LTR == RTL+mirror
3955     foreach (const QString objectName, objectNames)
3956         QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x());
3957
3958     delete canvasA;
3959     delete canvasB;
3960 }
3961
3962 void tst_QQuickListView::margins()
3963 {
3964     QQuickView *canvas = createView();
3965
3966     TestModel2 model;
3967     for (int i = 0; i < 50; i++)
3968         model.addItem("Item" + QString::number(i), "");
3969
3970     QDeclarativeContext *ctxt = canvas->rootContext();
3971     ctxt->setContextProperty("testModel", &model);
3972
3973     canvas->setSource(testFileUrl("margins.qml"));
3974     canvas->show();
3975     qApp->processEvents();
3976
3977     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3978     QTRY_VERIFY(listview != 0);
3979
3980     QQuickItem *contentItem = listview->contentItem();
3981     QTRY_VERIFY(contentItem != 0);
3982
3983     QCOMPARE(listview->contentY(), -30.);
3984     QCOMPARE(listview->yOrigin(), 0.);
3985
3986     // check end bound
3987     listview->positionViewAtEnd();
3988     qreal pos = listview->contentY();
3989     listview->setContentY(pos + 80);
3990     listview->returnToBounds();
3991     QTRY_COMPARE(listview->contentY(), pos + 50);
3992
3993     // remove item before visible and check that top margin is maintained
3994     // and yOrigin is updated
3995     listview->setContentY(100);
3996     model.removeItem(1);
3997     QTRY_COMPARE(listview->count(), model.count());
3998     listview->setContentY(-50);
3999     QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
4000     listview->returnToBounds();
4001     QCOMPARE(listview->yOrigin(), 20.);
4002     QTRY_COMPARE(listview->contentY(), -10.);
4003
4004     // reduce top margin
4005     listview->setTopMargin(20);
4006     QCOMPARE(listview->yOrigin(), 20.);
4007     QTRY_COMPARE(listview->contentY(), 0.);
4008
4009     // check end bound
4010     listview->positionViewAtEnd();
4011     pos = listview->contentY();
4012     listview->setContentY(pos + 80);
4013     listview->returnToBounds();
4014     QTRY_COMPARE(listview->contentY(), pos + 50);
4015
4016     // reduce bottom margin
4017     pos = listview->contentY();
4018     listview->setBottomMargin(40);
4019     QCOMPARE(listview->yOrigin(), 20.);
4020     QTRY_COMPARE(listview->contentY(), pos-10);
4021
4022     delete canvas;
4023 }
4024
4025 void tst_QQuickListView::snapToItem_data()
4026 {
4027     QTest::addColumn<QQuickListView::Orientation>("orientation");
4028     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
4029     QTest::addColumn<int>("highlightRangeMode");
4030     QTest::addColumn<QPoint>("flickStart");
4031     QTest::addColumn<QPoint>("flickEnd");
4032     QTest::addColumn<qreal>("snapAlignment");
4033     QTest::addColumn<qreal>("endExtent");
4034     QTest::addColumn<qreal>("startExtent");
4035
4036     QTest::newRow("vertical, left to right") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4037         << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 1200.0 << 0.0;
4038
4039     QTest::newRow("horizontal, left to right") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4040         << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 1200.0 << 0.0;
4041
4042     QTest::newRow("horizontal, right to left") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
4043         << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -1200.0 - 240.0 << -240.0;
4044
4045     QTest::newRow("vertical, left to right, enforce range") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4046         << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 1340.0 << -20.0;
4047
4048     QTest::newRow("horizontal, left to right, enforce range") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4049         << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 1340.0 << -20.0;
4050
4051     QTest::newRow("horizontal, right to left, enforce range") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
4052         << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -1200.0 - 240.0 - 140.0 << -220.0;
4053 }
4054
4055 void tst_QQuickListView::snapToItem()
4056 {
4057     QFETCH(QQuickListView::Orientation, orientation);
4058     QFETCH(Qt::LayoutDirection, layoutDirection);
4059     QFETCH(int, highlightRangeMode);
4060     QFETCH(QPoint, flickStart);
4061     QFETCH(QPoint, flickEnd);
4062     QFETCH(qreal, snapAlignment);
4063     QFETCH(qreal, endExtent);
4064     QFETCH(qreal, startExtent);
4065
4066     QQuickView *canvas = createView();
4067
4068     canvas->setSource(testFileUrl("snapToItem.qml"));
4069     canvas->show();
4070     qApp->processEvents();
4071
4072     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
4073     QTRY_VERIFY(listview != 0);
4074
4075     listview->setOrientation(orientation);
4076     listview->setLayoutDirection(layoutDirection);
4077     listview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
4078
4079     QQuickItem *contentItem = listview->contentItem();
4080     QTRY_VERIFY(contentItem != 0);
4081
4082     // confirm that a flick hits an item boundary
4083     flick(canvas, flickStart, flickEnd, 180);
4084     QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4085     if (orientation == QQuickListView::Vertical)
4086         QCOMPARE(qreal(fmod(listview->contentY(),80.0)), snapAlignment);
4087     else
4088         QCOMPARE(qreal(fmod(listview->contentX(),80.0)), snapAlignment);
4089
4090     // flick to end
4091     do {
4092         flick(canvas, flickStart, flickEnd, 180);
4093         QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4094     } while (orientation == QQuickListView::Vertical
4095            ? !listview->isAtYEnd()
4096            : layoutDirection == Qt::LeftToRight ? !listview->isAtXEnd() : !listview->isAtXBeginning());
4097
4098     if (orientation == QQuickListView::Vertical)
4099         QCOMPARE(listview->contentY(), endExtent);
4100     else
4101         QCOMPARE(listview->contentX(), endExtent);
4102
4103     // flick to start
4104     do {
4105         flick(canvas, flickEnd, flickStart, 180);
4106         QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4107     } while (orientation == QQuickListView::Vertical
4108            ? !listview->isAtYBeginning()
4109            : layoutDirection == Qt::LeftToRight ? !listview->isAtXBeginning() : !listview->isAtXEnd());
4110
4111     if (orientation == QQuickListView::Vertical)
4112         QCOMPARE(listview->contentY(), startExtent);
4113     else
4114         QCOMPARE(listview->contentX(), startExtent);
4115
4116     delete canvas;
4117 }
4118
4119 void tst_QQuickListView::qListModelInterface_items()
4120 {
4121     items<TestModel>(testFileUrl("listviewtest.qml"), false);
4122 }
4123
4124 void tst_QQuickListView::qListModelInterface_package_items()
4125 {
4126     items<TestModel>(testFileUrl("listviewtest-package.qml"), true);
4127 }
4128
4129 void tst_QQuickListView::qAbstractItemModel_items()
4130 {
4131     items<TestModel2>(testFileUrl("listviewtest.qml"), false);
4132 }
4133
4134 void tst_QQuickListView::qListModelInterface_changed()
4135 {
4136     changed<TestModel>(testFileUrl("listviewtest.qml"), false);
4137 }
4138
4139 void tst_QQuickListView::qListModelInterface_package_changed()
4140 {
4141     changed<TestModel>(testFileUrl("listviewtest-package.qml"), true);
4142 }
4143
4144 void tst_QQuickListView::qAbstractItemModel_changed()
4145 {
4146     changed<TestModel2>(testFileUrl("listviewtest.qml"), false);
4147 }
4148
4149 void tst_QQuickListView::qListModelInterface_inserted()
4150 {
4151     inserted<TestModel>(testFileUrl("listviewtest.qml"));
4152 }
4153
4154 void tst_QQuickListView::qListModelInterface_package_inserted()
4155 {
4156     inserted<TestModel>(testFileUrl("listviewtest-package.qml"));
4157 }
4158
4159 void tst_QQuickListView::qListModelInterface_inserted_more()
4160 {
4161     inserted_more<TestModel>();
4162 }
4163
4164 void tst_QQuickListView::qListModelInterface_inserted_more_data()
4165 {
4166     inserted_more_data();
4167 }
4168
4169 void tst_QQuickListView::qAbstractItemModel_inserted()
4170 {
4171     inserted<TestModel2>(testFileUrl("listviewtest.qml"));
4172 }
4173
4174 void tst_QQuickListView::qAbstractItemModel_inserted_more()
4175 {
4176     inserted_more<TestModel2>();
4177 }
4178
4179 void tst_QQuickListView::qAbstractItemModel_inserted_more_data()
4180 {
4181     inserted_more_data();
4182 }
4183
4184 void tst_QQuickListView::qListModelInterface_removed()
4185 {
4186     removed<TestModel>(testFileUrl("listviewtest.qml"), false);
4187     removed<TestModel>(testFileUrl("listviewtest.qml"), true);
4188 }
4189
4190 void tst_QQuickListView::qListModelInterface_package_removed()
4191 {
4192     removed<TestModel>(testFileUrl("listviewtest-package.qml"), false);
4193     removed<TestModel>(testFileUrl("listviewtest-package.qml"), true);
4194 }
4195
4196 void tst_QQuickListView::qAbstractItemModel_removed()
4197 {
4198     removed<TestModel2>(testFileUrl("listviewtest.qml"), false);
4199     removed<TestModel2>(testFileUrl("listviewtest.qml"), true);
4200 }
4201
4202 void tst_QQuickListView::qListModelInterface_moved()
4203 {
4204     moved<TestModel>(testFileUrl("listviewtest.qml"));
4205 }
4206
4207 void tst_QQuickListView::qListModelInterface_moved_data()
4208 {
4209     moved_data();
4210 }
4211
4212 void tst_QQuickListView::qListModelInterface_package_moved()
4213 {
4214     moved<TestModel>(testFileUrl("listviewtest-package.qml"));
4215 }
4216
4217 void tst_QQuickListView::qListModelInterface_package_moved_data()
4218 {
4219     moved_data();
4220 }
4221
4222 void tst_QQuickListView::qAbstractItemModel_moved()
4223 {
4224     moved<TestModel2>(testFileUrl("listviewtest.qml"));
4225 }
4226
4227 void tst_QQuickListView::qAbstractItemModel_moved_data()
4228 {
4229     moved_data();
4230 }
4231
4232 void tst_QQuickListView::qListModelInterface_clear()
4233 {
4234     clear<TestModel>(testFileUrl("listviewtest.qml"));
4235 }
4236
4237 void tst_QQuickListView::qListModelInterface_package_clear()
4238 {
4239     clear<TestModel>(testFileUrl("listviewtest-package.qml"));
4240 }
4241
4242 void tst_QQuickListView::qAbstractItemModel_clear()
4243 {
4244     clear<TestModel2>(testFileUrl("listviewtest.qml"));
4245 }
4246
4247 void tst_QQuickListView::qListModelInterface_sections()
4248 {
4249     sections<TestModel>(testFileUrl("listview-sections.qml"));
4250 }
4251
4252 void tst_QQuickListView::qListModelInterface_package_sections()
4253 {
4254     sections<TestModel>(testFileUrl("listview-sections-package.qml"));
4255 }
4256
4257 void tst_QQuickListView::qAbstractItemModel_sections()
4258 {
4259     sections<TestModel2>(testFileUrl("listview-sections.qml"));
4260 }
4261
4262 void tst_QQuickListView::creationContext()
4263 {
4264     QQuickView canvas;
4265     canvas.setGeometry(0,0,240,320);
4266     canvas.setSource(testFileUrl("creationContext.qml"));
4267     qApp->processEvents();
4268
4269     QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
4270     QVERIFY(rootItem);
4271     QVERIFY(rootItem->property("count").toInt() > 0);
4272
4273     QQuickItem *item;
4274     QVERIFY(item = rootItem->findChild<QQuickItem *>("listItem"));
4275     QCOMPARE(item->property("text").toString(), QString("Hello!"));
4276     QVERIFY(item = rootItem->findChild<QQuickItem *>("header"));
4277     QCOMPARE(item->property("text").toString(), QString("Hello!"));
4278     QVERIFY(item = rootItem->findChild<QQuickItem *>("footer"));
4279     QCOMPARE(item->property("text").toString(), QString("Hello!"));
4280     QVERIFY(item = rootItem->findChild<QQuickItem *>("section"));
4281     QCOMPARE(item->property("text").toString(), QString("Hello!"));
4282 }
4283
4284 void tst_QQuickListView::QTBUG_21742()
4285 {
4286     QQuickView canvas;
4287     canvas.setGeometry(0,0,200,200);
4288     canvas.setSource(testFileUrl("qtbug-21742.qml"));
4289     qApp->processEvents();
4290
4291     QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
4292     QVERIFY(rootItem);
4293     QCOMPARE(rootItem->property("count").toInt(), 1);
4294 }
4295
4296 QQuickView *tst_QQuickListView::createView()
4297 {
4298     QQuickView *canvas = new QQuickView(0);
4299     canvas->setGeometry(0,0,240,320);
4300
4301     return canvas;
4302 }
4303
4304 void tst_QQuickListView::flick(QQuickView *canvas, const QPoint &from, const QPoint &to, int duration)
4305 {
4306     const int pointCount = 5;
4307     QPoint diff = to - from;
4308
4309     // send press, five equally spaced moves, and release.
4310     QTest::mousePress(canvas, Qt::LeftButton, 0, from);
4311
4312     for (int i = 0; i < pointCount; ++i) {
4313         QMouseEvent mv(QEvent::MouseMove, from + (i+1)*diff/pointCount, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
4314         QApplication::sendEvent(canvas, &mv);
4315         QTest::qWait(duration/pointCount);
4316         QCoreApplication::processEvents();
4317     }
4318
4319     QTest::mouseRelease(canvas, Qt::LeftButton, 0, to);
4320 }
4321
4322 void tst_QQuickListView::asynchronous()
4323 {
4324     QQuickView *canvas = createView();
4325     canvas->show();
4326     QDeclarativeIncubationController controller;
4327     canvas->engine()->setIncubationController(&controller);
4328
4329     canvas->setSource(testFileUrl("asyncloader.qml"));
4330
4331     QQuickItem *rootObject = qobject_cast<QQuickItem*>(canvas->rootObject());
4332     QVERIFY(rootObject);
4333
4334     QQuickListView *listview = 0;
4335     while (!listview) {
4336         bool b = false;
4337         controller.incubateWhile(&b);
4338         listview = rootObject->findChild<QQuickListView*>("view");
4339     }
4340
4341     // items will be created one at a time
4342     for (int i = 0; i < 8; ++i) {
4343         QVERIFY(findItem<QQuickItem>(listview, "wrapper", i) == 0);
4344         QQuickItem *item = 0;
4345         while (!item) {
4346             bool b = false;
4347             controller.incubateWhile(&b);
4348             item = findItem<QQuickItem>(listview, "wrapper", i);
4349         }
4350     }
4351
4352     {
4353         bool b = true;
4354         controller.incubateWhile(&b);
4355     }
4356
4357     // verify positioning
4358     QQuickItem *contentItem = listview->contentItem();
4359     for (int i = 0; i < 8; ++i) {
4360         QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
4361         QTRY_COMPARE(item->y(), i*50.0);
4362     }
4363
4364     delete canvas;
4365 }
4366
4367 void tst_QQuickListView::snapOneItem_data()
4368 {
4369     QTest::addColumn<QQuickListView::Orientation>("orientation");
4370     QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
4371     QTest::addColumn<int>("highlightRangeMode");
4372     QTest::addColumn<QPoint>("flickStart");
4373     QTest::addColumn<QPoint>("flickEnd");
4374     QTest::addColumn<qreal>("snapAlignment");
4375     QTest::addColumn<qreal>("endExtent");
4376     QTest::addColumn<qreal>("startExtent");
4377
4378     QTest::newRow("vertical, left to right") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4379         << QPoint(20, 200) << QPoint(20, 20) << 180.0 << 560.0 << 0.0;
4380
4381     QTest::newRow("horizontal, left to right") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4382         << QPoint(200, 20) << QPoint(20, 20) << 180.0 << 560.0 << 0.0;
4383
4384     QTest::newRow("horizontal, right to left") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
4385         << QPoint(20, 20) << QPoint(200, 20) << -420.0 << -560.0 - 240.0 << -240.0;
4386
4387     QTest::newRow("vertical, left to right, enforce range") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4388         << QPoint(20, 200) << QPoint(20, 20) << 180.0 << 580.0 << -20.0;
4389
4390     QTest::newRow("horizontal, left to right, enforce range") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4391         << QPoint(200, 20) << QPoint(20, 20) << 180.0 << 580.0 << -20.0;
4392
4393     QTest::newRow("horizontal, right to left, enforce range") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
4394         << QPoint(20, 20) << QPoint(200, 20) << -420.0 << -580.0 - 240.0 << -220.0;
4395 }
4396
4397 void tst_QQuickListView::snapOneItem()
4398 {
4399     QFETCH(QQuickListView::Orientation, orientation);
4400     QFETCH(Qt::LayoutDirection, layoutDirection);
4401     QFETCH(int, highlightRangeMode);
4402     QFETCH(QPoint, flickStart);
4403     QFETCH(QPoint, flickEnd);
4404     QFETCH(qreal, snapAlignment);
4405     QFETCH(qreal, endExtent);
4406     QFETCH(qreal, startExtent);
4407
4408     QQuickView *canvas = createView();
4409
4410     canvas->setSource(testFileUrl("snapOneItem.qml"));
4411     canvas->show();
4412     qApp->processEvents();
4413
4414     QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
4415     QTRY_VERIFY(listview != 0);
4416
4417     listview->setOrientation(orientation);
4418     listview->setLayoutDirection(layoutDirection);
4419     listview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
4420
4421     QQuickItem *contentItem = listview->contentItem();
4422     QTRY_VERIFY(contentItem != 0);
4423
4424     QSignalSpy currentIndexSpy(listview, SIGNAL(currentIndexChanged()));
4425
4426     // confirm that a flick hits the next item boundary
4427     flick(canvas, flickStart, flickEnd, 180);
4428     QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4429     if (orientation == QQuickListView::Vertical)
4430         QCOMPARE(listview->contentY(), snapAlignment);
4431     else
4432         QCOMPARE(listview->contentX(), snapAlignment);
4433
4434     if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4435         QCOMPARE(listview->currentIndex(), 1);
4436         QCOMPARE(currentIndexSpy.count(), 1);
4437     }
4438
4439     // flick to end
4440     do {
4441         flick(canvas, flickStart, flickEnd, 180);
4442         QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4443     } while (orientation == QQuickListView::Vertical
4444            ? !listview->isAtYEnd()
4445            : layoutDirection == Qt::LeftToRight ? !listview->isAtXEnd() : !listview->isAtXBeginning());
4446
4447     if (orientation == QQuickListView::Vertical)
4448         QCOMPARE(listview->contentY(), endExtent);
4449     else
4450         QCOMPARE(listview->contentX(), endExtent);
4451
4452     if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4453         QCOMPARE(listview->currentIndex(), 3);
4454         QCOMPARE(currentIndexSpy.count(), 3);
4455     }
4456
4457     // flick to start
4458     do {
4459         flick(canvas, flickEnd, flickStart, 180);
4460         QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4461     } while (orientation == QQuickListView::Vertical
4462            ? !listview->isAtYBeginning()
4463            : layoutDirection == Qt::LeftToRight ? !listview->isAtXBeginning() : !listview->isAtXEnd());
4464
4465     if (orientation == QQuickListView::Vertical)
4466         QCOMPARE(listview->contentY(), startExtent);
4467     else
4468         QCOMPARE(listview->contentX(), startExtent);
4469
4470     if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4471         QCOMPARE(listview->currentIndex(), 0);
4472         QCOMPARE(currentIndexSpy.count(), 6);
4473     }
4474
4475     delete canvas;
4476 }
4477
4478 void tst_QQuickListView::unrequestedVisibility()
4479 {
4480     TestModel model;
4481     for (int i = 0; i < 30; i++)
4482         model.addItem("Item" + QString::number(i), QString::number(i));
4483
4484     QQuickView *canvas = new QQuickView(0);
4485     canvas->setGeometry(0,0,240,320);
4486
4487     QDeclarativeContext *ctxt = canvas->rootContext();
4488     ctxt->setContextProperty("testModel", &model);
4489     ctxt->setContextProperty("testWrap", QVariant(false));
4490
4491     canvas->setSource(testFileUrl("unrequestedItems.qml"));
4492
4493     canvas->show();
4494
4495     qApp->processEvents();
4496
4497
4498     QQuickListView *leftview = findItem<QQuickListView>(canvas->rootObject(), "leftList");
4499     QTRY_VERIFY(leftview != 0);
4500
4501     QQuickListView *rightview = findItem<QQuickListView>(canvas->rootObject(), "rightList");
4502     QTRY_VERIFY(rightview != 0);
4503
4504     QQuickItem *leftContent = leftview->contentItem();
4505     QTRY_VERIFY(leftContent != 0);
4506
4507     QQuickItem *rightContent = rightview->contentItem();
4508     QTRY_VERIFY(rightContent != 0);
4509
4510     rightview->setCurrentIndex(20);
4511
4512     QTRY_COMPARE(leftview->contentY(), 0.0);
4513     QTRY_COMPARE(rightview->contentY(), 100.0);
4514
4515     QQuickItem *item;
4516
4517     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
4518     QCOMPARE(item->isVisible(), true);
4519     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
4520     QCOMPARE(item->isVisible(), false);
4521
4522     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 19));
4523     QCOMPARE(item->isVisible(), false);
4524     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 19));
4525     QCOMPARE(item->isVisible(), true);
4526
4527     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 16));
4528     QCOMPARE(item->isVisible(), true);
4529     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 17));
4530     QCOMPARE(item->isVisible(), false);
4531     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 3));
4532     QCOMPARE(item->isVisible(), false);
4533     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 4));
4534     QCOMPARE(item->isVisible(), true);
4535
4536     rightview->setCurrentIndex(0);
4537
4538     QTRY_COMPARE(leftview->contentY(), 0.0);
4539     QTRY_COMPARE(rightview->contentY(), 0.0);
4540
4541     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
4542     QCOMPARE(item->isVisible(), true);
4543     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
4544     QTRY_COMPARE(item->isVisible(), true);
4545
4546     QVERIFY(!findItem<QQuickItem>(leftContent, "wrapper", 19));
4547     QVERIFY(!findItem<QQuickItem>(rightContent, "wrapper", 19));
4548
4549     leftview->setCurrentIndex(20);
4550
4551     QTRY_COMPARE(leftview->contentY(), 100.0);
4552     QTRY_COMPARE(rightview->contentY(), 0.0);
4553
4554     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
4555     QTRY_COMPARE(item->isVisible(), false);
4556     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
4557     QCOMPARE(item->isVisible(), true);
4558
4559     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 19));
4560     QCOMPARE(item->isVisible(), true);
4561     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 19));
4562     QCOMPARE(item->isVisible(), false);
4563
4564     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
4565     QCOMPARE(item->isVisible(), false);
4566     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4567     QCOMPARE(item->isVisible(), true);
4568     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4569     QCOMPARE(item->isVisible(), true);
4570     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4571     QCOMPARE(item->isVisible(), false);
4572
4573     model.moveItems(19, 1, 1);
4574     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4575
4576     QTRY_VERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
4577     QCOMPARE(item->isVisible(), false);
4578     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
4579     QCOMPARE(item->isVisible(), true);
4580
4581     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 19));
4582     QCOMPARE(item->isVisible(), true);
4583     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 19));
4584     QCOMPARE(item->isVisible(), false);
4585
4586     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4587     QCOMPARE(item->isVisible(), false);
4588     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4589     QCOMPARE(item->isVisible(), true);
4590     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4591     QCOMPARE(item->isVisible(), true);
4592     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4593     QCOMPARE(item->isVisible(), false);
4594
4595     model.moveItems(3, 4, 1);
4596     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4597
4598     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4599     QCOMPARE(item->isVisible(), false);
4600     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4601     QCOMPARE(item->isVisible(), true);
4602     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4603     QCOMPARE(item->isVisible(), true);
4604     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4605     QCOMPARE(item->isVisible(), false);
4606
4607     model.moveItems(4, 3, 1);
4608     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4609
4610     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4611     QCOMPARE(item->isVisible(), false);
4612     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4613     QCOMPARE(item->isVisible(), true);
4614     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4615     QCOMPARE(item->isVisible(), true);
4616     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4617     QCOMPARE(item->isVisible(), false);
4618
4619     model.moveItems(16, 17, 1);
4620     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4621
4622     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4623     QCOMPARE(item->isVisible(), false);
4624     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4625     QCOMPARE(item->isVisible(), true);
4626     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4627     QCOMPARE(item->isVisible(), true);
4628     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4629     QCOMPARE(item->isVisible(), false);
4630
4631     model.moveItems(17, 16, 1);
4632     QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4633
4634     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4635     QCOMPARE(item->isVisible(), false);
4636     QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 6));
4637     QCOMPARE(item->isVisible(), true);
4638     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4639     QCOMPARE(item->isVisible(), true);
4640     QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4641     QCOMPARE(item->isVisible(), false);
4642
4643     delete canvas;
4644 }
4645
4646 QQuickItem *tst_QQuickListView::findVisibleChild(QQuickItem *parent, const QString &objectName)
4647 {
4648     QQuickItem *item = 0;
4649     QList<QQuickItem*> items = parent->findChildren<QQuickItem*>(objectName);
4650     for (int i = 0; i < items.count(); ++i) {
4651         if (items.at(i)->isVisible()) {
4652             item = items.at(i);
4653             break;
4654         }
4655     }
4656     return item;
4657 }
4658 /*
4659    Find an item with the specified objectName.  If index is supplied then the
4660    item must also evaluate the {index} expression equal to index
4661 */
4662 template<typename T>
4663 T *tst_QQuickListView::findItem(QQuickItem *parent, const QString &objectName, int index)
4664 {
4665     const QMetaObject &mo = T::staticMetaObject;
4666     //qDebug() << parent->childItems().count() << "children";
4667     for (int i = 0; i < parent->childItems().count(); ++i) {
4668         QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
4669         if (!item)
4670             continue;
4671         //qDebug() << "try" << item;
4672         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
4673             if (index != -1) {
4674                 QDeclarativeExpression e(qmlContext(item), item, "index");
4675                 if (e.evaluate().toInt() == index)
4676                     return static_cast<T*>(item);
4677             } else {
4678                 return static_cast<T*>(item);
4679             }
4680         }
4681         item = findItem<T>(item, objectName, index);
4682         if (item)
4683             return static_cast<T*>(item);
4684     }
4685
4686     return 0;
4687 }
4688
4689 template<typename T>
4690 QList<T*> tst_QQuickListView::findItems(QQuickItem *parent, const QString &objectName, bool visibleOnly)
4691 {
4692     QList<T*> items;
4693     const QMetaObject &mo = T::staticMetaObject;
4694     //qDebug() << parent->childItems().count() << "children";
4695     for (int i = 0; i < parent->childItems().count(); ++i) {
4696         QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
4697         if (!item || (visibleOnly && !item->isVisible()))
4698             continue;
4699         //qDebug() << "try" << item;
4700         if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName))
4701             items.append(static_cast<T*>(item));
4702         items += findItems<T>(item, objectName);
4703     }
4704
4705     return items;
4706 }
4707
4708 void tst_QQuickListView::dumpTree(QQuickItem *parent, int depth)
4709 {
4710     static QString padding("                       ");
4711     for (int i = 0; i < parent->childItems().count(); ++i) {
4712         QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
4713         if (!item)
4714             continue;
4715         qDebug() << padding.left(depth*2) << item;
4716         dumpTree(item, depth+1);
4717     }
4718 }
4719
4720 QTEST_MAIN(tst_QQuickListView)
4721
4722 #include "tst_qquicklistview.moc"
4723