diff options
Diffstat (limited to 'tests/auto/widgets/itemviews')
53 files changed, 3853 insertions, 1273 deletions
diff --git a/tests/auto/widgets/itemviews/CMakeLists.txt b/tests/auto/widgets/itemviews/CMakeLists.txt index ec6e00ec7f..60e05fadb4 100644 --- a/tests/auto/widgets/itemviews/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/CMakeLists.txt @@ -1,4 +1,5 @@ -# Generated from itemviews.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause add_subdirectory(qabstractitemview) add_subdirectory(qdatawidgetmapper) diff --git a/tests/auto/widgets/itemviews/itemviews.pro b/tests/auto/widgets/itemviews/itemviews.pro deleted file mode 100644 index b9477b7a7e..0000000000 --- a/tests/auto/widgets/itemviews/itemviews.pro +++ /dev/null @@ -1,21 +0,0 @@ -TEMPLATE=subdirs -SUBDIRS=\ - qabstractitemview \ - qcolumnview \ - qdatawidgetmapper \ - qfileiconprovider \ - qheaderview \ - qitemdelegate \ - qitemeditorfactory \ - qitemview \ - qlistview \ - qlistwidget \ - qtableview \ - qtablewidget \ - qtreeview \ - qtreewidget \ - qtreewidgetitemiterator \ - -!qtConfig(private_tests): SUBDIRS -= \ - qcolumnview \ - qlistwidget \ diff --git a/tests/auto/widgets/itemviews/qabstractitemview/BLACKLIST b/tests/auto/widgets/itemviews/qabstractitemview/BLACKLIST index 1233bec166..778a25b2e4 100644 --- a/tests/auto/widgets/itemviews/qabstractitemview/BLACKLIST +++ b/tests/auto/widgets/itemviews/qabstractitemview/BLACKLIST @@ -1,2 +1,4 @@ -[task200665_itemEntered] -macos # Can't move cursor (QTBUG-76312) +[focusNextOnHide] +wayland +[selectionAutoScrolling] +wayland diff --git a/tests/auto/widgets/itemviews/qabstractitemview/CMakeLists.txt b/tests/auto/widgets/itemviews/qabstractitemview/CMakeLists.txt index 0b292e06f8..ca39501a95 100644 --- a/tests/auto/widgets/itemviews/qabstractitemview/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qabstractitemview/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qabstractitemview.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qabstractitemview Test: ##################################################################### -qt_add_test(tst_qabstractitemview +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qabstractitemview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qabstractitemview SOURCES tst_qabstractitemview.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::GuiPrivate Qt::TestPrivate diff --git a/tests/auto/widgets/itemviews/qabstractitemview/qabstractitemview.pro b/tests/auto/widgets/itemviews/qabstractitemview/qabstractitemview.pro deleted file mode 100644 index b9091a134e..0000000000 --- a/tests/auto/widgets/itemviews/qabstractitemview/qabstractitemview.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qabstractitemview -QT += widgets testlib testlib-private gui-private widgets-private -SOURCES += tst_qabstractitemview.cpp diff --git a/tests/auto/widgets/itemviews/qabstractitemview/qtlogo.png b/tests/auto/widgets/itemviews/qabstractitemview/qtlogo.png Binary files differnew file mode 100644 index 0000000000..4f68e162de --- /dev/null +++ b/tests/auto/widgets/itemviews/qabstractitemview/qtlogo.png diff --git a/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp b/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp index 02be27ffc7..4f2495375d 100644 --- a/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp +++ b/tests/auto/widgets/itemviews/qabstractitemview/tst_qabstractitemview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <private/qguiapplication_p.h> @@ -35,11 +10,13 @@ #include <QHeaderView> #include <QIdentityProxyModel> #include <QItemDelegate> +#include <QLabel> #include <QLineEdit> #include <QListWidget> #include <QProxyStyle> #include <QPushButton> #include <QScrollBar> +#include <QScroller> #include <QSignalSpy> #include <QSortFilterProxyModel> #include <QSpinBox> @@ -47,11 +24,13 @@ #include <QStringListModel> #include <QStyledItemDelegate> #include <QTableWidget> +#include <QTimer> #include <QTreeWidget> #include <QTest> #include <QVBoxLayout> #include <QtTest/private/qtesthelpers_p.h> #include <private/qabstractitemview_p.h> +#include <QtWidgets/private/qapplication_p.h> Q_DECLARE_METATYPE(Qt::ItemFlags); @@ -100,6 +79,7 @@ private slots: void selectAll(); void ctrlA(); void persistentEditorFocus(); + void pressClosesReleaseDoesntOpenEditor(); void setItemDelegate(); void setItemDelegate_data(); // The dragAndDrop() test doesn't work, and is thus disabled on Mac and Windows @@ -129,6 +109,7 @@ private slots: void QTBUG6407_extendedSelection(); void QTBUG6753_selectOnSelection(); void testDelegateDestroyEditor(); + void testDelegateDestroyEditorChild(); void testClickedSignal(); void testChangeEditorState(); void deselectInSingleSelection(); @@ -154,6 +135,24 @@ private slots: void checkFocusAfterActivationChanges_data(); void checkFocusAfterActivationChanges(); void dragSelectAfterNewPress(); + void dragWithSecondClick_data(); + void dragWithSecondClick(); + void clickAfterDoubleClick(); + void selectionCommand_data(); + void selectionCommand(); + void mouseSelection_data(); + void mouseSelection(); + void keepSingleSelectionOnEmptyAreaClick(); + void scrollerSmoothScroll(); + void inputMethodOpensEditor_data(); + void inputMethodOpensEditor(); + void selectionAutoScrolling_data(); + void selectionAutoScrolling(); + void testSpinBoxAsEditor_data(); + void testSpinBoxAsEditor(); + void removeIndexWhileEditing(); + void focusNextOnHide(); + private: static QAbstractItemView *viewFromString(const QByteArray &viewType, QWidget *parent = nullptr) { @@ -179,30 +178,32 @@ public: QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const override { openedEditor = new QWidget(parent); + virtualCtorCallCount++; return openedEditor; } void destroyEditor(QWidget *editor, const QModelIndex &) const override { - calledVirtualDtor = true; + virtualDtorCallCount++; editor->deleteLater(); } void changeSize() { size = QSize(50, 50); emit sizeHintChanged(QModelIndex()); } mutable QWidget *openedEditor = nullptr; QSize size; - mutable bool calledVirtualDtor = false; + mutable int virtualCtorCallCount = 0; + mutable int virtualDtorCallCount = 0; }; class DialogItemDelegate : public QStyledItemDelegate { public: using QStyledItemDelegate::QStyledItemDelegate; - QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const override { openedEditor = new QDialog(parent); return openedEditor; } - void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override { Q_UNUSED(model); Q_UNUSED(index); @@ -432,12 +433,12 @@ void tst_QAbstractItemView::basic_tests(QAbstractItemView *view) QVERIFY(spy.isValid()); view->setIconSize(QSize(32, 32)); QCOMPARE(view->iconSize(), QSize(32, 32)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).value<QSize>(), QSize(32, 32)); // Should this happen? view->setIconSize(QSize(-1, -1)); QCOMPARE(view->iconSize(), QSize(-1, -1)); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); QCOMPARE(view->currentIndex(), QModelIndex()); QCOMPARE(view->rootIndex(), QModelIndex()); @@ -516,8 +517,8 @@ void tst_QAbstractItemView::basic_tests(QAbstractItemView *view) #if QT_CONFIG(draganddrop) if (!view->model()) view->startDrag(Qt::CopyAction); - - view->viewOptions(); + QStyleOptionViewItem option; + view->initViewItemOption(&option); view->setState(QAbstractItemView::NoState); QCOMPARE(view->state(), QAbstractItemView::NoState); @@ -671,9 +672,9 @@ void tst_QAbstractItemView::selectAll() GeometriesTestView view; view.setModel(&model); - QCOMPARE(view.selectedIndexes().count(), 0); + QCOMPARE(view.selectedIndexes().size(), 0); view.selectAll(); - QCOMPARE(view.selectedIndexes().count(), 4 * 4); + QCOMPARE(view.selectedIndexes().size(), 4 * 4); } void tst_QAbstractItemView::ctrlA() @@ -682,9 +683,9 @@ void tst_QAbstractItemView::ctrlA() GeometriesTestView view; view.setModel(&model); - QCOMPARE(view.selectedIndexes().count(), 0); + QCOMPARE(view.selectedIndexes().size(), 0); QTest::keyClick(&view, Qt::Key_A, Qt::ControlModifier); - QCOMPARE(view.selectedIndexes().count(), 4 * 4); + QCOMPARE(view.selectedIndexes().size(), 4 * 4); } void tst_QAbstractItemView::persistentEditorFocus() @@ -701,7 +702,7 @@ void tst_QAbstractItemView::persistentEditorFocus() //these are spinboxes because we put numbers inside const QList<QSpinBox*> list = view.viewport()->findChildren<QSpinBox*>(); - QCOMPARE(list.count(), 2); //these should be the 2 editors + QCOMPARE(list.size(), 2); //these should be the 2 editors view.setCurrentIndex(model.index(0, 0)); QCOMPARE(view.currentIndex(), model.index(0, 0)); @@ -713,8 +714,8 @@ void tst_QAbstractItemView::persistentEditorFocus() const QPoint p(5, 5); for (QSpinBox *sb : list) { QTRY_VERIFY(sb->isVisible()); - QMouseEvent mouseEvent(QEvent::MouseButtonPress, p, Qt::LeftButton, - Qt::LeftButton, Qt::NoModifier); + QMouseEvent mouseEvent(QEvent::MouseButtonPress, p, sb->mapToGlobal(p), + Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); QCoreApplication::sendEvent(sb, &mouseEvent); if (!QApplication::focusWidget()) QSKIP("Some window managers don't handle focus that well"); @@ -722,6 +723,47 @@ void tst_QAbstractItemView::persistentEditorFocus() } } +/*! + A press into the selection area of an item being edited, but outside the editor, + closes the editor by transferring focus to the view. The corresponding release + should then not re-open the editor. + + QTBUG-20456. +*/ +void tst_QAbstractItemView::pressClosesReleaseDoesntOpenEditor() +{ + QStandardItemModel model(0, 1); + auto *parent = new QStandardItem("parent"); + for (const auto &childText : {"child1", "child2"}) { + auto *child = new QStandardItem(childText); + child->setFlags(Qt::ItemFlag::ItemIsEnabled | Qt::ItemFlag::ItemIsEditable | Qt::ItemIsSelectable); + parent->appendRow(child); + } + model.appendRow(parent); + + QTreeView view; + view.setModel(&model); + view.setExpanded(model.indexFromItem(parent), true); + view.setSelectionMode(QAbstractItemView::SingleSelection); + view.setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::DoubleClicked); + + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + const QRect childRect = view.visualRect(model.indexFromItem(parent->child(0))); + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, childRect.center()); // select + QVERIFY(view.selectionModel()->selectedIndexes().contains(model.indexFromItem(parent->child(0)))); + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, childRect.center()); // edit + QTRY_COMPARE(view.state(), QAbstractItemView::EditingState); + QPoint inChildOutsideEditor = QPoint(view.indentation() / 2, childRect.center().y()); + QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, inChildOutsideEditor); // focus itemview, editor closes + QCOMPARE(view.state(), QAbstractItemView::NoState); + QTest::qWait(10); // process some events, let the internal timer time out + QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, inChildOutsideEditor); // should not reopen editor + QTest::qWait(QApplication::doubleClickInterval() * 2); + QCOMPARE(view.state(), QAbstractItemView::NoState); +} + #if !defined(Q_OS_MAC) && !defined(Q_OS_WIN) @@ -899,7 +941,7 @@ void tst_QAbstractItemView::dragAndDrop() if (successes < attempts) { QString msg = QString("# successes (%1) < # attempts (%2)").arg(successes).arg(attempts); - QWARN(msg.toLatin1()); + qWarning() << qPrintable(msg); } QVERIFY(successes > 0); // allow for some "event unstability" (i.e. unless // successes == 0, QAbstractItemView is probably ok!) @@ -1009,7 +1051,6 @@ void tst_QAbstractItemView::setItemDelegate() centerOnScreen(&v); moveCursorAway(&v); v.show(); - QApplication::setActiveWindow(&v); QVERIFY(QTest::qWaitForWindowActive(&v)); QModelIndex index = model.index(cellToEdit.y(), cellToEdit.x()); @@ -1110,8 +1151,8 @@ void tst_QAbstractItemView::checkIntersectedRect_data() { QStandardItemModel *model = new QStandardItemModel; for (int i = 0; i < rowCount; ++i) { - const QList<QStandardItem *> sil({new QStandardItem(QLatin1String("Row %1 Item").arg(i)), - new QStandardItem(QLatin1String("2nd column"))}); + const QList<QStandardItem *> sil({new QStandardItem(QString("Row %1 Item").arg(i)), + new QStandardItem(QString("2nd column"))}); model->appendRow(sil); } return model; @@ -1218,7 +1259,6 @@ void tst_QAbstractItemView::task221955_selectedEditor() tree.show(); tree.setFocus(); tree.setCurrentIndex(tree.model()->index(1,0)); - QApplication::setActiveWindow(&tree); QVERIFY(QTest::qWaitForWindowActive(&tree)); QVERIFY(! tree.selectionModel()->selectedIndexes().contains(tree.model()->index(3,0))); @@ -1284,7 +1324,13 @@ void tst_QAbstractItemView::task200665_itemEntered() { if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("Wayland: This fails. Figure out why."); - + { + // skip if we can't move mouse + const QPoint cursorPos = QCursor::pos() + QPoint(10, 10); + QCursor::setPos(cursorPos); + if (!QTest::qWaitFor([cursorPos] { return QCursor::pos() == cursorPos; }, 500)) + QSKIP("Can't move mouse"); + } //we test that view will emit entered //when the scrollbar move but not the mouse itself QStandardItemModel model(1000, 1); @@ -1299,7 +1345,7 @@ void tst_QAbstractItemView::task200665_itemEntered() QSignalSpy spy(&view, &QAbstractItemView::entered); view.verticalScrollBar()->setValue(view.verticalScrollBar()->maximum()); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); } void tst_QAbstractItemView::task257481_emptyEditor() @@ -1322,17 +1368,17 @@ void tst_QAbstractItemView::task257481_emptyEditor() treeView.edit(model.index(0, 0)); QList<QLineEdit *> lineEditors = treeView.viewport()->findChildren<QLineEdit *>(); - QCOMPARE(lineEditors.count(), 1); + QCOMPARE(lineEditors.size(), 1); QVERIFY(!lineEditors.constFirst()->size().isEmpty()); treeView.edit(model.index(1, 0)); lineEditors = treeView.viewport()->findChildren<QLineEdit *>(); - QCOMPARE(lineEditors.count(), 1); + QCOMPARE(lineEditors.size(), 1); QVERIFY(!lineEditors.constFirst()->size().isEmpty()); treeView.edit(model.index(2, 0)); lineEditors = treeView.viewport()->findChildren<QLineEdit *>(); - QCOMPARE(lineEditors.count(), 1); + QCOMPARE(lineEditors.size(), 1); QVERIFY(!lineEditors.constFirst()->size().isEmpty()); } @@ -1366,7 +1412,7 @@ void tst_QAbstractItemView::shiftArrowSelectionAfterScrolling() QCOMPARE(view.currentIndex(), index1); QModelIndexList selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 2); + QCOMPARE(selected.size(), 2); QVERIFY(selected.contains(index0)); QVERIFY(selected.contains(index1)); } @@ -1406,7 +1452,8 @@ void tst_QAbstractItemView::shiftSelectionAfterRubberbandSelection() // The mouse move event has to be created manually because the QTest framework does not // contain a function for mouse moves with buttons pressed QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, pressPos); - QMouseEvent moveEvent(QEvent::MouseMove, releasePos, Qt::NoButton, Qt::LeftButton, Qt::NoModifier); + QMouseEvent moveEvent(QEvent::MouseMove, releasePos, view.viewport()->mapToGlobal(releasePos), + Qt::NoButton, Qt::LeftButton, Qt::NoModifier); bool moveEventReceived = qApp->notify(view.viewport(), &moveEvent); QVERIFY(moveEventReceived); QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, releasePos); @@ -1419,7 +1466,7 @@ void tst_QAbstractItemView::shiftSelectionAfterRubberbandSelection() // Verify that the selection worked OK QModelIndexList selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 2); + QCOMPARE(selected.size(), 2); QVERIFY(selected.contains(index1)); QVERIFY(selected.contains(index2)); @@ -1429,7 +1476,8 @@ void tst_QAbstractItemView::shiftSelectionAfterRubberbandSelection() // Repeat the same steps as above, but with a Shift-Arrow selection QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, pressPos); - QMouseEvent moveEvent2(QEvent::MouseMove, releasePos, Qt::NoButton, Qt::LeftButton, Qt::NoModifier); + QMouseEvent moveEvent2(QEvent::MouseMove, releasePos, view.viewport()->mapToGlobal(releasePos), + Qt::NoButton, Qt::LeftButton, Qt::NoModifier); moveEventReceived = qApp->notify(view.viewport(), &moveEvent2); QVERIFY(moveEventReceived); QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, releasePos); @@ -1441,7 +1489,7 @@ void tst_QAbstractItemView::shiftSelectionAfterRubberbandSelection() // Verify that the selection worked OK selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 2); + QCOMPARE(selected.size(), 2); QVERIFY(selected.contains(index1)); QVERIFY(selected.contains(index2)); } @@ -1470,7 +1518,7 @@ void tst_QAbstractItemView::ctrlRubberbandSelection() // Select item 1 view.setCurrentIndex(index1); QModelIndexList selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 1); + QCOMPARE(selected.size(), 1); QVERIFY(selected.contains(index1)); // Now press control and draw a rubberband around items 1 and 2. @@ -1479,14 +1527,15 @@ void tst_QAbstractItemView::ctrlRubberbandSelection() QPoint pressPos = view.visualRect(index1).topLeft() - QPoint(1, 1); QPoint releasePos = view.visualRect(index2).bottomRight() + QPoint(1, 1); QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::ControlModifier, pressPos); - QMouseEvent moveEvent(QEvent::MouseMove, releasePos, Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); + QMouseEvent moveEvent(QEvent::MouseMove, releasePos, view.viewport()->mapToGlobal(releasePos), + Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); bool moveEventReceived = qApp->notify(view.viewport(), &moveEvent); QVERIFY(moveEventReceived); QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::ControlModifier, releasePos); // Verify that item 2 is selected now selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 1); + QCOMPARE(selected.size(), 1); QVERIFY(selected.contains(index2)); } @@ -1508,7 +1557,6 @@ void tst_QAbstractItemView::QTBUG6407_extendedSelection() moveCursorAway(&view); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(&view, QApplication::activeWindow()); @@ -1519,21 +1567,21 @@ void tst_QAbstractItemView::QTBUG6407_extendedSelection() QVERIFY(view.viewport()->rect().contains(p)); QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, p); QCOMPARE(view.currentIndex(), index49); - QCOMPARE(view.selectedItems().count(), 1); + QCOMPARE(view.selectedItems().size(), 1); QModelIndex index47 = view.model()->index(47,0); p = view.visualRect(index47).center(); QVERIFY(view.viewport()->rect().contains(p)); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, p); QCOMPARE(view.currentIndex(), index47); - QCOMPARE(view.selectedItems().count(), 3); //49, 48, 47; + QCOMPARE(view.selectedItems().size(), 3); //49, 48, 47; QModelIndex index44 = view.model()->index(44,0); p = view.visualRect(index44).center(); QVERIFY(view.viewport()->rect().contains(p)); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, p); QCOMPARE(view.currentIndex(), index44); - QCOMPARE(view.selectedItems().count(), 6); //49 .. 44; + QCOMPARE(view.selectedItems().size(), 6); //49 .. 44; } @@ -1558,7 +1606,7 @@ void tst_QAbstractItemView::QTBUG6753_selectOnSelection() QTest::mouseMove(table.viewport(), itemRect.center()); QTest::mouseClick(table.viewport(), Qt::LeftButton, Qt::NoModifier, itemRect.center()); - QCOMPARE(table.selectedItems().count(), 1); + QCOMPARE(table.selectedItems().size(), 1); QCOMPARE(table.selectedItems().first(), table.item(item.row(), item.column())); } @@ -1569,9 +1617,31 @@ void tst_QAbstractItemView::testDelegateDestroyEditor() table.setItemDelegate(&delegate); table.edit(table.model()->index(1, 1)); QAbstractItemView *tv = &table; - QVERIFY(!delegate.calledVirtualDtor); + QCOMPARE(delegate.virtualDtorCallCount, 0); tv->closeEditor(delegate.openedEditor, QAbstractItemDelegate::NoHint); - QVERIFY(delegate.calledVirtualDtor); + QCOMPARE(delegate.virtualDtorCallCount, 1); +} + +void tst_QAbstractItemView::testDelegateDestroyEditorChild() +{ + QTreeWidget tree; + MyAbstractItemDelegate delegate; + tree.setItemDelegate(&delegate); + QTreeWidgetItem *topLevel = new QTreeWidgetItem; + QTreeWidgetItem *levelOne1 = new QTreeWidgetItem(topLevel); + QTreeWidgetItem *levelTwo1 = new QTreeWidgetItem(levelOne1); + QTreeWidgetItem *levelOne2 = new QTreeWidgetItem(topLevel); + QTreeWidgetItem *levelTwo2 = new QTreeWidgetItem(levelOne2); + tree.insertTopLevelItem(0, topLevel); + tree.openPersistentEditor(levelOne1); + tree.openPersistentEditor(levelTwo1); + tree.openPersistentEditor(levelOne2); + tree.openPersistentEditor(levelTwo2); + QCOMPARE(delegate.virtualCtorCallCount, 4); + levelOne1->removeChild(levelTwo1); + QCOMPARE(delegate.virtualDtorCallCount, 1); + topLevel->removeChild(levelOne2); + QCOMPARE(delegate.virtualDtorCallCount, 3); } void tst_QAbstractItemView::testClickedSignal() @@ -1584,7 +1654,6 @@ void tst_QAbstractItemView::testClickedSignal() centerOnScreen(&view); moveCursorAway(&view); view.showNormal(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(&view, QApplication::activeWindow()); @@ -1595,12 +1664,12 @@ void tst_QAbstractItemView::testClickedSignal() QSignalSpy clickedSpy(&view, &QTableWidget::clicked); QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, p); - QCOMPARE(clickedSpy.count(), 1); + QCOMPARE(clickedSpy.size(), 1); QTest::mouseClick(view.viewport(), Qt::RightButton, {}, p); // We expect that right-clicks do not cause the clicked() signal to // be emitted. - QCOMPARE(clickedSpy.count(), 1); + QCOMPARE(clickedSpy.size(), 1); } @@ -1635,7 +1704,6 @@ void tst_QAbstractItemView::testChangeEditorState() centerOnScreen(&view); moveCursorAway(&view); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(&view, QApplication::activeWindow()); @@ -1656,7 +1724,6 @@ void tst_QAbstractItemView::deselectInSingleSelection() QVERIFY(QTest::qWaitForWindowExposed(&view)); view.setSelectionMode(QAbstractItemView::SingleSelection); view.setEditTriggers(QAbstractItemView::NoEditTriggers); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowExposed(&view)); // mouse QModelIndex index22 = s.index(2, 2); @@ -1664,28 +1731,28 @@ void tst_QAbstractItemView::deselectInSingleSelection() QPoint clickpos = rect22.center(); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, clickpos); QCOMPARE(view.currentIndex(), index22); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 1); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, clickpos); QCOMPARE(view.currentIndex(), index22); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 0); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 0); // second click with modifier however does select QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, clickpos); QCOMPARE(view.currentIndex(), index22); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 1); // keyboard QTest::keyClick(&view, Qt::Key_Space, Qt::NoModifier); QCOMPARE(view.currentIndex(), index22); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 1); QTest::keyClick(&view, Qt::Key_Space, Qt::ControlModifier); QCOMPARE(view.currentIndex(), index22); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 0); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 0); // second keypress with modifier however does select QTest::keyClick(&view, Qt::Key_Space, Qt::ControlModifier); QCOMPARE(view.currentIndex(), index22); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 1); } void tst_QAbstractItemView::testNoActivateOnDisabledItem() @@ -1703,7 +1770,6 @@ void tst_QAbstractItemView::testNoActivateOnDisabledItem() moveCursorAway(&treeView); treeView.show(); - QApplication::setActiveWindow(&treeView); QVERIFY(QTest::qWaitForWindowActive(&treeView)); QSignalSpy activatedSpy(&treeView, &QAbstractItemView::activated); @@ -1713,7 +1779,7 @@ void tst_QAbstractItemView::testNoActivateOnDisabledItem() QPoint clickPos = treeView.visualRect(itemIndex).center(); QTest::mouseClick(treeView.viewport(), Qt::LeftButton, {}, clickPos); - QCOMPARE(activatedSpy.count(), 0); + QCOMPARE(activatedSpy.size(), 0); } void tst_QAbstractItemView::testFocusPolicy_data() @@ -1750,7 +1816,6 @@ void tst_QAbstractItemView::testFocusPolicy() moveCursorAway(&window); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); // itemview accepts focus => editor is closed => return focus to the itemview @@ -1788,7 +1853,6 @@ void tst_QAbstractItemView::QTBUG31411_noSelection() moveCursorAway(&window); window.show(); - QApplication::setActiveWindow(&window); QVERIFY(QTest::qWaitForWindowActive(&window)); qRegisterMetaType<QItemSelection>(); @@ -1807,7 +1871,7 @@ void tst_QAbstractItemView::QTBUG31411_noSelection() QVERIFY(editor2); QTest::keyClick(editor2, Qt::Key_Escape, Qt::NoModifier); - QCOMPARE(selectionChangeSpy.count(), 0); + QCOMPARE(selectionChangeSpy.size(), 0); } void tst_QAbstractItemView::QTBUG39324_settingSameInstanceOfIndexWidget() @@ -1849,7 +1913,7 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() // Click "C" QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(indexC).center()); QModelIndexList selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 1); + QCOMPARE(selected.size(), 1); QVERIFY(selected.contains(indexC)); // Insert new item "B1" @@ -1859,14 +1923,14 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() // Shift-click "D" -> we expect that "C" and "D" are selected QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, view.visualRect(indexD).center()); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 2); + QCOMPARE(selected.size(), 2); QVERIFY(selected.contains(indexC)); QVERIFY(selected.contains(indexD)); // Click "D" QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(indexD).center()); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 1); + QCOMPARE(selected.size(), 1); QVERIFY(selected.contains(indexD)); // Remove items "B" and "C" @@ -1878,7 +1942,7 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() // Shift-click "F" -> we expect that "D", "E", and "F" are selected QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, view.visualRect(indexF).center()); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 3); + QCOMPARE(selected.size(), 3); QVERIFY(selected.contains(indexD)); QVERIFY(selected.contains(indexE)); QVERIFY(selected.contains(indexF)); @@ -1887,7 +1951,7 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() while (view.currentIndex() != indexA) QTest::keyClick(&view, Qt::Key_Up); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 1); + QCOMPARE(selected.size(), 1); QVERIFY(selected.contains(indexA)); // Change the sort order @@ -1896,7 +1960,7 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() // Shift-click "F" -> All items should be selected QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, view.visualRect(indexF).center()); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), model.rowCount()); + QCOMPARE(selected.size(), model.rowCount()); // Restore the old sort order proxyModel.sort(0, Qt::AscendingOrder); @@ -1904,7 +1968,7 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() // Click "D" QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(indexD).center()); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 1); + QCOMPARE(selected.size(), 1); QVERIFY(selected.contains(indexD)); // Insert new item "B2" @@ -1914,7 +1978,7 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() // Press Shift+Down -> "D" and "E" should be selected. QTest::keyClick(&view, Qt::Key_Down, Qt::ShiftModifier); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 2); + QCOMPARE(selected.size(), 2); QVERIFY(selected.contains(indexD)); QVERIFY(selected.contains(indexE)); @@ -1923,7 +1987,7 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, view.visualRect(indexA).center()); view.setCurrentIndex(indexD); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 1); + QCOMPARE(selected.size(), 1); QVERIFY(selected.contains(indexD)); // Insert new item "B3" @@ -1933,7 +1997,7 @@ void tst_QAbstractItemView::shiftSelectionAfterChangingModelContents() // Press Shift+Down -> "D" and "E" should be selected. QTest::keyClick(&view, Qt::Key_Down, Qt::ShiftModifier); selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 2); + QCOMPARE(selected.size(), 2); QVERIFY(selected.contains(indexD)); QVERIFY(selected.contains(indexE)); } @@ -2211,14 +2275,14 @@ void tst_QAbstractItemView::testClickToSelect() // Click the center of the visualRect of item "A" QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, centerA); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.back().front().value<QRect>(), QRect(centerA, QSize(1, 1))); // Click a point slightly away from the center const QPoint nearCenterA = centerA + QPoint(1, 1); QVERIFY(visualRectA.contains(nearCenterA)); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, nearCenterA); - QCOMPARE(spy.count(), 2); + QCOMPARE(spy.size(), 2); QCOMPARE(spy.back().front().value<QRect>(), QRect(nearCenterA, QSize(1, 1))); } @@ -2388,15 +2452,11 @@ void tst_QAbstractItemView::inputMethodEnabled() // Check focus by switching the activation of the window to force a focus in view->setCurrentIndex(model->index(1, 0)); - QApplication::setActiveWindow(nullptr); - QApplication::setActiveWindow(view.data()); QVERIFY(QTest::qWaitForWindowActive(view.data())); QCOMPARE(view->testAttribute(Qt::WA_InputMethodEnabled), result); view->setCurrentIndex(QModelIndex()); QVERIFY(!view->testAttribute(Qt::WA_InputMethodEnabled)); - QApplication::setActiveWindow(nullptr); - QApplication::setActiveWindow(view.data()); QVERIFY(QTest::qWaitForWindowActive(view.data())); QModelIndex index = model->index(1, 0); QPoint p = view->visualRect(index).center(); @@ -2406,8 +2466,6 @@ void tst_QAbstractItemView::inputMethodEnabled() QCOMPARE(view->testAttribute(Qt::WA_InputMethodEnabled), result); index = model->index(0, 0); - QApplication::setActiveWindow(nullptr); - QApplication::setActiveWindow(view.data()); QVERIFY(QTest::qWaitForWindowActive(view.data())); p = view->visualRect(index).center(); QTest::mouseClick(view->viewport(), Qt::LeftButton, Qt::NoModifier, p); @@ -2417,11 +2475,11 @@ void tst_QAbstractItemView::inputMethodEnabled() // There is a case when it goes to the first visible item so we // make the flags of the first item match the ones we are testing // to check the attribute correctly - QApplication::setActiveWindow(nullptr); + QApplicationPrivate::setActiveWindow(nullptr); view->setCurrentIndex(QModelIndex()); view->reset(); item->setFlags(Qt::ItemFlags(itemFlags)); - QApplication::setActiveWindow(view.data()); + QApplicationPrivate::setActiveWindow(view.data()); QVERIFY(QTest::qWaitForWindowActive(view.data())); QCOMPARE(view->testAttribute(Qt::WA_InputMethodEnabled), result); } @@ -2532,11 +2590,11 @@ void tst_QAbstractItemView::checkFocusAfterActivationChanges() QVERIFY(QTest::qWaitForWindowExposed(delegate.openedEditor)); QVERIFY(delegate.openedEditor->hasFocus()); - QApplication::setActiveWindow(&otherTopLevel); + QApplicationPrivate::setActiveWindow(&otherTopLevel); otherTopLevel.setFocus(); QTRY_VERIFY(!delegate.openedEditor); - QApplication::setActiveWindow(&w); + QApplicationPrivate::setActiveWindow(&w); QVERIFY(QTest::qWaitForWindowActive(&w)); QVERIFY(view->hasFocus()); } @@ -2576,7 +2634,7 @@ void tst_QAbstractItemView::dragSelectAfterNewPress() // Verify that the selection worked OK QModelIndexList selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 3); + QCOMPARE(selected.size(), 3); for (int i = 0; i < 2; ++i) QVERIFY(selected.contains(model.index(i, 0))); @@ -2584,8 +2642,8 @@ void tst_QAbstractItemView::dragSelectAfterNewPress() const QPoint releasePos = view.visualRect(index5).center(); // The mouse move event has to be created manually because the QTest framework does not // contain a function for mouse moves with buttons pressed - QMouseEvent moveEvent2(QEvent::MouseMove, releasePos, Qt::NoButton, Qt::LeftButton, - Qt::ShiftModifier); + QMouseEvent moveEvent2(QEvent::MouseMove, releasePos, view.viewport()->mapToGlobal(releasePos), + Qt::NoButton, Qt::LeftButton, Qt::ShiftModifier); const bool moveEventReceived = qApp->notify(view.viewport(), &moveEvent2); QVERIFY(moveEventReceived); QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::ShiftModifier, releasePos); @@ -2593,10 +2651,915 @@ void tst_QAbstractItemView::dragSelectAfterNewPress() // Verify that the selection worked OK selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 6); + QCOMPARE(selected.size(), 6); for (int i = 0; i < 5; ++i) QVERIFY(selected.contains(model.index(i, 0))); } +void tst_QAbstractItemView::dragWithSecondClick_data() +{ + QTest::addColumn<QByteArray>("viewClass"); + QTest::addColumn<bool>("doubleClick"); + const QList<QByteArray> widgets { "QListView", "QTreeView" }; + for (const QByteArray &widget : widgets) { + QTest::newRow(widget + ": DoubleClick") << widget << true; + QTest::newRow(widget + ": Two Single Clicks") << widget << false; + } +} + +// inject the ability to record which indexes get dragged into any QAbstractItemView class +struct DragRecorder +{ + virtual ~DragRecorder() = default; + bool dragStarted = false; + QModelIndexList draggedIndexes; + QAbstractItemView *view; +}; + +template<class ViewClass> +class DragRecorderView : public ViewClass, public DragRecorder +{ +public: + DragRecorderView() + { view = this; } +protected: + void startDrag(Qt::DropActions) override + { + draggedIndexes = ViewClass::selectedIndexes(); + dragStarted = true; + } +}; + +void tst_QAbstractItemView::dragWithSecondClick() +{ + QFETCH(QByteArray, viewClass); + QFETCH(bool, doubleClick); + + QStandardItemModel model; + QStandardItem *parentItem = model.invisibleRootItem(); + for (int i = 0; i < 10; ++i) { + QStandardItem *item = new QStandardItem(QString("item %0").arg(i)); + item->setDragEnabled(true); + item->setEditable(false); + parentItem->appendRow(item); + } + + std::unique_ptr<DragRecorder> dragRecorder; + if (viewClass == "QTreeView") + dragRecorder.reset(new DragRecorderView<QTreeView>); + else if (viewClass == "QListView") + dragRecorder.reset(new DragRecorderView<QListView>); + + QAbstractItemView *view = dragRecorder->view; + view->setModel(&model); + view->setFixedSize(160, 650); // Minimum width for windows with frame on Windows 8 + view->setSelectionMode(QAbstractItemView::MultiSelection); + view->setDragDropMode(QAbstractItemView::InternalMove); + centerOnScreen(view); + moveCursorAway(view); + view->show(); + QVERIFY(QTest::qWaitForWindowExposed(view)); + + QModelIndex index0 = model.index(0, 0); + QModelIndex index1 = model.index(1, 0); + // Select item 0 using a single click + QTest::mouseClick(view->viewport(), Qt::LeftButton, Qt::NoModifier, + view->visualRect(index0).center()); + QCOMPARE(view->currentIndex(), index0); + + if (doubleClick) { + // press on same item within the double click interval + QTest::mouseDClick(view->viewport(), Qt::LeftButton, Qt::NoModifier, + view->visualRect(index0).center()); + } else { + // or on different item with a slow second press + QTest::mousePress(view->viewport(), Qt::LeftButton, Qt::NoModifier, + view->visualRect(index1).center()); + } + // then drag far enough with left button held + const QPoint dragTo = view->visualRect(index1).center() + + QPoint(2 * QApplication::startDragDistance(), + 2 * QApplication::startDragDistance()); + QMouseEvent mouseMoveEvent(QEvent::MouseMove, dragTo, view->viewport()->mapToGlobal(dragTo), + Qt::NoButton, Qt::LeftButton, Qt::NoModifier); + QVERIFY(QApplication::sendEvent(view->viewport(), &mouseMoveEvent)); + // twice since the view will first enter dragging state, then start the drag + // (not necessary to actually move the mouse) + QVERIFY(QApplication::sendEvent(view->viewport(), &mouseMoveEvent)); + QVERIFY(dragRecorder->dragStarted); + QTest::mouseRelease(view->viewport(), Qt::LeftButton, Qt::NoModifier, dragTo); +} + +void tst_QAbstractItemView::clickAfterDoubleClick() +{ + QTableWidget view(5, 5); + view.horizontalHeader()->hide(); + view.verticalHeader()->hide(); + view.setEditTriggers(QAbstractItemView::NoEditTriggers); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + const QModelIndex index = view.model()->index(1, 1); + QVERIFY(index.isValid()); + const QPoint clickPoint = view.visualRect(index).center(); + + // must use the QWindow overloads so that modality is respected + QWindow *window = view.window()->windowHandle(); + int clickCount = 0; + + connect(&view, &QAbstractItemView::doubleClicked, [&]{ + QDialog dialog(&view); + dialog.setModal(true); + QTimer::singleShot(0, [&]{ dialog.close(); }); + dialog.exec(); + }); + connect(&view, &QAbstractItemView::clicked, [&]{ + ++clickCount; + }); + + QTest::mouseClick(window, Qt::LeftButton, {}, clickPoint); + QCOMPARE(clickCount, 1); + // generates a click followed by a double click; double click opens + // dialog that eats second release + QTest::mouseDClick(window, Qt::LeftButton, {}, clickPoint); + QCOMPARE(clickCount, 2); + QTest::mouseClick(window, Qt::LeftButton, {}, clickPoint); + QCOMPARE(clickCount, 3); +} + +void tst_QAbstractItemView::selectionCommand_data() +{ + QTest::addColumn<QAbstractItemView::SelectionMode>("selectionMode"); + QTest::addColumn<Qt::KeyboardModifier>("keyboardModifier"); + QTest::addColumn<QItemSelectionModel::SelectionFlag>("selectionFlag"); + + QTest::newRow("NoSelection - NoModifier") << QAbstractItemView::NoSelection << Qt::NoModifier << QItemSelectionModel::NoUpdate; + QTest::newRow("NoSelection - ShiftModifier") << QAbstractItemView::NoSelection << Qt::ShiftModifier << QItemSelectionModel::NoUpdate; + QTest::newRow("NoSelection - ControlModifier") << QAbstractItemView::NoSelection << Qt::ControlModifier << QItemSelectionModel::NoUpdate; + QTest::newRow("NoSelection - AltModifier") << QAbstractItemView::NoSelection << Qt::AltModifier << QItemSelectionModel::NoUpdate; + + QTest::newRow("SingleSelection - NoModifier") << QAbstractItemView::SingleSelection << Qt::NoModifier << QItemSelectionModel::ClearAndSelect; + QTest::newRow("SingleSelection - ShiftModifier") << QAbstractItemView::SingleSelection << Qt::ShiftModifier << QItemSelectionModel::ClearAndSelect; + QTest::newRow("SingleSelection - ControlModifier") << QAbstractItemView::SingleSelection << Qt::ControlModifier << QItemSelectionModel::ClearAndSelect; + QTest::newRow("SingleSelection - AltModifier") << QAbstractItemView::SingleSelection << Qt::AltModifier << QItemSelectionModel::ClearAndSelect; + + QTest::newRow("MultiSelection - NoModifier") << QAbstractItemView::MultiSelection << Qt::NoModifier << QItemSelectionModel::Toggle; + QTest::newRow("MultiSelection - ShiftModifier") << QAbstractItemView::MultiSelection << Qt::ShiftModifier << QItemSelectionModel::Toggle; + QTest::newRow("MultiSelection - ControlModifier") << QAbstractItemView::MultiSelection << Qt::ControlModifier << QItemSelectionModel::Toggle; + QTest::newRow("MultiSelection - AltModifier") << QAbstractItemView::MultiSelection << Qt::AltModifier << QItemSelectionModel::Toggle; + + QTest::newRow("ExtendedSelection - NoModifier") << QAbstractItemView::ExtendedSelection << Qt::NoModifier << QItemSelectionModel::ClearAndSelect; + QTest::newRow("ExtendedSelection - ShiftModifier") << QAbstractItemView::ExtendedSelection << Qt::ShiftModifier << QItemSelectionModel::SelectCurrent; + QTest::newRow("ExtendedSelection - ControlModifier") << QAbstractItemView::ExtendedSelection << Qt::ControlModifier << QItemSelectionModel::Toggle; + QTest::newRow("ExtendedSelection - AltModifier") << QAbstractItemView::ExtendedSelection << Qt::AltModifier << QItemSelectionModel::ClearAndSelect; + + QTest::newRow("ContiguousSelection - NoModifier") << QAbstractItemView::ContiguousSelection << Qt::NoModifier << QItemSelectionModel::ClearAndSelect; + QTest::newRow("ContiguousSelection - ShiftModifier") << QAbstractItemView::ContiguousSelection << Qt::ShiftModifier << QItemSelectionModel::SelectCurrent; + QTest::newRow("ContiguousSelection - ControlModifier") << QAbstractItemView::ContiguousSelection << Qt::ControlModifier << QItemSelectionModel::SelectCurrent; + QTest::newRow("ContiguousSelection - AltModifier") << QAbstractItemView::ContiguousSelection << Qt::AltModifier << QItemSelectionModel::ClearAndSelect; +} + +void tst_QAbstractItemView::selectionCommand() +{ + QFETCH(QAbstractItemView::SelectionMode, selectionMode); + QFETCH(Qt::KeyboardModifier, keyboardModifier); + QFETCH(QItemSelectionModel::SelectionFlag, selectionFlag); + + QTableView view; + view.setSelectionMode(selectionMode); + QTest::keyPress(&view, Qt::Key_A, keyboardModifier); + QCOMPARE(selectionFlag, view.selectionCommand(QModelIndex(), nullptr)); +} + +struct SelectionEvent +{ + enum MouseEvent + { Press, Release, Click, Move }; + constexpr SelectionEvent(MouseEvent type, int r = -1) noexcept + : eventType(type), row(r) {} + constexpr SelectionEvent(MouseEvent type, Qt::KeyboardModifiers mod, int r = -1) noexcept + : eventType(type), keyboardModifiers(mod), row(r) {} + MouseEvent eventType = Press; + Qt::KeyboardModifiers keyboardModifiers = Qt::NoModifier; + int row = -1; +}; + +void tst_QAbstractItemView::mouseSelection_data() +{ + QTest::addColumn<QAbstractItemView::SelectionMode>("selectionMode"); + QTest::addColumn<bool>("dragEnabled"); + QTest::addColumn<QAbstractItemView::EditTrigger>("editTrigger"); + QTest::addColumn<QList<SelectionEvent>>("selectionEvents"); + QTest::addColumn<QList<int>>("selectedRows"); + + // single selection mode - always one row selected, modifiers ignored + QTest::addRow("Single:Press") << QAbstractItemView::SingleSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1)} + << QList{1}; + QTest::addRow("Single:Click") << QAbstractItemView::SingleSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent{SelectionEvent::Click, 1}} + << QList{1}; + QTest::addRow("Single:Press+Drag") << QAbstractItemView::SingleSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent{SelectionEvent::Move, 2}, + SelectionEvent{SelectionEvent::Release}} + << QList{2}; + QTest::addRow("Single:Shift+Click") << QAbstractItemView::SingleSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent{SelectionEvent::Click, Qt::ShiftModifier, 2}} + << QList{2}; + QTest::addRow("Single:Press;Ctrl+Press") << QAbstractItemView::SingleSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent{SelectionEvent::Press, 3}, + SelectionEvent{SelectionEvent::Press, Qt::ControlModifier, 3}} + << QList{3}; + QTest::addRow("Single:Ctrl+Click") << QAbstractItemView::SingleSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent{SelectionEvent::Click, Qt::ControlModifier, 3}} + << QList{3}; + QTest::addRow("Single:Click;Ctrl+Click") << QAbstractItemView::SingleSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent{SelectionEvent::Click, 3}, + SelectionEvent{SelectionEvent::Click, Qt::ControlModifier, 3}} + << QList<int>{}; + + // multi selection mode - selection toggles on press, selection can be drag-extended + // modifiers ignored + QTest::addRow("Multi:Press") << QAbstractItemView::MultiSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1)} + << QList{1}; + QTest::addRow("Multi:Press twice") << QAbstractItemView::MultiSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent(SelectionEvent::Press, 1)} + << QList<int>{}; + QTest::addRow("Multi:Press twice with drag enabled") << QAbstractItemView::MultiSelection << true + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Click, 1), + SelectionEvent(SelectionEvent::Press, 1)} + << QList{1}; + QTest::addRow("Multi:Press and click with drag enabled") << QAbstractItemView::MultiSelection << true + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent(SelectionEvent::Click, 1)} + << QList<int>{}; + QTest::addRow("Multi:Press,Press") << QAbstractItemView::MultiSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 2), + SelectionEvent(SelectionEvent::Press, 3)} + << QList{2, 3}; + QTest::addRow("Multi:Press,Drag") << QAbstractItemView::MultiSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent(SelectionEvent::Move, 5), + SelectionEvent(SelectionEvent::Release)} + << QList{1, 2, 3, 4, 5}; + QTest::addRow("Multi:Press,Drag,Deselect") << QAbstractItemView::MultiSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent(SelectionEvent::Move, 5), + SelectionEvent(SelectionEvent::Release), + SelectionEvent(SelectionEvent::Press, 3)} + << QList{1, 2, 4, 5}; + // drag-select a few indices; then drag-select a larger area that includes the first + QTest::addRow("Multi:Press,Drag;Surround") << QAbstractItemView::MultiSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 3), + SelectionEvent(SelectionEvent::Move, 5), + SelectionEvent(SelectionEvent::Release), + SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent(SelectionEvent::Move, 8), + SelectionEvent(SelectionEvent::Release)} + << QList{1, 2, 3, 4, 5, 6, 7, 8}; + // drag-select a few indices; then try to select more starting with the last -> not working + QTest::addRow("Multi:Press,Drag;Expand") << QAbstractItemView::MultiSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 3), + SelectionEvent(SelectionEvent::Move, 5), + SelectionEvent(SelectionEvent::Release), + SelectionEvent(SelectionEvent::Press, 5), // this will deselect #5 and not select 6/7/8 + SelectionEvent(SelectionEvent::Move, 8), + SelectionEvent(SelectionEvent::Release)} + << QList{3, 4}; + // Multi: Press-dragging a selection should not deselect #QTBUG-59888 + QTest::addRow("Multi:Press-Drag selection") << QAbstractItemView::MultiSelection << true + // with drag'n'drop enabled, we cannot drag a selection + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Click, 2), + SelectionEvent(SelectionEvent::Click, 3), + SelectionEvent(SelectionEvent::Click, 4), + SelectionEvent(SelectionEvent::Click, 5), + SelectionEvent(SelectionEvent::Press, 3), + // two moves needed because of distance and state logic in QAbstractItemView + SelectionEvent(SelectionEvent::Move, 5), + SelectionEvent(SelectionEvent::Move, 6)} + << QList{2, 3, 4, 5}; + + // Extended selection: Press selects a single item + QTest::addRow("Extended:Press") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 3)} + << QList{3}; + QTest::addRow("Extended:Press twice") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 3), + SelectionEvent(SelectionEvent::Press, 3)} + << QList{3}; + QTest::addRow("Extended:Press,Press") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 2), + SelectionEvent(SelectionEvent::Press, 3)} + << QList{3}; + // Extended selection: press with Ctrl toggles item + QTest::addRow("Extended:Press,Toggle") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 3), + SelectionEvent(SelectionEvent::Click, Qt::ControlModifier, 3)} + << QList<int>{}; + QTest::addRow("Extended:Press,Add") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent(SelectionEvent::Click, Qt::ControlModifier, 3)} + << QList{1, 3}; + // Extended selection: Shift creates a range between first and last pressed + QTest::addRow("Extended:Press,Range") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent(SelectionEvent::Press, Qt::ShiftModifier, 5)} + << QList{1, 2, 3, 4, 5}; + QTest::addRow("Extended:Press,Range,Fix Range") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 1), + SelectionEvent(SelectionEvent::Press, Qt::ShiftModifier, 5), + SelectionEvent(SelectionEvent::Press, Qt::ShiftModifier, 3)} + << QList{1, 2, 3}; + // Extended: dragging extends the selection + QTest::addRow("Extended:Press,Drag") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 2), + SelectionEvent(SelectionEvent::Move, 5)} + << QList{2, 3, 4, 5}; + // Extended: Ctrl+Press-dragging extends the selection + QTest::addRow("Extended:Press,Drag;Ctrl-Press,Drag") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Press, 2), + SelectionEvent(SelectionEvent::Move, 5), + SelectionEvent(SelectionEvent::Release), + SelectionEvent(SelectionEvent::Press, Qt::ControlModifier, 6), + SelectionEvent(SelectionEvent::Move, Qt::ControlModifier, 8), + SelectionEvent(SelectionEvent::Release, Qt::ControlModifier, 8)} + << QList{2, 3, 4, 5, 6, 7, 8}; + // Extended: Ctrl+Press-dragging in a selection should not deselect #QTBUG-59888 + QTest::addRow("Extended:Ctrl-Drag selection,no deselect") << QAbstractItemView::ExtendedSelection << true + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Click, 2), + SelectionEvent(SelectionEvent::Click, Qt::ShiftModifier, 5), + SelectionEvent(SelectionEvent::Press, Qt::ControlModifier, 3), + SelectionEvent(SelectionEvent::Move, Qt::ControlModifier, 5), + // two moves needed because of distance and state logic in QAbstractItemView + SelectionEvent(SelectionEvent::Move, Qt::ControlModifier, 6)} + << QList{2, 3, 4, 5}; + // Extended: Ctrl+Press-dragging with a selection extends, then drags #QTBUG-59888 + QTest::addRow("Extended:Ctrl-Drag selection") << QAbstractItemView::ExtendedSelection << true + << QAbstractItemView::NoEditTriggers + << QList{SelectionEvent(SelectionEvent::Click, 2), + SelectionEvent(SelectionEvent::Click, Qt::ShiftModifier, 5), + SelectionEvent(SelectionEvent::Press, Qt::ControlModifier, 6), + SelectionEvent(SelectionEvent::Move, Qt::ControlModifier, 7), + // two moves needed because of distance and state logic in 7QAbstractItemView + SelectionEvent(SelectionEvent::Move, Qt::ControlModifier, 8)} + << QList{2, 3, 4, 5, 6}; + // Extended: when drag is enabled, click with Ctrl toggles item instead of editing # QTBUG-111131 + QTest::addRow("Extended:Click,Toggle,editable") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::SelectedClicked + << QList{SelectionEvent(SelectionEvent::Click, 3), + SelectionEvent(SelectionEvent::Click, Qt::ControlModifier, 3)} + << QList<int>{}; + QTest::addRow("Extended:Click,Toggle,dragable,editable") << QAbstractItemView::ExtendedSelection << true + << QAbstractItemView::SelectedClicked + << QList{SelectionEvent(SelectionEvent::Click, 3), + SelectionEvent(SelectionEvent::Click, Qt::ControlModifier, 3)} + << QList<int>{}; + // Extended: when drag is enabled, click on selected without Ctrl clears before editing + QTest::addRow("Extended:Range,Click,editable") << QAbstractItemView::ExtendedSelection << false + << QAbstractItemView::SelectedClicked + << QList{SelectionEvent(SelectionEvent::Click, 1), + SelectionEvent(SelectionEvent::Click, Qt::ShiftModifier, 3), + SelectionEvent(SelectionEvent::Click, 2)} + << QList<int>{2}; + QTest::addRow("Extended:Range,Click,dragable,editable") << QAbstractItemView::ExtendedSelection << true + << QAbstractItemView::SelectedClicked + << QList{SelectionEvent(SelectionEvent::Click, 1), + SelectionEvent(SelectionEvent::Click, Qt::ShiftModifier, 3), + SelectionEvent(SelectionEvent::Click, 2)} + << QList<int>{2}; +} + +void tst_QAbstractItemView::mouseSelection() +{ + QFETCH(QAbstractItemView::SelectionMode, selectionMode); + QFETCH(bool, dragEnabled); + QFETCH(QAbstractItemView::EditTrigger, editTrigger); + QFETCH(QList<SelectionEvent>, selectionEvents); + QFETCH(QList<int>, selectedRows); + + QStandardItemModel model; + QStandardItem *parentItem = model.invisibleRootItem(); + for (int i = 0; i < 10; ++i) { + QStandardItem *item = new QStandardItem(QString("item %0").arg(i)); + item->setDragEnabled(dragEnabled); + item->setEditable(editTrigger != QAbstractItemView::NoEditTriggers); + parentItem->appendRow(item); + } + + std::unique_ptr<DragRecorder> dragRecorder(new DragRecorderView<QTreeView>); + QAbstractItemView *view = dragRecorder->view; + QVERIFY(view); + view->setModel(&model); + view->setDragEnabled(dragEnabled); + view->setSelectionMode(selectionMode); + view->setEditTriggers(editTrigger); + view->show(); + QVERIFY(QTest::qWaitForWindowActive(view)); + + Qt::MouseButton buttonDown = Qt::NoButton; + int targetRow = -1; + QModelIndex pressedIndex; + for (const auto &event : std::as_const(selectionEvents)) { + if (event.row != -1) + targetRow = event.row; + const QModelIndex targetIndex = model.index(targetRow, 0); + const QPoint targetPoint = view->visualRect(targetIndex).center(); + switch (event.eventType) { + case SelectionEvent::Press: + if (buttonDown != Qt::NoButton) { + QTest::mouseRelease(view->viewport(), buttonDown, event.keyboardModifiers, + view->visualRect(pressedIndex).center()); + } + buttonDown = Qt::LeftButton; + pressedIndex = model.index(targetRow, 0); + QTest::mousePress(view->viewport(), buttonDown, event.keyboardModifiers, targetPoint); + break; + case SelectionEvent::Release: + QTest::mouseRelease(view->viewport(), buttonDown, event.keyboardModifiers, targetPoint); + buttonDown = Qt::NoButton; + pressedIndex = QModelIndex(); + break; + case SelectionEvent::Click: + QTest::mouseClick(view->viewport(), Qt::LeftButton, event.keyboardModifiers, targetPoint); + buttonDown = Qt::NoButton; + break; + case SelectionEvent::Move: { + QMouseEvent mouseMoveEvent(QEvent::MouseMove, targetPoint, + view->viewport()->mapToGlobal(targetPoint), + Qt::NoButton, buttonDown, event.keyboardModifiers); + QApplication::sendEvent(view->viewport(), &mouseMoveEvent); + } + break; + } + } + + QList<int> actualSelected; + const auto selectedIndexes = dragRecorder->dragStarted + ? dragRecorder->draggedIndexes + : view->selectionModel()->selectedIndexes(); + for (auto index : selectedIndexes) + actualSelected << index.row(); + + QCOMPARE(actualSelected, selectedRows); +} + +/*! + Make sure that when clicking on empty space in the view, we don't + unselect the current row. + QTBUG-105870 +*/ +void tst_QAbstractItemView::keepSingleSelectionOnEmptyAreaClick() +{ + QListWidget view; + view.setSelectionMode(QAbstractItemView::SingleSelection); + QListWidgetItem *lastItem; + for (int i = 0; i < 5; i++) + lastItem = new QListWidgetItem("item " + QString::number(i), &view); + + // Make widget large enough so that there is empty area below the last item + view.setFixedSize(300, 500); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + // Select third row + view.setCurrentRow(2); + + // Click below the last row + QPoint targetPoint = view.visualItemRect(lastItem).bottomLeft(); + targetPoint += QPoint(10, 10); + + QTest::mouseClick(view.viewport(), Qt::MouseButton::LeftButton, Qt::NoModifier, targetPoint); + + QCOMPARE(view.currentRow(), 2); + QVERIFY(view.currentItem()->isSelected()); +} + +/*! + Verify that scrolling an autoScroll enabled itemview with a QScroller + produces a continuous, smooth scroll without any jumping around due to + the currentItem negotiation between QAbstractItemView and QScroller. + QTBUG-64543. +*/ +void tst_QAbstractItemView::scrollerSmoothScroll() +{ + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + + QListWidget view; + view.setAutoScroll(true); + view.setVerticalScrollMode(QListView::ScrollPerPixel); + + QScroller::grabGesture(view.viewport(), QScroller::TouchGesture); + QScroller::grabGesture(view.viewport(), QScroller::LeftMouseButtonGesture); + + for (int i = 0; i < 50; i++) { + QListWidgetItem* item = new QListWidgetItem("item " + QString::number(i), &view); + // gives items a touch friendly size so that only a few fit into the viewport + item->setSizeHint(QSize(100,50)); + } + + // make sure we have space for only a few items + view.setFixedSize(120, 200); + view.show(); + QVERIFY(QTest::qWaitForWindowActive(&view)); + + // we flick up, so we should never scroll back + int lastScrollPosition = 0; + bool scrollBack = false; + connect(view.verticalScrollBar(), &QScrollBar::valueChanged, [&](int value){ + scrollBack |= (value < lastScrollPosition); + lastScrollPosition = value; + }); + + // start in the middle + view.scrollToItem(view.item(25)); + QCOMPARE(view.currentItem(), view.item(0)); + QListWidgetItem *pressItem = view.item(23); + QPoint dragPosition = view.visualRect(view.indexFromItem(pressItem)).center(); + // the mouse press changes the current item temporarily, but the press is delayed + // by the gesture machinery. this is not what we are testing here, so skip the test + // if this fails within a reasonable amount of time. + QTest::mousePress(view.viewport(), Qt::LeftButton, Qt::NoModifier, dragPosition); + if (!(QTest::qWaitFor([&]{ return view.currentItem() == pressItem; }))) + QSKIP("Current item didn't change on press, skipping test"); + + // QAIV will reset the current item when the scroller changes state to Dragging + for (int y = 0; y < QApplication::startDragDistance() * 2; ++y) { + // gesture recognizer needs some throttling + QTest::qWait(10); + dragPosition -= QPoint(0, 10); + const QPoint globalPos = view.viewport()->mapToGlobal(dragPosition); + QMouseEvent mouseMoveEvent(QEvent::MouseMove, dragPosition, dragPosition, globalPos, + Qt::NoButton, Qt::LeftButton, Qt::NoModifier); + QApplication::sendEvent(view.viewport(), &mouseMoveEvent); + QVERIFY(!scrollBack); + } + + QTest::mouseRelease(view.viewport(), Qt::LeftButton, Qt::NoModifier, dragPosition); +} + +/*! + Verify that starting the editing of an item with a key press while a composing + input method is active doesn't break the input method. See QTBUG-54848. +*/ +void tst_QAbstractItemView::inputMethodOpensEditor_data() +{ + QTest::addColumn<QPoint>("editItem"); + QTest::addColumn<QString>("preedit"); + QTest::addColumn<QString>("commit"); + + QTest::addRow("IM accepted") << QPoint(1, 1) << "chang" << QString("长"); + QTest::addRow("IM cancelled") << QPoint(25, 25) << "chang" << QString(); +} + +void tst_QAbstractItemView::inputMethodOpensEditor() +{ + if (!QGuiApplicationPrivate::platformIntegration()->hasCapability(QPlatformIntegration::WindowActivation)) + QSKIP("QWindow::requestActivate() is not supported."); + + QTableWidget tableWidget(50, 50); + tableWidget.setEditTriggers(QAbstractItemView::AnyKeyPressed); + for (int r = 0; r < 50; ++r) { + for (int c = 0; c < 50; ++c ) + tableWidget.setItem(r, c, new QTableWidgetItem(QString("Item %1:%2").arg(r).arg(c))); + } + + tableWidget.show(); + QVERIFY(QTest::qWaitForWindowActive(&tableWidget)); + + const auto sendInputMethodEvent = [](const QString &preeditText, const QString &commitString = {}){ + QInputMethodEvent imEvent(preeditText, {}); + imEvent.setCommitString(commitString); + QApplication::sendEvent(QApplication::focusWidget(), &imEvent); + }; + + QCOMPARE(QApplication::focusWidget(), &tableWidget); + + QFETCH(QPoint, editItem); + QFETCH(QString, preedit); + QFETCH(QString, commit); + + tableWidget.setCurrentCell(editItem.y(), editItem.x()); + const QString orgText = tableWidget.currentItem()->text(); + const QModelIndex currentIndex = tableWidget.currentIndex(); + QCOMPARE(tableWidget.inputMethodQuery(Qt::ImCursorRectangle), tableWidget.visualRect(currentIndex)); + + // simulate the start of input via a composing input method + sendInputMethodEvent(preedit.left(1)); + QCOMPARE(tableWidget.state(), QAbstractItemView::EditingState); + QLineEdit *editor = tableWidget.findChild<QLineEdit*>(); + QVERIFY(editor); + QCOMPARE(editor->text(), QString()); + // the focus must remain with the tableWidget, as otherwise the compositing is interrupted + QCOMPARE(QApplication::focusWidget(), &tableWidget); + // the item view delegates input method queries to the editor + const QRect cursorRect = tableWidget.inputMethodQuery(Qt::ImCursorRectangle).toRect(); + QVERIFY(cursorRect.isValid()); + QVERIFY(tableWidget.visualRect(currentIndex).intersects(cursorRect)); + + // finish preediting, then commit or cancel the input + sendInputMethodEvent(preedit); + sendInputMethodEvent(QString(), commit); + // editing continues, the editor now has focus + QCOMPARE(tableWidget.state(), QAbstractItemView::EditingState); + QVERIFY(editor->hasFocus()); + // finish editing + QTest::keyClick(editor, Qt::Key_Return); + if (commit.isEmpty()) { + // if composition was cancelled, then the item's value is unchanged + QCOMPARE(tableWidget.currentItem()->text(), orgText); + } else { + // otherwise, the item's value is now the commit string + QTRY_COMPARE(tableWidget.currentItem()->text(), commit); + } +} + +void tst_QAbstractItemView::selectionAutoScrolling_data() +{ + QTest::addColumn<Qt::Orientation>("orientation"); + QTest::addColumn<int>("direction"); // negative or positive + + QTest::addRow("scroll up") << Qt::Vertical << -1; + QTest::addRow("scroll left") << Qt::Horizontal << -1; + QTest::addRow("scroll down") << Qt::Vertical << +1; + QTest::addRow("scroll right") << Qt::Horizontal << +1; +} + +void tst_QAbstractItemView::selectionAutoScrolling() +{ + QFETCH(Qt::Orientation, orientation); + QFETCH(int, direction); + + QListView listview; + listview.setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + listview.setResizeMode(QListView::Fixed); + listview.setAutoScroll(true); + listview.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + listview.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + listview.setSpacing(10); + listview.setGeometry(0, 0, 500, 500); + listview.setFrameShape(QFrame::Shape::NoFrame); + listview.setEditTriggers(QListView::NoEditTriggers); + + QStandardItemModel *listModel = new QStandardItemModel(&listview); + listview.setModel(listModel); + listview.setViewMode(QListView::IconMode); + listview.setSelectionMode(QListView::ExtendedSelection); + + QPixmap pm(50, 50); + pm.fill(Qt::red); + for (int i = 0; i < 80; i++) { + QStandardItem *item = new QStandardItem(pm, QString::number(i)); + listModel->appendRow(item); + } + + listview.show(); + QVERIFY(QTest::qWaitForWindowExposed(&listview)); + + listview.resize(200, 200); + // scroll to the middle + listview.verticalScrollBar()->setValue(listview.verticalScrollBar()->maximum() / 2); + listview.horizontalScrollBar()->setValue(listview.horizontalScrollBar()->maximum() / 2); + + // remove all visible items so that we don't select any items at the edges, as that + // would scroll the view already + for (int x = 0; x < listview.viewport()->width(); x += 5) { + for (int y = 0; y < listview.viewport()->height(); y += 5) { + const QModelIndex index = listview.indexAt(QPoint(x, y)); + if (index.isValid()) + delete listModel->itemFromIndex(index); + } + } + // remove all items around the edges of the model + QRect topLeftRect = listview.visualRect(listModel->index(0, 0)); + const QPoint topLeftCenter(topLeftRect.center()); + QPoint bottomRightCenter; + for (int x = 0; x < listview.horizontalScrollBar()->maximum() + listview.viewport()->width(); x += 5) { + const QModelIndex index = listview.indexAt(topLeftCenter + QPoint(x, 0)); + if (index.isValid()) { + delete listModel->itemFromIndex(index); + bottomRightCenter.rx() = x; + } + } + for (int y = 0; y < listview.verticalScrollBar()->maximum() + listview.viewport()->height(); y += 5) { + const QModelIndex index = listview.indexAt(topLeftCenter + QPoint(0, y)); + if (index.isValid()) { + delete listModel->itemFromIndex(index); + bottomRightCenter.ry() = y; + } + } + for (int x = 0; x < bottomRightCenter.x(); x += 5) { + const QModelIndex index = listview.indexAt(topLeftCenter + QPoint(x, bottomRightCenter.y())); + if (index.isValid()) + delete listModel->itemFromIndex(index); + } + for (int y = 0; y < bottomRightCenter.y(); y += 5) { + const QModelIndex index = listview.indexAt(topLeftCenter + QPoint(bottomRightCenter.x(), y)); + if (index.isValid()) + delete listModel->itemFromIndex(index); + } + + + // Simulate multiple select behavior; start in the middle, drag to the edge + const QPoint pressPoint(listview.viewport()->width() / 2, listview.viewport()->height() / 2); + QPoint dragPoint = pressPoint; + if (orientation == Qt::Vertical) { + dragPoint.rx() += 50; + dragPoint.ry() = direction > 0 ? listview.viewport()->height() : 0; + } else { + dragPoint.rx() = direction > 0 ? listview.viewport()->width() : 0; + dragPoint.ry() += 50; + } + + QTest::mousePress(listview.viewport(), Qt::LeftButton, Qt::NoModifier, pressPoint); + QMouseEvent mmEvent(QEvent::MouseMove, dragPoint, listview.viewport()->mapToGlobal(dragPoint), + Qt::NoButton, Qt::LeftButton, Qt::NoModifier); + QApplication::sendEvent(listview.viewport(), &mmEvent); // QTest::mouseMove is useless + + // check that we scrolled to the end + QScrollBar *scrollBar = orientation == Qt::Vertical + ? listview.verticalScrollBar() + : listview.horizontalScrollBar(); + + if (direction < 0) + QTRY_COMPARE(scrollBar->value(), 0); + else + QTRY_COMPARE(scrollBar->value(), scrollBar->maximum()); + QVERIFY(listview.selectionModel()->selectedIndexes().size() > 0); + + QTest::mouseRelease(listview.viewport(), Qt::LeftButton, Qt::NoModifier, dragPoint); +} +class SpinBoxDelegate : public QStyledItemDelegate +{ +public: + using QStyledItemDelegate::QStyledItemDelegate; + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &, const QModelIndex &) const override + { + QSpinBox *spinboxEditor = new QSpinBox(parent); + return spinboxEditor; + } + + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override + { + if (QSpinBox *spin = qobject_cast<QSpinBox *>(editor)) { + model->setData(index, spin->value()); + } + } +}; + +void tst_QAbstractItemView::testSpinBoxAsEditor_data() +{ + QTest::addColumn<bool>("keyboardTracking"); + QTest::newRow("true") << true; + QTest::newRow("false")<< false; +} + +void tst_QAbstractItemView::testSpinBoxAsEditor() +{ + QFETCH(bool, keyboardTracking); + + QStandardItemModel model(2, 2); + SpinBoxDelegate delegate; + + QTableView view; + view.setModel(&model); + view.setItemDelegate(&delegate); + + view.setCurrentIndex(model.index(0, 1)); + view.openPersistentEditor(model.index(0, 1)); + const QList<QSpinBox *> list = view.viewport()->findChildren<QSpinBox *>(); + QCOMPARE(list.size(), 1); + QSpinBox *sb = list.first(); + QVERIFY(sb); + + sb->setKeyboardTracking(keyboardTracking); + + centerOnScreen(&view); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QTRY_COMPARE(QApplication::focusWidget(), sb); + + QTest::keyClick(sb, Qt::Key_1, Qt::NoModifier); + QPoint clickpos = view.visualRect(model.index(0, 0)).center(); + QTest::mouseDClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, clickpos); + + QCOMPARE(model.data(model.index(0, 1)).toInt(), 1); +} + +void tst_QAbstractItemView::removeIndexWhileEditing() +{ + QTreeView view; + QStandardItemModel treeModel; + auto editableItem1 = new QStandardItem("aa"); + auto editableItem2 = new QStandardItem("ab"); + auto editableItem3 = new QStandardItem("ac"); + auto item = new QStandardItem("a"); + item->appendRow(editableItem1); + item->appendRow(editableItem2); + item->appendRow(editableItem3); + treeModel.setItem(0, 0, item); + QSortFilterProxyModel filterModel; + filterModel.setSourceModel(&treeModel); + view.setModel(&filterModel); + view.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + view.setExpanded(item->index(), true); + + filterModel.setFilterRegularExpression("a.*"); + + QTest::failOnWarning(QRegularExpression("QAbstractItemView::closeEditor called with an editor " + "that does not belong to this view")); + + // Verify that we shut editing down cleanly if the index we are editing is + // filtered out after committing + { + const QModelIndex filteredIndex = filterModel.mapFromSource(editableItem1->index()); + QVERIFY(filteredIndex.isValid()); + view.edit(filteredIndex); + QCOMPARE(view.state(), QAbstractItemView::EditingState); + QTRY_VERIFY(QApplication::focusWidget()); + QPointer<QLineEdit> lineEdit = qobject_cast<QLineEdit *>(QApplication::focusWidget()); + QVERIFY(lineEdit); + lineEdit->setText("c"); + QTest::keyClick(lineEdit, Qt::Key_Enter); + QTRY_VERIFY(!lineEdit); + QCOMPARE(editableItem1->data(Qt::DisplayRole), "c"); + QCOMPARE(view.state(), QAbstractItemView::NoState); + } + + // If we change the filter while we edit, then we should clean up state as well + { + const QModelIndex filteredIndex = filterModel.mapFromSource(editableItem2->index()); + QVERIFY(filteredIndex.isValid()); + view.edit(filteredIndex); + QCOMPARE(view.state(), QAbstractItemView::EditingState); + QTRY_VERIFY(QApplication::focusWidget()); + QPointer<QLineEdit> lineEdit = qobject_cast<QLineEdit *>(QApplication::focusWidget()); + QVERIFY(lineEdit); + filterModel.setFilterFixedString("c"); + QVERIFY(!filterModel.mapFromSource(editableItem2->index()).isValid()); + QTRY_VERIFY(!lineEdit); + QCOMPARE(view.state(), QAbstractItemView::NoState); + } +} + +void tst_QAbstractItemView::focusNextOnHide() +{ + QWidget widget; + QTableWidget table(10, 10); + table.setTabKeyNavigation(true); + QLineEdit lineEdit; + + QHBoxLayout layout; + layout.addWidget(&table); + layout.addWidget(&lineEdit); + widget.setLayout(&layout); + + widget.setTabOrder({&table, &lineEdit}); + + widget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + + QVERIFY(table.hasFocus()); + QCOMPARE(table.currentIndex(), table.model()->index(0, 0)); + QTest::keyPress(&table, Qt::Key_Tab); + QCOMPARE(table.currentIndex(), table.model()->index(0, 1)); + + table.hide(); + QCOMPARE(table.currentIndex(), table.model()->index(0, 1)); + QVERIFY(lineEdit.hasFocus()); +} + QTEST_MAIN(tst_QAbstractItemView) #include "tst_qabstractitemview.moc" diff --git a/tests/auto/widgets/itemviews/qcolumnview/BLACKLIST b/tests/auto/widgets/itemviews/qcolumnview/BLACKLIST new file mode 100644 index 0000000000..e20ee75eaa --- /dev/null +++ b/tests/auto/widgets/itemviews/qcolumnview/BLACKLIST @@ -0,0 +1,3 @@ +# QTBUG-41341 +[scrollTo] +macos diff --git a/tests/auto/widgets/itemviews/qcolumnview/CMakeLists.txt b/tests/auto/widgets/itemviews/qcolumnview/CMakeLists.txt index 002912d5cf..8cf19c703e 100644 --- a/tests/auto/widgets/itemviews/qcolumnview/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qcolumnview/CMakeLists.txt @@ -1,14 +1,21 @@ -# Generated from qcolumnview.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qcolumnview Test: ##################################################################### -qt_add_test(tst_qcolumnview +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qcolumnview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qcolumnview SOURCES ../../../../shared/fakedirmodel.h tst_qcolumnview.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/itemviews/qcolumnview/qcolumnview.pro b/tests/auto/widgets/itemviews/qcolumnview/qcolumnview.pro deleted file mode 100644 index 0b3dcd9e80..0000000000 --- a/tests/auto/widgets/itemviews/qcolumnview/qcolumnview.pro +++ /dev/null @@ -1,7 +0,0 @@ -CONFIG += testcase -QT += widgets widgets-private -QT += gui-private core-private testlib testlib-private - -SOURCES += tst_qcolumnview.cpp -HEADERS += ../../../../shared/fakedirmodel.h -TARGET = tst_qcolumnview diff --git a/tests/auto/widgets/itemviews/qcolumnview/tst_qcolumnview.cpp b/tests/auto/widgets/itemviews/qcolumnview/tst_qcolumnview.cpp index 6dd39d5d2a..1ff9973e00 100644 --- a/tests/auto/widgets/itemviews/qcolumnview/tst_qcolumnview.cpp +++ b/tests/auto/widgets/itemviews/qcolumnview/tst_qcolumnview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QColumnView> #include <QScrollBar> @@ -522,11 +497,11 @@ void tst_QColumnView::selectAll() view.setModel(&m_fakeDirModel); view.selectAll(); - QVERIFY(view.selectionModel()->selectedIndexes().count() >= 0); + QVERIFY(view.selectionModel()->selectedIndexes().size() >= 0); view.setCurrentIndex(m_fakeDirHomeIndex); view.selectAll(); - QVERIFY(view.selectionModel()->selectedIndexes().count() > 0); + QVERIFY(view.selectionModel()->selectedIndexes().size() > 0); QModelIndex file; for (int i = 0; i < m_fakeDirModel.rowCount(m_fakeDirHomeIndex); ++i) { @@ -537,10 +512,10 @@ void tst_QColumnView::selectAll() } view.setCurrentIndex(file); view.selectAll(); - QVERIFY(view.selectionModel()->selectedIndexes().count() > 0); + QVERIFY(view.selectionModel()->selectedIndexes().size() > 0); view.setCurrentIndex(QModelIndex()); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 0); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 0); } void tst_QColumnView::clicked() @@ -561,13 +536,13 @@ void tst_QColumnView::clicked() QPoint localPoint = view.visualRect(m_fakeDirHomeIndex).center(); QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, localPoint); - QCOMPARE(clickedSpy.count(), 1); + QCOMPARE(clickedSpy.size(), 1); QCoreApplication::processEvents(); if (sizeof(qreal) != sizeof(double)) QSKIP("Skipped due to rounding errors"); - for (int i = 0; i < view.createdColumns.count(); ++i) { + for (int i = 0; i < view.createdColumns.size(); ++i) { QAbstractItemView *column = view.createdColumns.at(i); if (column && column->selectionModel() && (column->rootIndex() == m_fakeDirHomeIndex)) QVERIFY(column->selectionModel()->selectedIndexes().isEmpty()); @@ -585,7 +560,7 @@ void tst_QColumnView::selectedColumns() QTest::qWait(ANIMATION_DELAY); - for (int i = 0; i < view.createdColumns.count(); ++i) { + for (int i = 0; i < view.createdColumns.size(); ++i) { QAbstractItemView *column = view.createdColumns.at(i); if (!column) continue; @@ -616,7 +591,7 @@ void tst_QColumnView::setSelectionModel() view.setSelectionModel(selectionModel); bool found = false; - for (int i = 0; i < view.createdColumns.count(); ++i) { + for (int i = 0; i < view.createdColumns.size(); ++i) { if (view.createdColumns.at(i)->selectionModel() == selectionModel) { found = true; break; @@ -656,7 +631,7 @@ void tst_QColumnView::moveGrip_basic() view.setMinimumWidth(200); grip->moveGrip(-800); QCOMPARE(view.width(), 200); - QCOMPARE(spy.count(), 5); + QCOMPARE(spy.size(), 5); } void tst_QColumnView::moveGrip_data() @@ -684,7 +659,7 @@ void tst_QColumnView::moveGrip() topLevel.show(); QVERIFY(QTest::qWaitForWindowActive(&topLevel)); - int columnNum = view.createdColumns.count() - 2; + int columnNum = view.createdColumns.size() - 2; QVERIFY(columnNum >= 0); const QObjectList list = view.createdColumns[columnNum]->children(); QColumnViewGrip *grip = nullptr; @@ -712,7 +687,7 @@ void tst_QColumnView::doubleClick() QCOMPARE(view.width(), 200); QTest::mouseDClick(grip, Qt::LeftButton); QCOMPARE(view.width(), view.sizeHint().width()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } void tst_QColumnView::gripMoved() @@ -736,7 +711,7 @@ void tst_QColumnView::gripMoved() QCoreApplication::processEvents(); QTest::mouseRelease(grip, Qt::LeftButton); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); QCOMPARE(view.width(), oldWidth + 65); } @@ -801,7 +776,7 @@ void tst_QColumnView::setPreviewWidget() void tst_QColumnView::sizes() { QColumnView view; - QCOMPARE(view.columnWidths().count(), 0); + QCOMPARE(view.columnWidths().size(), 0); const QList<int> newSizes{ 10, 4, 50, 6 }; @@ -812,16 +787,16 @@ void tst_QColumnView::sizes() view.setModel(&m_fakeDirModel); view.setCurrentIndex(m_fakeDirHomeIndex); - QList<int> postSizes = view.columnWidths().mid(0, newSizes.count()); - QCOMPARE(postSizes, newSizes.mid(0, postSizes.count())); + QList<int> postSizes = view.columnWidths().mid(0, newSizes.size()); + QCOMPARE(postSizes, newSizes.mid(0, postSizes.size())); - QVERIFY(view.columnWidths().count() > 1); + QVERIFY(view.columnWidths().size() > 1); QList<int> smallerSizes{ 6 }; view.setColumnWidths(smallerSizes); QList<int> expectedSizes = newSizes; expectedSizes[0] = 6; - postSizes = view.columnWidths().mid(0, newSizes.count()); - QCOMPARE(postSizes, expectedSizes.mid(0, postSizes.count())); + postSizes = view.columnWidths().mid(0, newSizes.size()); + QCOMPARE(postSizes, expectedSizes.mid(0, postSizes.size())); } void tst_QColumnView::rowDelegate() @@ -831,7 +806,7 @@ void tst_QColumnView::rowDelegate() view.setItemDelegateForRow(3, d); view.setModel(&m_fakeDirModel); - for (int i = 0; i < view.createdColumns.count(); ++i) { + for (int i = 0; i < view.createdColumns.size(); ++i) { QAbstractItemView *column = view.createdColumns.at(i); QCOMPARE(column->itemDelegateForRow(3), d); } @@ -981,7 +956,7 @@ void tst_QColumnView::dynamicModelChanges() model.appendRow(item); QVERIFY(QTest::qWaitForWindowExposed(&view)); //let the time for painting to occur - QTRY_COMPARE(delegate.paintedIndexes.count(), 1); + QTRY_COMPARE(delegate.paintedIndexes.size(), 1); QCOMPARE(*delegate.paintedIndexes.begin(), model.index(0,0)); } diff --git a/tests/auto/widgets/itemviews/qdatawidgetmapper/CMakeLists.txt b/tests/auto/widgets/itemviews/qdatawidgetmapper/CMakeLists.txt index 6cf9cbca43..e4b2c52fb2 100644 --- a/tests/auto/widgets/itemviews/qdatawidgetmapper/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qdatawidgetmapper/CMakeLists.txt @@ -1,13 +1,21 @@ -# Generated from qdatawidgetmapper.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qdatawidgetmapper Test: ##################################################################### -qt_add_test(tst_qdatawidgetmapper +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qdatawidgetmapper LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qdatawidgetmapper SOURCES tst_qdatawidgetmapper.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets + Qt::WidgetsPrivate ) diff --git a/tests/auto/widgets/itemviews/qdatawidgetmapper/qdatawidgetmapper.pro b/tests/auto/widgets/itemviews/qdatawidgetmapper/qdatawidgetmapper.pro deleted file mode 100644 index 53b23b5ef2..0000000000 --- a/tests/auto/widgets/itemviews/qdatawidgetmapper/qdatawidgetmapper.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_qdatawidgetmapper -QT += widgets testlib -SOURCES += tst_qdatawidgetmapper.cpp - - diff --git a/tests/auto/widgets/itemviews/qdatawidgetmapper/tst_qdatawidgetmapper.cpp b/tests/auto/widgets/itemviews/qdatawidgetmapper/tst_qdatawidgetmapper.cpp index 856672b957..c3527e4215 100644 --- a/tests/auto/widgets/itemviews/qdatawidgetmapper/tst_qdatawidgetmapper.cpp +++ b/tests/auto/widgets/itemviews/qdatawidgetmapper/tst_qdatawidgetmapper.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QComboBox> #include <QDataWidgetMapper> @@ -36,6 +11,8 @@ #include <QTextEdit> #include <QVBoxLayout> +#include <QtWidgets/private/qapplication_p.h> + class tst_QDataWidgetMapper: public QObject { Q_OBJECT @@ -277,22 +254,22 @@ void tst_QDataWidgetMapper::currentIndexChanged() QSignalSpy spy(&mapper, &QDataWidgetMapper::currentIndexChanged); mapper.toFirst(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0).toInt(), 0); mapper.toNext(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0).toInt(), 1); mapper.setCurrentIndex(7); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.takeFirst().at(0).toInt(), 7); mapper.setCurrentIndex(-1); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); mapper.setCurrentIndex(42); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QDataWidgetMapper::changingValues() @@ -443,27 +420,26 @@ void tst_QDataWidgetMapper::textEditDoesntChangeFocusOnTab_qtbug3305() container.show(); - QApplication::setActiveWindow(&container); QVERIFY(QTest::qWaitForWindowActive(&container)); int closeEditorSpyCount = 0; const QString textEditContents = textEdit->toPlainText(); - QCOMPARE(closeEditorSpy.count(), closeEditorSpyCount); + QCOMPARE(closeEditorSpy.size(), closeEditorSpyCount); QVERIFY(lineEdit->hasFocus()); QVERIFY(!textEdit->hasFocus()); // this will generate a closeEditor for the tab key, and another for the focus out QTest::keyClick(QApplication::focusWidget(), Qt::Key_Tab); closeEditorSpyCount += 2; - QTRY_COMPARE(closeEditorSpy.count(), closeEditorSpyCount); + QTRY_COMPARE(closeEditorSpy.size(), closeEditorSpyCount); QTRY_VERIFY(textEdit->hasFocus()); QVERIFY(!lineEdit->hasFocus()); // now that the text edit is focused, a tab keypress will insert a tab, not change focus QTest::keyClick(QApplication::focusWidget(), Qt::Key_Tab); - QTRY_COMPARE(closeEditorSpy.count(), closeEditorSpyCount); + QTRY_COMPARE(closeEditorSpy.size(), closeEditorSpyCount); QVERIFY(!lineEdit->hasFocus()); QVERIFY(textEdit->hasFocus()); @@ -474,7 +450,7 @@ void tst_QDataWidgetMapper::textEditDoesntChangeFocusOnTab_qtbug3305() QTRY_VERIFY(lineEdit->hasFocus()); QVERIFY(!textEdit->hasFocus()); ++closeEditorSpyCount; - QCOMPARE(closeEditorSpy.count(), closeEditorSpyCount); + QCOMPARE(closeEditorSpy.size(), closeEditorSpyCount); } QTEST_MAIN(tst_QDataWidgetMapper) diff --git a/tests/auto/widgets/itemviews/qfileiconprovider/CMakeLists.txt b/tests/auto/widgets/itemviews/qfileiconprovider/CMakeLists.txt index 4107c3584e..88ded71aac 100644 --- a/tests/auto/widgets/itemviews/qfileiconprovider/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qfileiconprovider/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qfileiconprovider.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qfileiconprovider Test: ##################################################################### -qt_add_test(tst_qfileiconprovider +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qfileiconprovider LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qfileiconprovider SOURCES tst_qfileiconprovider.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/itemviews/qfileiconprovider/qfileiconprovider.pro b/tests/auto/widgets/itemviews/qfileiconprovider/qfileiconprovider.pro deleted file mode 100644 index 37173a175e..0000000000 --- a/tests/auto/widgets/itemviews/qfileiconprovider/qfileiconprovider.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_qfileiconprovider -QT += widgets testlib -SOURCES += tst_qfileiconprovider.cpp - - diff --git a/tests/auto/widgets/itemviews/qfileiconprovider/tst_qfileiconprovider.cpp b/tests/auto/widgets/itemviews/qfileiconprovider/tst_qfileiconprovider.cpp index 4824973576..3cba6dbf8b 100644 --- a/tests/auto/widgets/itemviews/qfileiconprovider/tst_qfileiconprovider.cpp +++ b/tests/auto/widgets/itemviews/qfileiconprovider/tst_qfileiconprovider.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QFileIconProvider> @@ -47,7 +22,7 @@ private slots: void type_data(); void type(); - void taskQTBUG_46755_QFileIconEngine_crash(); + void iconPixmaps(); }; void tst_QFileIconProvider::qfileiconprovider() @@ -73,6 +48,7 @@ void tst_QFileIconProvider::iconType_data() // public QIcon icon(QFileIconProvider::IconType const& type) const void tst_QFileIconProvider::iconType() { + QGuiApplication::setDesktopSettingsAware(false); QFETCH(QFileIconProvider::IconType, type); QFileIconProvider provider; QVERIFY(!provider.icon(type).isNull()); @@ -126,17 +102,17 @@ void tst_QFileIconProvider::type() static QIcon getIcon() { QFileIconProvider fip; - return fip.icon(QDir::currentPath()); + return fip.icon(QFileInfo(QDir::currentPath())); } -void tst_QFileIconProvider::taskQTBUG_46755_QFileIconEngine_crash() +void tst_QFileIconProvider::iconPixmaps() { const QIcon &icon = getIcon(); const auto sizes = icon.availableSizes(); - for (const QSize &size : sizes) - icon.pixmap(size); - - // No crash, all good. + for (const QSize &size : sizes) { + QPixmap pixmap = icon.pixmap(size); + QVERIFY(!pixmap.isNull()); + } } QTEST_MAIN(tst_QFileIconProvider) diff --git a/tests/auto/widgets/itemviews/qheaderview/BLACKLIST b/tests/auto/widgets/itemviews/qheaderview/BLACKLIST index 4eceaae0d3..7c9152a16c 100644 --- a/tests/auto/widgets/itemviews/qheaderview/BLACKLIST +++ b/tests/auto/widgets/itemviews/qheaderview/BLACKLIST @@ -1,2 +1,3 @@ +# QTBUG-87406 [stretchAndRestoreLastSection] -opensuse-leap +android diff --git a/tests/auto/widgets/itemviews/qheaderview/CMakeLists.txt b/tests/auto/widgets/itemviews/qheaderview/CMakeLists.txt index d90c12869f..a13454085c 100644 --- a/tests/auto/widgets/itemviews/qheaderview/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qheaderview/CMakeLists.txt @@ -1,15 +1,20 @@ -# Generated from qheaderview.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qheaderview Test: ##################################################################### -qt_add_test(tst_qheaderview +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qheaderview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qheaderview SOURCES tst_qheaderview.cpp - DEFINES - QT_DISABLE_DEPRECATED_BEFORE=0 - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/itemviews/qheaderview/qheaderview.pro b/tests/auto/widgets/itemviews/qheaderview/qheaderview.pro deleted file mode 100644 index a5dfe2113f..0000000000 --- a/tests/auto/widgets/itemviews/qheaderview/qheaderview.pro +++ /dev/null @@ -1,10 +0,0 @@ -CONFIG += testcase -TARGET = tst_qheaderview - -QT += widgets widgets-private testlib -QT += core-private gui-private - -SOURCES += tst_qheaderview.cpp - - -DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0 diff --git a/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp b/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp index 3da8e344bd..57efa3e349 100644 --- a/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp +++ b/tests/auto/widgets/itemviews/qheaderview/tst_qheaderview.cpp @@ -1,31 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Copyright (C) 2012 Thorbjørn Lund Martsum - tmartsum[at]gmail.com -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2012 Thorbjørn Lund Martsum - tmartsum[at]gmail.com +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QHeaderView> #include <QProxyStyle> @@ -37,6 +12,7 @@ #include <QTest> #include <QTreeWidget> #include <QtWidgets/private/qheaderview_p.h> +#include <QtWidgets/private/qapplication_p.h> using BoolList = QList<bool>; using IntList = QList<int>; @@ -104,7 +80,6 @@ class tst_QHeaderView : public QObject public: tst_QHeaderView(); - static void initMain(); private slots: void initTestCase(); @@ -157,6 +132,7 @@ private slots: void moveAndInsertSection(); void highlightSections(); void showSortIndicator(); + void clearSectionSorting(); void sortIndicatorTracking(); void removeAndInsertRow(); void unhideSection(); @@ -171,7 +147,7 @@ private slots: void moveSectionAndReset(); void moveSectionAndRemove(); void saveRestore(); - void restoreQt4State(); + void QTBUG99487_saveRestoreQt5Compat(); void restoreToMoreColumns(); void restoreToMoreColumnsNoMovedColumns(); void restoreBeforeSetModel(); @@ -217,6 +193,7 @@ private slots: void QTBUG75615_sizeHintWithStylesheet(); void ensureNoIndexAtLength(); void offsetConsistent(); + void sectionsDontSortWhenNotClickingInThem(); void initialSortOrderRole(); @@ -249,6 +226,8 @@ private slots: void testResetCachedSizeHint(); void statusTips(); void testRemovingColumnsViaLayoutChanged(); + void testModelMovingColumns(); + void testModelMovingRows(); protected: void setupTestData(bool use_reset_model = false); @@ -265,14 +244,6 @@ protected: QElapsedTimer timer; }; -void tst_QHeaderView::initMain() -{ -#ifdef Q_OS_WIN - // Ensure minimum size constraints of framed windows on High DPI screens - QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); -#endif -} - class QtTestModel: public QAbstractTableModel { Q_OBJECT @@ -337,6 +308,12 @@ public: endRemoveRows(); } + void moveRow(int from, int to) + { + beginMoveRows(QModelIndex(), from, from, QModelIndex(), to); + endMoveRows(); + } + void removeOneColumn(int col) { beginRemoveColumns(QModelIndex(), col, col); @@ -358,6 +335,12 @@ public: endRemoveColumns(); } + void moveColumn(int from, int to) + { + beginMoveColumns(QModelIndex(), from, from, QModelIndex(), to); + endMoveColumns(); + } + void cleanup() { emit layoutAboutToBeChanged(); @@ -499,7 +482,7 @@ void tst_QHeaderView::init() QSignalSpy spy(view, &QHeaderView::sectionCountChanged); view->setModel(model); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); view->resize(200,200); } @@ -820,10 +803,10 @@ void tst_QHeaderView::visualIndexAt() for (int i : hidden) view->setSectionHidden(i, true); - for (int j = 0; j < from.count(); ++j) + for (int j = 0; j < from.size(); ++j) view->moveSection(from.at(j), to.at(j)); - for (int k = 0; k < coordinate.count(); ++k) + for (int k = 0; k < coordinate.size(); ++k) QTRY_COMPARE(view->visualIndexAt(coordinate.at(k)), visual.at(k)); } @@ -935,14 +918,14 @@ void tst_QHeaderView::swapSections() QCOMPARE(view->sectionsMoved(), true); for (int i = 0; i < view->count(); ++i) QCOMPARE(view->logicalIndex(i), logical.at(i)); - QCOMPARE(spy1.count(), 4); + QCOMPARE(spy1.size(), 4); logical = { 3, 1, 2, 0 }; view->swapSections(3, 0); QCOMPARE(view->sectionsMoved(), true); for (int j = 0; j < view->count(); ++j) QCOMPARE(view->logicalIndex(j), logical.at(j)); - QCOMPARE(spy1.count(), 6); + QCOMPARE(spy1.size(), 6); } void tst_QHeaderView::moveSection_data() @@ -988,9 +971,9 @@ void tst_QHeaderView::moveSection() QFETCH(const IntList, logical); QFETCH(int, count); - QCOMPARE(from.count(), to.count()); - QCOMPARE(from.count(), moved.count()); - QCOMPARE(view->count(), logical.count()); + QCOMPARE(from.size(), to.size()); + QCOMPARE(from.size(), moved.size()); + QCOMPARE(view->count(), logical.size()); QSignalSpy spy1(view, &QHeaderView::sectionMoved); QCOMPARE(view->sectionsMoved(), false); @@ -998,7 +981,7 @@ void tst_QHeaderView::moveSection() for (int h : hidden) view->setSectionHidden(h, true); - for (int i = 0; i < from.count(); ++i) { + for (int i = 0; i < from.size(); ++i) { view->moveSection(from.at(i), to.at(i)); QCOMPARE(view->sectionsMoved(), moved.at(i)); } @@ -1006,7 +989,7 @@ void tst_QHeaderView::moveSection() for (int j = 0; j < view->count(); ++j) QCOMPARE(view->logicalIndex(j), logical.at(j)); - QCOMPARE(spy1.count(), count); + QCOMPARE(spy1.size(), count); } void tst_QHeaderView::resizeAndMoveSection_data() @@ -1191,14 +1174,14 @@ void tst_QHeaderView::resizeWithResizeModes() QFETCH(const IntList, expected); view->setStretchLastSection(false); - for (int i = 0; i < sections.count(); ++i) { + for (int i = 0; i < sections.size(); ++i) { view->resizeSection(i, sections.at(i)); view->setSectionResizeMode(i, modes.at(i)); } topLevel->show(); QVERIFY(QTest::qWaitForWindowExposed(topLevel)); view->resize(size, size); - for (int j = 0; j < expected.count(); ++j) + for (int j = 0; j < expected.size(); ++j) QCOMPARE(view->sectionSize(j), expected.at(j)); } @@ -1225,7 +1208,7 @@ void tst_QHeaderView::moveAndInsertSection() view->moveSection(from, to); model->insertRow(insert); - for (int i = 0; i < mapping.count(); ++i) + for (int i = 0; i < mapping.size(); ++i) QCOMPARE(view->logicalIndex(i), mapping.at(i)); } @@ -1296,21 +1279,21 @@ void tst_QHeaderView::resizeSection() view->setSectionsMovable(true); view->setStretchLastSection(false); - for (int i = 0; i < logical.count(); ++i) + for (int i = 0; i < logical.size(); ++i) if (logical.at(i) > -1 && logical.at(i) < view->count()) // for now view->setSectionResizeMode(logical.at(i), mode.at(i)); - for (int j = 0; j < logical.count(); ++j) + for (int j = 0; j < logical.size(); ++j) view->resizeSection(logical.at(j), initial); QSignalSpy spy(view, &QHeaderView::sectionResized); - for (int k = 0; k < logical.count(); ++k) + for (int k = 0; k < logical.size(); ++k) view->resizeSection(logical.at(k), size.at(k)); - QCOMPARE(spy.count(), resized); + QCOMPARE(spy.size(), resized); - for (int l = 0; l < logical.count(); ++l) + for (int l = 0; l < logical.size(); ++l) QCOMPARE(view->sectionSize(logical.at(l)), expected.at(l)); } @@ -1342,6 +1325,75 @@ void tst_QHeaderView::showSortIndicator() // Don't assert baby :) } +void tst_QHeaderView::clearSectionSorting() +{ + QStandardItemModel m(4, 4); + QHeaderView h(Qt::Horizontal); + + QCOMPARE(h.sortIndicatorSection(), 0); + QCOMPARE(h.sortIndicatorOrder(), Qt::DescendingOrder); + + h.setModel(&m); + h.setSectionsClickable(true); + h.setSortIndicatorShown(true); + h.setSortIndicator(-1, Qt::DescendingOrder); + h.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&h)); + + QCOMPARE(h.sortIndicatorSection(), -1); + QCOMPARE(h.sortIndicatorOrder(), Qt::DescendingOrder); + + QSignalSpy sectionClickedSpy(&h, &QHeaderView::sectionClicked); + QVERIFY(sectionClickedSpy.isValid()); + QCOMPARE(sectionClickedSpy.size(), 0); + + QSignalSpy sortIndicatorChangedSpy(&h, &QHeaderView::sortIndicatorChanged); + QVERIFY(sortIndicatorChangedSpy.isValid()); + QCOMPARE(sortIndicatorChangedSpy.size(), 0); + + enum { Count = 30 }; + + // normal behavior: clicking multiple times will just toggle the sort indicator + for (int i = 0; i < Count; ++i) { + QTest::mouseClick(h.viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(5, 5)); + QCOMPARE(sectionClickedSpy.size(), i + 1); + QCOMPARE(sortIndicatorChangedSpy.size(), i + 1); + QCOMPARE(h.sortIndicatorSection(), 0); + const auto expectedOrder = (i % 2) == 0 ? Qt::AscendingOrder : Qt::DescendingOrder; + QCOMPARE(h.sortIndicatorOrder(), expectedOrder); + } + + h.setSortIndicator(-1, Qt::DescendingOrder); + h.setSortIndicatorClearable(true); + QCOMPARE(h.sortIndicatorSection(), -1); + QCOMPARE(h.sortIndicatorOrder(), Qt::DescendingOrder); + + sectionClickedSpy.clear(); + sortIndicatorChangedSpy.clear(); + + // clearing behavior: clicking multiple times will be tristate (asc, desc, nothing) + for (int i = 0; i < Count; ++i) { + QTest::mouseClick(h.viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(5, 5)); + QCOMPARE(sectionClickedSpy.size(), i + 1); + QCOMPARE(sortIndicatorChangedSpy.size(), i + 1); + switch (i % 3) { + case 0: + QCOMPARE(h.sortIndicatorSection(), 0); + QCOMPARE(h.sortIndicatorOrder(), Qt::AscendingOrder); + break; + case 1: + QCOMPARE(h.sortIndicatorSection(), 0); + QCOMPARE(h.sortIndicatorOrder(), Qt::DescendingOrder); + break; + case 2: + QCOMPARE(h.sortIndicatorSection(), -1); + QCOMPARE(h.sortIndicatorOrder(), Qt::AscendingOrder); + break; + } + } +} + void tst_QHeaderView::sortIndicatorTracking() { QtTestModel model(10, 10); @@ -1449,11 +1501,11 @@ void tst_QHeaderView::testEvent() void protected_QHeaderView::testEvent() { // No crashy please - QHoverEvent enterEvent(QEvent::HoverEnter, QPoint(), QPoint()); + QHoverEvent enterEvent(QEvent::HoverEnter, QPoint(), QPoint(), QPoint()); event(&enterEvent); - QHoverEvent eventLeave(QEvent::HoverLeave, QPoint(), QPoint()); + QHoverEvent eventLeave(QEvent::HoverLeave, QPoint(), QPoint(), QPoint()); event(&eventLeave); - QHoverEvent eventMove(QEvent::HoverMove, QPoint(), QPoint()); + QHoverEvent eventMove(QEvent::HoverMove, QPoint(), QPoint(), QPoint()); event(&eventMove); } @@ -1568,7 +1620,6 @@ void tst_QHeaderView::focusPolicy() widget.show(); widget.setFocus(Qt::OtherFocusReason); - QApplication::setActiveWindow(&widget); widget.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&widget)); QVERIFY(widget.hasFocus()); @@ -1673,18 +1724,26 @@ static QByteArray savedState() return h1.saveState(); } -void tst_QHeaderView::saveRestore() +// As generated by savedState() +static const QByteArray qt5SavedSate = QByteArrayLiteral("\x00\x00\x00\xFF\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x02\x01\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x04\b\x00\x00\x00\x01\x00\x00\x00\x03\x00\x00\x00""d\x00\x00\x00\xD2\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00""d\x00\x00\x00\x00\x00\x00\x00\x84\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00""d\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\n\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00""d\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x03\xE8\x00\x00\x00\x00\x00\x00\x00\x00\x00"); + +enum class SaveRestoreOption +{ + CheckGeneratedState, + DoNotCheckGeneratedState, +}; + +static void saveRestoreImpl(const QByteArray &state, SaveRestoreOption option) { QStandardItemModel m(4, 4); - const QByteArray s1 = savedState(); QHeaderView h2(Qt::Vertical); QSignalSpy spy(&h2, &QHeaderView::sortIndicatorChanged); h2.setModel(&m); - QVERIFY(h2.restoreState(s1)); + QVERIFY(h2.restoreState(state)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); QCOMPARE(spy.at(0).at(0).toInt(), 2); QCOMPARE(h2.logicalIndex(0), 2); @@ -1697,50 +1756,28 @@ void tst_QHeaderView::saveRestore() QVERIFY(h2.isSectionHidden(3)); QCOMPARE(h2.hiddenSectionCount(), 1); - QByteArray s2 = h2.saveState(); - QCOMPARE(s1, s2); + switch (option) { + case SaveRestoreOption::CheckGeneratedState: + { + QByteArray s2 = h2.saveState(); + QCOMPARE(state, s2); + break; + } + case SaveRestoreOption::DoNotCheckGeneratedState: + break; + }; QVERIFY(!h2.restoreState(QByteArrayLiteral("Garbage"))); } -void tst_QHeaderView::restoreQt4State() +void tst_QHeaderView::saveRestore() +{ + saveRestoreImpl(savedState(), SaveRestoreOption::CheckGeneratedState); +} + +void tst_QHeaderView::QTBUG99487_saveRestoreQt5Compat() { -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - // QTBUG-40462 - // Setting from Qt4, where information about multiple sections were grouped together in one - // sectionItem object - QStandardItemModel m(4, 10); - QHeaderView h2(Qt::Vertical); - QByteArray settings_qt4 = - QByteArray::fromHex("000000ff00000000000000010000000100000000010000000000000000000000000000" - "0000000003e80000000a0101000100000000000000000000000064ffffffff00000081" - "0000000000000001000003e80000000a00000000"); - QVERIFY(h2.restoreState(settings_qt4)); - int sectionItemsLengthTotal = 0; - for (int i = 0; i < h2.count(); ++i) - sectionItemsLengthTotal += h2.sectionSize(i); - QCOMPARE(sectionItemsLengthTotal, h2.length()); - - // Buggy setting where sum(sectionItems) != length. Check false is returned and this corrupted - // state isn't restored - QByteArray settings_buggy_length = - QByteArray::fromHex("000000ff000000000000000100000000000000050100000000000000000000000a4000" - "000000010000000600000258000000fb0000000a010100010000000000000000000000" - "0064ffffffff00000081000000000000000a000000d30000000100000000000000c800" - "000001000000000000008000000001000000000000005c00000001000000000000003c" - "0000000100000000000002580000000100000000000000000000000100000000000002" - "580000000100000000000002580000000100000000000003c000000001000000000000" - "03e8"); - int old_length = h2.length(); - QByteArray old_state = h2.saveState(); - // Check setting is correctly recognized as corrupted - QVERIFY(!h2.restoreState(settings_buggy_length)); - // Check nothing has been actually restored - QCOMPARE(h2.length(), old_length); - QCOMPARE(h2.saveState(), old_state); -#else - QSKIP("Qt4 compatibility no longer needed in Qt6"); -#endif + saveRestoreImpl(qt5SavedSate, SaveRestoreOption::DoNotCheckGeneratedState); } void tst_QHeaderView::restoreToMoreColumns() @@ -2010,9 +2047,9 @@ void tst_QHeaderView::sectionPressedSignal() QSignalSpy spy(&h, &QHeaderView::sectionPressed); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QTest::mousePress(h.viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(5, 5)); - QCOMPARE(spy.count(), count); + QCOMPARE(spy.size(), count); } void tst_QHeaderView::sectionClickedSignal() @@ -2032,19 +2069,19 @@ void tst_QHeaderView::sectionClickedSignal() QSignalSpy spy(&h, &QHeaderView::sectionClicked); QSignalSpy spy2(&h, &QHeaderView::sortIndicatorChanged); - QCOMPARE(spy.count(), 0); - QCOMPARE(spy2.count(), 0); + QCOMPARE(spy.size(), 0); + QCOMPARE(spy2.size(), 0); QTest::mouseClick(h.viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(5, 5)); - QCOMPARE(spy.count(), count); - QCOMPARE(spy2.count(), count); + QCOMPARE(spy.size(), count); + QCOMPARE(spy2.size(), count); //now let's try with the sort indicator hidden (the result should be the same spy.clear(); spy2.clear(); h.setSortIndicatorShown(false); QTest::mouseClick(h.viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(5, 5)); - QCOMPARE(spy.count(), count); - QCOMPARE(spy2.count(), count); + QCOMPARE(spy.size(), count); + QCOMPARE(spy2.size(), count); } void tst_QHeaderView::defaultSectionSize_data() @@ -2160,6 +2197,9 @@ void tst_QHeaderView::preserveHiddenSectionWidth() void tst_QHeaderView::invisibleStretchLastSection() { + if (QGuiApplication::platformName().startsWith(QLatin1String("eglfs"), Qt::CaseInsensitive)) + QSKIP("EGLFS does not allow resizing on top window"); + int count = 6; QStandardItemModel model(1, count); QHeaderView view(Qt::Horizontal); @@ -2225,7 +2265,7 @@ void tst_QHeaderView::task236450_hidden() for (int i : hide1) view.hideSection(i); - QCOMPARE(view.hiddenSectionCount(), hide1.count()); + QCOMPARE(view.hiddenSectionCount(), hide1.size()); for (int i = 0; i < 6; i++) QCOMPARE(!view.isSectionHidden(i), !hide1.contains(i)); @@ -2233,13 +2273,13 @@ void tst_QHeaderView::task236450_hidden() view.scheduleDelayedItemsLayout(); view.executeDelayedItemsLayout(); //force to do a relayout - QCOMPARE(view.hiddenSectionCount(), hide1.count()); + QCOMPARE(view.hiddenSectionCount(), hide1.size()); for (int i = 0; i < 6; i++) { QCOMPARE(!view.isSectionHidden(i), !hide1.contains(i)); view.setSectionHidden(i, hide2.contains(i)); } - QCOMPARE(view.hiddenSectionCount(), hide2.count()); + QCOMPARE(view.hiddenSectionCount(), hide2.size()); for (int i = 0; i < 6; i++) QCOMPARE(!view.isSectionHidden(i), !hide2.contains(i)); } @@ -2271,10 +2311,10 @@ void tst_QHeaderView::task248050_hideRow() //returns 0 if everything is fine. static int checkHeaderViewOrder(const QHeaderView *view, const IntList &expected) { - if (view->count() != expected.count()) + if (view->count() != expected.size()) return 1; - for (int i = 0; i < expected.count(); i++) { + for (int i = 0; i < expected.size(); i++) { if (view->logicalIndex(i) != expected.at(i)) return i + 10; if (view->visualIndex(expected.at(i)) != i) @@ -2365,8 +2405,8 @@ void tst_QHeaderView::QTBUG7833_sectionClicked() QTest::mouseClick(tv.horizontalHeader()->viewport(), Qt::LeftButton, Qt::NoModifier, QPoint(tv.horizontalHeader()->sectionViewportPosition(11) + tv.horizontalHeader()->sectionSize(11) / 2, 5)); - QCOMPARE(clickedSpy.count(), 1); - QCOMPARE(pressedSpy.count(), 1); + QCOMPARE(clickedSpy.size(), 1); + QCOMPARE(pressedSpy.size(), 1); QCOMPARE(clickedSpy.at(0).at(0).toInt(), 11); QCOMPARE(pressedSpy.at(0).at(0).toInt(), 11); @@ -2374,8 +2414,8 @@ void tst_QHeaderView::QTBUG7833_sectionClicked() QPoint(tv.horizontalHeader()->sectionViewportPosition(8) + tv.horizontalHeader()->sectionSize(0) / 2, 5)); - QCOMPARE(clickedSpy.count(), 2); - QCOMPARE(pressedSpy.count(), 2); + QCOMPARE(clickedSpy.size(), 2); + QCOMPARE(pressedSpy.size(), 2); QCOMPARE(clickedSpy.at(1).at(0).toInt(), 8); QCOMPARE(pressedSpy.at(1).at(0).toInt(), 8); @@ -2383,8 +2423,8 @@ void tst_QHeaderView::QTBUG7833_sectionClicked() QPoint(tv.horizontalHeader()->sectionViewportPosition(0) + tv.horizontalHeader()->sectionSize(0) / 2, 5)); - QCOMPARE(clickedSpy.count(), 3); - QCOMPARE(pressedSpy.count(), 3); + QCOMPARE(clickedSpy.size(), 3); + QCOMPARE(pressedSpy.size(), 3); QCOMPARE(clickedSpy.at(2).at(0).toInt(), 0); QCOMPARE(pressedSpy.at(2).at(0).toInt(), 0); } @@ -2511,7 +2551,7 @@ public: void insertRowAtBeginning() { Q_EMIT layoutAboutToBeChanged(); - m_displayNames.insert(0, QStringLiteral("Item %1").arg(m_displayNames.count())); + m_displayNames.insert(0, QStringLiteral("Item %1").arg(m_displayNames.size())); // Rows are always inserted at the beginning, so move all others. const auto pl = persistentIndexList(); // The vertical header view will have a persistent index stored here on the second call to insertRowAtBeginning. @@ -2527,7 +2567,7 @@ public: QModelIndex index(int row, int column, const QModelIndex &) const override { return createIndex(row, column); } QModelIndex parent(const QModelIndex &) const override { return QModelIndex(); } - int rowCount(const QModelIndex &) const override { return m_displayNames.count(); } + int rowCount(const QModelIndex &) const override { return m_displayNames.size(); } int columnCount(const QModelIndex &) const override { return 1; } private: @@ -2616,6 +2656,140 @@ void tst_QHeaderView::offsetConsistent() QVERIFY(offset2 > offset1); } +void tst_QHeaderView::sectionsDontSortWhenNotClickingInThem() +{ + QTableView qtv; + QStandardItemModel amodel(1000, 4); + qtv.setModel(&amodel); + QHeaderView *hv = qtv.horizontalHeader(); + hv->setSectionsClickable(true); + hv->setFirstSectionMovable(true); + hv->setSectionsMovable(false); + + enum { DefaultYOffset = 5, OutOfRangeYOffset = 10000 }; + + const auto pressOnSection = [&](int section, int yOffset = DefaultYOffset) + { + QTest::mousePress(hv->viewport(), Qt::LeftButton, Qt::NoModifier, + QPoint(hv->sectionViewportPosition(section) + hv->sectionSize(section) / 2, yOffset)); + }; + const auto moveOntoSection = [&](int section, int yOffset = DefaultYOffset) + { + QTest::mouseMove(hv->viewport(), + QPoint(hv->sectionViewportPosition(section) + hv->sectionSize(section) / 2, yOffset)); + }; + const auto releaseOnSection = [&](int section, int yOffset = DefaultYOffset) + { + QTest::mouseRelease(hv->viewport(), Qt::LeftButton, Qt::NoModifier, + QPoint(hv->sectionViewportPosition(section) + hv->sectionSize(section) / 2, yOffset)); + }; + + hv->setSortIndicator(-1, Qt::AscendingOrder); + QCOMPARE(hv->sortIndicatorSection(), -1); + + pressOnSection(0); + releaseOnSection(0); + // RESULT: sorting + QCOMPARE(hv->sortIndicatorSection(), 0); + + hv->setSortIndicator(-1, Qt::AscendingOrder); + QCOMPARE(hv->sortIndicatorSection(), -1); + + pressOnSection(0); + moveOntoSection(1); + releaseOnSection(1); + // RESULT: no sorting + QCOMPARE(hv->sortIndicatorSection(), -1); + + pressOnSection(0); + moveOntoSection(1); + moveOntoSection(2); + releaseOnSection(2); + // RESULT: no sorting + QCOMPARE(hv->sortIndicatorSection(), -1); + + pressOnSection(0); + moveOntoSection(1); + moveOntoSection(0); + releaseOnSection(0); + // RESULT: sorting by 0 + QCOMPARE(hv->sortIndicatorSection(), 0); + + pressOnSection(0); + moveOntoSection(1); + releaseOnSection(1); + // RESULT: no change, still sorting by 0 + QCOMPARE(hv->sortIndicatorSection(), 0); + + auto sortOrder = hv->sortIndicatorOrder(); + pressOnSection(1); + moveOntoSection(0); + releaseOnSection(0); + // RESULT: no change, still sorting by 0 + QCOMPARE(hv->sortIndicatorSection(), 0); + QCOMPARE(hv->sortIndicatorOrder(), sortOrder); + + pressOnSection(1); + moveOntoSection(0); + moveOntoSection(1); + releaseOnSection(1); + // RESULT: sorting by 1 + QCOMPARE(hv->sortIndicatorSection(), 1); + + pressOnSection(1); + moveOntoSection(0); + releaseOnSection(0); + // RESULT: no change, still sorting by 1 + QCOMPARE(hv->sortIndicatorSection(), 1); + + hv->setSortIndicator(-1, Qt::AscendingOrder); + QCOMPARE(hv->sortIndicatorSection(), -1); + + pressOnSection(0); + releaseOnSection(0, OutOfRangeYOffset); + // RESULT: no sorting + QCOMPARE(hv->sortIndicatorSection(), -1); + + pressOnSection(0); + moveOntoSection(0, OutOfRangeYOffset); + releaseOnSection(0, OutOfRangeYOffset); + // RESULT: no sorting + QCOMPARE(hv->sortIndicatorSection(), -1); + + pressOnSection(0); + moveOntoSection(0, OutOfRangeYOffset); + moveOntoSection(0); + releaseOnSection(0); + // RESULT: sorting by 0 + QCOMPARE(hv->sortIndicatorSection(), 0); + + pressOnSection(1); + releaseOnSection(1, OutOfRangeYOffset); + // RESULT: no change, still sorting by 0 + QCOMPARE(hv->sortIndicatorSection(), 0); + + pressOnSection(1); + moveOntoSection(1, OutOfRangeYOffset); + releaseOnSection(1, OutOfRangeYOffset); + // RESULT: no change, still sorting by 0 + QCOMPARE(hv->sortIndicatorSection(), 0); + + pressOnSection(1); + moveOntoSection(1, OutOfRangeYOffset); + moveOntoSection(1); + releaseOnSection(1); + // RESULT: sorting by 1 + QCOMPARE(hv->sortIndicatorSection(), 1); + + pressOnSection(2); + moveOntoSection(1); + moveOntoSection(2); + moveOntoSection(2, OutOfRangeYOffset); + releaseOnSection(2, OutOfRangeYOffset); + // RESULT: no change, still sorting by 1 + QCOMPARE(hv->sortIndicatorSection(), 1); +} + void tst_QHeaderView::initialSortOrderRole() { QTableView view; // ### Shadowing member view (of type QHeaderView) @@ -2677,10 +2851,6 @@ void tst_QHeaderView::calculateAndCheck(int cppline, const int precalced_compare int sum_visual = 0; int sum_logical = 0; - int sum_size = 0; - int sum_hidden_size = 0; - int sum_lookup_visual = 0; - int sum_lookup_logical = 0; int chk_visual = 1; int chk_logical = 1; @@ -2700,7 +2870,6 @@ void tst_QHeaderView::calculateAndCheck(int cppline, const int precalced_compare sum_visual += visual; sum_logical += logical; - sum_size += ssize; if (visual >= 0) { chk_visual %= p2; @@ -2720,7 +2889,6 @@ void tst_QHeaderView::calculateAndCheck(int cppline, const int precalced_compare if (view->isSectionHidden(i)) { view->showSection(i); int hiddensize = view->sectionSize(i); - sum_hidden_size += hiddensize; chk_hidden_size %= p2; chk_hidden_size += ( (hiddensize + 1) * (i + 1) * p1); // (hiddensize + 1) in the above to differ between hidden and size 0 @@ -2739,8 +2907,6 @@ void tst_QHeaderView::calculateAndCheck(int cppline, const int precalced_compare for (int u = 0; u < max_lookup_count; ++u) { int visu = view->visualIndexAt(u); int logi = view->logicalIndexAt(u); - sum_lookup_visual += logi; - sum_lookup_logical += visu; chk_lookup_visual %= p2; chk_lookup_visual *= ( (u + 1) * p1 * (visu + 2)); chk_lookup_logical %= p2; @@ -2870,7 +3036,7 @@ void tst_QHeaderView::additionalInit() model->setData(model->index(i, 0), QVariant(i)); s.setNum(i); s += QLatin1Char('.'); - s += 'a' + (i % 25); + s += QChar('a' + (i % 25)); model->setData(model->index(i, 1), QVariant(s)); } m_tableview->setUpdatesEnabled(updates_enabled); @@ -3216,7 +3382,7 @@ void tst_QHeaderView::stretchAndRestoreLastSection() const int someOtherSectionSize = 40; const int biggerSizeThanAnySection = 50; - QVERIFY(QTest::qWaitForWindowExposed(&tv)); + QVERIFY(QTest::qWaitForWindowActive(&tv)); QHeaderView &header = *tv.horizontalHeader(); // set minimum size before resizeSections() is called @@ -3442,14 +3608,13 @@ void tst_QHeaderView::statusTips() QtTestModel model(5, 5); headerView.setModel(&model); headerView.viewport()->setMouseTracking(true); - headerView.setGeometry(QRect(QPoint(QApplication::desktop()->geometry().center() - QPoint(250, 250)), + headerView.setGeometry(QRect(QPoint(QGuiApplication::primaryScreen()->geometry().center() - QPoint(250, 250)), QSize(500, 500))); headerView.show(); - QApplication::setActiveWindow(&headerView); QVERIFY(QTest::qWaitForWindowActive(&headerView)); // Ensure it is moved away first and then moved to the relevant section - QTest::mouseMove(QApplication::desktop(), + QTest::mouseMove(&headerView, headerView.rect().bottomLeft() + QPoint(20, 20)); QPoint centerPoint = QRect(headerView.sectionPosition(0), 0, headerView.sectionSize(0), headerView.height()).center(); @@ -3480,5 +3645,41 @@ void tst_QHeaderView::testRemovingColumnsViaLayoutChanged() // The main point of this test is that the section-size restoring code didn't go out of bounds. } +void tst_QHeaderView::testModelMovingColumns() +{ + QtTestModel model(10, 10); + QHeaderView hv(Qt::Horizontal); + hv.setModel(&model); + hv.resizeSections(QHeaderView::ResizeToContents); + hv.show(); + hv.hideSection(3); + QVERIFY(!hv.isSectionHidden(1)); + QVERIFY(hv.isSectionHidden(3)); + + QPersistentModelIndex index3 = model.index(0, 3); + model.moveColumn(3, 1); + QCOMPARE(index3.column(), 1); + QVERIFY(hv.isSectionHidden(1)); + QVERIFY(!hv.isSectionHidden(3)); +} + +void tst_QHeaderView::testModelMovingRows() +{ + QtTestModel model(10, 10); + QHeaderView hv(Qt::Vertical); + hv.setModel(&model); + hv.resizeSections(QHeaderView::ResizeToContents); + hv.show(); + hv.hideSection(3); + QVERIFY(!hv.isSectionHidden(1)); + QVERIFY(hv.isSectionHidden(3)); + + QPersistentModelIndex index3 = model.index(3, 0); + model.moveRow(3, 1); + QCOMPARE(index3.row(), 1); + QVERIFY(hv.isSectionHidden(1)); + QVERIFY(!hv.isSectionHidden(3)); +} + QTEST_MAIN(tst_QHeaderView) #include "tst_qheaderview.moc" diff --git a/tests/auto/widgets/itemviews/qitemdelegate/BLACKLIST b/tests/auto/widgets/itemviews/qitemdelegate/BLACKLIST deleted file mode 100644 index fea108f3fd..0000000000 --- a/tests/auto/widgets/itemviews/qitemdelegate/BLACKLIST +++ /dev/null @@ -1,4 +0,0 @@ -[enterKey] -opensuse-42.3 ci -[testLineEditValidation] -opensuse-42.3 ci diff --git a/tests/auto/widgets/itemviews/qitemdelegate/CMakeLists.txt b/tests/auto/widgets/itemviews/qitemdelegate/CMakeLists.txt index 3ea37aa737..58a00fdc9d 100644 --- a/tests/auto/widgets/itemviews/qitemdelegate/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qitemdelegate/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qitemdelegate.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qitemdelegate Test: ##################################################################### -qt_add_test(tst_qitemdelegate +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qitemdelegate LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qitemdelegate SOURCES tst_qitemdelegate.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets Qt::WidgetsPrivate @@ -16,7 +23,7 @@ qt_add_test(tst_qitemdelegate ## Scopes: ##################################################################### -qt_extend_target(tst_qitemdelegate CONDITION WIN32 - PUBLIC_LIBRARIES +qt_internal_extend_target(tst_qitemdelegate CONDITION WIN32 + LIBRARIES user32 ) diff --git a/tests/auto/widgets/itemviews/qitemdelegate/qitemdelegate.pro b/tests/auto/widgets/itemviews/qitemdelegate/qitemdelegate.pro deleted file mode 100644 index fc617b0e5d..0000000000 --- a/tests/auto/widgets/itemviews/qitemdelegate/qitemdelegate.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_qitemdelegate -QT += widgets widgets-private testlib -SOURCES += tst_qitemdelegate.cpp - -win32: QMAKE_USE += user32 diff --git a/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp b/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp index 36ab1a64a6..769456951f 100644 --- a/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp +++ b/tests/auto/widgets/itemviews/qitemdelegate/tst_qitemdelegate.cpp @@ -1,34 +1,14 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "../../../shared/highdpi.h" -#include <QtTest/QtTest> +#include <QTest> +#include <QTimeZone> +#include <QDateTime> +#include <QTimer> +#include <QTestEventLoop> +#include <QSignalSpy> #include <qabstractitemview.h> #include <qstandarditemmodel.h> @@ -55,11 +35,12 @@ #include <qscreen.h> #include <QtWidgets/private/qabstractitemdelegate_p.h> +#include <QtWidgets/private/qapplication_p.h> Q_DECLARE_METATYPE(QAbstractItemDelegate::EndEditHint) #if defined (Q_OS_WIN) -#include <windows.h> +#include <qt_windows.h> #define Q_CHECK_PAINTEVENTS \ if (::SwitchDesktop(::GetThreadDesktop(::GetCurrentThreadId())) == 0) \ QSKIP("The widgets don't get the paint events"); @@ -72,12 +53,12 @@ Q_DECLARE_METATYPE(QAbstractItemDelegate::EndEditHint) class TestItemDelegate : public QItemDelegate { public: - TestItemDelegate(QObject *parent = 0) : QItemDelegate(parent) {} + TestItemDelegate(QObject *parent = nullptr) : QItemDelegate(parent) {} ~TestItemDelegate() {} void drawDisplay(QPainter *painter, const QStyleOptionViewItem &option, - const QRect &rect, const QString &text) const + const QRect &rect, const QString &text) const override { displayText = text; displayFont = option.font; @@ -86,7 +67,7 @@ public: void drawDecoration(QPainter *painter, const QStyleOptionViewItem &option, - const QRect &rect, const QPixmap &pixmap) const + const QRect &rect, const QPixmap &pixmap) const override { decorationPixmap = pixmap; decorationRect = rect; @@ -113,7 +94,7 @@ public: return QItemDelegate::rect(option, index, role); } - inline bool eventFilter(QObject *object, QEvent *event) + inline bool eventFilter(QObject *object, QEvent *event) override { return QItemDelegate::eventFilter(object, event); } @@ -121,7 +102,7 @@ public: inline bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, - const QModelIndex &index) + const QModelIndex &index) override { return QItemDelegate::editorEvent(event, model, option, index); } @@ -149,19 +130,19 @@ public: ~TestItemModel() {} - int rowCount(const QModelIndex &parent) const + int rowCount(const QModelIndex &parent) const override { Q_UNUSED(parent); return 1; } - int columnCount(const QModelIndex &parent) const + int columnCount(const QModelIndex &parent) const override { Q_UNUSED(parent); return 1; } - QVariant data(const QModelIndex& index, int role) const + QVariant data(const QModelIndex& index, int role) const override { Q_UNUSED(index); static QPixmap pixmap(size); @@ -226,6 +207,8 @@ private slots: void dateTextForRole_data(); void dateTextForRole(); + void reuseEditor(); + private: #ifdef QT_BUILD_INTERNAL struct RoleDelegate : public QItemDelegate @@ -346,16 +329,15 @@ void tst_QItemDelegate::editorKeyPress() view.edit(index); QList<QLineEdit*> lineEditors = view.viewport()->findChildren<QLineEdit *>(); - QCOMPARE(lineEditors.count(), 1); + QCOMPARE(lineEditors.size(), 1); QLineEdit *editor = lineEditors.at(0); QCOMPARE(editor->selectedText(), initial); QTest::keyClicks(editor, expected); QTest::keyClick(editor, Qt::Key_Enter); - QApplication::processEvents(); - QCOMPARE(index.data().toString(), expected); + QTRY_COMPARE(index.data().toString(), expected); } void tst_QItemDelegate::doubleEditorNegativeInput() @@ -375,7 +357,7 @@ void tst_QItemDelegate::doubleEditorNegativeInput() view.edit(index); QList<QDoubleSpinBox*> editors = view.viewport()->findChildren<QDoubleSpinBox *>(); - QCOMPARE(editors.count(), 1); + QCOMPARE(editors.size(), 1); QDoubleSpinBox *editor = editors.at(0); QCOMPARE(editor->value(), double(10)); @@ -692,33 +674,33 @@ void tst_QItemDelegate::testEventFilter() //For each test we send a key event and check if signals were emitted. event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Tab, Qt::NoModifier); QVERIFY(delegate.eventFilter(&widget, event)); - QCOMPARE(closeEditorSpy.count(), 1); - QCOMPARE(commitDataSpy.count(), 1); + QCOMPARE(closeEditorSpy.size(), 1); + QCOMPARE(commitDataSpy.size(), 1); delete event; event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Backtab, Qt::NoModifier); QVERIFY(delegate.eventFilter(&widget, event)); - QCOMPARE(closeEditorSpy.count(), 2); - QCOMPARE(commitDataSpy.count(), 2); + QCOMPARE(closeEditorSpy.size(), 2); + QCOMPARE(commitDataSpy.size(), 2); delete event; event = new QKeyEvent(QEvent::KeyPress, Qt::Key_Escape, Qt::NoModifier); QVERIFY(delegate.eventFilter(&widget, event)); - QCOMPARE(closeEditorSpy.count(), 3); - QCOMPARE(commitDataSpy.count(), 2); + QCOMPARE(closeEditorSpy.size(), 3); + QCOMPARE(commitDataSpy.size(), 2); delete event; event = new QKeyEvent(QEvent::KeyPress, Qt::Key_A, Qt::NoModifier); QVERIFY(!delegate.eventFilter(&widget, event)); - QCOMPARE(closeEditorSpy.count(), 3); - QCOMPARE(commitDataSpy.count(), 2); + QCOMPARE(closeEditorSpy.size(), 3); + QCOMPARE(commitDataSpy.size(), 2); delete event; //Subtest focusEvent event = new QFocusEvent(QEvent::FocusOut); QVERIFY(!delegate.eventFilter(&widget, event)); - QCOMPARE(closeEditorSpy.count(), 4); - QCOMPARE(commitDataSpy.count(), 3); + QCOMPARE(closeEditorSpy.size(), 4); + QCOMPARE(commitDataSpy.size(), 3); delete event; } @@ -765,7 +747,6 @@ void tst_QItemDelegate::dateTimeEditor() widget.setItem(0, 2, item3); widget.show(); QVERIFY(QTest::qWaitForWindowExposed(&widget)); - QApplication::setActiveWindow(&widget); widget.editItem(item1); @@ -781,7 +762,6 @@ void tst_QItemDelegate::dateTimeEditor() timeEditor->setTime(time.addSecs(60)); widget.clearFocus(); - qApp->setActiveWindow(&widget); widget.setFocus(); widget.editItem(item2); @@ -812,7 +792,7 @@ void tst_QItemDelegate::dateTimeEditor() class ChooseEditorDelegate : public QItemDelegate { public: - virtual QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &o, const QModelIndex &i) const + virtual QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &o, const QModelIndex &i) const override { if (m_editor) { m_editor->setParent(parent); @@ -822,14 +802,14 @@ public: return m_editor; } - virtual void destroyEditor(QWidget *editor, const QModelIndex &i) const + virtual void destroyEditor(QWidget *editor, const QModelIndex &i) const override { // This is a reimplementation of QAbstractItemDelegate::destroyEditor just set the variable m_editor to 0 // The only reason we do this is to avoid the not recommended direct delete of editor (destroyEditor uses deleteLater) QItemDelegate::destroyEditor(editor, i); // Allow destroy m_editor = 0; // but clear the variable } - ChooseEditorDelegate(QObject *parent = 0) : QItemDelegate(parent) { } + ChooseEditorDelegate(QObject *parent = nullptr) : QItemDelegate(parent) { } void setNextOpenEditor(QWidget *w) { m_editor = w; } QWidget* currentEditor() const { return m_editor; } private: @@ -843,7 +823,9 @@ class FastEditItemView : public QTableView public: QWidget* fastEdit(const QModelIndex &i) // Consider this as QAbstractItemView::edit( ) { - QWidget *v = itemDelegate()->createEditor(viewport(), viewOptions(), i); + QStyleOptionViewItem option; + initViewItemOption(&option); + QWidget *v = itemDelegate()->createEditor(viewport(), option, i); if (v) itemDelegate()->setEditorData(v, i); return v; @@ -879,7 +861,7 @@ void tst_QItemDelegate::dateAndTimeEditorTest2() QCOMPARE(w.fastEdit(i1), timeEdit.data()); timeEdit->setTime(time1); d->setModelData(timeEdit, &s, i1); - QCOMPARE(s.data(i1).type(), QVariant::Time); // ensure that we wrote a time variant. + QCOMPARE(s.data(i1).metaType().id(), QMetaType::QTime); // ensure that we wrote a time variant. QCOMPARE(s.data(i1).toTime(), time1); // ensure that it is the correct time. w.doCloseEditor(timeEdit); QVERIFY(d->currentEditor() == 0); // should happen at doCloseEditor. We only test this once. @@ -908,7 +890,7 @@ void tst_QItemDelegate::dateAndTimeEditorTest2() QCOMPARE(dateTimeEdit->dateTime(), datetime2); dateTimeEdit->setDateTime(datetime1); d->setModelData(dateTimeEdit, &s, i1); - QCOMPARE(s.data(i1).type(), QVariant::DateTime); // ensure that we wrote a datetime variant. + QCOMPARE(s.data(i1).metaType().id(), QMetaType::QDateTime); // ensure that we wrote a datetime variant. QCOMPARE(s.data(i1).toDateTime(), datetime1); w.doCloseEditor(dateTimeEdit); @@ -919,7 +901,7 @@ void tst_QItemDelegate::dateAndTimeEditorTest2() QCOMPARE(w.fastEdit(i2), dateEdit.data()); dateEdit->setDate(date1); d->setModelData(dateEdit, &s, i2); - QCOMPARE(s.data(i2).type(), QVariant::Date); // ensure that we wrote a time variant. + QCOMPARE(s.data(i2).metaType().id(), QMetaType::QDate); // ensure that we wrote a time variant. QCOMPARE(s.data(i2).toDate(), date1); // ensure that it is the correct date. w.doCloseEditor(dateEdit); @@ -1000,30 +982,30 @@ void tst_QItemDelegate::decoration_data() int pm = QApplication::style()->pixelMetric(QStyle::PM_SmallIconSize); QTest::newRow("pixmap 30x30") - << (int)QVariant::Pixmap + << (int)QMetaType::QPixmap << QSize(30, 30) << QSize(30, 30); QTest::newRow("image 30x30") - << (int)QVariant::Image + << (int)QMetaType::QImage << QSize(30, 30) << QSize(30, 30); //The default engine scales pixmaps down if required, but never up. For WinCE we need bigger IconSize than 30 QTest::newRow("icon 30x30") - << (int)QVariant::Icon + << (int)QMetaType::QIcon << QSize(60, 60) << QSize(pm, pm); QTest::newRow("color 30x30") - << (int)QVariant::Color + << (int)QMetaType::QColor << QSize(30, 30) << QSize(pm, pm); // This demands too much memory and potentially hangs. Feel free to uncomment // for your own testing. // QTest::newRow("pixmap 30x30 big") -// << (int)QVariant::Pixmap +// << (int)QMetaType::QPixmap // << QSize(1024, 1024) // Over 1M // << QSize(1024, 1024); } @@ -1043,30 +1025,29 @@ void tst_QItemDelegate::decoration() TestItemDelegate delegate; table.setItemDelegate(&delegate); table.show(); - QApplication::setActiveWindow(&table); QVERIFY(QTest::qWaitForWindowActive(&table)); QVariant value; - switch ((QVariant::Type)type) { - case QVariant::Pixmap: { + switch (type) { + case QMetaType::QPixmap: { QPixmap pm(size); pm.fill(Qt::black); value = pm; break; } - case QVariant::Image: { + case QMetaType::QImage: { QImage img(size, QImage::Format_Mono); memset(img.bits(), 0, img.sizeInBytes()); value = img; break; } - case QVariant::Icon: { + case QMetaType::QIcon: { QPixmap pm(size); pm.fill(Qt::black); value = QIcon(pm); break; } - case QVariant::Color: + case QMetaType::QColor: value = QColor(Qt::green); break; default: @@ -1239,7 +1220,7 @@ void tst_QItemDelegate::editorEvent() QPoint pos = inCheck ? qApp->style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &option, 0).center() + QPoint(checkMargin, 0) : QPoint(200,200); QEvent *event = new QMouseEvent((QEvent::Type)type, - pos, + pos, pos, (Qt::MouseButton)button, (Qt::MouseButton)button, Qt::NoModifier); @@ -1298,14 +1279,13 @@ void tst_QItemDelegate::enterKey() QListView view; view.setModel(&model); view.show(); - QApplication::setActiveWindow(&view); view.setFocus(); QVERIFY(QTest::qWaitForWindowActive(&view)); struct TestDelegate : public QItemDelegate { WidgetType widgetType; - virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const + virtual QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& /*option*/, const QModelIndex& /*index*/) const override { QWidget *editor = 0; switch(widgetType) { @@ -1332,7 +1312,7 @@ void tst_QItemDelegate::enterKey() view.edit(index); QList<QWidget*> lineEditors = view.viewport()->findChildren<QWidget *>(QString::fromLatin1("TheEditor")); - QCOMPARE(lineEditors.count(), 1); + QCOMPARE(lineEditors.size(), 1); QPointer<QWidget> editor = lineEditors.at(0); QCOMPARE(editor->hasFocus(), true); @@ -1358,7 +1338,6 @@ void tst_QItemDelegate::task257859_finalizeEdit() QListView view; view.setModel(&model); view.show(); - QApplication::setActiveWindow(&view); view.setFocus(); QVERIFY(QTest::qWaitForWindowActive(&view)); @@ -1366,7 +1345,7 @@ void tst_QItemDelegate::task257859_finalizeEdit() view.edit(index); QList<QLineEdit *> lineEditors = view.viewport()->findChildren<QLineEdit *>(); - QCOMPARE(lineEditors.count(), 1); + QCOMPARE(lineEditors.size(), 1); QPointer<QWidget> editor = lineEditors.at(0); QCOMPARE(editor->hasFocus(), true); @@ -1388,6 +1367,7 @@ void tst_QItemDelegate::QTBUG4435_keepSelectionOnCheck() } QTableView view; view.setModel(&model); + view.setSelectionMode(QAbstractItemView::MultiSelection); view.setItemDelegate(new TestItemDelegate(&view)); view.show(); view.selectAll(); @@ -1398,11 +1378,16 @@ void tst_QItemDelegate::QTBUG4435_keepSelectionOnCheck() option.features = QStyleOptionViewItem::HasDisplay | QStyleOptionViewItem::HasCheckIndicator; option.checkState = Qt::CheckState(model.index(0, 0).data(Qt::CheckStateRole).toInt()); const int checkMargin = qApp->style()->pixelMetric(QStyle::PM_FocusFrameHMargin, 0, 0) + 1; - QPoint pos = qApp->style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &option, 0).center() - + QPoint(checkMargin, 0); - QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::ControlModifier, pos); - QTRY_VERIFY(view.selectionModel()->isColumnSelected(0, QModelIndex())); + QRect checkRect = qApp->style()->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &option, 0); + checkRect.translate(checkMargin, 0); + // click into the check mark checks, but doesn't change selection + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, checkRect.center()); QCOMPARE(model.item(0)->checkState(), Qt::Checked); + QTRY_VERIFY(view.selectionModel()->isColumnSelected(0, QModelIndex())); + // click outside the check mark doesn't check, and changes selection + QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, + checkRect.center() + QPoint(checkRect.width(), 0)); + QTRY_VERIFY(!view.selectionModel()->isColumnSelected(0, QModelIndex())); } void tst_QItemDelegate::comboBox() @@ -1414,7 +1399,7 @@ void tst_QItemDelegate::comboBox() widget.setItem(0, 0, item1); widget.show(); QVERIFY(QTest::qWaitForWindowExposed(&widget)); - QApplication::setActiveWindow(&widget); + QApplicationPrivate::setActiveWindow(&widget); widget.editItem(item1); @@ -1454,7 +1439,7 @@ void tst_QItemDelegate::testLineEditValidation() struct TestDelegate : public QItemDelegate { - virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const + virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override { Q_UNUSED(option); Q_UNUSED(index); @@ -1479,7 +1464,6 @@ void tst_QItemDelegate::testLineEditValidation() view.setItemDelegate(&delegate); view.show(); view.setFocus(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QPointer<QLineEdit> editor; @@ -1491,7 +1475,7 @@ void tst_QItemDelegate::testLineEditValidation() const auto findEditors = [&]() { return view.findChildren<QLineEdit *>(QStringLiteral("TheEditor")); }; - QCOMPARE(findEditors().count(), 1); + QCOMPARE(findEditors().size(), 1); editor = findEditors().at(0); editor->clear(); @@ -1511,7 +1495,7 @@ void tst_QItemDelegate::testLineEditValidation() view.setCurrentIndex(index); view.edit(index); - QTRY_COMPARE(findEditors().count(), 1); + QTRY_COMPARE(findEditors().size(), 1); editor = findEditors().at(0); editor->clear(); @@ -1533,13 +1517,13 @@ void tst_QItemDelegate::testLineEditValidation() // reset the view to forcibly close the editor view.reset(); - QTRY_COMPARE(findEditors().count(), 0); + QTRY_COMPARE(findEditors().size(), 0); // set a valid text again view.setCurrentIndex(index); view.edit(index); - QTRY_COMPARE(findEditors().count(), 1); + QTRY_COMPARE(findEditors().size(), 1); editor = findEditors().at(0); editor->clear(); @@ -1603,12 +1587,12 @@ void tst_QItemDelegate::dateTextForRole_data() QDate date(2013, 12, 11); QTime time(10, 9, 8, 765); // Ensure we exercise every time-spec variant: - QTest::newRow("local") << QDateTime(date, time, Qt::LocalTime); - QTest::newRow("UTC") << QDateTime(date, time, Qt::UTC); -#if QT_CONFIG(timezone) + QTest::newRow("local") << QDateTime(date, time); + QTest::newRow("UTC") << QDateTime(date, time, QTimeZone::UTC); +# if QT_CONFIG(timezone) QTest::newRow("zone") << QDateTime(date, time, QTimeZone("Europe/Dublin")); -#endif - QTest::newRow("offset") << QDateTime(date, time, Qt::OffsetFromUTC, 36000); +# endif + QTest::newRow("offset") << QDateTime(date, time, QTimeZone::fromSecondsAheadOfUtc(36000)); #endif } @@ -1631,6 +1615,75 @@ void tst_QItemDelegate::dateTextForRole() #endif } +void tst_QItemDelegate::reuseEditor() +{ + class ReusingDelegate: public QItemDelegate { + public: + using QItemDelegate::QItemDelegate; + ~ReusingDelegate() + { + if (cached) + cached->deleteLater(); + } + + QWidget* createEditor(QWidget* parent, + const QStyleOptionViewItem&, + const QModelIndex&) const override + { + auto *cb = new QComboBox(parent); + cb->addItem("One"); + cb->addItem("Two"); + cb->setEditable(true); + return cb; + } + + void setEditorData(QWidget* editor, const QModelIndex& index) + const override + { + auto *cb = qobject_cast<QComboBox*>(editor); + cb->setCurrentText(index.data(Qt::DisplayRole).toString()); + } + + void setModelData(QWidget* editor, + QAbstractItemModel* model, + const QModelIndex& index) const override + { + auto *cb = qobject_cast<QComboBox*>(editor); + model->setData(index, cb->currentText(), Qt::DisplayRole); + } + + void destroyEditor(QWidget* editor, const QModelIndex&) const override + { + auto *cb = qobject_cast<QComboBox*>(editor); + cb->setParent(nullptr); // How to completely detach the editor from treeview ? + cb->hide(); + cb->setEnabled(false); + cached = cb; + } + + private: + mutable QComboBox* cached = nullptr; + }; + + QStandardItemModel model; + model.appendRow(new QStandardItem("One")); + model.appendRow(new QStandardItem("Two")); + + ReusingDelegate delegate; + + QTreeView tree; + tree.setModel(&model); + tree.setItemDelegate(&delegate); + + tree.show(); + QVERIFY(QTest::qWaitForWindowActive(&tree)); + + tree.edit(model.index(0, 0)); + QTRY_VERIFY(qobject_cast<QComboBox *>(tree.focusWidget())); + + tree.close(); +} + // ### _not_ covered: // editing with a custom editor factory diff --git a/tests/auto/widgets/itemviews/qitemeditorfactory/CMakeLists.txt b/tests/auto/widgets/itemviews/qitemeditorfactory/CMakeLists.txt index e56ec2e865..608c323b44 100644 --- a/tests/auto/widgets/itemviews/qitemeditorfactory/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qitemeditorfactory/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qitemeditorfactory.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qitemeditorfactory Test: ##################################################################### -qt_add_test(tst_qitemeditorfactory +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qitemeditorfactory LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qitemeditorfactory SOURCES tst_qitemeditorfactory.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/itemviews/qitemeditorfactory/qitemeditorfactory.pro b/tests/auto/widgets/itemviews/qitemeditorfactory/qitemeditorfactory.pro deleted file mode 100644 index 5ff7771585..0000000000 --- a/tests/auto/widgets/itemviews/qitemeditorfactory/qitemeditorfactory.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_qitemeditorfactory -QT += widgets testlib -SOURCES += tst_qitemeditorfactory.cpp - - diff --git a/tests/auto/widgets/itemviews/qitemeditorfactory/tst_qitemeditorfactory.cpp b/tests/auto/widgets/itemviews/qitemeditorfactory/tst_qitemeditorfactory.cpp index ed4c543d0a..dcd751c696 100644 --- a/tests/auto/widgets/itemviews/qitemeditorfactory/tst_qitemeditorfactory.cpp +++ b/tests/auto/widgets/itemviews/qitemeditorfactory/tst_qitemeditorfactory.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QDoubleSpinBox> @@ -46,7 +21,7 @@ void tst_QItemEditorFactory::createEditor() QWidget parent; - QWidget *w = factory->createEditor(QVariant::String, &parent); + QWidget *w = factory->createEditor(QMetaType::QString, &parent); QCOMPARE(w->metaObject()->className(), "QExpandingLineEdit"); } @@ -63,23 +38,23 @@ void tst_QItemEditorFactory::createCustomEditor() { QItemEditorFactory editorFactory; - editorFactory.registerEditor(QVariant::Rect, creator); - editorFactory.registerEditor(QVariant::RectF, creator); + editorFactory.registerEditor(QMetaType::QRect, creator); + editorFactory.registerEditor(QMetaType::QRectF, creator); //creator should not be deleted as a result of calling the next line - editorFactory.registerEditor(QVariant::Rect, creator2); + editorFactory.registerEditor(QMetaType::QRect, creator2); QVERIFY(creator); //this should erase creator2 - editorFactory.registerEditor(QVariant::Rect, creator); + editorFactory.registerEditor(QMetaType::QRect, creator); QVERIFY(creator2.isNull()); QWidget parent; - QWidget *w = editorFactory.createEditor(QVariant::Rect, &parent); + QWidget *w = editorFactory.createEditor(QMetaType::QRect, &parent); QCOMPARE(w->metaObject()->className(), "QDoubleSpinBox"); - QCOMPARE(w->metaObject()->userProperty().type(), QVariant::Double); + QCOMPARE(w->metaObject()->userProperty().userType(), QMetaType::Double); } //editorFactory has been deleted, so should be creator @@ -99,12 +74,12 @@ void tst_QItemEditorFactory::uintValues() { QWidget *editor = editorFactory.createEditor(QMetaType::UInt, &parent); QCOMPARE(editor->metaObject()->className(), "QUIntSpinBox"); - QCOMPARE(editor->metaObject()->userProperty().type(), QVariant::UInt); + QCOMPARE(editor->metaObject()->userProperty().userType(), QMetaType::UInt); } { QWidget *editor = editorFactory.createEditor(QMetaType::Int, &parent); QCOMPARE(editor->metaObject()->className(), "QSpinBox"); - QCOMPARE(editor->metaObject()->userProperty().type(), QVariant::Int); + QCOMPARE(editor->metaObject()->userProperty().userType(), QMetaType::Int); } } diff --git a/tests/auto/widgets/itemviews/qitemview/CMakeLists.txt b/tests/auto/widgets/itemviews/qitemview/CMakeLists.txt index b4331b87ca..8ff1fd67c9 100644 --- a/tests/auto/widgets/itemviews/qitemview/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qitemview/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qitemview.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qitemview Test: ##################################################################### -qt_add_test(tst_qitemview +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qitemview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qitemview SOURCES tst_qitemview.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/itemviews/qitemview/qitemview.pro b/tests/auto/widgets/itemviews/qitemview/qitemview.pro deleted file mode 100644 index 079953d60d..0000000000 --- a/tests/auto/widgets/itemviews/qitemview/qitemview.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_qitemview -QT += widgets testlib -SOURCES += tst_qitemview.cpp - - diff --git a/tests/auto/widgets/itemviews/qitemview/tst_qitemview.cpp b/tests/auto/widgets/itemviews/qitemview/tst_qitemview.cpp index 244998eb1c..d5a6ddea6e 100644 --- a/tests/auto/widgets/itemviews/qitemview/tst_qitemview.cpp +++ b/tests/auto/widgets/itemviews/qitemview/tst_qitemview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QRandomGenerator> #include <QStack> diff --git a/tests/auto/widgets/itemviews/qitemview/viewstotest.cpp b/tests/auto/widgets/itemviews/qitemview/viewstotest.cpp index 19f0b80cc6..0c7c17503e 100644 --- a/tests/auto/widgets/itemviews/qitemview/viewstotest.cpp +++ b/tests/auto/widgets/itemviews/qitemview/viewstotest.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QHeaderView> #include <QListView> diff --git a/tests/auto/widgets/itemviews/qlistview/CMakeLists.txt b/tests/auto/widgets/itemviews/qlistview/CMakeLists.txt index 4a098c88f2..f6c9f154de 100644 --- a/tests/auto/widgets/itemviews/qlistview/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qlistview/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qlistview.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qlistview Test: ##################################################################### -qt_add_test(tst_qlistview +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qlistview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qlistview SOURCES tst_qlistview.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate @@ -19,7 +26,7 @@ qt_add_test(tst_qlistview ## Scopes: ##################################################################### -qt_extend_target(tst_qlistview CONDITION WIN32 - PUBLIC_LIBRARIES +qt_internal_extend_target(tst_qlistview CONDITION WIN32 + LIBRARIES user32 ) diff --git a/tests/auto/widgets/itemviews/qlistview/qlistview.pro b/tests/auto/widgets/itemviews/qlistview/qlistview.pro deleted file mode 100644 index 7e2062e86e..0000000000 --- a/tests/auto/widgets/itemviews/qlistview/qlistview.pro +++ /dev/null @@ -1,5 +0,0 @@ -CONFIG += testcase -TARGET = tst_qlistview -QT += widgets gui-private widgets-private core-private testlib testlib-private -SOURCES += tst_qlistview.cpp -win32: QMAKE_USE += user32 diff --git a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp index f5fcc35084..236cd6212f 100644 --- a/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp +++ b/tests/auto/widgets/itemviews/qlistview/tst_qlistview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QListWidget> @@ -37,14 +12,18 @@ #include <QTest> #include <QTimer> #include <QtMath> +#include <QProxyStyle> +#include <QVBoxLayout> +#include <QDialog> #include <QtTest/private/qtesthelpers_p.h> #include <QtWidgets/private/qlistview_p.h> +#include <QtWidgets/private/qapplication_p.h> using namespace QTestPrivate; #if defined(Q_OS_WIN) -# include <windows.h> +# include <qt_windows.h> # include <QDialog> # include <QGuiApplication> # include <QVBoxLayout> @@ -84,13 +63,19 @@ public: using QListView::setSelection; using QListView::setViewportMargins; using QListView::startDrag; - using QListView::viewOptions; + using QListView::initViewItemOption; QRegion getVisualRegionForSelection() const { return QListView::visualRegionForSelection(selectionModel()->selection()); } + void moveEvent(QMoveEvent *e) override + { + QListView::moveEvent(e); + m_gotValidResizeEvent = !e->pos().isNull(); + } friend class tst_QListView; + bool m_gotValidResizeEvent = false; }; class tst_QListView : public QObject @@ -109,6 +94,8 @@ private slots: void moveCursor(); void moveCursor2(); void moveCursor3(); + void moveCursor4(); + void moveCursor5(); void indexAt(); void clicked(); void singleSelectionRemoveRow(); @@ -164,10 +151,18 @@ private slots: void taskQTBUG_39902_mutualScrollBars(); void horizontalScrollingByVerticalWheelEvents(); void taskQTBUG_7232_AllowUserToControlSingleStep(); + void taskQTBUG_58749_adjustToContent(); void taskQTBUG_51086_skippingIndexesInSelectedIndexes(); void taskQTBUG_47694_indexOutOfBoundBatchLayout(); + void moveLastRow(); void itemAlignment(); + void internalDragDropMove_data(); void internalDragDropMove(); + void spacingWithWordWrap_data(); + void spacingWithWordWrap(); + void scrollOnRemove_data(); + void scrollOnRemove(); + void wordWrapNullIcon(); }; // Testing get/set functions @@ -433,7 +428,7 @@ void tst_QListView::cursorMove() } break; default: - QVERIFY(false); + QFAIL(qPrintable(QStringLiteral("Unexpected key: %1").arg(key))); } QCoreApplication::processEvents(); @@ -571,6 +566,76 @@ void tst_QListView::moveCursor3() QCOMPARE(view.selectionModel()->currentIndex(), model.index(0, 0)); } +void tst_QListView::moveCursor4() +{ + int indexCount = 100; + PublicListView listView; + QStandardItemModel model; + for (int i = 0; i < 100; i++) + { + QStandardItem* item = new QStandardItem(QString("item 0%0").arg(i)); + QFont font = item->font(); + font.setPixelSize(14); + item->setFont(font); + model.appendRow(item); + } + QFont font = model.item(0)->font(); + font.setPixelSize(50); + font.setBold(true); + model.item(0)->setFont(font); + listView.setModel(&model); + listView.setFixedSize(200, 200); + listView.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + listView.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + listView.show(); + listView.selectionModel()->setCurrentIndex(model.index(0, 0), QItemSelectionModel::SelectCurrent); + + QModelIndex idx = listView.moveCursor(PublicListView::MovePageDown, Qt::NoModifier); + + int actualIndex = 0; + int indexHeight = 0; + while (indexHeight <= listView.viewport()->height()) { + indexHeight += listView.visualRect(model.item(actualIndex)->index()).height(); + actualIndex++; + } + QTRY_COMPARE(idx, model.index(actualIndex - 2, 0)); + idx = listView.moveCursor(PublicListView::MoveUp, Qt::NoModifier); + QTRY_COMPARE(idx, model.index(0, 0)); + + listView.setCurrentIndex(model.index(indexCount - 2, 0)); + idx = listView.moveCursor(PublicListView::MovePageDown, Qt::NoModifier); + QTRY_COMPARE(idx, model.index(99, 0)); + + listView.setCurrentIndex(model.index(3, 0)); + actualIndex = 3; + indexHeight = 0; + while (indexHeight <= listView.viewport()->height()) { + indexHeight += listView.visualRect(model.item(actualIndex)->index()).height(); + actualIndex++; + } + idx = listView.moveCursor(PublicListView::MovePageDown, Qt::NoModifier); + QTRY_COMPARE(idx, model.index(actualIndex - 2, 0)); +} + +void tst_QListView::moveCursor5() +{ + PublicListView listView; + QStandardItemModel model; + QIcon icon(QPixmap(300,300)); + model.appendRow(new QStandardItem(icon,"11")); + model.appendRow(new QStandardItem(icon,"22")); + model.appendRow(new QStandardItem(icon,"33")); + listView.setModel(&model); + listView.setGeometry(10,10,200,200); + listView.setIconSize(QSize(300,300)); + listView.setViewMode(QListView::IconMode); + listView.setCurrentIndex(model.index(0, 0)); + + QModelIndex idx = listView.moveCursor(PublicListView::MovePageDown, Qt::NoModifier); + QTRY_COMPARE(idx, model.index(1, 0)); + idx = listView.moveCursor(PublicListView::MovePageUp, Qt::NoModifier); + QTRY_COMPARE(idx, model.index(0, 0)); +} class QListViewShowEventListener : public QListView { @@ -661,7 +726,7 @@ void tst_QListView::clicked() continue; QSignalSpy spy(&view, &QListView::clicked); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } } @@ -1090,7 +1155,7 @@ void tst_QListView::selection() v.setSelection(selectionRect, QItemSelectionModel::ClearAndSelect); const QModelIndexList selected = v.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), expectedItems.count()); + QCOMPARE(selected.size(), expectedItems.size()); for (const auto &idx : selected) QVERIFY(expectedItems.contains(idx.row())); } @@ -1500,7 +1565,7 @@ void tst_QListView::task203585_selectAll() QVERIFY(view.selectionModel()->selectedIndexes().isEmpty()); view.setRowHidden(0, false); view.selectAll(); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 1); } void tst_QListView::task228566_infiniteRelayout() @@ -1585,7 +1650,7 @@ void tst_QListView::task196118_visualRegionForSelection() view.selectionModel()->select(top1.index(), QItemSelectionModel::Select); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 1); QVERIFY(view.getVisualRegionForSelection().isEmpty()); } @@ -1642,7 +1707,6 @@ void tst_QListView::keyboardSearch() QListView view; view.setModel(&model); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QTest::keyClick(&view, Qt::Key_K); @@ -1683,7 +1747,7 @@ void tst_QListView::shiftSelectionWithNonUniformItemSizes() QTRY_COMPARE(view.currentIndex(), model.index(1, 0)); QModelIndexList selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 3); + QCOMPARE(selected.size(), 3); QVERIFY(!selected.contains(model.index(0, 0))); } { // Second test: QListView::TopToBottom flow @@ -1710,7 +1774,7 @@ void tst_QListView::shiftSelectionWithNonUniformItemSizes() QTRY_COMPARE(view.currentIndex(), model.index(1, 0)); QModelIndexList selected = view.selectionModel()->selectedIndexes(); - QCOMPARE(selected.count(), 3); + QCOMPARE(selected.size(), 3); QVERIFY(!selected.contains(model.index(0, 0))); } } @@ -1744,7 +1808,6 @@ void tst_QListView::shiftSelectionWithItemAlignment() view.resize(300, view.sizeHintForRow(0) * items.size() / 2 + view.horizontalScrollBar()->height()); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(static_cast<QWidget *>(&view), QApplication::activeWindow()); @@ -1772,7 +1835,7 @@ void tst_QListView::clickOnViewportClearsSelection() view.selectAll(); QModelIndex index = model.index(0); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 1); QVERIFY(view.selectionModel()->isSelected(index)); //we try to click outside of the index @@ -1780,7 +1843,7 @@ void tst_QListView::clickOnViewportClearsSelection() QTest::mousePress(view.viewport(), Qt::LeftButton, {}, point); //at this point, the selection shouldn't have changed - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 1); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 1); QVERIFY(view.selectionModel()->isSelected(index)); QTest::mouseRelease(view.viewport(), Qt::LeftButton, {}, point); @@ -1803,7 +1866,6 @@ void tst_QListView::task262152_setModelColumnNavigate() view.setModelColumn(1); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QCOMPARE(&view, QApplication::activeWindow()); QTest::keyClick(&view, Qt::Key_Down); @@ -1889,7 +1951,7 @@ void tst_QListView::taskQTBUG_435_deselectOnViewportClick() view.setModel(&model); view.setSelectionMode(QAbstractItemView::ExtendedSelection); view.selectAll(); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), model.rowCount()); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), model.rowCount()); const QRect itemRect = view.visualRect(model.index(model.rowCount() - 1)); @@ -1899,7 +1961,7 @@ void tst_QListView::taskQTBUG_435_deselectOnViewportClick() QVERIFY(!view.selectionModel()->hasSelection()); view.selectAll(); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), model.rowCount()); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), model.rowCount()); //and now the right button QTest::mouseClick(view.viewport(), Qt::RightButton, {}, p); @@ -2258,10 +2320,11 @@ void tst_QListView::testScrollToWithHidden() void tst_QListView::testViewOptions() { PublicListView view; - QStyleOptionViewItem options = view.viewOptions(); + QStyleOptionViewItem options; + view.initViewItemOption(&options); QCOMPARE(options.decorationPosition, QStyleOptionViewItem::Left); view.setViewMode(QListView::IconMode); - options = view.viewOptions(); + view.initViewItemOption(&options); QCOMPARE(options.decorationPosition, QStyleOptionViewItem::Top); } @@ -2469,6 +2532,46 @@ void tst_QListView::taskQTBUG_7232_AllowUserToControlSingleStep() QCOMPARE(hStep1, lv.horizontalScrollBar()->singleStep()); } +void tst_QListView::taskQTBUG_58749_adjustToContent() +{ + QStandardItemModel model; + model.setRowCount(20); + model.setColumnCount(1); + const QString rowStr = QStringLiteral("Row number txt:"); + for (int u = 0; u < model.rowCount(); ++u) + model.setData(model.index(u, 0), rowStr + QString::number(u)); + + QDialog w; // It really should work for QWidget, too, but sometimes an event (like move) + // is needed to get the resize triggered. + QVBoxLayout *l = new QVBoxLayout(&w); + l->setSizeConstraint(QLayout::SetFixedSize); + auto *view = new QListView; + view->setModel(&model); + view->setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); + l->addWidget(view); + l->setSizeConstraint(QLayout::SetFixedSize); + w.show(); + QVERIFY(QTest::qWaitForWindowExposed(&w)); + + const QString longText = "Here we have a row text that is somewhat longer ..."; + + QFontMetrics fm(w.font(), &w); + QRect r = fm.boundingRect(model.data(model.index(0, 0)).toString()); + const int longTextWidth = fm.horizontalAdvance(longText); + QVERIFY(w.height() > r.height() * model.rowCount()); + // We have a width longer than the width for the given index data. + QVERIFY(w.width() > r.width()); + // but ... the width should not have a width matching the much longer text. + QVERIFY(w.width() < longTextWidth); + + // use the long text and make sure the width is adjusted. + model.setData(model.index(0, 0), longText); + QApplication::processEvents(); + const QRect itemRect = view->visualRect(model.index(0, 0)); + QVERIFY(w.width() > itemRect.width()); + QCOMPARE_GE(view->width(), itemRect.width()); +} + void tst_QListView::taskQTBUG_51086_skippingIndexesInSelectedIndexes() { QStandardItemModel data(10, 1); @@ -2502,6 +2605,221 @@ void tst_QListView::taskQTBUG_47694_indexOutOfBoundBatchLayout() view.scrollTo(model.index(batchSize - 1, 0)); } +class TstMoveItem +{ + friend class TstMoveModel; +public: + TstMoveItem(TstMoveItem *parent = nullptr) + : parentItem(parent) + { + if (parentItem) + parentItem->childItems.append(this); + } + + ~TstMoveItem() + { + QList<TstMoveItem *> delItms; + delItms.swap(childItems); + qDeleteAll(delItms); + + if (parentItem) + parentItem->childItems.removeAll(this); + } + + int row() + { + if (parentItem) + return parentItem->childItems.indexOf(this); + return -1; + } + +public: + TstMoveItem *parentItem = nullptr; + QList<TstMoveItem *> childItems; + QHash<int, QVariant> data; +}; + +/*! + Test that removing the last row in an IconView mode QListView + doesn't crash. The model is specifically crafted to provoke a + stale QBspTree by returning a 0 column count for indexes without + children, which changes the column count after moving the last row. + + See QTBUG_95463. +*/ +class TstMoveModel : public QAbstractItemModel +{ + Q_OBJECT +public: + TstMoveModel(QObject *parent = nullptr) + : QAbstractItemModel(parent) + { + rootItem = new TstMoveItem; + rootItem->data.insert(Qt::DisplayRole, "root"); + + TstMoveItem *itm = new TstMoveItem(rootItem); + itm->data.insert(Qt::DisplayRole, "parentItem1"); + + TstMoveItem *itmCh = new TstMoveItem(itm); + itmCh->data.insert(Qt::DisplayRole, "childItem"); + + itm = new TstMoveItem(rootItem); + itm->data.insert(Qt::DisplayRole, "parentItem2"); + } + + ~TstMoveModel() + { + delete rootItem; + } + + QModelIndex index(int row, int column, const QModelIndex &idxPar = QModelIndex()) const override + { + QModelIndex idx; + if (hasIndex(row, column, idxPar)) { + TstMoveItem *parentItem = nullptr; + if (idxPar.isValid()) + parentItem = static_cast<TstMoveItem *>(idxPar.internalPointer()); + else + parentItem = rootItem; + + Q_ASSERT(parentItem); + TstMoveItem *childItem = parentItem->childItems.at(row); + if (childItem) + idx = createIndex(row, column, childItem); + } + return idx; + } + + QModelIndex parent(const QModelIndex &index) const override + { + QModelIndex idxPar; + if (index.isValid()) { + TstMoveItem *childItem = static_cast<TstMoveItem *>(index.internalPointer()); + TstMoveItem *parentItem = childItem->parentItem; + if (parentItem != rootItem) + idxPar = createIndex(parentItem->row(), 0, parentItem); + } + return idxPar; + } + + int columnCount(const QModelIndex &idxPar = QModelIndex()) const override + { + int cnt = 0; + if (idxPar.isValid()) { + TstMoveItem *parentItem = static_cast<TstMoveItem *>(idxPar.internalPointer()); + Q_ASSERT(parentItem); + cnt = parentItem->childItems.isEmpty() ? 0 : 1; + } else { + cnt = rootItem->childItems.isEmpty() ? 0 : 1; + } + return cnt; + } + + int rowCount(const QModelIndex &idxPar = QModelIndex()) const override + { + int cnt = 0; + if (idxPar.isValid()) { + TstMoveItem *parentItem = static_cast<TstMoveItem *>(idxPar.internalPointer()); + Q_ASSERT(parentItem); + cnt = parentItem->childItems.size(); + } else { + cnt = rootItem->childItems.size(); + } + return cnt; + } + + Qt::ItemFlags flags(const QModelIndex &index) const override + { + Q_UNUSED(index) + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; + } + + bool hasChildren(const QModelIndex &parent = QModelIndex()) const override + { + bool ret = false; + if (parent.isValid()) { + TstMoveItem *parentItem = static_cast<TstMoveItem *>(parent.internalPointer()); + Q_ASSERT(parentItem); + ret = parentItem->childItems.size() > 0; + } else { + ret = rootItem->childItems.size() > 0; + } + return ret; + } + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override + { + QVariant dt; + if (index.isValid()) { + TstMoveItem *item = static_cast<TstMoveItem *>(index.internalPointer()); + if (item) + dt = item->data.value(role); + } + return dt; + } + + bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) override + { + TstMoveItem *itmSrcParent = itemAt(sourceParent); + TstMoveItem *itmDestParent = itemAt(destinationParent); + + if (itmSrcParent && sourceRow >= 0 + && sourceRow + count <= itmSrcParent->childItems.size() + && itmDestParent && destinationChild <= itmDestParent->childItems.size()) { + beginMoveRows(sourceParent, sourceRow, sourceRow + count - 1, + destinationParent, destinationChild); + QList<TstMoveItem *> itemsToMove; + for (int i = 0; i < count; ++i) { + TstMoveItem *itm = itmSrcParent->childItems.at(sourceRow+i); + itemsToMove.append(itm); + } + for (int i = itemsToMove.size() -1; i >= 0; --i) { + TstMoveItem *itm = itemsToMove.at(i); + itm->parentItem->childItems.removeAll(itm); + itm->parentItem = itmDestParent; + itmDestParent->childItems.insert(destinationChild, itm); + } + endMoveRows(); + return true; + } + return false; + } + +private: + TstMoveItem *itemAt(const QModelIndex &index) const + { + TstMoveItem *item = nullptr; + if (index.isValid()) { + Q_ASSERT(index.model() == this); + item = static_cast<TstMoveItem *>(index.internalPointer()); + } else { + item = rootItem; + } + return item; + } + +private: + TstMoveItem *rootItem = nullptr; +}; + +void tst_QListView::moveLastRow() +{ + TstMoveModel model; + QListView view; + view.setModel(&model); + view.setRootIndex(model.index(0, 0, QModelIndex())); + view.setViewMode(QListView::IconMode); + view.show(); + + QVERIFY(QTest::qWaitForWindowActive(&view)); + + QModelIndex sourceParent = model.index(0, 0); + QModelIndex destinationParent = model.index(1, 0); + // must not crash when paint event is processed + model.moveRow(sourceParent, 0, destinationParent, 0); + QTest::qWait(100); +} + void tst_QListView::itemAlignment() { auto item1 = new QStandardItem("111"); @@ -2525,46 +2843,361 @@ void tst_QListView::itemAlignment() QVERIFY(w.visualRect(item1->index()).width() < w.visualRect(item2->index()).width()); } +void tst_QListView::internalDragDropMove_data() +{ + QTest::addColumn<QListView::ViewMode>("viewMode"); + QTest::addColumn<QAbstractItemView::DragDropMode>("dragDropMode"); + QTest::addColumn<Qt::DropActions>("supportedDropActions"); + QTest::addColumn<Qt::DropAction>("defaultDropAction"); + QTest::addColumn<Qt::ItemFlags>("itemFlags"); + QTest::addColumn<bool>("modelMoves"); + QTest::addColumn<QStringList>("expectedData"); + + const Qt::ItemFlags defaultFlags = Qt::ItemIsSelectable + | Qt::ItemIsEnabled + | Qt::ItemIsEditable + | Qt::ItemIsDragEnabled; + + const QStringList unchanged = QStringList{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}; + const QStringList reordered = QStringList{"0", "2", "3", "4", "5", "6", "7", "8", "9", "1"}; + const QStringList replaced = QStringList{"0", "2", "3", "4", "1", "6", "7", "8", "9"}; + + for (auto viewMode : { QListView::IconMode, QListView::ListMode }) { + for (auto modelMoves : { true, false } ) { + QByteArray rowName = viewMode == QListView::IconMode ? "icon" : "list" ; + rowName += modelMoves ? ", model moves" : ", model doesn't move"; + QTest::newRow((rowName + ", copy&move").constData()) + << viewMode + << QAbstractItemView::InternalMove + << (Qt::CopyAction|Qt::MoveAction) + << Qt::MoveAction + << defaultFlags + << modelMoves + // listview in IconMode doesn't change the model + << ((viewMode == QListView::IconMode && !modelMoves) ? unchanged : reordered); + + QTest::newRow((rowName + ", only move").constData()) + << viewMode + << QAbstractItemView::InternalMove + << (Qt::IgnoreAction|Qt::MoveAction) + << Qt::MoveAction + << defaultFlags + << modelMoves + // listview in IconMode doesn't change the model + << ((viewMode == QListView::IconMode && !modelMoves) ? unchanged : reordered); + + QTest::newRow((rowName + ", replace item").constData()) + << viewMode + << QAbstractItemView::InternalMove + << (Qt::IgnoreAction|Qt::MoveAction) + << Qt::MoveAction + << (defaultFlags | Qt::ItemIsDropEnabled) + << modelMoves + << replaced; + } + } +} + +/* + Test moving of items items via drag'n'drop. + + This should reorder items when an item is dropped in between two items, + or - if items can be dropped on - replace the content of the drop target. + + Test QListView in both icon and list view modes. + + See QTBUG-67440, QTBUG-83084, QTBUG-87057 +*/ void tst_QListView::internalDragDropMove() { const QString platform(QGuiApplication::platformName().toLower()); if (platform != QLatin1String("xcb")) QSKIP("Need a window system with proper DnD support via injected mouse events"); - // on an internal move, the item was deleted which should not happen - // see QTBUG-67440 + QFETCH(QListView::ViewMode, viewMode); + QFETCH(QAbstractItemView::DragDropMode, dragDropMode); + QFETCH(Qt::DropActions, supportedDropActions); + QFETCH(Qt::DropAction, defaultDropAction); + QFETCH(Qt::ItemFlags, itemFlags); + QFETCH(bool, modelMoves); + QFETCH(QStringList, expectedData); + + class ItemModel : public QStringListModel + { + public: + ItemModel() + { + QStringList list; + for (int i = 0; i < 10; ++i) { + list << QString::number(i); + } + setStringList(list); + } + + Qt::DropActions supportedDropActions() const override { return m_supportedDropActions; } + Qt::ItemFlags flags(const QModelIndex &index) const override + { + if (!index.isValid()) + return QStringListModel::flags(index); + return m_itemFlags; + } + bool moveRows(const QModelIndex &sourceParent, int sourceRow, int count, + const QModelIndex &destinationParent, int destinationChild) override + { + if (!m_modelMoves) // many models don't implement moveRows + return false; + return QStringListModel::moveRows(sourceParent, sourceRow, count, + destinationParent, destinationChild); + } + bool setItemData(const QModelIndex &index, const QMap<int, QVariant> &values) override + { + return QStringListModel::setData(index, values.value(Qt::DisplayRole), Qt::DisplayRole); + } + QVariant data(const QModelIndex &index, int role) const override + { + if (role == Qt::DecorationRole) + return QColor(Qt::GlobalColor(index.row() + 1)); + return QStringListModel::data(index, role); + } + QMap<int, QVariant> itemData(const QModelIndex &index) const override + { + auto item = QStringListModel::itemData(index); + item[Qt::DecorationRole] = data(index, Qt::DecorationRole); + return item; + } + + Qt::DropActions m_supportedDropActions; + Qt::ItemFlags m_itemFlags; + bool m_modelMoves; + }; + + ItemModel data; + data.m_supportedDropActions = supportedDropActions; + data.m_itemFlags = itemFlags; + data.m_modelMoves = modelMoves; - QStandardItemModel data(0, 1); - QPixmap pixmap(32, 32); - for (int i = 0; i < 10; ++i) { - pixmap.fill(Qt::GlobalColor(i + 1)); - data.appendRow(new QStandardItem(QIcon(pixmap), QString::number(i))); - } QItemSelectionModel selections(&data); PublicListView list; list.setWindowTitle(QTest::currentTestFunction()); - list.setViewMode(QListView::IconMode); - list.setDefaultDropAction(Qt::MoveAction); + list.setViewMode(viewMode); + list.setDragDropMode(dragDropMode); + list.setDefaultDropAction(defaultDropAction); list.setModel(&data); list.setSelectionModel(&selections); - list.resize(300, 300); + int itemHeight = list.sizeHintForIndex(data.index(1, 0)).height(); + list.resize(300, 15 * itemHeight); list.show(); selections.select(data.index(1, 0), QItemSelectionModel::Select); - QVERIFY(QTest::qWaitForWindowExposed(&list)); - + auto getSelectedTexts = [&]() -> QStringList { + QStringList selectedTexts; + for (auto index : selections.selectedIndexes()) + selectedTexts << data.itemData(index).value(Qt::DisplayRole).toString(); + return selectedTexts; + }; + // The test relies on the global position of mouse events; make sure + // the window is properly mapped on X11. + QVERIFY(QTest::qWaitForWindowActive(&list)); + QVERIFY(QTest::qWaitFor([&]() { return list.m_gotValidResizeEvent; })); // execute as soon as the eventloop is running again // which is the case inside list.startDrag() - QTimer::singleShot(0, [&list]() + QTimer::singleShot(0, [&]() { - const QPoint pos = list.rect().center(); - QMouseEvent mouseMove(QEvent::MouseMove, pos, list.mapToGlobal(pos), Qt::NoButton, {}, {}); + QPoint droppos; + // take into account subtle differences between icon and list mode in QListView's drop placement + if (itemFlags & Qt::ItemIsDropEnabled) + droppos = list.rectForIndex(data.index(5, 0)).center(); + else if (viewMode == QListView::IconMode) + droppos = list.rectForIndex(data.index(9, 0)).bottomRight() + QPoint(30, 30); + else + droppos = list.rectForIndex(data.index(9, 0)).bottomRight(); + + QMouseEvent mouseMove(QEvent::MouseMove, droppos, list.mapToGlobal(droppos), Qt::NoButton, {}, {}); QCoreApplication::sendEvent(&list, &mouseMove); - QMouseEvent mouseRelease(QEvent::MouseButtonRelease, pos, list.mapToGlobal(pos), Qt::LeftButton, {}, {}); + QMouseEvent mouseRelease(QEvent::MouseButtonRelease, droppos, list.mapToGlobal(droppos), Qt::LeftButton, {}, {}); QCoreApplication::sendEvent(&list, &mouseRelease); }); - const int expectedCount = data.rowCount(); - list.startDrag(Qt::MoveAction|Qt::CopyAction); - QCOMPARE(expectedCount, data.rowCount()); + + const QStringList expectedSelected = getSelectedTexts(); + + list.startDrag(Qt::MoveAction); + + QTRY_COMPARE(data.stringList(), expectedData); + + // if the model doesn't implement moveRows, or if items are replaced, then selection is lost + if (modelMoves && !(itemFlags & Qt::ItemIsDropEnabled)) { + const QStringList actualSelected = getSelectedTexts(); + QTRY_COMPARE(actualSelected, expectedSelected); + } +} + +/*! + Verify fix for QTBUG-92366 +*/ +void tst_QListView::spacingWithWordWrap_data() +{ + QTest::addColumn<bool>("scrollBarOverlap"); + + QTest::addRow("Without overlap") << false; + QTest::addRow("With overlap") << true; +} + +void tst_QListView::spacingWithWordWrap() +{ + QFETCH(bool, scrollBarOverlap); + + class MyStyle : public QProxyStyle + { + bool scrollBarOverlap; + public: + MyStyle(bool scrollBarOverlap) : scrollBarOverlap(scrollBarOverlap) {} + + int pixelMetric(PixelMetric metric, const QStyleOption *option = nullptr, + const QWidget *widget = nullptr) const override{ + switch (metric) { + case QStyle::PM_ScrollView_ScrollBarOverlap: return scrollBarOverlap; + default: + break; + } + return QProxyStyle::pixelMetric(metric, option, widget); + } + }; + + QStyle *oldStyle = QApplication::style(); + oldStyle->setParent(nullptr); + const auto resetStyle = qScopeGuard([oldStyle]{ + QApplication::setStyle(oldStyle); + }); + QApplication::setStyle(new MyStyle(scrollBarOverlap)); + + const int listViewResizeCount = 200; + QWidget window; + window.resize(300, 200); + QListView lv(&window); + + lv.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + lv.setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); + lv.setFlow(QListView::TopToBottom); + lv.setWordWrap(true); + lv.setSpacing(0); + lv.setGeometry(0, 0, 200, 150); + + QStandardItem *it1 = new QStandardItem("qqqqqqqqqqqqqqqqqqqqq-ttttttttttttttttt"); + QStandardItem *it2 = new QStandardItem("qqqqqqqqqqqqqqqq-tttttttttttt"); + QStandardItemModel model; + lv.setModel(&model); + model.appendRow(it1); + model.appendRow(it2); + + window.show(); + QVERIFY(QTest::qWaitForWindowExposed(&window)); + + QVERIFY(!lv.verticalScrollBar()->isVisible()); + for (int i = 0; i < listViewResizeCount; ++i) { + lv.resize(lv.width() + 1, lv.height()); + QRect rectForRowOne = lv.visualRect(model.index(0, 0)); + QRect rectForRowTwo = lv.visualRect(model.index(1, 0)); + + QCOMPARE(rectForRowOne.y() + rectForRowOne.height(), rectForRowTwo.y()); + } + + lv.resize(200, 150); + const QStringList &stringList = generateList(QStringLiteral("Test_Abnormal_Spacing"), 30); + for (const QString &item_string : stringList) { + QStandardItem *item = new QStandardItem(item_string); + model.appendRow(item); + } + + // test whether the height of item is correct if the vbar is shown. + QTRY_VERIFY(lv.verticalScrollBar()->isVisible()); + for (int i = 0; i < listViewResizeCount; ++i) { + lv.resize(lv.width() + 1, lv.height()); + QRect rectForRowOne = lv.visualRect(model.index(0, 0)); + QRect rectForRowTwo = lv.visualRect(model.index(1, 0)); + + QCOMPARE(rectForRowOne.y() + rectForRowOne.height(), rectForRowTwo.y()); + } +} + +void tst_QListView::scrollOnRemove_data() +{ + QTest::addColumn<QListView::ViewMode>("viewMode"); + QTest::addColumn<QAbstractItemView::SelectionMode>("selectionMode"); + + const QMetaObject &mo = QListView::staticMetaObject; + const auto viewModeEnum = mo.enumerator(mo.indexOfEnumerator("ViewMode")); + const auto selectionModeEnum = mo.enumerator(mo.indexOfEnumerator("SelectionMode")); + for (auto viewMode : { QListView::ListMode, QListView::IconMode }) { + const char *viewModeName = viewModeEnum.valueToKey(viewMode); + for (int index = 0; index < selectionModeEnum.keyCount(); ++index) { + const auto selectionMode = QAbstractItemView::SelectionMode(selectionModeEnum.value(index)); + const char *selectionModeName = selectionModeEnum.valueToKey(selectionMode); + QTest::addRow("%s, %s", viewModeName, selectionModeName) << viewMode << selectionMode; + } + } +} + +void tst_QListView::scrollOnRemove() +{ + QFETCH(QListView::ViewMode, viewMode); + QFETCH(QAbstractItemView::SelectionMode, selectionMode); + + QPixmap pixmap; + if (viewMode == QListView::IconMode) { + pixmap = QPixmap(25, 25); + pixmap.fill(Qt::red); + } + + QStandardItemModel model; + for (int i = 0; i < 50; ++i) { + QStandardItem *item = new QStandardItem(QString::number(i)); + item->setIcon(pixmap); + model.appendRow(item); + } + + QWidget widget; + QListView view(&widget); + view.setFixedSize(100, 100); + view.setAutoScroll(true); + if (viewMode == QListView::IconMode) + view.setWrapping(true); + view.setModel(&model); + view.setSelectionMode(selectionMode); + view.setViewMode(viewMode); + + widget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&widget)); + + QCOMPARE(view.verticalScrollBar()->value(), 0); + const QModelIndex item25 = model.index(25, 0); + view.scrollTo(item25); + QTRY_VERIFY(view.verticalScrollBar()->value() > 0); // layout and scrolling are delayed + const int item25Position = view.verticalScrollBar()->value(); + // selecting a fully visible item shouldn't scroll + view.selectionModel()->setCurrentIndex(item25, QItemSelectionModel::SelectCurrent); + QTRY_COMPARE(view.verticalScrollBar()->value(), item25Position); + + // removing the selected item might scroll if another item is selected + model.removeRow(25); + + // if nothing is selected now, then the view should not have scrolled + if (!view.selectionModel()->selectedIndexes().size()) + QTRY_COMPARE(view.verticalScrollBar()->value(), item25Position); +} + +void tst_QListView::wordWrapNullIcon() +{ + QListView listView; + listView.setViewMode(QListView::IconMode); + listView.setWrapping(true); + listView.setWordWrap(true); + listView.setFixedSize(QSize(100, 500)); + + QStandardItemModel model; + QStandardItem *item = new QStandardItem(QIcon(), "This is a long text for word wrapping Item_"); + model.appendRow(item); + listView.setModel(&model); + + listView.indexAt(QPoint(0, 0)); } diff --git a/tests/auto/widgets/itemviews/qlistwidget/CMakeLists.txt b/tests/auto/widgets/itemviews/qlistwidget/CMakeLists.txt index 426b9088bc..fea17e1d75 100644 --- a/tests/auto/widgets/itemviews/qlistwidget/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qlistwidget/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qlistwidget.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qlistwidget Test: ##################################################################### -qt_add_test(tst_qlistwidget +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qlistwidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qlistwidget SOURCES tst_qlistwidget.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/itemviews/qlistwidget/qlistwidget.pro b/tests/auto/widgets/itemviews/qlistwidget/qlistwidget.pro deleted file mode 100644 index b4aedf1bcf..0000000000 --- a/tests/auto/widgets/itemviews/qlistwidget/qlistwidget.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_qlistwidget -QT += widgets widgets-private testlib -QT += core-private gui-private -SOURCES += tst_qlistwidget.cpp - diff --git a/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp b/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp index 48b0cb8d6b..14a6cee0d9 100644 --- a/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp +++ b/tests/auto/widgets/itemviews/qlistwidget/tst_qlistwidget.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QCompleter> #include <QHBoxLayout> @@ -33,8 +8,11 @@ #include <QSignalSpy> #include <QStyledItemDelegate> #include <QTest> +#include <QLabel> #include <private/qlistwidget_p.h> +#include <QtWidgets/private/qapplication_p.h> + using IntList = QList<int>; class tst_QListWidget : public QObject @@ -97,6 +75,8 @@ private slots: void sortItems(); void sortHiddenItems(); void sortHiddenItems_data(); + void sortCheckStability_data(); + void sortCheckStability(); void closeEditor(); void setData_data(); void setData(); @@ -118,6 +98,8 @@ private slots: void QTBUG14363_completerWithAnyKeyPressedEditTriggers(); void mimeData(); void QTBUG50891_ensureSelectionModelSignalConnectionsAreSet(); + void createPersistentOnLayoutAboutToBeChanged(); + void createPersistentOnLayoutAboutToBeChangedAutoSort(); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void clearItemData(); #endif @@ -126,6 +108,7 @@ private slots: void moveRows(); void moveRowsInvalid_data(); void moveRowsInvalid(); + void noopDragDrop(); protected slots: void rowsAboutToBeInserted(const QModelIndex &parent, int first, int last) @@ -367,7 +350,7 @@ void tst_QListWidget::addItems() QString::number(testWidget->count() + 3), label}; testWidget->addItems(stringList); - QCOMPARE(testWidget->count(), count + stringList.count()); + QCOMPARE(testWidget->count(), count + stringList.size()); QCOMPARE(testWidget->item(testWidget->count()-1)->text(), label); } @@ -379,30 +362,30 @@ void tst_QListWidget::openPersistentEditor() QListWidgetItem *item = new QListWidgetItem(QString::number(testWidget->count())); testWidget->openPersistentEditor(item); - int childCount = testWidget->viewport()->children().count(); + int childCount = testWidget->viewport()->children().size(); testWidget->addItem(item); testWidget->openPersistentEditor(item); - QCOMPARE(childCount + 1, testWidget->viewport()->children().count()); + QCOMPARE(childCount + 1, testWidget->viewport()->children().size()); } void tst_QListWidget::closePersistentEditor() { // Boundary checking - int childCount = testWidget->viewport()->children().count(); + int childCount = testWidget->viewport()->children().size(); testWidget->closePersistentEditor(nullptr); QListWidgetItem *item = new QListWidgetItem(QString::number(testWidget->count())); testWidget->closePersistentEditor(item); - QCOMPARE(childCount, testWidget->viewport()->children().count()); + QCOMPARE(childCount, testWidget->viewport()->children().size()); // Create something testWidget->addItem(item); testWidget->openPersistentEditor(item); // actual test - childCount = testWidget->viewport()->children().count(); + childCount = testWidget->viewport()->children().size(); testWidget->closePersistentEditor(item); QCoreApplication::sendPostedEvents(nullptr, QEvent::DeferredDelete); - QCOMPARE(testWidget->viewport()->children().count(), childCount - 1); + QCOMPARE(testWidget->viewport()->children().size(), childCount - 1); } void tst_QListWidget::setItemHidden() @@ -552,7 +535,7 @@ void tst_QListWidget::editItem() item->setFlags(item->flags() | Qt::ItemIsEditable); testWidget->addItem(item); - int childCount = testWidget->viewport()->children().count(); + int childCount = testWidget->viewport()->children().size(); QWidget *existsAlready = testWidget->indexWidget(testWidget->model()->index(testWidget->row(item), 0)); testWidget->editItem(item); Qt::ItemFlags flags = item->flags(); @@ -560,7 +543,7 @@ void tst_QListWidget::editItem() // There doesn't seem to be a way to detect if the item has already been edited... if (!existsAlready && flags & Qt::ItemIsEditable && flags & Qt::ItemIsEnabled) { QList<QObject *> children = testWidget->viewport()->children(); - QVERIFY(children.count() > childCount); + QVERIFY(children.size() > childCount); bool found = false; for (int i = 0; i < children.size(); ++i) { if (children.at(i)->inherits("QExpandingLineEdit")) @@ -568,7 +551,7 @@ void tst_QListWidget::editItem() } QVERIFY(found); } else { - QCOMPARE(testWidget->viewport()->children().count(), childCount); + QCOMPARE(testWidget->viewport()->children().size(), childCount); } } @@ -577,12 +560,12 @@ void tst_QListWidget::findItems() // This really just tests that the items that are returned are converted from index's to items correctly. // Boundary checking - QCOMPARE(testWidget->findItems("GirlsCanWearJeansAndCutTheirHairShort", Qt::MatchExactly).count(), 0); + QCOMPARE(testWidget->findItems("GirlsCanWearJeansAndCutTheirHairShort", Qt::MatchExactly).size(), 0); populate(); for (int i = 0; i < testWidget->count(); ++i) - QCOMPARE(testWidget->findItems(testWidget->item(i)->text(), Qt::MatchExactly).count(), 1); + QCOMPARE(testWidget->findItems(testWidget->item(i)->text(), Qt::MatchExactly).size(), 1); } @@ -597,8 +580,8 @@ void tst_QListWidget::insertItem_data() QTest::newRow("Insert less then 0") << initialItems << -1 << "inserted" << 0; QTest::newRow("Insert at 0") << initialItems << 0 << "inserted" << 0; - QTest::newRow("Insert beyond count") << initialItems << initialItems.count()+1 << "inserted" << initialItems.count(); - QTest::newRow("Insert at count") << initialItems << initialItems.count() << "inserted" << initialItems.count(); + QTest::newRow("Insert beyond count") << initialItems << initialItems.size()+1 << "inserted" << initialItems.size(); + QTest::newRow("Insert at count") << initialItems << initialItems.size() << "inserted" << initialItems.size(); QTest::newRow("Insert in the middle") << initialItems << 1 << "inserted" << 1; } @@ -610,7 +593,7 @@ void tst_QListWidget::insertItem() QFETCH(int, expectedIndex); testWidget->insertItems(0, initialItems); - QCOMPARE(testWidget->count(), initialItems.count()); + QCOMPARE(testWidget->count(), initialItems.size()); testWidget->insertItem(insertIndex, itemLabel); @@ -619,7 +602,7 @@ void tst_QListWidget::insertItem() QCOMPARE(rcFirst[RowsInserted], expectedIndex); QCOMPARE(rcLast[RowsInserted], expectedIndex); - QCOMPARE(testWidget->count(), initialItems.count() + 1); + QCOMPARE(testWidget->count(), initialItems.size() + 1); QCOMPARE(testWidget->item(expectedIndex)->text(), itemLabel); } @@ -692,8 +675,8 @@ void tst_QListWidget::insertItems() for (int i = 0; i < testWidget->count(); ++i) QCOMPARE(testWidget->item(i)->listWidget(), testWidget); - QCOMPARE(itemChangedSpy.count(), 0); - QCOMPARE(dataChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); + QCOMPARE(dataChangedSpy.size(), 0); } void tst_QListWidget::itemAssignment() @@ -848,7 +831,7 @@ void tst_QListWidget::selectedItems() QListWidgetItem *item = testWidget->item(i); item->setSelected(true); QVERIFY(item->isSelected()); - QCOMPARE(testWidget->selectedItems().count(), 1); + QCOMPARE(testWidget->selectedItems().size(), 1); } //let's clear the selection testWidget->clearSelection(); @@ -866,7 +849,7 @@ void tst_QListWidget::selectedItems() // check that the correct number of items and the expected items are there QList<QListWidgetItem *> selectedItems = testWidget->selectedItems(); - QCOMPARE(selectedItems.count(), expectedRows.count()); + QCOMPARE(selectedItems.size(), expectedRows.size()); for (int row : expectedRows) QVERIFY(selectedItems.contains(testWidget->item(row))); @@ -962,20 +945,20 @@ void tst_QListWidget::moveItemsPriv() else QCOMPARE(testWidget->item(dstRow)->text(), QString::number(srcRow)); - QCOMPARE(beginMoveSpy.count(), 1); + QCOMPARE(beginMoveSpy.size(), 1); const QList<QVariant> &beginMoveArgs = beginMoveSpy.takeFirst(); QCOMPARE(beginMoveArgs.at(1).toInt(), srcRow); QCOMPARE(beginMoveArgs.at(2).toInt(), srcRow); QCOMPARE(beginMoveArgs.at(4).toInt(), dstRow); - QCOMPARE(movedSpy.count(), 1); + QCOMPARE(movedSpy.size(), 1); const QList<QVariant> &movedArgs = movedSpy.takeFirst(); QCOMPARE(movedArgs.at(1).toInt(), srcRow); QCOMPARE(movedArgs.at(2).toInt(), srcRow); QCOMPARE(movedArgs.at(4).toInt(), dstRow); } else { - QCOMPARE(beginMoveSpy.count(), 0); - QCOMPARE(movedSpy.count(), 0); + QCOMPARE(beginMoveSpy.size(), 0); + QCOMPARE(movedSpy.size(), 0); } } @@ -1068,7 +1051,7 @@ void tst_QListWidget::sortItems() testWidget->sortItems(order); - QCOMPARE(testWidget->count(), expectedList.count()); + QCOMPARE(testWidget->count(), expectedList.size()); for (int i = 0; i < testWidget->count(); ++i) QCOMPARE(testWidget->item(i)->text(), expectedList.at(i).toString()); @@ -1138,7 +1121,7 @@ void tst_QListWidget::sortHiddenItems() tw->setSortingEnabled(true); tw->sortItems(order); - QCOMPARE(tw->count(), expectedList.count()); + QCOMPARE(tw->count(), expectedList.size()); for (int i = 0; i < tw->count(); ++i) { QCOMPARE(tw->item(i)->text(), expectedList.at(i)); QCOMPARE(tw->item(i)->isHidden(), !expectedVisibility.at(i)); @@ -1150,6 +1133,64 @@ void tst_QListWidget::sortHiddenItems() delete tw; } +void tst_QListWidget::sortCheckStability_data() { + QTest::addColumn<Qt::SortOrder>("order"); + QTest::addColumn<QVariantList>("initialList"); + QTest::addColumn<QVariantList>("expectedList"); + + QTest::newRow("ascending strings") + << Qt::AscendingOrder + << QVariantList{ QString("a"), QString("b"), QString("b"), QString("a")} + << QVariantList{ QString("a"), QString("a"), QString("b"), QString("b")}; + + QTest::newRow("descending strings") + << Qt::DescendingOrder + << QVariantList{ QString("a"), QString("b"), QString("b"), QString("a")} + << QVariantList{ QString("b"), QString("b"), QString("a"), QString("a")}; + + QTest::newRow("ascending numbers") + << Qt::AscendingOrder + << QVariantList{ 1, 2, 2, 1} + << QVariantList{ 1, 1, 2, 2}; + + QTest::newRow("descending numbers") + << Qt::DescendingOrder + << QVariantList{ 1, 2, 2, 1} + << QVariantList{ 2, 2, 1, 1}; +} + +void tst_QListWidget::sortCheckStability() { + QFETCH(Qt::SortOrder, order); + QFETCH(const QVariantList, initialList); + QFETCH(const QVariantList, expectedList); + + for (const QVariant &data : initialList) { + QListWidgetItem *item = new QListWidgetItem(testWidget); + item->setData(Qt::DisplayRole, data); + } + + QAbstractItemModel *model = testWidget->model(); + QList<QPersistentModelIndex> persistent; + for (int j = 0; j < model->rowCount(QModelIndex()); ++j) + persistent << model->index(j, 0, QModelIndex()); + + testWidget->sortItems(order); + + QCOMPARE(testWidget->count(), expectedList.size()); + for (int i = 0; i < testWidget->count(); ++i) + QCOMPARE(testWidget->item(i)->text(), expectedList.at(i).toString()); + + QVector<QListWidgetItem*> itemOrder(testWidget->count()); + for (int i = 0; i < testWidget->count(); ++i) + itemOrder[i] = testWidget->item(i); + + qobject_cast<QListModel*>(testWidget->model())->ensureSorted(0, order, 1, 1); + testWidget->sortItems(order); + + for (int i = 0; i < testWidget->count(); ++i) + QCOMPARE(itemOrder[i],testWidget->item(i)); +} + class TestListWidget : public QListWidget { Q_OBJECT @@ -1235,17 +1276,17 @@ void tst_QListWidget::setData() QFETCH(QVariantList, values); QFETCH(int, expectedSignalCount); - QCOMPARE(roles.count(), values.count()); + QCOMPARE(roles.size(), values.size()); for (int manipulateModel = 0; manipulateModel < 2; ++manipulateModel) { testWidget->clear(); testWidget->insertItems(0, initialItems); - QCOMPARE(testWidget->count(), initialItems.count()); + QCOMPARE(testWidget->count(), initialItems.size()); QSignalSpy itemChanged(testWidget, &QListWidget::itemChanged); QSignalSpy dataChanged(testWidget->model(), &QAbstractItemModel::dataChanged); - for (int i = 0; i < roles.count(); ++i) { + for (int i = 0; i < roles.size(); ++i) { if (manipulateModel) testWidget->model()->setData( testWidget->model()->index(itemIndex, 0, testWidget->rootIndex()), @@ -1256,12 +1297,12 @@ void tst_QListWidget::setData() } // make sure the data is actually set - for (int i = 0; i < roles.count(); ++i) + for (int i = 0; i < roles.size(); ++i) QCOMPARE(testWidget->item(itemIndex)->data(roles.at(i)), values.at(i)); // make sure we get the right number of emits - QCOMPARE(itemChanged.count(), expectedSignalCount); - QCOMPARE(dataChanged.count(), expectedSignalCount); + QCOMPARE(itemChanged.size(), expectedSignalCount); + QCOMPARE(dataChanged.size(), expectedSignalCount); } } @@ -1403,11 +1444,11 @@ void tst_QListWidget::insertItemsWithSorting() w.addItem(str); break; } - QCOMPARE(w.count(), expectedItems.count()); + QCOMPARE(w.count(), expectedItems.size()); for (int i = 0; i < w.count(); ++i) QCOMPARE(w.item(i)->text(), expectedItems.at(i)); - for (int k = 0; k < persistent.count(); ++k) + for (int k = 0; k < persistent.size(); ++k) QCOMPARE(persistent.at(k).row(), expectedRows.at(k)); } } @@ -1503,7 +1544,7 @@ void tst_QListWidget::itemData() QCOMPARE(widget.currentRoles, QList<int> { Qt::UserRole + i }); } QMap<int, QVariant> flags = widget.model()->itemData(widget.model()->index(0, 0)); - QCOMPARE(flags.count(), 6); + QCOMPARE(flags.size(), 6); for (int i = 0; i < 4; ++i) QCOMPARE(flags[Qt::UserRole + i].toString(), QString::number(i + 1)); @@ -1547,19 +1588,19 @@ void tst_QListWidget::changeDataWithSorting() QListWidgetItem *item = w.item(itemIndex); item->setText(newValue); - for (int i = 0; i < expectedItems.count(); ++i) { + for (int i = 0; i < expectedItems.size(); ++i) { QCOMPARE(w.item(i)->text(), expectedItems.at(i)); - for (int j = 0; j < persistent.count(); ++j) { + for (int j = 0; j < persistent.size(); ++j) { if (persistent.at(j).row() == i) // the same toplevel row QCOMPARE(persistent.at(j).internalPointer(), static_cast<void *>(w.item(i))); } } - for (int k = 0; k < persistent.count(); ++k) + for (int k = 0; k < persistent.size(); ++k) QCOMPARE(persistent.at(k).row(), expectedRows.at(k)); - QCOMPARE(dataChangedSpy.count(), 1); - QCOMPARE(layoutChangedSpy.count(), reorderingExpected ? 1 : 0); + QCOMPARE(dataChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), reorderingExpected ? 1 : 0); } void tst_QListWidget::itemWidget() @@ -1638,7 +1679,7 @@ void tst_QListWidget::insertUnchanged() QListWidget w; QSignalSpy itemChangedSpy(&w, &QListWidget::itemChanged); QListWidgetItem item("foo", &w); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); } void tst_QListWidget::setSortingEnabled() @@ -1729,12 +1770,12 @@ void tst_QListWidget::QTBUG8086_currentItemChangedOnClick() QVERIFY(QTest::qWaitForWindowExposed(&win)); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); QTest::mouseClick(list.viewport(), Qt::LeftButton, {}, list.visualItemRect(list.item(2)).center()); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } @@ -1769,7 +1810,6 @@ void tst_QListWidget::QTBUG14363_completerWithAnyKeyPressedEditTriggers() new QListWidgetItem(QLatin1String("completer"), &listWidget); listWidget.show(); listWidget.setCurrentItem(item); - QApplication::setActiveWindow(&listWidget); QVERIFY(QTest::qWaitForWindowActive(&listWidget)); listWidget.setFocus(); QCOMPARE(QApplication::focusWidget(), &listWidget); @@ -1833,15 +1873,133 @@ void tst_QListWidget::QTBUG50891_ensureSelectionModelSignalConnectionsAreSet() QSignalSpy currentItemChangedSpy(&list, &QListWidget::currentItemChanged); QSignalSpy itemSelectionChangedSpy(&list, &QListWidget::itemSelectionChanged); - QCOMPARE(currentItemChangedSpy.count(), 0); - QCOMPARE(itemSelectionChangedSpy.count(), 0); + QCOMPARE(currentItemChangedSpy.size(), 0); + QCOMPARE(itemSelectionChangedSpy.size(), 0); QTest::mouseClick(list.viewport(), Qt::LeftButton, {}, list.visualItemRect(list.item(2)).center()); - QCOMPARE(currentItemChangedSpy.count(), 1); - QCOMPARE(itemSelectionChangedSpy.count(), 1); + QCOMPARE(currentItemChangedSpy.size(), 1); + QCOMPARE(itemSelectionChangedSpy.size(), 1); + +} + +void tst_QListWidget::createPersistentOnLayoutAboutToBeChanged() // QTBUG-93466 +{ + QListWidget widget; + QCOMPARE(widget.model()->columnCount(), 1); + widget.model()->insertRows(0, 3); + for (int row = 0; row < 3; ++row) + widget.model()->setData(widget.model()->index(row, 0), row); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(widget.model(), &QAbstractItemModel::layoutChanged); + connect(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &widget](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(widget.model()->index(row, 0)); + }); + connect(widget.model(), &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 1); + QCOMPARE(idxList.at(0).column(), 0); + QCOMPARE(idxList.at(0).data().toInt(), 0); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data().toInt(), -1); + QCOMPARE(idxList.at(2).row(), 2); + QCOMPARE(idxList.at(2).column(), 0); + QCOMPARE(idxList.at(2).data().toInt(), 2); + }); + widget.model()->setData(widget.model()->index(1, 0), -1); + widget.model()->sort(0); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + +void tst_QListWidget::createPersistentOnLayoutAboutToBeChangedAutoSort() // QTBUG-93466 +{ + QListWidget widget; + QCOMPARE(widget.model()->columnCount(), 1); + widget.model()->insertRows(0, 3); + for (int row = 0; row < 3; ++row) + widget.model()->setData(widget.model()->index(row, 0), row); + widget.setSortingEnabled(true); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(widget.model(), &QAbstractItemModel::layoutChanged); + connect(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &widget](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(widget.model()->index(row, 0)); + }); + connect(widget.model(), &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 1); + QCOMPARE(idxList.at(0).column(), 0); + QCOMPARE(idxList.at(0).data().toInt(), 0); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data().toInt(), -1); + QCOMPARE(idxList.at(2).row(), 2); + QCOMPARE(idxList.at(2).column(), 0); + QCOMPARE(idxList.at(2).data().toInt(), 2); + }); + widget.model()->setData(widget.model()->index(1, 0), -1); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + +// Test that dropping an item on or beneath itself remains a no-op +void tst_QListWidget::noopDragDrop() // QTBUG-100128 +{ + QListWidget listWidget; + QList<QListWidgetItem *> items; + for (int i = 0; i < 5; ++i) { + const QString number = QString::number(i); + QListWidgetItem *item = new QListWidgetItem(&listWidget); + item->setData(Qt::UserRole, number); + QLabel *label = new QLabel(number); + listWidget.setItemWidget(item, label); + items.append(item); + } + + listWidget.show(); + QVERIFY(QTest::qWaitForWindowExposed(&listWidget)); + + const QRect &lastItemRect = listWidget.visualItemRect(items.at(4)); + const QPoint &dragStart = lastItemRect.center(); + const QPoint &dropPointNirvana = lastItemRect.center() + QPoint(20, 2 * lastItemRect.height()); + + // Implement check as a macro (not a method) to safely determine the error location. + // The macro checks that item data and item widget remain unchanged when drag&drop are executed. + // In order to verify that the assets do *not* change, we can't use QTRY*: These macros would + // spin the event loop only once, while 3/4 mouse events need to get processed. + // That's why we spin the event loop 13 times, to make sure other unexpected or pending events + // get processed. +#define CHECK_ITEM {\ + const QString number = QString::number(4);\ + for (int i = 0; i < 13; ++i)\ + QApplication::processEvents();\ + QLabel *label = qobject_cast<QLabel *>(listWidget.itemWidget(items.at(4)));\ + QVERIFY(label);\ + QCOMPARE(label->text(), number);\ + const QString &data = items.at(4)->data(Qt::UserRole).toString();\ + QCOMPARE(data, number);\ + } + // Test dropping last item beneath itself + QTest::mousePress(&listWidget, Qt::LeftButton, Qt::KeyboardModifiers(), dragStart); + QTest::mouseMove(&listWidget, dropPointNirvana); + QTest::mouseRelease(&listWidget, Qt::LeftButton); + CHECK_ITEM; + + // Test dropping last item on itself + QTest::mousePress(&listWidget, Qt::LeftButton, Qt::KeyboardModifiers(), dragStart); + QTest::mouseMove(&listWidget, dropPointNirvana); + QTest::mouseMove(&listWidget, dragStart); + QTest::mouseRelease(&listWidget, Qt::LeftButton); + CHECK_ITEM; } #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) diff --git a/tests/auto/widgets/itemviews/qtableview/CMakeLists.txt b/tests/auto/widgets/itemviews/qtableview/CMakeLists.txt index d1291e709d..af78c0f5d3 100644 --- a/tests/auto/widgets/itemviews/qtableview/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qtableview/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qtableview.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtableview Test: ##################################################################### -qt_add_test(tst_qtableview +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtableview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtableview SOURCES tst_qtableview.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/itemviews/qtableview/qtableview.pro b/tests/auto/widgets/itemviews/qtableview/qtableview.pro deleted file mode 100644 index a02f96eb99..0000000000 --- a/tests/auto/widgets/itemviews/qtableview/qtableview.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_qtableview -QT += widgets widgets-private testlib -QT += core-private gui-private testlib-private - -SOURCES += tst_qtableview.cpp diff --git a/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp b/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp index 8e2f5ba068..681be2491d 100644 --- a/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp +++ b/tests/auto/widgets/itemviews/qtableview/tst_qtableview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QIdentityProxyModel> #include <QLabel> @@ -77,6 +52,13 @@ public: : QAbstractTableModel(parent), row_count(rows), column_count(columns) {} + void insertRows(int rows) + { + beginInsertRows(QModelIndex(), row_count, row_count + rows - 1); + row_count += rows; + endInsertRows(); + } + int rowCount(const QModelIndex& = QModelIndex()) const override { return row_count; @@ -257,7 +239,7 @@ public: using QTableView::setSelection; using QTableView::selectedIndexes; using QTableView::sizeHintForRow; - using QTableView::viewOptions; + using QTableView::initViewItemOption; bool checkSignalOrder = false; public slots: @@ -384,6 +366,9 @@ private slots: void sortingEnabled_data(); void sortingEnabled(); + void sortByColumn_data(); + void sortByColumn(); + void scrollTo_data(); void scrollTo(); @@ -405,6 +390,8 @@ private slots: void checkHeaderMinSize(); void resizeToContents(); + void resizeToContentsSpans(); + void resizeToContentsEarly(); void tabFocus(); void bigModel(); @@ -433,6 +420,11 @@ private slots: void taskQTBUG_30653_doItemsLayout(); void taskQTBUG_50171_selectRowAfterSwapColumns(); void deselectRow(); + void selectRowsAndCells(); + void selectColumnsAndCells(); + void selectWithHeader_data(); + void selectWithHeader(); + void resetDefaultSectionSize(); #if QT_CONFIG(wheelevent) void mouseWheel_data(); @@ -446,6 +438,7 @@ private slots: void viewOptions(); void taskQTBUG_7232_AllowUserToControlSingleStep(); + void rowsInVerticalHeader(); #if QT_CONFIG(textmarkdownwriter) void markdownWriter(); @@ -520,7 +513,7 @@ void tst_QTableView::emptyModel() QSignalSpy spy(&model, &QtTestTableModel::invalidIndexEncountered); view.setModel(&model); view.show(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QTableView::removeRows_data() @@ -545,10 +538,10 @@ void tst_QTableView::removeRows() view.show(); model.removeLastRow(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); model.removeAllRows(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QTableView::removeColumns_data() @@ -573,10 +566,10 @@ void tst_QTableView::removeColumns() view.show(); model.removeLastColumn(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); model.removeAllColumns(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QTableView::keyboardNavigation_data() @@ -617,7 +610,6 @@ void tst_QTableView::keyboardNavigation() view.setCurrentIndex(index); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); int row = rowCount - 1; @@ -1614,7 +1606,7 @@ void tst_QTableView::selection() view.setSelection(QRect(x, y, width, height), command); - QCOMPARE(view.selectedIndexes().count(), selectedCount); + QCOMPARE(view.selectedIndexes().size(), selectedCount); } void tst_QTableView::selectRow_data() @@ -1727,12 +1719,12 @@ void tst_QTableView::selectRow() view.setSelectionMode(mode); view.setSelectionBehavior(behavior); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 0); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 0); view.selectRow(row); //test we have 10 items selected - QCOMPARE(view.selectionModel()->selectedIndexes().count(), selectedItems); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), selectedItems); //test that all 10 items are in the same row for (int i = 0; selectedItems > 0 && i < rowCount; ++i) QCOMPARE(view.selectionModel()->selectedIndexes().at(i).row(), row); @@ -1848,11 +1840,11 @@ void tst_QTableView::selectColumn() view.setSelectionMode(mode); view.setSelectionBehavior(behavior); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), 0); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), 0); view.selectColumn(column); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), selectedItems); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), selectedItems); for (int i = 0; selectedItems > 0 && i < columnCount; ++i) QCOMPARE(view.selectionModel()->selectedIndexes().at(i).column(), column); } @@ -1952,8 +1944,8 @@ void QTest__keySequence(QWidget* widget, const QKeySequence &ks) { for (int i = 0; i < ks.count(); ++i) { - Qt::Key key = Qt::Key(ks[i] & ~Qt::KeyboardModifierMask); - Qt::KeyboardModifiers modifiers = Qt::KeyboardModifiers(ks[i] & Qt::KeyboardModifierMask); + Qt::Key key = ks[i].key(); + Qt::KeyboardModifiers modifiers = ks[i].keyboardModifiers(); QTest::keyClick(widget, key, modifiers); } } @@ -1997,22 +1989,22 @@ void tst_QTableView::selectall() // try slot first view.clearSelection(); - QCOMPARE(view.selectedIndexes().count(), 0); + QCOMPARE(view.selectedIndexes().size(), 0); view.selectAll(); - QCOMPARE(view.selectedIndexes().count(), selectedCount); + QCOMPARE(view.selectedIndexes().size(), selectedCount); // try by key sequence view.clearSelection(); - QCOMPARE(view.selectedIndexes().count(), 0); + QCOMPARE(view.selectedIndexes().size(), 0); QTest__keySequence(&view, QKeySequence(QKeySequence::SelectAll)); - QCOMPARE(view.selectedIndexes().count(), selectedCount); + QCOMPARE(view.selectedIndexes().size(), selectedCount); // check again with no selection mode view.clearSelection(); view.setSelectionMode(QAbstractItemView::NoSelection); - QCOMPARE(view.selectedIndexes().count(), 0); + QCOMPARE(view.selectedIndexes().size(), 0); QTest__keySequence(&view, QKeySequence(QKeySequence::SelectAll)); - QCOMPARE(view.selectedIndexes().count(), 0); + QCOMPARE(view.selectedIndexes().size(), 0); } #endif // QT_CONFIG(shortcut) @@ -2205,7 +2197,7 @@ void tst_QTableView::resizeRowsToContents() QSignalSpy resizedSpy(view.verticalHeader(), &QHeaderView::sectionResized); view.resizeRowsToContents(); - QCOMPARE(resizedSpy.count(), model.rowCount()); + QCOMPARE(resizedSpy.size(), model.rowCount()); for (int r = 0; r < model.rowCount(); ++r) QCOMPARE(view.rowHeight(r), rowHeight); } @@ -2251,7 +2243,7 @@ void tst_QTableView::resizeColumnsToContents() QSignalSpy resizedSpy(view.horizontalHeader(), &QHeaderView::sectionResized); view.resizeColumnsToContents(); - QCOMPARE(resizedSpy.count(), model.columnCount()); + QCOMPARE(resizedSpy.size(), model.columnCount()); for (int c = 0; c < model.columnCount(); ++c) QCOMPARE(view.columnWidth(c), columnWidth); } @@ -2266,46 +2258,46 @@ void tst_QTableView::rowViewportPosition_data() QTest::addColumn<int>("rowViewportPosition"); QTest::newRow("row 0, scroll per item 0") - << 10 << 40 << 0 << QAbstractItemView::ScrollPerItem << 0 << 0; + << 100 << 40 << 0 << QAbstractItemView::ScrollPerItem << 0 << 0; QTest::newRow("row 1, scroll per item, 0") - << 10 << 40 << 1 << QAbstractItemView::ScrollPerItem << 0 << 1 * 40; + << 100 << 40 << 1 << QAbstractItemView::ScrollPerItem << 0 << 1 * 40; QTest::newRow("row 1, scroll per item, 1") - << 10 << 40 << 1 << QAbstractItemView::ScrollPerItem << 1 << 0; + << 100 << 40 << 1 << QAbstractItemView::ScrollPerItem << 1 << 0; QTest::newRow("row 5, scroll per item, 0") - << 10 << 40 << 5 << QAbstractItemView::ScrollPerItem << 0 << 5 * 40; + << 100 << 40 << 5 << QAbstractItemView::ScrollPerItem << 0 << 5 * 40; QTest::newRow("row 5, scroll per item, 5") - << 10 << 40 << 5 << QAbstractItemView::ScrollPerItem << 5 << 0; + << 100 << 40 << 5 << QAbstractItemView::ScrollPerItem << 5 << 0; QTest::newRow("row 9, scroll per item, 0") - << 10 << 40 << 9 << QAbstractItemView::ScrollPerItem << 0 << 9 * 40; + << 100 << 40 << 9 << QAbstractItemView::ScrollPerItem << 0 << 9 * 40; QTest::newRow("row 9, scroll per item, 5") - << 10 << 40 << 9 << QAbstractItemView::ScrollPerItem << 5 << 4 * 40; + << 100 << 40 << 9 << QAbstractItemView::ScrollPerItem << 5 << 4 * 40; QTest::newRow("row 0, scroll per pixel 0") - << 10 << 40 << 0 << QAbstractItemView::ScrollPerPixel << 0 << 0; + << 100 << 40 << 0 << QAbstractItemView::ScrollPerPixel << 0 << 0; QTest::newRow("row 1, scroll per pixel, 0") - << 10 << 40 << 1 << QAbstractItemView::ScrollPerPixel << 0 << 1 * 40; + << 100 << 40 << 1 << QAbstractItemView::ScrollPerPixel << 0 << 1 * 40; QTest::newRow("row 1, scroll per pixel, 1") - << 10 << 40 << 1 << QAbstractItemView::ScrollPerPixel << 1 * 40 << 0; + << 100 << 40 << 1 << QAbstractItemView::ScrollPerPixel << 1 * 40 << 0; QTest::newRow("row 5, scroll per pixel, 0") - << 10 << 40 << 5 << QAbstractItemView::ScrollPerPixel << 0 << 5 * 40; + << 100 << 40 << 5 << QAbstractItemView::ScrollPerPixel << 0 << 5 * 40; QTest::newRow("row 5, scroll per pixel, 5") - << 10 << 40 << 5 << QAbstractItemView::ScrollPerPixel << 5 * 40 << 0; + << 100 << 40 << 5 << QAbstractItemView::ScrollPerPixel << 5 * 40 << 0; QTest::newRow("row 9, scroll per pixel, 0") - << 10 << 40 << 9 << QAbstractItemView::ScrollPerPixel << 0 << 9 * 40; + << 100 << 40 << 9 << QAbstractItemView::ScrollPerPixel << 0 << 9 * 40; QTest::newRow("row 9, scroll per pixel, 5") - << 10 << 40 << 9 << QAbstractItemView::ScrollPerPixel << 5 * 40 << 4 * 40; + << 100 << 40 << 9 << QAbstractItemView::ScrollPerPixel << 5 * 40 << 4 * 40; } void tst_QTableView::rowViewportPosition() @@ -2365,7 +2357,7 @@ void tst_QTableView::rowAt() for (int r = 0; r < rowCount; ++r) view.setRowHeight(r, rowHeight); - for (int i = 0; i < hiddenRows.count(); ++i) + for (int i = 0; i < hiddenRows.size(); ++i) view.hideRow(hiddenRows.at(i)); QCOMPARE(view.rowAt(coordinate), row); @@ -2429,46 +2421,46 @@ void tst_QTableView::columnViewportPosition_data() QTest::addColumn<int>("columnViewportPosition"); QTest::newRow("column 0, scroll per item 0") - << 10 << 40 << 0 << QAbstractItemView::ScrollPerItem << 0 << 0; + << 100 << 40 << 0 << QAbstractItemView::ScrollPerItem << 0 << 0; QTest::newRow("column 1, scroll per item, 0") - << 10 << 40 << 1 << QAbstractItemView::ScrollPerItem << 0 << 1 * 40; + << 100 << 40 << 1 << QAbstractItemView::ScrollPerItem << 0 << 1 * 40; QTest::newRow("column 1, scroll per item, 1") - << 10 << 40 << 1 << QAbstractItemView::ScrollPerItem << 1 << 0; + << 100 << 40 << 1 << QAbstractItemView::ScrollPerItem << 1 << 0; QTest::newRow("column 5, scroll per item, 0") - << 10 << 40 << 5 << QAbstractItemView::ScrollPerItem << 0 << 5 * 40; + << 100 << 40 << 5 << QAbstractItemView::ScrollPerItem << 0 << 5 * 40; QTest::newRow("column 5, scroll per item, 5") - << 10 << 40 << 5 << QAbstractItemView::ScrollPerItem << 5 << 0; + << 100 << 40 << 5 << QAbstractItemView::ScrollPerItem << 5 << 0; QTest::newRow("column 9, scroll per item, 0") - << 10 << 40 << 9 << QAbstractItemView::ScrollPerItem << 0 << 9 * 40; + << 100 << 40 << 9 << QAbstractItemView::ScrollPerItem << 0 << 9 * 40; QTest::newRow("column 9, scroll per item, 5") - << 10 << 40 << 9 << QAbstractItemView::ScrollPerItem << 5 << 4 * 40; + << 100 << 40 << 9 << QAbstractItemView::ScrollPerItem << 5 << 4 * 40; QTest::newRow("column 0, scroll per pixel 0") - << 10 << 40 << 0 << QAbstractItemView::ScrollPerPixel << 0 << 0; + << 100 << 40 << 0 << QAbstractItemView::ScrollPerPixel << 0 << 0; QTest::newRow("column 1, scroll per pixel 0") - << 10 << 40 << 1 << QAbstractItemView::ScrollPerPixel << 0 << 1 * 40; + << 100 << 40 << 1 << QAbstractItemView::ScrollPerPixel << 0 << 1 * 40; QTest::newRow("column 1, scroll per pixel 1") - << 10 << 40 << 1 << QAbstractItemView::ScrollPerPixel << 1 * 40 << 0; + << 100 << 40 << 1 << QAbstractItemView::ScrollPerPixel << 1 * 40 << 0; QTest::newRow("column 5, scroll per pixel 0") - << 10 << 40 << 5 << QAbstractItemView::ScrollPerPixel << 0 << 5 * 40; + << 100 << 40 << 5 << QAbstractItemView::ScrollPerPixel << 0 << 5 * 40; QTest::newRow("column 5, scroll per pixel 5") - << 10 << 40 << 5 << QAbstractItemView::ScrollPerPixel << 5 * 40 << 0; + << 100 << 40 << 5 << QAbstractItemView::ScrollPerPixel << 5 * 40 << 0; QTest::newRow("column 9, scroll per pixel 0") - << 10 << 40 << 9 << QAbstractItemView::ScrollPerPixel << 0 << 9 * 40; + << 100 << 40 << 9 << QAbstractItemView::ScrollPerPixel << 0 << 9 * 40; QTest::newRow("column 9, scroll per pixel 5") - << 10 << 40 << 9 << QAbstractItemView::ScrollPerPixel << 5 * 40 << 4 * 40; + << 100 << 40 << 9 << QAbstractItemView::ScrollPerPixel << 5 * 40 << 4 * 40; } void tst_QTableView::columnViewportPosition() @@ -2528,7 +2520,7 @@ void tst_QTableView::columnAt() for (int c = 0; c < columnCount; ++c) view.setColumnWidth(c, columnWidth); - for (int i = 0; i < hiddenColumns.count(); ++i) + for (int i = 0; i < hiddenColumns.size(); ++i) view.hideColumn(hiddenColumns.at(i)); QCOMPARE(view.columnAt(coordinate), column); @@ -2681,6 +2673,64 @@ void tst_QTableView::sortingEnabled() // QFETCH(int, columnCount); } +void tst_QTableView::sortByColumn_data() +{ + QTest::addColumn<bool>("sortingEnabled"); + QTest::newRow("sorting enabled") << true; + QTest::newRow("sorting disabled") << false; +} + +// Checks sorting and that sortByColumn also sets the sortIndicator +void tst_QTableView::sortByColumn() +{ + QFETCH(bool, sortingEnabled); + QTableView view; + QStandardItemModel model(4, 2); + QSortFilterProxyModel sfpm; // default QStandardItemModel does not support 'unsorted' state + sfpm.setSourceModel(&model); + model.setItem(0, 0, new QStandardItem("b")); + model.setItem(1, 0, new QStandardItem("d")); + model.setItem(2, 0, new QStandardItem("c")); + model.setItem(3, 0, new QStandardItem("a")); + model.setItem(0, 1, new QStandardItem("e")); + model.setItem(1, 1, new QStandardItem("g")); + model.setItem(2, 1, new QStandardItem("h")); + model.setItem(3, 1, new QStandardItem("f")); + + view.setSortingEnabled(sortingEnabled); + view.setModel(&sfpm); + view.show(); + + view.sortByColumn(1, Qt::DescendingOrder); + QCOMPARE(view.horizontalHeader()->sortIndicatorSection(), 1); + QCOMPARE(view.model()->data(view.model()->index(0, 0)).toString(), QString::fromLatin1("c")); + QCOMPARE(view.model()->data(view.model()->index(1, 0)).toString(), QString::fromLatin1("d")); + QCOMPARE(view.model()->data(view.model()->index(0, 1)).toString(), QString::fromLatin1("h")); + QCOMPARE(view.model()->data(view.model()->index(1, 1)).toString(), QString::fromLatin1("g")); + + view.sortByColumn(0, Qt::AscendingOrder); + QCOMPARE(view.horizontalHeader()->sortIndicatorSection(), 0); + QCOMPARE(view.model()->data(view.model()->index(0, 0)).toString(), QString::fromLatin1("a")); + QCOMPARE(view.model()->data(view.model()->index(1, 0)).toString(), QString::fromLatin1("b")); + QCOMPARE(view.model()->data(view.model()->index(0, 1)).toString(), QString::fromLatin1("f")); + QCOMPARE(view.model()->data(view.model()->index(1, 1)).toString(), QString::fromLatin1("e")); + + view.sortByColumn(-1, Qt::AscendingOrder); + QCOMPARE(view.horizontalHeader()->sortIndicatorSection(), -1); + QCOMPARE(view.model()->data(view.model()->index(0, 0)).toString(), QString::fromLatin1("b")); + QCOMPARE(view.model()->data(view.model()->index(1, 0)).toString(), QString::fromLatin1("d")); + QCOMPARE(view.model()->data(view.model()->index(0, 1)).toString(), QString::fromLatin1("e")); + QCOMPARE(view.model()->data(view.model()->index(1, 1)).toString(), QString::fromLatin1("g")); + + // a new 'sortByColumn()' should do a re-sort (e.g. due to the data changed), QTBUG-86268 + view.setModel(&model); + view.sortByColumn(0, Qt::AscendingOrder); + QCOMPARE(view.model()->data(view.model()->index(0, 0)).toString(), QString::fromLatin1("a")); + model.setItem(0, 0, new QStandardItem("x")); + view.sortByColumn(0, Qt::AscendingOrder); + QCOMPARE(view.model()->data(view.model()->index(0, 0)).toString(), QString::fromLatin1("b")); +} + void tst_QTableView::scrollTo_data() { QTest::addColumn<QAbstractItemView::ScrollMode>("verticalScrollMode"); @@ -3663,6 +3713,99 @@ void tst_QTableView::resizeToContents() } + +class SpanModel : public QAbstractTableModel +{ +public: + SpanModel(bool sectionsMoved) + : _sectionsMoved(sectionsMoved) + {} + int columnCount(const QModelIndex & = {}) const override { return 2; } + int rowCount(const QModelIndex & = {}) const override { return 1; } + QVariant data(const QModelIndex &idx, int role = Qt::DisplayRole) const override + { + if (role != Qt::DisplayRole) + return QVariant(); + const int col = _sectionsMoved ? 1 - idx.column() : idx.column(); + if (col == 0) + return "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."; + return QVariant(); + } +private: + bool _sectionsMoved; +}; + + +void tst_QTableView::resizeToContentsSpans() +{ + SpanModel model1(false); + SpanModel model2(true); + QTableView view1, view2, view3; + view1.setModel(&model1); + view2.setModel(&model2); + view2.horizontalHeader()->moveSection(0, 1); + view3.setModel(&model1); + + view1.setSpan(0, 0, 1, 2); + view2.setSpan(0, 1, 1, 2); + view1.show(); + view2.show(); + view3.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view1)); + QVERIFY(QTest::qWaitForWindowExposed(&view2)); + QVERIFY(QTest::qWaitForWindowExposed(&view3)); + view1.setColumnWidth(0, 100); + view1.setColumnWidth(1, 100); + view2.setColumnWidth(0, 100); + view2.setColumnWidth(1, 100); + view3.setColumnWidth(0, 200); + + view1.resizeRowToContents(0); + view2.resizeRowToContents(0); + view3.resizeRowToContents(0); + QCOMPARE(view1.rowHeight(0), view3.rowHeight(0)); + QCOMPARE(view2.rowHeight(0), view3.rowHeight(0)); + + view3.resizeColumnToContents(0); + view3.resizeRowToContents(0); + // height should be only 1 text line for easy testing + view1.setRowHeight(0, view3.verticalHeader()->sectionSize(0)); + view2.setRowHeight(0, view3.verticalHeader()->sectionSize(0)); + view1.resizeColumnToContents(0); + view2.resizeColumnToContents(1); + QCOMPARE(view1.columnWidth(0), view3.columnWidth(0) - view1.columnWidth(1)); + QCOMPARE(view2.columnWidth(0), view3.columnWidth(0) - view2.columnWidth(1)); +} + +void tst_QTableView::resizeToContentsEarly() +{ + QStringListModel model; + QTableView view; + + // connect to the model before setting it on the view + connect(&model, &QStringListModel::modelReset, &model, [&view]{ + view.resizeColumnsToContents(); + }); + connect(&model, &QStringListModel::modelReset, &model, [&view]{ + view.resizeRowsToContents(); + }); + + // the view only connects now to the model's signals, so responds to the + // reset signal *after* the lambdas above + view.setModel(&model); + + QStringList data(200, QString("Hello World")); + model.setStringList(data); + + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + view.verticalScrollBar()->setValue(view.verticalScrollBar()->maximum()); + + data = data.sliced(data.size() / 2); + model.setStringList(data); +} + QT_BEGIN_NAMESPACE extern bool Q_WIDGETS_EXPORT qt_tab_all_widgets(); // qapplication.cpp QT_END_NAMESPACE @@ -3685,7 +3828,6 @@ void tst_QTableView::tabFocus() QLineEdit *edit = new QLineEdit(&window); window.show(); - QApplication::setActiveWindow(&window); window.setFocus(); window.activateWindow(); QVERIFY(QTest::qWaitForWindowActive(&window)); @@ -4225,15 +4367,18 @@ void tst_QTableView::task191545_dragSelectRows() QHeaderView *vHeader = table.verticalHeader(); QWidget *vHeaderVp = vHeader->viewport(); QPoint rowPos(cellRect.center()); - QMouseEvent rowPressEvent(QEvent::MouseButtonPress, rowPos, Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); + QMouseEvent rowPressEvent(QEvent::MouseButtonPress, rowPos, rowPos, vHeaderVp->mapToGlobal(rowPos), + Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); QCoreApplication::sendEvent(vHeaderVp, &rowPressEvent); for (int i = 0; i < 4; ++i) { rowPos.setY(rowPos.y() + cellRect.height()); - QMouseEvent moveEvent(QEvent::MouseMove, rowPos, Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); + QMouseEvent moveEvent(QEvent::MouseMove, rowPos, rowPos, vHeaderVp->mapToGlobal(rowPos), + Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); QCoreApplication::sendEvent(vHeaderVp, &moveEvent); } - QMouseEvent rowReleaseEvent(QEvent::MouseButtonRelease, rowPos, Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); + QMouseEvent rowReleaseEvent(QEvent::MouseButtonRelease, rowPos, vHeaderVp->mapToGlobal(rowPos), + Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); QCoreApplication::sendEvent(vHeaderVp, &rowReleaseEvent); for (int i = 0; i < 4; ++i) { @@ -4247,15 +4392,18 @@ void tst_QTableView::task191545_dragSelectRows() QHeaderView *hHeader = table.horizontalHeader(); QWidget *hHeaderVp = hHeader->viewport(); QPoint colPos((cellRect.left() + cellRect.right()) / 2, 5); - QMouseEvent colPressEvent(QEvent::MouseButtonPress, colPos, Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); + QMouseEvent colPressEvent(QEvent::MouseButtonPress, colPos, hHeaderVp->mapToGlobal(colPos), + Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); QCoreApplication::sendEvent(hHeaderVp, &colPressEvent); for (int i = 0; i < 4; ++i) { colPos.setX(colPos.x() + cellRect.width()); - QMouseEvent moveEvent(QEvent::MouseMove, colPos, Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); + QMouseEvent moveEvent(QEvent::MouseMove, colPos, hHeaderVp->mapToGlobal(colPos), + Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); QCoreApplication::sendEvent(hHeaderVp, &moveEvent); } - QMouseEvent colReleaseEvent(QEvent::MouseButtonRelease, colPos, Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); + QMouseEvent colReleaseEvent(QEvent::MouseButtonRelease, colPos, hHeaderVp->mapToGlobal(colPos), + Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); QCoreApplication::sendEvent(hHeaderVp, &colReleaseEvent); for (int i = 0; i < 4; ++i) { @@ -4268,16 +4416,19 @@ void tst_QTableView::task191545_dragSelectRows() QRect cellRect = table.visualRect(model.index(2, 2)); QWidget *tableVp = table.viewport(); QPoint cellPos = cellRect.center(); - QMouseEvent cellPressEvent(QEvent::MouseButtonPress, cellPos, Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); + QMouseEvent cellPressEvent(QEvent::MouseButtonPress, cellPos, tableVp->mapToGlobal(cellPos), + Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); QCoreApplication::sendEvent(tableVp, &cellPressEvent); for (int i = 0; i < 6; ++i) { cellPos.setX(cellPos.x() + cellRect.width()); cellPos.setY(cellPos.y() + cellRect.height()); - QMouseEvent moveEvent(QEvent::MouseMove, cellPos, Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); + QMouseEvent moveEvent(QEvent::MouseMove, cellPos, tableVp->mapToGlobal(cellPos), + Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); QCoreApplication::sendEvent(tableVp, &moveEvent); } - QMouseEvent cellReleaseEvent(QEvent::MouseButtonRelease, cellPos, Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); + QMouseEvent cellReleaseEvent(QEvent::MouseButtonRelease, cellPos, tableVp->mapToGlobal(cellPos), + Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); QCoreApplication::sendEvent(tableVp, &cellReleaseEvent); for (int i = 0; i < 6; ++i) { @@ -4292,16 +4443,21 @@ void tst_QTableView::task191545_dragSelectRows() QRect cellRect = table.visualRect(model.index(3, 3)); QWidget *tableVp = table.viewport(); QPoint cellPos = cellRect.center(); - QMouseEvent cellPressEvent(QEvent::MouseButtonPress, cellPos, Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); + QMouseEvent cellPressEvent(QEvent::MouseButtonPress, cellPos, tableVp->mapToGlobal(cellPos), + Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); QCoreApplication::sendEvent(tableVp, &cellPressEvent); for (int i = 0; i < 6; ++i) { + // cellPos might have been updated by scrolling, so refresh + cellPos = table.visualRect(model.index(3+i, 3+i)).center(); cellPos.setX(cellPos.x() + cellRect.width()); cellPos.setY(cellPos.y() + cellRect.height()); - QMouseEvent moveEvent(QEvent::MouseMove, cellPos, Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); + QMouseEvent moveEvent(QEvent::MouseMove, cellPos, tableVp->mapToGlobal(cellPos), + Qt::NoButton, Qt::LeftButton, Qt::ControlModifier); QCoreApplication::sendEvent(tableVp, &moveEvent); } - QMouseEvent cellReleaseEvent(QEvent::MouseButtonRelease, cellPos, Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); + QMouseEvent cellReleaseEvent(QEvent::MouseButtonRelease, cellPos, tableVp->mapToGlobal(cellPos), + Qt::LeftButton, Qt::NoButton, Qt::ControlModifier); QCoreApplication::sendEvent(tableVp, &cellReleaseEvent); QTest::qWait(200); @@ -4523,7 +4679,8 @@ void tst_QTableView::taskQTBUG_10169_sizeHintForRow() void tst_QTableView::viewOptions() { QtTestTableView view; - QStyleOptionViewItem options = view.viewOptions(); + QStyleOptionViewItem options; + view.initViewItemOption(&options); QVERIFY(options.showDecorationSelected); } @@ -4656,6 +4813,147 @@ void tst_QTableView::deselectRow() QVERIFY(!tw.selectionModel()->isRowSelected(1, QModelIndex())); } +class QTableViewSelectCells : public QTableView +{ +public: + QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex &index, + const QEvent *) const override + { + return QTableView::selectionCommand(index, shiftPressed ? &mouseEvent : nullptr); + } + QMouseEvent mouseEvent = QMouseEvent(QEvent::MouseButtonPress, QPointF(), QPointF(), + Qt::LeftButton, Qt::LeftButton, Qt::ShiftModifier); + bool shiftPressed = false; +}; + +void tst_QTableView::selectRowsAndCells() +{ + const auto checkRows = [](const QModelIndexList &mil) + { + QCOMPARE(mil.size(), 3); + for (const auto &mi : mil) + QVERIFY(mi.row() >= 1 && mi.row() <= 3); + }; + QTableViewSelectCells tw; + QtTestTableModel model(5, 1); + tw.setSelectionBehavior(QAbstractItemView::SelectRows); + tw.setSelectionMode(QAbstractItemView::ExtendedSelection); + tw.setModel(&model); + tw.show(); + + tw.selectRow(1); + tw.shiftPressed = true; + tw.selectRow(2); + tw.shiftPressed = false; + QTest::mouseClick(tw.viewport(), Qt::LeftButton, Qt::ShiftModifier, tw.visualRect(model.index(3, 0)).center()); + checkRows(tw.selectionModel()->selectedRows()); + + tw.clearSelection(); + QTest::mouseClick(tw.viewport(), Qt::LeftButton, Qt::NoModifier, tw.visualRect(model.index(3, 0)).center()); + tw.shiftPressed = true; + tw.selectRow(1); + checkRows(tw.selectionModel()->selectedRows()); +} + +void tst_QTableView::selectColumnsAndCells() +{ + const auto checkColumns = [](const QModelIndexList &mil) + { + QCOMPARE(mil.size(), 3); + for (const auto &mi : mil) + QVERIFY(mi.column() >= 1 && mi.column() <= 3); + }; + QTableViewSelectCells tw; + QtTestTableModel model(1, 5); + tw.setSelectionBehavior(QAbstractItemView::SelectColumns); + tw.setSelectionMode(QAbstractItemView::ExtendedSelection); + tw.setModel(&model); + tw.show(); + + tw.selectColumn(1); + tw.shiftPressed = true; + tw.selectColumn(2); + tw.shiftPressed = false; + QTest::mouseClick(tw.viewport(), Qt::LeftButton, Qt::ShiftModifier, tw.visualRect(model.index(0, 3)).center()); + checkColumns(tw.selectionModel()->selectedColumns()); + + tw.clearSelection(); + QTest::mouseClick(tw.viewport(), Qt::LeftButton, Qt::NoModifier, tw.visualRect(model.index(0, 3)).center()); + tw.shiftPressed = true; + tw.selectColumn(1); + checkColumns(tw.selectionModel()->selectedColumns()); +} + +void tst_QTableView::selectWithHeader_data() +{ + QTest::addColumn<Qt::Orientation>("orientation"); + + QTest::addRow("horizontal") << Qt::Horizontal; + QTest::addRow("vertical") << Qt::Vertical; +} + +void tst_QTableView::selectWithHeader() +{ + QFETCH(Qt::Orientation, orientation); + + QTableWidget view(10, 10); + view.resize(200, 100); + view.show(); + + QVERIFY(QTest::qWaitForWindowExposed(&view)); + + QHeaderView *header = nullptr; + QPoint clickPos; + QModelIndex lastIndex; + + switch (orientation) { + case Qt::Horizontal: + header = view.horizontalHeader(); + clickPos.rx() = header->sectionPosition(0) + header->sectionSize(0) / 2; + clickPos.ry() = header->height() / 2; + lastIndex = view.model()->index(9, 0); + break; + case Qt::Vertical: + header = view.verticalHeader(); + clickPos.rx() = header->width() / 2; + clickPos.ry() = header->sectionPosition(0) + header->sectionSize(0) / 2; + lastIndex = view.model()->index(0, 9); + break; + } + + const auto isSelected = [&]{ + return orientation == Qt::Horizontal + ? view.selectionModel()->isColumnSelected(0) + : view.selectionModel()->isRowSelected(0); + }; + + QTest::mouseClick(header->viewport(), Qt::LeftButton, {}, clickPos); + QVERIFY(isSelected()); + QTest::mouseClick(header->viewport(), Qt::LeftButton, Qt::ControlModifier, clickPos); + QVERIFY(!isSelected()); + QTest::mouseClick(header->viewport(), Qt::LeftButton, {}, clickPos); + QVERIFY(isSelected()); + view.scrollTo(lastIndex); + QTest::mouseClick(header->viewport(), Qt::LeftButton, Qt::ControlModifier, clickPos); + QVERIFY(!isSelected()); +} + +void tst_QTableView::resetDefaultSectionSize() +{ + // Create a table and change its default section size and then reset it. + // This should be a no op so clicking on row 1 should select row 1 and not row + // 0 as previously. QTBUG-116013 + QTableWidget view(10, 10); + view.resize(300, 300); + view.verticalHeader()->setSectionResizeMode(QHeaderView::Fixed); + view.verticalHeader()->setDefaultSectionSize(120); + view.verticalHeader()->resetDefaultSectionSize(); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + QEXPECT_FAIL("", "Reverted fix for QTBUG-116013 due to QTBUG-122109", Continue); + QCOMPARE(view.verticalHeader()->logicalIndexAt(9, 45), 1); +} + // This has nothing to do with QTableView, but it's convenient to reuse the QtTestTableModel #if QT_CONFIG(textmarkdownwriter) @@ -4684,5 +4982,18 @@ void tst_QTableView::markdownWriter() } #endif +void tst_QTableView::rowsInVerticalHeader() +{ + QtTestTableModel model(0, 2); + QTableView view; + view.setModel(&model); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + auto *verticalHeader = view.verticalHeader(); + QCOMPARE(verticalHeader->count(), 0); + model.insertRows(2); + QCOMPARE(verticalHeader->count(), 2); +} + QTEST_MAIN(tst_QTableView) #include "tst_qtableview.moc" diff --git a/tests/auto/widgets/itemviews/qtablewidget/CMakeLists.txt b/tests/auto/widgets/itemviews/qtablewidget/CMakeLists.txt index cdb393484a..306ba4bdff 100644 --- a/tests/auto/widgets/itemviews/qtablewidget/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qtablewidget/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qtablewidget.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtablewidget Test: ##################################################################### -qt_add_test(tst_qtablewidget +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtablewidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtablewidget SOURCES tst_qtablewidget.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/itemviews/qtablewidget/qtablewidget.pro b/tests/auto/widgets/itemviews/qtablewidget/qtablewidget.pro deleted file mode 100644 index 114ce115eb..0000000000 --- a/tests/auto/widgets/itemviews/qtablewidget/qtablewidget.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qtablewidget -QT += widgets testlib -SOURCES += tst_qtablewidget.cpp diff --git a/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp b/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp index 345d048093..34972bc3e8 100644 --- a/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp +++ b/tests/auto/widgets/itemviews/qtablewidget/tst_qtablewidget.cpp @@ -1,34 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QHeaderView> #include <QLineEdit> #include <QMimeData> +#include <QScrollBar> #include <QSignalSpy> #include <QTableWidget> #include <QTest> @@ -49,6 +25,7 @@ private slots: void initTestCase(); void init(); void getSetCheck(); + void selectionRange(); void clear(); void clearContents(); void rowCount(); @@ -60,6 +37,8 @@ private slots: void takeItem(); void selectedItems_data(); void selectedItems(); + void selectedSpannedCells_data(); + void selectedSpannedCells(); void removeRow_data(); void removeRow(); void removeColumn_data(); @@ -85,12 +64,15 @@ private slots: void task219380_removeLastRow(); void task262056_sortDuplicate(); void itemWithHeaderItems(); + void checkHeaderItemFlagsConflict(); void mimeData(); void selectedRowAfterSorting(); void search(); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void clearItemData(); #endif + void createPersistentOnLayoutAboutToBeChanged(); + void createPersistentOnLayoutAboutToBeChangedAutoSort(); private: std::unique_ptr<QTableWidget> testWidget; @@ -153,6 +135,19 @@ void tst_QTableWidget::getSetCheck() QCOMPARE(obj1.itemPrototype(), nullptr); } +void tst_QTableWidget::selectionRange() +{ + QTableWidgetSelectionRange defaultSelection; + QTableWidgetSelectionRange selection(1, 2, 3, 4); + + QTableWidgetSelectionRange copy(selection); + QCOMPARE(copy, selection); + QVERIFY(copy != defaultSelection); + + defaultSelection = copy; + QCOMPARE(defaultSelection, copy); +} + void tst_QTableWidget::initTestCase() { testWidget.reset(new QTableWidget); @@ -199,8 +194,8 @@ void tst_QTableWidget::clear() QVERIFY(bla.isNull()); QVERIFY(!testWidget->item(0,0)); - QVERIFY(!testWidget->selectedRanges().count()); - QVERIFY(!testWidget->selectedItems().count()); + QVERIFY(!testWidget->selectedRanges().size()); + QVERIFY(!testWidget->selectedItems().size()); } void tst_QTableWidget::rowCount() @@ -367,7 +362,7 @@ void tst_QTableWidget::takeItem() QCOMPARE(item->text(), QString::number(row * column + column)); delete item; - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); const QList<QVariant> arguments = spy.takeFirst(); QCOMPARE(arguments.size(), 2); QCOMPARE(arguments.at(0).toInt(), row); @@ -532,24 +527,24 @@ void tst_QTableWidget::selectedItems() testWidget->setColumnHidden(column, true); // make sure we don't have any previous selections hanging around - QVERIFY(!testWidget->selectedRanges().count()); - QVERIFY(!testWidget->selectedItems().count()); + QVERIFY(!testWidget->selectedRanges().size()); + QVERIFY(!testWidget->selectedItems().size()); // select range and check that it is set correctly testWidget->setRangeSelected(selectionRange, true); if (selectionRange.topRow() >= 0) { - QCOMPARE(testWidget->selectedRanges().count(), 1); + QCOMPARE(testWidget->selectedRanges().size(), 1); QCOMPARE(testWidget->selectedRanges().at(0).topRow(), selectionRange.topRow()); QCOMPARE(testWidget->selectedRanges().at(0).bottomRow(), selectionRange.bottomRow()); QCOMPARE(testWidget->selectedRanges().at(0).leftColumn(), selectionRange.leftColumn()); QCOMPARE(testWidget->selectedRanges().at(0).rightColumn(), selectionRange.rightColumn()); } else { - QCOMPARE(testWidget->selectedRanges().count(), 0); + QCOMPARE(testWidget->selectedRanges().size(), 0); } // check that the correct number of items and the expected items are there const QList<QTableWidgetItem *> selectedItems = testWidget->selectedItems(); - QCOMPARE(selectedItems.count(), expectedItems.count()); + QCOMPARE(selectedItems.size(), expectedItems.size()); for (const auto &intPair : expectedItems) QVERIFY(selectedItems.contains(testWidget->item(intPair.first, intPair.second))); @@ -569,6 +564,75 @@ void tst_QTableWidget::selectedItems() } } +void tst_QTableWidget::selectedSpannedCells_data() +{ + QTest::addColumn<QRect>("spannedCells"); // in cells, not pixels + QTest::addColumn<QPoint>("selectionStartCell"); + QTest::addColumn<QPoint>("selectionEndCell"); + QTest::addColumn<int>("expectedSelectionRangeCount"); + QTest::addColumn<QTableWidgetSelectionRange>("expectedFirstSelectionRange"); + + QTest::newRow("merge 2 cells in column, select adjacent left") + << QRect(1, 2, 1, 2) << QPoint(0, 1) << QPoint(0, 3) + << 1 << QTableWidgetSelectionRange(1, 0, 3, 0); + + QTest::newRow("merge 2 cells in column, select those and one more") + << QRect(1, 2, 1, 2) << QPoint(1, 1) << QPoint(1, 3) + << 3 << QTableWidgetSelectionRange(1, 1, 1, 1); + + QTest::newRow("merge 2 cells in column, select rows above") + << QRect(1, 2, 1, 2) << QPoint(0, 0) << QPoint(3, 1) + << 1 << QTableWidgetSelectionRange(0, 0, 1, 3); + + QTest::newRow("merge 4 cells in column, select adjacent right") + << QRect(1, 0, 1, 4) << QPoint(2, 0) << QPoint(3, 3) + << 1 << QTableWidgetSelectionRange(0, 2, 3, 3); + + QTest::newRow("merge 3 cells in row, select those and one more") + << QRect(0, 1, 3, 1) << QPoint(0, 1) << QPoint(3, 1) + << 4 << QTableWidgetSelectionRange(1, 0, 1, 0); + + QTest::newRow("merge 3 cells in row, select adjacent to right") + << QRect(0, 1, 3, 1) << QPoint(3, 0) << QPoint(3, 2) + << 1 << QTableWidgetSelectionRange(0, 3, 2, 3); + + QTest::newRow("merge 3 cells in row, select adjacent above") + << QRect(0, 2, 3, 2) << QPoint(0, 1) << QPoint(2, 1) + << 1 << QTableWidgetSelectionRange(1, 0, 1, 2); +} + +void tst_QTableWidget::selectedSpannedCells() // QTBUG-255 +{ + QFETCH(QRect, spannedCells); + QFETCH(QPoint, selectionStartCell); + QFETCH(QPoint, selectionEndCell); + QFETCH(int, expectedSelectionRangeCount); + QFETCH(const QTableWidgetSelectionRange, expectedFirstSelectionRange); + + QTableWidget testWidget(4, 4); + testWidget.resize(600, 200); + testWidget.show(); + + // create and set items + for (int c = 0; c < 4; ++c) { + for (int r = 0; r < 4; ++r) + testWidget.setItem(r, c, new QTableWidgetItem(QString("Item %1 %2").arg(c).arg(r))); + } + + // merge some cells + testWidget.setSpan(spannedCells.top(), spannedCells.left(), spannedCells.height(), spannedCells.width()); + + // click one cell and shift-click another, to select a range + QTest::mouseClick(testWidget.viewport(), Qt::LeftButton, Qt::NoModifier, + testWidget.visualRect(testWidget.model()->index(selectionStartCell.y(), selectionStartCell.x())).center()); + QTest::mouseClick(testWidget.viewport(), Qt::LeftButton, Qt::ShiftModifier, + testWidget.visualRect(testWidget.model()->index(selectionEndCell.y(), selectionEndCell.x())).center()); + + auto ranges = testWidget.selectedRanges(); + QCOMPARE(ranges.size(), expectedSelectionRangeCount); + QCOMPARE(ranges.first(), expectedFirstSelectionRange); +} + void tst_QTableWidget::removeRow_data() { QTest::addColumn<int>("rowCount"); @@ -1132,15 +1196,15 @@ void tst_QTableWidget::sortItems() persistent << model->index(r, sortColumn, QModelIndex()); } - for (int h = 0; h < initialHidden.count(); ++h) + for (int h = 0; h < initialHidden.size(); ++h) testWidget->hideRow(initialHidden.at(h)); - QCOMPARE(testWidget->verticalHeader()->hiddenSectionCount(), initialHidden.count()); + QCOMPARE(testWidget->verticalHeader()->hiddenSectionCount(), initialHidden.size()); testWidget->sortItems(sortColumn, sortOrder); int te = 0; - for (int i = 0; i < rows.count(); ++i) { + for (int i = 0; i < rows.size(); ++i) { for (int j = 0; j < columnCount; ++j) { QString value; QTableWidgetItem *itm = testWidget->item(i, j); @@ -1154,7 +1218,7 @@ void tst_QTableWidget::sortItems() // << "expected" << rows.at(i); } - for (int k = 0; k < expectedHidden.count(); ++k) + for (int k = 0; k < expectedHidden.size(); ++k) QVERIFY(testWidget->isRowHidden(expectedHidden.at(k))); } @@ -1335,18 +1399,18 @@ void tst_QTableWidget::setItemWithSorting() } } - for (int k = 0; k < persistent.count(); ++k) { + for (int k = 0; k < persistent.size(); ++k) { QCOMPARE(persistent.at(k).row(), expectedRows.at(k)); int i = (persistent.at(k).row() * columnCount) + sortColumn; QCOMPARE(persistent.at(k).data().toString(), expectedValues.at(i)); } if (i == 0) - QCOMPARE(dataChangedSpy.count(), reorderingExpected ? 0 : 1); + QCOMPARE(dataChangedSpy.size(), reorderingExpected ? 0 : 1); else - QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.size(), 1); - QCOMPARE(layoutChangedSpy.count(), reorderingExpected ? 1 : 0); + QCOMPARE(layoutChangedSpy.size(), reorderingExpected ? 1 : 0); } } @@ -1382,7 +1446,7 @@ void tst_QTableWidget::itemData() QCOMPARE(widget.currentRoles, QList<int> { Qt::UserRole + i }); } QMap<int, QVariant> flags = widget.model()->itemData(widget.model()->index(0, 0)); - QCOMPARE(flags.count(), 6); + QCOMPARE(flags.size(), 6); for (int i = 0; i < 4; ++i) QCOMPARE(flags[Qt::UserRole + i].toString(), QString::number(i + 1)); } @@ -1395,7 +1459,7 @@ void tst_QTableWidget::setItemData() QTableWidgetItem *item = new QTableWidgetItem; table.setItem(0, 0, item); - QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.size(), 1); QModelIndex idx = qvariant_cast<QModelIndex>(dataChangedSpy.takeFirst().at(0)); QMap<int, QVariant> data; @@ -1407,7 +1471,7 @@ void tst_QTableWidget::setItemData() QCOMPARE(table.model()->data(idx, Qt::DisplayRole).toString(), QLatin1String("Display")); QCOMPARE(table.model()->data(idx, Qt::EditRole).toString(), QLatin1String("Display")); QCOMPARE(table.model()->data(idx, Qt::ToolTipRole).toString(), QLatin1String("ToolTip")); - QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.size(), 1); QCOMPARE(idx, qvariant_cast<QModelIndex>(dataChangedSpy.first().at(0))); QCOMPARE(idx, qvariant_cast<QModelIndex>(dataChangedSpy.first().at(1))); const auto roles = qvariant_cast<QList<int>>(dataChangedSpy.first().at(2)); @@ -1418,13 +1482,13 @@ void tst_QTableWidget::setItemData() dataChangedSpy.clear(); table.model()->setItemData(idx, data); - QCOMPARE(dataChangedSpy.count(), 0); + QCOMPARE(dataChangedSpy.size(), 0); data.clear(); data.insert(Qt::DisplayRole, QLatin1String("dizplaye")); table.model()->setItemData(idx, data); QCOMPARE(table.model()->data(idx, Qt::DisplayRole).toString(), QLatin1String("dizplaye")); - QCOMPARE(dataChangedSpy.count(), 1); + QCOMPARE(dataChangedSpy.size(), 1); QCOMPARE(QList<int>({ Qt::DisplayRole, Qt::EditRole }), qvariant_cast<QList<int>>(dataChangedSpy.first().at(2))); @@ -1493,6 +1557,12 @@ void tst_QTableWidget::sizeHint() QFETCH(Qt::ScrollBarPolicy, scrollBarPolicy); QFETCH(QSize, viewSize); + const QString defaultStyle = QApplication::style()->name(); + QApplication::setStyle("windows"); + const auto resetStyle = qScopeGuard([defaultStyle]{ + QApplication::setStyle(defaultStyle); + }); + QTableWidget view(2, 2); view.setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); view.setVerticalScrollBarPolicy(scrollBarPolicy); @@ -1512,18 +1582,21 @@ void tst_QTableWidget::sizeHint() QTRY_COMPARE(view.size(), viewSize); } + QApplication::processEvents(); // execute delayed layouts auto sizeHint = view.sizeHint(); view.hide(); QCOMPARE(view.sizeHint(), sizeHint); view.horizontalHeader()->hide(); view.show(); + QApplication::processEvents(); // execute delayed layouts sizeHint = view.sizeHint(); view.hide(); QCOMPARE(view.sizeHint(), sizeHint); view.verticalHeader()->hide(); view.show(); + QApplication::processEvents(); // execute delayed layouts sizeHint = view.sizeHint(); view.hide(); QCOMPARE(view.sizeHint(), sizeHint); @@ -1587,7 +1660,7 @@ void tst_QTableWidget::task262056_sortDuplicate() QSignalSpy layoutChangedSpy(testWidget->model(), &QAbstractItemModel::layoutChanged); testWidget->item(3,0)->setBackground(Qt::red); - QCOMPARE(layoutChangedSpy.count(),0); + QCOMPARE(layoutChangedSpy.size(),0); } @@ -1607,6 +1680,25 @@ void tst_QTableWidget::itemWithHeaderItems() QCOMPARE(table.item(0, 1), nullptr); } +void tst_QTableWidget::checkHeaderItemFlagsConflict() +{ + // QTBUG-113209 + // Check that setting header item doesn't set Qt::ItemNeverHasChildren + // Chech that header items do not emit itemChanged. + QTableWidget table(1, 1); + QSignalSpy itemChangeSpy(&table, &QTableWidget::itemChanged); + QVERIFY(itemChangeSpy.isValid()); + + QTableWidgetItem *item = new QTableWidgetItem("Initial"); + table.setHorizontalHeaderItem(0, item); + + QVERIFY(!(item->flags() & Qt::ItemNeverHasChildren)); + + item->setData(Qt::DisplayRole, "updated"); + + QCOMPARE(itemChangeSpy.size(), 0); +} + class TestTableWidget : public QTableWidget { Q_OBJECT @@ -1684,7 +1776,7 @@ void tst_QTableWidget::selectedRowAfterSorting() table.setProperty("sortingEnabled",true); table.selectRow(1); table.item(1,1)->setText("9"); - QCOMPARE(table.selectedItems().count(),3); + QCOMPARE(table.selectedItems().size(),3); const auto selectedItems = table.selectedItems(); for (QTableWidgetItem *item : selectedItems) QCOMPARE(item->row(), 0); @@ -1699,9 +1791,13 @@ void tst_QTableWidget::search() return item; }; - auto checkSeries = [](TestTableWidget &tw, const QList<QPair<QKeyEvent, int>> &series) { + struct KeyPress { + Qt::Key key; + QString text; + }; + auto checkSeries = [](TestTableWidget &tw, const QList<QPair<KeyPress, int>> &series) { for (const auto &p : series) { - QKeyEvent e = p.first; + QKeyEvent e(QEvent::KeyPress, p.first.key, Qt::NoModifier, p.first.text); tw.keyPressEvent(&e); QVERIFY(tw.selectionModel()->isSelected(tw.model()->index(p.second, 0))); } @@ -1714,12 +1810,12 @@ void tst_QTableWidget::search() tw.setItem(4, 0, createItem(" ")); tw.show(); - QKeyEvent evSpace(QEvent::KeyPress, Qt::Key_Space, Qt::NoModifier, " "); - QKeyEvent ev1(QEvent::KeyPress, Qt::Key_1, Qt::NoModifier, "1"); - QKeyEvent ev2(QEvent::KeyPress, Qt::Key_2, Qt::NoModifier, "2"); - QKeyEvent ev3(QEvent::KeyPress, Qt::Key_3, Qt::NoModifier, "3"); - QKeyEvent ev4(QEvent::KeyPress, Qt::Key_4, Qt::NoModifier, "4"); - QKeyEvent ev5(QEvent::KeyPress, Qt::Key_5, Qt::NoModifier, "5"); + KeyPress evSpace{Qt::Key_Space, " "}; + KeyPress ev1{Qt::Key_1, "1"}; + KeyPress ev2{Qt::Key_2, "2"}; + KeyPress ev3{Qt::Key_3, "3"}; + KeyPress ev4{Qt::Key_4, "4"}; + KeyPress ev5{Qt::Key_5, "5"}; checkSeries(tw, {{evSpace, 4}, {ev1, 4}}); QTest::qWait(QApplication::keyboardInputInterval() * 2); @@ -1752,5 +1848,74 @@ void tst_QTableWidget::clearItemData() } #endif +void tst_QTableWidget::createPersistentOnLayoutAboutToBeChanged() // QTBUG-93466 +{ + QTableWidget widget; + widget.model()->insertColumn(0); + QCOMPARE(widget.model()->columnCount(), 1); + widget.model()->insertRows(0, 3); + for (int row = 0; row < 3; ++row) + widget.model()->setData(widget.model()->index(row, 0), row); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(widget.model(), &QAbstractItemModel::layoutChanged); + connect(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &widget](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(widget.model()->index(row, 0)); + }); + connect(widget.model(), &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 1); + QCOMPARE(idxList.at(0).column(), 0); + QCOMPARE(idxList.at(0).data().toInt(), 0); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data().toInt(), -1); + QCOMPARE(idxList.at(2).row(), 2); + QCOMPARE(idxList.at(2).column(), 0); + QCOMPARE(idxList.at(2).data().toInt(), 2); + }); + widget.model()->setData(widget.model()->index(1, 0), -1); + widget.model()->sort(0); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + +void tst_QTableWidget::createPersistentOnLayoutAboutToBeChangedAutoSort() // QTBUG-93466 +{ + QTableWidget widget; + widget.model()->insertColumn(0); + QCOMPARE(widget.model()->columnCount(), 1); + widget.model()->insertRows(0, 3); + for (int row = 0; row < 3; ++row) + widget.model()->setData(widget.model()->index(row, 0), row); + widget.sortByColumn(0, Qt::AscendingOrder); + widget.setSortingEnabled(true); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(widget.model(), &QAbstractItemModel::layoutChanged); + connect(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &widget](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(widget.model()->index(row, 0)); + }); + connect(widget.model(), &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 1); + QCOMPARE(idxList.at(0).column(), 0); + QCOMPARE(idxList.at(0).data().toInt(), 0); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data().toInt(), -1); + QCOMPARE(idxList.at(2).row(), 2); + QCOMPARE(idxList.at(2).column(), 0); + QCOMPARE(idxList.at(2).data().toInt(), 2); + }); + widget.model()->setData(widget.model()->index(1, 0), -1); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + QTEST_MAIN(tst_QTableWidget) #include "tst_qtablewidget.moc" diff --git a/tests/auto/widgets/itemviews/qtreeview/CMakeLists.txt b/tests/auto/widgets/itemviews/qtreeview/CMakeLists.txt index a6c5adb967..deccd71f59 100644 --- a/tests/auto/widgets/itemviews/qtreeview/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qtreeview/CMakeLists.txt @@ -1,14 +1,21 @@ -# Generated from qtreeview.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtreeview Test: ##################################################################### -qt_add_test(tst_qtreeview +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtreeview LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtreeview SOURCES ../../../../shared/fakedirmodel.h tst_qtreeview.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::CorePrivate Qt::Gui Qt::GuiPrivate diff --git a/tests/auto/widgets/itemviews/qtreeview/qtreeview.pro b/tests/auto/widgets/itemviews/qtreeview/qtreeview.pro deleted file mode 100644 index 2530b44935..0000000000 --- a/tests/auto/widgets/itemviews/qtreeview/qtreeview.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_qtreeview -QT += widgets testlib -QT += widgets-private gui-private core-private testlib-private -SOURCES += tst_qtreeview.cpp -HEADERS += ../../../../shared/fakedirmodel.h diff --git a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp index 0c9c3dc3ad..9b02b0e80d 100644 --- a/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp +++ b/tests/auto/widgets/itemviews/qtreeview/tst_qtreeview.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include "../../../../shared/fakedirmodel.h" @@ -49,6 +24,8 @@ #include <private/qtreeview_p.h> #include <private/qtesthelpers_p.h> +#include <QtWidgets/private/qapplication_p.h> + using namespace QTestPrivate; #if QT_CONFIG(draganddrop) @@ -250,7 +227,10 @@ private slots: void taskQTBUG_7232_AllowUserToControlSingleStep(); void taskQTBUG_8376(); void taskQTBUG_61476(); + void taskQTBUG_42469_crash(); void testInitialFocus(); + void fetchUntilScreenFull(); + void expandAfterTake(); }; class QtTestModel: public QAbstractItemModel @@ -887,8 +867,8 @@ void tst_QTreeView::horizontalScrollMode() QTreeView view; setFrameless(&view); view.setModel(&model); - view.setFixedSize(100, 100); - view.header()->resizeSection(0, 200); + view.setFixedSize(100, 1000); + view.header()->resizeSection(0, 2000); view.show(); QCOMPARE(view.horizontalScrollMode(), QAbstractItemView::ScrollPerPixel); @@ -1069,7 +1049,7 @@ void tst_QTreeView::itemDelegateForColumnOrRow() QVERIFY(!view.itemDelegateForRow(0)); QVERIFY(!view.itemDelegateForColumn(0)); - QCOMPARE(view.itemDelegate(QModelIndex()), defaultDelegate); + QCOMPARE(view.itemDelegateForIndex(QModelIndex()), defaultDelegate); QStandardItemModel model; for (int i = 0; i < 100; ++i) { @@ -1085,26 +1065,26 @@ void tst_QTreeView::itemDelegateForColumnOrRow() QVERIFY(!view.itemDelegateForRow(0)); QVERIFY(!view.itemDelegateForColumn(0)); - QCOMPARE(view.itemDelegate(QModelIndex()), defaultDelegate); - QCOMPARE(view.itemDelegate(view.model()->index(0, 0)), defaultDelegate); + QCOMPARE(view.itemDelegateForIndex(QModelIndex()), defaultDelegate); + QCOMPARE(view.itemDelegateForIndex(view.model()->index(0, 0)), defaultDelegate); QPointer<QAbstractItemDelegate> rowDelegate = new QStyledItemDelegate; view.setItemDelegateForRow(0, rowDelegate); QVERIFY(!rowDelegate->parent()); QCOMPARE(view.itemDelegateForRow(0), rowDelegate); - QCOMPARE(view.itemDelegate(view.model()->index(0, 0)), rowDelegate); - QCOMPARE(view.itemDelegate(view.model()->index(0, 1)), rowDelegate); - QCOMPARE(view.itemDelegate(view.model()->index(1, 0)), defaultDelegate); - QCOMPARE(view.itemDelegate(view.model()->index(1, 1)), defaultDelegate); + QCOMPARE(view.itemDelegateForIndex(view.model()->index(0, 0)), rowDelegate); + QCOMPARE(view.itemDelegateForIndex(view.model()->index(0, 1)), rowDelegate); + QCOMPARE(view.itemDelegateForIndex(view.model()->index(1, 0)), defaultDelegate); + QCOMPARE(view.itemDelegateForIndex(view.model()->index(1, 1)), defaultDelegate); QPointer<QAbstractItemDelegate> columnDelegate = new QStyledItemDelegate; view.setItemDelegateForColumn(1, columnDelegate); QVERIFY(!columnDelegate->parent()); QCOMPARE(view.itemDelegateForColumn(1), columnDelegate); - QCOMPARE(view.itemDelegate(view.model()->index(0, 0)), rowDelegate); - QCOMPARE(view.itemDelegate(view.model()->index(0, 1)), rowDelegate); // row wins - QCOMPARE(view.itemDelegate(view.model()->index(1, 0)), defaultDelegate); - QCOMPARE(view.itemDelegate(view.model()->index(1, 1)), columnDelegate); + QCOMPARE(view.itemDelegateForIndex(view.model()->index(0, 0)), rowDelegate); + QCOMPARE(view.itemDelegateForIndex(view.model()->index(0, 1)), rowDelegate); // row wins + QCOMPARE(view.itemDelegateForIndex(view.model()->index(1, 0)), defaultDelegate); + QCOMPARE(view.itemDelegateForIndex(view.model()->index(1, 1)), columnDelegate); view.setItemDelegateForRow(0, nullptr); QVERIFY(!view.itemDelegateForRow(0)); @@ -1260,7 +1240,6 @@ void tst_QTreeView::keyboardSearchMultiColumn() view.setModel(&model); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); view.setCurrentIndex(model.index(0, 1)); @@ -1301,7 +1280,7 @@ void tst_QTreeView::setModel() QCOMPARE(view.header()->model(), model); QCOMPARE(view.selectionModel() != oldSelectionModel, (i == 0)); } - QTRY_COMPARE(modelDestroyedSpy.count(), 0); + QTRY_COMPARE(modelDestroyedSpy.size(), 0); view.setModel(nullptr); QCOMPARE(view.model(), nullptr); @@ -1356,7 +1335,7 @@ void tst_QTreeView::setHeader() Qt::Orientation orient = x ? Qt::Vertical : Qt::Horizontal; QHeaderView *head = new QHeaderView(orient); view.setHeader(head); - QCOMPARE(destroyedSpy.count(), 1); + QCOMPARE(destroyedSpy.size(), 1); QCOMPARE(head->parent(), &view); QCOMPARE(view.header(), head); view.setHeader(head); @@ -1539,10 +1518,10 @@ void tst_QTreeView::limitedExpand() QVERIFY(spy.isValid()); view.expand(model.index(0, 0)); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); view.expand(model.index(1, 0)); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } { QStringListModel model(QStringList() << "one" << "two"); @@ -1553,9 +1532,9 @@ void tst_QTreeView::limitedExpand() QVERIFY(spy.isValid()); view.expand(model.index(0, 0)); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); view.expandAll(); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } } @@ -1594,58 +1573,58 @@ void tst_QTreeView::expandAndCollapse() view.expand(QModelIndex()); QCOMPARE(view.isExpanded(QModelIndex()), false); view.collapse(QModelIndex()); - QCOMPARE(expandedSpy.count(), 0); - QCOMPARE(collapsedSpy.count(), 0); + QCOMPARE(expandedSpy.size(), 0); + QCOMPARE(collapsedSpy.size(), 0); // expand a first level item QVERIFY(!view.isExpanded(a)); view.expand(a); QVERIFY(view.isExpanded(a)); - QCOMPARE(expandedSpy.count(), 1); - QCOMPARE(collapsedSpy.count(), 0); + QCOMPARE(expandedSpy.size(), 1); + QCOMPARE(collapsedSpy.size(), 0); args = expandedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a); view.expand(a); QVERIFY(view.isExpanded(a)); - QCOMPARE(expandedSpy.count(), 0); - QCOMPARE(collapsedSpy.count(), 0); + QCOMPARE(expandedSpy.size(), 0); + QCOMPARE(collapsedSpy.size(), 0); // expand a second level item QVERIFY(!view.isExpanded(b)); view.expand(b); QVERIFY(view.isExpanded(a)); QVERIFY(view.isExpanded(b)); - QCOMPARE(expandedSpy.count(), 1); - QCOMPARE(collapsedSpy.count(), 0); + QCOMPARE(expandedSpy.size(), 1); + QCOMPARE(collapsedSpy.size(), 0); args = expandedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), b); view.expand(b); QVERIFY(view.isExpanded(b)); - QCOMPARE(expandedSpy.count(), 0); - QCOMPARE(collapsedSpy.count(), 0); + QCOMPARE(expandedSpy.size(), 0); + QCOMPARE(collapsedSpy.size(), 0); // collapse the first level item view.collapse(a); QVERIFY(!view.isExpanded(a)); QVERIFY(view.isExpanded(b)); - QCOMPARE(expandedSpy.count(), 0); - QCOMPARE(collapsedSpy.count(), 1); + QCOMPARE(expandedSpy.size(), 0); + QCOMPARE(collapsedSpy.size(), 1); args = collapsedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a); view.collapse(a); QVERIFY(!view.isExpanded(a)); - QCOMPARE(expandedSpy.count(), 0); - QCOMPARE(collapsedSpy.count(), 0); + QCOMPARE(expandedSpy.size(), 0); + QCOMPARE(collapsedSpy.size(), 0); // expand the first level item again view.expand(a); QVERIFY(view.isExpanded(a)); QVERIFY(view.isExpanded(b)); - QCOMPARE(expandedSpy.count(), 1); - QCOMPARE(collapsedSpy.count(), 0); + QCOMPARE(expandedSpy.size(), 1); + QCOMPARE(collapsedSpy.size(), 0); args = expandedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a); @@ -1653,8 +1632,8 @@ void tst_QTreeView::expandAndCollapse() view.collapse(b); QVERIFY(view.isExpanded(a)); QVERIFY(!view.isExpanded(b)); - QCOMPARE(expandedSpy.count(), 0); - QCOMPARE(collapsedSpy.count(), 1); + QCOMPARE(expandedSpy.size(), 0); + QCOMPARE(collapsedSpy.size(), 1); args = collapsedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), b); @@ -1662,8 +1641,8 @@ void tst_QTreeView::expandAndCollapse() view.collapse(a); QVERIFY(!view.isExpanded(a)); QVERIFY(!view.isExpanded(b)); - QCOMPARE(expandedSpy.count(), 0); - QCOMPARE(collapsedSpy.count(), 1); + QCOMPARE(expandedSpy.size(), 0); + QCOMPARE(collapsedSpy.size(), 1); args = collapsedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a); @@ -1675,8 +1654,8 @@ void tst_QTreeView::expandAndCollapse() QVERIFY(view.isExpanded(a)); QVERIFY(view.isExpanded(b)); QVERIFY(!view.isExpanded(c)); - QCOMPARE(expandedSpy.count(), 2); - QCOMPARE(collapsedSpy.count(), 0); + QCOMPARE(expandedSpy.size(), 2); + QCOMPARE(collapsedSpy.size(), 0); args = expandedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a); args = expandedSpy.takeFirst(); @@ -1687,8 +1666,8 @@ void tst_QTreeView::expandAndCollapse() QVERIFY(!view.isExpanded(a)); QVERIFY(!view.isExpanded(b)); QVERIFY(!view.isExpanded(c)); - QCOMPARE(expandedSpy.count(), 0); - QCOMPARE(collapsedSpy.count(), 2); + QCOMPARE(expandedSpy.size(), 0); + QCOMPARE(collapsedSpy.size(), 2); args = collapsedSpy.takeFirst(); QCOMPARE(qvariant_cast<QModelIndex>(args.at(0)), a); args = collapsedSpy.takeFirst(); @@ -1750,22 +1729,22 @@ void tst_QTreeView::expandAndCollapseAll() view.expandAll(); checkExpandState(model, view, QModelIndex(), true, &count); - QCOMPARE(collapsedSpy.count(), 0); - QCOMPARE(expandedSpy.count(), 39); // == 3 (first) + 9 (second) + 27 (third level) + QCOMPARE(collapsedSpy.size(), 0); + QCOMPARE(expandedSpy.size(), 39); // == 3 (first) + 9 (second) + 27 (third level) QCOMPARE(count, 39); collapsedSpy.clear(); expandedSpy.clear(); view.collapseAll(); checkExpandState(model, view, QModelIndex(), false, &count); - QCOMPARE(collapsedSpy.count(), 39); - QCOMPARE(expandedSpy.count(), 0); + QCOMPARE(collapsedSpy.size(), 39); + QCOMPARE(expandedSpy.size(), 0); QCOMPARE(count, 39); collapsedSpy.clear(); expandedSpy.clear(); view.expandRecursively(model.index(0, 0)); - QCOMPARE(expandedSpy.count(), 13); // 1 + 3 + 9 + QCOMPARE(expandedSpy.size(), 13); // 1 + 3 + 9 checkExpandState(model, view, model.index(0, 0), true, &count); QCOMPARE(count, 13); @@ -1777,9 +1756,9 @@ void tst_QTreeView::expandAndCollapseAll() expandedSpy.clear(); view.collapseAll(); view.expandRecursively(model.index(0, 0), 1); - QCOMPARE(expandedSpy.count(), 4); // 1 + 3 + QCOMPARE(expandedSpy.size(), 4); // 1 + 3 view.expandRecursively(model.index(0, 0), 2); - QCOMPARE(expandedSpy.count(), 13); // (1 + 3) + 9 + QCOMPARE(expandedSpy.size(), 13); // (1 + 3) + 9 checkExpandState(model, view, model.index(0, 0), true, &count); QCOMPARE(count, 13); @@ -1875,7 +1854,7 @@ void tst_QTreeView::keyboardNavigation() } break; default: - QVERIFY(false); + QFAIL(qPrintable(QStringLiteral("Unexpected key: %1").arg(key))); } QCOMPARE(view.currentIndex().row(), row); @@ -1946,7 +1925,6 @@ void tst_QTreeView::moveCursor() view.setColumnHidden(0, true); QVERIFY(view.isColumnHidden(0)); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); //here the first visible index should be selected @@ -2047,7 +2025,7 @@ void tst_QTreeView::setSelection() QFETCH(PointList, expectedItems); QFETCH(int, verticalOffset); - QtTestModel model(10, 5); + QtTestModel model(100, 5); model.levels = 1; model.setDecorationsEnabled(true); QTreeView view; @@ -2065,7 +2043,7 @@ void tst_QTreeView::setSelection() QVERIFY(selectionModel); const QModelIndexList selectedIndexes = selectionModel->selectedIndexes(); - QCOMPARE(selectedIndexes.count(), expectedItems.count()); + QCOMPARE(selectedIndexes.size(), expectedItems.size()); for (const QModelIndex &idx : selectedIndexes) QVERIFY(expectedItems.contains(QPoint(idx.column(), idx.row()))); } @@ -2169,7 +2147,7 @@ void tst_QTreeView::clicked() continue; QSignalSpy spy(&view, &QTreeView::clicked); QTest::mouseClick(view.viewport(), Qt::LeftButton, Qt::NoModifier, p); - QTRY_COMPARE(spy.count(), 1); + QTRY_COMPARE(spy.size(), 1); } } @@ -2250,7 +2228,7 @@ void tst_QTreeView::rowsAboutToBeRemoved() model.removeRows(1,1); QCOMPARE((view.state()), 0); // Should not be 5 (or any other number for that sake :) - QCOMPARE(spy1.count(), 1); + QCOMPARE(spy1.size(), 1); } @@ -2490,12 +2468,13 @@ void tst_QTreeView::spanningItems() // size hint // every second row is un-spanned - QStyleOptionViewItem option = view.viewOptions(); + QStyleOptionViewItem option; + view.initViewItemOption(&option); int w = view.header()->sectionSizeHint(0); for (int i = 0; i < model.rowCount(QModelIndex()); ++i) { if (!view.isFirstColumnSpanned(i, QModelIndex())) { QModelIndex index = model.index(i, 0, QModelIndex()); - w = qMax(w, view.itemDelegate(index)->sizeHint(option, index).width() + view.indentation()); + w = qMax(w, view.itemDelegateForIndex(index)->sizeHint(option, index).width() + view.indentation()); } } QCOMPARE(view.sizeHintForColumn(0), w); @@ -2561,28 +2540,28 @@ void tst_QTreeView::selectionWithHiddenItems() //child should not be selected as it is hidden (its parent is not expanded) view.selectAll(); - QCOMPARE(view.selectionModel()->selection().count(), 1); //one range - QCOMPARE(view.selectionModel()->selectedRows().count(), 4); + QCOMPARE(view.selectionModel()->selection().size(), 1); //one range + QCOMPARE(view.selectionModel()->selectedRows().size(), 4); view.expandAll(); QVERIFY(view.isExpanded(item1.index())); - QCOMPARE(view.selectionModel()->selection().count(), 1); - QCOMPARE(view.selectionModel()->selectedRows().count(), 4); + QCOMPARE(view.selectionModel()->selection().size(), 1); + QCOMPARE(view.selectionModel()->selectedRows().size(), 4); QVERIFY( !view.selectionModel()->isSelected(model.indexFromItem(&child))); view.clearSelection(); QVERIFY(view.isExpanded(item1.index())); //child should be selected as it is visible (its parent is expanded) view.selectAll(); - QCOMPARE(view.selectionModel()->selection().count(), 2); - QCOMPARE(view.selectionModel()->selectedRows().count(), 5); //everything is selected + QCOMPARE(view.selectionModel()->selection().size(), 2); + QCOMPARE(view.selectionModel()->selectedRows().size(), 5); //everything is selected view.clearSelection(); //we hide the node with a child (there should then be 3 items selected in 2 ranges) view.setRowHidden(1, QModelIndex(), true); QVERIFY(view.isExpanded(item1.index())); view.selectAll(); - QCOMPARE(view.selectionModel()->selection().count(), 2); - QCOMPARE(view.selectionModel()->selectedRows().count(), 3); + QCOMPARE(view.selectionModel()->selection().size(), 2); + QCOMPARE(view.selectionModel()->selectedRows().size(), 3); QVERIFY(!view.selectionModel()->isSelected(model.indexFromItem(&item1))); QVERIFY(!view.selectionModel()->isSelected(model.indexFromItem(&child))); @@ -2595,8 +2574,8 @@ void tst_QTreeView::selectionWithHiddenItems() QVERIFY(view.isExpanded(item1.index())); view.selectAll(); QVERIFY(view.isExpanded(item1.index())); - QCOMPARE(view.selectionModel()->selection().count(), 3); - QCOMPARE(view.selectionModel()->selectedRows().count(), 4); + QCOMPARE(view.selectionModel()->selection().size(), 3); + QCOMPARE(view.selectionModel()->selectedRows().size(), 4); QVERIFY( !view.selectionModel()->isSelected(model.indexFromItem(&item2))); view.setRowHidden(2, QModelIndex(), false); QVERIFY(view.isExpanded(item1.index())); @@ -2611,21 +2590,21 @@ void tst_QTreeView::selectAll() view2.setSelectionMode(QAbstractItemView::ExtendedSelection); view2.selectAll(); // Should work with an empty model //everything should be selected since we are in ExtendedSelection mode - QCOMPARE(view2.selectedIndexes().count(), model.rowCount() * model.columnCount()); + QCOMPARE(view2.selectedIndexes().size(), model.rowCount() * model.columnCount()); for (int i = 0; i < model.rowCount(); ++i) model.setData(model.index(i,0), QLatin1String("row ") + QString::number(i)); QTreeView view; view.setModel(&model); - int selectedCount = view.selectedIndexes().count(); + int selectedCount = view.selectedIndexes().size(); view.selectAll(); - QCOMPARE(view.selectedIndexes().count(), selectedCount); + QCOMPARE(view.selectedIndexes().size(), selectedCount); QTreeView view3; view3.setModel(&model); view3.setSelectionMode(QAbstractItemView::NoSelection); view3.selectAll(); - QCOMPARE(view3.selectedIndexes().count(), 0); + QCOMPARE(view3.selectedIndexes().size(), 0); } void tst_QTreeView::extendedSelection_data() @@ -2651,7 +2630,7 @@ void tst_QTreeView::extendedSelection() topLevel.show(); QVERIFY(QTest::qWaitForWindowExposed(&topLevel)); QTest::mousePress(view.viewport(), Qt::LeftButton, {}, mousePressPos); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), selectedCount); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), selectedCount); } void tst_QTreeView::rowSizeHint() @@ -2851,6 +2830,14 @@ void tst_QTreeView::sortByColumn() QCOMPARE(view.model()->data(view.model()->index(1, 0)).toString(), QString::fromLatin1("d")); QCOMPARE(view.model()->data(view.model()->index(0, 1)).toString(), QString::fromLatin1("e")); QCOMPARE(view.model()->data(view.model()->index(1, 1)).toString(), QString::fromLatin1("g")); + + // a new 'sortByColumn()' should do a re-sort (e.g. due to the data changed), QTBUG-86268 + view.setModel(&model); + view.sortByColumn(0, Qt::AscendingOrder); + QCOMPARE(view.model()->data(view.model()->index(0, 0)).toString(), QString::fromLatin1("a")); + model.setItem(0, 0, new QStandardItem("x")); + view.sortByColumn(0, Qt::AscendingOrder); + QCOMPARE(view.model()->data(view.model()->index(0, 0)).toString(), QString::fromLatin1("b")); } /* @@ -2883,7 +2870,7 @@ public: } void kill() { - for (int i = children.count() -1; i >= 0; --i) { + for (int i = children.size() -1; i >= 0; --i) { children.at(i)->kill(); if (parent == nullptr) { deadChildren.append(children.at(i)); @@ -2925,7 +2912,7 @@ public: emit layoutAboutToBeChanged(); QModelIndexList oldList = persistentIndexList(); QList<QStack<int>> oldListPath; - for (int i = 0; i < oldList.count(); ++i) { + for (int i = 0; i < oldList.size(); ++i) { QModelIndex idx = oldList.at(i); QStack<int> path; while (idx.isValid()) { @@ -2937,7 +2924,7 @@ public: root->kill(); QModelIndexList newList; - for (auto path : qAsConst(oldListPath)) { + for (auto path : std::as_const(oldListPath)) { QModelIndex idx; while (!path.isEmpty()) idx = index(path.pop(), 0, idx); @@ -2956,7 +2943,7 @@ public: if (parentNode->isDead) qFatal("%s: parentNode is dead!", Q_FUNC_INFO); } - return parentNode->children.count(); + return parentNode->children.size(); } int columnCount(const QModelIndex &parent = QModelIndex()) const override { @@ -3547,7 +3534,7 @@ void tst_QTreeView::task174627_moveLeftToRoot() QSignalSpy spy(&view, &task174627_TreeView::signalCurrentChanged); QTest::keyClick(&view, Qt::Key_Left); - QCOMPARE(spy.count(), 0); + QCOMPARE(spy.size(), 0); } void tst_QTreeView::task171902_expandWith1stColHidden() @@ -3716,7 +3703,6 @@ void tst_QTreeView::task224091_appendColumns() treeView->setModel(model); topLevel->show(); treeView->resize(50, 50); - QApplication::setActiveWindow(topLevel); QVERIFY(QTest::qWaitForWindowActive(topLevel)); QVERIFY(!treeView->verticalScrollBar()->isVisible()); @@ -4053,7 +4039,7 @@ void tst_QTreeView::task248022_changeSelection() &view, &TreeView::handleSelectionChanged); QTest::mouseClick(view.viewport(), Qt::LeftButton, {}, view.visualRect(model.index(1)).center()); - QCOMPARE(view.selectionModel()->selectedIndexes().count(), list.count()); + QCOMPARE(view.selectionModel()->selectedIndexes().size(), list.size()); } void tst_QTreeView::task245654_changeModelAndExpandAll() @@ -4092,7 +4078,6 @@ void tst_QTreeView::doubleClickedWithSpans() view.setModel(&model); view.setFirstColumnSpanned(0, QModelIndex(), true); view.show(); - QApplication::setActiveWindow(&view); QVERIFY(QTest::qWaitForWindowActive(&view)); QVERIFY(view.isActiveWindow()); @@ -4102,7 +4087,7 @@ void tst_QTreeView::doubleClickedWithSpans() QTest::mousePress(view.viewport(), Qt::LeftButton, {}, p); QTest::mouseDClick(view.viewport(), Qt::LeftButton, {}, p); QTest::mouseRelease(view.viewport(), Qt::LeftButton, {}, p); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); //let's click on the 2nd column p.setX(p.x() + view.header()->sectionSize(0)); @@ -4113,7 +4098,7 @@ void tst_QTreeView::doubleClickedWithSpans() QTest::mousePress(view.viewport(), Qt::LeftButton, {}, p); QTest::mouseDClick(view.viewport(), Qt::LeftButton, {}, p); QTest::mouseRelease(view.viewport(), Qt::LeftButton, {}, p); - QTRY_COMPARE(spy.count(), 2); + QTRY_COMPARE(spy.size(), 2); } void tst_QTreeView::taskQTBUG_6450_selectAllWith1stColumnHidden() @@ -4194,7 +4179,6 @@ void tst_QTreeView::keyboardNavigationWithDisabled() view.resize(200, view.visualRect(model.index(0,0)).height()*10); topLevel.show(); - QApplication::setActiveWindow(&topLevel); QVERIFY(QTest::qWaitForWindowActive(&topLevel)); QVERIFY(topLevel.isActiveWindow()); @@ -4777,10 +4761,9 @@ void tst_QTreeView::statusTip() mw.setCentralWidget(view); } mw.statusBar(); - mw.setGeometry(QRect(QPoint(QApplication::desktop()->geometry().center() - QPoint(250, 250)), + mw.setGeometry(QRect(QPoint(QGuiApplication::primaryScreen()->geometry().center() - QPoint(250, 250)), QSize(500, 500))); mw.show(); - QApplication::setActiveWindow(&mw); QVERIFY(QTest::qWaitForWindowActive(&mw)); // Ensure it is moved away first and then moved to the relevant section QTest::mouseMove(mw.windowHandle(), view->mapTo(&mw, view->rect().bottomLeft() + QPoint(20, 20))); @@ -4831,6 +4814,9 @@ void tst_QTreeView::fetchMoreOnScroll() if (QGuiApplication::platformName().startsWith(QLatin1String("wayland"), Qt::CaseInsensitive)) QSKIP("Wayland: This fails. Figure out why."); + if (QGuiApplication::platformName().startsWith(QLatin1String("eglfs"), Qt::CaseInsensitive)) + QSKIP("EGLFS does not allow resizing on top level window"); + QTreeView tw; FetchMoreModel im; tw.setModel(&im); @@ -4850,13 +4836,13 @@ void tst_QTreeView::checkIntersectedRect_data() { QStandardItemModel *model = new QStandardItemModel; for (int i = 0; i < rowCount; ++i) { - const QList<QStandardItem *> sil({new QStandardItem(QLatin1String("Row %1 Item").arg(i)), - new QStandardItem(QLatin1String("2nd column"))}); + const QList<QStandardItem *> sil({new QStandardItem(QString("Row %1 Item").arg(i)), + new QStandardItem(QString("2nd column"))}); model->appendRow(sil); } for (int i = 2; i < 4; ++i) { - const QList<QStandardItem *> sil({new QStandardItem(QLatin1String("Row %1 Item").arg(i)), - new QStandardItem(QLatin1String("2nd column"))}); + const QList<QStandardItem *> sil({new QStandardItem(QString("Row %1 Item").arg(i)), + new QStandardItem(QString("2nd column"))}); model->item(i)->appendRow(sil); } return model; @@ -4908,6 +4894,9 @@ void tst_QTreeView::checkIntersectedRect_data() void tst_QTreeView::checkIntersectedRect() { + if (QGuiApplication::platformName().startsWith(QLatin1String("eglfs"), Qt::CaseInsensitive)) + QSKIP("EGLFS does not allow resizing on top level window"); + QFETCH(QStandardItemModel *, model); QFETCH(const QList<QModelIndex>, changedIndexes); QFETCH(bool, isEmpty); @@ -5059,5 +5048,215 @@ void tst_QTreeView::taskQTBUG_61476() QCOMPARE(lastTopLevel->checkState(), Qt::Checked); } +void tst_QTreeView::taskQTBUG_42469_crash() +{ + QTreeWidget treeWidget; + QTreeWidgetItem *itemOne = new QTreeWidgetItem(QStringList("item1")); + QTreeWidgetItem *itemTwo = new QTreeWidgetItem(QStringList("item2")); + treeWidget.addTopLevelItem(itemOne); + treeWidget.addTopLevelItem(itemTwo); + treeWidget.topLevelItem(1)->addChild(new QTreeWidgetItem(QStringList("child1"))); + + treeWidget.setAnimated(true); + QObject::connect(&treeWidget, &QTreeWidget::itemExpanded, [&](QTreeWidgetItem* p_item) { + auto tempCount = treeWidget.topLevelItemCount(); + for (int j = 0; j < tempCount; ++j) + if (treeWidget.topLevelItem(j) != p_item) { + auto temp = treeWidget.topLevelItem(j); + temp->setHidden(true); + } + }); + + treeWidget.show(); + itemTwo->setExpanded(true); +} + +void tst_QTreeView::fetchUntilScreenFull() +{ + class TreeModel : public QAbstractItemModel + { + public: + const int maxChildren = 49; + explicit TreeModel(QObject* parent = nullptr) : QAbstractItemModel(parent) + { + QVariant rootData1("Parent Col 1"); + QVariant rootData2("Parent Col 2"); + QVector<QVariant> rootData; + rootData.append(rootData1); + rootData.append(rootData2); + + m_root = new TreeItem(rootData, nullptr); + + QVariant childData1("Col 1"); + QVariant childData2("Col 2"); + QVector<QVariant> childData; + childData.append(childData1); + childData.append(childData2); + + TreeItem* item_1 = new TreeItem(childData, m_root); + m_root->children.append(item_1); + + TreeItem* item_2 = new TreeItem(childData, item_1); + item_1->children.append(item_2); + } + + QModelIndex index(const int row, const int column, + const QModelIndex& parent = QModelIndex()) const override + { + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + TreeItem* parentItem = + parent.isValid() ? static_cast<TreeItem*>(parent.internalPointer()) : m_root; + TreeItem* childItem = parentItem->children.at(row); + return createIndex(row, column, childItem); + } + + int rowCount(const QModelIndex& parent) const override + { + if (parent.column() > 0) + return 0; + + TreeItem* parentItem = parent.isValid() ? static_cast<TreeItem*>(parent.internalPointer()) + : m_root; + return parentItem->children.size(); + } + + int columnCount(const QModelIndex&) const override { return 2; } + + QModelIndex parent(const QModelIndex& childIndex) const override + { + if (!childIndex.isValid()) + return QModelIndex(); + + TreeItem* parentItem = + static_cast<TreeItem*>(childIndex.internalPointer())->parent; + return parentItem == m_root ? QModelIndex() + : createIndex(parentItem->rowInParent(), 0, parentItem); + } + + QVariant data(const QModelIndex& index, const int role) const override + { + if (!index.isValid() || role != Qt::DisplayRole) + return QVariant(); + + TreeItem* item = static_cast<TreeItem*>(index.internalPointer()); + return item->data.at(index.column()); + } + + bool canFetchMore(const QModelIndex& parent) const override + { + if (!parent.isValid()) { + return false; + } else { + TreeItem* item = static_cast<TreeItem*>(parent.internalPointer()); + return item->children.size() < maxChildren; + } + } + + void fetchMore(const QModelIndex& parent) override + { + if (!parent.isValid()) + return; + + fetchMoreCount++; + TreeItem* parentItem = static_cast<TreeItem*>(parent.internalPointer()); + int childCount = parentItem->children.size(); + + beginInsertRows(parent, childCount, childCount); + + QVariant childData1("Col 1"); + QVariant childData2("Col 2"); + QVector<QVariant> childData; + childData.append(childData1); + childData.append(childData2); + TreeItem* newChild = new TreeItem(childData, parentItem); + parentItem->children.append(newChild); + + endInsertRows(); + } + + int fetchMoreCount = 0; + private: + struct TreeItem + { + TreeItem(const QVector<QVariant>& values, TreeItem* parent) + : data(values), parent(parent) + { + } + ~TreeItem() { qDeleteAll(children); } + int rowInParent() const + { + if (parent) + return parent->children.indexOf(const_cast<TreeItem*>(this)); + return 0; + } + QVector<QVariant> data; + QVector<TreeItem*> children; + TreeItem* parent = nullptr; + }; + TreeItem* m_root; + }; + + if (QGuiApplication::platformName().startsWith(QLatin1String("eglfs"), Qt::CaseInsensitive)) + QSKIP("EGLFS does not allow resizing on top level window"); + + + QTreeView tv; + TreeModel model; + tv.setModel(&model); + + const int itemHeight = tv.sizeHintForRow(0); + tv.resize(250, itemHeight * 10); + tv.show(); + QVERIFY(QTest::qWaitForWindowExposed(&tv)); + + tv.expand(model.index(0, 0)); + const int viewportHeight = tv.viewport()->height(); + const int itemCount = viewportHeight / itemHeight; + const int minFetchCount = itemCount - 1; + const int maxFetchCount = itemCount + 1; + + const bool expectedItemNumberFetched = model.fetchMoreCount >= minFetchCount + && model.fetchMoreCount <= maxFetchCount; + if (!expectedItemNumberFetched) + qDebug() << model.fetchMoreCount << minFetchCount << maxFetchCount; + QVERIFY(expectedItemNumberFetched); +} + +static void populateModel(QStandardItemModel *model) +{ + const int depth = 10; + for (int i1 = 0; i1 < depth; ++i1) { + QStandardItem *s1 = new QStandardItem; + s1->setText(QString::number(i1)); + model->appendRow(s1); + for (int i2 = 0; i2 < depth; ++i2) { + QStandardItem *s2 = new QStandardItem; + s2->setText(QStringLiteral("%1 - %2").arg(i1).arg(i2)); + s1->appendRow(s2); + for (int i3 = 0; i3 < depth; ++i3) { + QStandardItem *s3 = new QStandardItem; + s3->setText(QStringLiteral("%1 - %2 - %3").arg(i1).arg(i2).arg(i3)); + s2->appendRow(s3); + } + } + } +} + +void tst_QTreeView::expandAfterTake() +{ + QStandardItemModel model; + populateModel(&model); + QTreeView view; + view.setUniformRowHeights(true); + view.setModel(&model); + view.show(); + QVERIFY(QTest::qWaitForWindowExposed(&view)); + view.expandAll(); + model.takeItem(0); + populateModel(&model); // populate model again, having corrupted items inside QTreeViewPrivate::expandedIndexes + view.expandAll(); // adding new items to QTreeViewPrivate::expandedIndexes with corrupted persistent indices, causing crash sometimes +} QTEST_MAIN(tst_QTreeView) #include "tst_qtreeview.moc" diff --git a/tests/auto/widgets/itemviews/qtreewidget/CMakeLists.txt b/tests/auto/widgets/itemviews/qtreewidget/CMakeLists.txt index d58f004561..251dbb9b79 100644 --- a/tests/auto/widgets/itemviews/qtreewidget/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qtreewidget/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qtreewidget.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtreewidget Test: ##################################################################### -qt_add_test(tst_qtreewidget +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtreewidget LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtreewidget SOURCES tst_qtreewidget.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/itemviews/qtreewidget/qtreewidget.pro b/tests/auto/widgets/itemviews/qtreewidget/qtreewidget.pro deleted file mode 100644 index 3fa0681691..0000000000 --- a/tests/auto/widgets/itemviews/qtreewidget/qtreewidget.pro +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG += testcase -TARGET = tst_qtreewidget -QT += widgets testlib -SOURCES += tst_qtreewidget.cpp diff --git a/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp b/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp index f7b1cfec65..f4423831ca 100644 --- a/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp +++ b/tests/auto/widgets/itemviews/qtreewidget/tst_qtreewidget.cpp @@ -1,35 +1,11 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2021 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QApplication> #include <QHeaderView> #include <QLineEdit> +#include <QScopeGuard> #include <QScrollBar> #include <QSignalSpy> #include <QStyledItemDelegate> @@ -151,6 +127,9 @@ private slots: void getMimeDataWithInvalidItem(); void testVisualItemRect(); void reparentHiddenItem(); + void persistentChildIndex(); + void createPersistentOnLayoutAboutToBeChanged(); + void createPersistentOnLayoutAboutToBeChangedAutoSort(); #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void clearItemData(); #endif @@ -302,8 +281,8 @@ void tst_QTreeWidget::addTopLevelItem() while (!tops.isEmpty()) { TreeItem *ti = tops.takeFirst(); delete ti; - QCOMPARE(tree.topLevelItemCount(), tops.count()); - for (int i = 0; i < tops.count(); ++i) + QCOMPARE(tree.topLevelItemCount(), tops.size()); + for (int i = 0; i < tops.size(); ++i) QCOMPARE(tree.topLevelItem(i), tops.at(i)); } @@ -344,6 +323,7 @@ void tst_QTreeWidget::addTopLevelItem() // invalid insert { tops.clear(); + const auto sg = qScopeGuard([&] { qDeleteAll(std::exchange(tops, {})); }); for (int i = 0; i < 10; ++i) tops << new TreeItem(); int count = tree.topLevelItemCount(); @@ -398,12 +378,12 @@ void tst_QTreeWidget::currentItem() tree.setCurrentItem(item); QCOMPARE(tree.currentItem(), item); - QCOMPARE(currentItemChangedSpy.count(), 1); + QCOMPARE(currentItemChangedSpy.size(), 1); QVariantList args = currentItemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(1)), previous); - QCOMPARE(itemSelectionChangedSpy.count(), 1); + QCOMPARE(itemSelectionChangedSpy.size(), 1); itemSelectionChangedSpy.clear(); previous = item; @@ -416,15 +396,15 @@ void tst_QTreeWidget::currentItem() if (!currentItemChangedSpy.isEmpty()) { // ### we get a currentItemChanged() when what really // changed was just currentColumn(). Should it be like this? - QCOMPARE(currentItemChangedSpy.count(), 1); + QCOMPARE(currentItemChangedSpy.size(), 1); QVariantList args = currentItemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(1)), item); if (tree.selectionBehavior() == QAbstractItemView::SelectItems) { - QCOMPARE(itemSelectionChangedSpy.count(), 1); + QCOMPARE(itemSelectionChangedSpy.size(), 1); itemSelectionChangedSpy.clear(); } else { - QCOMPARE(itemSelectionChangedSpy.count(), 0); + QCOMPARE(itemSelectionChangedSpy.size(), 0); } } } @@ -484,7 +464,7 @@ void tst_QTreeWidget::editItem() QTest::keyClick(editor, Qt::Key_A); QTest::keyClick(editor, Qt::Key_Enter); QCoreApplication::processEvents(); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); QVariantList args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem *>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), col); @@ -640,7 +620,7 @@ void tst_QTreeWidget::setItemHidden2() { // From Task 78587 const QStringList hl({"ID", "Desc"}); - testWidget->setColumnCount(hl.count()); + testWidget->setColumnCount(hl.size()); testWidget->setHeaderLabels(hl); testWidget->setSortingEnabled(true); @@ -841,7 +821,7 @@ void tst_QTreeWidget::selectedItems() // check selectedItems const auto sel = testWidget->selectedItems(); - QCOMPARE(sel.count(), expectedItems.count()); + QCOMPARE(sel.size(), expectedItems.size()); for (const auto &itemPath : expectedItems) { QTreeWidgetItem *item = nullptr; for (int index : itemPath) { @@ -877,7 +857,7 @@ void tst_QTreeWidget::selectedItems() } item->setSelected(false); } - QCOMPARE(testWidget->selectedItems().count(), 0); + QCOMPARE(testWidget->selectedItems().size(), 0); } void tst_QTreeWidget::itemAssignment() @@ -1110,9 +1090,9 @@ void tst_QTreeWidget::findItems() QList<QTreeWidgetItem*> result = testWidget->findItems(pattern, Qt::MatchExactly|Qt::MatchRecursive); - QCOMPARE(result.count(), resultCount); + QCOMPARE(result.size(), resultCount); - for (int k = 0; k < result.count() && k < resultText.count(); ++k) + for (int k = 0; k < result.size() && k < resultText.size(); ++k) QCOMPARE(result.at(k)->text(column), resultText.at(k)); } @@ -1130,7 +1110,7 @@ void tst_QTreeWidget::findItemsInColumn() // Recursively search column one for 400. QList<QTreeWidgetItem*> items = testWidget->findItems("400", Qt::MatchExactly|Qt::MatchRecursive, 1); - QCOMPARE(items.count(), 1); + QCOMPARE(items.size(), 1); } void tst_QTreeWidget::sortItems_data() @@ -1197,16 +1177,16 @@ void tst_QTreeWidget::sortItems() testWidget->sortItems(column, order); QCOMPARE(testWidget->sortColumn(), column); - for (int k = 0; k < topLevelResult.count(); ++k) { + for (int k = 0; k < topLevelResult.size(); ++k) { QTreeWidgetItem *item = testWidget->topLevelItem(k); QCOMPARE(item->text(column), topLevelResult.at(k)); - for (int l = 0; l < childResult.count(); ++l) + for (int l = 0; l < childResult.size(); ++l) QCOMPARE(item->child(l)->text(column), childResult.at(l)); } - for (int m = 0; m < tops.count(); ++m) + for (int m = 0; m < tops.size(); ++m) QCOMPARE(tops.at(m).row(), expectedTopRows.at(m)); - for (int n = 0; n < children.count(); ++n) + for (int n = 0; n < children.size(); ++n) QCOMPARE(children.at(n).row(), expectedChildRows.at(n)); } @@ -1366,17 +1346,17 @@ void tst_QTreeWidget::insertTopLevelItems_data() const QStringList insert{ "baz" }; QTest::newRow("Insert at count") << initial << insert - << initial.count() << initial.count() - << initial.count() << initial.count(); + << initial.size() << initial.size() + << initial.size() << initial.size(); QTest::newRow("Insert in the middle") << initial << insert - << (initial.count() / 2) << (initial.count() / 2) - << (initial.count() / 2) << (initial.count() / 2); + << (initial.size() / 2) << (initial.size() / 2) + << (initial.size() / 2) << (initial.size() / 2); QTest::newRow("Insert less than 0") << initial << insert << -1 << -1 << -1 << -1; QTest::newRow("Insert beyond count") << initial << insert - << initial.count() + 1 << -1 - << initial.count() + 1 << -1; + << initial.size() + 1 << -1 + << initial.size() + 1 << -1; } void tst_QTreeWidget::insertTopLevelItems() @@ -1391,26 +1371,26 @@ void tst_QTreeWidget::insertTopLevelItems() { // insert the initial items QCOMPARE(testWidget->topLevelItemCount(), 0); - for (int i = 0; i < initialText.count(); ++i) { + for (int i = 0; i < initialText.size(); ++i) { QTreeWidgetItem *top = new QTreeWidgetItem(QStringList(initialText.at(i))); testWidget->addTopLevelItem(top); QCOMPARE(testWidget->indexOfTopLevelItem(top), i); } - QCOMPARE(testWidget->topLevelItemCount(), initialText.count()); + QCOMPARE(testWidget->topLevelItemCount(), initialText.size()); } { // test adding children QTreeWidgetItem *topLevel = testWidget->topLevelItem(0); - for (int i = 0; i < initialText.count(); ++i) + for (int i = 0; i < initialText.size(); ++i) topLevel->addChild(new QTreeWidgetItem(QStringList(initialText.at(i)))); - QCOMPARE(topLevel->childCount(), initialText.count()); + QCOMPARE(topLevel->childCount(), initialText.size()); } { // test adding more top level items QTreeWidgetItem *topsy = new QTreeWidgetItem(QStringList(insertText.at(0))); testWidget->insertTopLevelItem(insertTopLevelIndex, topsy); if (expectedTopLevelIndex == -1) { - QCOMPARE(testWidget->topLevelItemCount(), initialText.count()); + QCOMPARE(testWidget->topLevelItemCount(), initialText.size()); delete topsy; } else { QTreeWidgetItem *item = testWidget->topLevelItem(expectedTopLevelIndex); @@ -1426,7 +1406,7 @@ void tst_QTreeWidget::insertTopLevelItems() QTreeWidgetItem *child = new QTreeWidgetItem(QStringList(insertText.at(0))); topLevel->insertChild(insertChildIndex, child); if (expectedChildIndex == -1) { - QCOMPARE(topLevel->childCount(), initialText.count()); + QCOMPARE(topLevel->childCount(), initialText.size()); delete child; } else { QTreeWidgetItem *item = topLevel->child(expectedChildIndex); @@ -1524,7 +1504,7 @@ void tst_QTreeWidget::keyboardNavigation() if (testWidget->currentItem() != item) { QCOMPARE(testWidget->currentItem(), item->parent()); item = testWidget->currentItem(); - row = item->parent() ? item->parent()->indexOfChild(item) : testWidget->indexOfTopLevelItem(item);; + row = item->parent() ? item->parent()->indexOfChild(item) : testWidget->indexOfTopLevelItem(item); } break; case Qt::Key_Right: @@ -1539,7 +1519,7 @@ void tst_QTreeWidget::keyboardNavigation() } break; default: - QVERIFY(false); + QFAIL(qPrintable(QStringLiteral("Unexpected key: %1").arg(key))); } QTreeWidgetItem *current = testWidget->currentItem(); @@ -1612,7 +1592,7 @@ void tst_QTreeWidget::scrollToItem() void tst_QTreeWidget::setSortingEnabled() { const QStringList hl{ "ID" }; - testWidget->setColumnCount(hl.count()); + testWidget->setColumnCount(hl.size()); testWidget->setHeaderLabels(hl); QTreeWidgetItem *item1 = new QTreeWidgetItem(testWidget); @@ -1681,7 +1661,7 @@ void tst_QTreeWidget::addChild() QList<QTreeWidgetItem*> taken = item->takeChildren(); QCOMPARE(taken, children); QCOMPARE(item->childCount(), 0); - for (int i = 0; i < taken.count(); ++i) { + for (int i = 0; i < taken.size(); ++i) { QCOMPARE(taken.at(i)->parent(), nullptr); QCOMPARE(taken.at(i)->treeWidget(), nullptr); item->addChild(taken.at(i)); // re-add @@ -1691,8 +1671,8 @@ void tst_QTreeWidget::addChild() while (!children.isEmpty()) { QTreeWidgetItem *ti = children.takeFirst(); delete ti; - QCOMPARE(item->childCount(), children.count()); - for (int i = 0; i < children.count(); ++i) + QCOMPARE(item->childCount(), children.size()); + for (int i = 0; i < children.size(); ++i) QCOMPARE(item->child(i), children.at(i)); } @@ -1739,9 +1719,9 @@ void tst_QTreeWidget::setData() QSignalSpy itemChangedSpy( testWidget, &QTreeWidget::itemChanged); headerItem->setText(0, "test"); - QCOMPARE(dataChangedSpy.count(), 0); - QCOMPARE(headerDataChangedSpy.count(), 1); - QCOMPARE(itemChangedSpy.count(), 0); // no itemChanged() signal for header item + QCOMPARE(dataChangedSpy.size(), 0); + QCOMPARE(headerDataChangedSpy.size(), 1); + QCOMPARE(itemChangedSpy.size(), 0); // no itemChanged() signal for header item headerItem->setData(-1, -1, QVariant()); } @@ -1759,116 +1739,119 @@ void tst_QTreeWidget::setData() const QString text = QLatin1String("text ") + iS; item->setText(j, text); QCOMPARE(item->text(j), text); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setText(j, text); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); QPixmap pixmap(32, 32); pixmap.fill((i == 1) ? Qt::red : Qt::green); QIcon icon(pixmap); item->setIcon(j, icon); QCOMPARE(item->icon(j), icon); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setIcon(j, icon); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 1); + args = itemChangedSpy.takeFirst(); + QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); + QCOMPARE(qvariant_cast<int>(args.at(1)), j); const QString toolTip = QLatin1String("toolTip ") + iS; item->setToolTip(j, toolTip); QCOMPARE(item->toolTip(j), toolTip); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setToolTip(j, toolTip); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); const QString statusTip = QLatin1String("statusTip ") + iS; item->setStatusTip(j, statusTip); QCOMPARE(item->statusTip(j), statusTip); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setStatusTip(j, statusTip); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); const QString whatsThis = QLatin1String("whatsThis ") + iS; item->setWhatsThis(j, whatsThis); QCOMPARE(item->whatsThis(j), whatsThis); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setWhatsThis(j, whatsThis); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); QSize sizeHint(64*i, 48*i); item->setSizeHint(j, sizeHint); QCOMPARE(item->sizeHint(j), sizeHint); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setSizeHint(j, sizeHint); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); QFont font; item->setFont(j, font); QCOMPARE(item->font(j), font); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setFont(j, font); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); Qt::Alignment textAlignment((i == 1) ? Qt::AlignLeft|Qt::AlignVCenter : Qt::AlignRight); item->setTextAlignment(j, textAlignment); QCOMPARE(item->textAlignment(j), int(textAlignment)); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setTextAlignment(j, textAlignment); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); QColor backgroundColor((i == 1) ? Qt::blue : Qt::yellow); item->setBackground(j, backgroundColor); QCOMPARE(item->background(j).color(), backgroundColor); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setBackground(j, backgroundColor); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); const QColor foregroundColor((i == 1) ? Qt::green : Qt::cyan); item->setForeground(j, foregroundColor); QCOMPARE(item->foreground(j), foregroundColor); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setForeground(j, foregroundColor); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); Qt::CheckState checkState((i == 1) ? Qt::PartiallyChecked : Qt::Checked); item->setCheckState(j, checkState); QCOMPARE(item->checkState(j), checkState); - QCOMPARE(itemChangedSpy.count(), 1); + QCOMPARE(itemChangedSpy.size(), 1); args = itemChangedSpy.takeFirst(); QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setCheckState(j, checkState); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); QCOMPARE(item->text(j), text); QCOMPARE(item->icon(j), icon); @@ -1901,7 +1884,7 @@ void tst_QTreeWidget::setData() QCOMPARE(qvariant_cast<QTreeWidgetItem*>(args.at(0)), item); QCOMPARE(qvariant_cast<int>(args.at(1)), j); item->setBackground(j, pixmap); - QCOMPARE(itemChangedSpy.count(), 0); + QCOMPARE(itemChangedSpy.size(), 0); item->setData(j, Qt::DisplayRole, QVariant()); item->setData(j, Qt::DecorationRole, QVariant()); @@ -1914,7 +1897,7 @@ void tst_QTreeWidget::setData() item->setData(j, Qt::BackgroundRole, QVariant()); item->setData(j, Qt::ForegroundRole, QVariant()); item->setData(j, Qt::CheckStateRole, QVariant()); - QCOMPARE(itemChangedSpy.count(), 11); + QCOMPARE(itemChangedSpy.size(), 11); itemChangedSpy.clear(); QCOMPARE(item->data(j, Qt::DisplayRole).toString(), QString()); @@ -1935,13 +1918,13 @@ void tst_QTreeWidget::setData() // ### add more data types here item->setData(0, Qt::DisplayRole, 5); - QCOMPARE(item->data(0, Qt::DisplayRole).type(), QVariant::Int); + QCOMPARE(item->data(0, Qt::DisplayRole).userType(), QMetaType::Int); item->setData(0, Qt::DisplayRole, "test"); - QCOMPARE(item->data(0, Qt::DisplayRole).type(), QVariant::String); + QCOMPARE(item->data(0, Qt::DisplayRole).userType(), QMetaType::QString); item->setData(0, Qt::DisplayRole, 0.4); - QCOMPARE(item->data(0, Qt::DisplayRole).type(), QVariant::Double); + QCOMPARE(item->data(0, Qt::DisplayRole).userType(), QMetaType::Double); delete item; } @@ -1977,11 +1960,11 @@ void tst_QTreeWidget::itemData() QCOMPARE(widget.currentRoles, QList<int> { Qt::UserRole + i }); } QMap<int, QVariant> flags = widget.model()->itemData(widget.model()->index(0, 0)); - QCOMPARE(flags.count(), 6); + QCOMPARE(flags.size(), 6); for (int i = 0; i < 4; ++i) QCOMPARE(flags[Qt::UserRole + i].toString(), QString::number(i + 1)); flags = widget.model()->itemData(widget.model()->index(0, 1)); - QCOMPARE(flags.count(), 0); + QCOMPARE(flags.size(), 0); item.setBackground(0, QBrush(Qt::red)); item.setForeground(0, QBrush(Qt::green)); @@ -2050,7 +2033,7 @@ void tst_QTreeWidget::setHeaderLabels() { QStringList list = QString("a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z").split(QLatin1Char(',')); testWidget->setHeaderLabels(list); - QCOMPARE(testWidget->header()->count(), list.count()); + QCOMPARE(testWidget->header()->count(), list.size()); } void tst_QTreeWidget::setHeaderItem() @@ -2280,11 +2263,11 @@ void tst_QTreeWidget::insertItemsWithSorting() w.addTopLevelItem(new QTreeWidgetItem({ txt })); break; } - QCOMPARE(w.topLevelItemCount(), expectedItems.count()); + QCOMPARE(w.topLevelItemCount(), expectedItems.size()); for (int i = 0; i < w.topLevelItemCount(); ++i) QCOMPARE(w.topLevelItem(i)->text(0), expectedItems.at(i)); - for (int k = 0; k < persistent.count(); ++k) + for (int k = 0; k < persistent.size(); ++k) QCOMPARE(persistent.at(k).row(), expectedRows.at(k)); } } @@ -2325,13 +2308,13 @@ void tst_QTreeWidget::insertExpandedItemsWithSorting() QTreeWidgetItem *child = new QTreeWidgetItem(parent, {text}); items << child; } - QCOMPARE(parent->childCount(), childTexts.count()); + QCOMPARE(parent->childCount(), childTexts.size()); QVERIFY(parent->isExpanded()); } - QCOMPARE(tree.model()->rowCount(), parentTexts.count()); + QCOMPARE(tree.model()->rowCount(), parentTexts.size()); // verify that the items are still expanded - for (const QTreeWidgetItem *item : qAsConst(items)) { + for (const QTreeWidgetItem *item : std::as_const(items)) { if (item->childCount() > 0) QVERIFY(item->isExpanded()); QModelIndex idx = tree.indexFromItem(item); @@ -2349,10 +2332,10 @@ void tst_QTreeWidget::insertExpandedItemsWithSorting() PersistentModelIndexVec children; for (int i = 0; i < model->rowCount(parents.constFirst()); ++i) children.push_back(model->index(i, 0, parents.constFirst())); - for (int i = 0; i < parentResult.count(); ++i) { + for (int i = 0; i < parentResult.size(); ++i) { QTreeWidgetItem *item = tree.topLevelItem(i); QCOMPARE(item->text(0), parentResult.at(i)); - for (int j = 0; j < childResult.count(); ++j) + for (int j = 0; j < childResult.size(); ++j) QCOMPARE(item->child(j)->text(0), childResult.at(j)); } } @@ -2444,19 +2427,19 @@ void tst_QTreeWidget::changeDataWithSorting() QTreeWidgetItem *item = w.topLevelItem(itemIndex); item->setText(0, newValue); - for (int i = 0; i < expectedItems.count(); ++i) { + for (int i = 0; i < expectedItems.size(); ++i) { QCOMPARE(w.topLevelItem(i)->text(0), expectedItems.at(i)); - for (const QPersistentModelIndex &p : qAsConst(persistent)) { + for (const QPersistentModelIndex &p : std::as_const(persistent)) { if (p.row() == i) // the same toplevel row QCOMPARE(p.internalPointer(), static_cast<void *>(w.topLevelItem(i))); } } - for (int k = 0; k < persistent.count(); ++k) + for (int k = 0; k < persistent.size(); ++k) QCOMPARE(persistent.at(k).row(), expectedRows.at(k)); - QCOMPARE(dataChangedSpy.count(), 1); - QCOMPARE(layoutChangedSpy.count(), reorderingExpected ? 1 : 0); + QCOMPARE(dataChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), reorderingExpected ? 1 : 0); } void tst_QTreeWidget::changeDataWithStableSorting_data() @@ -2613,19 +2596,19 @@ void tst_QTreeWidget::changeDataWithStableSorting() item->setText(0, newValue); if (forceChange) item->emitDataChanged(); - for (int i = 0; i < expectedItems.count(); ++i) { + for (int i = 0; i < expectedItems.size(); ++i) { QCOMPARE(w.topLevelItem(i)->text(0), expectedItems.at(i)); - for (const QPersistentModelIndex &p : qAsConst(persistent)) { + for (const QPersistentModelIndex &p : std::as_const(persistent)) { if (p.row() == i) // the same toplevel row QCOMPARE(p.internalPointer(), static_cast<void *>(w.topLevelItem(i))); } } - for (int k = 0; k < persistent.count(); ++k) + for (int k = 0; k < persistent.size(); ++k) QCOMPARE(persistent.at(k).row(), expectedRows.at(k)); - QCOMPARE(dataChangedSpy.count(), 1); - QCOMPARE(layoutChangedSpy.count(), reorderingExpected ? 1 : 0); + QCOMPARE(dataChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), reorderingExpected ? 1 : 0); } void tst_QTreeWidget::sizeHint_data() @@ -2644,6 +2627,12 @@ void tst_QTreeWidget::sizeHint() QFETCH(Qt::ScrollBarPolicy, scrollBarPolicy); QFETCH(QSize, viewSize); + const QString defaultStyle = QApplication::style()->name(); + QApplication::setStyle("fusion"); + const auto resetStyle = qScopeGuard([defaultStyle]{ + QApplication::setStyle(defaultStyle); + }); + QTreeWidget view; view.setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); view.setVerticalScrollBarPolicy(scrollBarPolicy); @@ -2661,6 +2650,7 @@ void tst_QTreeWidget::sizeHint() QTRY_COMPARE(view.size(), viewSize); } + QApplication::processEvents(); // execute delayed layouts auto sizeHint = view.sizeHint(); view.hide(); QCOMPARE(view.sizeHint(), sizeHint); @@ -2731,8 +2721,8 @@ void tst_QTreeWidget::sortedIndexOfChild() tw.sortItems(0, sortOrder); tw.expandAll(); - QCOMPARE(itms.count(), expectedIndexes.count()); - for (int j = 0; j < expectedIndexes.count(); ++j) + QCOMPARE(itms.size(), expectedIndexes.size()); + for (int j = 0; j < expectedIndexes.size(); ++j) QCOMPARE(top->indexOfChild(itms.at(j)), expectedIndexes.at(j)); } @@ -2757,8 +2747,8 @@ void tst_QTreeWidget::expandAndCallapse() tw.collapseItem(top); tw.collapseItem(top); - QCOMPARE(spy0.count(), 3); - QCOMPARE(spy1.count(), 2); + QCOMPARE(spy0.size(), 3); + QCOMPARE(spy1.size(), 2); } void tst_QTreeWidget::setDisabled() @@ -2871,13 +2861,13 @@ void tst_QTreeWidget::removeSelectedItem() QItemSelectionModel *selModel = w->selectionModel(); QCOMPARE(selModel->hasSelection(), true); - QCOMPARE(selModel->selectedRows().count(), 1); + QCOMPARE(selModel->selectedRows().size(), 1); const QScopedPointer<QTreeWidgetItem> taken(w->takeTopLevelItem(2)); QCOMPARE(taken->text(0), QLatin1String("C")); QCOMPARE(selModel->hasSelection(), false); - QCOMPARE(selModel->selectedRows().count(), 0); + QCOMPARE(selModel->selectedRows().size(), 0); QItemSelection sel = selModel->selection(); QCOMPARE(selModel->isSelected(w->model()->index(0,0)), false); } @@ -2988,7 +2978,7 @@ protected: auto newItem = new QTreeWidgetItem({QString::number(i++)}); m_list.append(newItem); insertTopLevelItem(0, newItem); - while (m_list.count() > 10) + while (m_list.size() > 10) delete m_list.takeFirst(); } QTreeWidget::timerEvent(event); @@ -3302,7 +3292,7 @@ void tst_QTreeWidget::emitDataChanged() auto item = new PublicTreeItem; tree.insertTopLevelItem(0, item); item->emitDataChanged(); - QCOMPARE(spy.count(), 1); + QCOMPARE(spy.size(), 1); } void tst_QTreeWidget::setCurrentItemExpandsParent() @@ -3584,6 +3574,21 @@ void tst_QTreeWidget::reparentHiddenItem() QVERIFY(grandChild->isHidden()); } +void tst_QTreeWidget::persistentChildIndex() // QTBUG-90030 +{ + QTreeWidget tree; + QTreeWidgetItem *toplevel = new QTreeWidgetItem(QStringList{QStringLiteral("toplevel")}); + tree.addTopLevelItem(toplevel); + QModelIndex firstIndex = tree.model()->index(0, 0); + QTreeWidgetItem *child1 = new QTreeWidgetItem(QStringList{QStringLiteral("child1")}); + QTreeWidgetItem *child2 = new QTreeWidgetItem(QStringList{QStringLiteral("child2")}); + toplevel->addChildren({child1, child2}); + QPersistentModelIndex persistentIdx = tree.model()->index(1, 0, firstIndex); + QCOMPARE(persistentIdx.data().toString(), QStringLiteral("child2")); + tree.model()->removeRows(0, 1, firstIndex); + QCOMPARE(persistentIdx.data().toString(), QStringLiteral("child2")); +} + #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void tst_QTreeWidget::clearItemData() { @@ -3622,5 +3627,72 @@ void tst_QTreeWidget::clearItemData() } #endif +void tst_QTreeWidget::createPersistentOnLayoutAboutToBeChanged() // QTBUG-93466 +{ + QTreeWidget widget; + QCOMPARE(widget.model()->columnCount(), 1); + widget.model()->insertRows(0, 3); + for (int row = 0; row < 3; ++row) + widget.model()->setData(widget.model()->index(row, 0), row); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(widget.model(), &QAbstractItemModel::layoutChanged); + connect(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &widget](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(widget.model()->index(row, 0)); + }); + connect(widget.model(), &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 1); + QCOMPARE(idxList.at(0).column(), 0); + QCOMPARE(idxList.at(0).data().toInt(), 0); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data().toInt(), -1); + QCOMPARE(idxList.at(2).row(), 2); + QCOMPARE(idxList.at(2).column(), 0); + QCOMPARE(idxList.at(2).data().toInt(), 2); + }); + widget.model()->setData(widget.model()->index(1, 0), -1); + widget.model()->sort(0); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + +void tst_QTreeWidget::createPersistentOnLayoutAboutToBeChangedAutoSort() // QTBUG-93466 +{ + QTreeWidget widget; + QCOMPARE(widget.model()->columnCount(), 1); + widget.model()->insertRows(0, 3); + for (int row = 0; row < 3; ++row) + widget.model()->setData(widget.model()->index(row, 0), row); + widget.sortByColumn(0, Qt::AscendingOrder); + widget.setSortingEnabled(true); + QList<QPersistentModelIndex> idxList; + QSignalSpy layoutAboutToBeChangedSpy(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged); + QSignalSpy layoutChangedSpy(widget.model(), &QAbstractItemModel::layoutChanged); + connect(widget.model(), &QAbstractItemModel::layoutAboutToBeChanged, this, [&idxList, &widget](){ + idxList.clear(); + for (int row = 0; row < 3; ++row) + idxList << QPersistentModelIndex(widget.model()->index(row, 0)); + }); + connect(widget.model(), &QAbstractItemModel::layoutChanged, this, [&idxList](){ + QCOMPARE(idxList.size(), 3); + QCOMPARE(idxList.at(0).row(), 1); + QCOMPARE(idxList.at(0).column(), 0); + QCOMPARE(idxList.at(0).data().toInt(), 0); + QCOMPARE(idxList.at(1).row(), 0); + QCOMPARE(idxList.at(1).column(), 0); + QCOMPARE(idxList.at(1).data().toInt(), -1); + QCOMPARE(idxList.at(2).row(), 2); + QCOMPARE(idxList.at(2).column(), 0); + QCOMPARE(idxList.at(2).data().toInt(), 2); + }); + widget.model()->setData(widget.model()->index(1, 0), -1); + QCOMPARE(layoutAboutToBeChangedSpy.size(), 1); + QCOMPARE(layoutChangedSpy.size(), 1); +} + QTEST_MAIN(tst_QTreeWidget) #include "tst_qtreewidget.moc" diff --git a/tests/auto/widgets/itemviews/qtreewidgetitemiterator/CMakeLists.txt b/tests/auto/widgets/itemviews/qtreewidgetitemiterator/CMakeLists.txt index ac3a0645e4..d82eaadced 100644 --- a/tests/auto/widgets/itemviews/qtreewidgetitemiterator/CMakeLists.txt +++ b/tests/auto/widgets/itemviews/qtreewidgetitemiterator/CMakeLists.txt @@ -1,13 +1,20 @@ -# Generated from qtreewidgetitemiterator.pro. +# Copyright (C) 2022 The Qt Company Ltd. +# SPDX-License-Identifier: BSD-3-Clause ##################################################################### ## tst_qtreewidgetitemiterator Test: ##################################################################### -qt_add_test(tst_qtreewidgetitemiterator +if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) + cmake_minimum_required(VERSION 3.16) + project(tst_qtreewidgetitemiterator LANGUAGES CXX) + find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) +endif() + +qt_internal_add_test(tst_qtreewidgetitemiterator SOURCES tst_qtreewidgetitemiterator.cpp - PUBLIC_LIBRARIES + LIBRARIES Qt::Gui Qt::Widgets ) diff --git a/tests/auto/widgets/itemviews/qtreewidgetitemiterator/qtreewidgetitemiterator.pro b/tests/auto/widgets/itemviews/qtreewidgetitemiterator/qtreewidgetitemiterator.pro deleted file mode 100644 index 42a00618a2..0000000000 --- a/tests/auto/widgets/itemviews/qtreewidgetitemiterator/qtreewidgetitemiterator.pro +++ /dev/null @@ -1,6 +0,0 @@ -CONFIG += testcase -TARGET = tst_qtreewidgetitemiterator -QT += widgets testlib -SOURCES += tst_qtreewidgetitemiterator.cpp - - diff --git a/tests/auto/widgets/itemviews/qtreewidgetitemiterator/tst_qtreewidgetitemiterator.cpp b/tests/auto/widgets/itemviews/qtreewidgetitemiterator/tst_qtreewidgetitemiterator.cpp index 68d149fc6e..a650eb229e 100644 --- a/tests/auto/widgets/itemviews/qtreewidgetitemiterator/tst_qtreewidgetitemiterator.cpp +++ b/tests/auto/widgets/itemviews/qtreewidgetitemiterator/tst_qtreewidgetitemiterator.cpp @@ -1,30 +1,5 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the test suite of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QTreeWidget> @@ -891,7 +866,7 @@ void tst_QTreeWidgetItemIterator::iteratorflags() QTreeWidgetItemIterator it(testWidget, iteratorflags); it += start; int iMatch = 0; - while (*it && iMatch < matches.count()) { + while (*it && iMatch < matches.size()) { QTreeWidgetItem *item = *it; QCOMPARE(item->text(0), matches[iMatch]); ++it; |