diff --git a/Terminal.Gui/Input/KeyBindings.cs b/Terminal.Gui/Input/KeyBindings.cs index cb0b079c2..a55169606 100644 --- a/Terminal.Gui/Input/KeyBindings.cs +++ b/Terminal.Gui/Input/KeyBindings.cs @@ -1,5 +1,7 @@ #nullable enable +using System.Diagnostics; + namespace Terminal.Gui; /// diff --git a/Terminal.Gui/View/View.cs b/Terminal.Gui/View/View.cs index 441b0a3ac..654a4d915 100644 --- a/Terminal.Gui/View/View.cs +++ b/Terminal.Gui/View/View.cs @@ -230,6 +230,7 @@ public partial class View : Responder, ISupportInitializeNotification } Initialized?.Invoke (this, EventArgs.Empty); + } #endregion Constructors and Initialization diff --git a/Terminal.Gui/View/ViewKeyboard.cs b/Terminal.Gui/View/ViewKeyboard.cs index 84ca4841c..d1f04c654 100644 --- a/Terminal.Gui/View/ViewKeyboard.cs +++ b/Terminal.Gui/View/ViewKeyboard.cs @@ -1,4 +1,5 @@ using System.ComponentModel; +using System.Diagnostics; namespace Terminal.Gui; @@ -702,7 +703,7 @@ public partial class View return false; } - private bool ProcessSubViewKeyBindings (Key keyEvent, KeyBindingScope scope, ref bool? handled) + private bool ProcessSubViewKeyBindings (Key keyEvent, KeyBindingScope scope, ref bool? handled, bool invoke = true) { // Now, process any key bindings in the subviews that are tagged to KeyBindingScope.HotKey. foreach (View subview in Subviews) @@ -713,6 +714,12 @@ public partial class View { continue; } + + if (!invoke) + { + return true; + } + handled = subview.OnInvokingKeyBindings (keyEvent, scope); if (handled is { } && (bool)handled) @@ -721,7 +728,7 @@ public partial class View } } - bool recurse = subview.ProcessSubViewKeyBindings (keyEvent, scope, ref handled); + bool recurse = subview.ProcessSubViewKeyBindings (keyEvent, scope, ref handled, invoke); if (recurse || (handled is { } && (bool)handled)) { return true; @@ -731,6 +738,36 @@ public partial class View return false; } + // TODO: This is a "prototype" debug check. It may be too annyoing vs. useful. + // TODO: A better approach would be have Application hold a list of bound Hotkeys, similar to + // TODO: how Application holds a list of Application Scoped key bindings and then check that list. + /// + /// Returns true if Key is bound in this view heirarchy. For debugging + /// + /// + /// + public bool IsHotKeyKeyBound (Key key, out View boundView) + { + // recurse through the subviews to find the views that has the key bound + boundView = null; + + foreach (View subview in Subviews) + { + if (subview.KeyBindings.TryGet (key, KeyBindingScope.HotKey, out _)) + { + boundView = subview; + return true; + } + + if (subview.IsHotKeyKeyBound (key, out boundView)) + { + return true; + } + + } + return false; + } + /// /// Invoked when a key is pressed that may be mapped to a key binding. Set to true to /// stop the key from being processed by other views. @@ -756,6 +793,27 @@ public partial class View return null; } +#if DEBUG + + // TODO: Determine if App scope bindings should be fired first or last (currently last). + if (Application.TryGetKeyBindings (key, out List views)) + { + 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 annyoing vs. useful. + // Scour the bindings up our View heirarchy + // to ensure that the key is not already bound to a different set of commands. + if (SuperView?.IsHotKeyKeyBound (key, out View previouslyBoundView) ?? false) + { + Debug.WriteLine ($"WARNING: InvokeKeyBindings ({key}) - A subview or peer has bound this Key and will not see it: {previouslyBoundView}."); + } + +#endif + + foreach (Command command in binding.Commands) { if (!CommandImplementations.ContainsKey (command)) diff --git a/Terminal.Gui/View/ViewSubViews.cs b/Terminal.Gui/View/ViewSubViews.cs index ec5c68593..ad5e1016e 100644 --- a/Terminal.Gui/View/ViewSubViews.cs +++ b/Terminal.Gui/View/ViewSubViews.cs @@ -1,3 +1,5 @@ +using System.Diagnostics; + namespace Terminal.Gui; public partial class View diff --git a/Terminal.Gui/Views/Toplevel.cs b/Terminal.Gui/Views/Toplevel.cs index 44d90ed3c..617dfa7fc 100644 --- a/Terminal.Gui/Views/Toplevel.cs +++ b/Terminal.Gui/Views/Toplevel.cs @@ -119,7 +119,7 @@ public partial class Toplevel : View KeyBindings.Add (Key.Tab.WithShift.WithCtrl, Command.PreviousViewOrTop); // TODO: Refresh Key should be configurable - KeyBindings.Add (Key.F5, Command.Refresh); + KeyBindings.Add (Key.F5, KeyBindingScope.Application, Command.Refresh); KeyBindings.Add (Application.AlternateForwardKey, Command.NextViewOrTop); // Needed on Unix KeyBindings.Add (Application.AlternateBackwardKey, Command.PreviousViewOrTop); // Needed on Unix diff --git a/UICatalog/Scenarios/Shortcuts.cs b/UICatalog/Scenarios/Shortcuts.cs index 1b4a5848d..515ae96c9 100644 --- a/UICatalog/Scenarios/Shortcuts.cs +++ b/UICatalog/Scenarios/Shortcuts.cs @@ -1,6 +1,8 @@ using System; using System.Collections.ObjectModel; +using System.Diagnostics; using System.Linq; +using System.Text; using System.Threading.Tasks; using System.Timers; using Terminal.Gui; @@ -38,7 +40,9 @@ public class Shortcuts : Scenario Width = 40, Height = Dim.Fill (4), ColorScheme = Colors.ColorSchemes ["Toplevel"], - Source = new ListWrapper (eventSource) + Source = new ListWrapper (eventSource), + BorderStyle = LineStyle.Double, + Title = "E_vents" }; Application.Top.Add (eventLog); @@ -228,7 +232,7 @@ public class Shortcuts : Scenario Y = Pos.Bottom (vShortcut6), Width = Dim.Width (vShortcutSlider), Key = Key.F6, - Title = "No _Help", + Title = "Not _very much help", HelpText = "", }; @@ -348,7 +352,6 @@ public class Shortcuts : Scenario Application.Top.Add (hShortcut3); - foreach (View sh in Application.Top.Subviews.Where (v => v is Shortcut)!) { if (sh is Shortcut shortcut) diff --git a/UnitTests/View/NavigationTests.cs b/UnitTests/View/NavigationTests.cs index 3ae1ac500..6c53aceb8 100644 --- a/UnitTests/View/NavigationTests.cs +++ b/UnitTests/View/NavigationTests.cs @@ -1535,7 +1535,6 @@ public class NavigationTests (ITestOutputHelper output) } [Fact] - [AutoInitShutdown] public void WindowDispose_CanFocusProblem () { // Arrange