瀏覽代碼

Moved Toplevel keybindings out of Toplevel to Application.
Still need to move navigation code out of Toplevel

Tig 1 年之前
父節點
當前提交
c03dd32031
共有 36 個文件被更改,包括 954 次插入789 次删除
  1. 84 55
      Terminal.Gui/Application/Application.Keyboard.cs
  2. 1 1
      Terminal.Gui/Application/Application.cs
  3. 16 0
      Terminal.Gui/Input/KeyBinding.cs
  4. 120 18
      Terminal.Gui/Input/KeyBindings.cs
  5. 10 5
      Terminal.Gui/View/ViewKeyboard.cs
  6. 13 13
      Terminal.Gui/Views/DateField.cs
  7. 5 5
      Terminal.Gui/Views/FileDialog.cs
  8. 3 0
      Terminal.Gui/Views/Menu/Menu.cs
  9. 1 0
      Terminal.Gui/Views/Menu/MenuBarItem.cs
  10. 0 2
      Terminal.Gui/Views/MenuBarv2.cs
  11. 269 261
      Terminal.Gui/Views/Shortcut.cs
  12. 4 0
      Terminal.Gui/Views/Slider.cs
  13. 0 2
      Terminal.Gui/Views/StatusBar.cs
  14. 1 1
      Terminal.Gui/Views/TableView/CheckBoxTableSourceWrapper.cs
  15. 1 1
      Terminal.Gui/Views/TableView/TableView.cs
  16. 4 1
      Terminal.Gui/Views/TextField.cs
  17. 0 1
      Terminal.Gui/Views/TextValidateField.cs
  18. 9 9
      Terminal.Gui/Views/TextView.cs
  19. 13 13
      Terminal.Gui/Views/TimeField.cs
  20. 8 8
      Terminal.Gui/Views/Toplevel.cs
  21. 1 1
      Terminal.Gui/Views/TreeView/TreeView.cs
  22. 20 17
      UICatalog/Scenarios/KeyBindings.cs
  23. 1 1
      UICatalog/Scenarios/ListColumns.cs
  24. 1 1
      UICatalog/Scenarios/TableEditor.cs
  25. 2 0
      UICatalog/UICatalog.cs
  26. 2 2
      UnitTests/Application/ApplicationTests.cs
  27. 61 71
      UnitTests/Application/KeyboardTests.cs
  28. 32 32
      UnitTests/Input/KeyBindingTests.cs
  29. 1 187
      UnitTests/View/MouseTests.cs
  30. 204 15
      UnitTests/View/NavigationTests.cs
  31. 3 2
      UnitTests/View/ViewKeyBindingTests.cs
  32. 4 4
      UnitTests/Views/AllViewsTests.cs
  33. 37 41
      UnitTests/Views/OverlappedTests.cs
  34. 4 4
      UnitTests/Views/TableViewTests.cs
  35. 18 14
      UnitTests/Views/ToplevelTests.cs
  36. 1 1
      UnitTests/Views/TreeTableSourceTests.cs

+ 84 - 55
Terminal.Gui/Application/Application.Keyboard.cs

@@ -1,5 +1,6 @@
 #nullable enable
 #nullable enable
 using System.Text.Json.Serialization;
 using System.Text.Json.Serialization;
+using static System.Formats.Asn1.AsnWriter;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
@@ -19,6 +20,15 @@ public static partial class Application // Keyboard handling
             {
             {
                 Key oldKey = _alternateForwardKey;
                 Key oldKey = _alternateForwardKey;
                 _alternateForwardKey = value;
                 _alternateForwardKey = value;
+
+                if (_alternateForwardKey == Key.Empty)
+                {
+                    KeyBindings.Remove (_alternateForwardKey);
+                }
+                else
+                {
+                    KeyBindings.ReplaceKey (oldKey, _alternateForwardKey);
+                }
                 OnAlternateForwardKeyChanged (new (oldKey, value));
                 OnAlternateForwardKeyChanged (new (oldKey, value));
             }
             }
         }
         }
@@ -47,6 +57,16 @@ public static partial class Application // Keyboard handling
             {
             {
                 Key oldKey = _alternateBackwardKey;
                 Key oldKey = _alternateBackwardKey;
                 _alternateBackwardKey = value;
                 _alternateBackwardKey = value;
+
+                if (_alternateBackwardKey == Key.Empty)
+                {
+                    KeyBindings.Remove (_alternateBackwardKey);
+                }
+                else
+                {
+                    KeyBindings.ReplaceKey (oldKey, _alternateBackwardKey);
+                }
+
                 OnAlternateBackwardKeyChanged (new (oldKey, value));
                 OnAlternateBackwardKeyChanged (new (oldKey, value));
             }
             }
         }
         }
@@ -75,6 +95,14 @@ public static partial class Application // Keyboard handling
             {
             {
                 Key oldKey = _quitKey;
                 Key oldKey = _quitKey;
                 _quitKey = value;
                 _quitKey = value;
+                if (_quitKey == Key.Empty)
+                {
+                    KeyBindings.Remove (_quitKey);
+                }
+                else
+                {
+                    KeyBindings.ReplaceKey (oldKey, _quitKey);
+                }
                 OnQuitKeyChanged (new (oldKey, value));
                 OnQuitKeyChanged (new (oldKey, value));
             }
             }
         }
         }
@@ -139,26 +167,55 @@ public static partial class Application // Keyboard handling
             }
             }
         }
         }
 
 
-        // Invoke any global (Application-scoped) KeyBindings.
+        // Invoke any Application-scoped KeyBindings.
         // The first view that handles the key will stop the loop.
         // The first view that handles the key will stop the loop.
-        foreach (KeyValuePair<Key, List<View?>> binding in _keyBindings.Where (b => b.Key == keyEvent.KeyCode))
+        foreach (var binding in KeyBindings.Bindings.Where (b => b.Key == keyEvent.KeyCode))
         {
         {
-            foreach (View view in binding.Value)
+            if (binding.Value.BoundView is { })
             {
             {
-                if (view is { }
-                    && view.KeyBindings.TryGet (binding.Key, KeyBindingScope.Focused | KeyBindingScope.HotKey | KeyBindingScope.Application, out KeyBinding kb))
+                bool? handled = binding.Value.BoundView?.InvokeCommands (binding.Value.Commands, binding.Key, binding.Value);
+
+                if (handled != null && (bool)handled)
                 {
                 {
-                    //bool? handled = view.InvokeCommands (kb.Commands, binding.Key, kb);
-                    bool? handled = view?.OnInvokingKeyBindings (keyEvent, kb.Scope);
+                    return true;
+                }
+            }
+            else
+            {
+                if (!KeyBindings.TryGet (keyEvent, KeyBindingScope.Application, out KeyBinding appBinding))
+                {
+                    continue;
+                }
 
 
-                    if (handled != null && (bool)handled)
+                bool? toReturn = null;
+
+                foreach (Command command in appBinding.Commands)
+                {
+                    if (!CommandImplementations.ContainsKey (command))
                     {
                     {
-                        return true;
+                        throw new NotSupportedException (
+                                                         @$"A KeyBinding was set up for the command {command} ({keyEvent}) but that command is not supported by Application."
+                                                        );
+                    }
+
+                    if (CommandImplementations.TryGetValue (command, out Func<CommandContext, bool?>? implementation))
+                    {
+                        var context = new CommandContext (command, keyEvent, appBinding); // Create the context here
+                        toReturn = implementation (context);
+                    }
+
+                    // if ever see a true then that's what we will return
+                    if (toReturn ?? false)
+                    {
+                        toReturn = true;
                     }
                     }
                 }
                 }
+
+                return toReturn ?? true;
             }
             }
         }
         }
 
 
+
         return false;
         return false;
     }
     }
 
 
@@ -344,7 +401,7 @@ public static partial class Application // Keyboard handling
                     Command.Refresh,
                     Command.Refresh,
                     () =>
                     () =>
                     {
                     {
-                        Refresh (); 
+                        Refresh ();
 
 
                         return true;
                         return true;
                     }
                     }
@@ -370,7 +427,7 @@ public static partial class Application // Keyboard handling
 
 
         if (Environment.OSVersion.Platform == PlatformID.Unix)
         if (Environment.OSVersion.Platform == PlatformID.Unix)
         {
         {
-            KeyBindings.Add (Key.Z.WithCtrl, Command.Suspend);
+            KeyBindings.Add (Key.Z.WithCtrl, KeyBindingScope.Application, Command.Suspend);
         }
         }
 
 
 #if UNIX_KEY_BINDINGS
 #if UNIX_KEY_BINDINGS
@@ -398,37 +455,16 @@ public static partial class Application // Keyboard handling
                           .ToList ();
                           .ToList ();
     }
     }
 
 
-    /// <summary>
-    ///     Gets the list of Views that have <see cref="KeyBindingScope.Application"/> key bindings for the specified key.
-    /// </summary>
-    /// <remarks>
-    ///     This is an internal method used by the <see cref="View"/> class to add Application key bindings.
-    /// </remarks>
-    /// <param name="key">The key to check.</param>
-    /// <param name="views">Outputs the list of views bound to <paramref name="key"/></param>
-    /// <returns><see langword="True"/> if successful.</returns>
-    internal static bool TryGetKeyBindings (Key key, out List<View> views) { return _keyBindings.TryGetValue (key, out views); }
-
-    /// <summary>
-    ///     Removes an <see cref="KeyBindingScope.Application"/> scoped key binding.
-    /// </summary>
-    /// <remarks>
-    ///     This is an internal method used by the <see cref="View"/> class to remove Application key bindings.
-    /// </remarks>
-    /// <param name="key">The key that was bound.</param>
-    /// <param name="view">The view that is bound to the key.</param>
-    internal static void RemoveKeyBinding (Key key, View view)
-    {
-        if (_keyBindings.TryGetValue (key, out List<View> views))
-        {
-            views.Remove (view);
-
-            if (views.Count == 0)
-            {
-                _keyBindings.Remove (key);
-            }
-        }
-    }
+    ///// <summary>
+    /////     Gets the list of Views that have <see cref="KeyBindingScope.Application"/> key bindings for the specified key.
+    ///// </summary>
+    ///// <remarks>
+    /////     This is an internal method used by the <see cref="View"/> class to add Application key bindings.
+    ///// </remarks>
+    ///// <param name="key">The key to check.</param>
+    ///// <param name="views">Outputs the list of views bound to <paramref name="key"/></param>
+    ///// <returns><see langword="True"/> if successful.</returns>
+    //internal static bool TryGetKeyBindings (Key key, out List<View> views) { return _keyBindings.TryGetValue (key, out views); }
 
 
     /// <summary>
     /// <summary>
     ///     Removes all <see cref="KeyBindingScope.Application"/> scoped key bindings for the specified view.
     ///     Removes all <see cref="KeyBindingScope.Application"/> scoped key bindings for the specified view.
@@ -437,19 +473,12 @@ public static partial class Application // Keyboard handling
     ///     This is an internal method used by the <see cref="View"/> class to remove Application key bindings.
     ///     This is an internal method used by the <see cref="View"/> class to remove Application key bindings.
     /// </remarks>
     /// </remarks>
     /// <param name="view">The view that is bound to the key.</param>
     /// <param name="view">The view that is bound to the key.</param>
-    internal static void ClearKeyBindings (View view)
+    internal static void RemoveKeyBindings (View view)
     {
     {
-        foreach (Key key in _keyBindings.Keys)
-        {
-            _keyBindings [key].Remove (view);
-        }
+        var list = KeyBindings.Bindings
+                          .Where (kv => kv.Value.Scope != KeyBindingScope.Application)
+                          .Select (kv => kv.Value)
+                          .Distinct ()
+                          .ToList ();
     }
     }
-
-    /// <summary>
-    ///     Removes all <see cref="KeyBindingScope.Application"/> scoped key bindings for the specified view.
-    /// </summary>
-    /// <remarks>
-    ///     This is an internal method used by the <see cref="View"/> class to remove Application key bindings.
-    /// </remarks>
-    internal static void ClearKeyBindings () { _keyBindings.Clear (); }
 }
 }

+ 1 - 1
Terminal.Gui/Application/Application.cs

@@ -126,7 +126,7 @@ public static partial class Application
         KeyDown = null;
         KeyDown = null;
         KeyUp = null;
         KeyUp = null;
         SizeChanging = null;
         SizeChanging = null;
-        ClearKeyBindings ();
+        KeyBindings.Clear ();
 
 
         Colors.Reset ();
         Colors.Reset ();
 
 

+ 16 - 0
Terminal.Gui/Input/KeyBinding.cs

@@ -21,12 +21,28 @@ public record struct KeyBinding
         Context = context;
         Context = context;
     }
     }
 
 
+    /// <summary>Initializes a new instance.</summary>
+    /// <param name="commands">The commands this key binding will invoke.</param>
+    /// <param name="scope">The scope of the <see cref="Commands"/>.</param>
+    /// <param name="boundView">The view the key binding is bound to.</param>
+    /// <param name="context">Arbitrary context that can be associated with this key binding.</param>
+    public KeyBinding (Command [] commands, KeyBindingScope scope, View? boundView, object? context = null)
+    {
+        Commands = commands;
+        Scope = scope;
+        BoundView = boundView;
+        Context = context;
+    }
+
     /// <summary>The commands this key binding will invoke.</summary>
     /// <summary>The commands this key binding will invoke.</summary>
     public Command [] Commands { get; set; }
     public Command [] Commands { get; set; }
 
 
     /// <summary>The scope of the <see cref="Commands"/>.</summary>
     /// <summary>The scope of the <see cref="Commands"/>.</summary>
     public KeyBindingScope Scope { get; set; }
     public KeyBindingScope Scope { get; set; }
 
 
+    /// <summary>The view the key binding is bound to.</summary>
+    public View? BoundView { get; set; }
+
     /// <summary>
     /// <summary>
     ///     Arbitrary context that can be associated with this key binding.
     ///     Arbitrary context that can be associated with this key binding.
     /// </summary>
     /// </summary>

+ 120 - 18
Terminal.Gui/Input/KeyBindings.cs

@@ -1,6 +1,7 @@
 #nullable enable
 #nullable enable
 
 
 using System.Diagnostics;
 using System.Diagnostics;
+using Microsoft.CodeAnalysis;
 
 
 namespace Terminal.Gui;
 namespace Terminal.Gui;
 
 
@@ -34,7 +35,8 @@ public class KeyBindings
     /// <summary>Adds a <see cref="KeyBinding"/> to the collection.</summary>
     /// <summary>Adds a <see cref="KeyBinding"/> to the collection.</summary>
     /// <param name="key"></param>
     /// <param name="key"></param>
     /// <param name="binding"></param>
     /// <param name="binding"></param>
