소스 검색

Good progress. Tons of primitive tests. still pretty broken

Tig 11 달 전
부모
커밋
b31caea3f6

+ 27 - 8
Terminal.Gui/Application/Application.Keyboard.cs

@@ -332,9 +332,12 @@ public static partial class Application // Keyboard handling
                     Command.NextView,
                     Command.NextView,
                     () =>
                     () =>
                     {
                     {
-                        ApplicationNavigation.MoveNextView ();
-
-                        return true;
+                        View? current = Application.Current;
+                        if (current is {})
+                        {
+                            return current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
+                        }
+                        return false;
                     }
                     }
                    );
                    );
 
 
@@ -342,9 +345,12 @@ public static partial class Application // Keyboard handling
                     Command.PreviousView,
                     Command.PreviousView,
                     () =>
                     () =>
                     {
                     {
-                        ApplicationNavigation.MovePreviousView ();
-
-                        return true;
+                        View? current = Application.Current;
+                        if (current is { })
+                        {
+                            return current.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop);
+                        }
+                        return false;
                     }
                     }
                    );
                    );
 
 
@@ -352,9 +358,22 @@ public static partial class Application // Keyboard handling
                     Command.NextViewOrTop,
                     Command.NextViewOrTop,
                     () =>
                     () =>
                     {
                     {
-                        ApplicationNavigation.MoveNextViewOrTop ();
+                        if (ApplicationOverlapped.OverlappedTop is null)
+                        {
+                            View? current = Application.Current;
+                            if (current is { })
+                            {
+                                return current.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);
+                            }
+                        }
+                        else
+                        {
+                            ApplicationOverlapped.OverlappedMoveNext ();
 
 
-                        return true;
+                            return true;
+                        }
+
+                        return false;
                     }
                     }
                    );
                    );
 
 

+ 3 - 99
Terminal.Gui/Application/ApplicationNavigation.cs

@@ -107,108 +107,12 @@ public class ApplicationNavigation
 
 
         return view;
         return view;
     }
     }
+    
 
 
-    /// <summary>
-    ///     Moves the focus to the next focusable view.
-    ///     Honors <see cref="ViewArrangement.Overlapped"/> and will only move to the next subview
-    ///     if the current and next subviews are not overlapped.
-    /// </summary>
-    internal static void MoveNextView ()
-    {
-        View? old = GetDeepestFocusedSubview (Application.Current!.GetFocused ());
-
-        if (!Application.Current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop))
-        {
-            Application.Current.AdvanceFocus (NavigationDirection.Forward, null);
-        }
-
-        if (old != Application.Current.GetFocused () && old != Application.Current.GetFocused ()?.GetFocused ())
-        {
-            old?.SetNeedsDisplay ();
-            Application.Current.GetFocused ()?.SetNeedsDisplay ();
-        }
-        else
-        {
-            ApplicationOverlapped.SetFocusToNextViewWithWrap (Application.Current.SuperView?.TabIndexes, NavigationDirection.Forward);
-        }
-    }
-
-    /// <summary>
-    ///     Moves the focus to the next <see cref="Toplevel"/> subview or the next subview that has
-    ///     <see cref="ApplicationOverlapped.OverlappedTop"/> set.
-    /// </summary>
-    internal static void MoveNextViewOrTop ()
-    {
-        if (ApplicationOverlapped.OverlappedTop is null)
-        {
-            Toplevel? top = Application.Current!.Modal ? Application.Current : Application.Top;
-
-            if (!Application.Current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup))
-            {
-                Application.Current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
-
-                if (Application.Current.GetFocused () is null)
-                {
-                    Application.Current.RestoreFocus (TabBehavior.TabGroup);
-                }
-            }
-
-            if (top != Application.Current.GetFocused() && top != Application.Current.GetFocused ()?.GetFocused ())
-            {
-                top?.SetNeedsDisplay ();
-                Application.Current.GetFocused ()?.SetNeedsDisplay ();
-            }
-            else
-            {
-                ApplicationOverlapped.SetFocusToNextViewWithWrap (Application.Current.SuperView?.TabIndexes, NavigationDirection.Forward);
-            }
-
-            //top!.AdvanceFocus (NavigationDirection.Forward);
-
-            //if (top.GetFocused () is null)
-            //{
-            //    top.AdvanceFocus (NavigationDirection.Forward);
-            //}
-
-            //top.SetNeedsDisplay ();
-            ApplicationOverlapped.BringOverlappedTopToFront ();
-        }
-        else
-        {
-            ApplicationOverlapped.OverlappedMoveNext ();
-        }
-    }
-
-    // TODO: These methods should return bool to indicate if the focus was moved or not.
-
-    /// <summary>
-    ///     Moves the focus to the next view. Honors <see cref="ViewArrangement.Overlapped"/> and will only move to the next
-    ///     subview
-    ///     if the current and next subviews are not overlapped.
-    /// </summary>
-    internal static void MovePreviousView ()
-    {
-        View? old = GetDeepestFocusedSubview (Application.Current!.GetFocused());
-
-        if (!Application.Current.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop))
-        {
-            Application.Current.AdvanceFocus (NavigationDirection.Backward, null);
-        }
-
-        if (old != Application.Current.GetFocused() && old != Application.Current.GetFocused()?.GetFocused())
-        {
-            old?.SetNeedsDisplay ();
-            Application.Current.GetFocused ()?.SetNeedsDisplay ();
-        }
-        else
-        {
-            ApplicationOverlapped.SetFocusToNextViewWithWrap (Application.Current.SuperView?.TabIndexes?.Reverse (), NavigationDirection.Backward);
-        }
-    }
-
+    
     internal static void MovePreviousViewOrTop ()
     internal static void MovePreviousViewOrTop ()
     {
     {
-        if (ApplicationOverlapped.OverlappedTop is null)    
+        if (ApplicationOverlapped.OverlappedTop is null)
         {
         {
             Toplevel? top = Application.Current!.Modal ? Application.Current : Application.Top;
             Toplevel? top = Application.Current!.Modal ? Application.Current : Application.Top;
             top!.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);
             top!.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);

+ 27 - 31
Terminal.Gui/View/View.Navigation.cs

@@ -121,7 +121,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
             }
             }
 
 
             // Couldn't restore focus, so use Advance to navigate to the next focusable subview
             // Couldn't restore focus, so use Advance to navigate to the next focusable subview
-            if (AdvanceFocus (NavigationDirection.Forward, TabStop))
+            if (AdvanceFocus (NavigationDirection.Forward, null))
             {
             {
                 // A subview was focused. We're done because the subview has focus and it recursed up the superview hierarchy.
                 // A subview was focused. We're done because the subview has focus and it recursed up the superview hierarchy.
                 return true;
                 return true;
@@ -159,7 +159,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
         _hasFocus = true;
         _hasFocus = true;
 
 
         // Ensure that the peer loses focus
         // Ensure that the peer loses focus
-        focusedPeer?.LeaveFocus (this);
+        focusedPeer?.LeaveFocus (this, true);
 
 
         // We're the most focused view in the application, we need to set the focused view to this view.
         // We're the most focused view in the application, we need to set the focused view to this view.
         Application.Navigation?.SetFocused (this);
         Application.Navigation?.SetFocused (this);
@@ -226,32 +226,40 @@ public partial class View // Focus and cross-view navigation management (TabStop
         }
         }
 
 
         // If enteringView is null, we need to find the view that should get focus, and SetFocus on it.
         // If enteringView is null, we need to find the view that should get focus, and SetFocus on it.
-        if (enteringView is null && SuperView is { })
+        if (!traversingDown && enteringView is null)
         {
         {
-            if (SuperView?.PreviouslyMostFocused is { } && SuperView?.PreviouslyMostFocused != this)
+            if (SuperView?._previouslyMostFocused is { } && SuperView?._previouslyMostFocused != this)
             {
             {
-                SuperView?.PreviouslyMostFocused?.SetFocus ();
+                SuperView?._previouslyMostFocused?.SetFocus ();
 
 
                 // The above will cause LeaveFocus, so we can return
                 // The above will cause LeaveFocus, so we can return
                 return;
                 return;
             }
             }
 
 
-            if (Application.Navigation is { })
+            if (SuperView is {} && SuperView.AdvanceFocus (NavigationDirection.Forward, TabStop))
             {
             {
-                // Temporarily ensure this view can't get focus
-                bool prevCanFocus = _canFocus;
-                _canFocus = false;
-                ApplicationNavigation.MoveNextView ();
-                _canFocus = prevCanFocus;
-
                 // The above will cause LeaveFocus, so we can return
                 // The above will cause LeaveFocus, so we can return
                 return;
                 return;
             }
             }
+
+            //if (Application.Navigation is { })
+            //{
+            //    // Temporarily ensure this view can't get focus
+            //    bool prevCanFocus = _canFocus;
+            //    _canFocus = false;
+            //    ApplicationNavigation.MoveNextView ();
+            //    _canFocus = prevCanFocus;
+
+            //    // The above will cause LeaveFocus, so we can return
+            //    return;
+            //}
+
+            // No other focusable view to be found. Just "leave" us...
         }
         }
 
 
         // Before we can leave focus, we need to make sure that all views down the subview-hierarchy have left focus.
         // Before we can leave focus, we need to make sure that all views down the subview-hierarchy have left focus.
         View mostFocused = GetMostFocused ();
         View mostFocused = GetMostFocused ();
-        if (mostFocused is { })
+        if (mostFocused is { } && (enteringView is null || mostFocused != enteringView))
         {
         {
             // Start at the bottom and work our way up to us
             // Start at the bottom and work our way up to us
             View bottom = mostFocused;
             View bottom = mostFocused;
@@ -261,13 +269,10 @@ public partial class View // Focus and cross-view navigation management (TabStop
                 if (bottom.HasFocus)
                 if (bottom.HasFocus)
                 {
                 {
                     bottom.LeaveFocus (enteringView, true);
                     bottom.LeaveFocus (enteringView, true);
-
-                    break;
                 }
                 }
                 bottom = bottom.SuperView;
                 bottom = bottom.SuperView;
             }
             }
-
-            PreviouslyMostFocused = mostFocused;
+            _previouslyMostFocused = mostFocused;
         }
         }
 
 
         bool previousValue = HasFocus;
         bool previousValue = HasFocus;
@@ -282,7 +287,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
         View focusedPeer = SuperView?.GetFocused ();
         View focusedPeer = SuperView?.GetFocused ();
         _hasFocus = false;
         _hasFocus = false;
 
 
-        if (!traversingDown)
+        if (!traversingDown && CanFocus && Visible && Enabled)
         {
         {
             // Now ensure all views up the superview-hierarchy are unfocused
             // Now ensure all views up the superview-hierarchy are unfocused
             if (SuperView is { HasFocus: true } && focusedPeer == this)
             if (SuperView is { HasFocus: true } && focusedPeer == this)
@@ -304,7 +309,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
     ///     Caches the most focused subview when this view is losing focus. This is used by <see cref="RestoreFocus"/>.
     ///     Caches the most focused subview when this view is losing focus. This is used by <see cref="RestoreFocus"/>.
     /// </summary>
     /// </summary>
     [CanBeNull]
     [CanBeNull]
-    internal View PreviouslyMostFocused { get; set; }
+    private View _previouslyMostFocused;
 
 
     /// <summary>Virtual method invoked when this view is losing focus (leaving).</summary>
     /// <summary>Virtual method invoked when this view is losing focus (leaving).</summary>
     /// <param name="enteringView">The view that is gaining focus.</param>
     /// <param name="enteringView">The view that is gaining focus.</param>
@@ -350,16 +355,8 @@ public partial class View // Focus and cross-view navigation management (TabStop
         }
         }
 
 
         View focused = GetFocused ();
         View focused = GetFocused ();
-        if (focused is null)
-        {
-            View deepest = FindDeepestFocusableView (behavior, direction);
-            if (deepest is { })
-            {
-                return deepest.SetFocus ();
-            }
-        }
 
 
-        if (focused!.AdvanceFocus (direction, behavior))
+        if (focused is {} && focused.AdvanceFocus (direction, behavior))
         {
         {
             return true;
             return true;
         }
         }
