Skip to content

Commit

Permalink
iOS: Handle keyboard events when using an external keyboard
Browse files Browse the repository at this point in the history
This enables the two possible approaches for handling external keyboard
events. While support still exists for before 13.4 then both approaches
are needed. This ensures that all external keyboard events are handled
as key events and passed on accordingly. Additionally, this accounts
for possible shortcuts too, therefore a new function is added to
QShortcutMap to aid that.

As a result, code has now moved from QCocoaKeyMapper to be part of the
gui/platforms/darwin part to make it easier to reuse this code
elsewhere.

Fixes: QTBUG-85727
Change-Id: I349af43468b03fd8dcb16adba02669974affe154
Reviewed-by: Qt CI Bot <[email protected]>
Reviewed-by: Tor Arne Vestbø <[email protected]>
  • Loading branch information
AndyShawQt committed Jan 20, 2021
1 parent a5bb7b3 commit 15576c9
Show file tree
Hide file tree
Showing 19 changed files with 274 additions and 48 deletions.
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,7 @@ qt_internal_extend_target(Gui CONDITION MACOS
platform/macos/qcocoanativeinterface.mm
LIBRARIES
${FWAppKit}
${FWCarbon}
PUBLIC_LIBRARIES
${FWAppKit}
)
Expand All @@ -369,6 +370,7 @@ qt_internal_extend_target(Gui CONDITION APPLE
painting/qcoregraphics.mm painting/qcoregraphics_p.h
painting/qrasterbackingstore.cpp painting/qrasterbackingstore_p.h
platform/darwin/qmacmime.mm platform/darwin/qmacmime_p.h
platform/darwin/qapplekeymapper.mm platform/darwin/qapplekeymapper_p.h
text/coretext/qcoretextfontdatabase.mm text/coretext/qcoretextfontdatabase_p.h
text/coretext/qfontengine_coretext.mm text/coretext/qfontengine_coretext_p.h
LIBRARIES
Expand Down
43 changes: 42 additions & 1 deletion src/gui/kernel/qshortcutmap.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@
#include "qdebug.h"
#include "qevent.h"
#include "qlist.h"
#include "qcoreapplication.h"
#include "qguiapplication.h"
#include "qwindow.h"
#include <private/qkeymapper_p.h>
#include <QtCore/qloggingcategory.h>

Expand Down Expand Up @@ -665,6 +666,46 @@ void QShortcutMap::dispatchEvent(QKeyEvent *e)
QCoreApplication::sendEvent(const_cast<QObject *>(next->owner), &se);
}

QList<QKeySequence> QShortcutMap::keySequences(bool getAll) const
{
Q_D(const QShortcutMap);
QList<QKeySequence> keys;
for (auto sequence : d->sequences) {
bool addSequence = false;
if (sequence.enabled) {
if (getAll || sequence.context == Qt::ApplicationShortcut ||
sequence.owner == QGuiApplication::focusObject()) {
addSequence = true;
} else {
QObject *possibleWindow = sequence.owner;
while (possibleWindow) {
if (qobject_cast<QWindow *>(possibleWindow))
break;
possibleWindow = possibleWindow->parent();
}
if (possibleWindow == QGuiApplication::focusWindow()) {
if (sequence.context == Qt::WindowShortcut) {
addSequence = true;
} else if (sequence.context == Qt::WidgetWithChildrenShortcut) {
QObject *possibleWidget = QGuiApplication::focusObject();
while (possibleWidget->parent()) {
possibleWidget = possibleWidget->parent();
if (possibleWidget == sequence.owner) {
addSequence = true;
break;
}
}
}
}
}
if (addSequence)
keys << sequence.keyseq;
}
}
return keys;

}

/* \internal
QShortcutMap dump function, only available when DEBUG_QSHORTCUTMAP is
defined.
Expand Down
1 change: 1 addition & 0 deletions src/gui/kernel/qshortcutmap_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class Q_GUI_EXPORT QShortcutMap

bool tryShortcut(QKeyEvent *e);
bool hasShortcutForKeySequence(const QKeySequence &seq) const;
QList<QKeySequence> keySequences(bool getAll = false) const;

#ifdef Dump_QShortcutMap
void dumpMap() const;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2020 The Qt Company Ltd.
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
Expand Down Expand Up @@ -37,9 +37,17 @@
**
****************************************************************************/

#include <qglobal.h>

#ifdef Q_OS_MACOS
#include <AppKit/AppKit.h>
#endif

#if defined(QT_PLATFORM_UIKIT)
#include <UIKit/UIKit.h>
#endif

