Browse Source

Removed TabIndex etc...

Tig 11 months ago
parent
commit
1b1a2524e7

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

@@ -505,7 +505,7 @@ public partial class View // Drawing APIs
             if (TabStop == TabBehavior.TabGroup && _subviews.Count(v => v.Arrangement.HasFlag (ViewArrangement.Overlapped)) > 0)
             {
                 // TODO: This is a temporary hack to make overlapped non-Toplevels have a zorder. See also View.SetFocus
-                subviewsNeedingDraw = _tabIndexes.Where (
+                subviewsNeedingDraw = _subviews.Where (
                                                        view => view.Visible
                                                                && (view.NeedsDisplay || view.SubViewNeedsDisplay || view.LayoutNeeded)
                                                       ).Reverse ();

+ 2 - 15
Terminal.Gui/View/View.Hierarchy.cs

@@ -52,21 +52,10 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
             _subviews = new ();
         }
 
-        if (_tabIndexes is null)
-        {
-            _tabIndexes = new ();
-        }
-
         Debug.WriteLineIf (_subviews.Contains (view), $"BUGBUG: {view} has already been added to {this}.");
         _subviews.Add (view);
-        _tabIndexes.Add (view);
         view._superView = this;
 
-        if (view.CanFocus)
-        {
-            view._tabIndex = _tabIndexes.IndexOf (view);
-        }
-
         if (view.Enabled && view.Visible && view.CanFocus)
         {
             // Add will cause the newly added subview to gain focus if it's focusable
@@ -179,16 +168,14 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
         }
 
         Rectangle touched = view.Frame;
-        _subviews.Remove (view);
-        _tabIndexes!.Remove (view);
-        view._superView = null;
-        //view._tabIndex = -1;
 
         // If a view being removed is focused, it should lose focus.
         if (view.HasFocus)
         {
             view.HasFocus = false;
         }
+        _subviews.Remove (view);
+        view._superView = null; // Null this AFTER removing focus
 
         SetNeedsLayout ();
         SetNeedsDisplay ();

+ 24 - 160
Terminal.Gui/View/View.Navigation.cs

@@ -8,7 +8,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
     private bool _canFocus;
 
     /// <summary>
-    ///     Advances the focus to the next or previous view in <see cref="View.TabIndexes"/>, based on
+    ///     Advances the focus to the next or previous view in the focus chain, based on
     ///     <paramref name="direction"/>.
     ///     itself.
     /// </summary>
@@ -30,11 +30,6 @@ public partial class View // Focus and cross-view navigation management (TabStop
             return false;
         }
 
-        if (TabIndexes is null || TabIndexes.Count == 0)
-        {
-            return false;
-        }
-
         View? focused = Focused;
 
         if (focused is { } && focused.AdvanceFocus (direction, behavior))
@@ -42,14 +37,14 @@ public partial class View // Focus and cross-view navigation management (TabStop
             return true;
         }
 
-        View [] index = GetScopedTabIndexes (direction, behavior);
+        View [] index = GetSubviewFocusChain (direction, behavior);
 
         if (index.Length == 0)
         {
             return false;
         }
 
-        int focusedIndex = index.IndexOf (Focused);
+        int focusedIndex = index.IndexOf (Focused); // Will return -1 if Focused can't be found or is null
         var next = 0;
 
         if (focusedIndex < index.Length - 1)
@@ -69,7 +64,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
             //}
 
             // - If we are TabStop and our SuperView has at least one other TabStop subview, move to the SuperView's chain
-            if (TabStop == TabBehavior.TabStop && SuperView is { } && SuperView.GetScopedTabIndexes (direction, behavior).Length > 1)
+            if (TabStop == TabBehavior.TabStop && SuperView is { } && SuperView.GetSubviewFocusChain (direction, behavior).Length > 1)
             {
                 return false;
             }
@@ -81,7 +76,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
                 {
                     // Wrap to first focusable views
                     // BUGBUG: This should do a Restore Focus instead
-                    index = GetScopedTabIndexes (direction, null);
+                    index = GetSubviewFocusChain (direction, null);
                 }
             }
         }
@@ -110,10 +105,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
     ///         the next focusable view.
     ///     </para>
     ///     <para>
-    ///         When set to <see langword="false"/>, the <see cref="TabIndex"/> will be set to -1.
-    ///     </para>
-    ///     <para>
-    ///         When set to <see langword="false"/>, the values of <see cref="CanFocus"/> and <see cref="TabIndex"/> for all
+    ///         When set to <see langword="false"/>, the value of <see cref="CanFocus"/> for all
     ///         subviews will be cached so that when <see cref="CanFocus"/> is set back to <see langword="true"/>, the subviews
     ///         will be restored to their previous values.
     ///     </para>
@@ -163,8 +155,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
     public event EventHandler CanFocusChanged;
 
     /// <summary>
-    ///     Focuses the deepest focusable view in <see cref="View.TabIndexes"/> if one exists. If there are no views in
-    ///     <see cref="View.TabIndexes"/> then the focus is set to the view itself.
+    ///     Focuses the deepest focusable Subview if one exists. If there are no focusable Subviews then the focus is set to the view itself.
     /// </summary>
     /// <param name="direction"></param>
     /// <param name="behavior"></param>
