Key.cs 34 KB

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