Prechádzať zdrojové kódy

Manual backport from popover

Tig 10 mesiacov pred
rodič
commit
ac42da953f

+ 1 - 1
Terminal.Gui/View/View.Mouse.cs

@@ -351,7 +351,7 @@ public partial class View // Mouse APIs
         // BUGBUG: This should be named NewMouseClickEvent. Fix this in https://github.com/gui-cs/Terminal.Gui/issues/3029
 
         // Pre-conditions
-        if (!Enabled)
+        if (!Enabled || !CanFocus)
         {
             // QUESTION: Is this right? Should a disabled view eat mouse clicks?
             return args.Handled = false;

+ 1 - 1
Terminal.Gui/View/View.Navigation.cs

@@ -421,7 +421,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
     /// <exception cref="InvalidOperationException"></exception>
     private (bool focusSet, bool cancelled) SetHasFocusTrue (View? previousFocusedView, bool traversingUp = false)
     {
-        Debug.Assert (ApplicationNavigation.IsInHierarchy (SuperView, this));
+        Debug.Assert (SuperView is null || ApplicationNavigation.IsInHierarchy (SuperView, this));
 
         // Pre-conditions
         if (_hasFocus)

+ 34 - 0
Terminal.Gui/Views/Bar.cs

@@ -32,6 +32,7 @@ public class Bar : View, IOrientation, IDesignable
         _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
 
         Initialized += Bar_Initialized;
+        MouseEvent += OnMouseEvent;
 
         if (shortcuts is null)
         {
@@ -44,6 +45,38 @@ public class Bar : View, IOrientation, IDesignable
         }
     }
 
+    private void OnMouseEvent (object? sender, MouseEventEventArgs e)
+    {
+        NavigationDirection direction = NavigationDirection.Backward;
+
+        if (e.MouseEvent.Flags == MouseFlags.WheeledDown)
+        {
+            e.Handled = true;
+        }
+
+        if (e.MouseEvent.Flags == MouseFlags.WheeledUp)
+        {
+            direction = NavigationDirection.Forward;
+            e.Handled = true;
+        }
+
+        if (e.MouseEvent.Flags == MouseFlags.WheeledRight)
+        {
+            e.Handled = true;
+        }
+
+        if (e.MouseEvent.Flags == MouseFlags.WheeledLeft)
+        {
+            direction = NavigationDirection.Forward;
+            e.Handled = true;
+        }
+
+        if (e.Handled)
+        {
+            e.Handled = AdvanceFocus (direction, TabBehavior.TabStop);
+        }
+    }
+
     private void Bar_Initialized (object? sender, EventArgs e) { ColorScheme = Colors.ColorSchemes ["Menu"]; }
 
     /// <inheritdoc/>
@@ -243,6 +276,7 @@ public class Bar : View, IOrientation, IDesignable
         }
     }
 
+
     /// <inheritdoc />
     public bool EnableForDesign ()
     {

+ 11 - 9
Terminal.Gui/Views/CheckBox.cs

@@ -26,7 +26,7 @@ public class CheckBox : View
         AddCommand (Command.Select, AdvanceCheckState);
 
         // Accept (Enter key and double-click) - Raise Accept event - DO NOT advance state
-        AddCommand (Command.Accept, RaiseAcceptEvent);
+        AddCommand (Command.Accept, () => RaiseAcceptEvent ());
 
         // Hotkey - Advance state and raise Select event - DO NOT raise Accept
         AddCommand (Command.HotKey, AdvanceCheckState);
@@ -45,7 +45,9 @@ public class CheckBox : View
 
         if (e.MouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked))
         {
-            e.Handled = AdvanceCheckState () == true;
+            AdvanceCheckState ();
+            ;
+            //           e.Handled = AdvanceCheckState () == true;
         }
 
 #if CHECKBOX_SUPPORTS_DOUBLE_CLICK_ACCEPT
@@ -147,11 +149,6 @@ public class CheckBox : View
             return null;
         }
 
-        if (RaiseSelectEvent () == true)
-        {
-            return true;
-        }
-
         CancelEventArgs<CheckState> e = new (in _checkedState, ref value);
 
         if (OnCheckedStateChanging (e))
@@ -174,6 +171,11 @@ public class CheckBox : View
 
         CheckedStateChanged?.Invoke (this, args);
 
+        if (RaiseSelectEvent () == true)
+        {
+            return true;
+        }
+
         return false;
     }
 
@@ -183,7 +185,7 @@ public class CheckBox : View
     ///    The state cahnge can be cancelled by setting the args.Cancel to <see langword="true"/>.
     /// </para>
     /// </remarks>
-    protected virtual bool OnCheckedStateChanging (CancelEventArgs<CheckState> args) { return false;}
+    protected virtual bool OnCheckedStateChanging (CancelEventArgs<CheckState> args) { return false; }
 
     /// <summary>Raised when the <see cref="CheckBox"/> state is changing.</summary>
     /// <remarks>
@@ -241,7 +243,7 @@ public class CheckBox : View
 
         bool? cancelled = ChangeCheckedState (e.NewValue);
 
-        return !cancelled;
+        return cancelled;
     }
 
     /// <inheritdoc/>

