diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/gui/kernel/qevent.cpp | 10 | ||||
-rw-r--r-- | src/gui/kernel/qguiapplication.cpp | 13 | ||||
-rw-r--r-- | src/gui/kernel/qshortcutmap.cpp | 59 | ||||
-rw-r--r-- | src/gui/kernel/qshortcutmap_p.h | 5 | ||||
-rw-r--r-- | src/gui/kernel/qwindowsysteminterface.cpp | 180 | ||||
-rw-r--r-- | src/gui/kernel/qwindowsysteminterface.h | 18 | ||||
-rw-r--r-- | src/plugins/platforms/cocoa/qnsview.mm | 6 | ||||
-rw-r--r-- | src/testlib/qtestkeyboard.h | 6 |
8 files changed, 110 insertions, 187 deletions
diff --git a/src/gui/kernel/qevent.cpp b/src/gui/kernel/qevent.cpp index 780a102644..0bb21752bc 100644 --- a/src/gui/kernel/qevent.cpp +++ b/src/gui/kernel/qevent.cpp @@ -1029,8 +1029,10 @@ QWheelEvent::QWheelEvent(const QPointF &pos, const QPointF& globalPos, when keys are pressed or released. A key event contains a special accept flag that indicates whether - the receiver will handle the key event. This flag is set by default, - so there is no need to call accept() when acting on a key event. + the receiver will handle the key event. This flag is set by default + for QEvent::KeyPress and QEvent::KeyRelease, so there is no need to + call accept() when acting on a key event. For QEvent::ShortcutOverride + the receiver needs to explicitly accept the event to trigger the override. Calling ignore() on a key event will propagate it to the parent widget. The event is propagated up the parent widget chain until a widget accepts it or an event filter consumes it. @@ -1065,6 +1067,8 @@ QKeyEvent::QKeyEvent(Type type, int key, Qt::KeyboardModifiers modifiers, const nScanCode(0), nVirtualKey(0), nModifiers(0), c(count), autor(autorep) { + if (type == QEvent::ShortcutOverride) + ignore(); } /*! @@ -1092,6 +1096,8 @@ QKeyEvent::QKeyEvent(Type type, int key, Qt::KeyboardModifiers modifiers, nScanCode(nativeScanCode), nVirtualKey(nativeVirtualKey), nModifiers(nativeModifiers), c(count), autor(autorep) { + if (type == QEvent::ShortcutOverride) + ignore(); } diff --git a/src/gui/kernel/qguiapplication.cpp b/src/gui/kernel/qguiapplication.cpp index b309f67866..3f50ab8688 100644 --- a/src/gui/kernel/qguiapplication.cpp +++ b/src/gui/kernel/qguiapplication.cpp @@ -1915,19 +1915,6 @@ void QGuiApplicationPrivate::processKeyEvent(QWindowSystemInterfacePrivate::KeyE window = QGuiApplication::focusWindow(); } -#if !defined(Q_OS_OSX) - // On OS X the shortcut override is checked earlier, see: QWindowSystemInterface::handleKeyEvent() - const bool checkShortcut = e->keyType == QEvent::KeyPress && window != 0; - if (checkShortcut) { - QKeyEvent override(QEvent::ShortcutOverride, e->key, e->modifiers, - e->nativeScanCode, e->nativeVirtualKey, e->nativeModifiers, - e->unicode, e->repeat, e->repeatCount); - override.setTimestamp(e->timestamp); - if (QWindowSystemInterface::tryHandleShortcutOverrideEvent(window, &override)) - return; - } -#endif // Q_OS_OSX - QKeyEvent ev(e->keyType, e->key, e->modifiers, e->nativeScanCode, e->nativeVirtualKey, e->nativeModifiers, e->unicode, e->repeat, e->repeatCount); diff --git a/src/gui/kernel/qshortcutmap.cpp b/src/gui/kernel/qshortcutmap.cpp index 3e267f2e0b..3b2e6ffd29 100644 --- a/src/gui/kernel/qshortcutmap.cpp +++ b/src/gui/kernel/qshortcutmap.cpp @@ -309,59 +309,42 @@ QKeySequence::SequenceMatch QShortcutMap::state() } /*! \internal - Uses ShortcutOverride event to see if any widgets want to override - the event. If not, uses nextState(QKeyEvent) to check for a grabbed - Shortcut, and dispatchEvent() is found and identical. + Uses nextState(QKeyEvent) to check for a grabbed shortcut. - \note that this function should only be called from QWindowSystemInterface, - otherwise it will result in duplicate events. + If so, it is dispatched using dispatchEvent(). + + Returns true if a shortcut handled the event. \sa nextState, dispatchEvent */ -bool QShortcutMap::tryShortcutEvent(QObject *o, QKeyEvent *e) +bool QShortcutMap::tryShortcut(QKeyEvent *e) { Q_D(QShortcutMap); if (e->key() == Qt::Key_unknown) return false; - bool wasAccepted = e->isAccepted(); - bool wasSpontaneous = e->spont; - if (d->currentState == QKeySequence::NoMatch) { - ushort orgType = e->t; - e->t = QEvent::ShortcutOverride; - e->ignore(); - QCoreApplication::sendEvent(o, e); - e->t = orgType; - e->spont = wasSpontaneous; - if (e->isAccepted()) { - if (!wasAccepted) - e->ignore(); - return false; - } - } - - QKeySequence::SequenceMatch result = nextState(e); - bool stateWasAccepted = e->isAccepted(); - if (wasAccepted) - e->accept(); - else - e->ignore(); - - int identicalMatches = d->identicals.count(); + QKeySequence::SequenceMatch previousState = state(); - switch(result) { + switch (nextState(e)) { case QKeySequence::NoMatch: - return stateWasAccepted; + // In the case of going from a partial match to no match we handled the + // event, since we already stated that we did for the partial match. But + // in the normal case of directly going to no match we say we didn't. + return previousState == QKeySequence::PartialMatch; + case QKeySequence::PartialMatch: + // For a partial match we don't know yet if we will handle the shortcut + // but we need to say we did, so that we get the follow-up key-presses. + return true; case QKeySequence::ExactMatch: resetState(); dispatchEvent(e); + // If there are no identicals we've only found disabled shortcuts, and + // shouldn't say that we handled the event. + return d->identicals.count() > 0; default: - break; + Q_UNREACHABLE(); } - // If nextState is QKeySequence::ExactMatch && identicals.count == 0 - // we've only found disabled shortcuts - return identicalMatches > 0 || result == QKeySequence::PartialMatch; } /*! \internal @@ -396,10 +379,6 @@ QKeySequence::SequenceMatch QShortcutMap::nextState(QKeyEvent *e) } } - // Should we eat this key press? - if (d->currentState == QKeySequence::PartialMatch - || (d->currentState == QKeySequence::ExactMatch && d->identicals.count())) - e->accept(); // Does the new state require us to clean up? if (result == QKeySequence::NoMatch) clearSequence(d->currentSequences); diff --git a/src/gui/kernel/qshortcutmap_p.h b/src/gui/kernel/qshortcutmap_p.h index 2376d27c78..16542b078a 100644 --- a/src/gui/kernel/qshortcutmap_p.h +++ b/src/gui/kernel/qshortcutmap_p.h @@ -75,7 +75,9 @@ public: int setShortcutEnabled(bool enable, int id, QObject *owner, const QKeySequence &key = QKeySequence()); int setShortcutAutoRepeat(bool on, int id, QObject *owner, const QKeySequence &key = QKeySequence()); - bool tryShortcutEvent(QObject *o, QKeyEvent *e); + QKeySequence::SequenceMatch state(); + + bool tryShortcut(QKeyEvent *e); bool hasShortcutForKeySequence(const QKeySequence &seq) const; #ifdef Dump_QShortcutMap @@ -85,7 +87,6 @@ public: private: void resetState(); QKeySequence::SequenceMatch nextState(QKeyEvent *e); - QKeySequence::SequenceMatch state(); void dispatchEvent(QKeyEvent *e); QKeySequence::SequenceMatch find(QKeyEvent *e, int ignoredModifiers = 0); diff --git a/src/gui/kernel/qwindowsysteminterface.cpp b/src/gui/kernel/qwindowsysteminterface.cpp index 09e6e2deb8..e7abff9ccc 100644 --- a/src/gui/kernel/qwindowsysteminterface.cpp +++ b/src/gui/kernel/qwindowsysteminterface.cpp @@ -41,6 +41,7 @@ #include <qpa/qplatformintegration.h> #include <qdebug.h> #include "qhighdpiscaling_p.h" +#include <QtCore/qscopedvaluerollback.h> QT_BEGIN_NAMESPACE @@ -191,114 +192,50 @@ void QWindowSystemInterface::handleFrameStrutMouseEvent(QWindow *w, ulong timest QWindowSystemInterfacePrivate::handleWindowSystemEvent(e); } -bool QWindowSystemInterface::tryHandleShortcutEvent(QWindow *w, int k, Qt::KeyboardModifiers mods, - const QString & text, bool autorep, ushort count) -{ - unsigned long timestamp = QWindowSystemInterfacePrivate::eventTime.elapsed(); - return tryHandleShortcutEvent(w, timestamp, k, mods, text, autorep, count); -} - -bool QWindowSystemInterface::tryHandleShortcutEvent(QWindow *w, ulong timestamp, int k, Qt::KeyboardModifiers mods, - const QString & text, bool autorep, ushort count) -{ -#ifndef QT_NO_SHORTCUT - QGuiApplicationPrivate::modifier_buttons = mods; - - if (!w) - w = QGuiApplication::focusWindow(); - if (!w) - return false; - - QObject *focus = w->focusObject(); - if (!focus) - focus = w; - - QKeyEvent qevent(QEvent::ShortcutOverride, k, mods, text, autorep, count); - qevent.setTimestamp(timestamp); - return QGuiApplicationPrivate::instance()->shortcutMap.tryShortcutEvent(focus, &qevent); -#else - Q_UNUSED(w) - Q_UNUSED(timestamp) - Q_UNUSED(k) - Q_UNUSED(mods) - Q_UNUSED(text) - Q_UNUSED(autorep) - Q_UNUSED(count) - return false; -#endif -} - -bool QWindowSystemInterface::tryHandleShortcutOverrideEvent(QWindow *w, QKeyEvent *ev) -{ -#ifndef QT_NO_SHORTCUT - Q_ASSERT(ev->type() == QKeyEvent::ShortcutOverride); - QGuiApplicationPrivate::modifier_buttons = ev->modifiers(); - - QObject *focus = w->focusObject(); - if (!focus) - focus = w; - return QGuiApplicationPrivate::instance()->shortcutMap.tryShortcutEvent(focus, ev); -#else - Q_UNUSED(w) - Q_UNUSED(ev) - return false; -#endif -} - -// used by QTestLib to directly send shortcuts to objects -bool QWindowSystemInterface::tryHandleShortcutEventToObject(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, - const QString &text, bool autorep, ushort count) +bool QWindowSystemInterface::handleShortcutEvent(QWindow *window, ulong timestamp, int keyCode, Qt::KeyboardModifiers modifiers, quint32 nativeScanCode, + quint32 nativeVirtualKey, quint32 nativeModifiers, const QString &text, bool autorepeat, ushort count) { #ifndef QT_NO_SHORTCUT - QGuiApplicationPrivate::modifier_buttons = mods; - - QKeyEvent qevent(QEvent::ShortcutOverride, k, mods, text, autorep, count); - qevent.setTimestamp(timestamp); - return QGuiApplicationPrivate::instance()->shortcutMap.tryShortcutEvent(o, &qevent); -#else - Q_UNUSED(w) - Q_UNUSED(timestamp) - Q_UNUSED(k) - Q_UNUSED(mods) - Q_UNUSED(text) - Q_UNUSED(autorep) - Q_UNUSED(count) - return false; -#endif -} - -bool QWindowSystemInterface::tryHandleExtendedShortcutEvent(QWindow *w, int k, Qt::KeyboardModifiers mods, - quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, - const QString &text, bool autorep, ushort count) -{ - unsigned long timestamp = QWindowSystemInterfacePrivate::eventTime.elapsed(); - return tryHandleExtendedShortcutEvent(w, timestamp, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count); -} - -bool QWindowSystemInterface::tryHandleExtendedShortcutEvent(QWindow *w, ulong timestamp, int k, Qt::KeyboardModifiers mods, - quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, - const QString &text, bool autorep, ushort count) -{ -#ifndef QT_NO_SHORTCUT - QGuiApplicationPrivate::modifier_buttons = mods; + if (!window) + window = QGuiApplication::focusWindow(); + + QShortcutMap &shortcutMap = QGuiApplicationPrivate::instance()->shortcutMap; + if (shortcutMap.state() == QKeySequence::NoMatch) { + // Check if the shortcut is overridden by some object in the event delivery path (typically the focus object). + // If so, we should not look up the shortcut in the shortcut map, but instead deliver the event as a regular + // key event, so that the target that accepted the shortcut override event can handle it. Note that we only + // do this if the shortcut map hasn't found a partial shortcut match yet. If it has, the shortcut can not be + // overridden. + QWindowSystemInterfacePrivate::KeyEvent *shortcutOverrideEvent = new QWindowSystemInterfacePrivate::KeyEvent(window, timestamp, + QEvent::ShortcutOverride, keyCode, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorepeat, count); + + { + // FIXME: Template handleWindowSystemEvent to support both sync and async delivery + QScopedValueRollback<bool> syncRollback(QWindowSystemInterfacePrivate::synchronousWindowSystemEvents); + QWindowSystemInterfacePrivate::synchronousWindowSystemEvents = true; + + if (QWindowSystemInterfacePrivate::handleWindowSystemEvent(shortcutOverrideEvent)) + return false; + } + } - QObject *focus = w->focusObject(); - if (!focus) - focus = w; + // The shortcut event is dispatched as a QShortcutEvent, not a QKeyEvent, but we use + // the QKeyEvent as a container for the various properties that the shortcut map needs + // to inspect to determine if a shortcut matched the keys that were pressed. + QKeyEvent keyEvent(QEvent::ShortcutOverride, keyCode, modifiers, nativeScanCode, + nativeVirtualKey, nativeModifiers, text, autorepeat, count); - QKeyEvent qevent(QEvent::ShortcutOverride, k, mods, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count); - qevent.setTimestamp(timestamp); - return QGuiApplicationPrivate::instance()->shortcutMap.tryShortcutEvent(focus, &qevent); + return shortcutMap.tryShortcut(&keyEvent); #else - Q_UNUSED(w) + Q_UNUSED(window) Q_UNUSED(timestamp) - Q_UNUSED(k) - Q_UNUSED(mods) + Q_UNUSED(key) + Q_UNUSED(modifiers) Q_UNUSED(nativeScanCode) Q_UNUSED(nativeVirtualKey) Q_UNUSED(nativeModifiers) Q_UNUSED(text) - Q_UNUSED(autorep) + Q_UNUSED(autorepeat) Q_UNUSED(count) return false; #endif @@ -312,13 +249,8 @@ bool QWindowSystemInterface::handleKeyEvent(QWindow *w, QEvent::Type t, int k, Q bool QWindowSystemInterface::handleKeyEvent(QWindow *tlw, ulong timestamp, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text, bool autorep, ushort count) { - // This is special handling needed for OS X which eventually will call sendEvent(), on other platforms - // this might not be safe, e.g., on Android. See: QGuiApplicationPrivate::processKeyEvent() for - // shortcut overriding on other platforms. -#if defined(Q_OS_OSX) - if (t == QEvent::KeyPress && QWindowSystemInterface::tryHandleShortcutEvent(tlw, timestamp, k, mods, text)) + if (t == QEvent::KeyPress && QWindowSystemInterface::handleShortcutEvent(tlw, timestamp, k, mods, 0, 0, 0, text, autorep, count)) return true; -#endif // Q_OS_OSX QWindowSystemInterfacePrivate::KeyEvent * e = new QWindowSystemInterfacePrivate::KeyEvent(tlw, timestamp, t, k, mods, text, autorep, count); @@ -343,7 +275,9 @@ bool QWindowSystemInterface::handleExtendedKeyEvent(QWindow *tlw, ulong timestam const QString& text, bool autorep, ushort count, bool tryShortcutOverride) { - Q_UNUSED(tryShortcutOverride) + if (tryShortcutOverride && type == QEvent::KeyPress && QWindowSystemInterface::handleShortcutEvent(tlw, timestamp, key, modifiers, 0, 0, 0, text, autorep, count)) + return true; + QWindowSystemInterfacePrivate::KeyEvent * e = new QWindowSystemInterfacePrivate::KeyEvent(tlw, timestamp, type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, autorep, count); @@ -926,9 +860,41 @@ Q_GUI_EXPORT void qt_handleKeyEvent(QWindow *w, QEvent::Type t, int k, Qt::Keybo QWindowSystemInterface::setSynchronousWindowSystemEvents(wasSynchronous); } -Q_GUI_EXPORT bool qt_sendShortcutOverrideEvent(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, const QString &text = QString(), bool autorep = false, ushort count = 1) +Q_GUI_EXPORT bool qt_handleShortcutEvent(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, const QString &text = QString(), bool autorep = false, ushort count = 1) { - return QWindowSystemInterface::tryHandleShortcutEventToObject(o, timestamp, k, mods, text, autorep, count); +#ifndef QT_NO_SHORTCUT + + // FIXME: This method should not allow targeting a specific object, but should + // instead forward the event to a window, which then takes care of normal event + // propagation. We need to fix a lot of tests before we can refactor this (the + // window needs to be exposed and active and have a focus object), so we leave + // it as is for now. See QTBUG-48577. + + QGuiApplicationPrivate::modifier_buttons = mods; + + QKeyEvent qevent(QEvent::ShortcutOverride, k, mods, text, autorep, count); + qevent.setTimestamp(timestamp); + + QShortcutMap &shortcutMap = QGuiApplicationPrivate::instance()->shortcutMap; + if (shortcutMap.state() == QKeySequence::NoMatch) { + // Try sending as QKeyEvent::ShortcutOverride first + QCoreApplication::sendEvent(o, &qevent); + if (qevent.isAccepted()) + return false; + } + + // Then as QShortcutEvent + return shortcutMap.tryShortcut(&qevent); +#else + Q_UNUSED(o) + Q_UNUSED(timestamp) + Q_UNUSED(k) + Q_UNUSED(mods) + Q_UNUSED(text) + Q_UNUSED(autorep) + Q_UNUSED(count) + return false; +#endif } static QWindowSystemInterface::TouchPoint touchPoint(const QTouchEvent::TouchPoint& pt) diff --git a/src/gui/kernel/qwindowsysteminterface.h b/src/gui/kernel/qwindowsysteminterface.h index 97bd087b53..387c1e00b9 100644 --- a/src/gui/kernel/qwindowsysteminterface.h +++ b/src/gui/kernel/qwindowsysteminterface.h @@ -78,22 +78,8 @@ public: Qt::KeyboardModifiers mods = Qt::NoModifier, Qt::MouseEventSource source = Qt::MouseEventNotSynthesized); - static bool tryHandleShortcutOverrideEvent(QWindow *w, QKeyEvent *ev); - - static bool tryHandleShortcutEvent(QWindow *w, int k, Qt::KeyboardModifiers mods, - const QString & text = QString(), bool autorep = false, ushort count = 1); - static bool tryHandleShortcutEvent(QWindow *w, ulong timestamp, int k, Qt::KeyboardModifiers mods, - const QString & text = QString(), bool autorep = false, ushort count = 1); - - static bool tryHandleShortcutEventToObject(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, - const QString & text = QString(), bool autorep = false, ushort count = 1); - - static bool tryHandleExtendedShortcutEvent(QWindow *w, int k, Qt::KeyboardModifiers mods, - quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, - const QString & text = QString(), bool autorep = false, ushort count = 1); - static bool tryHandleExtendedShortcutEvent(QWindow *w, ulong timestamp, int k, Qt::KeyboardModifiers mods, - quint32 nativeScanCode, quint32 nativeVirtualKey, quint32 nativeModifiers, - const QString & text = QString(), bool autorep = false, ushort count = 1); + static bool handleShortcutEvent(QWindow *w, ulong timestamp, int k, Qt::KeyboardModifiers mods, quint32 nativeScanCode, + quint32 nativeVirtualKey, quint32 nativeModifiers, const QString & text = QString(), bool autorep = false, ushort count = 1); static bool handleKeyEvent(QWindow *w, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1); static bool handleKeyEvent(QWindow *w, ulong timestamp, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1); diff --git a/src/plugins/platforms/cocoa/qnsview.mm b/src/plugins/platforms/cocoa/qnsview.mm index a4f4c0855b..24ee26ce46 100644 --- a/src/plugins/platforms/cocoa/qnsview.mm +++ b/src/plugins/platforms/cocoa/qnsview.mm @@ -1446,10 +1446,8 @@ static QTabletEvent::TabletDevice wacomTabletDevice(NSEvent *theEvent) if (eventType == QEvent::KeyPress) { if (m_composingText.isEmpty()) { - QKeyEvent override(QEvent::ShortcutOverride, keyCode, modifiers, - nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], 1); - override.setTimestamp(timestamp); - m_sendKeyEvent = !QWindowSystemInterface::tryHandleShortcutOverrideEvent(focusWindow, &override); + m_sendKeyEvent = !QWindowSystemInterface::handleShortcutEvent(focusWindow, timestamp, keyCode, + modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers, text, [nsevent isARepeat], 1); } QObject *fo = QGuiApplication::focusObject(); diff --git a/src/testlib/qtestkeyboard.h b/src/testlib/qtestkeyboard.h index d73658d8fc..d2b3ef240b 100644 --- a/src/testlib/qtestkeyboard.h +++ b/src/testlib/qtestkeyboard.h @@ -57,7 +57,7 @@ QT_BEGIN_NAMESPACE Q_GUI_EXPORT void qt_handleKeyEvent(QWindow *w, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1); -Q_GUI_EXPORT bool qt_sendShortcutOverrideEvent(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, const QString &text = QString(), bool autorep = false, ushort count = 1); +Q_GUI_EXPORT bool qt_handleShortcutEvent(QObject *o, ulong timestamp, int k, Qt::KeyboardModifiers mods, const QString &text = QString(), bool autorep = false, ushort count = 1); namespace QTest { @@ -97,7 +97,7 @@ namespace QTest if (action == Shortcut) { int timestamp = 0; - qt_sendShortcutOverrideEvent(window, timestamp, code, modifier, text, repeat); + qt_handleShortcutEvent(window, timestamp, code, modifier, text, repeat); return; } @@ -178,7 +178,7 @@ namespace QTest QKeyEvent a(press ? QEvent::KeyPress : QEvent::KeyRelease, code, modifier, text, repeat); QSpontaneKeyEvent::setSpontaneous(&a); - if (press && qt_sendShortcutOverrideEvent(widget, a.timestamp(), code, modifier, text, repeat)) + if (press && qt_handleShortcutEvent(widget, a.timestamp(), code, modifier, text, repeat)) return; if (!qApp->notify(widget, &a)) QTest::qWarn("Keyboard event not accepted by receiving widget"); |