@@ -244,15 +235,10 @@ public partial class View // Focus and cross-view navigation management (TabStop
 
     private View? FindDeepestFocusableView (NavigationDirection direction, TabBehavior? behavior)
     {
-        View [] indicies = GetScopedTabIndexes (direction, behavior);
+        View [] indicies = GetSubviewFocusChain (direction, behavior);
 
         foreach (View v in indicies)
         {
-            if (v.TabIndexes.Count == 0)
-            {
-                return v;
-            }
-
             return v.FindDeepestFocusableView (direction, behavior);
         }
 
@@ -580,6 +566,16 @@ public partial class View // Focus and cross-view navigation management (TabStop
         View? focusedPeer = SuperView?.Focused;
         _hasFocus = false;
 
+        if (Application.Navigation is { })
+        {
+            View? appFocused = Application.Navigation.GetFocused ();
+
+            if (appFocused is { } || appFocused == this)
+            {
+                Application.Navigation.SetFocused (SuperView);
+            }
+        }
+
         NotifyFocusChanged (HasFocus, this, newFocusedVew);
 
         if (_hasFocus)
@@ -643,14 +639,6 @@ public partial class View // Focus and cross-view navigation management (TabStop
 
     #region Tab/Focus Handling
 
-    private List<View>? _tabIndexes;
-
-    // TODO: This should be a get-only property?
-    // BUGBUG: This returns an AsReadOnly list, but isn't declared as such.
-    /// <summary>Gets a list of the subviews that are a <see cref="TabStop"/>.</summary>
-    /// <value>The tabIndexes.</value>
-    public IList<View> TabIndexes => _tabIndexes?.AsReadOnly () ?? _empty;
-
     /// <summary>
     ///     Gets TabIndexes that are scoped to the specified behavior and direction. If behavior is null, all TabIndexes are
     ///     returned.
@@ -659,138 +647,25 @@ public partial class View // Focus and cross-view navigation management (TabStop
     /// <param name="behavior"></param>
     /// <returns></returns>
     /// GetScopedTabIndexes
-    private View [] GetScopedTabIndexes (NavigationDirection direction, TabBehavior? behavior)
+    private View [] GetSubviewFocusChain (NavigationDirection direction, TabBehavior? behavior)
     {
-        IEnumerable<View>? indicies;
+        IEnumerable<View>? fitleredSubviews;
 
         if (behavior.HasValue)
         {
-            indicies = _tabIndexes?.Where (v => v.TabStop == behavior && v is { CanFocus: true, Visible: true, Enabled: true });
+            fitleredSubviews = _subviews?.Where (v => v.TabStop == behavior && v is { CanFocus: true, Visible: true, Enabled: true });
         }
         else
         {
-            indicies = _tabIndexes?.Where (v => v is { CanFocus: true, Visible: true, Enabled: true });
+            fitleredSubviews = _subviews?.Where (v => v is { CanFocus: true, Visible: true, Enabled: true });
         }
 
         if (direction == NavigationDirection.Backward)
         {
-            indicies = indicies?.Reverse ();
+            fitleredSubviews = fitleredSubviews?.Reverse ();
         }
 
-        return indicies?.ToArray () ?? Array.Empty<View> ();
-    }
-
-    private int? _tabIndex; // null indicates the view has not yet been added to TabIndexes
- 
-    /// <summary>
-    ///     Indicates the order of the current <see cref="View"/> in <see cref="TabIndexes"/> list.
-    /// </summary>
-    /// <remarks>
-    ///     <para>
-    ///         If <see langword="null"/>, the view is not part of the tab order.
-    ///     </para>
-    ///     <para>
-    ///         On set, if <see cref="SuperView"/> is <see langword="null"/> or has not TabStops, <see cref="TabIndex"/> will
-    ///         be set to 0.
-    ///     </para>
-    ///     <para>
-    ///         On set, if <see cref="SuperView"/> has only one TabStop, <see cref="TabIndex"/> will be set to 0.
-    ///     </para>
-    ///     <para>
-    ///         See also <seealso cref="TabStop"/>.
-    ///     </para>
-    /// </remarks>
-    public int? TabIndex
-    {
-        get => _tabIndex;
-
-        // TOOD: This should be a get-only property. Introduce SetTabIndex (int value) (or similar).
-        set
-        {
-            // Once a view is in the tab order, it should not be removed from the tab order; set TabStop to NoStop instead.
-            Debug.Assert (value >= 0);
-            Debug.Assert (value is { });
-
-            if (SuperView?._tabIndexes is null || SuperView?._tabIndexes.Count == 1)
-            {
-                // BUGBUG: Property setters should set the property to the value passed in and not have side effects.
-                _tabIndex = 0;
-
-                return;
-            }
-
-            if (_tabIndex == value && TabIndexes.IndexOf (this) == value)
-            {
-                return;
-            }
-
-            _tabIndex = value > SuperView!.TabIndexes.Count - 1 ? SuperView._tabIndexes.Count - 1 :
-                        value < 0 ? 0 : value;
-            _tabIndex = GetGreatestTabIndexInSuperView ((int)_tabIndex);
-
-            if (SuperView._tabIndexes.IndexOf (this) != _tabIndex)
-            {
-                // BUGBUG: we have to use _tabIndexes and not TabIndexes because TabIndexes returns is a read-only version of _tabIndexes
-                SuperView._tabIndexes.Remove (this);
-                SuperView._tabIndexes.Insert ((int)_tabIndex, this);
-                UpdatePeerTabIndexes ();
-            }
-
-            return;
-
-            // Updates the <see cref="TabIndex"/>s of the views in the <see cref="SuperView"/>'s to match their order in <see cref="TabIndexes"/>.
-            void UpdatePeerTabIndexes ()
-            {
-                if (SuperView is null)
-                {
-                    return;
-                }
-
-                var i = 0;
-
-                foreach (View superViewTabStop in SuperView._tabIndexes)
-                {
-                    if (superViewTabStop._tabIndex is null)
-                    {
-                        continue;
-                    }
-
-                    superViewTabStop._tabIndex = i;
-                    i++;
-                }
-            }
-        }
-    }
-
-    /// <summary>
-    ///     Gets the greatest <see cref="TabIndex"/> of the <see cref="SuperView"/>'s <see cref="TabIndexes"/> that is less
-    ///     than or equal to <paramref name="idx"/>.
-    /// </summary>
-    /// <param name="idx"></param>
-    /// <returns>The minimum of <paramref name="idx"/> and the <see cref="SuperView"/>'s <see cref="TabIndexes"/>.</returns>
-    private int GetGreatestTabIndexInSuperView (int idx)
-    {
-        if (SuperView is null)
-        {
-            return 0;
-        }
-
-        var i = 0;
-
-        if (SuperView._tabIndexes is { })
-        {
-            foreach (View superViewTabStop in SuperView._tabIndexes)
-            {
-                if (superViewTabStop._tabIndex is null || superViewTabStop == this)
-                {
-                    continue;
-                }
-
-                i++;
-            }
-        }
-
-        return Math.Min (i, idx);
+        return fitleredSubviews?.ToArray () ?? Array.Empty<View> ();
     }
 
     private TabBehavior? _tabStop;
@@ -828,17 +703,6 @@ public partial class View // Focus and cross-view navigation management (TabStop
                 return;
             }
 
-            Debug.Assert (value is { });
-
-            if (_tabStop is null && TabIndex is null)
-            {
-                // This view has not yet been added to TabIndexes (TabStop has not been set previously).
-                if (SuperView?._tabIndexes is { })
-                {
-                    TabIndex = GetGreatestTabIndexInSuperView (SuperView is { } ? SuperView._tabIndexes.Count : 0);
-                }
-            }
-
             _tabStop = value;
         }
     }

+ 5 - 5
Terminal.Gui/Views/FileDialog.cs

@@ -463,12 +463,12 @@ public class FileDialog : Dialog
             _btnCancel.X = Pos.Func (CalculateOkButtonPosX);
             _btnOk.X = Pos.Right (_btnCancel) + 1;
 
-            // Flip tab order too for consistency
-            int? p1 = _btnOk.TabIndex;
-            int? p2 = _btnCancel.TabIndex;
+            //// Flip tab order too for consistency
+            //int? p1 = _btnOk.TabIndex;
+            //int? p2 = _btnCancel.TabIndex;
 
-            _btnOk.TabIndex = p2;
-            _btnCancel.TabIndex = p1;
+            //_btnOk.TabIndex = p2;
+            //_btnCancel.TabIndex = p1;
         }
 
         _tbPath.Caption = Style.PathCaption;

+ 4 - 4
UICatalog/Scenarios/Dialogs.cs

@@ -311,10 +311,10 @@ public class Dialogs : Scenario
                               buttons.Add (button);
                               dialog.AddButton (button);
 
-                              if (buttons.Count > 1)
-                              {
-                                  button.TabIndex = buttons [buttons.Count - 2].TabIndex + 1;
-                              }
+                              //if (buttons.Count > 1)
+                              //{
+                              //    button.TabIndex = buttons [buttons.Count - 2].TabIndex + 1;
+                              //}
                           };
             dialog.Add (add);
 

+ 4 - 4
UICatalog/Scenarios/DynamicMenuBar.cs

@@ -640,10 +640,10 @@ public class DynamicMenuBar : Scenario
             };
             frmMenu.Add (_lstMenus);
 
-            lblMenuBar.TabIndex = btnPrevious.TabIndex + 1;
-            _lstMenus.TabIndex = lblMenuBar.TabIndex + 1;
-            btnNext.TabIndex = _lstMenus.TabIndex + 1;
-            btnAdd.TabIndex = btnNext.TabIndex + 1;
+            //lblMenuBar.TabIndex = btnPrevious.TabIndex + 1;
+            //_lstMenus.TabIndex = lblMenuBar.TabIndex + 1;
+            //btnNext.TabIndex = _lstMenus.TabIndex + 1;
+            //btnAdd.TabIndex = btnNext.TabIndex + 1;
 
             var btnRemove = new Button { X = Pos.Left (btnAdd), Y = Pos.Top (btnAdd) + 1, Text = "Remove" };
             frmMenu.Add (btnRemove);

+ 86 - 52
UnitTests/Application/Application.NavigationTests.cs

@@ -13,16 +13,16 @@ public class ApplicationNavigationTests (ITestOutputHelper output)
     {
         bool raised = false;
 
-        Application.Navigation = new ApplicationNavigation();
+        Application.Navigation = new ApplicationNavigation ();
 
         Application.Navigation.FocusedChanged += ApplicationNavigationOnFocusedChanged;
 
-        Application.Navigation.SetFocused(new View ());
+        Application.Navigation.SetFocused (new View ());
 
         Assert.True (raised);
 
-        Application.Navigation.GetFocused().Dispose ();
-        Application.Navigation.SetFocused(null);
+        Application.Navigation.GetFocused ().Dispose ();
+        Application.Navigation.SetFocused (null);
 
         Application.Navigation.FocusedChanged -= ApplicationNavigationOnFocusedChanged;
 
@@ -30,10 +30,7 @@ public class ApplicationNavigationTests (ITestOutputHelper output)
 
         return;
 
-        void ApplicationNavigationOnFocusedChanged (object sender, EventArgs e)
-        {
-            raised = true;
-        }
+        void ApplicationNavigationOnFocusedChanged (object sender, EventArgs e) { raised = true; }
     }
 
     [Fact]
