1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the test suite of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
43 #include <QtTest/QtTest>
45 #include <qabstractitemmodel.h>
46 #include <qapplication.h>
47 #include <qlistview.h>
48 #include <qlistwidget.h>
49 #include <qitemdelegate.h>
50 #include <qstandarditemmodel.h>
51 #include <qstringlistmodel.h>
54 #include <QtWidgets/QScrollBar>
55 #include <QtWidgets/QDialog>
56 #include <QtWidgets/QStyledItemDelegate>
57 #if defined(Q_OS_WIN) || defined(Q_OS_WINCE)
59 # include <QtGui/QGuiApplication>
60 #include <qpa/qplatformnativeinterface.h>
63 #if defined(Q_OS_WIN) || defined(Q_OS_WINCE)
64 static inline HWND getHWNDForWidget(const QWidget *widget)
66 QWindow *window = widget->windowHandle();
67 return static_cast<HWND> (QGuiApplication::platformNativeInterface()->nativeResourceForWindow("handle", window));
71 class tst_QListView : public QObject
77 virtual ~tst_QListView();
82 void cleanupTestCase();
98 void singleSelectionRemoveRow();
99 void singleSelectionRemoveColumn();
103 void setCurrentIndex();
104 void selection_data();
107 void scrollBarRanges();
108 void scrollBarAsNeeded_data();
109 void scrollBarAsNeeded();
112 void setCurrentIndexAfterAppendRowCrash();
113 void emptyItemSize();
114 void task203585_selectAll();
115 void task228566_infiniteRelayout();
116 void task248430_crashWith0SizedItem();
117 void task250446_scrollChanged();
118 void task196118_visualRegionForSelection();
119 void task254449_draggingItemToNegativeCoordinates();
120 void keyboardSearch();
121 void shiftSelectionWithNonUniformItemSizes();
122 void clickOnViewportClearsSelection();
123 void task262152_setModelColumnNavigate();
124 void taskQTBUG_2233_scrollHiddenItems_data();
125 void taskQTBUG_2233_scrollHiddenItems();
126 void taskQTBUG_633_changeModelData();
127 void taskQTBUG_435_deselectOnViewportClick();
128 void taskQTBUG_2678_spacingAndWrappedText();
129 void taskQTBUG_5877_skippingItemInPageDownUp();
130 void taskQTBUG_9455_wrongScrollbarRanges();
131 void styleOptionViewItem();
132 void taskQTBUG_12308_artihmeticException();
133 void taskQTBUG_12308_wrongFlowLayout();
134 void taskQTBUG_21115_scrollToAndHiddenItems_data();
135 void taskQTBUG_21115_scrollToAndHiddenItems();
138 // Testing get/set functions
139 void tst_QListView::getSetCheck()
142 // Movement QListView::movement()
143 // void QListView::setMovement(Movement)
144 obj1.setMovement(QListView::Movement(QListView::Static));
145 QCOMPARE(QListView::Movement(QListView::Static), obj1.movement());
146 obj1.setMovement(QListView::Movement(QListView::Free));
147 QCOMPARE(QListView::Movement(QListView::Free), obj1.movement());
148 obj1.setMovement(QListView::Movement(QListView::Snap));
149 QCOMPARE(QListView::Movement(QListView::Snap), obj1.movement());
151 // Flow QListView::flow()
152 // void QListView::setFlow(Flow)
153 obj1.setFlow(QListView::Flow(QListView::LeftToRight));
154 QCOMPARE(QListView::Flow(QListView::LeftToRight), obj1.flow());
155 obj1.setFlow(QListView::Flow(QListView::TopToBottom));
156 QCOMPARE(QListView::Flow(QListView::TopToBottom), obj1.flow());
158 // ResizeMode QListView::resizeMode()
159 // void QListView::setResizeMode(ResizeMode)
160 obj1.setResizeMode(QListView::ResizeMode(QListView::Fixed));
161 QCOMPARE(QListView::ResizeMode(QListView::Fixed), obj1.resizeMode());
162 obj1.setResizeMode(QListView::ResizeMode(QListView::Adjust));
163 QCOMPARE(QListView::ResizeMode(QListView::Adjust), obj1.resizeMode());
165 // LayoutMode QListView::layoutMode()
166 // void QListView::setLayoutMode(LayoutMode)
167 obj1.setLayoutMode(QListView::LayoutMode(QListView::SinglePass));
168 QCOMPARE(QListView::LayoutMode(QListView::SinglePass), obj1.layoutMode());
169 obj1.setLayoutMode(QListView::LayoutMode(QListView::Batched));
170 QCOMPARE(QListView::LayoutMode(QListView::Batched), obj1.layoutMode());
172 // int QListView::spacing()
173 // void QListView::setSpacing(int)
175 QCOMPARE(0, obj1.spacing());
176 obj1.setSpacing(INT_MIN);
177 QCOMPARE(INT_MIN, obj1.spacing());
178 obj1.setSpacing(INT_MAX);
179 QCOMPARE(INT_MAX, obj1.spacing());
181 // ViewMode QListView::viewMode()
182 // void QListView::setViewMode(ViewMode)
183 obj1.setViewMode(QListView::ViewMode(QListView::ListMode));
184 QCOMPARE(QListView::ViewMode(QListView::ListMode), obj1.viewMode());
185 obj1.setViewMode(QListView::ViewMode(QListView::IconMode));
186 QCOMPARE(QListView::ViewMode(QListView::IconMode), obj1.viewMode());
188 // int QListView::modelColumn()
189 // void QListView::setModelColumn(int)
190 obj1.setModelColumn(0);
191 QCOMPARE(0, obj1.modelColumn());
192 obj1.setModelColumn(INT_MIN);
193 QCOMPARE(0, obj1.modelColumn()); // Less than 0 => 0
194 obj1.setModelColumn(INT_MAX);
195 QCOMPARE(0, obj1.modelColumn()); // No model => 0
197 // bool QListView::uniformItemSizes()
198 // void QListView::setUniformItemSizes(bool)
199 obj1.setUniformItemSizes(false);
200 QCOMPARE(false, obj1.uniformItemSizes());
201 obj1.setUniformItemSizes(true);
202 QCOMPARE(true, obj1.uniformItemSizes());
204 // make sure setViewMode() doesn't reset resizeMode
205 obj1.clearPropertyFlags();
206 obj1.setResizeMode(QListView::Adjust);
207 obj1.setViewMode(QListView::IconMode);
208 QCOMPARE(obj1.resizeMode(), QListView::Adjust);
210 obj1.setWordWrap(false);
211 QCOMPARE(false, obj1.wordWrap());
212 obj1.setWordWrap(true);
213 QCOMPARE(true, obj1. wordWrap());
216 class QtTestModel: public QAbstractListModel
219 QtTestModel(QObject *parent = 0): QAbstractListModel(parent),
220 colCount(0), rCount(0), wrongIndex(false) {}
221 int rowCount(const QModelIndex&) const { return rCount; }
222 int columnCount(const QModelIndex&) const { return colCount; }
223 bool isEditable(const QModelIndex &) const { return true; }
225 QVariant data(const QModelIndex &idx, int role) const
228 if (!m_icon.isNull() && role == Qt::DecorationRole) {
231 if (role != Qt::DisplayRole)
234 if (idx.row() < 0 || idx.column() < 0 || idx.column() >= colCount
235 || idx.row() >= rCount) {
237 qWarning("got invalid modelIndex %d/%d", idx.row(), idx.column());
239 return QString("%1/%2").arg(idx.row()).arg(idx.column());
244 beginRemoveRows(QModelIndex(), rCount - 2, rCount - 1);
251 beginRemoveRows(QModelIndex(), 0, rCount - 1);
256 void setDataIcon(const QIcon &icon)
261 int colCount, rCount;
263 mutable bool wrongIndex;
266 tst_QListView::tst_QListView()
270 tst_QListView::~tst_QListView()
274 void tst_QListView::initTestCase()
278 void tst_QListView::cleanupTestCase()
282 void tst_QListView::init()
284 #ifdef Q_OS_WINCE //disable magic for WindowsCE
285 qApp->setAutoMaximizeThreshold(-1);
289 void tst_QListView::cleanup()
294 void tst_QListView::noDelegate()
296 QtTestModel model(0);
297 model.rCount = model.colCount = 10;
299 view.setModel(&model);
300 view.setItemDelegate(0);
304 void tst_QListView::noModel()
308 view.setRowHidden(0, true);
311 void tst_QListView::emptyModel()
313 QtTestModel model(0);
315 view.setModel(&model);
317 QVERIFY(!model.wrongIndex);
320 void tst_QListView::removeRows()
322 QtTestModel model(0);
323 model.rCount = model.colCount = 10;
326 view.setModel(&model);
329 model.removeLastRow();
330 QVERIFY(!model.wrongIndex);
332 model.removeAllRows();
333 QVERIFY(!model.wrongIndex);
336 void tst_QListView::cursorMove()
341 QStandardItemModel model(rows, columns);
343 QListView view(&topLevel);
344 view.setModel(&model);
346 for (int j = 0; j < columns; ++j) {
347 view.setModelColumn(j);
348 for (int i = 0; i < rows; ++i) {
349 QModelIndex index = model.index(i, j);
350 model.setData(index, QString("[%1,%2]").arg(i).arg(j));
351 view.setCurrentIndex(index);
352 QApplication::processEvents();
353 QCOMPARE(view.currentIndex(), index);
357 QSize cellsize(60, 25);
358 int gap = 1; // compensate for the scrollbars
359 int displayColumns = 6;
361 view.resize((displayColumns + gap) * cellsize.width(),
362 int((ceil(double(rows) / displayColumns) + gap) * cellsize.height()));
363 view.setResizeMode(QListView::Adjust);
364 view.setGridSize(cellsize);
365 view.setViewMode(QListView::IconMode);
366 view.doItemsLayout();
369 QVector<Qt::Key> keymoves;
370 keymoves << Qt::Key_Up << Qt::Key_Up << Qt::Key_Right << Qt::Key_Right << Qt::Key_Up
371 << Qt::Key_Left << Qt::Key_Left << Qt::Key_Up << Qt::Key_Down << Qt::Key_Up
372 << Qt::Key_Up << Qt::Key_Up << Qt::Key_Up << Qt::Key_Up << Qt::Key_Up
373 << Qt::Key_Left << Qt::Key_Left << Qt::Key_Up << Qt::Key_Down;
375 int displayRow = rows / displayColumns - 1;
376 int displayColumn = displayColumns - (rows % displayColumns) - 1;
378 QApplication::instance()->processEvents();
379 for (int i = 0; i < keymoves.size(); ++i) {
380 Qt::Key key = keymoves.at(i);
381 QTest::keyClick(&view, key);
384 displayRow = qMax(0, displayRow - 1);
387 displayRow = qMin(rows / displayColumns - 1, displayRow + 1);
390 displayColumn = qMax(0, displayColumn - 1);
393 displayColumn = qMin(displayColumns-1, displayColumn + 1);
399 QApplication::instance()->processEvents();
401 int row = displayRow * displayColumns + displayColumn;
402 int column = columns - 1;
403 QModelIndex index = model.index(row, column);
404 QCOMPARE(view.currentIndex().row(), row);
405 QCOMPARE(view.currentIndex().column(), column);
406 QCOMPARE(view.currentIndex(), index);
410 void tst_QListView::hideRows()
412 QtTestModel model(0);
413 model.rCount = model.colCount = 10;
416 view.setModel(&model);
420 QVERIFY(!view.isRowHidden(2));
421 view.setRowHidden(2, true);
422 QVERIFY(view.isRowHidden(2));
423 view.setRowHidden(2, false);
424 QVERIFY(!view.isRowHidden(2));
427 QVERIFY(!view.isRowHidden(2));
428 view.setRowHidden(2, false);
429 QVERIFY(!view.isRowHidden(2));
432 QVERIFY(!view.isRowHidden(2));
433 view.setRowHidden(2, true);
434 QVERIFY(view.isRowHidden(2));
435 view.setRowHidden(2, true);
436 QVERIFY(view.isRowHidden(2));
437 view.setRowHidden(2, false);
438 QVERIFY(!view.isRowHidden(2));
440 // show in per-item mode, then hide the first row
441 view.setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
442 QVERIFY(!view.isRowHidden(0));
443 view.setRowHidden(0, true);
444 QVERIFY(view.isRowHidden(0));
445 view.setRowHidden(0, false);
446 QVERIFY(!view.isRowHidden(0));
448 QStandardItemModel sim(0);
449 QStandardItem *root = new QStandardItem("Root row");
450 for (int i=0;i<5;i++)
451 root->appendRow(new QStandardItem(QString("Row %1").arg(i)));
454 view.setRootIndex(root->index());
455 QVERIFY(!view.isRowHidden(0));
456 view.setRowHidden(0, true);
457 QVERIFY(view.isRowHidden(0));
458 view.setRowHidden(0, false);
459 QVERIFY(!view.isRowHidden(0));
463 void tst_QListView::moveCursor()
465 QtTestModel model(0);
466 model.rCount = model.colCount = 10;
469 view.setModel(&model);
471 QTest::keyClick(&view, Qt::Key_Down);
474 view.setModel(&model);
475 view.setRowHidden(0, true);
477 QTest::keyClick(&view, Qt::Key_Down);
478 QCOMPARE(view.selectionModel()->currentIndex(), model.index(1, 0));
481 class QMoveCursorListView : public QListView
484 QMoveCursorListView() : QListView() {}
486 enum CursorAction { MoveUp, MoveDown, MoveLeft, MoveRight,
487 MoveHome, MoveEnd, MovePageUp, MovePageDown,
488 MoveNext, MovePrevious };
490 QModelIndex moveCursor(QMoveCursorListView::CursorAction action, Qt::KeyboardModifiers modifiers)
492 return QListView::moveCursor((QListView::CursorAction)action, modifiers);
496 void tst_QListView::moveCursor2()
498 QtTestModel model(0);
503 model.setDataIcon(QIcon(pm));
505 QMoveCursorListView vu;
507 vu.setIconSize(QSize(36,48));
508 vu.setGridSize(QSize(34,56));
509 //Standard framesize is 1. If Framesize > 2 increase size
510 int frameSize = qApp->style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
511 vu.resize(300 + frameSize * 2,300);
512 vu.setFlow(QListView::LeftToRight);
513 vu.setMovement(QListView::Static);
514 vu.setWrapping(true);
515 vu.setViewMode(QListView::IconMode);
516 vu.setLayoutMode(QListView::Batched);
518 vu.selectionModel()->setCurrentIndex(model.index(0,0), QItemSelectionModel::SelectCurrent);
519 QCoreApplication::processEvents();
521 QModelIndex idx = vu.moveCursor(QMoveCursorListView::MoveHome, Qt::NoModifier);
522 QCOMPARE(idx, model.index(0,0));
523 idx = vu.moveCursor(QMoveCursorListView::MoveDown, Qt::NoModifier);
524 QCOMPARE(idx, model.index(8,0));
527 void tst_QListView::moveCursor3()
529 //this tests is for task 159792
530 //it tests that navigation works even with non uniform item sizes
532 QStandardItemModel model(0, 1);
533 QStandardItem *i1 = new QStandardItem("First item, long name");
534 QStandardItem *i2 = new QStandardItem("2nd item");
535 QStandardItem *i3 = new QStandardItem("Third item, long name");
536 i1->setSizeHint(QSize(200,32));
540 view.setModel(&model);
542 view.setCurrentIndex(model.index(0, 0));
544 QCOMPARE(view.selectionModel()->currentIndex(), model.index(0, 0));
545 QTest::keyClick(&view, Qt::Key_Down);
546 QCOMPARE(view.selectionModel()->currentIndex(), model.index(1, 0));
547 QTest::keyClick(&view, Qt::Key_Down);
548 QCOMPARE(view.selectionModel()->currentIndex(), model.index(2, 0));
549 QTest::keyClick(&view, Qt::Key_Up);
550 QCOMPARE(view.selectionModel()->currentIndex(), model.index(1, 0));
551 QTest::keyClick(&view, Qt::Key_Up);
552 QCOMPARE(view.selectionModel()->currentIndex(), model.index(0, 0));
556 class QListViewShowEventListener : public QListView
559 QListViewShowEventListener() : QListView() { m_shown = false;}
561 virtual void showEvent(QShowEvent * /*e*/)
563 int columnwidth = sizeHintForColumn(0);
564 QSize sz = sizeHintForIndex(model()->index(0,0));
566 // This should retrieve a model index in the 2nd section
567 m_index = indexAt(QPoint(columnwidth +2, sz.height()/2));
576 void tst_QListView::indexAt()
578 QtTestModel model(0);
583 view.setModel(&model);
584 view.setViewMode(QListView::ListMode);
585 view.setFlow(QListView::TopToBottom);
587 QSize sz = view.sizeHintForIndex(model.index(0,0));
589 index = view.indexAt(QPoint(20,0));
590 QVERIFY(index.isValid());
591 QCOMPARE(index.row(), 0);
593 index = view.indexAt(QPoint(20,sz.height()));
594 QVERIFY(index.isValid());
595 QCOMPARE(index.row(), 1);
597 index = view.indexAt(QPoint(20,2 * sz.height()));
598 QVERIFY(!index.isValid());
600 // Check when peeking out of the viewport bounds
601 index = view.indexAt(QPoint(view.viewport()->rect().width(), 0));
602 QVERIFY(!index.isValid());
603 index = view.indexAt(QPoint(-1, 0));
604 QVERIFY(!index.isValid());
605 index = view.indexAt(QPoint(20, view.viewport()->rect().height()));
606 QVERIFY(!index.isValid());
607 index = view.indexAt(QPoint(20, -1));
608 QVERIFY(!index.isValid());
611 QListViewShowEventListener view2;
612 // Set the height to a small enough value so that it wraps to a new section.
613 view2.resize(300,100);
614 view2.setModel(&model);
615 view2.setFlow(QListView::TopToBottom);
616 view2.setViewMode(QListView::ListMode);
617 view2.setWrapping(true);
618 // We really want to make sure it is shown, because the layout won't be known until it is shown
620 QVERIFY(QTest::qWaitForWindowExposed(&view2));
621 QTRY_VERIFY(view2.m_shown);
623 QVERIFY(view2.m_index.isValid());
624 QVERIFY(view2.m_index.row() != 0);
627 void tst_QListView::clicked()
633 qRegisterMetaType<QModelIndex>("QModelIndex");
636 view.setModel(&model);
639 QApplication::processEvents();
641 QModelIndex firstIndex = model.index(0, 0, QModelIndex());
642 QVERIFY(firstIndex.isValid());
643 int itemHeight = view.visualRect(firstIndex).height();
644 view.resize(200, itemHeight * (model.rCount + 1));
646 for (int i = 0; i < model.rCount; ++i) {
647 QPoint p(5, 1 + itemHeight * i);
648 QModelIndex index = view.indexAt(p);
649 if (!index.isValid())
651 QSignalSpy spy(&view, SIGNAL(clicked(const QModelIndex&)));
652 QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p);
653 QCOMPARE(spy.count(), 1);
657 void tst_QListView::singleSelectionRemoveRow()
660 items << "item1" << "item2" << "item3" << "item4";
661 QStringListModel model(items);
664 view.setModel(&model);
668 view.setCurrentIndex(model.index(1));
669 index = view.currentIndex();
670 QCOMPARE(view.model()->data(index).toString(), QString("item2"));
673 index = view.currentIndex();
674 QCOMPARE(view.model()->data(index).toString(), QString("item3"));
677 index = view.currentIndex();
678 QCOMPARE(view.model()->data(index).toString(), QString("item3"));
681 void tst_QListView::singleSelectionRemoveColumn()
685 QStandardItemModel model(numCols, numRows);
686 for (int r = 0; r < numRows; ++r)
687 for (int c = 0; c < numCols; ++c)
688 model.setData(model.index(r, c), QString("%1,%2").arg(r).arg(c));
691 view.setModel(&model);
695 view.setCurrentIndex(model.index(1, 1));
696 index = view.currentIndex();
697 QCOMPARE(view.model()->data(index).toString(), QString("1,1"));
699 model.removeColumn(1);
700 index = view.currentIndex();
701 QCOMPARE(view.model()->data(index).toString(), QString("1,0"));
703 model.removeColumn(0);
704 index = view.currentIndex();
705 QCOMPARE(view.model()->data(index).toString(), QString("1,2"));
708 void tst_QListView::modelColumn()
712 QStandardItemModel model(numCols, numRows);
713 for (int r = 0; r < numRows; ++r)
714 for (int c = 0; c < numCols; ++c)
715 model.setData(model.index(r, c), QString("%1,%2").arg(r).arg(c));
719 view.setModel(&model);
723 // Set and get with a valid model
726 // Default is column 0
727 QCOMPARE(view.modelColumn(), 0);
729 view.setModelColumn(0);
730 QCOMPARE(view.modelColumn(), 0);
731 view.setModelColumn(1);
732 QCOMPARE(view.modelColumn(), 1);
733 view.setModelColumn(2);
734 QCOMPARE(view.modelColumn(), 2);
736 // Out of bound cases should not modify the modelColumn
737 view.setModelColumn(-1);
738 QCOMPARE(view.modelColumn(), 2);
739 view.setModelColumn(INT_MAX);
740 QCOMPARE(view.modelColumn(), 2);
743 // See if it displays the right column using indexAt()...
744 view.resize(400,400);
747 for (int c = 0; c < 3; ++c) {
748 view.setModelColumn(c);
750 for (int y = 0; y < view.height(); ++y) {
751 QModelIndex idx = view.indexAt( QPoint(1, y) );
752 if (idx.row() == startrow + 1) ++startrow;
753 else if (idx.row() == -1) break;
754 QCOMPARE(idx.row(), startrow);
755 QCOMPARE(idx.column(), c);
757 QCOMPARE(startrow, 2);
761 void tst_QListView::hideFirstRow()
764 for (int i=0; i <100; ++i)
766 QStringListModel model(items);
769 view.setModel(&model);
770 view.setUniformItemSizes(true);
771 view.setRowHidden(0,true);
773 QVERIFY(QTest::qWaitForWindowExposed(&view));
777 void tst_QListView::batchedMode()
780 for (int i=0; i <3; ++i)
782 QStringListModel model(items);
785 view.setModel(&model);
786 view.setUniformItemSizes(true);
787 view.setViewMode(QListView::ListMode);
788 view.setLayoutMode(QListView::Batched);
789 view.setBatchSize(2);
790 view.resize(200,400);
792 QVERIFY(QTest::qWaitForWindowExposed(&view));
795 #if defined(Q_OS_WINCE)
799 for (int y = 0; y < view.height(); ++y) {
800 QModelIndex idx = view.indexAt( QPoint(1, y) );
803 if (idx.row() >= ba.size())
804 ba.resize(idx.row() + 1);
805 ba.setBit(idx.row(), true);
807 QCOMPARE(ba.size(), 3);
810 // Test the dynamic listview too.
811 view.setViewMode(QListView::IconMode);
812 view.setLayoutMode(QListView::Batched);
813 view.setFlow(QListView::TopToBottom);
814 view.setBatchSize(2);
816 #if !defined(Q_OS_WINCE)
823 for (int y = 0; y < view.height(); ++y) {
824 QModelIndex idx = view.indexAt( QPoint(1, y) );
827 if (idx.row() >= ba.size())
828 ba.resize(idx.row() + 1);
829 ba.setBit(idx.row(), true);
831 QCOMPARE(ba.size(), 3);
834 void tst_QListView::setCurrentIndex()
838 for (i=0; i <20; ++i)
839 items << QString("item %1").arg(i);
840 QStringListModel model(items);
843 view.setModel(&model);
845 view.resize(220,182);
848 for (int pass = 0; pass < 2; ++pass) {
849 view.setFlow(pass == 0 ? QListView::TopToBottom : QListView::LeftToRight);
850 QScrollBar *sb = pass == 0 ? view.verticalScrollBar() : view.horizontalScrollBar();
851 QList<QSize> gridsizes;
852 gridsizes << QSize() << QSize(200,38);
853 for (int ig = 0; ig < gridsizes.count(); ++ig) {
854 if (pass == 1 && !gridsizes.at(ig).isValid()) // the width of an item varies, so it might jump two times
856 view.setGridSize(gridsizes.at(ig));
858 qApp->processEvents();
859 int offset = sb->value();
861 // first "scroll" down, verify that we scroll one step at a time
863 for (i = 0; i < 20; ++i) {
864 QModelIndex idx = model.index(i,0);
865 view.setCurrentIndex(idx);
866 if (offset != sb->value()) {
867 // If it has scrolled, it should have scrolled only by one.
868 QCOMPARE(sb->value(), offset + 1);
874 --i; // item 20 does not exist
875 // and then "scroll" up, verify that we scroll one step at a time
876 for (; i >= 0; --i) {
877 QModelIndex idx = model.index(i,0);
878 view.setCurrentIndex(idx);
879 if (offset != sb->value()) {
880 // If it has scrolled, it should have scrolled only by one.
881 QCOMPARE(sb->value(), offset - 1);
890 class PublicListView : public QListView
893 PublicListView(QWidget *parent = 0) : QListView(parent)
897 void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags) {
898 QListView::setSelection(rect, flags);
900 QSize contentsSize() const { return QListView::contentsSize(); }
902 void setPositionForIndex(const QPoint &pos, const QModelIndex &index) {
903 QListView::setPositionForIndex(pos, index);
907 class TestDelegate : public QItemDelegate
910 TestDelegate(QObject *parent) : QItemDelegate(parent), m_sizeHint(50,50) {}
911 QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const { return m_sizeHint; }
916 typedef QList<int> IntList;
917 Q_DECLARE_METATYPE(IntList)
919 void tst_QListView::selection_data()
921 QTest::addColumn<int>("itemCount");
922 QTest::addColumn<int>("viewMode");
923 QTest::addColumn<int>("flow");
924 QTest::addColumn<bool>("wrapping");
925 QTest::addColumn<int>("spacing");
926 QTest::addColumn<QSize>("gridSize");
927 QTest::addColumn<IntList>("hiddenRows");
928 QTest::addColumn<QRect>("selectionRect");
929 QTest::addColumn<IntList>("expectedItems");
931 QTest::newRow("select all")
933 << int(QListView::ListMode)
934 << int(QListView::TopToBottom)
937 << QSize() // gridSize
938 << IntList() // hiddenRows
939 << QRect(0, 0, 10, 200) // selection rectangle
940 << (IntList() << 0 << 1 << 2 << 3); // expected items
942 QTest::newRow("select below, (on viewport)")
944 << int(QListView::ListMode)
945 << int(QListView::TopToBottom)
948 << QSize() // gridSize
949 << IntList() // hiddenRows
950 << QRect(10, 250, 1, 1) // selection rectangle
951 << IntList(); // expected items
953 QTest::newRow("select below 2, (on viewport)")
955 << int(QListView::ListMode)
956 << int(QListView::TopToBottom)
959 << QSize() // gridSize
960 << IntList() // hiddenRows
961 << QRect(10, 250, 1, 1) // selection rectangle
962 << IntList(); // expected items
964 QTest::newRow("select to the right, (on viewport)")
966 << int(QListView::ListMode)
967 << int(QListView::TopToBottom)
970 << QSize() // gridSize
971 << IntList() // hiddenRows
972 << QRect(300, 10, 1, 1) // selection rectangle
973 << IntList(); // expected items
975 QTest::newRow("select to the right 2, (on viewport)")
977 << int(QListView::ListMode)
978 << int(QListView::TopToBottom)
981 << QSize() // gridSize
982 << IntList() // hiddenRows
983 << QRect(300, 0, 1, 300) // selection rectangle
984 << IntList(); // expected items
986 #if defined(Q_OS_WINCE)
987 // depending on whether the display is double-pixeld, we need
988 // to click at a different position
989 bool doubledSize = false;
990 int dpi = GetDeviceCaps(GetDC(0), LOGPIXELSX);
991 if ((dpi < 1000) && (dpi > 0)) {
994 QTest::newRow("select inside contents, (on viewport)")
996 << int(QListView::ListMode)
997 << int(QListView::TopToBottom)
1000 << QSize() // gridSize
1001 << IntList() // hiddenRows
1002 << QRect(doubledSize?350:175,doubledSize?550:275, 1, 1)// selection rectangle
1003 << IntList(); // expected items
1005 QTest::newRow("select inside contents, (on viewport)")
1007 << int(QListView::ListMode)
1008 << int(QListView::TopToBottom)
1011 << QSize() // gridSize
1012 << IntList() // hiddenRows
1013 << QRect(175, 275, 1, 1) // selection rectangle
1014 << IntList(); // expected items
1017 QTest::newRow("select a tall rect in LeftToRight flow, wrap items")
1019 << int(QListView::ListMode)
1020 << int(QListView::LeftToRight)
1023 << QSize() // gridSize
1024 << IntList() // hiddenRows
1025 << QRect(90, 90, 1, 100) // selection rectangle
1026 << (IntList() // expected items
1027 << 11 << 12 << 13 << 14 << 15 << 16 << 17 << 18 << 19
1028 << 20 << 21 << 22 << 23 << 24 << 25 << 26 << 27 << 28 << 29
1031 QTest::newRow("select a wide rect in LeftToRight, wrap items")
1033 << int(QListView::ListMode)
1034 << int(QListView::LeftToRight)
1037 << QSize() // gridSize
1038 << IntList() // hiddenRows
1039 << QRect(90, 90, 200, 1) // selection rectangle
1040 << (IntList() // expected items
1041 << 11 << 12 << 13 << 14 << 15);
1043 QTest::newRow("select a wide negative rect in LeftToRight flow, wrap items")
1045 << int(QListView::ListMode)
1046 << int(QListView::LeftToRight)
1049 << QSize() // gridSize
1050 << IntList() // hiddenRows
1051 << QRect(290, 90, -200, 1) // selection rectangle
1052 << (IntList() // expected items
1053 << 11 << 12 << 13 << 14 << 15);
1055 QTest::newRow("select a tall rect in TopToBottom flow, wrap items")
1057 << int(QListView::ListMode)
1058 << int(QListView::TopToBottom)
1061 << QSize() // gridSize
1062 << IntList() // hiddenRows
1063 << QRect(90, 90, 1, 100) // selection rectangle
1064 << (IntList() // expected items
1069 QTest::newRow("select a tall negative rect in TopToBottom flow, wrap items")
1071 << int(QListView::ListMode)
1072 << int(QListView::TopToBottom)
1075 << QSize() // gridSize
1076 << IntList() // hiddenRows
1077 << QRect(90, 190, 1, -100) // selection rectangle
1078 << (IntList() // expected items
1083 QTest::newRow("select a wide rect in TopToBottom, wrap items")
1085 << int(QListView::ListMode)
1086 << int(QListView::TopToBottom)
1089 << QSize() // gridSize
1090 << IntList() // hiddenRows
1091 << QRect(90, 90, 100, 1) // selection rectangle
1092 << (IntList() // expected items
1105 void tst_QListView::selection()
1107 QFETCH(int, itemCount);
1108 QFETCH(int, viewMode);
1110 QFETCH(bool, wrapping);
1111 QFETCH(int, spacing);
1112 QFETCH(QSize, gridSize);
1113 QFETCH(IntList, hiddenRows);
1114 QFETCH(QRect, selectionRect);
1115 QFETCH(IntList, expectedItems);
1118 PublicListView v(&topLevel);
1121 model.rCount = itemCount;
1123 // avoid scrollbar size mismatches among different styles
1124 v.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1125 v.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
1127 v.setItemDelegate(new TestDelegate(&v));
1129 v.setViewMode(QListView::ViewMode(viewMode));
1130 v.setFlow(QListView::Flow(flow));
1131 v.setWrapping(wrapping);
1132 v.setResizeMode(QListView::Adjust);
1133 v.setSpacing(spacing);
1134 if (gridSize.isValid())
1135 v.setGridSize(gridSize);
1136 for (int j = 0; j < hiddenRows.count(); ++j) {
1137 v.setRowHidden(hiddenRows.at(j), true);
1140 #if defined(Q_OS_WINCE)
1141 // If the device is double-pixeled then the scrollbars become
1142 // 10 pixels wider than normal (Windows Style: 16, Windows Mobile Style: 26).
1143 // So we have to make the window slightly bigger to have the same count of
1144 // items in each row of the list view like in the other styles.
1145 static const int dpi = ::GetDeviceCaps(GetDC(0), LOGPIXELSX);
1146 if ((dpi < 1000) && (dpi > 0))
1153 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
1155 v.setSelection(selectionRect, QItemSelectionModel::ClearAndSelect);
1157 QModelIndexList selected = v.selectionModel()->selectedIndexes();
1159 QCOMPARE(selected.count(), expectedItems.count());
1160 for (int i = 0; i < selected.count(); ++i) {
1161 QVERIFY(expectedItems.contains(selected.at(i).row()));
1165 void tst_QListView::scrollTo()
1168 QListView lv(&topLevel);
1169 QStringListModel model(&lv);
1171 list << "Short item 1";
1172 list << "Short item 2";
1173 list << "Short item 3";
1174 list << "Short item 4";
1175 list << "Short item 5";
1176 list << "Short item 6";
1177 list << "Begin This is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\n"
1178 "This is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\n"
1179 "This is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\n"
1180 "This is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\n"
1181 "This is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\n"
1182 "This is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\n"
1183 "This is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\n"
1184 "This is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\n"
1185 "This is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\n"
1186 "This is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\n"
1187 "This is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\n"
1188 "This is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\n"
1189 "This is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\n"
1190 "This is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\n"
1191 "This is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\n"
1192 "This is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item\nThis is a very long item End\n";
1193 list << "Short item";
1194 list << "Short item";
1195 list << "Short item";
1196 list << "Short item";
1197 list << "Short item";
1198 list << "Short item";
1199 list << "Short item";
1200 list << "Short item";
1201 model.setStringList(list);
1202 lv.setModel(&model);
1203 lv.setFixedSize(100, 200);
1205 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
1207 //by default, the list view scrolls per item and has no wrapping
1208 QModelIndex index = model.index(6,0);
1210 //we save the size of the item for later comparisons
1211 const QSize itemsize = lv.visualRect(index).size();
1212 QVERIFY(itemsize.height() > lv.height());
1213 QVERIFY(itemsize.width() > lv.width());
1216 QPoint p = lv.visualRect(index).center();
1217 QTest::mouseClick(lv.viewport(), Qt::LeftButton, Qt::NoModifier, p);
1218 //let's wait because the scrolling is delayed
1219 QTest::qWait(QApplication::doubleClickInterval() + 150);
1220 QTRY_COMPARE(lv.visualRect(index).y(),0);
1222 //we scroll down. As the item is to tall for the view, it will disappear
1223 QTest::keyClick(lv.viewport(), Qt::Key_Down, Qt::NoModifier);
1224 QCOMPARE(lv.visualRect(index).y(), -itemsize.height());
1226 QTest::keyClick(lv.viewport(), Qt::Key_Up, Qt::NoModifier);
1227 QCOMPARE(lv.visualRect(index).y(), 0);
1229 //Let's enable wrapping
1231 lv.setWrapping(true);
1232 lv.horizontalScrollBar()->setValue(0); //let's scroll to the beginning
1235 p = lv.visualRect(index).center();
1236 QTest::mouseClick(lv.viewport(), Qt::LeftButton, Qt::NoModifier, p);
1237 //let's wait because the scrolling is delayed
1238 QTest::qWait(QApplication::doubleClickInterval() + 150);
1239 QTRY_COMPARE(lv.visualRect(index).x(),0);
1241 //we scroll right. As the item is too wide for the view, it will disappear
1242 QTest::keyClick(lv.viewport(), Qt::Key_Right, Qt::NoModifier);
1243 QCOMPARE(lv.visualRect(index).x(), -itemsize.width());
1245 QTest::keyClick(lv.viewport(), Qt::Key_Left, Qt::NoModifier);
1246 QCOMPARE(lv.visualRect(index).x(), 0);
1248 lv.setWrapping(false);
1249 qApp->processEvents(); //let the layout happen
1251 //Let's try with scrolling per pixel
1252 lv.setHorizontalScrollMode( QListView::ScrollPerPixel);
1253 lv.verticalScrollBar()->setValue(0); //scrolls back to the first item
1256 p = lv.visualRect(index).center();
1257 QTest::mouseClick(lv.viewport(), Qt::LeftButton, Qt::NoModifier, p);
1258 //let's wait because the scrolling is delayed
1259 QTest::qWait(QApplication::doubleClickInterval() + 150);
1260 QTRY_COMPARE(lv.visualRect(index).y(),0);
1262 //we scroll down. As the item is too tall for the view, it will partially disappear
1263 QTest::keyClick(lv.viewport(), Qt::Key_Down, Qt::NoModifier);
1264 QVERIFY(lv.visualRect(index).y()<0);
1266 QTest::keyClick(lv.viewport(), Qt::Key_Up, Qt::NoModifier);
1267 QCOMPARE(lv.visualRect(index).y(), 0);
1271 void tst_QListView::scrollBarRanges()
1273 const int rowCount = 10;
1274 const int rowHeight = 20;
1277 QListView lv(&topLevel);
1278 QStringListModel model(&lv);
1280 for (int i = 0; i < rowCount; ++i)
1281 list << QString::fromLatin1("Item %1").arg(i);
1283 model.setStringList(list);
1284 lv.setModel(&model);
1285 lv.resize(250, 130);
1286 TestDelegate *delegate = new TestDelegate(&lv);
1287 delegate->m_sizeHint = QSize(100, rowHeight);
1288 lv.setItemDelegate(delegate);
1291 for (int h = 30; h <= 210; ++h) {
1293 QApplication::processEvents(); // wait for the layout to be done
1294 int visibleRowCount = lv.viewport()->size().height() / rowHeight;
1295 int invisibleRowCount = rowCount - visibleRowCount;
1296 QCOMPARE(lv.verticalScrollBar()->maximum(), invisibleRowCount);
1300 void tst_QListView::scrollBarAsNeeded_data()
1302 QTest::addColumn<QSize>("size");
1303 QTest::addColumn<int>("itemCount");
1304 QTest::addColumn<int>("flow");
1305 QTest::addColumn<bool>("horizontalScrollBarVisible");
1306 QTest::addColumn<bool>("verticalScrollBarVisible");
1309 QTest::newRow("TopToBottom, count:0")
1312 << int(QListView::TopToBottom)
1316 QTest::newRow("TopToBottom, count:1")
1319 << int(QListView::TopToBottom)
1323 QTest::newRow("TopToBottom, count:20")
1326 << int(QListView::TopToBottom)
1330 QTest::newRow("LeftToRight, count:0")
1333 << int(QListView::LeftToRight)
1337 QTest::newRow("LeftToRight, count:1")
1340 << int(QListView::LeftToRight)
1344 QTest::newRow("LeftToRight, count:20")
1347 << int(QListView::LeftToRight)
1353 void tst_QListView::scrollBarAsNeeded()
1356 QFETCH(QSize, size);
1357 QFETCH(int, itemCount);
1359 QFETCH(bool, horizontalScrollBarVisible);
1360 QFETCH(bool, verticalScrollBarVisible);
1363 const int rowCounts[3] = {0, 1, 20};
1366 QListView lv(&topLevel);
1367 lv.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1368 lv.setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1369 lv.setFlow((QListView::Flow)flow);
1370 QStringListModel model(&lv);
1371 lv.setModel(&model);
1375 for (uint r = 0; r < sizeof(rowCounts)/sizeof(int); ++r) {
1378 for (i = 0; i < rowCounts[r]; ++i)
1379 list << QString::fromLatin1("Item %1").arg(i);
1381 model.setStringList(list);
1382 QApplication::processEvents();
1385 QStringList replacement;
1386 for (i = 0; i < itemCount; ++i) {
1387 replacement << QString::fromLatin1("Item %1").arg(i);
1389 model.setStringList(replacement);
1391 QApplication::processEvents();
1393 QTRY_COMPARE(lv.horizontalScrollBar()->isVisible(), horizontalScrollBarVisible);
1394 QTRY_COMPARE(lv.verticalScrollBar()->isVisible(), verticalScrollBarVisible);
1398 void tst_QListView::moveItems()
1400 QStandardItemModel model;
1401 for (int r = 0; r < 4; ++r) {
1402 for (int c = 0; c < 4; ++c) {
1403 QStandardItem* item = new QStandardItem(QString("standard item (%1,%2)").arg(r).arg(c));
1404 model.setItem(r, c, item);
1408 PublicListView view;
1409 view.setViewMode(QListView::IconMode);
1410 view.setResizeMode(QListView::Fixed);
1411 view.setWordWrap(true);
1412 view.setModel(&model);
1413 view.setItemDelegate(new TestDelegate(&view));
1415 for (int r = 0; r < model.rowCount(); ++r) {
1416 for (int c = 0; c < model.columnCount(); ++c) {
1417 const QModelIndex& idx = model.index(r, c);
1418 view.setPositionForIndex(QPoint(r * 75, r * 75), idx);
1422 QCOMPARE(view.contentsSize(), QSize(275, 275));
1425 void tst_QListView::wordWrap()
1428 lv.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1429 lv.setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);
1430 QStringListModel model(&lv);
1432 list << "Short item 1";
1433 list << "Short item 2";
1434 list << "Short item 3";
1435 list << "Begin\nThis item take severals Lines\nEnd";
1436 list << "And this is a very long item very long item this is a very vary vary long item"
1437 "very long very very long long long this is a long item a very long item a very very long item";
1438 list << "And this is a second even a little more long very long item very long item this is a very vary vary long item"
1439 "very long very very long long long this is a long item a very long item a very very long item";
1440 list << "Short item";
1441 list << "rzeofig zerig fslfgj smdlfkgj qmsdlfj amrzriougf qsla zrg fgsdf gsdfg sdfgs dfg sdfgcvb sdfg qsdjfh qsdfjklh qs";
1442 list << "Short item";
1443 model.setStringList(list);
1444 lv.setModel(&model);
1445 lv.setWordWrap(true);
1446 lv.setFixedSize(150, 150);
1448 QApplication::processEvents();
1450 QTRY_COMPARE(lv.horizontalScrollBar()->isVisible(), false);
1451 QTRY_COMPARE(lv.verticalScrollBar()->isVisible(), true);
1454 #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE)
1455 class SetCurrentIndexAfterAppendRowCrashDialog : public QDialog
1459 SetCurrentIndexAfterAppendRowCrashDialog()
1461 #if WINVER >= 0x0500
1462 listView = new QListView();
1463 listView->setViewMode(QListView::IconMode);
1465 model = new QStandardItemModel(this);
1466 listView->setModel(model);
1468 timer = new QTimer(this);
1469 connect(timer, SIGNAL(timeout()), this, SLOT(buttonClicked()));
1472 DWORD lParam = 0xFFFFFFFC/*OBJID_CLIENT*/;
1474 if (const HWND hwnd =getHWNDForWidget(this))
1475 SendMessage(hwnd, WM_GETOBJECT, wParam, lParam);
1480 void buttonClicked()
1483 QStandardItem *item = new QStandardItem("test");
1484 model->appendRow(item);
1485 listView->setCurrentIndex(model->indexFromItem(item));
1489 QListView *listView;
1490 QStandardItemModel *model;
1495 void tst_QListView::setCurrentIndexAfterAppendRowCrash()
1497 #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && WINVER >= 0x0500
1498 SetCurrentIndexAfterAppendRowCrashDialog w;
1501 QSKIP("This test only makes sense on windows 2000 and higher.");
1505 void tst_QListView::emptyItemSize()
1507 QStandardItemModel model;
1508 for (int r = 0; r < 4; ++r) {
1509 QStandardItem* item = new QStandardItem(QString("standard item (%1)").arg(r));
1510 model.setItem(r, 0, item);
1512 model.setItem(4, 0, new QStandardItem());
1514 PublicListView view;
1515 view.setModel(&model);
1517 for (int i = 0; i < 5; ++i)
1518 QVERIFY(!view.visualRect(model.index(i, 0)).isEmpty());
1521 void tst_QListView::task203585_selectAll()
1523 //we make sure that "select all" doesn't select the hidden items
1525 view.setSelectionMode(QAbstractItemView::ExtendedSelection);
1526 view.setModel(new QStringListModel( QStringList() << "foo"));
1527 view.setRowHidden(0, true);
1529 QVERIFY(view.selectionModel()->selectedIndexes().isEmpty());
1530 view.setRowHidden(0, false);
1532 QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1);
1535 void tst_QListView::task228566_infiniteRelayout()
1540 for (int i = 0; i < 10; ++i) {
1544 list << "BIGBIGBIGBIGBIGBIGBIGBIGBIGBIGBIGBIG";
1545 list << "BIGBIGBIGBIGBIGBIGBIGBIGBIGBIGBIGBIG";
1547 QStringListModel model(list);
1548 view.setModel(&model);
1549 view.setWrapping(true);
1550 view.setResizeMode(QListView::Adjust);
1552 const int itemHeight = view.visualRect( model.index(0, 0)).height();
1554 view.setFixedHeight(itemHeight * 12);
1556 QTest::qWait(100); //make sure the layout is done once
1558 QSignalSpy spy(view.horizontalScrollBar(), SIGNAL(rangeChanged(int, int)));
1561 //the layout should already have been done
1562 //so there should be no change made to the scrollbar
1563 QCOMPARE(spy.count(), 0);
1566 void tst_QListView::task248430_crashWith0SizedItem()
1569 view.setViewMode(QListView::IconMode);
1570 QStringListModel model(QStringList() << QLatin1String("item1") << QString());
1571 view.setModel(&model);
1573 QVERIFY(QTest::qWaitForWindowExposed(&view));
1577 void tst_QListView::task250446_scrollChanged()
1579 QStandardItemModel model(200, 1);
1581 view.setModel(&model);
1582 QModelIndex index = model.index(0, 0);
1583 QVERIFY(index.isValid());
1584 view.setCurrentIndex(index);
1586 QVERIFY(QTest::qWaitForWindowExposed(&view));
1587 const int scrollValue = view.verticalScrollBar()->maximum();
1588 view.verticalScrollBar()->setValue(scrollValue);
1589 QCOMPARE(view.verticalScrollBar()->value(), scrollValue);
1590 QCOMPARE(view.currentIndex(), index);
1592 view.showMinimized();
1594 QTRY_COMPARE(view.verticalScrollBar()->value(), scrollValue);
1595 QTRY_COMPARE(view.currentIndex(), index);
1599 QTRY_COMPARE(view.verticalScrollBar()->value(), scrollValue);
1600 QTRY_COMPARE(view.currentIndex(), index);
1603 void tst_QListView::task196118_visualRegionForSelection()
1605 class MyListView : public QListView
1608 QRegion visualRegionForSelection() const
1609 { return QListView::visualRegionForSelection( selectionModel()->selection()); }
1612 QStandardItemModel model;
1613 QStandardItem top1("top1");
1614 QStandardItem sub1("sub1");
1615 top1.appendRow(QList<QStandardItem*>() << &sub1);
1616 model.appendColumn(QList<QStandardItem*>() << &top1);
1617 view.setModel(&model);
1618 view.setRootIndex(top1.index());
1620 view.selectionModel()->select(top1.index(), QItemSelectionModel::Select);
1622 QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1);
1623 QVERIFY(view.visualRegionForSelection().isEmpty());
1626 void tst_QListView::task254449_draggingItemToNegativeCoordinates()
1628 //we'll check that the items are painted correctly
1629 class MyListView : public QListView
1632 void setPositionForIndex(const QPoint &position, const QModelIndex &index)
1633 { QListView::setPositionForIndex(position, index); }
1637 QStandardItemModel model(1,1);
1638 QModelIndex index = model.index(0,0);
1639 model.setData(index, QLatin1String("foo"));
1640 list.setModel(&model);
1641 list.setViewMode(QListView::IconMode);
1643 list.activateWindow();
1644 QVERIFY(QTest::qWaitForWindowActive(&list));
1647 class MyItemDelegate : public QStyledItemDelegate
1650 MyItemDelegate() : numPaints(0) { }
1651 void paint(QPainter *painter,
1652 const QStyleOptionViewItem &option, const QModelIndex &index) const
1655 QStyledItemDelegate::paint(painter, option, index);
1658 mutable int numPaints;
1660 delegate.numPaints = 0;
1661 list.setItemDelegate(&delegate);
1662 QApplication::processEvents();
1663 QTRY_VERIFY(delegate.numPaints > 0); //makes sure the layout is done
1665 const QPoint topLeft(-6, 0);
1666 list.setPositionForIndex(topLeft, index);
1668 //we'll make sure the item is repainted
1669 delegate.numPaints = 0;
1670 QApplication::processEvents();
1671 QTRY_COMPARE(delegate.numPaints, 1);
1672 QCOMPARE(list.visualRect(index).topLeft(), topLeft);
1676 void tst_QListView::keyboardSearch()
1679 items << "AB" << "AC" << "BA" << "BB" << "BD" << "KAFEINE" << "KONQUEROR" << "KOPETE" << "KOOKA" << "OKULAR";
1680 QStringListModel model(items);
1683 view.setModel(&model);
1685 qApp->setActiveWindow(&view);
1686 QVERIFY(QTest::qWaitForWindowActive(&view));
1688 // QCOMPARE(view.currentIndex() , model.index(0,0));
1690 QTest::keyClick(&view, Qt::Key_K);
1692 QCOMPARE(view.currentIndex() , model.index(5,0)); //KAFEINE
1694 QTest::keyClick(&view, Qt::Key_O);
1696 QCOMPARE(view.currentIndex() , model.index(6,0)); //KONQUEROR
1698 QTest::keyClick(&view, Qt::Key_N);
1700 QCOMPARE(view.currentIndex() , model.index(6,0)); //KONQUEROR
1703 void tst_QListView::shiftSelectionWithNonUniformItemSizes()
1705 // This checks that no items are selected unexpectedly by Shift-Arrow
1706 // when items with non-uniform sizes are laid out in a grid
1707 { // First test: QListView::LeftToRight flow
1709 items << "Long\nText" << "Text" << "Text" << "Text";
1710 QStringListModel model(items);
1713 view.setFixedSize(250, 250);
1714 view.setFlow(QListView::LeftToRight);
1715 view.setGridSize(QSize(100, 100));
1716 view.setSelectionMode(QListView::ExtendedSelection);
1717 view.setViewMode(QListView::IconMode);
1718 view.setModel(&model);
1720 QVERIFY(QTest::qWaitForWindowExposed(&view));
1722 // Verfify that item sizes are non-uniform
1723 QVERIFY(view.sizeHintForIndex(model.index(0, 0)).height() > view.sizeHintForIndex(model.index(1, 0)).height());
1725 QModelIndex index = model.index(3, 0);
1726 view.setCurrentIndex(index);
1727 QCOMPARE(view.currentIndex(), index);
1729 QTest::keyClick(&view, Qt::Key_Up, Qt::ShiftModifier);
1731 QCOMPARE(view.currentIndex(), model.index(1, 0));
1733 QModelIndexList selected = view.selectionModel()->selectedIndexes();
1734 QCOMPARE(selected.count(), 3);
1735 QVERIFY(!selected.contains(model.index(0, 0)));
1737 { // Second test: QListView::TopToBottom flow
1739 items << "ab" << "a" << "a" << "a";
1740 QStringListModel model(items);
1743 view.setFixedSize(250, 250);
1744 view.setFlow(QListView::TopToBottom);
1745 view.setGridSize(QSize(100, 100));
1746 view.setSelectionMode(QListView::ExtendedSelection);
1747 view.setViewMode(QListView::IconMode);
1748 view.setModel(&model);
1750 QVERIFY(QTest::qWaitForWindowExposed(&view));
1752 // Verfify that item sizes are non-uniform
1753 QVERIFY(view.sizeHintForIndex(model.index(0, 0)).width() > view.sizeHintForIndex(model.index(1, 0)).width());
1755 QModelIndex index = model.index(3, 0);
1756 view.setCurrentIndex(index);
1757 QCOMPARE(view.currentIndex(), index);
1759 QTest::keyClick(&view, Qt::Key_Left, Qt::ShiftModifier);
1761 QCOMPARE(view.currentIndex(), model.index(1, 0));
1763 QModelIndexList selected = view.selectionModel()->selectedIndexes();
1764 QCOMPARE(selected.count(), 3);
1765 QVERIFY(!selected.contains(model.index(0, 0)));
1769 void tst_QListView::clickOnViewportClearsSelection()
1773 QStringListModel model(items);
1775 view.setModel(&model);
1776 view.setSelectionMode(QListView::ExtendedSelection);
1779 QModelIndex index = model.index(0);
1780 QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1);
1781 QVERIFY(view.selectionModel()->isSelected(index));
1783 //we try to click outside of the index
1784 const QPoint point = view.visualRect(index).bottomRight() + QPoint(10,10);
1786 QTest::mousePress(view.viewport(), Qt::LeftButton, 0, point);
1787 //at this point, the selection shouldn't have changed
1788 QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1);
1789 QVERIFY(view.selectionModel()->isSelected(index));
1791 QTest::mouseRelease(view.viewport(), Qt::LeftButton, 0, point);
1792 //now the selection should be cleared
1793 QVERIFY(!view.selectionModel()->hasSelection());
1796 void tst_QListView::task262152_setModelColumnNavigate()
1799 QStandardItemModel model(3,2);
1800 model.setItem(0,1,new QStandardItem("[0,1]"));
1801 model.setItem(1,1,new QStandardItem("[1,1]"));
1802 model.setItem(2,1,new QStandardItem("[2,1]"));
1804 view.setModel(&model);
1805 view.setModelColumn(1);
1808 QApplication::setActiveWindow(&view);
1809 QVERIFY(QTest::qWaitForWindowActive(&view));
1810 QCOMPARE(static_cast<QWidget *>(&view), QApplication::activeWindow());
1811 QTest::keyClick(&view, Qt::Key_Down);
1813 QTRY_COMPARE(view.currentIndex(), model.index(1,1));
1814 QTest::keyClick(&view, Qt::Key_Down);
1816 QTRY_COMPARE(view.currentIndex(), model.index(2,1));
1819 void tst_QListView::taskQTBUG_2233_scrollHiddenItems_data()
1821 QTest::addColumn<int>("flow");
1823 QTest::newRow("TopToBottom") << static_cast<int>(QListView::TopToBottom);
1824 QTest::newRow("LeftToRight") << static_cast<int>(QListView::LeftToRight);
1827 void tst_QListView::taskQTBUG_2233_scrollHiddenItems()
1830 const int rowCount = 200;
1833 QListView view(&topLevel);
1834 QStringListModel model(&view);
1836 for (int i = 0; i < rowCount; ++i)
1837 list << QString::number(i);
1839 model.setStringList(list);
1840 view.setModel(&model);
1841 view.setUniformItemSizes(true);
1842 view.setViewMode(QListView::ListMode);
1843 for (int i = 0; i < rowCount / 2; ++i)
1844 view.setRowHidden(2 * i, true);
1845 view.setFlow(static_cast<QListView::Flow>(flow));
1846 view.resize(130, 130);
1848 for (int i = 0; i < 10; ++i) {
1849 (view.flow() == QListView::TopToBottom
1850 ? view.verticalScrollBar()
1851 : view.horizontalScrollBar())->setValue(i);
1852 QModelIndex index = view.indexAt(QPoint(0,0));
1853 QVERIFY(index.isValid());
1854 QCOMPARE(index.row(), 2 * i + 1);
1857 //QTBUG-7929 should not crash
1859 QVERIFY(QTest::qWaitForWindowExposed(&topLevel));
1860 QScrollBar *bar = view.flow() == QListView::TopToBottom
1861 ? view.verticalScrollBar() : view.horizontalScrollBar();
1863 int nbVisibleItem = rowCount / 2 - bar->maximum();
1865 bar->setValue(bar->maximum());
1866 QApplication::processEvents();
1867 for (int i = rowCount; i > rowCount / 2; i--) {
1868 view.setRowHidden(i, true);
1870 QApplication::processEvents();
1872 QCOMPARE(bar->value(), bar->maximum());
1873 QCOMPARE(bar->maximum(), rowCount/4 - nbVisibleItem);
1876 void tst_QListView::taskQTBUG_633_changeModelData()
1879 view.setFlow(QListView::LeftToRight);
1880 QStandardItemModel model(5,1);
1881 for (int i = 0; i < model.rowCount(); ++i) {
1882 model.setData( model.index(i, 0), QString::number(i));
1885 view.setModel(&model);
1887 QVERIFY(QTest::qWaitForWindowExposed(&view));
1888 model.setData( model.index(1, 0), QLatin1String("long long text"));
1889 QTest::qWait(100); //leave time for relayouting the items
1890 QRect rectLongText = view.visualRect(model.index(1,0));
1891 QRect rect2 = view.visualRect(model.index(2,0));
1892 QVERIFY( ! rectLongText.intersects(rect2) );
1895 void tst_QListView::taskQTBUG_435_deselectOnViewportClick()
1898 QStringListModel model( QStringList() << "1" << "2" << "3" << "4");
1899 view.setModel(&model);
1900 view.setSelectionMode(QAbstractItemView::ExtendedSelection);
1902 QCOMPARE(view.selectionModel()->selectedIndexes().count(), model.rowCount());
1905 const QRect itemRect = view.visualRect(model.index(model.rowCount() - 1));
1906 QPoint p = view.visualRect(model.index(model.rowCount() - 1)).center() + QPoint(0, itemRect.height());
1907 //first the left button
1908 QTest::mouseClick(view.viewport(), Qt::LeftButton, 0, p);
1909 QVERIFY(!view.selectionModel()->hasSelection());
1912 QCOMPARE(view.selectionModel()->selectedIndexes().count(), model.rowCount());
1914 //and now the right button
1915 QTest::mouseClick(view.viewport(), Qt::RightButton, 0, p);
1916 QVERIFY(!view.selectionModel()->hasSelection());
1919 void tst_QListView::taskQTBUG_2678_spacingAndWrappedText()
1921 static const QString lorem("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.");
1922 QStringListModel model(QStringList() << lorem << lorem << "foo" << lorem << "bar" << lorem << lorem);
1925 w.setViewMode(QListView::ListMode);
1926 w.setWordWrap(true);
1929 QVERIFY(QTest::qWaitForWindowExposed(&w));
1930 QCOMPARE(w.horizontalScrollBar()->minimum(), w.horizontalScrollBar()->maximum());
1933 void tst_QListView::taskQTBUG_5877_skippingItemInPageDownUp()
1935 QList<int> currentItemIndexes;
1936 QtTestModel model(0);
1940 currentItemIndexes << 0 << 6 << 16 << 25 << 34 << 42 << 57 << 68 << 77
1942 QMoveCursorListView vu;
1943 vu.setModel(&model);
1946 QVERIFY(QTest::qWaitForWindowExposed(&vu));
1948 int itemHeight = vu.visualRect(model.index(0, 0)).height();
1949 int visibleRowCount = vu.viewport()->height() / itemHeight;
1950 int scrolledRowCount = visibleRowCount - 1;
1952 for (int i = 0; i < currentItemIndexes.size(); ++i) {
1953 vu.selectionModel()->setCurrentIndex(model.index(currentItemIndexes[i], 0),
1954 QItemSelectionModel::SelectCurrent);
1956 QModelIndex idx = vu.moveCursor(QMoveCursorListView::MovePageDown, Qt::NoModifier);
1957 int newCurrent = qMin(currentItemIndexes[i] + scrolledRowCount, 99);
1958 QCOMPARE(idx, model.index(newCurrent, 0));
1960 idx = vu.moveCursor(QMoveCursorListView::MovePageUp, Qt::NoModifier);
1961 newCurrent = qMax(currentItemIndexes[i] - scrolledRowCount, 0);
1962 QCOMPARE(idx, model.index(newCurrent, 0));
1966 class ListView_9455 : public QListView
1969 QSize contentsSize() const
1971 return QListView::contentsSize();
1975 void tst_QListView::taskQTBUG_9455_wrongScrollbarRanges()
1978 const int nrItems = 8;
1979 for (int i = 0; i < nrItems; i++)
1980 list << QString().sprintf("item %d", i);
1982 QStringListModel model(list);
1985 w.setViewMode(QListView::IconMode);
1987 w.setMovement(QListView::Static);
1988 const int spacing = 40;
1989 w.setSpacing(spacing);
1991 QVERIFY(QTest::qWaitForWindowExposed(&w));
1992 QCOMPARE(w.verticalScrollBar()->maximum(), w.contentsSize().height() - w.viewport()->geometry().height());
1995 void tst_QListView::styleOptionViewItem()
1997 class MyDelegate : public QStyledItemDelegate
2000 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
2002 QVERIFY(qstyleoption_cast<const QStyleOptionViewItemV4 *>(&option));
2003 QStyleOptionViewItemV4 opt(option);
2004 initStyleOption(&opt, index);
2006 QCOMPARE(opt.index, index);
2008 QStyledItemDelegate::paint(painter, option, index);
2013 QStandardItemModel model;
2014 view.setModel(&model);
2015 MyDelegate delegate;
2016 view.setItemDelegate(&delegate);
2017 model.appendRow(QList<QStandardItem*>()
2018 << new QStandardItem("Beginning") << new QStandardItem("Middle") << new QStandardItem("Middle") << new QStandardItem("End") );
2021 view.showMaximized();
2022 QApplication::processEvents();
2025 void tst_QListView::taskQTBUG_12308_artihmeticException()
2028 lw.setLayoutMode(QListView::Batched);
2029 lw.setViewMode(QListView::IconMode);
2030 for (int i = 0; i < lw.batchSize() + 1; i++) {
2031 QListWidgetItem *item = new QListWidgetItem();
2032 item->setText(QString("Item %L1").arg(i));
2034 item->setHidden(true);
2037 QVERIFY(QTest::qWaitForWindowExposed(&lw));
2038 // No crash, it's all right.
2041 class Delegate12308 : public QStyledItemDelegate
2045 Delegate12308(QObject *parent = 0) : QStyledItemDelegate(parent)
2048 void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
2050 QVERIFY(option.rect.topLeft() != QPoint(-1, -1));
2051 QStyledItemDelegate::paint(painter, option, index);
2055 void tst_QListView::taskQTBUG_12308_wrongFlowLayout()
2058 Delegate12308 delegate;
2059 lw.setLayoutMode(QListView::Batched);
2060 lw.setViewMode(QListView::IconMode);
2061 lw.setItemDelegate(&delegate);
2062 for (int i = 0; i < lw.batchSize() + 1; i++) {
2063 QListWidgetItem *item = new QListWidgetItem();
2064 item->setText(QString("Item %L1").arg(i));
2066 if (!item->text().contains(QString::fromLatin1("1")))
2067 item->setHidden(true);
2070 QVERIFY(QTest::qWaitForWindowExposed(&lw));
2073 void tst_QListView::taskQTBUG_21115_scrollToAndHiddenItems_data()
2075 QTest::addColumn<int>("flow");
2076 QTest::newRow("flow TopToBottom") << static_cast<int>(QListView::TopToBottom);
2077 QTest::newRow("flow LeftToRight") << static_cast<int>(QListView::LeftToRight);
2080 void tst_QListView::taskQTBUG_21115_scrollToAndHiddenItems()
2085 lv.setUniformItemSizes(true);
2086 lv.setFlow(static_cast<QListView::Flow>(flow));
2088 QStringListModel model;
2090 for (int i = 0; i < 30; i++)
2091 list << QString::number(i);
2092 model.setStringList(list);
2093 lv.setModel(&model);
2095 QVERIFY(QTest::qWaitForWindowExposed(&lv));
2097 // Save first item rect for reference
2098 QRect firstItemRect = lv.visualRect(model.index(0, 0));
2100 // Select an item and scroll to selection
2101 QModelIndex index = model.index(2, 0);
2102 lv.setCurrentIndex(index);
2103 lv.scrollTo(index, QAbstractItemView::PositionAtTop);
2104 QApplication::processEvents();
2105 QCOMPARE(lv.visualRect(index), firstItemRect);
2107 // Hide some rows and scroll to selection
2108 for (int i = 0; i < 5; i++) {
2109 if (i == index.row())
2111 lv.setRowHidden(i, true);
2113 lv.scrollTo(index, QAbstractItemView::PositionAtTop);
2114 QApplication::processEvents();
2115 QCOMPARE(lv.visualRect(index), firstItemRect);
2119 QTEST_MAIN(tst_QListView)
2120 #include "tst_qlistview.moc"