-    public void Add (Key key, KeyBinding binding)
+    /// <param name="boundViewForAppScope">Optional View for <see cref="KeyBindingScope.Application"/> bindings.</param>
+    public void Add (Key key, KeyBinding binding, View? boundViewForAppScope = null)
     {
     {
         if (BoundView is { } && binding.Scope.FastHasFlags (KeyBindingScope.Application))
         if (BoundView is { } && binding.Scope.FastHasFlags (KeyBindingScope.Application))
         {
         {
@@ -43,10 +45,19 @@ public class KeyBindings
 
 
         if (TryGet (key, out KeyBinding _))
         if (TryGet (key, out KeyBinding _))
         {
         {
-            Bindings [key] = binding;
+            throw new InvalidOperationException(@$"A key binding for {key} exists ({binding}).");
+            //Bindings [key] = binding;
         }
         }
         else
         else
         {
         {
+            if (BoundView is { })
+            {
+                binding.BoundView = BoundView;
+            }
+            else
+            {
+                binding.BoundView = boundViewForAppScope;
+            }
             Bindings.Add (key, binding);
             Bindings.Add (key, binding);
         }
         }
     }
     }
@@ -64,12 +75,13 @@ public class KeyBindings
     /// </remarks>
     /// </remarks>
     /// <param name="key">The key to check.</param>
     /// <param name="key">The key to check.</param>
     /// <param name="scope">The scope for the command.</param>
     /// <param name="scope">The scope for the command.</param>
+    /// <param name="boundViewForAppScope">Optional View for <see cref="KeyBindingScope.Application"/> bindings.</param>
     /// <param name="commands">
     /// <param name="commands">
     ///     The command to invoked on the <see cref="View"/> when <paramref name="key"/> is pressed. When
     ///     The command to invoked on the <see cref="View"/> when <paramref name="key"/> is pressed. When
     ///     multiple commands are provided,they will be applied in sequence. The bound <paramref name="key"/> strike will be
     ///     multiple commands are provided,they will be applied in sequence. The bound <paramref name="key"/> strike will be
     ///     consumed if any took effect.
     ///     consumed if any took effect.
     /// </param>
     /// </param>
-    public void Add (Key key, KeyBindingScope scope, params Command [] commands)
+    public void Add (Key key, KeyBindingScope scope, View? boundViewForAppScope = null, params Command [] commands)
     {
     {
         if (BoundView is { } && scope.FastHasFlags (KeyBindingScope.Application))
         if (BoundView is { } && scope.FastHasFlags (KeyBindingScope.Application))
         {
         {
@@ -87,13 +99,43 @@ public class KeyBindings
             throw new ArgumentException (@"At least one command must be specified", nameof (commands));
             throw new ArgumentException (@"At least one command must be specified", nameof (commands));
         }
         }
 
 
-        if (TryGet (key, out KeyBinding _))
+        if (TryGet (key, out KeyBinding binding))
         {
         {
-            Bindings [key] = new (commands, scope);
+            throw new InvalidOperationException (@$"A key binding for {key} exists ({binding}).");
+            //Bindings [key] = new (commands, scope, BoundView);
         }
         }
         else
         else
         {
         {
-            Add (key, new KeyBinding (commands, scope));
+            Add (key, new KeyBinding (commands, scope, BoundView), boundViewForAppScope);
+        }
+    }
+
+    public void Add (Key key, KeyBindingScope scope,  params Command [] commands)
+    {
+        if (BoundView is { } && scope.FastHasFlags (KeyBindingScope.Application))
+        {
+            throw new ArgumentException ("Application scoped KeyBindings must be added via Application.KeyBindings.Add");
+        }
+
+        if (key is null || !key.IsValid)
+        {
+            //throw new ArgumentException ("Invalid Key", nameof (commands));
+            return;
+        }
+
+        if (commands.Length == 0)
+        {
+            throw new ArgumentException (@"At least one command must be specified", nameof (commands));
+        }
+
+        if (TryGet (key, out KeyBinding binding))
+        {
+            throw new InvalidOperationException (@$"A key binding for {key} exists ({binding}).");
+            //Bindings [key] = new (commands, scope, BoundView);
+        }
+        else
+        {
+            Add (key, new KeyBinding (commands, scope, BoundView), null);
         }
         }
     }
     }
 
 
@@ -103,8 +145,42 @@ public class KeyBindings
     ///         View - see <see cref="View.GetSupportedCommands"/>).
     ///         View - see <see cref="View.GetSupportedCommands"/>).
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
-    ///         This is a helper function for <see cref="Add(Key,KeyBindingScope,Terminal.Gui.Command[])"/> for
-    ///         <see cref="KeyBindingScope.Focused"/> scoped commands.
+    ///         This is a helper function for <see cref="Add(Key,KeyBinding,View?)"/>. If used for a View (<see cref="BoundView"/> is set), the scope will be set to <see cref="KeyBindingScope.Focused"/>.
+    ///         Otherwise, it will be set to <see cref="KeyBindingScope.Application"/>.
+    ///     </para>
+    ///     <para>
+    ///         If the key is already bound to a different array of <see cref="Command"/>s it will be rebound
+    ///         <paramref name="commands"/>.
+    ///     </para>
+    /// </summary>
+    /// <remarks>
+    ///     Commands are only ever applied to the current <see cref="View"/> (i.e. this feature cannot be used to switch
+    ///     focus to another view and perform multiple commands there).
+    /// </remarks>
+    /// <param name="key">The key to check.</param>
+    /// <param name="boundViewForAppScope">Optional View for <see cref="KeyBindingScope.Application"/> bindings.</param>
+    /// <param name="commands">
+    ///     The command to invoked on the <see cref="View"/> when <paramref name="key"/> is pressed. When
+    ///     multiple commands are provided,they will be applied in sequence. The bound <paramref name="key"/> strike will be
+    ///     consumed if any took effect.
+    /// </param>
+    public void Add (Key key, View? boundViewForAppScope = null, params Command [] commands)
+    {
+        if (BoundView is null && boundViewForAppScope is null)
+        {
+            throw new ArgumentException (@"Application scoped KeyBindings must provide a bound view to Add.", nameof(boundViewForAppScope));
+        }
+        Add (key, BoundView is { } ? KeyBindingScope.Focused : KeyBindingScope.Application, boundViewForAppScope, commands);
+    }
+
+    /// <summary>
+    ///     <para>
+    ///         Adds a new key combination that will trigger the commands in <paramref name="commands"/> (if supported by the
+    ///         View - see <see cref="View.GetSupportedCommands"/>).
+    ///     </para>
+    ///     <para>
+    ///         This is a helper function for <see cref="Add(Key,KeyBinding,View?)"/>. If used for a View (<see cref="BoundView"/> is set), the scope will be set to <see cref="KeyBindingScope.Focused"/>.
+    ///         Otherwise, it will be set to <see cref="KeyBindingScope.Application"/>.
     ///     </para>
     ///     </para>
     ///     <para>
     ///     <para>
     ///         If the key is already bound to a different array of <see cref="Command"/>s it will be rebound
     ///         If the key is already bound to a different array of <see cref="Command"/>s it will be rebound
@@ -123,14 +199,16 @@ public class KeyBindings
     /// </param>
     /// </param>
     public void Add (Key key, params Command [] commands)
     public void Add (Key key, params Command [] commands)
     {
     {
-        Add (key, KeyBindingScope.Focused, commands);
+        if (BoundView is null)
+        {
+            throw new ArgumentException (@"Application scoped KeyBindings must provide a boundViewForAppScope to Add.");
+        }
+        Add (key, BoundView is { } ? KeyBindingScope.Focused : KeyBindingScope.Application, null, commands);
     }
     }
 
 
     /// <summary>Removes all <see cref="KeyBinding"/> objects from the collection.</summary>
     /// <summary>Removes all <see cref="KeyBinding"/> objects from the collection.</summary>
     public void Clear ()
     public void Clear ()
     {
     {
-        Application.ClearKeyBindings (BoundView);
-
         Bindings.Clear ();
         Bindings.Clear ();
     }
     }
 
 
@@ -201,17 +279,23 @@ public class KeyBindings
 
 
     /// <summary>Removes a <see cref="KeyBinding"/> from the collection.</summary>
     /// <summary>Removes a <see cref="KeyBinding"/> from the collection.</summary>
     /// <param name="key"></param>
     /// <param name="key"></param>
-    public void Remove (Key key)
+    /// <param name="boundViewForAppScope">Optional View for <see cref="KeyBindingScope.Application"/> bindings.</param>
+    public void Remove (Key key, View? boundViewForAppScope = null)
     {
     {
+
+        if (!TryGet (key, out KeyBinding binding))
+        {
+            return;
+        }
+
         Bindings.Remove (key);
         Bindings.Remove (key);
-        Application.RemoveKeyBinding (key, BoundView);
     }
     }
 
 
     /// <summary>Replaces a key combination already bound to a set of <see cref="Command"/>s.</summary>
     /// <summary>Replaces a key combination already bound to a set of <see cref="Command"/>s.</summary>
     /// <remarks></remarks>
     /// <remarks></remarks>
     /// <param name="oldKey">The key to be replaced.</param>
     /// <param name="oldKey">The key to be replaced.</param>
     /// <param name="newKey">The new key to be used.</param>
     /// <param name="newKey">The new key to be used.</param>
-    public void Replace (Key oldKey, Key newKey)
+    public void ReplaceKey (Key oldKey, Key newKey)
     {
     {
         if (!TryGet (oldKey, out KeyBinding _))
         if (!TryGet (oldKey, out KeyBinding _))
         {
         {
@@ -223,6 +307,26 @@ public class KeyBindings
         Add (newKey, value);
         Add (newKey, value);
     }
     }
 
 
+    /// <summary>Replaces the commands already bound to a key.</summary>
+    /// <remarks>
+    ///     <para>
+    ///         If the key is not already bound, it will be added.
+    ///     </para>
+    /// </remarks>
+    /// <param name="key">The key bound to the command to be replaced.</param>
+    /// <param name="commands">The set of commands to replace the old ones with.</param>
+    public void ReplaceCommands (Key key, params Command [] commands)
+    {
+        if (TryGet (key, out KeyBinding binding))
+        {
+            binding.Commands = commands;
+        }
+        else
+        {
+            Add (key, commands);
+        }
+    }
+
     /// <summary>Gets the commands bound with the specified Key.</summary>
     /// <summary>Gets the commands bound with the specified Key.</summary>
     /// <remarks></remarks>
     /// <remarks></remarks>
     /// <param name="key">The key to check.</param>
     /// <param name="key">The key to check.</param>
@@ -233,13 +337,12 @@ public class KeyBindings
     /// <returns><see langword="true"/> if the Key is bound; otherwise <see langword="false"/>.</returns>
     /// <returns><see langword="true"/> if the Key is bound; otherwise <see langword="false"/>.</returns>
     public bool TryGet (Key key, out KeyBinding binding)
     public bool TryGet (Key key, out KeyBinding binding)
     {
     {
+        binding = new (Array.Empty<Command> (), KeyBindingScope.Disabled, null);
         if (key.IsValid)
         if (key.IsValid)
         {
         {
             return Bindings.TryGetValue (key, out binding);
             return Bindings.TryGetValue (key, out binding);
         }
         }
 
 
-        binding = new (Array.Empty<Command> (), KeyBindingScope.Focused);
-
         return false;
         return false;
     }
     }
 
 
@@ -254,6 +357,7 @@ public class KeyBindings
     /// <returns><see langword="true"/> if the Key is bound; otherwise <see langword="false"/>.</returns>
     /// <returns><see langword="true"/> if the Key is bound; otherwise <see langword="false"/>.</returns>
     public bool TryGet (Key key, KeyBindingScope scope, out KeyBinding binding)
     public bool TryGet (Key key, KeyBindingScope scope, out KeyBinding binding)
     {
     {
+        binding = new (Array.Empty<Command> (), KeyBindingScope.Disabled, null);
         if (key.IsValid && Bindings.TryGetValue (key, out binding))
         if (key.IsValid && Bindings.TryGetValue (key, out binding))
         {
         {
             if (scope.HasFlag (binding.Scope))
             if (scope.HasFlag (binding.Scope))
@@ -262,8 +366,6 @@ public class KeyBindings
             }
             }
         }
         }
 
 
-        binding = new (Array.Empty<Command> (), KeyBindingScope.Focused);
-
         return false;
         return false;
     }
     }
 }
 }

+ 10 - 5
Terminal.Gui/View/ViewKeyboard.cs

@@ -27,7 +27,7 @@ public partial class View
     private void DisposeKeyboard ()
     private void DisposeKeyboard ()
     {
     {
         TitleTextFormatter.HotKeyChanged -= TitleTextFormatter_HotKeyChanged;
         TitleTextFormatter.HotKeyChanged -= TitleTextFormatter_HotKeyChanged;
-        KeyBindings.Clear ();
+        Application.RemoveKeyBindings (this);
     }
     }
 
 
     #region HotKey Support
     #region HotKey Support
@@ -197,13 +197,17 @@ public partial class View
         {
         {
             KeyBinding keyBinding = new ([Command.HotKey], KeyBindingScope.HotKey, context);
             KeyBinding keyBinding = new ([Command.HotKey], KeyBindingScope.HotKey, context);
             // Add the base and Alt key
             // Add the base and Alt key
+            KeyBindings.Remove (newKey);
             KeyBindings.Add (newKey, keyBinding);
             KeyBindings.Add (newKey, keyBinding);
+            KeyBindings.Remove (newKey.WithAlt);
             KeyBindings.Add (newKey.WithAlt, keyBinding);
             KeyBindings.Add (newKey.WithAlt, keyBinding);
 
 
             // If the Key is A..Z, add ShiftMask and AltMask | ShiftMask
             // If the Key is A..Z, add ShiftMask and AltMask | ShiftMask
             if (newKey.IsKeyCodeAtoZ)
             if (newKey.IsKeyCodeAtoZ)
             {
             {
+                KeyBindings.Remove (newKey.WithShift);
                 KeyBindings.Add (newKey.WithShift, keyBinding);
                 KeyBindings.Add (newKey.WithShift, keyBinding);
+                KeyBindings.Remove (newKey.WithShift.WithAlt);
                 KeyBindings.Add (newKey.WithShift.WithAlt, keyBinding);
                 KeyBindings.Add (newKey.WithShift.WithAlt, keyBinding);
             }
             }
         }
         }
@@ -800,11 +804,12 @@ public partial class View
 #if DEBUG
 #if DEBUG
 
 
         // TODO: Determine if App scope bindings should be fired first or last (currently last).
         // TODO: Determine if App scope bindings should be fired first or last (currently last).
-        if (Application.TryGetKeyBindings (key, out List<View> views))
+        if (Application.KeyBindings.TryGet (key, KeyBindingScope.Focused | KeyBindingScope.HotKey, out KeyBinding b))
         {
         {
-            var boundView = views [0];
-            var commandBinding = boundView.KeyBindings.Get (key);
-            Debug.WriteLine ($"WARNING: InvokeKeyBindings ({key}) - An Application scope binding exists for this key. The registered view will not invoke Command.{commandBinding.Commands [0]}: {boundView}.");
+            //var boundView = views [0];
+            //var commandBinding = boundView.KeyBindings.Get (key);
+            Debug.WriteLine (
+                             $"WARNING: InvokeKeyBindings ({key}) - An Application scope binding exists for this key. The registered view will not invoke Command.");//{commandBinding.Commands [0]}: {boundView}.");
         }
         }
 
 
         // TODO: This is a "prototype" debug check. It may be too annoying vs. useful.
         // TODO: This is a "prototype" debug check. It may be too annoying vs. useful.

+ 13 - 13
Terminal.Gui/Views/DateField.cs

@@ -400,26 +400,26 @@ public class DateField : TextField
         AddCommand (Command.RightEnd, () => MoveEnd ());
         AddCommand (Command.RightEnd, () => MoveEnd ());
         AddCommand (Command.Right, () => MoveRight ());
         AddCommand (Command.Right, () => MoveRight ());
 
 
-        // Default keybindings for this view
-        KeyBindings.Add (Key.Delete, Command.DeleteCharRight);
-        KeyBindings.Add (Key.D.WithCtrl, Command.DeleteCharRight);
+        // Replace the commands defined in TextField
+        KeyBindings.ReplaceCommands (Key.Delete, Command.DeleteCharRight);
+        KeyBindings.ReplaceCommands (Key.D.WithCtrl, Command.DeleteCharRight);
 
 
-        KeyBindings.Add (Key.Backspace, Command.DeleteCharLeft);
+        KeyBindings.ReplaceCommands (Key.Backspace, Command.DeleteCharLeft);
 
 
-        KeyBindings.Add (Key.Home, Command.LeftHome);
-        KeyBindings.Add (Key.A.WithCtrl, Command.LeftHome);
+        KeyBindings.ReplaceCommands (Key.Home, Command.LeftHome);
+        KeyBindings.ReplaceCommands (Key.A.WithCtrl, Command.LeftHome);
 
 
-        KeyBindings.Add (Key.CursorLeft, Command.Left);
-        KeyBindings.Add (Key.B.WithCtrl, Command.Left);
+        KeyBindings.ReplaceCommands (Key.CursorLeft, Command.Left);
+        KeyBindings.ReplaceCommands (Key.B.WithCtrl, Command.Left);
 
 
-        KeyBindings.Add (Key.End, Command.RightEnd);
-        KeyBindings.Add (Key.E.WithCtrl, Command.RightEnd);
+        KeyBindings.ReplaceCommands (Key.End, Command.RightEnd);
+        KeyBindings.ReplaceCommands (Key.E.WithCtrl, Command.RightEnd);
 
 
-        KeyBindings.Add (Key.CursorRight, Command.Right);
-        KeyBindings.Add (Key.F.WithCtrl, Command.Right);
+        KeyBindings.ReplaceCommands (Key.CursorRight, Command.Right);
+        KeyBindings.ReplaceCommands (Key.F.WithCtrl, Command.Right);
 
 
 #if UNIX_KEY_BINDINGS
 #if UNIX_KEY_BINDINGS
-        KeyBindings.Add (Key.D.WithAlt, Command.DeleteCharLeft);
+        KeyBindings.ReplaceCommands (Key.D.WithAlt, Command.DeleteCharLeft);
 #endif
 #endif
 
 
     }
     }

+ 5 - 5
Terminal.Gui/Views/FileDialog.cs

@@ -134,7 +134,7 @@ public class FileDialog : Dialog
             FullRowSelect = true,
             FullRowSelect = true,
             CollectionNavigator = new FileDialogCollectionNavigator (this)
             CollectionNavigator = new FileDialogCollectionNavigator (this)
         };
         };
-        _tableView.KeyBindings.Add (Key.Space, Command.Select);
+        _tableView.KeyBindings.ReplaceCommands (Key.Space, Command.Select);
         _tableView.MouseClick += OnTableViewMouseClick;
         _tableView.MouseClick += OnTableViewMouseClick;
         _tableView.Style.InvertSelectedCellFirstCharacter = true;
         _tableView.Style.InvertSelectedCellFirstCharacter = true;
         Style.TableStyle = _tableView.Style;
         Style.TableStyle = _tableView.Style;
@@ -254,10 +254,10 @@ public class FileDialog : Dialog
         _tableView.KeyUp += (s, k) => k.Handled = TableView_KeyUp (k);
         _tableView.KeyUp += (s, k) => k.Handled = TableView_KeyUp (k);
         _tableView.SelectedCellChanged += TableView_SelectedCellChanged;
         _tableView.SelectedCellChanged += TableView_SelectedCellChanged;
 
 
