HotKeyTests.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421
  1. using System.Text;
  2. using UICatalog.Scenarios;
  3. using Xunit.Abstractions;
  4. namespace Terminal.Gui.ViewTests;
  5. public class HotKeyTests
  6. {
  7. private readonly ITestOutputHelper _output;
  8. public HotKeyTests (ITestOutputHelper output) { _output = output; }
  9. [Theory]
  10. [InlineData (KeyCode.A)]
  11. [InlineData (KeyCode.A | KeyCode.ShiftMask)]
  12. [InlineData (KeyCode.D1)]
  13. [InlineData (KeyCode.D1 | KeyCode.ShiftMask)] // '!'
  14. [InlineData ((KeyCode)'х')] // Cyrillic x
  15. [InlineData ((KeyCode)'你')] // Chinese ni
  16. public void AddKeyBindingsForHotKey_Sets (KeyCode key)
  17. {
  18. var view = new View ();
  19. view.HotKey = KeyCode.Z;
  20. Assert.Equal (string.Empty, view.Title);
  21. Assert.Equal (KeyCode.Z, view.HotKey);
  22. view.AddKeyBindingsForHotKey (KeyCode.Null, key);
  23. // Verify key bindings were set
  24. // As passed
  25. Command [] commands = view.HotKeyBindings.GetCommands (key);
  26. Assert.Contains (Command.HotKey, commands);
  27. commands = view.HotKeyBindings.GetCommands (key | KeyCode.AltMask);
  28. Assert.Contains (Command.HotKey, commands);
  29. KeyCode baseKey = key & ~KeyCode.ShiftMask;
  30. // If A...Z, with and without shift
  31. if (baseKey is >= KeyCode.A and <= KeyCode.Z)
  32. {
  33. commands = view.HotKeyBindings.GetCommands (key | KeyCode.ShiftMask);
  34. Assert.Contains (Command.HotKey, commands);
  35. commands = view.HotKeyBindings.GetCommands (key & ~KeyCode.ShiftMask);
  36. Assert.Contains (Command.HotKey, commands);
  37. commands = view.HotKeyBindings.GetCommands (key | KeyCode.AltMask);
  38. Assert.Contains (Command.HotKey, commands);
  39. commands = view.HotKeyBindings.GetCommands ((key & ~KeyCode.ShiftMask) | KeyCode.AltMask);
  40. Assert.Contains (Command.HotKey, commands);
  41. }
  42. else
  43. {
  44. // Non A..Z keys should not have shift bindings
  45. if (key.HasFlag (KeyCode.ShiftMask))
  46. {
  47. commands = view.HotKeyBindings.GetCommands (key & ~KeyCode.ShiftMask);
  48. Assert.Empty (commands);
  49. }
  50. else
  51. {
  52. commands = view.HotKeyBindings.GetCommands (key | KeyCode.ShiftMask);
  53. Assert.Empty (commands);
  54. }
  55. }
  56. }
  57. [Fact]
  58. public void AddKeyBindingsForHotKey_SetsBinding_Key ()
  59. {
  60. var view = new View ();
  61. view.HotKey = KeyCode.Z;
  62. Assert.Equal (string.Empty, view.Title);
  63. Assert.Equal (KeyCode.Z, view.HotKey);
  64. view.AddKeyBindingsForHotKey (view.HotKey, Key.A);
  65. view.HotKeyBindings.TryGet (Key.A, out var binding);
  66. Assert.Equal (Key.A, binding.Key);
  67. }
  68. [Fact]
  69. public void AddKeyBindingsForHotKey_SetsBinding_Data ()
  70. {
  71. var view = new View ();
  72. view.HotKey = KeyCode.Z;
  73. Assert.Equal (KeyCode.Z, view.HotKey);
  74. view.AddKeyBindingsForHotKey (view.HotKey, Key.A, "data");
  75. view.HotKeyBindings.TryGet (Key.A, out var binding);
  76. Assert.Equal ("data", binding.Data);
  77. }
  78. [Fact]
  79. public void Defaults ()
  80. {
  81. var view = new View ();
  82. Assert.Equal (string.Empty, view.Title);
  83. Assert.Equal (KeyCode.Null, view.HotKey);
  84. // Verify key bindings were set
  85. Command [] commands = view.KeyBindings.GetCommands (KeyCode.Null);
  86. Assert.Empty (commands);
  87. commands = view.HotKeyBindings.GetCommands (KeyCode.Null);
  88. Assert.Empty (commands);
  89. Assert.Empty (view.HotKeyBindings.GetBindings ());
  90. }
  91. [Theory]
  92. [InlineData (KeyCode.Null, true)] // non-shift
  93. [InlineData (KeyCode.ShiftMask, true)]
  94. [InlineData (KeyCode.AltMask, true)]
  95. [InlineData (KeyCode.ShiftMask | KeyCode.AltMask, true)]
  96. [InlineData (KeyCode.CtrlMask, false)]
  97. [InlineData (KeyCode.ShiftMask | KeyCode.CtrlMask, false)]
  98. public void NewKeyDownEvent_Runs_Default_HotKey_Command (KeyCode mask, bool expected)
  99. {
  100. var view = new View { HotKeySpecifier = (Rune)'^', Title = "^Test" };
  101. view.CanFocus = true;
  102. Assert.False (view.HasFocus);
  103. view.NewKeyDownEvent (KeyCode.T | mask);
  104. Assert.Equal (expected, view.HasFocus);
  105. }
  106. [Fact]
  107. public void NewKeyDownEvent_Ignores_Focus_KeyBindings_SuperView ()
  108. {
  109. var view = new View ();
  110. view.HotKeyBindings.Add (Key.A, Command.HotKey);
  111. view.KeyDownNotHandled += (s, e) => { Assert.Fail (); };
  112. var superView = new View ();
  113. superView.Add (view);
  114. var ke = Key.A;
  115. superView.NewKeyDownEvent (ke);
  116. }
  117. [Fact]
  118. public void NewKeyDownEvent_Honors_HotKey_KeyBindings_SuperView ()
  119. {
  120. var view = new View ();
  121. view.HotKeyBindings.Add (Key.A, Command.HotKey);
  122. bool hotKeyInvoked = false;
  123. view.HandlingHotKey += (s, e) => { hotKeyInvoked = true; };
  124. bool notHandled = false;
  125. view.KeyDownNotHandled += (s, e) => { notHandled = true; };
  126. var superView = new View ();
  127. superView.Add (view);
  128. var ke = Key.A;
  129. superView.NewKeyDownEvent (ke);
  130. Assert.False (notHandled);
  131. Assert.True (hotKeyInvoked);
  132. }
  133. [Fact]
  134. public void NewKeyDownEvent_InNewKeyDownEvent_Invokes_HotKey_Command_With_SuperView ()
  135. {
  136. var superView = new View ()
  137. {
  138. CanFocus = true
  139. };
  140. var view1 = new View
  141. {
  142. HotKeySpecifier = (Rune)'^',
  143. Title = "view^1",
  144. CanFocus = true
  145. };
  146. var view2 = new View
  147. {
  148. HotKeySpecifier = (Rune)'^',
  149. Title = "view^2",
  150. CanFocus = true
  151. };
  152. superView.Add (view1, view2);
  153. superView.SetFocus ();
  154. Assert.True (view1.HasFocus);
  155. var ke = Key.D2;
  156. superView.NewKeyDownEvent (ke);
  157. Assert.True (view2.HasFocus);
  158. }
  159. [Fact]
  160. public void Set_RemovesOldKeyBindings ()
  161. {
  162. var view = new View ();
  163. view.HotKey = KeyCode.A;
  164. Assert.Equal (string.Empty, view.Title);
  165. Assert.Equal (KeyCode.A, view.HotKey);
  166. // Verify key bindings were set
  167. Command [] commands = view.HotKeyBindings.GetCommands (KeyCode.A);
  168. Assert.Contains (Command.HotKey, commands);
  169. commands = view.HotKeyBindings.GetCommands (KeyCode.A | KeyCode.ShiftMask);
  170. Assert.Contains (Command.HotKey, commands);
  171. commands = view.HotKeyBindings.GetCommands (KeyCode.A | KeyCode.AltMask);
  172. Assert.Contains (Command.HotKey, commands);
  173. commands = view.HotKeyBindings.GetCommands (KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask);
  174. Assert.Contains (Command.HotKey, commands);
  175. // Now set again
  176. view.HotKey = KeyCode.B;
  177. Assert.Equal (string.Empty, view.Title);
  178. Assert.Equal (KeyCode.B, view.HotKey);
  179. commands = view.HotKeyBindings.GetCommands (KeyCode.A);
  180. Assert.DoesNotContain (Command.HotKey, commands);
  181. commands = view.HotKeyBindings.GetCommands (KeyCode.A | KeyCode.ShiftMask);
  182. Assert.DoesNotContain (Command.HotKey, commands);
  183. commands = view.HotKeyBindings.GetCommands (KeyCode.A | KeyCode.AltMask);
  184. Assert.DoesNotContain (Command.HotKey, commands);
  185. commands = view.HotKeyBindings.GetCommands (KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask);
  186. Assert.DoesNotContain (Command.HotKey, commands);
  187. }
  188. [Theory]
  189. [InlineData (KeyCode.A)]
  190. [InlineData (KeyCode.A | KeyCode.ShiftMask)]
  191. [InlineData (KeyCode.D1)]
  192. [InlineData (KeyCode.D1 | KeyCode.ShiftMask)]
  193. [InlineData ((KeyCode)'!')]
  194. [InlineData ((KeyCode)'х')] // Cyrillic x
  195. [InlineData ((KeyCode)'你')] // Chinese ni
  196. [InlineData ((KeyCode)'ö')] // German o umlaut
  197. [InlineData (KeyCode.Null)]
  198. public void Set_Sets_WithValidKey (KeyCode key)
  199. {
  200. var view = new View ();
  201. view.HotKey = key;
  202. Assert.Equal (key, view.HotKey);
  203. }
  204. [Theory]
  205. [InlineData (KeyCode.A)]
  206. [InlineData (KeyCode.A | KeyCode.ShiftMask)]
  207. [InlineData (KeyCode.D1)]
  208. [InlineData (KeyCode.D1 | KeyCode.ShiftMask)] // '!'
  209. [InlineData ((KeyCode)'х')] // Cyrillic x
  210. [InlineData ((KeyCode)'你')] // Chinese ni
  211. [InlineData ((KeyCode)'ö')] // German o umlaut
  212. public void Set_SetsKeyBindings (KeyCode key)
  213. {
  214. var view = new View ();
  215. view.HotKey = key;
  216. Assert.Equal (string.Empty, view.Title);
  217. Assert.Equal (key, view.HotKey);
  218. // Verify key bindings were set
  219. // As passed
  220. Command [] commands = view.HotKeyBindings.GetCommands (view.HotKey);
  221. Assert.Contains (Command.HotKey, commands);
  222. Key baseKey = view.HotKey.NoShift;
  223. // If A...Z, with and without shift
  224. if (baseKey.IsKeyCodeAtoZ)
  225. {
  226. commands = view.HotKeyBindings.GetCommands (view.HotKey.WithShift);
  227. Assert.Contains (Command.HotKey, commands);
  228. commands = view.HotKeyBindings.GetCommands (view.HotKey.NoShift);
  229. Assert.Contains (Command.HotKey, commands);
  230. commands = view.HotKeyBindings.GetCommands (view.HotKey.WithAlt);
  231. Assert.Contains (Command.HotKey, commands);
  232. commands = view.HotKeyBindings.GetCommands (view.HotKey.NoShift.WithAlt);
  233. Assert.Contains (Command.HotKey, commands);
  234. }
  235. else
  236. {
  237. // Non A..Z keys should not have shift bindings
  238. if (view.HotKey.IsShift)
  239. {
  240. commands = view.HotKeyBindings.GetCommands (view.HotKey.NoShift);
  241. Assert.Empty (commands);
  242. }
  243. else
  244. {
  245. commands = view.HotKeyBindings.GetCommands (view.HotKey.WithShift);
  246. Assert.Empty (commands);
  247. }
  248. }
  249. }
  250. [Fact]
  251. public void Set_Throws_If_Modifiers_Are_Included ()
  252. {
  253. var view = new View ();
  254. // A..Z must be naked (Alt is assumed)
  255. view.HotKey = Key.A.WithAlt;
  256. Assert.Throws<ArgumentException> (() => view.HotKey = Key.A.WithCtrl);
  257. Assert.Throws<ArgumentException> (
  258. () =>
  259. view.HotKey =
  260. KeyCode.A | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask
  261. );
  262. // All others must not have Ctrl (Alt is assumed)
  263. view.HotKey = Key.D1.WithAlt;
  264. Assert.Throws<ArgumentException> (() => view.HotKey = Key.D1.WithCtrl);
  265. Assert.Throws<ArgumentException> (
  266. () =>
  267. view.HotKey =
  268. KeyCode.D1 | KeyCode.ShiftMask | KeyCode.AltMask | KeyCode.CtrlMask
  269. );
  270. // Shift is ok (e.g. this is '!')
  271. view.HotKey = Key.D1.WithShift;
  272. }
  273. [Theory]
  274. [InlineData (KeyCode.Delete)]
  275. [InlineData (KeyCode.Backspace)]
  276. [InlineData (KeyCode.Tab)]
  277. [InlineData (KeyCode.Enter)]
  278. [InlineData (KeyCode.Esc)]
  279. [InlineData (KeyCode.Space)]
  280. [InlineData (KeyCode.CursorLeft)]
  281. [InlineData (KeyCode.F1)]
  282. [InlineData (KeyCode.Null | KeyCode.ShiftMask)]
  283. public void Set_Throws_With_Invalid_Key (KeyCode key)
  284. {
  285. var view = new View ();
  286. Assert.Throws<ArgumentException> (() => view.HotKey = key);
  287. }
  288. [Theory]
  289. [InlineData ("Test", KeyCode.Null)]
  290. [InlineData ("^Test", KeyCode.T)]
  291. [InlineData ("T^est", KeyCode.E)]
  292. [InlineData ("Te^st", KeyCode.S)]
  293. [InlineData ("Tes^t", KeyCode.T)]
  294. [InlineData ("other", KeyCode.Null)]
  295. [InlineData ("oTher", KeyCode.Null)]
  296. [InlineData ("^Öther", (KeyCode)'Ö')]
  297. [InlineData ("^öther", (KeyCode)'ö')]
  298. // BUGBUG: '!' should be supported. Line 968 of TextFormatter filters on char.IsLetterOrDigit
  299. //[InlineData ("Test^!", (Key)'!')]
  300. public void Title_Change_Sets_HotKey (string title, KeyCode expectedHotKey)
  301. {
  302. var view = new View { HotKeySpecifier = new Rune ('^'), Title = "^Hello" };
  303. Assert.Equal (KeyCode.H, view.HotKey);
  304. view.Title = title;
  305. Assert.Equal (expectedHotKey, view.HotKey);
  306. }
  307. [Theory]
  308. [InlineData ("^Test")]
  309. public void Title_Empty_Sets_HotKey_To_Null (string title)
  310. {
  311. var view = new View { HotKeySpecifier = (Rune)'^', Title = title };
  312. Assert.Equal (title, view.Title);
  313. Assert.Equal (KeyCode.T, view.HotKey);
  314. view.Title = string.Empty;
  315. Assert.Equal ("", view.Title);
  316. Assert.Equal (KeyCode.Null, view.HotKey);
  317. }
  318. [Fact]
  319. public void HotKey_Raises_HotKeyCommand ()
  320. {
  321. var hotKeyRaised = false;
  322. var acceptRaised = false;
  323. var selectRaised = false;
  324. Application.Top = new Toplevel ();
  325. var view = new View
  326. {
  327. CanFocus = true,
  328. HotKeySpecifier = new Rune ('_'),
  329. Title = "_Test"
  330. };
  331. Application.Top.Add (view);
  332. view.HandlingHotKey += (s, e) => hotKeyRaised = true;
  333. view.Accepting += (s, e) => acceptRaised = true;
  334. view.Selecting += (s, e) => selectRaised = true;
  335. Assert.Equal (KeyCode.T, view.HotKey);
  336. Assert.True (Application.RaiseKeyDownEvent (Key.T));
  337. Assert.True (hotKeyRaised);
  338. Assert.False (acceptRaised);
  339. Assert.False (selectRaised);
  340. hotKeyRaised = false;
  341. Assert.True (Application.RaiseKeyDownEvent (Key.T.WithAlt));
  342. Assert.True (hotKeyRaised);
  343. Assert.False (acceptRaised);
  344. Assert.False (selectRaised);
  345. hotKeyRaised = false;
  346. view.HotKey = KeyCode.E;
  347. Assert.True (Application.RaiseKeyDownEvent (Key.E.WithAlt));
  348. Assert.True (hotKeyRaised);
  349. Assert.False (acceptRaised);
  350. Assert.False (selectRaised);
  351. Application.Top.Dispose ();
  352. Application.ResetState (true);
  353. }
  354. }