mirror of
https://github.com/textmate/textmate.git
synced 2026-04-28 03:00:34 -04:00
Improve NSEvent decoding for non-Latin key mappings
If control (⌃) is down and the system gives us a non-ASCII key string then we will disregard it and instead convert the virtual key code to its ANSI character.
This commit is contained in:
@@ -83,6 +83,117 @@ static bool is_ascii (std::string const& str)
|
||||
return 0x20 < ch && ch < 0x7F;
|
||||
}
|
||||
|
||||
static char char_for_key_code (CGKeyCode key, bool shift)
|
||||
{
|
||||
static std::map<CGKeyCode, char> const RegularMap =
|
||||
{
|
||||
{ kVK_ANSI_1, '1' },
|
||||
{ kVK_ANSI_2, '2' },
|
||||
{ kVK_ANSI_3, '3' },
|
||||
{ kVK_ANSI_4, '4' },
|
||||
{ kVK_ANSI_5, '5' },
|
||||
{ kVK_ANSI_6, '6' },
|
||||
{ kVK_ANSI_7, '7' },
|
||||
{ kVK_ANSI_8, '8' },
|
||||
{ kVK_ANSI_9, '9' },
|
||||
{ kVK_ANSI_0, '0' },
|
||||
{ kVK_ANSI_A, 'a' },
|
||||
{ kVK_ANSI_B, 'b' },
|
||||
{ kVK_ANSI_C, 'c' },
|
||||
{ kVK_ANSI_D, 'd' },
|
||||
{ kVK_ANSI_E, 'e' },
|
||||
{ kVK_ANSI_F, 'f' },
|
||||
{ kVK_ANSI_G, 'g' },
|
||||
{ kVK_ANSI_H, 'h' },
|
||||
{ kVK_ANSI_I, 'i' },
|
||||
{ kVK_ANSI_J, 'j' },
|
||||
{ kVK_ANSI_K, 'k' },
|
||||
{ kVK_ANSI_L, 'l' },
|
||||
{ kVK_ANSI_M, 'm' },
|
||||
{ kVK_ANSI_N, 'n' },
|
||||
{ kVK_ANSI_O, 'o' },
|
||||
{ kVK_ANSI_P, 'p' },
|
||||
{ kVK_ANSI_Q, 'q' },
|
||||
{ kVK_ANSI_R, 'r' },
|
||||
{ kVK_ANSI_S, 's' },
|
||||
{ kVK_ANSI_T, 't' },
|
||||
{ kVK_ANSI_U, 'u' },
|
||||
{ kVK_ANSI_V, 'v' },
|
||||
{ kVK_ANSI_W, 'w' },
|
||||
{ kVK_ANSI_X, 'x' },
|
||||
{ kVK_ANSI_Y, 'y' },
|
||||
{ kVK_ANSI_Z, 'z' },
|
||||
|
||||
{ kVK_ANSI_LeftBracket, '[' },
|
||||
{ kVK_ANSI_RightBracket, ']' },
|
||||
{ kVK_ANSI_Slash, '/' },
|
||||
{ kVK_ANSI_Backslash, '\\' },
|
||||
{ kVK_ANSI_Comma, ',' },
|
||||
{ kVK_ANSI_Period, '.' },
|
||||
{ kVK_ANSI_Minus, '-' },
|
||||
{ kVK_ANSI_Equal, '=' },
|
||||
{ kVK_ANSI_Quote, '\'' },
|
||||
{ kVK_ANSI_Grave, '`' },
|
||||
{ kVK_ANSI_Semicolon, ';' },
|
||||
};
|
||||
|
||||
static std::map<CGKeyCode, char> const ShiftedMap =
|
||||
{
|
||||
{ kVK_ANSI_1, '!' },
|
||||
{ kVK_ANSI_2, '@' },
|
||||
{ kVK_ANSI_3, '#' },
|
||||
{ kVK_ANSI_4, '$' },
|
||||
{ kVK_ANSI_5, '%' },
|
||||
{ kVK_ANSI_6, '^' },
|
||||
{ kVK_ANSI_7, '&' },
|
||||
{ kVK_ANSI_8, '*' },
|
||||
{ kVK_ANSI_9, '(' },
|
||||
{ kVK_ANSI_0, ')' },
|
||||
{ kVK_ANSI_A, 'A' },
|
||||
{ kVK_ANSI_B, 'B' },
|
||||
{ kVK_ANSI_C, 'C' },
|
||||
{ kVK_ANSI_D, 'D' },
|
||||
{ kVK_ANSI_E, 'E' },
|
||||
{ kVK_ANSI_F, 'F' },
|
||||
{ kVK_ANSI_G, 'G' },
|
||||
{ kVK_ANSI_H, 'H' },
|
||||
{ kVK_ANSI_I, 'I' },
|
||||
{ kVK_ANSI_J, 'J' },
|
||||
{ kVK_ANSI_K, 'K' },
|
||||
{ kVK_ANSI_L, 'L' },
|
||||
{ kVK_ANSI_M, 'M' },
|
||||
{ kVK_ANSI_N, 'N' },
|
||||
{ kVK_ANSI_O, 'O' },
|
||||
{ kVK_ANSI_P, 'P' },
|
||||
{ kVK_ANSI_Q, 'Q' },
|
||||
{ kVK_ANSI_R, 'R' },
|
||||
{ kVK_ANSI_S, 'S' },
|
||||
{ kVK_ANSI_T, 'T' },
|
||||
{ kVK_ANSI_U, 'U' },
|
||||
{ kVK_ANSI_V, 'V' },
|
||||
{ kVK_ANSI_W, 'W' },
|
||||
{ kVK_ANSI_X, 'X' },
|
||||
{ kVK_ANSI_Y, 'Y' },
|
||||
{ kVK_ANSI_Z, 'Z' },
|
||||
|
||||
{ kVK_ANSI_LeftBracket, '{' },
|
||||
{ kVK_ANSI_RightBracket, '}' },
|
||||
{ kVK_ANSI_Slash, '?' },
|
||||
{ kVK_ANSI_Backslash, '|' },
|
||||
{ kVK_ANSI_Comma, '<' },
|
||||
{ kVK_ANSI_Period, '>' },
|
||||
{ kVK_ANSI_Minus, '_' },
|
||||
{ kVK_ANSI_Equal, '+' },
|
||||
{ kVK_ANSI_Quote, '"' },
|
||||
{ kVK_ANSI_Grave, '~' },
|
||||
{ kVK_ANSI_Semicolon, ':' },
|
||||
};
|
||||
|
||||
auto const& map = shift ? ShiftedMap : RegularMap;
|
||||
auto const it = map.find(key);
|
||||
return it != map.end() ? it->second : 0;
|
||||
}
|
||||
|
||||
/*
|
||||
The “simple” heuristic is the following:
|
||||
|
||||
@@ -91,6 +202,8 @@ static bool is_ascii (std::string const& str)
|
||||
if ⌘ changes key (Qwerty-Dvorak ⌘ hybrid):
|
||||
if ⌥ is down: treat ⌥ as literal
|
||||
if ⇧ is down and (changed) key is not a-z: treat ⇧ as literal else if key is a-z: upcase key string
|
||||
else if ⌃ is down and key string is non-ASCII
|
||||
ignore keymap and decode virtual key code to get ⌃ + «modifiers» + ASCII key
|
||||
else
|
||||
If ⌥ is down and character (with flags & ⌥⇧) is non-ASCII or if ⌥ doesn’t affect key string: treat ⌥ as literal
|
||||
if ⇧ is down and character (with flags & ⌥⇧) is non-ASCII or if ⇧ doesn’t affect key string, treat ⇧ as literal else if key is a-z: upcase key string
|
||||
@@ -139,30 +252,39 @@ std::string to_s (NSEvent* anEvent, bool preserveNumPadFlag)
|
||||
}
|
||||
else
|
||||
{
|
||||
if(flags & kCGEventFlagMaskAlternate)
|
||||
char ch;
|
||||
if((flags & kCGEventFlagMaskControl) && !is_ascii(keyStringNoFlags) && (ch = char_for_key_code(key, flags & kCGEventFlagMaskShift)))
|
||||
{
|
||||
std::string const keyStringAlternate = string_for(key, flags & (kCGEventFlagMaskAlternate|kCGEventFlagMaskShift));
|
||||
if(!is_ascii(keyStringAlternate) || keyStringNoFlags == keyStringAlternate)
|
||||
{
|
||||
D(DBF_NSEvent, bug("option (⌥) is literal\n"););
|
||||
newFlags |= kCGEventFlagMaskAlternate;
|
||||
flags &= ~kCGEventFlagMaskAlternate;
|
||||
}
|
||||
keyString = std::string(1, ch);
|
||||
newFlags |= flags & kCGEventFlagMaskAlternate;
|
||||
}
|
||||
|
||||
if(flags & kCGEventFlagMaskShift)
|
||||
else
|
||||
{
|
||||
std::string const keyStringShift = string_for(key, flags & (kCGEventFlagMaskAlternate|kCGEventFlagMaskShift));
|
||||
if(!is_ascii(keyStringShift) || keyStringNoFlags == keyStringShift)
|
||||
if(flags & kCGEventFlagMaskAlternate)
|
||||
{
|
||||
D(DBF_NSEvent, bug("shift (⇧) is literal\n"););
|
||||
newFlags |= kCGEventFlagMaskShift;
|
||||
flags &= ~kCGEventFlagMaskShift;
|
||||
std::string const keyStringAlternate = string_for(key, flags & (kCGEventFlagMaskAlternate|kCGEventFlagMaskShift));
|
||||
if(!is_ascii(keyStringAlternate) || keyStringNoFlags == keyStringAlternate)
|
||||
{
|
||||
D(DBF_NSEvent, bug("option (⌥) is literal\n"););
|
||||
newFlags |= kCGEventFlagMaskAlternate;
|
||||
flags &= ~kCGEventFlagMaskAlternate;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
if(flags & kCGEventFlagMaskShift)
|
||||
{
|
||||
D(DBF_NSEvent, bug("use NSEvent’s uppercased version\n"););
|
||||
keyString = keyStringShift;
|
||||
std::string const keyStringShift = string_for(key, flags & (kCGEventFlagMaskAlternate|kCGEventFlagMaskShift));
|
||||
if(!is_ascii(keyStringShift) || keyStringNoFlags == keyStringShift)
|
||||
{
|
||||
D(DBF_NSEvent, bug("shift (⇧) is literal\n"););
|
||||
newFlags |= kCGEventFlagMaskShift;
|
||||
flags &= ~kCGEventFlagMaskShift;
|
||||
}
|
||||
else
|
||||
{
|
||||
D(DBF_NSEvent, bug("use NSEvent’s uppercased version\n"););
|
||||
keyString = keyStringShift;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user