KeyboardEventTests.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. using System;
  2. using Xunit;
  3. using Xunit.Abstractions;
  4. // Alias Console to MockConsole so we don't accidentally use Console
  5. using Console = Terminal.Gui.FakeConsole;
  6. namespace Terminal.Gui.ViewTests;
  7. public class KeyboardEventTests {
  8. readonly ITestOutputHelper _output;
  9. public KeyboardEventTests (ITestOutputHelper output) => _output = output;
  10. [Fact]
  11. public void KeyPress_Handled_Cancels ()
  12. {
  13. var view = new View ();
  14. bool invokingKeyBindingsInvoked = false;
  15. bool processKeyPressInvoked = false;
  16. bool setHandledTo = false;
  17. view.KeyDown += (s, e) => {
  18. e.Handled = setHandledTo;
  19. Assert.Equal (setHandledTo, e.Handled);
  20. Assert.Equal (KeyCode.N, e.KeyCode);
  21. };
  22. view.InvokingKeyBindings += (s, e) => {
  23. invokingKeyBindingsInvoked = true;
  24. Assert.False (e.Handled);
  25. Assert.Equal (KeyCode.N, e.KeyCode);
  26. };
  27. view.ProcessKeyDown += (s, e) => {
  28. processKeyPressInvoked = true;
  29. Assert.False (e.Handled);
  30. Assert.Equal (KeyCode.N, e.KeyCode);
  31. };
  32. view.NewKeyDownEvent (new Key (KeyCode.N));
  33. Assert.True (invokingKeyBindingsInvoked);
  34. Assert.True (processKeyPressInvoked);
  35. invokingKeyBindingsInvoked = false;
  36. processKeyPressInvoked = false;
  37. setHandledTo = true;
  38. view.NewKeyDownEvent (new Key (KeyCode.N));
  39. Assert.False (invokingKeyBindingsInvoked);
  40. Assert.False (processKeyPressInvoked);
  41. }
  42. [Fact]
  43. public void InvokingKeyBindings_Handled_Cancels ()
  44. {
  45. var view = new View ();
  46. bool keyPressInvoked = false;
  47. bool invokingKeyBindingsInvoked = false;
  48. bool processKeyPressInvoked = false;
  49. bool setHandledTo = false;
  50. view.KeyDown += (s, e) => {
  51. keyPressInvoked = true;
  52. Assert.False (e.Handled);
  53. Assert.Equal (KeyCode.N, e.KeyCode);
  54. };
  55. view.InvokingKeyBindings += (s, e) => {
  56. invokingKeyBindingsInvoked = true;
  57. e.Handled = setHandledTo;
  58. Assert.Equal (setHandledTo, e.Handled);
  59. Assert.Equal (KeyCode.N, e.KeyCode);
  60. };
  61. view.ProcessKeyDown += (s, e) => {
  62. processKeyPressInvoked = true;
  63. processKeyPressInvoked = true;
  64. Assert.False (e.Handled);
  65. Assert.Equal (KeyCode.N, e.KeyCode);
  66. };
  67. view.NewKeyDownEvent (new Key (KeyCode.N));
  68. Assert.True (keyPressInvoked);
  69. Assert.True (invokingKeyBindingsInvoked);
  70. Assert.True (processKeyPressInvoked);
  71. keyPressInvoked = false;
  72. invokingKeyBindingsInvoked = false;
  73. processKeyPressInvoked = false;
  74. setHandledTo = true;
  75. view.NewKeyDownEvent (new Key (KeyCode.N));
  76. Assert.True (keyPressInvoked);
  77. Assert.True (invokingKeyBindingsInvoked);
  78. Assert.False (processKeyPressInvoked);
  79. }
  80. [Theory]
  81. [InlineData (null, null)]
  82. [InlineData (true, true)]
  83. [InlineData (false, false)]
  84. public void OnInvokingKeyBindings_Returns_Nullable_Properly (bool? toReturn, bool? expected)
  85. {
  86. var view = new KeyBindingsTestView ();
  87. view.CommandReturns = toReturn;
  88. bool? result = view.OnInvokingKeyBindings (new Key (KeyCode.A));
  89. Assert.Equal (expected, result);
  90. }
  91. /// <summary>
  92. /// A view that overrides the OnKey* methods so we can test that they are called.
  93. /// </summary>
  94. public class KeyBindingsTestView : View {
  95. public bool? CommandReturns { get; set; }
  96. public KeyBindingsTestView ()
  97. {
  98. CanFocus = true;
  99. AddCommand (Command.Default, () => CommandReturns);
  100. KeyBindings.Add (KeyCode.A, Command.Default);
  101. }
  102. }
  103. [Fact]
  104. public void KeyDown_Handled_True_Stops_Processing ()
  105. {
  106. bool keyDown = false;
  107. bool invokingKeyBindings = false;
  108. bool keyPressed = false;
  109. var view = new OnKeyTestView ();
  110. Assert.True (view.CanFocus);
  111. view.CancelVirtualMethods = false;
  112. view.KeyDown += (s, e) => {
  113. Assert.Equal (KeyCode.A, e.KeyCode);
  114. Assert.False (keyDown);
  115. Assert.False (view.OnKeyDownContinued);
  116. e.Handled = true;
  117. keyDown = true;
  118. };
  119. view.InvokingKeyBindings += (s, e) => {
  120. Assert.Equal (KeyCode.A, e.KeyCode);
  121. Assert.False (keyPressed);
  122. Assert.False (view.OnInvokingKeyBindingsContinued);
  123. e.Handled = true;
  124. invokingKeyBindings = true;
  125. };
  126. view.ProcessKeyDown += (s, e) => {
  127. Assert.Equal (KeyCode.A, e.KeyCode);
  128. Assert.False (keyPressed);
  129. Assert.False (view.OnKeyPressedContinued);
  130. e.Handled = true;
  131. keyPressed = true;
  132. };
  133. view.NewKeyDownEvent (new Key (KeyCode.A));
  134. Assert.True (keyDown);
  135. Assert.False (invokingKeyBindings);
  136. Assert.False (keyPressed);
  137. Assert.False (view.OnKeyDownContinued);
  138. Assert.False (view.OnInvokingKeyBindingsContinued);
  139. Assert.False (view.OnKeyPressedContinued);
  140. }
  141. [Fact]
  142. public void InvokingKeyBindings_Handled_True_Stops_Processing ()
  143. {
  144. bool keyDown = false;
  145. bool invokingKeyBindings = false;
  146. bool keyPressed = false;
  147. var view = new OnKeyTestView ();
  148. Assert.True (view.CanFocus);
  149. view.CancelVirtualMethods = false;
  150. view.KeyDown += (s, e) => {
  151. Assert.Equal (KeyCode.A, e.KeyCode);
  152. Assert.False (keyDown);
  153. Assert.False (view.OnKeyDownContinued);
  154. e.Handled = false;
  155. keyDown = true;
  156. };
  157. view.InvokingKeyBindings += (s, e) => {
  158. Assert.Equal (KeyCode.A, e.KeyCode);
  159. Assert.False (keyPressed);
  160. Assert.False (view.OnInvokingKeyBindingsContinued);
  161. e.Handled = true;
  162. invokingKeyBindings = true;
  163. };
  164. view.ProcessKeyDown += (s, e) => {
  165. Assert.Equal (KeyCode.A, e.KeyCode);
  166. Assert.False (keyPressed);
  167. Assert.False (view.OnKeyPressedContinued);
  168. e.Handled = true;
  169. keyPressed = true;
  170. };
  171. view.NewKeyDownEvent (new Key (KeyCode.A));
  172. Assert.True (keyDown);
  173. Assert.True (invokingKeyBindings);
  174. Assert.False (keyPressed);
  175. Assert.True (view.OnKeyDownContinued);
  176. Assert.False (view.OnInvokingKeyBindingsContinued);
  177. Assert.False (view.OnKeyPressedContinued);
  178. }
  179. [Fact]
  180. public void KeyPressed_Handled_True_Stops_Processing ()
  181. {
  182. bool keyDown = false;
  183. bool invokingKeyBindings = false;
  184. bool keyPressed = false;
  185. var view = new OnKeyTestView ();
  186. Assert.True (view.CanFocus);
  187. view.CancelVirtualMethods = false;
  188. view.KeyDown += (s, e) => {
  189. Assert.Equal (KeyCode.A, e.KeyCode);
  190. Assert.False (keyDown);
  191. Assert.False (view.OnKeyDownContinued);
  192. e.Handled = false;
  193. keyDown = true;
  194. };
  195. view.InvokingKeyBindings += (s, e) => {
  196. Assert.Equal (KeyCode.A, e.KeyCode);
  197. Assert.False (keyPressed);
  198. Assert.False (view.OnInvokingKeyBindingsContinued);
  199. e.Handled = false;
  200. invokingKeyBindings = true;
  201. };
  202. view.ProcessKeyDown += (s, e) => {
  203. Assert.Equal (KeyCode.A, e.KeyCode);
  204. Assert.False (keyPressed);
  205. Assert.False (view.OnKeyPressedContinued);
  206. e.Handled = true;
  207. keyPressed = true;
  208. };
  209. view.NewKeyDownEvent (new Key (KeyCode.A));
  210. Assert.True (keyDown);
  211. Assert.True (invokingKeyBindings);
  212. Assert.True (keyPressed);
  213. Assert.True (view.OnKeyDownContinued);
  214. Assert.True (view.OnInvokingKeyBindingsContinued);
  215. Assert.False (view.OnKeyPressedContinued);
  216. }
  217. [Fact]
  218. public void KeyUp_Handled_True_Stops_Processing ()
  219. {
  220. bool keyUp = false;
  221. var view = new OnKeyTestView ();
  222. Assert.True (view.CanFocus);
  223. view.CancelVirtualMethods = false;
  224. view.KeyUp += (s, e) => {
  225. Assert.Equal (KeyCode.A, e.KeyCode);
  226. Assert.False (keyUp);
  227. Assert.False (view.OnKeyPressedContinued);
  228. e.Handled = true;
  229. keyUp = true;
  230. };
  231. view.NewKeyUpEvent (new Key (KeyCode.A));
  232. Assert.True (keyUp);
  233. Assert.False (view.OnKeyUpContinued);
  234. Assert.False (view.OnKeyDownContinued);
  235. Assert.False (view.OnInvokingKeyBindingsContinued);
  236. Assert.False (view.OnKeyPressedContinued);
  237. }
  238. /// <summary>
  239. /// A view that overrides the OnKey* methods so we can test that they are called.
  240. /// </summary>
  241. public class OnKeyTestView : View {
  242. public bool CancelVirtualMethods { set; private get; }
  243. public OnKeyTestView () => CanFocus = true;
  244. public override string Text { get; set; }
  245. public bool OnKeyDownContinued { get; set; }
  246. public bool OnInvokingKeyBindingsContinued { get; set; }
  247. public bool OnKeyPressedContinued { get; set; }
  248. public bool OnKeyUpContinued { get; set; }
  249. public override bool OnKeyDown (Key keyEvent)
  250. {
  251. if (base.OnKeyDown (keyEvent)) {
  252. return true;
  253. }
  254. OnKeyDownContinued = true;
  255. return CancelVirtualMethods;
  256. }
  257. public override bool? OnInvokingKeyBindings (Key keyEvent)
  258. {
  259. bool? handled = base.OnInvokingKeyBindings (keyEvent);
  260. if (handled != null && (bool)handled) {
  261. return true;
  262. }
  263. OnInvokingKeyBindingsContinued = true;
  264. return CancelVirtualMethods;
  265. }
  266. public override bool OnProcessKeyDown (Key keyEvent)
  267. {
  268. if (base.OnProcessKeyDown (keyEvent)) {
  269. return true;
  270. }
  271. OnKeyPressedContinued = true;
  272. return CancelVirtualMethods;
  273. }
  274. public override bool OnKeyUp (Key keyEvent)
  275. {
  276. if (base.OnKeyUp (keyEvent)) {
  277. return true;
  278. }
  279. OnKeyUpContinued = true;
  280. return CancelVirtualMethods;
  281. }
  282. }
  283. [Theory]
  284. [InlineData (true, false, false)]
  285. [InlineData (true, true, false)]
  286. [InlineData (true, true, true)]
  287. public void Events_Are_Called_With_Only_Key_Modifiers (bool shift, bool alt, bool control)
  288. {
  289. bool keyDown = false;
  290. bool keyPressed = false;
  291. bool keyUp = false;
  292. var view = new OnKeyTestView ();
  293. view.CancelVirtualMethods = false;
  294. view.KeyDown += (s, e) => {
  295. Assert.Equal (KeyCode.Null, e.KeyCode & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask);
  296. Assert.Equal (shift, e.IsShift);
  297. Assert.Equal (alt, e.IsAlt);
  298. Assert.Equal (control, e.IsCtrl);
  299. Assert.False (keyDown);
  300. Assert.False (view.OnKeyDownContinued);
  301. keyDown = true;
  302. };
  303. view.ProcessKeyDown += (s, e) => {
  304. keyPressed = true;
  305. };
  306. view.KeyUp += (s, e) => {
  307. Assert.Equal (KeyCode.Null, e.KeyCode & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask);
  308. Assert.Equal (shift, e.IsShift);
  309. Assert.Equal (alt, e.IsAlt);
  310. Assert.Equal (control, e.IsCtrl);
  311. Assert.False (keyUp);
  312. Assert.False (view.OnKeyUpContinued);
  313. keyUp = true;
  314. };
  315. //view.ProcessKeyDownEvent (new (Key.Null | (shift ? Key.ShiftMask : 0) | (alt ? Key.AltMask : 0) | (control ? Key.CtrlMask : 0)));
  316. //Assert.True (keyDown);
  317. //Assert.True (view.OnKeyDownWasCalled);
  318. //Assert.True (view.OnProcessKeyDownWasCalled);
  319. view.NewKeyDownEvent (new Key (KeyCode.Null | (shift ? KeyCode.ShiftMask : 0) | (alt ? KeyCode.AltMask : 0) | (control ? KeyCode.CtrlMask : 0)));
  320. Assert.True (keyPressed);
  321. Assert.True (view.OnKeyDownContinued);
  322. Assert.True (view.OnKeyPressedContinued);
  323. view.NewKeyUpEvent (new Key (KeyCode.Null | (shift ? KeyCode.ShiftMask : 0) | (alt ? KeyCode.AltMask : 0) | (control ? KeyCode.CtrlMask : 0)));
  324. Assert.True (keyUp);
  325. Assert.True (view.OnKeyUpContinued);
  326. }
  327. /// <summary>
  328. /// This tests that when a new key down event is sent to the view
  329. /// the view will fire the 3 key-down related events: KeyDown, InvokingKeyBindings, and ProcessKeyDown.
  330. /// Note that KeyUp is independent.
  331. /// </summary>
  332. [Fact]
  333. public void AllViews_KeyDown_All_EventsFire ()
  334. {
  335. foreach (var view in TestHelpers.GetAllViews ()) {
  336. if (view == null) {
  337. _output.WriteLine ($"ERROR: null view from {nameof (TestHelpers.GetAllViews)}");
  338. continue;
  339. }
  340. _output.WriteLine ($"Testing {view.GetType ().Name}");
  341. bool keyDown = false;
  342. view.KeyDown += (s, a) => {
  343. a.Handled = false; // don't handle it so the other events are called
  344. keyDown = true;
  345. };
  346. bool invokingKeyBindings = false;
  347. view.InvokingKeyBindings += (s, a) => {
  348. a.Handled = false; // don't handle it so the other events are called
  349. invokingKeyBindings = true;
  350. };
  351. bool keyDownProcessed = false;
  352. view.ProcessKeyDown += (s, a) => {
  353. a.Handled = true;
  354. keyDownProcessed = true;
  355. };
  356. Assert.True (view.NewKeyDownEvent (Key.A)); // this will be true because the ProcessKeyDown event handled it
  357. Assert.True (keyDown);
  358. Assert.True (invokingKeyBindings);
  359. Assert.True (keyDownProcessed);
  360. view.Dispose ();
  361. }
  362. }
  363. /// <summary>
  364. /// This tests that when a new key up event is sent to the view
  365. /// the view will fire the 1 key-up related event: KeyUp
  366. /// </summary>
  367. [Fact]
  368. public void AllViews_KeyUp_All_EventsFire ()
  369. {
  370. foreach (var view in TestHelpers.GetAllViews ()) {
  371. if (view == null) {
  372. _output.WriteLine ($"ERROR: null view from {nameof (TestHelpers.GetAllViews)}");
  373. continue;
  374. }
  375. _output.WriteLine ($"Testing {view.GetType ().Name}");
  376. bool keyUp = false;
  377. view.KeyUp += (s, a) => {
  378. a.Handled = true;
  379. keyUp = true;
  380. };
  381. Assert.True (view.NewKeyUpEvent (Key.A)); // this will be true because the KeyUp event handled it
  382. Assert.True (keyUp);
  383. view.Dispose ();
  384. }
  385. }
  386. }