aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/doc/snippets/qml/tableview/selectionmodel.qml96
-rw-r--r--src/quick/items/qquicktableview.cpp158
-rw-r--r--src/quick/items/qquicktableview_p.h6
-rw-r--r--src/quick/items/qquicktableview_p_p.h10
-rw-r--r--tests/auto/quick/qquicktableview/data/tableviewwithselected1.qml72
-rw-r--r--tests/auto/quick/qquicktableview/data/tableviewwithselected2.qml78
-rw-r--r--tests/auto/quick/qquicktableview/tst_qquicktableview.cpp115
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"