+ 23 - 9
Terminal.Gui/Views/Menuv2.cs

@@ -17,13 +17,30 @@ public class Menuv2 : Bar
         Orientation = Orientation.Vertical;
         Width = Dim.Auto ();
         Height = Dim.Auto (DimAutoStyle.Content, 1);
-        ColorScheme = Colors.ColorSchemes ["Menu"];
         Initialized += Menuv2_Initialized;
+        VisibleChanged += OnVisibleChanged;
+    }
+
+    private void OnVisibleChanged (object sender, EventArgs e)
+    {
+        if (Visible)
+        {
+            //Application.GrabMouse(this);
+        }
+        else
+        {
+            if (Application.MouseGrabView == this)
+            {
+                //Application.UngrabMouse ();
+            }
+        }
     }
 
     private void Menuv2_Initialized (object sender, EventArgs e)
     {
         Border.Thickness = new Thickness (1, 1, 1, 1);
+        Border.LineStyle = LineStyle.Single;
+        ColorScheme = Colors.ColorSchemes ["Menu"];
     }
 
     // Menuv2 arranges the items horizontally.
@@ -52,7 +69,6 @@ public class Menuv2 : Bar
         if (view is Shortcut shortcut)
         {
             shortcut.CanFocus = true;
-            shortcut.KeyBindingScope = KeyBindingScope.Application;
             shortcut.Orientation = Orientation.Vertical;
             shortcut.HighlightStyle |= HighlightStyle.Hover;
 
@@ -64,20 +80,18 @@ public class Menuv2 : Bar
 
             void ShortcutOnAccept (object sender, HandledEventArgs e)
             {
-                if (Arrangement.HasFlag(ViewArrangement.Overlapped) && Visible)
+                if (Arrangement.HasFlag (ViewArrangement.Overlapped) && Visible)
                 {
                     Visible = false;
                     e.Handled = true;
 
                     return;
-
-                    //Enabled = Visible;
                 }
 
-                if (!e.Handled)
-                {
-                    RaiseAcceptEvent ();
-                }
+                //if (!e.Handled)
+                //{
+                //    RaiseAcceptEvent ();
+                //}
             }
         }
 

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

@@ -70,7 +70,22 @@ public class RadioGroup : View, IDesignable, IOrientation
                         return true;
                     }
                    );
