diff options
author | iain holmes <iaholmes@microsoft.com> | 2022-02-25 18:54:40 +0300 |
---|---|---|
committer | iain holmes <iaholmes@microsoft.com> | 2022-02-25 18:54:40 +0300 |
commit | f3a52ef95e84777fa6436fd5eb35235c7e403e79 (patch) | |
tree | f9c665bec3ccb765f150b443d0e64ffb6cc78a5a | |
parent | 5d0bb104f8c9b5f2cc088acbf86944a74a5945b4 (diff) |
Parse the hardware KeyCode into the KeyEventArgs.
-rw-r--r-- | Xwt.XamMac/Xwt.Mac/KeyboardUtil.cs | 288 | ||||
-rw-r--r-- | Xwt/Xwt/KeyEventArgs.cs | 10 |
2 files changed, 133 insertions, 165 deletions
diff --git a/Xwt.XamMac/Xwt.Mac/KeyboardUtil.cs b/Xwt.XamMac/Xwt.Mac/KeyboardUtil.cs index 3ae634ce..d0cea0a7 100644 --- a/Xwt.XamMac/Xwt.Mac/KeyboardUtil.cs +++ b/Xwt.XamMac/Xwt.Mac/KeyboardUtil.cs @@ -34,9 +34,9 @@ namespace Xwt.Mac public static KeyEventArgs ToXwtKeyEventArgs (this NSEvent keyEvent) { NSEventModifierMask mask; - Key key = GetXwtKey(keyEvent, out mask); + Key key = GetXwtKey(keyEvent, out mask, out var rawKey); ModifierKeys mod = mask.ToXwtValue (); - return new KeyEventArgs (key, keyEvent.KeyCode, mod, keyEvent.IsARepeat, (long)TimeSpan.FromSeconds (keyEvent.Timestamp).TotalMilliseconds, keyEvent.Characters, keyEvent.CharactersIgnoringModifiers, keyEvent); + return new KeyEventArgs (key, keyEvent.KeyCode, mod, keyEvent.IsARepeat, (long)TimeSpan.FromSeconds (keyEvent.Timestamp).TotalMilliseconds, keyEvent.Characters, keyEvent.CharactersIgnoringModifiers, keyEvent, rawKey); } static Key RemoveShift(Key key, ref NSEventModifierMask mask) @@ -45,82 +45,135 @@ namespace Xwt.Mac return key; } - static Key GetXwtKey (NSEvent keyEvent, out NSEventModifierMask modMask) + static Key GetXwtKey (NSEvent keyEvent, out NSEventModifierMask modMask, out Key rawKey) { + // NSEvent.KeyCode is the physical key pressed according to an ANSI US keyboard. + // With this pressing the м key on a Cyrillac keyboard or ㅍ in Korean both return v + // Parsing keyboard shortcuts should be based off this. + // Handling text input should be based off the Characters and CharactersIgnoringModifiers fields. + // For compatibility reasons the Key field parses Characters/CharactersIgnoringModifiers into a + // Xwt.Key enum value, but most of unicode characters are not parsed so KeyEventArgs.Key probably + // should be considered obsolete. modMask = keyEvent.ModifierFlags; + rawKey = 0; // special keys switch (keyEvent.KeyCode) { - case 65: return Key.NumPadDecimal; // kVK_ANSI_KeypadDecimal = 0x41 - case 67: return Key.NumPadMultiply; // kVK_ANSI_KeypadMultiply = 0x43 - case 69: return Key.NumPadAdd; // kVK_ANSI_KeypadPlus = 0x45 - case 71: return Key.NumLock; // kVK_ANSI_KeypadClear = 0x47 - case 75: return Key.NumPadDivide; // kVK_ANSI_KeypadDivide = 0x4B - case 76: return Key.NumPadEnter; // kVK_ANSI_KeypadEnter = 0x4C - case 78: return Key.NumPadSubtract; // kVK_ANSI_KeypadMinus = 0x4E - case 81: return Key.NumPadEnter; // kVK_ANSI_KeypadEquals = 0x51 - case 82: return Key.NumPad0; // kVK_ANSI_Keypad0 = 0x52 - case 83: return Key.NumPad1; // kVK_ANSI_Keypad1 = 0x53 - case 84: return Key.NumPad2; // kVK_ANSI_Keypad2 = 0x54 - case 85: return Key.NumPad3; // kVK_ANSI_Keypad3 = 0x55 - case 86: return Key.NumPad4; // kVK_ANSI_Keypad4 = 0x56 - case 87: return Key.NumPad5; // kVK_ANSI_Keypad5 = 0x57 - case 88: return Key.NumPad6; // kVK_ANSI_Keypad6 = 0x58 - case 89: return Key.NumPad7; // kVK_ANSI_Keypad7 = 0x59 - case 91: return Key.NumPad8; // kVK_ANSI_Keypad8 = 0x5B - case 92: return Key.NumPad9; // kVK_ANSI_Keypad9 = 0x5C + case 0: rawKey = Key.a; break; + case 1: rawKey = Key.s; break; + case 2: rawKey = Key.d; break; + case 3: rawKey = Key.f; break; + case 4: rawKey = Key.h; break; + case 5: rawKey = Key.g; break; + case 6: rawKey = Key.z; break; + case 7: rawKey = Key.x; break; + case 8: rawKey = Key.c; break; + case 9: rawKey = Key.v; break; + case 11: rawKey = Key.b; break; + case 12: rawKey = Key.q; break; + case 13: rawKey = Key.w; break; + case 14: rawKey = Key.e; break; + case 15: rawKey = Key.r; break; + case 16: rawKey = Key.y; break; + case 17: rawKey = Key.t; break; + case 18: rawKey = Key.K1; break; + case 19: rawKey = Key.K2; break; + case 20: rawKey = Key.K3; break; + case 21: rawKey = Key.K4; break; + case 22: rawKey = Key.K6; break; + case 23: rawKey = Key.K5; break; + case 24: rawKey = Key.Equal; break; + case 25: rawKey = Key.K9; break; + case 26: rawKey = Key.K7; break; + case 27: rawKey = Key.Minus; break; + case 28: rawKey = Key.K8; break; + case 29: rawKey = Key.K0; break; + case 30: rawKey = Key.RightSquareBracket; break; + case 31: rawKey = Key.o; break; + case 32: rawKey = Key.u; break; + case 33: rawKey = Key.LeftSquareBracket; break; + case 34: rawKey = Key.I; break; + case 35: rawKey = Key.P; break; + case 37: rawKey = Key.L; break; + case 38: rawKey = Key.J; break; + case 39: rawKey = Key.Quote; break; + case 40: rawKey = Key.K; break; + case 41: rawKey = Key.Semicolon; break; + case 42: rawKey = Key.Backslash; break; + case 43: rawKey = Key.Comma; break; + case 44: rawKey = Key.Slash; break; + case 45: rawKey = Key.N; break; + case 46: rawKey = Key.M; break; + case 47: rawKey = Key.Period; break; + case 50: rawKey = Key.Backtick; break; - case 36: return Key.Return; // kVK_Return = 0x24 - case 48: return Key.Tab; // kVK_Tab = 0x30 - case 49: return Key.Space; // kVK_Space = 0x31 - case 51: return Key.BackSpace; // kVK_Delete = 0x33 - case 53: return Key.Escape; // kVK_Escape = 0x35 - case 55: return Key.MetaLeft; // kVK_Command = 0x37 - case 56: return Key.ShiftLeft; // kVK_Shift = 0x38 - case 57: return Key.CapsLock; // kVK_CapsLock = 0x39 - case 58: return Key.AltLeft; // kVK_Option = 0x3A - case 59: return Key.ControlLeft; // kVK_Control = 0x3B - case 60: return Key.ShiftRight; // kVK_RightShift = 0x3C - case 61: return Key.AltRight; // kVK_RightOption = 0x3D - case 62: return Key.ControlRight; // kVK_RightControl = 0x3E - case 0x3F: return Key.function; // kVK_Function = 0x3F - case 0x40: return Key.F17; // kVK_F17 = 0x40 - case 0x48: return Key.AudioRaiseVolume; // kVK_VolumeUp = 0x48 - case 0x49: return Key.AudioLowerVolume; // kVK_VolumeDown = 0x49 - case 0x4A: return Key.AudioMute; // kVK_Mute = 0x4A - case 0x4F: return Key.F18; // kVK_F18 = 0x4F - case 0x50: return Key.F19; // kVK_F19 = 0x50 - case 0x5A: return Key.F20; // kVK_F20 = 0x5A - case 96: return Key.F5; // kVK_F5 = 0x60 - case 97: return Key.F6; // kVK_F6 = 0x61 - case 98: return Key.F7; // kVK_F7 = 0x62 - case 99: return Key.F3; // kVK_F3 = 0x63 - case 100: return Key.F8; // kVK_F8 = 0x64 - case 101: return Key.F9; // kVK_F9 = 0x65 - case 0x67: return Key.F11; // kVK_F11 = 0x67 - case 105: return Key.Print; // kVK_F13 = 0x69 - case 0x6A: return Key.F16; // kVK_F16 = 0x6A - case 0x6B: return Key.F14; // kVK_F14 = 0x6B - case 109: return Key.F10; // kVK_F10 = 0x6D - case 0x6F: return Key.F12; // kVK_F12 = 0x6F - case 0x71: return Key.F15; // kVK_F15 = 0x71 - case 114: return Key.Help; // kVK_Help = 0x72 - case 115: return Key.Home; // kVK_Home = 0x73 - case 116: return Key.PageUp; // kVK_PageUp = 0x74 - case 117: return Key.Delete; // kVK_ForwardDelete = 0x75 - case 118: return Key.F4; // kVK_F4 = 0x76 - case 119: return Key.End; // kVK_End = 0x77 - case 120: return Key.F2; // kVK_F2 = 0x78 - case 121: return Key.PageDown; // kVK_PageDown = 0x79 - case 122: return Key.F1; // kVK_F1 = 0x7A - case 123: return Key.Left; // kVK_LeftArrow = 0x7B - case 124: return Key.Right; // kVK_RightArrow = 0x7C - case 125: return Key.Down; // kVK_DownArrow = 0x7D - case 126: return Key.Up; // kVK_UpArrow = 0x7E - } + case 65: rawKey = Key.NumPadDecimal; return Key.NumPadDecimal; // kVK_ANSI_KeypadDecimal = 0x41 + case 67: rawKey = Key.NumPadMultiply; return Key.NumPadMultiply; // kVK_ANSI_KeypadMultiply = 0x43 + case 69: rawKey = Key.NumPadAdd; return Key.NumPadAdd; // kVK_ANSI_KeypadPlus = 0x45 + case 71: rawKey = Key.NumLock; return Key.NumLock; // kVK_ANSI_KeypadClear = 0x47 + case 75: rawKey = Key.NumPadDivide; return Key.NumPadDivide; // kVK_ANSI_KeypadDivide = 0x4B + case 76: rawKey = Key.NumPadEnter; return Key.NumPadEnter; // kVK_ANSI_KeypadEnter = 0x4C + case 78: rawKey = Key.NumPadSubtract; return Key.NumPadSubtract; // kVK_ANSI_KeypadMinus = 0x4E + case 81: rawKey = Key.NumPadEnter; return Key.NumPadEnter; // kVK_ANSI_KeypadEquals = 0x51 + case 82: rawKey = Key.NumPad0; return Key.NumPad0; // kVK_ANSI_Keypad0 = 0x52 + case 83: rawKey = Key.NumPad1; return Key.NumPad1; // kVK_ANSI_Keypad1 = 0x53 + case 84: rawKey = Key.NumPad2; return Key.NumPad2; // kVK_ANSI_Keypad2 = 0x54 + case 85: rawKey = Key.NumPad3; return Key.NumPad3; // kVK_ANSI_Keypad3 = 0x55 + case 86: rawKey = Key.NumPad4; return Key.NumPad4; // kVK_ANSI_Keypad4 = 0x56 + case 87: rawKey = Key.NumPad5; return Key.NumPad5; // kVK_ANSI_Keypad5 = 0x57 + case 88: rawKey = Key.NumPad6; return Key.NumPad6; // kVK_ANSI_Keypad6 = 0x58 + case 89: rawKey = Key.NumPad7; return Key.NumPad7; // kVK_ANSI_Keypad7 = 0x59 + case 91: rawKey = Key.NumPad8; return Key.NumPad8; // kVK_ANSI_Keypad8 = 0x5B + case 92: rawKey = Key.NumPad9; return Key.NumPad9; // kVK_ANSI_Keypad9 = 0x5C - // character keys: use character comparison, since KeyCode is always US - // and does not honor inernationalization like WPF or GTK + case 36: rawKey = Key.Return; return Key.Return; // kVK_Return = 0x24 + case 48: rawKey = Key.Tab; return Key.Tab; // kVK_Tab = 0x30 + case 49: rawKey = Key.Space; return Key.Space; // kVK_Space = 0x31 + case 51: rawKey = Key.BackSpace; return Key.BackSpace; // kVK_Delete = 0x33 + case 53: rawKey = Key.Escape; return Key.Escape; // kVK_Escape = 0x35 + case 55: rawKey = Key.MetaLeft; return Key.MetaLeft; // kVK_Command = 0x37 + case 56: rawKey = Key.ShiftLeft; return Key.ShiftLeft; // kVK_Shift = 0x38 + case 57: rawKey = Key.CapsLock; return Key.CapsLock; // kVK_CapsLock = 0x39 + case 58: rawKey = Key.AltLeft; return Key.AltLeft; // kVK_Option = 0x3A + case 59: rawKey = Key.ControlLeft; return Key.ControlLeft; // kVK_Control = 0x3B + case 60: rawKey = Key.ShiftRight; return Key.ShiftRight; // kVK_RightShift = 0x3C + case 61: rawKey = Key.AltRight; return Key.AltRight; // kVK_RightOption = 0x3D + case 62: rawKey = Key.ControlRight; return Key.ControlRight; // kVK_RightControl = 0x3E + case 0x3F: rawKey = Key.function; return Key.function; // kVK_Function = 0x3F + case 0x40: rawKey = Key.F17; return Key.F17; // kVK_F17 = 0x40 + case 0x48: rawKey = Key.AudioRaiseVolume; return Key.AudioRaiseVolume; // kVK_VolumeUp = 0x48 + case 0x49: rawKey = Key.AudioLowerVolume; return Key.AudioLowerVolume; // kVK_VolumeDown = 0x49 + case 0x4A: rawKey = Key.AudioMute; return Key.AudioMute; // kVK_Mute = 0x4A + case 0x4F: rawKey = Key.F18; return Key.F18; // kVK_F18 = 0x4F + case 0x50: rawKey = Key.F19; return Key.F19; // kVK_F19 = 0x50 + case 0x5A: rawKey = Key.F20; return Key.F20; // kVK_F20 = 0x5A + case 96: rawKey = Key.F5; return Key.F5; // kVK_F5 = 0x60 + case 97: rawKey = Key.F6; return Key.F6; // kVK_F6 = 0x61 + case 98: rawKey = Key.F7; return Key.F7; // kVK_F7 = 0x62 + case 99: rawKey = Key.F3; return Key.F3; // kVK_F3 = 0x63 + case 100: rawKey = Key.F8; return Key.F8; // kVK_F8 = 0x64 + case 101: rawKey = Key.F9; return Key.F9; // kVK_F9 = 0x65 + case 0x67: rawKey = Key.F11; return Key.F11; // kVK_F11 = 0x67 + case 105: rawKey = Key.Print; return Key.Print; // kVK_F13 = 0x69 + case 0x6A: rawKey = Key.F16; return Key.F16; // kVK_F16 = 0x6A + case 0x6B: rawKey = Key.F14; return Key.F14; // kVK_F14 = 0x6B + case 109: rawKey = Key.F10; return Key.F10; // kVK_F10 = 0x6D + case 0x6F: rawKey = Key.F12; return Key.F12; // kVK_F12 = 0x6F + case 0x71: rawKey = Key.F15; return Key.F15; // kVK_F15 = 0x71 + case 114: rawKey = Key.Help; return Key.Help; // kVK_Help = 0x72 + case 115: rawKey = Key.Home; return Key.Home; // kVK_Home = 0x73 + case 116: rawKey = Key.PageUp; return Key.PageUp; // kVK_PageUp = 0x74 + case 117: rawKey = Key.Delete; return Key.Delete; // kVK_ForwardDelete = 0x75 + case 118: rawKey = Key.F4; return Key.F4; // kVK_F4 = 0x76 + case 119: rawKey = Key.End; return Key.End; // kVK_End = 0x77 + case 120: rawKey = Key.F2; return Key.F2; // kVK_F2 = 0x78 + case 121: rawKey = Key.PageDown; return Key.PageDown; // kVK_PageDown = 0x79 + case 122: rawKey = Key.F1; return Key.F1; // kVK_F1 = 0x7A + case 123: rawKey = Key.Left; return Key.Left; // kVK_LeftArrow = 0x7B + case 124: rawKey = Key.Right; return Key.Right; // kVK_RightArrow = 0x7C + case 125: rawKey = Key.Down; return Key.Down; // kVK_DownArrow = 0x7D + case 126: rawKey = Key.Up; return Key.Up; // kVK_UpArrow = 0x7E + } // How non-roman keyboards seem to work in Cocoa, for example a Russian keyboard, is like so // If you press the м key on the keyboard (which is the 'v' key on a roman/english/qwerty keyboard) @@ -227,99 +280,6 @@ namespace Xwt.Mac case '|': return RemoveShift(Key.Pipe, ref modMask); case '`': return RemoveShift(Key.Backtick, ref modMask); case '~': return RemoveShift(Key.Tilde, ref modMask); - - // Alt+<Key.whatever> and Alt+Shift+<Key.whatever> generate printable characters - // so if we want to use them as keyboard shortcuts, we map them back to their - // group 0 key. For Alt+Shift+<whatever> we map it to the unshifted key - case '¯': return Key.Comma; - case '≤': return Key.Comma; - case '˘': return Key.Period; - case '≥': return Key.Period; - case '¿': return Key.Slash; - case '÷': return Key.Slash; - case 'Ú': return Key.Semicolon; - case '…': return Key.Semicolon; - case 'æ': return Key.Quote; - case 'Æ': return Key.Quote; - case '»': return Key.Backslash; - case '«': return Key.Backslash; - case '“': return Key.LeftSquareBracket; - case '”': return Key.LeftSquareBracket; - case '‘': return Key.RightSquareBracket; - case '’': return Key.RightSquareBracket; - case '–': return Key.Minus; - case '—': return Key.Minus; - case '≠': return Key.Equal; - case '±': return Key.Equal; - - case '¡': return Key.K1; - case '⁄': return Key.K1; - case '€': return Key.K2; - case '™': return Key.K2; - case '‹': return Key.K3; - case '¢': return Key.K4; - case '›': return Key.K4; - case '∞': return Key.K5; - case 'fi': return Key.K5; - case '§': return Key.K6; - case 'fl': return Key.K6; - case '¶': return Key.K7; - case '‡': return Key.K7; - case '•': return Key.K8; - case 'ª': return Key.K9; - case '·': return Key.K9; - case 'º': return Key.K0; - - case 'å': return Key.a; - case 'Å': return Key.a; - case '∫': return Key.b; - case 'ı': return Key.b; - case 'ç': return Key.c; - case 'Ç': return Key.c; - case '∂': return Key.d; - case 'Î': return Key.d; - case '´': return Key.e; - case '‰': return Key.e; - case 'ƒ': return Key.f; - case 'Ï': return Key.f; - case '©': return Key.g; - case 'Ì': return Key.g; - case '˙': return Key.h; - case 'Ó': return Key.h; - case 'È': return Key.i; - case '∆': return Key.j; - case 'Ô': return Key.j; - case '˚': return Key.k; - case '': return Key.k; - case '¬': return Key.l; - case 'Ò': return Key.l; - case 'µ': return Key.m; - case '˜': return Key.m; - case 'ˆ': return Key.n; - case 'ø': return Key.o; - case 'Ø': return Key.o; - case 'π': return Key.p; - case '∏': return Key.p; - case 'œ': return Key.q; - case 'Œ': return Key.q; - case '®': return Key.r; - case 'Â': return Key.r; - case 'ß': return Key.s; - case 'Í': return Key.s; - case '†': return Key.t; - case 'Ê': return Key.t; - case '¨': return Key.u; - case 'Ë': return Key.u; - case '√': return Key.v; - case '◊': return Key.v; - case '∑': return Key.w; - case '„': return Key.w; - case '≈': return Key.x; - case 'Ù': return Key.x; - case '¥': return Key.y; - case 'Á': return Key.y; - case 'Ω': return Key.z; - case 'Û': return Key.z; } return (Key)0; } diff --git a/Xwt/Xwt/KeyEventArgs.cs b/Xwt/Xwt/KeyEventArgs.cs index 452f6b89..c26fb2c6 100644 --- a/Xwt/Xwt/KeyEventArgs.cs +++ b/Xwt/Xwt/KeyEventArgs.cs @@ -101,10 +101,12 @@ namespace Xwt /// <param name="characters">The character string this event represents</param> /// <param name="charactersIgnoringModifiers">The character string this event represents ignoring any modifier keys that are pressed</param> /// <param name="nativeEvent">The native event object</param> - public KeyEventArgs(Key key, int nativeKeyCode, ModifierKeys modifiers, bool isRepeat, long timestamp, string characters, string charactersIgnoringModifiers, object nativeEvent) + /// <param name="hardwareKey">The hardware key pressed</param> + public KeyEventArgs(Key key, int nativeKeyCode, ModifierKeys modifiers, bool isRepeat, long timestamp, string characters, string charactersIgnoringModifiers, object nativeEvent, Key hardwareKey) : this(key, nativeKeyCode, modifiers, isRepeat, timestamp, characters, charactersIgnoringModifiers) { this.NativeEvent = nativeEvent; + this.RawKey = hardwareKey; } /// <summary> @@ -167,6 +169,12 @@ namespace Xwt /// </summary> /// <value>An opaque object representing the event in the native toolkit</value> public object NativeEvent { get; private set; } + + /// <summary> + /// Returns the physical key on the keyboard that was pressed according to the + /// ANSI standard US keyboard. + /// </summary> + public Key HardwareKey { get; internal set; } } } |