@@ -50,7 +47,8 @@ public class ApplicationNavigationTests (ITestOutputHelper output)
     public void GetDeepestFocusedSubview_ShouldReturnSameView_WhenNoSubviewsHaveFocus ()
     {
         // Arrange
-        var view = new View () { Id = "view", CanFocus = true }; ;
+        var view = new View () { Id = "view", CanFocus = true };
+        ;
 
         // Act
         var result = ApplicationNavigation.GetDeepestFocusedSubview (view);
@@ -63,10 +61,14 @@ public class ApplicationNavigationTests (ITestOutputHelper output)
     public void GetDeepestFocusedSubview_ShouldReturnFocusedSubview ()
     {
         // Arrange
-        var parentView = new View () { Id = "parentView", CanFocus = true }; ;
-        var childView1 = new View () { Id = "childView1", CanFocus = true }; ;
-        var childView2 = new View () { Id = "childView2", CanFocus = true }; ;
-        var grandChildView = new View () { Id = "grandChildView", CanFocus = true }; ;
+        var parentView = new View () { Id = "parentView", CanFocus = true };
+        ;
+        var childView1 = new View () { Id = "childView1", CanFocus = true };
+        ;
+        var childView2 = new View () { Id = "childView2", CanFocus = true };
+        ;
+        var grandChildView = new View () { Id = "grandChildView", CanFocus = true };
+        ;
 
         parentView.Add (childView1, childView2);
         childView2.Add (grandChildView);
@@ -84,11 +86,16 @@ public class ApplicationNavigationTests (ITestOutputHelper output)
     public void GetDeepestFocusedSubview_ShouldReturnDeepestFocusedSubview ()
     {
         // Arrange
-        var parentView = new View () { Id = "parentView", CanFocus = true }; ;
-        var childView1 = new View () { Id = "childView1", CanFocus = true }; ;
-        var childView2 = new View () { Id = "childView2", CanFocus = true }; ;
-        var grandChildView = new View () { Id = "grandChildView", CanFocus = true }; ;
-        var greatGrandChildView = new View () { Id = "greatGrandChildView", CanFocus = true }; ;
+        var parentView = new View () { Id = "parentView", CanFocus = true };
+        ;
+        var childView1 = new View () { Id = "childView1", CanFocus = true };
+        ;
+        var childView2 = new View () { Id = "childView2", CanFocus = true };
+        ;
+        var grandChildView = new View () { Id = "grandChildView", CanFocus = true };
+        ;
+        var greatGrandChildView = new View () { Id = "greatGrandChildView", CanFocus = true };
+        ;
 
         parentView.Add (childView1, childView2);
         childView2.Add (grandChildView);
@@ -113,48 +120,75 @@ public class ApplicationNavigationTests (ITestOutputHelper output)
         Assert.Equal (grandChildView, result);
     }
 
+    [Fact]
+    public void GetFocused_Returns_Null_If_No_Focused_View ()
+    {
+        Application.Navigation = new ();
 
-    //[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 ();
+        Application.Current = new Toplevel()
+        {
+            Id = "top",
+            CanFocus = true
+        };
 
-    //    // Act
-    //    ApplicationNavigation.MoveNextViewOrTop ();
+        View subView1 = new View ()
+        {
+            Id = "subView1",
+            CanFocus = true
+        };
 
-    //    // Assert
-    //    Assert.True (view2.HasFocus);
+        Application.Current.Add (subView1);
+        Assert.False (Application.Current.HasFocus);
 
-    //    top.Dispose ();
-    //}
+        Application.Current.SetFocus ();
+        Assert.True (subView1.HasFocus);
+        Assert.Equal (subView1, Application.Navigation.GetFocused ());
 
+        subView1.HasFocus = false;
+        Assert.False (subView1.HasFocus);
+        Assert.True (Application.Current.HasFocus);
+        Assert.Equal (Application.Current, Application.Navigation.GetFocused ());
 
+        Application.Current.HasFocus = false;
+        Assert.False (Application.Current.HasFocus);
+        Assert.Null (Application.Navigation.GetFocused ());
 
-    //[Fact]
-    //public void MovePreviousViewOrTop_ShouldMoveFocusToPreviousViewOrTop ()
-    //{
-    //    // Arrange
-    //    var top = new Toplevel ();
-    //    var view1 = new View () { Id = "view1", CanFocus = true, TabStop = TabBehavior.TabGroup };
-    //    var view2 = new View () { Id = "view2", CanFocus = true, TabStop = TabBehavior.TabGroup };
-    //    top.Add (view1, view2);
-    //    Application.Top = top;
-    //    Application.Current = top;
-    //    view2.SetFocus ();
+        Application.ResetState ();
+    }
 
-    //    // Act
-    //    ApplicationNavigation.MovePreviousViewOrTop ();
 
-    //    // Assert
-    //    Assert.True (view1.HasFocus);
+    [Fact]
+    public void GetFocused_Returns_Focused_View ()
+    {
+        Application.Navigation = new ();
 
-    //    top.Dispose ();
-    //}
+        Application.Current = new Toplevel ()
+        {
+            Id = "top",
+            CanFocus = true
+        };
+
+        View subView1 = new View ()
+        {
+            Id = "subView1",
+            CanFocus = true
+        };
+
+        View subView2 = new View ()
+        {
+            Id = "subView2",
+            CanFocus = true
+        };
+        Application.Current.Add (subView1, subView2);
+        Assert.False (Application.Current.HasFocus);
+
+        Application.Current.SetFocus ();
+        Assert.True (subView1.HasFocus);
+        Assert.Equal(subView1, Application.Navigation.GetFocused());
+
+        Application.Current.AdvanceFocus (NavigationDirection.Forward, null);
+        Assert.Equal (subView2, Application.Navigation.GetFocused ());
+
+        Application.ResetState ();
+    }
 }

+ 78 - 0
UnitTests/View/Navigation/AddRemoveTests.cs

@@ -98,6 +98,83 @@ public class AddRemoveNavigationTests (ITestOutputHelper _output) : TestsAllView
         Assert.True (subSubView.HasFocus);
     }
 
