Propagate ShortcutOverride events up the parent chain
As with key events, ShortcutOverride events have to propagate up the parent chain so that parent items can override shortcuts with their own key handling. For that to work, items that implement shortcutOverride but don't handle the event must explicitly ignore it, as the events, like all input events are accepted by default. This revealed that QQuickTextEdit (via QQuickTextControl) and QQuickTextInput did not correclty ignore unhandled shortcut overrides, so fix those implementations. As a drive-by, explicitly capture the event parameter in the QML test's signal handler. Task-number: QTBUG-107703 Pick-to: 6.4 Change-Id: If38469c000733f53f5f936de38869b3b6f9fb07a Reviewed-by: Mitch Curtis <mitch.curtis@qt.io> Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
parent
9e000eab70
commit
a818d49aa5
|
@ -322,6 +322,8 @@ void QQuickItemKeyFilter::shortcutOverride(QKeyEvent *event)
|
|||
{
|
||||
if (m_next)
|
||||
m_next->shortcutOverride(event);
|
||||
else
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
void QQuickItemKeyFilter::componentComplete()
|
||||
|
@ -5491,9 +5493,10 @@ void QQuickItemPrivate::deliverInputMethodEvent(QInputMethodEvent *e)
|
|||
|
||||
void QQuickItemPrivate::deliverShortcutOverrideEvent(QKeyEvent *event)
|
||||
{
|
||||
if (extra.isAllocated() && extra->keyHandler) {
|
||||
if (extra.isAllocated() && extra->keyHandler)
|
||||
extra->keyHandler->shortcutOverride(event);
|
||||
}
|
||||
else
|
||||
event->ignore();
|
||||
}
|
||||
|
||||
bool QQuickItemPrivate::anyPointerHandlerWants(const QPointerEvent *event, const QEventPoint &point) const
|
||||
|
|
|
@ -742,8 +742,7 @@ void QQuickTextControl::processEvent(QEvent *e, const QTransform &transform)
|
|||
case QEvent::ShortcutOverride:
|
||||
if (d->interactionFlags & Qt::TextEditable) {
|
||||
QKeyEvent* ke = static_cast<QKeyEvent *>(e);
|
||||
if (isCommonTextEditShortcut(ke))
|
||||
ke->accept();
|
||||
ke->setAccepted(isCommonTextEditShortcut(ke));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -1692,8 +1692,10 @@ bool QQuickTextInput::event(QEvent* ev)
|
|||
#if QT_CONFIG(shortcut)
|
||||
Q_D(QQuickTextInput);
|
||||
if (ev->type() == QEvent::ShortcutOverride) {
|
||||
if (d->m_readOnly)
|
||||
if (d->m_readOnly) {
|
||||
ev->ignore();
|
||||
return false;
|
||||
}
|
||||
QKeyEvent* ke = static_cast<QKeyEvent*>(ev);
|
||||
if (ke == QKeySequence::Copy
|
||||
|| ke == QKeySequence::Paste
|
||||
|
@ -1736,6 +1738,7 @@ bool QQuickTextInput::event(QEvent* ev)
|
|||
}
|
||||
}
|
||||
}
|
||||
ev->ignore();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
|
@ -765,8 +765,7 @@ bool QQuickDeliveryAgent::event(QEvent *ev)
|
|||
break;
|
||||
#endif
|
||||
case QEvent::ShortcutOverride:
|
||||
if (d->activeFocusItem)
|
||||
QCoreApplication::sendEvent(d->activeFocusItem, ev);
|
||||
d->deliverKeyEvent(static_cast<QKeyEvent *>(ev));
|
||||
break;
|
||||
case QEvent::InputMethod:
|
||||
case QEvent::InputMethodQuery:
|
||||
|
@ -826,10 +825,16 @@ void QQuickDeliveryAgentPrivate::deliverKeyEvent(QKeyEvent *e)
|
|||
{
|
||||
if (activeFocusItem) {
|
||||
const bool keyPress = (e->type() == QEvent::KeyPress);
|
||||
if (keyPress)
|
||||
switch (e->type()) {
|
||||
case QEvent::KeyPress:
|
||||
Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyPress, e->key(), e->modifiers());
|
||||
else
|
||||
break;
|
||||
case QEvent::KeyRelease:
|
||||
Q_QUICK_INPUT_PROFILE(QQuickProfiler::Key, QQuickProfiler::InputKeyRelease, e->key(), e->modifiers());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
QQuickItem *item = activeFocusItem;
|
||||
|
||||
|
@ -839,12 +844,10 @@ void QQuickDeliveryAgentPrivate::deliverKeyEvent(QKeyEvent *e)
|
|||
e->key(), e->modifiers(), e->text(),
|
||||
e->isAutoRepeat(), e->count());
|
||||
|
||||
e->accept();
|
||||
QCoreApplication::sendEvent(item, e);
|
||||
while (!e->isAccepted() && (item = item->parentItem())) {
|
||||
do {
|
||||
e->accept();
|
||||
QCoreApplication::sendEvent(item, e);
|
||||
}
|
||||
} while (!e->isAccepted() && (item = item->parentItem()));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ Item {
|
|||
id: txt
|
||||
x: 100
|
||||
text: "enter text"
|
||||
Keys.onShortcutOverride: {
|
||||
Keys.onShortcutOverride: (event) => {
|
||||
who = "TextEdit"
|
||||
event.accepted = (event.key === Qt.Key_Escape)
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ Item {
|
|||
height: width
|
||||
focus: true
|
||||
color: focus ? "red" : "gray"
|
||||
Keys.onShortcutOverride: {
|
||||
Keys.onShortcutOverride: (event) => {
|
||||
who = "Rectangle"
|
||||
event.accepted = (event.key === Qt.Key_Escape)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (C) 2022 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
|
||||
Window {
|
||||
id: root
|
||||
visible: true
|
||||
width: 200
|
||||
height: 200
|
||||
|
||||
property bool overridden: false
|
||||
property bool receivedA: false
|
||||
property bool receivedB: false
|
||||
Item {
|
||||
Keys.onShortcutOverride: (e) => e.accepted = root.overridden = (e.key === Qt.Key_A)
|
||||
|
||||
Item {
|
||||
focus: true
|
||||
Shortcut {
|
||||
sequence: "A"
|
||||
onActivated: root.receivedA = true
|
||||
}
|
||||
Shortcut {
|
||||
sequence: "B"
|
||||
onActivated: root.receivedB = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -536,6 +536,8 @@ private slots:
|
|||
|
||||
#if QT_CONFIG(shortcut)
|
||||
void testShortCut();
|
||||
void shortcutOverride_data();
|
||||
void shortcutOverride();
|
||||
#endif
|
||||
|
||||
void rendererInterface();
|
||||
|
@ -3695,6 +3697,39 @@ void tst_qquickwindow::testShortCut()
|
|||
QVERIFY(eventFilter.events.contains(int(QEvent::ShortcutOverride)));
|
||||
QVERIFY(window->property("received").value<bool>());
|
||||
}
|
||||
|
||||
void tst_qquickwindow::shortcutOverride_data()
|
||||
{
|
||||
QTest::addColumn<Qt::Key>("key");
|
||||
QTest::addColumn<bool>("overridden");
|
||||
QTest::addColumn<bool>("receivedA");
|
||||
QTest::addColumn<bool>("receivedB");
|
||||
|
||||
QTest::addRow("Space") << Qt::Key_Space << false << false << false;
|
||||
QTest::addRow("A") << Qt::Key_A << true << false << false;
|
||||
QTest::addRow("B") << Qt::Key_B << false << false << true;
|
||||
}
|
||||
|
||||
void tst_qquickwindow::shortcutOverride()
|
||||
{
|
||||
QFETCH(Qt::Key, key);
|
||||
QFETCH(bool, overridden);
|
||||
QFETCH(bool, receivedA);
|
||||
QFETCH(bool, receivedB);
|
||||
|
||||
QQmlEngine engine;
|
||||
QQmlComponent component(&engine);
|
||||
component.loadUrl(testFileUrl("shortcutOverride.qml"));
|
||||
|
||||
QScopedPointer<QWindow> window(qobject_cast<QQuickWindow *>(component.create()));
|
||||
QVERIFY(window);
|
||||
QVERIFY(QTest::qWaitForWindowActive(window.get()));
|
||||
|
||||
QTest::keyPress(window.get(), key);
|
||||
QCOMPARE(window->property("overridden").value<bool>(), overridden);
|
||||
QCOMPARE(window->property("receivedA").value<bool>(), receivedA);
|
||||
QCOMPARE(window->property("receivedB").value<bool>(), receivedB);
|
||||
}
|
||||
#endif
|
||||
|
||||
void tst_qquickwindow::rendererInterface()
|
||||
|
|
Loading…
Reference in New Issue