aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/quick/items/qquickselectable_p.h7
-rw-r--r--src/quick/items/qquicktableview.cpp38
-rw-r--r--src/quick/items/qquicktableview_p_p.h4
-rw-r--r--src/quicktemplates/qquickselectionrectangle.cpp40
-rw-r--r--tests/auto/quickcontrols/controls/data/tst_selectionrectangle.qml48
-rw-r--r--tests/manual/tableview/abstracttablemodel/main.qml4
6 files changed, 118 insertions, 23 deletions
diff --git a/src/quick/items/qquickselectable_p.h b/src/quick/items/qquickselectable_p.h
index 726352719f..c7fa5f1809 100644
--- a/src/quick/items/qquickselectable_p.h
+++ b/src/quick/items/qquickselectable_p.h
@@ -23,6 +23,11 @@ QT_BEGIN_NAMESPACE
class Q_QUICK_PRIVATE_EXPORT QQuickSelectable
{
public:
+ enum class CallBackFlag {
+ CancelSelection,
+ SelectionRectangleChanged
+ };
+
virtual QQuickItem *selectionPointerHandlerTarget() const = 0;
virtual bool startSelection(const QPointF &pos) = 0;
@@ -33,6 +38,8 @@ public:
virtual QRectF selectionRectangle() const = 0;
virtual QSizeF scrollTowardsSelectionPoint(const QPointF &pos, const QSizeF &step) = 0;
+
+ virtual void setCallback(std::function<void(CallBackFlag)> func) = 0;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp
index 0c218704c5..e432f7b4cb 100644
--- a/src/quick/items/qquicktableview.cpp
+++ b/src/quick/items/qquicktableview.cpp
@@ -1698,6 +1698,7 @@ void QQuickTableViewPrivate::setSelectionStartPos(const QPointF &pos)
return;
// Update selection model
+ QScopedValueRollback callbackGuard(inSelectionModelUpdate, true);
updateSelection(prevSelection, selection());
}
@@ -1746,6 +1747,7 @@ void QQuickTableViewPrivate::setSelectionEndPos(const QPointF &pos)
return;
// Update selection model
+ QScopedValueRollback callbackGuard(inSelectionModelUpdate, true);
updateSelection(prevSelection, selection());
}
@@ -1814,14 +1816,22 @@ void QQuickTableViewPrivate::updateSelection(const QRect &oldSelection, const QR
selectionModel->select(select, QItemSelectionModel::Select);
}
-void QQuickTableViewPrivate::clearSelection()
+void QQuickTableViewPrivate::cancelSelectionTracking()
{
+ // Cancel any ongoing key/mouse aided selection tracking
selectionStartCell = QPoint(-1, -1);
selectionEndCell = QPoint(-1, -1);
existingSelection.clear();
+ if (selectableCallbackFunction)
+ selectableCallbackFunction(QQuickSelectable::CallBackFlag::CancelSelection);
+}
- if (selectionModel)
- selectionModel->clearSelection();
+void QQuickTableViewPrivate::clearSelection()
+{
+ if (!selectionModel)
+ return;
+ QScopedValueRollback callbackGuard(inSelectionModelUpdate, true);
+ selectionModel->clearSelection();
}
void QQuickTableViewPrivate::normalizeSelection()
@@ -1959,6 +1969,11 @@ QSizeF QQuickTableViewPrivate::scrollTowardsSelectionPoint(const QPointF &pos, c
return dist;
}
+void QQuickTableViewPrivate::setCallback(std::function<void (CallBackFlag)> func)
+{
+ selectableCallbackFunction = func;
+}
+
QQuickTableViewAttached *QQuickTableViewPrivate::getAttachedObject(const QObject *object) const
{
QObject *attachedObject = qmlAttachedPropertiesObject<QQuickTableView>(object);
@@ -4065,10 +4080,11 @@ bool QQuickTableViewPrivate::currentInSelectionModel(const QPoint &cell) const
void QQuickTableViewPrivate::selectionChangedInSelectionModel(const QItemSelection &selected, const QItemSelection &deselected)
{
- if (!selectionModel->hasSelection()) {
- // Ensure that we cancel any ongoing key/mouse-based selections
- // if selectionModel.clearSelection() is called.
- clearSelection();
+ if (!inSelectionModelUpdate) {
+ // The selection model was manipulated outside of TableView
+ // and SelectionRectangle. In that case we cancel any ongoing
+ // selection tracking.
+ cancelSelectionTracking();
}
const auto &selectedIndexes = selected.indexes();
@@ -4900,8 +4916,10 @@ void QQuickTableViewPrivate::handleTap(const QQuickHandlerPoint &point)
// the current selection and move the current index instead.
if (pointerNavigationEnabled) {
q->closeEditor();
- if (selectionBehavior != QQuickTableView::SelectionDisabled)
+ if (selectionBehavior != QQuickTableView::SelectionDisabled) {
clearSelection();
+ cancelSelectionTracking();
+ }
setCurrentIndexFromTap(point.position());
}
}
@@ -5040,6 +5058,8 @@ bool QQuickTableViewPrivate::setCurrentIndexFromKeyEvent(QKeyEvent *e)
if (loadedItems.contains(serializedStartIndex)) {
const QRectF startGeometry = loadedItems.value(serializedStartIndex)->geometry();
setSelectionStartPos(startGeometry.center());
+ if (selectableCallbackFunction)
+ selectableCallbackFunction(QQuickSelectable::CallBackFlag::SelectionRectangleChanged);
}
}
};
@@ -5052,6 +5072,8 @@ bool QQuickTableViewPrivate::setCurrentIndexFromKeyEvent(QKeyEvent *e)
if (loadedItems.contains(serializedEndIndex)) {
const QRectF endGeometry = loadedItems.value(serializedEndIndex)->geometry();
setSelectionEndPos(endGeometry.center());
+ if (selectableCallbackFunction)
+ selectableCallbackFunction(QQuickSelectable::CallBackFlag::SelectionRectangleChanged);
}
}
selectionModel->setCurrentIndex(q->modelIndex(cell), QItemSelectionModel::NoUpdate);
diff --git a/src/quick/items/qquicktableview_p_p.h b/src/quick/items/qquicktableview_p_p.h
index a3768fda71..d2dc33c2f5 100644
--- a/src/quick/items/qquicktableview_p_p.h
+++ b/src/quick/items/qquicktableview_p_p.h
@@ -360,6 +360,8 @@ public:
QPointer<QItemSelectionModel> selectionModel;
QQuickTableView::SelectionBehavior selectionBehavior = QQuickTableView::SelectCells;
QQuickTableView::SelectionMode selectionMode = QQuickTableView::ExtendedSelection;
+ std::function<void(CallBackFlag)> selectableCallbackFunction;
+ bool inSelectionModelUpdate = false;
int assignedPositionViewAtRowAfterRebuild = 0;
int assignedPositionViewAtColumnAfterRebuild = 0;
@@ -587,6 +589,8 @@ public:
void normalizeSelection() override;
QRectF selectionRectangle() const override;
QSizeF scrollTowardsSelectionPoint(const QPointF &pos, const QSizeF &step) override;
+ void setCallback(std::function<void(CallBackFlag)> func) override;
+ void cancelSelectionTracking();
QPoint clampedCellAtPos(const QPointF &pos) const;
virtual void updateSelection(const QRect &oldSelection, const QRect &newSelection);
diff --git a/src/quicktemplates/qquickselectionrectangle.cpp b/src/quicktemplates/qquickselectionrectangle.cpp
index 7ef9996aae..fb737d017a 100644
--- a/src/quicktemplates/qquickselectionrectangle.cpp
+++ b/src/quicktemplates/qquickselectionrectangle.cpp
@@ -176,14 +176,6 @@ QQuickSelectionRectanglePrivate::QQuickSelectionRectanglePrivate()
m_scrollSpeed = QSizeF(qAbs(dist.width() * 0.007), qAbs(dist.height() * 0.007));
});
- QObject::connect(m_tapHandler, &QQuickTapHandler::tapped, [this] {
- const auto modifiers = m_tapHandler->point().modifiers();
- if (modifiers != Qt::NoModifier)
- return;
-
- updateActiveState(false);
- });
-
QObject::connect(m_tapHandler, &QQuickTapHandler::pressedChanged, [this]() {
if (!m_tapHandler->isPressed())
return;
@@ -223,9 +215,6 @@ QQuickSelectionRectanglePrivate::QQuickSelectionRectanglePrivate()
m_selectable->setSelectionEndPos(pos);
updateHandles();
updateActiveState(true);
- } else if (modifiers == Qt::NoModifier) {
- // Don't select any cell
- updateActiveState(false);
}
});
@@ -284,10 +273,11 @@ QQuickSelectionRectanglePrivate::QQuickSelectionRectanglePrivate()
return;
if (m_dragHandler->active()) {
- // Start a new selection if no selection exists from before.
- // Note that if the user pressed ControlModifier while starting
- // the drag, the first cell will be selected already on press.
- if (!m_active) {
+ // Start a new selection unless there is an active selection
+ // already, and one of the relevant modifiers are being held.
+ // In that case we continue to extend the active selection instead.
+ const bool modifiersHeld = modifiers & (Qt::ControlModifier | Qt::ShiftModifier);
+ if (!m_active || !modifiersHeld) {
if (!m_selectable->startSelection(startPos))
return;
m_selectable->setSelectionStartPos(startPos);
@@ -481,6 +471,25 @@ void QQuickSelectionRectanglePrivate::connectToTarget()
if (const auto flickable = qobject_cast<QQuickFlickable *>(m_target)) {
connect(flickable, &QQuickFlickable::interactiveChanged, this, &QQuickSelectionRectanglePrivate::updateSelectionMode);
}
+
+ // Add a callback function that tells if the selection was
+ // modified outside of the actions taken by SelectionRectangle.
+ m_selectable->setCallback([this](QQuickSelectable::CallBackFlag flag){
+ switch (flag) {
+ case QQuickSelectable::CallBackFlag::CancelSelection:
+ // The selection is either cleared, or can no longer be
+ // represented as a rectangle with two selection handles.
+ updateActiveState(false);
+ break;
+ case QQuickSelectable::CallBackFlag::SelectionRectangleChanged:
+ // The selection has changed, but the selection is still
+ // rectangular and without holes.
+ updateHandles();
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ });
}
void QQuickSelectionRectanglePrivate::updateSelectionMode()
@@ -559,6 +568,7 @@ void QQuickSelectionRectangle::setTarget(QQuickItem *target)
d->m_tapHandler->setParent(this);
d->m_dragHandler->setParent(this);
d->m_target->disconnect(this);
+ d->m_selectable->setCallback(nullptr);
}
d->m_target = target;
diff --git a/tests/auto/quickcontrols/controls/data/tst_selectionrectangle.qml b/tests/auto/quickcontrols/controls/data/tst_selectionrectangle.qml
index 5f6f60eba8..1c9413bb3e 100644
--- a/tests/auto/quickcontrols/controls/data/tst_selectionrectangle.qml
+++ b/tests/auto/quickcontrols/controls/data/tst_selectionrectangle.qml
@@ -726,4 +726,52 @@ TestCase {
}
}
+ function test_programmatic_unselect() {
+ // Check that the SelectionRectangle will be deactivated if the
+ // selection is changed programatically.
+ let tableView = createTemporaryObject(tableviewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ selectionRectangle.selectionMode = SelectionRectangle.Drag
+
+ verify(!tableView.selectionModel.hasSelection)
+ mouseDrag(tableView, 1, 1, (cellWidth * 2) - 2, 1, Qt.LeftButton)
+ compare(tableView.selectionModel.selectedIndexes.length, 2)
+ verify(selectionRectangle.active)
+
+ tableView.selectionModel.clearSelection()
+ verify(!selectionRectangle.active)
+ }
+
+ function test_extend_using_keyboard() {
+ // Check that the bottom-right selection handle will move if an
+ // acitve selection is extended with the keyboard
+ let tableView = createTemporaryObject(tableviewComp, testCase)
+ verify(tableView)
+ let selectionRectangle = tableView.selectionRectangle
+ verify(selectionRectangle)
+
+ selectionRectangle.bottomRightHandle = bottomRightHandleComp
+ selectionRectangle.selectionMode = SelectionRectangle.Drag
+
+ tableView.forceActiveFocus()
+ verify(!tableView.selectionModel.hasSelection)
+ mouseDrag(tableView, 1, 1, (cellWidth * 2) - 2, 1, Qt.LeftButton)
+ compare(tableView.selectionModel.selectedIndexes.length, 2)
+ verify(selectionRectangle.active)
+ verify(bottomRightHandle)
+ compare(bottomRightHandle.x, (cellWidth * 2) - (bottomRightHandle.width / 2))
+ compare(bottomRightHandle.y, cellHeight - (bottomRightHandle.height / 2))
+
+ keyPress(Qt.Key_Down, Qt.ShiftModifier)
+ keyRelease(Qt.Key_Down, Qt.ShiftModifier)
+ keyPress(Qt.Key_Right, Qt.ShiftModifier)
+ keyRelease(Qt.Key_Right, Qt.ShiftModifier)
+ verify(selectionRectangle.active)
+ compare(tableView.selectionModel.selectedIndexes.length, 6)
+ compare(bottomRightHandle.x, (cellWidth * 3) - (bottomRightHandle.width / 2))
+ compare(bottomRightHandle.y, (cellHeight * 2) - (bottomRightHandle.height / 2))
+ }
}
diff --git a/tests/manual/tableview/abstracttablemodel/main.qml b/tests/manual/tableview/abstracttablemodel/main.qml
index 9f4b8cfe7c..72b18e3480 100644
--- a/tests/manual/tableview/abstracttablemodel/main.qml
+++ b/tests/manual/tableview/abstracttablemodel/main.qml
@@ -179,6 +179,10 @@ ApplicationWindow {
"AnyKeyPressed",
]
}
+ Button {
+ text: "Clear selection"
+ onClicked: tableView.selectionModel.clearSelection()
+ }
}
}