diff options
author | Richard Moe Gustavsen <richard.gustavsen@qt.io> | 2022-05-31 13:38:01 +0200 |
---|---|---|
committer | Richard Moe Gustavsen <richard.gustavsen@qt.io> | 2022-06-04 22:45:24 +0200 |
commit | c20026be9382a55072df7033cf300d57b7dab975 (patch) | |
tree | 8f716677325ca409e4a9da15034cb7dfd470e9f2 /src | |
parent | a368e603451cd3c3d44776ff47351a1d93cb546b (diff) |
QQuickTreeView: implement support for selecting cells
Selecting cells in a table is quite different from selecting
cells in a tree. The reason is that in a table, all model
items share the same model parent. And ItemSelectionModel
is optimized for this case, meaning that you can select
an area of cells by simply specifying the top-left index,
and the bottom-right index, as long as the cells
in-between all have the same parent.
A tree is not structured this way. Instead it's structured
as a hierarchy of parent-child relationships, where the
requirement that all model items should have the same parent
obviously will not hold.
Because of this, the implementation in TreeView that lets
the user select cells, needs to be quite different from the
optimized version in TableView. Instead, it basically needs to
divide the selected area into individual rows, and sometimes
indices, that can be selected, or deselected, one-by-one.
This patch overrides the 'updateSelection()' function for
QQuickTreeView, and rewrites the logic to take all this into
account. This will make selecting cells work for TreeView.
Change-Id: I2157efcd0e83b5a0342f6af4018323b64d31f6f3
Reviewed-by: Mitch Curtis <mitch.curtis@qt.io>
Diffstat (limited to 'src')
-rw-r--r-- | src/quick/items/qquicktableview_p_p.h | 2 | ||||
-rw-r--r-- | src/quick/items/qquicktreeview.cpp | 58 | ||||
-rw-r--r-- | src/quick/items/qquicktreeview_p_p.h | 1 | ||||
-rw-r--r-- | src/quickcontrols2/basic/TreeViewDelegate.qml | 11 | ||||
-rw-r--r-- | src/quickcontrols2/macos/TreeViewDelegate.qml | 5 | ||||
-rw-r--r-- | src/quicknativestyle/controls/DefaultTreeViewDelegate.qml | 11 |
6 files changed, 82 insertions, 6 deletions
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h index fd2016571c..14dcddbb8d 100644 --- a/src/quick/items/qquicktableview_p_p.h +++ b/src/quick/items/qquicktableview_p_p.h @@ -539,7 +539,7 @@ public: QSizeF scrollTowardsSelectionPoint(const QPointF &pos, const QSizeF &step) override; QPoint clampedCellAtPos(const QPointF &pos) const; - void updateSelection(const QRect &oldSelection, const QRect &newSelection); + virtual void updateSelection(const QRect &oldSelection, const QRect &newSelection); QRect selection() const; // ---------------- }; diff --git a/src/quick/items/qquicktreeview.cpp b/src/quick/items/qquicktreeview.cpp index a56b4e1577..c8c345bee3 100644 --- a/src/quick/items/qquicktreeview.cpp +++ b/src/quick/items/qquicktreeview.cpp @@ -360,11 +360,69 @@ void QQuickTreeViewPrivate::updateRequiredProperties(int serializedModelIndex, Q setRequiredProperty("depth", m_treeModelToTableModel.depthAtRow(row), serializedModelIndex, object, init); } +void QQuickTreeViewPrivate::updateSelection(const QRect &oldSelection, const QRect &newSelection) +{ + Q_Q(QQuickTreeView); + + const QRect oldRect = oldSelection.normalized(); + const QRect newRect = newSelection.normalized(); + + if (oldSelection == newSelection) + return; + + // Select the rows inside newRect that doesn't overlap with oldRect + for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row) { + if (oldRect.y() != -1 && oldRect.y() <= row && row <= oldRect.y() + oldRect.height()) + continue; + const QModelIndex startIndex = q->modelIndex(newRect.x(), row); + const QModelIndex endIndex = q->modelIndex(newRect.x() + newRect.width(), row); + selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Select); + } + + if (oldRect.x() != -1) { + // Since oldRect is valid, this update is a continuation of an already existing selection! + + // Select the columns inside newRect that don't overlap with oldRect + for (int column = newRect.x(); column <= newRect.x() + newRect.width(); ++column) { + if (oldRect.x() <= column && column <= oldRect.x() + oldRect.width()) + continue; + for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row) + selectionModel->select(q->modelIndex(column, row), QItemSelectionModel::Select); + } + + // Unselect the rows inside oldRect that don't overlap with newRect + for (int row = oldRect.y(); row <= oldRect.y() + oldRect.height(); ++row) { + if (newRect.y() <= row && row <= newRect.y() + newRect.height()) + continue; + const QModelIndex startIndex = q->modelIndex(oldRect.x(), row); + const QModelIndex endIndex = q->modelIndex(oldRect.x() + oldRect.width(), row); + selectionModel->select(QItemSelection(startIndex, endIndex), QItemSelectionModel::Deselect); + } + + // Unselect the columns inside oldRect that don't overlap with newRect + for (int column = oldRect.x(); column <= oldRect.x() + oldRect.width(); ++column) { + if (newRect.x() <= column && column <= newRect.x() + newRect.width()) + continue; + // Since we're not allowed to call select/unselect on the selectionModel with + // indices from different parents, and since indicies from different parents are + // expected when working with trees, we need to unselect the indices in the column + // one by one, rather than the whole column in one go. This, however, can cause a + // lot of selection fragments in the selectionModel, which eventually can hurt + // performance. But large selections containing a lot of columns is not normally + // the case for a treeview, so accept this potential corner case for now. + for (int row = newRect.y(); row <= newRect.y() + newRect.height(); ++row) + selectionModel->select(q->modelIndex(column, row), QItemSelectionModel::Deselect); + } + } +} + QQuickTreeView::QQuickTreeView(QQuickItem *parent) : QQuickTableView(*(new QQuickTreeViewPrivate), parent) { Q_D(QQuickTreeView); + setSelectionBehavior(SelectRows); + // Note: QQuickTableView will only ever see the table model m_treeModelToTableModel, and // never the actual tree model that is assigned to us by the application. const auto modelAsVariant = QVariant::fromValue(std::addressof(d->m_treeModelToTableModel)); diff --git a/src/quick/items/qquicktreeview_p_p.h b/src/quick/items/qquicktreeview_p_p.h index 47d87d2ac1..83d94c4b06 100644 --- a/src/quick/items/qquicktreeview_p_p.h +++ b/src/quick/items/qquicktreeview_p_p.h @@ -78,6 +78,7 @@ public: const QVector<int> &roles); void updateRequiredProperties(int serializedModelIndex, QObject *object, bool init); + void updateSelection(const QRect &oldSelection, const QRect &newSelection) override; public: QQmlTreeModelToTableModel m_treeModelToTableModel; diff --git a/src/quickcontrols2/basic/TreeViewDelegate.qml b/src/quickcontrols2/basic/TreeViewDelegate.qml index af73d96db4..0a8817b947 100644 --- a/src/quickcontrols2/basic/TreeViewDelegate.qml +++ b/src/quickcontrols2/basic/TreeViewDelegate.qml @@ -79,7 +79,10 @@ T.TreeViewDelegate { } background: Rectangle { - color: control.row === control.treeView.currentRow + color: control.selected || control.current || + ((control.treeView.selectionBehavior === TableView.SelectRows + || control.treeView.selectionBehavior === TableView.SelectionDisabled) + && control.row === control.treeView.currentRow) ? control.palette.highlight : (control.treeView.alternatingRows && control.row % 2 !== 0 ? control.palette.alternateBase @@ -90,6 +93,10 @@ T.TreeViewDelegate { clip: false text: control.model.display elide: Text.ElideRight - color: control.row === control.treeView.currentRow ? control.palette.highlightedText : control.palette.buttonText + color: control.selected || control.current || + ((control.treeView.selectionBehavior === TableView.SelectRows + || control.treeView.selectionBehavior === TableView.SelectionDisabled) + && control.row === control.treeView.currentRow) + ? control.palette.highlightedText : control.palette.buttonText } } diff --git a/src/quickcontrols2/macos/TreeViewDelegate.qml b/src/quickcontrols2/macos/TreeViewDelegate.qml index adc1ee8528..807a1a6e06 100644 --- a/src/quickcontrols2/macos/TreeViewDelegate.qml +++ b/src/quickcontrols2/macos/TreeViewDelegate.qml @@ -47,7 +47,10 @@ NativeStyle.DefaultTreeViewDelegate { palette.highlightedText: "white" background: Rectangle { - color: control.row === control.treeView.currentRow + color: control.selected || control.current + || ((control.treeView.selectionBehavior === TableView.SelectRows + || control.treeView.selectionBehavior === TableView.SelectionDisabled) + && control.row === control.treeView.currentRow) ? control.palette.highlight : (control.treeView.alternatingRows && control.row % 2 !== 0 ? control.palette.alternateBase diff --git a/src/quicknativestyle/controls/DefaultTreeViewDelegate.qml b/src/quicknativestyle/controls/DefaultTreeViewDelegate.qml index 765f6d49d3..fc785fe080 100644 --- a/src/quicknativestyle/controls/DefaultTreeViewDelegate.qml +++ b/src/quicknativestyle/controls/DefaultTreeViewDelegate.qml @@ -77,7 +77,10 @@ T.TreeViewDelegate { } background: Rectangle { - color: control.row === control.treeView.currentRow + color: control.selected || control.current || + ((control.treeView.selectionBehavior === TableView.SelectRows + || control.treeView.selectionBehavior === TableView.SelectionDisabled) + && control.row === control.treeView.currentRow) ? control.palette.highlight : (control.treeView.alternatingRows && control.row % 2 !== 0 ? control.palette.alternateBase @@ -88,6 +91,10 @@ T.TreeViewDelegate { clip: false text: control.model.display elide: Text.ElideRight - color: control.row === control.treeView.currentRow ? control.palette.highlightedText : control.palette.buttonText + color: control.selected || control.current || + ((control.treeView.selectionBehavior === TableView.SelectRows + || control.treeView.selectionBehavior === TableView.SelectionDisabled) + && control.row === control.treeView.currentRow) + ? control.palette.highlightedText : control.palette.buttonText } } |