KeyboardTests.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. using Xunit.Abstractions;
  2. namespace Terminal.Gui.ApplicationTests;
  3. public class KeyboardTests
  4. {
  5. private readonly ITestOutputHelper _output;
  6. public KeyboardTests (ITestOutputHelper output)
  7. {
  8. _output = output;
  9. #if DEBUG_IDISPOSABLE
  10. Responder.Instances.Clear ();
  11. RunState.Instances.Clear ();
  12. #endif
  13. }
  14. [Fact]
  15. public void AlternateForwardKey_AlternateBackwardKey_Tests ()
  16. {
  17. Application.Init (new FakeDriver ());
  18. Toplevel top = Application.Top;
  19. var w1 = new Window ();
  20. var v1 = new TextField ();
  21. var v2 = new TextView ();
  22. w1.Add (v1, v2);
  23. var w2 = new Window ();
  24. var v3 = new CheckBox ();
  25. var v4 = new Button ();
  26. w2.Add (v3, v4);
  27. top.Add (w1, w2);
  28. Application.Iteration += (s, a) =>
  29. {
  30. Assert.True (v1.HasFocus);
  31. // Using default keys.
  32. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  33. Assert.True (v2.HasFocus);
  34. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  35. Assert.True (v3.HasFocus);
  36. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  37. Assert.True (v4.HasFocus);
  38. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  39. Assert.True (v1.HasFocus);
  40. top.NewKeyDownEvent (Key.Tab.WithShift.WithCtrl);
  41. Assert.True (v4.HasFocus);
  42. top.NewKeyDownEvent (Key.Tab.WithShift.WithCtrl);
  43. Assert.True (v3.HasFocus);
  44. top.NewKeyDownEvent (Key.Tab.WithShift.WithCtrl);
  45. Assert.True (v2.HasFocus);
  46. top.NewKeyDownEvent (Key.Tab.WithShift.WithCtrl);
  47. Assert.True (v1.HasFocus);
  48. top.NewKeyDownEvent (Key.PageDown.WithCtrl);
  49. Assert.True (v2.HasFocus);
  50. top.NewKeyDownEvent (Key.PageDown.WithCtrl);
  51. Assert.True (v3.HasFocus);
  52. top.NewKeyDownEvent (Key.PageDown.WithCtrl);
  53. Assert.True (v4.HasFocus);
  54. top.NewKeyDownEvent (Key.PageDown.WithCtrl);
  55. Assert.True (v1.HasFocus);
  56. top.NewKeyDownEvent (Key.PageUp.WithCtrl);
  57. Assert.True (v4.HasFocus);
  58. top.NewKeyDownEvent (Key.PageUp.WithCtrl);
  59. Assert.True (v3.HasFocus);
  60. top.NewKeyDownEvent (Key.PageUp.WithCtrl);
  61. Assert.True (v2.HasFocus);
  62. top.NewKeyDownEvent (Key.PageUp.WithCtrl);
  63. Assert.True (v1.HasFocus);
  64. // Using another's alternate keys.
  65. Application.AlternateForwardKey = Key.F7;
  66. Application.AlternateBackwardKey = Key.F6;
  67. top.NewKeyDownEvent (Key.F7);
  68. Assert.True (v2.HasFocus);
  69. top.NewKeyDownEvent (Key.F7);
  70. Assert.True (v3.HasFocus);
  71. top.NewKeyDownEvent (Key.F7);
  72. Assert.True (v4.HasFocus);
  73. top.NewKeyDownEvent (Key.F7);
  74. Assert.True (v1.HasFocus);
  75. top.NewKeyDownEvent (Key.F6);
  76. Assert.True (v4.HasFocus);
  77. top.NewKeyDownEvent (Key.F6);
  78. Assert.True (v3.HasFocus);
  79. top.NewKeyDownEvent (Key.F6);
  80. Assert.True (v2.HasFocus);
  81. top.NewKeyDownEvent (Key.F6);
  82. Assert.True (v1.HasFocus);
  83. Application.RequestStop ();
  84. };
  85. Application.Run (top);
  86. // Replacing the defaults keys to avoid errors on others unit tests that are using it.
  87. Application.AlternateForwardKey = Key.PageDown.WithCtrl;
  88. Application.AlternateBackwardKey = Key.PageUp.WithCtrl;
  89. Application.QuitKey = Key.Q.WithCtrl;
  90. Assert.Equal (KeyCode.PageDown | KeyCode.CtrlMask, Application.AlternateForwardKey.KeyCode);
  91. Assert.Equal (KeyCode.PageUp | KeyCode.CtrlMask, Application.AlternateBackwardKey.KeyCode);
  92. Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode);
  93. // Shutdown must be called to safely clean up Application if Init has been called
  94. Application.Shutdown ();
  95. }
  96. [Fact]
  97. [AutoInitShutdown]
  98. public void EnsuresTopOnFront_CanFocus_False_By_Keyboard ()
  99. {
  100. Toplevel top = Application.Top;
  101. var win = new Window
  102. {
  103. Title = "win",
  104. X = 0,
  105. Y = 0,
  106. Width = 20,
  107. Height = 10
  108. };
  109. var tf = new TextField { Width = 10 };
  110. win.Add (tf);
  111. var win2 = new Window
  112. {
  113. Title = "win2",
  114. X = 22,
  115. Y = 0,
  116. Width = 20,
  117. Height = 10
  118. };
  119. var tf2 = new TextField { Width = 10 };
  120. win2.Add (tf2);
  121. top.Add (win, win2);
  122. Application.Begin (top);
  123. Assert.True (win.CanFocus);
  124. Assert.True (win.HasFocus);
  125. Assert.True (win2.CanFocus);
  126. Assert.False (win2.HasFocus);
  127. Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
  128. win.CanFocus = false;
  129. Assert.False (win.CanFocus);
  130. Assert.False (win.HasFocus);
  131. Assert.True (win2.CanFocus);
  132. Assert.True (win2.HasFocus);
  133. Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
  134. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  135. Assert.True (win2.CanFocus);
  136. Assert.False (win.HasFocus);
  137. Assert.True (win2.CanFocus);
  138. Assert.True (win2.HasFocus);
  139. Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
  140. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  141. Assert.False (win.CanFocus);
  142. Assert.False (win.HasFocus);
  143. Assert.True (win2.CanFocus);
  144. Assert.True (win2.HasFocus);
  145. Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
  146. }
  147. [Fact]
  148. [AutoInitShutdown]
  149. public void EnsuresTopOnFront_CanFocus_True_By_Keyboard_ ()
  150. {
  151. Toplevel top = Application.Top;
  152. var win = new Window
  153. {
  154. Title = "win",
  155. X = 0,
  156. Y = 0,
  157. Width = 20,
  158. Height = 10
  159. };
  160. var tf = new TextField { Width = 10 };
  161. win.Add (tf);
  162. var win2 = new Window
  163. {
  164. Title = "win2",
  165. X = 22,
  166. Y = 0,
  167. Width = 20,
  168. Height = 10
  169. };
  170. var tf2 = new TextField { Width = 10 };
  171. win2.Add (tf2);
  172. top.Add (win, win2);
  173. Application.Begin (top);
  174. Assert.True (win.CanFocus);
  175. Assert.True (win.HasFocus);
  176. Assert.True (win2.CanFocus);
  177. Assert.False (win2.HasFocus);
  178. Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
  179. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  180. Assert.True (win.CanFocus);
  181. Assert.False (win.HasFocus);
  182. Assert.True (win2.CanFocus);
  183. Assert.True (win2.HasFocus);
  184. Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
  185. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  186. Assert.True (win.CanFocus);
  187. Assert.True (win.HasFocus);
  188. Assert.True (win2.CanFocus);
  189. Assert.False (win2.HasFocus);
  190. Assert.Equal ("win", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
  191. }
  192. [Fact]
  193. public void KeyUp_Event ()
  194. {
  195. Application.Init (new FakeDriver ());
  196. // Setup some fake keypresses (This)
  197. var input = "Tests";
  198. // Put a control-q in at the end
  199. FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo ('Q', ConsoleKey.Q, false, false, true));
  200. foreach (char c in input.Reverse ())
  201. {
  202. if (char.IsLetter (c))
  203. {
  204. FakeConsole.MockKeyPresses.Push (
  205. new ConsoleKeyInfo (
  206. c,
  207. (ConsoleKey)char.ToUpper (c),
  208. char.IsUpper (c),
  209. false,
  210. false
  211. )
  212. );
  213. }
  214. else
  215. {
  216. FakeConsole.MockKeyPresses.Push (
  217. new ConsoleKeyInfo (
  218. c,
  219. (ConsoleKey)c,
  220. false,
  221. false,
  222. false
  223. )
  224. );
  225. }
  226. }
  227. int stackSize = FakeConsole.MockKeyPresses.Count;
  228. var iterations = 0;
  229. Application.Iteration += (s, a) =>
  230. {
  231. iterations++;
  232. // Stop if we run out of control...
  233. if (iterations > 10)
  234. {
  235. Application.RequestStop ();
  236. }
  237. };
  238. var keyUps = 0;
  239. var output = string.Empty;
  240. Application.Top.KeyUp += (sender, args) =>
  241. {
  242. if (args.KeyCode != (KeyCode.CtrlMask | KeyCode.Q))
  243. {
  244. output += args.AsRune;
  245. }
  246. keyUps++;
  247. };
  248. Application.Run (Application.Top);
  249. // Input string should match output
  250. Assert.Equal (input, output);
  251. // # of key up events should match stack size
  252. //Assert.Equal (stackSize, keyUps);
  253. // We can't use numbers variables on the left side of an Assert.Equal/NotEqual,
  254. // it must be literal (Linux only).
  255. Assert.Equal (6, keyUps);
  256. // # of key up events should match # of iterations
  257. Assert.Equal (stackSize, iterations);
  258. Application.Shutdown ();
  259. Assert.Null (Application.Current);
  260. Assert.Null (Application.Top);
  261. Assert.Null (Application.MainLoop);
  262. Assert.Null (Application.Driver);
  263. }
  264. [Fact]
  265. [AutoInitShutdown]
  266. public void OnKeyDown_Application_KeyBinding ()
  267. {
  268. var view = new ScopedKeyBindingView ();
  269. var invoked = false;
  270. view.InvokingKeyBindings += (s, e) => invoked = true;
  271. Application.Top.Add (view);
  272. Application.Begin (Application.Top);
  273. Application.OnKeyDown (Key.A);
  274. Assert.True (invoked);
  275. Assert.True (view.ApplicationCommand);
  276. invoked = false;
  277. view.ApplicationCommand = false;
  278. view.KeyBindings.Remove (KeyCode.A);
  279. Application.OnKeyDown (Key.A); // old
  280. Assert.False (invoked);
  281. Assert.False (view.ApplicationCommand);
  282. view.KeyBindings.Add (Key.A.WithCtrl, KeyBindingScope.Application, Command.Save);
  283. Application.OnKeyDown (Key.A); // old
  284. Assert.False (invoked);
  285. Assert.False (view.ApplicationCommand);
  286. Application.OnKeyDown (Key.A.WithCtrl); // new
  287. Assert.True (invoked);
  288. Assert.True (view.ApplicationCommand);
  289. invoked = false;
  290. Application.OnKeyDown (Key.H);
  291. Assert.True (invoked);
  292. invoked = false;
  293. Assert.False (view.HasFocus);
  294. Application.OnKeyDown (Key.F);
  295. Assert.False (invoked);
  296. Assert.True (view.ApplicationCommand);
  297. Assert.True (view.HotKeyCommand);
  298. Assert.False (view.FocusedCommand);
  299. }
  300. [Fact]
  301. [AutoInitShutdown]
  302. public void OnKeyDown_Application_KeyBinding_Negative ()
  303. {
  304. var view = new ScopedKeyBindingView ();
  305. var invoked = false;
  306. view.InvokingKeyBindings += (s, e) => invoked = true;
  307. Application.Top.Add (view);
  308. Application.Begin (Application.Top);
  309. Application.OnKeyDown (Key.A.WithCtrl);
  310. Assert.False (invoked);
  311. Assert.False (view.ApplicationCommand);
  312. Assert.False (view.HotKeyCommand);
  313. Assert.False (view.FocusedCommand);
  314. invoked = false;
  315. Assert.False (view.HasFocus);
  316. Application.OnKeyDown (Key.Z);
  317. Assert.False (invoked);
  318. Assert.False (view.ApplicationCommand);
  319. Assert.False (view.HotKeyCommand);
  320. Assert.False (view.FocusedCommand);
  321. }
  322. [Fact]
  323. [AutoInitShutdown]
  324. public void QuitKey_Getter_Setter ()
  325. {
  326. Toplevel top = Application.Top;
  327. var isQuiting = false;
  328. top.Closing += (s, e) =>
  329. {
  330. isQuiting = true;
  331. e.Cancel = true;
  332. };
  333. Application.Begin (top);
  334. top.Running = true;
  335. Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode);
  336. Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
  337. Assert.True (isQuiting);
  338. isQuiting = false;
  339. Application.OnKeyDown (Key.Q.WithCtrl);
  340. Assert.True (isQuiting);
  341. isQuiting = false;
  342. Application.QuitKey = Key.C.WithCtrl;
  343. Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
  344. Assert.False (isQuiting);
  345. Application.OnKeyDown (Key.Q.WithCtrl);
  346. Assert.False (isQuiting);
  347. Application.OnKeyDown (Application.QuitKey);
  348. Assert.True (isQuiting);
  349. // Reset the QuitKey to avoid throws errors on another tests
  350. Application.QuitKey = Key.Q.WithCtrl;
  351. }
  352. // test Application key Bindings
  353. public class ScopedKeyBindingView : View
  354. {
  355. public ScopedKeyBindingView ()
  356. {
  357. AddCommand (Command.Save, () => ApplicationCommand = true);
  358. AddCommand (Command.HotKey, () => HotKeyCommand = true);
  359. AddCommand (Command.Left, () => FocusedCommand = true);
  360. KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Save);
  361. HotKey = KeyCode.H;
  362. KeyBindings.Add (Key.F, KeyBindingScope.Focused, Command.Left);
  363. }
  364. public bool ApplicationCommand { get; set; }
  365. public bool FocusedCommand { get; set; }
  366. public bool HotKeyCommand { get; set; }
  367. }
  368. }