1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
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"
60 Q_DECLARE_METATYPE(Qt::LayoutDirection)
61 Q_DECLARE_METATYPE(QQuickListView::Orientation)
63 class tst_QQuickListView : public QDeclarativeDataTest
70 // Test both QListModelInterface and QAbstractItemModel model types
71 void qListModelInterface_items();
72 void qListModelInterface_package_items();
73 void qAbstractItemModel_items();
75 void qListModelInterface_changed();
76 void qListModelInterface_package_changed();
77 void qAbstractItemModel_changed();
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();
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();
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();
102 void multipleChanges();
103 void multipleChanges_data();
105 void qListModelInterface_clear();
106 void qListModelInterface_package_clear();
107 void qAbstractItemModel_clear();
109 void insertBeforeVisible();
110 void insertBeforeVisible_data();
111 void swapWithFirstItem();
113 void currentIndex_delayedItemCreation();
114 void currentIndex_delayedItemCreation_data();
116 void noCurrentIndex();
118 void enforceRange_withoutHighlight();
120 void qListModelInterface_sections();
121 void qListModelInterface_package_sections();
122 void qAbstractItemModel_sections();
123 void sectionsPositioning();
124 void sectionsDelegate();
126 void positionViewAtIndex();
128 void propertyChanges();
129 void componentChanges();
131 void manualHighlight();
134 void header_delayItemCreation();
139 void resizeViewAndRepaint();
140 void sizeLessThan1();
142 void resizeDelegate();
143 void resizeFirstDelegate();
145 void indexAt_itemAt_data();
146 void indexAt_itemAt();
147 void incrementalModel();
151 void onRemove_data();
153 void test_mirroring();
155 void creationContext();
156 void snapToItem_data();
158 void snapOneItem_data();
166 void unrequestedVisibility();
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);
182 T *findItem(QQuickItem *parent, const QString &id, int index=-1);
184 QList<T*> findItems(QQuickItem *parent, const QString &objectName, bool visibleOnly = true);
185 void dumpTree(QQuickItem *parent, int depth = 0);
187 void inserted_more_data();
188 void removed_more_data();
192 class TestObject : public QObject
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)
202 TestObject(QObject *parent = 0)
203 : QObject(parent), mError(true), mAnimate(false), mInvalidHighlight(false)
206 bool error() const { return mError; }
207 void setError(bool err) { mError = err; emit changedError(); }
209 bool animate() const { return mAnimate; }
210 void setAnimate(bool anim) { mAnimate = anim; emit changedAnim(); }
212 bool invalidHighlight() const { return mInvalidHighlight; }
213 void setInvalidHighlight(bool invalid) { mInvalidHighlight = invalid; emit changedHl(); }
215 int cacheBuffer() const { return mCacheBuffer; }
216 void setCacheBuffer(int buffer) { mCacheBuffer = buffer; emit changedCacheBuffer(); }
222 void changedCacheBuffer();
227 bool mInvalidHighlight;
232 void tst_qquicklistview_move(int from, int to, int n, T *items)
235 // Only move forwards - flip if backwards moving
243 items->move(from, to);
247 typename T::ConstIterator it=items->begin(); it += from+n;
248 for (; i<to-from; ++i,++it)
249 replaced.append(*it);
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)
261 class TestModel : public QListModelInterface
265 TestModel(QObject *parent = 0) : QListModelInterface(parent) {}
268 enum Roles { Name, Number };
270 QString name(int index) const { return list.at(index).first; }
271 QString number(int index) const { return list.at(index).second; }
273 int count() const { return list.count(); }
275 QList<int> roles() const { return QList<int>() << Name << Number; }
276 QString toString(int role) const {
287 QVariant data(int index, int role) const
290 return list.at(index).first;
292 return list.at(index).second;
295 QHash<int, QVariant> data(int index, const QList<int> &roles) const {
296 QHash<int,QVariant> returnHash;
298 for (int i = 0; i < roles.size(); ++i) {
299 int role = roles.at(i);
303 info = list.at(index).first;
306 info = list.at(index).second;
311 returnHash.insert(role, info);
316 void addItem(const QString &name, const QString &number) {
317 list.append(QPair<QString,QString>(name, number));
318 emit itemsInserted(list.count()-1, 1);
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);
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());
332 void removeItem(int index) {
333 list.removeAt(index);
334 emit itemsRemoved(index, 1);
337 void removeItems(int index, int count) {
340 list.removeAt(index);
341 emit itemsRemoved(index, count);
344 void moveItem(int from, int to) {
346 emit itemsMoved(from, to, 1);
349 void moveItems(int from, int to, int count) {
350 tst_qquicklistview_move(from, to, count, &list);
351 emit itemsMoved(from, to, count);
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());
360 int count = list.count();
362 emit itemsRemoved(0, count);
366 QList<QPair<QString,QString> > list;
370 class TestModel2 : public QAbstractListModel
373 enum Roles { Name = Qt::UserRole+1, Number = Qt::UserRole+2 };
375 TestModel2(QObject *parent=0) : QAbstractListModel(parent) {
376 QHash<int, QByteArray> roles;
377 roles[Name] = "name";
378 roles[Number] = "number";
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 {
386 rv = list.at(index.row()).first;
387 else if (role == Number)
388 rv = list.at(index.row()).second;
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; }
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();
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();
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();
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();
423 void removeItem(int index) {
424 emit beginRemoveRows(QModelIndex(), index, index);
425 list.removeAt(index);
426 emit endRemoveRows();
429 void removeItems(int index, int count) {
430 emit beginRemoveRows(QModelIndex(), index, index+count-1);
432 list.removeAt(index);
433 emit endRemoveRows();
436 void moveItem(int from, int to) {
437 emit beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
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);
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));
454 int count = list.count();
455 emit beginRemoveRows(QModelIndex(), 0, count-1);
457 emit endRemoveRows();
461 QList<QPair<QString,QString> > list;
464 tst_QQuickListView::tst_QQuickListView()
469 void tst_QQuickListView::items(const QUrl &source, bool forceLayout)
471 QQuickView *canvas = createView();
474 model.addItem("Fred", "12345");
475 model.addItem("John", "2345");
476 model.addItem("Bob", "54321");
478 QDeclarativeContext *ctxt = canvas->rootContext();
479 ctxt->setContextProperty("testModel", &model);
481 TestObject *testObject = new TestObject;
482 ctxt->setContextProperty("testObject", testObject);
484 canvas->setSource(source);
485 qApp->processEvents();
487 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
488 QTRY_VERIFY(listview != 0);
490 QQuickItem *contentItem = listview->contentItem();
491 QTRY_VERIFY(contentItem != 0);
493 QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
494 QTRY_VERIFY(testObject->error() == false);
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
501 // current item should be first item
502 QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
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));
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());
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);
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);
533 // set an empty model and confirm that items are destroyed
535 ctxt->setContextProperty("testModel", &model2);
537 // Force a layout, necessary if ListView is completed before VisualDataModel.
539 QCOMPARE(listview->property("count").toInt(), 0);
541 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
542 QTRY_VERIFY(itemCount == 0);
544 QTRY_COMPARE(listview->highlightResizeSpeed(), 1000.0);
545 QTRY_COMPARE(listview->highlightMoveSpeed(), 1000.0);
553 void tst_QQuickListView::changed(const QUrl &source, bool forceLayout)
555 QQuickView *canvas = createView();
558 model.addItem("Fred", "12345");
559 model.addItem("John", "2345");
560 model.addItem("Bob", "54321");
562 QDeclarativeContext *ctxt = canvas->rootContext();
563 ctxt->setContextProperty("testModel", &model);
565 TestObject *testObject = new TestObject;
566 ctxt->setContextProperty("testObject", testObject);
568 canvas->setSource(source);
569 qApp->processEvents();
571 QQuickFlickable *listview = findItem<QQuickFlickable>(canvas->rootObject(), "list");
572 QTRY_VERIFY(listview != 0);
574 QQuickItem *contentItem = listview->contentItem();
575 QTRY_VERIFY(contentItem != 0);
577 // Force a layout, necessary if ListView is completed before VisualDataModel.
579 QCOMPARE(listview->property("count").toInt(), model.count());
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));
594 void tst_QQuickListView::inserted(const QUrl &source)
596 QQuickView *canvas = createView();
600 model.addItem("Fred", "12345");
601 model.addItem("John", "2345");
602 model.addItem("Bob", "54321");
604 QDeclarativeContext *ctxt = canvas->rootContext();
605 ctxt->setContextProperty("testModel", &model);
607 TestObject *testObject = new TestObject;
608 ctxt->setContextProperty("testObject", testObject);
610 canvas->setSource(source);
611 //canvas->setSource(testFileUrl("listviewtest.qml")));
612 qApp->processEvents();
614 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
615 QTRY_VERIFY(listview != 0);
617 QQuickItem *contentItem = listview->contentItem();
618 QTRY_VERIFY(contentItem != 0);
620 model.insertItem(1, "Will", "9876");
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
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));
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);
638 model.insertItem(0, "Foo", "1111"); // zero index, and current item
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
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));
650 QTRY_COMPARE(listview->currentIndex(), 1);
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);
658 for (int i = model.count(); i < 30; ++i)
659 model.insertItem(i, "Hello", QString::number(i));
661 listview->setContentY(80);
663 // Insert item outside visible area
664 model.insertItem(1, "Hello", "1324");
666 QTRY_VERIFY(listview->contentY() == 80);
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";
673 QTRY_COMPARE(item->y(), i*20.0 - 20.0);
676 // QTRY_COMPARE(listview->contentItemHeight(), model.count() * 20.0);
680 model.insertItem(0, "Hello", "1234");
681 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
683 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
685 QCOMPARE(item->y(), 0.);
686 QTRY_VERIFY(listview->contentY() == 0);
693 void tst_QQuickListView::inserted_more()
695 QFETCH(qreal, contentY);
696 QFETCH(int, insertIndex);
697 QFETCH(int, insertCount);
698 QFETCH(qreal, itemsOffsetAfterMove);
702 QQuickView *canvas = createView();
706 for (int i = 0; i < 30; i++)
707 model.addItem("Item" + QString::number(i), "");
709 QDeclarativeContext *ctxt = canvas->rootContext();
710 ctxt->setContextProperty("testModel", &model);
712 TestObject *testObject = new TestObject;
713 ctxt->setContextProperty("testObject", testObject);
715 canvas->setSource(testFileUrl("listviewtest.qml"));
716 qApp->processEvents();
718 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
719 QTRY_VERIFY(listview != 0);
720 QQuickItem *contentItem = listview->contentItem();
721 QTRY_VERIFY(contentItem != 0);
723 listview->setContentY(contentY);
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());
731 // check visibleItems.first() is in correct position
732 QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper", 0);
734 QCOMPARE(item0->y(), itemsOffsetAfterMove);
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();
745 QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
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);
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));
765 void tst_QQuickListView::inserted_more_data()
767 QTest::addColumn<qreal>("contentY");
768 QTest::addColumn<int>("insertIndex");
769 QTest::addColumn<int>("insertCount");
770 QTest::addColumn<qreal>("itemsOffsetAfterMove");
772 QTest::newRow("add 1, before visible items")
775 << -20.0; // insert above first visible i.e. 0 is at -20, first visible should not move
777 QTest::newRow("add multiple, before visible")
780 << -20.0 * 3; // again first visible should not move
782 QTest::newRow("add 1, at start of visible, content at start")
787 QTest::newRow("add multiple, start of visible, content at start")
792 QTest::newRow("add 1, at start of visible, content not at start")
797 QTest::newRow("add multiple, at start of visible, content not at start")
803 QTest::newRow("add 1, at end of visible, content at start")
808 QTest::newRow("add 1, at end of visible, content at start")
813 QTest::newRow("add 1, at end of visible, content not at start")
818 QTest::newRow("add multiple, at end of visible, content not at start")
824 QTest::newRow("add 1, after visible, content at start")
829 QTest::newRow("add 1, after visible, content at start")
834 QTest::newRow("add 1, after visible, content not at start")
839 QTest::newRow("add multiple, after visible, content not at start")
845 void tst_QQuickListView::insertBeforeVisible()
847 QFETCH(int, insertIndex);
848 QFETCH(int, insertCount);
849 QFETCH(int, cacheBuffer);
852 QQuickView *canvas = createView();
856 for (int i = 0; i < 30; i++)
857 model.addItem("Item" + QString::number(i), "");
859 QDeclarativeContext *ctxt = canvas->rootContext();
860 ctxt->setContextProperty("testModel", &model);
862 TestObject *testObject = new TestObject;
863 ctxt->setContextProperty("testObject", testObject);
865 canvas->setSource(testFileUrl("listviewtest.qml"));
866 qApp->processEvents();
868 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
869 QTRY_VERIFY(listview != 0);
870 QQuickItem *contentItem = listview->contentItem();
871 QTRY_VERIFY(contentItem != 0);
873 listview->setCacheBuffer(cacheBuffer);
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);
880 qApp->processEvents();
881 QTRY_COMPARE(listview->currentIndex(), firstVisibleIndex);
882 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", firstVisibleIndex);
884 QCOMPARE(item->y(), listview->contentY());
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());
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);
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);
906 QTRY_COMPARE(name->text(), model.name(i));
913 void tst_QQuickListView::insertBeforeVisible_data()
915 QTest::addColumn<int>("insertIndex");
916 QTest::addColumn<int>("insertCount");
917 QTest::addColumn<int>("cacheBuffer");
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;
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;
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;
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;
937 void tst_QQuickListView::removed(const QUrl &source, bool /* animated */)
939 QQuickView *canvas = createView();
942 for (int i = 0; i < 50; i++)
943 model.addItem("Item" + QString::number(i), "");
945 QDeclarativeContext *ctxt = canvas->rootContext();
946 ctxt->setContextProperty("testModel", &model);
948 TestObject *testObject = new TestObject;
949 ctxt->setContextProperty("testObject", testObject);
951 canvas->setSource(source);
953 qApp->processEvents();
955 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
956 QTRY_VERIFY(listview != 0);
958 QQuickItem *contentItem = listview->contentItem();
959 QTRY_VERIFY(contentItem != 0);
962 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
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));
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";
977 QTRY_VERIFY(item->y() == i*20);
980 // Remove first item (which is the current item);
982 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
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));
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";
997 QTRY_COMPARE(item->y(),i*20.0);
1000 // Remove items not visible
1001 model.removeItem(18);
1002 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
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";
1010 QTRY_COMPARE(item->y(),i*20.0);
1013 // Remove items before visible
1014 listview->setContentY(80);
1015 listview->setCurrentIndex(10);
1017 model.removeItem(1); // post: top item will be at 20
1018 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
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";
1025 QTRY_COMPARE(item->y(),20+i*20.0);
1028 // Remove current index
1029 QTRY_VERIFY(listview->currentIndex() == 9);
1030 QQuickItem *oldCurrent = listview->currentItem();
1031 model.removeItem(9);
1033 QTRY_COMPARE(listview->currentIndex(), 9);
1034 QTRY_VERIFY(listview->currentItem() != oldCurrent);
1036 listview->setContentY(20); // That's the top now
1037 // let transitions settle.
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";
1046 QTRY_COMPARE(item->y(),20+i*20.0);
1049 // remove current item beyond visible items.
1050 listview->setCurrentIndex(20);
1051 listview->setContentY(40);
1052 model.removeItem(20);
1054 QTRY_COMPARE(listview->currentIndex(), 20);
1055 QTRY_VERIFY(listview->currentItem() != 0);
1057 // remove item before current, but visible
1058 listview->setCurrentIndex(8);
1059 oldCurrent = listview->currentItem();
1060 model.removeItem(6);
1062 QTRY_COMPARE(listview->currentIndex(), 7);
1063 QTRY_VERIFY(listview->currentItem() == oldCurrent);
1065 listview->setContentY(80);
1068 // remove all visible items
1069 model.removeItems(1, 18);
1070 QTRY_COMPARE(listview->count() , model.count());
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";
1078 QTRY_COMPARE(item->y(),80+i*20.0);
1081 model.removeItems(1, 17);
1082 QTRY_COMPARE(listview->count() , model.count());
1084 model.removeItems(2, 1);
1085 QTRY_COMPARE(listview->count() , model.count());
1087 model.addItem("New", "1");
1088 QTRY_COMPARE(listview->count() , model.count());
1090 QTRY_VERIFY(name = findItem<QQuickText>(contentItem, "textName", model.count()-1));
1091 QCOMPARE(name->text(), QString("New"));
1093 // Add some more items so that we don't run out
1095 for (int i = 0; i < 50; i++)
1096 model.addItem("Item" + QString::number(i), "");
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));
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);
1115 void tst_QQuickListView::removed_more(const QUrl &source)
1117 QFETCH(qreal, contentY);
1118 QFETCH(int, removeIndex);
1119 QFETCH(int, removeCount);
1120 QFETCH(qreal, itemsOffsetAfterMove);
1124 QQuickView *canvas = createView();
1128 for (int i = 0; i < 30; i++)
1129 model.addItem("Item" + QString::number(i), "");
1131 QDeclarativeContext *ctxt = canvas->rootContext();
1132 ctxt->setContextProperty("testModel", &model);
1134 TestObject *testObject = new TestObject;
1135 ctxt->setContextProperty("testObject", testObject);
1137 canvas->setSource(source);
1138 qApp->processEvents();
1140 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1141 QTRY_VERIFY(listview != 0);
1142 QQuickItem *contentItem = listview->contentItem();
1143 QTRY_VERIFY(contentItem != 0);
1145 listview->setContentY(contentY);
1146 QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
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));
1153 model.removeItems(removeIndex, removeCount);
1154 QTRY_COMPARE(listview->property("count").toInt(), model.count());
1156 // check visibleItems.first() is in correct position
1157 QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper", 0);
1159 QCOMPARE(item0->y(), itemsOffsetAfterMove);
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();
1169 QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
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);
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));
1189 void tst_QQuickListView::removed_more_data()
1191 QTest::addColumn<qreal>("contentY");
1192 QTest::addColumn<int>("removeIndex");
1193 QTest::addColumn<int>("removeCount");
1194 QTest::addColumn<qreal>("itemsOffsetAfterMove");
1196 QTest::newRow("remove 1, before visible items")
1197 << 80.0 // show 4-19
1199 << 20.0; // visible items slide down by 1 item so that first visible does not move
1201 QTest::newRow("remove multiple, all before visible items")
1206 QTest::newRow("remove multiple, all before visible items, remove item 0")
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")
1216 << 20.0 * 3; // adjust for the 3 items removed before the visible
1218 QTest::newRow("remove multiple, mix of items from before and within visible items, remove item 0")
1221 << 20.0 * 4; // adjust for the 3 items removed before the visible
1224 QTest::newRow("remove 1, from start of visible, content at start")
1229 QTest::newRow("remove multiple, from start of visible, content at start")
1234 QTest::newRow("remove 1, from start of visible, content not at start")
1235 << 80.0 // show 4-19
1239 QTest::newRow("remove multiple, from start of visible, content not at start")
1240 << 80.0 // show 4-19
1245 QTest::newRow("remove 1, from middle of visible, content at start")
1250 QTest::newRow("remove multiple, from middle of visible, content at start")
1255 QTest::newRow("remove 1, from middle of visible, content not at start")
1256 << 80.0 // show 4-19
1260 QTest::newRow("remove multiple, from middle of visible, content not at start")
1261 << 80.0 // show 4-19
1266 QTest::newRow("remove 1, after visible, content at start")
1271 QTest::newRow("remove multiple, after visible, content at start")
1276 QTest::newRow("remove 1, after visible, content not at middle")
1277 << 80.0 // show 4-19
1281 QTest::newRow("remove multiple, after visible, content not at start")
1282 << 80.0 // show 4-19
1286 QTest::newRow("remove multiple, mix of items from within and after visible items")
1293 void tst_QQuickListView::clear(const QUrl &source)
1295 QQuickView *canvas = createView();
1298 for (int i = 0; i < 30; i++)
1299 model.addItem("Item" + QString::number(i), "");
1301 QDeclarativeContext *ctxt = canvas->rootContext();
1302 ctxt->setContextProperty("testModel", &model);
1304 TestObject *testObject = new TestObject;
1305 ctxt->setContextProperty("testObject", testObject);
1307 canvas->setSource(source);
1308 qApp->processEvents();
1310 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1311 QTRY_VERIFY(listview != 0);
1313 QQuickItem *contentItem = listview->contentItem();
1314 QTRY_VERIFY(contentItem != 0);
1318 QTRY_VERIFY(listview->count() == 0);
1319 QTRY_VERIFY(listview->currentItem() == 0);
1320 QTRY_VERIFY(listview->contentY() == 0);
1321 QVERIFY(listview->currentIndex() == -1);
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);
1334 void tst_QQuickListView::moved(const QUrl &source)
1336 QFETCH(qreal, contentY);
1340 QFETCH(qreal, itemsOffsetAfterMove);
1344 QQuickView *canvas = createView();
1348 for (int i = 0; i < 30; i++)
1349 model.addItem("Item" + QString::number(i), "");
1351 QDeclarativeContext *ctxt = canvas->rootContext();
1352 ctxt->setContextProperty("testModel", &model);
1354 TestObject *testObject = new TestObject;
1355 ctxt->setContextProperty("testObject", testObject);
1357 canvas->setSource(source);
1358 qApp->processEvents();
1360 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1361 QTRY_VERIFY(listview != 0);
1362 QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
1364 QQuickItem *contentItem = listview->contentItem();
1365 QTRY_VERIFY(contentItem != 0);
1367 QQuickItem *currentItem = listview->currentItem();
1368 QTRY_VERIFY(currentItem != 0);
1370 listview->setContentY(contentY);
1371 QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
1373 model.moveItems(from, to, count);
1374 QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
1376 QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
1377 int firstVisibleIndex = -1;
1378 for (int i=0; i<items.count(); i++) {
1379 if (items[i]->y() >= contentY) {
1380 QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
1381 firstVisibleIndex = e.evaluate().toInt();
1385 QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
1387 // Confirm items positioned correctly and indexes correct
1388 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1389 for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
1390 if (i >= firstVisibleIndex + 16) // index has moved out of view
1392 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1393 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1394 QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove);
1395 name = findItem<QQuickText>(contentItem, "textName", i);
1397 QTRY_COMPARE(name->text(), model.name(i));
1398 number = findItem<QQuickText>(contentItem, "textNumber", i);
1399 QVERIFY(number != 0);
1400 QTRY_COMPARE(number->text(), model.number(i));
1402 // current index should have been updated
1403 if (item == currentItem)
1404 QTRY_COMPARE(listview->currentIndex(), i);
1411 void tst_QQuickListView::moved_data()
1413 QTest::addColumn<qreal>("contentY");
1414 QTest::addColumn<int>("from");
1415 QTest::addColumn<int>("to");
1416 QTest::addColumn<int>("count");
1417 QTest::addColumn<qreal>("itemsOffsetAfterMove");
1419 // model starts with 30 items, each 20px high, in area 320px high
1420 // 16 items should be visible at a time
1421 // itemsOffsetAfterMove should be > 0 whenever items above the visible pos have moved
1423 QTest::newRow("move 1 forwards, within visible items")
1428 QTest::newRow("move 1 forwards, from non-visible -> visible")
1429 << 80.0 // show 4-19
1431 << 20.0; // removed 1 item above the first visible, so item 0 should drop down by 1 to minimize movement
1433 QTest::newRow("move 1 forwards, from non-visible -> visible (move first item)")
1434 << 80.0 // show 4-19
1436 << 20.0; // first item has moved to below item4, everything drops down by size of 1 item
1438 QTest::newRow("move 1 forwards, from visible -> non-visible")
1443 QTest::newRow("move 1 forwards, from visible -> non-visible (move first item)")
1449 QTest::newRow("move 1 backwards, within visible items")
1454 QTest::newRow("move 1 backwards, within visible items (to first index)")
1459 QTest::newRow("move 1 backwards, from non-visible -> visible")
1464 QTest::newRow("move 1 backwards, from non-visible -> visible (move last item)")
1469 QTest::newRow("move 1 backwards, from visible -> non-visible")
1470 << 80.0 // show 4-19
1472 << -20.0; // to minimize movement, item 0 moves to -20, and other items do not move
1474 QTest::newRow("move 1 backwards, from visible -> non-visible (move first item)")
1475 << 80.0 // show 4-19
1477 << -20.0; // to minimize movement, item 16 (now at 0) moves to -20, and other items do not move
1480 QTest::newRow("move multiple forwards, within visible items")
1485 QTest::newRow("move multiple forwards, before visible items")
1486 << 140.0 // show 7-22
1487 << 4 << 5 << 3 // 4,5,6 move to below 7
1488 << 20.0 * 3; // 4,5,6 moved down
1490 QTest::newRow("move multiple forwards, from non-visible -> visible")
1491 << 80.0 // show 4-19
1493 << 20.0 * 3; // moving 3 from above the content y should adjust y positions accordingly
1495 QTest::newRow("move multiple forwards, from non-visible -> visible (move first item)")
1496 << 80.0 // show 4-19
1498 << 20.0 * 3; // moving 3 from above the content y should adjust y positions accordingly
1500 QTest::newRow("move multiple forwards, mix of non-visible/visible")
1503 << 20.0; // item 1,2 are removed, item 3 is now first visible
1505 QTest::newRow("move multiple forwards, to bottom of view")
1510 QTest::newRow("move multiple forwards, to bottom of view, first->last")
1515 QTest::newRow("move multiple forwards, to bottom of view, content y not 0")
1520 QTest::newRow("move multiple forwards, from visible -> non-visible")
1525 QTest::newRow("move multiple forwards, from visible -> non-visible (move first item)")
1531 QTest::newRow("move multiple backwards, within visible items")
1536 QTest::newRow("move multiple backwards, within visible items (move first item)")
1541 QTest::newRow("move multiple backwards, from non-visible -> visible")
1546 QTest::newRow("move multiple backwards, from non-visible -> visible (move last item)")
1551 QTest::newRow("move multiple backwards, from visible -> non-visible")
1552 << 80.0 // show 4-19
1554 << -20.0 * 3; // to minimize movement, 0 moves by -60, and other items do not move
1556 QTest::newRow("move multiple backwards, from visible -> non-visible (move first item)")
1557 << 80.0 // show 4-19
1559 << -20.0 * 3; // to minimize movement, 16,17,18 move to above item 0, and other items do not move
1564 enum { Inserted, Removed, Moved, SetCurrent } type;
1569 static ListChange insert(int index, int count = 1) { ListChange c = { Inserted, index, count, -1 }; return c; }
1570 static ListChange remove(int index, int count = 1) { ListChange c = { Removed, index, count, -1 }; return c; }
1571 static ListChange move(int index, int to, int count) { ListChange c = { Moved, index, count, to }; return c; }
1572 static ListChange setCurrent(int index) { ListChange c = { SetCurrent, index, -1, -1 }; return c; }
1574 Q_DECLARE_METATYPE(QList<ListChange>)
1576 void tst_QQuickListView::multipleChanges()
1578 QFETCH(int, startCount);
1579 QFETCH(QList<ListChange>, changes);
1580 QFETCH(int, newCount);
1581 QFETCH(int, newCurrentIndex);
1583 QQuickView *canvas = createView();
1587 for (int i = 0; i < startCount; i++)
1588 model.addItem("Item" + QString::number(i), "");
1590 QDeclarativeContext *ctxt = canvas->rootContext();
1591 ctxt->setContextProperty("testModel", &model);
1593 TestObject *testObject = new TestObject;
1594 ctxt->setContextProperty("testObject", testObject);
1596 canvas->setSource(testFileUrl("listviewtest.qml"));
1597 qApp->processEvents();
1599 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1600 QTRY_VERIFY(listview != 0);
1602 for (int i=0; i<changes.count(); i++) {
1603 switch (changes[i].type) {
1604 case ListChange::Inserted:
1606 QList<QPair<QString, QString> > items;
1607 for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
1608 items << qMakePair(QString("new item " + j), QString::number(j));
1609 model.insertItems(changes[i].index, items);
1612 case ListChange::Removed:
1613 model.removeItems(changes[i].index, changes[i].count);
1615 case ListChange::Moved:
1616 model.moveItems(changes[i].index, changes[i].to, changes[i].count);
1618 case ListChange::SetCurrent:
1619 listview->setCurrentIndex(changes[i].index);
1624 QTRY_COMPARE(listview->count(), newCount);
1625 QCOMPARE(listview->count(), model.count());
1626 QTRY_COMPARE(listview->currentIndex(), newCurrentIndex);
1630 QQuickItem *contentItem = listview->contentItem();
1631 QTRY_VERIFY(contentItem != 0);
1632 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1633 for (int i=0; i < model.count() && i < itemCount; ++i) {
1634 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1635 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1636 name = findItem<QQuickText>(contentItem, "textName", i);
1638 QTRY_COMPARE(name->text(), model.name(i));
1639 number = findItem<QQuickText>(contentItem, "textNumber", i);
1640 QVERIFY(number != 0);
1641 QTRY_COMPARE(number->text(), model.number(i));
1648 void tst_QQuickListView::multipleChanges_data()
1650 QTest::addColumn<int>("startCount");
1651 QTest::addColumn<QList<ListChange> >("changes");
1652 QTest::addColumn<int>("newCount");
1653 QTest::addColumn<int>("newCurrentIndex");
1655 QList<ListChange> changes;
1657 for (int i=1; i<30; i++)
1658 changes << ListChange::remove(0);
1659 QTest::newRow("remove all but 1, first->last") << 30 << changes << 1 << 0;
1661 changes << ListChange::remove(0);
1662 QTest::newRow("remove all") << 30 << changes << 0 << -1;
1665 changes << ListChange::setCurrent(29);
1666 for (int i=29; i>0; i--)
1667 changes << ListChange::remove(i);
1668 QTest::newRow("remove last (current) -> first") << 30 << changes << 1 << 0;
1670 QTest::newRow("remove then insert at 0") << 10 << (QList<ListChange>()
1671 << ListChange::remove(0, 1)
1672 << ListChange::insert(0, 1)
1675 QTest::newRow("remove then insert at non-zero index") << 10 << (QList<ListChange>()
1676 << ListChange::setCurrent(2)
1677 << ListChange::remove(2, 1)
1678 << ListChange::insert(2, 1)
1681 QTest::newRow("remove current then insert below it") << 10 << (QList<ListChange>()
1682 << ListChange::setCurrent(1)
1683 << ListChange::remove(1, 3)
1684 << ListChange::insert(2, 2)
1687 QTest::newRow("remove current index then move it down") << 10 << (QList<ListChange>()
1688 << ListChange::setCurrent(2)
1689 << ListChange::remove(1, 3)
1690 << ListChange::move(1, 5, 1)
1693 QTest::newRow("remove current index then move it up") << 10 << (QList<ListChange>()
1694 << ListChange::setCurrent(5)
1695 << ListChange::remove(4, 3)
1696 << ListChange::move(4, 1, 1)
1700 QTest::newRow("insert multiple times") << 0 << (QList<ListChange>()
1701 << ListChange::insert(0, 2)
1702 << ListChange::insert(0, 4)
1703 << ListChange::insert(0, 6)
1706 QTest::newRow("insert multiple times with current index changes") << 0 << (QList<ListChange>()
1707 << ListChange::insert(0, 2)
1708 << ListChange::insert(0, 4)
1709 << ListChange::insert(0, 6)
1710 << ListChange::setCurrent(3)
1711 << ListChange::insert(3, 2)
1714 QTest::newRow("insert and remove all") << 0 << (QList<ListChange>()
1715 << ListChange::insert(0, 30)
1716 << ListChange::remove(0, 30)
1719 QTest::newRow("insert and remove current") << 30 << (QList<ListChange>()
1720 << ListChange::insert(1)
1721 << ListChange::setCurrent(1)
1722 << ListChange::remove(1)
1725 QTest::newRow("insert before 0, then remove cross section of new and old items") << 10 << (QList<ListChange>()
1726 << ListChange::insert(0, 10)
1727 << ListChange::remove(5, 10)
1730 QTest::newRow("insert multiple, then move new items to end") << 10 << (QList<ListChange>()
1731 << ListChange::insert(0, 3)
1732 << ListChange::move(0, 10, 3)
1735 QTest::newRow("insert multiple, then move new and some old items to end") << 10 << (QList<ListChange>()
1736 << ListChange::insert(0, 3)
1737 << ListChange::move(0, 8, 5)
1740 QTest::newRow("insert multiple at end, then move new and some old items to start") << 10 << (QList<ListChange>()
1741 << ListChange::setCurrent(9)
1742 << ListChange::insert(10, 3)
1743 << ListChange::move(8, 0, 5)
1747 QTest::newRow("move back and forth to same index") << 10 << (QList<ListChange>()
1748 << ListChange::setCurrent(1)
1749 << ListChange::move(1, 2, 2)
1750 << ListChange::move(2, 1, 2)
1753 QTest::newRow("move forwards then back") << 10 << (QList<ListChange>()
1754 << ListChange::setCurrent(2)
1755 << ListChange::move(1, 2, 3)
1756 << ListChange::move(3, 0, 5)
1759 QTest::newRow("move current, then remove it") << 10 << (QList<ListChange>()
1760 << ListChange::setCurrent(5)
1761 << ListChange::move(5, 0, 1)
1762 << ListChange::remove(0)
1765 QTest::newRow("move current, then insert before it") << 10 << (QList<ListChange>()
1766 << ListChange::setCurrent(5)
1767 << ListChange::move(5, 0, 1)
1768 << ListChange::insert(0)
1771 QTest::newRow("move multiple, then remove them") << 10 << (QList<ListChange>()
1772 << ListChange::setCurrent(1)
1773 << ListChange::move(5, 1, 3)
1774 << ListChange::remove(1, 3)
1777 QTest::newRow("move multiple, then insert before them") << 10 << (QList<ListChange>()
1778 << ListChange::setCurrent(5)
1779 << ListChange::move(5, 1, 3)
1780 << ListChange::insert(1, 5)
1783 QTest::newRow("move multiple, then insert after them") << 10 << (QList<ListChange>()
1784 << ListChange::setCurrent(3)
1785 << ListChange::move(0, 1, 2)
1786 << ListChange::insert(3, 5)
1790 QTest::newRow("clear current") << 0 << (QList<ListChange>()
1791 << ListChange::insert(0, 5)
1792 << ListChange::setCurrent(-1)
1793 << ListChange::remove(0, 5)
1794 << ListChange::insert(0, 5)
1798 void tst_QQuickListView::swapWithFirstItem()
1800 QQuickView *canvas = createView();
1804 for (int i = 0; i < 30; i++)
1805 model.addItem("Item" + QString::number(i), "");
1807 QDeclarativeContext *ctxt = canvas->rootContext();
1808 ctxt->setContextProperty("testModel", &model);
1810 TestObject *testObject = new TestObject;
1811 ctxt->setContextProperty("testObject", testObject);
1813 canvas->setSource(testFileUrl("listviewtest.qml"));
1814 qApp->processEvents();
1816 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1817 QTRY_VERIFY(listview != 0);
1819 // ensure content position is stable
1820 listview->setContentY(0);
1821 model.moveItem(1, 0);
1822 QTRY_VERIFY(listview->contentY() == 0);
1828 void tst_QQuickListView::enforceRange()
1830 QQuickView *canvas = createView();
1833 for (int i = 0; i < 30; i++)
1834 model.addItem("Item" + QString::number(i), "");
1836 QDeclarativeContext *ctxt = canvas->rootContext();
1837 ctxt->setContextProperty("testModel", &model);
1839 canvas->setSource(testFileUrl("listview-enforcerange.qml"));
1840 qApp->processEvents();
1842 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1843 QTRY_VERIFY(listview != 0);
1845 QTRY_COMPARE(listview->preferredHighlightBegin(), 100.0);
1846 QTRY_COMPARE(listview->preferredHighlightEnd(), 100.0);
1847 QTRY_COMPARE(listview->highlightRangeMode(), QQuickListView::StrictlyEnforceRange);
1849 QQuickItem *contentItem = listview->contentItem();
1850 QTRY_VERIFY(contentItem != 0);
1852 // view should be positioned at the top of the range.
1853 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
1855 QTRY_COMPARE(listview->contentY(), -100.0);
1857 QQuickText *name = findItem<QQuickText>(contentItem, "textName", 0);
1858 QTRY_VERIFY(name != 0);
1859 QTRY_COMPARE(name->text(), model.name(0));
1860 QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 0);
1861 QTRY_VERIFY(number != 0);
1862 QTRY_COMPARE(number->text(), model.number(0));
1864 // Check currentIndex is updated when contentItem moves
1865 listview->setContentY(20);
1867 QTRY_COMPARE(listview->currentIndex(), 6);
1871 for (int i = 0; i < 5; i++)
1872 model2.addItem("Item" + QString::number(i), "");
1874 ctxt->setContextProperty("testModel", &model2);
1875 QCOMPARE(listview->count(), 5);
1880 void tst_QQuickListView::enforceRange_withoutHighlight()
1883 // If no highlight is set but StrictlyEnforceRange is used, the content should still move
1884 // to the correct position (i.e. to the next/previous item, not next/previous section)
1885 // when moving up/down via incrementCurrentIndex() and decrementCurrentIndex()
1887 QQuickView *canvas = createView();
1892 model.addItem("Item 0", "a");
1893 model.addItem("Item 1", "b");
1894 model.addItem("Item 2", "b");
1895 model.addItem("Item 3", "c");
1897 QDeclarativeContext *ctxt = canvas->rootContext();
1898 ctxt->setContextProperty("testModel", &model);
1900 canvas->setSource(testFileUrl("listview-enforcerange-nohighlight.qml"));
1901 qApp->processEvents();
1903 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1904 QTRY_VERIFY(listview != 0);
1906 qreal expectedPos = -100.0;
1908 expectedPos += 10.0; // scroll past 1st section's delegate (10px height)
1909 QTRY_COMPARE(listview->contentY(), expectedPos);
1911 expectedPos += 20 + 10; // scroll past 1st section and section delegate of 2nd section
1912 QTest::keyClick(canvas, Qt::Key_Down);
1914 QTRY_COMPARE(listview->contentY(), expectedPos);
1916 expectedPos += 20; // scroll past 1st item of 2nd section
1917 QTest::keyClick(canvas, Qt::Key_Down);
1918 QTRY_COMPARE(listview->contentY(), expectedPos);
1920 expectedPos += 20 + 10; // scroll past 2nd item of 2nd section and section delegate of 3rd section
1921 QTest::keyClick(canvas, Qt::Key_Down);
1922 QTRY_COMPARE(listview->contentY(), expectedPos);
1927 void tst_QQuickListView::spacing()
1929 QQuickView *canvas = createView();
1933 for (int i = 0; i < 30; i++)
1934 model.addItem("Item" + QString::number(i), "");
1936 QDeclarativeContext *ctxt = canvas->rootContext();
1937 ctxt->setContextProperty("testModel", &model);
1939 TestObject *testObject = new TestObject;
1940 ctxt->setContextProperty("testObject", testObject);
1942 canvas->setSource(testFileUrl("listviewtest.qml"));
1943 qApp->processEvents();
1945 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1946 QTRY_VERIFY(listview != 0);
1948 QQuickItem *contentItem = listview->contentItem();
1949 QTRY_VERIFY(contentItem != 0);
1951 // Confirm items positioned correctly
1952 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1953 for (int i = 0; i < model.count() && i < itemCount; ++i) {
1954 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1955 if (!item) qWarning() << "Item" << i << "not found";
1957 QTRY_VERIFY(item->y() == i*20);
1960 listview->setSpacing(10);
1961 QTRY_VERIFY(listview->spacing() == 10);
1963 // Confirm items positioned correctly
1964 QTRY_VERIFY(findItems<QQuickItem>(contentItem, "wrapper").count() == 11);
1965 for (int i = 0; i < 11; ++i) {
1966 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1967 if (!item) qWarning() << "Item" << i << "not found";
1969 QTRY_VERIFY(item->y() == i*30);
1972 listview->setSpacing(0);
1974 // Confirm items positioned correctly
1975 QTRY_VERIFY(findItems<QQuickItem>(contentItem, "wrapper").count() >= 16);
1976 for (int i = 0; i < 16; ++i) {
1977 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1978 if (!item) qWarning() << "Item" << i << "not found";
1980 QTRY_COMPARE(item->y(), i*20.0);
1987 template <typename T>
1988 void tst_QQuickListView::sections(const QUrl &source)
1990 QQuickView *canvas = createView();
1994 for (int i = 0; i < 30; i++)
1995 model.addItem("Item" + QString::number(i), QString::number(i/5));
1997 QDeclarativeContext *ctxt = canvas->rootContext();
1998 ctxt->setContextProperty("testModel", &model);
2000 canvas->setSource(source);
2001 qApp->processEvents();
2003 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2004 QTRY_VERIFY(listview != 0);
2006 QQuickItem *contentItem = listview->contentItem();
2007 QTRY_VERIFY(contentItem != 0);
2009 // Confirm items positioned correctly
2010 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2011 for (int i = 0; i < model.count() && i < itemCount; ++i) {
2012 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2014 QTRY_COMPARE(item->y(), qreal(i*20 + ((i+4)/5) * 20));
2015 QQuickText *next = findItem<QQuickText>(item, "nextSection");
2016 QCOMPARE(next->text().toInt(), (i+1)/5);
2019 QSignalSpy currentSectionChangedSpy(listview, SIGNAL(currentSectionChanged()));
2021 // Remove section boundary
2022 model.removeItem(5);
2023 QTRY_COMPARE(listview->count(), model.count());
2025 // New section header created
2026 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 5);
2028 QTRY_COMPARE(item->height(), 40.0);
2030 model.insertItem(3, "New Item", "0");
2031 QTRY_COMPARE(listview->count(), model.count());
2033 // Section header moved
2034 item = findItem<QQuickItem>(contentItem, "wrapper", 5);
2036 QTRY_COMPARE(item->height(), 20.0);
2038 item = findItem<QQuickItem>(contentItem, "wrapper", 6);
2040 QTRY_COMPARE(item->height(), 40.0);
2042 // insert item which will become a section header
2043 model.insertItem(6, "Replace header", "1");
2044 QTRY_COMPARE(listview->count(), model.count());
2046 item = findItem<QQuickItem>(contentItem, "wrapper", 6);
2048 QTRY_COMPARE(item->height(), 40.0);
2050 item = findItem<QQuickItem>(contentItem, "wrapper", 7);
2052 QTRY_COMPARE(item->height(), 20.0);
2054 QTRY_COMPARE(listview->currentSection(), QString("0"));
2056 listview->setContentY(140);
2057 QTRY_COMPARE(listview->currentSection(), QString("1"));
2059 QTRY_COMPARE(currentSectionChangedSpy.count(), 1);
2061 listview->setContentY(20);
2062 QTRY_COMPARE(listview->currentSection(), QString("0"));
2064 QTRY_COMPARE(currentSectionChangedSpy.count(), 2);
2066 item = findItem<QQuickItem>(contentItem, "wrapper", 1);
2068 QTRY_COMPARE(item->height(), 20.0);
2070 // check that headers change when item changes
2071 listview->setContentY(0);
2072 model.modifyItem(0, "changed", "2");
2075 item = findItem<QQuickItem>(contentItem, "wrapper", 1);
2077 QTRY_COMPARE(item->height(), 40.0);
2082 void tst_QQuickListView::sectionsDelegate()
2084 QQuickView *canvas = createView();
2088 for (int i = 0; i < 30; i++)
2089 model.addItem("Item" + QString::number(i), QString::number(i/5));
2091 QDeclarativeContext *ctxt = canvas->rootContext();
2092 ctxt->setContextProperty("testModel", &model);
2094 canvas->setSource(testFileUrl("listview-sections_delegate.qml"));
2095 qApp->processEvents();
2097 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2098 QTRY_VERIFY(listview != 0);
2100 QQuickItem *contentItem = listview->contentItem();
2101 QTRY_VERIFY(contentItem != 0);
2103 // Confirm items positioned correctly
2104 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2105 for (int i = 0; i < model.count() && i < itemCount; ++i) {
2106 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2108 QTRY_COMPARE(item->y(), qreal(i*20 + ((i+5)/5) * 20));
2109 QQuickText *next = findItem<QQuickText>(item, "nextSection");
2110 QCOMPARE(next->text().toInt(), (i+1)/5);
2113 for (int i = 0; i < 3; ++i) {
2114 QQuickItem *item = findItem<QQuickItem>(contentItem, "sect_" + QString::number(i));
2116 QTRY_COMPARE(item->y(), qreal(i*20*6));
2119 model.modifyItem(0, "One", "aaa");
2120 model.modifyItem(1, "Two", "aaa");
2121 model.modifyItem(2, "Three", "aaa");
2122 model.modifyItem(3, "Four", "aaa");
2123 model.modifyItem(4, "Five", "aaa");
2126 for (int i = 0; i < 3; ++i) {
2127 QQuickItem *item = findItem<QQuickItem>(contentItem,
2128 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
2130 QTRY_COMPARE(item->y(), qreal(i*20*6));
2133 // remove section boundary
2134 model.removeItem(5);
2135 QTRY_COMPARE(listview->count(), model.count());
2136 for (int i = 0; i < 3; ++i) {
2137 QQuickItem *item = findItem<QQuickItem>(contentItem,
2138 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
2143 QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "sect_1");
2144 QCOMPARE(items.count(), 1);
2147 model.modifyItem(0, "One", "aaa");
2148 model.modifyItem(1, "One", "aaa");
2149 model.modifyItem(2, "One", "aaa");
2150 model.modifyItem(3, "Four", "aaa");
2151 model.modifyItem(4, "Four", "aaa");
2152 model.modifyItem(5, "Four", "aaa");
2153 model.modifyItem(6, "Five", "aaa");
2154 model.modifyItem(7, "Five", "aaa");
2155 model.modifyItem(8, "Five", "aaa");
2156 model.modifyItem(9, "Two", "aaa");
2157 model.modifyItem(10, "Two", "aaa");
2158 model.modifyItem(11, "Two", "aaa");
2159 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "sect_aaa").count(), 1);
2160 canvas->rootObject()->setProperty("sectionProperty", "name");
2161 // ensure view has settled.
2162 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "sect_Four").count(), 1);
2163 for (int i = 0; i < 4; ++i) {
2164 QQuickItem *item = findItem<QQuickItem>(contentItem,
2165 "sect_" + model.name(i*3));
2167 QTRY_COMPARE(item->y(), qreal(i*20*4));
2171 model.removeItems(10, 20);
2172 // ensure view has settled.
2173 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper").count(), 10);
2174 // Drag view up beyond bounds
2175 QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(20,20));
2177 QMouseEvent mv(QEvent::MouseMove, QPoint(20,0), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2178 QGuiApplication::sendEvent(canvas, &mv);
2181 QMouseEvent mv(QEvent::MouseMove, QPoint(20,-50), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2182 QGuiApplication::sendEvent(canvas, &mv);
2185 QMouseEvent mv(QEvent::MouseMove, QPoint(20,-200), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2186 QGuiApplication::sendEvent(canvas, &mv);
2188 QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(20,-200));
2189 // view should settle back at 0
2190 QTRY_COMPARE(listview->contentY(), 0.0);
2195 void tst_QQuickListView::sectionsPositioning()
2197 QQuickView *canvas = createView();
2201 for (int i = 0; i < 30; i++)
2202 model.addItem("Item" + QString::number(i), QString::number(i/5));
2204 QDeclarativeContext *ctxt = canvas->rootContext();
2205 ctxt->setContextProperty("testModel", &model);
2207 canvas->setSource(testFileUrl("listview-sections_delegate.qml"));
2208 qApp->processEvents();
2209 canvas->rootObject()->setProperty("sectionPositioning", QVariant(int(QQuickViewSection::InlineLabels | QQuickViewSection::CurrentLabelAtStart | QQuickViewSection::NextLabelAtEnd)));
2211 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2212 QTRY_VERIFY(listview != 0);
2214 QQuickItem *contentItem = listview->contentItem();
2215 QTRY_VERIFY(contentItem != 0);
2217 for (int i = 0; i < 3; ++i) {
2218 QQuickItem *item = findItem<QQuickItem>(contentItem, "sect_" + QString::number(i));
2220 QTRY_COMPARE(item->y(), qreal(i*20*6));
2223 QQuickItem *topItem = findVisibleChild(contentItem, "sect_0"); // section header
2225 QCOMPARE(topItem->y(), 0.);
2227 QQuickItem *bottomItem = findVisibleChild(contentItem, "sect_3"); // section footer
2228 QVERIFY(bottomItem);
2229 QCOMPARE(bottomItem->y(), 300.);
2231 // move down a little and check that section header is at top
2232 listview->setContentY(10);
2233 QCOMPARE(topItem->y(), 0.);
2235 // push the top header up
2236 listview->setContentY(110);
2237 topItem = findVisibleChild(contentItem, "sect_0"); // section header
2239 QCOMPARE(topItem->y(), 100.);
2241 QQuickItem *item = findVisibleChild(contentItem, "sect_1");
2243 QCOMPARE(item->y(), 120.);
2245 bottomItem = findVisibleChild(contentItem, "sect_4"); // section footer
2246 QVERIFY(bottomItem);
2247 QCOMPARE(bottomItem->y(), 410.);
2249 // Move past section 0
2250 listview->setContentY(120);
2251 topItem = findVisibleChild(contentItem, "sect_0"); // section header
2254 // Push section footer down
2255 listview->setContentY(70);
2256 bottomItem = findVisibleChild(contentItem, "sect_4"); // section footer
2257 QVERIFY(bottomItem);
2258 QCOMPARE(bottomItem->y(), 380.);
2260 // Change current section
2261 listview->setContentY(10);
2262 model.modifyItem(0, "One", "aaa");
2263 model.modifyItem(1, "Two", "aaa");
2264 model.modifyItem(2, "Three", "aaa");
2265 model.modifyItem(3, "Four", "aaa");
2266 model.modifyItem(4, "Five", "aaa");
2267 QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
2269 QTRY_COMPARE(listview->currentSection(), QString("aaa"));
2271 for (int i = 0; i < 3; ++i) {
2272 QQuickItem *item = findItem<QQuickItem>(contentItem,
2273 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
2275 QTRY_COMPARE(item->y(), qreal(i*20*6));
2278 QTRY_VERIFY(topItem = findVisibleChild(contentItem, "sect_aaa")); // section header
2279 QCOMPARE(topItem->y(), 10.);
2281 // remove section boundary
2282 listview->setContentY(120);
2283 model.removeItem(5);
2284 QTRY_COMPARE(listview->count(), model.count());
2285 for (int i = 0; i < 3; ++i) {
2286 QQuickItem *item = findItem<QQuickItem>(contentItem,
2287 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
2289 QTRY_COMPARE(item->y(), qreal(i*20*6));
2292 QVERIFY(topItem = findVisibleChild(contentItem, "sect_1"));
2293 QTRY_COMPARE(topItem->y(), 120.);
2295 // Change the next section
2296 listview->setContentY(0);
2297 bottomItem = findVisibleChild(contentItem, "sect_3"); // section footer
2298 QVERIFY(bottomItem);
2299 QTRY_COMPARE(bottomItem->y(), 300.);
2301 model.modifyItem(14, "New", "new");
2303 QTRY_VERIFY(bottomItem = findVisibleChild(contentItem, "sect_new")); // section footer
2304 QTRY_COMPARE(bottomItem->y(), 300.);
2306 // Turn sticky footer off
2307 listview->setContentY(40);
2308 canvas->rootObject()->setProperty("sectionPositioning", QVariant(int(QQuickViewSection::InlineLabels | QQuickViewSection::CurrentLabelAtStart)));
2309 item = findVisibleChild(contentItem, "sect_new"); // inline label restored
2311 QCOMPARE(item->y(), 360.);
2313 // Turn sticky header off
2314 listview->setContentY(30);
2315 canvas->rootObject()->setProperty("sectionPositioning", QVariant(int(QQuickViewSection::InlineLabels)));
2316 item = findVisibleChild(contentItem, "sect_aaa"); // inline label restored
2318 QCOMPARE(item->y(), 0.);
2323 void tst_QQuickListView::currentIndex_delayedItemCreation()
2325 QFETCH(bool, setCurrentToZero);
2327 QQuickView *canvas = createView();
2331 // test currentIndexChanged() is emitted even if currentIndex = 0 on start up
2332 // (since the currentItem will have changed and that shares the same index)
2333 canvas->rootContext()->setContextProperty("setCurrentToZero", setCurrentToZero);
2335 canvas->setSource(testFileUrl("fillModelOnComponentCompleted.qml"));
2336 qApp->processEvents();
2338 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2339 QTRY_VERIFY(listview != 0);
2341 QQuickItem *contentItem = listview->contentItem();
2342 QTRY_VERIFY(contentItem != 0);
2344 QSignalSpy spy(listview, SIGNAL(currentItemChanged()));
2345 QCOMPARE(listview->currentIndex(), 0);
2346 QTRY_COMPARE(spy.count(), 1);
2351 void tst_QQuickListView::currentIndex_delayedItemCreation_data()
2353 QTest::addColumn<bool>("setCurrentToZero");
2355 QTest::newRow("set to 0") << true;
2356 QTest::newRow("don't set to 0") << false;
2359 void tst_QQuickListView::currentIndex()
2362 for (int i = 0; i < 30; i++)
2363 model.addItem("Item" + QString::number(i), QString::number(i));
2365 QQuickView *canvas = new QQuickView(0);
2366 canvas->setGeometry(0,0,240,320);
2368 QDeclarativeContext *ctxt = canvas->rootContext();
2369 ctxt->setContextProperty("testModel", &model);
2370 ctxt->setContextProperty("testWrap", QVariant(false));
2372 QString filename(testFile("listview-initCurrent.qml"));
2373 canvas->setSource(QUrl::fromLocalFile(filename));
2375 qApp->processEvents();
2377 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2378 QTRY_VERIFY(listview != 0);
2380 QQuickItem *contentItem = listview->contentItem();
2381 QTRY_VERIFY(contentItem != 0);
2383 // current item should be 20th item at startup
2384 // and current item should be in view
2385 QCOMPARE(listview->currentIndex(), 20);
2386 QCOMPARE(listview->contentY(), 100.0);
2387 QCOMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 20));
2388 QCOMPARE(listview->highlightItem()->y(), listview->currentItem()->y());
2391 listview->setCurrentIndex(0);
2392 QCOMPARE(listview->currentIndex(), 0);
2393 // confirm that the velocity is updated
2394 QTRY_VERIFY(listview->verticalVelocity() != 0.0);
2396 listview->incrementCurrentIndex();
2397 QCOMPARE(listview->currentIndex(), 1);
2398 listview->decrementCurrentIndex();
2399 QCOMPARE(listview->currentIndex(), 0);
2401 listview->decrementCurrentIndex();
2402 QCOMPARE(listview->currentIndex(), 0);
2405 ctxt->setContextProperty("testWrap", QVariant(true));
2406 QVERIFY(listview->isWrapEnabled());
2408 listview->decrementCurrentIndex();
2409 QCOMPARE(listview->currentIndex(), model.count()-1);
2411 QTRY_COMPARE(listview->contentY(), 280.0);
2413 listview->incrementCurrentIndex();
2414 QCOMPARE(listview->currentIndex(), 0);
2416 QTRY_COMPARE(listview->contentY(), 0.0);
2419 // footer should become visible if it is out of view, and then current index is set to count-1
2420 canvas->rootObject()->setProperty("showFooter", true);
2421 QTRY_VERIFY(listview->footerItem());
2422 listview->setCurrentIndex(model.count()-2);
2423 QTRY_VERIFY(listview->footerItem()->y() > listview->contentY() + listview->height());
2424 listview->setCurrentIndex(model.count()-1);
2425 QTRY_COMPARE(listview->contentY() + listview->height(), (20.0 * model.count()) + listview->footerItem()->height());
2426 canvas->rootObject()->setProperty("showFooter", false);
2428 // header should become visible if it is out of view, and then current index is set to 0
2429 canvas->rootObject()->setProperty("showHeader", true);
2430 QTRY_VERIFY(listview->headerItem());
2431 listview->setCurrentIndex(1);
2432 QTRY_VERIFY(listview->headerItem()->y() + listview->headerItem()->height() < listview->contentY());
2433 listview->setCurrentIndex(0);
2434 QTRY_COMPARE(listview->contentY(), -listview->headerItem()->height());
2435 canvas->rootObject()->setProperty("showHeader", false);
2440 canvas->requestActivateWindow();
2441 QTest::qWaitForWindowShown(canvas);
2442 QTRY_VERIFY(qGuiApp->focusWindow() == canvas);
2444 listview->setCurrentIndex(0);
2446 QTest::keyClick(canvas, Qt::Key_Down);
2447 QCOMPARE(listview->currentIndex(), 1);
2449 QTest::keyClick(canvas, Qt::Key_Up);
2450 QCOMPARE(listview->currentIndex(), 0);
2452 // hold down Key_Down
2453 for (int i=0; i<model.count()-1; i++) {
2454 QTest::simulateEvent(canvas, true, Qt::Key_Down, Qt::NoModifier, "", true);
2455 QTRY_COMPARE(listview->currentIndex(), i+1);
2457 QTest::keyRelease(canvas, Qt::Key_Down);
2458 QTRY_COMPARE(listview->currentIndex(), model.count()-1);
2459 QTRY_COMPARE(listview->contentY(), 280.0);
2462 for (int i=model.count()-1; i > 0; i--) {
2463 QTest::simulateEvent(canvas, true, Qt::Key_Up, Qt::NoModifier, "", true);
2464 QTRY_COMPARE(listview->currentIndex(), i-1);
2466 QTest::keyRelease(canvas, Qt::Key_Up);
2467 QTRY_COMPARE(listview->currentIndex(), 0);
2468 QTRY_COMPARE(listview->contentY(), 0.0);
2471 // turn off auto highlight
2472 listview->setHighlightFollowsCurrentItem(false);
2473 QVERIFY(listview->highlightFollowsCurrentItem() == false);
2475 QVERIFY(listview->highlightItem());
2476 qreal hlPos = listview->highlightItem()->y();
2478 listview->setCurrentIndex(4);
2479 QTRY_COMPARE(listview->highlightItem()->y(), hlPos);
2481 // insert item before currentIndex
2482 listview->setCurrentIndex(28);
2483 model.insertItem(0, "Foo", "1111");
2484 QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29);
2486 // check removing highlight by setting currentIndex to -1;
2487 listview->setCurrentIndex(-1);
2489 QCOMPARE(listview->currentIndex(), -1);
2490 QVERIFY(!listview->highlightItem());
2491 QVERIFY(!listview->currentItem());
2496 void tst_QQuickListView::noCurrentIndex()
2499 for (int i = 0; i < 30; i++)
2500 model.addItem("Item" + QString::number(i), QString::number(i));
2502 QQuickView *canvas = new QQuickView(0);
2503 canvas->setGeometry(0,0,240,320);
2505 QDeclarativeContext *ctxt = canvas->rootContext();
2506 ctxt->setContextProperty("testModel", &model);
2508 QString filename(testFile("listview-noCurrent.qml"));
2509 canvas->setSource(QUrl::fromLocalFile(filename));
2511 qApp->processEvents();
2513 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2514 QTRY_VERIFY(listview != 0);
2516 QQuickItem *contentItem = listview->contentItem();
2517 QTRY_VERIFY(contentItem != 0);
2519 // current index should be -1 at startup
2520 // and we should not have a currentItem or highlightItem
2521 QCOMPARE(listview->currentIndex(), -1);
2522 QCOMPARE(listview->contentY(), 0.0);
2523 QVERIFY(!listview->highlightItem());
2524 QVERIFY(!listview->currentItem());
2526 listview->setCurrentIndex(2);
2527 QCOMPARE(listview->currentIndex(), 2);
2528 QVERIFY(listview->highlightItem());
2529 QVERIFY(listview->currentItem());
2534 void tst_QQuickListView::itemList()
2536 QQuickView *canvas = createView();
2538 canvas->setSource(testFileUrl("itemlist.qml"));
2539 qApp->processEvents();
2541 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "view");
2542 QTRY_VERIFY(listview != 0);
2544 QQuickItem *contentItem = listview->contentItem();
2545 QTRY_VERIFY(contentItem != 0);
2547 QQuickVisualItemModel *model = canvas->rootObject()->findChild<QQuickVisualItemModel*>("itemModel");
2548 QTRY_VERIFY(model != 0);
2550 QTRY_VERIFY(model->count() == 3);
2551 QTRY_COMPARE(listview->currentIndex(), 0);
2553 QQuickItem *item = findItem<QQuickItem>(contentItem, "item1");
2555 QTRY_COMPARE(item->x(), 0.0);
2556 QCOMPARE(item->height(), listview->height());
2558 QQuickText *text = findItem<QQuickText>(contentItem, "text1");
2560 QTRY_COMPARE(text->text(), QLatin1String("index: 0"));
2562 listview->setCurrentIndex(2);
2564 item = findItem<QQuickItem>(contentItem, "item3");
2566 QTRY_COMPARE(item->x(), 480.0);
2568 text = findItem<QQuickText>(contentItem, "text3");
2570 QTRY_COMPARE(text->text(), QLatin1String("index: 2"));
2575 void tst_QQuickListView::cacheBuffer()
2577 QQuickView *canvas = createView();
2580 for (int i = 0; i < 90; i++)
2581 model.addItem("Item" + QString::number(i), "");
2583 QDeclarativeContext *ctxt = canvas->rootContext();
2584 ctxt->setContextProperty("testModel", &model);
2586 TestObject *testObject = new TestObject;
2587 ctxt->setContextProperty("testObject", testObject);
2589 canvas->setSource(testFileUrl("listviewtest.qml"));
2591 qApp->processEvents();
2593 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2594 QTRY_VERIFY(listview != 0);
2596 QQuickItem *contentItem = listview->contentItem();
2597 QTRY_VERIFY(contentItem != 0);
2598 QTRY_VERIFY(listview->delegate() != 0);
2599 QTRY_VERIFY(listview->model() != 0);
2600 QTRY_VERIFY(listview->highlight() != 0);
2602 // Confirm items positioned correctly
2603 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2604 for (int i = 0; i < model.count() && i < itemCount; ++i) {
2605 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2606 if (!item) qWarning() << "Item" << i << "not found";
2608 QTRY_VERIFY(item->y() == i*20);
2611 QDeclarativeIncubationController controller;
2612 canvas->engine()->setIncubationController(&controller);
2614 testObject->setCacheBuffer(200);
2615 QTRY_VERIFY(listview->cacheBuffer() == 200);
2617 // items will be created one at a time
2618 for (int i = itemCount; i < qMin(itemCount+10,model.count()); ++i) {
2619 QVERIFY(findItem<QQuickItem>(listview, "wrapper", i) == 0);
2620 QQuickItem *item = 0;
2623 controller.incubateWhile(&b);
2624 item = findItem<QQuickItem>(listview, "wrapper", i);
2630 controller.incubateWhile(&b);
2633 int newItemCount = 0;
2634 newItemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2636 // Confirm items positioned correctly
2637 for (int i = 0; i < model.count() && i < newItemCount; ++i) {
2638 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2639 if (!item) qWarning() << "Item" << i << "not found";
2641 QTRY_VERIFY(item->y() == i*20);
2644 // move view and confirm items in view are visible immediately and outside are created async
2645 listview->setContentY(300);
2647 for (int i = 15; i < 32; ++i) {
2648 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2649 if (!item) qWarning() << "Item" << i << "not found";
2651 QVERIFY(item->y() == i*20);
2654 QVERIFY(findItem<QQuickItem>(listview, "wrapper", 32) == 0);
2656 // ensure buffered items are created
2657 for (int i = 32; i < qMin(41,model.count()); ++i) {
2658 QQuickItem *item = 0;
2660 qGuiApp->processEvents(); // allow refill to happen
2662 controller.incubateWhile(&b);
2663 item = findItem<QQuickItem>(listview, "wrapper", i);
2669 controller.incubateWhile(&b);
2676 void tst_QQuickListView::positionViewAtIndex()
2678 QQuickView *canvas = createView();
2681 for (int i = 0; i < 40; i++)
2682 model.addItem("Item" + QString::number(i), "");
2684 QDeclarativeContext *ctxt = canvas->rootContext();
2685 ctxt->setContextProperty("testModel", &model);
2687 TestObject *testObject = new TestObject;
2688 ctxt->setContextProperty("testObject", testObject);
2690 canvas->setSource(testFileUrl("listviewtest.qml"));
2691 qApp->processEvents();
2693 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2694 QTRY_VERIFY(listview != 0);
2696 QQuickItem *contentItem = listview->contentItem();
2697 QTRY_VERIFY(contentItem != 0);
2699 // Confirm items positioned correctly
2700 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2701 for (int i = 0; i < model.count() && i < itemCount; ++i) {
2702 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2703 if (!item) qWarning() << "Item" << i << "not found";
2705 QTRY_COMPARE(item->y(), i*20.);
2708 // Position on a currently visible item
2709 listview->positionViewAtIndex(3, QQuickListView::Beginning);
2710 QTRY_COMPARE(listview->contentY(), 60.);
2712 // Confirm items positioned correctly
2713 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2714 for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
2715 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2716 if (!item) qWarning() << "Item" << i << "not found";
2718 QTRY_COMPARE(item->y(), i*20.);
2721 // Position on an item beyond the visible items
2722 listview->positionViewAtIndex(22, QQuickListView::Beginning);
2723 QTRY_COMPARE(listview->contentY(), 440.);
2725 // Confirm items positioned correctly
2726 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2727 for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
2728 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2729 if (!item) qWarning() << "Item" << i << "not found";
2731 QTRY_COMPARE(item->y(), i*20.);
2734 // Position on an item that would leave empty space if positioned at the top
2735 listview->positionViewAtIndex(28, QQuickListView::Beginning);
2736 QTRY_COMPARE(listview->contentY(), 480.);
2738 // Confirm items positioned correctly
2739 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2740 for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
2741 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2742 if (!item) qWarning() << "Item" << i << "not found";
2744 QTRY_COMPARE(item->y(), i*20.);
2747 // Position at the beginning again
2748 listview->positionViewAtIndex(0, QQuickListView::Beginning);
2749 QTRY_COMPARE(listview->contentY(), 0.);
2751 // Confirm items positioned correctly
2752 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2753 for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2754 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2755 if (!item) qWarning() << "Item" << i << "not found";
2757 QTRY_COMPARE(item->y(), i*20.);
2760 // Position at End using last index
2761 listview->positionViewAtIndex(model.count()-1, QQuickListView::End);
2762 QTRY_COMPARE(listview->contentY(), 480.);
2764 // Confirm items positioned correctly
2765 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2766 for (int i = 24; i < model.count(); ++i) {
2767 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2768 if (!item) qWarning() << "Item" << i << "not found";
2770 QTRY_COMPARE(item->y(), i*20.);
2774 listview->positionViewAtIndex(20, QQuickListView::End);
2775 QTRY_COMPARE(listview->contentY(), 100.);
2777 // Position in Center
2778 listview->positionViewAtIndex(15, QQuickListView::Center);
2779 QTRY_COMPARE(listview->contentY(), 150.);
2781 // Ensure at least partially visible
2782 listview->positionViewAtIndex(15, QQuickListView::Visible);
2783 QTRY_COMPARE(listview->contentY(), 150.);
2785 listview->setContentY(302);
2786 listview->positionViewAtIndex(15, QQuickListView::Visible);
2787 QTRY_COMPARE(listview->contentY(), 302.);
2789 listview->setContentY(320);
2790 listview->positionViewAtIndex(15, QQuickListView::Visible);
2791 QTRY_COMPARE(listview->contentY(), 300.);
2793 listview->setContentY(85);
2794 listview->positionViewAtIndex(20, QQuickListView::Visible);
2795 QTRY_COMPARE(listview->contentY(), 85.);
2797 listview->setContentY(75);
2798 listview->positionViewAtIndex(20, QQuickListView::Visible);
2799 QTRY_COMPARE(listview->contentY(), 100.);
2801 // Ensure completely visible
2802 listview->setContentY(120);
2803 listview->positionViewAtIndex(20, QQuickListView::Contain);
2804 QTRY_COMPARE(listview->contentY(), 120.);
2806 listview->setContentY(302);
2807 listview->positionViewAtIndex(15, QQuickListView::Contain);
2808 QTRY_COMPARE(listview->contentY(), 300.);
2810 listview->setContentY(85);
2811 listview->positionViewAtIndex(20, QQuickListView::Contain);
2812 QTRY_COMPARE(listview->contentY(), 100.);
2814 // positionAtBeginnging
2815 listview->positionViewAtBeginning();
2816 QTRY_COMPARE(listview->contentY(), 0.);
2818 listview->setContentY(80);
2819 canvas->rootObject()->setProperty("showHeader", true);
2820 listview->positionViewAtBeginning();
2821 QTRY_COMPARE(listview->contentY(), -30.);
2824 listview->positionViewAtEnd();
2825 QTRY_COMPARE(listview->contentY(), 480.); // 40*20 - 320
2827 listview->setContentY(80);
2828 canvas->rootObject()->setProperty("showFooter", true);
2829 listview->positionViewAtEnd();
2830 QTRY_COMPARE(listview->contentY(), 510.);
2832 // set current item to outside visible view, position at beginning
2833 // and ensure highlight moves to current item
2834 listview->setCurrentIndex(1);
2835 listview->positionViewAtBeginning();
2836 QTRY_COMPARE(listview->contentY(), -30.);
2837 QVERIFY(listview->highlightItem());
2838 QCOMPARE(listview->highlightItem()->y(), 20.);
2844 void tst_QQuickListView::resetModel()
2846 QQuickView *canvas = createView();
2848 QStringList strings;
2849 strings << "one" << "two" << "three";
2850 QStringListModel model(strings);
2852 QDeclarativeContext *ctxt = canvas->rootContext();
2853 ctxt->setContextProperty("testModel", &model);
2855 canvas->setSource(testFileUrl("displaylist.qml"));
2856 qApp->processEvents();
2858 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2859 QTRY_VERIFY(listview != 0);
2861 QQuickItem *contentItem = listview->contentItem();
2862 QTRY_VERIFY(contentItem != 0);
2864 QTRY_COMPARE(listview->count(), model.rowCount());
2866 for (int i = 0; i < model.rowCount(); ++i) {
2867 QQuickText *display = findItem<QQuickText>(contentItem, "displayText", i);
2868 QTRY_VERIFY(display != 0);
2869 QTRY_COMPARE(display->text(), strings.at(i));
2873 strings << "four" << "five" << "six" << "seven";
2874 model.setStringList(strings);
2876 QTRY_COMPARE(listview->count(), model.rowCount());
2878 for (int i = 0; i < model.rowCount(); ++i) {
2879 QQuickText *display = findItem<QQuickText>(contentItem, "displayText", i);
2880 QTRY_VERIFY(display != 0);
2881 QTRY_COMPARE(display->text(), strings.at(i));
2887 void tst_QQuickListView::propertyChanges()
2889 QQuickView *canvas = createView();
2890 QTRY_VERIFY(canvas);
2891 canvas->setSource(testFileUrl("propertychangestest.qml"));
2893 QQuickListView *listView = canvas->rootObject()->findChild<QQuickListView*>("listView");
2894 QTRY_VERIFY(listView);
2896 QSignalSpy highlightFollowsCurrentItemSpy(listView, SIGNAL(highlightFollowsCurrentItemChanged()));
2897 QSignalSpy preferredHighlightBeginSpy(listView, SIGNAL(preferredHighlightBeginChanged()));
2898 QSignalSpy preferredHighlightEndSpy(listView, SIGNAL(preferredHighlightEndChanged()));
2899 QSignalSpy highlightRangeModeSpy(listView, SIGNAL(highlightRangeModeChanged()));
2900 QSignalSpy keyNavigationWrapsSpy(listView, SIGNAL(keyNavigationWrapsChanged()));
2901 QSignalSpy cacheBufferSpy(listView, SIGNAL(cacheBufferChanged()));
2902 QSignalSpy snapModeSpy(listView, SIGNAL(snapModeChanged()));
2904 QTRY_COMPARE(listView->highlightFollowsCurrentItem(), true);
2905 QTRY_COMPARE(listView->preferredHighlightBegin(), 0.0);
2906 QTRY_COMPARE(listView->preferredHighlightEnd(), 0.0);
2907 QTRY_COMPARE(listView->highlightRangeMode(), QQuickListView::ApplyRange);
2908 QTRY_COMPARE(listView->isWrapEnabled(), true);
2909 QTRY_COMPARE(listView->cacheBuffer(), 10);
2910 QTRY_COMPARE(listView->snapMode(), QQuickListView::SnapToItem);
2912 listView->setHighlightFollowsCurrentItem(false);
2913 listView->setPreferredHighlightBegin(1.0);
2914 listView->setPreferredHighlightEnd(1.0);
2915 listView->setHighlightRangeMode(QQuickListView::StrictlyEnforceRange);
2916 listView->setWrapEnabled(false);
2917 listView->setCacheBuffer(3);
2918 listView->setSnapMode(QQuickListView::SnapOneItem);
2920 QTRY_COMPARE(listView->highlightFollowsCurrentItem(), false);
2921 QTRY_COMPARE(listView->preferredHighlightBegin(), 1.0);
2922 QTRY_COMPARE(listView->preferredHighlightEnd(), 1.0);
2923 QTRY_COMPARE(listView->highlightRangeMode(), QQuickListView::StrictlyEnforceRange);
2924 QTRY_COMPARE(listView->isWrapEnabled(), false);
2925 QTRY_COMPARE(listView->cacheBuffer(), 3);
2926 QTRY_COMPARE(listView->snapMode(), QQuickListView::SnapOneItem);
2928 QTRY_COMPARE(highlightFollowsCurrentItemSpy.count(),1);
2929 QTRY_COMPARE(preferredHighlightBeginSpy.count(),1);
2930 QTRY_COMPARE(preferredHighlightEndSpy.count(),1);
2931 QTRY_COMPARE(highlightRangeModeSpy.count(),1);
2932 QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
2933 QTRY_COMPARE(cacheBufferSpy.count(),1);
2934 QTRY_COMPARE(snapModeSpy.count(),1);
2936 listView->setHighlightFollowsCurrentItem(false);
2937 listView->setPreferredHighlightBegin(1.0);
2938 listView->setPreferredHighlightEnd(1.0);
2939 listView->setHighlightRangeMode(QQuickListView::StrictlyEnforceRange);
2940 listView->setWrapEnabled(false);
2941 listView->setCacheBuffer(3);
2942 listView->setSnapMode(QQuickListView::SnapOneItem);
2944 QTRY_COMPARE(highlightFollowsCurrentItemSpy.count(),1);
2945 QTRY_COMPARE(preferredHighlightBeginSpy.count(),1);
2946 QTRY_COMPARE(preferredHighlightEndSpy.count(),1);
2947 QTRY_COMPARE(highlightRangeModeSpy.count(),1);
2948 QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
2949 QTRY_COMPARE(cacheBufferSpy.count(),1);
2950 QTRY_COMPARE(snapModeSpy.count(),1);
2955 void tst_QQuickListView::componentChanges()
2957 QQuickView *canvas = createView();
2958 QTRY_VERIFY(canvas);
2959 canvas->setSource(testFileUrl("propertychangestest.qml"));
2961 QQuickListView *listView = canvas->rootObject()->findChild<QQuickListView*>("listView");
2962 QTRY_VERIFY(listView);
2964 QDeclarativeComponent component(canvas->engine());
2965 component.setData("import QtQuick 2.0; Rectangle { color: \"blue\"; }", QUrl::fromLocalFile(""));
2967 QDeclarativeComponent delegateComponent(canvas->engine());
2968 delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
2970 QSignalSpy highlightSpy(listView, SIGNAL(highlightChanged()));
2971 QSignalSpy delegateSpy(listView, SIGNAL(delegateChanged()));
2972 QSignalSpy headerSpy(listView, SIGNAL(headerChanged()));
2973 QSignalSpy footerSpy(listView, SIGNAL(footerChanged()));
2975 listView->setHighlight(&component);
2976 listView->setHeader(&component);
2977 listView->setFooter(&component);
2978 listView->setDelegate(&delegateComponent);
2980 QTRY_COMPARE(listView->highlight(), &component);
2981 QTRY_COMPARE(listView->header(), &component);
2982 QTRY_COMPARE(listView->footer(), &component);
2983 QTRY_COMPARE(listView->delegate(), &delegateComponent);
2985 QTRY_COMPARE(highlightSpy.count(),1);
2986 QTRY_COMPARE(delegateSpy.count(),1);
2987 QTRY_COMPARE(headerSpy.count(),1);
2988 QTRY_COMPARE(footerSpy.count(),1);
2990 listView->setHighlight(&component);
2991 listView->setHeader(&component);
2992 listView->setFooter(&component);
2993 listView->setDelegate(&delegateComponent);
2995 QTRY_COMPARE(highlightSpy.count(),1);
2996 QTRY_COMPARE(delegateSpy.count(),1);
2997 QTRY_COMPARE(headerSpy.count(),1);
2998 QTRY_COMPARE(footerSpy.count(),1);
3003 void tst_QQuickListView::modelChanges()
3005 QQuickView *canvas = createView();
3006 QTRY_VERIFY(canvas);
3007 canvas->setSource(testFileUrl("propertychangestest.qml"));
3009 QQuickListView *listView = canvas->rootObject()->findChild<QQuickListView*>("listView");
3010 QTRY_VERIFY(listView);
3012 QDeclarativeListModel *alternateModel = canvas->rootObject()->findChild<QDeclarativeListModel*>("alternateModel");
3013 QTRY_VERIFY(alternateModel);
3014 QVariant modelVariant = QVariant::fromValue<QObject *>(alternateModel);
3015 QSignalSpy modelSpy(listView, SIGNAL(modelChanged()));
3017 listView->setModel(modelVariant);
3018 QTRY_COMPARE(listView->model(), modelVariant);
3019 QTRY_COMPARE(modelSpy.count(),1);
3021 listView->setModel(modelVariant);
3022 QTRY_COMPARE(modelSpy.count(),1);
3024 listView->setModel(QVariant());
3025 QTRY_COMPARE(modelSpy.count(),2);
3030 void tst_QQuickListView::QTBUG_9791()
3032 QQuickView *canvas = createView();
3034 canvas->setSource(testFileUrl("strictlyenforcerange.qml"));
3035 qApp->processEvents();
3037 QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3038 QTRY_VERIFY(listview != 0);
3040 QQuickItem *contentItem = listview->contentItem();
3041 QTRY_VERIFY(contentItem != 0);
3042 QTRY_VERIFY(listview->delegate() != 0);
3043 QTRY_VERIFY(listview->model() != 0);
3045 QMetaObject::invokeMethod(listview, "fillModel");
3046 qApp->processEvents();
3048 // Confirm items positioned correctly
3049 int itemCount = findItems<QQuickItem>(contentItem, "wrapper", false).count();
3050 QCOMPARE(itemCount, 3);
3052 for (int i = 0; i < itemCount; ++i) {
3053 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3054 if (!item) qWarning() << "Item" << i << "not found";
3056 QTRY_COMPARE(item->x(), i*300.0);
3059 // check that view is positioned correctly
3060 QTRY_COMPARE(listview->contentX(), 590.0);
3065 void tst_QQuickListView::manualHighlight()
3067 QQuickView *canvas = new QQuickView(0);
3068 canvas->setGeometry(0,0,240,320);
3070 QString filename(testFile("manual-highlight.qml"));
3071 canvas->setSource(QUrl::fromLocalFile(filename));
3073 qApp->processEvents();
3075 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3076 QTRY_VERIFY(listview != 0);
3078 QQuickItem *contentItem = listview->contentItem();
3079 QTRY_VERIFY(contentItem != 0);
3081 QTRY_COMPARE(listview->currentIndex(), 0);
3082 QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
3083 QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
3085 listview->setCurrentIndex(2);
3087 QTRY_COMPARE(listview->currentIndex(), 2);
3088 QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 2));
3089 QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
3092 listview->positionViewAtIndex(3, QQuickListView::Contain);
3094 QTRY_COMPARE(listview->currentIndex(), 2);
3095 QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 2));
3096 QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
3101 void tst_QQuickListView::QTBUG_11105()
3103 QQuickView *canvas = createView();
3106 for (int i = 0; i < 30; i++)
3107 model.addItem("Item" + QString::number(i), "");
3109 QDeclarativeContext *ctxt = canvas->rootContext();
3110 ctxt->setContextProperty("testModel", &model);
3112 TestObject *testObject = new TestObject;
3113 ctxt->setContextProperty("testObject", testObject);
3115 canvas->setSource(testFileUrl("listviewtest.qml"));
3116 qApp->processEvents();
3118 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3119 QTRY_VERIFY(listview != 0);
3121 QQuickItem *contentItem = listview->contentItem();
3122 QTRY_VERIFY(contentItem != 0);
3124 // Confirm items positioned correctly
3125 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3126 for (int i = 0; i < model.count() && i < itemCount; ++i) {
3127 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3128 if (!item) qWarning() << "Item" << i << "not found";
3130 QTRY_VERIFY(item->y() == i*20);
3133 listview->positionViewAtIndex(20, QQuickListView::Beginning);
3134 QCOMPARE(listview->contentY(), 280.);
3137 for (int i = 0; i < 5; i++)
3138 model2.addItem("Item" + QString::number(i), "");
3140 ctxt->setContextProperty("testModel", &model2);
3142 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3143 QCOMPARE(itemCount, 5);
3149 void tst_QQuickListView::header()
3151 QFETCH(QQuickListView::Orientation, orientation);
3152 QFETCH(Qt::LayoutDirection, layoutDirection);
3153 QFETCH(QPointF, initialHeaderPos);
3154 QFETCH(QPointF, firstDelegatePos);
3155 QFETCH(QPointF, initialContentPos);
3156 QFETCH(QPointF, changedHeaderPos);
3157 QFETCH(QPointF, changedContentPos);
3158 QFETCH(QPointF, resizeContentPos);
3161 for (int i = 0; i < 30; i++)
3162 model.addItem("Item" + QString::number(i), "");
3164 QQuickView *canvas = createView();
3165 canvas->rootContext()->setContextProperty("testModel", &model);
3166 canvas->rootContext()->setContextProperty("initialViewWidth", 240);
3167 canvas->rootContext()->setContextProperty("initialViewHeight", 320);
3168 canvas->setSource(testFileUrl("header.qml"));
3170 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3171 QTRY_VERIFY(listview != 0);
3172 listview->setOrientation(orientation);
3173 listview->setLayoutDirection(layoutDirection);
3175 QQuickItem *contentItem = listview->contentItem();
3176 QTRY_VERIFY(contentItem != 0);
3178 QQuickText *header = findItem<QQuickText>(contentItem, "header");
3181 QVERIFY(header == listview->headerItem());
3183 QCOMPARE(header->width(), 100.);
3184 QCOMPARE(header->height(), 30.);
3185 QCOMPARE(header->pos(), initialHeaderPos);
3186 QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos);
3188 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3190 QCOMPARE(item->pos(), firstDelegatePos);
3193 QCOMPARE(header->pos(), initialHeaderPos); // header should stay where it is
3195 for (int i = 0; i < 30; i++)
3196 model.addItem("Item" + QString::number(i), "");
3198 QSignalSpy headerItemSpy(listview, SIGNAL(headerItemChanged()));
3199 QMetaObject::invokeMethod(canvas->rootObject(), "changeHeader");
3201 QCOMPARE(headerItemSpy.count(), 1);
3203 header = findItem<QQuickText>(contentItem, "header");
3205 header = findItem<QQuickText>(contentItem, "header2");
3208 QVERIFY(header == listview->headerItem());
3210 QCOMPARE(header->pos(), changedHeaderPos);
3211 QCOMPARE(header->width(), 50.);
3212 QCOMPARE(header->height(), 20.);
3213 QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), changedContentPos);
3214 QCOMPARE(item->pos(), firstDelegatePos);
3219 // QTBUG-21207 header should become visible if view resizes from initial empty size
3221 canvas = createView();
3222 canvas->rootContext()->setContextProperty("testModel", &model);
3223 canvas->rootContext()->setContextProperty("initialViewWidth", 0.0);
3224 canvas->rootContext()->setContextProperty("initialViewHeight", 0.0);
3225 canvas->setSource(testFileUrl("header.qml"));
3227 listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3228 QTRY_VERIFY(listview != 0);
3229 listview->setOrientation(orientation);
3230 listview->setLayoutDirection(layoutDirection);
3232 listview->setWidth(240);
3233 listview->setHeight(320);
3234 QTRY_COMPARE(listview->headerItem()->pos(), initialHeaderPos);
3235 QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos);
3241 void tst_QQuickListView::header_data()
3243 QTest::addColumn<QQuickListView::Orientation>("orientation");
3244 QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3245 QTest::addColumn<QPointF>("initialHeaderPos");
3246 QTest::addColumn<QPointF>("changedHeaderPos");
3247 QTest::addColumn<QPointF>("initialContentPos");
3248 QTest::addColumn<QPointF>("changedContentPos");
3249 QTest::addColumn<QPointF>("firstDelegatePos");
3250 QTest::addColumn<QPointF>("resizeContentPos");
3252 // header1 = 100 x 30
3253 // header2 = 50 x 20
3254 // delegates = 240 x 20
3257 // header above items, top left
3258 QTest::newRow("vertical, left to right") << QQuickListView::Vertical << Qt::LeftToRight
3266 // header above items, top right
3267 QTest::newRow("vertical, layout right to left") << QQuickListView::Vertical << Qt::RightToLeft
3275 // header to left of items
3276 QTest::newRow("horizontal, layout left to right") << QQuickListView::Horizontal << Qt::LeftToRight
3284 // header to right of items
3285 QTest::newRow("horizontal, layout right to left") << QQuickListView::Horizontal << Qt::RightToLeft
3288 << QPointF(-240 + 100, 0)
3289 << QPointF(-240 + 50, 0)
3291 << QPointF(-240 + 40, 0);
3294 void tst_QQuickListView::header_delayItemCreation()
3296 QQuickView *canvas = createView();
3300 canvas->rootContext()->setContextProperty("setCurrentToZero", QVariant(false));
3301 canvas->setSource(testFileUrl("fillModelOnComponentCompleted.qml"));
3302 qApp->processEvents();
3304 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3305 QTRY_VERIFY(listview != 0);
3307 QQuickItem *contentItem = listview->contentItem();
3308 QTRY_VERIFY(contentItem != 0);
3310 QQuickText *header = findItem<QQuickText>(contentItem, "header");
3312 QCOMPARE(header->y(), -header->height());
3314 QCOMPARE(listview->contentY(), -header->height());
3317 QTRY_COMPARE(header->y(), -header->height());
3322 void tst_QQuickListView::footer()
3324 QFETCH(QQuickListView::Orientation, orientation);
3325 QFETCH(Qt::LayoutDirection, layoutDirection);
3326 QFETCH(QPointF, initialFooterPos);
3327 QFETCH(QPointF, firstDelegatePos);
3328 QFETCH(QPointF, initialContentPos);
3329 QFETCH(QPointF, changedFooterPos);
3330 QFETCH(QPointF, changedContentPos);
3331 QFETCH(QPointF, resizeContentPos);
3333 QQuickView *canvas = createView();
3336 for (int i = 0; i < 3; i++)
3337 model.addItem("Item" + QString::number(i), "");
3339 QDeclarativeContext *ctxt = canvas->rootContext();
3340 ctxt->setContextProperty("testModel", &model);
3342 canvas->setSource(testFileUrl("footer.qml"));
3344 qApp->processEvents();
3346 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3347 QTRY_VERIFY(listview != 0);
3348 listview->setOrientation(orientation);
3349 listview->setLayoutDirection(layoutDirection);
3351 QQuickItem *contentItem = listview->contentItem();
3352 QTRY_VERIFY(contentItem != 0);
3354 QQuickText *footer = findItem<QQuickText>(contentItem, "footer");
3357 QVERIFY(footer == listview->footerItem());
3359 QCOMPARE(footer->pos(), initialFooterPos);
3360 QCOMPARE(footer->width(), 100.);
3361 QCOMPARE(footer->height(), 30.);
3362 QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos);
3364 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3366 QCOMPARE(item->pos(), firstDelegatePos);
3369 model.removeItem(1);
3371 if (orientation == QQuickListView::Vertical) {
3372 QTRY_COMPARE(footer->y(), initialFooterPos.y() - 20); // delegate height = 20
3374 QTRY_COMPARE(footer->x(), layoutDirection == Qt::LeftToRight ?
3375 initialFooterPos.x() - 40 : initialFooterPos.x() + 40); // delegate width = 40
3381 QPointF posWhenNoItems(0, 0);
3382 if (orientation == QQuickListView::Horizontal && layoutDirection == Qt::RightToLeft)
3383 posWhenNoItems.setX(-100);
3384 QTRY_COMPARE(footer->pos(), posWhenNoItems);
3386 // if header is present, it's at a negative pos, so the footer should not move
3387 canvas->rootObject()->setProperty("showHeader", true);
3388 QTRY_COMPARE(footer->pos(), posWhenNoItems);
3389 canvas->rootObject()->setProperty("showHeader", false);
3392 for (int i = 0; i < 30; i++)
3393 model.addItem("Item" + QString::number(i), "");
3395 QSignalSpy footerItemSpy(listview, SIGNAL(footerItemChanged()));
3396 QMetaObject::invokeMethod(canvas->rootObject(), "changeFooter");
3398 QCOMPARE(footerItemSpy.count(), 1);
3400 footer = findItem<QQuickText>(contentItem, "footer");
3402 footer = findItem<QQuickText>(contentItem, "footer2");
3405 QVERIFY(footer == listview->footerItem());
3407 QCOMPARE(footer->pos(), changedFooterPos);
3408 QCOMPARE(footer->width(), 50.);
3409 QCOMPARE(footer->height(), 20.);
3410 QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), changedContentPos);
3412 item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3414 QCOMPARE(item->pos(), firstDelegatePos);
3416 listview->positionViewAtEnd();
3417 footer->setHeight(10);
3418 footer->setWidth(40);
3419 QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), resizeContentPos);
3424 void tst_QQuickListView::footer_data()
3426 QTest::addColumn<QQuickListView::Orientation>("orientation");
3427 QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3428 QTest::addColumn<QPointF>("initialFooterPos");
3429 QTest::addColumn<QPointF>("changedFooterPos");
3430 QTest::addColumn<QPointF>("initialContentPos");
3431 QTest::addColumn<QPointF>("changedContentPos");
3432 QTest::addColumn<QPointF>("firstDelegatePos");
3433 QTest::addColumn<QPointF>("resizeContentPos");
3435 // footer1 = 100 x 30
3436 // footer2 = 50 x 20
3437 // delegates = 40 x 20
3439 // view height = 320
3441 // footer below items, bottom left
3442 QTest::newRow("vertical, layout left to right") << QQuickListView::Vertical << Qt::LeftToRight
3443 << QPointF(0, 3 * 20)
3444 << QPointF(0, 30 * 20) // added 30 items
3448 << QPointF(0, 30 * 20 - 320 + 10);
3450 // footer below items, bottom right
3451 QTest::newRow("vertical, layout right to left") << QQuickListView::Vertical << Qt::RightToLeft
3452 << QPointF(0, 3 * 20)
3453 << QPointF(0, 30 * 20)
3457 << QPointF(0, 30 * 20 - 320 + 10);
3459 // footer to right of items
3460 QTest::newRow("horizontal, layout left to right") << QQuickListView::Horizontal << Qt::LeftToRight
3461 << QPointF(40 * 3, 0)
3462 << QPointF(40 * 30, 0)
3466 << QPointF(40 * 30 - 240 + 40, 0);
3468 // footer to left of items
3469 QTest::newRow("horizontal, layout right to left") << QQuickListView::Horizontal << Qt::RightToLeft
3470 << QPointF(-(40 * 3) - 100, 0)
3471 << QPointF(-(40 * 30) - 50, 0) // 50 = new footer width
3475 << QPointF(-(40 * 30) - 40, 0);
3478 class LVAccessor : public QQuickListView
3481 qreal minY() const { return minYExtent(); }
3482 qreal maxY() const { return maxYExtent(); }
3483 qreal minX() const { return minXExtent(); }
3484 qreal maxX() const { return maxXExtent(); }
3487 void tst_QQuickListView::headerFooter()
3491 QQuickView *canvas = createView();
3494 QDeclarativeContext *ctxt = canvas->rootContext();
3495 ctxt->setContextProperty("testModel", &model);
3497 canvas->setSource(testFileUrl("headerfooter.qml"));
3498 qApp->processEvents();
3500 QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3501 QTRY_VERIFY(listview != 0);
3503 QQuickItem *contentItem = listview->contentItem();
3504 QTRY_VERIFY(contentItem != 0);
3506 QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
3508 QCOMPARE(header->y(), -header->height());
3510 QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
3512 QCOMPARE(footer->y(), 0.);
3514 QCOMPARE(static_cast<LVAccessor*>(listview)->minY(), header->height());
3515 QCOMPARE(static_cast<LVAccessor*>(listview)->maxY(), header->height());
3521 QQuickView *canvas = createView();
3524 QDeclarativeContext *ctxt = canvas->rootContext();
3525 ctxt->setContextProperty("testModel", &model);
3527 canvas->setSource(testFileUrl("headerfooter.qml"));
3528 canvas->rootObject()->setProperty("horizontal", true);
3529 qApp->processEvents();
3531 QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3532 QTRY_VERIFY(listview != 0);
3534 QQuickItem *contentItem = listview->contentItem();
3535 QTRY_VERIFY(contentItem != 0);
3537 QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
3539 QCOMPARE(header->x(), -header->width());
3541 QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
3543 QCOMPARE(footer->x(), 0.);
3545 QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), header->width());
3546 QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), header->width());
3552 QQuickView *canvas = createView();
3555 QDeclarativeContext *ctxt = canvas->rootContext();
3556 ctxt->setContextProperty("testModel", &model);
3558 canvas->setSource(testFileUrl("headerfooter.qml"));
3559 canvas->rootObject()->setProperty("horizontal", true);
3560 canvas->rootObject()->setProperty("rtl", true);
3561 qApp->processEvents();
3563 QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3564 QTRY_VERIFY(listview != 0);
3566 QQuickItem *contentItem = listview->contentItem();
3567 QTRY_VERIFY(contentItem != 0);
3569 QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
3571 QCOMPARE(header->x(), 0.);
3573 QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
3575 QCOMPARE(footer->x(), -footer->width());
3577 QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), 240. - header->width());
3578 QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), 240. - header->width());
3584 void tst_QQuickListView::resizeView()
3586 QQuickView *canvas = createView();
3589 for (int i = 0; i < 40; i++)
3590 model.addItem("Item" + QString::number(i), "");
3592 QDeclarativeContext *ctxt = canvas->rootContext();
3593 ctxt->setContextProperty("testModel", &model);
3595 TestObject *testObject = new TestObject;
3596 ctxt->setContextProperty("testObject", testObject);
3598 canvas->setSource(testFileUrl("listviewtest.qml"));
3600 qApp->processEvents();
3602 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3603 QTRY_VERIFY(listview != 0);
3605 QQuickItem *contentItem = listview->contentItem();
3606 QTRY_VERIFY(contentItem != 0);
3608 // Confirm items positioned correctly
3609 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3610 for (int i = 0; i < model.count() && i < itemCount; ++i) {
3611 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3612 if (!item) qWarning() << "Item" << i << "not found";
3614 QTRY_COMPARE(item->y(), i*20.);
3617 QVariant heightRatio;
3618 QMetaObject::invokeMethod(canvas->rootObject(), "heightRatio", Q_RETURN_ARG(QVariant, heightRatio));
3619 QCOMPARE(heightRatio.toReal(), 0.4);
3621 listview->setHeight(200);
3623 QMetaObject::invokeMethod(canvas->rootObject(), "heightRatio", Q_RETURN_ARG(QVariant, heightRatio));
3624 QCOMPARE(heightRatio.toReal(), 0.25);
3626 // Ensure we handle -ve sizes
3627 listview->setHeight(-100);
3628 QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
3629 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper", false).count(), 1);
3631 listview->setCacheBuffer(200);
3632 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper", false).count(), 11);
3634 // ensure items in cache become visible
3635 listview->setHeight(200);
3636 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper", false).count(), 21);
3638 itemCount = findItems<QQuickItem>(contentItem, "wrapper", false).count();
3639 for (int i = 0; i < model.count() && i < itemCount; ++i) {
3640 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3641 if (!item) qWarning() << "Item" << i << "not found";
3643 QTRY_COMPARE(item->y(), i*20.);
3644 QCOMPARE(item->isVisible(), i < 11); // inside view visible, outside not visible
3647 // ensure items outside view become invisible
3648 listview->setHeight(100);
3649 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper", false).count(), 16);
3651 itemCount = findItems<QQuickItem>(contentItem, "wrapper", false).count();
3652 for (int i = 0; i < model.count() && i < itemCount; ++i) {
3653 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3654 if (!item) qWarning() << "Item" << i << "not found";
3656 QTRY_COMPARE(item->y(), i*20.);
3657 QCOMPARE(item->isVisible(), i < 6); // inside view visible, outside not visible
3664 void tst_QQuickListView::resizeViewAndRepaint()
3666 QQuickView *canvas = createView();
3670 for (int i = 0; i < 40; i++)
3671 model.addItem("Item" + QString::number(i), "");
3673 QDeclarativeContext *ctxt = canvas->rootContext();
3674 ctxt->setContextProperty("testModel", &model);
3675 ctxt->setContextProperty("initialHeight", 100);
3677 canvas->setSource(testFileUrl("resizeview.qml"));
3678 qApp->processEvents();
3680 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3681 QTRY_VERIFY(listview != 0);
3682 QQuickItem *contentItem = listview->contentItem();
3683 QTRY_VERIFY(contentItem != 0);
3685 // item at index 10 should not be currently visible
3686 QVERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 10));
3688 listview->setHeight(320);
3691 QSKIP("QTBUG-21590 view does not reliably receive polish without a running animation");
3694 QTRY_VERIFY(findItem<QQuickItem>(contentItem, "wrapper", 10));
3696 listview->setHeight(100);
3697 QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 10));
3702 void tst_QQuickListView::sizeLessThan1()
3704 QQuickView *canvas = createView();
3707 for (int i = 0; i < 30; i++)
3708 model.addItem("Item" + QString::number(i), "");
3710 QDeclarativeContext *ctxt = canvas->rootContext();
3711 ctxt->setContextProperty("testModel", &model);
3713 TestObject *testObject = new TestObject;
3714 ctxt->setContextProperty("testObject", testObject);
3716 canvas->setSource(testFileUrl("sizelessthan1.qml"));
3717 qApp->processEvents();
3719 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3720 QTRY_VERIFY(listview != 0);
3722 QQuickItem *contentItem = listview->contentItem();
3723 QTRY_VERIFY(contentItem != 0);
3725 // Confirm items positioned correctly
3726 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3727 for (int i = 0; i < model.count() && i < itemCount; ++i) {
3728 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3729 if (!item) qWarning() << "Item" << i << "not found";
3731 QTRY_COMPARE(item->y(), i*0.5);
3738 void tst_QQuickListView::QTBUG_14821()
3740 QQuickView *canvas = createView();
3742 canvas->setSource(testFileUrl("qtbug14821.qml"));
3743 qApp->processEvents();
3745 QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3746 QVERIFY(listview != 0);
3748 QQuickItem *contentItem = listview->contentItem();
3749 QVERIFY(contentItem != 0);
3751 listview->decrementCurrentIndex();
3752 QCOMPARE(listview->currentIndex(), 99);
3754 listview->incrementCurrentIndex();
3755 QCOMPARE(listview->currentIndex(), 0);
3760 void tst_QQuickListView::resizeDelegate()
3762 QQuickView *canvas = createView();
3765 QStringList strings;
3766 for (int i = 0; i < 30; ++i)
3767 strings << QString::number(i);
3768 QStringListModel model(strings);
3770 QDeclarativeContext *ctxt = canvas->rootContext();
3771 ctxt->setContextProperty("testModel", &model);
3773 canvas->setSource(testFileUrl("displaylist.qml"));
3774 qApp->processEvents();
3776 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3777 QVERIFY(listview != 0);
3779 QQuickItem *contentItem = listview->contentItem();
3780 QVERIFY(contentItem != 0);
3782 QCOMPARE(listview->count(), model.rowCount());
3784 listview->setCurrentIndex(25);
3785 listview->setContentY(0);
3788 for (int i = 0; i < 16; ++i) {
3789 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3791 QCOMPARE(item->y(), i*20.0);
3794 QCOMPARE(listview->currentItem()->y(), 500.0);
3795 QTRY_COMPARE(listview->highlightItem()->y(), 500.0);
3797 canvas->rootObject()->setProperty("delegateHeight", 30);
3800 for (int i = 0; i < 11; ++i) {
3801 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3803 QTRY_COMPARE(item->y(), i*30.0);
3806 QTRY_COMPARE(listview->currentItem()->y(), 750.0);
3807 QTRY_COMPARE(listview->highlightItem()->y(), 750.0);
3809 listview->setCurrentIndex(1);
3810 listview->positionViewAtIndex(25, QQuickListView::Beginning);
3811 listview->positionViewAtIndex(5, QQuickListView::Beginning);
3813 for (int i = 5; i < 16; ++i) {
3814 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3816 QCOMPARE(item->y(), i*30.0);
3819 QTRY_COMPARE(listview->currentItem()->y(), 30.0);
3820 QTRY_COMPARE(listview->highlightItem()->y(), 30.0);
3822 canvas->rootObject()->setProperty("delegateHeight", 20);
3825 for (int i = 5; i < 11; ++i) {
3826 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3828 QTRY_COMPARE(item->y(), 150 + (i-5)*20.0);
3831 QTRY_COMPARE(listview->currentItem()->y(), 70.0);
3832 QTRY_COMPARE(listview->highlightItem()->y(), 70.0);
3837 void tst_QQuickListView::resizeFirstDelegate()
3839 // QTBUG-20712: Content Y jumps constantly if first delegate height == 0
3840 // and other delegates have height > 0
3842 QSKIP("Test unstable - QTBUG-22872");
3844 QQuickView *canvas = createView();
3847 // bug only occurs when all items in the model are visible
3849 for (int i = 0; i < 10; i++)
3850 model.addItem("Item" + QString::number(i), "");
3852 QDeclarativeContext *ctxt = canvas->rootContext();
3853 ctxt->setContextProperty("testModel", &model);
3855 TestObject *testObject = new TestObject;
3856 ctxt->setContextProperty("testObject", testObject);
3858 canvas->setSource(testFileUrl("listviewtest.qml"));
3859 qApp->processEvents();
3861 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3862 QVERIFY(listview != 0);
3864 QQuickItem *contentItem = listview->contentItem();
3865 QVERIFY(contentItem != 0);
3867 QQuickItem *item = 0;
3868 for (int i = 0; i < model.count(); ++i) {
3869 item = findItem<QQuickItem>(contentItem, "wrapper", i);
3871 QCOMPARE(item->y(), i*20.0);
3874 item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3877 // check the content y has not jumped up and down
3878 QCOMPARE(listview->contentY(), 0.0);
3879 QSignalSpy spy(listview, SIGNAL(contentYChanged()));
3881 QCOMPARE(spy.count(), 0);
3883 for (int i = 1; i < model.count(); ++i) {
3884 item = findItem<QQuickItem>(contentItem, "wrapper", i);
3886 QTRY_COMPARE(item->y(), (i-1)*20.0);
3890 // QTBUG-22014: refill doesn't clear items scrolling off the top of the
3891 // list if they follow a zero-sized delegate
3893 for (int i = 0; i < 10; i++)
3894 model.addItem("Item" + QString::number(i), "");
3896 item = findItem<QQuickItem>(contentItem, "wrapper", 1);
3900 listview->setCurrentIndex(19);
3901 qApp->processEvents();
3903 // items 0-2 should have been deleted
3904 for (int i=0; i<3; i++) {
3905 QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper", i));
3912 void tst_QQuickListView::QTBUG_16037()
3914 QQuickView *canvas = createView();
3917 canvas->setSource(testFileUrl("qtbug16037.qml"));
3918 qApp->processEvents();
3920 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "listview");
3921 QTRY_VERIFY(listview != 0);
3923 QVERIFY(listview->contentHeight() <= 0.0);
3925 QMetaObject::invokeMethod(canvas->rootObject(), "setModel");
3927 QTRY_COMPARE(listview->contentHeight(), 80.0);
3932 void tst_QQuickListView::indexAt_itemAt_data()
3934 QTest::addColumn<qreal>("x");
3935 QTest::addColumn<qreal>("y");
3936 QTest::addColumn<int>("index");
3938 QTest::newRow("Item 0 - 0, 0") << 0. << 0. << 0;
3939 QTest::newRow("Item 0 - 0, 19") << 0. << 19. << 0;
3940 QTest::newRow("Item 0 - 239, 19") << 239. << 19. << 0;
3941 QTest::newRow("Item 1 - 0, 20") << 0. << 20. << 1;
3942 QTest::newRow("No Item - 240, 20") << 240. << 20. << -1;
3945 void tst_QQuickListView::indexAt_itemAt()
3951 QQuickView *canvas = createView();
3954 for (int i = 0; i < 30; i++)
3955 model.addItem("Item" + QString::number(i), "");
3957 QDeclarativeContext *ctxt = canvas->rootContext();
3958 ctxt->setContextProperty("testModel", &model);
3960 TestObject *testObject = new TestObject;
3961 ctxt->setContextProperty("testObject", testObject);
3963 canvas->setSource(testFileUrl("listviewtest.qml"));
3964 qApp->processEvents();
3966 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3967 QTRY_VERIFY(listview != 0);
3969 QQuickItem *contentItem = listview->contentItem();
3970 QTRY_VERIFY(contentItem != 0);
3972 QQuickItem *item = 0;
3974 item = findItem<QQuickItem>(contentItem, "wrapper", index);
3977 QCOMPARE(listview->indexAt(x,y), index);
3978 QVERIFY(listview->itemAt(x,y) == item);
3984 void tst_QQuickListView::incrementalModel()
3986 QQuickView *canvas = createView();
3988 IncrementalModel model;
3989 QDeclarativeContext *ctxt = canvas->rootContext();
3990 ctxt->setContextProperty("testModel", &model);
3992 canvas->setSource(testFileUrl("displaylist.qml"));
3993 qApp->processEvents();
3995 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3996 QTRY_VERIFY(listview != 0);
3998 QQuickItem *contentItem = listview->contentItem();
3999 QTRY_VERIFY(contentItem != 0);
4001 QTRY_COMPARE(listview->count(), 20);
4003 listview->positionViewAtIndex(10, QQuickListView::Beginning);
4005 QTRY_COMPARE(listview->count(), 25);
4010 void tst_QQuickListView::onAdd()
4012 QFETCH(int, initialItemCount);
4013 QFETCH(int, itemsToAdd);
4015 const int delegateHeight = 10;
4018 // these initial items should not trigger ListView.onAdd
4019 for (int i=0; i<initialItemCount; i++)
4020 model.addItem("dummy value", "dummy value");
4022 QQuickView *canvas = createView();
4023 canvas->setGeometry(0,0,200, delegateHeight * (initialItemCount + itemsToAdd));
4024 QDeclarativeContext *ctxt = canvas->rootContext();
4025 ctxt->setContextProperty("testModel", &model);
4026 ctxt->setContextProperty("delegateHeight", delegateHeight);
4027 canvas->setSource(testFileUrl("attachedSignals.qml"));
4029 QObject *object = canvas->rootObject();
4030 object->setProperty("width", canvas->width());
4031 object->setProperty("height", canvas->height());
4032 qApp->processEvents();
4034 QList<QPair<QString, QString> > items;
4035 for (int i=0; i<itemsToAdd; i++)
4036 items << qMakePair(QString("value %1").arg(i), QString::number(i));
4037 model.addItems(items);
4038 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
4040 QVariantList result = object->property("addedDelegates").toList();
4041 QCOMPARE(result.count(), items.count());
4042 for (int i=0; i<items.count(); i++)
4043 QCOMPARE(result[i].toString(), items[i].first);
4048 void tst_QQuickListView::onAdd_data()
4050 QTest::addColumn<int>("initialItemCount");
4051 QTest::addColumn<int>("itemsToAdd");
4053 QTest::newRow("0, add 1") << 0 << 1;
4054 QTest::newRow("0, add 2") << 0 << 2;
4055 QTest::newRow("0, add 10") << 0 << 10;
4057 QTest::newRow("1, add 1") << 1 << 1;
4058 QTest::newRow("1, add 2") << 1 << 2;
4059 QTest::newRow("1, add 10") << 1 << 10;
4061 QTest::newRow("5, add 1") << 5 << 1;
4062 QTest::newRow("5, add 2") << 5 << 2;
4063 QTest::newRow("5, add 10") << 5 << 10;
4066 void tst_QQuickListView::onRemove()
4068 QFETCH(int, initialItemCount);
4069 QFETCH(int, indexToRemove);
4070 QFETCH(int, removeCount);
4072 const int delegateHeight = 10;
4074 for (int i=0; i<initialItemCount; i++)
4075 model.addItem(QString("value %1").arg(i), "dummy value");
4077 QQuickView *canvas = createView();
4078 QDeclarativeContext *ctxt = canvas->rootContext();
4079 ctxt->setContextProperty("testModel", &model);
4080 ctxt->setContextProperty("delegateHeight", delegateHeight);
4081 canvas->setSource(testFileUrl("attachedSignals.qml"));
4082 QObject *object = canvas->rootObject();
4084 model.removeItems(indexToRemove, removeCount);
4085 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
4087 QCOMPARE(object->property("removedDelegateCount"), QVariant(removeCount));
4092 void tst_QQuickListView::onRemove_data()
4094 QTest::addColumn<int>("initialItemCount");
4095 QTest::addColumn<int>("indexToRemove");
4096 QTest::addColumn<int>("removeCount");
4098 QTest::newRow("remove first") << 1 << 0 << 1;
4099 QTest::newRow("two items, remove first") << 2 << 0 << 1;
4100 QTest::newRow("two items, remove last") << 2 << 1 << 1;
4101 QTest::newRow("two items, remove all") << 2 << 0 << 2;
4103 QTest::newRow("four items, remove first") << 4 << 0 << 1;
4104 QTest::newRow("four items, remove 0-2") << 4 << 0 << 2;
4105 QTest::newRow("four items, remove 1-3") << 4 << 1 << 2;
4106 QTest::newRow("four items, remove 2-4") << 4 << 2 << 2;
4107 QTest::newRow("four items, remove last") << 4 << 3 << 1;
4108 QTest::newRow("four items, remove all") << 4 << 0 << 4;
4110 QTest::newRow("ten items, remove 1-8") << 10 << 0 << 8;
4111 QTest::newRow("ten items, remove 2-7") << 10 << 2 << 5;
4112 QTest::newRow("ten items, remove 4-10") << 10 << 4 << 6;
4115 void tst_QQuickListView::rightToLeft()
4117 QQuickView *canvas = createView();
4118 canvas->setGeometry(0,0,640,320);
4119 canvas->setSource(testFileUrl("rightToLeft.qml"));
4120 qApp->processEvents();
4122 QVERIFY(canvas->rootObject() != 0);
4123 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "view");
4124 QTRY_VERIFY(listview != 0);
4126 QQuickItem *contentItem = listview->contentItem();
4127 QTRY_VERIFY(contentItem != 0);
4129 QQuickVisualItemModel *model = canvas->rootObject()->findChild<QQuickVisualItemModel*>("itemModel");
4130 QTRY_VERIFY(model != 0);
4132 QTRY_VERIFY(model->count() == 3);
4133 QTRY_COMPARE(listview->currentIndex(), 0);
4135 // initial position at first item, right edge aligned
4136 QCOMPARE(listview->contentX(), -640.);
4138 QQuickItem *item = findItem<QQuickItem>(contentItem, "item1");
4140 QTRY_COMPARE(item->x(), -100.0);
4141 QCOMPARE(item->height(), listview->height());
4143 QQuickText *text = findItem<QQuickText>(contentItem, "text1");
4145 QTRY_COMPARE(text->text(), QLatin1String("index: 0"));
4147 listview->setCurrentIndex(2);
4149 item = findItem<QQuickItem>(contentItem, "item3");
4151 QTRY_COMPARE(item->x(), -540.0);
4153 text = findItem<QQuickText>(contentItem, "text3");
4155 QTRY_COMPARE(text->text(), QLatin1String("index: 2"));
4157 QCOMPARE(listview->contentX(), -640.);
4159 // Ensure resizing maintains position relative to right edge
4160 qobject_cast<QQuickItem*>(canvas->rootObject())->setWidth(600);
4161 QTRY_COMPARE(listview->contentX(), -600.);
4166 void tst_QQuickListView::test_mirroring()
4168 QQuickView *canvasA = createView();
4169 canvasA->setSource(testFileUrl("rightToLeft.qml"));
4170 QQuickListView *listviewA = findItem<QQuickListView>(canvasA->rootObject(), "view");
4171 QTRY_VERIFY(listviewA != 0);
4173 QQuickView *canvasB = createView();
4174 canvasB->setSource(testFileUrl("rightToLeft.qml"));
4175 QQuickListView *listviewB = findItem<QQuickListView>(canvasB->rootObject(), "view");
4176 QTRY_VERIFY(listviewA != 0);
4177 qApp->processEvents();
4179 QList<QString> objectNames;
4180 objectNames << "item1" << "item2"; // << "item3"
4182 listviewA->setProperty("layoutDirection", Qt::LeftToRight);
4183 listviewB->setProperty("layoutDirection", Qt::RightToLeft);
4184 QCOMPARE(listviewA->layoutDirection(), listviewA->effectiveLayoutDirection());
4187 foreach (const QString objectName, objectNames)
4188 QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x());
4190 listviewA->setProperty("layoutDirection", Qt::LeftToRight);
4191 listviewB->setProperty("layoutDirection", Qt::LeftToRight);
4194 foreach (const QString objectName, objectNames)
4195 QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x());
4197 QVERIFY(listviewB->layoutDirection() == listviewB->effectiveLayoutDirection());
4198 QQuickItemPrivate::get(listviewB)->setLayoutMirror(true);
4199 QVERIFY(listviewB->layoutDirection() != listviewB->effectiveLayoutDirection());
4201 // LTR != LTR+mirror
4202 foreach (const QString objectName, objectNames)
4203 QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x());
4205 listviewA->setProperty("layoutDirection", Qt::RightToLeft);
4207 // RTL == LTR+mirror
4208 foreach (const QString objectName, objectNames)
4209 QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x());
4211 listviewB->setProperty("layoutDirection", Qt::RightToLeft);
4213 // RTL != RTL+mirror
4214 foreach (const QString objectName, objectNames)
4215 QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x());
4217 listviewA->setProperty("layoutDirection", Qt::LeftToRight);
4219 // LTR == RTL+mirror
4220 foreach (const QString objectName, objectNames)
4221 QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x());
4227 void tst_QQuickListView::margins()
4229 QQuickView *canvas = createView();
4232 for (int i = 0; i < 50; i++)
4233 model.addItem("Item" + QString::number(i), "");
4235 QDeclarativeContext *ctxt = canvas->rootContext();
4236 ctxt->setContextProperty("testModel", &model);
4238 canvas->setSource(testFileUrl("margins.qml"));
4240 qApp->processEvents();
4242 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
4243 QTRY_VERIFY(listview != 0);
4245 QQuickItem *contentItem = listview->contentItem();
4246 QTRY_VERIFY(contentItem != 0);
4248 QCOMPARE(listview->contentY(), -30.);
4249 QCOMPARE(listview->yOrigin(), 0.);
4252 listview->positionViewAtEnd();
4253 qreal pos = listview->contentY();
4254 listview->setContentY(pos + 80);
4255 listview->returnToBounds();
4256 QTRY_COMPARE(listview->contentY(), pos + 50);
4258 // remove item before visible and check that top margin is maintained
4259 // and yOrigin is updated
4260 listview->setContentY(100);
4261 model.removeItem(1);
4262 QTRY_COMPARE(listview->count(), model.count());
4263 listview->setContentY(-50);
4264 QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
4265 listview->returnToBounds();
4266 QCOMPARE(listview->yOrigin(), 20.);
4267 QTRY_COMPARE(listview->contentY(), -10.);
4269 // reduce top margin
4270 listview->setTopMargin(20);
4271 QCOMPARE(listview->yOrigin(), 20.);
4272 QTRY_COMPARE(listview->contentY(), 0.);
4275 listview->positionViewAtEnd();
4276 pos = listview->contentY();
4277 listview->setContentY(pos + 80);
4278 listview->returnToBounds();
4279 QTRY_COMPARE(listview->contentY(), pos + 50);
4281 // reduce bottom margin
4282 pos = listview->contentY();
4283 listview->setBottomMargin(40);
4284 QCOMPARE(listview->yOrigin(), 20.);
4285 QTRY_COMPARE(listview->contentY(), pos-10);
4290 void tst_QQuickListView::snapToItem_data()
4292 QTest::addColumn<QQuickListView::Orientation>("orientation");
4293 QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
4294 QTest::addColumn<int>("highlightRangeMode");
4295 QTest::addColumn<QPoint>("flickStart");
4296 QTest::addColumn<QPoint>("flickEnd");
4297 QTest::addColumn<qreal>("snapAlignment");
4298 QTest::addColumn<qreal>("endExtent");
4299 QTest::addColumn<qreal>("startExtent");
4301 QTest::newRow("vertical, left to right") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4302 << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 1200.0 << 0.0;
4304 QTest::newRow("horizontal, left to right") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4305 << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 1200.0 << 0.0;
4307 QTest::newRow("horizontal, right to left") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
4308 << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -1200.0 - 240.0 << -240.0;
4310 QTest::newRow("vertical, left to right, enforce range") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4311 << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 1340.0 << -20.0;
4313 QTest::newRow("horizontal, left to right, enforce range") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4314 << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 1340.0 << -20.0;
4316 QTest::newRow("horizontal, right to left, enforce range") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
4317 << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -1200.0 - 240.0 - 140.0 << -220.0;
4320 void tst_QQuickListView::snapToItem()
4322 QFETCH(QQuickListView::Orientation, orientation);
4323 QFETCH(Qt::LayoutDirection, layoutDirection);
4324 QFETCH(int, highlightRangeMode);
4325 QFETCH(QPoint, flickStart);
4326 QFETCH(QPoint, flickEnd);
4327 QFETCH(qreal, snapAlignment);
4328 QFETCH(qreal, endExtent);
4329 QFETCH(qreal, startExtent);
4331 QQuickView *canvas = createView();
4333 canvas->setSource(testFileUrl("snapToItem.qml"));
4335 qApp->processEvents();
4337 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
4338 QTRY_VERIFY(listview != 0);
4340 listview->setOrientation(orientation);
4341 listview->setLayoutDirection(layoutDirection);
4342 listview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
4344 QQuickItem *contentItem = listview->contentItem();
4345 QTRY_VERIFY(contentItem != 0);
4347 // confirm that a flick hits an item boundary
4348 flick(canvas, flickStart, flickEnd, 180);
4349 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4350 if (orientation == QQuickListView::Vertical)
4351 QCOMPARE(qreal(fmod(listview->contentY(),80.0)), snapAlignment);
4353 QCOMPARE(qreal(fmod(listview->contentX(),80.0)), snapAlignment);
4357 flick(canvas, flickStart, flickEnd, 180);
4358 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4359 } while (orientation == QQuickListView::Vertical
4360 ? !listview->isAtYEnd()
4361 : layoutDirection == Qt::LeftToRight ? !listview->isAtXEnd() : !listview->isAtXBeginning());
4363 if (orientation == QQuickListView::Vertical)
4364 QCOMPARE(listview->contentY(), endExtent);
4366 QCOMPARE(listview->contentX(), endExtent);
4370 flick(canvas, flickEnd, flickStart, 180);
4371 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4372 } while (orientation == QQuickListView::Vertical
4373 ? !listview->isAtYBeginning()
4374 : layoutDirection == Qt::LeftToRight ? !listview->isAtXBeginning() : !listview->isAtXEnd());
4376 if (orientation == QQuickListView::Vertical)
4377 QCOMPARE(listview->contentY(), startExtent);
4379 QCOMPARE(listview->contentX(), startExtent);
4384 void tst_QQuickListView::qListModelInterface_items()
4386 items<TestModel>(testFileUrl("listviewtest.qml"), false);
4389 void tst_QQuickListView::qListModelInterface_package_items()
4391 items<TestModel>(testFileUrl("listviewtest-package.qml"), true);
4394 void tst_QQuickListView::qAbstractItemModel_items()
4396 items<TestModel2>(testFileUrl("listviewtest.qml"), false);
4399 void tst_QQuickListView::qListModelInterface_changed()
4401 changed<TestModel>(testFileUrl("listviewtest.qml"), false);
4404 void tst_QQuickListView::qListModelInterface_package_changed()
4406 changed<TestModel>(testFileUrl("listviewtest-package.qml"), true);
4409 void tst_QQuickListView::qAbstractItemModel_changed()
4411 changed<TestModel2>(testFileUrl("listviewtest.qml"), false);
4414 void tst_QQuickListView::qListModelInterface_inserted()
4416 inserted<TestModel>(testFileUrl("listviewtest.qml"));
4419 void tst_QQuickListView::qListModelInterface_package_inserted()
4421 inserted<TestModel>(testFileUrl("listviewtest-package.qml"));
4424 void tst_QQuickListView::qListModelInterface_inserted_more()
4426 inserted_more<TestModel>();
4429 void tst_QQuickListView::qListModelInterface_inserted_more_data()
4431 inserted_more_data();
4434 void tst_QQuickListView::qAbstractItemModel_inserted()
4436 inserted<TestModel2>(testFileUrl("listviewtest.qml"));
4439 void tst_QQuickListView::qAbstractItemModel_inserted_more()
4441 inserted_more<TestModel2>();
4444 void tst_QQuickListView::qAbstractItemModel_inserted_more_data()
4446 inserted_more_data();
4449 void tst_QQuickListView::qListModelInterface_removed()
4451 removed<TestModel>(testFileUrl("listviewtest.qml"), false);
4452 removed<TestModel>(testFileUrl("listviewtest.qml"), true);
4455 void tst_QQuickListView::qListModelInterface_removed_more()
4457 removed_more<TestModel>(testFileUrl("listviewtest.qml"));
4460 void tst_QQuickListView::qListModelInterface_removed_more_data()
4462 removed_more_data();
4465 void tst_QQuickListView::qListModelInterface_package_removed()
4467 removed<TestModel>(testFileUrl("listviewtest-package.qml"), false);
4468 removed<TestModel>(testFileUrl("listviewtest-package.qml"), true);
4471 void tst_QQuickListView::qAbstractItemModel_removed()
4473 removed<TestModel2>(testFileUrl("listviewtest.qml"), false);
4474 removed<TestModel2>(testFileUrl("listviewtest.qml"), true);
4477 void tst_QQuickListView::qAbstractItemModel_removed_more()
4479 removed_more<TestModel2>(testFileUrl("listviewtest.qml"));
4482 void tst_QQuickListView::qAbstractItemModel_removed_more_data()
4484 removed_more_data();
4487 void tst_QQuickListView::qListModelInterface_moved()
4489 moved<TestModel>(testFileUrl("listviewtest.qml"));
4492 void tst_QQuickListView::qListModelInterface_moved_data()
4497 void tst_QQuickListView::qListModelInterface_package_moved()
4499 moved<TestModel>(testFileUrl("listviewtest-package.qml"));
4502 void tst_QQuickListView::qListModelInterface_package_moved_data()
4507 void tst_QQuickListView::qAbstractItemModel_moved()
4509 moved<TestModel2>(testFileUrl("listviewtest.qml"));
4512 void tst_QQuickListView::qAbstractItemModel_moved_data()
4517 void tst_QQuickListView::qListModelInterface_clear()
4519 clear<TestModel>(testFileUrl("listviewtest.qml"));
4522 void tst_QQuickListView::qListModelInterface_package_clear()
4524 clear<TestModel>(testFileUrl("listviewtest-package.qml"));
4527 void tst_QQuickListView::qAbstractItemModel_clear()
4529 clear<TestModel2>(testFileUrl("listviewtest.qml"));
4532 void tst_QQuickListView::qListModelInterface_sections()
4534 sections<TestModel>(testFileUrl("listview-sections.qml"));
4537 void tst_QQuickListView::qListModelInterface_package_sections()
4539 sections<TestModel>(testFileUrl("listview-sections-package.qml"));
4542 void tst_QQuickListView::qAbstractItemModel_sections()
4544 sections<TestModel2>(testFileUrl("listview-sections.qml"));
4547 void tst_QQuickListView::creationContext()
4550 canvas.setGeometry(0,0,240,320);
4551 canvas.setSource(testFileUrl("creationContext.qml"));
4552 qApp->processEvents();
4554 QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
4556 QVERIFY(rootItem->property("count").toInt() > 0);
4559 QVERIFY(item = rootItem->findChild<QQuickItem *>("listItem"));
4560 QCOMPARE(item->property("text").toString(), QString("Hello!"));
4561 QVERIFY(item = rootItem->findChild<QQuickItem *>("header"));
4562 QCOMPARE(item->property("text").toString(), QString("Hello!"));
4563 QVERIFY(item = rootItem->findChild<QQuickItem *>("footer"));
4564 QCOMPARE(item->property("text").toString(), QString("Hello!"));
4565 QVERIFY(item = rootItem->findChild<QQuickItem *>("section"));
4566 QCOMPARE(item->property("text").toString(), QString("Hello!"));
4569 void tst_QQuickListView::QTBUG_21742()
4572 canvas.setGeometry(0,0,200,200);
4573 canvas.setSource(testFileUrl("qtbug-21742.qml"));
4574 qApp->processEvents();
4576 QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
4578 QCOMPARE(rootItem->property("count").toInt(), 1);
4581 QQuickView *tst_QQuickListView::createView()
4583 QQuickView *canvas = new QQuickView(0);
4584 canvas->setGeometry(0,0,240,320);
4589 void tst_QQuickListView::flick(QQuickView *canvas, const QPoint &from, const QPoint &to, int duration)
4591 const int pointCount = 5;
4592 QPoint diff = to - from;
4594 // send press, five equally spaced moves, and release.
4595 QTest::mousePress(canvas, Qt::LeftButton, 0, from);
4597 for (int i = 0; i < pointCount; ++i) {
4598 QMouseEvent mv(QEvent::MouseMove, from + (i+1)*diff/pointCount, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
4599 QGuiApplication::sendEvent(canvas, &mv);
4600 QTest::qWait(duration/pointCount);
4601 QCoreApplication::processEvents();
4604 QTest::mouseRelease(canvas, Qt::LeftButton, 0, to);
4607 void tst_QQuickListView::asynchronous()
4609 QQuickView *canvas = createView();
4611 QDeclarativeIncubationController controller;
4612 canvas->engine()->setIncubationController(&controller);
4614 canvas->setSource(testFileUrl("asyncloader.qml"));
4616 QQuickItem *rootObject = qobject_cast<QQuickItem*>(canvas->rootObject());
4617 QVERIFY(rootObject);
4619 QQuickListView *listview = 0;
4622 controller.incubateWhile(&b);
4623 listview = rootObject->findChild<QQuickListView*>("view");
4626 // items will be created one at a time
4627 for (int i = 0; i < 8; ++i) {
4628 QVERIFY(findItem<QQuickItem>(listview, "wrapper", i) == 0);
4629 QQuickItem *item = 0;
4632 controller.incubateWhile(&b);
4633 item = findItem<QQuickItem>(listview, "wrapper", i);
4639 controller.incubateWhile(&b);
4642 // verify positioning
4643 QQuickItem *contentItem = listview->contentItem();
4644 for (int i = 0; i < 8; ++i) {
4645 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
4646 QTRY_COMPARE(item->y(), i*50.0);
4652 void tst_QQuickListView::snapOneItem_data()
4654 QTest::addColumn<QQuickListView::Orientation>("orientation");
4655 QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
4656 QTest::addColumn<int>("highlightRangeMode");
4657 QTest::addColumn<QPoint>("flickStart");
4658 QTest::addColumn<QPoint>("flickEnd");
4659 QTest::addColumn<qreal>("snapAlignment");
4660 QTest::addColumn<qreal>("endExtent");
4661 QTest::addColumn<qreal>("startExtent");
4663 QTest::newRow("vertical, left to right") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4664 << QPoint(20, 200) << QPoint(20, 20) << 180.0 << 560.0 << 0.0;
4666 QTest::newRow("horizontal, left to right") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4667 << QPoint(200, 20) << QPoint(20, 20) << 180.0 << 560.0 << 0.0;
4669 QTest::newRow("horizontal, right to left") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
4670 << QPoint(20, 20) << QPoint(200, 20) << -420.0 << -560.0 - 240.0 << -240.0;
4672 QTest::newRow("vertical, left to right, enforce range") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4673 << QPoint(20, 200) << QPoint(20, 20) << 180.0 << 580.0 << -20.0;
4675 QTest::newRow("horizontal, left to right, enforce range") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4676 << QPoint(200, 20) << QPoint(20, 20) << 180.0 << 580.0 << -20.0;
4678 QTest::newRow("horizontal, right to left, enforce range") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
4679 << QPoint(20, 20) << QPoint(200, 20) << -420.0 << -580.0 - 240.0 << -220.0;
4682 void tst_QQuickListView::snapOneItem()
4684 QFETCH(QQuickListView::Orientation, orientation);
4685 QFETCH(Qt::LayoutDirection, layoutDirection);
4686 QFETCH(int, highlightRangeMode);
4687 QFETCH(QPoint, flickStart);
4688 QFETCH(QPoint, flickEnd);
4689 QFETCH(qreal, snapAlignment);
4690 QFETCH(qreal, endExtent);
4691 QFETCH(qreal, startExtent);
4694 // This test seems to be unreliable - different test data fails on different runs
4695 QSKIP("QTBUG-23481");
4698 QQuickView *canvas = createView();
4700 canvas->setSource(testFileUrl("snapOneItem.qml"));
4702 qApp->processEvents();
4704 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
4705 QTRY_VERIFY(listview != 0);
4707 listview->setOrientation(orientation);
4708 listview->setLayoutDirection(layoutDirection);
4709 listview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
4711 QQuickItem *contentItem = listview->contentItem();
4712 QTRY_VERIFY(contentItem != 0);
4714 QSignalSpy currentIndexSpy(listview, SIGNAL(currentIndexChanged()));
4716 // confirm that a flick hits the next item boundary
4717 flick(canvas, flickStart, flickEnd, 180);
4718 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4719 if (orientation == QQuickListView::Vertical)
4720 QCOMPARE(listview->contentY(), snapAlignment);
4722 QCOMPARE(listview->contentX(), snapAlignment);
4724 if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4725 QCOMPARE(listview->currentIndex(), 1);
4726 QCOMPARE(currentIndexSpy.count(), 1);
4731 flick(canvas, flickStart, flickEnd, 180);
4732 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4733 } while (orientation == QQuickListView::Vertical
4734 ? !listview->isAtYEnd()
4735 : layoutDirection == Qt::LeftToRight ? !listview->isAtXEnd() : !listview->isAtXBeginning());
4737 if (orientation == QQuickListView::Vertical)
4738 QCOMPARE(listview->contentY(), endExtent);
4740 QCOMPARE(listview->contentX(), endExtent);
4742 if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4743 QCOMPARE(listview->currentIndex(), 3);
4744 QCOMPARE(currentIndexSpy.count(), 3);
4749 flick(canvas, flickEnd, flickStart, 180);
4750 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4751 } while (orientation == QQuickListView::Vertical
4752 ? !listview->isAtYBeginning()
4753 : layoutDirection == Qt::LeftToRight ? !listview->isAtXBeginning() : !listview->isAtXEnd());
4755 if (orientation == QQuickListView::Vertical)
4756 QCOMPARE(listview->contentY(), startExtent);
4758 QCOMPARE(listview->contentX(), startExtent);
4760 if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4761 QCOMPARE(listview->currentIndex(), 0);
4762 QCOMPARE(currentIndexSpy.count(), 6);
4768 void tst_QQuickListView::unrequestedVisibility()
4771 for (int i = 0; i < 30; i++)
4772 model.addItem("Item" + QString::number(i), QString::number(i));
4774 QQuickView *canvas = new QQuickView(0);
4775 canvas->setGeometry(0,0,240,320);
4777 QDeclarativeContext *ctxt = canvas->rootContext();
4778 ctxt->setContextProperty("testModel", &model);
4779 ctxt->setContextProperty("testWrap", QVariant(false));
4781 canvas->setSource(testFileUrl("unrequestedItems.qml"));
4785 qApp->processEvents();
4788 QQuickListView *leftview = findItem<QQuickListView>(canvas->rootObject(), "leftList");
4789 QTRY_VERIFY(leftview != 0);
4791 QQuickListView *rightview = findItem<QQuickListView>(canvas->rootObject(), "rightList");
4792 QTRY_VERIFY(rightview != 0);
4794 QQuickItem *leftContent = leftview->contentItem();
4795 QTRY_VERIFY(leftContent != 0);
4797 QQuickItem *rightContent = rightview->contentItem();
4798 QTRY_VERIFY(rightContent != 0);
4800 rightview->setCurrentIndex(20);
4802 QTRY_COMPARE(leftview->contentY(), 0.0);
4803 QTRY_COMPARE(rightview->contentY(), 100.0);
4807 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
4808 QCOMPARE(item->isVisible(), true);
4809 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
4810 QCOMPARE(item->isVisible(), false);
4812 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 19));
4813 QCOMPARE(item->isVisible(), false);
4814 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 19));
4815 QCOMPARE(item->isVisible(), true);
4817 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 16));
4818 QCOMPARE(item->isVisible(), true);
4819 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 17));
4820 QCOMPARE(item->isVisible(), false);
4821 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 3));
4822 QCOMPARE(item->isVisible(), false);
4823 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 4));
4824 QCOMPARE(item->isVisible(), true);
4826 rightview->setCurrentIndex(0);
4828 QTRY_COMPARE(leftview->contentY(), 0.0);
4829 QTRY_COMPARE(rightview->contentY(), 0.0);
4831 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
4832 QCOMPARE(item->isVisible(), true);
4833 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
4834 QTRY_COMPARE(item->isVisible(), true);
4836 QVERIFY(!findItem<QQuickItem>(leftContent, "wrapper", 19));
4837 QVERIFY(!findItem<QQuickItem>(rightContent, "wrapper", 19));
4839 leftview->setCurrentIndex(20);
4841 QTRY_COMPARE(leftview->contentY(), 100.0);
4842 QTRY_COMPARE(rightview->contentY(), 0.0);
4844 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
4845 QTRY_COMPARE(item->isVisible(), false);
4846 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
4847 QCOMPARE(item->isVisible(), true);
4849 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 19));
4850 QCOMPARE(item->isVisible(), true);
4851 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 19));
4852 QCOMPARE(item->isVisible(), false);
4854 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
4855 QCOMPARE(item->isVisible(), false);
4856 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4857 QCOMPARE(item->isVisible(), true);
4858 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4859 QCOMPARE(item->isVisible(), true);
4860 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4861 QCOMPARE(item->isVisible(), false);
4863 model.moveItems(19, 1, 1);
4864 QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4866 QTRY_VERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
4867 QCOMPARE(item->isVisible(), false);
4868 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
4869 QCOMPARE(item->isVisible(), true);
4871 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 19));
4872 QCOMPARE(item->isVisible(), true);
4873 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 19));
4874 QCOMPARE(item->isVisible(), false);
4876 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4877 QCOMPARE(item->isVisible(), false);
4878 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4879 QCOMPARE(item->isVisible(), true);
4880 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4881 QCOMPARE(item->isVisible(), true);
4882 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4883 QCOMPARE(item->isVisible(), false);
4885 model.moveItems(3, 4, 1);
4886 QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4888 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4889 QCOMPARE(item->isVisible(), false);
4890 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4891 QCOMPARE(item->isVisible(), true);
4892 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4893 QCOMPARE(item->isVisible(), true);
4894 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4895 QCOMPARE(item->isVisible(), false);
4897 model.moveItems(4, 3, 1);
4898 QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4900 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4901 QCOMPARE(item->isVisible(), false);
4902 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4903 QCOMPARE(item->isVisible(), true);
4904 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4905 QCOMPARE(item->isVisible(), true);
4906 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4907 QCOMPARE(item->isVisible(), false);
4909 model.moveItems(16, 17, 1);
4910 QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4912 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4913 QCOMPARE(item->isVisible(), false);
4914 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4915 QCOMPARE(item->isVisible(), true);
4916 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4917 QCOMPARE(item->isVisible(), true);
4918 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4919 QCOMPARE(item->isVisible(), false);
4921 model.moveItems(17, 16, 1);
4922 QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4924 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4925 QCOMPARE(item->isVisible(), false);
4926 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4927 QCOMPARE(item->isVisible(), true);
4928 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4929 QCOMPARE(item->isVisible(), true);
4930 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4931 QCOMPARE(item->isVisible(), false);
4936 QQuickItem *tst_QQuickListView::findVisibleChild(QQuickItem *parent, const QString &objectName)
4938 QQuickItem *item = 0;
4939 QList<QQuickItem*> items = parent->findChildren<QQuickItem*>(objectName);
4940 for (int i = 0; i < items.count(); ++i) {
4941 if (items.at(i)->isVisible()) {
4949 Find an item with the specified objectName. If index is supplied then the
4950 item must also evaluate the {index} expression equal to index
4952 template<typename T>
4953 T *tst_QQuickListView::findItem(QQuickItem *parent, const QString &objectName, int index)
4955 const QMetaObject &mo = T::staticMetaObject;
4956 //qDebug() << parent->childItems().count() << "children";
4957 for (int i = 0; i < parent->childItems().count(); ++i) {
4958 QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
4961 //qDebug() << "try" << item;
4962 if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
4964 QDeclarativeExpression e(qmlContext(item), item, "index");
4965 if (e.evaluate().toInt() == index)
4966 return static_cast<T*>(item);
4968 return static_cast<T*>(item);
4971 item = findItem<T>(item, objectName, index);
4973 return static_cast<T*>(item);
4979 template<typename T>
4980 QList<T*> tst_QQuickListView::findItems(QQuickItem *parent, const QString &objectName, bool visibleOnly)
4983 const QMetaObject &mo = T::staticMetaObject;
4984 //qDebug() << parent->childItems().count() << "children";
4985 for (int i = 0; i < parent->childItems().count(); ++i) {
4986 QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
4987 if (!item || (visibleOnly && !item->isVisible()))
4989 //qDebug() << "try" << item;
4990 if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName))
4991 items.append(static_cast<T*>(item));
4992 items += findItems<T>(item, objectName);
4998 void tst_QQuickListView::dumpTree(QQuickItem *parent, int depth)
5000 static QString padding(" ");
5001 for (int i = 0; i < parent->childItems().count(); ++i) {
5002 QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
5005 qDebug() << padding.left(depth*2) << item;
5006 dumpTree(item, depth+1);
5010 QTEST_MAIN(tst_QQuickListView)
5012 #include "tst_qquicklistview.moc"