summaryrefslogtreecommitdiffstats
path: root/src/plugins/platforms/cocoa/qnsview_keys.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/platforms/cocoa/qnsview_keys.mm')
-rw-r--r--src/plugins/platforms/cocoa/qnsview_keys.mm156
1 files changed, 91 insertions, 65 deletions
diff --git a/src/plugins/platforms/cocoa/qnsview_keys.mm b/src/plugins/platforms/cocoa/qnsview_keys.mm
index d6e0c4b012..abee622e65 100644
--- a/src/plugins/platforms/cocoa/qnsview_keys.mm
+++ b/src/plugins/platforms/cocoa/qnsview_keys.mm
@@ -1,46 +1,65 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the plugins of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
// This file is included from qnsview.mm, and only used to organize the code
+/*
+ Determines if the text represents one of the "special keys" on macOS
+
+ As a legacy from OpenStep, macOS reserves the range 0xF700-0xF8FF of the
+ Unicode private use area for representing function keys on the keyboard:
+
+ http://www.unicode.org/Public/MAPPINGS/VENDORS/APPLE/CORPCHAR.TXT
+
+ https://developer.apple.com/documentation/appkit/nsevent/specialkey
+
+ These code points are not supposed to have any glyphs associated with them,
+ but since we can't guarantee that the system doesn't have a font that does
+ provide glyphs for this range (Arial Unicode MS e.g.) we need to filter
+ the text of our key events up front.
+*/
+static bool isSpecialKey(const QString &text)
+{
+ if (text.length() != 1)
+ return false;
+
+ const char16_t unicode = text.at(0).unicode();
+ if (unicode >= 0xF700 && unicode <= 0xF8FF)
+ return true;
+
+ return false;
+}
+
+static bool sendAsShortcut(const KeyEvent &keyEvent, QWindow *window)
+{
+ KeyEvent shortcutEvent = keyEvent;
+ shortcutEvent.type = QEvent::Shortcut;
+ qCDebug(lcQpaKeys) << "Trying potential shortcuts in" << window
+ << "for" << shortcutEvent;
+
+ if (shortcutEvent.sendWindowSystemEvent(window)) {
+ qCDebug(lcQpaKeys) << "Found matching shortcut; will not send as key event";
+ return true;
+ }
+ qCDebug(lcQpaKeys) << "No matching shortcuts; continuing with key event delivery";
+ return false;
+}
+
@implementation QNSView (Keys)
+- (bool)performKeyEquivalent:(NSEvent *)nsevent
+{
+ // Implemented to handle shortcuts for modified Tab keys, which are
+ // handled by Cocoa and not delivered to your keyDown implementation.
+ if (nsevent.type == NSEventTypeKeyDown && m_composingText.isEmpty()) {
+ const bool ctrlDown = [nsevent modifierFlags] & NSEventModifierFlagControl;
+ const bool isTabKey = nsevent.keyCode == kVK_Tab;
+ if (ctrlDown && isTabKey && sendAsShortcut(KeyEvent(nsevent), [self topLevelWindow]))
+ return YES;
+ }
+ return NO;
+}
+
- (bool)handleKeyEvent:(NSEvent *)nsevent
{
qCDebug(lcQpaKeys) << "Handling" << nsevent;
@@ -52,23 +71,20 @@
// We will send a key event unless the input method handles it
QBoolBlocker sendKeyEventGuard(m_sendKeyEvent, true);
+ // Assume we should send key events with text, unless told
+ // otherwise by doCommandBySelector.
+ m_sendKeyEventWithoutText = false;
+
+ bool didInterpretKeyEvent = false;
+
if (keyEvent.type == QEvent::KeyPress) {
if (m_composingText.isEmpty()) {
- KeyEvent shortcutEvent = keyEvent;
- shortcutEvent.type = QEvent::Shortcut;
- qCDebug(lcQpaKeys) << "Trying potential shortcuts in" << window
- << "for" << shortcutEvent;
-
- if (shortcutEvent.sendWindowSystemEvent(window)) {
- qCDebug(lcQpaKeys) << "Found matching shortcut; will not send as key event";
+ if (sendAsShortcut(keyEvent, window))
return true;
- } else {
- qCDebug(lcQpaKeys) << "No matching shortcuts; continuing with key event delivery";
- }
}
- QObject *focusObject = m_platformWindow->window()->focusObject();
+ QObject *focusObject = m_platformWindow ? m_platformWindow->window()->focusObject() : nullptr;
if (m_sendKeyEvent && focusObject) {
if (auto queryResult = queryInputMethod(focusObject, Qt::ImHints)) {
auto hints = static_cast<Qt::InputMethodHints>(queryResult.value(Qt::ImHints).toUInt());
@@ -97,8 +113,12 @@
qCDebug(lcQpaKeys) << "Interpreting key event for focus object" << focusObject;
m_currentlyInterpretedKeyEvent = nsevent;
- [self interpretKeyEvents:@[nsevent]];
+ if (![self.inputContext handleEvent:nsevent]) {
+ qCDebug(lcQpaKeys) << "Input context did not consume event";
+ m_sendKeyEvent = true;
+ }
m_currentlyInterpretedKeyEvent = 0;
+ didInterpretKeyEvent = true;
// If the last key we sent was dead, then pass the next
// key to the IM as well to complete composition.
@@ -111,7 +131,10 @@
bool accepted = true;
if (m_sendKeyEvent && m_composingText.isEmpty()) {
- KeyEvent keyEvent(nsevent);
+ // Trust text input system on whether to send the event with text or not,
+ // or otherwise apply heuristics to filter out private use symbols.
+ if (didInterpretKeyEvent ? m_sendKeyEventWithoutText : isSpecialKey(keyEvent.text))
+ keyEvent.text = {};
qCDebug(lcQpaKeys) << "Sending as" << keyEvent;
accepted = keyEvent.sendWindowSystemEvent(window);
}
@@ -148,7 +171,7 @@
const bool keyUpAccepted = [self handleKeyEvent:nsevent];
// Propagate the keyUp if neither Qt accepted it nor the corresponding KeyDown was
- // accepted. Qt text controls wil often not use and ignore keyUp events, but we
+ // accepted. Qt text controls will often not use and ignore keyUp events, but we
// want to avoid propagating unmatched keyUps.
const bool keyDownAccepted = m_acceptedKeyDowns.remove(nsevent.keyCode);
if (!keyUpAccepted && !keyDownAccepted)
@@ -165,8 +188,10 @@
// Handling the key event may recurse back here through interpretKeyEvents
// (when IM is enabled), so we need to guard against that.
- if (currentEvent == m_currentlyInterpretedKeyEvent)
+ if (currentEvent == m_currentlyInterpretedKeyEvent) {
+ m_sendKeyEvent = true;
return;
+ }
// Send Command+Key_Period and Escape as normal keypresses so that
// the key sequence is delivered through Qt. That way clients can
@@ -232,6 +257,8 @@
KeyEvent::KeyEvent(NSEvent *nsevent)
{
timestamp = nsevent.timestamp * 1000;
+ nativeModifiers = nsevent.modifierFlags;
+ modifiers = QAppleKeyMapper::fromCocoaModifiers(nativeModifiers);
switch (nsevent.type) {
case NSEventTypeKeyDown: type = QEvent::KeyPress; break;
@@ -239,6 +266,15 @@ KeyEvent::KeyEvent(NSEvent *nsevent)
default: break; // Must be manually set
}
+ switch (nsevent.type) {
+ case NSEventTypeKeyDown:
+ case NSEventTypeKeyUp:
+ case NSEventTypeFlagsChanged:
+ nativeVirtualKey = nsevent.keyCode;
+ default:
+ break;
+ }
+
if (nsevent.type == NSEventTypeKeyDown || nsevent.type == NSEventTypeKeyUp) {
NSString *charactersIgnoringModifiers = nsevent.charactersIgnoringModifiers;
NSString *characters = nsevent.characters;
@@ -259,19 +295,10 @@ KeyEvent::KeyEvent(NSEvent *nsevent)
key = QAppleKeyMapper::fromCocoaKey(character);
}
- // Ignore text for the U+F700-U+F8FF range. This is used by Cocoa when
- // delivering function keys (e.g. arrow keys, backspace, F1-F35, etc.)
- if (!(modifiers & (Qt::ControlModifier | Qt::MetaModifier))
- && (character.unicode() < 0xf700 || character.unicode() > 0xf8ff))
- text = QString::fromNSString(characters);
+ text = QString::fromNSString(characters);
isRepeat = nsevent.ARepeat;
}
-
- nativeVirtualKey = nsevent.keyCode;
-
- nativeModifiers = nsevent.modifierFlags;
- modifiers = QAppleKeyMapper::fromCocoaModifiers(nativeModifiers);
}
bool KeyEvent::sendWindowSystemEvent(QWindow *window) const
@@ -285,10 +312,9 @@ bool KeyEvent::sendWindowSystemEvent(QWindow *window) const
case QEvent::KeyPress:
case QEvent::KeyRelease: {
static const int count = 1;
- static const bool tryShortcutOverride = false;
QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp,
type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers,
- text, isRepeat, count, tryShortcutOverride);
+ text, isRepeat, count);
// FIXME: Make handleExtendedKeyEvent synchronous
return QWindowSystemInterface::flushWindowSystemEvents();
}