+
+    [Fact]
+    public void Remove_Subview_Raises_HasFocusChanged ()
+    {
+        var top = new View
+        {
+            Id = "top",
+            CanFocus = true
+        };
+
+        var subView1 = new View
+        {
+            Id = "subView1",
+            CanFocus = true
+        };
+
+        var subView2 = new View
+        {
+            Id = "subView2",
+            CanFocus = true
+        };
+        top.Add (subView1, subView2);
+
+        var subView1HasFocusChangedTrueCount = 0;
+        var subView1HasFocusChangedFalseCount = 0;
+
+        subView1.HasFocusChanged += (s, e) =>
+        {
+            if (e.NewValue)
+            {
+                subView1HasFocusChangedTrueCount++;
+            }
+            else
+            {
+                subView1HasFocusChangedFalseCount++;
+            }
+        };
+
+        var subView2HasFocusChangedTrueCount = 0;
+        var subView2HasFocusChangedFalseCount = 0;
+
+        subView2.HasFocusChanged += (s, e) =>
+        {
+            if (e.NewValue)
+            {
+                subView2HasFocusChangedTrueCount++;
+            }
+            else
+            {
+                subView2HasFocusChangedFalseCount++;
+            }
+        };
+
+        top.SetFocus ();
+        Assert.True (top.HasFocus);
+        Assert.True (subView1.HasFocus);
+        Assert.False (subView2.HasFocus);
+
+        Assert.Equal (1, subView1HasFocusChangedTrueCount);
+        Assert.Equal (0, subView1HasFocusChangedFalseCount);
+
+        Assert.Equal (0, subView2HasFocusChangedTrueCount);
+        Assert.Equal (0, subView2HasFocusChangedFalseCount);
+
+        top.Remove (subView1); // this should have the same resuilt as top.AdvanceFocus (NavigationDirection.Forward, null);
+
+        Assert.False (subView1.HasFocus);
+        Assert.True (subView2.HasFocus);
+
+        Assert.Equal (1, subView1HasFocusChangedTrueCount);
+        Assert.Equal (1, subView1HasFocusChangedFalseCount);
+
+        Assert.Equal (1, subView2HasFocusChangedTrueCount);
+        Assert.Equal (0, subView2HasFocusChangedFalseCount);
+    }
+
+
     [Fact]
     public void Remove_Focused_Subview_Keeps_Focus_And_SubView_Looses_Focus ()
     {
@@ -155,6 +232,7 @@ public class AddRemoveNavigationTests (ITestOutputHelper _output) : TestsAllView
         Assert.False (subView2.HasFocus);
 
         top.Remove (subView1);
+
         Assert.True (top.HasFocus);
         Assert.True (subView2.HasFocus);
         Assert.Equal (subView2, top.Focused);

+ 257 - 274
UnitTests/View/Navigation/AdvanceFocusTests.cs

@@ -5,211 +5,82 @@ namespace Terminal.Gui.ViewTests;
 public class AdvanceFocusTests (ITestOutputHelper _output)
 {
     [Fact]
-    public void Subviews_TabIndexes_AreEqual ()
-    {
-        var r = new View ();
-        var v1 = new View { CanFocus = true };
-        var v2 = new View { CanFocus = true };
-        var v3 = new View { CanFocus = true };
-
-        r.Add (v1, v2, v3);
-
-        Assert.True (r.Subviews.IndexOf (v1) == 0);
-        Assert.True (r.Subviews.IndexOf (v2) == 1);
-        Assert.True (r.Subviews.IndexOf (v3) == 2);
-
-        Assert.True (r.TabIndexes.IndexOf (v1) == 0);
-        Assert.True (r.TabIndexes.IndexOf (v2) == 1);
-        Assert.True (r.TabIndexes.IndexOf (v3) == 2);
-
-        Assert.Equal (r.Subviews.IndexOf (v1), r.TabIndexes.IndexOf (v1));
-        Assert.Equal (r.Subviews.IndexOf (v2), r.TabIndexes.IndexOf (v2));
-        Assert.Equal (r.Subviews.IndexOf (v3), r.TabIndexes.IndexOf (v3));
-        r.Dispose ();
-    }
-
-    [Fact]
-    public void TabIndex_Invert_Order ()
-    {
-        var r = new View ();
-        var v1 = new View { Id = "1", CanFocus = true };
-        var v2 = new View { Id = "2", CanFocus = true };
-        var v3 = new View { Id = "3", CanFocus = true };
-
-        r.Add (v1, v2, v3);
-
-        v1.TabIndex = 2;
-        v2.TabIndex = 1;
-        v3.TabIndex = 0;
-        Assert.True (r.TabIndexes.IndexOf (v1) == 2);
-        Assert.True (r.TabIndexes.IndexOf (v2) == 1);
-        Assert.True (r.TabIndexes.IndexOf (v3) == 0);
-
-        Assert.True (r.Subviews.IndexOf (v1) == 0);
-        Assert.True (r.Subviews.IndexOf (v2) == 1);
-        Assert.True (r.Subviews.IndexOf (v3) == 2);
-    }
-
-    [Fact]
-    public void TabIndex_Invert_Order_Added_One_By_One_Does_Not_Do_What_Is_Expected ()
-    {
-        var r = new View ();
-        var v1 = new View { Id = "1", CanFocus = true };
-        r.Add (v1);
-        v1.TabIndex = 2;
-        var v2 = new View { Id = "2", CanFocus = true };
-        r.Add (v2);
-        v2.TabIndex = 1;
-        var v3 = new View { Id = "3", CanFocus = true };
-        r.Add (v3);
-        v3.TabIndex = 0;
-
-        Assert.False (r.TabIndexes.IndexOf (v1) == 2);
-        Assert.True (r.TabIndexes.IndexOf (v1) == 1);
-        Assert.False (r.TabIndexes.IndexOf (v2) == 1);
-        Assert.True (r.TabIndexes.IndexOf (v2) == 2);
-
-        // Only the last is in the expected index
-        Assert.True (r.TabIndexes.IndexOf (v3) == 0);
-
-        Assert.True (r.Subviews.IndexOf (v1) == 0);
-        Assert.True (r.Subviews.IndexOf (v2) == 1);
-        Assert.True (r.Subviews.IndexOf (v3) == 2);
-    }
-
-    [Fact]
-    public void TabIndex_Invert_Order_Mixed ()
-    {
-        var r = new View ();
-        var vl1 = new View { Id = "vl1" };
-        var v1 = new View { Id = "v1", CanFocus = true };
-        var vl2 = new View { Id = "vl2" };
-        var v2 = new View { Id = "v2", CanFocus = true };
-        var vl3 = new View { Id = "vl3" };
-        var v3 = new View { Id = "v3", CanFocus = true };
-
-        r.Add (vl1, v1, vl2, v2, vl3, v3);
-
-        v1.TabIndex = 2;
-        v2.TabIndex = 1;
-        v3.TabIndex = 0;
-        Assert.True (r.TabIndexes.IndexOf (v1) == 4);
-        Assert.True (r.TabIndexes.IndexOf (v2) == 2);
-        Assert.True (r.TabIndexes.IndexOf (v3) == 0);
-
-        Assert.True (r.Subviews.IndexOf (v1) == 1);
-        Assert.True (r.Subviews.IndexOf (v2) == 3);
-        Assert.True (r.Subviews.IndexOf (v3) == 5);
-    }
-
-    [Fact]
-    public void TabIndex_Set_CanFocus_False ()
+    public void AdvanceFocus_CanFocus_Mixed ()
     {
         var r = new View ();
-        var v1 = new View { CanFocus = true };
-        var v2 = new View { CanFocus = true };
-        var v3 = new View { CanFocus = true };
+        var v1 = new View { CanFocus = true, TabStop = TabBehavior.NoStop };
+        var v2 = new View { CanFocus = false, TabStop = TabBehavior.TabStop };
+        var v3 = new View { CanFocus = false, TabStop = TabBehavior.NoStop };
 
         r.Add (v1, v2, v3);
 
-        v1.CanFocus = false;
-        v1.TabIndex = 0;
-        Assert.True (r.Subviews.IndexOf (v1) == 0);
-        Assert.True (r.TabIndexes.IndexOf (v1) == 0);
-        Assert.NotEqual (-1, v1.TabIndex);
+        r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
+        Assert.False (v1.HasFocus);
+        Assert.False (v2.HasFocus);
+        Assert.False (v3.HasFocus);
+        r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
+        Assert.False (v1.HasFocus);
+        Assert.False (v2.HasFocus);
+        Assert.False (v3.HasFocus);
+        r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
+        Assert.False (v1.HasFocus);
+        Assert.False (v2.HasFocus);
+        Assert.False (v3.HasFocus);
         r.Dispose ();
     }
 
-    [Fact]
-    public void TabIndex_Set_CanFocus_False_To_True ()
+    [Theory]
+    [CombinatorialData]
+    public void AdvanceFocus_Change_CanFocus_Works ([CombinatorialValues (TabBehavior.NoStop, TabBehavior.TabStop, TabBehavior.TabGroup)] TabBehavior behavior)
     {
-        var r = new View ();
+        var r = new View { CanFocus = true };
         var v1 = new View ();
-        var v2 = new View { CanFocus = true };
-        var v3 = new View { CanFocus = true };
-
-        r.Add (v1, v2, v3);
-
-        v1.CanFocus = true;
-        v1.TabIndex = 1;
-        Assert.True (r.Subviews.IndexOf (v1) == 0);
-        Assert.True (r.TabIndexes.IndexOf (v1) == 1);
-        r.Dispose ();
-    }
-
-    [Fact]
-    public void TabIndex_Set_CanFocus_HigherValues ()
-    {
-        var r = new View ();
-        var v1 = new View { CanFocus = true };
-        var v2 = new View { CanFocus = true };
-        var v3 = new View { CanFocus = true };
-
-        r.Add (v1, v2, v3);
-
-        v1.TabIndex = 3;
-        Assert.True (r.Subviews.IndexOf (v1) == 0);
-        Assert.True (r.TabIndexes.IndexOf (v1) == 2);
-        r.Dispose ();
-    }
-
-    [Fact]
-    public void TabIndex_Set_CanFocus_LowerValues ()
-    {
-        var r = new View ();
-        var v1 = new View { CanFocus = true };
-        var v2 = new View { CanFocus = true };
-        var v3 = new View { CanFocus = true };
+        var v2 = new View ();
+        var v3 = new View ();
+        Assert.True (r.CanFocus);
+        Assert.False (v1.CanFocus);
+        Assert.False (v2.CanFocus);
+        Assert.False (v3.CanFocus);
 
         r.Add (v1, v2, v3);
 
-        //v1.TabIndex = -1;
-        Assert.True (r.Subviews.IndexOf (v1) == 0);
-        Assert.True (r.TabIndexes.IndexOf (v1) == 0);
-        r.Dispose ();
-    }
-
-    [Fact]
-    public void TabIndex_Set_CanFocus_ValidValues ()
-    {
-        var r = new View ();
-        var v1 = new View { CanFocus = true };
-        var v2 = new View { CanFocus = true };
-        var v3 = new View { CanFocus = true };
+        r.AdvanceFocus (NavigationDirection.Forward, behavior);
+        Assert.False (v1.HasFocus);
+        Assert.False (v2.HasFocus);
+        Assert.False (v3.HasFocus);
 
-        r.Add (v1, v2, v3);
+        v1.CanFocus = true;
+        v1.TabStop = behavior;
+        r.AdvanceFocus (NavigationDirection.Forward, behavior);
+        Assert.True (v1.HasFocus);
+        Assert.False (v2.HasFocus);
+        Assert.False (v3.HasFocus);
 
-        v1.TabIndex = 1;
-        Assert.True (r.Subviews.IndexOf (v1) == 0);
-        Assert.True (r.TabIndexes.IndexOf (v1) == 1);
+        v2.CanFocus = true;
+        v2.TabStop = behavior;
+        r.AdvanceFocus (NavigationDirection.Forward, behavior);
+        Assert.False (v1.HasFocus);
+        Assert.True (v2.HasFocus);
+        Assert.False (v3.HasFocus);
 
-        v1.TabIndex = 2;
-        Assert.True (r.Subviews.IndexOf (v1) == 0);
-        Assert.True (r.TabIndexes.IndexOf (v1) == 2);
+        v3.CanFocus = true;
+        v3.TabStop = behavior;
+        r.AdvanceFocus (NavigationDirection.Forward, behavior);
+        Assert.False (v1.HasFocus);
+        Assert.False (v2.HasFocus);
+        Assert.True (v3.HasFocus);
         r.Dispose ();
     }
 
-
-    [Theory]
-    [CombinatorialData]
-    public void TabStop_And_CanFocus_Are_Decoupled (bool canFocus, TabBehavior tabStop)
-    {
-        var view = new View { CanFocus = canFocus, TabStop = tabStop };
-
-        Assert.Equal (canFocus, view.CanFocus);
-        Assert.Equal (tabStop, view.TabStop);
-    }
-
-
     [Fact]
     public void AdvanceFocus_Compound_Subview ()
     {
-        var top = new View () { Id = "top", CanFocus = true };
+        var top = new View { Id = "top", CanFocus = true };
 
-        var compoundSubview = new View ()
+        var compoundSubview = new View
         {
             CanFocus = true,
-            Id = "compoundSubview",
+            Id = "compoundSubview"
         };
         var v1 = new View { Id = "v1", CanFocus = true };
         var v2 = new View { Id = "v2", CanFocus = true };
@@ -237,10 +108,11 @@ public class AdvanceFocusTests (ITestOutputHelper _output)
         View otherSubview = new ()
         {
             CanFocus = true,
-            Id = "otherSubview",
+            Id = "otherSubview"
         };
 
         top.Add (otherSubview);
+
         // Adding a focusable subview causes advancefocus
         Assert.True (otherSubview.HasFocus);
         Assert.False (v1.HasFocus);
@@ -260,6 +132,7 @@ public class AdvanceFocusTests (ITestOutputHelper _output)
         Assert.False (v3.HasFocus);
 
         Assert.True (otherSubview.HasFocus);
+
         // v2 was previously focused down the compoundSubView focus chain
         top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
         Assert.False (v1.HasFocus);
@@ -269,99 +142,6 @@ public class AdvanceFocusTests (ITestOutputHelper _output)
         top.Dispose ();
     }
 
-    [Fact]
-    public void AdvanceFocus_With_CanFocus_Are_All_True ()
-    {
-        var top = new View () { Id = "top", CanFocus = true };
-        var v1 = new View { Id = "v1", CanFocus = true };
-        var v2 = new View { Id = "v2", CanFocus = true };
-        var v3 = new View { Id = "v3", CanFocus = true };
-
-        top.Add (v1, v2, v3);
-
-        top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
-        Assert.True (v1.HasFocus);
-        Assert.False (v2.HasFocus);
-        Assert.False (v3.HasFocus);
-        top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
-        Assert.False (v1.HasFocus);
-        Assert.True (v2.HasFocus);
-        Assert.False (v3.HasFocus);
-        top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
-        Assert.False (v1.HasFocus);
-        Assert.False (v2.HasFocus);
-        Assert.True (v3.HasFocus);
-        top.Dispose ();
-    }
-
-    [Fact]
-    public void AdvanceFocus_CanFocus_Mixed ()
-    {
-        var r = new View ();
-        var v1 = new View { CanFocus = true, TabStop = TabBehavior.NoStop };
-        var v2 = new View { CanFocus = false, TabStop = TabBehavior.TabStop };
-        var v3 = new View { CanFocus = false, TabStop = TabBehavior.NoStop };
-
-        r.Add (v1, v2, v3);
-
-        r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
-        Assert.False (v1.HasFocus);
-        Assert.False (v2.HasFocus);
-        Assert.False (v3.HasFocus);
-        r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
-        Assert.False (v1.HasFocus);
-        Assert.False (v2.HasFocus);
-        Assert.False (v3.HasFocus);
-        r.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
-        Assert.False (v1.HasFocus);
-        Assert.False (v2.HasFocus);
-        Assert.False (v3.HasFocus);
-        r.Dispose ();
-    }
-
-    [Theory]
-    [CombinatorialData]
-    public void AdvanceFocus_Change_CanFocus_Works ([CombinatorialValues (TabBehavior.NoStop, TabBehavior.TabStop, TabBehavior.TabGroup)] TabBehavior behavior)
-    {
-        var r = new View () { CanFocus = true };
-        var v1 = new View ();
-        var v2 = new View ();
-        var v3 = new View ();
-        Assert.True (r.CanFocus);
-        Assert.False (v1.CanFocus);
-        Assert.False (v2.CanFocus);
-        Assert.False (v3.CanFocus);
-
-        r.Add (v1, v2, v3);
-
-        r.AdvanceFocus (NavigationDirection.Forward, behavior);
-        Assert.False (v1.HasFocus);
-        Assert.False (v2.HasFocus);
-        Assert.False (v3.HasFocus);
-
-        v1.CanFocus = true;
-        v1.TabStop = behavior;
-        r.AdvanceFocus (NavigationDirection.Forward, behavior);
-        Assert.True (v1.HasFocus);
-        Assert.False (v2.HasFocus);
-        Assert.False (v3.HasFocus);
-
-        v2.CanFocus = true;
-        v2.TabStop = behavior;
-        r.AdvanceFocus (NavigationDirection.Forward, behavior);
-        Assert.False (v1.HasFocus);
-        Assert.True (v2.HasFocus);
-        Assert.False (v3.HasFocus);
-
-        v3.CanFocus = true;
-        v3.TabStop = behavior;
-        r.AdvanceFocus (NavigationDirection.Forward, behavior);
-        Assert.False (v1.HasFocus);
-        Assert.False (v2.HasFocus);
-        Assert.True (v3.HasFocus);
-        r.Dispose ();
-    }
-
     [Fact]
     public void AdvanceFocus_NoStop_And_CanFocus_True_No_Focus ()
     {
@@ -459,4 +239,207 @@ public class AdvanceFocusTests (ITestOutputHelper _output)
         Assert.False (v3.HasFocus);
         r.Dispose ();
     }
+
+    [Fact]
+    public void AdvanceFocus_Subviews_Raises_HasFocusChanged ()
+    {
+        var top = new View
+        {
+            Id = "top",
+            CanFocus = true
+        };
+
+        var subView1 = new View
+        {
+            Id = "subView1",
+            CanFocus = true
+        };
+
+        var subView2 = new View
+        {
+            Id = "subView2",
+            CanFocus = true
+        };
+        top.Add (subView1, subView2);
+
+        var subView1HasFocusChangedTrueCount = 0;
+        var subView1HasFocusChangedFalseCount = 0;
+
+        subView1.HasFocusChanged += (s, e) =>
+                                    {
+                                        if (e.NewValue)
+                                        {
+                                            subView1HasFocusChangedTrueCount++;
+                                        }
+                                        else
+                                        {
+                                            subView1HasFocusChangedFalseCount++;
+                                        }
+                                    };
+
+        var subView2HasFocusChangedTrueCount = 0;
+        var subView2HasFocusChangedFalseCount = 0;
+
+        subView2.HasFocusChanged += (s, e) =>
+                                    {
+                                        if (e.NewValue)
+                                        {
+                                            subView2HasFocusChangedTrueCount++;
+                                        }
+                                        else
+                                        {
+                                            subView2HasFocusChangedFalseCount++;
+                                        }
+                                    };
+
+        top.SetFocus ();
+        Assert.True (top.HasFocus);
+        Assert.True (subView1.HasFocus);
+        Assert.False (subView2.HasFocus);
+
+        Assert.Equal (1, subView1HasFocusChangedTrueCount);
+        Assert.Equal (0, subView1HasFocusChangedFalseCount);
+
+        Assert.Equal (0, subView2HasFocusChangedTrueCount);
+        Assert.Equal (0, subView2HasFocusChangedFalseCount);
+
+        top.AdvanceFocus (NavigationDirection.Forward, null);
+        Assert.False (subView1.HasFocus);
+        Assert.True (subView2.HasFocus);
+
+        Assert.Equal (1, subView1HasFocusChangedTrueCount);
+        Assert.Equal (1, subView1HasFocusChangedFalseCount);
+
+        Assert.Equal (1, subView2HasFocusChangedTrueCount);
+        Assert.Equal (0, subView2HasFocusChangedFalseCount);
+
+        top.AdvanceFocus (NavigationDirection.Forward, null);
+        Assert.True (subView1.HasFocus);
+        Assert.False (subView2.HasFocus);
+
+        Assert.Equal (2, subView1HasFocusChangedTrueCount);
+        Assert.Equal (1, subView1HasFocusChangedFalseCount);
+
+        Assert.Equal (1, subView2HasFocusChangedTrueCount);
+        Assert.Equal (1, subView2HasFocusChangedFalseCount);
+    }
+
+    [Fact]
+    public void AdvanceFocus_Subviews_Raises_HasFocusChanging ()
+    {
+        var top = new View
+        {
+            Id = "top",
+            CanFocus = true
+        };
+
+        var subView1 = new View
+        {
+            Id = "subView1",
+            CanFocus = true
+        };
+
+        var subView2 = new View
+        {
+            Id = "subView2",
+            CanFocus = true
+        };
+        top.Add (subView1, subView2);
+
+        var subView1HasFocusChangingTrueCount = 0;
+        var subView1HasFocusChangingFalseCount = 0;
+
+        subView1.HasFocusChanging += (s, e) =>
+                                     {
+                                         if (e.NewValue)
+                                         {
+                                             subView1HasFocusChangingTrueCount++;
+                                         }
+                                         else
+                                         {
+                                             subView1HasFocusChangingFalseCount++;
+                                         }
+                                     };
+
+        var subView2HasFocusChangingTrueCount = 0;
+        var subView2HasFocusChangingFalseCount = 0;
+
+        subView2.HasFocusChanging += (s, e) =>
+                                     {
+                                         if (e.NewValue)
+                                         {
+                                             subView2HasFocusChangingTrueCount++;
+                                         }
+                                         else
+                                         {
+                                             subView2HasFocusChangingFalseCount++;
+                                         }
+                                     };
+
+        top.SetFocus ();
+        Assert.True (top.HasFocus);
+        Assert.True (subView1.HasFocus);
+        Assert.False (subView2.HasFocus);
+
+        Assert.Equal (1, subView1HasFocusChangingTrueCount);
+        Assert.Equal (0, subView1HasFocusChangingFalseCount);
+
+        Assert.Equal (0, subView2HasFocusChangingTrueCount);
+        Assert.Equal (0, subView2HasFocusChangingFalseCount);
+
+        top.AdvanceFocus (NavigationDirection.Forward, null);
+        Assert.False (subView1.HasFocus);
+        Assert.True (subView2.HasFocus);
+
+        Assert.Equal (1, subView1HasFocusChangingTrueCount);
+        Assert.Equal (1, subView1HasFocusChangingFalseCount);
+
+        Assert.Equal (1, subView2HasFocusChangingTrueCount);
+        Assert.Equal (0, subView2HasFocusChangingFalseCount);
+
+        top.AdvanceFocus (NavigationDirection.Forward, null);
+        Assert.True (subView1.HasFocus);
+        Assert.False (subView2.HasFocus);
+
+        Assert.Equal (2, subView1HasFocusChangingTrueCount);
+        Assert.Equal (1, subView1HasFocusChangingFalseCount);
+
+        Assert.Equal (1, subView2HasFocusChangingTrueCount);
+        Assert.Equal (1, subView2HasFocusChangingFalseCount);
+    }
+
+    [Fact]
+    public void AdvanceFocus_With_CanFocus_Are_All_True ()
+    {
+        var top = new View { Id = "top", CanFocus = true };
+        var v1 = new View { Id = "v1", CanFocus = true };
+        var v2 = new View { Id = "v2", CanFocus = true };
+        var v3 = new View { Id = "v3", CanFocus = true };
+
+        top.Add (v1, v2, v3);
+
+        top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
+        Assert.True (v1.HasFocus);
+        Assert.False (v2.HasFocus);
+        Assert.False (v3.HasFocus);
+        top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
+        Assert.False (v1.HasFocus);
+        Assert.True (v2.HasFocus);
+        Assert.False (v3.HasFocus);
+        top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
+        Assert.False (v1.HasFocus);
+        Assert.False (v2.HasFocus);
+        Assert.True (v3.HasFocus);
+        top.Dispose ();
+    }
+
+    [Theory]
+    [CombinatorialData]
+    public void TabStop_And_CanFocus_Are_Decoupled (bool canFocus, TabBehavior tabStop)
+    {
+        var view = new View { CanFocus = canFocus, TabStop = tabStop };
+
+        Assert.Equal (canFocus, view.CanFocus);
+        Assert.Equal (tabStop, view.TabStop);
+    }
 }

+ 42 - 42
UnitTests/View/Navigation/CanFocusTests.cs

@@ -244,48 +244,48 @@ public class CanFocusTests (ITestOutputHelper _output) : TestsAllViews
         Application.Shutdown ();
     }
 