-        _tableView.KeyBindings.Add (Key.Home, Command.TopHome);
-        _tableView.KeyBindings.Add (Key.End, Command.BottomEnd);
-        _tableView.KeyBindings.Add (Key.Home.WithShift, Command.TopHomeExtend);
-        _tableView.KeyBindings.Add (Key.End.WithShift, Command.BottomEndExtend);
+        _tableView.KeyBindings.ReplaceCommands (Key.Home, Command.TopHome);
+        _tableView.KeyBindings.ReplaceCommands (Key.End, Command.BottomEnd);
+        _tableView.KeyBindings.ReplaceCommands (Key.Home.WithShift, Command.TopHomeExtend);
+        _tableView.KeyBindings.ReplaceCommands (Key.End.WithShift, Command.BottomEndExtend);
 
 
         _treeView.KeyDown += (s, k) =>
         _treeView.KeyDown += (s, k) =>
                              {
                              {

+ 3 - 0
Terminal.Gui/Views/Menu/Menu.cs

@@ -192,13 +192,16 @@ internal sealed class Menu : View
 
 
             if ((KeyCode)menuItem.HotKey.Value != KeyCode.Null)
             if ((KeyCode)menuItem.HotKey.Value != KeyCode.Null)
             {
             {
+                KeyBindings.Remove ((KeyCode)menuItem.HotKey.Value);
                 KeyBindings.Add ((KeyCode)menuItem.HotKey.Value, keyBinding);
                 KeyBindings.Add ((KeyCode)menuItem.HotKey.Value, keyBinding);
+                KeyBindings.Remove ((KeyCode)menuItem.HotKey.Value | KeyCode.AltMask);
                 KeyBindings.Add ((KeyCode)menuItem.HotKey.Value | KeyCode.AltMask, keyBinding);
                 KeyBindings.Add ((KeyCode)menuItem.HotKey.Value | KeyCode.AltMask, keyBinding);
             }
             }
 
 
             if (menuItem.Shortcut != KeyCode.Null)
             if (menuItem.Shortcut != KeyCode.Null)
             {
             {
                 keyBinding = new ([Command.Select], KeyBindingScope.HotKey, menuItem);
                 keyBinding = new ([Command.Select], KeyBindingScope.HotKey, menuItem);
+                KeyBindings.Remove (menuItem.Shortcut);
                 KeyBindings.Add (menuItem.Shortcut, keyBinding);
                 KeyBindings.Add (menuItem.Shortcut, keyBinding);
             }
             }
 
 

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

@@ -103,6 +103,7 @@ public class MenuBarItem : MenuItem
             if (menuItem.Shortcut != KeyCode.Null)
             if (menuItem.Shortcut != KeyCode.Null)
             {
             {
                 KeyBinding keyBinding = new ([Command.Select], KeyBindingScope.HotKey, menuItem);
                 KeyBinding keyBinding = new ([Command.Select], KeyBindingScope.HotKey, menuItem);
+                menuBar.KeyBindings.Remove (menuItem.Shortcut);
                 menuBar.KeyBindings.Add (menuItem.Shortcut, keyBinding);
                 menuBar.KeyBindings.Add (menuItem.Shortcut, keyBinding);
             }
             }
 
 

+ 0 - 2
Terminal.Gui/Views/MenuBarv2.cs

@@ -43,8 +43,6 @@ public class MenuBarv2 : Bar
 
 
         if (view is Shortcut shortcut)
         if (view is Shortcut shortcut)
         {
         {
-            shortcut.KeyBindingScope = KeyBindingScope.Application;
-
             // TODO: not happy about using AlignmentModes for this. Too implied.
             // TODO: not happy about using AlignmentModes for this. Too implied.
             // TODO: instead, add a property (a style enum?) to Shortcut to control this
             // TODO: instead, add a property (a style enum?) to Shortcut to control this
             //shortcut.AlignmentModes = AlignmentModes.EndToStart;
             //shortcut.AlignmentModes = AlignmentModes.EndToStart;

+ 269 - 261
Terminal.Gui/Views/Shortcut.cs

@@ -440,353 +440,361 @@ public class Shortcut : View
     }
     }
 
 
     private void SetCommandViewDefaultLayout ()
     private void SetCommandViewDefaultLayout ()
-{
-    CommandView.Margin.Thickness = GetMarginThickness ();
-    CommandView.X = Pos.Align (Alignment.End, AlignmentModes);
-    CommandView.Y = 0; //Pos.Center ();
-}
+    {
+        CommandView.Margin.Thickness = GetMarginThickness ();
+        CommandView.X = Pos.Align (Alignment.End, AlignmentModes);
+        CommandView.Y = 0; //Pos.Center ();
+    }
 
 
-private void Shortcut_TitleChanged (object sender, EventArgs<string> e)
-{
-    // If the Title changes, update the CommandView text.
-    // This is a helper to make it easier to set the CommandView text.
-    // CommandView is public and replaceable, but this is a convenience.
-    _commandView.Text = Title;
-}
+    private void Shortcut_TitleChanged (object sender, EventArgs<string> e)
+    {
+        // If the Title changes, update the CommandView text.
+        // This is a helper to make it easier to set the CommandView text.
+        // CommandView is public and replaceable, but this is a convenience.
+        _commandView.Text = Title;
+    }
 
 
-#endregion Command
+    #endregion Command
 
 
-#region Help
+    #region Help
 
 
-/// <summary>
-///     The subview that displays the help text for the command. Internal for unit testing.
-/// </summary>
-internal View HelpView { get; } = new ();
+    /// <summary>
+    ///     The subview that displays the help text for the command. Internal for unit testing.
+    /// </summary>
+    internal View HelpView { get; } = new ();
 
 
-private void SetHelpViewDefaultLayout ()
-{
-    HelpView.Margin.Thickness = GetMarginThickness ();
-    HelpView.X = Pos.Align (Alignment.End, AlignmentModes);
-    HelpView.Y = 0; //Pos.Center ();
-    HelpView.Width = Dim.Auto (DimAutoStyle.Text);
-    HelpView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
-
-    HelpView.Visible = true;
-    HelpView.VerticalTextAlignment = Alignment.Center;
-}
+    private void SetHelpViewDefaultLayout ()
+    {
+        HelpView.Margin.Thickness = GetMarginThickness ();
+        HelpView.X = Pos.Align (Alignment.End, AlignmentModes);
+        HelpView.Y = 0; //Pos.Center ();
+        HelpView.Width = Dim.Auto (DimAutoStyle.Text);
+        HelpView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
+
+        HelpView.Visible = true;
+        HelpView.VerticalTextAlignment = Alignment.Center;
+    }
 
 
-/// <summary>
-///     Gets or sets the help text displayed in the middle of the Shortcut. Identical in function to <see cref="HelpText"/>
-///     .
-/// </summary>
-public override string Text
-{
-    get => HelpView?.Text;
-    set
+    /// <summary>
+    ///     Gets or sets the help text displayed in the middle of the Shortcut. Identical in function to <see cref="HelpText"/>
+    ///     .
+    /// </summary>
+    public override string Text
     {
     {
-        if (HelpView != null)
+        get => HelpView?.Text;
+        set
         {
         {
-            HelpView.Text = value;
-            ShowHide ();
+            if (HelpView != null)
+            {
+                HelpView.Text = value;
+                ShowHide ();
+            }
         }
         }
     }
     }
-}
 
 
-/// <summary>
-///     Gets or sets the help text displayed in the middle of the Shortcut.
-/// </summary>
-public string HelpText
-{
-    get => HelpView?.Text;
-    set
+    /// <summary>
+    ///     Gets or sets the help text displayed in the middle of the Shortcut.
+    /// </summary>
+    public string HelpText
     {
     {
-        if (HelpView != null)
+        get => HelpView?.Text;
+        set
         {
         {
-            HelpView.Text = value;
-            ShowHide ();
+            if (HelpView != null)
+            {
+                HelpView.Text = value;
+                ShowHide ();
+            }
         }
         }
     }
     }
-}
 
 
-#endregion Help
+    #endregion Help
 
 
-#region Key
+    #region Key
 
 
-private Key _key = Key.Empty;
+    private Key _key = Key.Empty;
 
 
-/// <summary>
-///     Gets or sets the <see cref="Key"/> that will be bound to the <see cref="Command.Accept"/> command.
-/// </summary>
-public Key Key
-{
-    get => _key;
-    set
+    /// <summary>
+    ///     Gets or sets the <see cref="Key"/> that will be bound to the <see cref="Command.Accept"/> command.
+    /// </summary>
+    public Key Key
     {
     {
-        if (value == null)
+        get => _key;
+        set
         {
         {
-            throw new ArgumentNullException ();
-        }
+            if (value == null)
+            {
+                throw new ArgumentNullException ();
+            }
 
 
-        _key = value;
+            _key = value;
 
 
-        UpdateKeyBinding ();
+            UpdateKeyBinding ();
 
 
-        KeyView.Text = Key == Key.Empty ? string.Empty : $"{Key}";
-        ShowHide ();
+            KeyView.Text = Key == Key.Empty ? string.Empty : $"{Key}";
+            ShowHide ();
+        }
     }
     }
-}
 
 
-private KeyBindingScope _keyBindingScope = KeyBindingScope.HotKey;
+    private KeyBindingScope _keyBindingScope = KeyBindingScope.HotKey;
 
 
-/// <summary>
-///     Gets or sets the scope for the key binding for how <see cref="Key"/> is bound to <see cref="Command"/>.
-/// </summary>
-public KeyBindingScope KeyBindingScope
-{
-    get => _keyBindingScope;
-    set
+    /// <summary>
+    ///     Gets or sets the scope for the key binding for how <see cref="Key"/> is bound to <see cref="Command"/>.
+    /// </summary>
+    public KeyBindingScope KeyBindingScope
     {
     {
-        _keyBindingScope = value;
+        get => _keyBindingScope;
+        set
+        {
+            _keyBindingScope = value;
 
 
-        UpdateKeyBinding ();
+            UpdateKeyBinding ();
+        }
     }
     }
-}
 
 
-/// <summary>
-///     Gets the subview that displays the key. Internal for unit testing.
-/// </summary>
+    /// <summary>
+    ///     Gets the subview that displays the key. Internal for unit testing.
+    /// </summary>
 
 
-internal View KeyView { get; } = new ();
+    internal View KeyView { get; } = new ();
 
 
-private int _minimumKeyTextSize;
+    private int _minimumKeyTextSize;
 
 
-/// <summary>
-/// Gets or sets the minimum size of the key text. Useful for aligning the key text with other <see cref="Shortcut"/>s.
-/// </summary>
-public int MinimumKeyTextSize
-{
-    get => _minimumKeyTextSize;
-    set
+    /// <summary>
+    /// Gets or sets the minimum size of the key text. Useful for aligning the key text with other <see cref="Shortcut"/>s.
+    /// </summary>
+    public int MinimumKeyTextSize
     {
     {
-        if (value == _minimumKeyTextSize)
+        get => _minimumKeyTextSize;
+        set
         {
         {
-            //return;
-        }
+            if (value == _minimumKeyTextSize)
+            {
+                //return;
+            }
 
 
-        _minimumKeyTextSize = value;
-        SetKeyViewDefaultLayout ();
-        CommandView.SetNeedsLayout ();
-        HelpView.SetNeedsLayout ();
-        KeyView.SetNeedsLayout ();
-        SetSubViewNeedsDisplay ();
+            _minimumKeyTextSize = value;
+            SetKeyViewDefaultLayout ();
+            CommandView.SetNeedsLayout ();
+            HelpView.SetNeedsLayout ();
+            KeyView.SetNeedsLayout ();
+            SetSubViewNeedsDisplay ();
+        }
     }
     }
-}
-
-private int GetMinimumKeyViewSize () { return MinimumKeyTextSize; }
 
 
-private void SetKeyViewDefaultLayout ()
-{
-    KeyView.Margin.Thickness = GetMarginThickness ();
-    KeyView.X = Pos.Align (Alignment.End, AlignmentModes);
-    KeyView.Y = 0; //Pos.Center ();
-    KeyView.Width = Dim.Auto (DimAutoStyle.Text, Dim.Func (GetMinimumKeyViewSize));
-    KeyView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
-
-    KeyView.Visible = true;
-
-    // Right align the text in the keyview
-    KeyView.TextAlignment = Alignment.End;
-    KeyView.VerticalTextAlignment = Alignment.Center;
-    KeyView.KeyBindings.Clear ();
-}
+    private int GetMinimumKeyViewSize () { return MinimumKeyTextSize; }
 
 
-private void UpdateKeyBinding ()
-{
-    if (Key != null)
+    private void SetKeyViewDefaultLayout ()
     {
     {
-        // Disable the command view key bindings
-        CommandView.KeyBindings.Remove (Key);
-        CommandView.KeyBindings.Remove (CommandView.HotKey);
-        KeyBindings.Remove (Key);
-        KeyBindings.Add (Key, KeyBindingScope | KeyBindingScope.HotKey, Command.Accept);
-        //KeyBindings.Add (Key, KeyBindingScope.HotKey, Command.Accept);
+        KeyView.Margin.Thickness = GetMarginThickness ();
+        KeyView.X = Pos.Align (Alignment.End, AlignmentModes);
+        KeyView.Y = 0; //Pos.Center ();
+        KeyView.Width = Dim.Auto (DimAutoStyle.Text, Dim.Func (GetMinimumKeyViewSize));
+        KeyView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1;
+
+        KeyView.Visible = true;
+
+        // Right align the text in the keyview
+        KeyView.TextAlignment = Alignment.End;
+        KeyView.VerticalTextAlignment = Alignment.Center;
+        KeyView.KeyBindings.Clear ();
     }
     }
-}
-
-#endregion Key
 
 
-#region Accept Handling
+    private void UpdateKeyBinding ()
+    {
+        if (Key != null)
+        {
+            // Disable the command view key bindings
+            CommandView.KeyBindings.Remove (Key);
+            CommandView.KeyBindings.Remove (CommandView.HotKey);
 
 
-/// <summary>
-///     Called when the <see cref="Command.Accept"/> command is received. This
-///     occurs
-///     - if the user clicks anywhere on the shortcut with the mouse
-///     - if the user presses Key
-///     - if the user presses the HotKey specified by CommandView
-///     - if HasFocus and the user presses Space or Enter (or any other key bound to Command.Accept).
-/// </summary>
-protected bool? OnAccept (CommandContext ctx)
-{
-    var cancel = false;
+            if (KeyBindingScope.FastHasFlags (KeyBindingScope.Application))
+            {
+                Application.KeyBindings.Remove (Key);
+                Application.KeyBindings.Add (Key, this, Command.Accept);
+            }
+            else
+            {
+                KeyBindings.Remove (Key);
+                KeyBindings.Add (Key, KeyBindingScope | KeyBindingScope.HotKey, Command.Accept);
+            }
+        }
+    }
 
 
-    switch (ctx.KeyBinding?.Scope)
-    {
-        case KeyBindingScope.Application:
-            cancel = base.OnAccept () == true;
+    #endregion Key
 
 
-            break;
+    #region Accept Handling
 
 
-        case KeyBindingScope.Focused:
-            base.OnAccept ();
+    /// <summary>
+    ///     Called when the <see cref="Command.Accept"/> command is received. This
+    ///     occurs
+    ///     - if the user clicks anywhere on the shortcut with the mouse
+    ///     - if the user presses Key
+    ///     - if the user presses the HotKey specified by CommandView
+    ///     - if HasFocus and the user presses Space or Enter (or any other key bound to Command.Accept).
+    /// </summary>
+    protected bool? OnAccept (CommandContext ctx)
+    {
+        var cancel = false;
 
 
-            // cancel if we're focused
-            cancel = true;
+        switch (ctx.KeyBinding?.Scope)
+        {
+            case KeyBindingScope.Application:
+                cancel = base.OnAccept () == true;
 
 
-            break;
+                break;
 
 
-        case KeyBindingScope.HotKey:
-            cancel = base.OnAccept () == true;
+            case KeyBindingScope.Focused:
+                base.OnAccept ();
 
 
-            if (CanFocus)
-            {
-                SetFocus ();
+                // cancel if we're focused
                 cancel = true;
                 cancel = true;
-            }
 
 
-            break;
+                break;
 
 
-        default:
-            // Mouse
-            cancel = base.OnAccept () == true;
+            case KeyBindingScope.HotKey:
+                cancel = base.OnAccept () == true;
 
 
-            break;
-    }
+                if (CanFocus)
+                {
+                    SetFocus ();
+                    cancel = true;
+                }
 
 
-    CommandView.InvokeCommand (Command.Accept, ctx.Key, ctx.KeyBinding);
+                break;
 
 
-    if (Action is { })
-    {
-        Action.Invoke ();
-        // Assume if there's a subscriber to Action, it's handled.
-        cancel = true;
-    }
+            default:
+                // Mouse
+                cancel = base.OnAccept () == true;
 
 
-    return cancel;
-}
+                break;
+        }
 
 
-/// <summary>
-///     Gets or sets the action to be invoked when the shortcut key is pressed or the shortcut is clicked on with the
-///     mouse.
-/// </summary>
-/// <remarks>
-///     Note, the <see cref="View.Accept"/> event is fired first, and if cancelled, the event will not be invoked.
-/// </remarks>
-[CanBeNull]
-public Action Action { get; set; }
+        CommandView.InvokeCommand (Command.Accept, ctx.Key, ctx.KeyBinding);
 
 
-#endregion Accept Handling
+        if (Action is { })
+        {
+            Action.Invoke ();
+            // Assume if there's a subscriber to Action, it's handled.
+            cancel = true;
+        }
 
 
-private bool? OnSelect (CommandContext ctx)
-{
-    if (CommandView.GetSupportedCommands ().Contains (Command.Select))
-    {
-        return CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
+        return cancel;
     }
     }
-    return false;
-
-}
 
 
+    /// <summary>
+    ///     Gets or sets the action to be invoked when the shortcut key is pressed or the shortcut is clicked on with the
+    ///     mouse.
+    /// </summary>
+    /// <remarks>
+    ///     Note, the <see cref="View.Accept"/> event is fired first, and if cancelled, the event will not be invoked.
+    /// </remarks>
+    [CanBeNull]
+    public Action Action { get; set; }
 
 
-#region Focus
+    #endregion Accept Handling
 
 
-/// <inheritdoc/>
-public override ColorScheme ColorScheme
-{
-    get => base.ColorScheme;
-    set
+    private bool? OnSelect (CommandContext ctx)
     {
     {
-        base.ColorScheme = value;
-        SetColors ();
+        if (CommandView.GetSupportedCommands ().Contains (Command.Select))
+        {
+            return CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
+        }
+        return false;
+
     }
     }
-}
 
 
-/// <summary>
-/// </summary>
-internal void SetColors ()
-{
-    // Border should match superview.
-    Border.ColorScheme = SuperView?.ColorScheme;
 
 
-    if (HasFocus)
+    #region Focus
+
+    /// <inheritdoc/>
+    public override ColorScheme ColorScheme
     {
     {
-        // When we have focus, we invert the colors
-        base.ColorScheme = new (base.ColorScheme)
+        get => base.ColorScheme;
+        set
         {
         {
-            Normal = base.ColorScheme.Focus,
-            HotNormal = base.ColorScheme.HotFocus,
-            HotFocus = base.ColorScheme.HotNormal,
-            Focus = base.ColorScheme.Normal
-        };
-    }
-    else
-    {
-        base.ColorScheme = SuperView?.ColorScheme ?? base.ColorScheme;
+            base.ColorScheme = value;
+            SetColors ();
+        }
     }
     }
 
 
-    // Set KeyView's colors to show "hot"
-    if (IsInitialized && base.ColorScheme is { })
+    /// <summary>
+    /// </summary>
+    internal void SetColors ()
     {
     {
-        var cs = new ColorScheme (base.ColorScheme)
+        // Border should match superview.
+        Border.ColorScheme = SuperView?.ColorScheme;
+
+        if (HasFocus)
         {
         {
-            Normal = base.ColorScheme.HotNormal,
-            HotNormal = base.ColorScheme.Normal
-        };
-        KeyView.ColorScheme = cs;
+            // When we have focus, we invert the colors
+            base.ColorScheme = new (base.ColorScheme)
+            {
+                Normal = base.ColorScheme.Focus,
+                HotNormal = base.ColorScheme.HotFocus,
+                HotFocus = base.ColorScheme.HotNormal,
+                Focus = base.ColorScheme.Normal
+            };
+        }
+        else
+        {
+            base.ColorScheme = SuperView?.ColorScheme ?? base.ColorScheme;
+        }
+
+        // Set KeyView's colors to show "hot"
+        if (IsInitialized && base.ColorScheme is { })
+        {
+            var cs = new ColorScheme (base.ColorScheme)
+            {
+                Normal = base.ColorScheme.HotNormal,
+                HotNormal = base.ColorScheme.Normal
+            };
+            KeyView.ColorScheme = cs;
+        }
     }
     }
-}
 
 
-View _lastFocusedView;
-/// <inheritdoc/>
-public override bool OnEnter (View view)
-{
-    SetColors ();
-    _lastFocusedView = view;
+    View _lastFocusedView;
+    /// <inheritdoc/>
+    public override bool OnEnter (View view)
+    {
+        SetColors ();
+        _lastFocusedView = view;
 
 
-    return base.OnEnter (view);
-}
+        return base.OnEnter (view);
+    }
 
 
-/// <inheritdoc/>
-public override bool OnLeave (View view)
-{
-    SetColors ();
-    _lastFocusedView = this;
+    /// <inheritdoc/>
+    public override bool OnLeave (View view)
+    {
+        SetColors ();
+        _lastFocusedView = this;
 
 
-    return base.OnLeave (view);
-}
+        return base.OnLeave (view);
+    }
 
 
-#endregion Focus
+    #endregion Focus
 
 
-/// <inheritdoc/>
-protected override void Dispose (bool disposing)
-{
-    if (disposing)
+    /// <inheritdoc/>
+    protected override void Dispose (bool disposing)
     {
     {
-        if (CommandView?.IsAdded == false)
+        if (disposing)
         {
         {
-            CommandView.Dispose ();
-        }
+            if (CommandView?.IsAdded == false)
+            {
+                CommandView.Dispose ();
+            }
 
 
-        if (HelpView?.IsAdded == false)
-        {
-            HelpView.Dispose ();
-        }
+            if (HelpView?.IsAdded == false)
+            {
+                HelpView.Dispose ();
+            }
 
 
-        if (KeyView?.IsAdded == false)
-        {
-            KeyView.Dispose ();
+            if (KeyView?.IsAdded == false)
+            {
+                KeyView.Dispose ();
+            }
         }
         }
-    }
 
 
-    base.Dispose (disposing);
-}
+        base.Dispose (disposing);
+    }
 }
 }