+        AddCommand (
+                    Command.Select,
+                    () =>
+                    {
+                        if (SelectedItem == Cursor)
+                        {
+                            if (!MoveDownRight ())
+                            {
+                                MoveHome ();
+                            }
+                        }
 
+                        SelectedItem = Cursor;
+
+                        return true;
+                    });
         AddCommand (
                     Command.Select,
                     () =>

+ 29 - 12
Terminal.Gui/Views/Shortcut.cs

@@ -102,7 +102,7 @@ public class Shortcut : View, IOrientation, IDesignable
 
         AddCommand (Command.HotKey, ctx => DispatchAcceptCommand (ctx));
         AddCommand (Command.Accept, ctx => DispatchAcceptCommand (ctx));
-        AddCommand (Command.Select, ctx => OnSelect (ctx));
+        AddCommand (Command.Select, ctx => RaiseSelectEvent ());
 
         TitleChanged += Shortcut_TitleChanged; // This needs to be set before CommandView is set
 
@@ -123,8 +123,8 @@ public class Shortcut : View, IOrientation, IDesignable
 
         // If the user clicks anywhere on the Shortcut, other than the CommandView, invoke the Command
         MouseClick += Shortcut_MouseClick;
-        HelpView.MouseClick += Subview_MouseClick;
-        KeyView.MouseClick += Subview_MouseClick;
+        //HelpView.MouseClick += Subview_MouseClick;
+        //KeyView.MouseClick += Subview_MouseClick;
         LayoutStarted += OnLayoutStarted;
         Initialized += OnInitialized;
 
@@ -375,8 +375,8 @@ public class Shortcut : View, IOrientation, IDesignable
 
         if (!e.Handled)
         {
-            // If the subview (likely CommandView) didn't handle the mouse click, invoke the command.
-            e.Handled = InvokeCommand (Command.Accept) == true;
+            // If the subview (likely CommandView) didn't handle the mouse click, invoke the Select command.
+            e.Handled = CommandView.InvokeCommand (Command.Accept) == true;
         }
 
         if (CanFocus)
@@ -387,7 +387,11 @@ public class Shortcut : View, IOrientation, IDesignable
 
     private void Subview_MouseClick (object sender, MouseEventEventArgs e)
     {
-        // TODO: Remove. This does nothing.
+        if (!e.Handled)
+        {
+            // If the subview (likely CommandView) didn't handle the mouse click, invoke the command.
+            e.Handled = InvokeCommand (Command.Accept) == true;
+        }
     }
 
     #region IOrientation members
@@ -520,10 +524,23 @@ public class Shortcut : View, IOrientation, IDesignable
 
             _commandView.Select += CommandViewOnSelect;
 
+            _commandView.MouseClick += CommandViewOnMouseClick;
+
+            void CommandViewOnMouseClick (object sender, MouseEventEventArgs e)
+            {
+                //if (!e.Handled)
+                {
+                    // If the subview (likely CommandView) didn't handle the mouse click, invoke the command.
+                    InvokeCommand (Command.HotKey);
+                }
+            }
+
             void CommandViewOnSelect (object sender, HandledEventArgs e)
             {
-                SetFocus ();
-                //OnAccept ();
+                //e.Handled = true;
+                RaiseAcceptEvent ();
+
+                // SetFocus ();
             }
 
             SetCommandViewDefaultLayout ();
@@ -769,10 +786,10 @@ public class Shortcut : View, IOrientation, IDesignable
 
         cancel = RaiseAcceptEvent () == true;
 
-        if (!cancel && ctx.KeyBinding?.BoundView != CommandView)
-        {
-           // CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
-        }
+        //if (!cancel && ctx.KeyBinding?.BoundView != CommandView)
+        //{
+        //   // CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
+        //}
 
         if (Action is { })
         {

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

@@ -2460,8 +2460,8 @@ public class TextView : View
 
         KeyBindings.Add (Key.C.WithCtrl, Command.Copy);
 
-        KeyBindings.Add (Key.W.WithCtrl, Command.Cut);
-        KeyBindings.Add (Key.X.WithCtrl, Command.Cut);
+        KeyBindings.Add (Key.W.WithCtrl, Command.Cut); // Move to Unix?
+        KeyBindings.Add (Key.X.WithCtrl, Command.Cut); 
 
         KeyBindings.Add (Key.CursorLeft.WithCtrl, Command.WordLeft);
 
@@ -2495,7 +2495,6 @@ public class TextView : View
         KeyBindings.Add (Key.V.WithAlt, Command.PageUp);
         KeyBindings.Add (Key.F.WithAlt, Command.WordRight);
         KeyBindings.Add (Key.K.WithAlt, Command.CutToStartLine); // kill-to-start
-
 #endif
 
         _currentCulture = Thread.CurrentThread.CurrentUICulture;

+ 101 - 92
UICatalog/Scenarios/Shortcuts.cs

@@ -38,13 +38,13 @@ public class Shortcuts : Scenario
         var eventLog = new ListView
         {
             X = Pos.AnchorEnd (),
-            Width = 40,
             Height = Dim.Fill (4),
             ColorScheme = Colors.ColorSchemes ["Toplevel"],
             Source = new ListWrapper<string> (eventSource),
             BorderStyle = LineStyle.Double,
             Title = "E_vents"
         };
+        eventLog.Width = Dim.Func (() => Math.Min (Application.Top.Viewport.Width / 2, eventLog?.MaxLength + eventLog.GetAdornmentsThickness ().Horizontal ?? 0));
         Application.Top.Add (eventLog);
 
         var vShortcut1 = new Shortcut
@@ -72,15 +72,16 @@ public class Shortcuts : Scenario
             CommandView = new RadioGroup
             {
                 Orientation = Orientation.Vertical,
-                RadioLabels = ["O_ne", "T_wo", "Th_ree", "Fo_ur"]
+                RadioLabels = ["O_ne", "T_wo", "Th_ree", "Fo_ur"],
+                CanFocus = false
             },
         };
 
         ((RadioGroup)vShortcut2.CommandView).SelectedItemChanged += (o, args) =>
-                                                                    {
-                                                                        eventSource.Add ($"SelectedItemChanged: {o.GetType ().Name} - {args.SelectedItem}");
-                                                                        eventLog.MoveDown ();
-                                                                    };
+        {
+            eventSource.Add ($"SelectedItemChanged: {o.GetType ().Name} - {args.SelectedItem}");
+            eventLog.MoveDown ();
+        };
 
         Application.Top.Add (vShortcut2);
 
@@ -89,7 +90,12 @@ public class Shortcuts : Scenario
             Orientation = Orientation.Vertical,
             X = 0,
             Y = Pos.Bottom (vShortcut2),
-            CommandView = new CheckBox { Text = "_Align" },
+            CommandView = new CheckBox
+            {
+                Text = "_Align",
+                CanFocus = false,
+                HighlightStyle = HighlightStyle.None,
+            },
             Key = Key.F5.WithCtrl.WithAlt.WithShift,
             HelpText = "Width is Fill",
             Width = Dim.Fill () - Dim.Width (eventLog),
@@ -97,30 +103,30 @@ public class Shortcuts : Scenario
         };
 
         ((CheckBox)vShortcut3.CommandView).CheckedStateChanging += (s, e) =>
