瀏覽代碼

Fixed Deep KeyBinding issues.
Untested

Tig 1 年之前
父節點
當前提交
9424297423

+ 12 - 4
Terminal.Gui/Application/ApplicationKeyboard.cs

@@ -141,12 +141,20 @@ partial class Application
         {
             foreach (View view in binding.Value)
             {
-                bool? handled = view?.OnInvokingKeyBindings (keyEvent);
-
-                if (handled != null && (bool)handled)
+                if (view is {} && view.KeyBindings.TryGet (binding.Key, (KeyBindingScope)0xFFFF, out KeyBinding kb))
                 {
-                    return true;
+                    bool? handled = view.InvokeCommands (kb.Commands, binding.Key, kb);
+                    if (handled != null && (bool)handled)
+                    {
+                        return true;
+                    }
                 }
+                //bool? handled = view?.OnInvokingKeyBindings (keyEvent, KeyBindingScope.Application);
+
+                //if (handled != null && (bool)handled)
+                //{
+                //    return true;
+                //}
             }
         }
 

+ 17 - 17
Terminal.Gui/View/ViewKeyboard.cs

@@ -414,7 +414,7 @@ public partial class View
             return true;
         }
 
-        bool? handled = OnInvokingKeyBindings (keyEvent);
+        bool? handled = OnInvokingKeyBindings (keyEvent, KeyBindingScope.HotKey | KeyBindingScope.Focused);
 
         if (handled is { } && (bool)handled)
         {
@@ -636,10 +636,10 @@ public partial class View
     ///     <see langword="false"/> if the key press was not handled. <see langword="true"/> if the keypress was handled
     ///     and no other view should see it.
     /// </returns>
-    public virtual bool? OnInvokingKeyBindings (Key keyEvent)
+    public virtual bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope)
     {
-        // fire event only if there's an app or hotkey binding for the key
-        if (KeyBindings.TryGet (keyEvent, KeyBindingScope.Application | KeyBindingScope.HotKey, out KeyBinding _))
+        // fire event only if there's an hotkey binding for the key
+        if (KeyBindings.TryGet (keyEvent, scope, out KeyBinding _))
         {
             InvokingKeyBindings?.Invoke (this, keyEvent);
             if (keyEvent.Handled)
@@ -655,7 +655,7 @@ public partial class View
         //   `InvokeKeyBindings` returns `false`. Continue passing the event (return `false` from `OnInvokeKeyBindings`)..
         // * If key bindings were found, and any handled the key (at least one `Command` returned `true`),
         //   `InvokeKeyBindings` returns `true`. Continue passing the event (return `false` from `OnInvokeKeyBindings`).
-        bool? handled = InvokeKeyBindings (keyEvent);
+        bool? handled = InvokeKeyBindings (keyEvent, scope);
 
         if (handled is { } && (bool)handled)
         {
@@ -664,22 +664,22 @@ public partial class View
             return true;
         }
 
-        if (Margin is { } && ProcessAdornmentKeyBindings (Margin, keyEvent, ref handled))
+        if (Margin is { } && ProcessAdornmentKeyBindings (Margin, keyEvent, scope, ref handled))
         {
             return true;
         }
 
-        if (Padding is { } && ProcessAdornmentKeyBindings (Padding, keyEvent, ref handled))
+        if (Padding is { } && ProcessAdornmentKeyBindings (Padding, keyEvent, scope, ref handled))
         {
             return true;
         }
 
-        if (Border is { } && ProcessAdornmentKeyBindings (Border, keyEvent, ref handled))
+        if (Border is { } && ProcessAdornmentKeyBindings (Border, keyEvent, scope, ref handled))
         {
             return true;
         }
 
-        if (ProcessSubViewKeyBindings (keyEvent, ref handled))
+        if (ProcessSubViewKeyBindings (keyEvent, scope, ref handled))
         {
             return true;
         }
@@ -687,11 +687,11 @@ public partial class View
         return handled;
     }
 
-    private bool ProcessAdornmentKeyBindings (Adornment adornment, Key keyEvent, ref bool? handled)
+    private bool ProcessAdornmentKeyBindings (Adornment adornment, Key keyEvent, KeyBindingScope scope, ref bool? handled)
     {
         foreach (View subview in adornment?.Subviews)
         {
-            handled = subview.OnInvokingKeyBindings (keyEvent);
+            handled = subview.OnInvokingKeyBindings (keyEvent, scope);
 
             if (handled is { } && (bool)handled)
             {
@@ -702,15 +702,15 @@ public partial class View
         return false;
     }
 
-    private bool ProcessSubViewKeyBindings (Key keyEvent, ref bool? handled)
+    private bool ProcessSubViewKeyBindings (Key keyEvent, KeyBindingScope scope, ref bool? handled)
     {
         // Now, process any key bindings in the subviews that are tagged to KeyBindingScope.HotKey.
         foreach (View subview in Subviews)
         {
-            if (subview.KeyBindings.TryGet (keyEvent, KeyBindingScope.HotKey, out KeyBinding binding))
+            if (subview.KeyBindings.TryGet (keyEvent, scope, out KeyBinding binding))
             {
                 //keyEvent.Scope = KeyBindingScope.HotKey;
-                handled = subview.OnInvokingKeyBindings (keyEvent);
+                handled = subview.OnInvokingKeyBindings (keyEvent, scope);
 
                 if (handled is { } && (bool)handled)
                 {
@@ -718,7 +718,7 @@ public partial class View
                 }
             }
 
-            bool recurse = subview.ProcessSubViewKeyBindings (keyEvent, ref handled);
+            bool recurse = subview.ProcessSubViewKeyBindings (keyEvent, scope, ref handled);
             if (recurse || (handled is { } && (bool)handled))
             {
                 return true;
@@ -744,11 +744,11 @@ public partial class View
     ///     commands were invoked and at least one handled the command. <see langword="false"/> if commands were invoked and at
     ///     none handled the command.
     /// </returns>
-    protected bool? InvokeKeyBindings (Key key)
+    protected bool? InvokeKeyBindings (Key key, KeyBindingScope scope)
     {
         bool? toReturn = null;
 
-        if (!KeyBindings.TryGet (key, out KeyBinding binding))
+        if (!KeyBindings.TryGet (key, scope, out KeyBinding binding))
         {
             return null;
         }

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

@@ -273,9 +273,9 @@ internal sealed class Menu : View
     }
 
     /// <inheritdoc/>
-    public override bool? OnInvokingKeyBindings (Key keyEvent)
+    public override bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope)
     {
-        bool? handled = base.OnInvokingKeyBindings (keyEvent);
+        bool? handled = base.OnInvokingKeyBindings (keyEvent, scope);
 
         if (handled is { } && (bool)handled)
         {
@@ -284,7 +284,7 @@ internal sealed class Menu : View
 
         // TODO: Determine if there's a cleaner way to handle this
         // This supports the case where the menu bar is a context menu
-        return _host.OnInvokingKeyBindings (keyEvent);
+        return _host.OnInvokingKeyBindings (keyEvent, scope);
     }
 
     private void Current_TerminalResized (object sender, SizeChangedEventArgs e)

+ 17 - 0
Terminal.Gui/Views/RadioGroup.cs

@@ -25,6 +25,11 @@ public class RadioGroup : View
                     Command.LineUp,
                     () =>
                     {
+                        if (!HasFocus)
+                        {
+                            return false;
+                        }
+
                         MoveUpLeft ();
 
                         return true;
@@ -35,6 +40,10 @@ public class RadioGroup : View
                     Command.LineDown,
                     () =>
                     {
+                        if (!HasFocus)
+                        {
+                            return false;
+                        }
                         MoveDownRight ();
 
                         return true;
@@ -45,6 +54,10 @@ public class RadioGroup : View
                     Command.TopHome,
                     () =>
                     {
+                        if (!HasFocus)
+                        {
+                            return false;
+                        }
                         MoveHome ();
 
                         return true;
@@ -55,6 +68,10 @@ public class RadioGroup : View
                     Command.BottomEnd,
                     () =>
                     {
+                        if (!HasFocus)
+                        {
+                            return false;
+                        }
                         MoveEnd ();
 
                         return true;

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

@@ -392,7 +392,7 @@ public class ScrollView : View
             return true;
         }
 
-        bool? result = InvokeKeyBindings (a);
+        bool? result = InvokeKeyBindings (a, KeyBindingScope.HotKey);
 
         if (result is { })
         {

+ 22 - 4
Terminal.Gui/Views/Shortcut.cs

@@ -59,10 +59,11 @@ public class Shortcut : View
         Width = GetWidthDimAuto ();
         Height = Dim.Auto (DimAutoStyle.Content, 1);
 
-        AddCommand (Command.HotKey, ctx => OnAccept(ctx));
+        AddCommand (Command.HotKey, ctx => OnAccept (ctx));
         AddCommand (Command.Accept, ctx => OnAccept (ctx));
-        KeyBindings.Add (KeyCode.Space, Command.Accept);
+        AddCommand (Command.Select, ctx => OnSelect (ctx));
         KeyBindings.Add (KeyCode.Enter, Command.Accept);
+        KeyBindings.Add (KeyCode.Space, Command.Select);
 
         TitleChanged += Shortcut_TitleChanged; // This needs to be set before CommandView is set
 
@@ -130,6 +131,7 @@ public class Shortcut : View
         }
     }
 
+
     /// <summary>
     ///     Creates a new instance of <see cref="Shortcut"/>.
     /// </summary>
@@ -403,6 +405,10 @@ public class Shortcut : View
             _commandView = value;
             _commandView.Id = "_commandView";
 
+            // The default behavior is for CommandView to not get focus. I
+            // If you want it to get focus, you need to set it.
+            _commandView.CanFocus = false;
+
             _commandView.MouseClick += Shortcut_MouseClick;
             _commandView.Accept += CommandViewAccept;
 
@@ -608,7 +614,8 @@ public class Shortcut : View
             CommandView.KeyBindings.Remove (Key);
             CommandView.KeyBindings.Remove (CommandView.HotKey);
             KeyBindings.Remove (Key);
-            KeyBindings.Add (Key, KeyBindingScope, Command.Accept);
+            KeyBindings.Add (Key, KeyBindingScope | KeyBindingScope.HotKey, Command.Accept);
+            //KeyBindings.Add (Key, KeyBindingScope.HotKey, Command.Accept);
         }
     }
 
@@ -637,7 +644,7 @@ public class Shortcut : View
 
             case KeyBindingScope.Focused:
                 // TODO: Figure this out
-                cancel = false;
+                cancel = base.OnAccept () == true;
 
                 break;
 
@@ -678,6 +685,17 @@ public class Shortcut : View
 
     #endregion Accept Handling
 
+    private bool? OnSelect (CommandContext ctx)
+    {
+        if (CommandView.GetSupportedCommands ().Contains (Command.Select))
+        {
+           return CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
+        }
+        return false;
+
+    }
+
+
     #region Focus
 
     /// <inheritdoc/>

+ 16 - 7
Terminal.Gui/Views/Slider.cs

@@ -1,4 +1,6 @@
-namespace Terminal.Gui;
+using System.Transactions;
+
+namespace Terminal.Gui;
 
 /// <summary>Slider control.</summary>
 public class Slider : Slider<object>
@@ -1418,7 +1420,8 @@ public class Slider<T> : View
         AddCommand (Command.RightEnd, () => MoveEnd ());
         AddCommand (Command.RightExtend, () => ExtendPlus ());
         AddCommand (Command.LeftExtend, () => ExtendMinus ());
-        AddCommand (Command.Accept, () => Set ());
+        AddCommand (Command.Select, () => Select ());
+        AddCommand (Command.Accept, () => Accept ());
 
         SetKeyBindings ();
     }
@@ -1454,7 +1457,7 @@ public class Slider<T> : View
         KeyBindings.Add (Key.Home, Command.LeftHome);
         KeyBindings.Add (Key.End, Command.RightEnd);
         KeyBindings.Add (Key.Enter, Command.Accept);
-        KeyBindings.Add (Key.Space, Command.Accept);
+        KeyBindings.Add (Key.Space, Command.Select);
     }
 
     private Dictionary<int, SliderOption<T>> GetSetOptionDictionary () { return _setOptions.ToDictionary (e => e, e => _options [e]); }
@@ -1651,8 +1654,6 @@ public class Slider<T> : View
             default:
                 throw new ArgumentOutOfRangeException (_config._type.ToString ());
         }
-        OnAccept ();
-
     }
 
     internal bool ExtendPlus ()
@@ -1735,12 +1736,20 @@ public class Slider<T> : View
         return true;
     }
 
-    internal bool Set ()
+    internal bool Select ()
     {
-        SetFocusedOption ();
+        SetFocusedOption();
+
         return true;
     }
 
+    internal bool Accept ()
+    {
+        SetFocusedOption ();
+
+        return OnAccept () == true;
+    }
+
     internal bool MovePlus ()
     {
         bool cancelled = OnOptionFocused (

+ 2 - 2
Terminal.Gui/Views/TextField.cs

@@ -1034,7 +1034,7 @@ public class TextField : View
     }
 
     /// <inheritdoc/>
-    public override bool? OnInvokingKeyBindings (Key a)
+    public override bool? OnInvokingKeyBindings (Key a, KeyBindingScope scope)
     {
         // Give autocomplete first opportunity to respond to key presses
         if (SelectedLength == 0 && Autocomplete.Suggestions.Count > 0 && Autocomplete.ProcessKey (a))
@@ -1042,7 +1042,7 @@ public class TextField : View
             return true;
         }
 
-        return base.OnInvokingKeyBindings (a);
+        return base.OnInvokingKeyBindings (a, scope);
     }
 
     /// <inheritdoc/>

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

@@ -3634,7 +3634,7 @@ public class TextView : View
     }
 
     /// <inheritdoc/>
