diff options
Diffstat (limited to 'tests/auto/quick/qquicktableview/tst_qquicktableview.cpp')
-rw-r--r-- | tests/auto/quick/qquicktableview/tst_qquicktableview.cpp | 663 |
1 files changed, 608 insertions, 55 deletions
diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index ecea4adfd5..56dee4b585 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -1,5 +1,5 @@ // Copyright (C) 2018 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include <QtTest/QtTest> #include <QtQuickTest/quicktest.h> @@ -87,6 +87,7 @@ private slots: void checkZeroSizedDelegate(); void checkImplicitSizeDelegate(); void checkZeroSizedTableView(); + void checkZeroSizedViewPort(); void checkColumnWidthWithoutProvider(); void checkColumnWidthAndRowHeightFunctions(); void checkDelegateWithAnchors(); @@ -103,7 +104,7 @@ private slots: void isColumnLoadedAndIsRowLoaded(); void checkForceLayoutFunction(); void checkForceLayoutEndUpDoingALayout(); - void checkForceLayoutDuringModelChange(); + void checkForceLayoutInbetweenAddingRowsToModel(); void checkForceLayoutWhenAllItemsAreHidden(); void checkLayoutChangedSignal(); void checkContentWidthAndHeight(); @@ -167,6 +168,7 @@ private slots: void checkSyncView_pageFlicking(); void checkSyncView_emptyModel(); void checkSyncView_topLeftChanged(); + void checkSyncView_unloadHeader(); void delegateWithRequiredProperties(); void checkThatFetchMoreIsCalledWhenScrolledToTheEndOfTable(); void replaceModel(); @@ -188,6 +190,10 @@ private slots: void positionViewAtCellForLargeCells_data(); void positionViewAtCellForLargeCells(); void positionViewAtCellForLargeCellsUsingSubrect(); + void positionViewAtLastRow_data(); + void positionViewAtLastRow(); + void positionViewAtLastColumn_data(); + void positionViewAtLastColumn(); void itemAtCell_data(); void itemAtCell(); void leftRightTopBottomProperties_data(); @@ -208,6 +214,7 @@ private slots: void testSelectableStartPosEndPosOutsideView(); void testSelectableScrollTowardsPos(); void setCurrentIndexFromSelectionModel(); + void clearSelectionOnTap_data(); void clearSelectionOnTap(); void moveCurrentIndexUsingArrowKeys(); void moveCurrentIndexUsingHomeAndEndKeys(); @@ -244,6 +251,7 @@ private slots: void deletedDelegate(); void columnResizing_data(); void columnResizing(); + void tableViewInteractive(); void rowResizing_data(); void rowResizing(); void rowAndColumnResizing_data(); @@ -267,6 +275,17 @@ private slots: void editWarning_nonEditableModelItem(); void attachedPropertiesOnEditDelegate(); void requiredPropertiesOnEditDelegate(); + void resettingRolesRespected(); + void checkScroll_data(); + void checkScroll(); + void checkRebuildJsModel(); + + // Row and column reordering + void checkVisualRowColumnAfterReorder(); + void checkColumnRowSizeAfterReorder(); + void checkCellModelIdxAfterReorder(); + void checkEditAfterReorder(); + void checkSelectionAfterReorder(); }; tst_QQuickTableView::tst_QQuickTableView() @@ -300,6 +319,18 @@ QPoint tst_QQuickTableView::getContextRowAndColumn(const QQuickItem *item) const return QPoint(column, row); } +static void sendWheelEvent(QQuickWindow *window, QPoint pos, QPoint angleDelta, + QPoint pixelDelta, + Qt::KeyboardModifiers modifiers = Qt::NoModifier, + Qt::ScrollPhase phase = Qt::NoScrollPhase, + bool inverted = false) { + QWheelEvent wheelEvent(pos, window->mapToGlobal(pos), pixelDelta, + angleDelta, Qt::NoButton, modifiers, phase, + inverted); + QGuiApplication::sendEvent(window, &wheelEvent); + qApp->processEvents(); +} + void tst_QQuickTableView::setAndGetModel_data() { QTest::addColumn<QVariant>("model"); @@ -443,7 +474,7 @@ void tst_QQuickTableView::checkZeroSizedTableView() WAIT_UNTIL_POLISHED; QCOMPARE(tableViewPrivate->loadedItems.size(), 2); - const auto item = tableView->itemAtCell(0, 0); + const auto item = tableView->itemAtIndex(tableView->index(0, 0)); QVERIFY(item); QCOMPARE(item->width(), 200); @@ -457,6 +488,17 @@ void tst_QQuickTableView::checkZeroSizedTableView() QCOMPARE(tableViewPrivate->loadedItems.size(), 1); } +void tst_QQuickTableView::checkZeroSizedViewPort() +{ + LOAD_TABLEVIEW("zerosizedviewport.qml"); + + auto model = TestModelAsVariant(20, 20); + tableView->setModel(model); + + WAIT_UNTIL_POLISHED; + + QVERIFY(!tableViewPrivate->loadedItems.isEmpty()); +} void tst_QQuickTableView::checkColumnWidthWithoutProvider() { @@ -794,10 +836,11 @@ void tst_QQuickTableView::checkForceLayoutEndUpDoingALayout() QCOMPARE(tableView->contentHeight(), (9 * (newDelegateSize + rowSpacing)) - rowSpacing); } -void tst_QQuickTableView::checkForceLayoutDuringModelChange() +void tst_QQuickTableView::checkForceLayoutInbetweenAddingRowsToModel() { - // Check that TableView doesn't assert if we call - // forceLayout() in the middle of a model change. + // Check that TableView doesn't assert if we call forceLayout() while waiting + // for a callback from the model that the row count has changed. Also make sure + // that we don't move the contentItem while doing so. LOAD_TABLEVIEW("plaintableview.qml"); const int initialRowCount = 10; @@ -812,9 +855,13 @@ void tst_QQuickTableView::checkForceLayoutDuringModelChange() WAIT_UNTIL_POLISHED; + const int contentY = 10; + tableView->setContentY(contentY); QCOMPARE(tableView->rows(), initialRowCount); + QCOMPARE(tableView->contentY(), contentY); model.addRow(0); QCOMPARE(tableView->rows(), initialRowCount + 1); + QCOMPARE(tableView->contentY(), contentY); } void tst_QQuickTableView::checkForceLayoutWhenAllItemsAreHidden() @@ -2451,7 +2498,7 @@ void tst_QQuickTableView::checkChangingModelFromDelegate() // And since the QML code tried to add another row as well, we // expect rebuildScheduled to be true, and a polish event to be pending. QVERIFY(tableViewPrivate->scheduledRebuildOptions); - QCOMPARE(tableViewPrivate->polishScheduled, true); + QVERIFY(tableViewPrivate->polishScheduled); WAIT_UNTIL_POLISHED; // After handling the polish event, we expect also the third row to now be added @@ -3253,6 +3300,21 @@ void tst_QQuickTableView::checkSyncView_emptyModel() QCOMPARE(tableViewVPrivate->loadedTableOuterRect.left(), 0); } +void tst_QQuickTableView::checkSyncView_unloadHeader() +{ + // Check that we don't get a crash in TableView if one + // of the sync children is suddenly deleted (from e.g a Loader). + LOAD_TABLEVIEW("unloadheader.qml"); + + const auto loader = view->rootObject()->property("loader").value<QQuickLoader *>(); + QVERIFY(loader); + QVERIFY(loader->item()); + loader->setActive(false); + QVERIFY(!loader->item()); + gc(*qmlEngine(tableView)); + tableView->forceLayout(); +} + void tst_QQuickTableView::checkSyncView_topLeftChanged() { LOAD_TABLEVIEW("syncviewsimple.qml"); @@ -3770,14 +3832,15 @@ void tst_QQuickTableView::positionViewAtCellWithAnimation() QPoint cell(tableView->rightColumn(), tableView->bottomRow()); const QRectF cellGeometry = tableViewPrivate->loadedTableItem(cell)->geometry(); - const int modelIndex = tableViewPrivate->modelIndexAtCell(cell); + const int serializedIndex = tableViewPrivate->modelIndexAtCell(cell); + const QModelIndex index = tableView->index(cell.y(), cell.x()); - QVERIFY(tableViewPrivate->loadedItems.contains(modelIndex)); + QVERIFY(tableViewPrivate->loadedItems.contains(serializedIndex)); QVERIFY(!tableViewPrivate->positionXAnimation.isRunning()); QVERIFY(!tableViewPrivate->positionYAnimation.isRunning()); // Animate the cell to the top left location in the view - tableView->positionViewAtCell(cell, QQuickTableView::AlignTop | QQuickTableView::AlignLeft); + tableView->positionViewAtIndex(index, QQuickTableView::AlignTop | QQuickTableView::AlignLeft); // Wait for animation to finish QVERIFY(tableViewPrivate->positionXAnimation.isRunning()); @@ -3786,13 +3849,13 @@ void tst_QQuickTableView::positionViewAtCellWithAnimation() QTRY_COMPARE(tableViewPrivate->positionYAnimation.isRunning(), false); // Check that the cell is now placed in the top left corner - QVERIFY(tableViewPrivate->loadedItems.contains(modelIndex)); + QVERIFY(tableViewPrivate->loadedItems.contains(serializedIndex)); QPointF expectedPos = tableView->mapToItem(tableView->contentItem(), QPointF(0, 0)); QCOMPARE(cellGeometry.x(), expectedPos.x()); QCOMPARE(cellGeometry.y(), expectedPos.y()); // Animate the cell to the top right location in the view - tableView->positionViewAtCell(cell, QQuickTableView::AlignTop | QQuickTableView::AlignRight); + tableView->positionViewAtIndex(index, QQuickTableView::AlignTop | QQuickTableView::AlignRight); // Wait for animation to finish QVERIFY(tableViewPrivate->positionXAnimation.isRunning()); @@ -3800,13 +3863,13 @@ void tst_QQuickTableView::positionViewAtCellWithAnimation() QTRY_COMPARE(tableViewPrivate->positionXAnimation.isRunning(), false); // Check that the cell is now placed in the top right corner - QVERIFY(tableViewPrivate->loadedItems.contains(modelIndex)); + QVERIFY(tableViewPrivate->loadedItems.contains(serializedIndex)); expectedPos = tableView->mapToItem(tableView->contentItem(), QPointF(tableView->width(), 0)); QCOMPARE(cellGeometry.right(), expectedPos.x()); QCOMPARE(cellGeometry.y(), expectedPos.y()); // Animate the cell to the bottom left location in the view - tableView->positionViewAtCell(cell, QQuickTableView::AlignBottom | QQuickTableView::AlignLeft); + tableView->positionViewAtIndex(index, QQuickTableView::AlignBottom | QQuickTableView::AlignLeft); // Wait for animation to finish QVERIFY(tableViewPrivate->positionXAnimation.isRunning()); @@ -3815,7 +3878,7 @@ void tst_QQuickTableView::positionViewAtCellWithAnimation() QTRY_COMPARE(tableViewPrivate->positionYAnimation.isRunning(), false); // Check that the cell is now placed in the bottom left corner - QVERIFY(tableViewPrivate->loadedItems.contains(modelIndex)); + QVERIFY(tableViewPrivate->loadedItems.contains(serializedIndex)); expectedPos = tableView->mapToItem(tableView->contentItem(), QPointF(0, tableView->height())); QCOMPARE(cellGeometry.x(), expectedPos.x()); QCOMPARE(cellGeometry.bottom(), expectedPos.y()); @@ -3829,7 +3892,7 @@ void tst_QQuickTableView::positionViewAtCellWithAnimation() QTRY_COMPARE(tableViewPrivate->positionXAnimation.isRunning(), false); // Check that the cell is now placed in the bottom right corner - QVERIFY(tableViewPrivate->loadedItems.contains(modelIndex)); + QVERIFY(tableViewPrivate->loadedItems.contains(serializedIndex)); expectedPos = tableView->mapToItem(tableView->contentItem(), QPointF(tableView->width(), tableView->height())); QCOMPARE(cellGeometry.right(), expectedPos.x()); QCOMPARE(cellGeometry.bottom(), expectedPos.y()); @@ -4070,6 +4133,122 @@ void tst_QQuickTableView::positionViewAtCellForLargeCellsUsingSubrect() QCOMPARE(tableView->contentY(), -(tableView->height() - subRect.bottom())); } +void tst_QQuickTableView::positionViewAtLastRow_data() +{ + QTest::addColumn<QString>("signalToTest"); + QTest::addColumn<bool>("animate"); + + QTest::newRow("positionOnRowsChanged, animate=false") << "positionOnRowsChanged" << false; + QTest::newRow("positionOnRowsChanged, animate=true") << "positionOnRowsChanged" << true; + QTest::newRow("positionOnContentHeightChanged, animate=false") << "positionOnContentHeightChanged" << false; + QTest::newRow("positionOnContentHeightChanged, animate=true") << "positionOnContentHeightChanged" << true; +} + +void tst_QQuickTableView::positionViewAtLastRow() +{ + // Check that we can make TableView always scroll to the + // last row in the model by positioning the view upon + // a rowsChanged callback + QFETCH(QString, signalToTest); + QFETCH(bool, animate); + + LOAD_TABLEVIEW("positionlast.qml"); + + // Use a very large model to indirectly test that we "fast-flick" to + // the end at start-up (instead of loading and unloading rows, which + // would take forever). + TestModel model(2000000, 2000000); + tableView->setModel(QVariant::fromValue(&model)); + tableView->setAnimate(animate); + + view->rootObject()->setProperty(signalToTest.toUtf8().constData(), true); + + WAIT_UNTIL_POLISHED; + + const qreal delegateSize = 100.; + const qreal viewportRowCount = tableView->height() / delegateSize; + + // Check that the viewport is positioned at the last row at start-up + QCOMPARE(tableView->rows(), model.rowCount()); + QCOMPARE(tableView->bottomRow(), model.rowCount() - 1); + QCOMPARE(tableView->contentY(), (model.rowCount() - viewportRowCount) * delegateSize); + + // Check that the viewport is positioned at the last + // row after more rows are added. + for (int row = 0; row < 2; ++row) { + model.addRow(model.rowCount() - 1); + + WAIT_UNTIL_POLISHED; + + if (tableView->animate()) { + QVERIFY(tableViewPrivate->positionYAnimation.isRunning()); + QTRY_VERIFY(!tableViewPrivate->positionYAnimation.isRunning()); + } + + QCOMPARE(tableView->rows(), model.rowCount()); + QCOMPARE(tableView->bottomRow(), model.rowCount() - 1); + QCOMPARE(tableView->contentY(), (model.rowCount() - viewportRowCount) * delegateSize); + } +} + +void tst_QQuickTableView::positionViewAtLastColumn_data() +{ + QTest::addColumn<QString>("signalToTest"); + QTest::addColumn<bool>("animate"); + + QTest::newRow("positionOnColumnsChanged, animate=false") << "positionOnColumnsChanged" << false; + QTest::newRow("positionOnColumnsChanged, animate=true") << "positionOnColumnsChanged" << true; + QTest::newRow("positionOnContentWidthChanged, animate=false") << "positionOnContentWidthChanged" << false; + QTest::newRow("positionOnContentWidthChanged, animate=true") << "positionOnContentWidthChanged" << true; +} + +void tst_QQuickTableView::positionViewAtLastColumn() +{ + // Check that we can make TableView always scroll to the + // last column in the model by positioning the view upon + // a columnsChanged callback + QFETCH(QString, signalToTest); + QFETCH(bool, animate); + + LOAD_TABLEVIEW("positionlast.qml"); + + // Use a very large model to indirectly test that we "fast-flick" to + // the end at start-up (instead of loading and unloading columns, which + // would take forever). + TestModel model(2000000, 2000000); + tableView->setModel(QVariant::fromValue(&model)); + tableView->setAnimate(animate); + + view->rootObject()->setProperty(signalToTest.toUtf8().constData(), true); + + WAIT_UNTIL_POLISHED; + + const qreal delegateSize = 100.; + const qreal viewportColumnCount = tableView->width() / delegateSize; + + // Check that the viewport is positioned at the last column at start-up + QCOMPARE(tableView->columns(), model.columnCount()); + QCOMPARE(tableView->rightColumn(), model.columnCount() - 1); + QCOMPARE(tableView->contentX(), (model.columnCount() - viewportColumnCount) * delegateSize); + + // Check that the viewport is positioned at the last + // column after more columns are added. + for (int column = 0; column < 2; ++column) { + model.addColumn(model.columnCount() - 1); + + WAIT_UNTIL_POLISHED; + + if (tableView->animate()) { + QVERIFY(tableViewPrivate->positionXAnimation.isRunning()); + QTRY_VERIFY(!tableViewPrivate->positionXAnimation.isRunning()); + } + + QCOMPARE(tableView->columns(), model.columnCount()); + QCOMPARE(tableView->rightColumn(), model.columnCount() - 1); + QCOMPARE(tableView->contentX(), (model.columnCount() - viewportColumnCount) * delegateSize); + } +} + void tst_QQuickTableView::itemAtCell_data() { QTest::addColumn<QPoint>("cell"); @@ -4378,7 +4557,7 @@ void tst_QQuickTableView::warnOnWrongModelInSelectionModel() selectionModel.setModel(&model2); // And change currentIndex. This will produce a warning. - QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*model differs.*")); + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*TableView.selectionModel.model.*")); selectionModel.setCurrentIndex(model2.index(0, 0), QItemSelectionModel::NoUpdate); } @@ -4410,9 +4589,10 @@ void tst_QQuickTableView::selectionBehaviorCells_data() void tst_QQuickTableView::selectionBehaviorCells() { - // Check that the TableView implement QQuickSelectableInterface setSelectionStartPos, setSelectionEndPos - // and clearSelection correctly. Do this by calling setSelectionStartPos/setSelectionEndPos on top of - // different cells, and see that we end up with the expected selections. + // Check that the TableView implement QQuickSelectableInterface startSelection, + // setSelectionStartPos, setSelectionEndPos and clearSelection correctly. Do this by + // calling setSelectionStartPos/setSelectionEndPos on top of different cells, and see + // that we end up with the expected selections. QFETCH(QPoint, endCellDist); LOAD_TABLEVIEW("tableviewwithselected1.qml"); @@ -4442,6 +4622,7 @@ void tst_QQuickTableView::selectionBehaviorCells() const QPointF endPos(endItem->x(), endItem->y()); const QPointF endPosWrapped(endItemWrapped->x(), endItemWrapped->y()); + QVERIFY(tableViewPrivate->startSelection(startPos, Qt::NoModifier)); tableViewPrivate->setSelectionStartPos(startPos); tableViewPrivate->setSelectionEndPos(endPos); @@ -4482,8 +4663,9 @@ void tst_QQuickTableView::selectionBehaviorCells() void tst_QQuickTableView::selectionBehaviorRows() { - // Check that the TableView implement QQuickSelectableInterface setSelectionStartPos, setSelectionEndPos - // and clearSelection correctly for QQuickTableView::SelectRows. + // Check that the TableView implement QQuickSelectableInterface startSelection, + // setSelectionStartPos, setSelectionEndPos and clearSelection correctly for + // QQuickTableView::SelectRows. LOAD_TABLEVIEW("tableviewwithselected1.qml"); TestModel model(10, 10); @@ -4498,6 +4680,7 @@ void tst_QQuickTableView::selectionBehaviorRows() QCOMPARE(selectionModel.hasSelection(), false); // Drag from row 0 to row 3 + QVERIFY(tableViewPrivate->startSelection(QPointF(0, 0), Qt::NoModifier)); tableViewPrivate->setSelectionStartPos(QPointF(0, 0)); tableViewPrivate->setSelectionEndPos(QPointF(60, 60)); @@ -4518,6 +4701,7 @@ void tst_QQuickTableView::selectionBehaviorRows() QCOMPARE(selectionModel.hasSelection(), false); // Drag from row 3 to row 0 (and overshoot mouse) + QVERIFY(tableViewPrivate->startSelection(QPointF(60, 60), Qt::NoModifier)); tableViewPrivate->setSelectionStartPos(QPointF(60, 60)); tableViewPrivate->setSelectionEndPos(QPointF(-10, -10)); @@ -4536,8 +4720,9 @@ void tst_QQuickTableView::selectionBehaviorRows() void tst_QQuickTableView::selectionBehaviorColumns() { - // Check that the TableView implement QQuickSelectableInterface setSelectionStartPos, setSelectionEndPos - // and clearSelection correctly for QQuickTableView::SelectColumns. + // Check that the TableView implement QQuickSelectableInterface startSelection, + // setSelectionStartPos, setSelectionEndPos and clearSelection correctly for + // QQuickTableView::SelectColumns. LOAD_TABLEVIEW("tableviewwithselected1.qml"); TestModel model(10, 10); @@ -4552,6 +4737,7 @@ void tst_QQuickTableView::selectionBehaviorColumns() QCOMPARE(selectionModel.hasSelection(), false); // Drag from column 0 to column 3 + QVERIFY(tableViewPrivate->startSelection(QPointF(60, 60), Qt::NoModifier)); tableViewPrivate->setSelectionStartPos(QPointF(0, 0)); tableViewPrivate->setSelectionEndPos(QPointF(60, 60)); @@ -4572,6 +4758,7 @@ void tst_QQuickTableView::selectionBehaviorColumns() QCOMPARE(selectionModel.hasSelection(), false); // Drag from column 3 to column 0 (and overshoot mouse) + QVERIFY(tableViewPrivate->startSelection(QPointF(60, 60), Qt::NoModifier)); tableViewPrivate->setSelectionStartPos(QPointF(60, 60)); tableViewPrivate->setSelectionEndPos(QPointF(-10, -10)); @@ -4590,8 +4777,8 @@ void tst_QQuickTableView::selectionBehaviorColumns() void tst_QQuickTableView::selectionBehaviorDisabled() { - // Check that the TableView implement QQuickSelectableInterface setSelectionStartPos, setSelectionEndPos - // and clearSelection correctly for QQuickTableView::SelectionDisabled. + // Check that the TableView implement QQuickSelectableInterface startSelection + // correctly for QQuickTableView::SelectionDisabled. LOAD_TABLEVIEW("tableviewwithselected1.qml"); TestModel model(10, 10); @@ -4605,18 +4792,18 @@ void tst_QQuickTableView::selectionBehaviorDisabled() QCOMPARE(selectionModel.hasSelection(), false); - // Drag from column 0 to column 3 - tableViewPrivate->setSelectionStartPos(QPointF(0, 0)); - tableViewPrivate->setSelectionEndPos(QPointF(60, 60)); + // Try to start a selection + QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*TableView.SelectionDisabled")); + QVERIFY(!tableViewPrivate->startSelection(QPointF(0, 0), Qt::NoModifier)); QCOMPARE(selectionModel.hasSelection(), false); } void tst_QQuickTableView::testSelectableStartPosEndPosOutsideView() { - // Call setSelectionStartPos and setSelectionEndPos with positions outside the view. - // This should first of all not crash, but instead just clamp the selection to the - // cells that are visible inside the view. + // Call startSelection, setSelectionStartPos and setSelectionEndPos with positions + // outside the view. This should first of all not crash, but instead just clamp the + // selection to the cells that are visible inside the view. LOAD_TABLEVIEW("tableviewwithselected1.qml"); TestModel model(10, 10); @@ -4637,6 +4824,7 @@ void tst_QQuickTableView::testSelectableStartPosEndPosOutsideView() const QPointF outsideTop(centerPos.x(), -100); const QPointF outsideBottom(centerPos.x(), tableView->height() + 100); + QVERIFY(tableViewPrivate->startSelection(centerPos, Qt::NoModifier)); tableViewPrivate->setSelectionStartPos(centerPos); tableViewPrivate->setSelectionEndPos(outsideLeft); @@ -4687,23 +4875,23 @@ void tst_QQuickTableView::testSelectableScrollTowardsPos() const QPointF bottomLeft(-100, tableView->height() + 100); const QPointF bottomRight(tableView->width() + 100, tableView->height() + 100); - tableViewPrivate->scrollTowardsSelectionPoint(topRight, step); + tableViewPrivate->scrollTowardsPoint(topRight, step); QCOMPARE(tableView->contentX(), step.width()); QCOMPARE(tableView->contentY(), 0); - tableViewPrivate->scrollTowardsSelectionPoint(bottomRight, step); + tableViewPrivate->scrollTowardsPoint(bottomRight, step); QCOMPARE(tableView->contentX(), step.width() * 2); QCOMPARE(tableView->contentY(), step.height()); - tableViewPrivate->scrollTowardsSelectionPoint(bottomLeft, step); + tableViewPrivate->scrollTowardsPoint(bottomLeft, step); QCOMPARE(tableView->contentX(), step.width()); QCOMPARE(tableView->contentY(), step.height() * 2); - tableViewPrivate->scrollTowardsSelectionPoint(topLeft, step); + tableViewPrivate->scrollTowardsPoint(topLeft, step); QCOMPARE(tableView->contentX(), 0); QCOMPARE(tableView->contentY(), step.height()); - tableViewPrivate->scrollTowardsSelectionPoint(topLeft, step); + tableViewPrivate->scrollTowardsPoint(topLeft, step); QCOMPARE(tableView->contentX(), 0); QCOMPARE(tableView->contentY(), 0); } @@ -4743,12 +4931,25 @@ void tst_QQuickTableView::setCurrentIndexFromSelectionModel() QVERIFY(tableView->itemAtCell(cellAtEnd)->property(kCurrent).toBool()); } +void tst_QQuickTableView::clearSelectionOnTap_data() +{ + QTest::addColumn<bool>("selectionEnabled"); + QTest::newRow("selections enabled") << true; + QTest::newRow("selections disabled") << false; +} + void tst_QQuickTableView::clearSelectionOnTap() { + // Check that we clear the current selection when tapping + // inside TableView. But only if TableView has selections + // enabled. Otherwise, TableView should not touch the selection model. + QFETCH(bool, selectionEnabled); LOAD_TABLEVIEW("tableviewwithselected2.qml"); TestModel model(40, 40); tableView->setModel(QVariant::fromValue(&model)); + if (!selectionEnabled) + tableView->setSelectionBehavior(QQuickTableView::SelectionDisabled); WAIT_UNTIL_POLISHED; @@ -4757,13 +4958,14 @@ void tst_QQuickTableView::clearSelectionOnTap() tableView->selectionModel()->select(index, QItemSelectionModel::Select); QCOMPARE(tableView->selectionModel()->selectedIndexes().size(), 1); - // Click on a cell. This should remove the selection - const auto item = tableView->itemAtCell(0, 0); + // Click on a cell + const auto item = tableView->itemAtIndex(tableView->index(0, 0)); QVERIFY(item); QPoint localPos = QPoint(item->width() / 2, item->height() / 2); QPoint pos = item->window()->contentItem()->mapFromItem(item, localPos).toPoint(); QTest::mouseClick(item->window(), Qt::LeftButton, Qt::NoModifier, pos); - QCOMPARE(tableView->selectionModel()->selectedIndexes().size(), 0); + + QCOMPARE(tableView->selectionModel()->hasSelection(), !selectionEnabled); } void tst_QQuickTableView::moveCurrentIndexUsingArrowKeys() @@ -5358,7 +5560,7 @@ void tst_QQuickTableView::disablePointerNavigation() // Enable navigation, and try again tableView->setPointerNavigationEnabled(true); QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, pos); - QCOMPARE(selectionModel.currentIndex(), tableView->modelIndex(0, 0)); + QCOMPARE(selectionModel.currentIndex(), tableView->index(0, 0)); QVERIFY(item0_0->property("current").toBool()); QCOMPARE(tableView->currentColumn(), cell0_0.x()); QCOMPARE(tableView->currentRow(), cell0_0.y()); @@ -5754,7 +5956,7 @@ void tst_QQuickTableView::boundDelegateComponent() QVERIFY(inner != nullptr); QQuickTableView *tableView = qobject_cast<QQuickTableView *>(inner); QVERIFY(tableView != nullptr); - QObject *item = tableView->itemAtCell(0, 0); + QObject *item = tableView->itemAtCell({0, 0}); QVERIFY(item); QCOMPARE(item->objectName(), QLatin1String("fooouterundefined")); @@ -5762,7 +5964,7 @@ void tst_QQuickTableView::boundDelegateComponent() QVERIFY(inner2 != nullptr); QQuickTableView *tableView2 = qobject_cast<QQuickTableView *>(inner2); QVERIFY(tableView2 != nullptr); - QObject *item2 = tableView2->itemAtCell(0, 0); + QObject *item2 = tableView2->itemAtCell({0, 0}); QVERIFY(item2); QCOMPARE(item2->objectName(), QLatin1String("fooouter0")); @@ -5784,7 +5986,7 @@ void tst_QQuickTableView::boundDelegateComponent() QVERIFY(innerTableView != nullptr); QCOMPARE(innerTableView->rows(), 3); for (int i = 0; i < 3; ++i) - QVERIFY(innerTableView->itemAtCell(0, i)->objectName().isEmpty()); + QVERIFY(innerTableView->itemAtIndex(innerTableView->index(i, 0))->objectName().isEmpty()); } void tst_QQuickTableView::setColumnWidth_data() @@ -6127,6 +6329,47 @@ void tst_QQuickTableView::deletedDelegate() QTRY_COMPARE(tv->delegate(), nullptr); } +void tst_QQuickTableView::tableViewInteractive() +{ + LOAD_TABLEVIEW("tableviewinteractive.qml"); + + auto *root = view->rootObject(); + QVERIFY(root); + auto *window = root->window(); + QVERIFY(window); + + int eventCount = root->property("eventCount").toInt(); + QCOMPARE(eventCount, 0); + + // Event though we make 'interactive' as false, the TableView has + // pointerNacigationEnabled set as true by default, which allows it to consume + // mouse events and thus, eventCount still be zero + tableView->setInteractive(false); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(100, 100)); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(100, 100)); + eventCount = root->property("eventCount").toInt(); + QCOMPARE(eventCount, 0); + + // Making both 'interactive' and 'pointerNavigationEnabled' as false, doesn't + // allow TableView (and its parent Flickable) to consume mouse event and it + // passes to the below visual item + tableView->setInteractive(false); + tableView->setPointerNavigationEnabled(false); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(100, 100)); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(100, 100)); + eventCount = root->property("eventCount").toInt(); + QCOMPARE(eventCount, 1); + + // Making 'interactive' as true and 'pointerNavigationEnabled' as false, + // allows parent of TableView (i.e. Flickable) to consume mouse events + tableView->setInteractive(true); + tableView->setPointerNavigationEnabled(false); + QTest::mousePress(window, Qt::LeftButton, Qt::NoModifier, QPoint(100, 100)); + QTest::mouseRelease(window, Qt::LeftButton, Qt::NoModifier, QPoint(100, 100)); + eventCount = root->property("eventCount").toInt(); + QCOMPARE(eventCount, 1); +} + void tst_QQuickTableView::columnResizing_data() { QTest::addColumn<int>("column"); @@ -6161,7 +6404,7 @@ void tst_QQuickTableView::columnResizing() QSignalSpy currentIndexSpy(tableView->selectionModel(), &QItemSelectionModel::currentChanged); QSignalSpy selectionSpy(tableView->selectionModel(), &QItemSelectionModel::selectionChanged); - const auto item = tableView->itemAtCell(column, 0); + const auto item = tableView->itemAtIndex(tableView->index(0, column)); QQuickWindow *window = item->window(); const qreal columnStartWidth = tableView->columnWidth(column); @@ -6212,7 +6455,7 @@ void tst_QQuickTableView::rowResizing() QSignalSpy currentIndexSpy(tableView->selectionModel(), &QItemSelectionModel::currentChanged); QSignalSpy selectionSpy(tableView->selectionModel(), &QItemSelectionModel::selectionChanged); - const auto item = tableView->itemAtCell(0, row); + const auto item = tableView->itemAtIndex(tableView->index(row, 0)); QQuickWindow *window = item->window(); const qreal rowStartHeight = tableView->rowHeight(row); @@ -6265,7 +6508,7 @@ void tst_QQuickTableView::rowAndColumnResizing() QCOMPARE(tableView->explicitColumnWidth(rowAndColumn), -1); QCOMPARE(tableView->explicitRowHeight(rowAndColumn), -1); - const auto item = tableView->itemAtCell(rowAndColumn, rowAndColumn); + const auto item = tableView->itemAtIndex(tableView->index(rowAndColumn, rowAndColumn)); QVERIFY(item); if (addDelegateDragHandler) { @@ -6320,7 +6563,7 @@ void tst_QQuickTableView::columnResizingDisabled() const int row = 1; QCOMPARE(tableView->explicitRowHeight(row), -1); - const auto item = tableView->itemAtCell(0, row); + const auto item = tableView->itemAtIndex(tableView->index(row, 0)); QQuickWindow *window = item->window(); const QPoint localPos = QPoint(item->width() / 2, item->height()); @@ -6357,7 +6600,7 @@ void tst_QQuickTableView::rowResizingDisabled() const int row = 1; QCOMPARE(tableView->explicitRowHeight(row), -1); - const auto item = tableView->itemAtCell(0, row); + const auto item = tableView->itemAtIndex(tableView->index(row, 0)); QQuickWindow *window = item->window(); const QPoint localPos = QPoint(item->width() / 2, item->height()); @@ -6395,7 +6638,7 @@ void tst_QQuickTableView::dragFromCellCenter() const int row = 1; QCOMPARE(tableView->explicitRowHeight(row), -1); - const auto item = tableView->itemAtCell(0, row); + const auto item = tableView->itemAtIndex(tableView->index(row, 0)); QQuickWindow *window = item->window(); const QPoint localPos = QPoint(item->width() / 2, item->height() / 2); @@ -6590,7 +6833,7 @@ void tst_QQuickTableView::editUsingEditTriggers() if (editTriggers & QQuickTableView::SelectedTapped) { // select cell first, then tap on it - tableView->selectionModel()->setCurrentIndex(index1, QItemSelectionModel::NoUpdate); + tableView->selectionModel()->setCurrentIndex(index1, QItemSelectionModel::Select); QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos1); QCOMPARE(tableView->selectionModel()->currentIndex(), index1); const auto editItem1 = tableView->property(kEditItem).value<QQuickItem *>(); @@ -6611,6 +6854,11 @@ void tst_QQuickTableView::editUsingEditTriggers() QVERIFY(!tableView->property(kEditItem).value<QQuickItem *>()); QVERIFY(!tableView->property(kEditIndex).value<QModelIndex>().isValid()); QCOMPARE(tableView->selectionModel()->currentIndex(), index2); + + // tap on the current cell. This alone should not start an edit (unless it's also selected) + QTest::mouseClick(window, Qt::LeftButton, Qt::NoModifier, tapPos1); + QVERIFY(!tableView->property(kEditItem).value<QQuickItem *>()); + QVERIFY(!tableView->property(kEditIndex).value<QModelIndex>().isValid()); } if (editTriggers & QQuickTableView::EditKeyPressed) { @@ -7075,7 +7323,7 @@ void tst_QQuickTableView::editWarning_noEditDelegate() WAIT_UNTIL_POLISHED; QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*cannot edit: no TableView.editDelegate set!")); - tableView->edit(tableView->modelIndex(1, 1)); + tableView->edit(tableView->index(1, 1)); } void tst_QQuickTableView::editWarning_invalidIndex() @@ -7090,7 +7338,7 @@ void tst_QQuickTableView::editWarning_invalidIndex() WAIT_UNTIL_POLISHED; QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*cannot edit: index is not valid!")); - tableView->edit(tableView->modelIndex(-1, -1)); + tableView->edit(tableView->index(-1, -1)); } void tst_QQuickTableView::editWarning_nonEditableModelItem() @@ -7108,7 +7356,7 @@ void tst_QQuickTableView::editWarning_nonEditableModelItem() WAIT_UNTIL_POLISHED; QTest::ignoreMessage(QtWarningMsg, QRegularExpression(".*cannot edit:.*flags.*Qt::ItemIsEditable")); - tableView->edit(tableView->modelIndex(1, 1)); + tableView->edit(tableView->index(1, 1)); } void tst_QQuickTableView::attachedPropertiesOnEditDelegate() @@ -7214,7 +7462,7 @@ void tst_QQuickTableView::requiredPropertiesOnEditDelegate() const QPoint cell(1, 1); const QModelIndex index1 = tableView->modelIndex(cell); - const QModelIndex index2 = tableView->modelIndex(2, 2); + const QModelIndex index2 = tableView->index(2, 2); tableView->edit(index1); @@ -7232,6 +7480,311 @@ void tst_QQuickTableView::requiredPropertiesOnEditDelegate() QCOMPARE(textInput->property("current").toBool(), false); } +void tst_QQuickTableView::resettingRolesRespected() +{ + LOAD_TABLEVIEW("resetModelData.qml"); + + TestModel model(1, 1); + tableView->setModel(QVariant::fromValue(&model)); + + WAIT_UNTIL_POLISHED; + + QVERIFY(!tableView->property("success").toBool()); + model.useCustomRoleNames(true); + QTRY_VERIFY(tableView->property("success").toBool()); +} + +void tst_QQuickTableView::checkScroll_data() +{ + QTest::addColumn<bool>("resizableColumns"); + QTest::addColumn<bool>("resizableRows"); + QTest::newRow("T, T") << true << true; + QTest::newRow("T, F") << true << false; + QTest::newRow("F, T") << false << true; + QTest::newRow("F, F") << false << false; +} + +/*! + Make sure that the TableView is scrollable regardless + of the values of resizableColumns and resizableRows. +*/ +void tst_QQuickTableView::checkScroll() // QTBUG-116566 +{ + QFETCH(bool, resizableColumns); + QFETCH(bool, resizableRows); + LOAD_TABLEVIEW("plaintableview.qml"); // gives us 'tableView' variable + + tableView->setResizableColumns(resizableColumns); + tableView->setResizableRows(resizableRows); + + auto model = TestModelAsVariant(20, 10); + tableView->setModel(model); + + WAIT_UNTIL_POLISHED; + + // Scroll with the mouse wheel + sendWheelEvent(view, {10, 10}, {0, -120}, {0, -120}); + + // Check that scrolling succeeded + QTRY_COMPARE_GT(tableView->contentY(), 20); +} + +void tst_QQuickTableView::checkRebuildJsModel() +{ + LOAD_TABLEVIEW("resetJsModelData.qml"); // gives us 'tableView' variable + + // Generate javascript model + const int size = 5; + const char* modelUpdated = "modelUpdated"; + + QJSEngine jsEngine; + QJSValue jsArray; + jsArray = jsEngine.newArray(size); + for (int i = 0; i < size; ++i) + jsArray.setProperty(i, QRandomGenerator::global()->generate()); + + QVariant jsModel = QVariant::fromValue(jsArray); + tableView->setModel(jsModel); + WAIT_UNTIL_POLISHED; + + // Model change would be triggered for the first time + QCOMPARE(tableView->property(modelUpdated).toInt(), 1); + + // Set the same model once again and check if model changes + tableView->setModel(jsModel); + QCOMPARE(tableView->property(modelUpdated).toInt(), 1); +} + +void tst_QQuickTableView::checkVisualRowColumnAfterReorder() +{ + LOAD_TABLEVIEW("reordertableview.qml"); // gives us 'tableView' variable + auto model = TestModelAsVariant(3, 3); + tableView->setModel(model); + + WAIT_UNTIL_POLISHED; + + QSignalSpy columnMovedSpy(tableView, &QQuickTableView::columnMoved); + QSignalSpy rowMovedSpy(tableView, &QQuickTableView::rowMoved); + + // Move row and column + tableView->moveColumn(0, 2); + WAIT_UNTIL_POLISHED; + QCOMPARE(columnMovedSpy.size(), 3); + + tableView->moveRow(1, 0); + WAIT_UNTIL_POLISHED; + QCOMPARE(rowMovedSpy.size(), 2); + + QVariantList firstColumnVar = columnMovedSpy.takeFirst(); + QCOMPARE(firstColumnVar.at(0), 0); // Logical index + QCOMPARE(firstColumnVar.at(1), 0); // Old visual index + QCOMPARE(firstColumnVar.at(2), 2); // New visual index + + QVariantList firstRowVar = rowMovedSpy.takeFirst(); + QCOMPARE(firstRowVar.at(0), 0); // Logical index + QCOMPARE(firstRowVar.at(1), 0); // Old visual index + QCOMPARE(firstRowVar.at(2), 1); // New visual index +} + +void tst_QQuickTableView::checkColumnRowSizeAfterReorder() +{ + LOAD_TABLEVIEW("reordertableview.qml"); // gives us 'tableView' variable + auto model = TestModelAsVariant(3, 3); + tableView->setModel(model); + + WAIT_UNTIL_POLISHED; + + const QSignalSpy columMovedSpy(tableView, &QQuickTableView::columnMoved); + const QSignalSpy rowMovedSpy(tableView, &QQuickTableView::rowMoved); + + for (int index = 0, minSize = 10; index < tableView->columns(); index++, minSize+=10) { + tableView->setColumnWidth(index, minSize); + tableView->setRowHeight(index, minSize); + } + WAIT_UNTIL_POLISHED; + + // Move row and column + tableView->moveColumn(0, 2); + WAIT_UNTIL_POLISHED; + QCOMPARE(columMovedSpy.size(), 3); + + tableView->moveRow(0, 2); + WAIT_UNTIL_POLISHED; + QCOMPARE(rowMovedSpy.size(), 3); + + QCOMPARE(tableView->columnWidth(0), 20); + QCOMPARE(tableView->columnWidth(1), 30); + QCOMPARE(tableView->columnWidth(2), 10); + + QCOMPARE(tableView->rowHeight(0), 20); + QCOMPARE(tableView->rowHeight(1), 30); + QCOMPARE(tableView->rowHeight(2), 10); +} + +void tst_QQuickTableView::checkCellModelIdxAfterReorder() +{ + LOAD_TABLEVIEW("reordertableview.qml"); // gives us 'tableView' variable + auto model = TestModelAsVariant(3, 3); + tableView->setModel(model); + + WAIT_UNTIL_POLISHED; + + const QSignalSpy columnMovedSpy(tableView, &QQuickTableView::columnMoved); + const QSignalSpy rowMovedSpy(tableView, &QQuickTableView::rowMoved); + + const QSharedPointer<TestModel> testModel = model.value<QSharedPointer<TestModel>>(); + const QString objNameItem21(tableView->itemAtIndex(testModel->index(2, 1))->objectName()); + const QString objNameItem00(tableView->itemAtIndex(testModel->index(0 ,0))->objectName()); + const QString objNameItem11(tableView->itemAtIndex(testModel->index(1 ,1))->objectName()); + + // Move row and column + tableView->moveColumn(0, 2); + WAIT_UNTIL_POLISHED; + QCOMPARE(columnMovedSpy.size(), 3); + + tableView->moveRow(1, 0); + WAIT_UNTIL_POLISHED; + QCOMPARE(rowMovedSpy.size(), 2); + + // Check model index - index() + QModelIndex modelIndex = tableView->index(0, 0); + QCOMPARE(modelIndex.column(), 1); + QCOMPARE(modelIndex.row(), 1); + + modelIndex = tableView->index(1, 1); + QCOMPARE(modelIndex.column(), 2); + QCOMPARE(modelIndex.row(), 0); + + modelIndex = tableView->index(2, 2); + QCOMPARE(modelIndex.column(), 0); + QCOMPARE(modelIndex.row(), 2); + + // Check cell index - cellAtIndex() + { + QPoint cell = tableView->cellAtIndex(testModel->index(0, 0)); + QCOMPARE(cell.x(), 2); + QCOMPARE(cell.y(), 1); + } + + // Check column and row index - columnAtIndex(), rowAtIndex() + { + int columnIndex = tableView->columnAtIndex(testModel->index(0, 0)); + int rowIndex = tableView->rowAtIndex(testModel->index(0, 0)); + QCOMPARE(columnIndex, 2); + QCOMPARE(rowIndex, 1); + } + + // Check item - itemAtIndex() + // Item at index provides the item that is mapped to that model index + // and it shouldn't be confused with cell index + { + QQuickItem *item = tableView->itemAtIndex(testModel->index(0 ,0)); + QCOMPARE(objNameItem00, item->objectName()); + } + + // Check item at cell localtion 0, 0 - itemAtCell() + { + QQuickItem *item = tableView->itemAtCell(QPoint(0, 0)); + QCOMPARE(objNameItem11, item->objectName()); + } +} + +void tst_QQuickTableView::checkEditAfterReorder() +{ + LOAD_TABLEVIEW("editdelegate.qml"); // gives us 'tableView' variable + auto model = TestModelAsVariant(3, 3); + tableView->setModel(model); + + WAIT_UNTIL_POLISHED; + + const QSignalSpy columnMovedSpy(tableView, &QQuickTableView::columnMoved); + const QSignalSpy rowMovedSpy(tableView, &QQuickTableView::rowMoved); + + // Move row and column + tableView->moveColumn(0, 1); + WAIT_UNTIL_POLISHED; + QCOMPARE(columnMovedSpy.size(), 2); + + tableView->moveRow(0, 1); + WAIT_UNTIL_POLISHED; + QCOMPARE(rowMovedSpy.size(), 2); + + // Edit model index (0, 0) + const QSharedPointer<TestModel> testModel = model.value<QSharedPointer<TestModel>>(); + const auto cellItem1 = tableView->itemAtCell(QPoint(0, 0)); + QCOMPARE(cellItem1->property("editing").toBool(), false); + + tableView->edit(testModel->index(1, 1)); + QCOMPARE(cellItem1->property("editing").toBool(), true); + + // Close the editor + tableView->closeEditor(); + QCOMPARE(cellItem1->property("editing").toBool(), false); +} + +void tst_QQuickTableView::checkSelectionAfterReorder() +{ + LOAD_TABLEVIEW("tableviewwithselected1.qml"); + + TestModel model(10, 10); + QItemSelectionModel selectionModel(&model); + + tableView->setModel(QVariant::fromValue(&model)); + tableView->setSelectionModel(&selectionModel); + + WAIT_UNTIL_POLISHED; + + QCOMPARE(selectionModel.hasSelection(), false); + QCOMPARE(tableView->selectionBehavior(), QQuickTableView::SelectCells); + + const QSignalSpy columnMovedSpy(tableView, &QQuickTableView::columnMoved); + tableView->moveColumn(0, 2); + WAIT_UNTIL_POLISHED; + QCOMPARE(columnMovedSpy.size(), 3); + + const QPoint endCellDist(1, 1); + const QPoint startCell(0, 0); + const QPoint endCell = startCell + endCellDist; + + const QQuickItem *startItem = tableView->itemAtCell(startCell); + const QQuickItem *endItem = tableView->itemAtCell(endCell); + QVERIFY(startItem); + QVERIFY(endItem); + + const QPointF startPos(startItem->x(), startItem->y()); + const QPointF endPos(endItem->x(), endItem->y()); + + QVERIFY(tableViewPrivate->startSelection(startPos, Qt::NoModifier)); + tableViewPrivate->setSelectionStartPos(startPos); + tableViewPrivate->setSelectionEndPos(endPos); + + QCOMPARE(selectionModel.hasSelection(), true); + + const int x1 = qMin(startCell.x(), endCell.x()); + const int x2 = qMax(startCell.x(), endCell.x()); + const int y1 = qMin(startCell.y(), endCell.y()); + const int y2 = qMax(startCell.y(), endCell.y()); + + for (int x = x1; x <= x2; ++x) { + for (int y = y1; y <= y2; ++y) { + const auto index = tableView->index(y, x); + QVERIFY(selectionModel.isSelected(index)); + } + } + + const int expectedCount = (x2 - x1 + 1) * (y2 - y1 + 1); + const int actualCount = selectionModel.selectedIndexes().size(); + QCOMPARE(actualCount, expectedCount); + + // The column which has been moved shouldn't have the selected + // bit enabled + for (int index = 0; index < model.rowCount(); index++) + QCOMPARE(selectionModel.isSelected(model.index(index, 0)), false); + + tableViewPrivate->clearSelection(); + QCOMPARE(selectionModel.hasSelection(), false); +} + QTEST_MAIN(tst_QQuickTableView) #include "tst_qquicktableview.moc" |