-                                                      {
-                                                          if (vShortcut3.CommandView is CheckBox cb)
-                                                          {
-                                                              eventSource.Add ($"Toggle: {cb.Text}");
-                                                              eventLog.MoveDown ();
-
-                                                              var max = 0;
-                                                              var toAlign = Application.Top.Subviews.Where (v => v is Shortcut { Orientation: Orientation.Vertical, Width: not DimAbsolute });
-
-                                                              if (e.NewValue == CheckState.Checked)
-                                                              {
-                                                                  foreach (Shortcut peer in toAlign)
-                                                                  {
-                                                                      // DANGER: KeyView is internal so we can't access it. So we assume this is how it works.
-                                                                      max = Math.Max (max, peer.Key.ToString ().GetColumns ());
-                                                                  }
-                                                              }
-
-                                                              foreach (Shortcut peer in toAlign)
-                                                              {
-                                                                  peer.MinimumKeyTextSize = max;
-                                                              }
-                                                          }
-                                                      };
+        {
+            if (vShortcut3.CommandView is CheckBox cb)
+            {
+                eventSource.Add ($"{vShortcut3.Id}.CommandView.CheckedStateChanging: {cb.Text}");
+                eventLog.MoveDown ();
+
+                var max = 0;
+                var toAlign = Application.Top.Subviews.Where (v => v is Shortcut { Orientation: Orientation.Vertical, Width: not DimAbsolute });
+
+                if (e.NewValue == CheckState.Checked)
+                {
+                    foreach (Shortcut peer in toAlign)
+                    {
+                        // DANGER: KeyView is internal so we can't access it. So we assume this is how it works.
+                        max = Math.Max (max, peer.Key.ToString ().GetColumns ());
+                    }
+                }
+
+                foreach (Shortcut peer in toAlign)
+                {
+                    peer.MinimumKeyTextSize = max;
+                }
+            }
+        };
         Application.Top.Add (vShortcut3);
 
         var vShortcut4 = new Shortcut
@@ -132,6 +138,8 @@ public class Shortcuts : Scenario
             CommandView = new Button
             {
                 Title = "_Button",
+                ShadowStyle = ShadowStyle.None,
+                HighlightStyle = HighlightStyle.None
             },
             HelpText = "Width is Fill",
             Key = Key.K,
@@ -156,21 +164,21 @@ public class Shortcuts : Scenario
         };
 
         ((CheckBox)vShortcut5.CommandView).CheckedStateChanging += (s, e) =>
-                                                     {
-                                                         if (vShortcut5.CommandView is CheckBox cb)
-                                                         {
-                                                             eventSource.Add ($"Toggle: {cb.Text}");
-                                                             eventLog.MoveDown ();
-
-                                                             foreach (Shortcut peer in Application.Top.Subviews.Where (v => v is Shortcut)!)
-                                                             {
-                                                                 if (peer.CanFocus)
-                                                                 {
-                                                                     peer.CommandView.CanFocus = e.NewValue == CheckState.Checked;
-                                                                 }
-                                                             }
-                                                         }
-                                                     };
+        {
+            if (vShortcut5.CommandView is CheckBox cb)
+            {
+                eventSource.Add ($"Toggle: {cb.Text}");
+                eventLog.MoveDown ();
+
+                //foreach (Shortcut peer in Application.Top.Subviews.Where (v => v is Shortcut)!)
+                //{
+                //    if (peer.CanFocus)
+                //    {
+                //        peer.CommandView.CanFocus = e.NewValue == CheckState.Checked;
+                //    }
+                //}
+            }
+        };
         Application.Top.Add (vShortcut5);
 
         var vShortcutSlider = new Shortcut
@@ -194,10 +202,10 @@ public class Shortcuts : Scenario
         ((Slider<string>)vShortcutSlider.CommandView).SetOption (0);
 
         ((Slider<string>)vShortcutSlider.CommandView).OptionsChanged += (o, args) =>
-                                                                       {
-                                                                           eventSource.Add ($"OptionsChanged: {o.GetType ().Name} - {string.Join (",", ((Slider<string>)o).GetSetOptions ())}");
-                                                                           eventLog.MoveDown ();
-                                                                       };
+        {
+            eventSource.Add ($"OptionsChanged: {o.GetType ().Name} - {string.Join (",", ((Slider<string>)o).GetSetOptions ())}");
+            eventLog.MoveDown ();
+        };
 
         Application.Top.Add (vShortcutSlider);
 
@@ -258,20 +266,20 @@ public class Shortcuts : Scenario
             AutoReset = true,
         };
         timer.Elapsed += (o, args) =>
-                         {
-                             if (hShortcut1.CommandView is ProgressBar pb)
-                             {
-                                 if (pb.Fraction == 1.0)
-                                 {
-                                     pb.Fraction = 0;
-                                 }
-                                 pb.Fraction += 0.01f;
-
-                                 Application.Wakeup ();
-
-                                 pb.SetNeedsDisplay ();
-                             }
-                         };
+        {
+            if (hShortcut1.CommandView is ProgressBar pb)
+            {
+                if (pb.Fraction == 1.0)
+                {
+                    pb.Fraction = 0;
+                }
+                pb.Fraction += 0.01f;
+
+                Application.Wakeup ();
+
+                pb.SetNeedsDisplay ();
+            }
+        };
         timer.Start ();
 
         Application.Top.Add (hShortcut1);
@@ -314,12 +322,12 @@ public class Shortcuts : Scenario
             CanFocus = false
         };
         bgColor.ColorChanged += (o, args) =>
-                                {
-                                    Application.Top.ColorScheme = new ColorScheme (Application.Top.ColorScheme)
-                                    {
-                                        Normal = new Attribute (Application.Top.ColorScheme.Normal.Foreground, args.CurrentValue),
-                                    };
-                                };
+        {
+            Application.Top.ColorScheme = new ColorScheme (Application.Top.ColorScheme)
+            {
+                Normal = new Attribute (Application.Top.ColorScheme.Normal.Foreground, args.CurrentValue),
+            };
+        };
         hShortcutBG.CommandView = bgColor;
 
         Application.Top.Add (hShortcutBG);