+ 4 - 0
Terminal.Gui/Views/Slider.cs

@@ -1454,9 +1454,13 @@ public class Slider<T> : View
             KeyBindings.Add (Key.CursorUp.WithCtrl, Command.LeftExtend);
             KeyBindings.Add (Key.CursorUp.WithCtrl, Command.LeftExtend);
         }
         }
 
 
+        KeyBindings.Remove (Key.Home);
         KeyBindings.Add (Key.Home, Command.LeftHome);
         KeyBindings.Add (Key.Home, Command.LeftHome);
+        KeyBindings.Remove (Key.End);
         KeyBindings.Add (Key.End, Command.RightEnd);
         KeyBindings.Add (Key.End, Command.RightEnd);
+        KeyBindings.Remove (Key.Enter);
         KeyBindings.Add (Key.Enter, Command.Accept);
         KeyBindings.Add (Key.Enter, Command.Accept);
+        KeyBindings.Remove (Key.Space);
         KeyBindings.Add (Key.Space, Command.Select);
         KeyBindings.Add (Key.Space, Command.Select);
     }
     }
 
 

+ 0 - 2
Terminal.Gui/Views/StatusBar.cs

@@ -65,8 +65,6 @@ public class StatusBar : Bar
 
 
         if (view is Shortcut shortcut)
         if (view is Shortcut shortcut)
         {
         {
-            shortcut.KeyBindingScope = KeyBindingScope.Application;
-
             // TODO: not happy about using AlignmentModes for this. Too implied.
             // TODO: not happy about using AlignmentModes for this. Too implied.
             // TODO: instead, add a property (a style enum?) to Shortcut to control this
             // TODO: instead, add a property (a style enum?) to Shortcut to control this
             shortcut.AlignmentModes = AlignmentModes.EndToStart;
             shortcut.AlignmentModes = AlignmentModes.EndToStart;

+ 1 - 1
Terminal.Gui/Views/TableView/CheckBoxTableSourceWrapper.cs

@@ -26,7 +26,7 @@ public abstract class CheckBoxTableSourceWrapperBase : ITableSource
         Wrapping = toWrap;
         Wrapping = toWrap;
         this.tableView = tableView;
         this.tableView = tableView;
 
 
-        tableView.KeyBindings.Add (Key.Space, Command.Select);
+        tableView.KeyBindings.ReplaceCommands (Key.Space, Command.Select);
 
 
         tableView.MouseClick += TableView_MouseClick;
         tableView.MouseClick += TableView_MouseClick;
         tableView.CellToggled += TableView_CellToggled;
         tableView.CellToggled += TableView_CellToggled;

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

@@ -319,7 +319,7 @@ public class TableView : View
         {
         {
             if (cellActivationKey != value)
             if (cellActivationKey != value)
             {
             {
-                KeyBindings.Replace (cellActivationKey, value);
+                KeyBindings.ReplaceKey (cellActivationKey, value);
 
 
                 // of API user is mixing and matching old and new methods of keybinding then they may have lost
                 // of API user is mixing and matching old and new methods of keybinding then they may have lost
                 // the old binding (e.g. with ClearKeybindings) so KeyBindings.Replace alone will fail
                 // the old binding (e.g. with ClearKeybindings) so KeyBindings.Replace alone will fail

+ 4 - 1
Terminal.Gui/Views/TextField.cs

@@ -1332,7 +1332,10 @@ public class TextField : View
                                );
                                );
     }
     }
 
 
-    private void ContextMenu_KeyChanged (object sender, KeyChangedEventArgs e) { KeyBindings.Replace (e.OldKey.KeyCode, e.NewKey.KeyCode); }
+    private void ContextMenu_KeyChanged (object sender, KeyChangedEventArgs e)
+    {
+        KeyBindings.ReplaceKey (e.OldKey.KeyCode, e.NewKey.KeyCode);
+    }
 
 
     private List<Rune> DeleteSelectedText ()
     private List<Rune> DeleteSelectedText ()
     {
     {

+ 0 - 1
Terminal.Gui/Views/TextValidateField.cs

@@ -464,7 +464,6 @@ namespace Terminal.Gui
             KeyBindings.Add (Key.Home, Command.LeftHome);
             KeyBindings.Add (Key.Home, Command.LeftHome);
             KeyBindings.Add (Key.End, Command.RightEnd);
             KeyBindings.Add (Key.End, Command.RightEnd);
 
 
-            KeyBindings.Add (Key.Delete, Command.DeleteCharRight);
             KeyBindings.Add (Key.Delete, Command.DeleteCharRight);
             KeyBindings.Add (Key.Delete, Command.DeleteCharRight);
 
 
             KeyBindings.Add (Key.Backspace, Command.DeleteCharLeft);
             KeyBindings.Add (Key.Backspace, Command.DeleteCharLeft);

+ 9 - 9
Terminal.Gui/Views/TextView.cs

@@ -2369,8 +2369,8 @@ public class TextView : View
                    );
                    );
         AddCommand (Command.Tab, () => ProcessTab ());
         AddCommand (Command.Tab, () => ProcessTab ());
         AddCommand (Command.BackTab, () => ProcessBackTab ());
         AddCommand (Command.BackTab, () => ProcessBackTab ());
-        AddCommand (Command.NextView, () => ProcessMoveNextView ());
-        AddCommand (Command.PreviousView, () => ProcessMovePreviousView ());
+        //AddCommand (Command.NextView, () => ProcessMoveNextView ());
+        //AddCommand (Command.PreviousView, () => ProcessMovePreviousView ());
 
 
         AddCommand (
         AddCommand (
                     Command.Undo,
                     Command.Undo,
@@ -2503,11 +2503,11 @@ public class TextView : View
         KeyBindings.Add (Key.Tab, Command.Tab);
         KeyBindings.Add (Key.Tab, Command.Tab);
         KeyBindings.Add (Key.Tab.WithShift, Command.BackTab);
         KeyBindings.Add (Key.Tab.WithShift, Command.BackTab);
 
 
-        KeyBindings.Add (Key.Tab.WithCtrl, Command.NextView);
-        KeyBindings.Add (Application.AlternateForwardKey, Command.NextView);
+        //KeyBindings.Add (Key.Tab.WithCtrl, Command.NextView);
+        //KeyBindings.Add (Application.AlternateForwardKey, Command.NextView);
 
 
-        KeyBindings.Add (Key.Tab.WithCtrl.WithShift, Command.PreviousView);
-        KeyBindings.Add (Application.AlternateBackwardKey, Command.PreviousView);
+        //KeyBindings.Add (Key.Tab.WithCtrl.WithShift, Command.PreviousView);
+        //KeyBindings.Add (Application.AlternateBackwardKey, Command.PreviousView);
 
 
         KeyBindings.Add (Key.Z.WithCtrl, Command.Undo);
         KeyBindings.Add (Key.Z.WithCtrl, Command.Undo);
         KeyBindings.Add (Key.R.WithCtrl, Command.Redo);
         KeyBindings.Add (Key.R.WithCtrl, Command.Redo);
@@ -4318,7 +4318,7 @@ public class TextView : View
         DoNeededAction ();
         DoNeededAction ();
     }
     }
 
 
-    private void ContextMenu_KeyChanged (object sender, KeyChangedEventArgs e) { KeyBindings.Replace (e.OldKey, e.NewKey); }
+    private void ContextMenu_KeyChanged (object sender, KeyChangedEventArgs e) { KeyBindings.ReplaceKey (e.OldKey, e.NewKey); }
 
 
     private bool DeleteTextBackwards ()
     private bool DeleteTextBackwards ()
     {
     {
@@ -6393,8 +6393,8 @@ public class TextView : View
         _selectionStartRow = CurrentRow;
         _selectionStartRow = CurrentRow;
     }
     }
 
 
-    private void Top_AlternateBackwardKeyChanged (object sender, KeyChangedEventArgs e) { KeyBindings.Replace (e.OldKey, e.NewKey); }
-    private void Top_AlternateForwardKeyChanged (object sender, KeyChangedEventArgs e) { KeyBindings.Replace (e.OldKey, e.NewKey); }
+    private void Top_AlternateBackwardKeyChanged (object sender, KeyChangedEventArgs e) { KeyBindings.ReplaceKey (e.OldKey, e.NewKey); }
+    private void Top_AlternateForwardKeyChanged (object sender, KeyChangedEventArgs e) { KeyBindings.ReplaceKey (e.OldKey, e.NewKey); }
 
 
     // Tries to snap the cursor to the tracking column
     // Tries to snap the cursor to the tracking column
     private void TrackColumn ()
     private void TrackColumn ()

+ 13 - 13
Terminal.Gui/Views/TimeField.cs

@@ -58,26 +58,26 @@ public class TimeField : TextField
         AddCommand (Command.RightEnd, () => MoveEnd ());
         AddCommand (Command.RightEnd, () => MoveEnd ());
         AddCommand (Command.Right, () => MoveRight ());
         AddCommand (Command.Right, () => MoveRight ());
 
 
-        // Default keybindings for this view
-        KeyBindings.Add (Key.Delete, Command.DeleteCharRight);
-        KeyBindings.Add (Key.D.WithCtrl, Command.DeleteCharRight);
+        // Replace the key bindings defined in TextField
+        KeyBindings.ReplaceCommands (Key.Delete, Command.DeleteCharRight);
+        KeyBindings.ReplaceCommands (Key.D.WithCtrl, Command.DeleteCharRight);
 
 
-        KeyBindings.Add (Key.Backspace, Command.DeleteCharLeft);
+        KeyBindings.ReplaceCommands (Key.Backspace, Command.DeleteCharLeft);
 
 
-        KeyBindings.Add (Key.Home, Command.LeftHome);
-        KeyBindings.Add (Key.A.WithCtrl, Command.LeftHome);
+        KeyBindings.ReplaceCommands (Key.Home, Command.LeftHome);
+        KeyBindings.ReplaceCommands (Key.A.WithCtrl, Command.LeftHome);
 
 
-        KeyBindings.Add (Key.CursorLeft, Command.Left);
-        KeyBindings.Add (Key.B.WithCtrl, Command.Left);
+        KeyBindings.ReplaceCommands (Key.CursorLeft, Command.Left);
+        KeyBindings.ReplaceCommands (Key.B.WithCtrl, Command.Left);
 
 
-        KeyBindings.Add (Key.End, Command.RightEnd);
-        KeyBindings.Add (Key.E.WithCtrl, Command.RightEnd);
+        KeyBindings.ReplaceCommands (Key.End, Command.RightEnd);
+        KeyBindings.ReplaceCommands (Key.E.WithCtrl, Command.RightEnd);
 
 
-        KeyBindings.Add (Key.CursorRight, Command.Right);
-        KeyBindings.Add (Key.F.WithCtrl, Command.Right);
+        KeyBindings.ReplaceCommands (Key.CursorRight, Command.Right);
+        KeyBindings.ReplaceCommands (Key.F.WithCtrl, Command.Right);
 
 
 #if UNIX_KEY_BINDINGS
 #if UNIX_KEY_BINDINGS
-        KeyBindings.Add (Key.D.WithAlt, Command.DeleteCharLeft);
+        KeyBindings.ReplaceCommands (Key.D.WithAlt, Command.DeleteCharLeft);
 #endif
 #endif
     }
     }
 
 

+ 8 - 8
Terminal.Gui/Views/Toplevel.cs

@@ -34,7 +34,7 @@ public partial class Toplevel : View
 
 
         ColorScheme = Colors.ColorSchemes ["TopLevel"];
         ColorScheme = Colors.ColorSchemes ["TopLevel"];
 
 
-        ConfigureKeyBindings ();
+        //ConfigureKeyBindings ();
 
 
         MouseClick += Toplevel_MouseClick;
         MouseClick += Toplevel_MouseClick;
     }
     }
@@ -188,7 +188,7 @@ public partial class Toplevel : View
     /// <param name="e"></param>
     /// <param name="e"></param>
     public virtual void OnAlternateBackwardKeyChanged (KeyChangedEventArgs e)
     public virtual void OnAlternateBackwardKeyChanged (KeyChangedEventArgs e)
     {
     {
-        KeyBindings.Replace (e.OldKey, e.NewKey);
+        KeyBindings.ReplaceKey (e.OldKey, e.NewKey);
         AlternateBackwardKeyChanged?.Invoke (this, e);
         AlternateBackwardKeyChanged?.Invoke (this, e);
     }
     }
 
 
@@ -197,7 +197,7 @@ public partial class Toplevel : View
     /// <param name="e"></param>
     /// <param name="e"></param>
     public virtual void OnAlternateForwardKeyChanged (KeyChangedEventArgs e)
     public virtual void OnAlternateForwardKeyChanged (KeyChangedEventArgs e)
     {
     {
-        KeyBindings.Replace (e.OldKey, e.NewKey);
+        KeyBindings.ReplaceKey (e.OldKey, e.NewKey);
         AlternateForwardKeyChanged?.Invoke (this, e);
         AlternateForwardKeyChanged?.Invoke (this, e);
     }
     }
 
 
@@ -205,7 +205,7 @@ public partial class Toplevel : View
     /// <param name="e"></param>
     /// <param name="e"></param>
     public virtual void OnQuitKeyChanged (KeyChangedEventArgs e)
     public virtual void OnQuitKeyChanged (KeyChangedEventArgs e)
     {
     {
-        KeyBindings.Replace (e.OldKey, e.NewKey);
+        KeyBindings.ReplaceKey (e.OldKey, e.NewKey);
         QuitKeyChanged?.Invoke (this, e);
         QuitKeyChanged?.Invoke (this, e);
     }
     }
 
 
@@ -650,7 +650,7 @@ public partial class Toplevel : View
     /// <summary>
     /// <summary>
     ///     Moves the focus to 
     ///     Moves the focus to 
     /// </summary>
     /// </summary>
-    private void MoveNextView ()
+    internal void MoveNextView ()
     {
     {
         View old = GetDeepestFocusedSubview (Focused);
         View old = GetDeepestFocusedSubview (Focused);
 
 
@@ -670,7 +670,7 @@ public partial class Toplevel : View
         }
         }
     }
     }
 
 
-    private void MoveNextViewOrTop ()
+    internal void MoveNextViewOrTop ()
     {
     {
         if (Application.OverlappedTop is null)
         if (Application.OverlappedTop is null)
         {
         {
@@ -691,7 +691,7 @@ public partial class Toplevel : View
         }
         }
     }
     }
 
 
-    private void MovePreviousView ()
+    internal void MovePreviousView ()
     {
     {
         View old = GetDeepestFocusedSubview (Focused);
         View old = GetDeepestFocusedSubview (Focused);
 
 
@@ -711,7 +711,7 @@ public partial class Toplevel : View
         }
         }
     }
     }
 
 
-    private void MovePreviousViewOrTop ()
+    internal void MovePreviousViewOrTop ()
     {
     {
         if (Application.OverlappedTop is null)
         if (Application.OverlappedTop is null)
         {
         {

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

@@ -352,7 +352,7 @@ public class TreeView<T> : View, ITreeView where T : class
         {
         {
             if (objectActivationKey != value)
             if (objectActivationKey != value)
             {
             {
-                KeyBindings.Replace (ObjectActivationKey, value);
+                KeyBindings.ReplaceKey (ObjectActivationKey, value);
                 objectActivationKey = value;
                 objectActivationKey = value;
             }
             }
         }
         }

+ 20 - 17
UICatalog/Scenarios/KeyBindings.cs

@@ -80,13 +80,10 @@ public sealed class KeyBindings : Scenario
         };
         };
         appWindow.Add (appBindingsListView);
         appWindow.Add (appBindingsListView);
 
 
-        foreach (var appBinding in Application.GetKeyBindings ())
+        foreach (var appBinding in Application.KeyBindings.Bindings)
         {
         {
-            foreach (var view in appBinding.Value)
-            {
-                var commands = view.KeyBindings.GetCommands (appBinding.Key);
-                appBindings.Add ($"{appBinding.Key} -> {view.GetType ().Name} - {commands [0]}");
-            }
+                var commands = Application.KeyBindings.GetCommands (appBinding.Key);
+                appBindings.Add ($"{appBinding.Key} -> {appBinding.Value.BoundView?.GetType ().Name} - {commands [0]}");
         }
         }
 
 
         ObservableCollection<string> hotkeyBindings = new ();
         ObservableCollection<string> hotkeyBindings = new ();
@@ -153,10 +150,10 @@ public sealed class KeyBindings : Scenario
 
 
     private void AppWindow_Leave (object sender, FocusEventArgs e)
     private void AppWindow_Leave (object sender, FocusEventArgs e)
     {
     {
-        //foreach (var binding in Application.Top.MostFocused.KeyBindings.Bindings.Where (b => b.Value.Scope == KeyBindingScope.Focused))
-        //{
-        //    _focusedBindings.Add ($"{binding.Key} -> {binding.Value.Commands [0]}");
-        //}
+        foreach (var binding in Application.Top.MostFocused.KeyBindings.Bindings.Where (b => b.Value.Scope == KeyBindingScope.Focused))
+        {
+            _focusedBindings.Add ($"{binding.Key} -> {binding.Value.Commands [0]}");
+        }
     }
     }
 }
 }
 
 
@@ -166,28 +163,34 @@ public class KeyBindingsDemo : View
     {
     {
         CanFocus = true;
         CanFocus = true;
 
 
+
+        AddCommand (Command.Save, ctx =>
+                                 {
+                                     MessageBox.Query ($"{ctx.KeyBinding?.Scope}", $"Key: {ctx.Key}\nCommand: {ctx.Command}", buttons: "Ok");
+                                     return true;
+                                 });
         AddCommand (Command.New, ctx =>
         AddCommand (Command.New, ctx =>
                                 {
                                 {
-                                    MessageBox.Query ("Hi", $"Key: {ctx.Key}\nCommand: {ctx.Command}", buttons: "Ok");
-
+                                    MessageBox.Query ($"{ctx.KeyBinding?.Scope}", $"Key: {ctx.Key}\nCommand: {ctx.Command}", buttons: "Ok");
                                     return true;
                                     return true;
                                 });
                                 });
         AddCommand (Command.HotKey, ctx =>
         AddCommand (Command.HotKey, ctx =>
         {
         {
-            MessageBox.Query ("Hi", $"Key: {ctx.Key}\nCommand: {ctx.Command}", buttons: "Ok");
+            MessageBox.Query ($"{ctx.KeyBinding?.Scope}", $"Key: {ctx.Key}\nCommand: {ctx.Command}", buttons: "Ok");
             SetFocus ();
             SetFocus ();
             return true;
             return true;
         });
         });
 
 
-        KeyBindings.Add (Key.F3, KeyBindingScope.Focused, Command.New);
-        KeyBindings.Add (Key.F4, KeyBindingScope.Application, Command.New);
-
+        KeyBindings.Add (Key.F2, KeyBindingScope.Focused, Command.Save);
+        KeyBindings.Add (Key.F3, Command.New); // same as specifying KeyBindingScope.Focused
+        Application.KeyBindings.Add (Key.F4, this, Command.New);
 
 
         AddCommand (Command.QuitToplevel, ctx =>
         AddCommand (Command.QuitToplevel, ctx =>
                                          {
                                          {
+                                             MessageBox.Query ($"{ctx.KeyBinding?.Scope}", $"Key: {ctx.Key}\nCommand: {ctx.Command}", buttons: "Ok");
                                              Application.RequestStop ();
                                              Application.RequestStop ();
                                              return true;
                                              return true;
                                          });
                                          });
-        KeyBindings.Add (Key.Q.WithCtrl, KeyBindingScope.Application, Command.QuitToplevel);
+        Application.KeyBindings.Add (Key.Q.WithAlt, this, Command.QuitToplevel);
     }
     }
 }
 }

