diff options
| author | Richard Moe Gustavsen <richard.gustavsen@qt.io> | 2022-12-20 15:57:23 +0100 |
|---|---|---|
| committer | Richard Moe Gustavsen <richard.gustavsen@qt.io> | 2023-01-20 20:39:11 +0100 |
| commit | ea5200b82f21e0f4d080d3fc256b218e0ee56f3d (patch) | |
| tree | be8473c4b27dec02305d71e0e36600646004a5cb /src/quick/items/qquicktableview.cpp | |
| parent | e34142e6cda177b601d8e5daba00a2ab84828db1 (diff) | |
QQuickTableView: set active focus directly on the edit item
When starting to edit a cell, the current implementation
calls editItem->nextItemInFocusChain(true) to resolve the
child to get active focus. But a better way is to instead
rely on the edit item being a FocusScope. That way, we can
simply set active focus on the edit item directly, and rely
on the FocusScope forwarding active focus to the right child.
After all, that is what FocusScopes are for.
This patch will therefore change the implementation to set active
focus directly on the edit item.
But doing so turns out to cause tabbing from one cell
to the next to stop working. The reason is that QQuickItem refuses
to change setActiveFocusOnTab() on an item that has active focus.
Instead, the focus item will eat the tab event, and
use it to transfer focus to the next control in the chain.
We therefore change the implementation to use an event filter on the
focus object. That way, we're are guaranteed to always get a first
shot at handling all critical key events that are needed for editing
to work correctly. This includes tabbing, but even more
imporantant, also Qt::Key_Enter, which is needed to allow
the user to commit and close the editor.
Pick-to: 6.5
Change-Id: I215b7efc52093eb0bd7f6a4fb60a57f83101e288
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Diffstat (limited to 'src/quick/items/qquicktableview.cpp')
| -rw-r--r-- | src/quick/items/qquicktableview.cpp | 134 |
1 files changed, 70 insertions, 64 deletions
diff --git a/src/quick/items/qquicktableview.cpp b/src/quick/items/qquicktableview.cpp index 42aaaeb82c..36537a20b0 100644 --- a/src/quick/items/qquicktableview.cpp +++ b/src/quick/items/qquicktableview.cpp @@ -1345,10 +1345,9 @@ \snippet qml/tableview/editdelegate.qml 1 - When the edit delegate is instantiated, it will call \l QQuickItem::forceActiveFocus() - on the first item inside the delegate that has \l QQuickItem::activeFocusOnTab set to - \c true. You can override this by calling \l QQuickItem::forceActiveFocus() explicitly - on some other item inside the delegate from \l {Component::completed()}{Component.completed()}. + When the edit delegate is instantiated, TableView will call \l QQuickItem::forceActiveFocus() + on it. If you want active focus to be set on a child of the edit delegate instead, let + the edit delegate be a \l FocusScope. \sa editTriggers, TableView::commit, edit(), closeEditor(), {Editing cells} */ @@ -5046,34 +5045,33 @@ bool QQuickTableViewPrivate::editFromKeyEvent(QKeyEvent *e) if (!attached || !attached->editDelegate()) return false; - bool anyKeyAccepted = false; + bool anyKeyPressed = false; bool editKeyPressed = false; - if (editTriggers & QQuickTableView::AnyKeyPressed) { - switch (e->key()) { - case Qt::Key_Shift: - case Qt::Key_Alt: - case Qt::Key_Control: - case Qt::Key_Meta: - break; - default: - anyKeyAccepted = true; - } - } - - if (editTriggers & QQuickTableView::EditKeyPressed) { - switch (e->key()) { - case Qt::Key_Return: - case Qt::Key_Enter: + switch (e->key()) { + case Qt::Key_Return: + case Qt::Key_Enter: #ifndef Q_OS_MACOS - case Qt::Key_F2: + case Qt::Key_F2: #endif - editKeyPressed = true; - break; - } + anyKeyPressed = true; + editKeyPressed = true; + break; + case Qt::Key_Shift: + case Qt::Key_Alt: + case Qt::Key_Control: + case Qt::Key_Meta: + case Qt::Key_Tab: + case Qt::Key_Backtab: + break; + default: + anyKeyPressed = true; } - if (!(editKeyPressed || anyKeyAccepted)) + const bool anyKeyAccepted = anyKeyPressed && (editTriggers & QQuickTableView::AnyKeyPressed); + const bool editKeyAccepted = editKeyPressed && (editTriggers & QQuickTableView::EditKeyPressed); + + if (!(editKeyAccepted || anyKeyAccepted)) return false; if (!canEdit(index, false)) { @@ -6114,22 +6112,16 @@ void QQuickTableView::edit(const QModelIndex &index) // Inform the delegate, and the edit delegate, that they're being edited d->setRequiredProperty(kRequiredProperty_editing, QVariant::fromValue(true), cellIndex, cellItem, false); - // Find the first child inside the delegate that wants focus. But - // we only transfer focus if the edit item didn't already set - // it to some other internal item explicitly upon construction. - QObject *focusObject = d->editItem->window()->focusObject(); - QQuickItem *focusItem = qobject_cast<QQuickItem *>(focusObject); - if (!d->editItem->isAncestorOf(focusItem)) { - QQuickItem *newFocusItem = d->editItem; - if (!newFocusItem->activeFocusOnTab()) - newFocusItem = d->editItem->nextItemInFocusChain(true); - if (newFocusItem == d->editItem || d->editItem->isAncestorOf(newFocusItem)) { - // Set activeFocusOnTab to false to ensure that QQuickTableView::keyPressEvent() - // receives the tab keys, instead of QQuickItemPrivate::focusNextPrev(). - // TableView knows better which cell is next in the focus chain. - newFocusItem->setActiveFocusOnTab(false); - newFocusItem->forceActiveFocus(Qt::MouseFocusReason); - } + // Transfer focus to the edit item + d->editItem->forceActiveFocus(Qt::MouseFocusReason); + + // Install an event filter on the focus object to handle Enter and Tab. + // Note that the focusObject doesn't need to be the editItem itself, in + // case the editItem is a FocusScope. + if (QObject *focusObject = d->editItem->window()->focusObject()) { + QQuickItem *focusItem = qobject_cast<QQuickItem *>(focusObject); + if (focusItem == d->editItem || d->editItem->isAncestorOf(focusItem)) + focusItem->installEventFilter(this); } } @@ -6232,28 +6224,6 @@ void QQuickTableView::keyPressEvent(QKeyEvent *e) if (d->editIndex.isValid()) { // While editing, we limit the keys that we // handle to not interfere with editing. - if (d->editTriggers != QQuickTableView::NoEditTriggers) { - switch (e->key()) { - case Qt::Key_Enter: - case Qt::Key_Return: - if (auto attached = d->getAttachedObject(d->editItem)) - emit attached->commit(); - closeEditor(); - break; - case Qt::Key_Escape: - closeEditor(); - break; - case Qt::Key_Tab: - case Qt::Key_Backtab: - if (activeFocusOnTab()) { - if (auto attached = d->getAttachedObject(d->editItem)) - emit attached->commit(); - if (d->setCurrentIndexFromKeyEvent(e)) - edit(d->selectionModel->currentIndex()); - } - break; - } - } return; } @@ -6266,6 +6236,42 @@ void QQuickTableView::keyPressEvent(QKeyEvent *e) QQuickFlickable::keyPressEvent(e); } +bool QQuickTableView::eventFilter(QObject *obj, QEvent *event) +{ + Q_D(QQuickTableView); + + if (event->type() == QEvent::KeyPress) { + Q_ASSERT(d->editItem); + QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event); + switch (keyEvent->key()) { + case Qt::Key_Enter: + case Qt::Key_Return: + if (auto attached = d->getAttachedObject(d->editItem)) + emit attached->commit(); + closeEditor(); + return true; + case Qt::Key_Tab: + case Qt::Key_Backtab: + if (activeFocusOnTab()) { + if (auto attached = d->getAttachedObject(d->editItem)) + emit attached->commit(); + closeEditor(); + if (d->setCurrentIndexFromKeyEvent(keyEvent)) { + const QModelIndex currentIndex = d->selectionModel->currentIndex(); + if (d->canEdit(currentIndex, false)) + edit(currentIndex); + } + return true; + } + case Qt::Key_Escape: + closeEditor(); + return true; + } + } + + return QQuickFlickable::eventFilter(obj, event); +} + bool QQuickTableView::alternatingRows() const { return d_func()->alternatingRows; |
