Key.cs 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics.CodeAnalysis;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Text.Json.Serialization;
  7. namespace Terminal.Gui;
  8. /// <summary>
  9. /// Provides an abstraction for common keyboard operations and state. Used for processing keyboard input and raising keyboard events.
  10. /// </summary>
  11. /// <remarks>
  12. /// <para>
  13. /// This class provides a high-level abstraction with helper methods and properties for common keyboard operations. Use this class
  14. /// instead of the <see cref="Terminal.Gui.KeyCode"/> enumeration for keyboard input whenever possible.
  15. /// </para>
  16. /// <para>
  17. ///
  18. /// </para>
  19. /// <para>
  20. /// The default value for <see cref="Key"/> is <see cref="KeyCode.Null"/> and can be tested using <see cref="Key.Empty"/>.
  21. /// </para>
  22. /// <para>
  23. /// <list type="table">
  24. /// <listheader>
  25. /// <term>Concept</term><description>Definition</description>
  26. /// </listheader>
  27. /// <item>
  28. /// <term>Testing Shift State</term>
  29. /// <description>
  30. /// The <c>Is</c> properties (<see cref="IsShift"/>,<see cref="IsCtrl"/>, <see cref="IsAlt"/>) test for shift state; whether the key press was modified by a shift key.
  31. /// </description>
  32. /// </item>
  33. /// <item>
  34. /// <term>Adding Shift State</term>
  35. /// <description>
  36. /// The <c>With</c> properties (<see cref="WithShift"/>,<see cref="WithCtrl"/>, <see cref="WithAlt"/>) return a copy of the Key with the shift modifier applied. This
  37. /// is useful for specifying a key that requires a shift modifier (e.g. <c>var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel;</c>.
  38. /// </description>
  39. /// </item>
  40. /// <item>
  41. /// <term>Removing Shift State</term>
  42. /// <description>
  43. /// The <c>No</c> properties (<see cref="NoShift"/>,<see cref="NoCtrl"/>, <see cref="NoAlt"/>) return a copy of the Key with the shift modifier removed. This
  44. /// is useful for specifying a key that does not require a shift modifier (e.g. <c>var ControlDelete = ControlAltDelete.NoCtrl;</c>.
  45. /// </description>
  46. /// </item>
  47. /// <item>
  48. /// <term>Encoding of A..Z</term>
  49. /// <description>
  50. /// Lowercase alpha keys are encoded (in <see cref="Key.KeyCode"/>) as values between 65 and 90 corresponding to
  51. /// the un-shifted A to Z keys on a keyboard. Properties are provided for these (e.g. <see cref="Key.A"/>, <see cref="Key.B"/>, etc.).
  52. /// Even though the encoded values are the same as the ASCII values for uppercase characters, these enum values represent *lowercase*, un-shifted characters.
  53. /// </description>
  54. /// </item>
  55. /// <item>
  56. /// <term>Persistence as strings</term>
  57. /// <description>
  58. /// Keys are persisted as <c>"[Modifiers]+[Key]</c>. For example <c>new Key(Key.Delete).WithAlt.WithDel</c> is persisted as <c>"Ctrl+Alt+Delete"</c>. See <see cref="ToString()"/>
  59. /// and <see cref="TryParse(string, out Terminal.Gui.Key)"/> for more information.
  60. /// </description>
  61. /// </item>
  62. /// </list>
  63. /// </para>
  64. /// </remarks>
  65. [JsonConverter (typeof (KeyJsonConverter))]
  66. public class Key : EventArgs, IEquatable<Key> {
  67. /// <summary>
  68. /// Constructs a new <see cref="Key"/>
  69. /// </summary>
  70. public Key () : this (KeyCode.Null) { }
  71. /// <summary>
  72. /// Constructs a new <see cref="Key"/> from the provided Key value
  73. /// </summary>
  74. /// <param name="k">The key</param>
  75. public Key (KeyCode k) => KeyCode = k;
  76. /// <summary>
  77. /// Indicates if the current Key event has already been processed and the driver should stop notifying any other event subscriber.
  78. /// Its important to set this value to true specially when updating any View's layout from inside the subscriber method.
  79. /// </summary>
  80. public bool Handled { get; set; } = false;
  81. /// <summary>
  82. /// The encoded key value.
  83. /// </summary>
  84. /// <para>
  85. /// IMPORTANT: Lowercase alpha keys are encoded (in <see cref="Gui.KeyCode"/>) as values between 65 and 90 corresponding to the un-shifted A to Z keys on a keyboard. Enum values
  86. /// are provided for these (e.g. <see cref="KeyCode.A"/>, <see cref="KeyCode.B"/>, etc.). Even though the values are the same as the ASCII
  87. /// values for uppercase characters, these enum values represent *lowercase*, un-shifted characters.
  88. /// </para>
  89. /// <remarks>
  90. /// This property is the backing data for the <see cref="Key"/>. It is a <see cref="KeyCode"/> enum value.
  91. /// </remarks>
  92. [JsonInclude] [JsonConverter (typeof (KeyCodeJsonConverter))]
  93. public KeyCode KeyCode { get; init; }
  94. /// <summary>
  95. /// Enables passing the key binding scope with the event. Default is <see cref="KeyBindingScope.Focused"/>.
  96. /// </summary>
  97. public KeyBindingScope Scope { get; set; } = KeyBindingScope.Focused;
  98. /// <summary>
  99. /// The key value as a Rune. This is the actual value of the key pressed, and is independent of the modifiers.
  100. /// </summary>
  101. /// <remarks>
  102. /// 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.
  103. /// If the key is outside of the <see cref="KeyCode.CharMask"/> range, this will be <see langword="default"/>.
  104. /// </remarks>
  105. public Rune AsRune => ToRune (KeyCode);
  106. /// <summary>
  107. /// Converts a <see cref="KeyCode"/> to a <see cref="Rune"/>.
  108. /// </summary>
  109. /// <remarks>
  110. /// 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.
  111. /// If the key is outside of the <see cref="KeyCode.CharMask"/> range, this will be <see langword="default"/>.
  112. /// </remarks>
  113. /// <param name="key"></param>
  114. /// <returns>The key converted to a rune. <see langword="default"/> if conversion is not possible.</returns>
  115. public static Rune ToRune (KeyCode key)
  116. {
  117. if (key is KeyCode.Null or KeyCode.SpecialMask || key.HasFlag (KeyCode.CtrlMask) || key.HasFlag (KeyCode.AltMask)) {
  118. return default;
  119. }
  120. // Extract the base key (removing modifier flags)
  121. var baseKey = key & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask;
  122. switch (baseKey) {
  123. case >= KeyCode.A and <= KeyCode.Z when !key.HasFlag (KeyCode.ShiftMask):
  124. return new Rune ((char)(baseKey + 32));
  125. case >= KeyCode.A and <= KeyCode.Z:
  126. return new Rune ((char)baseKey);
  127. case > KeyCode.Null and < KeyCode.A:
  128. return new Rune ((char)baseKey);
  129. }
  130. if (Enum.IsDefined (typeof (KeyCode), baseKey)) {
  131. return default;
  132. }
  133. return new Rune ((char)baseKey);
  134. }
  135. /// <summary>
  136. /// Gets a value indicating whether the Shift key was pressed.
  137. /// </summary>
  138. /// <value><see langword="true"/> if is shift; otherwise, <see langword="false"/>.</value>
  139. public bool IsShift => (KeyCode & KeyCode.ShiftMask) != 0;
  140. /// <summary>
  141. /// Gets a value indicating whether the Alt key was pressed (real or synthesized)
  142. /// </summary>
  143. /// <value><see langword="true"/> if is alternate; otherwise, <see langword="false"/>.</value>
  144. public bool IsAlt => (KeyCode & KeyCode.AltMask) != 0;
  145. /// <summary>
  146. /// Gets a value indicating whether the Ctrl key was pressed.
  147. /// </summary>
  148. /// <value><see langword="true"/> if is ctrl; otherwise, <see langword="false"/>.</value>
  149. public bool IsCtrl => (KeyCode & KeyCode.CtrlMask) != 0;
  150. /// <summary>
  151. /// Gets a value indicating whether the KeyCode is composed of a lower case letter from 'a' to 'z', independent of the shift key.
  152. /// </summary>
  153. /// <remarks>
  154. /// IMPORTANT: Lowercase alpha keys are encoded in <see cref="Key.KeyCode"/> as values between 65 and 90 corresponding to
  155. /// the un-shifted A to Z keys on a keyboard. Helper properties are provided these (e.g. <see cref="Key.A"/>, <see cref="Key.B"/>, etc.).
  156. /// Even though the values are the same as the ASCII values for uppercase characters, these enum values represent *lowercase*, un-shifted characters.
  157. /// </remarks>
  158. public bool IsKeyCodeAtoZ => GetIsKeyCodeAtoZ (KeyCode);
  159. /// <summary>
  160. /// Tests if a KeyCode is composed of a lower case letter from 'a' to 'z', independent of the shift key.
  161. /// </summary>
  162. /// <remarks>
  163. /// IMPORTANT: Lowercase alpha keys are encoded in <see cref="Key.KeyCode"/> as values between 65 and 90 corresponding to
  164. /// the un-shifted A to Z keys on a keyboard. Helper properties are provided these (e.g. <see cref="Key.A"/>, <see cref="Key.B"/>, etc.).
  165. /// Even though the values are the same as the ASCII values for uppercase characters, these enum values represent *lowercase*, un-shifted characters.
  166. /// </remarks>
  167. public static bool GetIsKeyCodeAtoZ (KeyCode keyCode)
  168. {
  169. if ((keyCode & KeyCode.AltMask) != 0 || (keyCode & KeyCode.CtrlMask) != 0) {
  170. return false;
  171. }
  172. if ((keyCode & ~KeyCode.Space & ~KeyCode.ShiftMask) is >= KeyCode.A and <= KeyCode.Z) {
  173. return true;
  174. }
  175. return (keyCode & KeyCode.CharMask) is >= KeyCode.A and <= KeyCode.Z;
  176. }
  177. /// <summary>
  178. /// Indicates whether the <see cref="Key"/> is valid or not. Invalid keys are <see cref="Key.Empty"/>,
  179. /// and keys with only shift modifiers.
  180. /// </summary>
  181. public bool IsValid => this != Empty && (NoAlt.NoShift.NoCtrl != Empty);
  182. /// <summary>
  183. /// Helper for specifying a shifted <see cref="Key"/>.
  184. /// <code>
  185. /// var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel;
  186. /// </code>
  187. /// </summary>
  188. public Key WithShift => new (KeyCode | KeyCode.ShiftMask);
  189. /// <summary>
  190. /// Helper for removing a shift modifier from a <see cref="Key"/>.
  191. /// <code>
  192. /// var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel;
  193. /// var AltDelete = ControlAltDelete.NoCtrl;
  194. /// </code>
  195. /// </summary>
  196. public Key NoShift => new (KeyCode & ~KeyCode.ShiftMask);
  197. /// <summary>
  198. /// Helper for specifying a shifted <see cref="Key"/>.
  199. /// <code>
  200. /// var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel;
  201. /// </code>
  202. /// </summary>
  203. public Key WithCtrl => new (KeyCode | KeyCode.CtrlMask);
  204. /// <summary>
  205. /// Helper for removing a shift modifier from a <see cref="Key"/>.
  206. /// <code>
  207. /// var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel;
  208. /// var AltDelete = ControlAltDelete.NoCtrl;
  209. /// </code>
  210. /// </summary>
  211. public Key NoCtrl => new (KeyCode & ~KeyCode.CtrlMask);
  212. /// <summary>
  213. /// Helper for specifying a shifted <see cref="Key"/>.
  214. /// <code>
  215. /// var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel;
  216. /// </code>
  217. /// </summary>
  218. public Key WithAlt => new (KeyCode | KeyCode.AltMask);
  219. /// <summary>
  220. /// Helper for removing a shift modifier from a <see cref="Key"/>.
  221. /// <code>
  222. /// var ControlAltDelete = new Key(Key.Delete).WithAlt.WithDel;
  223. /// var AltDelete = ControlAltDelete.NoCtrl;
  224. /// </code>
  225. /// </summary>
  226. public Key NoAlt => new (KeyCode & ~KeyCode.AltMask);
  227. #region Operators
  228. /// <summary>
  229. /// Explicitly cast a <see cref="Key"/> to a <see cref="Rune"/>. The conversion is lossy.
  230. /// </summary>
  231. /// <remarks>
  232. /// Uses <see cref="AsRune"/>.
  233. /// </remarks>
  234. /// <param name="kea"></param>
  235. public static explicit operator Rune (Key kea) => kea.AsRune;
  236. /// <summary>
  237. /// Explicitly cast <see cref="Key"/> to a <see langword="char"/>. The conversion is lossy.
  238. /// </summary>
  239. /// <param name="kea"></param>
  240. public static explicit operator char (Key kea) => (char)kea.AsRune.Value;
  241. /// <summary>
  242. /// Explicitly cast <see cref="Key"/> to a <see cref="KeyCode"/>. The conversion is lossy.
  243. /// </summary>
  244. /// <param name="key"></param>
  245. public static explicit operator KeyCode (Key key) => key.KeyCode;
  246. /// <summary>
  247. /// Cast <see cref="KeyCode"/> to a <see cref="Key"/>.
  248. /// </summary>
  249. /// <param name="keyCode"></param>
  250. public static implicit operator Key (KeyCode keyCode) => new (keyCode);
  251. /// <summary>
  252. /// Cast <see langword="char"/> to a <see cref="Key"/>.
  253. /// </summary>
  254. /// <param name="ch"></param>
  255. public static implicit operator Key (char ch) => new ((KeyCode)ch);
  256. /// <inheritdoc/>
  257. public override bool Equals (object obj) => obj is Key k && k.KeyCode == KeyCode;
  258. bool IEquatable<Key>.Equals (Key other) => Equals ((object)other);
  259. /// <inheritdoc/>
  260. public override int GetHashCode () => (int)KeyCode;
  261. /// <summary>
  262. /// </summary>
  263. /// <param name="a"></param>
  264. /// <param name="b"></param>
  265. /// <returns></returns>
  266. public static bool operator == (Key a, Key b) => a?.KeyCode == b?.KeyCode;
  267. /// <summary>
  268. /// </summary>
  269. /// <param name="a"></param>
  270. /// <param name="b"></param>
  271. /// <returns></returns>
  272. public static bool operator != (Key a, Key b) => a?.KeyCode != b?.KeyCode;
  273. /// <summary>
  274. /// Compares two <see cref="Key"/>s for less-than.
  275. /// </summary>
  276. /// <param name="a"></param>
  277. /// <param name="b"></param>
  278. /// <returns></returns>
  279. public static bool operator < (Key a, Key b) => a?.KeyCode < b?.KeyCode;
  280. /// <summary>
  281. /// Compares two <see cref="Key"/>s for greater-than.
  282. /// </summary>
  283. /// <param name="a"></param>
  284. /// <param name="b"></param>
  285. /// <returns></returns>
  286. public static bool operator > (Key a, Key b) => a?.KeyCode > b?.KeyCode;
  287. /// <summary>
  288. /// Compares two <see cref="Key"/>s for greater-than-or-equal-to.
  289. /// </summary>
  290. /// <param name="a"></param>
  291. /// <param name="b"></param>
  292. /// <returns></returns>
  293. public static bool operator <= (Key a, Key b) => a?.KeyCode <= b?.KeyCode;
  294. /// <summary>
  295. /// Compares two <see cref="Key"/>s for greater-than-or-equal-to.
  296. /// </summary>
  297. /// <param name="a"></param>
  298. /// <param name="b"></param>
  299. /// <returns></returns>
  300. public static bool operator >= (Key a, Key b) => a?.KeyCode >= b?.KeyCode;
  301. #endregion Operators
  302. #region String conversion
  303. /// <summary>
  304. /// Pretty prints the KeyEvent
  305. /// </summary>
  306. /// <returns></returns>
  307. public override string ToString () => ToString (KeyCode, (Rune)'+');
  308. static string GetKeyString (KeyCode key)
  309. {
  310. if (key is KeyCode.Null or KeyCode.SpecialMask) {
  311. return string.Empty;
  312. }
  313. // Extract the base key (removing modifier flags)
  314. var baseKey = key & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask;
  315. if (!key.HasFlag (KeyCode.ShiftMask) && baseKey is >= KeyCode.A and <= KeyCode.Z) {
  316. return ((char)(key + 32)).ToString ();
  317. }
  318. if (key is >= KeyCode.Space and < KeyCode.A) {
  319. return ((char)key).ToString ();
  320. }
  321. string keyName = Enum.GetName (typeof (KeyCode), key);
  322. return !string.IsNullOrEmpty (keyName) ? keyName : ((char)key).ToString ();
  323. }
  324. /// <summary>
  325. /// Formats a <see cref="KeyCode"/> as a string using the default separator of '+'
  326. /// </summary>
  327. /// <param name="key">The key to format.</param>
  328. /// <returns>The formatted string. If the key is a printable character, it will be returned as a string. Otherwise, the key name will be returned.</returns>
  329. public static string ToString (KeyCode key) => ToString (key, (Rune)'+');
  330. /// <summary>
  331. /// Formats a <see cref="KeyCode"/> as a string.
  332. /// </summary>
  333. /// <param name="key">The key to format.</param>
  334. /// <param name="separator">The character to use as a separator between modifier keys and and the key itself.</param>
  335. /// <returns>The formatted string. If the key is a printable character, it will be returned as a string. Otherwise, the key name will be returned.</returns>
  336. public static string ToString (KeyCode key, Rune separator)
  337. {
  338. if (key is KeyCode.Null || (key & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask) == 0) {
  339. // Same as Key.IsValid
  340. return @"Null";
  341. }
  342. var sb = new StringBuilder ();
  343. // Extract the base key (removing modifier flags)
  344. var baseKey = key & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask;
  345. // Extract and handle modifiers
  346. bool hasModifiers = false;
  347. if ((key & KeyCode.CtrlMask) != 0) {
  348. sb.Append ($"Ctrl{separator}");
  349. hasModifiers = true;
  350. }
  351. if ((key & KeyCode.AltMask) != 0) {
  352. sb.Append ($"Alt{separator}");
  353. hasModifiers = true;
  354. }
  355. if ((key & KeyCode.ShiftMask) != 0 && !GetIsKeyCodeAtoZ (key)) {
  356. sb.Append ($"Shift{separator}");
  357. hasModifiers = true;
  358. }
  359. // Handle special cases and modifiers on their own
  360. if (key != KeyCode.SpecialMask && (baseKey != KeyCode.Null || hasModifiers)) {
  361. if ((key & KeyCode.SpecialMask) != 0 && (baseKey & ~KeyCode.Space) is >= KeyCode.A and <= KeyCode.Z) {
  362. sb.Append (baseKey & ~KeyCode.Space);
  363. } else {
  364. // Append the actual key name
  365. sb.Append (GetKeyString (baseKey));
  366. }
  367. }
  368. string result = sb.ToString ();
  369. result = TrimEndRune (result, separator);
  370. return result;
  371. }
  372. static string TrimEndRune (string input, Rune runeToTrim)
  373. {
  374. // Convert the Rune to a string (which may be one or two chars)
  375. string runeString = runeToTrim.ToString ();
  376. if (input.EndsWith (runeString)) {
  377. // Remove the rune from the end of the string
  378. return input.Substring (0, input.Length - runeString.Length);
  379. }
  380. return input;
  381. }
  382. static readonly Dictionary<string, KeyCode> _modifierDict = new (comparer: StringComparer.InvariantCultureIgnoreCase) {
  383. { "Shift", KeyCode.ShiftMask },
  384. { "Ctrl", KeyCode.CtrlMask },
  385. { "Alt", KeyCode.AltMask }
  386. };
  387. /// <summary>
  388. /// Converts the provided string to a new <see cref="Key"/> instance.
  389. /// </summary>
  390. /// <param name="text">The text to analyze. Formats supported are
  391. /// "Ctrl+X", "Alt+X", "Shift+X", "Ctrl+Alt+X", "Ctrl+Shift+X", "Alt+Shift+X", "Ctrl+Alt+Shift+X", and "X".
  392. /// </param>
  393. /// <param name="key">The parsed value.</param>
  394. /// <returns>A boolean value indicating whether parsing was successful.</returns>
  395. /// <remarks>
  396. /// </remarks>
  397. public static bool TryParse (string text, [NotNullWhen (true)] out Key key)
  398. {
  399. if (string.IsNullOrEmpty (text)) {
  400. key = new Key (KeyCode.Null);
  401. return true;
  402. }
  403. key = null;
  404. // Split the string into parts
  405. string [] parts = text.Split ('+', '-');
  406. if (parts.Length is 0 or > 4 || parts.Any (string.IsNullOrEmpty)) {
  407. return false;
  408. }
  409. // if it's just a shift key
  410. if (parts.Length == 1) {
  411. switch (parts [0]) {
  412. case "Ctrl":
  413. key = new Key (KeyCode.CtrlKey);
  414. return true;
  415. case "Alt":
  416. key = new Key (KeyCode.AltKey);
  417. return true;
  418. case "Shift":
  419. key = new Key (KeyCode.ShiftKey);
  420. return true;
  421. }
  422. }
  423. var modifiers = KeyCode.Null;
  424. for (int index = 0; index < parts.Length; index++) {
  425. if (_modifierDict.TryGetValue (parts [index].ToLowerInvariant (), out var modifier)) {
  426. modifiers |= modifier;
  427. parts [index] = string.Empty; // eat it
  428. }
  429. }
  430. // we now have the modifiers
  431. string partNotFound = parts.FirstOrDefault (p => !string.IsNullOrEmpty (p), string.Empty);
  432. var parsedKeyCode = KeyCode.Null;
  433. int parsedInt = 0;
  434. if (partNotFound.Length == 1) {
  435. var keyCode = (KeyCode)partNotFound [0];
  436. // if it's a single digit int, treat it as such
  437. if (int.TryParse (partNotFound,
  438. System.Globalization.NumberStyles.Integer,
  439. System.Globalization.CultureInfo.InvariantCulture,
  440. out parsedInt)) {
  441. keyCode = (KeyCode)((int)KeyCode.D0 + parsedInt);
  442. } else if (Enum.TryParse (partNotFound, false, out parsedKeyCode)) {
  443. if (parsedKeyCode != KeyCode.Null) {
  444. if (parsedKeyCode is >= KeyCode.A and <= KeyCode.Z && modifiers == 0) {
  445. key = new Key (parsedKeyCode | KeyCode.ShiftMask);
  446. return true;
  447. }
  448. key = new Key ((KeyCode)parsedKeyCode | modifiers);
  449. return true;
  450. }
  451. }
  452. key = new Key (keyCode | modifiers);
  453. return true;
  454. }
  455. if (Enum.TryParse (partNotFound, true, out parsedKeyCode)) {
  456. if (parsedKeyCode != KeyCode.Null) {
  457. if (parsedKeyCode is >= KeyCode.A and <= KeyCode.Z && modifiers == 0) {
  458. key = new Key (parsedKeyCode | KeyCode.ShiftMask);
  459. return true;
  460. }
  461. key = new Key (parsedKeyCode | modifiers);
  462. return true;
  463. }
  464. }
  465. // if it's a number int, treat it as a unicode value
  466. if (int.TryParse (partNotFound,
  467. System.Globalization.NumberStyles.Number,
  468. System.Globalization.CultureInfo.InvariantCulture, out parsedInt)) {
  469. if (!Rune.IsValid (parsedInt)) {
  470. return false;
  471. }
  472. if ((KeyCode)parsedInt is >= KeyCode.A and <= KeyCode.Z && modifiers == 0) {
  473. key = new Key ((KeyCode)parsedInt | KeyCode.ShiftMask);
  474. return true;
  475. }
  476. key = new Key ((KeyCode)parsedInt);
  477. return true;
  478. }
  479. if (!Enum.TryParse (partNotFound, true, out parsedKeyCode)) {
  480. return false;
  481. }
  482. if (GetIsKeyCodeAtoZ (parsedKeyCode)) {
  483. key = new Key (parsedKeyCode | modifiers & ~KeyCode.Space);
  484. return true;
  485. }
  486. return false;
  487. }
  488. #endregion
  489. #region Standard Key Definitions
  490. /// <summary>
  491. /// An uninitialized The <see cref="Key"/> object.
  492. /// </summary>
  493. public new static readonly Key Empty = new ();
  494. /// <summary>
  495. /// The <see cref="Key"/> object for the Backspace key.
  496. /// </summary>
  497. public static readonly Key Backspace = new (KeyCode.Backspace);
  498. /// <summary>
  499. /// The <see cref="Key"/> object for the tab key (forwards tab key).
  500. /// </summary>
  501. public static readonly Key Tab = new (KeyCode.Tab);
  502. /// <summary>
  503. /// The <see cref="Key"/> object for the return key.
  504. /// </summary>
  505. public static readonly Key Enter = new (KeyCode.Enter);
  506. /// <summary>
  507. /// The <see cref="Key"/> object for the clear key.
  508. /// </summary>
  509. public static readonly Key Clear = new (KeyCode.Clear);
  510. /// <summary>
  511. /// The <see cref="Key"/> object for the Shift key.
  512. /// </summary>
  513. public static readonly Key Shift = new (KeyCode.ShiftKey);
  514. /// <summary>
  515. /// The <see cref="Key"/> object for the Ctrl key.
  516. /// </summary>
  517. public static readonly Key Ctrl = new (KeyCode.CtrlKey);
  518. /// <summary>
  519. /// The <see cref="Key"/> object for the Alt key.
  520. /// </summary>
  521. public static readonly Key Alt = new (KeyCode.AltKey);
  522. /// <summary>
  523. /// The <see cref="Key"/> object for the CapsLock key.
  524. /// </summary>
  525. public static readonly Key CapsLock = new (KeyCode.CapsLock);
  526. /// <summary>
  527. /// The <see cref="Key"/> object for the Escape key.
  528. /// </summary>
  529. public static readonly Key Esc = new (KeyCode.Esc);
  530. /// <summary>
  531. /// The <see cref="Key"/> object for the Space bar key.
  532. /// </summary>
  533. public static readonly Key Space = new (KeyCode.Space);
  534. /// <summary>
  535. /// The <see cref="Key"/> object for 0 key.
  536. /// </summary>
  537. public static readonly Key D0 = new (KeyCode.D0);
  538. /// <summary>
  539. /// The <see cref="Key"/> object for 1 key.
  540. /// </summary>
  541. public static readonly Key D1 = new (KeyCode.D1);
  542. /// <summary>
  543. /// The <see cref="Key"/> object for 2 key.
  544. /// </summary>
  545. public static readonly Key D2 = new (KeyCode.D2);
  546. /// <summary>
  547. /// The <see cref="Key"/> object for 3 key.
  548. /// </summary>
  549. public static readonly Key D3 = new (KeyCode.D3);
  550. /// <summary>
  551. /// The <see cref="Key"/> object for 4 key.
  552. /// </summary>
  553. public static readonly Key D4 = new (KeyCode.D4);
  554. /// <summary>
  555. /// The <see cref="Key"/> object for 5 key.
  556. /// </summary>
  557. public static readonly Key D5 = new (KeyCode.D5);
  558. /// <summary>
  559. /// The <see cref="Key"/> object for 6 key.
  560. /// </summary>
  561. public static readonly Key D6 = new (KeyCode.D6);
  562. /// <summary>
  563. /// The <see cref="Key"/> object for 7 key.
  564. /// </summary>
  565. public static readonly Key D7 = new (KeyCode.D7);
  566. /// <summary>
  567. /// The <see cref="Key"/> object for 8 key.
  568. /// </summary>
  569. public static readonly Key D8 = new (KeyCode.D8);
  570. /// <summary>
  571. /// The <see cref="Key"/> object for 9 key.
  572. /// </summary>
  573. public static readonly Key D9 = new (KeyCode.D9);
  574. /// <summary>
  575. /// The <see cref="Key"/> object for the A key (un-shifted). Use <c>Key.A.WithShift</c> for uppercase 'A'.
  576. /// </summary>
  577. public static readonly Key A = new (KeyCode.A);
  578. /// <summary>
  579. /// The <see cref="Key"/> object for the B key (un-shifted). Use <c>Key.B.WithShift</c> for uppercase 'B'.
  580. /// </summary>
  581. public static readonly Key B = new (KeyCode.B);
  582. /// <summary>
  583. /// The <see cref="Key"/> object for the C key (un-shifted). Use <c>Key.C.WithShift</c> for uppercase 'C'.
  584. /// </summary>
  585. public static readonly Key C = new (KeyCode.C);
  586. /// <summary>
  587. /// The <see cref="Key"/> object for the D key (un-shifted). Use <c>Key.D.WithShift</c> for uppercase 'D'.
  588. /// </summary>
  589. public static readonly Key D = new (KeyCode.D);
  590. /// <summary>
  591. /// The <see cref="Key"/> object for the E key (un-shifted). Use <c>Key.E.WithShift</c> for uppercase 'E'.
  592. /// </summary>
  593. public static readonly Key E = new (KeyCode.E);
  594. /// <summary>
  595. /// The <see cref="Key"/> object for the F key (un-shifted). Use <c>Key.F.WithShift</c> for uppercase 'F'.
  596. /// </summary>
  597. public static readonly Key F = new (KeyCode.F);
  598. /// <summary>
  599. /// The <see cref="Key"/> object for the G key (un-shifted). Use <c>Key.G.WithShift</c> for uppercase 'G'.
  600. /// </summary>
  601. public static readonly Key G = new (KeyCode.G);
  602. /// <summary>
  603. /// The <see cref="Key"/> object for the H key (un-shifted). Use <c>Key.H.WithShift</c> for uppercase 'H'.
  604. /// </summary>
  605. public static readonly Key H = new (KeyCode.H);
  606. /// <summary>
  607. /// The <see cref="Key"/> object for the I key (un-shifted). Use <c>Key.I.WithShift</c> for uppercase 'I'.
  608. /// </summary>
  609. public static readonly Key I = new (KeyCode.I);
  610. /// <summary>
  611. /// The <see cref="Key"/> object for the J key (un-shifted). Use <c>Key.J.WithShift</c> for uppercase 'J'.
  612. /// </summary>
  613. public static readonly Key J = new (KeyCode.J);
  614. /// <summary>
  615. /// The <see cref="Key"/> object for the K key (un-shifted). Use <c>Key.K.WithShift</c> for uppercase 'K'.
  616. /// </summary>
  617. public static readonly Key K = new (KeyCode.K);
  618. /// <summary>
  619. /// The <see cref="Key"/> object for the L key (un-shifted). Use <c>Key.L.WithShift</c> for uppercase 'L'.
  620. /// </summary>
  621. public static readonly Key L = new (KeyCode.L);
  622. /// <summary>
  623. /// The <see cref="Key"/> object for the M key (un-shifted). Use <c>Key.M.WithShift</c> for uppercase 'M'.
  624. /// </summary>
  625. public static readonly Key M = new (KeyCode.M);
  626. /// <summary>
  627. /// The <see cref="Key"/> object for the N key (un-shifted). Use <c>Key.N.WithShift</c> for uppercase 'N'.
  628. /// </summary>
  629. public static readonly Key N = new (KeyCode.N);
  630. /// <summary>
  631. /// The <see cref="Key"/> object for the O key (un-shifted). Use <c>Key.O.WithShift</c> for uppercase 'O'.
  632. /// </summary>
  633. public static readonly Key O = new (KeyCode.O);
  634. /// <summary>
  635. /// The <see cref="Key"/> object for the P key (un-shifted). Use <c>Key.P.WithShift</c> for uppercase 'P'.
  636. /// </summary>
  637. public static readonly Key P = new (KeyCode.P);
  638. /// <summary>
  639. /// The <see cref="Key"/> object for the Q key (un-shifted). Use <c>Key.Q.WithShift</c> for uppercase 'Q'.
  640. /// </summary>
  641. public static readonly Key Q = new (KeyCode.Q);
  642. /// <summary>
  643. /// The <see cref="Key"/> object for the R key (un-shifted). Use <c>Key.R.WithShift</c> for uppercase 'R'.
  644. /// </summary>
  645. public static readonly Key R = new (KeyCode.R);
  646. /// <summary>
  647. /// The <see cref="Key"/> object for the S key (un-shifted). Use <c>Key.S.WithShift</c> for uppercase 'S'.
  648. /// </summary>
  649. public static readonly Key S = new (KeyCode.S);
  650. /// <summary>
  651. /// The <see cref="Key"/> object for the T key (un-shifted). Use <c>Key.T.WithShift</c> for uppercase 'T'.
  652. /// </summary>
  653. public static readonly Key T = new (KeyCode.T);
  654. /// <summary>
  655. /// The <see cref="Key"/> object for the U key (un-shifted). Use <c>Key.U.WithShift</c> for uppercase 'U'.
  656. /// </summary>
  657. public static readonly Key U = new (KeyCode.U);
  658. /// <summary>
  659. /// The <see cref="Key"/> object for the V key (un-shifted). Use <c>Key.V.WithShift</c> for uppercase 'V'.
  660. /// </summary>
  661. public static readonly Key V = new (KeyCode.V);
  662. /// <summary>
  663. /// The <see cref="Key"/> object for the W key (un-shifted). Use <c>Key.W.WithShift</c> for uppercase 'W'.
  664. /// </summary>
  665. public static readonly Key W = new (KeyCode.W);
  666. /// <summary>
  667. /// The <see cref="Key"/> object for the X key (un-shifted). Use <c>Key.X.WithShift</c> for uppercase 'X'.
  668. /// </summary>
  669. public static readonly Key X = new (KeyCode.X);
  670. /// <summary>
  671. /// The <see cref="Key"/> object for the Y key (un-shifted). Use <c>Key.Y.WithShift</c> for uppercase 'Y'.
  672. /// </summary>
  673. public static readonly Key Y = new (KeyCode.Y);
  674. /// <summary>
  675. /// The <see cref="Key"/> object for the Z key (un-shifted). Use <c>Key.Z.WithShift</c> for uppercase 'Z'.
  676. /// </summary>
  677. public static readonly Key Z = new (KeyCode.Z);
  678. /// <summary>
  679. /// The <see cref="Key"/> object for the Delete key.
  680. /// </summary>
  681. public static readonly Key Delete = new (KeyCode.Delete);
  682. /// <summary>
  683. /// The <see cref="Key"/> object for the Cursor up key.
  684. /// </summary>
  685. public static readonly Key CursorUp = new (KeyCode.CursorUp);
  686. /// <summary>
  687. /// The <see cref="Key"/> object for Cursor down key.
  688. /// </summary>
  689. public static readonly Key CursorDown = new (KeyCode.CursorDown);
  690. /// <summary>
  691. /// The <see cref="Key"/> object for Cursor left key.
  692. /// </summary>
  693. public static readonly Key CursorLeft = new (KeyCode.CursorLeft);
  694. /// <summary>
  695. /// The <see cref="Key"/> object for Cursor right key.
  696. /// </summary>
  697. public static readonly Key CursorRight = new (KeyCode.CursorRight);
  698. /// <summary>
  699. /// The <see cref="Key"/> object for Page Up key.
  700. /// </summary>
  701. public static readonly Key PageUp = new (KeyCode.PageUp);
  702. /// <summary>
  703. /// The <see cref="Key"/> object for Page Down key.
  704. /// </summary>
  705. public static readonly Key PageDown = new (KeyCode.PageDown);
  706. /// <summary>
  707. /// The <see cref="Key"/> object for Home key.
  708. /// </summary>
  709. public static readonly Key Home = new (KeyCode.Home);
  710. /// <summary>
  711. /// The <see cref="Key"/> object for End key.
  712. /// </summary>
  713. public static readonly Key End = new (KeyCode.End);
  714. /// <summary>
  715. /// The <see cref="Key"/> object for Insert Character key.
  716. /// </summary>
  717. public static readonly Key InsertChar = new (KeyCode.InsertChar);
  718. /// <summary>
  719. /// The <see cref="Key"/> object for Delete Character key.
  720. /// </summary>
  721. public static readonly Key DeleteChar = new (KeyCode.DeleteChar);
  722. /// <summary>
  723. /// The <see cref="Key"/> object for Print Screen key.
  724. /// </summary>
  725. public static readonly Key PrintScreen = new (KeyCode.PrintScreen);
  726. /// <summary>
  727. /// The <see cref="Key"/> object for F1 key.
  728. /// </summary>
  729. public static readonly Key F1 = new (KeyCode.F1);
  730. /// <summary>
  731. /// The <see cref="Key"/> object for F2 key.
  732. /// </summary>
  733. public static readonly Key F2 = new (KeyCode.F2);
  734. /// <summary>
  735. /// The <see cref="Key"/> object for F3 key.
  736. /// </summary>
  737. public static readonly Key F3 = new (KeyCode.F3);
  738. /// <summary>
  739. /// The <see cref="Key"/> object for F4 key.
  740. /// </summary>
  741. public static readonly Key F4 = new (KeyCode.F4);
  742. /// <summary>
  743. /// The <see cref="Key"/> object for F5 key.
  744. /// </summary>
  745. public static readonly Key F5 = new (KeyCode.F5);
  746. /// <summary>
  747. /// The <see cref="Key"/> object for F6 key.
  748. /// </summary>
  749. public static readonly Key F6 = new (KeyCode.F6);
  750. /// <summary>
  751. /// The <see cref="Key"/> object for F7 key.
  752. /// </summary>
  753. public static readonly Key F7 = new (KeyCode.F7);
  754. /// <summary>
  755. /// The <see cref="Key"/> object for F8 key.
  756. /// </summary>
  757. public static readonly Key F8 = new (KeyCode.F8);
  758. /// <summary>
  759. /// The <see cref="Key"/> object for F9 key.
  760. /// </summary>
  761. public static readonly Key F9 = new (KeyCode.F9);
  762. /// <summary>
  763. /// The <see cref="Key"/> object for F10 key.
  764. /// </summary>
  765. public static readonly Key F10 = new (KeyCode.F10);
  766. /// <summary>
  767. /// The <see cref="Key"/> object for F11 key.
  768. /// </summary>
  769. public static readonly Key F11 = new (KeyCode.F11);
  770. /// <summary>
  771. /// The <see cref="Key"/> object for F12 key.
  772. /// </summary>
  773. public static readonly Key F12 = new (KeyCode.F12);
  774. /// <summary>
  775. /// The <see cref="Key"/> object for F13 key.
  776. /// </summary>
  777. public static readonly Key F13 = new (KeyCode.F13);
  778. /// <summary>
  779. /// The <see cref="Key"/> object for F14 key.
  780. /// </summary>
  781. public static readonly Key F14 = new (KeyCode.F14);
  782. /// <summary>
  783. /// The <see cref="Key"/> object for F15 key.
  784. /// </summary>
  785. public static readonly Key F15 = new (KeyCode.F15);
  786. /// <summary>
  787. /// The <see cref="Key"/> object for F16 key.
  788. /// </summary>
  789. public static readonly Key F16 = new (KeyCode.F16);
  790. /// <summary>
  791. /// The <see cref="Key"/> object for F17 key.
  792. /// </summary>
  793. public static readonly Key F17 = new (KeyCode.F17);
  794. /// <summary>
  795. /// The <see cref="Key"/> object for F18 key.
  796. /// </summary>
  797. public static readonly Key F18 = new (KeyCode.F18);
  798. /// <summary>
  799. /// The <see cref="Key"/> object for F19 key.
  800. /// </summary>
  801. public static readonly Key F19 = new (KeyCode.F19);
  802. /// <summary>
  803. /// The <see cref="Key"/> object for F20 key.
  804. /// </summary>
  805. public static readonly Key F20 = new (KeyCode.F20);
  806. /// <summary>
  807. /// The <see cref="Key"/> object for F21 key.
  808. /// </summary>
  809. public static readonly Key F21 = new (KeyCode.F21);
  810. /// <summary>
  811. /// The <see cref="Key"/> object for F22 key.
  812. /// </summary>
  813. public static readonly Key F22 = new (KeyCode.F22);
  814. /// <summary>
  815. /// The <see cref="Key"/> object for F23 key.
  816. /// </summary>
  817. public static readonly Key F23 = new (KeyCode.F23);
  818. /// <summary>
  819. /// The <see cref="Key"/> object for F24 key.
  820. /// </summary>
  821. public static readonly Key F24 = new (KeyCode.F24);
  822. #endregion
  823. }