using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Text; using System.Text.Json.Serialization; using static System.Runtime.CompilerServices.RuntimeHelpers; namespace Terminal.Gui; /// /// Provides an abstraction for common keyboard operations and state. Used for processing keyboard input and raising keyboard events. /// /// /// /// This class provides a high-level abstraction with helper methods and properties for common keyboard operations. Use this class /// instead of the enumeration for keyboard input whenever possible. /// /// /// /// /// /// The default value for is and can be tested using . /// /// /// /// /// ConceptDefinition /// /// /// Testing Shift State /// /// The Is properties (,, ) test for shift state; whether the key press was modified by a shift key. /// /// /// /// Adding Shift State /// /// The With properties (,, ) return a copy of the Key with the shift modifier applied. This /// is useful for specifying a key that requires a shift modifier (e.g. var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel;. /// /// /// /// Removing Shift State /// /// The No properties (,, ) return a copy of the Key with the shift modifier removed. This /// is useful for specifying a key that does not require a shift modifier (e.g. var ControlDelete = ControlAltDelete.NoCtrl;. /// /// /// /// Encoding of A..Z /// /// Lowercase alpha keys are encoded (in ) as values between 65 and 90 corresponding to /// the un-shifted A to Z keys on a keyboard. Properties are provided for these (e.g. , , etc.). /// Even though the encoded values are the same as the ASCII values for uppercase characters, these enum values represent *lowercase*, un-shifted characters. /// /// /// /// Persistence as strings /// /// Keys are persisted as "[Modifiers]+[Key]. For example new Key(Key.Delete).WithAlt.WithDel is persisted as "Ctrl+Alt+Delete". See /// and for more information. /// /// /// /// /// public class Key : EventArgs, IEquatable { /// /// Constructs a new /// public Key () : this (KeyCode.Null) { } /// /// Constructs a new from the provided Key value /// /// The key public Key (KeyCode k) => KeyCode = k; /// /// Constructs a new from a char. /// /// /// /// The key codes for the A..Z keys are encoded as values between 65 and 90 ( through ). /// While these are the same as the ASCII values for uppercase characters, they represent *keys*, not characters. /// Therefore, this constructor will store 'A'..'Z' as .. with the /// set and will /// store `a`..`z` as ... /// /// /// public Key (char ch) { switch (ch) { case >= 'A' and <= 'Z': // Upper case A..Z mean "Shift-char" so we need to add Shift KeyCode = (KeyCode)ch | KeyCode.ShiftMask; break; case >= 'a' and <= 'z': // Lower case a..z mean no shift, so we need to store as Key.A...Key.Z KeyCode = (KeyCode)(ch - 32); return; default: KeyCode = (KeyCode)ch; break; } } /// /// Constructs a new Key from a string describing the key. /// See for information /// on the format of the string. /// /// The string describing the key. public Key (string str) { var result = Key.TryParse (str, out Key key); if (!result) { throw new ArgumentException (@$"Invalid key string: {str}", nameof (str)); } KeyCode = key.KeyCode; } /// /// Indicates if the current Key event has already been processed and the driver should stop notifying any other event subscriber. /// Its important to set this value to true specially when updating any View's layout from inside the subscriber method. /// public bool Handled { get; set; } = false; /// /// The encoded key value. /// /// /// IMPORTANT: Lowercase alpha keys are encoded (in ) as values between 65 and /// 90 corresponding to the un-shifted A to Z keys on a keyboard. Enum values /// are provided for these (e.g. , , etc.). Even though the values are the same as the ASCII /// values for uppercase characters, these enum values represent *lowercase*, un-shifted characters. /// /// /// This property is the backing data for the . It is a enum value. /// public KeyCode KeyCode { get; init; } /// /// Enables passing the key binding scope with the event. Default is . /// public KeyBindingScope Scope { get; set; } = KeyBindingScope.Focused; /// /// The key value as a Rune. This is the actual value of the key pressed, and is independent of the modifiers. Useful /// for determining if a key represents is a printable character. /// /// /// /// Keys with Ctrl or Alt modifiers will return . /// /// /// If the key is a letter key (A-Z), the Rune will be the upper or lower case letter depending on whether /// is set. /// /// /// If the key is outside of the range, the returned Rune will be . /// /// public Rune AsRune => ToRune (KeyCode); /// /// Converts a to a . Useful /// for determining if a key represents is a printable character. /// /// /// /// Keys with Ctrl or Alt modifiers will return . /// /// /// If the key is a letter key (A-Z), the Rune will be the upper or lower case letter depending on whether /// is set. /// /// /// If the key is outside of the range, the returned Rune will be . /// /// /// /// The key converted to a Rune. if conversion is not possible. public static Rune ToRune (KeyCode key) { if (key is KeyCode.Null or KeyCode.SpecialMask || key.HasFlag (KeyCode.CtrlMask) || key.HasFlag (KeyCode.AltMask)) { return default; } // Extract the base key code var baseKey = key; if (baseKey.HasFlag(KeyCode.ShiftMask)) { baseKey &= ~KeyCode.ShiftMask; } switch (baseKey) { case >= KeyCode.A and <= KeyCode.Z when !key.HasFlag (KeyCode.ShiftMask): return new Rune ((uint)(baseKey + 32)); case >= KeyCode.A and <= KeyCode.Z when key.HasFlag (KeyCode.ShiftMask): return new Rune ((uint)baseKey); case > KeyCode.Null and < KeyCode.A: return new Rune ((uint)baseKey); } if (Enum.IsDefined (typeof (KeyCode), baseKey)) { return default; } return new Rune ((uint)baseKey); } /// /// Gets a value indicating whether the Shift key was pressed. /// /// if is shift; otherwise, . public bool IsShift => (KeyCode & KeyCode.ShiftMask) != 0; /// /// Gets a value indicating whether the Alt key was pressed (real or synthesized) /// /// if is alternate; otherwise, . public bool IsAlt => (KeyCode & KeyCode.AltMask) != 0; /// /// Gets a value indicating whether the Ctrl key was pressed. /// /// if is ctrl; otherwise, . public bool IsCtrl => (KeyCode & KeyCode.CtrlMask) != 0; /// /// Gets a value indicating whether the key represents a key in the range of to , /// regardless of the . This is useful for testing if a key is based on these keys which are /// special cased. /// /// /// IMPORTANT: Lowercase alpha keys are encoded in as values between 65 and 90 corresponding to /// the un-shifted A to Z keys on a keyboard. Helper properties are provided these (e.g. , , etc.). /// Even though the values are the same as the ASCII values for uppercase characters, these enum values represent *lowercase*, un-shifted characters. /// public bool IsKeyCodeAtoZ => GetIsKeyCodeAtoZ (KeyCode); /// /// Tests if a KeyCode represents a key in the range of to , /// regardless of the . This is useful for testing if a key is based on these keys which are /// special cased. /// /// /// IMPORTANT: Lowercase alpha keys are encoded in as values between 65 and 90 corresponding to /// the un-shifted A to Z keys on a keyboard. Helper properties are provided these (e.g. , , etc.). /// Even though the values are the same as the ASCII values for uppercase characters, these enum values represent *lowercase*, un-shifted characters. /// public static bool GetIsKeyCodeAtoZ (KeyCode keyCode) { if ((keyCode & KeyCode.AltMask) != 0 || (keyCode & KeyCode.CtrlMask) != 0) { return false; } if ((keyCode & ~KeyCode.Space & ~KeyCode.ShiftMask) is >= KeyCode.A and <= KeyCode.Z) { return true; } return (keyCode & KeyCode.CharMask) is >= KeyCode.A and <= KeyCode.Z; } /// /// Indicates whether the is valid or not. Invalid keys are , /// and keys with only shift modifiers. /// public bool IsValid => this != Empty && (NoAlt.NoShift.NoCtrl != Empty); /// /// Helper for specifying a shifted . /// /// var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel; /// /// public Key WithShift => new (KeyCode | KeyCode.ShiftMask); /// /// Helper for removing a shift modifier from a . /// /// var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel; /// var AltDelete = ControlAltDelete.NoCtrl; /// /// public Key NoShift => new (KeyCode & ~KeyCode.ShiftMask); /// /// Helper for specifying a shifted . /// /// var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel; /// /// public Key WithCtrl => new (KeyCode | KeyCode.CtrlMask); /// /// Helper for removing a shift modifier from a . /// /// var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel; /// var AltDelete = ControlAltDelete.NoCtrl; /// /// public Key NoCtrl => new (KeyCode & ~KeyCode.CtrlMask); /// /// Helper for specifying a shifted . /// /// var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel; /// /// public Key WithAlt => new (KeyCode | KeyCode.AltMask); /// /// Helper for removing a shift modifier from a . /// /// var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel; /// var AltDelete = ControlAltDelete.NoCtrl; /// /// public Key NoAlt => new (KeyCode & ~KeyCode.AltMask); #region Operators /// /// Explicitly cast a to a . The conversion is lossy because properties /// such as are not encoded in . /// /// /// Uses . /// /// public static explicit operator Rune (Key kea) => kea.AsRune; // BUGBUG: (Tig) I do not think this cast operator is really needed. /// /// Explicitly cast to a . The conversion is lossy because properties /// such as are not encoded in . /// /// public static explicit operator uint (Key kea) => (uint)kea.KeyCode; /// /// Explicitly cast to a . The conversion is lossy because properties /// such as are not encoded in . /// /// public static explicit operator KeyCode (Key key) => key.KeyCode; /// /// Cast to a . /// /// public static implicit operator Key (KeyCode keyCode) => new Key (keyCode); /// /// Cast to a . /// /// /// See for more information. /// /// public static implicit operator Key (char ch) => new Key (ch); /// /// Cast to a . /// /// /// See for more information. /// /// public static implicit operator Key (string str) => new Key (str); /// /// Cast a to a . /// /// /// See for more information. /// /// public static implicit operator string (Key key) => key.ToString (); /// public override bool Equals (object obj) => obj is Key k && k.KeyCode == KeyCode; bool IEquatable.Equals (Key other) => Equals ((object)other); /// public override int GetHashCode () => (int)KeyCode; /// /// Compares two s for equality. /// /// /// /// public static bool operator == (Key a, Key b) => a?.KeyCode == b?.KeyCode; /// /// Compares two s for not equality. /// /// /// /// public static bool operator != (Key a, Key b) => a?.KeyCode != b?.KeyCode; /// /// Compares two s for less-than. /// /// /// /// public static bool operator < (Key a, Key b) => a?.KeyCode < b?.KeyCode; /// /// Compares two s for greater-than. /// /// /// /// public static bool operator > (Key a, Key b) => a?.KeyCode > b?.KeyCode; /// /// Compares two s for greater-than-or-equal-to. /// /// /// /// public static bool operator <= (Key a, Key b) => a?.KeyCode <= b?.KeyCode; /// /// Compares two s for greater-than-or-equal-to. /// /// /// /// public static bool operator >= (Key a, Key b) => a?.KeyCode >= b?.KeyCode; #endregion Operators #region String conversion /// /// Pretty prints the KeyEvent /// /// public override string ToString () => ToString (KeyCode, (Rune)'+'); static string GetKeyString (KeyCode key) { if (key is KeyCode.Null or KeyCode.SpecialMask) { return string.Empty; } // Extract the base key (removing modifier flags) var baseKey = key & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask; if (!key.HasFlag (KeyCode.ShiftMask) && baseKey is >= KeyCode.A and <= KeyCode.Z) { return ((Rune)(uint)(key + 32)).ToString (); } if (key is > KeyCode.Space and < KeyCode.A) { return ((Rune)(uint)key).ToString (); } string keyName = Enum.GetName (typeof (KeyCode), key); return !string.IsNullOrEmpty (keyName) ? keyName : ((Rune)(uint)key).ToString (); } /// /// Formats a as a string using the default separator of '+' /// /// The key to format. /// The formatted string. If the key is a printable character, it will be returned as a string. Otherwise, the key name will be returned. public static string ToString (KeyCode key) => ToString (key, (Rune)'+'); /// /// Formats a as a string. /// /// The key to format. /// The character to use as a separator between modifier keys and and the key itself. /// The formatted string. If the key is a printable character, it will be returned as a string. Otherwise, the key name will be returned. public static string ToString (KeyCode key, Rune separator) { if (key is KeyCode.Null) { // Same as Key.IsValid return @"Null"; } var sb = new StringBuilder (); // Extract the base key (removing modifier flags) var baseKey = key & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask; // Extract and handle modifiers bool hasModifiers = false; if ((key & KeyCode.CtrlMask) != 0) { sb.Append ($"Ctrl{separator}"); hasModifiers = true; } if ((key & KeyCode.AltMask) != 0) { sb.Append ($"Alt{separator}"); hasModifiers = true; } if ((key & KeyCode.ShiftMask) != 0 && !GetIsKeyCodeAtoZ (key)) { sb.Append ($"Shift{separator}"); hasModifiers = true; } // Handle special cases and modifiers on their own if (key != KeyCode.SpecialMask && (baseKey != KeyCode.Null || hasModifiers)) { if ((key & KeyCode.SpecialMask) != 0 && (baseKey & ~KeyCode.Space) is >= KeyCode.A and <= KeyCode.Z) { sb.Append (baseKey & ~KeyCode.Space); } else { // Append the actual key name sb.Append (GetKeyString (baseKey)); } } return TrimEndSeparator (sb.ToString (), separator); } static string TrimEndSeparator (string input, Rune separator) { // Trim the trailing separator (+). Unless there are two separators at the end. // "+" (don't trim) // "Ctrl+" (trim) // "Ctrl++" (trim) if (input.Length > 1 && new Rune(input [^1]) == separator && new Rune(input [^2]) != separator) { return input [..^1]; } return input; } static readonly Dictionary _modifierDict = new (comparer: StringComparer.InvariantCultureIgnoreCase) { { "Shift", KeyCode.ShiftMask }, { "Ctrl", KeyCode.CtrlMask }, { "Alt", KeyCode.AltMask } }; /// /// Converts the provided string to a new instance. /// /// The text to analyze. Formats supported are /// "Ctrl+X", "Alt+X", "Shift+X", "Ctrl+Alt+X", "Ctrl+Shift+X", "Alt+Shift+X", "Ctrl+Alt+Shift+X", and "X". /// /// The parsed value. /// A boolean value indicating whether parsing was successful. /// /// public static bool TryParse (string text, [NotNullWhen (true)] out Key key) { if (string.IsNullOrEmpty (text)) { key = new Key (KeyCode.Null); return true; } key = null; // Split the string into parts string [] parts = text.Split ('+', '-'); if (parts.Length is 0 or > 4 || parts.Any (string.IsNullOrEmpty)) { return false; } // if it's just a shift key if (parts.Length == 1) { switch (parts [0]) { case "Ctrl": key = new Key (KeyCode.CtrlMask); return true; case "Alt": key = new Key (KeyCode.AltMask); return true; case "Shift": key = new Key (KeyCode.ShiftMask); return true; } } var modifiers = KeyCode.Null; for (int index = 0; index < parts.Length; index++) { if (_modifierDict.TryGetValue (parts [index].ToLowerInvariant (), out var modifier)) { modifiers |= modifier; parts [index] = string.Empty; // eat it } } // we now have the modifiers string partNotFound = parts.FirstOrDefault (p => !string.IsNullOrEmpty (p), string.Empty); var parsedKeyCode = KeyCode.Null; int parsedInt = 0; if (partNotFound.Length == 1) { var keyCode = (KeyCode)partNotFound [0]; // if it's a single digit int, treat it as such if (int.TryParse (partNotFound, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out parsedInt)) { keyCode = (KeyCode)((int)KeyCode.D0 + parsedInt); } else if (Enum.TryParse (partNotFound, false, out parsedKeyCode)) { if (parsedKeyCode != KeyCode.Null) { if (parsedKeyCode is >= KeyCode.A and <= KeyCode.Z && modifiers == 0) { key = new Key (parsedKeyCode | KeyCode.ShiftMask); return true; } key = new Key ((KeyCode)parsedKeyCode | modifiers); return true; } } key = new Key (keyCode | modifiers); return true; } if (Enum.TryParse (partNotFound, true, out parsedKeyCode)) { if (parsedKeyCode != KeyCode.Null) { if (parsedKeyCode is >= KeyCode.A and <= KeyCode.Z && modifiers == 0) { key = new Key (parsedKeyCode | KeyCode.ShiftMask); return true; } key = new Key (parsedKeyCode | modifiers); return true; } } // if it's a number int, treat it as a unicode value if (int.TryParse (partNotFound, System.Globalization.NumberStyles.Number, System.Globalization.CultureInfo.InvariantCulture, out parsedInt)) { if (!Rune.IsValid (parsedInt)) { return false; } if ((KeyCode)parsedInt is >= KeyCode.A and <= KeyCode.Z && modifiers == 0) { key = new Key ((KeyCode)parsedInt | KeyCode.ShiftMask); return true; } key = new Key ((KeyCode)parsedInt); return true; } if (!Enum.TryParse (partNotFound, true, out parsedKeyCode)) { return false; } if (GetIsKeyCodeAtoZ (parsedKeyCode)) { key = new Key (parsedKeyCode | modifiers & ~KeyCode.Space); return true; } return false; } #endregion #region Standard Key Definitions /// /// An uninitialized The object. /// public new static Key Empty => new (); /// /// The object for the Backspace key. /// public static Key Backspace => new (KeyCode.Backspace); /// /// The object for the tab key (forwards tab key). /// public static Key Tab => new (KeyCode.Tab); /// /// The object for the return key. /// public static Key Enter => new (KeyCode.Enter); /// /// The object for the clear key. /// public static Key Clear => new (KeyCode.Clear); /// /// The object for the Escape key. /// public static Key Esc => new (KeyCode.Esc); /// /// The object for the Space bar key. /// public static Key Space => new (KeyCode.Space); /// /// The object for 0 key. /// public static Key D0 => new (KeyCode.D0); /// /// The object for 1 key. /// public static Key D1 => new (KeyCode.D1); /// /// The object for 2 key. /// public static Key D2 => new (KeyCode.D2); /// /// The object for 3 key. /// public static Key D3 => new (KeyCode.D3); /// /// The object for 4 key. /// public static Key D4 => new (KeyCode.D4); /// /// The object for 5 key. /// public static Key D5 => new (KeyCode.D5); /// /// The object for 6 key. /// public static Key D6 => new (KeyCode.D6); /// /// The object for 7 key. /// public static Key D7 => new (KeyCode.D7); /// /// The object for 8 key. /// public static Key D8 => new (KeyCode.D8); /// /// The object for 9 key. /// public static Key D9 => new (KeyCode.D9); /// /// The object for the A key (un-shifted). Use Key.A.WithShift for uppercase 'A'. /// public static Key A => new (KeyCode.A); /// /// The object for the B key (un-shifted). Use Key.B.WithShift for uppercase 'B'. /// public static Key B => new (KeyCode.B); /// /// The object for the C key (un-shifted). Use Key.C.WithShift for uppercase 'C'. /// public static Key C => new (KeyCode.C); /// /// The object for the D key (un-shifted). Use Key.D.WithShift for uppercase 'D'. /// public static Key D => new (KeyCode.D); /// /// The object for the E key (un-shifted). Use Key.E.WithShift for uppercase 'E'. /// public static Key E => new (KeyCode.E); /// /// The object for the F key (un-shifted). Use Key.F.WithShift for uppercase 'F'. /// public static Key F => new (KeyCode.F); /// /// The object for the G key (un-shifted). Use Key.G.WithShift for uppercase 'G'. /// public static Key G => new (KeyCode.G); /// /// The object for the H key (un-shifted). Use Key.H.WithShift for uppercase 'H'. /// public static Key H => new (KeyCode.H); /// /// The object for the I key (un-shifted). Use Key.I.WithShift for uppercase 'I'. /// public static Key I => new (KeyCode.I); /// /// The object for the J key (un-shifted). Use Key.J.WithShift for uppercase 'J'. /// public static Key J => new (KeyCode.J); /// /// The object for the K key (un-shifted). Use Key.K.WithShift for uppercase 'K'. /// public static Key K => new (KeyCode.K); /// /// The object for the L key (un-shifted). Use Key.L.WithShift for uppercase 'L'. /// public static Key L => new (KeyCode.L); /// /// The object for the M key (un-shifted). Use Key.M.WithShift for uppercase 'M'. /// public static Key M => new (KeyCode.M); /// /// The object for the N key (un-shifted). Use Key.N.WithShift for uppercase 'N'. /// public static Key N => new (KeyCode.N); /// /// The object for the O key (un-shifted). Use Key.O.WithShift for uppercase 'O'. /// public static Key O => new (KeyCode.O); /// /// The object for the P key (un-shifted). Use Key.P.WithShift for uppercase 'P'. /// public static Key P => new (KeyCode.P); /// /// The object for the Q key (un-shifted). Use Key.Q.WithShift for uppercase 'Q'. /// public static Key Q => new (KeyCode.Q); /// /// The object for the R key (un-shifted). Use Key.R.WithShift for uppercase 'R'. /// public static Key R => new (KeyCode.R); /// /// The object for the S key (un-shifted). Use Key.S.WithShift for uppercase 'S'. /// public static Key S => new (KeyCode.S); /// /// The object for the T key (un-shifted). Use Key.T.WithShift for uppercase 'T'. /// public static Key T => new (KeyCode.T); /// /// The object for the U key (un-shifted). Use Key.U.WithShift for uppercase 'U'. /// public static Key U => new (KeyCode.U); /// /// The object for the V key (un-shifted). Use Key.V.WithShift for uppercase 'V'. /// public static Key V => new (KeyCode.V); /// /// The object for the W key (un-shifted). Use Key.W.WithShift for uppercase 'W'. /// public static Key W => new (KeyCode.W); /// /// The object for the X key (un-shifted). Use Key.X.WithShift for uppercase 'X'. /// public static Key X => new (KeyCode.X); /// /// The object for the Y key (un-shifted). Use Key.Y.WithShift for uppercase 'Y'. /// public static Key Y => new (KeyCode.Y); /// /// The object for the Z key (un-shifted). Use Key.Z.WithShift for uppercase 'Z'. /// public static Key Z => new (KeyCode.Z); /// /// The object for the Delete key. /// public static Key Delete => new (KeyCode.Delete); /// /// The object for the Cursor up key. /// public static Key CursorUp => new (KeyCode.CursorUp); /// /// The object for Cursor down key. /// public static Key CursorDown => new (KeyCode.CursorDown); /// /// The object for Cursor left key. /// public static Key CursorLeft => new (KeyCode.CursorLeft); /// /// The object for Cursor right key. /// public static Key CursorRight => new (KeyCode.CursorRight); /// /// The object for Page Up key. /// public static Key PageUp => new (KeyCode.PageUp); /// /// The object for Page Down key. /// public static Key PageDown => new (KeyCode.PageDown); /// /// The object for Home key. /// public static Key Home => new (KeyCode.Home); /// /// The object for End key. /// public static Key End => new (KeyCode.End); /// /// The object for Insert Character key. /// public static Key InsertChar => new (KeyCode.Insert); /// /// The object for Delete Character key. /// public static Key DeleteChar => new (KeyCode.Delete); /// /// The object for Print Screen key. /// public static Key PrintScreen => new (KeyCode.PrintScreen); /// /// The object for F1 key. /// public static Key F1 => new (KeyCode.F1); /// /// The object for F2 key. /// public static Key F2 => new (KeyCode.F2); /// /// The object for F3 key. /// public static Key F3 => new (KeyCode.F3); /// /// The object for F4 key. /// public static Key F4 => new (KeyCode.F4); /// /// The object for F5 key. /// public static Key F5 => new (KeyCode.F5); /// /// The object for F6 key. /// public static Key F6 => new (KeyCode.F6); /// /// The object for F7 key. /// public static Key F7 => new (KeyCode.F7); /// /// The object for F8 key. /// public static Key F8 => new (KeyCode.F8); /// /// The object for F9 key. /// public static Key F9 => new (KeyCode.F9); /// /// The object for F10 key. /// public static Key F10 => new (KeyCode.F10); /// /// The object for F11 key. /// public static Key F11 => new (KeyCode.F11); /// /// The object for F12 key. /// public static Key F12 => new (KeyCode.F12); /// /// The object for F13 key. /// public static Key F13 => new (KeyCode.F13); /// /// The object for F14 key. /// public static Key F14 => new (KeyCode.F14); /// /// The object for F15 key. /// public static Key F15 => new (KeyCode.F15); /// /// The object for F16 key. /// public static Key F16 => new (KeyCode.F16); /// /// The object for F17 key. /// public static Key F17 => new (KeyCode.F17); /// /// The object for F18 key. /// public static Key F18 => new (KeyCode.F18); /// /// The object for F19 key. /// public static Key F19 => new (KeyCode.F19); /// /// The object for F20 key. /// public static Key F20 => new (KeyCode.F20); /// /// The object for F21 key. /// public static Key F21 => new (KeyCode.F21); /// /// The object for F22 key. /// public static Key F22 => new (KeyCode.F22); /// /// The object for F23 key. /// public static Key F23 => new (KeyCode.F23); /// /// The object for F24 key. /// public static Key F24 => new (KeyCode.F24); #endregion }