Tig 9 kuukautta sitten
vanhempi
commit
3f3ceae9c7

+ 19 - 60
Terminal.Gui/View/View.Keyboard.cs

@@ -255,9 +255,7 @@ public partial class View // Keyboard APIs
     ///     <para>
     ///         If a more focused subview does not handle the key press, this method raises <see cref="OnKeyDown"/>/
     ///         <see cref="KeyDown"/> to allow the
-    ///         view to pre-process the key press. If <see cref="OnKeyDown"/>/<see cref="KeyDown"/> is not handled
-    ///         <see cref="InvokingKeyBindings"/>/<see cref="OnInvokingKeyBindings"/>  will be raised to invoke any key
-    ///         bindings.
+    ///         view to pre-process the key press. If <see cref="OnKeyDown"/>/<see cref="KeyDown"/> is not handled any commands bound to the key will be invoked.
     ///         Then, only if no key bindings are
     ///         handled, <see cref="OnKeyDownNotHandled"/>/<see cref="KeyDownNotHandled"/> will be raised allowing the view to
     ///         process the key press.
@@ -292,8 +290,8 @@ public partial class View // Keyboard APIs
 
         // During (this is what can be cancelled)
 
-        // TODO: NewKeyDownEvent returns bool. It should be bool? so state of RaiseInvokingKeyBindingsAndInvokeCommands can be reflected up stack
-        if (RaiseInvokingKeyBindingsAndInvokeCommands (key) is true || key.Handled)
+        // TODO: NewKeyDownEvent returns bool. It should be bool? so state of InvokeCommands can be reflected up stack
+        if (InvokeCommandsBoundToKey (key) is true || key.Handled)
         {
             return true;
         }
@@ -336,7 +334,7 @@ public partial class View // Keyboard APIs
 
     /// <summary>
     ///     Called when the user presses a key, allowing subscribers to pre-process the key down event. Called
-    ///     before <see cref="InvokingKeyBindings"/> and <see cref="KeyDownNotHandled"/> are raised. Set
+    ///     before key bindings are invoked and <see cref="KeyDownNotHandled"/> is raised. Set
     ///     <see cref="Key.Handled"/>
     ///     to true to
     ///     stop the key from being processed further.
@@ -357,7 +355,7 @@ public partial class View // Keyboard APIs
 
     /// <summary>
     ///     Raised when the user presses a key, allowing subscribers to pre-process the key down event. Called
-    ///     before <see cref="InvokingKeyBindings"/> and <see cref="KeyDownNotHandled"/> are raised. Set
+    ///     before key bindings are invoked and <see cref="KeyDownNotHandled"/> is raised. Set
     ///     <see cref="Key.Handled"/>
     ///     to true to
     ///     stop the key from being processed further.
@@ -508,8 +506,7 @@ public partial class View // Keyboard APIs
     private Dictionary<Command, CommandImplementation> CommandImplementations { get; } = new ();
 
     /// <summary>
-    ///     INTERNAL API: Raises the <see cref="InvokingKeyBindings"/> event and invokes the commands bound to
-    ///     <paramref name="key"/>.
+    ///     INTERNAL API: Invokes any commands bound to <paramref name="key"/> on this view, adornments, and subviews.
     /// </summary>
     /// <param name="key"></param>
     /// <returns>
@@ -517,28 +514,13 @@ public partial class View // Keyboard APIs
     ///     continue.
     ///     <see langword="false"/> if a command was invoked and was not handled (or cancelled); input processing should
     ///     continue.
-    ///     <see langword="true"/> if <see cref="InvokingKeyBindings"/> was handled or a command was invoked and handled (or
+    ///     <see langword="true"/> if at least one command was invoked and handled (or
     ///     cancelled); input processing should stop.
     /// </returns>
-    internal bool? RaiseInvokingKeyBindingsAndInvokeCommands (Key key)
+    internal bool? InvokeCommandsBoundToKey (Key key)
     {
         KeyBindingScope scope = KeyBindingScope.Focused | KeyBindingScope.HotKey;
 
-        // During
-        if (OnInvokingKeyBindings (key, scope))
-        {
-            return true;
-        }
-
-        InvokingKeyBindings?.Invoke (this, key);
-
-        if (key.Handled)
-        {
-            return true;
-        }
-
-        // After
-
         // * If no key binding was found, `InvokeKeyBindings` returns `null`.
         //   Continue passing the event (return `false` from `OnInvokeKeyBindings`).
         // * If key bindings were found, but none handled the key (all `Command`s returned `false`),
@@ -547,29 +529,29 @@ public partial class View // Keyboard APIs
         //   `InvokeKeyBindings` returns `true`. Continue passing the event (return `false` from `OnInvokeKeyBindings`).
         bool?  handled = InvokeCommands (key, scope);
 
-        if (handled is { } && (bool)handled)
+        if (handled is true)
         {
             // Stop processing if any key binding handled the key.
             // DO NOT stop processing if there are no matching key bindings or none of the key bindings handled the key
             return handled;
         }
 
-        if (Margin is { } && ProcessAdornmentKeyBindings (Margin, key, scope, ref handled))
+        if (Margin is { } && InvokeCommandsBoundToKeyOnAdornment (Margin, key, scope, ref handled))
         {
             return true;
         }
 
-        if (Padding is { } && ProcessAdornmentKeyBindings (Padding, key, scope, ref handled))
+        if (Padding is { } && InvokeCommandsBoundToKeyOnAdornment (Padding, key, scope, ref handled))
         {
             return true;
         }
 
-        if (Border is { } && ProcessAdornmentKeyBindings (Border, key, scope, ref handled))
+        if (Border is { } && InvokeCommandsBoundToKeyOnAdornment (Border, key, scope, ref handled))
         {
             return true;
         }
 
-        if (ProcessSubViewKeyBindings (key, scope, ref handled))
+        if (InvokeCommandsBoundToKeyOnSubviews (key, scope, ref handled))
         {
             return true;
         }
@@ -577,32 +559,9 @@ public partial class View // Keyboard APIs
         return handled;
     }
 
