Widgets: use QPlatformTheme::ButtonPressKeys for pressing buttons

QComboBox is included because it works like a button when it is not
editable. QGroupBox is included because it has a checkbox and QCheckBox
is a subclass of QAbstractButton.

Change-Id: Iad89259314e77f78c915dce83ec601df94c88941
Reviewed-by: Richard Moe Gustavsen <richard.gustavsen@qt.io>
This commit is contained in:
Noah Davis 2022-02-09 10:32:53 -05:00
parent 2de667a6ff
commit 64ffe0aacb
11 changed files with 156 additions and 41 deletions

View File

@ -55,6 +55,7 @@
#ifndef QT_NO_ACCESSIBILITY
#include "qaccessible.h"
#endif
#include <qpa/qplatformtheme.h>
#include <algorithm>
@ -1043,19 +1044,19 @@ void QAbstractButton::keyPressEvent(QKeyEvent *e)
{
Q_D(QAbstractButton);
bool next = true;
switch (e->key()) {
case Qt::Key_Enter:
case Qt::Key_Return:
e->ignore();
break;
case Qt::Key_Select:
case Qt::Key_Space:
if (!e->isAutoRepeat()) {
setDown(true);
repaint();
d->emitPressed();
}
break;
const auto key = e->key();
const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
->themeHint(QPlatformTheme::ButtonPressKeys)
.value<QList<Qt::Key>>();
if (buttonPressKeys.contains(key) && !e->isAutoRepeat()) {
setDown(true);
repaint();
d->emitPressed();
return;
}
switch (key) {
case Qt::Key_Up:
next = false;
Q_FALLTHROUGH();
@ -1120,15 +1121,15 @@ void QAbstractButton::keyReleaseEvent(QKeyEvent *e)
if (!e->isAutoRepeat())
d->repeatTimer.stop();
switch (e->key()) {
case Qt::Key_Select:
case Qt::Key_Space:
if (!e->isAutoRepeat() && d->down)
d->click();
break;
default:
e->ignore();
const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
->themeHint(QPlatformTheme::ButtonPressKeys)
.value<QList<Qt::Key>>();
if (buttonPressKeys.contains(e->key()) && !e->isAutoRepeat() && d->down) {
d->click();
return;
}
e->ignore();
}
/*!\reimp

View File

@ -3196,7 +3196,23 @@ void QComboBox::keyPressEvent(QKeyEvent *e)
Move move = NoMove;
int newIndex = currentIndex();
switch (e->key()) {
bool pressLikeButton = !d->lineEdit;
#ifdef QT_KEYPAD_NAVIGATION
pressLikeButton |= QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus();
#endif
auto key = e->key();
if (pressLikeButton) {
const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
->themeHint(QPlatformTheme::ButtonPressKeys)
.value<QList<Qt::Key>>();
if (buttonPressKeys.contains(key)) {
showPopup();
return;
}
}
switch (key) {
case Qt::Key_Up:
if (e->modifiers() & Qt::ControlModifier)
break; // pass to line edit for auto completion
@ -3238,26 +3254,11 @@ void QComboBox::keyPressEvent(QKeyEvent *e)
return;
}
break;
case Qt::Key_Space:
if (!d->lineEdit) {
showPopup();
return;
}
break;
case Qt::Key_Enter:
case Qt::Key_Return:
case Qt::Key_Escape:
if (!d->lineEdit)
e->ignore();
break;
#ifdef QT_KEYPAD_NAVIGATION
case Qt::Key_Select:
if (QApplicationPrivate::keypadNavigationEnabled()
&& (!hasEditFocus() || !d->lineEdit)) {
showPopup();
return;
}
break;
case Qt::Key_Left:
case Qt::Key_Right:
if (QApplicationPrivate::keypadNavigationEnabled() && !hasEditFocus())

View File

@ -54,6 +54,8 @@
#include "qaccessible.h"
#endif
#include <private/qwidget_p.h>
#include <private/qguiapplication_p.h>
#include <qpa/qplatformtheme.h>
#include "qdebug.h"
@ -360,7 +362,10 @@ bool QGroupBox::event(QEvent *e)
return true;
case QEvent::KeyPress: {
QKeyEvent *k = static_cast<QKeyEvent*>(e);
if (!k->isAutoRepeat() && (k->key() == Qt::Key_Select || k->key() == Qt::Key_Space)) {
const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
->themeHint(QPlatformTheme::ButtonPressKeys)
.value<QList<Qt::Key>>();
if (!k->isAutoRepeat() && buttonPressKeys.contains(k->key())) {
d->pressedControl = QStyle::SC_GroupBoxCheckBox;
update(style()->subControlRect(QStyle::CC_GroupBox, &box, QStyle::SC_GroupBoxCheckBox, this));
return true;
@ -369,7 +374,10 @@ bool QGroupBox::event(QEvent *e)
}
case QEvent::KeyRelease: {
QKeyEvent *k = static_cast<QKeyEvent*>(e);
if (!k->isAutoRepeat() && (k->key() == Qt::Key_Select || k->key() == Qt::Key_Space)) {
const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
->themeHint(QPlatformTheme::ButtonPressKeys)
.value<QList<Qt::Key>>();
if (!k->isAutoRepeat() && buttonPressKeys.contains(k->key())) {
bool toggle = (d->pressedControl == QStyle::SC_GroupBoxLabel
|| d->pressedControl == QStyle::SC_GroupBoxCheckBox);
d->pressedControl = QStyle::SC_None;

View File

@ -42,6 +42,7 @@
#include <private/qguiapplication_p.h>
#include <qpa/qplatformintegration.h>
#include <qpa/qplatformtheme.h>
class tst_QAbstractButton : public QObject
{
@ -81,6 +82,8 @@ private slots:
void keyNavigation();
#endif
void buttonPressKeys();
protected slots:
void onClicked();
void onToggled( bool on );
@ -276,7 +279,13 @@ void tst_QAbstractButton::setAutoRepeat()
QCOMPARE(press_count, click_count);
QVERIFY(click_count > 1);
break;
case 4:
case 4: {
const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
->themeHint(QPlatformTheme::ButtonPressKeys)
.value<QList<Qt::Key>>();
if (buttonPressKeys.contains(Qt::Key_Enter)) {
QSKIP("platform theme has Key_Enter in ButtonPressKeys");
}
// check that pressing ENTER has no effect when autorepeat is false
testWidget->setDown( false );
testWidget->setAutoRepeat( false );
@ -293,7 +302,14 @@ void tst_QAbstractButton::setAutoRepeat()
QVERIFY( click_count == 0 );
break;
case 5:
}
case 5: {
const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
->themeHint(QPlatformTheme::ButtonPressKeys)
.value<QList<Qt::Key>>();
if (buttonPressKeys.contains(Qt::Key_Enter)) {
QSKIP("platform theme has Key_Enter in ButtonPressKeys");
}
// check that pressing ENTER has no effect when autorepeat is true
testWidget->setDown( false );
testWidget->setAutoRepeat( true );
@ -311,6 +327,7 @@ void tst_QAbstractButton::setAutoRepeat()
QVERIFY( click_count == 0 );
break;
}
case 6:
// verify autorepeat is off by default.
MyButton tmp( 0);
@ -687,5 +704,16 @@ void tst_QAbstractButton::keyNavigation()
}
#endif
void tst_QAbstractButton::buttonPressKeys()
{
const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
->themeHint(QPlatformTheme::ButtonPressKeys)
.value<QList<Qt::Key>>();
for (int i = 0; i < buttonPressKeys.length(); ++i) {
QTest::keyClick(testWidget, buttonPressKeys[i]);
QCOMPARE(click_count, i + 1);
}
}
QTEST_MAIN(tst_QAbstractButton)
#include "tst_qabstractbutton.moc"

View File

@ -170,6 +170,7 @@ private slots:
void checkMenuItemPosWhenStyleSheetIsSet();
void checkEmbeddedLineEditWhenStyleSheetIsSet();
void propagateStyleChanges();
void buttonPressKeys();
private:
PlatformInputContext m_platformInputContext;
@ -3588,5 +3589,24 @@ void tst_QComboBox::propagateStyleChanges()
QVERIFY(frameStyle.inquired);
}
void tst_QComboBox::buttonPressKeys()
{
QComboBox comboBox;
comboBox.setEditable(false);
comboBox.addItem(QString::number(1));
comboBox.addItem(QString::number(2));
const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
->themeHint(QPlatformTheme::ButtonPressKeys)
.value<QList<Qt::Key>>();
for (int i = 0; i < buttonPressKeys.length(); ++i) {
QTest::keyClick(&comboBox, buttonPressKeys[i]);
// On some platforms, a window will not be immediately visible,
// but take some event-loop iterations to complete.
// Using QTRY_VERIFY to deal with that.
QTRY_VERIFY(comboBox.view()->isVisible());
comboBox.hidePopup();
}
}
QTEST_MAIN(tst_QComboBox)
#include "tst_qcombobox.moc"

View File

@ -9,5 +9,6 @@ qt_internal_add_test(tst_qcommandlinkbutton
tst_qcommandlinkbutton.cpp
PUBLIC_LIBRARIES
Qt::Gui
Qt::GuiPrivate
Qt::Widgets
)

View File

@ -40,6 +40,9 @@
#include <QGridLayout>
#include <QPainter>
#include <private/qguiapplication_p.h>
#include <qpa/qplatformtheme.h>
class tst_QCommandLinkButton : public QObject
{
Q_OBJECT
@ -226,6 +229,13 @@ void tst_QCommandLinkButton::setAutoRepeat()
// check that pressing ENTER has no effect
resetCounters();
testWidget->setDown( false );
// Skip after reset if ButtonPressKeys has Key_Enter
const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
->themeHint(QPlatformTheme::ButtonPressKeys)
.value<QList<Qt::Key>>();
if (buttonPressKeys.contains(Qt::Key_Enter)) {
return;
}
testWidget->setAutoRepeat( false );
QTest::keyPress( testWidget, Qt::Key_Enter );
@ -258,6 +268,14 @@ void tst_QCommandLinkButton::pressed()
QCOMPARE( press_count, (uint)1 );
QCOMPARE( release_count, (uint)1 );
// Skip if ButtonPressKeys has Key_Enter
const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
->themeHint(QPlatformTheme::ButtonPressKeys)
.value<QList<Qt::Key>>();
if (buttonPressKeys.contains(Qt::Key_Enter)) {
return;
}
QTest::keyPress( testWidget,Qt::Key_Enter );
QCOMPARE( press_count, (uint)1 );
QCOMPARE( release_count, (uint)1 );

View File

@ -9,5 +9,6 @@ qt_internal_add_test(tst_qgroupbox
tst_qgroupbox.cpp
PUBLIC_LIBRARIES
Qt::Gui
Qt::GuiPrivate
Qt::Widgets
)

View File

@ -36,6 +36,9 @@
#include <QDialog>
#include <QSignalSpy>
#include <private/qguiapplication_p.h>
#include <qpa/qplatformtheme.h>
#include "qgroupbox.h"
class tst_QGroupBox : public QObject
@ -70,6 +73,7 @@ private slots:
void propagateFocus();
void task_QTBUG_19170_ignoreMouseReleaseEvent();
void task_QTBUG_15519_propagateMouseEvents();
void buttonPressKeys();
private:
bool checked;
@ -611,6 +615,20 @@ void tst_QGroupBox::task_QTBUG_15519_propagateMouseEvents()
QCOMPARE(parent.mouseMoved, true);
}
void tst_QGroupBox::buttonPressKeys()
{
QGroupBox groupBox;
groupBox.setCheckable(true);
QSignalSpy clickedSpy(&groupBox, &QGroupBox::clicked);
const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
->themeHint(QPlatformTheme::ButtonPressKeys)
.value<QList<Qt::Key>>();
for (int i = 0; i < buttonPressKeys.length(); ++i) {
QTest::keyClick(&groupBox, buttonPressKeys[i]);
QCOMPARE(clickedSpy.length(), i + 1);
}
}
void tst_QGroupBox::sendMouseMoveEvent(QWidget *widget, const QPoint &localPos)
{
// Send a MouseMove event without actually moving the pointer

View File

@ -9,5 +9,6 @@ qt_internal_add_test(tst_qpushbutton
tst_qpushbutton.cpp
PUBLIC_LIBRARIES
Qt::Gui
Qt::GuiPrivate
Qt::Widgets
)

View File

@ -41,6 +41,9 @@
#include <QStyleFactory>
#include <QTabWidget>
#include <private/qguiapplication_p.h>
#include <qpa/qplatformtheme.h>
class tst_QPushButton : public QObject
{
Q_OBJECT
@ -220,6 +223,13 @@ void tst_QPushButton::autoRepeat()
// check that pressing ENTER has no effect
resetCounters();
testWidget->setDown( false );
// Skip after reset if ButtonPressKeys has Key_Enter
const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
->themeHint(QPlatformTheme::ButtonPressKeys)
.value<QList<Qt::Key>>();
if (buttonPressKeys.contains(Qt::Key_Enter)) {
return;
}
testWidget->setAutoRepeat( false );
QTest::keyPress( testWidget, Qt::Key_Enter );
@ -255,6 +265,14 @@ void tst_QPushButton::pressed()
QCOMPARE( press_count, (uint)1 );
QCOMPARE( release_count, (uint)1 );
// Skip if ButtonPressKeys has Key_Enter
const auto buttonPressKeys = QGuiApplicationPrivate::platformTheme()
->themeHint(QPlatformTheme::ButtonPressKeys)
.value<QList<Qt::Key>>();
if (buttonPressKeys.contains(Qt::Key_Enter)) {
return;
}
QTest::keyPress( testWidget,Qt::Key_Enter );
QCOMPARE( press_count, (uint)1 );
QCOMPARE( release_count, (uint)1 );