aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRichard Moe Gustavsen <richard.gustavsen@qt.io>2021-04-15 09:40:34 +0200
committerRichard Moe Gustavsen <richard.gustavsen@qt.io>2021-06-02 13:33:49 +0200
commitc6c31740a7377715ce53fea5f323c4e48525e425 (patch)
treea36a488ecf7e21f8a1e98f6b145e9c8456771612
parent28c4d536e2bbf65d85c89efe1b64fc4e714e02bc (diff)
Selection support: support setting a QItemSelectionModel on TableView
Add support for assigning a QItemSelectionModel to TableView. By doing so, delegate items that has a "required property selected" defined will get this updated according to the state of the selection model. It's essential that the property is defined as "required". If not, the property will simply be ignored by TableView. This is done to ensure that existing applications that already has a "selected" property defined, will continue to work as before, unaffected by the new selection API. [ChangeLog][QtQuick] TableView now supports selections by using an ItemSelectionModel. Task-number: QTBUG-74750 Change-Id: I4f4d75e9e65563b9aab0c54f3aa4aad2f6883952 Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
-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"