-    /// <summary>
-    ///     Called when a key is pressed that may be mapped to a key binding. Set <see cref="Key.Handled"/> to true to
-    ///     stop the key from being processed by other views.
-    /// </summary>
-    /// <remarks>
-    ///     <para>See <see href="../docs/keyboard.md">for an overview of Terminal.Gui keyboard APIs.</see></para>
-    /// </remarks>
-    /// <param name="key">Contains the details about the key that produced the event.</param>
-    /// <param name="scope">The scope.</param>
-    /// <returns>
-    ///     <see langword="false"/> if the event was raised and was not handled (or cancelled); input processing should
-    ///     continue.
-    ///     <see langword="true"/> if the event was raised and handled (or cancelled); input processing should stop.
-    /// </returns>
-    protected virtual bool OnInvokingKeyBindings (Key key, KeyBindingScope scope) { return false; }
-
-    // TODO: This does not carry KeyBindingScope, but OnInvokingKeyBindings does
-    /// <summary>
-    ///     Raised when a key is pressed that may be mapped to a key binding. Set <see cref="Key.Handled"/> to true to
-    ///     stop the key from being processed by other views.
-    /// </summary>
-    public event EventHandler<Key>? InvokingKeyBindings;
-
-    private bool ProcessAdornmentKeyBindings (Adornment adornment, Key key, KeyBindingScope scope, ref bool? handled)
+    private static bool InvokeCommandsBoundToKeyOnAdornment (Adornment adornment, Key key, KeyBindingScope scope, ref bool? handled)
     {
-        bool? adornmentHandled = adornment.RaiseInvokingKeyBindingsAndInvokeCommands (key);
+        bool? adornmentHandled = adornment.InvokeCommandsBoundToKey (key);
 
         if (adornmentHandled is true)
         {
@@ -616,7 +575,7 @@ public partial class View // Keyboard APIs
 
         foreach (View subview in adornment.Subviews)
         {
-            bool? subViewHandled = subview.RaiseInvokingKeyBindingsAndInvokeCommands (key);
+            bool? subViewHandled = subview.InvokeCommandsBoundToKey (key);
 
             if (subViewHandled is { })
             {
@@ -632,7 +591,7 @@ public partial class View // Keyboard APIs
         return false;
     }
 
-    private bool ProcessSubViewKeyBindings (Key key, KeyBindingScope scope, ref bool? handled, bool invoke = true)
+    private bool InvokeCommandsBoundToKeyOnSubviews (Key key, KeyBindingScope scope, ref bool? handled, bool invoke = true)
     {
         // Now, process any key bindings in the subviews that are tagged to KeyBindingScope.HotKey.
         foreach (View subview in Subviews)
@@ -654,7 +613,7 @@ public partial class View // Keyboard APIs
                     return true;
                 }
 
-                bool? subViewHandled = subview.RaiseInvokingKeyBindingsAndInvokeCommands (key);
+                bool? subViewHandled = subview.InvokeCommandsBoundToKey (key);
 
                 if (subViewHandled is { })
                 {
@@ -667,7 +626,7 @@ public partial class View // Keyboard APIs
                 }
             }
 
-            bool recurse = subview.ProcessSubViewKeyBindings (key, scope, ref handled, invoke);
+            bool recurse = subview.InvokeCommandsBoundToKeyOnSubviews (key, scope, ref handled, invoke);
 
             if (recurse || (handled is { } && (bool)handled))
             {

+ 1 - 1
Terminal.Gui/Views/Menu/Menu.cs

@@ -309,7 +309,7 @@ internal sealed class Menu : View
     protected override bool OnKeyDownNotHandled (Key keyEvent)
     {
         // We didn't handle the key, pass it on to host
-        return _host.RaiseInvokingKeyBindingsAndInvokeCommands (keyEvent) == true;
+        return _host.InvokeCommandsBoundToKey (keyEvent) == true;
     }
 
     private void Current_TerminalResized (object? sender, SizeChangedEventArgs e)

+ 96 - 70
UICatalog/Scenarios/Keys.cs

@@ -10,109 +10,136 @@ public class Keys : Scenario
     public override void Main ()
     {
         Application.Init ();
-        ObservableCollection<string> keyPressedList = [];
-        ObservableCollection<string> invokingKeyBindingsList = new ();
+        ObservableCollection<string> keyDownList = [];
+        ObservableCollection<string> keyDownNotHandledList = new ();
 
         var win = new Window { Title = GetQuitKeyAndName () };
-        var editLabel = new Label { X = 0, Y = 0, Text = "Type text here:" };
-        win.Add (editLabel);
 
-        var edit = new TextField { X = Pos.Right (editLabel) + 1, Y = Pos.Top (editLabel), Width = Dim.Fill (2) };
+        var label = new Label
+        {
+            X = 0,
+            Y = 0,
+            Text = "_Type text here:"
+        };
+        win.Add (label);
+
+        var edit = new TextField
+        {
+            X = Pos.Right (label) + 1,
+            Y = Pos.Top (label),
+            Width = Dim.Fill (2),
+            Height = 1,
+        };
         win.Add (edit);
 
-        edit.KeyDown += (s, a) => { keyPressedList.Add (a.ToString ()); };
+        label = new Label
+        {
+            X = 0,
+            Y = Pos.Bottom (label),
+            Text = "Last _Application.KeyDown:"
+        };
+        win.Add (label);
+        var labelAppKeypress = new Label
+        {
+            X = Pos.Right (label) + 1,
+            Y = Pos.Top (label)
+        };
+        win.Add (labelAppKeypress);
 
-        edit.InvokingKeyBindings += (s, a) =>
-                                    {
-                                        if (edit.KeyBindings.TryGet (a, out KeyBinding binding))
-                                        {
-                                            invokingKeyBindingsList.Add ($"{a}: {string.Join (",", binding.Commands)}");
-                                        }
-                                    };
+        Application.KeyDown += (s, e) => labelAppKeypress.Text = e.ToString ();
 
-        // Last KeyPress: ______
-        var keyPressedLabel = new Label
+        label = new ()
         {
-            X = Pos.Left (editLabel), Y = Pos.Top (editLabel) + 1, Text = "Last TextView.KeyPressed:"
+            X = 0,
+            Y = Pos.Bottom (label),
+            Text = "_Last TextField.KeyDown:"
         };
-        win.Add (keyPressedLabel);
-        var labelTextViewKeypress = new Label { X = Pos.Right (keyPressedLabel) + 1, Y = Pos.Top (keyPressedLabel) };
-        win.Add (labelTextViewKeypress);
+        win.Add (label);
 
-        edit.KeyDown += (s, e) => labelTextViewKeypress.Text = e.ToString ();
-
-        keyPressedLabel = new Label
+        var lastTextFieldKeyDownLabel = new Label
         {
-            X = Pos.Left (keyPressedLabel), Y = Pos.Bottom (keyPressedLabel), Text = "Last Application.KeyDown:"
+            X = Pos.Right (label) + 1,
+            Y = Pos.Top (label),
+            Height = 1,
         };
-        win.Add (keyPressedLabel);
-        var labelAppKeypress = new Label { X = Pos.Right (keyPressedLabel) + 1, Y = Pos.Top (keyPressedLabel) };
-        win.Add (labelAppKeypress);
+        win.Add (lastTextFieldKeyDownLabel);
 
-        Application.KeyDown += (s, e) => labelAppKeypress.Text = e.ToString ();
+        edit.KeyDown += (s, e) => lastTextFieldKeyDownLabel.Text = e.ToString ();
 
-        // Key stroke log:
-        var keyLogLabel = new Label
+        // Application key event log:
+        label = new Label
         {
-            X = Pos.Left (editLabel), Y = Pos.Top (editLabel) + 4, Text = "Application Key Events:"
+            X = 0,
+            Y = Pos.Bottom (label) + 1,
+            Text = "Application Key Events:"
         };
-        win.Add (keyLogLabel);
+        win.Add (label);
         int maxKeyString = Key.CursorRight.WithAlt.WithCtrl.WithShift.ToString ().Length;
-        var yOffset = 1;
-        ObservableCollection<string> keyEventlist = new ();
 
-        var keyEventListView = new ListView
+        ObservableCollection<string> keyEventList = new ();
+
+        var appKeyEventListView = new ListView
         {
             X = 0,
-            Y = Pos.Top (keyLogLabel) + yOffset,
-            Width = "Key Down:".Length + maxKeyString,
+            Y = Pos.Bottom (label),
+            Width = "KeyDown:".Length + maxKeyString,
             Height = Dim.Fill (),
-            Source = new ListWrapper<string> (keyEventlist)
+            Source = new ListWrapper<string> (keyEventList)
         };
-        keyEventListView.ColorScheme = Colors.ColorSchemes ["TopLevel"];
-        win.Add (keyEventListView);
-
-        // OnKeyPressed
-        var onKeyPressedLabel = new Label
+        appKeyEventListView.ColorScheme = Colors.ColorSchemes ["TopLevel"];
+        win.Add (appKeyEventListView);
+
+        // View key events...
+        edit.KeyDown += (s, a) => { keyDownList.Add (a.ToString ()); };
+
+        edit.KeyDownNotHandled += (s, a) =>
+                                  {
+                                      if (edit.KeyBindings.TryGet (a, out KeyBinding binding))
+                                      {
+                                          keyDownNotHandledList.Add ($"{a}: {string.Join (",", binding.Commands)}");
+                                      }
+                                  };
+
+        // KeyDown
+        label = new Label
         {
-            X = Pos.Right (keyEventListView) + 1, Y = Pos.Top (editLabel) + 4, Text = "TextView KeyDown:"
+            X = Pos.Right (appKeyEventListView) + 1,
+            Y = Pos.Top (label),
+            Text = "TextView Key Down:"
         };
-        win.Add (onKeyPressedLabel);
+        win.Add (label);
 
-        yOffset = 1;
-
-        var onKeyPressedListView = new ListView
+        var onKeyDownListView = new ListView
         {
-            X = Pos.Left (onKeyPressedLabel),
-            Y = Pos.Top (onKeyPressedLabel) + yOffset,
+            X = Pos.Left (label),
+            Y = Pos.Bottom (label),
             Width = maxKeyString,
             Height = Dim.Fill (),
-            Source = new ListWrapper<string> (keyPressedList)
+            Source = new ListWrapper<string> (keyDownList)
         };
-        onKeyPressedListView.ColorScheme = Colors.ColorSchemes ["TopLevel"];
-        win.Add (onKeyPressedListView);
+        onKeyDownListView.ColorScheme = Colors.ColorSchemes ["TopLevel"];
+        win.Add (onKeyDownListView);
 
-        // OnInvokeKeyBindings
-        var onInvokingKeyBindingsLabel = new Label
+        // KeyDownNotHandled
+        label = new Label
         {
-            X = Pos.Right (onKeyPressedListView) + 1,
-            Y = Pos.Top (editLabel) + 4,
-            Text = "TextView InvokingKeyBindings:"
+            X = Pos.Right (onKeyDownListView) + 1,
+            Y = Pos.Top (label),
+            Text = "TextView KeyDownNotHandled:"
         };
-        win.Add (onInvokingKeyBindingsLabel);
+        win.Add (label);
 
-        var onInvokingKeyBindingsListView = new ListView
+        var onKeyDownNotHandledListView = new ListView
         {
-            X = Pos.Left (onInvokingKeyBindingsLabel),
-            Y = Pos.Top (onInvokingKeyBindingsLabel) + yOffset,
-            Width = Dim.Fill (1),
+            X = Pos.Left (label),
+            Y = Pos.Bottom (label),
+            Width = maxKeyString,
             Height = Dim.Fill (),
-            Source = new ListWrapper<string> (invokingKeyBindingsList)
+            Source = new ListWrapper<string> (keyDownNotHandledList)
         };
-        onInvokingKeyBindingsListView.ColorScheme = Colors.ColorSchemes ["TopLevel"];
-        win.Add (onInvokingKeyBindingsListView);
+        onKeyDownNotHandledListView.ColorScheme = Colors.ColorSchemes ["TopLevel"];
+        win.Add (onKeyDownNotHandledListView);
 
-        //Application.KeyDown += (s, a) => KeyDownPressUp (a, "Down");
         Application.KeyDown += (s, a) => KeyDownPressUp (a, "Down");
         Application.KeyUp += (s, a) => KeyDownPressUp (a, "Up");
 
@@ -120,10 +147,9 @@ public class Keys : Scenario
         {
             // BUGBUG: KeyEvent.ToString is badly broken
             var msg = $"Key{updown,-7}: {args}";
-            keyEventlist.Add (msg);
-            keyEventListView.MoveDown ();
-            onKeyPressedListView.MoveDown ();
-            onInvokingKeyBindingsListView.MoveDown ();
+            keyEventList.Add (msg);
+            appKeyEventListView.MoveDown ();
+            onKeyDownNotHandledListView.MoveDown ();
         }
 
         Application.Run (win);

+ 1 - 1
UICatalog/Scenarios/VkeyPacketSimulator.cs

@@ -148,7 +148,7 @@ public class VkeyPacketSimulator : Scenario
                                }
                            };
 
-        tvInput.InvokingKeyBindings += (s, e) =>
+        tvInput.KeyDownNotHandled += (s, e) =>
                                        {
                                            Key ev = e;
 

+ 16 - 16
UnitTests/Application/KeyboardTests.cs

@@ -163,39 +163,39 @@ public class KeyboardTests
     public void KeyBinding_OnKeyDown ()
     {
         var view = new ScopedKeyBindingView ();
-        var invoked = false;
-        view.InvokingKeyBindings += (s, e) => invoked = true;
+        var keyWasHandled = false;
+        view.KeyDownNotHandled += (s, e) => keyWasHandled = true;
 
         var top = new Toplevel ();
         top.Add (view);
         Application.Begin (top);
 
         Application.RaiseKeyDownEvent (Key.A);
-        Assert.False (invoked);
+        Assert.False (keyWasHandled);
         Assert.True (view.ApplicationCommand);
 
-        invoked = false;
+        keyWasHandled = false;
         view.ApplicationCommand = false;
         Application.KeyBindings.Remove (KeyCode.A);
         Application.RaiseKeyDownEvent (Key.A); // old
-        Assert.False (invoked);
+        Assert.False (keyWasHandled);
         Assert.False (view.ApplicationCommand);
         Application.KeyBindings.Add (Key.A.WithCtrl, view, Command.Save);
         Application.RaiseKeyDownEvent (Key.A); // old
-        Assert.False (invoked);
+        Assert.False (keyWasHandled);
         Assert.False (view.ApplicationCommand);
         Application.RaiseKeyDownEvent (Key.A.WithCtrl); // new
-        Assert.False (invoked);
+        Assert.False (keyWasHandled);
         Assert.True (view.ApplicationCommand);
 
-        invoked = false;
+        keyWasHandled = false;
         Application.RaiseKeyDownEvent (Key.H);
-        Assert.True (invoked);
+        Assert.False (keyWasHandled);
 
-        invoked = false;
+        keyWasHandled = false;
         Assert.False (view.HasFocus);
         Application.RaiseKeyDownEvent (Key.F);
-        Assert.False (invoked);
+        Assert.False (keyWasHandled);
 
         Assert.True (view.ApplicationCommand);
         Assert.True (view.HotKeyCommand);
@@ -208,23 +208,23 @@ public class KeyboardTests
     public void KeyBinding_OnKeyDown_Negative ()
     {
         var view = new ScopedKeyBindingView ();
-        var invoked = false;
-        view.InvokingKeyBindings += (s, e) => invoked = true;
+        var keyWasHandled = false;
+        view.KeyDownNotHandled += (s, e) => keyWasHandled = true;
 
         var top = new Toplevel ();
         top.Add (view);
         Application.Begin (top);
 
         Application.RaiseKeyDownEvent (Key.A.WithCtrl);
-        Assert.False (invoked);
+        Assert.False (keyWasHandled);
         Assert.False (view.ApplicationCommand);
         Assert.False (view.HotKeyCommand);
         Assert.False (view.FocusedCommand);
 
-        invoked = false;
+        keyWasHandled = false;
         Assert.False (view.HasFocus);
         Application.RaiseKeyDownEvent (Key.Z);
-        Assert.False (invoked);
+        Assert.False (keyWasHandled);
         Assert.False (view.ApplicationCommand);
         Assert.False (view.HotKeyCommand);
         Assert.False (view.FocusedCommand);

+ 8 - 4
UnitTests/View/HotKeyTests.cs → UnitTests/View/Keyboard/HotKeyTests.cs

@@ -95,7 +95,7 @@ public class HotKeyTests
     {
         var view = new View ();
         view.KeyBindings.Add (Key.A, Command.HotKey); // implies KeyBindingScope.Focused - so this should not be invoked
-        view.InvokingKeyBindings += (s, e) => { Assert.Fail (); };
+        view.KeyDownNotHandled += (s, e) => { Assert.Fail (); };
 
         var superView = new View ();
         superView.Add (view);
@@ -109,8 +109,11 @@ public class HotKeyTests
     {
         var view = new View ();
         view.KeyBindings.Add (Key.A, KeyBindingScope.HotKey, Command.HotKey);
-        bool invoked = false;
-        view.InvokingKeyBindings += (s, e) => { invoked = true; };
+        bool hotKeyInvoked = false;
+        view.HandlingHotKey += (s, e) => { hotKeyInvoked = true; };
+
+        bool notHandled = false;
+        view.KeyDownNotHandled += (s, e) => { notHandled = true; };
 
         var superView = new View ();
         superView.Add (view);
@@ -118,7 +121,8 @@ public class HotKeyTests
         var ke = Key.A;
         superView.NewKeyDownEvent (ke);
 
-        Assert.True (invoked);
+        Assert.False (notHandled);
+        Assert.True (hotKeyInvoked);
     }
 
 

+ 23 - 176
UnitTests/View/Keyboard/KeyboardEventTests.cs

@@ -7,8 +7,8 @@ namespace Terminal.Gui.ViewTests;
 public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
 {
     /// <summary>
-    ///     This tests that when a new key down event is sent to the view  will fire the 3 key-down related
-    ///     events: KeyDown, InvokingKeyBindings, and ProcessKeyDown. Note that KeyUp is independent.
+    ///     This tests that when a new key down event is sent to the view  will fire the key-down related
+    ///     events: KeyDown and KeyDownNotHandled. Note that KeyUp is independent.
     /// </summary>
     [Theory]
     [MemberData (nameof (AllViewTypes))]
@@ -33,27 +33,18 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
                             keyDown = true;
                         };
 
-        var invokingKeyBindings = false;
-
-        view.InvokingKeyBindings += (s, a) =>
-                                    {
-                                        a.Handled = false; // don't handle it so the other events are called
-                                        invokingKeyBindings = true;
-                                    };
-
-        var keyDownProcessed = false;
+        var keyDownNotHandled = false;
 
         view.KeyDownNotHandled += (s, a) =>
                                {
                                    a.Handled = true;
-                                   keyDownProcessed = true;
+                                   keyDownNotHandled = true;
                                };
 
         // Key.Empty is invalid, but it's used here to test that the event is fired
         Assert.True (view.NewKeyDownEvent (Key.Empty)); // this will be true because the ProcessKeyDown event handled it
         Assert.True (keyDown);
-        Assert.True (invokingKeyBindings);
-        Assert.True (keyDownProcessed);
+        Assert.True (keyDownNotHandled);
         view.Dispose ();
     }
 
@@ -96,7 +87,7 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
     public void NewKeyDownUpEvents_Events_Are_Raised_With_Only_Key_Modifiers (bool shift, bool alt, bool control)
     {
         var keyDown = false;
-        var keyPressed = false;
+        var keyDownNotHandled = false;
         var keyUp = false;
 
         var view = new OnNewKeyTestView ();
@@ -112,7 +103,7 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
                             Assert.True (view.OnKeyDownCalled);
                             keyDown = true;
                         };
-        view.KeyDownNotHandled += (s, e) => { keyPressed = true; };
+        view.KeyDownNotHandled += (s, e) => { keyDownNotHandled = true; };
 
         view.KeyUp += (s, e) =>
                       {
@@ -125,11 +116,6 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
                           keyUp = true;
                       };
 
-        //view.ProcessKeyDownEvent (new (Key.Null | (shift ? Key.ShiftMask : 0) | (alt ? Key.AltMask : 0) | (control ? Key.CtrlMask : 0)));
-        //Assert.True (keyDown);
-        //Assert.True (view.OnKeyDownWasCalled);
-        //Assert.True (view.OnProcessKeyDownWasCalled);
-
         view.NewKeyDownEvent (
                               new (
                                    KeyCode.Null
@@ -138,7 +124,7 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
                                    | (control ? KeyCode.CtrlMask : 0)
                                   )
                              );
-        Assert.True (keyPressed);
+        Assert.True (keyDownNotHandled);
         Assert.True (view.OnKeyDownCalled);
         Assert.True (view.OnProcessKeyDownCalled);
 
@@ -154,107 +140,11 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
         Assert.True (view.OnKeyUpCalled);
     }
 
-    [Fact]
-    public void NewKeyDownEvent_InvokingKeyBindings_Handled_Cancels ()
-    {
-        var view = new View ();
-        var keyPressInvoked = false;
-        var invokingKeyBindingsInvoked = false;
-        var processKeyPressInvoked = false;
-        var setHandledTo = false;
-
-        view.KeyDown += (s, e) =>
-                        {
-                            keyPressInvoked = true;
-                            Assert.False (e.Handled);
-                            Assert.Equal (KeyCode.N, e.KeyCode);
-                        };
-
-        view.InvokingKeyBindings += (s, e) =>
-                                    {
-                                        invokingKeyBindingsInvoked = true;
-                                        e.Handled = setHandledTo;
-                                        Assert.Equal (setHandledTo, e.Handled);
-                                        Assert.Equal (KeyCode.N, e.KeyCode);
-                                    };
-
-        view.KeyDownNotHandled += (s, e) =>
-                               {
-                                   processKeyPressInvoked = true;
-                                   processKeyPressInvoked = true;
-                                   Assert.False (e.Handled);
-                                   Assert.Equal (KeyCode.N, e.KeyCode);
-                               };
-
-        view.NewKeyDownEvent (Key.N);
-        Assert.True (keyPressInvoked);
-        Assert.True (invokingKeyBindingsInvoked);
-        Assert.True (processKeyPressInvoked);
-
-        keyPressInvoked = false;
-        invokingKeyBindingsInvoked = false;
-        processKeyPressInvoked = false;
-        setHandledTo = true;
-        view.NewKeyDownEvent (Key.N);
-        Assert.True (keyPressInvoked);
-        Assert.True (invokingKeyBindingsInvoked);
-        Assert.False (processKeyPressInvoked);
-    }
-
-    [Fact]
-    public void NewKeyDownEvent_InvokingKeyBindings_Handled_True_Stops_Processing ()
-    {
-        var keyDown = false;
-        var invokingKeyBindings = false;
-        var keyPressed = false;
-
-        var view = new OnNewKeyTestView ();
-        Assert.True (view.CanFocus);
-        view.CancelVirtualMethods = false;
-
-        view.KeyDown += (s, e) =>
-                        {
-                            Assert.Equal (KeyCode.A, e.KeyCode);
-                            Assert.False (keyDown);
-                            Assert.True (view.OnKeyDownCalled);
-                            e.Handled = false;
-                            keyDown = true;
-                        };
-
-        view.InvokingKeyBindings += (s, e) =>
-                                    {
-                                        Assert.Equal (KeyCode.A, e.KeyCode);
-                                        Assert.False (keyPressed);
-                                        Assert.True (view.OnInvokingKeyBindingsCalled);
-                                        e.Handled = true;
-                                        invokingKeyBindings = true;
-                                    };
-
-        view.KeyDownNotHandled += (s, e) =>
-                               {
-                                   Assert.Equal (KeyCode.A, e.KeyCode);
-                                   Assert.False (keyPressed);
-                                   Assert.False (view.OnProcessKeyDownCalled);
-                                   e.Handled = true;
-                                   keyPressed = true;
-                               };
-
-        view.NewKeyDownEvent (Key.A);
-        Assert.True (keyDown);
-        Assert.True (invokingKeyBindings);
-        Assert.False (keyPressed);
-
-        Assert.True (view.OnKeyDownCalled);
-        Assert.True (view.OnInvokingKeyBindingsCalled);
-        Assert.False (view.OnProcessKeyDownCalled);
-    }
-
     [Fact]
     public void NewKeyDownEvent_Handled_True_Stops_Processing ()
     {
         var keyDown = false;
-        var invokingKeyBindings = false;
-        var keyPressed = false;
+        var keyDownNotHandled = false;
 
         var view = new OnNewKeyTestView ();
         Assert.True (view.CanFocus);
@@ -269,31 +159,21 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
                             keyDown = true;
                         };
 
-        view.InvokingKeyBindings += (s, e) =>
-                                    {
-                                        Assert.Equal (KeyCode.A, e.KeyCode);
-                                        Assert.False (keyPressed);
-                                        Assert.False (view.OnInvokingKeyBindingsCalled);
-                                        e.Handled = true;
-                                        invokingKeyBindings = true;
-                                    };
 
         view.KeyDownNotHandled += (s, e) =>
                                {
                                    Assert.Equal (KeyCode.A, e.KeyCode);
-                                   Assert.False (keyPressed);
+                                   Assert.False (keyDownNotHandled);
                                    Assert.False (view.OnProcessKeyDownCalled);
                                    e.Handled = true;
-                                   keyPressed = true;
+                                   keyDownNotHandled = true;
                                };
 
         view.NewKeyDownEvent (Key.A);
         Assert.True (keyDown);
-        Assert.False (invokingKeyBindings);
-        Assert.False (keyPressed);
+        Assert.False (keyDownNotHandled);
 
         Assert.True (view.OnKeyDownCalled);
-        Assert.False (view.OnInvokingKeyBindingsCalled);
         Assert.False (view.OnProcessKeyDownCalled);
     }
 
@@ -301,8 +181,7 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
     public void NewKeyDownEvent_KeyDown_Handled_Stops_Processing ()
     {
         var view = new View ();
-        var invokingKeyBindingsInvoked = false;
-        var processKeyPressInvoked = false;
+        var keyDownNotHandled = false;
         var setHandledTo = false;
 
         view.KeyDown += (s, e) =>
@@ -312,38 +191,27 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
                             Assert.Equal (KeyCode.N, e.KeyCode);
                         };
 
-        view.InvokingKeyBindings += (s, e) =>
-                                    {
-                                        invokingKeyBindingsInvoked = true;
-                                        Assert.False (e.Handled);
-                                        Assert.Equal (KeyCode.N, e.KeyCode);
-                                    };
-
         view.KeyDownNotHandled += (s, e) =>
                                {
-                                   processKeyPressInvoked = true;
+                                   keyDownNotHandled = true;
                                    Assert.False (e.Handled);
                                    Assert.Equal (KeyCode.N, e.KeyCode);
                                };
 
         view.NewKeyDownEvent (Key.N);
-        Assert.True (invokingKeyBindingsInvoked);
-        Assert.True (processKeyPressInvoked);
+        Assert.True (keyDownNotHandled);
 
-        invokingKeyBindingsInvoked = false;
-        processKeyPressInvoked = false;
+        keyDownNotHandled = false;
         setHandledTo = true;
         view.NewKeyDownEvent (Key.N);
-        Assert.False (invokingKeyBindingsInvoked);
-        Assert.False (processKeyPressInvoked);
+        Assert.False (keyDownNotHandled);
     }
 
     [Fact]
     public void NewKeyDownEvent_ProcessKeyDown_Handled_Stops_Processing ()
     {
         var keyDown = false;
-        var invokingKeyBindings = false;
-        var processKeyDown = false;
+        var keyDownNotHandled = false;
 
         var view = new OnNewKeyTestView ();
         Assert.True (view.CanFocus);
@@ -358,31 +226,20 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
                             keyDown = true;
                         };
 
-        view.InvokingKeyBindings += (s, e) =>
-                                    {
-                                        Assert.Equal (KeyCode.A, e.KeyCode);
-                                        Assert.False (processKeyDown);
-                                        Assert.True (view.OnInvokingKeyBindingsCalled);
-                                        e.Handled = false;
-                                        invokingKeyBindings = true;
-                                    };
-
         view.KeyDownNotHandled += (s, e) =>
                                {
                                    Assert.Equal (KeyCode.A, e.KeyCode);
-                                   Assert.False (processKeyDown);
+                                   Assert.False (keyDownNotHandled);
                                    Assert.True (view.OnProcessKeyDownCalled);
                                    e.Handled = true;
-                                   processKeyDown = true;
+                                   keyDownNotHandled = true;
                                };
 
         view.NewKeyDownEvent (Key.A);
         Assert.True (keyDown);
-        Assert.True (invokingKeyBindings);
-        Assert.True (processKeyDown);
+        Assert.True (keyDownNotHandled);
 
         Assert.True (view.OnKeyDownCalled);
-        Assert.True (view.OnInvokingKeyBindingsCalled);
         Assert.True (view.OnProcessKeyDownCalled);
     }
 
@@ -409,7 +266,6 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
 
         Assert.True (view.OnKeyUpCalled);
         Assert.False (view.OnKeyDownCalled);
-        Assert.False (view.OnInvokingKeyBindingsCalled);
         Assert.False (view.OnProcessKeyDownCalled);
     }
 