+ 1 - 1
UICatalog/Scenarios/ListColumns.cs

@@ -254,7 +254,7 @@ public class ListColumns : Scenario
         // if user clicks the mouse in TableView
         // if user clicks the mouse in TableView
         _listColView.MouseClick += (s, e) => { _listColView.ScreenToCell (e.MouseEvent.Position, out int? clickedCol); };
         _listColView.MouseClick += (s, e) => { _listColView.ScreenToCell (e.MouseEvent.Position, out int? clickedCol); };
 
 
-        _listColView.KeyBindings.Add (Key.Space, Command.Accept);
+        _listColView.KeyBindings.ReplaceCommands (Key.Space, Command.Accept);
 
 
         top.Add (appWindow);
         top.Add (appWindow);
 
 

+ 1 - 1
UICatalog/Scenarios/TableEditor.cs

@@ -769,7 +769,7 @@ public class TableEditor : Scenario
                                      }
                                      }
                                  };
                                  };
 
 
-        _tableView.KeyBindings.Add (Key.Space, Command.Accept);
+        _tableView.KeyBindings.ReplaceCommands (Key.Space, Command.Accept);
 
 
         // Run - Start the application.
         // Run - Start the application.
         Application.Run (appWindow);
         Application.Run (appWindow);

+ 2 - 0
UICatalog/UICatalog.cs

@@ -605,7 +605,9 @@ internal class UICatalogApp
             ScenarioList.CellActivated += ScenarioView_OpenSelectedItem;
             ScenarioList.CellActivated += ScenarioView_OpenSelectedItem;
 
 
             // TableView typically is a grid where nav keys are biased for moving left/right.
             // TableView typically is a grid where nav keys are biased for moving left/right.
+            ScenarioList.KeyBindings.Remove (Key.Home);
             ScenarioList.KeyBindings.Add (Key.Home, Command.TopHome);
             ScenarioList.KeyBindings.Add (Key.Home, Command.TopHome);
+            ScenarioList.KeyBindings.Remove (Key.End);
             ScenarioList.KeyBindings.Add (Key.End, Command.BottomEnd);
             ScenarioList.KeyBindings.Add (Key.End, Command.BottomEnd);
 
 
             // Ideally, TableView.MultiSelect = false would turn off any keybindings for
             // Ideally, TableView.MultiSelect = false would turn off any keybindings for

+ 2 - 2
UnitTests/Application/ApplicationTests.cs

@@ -199,7 +199,7 @@ public class ApplicationTests
             Assert.Null (Application._mouseEnteredView);
             Assert.Null (Application._mouseEnteredView);
 
 
             // Keyboard
             // Keyboard
-            Assert.Empty (Application.GetViewsWithKeyBindings ());
+            Assert.Empty (Application.GetViewKeyBindings ());
 
 
             // Events - Can't check
             // Events - Can't check
             //Assert.Null (Application.NotifyNewRunState);
             //Assert.Null (Application.NotifyNewRunState);
@@ -233,7 +233,7 @@ public class ApplicationTests
         Application.AlternateBackwardKey = Key.A;
         Application.AlternateBackwardKey = Key.A;
         Application.AlternateForwardKey = Key.B;
         Application.AlternateForwardKey = Key.B;
         Application.QuitKey = Key.C;
         Application.QuitKey = Key.C;
-        Application.AddKeyBinding (Key.A, new View ());
+        Application.KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Cancel);
 
 
         //Application.OverlappedChildren = new List<View> ();
         //Application.OverlappedChildren = new List<View> ();
         //Application.OverlappedTop = 
         //Application.OverlappedTop = 

+ 61 - 71
UnitTests/Application/KeyboardTests.cs

@@ -71,7 +71,7 @@ public class KeyboardTests
         // After Init
         // After Init
         Assert.Equal (Key.Esc, Application.QuitKey);
         Assert.Equal (Key.Esc, Application.QuitKey);
 
 
-        Application.Shutdown();
+        Application.Shutdown ();
     }
     }
 
 
     private object _timeoutLock;
     private object _timeoutLock;
@@ -200,62 +200,62 @@ public class KeyboardTests
                                      Assert.True (v1.HasFocus);
                                      Assert.True (v1.HasFocus);
 
 
                                      // Using default keys.
                                      // Using default keys.
-                                     top.NewKeyDownEvent (Key.Tab.WithCtrl);
+                                     Application.OnKeyDown (Key.Tab.WithCtrl);
                                      Assert.True (v2.HasFocus);
                                      Assert.True (v2.HasFocus);
-                                     top.NewKeyDownEvent (Key.Tab.WithCtrl);
+                                     Application.OnKeyDown (Key.Tab.WithCtrl);
                                      Assert.True (v3.HasFocus);
                                      Assert.True (v3.HasFocus);
-                                     top.NewKeyDownEvent (Key.Tab.WithCtrl);
+                                     Application.OnKeyDown (Key.Tab.WithCtrl);
                                      Assert.True (v4.HasFocus);
                                      Assert.True (v4.HasFocus);
-                                     top.NewKeyDownEvent (Key.Tab.WithCtrl);
+                                     Application.OnKeyDown (Key.Tab.WithCtrl);
                                      Assert.True (v1.HasFocus);
                                      Assert.True (v1.HasFocus);
 
 
-                                     top.NewKeyDownEvent (Key.Tab.WithShift.WithCtrl);
+                                     Application.OnKeyDown (Key.Tab.WithShift.WithCtrl);
                                      Assert.True (v4.HasFocus);
                                      Assert.True (v4.HasFocus);
-                                     top.NewKeyDownEvent (Key.Tab.WithShift.WithCtrl);
+                                     Application.OnKeyDown (Key.Tab.WithShift.WithCtrl);
                                      Assert.True (v3.HasFocus);
                                      Assert.True (v3.HasFocus);
-                                     top.NewKeyDownEvent (Key.Tab.WithShift.WithCtrl);
+                                     Application.OnKeyDown (Key.Tab.WithShift.WithCtrl);
                                      Assert.True (v2.HasFocus);
                                      Assert.True (v2.HasFocus);
-                                     top.NewKeyDownEvent (Key.Tab.WithShift.WithCtrl);
+                                     Application.OnKeyDown (Key.Tab.WithShift.WithCtrl);
                                      Assert.True (v1.HasFocus);
                                      Assert.True (v1.HasFocus);
 
 
-                                     top.NewKeyDownEvent (Key.PageDown.WithCtrl);
+                                     Application.OnKeyDown (Key.PageDown.WithCtrl);
                                      Assert.True (v2.HasFocus);
                                      Assert.True (v2.HasFocus);
-                                     top.NewKeyDownEvent (Key.PageDown.WithCtrl);
+                                     Application.OnKeyDown (Key.PageDown.WithCtrl);
                                      Assert.True (v3.HasFocus);
                                      Assert.True (v3.HasFocus);
-                                     top.NewKeyDownEvent (Key.PageDown.WithCtrl);
+                                     Application.OnKeyDown (Key.PageDown.WithCtrl);
                                      Assert.True (v4.HasFocus);
                                      Assert.True (v4.HasFocus);
-                                     top.NewKeyDownEvent (Key.PageDown.WithCtrl);
+                                     Application.OnKeyDown (Key.PageDown.WithCtrl);
                                      Assert.True (v1.HasFocus);
                                      Assert.True (v1.HasFocus);
 
 
-                                     top.NewKeyDownEvent (Key.PageUp.WithCtrl);
+                                     Application.OnKeyDown (Key.PageUp.WithCtrl);
                                      Assert.True (v4.HasFocus);
                                      Assert.True (v4.HasFocus);
-                                     top.NewKeyDownEvent (Key.PageUp.WithCtrl);
+                                     Application.OnKeyDown (Key.PageUp.WithCtrl);
                                      Assert.True (v3.HasFocus);
                                      Assert.True (v3.HasFocus);
-                                     top.NewKeyDownEvent (Key.PageUp.WithCtrl);
+                                     Application.OnKeyDown (Key.PageUp.WithCtrl);
                                      Assert.True (v2.HasFocus);
                                      Assert.True (v2.HasFocus);
-                                     top.NewKeyDownEvent (Key.PageUp.WithCtrl);
+                                     Application.OnKeyDown (Key.PageUp.WithCtrl);
                                      Assert.True (v1.HasFocus);
                                      Assert.True (v1.HasFocus);
 
 
                                      // Using another's alternate keys.
                                      // Using another's alternate keys.
                                      Application.AlternateForwardKey = Key.F7;
                                      Application.AlternateForwardKey = Key.F7;
                                      Application.AlternateBackwardKey = Key.F6;
                                      Application.AlternateBackwardKey = Key.F6;
 
 
-                                     top.NewKeyDownEvent (Key.F7);
+                                     Application.OnKeyDown (Key.F7);
                                      Assert.True (v2.HasFocus);
                                      Assert.True (v2.HasFocus);
-                                     top.NewKeyDownEvent (Key.F7);
+                                     Application.OnKeyDown (Key.F7);
                                      Assert.True (v3.HasFocus);
                                      Assert.True (v3.HasFocus);
-                                     top.NewKeyDownEvent (Key.F7);
+                                     Application.OnKeyDown (Key.F7);
                                      Assert.True (v4.HasFocus);
                                      Assert.True (v4.HasFocus);
-                                     top.NewKeyDownEvent (Key.F7);
+                                     Application.OnKeyDown (Key.F7);
                                      Assert.True (v1.HasFocus);
                                      Assert.True (v1.HasFocus);
 
 
-                                     top.NewKeyDownEvent (Key.F6);
+                                     Application.OnKeyDown (Key.F6);
                                      Assert.True (v4.HasFocus);
                                      Assert.True (v4.HasFocus);
-                                     top.NewKeyDownEvent (Key.F6);
+                                     Application.OnKeyDown (Key.F6);
                                      Assert.True (v3.HasFocus);
                                      Assert.True (v3.HasFocus);
-                                     top.NewKeyDownEvent (Key.F6);
+                                     Application.OnKeyDown (Key.F6);
                                      Assert.True (v2.HasFocus);
                                      Assert.True (v2.HasFocus);
-                                     top.NewKeyDownEvent (Key.F6);
+                                     Application.OnKeyDown (Key.F6);
                                      Assert.True (v1.HasFocus);
                                      Assert.True (v1.HasFocus);
 
 
                                      Application.RequestStop ();
                                      Application.RequestStop ();
@@ -321,14 +321,14 @@ public class KeyboardTests
         Assert.True (win2.HasFocus);
         Assert.True (win2.HasFocus);
         Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
         Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
 
 
-        top.NewKeyDownEvent (Key.Tab.WithCtrl);
+        Application.OnKeyDown (Key.Tab.WithCtrl);
         Assert.True (win2.CanFocus);
         Assert.True (win2.CanFocus);
         Assert.False (win.HasFocus);
         Assert.False (win.HasFocus);
         Assert.True (win2.CanFocus);
         Assert.True (win2.CanFocus);
         Assert.True (win2.HasFocus);
         Assert.True (win2.HasFocus);
         Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
         Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
 
 
-        top.NewKeyDownEvent (Key.Tab.WithCtrl);
+        Application.OnKeyDown (Key.Tab.WithCtrl);
         Assert.False (win.CanFocus);
         Assert.False (win.CanFocus);
         Assert.False (win.HasFocus);
         Assert.False (win.HasFocus);
         Assert.True (win2.CanFocus);
         Assert.True (win2.CanFocus);
@@ -374,14 +374,14 @@ public class KeyboardTests
         Assert.False (win2.HasFocus);
         Assert.False (win2.HasFocus);
         Assert.Equal ("win", ((Window)top.Subviews [^1]).Title);
         Assert.Equal ("win", ((Window)top.Subviews [^1]).Title);
 
 
-        top.NewKeyDownEvent (Key.Tab.WithCtrl);
+        Application.OnKeyDown (Key.Tab.WithCtrl);
         Assert.True (win.CanFocus);
         Assert.True (win.CanFocus);
         Assert.False (win.HasFocus);
         Assert.False (win.HasFocus);
         Assert.True (win2.CanFocus);
         Assert.True (win2.CanFocus);
         Assert.True (win2.HasFocus);
         Assert.True (win2.HasFocus);
         Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
         Assert.Equal ("win2", ((Window)top.Subviews [^1]).Title);
 
 
-        top.NewKeyDownEvent (Key.Tab.WithCtrl);
+        Application.OnKeyDown (Key.Tab.WithCtrl);
         Assert.True (win.CanFocus);
         Assert.True (win.CanFocus);
         Assert.True (win.HasFocus);
         Assert.True (win.HasFocus);
         Assert.True (win2.CanFocus);
         Assert.True (win2.CanFocus);
@@ -496,21 +496,21 @@ public class KeyboardTests
         Application.Begin (top);
         Application.Begin (top);
 
 
         Application.OnKeyDown (Key.A);
         Application.OnKeyDown (Key.A);
-        Assert.True (invoked);
+        Assert.False (invoked);
         Assert.True (view.ApplicationCommand);
         Assert.True (view.ApplicationCommand);
 
 
         invoked = false;
         invoked = false;
         view.ApplicationCommand = false;
         view.ApplicationCommand = false;
-        view.KeyBindings.Remove (KeyCode.A);
+        Application.KeyBindings.Remove (KeyCode.A); 
         Application.OnKeyDown (Key.A); // old
         Application.OnKeyDown (Key.A); // old
         Assert.False (invoked);
         Assert.False (invoked);
         Assert.False (view.ApplicationCommand);
         Assert.False (view.ApplicationCommand);
-        view.KeyBindings.Add (Key.A.WithCtrl, KeyBindingScope.Application, Command.Save);
+        Application.KeyBindings.Add (Key.A.WithCtrl, view, Command.Save);
         Application.OnKeyDown (Key.A); // old
         Application.OnKeyDown (Key.A); // old
         Assert.False (invoked);
         Assert.False (invoked);
         Assert.False (view.ApplicationCommand);
         Assert.False (view.ApplicationCommand);
         Application.OnKeyDown (Key.A.WithCtrl); // new
         Application.OnKeyDown (Key.A.WithCtrl); // new
-        Assert.True (invoked);
+        Assert.False (invoked);
         Assert.True (view.ApplicationCommand);
         Assert.True (view.ApplicationCommand);
 
 
         invoked = false;
         invoked = false;
@@ -556,70 +556,60 @@ public class KeyboardTests
         top.Dispose ();
         top.Dispose ();
     }
     }
 
 
-
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void KeyBinding_AddKeyBinding_Adds ()
+    public void KeyBinding_Application_KeyBindings_Add_Adds ()
     {
     {
-        View view1 = new ();
-        Application.AddKeyBinding (Key.A, view1);
+        Application.KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Accept);
+        Application.KeyBindings.Add (Key.B, KeyBindingScope.Application, Command.Accept);
 
 
-        View view2 = new ();
-        Application.AddKeyBinding (Key.A, view2);
-
-        Assert.True (Application.TryGetKeyBindings (Key.A, out List<View> views));
-        Assert.Contains (view1, views);
-        Assert.Contains (view2, views);
-
-        Assert.False (Application.TryGetKeyBindings (Key.B, out List<View> _));
+        Assert.True (Application.KeyBindings.TryGet (Key.A, out var binding));
+        Assert.Null (binding.BoundView);
+        Assert.True (Application.KeyBindings.TryGet (Key.B, out binding));
+        Assert.Null (binding.BoundView);
     }
     }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void KeyBinding_ViewKeyBindings_Add_Adds ()
+    public void KeyBinding_View_KeyBindings_Add_Adds ()
     {
     {
         View view1 = new ();
         View view1 = new ();
-        view1.KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Save);
-        view1.KeyBindings.Add (Key.B, KeyBindingScope.HotKey, Command.Left);
-        Assert.Single (Application.GetViewsWithKeyBindings ());
+        Application.KeyBindings.Add (Key.A, view1, Command.Accept);
 
 
         View view2 = new ();
         View view2 = new ();
-        view2.KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Save);
-        view2.KeyBindings.Add (Key.B, KeyBindingScope.HotKey, Command.Left);
-
-        Assert.True (Application.TryGetKeyBindings (Key.A, out List<View> views));
-        Assert.Contains (view1, views);
-        Assert.Contains (view2, views);
+        Application.KeyBindings.Add (Key.B, view2, Command.Accept);
 
 
-        Assert.False (Application.TryGetKeyBindings (Key.B, out List<View> _));
+        Assert.True (Application.KeyBindings.TryGet (Key.A, out var binding));
+        Assert.Equal (view1, binding.BoundView);
+        Assert.True (Application.KeyBindings.TryGet (Key.B, out binding));
+        Assert.Equal (view2, binding.BoundView);
     }
     }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void KeyBinding_RemoveKeyBinding_Removes ()
+    public void KeyBinding_Application_RemoveKeyBinding_Removes ()
     {
     {
-        View view1 = new ();
-        Application.AddKeyBinding (Key.A, view1);
+        Application.KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Accept);
 
 
-        Assert.True (Application.TryGetKeyBindings (Key.A, out List<View> views));
-        Assert.Contains (view1, views);
+        Assert.True (Application.KeyBindings.TryGet (Key.A, out _));
 
 
-        Application.RemoveKeyBinding (Key.A, view1);
-        Assert.False (Application.TryGetKeyBindings (Key.A, out List<View> _));
+        Application.KeyBindings.Remove (Key.A);
+        Assert.False (Application.KeyBindings.TryGet (Key.A, out _));
     }
     }
 
 
     [Fact]
     [Fact]
     [AutoInitShutdown]
     [AutoInitShutdown]
