/**************************************************************************** ** ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** ** This file is part of the test suite of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the either Technology Preview License Agreement or the ** Beta Release License Agreement. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain ** additional rights. These rights are described in the Nokia Qt LGPL ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this ** package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at qt-sales@nokia.com. ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #if defined(Q_OS_WIN) || defined(Q_OS_WINCE) #include #endif //TESTED_CLASS= //TESTED_FILES= class tst_QListView : public QObject { Q_OBJECT public: tst_QListView(); virtual ~tst_QListView(); public slots: void initTestCase(); void cleanupTestCase(); void init(); void cleanup(); private slots: void getSetCheck(); void noDelegate(); void noModel(); void emptyModel(); void removeRows(); void cursorMove(); void hideRows(); void moveCursor(); void moveCursor2(); void moveCursor3(); void indexAt(); void clicked(); void singleSelectionRemoveRow(); void singleSelectionRemoveColumn(); void modelColumn(); void hideFirstRow(); void batchedMode(); void setCurrentIndex(); void selection_data(); void selection(); void scrollTo(); void scrollBarRanges(); void scrollBarAsNeeded_data(); void scrollBarAsNeeded(); void moveItems(); void wordWrap(); void setCurrentIndexAfterAppendRowCrash(); void emptyItemSize(); void task203585_selectAll(); void task228566_infiniteRelayout(); void task248430_crashWith0SizedItem(); void task250446_scrollChanged(); void task196118_visualRegionForSelection(); void keyboardSearch(); }; // Testing get/set functions void tst_QListView::getSetCheck() { QListView obj1; // Movement QListView::movement() // void QListView::setMovement(Movement) obj1.setMovement(QListView::Movement(QListView::Static)); QCOMPARE(QListView::Movement(QListView::Static), obj1.movement()); obj1.setMovement(QListView::Movement(QListView::Free)); QCOMPARE(QListView::Movement(QListView::Free), obj1.movement()); obj1.setMovement(QListView::Movement(QListView::Snap)); QCOMPARE(QListView::Movement(QListView::Snap), obj1.movement()); // Flow QListView::flow() // void QListView::setFlow(Flow) obj1.setFlow(QListView::Flow(QListView::LeftToRight)); QCOMPARE(QListView::Flow(QListView::LeftToRight), obj1.flow()); obj1.setFlow(QListView::Flow(QListView::TopToBottom)); QCOMPARE(QListView::Flow(QListView::TopToBottom), obj1.flow()); // ResizeMode QListView::resizeMode() // void QListView::setResizeMode(ResizeMode) obj1.setResizeMode(QListView::ResizeMode(QListView::Fixed)); QCOMPARE(QListView::ResizeMode(QListView::Fixed), obj1.resizeMode()); obj1.setResizeMode(QListView::ResizeMode(QListView::Adjust)); QCOMPARE(QListView::ResizeMode(QListView::Adjust), obj1.resizeMode()); // LayoutMode QListView::layoutMode() // void QListView::setLayoutMode(LayoutMode) obj1.setLayoutMode(QListView::LayoutMode(QListView::SinglePass)); QCOMPARE(QListView::LayoutMode(QListView::SinglePass), obj1.layoutMode()); obj1.setLayoutMode(QListView::LayoutMode(QListView::Batched)); QCOMPARE(QListView::LayoutMode(QListView::Batched), obj1.layoutMode()); // int QListView::spacing() // void QListView::setSpacing(int) obj1.setSpacing(0); QCOMPARE(0, obj1.spacing()); obj1.setSpacing(INT_MIN); QCOMPARE(INT_MIN, obj1.spacing()); obj1.setSpacing(INT_MAX); QCOMPARE(INT_MAX, obj1.spacing()); // ViewMode QListView::viewMode() // void QListView::setViewMode(ViewMode) obj1.setViewMode(QListView::ViewMode(QListView::ListMode)); QCOMPARE(QListView::ViewMode(QListView::ListMode), obj1.viewMode()); obj1.setViewMode(QListView::ViewMode(QListView::IconMode)); QCOMPARE(QListView::ViewMode(QListView::IconMode), obj1.viewMode()); // int QListView::modelColumn() // void QListView::setModelColumn(int) obj1.setModelColumn(0); QCOMPARE(0, obj1.modelColumn()); obj1.setModelColumn(INT_MIN); QCOMPARE(0, obj1.modelColumn()); // Less than 0 => 0 obj1.setModelColumn(INT_MAX); QCOMPARE(0, obj1.modelColumn()); // No model => 0 // bool QListView::uniformItemSizes() // void QListView::setUniformItemSizes(bool) obj1.setUniformItemSizes(false); QCOMPARE(false, obj1.uniformItemSizes()); obj1.setUniformItemSizes(true); QCOMPARE(true, obj1.uniformItemSizes()); // make sure setViewMode() doesn't reset resizeMode obj1.clearPropertyFlags(); obj1.setResizeMode(QListView::Adjust); obj1.setViewMode(QListView::IconMode); QCOMPARE(obj1.resizeMode(), QListView::Adjust); obj1.setWordWrap(false); QCOMPARE(false, obj1.wordWrap()); obj1.setWordWrap(true); QCOMPARE(true, obj1. wordWrap()); } class QtTestModel: public QAbstractListModel { public: QtTestModel(QObject *parent = 0): QAbstractListModel(parent), colCount(0), rCount(0), wrongIndex(false) {} int rowCount(const QModelIndex&) const { return rCount; } int columnCount(const QModelIndex&) const { return colCount; } bool isEditable(const QModelIndex &) const { return true; } QVariant data(const QModelIndex &idx, int role) const { if (!m_icon.isNull() && role == Qt::DecorationRole) { return m_icon; } if (role != Qt::DisplayRole) return QVariant(); if (idx.row() < 0 || idx.column() < 0 || idx.column() >= colCount || idx.row() >= rCount) { wrongIndex = true; qWarning("got invalid modelIndex %d/%d", idx.row(), idx.column()); } return QString("%1/%2").arg(idx.row()).arg(idx.column()); } void removeLastRow() { beginRemoveRows(QModelIndex(), rCount - 2, rCount - 1); --rCount; endRemoveRows(); } void removeAllRows() { beginRemoveRows(QModelIndex(), 0, rCount - 1); rCount = 0; endRemoveRows(); } void setDataIcon(const QIcon &icon) { m_icon = icon; } int colCount, rCount; QIcon m_icon; mutable bool wrongIndex; }; tst_QListView::tst_QListView() { } tst_QListView::~tst_QListView() { } void tst_QListView::initTestCase() { } void tst_QListView::cleanupTestCase() { } void tst_QListView::init() { #ifdef Q_OS_WINCE //disable magic for WindowsCE qApp->setAutoMaximizeThreshold(-1); #endif } void tst_QListView::cleanup() { } void tst_QListView::noDelegate() { QtTestModel model(0); model.rCount = model.colCount = 10; QListView view; view.setModel(&model); view.setItemDelegate(0); view.show(); } void tst_QListView::noModel() { QListView view; view.show(); view.setRowHidden(0, true); } void tst_QListView::emptyModel() { QtTestModel model(0); QListView view; view.setModel(&model); view.show(); QVERIFY(!model.wrongIndex); } void tst_QListView::removeRows() { QtTestModel model(0); model.rCount = model.colCount = 10; QListView view; view.setModel(&model); view.show(); model.removeLastRow(); QVERIFY(!model.wrongIndex); model.removeAllRows(); QVERIFY(!model.wrongIndex); } void tst_QListView::cursorMove() { int rows = 6*6; int columns = 6; QStandardItemModel model(rows, columns); QListView view; view.setModel(&model); for (int j = 0; j < columns; ++j) { view.setModelColumn(j); for (int i = 0; i < rows; ++i) { QModelIndex index = model.index(i, j); model.setData(index, QString("[%1,%2]").arg(i).arg(j)); view.setCurrentIndex(index); QApplication::processEvents(); QCOMPARE(view.currentIndex(), index); } } QSize cellsize(60, 25); int gap = 1; // compensate for the scrollbars int displayColumns = 6; view.resize((displayColumns + gap) * cellsize.width(), int((ceil(double(rows) / displayColumns) + gap) * cellsize.height())); view.setResizeMode(QListView::Adjust); view.setGridSize(cellsize); view.setViewMode(QListView::IconMode); view.doItemsLayout(); view.show(); QVector keymoves; keymoves << Qt::Key_Up << Qt::Key_Up << Qt::Key_Right << Qt::Key_Right << Qt::Key_Up << Qt::Key_Left << Qt::Key_Left << Qt::Key_Up << Qt::Key_Down << Qt::Key_Up << Qt::Key_Up << Qt::Key_Up << Qt::Key_Up << Qt::Key_Up << Qt::Key_Up << Qt::Key_Left << Qt::Key_Left << Qt::Key_Up << Qt::Key_Down; int displayRow = rows / displayColumns - 1; int displayColumn = displayColumns - (rows % displayColumns) - 1; QApplication::instance()->processEvents(); for (int i = 0; i < keymoves.size(); ++i) { Qt::Key key = keymoves.at(i); QTest::keyClick(&view, key); switch (key) { case Qt::Key_Up: displayRow = qMax(0, displayRow - 1); break; case Qt::Key_Down: displayRow = qMin(rows / displayColumns - 1, displayRow + 1); break; case Qt::Key_Left: displayColumn = qMax(0, displayColumn - 1); break; case Qt::Key_Right: displayColumn = qMin(displayColumns-1, displayColumn + 1); break; default: QVERIFY(false); } QApplication::instance()->processEvents(); int row = displayRow * displayColumns + displayColumn; int column = columns - 1; QModelIndex index = model.index(row, column); QCOMPARE(view.currentIndex().row(), row); QCOMPARE(view.currentIndex().column(), column); QCOMPARE(view.currentIndex(), index); } } void tst_QListView::hideRows() { QtTestModel model(0); model.rCount = model.colCount = 10; QListView view; view.setModel(&model); view.show(); // hide then show QVERIFY(!view.isRowHidden(2)); view.setRowHidden(2, true); QVERIFY(view.isRowHidden(2)); view.setRowHidden(2, false); QVERIFY(!view.isRowHidden(2)); // re show same row QVERIFY(!view.isRowHidden(2)); view.setRowHidden(2, false); QVERIFY(!view.isRowHidden(2)); // double hidding QVERIFY(!view.isRowHidden(2)); view.setRowHidden(2, true); QVERIFY(view.isRowHidden(2)); view.setRowHidden(2, true); QVERIFY(view.isRowHidden(2)); view.setRowHidden(2, false); QVERIFY(!view.isRowHidden(2)); // show in per-item mode, then hide the first row view.setVerticalScrollMode(QAbstractItemView::ScrollPerItem); QVERIFY(!view.isRowHidden(0)); view.setRowHidden(0, true); QVERIFY(view.isRowHidden(0)); view.setRowHidden(0, false); QVERIFY(!view.isRowHidden(0)); } void tst_QListView::moveCursor() { QtTestModel model(0); model.rCount = model.colCount = 10; QListView view; view.setModel(&model); QTest::keyClick(&view, Qt::Key_Down); view.setModel(0); view.setModel(&model); view.setRowHidden(0, true); QTest::keyClick(&view, Qt::Key_Down); QCOMPARE(view.selectionModel()->currentIndex(), model.index(1, 0)); } class QMoveCursorListView : public QListView { public: QMoveCursorListView() : QListView() {} enum CursorAction { MoveUp, MoveDown, MoveLeft, MoveRight, MoveHome, MoveEnd, MovePageUp, MovePageDown, MoveNext, MovePrevious }; QModelIndex moveCursor(QMoveCursorListView::CursorAction action, Qt::KeyboardModifiers modifiers) { return QListView::moveCursor((QListView::CursorAction)action, modifiers); } }; void tst_QListView::moveCursor2() { QtTestModel model(0); model.colCount = 1; model.rCount = 100; QPixmap pm(32, 32); pm.fill(Qt::green); model.setDataIcon(QIcon(pm)); QMoveCursorListView vu; vu.setModel(&model); vu.setIconSize(QSize(36,48)); vu.setGridSize(QSize(34,56)); //Standard framesize is 1. If Framesize > 2 increase size int frameSize = qApp->style()->pixelMetric(QStyle::PM_DefaultFrameWidth); vu.resize(300 + frameSize * 2,300); vu.setFlow(QListView::LeftToRight); vu.setMovement(QListView::Static); vu.setWrapping(true); vu.setViewMode(QListView::IconMode); vu.setLayoutMode(QListView::Batched); vu.show(); vu.selectionModel()->setCurrentIndex(model.index(0,0), QItemSelectionModel::SelectCurrent); QCoreApplication::processEvents(); QModelIndex idx = vu.moveCursor(QMoveCursorListView::MoveHome, Qt::NoModifier); QCOMPARE(idx, model.index(0,0)); idx = vu.moveCursor(QMoveCursorListView::MoveDown, Qt::NoModifier); QModelIndex p = model.index(8,0); QCOMPARE(idx, model.index(8,0)); } void tst_QListView::moveCursor3() { //this tests is for task 159792 //it tests that navigation works even with non uniform item sizes QListView view; QStandardItemModel model(0, 1); QStandardItem *i1 = new QStandardItem("First item, long name"); QStandardItem *i2 = new QStandardItem("2nd item"); QStandardItem *i3 = new QStandardItem("Third item, long name"); i1->setSizeHint(QSize(200,32)); model.appendRow(i1); model.appendRow(i2); model.appendRow(i3); view.setModel(&model); view.setCurrentIndex(model.index(0, 0)); QCOMPARE(view.selectionModel()->currentIndex(), model.index(0, 0)); QTest::keyClick(&view, Qt::Key_Down); QCOMPARE(view.selectionModel()->currentIndex(), model.index(1, 0)); QTest::keyClick(&view, Qt::Key_Down); QCOMPARE(view.selectionModel()->currentIndex(), model.index(2, 0)); QTest::keyClick(&view, Qt::Key_Up); QCOMPARE(view.selectionModel()->currentIndex(), model.index(1, 0)); QTest::keyClick(&view, Qt::Key_Up); QCOMPARE(view.selectionModel()->currentIndex(), model.index(0, 0)); } class QListViewShowEventListener : public QListView { public: QListViewShowEventListener() : QListView() { m_shown = false;} virtual void showEvent(QShowEvent * /*e*/) { int columnwidth = sizeHintForColumn(0); QSize sz = sizeHintForIndex(model()->index(0,0)); // This should retrieve a model index in the 2nd section m_index = indexAt(QPoint(columnwidth +2, sz.height()/2)); m_shown = true; } QModelIndex m_index; bool m_shown; }; void tst_QListView::indexAt() { QtTestModel model(0); model.rCount = 2; model.colCount = 1; QListView view; view.setModel(&model); view.setViewMode(QListView::ListMode); view.setFlow(QListView::TopToBottom); QSize sz = view.sizeHintForIndex(model.index(0,0)); QModelIndex index; index = view.indexAt(QPoint(20,0)); QVERIFY(index.isValid()); QCOMPARE(index.row(), 0); index = view.indexAt(QPoint(20,sz.height())); QVERIFY(index.isValid()); QCOMPARE(index.row(), 1); index = view.indexAt(QPoint(20,2 * sz.height())); QVERIFY(!index.isValid()); model.rCount = 30; QListViewShowEventListener view2; // Set the height to a small enough value so that it wraps to a new section. view2.resize(300,100); view2.setModel(&model); view2.setFlow(QListView::TopToBottom); view2.setViewMode(QListView::ListMode); view2.setWrapping(true); // We really want to make sure it is shown, because the layout won't be known until it is shown view2.show(); for (int i = 0; i < 5 && !view2.m_shown; ++i) { QTest::qWait(500); } QVERIFY(view2.m_index.isValid()); QVERIFY(view2.m_index.row() != 0); } void tst_QListView::clicked() { QtTestModel model; model.rCount = 10; model.colCount = 2; qRegisterMetaType("QModelIndex"); QListView view; view.setModel(&model); view.show(); QApplication::processEvents(); QModelIndex firstIndex = model.index(0, 0, QModelIndex()); QVERIFY(firstIndex.isValid()); int itemHeight = view.visualRect(firstIndex).height(); view.resize(200, itemHeight * (model.rCount + 1)); for (int i = 0; i < model.rCount; ++i) { QPoint p(5, 1 + itemHeight * i); QModelIndex index = view.indexAt(p); if (!index.isValid()) continue; QSignalSpy spy(&view, SIGNAL(clicked(const QModelIndex&))); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p); QCOMPARE(spy.count(), 1); } } void tst_QListView::singleSelectionRemoveRow() { QStringList items; items << "item1" << "item2" << "item3" << "item4"; QStringListModel model(items); QListView view; view.setModel(&model); view.show(); QModelIndex index; view.setCurrentIndex(model.index(1)); index = view.currentIndex(); QCOMPARE(view.model()->data(index).toString(), QString("item2")); model.removeRow(1); index = view.currentIndex(); QCOMPARE(view.model()->data(index).toString(), QString("item3")); model.removeRow(0); index = view.currentIndex(); QCOMPARE(view.model()->data(index).toString(), QString("item3")); } void tst_QListView::singleSelectionRemoveColumn() { int numCols = 3; int numRows = 3; QStandardItemModel model(numCols, numRows); for (int r = 0; r < numRows; ++r) for (int c = 0; c < numCols; ++c) model.setData(model.index(r, c), QString("%1,%2").arg(r).arg(c)); QListView view; view.setModel(&model); view.show(); QModelIndex index; view.setCurrentIndex(model.index(1, 1)); index = view.currentIndex(); QCOMPARE(view.model()->data(index).toString(), QString("1,1")); model.removeColumn(1); index = view.currentIndex(); QCOMPARE(view.model()->data(index).toString(), QString("1,0")); model.removeColumn(0); index = view.currentIndex(); QCOMPARE(view.model()->data(index).toString(), QString("1,2")); } void tst_QListView::modelColumn() { int numCols = 3; int numRows = 3; QStandardItemModel model(numCols, numRows); for (int r = 0; r < numRows; ++r) for (int c = 0; c < numCols; ++c) model.setData(model.index(r, c), QString("%1,%2").arg(r).arg(c)); QListView view; view.setModel(&model); // // Set and get with a valid model // // Default is column 0 QCOMPARE(view.modelColumn(), 0); view.setModelColumn(0); QCOMPARE(view.modelColumn(), 0); view.setModelColumn(1); QCOMPARE(view.modelColumn(), 1); view.setModelColumn(2); QCOMPARE(view.modelColumn(), 2); // Out of bound cases should not modify the modelColumn view.setModelColumn(-1); QCOMPARE(view.modelColumn(), 2); view.setModelColumn(INT_MAX); QCOMPARE(view.modelColumn(), 2); // See if it displays the right column using indexAt()... view.resize(400,400); view.show(); for (int c = 0; c < 3; ++c) { view.setModelColumn(c); int startrow = 0; for (int y = 0; y < view.height(); ++y) { QModelIndex idx = view.indexAt( QPoint(1, y) ); if (idx.row() == startrow + 1) ++startrow; else if (idx.row() == -1) break; QCOMPARE(idx.row(), startrow); QCOMPARE(idx.column(), c); } QCOMPARE(startrow, 2); } } void tst_QListView::hideFirstRow() { QStringList items; for (int i=0; i <100; ++i) items << "item"; QStringListModel model(items); QListView view; view.setModel(&model); view.setUniformItemSizes(true); view.setRowHidden(0,true); view.show(); QTest::qWait(100); } void tst_QListView::batchedMode() { QStringList items; for (int i=0; i <3; ++i) items << "item"; QStringListModel model(items); QListView view; view.setModel(&model); view.setUniformItemSizes(true); view.setViewMode(QListView::ListMode); view.setLayoutMode(QListView::Batched); view.setBatchSize(2); view.resize(200,400); view.show(); #if !defined(Q_OS_WINCE) QTest::qWait(100); #else QTest::qWait(2000); #endif QBitArray ba; for (int y = 0; y < view.height(); ++y) { QModelIndex idx = view.indexAt( QPoint(1, y) ); if (!idx.isValid()) break; if (idx.row() >= ba.size()) ba.resize(idx.row() + 1); ba.setBit(idx.row(), true); } QCOMPARE(ba.size(), 3); // Test the dynamic listview too. view.setViewMode(QListView::IconMode); view.setLayoutMode(QListView::Batched); view.setFlow(QListView::TopToBottom); view.setBatchSize(2); #if !defined(Q_OS_WINCE) QTest::qWait(100); #else QTest::qWait(2000); #endif ba.clear(); for (int y = 0; y < view.height(); ++y) { QModelIndex idx = view.indexAt( QPoint(1, y) ); if (!idx.isValid()) break; if (idx.row() >= ba.size()) ba.resize(idx.row() + 1); ba.setBit(idx.row(), true); } QCOMPARE(ba.size(), 3); } void tst_QListView::setCurrentIndex() { QStringList items; int i; for (i=0; i <20; ++i) items << QString("item %1").arg(i); QStringListModel model(items); QListView view; view.setModel(&model); view.resize(220,182); view.show(); for (int pass = 0; pass < 2; ++pass) { view.setFlow(pass == 0 ? QListView::TopToBottom : QListView::LeftToRight); QScrollBar *sb = pass == 0 ? view.verticalScrollBar() : view.horizontalScrollBar(); QList gridsizes; gridsizes << QSize() << QSize(200,38); for (int ig = 0; ig < gridsizes.count(); ++ig) { if (pass == 1 && !gridsizes.at(ig).isValid()) // the width of an item varies, so it might jump two times continue; view.setGridSize(gridsizes.at(ig)); qApp->processEvents(); int offset = sb->value(); // first "scroll" down, verify that we scroll one step at a time i = 0; for (i = 0; i < 20; ++i) { QModelIndex idx = model.index(i,0); view.setCurrentIndex(idx); if (offset != sb->value()) { // If it has scrolled, it should have scrolled only by one. QCOMPARE(sb->value(), offset + 1); ++offset; } //QTest::qWait(50); } --i; // item 20 does not exist // and then "scroll" up, verify that we scroll one step at a time for (; i >= 0; --i) { QModelIndex idx = model.index(i,0); view.setCurrentIndex(idx); if (offset != sb->value()) { // If it has scrolled, it should have scrolled only by one. QCOMPARE(sb->value(), offset - 1); --offset; } //QTest::qWait(50); } } } } class PublicListView : public QListView { public: PublicListView(QWidget *parent = 0) : QListView(parent) { } void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags flags) { QListView::setSelection(rect, flags); } QSize contentsSize() const { return QListView::contentsSize(); } void setPositionForIndex(const QPoint &pos, const QModelIndex &index) { QListView::setPositionForIndex(pos, index); } }; class TestDelegate : public QItemDelegate { public: TestDelegate(QObject *parent) : QItemDelegate(parent), m_sizeHint(50,50) {} QSize sizeHint(const QStyleOptionViewItem &, const QModelIndex &) const { return m_sizeHint; } QSize m_sizeHint; }; typedef QList IntList; Q_DECLARE_METATYPE(IntList) void tst_QListView::selection_data() { QTest::addColumn("itemCount"); QTest::addColumn("viewMode"); QTest::addColumn("flow"); QTest::addColumn("wrapping"); QTest::addColumn("spacing"); QTest::addColumn("gridSize"); QTest::addColumn("hiddenRows"); QTest::addColumn("selectionRect"); QTest::addColumn("expectedItems"); QTest::newRow("select all") << 4 // itemCount << int(QListView::ListMode) << int(QListView::TopToBottom) << false // wrapping << 0 // spacing << QSize() // gridSize << IntList() // hiddenRows << QRect(0, 0, 10, 200) // selection rectangle << (IntList() << 0 << 1 << 2 << 3); // expected items QTest::newRow("select below, (on viewport)") << 4 // itemCount << int(QListView::ListMode) << int(QListView::TopToBottom) << false // wrapping << 0 // spacing << QSize() // gridSize << IntList() // hiddenRows << QRect(10, 250, 1, 1) // selection rectangle << IntList(); // expected items QTest::newRow("select below 2, (on viewport)") << 4 // itemCount << int(QListView::ListMode) << int(QListView::TopToBottom) << true // wrapping << 0 // spacing << QSize() // gridSize << IntList() // hiddenRows << QRect(10, 250, 1, 1) // selection rectangle << IntList(); // expected items QTest::newRow("select to the right, (on viewport)") << 40 // itemCount << int(QListView::ListMode) << int(QListView::TopToBottom) << true // wrapping << 0 // spacing << QSize() // gridSize << IntList() // hiddenRows << QRect(300, 10, 1, 1) // selection rectangle << IntList(); // expected items QTest::newRow("select to the right, (on viewport)") << 40 // itemCount << int(QListView::ListMode) << int(QListView::TopToBottom) << true // wrapping << 0 // spacing << QSize() // gridSize << IntList() // hiddenRows << QRect(300, 0, 1, 300) // selection rectangle << IntList(); // expected items #if defined(Q_OS_WINCE) // depending on wether the display is double-pixeld, we need // to click at a different position bool doubledSize = false; int dpi = GetDeviceCaps(GetDC(0), LOGPIXELSX); if ((dpi < 1000) && (dpi > 0)) { doubledSize = true; } QTest::newRow("select inside contents, (on viewport)") << 35 // itemCount << int(QListView::ListMode) << int(QListView::TopToBottom) << true // wrapping << 0 // spacing << QSize() // gridSize << IntList() // hiddenRows << QRect(doubledSize?350:175,doubledSize?550:275, 1, 1)// selection rectangle << IntList(); // expected items #else QTest::newRow("select inside contents, (on viewport)") << 35 // itemCount << int(QListView::ListMode) << int(QListView::TopToBottom) << true // wrapping << 0 // spacing << QSize() // gridSize << IntList() // hiddenRows << QRect(175, 275, 1, 1) // selection rectangle << IntList(); // expected items #endif QTest::newRow("select a tall rect in LeftToRight flow, wrap items") << 70 // itemCount << int(QListView::ListMode) << int(QListView::LeftToRight) << true // wrapping << 0 // spacing << QSize() // gridSize << IntList() // hiddenRows << QRect(90, 90, 1, 100) // selection rectangle << (IntList() // expected items << 11 << 12 << 13 << 14 << 15 << 16 << 17 << 18 << 19 << 20 << 21 << 22 << 23 << 24 << 25 << 26 << 27 << 28 << 29 << 30 << 31); QTest::newRow("select a wide rect in LeftToRight, wrap items") << 70 // itemCount << int(QListView::ListMode) << int(QListView::LeftToRight) << true // wrapping << 0 // spacing << QSize() // gridSize << IntList() // hiddenRows << QRect(90, 90, 200, 1) // selection rectangle << (IntList() // expected items << 11 << 12 << 13 << 14 << 15); QTest::newRow("select a wide negative rect in LeftToRight flow, wrap items") << 70 // itemCount << int(QListView::ListMode) << int(QListView::LeftToRight) << true // wrapping << 0 // spacing << QSize() // gridSize << IntList() // hiddenRows << QRect(290, 90, -200, 1) // selection rectangle << (IntList() // expected items << 11 << 12 << 13 << 14 << 15); QTest::newRow("select a tall rect in TopToBottom flow, wrap items") << 70 // itemCount << int(QListView::ListMode) << int(QListView::TopToBottom) << true // wrapping << 0 // spacing << QSize() // gridSize << IntList() // hiddenRows << QRect(90, 90, 1, 100) // selection rectangle << (IntList() // expected items << 11 << 12 << 13); QTest::newRow("select a tall negative rect in TopToBottom flow, wrap items") << 70 // itemCount << int(QListView::ListMode) << int(QListView::TopToBottom) << true // wrapping << 0 // spacing << QSize() // gridSize << IntList() // hiddenRows << QRect(90, 190, 1, -100) // selection rectangle << (IntList() // expected items << 11 << 12 << 13); QTest::newRow("select a wide rect in TopToBottom, wrap items") << 70 // itemCount << int(QListView::ListMode) << int(QListView::TopToBottom) << true // wrapping << 0 // spacing << QSize() // gridSize << IntList() // hiddenRows << QRect(90, 90, 100, 1) // selection rectangle << (IntList() // expected items << 20 << 30 << 11 << 21 << 31 << 12 << 22 << 13 << 23 << 14 << 24 << 15 << 25 << 16 << 26 << 17 << 27 << 18 << 28 << 19 << 29); } void tst_QListView::selection() { QFETCH(int, itemCount); QFETCH(int, viewMode); QFETCH(int, flow); QFETCH(bool, wrapping); QFETCH(int, spacing); QFETCH(QSize, gridSize); QFETCH(IntList, hiddenRows); QFETCH(QRect, selectionRect); QFETCH(IntList, expectedItems); PublicListView v; QtTestModel model; model.colCount = 1; model.rCount = itemCount; // avoid scrollbar size mismatches among different styles v.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); v.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); v.setItemDelegate(new TestDelegate(&v)); v.setModel(&model); v.setViewMode(QListView::ViewMode(viewMode)); v.setFlow(QListView::Flow(flow)); v.setWrapping(wrapping); v.setResizeMode(QListView::Adjust); v.setSpacing(spacing); if (gridSize.isValid()) v.setGridSize(gridSize); for (int j = 0; j < hiddenRows.count(); ++j) { v.setRowHidden(hiddenRows.at(j), true); } #if defined(Q_OS_WINCE) // If the device is double-pixeled then the scrollbars become // 10 pixels wider than normal (Windows Style: 16, Windows Mobile Style: 26). // So we have to make the window slightly bigger to have the same count of // items in each row of the list view like in the other styles. static const int dpi = ::GetDeviceCaps(GetDC(0), LOGPIXELSX); if ((dpi < 1000) && (dpi > 0)) v.resize(535,535); #else v.resize(525,525); #endif v.show(); QApplication::processEvents(); v.setSelection(selectionRect, QItemSelectionModel::ClearAndSelect); QModelIndexList selected = v.selectionModel()->selectedIndexes(); QCOMPARE(selected.count(), expectedItems.count()); for (int i = 0; i < selected.count(); ++i) { QVERIFY(expectedItems.contains(selected.at(i).row())); } } void tst_QListView::scrollTo() { QListView lv; QStringListModel model(&lv); QStringList list; list << "Short item 1"; list << "Short item 2"; list << "Short item 3"; list << "Short item 4"; list << "Short item 5"; list << "Short item 6"; 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" "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" "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" "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" "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" "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" "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" "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" "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" "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" "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" "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" "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" "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" "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" "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"; list << "Short item"; list << "Short item"; list << "Short item"; list << "Short item"; list << "Short item"; list << "Short item"; list << "Short item"; list << "Short item"; model.setStringList(list); lv.setModel(&model); lv.setFixedSize(100, 200); lv.show(); //by default, the list view scrolls per item and has no wrapping QModelIndex index = model.index(6,0); //we save the size of the item for later comparisons const QSize itemsize = lv.visualRect(index).size(); QVERIFY(itemsize.height() > lv.height()); QVERIFY(itemsize.width() > lv.width()); //we click the item QPoint p = lv.visualRect(index).center(); QTest::mouseClick(lv.viewport(), Qt::LeftButton, Qt::NoModifier, p); //let's wait 1 second because the scrolling is delayed QTest::qWait(1000); QCOMPARE(lv.visualRect(index).y(),0); //we scroll down. As the item is to tall for the view, it will disappear QTest::keyClick(lv.viewport(), Qt::Key_Down, Qt::NoModifier); QCOMPARE(lv.visualRect(index).y(), -itemsize.height()); QTest::keyClick(lv.viewport(), Qt::Key_Up, Qt::NoModifier); QCOMPARE(lv.visualRect(index).y(), 0); //Let's enable wrapping lv.setWrapping(true); lv.horizontalScrollBar()->setValue(0); //let's scroll to the beginning //we click the item p = lv.visualRect(index).center(); QTest::mouseClick(lv.viewport(), Qt::LeftButton, Qt::NoModifier, p); //let's wait 1 second because the scrolling is delayed QTest::qWait(1000); QCOMPARE(lv.visualRect(index).x(),0); //we scroll right. As the item is too wide for the view, it will disappear QTest::keyClick(lv.viewport(), Qt::Key_Right, Qt::NoModifier); QCOMPARE(lv.visualRect(index).x(), -itemsize.width()); QTest::keyClick(lv.viewport(), Qt::Key_Left, Qt::NoModifier); QCOMPARE(lv.visualRect(index).x(), 0); lv.setWrapping(false); qApp->processEvents(); //let the layout happen //Let's try with scrolling per pixel lv.setHorizontalScrollMode( QListView::ScrollPerPixel); lv.verticalScrollBar()->setValue(0); //scrolls back to the first item //we click the item p = lv.visualRect(index).center(); QTest::mouseClick(lv.viewport(), Qt::LeftButton, Qt::NoModifier, p); //let's wait 1 second because the scrolling is delayed QTest::qWait(1000); QCOMPARE(lv.visualRect(index).y(),0); //we scroll down. As the item is too tall for the view, it will partially disappear QTest::keyClick(lv.viewport(), Qt::Key_Down, Qt::NoModifier); QVERIFY(lv.visualRect(index).y()<0); QTest::keyClick(lv.viewport(), Qt::Key_Up, Qt::NoModifier); QCOMPARE(lv.visualRect(index).y(), 0); } void tst_QListView::scrollBarRanges() { const int rowCount = 10; const int rowHeight = 20; QListView lv; QStringListModel model(&lv); QStringList list; for (int i = 0; i < rowCount; ++i) list << QString::fromAscii("Item %1").arg(i); model.setStringList(list); lv.setModel(&model); lv.resize(250, 130); TestDelegate *delegate = new TestDelegate(&lv); delegate->m_sizeHint = QSize(100, rowHeight); lv.setItemDelegate(delegate); lv.show(); for (int h = 30; h <= 210; ++h) { lv.resize(250, h); QTest::qWait(100); // wait for the layout to be done int visibleRowCount = lv.viewport()->size().height() / rowHeight; int invisibleRowCount = rowCount - visibleRowCount; QCOMPARE(lv.verticalScrollBar()->maximum(), invisibleRowCount); } } void tst_QListView::scrollBarAsNeeded_data() { QTest::addColumn("size"); QTest::addColumn("itemCount"); QTest::addColumn("flow"); QTest::addColumn("horizontalScrollBarVisible"); QTest::addColumn("verticalScrollBarVisible"); QTest::newRow("TopToBottom, count:0") << QSize(200, 100) << 0 << int(QListView::TopToBottom) << false << false; QTest::newRow("TopToBottom, count:1") << QSize(200, 100) << 1 << int(QListView::TopToBottom) << false << false; QTest::newRow("TopToBottom, count:20") << QSize(200, 100) << 20 << int(QListView::TopToBottom) << false << true; QTest::newRow("LeftToRight, count:0") << QSize(200, 100) << 0 << int(QListView::LeftToRight) << false << false; QTest::newRow("LeftToRight, count:1") << QSize(200, 100) << 1 << int(QListView::LeftToRight) << false << false; QTest::newRow("LeftToRight, count:20") << QSize(200, 100) << 20 << int(QListView::LeftToRight) << true << false; } void tst_QListView::scrollBarAsNeeded() { QFETCH(QSize, size); QFETCH(int, itemCount); QFETCH(int, flow); QFETCH(bool, horizontalScrollBarVisible); QFETCH(bool, verticalScrollBarVisible); const int rowCounts[3] = {0, 1, 20}; QListView lv; lv.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); lv.setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); lv.setFlow((QListView::Flow)flow); QStringListModel model(&lv); lv.setModel(&model); lv.resize(size); lv.show(); for (uint r = 0; r < sizeof(rowCounts)/sizeof(int); ++r) { QStringList list; int i; for (i = 0; i < rowCounts[r]; ++i) list << QString::fromAscii("Item %1").arg(i); model.setStringList(list); QApplication::processEvents(); QTest::qWait(100); QStringList replacement; for (i = 0; i < itemCount; ++i) { replacement << QString::fromAscii("Item %1").arg(i); } model.setStringList(replacement); QApplication::processEvents(); QTest::qWait(100); QCOMPARE(lv.horizontalScrollBar()->isVisible(), horizontalScrollBarVisible); QCOMPARE(lv.verticalScrollBar()->isVisible(), verticalScrollBarVisible); } } void tst_QListView::moveItems() { QStandardItemModel model; for (int r = 0; r < 4; ++r) { for (int c = 0; c < 4; ++c) { QStandardItem* item = new QStandardItem(QString("standard item (%1,%2)").arg(r).arg(c)); model.setItem(r, c, item); } } PublicListView view; view.setViewMode(QListView::IconMode); view.setResizeMode(QListView::Fixed); view.setWordWrap(true); view.setModel(&model); view.setItemDelegate(new TestDelegate(&view)); for (int r = 0; r < model.rowCount(); ++r) { for (int c = 0; c < model.columnCount(); ++c) { const QModelIndex& idx = model.index(r, c); view.setPositionForIndex(QPoint(r * 75, r * 75), idx); } } QCOMPARE(view.contentsSize(), QSize(275, 275)); } void tst_QListView::wordWrap() { QListView lv; lv.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); lv.setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); QStringListModel model(&lv); QStringList list; list << "Short item 1"; list << "Short item 2"; list << "Short item 3"; list << "Begin\nThis item take severals Lines\nEnd"; list << "And this is a very long item very long item this is a very vary vary long item" "very long very very long long long this is a long item a very long item a very very long item"; 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" "very long very very long long long this is a long item a very long item a very very long item"; list << "Short item"; list << "rzeofig zerig fslfgj smdlfkgj qmsdlfj amrzriougf qsla zrg fgsdf gsdfg sdfgs dfg sdfgcvb sdfg qsdjfh qsdfjklh qs"; list << "Short item"; model.setStringList(list); lv.setModel(&model); lv.setWordWrap(true); lv.setFixedSize(150, 150); lv.show(); QApplication::processEvents(); QTest::qWait(100); QCOMPARE(lv.horizontalScrollBar()->isVisible(), false); QCOMPARE(lv.verticalScrollBar()->isVisible(), true); } #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) class SetCurrentIndexAfterAppendRowCrashDialog : public QDialog { Q_OBJECT public: SetCurrentIndexAfterAppendRowCrashDialog() { #if WINVER >= 0x0500 listView = new QListView(); listView->setViewMode(QListView::IconMode); model = new QStandardItemModel(this); listView->setModel(model); timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(buttonClicked())); timer->start(1000); DWORD lParam = 0xFFFFFFFC/*OBJID_CLIENT*/; DWORD wParam = 0; SendMessage(winId(), WM_GETOBJECT, wParam, lParam); #endif } private slots: void buttonClicked() { timer->stop(); QStandardItem *item = new QStandardItem("test"); model->appendRow(item); listView->setCurrentIndex(model->indexFromItem(item)); close(); } private: QListView *listView; QStandardItemModel *model; QTimer *timer; }; #endif void tst_QListView::setCurrentIndexAfterAppendRowCrash() { #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && WINVER >= 0x0500 SetCurrentIndexAfterAppendRowCrashDialog w; w.exec(); #else QSKIP("This test only makes sense on windows 2000 and higher.", SkipAll); #endif } void tst_QListView::emptyItemSize() { QStandardItemModel model; for (int r = 0; r < 4; ++r) { QStandardItem* item = new QStandardItem(QString("standard item (%1)").arg(r)); model.setItem(r, 0, item); } model.setItem(4, 0, new QStandardItem()); PublicListView view; view.setModel(&model); for (int i = 0; i < 5; ++i) QVERIFY(!view.visualRect(model.index(i, 0)).isEmpty()); } void tst_QListView::task203585_selectAll() { //we make sure that "select all" doesn't select the hidden items QListView view; view.setSelectionMode(QAbstractItemView::ExtendedSelection); view.setModel(new QStringListModel( QStringList() << "foo")); view.setRowHidden(0, true); view.selectAll(); QVERIFY(view.selectionModel()->selectedIndexes().isEmpty()); view.setRowHidden(0, false); view.selectAll(); QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); } void tst_QListView::task228566_infiniteRelayout() { QListView view; QStringList list; for (int i = 0; i < 10; ++i) { list << "small"; } list << "BIGBIGBIGBIGBIGBIGBIGBIGBIGBIGBIGBIG"; list << "BIGBIGBIGBIGBIGBIGBIGBIGBIGBIGBIGBIG"; QStringListModel model(list); view.setModel(&model); view.setWrapping(true); view.setResizeMode(QListView::Adjust); const int itemHeight = view.visualRect( model.index(0, 0)).height(); view.setFixedHeight(itemHeight * 12); view.show(); QTest::qWait(100); //make sure the layout is done once QSignalSpy spy(view.horizontalScrollBar(), SIGNAL(rangeChanged(int, int))); QTest::qWait(200); //the layout should already have been done //so there should be no change made to the scrollbar QCOMPARE(spy.count(), 0); } void tst_QListView::task248430_crashWith0SizedItem() { QListView view; view.setViewMode(QListView::IconMode); QStringListModel model(QStringList() << QLatin1String("item1") << QString()); view.setModel(&model); view.show(); QTest::qWait(100); } void tst_QListView::task250446_scrollChanged() { QStandardItemModel model(200, 1); QListView view; view.setModel(&model); QModelIndex index = model.index(0, 0); QVERIFY(index.isValid()); view.setCurrentIndex(index); view.show(); QTest::qWait(100); const int scrollValue = view.verticalScrollBar()->maximum(); view.verticalScrollBar()->setValue(scrollValue); QCOMPARE(view.verticalScrollBar()->value(), scrollValue); QCOMPARE(view.currentIndex(), index); view.showMinimized(); QTest::qWait(100); QCOMPARE(view.verticalScrollBar()->value(), scrollValue); QCOMPARE(view.currentIndex(), index); view.showNormal(); QTest::qWait(100); QCOMPARE(view.verticalScrollBar()->value(), scrollValue); QCOMPARE(view.currentIndex(), index); } void tst_QListView::task196118_visualRegionForSelection() { class MyListView : public QListView { public: QRegion visualRegionForSelection() const { return QListView::visualRegionForSelection( selectionModel()->selection()); } } view; QStandardItemModel model; QStandardItem top1("top1"); QStandardItem sub1("sub1"); top1.appendRow(QList() << &sub1); model.appendColumn(QList() << &top1); view.setModel(&model); view.setRootIndex(top1.index()); view.selectionModel()->select(top1.index(), QItemSelectionModel::Select); QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); QVERIFY(view.visualRegionForSelection().isEmpty()); } void tst_QListView::keyboardSearch() { QStringList items; items << "AB" << "AC" << "BA" << "BB" << "BD" << "KAFEINE" << "KONQUEROR" << "KOPETE" << "KOOKA" << "OKULAR"; QStringListModel model(items); QListView view; view.setModel(&model); view.show(); QTest::qWait(30); // QCOMPARE(view.currentIndex() , model.index(0,0)); QTest::keyClick(&view, Qt::Key_K); QTest::qWait(10); QCOMPARE(view.currentIndex() , model.index(5,0)); //KAFEINE QTest::keyClick(&view, Qt::Key_O); QTest::qWait(10); QCOMPARE(view.currentIndex() , model.index(6,0)); //KONQUEROR QTest::keyClick(&view, Qt::Key_N); QTest::qWait(10); QCOMPARE(view.currentIndex() , model.index(6,0)); //KONQUEROR } QTEST_MAIN(tst_QListView) #include "tst_qlistview.moc"