@@ -417,12 +273,12 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
     [InlineData (null, null)]
     [InlineData (true, true)]
     [InlineData (false, false)]
-    public void RaiseInvokingKeyBindingsAndInvokeCommands_Returns_Nullable_Properly (bool? toReturn, bool? expected)
+    public void InvokeCommandsBoundToKey_Returns_Nullable_Properly (bool? toReturn, bool? expected)
     {
         var view = new KeyBindingsTestView ();
         view.CommandReturns = toReturn;
 
-        bool? result = view.RaiseInvokingKeyBindingsAndInvokeCommands (Key.A);
+        bool? result = view.InvokeCommandsBoundToKey (Key.A);
         Assert.Equal (expected, result);
     }
 
@@ -444,20 +300,11 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
     {
         public OnNewKeyTestView () { CanFocus = true; }
         public bool CancelVirtualMethods { set; private get; }
-        public bool OnInvokingKeyBindingsCalled { get; set; }
         public bool OnKeyDownCalled { get; set; }
         public bool OnProcessKeyDownCalled { get; set; }
         public bool OnKeyUpCalled { get; set; }
         public override string Text { get; set; }
 
-        protected override bool OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope)
-        {
-
-            OnInvokingKeyBindingsCalled = true;
-
-            return CancelVirtualMethods;
-        }
-
         protected override bool OnKeyDown (Key keyEvent)
         {
             OnKeyDownCalled = true;

+ 28 - 26
UnitTests/View/ViewKeyBindingTests.cs → UnitTests/View/Keyboard/ViewKeyBindingTests.cs

@@ -11,37 +11,38 @@ public class ViewKeyBindingTests (ITestOutputHelper output)
     public void Focus_KeyBinding ()
     {
         var view = new ScopedKeyBindingView ();
-        var invoked = false;
-        view.InvokingKeyBindings += (s, e) => invoked = true;
+        var keyWasHandled = false;
+        view.KeyDownNotHandled += (s, e) => keyWasHandled = true;
 
         var top = new Toplevel ();
         top.Add (view);
         Application.Begin (top);
 
         Application.RaiseKeyDownEvent (Key.A);
-        Assert.False (invoked);
+        Assert.False (keyWasHandled);
         Assert.True (view.ApplicationCommand);
 
-        invoked = false;
+        keyWasHandled = false;
         Application.RaiseKeyDownEvent (Key.H);
-        Assert.True (invoked);
+        Assert.True (view.HotKeyCommand);
+        Assert.False (keyWasHandled);
 
-        invoked = false;
+        keyWasHandled = false;
         Assert.False (view.HasFocus);
         Application.RaiseKeyDownEvent (Key.F);
-        Assert.False (invoked);
+        Assert.False (keyWasHandled);
         Assert.False (view.FocusedCommand);
 
-        invoked = false;
+        keyWasHandled = false;
         view.CanFocus = true;
         view.SetFocus ();
         Assert.True (view.HasFocus);
         Application.RaiseKeyDownEvent (Key.F);
-        Assert.True (invoked);
+        Assert.True (view.FocusedCommand);
+        Assert.False (keyWasHandled); // Command was invoked, but wasn't handled
 
         Assert.True (view.ApplicationCommand);
         Assert.True (view.HotKeyCommand);
-        Assert.True (view.FocusedCommand);
         top.Dispose ();
     }
 
@@ -50,23 +51,23 @@ public class ViewKeyBindingTests (ITestOutputHelper output)
     public void Focus_KeyBinding_Negative ()
     {
         var view = new ScopedKeyBindingView ();
-        var invoked = false;
-        view.InvokingKeyBindings += (s, e) => invoked = true;
+        var keyWasHandled = false;
+        view.KeyDownNotHandled += (s, e) => keyWasHandled = true;
 
         var top = new Toplevel ();
         top.Add (view);
         Application.Begin (top);
 
         Application.RaiseKeyDownEvent (Key.Z);
-        Assert.False (invoked);
+        Assert.False (keyWasHandled);
         Assert.False (view.ApplicationCommand);
         Assert.False (view.HotKeyCommand);
         Assert.False (view.FocusedCommand);
 
-        invoked = false;
+        keyWasHandled = false;
         Assert.False (view.HasFocus);
         Application.RaiseKeyDownEvent (Key.F);
-        Assert.False (invoked);
+        Assert.False (keyWasHandled);
         Assert.False (view.ApplicationCommand);
         Assert.False (view.HotKeyCommand);
         Assert.False (view.FocusedCommand);
@@ -78,28 +79,29 @@ public class ViewKeyBindingTests (ITestOutputHelper output)
     public void HotKey_KeyBinding ()
     {
         var view = new ScopedKeyBindingView ();
-        var invoked = false;
-        view.InvokingKeyBindings += (s, e) => invoked = true;
+        var keyWasHandled = false;
+        view.KeyDownNotHandled += (s, e) => keyWasHandled = true;
 
         var top = new Toplevel ();
         top.Add (view);
         Application.Begin (top);
 
-        invoked = false;
+        keyWasHandled = false;
         Application.RaiseKeyDownEvent (Key.H);
-        Assert.True (invoked);
         Assert.True (view.HotKeyCommand);
+        Assert.False (keyWasHandled);
 
         view.HotKey = KeyCode.Z;
-        invoked = false;
+        keyWasHandled = false;
         view.HotKeyCommand = false;
         Application.RaiseKeyDownEvent (Key.H); // old hot key
-        Assert.False (invoked);
+        Assert.False (keyWasHandled);
         Assert.False (view.HotKeyCommand);
 
         Application.RaiseKeyDownEvent (Key.Z); // new hot key
-        Assert.True (invoked);
         Assert.True (view.HotKeyCommand);
+        Assert.False (keyWasHandled);
+
         top.Dispose ();
     }
 
@@ -108,18 +110,18 @@ public class ViewKeyBindingTests (ITestOutputHelper output)
     public void HotKey_KeyBinding_Negative ()
     {
         var view = new ScopedKeyBindingView ();
-        var invoked = false;
-        view.InvokingKeyBindings += (s, e) => invoked = true;
+        var keyWasHandled = false;
+        view.KeyDownNotHandled += (s, e) => keyWasHandled = true;
 
         var top = new Toplevel ();
         top.Add (view);
         Application.Begin (top);
 
         Application.RaiseKeyDownEvent (Key.Z);
-        Assert.False (invoked);
+        Assert.False (keyWasHandled);
         Assert.False (view.HotKeyCommand);
 
-        invoked = false;
+        keyWasHandled = false;
         Application.RaiseKeyDownEvent (Key.F);
         Assert.False (view.HotKeyCommand);
         top.Dispose ();

+ 19 - 19
docfx/docs/keyboard.md

@@ -59,22 +59,25 @@ The Command can be invoked even if the `View` that defines them is not focused o
 
 ### **Handling Keyboard Events**
 
-Keyboard events are retrieved from [Console Drivers](drivers.md) and passed on 
-to the [Application](~/api/Terminal.Gui.Application.yml) class by the [Main Loop](mainloop.md). 
+Keyboard events are retrieved from [Console Drivers](drivers.md) each iteration of the [Application](~/api/Terminal.Gui.Application.yml) [Main Loop](mainloop.md). The console driver raises the @Terminal.Gui.ConsoleDriver.KeyDown and @Terminal.Gui.ConsoleDriver.KeyUp events which invoke @Terminal.Gui.Application.RaiseKeyDown(Terminal.Gui.Key) and @Terminal.Gui.Application.RaiseKeyUp(Terminal.Gui.Key) respectively.
 
-[Application](~/api/Terminal.Gui.Application.yml) then determines the current [Toplevel](~/api/Terminal.Gui.Toplevel.yml) view
-(either the default created by calling @Terminal.Gui.Application.Init(Terminal.Gui.ConsoleDriver,System.String), or the one set by calling `Application.Run`). The mouse event, using [Viewport-relative coordinates](xref:Terminal.Gui.View.Viewport) is then passed to the @Terminal.Gui.View.NewKeyDownEvent(Terminal.Gui.Key) method of the current [Toplevel](~/api/Terminal.Gui.Toplevel.yml) view. 
+    NOTE: Not all drivers/platforms support sensing distinct KeyUp events. These drivers will simulate KeyUp events by raising @Terminal.Gui.ConsoleDriver.KeyUp after @Terminal.Gui.ConsoleDriver.KeyDown.
 
-If the view is enabled, the @Terminal.Gui.View.NewKeyDownEvent(Terminal.Gui.Key) method will do the following: 
+@Terminal.Gui.Application.RaiseKeyDown(Terminal.Gui.Key) raises @Terminal.Gui.Application.KeyDown and then calls @Terminal.Gui.View.NewKeyDownEvent(Terminal.Gui.Key) on all toplevel Views. If no View handles the key event, any Application-scoped key bindings will be invoked.
 
-1) If the view has a subview that has focus, 'ProcessKeyDown' on the focused view will be called. If the focused view handles the key press, processing stops.
-2) If there is no focused sub-view, or the focused sub-view does not handle the key press, @Terminal.Gui.View.OnKeyDown(Terminal.Gui.Key) will be called. If the view handles the key press, processing stops.
-3) If the view does not handle the key press, @Terminal.Gui.TextField.OnInvokingKeyBindings(Terminal.Gui.Key,Terminal.Gui.KeyBindingScope) will be called. This method calls @Terminal.Gui.View.InvokeKeyBindings(Terminal.Gui.Key,Terminal.Gui.KeyBindingScope) to invoke any keys bound to commands. If the key is bound and any of it's command handlers return true, processing stops.
-4) If the key is not bound, or the bound command handlers do not return true, @Terminal.Gui.View.OnProcessKeyDown(Terminal.Gui.Key) is called. If the view handles the key press, processing stops.
[email protected](Terminal.Gui.Key) raises @Terminal.Gui.Application.KeyDown and then calls @Terminal.Gui.View.NewKeyUpEvent(Terminal.Gui.Key) on all toplevel Views.
+
+If a view is enabled, the @Terminal.Gui.View.NewKeyDownEvent(Terminal.Gui.Key) method will do the following: 
+
+1) If the view has a subview that has focus, 'NewKeyDown' on the focused view will be called. This is recursive. If the most-focused view handles the key press, processing stops.
+2) If there is no most-focused sub-view, or a most-focused sub-view does not handle the key press, @Terminal.Gui.View.OnKeyDown(Terminal.Gui.Key) will be called. If the view handles the key press, processing stops.
+3) If @Terminal.Gui.View.OnKeyDown(Terminal.Gui.Key) does not handle the event. @Terminal.Gui.View.KeyDown will be raised.
+4) If the view does not handle the key down event, any bindings for the key will be invoked (see @Terminal.Gui.View.KeyBindings). If the key is bound and any of it's command handlers return true, processing stops.
+5) If the key is not bound, or the bound command handlers do not return true, @Terminal.Gui.View.OnKeyDownNotHandled(Terminal.Gui.Key) is called. 
 
 ## **Application Key Handling**
 