-    public void KeyBinding_ViewKeyBindings_RemoveKeyBinding_Removes ()
+    public void KeyBinding_View_KeyBindings_RemoveKeyBinding_Removes ()
     {
     {
+
         View view1 = new ();
         View view1 = new ();
-        view1.KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Save);
+        Application.KeyBindings.Add (Key.A, view1, Command.Accept);
 
 
-        Assert.True (Application.TryGetKeyBindings (Key.A, out List<View> views));
-        Assert.Contains (view1, views);
+        View view2 = new ();
+        Application.KeyBindings.Add (Key.B, view1, Command.Accept);
 
 
-        view1.KeyBindings.Remove (Key.A);
-        Assert.False (Application.TryGetKeyBindings (Key.A, out List<View> _));
+        Application.KeyBindings.Remove (Key.A, view1);
+        Assert.False (Application.KeyBindings.TryGet (Key.A, out _));
     }
     }
 
 
     // Test View for testing Application key Bindings
     // Test View for testing Application key Bindings
@@ -631,9 +621,9 @@ public class KeyboardTests
             AddCommand (Command.HotKey, () => HotKeyCommand = true);
             AddCommand (Command.HotKey, () => HotKeyCommand = true);
             AddCommand (Command.Left, () => FocusedCommand = true);
             AddCommand (Command.Left, () => FocusedCommand = true);
 
 
-            KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Save);
+            Application.KeyBindings.Add (Key.A, this, Command.Save);
             HotKey = KeyCode.H;
             HotKey = KeyCode.H;
-            KeyBindings.Add (Key.F, KeyBindingScope.Focused, Command.Left);
+            KeyBindings.Add (Key.F, Command.Left);
         }
         }
 
 
         public bool ApplicationCommand { get; set; }
         public bool ApplicationCommand { get; set; }

+ 32 - 32
UnitTests/Input/KeyBindingTests.cs

@@ -21,12 +21,12 @@ public class KeyBindingTests
         var keyBindings = new KeyBindings ();
         var keyBindings = new KeyBindings ();
         Command [] commands = { Command.Right, Command.Left };
         Command [] commands = { Command.Right, Command.Left };
 
 
-        keyBindings.Add (Key.A, commands);
+        keyBindings.Add (Key.A, KeyBindingScope.Application, commands);
         Command [] resultCommands = keyBindings.GetCommands (Key.A);
         Command [] resultCommands = keyBindings.GetCommands (Key.A);
         Assert.Contains (Command.Right, resultCommands);
         Assert.Contains (Command.Right, resultCommands);
         Assert.Contains (Command.Left, resultCommands);
         Assert.Contains (Command.Left, resultCommands);
 
 
-        keyBindings.Add (Key.B, commands);
+        keyBindings.Add (Key.B, KeyBindingScope.Application, commands);
         resultCommands = keyBindings.GetCommands (Key.B);
         resultCommands = keyBindings.GetCommands (Key.B);
         Assert.Contains (Command.Right, resultCommands);
         Assert.Contains (Command.Right, resultCommands);
         Assert.Contains (Command.Left, resultCommands);
         Assert.Contains (Command.Left, resultCommands);
@@ -36,11 +36,11 @@ public class KeyBindingTests
     public void Add_Single_Adds ()
     public void Add_Single_Adds ()
     {
     {
         var keyBindings = new KeyBindings ();
         var keyBindings = new KeyBindings ();
-        keyBindings.Add (Key.A, Command.HotKey);
+        keyBindings.Add (Key.A, KeyBindingScope.Application, Command.HotKey);
         Command [] resultCommands = keyBindings.GetCommands (Key.A);
         Command [] resultCommands = keyBindings.GetCommands (Key.A);
         Assert.Contains (Command.HotKey, resultCommands);
         Assert.Contains (Command.HotKey, resultCommands);
 
 
-        keyBindings.Add (Key.B, Command.HotKey);
+        keyBindings.Add (Key.B, KeyBindingScope.Application, Command.HotKey);
         resultCommands = keyBindings.GetCommands (Key.B);
         resultCommands = keyBindings.GetCommands (Key.B);
         Assert.Contains (Command.HotKey, resultCommands);
         Assert.Contains (Command.HotKey, resultCommands);
     }
     }
@@ -50,7 +50,7 @@ public class KeyBindingTests
     public void Clear_Clears ()
     public void Clear_Clears ()
     {
     {
         var keyBindings = new KeyBindings ();
         var keyBindings = new KeyBindings ();
-        keyBindings.Add (Key.B, Command.HotKey);
+        keyBindings.Add (Key.B, KeyBindingScope.Application, Command.HotKey);
         keyBindings.Clear ();
         keyBindings.Clear ();
         Command [] resultCommands = keyBindings.GetCommands (Key.A);
         Command [] resultCommands = keyBindings.GetCommands (Key.A);
         Assert.Empty (resultCommands);
         Assert.Empty (resultCommands);
@@ -78,7 +78,7 @@ public class KeyBindingTests
     public void GetCommands_WithCommands_ReturnsCommands ()
     public void GetCommands_WithCommands_ReturnsCommands ()
     {
     {
         var keyBindings = new KeyBindings ();
         var keyBindings = new KeyBindings ();
-        keyBindings.Add (Key.A, Command.HotKey);
+        keyBindings.Add (Key.A, KeyBindingScope.Application, Command.HotKey);
         Command [] resultCommands = keyBindings.GetCommands (Key.A);
         Command [] resultCommands = keyBindings.GetCommands (Key.A);
         Assert.Contains (Command.HotKey, resultCommands);
         Assert.Contains (Command.HotKey, resultCommands);
     }
     }
@@ -88,8 +88,8 @@ public class KeyBindingTests
     {
     {
         var keyBindings = new KeyBindings ();
         var keyBindings = new KeyBindings ();
         Command [] commands = { Command.Right, Command.Left };
         Command [] commands = { Command.Right, Command.Left };
-        keyBindings.Add (Key.A, commands);
-        keyBindings.Add (Key.B, commands);
+        keyBindings.Add (Key.A, KeyBindingScope.Application, commands);
+        keyBindings.Add (Key.B, KeyBindingScope.Application, commands);
         Command [] resultCommands = keyBindings.GetCommands (Key.A);
         Command [] resultCommands = keyBindings.GetCommands (Key.A);
         Assert.Contains (Command.Right, resultCommands);
         Assert.Contains (Command.Right, resultCommands);
         Assert.Contains (Command.Left, resultCommands);
         Assert.Contains (Command.Left, resultCommands);
@@ -103,7 +103,7 @@ public class KeyBindingTests
     {
     {
         var keyBindings = new KeyBindings ();
         var keyBindings = new KeyBindings ();
         Command [] commands = { Command.Right, Command.Left };
         Command [] commands = { Command.Right, Command.Left };
-        keyBindings.Add (Key.A, commands);
+        keyBindings.Add (Key.A, KeyBindingScope.Application, commands);
         Command [] resultCommands = keyBindings.GetCommands (Key.A);
         Command [] resultCommands = keyBindings.GetCommands (Key.A);
         Assert.Contains (Command.Right, resultCommands);
         Assert.Contains (Command.Right, resultCommands);
         Assert.Contains (Command.Left, resultCommands);
         Assert.Contains (Command.Left, resultCommands);
@@ -114,10 +114,10 @@ public class KeyBindingTests
     {
     {
         var keyBindings = new KeyBindings ();
         var keyBindings = new KeyBindings ();
         Command [] commands1 = { Command.Right, Command.Left };
         Command [] commands1 = { Command.Right, Command.Left };
-        keyBindings.Add (Key.A, commands1);
+        keyBindings.Add (Key.A, KeyBindingScope.Application, commands1);
 
 
         Command [] commands2 = { Command.LineUp, Command.LineDown };
         Command [] commands2 = { Command.LineUp, Command.LineDown };
-        keyBindings.Add (Key.B, commands2);
+        keyBindings.Add (Key.B, KeyBindingScope.Application, commands2);
 
 
         Key key = keyBindings.GetKeyFromCommands (commands1);
         Key key = keyBindings.GetKeyFromCommands (commands1);
         Assert.Equal (Key.A, key);
         Assert.Equal (Key.A, key);
@@ -133,7 +133,7 @@ public class KeyBindingTests
     public void GetKeyFromCommands_OneCommand ()
     public void GetKeyFromCommands_OneCommand ()
     {
     {
         var keyBindings = new KeyBindings ();
         var keyBindings = new KeyBindings ();
-        keyBindings.Add (Key.A, Command.Right);
+        keyBindings.Add (Key.A, KeyBindingScope.Application, Command.Right);
 
 
         Key key = keyBindings.GetKeyFromCommands (Command.Right);
         Key key = keyBindings.GetKeyFromCommands (Command.Right);
         Assert.Equal (Key.A, key);
         Assert.Equal (Key.A, key);
@@ -154,66 +154,66 @@ public class KeyBindingTests
     public void GetKeyFromCommands_WithCommands_ReturnsKey ()
     public void GetKeyFromCommands_WithCommands_ReturnsKey ()
     {
     {
         var keyBindings = new KeyBindings ();
         var keyBindings = new KeyBindings ();
-        keyBindings.Add (Key.A, Command.HotKey);
+        keyBindings.Add (Key.A, KeyBindingScope.Application, Command.HotKey);
         Key resultKey = keyBindings.GetKeyFromCommands (Command.HotKey);
         Key resultKey = keyBindings.GetKeyFromCommands (Command.HotKey);
         Assert.Equal (Key.A, resultKey);
         Assert.Equal (Key.A, resultKey);
     }
     }
 
 
     // Add should not allow duplicates
     // Add should not allow duplicates
     [Fact]
     [Fact]
-    public void Add_Replaces_If_Exists ()
+    public void Add_Throws_If_Exists ()
     {
     {
         var keyBindings = new KeyBindings ();
         var keyBindings = new KeyBindings ();
-        keyBindings.Add (Key.A, Command.HotKey);
-        keyBindings.Add (Key.A, Command.Accept);
+        keyBindings.Add (Key.A, KeyBindingScope.Application, Command.HotKey);
+        Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, KeyBindingScope.Application, Command.Accept));
 
 
         Command [] resultCommands = keyBindings.GetCommands (Key.A);
         Command [] resultCommands = keyBindings.GetCommands (Key.A);
-        Assert.DoesNotContain (Command.HotKey, resultCommands);
+        Assert.Contains (Command.HotKey, resultCommands);
 
 
         keyBindings = new ();
         keyBindings = new ();
         keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.HotKey);
         keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.HotKey);
-        keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.Accept);
+        Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.Accept));
 
 
         resultCommands = keyBindings.GetCommands (Key.A);
         resultCommands = keyBindings.GetCommands (Key.A);
-        Assert.DoesNotContain (Command.HotKey, resultCommands);
+        Assert.Contains (Command.HotKey, resultCommands);
 
 
         keyBindings = new ();
         keyBindings = new ();
         keyBindings.Add (Key.A, KeyBindingScope.HotKey, Command.HotKey);
         keyBindings.Add (Key.A, KeyBindingScope.HotKey, Command.HotKey);
-        keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.Accept);
+        Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, KeyBindingScope.Focused, Command.Accept));
 
 
         resultCommands = keyBindings.GetCommands (Key.A);
         resultCommands = keyBindings.GetCommands (Key.A);
-        Assert.DoesNotContain (Command.HotKey, resultCommands);
+        Assert.Contains (Command.HotKey, resultCommands);
 
 
         keyBindings = new ();
         keyBindings = new ();
         keyBindings.Add (Key.A, new KeyBinding (new [] { Command.HotKey }, KeyBindingScope.HotKey));
         keyBindings.Add (Key.A, new KeyBinding (new [] { Command.HotKey }, KeyBindingScope.HotKey));
-        keyBindings.Add (Key.A, new KeyBinding (new [] { Command.Accept }, KeyBindingScope.HotKey));
+        Assert.Throws<InvalidOperationException> (() => keyBindings.Add (Key.A, new KeyBinding (new [] { Command.Accept }, KeyBindingScope.HotKey)));
 
 
         resultCommands = keyBindings.GetCommands (Key.A);
         resultCommands = keyBindings.GetCommands (Key.A);
-        Assert.DoesNotContain (Command.HotKey, resultCommands);
+        Assert.Contains (Command.HotKey, resultCommands);
     }
     }
 
 
     [Fact]
     [Fact]
     public void Replace_Key ()
     public void Replace_Key ()
     {
     {
         var keyBindings = new KeyBindings ();
         var keyBindings = new KeyBindings ();
-        keyBindings.Add (Key.A, Command.HotKey);
-        keyBindings.Add (Key.B, Command.HotKey);
-        keyBindings.Add (Key.C, Command.HotKey);
-        keyBindings.Add (Key.D, Command.HotKey);
+        keyBindings.Add (Key.A, KeyBindingScope.Application, Command.HotKey);
+        keyBindings.Add (Key.B, KeyBindingScope.Application, Command.HotKey);
+        keyBindings.Add (Key.C, KeyBindingScope.Application, Command.HotKey);
+        keyBindings.Add (Key.D, KeyBindingScope.Application, Command.HotKey);
 
 
-        keyBindings.Replace (Key.A, Key.E);
+        keyBindings.ReplaceKey (Key.A, Key.E);
         Assert.Empty (keyBindings.GetCommands (Key.A));
         Assert.Empty (keyBindings.GetCommands (Key.A));
         Assert.Contains (Command.HotKey, keyBindings.GetCommands (Key.E));
         Assert.Contains (Command.HotKey, keyBindings.GetCommands (Key.E));
 
 
-        keyBindings.Replace (Key.B, Key.F);
+        keyBindings.ReplaceKey (Key.B, Key.F);
         Assert.Empty (keyBindings.GetCommands (Key.B));
         Assert.Empty (keyBindings.GetCommands (Key.B));
         Assert.Contains (Command.HotKey, keyBindings.GetCommands (Key.F));
         Assert.Contains (Command.HotKey, keyBindings.GetCommands (Key.F));
 
 
-        keyBindings.Replace (Key.C, Key.G);
+        keyBindings.ReplaceKey (Key.C, Key.G);
         Assert.Empty (keyBindings.GetCommands (Key.C));
         Assert.Empty (keyBindings.GetCommands (Key.C));
         Assert.Contains (Command.HotKey, keyBindings.GetCommands (Key.G));
         Assert.Contains (Command.HotKey, keyBindings.GetCommands (Key.G));
 
 
-        keyBindings.Replace (Key.D, Key.H);
+        keyBindings.ReplaceKey (Key.D, Key.H);
         Assert.Empty (keyBindings.GetCommands (Key.D));
         Assert.Empty (keyBindings.GetCommands (Key.D));
         Assert.Contains (Command.HotKey, keyBindings.GetCommands (Key.H));
         Assert.Contains (Command.HotKey, keyBindings.GetCommands (Key.H));
     }
     }
@@ -312,7 +312,7 @@ public class KeyBindingTests
     public void TryGet_WithCommands_ReturnsTrue ()
     public void TryGet_WithCommands_ReturnsTrue ()
     {
     {
         var keyBindings = new KeyBindings ();
         var keyBindings = new KeyBindings ();
-        keyBindings.Add (Key.A, Command.HotKey);
+        keyBindings.Add (Key.A, KeyBindingScope.Application, Command.HotKey);
         bool result = keyBindings.TryGet (Key.A, out KeyBinding bindings);
         bool result = keyBindings.TryGet (Key.A, out KeyBinding bindings);
         Assert.True (result);
         Assert.True (result);
         Assert.Contains (Command.HotKey, bindings.Commands);
         Assert.Contains (Command.HotKey, bindings.Commands);

+ 1 - 187
UnitTests/View/MouseTests.cs

@@ -92,193 +92,7 @@ public class MouseTests (ITestOutputHelper output) : TestsAllViews
         view.NewMouseEvent (new MouseEvent () { Flags = mouseFlags });
         view.NewMouseEvent (new MouseEvent () { Flags = mouseFlags });
         Assert.Equal (mouseFlagsFromEvent, expectedMouseFlagsFromEvent);
         Assert.Equal (mouseFlagsFromEvent, expectedMouseFlagsFromEvent);
     }
     }
