diff options
-rw-r--r-- | src/quick/doc/snippets/qml/tableview/selectionmodel.qml | 96 | ||||
-rw-r--r-- | src/quick/items/qquicktableview.cpp | 158 | ||||
-rw-r--r-- | src/quick/items/qquicktableview_p.h | 6 | ||||
-rw-r--r-- | src/quick/items/qquicktableview_p_p.h | 10 | ||||
-rw-r--r-- | tests/auto/quick/qquicktableview/data/tableviewwithselected1.qml | 72 | ||||
-rw-r--r-- | tests/auto/quick/qquicktableview/data/tableviewwithselected2.qml | 78 | ||||
-rw-r--r-- | tests/auto/quick/qquicktableview/tst_qquicktableview.cpp | 115 |
7 files changed, 530 insertions, 5 deletions
diff --git a/src/quick/doc/snippets/qml/tableview/selectionmodel.qml b/src/quick/doc/snippets/qml/tableview/selectionmodel.qml new file mode 100644 index 0000000000..b971e89979 --- /dev/null +++ b/src/quick/doc/snippets/qml/tableview/selectionmodel.qml @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** 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. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick + +import QtQuick +import QtQuick.Window +import QtQuick.Controls +import QtQml.Models +import Qt.labs.qmlmodels + +Window { + width: 480 + height: 640 + visible: true + visibility: Window.AutomaticVisibility + + Button { + id: button + text: "Toggle" + onClicked: tableView.selectionModel.select(tableView.model.index(0, 0), ItemSelectionModel.Toggle) + } + +//![0] + TableView { + id: tableView + anchors.fill: parent + anchors.topMargin: button.height + clip: true + + model: TableModel { + TableModelColumn { display: "name" } + rows: [ { "name": "Harry" }, { "name": "Hedwig" } ] + } + + selectionModel: ItemSelectionModel { + model: tableView.model + } + + delegate: Rectangle { + implicitWidth: 100 + implicitHeight: 30 + required property bool selected + color: selected ? "green" : "lightgray" + Text { text: display } + } + } +//![0] +} diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index a4c5bf61cf..c79d25700b 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -187,6 +187,26 @@ moves together with the table as you flick: \snippet qml/tableview/tableviewwithheader.qml 0 + + \section1 Selecting items + + You can add selection support to TableView by assigning a QSelectionModel to + the \l selectionModel property. It will then use this model to control which + delegate items should be shown as selected. For a delegate item to be shown as + selected, it needs to contain the following property: + \code + required property bool selected + \endcode + + \note It's important for the \c selected property to be defined as \c required. + This will inform TableView that it should take responsibility for the property's + value. If it's not defined as required, it will simply be ignored. + See also \l {Required Properties}. + + The following snippet shows how an application can render the delegate differently + depending on the \c selected property: + + \snippet qml/tableview/selectionmodel.qml 0 */ /*! @@ -405,6 +425,17 @@ */ /*! + \qmlproperty QSelectionModel QtQuick::TableView::selectionModel + + This property can be set to control which delegate items should be shown as + selected. If the delegate has a \c {required property bool selected} + defined, TableView will keep it in sync with the selection state of the + corresponding model item in the selection model. + + \sa {Selecting items} +*/ + +/*! \qmlmethod QtQuick::TableView::positionViewAtCell(point cell, Qt.Alignment alignment, point offset) Positions \l {Flickable::}{contentX} and \l {Flickable::}{contentY} such @@ -656,6 +687,8 @@ static const Qt::Edge allTableEdges[] = { Qt::LeftEdge, Qt::RightEdge, Qt::TopEd static const int kEdgeIndexNotSet = -2; static const int kEdgeIndexAtEnd = -3; +static const char* kRequiredProperty = "_qt_isrequiredpropery_selected"; + const QPoint QQuickTableViewPrivate::kLeft = QPoint(-1, 0); const QPoint QQuickTableViewPrivate::kRight = QPoint(1, 0); const QPoint QQuickTableViewPrivate::kUp = QPoint(0, -1); @@ -783,6 +816,17 @@ QPoint QQuickTableViewPrivate::cellAtModelIndex(int modelIndex) const } } +int QQuickTableViewPrivate::modelIndexToCellIndex(const QModelIndex &modelIndex) const +{ + // Convert QModelIndex to cell index. A cell index is just an + // integer representation of a cell instead of using a QPoint. + if (modelIndex.parent().isValid()) { + // TableView only uses the root items of the model + return -1; + } + return modelIndexAtCell(QPoint(modelIndex.column(), modelIndex.row())); +} + int QQuickTableViewPrivate::edgeToArrayIndex(Qt::Edge edge) { return int(log2(float(edge))); @@ -2670,6 +2714,65 @@ void QQuickTableViewPrivate::createWrapperModel() model = tableModel; } +bool QQuickTableViewPrivate::selectedInSelectionModel(const QPoint &cell) const +{ + if (!selectionModel) + return false; + + QAbstractItemModel *model = selectionModel->model(); + if (!model) + return false; + + const QModelIndex modelIndex = model->index(cell.y(), cell.x()); + return selectionModel->isSelected(modelIndex); +} + +void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected) const +{ + const auto &selectedIndexes = selected.indexes(); + const auto &deselectedIndexes = deselected.indexes(); + for (int i = 0; i < selectedIndexes.count(); ++i) + setSelectedOnDelegateItem(selectedIndexes.at(i), true); + for (int i = 0; i < deselectedIndexes.count(); ++i) + setSelectedOnDelegateItem(deselectedIndexes.at(i), false); +} + +void QQuickTableViewPrivate::updateSelectedOnAllDelegateItems() const +{ + for (auto it = loadedItems.keyBegin(), end = loadedItems.keyEnd(); it != end; ++it) { + const QPoint cell = cellAtModelIndex(*it); + const bool selected = selectedInSelectionModel(cell); + setSelectedOnDelegateItem(loadedTableItem(cell)->item, selected); + } +} + +void QQuickTableViewPrivate::setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select) const +{ + const int cellIndex = modelIndexToCellIndex(modelIndex); + if (!loadedItems.contains(cellIndex)) + return; + const QPoint cell = cellAtModelIndex(cellIndex); + setSelectedOnDelegateItem(loadedTableItem(cell)->item, select); +} + +void QQuickTableViewPrivate::setSelectedOnDelegateItem(QQuickItem *delegateItem, bool select) const +{ + if (!delegateItem->property(kRequiredProperty).toBool()) { + // We only assign to "selected" if it's a required property. Otherwise + // we assume (for backwards compatibility) that the property is used + // by the delegate for something else. + // Note: kRequiredProperty is a work-around until QMetaProperty::isRequired() works. + return; + } + + // Note that several delegates might be in use (in case of a DelegateChooser), and + // the delegate can also change. So we cannot cache the propertyIndex. + const auto metaObject = delegateItem->metaObject(); + const int propertyIndex = metaObject->indexOfProperty("selected"); + const auto metaProperty = metaObject->property(propertyIndex); + metaProperty.write(delegateItem, QVariant::fromValue(select)); +} + void QQuickTableViewPrivate::itemCreatedCallback(int modelIndex, QObject*) { if (blockItemCreatedCallback) @@ -2689,12 +2792,22 @@ void QQuickTableViewPrivate::itemCreatedCallback(int modelIndex, QObject*) void QQuickTableViewPrivate::initItemCallback(int modelIndex, QObject *object) { - Q_UNUSED(modelIndex); Q_Q(QQuickTableView); - if (auto item = qmlobject_cast<QQuickItem*>(object)) { - item->setParentItem(q->contentItem()); - item->setZ(1); + auto item = static_cast<QQuickItem*>(object); + + item->setParentItem(q->contentItem()); + item->setZ(1); + + const QPoint cell = cellAtModelIndex(modelIndex); + const bool selected = selectedInSelectionModel(cell); + + if (qobject_cast<QQmlTableInstanceModel *>(model)) { + const bool wasRequired = model->setRequiredProperty(modelIndex, QStringLiteral("selected"), selected); + if (wasRequired) { + // Work-around until QMetaProperty::isRequired() works + item->setProperty(kRequiredProperty, true); + } } if (auto attached = getAttachedObject(object)) @@ -2711,7 +2824,10 @@ void QQuickTableViewPrivate::itemPooledCallback(int modelIndex, QObject *object) void QQuickTableViewPrivate::itemReusedCallback(int modelIndex, QObject *object) { - Q_UNUSED(modelIndex); + auto item = static_cast<QQuickItem*>(object); + const QPoint cell = cellAtModelIndex(modelIndex); + const bool selected = selectedInSelectionModel(cell); + setSelectedOnDelegateItem(item, selected); if (auto attached = getAttachedObject(object)) emit attached->reused(); @@ -3373,6 +3489,38 @@ void QQuickTableView::setSyncDirection(Qt::Orientations direction) emit syncDirectionChanged(); } +QItemSelectionModel *QQuickTableView::selectionModel() const +{ + return d_func()->selectionModel; +} + +void QQuickTableView::setSelectionModel(QItemSelectionModel *selectionModel) +{ + Q_D(QQuickTableView); + if (d->selectionModel == selectionModel) + return; + + // Note: There is no need to rebuild the table when the selection model + // changes, since selections only affect the internals of the delegate + // items, and not the layout of the TableView. + + if (d->selectionModel) { + QQuickTableViewPrivate::disconnect(d->selectionModel, &QItemSelectionModel::selectionChanged, + d, &QQuickTableViewPrivate::selectionChangedInSelectionModel); + } + + d->selectionModel = selectionModel; + + if (d->selectionModel) { + QQuickTableViewPrivate::connect(d->selectionModel, &QItemSelectionModel::selectionChanged, + d, &QQuickTableViewPrivate::selectionChangedInSelectionModel); + } + + d->updateSelectedOnAllDelegateItems(); + + emit selectionModelChanged(); +} + int QQuickTableView::leftColumn() const { Q_D(const QQuickTableView); diff --git a/src/quick/items/qquicktableview_p.h b/src/quick/items/qquicktableview_p.h index 0a63721d19..04270a4773 100644 --- a/src/quick/items/qquicktableview_p.h +++ b/src/quick/items/qquicktableview_p.h @@ -63,6 +63,7 @@ QT_BEGIN_NAMESPACE class QQuickTableViewAttached; class QQuickTableViewPrivate; +class QItemSelectionModel; class Q_QUICK_PRIVATE_EXPORT QQuickTableView : public QQuickFlickable { @@ -85,6 +86,7 @@ class Q_QUICK_PRIVATE_EXPORT QQuickTableView : public QQuickFlickable Q_PROPERTY(int rightColumn READ rightColumn NOTIFY rightColumnChanged REVISION(6, 0)) Q_PROPERTY(int topRow READ topRow NOTIFY topRowChanged REVISION(6, 0)) Q_PROPERTY(int bottomRow READ bottomRow NOTIFY bottomRowChanged REVISION(6, 0)) + Q_PROPERTY(QItemSelectionModel *selectionModel READ selectionModel WRITE setSelectionModel NOTIFY selectionModelChanged REVISION(6, 2)) QML_NAMED_ELEMENT(TableView) QML_ADDED_IN_VERSION(2, 12) @@ -126,6 +128,9 @@ public: Qt::Orientations syncDirection() const; void setSyncDirection(Qt::Orientations direction); + QItemSelectionModel *selectionModel() const; + void setSelectionModel(QItemSelectionModel *selectionModel); + int leftColumn() const; int rightColumn() const; int topRow() const; @@ -167,6 +172,7 @@ Q_SIGNALS: Q_REVISION(6, 0) void rightColumnChanged(); Q_REVISION(6, 0) void topRowChanged(); Q_REVISION(6, 0) void bottomRowChanged(); + Q_REVISION(6, 2) void selectionModelChanged(); protected: void geometryChange(const QRectF &newGeometry, const QRectF &oldGeometry) override; diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h index 713364097b..73a4b4f564 100644 --- a/src/quick/items/qquicktableview_p_p.h +++ b/src/quick/items/qquicktableview_p_p.h @@ -55,6 +55,7 @@ #include <QtCore/qtimer.h> #include <QtCore/private/qflatmap_p.h> +#include <QtCore/qitemselectionmodel.h> #include <QtQmlModels/private/qqmltableinstancemodel_p.h> #include <QtQml/private/qqmlincubator_p.h> #include <QtQmlModels/private/qqmlchangeset_p.h> @@ -314,6 +315,8 @@ public: QList<QPointer<QQuickTableView> > syncChildren; Qt::Orientations assignedSyncDirection = Qt::Horizontal | Qt::Vertical; + QPointer<QItemSelectionModel> selectionModel; + int assignedPositionViewAtRow = 0; int assignedPositionViewAtColumn = 0; int positionViewAtRow = 0; @@ -339,6 +342,7 @@ public: int modelIndexAtCell(const QPoint &cell) const; QPoint cellAtModelIndex(int modelIndex) const; + int modelIndexToCellIndex(const QModelIndex &modelIndex) const; qreal sizeHintForColumn(int column) const; qreal sizeHintForRow(int row) const; @@ -459,6 +463,12 @@ public: void syncViewportRect(); void syncViewportPosRecursive(); + bool selectedInSelectionModel(const QPoint &cell) const; + void selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected) const; + void updateSelectedOnAllDelegateItems() const; + void setSelectedOnDelegateItem(const QModelIndex &modelIndex, bool select) const; + void setSelectedOnDelegateItem(QQuickItem *delegateItem, bool select) const; + void fetchMoreData(); void _q_componentFinalized(); diff --git a/tests/auto/quick/qquicktableview/data/tableviewwithselected1.qml b/tests/auto/quick/qquicktableview/data/tableviewwithselected1.qml new file mode 100644 index 0000000000..efa0befaf5 --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/tableviewwithselected1.qml @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick +import QtQuick.Window +import QtQml.Models + +Item { + width: 640 + height: 450 + + property alias tableView: tableView + + TableView { + id: tableView + width: 600 + height: 400 + anchors.margins: 1 + clip: true + delegate: tableViewDelegate + columnSpacing: 1 + rowSpacing: 1 + } + + Component { + id: tableViewDelegate + Rectangle { + objectName: "tableViewDelegate" + implicitWidth: 100 + implicitHeight: 100 + required property bool selected + color: selected ? "lightgray" : "green" + } + } + +} diff --git a/tests/auto/quick/qquicktableview/data/tableviewwithselected2.qml b/tests/auto/quick/qquicktableview/data/tableviewwithselected2.qml new file mode 100644 index 0000000000..bb90d3f4bd --- /dev/null +++ b/tests/auto/quick/qquicktableview/data/tableviewwithselected2.qml @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2021 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQuick module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** 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 Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** 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-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick +import QtQuick.Window +import QtQml.Models + +Item { + width: 640 + height: 450 + + property alias tableView: tableView + + TableView { + id: tableView + width: 600 + height: 400 + anchors.margins: 1 + clip: true + delegate: tableViewDelegate + columnSpacing: 1 + rowSpacing: 1 + selectionModel: ItemSelectionModel { + model: tableView.model + } + + } + + Component { + id: tableViewDelegate + Rectangle { + objectName: "tableViewDelegate" + implicitWidth: 100 + implicitHeight: 100 + // Add a selected property, but since it's not + // required, TableView should not touch it. + property bool selected + color: selected ? "lightgray" : "green" + } + } + +} diff --git a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp index 6bd05c876f..dece7e53d9 100644 --- a/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp +++ b/tests/auto/quick/qquicktableview/tst_qquicktableview.cpp @@ -198,6 +198,10 @@ private slots: void leftRightTopBottomProperties(); void checkContentSize_data(); void checkContentSize(); + void checkSelectionModelWithRequiredSelectedProperty_data(); + void checkSelectionModelWithRequiredSelectedProperty(); + void checkSelectionModelWithUnrequiredSelectedProperty(); + void removeAndAddSelectionModel(); }; tst_QQuickTableView::tst_QQuickTableView() @@ -3451,6 +3455,117 @@ void tst_QQuickTableView::checkContentSize() QCOMPARE(tableView->contentHeight(), rowCount == 0 ? 0 : (rowCount * (delegateHeight + rowSpacing)) - rowSpacing); } +void tst_QQuickTableView::checkSelectionModelWithRequiredSelectedProperty_data() +{ + QTest::addColumn<QVector<QPoint>>("selected"); + QTest::addColumn<QPoint>("toggle"); + + QTest::newRow("nothing selected") << QVector<QPoint>() << QPoint(0,0); + QTest::newRow("one item selected") << (QVector<QPoint>() << QPoint(0, 0)) << QPoint(1, 1); + QTest::newRow("two items selected") << (QVector<QPoint>() << QPoint(1, 1) << QPoint(2, 2)) << QPoint(1, 1); +} + +void tst_QQuickTableView::checkSelectionModelWithRequiredSelectedProperty() +{ + // Check that if you add a "required property selected" to the delegate, + // TableView will give it a value upon creation that matches the state + // in the selection model. + QFETCH(QVector<QPoint>, selected); + QFETCH(QPoint, toggle); + + LOAD_TABLEVIEW("tableviewwithselected1.qml"); + + TestModel model(10, 10); + QItemSelectionModel selectionModel(&model); + + // Set initially selected cells + for (auto it = selected.constBegin(); it != selected.constEnd(); ++it) { + const QPoint &cell = *it; + selectionModel.select(model.index(cell.y(), cell.x()), QItemSelectionModel::Select); + } + + tableView->setModel(QVariant::fromValue(&model)); + tableView->setSelectionModel(&selectionModel); + + WAIT_UNTIL_POLISHED; + + // Check that all delegates have "selected" set with the initial value + for (auto fxItem : tableViewPrivate->loadedItems) { + const auto context = qmlContext(fxItem->item.data()); + const int row = context->contextProperty("row").toInt(); + const int column = context->contextProperty("column").toInt(); + const bool selected = fxItem->item->property("selected").toBool(); + const auto modelIndex = model.index(row, column); + QCOMPARE(selected, selectionModel.isSelected(modelIndex)); + } + + // Toggle selected on one of the model indices, and check + // that the "selected" property got updated as well + const QModelIndex toggleIndex = model.index(toggle.y(), toggle.x()); + const bool wasSelected = selectionModel.isSelected(toggleIndex); + selectionModel.select(toggleIndex, QItemSelectionModel::Toggle); + const auto fxItem = tableViewPrivate->loadedTableItem(toggle); + const bool isSelected = fxItem->item->property("selected").toBool(); + QCOMPARE(isSelected, !wasSelected); +} + +void tst_QQuickTableView::checkSelectionModelWithUnrequiredSelectedProperty() +{ + // Check that if there is a property "selected" in the delegate, but it's + // not required, then TableView will not touch it. This is for legacy reasons, to + // not break applications written before Qt 6.2 that has such a property + // added for application logic. + LOAD_TABLEVIEW("tableviewwithselected2.qml"); + + TestModel model(10, 10); + tableView->setModel(QVariant::fromValue(&model)); + QItemSelectionModel *selectionModel = tableView->selectionModel(); + QVERIFY(selectionModel); + + // Select a cell + selectionModel->select(model.index(1, 1), QItemSelectionModel::Select); + + WAIT_UNTIL_POLISHED; + + const auto fxItem = tableViewPrivate->loadedTableItem(QPoint(1, 1)); + const bool selected = fxItem->item->property("selected").toBool(); + QCOMPARE(selected, false); +} + +void tst_QQuickTableView::removeAndAddSelectionModel() +{ + // Check that if we remove the selection model from TableView, all delegates + // will be unselected. And opposite, if we add the selection model back, the + // delegates will be updated. + LOAD_TABLEVIEW("tableviewwithselected1.qml"); + + TestModel model(10, 10); + QItemSelectionModel selectionModel(&model); + + // Select a cell in the selection model + selectionModel.select(model.index(1, 1), QItemSelectionModel::Select); + + tableView->setModel(QVariant::fromValue(&model)); + tableView->setSelectionModel(&selectionModel); + + WAIT_UNTIL_POLISHED; + + // Check that the delegate item is selected + const auto fxItem = tableViewPrivate->loadedTableItem(QPoint(1, 1)); + bool selected = fxItem->item->property("selected").toBool(); + QCOMPARE(selected, true); + + // Remove the selection model, and check that the delegate item is now unselected + tableView->setSelectionModel(nullptr); + selected = fxItem->item->property("selected").toBool(); + QCOMPARE(selected, false); + + // Add the selection model back, and check that the delegate item is selected again + tableView->setSelectionModel(&selectionModel); + selected = fxItem->item->property("selected").toBool(); + QCOMPARE(selected, true); +} + QTEST_MAIN(tst_QQuickTableView) #include "tst_qquicktableview.moc" |