@@ -415,10 +412,9 @@ public partial class View // Focus and cross-view navigation management (TabStop
     {
     {
         if (GetFocused () is null && _subviews?.Count > 0)
         if (GetFocused () is null && _subviews?.Count > 0)
         {
         {
-            // TODO: Find the previous focused view and set focus to it
-            if (PreviouslyMostFocused is { } && PreviouslyMostFocused.TabStop == behavior)
+            if (_previouslyMostFocused is { }/* && (behavior is null || _previouslyMostFocused.TabStop == behavior)*/)
             {
             {
-                return PreviouslyMostFocused.SetFocus ();
+                return _previouslyMostFocused.SetFocus ();
             }
             }
             return false;
             return false;
         }
         }

+ 11 - 2
Terminal.Gui/View/View.cs

@@ -296,7 +296,7 @@ public partial class View : Responder, ISupportInitializeNotification
     private bool _oldEnabled;
     private bool _oldEnabled;
 
 
     /// <summary>Gets or sets a value indicating whether this <see cref="Responder"/> can respond to user interaction.</summary>
     /// <summary>Gets or sets a value indicating whether this <see cref="Responder"/> can respond to user interaction.</summary>
-    public virtual bool Enabled
+    public bool Enabled
     {
     {
         get => _enabled;
         get => _enabled;
         set
         set
@@ -313,6 +313,11 @@ public partial class View : Responder, ISupportInitializeNotification
                 HasFocus = false;
                 HasFocus = false;
             }
             }
 
 
+            if (_enabled && CanFocus && Visible && !HasFocus && SuperView?.GetFocused() == null)
+            {
+                SetFocus ();
+            }
+
             OnEnabledChanged ();
             OnEnabledChanged ();
             SetNeedsDisplay ();
             SetNeedsDisplay ();
 
 
@@ -366,13 +371,17 @@ public partial class View : Responder, ISupportInitializeNotification
                 {
                 {
                     HasFocus = false;
                     HasFocus = false;
                 }
                 }
-
                 if (IsInitialized && ClearOnVisibleFalse)
                 if (IsInitialized && ClearOnVisibleFalse)
                 {
                 {
                     Clear ();
                     Clear ();
                 }
                 }
             }
             }
 
 
+            if (_visible && CanFocus && Enabled && !HasFocus && SuperView?.GetFocused () == null)
+            {
+                SetFocus ();
+            }
+
             OnVisibleChanged ();
             OnVisibleChanged ();
             SetNeedsDisplay ();
             SetNeedsDisplay ();
         }
         }

+ 21 - 56
UnitTests/Application/Application.NavigationTests.cs

@@ -13,6 +13,8 @@ public class ApplicationNavigationTests (ITestOutputHelper output)
     {
     {
         bool raised = false;
         bool raised = false;
 
 
+        Application.Navigation = new ApplicationNavigation();
+
         Application.Navigation.FocusedChanged += ApplicationNavigationOnFocusedChanged;
         Application.Navigation.FocusedChanged += ApplicationNavigationOnFocusedChanged;
 
 
         Application.Navigation.SetFocused(new View ());
         Application.Navigation.SetFocused(new View ());
@@ -24,6 +26,8 @@ public class ApplicationNavigationTests (ITestOutputHelper output)
 
 
         Application.Navigation.FocusedChanged -= ApplicationNavigationOnFocusedChanged;
         Application.Navigation.FocusedChanged -= ApplicationNavigationOnFocusedChanged;
 
 
+        Application.Navigation = null;
+
         return;
         return;
 
 
         void ApplicationNavigationOnFocusedChanged (object sender, EventArgs e)
         void ApplicationNavigationOnFocusedChanged (object sender, EventArgs e)
@@ -109,68 +113,29 @@ public class ApplicationNavigationTests (ITestOutputHelper output)
         Assert.Equal (grandChildView, result);
         Assert.Equal (grandChildView, result);
     }
     }
 
 
-    [Fact]
-    public void MoveNextView_ShouldMoveFocusToNextView ()
-    {
-        // Arrange
-        var top = new Toplevel ();
-        var view1 = new View () { Id = "view1", CanFocus = true };
-        var view2 = new View () { Id = "view2", CanFocus = true };
-        top.Add (view1, view2);
-        Application.Top = top;
-        Application.Current = top;
-        view1.SetFocus ();
-
-        // Act
-        ApplicationNavigation.MoveNextView ();
-
-        // Assert
-        Assert.True (view2.HasFocus);
-
-        top.Dispose ();
-    }
-
-    [Fact]
-    public void MoveNextViewOrTop_ShouldMoveFocusToNextViewOrTop ()
-    {
-        // Arrange
-        var top = new Toplevel ();
-        var view1 = new View () { Id = "view1", CanFocus = true };
-        var view2 = new View () { Id = "view2", CanFocus = true };
-        top.Add (view1, view2);
-        Application.Top = top;
-        Application.Current = top;
-        view1.SetFocus ();
-
-        // Act
-        ApplicationNavigation.MoveNextViewOrTop ();
 
 
-        // Assert
-        Assert.True (view2.HasFocus);
+    //[Fact]
+    //public void MoveNextViewOrTop_ShouldMoveFocusToNextViewOrTop ()
+    //{
+    //    // Arrange
+    //    var top = new Toplevel ();
+    //    var view1 = new View () { Id = "view1", CanFocus = true };
+    //    var view2 = new View () { Id = "view2", CanFocus = true };
+    //    top.Add (view1, view2);
+    //    Application.Top = top;
+    //    Application.Current = top;
+    //    view1.SetFocus ();
 
 
-        top.Dispose ();
-    }
+    //    // Act
+    //    ApplicationNavigation.MoveNextViewOrTop ();
 
 
-    [Fact]
-    public void MovePreviousView_ShouldMoveFocusToPreviousView ()
-    {
-        // Arrange
-        var top = new Toplevel ();
-        var view1 = new View () { Id = "view1", CanFocus = true };
-        var view2 = new View () { Id = "view2", CanFocus = true };
-        top.Add (view1, view2);
-        Application.Top = top;
-        Application.Current = top;
-        view2.SetFocus ();
+    //    // Assert
+    //    Assert.True (view2.HasFocus);
 
 
-        // Act
-        ApplicationNavigation.MovePreviousView ();
+    //    top.Dispose ();
+    //}
 
 
-        // Assert
-        Assert.True (view1.HasFocus);
 
 
-        top.Dispose ();
-    }
 
 
     [Fact]
     [Fact]
     public void MovePreviousViewOrTop_ShouldMoveFocusToPreviousViewOrTop ()
     public void MovePreviousViewOrTop_ShouldMoveFocusToPreviousViewOrTop ()

+ 172 - 34
UnitTests/Application/KeyboardTests.cs

@@ -177,20 +177,177 @@ public class KeyboardTests
         }
         }
     }
     }
 
 
-    [Fact (Skip = "Replace when new key statics are added.")]
+
+    [Fact]
+    public void NextTabKey_Moves_Focus_To_Next_TabStop ()
+    {
+        // Arrange
+        Application.Navigation = new ApplicationNavigation ();
+        var top = new Toplevel ();
+        var view1 = new View () { Id = "view1", CanFocus = true };
+        var view2 = new View () { Id = "view2", CanFocus = true };
+        top.Add (view1, view2);
+        Application.Top = top;
+        Application.Current = top;
+        view1.SetFocus ();
+
+        // Act
+        Application.OnKeyDown (Application.NextTabKey);
+
+        // Assert
+        Assert.True (view2.HasFocus);
+
+        top.Dispose ();
+        Application.Navigation = null;
+    }
+
+    [Fact]
+    public void PrevTabKey_Moves_Focus_To_Prev_TabStop ()
+    {
+        // Arrange
+        Application.Navigation = new ApplicationNavigation ();
+        var top = new Toplevel ();
+        var view1 = new View () { Id = "view1", CanFocus = true };
+        var view2 = new View () { Id = "view2", CanFocus = true };
+        top.Add (view1, view2);
+        Application.Top = top;
+        Application.Current = top;
+        view1.SetFocus ();
+
+        // Act
+        Application.OnKeyDown (Application.NextTabKey);
+
+        // Assert
+        Assert.True (view2.HasFocus);
+
+        top.Dispose ();
+        Application.Navigation = null;
+    }
+
+
+
+    [Fact]
+    public void NextTabGroupKey_Moves_Focus_To_TabStop_In_Next_TabGroup ()
+    {
+        // Arrange
+        Application.Navigation = new ApplicationNavigation ();
+        var top = new Toplevel ();
+        var view1 = new View ()
+        {
+            Id = "view1", 
+            CanFocus = true, 
+            TabStop = TabBehavior.TabGroup
+        };
+
+        var subView1 = new View ()
+        {
+            Id = "subView1",
+            CanFocus = true,
+            TabStop = TabBehavior.TabStop
+        };
+
+        view1.Add (subView1);
+
+        var view2 = new View ()
+        {
+            Id = "view2", 
+            CanFocus = true, 
+            TabStop = TabBehavior.TabGroup
+        };
+        var subView2 = new View ()
+        {
+            Id = "subView2",
+            CanFocus = true,
+            TabStop = TabBehavior.TabStop
+        };
+        view2.Add (subView2);
+
+        top.Add (view1, view2);
+        Application.Top = top;
+        Application.Current = top;
+        view1.SetFocus ();
+        Assert.True (view1.HasFocus);
+        Assert.True (subView1.HasFocus);
+
+        // Act
+        Application.OnKeyDown (Application.NextTabGroupKey);
+
+        // Assert
+        Assert.True (view2.HasFocus);
+        Assert.True (subView2.HasFocus);
+
+        top.Dispose ();
+        Application.Navigation = null;
+    }
+
+    [Fact]
+    public void PrevTabGroupKey_Moves_Focus_To_TabStop_In_Prev_TabGroup ()
+    {
+        // Arrange
+        Application.Navigation = new ApplicationNavigation ();
+        var top = new Toplevel ();
+        var view1 = new View ()
+        {
+            Id = "view1",
+            CanFocus = true,
+            TabStop = TabBehavior.TabGroup
+        };
+
+        var subView1 = new View ()
+        {
+            Id = "subView1",
+            CanFocus = true,
+            TabStop = TabBehavior.TabStop
+        };
+
+        view1.Add (subView1);
+
+        var view2 = new View ()
+        {
+            Id = "view2",
+            CanFocus = true,
+            TabStop = TabBehavior.TabGroup
+        };
+        var subView2 = new View ()
+        {
+            Id = "subView2",
+            CanFocus = true,
+            TabStop = TabBehavior.TabStop
+        };
+        view2.Add (subView2);
+
+        top.Add (view1, view2);
+        Application.Top = top;
+        Application.Current = top;
+        view1.SetFocus ();
+        Assert.True (view1.HasFocus);
+        Assert.True (subView1.HasFocus);
+
+        // Act
+        Application.OnKeyDown (Application.PrevTabGroupKey);
+
+        // Assert
+        Assert.True (view2.HasFocus);
+        Assert.True (subView2.HasFocus);
+
+        top.Dispose ();
+        Application.Navigation = null;
+    }
+
+    [Fact]
     public void NextTabGroupKey_PrevTabGroupKey_Tests ()
     public void NextTabGroupKey_PrevTabGroupKey_Tests ()
     {
     {
         Application.Init (new FakeDriver ());
         Application.Init (new FakeDriver ());
 
 
-        Toplevel top = new ();
-        var w1 = new Window ();
-        var v1 = new TextField ();
-        var v2 = new TextView ();
+        Toplevel top = new (); // TabGroup
+        var w1 = new Window (); // TabGroup
+        var v1 = new TextField (); // TabStop
+        var v2 = new TextView (); // TabStop
         w1.Add (v1, v2);
         w1.Add (v1, v2);
 
 
-        var w2 = new Window ();
-        var v3 = new CheckBox ();
-        var v4 = new Button ();
+        var w2 = new Window (); // TabGroup
+        var v3 = new CheckBox (); // TabStop
+        var v4 = new Button (); // TabStop
         w2.Add (v3, v4);
         w2.Add (v3, v4);
 
 
         top.Add (w1, w2);
         top.Add (w1, w2);
@@ -199,47 +356,28 @@ public class KeyboardTests
                                  {
                                  {
                                      Assert.True (v1.HasFocus);
                                      Assert.True (v1.HasFocus);
 
 
-                                     // Using default keys.
-                                     Application.OnKeyDown (Key.F6);
-                                     Assert.True (v2.HasFocus);
+                                     // Across TabGroups
                                      Application.OnKeyDown (Key.F6);
                                      Application.OnKeyDown (Key.F6);
                                      Assert.True (v3.HasFocus);
                                      Assert.True (v3.HasFocus);
                                      Application.OnKeyDown (Key.F6);
                                      Application.OnKeyDown (Key.F6);
-                                     Assert.True (v4.HasFocus);
-                                     Application.OnKeyDown (Key.F6);
                                      Assert.True (v1.HasFocus);
                                      Assert.True (v1.HasFocus);
 
 
-                                     Application.OnKeyDown (Key.F6.WithShift);
-                                     Assert.True (v4.HasFocus);
                                      Application.OnKeyDown (Key.F6.WithShift);
                                      Application.OnKeyDown (Key.F6.WithShift);
                                      Assert.True (v3.HasFocus);
                                      Assert.True (v3.HasFocus);
                                      Application.OnKeyDown (Key.F6.WithShift);
                                      Application.OnKeyDown (Key.F6.WithShift);
-                                     Assert.True (v2.HasFocus);
-                                     Application.OnKeyDown (Key.F6.WithShift);
                                      Assert.True (v1.HasFocus);
                                      Assert.True (v1.HasFocus);
-                                     
-                                     // Using alternate keys.
-                                     Application.NextTabGroupKey = Key.F7;
-                                     Application.PrevTabGroupKey = Key.F8;
 
 
-                                     Application.OnKeyDown (Key.F7);
+                                     // Restore?
+                                     Application.OnKeyDown (Key.Tab);
                                      Assert.True (v2.HasFocus);
                                      Assert.True (v2.HasFocus);
-                                     Application.OnKeyDown (Key.F7);
-                                     Assert.True (v3.HasFocus);
-                                     Application.OnKeyDown (Key.F7);
-                                     Assert.True (v4.HasFocus);
-                                     Application.OnKeyDown (Key.F7);
-                                     Assert.True (v1.HasFocus);
 
 
-                                     Application.OnKeyDown (Key.F8);
-                                     Assert.True (v4.HasFocus);
-                                     Application.OnKeyDown (Key.F8);
+                                     Application.OnKeyDown (Key.F6);
                                      Assert.True (v3.HasFocus);
                                      Assert.True (v3.HasFocus);
-                                     Application.OnKeyDown (Key.F8);
+
+                                     Application.OnKeyDown (Key.F6);
                                      Assert.True (v2.HasFocus);
                                      Assert.True (v2.HasFocus);
-                                     Application.OnKeyDown (Key.F8);
-                                     Assert.True (v1.HasFocus);
 
 
+  
                                      Application.RequestStop ();
                                      Application.RequestStop ();
                                  };
                                  };
 
 

+ 1 - 0
UnitTests/View/FindDeepestViewTests.cs

@@ -1,5 +1,6 @@
 
 
 #nullable enable
 #nullable enable
+using Microsoft.VisualStudio.TestPlatform.Utilities;
 using Xunit.Abstractions;
 using Xunit.Abstractions;
 
 
 namespace Terminal.Gui.ViewTests;
 namespace Terminal.Gui.ViewTests;

+ 290 - 0
UnitTests/View/Layout/ToScreenTests.cs

@@ -940,4 +940,294 @@ public class ToScreenTests (ITestOutputHelper output)
         // Assert
         // Assert
         Assert.Equal (expectedX, screen.X);
         Assert.Equal (expectedX, screen.X);
     }
     }