-    [Fact]
-    public void CanFocus_Set_Changes_TabIndex_And_TabStop ()
-    {
-        var r = new View ();
-        var v1 = new View { Text = "1" };
-        var v2 = new View { Text = "2" };
-        var v3 = new View { Text = "3" };
-
-        r.Add (v1, v2, v3);
-
-        v2.CanFocus = true;
-        Assert.Equal (r.TabIndexes.IndexOf (v2), v2.TabIndex);
-        Assert.Equal (0, v2.TabIndex);
-        Assert.Equal (TabBehavior.TabStop, v2.TabStop);
-
-        v1.CanFocus = true;
-        Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex);
-        Assert.Equal (1, v1.TabIndex);
-        Assert.Equal (TabBehavior.TabStop, v1.TabStop);
-
-        v1.TabIndex = 2;
-        Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex);
-        Assert.Equal (1, v1.TabIndex);
-        v3.CanFocus = true;
-        Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex);
-        Assert.Equal (1, v1.TabIndex);
-        Assert.Equal (r.TabIndexes.IndexOf (v3), v3.TabIndex);
-        Assert.Equal (2, v3.TabIndex);
-        Assert.Equal (TabBehavior.TabStop, v3.TabStop);
-
-        v2.CanFocus = false;
-        Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex);
-        Assert.Equal (1, v1.TabIndex);
-        Assert.Equal (TabBehavior.TabStop, v1.TabStop);
-        Assert.Equal (r.TabIndexes.IndexOf (v2), v2.TabIndex); // TabIndex is not changed
-        Assert.NotEqual (-1, v2.TabIndex);
-        Assert.Equal (TabBehavior.TabStop, v2.TabStop); // TabStop is not changed
-        Assert.Equal (r.TabIndexes.IndexOf (v3), v3.TabIndex);
-        Assert.Equal (2, v3.TabIndex);
-        Assert.Equal (TabBehavior.TabStop, v3.TabStop);
-        r.Dispose ();
-    }
+    //[Fact]
+    //public void CanFocus_Set_Changes_TabIndex_And_TabStop ()
+    //{
+    //    var r = new View ();
+    //    var v1 = new View { Text = "1" };
+    //    var v2 = new View { Text = "2" };
+    //    var v3 = new View { Text = "3" };
+
+    //    r.Add (v1, v2, v3);
+
+    //    v2.CanFocus = true;
+    //    Assert.Equal (r.TabIndexes.IndexOf (v2), v2.TabIndex);
+    //    Assert.Equal (0, v2.TabIndex);
+    //    Assert.Equal (TabBehavior.TabStop, v2.TabStop);
+
+    //    v1.CanFocus = true;
+    //    Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex);
+    //    Assert.Equal (1, v1.TabIndex);
+    //    Assert.Equal (TabBehavior.TabStop, v1.TabStop);
+
+    //    v1.TabIndex = 2;
+    //    Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex);
+    //    Assert.Equal (1, v1.TabIndex);
+    //    v3.CanFocus = true;
+    //    Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex);
+    //    Assert.Equal (1, v1.TabIndex);
+    //    Assert.Equal (r.TabIndexes.IndexOf (v3), v3.TabIndex);
+    //    Assert.Equal (2, v3.TabIndex);
+    //    Assert.Equal (TabBehavior.TabStop, v3.TabStop);
+
+    //    v2.CanFocus = false;
+    //    Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex);
+    //    Assert.Equal (1, v1.TabIndex);
+    //    Assert.Equal (TabBehavior.TabStop, v1.TabStop);
+    //    Assert.Equal (r.TabIndexes.IndexOf (v2), v2.TabIndex); // TabIndex is not changed
+    //    Assert.NotEqual (-1, v2.TabIndex);
+    //    Assert.Equal (TabBehavior.TabStop, v2.TabStop); // TabStop is not changed
+    //    Assert.Equal (r.TabIndexes.IndexOf (v3), v3.TabIndex);
+    //    Assert.Equal (2, v3.TabIndex);
+    //    Assert.Equal (TabBehavior.TabStop, v3.TabStop);
+    //    r.Dispose ();
+    //}
 
     [Fact]
     public void CanFocus_True_Focuses ()