#include "qcocoakeymapper.h"
#include "qapplekeymapper_p.h"

#include <QtCore/qloggingcategory.h>
#include <QtGui/QGuiApplication>
Expand All @@ -65,6 +73,33 @@
return swappedModifiers;
}

Qt::Key QAppleKeyMapper::fromNSString(Qt::KeyboardModifiers qtModifiers, NSString *characters,
NSString *charactersIgnoringModifiers)
{
if ([characters isEqualToString:@"\t"]) {
if (qtModifiers & Qt::ShiftModifier)
return Qt::Key_Backtab;
return Qt::Key_Tab;
} else if ([characters isEqualToString:@"\r"]) {
if (qtModifiers & Qt::KeypadModifier)
return Qt::Key_Enter;
return Qt::Key_Return;
}
if ([characters length] != 0 || [charactersIgnoringModifiers length] != 0) {
QChar ch;
if (((qtModifiers & Qt::MetaModifier) || (qtModifiers & Qt::AltModifier)) &&
([charactersIgnoringModifiers length] != 0)) {
ch = QChar([charactersIgnoringModifiers characterAtIndex:0]);
} else if ([characters length] != 0) {
ch = QChar([characters characterAtIndex:0]);
}
if (!ch.isNull())
return Qt::Key(ch.toUpper().unicode());
}
return Qt::Key_unknown;
}

#ifdef Q_OS_MACOS
static constexpr std::tuple<NSEventModifierFlags, Qt::KeyboardModifier> cocoaModifierMap[] = {
{ NSEventModifierFlagShift, Qt::ShiftModifier },
{ NSEventModifierFlagControl, Qt::ControlModifier },
Expand All @@ -73,7 +108,7 @@
{ NSEventModifierFlagNumericPad, Qt::KeypadModifier }
};