+
+
+    [Fact]
+    [AutoInitShutdown]
+    public void ScreenToView_ViewToScreen_FindDeepestView_Full_Top ()
+    {
+        Toplevel top = new ();
+        top.BorderStyle = LineStyle.Single;
+
+        var view = new View
+        {
+            X = 3,
+            Y = 2,
+            Width = 10,
+            Height = 1,
+            Text = "0123456789"
+        };
+        top.Add (view);
+
+        Application.Begin (top);
+
+        Assert.Equal (Application.Current, top);
+        Assert.Equal (new (0, 0, 80, 25), new Rectangle (0, 0, View.Driver.Cols, View.Driver.Rows));
+        Assert.Equal (new (0, 0, View.Driver.Cols, View.Driver.Rows), top.Frame);
+        Assert.Equal (new (0, 0, 80, 25), top.Frame);
+
+        ((FakeDriver)Application.Driver!).SetBufferSize (20, 10);
+        Assert.Equal (new (0, 0, View.Driver.Cols, View.Driver.Rows), top.Frame);
+        Assert.Equal (new (0, 0, 20, 10), top.Frame);
+
+        _ = TestHelpers.AssertDriverContentsWithFrameAre (
+                                                          @"
+┌──────────────────┐
+│                  │
+│                  │
+│   0123456789     │
+│                  │
+│                  │
+│                  │
+│                  │
+│                  │
+└──────────────────┘"
+        ,
+                                                          _output
+                                                         );
+
+        // top
+        Assert.Equal (Point.Empty, top.ScreenToFrame (new (0, 0)));
+        Point screen = top.Margin.ViewportToScreen (new Point (0, 0));
+        Assert.Equal (0, screen.X);
+        Assert.Equal (0, screen.Y);
+        screen = top.Border.ViewportToScreen (new Point (0, 0));
+        Assert.Equal (0, screen.X);
+        Assert.Equal (0, screen.Y);
+        screen = top.Padding.ViewportToScreen (new Point (0, 0));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = top.ViewportToScreen (new Point (0, 0));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = top.ViewportToScreen (new Point (-1, -1));
+        Assert.Equal (0, screen.X);
+        Assert.Equal (0, screen.Y);
+        var found = View.FindDeepestView (top, new (0, 0));
+        Assert.Equal (top.Border, found);
+
+        Assert.Equal (0, found.Frame.X);
+        Assert.Equal (0, found.Frame.Y);
+        Assert.Equal (new (3, 2), top.ScreenToFrame (new (3, 2)));
+        screen = top.ViewportToScreen (new Point (3, 2));
+        Assert.Equal (4, screen.X);
+        Assert.Equal (3, screen.Y);
+        found = View.FindDeepestView (top, new (screen.X, screen.Y));
+        Assert.Equal (view, found);
+
+        //Assert.Equal (0, found.FrameToScreen ().X);
+        //Assert.Equal (0, found.FrameToScreen ().Y);
+        found = View.FindDeepestView (top, new (3, 2));
+        Assert.Equal (top, found);
+
+        //Assert.Equal (3, found.FrameToScreen ().X);
+        //Assert.Equal (2, found.FrameToScreen ().Y);
+        Assert.Equal (new (13, 2), top.ScreenToFrame (new (13, 2)));
+        screen = top.ViewportToScreen (new Point (12, 2));
+        Assert.Equal (13, screen.X);
+        Assert.Equal (3, screen.Y);
+        found = View.FindDeepestView (top, new (screen.X, screen.Y));
+        Assert.Equal (view, found);
+
+        //Assert.Equal (9, found.FrameToScreen ().X);
+        //Assert.Equal (0, found.FrameToScreen ().Y);
+        screen = top.ViewportToScreen (new Point (13, 2));
+        Assert.Equal (14, screen.X);
+        Assert.Equal (3, screen.Y);
+        found = View.FindDeepestView (top, new (13, 2));
+        Assert.Equal (top, found);
+
+        //Assert.Equal (13, found.FrameToScreen ().X);
+        //Assert.Equal (2, found.FrameToScreen ().Y);
+        Assert.Equal (new (14, 3), top.ScreenToFrame (new (14, 3)));
+        screen = top.ViewportToScreen (new Point (14, 3));
+        Assert.Equal (15, screen.X);
+        Assert.Equal (4, screen.Y);
+        found = View.FindDeepestView (top, new (14, 3));
+        Assert.Equal (top, found);
+
+        //Assert.Equal (14, found.FrameToScreen ().X);
+        //Assert.Equal (3, found.FrameToScreen ().Y);
+
+        // view
+        Assert.Equal (new (-4, -3), view.ScreenToFrame (new (0, 0)));
+        screen = view.Margin.ViewportToScreen (new Point (-3, -2));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = view.Border.ViewportToScreen (new Point (-3, -2));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = view.Padding.ViewportToScreen (new Point (-3, -2));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = view.ViewportToScreen (new Point (-3, -2));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = view.ViewportToScreen (new Point (-4, -3));
+        Assert.Equal (0, screen.X);
+        Assert.Equal (0, screen.Y);
+        found = View.FindDeepestView (top, new (0, 0));
+        Assert.Equal (top.Border, found);
+
+        Assert.Equal (new (-1, -1), view.ScreenToFrame (new (3, 2)));
+        screen = view.ViewportToScreen (new Point (0, 0));
+        Assert.Equal (4, screen.X);
+        Assert.Equal (3, screen.Y);
+        found = View.FindDeepestView (top, new (4, 3));
+        Assert.Equal (view, found);
+
+        Assert.Equal (new (9, -1), view.ScreenToFrame (new (13, 2)));
+        screen = view.ViewportToScreen (new Point (10, 0));
+        Assert.Equal (14, screen.X);
+        Assert.Equal (3, screen.Y);
+        found = View.FindDeepestView (top, new (14, 3));
+        Assert.Equal (top, found);
+
+        Assert.Equal (new (10, 0), view.ScreenToFrame (new (14, 3)));
+        screen = view.ViewportToScreen (new Point (11, 1));
+        Assert.Equal (15, screen.X);
+        Assert.Equal (4, screen.Y);
+        found = View.FindDeepestView (top, new (15, 4));
+        Assert.Equal (top, found);
+        top.Dispose ();
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void ScreenToView_ViewToScreen_FindDeepestView_Smaller_Top ()
+    {
+        var top = new Toplevel
+        {
+            X = 3,
+            Y = 2,
+            Width = 20,
+            Height = 10,
+            BorderStyle = LineStyle.Single
+        };
+
+        var view = new View
+        {
+            X = 3,
+            Y = 2,
+            Width = 10,
+            Height = 1,
+            Text = "0123456789"
+        };
+        top.Add (view);
+
+        Application.Begin (top);
+
+        Assert.Equal (Application.Current, top);
+        Assert.Equal (new (0, 0, 80, 25), new Rectangle (0, 0, View.Driver.Cols, View.Driver.Rows));
+        Assert.NotEqual (new (0, 0, View.Driver.Cols, View.Driver.Rows), top.Frame);
+        Assert.Equal (new (3, 2, 20, 10), top.Frame);
+
+        ((FakeDriver)Application.Driver!).SetBufferSize (30, 20);
+        Assert.Equal (new (0, 0, 30, 20), new Rectangle (0, 0, View.Driver.Cols, View.Driver.Rows));
+        Assert.NotEqual (new (0, 0, View.Driver.Cols, View.Driver.Rows), top.Frame);
+        Assert.Equal (new (3, 2, 20, 10), top.Frame);
+
+        Rectangle frame = TestHelpers.AssertDriverContentsWithFrameAre (
+                                                                        @"
+   ┌──────────────────┐
+   │                  │
+   │                  │
+   │   0123456789     │
+   │                  │
+   │                  │
+   │                  │
+   │                  │
+   │                  │
+   └──────────────────┘"
+        ,
+                                                                        _output
+                                                                       );
+
+        // mean the output started at col 3 and line 2
+        // which result with a width of 23 and a height of 10 on the output
+        Assert.Equal (new (3, 2, 23, 10), frame);
+
+        // top
+        Assert.Equal (new (-3, -2), top.ScreenToFrame (new (0, 0)));
+        Point screen = top.Margin.ViewportToScreen (new Point (-3, -2));
+        Assert.Equal (0, screen.X);
+        Assert.Equal (0, screen.Y);
+        screen = top.Border.ViewportToScreen (new Point (-3, -2));
+        Assert.Equal (0, screen.X);
+        Assert.Equal (0, screen.Y);
+        screen = top.Padding.ViewportToScreen (new Point (-3, -2));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = top.ViewportToScreen (new Point (-3, -2));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = top.ViewportToScreen (new Point (-4, -3));
+        Assert.Equal (0, screen.X);
+        Assert.Equal (0, screen.Y);
+        var found = View.FindDeepestView (top, new (-4, -3));
+        Assert.Null (found);
+        Assert.Equal (Point.Empty, top.ScreenToFrame (new (3, 2)));
+        screen = top.ViewportToScreen (new Point (0, 0));
+        Assert.Equal (4, screen.X);
+        Assert.Equal (3, screen.Y);
+        Assert.Equal (top.Border, View.FindDeepestView (top, new (3, 2)));
+
+        //Assert.Equal (0, found.FrameToScreen ().X);
+        //Assert.Equal (0, found.FrameToScreen ().Y);
+        Assert.Equal (new (10, 0), top.ScreenToFrame (new (13, 2)));
+        screen = top.ViewportToScreen (new Point (10, 0));
+        Assert.Equal (14, screen.X);
+        Assert.Equal (3, screen.Y);
+        Assert.Equal (top.Border, View.FindDeepestView (top, new (13, 2)));
+
+        //Assert.Equal (10, found.FrameToScreen ().X);
+        //Assert.Equal (0, found.FrameToScreen ().Y);
+        Assert.Equal (new (11, 1), top.ScreenToFrame (new (14, 3)));
+        screen = top.ViewportToScreen (new Point (11, 1));
+        Assert.Equal (15, screen.X);
+        Assert.Equal (4, screen.Y);
+        Assert.Equal (top, View.FindDeepestView (top, new (14, 3)));
+
+        // view
+        Assert.Equal (new (-7, -5), view.ScreenToFrame (new (0, 0)));
+        screen = view.Margin.ViewportToScreen (new Point (-6, -4));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = view.Border.ViewportToScreen (new Point (-6, -4));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = view.Padding.ViewportToScreen (new Point (-6, -4));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        screen = view.ViewportToScreen (new Point (-6, -4));
+        Assert.Equal (1, screen.X);
+        Assert.Equal (1, screen.Y);
+        Assert.Null (View.FindDeepestView (top, new (1, 1)));
+        Assert.Equal (new (-4, -3), view.ScreenToFrame (new (3, 2)));
+        screen = view.ViewportToScreen (new Point (-3, -2));
+        Assert.Equal (4, screen.X);
+        Assert.Equal (3, screen.Y);
+        Assert.Equal (top, View.FindDeepestView (top, new (4, 3)));
+        Assert.Equal (new (-1, -1), view.ScreenToFrame (new (6, 4)));
+        screen = view.ViewportToScreen (new Point (0, 0));
+        Assert.Equal (7, screen.X);
+        Assert.Equal (5, screen.Y);
+        Assert.Equal (view, View.FindDeepestView (top, new (7, 5)));
+        Assert.Equal (new (6, -1), view.ScreenToFrame (new (13, 4)));
+        screen = view.ViewportToScreen (new Point (7, 0));
+        Assert.Equal (14, screen.X);
+        Assert.Equal (5, screen.Y);
+        Assert.Equal (view, View.FindDeepestView (top, new (14, 5)));
+        Assert.Equal (new (7, -2), view.ScreenToFrame (new (14, 3)));
+        screen = view.ViewportToScreen (new Point (8, -1));
+        Assert.Equal (15, screen.X);
+        Assert.Equal (4, screen.Y);
+        Assert.Equal (top, View.FindDeepestView (top, new (15, 4)));
+        Assert.Equal (new (16, -2), view.ScreenToFrame (new (23, 3)));
+        screen = view.ViewportToScreen (new Point (17, -1));
+        Assert.Equal (24, screen.X);
+        Assert.Equal (4, screen.Y);
+        Assert.Null (View.FindDeepestView (top, new (24, 4)));
+        top.Dispose ();
+    }
 }
 }

