aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorRichard Moe Gustavsen <richard.gustavsen@qt.io>2022-05-31 13:38:01 +0200
committerRichard Moe Gustavsen <richard.gustavsen@qt.io>2022-06-04 22:45:24 +0200
commitc20026be9382a55072df7033cf300d57b7dab975 (patch)
tree8f716677325ca409e4a9da15034cb7dfd470e9f2 /src
parenta368e603451cd3c3d44776ff47351a1d93cb546b (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.h2
-rw-r--r--src/quick/items/qquicktreeview.cpp58
-rw-r--r--src/quick/items/qquicktreeview_p_p.h1
-rw-r--r--src/quickcontrols2/basic/TreeViewDelegate.qml11
-rw-r--r--src/quickcontrols2/macos/TreeViewDelegate.qml5
-rw-r--r--src/quicknativestyle/controls/DefaultTreeViewDelegate.qml11
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
}
}