From b23626d0b8ceff93c85da769258779ba61b381ae Mon Sep 17 00:00:00 2001 From: Richard Moe Gustavsen Date: Thu, 7 Apr 2022 10:07:28 +0200 Subject: [PATCH] QQuickTreeView: add keyboard navigation support Override keyPressEvent in QQuickTreeView to let the left and right arrow keys expand and collapse the current row in the tree. Note that the application developer can set keyNavigationEnabled to false if this default behavior is not wanted. Change-Id: If5201dafd1d3d7e2e4a113aeb2a60f601a1a4c35 Reviewed-by: Mitch Curtis --- src/quick/items/qquicktreeview.cpp | 27 ++++++++ src/quick/items/qquicktreeview_p.h | 3 + .../qquicktreeview/data/CustomDelegate.qml | 4 +- .../qquicktreeview/data/normaltreeview.qml | 1 + .../qquicktreeview/tst_qquicktreeview.cpp | 63 +++++++++++++++++++ 5 files changed, 97 insertions(+), 1 deletion(-) diff --git a/src/quick/items/qquicktreeview.cpp b/src/quick/items/qquicktreeview.cpp index dca7940149..a56b4e1577 100644 --- a/src/quick/items/qquicktreeview.cpp +++ b/src/quick/items/qquicktreeview.cpp @@ -571,6 +571,33 @@ QModelIndex QQuickTreeView::modelIndex(int column, int row) const return modelIndex({column, row}); } +void QQuickTreeView::keyPressEvent(QKeyEvent *event) +{ + event->ignore(); + + if (!keyNavigationEnabled()) + return; + if (!selectionModel()) + return; + + const int row = cellAtIndex(selectionModel()->currentIndex()).y(); + switch (event->key()) { + case Qt::Key_Left: + collapse(row); + event->accept(); + break; + case Qt::Key_Right: + expand(row); + event->accept(); + break; + default: + break; + } + + if (!event->isAccepted()) + QQuickTableView::keyPressEvent(event); +} + QT_END_NAMESPACE #include "moc_qquicktreeview_p.cpp" diff --git a/src/quick/items/qquicktreeview_p.h b/src/quick/items/qquicktreeview_p.h index bfebcea619..b57ee48ff0 100644 --- a/src/quick/items/qquicktreeview_p.h +++ b/src/quick/items/qquicktreeview_p.h @@ -87,6 +87,9 @@ signals: void expanded(int row, int depth); void collapsed(int row, bool recursively); +protected: + void keyPressEvent(QKeyEvent *event) override; + private: Q_DISABLE_COPY(QQuickTreeView) Q_DECLARE_PRIVATE(QQuickTreeView) diff --git a/tests/auto/quick/qquicktreeview/data/CustomDelegate.qml b/tests/auto/quick/qquicktreeview/data/CustomDelegate.qml index 47c36977df..1a501141f0 100644 --- a/tests/auto/quick/qquicktreeview/data/CustomDelegate.qml +++ b/tests/auto/quick/qquicktreeview/data/CustomDelegate.qml @@ -40,11 +40,12 @@ import QtQuick import TestModel -Item { +Rectangle { id: root implicitWidth: padding + label.x + label.implicitWidth + padding implicitHeight: label.implicitHeight * 1.5 + color: current ? "lightgreen" : "white" property alias text: label.text @@ -57,6 +58,7 @@ Item { required property bool expanded required property int hasChildren required property int depth + required property bool current TapHandler { onTapped: treeView.toggleExpanded(row) diff --git a/tests/auto/quick/qquicktreeview/data/normaltreeview.qml b/tests/auto/quick/qquicktreeview/data/normaltreeview.qml index 58f077fd6b..b72799ba03 100644 --- a/tests/auto/quick/qquicktreeview/data/normaltreeview.qml +++ b/tests/auto/quick/qquicktreeview/data/normaltreeview.qml @@ -51,6 +51,7 @@ Item { anchors.fill:parent anchors.margins: 10 model: TestModel {} + selectionModel: ItemSelectionModel {} clip: true delegate: CustomDelegate {} diff --git a/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp b/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp index 1779beb9a0..5101148209 100644 --- a/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp +++ b/tests/auto/quick/qquicktreeview/tst_qquicktreeview.cpp @@ -109,6 +109,7 @@ private slots: void emptyModel(); void updatedModifiedModel(); void insertRows(); + void toggleExpandedUsingArrowKeys(); }; tst_qquicktreeview::tst_qquicktreeview() @@ -731,6 +732,68 @@ void tst_qquicktreeview::expandToIndex() QCOMPARE(treeView->rows(), 13); } +void tst_qquicktreeview::toggleExpandedUsingArrowKeys() +{ + // Check that you can use the left and right arrow key to + // expand and collapse nodes in the tree. + LOAD_TREEVIEW("normaltreeview.qml"); + + treeView->setFocus(true); + QQuickWindow *window = treeView->window(); + + // Start by making cell 0, 0 current + treeView->selectionModel()->setCurrentIndex(treeView->modelIndex(0, 0), QItemSelectionModel::NoUpdate); + + // Expand row 0 + const int row0 = 0; + QCOMPARE(treeView->rows(), 1); + QVERIFY(!treeView->isExpanded(row0)); + QTest::keyPress(window, Qt::Key_Right); + QVERIFY(treeView->isExpanded(row0)); + + // A polish event was scheduled, but since the call to keyPress() processes + // events, WAIT_UNTIL_POLISHED will be unreliable. So use QTRY_COMPARE instead. + QTRY_COMPARE(treeView->rows(), 5); + + // Hitting Key_Right again should be a no-op + QTest::keyPress(window, Qt::Key_Right); + QVERIFY(treeView->isExpanded(row0)); + QCOMPARE(treeView->selectionModel()->currentIndex(), treeView->modelIndex(0, row0)); + + // Move down to row 1 and try to expand it. Since Row 1 + // doesn't have children, expanding it will be a no-op. + // And also, it shouldn't move currentIndex to the next + // column either, it should stay in the tree column. + const int row1 = 1; + QVERIFY(!treeView->isExpanded(row1)); + QTest::keyPress(window, Qt::Key_Down); + QTest::keyPress(window, Qt::Key_Right); + QVERIFY(!treeView->isExpanded(row1)); + QCOMPARE(treeView->selectionModel()->currentIndex(), treeView->modelIndex(0, row1)); + + // Move down to row 4 and expand it + const int row4 = 4; + QCOMPARE(treeView->rows(), 5); + while (treeView->currentRow() != row4) + QTest::keyPress(window, Qt::Key_Down); + QVERIFY(!treeView->isExpanded(row4)); + QTest::keyPress(window, Qt::Key_Right); + QVERIFY(treeView->isExpanded(row4)); + QCOMPARE(treeView->selectionModel()->currentIndex(), treeView->modelIndex(0, row4)); + + // Move up again to row 0 and collapse it + while (treeView->currentRow() != row0) + QTest::keyPress(window, Qt::Key_Up); + QVERIFY(treeView->isExpanded(row0)); + QTest::keyPress(window, Qt::Key_Left); + QVERIFY(!treeView->isExpanded(row0)); + QTRY_COMPARE(treeView->rows(), 1); + + // Hitting Key_Left again should be a no-op + QTest::keyPress(window, Qt::Key_Left); + QVERIFY(!treeView->isExpanded(row0)); + QCOMPARE(treeView->selectionModel()->currentIndex(), treeView->modelIndex(0, row0)); +} QTEST_MAIN(tst_qquicktreeview)