+ 1 - 1
UnitTests/View/Navigation/AdvanceFocusTests.cs

@@ -349,7 +349,7 @@ public class AdvanceFocusTests (ITestOutputHelper _output)
     }
     }
 
 
     [Fact]
     [Fact]
-    public void AdvacneFocus_NoStop_Prevents_Stop ()
+    public void AdvanceFocus_NoStop_Prevents_Stop ()
     {
     {
         var r = new View ();
         var r = new View ();
         var v1 = new View { CanFocus = true, TabStop = TabBehavior.NoStop };
         var v1 = new View { CanFocus = true, TabStop = TabBehavior.NoStop };

+ 317 - 0
UnitTests/View/Navigation/EnabledTests.cs

@@ -0,0 +1,317 @@
+using Xunit.Abstractions;
+
+namespace Terminal.Gui.ViewTests;
+
+public class EnabledTests (ITestOutputHelper _output) : TestsAllViews
+{
+    [Fact]
+    public void Enabled_False_Leaves ()
+    {
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+
+        view.Enabled = false;
+        Assert.False (view.HasFocus);
+    }
+
+    [Fact]
+    public void Enabled_False_Leaves_Subview ()
+    {
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        var subView = new View
+        {
+            Id = "subView",
+            CanFocus = true
+        };
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+        Assert.Equal (subView, view.GetFocused ());
+
+        view.Enabled = false;
+        Assert.False (view.HasFocus);
+        Assert.False (subView.HasFocus);
+    }
+
+    [Fact]
+    public void Enabled_False_Leaves_Subview2 ()
+    {
+        var view = new Window
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        var subView = new View
+        {
+            Id = "subView",
+            CanFocus = true
+        };
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+        Assert.Equal (subView, view.GetFocused ());
+
+        view.Enabled = false;
+        Assert.False (view.HasFocus);
+        Assert.False (subView.HasFocus);
+    }
+
+    [Fact]
+    public void Enabled_False_On_Subview_Leaves_Just_Subview ()
+    {
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        var subView = new View
+        {
+            Id = "subView",
+            CanFocus = true
+        };
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+        Assert.Equal (subView, view.GetFocused ());
+
+        subView.Enabled = false;
+        Assert.True (view.HasFocus);
+        Assert.False (subView.HasFocus);
+    }
+
+    [Fact]
+    public void Enabled_False_Focuses_Deepest_Focusable_Subview ()
+    {
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        var subView = new View
+        {
+            Id = "subView",
+            CanFocus = true
+        };
+
+        var subViewSubView1 = new View
+        {
+            Id = "subViewSubView1",
+            CanFocus = false
+        };
+
+        var subViewSubView2 = new View
+        {
+            Id = "subViewSubView2",
+            CanFocus = true
+        };
+
+        var subViewSubView3 = new View
+        {
+            Id = "subViewSubView3",
+            CanFocus = true // This is the one that will be focused
+        };
+        subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.True (subView.HasFocus);
+        Assert.Equal (subView, view.GetFocused ());
+        Assert.Equal (subViewSubView2, subView.GetFocused ());
+
+        subViewSubView2.Enabled = false;
+        Assert.True (subView.HasFocus);
+        Assert.Equal (subView, view.GetFocused ());
+        Assert.Equal (subViewSubView3, subView.GetFocused ());
+        Assert.True (subViewSubView3.HasFocus);
+    }
+
+    [Fact]
+    public void Enabled_True_Subview_Focuses_SubView ()
+    {
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true,
+            Enabled = false
+        };
+
+        var subView = new View
+        {
+            Id = "subView",
+            CanFocus = true
+        };
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.False (view.HasFocus);
+        Assert.False (subView.HasFocus);
+
+        view.Enabled = true;
+        Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+    }
+
+    [Fact]
+    public void Enabled_True_On_Subview_Focuses ()
+    {
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        var subView = new View
+        {
+            Id = "subView",
+            CanFocus = true,
+            Enabled = false
+        };
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.False (subView.HasFocus);
+
+        subView.Enabled = true;
+        Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+    }
+
+    [Fact]
+    public void Enabled_True_Focuses_Deepest_Focusable_Subview ()
+    {
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        var subView = new View
+        {
+            Id = "subView",
+            CanFocus = true,
+            Enabled = false
+        };
+
+        var subViewSubView1 = new View
+        {
+            Id = "subViewSubView1",
+            CanFocus = false
+        };
+
+        var subViewSubView2 = new View
+        {
+            Id = "subViewSubView2",
+            CanFocus = true // This is the one that will be focused
+        };
+
+        var subViewSubView3 = new View
+        {
+            Id = "subViewSubView3",
+            CanFocus = true
+        };
+        subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.False (subView.HasFocus);
+        Assert.False (subViewSubView2.HasFocus);
+
+        subView.Enabled = true;
+        Assert.True (subView.HasFocus);
+        Assert.Equal (subView, view.GetFocused ());
+        Assert.Equal (subViewSubView2, subView.GetFocused ());
+        Assert.True (subViewSubView2.HasFocus);
+    }
+
+    [Fact]
+    [AutoInitShutdown]
+    public void _Enabled_Sets_Also_Sets_Subviews ()
+    {
+        var wasClicked = false;
+        var button = new Button { Text = "Click Me" };
+        button.IsDefault = true;
+        button.Accept += (s, e) => wasClicked = !wasClicked;
+        var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () };
+        win.Add (button);
+        var top = new Toplevel ();
+        top.Add (win);
+
+        var iterations = 0;
+
+        Application.Iteration += (s, a) =>
+                                 {
+                                     iterations++;
+
+                                     win.NewKeyDownEvent (Key.Enter);
+                                     Assert.True (wasClicked);
+                                     button.NewMouseEvent (new () { Flags = MouseFlags.Button1Clicked });
+                                     Assert.False (wasClicked);
+                                     Assert.True (button.Enabled);
+                                     Assert.True (button.CanFocus);
+                                     Assert.True (button.HasFocus);
+                                     Assert.True (win.Enabled);
+                                     Assert.True (win.CanFocus);
+                                     Assert.True (win.HasFocus);
+
+                                     Assert.True (button.HasFocus);
+                                     win.Enabled = false;
+                                     Assert.False (button.HasFocus);
+                                     button.NewKeyDownEvent (Key.Enter);
+                                     Assert.False (wasClicked);
+                                     button.NewMouseEvent (new () { Flags = MouseFlags.Button1Clicked });
+                                     Assert.False (wasClicked);
+                                     Assert.False (button.Enabled);
+                                     Assert.True (button.CanFocus);
+                                     Assert.False (button.HasFocus);
+                                     Assert.False (win.Enabled);
+                                     Assert.True (win.CanFocus);
+                                     Assert.False (win.HasFocus);
+                                     button.SetFocus ();
+                                     Assert.False (button.HasFocus);
+                                     Assert.False (win.HasFocus);
+                                     win.SetFocus ();
+                                     Assert.False (button.HasFocus);
+                                     Assert.False (win.HasFocus);
+
+                                     win.Enabled = true;
+                                     win.FocusDeepest (null, NavigationDirection.Forward);
+                                     Assert.True (button.HasFocus);
+                                     Assert.True (win.HasFocus);
+
+                                     Application.RequestStop ();
+                                 };
+
+        Application.Run (top);
+
+        Assert.Equal (1, iterations);
+        top.Dispose ();
+    }
+}

+ 451 - 0
UnitTests/View/Navigation/EnterLeaveTests.cs