+ 67 - 32
UnitTests/View/Navigation/HasFocusChangeEventTests.cs

@@ -5,6 +5,7 @@ namespace Terminal.Gui.ViewTests;
 public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllViews
 {
     #region HasFocusChanging_NewValue_True
+
     [Fact]
     public void HasFocusChanging_SetFocus_Raises ()
     {
@@ -16,6 +17,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "view",
             CanFocus = true
         };
+
         view.HasFocusChanging += (s, e) =>
                                  {
                                      if (e.NewValue)
@@ -49,6 +51,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "view",
             CanFocus = true
         };
+
         view.HasFocusChanging += (s, e) =>
                                  {
                                      if (e.NewValue)
@@ -69,17 +72,18 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subview",
             CanFocus = true
         };
+
         subview.HasFocusChanging += (s, e) =>
-                                 {
-                                     if (e.NewValue)
-                                     {
-                                         subviewHasFocusTrueCount++;
-                                     }
-                                     else
-                                     {
-                                         subviewHasFocusFalseCount++;
-                                     }
-                                 };
+                                    {
+                                        if (e.NewValue)
+                                        {
+                                            subviewHasFocusTrueCount++;
+                                        }
+                                        else
+                                        {
+                                            subviewHasFocusFalseCount++;
+                                        }
+                                    };
 
         view.Add (subview);
 
@@ -104,6 +108,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "view",
             CanFocus = true
         };