-
-    [Theory]
-    [MemberData (nameof (AllViewTypes))]
-
-    public void AllViews_Enter_Leave_Events (Type viewType)
-    {
-        var view = CreateInstanceIfNotGeneric (viewType);
-
-        if (view == null)
-        {
-            output.WriteLine ($"Ignoring {viewType} - It's a Generic");
-            return;
-        }
-
-        if (!view.CanFocus)
-        {
-            output.WriteLine ($"Ignoring {viewType} - It can't focus.");
-
-            return;
-        }
-
-        if (view is Toplevel && ((Toplevel)view).Modal)
-        {
-            output.WriteLine ($"Ignoring {viewType} - It's a Modal Toplevel");
-
-            return;
-        }
-
-        Application.Init (new FakeDriver ());
-
-        Toplevel top = new ()
-        {
-            Height = 10,
-            Width = 10
-        };
-
-        View otherView = new ()
-        {
-            X = 0, Y = 0,
-            Height = 1,
-            Width = 1,
-            CanFocus = true,
-        };
-
-        view.X = Pos.Right (otherView);
-        view.Y = 0;
-        view.Width = 10;
-        view.Height = 1;
-
-        var nEnter = 0;
-        var nLeave = 0;
-
-        view.Enter += (s, e) => nEnter++;
-        view.Leave += (s, e) => nLeave++;
-
-        top.Add (view, otherView);
-        Application.Begin (top);
-
-        // Start with the focus on our test view
-        view.SetFocus ();
-
-        Assert.Equal (1, nEnter);
-        Assert.Equal (0, nLeave);
-
-        // Use keyboard to navigate to next view (otherView). 
-        if (view is TextView)
-        {
-            top.NewKeyDownEvent (Key.Tab.WithCtrl);
-        }
-        else if (view is DatePicker)
-        {
-            for (var i = 0; i < 4; i++)
-            {
-                top.NewKeyDownEvent (Key.Tab.WithCtrl);
-            }
-        }
-        else
-        {
-            top.NewKeyDownEvent (Key.Tab);
-        }
-
-        Assert.Equal (1, nEnter);
-        Assert.Equal (1, nLeave);
-
-        top.NewKeyDownEvent (Key.Tab);
-
-        Assert.Equal (2, nEnter);
-        Assert.Equal (1, nLeave);
-
-        top.Dispose ();
-        Application.Shutdown ();
-    }
-
-
-    [Theory]
-    [MemberData (nameof (AllViewTypes))]
-
-    public void AllViews_Enter_Leave_Events_Visible_False (Type viewType)
-    {
-        var view = CreateInstanceIfNotGeneric (viewType);
-
-        if (view == null)
-        {
-            output.WriteLine ($"Ignoring {viewType} - It's a Generic");
-            return;
-        }
-
-        if (!view.CanFocus)
-        {
-            output.WriteLine ($"Ignoring {viewType} - It can't focus.");
-
-            return;
-        }
-
-        if (view is Toplevel && ((Toplevel)view).Modal)
-        {
-            output.WriteLine ($"Ignoring {viewType} - It's a Modal Toplevel");
-
-            return;
-        }
-
-        Application.Init (new FakeDriver ());
-
-        Toplevel top = new ()
-        {
-            Height = 10,
-            Width = 10
-        };
-
-        View otherView = new ()
-        {
-            X = 0, Y = 0,
-            Height = 1,
-            Width = 1,
-            CanFocus = true,
-        };
-
-        view.Visible = false;
-        view.X = Pos.Right (otherView);
-        view.Y = 0;
-        view.Width = 10;
-        view.Height = 1;
-
-        var nEnter = 0;
-        var nLeave = 0;
-
-        view.Enter += (s, e) => nEnter++;
-        view.Leave += (s, e) => nLeave++;
-
-        top.Add (view, otherView);
-        Application.Begin (top);
-
-        // Start with the focus on our test view
-        view.SetFocus ();
-
-        Assert.Equal (0, nEnter);
-        Assert.Equal (0, nLeave);
-
-        // Use keyboard to navigate to next view (otherView). 
-        if (view is TextView)
-        {
-            top.NewKeyDownEvent (Key.Tab.WithCtrl);
-        }
-        else if (view is DatePicker)
-        {
-            for (var i = 0; i < 4; i++)
-            {
-                top.NewKeyDownEvent (Key.Tab.WithCtrl);
-            }
-        }
-        else
-        {
-            top.NewKeyDownEvent (Key.Tab);
-        }
-
-        Assert.Equal (0, nEnter);
-        Assert.Equal (0, nLeave);
-
-        top.NewKeyDownEvent (Key.Tab);
-
-        Assert.Equal (0, nEnter);
-        Assert.Equal (0, nLeave);
-
-        top.Dispose ();
-        Application.Shutdown ();
-    }
-
+    
     [Fact]
     [Fact]
     public void NewMouseEvent_Invokes_MouseEvent_Properly ()
     public void NewMouseEvent_Invokes_MouseEvent_Properly ()
     {
     {

+ 204 - 15
UnitTests/View/NavigationTests.cs

@@ -2,7 +2,7 @@
 
 
 namespace Terminal.Gui.ViewTests;
 namespace Terminal.Gui.ViewTests;
 
 
-public class NavigationTests (ITestOutputHelper output)
+public class NavigationTests (ITestOutputHelper output) : TestsAllViews
 {
 {
     [Fact]
     [Fact]
     public void BringSubviewForward_Subviews_vs_TabIndexes ()
     public void BringSubviewForward_Subviews_vs_TabIndexes ()
@@ -324,13 +324,13 @@ public class NavigationTests (ITestOutputHelper output)
         Assert.True (view2.CanFocus);
         Assert.True (view2.CanFocus);
         Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus
         Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus
 
 
-        Assert.True (top.NewKeyDownEvent (Key.Tab));
+        Assert.True (Application.OnKeyDown (Key.Tab));
         Assert.True (view1.CanFocus);
         Assert.True (view1.CanFocus);
         Assert.False (view1.HasFocus); // Only one of the most focused toplevels view can have focus
         Assert.False (view1.HasFocus); // Only one of the most focused toplevels view can have focus
         Assert.True (view2.CanFocus);
         Assert.True (view2.CanFocus);
         Assert.True (view2.HasFocus);
         Assert.True (view2.HasFocus);
 
 
-        Assert.True (top.NewKeyDownEvent (Key.Tab));
+        Assert.True (Application.OnKeyDown (Key.Tab));
         Assert.True (view1.CanFocus);
         Assert.True (view1.CanFocus);
         Assert.True (view1.HasFocus);
         Assert.True (view1.HasFocus);
         Assert.True (view2.CanFocus);
         Assert.True (view2.CanFocus);
@@ -365,13 +365,13 @@ public class NavigationTests (ITestOutputHelper output)
         Assert.True (view2.CanFocus);
         Assert.True (view2.CanFocus);
         Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus
         Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus
 
 
-        Assert.True (top.NewKeyDownEvent (Key.Tab.WithCtrl));
+        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl));
         Assert.True (view1.CanFocus);
         Assert.True (view1.CanFocus);
         Assert.False (view1.HasFocus); // Only one of the most focused toplevels view can have focus
         Assert.False (view1.HasFocus); // Only one of the most focused toplevels view can have focus
         Assert.True (view2.CanFocus);
         Assert.True (view2.CanFocus);
         Assert.True (view2.HasFocus);
         Assert.True (view2.HasFocus);
 
 
-        Assert.True (top.NewKeyDownEvent (Key.Tab.WithCtrl));
+        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl));
         Assert.True (view1.CanFocus);
         Assert.True (view1.CanFocus);
         Assert.True (view1.HasFocus);
         Assert.True (view1.HasFocus);
         Assert.True (view2.CanFocus);
         Assert.True (view2.CanFocus);
@@ -417,14 +417,14 @@ public class NavigationTests (ITestOutputHelper output)
         Assert.True (view2.CanFocus);
         Assert.True (view2.CanFocus);
         Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus
         Assert.False (view2.HasFocus); // Only one of the most focused toplevels view can have focus
 
 
-        Assert.True (top.NewKeyDownEvent (Key.Tab.WithCtrl));
-        Assert.True (top.NewKeyDownEvent (Key.Tab.WithCtrl));
+        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl));
+        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl));
         Assert.True (view1.CanFocus);
         Assert.True (view1.CanFocus);
         Assert.False (view1.HasFocus); // Only one of the most focused toplevels view can have focus
         Assert.False (view1.HasFocus); // Only one of the most focused toplevels view can have focus
         Assert.True (view2.CanFocus);
         Assert.True (view2.CanFocus);
         Assert.True (view2.HasFocus);
         Assert.True (view2.HasFocus);
 
 
-        Assert.True (top.NewKeyDownEvent (Key.Tab.WithCtrl));
+        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl));
         Assert.True (view1.CanFocus);
         Assert.True (view1.CanFocus);
         Assert.True (view1.HasFocus);
         Assert.True (view1.HasFocus);
         Assert.True (view2.CanFocus);
         Assert.True (view2.CanFocus);
@@ -530,6 +530,7 @@ public class NavigationTests (ITestOutputHelper output)
     }
     }
 
 
     [Fact]
     [Fact]
+    [AutoInitShutdown]
     public void FocusNearestView_Ensure_Focus_Ordered ()
     public void FocusNearestView_Ensure_Focus_Ordered ()
     {
     {
         var top = new Toplevel ();
         var top = new Toplevel ();
@@ -544,16 +545,17 @@ public class NavigationTests (ITestOutputHelper output)
         frm.Add (frmSubview);
         frm.Add (frmSubview);
         top.Add (frm);
         top.Add (frm);
 
 
-        top.NewKeyDownEvent (Key.Tab);
+        Application.Begin (top);
         Assert.Equal ("WindowSubview", top.MostFocused.Text);
         Assert.Equal ("WindowSubview", top.MostFocused.Text);
-        top.NewKeyDownEvent (Key.Tab);
+
+        Application.OnKeyDown (Key.Tab);
         Assert.Equal ("FrameSubview", top.MostFocused.Text);
         Assert.Equal ("FrameSubview", top.MostFocused.Text);
-        top.NewKeyDownEvent (Key.Tab);
+        Application.OnKeyDown (Key.Tab);
         Assert.Equal ("WindowSubview", top.MostFocused.Text);
         Assert.Equal ("WindowSubview", top.MostFocused.Text);
 
 
-        top.NewKeyDownEvent (Key.Tab.WithShift);
+        Application.OnKeyDown (Key.Tab.WithShift);
         Assert.Equal ("FrameSubview", top.MostFocused.Text);
         Assert.Equal ("FrameSubview", top.MostFocused.Text);
-        top.NewKeyDownEvent (Key.Tab.WithShift);
+        Application.OnKeyDown (Key.Tab.WithShift);
         Assert.Equal ("WindowSubview", top.MostFocused.Text);
         Assert.Equal ("WindowSubview", top.MostFocused.Text);
         top.Dispose ();
         top.Dispose ();
     }
     }
@@ -605,7 +607,7 @@ public class NavigationTests (ITestOutputHelper output)
         Assert.False (removed);
         Assert.False (removed);
         Assert.Null (view3);
         Assert.Null (view3);
 
 
-        Assert.True (top1.NewKeyDownEvent (Key.Tab.WithCtrl));
+        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl));
         Assert.True (top1.HasFocus);
         Assert.True (top1.HasFocus);
         Assert.False (view1.HasFocus);
         Assert.False (view1.HasFocus);
         Assert.True (view2.HasFocus);
         Assert.True (view2.HasFocus);
@@ -613,7 +615,7 @@ public class NavigationTests (ITestOutputHelper output)
         Assert.NotNull (view3);
         Assert.NotNull (view3);
 
 
         Exception exception =
         Exception exception =
-            Record.Exception (() => top1.NewKeyDownEvent (Key.Tab.WithCtrl));
+            Record.Exception (() => Application.OnKeyDown (Key.Tab.WithCtrl));
         Assert.Null (exception);
         Assert.Null (exception);
         Assert.True (removed);
         Assert.True (removed);
         Assert.Null (view3);
         Assert.Null (view3);
@@ -1582,4 +1584,191 @@ public class NavigationTests (ITestOutputHelper output)
         Assert.True (view.HasFocus);
         Assert.True (view.HasFocus);
         Assert.Null (view.MostFocused); // BUGBUG: Should be view
         Assert.Null (view.MostFocused); // BUGBUG: Should be view
     }
     }
+
+
+    [Theory]
+    [MemberData (nameof (AllViewTypes))]
+
+    public void AllViews_Enter_Leave_Events (Type viewType)
+    {
+        var view = CreateInstanceIfNotGeneric (viewType);
+
+        if (view == null)
+        {
+            output.WriteLine ($"Ignoring {viewType} - It's a Generic");
+            return;
+        }
+
+        if (!view.CanFocus)
+        {
+            output.WriteLine ($"Ignoring {viewType} - It can't focus.");
+
+            return;
+        }
+
+        if (view is Toplevel && ((Toplevel)view).Modal)
+        {
+            output.WriteLine ($"Ignoring {viewType} - It's a Modal Toplevel");
+
+            return;
+        }
+
+        Application.Init (new FakeDriver ());
+
+        Toplevel top = new ()
+        {
+            Height = 10,
+            Width = 10
+        };
+
+        View otherView = new ()
+        {
+            X = 0, Y = 0,
+            Height = 1,
+            Width = 1,
+            CanFocus = true,
+        };
+
+        view.X = Pos.Right (otherView);
+        view.Y = 0;
+        view.Width = 10;
+        view.Height = 1;
+
+        var nEnter = 0;
+        var nLeave = 0;
+
+        view.Enter += (s, e) => nEnter++;
+        view.Leave += (s, e) => nLeave++;
+
+        top.Add (view, otherView);
+        Application.Begin (top);
+
+        // Start with the focus on our test view
+        view.SetFocus ();
+
+        Assert.Equal (1, nEnter);
+        Assert.Equal (0, nLeave);
+
+        // Use keyboard to navigate to next view (otherView). 
+        if (view is TextView)
+        {
+            Application.OnKeyDown (Key.Tab.WithCtrl);
+        }
+        else if (view is DatePicker)
+        {
+            for (var i = 0; i < 4; i++)
+            {
+                Application.OnKeyDown (Key.Tab.WithCtrl);
+            }
+        }
+        else
+        {
+            Application.OnKeyDown (Key.Tab);
+        }
+
+        Assert.Equal (1, nEnter);
+        Assert.Equal (1, nLeave);
+
+        Application.OnKeyDown (Key.Tab);
+
+        Assert.Equal (2, nEnter);
+        Assert.Equal (1, nLeave);
+
+        top.Dispose ();
+        Application.Shutdown ();
+    }
+
+
+    [Theory]
+    [MemberData (nameof (AllViewTypes))]
+
+    public void AllViews_Enter_Leave_Events_Visible_False (Type viewType)
+    {
+        var view = CreateInstanceIfNotGeneric (viewType);
+
+        if (view == null)
+        {
+            output.WriteLine ($"Ignoring {viewType} - It's a Generic");
+            return;
+        }
+
+        if (!view.CanFocus)
+        {
+            output.WriteLine ($"Ignoring {viewType} - It can't focus.");
+
+            return;
+        }
+
+        if (view is Toplevel && ((Toplevel)view).Modal)
+        {
+            output.WriteLine ($"Ignoring {viewType} - It's a Modal Toplevel");
+
+            return;
+        }
+
+        Application.Init (new FakeDriver ());
+
+        Toplevel top = new ()
+        {
+            Height = 10,
+            Width = 10
+        };
+
+        View otherView = new ()
+        {
+            X = 0, Y = 0,
+            Height = 1,
+            Width = 1,
+            CanFocus = true,
+        };
+
+        view.Visible = false;
+        view.X = Pos.Right (otherView);
+        view.Y = 0;
+        view.Width = 10;
+        view.Height = 1;
+
+        var nEnter = 0;
+        var nLeave = 0;
+
+        view.Enter += (s, e) => nEnter++;
+        view.Leave += (s, e) => nLeave++;
+
+        top.Add (view, otherView);
+        Application.Begin (top);
+
+        // Start with the focus on our test view
+        view.SetFocus ();
+
+        Assert.Equal (0, nEnter);
+        Assert.Equal (0, nLeave);
+
+        // Use keyboard to navigate to next view (otherView). 
+        if (view is TextView)
+        {
+            Application.OnKeyDown (Key.Tab.WithCtrl);
+        }
+        else if (view is DatePicker)
+        {
+            for (var i = 0; i < 4; i++)
+            {
+                Application.OnKeyDown (Key.Tab.WithCtrl);
+            }
+        }
+        else
+        {
+            Application.OnKeyDown (Key.Tab);
+        }
+
+        Assert.Equal (0, nEnter);
+        Assert.Equal (0, nLeave);
+
+        top.NewKeyDownEvent (Key.Tab);
+
+        Assert.Equal (0, nEnter);
+        Assert.Equal (0, nLeave);
+
+        top.Dispose ();
+        Application.Shutdown ();
+    }
 }
 }

+ 3 - 2
UnitTests/View/ViewKeyBindingTests.cs

@@ -19,7 +19,8 @@ public class ViewKeyBindingTests (ITestOutputHelper output)
         Application.Begin (top);
         Application.Begin (top);
 
 
         Application.OnKeyDown (Key.A);
         Application.OnKeyDown (Key.A);
-        Assert.True (invoked);
+        Assert.False (invoked);
+        Assert.True (view.ApplicationCommand);
 
 
         invoked = false;
         invoked = false;
         Application.OnKeyDown (Key.H);
         Application.OnKeyDown (Key.H);
@@ -134,7 +135,7 @@ public class ViewKeyBindingTests (ITestOutputHelper output)
             AddCommand (Command.HotKey, () => HotKeyCommand = true);
             AddCommand (Command.HotKey, () => HotKeyCommand = true);
             AddCommand (Command.Left, () => FocusedCommand = true);
             AddCommand (Command.Left, () => FocusedCommand = true);
 
 
-            KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Save);
+            Application.KeyBindings.Add (Key.A, this, Command.Save);
             HotKey = KeyCode.H;
             HotKey = KeyCode.H;
             KeyBindings.Add (Key.F, KeyBindingScope.Focused, Command.Left);
             KeyBindings.Add (Key.F, KeyBindingScope.Focused, Command.Left);
         }
         }

+ 4 - 4
UnitTests/Views/AllViewsTests.cs

@@ -108,21 +108,21 @@ public class AllViewsTests (ITestOutputHelper output) : TestsAllViews
 
 
         if (vType is TextView)
         if (vType is TextView)
         {
         {
-            top.NewKeyDownEvent (Key.Tab.WithCtrl);
+            Application.OnKeyDown (Key.Tab.WithCtrl);
         }
         }
         else if (vType is DatePicker)
         else if (vType is DatePicker)
         {
         {
             for (var i = 0; i < 4; i++)
             for (var i = 0; i < 4; i++)
             {
             {
-                top.NewKeyDownEvent (Key.Tab.WithCtrl);
+                Application.OnKeyDown (Key.Tab.WithCtrl);
             }
             }
         }
         }
         else
         else
         {
         {
-            top.NewKeyDownEvent (Key.Tab);
+            Application.OnKeyDown (Key.Tab);
         }
         }
 
 
-        top.NewKeyDownEvent (Key.Tab);
+        Application.OnKeyDown (Key.Tab);
 
 
         Assert.Equal (2, vTypeEnter);
         Assert.Equal (2, vTypeEnter);
         Assert.Equal (1, vTypeLeave);
         Assert.Equal (1, vTypeLeave);

+ 37 - 41
UnitTests/Views/OverlappedTests.cs

@@ -1030,28 +1030,30 @@ public class OverlappedTests
 
 
         var win1 = new Window { Id = "win1", Width = Dim.Percent (50), Height = Dim.Fill () };
         var win1 = new Window { Id = "win1", Width = Dim.Percent (50), Height = Dim.Fill () };
         var lblTf1W1 = new Label { Text = "Enter text in TextField on Win1:" };
         var lblTf1W1 = new Label { Text = "Enter text in TextField on Win1:" };
-        var tf1W1 = new TextField { X = Pos.Right (lblTf1W1) + 1, Width = Dim.Fill (), Text = "Text1 on Win1" };
+        var tf1W1 = new TextField { Id="tf1W1", X = Pos.Right (lblTf1W1) + 1, Width = Dim.Fill (), Text = "Text1 on Win1" };
         var lblTvW1 = new Label { Y = Pos.Bottom (lblTf1W1) + 1, Text = "Enter text in TextView on Win1:" };
         var lblTvW1 = new Label { Y = Pos.Bottom (lblTf1W1) + 1, Text = "Enter text in TextView on Win1:" };
 
 
         var tvW1 = new TextView
         var tvW1 = new TextView
         {
         {
+            Id = "tvW1",
             X = Pos.Left (tf1W1), Width = Dim.Fill (), Height = 2, Text = "First line Win1\nSecond line Win1"
             X = Pos.Left (tf1W1), Width = Dim.Fill (), Height = 2, Text = "First line Win1\nSecond line Win1"
         };
         };
         var lblTf2W1 = new Label { Y = Pos.Bottom (lblTvW1) + 1, Text = "Enter text in TextField on Win1:" };
         var lblTf2W1 = new Label { Y = Pos.Bottom (lblTvW1) + 1, Text = "Enter text in TextField on Win1:" };
