Key.cs 34 KB

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