KeyboardTests.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  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_And_Mouse ()
  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. win.OnMouseEvent (new MouseEvent { Flags = MouseFlags.Button1Pressed });
  147. Assert.False (win.CanFocus);
  148. Assert.False (win.HasFocus);
  149. Assert.True (win2.CanFocus);
  150. Assert.True (win2.HasFocus);
  151. Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
  152. win2.OnMouseEvent (new MouseEvent { Flags = MouseFlags.Button1Released });
  153. Assert.Null (Toplevel._dragPosition);
  154. }
  155. [Fact]
  156. [AutoInitShutdown]
  157. public void EnsuresTopOnFront_CanFocus_True_By_Keyboard_And_Mouse ()
  158. {
  159. Toplevel top = Application.Top;
  160. var win = new Window
  161. {
  162. Title = "win",
  163. X = 0,
  164. Y = 0,
  165. Width = 20,
  166. Height = 10
  167. };
  168. var tf = new TextField { Width = 10 };
  169. win.Add (tf);
  170. var win2 = new Window
  171. {
  172. Title = "win2",
  173. X = 22,
  174. Y = 0,
  175. Width = 20,
  176. Height = 10
  177. };
  178. var tf2 = new TextField { Width = 10 };
  179. win2.Add (tf2);
  180. top.Add (win, win2);
  181. Application.Begin (top);
  182. Assert.True (win.CanFocus);
  183. Assert.True (win.HasFocus);
  184. Assert.True (win2.CanFocus);
  185. Assert.False (win2.HasFocus);
  186. Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
  187. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  188. Assert.True (win.CanFocus);
  189. Assert.False (win.HasFocus);
  190. Assert.True (win2.CanFocus);
  191. Assert.True (win2.HasFocus);
  192. Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
  193. top.NewKeyDownEvent (Key.Tab.WithCtrl);
  194. Assert.True (win.CanFocus);
  195. Assert.True (win.HasFocus);
  196. Assert.True (win2.CanFocus);
  197. Assert.False (win2.HasFocus);
  198. Assert.Equal ("win", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
  199. win2.OnMouseEvent (new MouseEvent { Flags = MouseFlags.Button1Pressed });
  200. Assert.True (win.CanFocus);
  201. Assert.False (win.HasFocus);
  202. Assert.True (win2.CanFocus);
  203. Assert.True (win2.HasFocus);
  204. Assert.Equal ("win2", ((Window)top.Subviews [top.Subviews.Count - 1]).Title);
  205. win2.OnMouseEvent (new MouseEvent { Flags = MouseFlags.Button1Released });
  206. Assert.Null (Toplevel._dragPosition);
  207. }
  208. [Fact]
  209. public void KeyUp_Event ()
  210. {
  211. Application.Init (new FakeDriver ());
  212. // Setup some fake keypresses (This)
  213. var input = "Tests";
  214. // Put a control-q in at the end
  215. FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo ('Q', ConsoleKey.Q, false, false, true));
  216. foreach (char c in input.Reverse ())
  217. {
  218. if (char.IsLetter (c))
  219. {
  220. FakeConsole.MockKeyPresses.Push (
  221. new ConsoleKeyInfo (
  222. c,
  223. (ConsoleKey)char.ToUpper (c),
  224. char.IsUpper (c),
  225. false,
  226. false
  227. )
  228. );
  229. }
  230. else
  231. {
  232. FakeConsole.MockKeyPresses.Push (
  233. new ConsoleKeyInfo (
  234. c,
  235. (ConsoleKey)c,
  236. false,
  237. false,
  238. false
  239. )
  240. );
  241. }
  242. }
  243. int stackSize = FakeConsole.MockKeyPresses.Count;
  244. var iterations = 0;
  245. Application.Iteration += (s, a) =>
  246. {
  247. iterations++;
  248. // Stop if we run out of control...
  249. if (iterations > 10)
  250. {
  251. Application.RequestStop ();
  252. }
  253. };
  254. var keyUps = 0;
  255. var output = string.Empty;
  256. Application.Top.KeyUp += (sender, args) =>
  257. {
  258. if (args.KeyCode != (KeyCode.CtrlMask | KeyCode.Q))
  259. {
  260. output += args.AsRune;
  261. }
  262. keyUps++;
  263. };
  264. Application.Run (Application.Top);
  265. // Input string should match output
  266. Assert.Equal (input, output);
  267. // # of key up events should match stack size
  268. //Assert.Equal (stackSize, keyUps);
  269. // We can't use numbers variables on the left side of an Assert.Equal/NotEqual,
  270. // it must be literal (Linux only).
  271. Assert.Equal (6, keyUps);
  272. // # of key up events should match # of iterations
  273. Assert.Equal (stackSize, iterations);
  274. Application.Shutdown ();
  275. Assert.Null (Application.Current);
  276. Assert.Null (Application.Top);
  277. Assert.Null (Application.MainLoop);
  278. Assert.Null (Application.Driver);
  279. }
  280. [Fact]
  281. [AutoInitShutdown]
  282. public void OnKeyDown_Application_KeyBinding ()
  283. {
  284. var view = new ScopedKeyBindingView ();
  285. var invoked = false;
  286. view.InvokingKeyBindings += (s, e) => invoked = true;
  287. Application.Top.Add (view);
  288. Application.Begin (Application.Top);
  289. Application.OnKeyDown (Key.A);
  290. Assert.True (invoked);
  291. Assert.True (view.ApplicationCommand);
  292. invoked = false;
  293. view.ApplicationCommand = false;
  294. view.KeyBindings.Remove (KeyCode.A);
  295. Application.OnKeyDown (Key.A); // old
  296. Assert.False (invoked);
  297. Assert.False (view.ApplicationCommand);
  298. view.KeyBindings.Add (Key.A.WithCtrl, KeyBindingScope.Application, Command.Save);
  299. Application.OnKeyDown (Key.A); // old
  300. Assert.False (invoked);
  301. Assert.False (view.ApplicationCommand);
  302. Application.OnKeyDown (Key.A.WithCtrl); // new
  303. Assert.True (invoked);
  304. Assert.True (view.ApplicationCommand);
  305. invoked = false;
  306. Application.OnKeyDown (Key.H);
  307. Assert.True (invoked);
  308. invoked = false;
  309. Assert.False (view.HasFocus);
  310. Application.OnKeyDown (Key.F);
  311. Assert.False (invoked);
  312. Assert.True (view.ApplicationCommand);
  313. Assert.True (view.HotKeyCommand);
  314. Assert.False (view.FocusedCommand);
  315. }
  316. [Fact]
  317. [AutoInitShutdown]
  318. public void OnKeyDown_Application_KeyBinding_Negative ()
  319. {
  320. var view = new ScopedKeyBindingView ();
  321. var invoked = false;
  322. view.InvokingKeyBindings += (s, e) => invoked = true;
  323. Application.Top.Add (view);
  324. Application.Begin (Application.Top);
  325. Application.OnKeyDown (Key.A.WithCtrl);
  326. Assert.False (invoked);
  327. Assert.False (view.ApplicationCommand);
  328. Assert.False (view.HotKeyCommand);
  329. Assert.False (view.FocusedCommand);
  330. invoked = false;
  331. Assert.False (view.HasFocus);
  332. Application.OnKeyDown (Key.Z);
  333. Assert.False (invoked);
  334. Assert.False (view.ApplicationCommand);
  335. Assert.False (view.HotKeyCommand);
  336. Assert.False (view.FocusedCommand);
  337. }
  338. [Fact]
  339. [AutoInitShutdown]
  340. public void QuitKey_Getter_Setter ()
  341. {
  342. Toplevel top = Application.Top;
  343. var isQuiting = false;
  344. top.Closing += (s, e) =>
  345. {
  346. isQuiting = true;
  347. e.Cancel = true;
  348. };
  349. Application.Begin (top);
  350. top.Running = true;
  351. Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode);
  352. Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
  353. Assert.True (isQuiting);
  354. isQuiting = false;
  355. Application.OnKeyDown (Key.Q.WithCtrl);
  356. Assert.True (isQuiting);
  357. isQuiting = false;
  358. Application.QuitKey = Key.C.WithCtrl;
  359. Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
  360. Assert.False (isQuiting);
  361. Application.OnKeyDown (Key.Q.WithCtrl);
  362. Assert.False (isQuiting);
  363. Application.OnKeyDown (Application.QuitKey);
  364. Assert.True (isQuiting);
  365. // Reset the QuitKey to avoid throws errors on another tests
  366. Application.QuitKey = Key.Q.WithCtrl;
  367. }
  368. // test Application key Bindings
  369. public class ScopedKeyBindingView : View
  370. {
  371. public ScopedKeyBindingView ()
  372. {
  373. AddCommand (Command.Save, () => ApplicationCommand = true);
  374. AddCommand (Command.HotKey, () => HotKeyCommand = true);
  375. AddCommand (Command.Left, () => FocusedCommand = true);
  376. KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Save);
  377. HotKey = KeyCode.H;
  378. KeyBindings.Add (Key.F, KeyBindingScope.Focused, Command.Left);
  379. }
  380. public bool ApplicationCommand { get; set; }
  381. public bool FocusedCommand { get; set; }
  382. public bool HotKeyCommand { get; set; }
  383. }
  384. }