-        var tf2W1 = new TextField { X = Pos.Left (tf1W1), Width = Dim.Fill (), Text = "Text2 on Win1" };
+        var tf2W1 = new TextField { Id = "tf2W1", X = Pos.Left (tf1W1), Width = Dim.Fill (), Text = "Text2 on Win1" };
         win1.Add (lblTf1W1, tf1W1, lblTvW1, tvW1, lblTf2W1, tf2W1);
         win1.Add (lblTf1W1, tf1W1, lblTvW1, tvW1, lblTf2W1, tf2W1);
 
 
         var win2 = new Window { Id = "win2", Width = Dim.Percent (50), Height = Dim.Fill () };
         var win2 = new Window { Id = "win2", Width = Dim.Percent (50), Height = Dim.Fill () };
         var lblTf1W2 = new Label { Text = "Enter text in TextField on Win2:" };
         var lblTf1W2 = new Label { Text = "Enter text in TextField on Win2:" };
-        var tf1W2 = new TextField { X = Pos.Right (lblTf1W2) + 1, Width = Dim.Fill (), Text = "Text1 on Win2" };
+        var tf1W2 = new TextField { Id = "tf1W2", X = Pos.Right (lblTf1W2) + 1, Width = Dim.Fill (), Text = "Text1 on Win2" };
         var lblTvW2 = new Label { Y = Pos.Bottom (lblTf1W2) + 1, Text = "Enter text in TextView on Win2:" };
         var lblTvW2 = new Label { Y = Pos.Bottom (lblTf1W2) + 1, Text = "Enter text in TextView on Win2:" };
 
 
         var tvW2 = new TextView
         var tvW2 = new TextView
         {
         {
+            Id = "tvW2",
             X = Pos.Left (tf1W2), Width = Dim.Fill (), Height = 2, Text = "First line Win1\nSecond line Win2"
             X = Pos.Left (tf1W2), Width = Dim.Fill (), Height = 2, Text = "First line Win1\nSecond line Win2"
         };
         };
         var lblTf2W2 = new Label { Y = Pos.Bottom (lblTvW2) + 1, Text = "Enter text in TextField on Win2:" };
         var lblTf2W2 = new Label { Y = Pos.Bottom (lblTvW2) + 1, Text = "Enter text in TextField on Win2:" };
-        var tf2W2 = new TextField { X = Pos.Left (tf1W2), Width = Dim.Fill (), Text = "Text2 on Win2" };
+        var tf2W2 = new TextField { Id = "tf2W2", X = Pos.Left (tf1W2), Width = Dim.Fill (), Text = "Text2 on Win2" };
         win2.Add (lblTf1W2, tf1W2, lblTvW2, tvW2, lblTf2W2, tf2W2);
         win2.Add (lblTf1W2, tf1W2, lblTvW2, tvW2, lblTf2W2, tf2W2);
 
 
         win1.Closing += (s, e) => isRunning = false;
         win1.Closing += (s, e) => isRunning = false;
@@ -1104,72 +1106,69 @@ public class OverlappedTests
         Assert.True (Application.OnKeyDown (Key.Tab));
         Assert.True (Application.OnKeyDown (Key.Tab));
         Assert.Equal ($"\tFirst line Win1{Environment.NewLine}Second line Win1", tvW1.Text);
         Assert.Equal ($"\tFirst line Win1{Environment.NewLine}Second line Win1", tvW1.Text);
 
 
-        Assert.True (
-                     Application.OnKeyDown (Key.Tab.WithShift)
-                    );
+        Assert.True (Application.OnKeyDown (Key.Tab.WithShift));
         Assert.Equal ($"First line Win1{Environment.NewLine}Second line Win1", tvW1.Text);
         Assert.Equal ($"First line Win1{Environment.NewLine}Second line Win1", tvW1.Text);
 
 
-        Assert.True (
-                     Application.OnKeyDown (Key.Tab.WithCtrl)
-                    );
+        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl));    // move to win2
+        Assert.Equal (win2, Application.OverlappedChildren [0]);
+
+        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl.WithShift));    // move back to win1
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (win1, Application.OverlappedChildren [0]);
-        Assert.Equal (tf2W1, win1.MostFocused);
-        Assert.True (Application.OnKeyDown (Key.Tab));
+
+        Assert.Equal (tvW1, win1.MostFocused);
+        Assert.True (Application.OnKeyDown (Key.Tab));    // text view eats tab
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (win1, Application.OverlappedChildren [0]);
-        Assert.Equal (tf1W1, win1.MostFocused);
+        Assert.Equal (tvW1, win1.MostFocused);
+
+        tvW1.AllowsTab = false;
+        Assert.True (Application.OnKeyDown (Key.Tab));    // text view eats tab
+        Assert.Equal (win1, Application.OverlappedChildren [0]);
+        Assert.Equal (tf2W1, win1.MostFocused);
+
         Assert.True (Application.OnKeyDown (Key.CursorRight));
         Assert.True (Application.OnKeyDown (Key.CursorRight));
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (win1, Application.OverlappedChildren [0]);
-        Assert.Equal (tf1W1, win1.MostFocused);
+        Assert.Equal (tf2W1, win1.MostFocused);
         Assert.True (Application.OnKeyDown (Key.CursorDown));
         Assert.True (Application.OnKeyDown (Key.CursorDown));
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (win1, Application.OverlappedChildren [0]);
-        Assert.Equal (tvW1, win1.MostFocused);
+        Assert.Equal (tf1W1, win1.MostFocused);
 #if UNIX_KEY_BINDINGS
 #if UNIX_KEY_BINDINGS
         Assert.True (Application.OverlappedChildren [0].ProcessKeyDown (new (Key.I.WithCtrl)));
         Assert.True (Application.OverlappedChildren [0].ProcessKeyDown (new (Key.I.WithCtrl)));
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (tf2W1, win1.MostFocused);
         Assert.Equal (tf2W1, win1.MostFocused);
 #endif
 #endif
-        Assert.True (
-                     Application.OverlappedChildren [0]
-                                .NewKeyDownEvent (Key.Tab.WithShift)
-                    );
+        Assert.True (Application.OnKeyDown (Key.Tab));
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (tvW1, win1.MostFocused);
         Assert.Equal (tvW1, win1.MostFocused);
-        Assert.True (Application.OnKeyDown (Key.CursorLeft));
+        Assert.True (Application.OnKeyDown (Key.CursorLeft));  // The view to the left of tvW1 is tf2W1, but tvW1 is still focused and eats cursor keys
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (win1, Application.OverlappedChildren [0]);
-        Assert.Equal (tf1W1, win1.MostFocused);
+        Assert.Equal (tvW1, win1.MostFocused);
         Assert.True (Application.OnKeyDown (Key.CursorUp));
         Assert.True (Application.OnKeyDown (Key.CursorUp));
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (win1, Application.OverlappedChildren [0]);
-        Assert.Equal (tf2W1, win1.MostFocused);
+        Assert.Equal (tvW1, win1.MostFocused);
         Assert.True (Application.OnKeyDown (Key.Tab));
         Assert.True (Application.OnKeyDown (Key.Tab));
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (win1, Application.OverlappedChildren [0]);
-        Assert.Equal (tf1W1, win1.MostFocused);
+        Assert.Equal (tf2W1, win1.MostFocused);
 
 
-        Assert.True (
-                     Application.OverlappedChildren [0]
-                                .NewKeyDownEvent (Key.Tab.WithCtrl)
-                    );
+        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl)); // Move to win2
         Assert.Equal (win2, Application.OverlappedChildren [0]);
         Assert.Equal (win2, Application.OverlappedChildren [0]);
         Assert.Equal (tf1W2, win2.MostFocused);
         Assert.Equal (tf1W2, win2.MostFocused);
         tf2W2.SetFocus ();
         tf2W2.SetFocus ();
         Assert.True (tf2W2.HasFocus);
         Assert.True (tf2W2.HasFocus);
 
 
-        Assert.True (
-                     Application.OverlappedChildren [0]
-                                .NewKeyDownEvent (Key.Tab.WithCtrl.WithShift)
-                    );
+        Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl.WithShift));
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (win1, Application.OverlappedChildren [0]);
-        Assert.Equal (tf1W1, win1.MostFocused);
+        Assert.Equal (tf2W1, win1.MostFocused);
         Assert.True (Application.OnKeyDown (Application.AlternateForwardKey));
         Assert.True (Application.OnKeyDown (Application.AlternateForwardKey));
         Assert.Equal (win2, Application.OverlappedChildren [0]);
         Assert.Equal (win2, Application.OverlappedChildren [0]);
         Assert.Equal (tf2W2, win2.MostFocused);
         Assert.Equal (tf2W2, win2.MostFocused);
         Assert.True (Application.OnKeyDown (Application.AlternateBackwardKey));
         Assert.True (Application.OnKeyDown (Application.AlternateBackwardKey));
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (win1, Application.OverlappedChildren [0]);
-        Assert.Equal (tf1W1, win1.MostFocused);
+        Assert.Equal (tf2W1, win1.MostFocused);
         Assert.True (Application.OnKeyDown (Key.CursorDown));
         Assert.True (Application.OnKeyDown (Key.CursorDown));
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (win1, Application.OverlappedChildren [0]);
-        Assert.Equal (tvW1, win1.MostFocused);
+        Assert.Equal (tf1W1, win1.MostFocused);
 #if UNIX_KEY_BINDINGS
 #if UNIX_KEY_BINDINGS
-        Assert.True (Application.OverlappedChildren [0].ProcessKeyDown (new (Key.B.WithCtrl)));
+        Assert.True (Application.OnKeyDown (new (Key.B.WithCtrl)));
 #else
 #else
         Assert.True (Application.OnKeyDown (Key.CursorLeft));
         Assert.True (Application.OnKeyDown (Key.CursorLeft));
 #endif
 #endif
@@ -1180,20 +1179,17 @@ public class OverlappedTests
         Assert.Equal (tvW1, win1.MostFocused);
         Assert.Equal (tvW1, win1.MostFocused);
         Assert.Equal (Point.Empty, tvW1.CursorPosition);
         Assert.Equal (Point.Empty, tvW1.CursorPosition);
 
 
-        Assert.True (
-                     Application.OverlappedChildren [0]
-                                .NewKeyDownEvent (Key.End.WithCtrl)
-                    );
+        Assert.True (Application.OnKeyDown (Key.End.WithCtrl));
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (tvW1, win1.MostFocused);
         Assert.Equal (tvW1, win1.MostFocused);
         Assert.Equal (new (16, 1), tvW1.CursorPosition);
         Assert.Equal (new (16, 1), tvW1.CursorPosition);
 #if UNIX_KEY_BINDINGS
 #if UNIX_KEY_BINDINGS
-        Assert.True (Application.OverlappedChildren [0].ProcessKeyDown (new (Key.F.WithCtrl)));
+        Assert.True (Application.OnKeyDown (new (Key.F.WithCtrl)));
 #else
 #else
         Assert.True (Application.OnKeyDown (Key.CursorRight));
         Assert.True (Application.OnKeyDown (Key.CursorRight));
 #endif
 #endif
         Assert.Equal (win1, Application.OverlappedChildren [0]);
         Assert.Equal (win1, Application.OverlappedChildren [0]);
-        Assert.Equal (tf2W1, win1.MostFocused);
+        Assert.Equal (tvW1, win1.MostFocused);
 
 
 #if UNIX_KEY_BINDINGS
 #if UNIX_KEY_BINDINGS
         Assert.True (Application.OverlappedChildren [0].ProcessKeyDown (new (Key.L.WithCtrl)));
         Assert.True (Application.OverlappedChildren [0].ProcessKeyDown (new (Key.L.WithCtrl)));

+ 4 - 4
UnitTests/Views/TableViewTests.cs

@@ -2993,7 +2993,7 @@ A B C
         dt.Rows.Add (1, 2, 3, 4, 5, 6);
         dt.Rows.Add (1, 2, 3, 4, 5, 6);
 
 
         tableView.MultiSelect = true;
         tableView.MultiSelect = true;
-        tableView.KeyBindings.Add (Key.Space, Command.Select);
+        tableView.KeyBindings.ReplaceCommands (Key.Space, Command.Select);
 
 
         Point selectedCell = tableView.GetAllSelectedCells ().Single ();
         Point selectedCell = tableView.GetAllSelectedCells ().Single ();
         Assert.Equal (0, selectedCell.X);
         Assert.Equal (0, selectedCell.X);
@@ -3065,7 +3065,7 @@ A B C
         dt.Rows.Add (1, 2, 3, 4, 5, 6);
         dt.Rows.Add (1, 2, 3, 4, 5, 6);
         tableView.FullRowSelect = true;
         tableView.FullRowSelect = true;
         tableView.MultiSelect = true;
         tableView.MultiSelect = true;
-        tableView.KeyBindings.Add (Key.Space, Command.Select);
+        tableView.KeyBindings.ReplaceCommands (Key.Space, Command.Select);
 
 
         // Toggle Select Cell 0,0
         // Toggle Select Cell 0,0
         tableView.NewKeyDownEvent (new() { KeyCode = KeyCode.Space });
         tableView.NewKeyDownEvent (new() { KeyCode = KeyCode.Space });
@@ -3101,7 +3101,7 @@ A B C
         dt.Rows.Add (1, 2, 3, 4, 5, 6);
         dt.Rows.Add (1, 2, 3, 4, 5, 6);
         dt.Rows.Add (1, 2, 3, 4, 5, 6);
         dt.Rows.Add (1, 2, 3, 4, 5, 6);
         tableView.MultiSelect = true;
         tableView.MultiSelect = true;
-        tableView.KeyBindings.Add (Key.Space, Command.Select);
+        tableView.KeyBindings.ReplaceCommands (Key.Space, Command.Select);
 
 
         // Make a square selection
         // Make a square selection
         tableView.NewKeyDownEvent (new() { KeyCode = KeyCode.ShiftMask | KeyCode.CursorDown });
         tableView.NewKeyDownEvent (new() { KeyCode = KeyCode.ShiftMask | KeyCode.CursorDown });
@@ -3142,7 +3142,7 @@ A B C
         dt.Rows.Add (1, 2, 3, 4, 5, 6);
         dt.Rows.Add (1, 2, 3, 4, 5, 6);
         dt.Rows.Add (1, 2, 3, 4, 5, 6);
         dt.Rows.Add (1, 2, 3, 4, 5, 6);
         tableView.MultiSelect = true;
         tableView.MultiSelect = true;
-        tableView.KeyBindings.Add (Key.Space, Command.Select);
+        tableView.KeyBindings.ReplaceCommands (Key.Space, Command.Select);
 
 
         // Make first square selection (0,0 to 1,1)
         // Make first square selection (0,0 to 1,1)
         tableView.NewKeyDownEvent (new() { KeyCode = KeyCode.ShiftMask | KeyCode.CursorDown });
         tableView.NewKeyDownEvent (new() { KeyCode = KeyCode.ShiftMask | KeyCode.CursorDown });

+ 18 - 14
UnitTests/Views/ToplevelTests.cs

@@ -485,27 +485,31 @@ public partial class ToplevelTests (ITestOutputHelper output)
         Assert.Equal ($"First line Win1{Environment.NewLine}Second line Win1", tvW1.Text);
         Assert.Equal ($"First line Win1{Environment.NewLine}Second line Win1", tvW1.Text);
         Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl));
         Assert.True (Application.OnKeyDown (Key.Tab.WithCtrl));
         Assert.Equal (win1, top.Focused);
         Assert.Equal (win1, top.Focused);
-        Assert.Equal (tf2W1, top.MostFocused);
+        Assert.Equal (tf2W1, top.MostFocused); // tf2W1 is last subview in win1 - tabbing should take us to first subview of win2
         Assert.True (Application.OnKeyDown (Key.Tab));
         Assert.True (Application.OnKeyDown (Key.Tab));
-        Assert.Equal (win1, top.Focused);
-        Assert.Equal (tf1W1, top.MostFocused);
-        Assert.True (Application.OnKeyDown (Key.CursorRight));
-        Assert.Equal (win1, top.Focused);
-        Assert.Equal (tf1W1, top.MostFocused);
-        Assert.True (Application.OnKeyDown (Key.CursorDown));
-        Assert.Equal (win1, top.Focused);
-        Assert.Equal (tvW1, top.MostFocused);
+        Assert.Equal (win2, top.Focused);
+        Assert.Equal (tf1W2, top.MostFocused);
+        Assert.True (Application.OnKeyDown (Key.CursorRight)); // move char to right in tf1W2
+        Assert.Equal (win2, top.Focused);
+        Assert.Equal (tf1W2, top.MostFocused);   
+        Assert.True (Application.OnKeyDown (Key.CursorDown)); // move down to next view (tvW2)
+        Assert.Equal (win2, top.Focused);
+        Assert.Equal (tvW2, top.MostFocused);
 #if UNIX_KEY_BINDINGS
 #if UNIX_KEY_BINDINGS
         Assert.True (Application.OnKeyDown (new (Key.I.WithCtrl)));
         Assert.True (Application.OnKeyDown (new (Key.I.WithCtrl)));
         Assert.Equal (win1, top.Focused);
         Assert.Equal (win1, top.Focused);
         Assert.Equal (tf2W1, top.MostFocused);
         Assert.Equal (tf2W1, top.MostFocused);
 #endif
 #endif
-        Assert.True (Application.OnKeyDown (Key.Tab.WithShift));
-        Assert.Equal (win1, top.Focused);
-        Assert.Equal (tvW1, top.MostFocused);
+        Assert.True (Application.OnKeyDown (Key.Tab.WithShift)); // Ignored. TextView eats shift-tab by default
+        Assert.Equal (win2, top.Focused);
+        Assert.Equal (tvW2, top.MostFocused);
+        tvW2.AllowsTab = false;
+        Assert.True (Application.OnKeyDown (Key.Tab.WithShift)); 
+        Assert.Equal (win2, top.Focused);
+        Assert.Equal (tf1W2, top.MostFocused);
         Assert.True (Application.OnKeyDown (Key.CursorLeft));
         Assert.True (Application.OnKeyDown (Key.CursorLeft));
-        Assert.Equal (win1, top.Focused);
-        Assert.Equal (tf1W1, top.MostFocused);
+        Assert.Equal (win2, top.Focused);
+        Assert.Equal (tf1W2, top.MostFocused);
         Assert.True (Application.OnKeyDown (Key.CursorUp));
         Assert.True (Application.OnKeyDown (Key.CursorUp));
         Assert.Equal (win1, top.Focused);
         Assert.Equal (win1, top.Focused);
         Assert.Equal (tf2W1, top.MostFocused);
         Assert.Equal (tf2W1, top.MostFocused);

+ 1 - 1
UnitTests/Views/TreeTableSourceTests.cs

@@ -187,7 +187,7 @@ public class TreeTableSourceTests : IDisposable
         Assert.Equal (0, tv.SelectedRow);
         Assert.Equal (0, tv.SelectedRow);
         Assert.Equal (1, tv.SelectedColumn);
         Assert.Equal (1, tv.SelectedColumn);
 
 
-        top.NewKeyDownEvent (Key.CursorRight);
+        Application.OnKeyDown (Key.CursorRight);
 
 
         tv.Draw ();
         tv.Draw ();