| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- namespace Terminal.Gui.Drivers;
- /// <summary>Helper class to handle mapping between <see cref="KeyCode"/> and <see cref="ConsoleKeyInfo"/>.</summary>
- public static class ConsoleKeyMapping
- {
- /// <summary>
- /// Gets a <see cref="ConsoleKeyInfo"/> from a <see cref="KeyCode"/>.
- /// </summary>
- /// <param name="key">The key code to convert.</param>
- /// <returns>A ConsoleKeyInfo representing the key.</returns>
- /// <remarks>
- /// This method is primarily used for test simulation via <see cref="IKeyConverter{T}.ToKeyInfo"/>.
- /// It produces a keyboard-layout-agnostic "best effort" ConsoleKeyInfo suitable for testing.
- /// For shifted characters (e.g., Shift+2), the character returned is US keyboard layout (Shift+2 = '@').
- /// This is acceptable for test simulation but may not match the user's actual keyboard layout.
- /// </remarks>
- public static ConsoleKeyInfo GetConsoleKeyInfoFromKeyCode (KeyCode key)
- {
- ConsoleModifiers modifiers = MapToConsoleModifiers (key);
- KeyCode keyWithoutModifiers = key & ~KeyCode.CtrlMask & ~KeyCode.ShiftMask & ~KeyCode.AltMask;
- // Map to ConsoleKey enum
- (ConsoleKey consoleKey, char keyChar) = MapToConsoleKeyAndChar (keyWithoutModifiers, modifiers);
- return new (
- keyChar,
- consoleKey,
- modifiers.HasFlag (ConsoleModifiers.Shift),
- modifiers.HasFlag (ConsoleModifiers.Alt),
- modifiers.HasFlag (ConsoleModifiers.Control)
- );
- }
- /// <summary>Gets <see cref="ConsoleModifiers"/> from <see cref="bool"/> modifiers.</summary>
- /// <param name="shift">The shift key.</param>
- /// <param name="alt">The alt key.</param>
- /// <param name="control">The control key.</param>
- /// <returns>The console modifiers.</returns>
- public static ConsoleModifiers GetModifiers (bool shift, bool alt, bool control)
- {
- var modifiers = new ConsoleModifiers ();
- if (shift)
- {
- modifiers |= ConsoleModifiers.Shift;
- }
- if (alt)
- {
- modifiers |= ConsoleModifiers.Alt;
- }
- if (control)
- {
- modifiers |= ConsoleModifiers.Control;
- }
- return modifiers;
- }
- /// <summary>Maps a <see cref="ConsoleKeyInfo"/> to a <see cref="KeyCode"/>.</summary>
- /// <param name="consoleKeyInfo">The console key.</param>
- /// <returns>The <see cref="KeyCode"/> or the <paramref name="consoleKeyInfo"/>.</returns>
- public static KeyCode MapConsoleKeyInfoToKeyCode (ConsoleKeyInfo consoleKeyInfo)
- {
- KeyCode keyCode;
- switch (consoleKeyInfo.Key)
- {
- case ConsoleKey.Enter:
- keyCode = KeyCode.Enter;
- break;
- case ConsoleKey.Delete:
- keyCode = KeyCode.Delete;
- break;
- case ConsoleKey.UpArrow:
- keyCode = KeyCode.CursorUp;
- break;
- case ConsoleKey.DownArrow:
- keyCode = KeyCode.CursorDown;
- break;
- case ConsoleKey.LeftArrow:
- keyCode = KeyCode.CursorLeft;
- break;
- case ConsoleKey.RightArrow:
- keyCode = KeyCode.CursorRight;
- break;
- case ConsoleKey.PageUp:
- keyCode = KeyCode.PageUp;
- break;
- case ConsoleKey.PageDown:
- keyCode = KeyCode.PageDown;
- break;
- case ConsoleKey.Home:
- keyCode = KeyCode.Home;
- break;
- case ConsoleKey.End:
- keyCode = KeyCode.End;
- break;
- case ConsoleKey.Insert:
- keyCode = KeyCode.Insert;
- break;
- case ConsoleKey.F1:
- keyCode = KeyCode.F1;
- break;
- case ConsoleKey.F2:
- keyCode = KeyCode.F2;
- break;
- case ConsoleKey.F3:
- keyCode = KeyCode.F3;
- break;
- case ConsoleKey.F4:
- keyCode = KeyCode.F4;
- break;
- case ConsoleKey.F5:
- keyCode = KeyCode.F5;
- break;
- case ConsoleKey.F6:
- keyCode = KeyCode.F6;
- break;
- case ConsoleKey.F7:
- keyCode = KeyCode.F7;
- break;
- case ConsoleKey.F8:
- keyCode = KeyCode.F8;
- break;
- case ConsoleKey.F9:
- keyCode = KeyCode.F9;
- break;
- case ConsoleKey.F10:
- keyCode = KeyCode.F10;
- break;
- case ConsoleKey.F11:
- keyCode = KeyCode.F11;
- break;
- case ConsoleKey.F12:
- keyCode = KeyCode.F12;
- break;
- case ConsoleKey.F13:
- keyCode = KeyCode.F13;
- break;
- case ConsoleKey.F14:
- keyCode = KeyCode.F14;
- break;
- case ConsoleKey.F15:
- keyCode = KeyCode.F15;
- break;
- case ConsoleKey.F16:
- keyCode = KeyCode.F16;
- break;
- case ConsoleKey.F17:
- keyCode = KeyCode.F17;
- break;
- case ConsoleKey.F18:
- keyCode = KeyCode.F18;
- break;
- case ConsoleKey.F19:
- keyCode = KeyCode.F19;
- break;
- case ConsoleKey.F20:
- keyCode = KeyCode.F20;
- break;
- case ConsoleKey.F21:
- keyCode = KeyCode.F21;
- break;
- case ConsoleKey.F22:
- keyCode = KeyCode.F22;
- break;
- case ConsoleKey.F23:
- keyCode = KeyCode.F23;
- break;
- case ConsoleKey.F24:
- keyCode = KeyCode.F24;
- break;
- case ConsoleKey.Clear:
- keyCode = KeyCode.Clear;
- break;
- case ConsoleKey.Tab:
- keyCode = KeyCode.Tab;
- break;
- case ConsoleKey.Spacebar:
- keyCode = KeyCode.Space;
- break;
- case ConsoleKey.Backspace:
- keyCode = KeyCode.Backspace;
- break;
- default:
- if ((int)consoleKeyInfo.KeyChar is >= 1 and <= 26)
- {
- keyCode = (KeyCode)(consoleKeyInfo.KeyChar + 64);
- }
- else
- {
- keyCode = (KeyCode)consoleKeyInfo.KeyChar;
- }
- break;
- }
- keyCode |= MapToKeyCodeModifiers (consoleKeyInfo.Modifiers, keyCode);
- return keyCode;
- }
- /// <summary>Map existing <see cref="KeyCode"/> modifiers to <see cref="ConsoleModifiers"/>.</summary>
- /// <param name="key">The key code.</param>
- /// <returns>The console modifiers.</returns>
- public static ConsoleModifiers MapToConsoleModifiers (KeyCode key)
- {
- var modifiers = new ConsoleModifiers ();
- // BUGFIX: Only set Shift if ShiftMask is explicitly set.
- // KeyCode.A-Z (65-90) represent UNSHIFTED keys, even though their numeric values
- // match uppercase ASCII characters. Do NOT check char.IsUpper!
- if (key.HasFlag (KeyCode.ShiftMask))
- {
- modifiers |= ConsoleModifiers.Shift;
- }
- if (key.HasFlag (KeyCode.AltMask))
- {
- modifiers |= ConsoleModifiers.Alt;
- }
- if (key.HasFlag (KeyCode.CtrlMask))
- {
- modifiers |= ConsoleModifiers.Control;
- }
- return modifiers;
- }
- /// <summary>Maps a <see cref="ConsoleKeyInfo"/> to a <see cref="KeyCode"/>.</summary>
- /// <param name="modifiers">The console modifiers.</param>
- /// <param name="key">The key code.</param>
- /// <returns>The <see cref="KeyCode"/> with <see cref="ConsoleModifiers"/> or the <paramref name="key"/></returns>
- public static KeyCode MapToKeyCodeModifiers (ConsoleModifiers modifiers, KeyCode key)
- {
- var keyMod = new KeyCode ();
- if ((modifiers & ConsoleModifiers.Shift) != 0)
- {
- keyMod = KeyCode.ShiftMask;
- }
- if ((modifiers & ConsoleModifiers.Control) != 0)
- {
- keyMod |= KeyCode.CtrlMask;
- }
- if ((modifiers & ConsoleModifiers.Alt) != 0)
- {
- keyMod |= KeyCode.AltMask;
- }
- return keyMod != KeyCode.Null ? keyMod | key : key;
- }
- /// <summary>
- /// Maps a KeyCode to its corresponding ConsoleKey and character representation.
- /// </summary>
- private static (ConsoleKey consoleKey, char keyChar) MapToConsoleKeyAndChar (KeyCode key, ConsoleModifiers modifiers)
- {
- var keyValue = (uint)key;
- // Check if this is a special key (value > MaxCodePoint means it's offset by MaxCodePoint)
- if (keyValue > (uint)KeyCode.MaxCodePoint)
- {
- var specialKey = (ConsoleKey)(keyValue - (uint)KeyCode.MaxCodePoint);
- // Special keys don't have printable characters
- char specialChar = specialKey switch
- {
- ConsoleKey.Enter => '\r',
- ConsoleKey.Tab => '\t',
- ConsoleKey.Escape => '\u001B',
- ConsoleKey.Backspace => '\b',
- ConsoleKey.Spacebar => ' ',
- _ => '\0' // Function keys, arrows, etc. have no character
- };
- return (specialKey, specialChar);
- }
- // Handle letter keys (A-Z)
- if (keyValue >= (uint)KeyCode.A && keyValue <= (uint)KeyCode.Z)
- {
- var letterKey = (ConsoleKey)keyValue;
- var letterChar = (char)('a' + (keyValue - (uint)KeyCode.A));
- if (modifiers.HasFlag (ConsoleModifiers.Shift))
- {
- letterChar = char.ToUpper (letterChar);
- }
- return (letterKey, letterChar);
- }
- // Handle number keys (D0-D9) with US keyboard layout
- if (keyValue >= (uint)KeyCode.D0 && keyValue <= (uint)KeyCode.D9)
- {
- var numberKey = (ConsoleKey)keyValue;
- char numberChar;
- if (modifiers.HasFlag (ConsoleModifiers.Shift))
- {
- // US keyboard layout: Shift+0-9 produces )!@#$%^&*(
- numberChar = ")!@#$%^&*(" [(int)(keyValue - (uint)KeyCode.D0)];
- }
- else
- {
- numberChar = (char)('0' + (keyValue - (uint)KeyCode.D0));
- }
- return (numberKey, numberChar);
- }
- // Handle other standard keys
- var standardKey = (ConsoleKey)keyValue;
- if (Enum.IsDefined (typeof (ConsoleKey), (int)keyValue))
- {
- char standardChar = standardKey switch
- {
- ConsoleKey.Enter => '\r',
- ConsoleKey.Tab => '\t',
- ConsoleKey.Escape => '\u001B',
- ConsoleKey.Backspace => '\b',
- ConsoleKey.Spacebar => ' ',
- ConsoleKey.Clear => '\0',
- _ when keyValue <= 0x1F => '\0', // Control characters
- _ => (char)keyValue
- };
- return (standardKey, standardChar);
- }
- // For printable Unicode characters, return character with ConsoleKey.None
- if (keyValue <= 0x10FFFF && !char.IsControl ((char)keyValue))
- {
- return (ConsoleKey.None, (char)keyValue);
- }
- // Fallback
- return (ConsoleKey.None, (char)keyValue);
- }
- }
|