HotKeyTests.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. using System.Text;
  2. using Xunit.Abstractions;
  3. namespace Terminal.Gui.ViewTests;
  4. public class HotKeyTests
  5. {
  6. private readonly ITestOutputHelper _output;
  7. public HotKeyTests (ITestOutputHelper output) { _output = output; }
  8. [Theory]
  9. [InlineData (KeyCode.A)]
  10. [InlineData (KeyCode.A | KeyCode.ShiftMask)]
  11. [InlineData (KeyCode.D1)]
  12. [InlineData (KeyCode.D1 | KeyCode.ShiftMask)] // '!'
  13. [InlineData ((KeyCode)'х')] // Cyrillic x
  14. [InlineData ((KeyCode)'你')] // Chinese ni
  15. public void AddKeyBindingsForHotKey_Sets (KeyCode key)
  16. {
  17. var view = new View ();
  18. view.HotKey = KeyCode.Z;
  19. Assert.Equal (string.Empty, view.Title);
  20. Assert.Equal (KeyCode.Z, view.HotKey);
  21. view.AddKeyBindingsForHotKey (KeyCode.Null, key);
  22. // Verify key bindings were set
  23. // As passed
  24. Command [] commands = view.KeyBindings.GetCommands (key);
  25. Assert.Contains (Command.Accept, commands);
  26. commands = view.KeyBindings.GetCommands (key | KeyCode.AltMask);
  27. Assert.Contains (Command.Accept, commands);
  28. KeyCode baseKey = key & ~KeyCode.ShiftMask;
  29. // If A...Z, with and without shift
  30. if (baseKey is >= KeyCode.A and <= KeyCode.Z)
  31. {
  32. commands = view.KeyBindings.GetCommands (key | KeyCode.ShiftMask);
  33. Assert.Contains (Command.Accept, commands);
  34. commands = view.KeyBindings.GetCommands (key & ~KeyCode.ShiftMask);
  35. Assert.Contains (Command.Accept, commands);
  36. commands = view.KeyBindings.GetCommands (key | KeyCode.AltMask);
  37. Assert.Contains (Command.Accept, commands);
  38. commands = view.KeyBindings.GetCommands ((key & ~KeyCode.ShiftMask) | KeyCode.AltMask);
  39. Assert.Contains (Command.Accept, commands);
  40. }
  41. else
  42. {
  43. // Non A..Z keys should not have shift bindings
  44. if (key.HasFlag (KeyCode.ShiftMask))
  45. {
  46. commands = view.KeyBindings.GetCommands (key & ~KeyCode.ShiftMask);
  47. Assert.Empty (commands);
  48. }
  49. else
  50. {
  51. commands = view.KeyBindings.GetCommands (key | KeyCode.ShiftMask);
  52. Assert.Empty (commands);
  53. }
  54. }
  55. }
  56. [Fact]
  57. public void Defaults ()
  58. {
  59. var view = new View ();
  60. Assert.Equal (string.Empty, view.Title);
  61. Assert.Equal (KeyCode.Null, view.HotKey);
  62. // Verify key bindings were set
  63. Command [] commands = view.KeyBindings.GetCommands (KeyCode.Null);
  64. Assert.Empty (commands);
  65. }
  66. [Theory]
  67. [InlineData (KeyCode.Null, true)] // non-shift
  68. [InlineData (KeyCode.ShiftMask, true)]
  69. [InlineData (KeyCode.AltMask, true)]
  70. [InlineData (KeyCode.ShiftMask | KeyCode.AltMask, true)]
  71. [InlineData (KeyCode.CtrlMask, false)]
  72. [InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask, false)]
  73. public void KeyPress_Runs_Default_HotKey_Command (KeyCode mask, bool expected)
  74. {
  75. var view = new View { HotKeySpecifier = (Rune)'^', Text = "^Test" };
  76. view.CanFocus = true;
  77. Assert.False (view.HasFocus);
  78. view.NewKeyDownEvent (new Key (KeyCode.T | mask));
  79. Assert.Equal (expected, view.HasFocus);
  80. }
  81. [Fact]
  82. public void ProcessKeyDown_Ignores_KeyBindings_Out_Of_Scope_SuperView ()
  83. {
  84. var view = new View ();
  85. view.KeyBindings.Add (KeyCode.A, Command.Default);
  86. view.InvokingKeyBindings += (s, e) => { Assert.Fail (); };
  87. var superView = new View ();
  88. superView.Add (view);
  89. var ke = new Key (KeyCode.A);
  90. superView.NewKeyDownEvent (ke);
  91. }
  92. [Fact]
  93. public void ProcessKeyDown_Invokes_HotKey_Command_With_SuperView ()
  94. {
  95. var view = new View { HotKeySpecifier = (Rune)'^', Text = "^Test" };
  96. var superView = new View ();
  97. superView.Add (view);
  98. view.CanFocus = true;
  99. Assert.False (view.HasFocus);
  100. var ke = new Key (KeyCode.T);
  101. superView.NewKeyDownEvent (ke);
  102. Assert.True (view.HasFocus);
  103. }
  104. [Fact]
  105. public void Set_RemovesOldKeyBindings ()
  106. {
  107. var view = new View ();
  108. view.HotKey = KeyCode.A;
  109. Assert.Equal (string.Empty, view.Title);
  110. Assert.Equal (KeyCode.A, view.HotKey);
  111. // Verify key bindings were set
  112. Command [] commands = view.KeyBindings.GetCommands (KeyCode.A);
  113. Assert.Contains (Command.Accept, commands);
  114. commands = view.KeyBindings.GetCommands (KeyCode.A | KeyCode.ShiftMask);
  115. Assert.Contains (Command.Accept, commands);
  116. commands = view.KeyBindings.GetCommands (KeyCode.A | KeyCode.AltMask);
  117. Assert.Contains (Command.Accept, commands);
  118. commands = view.KeyBindings.GetCommands (KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask);
  119. Assert.Contains (Command.Accept, commands);
  120. // Now set again
  121. view.HotKey = KeyCode.B;
  122. Assert.Equal (string.Empty, view.Title);
  123. Assert.Equal (KeyCode.B, view.HotKey);
  124. commands = view.KeyBindings.GetCommands (KeyCode.A);
  125. Assert.DoesNotContain (Command.Accept, commands);
  126. commands = view.KeyBindings.GetCommands (KeyCode.A | KeyCode.ShiftMask);
  127. Assert.DoesNotContain (Command.Accept, commands);
  128. commands = view.KeyBindings.GetCommands (KeyCode.A | KeyCode.AltMask);
  129. Assert.DoesNotContain (Command.Accept, commands);
  130. commands = view.KeyBindings.GetCommands (KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask);
  131. Assert.DoesNotContain (Command.Accept, commands);
  132. }
  133. [Theory]
  134. [InlineData (KeyCode.A)]
  135. [InlineData ((KeyCode)'a')]
  136. [InlineData (KeyCode.A | KeyCode.ShiftMask)]
  137. [InlineData (KeyCode.D1)]
  138. [InlineData (KeyCode.D1 | KeyCode.ShiftMask)]
  139. [InlineData ((KeyCode)'!')]
  140. [InlineData ((KeyCode)'х')] // Cyrillic x
  141. [InlineData ((KeyCode)'你')] // Chinese ni
  142. [InlineData ((KeyCode)'ö')] // German o umlaut
  143. [InlineData (KeyCode.Null)]
  144. public void Set_Sets_WithValidKey (KeyCode key)
  145. {
  146. var view = new View ();
  147. view.HotKey = key;
  148. Assert.Equal (key, view.HotKey);
  149. }
  150. [Theory]
  151. [InlineData (KeyCode.A)]
  152. [InlineData (KeyCode.A | KeyCode.ShiftMask)]
  153. [InlineData (KeyCode.D1)]
  154. [InlineData (KeyCode.D1 | KeyCode.ShiftMask)] // '!'
  155. [InlineData ((KeyCode)'х')] // Cyrillic x
  156. [InlineData ((KeyCode)'你')] // Chinese ni
  157. [InlineData ((KeyCode)'ö')] // German o umlaut
  158. public void Set_SetsKeyBindings (KeyCode key)
  159. {
  160. var view = new View ();
  161. view.HotKey = key;
  162. Assert.Equal (string.Empty, view.Title);
  163. Assert.Equal (key, view.HotKey);
  164. // Verify key bindings were set
  165. // As passed
  166. Command [] commands = view.KeyBindings.GetCommands (key);
  167. Assert.Contains (Command.Accept, commands);
  168. Key baseKey = ((Key)key).NoShift;
  169. // If A...Z, with and without shift
  170. if (baseKey.IsKeyCodeAtoZ)
  171. {
  172. commands = view.KeyBindings.GetCommands (((Key)key).WithShift);
  173. Assert.Contains (Command.Accept, commands);
  174. commands = view.KeyBindings.GetCommands (((Key)key).NoShift);
  175. Assert.Contains (Command.Accept, commands);
  176. commands = view.KeyBindings.GetCommands (((Key)key).WithAlt);
  177. Assert.Contains (Command.Accept, commands);
  178. commands = view.KeyBindings.GetCommands (((Key)key).NoShift.WithAlt);
  179. Assert.Contains (Command.Accept, commands);
  180. }
  181. else
  182. {
  183. // Non A..Z keys should not have shift bindings
  184. if (((Key)key).IsShift)
  185. {
  186. commands = view.KeyBindings.GetCommands (((Key)key).NoShift);
  187. Assert.Empty (commands);
  188. }
  189. else
  190. {
  191. commands = view.KeyBindings.GetCommands (((Key)key).WithShift);
  192. Assert.Empty (commands);
  193. }
  194. }
  195. }
  196. [Fact]
  197. public void Set_Throws_If_Modifiers_Are_Included ()
  198. {
  199. var view = new View ();
  200. // A..Z must be naked (Alt is assumed)
  201. view.HotKey = KeyCode.A | KeyCode.AltMask;
  202. Assert.Throws<ArgumentException> (() => view.HotKey = KeyCode.A | KeyCode.CtrlMask);
  203. Assert.Throws<ArgumentException> (
  204. () =>
  205. view.HotKey =
  206. KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask
  207. );
  208. // All others must not have Ctrl (Alt is assumed)
  209. view.HotKey = KeyCode.D1 | KeyCode.AltMask;
  210. Assert.Throws<ArgumentException> (() => view.HotKey = KeyCode.D1 | KeyCode.CtrlMask);
  211. Assert.Throws<ArgumentException> (
  212. () =>
  213. view.HotKey =
  214. KeyCode.D1 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask
  215. );
  216. // Shift is ok (e.g. this is '!')
  217. view.HotKey = KeyCode.D1 | KeyCode.ShiftMask;
  218. }
  219. [Theory]
  220. [InlineData (KeyCode.Delete)]
  221. [InlineData (KeyCode.Backspace)]
  222. [InlineData (KeyCode.Tab)]
  223. [InlineData (KeyCode.Enter)]
  224. [InlineData (KeyCode.Esc)]
  225. [InlineData (KeyCode.Space)]
  226. [InlineData (KeyCode.CursorLeft)]
  227. [InlineData (KeyCode.F1)]
  228. [InlineData (KeyCode.Null | KeyCode.ShiftMask)]
  229. public void Set_Throws_With_Invalid_Key (KeyCode key)
  230. {
  231. var view = new View ();
  232. Assert.Throws<ArgumentException> (() => view.HotKey = key);
  233. }
  234. [Theory]
  235. [InlineData ("Test", KeyCode.Null)]
  236. [InlineData ("^Test", KeyCode.T)]
  237. [InlineData ("T^est", KeyCode.E)]
  238. [InlineData ("Te^st", KeyCode.S)]
  239. [InlineData ("Tes^t", KeyCode.T)]
  240. [InlineData ("other", KeyCode.Null)]
  241. [InlineData ("oTher", KeyCode.Null)]
  242. [InlineData ("^Öther", (KeyCode)'Ö')]
  243. [InlineData ("^öther", (KeyCode)'ö')]
  244. // BUGBUG: '!' should be supported. Line 968 of TextFormatter filters on char.IsLetterOrDigit
  245. //[InlineData ("Test^!", (Key)'!')]
  246. public void Text_Change_Sets_HotKey (string text, KeyCode expectedHotKey)
  247. {
  248. var view = new View { HotKeySpecifier = new Rune ('^'), Text = "^Hello" };
  249. Assert.Equal (KeyCode.H, view.HotKey);
  250. view.Text = text;
  251. Assert.Equal (expectedHotKey, view.HotKey);
  252. }
  253. [Theory]
  254. [InlineData ("^Test")]
  255. public void Text_Empty_Sets_HotKey_To_Null (string text)
  256. {
  257. var view = new View { HotKeySpecifier = (Rune)'^', Text = text };
  258. Assert.Equal (text, view.Text);
  259. Assert.Equal (KeyCode.T, view.HotKey);
  260. view.Text = string.Empty;
  261. Assert.Equal ("", view.Text);
  262. Assert.Equal (KeyCode.Null, view.HotKey);
  263. }
  264. }