diff --git a/Terminal.Gui/Application.cs b/Terminal.Gui/Application.cs
index 1501e5c14..8e414969c 100644
--- a/Terminal.Gui/Application.cs
+++ b/Terminal.Gui/Application.cs
@@ -157,6 +157,7 @@ public static partial class Application
KeyDown = null;
KeyUp = null;
SizeChanging = null;
+ ClearKeyBindings ();
Colors.Reset ();
@@ -1948,6 +1949,32 @@ public static partial class Application
_keyBindings [key].Add (view);
}
+ ///
+ /// Gets the list of Views that have key bindings.
+ ///
+ ///
+ /// This is an internal method used by the class to add Application key bindings.
+ ///
+ /// The list of Views that have Application-scoped key bindings.
+ internal static List GetViewsWithKeyBindings ()
+ {
+ return _keyBindings.Values.SelectMany (v => v).ToList ();
+ }
+
+ ///
+ /// Gets the list of Views that have key bindings for the specified key.
+ ///
+ ///
+ /// This is an internal method used by the class to add Application key bindings.
+ ///
+ /// The key to check.
+ /// Outputs the list of views bound to
+ /// if successful.
+ internal static bool TryGetKeyBindings (Key key, out List views)
+ {
+ return _keyBindings.TryGetValue (key, out views);
+ }
+
///
/// Removes an scoped key binding.
///
@@ -1958,9 +1985,14 @@ public static partial class Application
/// The view that is bound to the key.
internal static void RemoveKeyBinding (Key key, View view)
{
- if (_keyBindings.TryGetValue (key, out List binding))
+ if (_keyBindings.TryGetValue (key, out List views))
{
- binding.Remove (view);
+ views.Remove (view);
+
+ if (views.Count == 0)
+ {
+ _keyBindings.Remove (key);
+ }
}
}
@@ -1971,7 +2003,7 @@ public static partial class Application
/// This is an internal method used by the class to remove Application key bindings.
///
/// The view that is bound to the key.
- internal static void RemoveAllKeyBindings (View view)
+ internal static void ClearKeyBindings (View view)
{
foreach (Key key in _keyBindings.Keys)
{
@@ -1979,5 +2011,17 @@ public static partial class Application
}
}
+ ///
+ /// Removes all scoped key bindings for the specified view.
+ ///
+ ///
+ /// This is an internal method used by the class to remove Application key bindings.
+ ///
+ /// The view that is bound to the key.
+ internal static void ClearKeyBindings ()
+ {
+ _keyBindings.Clear ();
+ }
+
#endregion Keyboard handling
}
diff --git a/Terminal.Gui/Input/KeyBindings.cs b/Terminal.Gui/Input/KeyBindings.cs
index 5285e3c55..bb487773f 100644
--- a/Terminal.Gui/Input/KeyBindings.cs
+++ b/Terminal.Gui/Input/KeyBindings.cs
@@ -81,10 +81,6 @@ public class KeyBindings
else
{
Add (key, new KeyBinding (commands, scope));
- if (scope.FastHasFlags (KeyBindingScope.Application))
- {
- Application.AddKeyBinding (key, BoundView);
- }
}
}
@@ -120,7 +116,7 @@ public class KeyBindings
/// Removes all objects from the collection.
public void Clear ()
{
- Application.RemoveAllKeyBindings (BoundView);
+ Application.ClearKeyBindings (BoundView);
Bindings.Clear ();
}
diff --git a/UnitTests/Application/ApplicationTests.cs b/UnitTests/Application/ApplicationTests.cs
index 1910df0be..498842437 100644
--- a/UnitTests/Application/ApplicationTests.cs
+++ b/UnitTests/Application/ApplicationTests.cs
@@ -193,6 +193,9 @@ public class ApplicationTests
Assert.Empty (Application._topLevels);
Assert.Null (Application._mouseEnteredView);
+ // Keyboard
+ Assert.Empty (Application.GetViewsWithKeyBindings ());
+
// Events - Can't check
//Assert.Null (Application.NotifyNewRunState);
//Assert.Null (Application.NotifyNewRunState);
@@ -225,6 +228,7 @@ public class ApplicationTests
Application.AlternateBackwardKey = Key.A;
Application.AlternateForwardKey = Key.B;
Application.QuitKey = Key.C;
+ Application.AddKeyBinding(Key.A, new View ());
//Application.OverlappedChildren = new List ();
//Application.OverlappedTop =
diff --git a/UnitTests/Application/KeyboardTests.cs b/UnitTests/Application/KeyboardTests.cs
index 8e4b0167e..a293de45a 100644
--- a/UnitTests/Application/KeyboardTests.cs
+++ b/UnitTests/Application/KeyboardTests.cs
@@ -2,6 +2,9 @@
namespace Terminal.Gui.ApplicationTests;
+///
+/// Application tests for keyboard support.
+///
public class KeyboardTests
{
private readonly ITestOutputHelper _output;
@@ -15,6 +18,46 @@ public class KeyboardTests
#endif
}
+
+ [Fact]
+ [AutoInitShutdown]
+ public void QuitKey_Getter_Setter ()
+ {
+ Toplevel top = new ();
+ var isQuiting = false;
+
+ top.Closing += (s, e) =>
+ {
+ isQuiting = true;
+ e.Cancel = true;
+ };
+
+ Application.Begin (top);
+ top.Running = true;
+
+ Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode);
+ Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
+ Assert.True (isQuiting);
+
+ isQuiting = false;
+ Application.OnKeyDown (Key.Q.WithCtrl);
+ Assert.True (isQuiting);
+
+ isQuiting = false;
+ Application.QuitKey = Key.C.WithCtrl;
+ Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
+ Assert.False (isQuiting);
+ Application.OnKeyDown (Key.Q.WithCtrl);
+ Assert.False (isQuiting);
+
+ Application.OnKeyDown (Application.QuitKey);
+ Assert.True (isQuiting);
+
+ // Reset the QuitKey to avoid throws errors on another tests
+ Application.QuitKey = Key.Q.WithCtrl;
+ top.Dispose ();
+ }
+
[Fact]
public void AlternateForwardKey_AlternateBackwardKey_Tests ()
{
@@ -320,7 +363,7 @@ public class KeyboardTests
[Fact]
[AutoInitShutdown]
- public void OnKeyDown_Application_KeyBinding ()
+ public void KeyBinding_OnKeyDown ()
{
var view = new ScopedKeyBindingView ();
var invoked = false;
@@ -365,7 +408,7 @@ public class KeyboardTests
[Fact]
[AutoInitShutdown]
- public void OnKeyDown_Application_KeyBinding_Negative ()
+ public void KeyBinding_OnKeyDown_Negative ()
{
var view = new ScopedKeyBindingView ();
var invoked = false;
@@ -391,46 +434,73 @@ public class KeyboardTests
top.Dispose ();
}
+
[Fact]
[AutoInitShutdown]
- public void QuitKey_Getter_Setter ()
+ public void KeyBinding_AddKeyBinding_Adds ()
{
- Toplevel top = new ();
- var isQuiting = false;
+ View view1 = new ();
+ Application.AddKeyBinding (Key.A, view1);
- top.Closing += (s, e) =>
- {
- isQuiting = true;
- e.Cancel = true;
- };
+ View view2 = new ();
+ Application.AddKeyBinding (Key.A, view2);
- Application.Begin (top);
- top.Running = true;
+ Assert.True (Application.TryGetKeyBindings (Key.A, out List views));
+ Assert.Contains (view1, views);
+ Assert.Contains (view2, views);
- Assert.Equal (KeyCode.Q | KeyCode.CtrlMask, Application.QuitKey.KeyCode);
- Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
- Assert.True (isQuiting);
-
- isQuiting = false;
- Application.OnKeyDown (Key.Q.WithCtrl);
- Assert.True (isQuiting);
-
- isQuiting = false;
- Application.QuitKey = Key.C.WithCtrl;
- Application.Driver.SendKeys ('Q', ConsoleKey.Q, false, false, true);
- Assert.False (isQuiting);
- Application.OnKeyDown (Key.Q.WithCtrl);
- Assert.False (isQuiting);
-
- Application.OnKeyDown (Application.QuitKey);
- Assert.True (isQuiting);
-
- // Reset the QuitKey to avoid throws errors on another tests
- Application.QuitKey = Key.Q.WithCtrl;
- top.Dispose ();
+ Assert.False (Application.TryGetKeyBindings (Key.B, out List _));
}
- // test Application key Bindings
+ [Fact]
+ [AutoInitShutdown]
+ public void KeyBinding_ViewKeyBindings_Add_Adds ()
+ {
+ 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 ());
+
+ 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 views));
+ Assert.Contains (view1, views);
+ Assert.Contains (view2, views);
+
+ Assert.False (Application.TryGetKeyBindings (Key.B, out List _));
+ }
+
+ [Fact]
+ [AutoInitShutdown]
+ public void KeyBinding_RemoveKeyBinding_Removes ()
+ {
+ View view1 = new ();
+ Application.AddKeyBinding (Key.A, view1);
+
+ Assert.True (Application.TryGetKeyBindings (Key.A, out List views));
+ Assert.Contains (view1, views);
+
+ Application.RemoveKeyBinding (Key.A, view1);
+ Assert.False (Application.TryGetKeyBindings (Key.A, out List _));
+ }
+
+ [Fact]
+ [AutoInitShutdown]
+ public void KeyBinding_ViewKeyBindings_RemoveKeyBinding_Removes ()
+ {
+ View view1 = new ();
+ view1.KeyBindings.Add (Key.A, KeyBindingScope.Application, Command.Save);
+
+ Assert.True (Application.TryGetKeyBindings (Key.A, out List views));
+ Assert.Contains (view1, views);
+
+ view1.KeyBindings.Remove (Key.A);
+ Assert.False (Application.TryGetKeyBindings (Key.A, out List _));
+ }
+
+ // Test View for testing Application key Bindings
public class ScopedKeyBindingView : View
{
public ScopedKeyBindingView ()