@@ -0,0 +1,451 @@
+using Xunit.Abstractions;
+
+namespace Terminal.Gui.ViewTests;
+
+public class EnterLeaveTests (ITestOutputHelper _output) : TestsAllViews
+{
+    [Fact]
+    public void SetFocus_FiresEnter ()
+    {
+        int nEnter = 0;
+        int nLeave = 0;
+        var view = new View ()
+        {
+            Id = "view",
+            CanFocus = true
+        };
+        view.Enter += (s, e) => nEnter++;
+        view.Leave += (s, e) => nLeave++;
+
+        Assert.True (view.CanFocus);
+        Assert.False (view.HasFocus);
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.Equal (1, nEnter);
+        Assert.Equal (0, nLeave);
+    }
+
+    [Fact]
+    public void RemoveFocus_FiresLeave ()
+    {
+        int nEnter = 0;
+        int nLeave = 0;
+        var view = new View ()
+        {
+            Id = "view",
+            CanFocus = true
+        };
+        view.Enter += (s, e) => nEnter++;
+        view.Leave += (s, e) => nLeave++;
+
+        Assert.True (view.CanFocus);
+        Assert.False (view.HasFocus);
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.Equal (1, nEnter);
+        Assert.Equal (0, nLeave);
+
+        view.HasFocus = false;
+        Assert.Equal (1, nEnter);
+        Assert.Equal (1, nLeave);
+    }
+    
+    [Fact]
+    public void SetFocus_SubView_SetFocus_FiresEnter ()
+    {
+        int viewEnterCount = 0;
+        int viewLeaveCount = 0;
+        var view = new View ()
+        {
+            Id = "view",
+            CanFocus = true
+        };
+        view.Enter += (s, e) => viewEnterCount++;
+        view.Leave += (s, e) => viewLeaveCount++;
+
+        int subviewEnterCount = 0;
+        int subviewLeaveCount = 0;
+        var subview = new View ()
+        {
+            Id = "subview",
+            CanFocus = true
+        };
+        subview.Enter += (s, e) => subviewEnterCount++;
+        subview.Leave += (s, e) => subviewLeaveCount++;
+
+        view.Add (subview);
+
+        view.SetFocus ();
+
+        Assert.Equal (1, viewEnterCount);
+        Assert.Equal (0, viewLeaveCount);
+
+        Assert.Equal (1, subviewEnterCount);
+        Assert.Equal (0, subviewLeaveCount);
+    }
+
+    [Fact]
+    public void RemoveFocus_SubView_FiresLeave ()
+    {
+        int viewEnterCount = 0;
+        int viewLeaveCount = 0;
+        var view = new View ()
+        {
+            Id = "view",
+            CanFocus = true
+        };
+        view.Enter += (s, e) => viewEnterCount++;
+        view.Leave += (s, e) => viewLeaveCount++;
+
+        int subviewEnterCount = 0;
+        int subviewLeaveCount = 0;
+        var subview = new View ()
+        {
+            Id = "subview",
+            CanFocus = true
+        };
+        subview.Enter += (s, e) => subviewEnterCount++;
+        subview.Leave += (s, e) => subviewLeaveCount++;
+
+        view.Add (subview);
+
+        view.SetFocus ();
+
+        view.HasFocus = false;
+
+        Assert.Equal (1, viewEnterCount);
+        Assert.Equal (1, viewLeaveCount);
+
+        Assert.Equal (1, subviewEnterCount);
+        Assert.Equal (1, subviewLeaveCount);
+
+        view.SetFocus ();
+
+        Assert.Equal (2, viewEnterCount);
+        Assert.Equal (1, viewLeaveCount);
+
+        Assert.Equal (2, subviewEnterCount);
+        Assert.Equal (1, subviewLeaveCount);
+
+        subview.HasFocus = false;
+
+        Assert.Equal (2, viewEnterCount);
+        Assert.Equal (2, viewLeaveCount);
+
+        Assert.Equal (2, subviewEnterCount);
+        Assert.Equal (2, subviewLeaveCount);
+    }
+    
+    [Fact]
+    public void SetFocus_CompoundSubView_FiresEnter ()
+    {
+        int viewEnterCount = 0;
+        int viewLeaveCount = 0;
+        var view = new View ()
+        {
+            Id = "view",
+            CanFocus = true
+        };
+        view.Enter += (s, e) => viewEnterCount++;
+        view.Leave += (s, e) => viewLeaveCount++;
+
+        int subViewEnterCount = 0;
+        int subViewLeaveCount = 0;
+        var subView = new View ()
+        {
+            Id = "subView",
+            CanFocus = true
+        };
+        subView.Enter += (s, e) => subViewEnterCount++;
+        subView.Leave += (s, e) => subViewLeaveCount++;
+
+        int subviewSubView1EnterCount = 0;
+        int subviewSubView1LeaveCount = 0;
+        var subViewSubView1 = new View ()
+        {
+            Id = "subViewSubView1",
+            CanFocus = false
+        };
+        subViewSubView1.Enter += (s, e) => subviewSubView1EnterCount++;
+        subViewSubView1.Leave += (s, e) => subviewSubView1LeaveCount++;
+
+        int subviewSubView2EnterCount = 0;
+        int subviewSubView2LeaveCount = 0;
+        var subViewSubView2 = new View ()
+        {
+            Id = "subViewSubView2",
+            CanFocus = true
+        };
+        subViewSubView2.Enter += (s, e) => subviewSubView2EnterCount++;
+        subViewSubView2.Leave += (s, e) => subviewSubView2LeaveCount++;
+
+        int subviewSubView3EnterCount = 0;
+        int subviewSubView3LeaveCount = 0;
+        var subViewSubView3 = new View ()
+        {
+            Id = "subViewSubView3",
+            CanFocus = false
+        };
+        subViewSubView3.Enter += (s, e) => subviewSubView3EnterCount++;
+        subViewSubView3.Leave += (s, e) => subviewSubView3LeaveCount++;
+
+        subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.True(view.HasFocus);
+        Assert.True (subView.HasFocus);
+        Assert.False (subViewSubView1.HasFocus);
+        Assert.True (subViewSubView2.HasFocus);
+        Assert.False (subViewSubView3.HasFocus);
+
+        Assert.Equal (1, viewEnterCount);
+        Assert.Equal (0, viewLeaveCount);
+
+        Assert.Equal (1, subViewEnterCount);
+        Assert.Equal (0, subViewLeaveCount);
+
+        Assert.Equal (0, subviewSubView1EnterCount);
+        Assert.Equal (0, subviewSubView1LeaveCount);
+
+        Assert.Equal (1, subviewSubView2EnterCount);
+        Assert.Equal (0, subviewSubView2LeaveCount);
+
+        Assert.Equal (0, subviewSubView3EnterCount);
+        Assert.Equal (0, subviewSubView3LeaveCount);
+    }
+
+
+    [Fact]
+    public void RemoveFocus_CompoundSubView_FiresLeave ()
+    {
+        int viewEnterCount = 0;
+        int viewLeaveCount = 0;
+        var view = new View ()
+        {
+            Id = "view",
+            CanFocus = true
+        };
+        view.Enter += (s, e) => viewEnterCount++;
+        view.Leave += (s, e) => viewLeaveCount++;
+
+        int subViewEnterCount = 0;
+        int subViewLeaveCount = 0;
+        var subView = new View ()
+        {
+            Id = "subView",
+            CanFocus = true
+        };
+        subView.Enter += (s, e) => subViewEnterCount++;
+        subView.Leave += (s, e) => subViewLeaveCount++;
+
+        int subviewSubView1EnterCount = 0;
+        int subviewSubView1LeaveCount = 0;
+        var subViewSubView1 = new View ()
+        {
+            Id = "subViewSubView1",
+            CanFocus = false
+        };
+        subViewSubView1.Enter += (s, e) => subviewSubView1EnterCount++;
+        subViewSubView1.Leave += (s, e) => subviewSubView1LeaveCount++;
+
+        int subviewSubView2EnterCount = 0;
+        int subviewSubView2LeaveCount = 0;
+        var subViewSubView2 = new View ()
+        {
+            Id = "subViewSubView2",
+            CanFocus = true
+        };
+        subViewSubView2.Enter += (s, e) => subviewSubView2EnterCount++;
+        subViewSubView2.Leave += (s, e) => subviewSubView2LeaveCount++;
+
+        int subviewSubView3EnterCount = 0;
+        int subviewSubView3LeaveCount = 0;
+        var subViewSubView3 = new View ()
+        {
+            Id = "subViewSubView3",
+            CanFocus = false
+        };
+        subViewSubView3.Enter += (s, e) => subviewSubView3EnterCount++;
+        subViewSubView3.Leave += (s, e) => subviewSubView3LeaveCount++;
+
+        subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+        Assert.False (subViewSubView1.HasFocus);
+        Assert.True (subViewSubView2.HasFocus);
+        Assert.False (subViewSubView3.HasFocus);
+
+        view.HasFocus = false;
+        Assert.False (view.HasFocus);
+        Assert.False (subView.HasFocus);
+        Assert.False (subViewSubView1.HasFocus);
+        Assert.False (subViewSubView2.HasFocus);
+        Assert.False (subViewSubView3.HasFocus);
+
+        Assert.Equal (1, viewEnterCount);
+        Assert.Equal (1, viewLeaveCount);
+
+        Assert.Equal (1, subViewEnterCount);
+        Assert.Equal (1, subViewLeaveCount);
+
+        Assert.Equal (0, subviewSubView1EnterCount);
+        Assert.Equal (0, subviewSubView1LeaveCount);
+
+        Assert.Equal (1, subviewSubView2EnterCount);
+        Assert.Equal (1, subviewSubView2LeaveCount);
+
+        Assert.Equal (0, subviewSubView3EnterCount);
+        Assert.Equal (0, subviewSubView3LeaveCount);
+    }
+
+
+    [Fact]
+    public void SetFocus_Peer_LeavesOther ()
+    {
+        var view = new View ()
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        var subview1 = new View ()
+        {
+            Id = "subview1",
+            CanFocus = true
+        };
+
+        var subview2 = new View ()
+        {
+            Id = "subview2",
+            CanFocus = true
+        };
+        view.Add (subview1, subview2);
+
+        view.SetFocus ();
+        Assert.Equal (subview1, view.GetFocused ());
+        Assert.True (subview1.HasFocus);
+        Assert.False (subview2.HasFocus);
+
+        subview2.SetFocus ();
+        Assert.Equal (subview2, view.GetFocused ());
+        Assert.True (subview2.HasFocus);
+        Assert.False (subview1.HasFocus);
+    }
+
+    [Fact]
+    public void SetFocus_Peer_LeavesOthers_Subviews ()
+    {
+        var top = new View
+        {
+            Id = "top",
+            CanFocus = true
+        };
+        var view1 = new View
+        {
+            Id = "view1",
+            CanFocus = true
+        };
+
+        var subView1 = new View
+        {
+            Id = "subView1",
+            CanFocus = true
+        };
+
+        view1.Add (subView1);
+
+        var subView1SubView1 = new View
+        {
+            Id = "subView1subView1",
+            CanFocus = true
+        };
+
+        subView1.Add (subView1SubView1);
+
+        var view2 = new View
+        {
+            Id = "view2",
+            CanFocus = true
+        };
+
+        top.Add (view1, view2);
+        Assert.False (view1.HasFocus);
+        Assert.False (view2.HasFocus);
+
+        view1.SetFocus ();
+        Assert.True (view1.HasFocus);
+        Assert.True (subView1.HasFocus);
+        Assert.True (subView1SubView1.HasFocus);
+        Assert.Equal (subView1, view1.GetFocused ());
+        Assert.Equal (subView1SubView1, subView1.GetFocused ());
+
+        view2.SetFocus ();
+        Assert.False (view1.HasFocus);
+        Assert.True (view2.HasFocus);
+    }
+
+
+    [Fact]
+    public void HasFocus_False_Leave_Invoked ()
+    {
+        var view = new View ()
+        {
+            Id = "view",
+            CanFocus = true
+        };
+        Assert.True (view.CanFocus);
+        Assert.False (view.HasFocus);
+
+        int leaveInvoked = 0;
+
+        view.Leave += (s, e) => leaveInvoked++;
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.Equal (0, leaveInvoked);
+
+        view.HasFocus = false;
+        Assert.False (view.HasFocus);
+        Assert.Equal (1, leaveInvoked);
+    }
+
+    [Fact]
+    public void HasFocus_False_Leave_Invoked_ForAllSubViews ()
+    {
+        var view = new View ()
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        var subview = new View ()
+        {
+            Id = "subview",
+            CanFocus = true
+        };
+        view.Add (subview);
+
+        int leaveInvoked = 0;
+
+        view.Leave += (s, e) => leaveInvoked++;
+        subview.Leave += (s, e) => leaveInvoked++;
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.Equal (0, leaveInvoked);
+
+        view.HasFocus = false;
+        Assert.False (view.HasFocus);
+        Assert.False (subview.HasFocus);
+        Assert.Equal (2, leaveInvoked);
+    }
+
+}

+ 52 - 54
UnitTests/View/Navigation/HasFocusTests.cs

@@ -74,32 +74,38 @@ public class HasFocusTests (ITestOutputHelper _output) : TestsAllViews
     }
     }
 
 
 
 
+
     [Fact]
     [Fact]
-    public void HasFocus_False_Leave_Invoked ()
+    public void Enabled_False_Sets_HasFocus_To_False ()
     {
     {
-        var view = new View ()
-        {
-            Id = "view",
-            CanFocus = true
-        };
-        Assert.True (view.CanFocus);
-        Assert.False (view.HasFocus);
-
-        int leaveInvoked = 0;
-
-        view.Leave += (s, e) => leaveInvoked++;
+        var wasClicked = false;
+        var view = new Button { Text = "Click Me" };
+        view.Accept += (s, e) => wasClicked = !wasClicked;
 
 
-        view.SetFocus ();
+        view.NewKeyDownEvent (Key.Space);
+        Assert.True (wasClicked);
+        view.NewMouseEvent (new () { Flags = MouseFlags.Button1Clicked });
+        Assert.False (wasClicked);
+        Assert.True (view.Enabled);
+        Assert.True (view.CanFocus);
         Assert.True (view.HasFocus);
         Assert.True (view.HasFocus);
-        Assert.Equal (0, leaveInvoked);
 
 
-        view.HasFocus = false;
+        view.Enabled = false;
+        view.NewKeyDownEvent (Key.Space);
+        Assert.False (wasClicked);
+        view.NewMouseEvent (new () { Flags = MouseFlags.Button1Clicked });
+        Assert.False (wasClicked);
+        Assert.False (view.Enabled);
+        Assert.True (view.CanFocus);
+        Assert.False (view.HasFocus);
+        view.SetFocus ();
         Assert.False (view.HasFocus);
         Assert.False (view.HasFocus);
-        Assert.Equal (1, leaveInvoked);
     }
     }
 
 
+
+
     [Fact]
     [Fact]
-    public void HasFocus_False_Leave_Invoked_ForAllSubViews ()
+    public void HasFocus_False_CompoundSubView_Leaves_All ()
     {
     {
         var view = new View ()
         var view = new View ()
         {
         {
@@ -107,54 +113,46 @@ public class HasFocusTests (ITestOutputHelper _output) : TestsAllViews
             CanFocus = true
             CanFocus = true
         };
         };
 
 
-        var subview = new View ()
+        var subView = new View ()
         {
         {
-            Id = "subview",
+            Id = "subView",
             CanFocus = true
             CanFocus = true
         };
         };
-        view.Add (subview);
-
-        int leaveInvoked = 0;
 
 
-        view.Leave += (s, e) => leaveInvoked++;
-        subview.Leave += (s, e) => leaveInvoked++;
+        var subViewSubView1 = new View ()
+        {
+            Id = "subViewSubView1",
+            CanFocus = false
+        };
 
 
-        view.SetFocus ();
-        Assert.True (view.HasFocus);
-        Assert.Equal (0, leaveInvoked);
+        var subViewSubView2 = new View ()
+        {
+            Id = "subViewSubView2",
+            CanFocus = true
+        };
 
 
-        view.HasFocus = false;
-        Assert.False (view.HasFocus);
-        Assert.False (subview.HasFocus);
-        Assert.Equal (2, leaveInvoked);
-    }
+        var subViewSubView3 = new View ()
+        {
+            Id = "subViewSubView3",
+            CanFocus = false
+        };
 
 
+        subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
 
 
-    [Fact]
-    public void Enabled_False_Sets_HasFocus_To_False ()
-    {
-        var wasClicked = false;
-        var view = new Button { Text = "Click Me" };
-        view.Accept += (s, e) => wasClicked = !wasClicked;
+        view.Add (subView);
 
 
-        view.NewKeyDownEvent (Key.Space);
-        Assert.True (wasClicked);
-        view.NewMouseEvent (new () { Flags = MouseFlags.Button1Clicked });
-        Assert.False (wasClicked);
-        Assert.True (view.Enabled);
-        Assert.True (view.CanFocus);
+        view.SetFocus ();
         Assert.True (view.HasFocus);
         Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+        Assert.False (subViewSubView1.HasFocus);
+        Assert.True (subViewSubView2.HasFocus);
+        Assert.False (subViewSubView3.HasFocus);
 
 
-        view.Enabled = false;
-        view.NewKeyDownEvent (Key.Space);
-        Assert.False (wasClicked);
-        view.NewMouseEvent (new () { Flags = MouseFlags.Button1Clicked });
-        Assert.False (wasClicked);
-        Assert.False (view.Enabled);
-        Assert.True (view.CanFocus);
-        Assert.False (view.HasFocus);
-        view.SetFocus ();
+        view.HasFocus = false;
         Assert.False (view.HasFocus);
         Assert.False (view.HasFocus);
+        Assert.False (subView.HasFocus);
+        Assert.False (subViewSubView1.HasFocus);
+        Assert.False (subViewSubView2.HasFocus);
+        Assert.False (subViewSubView3.HasFocus);
     }
     }