-To define application key handling logic for an entire application in cases where the methods listed above are not suitable, use the `Application.OnKeyDown` event. 
+To define application key handling logic for an entire application in cases where the methods listed above are not suitable, use the @Terminal.Gui.Application.KeyDown event. 
 
 ## **Key Down/Up Events**
 
@@ -90,17 +93,14 @@ To define application key handling logic for an entire application in cases wher
     - `NewKeyDownEvent` is called on the most-focused SubView (if any) that has focus. If that call returns true, the method returns.
     - Calls `OnKeyDown`.
   - **During**
-    - Assuming `OnKeyDown` call returns false (indicating the key wasn't handled)
-       - `OnInvokingKeyBindings` is called to invoke any bound commands.
-       - `OnInvokingKeyBindings` fires the `InvokingKeyBindings` event
+    - Assuming `OnKeyDown` call returns false (indicating the key wasn't handled) any commands bound to the key will be invoked.
   - **After**
-    - Assuming `OnInvokingKeyBindings` returns false (indicating the key wasn't handled)
-       - `OnProcessKeyDown` is called to process the key.
-       - `OnProcessKeyDown` fires the `ProcessKeyDown` event
+    - Assuming no keybinding was found or all invoked commands were not handled:
+       - `OnKeyDownNotHandled` is called to process the key.
+       - `KeyDownNotHandled` is raised.
 
-- Subclasses of `View` can (rarely) override `OnKeyDown` to see keys before they are processed by `OnInvokingKeyBindings` and `OnProcessKeyDown
-- Subclasses of `View` can (rarely) override `OnInvokingKeyBindings` to see keys before they are processed by `OnProcessKeyDown`
-- Subclasses of `View` can (often) override `OnProcessKeyDown` to do normal key processing.
+- Subclasses of `View` can (rarely) override `OnKeyDown` (or subscribe to `KeyDown`) to see keys before they are processed 
+- Subclasses of `View` can (often) override `OnKeyDownNotHandled` to do key processing for keys that were not previously handled. `TextField` and `TextView` are examples.
 
 ## ConsoleDriver