@@ -336,9 +344,9 @@ public class Shortcuts : Scenario
             CanFocus = false
         };
         hShortcut3.Accept += (o, args) =>
-                            {
-                                Application.RequestStop ();
-                            };
+        {
+            Application.RequestStop ();
+        };
 
         Application.Top.Add (hShortcut3);
 
@@ -347,30 +355,30 @@ public class Shortcuts : Scenario
             if (sh is Shortcut shortcut)
             {
                 shortcut.Select += (o, args) =>
-                                   {
-                                       eventSource.Add ($"Select: {shortcut!.CommandView.Text} {shortcut!.CommandView.GetType ().Name}");
-                                       eventLog.MoveDown ();
-                                       args.Handled = true;
-                                   };
+                {
+                    eventSource.Add ($"{shortcut!.Id}.Select: {shortcut!.CommandView.Text} {shortcut!.CommandView.GetType ().Name}");
+                    eventLog.MoveDown ();
+                    args.Handled = true;
+                };
 
                 shortcut.CommandView.Select += (o, args) =>
-                                               {
-                                                   eventSource.Add ($"CommandView.Select: {shortcut!.CommandView.Text} {shortcut!.CommandView.GetType ().Name}");
-                                                   eventLog.MoveDown ();
-                                               };
+                {
+                    eventSource.Add ($"{shortcut!.Id}.CommandView.Select: {shortcut!.CommandView.Text} {shortcut!.CommandView.GetType ().Name}");
+                    eventLog.MoveDown ();
+                };
 
                 shortcut.Accept += (o, args) =>
-                                       {
-                                           eventSource.Add ($"Accept: {shortcut!.CommandView.Text} {shortcut!.CommandView.GetType ().Name}");
-                                           eventLog.MoveDown ();
-                                           args.Handled = true;
-                                       };
+                {
+                    eventSource.Add ($"{shortcut!.Id}.Accept: {shortcut!.CommandView.Text} {shortcut!.CommandView.GetType ().Name}");
+                    eventLog.MoveDown ();
+                    args.Handled = true;
+                };
 
                 shortcut.CommandView.Accept += (o, args) =>
-                                               {
-                                                   eventSource.Add ($"CommandView.Accept: {shortcut!.CommandView.Text} {shortcut!.CommandView.GetType ().Name}");
-                                                   eventLog.MoveDown ();
-                                               };
+                {
+                    eventSource.Add ($"{shortcut!.Id}.CommandView.Accept: {shortcut!.CommandView.Text} {shortcut!.CommandView.GetType ().Name}");
+                    eventLog.MoveDown ();
+                };
             }
         }
 
@@ -380,6 +388,7 @@ public class Shortcuts : Scenario
     private void Button_Clicked (object sender, HandledEventArgs e)
     {
         e.Handled = true;
-        MessageBox.Query ("Hi", $"You clicked {sender}");
+        View view = sender as View;
+        MessageBox.Query ("Hi", $"You clicked {view!.Text}", "_Ok");
     }
 }

+ 5 - 9
UnitTests/Input/KeyBindingTests.cs

@@ -103,7 +103,9 @@ public class KeyBindingTests
     public void Defaults ()
     {
         var keyBindings = new KeyBindings ();
-        Assert.Throws<InvalidOperationException> (() => keyBindings.GetKeyFromCommands (Command.Accept));
+        Assert.Empty (keyBindings.Bindings);
+        Assert.Null (keyBindings.GetKeyFromCommands (Command.Accept));
+        Assert.Null (keyBindings.BoundView);
     }
 
     [Fact]
@@ -173,9 +175,6 @@ public class KeyBindingTests
 
         key = keyBindings.GetKeyFromCommands (commands2);
         Assert.Equal (Key.B, key);
-
-        // Negative case
-        Assert.Throws<InvalidOperationException> (() => key = keyBindings.GetKeyFromCommands (Command.RightEnd));
     }
 
     [Fact]
@@ -186,17 +185,14 @@ public class KeyBindingTests
 
         Key key = keyBindings.GetKeyFromCommands (Command.Right);
         Assert.Equal (Key.A, key);
-
-        // Negative case
-        Assert.Throws<InvalidOperationException> (() => key = keyBindings.GetKeyFromCommands (Command.Left));
     }
 
     // GetKeyFromCommands
     [Fact]
-    public void GetKeyFromCommands_Unknown_Throws_InvalidOperationException ()
+    public void GetKeyFromCommands_Unknown_Returns_Key_Empty ()
     {
         var keyBindings = new KeyBindings ();
-        Assert.Throws<InvalidOperationException> (() => keyBindings.GetKeyFromCommands (Command.Accept));
+        Assert.Null (keyBindings.GetKeyFromCommands (Command.Accept));
     }
 
     [Fact]

+ 45 - 26
UnitTests/TestHelpers.cs

@@ -48,7 +48,7 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
         bool useFakeClipboard = true,
         bool fakeClipboardAlwaysThrowsNotSupportedException = false,
         bool fakeClipboardIsSupportedAlwaysTrue = false,
