gpui_macos: Skip IME for Cmd+key events on non-QWERTY layouts (#51394)

jamarju created

Closes #51297

On non-QWERTY layouts, all Cmd+key events are routed through the macOS
IME because `key_char` is always `None` when Cmd is held. For certain
characters (dead keys like backtick, and non-dead keys like รง), the IME
calls `insertText:` instead of `doCommandBySelector:`, consuming the
event before it reaches GPUI's keybinding system or macOS system
shortcuts.

This adds `!platform` to the IME-path condition in `handle_key_event` so
Cmd+key events bypass the IME (except when composing). GPUI handles them
if a binding matches, otherwise `performKeyEquivalent:` returns `NO` and
macOS handles them.

**This won't fully fix Cmd+backtick window cycling by itself** because
Zed's key_equivalents system maps default keybindings onto the physical
backtick key on various layouts. For example, `cmd-'`
(ToggleSelectedDiffHunks) maps to the backtick key on Spanish, `cmd-=`
(IncreaseBufferFontSize) on Scandinavian layouts, `cmd-"`
(ExpandAllDiffHunks) on German/Portuguese/Swiss, and `cmd-}`
(ActivateNextItem) on Spanish-ISO. These Zed bindings shadow the
backtick key and consume the event before macOS can cycle windows. I'd
appreciate guidance on the preferred approach to resolve these
keybinding conflicts -- IMO, Zed's default shortcuts should not be
interfering with Cmd+backtick for any layout.

Release Notes:

- Fixed Cmd+key shortcuts being consumed by the IME on non-QWERTY
keyboard layouts, preventing Zed keybindings and macOS system shortcuts
from working with special characters.

Change summary

crates/gpui_macos/src/window.rs | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)

Detailed changes

crates/gpui_macos/src/window.rs ๐Ÿ”—

@@ -1799,10 +1799,13 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
             // may need them even if there is no marked text;
             // however we skip keys with control or the input handler adds control-characters to the buffer.
             // and keys with function, as the input handler swallows them.
+            // and keys with platform (Cmd), so that Cmd+key events (e.g. Cmd+`) are not
+            // consumed by the IME on non-QWERTY / dead-key layouts.
             if is_composing
                 || (key_down_event.keystroke.key_char.is_none()
                     && !key_down_event.keystroke.modifiers.control
-                    && !key_down_event.keystroke.modifiers.function)
+                    && !key_down_event.keystroke.modifiers.function
+                    && !key_down_event.keystroke.modifiers.platform)
             {
                 {
                     let mut lock = window_state.as_ref().lock();