-    public override bool? OnInvokingKeyBindings (Key a)
+    public override bool? OnInvokingKeyBindings (Key a, KeyBindingScope scope)
     {
         if (!a.IsValid)
         {
@@ -3647,7 +3647,7 @@ public class TextView : View
             return true;
         }
 
-        return base.OnInvokingKeyBindings (a);
+        return base.OnInvokingKeyBindings (a, scope);
     }
 
     /// <inheritdoc/>

+ 21 - 10
UICatalog/Scenarios/Shortcuts.cs

@@ -66,7 +66,7 @@ public class Shortcuts : Scenario
             CommandView = new RadioGroup
             {
                 Orientation = Orientation.Vertical,
-                RadioLabels = ["One", "Two", "Three", "Four"]
+                RadioLabels = ["O_ne", "T_wo", "Th_ree", "Fo_ur"]
             }
         };
 
@@ -173,7 +173,6 @@ public class Shortcuts : Scenario
                                                                  if (peer.CanFocus)
                                                                  {
                                                                      peer.CommandView.CanFocus = e.NewValue == true;
-                                                                     //peer.SetColors ();
                                                                  }
                                                              }
                                                          }
@@ -185,7 +184,6 @@ public class Shortcuts : Scenario
             Orientation = Orientation.Vertical,
             X = 0,
             Y = Pos.Bottom (vShortcut5),
