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;
120 if (![self.inputContext handleEvent:nsevent]) {
121 qCDebug(lcQpaKeys) <<
"Input context did not consume event";
122 m_sendKeyEvent =
true;
124 m_currentlyInterpretedKeyEvent = 0;
125 didInterpretKeyEvent =
true;
129 m_lastKeyDead = isDeadKey;
136 bool accepted =
true;
137 if (m_sendKeyEvent && m_composingText.isEmpty()) {
140 if (didInterpretKeyEvent ? m_sendKeyEventWithoutText : isSpecialKey(keyEvent.text))
142 qCDebug(lcQpaKeys) <<
"Sending as" << keyEvent;
143 accepted = keyEvent.sendWindowSystemEvent(window);
148- (
void)keyDown:(NSEvent *)nsevent
150 if ([self isTransparentForUserInput])
151 return [super keyDown:nsevent];
153 const bool accepted = [self handleKeyEvent:nsevent];
160 const bool shouldPropagate = QCoreApplication::testAttribute(Qt::AA_PluginApplication) && !accepted;
163 if (!shouldPropagate)
164 m_acceptedKeyDowns.insert(nsevent.keyCode);
167 [super keyDown:nsevent];
170- (
void)keyUp:(NSEvent *)nsevent
172 if ([self isTransparentForUserInput])
173 return [super keyUp:nsevent];
175 const bool keyUpAccepted = [self handleKeyEvent:nsevent];
180 const bool keyDownAccepted = m_acceptedKeyDowns.remove(nsevent.keyCode);
181 if (!keyUpAccepted && !keyDownAccepted)
182 [super keyUp:nsevent];
185- (
void)cancelOperation:(id)sender
189 NSEvent *currentEvent = NSApp.currentEvent;
190 if (!currentEvent || currentEvent.type != NSEventTypeKeyDown)
195 if (currentEvent == m_currentlyInterpretedKeyEvent) {
196 m_sendKeyEvent =
true;
203 [self handleKeyEvent:currentEvent];
206- (
void)flagsChanged:(NSEvent *)nsevent
210 KeyEvent keyEvent(nsevent);
211 qCDebug(lcQpaKeys) <<
"Flags changed resulting in" << keyEvent.modifiers;
214 static NSEventModifierFlags m_lastKnownModifiers;
215 NSEventModifierFlags lastKnownModifiers = m_lastKnownModifiers;
216 NSEventModifierFlags newModifiers = lastKnownModifiers ^ keyEvent.nativeModifiers;
217 m_lastKnownModifiers = keyEvent.nativeModifiers;
219 static constexpr std::tuple<NSEventModifierFlags, Qt::Key> modifierMap[] = {
220 { NSEventModifierFlagShift, Qt::Key_Shift },
221 { NSEventModifierFlagControl, Qt::Key_Meta },
222 { NSEventModifierFlagCommand, Qt::Key_Control },
223 { NSEventModifierFlagOption, Qt::Key_Alt },
224 { NSEventModifierFlagCapsLock, Qt::Key_CapsLock }
227 for (
auto [macModifier, qtKey] : modifierMap) {
228 if (!(newModifiers & macModifier))
232 if (qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta)) {
233 if (qtKey == Qt::Key_Meta)
234 qtKey = Qt::Key_Control;
235 else if (qtKey == Qt::Key_Control)
236 qtKey = Qt::Key_Meta;
239 KeyEvent modifierEvent = keyEvent;
240 modifierEvent.type = lastKnownModifiers & macModifier
241 ? QEvent::KeyRelease : QEvent::KeyPress;
243 modifierEvent.key = qtKey;
246 modifierEvent.modifiers ^= QAppleKeyMapper::fromCocoaModifiers(macModifier);
247 modifierEvent.nativeModifiers ^= macModifier;
250 QWindow *window = m_platformWindow->window();
252 qCDebug(lcQpaKeys) <<
"Sending" << modifierEvent;
253 modifierEvent.sendWindowSystemEvent(window);
257#if QT_MACOS_PLATFORM_SDK_EQUAL_OR_ABOVE(150000
)
258- (
void)contextMenuKeyDown:(NSEvent *)nsevent
260 qCDebug(lcQpaKeys) <<
"Handling context menu key down for" << nsevent;
262 if ([self isTransparentForUserInput])
263 return [super contextMenuKeyDown:nsevent];
265 if ([self handleKeyEvent:nsevent]) {
266 qCDebug(lcQpaKeys) <<
"Accepted context menu event as regular key down";
267 m_acceptedKeyDowns.insert(nsevent.keyCode);
271 [super contextMenuKeyDown:nsevent];
282KeyEvent::KeyEvent(NSEvent *nsevent)
284 timestamp = nsevent.timestamp * 1000;
285 nativeModifiers = nsevent.modifierFlags;
286 modifiers = QAppleKeyMapper::fromCocoaModifiers(nativeModifiers);
288 switch (nsevent.type) {
289 case NSEventTypeKeyDown: type = QEvent::KeyPress;
break;
290 case NSEventTypeKeyUp: type = QEvent::KeyRelease;
break;
294 switch (nsevent.type) {
295 case NSEventTypeKeyDown:
296 case NSEventTypeKeyUp:
297 case NSEventTypeFlagsChanged:
298 nativeVirtualKey = nsevent.keyCode;
303 if (nsevent.type == NSEventTypeKeyDown || nsevent.type == NSEventTypeKeyUp) {
304 NSString *charactersIgnoringModifiers = nsevent.charactersIgnoringModifiers;
305 NSString *characters = nsevent.characters;
307 QChar character = QChar::ReplacementCharacter;
314 if (characters.length || charactersIgnoringModifiers.length) {
315 if (nativeModifiers & (NSEventModifierFlagControl | NSEventModifierFlagOption)
316 && charactersIgnoringModifiers.length)
317 character = QChar([charactersIgnoringModifiers characterAtIndex:0]);
318 else if (characters.length)
319 character = QChar([characters characterAtIndex:0]);
320 key = QAppleKeyMapper::fromCocoaKey(character);
323 text = QString::fromNSString(characters);
325 isRepeat = nsevent.ARepeat;
329bool KeyEvent::sendWindowSystemEvent(QWindow *window)
const
332 case QEvent::Shortcut: {
333 return QWindowSystemInterface::handleShortcutEvent(window, timestamp,
334 key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers,
337 case QEvent::KeyPress:
338 case QEvent::KeyRelease: {
339 static const int count = 1;
340 QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp,
341 type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers,
342 text, isRepeat, count);
344 return QWindowSystemInterface::flushWindowSystemEvents();
347 qCritical() <<
"KeyEvent can not send event type" << type;
354 QDebugStateSaver saver(debug);
355 debug.nospace().verbosity(0) <<
"KeyEvent("
356 << e.type <<
", timestamp=" << e.timestamp
357 <<
", key=" << e.key <<
", modifiers=" << e.modifiers
358 <<
", text="<< e.text <<
", isRepeat=" << e.isRepeat
359 <<
", nativeVirtualKey=" << e.nativeVirtualKey
360 <<
", nativeModifiers=" << e.nativeModifiers
QDebug operator<<(QDebug dbg, const QFileInfo &fi)