using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
using System.Text.Json.Serialization;
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.
///
///
///
///
///
[JsonConverter (typeof (KeyJsonConverter))]
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;
///
/// 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.
///
[JsonInclude] [JsonConverter (typeof (KeyCodeJsonConverter))]
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.
///
///
/// If the key pressed is a letter (a-z or A-Z), this will be the upper or lower case letter depending on whether the shift key is pressed.
/// If the key is outside of the range, this will be .
///
public Rune AsRune => ToRune (KeyCode);
///
/// Converts a to a .
///
///
/// If the key is a letter (a-z or A-Z), this will be the upper or lower case letter depending on whether the shift key is pressed.
/// If the key is outside of the range, this 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 (removing modifier flags)
var baseKey = key & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask;
switch (baseKey) {
case >= KeyCode.A and <= KeyCode.Z when !key.HasFlag (KeyCode.ShiftMask):
return new Rune ((char)(baseKey + 32));
case >= KeyCode.A and <= KeyCode.Z:
return new Rune ((char)baseKey);
case > KeyCode.Null and < KeyCode.A:
return new Rune ((char)baseKey);
}
if (Enum.IsDefined (typeof (KeyCode), baseKey)) {
return default;
}
return new Rune ((char)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 KeyCode is composed of a lower case letter from 'a' to 'z', independent of the shift key.
///
///
/// 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 is composed of a lower case letter from 'a' to 'z', independent of the shift key.
///
///
/// 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.
///
///
/// Uses .
///
///
public static explicit operator Rune (Key kea) => kea.AsRune;
///
/// Explicitly cast to a . The conversion is lossy.
///
///
public static explicit operator char (Key kea) => (char)kea.AsRune.Value;
///
/// Explicitly cast to a . The conversion is lossy.
///
///
public static explicit operator KeyCode (Key key) => key.KeyCode;
///
/// Cast to a .
///
///
public static implicit operator Key (KeyCode keyCode) => new (keyCode);
///
/// Cast to a .
///
///
public static implicit operator Key (char ch) => new ((KeyCode)ch);
///
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;
///
///
///
///
///
public static bool operator == (Key a, Key b) => a?.KeyCode == b?.KeyCode;
///
///
///
///
///
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 ((char)(key + 32)).ToString ();
}
if (key is >= KeyCode.Space and < KeyCode.A) {
return ((char)key).ToString ();
}
string keyName = Enum.GetName (typeof (KeyCode), key);
return !string.IsNullOrEmpty (keyName) ? keyName : ((char)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 || (key & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask) == 0) {
// 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));
}
}
string result = sb.ToString ();
result = TrimEndRune (result, separator);
return result;
}
static string TrimEndRune (string input, Rune runeToTrim)
{
// Convert the Rune to a string (which may be one or two chars)
string runeString = runeToTrim.ToString ();
if (input.EndsWith (runeString)) {
// Remove the rune from the end of the string
return input.Substring (0, input.Length - runeString.Length);
}
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.CtrlKey);
return true;
case "Alt":
key = new Key (KeyCode.AltKey);
return true;
case "Shift":
key = new Key (KeyCode.ShiftKey);
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 readonly Key Empty = new ();
///
/// The object for the Backspace key.
///
public static readonly Key Backspace = new (KeyCode.Backspace);
///
/// The object for the tab key (forwards tab key).
///
public static readonly Key Tab = new (KeyCode.Tab);
///
/// The object for the return key.
///
public static readonly Key Enter = new (KeyCode.Enter);
///
/// The object for the clear key.
///
public static readonly Key Clear = new (KeyCode.Clear);
///
/// The object for the Shift key.
///
public static readonly Key Shift = new (KeyCode.ShiftKey);
///
/// The object for the Ctrl key.
///
public static readonly Key Ctrl = new (KeyCode.CtrlKey);
///
/// The object for the Alt key.
///
public static readonly Key Alt = new (KeyCode.AltKey);
///
/// The object for the CapsLock key.
///
public static readonly Key CapsLock = new (KeyCode.CapsLock);
///
/// The object for the Escape key.
///
public static readonly Key Esc = new (KeyCode.Esc);
///
/// The object for the Space bar key.
///
public static readonly Key Space = new (KeyCode.Space);
///
/// The object for 0 key.
///
public static readonly Key D0 = new (KeyCode.D0);
///
/// The object for 1 key.
///
public static readonly Key D1 = new (KeyCode.D1);
///
/// The object for 2 key.
///
public static readonly Key D2 = new (KeyCode.D2);
///
/// The object for 3 key.
///
public static readonly Key D3 = new (KeyCode.D3);
///
/// The object for 4 key.
///
public static readonly Key D4 = new (KeyCode.D4);
///
/// The object for 5 key.
///
public static readonly Key D5 = new (KeyCode.D5);
///
/// The object for 6 key.
///
public static readonly Key D6 = new (KeyCode.D6);
///
/// The object for 7 key.
///
public static readonly Key D7 = new (KeyCode.D7);
///
/// The object for 8 key.
///
public static readonly Key D8 = new (KeyCode.D8);
///
/// The object for 9 key.
///
public static readonly Key D9 = new (KeyCode.D9);
///
/// The object for the A key (un-shifted). Use Key.A.WithShift for uppercase 'A'.
///
public static readonly Key A = new (KeyCode.A);
///
/// The object for the B key (un-shifted). Use Key.B.WithShift for uppercase 'B'.
///
public static readonly Key B = new (KeyCode.B);
///
/// The object for the C key (un-shifted). Use Key.C.WithShift for uppercase 'C'.
///
public static readonly Key C = new (KeyCode.C);
///
/// The object for the D key (un-shifted). Use Key.D.WithShift for uppercase 'D'.
///
public static readonly Key D = new (KeyCode.D);
///
/// The object for the E key (un-shifted). Use Key.E.WithShift for uppercase 'E'.
///
public static readonly Key E = new (KeyCode.E);
///
/// The object for the F key (un-shifted). Use Key.F.WithShift for uppercase 'F'.
///
public static readonly Key F = new (KeyCode.F);
///
/// The object for the G key (un-shifted). Use Key.G.WithShift for uppercase 'G'.
///
public static readonly Key G = new (KeyCode.G);
///
/// The object for the H key (un-shifted). Use Key.H.WithShift for uppercase 'H'.
///
public static readonly Key H = new (KeyCode.H);
///
/// The object for the I key (un-shifted). Use Key.I.WithShift for uppercase 'I'.
///
public static readonly Key I = new (KeyCode.I);
///
/// The object for the J key (un-shifted). Use Key.J.WithShift for uppercase 'J'.
///
public static readonly Key J = new (KeyCode.J);
///
/// The object for the K key (un-shifted). Use Key.K.WithShift for uppercase 'K'.
///
public static readonly Key K = new (KeyCode.K);
///
/// The object for the L key (un-shifted). Use Key.L.WithShift for uppercase 'L'.
///
public static readonly Key L = new (KeyCode.L);
///
/// The object for the M key (un-shifted). Use Key.M.WithShift for uppercase 'M'.
///
public static readonly Key M = new (KeyCode.M);
///
/// The object for the N key (un-shifted). Use Key.N.WithShift for uppercase 'N'.
///
public static readonly Key N = new (KeyCode.N);
///
/// The object for the O key (un-shifted). Use Key.O.WithShift for uppercase 'O'.
///
public static readonly Key O = new (KeyCode.O);
///
/// The object for the P key (un-shifted). Use Key.P.WithShift for uppercase 'P'.
///
public static readonly Key P = new (KeyCode.P);
///
/// The object for the Q key (un-shifted). Use Key.Q.WithShift for uppercase 'Q'.
///
public static readonly Key Q = new (KeyCode.Q);
///
/// The object for the R key (un-shifted). Use Key.R.WithShift for uppercase 'R'.
///
public static readonly Key R = new (KeyCode.R);
///
/// The object for the S key (un-shifted). Use Key.S.WithShift for uppercase 'S'.
///
public static readonly Key S = new (KeyCode.S);
///
/// The object for the T key (un-shifted). Use Key.T.WithShift for uppercase 'T'.
///
public static readonly Key T = new (KeyCode.T);
///
/// The object for the U key (un-shifted). Use Key.U.WithShift for uppercase 'U'.
///
public static readonly Key U = new (KeyCode.U);
///
/// The object for the V key (un-shifted). Use Key.V.WithShift for uppercase 'V'.
///
public static readonly Key V = new (KeyCode.V);
///
/// The object for the W key (un-shifted). Use Key.W.WithShift for uppercase 'W'.
///
public static readonly Key W = new (KeyCode.W);
///
/// The object for the X key (un-shifted). Use Key.X.WithShift for uppercase 'X'.
///
public static readonly Key X = new (KeyCode.X);
///
/// The object for the Y key (un-shifted). Use Key.Y.WithShift for uppercase 'Y'.
///
public static readonly Key Y = new (KeyCode.Y);
///
/// The object for the Z key (un-shifted). Use Key.Z.WithShift for uppercase 'Z'.
///
public static readonly Key Z = new (KeyCode.Z);
///
/// The object for the Delete key.
///
public static readonly Key Delete = new (KeyCode.Delete);
///
/// The object for the Cursor up key.
///
public static readonly Key CursorUp = new (KeyCode.CursorUp);
///
/// The object for Cursor down key.
///
public static readonly Key CursorDown = new (KeyCode.CursorDown);
///
/// The object for Cursor left key.
///
public static readonly Key CursorLeft = new (KeyCode.CursorLeft);
///
/// The object for Cursor right key.
///
public static readonly Key CursorRight = new (KeyCode.CursorRight);
///
/// The object for Page Up key.
///
public static readonly Key PageUp = new (KeyCode.PageUp);
///
/// The object for Page Down key.
///
public static readonly Key PageDown = new (KeyCode.PageDown);
///
/// The object for Home key.
///
public static readonly Key Home = new (KeyCode.Home);
///
/// The object for End key.
///
public static readonly Key End = new (KeyCode.End);
///
/// The object for Insert Character key.
///
public static readonly Key InsertChar = new (KeyCode.InsertChar);
///
/// The object for Delete Character key.
///
public static readonly Key DeleteChar = new (KeyCode.DeleteChar);
///
/// The object for Print Screen key.
///
public static readonly Key PrintScreen = new (KeyCode.PrintScreen);
///
/// The object for F1 key.
///
public static readonly Key F1 = new (KeyCode.F1);
///
/// The object for F2 key.
///
public static readonly Key F2 = new (KeyCode.F2);
///
/// The object for F3 key.
///
public static readonly Key F3 = new (KeyCode.F3);
///
/// The object for F4 key.
///
public static readonly Key F4 = new (KeyCode.F4);
///
/// The object for F5 key.
///
public static readonly Key F5 = new (KeyCode.F5);
///
/// The object for F6 key.
///
public static readonly Key F6 = new (KeyCode.F6);
///
/// The object for F7 key.
///
public static readonly Key F7 = new (KeyCode.F7);
///
/// The object for F8 key.
///
public static readonly Key F8 = new (KeyCode.F8);
///
/// The object for F9 key.
///
public static readonly Key F9 = new (KeyCode.F9);
///
/// The object for F10 key.
///
public static readonly Key F10 = new (KeyCode.F10);
///
/// The object for F11 key.
///
public static readonly Key F11 = new (KeyCode.F11);
///
/// The object for F12 key.
///
public static readonly Key F12 = new (KeyCode.F12);
///
/// The object for F13 key.
///
public static readonly Key F13 = new (KeyCode.F13);
///
/// The object for F14 key.
///
public static readonly Key F14 = new (KeyCode.F14);
///
/// The object for F15 key.
///
public static readonly Key F15 = new (KeyCode.F15);
///
/// The object for F16 key.
///
public static readonly Key F16 = new (KeyCode.F16);
///
/// The object for F17 key.
///
public static readonly Key F17 = new (KeyCode.F17);
///
/// The object for F18 key.
///
public static readonly Key F18 = new (KeyCode.F18);
///
/// The object for F19 key.
///
public static readonly Key F19 = new (KeyCode.F19);
///
/// The object for F20 key.
///
public static readonly Key F20 = new (KeyCode.F20);
///
/// The object for F21 key.
///
public static readonly Key F21 = new (KeyCode.F21);
///
/// The object for F22 key.
///
public static readonly Key F22 = new (KeyCode.F22);
///
/// The object for F23 key.
///
public static readonly Key F23 = new (KeyCode.F23);
///
/// The object for F24 key.
///
public static readonly Key F24 = new (KeyCode.F24);
#endregion
}