fix: support keyboard shortcuts with CJK input sources (Korean, Chinese, Japanese) (#1649)
When a non-Latin input source is active, event.charactersIgnoringModifiers returns CJK characters that cannot match Latin shortcut keys. This adds ASCII-capable input source fallback in KeyboardLayout and updates the matchShortcut guard to skip early-return when event chars are non-ASCII. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
22689d5e0d
commit
8b49d66763
2 changed files with 30 additions and 3 deletions
|
|
@ -10472,8 +10472,13 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
|
|||
// For command-based shortcuts, trust AppKit's layout-aware characters when present.
|
||||
// Keep this strict for letter shortcuts to avoid physical-key collisions across layouts,
|
||||
// while still allowing keyCode fallback for digit/punctuation shortcuts on non-US layouts.
|
||||
// When a non-Latin input source is active (Korean, Chinese, Japanese, etc.),
|
||||
// charactersIgnoringModifiers returns non-ASCII characters that can never match
|
||||
// a Latin shortcut key — skip this guard and fall through to layout-based matching.
|
||||
let hasEventChars = !(eventCharsIgnoringModifiers?.isEmpty ?? true)
|
||||
let eventCharsAreASCII = eventCharsIgnoringModifiers?.allSatisfy(\.isASCII) ?? true
|
||||
if hasEventChars,
|
||||
eventCharsAreASCII,
|
||||
flags.contains(.command),
|
||||
!flags.contains(.control),
|
||||
shouldRequireCharacterMatchForCommandShortcut(shortcutKey: shortcutKey) {
|
||||
|
|
@ -10496,12 +10501,15 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
|
|||
// so keep ANSI keyCode fallback for control-modified shortcuts. Also allow fallback for
|
||||
// command punctuation shortcuts, since some non-US layouts report different characters
|
||||
// for the same physical key even when menu-equivalent semantics should still apply.
|
||||
// When a non-Latin input source is active, treat non-ASCII event chars the same as
|
||||
// absent chars — they carry no usable Latin key identity.
|
||||
let hasUsableEventChars = hasEventChars && eventCharsAreASCII
|
||||
let allowANSIKeyCodeFallback = flags.contains(.control)
|
||||
|| (flags.contains(.command)
|
||||
&& !flags.contains(.control)
|
||||
&& (
|
||||
!shouldRequireCharacterMatchForCommandShortcut(shortcutKey: shortcutKey)
|
||||
|| (!hasEventChars && (layoutCharacter?.isEmpty ?? true))
|
||||
|| (!hasUsableEventChars && (layoutCharacter?.isEmpty ?? true))
|
||||
))
|
||||
if allowANSIKeyCodeFallback, let expectedKeyCode = keyCodeForShortcutKey(shortcutKey) {
|
||||
return event.keyCode == expectedKeyCode
|
||||
|
|
|
|||
|
|
@ -15,12 +15,31 @@ class KeyboardLayout {
|
|||
|
||||
/// Translate a physical keyCode to the character AppKit would use for shortcut matching,
|
||||
/// preserving command-aware layouts such as "Dvorak - QWERTY Command".
|
||||
/// CJK input sources (Korean, Chinese, Japanese) lack kTISPropertyUnicodeKeyLayoutData,
|
||||
/// so we fall back to TISCopyCurrentASCIICapableKeyboardInputSource() in that case.
|
||||
static func character(
|
||||
forKeyCode keyCode: UInt16,
|
||||
modifierFlags: NSEvent.ModifierFlags = []
|
||||
) -> String? {
|
||||
guard let source = TISCopyCurrentKeyboardInputSource()?.takeRetainedValue(),
|
||||
let layoutDataPointer = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData) else {
|
||||
if let source = TISCopyCurrentKeyboardInputSource()?.takeRetainedValue(),
|
||||
let result = characterFromInputSource(source, forKeyCode: keyCode, modifierFlags: modifierFlags) {
|
||||
return result
|
||||
}
|
||||
// Current input source has no Unicode layout data (e.g. Korean, Chinese, Japanese IME).
|
||||
// Fall back to the ASCII-capable source so shortcut matching still works.
|
||||
if let asciiSource = TISCopyCurrentASCIICapableKeyboardInputSource()?.takeRetainedValue(),
|
||||
let result = characterFromInputSource(asciiSource, forKeyCode: keyCode, modifierFlags: modifierFlags) {
|
||||
return result
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
private static func characterFromInputSource(
|
||||
_ source: TISInputSource,
|
||||
forKeyCode keyCode: UInt16,
|
||||
modifierFlags: NSEvent.ModifierFlags
|
||||
) -> String? {
|
||||
guard let layoutDataPointer = TISGetInputSourceProperty(source, kTISPropertyUnicodeKeyLayoutData) else {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue