From a8dd4f7d5b8f61b52a5aea7b8418a6a3961cae1e Mon Sep 17 00:00:00 2001 From: J-P Nurmi Date: Tue, 13 Jun 2017 15:05:59 +0200 Subject: QQuickAbstractButton: add preliminary support for mnemonics This patch adds mnemonic shortcut functionality. We don't have a nice way to visualize mnemonics yet, so we just remove the ampersands from the UI for now. Change-Id: I90f54bd18d5d17f11e02c18c8461bfc25ce51cf1 Reviewed-by: Qt CI Bot Reviewed-by: Mitch Curtis --- src/quicktemplates2/qquickabstractbutton.cpp | 86 ++++++++++++++++++++++++- src/quicktemplates2/qquickabstractbutton_p.h | 8 +++ src/quicktemplates2/qquickabstractbutton_p_p.h | 10 +++ tests/auto/controls/data/tst_abstractbutton.qml | 34 ++++++++++ 4 files changed, 136 insertions(+), 2 deletions(-) diff --git a/src/quicktemplates2/qquickabstractbutton.cpp b/src/quicktemplates2/qquickabstractbutton.cpp index 23b73200..e66a33e5 100644 --- a/src/quicktemplates2/qquickabstractbutton.cpp +++ b/src/quicktemplates2/qquickabstractbutton.cpp @@ -39,9 +39,12 @@ #include "qquickbuttongroup_p.h" #include "qquickaction_p.h" #include "qquickaction_p_p.h" +#include "qquickshortcutcontext_p_p.h" #include #include +#include +#include #include #include @@ -176,6 +179,9 @@ QQuickAbstractButtonPrivate::QQuickAbstractButtonPrivate() holdTimer(0), delayTimer(0), repeatTimer(0), +#if QT_CONFIG(shortcut) + shortcutId(0), +#endif pressButtons(Qt::NoButton), indicator(nullptr), group(nullptr), @@ -303,6 +309,30 @@ void QQuickAbstractButtonPrivate::stopPressRepeat() } } +#if QT_CONFIG(shortcut) +void QQuickAbstractButtonPrivate::grabShortcut() +{ + Q_Q(QQuickAbstractButton); + if (shortcut.isEmpty()) + return; + + shortcutId = QGuiApplicationPrivate::instance()->shortcutMap.addShortcut(q, shortcut, Qt::WindowShortcut, QQuickShortcutContext::matcher); + + if (!q->isEnabled()) + QGuiApplicationPrivate::instance()->shortcutMap.setShortcutEnabled(false, shortcutId, q); +} + +void QQuickAbstractButtonPrivate::ungrabShortcut() +{ + Q_Q(QQuickAbstractButton); + if (!shortcutId) + return; + + QGuiApplicationPrivate::instance()->shortcutMap.removeShortcut(shortcutId, q); + shortcutId = 0; +} +#endif + void QQuickAbstractButtonPrivate::click() { Q_Q(QQuickAbstractButton); @@ -424,8 +454,12 @@ void QQuickAbstractButton::setText(const QString &text) if (d->text == text) return; - d->text = text; - setAccessibleName(text); +#if QT_CONFIG(shortcut) + setShortcut(QKeySequence::mnemonic(text)); +#endif + + d->text = QPlatformTheme::removeMnemonics(text); // ### TODO: visualize mnemonics + setAccessibleName(d->text); buttonChange(ButtonTextChange); emit textChanged(); } @@ -775,6 +809,25 @@ void QQuickAbstractButton::setAction(QQuickAction *action) emit actionChanged(); } +#if QT_CONFIG(shortcut) +QKeySequence QQuickAbstractButton::shortcut() const +{ + Q_D(const QQuickAbstractButton); + return d->shortcut; +} + +void QQuickAbstractButton::setShortcut(const QKeySequence &shortcut) +{ + Q_D(QQuickAbstractButton); + if (d->shortcut == shortcut) + return; + + d->ungrabShortcut(); + d->shortcut = shortcut; + d->grabShortcut(); +} +#endif + /*! \qmlmethod void QtQuick.Controls::AbstractButton::toggle() @@ -786,6 +839,21 @@ void QQuickAbstractButton::toggle() setChecked(!d->checked); } +bool QQuickAbstractButton::event(QEvent *event) +{ + Q_D(QQuickAbstractButton); +#if QT_CONFIG(shortcut) + if (event->type() == QEvent::Shortcut) { + QShortcutEvent *se = static_cast(event); + if (se->shortcutId() == d->shortcutId) { + d->click(); + return true; + } + } +#endif + return QQuickControl::event(event); +} + void QQuickAbstractButton::focusOutEvent(QFocusEvent *event) { Q_D(QQuickAbstractButton); @@ -857,6 +925,20 @@ void QQuickAbstractButton::timerEvent(QTimerEvent *event) } } +void QQuickAbstractButton::itemChange(ItemChange change, const ItemChangeData &value) +{ + Q_D(QQuickAbstractButton); + QQuickControl::itemChange(change, value); +#if QT_CONFIG(shortcut) + if (change == ItemVisibleHasChanged) { + if (value.boolValue) + d->grabShortcut(); + else + d->ungrabShortcut(); + } +#endif +} + void QQuickAbstractButton::buttonChange(ButtonChange change) { Q_D(QQuickAbstractButton); diff --git a/src/quicktemplates2/qquickabstractbutton_p.h b/src/quicktemplates2/qquickabstractbutton_p.h index 25827c9f..81384298 100644 --- a/src/quicktemplates2/qquickabstractbutton_p.h +++ b/src/quicktemplates2/qquickabstractbutton_p.h @@ -116,6 +116,11 @@ public: QQuickAction *action() const; void setAction(QQuickAction *action); +#if QT_CONFIG(shortcut) + QKeySequence shortcut() const; + void setShortcut(const QKeySequence &shortcut); +#endif + public Q_SLOTS: void toggle(); @@ -141,6 +146,7 @@ Q_SIGNALS: protected: QQuickAbstractButton(QQuickAbstractButtonPrivate &dd, QQuickItem *parent); + bool event(QEvent *event) override; void focusOutEvent(QFocusEvent *event) override; void keyPressEvent(QKeyEvent *event) override; void keyReleaseEvent(QKeyEvent *event) override; @@ -148,6 +154,8 @@ protected: void mouseDoubleClickEvent(QMouseEvent *event) override; void timerEvent(QTimerEvent *event) override; + void itemChange(ItemChange change, const ItemChangeData &value) override; + enum ButtonChange { ButtonAutoRepeatChange, ButtonCheckedChange, diff --git a/src/quicktemplates2/qquickabstractbutton_p_p.h b/src/quicktemplates2/qquickabstractbutton_p_p.h index f1fef5e5..659846e5 100644 --- a/src/quicktemplates2/qquickabstractbutton_p_p.h +++ b/src/quicktemplates2/qquickabstractbutton_p_p.h @@ -50,6 +50,7 @@ #include #include +#include QT_BEGIN_NAMESPACE @@ -81,6 +82,11 @@ public: void startPressRepeat(); void stopPressRepeat(); +#if QT_CONFIG(shortcut) + void grabShortcut(); + void ungrabShortcut(); +#endif + QQuickAbstractButton *findCheckedButton() const; QList findExclusiveButtons() const; @@ -101,6 +107,10 @@ public: int holdTimer; int delayTimer; int repeatTimer; +#if QT_CONFIG(shortcut) + int shortcutId; + QKeySequence shortcut; +#endif QQuickIcon icon; QPointF pressPoint; Qt::MouseButtons pressButtons; diff --git a/tests/auto/controls/data/tst_abstractbutton.qml b/tests/auto/controls/data/tst_abstractbutton.qml index a7f4ab8a..c65930be 100644 --- a/tests/auto/controls/data/tst_abstractbutton.qml +++ b/tests/auto/controls/data/tst_abstractbutton.qml @@ -289,4 +289,38 @@ TestCase { compare(buttonSpy.count, data.clicked ? 1 : 0) compare(actionSpy.count, data.triggered ? 1 : 0) } + + function test_mnemonic() { + if (Qt.platform.os === "osx" || Qt.platform.os === "macos") + skip("Mnemonics are not used on macOS") + + var control = createTemporaryObject(button, testCase) + verify(control) + + control.text = "&Hello" + compare(control.text, "Hello") // ### TODO: visualize mnemonics + + var clickSpy = signalSpy.createObject(control, {target: control, signalName: "clicked"}) + verify(clickSpy.valid) + + keyClick(Qt.Key_H, Qt.AltModifier) + compare(clickSpy.count, 1) + + control.visible = false + keyClick(Qt.Key_H, Qt.AltModifier) + compare(clickSpy.count, 1) + + control.visible = true + keyClick(Qt.Key_H, Qt.AltModifier) + compare(clickSpy.count, 2) + + control.text = "Te&st" + compare(control.text, "Test") // ### TODO: visualize mnemonics + + keyClick(Qt.Key_H, Qt.AltModifier) + compare(clickSpy.count, 2) + + keyClick(Qt.Key_S, Qt.AltModifier) + compare(clickSpy.count, 3) + } } -- cgit v1.2.3