#nullable enable
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
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; }
///
/// Copy constructor.
///
/// The Key to copy
public Key (Key key)
{
KeyCode = key.KeyCode;
Handled = key.Handled;
}
/// 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)
{
bool result = TryParse (str, out Key key);
if (!result)
{
throw new ArgumentException (@$"Invalid key string: {str}", nameof (str));
}
KeyCode = key.KeyCode;
}
///
/// 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);
///
/// Indicates if the current Key event has already been processed and the driver should stop notifying any other
/// event subscriber. It's important to set this value to true specially when updating any View's layout from inside
/// the
/// subscriber method.
///
public bool Handled { get; set; }
/// 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);
/// Gets a value indicating whether the Shift key was pressed.
/// if is shift; otherwise, .
public bool IsShift => (KeyCode & KeyCode.ShiftMask) != 0;
///
/// 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;
private readonly KeyCode _keyCode;
/// 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 => _keyCode;
init
{
#if DEBUG
if (GetIsKeyCodeAtoZ (value) && (value & KeyCode.Space) != 0)
{
throw new ArgumentException (@$"Invalid KeyCode: {value} is invalid.", nameof (value));
}
#endif
_keyCode = value;
}
}
///
/// Helper for removing a shift modifier from a .
///
/// var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel;
/// var AltDelete = ControlAltDelete.NoCtrl;
///
///
public Key NoAlt => new (this) { KeyCode = 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 NoCtrl => new (this) { KeyCode = 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 NoShift => new (this) { KeyCode = KeyCode & ~KeyCode.ShiftMask };
///
/// Helper for specifying a shifted .
///
/// var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel;
///
///
public Key WithAlt => new (this) { KeyCode = KeyCode | KeyCode.AltMask };
///
/// Helper for specifying a shifted .
///
/// var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel;
///
///
public Key WithCtrl => new (this) { KeyCode = KeyCode | KeyCode.CtrlMask };
///
/// Helper for specifying a shifted .
///
/// var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel;
///
///
public Key WithShift => new (this) { KeyCode = KeyCode | KeyCode.ShiftMask };
///
/// 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;
}
///
/// 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 (Rune);
}
// Extract the base key code
KeyCode 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 ((uint)(baseKey + 32));
case >= KeyCode.A and <= KeyCode.Z when key.HasFlag (KeyCode.ShiftMask):
return new ((uint)baseKey);
case > KeyCode.Null and < KeyCode.A:
return new ((uint)baseKey);
}
if (Enum.IsDefined (typeof (KeyCode), baseKey))
{
return default (Rune);
}
return new ((uint)baseKey);
}
#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) { return 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) { return (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) { return key.KeyCode; }
/// Cast to a .
///
public static implicit operator Key (KeyCode keyCode) { return new (keyCode); }
/// Cast to a .
/// See for more information.
///
public static implicit operator Key (char ch) { return new (ch); }
/// Cast to a .
/// See for more information.
///
public static implicit operator Key (string str) { return new (str); }
/// Cast a to a .
/// See for more information.
///
public static implicit operator string (Key key) { return key.ToString (); }
///
public override bool Equals (object? obj)
{
if (obj is Key other)
{
return other._keyCode == _keyCode && other.Handled == Handled;
}
return false;
}
bool IEquatable.Equals (Key? other) { return Equals (other); }
///
public override int GetHashCode () { return _keyCode.GetHashCode (); }
/// Compares two s for equality.
///
///
///
public static bool operator == (Key a, Key b) { return a!.Equals (b); }
/// Compares two s for not equality.
///
///
///
public static bool operator != (Key? a, Key? b) { return !a!.Equals (b); }
/// Compares two s for less-than.
///
///
///
public static bool operator < (Key a, Key b) { return a?.KeyCode < b?.KeyCode; }
/// Compares two s for greater-than.
///
///
///
public static bool operator > (Key a, Key b) { return a?.KeyCode > b?.KeyCode; }
/// Compares two s for greater-than-or-equal-to.
///
///
///
public static bool operator <= (Key a, Key b) { return a?.KeyCode <= b?.KeyCode; }
/// Compares two s for greater-than-or-equal-to.
///
///
///
public static bool operator >= (Key a, Key b) { return a?.KeyCode >= b?.KeyCode; }
#endregion Operators
#region String conversion
/// Pretty prints the Key.
///
public override string ToString () { return ToString (KeyCode, Separator); }
private static string GetKeyString (KeyCode key)
{
if (key is KeyCode.Null or KeyCode.SpecialMask)
{
return string.Empty;
}
// Extract the base key (removing modifier flags)
KeyCode 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) { return ToString (key, Separator); }
/// Formats a as a string.
/// The key to format.
/// The character to use as a separator between modifier keys 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)
KeyCode baseKey = key & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask;
// Extract and handle modifiers
var 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);
}
private 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;
}
private static readonly Dictionary _modifierDict =
new (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", "X", and "120" (Unicode codepoint).
///
/// The separator can be any character, not just (e.g. "Ctrl@Alt@X").
///
///
/// The parsed value.
/// A boolean value indicating whether parsing was successful.
///
public static bool TryParse (string text, out Key key)
{
if (string.IsNullOrEmpty (text))
{
key = Empty;
return true;
}
switch (text)
{
case "Ctrl":
key = KeyCode.CtrlMask;
return true;
case "Alt":
key = KeyCode.AltMask;
return true;
case "Shift":
key = KeyCode.ShiftMask;
return true;
}
key = null!;
Rune separator = Separator;
// Perhaps the separator was written using a different Key.Separator? Does the string
// start with "Ctrl", "Alt" or "Shift"? If so, get the char after the modifier string and use that as the separator.
if (text.StartsWith ("Ctrl", StringComparison.InvariantCultureIgnoreCase))
{
separator = (Rune)text [4];
}
else if (text.StartsWith ("Alt", StringComparison.InvariantCultureIgnoreCase))
{
separator = (Rune)text [3];
}
else if (text.StartsWith ("Shift", StringComparison.InvariantCultureIgnoreCase))
{
separator = (Rune)text [5];
}
else if (text.EndsWith ("Ctrl", StringComparison.InvariantCultureIgnoreCase))
{
separator = (Rune)text [^5];
}
else if (text.EndsWith ("Alt", StringComparison.InvariantCultureIgnoreCase))
{
separator = (Rune)text [^4];
}
else if (text.EndsWith ("Shift", StringComparison.InvariantCultureIgnoreCase))
{
separator = (Rune)text [^6];
}
// Split the string into parts using the set Separator
string [] parts = text.Split ((char)separator.Value);
if (parts.Length is > 4)
{
// Invalid
return false;
}
// e.g. "Ctrl++"
if ((Rune)text [^1] != separator && parts.Any (string.IsNullOrEmpty))
{
// Invalid
return false;
}
if ((Rune)text [^1] == separator)
{
parts [^1] = separator.Value.ToString ();
key = (char)separator.Value;
}
if (separator != Separator && (parts.Length is 1 || (key is { } && parts.Length is 2)))
{
parts = text.Split ((char)separator.Value);
if (parts.Length is 0 or > 4 || parts.Any (string.IsNullOrEmpty))
{
// Invalid
return false;
}
}
var modifiers = KeyCode.Null;
for (var index = 0; index < parts.Length; index++)
{
if (_modifierDict.TryGetValue (parts [index].ToLowerInvariant (), out KeyCode 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;
var 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,
NumberStyles.Integer,
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 (parsedKeyCode | KeyCode.ShiftMask);
return true;
}
key = new (parsedKeyCode | modifiers);
return true;
}
}
if (GetIsKeyCodeAtoZ (keyCode) && (keyCode & KeyCode.Space) != 0)
{
keyCode &= ~KeyCode.Space;
}
key = new (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 (parsedKeyCode | KeyCode.ShiftMask);
return true;
}
if (GetIsKeyCodeAtoZ (parsedKeyCode) && (parsedKeyCode & KeyCode.Space) != 0)
{
parsedKeyCode = parsedKeyCode & ~KeyCode.Space;
}
key = new (parsedKeyCode | modifiers);
return true;
}
}
// if it's a number int, treat it as a unicode value
if (int.TryParse (
partNotFound,
NumberStyles.Number,
CultureInfo.InvariantCulture,
out parsedInt
))
{
if (!Rune.IsValid (parsedInt))
{
return false;
}
if ((KeyCode)parsedInt is >= KeyCode.A and <= KeyCode.Z && modifiers == 0)
{
key = new ((KeyCode)parsedInt | KeyCode.ShiftMask);
return true;
}
key = new ((KeyCode)parsedInt);
return true;
}
if (!Enum.TryParse (partNotFound, true, out parsedKeyCode))
{
return false;
}
if (GetIsKeyCodeAtoZ (parsedKeyCode))
{
key = new (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
private static Rune _separator = new ('+');
/// Gets or sets the separator character used when parsing and printing Keys. E.g. Ctrl+A. The default is '+'.
[SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
public static Rune Separator
{
get => _separator;
set
{
if (_separator != value)
{
_separator = value == default (Rune) ? new ('+') : value;
}
}
}
}