-            Key = Key.F5,
             HelpText = "Width is Fill",
             Width = Dim.Width (vShortcut5),
 
@@ -193,8 +191,9 @@ public class Shortcuts : Scenario
             CommandView = new Slider<string>
             {
                 Orientation = Orientation.Vertical,
-                AllowEmpty = false
-            }
+                AllowEmpty = true
+            },
+            Key = Key.F5,
         };
 
         ((Slider<string>)vShortcutSlider.CommandView).Options = new () { new () { Legend = "A" }, new () { Legend = "B" }, new () { Legend = "C" } };
@@ -202,7 +201,7 @@ public class Shortcuts : Scenario
 
         ((Slider<string>)vShortcutSlider.CommandView).OptionsChanged += (o, args) =>
                                                                        {
-                                                                           eventSource.Add ($"OptionsChanged: {o.GetType ().Name} - {args.Options}");
+                                                                           eventSource.Add ($"OptionsChanged: {o.GetType ().Name} - {string.Join (",", ((Slider<string>)o).GetSetOptions ())}");
                                                                            eventLog.MoveDown ();
                                                                        };
 
@@ -220,9 +219,22 @@ public class Shortcuts : Scenario
         };
 
         Application.Top.Add (vShortcut6);
-        vShortcut6.SetFocus ();
 