+
         view.HasFocusChanging += (s, e) =>
                                  {
                                      if (e.NewValue)
@@ -124,6 +129,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subview",
             CanFocus = true
         };
+
         subview.HasFocusChanging += (s, e) =>
                                     {
                                         if (e.NewValue)
@@ -158,6 +164,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "view",
             CanFocus = true
         };
+
         view.HasFocusChanging += (s, e) =>
                                  {
                                      if (e.NewValue)
@@ -178,6 +185,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subView",
             CanFocus = true
         };
+
         subView.HasFocusChanging += (s, e) =>
                                     {
                                         if (e.NewValue)
@@ -198,6 +206,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subViewSubView1",
             CanFocus = false
         };
+
         subViewSubView1.HasFocusChanging += (s, e) =>
                                             {
                                                 if (e.NewValue)
@@ -218,6 +227,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subViewSubView2",
             CanFocus = true
         };
+
         subViewSubView2.HasFocusChanging += (s, e) =>
                                             {
                                                 if (e.NewValue)
@@ -238,6 +248,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subViewSubView3",
             CanFocus = false
         };
+
         subViewSubView3.HasFocusChanging += (s, e) =>
                                             {
                                                 if (e.NewValue)
@@ -288,6 +299,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "view",
             CanFocus = true
         };
+
         view.HasFocusChanging += (s, e) =>
                                  {
                                      if (e.NewValue)
@@ -309,6 +321,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subview",
             CanFocus = true
         };
+
         subview.HasFocusChanging += (s, e) =>
                                     {
                                         if (e.NewValue)
@@ -346,6 +359,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "view",
             CanFocus = true
         };
+
         view.HasFocusChanging += (s, e) =>
                                  {
                                      if (e.NewValue)
@@ -367,6 +381,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subview",
             CanFocus = true
         };
+
         subview.HasFocusChanging += (s, e) =>
                                     {
                                         if (e.NewValue)
@@ -404,6 +419,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "view",
             CanFocus = true
         };
+
         view.HasFocusChanging += (s, e) =>
                                  {
                                      if (e.NewValue)
@@ -424,6 +440,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subview",
             CanFocus = true
         };
+
         subview.HasFocusChanging += (s, e) =>
                                     {
                                         if (e.NewValue)
@@ -463,6 +480,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "view",
             CanFocus = true
         };
+
         view.HasFocusChanging += (s, e) =>
                                  {
                                      if (e.NewValue)
@@ -483,6 +501,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subview",
             CanFocus = true
         };
+
         subview.HasFocusChanging += (s, e) =>
                                     {
                                         if (e.NewValue)
@@ -537,6 +556,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "view",
             CanFocus = true
         };
+
         view.HasFocusChanging += (s, e) =>
                                  {
                                      if (e.NewValue)
@@ -575,6 +595,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "view",
             CanFocus = true
         };
+
         view.HasFocusChanging += (s, e) =>
                                  {
                                      if (e.NewValue)
@@ -595,6 +616,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subview",
             CanFocus = true
         };
+
         subview.HasFocusChanging += (s, e) =>
                                     {
                                         if (e.NewValue)
@@ -618,7 +640,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
         Assert.Equal (0, subviewHasFocusFalseCount);
 
         view.HasFocus = false;
-        Assert.False(view.HasFocus);
+        Assert.False (view.HasFocus);
         Assert.False (subview.HasFocus);
         Assert.Equal (1, hasFocusTrueCount);
         Assert.Equal (1, hasFocusFalseCount);
@@ -640,6 +662,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "view",
             CanFocus = true
         };
+
         view.HasFocusChanging += (s, e) =>
                                  {
                                      if (e.NewValue)
@@ -660,6 +683,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subview",
             CanFocus = true
         };
+
         subview.HasFocusChanging += (s, e) =>
                                     {
                                         if (e.NewValue)
@@ -691,6 +715,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
         Assert.Equal (1, subviewHasFocusFalseCount);
 
     }
+
     #endregion HasFocusChanging_NewValue_False
 
     #region HasFocusChanged
@@ -706,6 +731,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "view",
             CanFocus = true
         };
+
         view.HasFocusChanged += (s, e) =>
                                 {
                                     if (e.NewValue)
@@ -743,6 +769,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "view",
             CanFocus = true
         };
+
         view.HasFocusChanged += (s, e) =>
                                 {
                                     if (e.NewValue)
@@ -763,6 +790,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subview",
             CanFocus = true
         };
+
         subview.HasFocusChanged += (s, e) =>
                                    {
                                        if (e.NewValue)
@@ -821,6 +849,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "view",
             CanFocus = true
         };
+
         view.HasFocusChanged += (s, e) =>
                                 {
                                     if (e.NewValue)
@@ -841,6 +870,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subView",
             CanFocus = true
         };
+
         subView.HasFocusChanged += (s, e) =>
                                    {
                                        if (e.NewValue)
@@ -861,6 +891,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subViewSubView1",
             CanFocus = false
         };
+
         subViewSubView1.HasFocusChanged += (s, e) =>
                                            {
                                                if (e.NewValue)
@@ -881,6 +912,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subViewSubView2",
             CanFocus = true
         };
+
         subViewSubView2.HasFocusChanged += (s, e) =>
                                            {
                                                if (e.NewValue)
@@ -900,6 +932,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subViewSubView3",
             CanFocus = false
         };
+
         subViewSubView3.HasFocusChanged += (s, e) =>
                                            {
                                                if (e.NewValue)
@@ -967,32 +1000,33 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "view",
             CanFocus = true
         };
+
         view.HasFocusChanged += (s, e) =>
-        {
-            if (e.NewValue)
-            {
-                Assert.True (view.HasFocus);
-                Assert.True (subView1.HasFocus);
-                Assert.False (subView2.HasFocus);
+                                {
+                                    if (e.NewValue)
+                                    {
+                                        Assert.True (view.HasFocus);
+                                        Assert.True (subView1.HasFocus);
+                                        Assert.False (subView2.HasFocus);
 
-                subView1.Visible = true;
-                subView2.Visible = false;
+                                        subView1.Visible = true;
+                                        subView2.Visible = false;
 
-                Assert.True (view.HasFocus);
-                Assert.True (subView1.HasFocus);
-                Assert.False (subView2.HasFocus);
+                                        Assert.True (view.HasFocus);
+                                        Assert.True (subView1.HasFocus);
+                                        Assert.False (subView2.HasFocus);
 
-            }
-            else
-            {
-                Assert.False (view.HasFocus);
-                Assert.False (subView1.HasFocus);
-                Assert.False (subView2.HasFocus);
+                                    }
+                                    else
+                                    {
+                                        Assert.False (view.HasFocus);
+                                        Assert.False (subView1.HasFocus);
+                                        Assert.False (subView2.HasFocus);
 
-                subView1.Visible = false;
-                subView2.Visible = true;
-            }
-        };
+                                        subView1.Visible = false;
+                                        subView2.Visible = true;
+                                    }
+                                };
 
         view.Add (subView1, subView2);
 