-
 }
 }

+ 44 - 397
UnitTests/View/Navigation/NavigationTests.cs

@@ -1,4 +1,5 @@
 using Xunit.Abstractions;
 using Xunit.Abstractions;
+using static System.Net.Mime.MediaTypeNames;
 
 
 namespace Terminal.Gui.ViewTests;
 namespace Terminal.Gui.ViewTests;
 
 
@@ -32,7 +33,7 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
         {
         {
             Id = "otherView",
             Id = "otherView",
             CanFocus = true,
             CanFocus = true,
-            TabStop = view.TabStop
+            TabStop = view.TabStop == TabBehavior.NoStop ? TabBehavior.TabStop : view.TabStop
         };
         };
 
 
         top.Add (view, otherView);
         top.Add (view, otherView);
@@ -126,7 +127,7 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
             Height = 1,
             Height = 1,
             Width = 1,
             Width = 1,
             CanFocus = true,
             CanFocus = true,
-            TabStop = view.TabStop
+            TabStop = view.TabStop == TabBehavior.NoStop ? TabBehavior.TabStop : view.TabStop
         };
         };
 
 
         view.X = Pos.Right (otherView);
         view.X = Pos.Right (otherView);
@@ -155,42 +156,36 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
         Assert.Equal (0, nLeave);
         Assert.Equal (0, nLeave);
 
 
         // Use keyboard to navigate to next view (otherView).
         // Use keyboard to navigate to next view (otherView).
-        if (view is TextView)
-        {
-            Application.OnKeyDown (Key.F6);
-        }
-        else
+        var tries = 0;
+
+        while (view.HasFocus)
         {
         {
-            var tries = 0;
+            if (++tries > 10)
+            {
+                Assert.Fail ($"{view} is not leaving.");
+            }
 
 
-            while (view.HasFocus)
+            switch (view.TabStop)
             {
             {
-                if (++tries > 10)
-                {
-                    Assert.Fail ($"{view} is not leaving.");
-                }
-
-                switch (view.TabStop)
-                {
-                    case TabBehavior.NoStop:
-                        Application.OnKeyDown (Key.Tab);
-
-                        break;
-                    case TabBehavior.TabStop:
-                        Application.OnKeyDown (Key.Tab);
-
-                        break;
-                    case TabBehavior.TabGroup:
-                        Application.OnKeyDown (Key.F6);
-
-                        break;
-                    case null:
-                        Application.OnKeyDown (Key.Tab);
-
-                        break;
-                    default:
-                        throw new ArgumentOutOfRangeException ();
-                }
+                case null:
+                case TabBehavior.NoStop:
+                case TabBehavior.TabStop:
+                    if (Application.OnKeyDown (Key.Tab))
+                    {
+                        if (view.HasFocus)
+                        {
+                            // Try another nav key (e.g. for TextView that eats Tab)
+                            Application.OnKeyDown (Key.CursorDown);
+                        }
+                    };
+                    break;
+
+                case TabBehavior.TabGroup:
+                    Application.OnKeyDown (Key.F6);
+
+                    break;
+                default:
+                    throw new ArgumentOutOfRangeException ();
             }
             }
         }
         }
 
 
@@ -382,68 +377,6 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
         r.Dispose ();
         r.Dispose ();
     }
     }
 
 
-    [Fact]
-    [AutoInitShutdown]
-    public void Enabled_Sets_Also_Sets_Subviews ()
-    {
-        var wasClicked = false;
-        var button = new Button { Text = "Click Me" };
-        button.IsDefault = true;
-        button.Accept += (s, e) => wasClicked = !wasClicked;
-        var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () };
-        win.Add (button);
-        var top = new Toplevel ();
-        top.Add (win);
-
-        var iterations = 0;
-
-        Application.Iteration += (s, a) =>
-                                 {
-                                     iterations++;
-
-                                     win.NewKeyDownEvent (Key.Enter);
-                                     Assert.True (wasClicked);
-                                     button.NewMouseEvent (new () { Flags = MouseFlags.Button1Clicked });
-                                     Assert.False (wasClicked);
-                                     Assert.True (button.Enabled);
-                                     Assert.True (button.CanFocus);
-                                     Assert.True (button.HasFocus);
-                                     Assert.True (win.Enabled);
-                                     Assert.True (win.CanFocus);
-                                     Assert.True (win.HasFocus);
-
-                                     win.Enabled = false;
-                                     button.NewKeyDownEvent (Key.Enter);
-                                     Assert.False (wasClicked);
-                                     button.NewMouseEvent (new () { Flags = MouseFlags.Button1Clicked });
-                                     Assert.False (wasClicked);
-                                     Assert.False (button.Enabled);
-                                     Assert.True (button.CanFocus);
-                                     Assert.False (button.HasFocus);
-                                     Assert.False (win.Enabled);
-                                     Assert.True (win.CanFocus);
-                                     Assert.False (win.HasFocus);
-                                     button.SetFocus ();
-                                     Assert.False (button.HasFocus);
-                                     Assert.False (win.HasFocus);
-                                     win.SetFocus ();
-                                     Assert.False (button.HasFocus);
-                                     Assert.False (win.HasFocus);
-
-                                     win.Enabled = true;
-                                     win.FocusDeepest (null, NavigationDirection.Forward);
-                                     Assert.True (button.HasFocus);
-                                     Assert.True (win.HasFocus);
-
-                                     Application.RequestStop ();
-                                 };
-
-        Application.Run (top);
-
-        Assert.Equal (1, iterations);
-        top.Dispose ();
-    }
-
     // View.Focused & View.MostFocused tests
     // View.Focused & View.MostFocused tests
 
 
     // View.Focused - No subviews
     // View.Focused - No subviews
@@ -461,42 +394,42 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
     }
     }
 
 
     [Fact]
     [Fact]
-    [AutoInitShutdown]
     public void FocusNearestView_Ensure_Focus_Ordered ()
     public void FocusNearestView_Ensure_Focus_Ordered ()
     {
     {
-        var top = new Toplevel ();
+        Application.Top = Application.Current = new Toplevel ();
 
 
         var win = new Window ();
         var win = new Window ();
         var winSubview = new View { CanFocus = true, Text = "WindowSubview" };
         var winSubview = new View { CanFocus = true, Text = "WindowSubview" };
         win.Add (winSubview);
         win.Add (winSubview);
-        top.Add (win);
+        Application.Current.Add (win);
 
 
         var frm = new FrameView ();
         var frm = new FrameView ();
         var frmSubview = new View { CanFocus = true, Text = "FrameSubview" };
         var frmSubview = new View { CanFocus = true, Text = "FrameSubview" };
         frm.Add (frmSubview);
         frm.Add (frmSubview);
-        top.Add (frm);
+        Application.Current.Add (frm);
+        Application.Current.SetFocus ();
 
 
-        Application.Begin (top);
-        Assert.Equal ("WindowSubview", top.GetMostFocused ().Text);
+        Assert.Equal (winSubview, Application.Current.GetMostFocused ());
 
 
-        Application.OnKeyDown (Key.Tab);
-        Assert.Equal ("WindowSubview", top.GetMostFocused ().Text);
+        Application.OnKeyDown (Key.Tab); // Move to the next TabStop. There is none. So we should stay.
+        Assert.Equal (winSubview, Application.Current.GetMostFocused ());
 
 
         Application.OnKeyDown (Key.F6);
         Application.OnKeyDown (Key.F6);
-        Assert.Equal ("FrameSubview", top.GetMostFocused ().Text);
+        Assert.Equal (frmSubview, Application.Current.GetMostFocused ());
 
 
         Application.OnKeyDown (Key.Tab);
         Application.OnKeyDown (Key.Tab);
-        Assert.Equal ("FrameSubview", top.GetMostFocused ().Text);
+        Assert.Equal (frmSubview, Application.Current.GetMostFocused ());
 
 
         Application.OnKeyDown (Key.F6);
         Application.OnKeyDown (Key.F6);
-        Assert.Equal ("WindowSubview", top.GetMostFocused ().Text);
+        Assert.Equal (winSubview, Application.Current.GetMostFocused ());
 
 
         Application.OnKeyDown (Key.F6.WithShift);
         Application.OnKeyDown (Key.F6.WithShift);
-        Assert.Equal ("FrameSubview", top.GetMostFocused ().Text);
+        Assert.Equal (frmSubview, Application.Current.GetMostFocused ());
 
 
         Application.OnKeyDown (Key.F6.WithShift);
         Application.OnKeyDown (Key.F6.WithShift);
-        Assert.Equal ("WindowSubview", top.GetMostFocused ().Text);
-        top.Dispose ();
+        Assert.Equal (winSubview, Application.Current.GetMostFocused ());
+
+        Application.Current.Dispose ();
     }
     }
 
 
     [Fact]
     [Fact]
@@ -797,290 +730,4 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
     }
     }
 #endif
 #endif
 
 