-        ((CheckBox)vShortcut3.CommandView).OnToggled ();
+
+        var vShortcut7 = new Shortcut
+        {
+            Orientation = Orientation.Vertical,
+            X = 0,
+            Y = Pos.Bottom (vShortcut6),
+            Width = Dim.Width (vShortcutSlider),
+            Key = Key.F6,
+            Title = "No _Help",
+            HelpText = "",
+        };
+
+        Application.Top.Add (vShortcut7);
+        vShortcut7.SetFocus ();
+
 
         // Horizontal
         var hShortcut1 = new Shortcut
@@ -350,8 +362,7 @@ public class Shortcuts : Scenario
             }
         }
 
-        ((CheckBox)vShortcut5.CommandView).OnToggled ();
-        ((CheckBox)vShortcut5.CommandView).OnToggled ();
+        //((CheckBox)vShortcut5.CommandView).OnToggled ();
     }
 
     private void Button_Clicked (object sender, EventArgs e) { MessageBox.Query ("Hi", $"You clicked {sender}"); }

+ 36 - 5
UICatalog/Scenarios/Sliders.cs

@@ -1,5 +1,7 @@
 using System;
 using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Diagnostics.Tracing;
 using System.Linq;
 using System.Text;
 using Terminal.Gui;
@@ -242,7 +244,7 @@ public class Sliders : Scenario
                                       }
                                   };
         configView.Add (dimAutoUsesMin);