-        ConfigurationManager.ConfigLocations configLocation = ConfigurationManager.ConfigLocations.None
+        ConfigLocations configLocation = ConfigLocations.None
     )
     {
         AutoInit = autoInit;
@@ -59,7 +59,7 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
         FakeDriver.FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException =
             fakeClipboardAlwaysThrowsNotSupportedException;
         FakeDriver.FakeBehaviors.FakeClipboardIsSupportedAlwaysFalse = fakeClipboardIsSupportedAlwaysTrue;
-        ConfigurationManager.Locations = configLocation;
+        Locations = configLocation;
     }
 
     private readonly Type _driverType;
@@ -73,10 +73,8 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
 
         if (AutoInit)
         {
-            try
+            // try
             {
-                // TODO: This Dispose call is here until all unit tests that don't correctly dispose Toplevel's they create are fixed.
-                //Application.Top?.Dispose ();
                 Application.Shutdown ();
 #if DEBUG_IDISPOSABLE
                 if (Responder.Instances.Count == 0)
@@ -89,22 +87,21 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
                 }
 #endif
             }
-            catch (Exception e)
-            {
-                Assert.Fail ($"Application.Shutdown threw an exception after the test exited: {e}");
-            }
-            finally
+            //catch (Exception e)
+            //{
+            //    Assert.Fail ($"Application.Shutdown threw an exception after the test exited: {e}");
+            //}
+            //finally
             {
 #if DEBUG_IDISPOSABLE
                 Responder.Instances.Clear ();
-                Application.ResetState (ignoreDisposed: true);
+                Application.ResetState (true);
 #endif
-                ConfigurationManager.Reset ();
-                ConfigurationManager.Locations = CM.ConfigLocations.None;
             }
-
-
         }
+
+        // Enable subsequent tests that call Init to get all config files (the default).
+        Locations = ConfigLocations.All;
     }
 
     public override void Before (MethodInfo methodUnderTest)
@@ -113,8 +110,6 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
 
         if (AutoInit)
         {
-            ConfigurationManager.Reset ();
-
 #if DEBUG_IDISPOSABLE
 
             // Clear out any lingering Responder instances from previous tests
@@ -134,10 +129,15 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute
     private bool AutoInit { get; }
 
     private List<object> _savedValues;
-
-   
 }
 
+/// <summary>
+///     Enables test functions annotated with the [TestRespondersDisposed] attribute to ensure all Views are disposed.
+/// </summary>
+/// <remarks>
+///     On Before, sets Configuration.Locations to ConfigLocations.DefaultOnly.
+///     On After, sets Configuration.Locations to ConfigLocations.All.
+/// </remarks>
 [AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)]
 public class TestRespondersDisposed : BeforeAfterTestAttribute
 {
@@ -148,6 +148,9 @@ public class TestRespondersDisposed : BeforeAfterTestAttribute
         Debug.WriteLine ($"After: {methodUnderTest.Name}");
         base.After (methodUnderTest);
 
+        // Reset the to default All
+        Locations = ConfigLocations.All;
+
 #if DEBUG_IDISPOSABLE
         Assert.Empty (Responder.Instances);
 #endif
@@ -156,6 +159,11 @@ public class TestRespondersDisposed : BeforeAfterTestAttribute
     public override void Before (MethodInfo methodUnderTest)
     {
         Debug.WriteLine ($"Before: {methodUnderTest.Name}");
+
+        // Force the use of the default config file
+        Locations = ConfigLocations.DefaultOnly;
+        Reset ();
+
         base.Before (methodUnderTest);
 #if DEBUG_IDISPOSABLE
 
@@ -167,15 +175,17 @@ public class TestRespondersDisposed : BeforeAfterTestAttribute
 }
 
 // TODO: Make this inherit from TestRespondersDisposed so that all tests that don't dispose Views correctly can be identified and fixed
+/// <summary>
+///     Enables test functions annotated with the [SetupFakeDriver] attribute to set Application.Driver to new
+///     FakeDriver(). The driver is set up with 25 rows and columns.
+/// </summary>
+/// <remarks>
+///     On Before, sets Configuration.Locations to ConfigLocations.DefaultOnly.
+///     On After, sets Configuration.Locations to ConfigLocations.All.
+/// </remarks>
 [AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)]
 public class SetupFakeDriverAttribute : BeforeAfterTestAttribute
 {
-    /// <summary>
-    ///     Enables test functions annotated with the [SetupFakeDriver] attribute to set Application.Driver to new
-    ///     FakeDriver(). The driver is setup with 25 rows and columns.
-    /// </summary>
-    public SetupFakeDriverAttribute () { }
-
     public override void After (MethodInfo methodUnderTest)
     {
         Debug.WriteLine ($"After: {methodUnderTest.Name}");
@@ -185,16 +195,25 @@ public class SetupFakeDriverAttribute : BeforeAfterTestAttribute
 
         Application.Driver = null;
         base.After (methodUnderTest);
+
+        // Reset the to default All
+        Locations = ConfigLocations.All;
     }
 
     public override void Before (MethodInfo methodUnderTest)
     {
         Debug.WriteLine ($"Before: {methodUnderTest.Name}");
-        Application.ResetState (ignoreDisposed: true);
+
+        // Force the use of the default config file
+        Locations = ConfigLocations.DefaultOnly;
+        Reset ();
+
+        Application.ResetState (true);
         Assert.Null (Application.Driver);
         Application.Driver = new FakeDriver { Rows = 25, Cols = 25 };
 
         base.Before (methodUnderTest);
+
     }
 }
 