-    [Fact]
-    [AutoInitShutdown]
-    public void ScreenToView_ViewToScreen_FindDeepestView_Full_Top ()
-    {
-        Toplevel top = new ();
-        top.BorderStyle = LineStyle.Single;
-
-        var view = new View
-        {
-            X = 3,
-            Y = 2,
-            Width = 10,
-            Height = 1,
-            Text = "0123456789"
-        };
-        top.Add (view);
-
-        Application.Begin (top);
-
-        Assert.Equal (Application.Current, top);
-        Assert.Equal (new (0, 0, 80, 25), new Rectangle (0, 0, View.Driver.Cols, View.Driver.Rows));
-        Assert.Equal (new (0, 0, View.Driver.Cols, View.Driver.Rows), top.Frame);
-        Assert.Equal (new (0, 0, 80, 25), top.Frame);
-
-        ((FakeDriver)Application.Driver!).SetBufferSize (20, 10);
-        Assert.Equal (new (0, 0, View.Driver.Cols, View.Driver.Rows), top.Frame);
-        Assert.Equal (new (0, 0, 20, 10), top.Frame);
-
-        _ = TestHelpers.AssertDriverContentsWithFrameAre (
-                                                          @"
-┌──────────────────┐
-│                  │
-│                  │
-│   0123456789     │
-│                  │
-│                  │
-│                  │
-│                  │
-│                  │
-└──────────────────┘",
-                                                          _output
-                                                         );
-
-        // top
-        Assert.Equal (Point.Empty, top.ScreenToFrame (new (0, 0)));
-        Point screen = top.Margin.ViewportToScreen (new Point (0, 0));
-        Assert.Equal (0, screen.X);
-        Assert.Equal (0, screen.Y);
-        screen = top.Border.ViewportToScreen (new Point (0, 0));
-        Assert.Equal (0, screen.X);
-        Assert.Equal (0, screen.Y);
-        screen = top.Padding.ViewportToScreen (new Point (0, 0));
-        Assert.Equal (1, screen.X);
-        Assert.Equal (1, screen.Y);
-        screen = top.ViewportToScreen (new Point (0, 0));
-        Assert.Equal (1, screen.X);
-        Assert.Equal (1, screen.Y);
-        screen = top.ViewportToScreen (new Point (-1, -1));
-        Assert.Equal (0, screen.X);
-        Assert.Equal (0, screen.Y);
-        var found = View.FindDeepestView (top, new (0, 0));
-        Assert.Equal (top.Border, found);
-
-        Assert.Equal (0, found.Frame.X);
-        Assert.Equal (0, found.Frame.Y);
-        Assert.Equal (new (3, 2), top.ScreenToFrame (new (3, 2)));
-        screen = top.ViewportToScreen (new Point (3, 2));
-        Assert.Equal (4, screen.X);
-        Assert.Equal (3, screen.Y);
-        found = View.FindDeepestView (top, new (screen.X, screen.Y));
-        Assert.Equal (view, found);
-
-        //Assert.Equal (0, found.FrameToScreen ().X);
-        //Assert.Equal (0, found.FrameToScreen ().Y);
-        found = View.FindDeepestView (top, new (3, 2));
-        Assert.Equal (top, found);
-
-        //Assert.Equal (3, found.FrameToScreen ().X);
-        //Assert.Equal (2, found.FrameToScreen ().Y);
-        Assert.Equal (new (13, 2), top.ScreenToFrame (new (13, 2)));
-        screen = top.ViewportToScreen (new Point (12, 2));
-        Assert.Equal (13, screen.X);
-        Assert.Equal (3, screen.Y);
-        found = View.FindDeepestView (top, new (screen.X, screen.Y));
-        Assert.Equal (view, found);
-
-        //Assert.Equal (9, found.FrameToScreen ().X);
-        //Assert.Equal (0, found.FrameToScreen ().Y);
-        screen = top.ViewportToScreen (new Point (13, 2));
-        Assert.Equal (14, screen.X);
-        Assert.Equal (3, screen.Y);
-        found = View.FindDeepestView (top, new (13, 2));
-        Assert.Equal (top, found);
-
-        //Assert.Equal (13, found.FrameToScreen ().X);
-        //Assert.Equal (2, found.FrameToScreen ().Y);
-        Assert.Equal (new (14, 3), top.ScreenToFrame (new (14, 3)));
-        screen = top.ViewportToScreen (new Point (14, 3));
-        Assert.Equal (15, screen.X);
-        Assert.Equal (4, screen.Y);
-        found = View.FindDeepestView (top, new (14, 3));
-        Assert.Equal (top, found);
-
-        //Assert.Equal (14, found.FrameToScreen ().X);
-        //Assert.Equal (3, found.FrameToScreen ().Y);
-
-        // view
-        Assert.Equal (new (-4, -3), view.ScreenToFrame (new (0, 0)));
-        screen = view.Margin.ViewportToScreen (new Point (-3, -2));
-        Assert.Equal (1, screen.X);
-        Assert.Equal (1, screen.Y);
-        screen = view.Border.ViewportToScreen (new Point (-3, -2));
-        Assert.Equal (1, screen.X);
-        Assert.Equal (1, screen.Y);
-        screen = view.Padding.ViewportToScreen (new Point (-3, -2));
-        Assert.Equal (1, screen.X);
-        Assert.Equal (1, screen.Y);
-        screen = view.ViewportToScreen (new Point (-3, -2));
-        Assert.Equal (1, screen.X);
-        Assert.Equal (1, screen.Y);
-        screen = view.ViewportToScreen (new Point (-4, -3));
-        Assert.Equal (0, screen.X);
-        Assert.Equal (0, screen.Y);
-        found = View.FindDeepestView (top, new (0, 0));
-        Assert.Equal (top.Border, found);
-
-        Assert.Equal (new (-1, -1), view.ScreenToFrame (new (3, 2)));
-        screen = view.ViewportToScreen (new Point (0, 0));
-        Assert.Equal (4, screen.X);
-        Assert.Equal (3, screen.Y);
-        found = View.FindDeepestView (top, new (4, 3));
-        Assert.Equal (view, found);
-
-        Assert.Equal (new (9, -1), view.ScreenToFrame (new (13, 2)));
-        screen = view.ViewportToScreen (new Point (10, 0));
-        Assert.Equal (14, screen.X);
-        Assert.Equal (3, screen.Y);
-        found = View.FindDeepestView (top, new (14, 3));
-        Assert.Equal (top, found);
-
-        Assert.Equal (new (10, 0), view.ScreenToFrame (new (14, 3)));
-        screen = view.ViewportToScreen (new Point (11, 1));
-        Assert.Equal (15, screen.X);
-        Assert.Equal (4, screen.Y);
-        found = View.FindDeepestView (top, new (15, 4));
-        Assert.Equal (top, found);
-        top.Dispose ();
-    }
-
-    [Fact]
-    [AutoInitShutdown]
-    public void ScreenToView_ViewToScreen_FindDeepestView_Smaller_Top ()
-    {
-        var top = new Toplevel
-        {
-            X = 3,
-            Y = 2,
-            Width = 20,
-            Height = 10,
-            BorderStyle = LineStyle.Single
-        };
-
-        var view = new View
-        {
-            X = 3,
-            Y = 2,
-            Width = 10,
-            Height = 1,
-            Text = "0123456789"
-        };
-        top.Add (view);
-
-        Application.Begin (top);
-
-        Assert.Equal (Application.Current, top);
-        Assert.Equal (new (0, 0, 80, 25), new Rectangle (0, 0, View.Driver.Cols, View.Driver.Rows));
-        Assert.NotEqual (new (0, 0, View.Driver.Cols, View.Driver.Rows), top.Frame);
-        Assert.Equal (new (3, 2, 20, 10), top.Frame);
-
-        ((FakeDriver)Application.Driver!).SetBufferSize (30, 20);
-        Assert.Equal (new (0, 0, 30, 20), new Rectangle (0, 0, View.Driver.Cols, View.Driver.Rows));
-        Assert.NotEqual (new (0, 0, View.Driver.Cols, View.Driver.Rows), top.Frame);
-        Assert.Equal (new (3, 2, 20, 10), top.Frame);
-
-        Rectangle frame = TestHelpers.AssertDriverContentsWithFrameAre (
-                                                                        @"
-   ┌──────────────────┐
-   │                  │
-   │                  │
-   │   0123456789     │
-   │                  │
-   │                  │
-   │                  │
-   │                  │
-   │                  │
-   └──────────────────┘",
-                                                                        _output
-                                                                       );
-
-        // mean the output started at col 3 and line 2
-        // which result with a width of 23 and a height of 10 on the output
-        Assert.Equal (new (3, 2, 23, 10), frame);
-
-        // top
-        Assert.Equal (new (-3, -2), top.ScreenToFrame (new (0, 0)));
-        Point screen = top.Margin.ViewportToScreen (new Point (-3, -2));
-        Assert.Equal (0, screen.X);
-        Assert.Equal (0, screen.Y);
-        screen = top.Border.ViewportToScreen (new Point (-3, -2));
-        Assert.Equal (0, screen.X);
-        Assert.Equal (0, screen.Y);
-        screen = top.Padding.ViewportToScreen (new Point (-3, -2));
-        Assert.Equal (1, screen.X);
-        Assert.Equal (1, screen.Y);
-        screen = top.ViewportToScreen (new Point (-3, -2));
-        Assert.Equal (1, screen.X);
-        Assert.Equal (1, screen.Y);
-        screen = top.ViewportToScreen (new Point (-4, -3));
-        Assert.Equal (0, screen.X);
-        Assert.Equal (0, screen.Y);
-        var found = View.FindDeepestView (top, new (-4, -3));
-        Assert.Null (found);
-        Assert.Equal (Point.Empty, top.ScreenToFrame (new (3, 2)));
-        screen = top.ViewportToScreen (new Point (0, 0));
-        Assert.Equal (4, screen.X);
-        Assert.Equal (3, screen.Y);
-        Assert.Equal (top.Border, View.FindDeepestView (top, new (3, 2)));
-
-        //Assert.Equal (0, found.FrameToScreen ().X);
-        //Assert.Equal (0, found.FrameToScreen ().Y);
-        Assert.Equal (new (10, 0), top.ScreenToFrame (new (13, 2)));
-        screen = top.ViewportToScreen (new Point (10, 0));
-        Assert.Equal (14, screen.X);
-        Assert.Equal (3, screen.Y);
-        Assert.Equal (top.Border, View.FindDeepestView (top, new (13, 2)));
-
-        //Assert.Equal (10, found.FrameToScreen ().X);
-        //Assert.Equal (0, found.FrameToScreen ().Y);
-        Assert.Equal (new (11, 1), top.ScreenToFrame (new (14, 3)));
-        screen = top.ViewportToScreen (new Point (11, 1));
-        Assert.Equal (15, screen.X);
-        Assert.Equal (4, screen.Y);
-        Assert.Equal (top, View.FindDeepestView (top, new (14, 3)));
-
-        // view
-        Assert.Equal (new (-7, -5), view.ScreenToFrame (new (0, 0)));
-        screen = view.Margin.ViewportToScreen (new Point (-6, -4));
-        Assert.Equal (1, screen.X);
-        Assert.Equal (1, screen.Y);
-        screen = view.Border.ViewportToScreen (new Point (-6, -4));
-        Assert.Equal (1, screen.X);
-        Assert.Equal (1, screen.Y);
-        screen = view.Padding.ViewportToScreen (new Point (-6, -4));
-        Assert.Equal (1, screen.X);
-        Assert.Equal (1, screen.Y);
-        screen = view.ViewportToScreen (new Point (-6, -4));
-        Assert.Equal (1, screen.X);
-        Assert.Equal (1, screen.Y);
-        Assert.Null (View.FindDeepestView (top, new (1, 1)));
-        Assert.Equal (new (-4, -3), view.ScreenToFrame (new (3, 2)));
-        screen = view.ViewportToScreen (new Point (-3, -2));
-        Assert.Equal (4, screen.X);
-        Assert.Equal (3, screen.Y);
-        Assert.Equal (top, View.FindDeepestView (top, new (4, 3)));
-        Assert.Equal (new (-1, -1), view.ScreenToFrame (new (6, 4)));
-        screen = view.ViewportToScreen (new Point (0, 0));
-        Assert.Equal (7, screen.X);
-        Assert.Equal (5, screen.Y);
-        Assert.Equal (view, View.FindDeepestView (top, new (7, 5)));
-        Assert.Equal (new (6, -1), view.ScreenToFrame (new (13, 4)));
-        screen = view.ViewportToScreen (new Point (7, 0));
-        Assert.Equal (14, screen.X);
-        Assert.Equal (5, screen.Y);
-        Assert.Equal (view, View.FindDeepestView (top, new (14, 5)));
-        Assert.Equal (new (7, -2), view.ScreenToFrame (new (14, 3)));
-        screen = view.ViewportToScreen (new Point (8, -1));
-        Assert.Equal (15, screen.X);
-        Assert.Equal (4, screen.Y);
-        Assert.Equal (top, View.FindDeepestView (top, new (15, 4)));
-        Assert.Equal (new (16, -2), view.ScreenToFrame (new (23, 3)));
-        screen = view.ViewportToScreen (new Point (17, -1));
-        Assert.Equal (24, screen.X);
-        Assert.Equal (4, screen.Y);
-        Assert.Null (View.FindDeepestView (top, new (24, 4)));
-        top.Dispose ();
-    }
 }
 }

+ 169 - 0
UnitTests/View/Navigation/RestoreFocusTests.cs

@@ -0,0 +1,169 @@
+using Xunit.Abstractions;
+
+namespace Terminal.Gui.ViewTests;
+
+public class RestoreFocusTests (ITestOutputHelper _output) : TestsAllViews
+{
+    [Fact]
+    public void RestoreFocus_Restores ()
+    {
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        var subView = new View
+        {
+            Id = "subView",
+            CanFocus = true
+        };
+
+        var subViewSubView1 = new View
+        {
+            Id = "subViewSubView1",
+            CanFocus = true
+        };
+
+        var subViewSubView2 = new View
+        {
+            Id = "subViewSubView2",
+            CanFocus = true
+        };
+
+        var subViewSubView3 = new View
+        {
+            Id = "subViewSubView3",
+            CanFocus = true
+        };
+        subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+        Assert.Equal (subView, view.GetFocused ());
+        Assert.True (subViewSubView1.HasFocus);
+        Assert.Equal (subViewSubView1, subView.GetFocused ());
+
+        view.HasFocus = false;
+        Assert.False (view.HasFocus);
+        Assert.False (subView.HasFocus);
+        Assert.False (subViewSubView1.HasFocus);
+        Assert.False (subViewSubView2.HasFocus);
+        Assert.False (subViewSubView3.HasFocus);
+
+        view.RestoreFocus (view.TabStop);
+        Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+        Assert.Equal (subView, view.GetFocused ());
+        Assert.Equal (subViewSubView1, subView.GetFocused ());
+        Assert.True (subViewSubView1.HasFocus);
+        Assert.False (subViewSubView2.HasFocus);
+        Assert.False (subViewSubView3.HasFocus);
+
+        subViewSubView2.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+        Assert.False (subViewSubView1.HasFocus);
+        Assert.True (subViewSubView2.HasFocus);
+        Assert.False (subViewSubView3.HasFocus);
+
+        view.HasFocus = false;
+        Assert.False (view.HasFocus);
+        Assert.False (subView.HasFocus);
+        Assert.False (subViewSubView1.HasFocus);
+        Assert.False (subViewSubView2.HasFocus);
+        Assert.False (subViewSubView3.HasFocus);
+
+        view.RestoreFocus (view.TabStop);
+        Assert.True (subView.HasFocus);
+        Assert.Equal (subView, view.GetFocused ());
+        Assert.True (subViewSubView2.HasFocus);
+        Assert.Equal (subViewSubView2, subView.GetFocused ());
+        Assert.False (subViewSubView1.HasFocus);
+        Assert.False (subViewSubView3.HasFocus);
+    }
+
+    [Fact]
+    public void RestoreFocus_Across_TabGroup ()
+    {
+        var top = new View
+        {
+            Id = "top",
+            CanFocus = true,
+            TabStop = TabBehavior.TabGroup
+        };
+
+        var tabGroup1 = new View
+        {
+            Id = "tabGroup1",
+            CanFocus = true,
+            TabStop = TabBehavior.TabGroup
+        };
+
+        var tabGroup1SubView1 = new View
+        {
+            Id = "tabGroup1SubView1",
+            CanFocus = true
+        };
+
+        var tabGroup1SubView2 = new View
+        {
+            Id = "tabGroup1SubView2",
+            CanFocus = true
+        };
+        tabGroup1.Add (tabGroup1SubView1, tabGroup1SubView2);
+
+        var tabGroup2 = new View
+        {
+            Id = "tabGroup2",
+            CanFocus = true,
+            TabStop = TabBehavior.TabGroup
+        };
+
+        var tabGroup2SubView1 = new View
+        {
+            Id = "tabGroup2SubView1",
+            CanFocus = true
+        };
+
+        var tabGroup2SubView2 = new View
+        {
+            Id = "tabGroup2SubView2",
+            CanFocus = true
+        };
+        tabGroup2.Add (tabGroup2SubView1, tabGroup2SubView2);
+
+        top.Add (tabGroup1, tabGroup2);
+
+        top.SetFocus ();
+        Assert.True (top.HasFocus);
+        Assert.Equal (tabGroup1, top.GetFocused ());
+        Assert.Equal (tabGroup1SubView1, tabGroup1.GetFocused ());
+
+        top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
+        Assert.True (top.HasFocus);
+        Assert.Equal (tabGroup2, top.GetFocused ());
+        Assert.Equal (tabGroup2SubView1, tabGroup2.GetFocused ());
+
+        top.HasFocus = false;
+        Assert.False (top.HasFocus);
+
+        top.RestoreFocus (null);
+        Assert.True (top.HasFocus);
+        Assert.Equal (tabGroup2, top.GetFocused ());
+        Assert.Equal (tabGroup2SubView1, tabGroup2.GetFocused ());
+
+        top.HasFocus = false;
+        Assert.False (top.HasFocus);
+
+        top.RestoreFocus (TabBehavior.TabGroup);
+        Assert.True (top.HasFocus);
+        Assert.Equal (tabGroup2, top.GetFocused ());
+        Assert.Equal (tabGroup2SubView1, tabGroup2.GetFocused ());
+
+
+    }
+}