Qt::KeyboardModifiers QCocoaKeyMapper::fromCocoaModifiers(NSEventModifierFlags cocoaModifiers)
Qt::KeyboardModifiers QAppleKeyMapper::fromCocoaModifiers(NSEventModifierFlags cocoaModifiers)
{
Qt::KeyboardModifiers qtModifiers = Qt::NoModifier;
for (const auto &[cocoaModifier, qtModifier] : cocoaModifierMap) {
Expand All @@ -84,7 +119,7 @@
return swapModifiersIfNeeded(qtModifiers);
}

NSEventModifierFlags QCocoaKeyMapper::toCocoaModifiers(Qt::KeyboardModifiers qtModifiers)
NSEventModifierFlags QAppleKeyMapper::toCocoaModifiers(Qt::KeyboardModifiers qtModifiers)
{
qtModifiers = swapModifiersIfNeeded(qtModifiers);

Expand Down Expand Up @@ -353,7 +388,7 @@ static int toKeyCode(const QChar &key, int virtualKey, int modifiers)
{ NSHelpFunctionKey, Qt::Key_Help },
};

QChar QCocoaKeyMapper::toCocoaKey(Qt::Key key)
QChar QAppleKeyMapper::toCocoaKey(Qt::Key key)
{
// Prioritize overloaded keys
if (key == Qt::Key_Return)
Expand All @@ -371,7 +406,7 @@ static int toKeyCode(const QChar &key, int virtualKey, int modifiers)
return reverseCocoaKeys.value(key);
}

Qt::Key QCocoaKeyMapper::fromCocoaKey(QChar keyCode)
Qt::Key QAppleKeyMapper::fromCocoaKey(QChar keyCode)
{
if (auto key = cocoaKeys.value(keyCode.unicode()))
return key;
Expand All @@ -381,12 +416,12 @@ static int toKeyCode(const QChar &key, int virtualKey, int modifiers)

// ------------------------------------------------

Qt::KeyboardModifiers QCocoaKeyMapper::queryKeyboardModifiers()
Qt::KeyboardModifiers QAppleKeyMapper::queryKeyboardModifiers()
{
return fromCocoaModifiers(NSEvent.modifierFlags);
}

bool QCocoaKeyMapper::updateKeyboard()
bool QAppleKeyMapper::updateKeyboard()
{
QCFType<TISInputSourceRef> source = TISCopyInputMethodKeyboardLayoutOverride();
if (!source)
Expand Down Expand Up @@ -442,11 +477,11 @@ static int toKeyCode(const QChar &key, int virtualKey, int modifiers)
Returns a key map for the given \virtualKey based on all
possible modifier combinations.
*/
const QCocoaKeyMapper::KeyMap &QCocoaKeyMapper::keyMapForKey(VirtualKeyCode virtualKey, QChar unicodeKey) const
const QAppleKeyMapper::KeyMap &QAppleKeyMapper::keyMapForKey(VirtualKeyCode virtualKey, QChar unicodeKey) const
{
static_assert(sizeof(modifierCombinations) / sizeof(Qt::KeyboardModifiers) == kNumModifierCombinations);

const_cast<QCocoaKeyMapper *>(this)->updateKeyboard();
const_cast<QAppleKeyMapper *>(this)->updateKeyboard();

auto &keyMap = m_keyMap[virtualKey];
if (keyMap[Qt::NoModifier] != Qt::Key_unknown)
Expand Down Expand Up @@ -509,7 +544,7 @@ static int toKeyCode(const QChar &key, int virtualKey, int modifiers)
return keyMap;
}

QList<int> QCocoaKeyMapper::possibleKeys(const QKeyEvent *event) const
QList<int> QAppleKeyMapper::possibleKeys(const QKeyEvent *event) const
{
QList<int> ret;

Expand Down Expand Up @@ -544,4 +579,75 @@ static int toKeyCode(const QChar &key, int virtualKey, int modifiers)
return ret;
}



#else
// Keyboard keys (non-modifiers)
API_AVAILABLE(ios(13.4)) static QHash<NSString *, Qt::Key> uiKitKeys = {
#if QT_IOS_PLATFORM_SDK_EQUAL_OR_ABOVE(__IPHONE_13_4)
{ UIKeyInputF1, Qt::Key_F1 },
{ UIKeyInputF2, Qt::Key_F2 },
{ UIKeyInputF3, Qt::Key_F3 },
{ UIKeyInputF4, Qt::Key_F4 },
{ UIKeyInputF5, Qt::Key_F5 },
{ UIKeyInputF6, Qt::Key_F6 },
{ UIKeyInputF7, Qt::Key_F7 },
{ UIKeyInputF8, Qt::Key_F8 },
{ UIKeyInputF9, Qt::Key_F9 },
{ UIKeyInputF10, Qt::Key_F10 },
{ UIKeyInputF11, Qt::Key_F11 },
{ UIKeyInputF12, Qt::Key_F12 },
{ UIKeyInputHome, Qt::Key_Home },
{ UIKeyInputEnd, Qt::Key_End },
{ UIKeyInputPageUp, Qt::Key_PageUp },
{ UIKeyInputPageDown, Qt::Key_PageDown },
#endif
{ UIKeyInputEscape, Qt::Key_Escape },
{ UIKeyInputUpArrow, Qt::Key_Up },
{ UIKeyInputDownArrow, Qt::Key_Down },
{ UIKeyInputLeftArrow, Qt::Key_Left },
{ UIKeyInputRightArrow, Qt::Key_Right }
};

API_AVAILABLE(ios(13.4)) Qt::Key QAppleKeyMapper::fromUIKitKey(NSString *keyCode)
{
if (auto key = uiKitKeys.value(keyCode))
return key;

return Qt::Key_unknown;
}

static constexpr std::tuple<ulong, Qt::KeyboardModifier> uiKitModifierMap[] = {
{ UIKeyModifierShift, Qt::ShiftModifier },
{ UIKeyModifierControl, Qt::ControlModifier },
{ UIKeyModifierCommand, Qt::MetaModifier },
{ UIKeyModifierAlternate, Qt::AltModifier },
{ UIKeyModifierNumericPad, Qt::KeypadModifier }
};

ulong QAppleKeyMapper::toUIKitModifiers(Qt::KeyboardModifiers qtModifiers)
{
qtModifiers = swapModifiersIfNeeded(qtModifiers);

ulong nativeModifiers = 0;
for (const auto &[nativeModifier, qtModifier] : uiKitModifierMap) {
if (qtModifiers & qtModifier)
nativeModifiers |= nativeModifier;
}

return nativeModifiers;
}

Qt::KeyboardModifiers QAppleKeyMapper::fromUIKitModifiers(ulong nativeModifiers)
{
Qt::KeyboardModifiers qtModifiers = Qt::NoModifier;
for (const auto &[nativeModifier, qtModifier] : uiKitModifierMap) {
if (nativeModifiers & nativeModifier)
qtModifiers |= qtModifier;
}

return swapModifiersIfNeeded(qtModifiers);
}
#endif

QT_END_NAMESPACE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2021 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
Expand Down Expand Up @@ -37,10 +37,12 @@
**
****************************************************************************/

#ifndef QCOCOAKEYMAPPER_H
#define QCOCOAKEYMAPPER_H
#ifndef QAPPLEKEYMAPPER_H
#define QAPPLEKEYMAPPER_H

#ifdef Q_OS_MACOS
#include <Carbon/Carbon.h>
#endif

#include <QtCore/QList>
#include <QtGui/QKeyEvent>
Expand All @@ -49,19 +51,26 @@

QT_BEGIN_NAMESPACE

class QCocoaKeyMapper
class Q_GUI_EXPORT QAppleKeyMapper
{
public:
static Qt::KeyboardModifiers queryKeyboardModifiers();
QList<int> possibleKeys(const QKeyEvent *event) const;

static Qt::Key fromNSString(Qt::KeyboardModifiers qtMods, NSString *characters,
NSString *charactersIgnoringModifiers);
#ifdef Q_OS_MACOS
static Qt::KeyboardModifiers fromCocoaModifiers(NSEventModifierFlags cocoaModifiers);
static NSEventModifierFlags toCocoaModifiers(Qt::KeyboardModifiers);

static QChar toCocoaKey(Qt::Key key);
static Qt::Key fromCocoaKey(QChar keyCode);

#else
static Qt::Key fromUIKitKey(NSString *keyCode);
static Qt::KeyboardModifiers fromUIKitModifiers(ulong uikitModifiers);
static ulong toUIKitModifiers(Qt::KeyboardModifiers);
#endif
private:
#ifdef Q_OS_MACOS
static constexpr int kNumModifierCombinations = 16;
struct KeyMap : std::array<char32_t, kNumModifierCombinations>
{
Expand All @@ -85,6 +94,7 @@ class QCocoaKeyMapper
mutable UInt32 m_deadKeyState = 0; // Maintains dead key state beween calls to UCKeyTranslate

mutable QHash<VirtualKeyCode, KeyMap> m_keyMap;
#endif
};

QT_END_NAMESPACE
Expand Down
1 change: 0 additions & 1 deletion src/plugins/platforms/cocoa/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ qt_internal_add_plugin(QCocoaIntegrationPlugin
qcocoainputcontext.h qcocoainputcontext.mm
qcocoaintegration.h qcocoaintegration.mm
qcocoaintrospection.h qcocoaintrospection.mm
qcocoakeymapper.h qcocoakeymapper.mm
qcocoamenu.h qcocoamenu.mm
qcocoamenubar.h qcocoamenubar.mm
qcocoamenuitem.h qcocoamenuitem.mm
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/platforms/cocoa/qcocoaapplicationdelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -371,7 +371,7 @@ - (void)qt_itemFired:(QCocoaNSMenuItem *)item
return;

QScopedScopeLevelCounter scopeLevelCounter(QGuiApplicationPrivate::instance()->threadData.loadRelaxed());
QGuiApplicationPrivate::modifier_buttons = QCocoaKeyMapper::fromCocoaModifiers([NSEvent modifierFlags]);
QGuiApplicationPrivate::modifier_buttons = QAppleKeyMapper::fromCocoaModifiers([NSEvent modifierFlags]);

static QMetaMethod activatedSignal = QMetaMethod::fromSignal(&QCocoaMenuItem::activated);
activatedSignal.invoke(platformItem, Qt::QueuedConnection);
Expand Down
4 changes: 2 additions & 2 deletions src/plugins/platforms/cocoa/qcocoaintegration.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@
#include "qcocoaclipboard.h"
#include "qcocoadrag.h"
#include "qcocoaservices.h"
#include "qcocoakeymapper.h"
#if QT_CONFIG(vulkan)
#include "qcocoavulkaninstance.h"
#endif
Expand All @@ -57,6 +56,7 @@
#include <qpa/qplatformintegration.h>
#include <QtGui/private/qcoretextfontdatabase_p.h>
#include <QtGui/private/qopenglcontext_p.h>
#include <QtGui/private/qapplekeymapper_p.h>

Q_FORWARD_DECLARE_OBJC_CLASS(NSToolbar);

Expand Down Expand Up @@ -156,7 +156,7 @@ private Q_SLOTS:
QScopedPointer<QCocoaDrag> mCocoaDrag;
QScopedPointer<QCocoaNativeInterface> mNativeInterface;
QScopedPointer<QCocoaServices> mServices;
QScopedPointer<QCocoaKeyMapper> mKeyboardMapper;
QScopedPointer<QAppleKeyMapper> mKeyboardMapper;

#if QT_CONFIG(vulkan)
mutable QCocoaVulkanInstance *mCocoaVulkanInstance = nullptr;
Expand Down
Loading

0 comments on commit 15576c9

Please sign in to comment.