KeyboardTests.cs 12 KB

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