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)
288 timestamp = (nsevent ? nsevent.timestamp : NSProcessInfo.processInfo.systemUptime) * 1000;
289 nativeModifiers = nsevent ? nsevent.modifierFlags : NSEvent.modifierFlags;
290 modifiers = QAppleKeyMapper::fromCocoaModifiers(nativeModifiers);
292 switch (nsevent.type) {
293 case NSEventTypeKeyDown: type = QEvent::KeyPress;
break;
294 case NSEventTypeKeyUp: type = QEvent::KeyRelease;
break;
298 switch (nsevent.type) {
299 case NSEventTypeKeyDown:
300 case NSEventTypeKeyUp:
301 case NSEventTypeFlagsChanged:
302 nativeVirtualKey = nsevent.keyCode;
307 if (nsevent.type == NSEventTypeKeyDown || nsevent.type == NSEventTypeKeyUp) {
308 NSString *charactersIgnoringModifiers = nsevent.charactersIgnoringModifiers;
309 NSString *characters = nsevent.characters;
311 QChar character = QChar::ReplacementCharacter;
318 if (characters.length || charactersIgnoringModifiers.length) {
319 if (nativeModifiers & (NSEventModifierFlagControl | NSEventModifierFlagOption)
320 && charactersIgnoringModifiers.length)
321 character = QChar([charactersIgnoringModifiers characterAtIndex:0]);
322 else if (characters.length)
323 character = QChar([characters characterAtIndex:0]);
324 key = QAppleKeyMapper::fromCocoaKey(character);
327 text = QString::fromNSString(characters);
329 isRepeat = nsevent.ARepeat;
333bool KeyEvent::sendWindowSystemEvent(QWindow *window)
const
336 case QEvent::Shortcut: {
337 return QWindowSystemInterface::handleShortcutEvent(window, timestamp,
338 key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers,
341 case QEvent::KeyPress:
342 case QEvent::KeyRelease: {
343 static const int count = 1;
344 QWindowSystemInterface::handleExtendedKeyEvent(window, timestamp,
345 type, key, modifiers, nativeScanCode, nativeVirtualKey, nativeModifiers,
346 text, isRepeat, count);
348 return QWindowSystemInterface::flushWindowSystemEvents();
351 qCritical() <<
"KeyEvent can not send event type" << type;
358 QDebugStateSaver saver(debug);
359 debug.nospace().verbosity(0) <<
"KeyEvent("
360 << e.type <<
", timestamp=" << e.timestamp
361 <<
", key=" << e.key <<
", modifiers=" << e.modifiers
362 <<
", text="<< e.text <<
", isRepeat=" << e.isRepeat
363 <<
", nativeVirtualKey=" << e.nativeVirtualKey
364 <<
", nativeModifiers=" << e.nativeModifiers
QDebug operator<<(QDebug dbg, const QFileInfo &fi)