8
9
10
11
12
13
14
15
16
17
18
19
20
21
22static bool isSpecialKey(
const QString &text)
24 if (text.length() != 1)
27 const char16_t unicode = text.at(0).unicode();
28 if (unicode >= 0xF700 && unicode <= 0xF8FF)
34static bool sendAsShortcut(
const KeyEvent &keyEvent, QWindow *window)
36 KeyEvent shortcutEvent = keyEvent;
37 shortcutEvent.type = QEvent::Shortcut;
38 qCDebug(lcQpaKeys) <<
"Trying potential shortcuts in" << window
39 <<
"for" << shortcutEvent;
41 if (shortcutEvent.sendWindowSystemEvent(window)) {
42 qCDebug(lcQpaKeys) <<
"Found matching shortcut; will not send as key event";
45 qCDebug(lcQpaKeys) <<
"No matching shortcuts; continuing with key event delivery";
49@implementation QNSView (Keys)
51- (
bool)performKeyEquivalent:(NSEvent *)nsevent
55 if (nsevent.type == NSEventTypeKeyDown && m_composingText.isEmpty()) {
56 const bool ctrlDown = [nsevent modifierFlags] & NSEventModifierFlagControl;
57 const bool isTabKey = nsevent.keyCode == kVK_Tab;
58 if (ctrlDown && isTabKey && sendAsShortcut(KeyEvent(nsevent), [self topLevelWindow]))
64- (
bool)handleKeyEvent:(NSEvent *)nsevent
66 qCDebug(lcQpaKeys) <<
"Handling" << nsevent;
67 KeyEvent keyEvent(nsevent);
70 QWindow *window = [self topLevelWindow];
73 QScopedValueRollback sendKeyEventGuard(m_sendKeyEvent,
true);
77 m_sendKeyEventWithoutText =
false;
79 bool didInterpretKeyEvent =
false;
81 if (keyEvent.type == QEvent::KeyPress) {
83 if (m_composingText.isEmpty()) {
84 if (sendAsShortcut(keyEvent, window))
88 QObject *focusObject = m_platformWindow ? m_platformWindow->window()->focusObject() :
nullptr;
89 if (m_sendKeyEvent && focusObject) {
90 if (
auto queryResult = queryInputMethod(focusObject, Qt::ImHints)) {
91 auto hints =
static_cast<Qt::InputMethodHints>(queryResult.value(Qt::ImHints).toUInt());
94 const bool isDeadKey = !nsevent.characters.length;
95 const bool ignoreHidden = (hints & Qt::ImhHiddenText) && !isDeadKey && !m_lastKeyDead;
97 if (!(hints & Qt::ImhDigitsOnly || hints & Qt::ImhFormattedNumbersOnly || ignoreHidden)) {
100 m_sendKeyEvent =
false;
112 if (!(nsevent.modifierFlags & (NSEventModifierFlagCommand | NSEventModifierFlagControl)))
113 [NSCursor setHiddenUntilMouseMoves:YES];
115 qCDebug(lcQpaKeys) <<
"Interpreting key event for focus object" << focusObject;
116 m_currentlyInterpretedKeyEvent = nsevent;
117 if (![self.inputContext handleEvent:nsevent]) {
118 qCDebug(lcQpaKeys) <<
"Input context did not consume event";
119 m_sendKeyEvent =
true;
121 m_currentlyInterpretedKeyEvent = 0;
122 didInterpretKeyEvent =
true;
126 m_lastKeyDead = isDeadKey;
133 bool accepted =
true;
134 if (m_sendKeyEvent && m_composingText.isEmpty()) {
137 if (didInterpretKeyEvent ? m_sendKeyEventWithoutText : isSpecialKey(keyEvent.text))
139 qCDebug(lcQpaKeys) <<
"Sending as" << keyEvent;
140 accepted = keyEvent.sendWindowSystemEvent(window);
145- (
void)keyDown:(NSEvent *)nsevent
147 if ([self isTransparentForUserInput])
148 return [super keyDown:nsevent];
150 const bool accepted = [self handleKeyEvent:nsevent];
157 const bool shouldPropagate = QCoreApplication::testAttribute(Qt::AA_PluginApplication) && !accepted;
160 if (!shouldPropagate)
161 m_acceptedKeyDowns.insert(nsevent.keyCode);
164 [super keyDown:nsevent];
167- (
void)keyUp:(NSEvent *)nsevent
169 if ([self isTransparentForUserInput])
170 return [super keyUp:nsevent];
172 const bool keyUpAccepted = [self handleKeyEvent:nsevent];
177 const bool keyDownAccepted = m_acceptedKeyDowns.remove(nsevent.keyCode);
178 if (!keyUpAccepted && !keyDownAccepted)
179 [super keyUp:nsevent];
182- (
void)cancelOperation:(id)sender
186 NSEvent *currentEvent = NSApp.currentEvent;
187 if (!currentEvent || currentEvent.type != NSEventTypeKeyDown)
192 if (currentEvent == m_currentlyInterpretedKeyEvent) {
193 m_sendKeyEvent =
true;
200 [self handleKeyEvent:currentEvent];
203- (
void)flagsChanged:(NSEvent *)nsevent
207 KeyEvent keyEvent(nsevent);
208 qCDebug(lcQpaKeys) <<
"Flags changed resulting in" << keyEvent.modifiers;
211 static NSEventModifierFlags m_lastKnownModifiers;
212 NSEventModifierFlags lastKnownModifiers = m_lastKnownModifiers;
213 NSEventModifierFlags newModifiers = lastKnownModifiers ^ keyEvent.nativeModifiers;
214 m_lastKnownModifiers = keyEvent.nativeModifiers;
216 static constexpr std::tuple<NSEventModifierFlags, Qt::Key> modifierMap[] = {
217 { NSEventModifierFlagShift, Qt::Key_Shift },
218 { NSEventModifierFlagControl, Qt::Key_Meta },
219 { NSEventModifierFlagCommand, Qt::Key_Control },
220 { NSEventModifierFlagOption, Qt::Key_Alt },
221 { NSEventModifierFlagCapsLock, Qt::Key_CapsLock }
224 for (
auto [macModifier, qtKey] : modifierMap) {
225 if (!(newModifiers & macModifier))
229 if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
230 if (qtKey == Qt::Key_Meta)
231 qtKey = Qt::Key_Control;
232 else if (qtKey == Qt::Key_Control)
233 qtKey = Qt::Key_Meta;
236 KeyEvent modifierEvent = keyEvent;
237 modifierEvent.type = lastKnownModifiers & macModifier
238 ? QEvent::KeyRelease : QEvent::KeyPress;
240 modifierEvent.key = qtKey;
243 modifierEvent.modifiers ^= QAppleKeyMapper::fromCocoaModifiers(macModifier);
244 modifierEvent.nativeModifiers ^= macModifier;
247 QWindow *window = m_platformWindow->window();
249 qCDebug(lcQpaKeys) <<
"Sending" << modifierEvent;
250 modifierEvent.sendWindowSystemEvent(window);
254#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(150000
)
255- (
void)contextMenuKeyDown:(NSEvent *)nsevent
257 qCDebug(lcQpaKeys) <<
"Handling context menu key down for" << nsevent;
259 if ([self isTransparentForUserInput])
260 return [super contextMenuKeyDown:nsevent];
262 if ([self handleKeyEvent:nsevent]) {
263 qCDebug(lcQpaKeys) <<
"Accepted context menu event as regular key down";
264 m_acceptedKeyDowns.insert(nsevent.keyCode);
268 [super contextMenuKeyDown:nsevent];
279KeyEvent::KeyEvent(NSEvent *nsevent)
281 timestamp = nsevent.timestamp * 1000;
282 nativeModifiers = nsevent.modifierFlags;
283 modifiers = QAppleKeyMapper::fromCocoaModifiers(nativeModifiers);
285 switch (nsevent.type) {
286 case NSEventTypeKeyDown: type = QEvent::KeyPress;
break;
287 case NSEventTypeKeyUp: type = QEvent::KeyRelease;
break;
291 switch (nsevent.type) {
292 case NSEventTypeKeyDown:
293 case NSEventTypeKeyUp:
294 case NSEventTypeFlagsChanged:
295 nativeVirtualKey = nsevent.keyCode;
300 if (nsevent.type == NSEventTypeKeyDown || nsevent.type == NSEventTypeKeyUp) {
301 NSString *charactersIgnoringModifiers = nsevent.charactersIgnoringModifiers;
302 NSString *characters = nsevent.characters;
304 QChar character = QChar::ReplacementCharacter;
311 if (characters.length || charactersIgnoringModifiers.length) {
312 if (nativeModifiers & (NSEventModifierFlagControl | NSEventModifierFlagOption)
313 && charactersIgnoringModifiers.length)
314 character = QChar([charactersIgnoringModifiers characterAtIndex:0]);
315 else if (characters.length)
316 character = QChar([characters characterAtIndex:0]);
317 key = QAppleKeyMapper::fromCocoaKey(character);
320 text = QString::fromNSString(characters);
322 isRepeat = nsevent.ARepeat;
326bool KeyEvent::sendWindowSystemEvent(QWindow *window)
const
329 case QEvent::Shortcut: {
330 return QWindowSystemInterface::handleShortcutEvent(window, timestamp,
331 key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers,
334 case QEvent::KeyPress:
335 case QEvent::KeyRelease: {
336 static const int count = 1;
337 QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp,
338 type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers,
339 text, isRepeat, count);
341 return QWindowSystemInterface::flushWindowSystemEvents();
344 qCritical() <<
"KeyEvent can not send event type" << type;
351 QDebugStateSaver saver(debug);
352 debug.nospace().verbosity(0) <<
"KeyEvent("
353 << e.type <<
", timestamp=" << e.timestamp
354 <<
", key=" << e.key <<
", modifiers=" << e.modifiers
355 <<
", text="<< e.text <<
", isRepeat=" << e.isRepeat
356 <<
", nativeVirtualKey=" << e.nativeVirtualKey
357 <<
", nativeModifiers=" << e.nativeModifiers
QDebug operator<<(QDebug dbg, const QFileInfo &fi)