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