From 702ef467277e6e4b1e5ff81a423e9ea1dca5e9f9 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 28 Sep 2022 00:07:55 +0100 Subject: [PATCH 1/2] Fixes #2069. KeyDown and KeyUp events must run before OnKeyDown and OnKeyUp. --- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 3 + Terminal.Gui/Core/View.cs | 20 ++++-- UnitTests/ViewTests.cs | 68 +++++++++++++++++++ 3 files changed, 87 insertions(+), 4 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 50662f364..d594eea8c 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -370,12 +370,14 @@ namespace Terminal.Gui { return keyMod != Key.Null ? keyMod | key : key; } + Action keyDownHandler; Action keyHandler; Action keyUpHandler; private CursorVisibility savedCursorVisibility; public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) { + this.keyDownHandler = keyDownHandler; this.keyHandler = keyHandler; this.keyUpHandler = keyUpHandler; @@ -400,6 +402,7 @@ namespace Terminal.Gui { keyModifiers.Ctrl = true; } + keyDownHandler (new KeyEvent (map, keyModifiers)); keyHandler (new KeyEvent (map, keyModifiers)); keyUpHandler (new KeyEvent (map, keyModifiers)); } diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index 67dfdf755..ea9d80885 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -1932,8 +1932,14 @@ namespace Terminal.Gui { if (args.Handled) { return true; } - if (Focused?.Enabled == true && Focused?.OnKeyDown (keyEvent) == true) { - return true; + if (Focused?.Enabled == true) { + Focused.KeyDown?.Invoke (args); + if (args.Handled) { + return true; + } + if (Focused?.OnKeyDown (keyEvent) == true) { + return true; + } } return false; @@ -1956,8 +1962,14 @@ namespace Terminal.Gui { if (args.Handled) { return true; } - if (Focused?.Enabled == true && Focused?.OnKeyUp (keyEvent) == true) { - return true; + if (Focused?.Enabled == true) { + Focused.KeyUp?.Invoke (args); + if (args.Handled) { + return true; + } + if (Focused?.OnKeyUp (keyEvent) == true) { + return true; + } } return false; diff --git a/UnitTests/ViewTests.cs b/UnitTests/ViewTests.cs index 3c03abe3f..baf66c497 100644 --- a/UnitTests/ViewTests.cs +++ b/UnitTests/ViewTests.cs @@ -3910,5 +3910,73 @@ This is a tes Assert.True (viewCalled); Assert.True (tvCalled); } + + [Fact, AutoInitShutdown] + public void KeyDown_And_KeyUp_Events_Must_Called_Before_OnKeyDown_And_OnKeyUp () + { + var view = new DerivedView (); + view.KeyDown += (e) => { + Assert.Equal (Key.a, e.KeyEvent.Key); + Assert.False (view.IsKeyDown); + e.Handled = true; + }; + view.KeyPress += (e) => { + Assert.Equal (Key.a, e.KeyEvent.Key); + Assert.False (view.IsKeyPress); + e.Handled = true; + }; + view.KeyUp += (e) => { + Assert.Equal (Key.a, e.KeyEvent.Key); + Assert.False (view.IsKeyUp); + e.Handled = true; + }; + + var iterations = -1; + + Application.Iteration += () => { + iterations++; + if (iterations == 0) { + Console.MockKeyPresses.Push (new ConsoleKeyInfo ('a', ConsoleKey.A, false, false, false)); + } else if (iterations == 1) { + Application.RequestStop (); + } + }; + + Application.Top.Add (view); + + Assert.True (view.CanFocus); + + Application.Run (); + Application.Shutdown (); + } + + public class DerivedView : View { + public DerivedView () + { + CanFocus = true; + } + + public bool IsKeyDown { get; set; } + public bool IsKeyPress { get; set; } + public bool IsKeyUp { get; set; } + + public override bool OnKeyDown (KeyEvent keyEvent) + { + IsKeyDown = true; + return base.OnKeyDown (keyEvent); + } + + public override bool ProcessKey (KeyEvent keyEvent) + { + IsKeyPress = true; + return base.ProcessKey (keyEvent); + } + + public override bool OnKeyUp (KeyEvent keyEvent) + { + IsKeyUp = true; + return base.OnKeyUp (keyEvent); + } + } } } From eaaa6364972883d4f7da570db05d03a6b52a1b36 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 28 Sep 2022 10:35:10 +0100 Subject: [PATCH 2/2] Adding a more realistic key down/up event with null key. --- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 18 ++-- UnitTests/ViewTests.cs | 90 ++++++++++++++++--- 2 files changed, 87 insertions(+), 21 deletions(-) diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index d594eea8c..8189207bc 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -388,19 +388,23 @@ namespace Terminal.Gui { void ProcessInput (ConsoleKeyInfo consoleKey) { keyModifiers = new KeyModifiers (); - var map = MapKey (consoleKey); - if (map == (Key)0xffffffff) - return; - - if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Alt)) { - keyModifiers.Alt = true; - } if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Shift)) { keyModifiers.Shift = true; } + if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Alt)) { + keyModifiers.Alt = true; + } if (consoleKey.Modifiers.HasFlag (ConsoleModifiers.Control)) { keyModifiers.Ctrl = true; } + var map = MapKey (consoleKey); + if (map == (Key)0xffffffff) { + if ((consoleKey.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + keyDownHandler (new KeyEvent (map, keyModifiers)); + keyUpHandler (new KeyEvent (map, keyModifiers)); + } + return; + } keyDownHandler (new KeyEvent (map, keyModifiers)); keyHandler (new KeyEvent (map, keyModifiers)); diff --git a/UnitTests/ViewTests.cs b/UnitTests/ViewTests.cs index baf66c497..5218060ab 100644 --- a/UnitTests/ViewTests.cs +++ b/UnitTests/ViewTests.cs @@ -3914,40 +3914,50 @@ This is a tes [Fact, AutoInitShutdown] public void KeyDown_And_KeyUp_Events_Must_Called_Before_OnKeyDown_And_OnKeyUp () { + var keyDown = false; + var keyPress = false; + var keyUp = false; + var view = new DerivedView (); view.KeyDown += (e) => { Assert.Equal (Key.a, e.KeyEvent.Key); + Assert.False (keyDown); Assert.False (view.IsKeyDown); e.Handled = true; + keyDown = true; }; view.KeyPress += (e) => { Assert.Equal (Key.a, e.KeyEvent.Key); + Assert.False (keyPress); Assert.False (view.IsKeyPress); e.Handled = true; + keyPress = true; }; view.KeyUp += (e) => { Assert.Equal (Key.a, e.KeyEvent.Key); + Assert.False (keyUp); Assert.False (view.IsKeyUp); e.Handled = true; - }; - - var iterations = -1; - - Application.Iteration += () => { - iterations++; - if (iterations == 0) { - Console.MockKeyPresses.Push (new ConsoleKeyInfo ('a', ConsoleKey.A, false, false, false)); - } else if (iterations == 1) { - Application.RequestStop (); - } + keyUp = true; }; Application.Top.Add (view); + Console.MockKeyPresses.Push (new ConsoleKeyInfo ('a', ConsoleKey.A, false, false, false)); + + Application.Iteration += () => Application.RequestStop (); + Assert.True (view.CanFocus); Application.Run (); Application.Shutdown (); + + Assert.True (keyDown); + Assert.True (keyPress); + Assert.True (keyUp); + Assert.False (view.IsKeyDown); + Assert.False (view.IsKeyPress); + Assert.False (view.IsKeyUp); } public class DerivedView : View { @@ -3963,20 +3973,72 @@ This is a tes public override bool OnKeyDown (KeyEvent keyEvent) { IsKeyDown = true; - return base.OnKeyDown (keyEvent); + return true; } public override bool ProcessKey (KeyEvent keyEvent) { IsKeyPress = true; - return base.ProcessKey (keyEvent); + return true; } public override bool OnKeyUp (KeyEvent keyEvent) { IsKeyUp = true; - return base.OnKeyUp (keyEvent); + return true; } } + + [Theory, AutoInitShutdown] + [InlineData (true, false, false)] + [InlineData (true, true, false)] + [InlineData (true, true, true)] + public void KeyDown_And_KeyUp_Events_With_Only_Key_Modifiers (bool shift, bool alt, bool control) + { + var keyDown = false; + var keyPress = false; + var keyUp = false; + + var view = new DerivedView (); + view.KeyDown += (e) => { + Assert.Equal (-1, e.KeyEvent.KeyValue); + Assert.Equal (shift, e.KeyEvent.IsShift); + Assert.Equal (alt, e.KeyEvent.IsAlt); + Assert.Equal (control, e.KeyEvent.IsCtrl); + Assert.False (keyDown); + Assert.False (view.IsKeyDown); + keyDown = true; + }; + view.KeyPress += (e) => { + keyPress = true; + }; + view.KeyUp += (e) => { + Assert.Equal (-1, e.KeyEvent.KeyValue); + Assert.Equal (shift, e.KeyEvent.IsShift); + Assert.Equal (alt, e.KeyEvent.IsAlt); + Assert.Equal (control, e.KeyEvent.IsCtrl); + Assert.False (keyUp); + Assert.False (view.IsKeyUp); + keyUp = true; + }; + + Application.Top.Add (view); + + Console.MockKeyPresses.Push (new ConsoleKeyInfo ('\0', (ConsoleKey)'\0', shift, alt, control)); + + Application.Iteration += () => Application.RequestStop (); + + Assert.True (view.CanFocus); + + Application.Run (); + Application.Shutdown (); + + Assert.True (keyDown); + Assert.False (keyPress); + Assert.True (keyUp); + Assert.True (view.IsKeyDown); + Assert.False (view.IsKeyPress); + Assert.True (view.IsKeyUp); + } } }