+ 119 - 92
UnitTests/UICatalog/ScenarioTests.cs

@@ -30,6 +30,10 @@ public class ScenarioTests : TestsAllViews
         Assert.Null (_timeoutLock);
         _timeoutLock = new ();
 
+        // Disable any UIConfig settings
+        ConfigurationManager.ConfigLocations savedConfigLocations = ConfigurationManager.Locations;
+        ConfigurationManager.Locations = ConfigurationManager.ConfigLocations.DefaultOnly;
+
         // If a previous test failed, this will ensure that the Application is in a clean state
         Application.ResetState (true);
 
@@ -72,6 +76,9 @@ public class ScenarioTests : TestsAllViews
             _timeoutLock = null;
         }
 
+        // Restore the configuration locations
+        ConfigurationManager.Locations = savedConfigLocations;
+        ConfigurationManager.Reset ();
         return;
 
         void OnApplicationOnInitializedChanged (object s, EventArgs<bool> a)
@@ -109,6 +116,10 @@ public class ScenarioTests : TestsAllViews
             Assert.Fail (
                          $"'{scenario.GetName ()}' failed to Quit with {Application.QuitKey} after {abortTime}ms and {iterationCount} iterations. Force quit.");
 
+            // Restore the configuration locations
+            ConfigurationManager.Locations = savedConfigLocations;
+            ConfigurationManager.Reset ();
+
             Application.ResetState (true);
 
             return false;
@@ -135,6 +146,10 @@ public class ScenarioTests : TestsAllViews
     [Fact]
     public void Run_All_Views_Tester_Scenario ()
     {
+        // Disable any UIConfig settings
+        ConfigurationManager.ConfigLocations savedConfigLocations = ConfigurationManager.Locations;
+        ConfigurationManager.Locations = ConfigurationManager.ConfigLocations.DefaultOnly;
+
         Window _leftPane;
         ListView _classListView;
         FrameView _hostPane;
@@ -270,69 +285,69 @@ public class ScenarioTests : TestsAllViews
         _classListView.OpenSelectedItem += (s, a) => { _settingsPane.SetFocus (); };
 
         _classListView.SelectedItemChanged += (s, args) =>
-                                              {
-                                                  // Remove existing class, if any
-                                                  if (_curView != null)
-                                                  {
-                                                      _curView.LayoutComplete -= LayoutCompleteHandler;
-                                                      _hostPane.Remove (_curView);
-                                                      _curView.Dispose ();
-                                                      _curView = null;
-                                                      _hostPane.FillRect (_hostPane.Viewport);
-                                                  }
-
-                                                  _curView = CreateClass (_viewClasses.Values.ToArray () [_classListView.SelectedItem]);
-                                              };
+        {
+            // Remove existing class, if any
+            if (_curView != null)
+            {
+                _curView.LayoutComplete -= LayoutCompleteHandler;
+                _hostPane.Remove (_curView);
+                _curView.Dispose ();
+                _curView = null;
+                _hostPane.FillRect (_hostPane.Viewport);
+            }
+
+            _curView = CreateClass (_viewClasses.Values.ToArray () [_classListView.SelectedItem]);
+        };
 
         _xRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
 
         _xText.TextChanged += (s, args) =>
-                              {
-                                  try
-                                  {
-                                      _xVal = int.Parse (_xText.Text);
-                                      DimPosChanged (_curView);
-                                  }
-                                  catch
-                                  { }
-                              };
+        {
+            try
+            {
+                _xVal = int.Parse (_xText.Text);
+                DimPosChanged (_curView);
+            }
+            catch
+            { }
+        };
 
         _yText.TextChanged += (s, e) =>
-                              {
-                                  try
-                                  {
-                                      _yVal = int.Parse (_yText.Text);
-                                      DimPosChanged (_curView);
-                                  }
-                                  catch
-                                  { }
-                              };
+        {
+            try
+            {
+                _yVal = int.Parse (_yText.Text);
+                DimPosChanged (_curView);
+            }
+            catch
+            { }
+        };
 
         _yRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
 
         _wRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
 
         _wText.TextChanged += (s, args) =>
-                              {
-                                  try
-                                  {
-                                      _wVal = int.Parse (_wText.Text);
-                                      DimPosChanged (_curView);
-                                  }
-                                  catch
-                                  { }
-                              };
+        {
+            try
+            {
+                _wVal = int.Parse (_wText.Text);
+                DimPosChanged (_curView);
+            }
+            catch
+            { }
+        };
 
         _hText.TextChanged += (s, args) =>
-                              {
-                                  try
-                                  {
-                                      _hVal = int.Parse (_hText.Text);
-                                      DimPosChanged (_curView);
-                                  }
-                                  catch
-                                  { }
-                              };
+        {
+            try
+            {
+                _hVal = int.Parse (_hText.Text);
+                DimPosChanged (_curView);
+            }
+            catch
+            { }
+        };
 
         _hRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView);
 
