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_removed_more();
89 void qListModelInterface_removed_more_data();
90 void qListModelInterface_package_removed();
91 void qAbstractItemModel_removed();
92 void qAbstractItemModel_removed_more();
93 void qAbstractItemModel_removed_more_data();
95 void qListModelInterface_moved();
96 void qListModelInterface_moved_data();
97 void qListModelInterface_package_moved();
98 void qListModelInterface_package_moved_data();
99 void qAbstractItemModel_moved();
100 void qAbstractItemModel_moved_data();
102 void multipleChanges();
103 void multipleChanges_data();
105 void qListModelInterface_clear();
106 void qListModelInterface_package_clear();
107 void qAbstractItemModel_clear();
109 void insertBeforeVisible();
110 void insertBeforeVisible_data();
111 void swapWithFirstItem();
113 void currentIndex_delayedItemCreation();
114 void currentIndex_delayedItemCreation_data();
116 void noCurrentIndex();
118 void enforceRange_withoutHighlight();
120 void qListModelInterface_sections();
121 void qListModelInterface_package_sections();
122 void qAbstractItemModel_sections();
123 void sectionsPositioning();
124 void sectionsDelegate();
126 void positionViewAtIndex();
128 void propertyChanges();
129 void componentChanges();
131 void manualHighlight();
134 void header_delayItemCreation();
139 void resizeViewAndRepaint();
140 void sizeLessThan1();
142 void resizeDelegate();
143 void resizeFirstDelegate();
145 void indexAt_itemAt_data();
146 void indexAt_itemAt();
147 void incrementalModel();
151 void onRemove_data();
153 void test_mirroring();
155 void creationContext();
156 void snapToItem_data();
158 void snapOneItem_data();
166 void unrequestedVisibility();
169 template <class T> void items(const QUrl &source, bool forceLayout);
170 template <class T> void changed(const QUrl &source, bool forceLayout);
171 template <class T> void inserted(const QUrl &source);
172 template <class T> void inserted_more();
173 template <class T> void removed(const QUrl &source, bool animated);
174 template <class T> void removed_more(const QUrl &source);
175 template <class T> void moved(const QUrl &source);
176 template <class T> void clear(const QUrl &source);
177 template <class T> void sections(const QUrl &source);
178 QQuickView *createView();
179 void flick(QQuickView *canvas, const QPoint &from, const QPoint &to, int duration);
180 QQuickItem *findVisibleChild(QQuickItem *parent, const QString &objectName);
182 T *findItem(QQuickItem *parent, const QString &id, int index=-1);
184 QList<T*> findItems(QQuickItem *parent, const QString &objectName, bool visibleOnly = true);
185 void dumpTree(QQuickItem *parent, int depth = 0);
187 void inserted_more_data();
188 void removed_more_data();
192 class TestObject : public QObject
196 Q_PROPERTY(bool error READ error WRITE setError NOTIFY changedError)
197 Q_PROPERTY(bool animate READ animate NOTIFY changedAnim)
198 Q_PROPERTY(bool invalidHighlight READ invalidHighlight NOTIFY changedHl)
199 Q_PROPERTY(int cacheBuffer READ cacheBuffer NOTIFY changedCacheBuffer)
202 TestObject(QObject *parent = 0)
203 : QObject(parent), mError(true), mAnimate(false), mInvalidHighlight(false)
206 bool error() const { return mError; }
207 void setError(bool err) { mError = err; emit changedError(); }
209 bool animate() const { return mAnimate; }
210 void setAnimate(bool anim) { mAnimate = anim; emit changedAnim(); }
212 bool invalidHighlight() const { return mInvalidHighlight; }
213 void setInvalidHighlight(bool invalid) { mInvalidHighlight = invalid; emit changedHl(); }
215 int cacheBuffer() const { return mCacheBuffer; }
216 void setCacheBuffer(int buffer) { mCacheBuffer = buffer; emit changedCacheBuffer(); }
222 void changedCacheBuffer();
227 bool mInvalidHighlight;
232 void tst_qquicklistview_move(int from, int to, int n, T *items)
235 // Only move forwards - flip if backwards moving
243 items->move(from, to);
247 typename T::ConstIterator it=items->begin(); it += from+n;
248 for (; i<to-from; ++i,++it)
249 replaced.append(*it);
251 it=items->begin(); it += from;
252 for (; i<n; ++i,++it)
253 replaced.append(*it);
254 typename T::ConstIterator f=replaced.begin();
255 typename T::Iterator t=items->begin(); t += from;
256 for (; f != replaced.end(); ++f, ++t)
261 class TestModel : public QListModelInterface
265 TestModel(QObject *parent = 0) : QListModelInterface(parent) {}
268 enum Roles { Name, Number };
270 QString name(int index) const { return list.at(index).first; }
271 QString number(int index) const { return list.at(index).second; }
273 int count() const { return list.count(); }
275 QList<int> roles() const { return QList<int>() << Name << Number; }
276 QString toString(int role) const {
287 QVariant data(int index, int role) const
290 return list.at(index).first;
292 return list.at(index).second;
295 QHash<int, QVariant> data(int index, const QList<int> &roles) const {
296 QHash<int,QVariant> returnHash;
298 for (int i = 0; i < roles.size(); ++i) {
299 int role = roles.at(i);
303 info = list.at(index).first;
306 info = list.at(index).second;
311 returnHash.insert(role, info);
316 void addItem(const QString &name, const QString &number) {
317 list.append(QPair<QString,QString>(name, number));
318 emit itemsInserted(list.count()-1, 1);
321 void insertItem(int index, const QString &name, const QString &number) {
322 list.insert(index, QPair<QString,QString>(name, number));
323 emit itemsInserted(index, 1);
326 void insertItems(int index, const QList<QPair<QString, QString> > &items) {
327 for (int i=0; i<items.count(); i++)
328 list.insert(index + i, QPair<QString,QString>(items[i].first, items[i].second));
329 emit itemsInserted(index, items.count());
332 void removeItem(int index) {
333 list.removeAt(index);
334 emit itemsRemoved(index, 1);
337 void removeItems(int index, int count) {
340 list.removeAt(index);
341 emit itemsRemoved(index, count);
344 void moveItem(int from, int to) {
346 emit itemsMoved(from, to, 1);
349 void moveItems(int from, int to, int count) {
350 tst_qquicklistview_move(from, to, count, &list);
351 emit itemsMoved(from, to, count);
354 void modifyItem(int index, const QString &name, const QString &number) {
355 list[index] = QPair<QString,QString>(name, number);
356 emit itemsChanged(index, 1, roles());
360 int count = list.count();
362 emit itemsRemoved(0, count);
366 QList<QPair<QString,QString> > list;
370 class TestModel2 : public QAbstractListModel
373 enum Roles { Name = Qt::UserRole+1, Number = Qt::UserRole+2 };
375 TestModel2(QObject *parent=0) : QAbstractListModel(parent) {
376 QHash<int, QByteArray> roles;
377 roles[Name] = "name";
378 roles[Number] = "number";
382 int rowCount(const QModelIndex &parent=QModelIndex()) const { Q_UNUSED(parent); return list.count(); }
383 QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const {
386 rv = list.at(index.row()).first;
387 else if (role == Number)
388 rv = list.at(index.row()).second;
393 int count() const { return rowCount(); }
394 QString name(int index) const { return list.at(index).first; }
395 QString number(int index) const { return list.at(index).second; }
397 void addItem(const QString &name, const QString &number) {
398 emit beginInsertRows(QModelIndex(), list.count(), list.count());
399 list.append(QPair<QString,QString>(name, number));
400 emit endInsertRows();
403 void addItems(const QList<QPair<QString, QString> > &items) {
404 emit beginInsertRows(QModelIndex(), list.count(), list.count()+items.count()-1);
405 for (int i=0; i<items.count(); i++)
406 list.append(QPair<QString,QString>(items[i].first, items[i].second));
407 emit endInsertRows();
410 void insertItem(int index, const QString &name, const QString &number) {
411 emit beginInsertRows(QModelIndex(), index, index);
412 list.insert(index, QPair<QString,QString>(name, number));
413 emit endInsertRows();
416 void insertItems(int index, const QList<QPair<QString, QString> > &items) {
417 emit beginInsertRows(QModelIndex(), index, index+items.count()-1);
418 for (int i=0; i<items.count(); i++)
419 list.insert(index + i, QPair<QString,QString>(items[i].first, items[i].second));
420 emit endInsertRows();
423 void removeItem(int index) {
424 emit beginRemoveRows(QModelIndex(), index, index);
425 list.removeAt(index);
426 emit endRemoveRows();
429 void removeItems(int index, int count) {
430 emit beginRemoveRows(QModelIndex(), index, index+count-1);
432 list.removeAt(index);
433 emit endRemoveRows();
436 void moveItem(int from, int to) {
437 emit beginMoveRows(QModelIndex(), from, from, QModelIndex(), to);
442 void moveItems(int from, int to, int count) {
443 emit beginMoveRows(QModelIndex(), from, from+count-1, QModelIndex(), to > from ? to+count : to);
444 tst_qquicklistview_move(from, to, count, &list);
448 void modifyItem(int idx, const QString &name, const QString &number) {
449 list[idx] = QPair<QString,QString>(name, number);
450 emit dataChanged(index(idx,0), index(idx,0));
454 int count = list.count();
455 emit beginRemoveRows(QModelIndex(), 0, count-1);
457 emit endRemoveRows();
461 QList<QPair<QString,QString> > list;
464 tst_QQuickListView::tst_QQuickListView()
469 void tst_QQuickListView::items(const QUrl &source, bool forceLayout)
471 QQuickView *canvas = createView();
474 model.addItem("Fred", "12345");
475 model.addItem("John", "2345");
476 model.addItem("Bob", "54321");
478 QDeclarativeContext *ctxt = canvas->rootContext();
479 ctxt->setContextProperty("testModel", &model);
481 TestObject *testObject = new TestObject;
482 ctxt->setContextProperty("testObject", testObject);
484 canvas->setSource(source);
485 qApp->processEvents();
487 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
488 QTRY_VERIFY(listview != 0);
490 QQuickItem *contentItem = listview->contentItem();
491 QTRY_VERIFY(contentItem != 0);
493 QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
494 QTRY_VERIFY(testObject->error() == false);
496 QTRY_VERIFY(listview->highlightItem() != 0);
497 QTRY_COMPARE(listview->count(), model.count());
498 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
499 QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
501 // current item should be first item
502 QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
504 for (int i = 0; i < model.count(); ++i) {
505 QQuickText *name = findItem<QQuickText>(contentItem, "textName", i);
506 QTRY_VERIFY(name != 0);
507 QTRY_COMPARE(name->text(), model.name(i));
508 QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", i);
509 QTRY_VERIFY(number != 0);
510 QTRY_COMPARE(number->text(), model.number(i));
513 // switch to other delegate
514 testObject->setAnimate(true);
515 QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
516 QTRY_VERIFY(testObject->error() == false);
517 QTRY_VERIFY(listview->currentItem());
519 // set invalid highlight
520 testObject->setInvalidHighlight(true);
521 QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
522 QTRY_VERIFY(testObject->error() == false);
523 QTRY_VERIFY(listview->currentItem());
524 QTRY_VERIFY(listview->highlightItem() == 0);
526 // back to normal highlight
527 testObject->setInvalidHighlight(false);
528 QMetaObject::invokeMethod(canvas->rootObject(), "checkProperties");
529 QTRY_VERIFY(testObject->error() == false);
530 QTRY_VERIFY(listview->currentItem());
531 QTRY_VERIFY(listview->highlightItem() != 0);
533 // set an empty model and confirm that items are destroyed
535 ctxt->setContextProperty("testModel", &model2);
537 // Force a layout, necessary if ListView is completed before VisualDataModel.
539 QCOMPARE(listview->property("count").toInt(), 0);
541 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
542 QTRY_VERIFY(itemCount == 0);
544 QTRY_COMPARE(listview->highlightResizeSpeed(), 1000.0);
545 QTRY_COMPARE(listview->highlightMoveSpeed(), 1000.0);
553 void tst_QQuickListView::changed(const QUrl &source, bool forceLayout)
555 QQuickView *canvas = createView();
558 model.addItem("Fred", "12345");
559 model.addItem("John", "2345");
560 model.addItem("Bob", "54321");
562 QDeclarativeContext *ctxt = canvas->rootContext();
563 ctxt->setContextProperty("testModel", &model);
565 TestObject *testObject = new TestObject;
566 ctxt->setContextProperty("testObject", testObject);
568 canvas->setSource(source);
569 qApp->processEvents();
571 QQuickFlickable *listview = findItem<QQuickFlickable>(canvas->rootObject(), "list");
572 QTRY_VERIFY(listview != 0);
574 QQuickItem *contentItem = listview->contentItem();
575 QTRY_VERIFY(contentItem != 0);
577 // Force a layout, necessary if ListView is completed before VisualDataModel.
579 QCOMPARE(listview->property("count").toInt(), model.count());
581 model.modifyItem(1, "Will", "9876");
582 QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
583 QTRY_VERIFY(name != 0);
584 QTRY_COMPARE(name->text(), model.name(1));
585 QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
586 QTRY_VERIFY(number != 0);
587 QTRY_COMPARE(number->text(), model.number(1));
594 void tst_QQuickListView::inserted(const QUrl &source)
596 QQuickView *canvas = createView();
600 model.addItem("Fred", "12345");
601 model.addItem("John", "2345");
602 model.addItem("Bob", "54321");
604 QDeclarativeContext *ctxt = canvas->rootContext();
605 ctxt->setContextProperty("testModel", &model);
607 TestObject *testObject = new TestObject;
608 ctxt->setContextProperty("testObject", testObject);
610 canvas->setSource(source);
611 //canvas->setSource(testFileUrl("listviewtest.qml")));
612 qApp->processEvents();
614 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
615 QTRY_VERIFY(listview != 0);
617 QQuickItem *contentItem = listview->contentItem();
618 QTRY_VERIFY(contentItem != 0);
620 model.insertItem(1, "Will", "9876");
622 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
623 QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
625 QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
626 QTRY_VERIFY(name != 0);
627 QTRY_COMPARE(name->text(), model.name(1));
628 QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
629 QTRY_VERIFY(number != 0);
630 QTRY_COMPARE(number->text(), model.number(1));
632 // Confirm items positioned correctly
633 for (int i = 0; i < model.count(); ++i) {
634 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
635 QTRY_COMPARE(item->y(), i*20.0);
638 model.insertItem(0, "Foo", "1111"); // zero index, and current item
640 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
641 QTRY_COMPARE(contentItem->childItems().count(), model.count()+1); // assumes all are visible, +1 for the (default) highlight item
643 name = findItem<QQuickText>(contentItem, "textName", 0);
644 QTRY_VERIFY(name != 0);
645 QTRY_COMPARE(name->text(), model.name(0));
646 number = findItem<QQuickText>(contentItem, "textNumber", 0);
647 QTRY_VERIFY(number != 0);
648 QTRY_COMPARE(number->text(), model.number(0));
650 QTRY_COMPARE(listview->currentIndex(), 1);
652 // Confirm items positioned correctly
653 for (int i = 0; i < model.count(); ++i) {
654 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
655 QTRY_COMPARE(item->y(), i*20.0);
658 for (int i = model.count(); i < 30; ++i)
659 model.insertItem(i, "Hello", QString::number(i));
661 listview->setContentY(80);
663 // Insert item outside visible area
664 model.insertItem(1, "Hello", "1324");
666 QTRY_VERIFY(listview->contentY() == 80);
668 // Confirm items positioned correctly
669 for (int i = 5; i < 5+15; ++i) {
670 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
671 if (!item) qWarning() << "Item" << i << "not found";
673 QTRY_COMPARE(item->y(), i*20.0 - 20.0);
676 // QTRY_COMPARE(listview->contentItemHeight(), model.count() * 20.0);
680 model.insertItem(0, "Hello", "1234");
681 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
683 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
685 QCOMPARE(item->y(), 0.);
686 QTRY_VERIFY(listview->contentY() == 0);
693 void tst_QQuickListView::inserted_more()
695 QFETCH(qreal, contentY);
696 QFETCH(int, insertIndex);
697 QFETCH(int, insertCount);
698 QFETCH(qreal, itemsOffsetAfterMove);
702 QQuickView *canvas = createView();
706 for (int i = 0; i < 30; i++)
707 model.addItem("Item" + QString::number(i), "");
709 QDeclarativeContext *ctxt = canvas->rootContext();
710 ctxt->setContextProperty("testModel", &model);
712 TestObject *testObject = new TestObject;
713 ctxt->setContextProperty("testObject", testObject);
715 canvas->setSource(testFileUrl("listviewtest.qml"));
716 qApp->processEvents();
718 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
719 QTRY_VERIFY(listview != 0);
720 QQuickItem *contentItem = listview->contentItem();
721 QTRY_VERIFY(contentItem != 0);
723 listview->setContentY(contentY);
725 QList<QPair<QString, QString> > newData;
726 for (int i=0; i<insertCount; i++)
727 newData << qMakePair(QString("value %1").arg(i), QString::number(i));
728 model.insertItems(insertIndex, newData);
729 QTRY_COMPARE(listview->property("count").toInt(), model.count());
731 // check visibleItems.first() is in correct position
732 QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper", 0);
734 QCOMPARE(item0->y(), itemsOffsetAfterMove);
736 QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
737 int firstVisibleIndex = -1;
738 for (int i=0; i<items.count(); i++) {
739 if (items[i]->y() >= contentY) {
740 QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
741 firstVisibleIndex = e.evaluate().toInt();
745 QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
747 // Confirm items positioned correctly and indexes correct
748 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
749 for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
750 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
751 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
752 QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove);
753 name = findItem<QQuickText>(contentItem, "textName", i);
755 QTRY_COMPARE(name->text(), model.name(i));
756 number = findItem<QQuickText>(contentItem, "textNumber", i);
757 QVERIFY(number != 0);
758 QTRY_COMPARE(number->text(), model.number(i));
765 void tst_QQuickListView::inserted_more_data()
767 QTest::addColumn<qreal>("contentY");
768 QTest::addColumn<int>("insertIndex");
769 QTest::addColumn<int>("insertCount");
770 QTest::addColumn<qreal>("itemsOffsetAfterMove");
772 QTest::newRow("add 1, before visible items")
775 << -20.0; // insert above first visible i.e. 0 is at -20, first visible should not move
777 QTest::newRow("add multiple, before visible")
780 << -20.0 * 3; // again first visible should not move
782 QTest::newRow("add 1, at start of visible, content at start")
787 QTest::newRow("add multiple, start of visible, content at start")
792 QTest::newRow("add 1, at start of visible, content not at start")
797 QTest::newRow("add multiple, at start of visible, content not at start")
803 QTest::newRow("add 1, at end of visible, content at start")
808 QTest::newRow("add 1, at end of visible, content at start")
813 QTest::newRow("add 1, at end of visible, content not at start")
818 QTest::newRow("add multiple, at end of visible, content not at start")
824 QTest::newRow("add 1, after visible, content at start")
829 QTest::newRow("add 1, after visible, content at start")
834 QTest::newRow("add 1, after visible, content not at start")
839 QTest::newRow("add multiple, after visible, content not at start")
845 void tst_QQuickListView::insertBeforeVisible()
847 QFETCH(int, insertIndex);
848 QFETCH(int, insertCount);
849 QFETCH(int, cacheBuffer);
852 QQuickView *canvas = createView();
856 for (int i = 0; i < 30; i++)
857 model.addItem("Item" + QString::number(i), "");
859 QDeclarativeContext *ctxt = canvas->rootContext();
860 ctxt->setContextProperty("testModel", &model);
862 TestObject *testObject = new TestObject;
863 ctxt->setContextProperty("testObject", testObject);
865 canvas->setSource(testFileUrl("listviewtest.qml"));
866 qApp->processEvents();
868 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
869 QTRY_VERIFY(listview != 0);
870 QQuickItem *contentItem = listview->contentItem();
871 QTRY_VERIFY(contentItem != 0);
873 listview->setCacheBuffer(cacheBuffer);
875 // trigger a refill (not just setting contentY) so that the visibleItems grid is updated
876 int firstVisibleIndex = 20; // move to an index where the top item is not visible
877 listview->setContentY(firstVisibleIndex * 20.0);
878 listview->setCurrentIndex(firstVisibleIndex);
880 qApp->processEvents();
881 QTRY_COMPARE(listview->currentIndex(), firstVisibleIndex);
882 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", firstVisibleIndex);
884 QCOMPARE(item->y(), listview->contentY());
886 QList<QPair<QString, QString> > newData;
887 for (int i=0; i<insertCount; i++)
888 newData << qMakePair(QString("value %1").arg(i), QString::number(i));
889 model.insertItems(insertIndex, newData);
890 QTRY_COMPARE(listview->property("count").toInt(), model.count());
892 // now, moving to the top of the view should position the inserted items correctly
893 int itemsOffsetAfterMove = -(insertCount * 20);
894 listview->setCurrentIndex(0);
895 QTRY_COMPARE(listview->currentIndex(), 0);
896 QTRY_COMPARE(listview->contentY(), 0.0 + itemsOffsetAfterMove);
898 // Confirm items positioned correctly and indexes correct
899 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
900 for (int i = 0; i < model.count() && i < itemCount; ++i) {
901 item = findItem<QQuickItem>(contentItem, "wrapper", i);
902 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
903 QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove);
904 name = findItem<QQuickText>(contentItem, "textName", i);
906 QTRY_COMPARE(name->text(), model.name(i));
913 void tst_QQuickListView::insertBeforeVisible_data()
915 QTest::addColumn<int>("insertIndex");
916 QTest::addColumn<int>("insertCount");
917 QTest::addColumn<int>("cacheBuffer");
919 QTest::newRow("insert 1 at 0, 0 buffer") << 0 << 1 << 0;
920 QTest::newRow("insert 1 at 0, 100 buffer") << 0 << 1 << 100;
921 QTest::newRow("insert 1 at 0, 500 buffer") << 0 << 1 << 500;
923 QTest::newRow("insert 1 at 1, 0 buffer") << 1 << 1 << 0;
924 QTest::newRow("insert 1 at 1, 100 buffer") << 1 << 1 << 100;
925 QTest::newRow("insert 1 at 1, 500 buffer") << 1 << 1 << 500;
927 QTest::newRow("insert multiple at 0, 0 buffer") << 0 << 3 << 0;
928 QTest::newRow("insert multiple at 0, 100 buffer") << 0 << 3 << 100;
929 QTest::newRow("insert multiple at 0, 500 buffer") << 0 << 3 << 500;
931 QTest::newRow("insert multiple at 1, 0 buffer") << 1 << 3 << 0;
932 QTest::newRow("insert multiple at 1, 100 buffer") << 1 << 3 << 100;
933 QTest::newRow("insert multiple at 1, 500 buffer") << 1 << 3 << 500;
937 void tst_QQuickListView::removed(const QUrl &source, bool /* animated */)
939 QQuickView *canvas = createView();
942 for (int i = 0; i < 50; i++)
943 model.addItem("Item" + QString::number(i), "");
945 QDeclarativeContext *ctxt = canvas->rootContext();
946 ctxt->setContextProperty("testModel", &model);
948 TestObject *testObject = new TestObject;
949 ctxt->setContextProperty("testObject", testObject);
951 canvas->setSource(source);
953 qApp->processEvents();
955 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
956 QTRY_VERIFY(listview != 0);
958 QQuickItem *contentItem = listview->contentItem();
959 QTRY_VERIFY(contentItem != 0);
962 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
964 QQuickText *name = findItem<QQuickText>(contentItem, "textName", 1);
965 QTRY_VERIFY(name != 0);
966 QTRY_COMPARE(name->text(), model.name(1));
967 QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 1);
968 QTRY_VERIFY(number != 0);
969 QTRY_COMPARE(number->text(), model.number(1));
971 // Confirm items positioned correctly
972 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
973 for (int i = 0; i < model.count() && i < itemCount; ++i) {
974 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
975 if (!item) qWarning() << "Item" << i << "not found";
977 QTRY_VERIFY(item->y() == i*20);
980 // Remove first item (which is the current item);
982 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
984 name = findItem<QQuickText>(contentItem, "textName", 0);
985 QTRY_VERIFY(name != 0);
986 QTRY_COMPARE(name->text(), model.name(0));
987 number = findItem<QQuickText>(contentItem, "textNumber", 0);
988 QTRY_VERIFY(number != 0);
989 QTRY_COMPARE(number->text(), model.number(0));
991 // Confirm items positioned correctly
992 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
993 for (int i = 0; i < model.count() && i < itemCount; ++i) {
994 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
995 if (!item) qWarning() << "Item" << i << "not found";
997 QTRY_COMPARE(item->y(),i*20.0);
1000 // Remove items not visible
1001 model.removeItem(18);
1002 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
1004 // Confirm items positioned correctly
1005 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1006 for (int i = 0; i < model.count() && i < itemCount; ++i) {
1007 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1008 if (!item) qWarning() << "Item" << i << "not found";
1010 QTRY_COMPARE(item->y(),i*20.0);
1013 // Remove items before visible
1014 listview->setContentY(80);
1015 listview->setCurrentIndex(10);
1017 model.removeItem(1); // post: top item will be at 20
1018 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
1020 // Confirm items positioned correctly
1021 for (int i = 2; i < 18; ++i) {
1022 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1023 if (!item) qWarning() << "Item" << i << "not found";
1025 QTRY_COMPARE(item->y(),20+i*20.0);
1028 // Remove current index
1029 QTRY_VERIFY(listview->currentIndex() == 9);
1030 QQuickItem *oldCurrent = listview->currentItem();
1031 model.removeItem(9);
1033 QTRY_COMPARE(listview->currentIndex(), 9);
1034 QTRY_VERIFY(listview->currentItem() != oldCurrent);
1036 listview->setContentY(20); // That's the top now
1037 // let transitions settle.
1040 // Confirm items positioned correctly
1041 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1042 for (int i = 0; i < model.count() && i < itemCount; ++i) {
1043 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1044 if (!item) qWarning() << "Item" << i << "not found";
1046 QTRY_COMPARE(item->y(),20+i*20.0);
1049 // remove current item beyond visible items.
1050 listview->setCurrentIndex(20);
1051 listview->setContentY(40);
1052 model.removeItem(20);
1054 QTRY_COMPARE(listview->currentIndex(), 20);
1055 QTRY_VERIFY(listview->currentItem() != 0);
1057 // remove item before current, but visible
1058 listview->setCurrentIndex(8);
1059 oldCurrent = listview->currentItem();
1060 model.removeItem(6);
1062 QTRY_COMPARE(listview->currentIndex(), 7);
1063 QTRY_VERIFY(listview->currentItem() == oldCurrent);
1065 listview->setContentY(80);
1068 // remove all visible items
1069 model.removeItems(1, 18);
1070 QTRY_COMPARE(listview->count() , model.count());
1072 // Confirm items positioned correctly
1073 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1074 for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
1075 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i+1);
1076 if (!item) qWarning() << "Item" << i+1 << "not found";
1078 QTRY_COMPARE(item->y(),80+i*20.0);
1081 model.removeItems(1, 17);
1082 QTRY_COMPARE(listview->count() , model.count());
1084 model.removeItems(2, 1);
1085 QTRY_COMPARE(listview->count() , model.count());
1087 model.addItem("New", "1");
1088 QTRY_COMPARE(listview->count() , model.count());
1090 QTRY_VERIFY(name = findItem<QQuickText>(contentItem, "textName", model.count()-1));
1091 QCOMPARE(name->text(), QString("New"));
1093 // Add some more items so that we don't run out
1095 for (int i = 0; i < 50; i++)
1096 model.addItem("Item" + QString::number(i), "");
1098 // QTBUG-QTBUG-20575
1099 listview->setCurrentIndex(0);
1100 listview->setContentY(30);
1101 model.removeItem(0);
1102 QTRY_VERIFY(name = findItem<QQuickText>(contentItem, "textName", 0));
1104 // QTBUG-19198 move to end and remove all visible items one at a time.
1105 listview->positionViewAtEnd();
1106 for (int i = 0; i < 18; ++i)
1107 model.removeItems(model.count() - 1, 1);
1108 QTRY_VERIFY(findItems<QQuickItem>(contentItem, "wrapper").count() > 16);
1115 void tst_QQuickListView::removed_more(const QUrl &source)
1117 QFETCH(qreal, contentY);
1118 QFETCH(int, removeIndex);
1119 QFETCH(int, removeCount);
1120 QFETCH(qreal, itemsOffsetAfterMove);
1124 QQuickView *canvas = createView();
1128 for (int i = 0; i < 30; i++)
1129 model.addItem("Item" + QString::number(i), "");
1131 QDeclarativeContext *ctxt = canvas->rootContext();
1132 ctxt->setContextProperty("testModel", &model);
1134 TestObject *testObject = new TestObject;
1135 ctxt->setContextProperty("testObject", testObject);
1137 canvas->setSource(source);
1138 qApp->processEvents();
1140 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1141 QTRY_VERIFY(listview != 0);
1142 QQuickItem *contentItem = listview->contentItem();
1143 QTRY_VERIFY(contentItem != 0);
1145 listview->setContentY(contentY);
1146 QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
1148 // wait for refill (after refill, items above the firstVisibleIndex-1 should not be rendered)
1149 int firstVisibleIndex = contentY / 20;
1150 if (firstVisibleIndex - 2 >= 0)
1151 QTRY_VERIFY(!findItem<QQuickText>(contentItem, "textName", firstVisibleIndex - 2));
1153 model.removeItems(removeIndex, removeCount);
1154 QTRY_COMPARE(listview->property("count").toInt(), model.count());
1156 // check visibleItems.first() is in correct position
1157 QQuickItem *item0 = findItem<QQuickItem>(contentItem, "wrapper", 0);
1159 QCOMPARE(item0->y(), itemsOffsetAfterMove);
1161 QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
1162 for (int i=0; i<items.count(); i++) {
1163 if (items[i]->y() >= contentY) {
1164 QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
1165 firstVisibleIndex = e.evaluate().toInt();
1169 QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
1171 // Confirm items positioned correctly and indexes correct
1172 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1173 for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
1174 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1175 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1176 QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove);
1177 name = findItem<QQuickText>(contentItem, "textName", i);
1179 QTRY_COMPARE(name->text(), model.name(i));
1180 number = findItem<QQuickText>(contentItem, "textNumber", i);
1181 QVERIFY(number != 0);
1182 QTRY_COMPARE(number->text(), model.number(i));
1189 void tst_QQuickListView::removed_more_data()
1191 QTest::addColumn<qreal>("contentY");
1192 QTest::addColumn<int>("removeIndex");
1193 QTest::addColumn<int>("removeCount");
1194 QTest::addColumn<qreal>("itemsOffsetAfterMove");
1196 QTest::newRow("remove 1, before visible items")
1197 << 80.0 // show 4-19
1199 << 20.0; // visible items slide down by 1 item so that first visible does not move
1201 QTest::newRow("remove multiple, all before visible items")
1206 QTest::newRow("remove multiple, all before visible items, remove item 0")
1211 // remove 1,2,3 before the visible pos, 0 moves down to just before the visible pos,
1212 // items 4,5 are removed from view, item 6 slides up to original pos of item 4 (80px)
1213 QTest::newRow("remove multiple, mix of items from before and within visible items")
1216 << 20.0 * 3; // adjust for the 3 items removed before the visible
1218 QTest::newRow("remove multiple, mix of items from before and within visible items, remove item 0")
1221 << 20.0 * 4; // adjust for the 3 items removed before the visible
1224 QTest::newRow("remove 1, from start of visible, content at start")
1229 QTest::newRow("remove multiple, from start of visible, content at start")
1234 QTest::newRow("remove 1, from start of visible, content not at start")
1235 << 80.0 // show 4-19
1239 QTest::newRow("remove multiple, from start of visible, content not at start")
1240 << 80.0 // show 4-19
1245 QTest::newRow("remove 1, from middle of visible, content at start")
1250 QTest::newRow("remove multiple, from middle of visible, content at start")
1255 QTest::newRow("remove 1, from middle of visible, content not at start")
1256 << 80.0 // show 4-19
1260 QTest::newRow("remove multiple, from middle of visible, content not at start")
1261 << 80.0 // show 4-19
1266 QTest::newRow("remove 1, after visible, content at start")
1271 QTest::newRow("remove multiple, after visible, content at start")
1276 QTest::newRow("remove 1, after visible, content not at middle")
1277 << 80.0 // show 4-19
1281 QTest::newRow("remove multiple, after visible, content not at start")
1282 << 80.0 // show 4-19
1286 QTest::newRow("remove multiple, mix of items from within and after visible items")
1293 void tst_QQuickListView::clear(const QUrl &source)
1295 QQuickView *canvas = createView();
1298 for (int i = 0; i < 30; i++)
1299 model.addItem("Item" + QString::number(i), "");
1301 QDeclarativeContext *ctxt = canvas->rootContext();
1302 ctxt->setContextProperty("testModel", &model);
1304 TestObject *testObject = new TestObject;
1305 ctxt->setContextProperty("testObject", testObject);
1307 canvas->setSource(source);
1308 qApp->processEvents();
1310 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1311 QTRY_VERIFY(listview != 0);
1313 QQuickItem *contentItem = listview->contentItem();
1314 QTRY_VERIFY(contentItem != 0);
1318 QTRY_VERIFY(listview->count() == 0);
1319 QTRY_VERIFY(listview->currentItem() == 0);
1320 QTRY_VERIFY(listview->contentY() == 0);
1321 QVERIFY(listview->currentIndex() == -1);
1323 // confirm sanity when adding an item to cleared list
1324 model.addItem("New", "1");
1325 QTRY_VERIFY(listview->count() == 1);
1326 QVERIFY(listview->currentItem() != 0);
1327 QVERIFY(listview->currentIndex() == 0);
1334 void tst_QQuickListView::moved(const QUrl &source)
1336 QFETCH(qreal, contentY);
1340 QFETCH(qreal, itemsOffsetAfterMove);
1344 QQuickView *canvas = createView();
1348 for (int i = 0; i < 30; i++)
1349 model.addItem("Item" + QString::number(i), "");
1351 QDeclarativeContext *ctxt = canvas->rootContext();
1352 ctxt->setContextProperty("testModel", &model);
1354 TestObject *testObject = new TestObject;
1355 ctxt->setContextProperty("testObject", testObject);
1357 canvas->setSource(source);
1358 qApp->processEvents();
1360 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1361 QTRY_VERIFY(listview != 0);
1363 QQuickItem *contentItem = listview->contentItem();
1364 QTRY_VERIFY(contentItem != 0);
1366 QQuickItem *currentItem = listview->currentItem();
1367 QTRY_VERIFY(currentItem != 0);
1369 listview->setContentY(contentY);
1370 QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
1372 model.moveItems(from, to, count);
1373 QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
1375 QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "wrapper");
1376 int firstVisibleIndex = -1;
1377 for (int i=0; i<items.count(); i++) {
1378 if (items[i]->y() >= contentY) {
1379 QDeclarativeExpression e(qmlContext(items[i]), items[i], "index");
1380 firstVisibleIndex = e.evaluate().toInt();
1384 QVERIFY2(firstVisibleIndex >= 0, QTest::toString(firstVisibleIndex));
1386 // Confirm items positioned correctly and indexes correct
1387 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1388 for (int i = firstVisibleIndex; i < model.count() && i < itemCount; ++i) {
1389 if (i >= firstVisibleIndex + 16) // index has moved out of view
1391 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1392 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1393 QTRY_COMPARE(item->y(), i*20.0 + itemsOffsetAfterMove);
1394 name = findItem<QQuickText>(contentItem, "textName", i);
1396 QTRY_COMPARE(name->text(), model.name(i));
1397 number = findItem<QQuickText>(contentItem, "textNumber", i);
1398 QVERIFY(number != 0);
1399 QTRY_COMPARE(number->text(), model.number(i));
1401 // current index should have been updated
1402 if (item == currentItem)
1403 QTRY_COMPARE(listview->currentIndex(), i);
1410 void tst_QQuickListView::moved_data()
1412 QTest::addColumn<qreal>("contentY");
1413 QTest::addColumn<int>("from");
1414 QTest::addColumn<int>("to");
1415 QTest::addColumn<int>("count");
1416 QTest::addColumn<qreal>("itemsOffsetAfterMove");
1418 // model starts with 30 items, each 20px high, in area 320px high
1419 // 16 items should be visible at a time
1420 // itemsOffsetAfterMove should be > 0 whenever items above the visible pos have moved
1422 QTest::newRow("move 1 forwards, within visible items")
1427 QTest::newRow("move 1 forwards, from non-visible -> visible")
1428 << 80.0 // show 4-19
1430 << 20.0; // removed 1 item above the first visible, so item 0 should drop down by 1 to minimize movement
1432 QTest::newRow("move 1 forwards, from non-visible -> visible (move first item)")
1433 << 80.0 // show 4-19
1435 << 20.0; // first item has moved to below item4, everything drops down by size of 1 item
1437 QTest::newRow("move 1 forwards, from visible -> non-visible")
1442 QTest::newRow("move 1 forwards, from visible -> non-visible (move first item)")
1448 QTest::newRow("move 1 backwards, within visible items")
1453 QTest::newRow("move 1 backwards, within visible items (to first index)")
1458 QTest::newRow("move 1 backwards, from non-visible -> visible")
1463 QTest::newRow("move 1 backwards, from non-visible -> visible (move last item)")
1468 QTest::newRow("move 1 backwards, from visible -> non-visible")
1469 << 80.0 // show 4-19
1471 << -20.0; // to minimize movement, item 0 moves to -20, and other items do not move
1473 QTest::newRow("move 1 backwards, from visible -> non-visible (move first item)")
1474 << 80.0 // show 4-19
1476 << -20.0; // to minimize movement, item 16 (now at 0) moves to -20, and other items do not move
1479 QTest::newRow("move multiple forwards, within visible items")
1484 QTest::newRow("move multiple forwards, before visible items")
1485 << 140.0 // show 7-22
1486 << 4 << 5 << 3 // 4,5,6 move to below 7
1487 << 20.0 * 3; // 4,5,6 moved down
1489 QTest::newRow("move multiple forwards, from non-visible -> visible")
1490 << 80.0 // show 4-19
1492 << 20.0 * 3; // moving 3 from above the content y should adjust y positions accordingly
1494 QTest::newRow("move multiple forwards, from non-visible -> visible (move first item)")
1495 << 80.0 // show 4-19
1497 << 20.0 * 3; // moving 3 from above the content y should adjust y positions accordingly
1499 QTest::newRow("move multiple forwards, from visible -> non-visible")
1504 QTest::newRow("move multiple forwards, from visible -> non-visible (move first item)")
1510 QTest::newRow("move multiple backwards, within visible items")
1515 QTest::newRow("move multiple backwards, within visible items (move first item)")
1520 QTest::newRow("move multiple backwards, from non-visible -> visible")
1525 QTest::newRow("move multiple backwards, from non-visible -> visible (move last item)")
1530 QTest::newRow("move multiple backwards, from visible -> non-visible")
1531 << 80.0 // show 4-19
1533 << -20.0 * 3; // to minimize movement, 0 moves by -60, and other items do not move
1535 QTest::newRow("move multiple backwards, from visible -> non-visible (move first item)")
1536 << 80.0 // show 4-19
1538 << -20.0 * 3; // to minimize movement, 16,17,18 move to above item 0, and other items do not move
1543 enum { Inserted, Removed, Moved, SetCurrent } type;
1548 static ListChange insert(int index, int count = 1) { ListChange c = { Inserted, index, count, -1 }; return c; }
1549 static ListChange remove(int index, int count = 1) { ListChange c = { Removed, index, count, -1 }; return c; }
1550 static ListChange move(int index, int to, int count) { ListChange c = { Moved, index, count, to }; return c; }
1551 static ListChange setCurrent(int index) { ListChange c = { SetCurrent, index, -1, -1 }; return c; }
1553 Q_DECLARE_METATYPE(QList<ListChange>)
1555 void tst_QQuickListView::multipleChanges()
1557 QFETCH(int, startCount);
1558 QFETCH(QList<ListChange>, changes);
1559 QFETCH(int, newCount);
1560 QFETCH(int, newCurrentIndex);
1562 QQuickView *canvas = createView();
1566 for (int i = 0; i < startCount; i++)
1567 model.addItem("Item" + QString::number(i), "");
1569 QDeclarativeContext *ctxt = canvas->rootContext();
1570 ctxt->setContextProperty("testModel", &model);
1572 TestObject *testObject = new TestObject;
1573 ctxt->setContextProperty("testObject", testObject);
1575 canvas->setSource(testFileUrl("listviewtest.qml"));
1576 qApp->processEvents();
1578 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1579 QTRY_VERIFY(listview != 0);
1581 for (int i=0; i<changes.count(); i++) {
1582 switch (changes[i].type) {
1583 case ListChange::Inserted:
1585 QList<QPair<QString, QString> > items;
1586 for (int j=changes[i].index; j<changes[i].index + changes[i].count; ++j)
1587 items << qMakePair(QString("new item " + j), QString::number(j));
1588 model.insertItems(changes[i].index, items);
1591 case ListChange::Removed:
1592 model.removeItems(changes[i].index, changes[i].count);
1594 case ListChange::Moved:
1595 model.moveItems(changes[i].index, changes[i].to, changes[i].count);
1597 case ListChange::SetCurrent:
1598 listview->setCurrentIndex(changes[i].index);
1603 QTRY_COMPARE(listview->count(), newCount);
1604 QCOMPARE(listview->count(), model.count());
1605 QTRY_COMPARE(listview->currentIndex(), newCurrentIndex);
1609 QQuickItem *contentItem = listview->contentItem();
1610 QTRY_VERIFY(contentItem != 0);
1611 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1612 for (int i=0; i < model.count() && i < itemCount; ++i) {
1613 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1614 QVERIFY2(item, QTest::toString(QString("Item %1 not found").arg(i)));
1615 name = findItem<QQuickText>(contentItem, "textName", i);
1617 QTRY_COMPARE(name->text(), model.name(i));
1618 number = findItem<QQuickText>(contentItem, "textNumber", i);
1619 QVERIFY(number != 0);
1620 QTRY_COMPARE(number->text(), model.number(i));
1627 void tst_QQuickListView::multipleChanges_data()
1629 QTest::addColumn<int>("startCount");
1630 QTest::addColumn<QList<ListChange> >("changes");
1631 QTest::addColumn<int>("newCount");
1632 QTest::addColumn<int>("newCurrentIndex");
1634 QList<ListChange> changes;
1636 for (int i=1; i<30; i++)
1637 changes << ListChange::remove(0);
1638 QTest::newRow("remove all but 1, first->last") << 30 << changes << 1 << 0;
1640 changes << ListChange::remove(0);
1641 QTest::newRow("remove all") << 30 << changes << 0 << -1;
1644 changes << ListChange::setCurrent(29);
1645 for (int i=29; i>0; i--)
1646 changes << ListChange::remove(i);
1647 QTest::newRow("remove last (current) -> first") << 30 << changes << 1 << 0;
1649 QTest::newRow("remove then insert at 0") << 10 << (QList<ListChange>()
1650 << ListChange::remove(0, 1)
1651 << ListChange::insert(0, 1)
1654 QTest::newRow("remove then insert at non-zero index") << 10 << (QList<ListChange>()
1655 << ListChange::setCurrent(2)
1656 << ListChange::remove(2, 1)
1657 << ListChange::insert(2, 1)
1660 QTest::newRow("remove current then insert below it") << 10 << (QList<ListChange>()
1661 << ListChange::setCurrent(1)
1662 << ListChange::remove(1, 3)
1663 << ListChange::insert(2, 2)
1666 QTest::newRow("remove current index then move it down") << 10 << (QList<ListChange>()
1667 << ListChange::setCurrent(2)
1668 << ListChange::remove(1, 3)
1669 << ListChange::move(1, 5, 1)
1672 QTest::newRow("remove current index then move it up") << 10 << (QList<ListChange>()
1673 << ListChange::setCurrent(5)
1674 << ListChange::remove(4, 3)
1675 << ListChange::move(4, 1, 1)
1679 QTest::newRow("insert multiple times") << 0 << (QList<ListChange>()
1680 << ListChange::insert(0, 2)
1681 << ListChange::insert(0, 4)
1682 << ListChange::insert(0, 6)
1685 QTest::newRow("insert multiple times with current index changes") << 0 << (QList<ListChange>()
1686 << ListChange::insert(0, 2)
1687 << ListChange::insert(0, 4)
1688 << ListChange::insert(0, 6)
1689 << ListChange::setCurrent(3)
1690 << ListChange::insert(3, 2)
1693 QTest::newRow("insert and remove all") << 0 << (QList<ListChange>()
1694 << ListChange::insert(0, 30)
1695 << ListChange::remove(0, 30)
1698 QTest::newRow("insert and remove current") << 30 << (QList<ListChange>()
1699 << ListChange::insert(1)
1700 << ListChange::setCurrent(1)
1701 << ListChange::remove(1)
1704 QTest::newRow("insert before 0, then remove cross section of new and old items") << 10 << (QList<ListChange>()
1705 << ListChange::insert(0, 10)
1706 << ListChange::remove(5, 10)
1709 QTest::newRow("insert multiple, then move new items to end") << 10 << (QList<ListChange>()
1710 << ListChange::insert(0, 3)
1711 << ListChange::move(0, 10, 3)
1714 QTest::newRow("insert multiple, then move new and some old items to end") << 10 << (QList<ListChange>()
1715 << ListChange::insert(0, 3)
1716 << ListChange::move(0, 8, 5)
1719 QTest::newRow("insert multiple at end, then move new and some old items to start") << 10 << (QList<ListChange>()
1720 << ListChange::setCurrent(9)
1721 << ListChange::insert(10, 3)
1722 << ListChange::move(8, 0, 5)
1726 QTest::newRow("move back and forth to same index") << 10 << (QList<ListChange>()
1727 << ListChange::setCurrent(1)
1728 << ListChange::move(1, 2, 2)
1729 << ListChange::move(2, 1, 2)
1732 QTest::newRow("move forwards then back") << 10 << (QList<ListChange>()
1733 << ListChange::setCurrent(2)
1734 << ListChange::move(1, 2, 3)
1735 << ListChange::move(3, 0, 5)
1738 QTest::newRow("move current, then remove it") << 10 << (QList<ListChange>()
1739 << ListChange::setCurrent(5)
1740 << ListChange::move(5, 0, 1)
1741 << ListChange::remove(0)
1744 QTest::newRow("move current, then insert before it") << 10 << (QList<ListChange>()
1745 << ListChange::setCurrent(5)
1746 << ListChange::move(5, 0, 1)
1747 << ListChange::insert(0)
1750 QTest::newRow("move multiple, then remove them") << 10 << (QList<ListChange>()
1751 << ListChange::setCurrent(1)
1752 << ListChange::move(5, 1, 3)
1753 << ListChange::remove(1, 3)
1756 QTest::newRow("move multiple, then insert before them") << 10 << (QList<ListChange>()
1757 << ListChange::setCurrent(5)
1758 << ListChange::move(5, 1, 3)
1759 << ListChange::insert(1, 5)
1762 QTest::newRow("move multiple, then insert after them") << 10 << (QList<ListChange>()
1763 << ListChange::setCurrent(3)
1764 << ListChange::move(0, 1, 2)
1765 << ListChange::insert(3, 5)
1769 QTest::newRow("clear current") << 0 << (QList<ListChange>()
1770 << ListChange::insert(0, 5)
1771 << ListChange::setCurrent(-1)
1772 << ListChange::remove(0, 5)
1773 << ListChange::insert(0, 5)
1777 void tst_QQuickListView::swapWithFirstItem()
1779 QQuickView *canvas = createView();
1783 for (int i = 0; i < 30; i++)
1784 model.addItem("Item" + QString::number(i), "");
1786 QDeclarativeContext *ctxt = canvas->rootContext();
1787 ctxt->setContextProperty("testModel", &model);
1789 TestObject *testObject = new TestObject;
1790 ctxt->setContextProperty("testObject", testObject);
1792 canvas->setSource(testFileUrl("listviewtest.qml"));
1793 qApp->processEvents();
1795 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1796 QTRY_VERIFY(listview != 0);
1798 // ensure content position is stable
1799 listview->setContentY(0);
1800 model.moveItem(1, 0);
1801 QTRY_VERIFY(listview->contentY() == 0);
1807 void tst_QQuickListView::enforceRange()
1809 QQuickView *canvas = createView();
1812 for (int i = 0; i < 30; i++)
1813 model.addItem("Item" + QString::number(i), "");
1815 QDeclarativeContext *ctxt = canvas->rootContext();
1816 ctxt->setContextProperty("testModel", &model);
1818 canvas->setSource(testFileUrl("listview-enforcerange.qml"));
1819 qApp->processEvents();
1821 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1822 QTRY_VERIFY(listview != 0);
1824 QTRY_COMPARE(listview->preferredHighlightBegin(), 100.0);
1825 QTRY_COMPARE(listview->preferredHighlightEnd(), 100.0);
1826 QTRY_COMPARE(listview->highlightRangeMode(), QQuickListView::StrictlyEnforceRange);
1828 QQuickItem *contentItem = listview->contentItem();
1829 QTRY_VERIFY(contentItem != 0);
1831 // view should be positioned at the top of the range.
1832 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
1834 QTRY_COMPARE(listview->contentY(), -100.0);
1836 QQuickText *name = findItem<QQuickText>(contentItem, "textName", 0);
1837 QTRY_VERIFY(name != 0);
1838 QTRY_COMPARE(name->text(), model.name(0));
1839 QQuickText *number = findItem<QQuickText>(contentItem, "textNumber", 0);
1840 QTRY_VERIFY(number != 0);
1841 QTRY_COMPARE(number->text(), model.number(0));
1843 // Check currentIndex is updated when contentItem moves
1844 listview->setContentY(20);
1846 QTRY_COMPARE(listview->currentIndex(), 6);
1850 for (int i = 0; i < 5; i++)
1851 model2.addItem("Item" + QString::number(i), "");
1853 ctxt->setContextProperty("testModel", &model2);
1854 QCOMPARE(listview->count(), 5);
1859 void tst_QQuickListView::enforceRange_withoutHighlight()
1862 // If no highlight is set but StrictlyEnforceRange is used, the content should still move
1863 // to the correct position (i.e. to the next/previous item, not next/previous section)
1864 // when moving up/down via incrementCurrentIndex() and decrementCurrentIndex()
1866 QQuickView *canvas = createView();
1871 model.addItem("Item 0", "a");
1872 model.addItem("Item 1", "b");
1873 model.addItem("Item 2", "b");
1874 model.addItem("Item 3", "c");
1876 QDeclarativeContext *ctxt = canvas->rootContext();
1877 ctxt->setContextProperty("testModel", &model);
1879 canvas->setSource(testFileUrl("listview-enforcerange-nohighlight.qml"));
1880 qApp->processEvents();
1882 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1883 QTRY_VERIFY(listview != 0);
1885 qreal expectedPos = -100.0;
1887 expectedPos += 10.0; // scroll past 1st section's delegate (10px height)
1888 QTRY_COMPARE(listview->contentY(), expectedPos);
1890 expectedPos += 20 + 10; // scroll past 1st section and section delegate of 2nd section
1891 QTest::keyClick(canvas, Qt::Key_Down);
1893 QTRY_COMPARE(listview->contentY(), expectedPos);
1895 expectedPos += 20; // scroll past 1st item of 2nd section
1896 QTest::keyClick(canvas, Qt::Key_Down);
1897 QTRY_COMPARE(listview->contentY(), expectedPos);
1899 expectedPos += 20 + 10; // scroll past 2nd item of 2nd section and section delegate of 3rd section
1900 QTest::keyClick(canvas, Qt::Key_Down);
1901 QTRY_COMPARE(listview->contentY(), expectedPos);
1906 void tst_QQuickListView::spacing()
1908 QQuickView *canvas = createView();
1912 for (int i = 0; i < 30; i++)
1913 model.addItem("Item" + QString::number(i), "");
1915 QDeclarativeContext *ctxt = canvas->rootContext();
1916 ctxt->setContextProperty("testModel", &model);
1918 TestObject *testObject = new TestObject;
1919 ctxt->setContextProperty("testObject", testObject);
1921 canvas->setSource(testFileUrl("listviewtest.qml"));
1922 qApp->processEvents();
1924 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1925 QTRY_VERIFY(listview != 0);
1927 QQuickItem *contentItem = listview->contentItem();
1928 QTRY_VERIFY(contentItem != 0);
1930 // Confirm items positioned correctly
1931 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1932 for (int i = 0; i < model.count() && i < itemCount; ++i) {
1933 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1934 if (!item) qWarning() << "Item" << i << "not found";
1936 QTRY_VERIFY(item->y() == i*20);
1939 listview->setSpacing(10);
1940 QTRY_VERIFY(listview->spacing() == 10);
1942 // Confirm items positioned correctly
1943 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1944 for (int i = 0; i < model.count() && i < itemCount; ++i) {
1945 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1946 if (!item) qWarning() << "Item" << i << "not found";
1948 QTRY_VERIFY(item->y() == i*30);
1951 listview->setSpacing(0);
1953 // Confirm items positioned correctly
1954 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1955 for (int i = 0; i < model.count() && i < itemCount; ++i) {
1956 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1957 if (!item) qWarning() << "Item" << i << "not found";
1959 QTRY_COMPARE(item->y(), i*20.0);
1966 template <typename T>
1967 void tst_QQuickListView::sections(const QUrl &source)
1969 QQuickView *canvas = createView();
1973 for (int i = 0; i < 30; i++)
1974 model.addItem("Item" + QString::number(i), QString::number(i/5));
1976 QDeclarativeContext *ctxt = canvas->rootContext();
1977 ctxt->setContextProperty("testModel", &model);
1979 canvas->setSource(source);
1980 qApp->processEvents();
1982 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
1983 QTRY_VERIFY(listview != 0);
1985 QQuickItem *contentItem = listview->contentItem();
1986 QTRY_VERIFY(contentItem != 0);
1988 // Confirm items positioned correctly
1989 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
1990 for (int i = 0; i < model.count() && i < itemCount; ++i) {
1991 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
1993 QTRY_COMPARE(item->y(), qreal(i*20 + ((i+4)/5) * 20));
1994 QQuickText *next = findItem<QQuickText>(item, "nextSection");
1995 QCOMPARE(next->text().toInt(), (i+1)/5);
1998 QSignalSpy currentSectionChangedSpy(listview, SIGNAL(currentSectionChanged()));
2000 // Remove section boundary
2001 model.removeItem(5);
2002 QTRY_COMPARE(listview->count(), model.count());
2004 // New section header created
2005 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 5);
2007 QTRY_COMPARE(item->height(), 40.0);
2009 model.insertItem(3, "New Item", "0");
2010 QTRY_COMPARE(listview->count(), model.count());
2012 // Section header moved
2013 item = findItem<QQuickItem>(contentItem, "wrapper", 5);
2015 QTRY_COMPARE(item->height(), 20.0);
2017 item = findItem<QQuickItem>(contentItem, "wrapper", 6);
2019 QTRY_COMPARE(item->height(), 40.0);
2021 // insert item which will become a section header
2022 model.insertItem(6, "Replace header", "1");
2023 QTRY_COMPARE(listview->count(), model.count());
2025 item = findItem<QQuickItem>(contentItem, "wrapper", 6);
2027 QTRY_COMPARE(item->height(), 40.0);
2029 item = findItem<QQuickItem>(contentItem, "wrapper", 7);
2031 QTRY_COMPARE(item->height(), 20.0);
2033 QTRY_COMPARE(listview->currentSection(), QString("0"));
2035 listview->setContentY(140);
2036 QTRY_COMPARE(listview->currentSection(), QString("1"));
2038 QTRY_COMPARE(currentSectionChangedSpy.count(), 1);
2040 listview->setContentY(20);
2041 QTRY_COMPARE(listview->currentSection(), QString("0"));
2043 QTRY_COMPARE(currentSectionChangedSpy.count(), 2);
2045 item = findItem<QQuickItem>(contentItem, "wrapper", 1);
2047 QTRY_COMPARE(item->height(), 20.0);
2049 // check that headers change when item changes
2050 listview->setContentY(0);
2051 model.modifyItem(0, "changed", "2");
2054 item = findItem<QQuickItem>(contentItem, "wrapper", 1);
2056 QTRY_COMPARE(item->height(), 40.0);
2061 void tst_QQuickListView::sectionsDelegate()
2063 QQuickView *canvas = createView();
2067 for (int i = 0; i < 30; i++)
2068 model.addItem("Item" + QString::number(i), QString::number(i/5));
2070 QDeclarativeContext *ctxt = canvas->rootContext();
2071 ctxt->setContextProperty("testModel", &model);
2073 canvas->setSource(testFileUrl("listview-sections_delegate.qml"));
2074 qApp->processEvents();
2076 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2077 QTRY_VERIFY(listview != 0);
2079 QQuickItem *contentItem = listview->contentItem();
2080 QTRY_VERIFY(contentItem != 0);
2082 // Confirm items positioned correctly
2083 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2084 for (int i = 0; i < model.count() && i < itemCount; ++i) {
2085 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2087 QTRY_COMPARE(item->y(), qreal(i*20 + ((i+5)/5) * 20));
2088 QQuickText *next = findItem<QQuickText>(item, "nextSection");
2089 QCOMPARE(next->text().toInt(), (i+1)/5);
2092 for (int i = 0; i < 3; ++i) {
2093 QQuickItem *item = findItem<QQuickItem>(contentItem, "sect_" + QString::number(i));
2095 QTRY_COMPARE(item->y(), qreal(i*20*6));
2098 model.modifyItem(0, "One", "aaa");
2099 model.modifyItem(1, "Two", "aaa");
2100 model.modifyItem(2, "Three", "aaa");
2101 model.modifyItem(3, "Four", "aaa");
2102 model.modifyItem(4, "Five", "aaa");
2105 for (int i = 0; i < 3; ++i) {
2106 QQuickItem *item = findItem<QQuickItem>(contentItem,
2107 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
2109 QTRY_COMPARE(item->y(), qreal(i*20*6));
2112 // remove section boundary
2113 model.removeItem(5);
2114 QTRY_COMPARE(listview->count(), model.count());
2115 for (int i = 0; i < 3; ++i) {
2116 QQuickItem *item = findItem<QQuickItem>(contentItem,
2117 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
2122 QList<QQuickItem*> items = findItems<QQuickItem>(contentItem, "sect_1");
2123 QCOMPARE(items.count(), 1);
2126 model.modifyItem(0, "One", "aaa");
2127 model.modifyItem(1, "One", "aaa");
2128 model.modifyItem(2, "One", "aaa");
2129 model.modifyItem(3, "Four", "aaa");
2130 model.modifyItem(4, "Four", "aaa");
2131 model.modifyItem(5, "Four", "aaa");
2132 model.modifyItem(6, "Five", "aaa");
2133 model.modifyItem(7, "Five", "aaa");
2134 model.modifyItem(8, "Five", "aaa");
2135 model.modifyItem(9, "Two", "aaa");
2136 model.modifyItem(10, "Two", "aaa");
2137 model.modifyItem(11, "Two", "aaa");
2138 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "sect_aaa").count(), 1);
2139 canvas->rootObject()->setProperty("sectionProperty", "name");
2140 // ensure view has settled.
2141 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "sect_Four").count(), 1);
2142 for (int i = 0; i < 4; ++i) {
2143 QQuickItem *item = findItem<QQuickItem>(contentItem,
2144 "sect_" + model.name(i*3));
2146 QTRY_COMPARE(item->y(), qreal(i*20*4));
2150 model.removeItems(10, 20);
2151 // ensure view has settled.
2152 QTRY_COMPARE(findItems<QQuickItem>(contentItem, "wrapper").count(), 10);
2153 // Drag view up beyond bounds
2154 QTest::mousePress(canvas, Qt::LeftButton, 0, QPoint(20,20));
2156 QMouseEvent mv(QEvent::MouseMove, QPoint(20,0), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2157 QGuiApplication::sendEvent(canvas, &mv);
2160 QMouseEvent mv(QEvent::MouseMove, QPoint(20,-50), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2161 QGuiApplication::sendEvent(canvas, &mv);
2164 QMouseEvent mv(QEvent::MouseMove, QPoint(20,-200), Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
2165 QGuiApplication::sendEvent(canvas, &mv);
2167 QTest::mouseRelease(canvas, Qt::LeftButton, 0, QPoint(20,-200));
2168 // view should settle back at 0
2169 QTRY_COMPARE(listview->contentY(), 0.0);
2174 void tst_QQuickListView::sectionsPositioning()
2176 QQuickView *canvas = createView();
2180 for (int i = 0; i < 30; i++)
2181 model.addItem("Item" + QString::number(i), QString::number(i/5));
2183 QDeclarativeContext *ctxt = canvas->rootContext();
2184 ctxt->setContextProperty("testModel", &model);
2186 canvas->setSource(testFileUrl("listview-sections_delegate.qml"));
2187 qApp->processEvents();
2188 canvas->rootObject()->setProperty("sectionPositioning", QVariant(int(QQuickViewSection::InlineLabels | QQuickViewSection::CurrentLabelAtStart | QQuickViewSection::NextLabelAtEnd)));
2190 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2191 QTRY_VERIFY(listview != 0);
2193 QQuickItem *contentItem = listview->contentItem();
2194 QTRY_VERIFY(contentItem != 0);
2196 for (int i = 0; i < 3; ++i) {
2197 QQuickItem *item = findItem<QQuickItem>(contentItem, "sect_" + QString::number(i));
2199 QTRY_COMPARE(item->y(), qreal(i*20*6));
2202 QQuickItem *topItem = findVisibleChild(contentItem, "sect_0"); // section header
2204 QCOMPARE(topItem->y(), 0.);
2206 QQuickItem *bottomItem = findVisibleChild(contentItem, "sect_3"); // section footer
2207 QVERIFY(bottomItem);
2208 QCOMPARE(bottomItem->y(), 300.);
2210 // move down a little and check that section header is at top
2211 listview->setContentY(10);
2212 QCOMPARE(topItem->y(), 0.);
2214 // push the top header up
2215 listview->setContentY(110);
2216 topItem = findVisibleChild(contentItem, "sect_0"); // section header
2218 QCOMPARE(topItem->y(), 100.);
2220 QQuickItem *item = findVisibleChild(contentItem, "sect_1");
2222 QCOMPARE(item->y(), 120.);
2224 bottomItem = findVisibleChild(contentItem, "sect_4"); // section footer
2225 QVERIFY(bottomItem);
2226 QCOMPARE(bottomItem->y(), 410.);
2228 // Move past section 0
2229 listview->setContentY(120);
2230 topItem = findVisibleChild(contentItem, "sect_0"); // section header
2233 // Push section footer down
2234 listview->setContentY(70);
2235 bottomItem = findVisibleChild(contentItem, "sect_4"); // section footer
2236 QVERIFY(bottomItem);
2237 QCOMPARE(bottomItem->y(), 380.);
2239 // Change current section
2240 listview->setContentY(10);
2241 model.modifyItem(0, "One", "aaa");
2242 model.modifyItem(1, "Two", "aaa");
2243 model.modifyItem(2, "Three", "aaa");
2244 model.modifyItem(3, "Four", "aaa");
2245 model.modifyItem(4, "Five", "aaa");
2248 QTRY_COMPARE(listview->currentSection(), QString("aaa"));
2250 for (int i = 0; i < 3; ++i) {
2251 QQuickItem *item = findItem<QQuickItem>(contentItem,
2252 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
2254 QTRY_COMPARE(item->y(), qreal(i*20*6));
2257 topItem = findVisibleChild(contentItem, "sect_aaa"); // section header
2259 QCOMPARE(topItem->y(), 10.);
2261 // remove section boundary
2262 listview->setContentY(120);
2263 model.removeItem(5);
2264 QTRY_COMPARE(listview->count(), model.count());
2265 for (int i = 0; i < 3; ++i) {
2266 QQuickItem *item = findItem<QQuickItem>(contentItem,
2267 "sect_" + (i == 0 ? QString("aaa") : QString::number(i)));
2269 QTRY_COMPARE(item->y(), qreal(i*20*6));
2272 QVERIFY(topItem = findVisibleChild(contentItem, "sect_1"));
2273 QTRY_COMPARE(topItem->y(), 120.);
2275 // Change the next section
2276 listview->setContentY(0);
2277 bottomItem = findVisibleChild(contentItem, "sect_3"); // section footer
2278 QVERIFY(bottomItem);
2279 QTRY_COMPARE(bottomItem->y(), 300.);
2281 model.modifyItem(14, "New", "new");
2283 QTRY_VERIFY(bottomItem = findVisibleChild(contentItem, "sect_new")); // section footer
2284 QTRY_COMPARE(bottomItem->y(), 300.);
2286 // Turn sticky footer off
2287 listview->setContentY(40);
2288 canvas->rootObject()->setProperty("sectionPositioning", QVariant(int(QQuickViewSection::InlineLabels | QQuickViewSection::CurrentLabelAtStart)));
2289 item = findVisibleChild(contentItem, "sect_new"); // inline label restored
2291 QCOMPARE(item->y(), 360.);
2293 // Turn sticky header off
2294 listview->setContentY(30);
2295 canvas->rootObject()->setProperty("sectionPositioning", QVariant(int(QQuickViewSection::InlineLabels)));
2296 item = findVisibleChild(contentItem, "sect_aaa"); // inline label restored
2298 QCOMPARE(item->y(), 0.);
2303 void tst_QQuickListView::currentIndex_delayedItemCreation()
2305 QFETCH(bool, setCurrentToZero);
2307 QQuickView *canvas = createView();
2311 // test currentIndexChanged() is emitted even if currentIndex = 0 on start up
2312 // (since the currentItem will have changed and that shares the same index)
2313 canvas->rootContext()->setContextProperty("setCurrentToZero", setCurrentToZero);
2315 canvas->setSource(testFileUrl("fillModelOnComponentCompleted.qml"));
2316 qApp->processEvents();
2318 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2319 QTRY_VERIFY(listview != 0);
2321 QQuickItem *contentItem = listview->contentItem();
2322 QTRY_VERIFY(contentItem != 0);
2324 QSignalSpy spy(listview, SIGNAL(currentItemChanged()));
2325 QCOMPARE(listview->currentIndex(), 0);
2326 QTRY_COMPARE(spy.count(), 1);
2331 void tst_QQuickListView::currentIndex_delayedItemCreation_data()
2333 QTest::addColumn<bool>("setCurrentToZero");
2335 QTest::newRow("set to 0") << true;
2336 QTest::newRow("don't set to 0") << false;
2339 void tst_QQuickListView::currentIndex()
2342 for (int i = 0; i < 30; i++)
2343 model.addItem("Item" + QString::number(i), QString::number(i));
2345 QQuickView *canvas = new QQuickView(0);
2346 canvas->setGeometry(0,0,240,320);
2348 QDeclarativeContext *ctxt = canvas->rootContext();
2349 ctxt->setContextProperty("testModel", &model);
2350 ctxt->setContextProperty("testWrap", QVariant(false));
2352 QString filename(testFile("listview-initCurrent.qml"));
2353 canvas->setSource(QUrl::fromLocalFile(filename));
2355 qApp->processEvents();
2357 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2358 QTRY_VERIFY(listview != 0);
2360 QQuickItem *contentItem = listview->contentItem();
2361 QTRY_VERIFY(contentItem != 0);
2363 // current item should be 20th item at startup
2364 // and current item should be in view
2365 QCOMPARE(listview->currentIndex(), 20);
2366 QCOMPARE(listview->contentY(), 100.0);
2367 QCOMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 20));
2368 QCOMPARE(listview->highlightItem()->y(), listview->currentItem()->y());
2371 listview->setCurrentIndex(0);
2372 QCOMPARE(listview->currentIndex(), 0);
2373 // confirm that the velocity is updated
2374 QTRY_VERIFY(listview->verticalVelocity() != 0.0);
2376 listview->incrementCurrentIndex();
2377 QCOMPARE(listview->currentIndex(), 1);
2378 listview->decrementCurrentIndex();
2379 QCOMPARE(listview->currentIndex(), 0);
2381 listview->decrementCurrentIndex();
2382 QCOMPARE(listview->currentIndex(), 0);
2385 ctxt->setContextProperty("testWrap", QVariant(true));
2386 QVERIFY(listview->isWrapEnabled());
2388 listview->decrementCurrentIndex();
2389 QCOMPARE(listview->currentIndex(), model.count()-1);
2391 QTRY_COMPARE(listview->contentY(), 280.0);
2393 listview->incrementCurrentIndex();
2394 QCOMPARE(listview->currentIndex(), 0);
2396 QTRY_COMPARE(listview->contentY(), 0.0);
2399 // footer should become visible if it is out of view, and then current index is set to count-1
2400 canvas->rootObject()->setProperty("showFooter", true);
2401 QTRY_VERIFY(listview->footerItem());
2402 listview->setCurrentIndex(model.count()-2);
2403 QTRY_VERIFY(listview->footerItem()->y() > listview->contentY() + listview->height());
2404 listview->setCurrentIndex(model.count()-1);
2405 QTRY_COMPARE(listview->contentY() + listview->height(), (20.0 * model.count()) + listview->footerItem()->height());
2406 canvas->rootObject()->setProperty("showFooter", false);
2408 // header should become visible if it is out of view, and then current index is set to 0
2409 canvas->rootObject()->setProperty("showHeader", true);
2410 QTRY_VERIFY(listview->headerItem());
2411 listview->setCurrentIndex(1);
2412 QTRY_VERIFY(listview->headerItem()->y() + listview->headerItem()->height() < listview->contentY());
2413 listview->setCurrentIndex(0);
2414 QTRY_COMPARE(listview->contentY(), -listview->headerItem()->height());
2415 canvas->rootObject()->setProperty("showHeader", false);
2420 canvas->requestActivateWindow();
2421 QTest::qWaitForWindowShown(canvas);
2422 QTRY_VERIFY(qGuiApp->focusWindow() == canvas);
2424 listview->setCurrentIndex(0);
2426 QTest::keyClick(canvas, Qt::Key_Down);
2427 QCOMPARE(listview->currentIndex(), 1);
2429 QTest::keyClick(canvas, Qt::Key_Up);
2430 QCOMPARE(listview->currentIndex(), 0);
2432 // hold down Key_Down
2433 for (int i=0; i<model.count()-1; i++) {
2434 QTest::simulateEvent(canvas, true, Qt::Key_Down, Qt::NoModifier, "", true);
2435 QTRY_COMPARE(listview->currentIndex(), i+1);
2437 QTest::keyRelease(canvas, Qt::Key_Down);
2438 QTRY_COMPARE(listview->currentIndex(), model.count()-1);
2439 QTRY_COMPARE(listview->contentY(), 280.0);
2442 for (int i=model.count()-1; i > 0; i--) {
2443 QTest::simulateEvent(canvas, true, Qt::Key_Up, Qt::NoModifier, "", true);
2444 QTRY_COMPARE(listview->currentIndex(), i-1);
2446 QTest::keyRelease(canvas, Qt::Key_Up);
2447 QTRY_COMPARE(listview->currentIndex(), 0);
2448 QTRY_COMPARE(listview->contentY(), 0.0);
2451 // turn off auto highlight
2452 listview->setHighlightFollowsCurrentItem(false);
2453 QVERIFY(listview->highlightFollowsCurrentItem() == false);
2455 QVERIFY(listview->highlightItem());
2456 qreal hlPos = listview->highlightItem()->y();
2458 listview->setCurrentIndex(4);
2459 QTRY_COMPARE(listview->highlightItem()->y(), hlPos);
2461 // insert item before currentIndex
2462 listview->setCurrentIndex(28);
2463 model.insertItem(0, "Foo", "1111");
2464 QTRY_COMPARE(canvas->rootObject()->property("current").toInt(), 29);
2466 // check removing highlight by setting currentIndex to -1;
2467 listview->setCurrentIndex(-1);
2469 QCOMPARE(listview->currentIndex(), -1);
2470 QVERIFY(!listview->highlightItem());
2471 QVERIFY(!listview->currentItem());
2476 void tst_QQuickListView::noCurrentIndex()
2479 for (int i = 0; i < 30; i++)
2480 model.addItem("Item" + QString::number(i), QString::number(i));
2482 QQuickView *canvas = new QQuickView(0);
2483 canvas->setGeometry(0,0,240,320);
2485 QDeclarativeContext *ctxt = canvas->rootContext();
2486 ctxt->setContextProperty("testModel", &model);
2488 QString filename(testFile("listview-noCurrent.qml"));
2489 canvas->setSource(QUrl::fromLocalFile(filename));
2491 qApp->processEvents();
2493 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2494 QTRY_VERIFY(listview != 0);
2496 QQuickItem *contentItem = listview->contentItem();
2497 QTRY_VERIFY(contentItem != 0);
2499 // current index should be -1 at startup
2500 // and we should not have a currentItem or highlightItem
2501 QCOMPARE(listview->currentIndex(), -1);
2502 QCOMPARE(listview->contentY(), 0.0);
2503 QVERIFY(!listview->highlightItem());
2504 QVERIFY(!listview->currentItem());
2506 listview->setCurrentIndex(2);
2507 QCOMPARE(listview->currentIndex(), 2);
2508 QVERIFY(listview->highlightItem());
2509 QVERIFY(listview->currentItem());
2514 void tst_QQuickListView::itemList()
2516 QQuickView *canvas = createView();
2518 canvas->setSource(testFileUrl("itemlist.qml"));
2519 qApp->processEvents();
2521 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "view");
2522 QTRY_VERIFY(listview != 0);
2524 QQuickItem *contentItem = listview->contentItem();
2525 QTRY_VERIFY(contentItem != 0);
2527 QQuickVisualItemModel *model = canvas->rootObject()->findChild<QQuickVisualItemModel*>("itemModel");
2528 QTRY_VERIFY(model != 0);
2530 QTRY_VERIFY(model->count() == 3);
2531 QTRY_COMPARE(listview->currentIndex(), 0);
2533 QQuickItem *item = findItem<QQuickItem>(contentItem, "item1");
2535 QTRY_COMPARE(item->x(), 0.0);
2536 QCOMPARE(item->height(), listview->height());
2538 QQuickText *text = findItem<QQuickText>(contentItem, "text1");
2540 QTRY_COMPARE(text->text(), QLatin1String("index: 0"));
2542 listview->setCurrentIndex(2);
2544 item = findItem<QQuickItem>(contentItem, "item3");
2546 QTRY_COMPARE(item->x(), 480.0);
2548 text = findItem<QQuickText>(contentItem, "text3");
2550 QTRY_COMPARE(text->text(), QLatin1String("index: 2"));
2555 void tst_QQuickListView::cacheBuffer()
2557 QQuickView *canvas = createView();
2560 for (int i = 0; i < 90; i++)
2561 model.addItem("Item" + QString::number(i), "");
2563 QDeclarativeContext *ctxt = canvas->rootContext();
2564 ctxt->setContextProperty("testModel", &model);
2566 TestObject *testObject = new TestObject;
2567 ctxt->setContextProperty("testObject", testObject);
2569 canvas->setSource(testFileUrl("listviewtest.qml"));
2571 qApp->processEvents();
2573 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2574 QTRY_VERIFY(listview != 0);
2576 QQuickItem *contentItem = listview->contentItem();
2577 QTRY_VERIFY(contentItem != 0);
2578 QTRY_VERIFY(listview->delegate() != 0);
2579 QTRY_VERIFY(listview->model() != 0);
2580 QTRY_VERIFY(listview->highlight() != 0);
2582 // Confirm items positioned correctly
2583 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2584 for (int i = 0; i < model.count() && i < itemCount; ++i) {
2585 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2586 if (!item) qWarning() << "Item" << i << "not found";
2588 QTRY_VERIFY(item->y() == i*20);
2591 QDeclarativeIncubationController controller;
2592 canvas->engine()->setIncubationController(&controller);
2594 testObject->setCacheBuffer(200);
2595 QTRY_VERIFY(listview->cacheBuffer() == 200);
2597 // items will be created one at a time
2598 for (int i = itemCount; i < qMin(itemCount+10,model.count()); ++i) {
2599 QVERIFY(findItem<QQuickItem>(listview, "wrapper", i) == 0);
2600 QQuickItem *item = 0;
2603 controller.incubateWhile(&b);
2604 item = findItem<QQuickItem>(listview, "wrapper", i);
2610 controller.incubateWhile(&b);
2613 int newItemCount = 0;
2614 newItemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2616 // Confirm items positioned correctly
2617 for (int i = 0; i < model.count() && i < newItemCount; ++i) {
2618 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2619 if (!item) qWarning() << "Item" << i << "not found";
2621 QTRY_VERIFY(item->y() == i*20);
2624 // move view and confirm items in view are visible immediately and outside are created async
2625 listview->setContentY(300);
2627 for (int i = 15; i < 32; ++i) {
2628 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2629 if (!item) qWarning() << "Item" << i << "not found";
2631 QVERIFY(item->y() == i*20);
2634 QVERIFY(findItem<QQuickItem>(listview, "wrapper", 32) == 0);
2636 // ensure buffered items are created
2637 for (int i = 32; i < qMin(41,model.count()); ++i) {
2638 QQuickItem *item = 0;
2640 qGuiApp->processEvents(); // allow refill to happen
2642 controller.incubateWhile(&b);
2643 item = findItem<QQuickItem>(listview, "wrapper", i);
2649 controller.incubateWhile(&b);
2656 void tst_QQuickListView::positionViewAtIndex()
2658 QQuickView *canvas = createView();
2661 for (int i = 0; i < 40; i++)
2662 model.addItem("Item" + QString::number(i), "");
2664 QDeclarativeContext *ctxt = canvas->rootContext();
2665 ctxt->setContextProperty("testModel", &model);
2667 TestObject *testObject = new TestObject;
2668 ctxt->setContextProperty("testObject", testObject);
2670 canvas->setSource(testFileUrl("listviewtest.qml"));
2671 qApp->processEvents();
2673 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2674 QTRY_VERIFY(listview != 0);
2676 QQuickItem *contentItem = listview->contentItem();
2677 QTRY_VERIFY(contentItem != 0);
2679 // Confirm items positioned correctly
2680 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2681 for (int i = 0; i < model.count() && i < itemCount; ++i) {
2682 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2683 if (!item) qWarning() << "Item" << i << "not found";
2685 QTRY_COMPARE(item->y(), i*20.);
2688 // Position on a currently visible item
2689 listview->positionViewAtIndex(3, QQuickListView::Beginning);
2690 QTRY_COMPARE(listview->contentY(), 60.);
2692 // Confirm items positioned correctly
2693 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2694 for (int i = 3; i < model.count() && i < itemCount-3-1; ++i) {
2695 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2696 if (!item) qWarning() << "Item" << i << "not found";
2698 QTRY_COMPARE(item->y(), i*20.);
2701 // Position on an item beyond the visible items
2702 listview->positionViewAtIndex(22, QQuickListView::Beginning);
2703 QTRY_COMPARE(listview->contentY(), 440.);
2705 // Confirm items positioned correctly
2706 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2707 for (int i = 22; i < model.count() && i < itemCount-22-1; ++i) {
2708 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2709 if (!item) qWarning() << "Item" << i << "not found";
2711 QTRY_COMPARE(item->y(), i*20.);
2714 // Position on an item that would leave empty space if positioned at the top
2715 listview->positionViewAtIndex(28, QQuickListView::Beginning);
2716 QTRY_COMPARE(listview->contentY(), 480.);
2718 // Confirm items positioned correctly
2719 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2720 for (int i = 24; i < model.count() && i < itemCount-24-1; ++i) {
2721 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2722 if (!item) qWarning() << "Item" << i << "not found";
2724 QTRY_COMPARE(item->y(), i*20.);
2727 // Position at the beginning again
2728 listview->positionViewAtIndex(0, QQuickListView::Beginning);
2729 QTRY_COMPARE(listview->contentY(), 0.);
2731 // Confirm items positioned correctly
2732 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2733 for (int i = 0; i < model.count() && i < itemCount-1; ++i) {
2734 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2735 if (!item) qWarning() << "Item" << i << "not found";
2737 QTRY_COMPARE(item->y(), i*20.);
2740 // Position at End using last index
2741 listview->positionViewAtIndex(model.count()-1, QQuickListView::End);
2742 QTRY_COMPARE(listview->contentY(), 480.);
2744 // Confirm items positioned correctly
2745 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
2746 for (int i = 24; i < model.count(); ++i) {
2747 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
2748 if (!item) qWarning() << "Item" << i << "not found";
2750 QTRY_COMPARE(item->y(), i*20.);
2754 listview->positionViewAtIndex(20, QQuickListView::End);
2755 QTRY_COMPARE(listview->contentY(), 100.);
2757 // Position in Center
2758 listview->positionViewAtIndex(15, QQuickListView::Center);
2759 QTRY_COMPARE(listview->contentY(), 150.);
2761 // Ensure at least partially visible
2762 listview->positionViewAtIndex(15, QQuickListView::Visible);
2763 QTRY_COMPARE(listview->contentY(), 150.);
2765 listview->setContentY(302);
2766 listview->positionViewAtIndex(15, QQuickListView::Visible);
2767 QTRY_COMPARE(listview->contentY(), 302.);
2769 listview->setContentY(320);
2770 listview->positionViewAtIndex(15, QQuickListView::Visible);
2771 QTRY_COMPARE(listview->contentY(), 300.);
2773 listview->setContentY(85);
2774 listview->positionViewAtIndex(20, QQuickListView::Visible);
2775 QTRY_COMPARE(listview->contentY(), 85.);
2777 listview->setContentY(75);
2778 listview->positionViewAtIndex(20, QQuickListView::Visible);
2779 QTRY_COMPARE(listview->contentY(), 100.);
2781 // Ensure completely visible
2782 listview->setContentY(120);
2783 listview->positionViewAtIndex(20, QQuickListView::Contain);
2784 QTRY_COMPARE(listview->contentY(), 120.);
2786 listview->setContentY(302);
2787 listview->positionViewAtIndex(15, QQuickListView::Contain);
2788 QTRY_COMPARE(listview->contentY(), 300.);
2790 listview->setContentY(85);
2791 listview->positionViewAtIndex(20, QQuickListView::Contain);
2792 QTRY_COMPARE(listview->contentY(), 100.);
2794 // positionAtBeginnging
2795 listview->positionViewAtBeginning();
2796 QTRY_COMPARE(listview->contentY(), 0.);
2798 listview->setContentY(80);
2799 canvas->rootObject()->setProperty("showHeader", true);
2800 listview->positionViewAtBeginning();
2801 QTRY_COMPARE(listview->contentY(), -30.);
2804 listview->positionViewAtEnd();
2805 QTRY_COMPARE(listview->contentY(), 480.); // 40*20 - 320
2807 listview->setContentY(80);
2808 canvas->rootObject()->setProperty("showFooter", true);
2809 listview->positionViewAtEnd();
2810 QTRY_COMPARE(listview->contentY(), 510.);
2812 // set current item to outside visible view, position at beginning
2813 // and ensure highlight moves to current item
2814 listview->setCurrentIndex(1);
2815 listview->positionViewAtBeginning();
2816 QTRY_COMPARE(listview->contentY(), -30.);
2817 QVERIFY(listview->highlightItem());
2818 QCOMPARE(listview->highlightItem()->y(), 20.);
2824 void tst_QQuickListView::resetModel()
2826 QQuickView *canvas = createView();
2828 QStringList strings;
2829 strings << "one" << "two" << "three";
2830 QStringListModel model(strings);
2832 QDeclarativeContext *ctxt = canvas->rootContext();
2833 ctxt->setContextProperty("testModel", &model);
2835 canvas->setSource(testFileUrl("displaylist.qml"));
2836 qApp->processEvents();
2838 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
2839 QTRY_VERIFY(listview != 0);
2841 QQuickItem *contentItem = listview->contentItem();
2842 QTRY_VERIFY(contentItem != 0);
2844 QTRY_COMPARE(listview->count(), model.rowCount());
2846 for (int i = 0; i < model.rowCount(); ++i) {
2847 QQuickText *display = findItem<QQuickText>(contentItem, "displayText", i);
2848 QTRY_VERIFY(display != 0);
2849 QTRY_COMPARE(display->text(), strings.at(i));
2853 strings << "four" << "five" << "six" << "seven";
2854 model.setStringList(strings);
2856 QTRY_COMPARE(listview->count(), model.rowCount());
2858 for (int i = 0; i < model.rowCount(); ++i) {
2859 QQuickText *display = findItem<QQuickText>(contentItem, "displayText", i);
2860 QTRY_VERIFY(display != 0);
2861 QTRY_COMPARE(display->text(), strings.at(i));
2867 void tst_QQuickListView::propertyChanges()
2869 QQuickView *canvas = createView();
2870 QTRY_VERIFY(canvas);
2871 canvas->setSource(testFileUrl("propertychangestest.qml"));
2873 QQuickListView *listView = canvas->rootObject()->findChild<QQuickListView*>("listView");
2874 QTRY_VERIFY(listView);
2876 QSignalSpy highlightFollowsCurrentItemSpy(listView, SIGNAL(highlightFollowsCurrentItemChanged()));
2877 QSignalSpy preferredHighlightBeginSpy(listView, SIGNAL(preferredHighlightBeginChanged()));
2878 QSignalSpy preferredHighlightEndSpy(listView, SIGNAL(preferredHighlightEndChanged()));
2879 QSignalSpy highlightRangeModeSpy(listView, SIGNAL(highlightRangeModeChanged()));
2880 QSignalSpy keyNavigationWrapsSpy(listView, SIGNAL(keyNavigationWrapsChanged()));
2881 QSignalSpy cacheBufferSpy(listView, SIGNAL(cacheBufferChanged()));
2882 QSignalSpy snapModeSpy(listView, SIGNAL(snapModeChanged()));
2884 QTRY_COMPARE(listView->highlightFollowsCurrentItem(), true);
2885 QTRY_COMPARE(listView->preferredHighlightBegin(), 0.0);
2886 QTRY_COMPARE(listView->preferredHighlightEnd(), 0.0);
2887 QTRY_COMPARE(listView->highlightRangeMode(), QQuickListView::ApplyRange);
2888 QTRY_COMPARE(listView->isWrapEnabled(), true);
2889 QTRY_COMPARE(listView->cacheBuffer(), 10);
2890 QTRY_COMPARE(listView->snapMode(), QQuickListView::SnapToItem);
2892 listView->setHighlightFollowsCurrentItem(false);
2893 listView->setPreferredHighlightBegin(1.0);
2894 listView->setPreferredHighlightEnd(1.0);
2895 listView->setHighlightRangeMode(QQuickListView::StrictlyEnforceRange);
2896 listView->setWrapEnabled(false);
2897 listView->setCacheBuffer(3);
2898 listView->setSnapMode(QQuickListView::SnapOneItem);
2900 QTRY_COMPARE(listView->highlightFollowsCurrentItem(), false);
2901 QTRY_COMPARE(listView->preferredHighlightBegin(), 1.0);
2902 QTRY_COMPARE(listView->preferredHighlightEnd(), 1.0);
2903 QTRY_COMPARE(listView->highlightRangeMode(), QQuickListView::StrictlyEnforceRange);
2904 QTRY_COMPARE(listView->isWrapEnabled(), false);
2905 QTRY_COMPARE(listView->cacheBuffer(), 3);
2906 QTRY_COMPARE(listView->snapMode(), QQuickListView::SnapOneItem);
2908 QTRY_COMPARE(highlightFollowsCurrentItemSpy.count(),1);
2909 QTRY_COMPARE(preferredHighlightBeginSpy.count(),1);
2910 QTRY_COMPARE(preferredHighlightEndSpy.count(),1);
2911 QTRY_COMPARE(highlightRangeModeSpy.count(),1);
2912 QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
2913 QTRY_COMPARE(cacheBufferSpy.count(),1);
2914 QTRY_COMPARE(snapModeSpy.count(),1);
2916 listView->setHighlightFollowsCurrentItem(false);
2917 listView->setPreferredHighlightBegin(1.0);
2918 listView->setPreferredHighlightEnd(1.0);
2919 listView->setHighlightRangeMode(QQuickListView::StrictlyEnforceRange);
2920 listView->setWrapEnabled(false);
2921 listView->setCacheBuffer(3);
2922 listView->setSnapMode(QQuickListView::SnapOneItem);
2924 QTRY_COMPARE(highlightFollowsCurrentItemSpy.count(),1);
2925 QTRY_COMPARE(preferredHighlightBeginSpy.count(),1);
2926 QTRY_COMPARE(preferredHighlightEndSpy.count(),1);
2927 QTRY_COMPARE(highlightRangeModeSpy.count(),1);
2928 QTRY_COMPARE(keyNavigationWrapsSpy.count(),1);
2929 QTRY_COMPARE(cacheBufferSpy.count(),1);
2930 QTRY_COMPARE(snapModeSpy.count(),1);
2935 void tst_QQuickListView::componentChanges()
2937 QQuickView *canvas = createView();
2938 QTRY_VERIFY(canvas);
2939 canvas->setSource(testFileUrl("propertychangestest.qml"));
2941 QQuickListView *listView = canvas->rootObject()->findChild<QQuickListView*>("listView");
2942 QTRY_VERIFY(listView);
2944 QDeclarativeComponent component(canvas->engine());
2945 component.setData("import QtQuick 2.0; Rectangle { color: \"blue\"; }", QUrl::fromLocalFile(""));
2947 QDeclarativeComponent delegateComponent(canvas->engine());
2948 delegateComponent.setData("import QtQuick 2.0; Text { text: '<b>Name:</b> ' + name }", QUrl::fromLocalFile(""));
2950 QSignalSpy highlightSpy(listView, SIGNAL(highlightChanged()));
2951 QSignalSpy delegateSpy(listView, SIGNAL(delegateChanged()));
2952 QSignalSpy headerSpy(listView, SIGNAL(headerChanged()));
2953 QSignalSpy footerSpy(listView, SIGNAL(footerChanged()));
2955 listView->setHighlight(&component);
2956 listView->setHeader(&component);
2957 listView->setFooter(&component);
2958 listView->setDelegate(&delegateComponent);
2960 QTRY_COMPARE(listView->highlight(), &component);
2961 QTRY_COMPARE(listView->header(), &component);
2962 QTRY_COMPARE(listView->footer(), &component);
2963 QTRY_COMPARE(listView->delegate(), &delegateComponent);
2965 QTRY_COMPARE(highlightSpy.count(),1);
2966 QTRY_COMPARE(delegateSpy.count(),1);
2967 QTRY_COMPARE(headerSpy.count(),1);
2968 QTRY_COMPARE(footerSpy.count(),1);
2970 listView->setHighlight(&component);
2971 listView->setHeader(&component);
2972 listView->setFooter(&component);
2973 listView->setDelegate(&delegateComponent);
2975 QTRY_COMPARE(highlightSpy.count(),1);
2976 QTRY_COMPARE(delegateSpy.count(),1);
2977 QTRY_COMPARE(headerSpy.count(),1);
2978 QTRY_COMPARE(footerSpy.count(),1);
2983 void tst_QQuickListView::modelChanges()
2985 QQuickView *canvas = createView();
2986 QTRY_VERIFY(canvas);
2987 canvas->setSource(testFileUrl("propertychangestest.qml"));
2989 QQuickListView *listView = canvas->rootObject()->findChild<QQuickListView*>("listView");
2990 QTRY_VERIFY(listView);
2992 QDeclarativeListModel *alternateModel = canvas->rootObject()->findChild<QDeclarativeListModel*>("alternateModel");
2993 QTRY_VERIFY(alternateModel);
2994 QVariant modelVariant = QVariant::fromValue<QObject *>(alternateModel);
2995 QSignalSpy modelSpy(listView, SIGNAL(modelChanged()));
2997 listView->setModel(modelVariant);
2998 QTRY_COMPARE(listView->model(), modelVariant);
2999 QTRY_COMPARE(modelSpy.count(),1);
3001 listView->setModel(modelVariant);
3002 QTRY_COMPARE(modelSpy.count(),1);
3004 listView->setModel(QVariant());
3005 QTRY_COMPARE(modelSpy.count(),2);
3010 void tst_QQuickListView::QTBUG_9791()
3012 QQuickView *canvas = createView();
3014 canvas->setSource(testFileUrl("strictlyenforcerange.qml"));
3015 qApp->processEvents();
3017 QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3018 QTRY_VERIFY(listview != 0);
3020 QQuickItem *contentItem = listview->contentItem();
3021 QTRY_VERIFY(contentItem != 0);
3022 QTRY_VERIFY(listview->delegate() != 0);
3023 QTRY_VERIFY(listview->model() != 0);
3025 QMetaObject::invokeMethod(listview, "fillModel");
3026 qApp->processEvents();
3028 // Confirm items positioned correctly
3029 int itemCount = findItems<QQuickItem>(contentItem, "wrapper", false).count();
3030 QCOMPARE(itemCount, 3);
3032 for (int i = 0; i < itemCount; ++i) {
3033 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3034 if (!item) qWarning() << "Item" << i << "not found";
3036 QTRY_COMPARE(item->x(), i*300.0);
3039 // check that view is positioned correctly
3040 QTRY_COMPARE(listview->contentX(), 590.0);
3045 void tst_QQuickListView::manualHighlight()
3047 QQuickView *canvas = new QQuickView(0);
3048 canvas->setGeometry(0,0,240,320);
3050 QString filename(testFile("manual-highlight.qml"));
3051 canvas->setSource(QUrl::fromLocalFile(filename));
3053 qApp->processEvents();
3055 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3056 QTRY_VERIFY(listview != 0);
3058 QQuickItem *contentItem = listview->contentItem();
3059 QTRY_VERIFY(contentItem != 0);
3061 QTRY_COMPARE(listview->currentIndex(), 0);
3062 QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 0));
3063 QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
3065 listview->setCurrentIndex(2);
3067 QTRY_COMPARE(listview->currentIndex(), 2);
3068 QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 2));
3069 QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
3072 listview->positionViewAtIndex(3, QQuickListView::Contain);
3074 QTRY_COMPARE(listview->currentIndex(), 2);
3075 QTRY_COMPARE(listview->currentItem(), findItem<QQuickItem>(contentItem, "wrapper", 2));
3076 QTRY_COMPARE(listview->highlightItem()->y() - 5, listview->currentItem()->y());
3081 void tst_QQuickListView::QTBUG_11105()
3083 QQuickView *canvas = createView();
3086 for (int i = 0; i < 30; i++)
3087 model.addItem("Item" + QString::number(i), "");
3089 QDeclarativeContext *ctxt = canvas->rootContext();
3090 ctxt->setContextProperty("testModel", &model);
3092 TestObject *testObject = new TestObject;
3093 ctxt->setContextProperty("testObject", testObject);
3095 canvas->setSource(testFileUrl("listviewtest.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 // Confirm items positioned correctly
3105 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3106 for (int i = 0; i < model.count() && i < itemCount; ++i) {
3107 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3108 if (!item) qWarning() << "Item" << i << "not found";
3110 QTRY_VERIFY(item->y() == i*20);
3113 listview->positionViewAtIndex(20, QQuickListView::Beginning);
3114 QCOMPARE(listview->contentY(), 280.);
3117 for (int i = 0; i < 5; i++)
3118 model2.addItem("Item" + QString::number(i), "");
3120 ctxt->setContextProperty("testModel", &model2);
3122 itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3123 QCOMPARE(itemCount, 5);
3129 void tst_QQuickListView::header()
3131 QFETCH(QQuickListView::Orientation, orientation);
3132 QFETCH(Qt::LayoutDirection, layoutDirection);
3133 QFETCH(QPointF, initialHeaderPos);
3134 QFETCH(QPointF, firstDelegatePos);
3135 QFETCH(QPointF, initialContentPos);
3136 QFETCH(QPointF, changedHeaderPos);
3137 QFETCH(QPointF, changedContentPos);
3138 QFETCH(QPointF, resizeContentPos);
3141 for (int i = 0; i < 30; i++)
3142 model.addItem("Item" + QString::number(i), "");
3144 QQuickView *canvas = createView();
3145 canvas->rootContext()->setContextProperty("testModel", &model);
3146 canvas->rootContext()->setContextProperty("initialViewWidth", 240);
3147 canvas->rootContext()->setContextProperty("initialViewHeight", 320);
3148 canvas->setSource(testFileUrl("header.qml"));
3150 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3151 QTRY_VERIFY(listview != 0);
3152 listview->setOrientation(orientation);
3153 listview->setLayoutDirection(layoutDirection);
3155 QQuickItem *contentItem = listview->contentItem();
3156 QTRY_VERIFY(contentItem != 0);
3158 QQuickText *header = findItem<QQuickText>(contentItem, "header");
3161 QVERIFY(header == listview->headerItem());
3163 QCOMPARE(header->width(), 100.);
3164 QCOMPARE(header->height(), 30.);
3165 QCOMPARE(header->pos(), initialHeaderPos);
3166 QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos);
3168 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3170 QCOMPARE(item->pos(), firstDelegatePos);
3173 QCOMPARE(header->pos(), initialHeaderPos); // header should stay where it is
3175 for (int i = 0; i < 30; i++)
3176 model.addItem("Item" + QString::number(i), "");
3178 QSignalSpy headerItemSpy(listview, SIGNAL(headerItemChanged()));
3179 QMetaObject::invokeMethod(canvas->rootObject(), "changeHeader");
3181 QCOMPARE(headerItemSpy.count(), 1);
3183 header = findItem<QQuickText>(contentItem, "header");
3185 header = findItem<QQuickText>(contentItem, "header2");
3188 QVERIFY(header == listview->headerItem());
3190 QCOMPARE(header->pos(), changedHeaderPos);
3191 QCOMPARE(header->width(), 50.);
3192 QCOMPARE(header->height(), 20.);
3193 QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), changedContentPos);
3194 QCOMPARE(item->pos(), firstDelegatePos);
3199 // QTBUG-21207 header should become visible if view resizes from initial empty size
3201 canvas = createView();
3202 canvas->rootContext()->setContextProperty("testModel", &model);
3203 canvas->rootContext()->setContextProperty("initialViewWidth", 0.0);
3204 canvas->rootContext()->setContextProperty("initialViewHeight", 0.0);
3205 canvas->setSource(testFileUrl("header.qml"));
3207 listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3208 QTRY_VERIFY(listview != 0);
3209 listview->setOrientation(orientation);
3210 listview->setLayoutDirection(layoutDirection);
3212 listview->setWidth(240);
3213 listview->setHeight(320);
3214 QTRY_COMPARE(listview->headerItem()->pos(), initialHeaderPos);
3215 QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos);
3221 void tst_QQuickListView::header_data()
3223 QTest::addColumn<QQuickListView::Orientation>("orientation");
3224 QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3225 QTest::addColumn<QPointF>("initialHeaderPos");
3226 QTest::addColumn<QPointF>("changedHeaderPos");
3227 QTest::addColumn<QPointF>("initialContentPos");
3228 QTest::addColumn<QPointF>("changedContentPos");
3229 QTest::addColumn<QPointF>("firstDelegatePos");
3230 QTest::addColumn<QPointF>("resizeContentPos");
3232 // header1 = 100 x 30
3233 // header2 = 50 x 20
3234 // delegates = 240 x 20
3237 // header above items, top left
3238 QTest::newRow("vertical, left to right") << QQuickListView::Vertical << Qt::LeftToRight
3246 // header above items, top right
3247 QTest::newRow("vertical, layout right to left") << QQuickListView::Vertical << Qt::RightToLeft
3255 // header to left of items
3256 QTest::newRow("horizontal, layout left to right") << QQuickListView::Horizontal << Qt::LeftToRight
3264 // header to right of items
3265 QTest::newRow("horizontal, layout right to left") << QQuickListView::Horizontal << Qt::RightToLeft
3268 << QPointF(-240 + 100, 0)
3269 << QPointF(-240 + 50, 0)
3271 << QPointF(-240 + 40, 0);
3274 void tst_QQuickListView::header_delayItemCreation()
3276 QQuickView *canvas = createView();
3280 canvas->rootContext()->setContextProperty("setCurrentToZero", QVariant(false));
3281 canvas->setSource(testFileUrl("fillModelOnComponentCompleted.qml"));
3282 qApp->processEvents();
3284 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3285 QTRY_VERIFY(listview != 0);
3287 QQuickItem *contentItem = listview->contentItem();
3288 QTRY_VERIFY(contentItem != 0);
3290 QQuickText *header = findItem<QQuickText>(contentItem, "header");
3292 QCOMPARE(header->y(), -header->height());
3294 QCOMPARE(listview->contentY(), -header->height());
3297 QTRY_COMPARE(header->y(), -header->height());
3302 void tst_QQuickListView::footer()
3304 QFETCH(QQuickListView::Orientation, orientation);
3305 QFETCH(Qt::LayoutDirection, layoutDirection);
3306 QFETCH(QPointF, initialFooterPos);
3307 QFETCH(QPointF, firstDelegatePos);
3308 QFETCH(QPointF, initialContentPos);
3309 QFETCH(QPointF, changedFooterPos);
3310 QFETCH(QPointF, changedContentPos);
3311 QFETCH(QPointF, resizeContentPos);
3313 QQuickView *canvas = createView();
3316 for (int i = 0; i < 3; i++)
3317 model.addItem("Item" + QString::number(i), "");
3319 QDeclarativeContext *ctxt = canvas->rootContext();
3320 ctxt->setContextProperty("testModel", &model);
3322 canvas->setSource(testFileUrl("footer.qml"));
3324 qApp->processEvents();
3326 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3327 QTRY_VERIFY(listview != 0);
3328 listview->setOrientation(orientation);
3329 listview->setLayoutDirection(layoutDirection);
3331 QQuickItem *contentItem = listview->contentItem();
3332 QTRY_VERIFY(contentItem != 0);
3334 QQuickText *footer = findItem<QQuickText>(contentItem, "footer");
3337 QVERIFY(footer == listview->footerItem());
3339 QCOMPARE(footer->pos(), initialFooterPos);
3340 QCOMPARE(footer->width(), 100.);
3341 QCOMPARE(footer->height(), 30.);
3342 QCOMPARE(QPointF(listview->contentX(), listview->contentY()), initialContentPos);
3344 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3346 QCOMPARE(item->pos(), firstDelegatePos);
3349 model.removeItem(1);
3351 if (orientation == QQuickListView::Vertical) {
3352 QTRY_COMPARE(footer->y(), initialFooterPos.y() - 20); // delegate height = 20
3354 QTRY_COMPARE(footer->x(), layoutDirection == Qt::LeftToRight ?
3355 initialFooterPos.x() - 40 : initialFooterPos.x() + 40); // delegate width = 40
3361 QPointF posWhenNoItems(0, 0);
3362 if (orientation == QQuickListView::Horizontal && layoutDirection == Qt::RightToLeft)
3363 posWhenNoItems.setX(-100);
3364 QTRY_COMPARE(footer->pos(), posWhenNoItems);
3366 // if header is present, it's at a negative pos, so the footer should not move
3367 canvas->rootObject()->setProperty("showHeader", true);
3368 QTRY_COMPARE(footer->pos(), posWhenNoItems);
3369 canvas->rootObject()->setProperty("showHeader", false);
3372 for (int i = 0; i < 30; i++)
3373 model.addItem("Item" + QString::number(i), "");
3375 QSignalSpy footerItemSpy(listview, SIGNAL(footerItemChanged()));
3376 QMetaObject::invokeMethod(canvas->rootObject(), "changeFooter");
3378 QCOMPARE(footerItemSpy.count(), 1);
3380 footer = findItem<QQuickText>(contentItem, "footer");
3382 footer = findItem<QQuickText>(contentItem, "footer2");
3385 QVERIFY(footer == listview->footerItem());
3387 QCOMPARE(footer->pos(), changedFooterPos);
3388 QCOMPARE(footer->width(), 50.);
3389 QCOMPARE(footer->height(), 20.);
3390 QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), changedContentPos);
3392 item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3394 QCOMPARE(item->pos(), firstDelegatePos);
3396 listview->positionViewAtEnd();
3397 footer->setHeight(10);
3398 footer->setWidth(40);
3399 QTRY_COMPARE(QPointF(listview->contentX(), listview->contentY()), resizeContentPos);
3404 void tst_QQuickListView::footer_data()
3406 QTest::addColumn<QQuickListView::Orientation>("orientation");
3407 QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
3408 QTest::addColumn<QPointF>("initialFooterPos");
3409 QTest::addColumn<QPointF>("changedFooterPos");
3410 QTest::addColumn<QPointF>("initialContentPos");
3411 QTest::addColumn<QPointF>("changedContentPos");
3412 QTest::addColumn<QPointF>("firstDelegatePos");
3413 QTest::addColumn<QPointF>("resizeContentPos");
3415 // footer1 = 100 x 30
3416 // footer2 = 50 x 20
3417 // delegates = 40 x 20
3419 // view height = 320
3421 // footer below items, bottom left
3422 QTest::newRow("vertical, layout left to right") << QQuickListView::Vertical << Qt::LeftToRight
3423 << QPointF(0, 3 * 20)
3424 << QPointF(0, 30 * 20) // added 30 items
3428 << QPointF(0, 30 * 20 - 320 + 10);
3430 // footer below items, bottom right
3431 QTest::newRow("vertical, layout right to left") << QQuickListView::Vertical << Qt::RightToLeft
3432 << QPointF(0, 3 * 20)
3433 << QPointF(0, 30 * 20)
3437 << QPointF(0, 30 * 20 - 320 + 10);
3439 // footer to right of items
3440 QTest::newRow("horizontal, layout left to right") << QQuickListView::Horizontal << Qt::LeftToRight
3441 << QPointF(40 * 3, 0)
3442 << QPointF(40 * 30, 0)
3446 << QPointF(40 * 30 - 240 + 40, 0);
3448 // footer to left of items
3449 QTest::newRow("horizontal, layout right to left") << QQuickListView::Horizontal << Qt::RightToLeft
3450 << QPointF(-(40 * 3) - 100, 0)
3451 << QPointF(-(40 * 30) - 50, 0) // 50 = new footer width
3455 << QPointF(-(40 * 30) - 40, 0);
3458 class LVAccessor : public QQuickListView
3461 qreal minY() const { return minYExtent(); }
3462 qreal maxY() const { return maxYExtent(); }
3463 qreal minX() const { return minXExtent(); }
3464 qreal maxX() const { return maxXExtent(); }
3467 void tst_QQuickListView::headerFooter()
3471 QQuickView *canvas = createView();
3474 QDeclarativeContext *ctxt = canvas->rootContext();
3475 ctxt->setContextProperty("testModel", &model);
3477 canvas->setSource(testFileUrl("headerfooter.qml"));
3478 qApp->processEvents();
3480 QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3481 QTRY_VERIFY(listview != 0);
3483 QQuickItem *contentItem = listview->contentItem();
3484 QTRY_VERIFY(contentItem != 0);
3486 QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
3488 QCOMPARE(header->y(), -header->height());
3490 QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
3492 QCOMPARE(footer->y(), 0.);
3494 QCOMPARE(static_cast<LVAccessor*>(listview)->minY(), header->height());
3495 QCOMPARE(static_cast<LVAccessor*>(listview)->maxY(), header->height());
3501 QQuickView *canvas = createView();
3504 QDeclarativeContext *ctxt = canvas->rootContext();
3505 ctxt->setContextProperty("testModel", &model);
3507 canvas->setSource(testFileUrl("headerfooter.qml"));
3508 canvas->rootObject()->setProperty("horizontal", true);
3509 qApp->processEvents();
3511 QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3512 QTRY_VERIFY(listview != 0);
3514 QQuickItem *contentItem = listview->contentItem();
3515 QTRY_VERIFY(contentItem != 0);
3517 QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
3519 QCOMPARE(header->x(), -header->width());
3521 QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
3523 QCOMPARE(footer->x(), 0.);
3525 QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), header->width());
3526 QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), header->width());
3532 QQuickView *canvas = createView();
3535 QDeclarativeContext *ctxt = canvas->rootContext();
3536 ctxt->setContextProperty("testModel", &model);
3538 canvas->setSource(testFileUrl("headerfooter.qml"));
3539 canvas->rootObject()->setProperty("horizontal", true);
3540 canvas->rootObject()->setProperty("rtl", true);
3541 qApp->processEvents();
3543 QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3544 QTRY_VERIFY(listview != 0);
3546 QQuickItem *contentItem = listview->contentItem();
3547 QTRY_VERIFY(contentItem != 0);
3549 QQuickItem *header = findItem<QQuickItem>(contentItem, "header");
3551 QCOMPARE(header->x(), 0.);
3553 QQuickItem *footer = findItem<QQuickItem>(contentItem, "footer");
3555 QCOMPARE(footer->x(), -footer->width());
3557 QCOMPARE(static_cast<LVAccessor*>(listview)->minX(), 240. - header->width());
3558 QCOMPARE(static_cast<LVAccessor*>(listview)->maxX(), 240. - header->width());
3564 void tst_QQuickListView::resizeView()
3566 QQuickView *canvas = createView();
3569 for (int i = 0; i < 40; i++)
3570 model.addItem("Item" + QString::number(i), "");
3572 QDeclarativeContext *ctxt = canvas->rootContext();
3573 ctxt->setContextProperty("testModel", &model);
3575 TestObject *testObject = new TestObject;
3576 ctxt->setContextProperty("testObject", testObject);
3578 canvas->setSource(testFileUrl("listviewtest.qml"));
3579 qApp->processEvents();
3581 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3582 QTRY_VERIFY(listview != 0);
3584 QQuickItem *contentItem = listview->contentItem();
3585 QTRY_VERIFY(contentItem != 0);
3587 // Confirm items positioned correctly
3588 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3589 for (int i = 0; i < model.count() && i < itemCount; ++i) {
3590 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3591 if (!item) qWarning() << "Item" << i << "not found";
3593 QTRY_COMPARE(item->y(), i*20.);
3596 QVariant heightRatio;
3597 QMetaObject::invokeMethod(canvas->rootObject(), "heightRatio", Q_RETURN_ARG(QVariant, heightRatio));
3598 QCOMPARE(heightRatio.toReal(), 0.4);
3600 listview->setHeight(200);
3602 QMetaObject::invokeMethod(canvas->rootObject(), "heightRatio", Q_RETURN_ARG(QVariant, heightRatio));
3603 QCOMPARE(heightRatio.toReal(), 0.25);
3609 void tst_QQuickListView::resizeViewAndRepaint()
3611 QQuickView *canvas = createView();
3615 for (int i = 0; i < 40; i++)
3616 model.addItem("Item" + QString::number(i), "");
3618 QDeclarativeContext *ctxt = canvas->rootContext();
3619 ctxt->setContextProperty("testModel", &model);
3620 ctxt->setContextProperty("initialHeight", 100);
3622 canvas->setSource(testFileUrl("resizeview.qml"));
3623 qApp->processEvents();
3625 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3626 QTRY_VERIFY(listview != 0);
3627 QQuickItem *contentItem = listview->contentItem();
3628 QTRY_VERIFY(contentItem != 0);
3630 // item at index 10 should not be currently visible
3631 QVERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 10));
3633 listview->setHeight(320);
3636 QSKIP("QTBUG-21590 view does not reliably receive polish without a running animation");
3639 QTRY_VERIFY(findItem<QQuickItem>(contentItem, "wrapper", 10));
3641 listview->setHeight(100);
3642 QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper", 10));
3647 void tst_QQuickListView::sizeLessThan1()
3649 QQuickView *canvas = createView();
3652 for (int i = 0; i < 30; i++)
3653 model.addItem("Item" + QString::number(i), "");
3655 QDeclarativeContext *ctxt = canvas->rootContext();
3656 ctxt->setContextProperty("testModel", &model);
3658 TestObject *testObject = new TestObject;
3659 ctxt->setContextProperty("testObject", testObject);
3661 canvas->setSource(testFileUrl("sizelessthan1.qml"));
3662 qApp->processEvents();
3664 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3665 QTRY_VERIFY(listview != 0);
3667 QQuickItem *contentItem = listview->contentItem();
3668 QTRY_VERIFY(contentItem != 0);
3670 // Confirm items positioned correctly
3671 int itemCount = findItems<QQuickItem>(contentItem, "wrapper").count();
3672 for (int i = 0; i < model.count() && i < itemCount; ++i) {
3673 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3674 if (!item) qWarning() << "Item" << i << "not found";
3676 QTRY_COMPARE(item->y(), i*0.5);
3683 void tst_QQuickListView::QTBUG_14821()
3685 QQuickView *canvas = createView();
3687 canvas->setSource(testFileUrl("qtbug14821.qml"));
3688 qApp->processEvents();
3690 QQuickListView *listview = qobject_cast<QQuickListView*>(canvas->rootObject());
3691 QVERIFY(listview != 0);
3693 QQuickItem *contentItem = listview->contentItem();
3694 QVERIFY(contentItem != 0);
3696 listview->decrementCurrentIndex();
3697 QCOMPARE(listview->currentIndex(), 99);
3699 listview->incrementCurrentIndex();
3700 QCOMPARE(listview->currentIndex(), 0);
3705 void tst_QQuickListView::resizeDelegate()
3707 QQuickView *canvas = createView();
3710 QStringList strings;
3711 for (int i = 0; i < 30; ++i)
3712 strings << QString::number(i);
3713 QStringListModel model(strings);
3715 QDeclarativeContext *ctxt = canvas->rootContext();
3716 ctxt->setContextProperty("testModel", &model);
3718 canvas->setSource(testFileUrl("displaylist.qml"));
3719 qApp->processEvents();
3721 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3722 QVERIFY(listview != 0);
3724 QQuickItem *contentItem = listview->contentItem();
3725 QVERIFY(contentItem != 0);
3727 QCOMPARE(listview->count(), model.rowCount());
3729 listview->setCurrentIndex(25);
3730 listview->setContentY(0);
3733 for (int i = 0; i < 16; ++i) {
3734 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3736 QCOMPARE(item->y(), i*20.0);
3739 QCOMPARE(listview->currentItem()->y(), 500.0);
3740 QTRY_COMPARE(listview->highlightItem()->y(), 500.0);
3742 canvas->rootObject()->setProperty("delegateHeight", 30);
3745 for (int i = 0; i < 11; ++i) {
3746 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3748 QTRY_COMPARE(item->y(), i*30.0);
3751 QTRY_COMPARE(listview->currentItem()->y(), 750.0);
3752 QTRY_COMPARE(listview->highlightItem()->y(), 750.0);
3754 listview->setCurrentIndex(1);
3755 listview->positionViewAtIndex(25, QQuickListView::Beginning);
3756 listview->positionViewAtIndex(5, QQuickListView::Beginning);
3758 for (int i = 5; i < 16; ++i) {
3759 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3761 QCOMPARE(item->y(), i*30.0);
3764 QTRY_COMPARE(listview->currentItem()->y(), 30.0);
3765 QTRY_COMPARE(listview->highlightItem()->y(), 30.0);
3767 canvas->rootObject()->setProperty("delegateHeight", 20);
3770 for (int i = 5; i < 11; ++i) {
3771 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
3773 QTRY_COMPARE(item->y(), 150 + (i-5)*20.0);
3776 QTRY_COMPARE(listview->currentItem()->y(), 70.0);
3777 QTRY_COMPARE(listview->highlightItem()->y(), 70.0);
3782 void tst_QQuickListView::resizeFirstDelegate()
3784 // QTBUG-20712: Content Y jumps constantly if first delegate height == 0
3785 // and other delegates have height > 0
3787 QSKIP("Test unstable - QTBUG-22872");
3789 QQuickView *canvas = createView();
3792 // bug only occurs when all items in the model are visible
3794 for (int i = 0; i < 10; i++)
3795 model.addItem("Item" + QString::number(i), "");
3797 QDeclarativeContext *ctxt = canvas->rootContext();
3798 ctxt->setContextProperty("testModel", &model);
3800 TestObject *testObject = new TestObject;
3801 ctxt->setContextProperty("testObject", testObject);
3803 canvas->setSource(testFileUrl("listviewtest.qml"));
3804 qApp->processEvents();
3806 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3807 QVERIFY(listview != 0);
3809 QQuickItem *contentItem = listview->contentItem();
3810 QVERIFY(contentItem != 0);
3812 QQuickItem *item = 0;
3813 for (int i = 0; i < model.count(); ++i) {
3814 item = findItem<QQuickItem>(contentItem, "wrapper", i);
3816 QCOMPARE(item->y(), i*20.0);
3819 item = findItem<QQuickItem>(contentItem, "wrapper", 0);
3822 // check the content y has not jumped up and down
3823 QCOMPARE(listview->contentY(), 0.0);
3824 QSignalSpy spy(listview, SIGNAL(contentYChanged()));
3826 QCOMPARE(spy.count(), 0);
3828 for (int i = 1; i < model.count(); ++i) {
3829 item = findItem<QQuickItem>(contentItem, "wrapper", i);
3831 QTRY_COMPARE(item->y(), (i-1)*20.0);
3835 // QTBUG-22014: refill doesn't clear items scrolling off the top of the
3836 // list if they follow a zero-sized delegate
3838 for (int i = 0; i < 10; i++)
3839 model.addItem("Item" + QString::number(i), "");
3841 item = findItem<QQuickItem>(contentItem, "wrapper", 1);
3845 listview->setCurrentIndex(19);
3846 qApp->processEvents();
3848 // items 0-2 should have been deleted
3849 for (int i=0; i<3; i++) {
3850 QTRY_VERIFY(!findItem<QQuickItem>(contentItem, "wrapper", i));
3857 void tst_QQuickListView::QTBUG_16037()
3859 QQuickView *canvas = createView();
3862 canvas->setSource(testFileUrl("qtbug16037.qml"));
3863 qApp->processEvents();
3865 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "listview");
3866 QTRY_VERIFY(listview != 0);
3868 QVERIFY(listview->contentHeight() <= 0.0);
3870 QMetaObject::invokeMethod(canvas->rootObject(), "setModel");
3872 QTRY_COMPARE(listview->contentHeight(), 80.0);
3877 void tst_QQuickListView::indexAt_itemAt_data()
3879 QTest::addColumn<qreal>("x");
3880 QTest::addColumn<qreal>("y");
3881 QTest::addColumn<int>("index");
3883 QTest::newRow("Item 0 - 0, 0") << 0. << 0. << 0;
3884 QTest::newRow("Item 0 - 0, 19") << 0. << 19. << 0;
3885 QTest::newRow("Item 0 - 239, 19") << 239. << 19. << 0;
3886 QTest::newRow("Item 1 - 0, 20") << 0. << 20. << 1;
3887 QTest::newRow("No Item - 240, 20") << 240. << 20. << -1;
3890 void tst_QQuickListView::indexAt_itemAt()
3896 QQuickView *canvas = createView();
3899 for (int i = 0; i < 30; i++)
3900 model.addItem("Item" + QString::number(i), "");
3902 QDeclarativeContext *ctxt = canvas->rootContext();
3903 ctxt->setContextProperty("testModel", &model);
3905 TestObject *testObject = new TestObject;
3906 ctxt->setContextProperty("testObject", testObject);
3908 canvas->setSource(testFileUrl("listviewtest.qml"));
3909 qApp->processEvents();
3911 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3912 QTRY_VERIFY(listview != 0);
3914 QQuickItem *contentItem = listview->contentItem();
3915 QTRY_VERIFY(contentItem != 0);
3917 QQuickItem *item = 0;
3919 item = findItem<QQuickItem>(contentItem, "wrapper", index);
3922 QCOMPARE(listview->indexAt(x,y), index);
3923 QVERIFY(listview->itemAt(x,y) == item);
3929 void tst_QQuickListView::incrementalModel()
3931 QQuickView *canvas = createView();
3933 IncrementalModel model;
3934 QDeclarativeContext *ctxt = canvas->rootContext();
3935 ctxt->setContextProperty("testModel", &model);
3937 canvas->setSource(testFileUrl("displaylist.qml"));
3938 qApp->processEvents();
3940 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
3941 QTRY_VERIFY(listview != 0);
3943 QQuickItem *contentItem = listview->contentItem();
3944 QTRY_VERIFY(contentItem != 0);
3946 QTRY_COMPARE(listview->count(), 20);
3948 listview->positionViewAtIndex(10, QQuickListView::Beginning);
3950 QTRY_COMPARE(listview->count(), 25);
3955 void tst_QQuickListView::onAdd()
3957 QFETCH(int, initialItemCount);
3958 QFETCH(int, itemsToAdd);
3960 const int delegateHeight = 10;
3963 // these initial items should not trigger ListView.onAdd
3964 for (int i=0; i<initialItemCount; i++)
3965 model.addItem("dummy value", "dummy value");
3967 QQuickView *canvas = createView();
3968 canvas->setGeometry(0,0,200, delegateHeight * (initialItemCount + itemsToAdd));
3969 QDeclarativeContext *ctxt = canvas->rootContext();
3970 ctxt->setContextProperty("testModel", &model);
3971 ctxt->setContextProperty("delegateHeight", delegateHeight);
3972 canvas->setSource(testFileUrl("attachedSignals.qml"));
3974 QObject *object = canvas->rootObject();
3975 object->setProperty("width", canvas->width());
3976 object->setProperty("height", canvas->height());
3977 qApp->processEvents();
3979 QList<QPair<QString, QString> > items;
3980 for (int i=0; i<itemsToAdd; i++)
3981 items << qMakePair(QString("value %1").arg(i), QString::number(i));
3982 model.addItems(items);
3983 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
3985 QVariantList result = object->property("addedDelegates").toList();
3986 QCOMPARE(result.count(), items.count());
3987 for (int i=0; i<items.count(); i++)
3988 QCOMPARE(result[i].toString(), items[i].first);
3993 void tst_QQuickListView::onAdd_data()
3995 QTest::addColumn<int>("initialItemCount");
3996 QTest::addColumn<int>("itemsToAdd");
3998 QTest::newRow("0, add 1") << 0 << 1;
3999 QTest::newRow("0, add 2") << 0 << 2;
4000 QTest::newRow("0, add 10") << 0 << 10;
4002 QTest::newRow("1, add 1") << 1 << 1;
4003 QTest::newRow("1, add 2") << 1 << 2;
4004 QTest::newRow("1, add 10") << 1 << 10;
4006 QTest::newRow("5, add 1") << 5 << 1;
4007 QTest::newRow("5, add 2") << 5 << 2;
4008 QTest::newRow("5, add 10") << 5 << 10;
4011 void tst_QQuickListView::onRemove()
4013 QFETCH(int, initialItemCount);
4014 QFETCH(int, indexToRemove);
4015 QFETCH(int, removeCount);
4017 const int delegateHeight = 10;
4019 for (int i=0; i<initialItemCount; i++)
4020 model.addItem(QString("value %1").arg(i), "dummy value");
4022 QQuickView *canvas = createView();
4023 QDeclarativeContext *ctxt = canvas->rootContext();
4024 ctxt->setContextProperty("testModel", &model);
4025 ctxt->setContextProperty("delegateHeight", delegateHeight);
4026 canvas->setSource(testFileUrl("attachedSignals.qml"));
4027 QObject *object = canvas->rootObject();
4029 model.removeItems(indexToRemove, removeCount);
4030 QTRY_COMPARE(canvas->rootObject()->property("count").toInt(), model.count());
4032 QCOMPARE(object->property("removedDelegateCount"), QVariant(removeCount));
4037 void tst_QQuickListView::onRemove_data()
4039 QTest::addColumn<int>("initialItemCount");
4040 QTest::addColumn<int>("indexToRemove");
4041 QTest::addColumn<int>("removeCount");
4043 QTest::newRow("remove first") << 1 << 0 << 1;
4044 QTest::newRow("two items, remove first") << 2 << 0 << 1;
4045 QTest::newRow("two items, remove last") << 2 << 1 << 1;
4046 QTest::newRow("two items, remove all") << 2 << 0 << 2;
4048 QTest::newRow("four items, remove first") << 4 << 0 << 1;
4049 QTest::newRow("four items, remove 0-2") << 4 << 0 << 2;
4050 QTest::newRow("four items, remove 1-3") << 4 << 1 << 2;
4051 QTest::newRow("four items, remove 2-4") << 4 << 2 << 2;
4052 QTest::newRow("four items, remove last") << 4 << 3 << 1;
4053 QTest::newRow("four items, remove all") << 4 << 0 << 4;
4055 QTest::newRow("ten items, remove 1-8") << 10 << 0 << 8;
4056 QTest::newRow("ten items, remove 2-7") << 10 << 2 << 5;
4057 QTest::newRow("ten items, remove 4-10") << 10 << 4 << 6;
4060 void tst_QQuickListView::rightToLeft()
4062 QQuickView *canvas = createView();
4063 canvas->setGeometry(0,0,640,320);
4064 canvas->setSource(testFileUrl("rightToLeft.qml"));
4065 qApp->processEvents();
4067 QVERIFY(canvas->rootObject() != 0);
4068 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "view");
4069 QTRY_VERIFY(listview != 0);
4071 QQuickItem *contentItem = listview->contentItem();
4072 QTRY_VERIFY(contentItem != 0);
4074 QQuickVisualItemModel *model = canvas->rootObject()->findChild<QQuickVisualItemModel*>("itemModel");
4075 QTRY_VERIFY(model != 0);
4077 QTRY_VERIFY(model->count() == 3);
4078 QTRY_COMPARE(listview->currentIndex(), 0);
4080 // initial position at first item, right edge aligned
4081 QCOMPARE(listview->contentX(), -640.);
4083 QQuickItem *item = findItem<QQuickItem>(contentItem, "item1");
4085 QTRY_COMPARE(item->x(), -100.0);
4086 QCOMPARE(item->height(), listview->height());
4088 QQuickText *text = findItem<QQuickText>(contentItem, "text1");
4090 QTRY_COMPARE(text->text(), QLatin1String("index: 0"));
4092 listview->setCurrentIndex(2);
4094 item = findItem<QQuickItem>(contentItem, "item3");
4096 QTRY_COMPARE(item->x(), -540.0);
4098 text = findItem<QQuickText>(contentItem, "text3");
4100 QTRY_COMPARE(text->text(), QLatin1String("index: 2"));
4102 QCOMPARE(listview->contentX(), -640.);
4104 // Ensure resizing maintains position relative to right edge
4105 qobject_cast<QQuickItem*>(canvas->rootObject())->setWidth(600);
4106 QTRY_COMPARE(listview->contentX(), -600.);
4111 void tst_QQuickListView::test_mirroring()
4113 QQuickView *canvasA = createView();
4114 canvasA->setSource(testFileUrl("rightToLeft.qml"));
4115 QQuickListView *listviewA = findItem<QQuickListView>(canvasA->rootObject(), "view");
4116 QTRY_VERIFY(listviewA != 0);
4118 QQuickView *canvasB = createView();
4119 canvasB->setSource(testFileUrl("rightToLeft.qml"));
4120 QQuickListView *listviewB = findItem<QQuickListView>(canvasB->rootObject(), "view");
4121 QTRY_VERIFY(listviewA != 0);
4122 qApp->processEvents();
4124 QList<QString> objectNames;
4125 objectNames << "item1" << "item2"; // << "item3"
4127 listviewA->setProperty("layoutDirection", Qt::LeftToRight);
4128 listviewB->setProperty("layoutDirection", Qt::RightToLeft);
4129 QCOMPARE(listviewA->layoutDirection(), listviewA->effectiveLayoutDirection());
4132 foreach (const QString objectName, objectNames)
4133 QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x());
4135 listviewA->setProperty("layoutDirection", Qt::LeftToRight);
4136 listviewB->setProperty("layoutDirection", Qt::LeftToRight);
4139 foreach (const QString objectName, objectNames)
4140 QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x());
4142 QVERIFY(listviewB->layoutDirection() == listviewB->effectiveLayoutDirection());
4143 QQuickItemPrivate::get(listviewB)->setLayoutMirror(true);
4144 QVERIFY(listviewB->layoutDirection() != listviewB->effectiveLayoutDirection());
4146 // LTR != LTR+mirror
4147 foreach (const QString objectName, objectNames)
4148 QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x());
4150 listviewA->setProperty("layoutDirection", Qt::RightToLeft);
4152 // RTL == LTR+mirror
4153 foreach (const QString objectName, objectNames)
4154 QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x());
4156 listviewB->setProperty("layoutDirection", Qt::RightToLeft);
4158 // RTL != RTL+mirror
4159 foreach (const QString objectName, objectNames)
4160 QVERIFY(findItem<QQuickItem>(listviewA, objectName)->x() != findItem<QQuickItem>(listviewB, objectName)->x());
4162 listviewA->setProperty("layoutDirection", Qt::LeftToRight);
4164 // LTR == RTL+mirror
4165 foreach (const QString objectName, objectNames)
4166 QCOMPARE(findItem<QQuickItem>(listviewA, objectName)->x(), findItem<QQuickItem>(listviewB, objectName)->x());
4172 void tst_QQuickListView::margins()
4174 QQuickView *canvas = createView();
4177 for (int i = 0; i < 50; i++)
4178 model.addItem("Item" + QString::number(i), "");
4180 QDeclarativeContext *ctxt = canvas->rootContext();
4181 ctxt->setContextProperty("testModel", &model);
4183 canvas->setSource(testFileUrl("margins.qml"));
4185 qApp->processEvents();
4187 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
4188 QTRY_VERIFY(listview != 0);
4190 QQuickItem *contentItem = listview->contentItem();
4191 QTRY_VERIFY(contentItem != 0);
4193 QCOMPARE(listview->contentY(), -30.);
4194 QCOMPARE(listview->yOrigin(), 0.);
4197 listview->positionViewAtEnd();
4198 qreal pos = listview->contentY();
4199 listview->setContentY(pos + 80);
4200 listview->returnToBounds();
4201 QTRY_COMPARE(listview->contentY(), pos + 50);
4203 // remove item before visible and check that top margin is maintained
4204 // and yOrigin is updated
4205 listview->setContentY(100);
4206 model.removeItem(1);
4207 QTRY_COMPARE(listview->count(), model.count());
4208 listview->setContentY(-50);
4209 QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false);
4210 listview->returnToBounds();
4211 QCOMPARE(listview->yOrigin(), 20.);
4212 QTRY_COMPARE(listview->contentY(), -10.);
4214 // reduce top margin
4215 listview->setTopMargin(20);
4216 QCOMPARE(listview->yOrigin(), 20.);
4217 QTRY_COMPARE(listview->contentY(), 0.);
4220 listview->positionViewAtEnd();
4221 pos = listview->contentY();
4222 listview->setContentY(pos + 80);
4223 listview->returnToBounds();
4224 QTRY_COMPARE(listview->contentY(), pos + 50);
4226 // reduce bottom margin
4227 pos = listview->contentY();
4228 listview->setBottomMargin(40);
4229 QCOMPARE(listview->yOrigin(), 20.);
4230 QTRY_COMPARE(listview->contentY(), pos-10);
4235 void tst_QQuickListView::snapToItem_data()
4237 QTest::addColumn<QQuickListView::Orientation>("orientation");
4238 QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
4239 QTest::addColumn<int>("highlightRangeMode");
4240 QTest::addColumn<QPoint>("flickStart");
4241 QTest::addColumn<QPoint>("flickEnd");
4242 QTest::addColumn<qreal>("snapAlignment");
4243 QTest::addColumn<qreal>("endExtent");
4244 QTest::addColumn<qreal>("startExtent");
4246 QTest::newRow("vertical, left to right") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4247 << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 1200.0 << 0.0;
4249 QTest::newRow("horizontal, left to right") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4250 << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 1200.0 << 0.0;
4252 QTest::newRow("horizontal, right to left") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
4253 << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -1200.0 - 240.0 << -240.0;
4255 QTest::newRow("vertical, left to right, enforce range") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4256 << QPoint(20, 200) << QPoint(20, 20) << 60.0 << 1340.0 << -20.0;
4258 QTest::newRow("horizontal, left to right, enforce range") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4259 << QPoint(200, 20) << QPoint(20, 20) << 60.0 << 1340.0 << -20.0;
4261 QTest::newRow("horizontal, right to left, enforce range") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
4262 << QPoint(20, 20) << QPoint(200, 20) << -60.0 << -1200.0 - 240.0 - 140.0 << -220.0;
4265 void tst_QQuickListView::snapToItem()
4267 QFETCH(QQuickListView::Orientation, orientation);
4268 QFETCH(Qt::LayoutDirection, layoutDirection);
4269 QFETCH(int, highlightRangeMode);
4270 QFETCH(QPoint, flickStart);
4271 QFETCH(QPoint, flickEnd);
4272 QFETCH(qreal, snapAlignment);
4273 QFETCH(qreal, endExtent);
4274 QFETCH(qreal, startExtent);
4276 QQuickView *canvas = createView();
4278 canvas->setSource(testFileUrl("snapToItem.qml"));
4280 qApp->processEvents();
4282 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
4283 QTRY_VERIFY(listview != 0);
4285 listview->setOrientation(orientation);
4286 listview->setLayoutDirection(layoutDirection);
4287 listview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
4289 QQuickItem *contentItem = listview->contentItem();
4290 QTRY_VERIFY(contentItem != 0);
4292 // confirm that a flick hits an item boundary
4293 flick(canvas, flickStart, flickEnd, 180);
4294 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4295 if (orientation == QQuickListView::Vertical)
4296 QCOMPARE(qreal(fmod(listview->contentY(),80.0)), snapAlignment);
4298 QCOMPARE(qreal(fmod(listview->contentX(),80.0)), snapAlignment);
4302 flick(canvas, flickStart, flickEnd, 180);
4303 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4304 } while (orientation == QQuickListView::Vertical
4305 ? !listview->isAtYEnd()
4306 : layoutDirection == Qt::LeftToRight ? !listview->isAtXEnd() : !listview->isAtXBeginning());
4308 if (orientation == QQuickListView::Vertical)
4309 QCOMPARE(listview->contentY(), endExtent);
4311 QCOMPARE(listview->contentX(), endExtent);
4315 flick(canvas, flickEnd, flickStart, 180);
4316 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4317 } while (orientation == QQuickListView::Vertical
4318 ? !listview->isAtYBeginning()
4319 : layoutDirection == Qt::LeftToRight ? !listview->isAtXBeginning() : !listview->isAtXEnd());
4321 if (orientation == QQuickListView::Vertical)
4322 QCOMPARE(listview->contentY(), startExtent);
4324 QCOMPARE(listview->contentX(), startExtent);
4329 void tst_QQuickListView::qListModelInterface_items()
4331 items<TestModel>(testFileUrl("listviewtest.qml"), false);
4334 void tst_QQuickListView::qListModelInterface_package_items()
4336 items<TestModel>(testFileUrl("listviewtest-package.qml"), true);
4339 void tst_QQuickListView::qAbstractItemModel_items()
4341 items<TestModel2>(testFileUrl("listviewtest.qml"), false);
4344 void tst_QQuickListView::qListModelInterface_changed()
4346 changed<TestModel>(testFileUrl("listviewtest.qml"), false);
4349 void tst_QQuickListView::qListModelInterface_package_changed()
4351 changed<TestModel>(testFileUrl("listviewtest-package.qml"), true);
4354 void tst_QQuickListView::qAbstractItemModel_changed()
4356 changed<TestModel2>(testFileUrl("listviewtest.qml"), false);
4359 void tst_QQuickListView::qListModelInterface_inserted()
4361 inserted<TestModel>(testFileUrl("listviewtest.qml"));
4364 void tst_QQuickListView::qListModelInterface_package_inserted()
4366 inserted<TestModel>(testFileUrl("listviewtest-package.qml"));
4369 void tst_QQuickListView::qListModelInterface_inserted_more()
4371 inserted_more<TestModel>();
4374 void tst_QQuickListView::qListModelInterface_inserted_more_data()
4376 inserted_more_data();
4379 void tst_QQuickListView::qAbstractItemModel_inserted()
4381 inserted<TestModel2>(testFileUrl("listviewtest.qml"));
4384 void tst_QQuickListView::qAbstractItemModel_inserted_more()
4386 inserted_more<TestModel2>();
4389 void tst_QQuickListView::qAbstractItemModel_inserted_more_data()
4391 inserted_more_data();
4394 void tst_QQuickListView::qListModelInterface_removed()
4396 removed<TestModel>(testFileUrl("listviewtest.qml"), false);
4397 removed<TestModel>(testFileUrl("listviewtest.qml"), true);
4400 void tst_QQuickListView::qListModelInterface_removed_more()
4402 removed_more<TestModel>(testFileUrl("listviewtest.qml"));
4405 void tst_QQuickListView::qListModelInterface_removed_more_data()
4407 removed_more_data();
4410 void tst_QQuickListView::qListModelInterface_package_removed()
4412 removed<TestModel>(testFileUrl("listviewtest-package.qml"), false);
4413 removed<TestModel>(testFileUrl("listviewtest-package.qml"), true);
4416 void tst_QQuickListView::qAbstractItemModel_removed()
4418 removed<TestModel2>(testFileUrl("listviewtest.qml"), false);
4419 removed<TestModel2>(testFileUrl("listviewtest.qml"), true);
4422 void tst_QQuickListView::qAbstractItemModel_removed_more()
4424 removed_more<TestModel2>(testFileUrl("listviewtest.qml"));
4427 void tst_QQuickListView::qAbstractItemModel_removed_more_data()
4429 removed_more_data();
4432 void tst_QQuickListView::qListModelInterface_moved()
4434 moved<TestModel>(testFileUrl("listviewtest.qml"));
4437 void tst_QQuickListView::qListModelInterface_moved_data()
4442 void tst_QQuickListView::qListModelInterface_package_moved()
4444 moved<TestModel>(testFileUrl("listviewtest-package.qml"));
4447 void tst_QQuickListView::qListModelInterface_package_moved_data()
4452 void tst_QQuickListView::qAbstractItemModel_moved()
4454 moved<TestModel2>(testFileUrl("listviewtest.qml"));
4457 void tst_QQuickListView::qAbstractItemModel_moved_data()
4462 void tst_QQuickListView::qListModelInterface_clear()
4464 clear<TestModel>(testFileUrl("listviewtest.qml"));
4467 void tst_QQuickListView::qListModelInterface_package_clear()
4469 clear<TestModel>(testFileUrl("listviewtest-package.qml"));
4472 void tst_QQuickListView::qAbstractItemModel_clear()
4474 clear<TestModel2>(testFileUrl("listviewtest.qml"));
4477 void tst_QQuickListView::qListModelInterface_sections()
4479 sections<TestModel>(testFileUrl("listview-sections.qml"));
4482 void tst_QQuickListView::qListModelInterface_package_sections()
4484 sections<TestModel>(testFileUrl("listview-sections-package.qml"));
4487 void tst_QQuickListView::qAbstractItemModel_sections()
4489 sections<TestModel2>(testFileUrl("listview-sections.qml"));
4492 void tst_QQuickListView::creationContext()
4495 canvas.setGeometry(0,0,240,320);
4496 canvas.setSource(testFileUrl("creationContext.qml"));
4497 qApp->processEvents();
4499 QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
4501 QVERIFY(rootItem->property("count").toInt() > 0);
4504 QVERIFY(item = rootItem->findChild<QQuickItem *>("listItem"));
4505 QCOMPARE(item->property("text").toString(), QString("Hello!"));
4506 QVERIFY(item = rootItem->findChild<QQuickItem *>("header"));
4507 QCOMPARE(item->property("text").toString(), QString("Hello!"));
4508 QVERIFY(item = rootItem->findChild<QQuickItem *>("footer"));
4509 QCOMPARE(item->property("text").toString(), QString("Hello!"));
4510 QVERIFY(item = rootItem->findChild<QQuickItem *>("section"));
4511 QCOMPARE(item->property("text").toString(), QString("Hello!"));
4514 void tst_QQuickListView::QTBUG_21742()
4517 canvas.setGeometry(0,0,200,200);
4518 canvas.setSource(testFileUrl("qtbug-21742.qml"));
4519 qApp->processEvents();
4521 QQuickItem *rootItem = qobject_cast<QQuickItem *>(canvas.rootObject());
4523 QCOMPARE(rootItem->property("count").toInt(), 1);
4526 QQuickView *tst_QQuickListView::createView()
4528 QQuickView *canvas = new QQuickView(0);
4529 canvas->setGeometry(0,0,240,320);
4534 void tst_QQuickListView::flick(QQuickView *canvas, const QPoint &from, const QPoint &to, int duration)
4536 const int pointCount = 5;
4537 QPoint diff = to - from;
4539 // send press, five equally spaced moves, and release.
4540 QTest::mousePress(canvas, Qt::LeftButton, 0, from);
4542 for (int i = 0; i < pointCount; ++i) {
4543 QMouseEvent mv(QEvent::MouseMove, from + (i+1)*diff/pointCount, Qt::LeftButton, Qt::LeftButton,Qt::NoModifier);
4544 QGuiApplication::sendEvent(canvas, &mv);
4545 QTest::qWait(duration/pointCount);
4546 QCoreApplication::processEvents();
4549 QTest::mouseRelease(canvas, Qt::LeftButton, 0, to);
4552 void tst_QQuickListView::asynchronous()
4554 QQuickView *canvas = createView();
4556 QDeclarativeIncubationController controller;
4557 canvas->engine()->setIncubationController(&controller);
4559 canvas->setSource(testFileUrl("asyncloader.qml"));
4561 QQuickItem *rootObject = qobject_cast<QQuickItem*>(canvas->rootObject());
4562 QVERIFY(rootObject);
4564 QQuickListView *listview = 0;
4567 controller.incubateWhile(&b);
4568 listview = rootObject->findChild<QQuickListView*>("view");
4571 // items will be created one at a time
4572 for (int i = 0; i < 8; ++i) {
4573 QVERIFY(findItem<QQuickItem>(listview, "wrapper", i) == 0);
4574 QQuickItem *item = 0;
4577 controller.incubateWhile(&b);
4578 item = findItem<QQuickItem>(listview, "wrapper", i);
4584 controller.incubateWhile(&b);
4587 // verify positioning
4588 QQuickItem *contentItem = listview->contentItem();
4589 for (int i = 0; i < 8; ++i) {
4590 QQuickItem *item = findItem<QQuickItem>(contentItem, "wrapper", i);
4591 QTRY_COMPARE(item->y(), i*50.0);
4597 void tst_QQuickListView::snapOneItem_data()
4599 QTest::addColumn<QQuickListView::Orientation>("orientation");
4600 QTest::addColumn<Qt::LayoutDirection>("layoutDirection");
4601 QTest::addColumn<int>("highlightRangeMode");
4602 QTest::addColumn<QPoint>("flickStart");
4603 QTest::addColumn<QPoint>("flickEnd");
4604 QTest::addColumn<qreal>("snapAlignment");
4605 QTest::addColumn<qreal>("endExtent");
4606 QTest::addColumn<qreal>("startExtent");
4608 QTest::newRow("vertical, left to right") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4609 << QPoint(20, 200) << QPoint(20, 20) << 180.0 << 560.0 << 0.0;
4611 QTest::newRow("horizontal, left to right") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::NoHighlightRange)
4612 << QPoint(200, 20) << QPoint(20, 20) << 180.0 << 560.0 << 0.0;
4614 QTest::newRow("horizontal, right to left") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::NoHighlightRange)
4615 << QPoint(20, 20) << QPoint(200, 20) << -420.0 << -560.0 - 240.0 << -240.0;
4617 QTest::newRow("vertical, left to right, enforce range") << QQuickListView::Vertical << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4618 << QPoint(20, 200) << QPoint(20, 20) << 180.0 << 580.0 << -20.0;
4620 QTest::newRow("horizontal, left to right, enforce range") << QQuickListView::Horizontal << Qt::LeftToRight << int(QQuickItemView::StrictlyEnforceRange)
4621 << QPoint(200, 20) << QPoint(20, 20) << 180.0 << 580.0 << -20.0;
4623 QTest::newRow("horizontal, right to left, enforce range") << QQuickListView::Horizontal << Qt::RightToLeft << int(QQuickItemView::StrictlyEnforceRange)
4624 << QPoint(20, 20) << QPoint(200, 20) << -420.0 << -580.0 - 240.0 << -220.0;
4627 void tst_QQuickListView::snapOneItem()
4629 QFETCH(QQuickListView::Orientation, orientation);
4630 QFETCH(Qt::LayoutDirection, layoutDirection);
4631 QFETCH(int, highlightRangeMode);
4632 QFETCH(QPoint, flickStart);
4633 QFETCH(QPoint, flickEnd);
4634 QFETCH(qreal, snapAlignment);
4635 QFETCH(qreal, endExtent);
4636 QFETCH(qreal, startExtent);
4639 // This test seems to be unreliable - different test data fails on different runs
4640 QSKIP("QTBUG-23481");
4643 QQuickView *canvas = createView();
4645 canvas->setSource(testFileUrl("snapOneItem.qml"));
4647 qApp->processEvents();
4649 QQuickListView *listview = findItem<QQuickListView>(canvas->rootObject(), "list");
4650 QTRY_VERIFY(listview != 0);
4652 listview->setOrientation(orientation);
4653 listview->setLayoutDirection(layoutDirection);
4654 listview->setHighlightRangeMode(QQuickItemView::HighlightRangeMode(highlightRangeMode));
4656 QQuickItem *contentItem = listview->contentItem();
4657 QTRY_VERIFY(contentItem != 0);
4659 QSignalSpy currentIndexSpy(listview, SIGNAL(currentIndexChanged()));
4661 // confirm that a flick hits the next item boundary
4662 flick(canvas, flickStart, flickEnd, 180);
4663 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4664 if (orientation == QQuickListView::Vertical)
4665 QCOMPARE(listview->contentY(), snapAlignment);
4667 QCOMPARE(listview->contentX(), snapAlignment);
4669 if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4670 QCOMPARE(listview->currentIndex(), 1);
4671 QCOMPARE(currentIndexSpy.count(), 1);
4676 flick(canvas, flickStart, flickEnd, 180);
4677 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4678 } while (orientation == QQuickListView::Vertical
4679 ? !listview->isAtYEnd()
4680 : layoutDirection == Qt::LeftToRight ? !listview->isAtXEnd() : !listview->isAtXBeginning());
4682 if (orientation == QQuickListView::Vertical)
4683 QCOMPARE(listview->contentY(), endExtent);
4685 QCOMPARE(listview->contentX(), endExtent);
4687 if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4688 QCOMPARE(listview->currentIndex(), 3);
4689 QCOMPARE(currentIndexSpy.count(), 3);
4694 flick(canvas, flickEnd, flickStart, 180);
4695 QTRY_VERIFY(listview->isMoving() == false); // wait until it stops
4696 } while (orientation == QQuickListView::Vertical
4697 ? !listview->isAtYBeginning()
4698 : layoutDirection == Qt::LeftToRight ? !listview->isAtXBeginning() : !listview->isAtXEnd());
4700 if (orientation == QQuickListView::Vertical)
4701 QCOMPARE(listview->contentY(), startExtent);
4703 QCOMPARE(listview->contentX(), startExtent);
4705 if (QQuickItemView::HighlightRangeMode(highlightRangeMode) == QQuickItemView::StrictlyEnforceRange) {
4706 QCOMPARE(listview->currentIndex(), 0);
4707 QCOMPARE(currentIndexSpy.count(), 6);
4713 void tst_QQuickListView::unrequestedVisibility()
4716 for (int i = 0; i < 30; i++)
4717 model.addItem("Item" + QString::number(i), QString::number(i));
4719 QQuickView *canvas = new QQuickView(0);
4720 canvas->setGeometry(0,0,240,320);
4722 QDeclarativeContext *ctxt = canvas->rootContext();
4723 ctxt->setContextProperty("testModel", &model);
4724 ctxt->setContextProperty("testWrap", QVariant(false));
4726 canvas->setSource(testFileUrl("unrequestedItems.qml"));
4730 qApp->processEvents();
4733 QQuickListView *leftview = findItem<QQuickListView>(canvas->rootObject(), "leftList");
4734 QTRY_VERIFY(leftview != 0);
4736 QQuickListView *rightview = findItem<QQuickListView>(canvas->rootObject(), "rightList");
4737 QTRY_VERIFY(rightview != 0);
4739 QQuickItem *leftContent = leftview->contentItem();
4740 QTRY_VERIFY(leftContent != 0);
4742 QQuickItem *rightContent = rightview->contentItem();
4743 QTRY_VERIFY(rightContent != 0);
4745 rightview->setCurrentIndex(20);
4747 QTRY_COMPARE(leftview->contentY(), 0.0);
4748 QTRY_COMPARE(rightview->contentY(), 100.0);
4752 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
4753 QCOMPARE(item->isVisible(), true);
4754 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
4755 QCOMPARE(item->isVisible(), false);
4757 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 19));
4758 QCOMPARE(item->isVisible(), false);
4759 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 19));
4760 QCOMPARE(item->isVisible(), true);
4762 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 16));
4763 QCOMPARE(item->isVisible(), true);
4764 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 17));
4765 QCOMPARE(item->isVisible(), false);
4766 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 3));
4767 QCOMPARE(item->isVisible(), false);
4768 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 4));
4769 QCOMPARE(item->isVisible(), true);
4771 rightview->setCurrentIndex(0);
4773 QTRY_COMPARE(leftview->contentY(), 0.0);
4774 QTRY_COMPARE(rightview->contentY(), 0.0);
4776 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
4777 QCOMPARE(item->isVisible(), true);
4778 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
4779 QTRY_COMPARE(item->isVisible(), true);
4781 QVERIFY(!findItem<QQuickItem>(leftContent, "wrapper", 19));
4782 QVERIFY(!findItem<QQuickItem>(rightContent, "wrapper", 19));
4784 leftview->setCurrentIndex(20);
4786 QTRY_COMPARE(leftview->contentY(), 100.0);
4787 QTRY_COMPARE(rightview->contentY(), 0.0);
4789 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
4790 QTRY_COMPARE(item->isVisible(), false);
4791 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
4792 QCOMPARE(item->isVisible(), true);
4794 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 19));
4795 QCOMPARE(item->isVisible(), true);
4796 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 19));
4797 QCOMPARE(item->isVisible(), false);
4799 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 3));
4800 QCOMPARE(item->isVisible(), false);
4801 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4802 QCOMPARE(item->isVisible(), true);
4803 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4804 QCOMPARE(item->isVisible(), true);
4805 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4806 QCOMPARE(item->isVisible(), false);
4808 model.moveItems(19, 1, 1);
4809 QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4811 QTRY_VERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 1));
4812 QCOMPARE(item->isVisible(), false);
4813 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 1));
4814 QCOMPARE(item->isVisible(), true);
4816 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 19));
4817 QCOMPARE(item->isVisible(), true);
4818 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 19));
4819 QCOMPARE(item->isVisible(), false);
4821 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4822 QCOMPARE(item->isVisible(), false);
4823 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4824 QCOMPARE(item->isVisible(), true);
4825 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4826 QCOMPARE(item->isVisible(), true);
4827 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4828 QCOMPARE(item->isVisible(), false);
4830 model.moveItems(3, 4, 1);
4831 QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4833 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4834 QCOMPARE(item->isVisible(), false);
4835 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4836 QCOMPARE(item->isVisible(), true);
4837 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4838 QCOMPARE(item->isVisible(), true);
4839 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4840 QCOMPARE(item->isVisible(), false);
4842 model.moveItems(4, 3, 1);
4843 QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4845 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4846 QCOMPARE(item->isVisible(), false);
4847 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4848 QCOMPARE(item->isVisible(), true);
4849 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4850 QCOMPARE(item->isVisible(), true);
4851 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4852 QCOMPARE(item->isVisible(), false);
4854 model.moveItems(16, 17, 1);
4855 QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4857 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4858 QCOMPARE(item->isVisible(), false);
4859 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4860 QCOMPARE(item->isVisible(), true);
4861 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4862 QCOMPARE(item->isVisible(), true);
4863 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4864 QCOMPARE(item->isVisible(), false);
4866 model.moveItems(17, 16, 1);
4867 QTRY_COMPARE(QQuickItemPrivate::get(leftview)->polishScheduled, false);
4869 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 4));
4870 QCOMPARE(item->isVisible(), false);
4871 QVERIFY(item = findItem<QQuickItem>(leftContent, "wrapper", 5));
4872 QCOMPARE(item->isVisible(), true);
4873 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 16));
4874 QCOMPARE(item->isVisible(), true);
4875 QVERIFY(item = findItem<QQuickItem>(rightContent, "wrapper", 17));
4876 QCOMPARE(item->isVisible(), false);
4881 QQuickItem *tst_QQuickListView::findVisibleChild(QQuickItem *parent, const QString &objectName)
4883 QQuickItem *item = 0;
4884 QList<QQuickItem*> items = parent->findChildren<QQuickItem*>(objectName);
4885 for (int i = 0; i < items.count(); ++i) {
4886 if (items.at(i)->isVisible()) {
4894 Find an item with the specified objectName. If index is supplied then the
4895 item must also evaluate the {index} expression equal to index
4897 template<typename T>
4898 T *tst_QQuickListView::findItem(QQuickItem *parent, const QString &objectName, int index)
4900 const QMetaObject &mo = T::staticMetaObject;
4901 //qDebug() << parent->childItems().count() << "children";
4902 for (int i = 0; i < parent->childItems().count(); ++i) {
4903 QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
4906 //qDebug() << "try" << item;
4907 if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName)) {
4909 QDeclarativeExpression e(qmlContext(item), item, "index");
4910 if (e.evaluate().toInt() == index)
4911 return static_cast<T*>(item);
4913 return static_cast<T*>(item);
4916 item = findItem<T>(item, objectName, index);
4918 return static_cast<T*>(item);
4924 template<typename T>
4925 QList<T*> tst_QQuickListView::findItems(QQuickItem *parent, const QString &objectName, bool visibleOnly)
4928 const QMetaObject &mo = T::staticMetaObject;
4929 //qDebug() << parent->childItems().count() << "children";
4930 for (int i = 0; i < parent->childItems().count(); ++i) {
4931 QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
4932 if (!item || (visibleOnly && !item->isVisible()))
4934 //qDebug() << "try" << item;
4935 if (mo.cast(item) && (objectName.isEmpty() || item->objectName() == objectName))
4936 items.append(static_cast<T*>(item));
4937 items += findItems<T>(item, objectName);
4943 void tst_QQuickListView::dumpTree(QQuickItem *parent, int depth)
4945 static QString padding(" ");
4946 for (int i = 0; i < parent->childItems().count(); ++i) {
4947 QQuickItem *item = qobject_cast<QQuickItem*>(parent->childItems().at(i));
4950 qDebug() << padding.left(depth*2) << item;
4951 dumpTree(item, depth+1);
4955 QTEST_MAIN(tst_QQuickListView)
4957 #include "tst_qquicklistview.moc"