@@ -1006,5 +1040,6 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
         Assert.False (subView1.HasFocus);
         Assert.False (subView2.HasFocus);
     }
+
     #endregion HasFocusChanged
 }

+ 75 - 0
UnitTests/View/Navigation/HasFocusTests.cs

@@ -155,4 +155,79 @@ public class HasFocusTests (ITestOutputHelper _output) : TestsAllViews
         Assert.False (subViewSubView2.HasFocus);
         Assert.False (subViewSubView3.HasFocus);
     }
+
+    [Fact]
+    public void HasFocus_False_Subview_Raises_HasFocusChanged ()
+    {
+        var top = new View
+        {
+            Id = "top",
+            CanFocus = true
+        };
+
+        var subView1 = new View
+        {
+            Id = "subView1",
+            CanFocus = true
+        };
+
+        var subView2 = new View
+        {
+            Id = "subView2",
+            CanFocus = true
+        };
+        top.Add (subView1, subView2);
+
+        var subView1HasFocusChangedTrueCount = 0;
+        var subView1HasFocusChangedFalseCount = 0;
+
+        subView1.HasFocusChanged += (s, e) =>
+        {
+            if (e.NewValue)
+            {
+                subView1HasFocusChangedTrueCount++;
+            }
+            else
+            {
+                subView1HasFocusChangedFalseCount++;
+            }
+        };
+
+        var subView2HasFocusChangedTrueCount = 0;
+        var subView2HasFocusChangedFalseCount = 0;
+
+        subView2.HasFocusChanged += (s, e) =>
+        {
+            if (e.NewValue)
+            {
+                subView2HasFocusChangedTrueCount++;
+            }
+            else
+            {
+                subView2HasFocusChangedFalseCount++;
+            }
+        };
+
+        top.SetFocus ();
+        Assert.True (top.HasFocus);
+        Assert.True (subView1.HasFocus);
+        Assert.False (subView2.HasFocus);
+
+        Assert.Equal (1, subView1HasFocusChangedTrueCount);
+        Assert.Equal (0, subView1HasFocusChangedFalseCount);
+
+        Assert.Equal (0, subView2HasFocusChangedTrueCount);
+        Assert.Equal (0, subView2HasFocusChangedFalseCount);
+
+        subView1.HasFocus = false; // this should have the same resuilt as top.AdvanceFocus (NavigationDirection.Forward, null);
+
+        Assert.False (subView1.HasFocus);
+        Assert.True (subView2.HasFocus);
+
+        Assert.Equal (1, subView1HasFocusChangedTrueCount);
+        Assert.Equal (1, subView1HasFocusChangedFalseCount);
+
+        Assert.Equal (1, subView2HasFocusChangedTrueCount);
+        Assert.Equal (0, subView2HasFocusChangedFalseCount);
+    }
 }

+ 0 - 42
UnitTests/View/Navigation/NavigationTests.cs

@@ -313,48 +313,6 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
 
     }
 
-    [Fact]
-    public void BringSubviewForward_Subviews_vs_TabIndexes ()
-    {
-        var r = new View ();
-        var v1 = new View { CanFocus = true };
-        var v2 = new View { CanFocus = true };
-        var v3 = new View { CanFocus = true };
-
-        r.Add (v1, v2, v3);
-
-        r.BringSubviewForward (v1);
-        Assert.True (r.Subviews.IndexOf (v1) == 1);
-        Assert.True (r.Subviews.IndexOf (v2) == 0);
-        Assert.True (r.Subviews.IndexOf (v3) == 2);
-
-        Assert.True (r.TabIndexes.IndexOf (v1) == 0);
-        Assert.True (r.TabIndexes.IndexOf (v2) == 1);
-        Assert.True (r.TabIndexes.IndexOf (v3) == 2);
-        r.Dispose ();
-    }
-
-    [Fact]
-    public void BringSubviewToFront_Subviews_vs_TabIndexes ()
-    {
-        var r = new View ();
-        var v1 = new View { CanFocus = true };
-        var v2 = new View { CanFocus = true };
-        var v3 = new View { CanFocus = true };
-
-        r.Add (v1, v2, v3);
-
-        r.BringSubviewToFront (v1);
-        Assert.True (r.Subviews.IndexOf (v1) == 2);
-        Assert.True (r.Subviews.IndexOf (v2) == 0);
-        Assert.True (r.Subviews.IndexOf (v3) == 1);
-
-        Assert.True (r.TabIndexes.IndexOf (v1) == 0);
-        Assert.True (r.TabIndexes.IndexOf (v2) == 1);
-        Assert.True (r.TabIndexes.IndexOf (v3) == 2);
-        r.Dispose ();
-    }
-
     // View.Focused & View.MostFocused tests
 
     // View.Focused - No subviews

+ 2 - 25
UnitTests/View/ViewTests.cs

@@ -1064,7 +1064,6 @@ At 0,0
                                      Assert.True (win.Visible);
                                      Assert.True (win.CanFocus);
                                      Assert.True (win.HasFocus);
-                                     Assert.True (RunesCount () > 0);
 
                                      win.Visible = false;
                                      Assert.True (button.Visible);
@@ -1073,21 +1072,18 @@ At 0,0
                                      Assert.False (win.Visible);
                                      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);
-                                     top.Draw ();
-                                     Assert.True (RunesCount () == 0);
 
                                      win.Visible = true;
-                                     win.FocusDeepest (NavigationDirection.Forward, null);
                                      Assert.True (button.HasFocus);
                                      Assert.True (win.HasFocus);
-                                     top.Draw ();
-                                     Assert.True (RunesCount () > 0);
 
                                      Application.RequestStop ();
                                  };
@@ -1095,25 +1091,6 @@ At 0,0
         Application.Run (top);
         top.Dispose ();
         Assert.Equal (1, iterations);
-
-        int RunesCount ()
-        {
-            Cell [,] contents = ((FakeDriver)Application.Driver).Contents;
-            var runesCount = 0;
-
-            for (var i = 0; i < Application.Driver!.Rows; i++)
-            {
-                for (var j = 0; j < Application.Driver!.Cols; j++)
-                {
-                    if (contents [i, j].Rune != (Rune)' ')
-                    {
-                        runesCount++;
-                    }
-                }
-            }
-
-            return runesCount;
-        }
     }
 
     public class DerivedView : View

+ 4 - 3
UnitTests/Views/ComboBoxTests.cs

@@ -506,7 +506,7 @@ public class ComboBoxTests (ITestOutputHelper output)
         top.Add (otherView, cb);
         Application.Begin (top);
 
-        Assert.False (cb.HasFocus);
+        Assert.True (cb.HasFocus);
 
         Assert.True (cb.HideDropdownListOnClick);
         Assert.False (cb.IsShow);
@@ -857,7 +857,7 @@ Three ",
         Assert.True (Application.OnKeyDown (Key.CursorDown)); // losing focus
         Assert.False (cb.IsShow);
         Assert.False (cb.HasFocus);
-        top.FocusDeepest (NavigationDirection.Forward, null); // Gets focus again
+        cb.SetFocus ();
         Assert.False (cb.IsShow);
         Assert.True (cb.HasFocus);
         cb.Expand ();
@@ -969,7 +969,8 @@ Three
         Assert.False (cb.IsShow);
         Assert.Equal (-1, cb.SelectedItem);
         Assert.Equal ("One", cb.Text);
-        top.FocusDeepest (NavigationDirection.Forward, null); // Gets focus again
+
+        cb.SetFocus ();
         Assert.True (cb.HasFocus);
         Assert.False (cb.IsShow);
         Assert.Equal (-1, cb.SelectedItem);

+ 1 - 1
UnitTests/Views/OverlappedTests.cs

@@ -1232,7 +1232,7 @@ public class OverlappedTests
         Assert.Equal (superView.MostFocused, current);
 
         // Act
-        ApplicationOverlapped.SetFocusToNextViewWithWrap (Application.Current.SuperView.TabIndexes, NavigationDirection.Forward);
+        ApplicationOverlapped.SetFocusToNextViewWithWrap (Application.Current.SuperView.Subviews, NavigationDirection.Forward);
 
         // Assert
         Assert.True (view1.HasFocus);