1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the test suite of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
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_package_removed();
89 void qAbstractItemModel_removed();
91 void qListModelInterface_moved();
92 void qListModelInterface_moved_data();
93 void qListModelInterface_package_moved();
94 void qListModelInterface_package_moved_data();
95 void qAbstractItemModel_moved();
96 void qAbstractItemModel_moved_data();
98 void multipleChanges();
99 void multipleChanges_data();
101 void qListModelInterface_clear();
102 void qListModelInterface_package_clear();
103 void qAbstractItemModel_clear();
105 void insertBeforeVisible();
106 void insertBeforeVisible_data();
107 void swapWithFirstItem();
109 void currentIndex_delayedItemCreation();
110 void currentIndex_delayedItemCreation_data();
112 void noCurrentIndex();
114 void enforceRange_withoutHighlight();
116 void qListModelInterface_sections();
117 void qListModelInterface_package_sections();
118 void qAbstractItemModel_sections();
119 void sectionsPositioning();
120 void sectionsDelegate();
122 void positionViewAtIndex();
124 void propertyChanges();
125 void componentChanges();
127 void manualHighlight();
130 void header_delayItemCreation();
135 void resizeViewAndRepaint();
136 void sizeLessThan1();
138 void resizeDelegate();
139 void resizeFirstDelegate();
142 void incrementalModel();
146 void onRemove_data();
148 void test_mirroring();
150 void creationContext();
151 void snapToItem_data();
153 void snapOneItem_data();
161 void unrequestedVisibility();
164 template <class T> void items(const QUrl &source, bool forceLayout);
165 template <class T> void changed(const QUrl &source, bool forceLayout);
166 template <class T> void inserted(const QUrl &source);
167 template <class T> void inserted_more();
168 template <class T> void removed(const QUrl &source, bool animated);
169 template <class T> void moved(const QUrl &source);
170 template <class T> void clear(const QUrl &source);
171 template <class T> void sections(const QUrl &source);
172 QQuickView *createView();
173 void flick(QQuickView *canvas, const QPoint &from, const QPoint &to, int duration);
174 QQuickItem *findVisibleChild(QQuickItem *parent, const QString &objectName);
176 T *findItem(QQuickItem *parent, const QString &id, int index=-1);
178 QList<T*> findItems(QQuickItem *parent, const QString &objectName, bool visibleOnly = true);
179 void dumpTree(QQuickItem *parent, int depth = 0);
181 void inserted_more_data();
185 class TestObject : public QObject
189 Q_PROPERTY(bool error READ error WRITE setError NOTIFY changedError)
190 Q_PROPERTY(bool animate READ animate NOTIFY changedAnim)
191 Q_PROPERTY(bool invalidHighlight READ invalidHighlight NOTIFY changedHl)
192 Q_PROPERTY(int cacheBuffer READ cacheBuffer NOTIFY changedCacheBuffer)
195 TestObject(QObject *parent = 0)
196 : QObject(parent), mError(true), mAnimate(false), mInvalidHighlight(false)
199 bool error() const { return mError; }
200 void setError(bool err) { mError = err; emit changedError(); }
202 bool animate() const { return mAnimate; }
203 void setAnimate(bool anim) { mAnimate = anim; emit changedAnim(); }
205 bool invalidHighlight() const { return mInvalidHighlight; }
206 void setInvalidHighlight(bool invalid) { mInvalidHighlight = invalid; emit changedHl(); }
208 int cacheBuffer() const { return mCacheBuffer; }
209 void setCacheBuffer(int buffer) { mCacheBuffer = buffer; emit changedCacheBuffer(); }
215 void changedCacheBuffer();
220 bool mInvalidHighlight;
225 void tst_qquicklistview_move(int from, int to, int n, T *items)
228 // Only move forwards - flip if backwards moving
236 items->move(from, to);
240 typename T::ConstIterator it=items->begin(); it += from+n;
241 for (; i<to-from; ++i,++it)
242 replaced.append(*it);
244 it=items->begin(); it += from;
245 for (; i<n; ++i,++it)
246 replaced.append(*it);
247 typename T::ConstIterator f=replaced.begin();
248 typename T::Iterator t=items->begin(); t += from;
249 for (; f != replaced.end(); ++f, ++t)
254 class TestModel : public QListModelInterface
258 TestModel(QObject *parent = 0) : QListModelInterface(parent) {}
261 enum Roles { Name, Number };
263 QString name(int index) const { return list.at(index).first; }
264 QString number(int index) const { return list.at(index).second; }
266 int count() const { return list.count(); }
268 QList<int> roles() const { return QList<int>() << Name << Number; }
269 QString toString(int role) const {
280 QVariant data(int index, int role) const
283 return list.at(index).first;
285 return list.at(index).second;
288 QHash<int, QVariant> data(int index, const QList<int> &roles) const {
289 QHash<int,QVariant> returnHash;
291 for (int i = 0; i < roles.size(); ++i) {
292 int role = roles.at(i);
296 info = list.at(index).first;
299 info = list.at(index).second;
304 returnHash.insert(role, info);
309 void addItem(const QString &name, const QString &number) {
310 list.append(QPair<QString,QString>(name, number));
311 emit itemsInserted(list.count()-1, 1);
314 void insertItem(int index, const QString &name, const QString &number) {
315 list.insert(index, QPair<QString,QString>(name, number));
316 emit itemsInserted(index, 1);
319 void insertItems(int index, const QList<QPair<QString, QString> > &items) {
320 for (int i=0; i<items.count(); i++)
321 list.insert(index + i, QPair<QString,QString>(items[i].first, items[i].second));
322 emit itemsInserted(index, items.count());
325 void removeItem(int index) {
326 list.removeAt(index);
327 emit itemsRemoved(index, 1);
330 void removeItems(int index, int count) {
333 list.removeAt(index);
334 emit itemsRemoved(index, count);
337 void moveItem(int from, int to) {
339 emit itemsMoved(from, to, 1);
342 void moveItems(int from, int to, int count) {
343 tst_qquicklistview_move(from, to, count, &list);
344 emit itemsMoved(from, to, count);
347 void modifyItem(int index, const QString &name, const QString &number) {
348 list[index] = QPair<QString,QString>(name, number);
349 emit itemsChanged(index, 1, roles());
353 int count = list.count();
355 emit itemsRemoved(0, count);
359 QList<QPair<QString,QString> > list;
363 class TestModel2 : public QAbstractListModel
366 enum Roles { Name = Qt::UserRole+1, Number = Qt::UserRole+2 };
368 TestModel2(QObject *parent=0) : QAbstractListModel(parent) {
369 QHash<int, QByteArray> roles;
370 roles[Name] = "name";
371 roles[Number] = "number";
375 int rowCount(const QModelIndex &parent=QModelIndex()) const { Q_UNUSED(parent); return list.count(); }
376 QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const {
379 rv = list.at(index.row()).first;
380 else if (role == Number)
381 rv = list.at(index.row()).second;
386 int count() const { return rowCount(); }
387 QString name(int index) const { return list.at(index).first; }
388 QString number(int index) const { return list.at(index).second; }
390 void addItem(const QString &name, const QString &number) {
391 emit beginInsertRows(QModelIndex(), list.count(), list.count());
392 list.append(QPair<QString,QString>(name, number));
393 emit endInsertRows();
396 void addItems(const QList<QPair<QString, QString> > &items) {
397 emit beginInsertRows(QModelIndex(), list.count(), list.count()+items.count()-1);
398 for (int i=0; i<items.count(); i++)
399 list.append(QPair<QString,QString>(items[i].first, items[i].second));
400 emit endInsertRows();
403 void insertItem(int index, const QString &name, const QString &number) {
404 emit beginInsertRows(QModelIndex(), index, index);
405 list.insert(index, QPair<QString,QString>(name, number));
406 emit endInsertRows();
409 void insertItems(int index, const QList<QPair<QString, QString> > &items) {
410 emit beginInsertRows(QModelIndex(), index, index+items.count()-1);
411 for (int i=0; i<items.count(); i++)
412 list.insert(index + i, QPair<QString,QString>(items[i].first, items[i].second));
413 emit endInsertRows();
416 void removeItem(int index) {
417 emit beginRemoveRows(QModelIndex(), index, index);
418 list.removeAt(index);
419 emit endRemoveRows();
422 void removeItems(int index, int count) {
423 emit beginRemoveRows(QModelIndex(), index, index+count-1);
425 list.removeAt(index);
426 emit endRemoveRows();
429 void moveItem(int from, int to) {
430 emit beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
435 void moveItems(int from, int to, int count) {
436 emit beginMoveRows(QModelIndex(), from, from+count-1, QModelIndex(), to > from ? to+count : to);
437 tst_qquicklistview_move(from, to, count, &list);
441 void modifyItem(int idx, const QString &name, const QString &number) {
442 list[idx] = QPair<QString,QString>(name, number);
443 emit dataChanged(index(idx,0), index(idx,0));
447 int count = list.count();
448 emit beginRemoveRows(QModelIndex(), 0, count-1);
450 emit endRemoveRows();
454 QList<QPair<QString,QString> > list;
457 tst_QQuickListView::tst_QQuickListView()
462 void tst_QQuickListView::items(const QUrl &source, bool forceLayout)
464 QQuickView *canvas = createView();
467 model.addItem("Fred", "12345");
468 model.addItem("John", "2345");
469 model.addItem("Bob", "54321");
471 QDeclarativeContext *ctxt = canvas->rootContext();
472 ctxt->setContextProperty("testModel", &model);
474 TestObject *testObject = new TestObject;
475 ctxt->setContextProperty("testObject", testObject);
477 canvas->setSource(source);
478 qApp->processEvents();
480 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
481 QTRY_VERIFY(listview != 0);
483 QQuickItem *contentItem = listview->contentItem();
484 QTRY_VERIFY(contentItem != 0);
486 QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
487 QTRY_VERIFY(testObject->error() == false);
489 QTRY_VERIFY(listview->highlightItem() != 0);
490 QTRY_COMPARE(listview->count(), model.count());
491 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
492 QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
494 // current item should be first item
495 QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
497 for (int i = 0; i < model.count(); ++i) {
498 QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
499 QTRY_VERIFY(name != 0);
500 QTRY_COMPARE(name->text(), model.name(i));
501 QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
502 QTRY_VERIFY(number != 0);
503 QTRY_COMPARE(number->text(), model.number(i));
506 // switch to other delegate
507 testObject->setAnimate(true);
508 QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
509 QTRY_VERIFY(testObject->error() == false);
510 QTRY_VERIFY(listview->currentItem());
512 // set invalid highlight
513 testObject->setInvalidHighlight(true);
514 QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
515 QTRY_VERIFY(testObject->error() == false);
516 QTRY_VERIFY(listview->currentItem());
517 QTRY_VERIFY(listview->highlightItem() == 0);
519 // back to normal highlight
520 testObject->setInvalidHighlight(false);
521 QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
522 QTRY_VERIFY(testObject->error() == false);
523 QTRY_VERIFY(listview->currentItem());
524 QTRY_VERIFY(listview->highlightItem() != 0);
526 // set an empty model and confirm that items are destroyed
528 ctxt->setContextProperty("testModel", &model2);
530 // Force a layout, necessary if ListView is completed before VisualDataModel.
532 QCOMPARE(listview->property("count").toInt(), 0);
534 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
535 QTRY_VERIFY(itemCount == 0);
537 QTRY_COMPARE(listview->highlightResizeSpeed(), 1000.0);
538 QTRY_COMPARE(listview->highlightMoveSpeed(), 1000.0);
546 void tst_QQuickListView::changed(const QUrl &source, bool forceLayout)
548 QQuickView *canvas = createView();
551 model.addItem("Fred", "12345");
552 model.addItem("John", "2345");
553 model.addItem("Bob", "54321");
555 QDeclarativeContext *ctxt = canvas->rootContext();
556 ctxt->setContextProperty("testModel", &model);
558 TestObject *testObject = new TestObject;
559 ctxt->setContextProperty("testObject", testObject);
561 canvas->setSource(source);
562 qApp->processEvents();
564 QQuickFlickable *listview = findItem<QQuickFlickable>(canvas->rootObject(), "list");
565 QTRY_VERIFY(listview != 0);
567 QQuickItem *contentItem = listview->contentItem();
568 QTRY_VERIFY(contentItem != 0);
570 // Force a layout, necessary if ListView is completed before VisualDataModel.
572 QCOMPARE(listview->property("count").toInt(), model.count());
574 model.modifyItem(1, "Will", "9876");
575 QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
576 QTRY_VERIFY(name != 0);
577 QTRY_COMPARE(name->text(), model.name(1));
578 QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
579 QTRY_VERIFY(number != 0);
580 QTRY_COMPARE(number->text(), model.number(1));
587 void tst_QQuickListView::inserted(const QUrl &source)
589 QQuickView *canvas = createView();
593 model.addItem("Fred", "12345");
594 model.addItem("John", "2345");
595 model.addItem("Bob", "54321");
597 QDeclarativeContext *ctxt = canvas->rootContext();
598 ctxt->setContextProperty("testModel", &model);
600 TestObject *testObject = new TestObject;
601 ctxt->setContextProperty("testObject", testObject);
603 canvas->setSource(source);
604 //canvas->setSource(testFileUrl("listviewtest.qml")));
605 qApp->processEvents();
607 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
608 QTRY_VERIFY(listview != 0);
610 QQuickItem *contentItem = listview->contentItem();
611 QTRY_VERIFY(contentItem != 0);
613 model.insertItem(1, "Will", "9876");
615 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
616 QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
618 QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
619 QTRY_VERIFY(name != 0);
620 QTRY_COMPARE(name->text(), model.name(1));
621 QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
622 QTRY_VERIFY(number != 0);
623 QTRY_COMPARE(number->text(), model.number(1));
625 // Confirm items positioned correctly
626 for (int i = 0; i < model.count(); ++i) {
627 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
628 QTRY_COMPARE(item->y(), i*20.0);
631 model.insertItem(0, "Foo", "1111"); // zero index, and current item
633 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
634 QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
636 name = findItem<QQuickText>(contentItem, "textName", 0);
637 QTRY_VERIFY(name != 0);
638 QTRY_COMPARE(name->text(), model.name(0));
639 number = findItem<QQuickText>(contentItem, "textNumber", 0);
640 QTRY_VERIFY(number != 0);
641 QTRY_COMPARE(number->text(), model.number(0));
643 QTRY_COMPARE(listview->currentIndex(), 1);
645 // Confirm items positioned correctly
646 for (int i = 0; i < model.count(); ++i) {
647 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
648 QTRY_COMPARE(item->y(), i*20.0);
651 for (int i = model.count(); i < 30; ++i)
652 model.insertItem(i, "Hello", QString::number(i));
654 listview->setContentY(80);
656 // Insert item outside visible area
657 model.insertItem(1, "Hello", "1324");
659 QTRY_VERIFY(listview->contentY() == 80);
661 // Confirm items positioned correctly
662 for (int i = 5; i < 5+15; ++i) {
663 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
664 if (!item) qWarning() << "Item" << i << "not found";
666 QTRY_COMPARE(item->y(), i*20.0 - 20.0);
669 // QTRY_COMPARE(listview->contentItemHeight(), model.count() * 20.0);
673 model.insertItem(0, "Hello", "1234");
674 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
676 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
678 QCOMPARE(item->y(), 0.);
679 QVERIFY(listview->contentY() == 0);
686 void tst_QQuickListView::inserted_more()
688 QFETCH(qreal, contentY);
689 QFETCH(int, insertIndex);
690 QFETCH(int, insertCount);
691 QFETCH(qreal, itemsOffsetAfterMove);
695 QQuickView *canvas = createView();
699 for (int i = 0; i < 30; i++)
700 model.addItem("Item" + QString::number(i), "");
702 QDeclarativeContext *ctxt = canvas->rootContext();
703 ctxt->setContextProperty("testModel", &model);
705 TestObject *testObject = new TestObject;
706 ctxt->setContextProperty("testObject", testObject);
708 canvas->setSource(testFileUrl("listviewtest.qml"));
709 qApp->processEvents();
711 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
712 QTRY_VERIFY(listview != 0);
713 QQuickItem *contentItem = listview->contentItem();
714 QTRY_VERIFY(contentItem != 0);
716 listview->setContentY(contentY);
718 QList<QPair<QString, QString> > newData;
719 for (int i=0; i<insertCount; i++)
720 newData << qMakePair(QString("value %1").arg(i), QString::number(i));
721 model.insertItems(insertIndex, newData);
722 QTRY_COMPARE(listview->property("count").toInt(), model.count());
724 // check visibleItems.first() is in correct position
725 QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper", 0);
727 QCOMPARE(item0->y(), itemsOffsetAfterMove);
729 QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
730 int firstVisibleIndex = -1;
731 for (int i=0; i<items.count(); i++) {
732 if (items[i]->y() >= contentY) {
733 QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
734 firstVisibleIndex = e.evaluate().toInt();
738 QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
740 // Confirm items positioned correctly and indexes correct
741 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
742 for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
743 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
744 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
745 QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove);
746 name = findItem<QQuickText>(contentItem, "textName", i);
748 QTRY_COMPARE(name->text(), model.name(i));
749 number = findItem<QQuickText>(contentItem, "textNumber", i);
750 QVERIFY(number != 0);
751 QTRY_COMPARE(number->text(), model.number(i));
758 void tst_QQuickListView::inserted_more_data()
760 QTest::addColumn<qreal>("contentY");
761 QTest::addColumn<int>("insertIndex");
762 QTest::addColumn<int>("insertCount");
763 QTest::addColumn<qreal>("itemsOffsetAfterMove");
765 QTest::newRow("add 1, before visible items")
768 << -20.0; // insert above first visible i.e. 0 is at -20, first visible should not move
770 QTest::newRow("add multiple, before visible")
773 << -20.0 * 3; // again first visible should not move
775 QTest::newRow("add 1, at start of visible, content at start")
780 QTest::newRow("add multiple, start of visible, content at start")
785 QTest::newRow("add 1, at start of visible, content not at start")
790 QTest::newRow("add multiple, at start of visible, content not at start")
796 QTest::newRow("add 1, at end of visible, content at start")
801 QTest::newRow("add 1, at end of visible, content at start")
806 QTest::newRow("add 1, at end of visible, content not at start")
811 QTest::newRow("add multiple, at end of visible, content not at start")
817 QTest::newRow("add 1, after visible, content at start")
822 QTest::newRow("add 1, after visible, content at start")
827 QTest::newRow("add 1, after visible, content not at start")
832 QTest::newRow("add multiple, after visible, content not at start")
838 void tst_QQuickListView::insertBeforeVisible()
840 QFETCH(int, insertIndex);
841 QFETCH(int, insertCount);
842 QFETCH(int, cacheBuffer);
845 QQuickView *canvas = createView();
849 for (int i = 0; i < 30; i++)
850 model.addItem("Item" + QString::number(i), "");
852 QDeclarativeContext *ctxt = canvas->rootContext();
853 ctxt->setContextProperty("testModel", &model);
855 TestObject *testObject = new TestObject;
856 ctxt->setContextProperty("testObject", testObject);
858 canvas->setSource(testFileUrl("listviewtest.qml"));
859 qApp->processEvents();
861 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
862 QTRY_VERIFY(listview != 0);
863 QQuickItem *contentItem = listview->contentItem();
864 QTRY_VERIFY(contentItem != 0);
866 listview->setCacheBuffer(cacheBuffer);
868 // trigger a refill (not just setting contentY) so that the visibleItems grid is updated
869 int firstVisibleIndex = 20; // move to an index where the top item is not visible
870 listview->setContentY(firstVisibleIndex * 20.0);
871 listview->setCurrentIndex(firstVisibleIndex);
872 qApp->processEvents();
873 QTRY_COMPARE(listview->currentIndex(), firstVisibleIndex);
874 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", firstVisibleIndex);
876 QCOMPARE(item->y(), listview->contentY());
878 QList<QPair<QString, QString> > newData;
879 for (int i=0; i<insertCount; i++)
880 newData << qMakePair(QString("value %1").arg(i), QString::number(i));
881 model.insertItems(insertIndex, newData);
882 QTRY_COMPARE(listview->property("count").toInt(), model.count());
884 // now, moving to the top of the view should position the inserted items correctly
885 int itemsOffsetAfterMove = -(insertCount * 20);
886 listview->setCurrentIndex(0);
887 QTRY_COMPARE(listview->currentIndex(), 0);
888 QTRY_COMPARE(listview->contentY(), 0.0 + itemsOffsetAfterMove);
890 // Confirm items positioned correctly and indexes correct
891 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
892 for (int i = 0; i < model.count() && i < itemCount; ++i) {
893 item = findItem<QQuickItem>(contentItem, "wrapper", i);
894 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
895 QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove);
896 name = findItem<QQuickText>(contentItem, "textName", i);
898 QTRY_COMPARE(name->text(), model.name(i));
905 void tst_QQuickListView::insertBeforeVisible_data()
907 QTest::addColumn<int>("insertIndex");
908 QTest::addColumn<int>("insertCount");
909 QTest::addColumn<int>("cacheBuffer");
911 QTest::newRow("insert 1 at 0, 0 buffer") << 0 << 1 << 0;
912 QTest::newRow("insert 1 at 0, 100 buffer") << 0 << 1 << 100;
913 QTest::newRow("insert 1 at 0, 500 buffer") << 0 << 1 << 500;
915 QTest::newRow("insert 1 at 1, 0 buffer") << 1 << 1 << 0;
916 QTest::newRow("insert 1 at 1, 100 buffer") << 1 << 1 << 100;
917 QTest::newRow("insert 1 at 1, 500 buffer") << 1 << 1 << 500;
919 QTest::newRow("insert multiple at 0, 0 buffer") << 0 << 3 << 0;
920 QTest::newRow("insert multiple at 0, 100 buffer") << 0 << 3 << 100;
921 QTest::newRow("insert multiple at 0, 500 buffer") << 0 << 3 << 500;
923 QTest::newRow("insert multiple at 1, 0 buffer") << 1 << 3 << 0;
924 QTest::newRow("insert multiple at 1, 100 buffer") << 1 << 3 << 100;
925 QTest::newRow("insert multiple at 1, 500 buffer") << 1 << 3 << 500;
929 void tst_QQuickListView::removed(const QUrl &source, bool /* animated */)
931 QQuickView *canvas = createView();
934 for (int i = 0; i < 50; i++)
935 model.addItem("Item" + QString::number(i), "");
937 QDeclarativeContext *ctxt = canvas->rootContext();
938 ctxt->setContextProperty("testModel", &model);
940 TestObject *testObject = new TestObject;
941 ctxt->setContextProperty("testObject", testObject);
943 canvas->setSource(source);
945 qApp->processEvents();
947 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
948 QTRY_VERIFY(listview != 0);
950 QQuickItem *contentItem = listview->contentItem();
951 QTRY_VERIFY(contentItem != 0);
954 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
956 QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
957 QTRY_VERIFY(name != 0);
958 QTRY_COMPARE(name->text(), model.name(1));
959 QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
960 QTRY_VERIFY(number != 0);
961 QTRY_COMPARE(number->text(), model.number(1));
963 // Confirm items positioned correctly
964 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
965 for (int i = 0; i < model.count() && i < itemCount; ++i) {
966 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
967 if (!item) qWarning() << "Item" << i << "not found";
969 QTRY_VERIFY(item->y() == i*20);
972 // Remove first item (which is the current item);
973 model.removeItem(0); // post: top item starts at 20
974 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
976 name = findItem<QQuickText>(contentItem, "textName", 0);
977 QTRY_VERIFY(name != 0);
978 QTRY_COMPARE(name->text(), model.name(0));
979 number = findItem<QQuickText>(contentItem, "textNumber", 0);
980 QTRY_VERIFY(number != 0);
981 QTRY_COMPARE(number->text(), model.number(0));
983 // Confirm items positioned correctly
984 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
985 for (int i = 0; i < model.count() && i < itemCount; ++i) {
986 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
987 if (!item) qWarning() << "Item" << i << "not found";
989 QTRY_COMPARE(item->y(),i*20.0 + 20.0);
992 // Remove items not visible
993 model.removeItem(18);
994 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
996 // Confirm items positioned correctly
997 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
998 for (int i = 0; i < model.count() && i < itemCount; ++i) {
999 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1000 if (!item) qWarning() << "Item" << i << "not found";
1002 QTRY_COMPARE(item->y(),i*20.0+20.0);
1005 // Remove items before visible
1006 listview->setContentY(80);
1007 listview->setCurrentIndex(10);
1009 model.removeItem(1); // post: top item will be at 40
1010 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
1012 // Confirm items positioned correctly
1013 for (int i = 2; i < 18; ++i) {
1014 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1015 if (!item) qWarning() << "Item" << i << "not found";
1017 QTRY_COMPARE(item->y(),40+i*20.0);
1020 // Remove current index
1021 QTRY_VERIFY(listview->currentIndex() == 9);
1022 QQuickItem *oldCurrent = listview->currentItem();
1023 model.removeItem(9);
1025 QTRY_COMPARE(listview->currentIndex(), 9);
1026 QTRY_VERIFY(listview->currentItem() != oldCurrent);
1028 listview->setContentY(40); // That's the top now
1029 // let transitions settle.
1032 // Confirm items positioned correctly
1033 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1034 for (int i = 0; i < model.count() && i < itemCount; ++i) {
1035 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1036 if (!item) qWarning() << "Item" << i << "not found";
1038 QTRY_COMPARE(item->y(),40+i*20.0);
1041 // remove current item beyond visible items.
1042 listview->setCurrentIndex(20);
1043 listview->setContentY(40);
1044 model.removeItem(20);
1046 QTRY_COMPARE(listview->currentIndex(), 20);
1047 QTRY_VERIFY(listview->currentItem() != 0);
1049 // remove item before current, but visible
1050 listview->setCurrentIndex(8);
1051 oldCurrent = listview->currentItem();
1052 model.removeItem(6);
1054 QTRY_COMPARE(listview->currentIndex(), 7);
1055 QTRY_VERIFY(listview->currentItem() == oldCurrent);
1057 listview->setContentY(80);
1060 // remove all visible items
1061 model.removeItems(1, 18);
1062 QTRY_COMPARE(listview->count() , model.count());
1064 // Confirm items positioned correctly
1065 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1066 for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
1067 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i+1);
1068 if (!item) qWarning() << "Item" << i+1 << "not found";
1070 QTRY_COMPARE(item->y(),80+i*20.0);
1073 model.removeItems(1, 17);
1074 QTRY_COMPARE(listview->count() , model.count());
1076 model.removeItems(2, 1);
1077 QTRY_COMPARE(listview->count() , model.count());
1079 model.addItem("New", "1");
1080 QTRY_COMPARE(listview->count() , model.count());
1082 QTRY_VERIFY(name = findItem<QQuickText>(contentItem, "textName", model.count()-1));
1083 QCOMPARE(name->text(), QString("New"));
1085 // Add some more items so that we don't run out
1087 for (int i = 0; i < 50; i++)
1088 model.addItem("Item" + QString::number(i), "");
1090 // QTBUG-QTBUG-20575
1091 listview->setCurrentIndex(0);
1092 listview->setContentY(30);
1093 model.removeItem(0);
1094 QTRY_VERIFY(name = findItem<QQuickText>(contentItem, "textName", 0));
1096 // QTBUG-19198 move to end and remove all visible items one at a time.
1097 listview->positionViewAtEnd();
1098 for (int i = 0; i < 18; ++i)
1099 model.removeItems(model.count() - 1, 1);
1100 QTRY_VERIFY(findItems<QQuickItem>(contentItem, "wrapper").count() > 16);
1107 void tst_QQuickListView::clear(const QUrl &source)
1109 QQuickView *canvas = createView();
1112 for (int i = 0; i < 30; i++)
1113 model.addItem("Item" + QString::number(i), "");
1115 QDeclarativeContext *ctxt = canvas->rootContext();
1116 ctxt->setContextProperty("testModel", &model);
1118 TestObject *testObject = new TestObject;
1119 ctxt->setContextProperty("testObject", testObject);
1121 canvas->setSource(source);
1122 qApp->processEvents();
1124 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1125 QTRY_VERIFY(listview != 0);
1127 QQuickItem *contentItem = listview->contentItem();
1128 QTRY_VERIFY(contentItem != 0);
1132 QTRY_VERIFY(listview->count() == 0);
1133 QTRY_VERIFY(listview->currentItem() == 0);
1134 QTRY_VERIFY(listview->contentY() == 0);
1135 QVERIFY(listview->currentIndex() == -1);
1137 // confirm sanity when adding an item to cleared list
1138 model.addItem("New", "1");
1139 QTRY_VERIFY(listview->count() == 1);
1140 QVERIFY(listview->currentItem() != 0);
1141 QVERIFY(listview->currentIndex() == 0);
1148 void tst_QQuickListView::moved(const QUrl &source)
1150 QFETCH(qreal, contentY);
1154 QFETCH(qreal, itemsOffsetAfterMove);
1158 QQuickView *canvas = createView();
1162 for (int i = 0; i < 30; i++)
1163 model.addItem("Item" + QString::number(i), "");
1165 QDeclarativeContext *ctxt = canvas->rootContext();
1166 ctxt->setContextProperty("testModel", &model);
1168 TestObject *testObject = new TestObject;
1169 ctxt->setContextProperty("testObject", testObject);
1171 canvas->setSource(source);
1172 qApp->processEvents();
1174 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1175 QTRY_VERIFY(listview != 0);
1177 QQuickItem *contentItem = listview->contentItem();
1178 QTRY_VERIFY(contentItem != 0);
1180 QQuickItem *currentItem = listview->currentItem();
1181 QTRY_VERIFY(currentItem != 0);
1183 listview->setContentY(contentY);
1184 model.moveItems(from, to, count);
1186 // wait for items to move
1189 QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
1190 int firstVisibleIndex = -1;
1191 for (int i=0; i<items.count(); i++) {
1192 if (items[i]->y() >= contentY) {
1193 QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
1194 firstVisibleIndex = e.evaluate().toInt();
1198 QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
1200 // Confirm items positioned correctly and indexes correct
1201 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1202 for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
1203 if (i >= firstVisibleIndex + 16) // index has moved out of view
1205 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1206 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1207 QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove);
1208 name = findItem<QQuickText>(contentItem, "textName", i);
1210 QTRY_COMPARE(name->text(), model.name(i));
1211 number = findItem<QQuickText>(contentItem, "textNumber", i);
1212 QVERIFY(number != 0);
1213 QTRY_COMPARE(number->text(), model.number(i));
1215 // current index should have been updated
1216 if (item == currentItem)
1217 QTRY_COMPARE(listview->currentIndex(), i);
1224 void tst_QQuickListView::moved_data()
1226 QTest::addColumn<qreal>("contentY");
1227 QTest::addColumn<int>("from");
1228 QTest::addColumn<int>("to");
1229 QTest::addColumn<int>("count");
1230 QTest::addColumn<qreal>("itemsOffsetAfterMove");
1232 // model starts with 30 items, each 20px high, in area 320px high
1233 // 16 items should be visible at a time
1234 // itemsOffsetAfterMove should be > 0 whenever items above the visible pos have moved
1236 QTest::newRow("move 1 forwards, within visible items")
1241 QTest::newRow("move 1 forwards, from non-visible -> visible")
1242 << 80.0 // show 4-19
1244 << 20.0; // removed 1 item above the first visible, so item 0 should drop down by 1 to minimize movement
1246 QTest::newRow("move 1 forwards, from non-visible -> visible (move first item)")
1247 << 80.0 // show 4-19
1249 << 20.0; // first item has moved to below item4, everything drops down by size of 1 item
1251 QTest::newRow("move 1 forwards, from visible -> non-visible")
1256 QTest::newRow("move 1 forwards, from visible -> non-visible (move first item)")
1262 QTest::newRow("move 1 backwards, within visible items")
1267 QTest::newRow("move 1 backwards, within visible items (to first index)")
1272 QTest::newRow("move 1 backwards, from non-visible -> visible")
1277 QTest::newRow("move 1 backwards, from non-visible -> visible (move last item)")
1282 QTest::newRow("move 1 backwards, from visible -> non-visible")
1283 << 80.0 // show 4-19
1285 << -20.0; // to minimize movement, item 0 moves to -20, and other items do not move
1287 QTest::newRow("move 1 backwards, from visible -> non-visible (move first item)")
1288 << 80.0 // show 4-19
1290 << -20.0; // to minimize movement, item 16 (now at 0) moves to -20, and other items do not move
1293 QTest::newRow("move multiple forwards, within visible items")
1298 QTest::newRow("move multiple forwards, before visible items")
1299 << 140.0 // show 7-22
1300 << 4 << 5 << 3 // 4,5,6 move to below 7
1301 << 20.0 * 3; // 4,5,6 moved down
1303 QTest::newRow("move multiple forwards, from non-visible -> visible")
1304 << 80.0 // show 4-19
1306 << 20.0 * 3; // moving 3 from above the content y should adjust y positions accordingly
1308 QTest::newRow("move multiple forwards, from non-visible -> visible (move first item)")
1309 << 80.0 // show 4-19
1311 << 20.0 * 3; // moving 3 from above the content y should adjust y positions accordingly
1313 QTest::newRow("move multiple forwards, from visible -> non-visible")
1318 QTest::newRow("move multiple forwards, from visible -> non-visible (move first item)")
1324 QTest::newRow("move multiple backwards, within visible items")
1329 QTest::newRow("move multiple backwards, within visible items (move first item)")
1334 QTest::newRow("move multiple backwards, from non-visible -> visible")
1339 QTest::newRow("move multiple backwards, from non-visible -> visible (move last item)")
1344 QTest::newRow("move multiple backwards, from visible -> non-visible")
1345 << 80.0 // show 4-19
1347 << -20.0 * 3; // to minimize movement, 0 moves by -60, and other items do not move
1349 QTest::newRow("move multiple backwards, from visible -> non-visible (move first item)")
1350 << 80.0 // show 4-19
1352 << -20.0 * 3; // to minimize movement, 16,17,18 move to above item 0, and other items do not move
1357 enum { Inserted, Removed, Moved, SetCurrent } type;
1362 static ListChange insert(int index, int count = 1) { ListChange c = { Inserted, index, count, -1 }; return c; }
1363 static ListChange remove(int index, int count = 1) { ListChange c = { Removed, index, count, -1 }; return c; }
1364 static ListChange move(int index, int to, int count) { ListChange c = { Moved, index, count, to }; return c; }
1365 static ListChange setCurrent(int index) { ListChange c = { SetCurrent, index, -1, -1 }; return c; }
1367 Q_DECLARE_METATYPE(QList<ListChange>)
1369 void tst_QQuickListView::multipleChanges()
1371 QFETCH(int, startCount);
1372 QFETCH(QList<ListChange>, changes);
1373 QFETCH(int, newCount);
1374 QFETCH(int, newCurrentIndex);
1376 QQuickView *canvas = createView();
1380 for (int i = 0; i < startCount; i++)
1381 model.addItem("Item" + QString::number(i), "");
1383 QDeclarativeContext *ctxt = canvas->rootContext();
1384 ctxt->setContextProperty("testModel", &model);
1386 TestObject *testObject = new TestObject;
1387 ctxt->setContextProperty("testObject", testObject);
1389 canvas->setSource(testFileUrl("listviewtest.qml"));
1390 qApp->processEvents();
1392 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1393 QTRY_VERIFY(listview != 0);
1395 for (int i=0; i<changes.count(); i++) {
1396 switch (changes[i].type) {
1397 case ListChange::Inserted:
1399 QList<QPair<QString, QString> > items;
1400 for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
1401 items << qMakePair(QString("new item " + j), QString::number(j));
1402 model.insertItems(changes[i].index, items);
1405 case ListChange::Removed:
1406 model.removeItems(changes[i].index, changes[i].count);
1408 case ListChange::Moved:
1409 model.moveItems(changes[i].index, changes[i].to, changes[i].count);
1411 case ListChange::SetCurrent:
1412 listview->setCurrentIndex(changes[i].index);
1417 QTRY_COMPARE(listview->count(), newCount);
1418 QCOMPARE(listview->count(), model.count());
1419 QTRY_COMPARE(listview->currentIndex(), newCurrentIndex);
1423 QQuickItem *contentItem = listview->contentItem();
1424 QTRY_VERIFY(contentItem != 0);
1425 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1426 for (int i=0; i < model.count() && i < itemCount; ++i) {
1427 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1428 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1429 name = findItem<QQuickText>(contentItem, "textName", i);
1431 QTRY_COMPARE(name->text(), model.name(i));
1432 number = findItem<QQuickText>(contentItem, "textNumber", i);
1433 QVERIFY(number != 0);
1434 QTRY_COMPARE(number->text(), model.number(i));
1441 void tst_QQuickListView::multipleChanges_data()
1443 QTest::addColumn<int>("startCount");
1444 QTest::addColumn<QList<ListChange> >("changes");
1445 QTest::addColumn<int>("newCount");
1446 QTest::addColumn<int>("newCurrentIndex");
1448 QList<ListChange> changes;
1450 for (int i=1; i<30; i++)
1451 changes << ListChange::remove(0);
1452 QTest::newRow("remove all but 1, first->last") << 30 << changes << 1 << 0;
1454 changes << ListChange::remove(0);
1455 QTest::newRow("remove all") << 30 << changes << 0 << -1;
1458 changes << ListChange::setCurrent(29);
1459 for (int i=29; i>0; i--)
1460 changes << ListChange::remove(i);
1461 QTest::newRow("remove last (current) -> first") << 30 << changes << 1 << 0;
1463 QTest::newRow("remove then insert at 0") << 10 << (QList<ListChange>()
1464 << ListChange::remove(0, 1)
1465 << ListChange::insert(0, 1)
1468 QTest::newRow("remove then insert at non-zero index") << 10 << (QList<ListChange>()
1469 << ListChange::setCurrent(2)
1470 << ListChange::remove(2, 1)
1471 << ListChange::insert(2, 1)
1474 QTest::newRow("remove current then insert below it") << 10 << (QList<ListChange>()
1475 << ListChange::setCurrent(1)
1476 << ListChange::remove(1, 3)
1477 << ListChange::insert(2, 2)
1480 QTest::newRow("remove current index then move it down") << 10 << (QList<ListChange>()
1481 << ListChange::setCurrent(2)
1482 << ListChange::remove(1, 3)
1483 << ListChange::move(1, 5, 1)
1486 QTest::newRow("remove current index then move it up") << 10 << (QList<ListChange>()
1487 << ListChange::setCurrent(5)
1488 << ListChange::remove(4, 3)
1489 << ListChange::move(4, 1, 1)
1493 QTest::newRow("insert multiple times") << 0 << (QList<ListChange>()
1494 << ListChange::insert(0, 2)
1495 << ListChange::insert(0, 4)
1496 << ListChange::insert(0, 6)
1499 QTest::newRow("insert multiple times with current index changes") << 0 << (QList<ListChange>()
1500 << ListChange::insert(0, 2)
1501 << ListChange::insert(0, 4)
1502 << ListChange::insert(0, 6)
1503 << ListChange::setCurrent(3)
1504 << ListChange::insert(3, 2)
1507 QTest::newRow("insert and remove all") << 0 << (QList<ListChange>()
1508 << ListChange::insert(0, 30)
1509 << ListChange::remove(0, 30)
1512 QTest::newRow("insert and remove current") << 30 << (QList<ListChange>()
1513 << ListChange::insert(1)
1514 << ListChange::setCurrent(1)
1515 << ListChange::remove(1)
1518 QTest::newRow("insert before 0, then remove cross section of new and old items") << 10 << (QList<ListChange>()
1519 << ListChange::insert(0, 10)
1520 << ListChange::remove(5, 10)
1523 QTest::newRow("insert multiple, then move new items to end") << 10 << (QList<ListChange>()
1524 << ListChange::insert(0, 3)
1525 << ListChange::move(0, 10, 3)
1528 QTest::newRow("insert multiple, then move new and some old items to end") << 10 << (QList<ListChange>()
1529 << ListChange::insert(0, 3)
1530 << ListChange::move(0, 8, 5)
1533 QTest::newRow("insert multiple at end, then move new and some old items to start") << 10 << (QList<ListChange>()
1534 << ListChange::setCurrent(9)
1535 << ListChange::insert(10, 3)
1536 << ListChange::move(8, 0, 5)
1540 QTest::newRow("move back and forth to same index") << 10 << (QList<ListChange>()
1541 << ListChange::setCurrent(1)
1542 << ListChange::move(1, 2, 2)
1543 << ListChange::move(2, 1, 2)
1546 QTest::newRow("move forwards then back") << 10 << (QList<ListChange>()
1547 << ListChange::setCurrent(2)
1548 << ListChange::move(1, 2, 3)
1549 << ListChange::move(3, 0, 5)
1552 QTest::newRow("move current, then remove it") << 10 << (QList<ListChange>()
1553 << ListChange::setCurrent(5)
1554 << ListChange::move(5, 0, 1)
1555 << ListChange::remove(0)
1558 QTest::newRow("move current, then insert before it") << 10 << (QList<ListChange>()
1559 << ListChange::setCurrent(5)
1560 << ListChange::move(5, 0, 1)
1561 << ListChange::insert(0)
1564 QTest::newRow("move multiple, then remove them") << 10 << (QList<ListChange>()
1565 << ListChange::setCurrent(1)
1566 << ListChange::move(5, 1, 3)
1567 << ListChange::remove(1, 3)
1570 QTest::newRow("move multiple, then insert before them") << 10 << (QList<ListChange>()
1571 << ListChange::setCurrent(5)
1572 << ListChange::move(5, 1, 3)
1573 << ListChange::insert(1, 5)
1576 QTest::newRow("move multiple, then insert after them") << 10 << (QList<ListChange>()
1577 << ListChange::setCurrent(3)
1578 << ListChange::move(0, 1, 2)
1579 << ListChange::insert(3, 5)
1583 QTest::newRow("clear current") << 0 << (QList<ListChange>()
1584 << ListChange::insert(0, 5)
1585 << ListChange::setCurrent(-1)
1586 << ListChange::remove(0, 5)
1587 << ListChange::insert(0, 5)
1591 void tst_QQuickListView::swapWithFirstItem()
1593 QQuickView *canvas = createView();
1597 for (int i = 0; i < 30; i++)
1598 model.addItem("Item" + QString::number(i), "");
1600 QDeclarativeContext *ctxt = canvas->rootContext();
1601 ctxt->setContextProperty("testModel", &model);
1603 TestObject *testObject = new TestObject;
1604 ctxt->setContextProperty("testObject", testObject);
1606 canvas->setSource(testFileUrl("listviewtest.qml"));
1607 qApp->processEvents();
1609 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1610 QTRY_VERIFY(listview != 0);
1612 // ensure content position is stable
1613 listview->setContentY(0);
1614 model.moveItem(1, 0);
1615 QTRY_VERIFY(listview->contentY() == 0);
1621 void tst_QQuickListView::enforceRange()
1623 QQuickView *canvas = createView();
1626 for (int i = 0; i < 30; i++)
1627 model.addItem("Item" + QString::number(i), "");
1629 QDeclarativeContext *ctxt = canvas->rootContext();
1630 ctxt->setContextProperty("testModel", &model);
1632 canvas->setSource(testFileUrl("listview-enforcerange.qml"));
1633 qApp->processEvents();
1635 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1636 QTRY_VERIFY(listview != 0);
1638 QTRY_COMPARE(listview->preferredHighlightBegin(), 100.0);
1639 QTRY_COMPARE(listview->preferredHighlightEnd(), 100.0);
1640 QTRY_COMPARE(listview->highlightRangeMode(), QQuickListView::StrictlyEnforceRange);
1642 QQuickItem *contentItem = listview->contentItem();
1643 QTRY_VERIFY(contentItem != 0);
1645 // view should be positioned at the top of the range.
1646 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
1648 QTRY_COMPARE(listview->contentY(), -100.0);
1650 QQuickText *name = findItem<QQuickText>(contentItem, "textName", 0);
1651 QTRY_VERIFY(name != 0);
1652 QTRY_COMPARE(name->text(), model.name(0));
1653 QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 0);
1654 QTRY_VERIFY(number != 0);
1655 QTRY_COMPARE(number->text(), model.number(0));
1657 // Check currentIndex is updated when contentItem moves
1658 listview->setContentY(20);
1660 QTRY_COMPARE(listview->currentIndex(), 6);
1664 for (int i = 0; i < 5; i++)
1665 model2.addItem("Item" + QString::number(i), "");
1667 ctxt->setContextProperty("testModel", &model2);
1668 QCOMPARE(listview->count(), 5);
1673 void tst_QQuickListView::enforceRange_withoutHighlight()
1676 // If no highlight is set but StrictlyEnforceRange is used, the content should still move
1677 // to the correct position (i.e. to the next/previous item, not next/previous section)
1678 // when moving up/down via incrementCurrentIndex() and decrementCurrentIndex()
1680 QQuickView *canvas = createView();
1685 model.addItem("Item 0", "a");
1686 model.addItem("Item 1", "b");
1687 model.addItem("Item 2", "b");
1688 model.addItem("Item 3", "c");
1690 QDeclarativeContext *ctxt = canvas->rootContext();
1691 ctxt->setContextProperty("testModel", &model);
1693 canvas->setSource(testFileUrl("listview-enforcerange-nohighlight.qml"));
1694 qApp->processEvents();
1696 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1697 QTRY_VERIFY(listview != 0);
1699 qreal expectedPos = -100.0;
1701 expectedPos += 10.0; // scroll past 1st section's delegate (10px height)
1702 QTRY_COMPARE(listview->contentY(), expectedPos);
1704 expectedPos += 20 + 10; // scroll past 1st section and section delegate of 2nd section
1705 QTest::keyClick(canvas, Qt::Key_Down);
1707 QTRY_COMPARE(listview->contentY(), expectedPos);
1709 expectedPos += 20; // scroll past 1st item of 2nd section
1710 QTest::keyClick(canvas, Qt::Key_Down);
1711 QTRY_COMPARE(listview->contentY(), expectedPos);
1713 expectedPos += 20 + 10; // scroll past 2nd item of 2nd section and section delegate of 3rd section
1714 QTest::keyClick(canvas, Qt::Key_Down);
1715 QTRY_COMPARE(listview->contentY(), expectedPos);
1720 void tst_QQuickListView::spacing()
1722 QQuickView *canvas = createView();
1726 for (int i = 0; i < 30; i++)
1727 model.addItem("Item" + QString::number(i), "");
1729 QDeclarativeContext *ctxt = canvas->rootContext();
1730 ctxt->setContextProperty("testModel", &model);
1732 TestObject *testObject = new TestObject;
1733 ctxt->setContextProperty("testObject", testObject);
1735 canvas->setSource(testFileUrl("listviewtest.qml"));
1736 qApp->processEvents();
1738 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1739 QTRY_VERIFY(listview != 0);
1741 QQuickItem *contentItem = listview->contentItem();
1742 QTRY_VERIFY(contentItem != 0);
1744 // Confirm items positioned correctly
1745 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1746 for (int i = 0; i < model.count() && i < itemCount; ++i) {
1747 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1748 if (!item) qWarning() << "Item" << i << "not found";
1750 QTRY_VERIFY(item->y() == i*20);
1753 listview->setSpacing(10);
1754 QTRY_VERIFY(listview->spacing() == 10);
1756 // Confirm items positioned correctly
1757 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1758 for (int i = 0; i < model.count() && i < itemCount; ++i) {
1759 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1760 if (!item) qWarning() << "Item" << i << "not found";
1762 QTRY_VERIFY(item->y() == i*30);
1765 listview->setSpacing(0);
1767 // Confirm items positioned correctly
1768 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1769 for (int i = 0; i < model.count() && i < itemCount; ++i) {
1770 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1771 if (!item) qWarning() << "Item" << i << "not found";
1773 QTRY_COMPARE(item->y(), i*20.0);
1780 template <typename T>
1781 void tst_QQuickListView::sections(const QUrl &source)
1783 QQuickView *canvas = createView();
1787 for (int i = 0; i < 30; i++)
1788 model.addItem("Item" + QString::number(i), QString::number(i/5));
1790 QDeclarativeContext *ctxt = canvas->rootContext();
1791 ctxt->setContextProperty("testModel", &model);
1793 canvas->setSource(source);
1794 qApp->processEvents();
1796 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1797 QTRY_VERIFY(listview != 0);
1799 QQuickItem *contentItem = listview->contentItem();
1800 QTRY_VERIFY(contentItem != 0);
1802 // Confirm items positioned correctly
1803 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1804 for (int i = 0; i < model.count() && i < itemCount; ++i) {
1805 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1807 QTRY_COMPARE(item->y(), qreal(i*20 + ((i+4)/5) * 20));
1808 QQuickText *next = findItem<QQuickText>(item, "nextSection");
1809 QCOMPARE(next->text().toInt(), (i+1)/5);
1812 QSignalSpy currentSectionChangedSpy(listview, SIGNAL(currentSectionChanged()));
1814 // Remove section boundary
1815 model.removeItem(5);
1816 QTRY_COMPARE(listview->count(), model.count());
1818 // New section header created
1819 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 5);
1821 QTRY_COMPARE(item->height(), 40.0);
1823 model.insertItem(3, "New Item", "0");
1824 QTRY_COMPARE(listview->count(), model.count());
1826 // Section header moved
1827 item = findItem<QQuickItem>(contentItem, "wrapper", 5);
1829 QTRY_COMPARE(item->height(), 20.0);
1831 item = findItem<QQuickItem>(contentItem, "wrapper", 6);
1833 QTRY_COMPARE(item->height(), 40.0);
1835 // insert item which will become a section header
1836 model.insertItem(6, "Replace header", "1");
1837 QTRY_COMPARE(listview->count(), model.count());
1839 item = findItem<QQuickItem>(contentItem, "wrapper", 6);
1841 QTRY_COMPARE(item->height(), 40.0);
1843 item = findItem<QQuickItem>(contentItem, "wrapper", 7);
1845 QTRY_COMPARE(item->height(), 20.0);
1847 QTRY_COMPARE(listview->currentSection(), QString("0"));
1849 listview->setContentY(140);
1850 QTRY_COMPARE(listview->currentSection(), QString("1"));
1852 QTRY_COMPARE(currentSectionChangedSpy.count(), 1);
1854 listview->setContentY(20);
1855 QTRY_COMPARE(listview->currentSection(), QString("0"));
1857 QTRY_COMPARE(currentSectionChangedSpy.count(), 2);
1859 item = findItem<QQuickItem>(contentItem, "wrapper", 1);
1861 QTRY_COMPARE(item->height(), 20.0);
1863 // check that headers change when item changes
1864 listview->setContentY(0);
1865 model.modifyItem(0, "changed", "2");
1868 item = findItem<QQuickItem>(contentItem, "wrapper", 1);
1870 QTRY_COMPARE(item->height(), 40.0);
1875 void tst_QQuickListView::sectionsDelegate()
1877 QQuickView *canvas = createView();
1881 for (int i = 0; i < 30; i++)
1882 model.addItem("Item" + QString::number(i), QString::number(i/5));
1884 QDeclarativeContext *ctxt = canvas->rootContext();
1885 ctxt->setContextProperty("testModel", &model);
1887 canvas->setSource(testFileUrl("listview-sections_delegate.qml"));
1888 qApp->processEvents();
1890 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1891 QTRY_VERIFY(listview != 0);
1893 QQuickItem *contentItem = listview->contentItem();
1894 QTRY_VERIFY(contentItem != 0);
1896 // Confirm items positioned correctly
1897 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1898 for (int i = 0; i < model.count() && i < itemCount; ++i) {
1899 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1901 QTRY_COMPARE(item->y(), qreal(i*20 + ((i+5)/5) * 20));
1902 QQuickText *next = findItem<QQuickText>(item, "nextSection");
1903 QCOMPARE(next->text().toInt(), (i+1)/5);
1906 for (int i = 0; i < 3; ++i) {
1907 QQuickItem *item = findItem<QQuickItem>(contentItem, "sect_" + QString::number(i));
1909 QTRY_COMPARE(item->y(), qreal(i*20*6));
1912 model.modifyItem(0, "One", "aaa");
1913 model.modifyItem(1, "Two", "aaa");
1914 model.modifyItem(2, "Three", "aaa");
1915 model.modifyItem(3, "Four", "aaa");
1916 model.modifyItem(4, "Five", "aaa");
1919 for (int i = 0; i < 3; ++i) {
1920 QQuickItem *item = findItem<QQuickItem>(contentItem,
1921 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
1923 QTRY_COMPARE(item->y(), qreal(i*20*6));
1926 // remove section boundary
1927 model.removeItem(5);
1928 QTRY_COMPARE(listview->count(), model.count());
1929 for (int i = 0; i < 3; ++i) {
1930 QQuickItem *item = findItem<QQuickItem>(contentItem,
1931 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
1936 QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "sect_1");
1937 QCOMPARE(items.count(), 1);
1940 model.modifyItem(0, "One", "aaa");
1941 model.modifyItem(1, "One", "aaa");
1942 model.modifyItem(2, "One", "aaa");
1943 model.modifyItem(3, "Four", "aaa");
1944 model.modifyItem(4, "Four", "aaa");
1945 model.modifyItem(5, "Four", "aaa");
1946 model.modifyItem(6, "Five", "aaa");
1947 model.modifyItem(7, "Five", "aaa");
1948 model.modifyItem(8, "Five", "aaa");
1949 model.modifyItem(9, "Two", "aaa");
1950 model.modifyItem(10, "Two", "aaa");
1951 model.modifyItem(11, "Two", "aaa");
1952 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "sect_aaa").count(), 1);
1953 canvas->rootObject()->setProperty("sectionProperty", "name");
1954 // ensure view has settled.
1955 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "sect_Four").count(), 1);
1956 for (int i = 0; i < 4; ++i) {
1957 QQuickItem *item = findItem<QQuickItem>(contentItem,
1958 "sect_" + model.name(i*3));
1960 QTRY_COMPARE(item->y(), qreal(i*20*4));
1964 model.removeItems(10, 20);
1965 // ensure view has settled.
1966 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper").count(), 10);
1967 // Drag view up beyond bounds
1968 QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(20,20));
1970 QMouseEvent mv(QEvent::MouseMove, QPoint(20,0), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1971 QApplication::sendEvent(canvas, &mv);
1974 QMouseEvent mv(QEvent::MouseMove, QPoint(20,-50), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1975 QApplication::sendEvent(canvas, &mv);
1978 QMouseEvent mv(QEvent::MouseMove, QPoint(20,-200), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
1979 QApplication::sendEvent(canvas, &mv);
1981 QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(20,-200));
1982 // view should settle back at 0
1983 QTRY_COMPARE(listview->contentY(), 0.0);
1988 void tst_QQuickListView::sectionsPositioning()
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(testFileUrl("listview-sections_delegate.qml"));
2001 qApp->processEvents();
2002 canvas->rootObject()->setProperty("sectionPositioning", QVariant(int(QQuickViewSection::InlineLabels | QQuickViewSection::CurrentLabelAtStart | QQuickViewSection::NextLabelAtEnd)));
2004 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2005 QTRY_VERIFY(listview != 0);
2007 QQuickItem *contentItem = listview->contentItem();
2008 QTRY_VERIFY(contentItem != 0);
2010 for (int i = 0; i < 3; ++i) {
2011 QQuickItem *item = findItem<QQuickItem>(contentItem, "sect_" + QString::number(i));
2013 QTRY_COMPARE(item->y(), qreal(i*20*6));
2016 QQuickItem *topItem = findVisibleChild(contentItem, "sect_0"); // section header
2018 QCOMPARE(topItem->y(), 0.);
2020 QQuickItem *bottomItem = findVisibleChild(contentItem, "sect_3"); // section footer
2021 QVERIFY(bottomItem);
2022 QCOMPARE(bottomItem->y(), 300.);
2024 // move down a little and check that section header is at top
2025 listview->setContentY(10);
2026 QCOMPARE(topItem->y(), 0.);
2028 // push the top header up
2029 listview->setContentY(110);
2030 topItem = findVisibleChild(contentItem, "sect_0"); // section header
2032 QCOMPARE(topItem->y(), 100.);
2034 QQuickItem *item = findVisibleChild(contentItem, "sect_1");
2036 QCOMPARE(item->y(), 120.);
2038 bottomItem = findVisibleChild(contentItem, "sect_4"); // section footer
2039 QVERIFY(bottomItem);
2040 QCOMPARE(bottomItem->y(), 410.);
2042 // Move past section 0
2043 listview->setContentY(120);
2044 topItem = findVisibleChild(contentItem, "sect_0"); // section header
2047 // Push section footer down
2048 listview->setContentY(70);
2049 bottomItem = findVisibleChild(contentItem, "sect_4"); // section footer
2050 QVERIFY(bottomItem);
2051 QCOMPARE(bottomItem->y(), 380.);
2053 // Change current section
2054 listview->setContentY(10);
2055 model.modifyItem(0, "One", "aaa");
2056 model.modifyItem(1, "Two", "aaa");
2057 model.modifyItem(2, "Three", "aaa");
2058 model.modifyItem(3, "Four", "aaa");
2059 model.modifyItem(4, "Five", "aaa");
2062 QTRY_COMPARE(listview->currentSection(), QString("aaa"));
2064 for (int i = 0; i < 3; ++i) {
2065 QQuickItem *item = findItem<QQuickItem>(contentItem,
2066 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
2068 QTRY_COMPARE(item->y(), qreal(i*20*6));
2071 topItem = findVisibleChild(contentItem, "sect_aaa"); // section header
2073 QCOMPARE(topItem->y(), 10.);
2075 // remove section boundary
2076 listview->setContentY(120);
2077 model.removeItem(5);
2078 QTRY_COMPARE(listview->count(), model.count());
2079 for (int i = 0; i < 3; ++i) {
2080 QQuickItem *item = findItem<QQuickItem>(contentItem,
2081 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
2083 QTRY_COMPARE(item->y(), qreal(i*20*6));
2086 QVERIFY(topItem = findVisibleChild(contentItem, "sect_1"));
2087 QTRY_COMPARE(topItem->y(), 120.);
2089 // Change the next section
2090 listview->setContentY(0);
2091 bottomItem = findVisibleChild(contentItem, "sect_3"); // section footer
2092 QVERIFY(bottomItem);
2093 QTRY_COMPARE(bottomItem->y(), 300.);
2095 model.modifyItem(14, "New", "new");
2097 QTRY_VERIFY(bottomItem = findVisibleChild(contentItem, "sect_new")); // section footer
2098 QTRY_COMPARE(bottomItem->y(), 300.);
2100 // Turn sticky footer off
2101 listview->setContentY(40);
2102 canvas->rootObject()->setProperty("sectionPositioning", QVariant(int(QQuickViewSection::InlineLabels | QQuickViewSection::CurrentLabelAtStart)));
2103 item = findVisibleChild(contentItem, "sect_new"); // inline label restored
2105 QCOMPARE(item->y(), 360.);
2107 // Turn sticky header off
2108 listview->setContentY(30);
2109 canvas->rootObject()->setProperty("sectionPositioning", QVariant(int(QQuickViewSection::InlineLabels)));
2110 item = findVisibleChild(contentItem, "sect_aaa"); // inline label restored
2112 QCOMPARE(item->y(), 0.);
2117 void tst_QQuickListView::currentIndex_delayedItemCreation()
2119 QFETCH(bool, setCurrentToZero);
2121 QQuickView *canvas = createView();
2125 // test currentIndexChanged() is emitted even if currentIndex = 0 on start up
2126 // (since the currentItem will have changed and that shares the same index)
2127 canvas->rootContext()->setContextProperty("setCurrentToZero", setCurrentToZero);
2129 canvas->setSource(testFileUrl("fillModelOnComponentCompleted.qml"));
2130 qApp->processEvents();
2132 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2133 QTRY_VERIFY(listview != 0);
2135 QQuickItem *contentItem = listview->contentItem();
2136 QTRY_VERIFY(contentItem != 0);
2138 QSignalSpy spy(listview, SIGNAL(currentItemChanged()));
2139 QCOMPARE(listview->currentIndex(), 0);
2140 QTRY_COMPARE(spy.count(), 1);
2145 void tst_QQuickListView::currentIndex_delayedItemCreation_data()
2147 QTest::addColumn<bool>("setCurrentToZero");
2149 QTest::newRow("set to 0") << true;
2150 QTest::newRow("don't set to 0") << false;
2153 void tst_QQuickListView::currentIndex()
2156 for (int i = 0; i < 30; i++)
2157 model.addItem("Item" + QString::number(i), QString::number(i));
2159 QQuickView *canvas = new QQuickView(0);
2160 canvas->setGeometry(0,0,240,320);
2162 QDeclarativeContext *ctxt = canvas->rootContext();
2163 ctxt->setContextProperty("testModel", &model);
2164 ctxt->setContextProperty("testWrap", QVariant(false));
2166 QString filename(testFile("listview-initCurrent.qml"));
2167 canvas->setSource(QUrl::fromLocalFile(filename));
2169 qApp->processEvents();
2171 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2172 QTRY_VERIFY(listview != 0);
2174 QQuickItem *contentItem = listview->contentItem();
2175 QTRY_VERIFY(contentItem != 0);
2177 // current item should be 20th item at startup
2178 // and current item should be in view
2179 QCOMPARE(listview->currentIndex(), 20);
2180 QCOMPARE(listview->contentY(), 100.0);
2181 QCOMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 20));
2182 QCOMPARE(listview->highlightItem()->y(), listview->currentItem()->y());
2185 listview->setCurrentIndex(0);
2186 QCOMPARE(listview->currentIndex(), 0);
2187 // confirm that the velocity is updated
2188 QTRY_VERIFY(listview->verticalVelocity() != 0.0);
2190 listview->incrementCurrentIndex();
2191 QCOMPARE(listview->currentIndex(), 1);
2192 listview->decrementCurrentIndex();
2193 QCOMPARE(listview->currentIndex(), 0);
2195 listview->decrementCurrentIndex();
2196 QCOMPARE(listview->currentIndex(), 0);
2199 ctxt->setContextProperty("testWrap", QVariant(true));
2200 QVERIFY(listview->isWrapEnabled());
2202 listview->decrementCurrentIndex();
2203 QCOMPARE(listview->currentIndex(), model.count()-1);
2205 QTRY_COMPARE(listview->contentY(), 280.0);
2207 listview->incrementCurrentIndex();
2208 QCOMPARE(listview->currentIndex(), 0);
2210 QTRY_COMPARE(listview->contentY(), 0.0);
2213 // footer should become visible if it is out of view, and then current index is set to count-1
2214 canvas->rootObject()->setProperty("showFooter", true);
2215 QTRY_VERIFY(listview->footerItem());
2216 listview->setCurrentIndex(model.count()-2);
2217 QTRY_VERIFY(listview->footerItem()->y() > listview->contentY() + listview->height());
2218 listview->setCurrentIndex(model.count()-1);
2219 QTRY_COMPARE(listview->contentY() + listview->height(), (20.0 * model.count()) + listview->footerItem()->height());
2220 canvas->rootObject()->setProperty("showFooter", false);
2222 // header should become visible if it is out of view, and then current index is set to 0
2223 canvas->rootObject()->setProperty("showHeader", true);
2224 QTRY_VERIFY(listview->headerItem());
2225 listview->setCurrentIndex(1);
2226 QTRY_VERIFY(listview->headerItem()->y() + listview->headerItem()->height() < listview->contentY());
2227 listview->setCurrentIndex(0);
2228 QTRY_COMPARE(listview->contentY(), -listview->headerItem()->height());
2229 canvas->rootObject()->setProperty("showHeader", false);
2234 canvas->requestActivateWindow();
2235 QTest::qWaitForWindowShown(canvas);
2236 QTRY_VERIFY(qGuiApp->focusWindow() == canvas);
2238 listview->setCurrentIndex(0);
2240 QTest::keyClick(canvas, Qt::Key_Down);
2241 QCOMPARE(listview->currentIndex(), 1);
2243 QTest::keyClick(canvas, Qt::Key_Up);
2244 QCOMPARE(listview->currentIndex(), 0);
2246 // hold down Key_Down
2247 for (int i=0; i<model.count()-1; i++) {
2248 QTest::simulateEvent(canvas, true, Qt::Key_Down, Qt::NoModifier, "", true);
2249 QTRY_COMPARE(listview->currentIndex(), i+1);
2251 QTest::keyRelease(canvas, Qt::Key_Down);
2252 QTRY_COMPARE(listview->currentIndex(), model.count()-1);
2253 QTRY_COMPARE(listview->contentY(), 280.0);
2256 for (int i=model.count()-1; i > 0; i--) {
2257 QTest::simulateEvent(canvas, true, Qt::Key_Up, Qt::NoModifier, "", true);
2258 QTRY_COMPARE(listview->currentIndex(), i-1);
2260 QTest::keyRelease(canvas, Qt::Key_Up);
2261 QTRY_COMPARE(listview->currentIndex(), 0);
2262 QTRY_COMPARE(listview->contentY(), 0.0);
2265 // turn off auto highlight
2266 listview->setHighlightFollowsCurrentItem(false);
2267 QVERIFY(listview->highlightFollowsCurrentItem() == false);
2269 QVERIFY(listview->highlightItem());
2270 qreal hlPos = listview->highlightItem()->y();
2272 listview->setCurrentIndex(4);
2273 QTRY_COMPARE(listview->highlightItem()->y(), hlPos);
2275 // insert item before currentIndex
2276 listview->setCurrentIndex(28);
2277 model.insertItem(0, "Foo", "1111");
2278 QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29);
2280 // check removing highlight by setting currentIndex to -1;
2281 listview->setCurrentIndex(-1);
2283 QCOMPARE(listview->currentIndex(), -1);
2284 QVERIFY(!listview->highlightItem());
2285 QVERIFY(!listview->currentItem());
2290 void tst_QQuickListView::noCurrentIndex()
2293 for (int i = 0; i < 30; i++)
2294 model.addItem("Item" + QString::number(i), QString::number(i));
2296 QQuickView *canvas = new QQuickView(0);
2297 canvas->setGeometry(0,0,240,320);
2299 QDeclarativeContext *ctxt = canvas->rootContext();
2300 ctxt->setContextProperty("testModel", &model);
2302 QString filename(testFile("listview-noCurrent.qml"));
2303 canvas->setSource(QUrl::fromLocalFile(filename));
2305 qApp->processEvents();
2307 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2308 QTRY_VERIFY(listview != 0);
2310 QQuickItem *contentItem = listview->contentItem();
2311 QTRY_VERIFY(contentItem != 0);
2313 // current index should be -1 at startup
2314 // and we should not have a currentItem or highlightItem
2315 QCOMPARE(listview->currentIndex(), -1);
2316 QCOMPARE(listview->contentY(), 0.0);
2317 QVERIFY(!listview->highlightItem());
2318 QVERIFY(!listview->currentItem());
2320 listview->setCurrentIndex(2);
2321 QCOMPARE(listview->currentIndex(), 2);
2322 QVERIFY(listview->highlightItem());
2323 QVERIFY(listview->currentItem());
2328 void tst_QQuickListView::itemList()
2330 QQuickView *canvas = createView();
2332 canvas->setSource(testFileUrl("itemlist.qml"));
2333 qApp->processEvents();
2335 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "view");
2336 QTRY_VERIFY(listview != 0);
2338 QQuickItem *contentItem = listview->contentItem();
2339 QTRY_VERIFY(contentItem != 0);
2341 QQuickVisualItemModel *model = canvas->rootObject()->findChild<QQuickVisualItemModel*>("itemModel");
2342 QTRY_VERIFY(model != 0);
2344 QTRY_VERIFY(model->count() == 3);
2345 QTRY_COMPARE(listview->currentIndex(), 0);
2347 QQuickItem *item = findItem<QQuickItem>(contentItem, "item1");
2349 QTRY_COMPARE(item->x(), 0.0);
2350 QCOMPARE(item->height(), listview->height());
2352 QQuickText *text = findItem<QQuickText>(contentItem, "text1");
2354 QTRY_COMPARE(text->text(), QLatin1String("index: 0"));
2356 listview->setCurrentIndex(2);
2358 item = findItem<QQuickItem>(contentItem, "item3");
2360 QTRY_COMPARE(item->x(), 480.0);
2362 text = findItem<QQuickText>(contentItem, "text3");
2364 QTRY_COMPARE(text->text(), QLatin1String("index: 2"));
2369 void tst_QQuickListView::cacheBuffer()
2371 QQuickView *canvas = createView();
2374 for (int i = 0; i < 90; i++)
2375 model.addItem("Item" + QString::number(i), "");
2377 QDeclarativeContext *ctxt = canvas->rootContext();
2378 ctxt->setContextProperty("testModel", &model);
2380 TestObject *testObject = new TestObject;
2381 ctxt->setContextProperty("testObject", testObject);
2383 canvas->setSource(testFileUrl("listviewtest.qml"));
2385 qApp->processEvents();
2387 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2388 QTRY_VERIFY(listview != 0);
2390 QQuickItem *contentItem = listview->contentItem();
2391 QTRY_VERIFY(contentItem != 0);
2392 QTRY_VERIFY(listview->delegate() != 0);
2393 QTRY_VERIFY(listview->model() != 0);
2394 QTRY_VERIFY(listview->highlight() != 0);
2396 // Confirm items positioned correctly
2397 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2398 for (int i = 0; i < model.count() && i < itemCount; ++i) {
2399 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2400 if (!item) qWarning() << "Item" << i << "not found";
2402 QTRY_VERIFY(item->y() == i*20);
2405 QDeclarativeIncubationController controller;
2406 canvas->engine()->setIncubationController(&controller);
2408 testObject->setCacheBuffer(200);
2409 QTRY_VERIFY(listview->cacheBuffer() == 200);
2411 // items will be created one at a time
2412 for (int i = itemCount; i < qMin(itemCount+10,model.count()); ++i) {
2413 QVERIFY(findItem<QQuickItem>(listview, "wrapper", i) == 0);
2414 QQuickItem *item = 0;
2417 controller.incubateWhile(&b);
2418 item = findItem<QQuickItem>(listview, "wrapper", i);
2424 controller.incubateWhile(&b);
2427 int newItemCount = 0;
2428 newItemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2430 // Confirm items positioned correctly
2431 for (int i = 0; i < model.count() && i < newItemCount; ++i) {
2432 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2433 if (!item) qWarning() << "Item" << i << "not found";
2435 QTRY_VERIFY(item->y() == i*20);
2438 // move view and confirm items in view are visible immediately and outside are created async
2439 listview->setContentY(300);
2441 for (int i = 15; i < 32; ++i) {
2442 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2443 if (!item) qWarning() << "Item" << i << "not found";
2445 QVERIFY(item->y() == i*20);
2448 QVERIFY(findItem<QQuickItem>(listview, "wrapper", 32) == 0);
2450 // ensure buffered items are created
2451 for (int i = 32; i < qMin(41,model.count()); ++i) {
2452 QQuickItem *item = 0;
2454 qGuiApp->processEvents(); // allow refill to happen
2456 controller.incubateWhile(&b);
2457 item = findItem<QQuickItem>(listview, "wrapper", i);
2463 controller.incubateWhile(&b);
2470 void tst_QQuickListView::positionViewAtIndex()
2472 QQuickView *canvas = createView();
2475 for (int i = 0; i < 40; i++)
2476 model.addItem("Item" + QString::number(i), "");
2478 QDeclarativeContext *ctxt = canvas->rootContext();
2479 ctxt->setContextProperty("testModel", &model);
2481 TestObject *testObject = new TestObject;
2482 ctxt->setContextProperty("testObject", testObject);
2484 canvas->setSource(testFileUrl("listviewtest.qml"));
2485 qApp->processEvents();
2487 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2488 QTRY_VERIFY(listview != 0);
2490 QQuickItem *contentItem = listview->contentItem();
2491 QTRY_VERIFY(contentItem != 0);
2493 // Confirm items positioned correctly
2494 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2495 for (int i = 0; i < model.count() && i < itemCount; ++i) {
2496 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2497 if (!item) qWarning() << "Item" << i << "not found";
2499 QTRY_COMPARE(item->y(), i*20.);
2502 // Position on a currently visible item
2503 listview->positionViewAtIndex(3, QQuickListView::Beginning);
2504 QTRY_COMPARE(listview->contentY(), 60.);
2506 // Confirm items positioned correctly
2507 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2508 for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
2509 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2510 if (!item) qWarning() << "Item" << i << "not found";
2512 QTRY_COMPARE(item->y(), i*20.);
2515 // Position on an item beyond the visible items
2516 listview->positionViewAtIndex(22, QQuickListView::Beginning);
2517 QTRY_COMPARE(listview->contentY(), 440.);
2519 // Confirm items positioned correctly
2520 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2521 for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
2522 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2523 if (!item) qWarning() << "Item" << i << "not found";
2525 QTRY_COMPARE(item->y(), i*20.);
2528 // Position on an item that would leave empty space if positioned at the top
2529 listview->positionViewAtIndex(28, QQuickListView::Beginning);
2530 QTRY_COMPARE(listview->contentY(), 480.);
2532 // Confirm items positioned correctly
2533 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2534 for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
2535 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2536 if (!item) qWarning() << "Item" << i << "not found";
2538 QTRY_COMPARE(item->y(), i*20.);
2541 // Position at the beginning again
2542 listview->positionViewAtIndex(0, QQuickListView::Beginning);
2543 QTRY_COMPARE(listview->contentY(), 0.);
2545 // Confirm items positioned correctly
2546 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2547 for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2548 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2549 if (!item) qWarning() << "Item" << i << "not found";
2551 QTRY_COMPARE(item->y(), i*20.);
2554 // Position at End using last index
2555 listview->positionViewAtIndex(model.count()-1, QQuickListView::End);
2556 QTRY_COMPARE(listview->contentY(), 480.);
2558 // Confirm items positioned correctly
2559 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2560 for (int i = 24; i < model.count(); ++i) {
2561 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2562 if (!item) qWarning() << "Item" << i << "not found";
2564 QTRY_COMPARE(item->y(), i*20.);
2568 listview->positionViewAtIndex(20, QQuickListView::End);
2569 QTRY_COMPARE(listview->contentY(), 100.);
2571 // Position in Center
2572 listview->positionViewAtIndex(15, QQuickListView::Center);
2573 QTRY_COMPARE(listview->contentY(), 150.);
2575 // Ensure at least partially visible
2576 listview->positionViewAtIndex(15, QQuickListView::Visible);
2577 QTRY_COMPARE(listview->contentY(), 150.);
2579 listview->setContentY(302);
2580 listview->positionViewAtIndex(15, QQuickListView::Visible);
2581 QTRY_COMPARE(listview->contentY(), 302.);
2583 listview->setContentY(320);
2584 listview->positionViewAtIndex(15, QQuickListView::Visible);
2585 QTRY_COMPARE(listview->contentY(), 300.);
2587 listview->setContentY(85);
2588 listview->positionViewAtIndex(20, QQuickListView::Visible);
2589 QTRY_COMPARE(listview->contentY(), 85.);
2591 listview->setContentY(75);
2592 listview->positionViewAtIndex(20, QQuickListView::Visible);
2593 QTRY_COMPARE(listview->contentY(), 100.);
2595 // Ensure completely visible
2596 listview->setContentY(120);
2597 listview->positionViewAtIndex(20, QQuickListView::Contain);
2598 QTRY_COMPARE(listview->contentY(), 120.);
2600 listview->setContentY(302);
2601 listview->positionViewAtIndex(15, QQuickListView::Contain);
2602 QTRY_COMPARE(listview->contentY(), 300.);
2604 listview->setContentY(85);
2605 listview->positionViewAtIndex(20, QQuickListView::Contain);
2606 QTRY_COMPARE(listview->contentY(), 100.);
2608 // positionAtBeginnging
2609 listview->positionViewAtBeginning();
2610 QTRY_COMPARE(listview->contentY(), 0.);
2612 listview->setContentY(80);
2613 canvas->rootObject()->setProperty("showHeader", true);
2614 listview->positionViewAtBeginning();
2615 QTRY_COMPARE(listview->contentY(), -30.);
2618 listview->positionViewAtEnd();
2619 QTRY_COMPARE(listview->contentY(), 480.); // 40*20 - 320
2621 listview->setContentY(80);
2622 canvas->rootObject()->setProperty("showFooter", true);
2623 listview->positionViewAtEnd();
2624 QTRY_COMPARE(listview->contentY(), 510.);
2626 // set current item to outside visible view, position at beginning
2627 // and ensure highlight moves to current item
2628 listview->setCurrentIndex(1);
2629 listview->positionViewAtBeginning();
2630 QTRY_COMPARE(listview->contentY(), -30.);
2631 QVERIFY(listview->highlightItem());
2632 QCOMPARE(listview->highlightItem()->y(), 20.);
2638 void tst_QQuickListView::resetModel()
2640 QQuickView *canvas = createView();
2642 QStringList strings;
2643 strings << "one" << "two" << "three";
2644 QStringListModel model(strings);
2646 QDeclarativeContext *ctxt = canvas->rootContext();
2647 ctxt->setContextProperty("testModel", &model);
2649 canvas->setSource(testFileUrl("displaylist.qml"));
2650 qApp->processEvents();
2652 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2653 QTRY_VERIFY(listview != 0);
2655 QQuickItem *contentItem = listview->contentItem();
2656 QTRY_VERIFY(contentItem != 0);
2658 QTRY_COMPARE(listview->count(), model.rowCount());
2660 for (int i = 0; i < model.rowCount(); ++i) {
2661 QQuickText *display = findItem<QQuickText>(contentItem, "displayText", i);
2662 QTRY_VERIFY(display != 0);
2663 QTRY_COMPARE(display->text(), strings.at(i));
2667 strings << "four" << "five" << "six" << "seven";
2668 model.setStringList(strings);
2670 QTRY_COMPARE(listview->count(), model.rowCount());
2672 for (int i = 0; i < model.rowCount(); ++i) {
2673 QQuickText *display = findItem<QQuickText>(contentItem, "displayText", i);
2674 QTRY_VERIFY(display != 0);
2675 QTRY_COMPARE(display->text(), strings.at(i));
2681 void tst_QQuickListView::propertyChanges()
2683 QQuickView *canvas = createView();
2684 QTRY_VERIFY(canvas);
2685 canvas->setSource(testFileUrl("propertychangestest.qml"));
2687 QQuickListView *listView = canvas->rootObject()->findChild<QQuickListView*>("listView");
2688 QTRY_VERIFY(listView);
2690 QSignalSpy highlightFollowsCurrentItemSpy(listView, SIGNAL(highlightFollowsCurrentItemChanged()));
2691 QSignalSpy preferredHighlightBeginSpy(listView, SIGNAL(preferredHighlightBeginChanged()));
2692 QSignalSpy preferredHighlightEndSpy(listView, SIGNAL(preferredHighlightEndChanged()));
2693 QSignalSpy highlightRangeModeSpy(listView, SIGNAL(highlightRangeModeChanged()));
2694 QSignalSpy keyNavigationWrapsSpy(listView, SIGNAL(keyNavigationWrapsChanged()));
2695 QSignalSpy cacheBufferSpy(listView, SIGNAL(cacheBufferChanged()));
2696 QSignalSpy snapModeSpy(listView, SIGNAL(snapModeChanged()));
2698 QTRY_COMPARE(listView->highlightFollowsCurrentItem(), true);
2699 QTRY_COMPARE(listView->preferredHighlightBegin(), 0.0);
2700 QTRY_COMPARE(listView->preferredHighlightEnd(), 0.0);
2701 QTRY_COMPARE(listView->highlightRangeMode(), QQuickListView::ApplyRange);
2702 QTRY_COMPARE(listView->isWrapEnabled(), true);
2703 QTRY_COMPARE(listView->cacheBuffer(), 10);
2704 QTRY_COMPARE(listView->snapMode(), QQuickListView::SnapToItem);
2706 listView->setHighlightFollowsCurrentItem(false);
2707 listView->setPreferredHighlightBegin(1.0);
2708 listView->setPreferredHighlightEnd(1.0);
2709 listView->setHighlightRangeMode(QQuickListView::StrictlyEnforceRange);
2710 listView->setWrapEnabled(false);
2711 listView->setCacheBuffer(3);
2712 listView->setSnapMode(QQuickListView::SnapOneItem);
2714 QTRY_COMPARE(listView->highlightFollowsCurrentItem(), false);
2715 QTRY_COMPARE(listView->preferredHighlightBegin(), 1.0);
2716 QTRY_COMPARE(listView->preferredHighlightEnd(), 1.0);
2717 QTRY_COMPARE(listView->highlightRangeMode(), QQuickListView::StrictlyEnforceRange);
2718 QTRY_COMPARE(listView->isWrapEnabled(), false);
2719 QTRY_COMPARE(listView->cacheBuffer(), 3);
2720 QTRY_COMPARE(listView->snapMode(), QQuickListView::SnapOneItem);
2722 QTRY_COMPARE(highlightFollowsCurrentItemSpy.count(),1);
2723 QTRY_COMPARE(preferredHighlightBeginSpy.count(),1);
2724 QTRY_COMPARE(preferredHighlightEndSpy.count(),1);
2725 QTRY_COMPARE(highlightRangeModeSpy.count(),1);
2726 QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
2727 QTRY_COMPARE(cacheBufferSpy.count(),1);
2728 QTRY_COMPARE(snapModeSpy.count(),1);
2730 listView->setHighlightFollowsCurrentItem(false);
2731 listView->setPreferredHighlightBegin(1.0);
2732 listView->setPreferredHighlightEnd(1.0);
2733 listView->setHighlightRangeMode(QQuickListView::StrictlyEnforceRange);
2734 listView->setWrapEnabled(false);
2735 listView->setCacheBuffer(3);
2736 listView->setSnapMode(QQuickListView::SnapOneItem);
2738 QTRY_COMPARE(highlightFollowsCurrentItemSpy.count(),1);
2739 QTRY_COMPARE(preferredHighlightBeginSpy.count(),1);
2740 QTRY_COMPARE(preferredHighlightEndSpy.count(),1);
2741 QTRY_COMPARE(highlightRangeModeSpy.count(),1);
2742 QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
2743 QTRY_COMPARE(cacheBufferSpy.count(),1);
2744 QTRY_COMPARE(snapModeSpy.count(),1);
2749 void tst_QQuickListView::componentChanges()
2751 QQuickView *canvas = createView();
2752 QTRY_VERIFY(canvas);
2753 canvas->setSource(testFileUrl("propertychangestest.qml"));
2755 QQuickListView *listView = canvas->rootObject()->findChild<QQuickListView*>("listView");
2756 QTRY_VERIFY(listView);
2758 QDeclarativeComponent component(canvas->engine());
2759 component.setData("import QtQuick 2.0; Rectangle { color: \"blue\"; }", QUrl::fromLocalFile(""));
2761 QDeclarativeComponent delegateComponent(canvas->engine());
2762 delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
2764 QSignalSpy highlightSpy(listView, SIGNAL(highlightChanged()));
2765 QSignalSpy delegateSpy(listView, SIGNAL(delegateChanged()));
2766 QSignalSpy headerSpy(listView, SIGNAL(headerChanged()));
2767 QSignalSpy footerSpy(listView, SIGNAL(footerChanged()));
2769 listView->setHighlight(&component);
2770 listView->setHeader(&component);
2771 listView->setFooter(&component);
2772 listView->setDelegate(&delegateComponent);
2774 QTRY_COMPARE(listView->highlight(), &component);
2775 QTRY_COMPARE(listView->header(), &component);
2776 QTRY_COMPARE(listView->footer(), &component);
2777 QTRY_COMPARE(listView->delegate(), &delegateComponent);
2779 QTRY_COMPARE(highlightSpy.count(),1);
2780 QTRY_COMPARE(delegateSpy.count(),1);
2781 QTRY_COMPARE(headerSpy.count(),1);
2782 QTRY_COMPARE(footerSpy.count(),1);
2784 listView->setHighlight(&component);
2785 listView->setHeader(&component);
2786 listView->setFooter(&component);
2787 listView->setDelegate(&delegateComponent);
2789 QTRY_COMPARE(highlightSpy.count(),1);
2790 QTRY_COMPARE(delegateSpy.count(),1);
2791 QTRY_COMPARE(headerSpy.count(),1);
2792 QTRY_COMPARE(footerSpy.count(),1);
2797 void tst_QQuickListView::modelChanges()
2799 QQuickView *canvas = createView();
2800 QTRY_VERIFY(canvas);
2801 canvas->setSource(testFileUrl("propertychangestest.qml"));
2803 QQuickListView *listView = canvas->rootObject()->findChild<QQuickListView*>("listView");
2804 QTRY_VERIFY(listView);
2806 QDeclarativeListModel *alternateModel = canvas->rootObject()->findChild<QDeclarativeListModel*>("alternateModel");
2807 QTRY_VERIFY(alternateModel);
2808 QVariant modelVariant = QVariant::fromValue<QObject *>(alternateModel);
2809 QSignalSpy modelSpy(listView, SIGNAL(modelChanged()));
2811 listView->setModel(modelVariant);
2812 QTRY_COMPARE(listView->model(), modelVariant);
2813 QTRY_COMPARE(modelSpy.count(),1);
2815 listView->setModel(modelVariant);
2816 QTRY_COMPARE(modelSpy.count(),1);
2818 listView->setModel(QVariant());
2819 QTRY_COMPARE(modelSpy.count(),2);
2824 void tst_QQuickListView::QTBUG_9791()
2826 QQuickView *canvas = createView();
2828 canvas->setSource(testFileUrl("strictlyenforcerange.qml"));
2829 qApp->processEvents();
2831 QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
2832 QTRY_VERIFY(listview != 0);
2834 QQuickItem *contentItem = listview->contentItem();
2835 QTRY_VERIFY(contentItem != 0);
2836 QTRY_VERIFY(listview->delegate() != 0);
2837 QTRY_VERIFY(listview->model() != 0);
2839 QMetaObject::invokeMethod(listview, "fillModel");
2840 qApp->processEvents();
2842 // Confirm items positioned correctly
2843 int itemCount = findItems<QQuickItem>(contentItem, "wrapper", false).count();
2844 QCOMPARE(itemCount, 3);
2846 for (int i = 0; i < itemCount; ++i) {
2847 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2848 if (!item) qWarning() << "Item" << i << "not found";
2850 QTRY_COMPARE(item->x(), i*300.0);
2853 // check that view is positioned correctly
2854 QTRY_COMPARE(listview->contentX(), 590.0);
2859 void tst_QQuickListView::manualHighlight()
2861 QQuickView *canvas = new QQuickView(0);
2862 canvas->setGeometry(0,0,240,320);
2864 QString filename(testFile("manual-highlight.qml"));
2865 canvas->setSource(QUrl::fromLocalFile(filename));
2867 qApp->processEvents();
2869 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2870 QTRY_VERIFY(listview != 0);
2872 QQuickItem *contentItem = listview->contentItem();
2873 QTRY_VERIFY(contentItem != 0);
2875 QTRY_COMPARE(listview->currentIndex(), 0);
2876 QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
2877 QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
2879 listview->setCurrentIndex(2);
2881 QTRY_COMPARE(listview->currentIndex(), 2);
2882 QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 2));
2883 QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
2886 listview->positionViewAtIndex(3, QQuickListView::Contain);
2888 QTRY_COMPARE(listview->currentIndex(), 2);
2889 QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 2));
2890 QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
2895 void tst_QQuickListView::QTBUG_11105()
2897 QQuickView *canvas = createView();
2900 for (int i = 0; i < 30; i++)
2901 model.addItem("Item" + QString::number(i), "");
2903 QDeclarativeContext *ctxt = canvas->rootContext();
2904 ctxt->setContextProperty("testModel", &model);
2906 TestObject *testObject = new TestObject;
2907 ctxt->setContextProperty("testObject", testObject);
2909 canvas->setSource(testFileUrl("listviewtest.qml"));
2910 qApp->processEvents();
2912 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2913 QTRY_VERIFY(listview != 0);
2915 QQuickItem *contentItem = listview->contentItem();
2916 QTRY_VERIFY(contentItem != 0);
2918 // Confirm items positioned correctly
2919 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2920 for (int i = 0; i < model.count() && i < itemCount; ++i) {
2921 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2922 if (!item) qWarning() << "Item" << i << "not found";
2924 QTRY_VERIFY(item->y() == i*20);
2927 listview->positionViewAtIndex(20, QQuickListView::Beginning);
2928 QCOMPARE(listview->contentY(), 280.);
2931 for (int i = 0; i < 5; i++)
2932 model2.addItem("Item" + QString::number(i), "");
2934 ctxt->setContextProperty("testModel", &model2);
2936 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2937 QCOMPARE(itemCount, 5);
2943 void tst_QQuickListView::header()
2945 QFETCH(QQuickListView::Orientation, orientation);
2946 QFETCH(Qt::LayoutDirection, layoutDirection);
2947 QFETCH(QPointF, initialHeaderPos);
2948 QFETCH(QPointF, firstDelegatePos);
2949 QFETCH(QPointF, initialContentPos);
2950 QFETCH(QPointF, changedHeaderPos);
2951 QFETCH(QPointF, changedContentPos);
2952 QFETCH(QPointF, resizeContentPos);
2955 for (int i = 0; i < 30; i++)
2956 model.addItem("Item" + QString::number(i), "");
2958 QQuickView *canvas = createView();
2959 canvas->rootContext()->setContextProperty("testModel", &model);
2960 canvas->rootContext()->setContextProperty("initialViewWidth", 240);
2961 canvas->rootContext()->setContextProperty("initialViewHeight", 320);
2962 canvas->setSource(testFileUrl("header.qml"));
2964 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2965 QTRY_VERIFY(listview != 0);
2966 listview->setOrientation(orientation);
2967 listview->setLayoutDirection(layoutDirection);
2969 QQuickItem *contentItem = listview->contentItem();
2970 QTRY_VERIFY(contentItem != 0);
2972 QQuickText *header = findItem<QQuickText>(contentItem, "header");
2975 QVERIFY(header == listview->headerItem());
2977 QCOMPARE(header->width(), 100.);
2978 QCOMPARE(header->height(), 30.);
2979 QCOMPARE(header->pos(), initialHeaderPos);
2980 QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos);
2982 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
2984 QCOMPARE(item->pos(), firstDelegatePos);
2987 QCOMPARE(header->pos(), initialHeaderPos); // header should stay where it is
2989 for (int i = 0; i < 30; i++)
2990 model.addItem("Item" + QString::number(i), "");
2992 QSignalSpy headerItemSpy(listview, SIGNAL(headerItemChanged()));
2993 QMetaObject::invokeMethod(canvas->rootObject(), "changeHeader");
2995 QCOMPARE(headerItemSpy.count(), 1);
2997 header = findItem<QQuickText>(contentItem, "header");
2999 header = findItem<QQuickText>(contentItem, "header2");
3002 QVERIFY(header == listview->headerItem());
3004 QCOMPARE(header->pos(), changedHeaderPos);
3005 QCOMPARE(header->width(), 50.);
3006 QCOMPARE(header->height(), 20.);
3007 QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), changedContentPos);
3008 QCOMPARE(item->pos(), firstDelegatePos);
3013 // QTBUG-21207 header should become visible if view resizes from initial empty size
3015 canvas = createView();
3016 canvas->rootContext()->setContextProperty("testModel", &model);
3017 canvas->rootContext()->setContextProperty("initialViewWidth", 0.0);
3018 canvas->rootContext()->setContextProperty("initialViewHeight", 0.0);
3019 canvas->setSource(testFileUrl("header.qml"));
3021 listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3022 QTRY_VERIFY(listview != 0);
3023 listview->setOrientation(orientation);
3024 listview->setLayoutDirection(layoutDirection);
3026 listview->setWidth(240);
3027 listview->setHeight(320);
3028 QTRY_COMPARE(listview->headerItem()->pos(), initialHeaderPos);
3029 QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos);
3035 void tst_QQuickListView::header_data()
3037 QTest::addColumn<QQuickListView::Orientation>("orientation");
3038 QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3039 QTest::addColumn<QPointF>("initialHeaderPos");
3040 QTest::addColumn<QPointF>("changedHeaderPos");
3041 QTest::addColumn<QPointF>("initialContentPos");
3042 QTest::addColumn<QPointF>("changedContentPos");
3043 QTest::addColumn<QPointF>("firstDelegatePos");
3044 QTest::addColumn<QPointF>("resizeContentPos");
3046 // header1 = 100 x 30
3047 // header2 = 50 x 20
3048 // delegates = 240 x 20
3051 // header above items, top left
3052 QTest::newRow("vertical, left to right") << QQuickListView::Vertical << Qt::LeftToRight
3060 // header above items, top right
3061 QTest::newRow("vertical, layout right to left") << QQuickListView::Vertical << Qt::RightToLeft
3069 // header to left of items
3070 QTest::newRow("horizontal, layout left to right") << QQuickListView::Horizontal << Qt::LeftToRight
3078 // header to right of items
3079 QTest::newRow("horizontal, layout right to left") << QQuickListView::Horizontal << Qt::RightToLeft
3082 << QPointF(-240 + 100, 0)
3083 << QPointF(-240 + 50, 0)
3085 << QPointF(-240 + 40, 0);
3088 void tst_QQuickListView::header_delayItemCreation()
3090 QQuickView *canvas = createView();
3094 canvas->rootContext()->setContextProperty("setCurrentToZero", QVariant(false));
3095 canvas->setSource(testFileUrl("fillModelOnComponentCompleted.qml"));
3096 qApp->processEvents();
3098 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3099 QTRY_VERIFY(listview != 0);
3101 QQuickItem *contentItem = listview->contentItem();
3102 QTRY_VERIFY(contentItem != 0);
3104 QQuickText *header = findItem<QQuickText>(contentItem, "header");
3106 QCOMPARE(header->y(), -header->height());
3108 QCOMPARE(listview->contentY(), -header->height());
3111 QTRY_COMPARE(header->y(), -header->height());
3116 void tst_QQuickListView::footer()
3118 QFETCH(QQuickListView::Orientation, orientation);
3119 QFETCH(Qt::LayoutDirection, layoutDirection);
3120 QFETCH(QPointF, initialFooterPos);
3121 QFETCH(QPointF, firstDelegatePos);
3122 QFETCH(QPointF, initialContentPos);
3123 QFETCH(QPointF, changedFooterPos);
3124 QFETCH(QPointF, changedContentPos);
3125 QFETCH(QPointF, resizeContentPos);
3127 QQuickView *canvas = createView();
3130 for (int i = 0; i < 3; i++)
3131 model.addItem("Item" + QString::number(i), "");
3133 QDeclarativeContext *ctxt = canvas->rootContext();
3134 ctxt->setContextProperty("testModel", &model);
3136 canvas->setSource(testFileUrl("footer.qml"));
3138 qApp->processEvents();
3140 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3141 QTRY_VERIFY(listview != 0);
3142 listview->setOrientation(orientation);
3143 listview->setLayoutDirection(layoutDirection);
3145 QQuickItem *contentItem = listview->contentItem();
3146 QTRY_VERIFY(contentItem != 0);
3148 QQuickText *footer = findItem<QQuickText>(contentItem, "footer");
3151 QVERIFY(footer == listview->footerItem());
3153 QCOMPARE(footer->pos(), initialFooterPos);
3154 QCOMPARE(footer->width(), 100.);
3155 QCOMPARE(footer->height(), 30.);
3156 QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos);
3158 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3160 QCOMPARE(item->pos(), firstDelegatePos);
3163 model.removeItem(1);
3165 if (orientation == QQuickListView::Vertical) {
3166 QTRY_COMPARE(footer->y(), initialFooterPos.y() - 20); // delegate height = 20
3168 QTRY_COMPARE(footer->x(), layoutDirection == Qt::LeftToRight ?
3169 initialFooterPos.x() - 40 : initialFooterPos.x() + 40); // delegate width = 40
3175 QPointF posWhenNoItems(0, 0);
3176 if (orientation == QQuickListView::Horizontal && layoutDirection == Qt::RightToLeft)
3177 posWhenNoItems.setX(-100);
3178 QTRY_COMPARE(footer->pos(), posWhenNoItems);
3180 // if header is present, it's at a negative pos, so the footer should not move
3181 canvas->rootObject()->setProperty("showHeader", true);
3182 QTRY_COMPARE(footer->pos(), posWhenNoItems);
3183 canvas->rootObject()->setProperty("showHeader", false);
3186 for (int i = 0; i < 30; i++)
3187 model.addItem("Item" + QString::number(i), "");
3189 QSignalSpy footerItemSpy(listview, SIGNAL(footerItemChanged()));
3190 QMetaObject::invokeMethod(canvas->rootObject(), "changeFooter");
3192 QCOMPARE(footerItemSpy.count(), 1);
3194 footer = findItem<QQuickText>(contentItem, "footer");
3196 footer = findItem<QQuickText>(contentItem, "footer2");
3199 QVERIFY(footer == listview->footerItem());
3201 QCOMPARE(footer->pos(), changedFooterPos);
3202 QCOMPARE(footer->width(), 50.);
3203 QCOMPARE(footer->height(), 20.);
3204 QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), changedContentPos);
3206 item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3208 QCOMPARE(item->pos(), firstDelegatePos);
3210 listview->positionViewAtEnd();
3211 footer->setHeight(10);
3212 footer->setWidth(40);
3213 QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), resizeContentPos);
3218 void tst_QQuickListView::footer_data()
3220 QTest::addColumn<QQuickListView::Orientation>("orientation");
3221 QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3222 QTest::addColumn<QPointF>("initialFooterPos");
3223 QTest::addColumn<QPointF>("changedFooterPos");
3224 QTest::addColumn<QPointF>("initialContentPos");
3225 QTest::addColumn<QPointF>("changedContentPos");
3226 QTest::addColumn<QPointF>("firstDelegatePos");
3227 QTest::addColumn<QPointF>("resizeContentPos");
3229 // footer1 = 100 x 30
3230 // footer2 = 50 x 20
3231 // delegates = 40 x 20
3233 // view height = 320
3235 // footer below items, bottom left
3236 QTest::newRow("vertical, layout left to right") << QQuickListView::Vertical << Qt::LeftToRight
3237 << QPointF(0, 3 * 20)
3238 << QPointF(0, 30 * 20) // added 30 items
3242 << QPointF(0, 30 * 20 - 320 + 10);
3244 // footer below items, bottom right
3245 QTest::newRow("vertical, layout right to left") << QQuickListView::Vertical << Qt::RightToLeft
3246 << QPointF(0, 3 * 20)
3247 << QPointF(0, 30 * 20)
3251 << QPointF(0, 30 * 20 - 320 + 10);
3253 // footer to right of items
3254 QTest::newRow("horizontal, layout left to right") << QQuickListView::Horizontal << Qt::LeftToRight
3255 << QPointF(40 * 3, 0)
3256 << QPointF(40 * 30, 0)
3260 << QPointF(40 * 30 - 240 + 40, 0);
3262 // footer to left of items
3263 QTest::newRow("horizontal, layout right to left") << QQuickListView::Horizontal << Qt::RightToLeft
3264 << QPointF(-(40 * 3) - 100, 0)
3265 << QPointF(-(40 * 30) - 50, 0) // 50 = new footer width
3269 << QPointF(-(40 * 30) - 40, 0);
3272 class LVAccessor : public QQuickListView
3275 qreal minY() const { return minYExtent(); }
3276 qreal maxY() const { return maxYExtent(); }
3277 qreal minX() const { return minXExtent(); }
3278 qreal maxX() const { return maxXExtent(); }
3281 void tst_QQuickListView::headerFooter()
3285 QQuickView *canvas = createView();
3288 QDeclarativeContext *ctxt = canvas->rootContext();
3289 ctxt->setContextProperty("testModel", &model);
3291 canvas->setSource(testFileUrl("headerfooter.qml"));
3292 qApp->processEvents();
3294 QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3295 QTRY_VERIFY(listview != 0);
3297 QQuickItem *contentItem = listview->contentItem();
3298 QTRY_VERIFY(contentItem != 0);
3300 QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
3302 QCOMPARE(header->y(), -header->height());
3304 QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
3306 QCOMPARE(footer->y(), 0.);
3308 QCOMPARE(static_cast<LVAccessor*>(listview)->minY(), header->height());
3309 QCOMPARE(static_cast<LVAccessor*>(listview)->maxY(), header->height());
3315 QQuickView *canvas = createView();
3318 QDeclarativeContext *ctxt = canvas->rootContext();
3319 ctxt->setContextProperty("testModel", &model);
3321 canvas->setSource(testFileUrl("headerfooter.qml"));
3322 canvas->rootObject()->setProperty("horizontal", true);
3323 qApp->processEvents();
3325 QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3326 QTRY_VERIFY(listview != 0);
3328 QQuickItem *contentItem = listview->contentItem();
3329 QTRY_VERIFY(contentItem != 0);
3331 QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
3333 QCOMPARE(header->x(), -header->width());
3335 QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
3337 QCOMPARE(footer->x(), 0.);
3339 QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), header->width());
3340 QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), header->width());
3346 QQuickView *canvas = createView();
3349 QDeclarativeContext *ctxt = canvas->rootContext();
3350 ctxt->setContextProperty("testModel", &model);
3352 canvas->setSource(testFileUrl("headerfooter.qml"));
3353 canvas->rootObject()->setProperty("horizontal", true);
3354 canvas->rootObject()->setProperty("rtl", true);
3355 qApp->processEvents();
3357 QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3358 QTRY_VERIFY(listview != 0);
3360 QQuickItem *contentItem = listview->contentItem();
3361 QTRY_VERIFY(contentItem != 0);
3363 QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
3365 QCOMPARE(header->x(), 0.);
3367 QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
3369 QCOMPARE(footer->x(), -footer->width());
3371 QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), 240. - header->width());
3372 QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), 240. - header->width());
3378 void tst_QQuickListView::resizeView()
3380 QQuickView *canvas = createView();
3383 for (int i = 0; i < 40; i++)
3384 model.addItem("Item" + QString::number(i), "");
3386 QDeclarativeContext *ctxt = canvas->rootContext();
3387 ctxt->setContextProperty("testModel", &model);
3389 TestObject *testObject = new TestObject;
3390 ctxt->setContextProperty("testObject", testObject);
3392 canvas->setSource(testFileUrl("listviewtest.qml"));
3393 qApp->processEvents();
3395 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3396 QTRY_VERIFY(listview != 0);
3398 QQuickItem *contentItem = listview->contentItem();
3399 QTRY_VERIFY(contentItem != 0);
3401 // Confirm items positioned correctly
3402 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3403 for (int i = 0; i < model.count() && i < itemCount; ++i) {
3404 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3405 if (!item) qWarning() << "Item" << i << "not found";
3407 QTRY_COMPARE(item->y(), i*20.);
3410 QVariant heightRatio;
3411 QMetaObject::invokeMethod(canvas->rootObject(), "heightRatio", Q_RETURN_ARG(QVariant, heightRatio));
3412 QCOMPARE(heightRatio.toReal(), 0.4);
3414 listview->setHeight(200);
3416 QMetaObject::invokeMethod(canvas->rootObject(), "heightRatio", Q_RETURN_ARG(QVariant, heightRatio));
3417 QCOMPARE(heightRatio.toReal(), 0.25);
3423 void tst_QQuickListView::resizeViewAndRepaint()
3425 QQuickView *canvas = createView();
3429 for (int i = 0; i < 40; i++)
3430 model.addItem("Item" + QString::number(i), "");
3432 QDeclarativeContext *ctxt = canvas->rootContext();
3433 ctxt->setContextProperty("testModel", &model);
3434 ctxt->setContextProperty("initialHeight", 100);
3436 canvas->setSource(testFileUrl("resizeview.qml"));
3437 qApp->processEvents();
3439 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3440 QTRY_VERIFY(listview != 0);
3441 QQuickItem *contentItem = listview->contentItem();
3442 QTRY_VERIFY(contentItem != 0);
3444 // item at index 10 should not be currently visible
3445 QVERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 10));
3447 listview->setHeight(320);
3448 QTRY_VERIFY(findItem<QQuickItem>(contentItem, "wrapper", 10));
3450 listview->setHeight(100);
3451 QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 10));
3456 void tst_QQuickListView::sizeLessThan1()
3458 QQuickView *canvas = createView();
3461 for (int i = 0; i < 30; i++)
3462 model.addItem("Item" + QString::number(i), "");
3464 QDeclarativeContext *ctxt = canvas->rootContext();
3465 ctxt->setContextProperty("testModel", &model);
3467 TestObject *testObject = new TestObject;
3468 ctxt->setContextProperty("testObject", testObject);
3470 canvas->setSource(testFileUrl("sizelessthan1.qml"));
3471 qApp->processEvents();
3473 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3474 QTRY_VERIFY(listview != 0);
3476 QQuickItem *contentItem = listview->contentItem();
3477 QTRY_VERIFY(contentItem != 0);
3479 // Confirm items positioned correctly
3480 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3481 for (int i = 0; i < model.count() && i < itemCount; ++i) {
3482 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3483 if (!item) qWarning() << "Item" << i << "not found";
3485 QTRY_COMPARE(item->y(), i*0.5);
3492 void tst_QQuickListView::QTBUG_14821()
3494 QQuickView *canvas = createView();
3496 canvas->setSource(testFileUrl("qtbug14821.qml"));
3497 qApp->processEvents();
3499 QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3500 QVERIFY(listview != 0);
3502 QQuickItem *contentItem = listview->contentItem();
3503 QVERIFY(contentItem != 0);
3505 listview->decrementCurrentIndex();
3506 QCOMPARE(listview->currentIndex(), 99);
3508 listview->incrementCurrentIndex();
3509 QCOMPARE(listview->currentIndex(), 0);
3514 void tst_QQuickListView::resizeDelegate()
3516 QQuickView *canvas = createView();
3519 QStringList strings;
3520 for (int i = 0; i < 30; ++i)
3521 strings << QString::number(i);
3522 QStringListModel model(strings);
3524 QDeclarativeContext *ctxt = canvas->rootContext();
3525 ctxt->setContextProperty("testModel", &model);
3527 canvas->setSource(testFileUrl("displaylist.qml"));
3528 qApp->processEvents();
3530 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3531 QVERIFY(listview != 0);
3533 QQuickItem *contentItem = listview->contentItem();
3534 QVERIFY(contentItem != 0);
3536 QCOMPARE(listview->count(), model.rowCount());
3538 listview->setCurrentIndex(25);
3539 listview->setContentY(0);
3542 for (int i = 0; i < 16; ++i) {
3543 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3545 QCOMPARE(item->y(), i*20.0);
3548 QCOMPARE(listview->currentItem()->y(), 500.0);
3549 QTRY_COMPARE(listview->highlightItem()->y(), 500.0);
3551 canvas->rootObject()->setProperty("delegateHeight", 30);
3554 for (int i = 0; i < 11; ++i) {
3555 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3557 QTRY_COMPARE(item->y(), i*30.0);
3560 QTRY_COMPARE(listview->currentItem()->y(), 750.0);
3561 QTRY_COMPARE(listview->highlightItem()->y(), 750.0);
3563 listview->setCurrentIndex(1);
3564 listview->positionViewAtIndex(25, QQuickListView::Beginning);
3565 listview->positionViewAtIndex(5, QQuickListView::Beginning);
3567 for (int i = 5; i < 16; ++i) {
3568 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3570 QCOMPARE(item->y(), i*30.0);
3573 QTRY_COMPARE(listview->currentItem()->y(), 30.0);
3574 QTRY_COMPARE(listview->highlightItem()->y(), 30.0);
3576 canvas->rootObject()->setProperty("delegateHeight", 20);
3579 for (int i = 5; i < 11; ++i) {
3580 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3582 QTRY_COMPARE(item->y(), 150 + (i-5)*20.0);
3585 QTRY_COMPARE(listview->currentItem()->y(), 70.0);
3586 QTRY_COMPARE(listview->highlightItem()->y(), 70.0);
3591 void tst_QQuickListView::resizeFirstDelegate()
3593 // QTBUG-20712: Content Y jumps constantly if first delegate height == 0
3594 // and other delegates have height > 0
3596 QSKIP("Test unstable - QTBUG-22872");
3598 QQuickView *canvas = createView();
3601 // bug only occurs when all items in the model are visible
3603 for (int i = 0; i < 10; i++)
3604 model.addItem("Item" + QString::number(i), "");
3606 QDeclarativeContext *ctxt = canvas->rootContext();
3607 ctxt->setContextProperty("testModel", &model);
3609 TestObject *testObject = new TestObject;
3610 ctxt->setContextProperty("testObject", testObject);
3612 canvas->setSource(testFileUrl("listviewtest.qml"));
3613 qApp->processEvents();
3615 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3616 QVERIFY(listview != 0);
3618 QQuickItem *contentItem = listview->contentItem();
3619 QVERIFY(contentItem != 0);
3621 QQuickItem *item = 0;
3622 for (int i = 0; i < model.count(); ++i) {
3623 item = findItem<QQuickItem>(contentItem, "wrapper", i);
3625 QCOMPARE(item->y(), i*20.0);
3628 item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3631 // check the content y has not jumped up and down
3632 QCOMPARE(listview->contentY(), 0.0);
3633 QSignalSpy spy(listview, SIGNAL(contentYChanged()));
3635 QCOMPARE(spy.count(), 0);
3637 for (int i = 1; i < model.count(); ++i) {
3638 item = findItem<QQuickItem>(contentItem, "wrapper", i);
3640 QTRY_COMPARE(item->y(), (i-1)*20.0);
3644 // QTBUG-22014: refill doesn't clear items scrolling off the top of the
3645 // list if they follow a zero-sized delegate
3647 for (int i = 0; i < 10; i++)
3648 model.addItem("Item" + QString::number(i), "");
3650 item = findItem<QQuickItem>(contentItem, "wrapper", 1);
3654 listview->setCurrentIndex(19);
3655 qApp->processEvents();
3657 // items 0-2 should have been deleted
3658 for (int i=0; i<3; i++) {
3659 QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper", i));
3666 void tst_QQuickListView::QTBUG_16037()
3668 QQuickView *canvas = createView();
3671 canvas->setSource(testFileUrl("qtbug16037.qml"));
3672 qApp->processEvents();
3674 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "listview");
3675 QTRY_VERIFY(listview != 0);
3677 QVERIFY(listview->contentHeight() <= 0.0);
3679 QMetaObject::invokeMethod(canvas->rootObject(), "setModel");
3681 QTRY_COMPARE(listview->contentHeight(), 80.0);
3686 void tst_QQuickListView::indexAt()
3688 QQuickView *canvas = createView();
3691 for (int i = 0; i < 30; i++)
3692 model.addItem("Item" + QString::number(i), "");
3694 QDeclarativeContext *ctxt = canvas->rootContext();
3695 ctxt->setContextProperty("testModel", &model);
3697 TestObject *testObject = new TestObject;
3698 ctxt->setContextProperty("testObject", testObject);
3700 canvas->setSource(testFileUrl("listviewtest.qml"));
3701 qApp->processEvents();
3703 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3704 QTRY_VERIFY(listview != 0);
3706 QQuickItem *contentItem = listview->contentItem();
3707 QTRY_VERIFY(contentItem != 0);
3709 QCOMPARE(listview->indexAt(0,0), 0);
3710 QCOMPARE(listview->indexAt(0,19), 0);
3711 QCOMPARE(listview->indexAt(239,19), 0);
3712 QCOMPARE(listview->indexAt(0,20), 1);
3713 QCOMPARE(listview->indexAt(240,20), -1);
3719 void tst_QQuickListView::incrementalModel()
3721 QQuickView *canvas = createView();
3723 IncrementalModel model;
3724 QDeclarativeContext *ctxt = canvas->rootContext();
3725 ctxt->setContextProperty("testModel", &model);
3727 canvas->setSource(testFileUrl("displaylist.qml"));
3728 qApp->processEvents();
3730 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3731 QTRY_VERIFY(listview != 0);
3733 QQuickItem *contentItem = listview->contentItem();
3734 QTRY_VERIFY(contentItem != 0);
3736 QTRY_COMPARE(listview->count(), 20);
3738 listview->positionViewAtIndex(10, QQuickListView::Beginning);
3740 QTRY_COMPARE(listview->count(), 25);
3745 void tst_QQuickListView::onAdd()
3747 QFETCH(int, initialItemCount);
3748 QFETCH(int, itemsToAdd);
3750 const int delegateHeight = 10;
3753 // these initial items should not trigger ListView.onAdd
3754 for (int i=0; i<initialItemCount; i++)
3755 model.addItem("dummy value", "dummy value");
3757 QQuickView *canvas = createView();
3758 canvas->setGeometry(0,0,200, delegateHeight * (initialItemCount + itemsToAdd));
3759 QDeclarativeContext *ctxt = canvas->rootContext();
3760 ctxt->setContextProperty("testModel", &model);
3761 ctxt->setContextProperty("delegateHeight", delegateHeight);
3762 canvas->setSource(testFileUrl("attachedSignals.qml"));
3764 QObject *object = canvas->rootObject();
3765 object->setProperty("width", canvas->width());
3766 object->setProperty("height", canvas->height());
3767 qApp->processEvents();
3769 QList<QPair<QString, QString> > items;
3770 for (int i=0; i<itemsToAdd; i++)
3771 items << qMakePair(QString("value %1").arg(i), QString::number(i));
3772 model.addItems(items);
3773 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
3775 QVariantList result = object->property("addedDelegates").toList();
3776 QCOMPARE(result.count(), items.count());
3777 for (int i=0; i<items.count(); i++)
3778 QCOMPARE(result[i].toString(), items[i].first);
3783 void tst_QQuickListView::onAdd_data()
3785 QTest::addColumn<int>("initialItemCount");
3786 QTest::addColumn<int>("itemsToAdd");
3788 QTest::newRow("0, add 1") << 0 << 1;
3789 QTest::newRow("0, add 2") << 0 << 2;
3790 QTest::newRow("0, add 10") << 0 << 10;
3792 QTest::newRow("1, add 1") << 1 << 1;
3793 QTest::newRow("1, add 2") << 1 << 2;
3794 QTest::newRow("1, add 10") << 1 << 10;
3796 QTest::newRow("5, add 1") << 5 << 1;
3797 QTest::newRow("5, add 2") << 5 << 2;
3798 QTest::newRow("5, add 10") << 5 << 10;
3801 void tst_QQuickListView::onRemove()
3803 QFETCH(int, initialItemCount);
3804 QFETCH(int, indexToRemove);
3805 QFETCH(int, removeCount);
3807 const int delegateHeight = 10;
3809 for (int i=0; i<initialItemCount; i++)
3810 model.addItem(QString("value %1").arg(i), "dummy value");
3812 QQuickView *canvas = createView();
3813 QDeclarativeContext *ctxt = canvas->rootContext();
3814 ctxt->setContextProperty("testModel", &model);
3815 ctxt->setContextProperty("delegateHeight", delegateHeight);
3816 canvas->setSource(testFileUrl("attachedSignals.qml"));
3817 QObject *object = canvas->rootObject();
3819 model.removeItems(indexToRemove, removeCount);
3820 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
3822 QCOMPARE(object->property("removedDelegateCount"), QVariant(removeCount));
3827 void tst_QQuickListView::onRemove_data()
3829 QTest::addColumn<int>("initialItemCount");
3830 QTest::addColumn<int>("indexToRemove");
3831 QTest::addColumn<int>("removeCount");
3833 QTest::newRow("remove first") << 1 << 0 << 1;
3834 QTest::newRow("two items, remove first") << 2 << 0 << 1;
3835 QTest::newRow("two items, remove last") << 2 << 1 << 1;
3836 QTest::newRow("two items, remove all") << 2 << 0 << 2;
3838 QTest::newRow("four items, remove first") << 4 << 0 << 1;
3839 QTest::newRow("four items, remove 0-2") << 4 << 0 << 2;
3840 QTest::newRow("four items, remove 1-3") << 4 << 1 << 2;
3841 QTest::newRow("four items, remove 2-4") << 4 << 2 << 2;
3842 QTest::newRow("four items, remove last") << 4 << 3 << 1;
3843 QTest::newRow("four items, remove all") << 4 << 0 << 4;
3845 QTest::newRow("ten items, remove 1-8") << 10 << 0 << 8;
3846 QTest::newRow("ten items, remove 2-7") << 10 << 2 << 5;
3847 QTest::newRow("ten items, remove 4-10") << 10 << 4 << 6;
3850 void tst_QQuickListView::rightToLeft()
3852 QQuickView *canvas = createView();
3853 canvas->setGeometry(0,0,640,320);
3854 canvas->setSource(testFileUrl("rightToLeft.qml"));
3855 qApp->processEvents();
3857 QVERIFY(canvas->rootObject() != 0);
3858 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "view");
3859 QTRY_VERIFY(listview != 0);
3861 QQuickItem *contentItem = listview->contentItem();
3862 QTRY_VERIFY(contentItem != 0);
3864 QQuickVisualItemModel *model = canvas->rootObject()->findChild<QQuickVisualItemModel*>("itemModel");
3865 QTRY_VERIFY(model != 0);
3867 QTRY_VERIFY(model->count() == 3);
3868 QTRY_COMPARE(listview->currentIndex(), 0);
3870 // initial position at first item, right edge aligned
3871 QCOMPARE(listview->contentX(), -640.);
3873 QQuickItem *item = findItem<QQuickItem>(contentItem, "item1");
3875 QTRY_COMPARE(item->x(), -100.0);
3876 QCOMPARE(item->height(), listview->height());
3878 QQuickText *text = findItem<QQuickText>(contentItem, "text1");
3880 QTRY_COMPARE(text->text(), QLatin1String("index: 0"));
3882 listview->setCurrentIndex(2);
3884 item = findItem<QQuickItem>(contentItem, "item3");
3886 QTRY_COMPARE(item->x(), -540.0);
3888 text = findItem<QQuickText>(contentItem, "text3");
3890 QTRY_COMPARE(text->text(), QLatin1String("index: 2"));
3892 QCOMPARE(listview->contentX(), -640.);
3894 // Ensure resizing maintains position relative to right edge
3895 qobject_cast<QQuickItem*>(canvas->rootObject())->setWidth(600);
3896 QTRY_COMPARE(listview->contentX(), -600.);
3901 void tst_QQuickListView::test_mirroring()
3903 QQuickView *canvasA = createView();
3904 canvasA->setSource(testFileUrl("rightToLeft.qml"));
3905 QQuickListView *listviewA = findItem<QQuickListView>(canvasA->rootObject(), "view");
3906 QTRY_VERIFY(listviewA != 0);
3908 QQuickView *canvasB = createView();
3909 canvasB->setSource(testFileUrl("rightToLeft.qml"));
3910 QQuickListView *listviewB = findItem<QQuickListView>(canvasB->rootObject(), "view");
3911 QTRY_VERIFY(listviewA != 0);
3912 qApp->processEvents();
3914 QList<QString> objectNames;
3915 objectNames << "item1" << "item2"; // << "item3"
3917 listviewA->setProperty("layoutDirection", Qt::LeftToRight);
3918 listviewB->setProperty("layoutDirection", Qt::RightToLeft);
3919 QCOMPARE(listviewA->layoutDirection(), listviewA->effectiveLayoutDirection());
3922 foreach (const QString objectName, objectNames)
3923 QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x());
3925 listviewA->setProperty("layoutDirection", Qt::LeftToRight);
3926 listviewB->setProperty("layoutDirection", Qt::LeftToRight);
3929 foreach (const QString objectName, objectNames)
3930 QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x());
3932 QVERIFY(listviewB->layoutDirection() == listviewB->effectiveLayoutDirection());
3933 QQuickItemPrivate::get(listviewB)->setLayoutMirror(true);
3934 QVERIFY(listviewB->layoutDirection() != listviewB->effectiveLayoutDirection());
3936 // LTR != LTR+mirror
3937 foreach (const QString objectName, objectNames)
3938 QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x());
3940 listviewA->setProperty("layoutDirection", Qt::RightToLeft);
3942 // RTL == LTR+mirror
3943 foreach (const QString objectName, objectNames)
3944 QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x());
3946 listviewB->setProperty("layoutDirection", Qt::RightToLeft);
3948 // RTL != RTL+mirror
3949 foreach (const QString objectName, objectNames)
3950 QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x());
3952 listviewA->setProperty("layoutDirection", Qt::LeftToRight);
3954 // LTR == RTL+mirror
3955 foreach (const QString objectName, objectNames)
3956 QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x());
3962 void tst_QQuickListView::margins()
3964 QQuickView *canvas = createView();
3967 for (int i = 0; i < 50; i++)
3968 model.addItem("Item" + QString::number(i), "");
3970 QDeclarativeContext *ctxt = canvas->rootContext();
3971 ctxt->setContextProperty("testModel", &model);
3973 canvas->setSource(testFileUrl("margins.qml"));
3975 qApp->processEvents();
3977 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3978 QTRY_VERIFY(listview != 0);
3980 QQuickItem *contentItem = listview->contentItem();
3981 QTRY_VERIFY(contentItem != 0);
3983 QCOMPARE(listview->contentY(), -30.);
3984 QCOMPARE(listview->yOrigin(), 0.);
3987 listview->positionViewAtEnd();
3988 qreal pos = listview->contentY();
3989 listview->setContentY(pos + 80);
3990 listview->returnToBounds();
3991 QTRY_COMPARE(listview->contentY(), pos + 50);
3993 // remove item before visible and check that top margin is maintained
3994 // and yOrigin is updated
3995 listview->setContentY(100);
3996 model.removeItem(1);
3997 QTRY_COMPARE(listview->count(), model.count());
3998 listview->setContentY(-50);
3999 QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
4000 listview->returnToBounds();
4001 QCOMPARE(listview->yOrigin(), 20.);
4002 QTRY_COMPARE(listview->contentY(), -10.);
4004 // reduce top margin
4005 listview->setTopMargin(20);
4006 QCOMPARE(listview->yOrigin(), 20.);
4007 QTRY_COMPARE(listview->contentY(), 0.);
4010 listview->positionViewAtEnd();
4011 pos = listview->contentY();
4012 listview->setContentY(pos + 80);
4013 listview->returnToBounds();
4014 QTRY_COMPARE(listview->contentY(), pos + 50);
4016 // reduce bottom margin
4017 pos = listview->contentY();
4018 listview->setBottomMargin(40);
4019 QCOMPARE(listview->yOrigin(), 20.);
4020 QTRY_COMPARE(listview->contentY(), pos-10);
4025 void tst_QQuickListView::snapToItem_data()
4027 QTest::addColumn<QQuickListView::Orientation>("orientation");
4028 QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
4029 QTest::addColumn<int>("highlightRangeMode");
4030 QTest::addColumn<QPoint>("flickStart");
4031 QTest::addColumn<QPoint>("flickEnd");
4032 QTest::addColumn<qreal>("snapAlignment");
4033 QTest::addColumn<qreal>("endExtent");
4034 QTest::addColumn<qreal>("startExtent");
4036 QTest::newRow("vertical, left to right") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4037 << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 1200.0 << 0.0;
4039 QTest::newRow("horizontal, left to right") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4040 << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 1200.0 << 0.0;
4042 QTest::newRow("horizontal, right to left") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
4043 << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -1200.0 - 240.0 << -240.0;
4045 QTest::newRow("vertical, left to right, enforce range") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4046 << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 1340.0 << -20.0;
4048 QTest::newRow("horizontal, left to right, enforce range") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4049 << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 1340.0 << -20.0;
4051 QTest::newRow("horizontal, right to left, enforce range") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
4052 << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -1200.0 - 240.0 - 140.0 << -220.0;
4055 void tst_QQuickListView::snapToItem()
4057 QFETCH(QQuickListView::Orientation, orientation);
4058 QFETCH(Qt::LayoutDirection, layoutDirection);
4059 QFETCH(int, highlightRangeMode);
4060 QFETCH(QPoint, flickStart);
4061 QFETCH(QPoint, flickEnd);
4062 QFETCH(qreal, snapAlignment);
4063 QFETCH(qreal, endExtent);
4064 QFETCH(qreal, startExtent);
4066 QQuickView *canvas = createView();
4068 canvas->setSource(testFileUrl("snapToItem.qml"));
4070 qApp->processEvents();
4072 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
4073 QTRY_VERIFY(listview != 0);
4075 listview->setOrientation(orientation);
4076 listview->setLayoutDirection(layoutDirection);
4077 listview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
4079 QQuickItem *contentItem = listview->contentItem();
4080 QTRY_VERIFY(contentItem != 0);
4082 // confirm that a flick hits an item boundary
4083 flick(canvas, flickStart, flickEnd, 180);
4084 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4085 if (orientation == QQuickListView::Vertical)
4086 QCOMPARE(qreal(fmod(listview->contentY(),80.0)), snapAlignment);
4088 QCOMPARE(qreal(fmod(listview->contentX(),80.0)), snapAlignment);
4092 flick(canvas, flickStart, flickEnd, 180);
4093 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4094 } while (orientation == QQuickListView::Vertical
4095 ? !listview->isAtYEnd()
4096 : layoutDirection == Qt::LeftToRight ? !listview->isAtXEnd() : !listview->isAtXBeginning());
4098 if (orientation == QQuickListView::Vertical)
4099 QCOMPARE(listview->contentY(), endExtent);
4101 QCOMPARE(listview->contentX(), endExtent);
4105 flick(canvas, flickEnd, flickStart, 180);
4106 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4107 } while (orientation == QQuickListView::Vertical
4108 ? !listview->isAtYBeginning()
4109 : layoutDirection == Qt::LeftToRight ? !listview->isAtXBeginning() : !listview->isAtXEnd());
4111 if (orientation == QQuickListView::Vertical)
4112 QCOMPARE(listview->contentY(), startExtent);
4114 QCOMPARE(listview->contentX(), startExtent);
4119 void tst_QQuickListView::qListModelInterface_items()
4121 items<TestModel>(testFileUrl("listviewtest.qml"), false);
4124 void tst_QQuickListView::qListModelInterface_package_items()
4126 items<TestModel>(testFileUrl("listviewtest-package.qml"), true);
4129 void tst_QQuickListView::qAbstractItemModel_items()
4131 items<TestModel2>(testFileUrl("listviewtest.qml"), false);
4134 void tst_QQuickListView::qListModelInterface_changed()
4136 changed<TestModel>(testFileUrl("listviewtest.qml"), false);
4139 void tst_QQuickListView::qListModelInterface_package_changed()
4141 changed<TestModel>(testFileUrl("listviewtest-package.qml"), true);
4144 void tst_QQuickListView::qAbstractItemModel_changed()
4146 changed<TestModel2>(testFileUrl("listviewtest.qml"), false);
4149 void tst_QQuickListView::qListModelInterface_inserted()
4151 inserted<TestModel>(testFileUrl("listviewtest.qml"));
4154 void tst_QQuickListView::qListModelInterface_package_inserted()
4156 inserted<TestModel>(testFileUrl("listviewtest-package.qml"));
4159 void tst_QQuickListView::qListModelInterface_inserted_more()
4161 inserted_more<TestModel>();
4164 void tst_QQuickListView::qListModelInterface_inserted_more_data()
4166 inserted_more_data();
4169 void tst_QQuickListView::qAbstractItemModel_inserted()
4171 inserted<TestModel2>(testFileUrl("listviewtest.qml"));
4174 void tst_QQuickListView::qAbstractItemModel_inserted_more()
4176 inserted_more<TestModel2>();
4179 void tst_QQuickListView::qAbstractItemModel_inserted_more_data()
4181 inserted_more_data();
4184 void tst_QQuickListView::qListModelInterface_removed()
4186 removed<TestModel>(testFileUrl("listviewtest.qml"), false);
4187 removed<TestModel>(testFileUrl("listviewtest.qml"), true);
4190 void tst_QQuickListView::qListModelInterface_package_removed()
4192 removed<TestModel>(testFileUrl("listviewtest-package.qml"), false);
4193 removed<TestModel>(testFileUrl("listviewtest-package.qml"), true);
4196 void tst_QQuickListView::qAbstractItemModel_removed()
4198 removed<TestModel2>(testFileUrl("listviewtest.qml"), false);
4199 removed<TestModel2>(testFileUrl("listviewtest.qml"), true);
4202 void tst_QQuickListView::qListModelInterface_moved()
4204 moved<TestModel>(testFileUrl("listviewtest.qml"));
4207 void tst_QQuickListView::qListModelInterface_moved_data()
4212 void tst_QQuickListView::qListModelInterface_package_moved()
4214 moved<TestModel>(testFileUrl("listviewtest-package.qml"));
4217 void tst_QQuickListView::qListModelInterface_package_moved_data()
4222 void tst_QQuickListView::qAbstractItemModel_moved()
4224 moved<TestModel2>(testFileUrl("listviewtest.qml"));
4227 void tst_QQuickListView::qAbstractItemModel_moved_data()
4232 void tst_QQuickListView::qListModelInterface_clear()
4234 clear<TestModel>(testFileUrl("listviewtest.qml"));
4237 void tst_QQuickListView::qListModelInterface_package_clear()
4239 clear<TestModel>(testFileUrl("listviewtest-package.qml"));
4242 void tst_QQuickListView::qAbstractItemModel_clear()
4244 clear<TestModel2>(testFileUrl("listviewtest.qml"));
4247 void tst_QQuickListView::qListModelInterface_sections()
4249 sections<TestModel>(testFileUrl("listview-sections.qml"));
4252 void tst_QQuickListView::qListModelInterface_package_sections()
4254 sections<TestModel>(testFileUrl("listview-sections-package.qml"));
4257 void tst_QQuickListView::qAbstractItemModel_sections()
4259 sections<TestModel2>(testFileUrl("listview-sections.qml"));
4262 void tst_QQuickListView::creationContext()
4265 canvas.setGeometry(0,0,240,320);
4266 canvas.setSource(testFileUrl("creationContext.qml"));
4267 qApp->processEvents();
4269 QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
4271 QVERIFY(rootItem->property("count").toInt() > 0);
4274 QVERIFY(item = rootItem->findChild<QQuickItem *>("listItem"));
4275 QCOMPARE(item->property("text").toString(), QString("Hello!"));
4276 QVERIFY(item = rootItem->findChild<QQuickItem *>("header"));
4277 QCOMPARE(item->property("text").toString(), QString("Hello!"));
4278 QVERIFY(item = rootItem->findChild<QQuickItem *>("footer"));
4279 QCOMPARE(item->property("text").toString(), QString("Hello!"));
4280 QVERIFY(item = rootItem->findChild<QQuickItem *>("section"));
4281 QCOMPARE(item->property("text").toString(), QString("Hello!"));
4284 void tst_QQuickListView::QTBUG_21742()
4287 canvas.setGeometry(0,0,200,200);
4288 canvas.setSource(testFileUrl("qtbug-21742.qml"));
4289 qApp->processEvents();
4291 QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
4293 QCOMPARE(rootItem->property("count").toInt(), 1);
4296 QQuickView *tst_QQuickListView::createView()
4298 QQuickView *canvas = new QQuickView(0);
4299 canvas->setGeometry(0,0,240,320);
4304 void tst_QQuickListView::flick(QQuickView *canvas, const QPoint &from, const QPoint &to, int duration)
4306 const int pointCount = 5;
4307 QPoint diff = to - from;
4309 // send press, five equally spaced moves, and release.
4310 QTest::mousePress(canvas, Qt::LeftButton, 0, from);
4312 for (int i = 0; i < pointCount; ++i) {
4313 QMouseEvent mv(QEvent::MouseMove, from + (i+1)*diff/pointCount, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
4314 QApplication::sendEvent(canvas, &mv);
4315 QTest::qWait(duration/pointCount);
4316 QCoreApplication::processEvents();
4319 QTest::mouseRelease(canvas, Qt::LeftButton, 0, to);
4322 void tst_QQuickListView::asynchronous()
4324 QQuickView *canvas = createView();
4326 QDeclarativeIncubationController controller;
4327 canvas->engine()->setIncubationController(&controller);
4329 canvas->setSource(testFileUrl("asyncloader.qml"));
4331 QQuickItem *rootObject = qobject_cast<QQuickItem*>(canvas->rootObject());
4332 QVERIFY(rootObject);
4334 QQuickListView *listview = 0;
4337 controller.incubateWhile(&b);
4338 listview = rootObject->findChild<QQuickListView*>("view");
4341 // items will be created one at a time
4342 for (int i = 0; i < 8; ++i) {
4343 QVERIFY(findItem<QQuickItem>(listview, "wrapper", i) == 0);
4344 QQuickItem *item = 0;
4347 controller.incubateWhile(&b);
4348 item = findItem<QQuickItem>(listview, "wrapper", i);
4354 controller.incubateWhile(&b);
4357 // verify positioning
4358 QQuickItem *contentItem = listview->contentItem();
4359 for (int i = 0; i < 8; ++i) {
4360 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
4361 QTRY_COMPARE(item->y(), i*50.0);
4367 void tst_QQuickListView::snapOneItem_data()
4369 QTest::addColumn<QQuickListView::Orientation>("orientation");
4370 QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
4371 QTest::addColumn<int>("highlightRangeMode");
4372 QTest::addColumn<QPoint>("flickStart");
4373 QTest::addColumn<QPoint>("flickEnd");
4374 QTest::addColumn<qreal>("snapAlignment");
4375 QTest::addColumn<qreal>("endExtent");
4376 QTest::addColumn<qreal>("startExtent");
4378 QTest::newRow("vertical, left to right") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4379 << QPoint(20, 200) << QPoint(20, 20) << 180.0 << 560.0 << 0.0;
4381 QTest::newRow("horizontal, left to right") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4382 << QPoint(200, 20) << QPoint(20, 20) << 180.0 << 560.0 << 0.0;
4384 QTest::newRow("horizontal, right to left") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
4385 << QPoint(20, 20) << QPoint(200, 20) << -420.0 << -560.0 - 240.0 << -240.0;
4387 QTest::newRow("vertical, left to right, enforce range") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4388 << QPoint(20, 200) << QPoint(20, 20) << 180.0 << 580.0 << -20.0;
4390 QTest::newRow("horizontal, left to right, enforce range") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4391 << QPoint(200, 20) << QPoint(20, 20) << 180.0 << 580.0 << -20.0;
4393 QTest::newRow("horizontal, right to left, enforce range") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
4394 << QPoint(20, 20) << QPoint(200, 20) << -420.0 << -580.0 - 240.0 << -220.0;
4397 void tst_QQuickListView::snapOneItem()
4399 QFETCH(QQuickListView::Orientation, orientation);
4400 QFETCH(Qt::LayoutDirection, layoutDirection);
4401 QFETCH(int, highlightRangeMode);
4402 QFETCH(QPoint, flickStart);
4403 QFETCH(QPoint, flickEnd);
4404 QFETCH(qreal, snapAlignment);
4405 QFETCH(qreal, endExtent);
4406 QFETCH(qreal, startExtent);
4408 QQuickView *canvas = createView();
4410 canvas->setSource(testFileUrl("snapOneItem.qml"));
4412 qApp->processEvents();
4414 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
4415 QTRY_VERIFY(listview != 0);
4417 listview->setOrientation(orientation);
4418 listview->setLayoutDirection(layoutDirection);
4419 listview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
4421 QQuickItem *contentItem = listview->contentItem();
4422 QTRY_VERIFY(contentItem != 0);
4424 QSignalSpy currentIndexSpy(listview, SIGNAL(currentIndexChanged()));
4426 // confirm that a flick hits the next item boundary
4427 flick(canvas, flickStart, flickEnd, 180);
4428 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4429 if (orientation == QQuickListView::Vertical)
4430 QCOMPARE(listview->contentY(), snapAlignment);
4432 QCOMPARE(listview->contentX(), snapAlignment);
4434 if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4435 QCOMPARE(listview->currentIndex(), 1);
4436 QCOMPARE(currentIndexSpy.count(), 1);
4441 flick(canvas, flickStart, flickEnd, 180);
4442 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4443 } while (orientation == QQuickListView::Vertical
4444 ? !listview->isAtYEnd()
4445 : layoutDirection == Qt::LeftToRight ? !listview->isAtXEnd() : !listview->isAtXBeginning());
4447 if (orientation == QQuickListView::Vertical)
4448 QCOMPARE(listview->contentY(), endExtent);
4450 QCOMPARE(listview->contentX(), endExtent);
4452 if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4453 QCOMPARE(listview->currentIndex(), 3);
4454 QCOMPARE(currentIndexSpy.count(), 3);
4459 flick(canvas, flickEnd, flickStart, 180);
4460 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4461 } while (orientation == QQuickListView::Vertical
4462 ? !listview->isAtYBeginning()
4463 : layoutDirection == Qt::LeftToRight ? !listview->isAtXBeginning() : !listview->isAtXEnd());
4465 if (orientation == QQuickListView::Vertical)
4466 QCOMPARE(listview->contentY(), startExtent);
4468 QCOMPARE(listview->contentX(), startExtent);
4470 if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4471 QCOMPARE(listview->currentIndex(), 0);
4472 QCOMPARE(currentIndexSpy.count(), 6);
4478 void tst_QQuickListView::unrequestedVisibility()
4481 for (int i = 0; i < 30; i++)
4482 model.addItem("Item" + QString::number(i), QString::number(i));
4484 QQuickView *canvas = new QQuickView(0);
4485 canvas->setGeometry(0,0,240,320);
4487 QDeclarativeContext *ctxt = canvas->rootContext();
4488 ctxt->setContextProperty("testModel", &model);
4489 ctxt->setContextProperty("testWrap", QVariant(false));
4491 canvas->setSource(testFileUrl("unrequestedItems.qml"));
4495 qApp->processEvents();
4498 QQuickListView *leftview = findItem<QQuickListView>(canvas->rootObject(), "leftList");
4499 QTRY_VERIFY(leftview != 0);
4501 QQuickListView *rightview = findItem<QQuickListView>(canvas->rootObject(), "rightList");
4502 QTRY_VERIFY(rightview != 0);
4504 QQuickItem *leftContent = leftview->contentItem();
4505 QTRY_VERIFY(leftContent != 0);
4507 QQuickItem *rightContent = rightview->contentItem();
4508 QTRY_VERIFY(rightContent != 0);
4510 rightview->setCurrentIndex(20);
4512 QTRY_COMPARE(leftview->contentY(), 0.0);
4513 QTRY_COMPARE(rightview->contentY(), 100.0);
4517 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
4518 QCOMPARE(item->isVisible(), true);
4519 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
4520 QCOMPARE(item->isVisible(), false);
4522 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 19));
4523 QCOMPARE(item->isVisible(), false);
4524 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 19));
4525 QCOMPARE(item->isVisible(), true);
4527 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 16));
4528 QCOMPARE(item->isVisible(), true);
4529 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 17));
4530 QCOMPARE(item->isVisible(), false);
4531 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 3));
4532 QCOMPARE(item->isVisible(), false);
4533 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 4));
4534 QCOMPARE(item->isVisible(), true);
4536 rightview->setCurrentIndex(0);
4538 QTRY_COMPARE(leftview->contentY(), 0.0);
4539 QTRY_COMPARE(rightview->contentY(), 0.0);
4541 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
4542 QCOMPARE(item->isVisible(), true);
4543 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
4544 QTRY_COMPARE(item->isVisible(), true);
4546 QVERIFY(!findItem<QQuickItem>(leftContent, "wrapper", 19));
4547 QVERIFY(!findItem<QQuickItem>(rightContent, "wrapper", 19));
4549 leftview->setCurrentIndex(20);
4551 QTRY_COMPARE(leftview->contentY(), 100.0);
4552 QTRY_COMPARE(rightview->contentY(), 0.0);
4554 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
4555 QTRY_COMPARE(item->isVisible(), false);
4556 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
4557 QCOMPARE(item->isVisible(), true);
4559 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 19));
4560 QCOMPARE(item->isVisible(), true);
4561 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 19));
4562 QCOMPARE(item->isVisible(), false);
4564 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
4565 QCOMPARE(item->isVisible(), false);
4566 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4567 QCOMPARE(item->isVisible(), true);
4568 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4569 QCOMPARE(item->isVisible(), true);
4570 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4571 QCOMPARE(item->isVisible(), false);
4573 model.moveItems(19, 1, 1);
4574 QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4576 QTRY_VERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
4577 QCOMPARE(item->isVisible(), false);
4578 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
4579 QCOMPARE(item->isVisible(), true);
4581 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 19));
4582 QCOMPARE(item->isVisible(), true);
4583 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 19));
4584 QCOMPARE(item->isVisible(), false);
4586 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4587 QCOMPARE(item->isVisible(), false);
4588 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4589 QCOMPARE(item->isVisible(), true);
4590 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4591 QCOMPARE(item->isVisible(), true);
4592 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4593 QCOMPARE(item->isVisible(), false);
4595 model.moveItems(3, 4, 1);
4596 QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4598 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4599 QCOMPARE(item->isVisible(), false);
4600 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4601 QCOMPARE(item->isVisible(), true);
4602 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4603 QCOMPARE(item->isVisible(), true);
4604 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4605 QCOMPARE(item->isVisible(), false);
4607 model.moveItems(4, 3, 1);
4608 QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4610 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4611 QCOMPARE(item->isVisible(), false);
4612 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4613 QCOMPARE(item->isVisible(), true);
4614 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4615 QCOMPARE(item->isVisible(), true);
4616 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4617 QCOMPARE(item->isVisible(), false);
4619 model.moveItems(16, 17, 1);
4620 QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4622 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4623 QCOMPARE(item->isVisible(), false);
4624 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4625 QCOMPARE(item->isVisible(), true);
4626 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4627 QCOMPARE(item->isVisible(), true);
4628 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4629 QCOMPARE(item->isVisible(), false);
4631 model.moveItems(17, 16, 1);
4632 QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4634 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4635 QCOMPARE(item->isVisible(), false);
4636 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 6));
4637 QCOMPARE(item->isVisible(), true);
4638 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4639 QCOMPARE(item->isVisible(), true);
4640 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4641 QCOMPARE(item->isVisible(), false);
4646 QQuickItem *tst_QQuickListView::findVisibleChild(QQuickItem *parent, const QString &objectName)
4648 QQuickItem *item = 0;
4649 QList<QQuickItem*> items = parent->findChildren<QQuickItem*>(objectName);
4650 for (int i = 0; i < items.count(); ++i) {
4651 if (items.at(i)->isVisible()) {
4659 Find an item with the specified objectName. If index is supplied then the
4660 item must also evaluate the {index} expression equal to index
4662 template<typename T>
4663 T *tst_QQuickListView::findItem(QQuickItem *parent, const QString &objectName, int index)
4665 const QMetaObject &mo = T::staticMetaObject;
4666 //qDebug() << parent->childItems().count() << "children";
4667 for (int i = 0; i < parent->childItems().count(); ++i) {
4668 QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
4671 //qDebug() << "try" << item;
4672 if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
4674 QDeclarativeExpression e(qmlContext(item), item, "index");
4675 if (e.evaluate().toInt() == index)
4676 return static_cast<T*>(item);
4678 return static_cast<T*>(item);
4681 item = findItem<T>(item, objectName, index);
4683 return static_cast<T*>(item);
4689 template<typename T>
4690 QList<T*> tst_QQuickListView::findItems(QQuickItem *parent, const QString &objectName, bool visibleOnly)
4693 const QMetaObject &mo = T::staticMetaObject;
4694 //qDebug() << parent->childItems().count() << "children";
4695 for (int i = 0; i < parent->childItems().count(); ++i) {
4696 QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
4697 if (!item || (visibleOnly && !item->isVisible()))
4699 //qDebug() << "try" << item;
4700 if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName))
4701 items.append(static_cast<T*>(item));
4702 items += findItems<T>(item, objectName);
4708 void tst_QQuickListView::dumpTree(QQuickItem *parent, int depth)
4710 static QString padding(" ");
4711 for (int i = 0; i < parent->childItems().count(); ++i) {
4712 QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
4715 qDebug() << padding.left(depth*2) << item;
4716 dumpTree(item, depth+1);
4720 QTEST_MAIN(tst_QQuickListView)
4722 #include "tst_qquicklistview.moc"