+ 68 - 16
UnitTests/View/Navigation/SetFocusTests.cs

@@ -4,11 +4,10 @@ namespace Terminal.Gui.ViewTests;
 
 
 public class SetFocusTests (ITestOutputHelper _output) : TestsAllViews
 public class SetFocusTests (ITestOutputHelper _output) : TestsAllViews
 {
 {
-   
     [Fact]
     [Fact]
     public void SetFocus_With_Null_Superview_Does_Not_Throw_Exception ()
     public void SetFocus_With_Null_Superview_Does_Not_Throw_Exception ()
     {
     {
-        var view = new View ()
+        var view = new View
         {
         {
             Id = "view",
             Id = "view",
             CanFocus = true
             CanFocus = true
@@ -26,7 +25,7 @@ public class SetFocusTests (ITestOutputHelper _output) : TestsAllViews
     [Fact]
     [Fact]
     public void SetFocus_SetsFocus ()
     public void SetFocus_SetsFocus ()
     {
     {
-        var view = new View ()
+        var view = new View
         {
         {
             Id = "view",
             Id = "view",
             CanFocus = true
             CanFocus = true
@@ -41,7 +40,7 @@ public class SetFocusTests (ITestOutputHelper _output) : TestsAllViews
     [Fact]
     [Fact]
     public void SetFocus_NoSubView_Focused_Is_Null ()
     public void SetFocus_NoSubView_Focused_Is_Null ()
     {
     {
-        var view = new View ()
+        var view = new View
         {
         {
             Id = "view",
             Id = "view",
             CanFocus = true
             CanFocus = true
@@ -57,13 +56,13 @@ public class SetFocusTests (ITestOutputHelper _output) : TestsAllViews
     [Fact]
     [Fact]
     public void SetFocus_SubView_Focused_Is_Set ()
     public void SetFocus_SubView_Focused_Is_Set ()
     {
     {
-        var view = new View ()
+        var view = new Window
         {
         {
             Id = "view",
             Id = "view",
             CanFocus = true
             CanFocus = true
         };
         };
 
 
-        var subview = new View ()
+        var subview = new View
         {
         {
             Id = "subview",
             Id = "subview",
             CanFocus = true
             CanFocus = true
@@ -80,13 +79,13 @@ public class SetFocusTests (ITestOutputHelper _output) : TestsAllViews
     [Fact]
     [Fact]
     public void SetFocus_SetsFocus_DeepestSubView ()
     public void SetFocus_SetsFocus_DeepestSubView ()
     {
     {
-        var view = new View ()
+        var view = new View
         {
         {
             Id = "view",
             Id = "view",
             CanFocus = true
             CanFocus = true
         };
         };
 
 
-        var subview = new View ()
+        var subview = new View
         {
         {
             Id = "subview",
             Id = "subview",
             CanFocus = true
             CanFocus = true
@@ -101,30 +100,31 @@ public class SetFocusTests (ITestOutputHelper _output) : TestsAllViews
     [Fact]
     [Fact]
     public void SetFocus_SetsFocus_DeepestSubView_CompoundSubView ()
     public void SetFocus_SetsFocus_DeepestSubView_CompoundSubView ()
     {
     {
-        var view = new View ()
+        var view = new View
         {
         {
             Id = "view",
             Id = "view",
             CanFocus = true
             CanFocus = true
         };
         };
 
 
-        var subView = new View ()
+        var subView = new View
         {
         {
             Id = "subView",
             Id = "subView",
             CanFocus = true
             CanFocus = true
         };
         };
 
 
-        var subViewSubView1 = new View ()
+        var subViewSubView1 = new View
         {
         {
             Id = "subViewSubView1",
             Id = "subViewSubView1",
             CanFocus = false
             CanFocus = false
         };
         };
 
 
-        var subViewSubView2 = new View ()
+        var subViewSubView2 = new View
         {
         {
             Id = "subViewSubView2",
             Id = "subViewSubView2",
             CanFocus = true
             CanFocus = true
         };
         };
-        var subViewSubView3 = new View ()
+
+        var subViewSubView3 = new View
         {
         {
             Id = "subViewSubView3",
             Id = "subViewSubView3",
             CanFocus = false
             CanFocus = false
@@ -139,22 +139,73 @@ public class SetFocusTests (ITestOutputHelper _output) : TestsAllViews
         Assert.Equal (subViewSubView2, subView.GetFocused ());
         Assert.Equal (subViewSubView2, subView.GetFocused ());
     }
     }
 
 
+    [Fact]
+    public void SetFocus_CompoundSubView_SetFocus_Sets ()
+    {
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        var subView = new View
+        {
+            Id = "subView",
+            CanFocus = true
+        };
+
+        var subViewSubView1 = new View
+        {
+            Id = "subViewSubView1",
+            CanFocus = true
+        };
+
+        var subViewSubView2 = new View
+        {
+            Id = "subViewSubView2",
+            CanFocus = true
+        };
+
+        var subViewSubView3 = new View
+        {
+            Id = "subViewSubView3",
+            CanFocus = true
+        };
+        subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+        Assert.Equal (subView, view.GetFocused ());
+        Assert.True (subViewSubView1.HasFocus);
+        Assert.Equal (subViewSubView1, subView.GetFocused ());
+
+        subViewSubView2.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+        Assert.False (subViewSubView1.HasFocus);
+        Assert.True (subViewSubView2.HasFocus);
+        Assert.False (subViewSubView3.HasFocus);
+    }
+
     [Fact]
     [Fact]
     public void SetFocus_Peer_LeavesOther ()
     public void SetFocus_Peer_LeavesOther ()
     {
     {
-        var view = new View ()
+        var view = new View
         {
         {
             Id = "view",
             Id = "view",
             CanFocus = true
             CanFocus = true
         };
         };
 
 
-        var subview1 = new View ()
+        var subview1 = new View
         {
         {
             Id = "subview1",
             Id = "subview1",
             CanFocus = true
             CanFocus = true
         };
         };
 
 
-        var subview2 = new View ()
+        var subview2 = new View
         {
         {
             Id = "subview2",
             Id = "subview2",
             CanFocus = true
             CanFocus = true
@@ -180,6 +231,7 @@ public class SetFocusTests (ITestOutputHelper _output) : TestsAllViews
             Id = "top",
             Id = "top",
             CanFocus = true
             CanFocus = true
         };
         };
+
         var view1 = new View
         var view1 = new View
         {
         {
             Id = "view1",
             Id = "view1",

+ 253 - 0
UnitTests/View/Navigation/VisibleTests.cs

@@ -0,0 +1,253 @@
+using Xunit.Abstractions;
+
+namespace Terminal.Gui.ViewTests;
+
+public class VisibleTests (ITestOutputHelper _output) : TestsAllViews
+{
+    [Fact]
+    public void Visible_False_Leaves ()
+    {
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+
+        view.Visible = false;
+        Assert.False (view.HasFocus);
+    }
+
+    [Fact]
+    public void Visible_False_Leaves_Subview ()
+    {
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        var subView = new View
+        {
+            Id = "subView",
+            CanFocus = true
+        };
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+        Assert.Equal (subView, view.GetFocused ());
+
+        view.Visible = false;
+        Assert.False (view.HasFocus);
+        Assert.False (subView.HasFocus);
+    }
+
+    [Fact]
+    public void Visible_False_Leaves_Subview2 ()
+    {
+        var view = new Window
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        var subView = new View
+        {
+            Id = "subView",
+            CanFocus = true
+        };
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+        Assert.Equal (subView, view.GetFocused ());
+
+        view.Visible = false;
+        Assert.False (view.HasFocus);
+        Assert.False (subView.HasFocus);
+    }
+
+    [Fact]
+    public void Visible_False_On_Subview_Leaves_Just_Subview ()
+    {
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        var subView = new View
+        {
+            Id = "subView",
+            CanFocus = true
+        };
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+        Assert.Equal (subView, view.GetFocused ());
+
+        subView.Visible = false;
+        Assert.True (view.HasFocus);
+        Assert.False (subView.HasFocus);
+    }
+
+    [Fact]
+    public void Visible_False_Focuses_Deepest_Focusable_Subview ()
+    {
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        var subView = new View
+        {
+            Id = "subView",
+            CanFocus = true
+        };
+
+        var subViewSubView1 = new View
+        {
+            Id = "subViewSubView1",
+            CanFocus = false
+        };
+
+        var subViewSubView2 = new View
+        {
+            Id = "subViewSubView2",
+            CanFocus = true
+        };
+
+        var subViewSubView3 = new View
+        {
+            Id = "subViewSubView3",
+            CanFocus = true // This is the one that will be focused
+        };
+        subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.True (subView.HasFocus);
+        Assert.Equal (subView, view.GetFocused ());
+        Assert.Equal (subViewSubView2, subView.GetFocused ());
+
+        subViewSubView2.Visible = false;
+        Assert.True (subView.HasFocus);
+        Assert.Equal (subView, view.GetFocused ());
+        Assert.Equal (subViewSubView3, subView.GetFocused ());
+        Assert.True (subViewSubView3.HasFocus);
+    }
+
+    [Fact]
+    public void Visible_True_Subview_Focuses_SubView ()
+    {
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true,
+            Visible = false
+        };
+
+        var subView = new View
+        {
+            Id = "subView",
+            CanFocus = true
+        };
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.False (view.HasFocus);
+        Assert.False (subView.HasFocus);
+
+        view.Visible = true;
+        Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+    }
+
+    [Fact]
+    public void Visible_True_On_Subview_Focuses ()
+    {
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        var subView = new View
+        {
+            Id = "subView",
+            CanFocus = true,
+            Visible = false
+        };
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.False (subView.HasFocus);
+
+        subView.Visible = true;
+        Assert.True (view.HasFocus);
+        Assert.True (subView.HasFocus);
+    }
+
+    [Fact]
+    public void Visible_True_Focuses_Deepest_Focusable_Subview ()
+    {
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+
+        var subView = new View
+        {
+            Id = "subView",
+            CanFocus = true,
+            Visible = false
+        };
+
+        var subViewSubView1 = new View
+        {
+            Id = "subViewSubView1",
+            CanFocus = false
+        };
+
+        var subViewSubView2 = new View
+        {
+            Id = "subViewSubView2",
+            CanFocus = true // This is the one that will be focused
+        };
+
+        var subViewSubView3 = new View
+        {
+            Id = "subViewSubView3",
+            CanFocus = true
+        };
+        subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
+
+        view.Add (subView);
+
+        view.SetFocus ();
+        Assert.False (subView.HasFocus);
+        Assert.False (subViewSubView2.HasFocus);
+
+        subView.Visible = true;
+        Assert.True (subView.HasFocus);
+        Assert.Equal (subView, view.GetFocused ());
+        Assert.Equal (subViewSubView2, subView.GetFocused ());
+        Assert.True (subViewSubView2.HasFocus);
+    }
+}