-    
+
         #region Slider Orientation Slider
 
         Slider<string> orientationSlider = new (new List<string> { "Horizontal", "Vertical" })
@@ -393,7 +395,7 @@ public class Sliders : Scenario
         FrameView spacingOptions = new ()
         {
             Title = "Spacing Options",
-            X = Pos.Right(orientationSlider),
+            X = Pos.Right (orientationSlider),
             Y = Pos.Top (orientationSlider),
             Width = Dim.Fill (),
             Height = Dim.Auto (),
@@ -407,7 +409,7 @@ public class Sliders : Scenario
 
         Buttons.NumericUpDown<int> innerSpacingUpDown = new ()
         {
-           X = Pos.Right(label) + 1
+            X = Pos.Right (label) + 1
         };
 
         innerSpacingUpDown.Value = app.Subviews.OfType<Slider> ().First ().MinimumInnerSpacing;
@@ -429,8 +431,8 @@ public class Sliders : Scenario
 
 
 
-        spacingOptions.Add(label, innerSpacingUpDown);
-        configView.Add(spacingOptions);
+        spacingOptions.Add (label, innerSpacingUpDown);
+        configView.Add (spacingOptions);
 
         #endregion
 
@@ -564,6 +566,35 @@ public class Sliders : Scenario
 
         #endregion Config Slider
 
+        ObservableCollection<string> eventSource = new ();
+        var eventLog = new ListView
+        {
+            X = Pos.Right (sliderBGColor),
+            Y = Pos.Bottom (spacingOptions),
+            Width = Dim.Fill (),
+            Height = Dim.Fill (),
+            ColorScheme = Colors.ColorSchemes ["Toplevel"],
+            Source = new ListWrapper<string> (eventSource)
+        };
+        configView.Add (eventLog);
+
+
+        foreach (Slider slider in app.Subviews.Where (v => v is Slider)!)
+        {
+            slider.Accept += (o, args) =>
+                             {
+                                 eventSource.Add ($"Accept: {string.Join(",", slider.GetSetOptions ())}");
+                                 eventLog.MoveDown ();
+                                 args.Cancel = true;
+                             };
+            slider.OptionsChanged += (o, args) =>
+                             {
+                                 eventSource.Add ($"OptionsChanged: {string.Join (",", slider.GetSetOptions ())}");
+                                 eventLog.MoveDown ();
+                                 args.Cancel = true;
+                             };
+        }
+
         app.FocusFirst ();
 
         Application.Run (app);

+ 1 - 1
UICatalog/Scenarios/VkeyPacketSimulator.cs

@@ -104,7 +104,7 @@ public class VkeyPacketSimulator : Scenario
                                 if (_outputStarted)
                                 {
                                     // If the key wasn't handled by the TextView will popup a Dialog with the keys pressed.
-                                    bool? handled = tvOutput.OnInvokingKeyBindings (e);
+                                    bool? handled = tvOutput.OnInvokingKeyBindings (e, KeyBindingScope.HotKey | KeyBindingScope.Focused);
 
                                     if (handled == null || handled == false)
                                     {

+ 3 - 3
UnitTests/View/KeyboardEventTests.cs

@@ -421,7 +421,7 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
         var view = new KeyBindingsTestView ();
         view.CommandReturns = toReturn;
 
-        bool? result = view.OnInvokingKeyBindings (Key.A);
+        bool? result = view.OnInvokingKeyBindings (Key.A, KeyBindingScope.HotKey | KeyBindingScope.Focused);
         Assert.Equal (expected, result);
     }
 
@@ -449,9 +449,9 @@ public class KeyboardEventTests (ITestOutputHelper output) : TestsAllViews
         public bool OnKeyUpContinued { get; set; }
         public override string Text { get; set; }
 
-        public override bool? OnInvokingKeyBindings (Key keyEvent)
+        public override bool? OnInvokingKeyBindings (Key keyEvent, KeyBindingScope scope)
         {
-            bool? handled = base.OnInvokingKeyBindings (keyEvent);
+            bool? handled = base.OnInvokingKeyBindings (keyEvent, scope);
 
             if (handled != null && (bool)handled)
             {

+ 37 - 0
UnitTests/Views/ShortcutTests.cs

@@ -277,4 +277,41 @@ public class ShortcutTests
         Assert.False (shortcut.KeyView.Visible);
         Assert.DoesNotContain (shortcut.KeyView, shortcut.Subviews);
     }
+
+    [Fact]
+    public void Focus_CanFocus_Default_Is_True ()
+    {
+        Shortcut shortcut = new ();
+        shortcut.Key = Key.A;
+        shortcut.Text = "Help";
+        shortcut.Title = "Command";
+        Assert.True (shortcut.CanFocus);
+        Assert.False (shortcut.CommandView.CanFocus);
+    }
+
+    [Fact]
+    public void Focus_CanFocus_CommandView_Add_Tracks ()
+    {
+        Shortcut shortcut = new ();
+        Assert.True (shortcut.CanFocus);
+        Assert.False (shortcut.CommandView.CanFocus);
+
+        shortcut.CommandView = new () { CanFocus = true };
+        Assert.False (shortcut.CommandView.CanFocus);
+
+        shortcut.CommandView.CanFocus = true;
+        Assert.True (shortcut.CommandView.CanFocus);
+
+        shortcut.CanFocus = false;
+        Assert.False (shortcut.CanFocus);
+        Assert.True (shortcut.CommandView.CanFocus);
+
+        shortcut.CommandView.CanFocus = false;
+        Assert.False (shortcut.CanFocus);
+        Assert.False (shortcut.CommandView.CanFocus);
+
+        shortcut.CommandView.CanFocus = true;
+        Assert.False (shortcut.CanFocus);
+        Assert.True (shortcut.CommandView.CanFocus);
+    }
 }

+ 1 - 1
UnitTests/Views/SliderTests.cs

@@ -310,7 +310,7 @@ public class SliderTests
 
         // Act
         slider.FocusedOption = 2;
-        bool result = slider.Set ();
+        bool result = slider.Select ();
 
         // Assert
         Assert.True (result);