@@ -345,23 +360,23 @@ public class ScenarioTests : TestsAllViews
         var iterations = 0;
 
         Application.Iteration += (s, a) =>
-                                 {
-                                     iterations++;
-
-                                     if (iterations < _viewClasses.Count)
-                                     {
-                                         _classListView.MoveDown ();
-
-                                         Assert.Equal (
-                                                       _curView.GetType ().Name,
-                                                       _viewClasses.Values.ToArray () [_classListView.SelectedItem].Name
-                                                      );
-                                     }
-                                     else
-                                     {
-                                         Application.RequestStop ();
-                                     }
-                                 };
+        {
+            iterations++;
+
+            if (iterations < _viewClasses.Count)
+            {
+                _classListView.MoveDown ();
+
+                Assert.Equal (
+                              _curView.GetType ().Name,
+                              _viewClasses.Values.ToArray () [_classListView.SelectedItem].Name
+                             );
+            }
+            else
+            {
+                Application.RequestStop ();
+            }
+        };
 
         Application.Run (top);
 
@@ -370,6 +385,10 @@ public class ScenarioTests : TestsAllViews
         top.Dispose ();
         Application.Shutdown ();
 
+        // Restore the configuration locations
+        ConfigurationManager.Locations = savedConfigLocations;
+        ConfigurationManager.Reset ();
+
         void DimPosChanged (View view)
         {
             if (view == null)
@@ -586,6 +605,10 @@ public class ScenarioTests : TestsAllViews
     [Fact]
     public void Run_Generic ()
     {
+        // Disable any UIConfig settings
+        ConfigurationManager.ConfigLocations savedConfigLocations = ConfigurationManager.Locations;
+        ConfigurationManager.Locations = ConfigurationManager.ConfigLocations.DefaultOnly;
+
         ObservableCollection<Scenario> scenarios = Scenario.GetScenarios ();
         Assert.NotEmpty (scenarios);
 
@@ -603,40 +626,40 @@ public class ScenarioTests : TestsAllViews
         var abortCount = 0;
 
         Func<bool> abortCallback = () =>
-                                   {
-                                       abortCount++;
-                                       _output.WriteLine ($"'Generic' abortCount {abortCount}");
-                                       Application.RequestStop ();
+        {
+            abortCount++;
+            _output.WriteLine ($"'Generic' abortCount {abortCount}");
+            Application.RequestStop ();
 
-                                       return false;
-                                   };
+            return false;
+        };
 
         var iterations = 0;
         object token = null;
 
         Application.Iteration += (s, a) =>
-                                 {
-                                     if (token == null)
-                                     {
-                                         // Timeout only must start at first iteration
-                                         token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (ms), abortCallback);
-                                     }
-
-                                     iterations++;
-                                     _output.WriteLine ($"'Generic' iteration {iterations}");
-
-                                     // Stop if we run out of control...
-                                     if (iterations == 10)
-                                     {
-                                         _output.WriteLine ("'Generic' had to be force quit!");
-                                         Application.RequestStop ();
-                                     }
-                                 };
+        {
+            if (token == null)
+            {
+                // Timeout only must start at first iteration
+                token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (ms), abortCallback);
+            }
+
+            iterations++;
+            _output.WriteLine ($"'Generic' iteration {iterations}");
+
+            // Stop if we run out of control...
+            if (iterations == 10)
+            {
+                _output.WriteLine ("'Generic' had to be force quit!");
+                Application.RequestStop ();
+            }
+        };
 
         Application.KeyDown += (sender, args) =>
-                               {
-                                   Assert.Equal (Application.QuitKey, args.KeyCode);
-                               };
+        {
+            Assert.Equal (Application.QuitKey, args.KeyCode);
+        };
 
         generic.Main ();
 
@@ -650,6 +673,10 @@ public class ScenarioTests : TestsAllViews
         // Shutdown must be called to safely clean up Application if Init has been called
         Application.Shutdown ();
 
+        // Restore the configuration locations
+        ConfigurationManager.Locations = savedConfigLocations;
+        ConfigurationManager.Reset ();
+
 #if DEBUG_IDISPOSABLE
         Assert.Empty (Responder.Instances);
 #endif

+ 13 - 10
UnitTests/View/Navigation/NavigationTests.cs

@@ -129,21 +129,24 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
         var hasFocusFalse = 0;
 
         view.HasFocusChanged += (s, e) =>
-                                {
-                                    if (e.NewValue)
-                                    {
-                                        hasFocusTrue++;
-                                    }
-                                    else
-                                    {
-                                        hasFocusFalse++;
-                                    }
-                                };
+        {
+            if (e.NewValue)
+            {
+                hasFocusTrue++;
+            }
+            else
+            {
+                hasFocusFalse++;
+            }
+        };
 
         top.Add (view, otherView);
         Assert.False (view.HasFocus);
         Assert.False (otherView.HasFocus);
 
+        // Ensure the view is Visible
+        view.Visible = true;
+
         Application.Top.SetFocus ();
         Assert.True (Application.Top!.HasFocus);
         Assert.True (top.HasFocus);