From e7cff5bca7aefaea63e80598babf6bd7917aa1c3 Mon Sep 17 00:00:00 2001 From: Volker Hilsheimer Date: Tue, 10 Mar 2020 14:37:15 +0100 Subject: [PATCH] Fix keypad navigation within a button group for push buttons Keypad navigation within a group should work for auto-exclusive buttons, or for checkable buttons that are in a button group. Since the code already tests whether the button should be treated like an exclusive (which implies checkable) button, use the result of that test when finding the candidate button to move focus to, and not only when actually changing the checked button and the focus. Change-Id: I4dc41a90d51a8304483046252ceff0ebfe2a2e52 Fixes: QTBUG-27151 Done-with: david.faure@kdab.com Reviewed-by: Volker Hilsheimer --- src/widgets/widgets/qabstractbutton.cpp | 4 +- .../widgets/qbuttongroup/tst_qbuttongroup.cpp | 68 +++++++++++++++++++ 2 files changed, 70 insertions(+), 2 deletions(-) diff --git a/src/widgets/widgets/qabstractbutton.cpp b/src/widgets/widgets/qabstractbutton.cpp index 7961d0a21b..badeec37ee 100644 --- a/src/widgets/widgets/qabstractbutton.cpp +++ b/src/widgets/widgets/qabstractbutton.cpp @@ -246,7 +246,7 @@ void QAbstractButtonPrivate::notifyChecked() void QAbstractButtonPrivate::moveFocus(int key) { - QList buttonList = queryButtonList();; + QList buttonList = queryButtonList(); #if QT_CONFIG(buttongroup) bool exclusive = group ? group->d_func()->exclusive : autoExclusive; #else @@ -266,7 +266,7 @@ void QAbstractButtonPrivate::moveFocus(int key) for (int i = 0; i < buttonList.count(); ++i) { QAbstractButton *button = buttonList.at(i); if (button != f && button->window() == f->window() && button->isEnabled() && !button->isHidden() && - (autoExclusive || (button->focusPolicy() & focus_flag) == focus_flag)) { + (exclusive || (button->focusPolicy() & focus_flag) == focus_flag)) { QRect buttonRect = button->rect().translated(button->mapToGlobal(QPoint(0,0))); QPoint p = buttonRect.center(); diff --git a/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp b/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp index 279fe49e3a..3b8bcf98c3 100644 --- a/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp +++ b/tests/auto/widgets/widgets/qbuttongroup/tst_qbuttongroup.cpp @@ -83,6 +83,7 @@ Q_OBJECT private slots: void arrowKeyNavigation(); + void keyNavigationPushButtons(); void exclusive(); void exclusiveWithActions(); void testSignals(); @@ -185,6 +186,73 @@ void tst_QButtonGroup::arrowKeyNavigation() QVERIFY(bt3.hasFocus()); } +/* + Test that tab and arrow key navigation through buttons + in an invisible button group works as expected. Tabbing + into the group should give focus to the checked button, + and arrow navigation should change the checked button and + move focus. +*/ +void tst_QButtonGroup::keyNavigationPushButtons() +{ + if (!qt_tab_all_widgets()) + QSKIP("This test requires full keyboard control to be enabled."); + + QDialog dlg(nullptr); + QLineEdit *le1 = new QLineEdit; + le1->setObjectName("le1"); + QPushButton *pb1 = new QPushButton("Exclusive 1"); + pb1->setObjectName("pb1"); + pb1->setCheckable(true); + pb1->setChecked(true); + QPushButton *pb2 = new QPushButton("Exclusive 2"); + pb2->setObjectName("pb2"); + pb2->setCheckable(true); + QPushButton *pb3 = new QPushButton("Exclusive 3"); + pb3->setObjectName("pb3"); + pb3->setCheckable(true); + QLineEdit *le2 = new QLineEdit; + le2->setObjectName("le2"); + + QVBoxLayout* layout = new QVBoxLayout(&dlg); + layout->addWidget(le1); + layout->addWidget(pb1); + layout->addWidget(pb2); + layout->addWidget(pb3); + layout->addWidget(le2); + + QButtonGroup *buttonGroup = new QButtonGroup; + buttonGroup->addButton(pb1); + buttonGroup->addButton(pb2); + buttonGroup->addButton(pb3); + + dlg.show(); + qApp->setActiveWindow(&dlg); + if (!QTest::qWaitForWindowActive(&dlg)) + QSKIP("Window activation failed, skipping test"); + + QVERIFY2(le1->hasFocus(), qPrintable(qApp->focusWidget()->objectName())); + QTest::keyClick(qApp->focusWidget(), Qt::Key_Tab); + QVERIFY2(pb1->hasFocus(), qPrintable(qApp->focusWidget()->objectName())); + QVERIFY2(pb1->isChecked(), qPrintable(buttonGroup->checkedButton()->objectName())); + QTest::keyClick(qApp->focusWidget(), Qt::Key_Down); + QVERIFY2(pb2->hasFocus(), qPrintable(qApp->focusWidget()->objectName())); + QVERIFY2(pb2->isChecked(), qPrintable(buttonGroup->checkedButton()->objectName())); + QTest::keyClick(qApp->focusWidget(), Qt::Key_Down); + QVERIFY2(pb3->hasFocus(), qPrintable(qApp->focusWidget()->objectName())); + QVERIFY2(pb3->isChecked(), qPrintable(buttonGroup->checkedButton()->objectName())); + QTest::keyClick(qApp->focusWidget(), Qt::Key_Up); + QVERIFY2(pb2->hasFocus(), qPrintable(qApp->focusWidget()->objectName())); + QVERIFY2(pb2->isChecked(), qPrintable(buttonGroup->checkedButton()->objectName())); + QTest::keyClick(qApp->focusWidget(), Qt::Key_Tab); + QVERIFY2(le2->hasFocus(), qPrintable(qApp->focusWidget()->objectName())); + QTest::keyClick(qApp->focusWidget(), Qt::Key_Backtab); + QVERIFY2(pb2->hasFocus(), qPrintable(qApp->focusWidget()->objectName())); + QVERIFY2(pb2->isChecked(), qPrintable(buttonGroup->checkedButton()->objectName())); + QTest::keyClick(qApp->focusWidget(), Qt::Key_Backtab); + QVERIFY2(le1->hasFocus(), qPrintable(qApp->focusWidget()->objectName())); +} + void tst_QButtonGroup::exclusiveWithActions() { QDialog dlg(0);