Browse Source

Done with refactor. Nav unit tests all pass. Fixing View unit tests...

Tig 11 months ago
parent
commit
d1d7ab5fa8

+ 17 - 4
Terminal.Gui/Application/Application.Keyboard.cs

@@ -333,7 +333,7 @@ public static partial class Application // Keyboard handling
                     () =>
                     {
                         View? current = Application.Current;
-                        if (current is {})
+                        if (current is { })
                         {
                             return current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
                         }
@@ -363,7 +363,7 @@ public static partial class Application // Keyboard handling
                             View? current = Application.Current;
                             if (current is { })
                             {
-                                return current.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);
+                                return current.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup);
                             }
                         }
                         else
@@ -381,9 +381,22 @@ public static partial class Application // Keyboard handling
                     Command.PreviousViewOrTop,
                     () =>
                     {
-                        ApplicationNavigation.MovePreviousViewOrTop ();
+                        if (ApplicationOverlapped.OverlappedTop is null)
+                        {
+                            View? current = Application.Current;
+                            if (current is { })
+                            {
+                                return current.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);
+                            }
+                        }
+                        else
+                        {
+                            ApplicationOverlapped.OverlappedMovePrevious();
 
-                        return true;
+                            return true;
+                        }
+
+                        return false;
                     }
                    );
 

+ 6 - 1
Terminal.Gui/Application/Application.Run.cs

@@ -186,7 +186,12 @@ public static partial class Application // Run (Begin, Run, End, Stop)
 
         toplevel.LayoutSubviews ();
         toplevel.PositionToplevels ();
-        toplevel.AdvanceFocus (NavigationDirection.Forward, null);//.FocusDeepest (null, NavigationDirection.Forward);
+        // Try to set initial focus to any TabGroup
+        if (!toplevel.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabGroup))
+        {
+            // That didn't work. Try TabStop.
+            toplevel.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
+        }
         ApplicationOverlapped.BringOverlappedTopToFront ();
 
         if (refreshDriver)

+ 0 - 23
Terminal.Gui/Application/ApplicationNavigation.cs

@@ -107,27 +107,4 @@ public class ApplicationNavigation
 
         return view;
     }
-    
-
-    
-    internal static void MovePreviousViewOrTop ()
-    {
-        if (ApplicationOverlapped.OverlappedTop is null)
-        {
-            Toplevel? top = Application.Current!.Modal ? Application.Current : Application.Top;
-            top!.AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabGroup);
-
-            if (top.Focused is null)
-            {
-                top.AdvanceFocus (NavigationDirection.Backward, null);
-            }
-
-            top.SetNeedsDisplay ();
-            ApplicationOverlapped.BringOverlappedTopToFront ();
-        }
-        else
-        {
-            ApplicationOverlapped.OverlappedMovePrevious ();
-        }
-    }
 }

+ 83 - 82
Terminal.Gui/View/View.Navigation.cs

@@ -46,7 +46,8 @@ public partial class View // Focus and cross-view navigation management (TabStop
                 if (value)
                 {
                     // NOTE: If Application.Navigation is null, we pass null to FocusChanging. For unit tests.
-                    if (SetHasFocusTrue (Application.Navigation?.GetFocused ()))
+                    (bool focusSet, bool _) = SetHasFocusTrue (Application.Navigation?.GetFocused ());
+                    if (focusSet)
                     {
                         // The change happened
                         // HasFocus is now true
@@ -67,7 +68,8 @@ public partial class View // Focus and cross-view navigation management (TabStop
     /// </summary>
     public bool SetFocus ()
     {
-        return SetHasFocusTrue (Application.Navigation?.GetFocused ());
+        (bool focusSet, bool _) = SetHasFocusTrue (Application.Navigation?.GetFocused ());
+        return focusSet;
     }
 
     /// <summary>
@@ -78,82 +80,58 @@ public partial class View // Focus and cross-view navigation management (TabStop
     /// <param name="traversingUp"></param>
     /// <returns><see langword="true"/> if <see cref="HasFocus"/> was changed to <see langword="true"/>.</returns>
     /// <exception cref="InvalidOperationException"></exception>
-    private bool SetHasFocusTrue ([CanBeNull] View previousFocusedView, bool traversingUp = false)
+    private (bool focusSet, bool cancelled) SetHasFocusTrue ([CanBeNull] View previousFocusedView, bool traversingUp = false)
     {
         Debug.Assert (ApplicationNavigation.IsInHierarchy (SuperView, this));
 
         // Pre-conditions
         if (_hasFocus)
         {
-            return false;
+            return (false, false);
         }
 
         if (CanFocus && SuperView is { CanFocus: false })
         {
             Debug.WriteLine ($@"WARNING: Attempt to FocusChanging where SuperView.CanFocus == false. {this}");
-            return false;
+            return (false, false);
         }
 
         if (!CanBeVisible (this) || !Enabled)
         {
-            return false;
+            return (false, false);
         }
 
         if (!CanFocus)
         {
-            return false;
+            return (false, false);
         }
 
         bool previousValue = HasFocus;
 
-        if (!traversingUp)
-        {
-
-            // If we're here, we can be focused. But we may have subviews.
-
-            // Restore focus to the previously most focused subview in the subview-hierarchy
-            if (RestoreFocus (TabStop))
-            {
-                // A subview was focused. We're done because the subview has focus and it recursed up the superview hierarchy.
-                return true;
-            }
-
-            // Couldn't restore focus, so use Advance to navigate to the next focusable subview
-            if (AdvanceFocus (NavigationDirection.Forward, null))
-            {
-                // A subview was focused. We're done because the subview has focus and it recursed up the superview hierarchy.
-                return true;
-            }
-        }
+        bool cancelled = NotifyFocusChanging (false, true, previousFocusedView, this);
 
-        if (NotifyFocusChanging (false, true, previousFocusedView, this))
+        if (cancelled)
         {
-            return false;
+            return (false, true);
         }
 
-        // If we're here, we're the most-focusable view in the application OR we're traversing up the superview hierarchy.
+        //// If we previously had a subview with focus (`Focused = subview`), we need to make sure that all subviews down the `subview`-hierarchy LeaveFocus.
+        //// LeaveFocus will recurse down the subview hierarchy and will also set PreviouslyMostFocused
+        //View focused = Focused;
+        //focused?.SetHasFocusFalse (this, true);
 
-        // If we previously had a subview with focus (`Focused = subview`), we need to make sure that all subviews down the `subview`-hierarchy LeaveFocus.
-        // LeaveFocus will recurse down the subview hierarchy and will also set PreviouslyMostFocused
-        View focused = Focused;
-        focused?.SetHasFocusFalse (this, true);
-
-        // We need to ensure all superviews up the superview hierarchy have focus.
+        // Make sure superviews up the superview hierarchy have focus.
         // Any of them may cancel gaining focus. In which case we need to back out.
         if (SuperView is { HasFocus: false } sv)
         {
-            // Tell SetHasFocusTrue that we're traversing up the superview hierarchy
-            if (!sv.SetHasFocusTrue (previousFocusedView, true))
+            (bool focusSet, bool svCancelled) = sv.SetHasFocusTrue (previousFocusedView, true);
+            if (!focusSet)
             {
-                // The change didn't happen.
-                return false;
+                return (false, svCancelled);
             }
         }
 
-        // If we're here:
-        // - we're the most-focusable view in the application
-        // - all superviews up the superview hierarchy have focus.
-        // - By setting _hasFocus to true we definitively change HasFocus for this view.
+        // By setting _hasFocus to true we definitively change HasFocus for this view.
 
         // Get whatever peer has focus, if any
         View focusedPeer = SuperView?.Focused;
@@ -163,11 +141,23 @@ public partial class View // Focus and cross-view navigation management (TabStop
         // Ensure that the peer loses focus
         focusedPeer?.SetHasFocusFalse (this, true);
 
-        // We're the most focused view in the application, we need to set the focused view to this view.
-        Application.Navigation?.SetFocused (this);
+        if (!traversingUp)
+        {
+            // Restore focus to the previously most focused subview in the subview-hierarchy
+            if (!RestoreFocus (TabStop))
+            {
+                // Couldn't restore focus, so use Advance to navigate to the next focusable subview
+                if (!AdvanceFocus (NavigationDirection.Forward, null))
+                {
+                    // Couldn't advance, so we're the most focused view in the application
+                    _previouslyMostFocused = null;
+                    Application.Navigation?.SetFocused (this);
+                    //NotifyFocusChanged (HasFocus, previousFocusedView, this);
+                }
+            }
+        }
 
         NotifyFocusChanged (HasFocus, previousFocusedView, this);
-
         SetNeedsDisplay ();
 
         // Post-conditions - prove correctness
@@ -176,7 +166,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
             throw new InvalidOperationException ($"NotifyFocusChanging was not cancelled and the HasFocus value did not change.");
         }
 
-        return true;
+        return (true, false);
     }
 
 
@@ -204,8 +194,13 @@ public partial class View // Focus and cross-view navigation management (TabStop
     /// <summary>
     ///     Invoked when <see cref="View.HasFocus"/> is about to change. This method is called before the <see cref="HasFocusChanging"/> event is raised.
     /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Use <see cref="OnHasFocusChanged"/> to be notified after the focus has changed.
+    ///     </para>
+    /// </remarks>
     /// <param name="currentHasFocus">The current value of <see cref="View.HasFocus"/>.</param>
-    /// <param name="newHasFocus">The value <see cref="View.HasFocus"/> will have if the event is not cancelled.</param>
+    /// <param name="newHasFocus">The value <see cref="View.HasFocus"/> will have if the focus change happens.</param>
     /// <param name="currentFocused">The view that is currently Focused. May be <see langword="null"/>.</param>
     /// <param name="newFocused">The view that will be focused. May be <see langword="null"/>.</param>
     /// <returns> <see langword="true"/>, if the change to <see cref="View.HasFocus"/> is to be cancelled, <see langword="false"/> otherwise.</returns>
@@ -214,57 +209,70 @@ public partial class View // Focus and cross-view navigation management (TabStop
         return false;
     }
 
-    /// <summary>Raised when the view is gaining (entering) focus. Can be cancelled.</summary>
+    /// <summary>
+    ///     Raised when <see cref="View.HasFocus"/> is about to change.
+    /// </summary>
+    /// <remarks>
+    ///     <para>
+    ///         Cancel the event to prevent the focus from changing.
+    ///     </para>
+    ///     <para>
+    ///         Use <see cref="HasFocusChanged"/> to be notified after the focus has changed.
+    ///     </para>
+    /// </remarks>
     public event EventHandler<HasFocusEventArgs> HasFocusChanging;
 
     /// <summary>
-    ///     Called when focus has changed to another view.
+    ///     Called when this view should stop being focused.
     /// </summary>
-    /// <param name="focusedVew">The view that now has focus. If <see langword="null"/> there is no view that has focus.</param>
+    /// <param name="newFocusedVew">The new focused view. If <see langword="null"/> it is not known which view will be focused.</param>
     /// <exception cref="InvalidOperationException"></exception>
-    private void SetHasFocusFalse ([CanBeNull] View focusedVew, bool traversingDown = false)
+    private void SetHasFocusFalse ([CanBeNull] View newFocusedVew, bool traversingDown = false)
     {
         // Pre-conditions
         if (!_hasFocus)
         {
-            throw new InvalidOperationException ($"FocusChanged should not be called if the view does not have focus.");
+            throw new InvalidOperationException ($"SetHasFocusFalse should not be called if the view does not have focus.");
         }
 
-        // If enteringView is null, we need to find the view that should get focus, and SetFocus on it.
-        if (!traversingDown && focusedVew is null)
+        // If newFocusedVew is null, we need to find the view that should get focus, and SetFocus on it.
+        if (!traversingDown && newFocusedVew is null)
         {
             if (SuperView?._previouslyMostFocused is { } && SuperView?._previouslyMostFocused != this)
             {
                 SuperView?._previouslyMostFocused?.SetFocus ();
 
-                // The above will cause FocusChanged, so we can return
+                // The above will cause SetHasFocusFalse, so we can return
                 return;
             }
 
             if (SuperView is { } && SuperView.AdvanceFocus (NavigationDirection.Forward, TabStop))
             {
-                // The above will cause FocusChanged, so we can return
+                // The above will cause SetHasFocusFalse, so we can return
                 return;
             }
 
-            //if (Application.Navigation is { })
-            //{
-            //    // Temporarily ensure this view can't get focus
-            //    bool prevCanFocus = _canFocus;
-            //    _canFocus = false;
-            //    Application.Navigation.;
-            //    _canFocus = prevCanFocus;
+            if (Application.Navigation is { })
+            {
+                // Temporarily ensure this view can't get focus
+                bool prevCanFocus = _canFocus;
+                _canFocus = false;
+                bool restoredFocus = Application.Current!.RestoreFocus (null);
+                _canFocus = prevCanFocus;
 
-            //    // The above will cause LeaveFocus, so we can return
-            //    return;
-            //}
+                if (restoredFocus)
+                {
+                    // The above caused SetHasFocusFalse, 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.
         View mostFocused = MostFocused;
-        if (mostFocused is { } && (focusedVew is null || mostFocused != focusedVew))
+        if (mostFocused is { } && (newFocusedVew is null || mostFocused != newFocusedVew))
         {
             // Start at the bottom and work our way up to us
             View bottom = mostFocused;
@@ -273,7 +281,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
             {
                 if (bottom.HasFocus)
                 {
-                    bottom.SetHasFocusFalse (focusedVew, true);
+                    bottom.SetHasFocusFalse (newFocusedVew, true);
                 }
                 bottom = bottom.SuperView;
             }
@@ -283,26 +291,17 @@ public partial class View // Focus and cross-view navigation management (TabStop
         bool previousValue = HasFocus;
 
         // Note, can't be cancelled.
-        NotifyFocusChanging (HasFocus, !HasFocus, focusedVew, this);
+        NotifyFocusChanging (HasFocus, !HasFocus, newFocusedVew, this);
 
         // Get whatever peer has focus, if any
         View focusedPeer = SuperView?.Focused;
         _hasFocus = false;
 
-        NotifyFocusChanged (HasFocus, this, focusedVew);
-
-        if (!traversingDown && CanFocus && Visible && Enabled)
-        {
-            // Now ensure all views up the superview-hierarchy are unfocused
-            if (SuperView is { HasFocus: true } && focusedPeer == this)
-            {
-                SuperView.SetHasFocusFalse (focusedVew);
-            }
-        }
+        NotifyFocusChanged (HasFocus, this, newFocusedVew);
 
         if (SuperView is { })
         {
-            SuperView._previouslyMostFocused = this;
+            SuperView._previouslyMostFocused = focusedPeer;
         }
 
         // Post-conditions - prove correctness
@@ -428,7 +427,9 @@ public partial class View // Focus and cross-view navigation management (TabStop
         }
 
         // The subview does not have focus, but at least one other that can. Can this one be focused?
-        return view.SetHasFocusTrue (Focused);
+        (bool focusSet, bool _) = view.SetHasFocusTrue (Focused);
+
+        return focusSet;
     }
 
 

+ 27 - 30
Terminal.Gui/Views/ComboBox.cs

@@ -8,6 +8,7 @@
 using System.Collections.ObjectModel;
 using System.ComponentModel;
 using System.Diagnostics;
+using System.Threading.Channels;
 
 namespace Terminal.Gui;
 
@@ -31,7 +32,7 @@ public class ComboBox : View, IDesignable
         CanFocus = true;
         _search = new TextField () { CanFocus = true, TabStop = TabBehavior.NoStop };
 
-        _listview = new ComboListView (this, HideDropdownListOnClick) { CanFocus = true, TabStop = TabBehavior.NoStop};
+        _listview = new ComboListView (this, HideDropdownListOnClick) { CanFocus = true, TabStop = TabBehavior.NoStop };
 
         _search.TextChanged += Search_Changed;
         _search.Accept += Search_Accept;
@@ -299,19 +300,6 @@ public class ComboBox : View, IDesignable
         Driver.AddRune (Glyphs.DownArrow);
     }
 
-    /// <inheritdoc/>
-    protected override bool OnHasFocusChanging (bool currentHasFocus, bool newHasFocus, [CanBeNull] View currentFocused, [CanBeNull] View newFocused)
-    {
-        bool cancel = false;
-        if (!_search.HasFocus && !_listview.HasFocus)
-        {
-            cancel = _search.SetFocus ();
-        }
-
-        _search.CursorPosition = _search.Text.GetRuneCount ();
-
-        return cancel;
-    }
 
     /// <summary>Virtual method which invokes the <see cref="Expanded"/> event.</summary>
     public virtual void OnExpanded () { Expanded?.Invoke (this, EventArgs.Empty); }
@@ -319,25 +307,34 @@ public class ComboBox : View, IDesignable
     /// <inheritdoc/>
     protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view)
     {
-        if (_source?.Count > 0
-            && _selectedItem > -1
-            && _selectedItem < _source.Count - 1
-            && _text != _source.ToList () [_selectedItem].ToString ())
+        if (newHasFocus)
         {
-            SetValue (_source.ToList () [_selectedItem].ToString ());
+            if (!_search.HasFocus && !_listview.HasFocus)
+            {
+                _search.SetFocus ();
+            }
+            _search.CursorPosition = _search.Text.GetRuneCount ();
         }
+        else
+        { 
+            if (_source?.Count > 0
+              && _selectedItem > -1
+              && _selectedItem < _source.Count - 1
+              && _text != _source.ToList () [_selectedItem].ToString ())
+            {
+                SetValue (_source.ToList () [_selectedItem].ToString ());
+            }
 
-        if (_autoHide && IsShow && view != this && view != _search && view != _listview)
-        {
-            IsShow = false;
-            HideList ();
-        }
-        else if (_listview.TabStop?.HasFlag (TabBehavior.TabStop) ?? false)
-        {
-            _listview.TabStop = TabBehavior.NoStop;
+            if (_autoHide && IsShow && view != this && view != _search && view != _listview)
+            {
+                IsShow = false;
+                HideList ();
+            }
+            else if (_listview.TabStop?.HasFlag (TabBehavior.TabStop) ?? false)
+            {
+                _listview.TabStop = TabBehavior.NoStop;
+            }
         }
-
-        return;
     }
 
     /// <summary>Invokes the OnOpenSelectedItem event if it is defined.</summary>
@@ -565,7 +562,7 @@ public class ComboBox : View, IDesignable
     {
         if (HasItems ())
         {
-           return  _listview.MoveUp ();
+            return _listview.MoveUp ();
         }
 
         return false;

+ 4 - 1
Terminal.Gui/Views/FileDialog.cs

@@ -518,7 +518,10 @@ public class FileDialog : Dialog
             };
             AllowedTypeMenuClicked (0);
 
-            _allowedTypeMenuBar.HasFocusChanging += (s, e) => { _allowedTypeMenuBar.OpenMenu (0); };
+            _allowedTypeMenuBar.HasFocusChanging += (s, e) =>
+                                                    {
+                                                        _allowedTypeMenuBar.OpenMenu (0);
+                                                    };
 
             _allowedTypeMenuBar.DrawContentComplete += (s, e) =>
                                                        {

+ 4 - 0
Terminal.Gui/Views/HexView.cs

@@ -760,6 +760,10 @@ public class HexView : View
 
     private void RedisplayLine (long pos)
     {
+        if (bytesPerLine == 0)
+        {
+            return;
+        }
         var delta = (int)(pos - DisplayStart);
         int line = delta / bytesPerLine;
 

+ 2 - 4
Terminal.Gui/Views/ListView.cs

@@ -739,14 +739,12 @@ public class ListView : View, IDesignable
     }
 
     /// <inheritdoc/>
-    protected override bool OnHasFocusChanging (bool currentHasFocus, bool newHasFocus, [CanBeNull] View currentFocused, [CanBeNull] View newFocused)
+    protected override void OnHasFocusChanged (bool newHasFocus, [CanBeNull] View currentFocused, [CanBeNull] View newFocused)
     {
-        if (_lastSelectedItem != _selected)
+        if (newHasFocus && _lastSelectedItem != _selected)
         {
             EnsureSelectedItemVisible ();
         }
-
-        return false; // Don't cancel the focus switch
     }
 
     // TODO: This should be cancelable

+ 56 - 84
Terminal.Gui/Views/Menu/MenuBar.cs

@@ -86,45 +86,10 @@ public class MenuBar : View, IDesignable
         Added += MenuBar_Added;
 
         // Things this view knows how to do
-        AddCommand (
-                    Command.Left,
-                    () =>
-                    {
-                        MoveLeft ();
-
-                        return true;
-                    }
-                   );
-
-        AddCommand (
-                    Command.Right,
-                    () =>
-                    {
-                        MoveRight ();
-
-                        return true;
-                    }
-                   );
-
-        AddCommand (
-                    Command.Cancel,
-                    () =>
-                    {
-                        CloseMenuBar ();
-
-                        return true;
-                    }
-                   );
-
-        AddCommand (
-                    Command.Accept,
-                    () =>
-                    {
-                        ProcessMenu (_selected, Menus [_selected]);
-
-                        return true;
-                    }
-                   );
+        AddCommand (Command.Left, () => MoveLeft ());
+        AddCommand (Command.Right, () => MoveRight ());
+        AddCommand (Command.Cancel, () => CloseMenuBar ());
+        AddCommand (Command.Accept, () => _selected >= 0 && ProcessMenu (_selected, Menus [_selected]));
         AddCommand (Command.ToggleExpandCollapse, ctx => Select (Menus.IndexOf (ctx.KeyBinding?.Context)));
         AddCommand (Command.Select, ctx => Run ((ctx.KeyBinding?.Context as MenuItem)?.Action));
 
@@ -225,7 +190,6 @@ public class MenuBar : View, IDesignable
             if (value && UseKeysUpDownAsKeysLeftRight)
             {
                 _useKeysUpDownAsKeysLeftRight = false;
-                SetNeedsDisplay ();
             }
         }
     }
@@ -375,7 +339,7 @@ public class MenuBar : View, IDesignable
     }
 
     /// <summary>Opens the Menu programatically, as though the F9 key were pressed.</summary>
-    public void OpenMenu ()
+    public bool OpenMenu ()
     {
         MenuBar mbar = GetMouseGrabViewInstance (this);
 
@@ -386,11 +350,10 @@ public class MenuBar : View, IDesignable
 
         if (!Enabled || _openMenu is { })
         {
-            return;
+            return false;
         }
 
         _selected = 0;
-        SetNeedsDisplay ();
 
         _previousFocused = null;//SuperView is null ? Application.Current?.Focused : SuperView.Focused;
         OpenMenu (_selected);
@@ -402,15 +365,17 @@ public class MenuBar : View, IDesignable
                                )
             && !CloseMenu (false))
         {
-            return;
+            return IsMenuOpen;
         }
 
         if (!OpenCurrentMenu.CheckSubMenu ())
         {
-            return;
+            return IsMenuOpen;
         }
 
         Application.GrabMouse (this);
+
+        return IsMenuOpen;
     }
 
     /// <inheritdoc/>
@@ -446,7 +411,7 @@ public class MenuBar : View, IDesignable
 
     // Activates the menu, handles either first focus, or activating an entry when it was already active
     // For mouse events.
-    internal void Activate (int idx, int sIdx = -1, MenuBarItem subMenu = null)
+    internal bool Activate (int idx, int sIdx = -1, MenuBarItem subMenu = null)
     {
         _selected = idx;
         _selectedSub = sIdx;
@@ -456,8 +421,7 @@ public class MenuBar : View, IDesignable
             _previousFocused = null;//SuperView is null ? Application.Current?.Focused ?? null : SuperView.Focused;
         }
 
-        OpenMenu (idx, sIdx, subMenu);
-        SetNeedsDisplay ();
+        return OpenMenu (idx, sIdx, subMenu);
     }
 
     internal void CleanUp ()
@@ -479,7 +443,6 @@ public class MenuBar : View, IDesignable
             _lastFocused.SetFocus ();
         }
 
-        SetNeedsDisplay ();
         Application.UngrabMouse ();
         _isCleaning = false;
     }
@@ -550,8 +513,6 @@ public class MenuBar : View, IDesignable
                     Application.Current?.Remove (_openMenu);
                 }
 
-                SetNeedsDisplay ();
-
                 if (_previousFocused is Menu && _openMenu is { } && _previousFocused.ToString () != OpenCurrentMenu.ToString ())
                 {
                     _previousFocused.SetFocus ();
@@ -603,7 +564,6 @@ public class MenuBar : View, IDesignable
 
             case true:
                 _selectedSub = -1;
-                SetNeedsDisplay ();
                 RemoveAllOpensSubMenus ();
                 OpenCurrentMenu?._previousSubFocused.SetFocus ();
                 _openSubMenu = null;
@@ -644,7 +604,7 @@ public class MenuBar : View, IDesignable
                    );
     }
 
-    internal void NextMenu (bool isSubMenu = false, bool ignoreUseSubMenusSingleFrame = false)
+    internal bool NextMenu (bool isSubMenu = false, bool ignoreUseSubMenusSingleFrame = false)
     {
         switch (isSubMenu)
         {
@@ -664,7 +624,7 @@ public class MenuBar : View, IDesignable
 
                 if (_selected > -1 && !CloseMenu (true, ignoreUseSubMenusSingleFrame))
                 {
-                    return;
+                    return false;
                 }
 
                 OpenMenu (_selected);
@@ -696,7 +656,7 @@ public class MenuBar : View, IDesignable
                     {
                         if (_openSubMenu is { } && !CloseMenu (false, true))
                         {
-                            return;
+                            return IsMenuOpen;
                         }
 
                         NextMenu (false, ignoreUseSubMenusSingleFrame);
@@ -714,14 +674,12 @@ public class MenuBar : View, IDesignable
                     {
                         if (CloseMenu (false, true, ignoreUseSubMenusSingleFrame))
                         {
-                            NextMenu (false, ignoreUseSubMenusSingleFrame);
+                            return NextMenu (false, ignoreUseSubMenusSingleFrame);
                         }
 
-                        return;
+                        return IsMenuOpen;
                     }
 
-                    SetNeedsDisplay ();
-
                     if (UseKeysUpDownAsKeysLeftRight)
                     {
                         OpenCurrentMenu.CheckSubMenu ();
@@ -730,9 +688,11 @@ public class MenuBar : View, IDesignable
 
                 break;
         }
+
+        return IsMenuOpen;
     }
 
-    internal void OpenMenu (int index, int sIndex = -1, MenuBarItem subMenu = null)
+    internal bool OpenMenu (int index, int sIndex = -1, MenuBarItem subMenu = null)
     {
         _isMenuOpening = true;
         MenuOpeningEventArgs newMenu = OnMenuOpening (Menus [index]);
@@ -741,7 +701,7 @@ public class MenuBar : View, IDesignable
         {
             _isMenuOpening = false;
 
-            return;
+            return false;
         }
 
         if (newMenu.NewMenuBarItem is { })
@@ -759,7 +719,7 @@ public class MenuBar : View, IDesignable
 
                 if (_openSubMenu is { } && !CloseMenu (false, true))
                 {
-                    return;
+                    return IsMenuOpen;
                 }
 
                 if (_openMenu is { })
@@ -895,9 +855,11 @@ public class MenuBar : View, IDesignable
 
         _isMenuOpening = false;
         IsMenuOpen = true;
+
+        return true;
     }
 
-    internal void PreviousMenu (bool isSubMenu = false, bool ignoreUseSubMenusSingleFrame = false)
+    internal bool PreviousMenu (bool isSubMenu = false, bool ignoreUseSubMenusSingleFrame = false)
     {
         switch (isSubMenu)
         {
@@ -913,10 +875,10 @@ public class MenuBar : View, IDesignable
 
                 if (_selected > -1 && !CloseMenu (true, false, ignoreUseSubMenusSingleFrame))
                 {
-                    return;
+                    return true;
                 }
 
-                OpenMenu (_selected);
+                bool opened = OpenMenu (_selected);
 
                 if (!SelectEnabledItem (
                                         OpenCurrentMenu.BarItems.Children,
@@ -928,21 +890,23 @@ public class MenuBar : View, IDesignable
                     OpenCurrentMenu._currentChild = 0;
                 }
 
-                break;
+                return opened;
+
             case true:
                 if (_selectedSub > -1)
                 {
                     _selectedSub--;
                     RemoveSubMenu (_selectedSub, ignoreUseSubMenusSingleFrame);
-                    SetNeedsDisplay ();
                 }
                 else
                 {
-                    PreviousMenu ();
+                    return PreviousMenu ();
                 }
 
                 break;
         }
+
+        return true;
     }
 
     internal void RemoveAllOpensSubMenus ()
@@ -1063,11 +1027,11 @@ public class MenuBar : View, IDesignable
         return Run (item?.Action);
     }
 
-    private void CloseMenuBar ()
+    private bool CloseMenuBar ()
     {
         if (!CloseMenu (false))
         {
-            return;
+            return false;
         }
 
         if (_openedByAltKey)
@@ -1076,7 +1040,7 @@ public class MenuBar : View, IDesignable
             LastFocused?.SetFocus ();
         }
 
-        SetNeedsDisplay ();
+        return true;
     }
 
     private Point GetLocationOffset ()
@@ -1095,7 +1059,7 @@ public class MenuBar : View, IDesignable
         Added -= MenuBar_Added;
     }
 
-    private void MoveLeft ()
+    private bool MoveLeft ()
     {
         _selected--;
 
@@ -1104,22 +1068,30 @@ public class MenuBar : View, IDesignable
             _selected = Menus.Length - 1;
         }
 
-        OpenMenu (_selected);
-        SetNeedsDisplay ();
+        if (_selected < 0)
+        {
+            return false;
+        }
+
+        return OpenMenu (_selected);
     }
 
-    private void MoveRight ()
+    private bool MoveRight ()
     {
+        if (Menus.Length == 0)
+        {
+            return false;
+        }
         _selected = (_selected + 1) % Menus.Length;
-        OpenMenu (_selected);
-        SetNeedsDisplay ();
+
+        return OpenMenu (_selected);
     }
 
-    private void ProcessMenu (int i, MenuBarItem mi)
+    private bool ProcessMenu (int i, MenuBarItem mi)
     {
         if (_selected < 0 && IsMenuOpen)
         {
-            return;
+            return false;
         }
 
         if (mi.IsTopLevel)
@@ -1133,7 +1105,7 @@ public class MenuBar : View, IDesignable
         {
             Application.GrabMouse (this);
             _selected = i;
-            OpenMenu (i);
+            bool opened = OpenMenu (i);
 
             if (!SelectEnabledItem (
                                     OpenCurrentMenu.BarItems.Children,
@@ -1142,16 +1114,17 @@ public class MenuBar : View, IDesignable
                                    )
                 && !CloseMenu (false))
             {
-                return;
+                return false;
             }
 
             if (!OpenCurrentMenu.CheckSubMenu ())
             {
-                return;
+                return true;
             }
+            return opened;
         }
 
-        SetNeedsDisplay ();
+        return true;
     }
 
     private void RemoveSubMenu (int index, bool ignoreUseSubMenusSingleFrame = false)
@@ -1256,7 +1229,6 @@ public class MenuBar : View, IDesignable
             if (value && UseSubMenusSingleFrame)
             {
                 UseSubMenusSingleFrame = false;
-                SetNeedsDisplay ();
             }
         }
     }

+ 17 - 17
UnitTests/Application/Application.NavigationTests.cs

@@ -137,24 +137,24 @@ public class ApplicationNavigationTests (ITestOutputHelper output)
 
 
 
-    [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 ();
+    //[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 ();
 
-        // Act
-        ApplicationNavigation.MovePreviousViewOrTop ();
+    //    // Act
+    //    ApplicationNavigation.MovePreviousViewOrTop ();
 
-        // Assert
-        Assert.True (view1.HasFocus);
+    //    // Assert
+    //    Assert.True (view1.HasFocus);
 
-        top.Dispose ();
-    }
+    //    top.Dispose ();
+    //}
 }

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

@@ -75,14 +75,11 @@ public class AddRemoveNavigationTests (ITestOutputHelper _output) : TestsAllView
             CanFocus = true
         };
 
-        int nLeave = 0;
-
         View subView = new View ()
         {
             Id = "subView",
             CanFocus = true
         };
-        subView.HasFocusChanged += (s, e) => nLeave++;
 
         top.Add (subView);
 
@@ -95,7 +92,6 @@ public class AddRemoveNavigationTests (ITestOutputHelper _output) : TestsAllView
         Assert.True (top.HasFocus);
         Assert.Equal (null, top.Focused);
         Assert.False (subView.HasFocus);
-        Assert.Equal (1, nLeave);
     }
 
     [Fact]
@@ -107,14 +103,11 @@ public class AddRemoveNavigationTests (ITestOutputHelper _output) : TestsAllView
             CanFocus = true
         };
 
-        int nLeave1 = 0;
-
         View subView1 = new View ()
         {
             Id = "subView1",
             CanFocus = true
         };
-        subView1.HasFocusChanged += (s, e) => nLeave1++;
 
         View subView2 = new View ()
         {
@@ -134,7 +127,5 @@ public class AddRemoveNavigationTests (ITestOutputHelper _output) : TestsAllView
         Assert.True (subView2.HasFocus);
         Assert.Equal (subView2, top.Focused);
         Assert.False (subView1.HasFocus);
-        Assert.Equal (1, nLeave1);
     }
-
 }

+ 308 - 99
UnitTests/View/Navigation/HasFocusChangeEventTests.cs

@@ -4,6 +4,7 @@ namespace Terminal.Gui.ViewTests;
 
 public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllViews
 {
+    #region HasFocusChanging_NewValue_True
     [Fact]
     public void HasFocusChanging_SetFocus_Raises ()
     {
@@ -452,7 +453,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
 
 
     [Fact]
-    public void HasFocusChanging_SetFocus_On_Subview_SubView_Can_Cancel ()
+    public void HasFocusChanging_SetFocus_On_Subview_If_SubView_Cancels ()
     {
         var hasFocusTrueCount = 0;
         var hasFocusFalseCount = 0;
@@ -499,100 +500,318 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
 
         subview.SetFocus ();
 
+        Assert.False (view.HasFocus); // Never had focus
+        Assert.False (subview.HasFocus); // Cancelled
+
+        Assert.Equal (0, hasFocusTrueCount);
+        Assert.Equal (0, hasFocusFalseCount);
+
+        Assert.Equal (1, subviewHasFocusTrueCount);
+        Assert.Equal (0, subviewHasFocusFalseCount);
+
+        // Now set focus on the view
+        view.SetFocus ();
+
+        Assert.True (view.HasFocus);
+        Assert.False (subview.HasFocus); // Cancelled
+
+        Assert.Equal (1, hasFocusTrueCount);
+        Assert.Equal (0, hasFocusFalseCount);
+
+        Assert.Equal (2, subviewHasFocusTrueCount);
+        Assert.Equal (0, subviewHasFocusFalseCount);
+    }
+
+    #endregion HasFocusChanging_NewValue_True
+
+    #region HasFocusChanging_NewValue_False
+
+    [Fact]
+    public void HasFocusChanging_RemoveFocus_Raises ()
+    {
+        var hasFocusTrueCount = 0;
+        var hasFocusFalseCount = 0;
+
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+        view.HasFocusChanging += (s, e) =>
+                                 {
+                                     if (e.NewValue)
+                                     {
+                                         hasFocusTrueCount++;
+                                     }
+                                     else
+                                     {
+                                         hasFocusFalseCount++;
+                                     }
+                                 };
+
+        Assert.True (view.CanFocus);
+        Assert.False (view.HasFocus);
+
+        view.SetFocus ();
+        Assert.True (view.HasFocus);
+        Assert.Equal (1, hasFocusTrueCount);
+        Assert.Equal (0, hasFocusFalseCount);
+
+        view.HasFocus = false;
         Assert.False (view.HasFocus);
+        Assert.Equal (1, hasFocusTrueCount);
+        Assert.Equal (1, hasFocusFalseCount);
+    }
+
+
+    [Fact]
+    public void HasFocusChanging_RemoveFocus_SubView_SetFocus_Raises ()
+    {
+        var hasFocusTrueCount = 0;
+        var hasFocusFalseCount = 0;
+
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+        view.HasFocusChanging += (s, e) =>
+                                 {
+                                     if (e.NewValue)
+                                     {
+                                         hasFocusTrueCount++;
+                                     }
+                                     else
+                                     {
+                                         hasFocusFalseCount++;
+                                     }
+                                 };
+
+        var subviewHasFocusTrueCount = 0;
+        var subviewHasFocusFalseCount = 0;
+
+        var subview = new View
+        {
+            Id = "subview",
+            CanFocus = true
+        };
+        subview.HasFocusChanging += (s, e) =>
+                                    {
+                                        if (e.NewValue)
+                                        {
+                                            subviewHasFocusTrueCount++;
+                                        }
+                                        else
+                                        {
+                                            subviewHasFocusFalseCount++;
+                                        }
+                                    };
+
+        view.Add (subview);
+
+        view.SetFocus ();
+
+        Assert.Equal (1, hasFocusTrueCount);
+        Assert.Equal (0, hasFocusFalseCount);
+
+        Assert.Equal (1, subviewHasFocusTrueCount);
+        Assert.Equal (0, subviewHasFocusFalseCount);
+
+        view.HasFocus = false;
+        Assert.False(view.HasFocus);
         Assert.False (subview.HasFocus);
+        Assert.Equal (1, hasFocusTrueCount);
+        Assert.Equal (1, hasFocusFalseCount);
 
-        Assert.Equal (0, hasFocusTrueCount);
+        Assert.Equal (1, subviewHasFocusTrueCount);
+        Assert.Equal (1, subviewHasFocusFalseCount);
+    }
+
+
+
+    [Fact]
+    public void HasFocusChanging_RemoveFocus_On_SubView_SubView_SetFocus_Raises ()
+    {
+        var hasFocusTrueCount = 0;
+        var hasFocusFalseCount = 0;
+
+        var view = new View
+        {
+            Id = "view",
+            CanFocus = true
+        };
+        view.HasFocusChanging += (s, e) =>
+                                 {
+                                     if (e.NewValue)
+                                     {
+                                         hasFocusTrueCount++;
+                                     }
+                                     else
+                                     {
+                                         hasFocusFalseCount++;
+                                     }
+                                 };
+
+        var subviewHasFocusTrueCount = 0;
+        var subviewHasFocusFalseCount = 0;
+
+        var subview = new View
+        {
+            Id = "subview",
+            CanFocus = true
+        };
+        subview.HasFocusChanging += (s, e) =>
+                                    {
+                                        if (e.NewValue)
+                                        {
+                                            subviewHasFocusTrueCount++;
+                                        }
+                                        else
+                                        {
+                                            subviewHasFocusFalseCount++;
+                                        }
+                                    };
+
+        view.Add (subview);
+
+        subview.SetFocus ();
+
+        Assert.Equal (1, hasFocusTrueCount);
         Assert.Equal (0, hasFocusFalseCount);
 
         Assert.Equal (1, subviewHasFocusTrueCount);
         Assert.Equal (0, subviewHasFocusFalseCount);
+
+        subview.HasFocus = false;
+        Assert.False (subview.HasFocus);
+        Assert.Equal (1, hasFocusTrueCount);
+        Assert.Equal (0, hasFocusFalseCount);
+
+        Assert.Equal (1, subviewHasFocusTrueCount);
+        Assert.Equal (1, subviewHasFocusFalseCount);
+
     }
+    #endregion HasFocusChanging_NewValue_False
+
+    #region HasFocusChanged
 
     [Fact]
-    public void RemoveFocus_Raises_HasFocusChanged ()
+    public void HasFocusChanged_RemoveFocus_Raises ()
     {
-        var nEnter = 0;
-        var nLeave = 0;
+        var newValueTrueCount = 0;
+        var newValueFalseCount = 0;
 
         var view = new View
         {
             Id = "view",
             CanFocus = true
         };
-        view.HasFocusChanging += (s, e) => nEnter++;
-        view.HasFocusChanged += (s, e) => nLeave++;
+        view.HasFocusChanged += (s, e) =>
+                                {
+                                    if (e.NewValue)
+                                    {
+                                        newValueTrueCount++;
+                                    }
+                                    else
+                                    {
+                                        newValueFalseCount++;
+                                    }
+                                };
 
         Assert.True (view.CanFocus);
         Assert.False (view.HasFocus);
 
         view.SetFocus ();
         Assert.True (view.HasFocus);
-        Assert.Equal (1, nEnter);
-        Assert.Equal (0, nLeave);
+        Assert.Equal (1, newValueTrueCount);
+        Assert.Equal (0, newValueFalseCount);
 
         view.HasFocus = false;
-        Assert.Equal (1, nEnter);
-        Assert.Equal (1, nLeave);
+        Assert.Equal (1, newValueTrueCount);
+        Assert.Equal (1, newValueFalseCount);
     }
 
 
     [Fact]
-    public void RemoveFocus_SubView_Raises_HasFocusChanged ()
+    public void HasFocusChanged_With_SubView_Raises ()
     {
-        var viewEnterCount = 0;
-        var viewLeaveCount = 0;
+        var newValueTrueCount = 0;
+        var newValueFalseCount = 0;
 
         var view = new View
         {
             Id = "view",
             CanFocus = true
         };
-        view.HasFocusChanging += (s, e) => viewEnterCount++;
-        view.HasFocusChanged += (s, e) => viewLeaveCount++;
+        view.HasFocusChanged += (s, e) =>
+                                {
+                                    if (e.NewValue)
+                                    {
+                                        newValueTrueCount++;
+                                    }
+                                    else
+                                    {
+                                        newValueFalseCount++;
+                                    }
+                                };
 
-        var subviewEnterCount = 0;
-        var subviewLeaveCount = 0;
+        var subviewNewValueTrueCount = 0;
+        var subviewNewValueFalseCount = 0;
 
         var subview = new View
         {
             Id = "subview",
             CanFocus = true
         };
-        subview.HasFocusChanging += (s, e) => subviewEnterCount++;
-        subview.HasFocusChanged += (s, e) => subviewLeaveCount++;
+        subview.HasFocusChanged += (s, e) =>
+                                   {
+                                       if (e.NewValue)
+                                       {
+                                           subviewNewValueTrueCount++;
+                                       }
+                                       else
+                                       {
+                                           subviewNewValueFalseCount++;
+                                       }
+                                   };
 
         view.Add (subview);
 
         view.SetFocus ();
+        Assert.Equal (1, newValueTrueCount);
+        Assert.Equal (0, newValueFalseCount);
+
+        Assert.Equal (1, subviewNewValueTrueCount);
+        Assert.Equal (0, subviewNewValueFalseCount);
 
         view.HasFocus = false;
 
-        Assert.Equal (1, viewEnterCount);
-        Assert.Equal (1, viewLeaveCount);
+        Assert.Equal (1, newValueTrueCount);
+        Assert.Equal (1, newValueFalseCount);
 
-        Assert.Equal (1, subviewEnterCount);
-        Assert.Equal (1, subviewLeaveCount);
+        Assert.Equal (1, subviewNewValueTrueCount);
+        Assert.Equal (1, subviewNewValueFalseCount);
 
         view.SetFocus ();
 
-        Assert.Equal (2, viewEnterCount);
-        Assert.Equal (1, viewLeaveCount);
+        Assert.Equal (2, newValueTrueCount);
+        Assert.Equal (1, newValueFalseCount);
 
-        Assert.Equal (2, subviewEnterCount);
-        Assert.Equal (1, subviewLeaveCount);
+        Assert.Equal (2, subviewNewValueTrueCount);
+        Assert.Equal (1, subviewNewValueFalseCount);
 
         subview.HasFocus = false;
 
-        Assert.Equal (2, viewEnterCount);
-        Assert.Equal (2, viewLeaveCount);
+        Assert.Equal (2, newValueTrueCount);
+        Assert.Equal (1, newValueFalseCount);
 
-        Assert.Equal (2, subviewEnterCount);
-        Assert.Equal (2, subviewLeaveCount);
+        Assert.Equal (2, subviewNewValueTrueCount);
+        Assert.Equal (2, subviewNewValueFalseCount);
     }
 
+
     [Fact]
-    public void RemoveFocus_CompoundSubView_Raises_HasFocusChanged ()
+    public void HasFocusChanged_CompoundSubView_Raises ()
     {
         var viewEnterCount = 0;
         var viewLeaveCount = 0;
@@ -602,8 +821,17 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "view",
             CanFocus = true
         };
-        view.HasFocusChanging += (s, e) => viewEnterCount++;
-        view.HasFocusChanged += (s, e) => viewLeaveCount++;
+        view.HasFocusChanged += (s, e) =>
+                                {
+                                    if (e.NewValue)
+                                    {
+                                        viewEnterCount++;
+                                    }
+                                    else
+                                    {
+                                        viewLeaveCount++;
+                                    }
+                                };
 
         var subViewEnterCount = 0;
         var subViewLeaveCount = 0;
@@ -613,8 +841,17 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subView",
             CanFocus = true
         };
-        subView.HasFocusChanging += (s, e) => subViewEnterCount++;
-        subView.HasFocusChanged += (s, e) => subViewLeaveCount++;
+        subView.HasFocusChanged += (s, e) =>
+                                   {
+                                       if (e.NewValue)
+                                       {
+                                           subViewEnterCount++;
+                                       }
+                                       else
+                                       {
+                                           subViewLeaveCount++;
+                                       }
+                                   };
 
         var subviewSubView1EnterCount = 0;
         var subviewSubView1LeaveCount = 0;
@@ -624,8 +861,17 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subViewSubView1",
             CanFocus = false
         };
-        subViewSubView1.HasFocusChanging += (s, e) => subviewSubView1EnterCount++;
-        subViewSubView1.HasFocusChanged += (s, e) => subviewSubView1LeaveCount++;
+        subViewSubView1.HasFocusChanged += (s, e) =>
+                                           {
+                                               if (e.NewValue)
+                                               {
+                                                   subviewSubView1EnterCount++;
+                                               }
+                                               else
+                                               {
+                                                   subviewSubView1LeaveCount++;
+                                               }
+                                           };
 
         var subviewSubView2EnterCount = 0;
         var subviewSubView2LeaveCount = 0;
@@ -635,9 +881,17 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subViewSubView2",
             CanFocus = true
         };
-        subViewSubView2.HasFocusChanging += (s, e) => subviewSubView2EnterCount++;
-        subViewSubView2.HasFocusChanged += (s, e) => subviewSubView2LeaveCount++;
-
+        subViewSubView2.HasFocusChanged += (s, e) =>
+                                           {
+                                               if (e.NewValue)
+                                               {
+                                                   subviewSubView2EnterCount++;
+                                               }
+                                               else
+                                               {
+                                                   subviewSubView2LeaveCount++;
+                                               }
+                                           };
         var subviewSubView3EnterCount = 0;
         var subviewSubView3LeaveCount = 0;
 
@@ -646,8 +900,17 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
             Id = "subViewSubView3",
             CanFocus = false
         };
-        subViewSubView3.HasFocusChanging += (s, e) => subviewSubView3EnterCount++;
-        subViewSubView3.HasFocusChanged += (s, e) => subviewSubView3LeaveCount++;
+        subViewSubView3.HasFocusChanged += (s, e) =>
+                                           {
+                                               if (e.NewValue)
+                                               {
+                                                   subviewSubView3EnterCount++;
+                                               }
+                                               else
+                                               {
+                                                   subviewSubView3LeaveCount++;
+                                               }
+                                           };
 
         subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
 
@@ -682,59 +945,5 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView
         Assert.Equal (0, subviewSubView3EnterCount);
         Assert.Equal (0, subviewSubView3LeaveCount);
     }
-
-    [Fact]
-    public void HasFocus_False_Leave_Raised ()
-    {
-        var view = new View
-        {
-            Id = "view",
-            CanFocus = true
-        };
-        Assert.True (view.CanFocus);
-        Assert.False (view.HasFocus);
-
-        var leaveInvoked = 0;
-
-        view.HasFocusChanged += (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_Raised_ForAllSubViews ()
-    {
-        var view = new View
-        {
-            Id = "view",
-            CanFocus = true
-        };
-
-        var subview = new View
-        {
-            Id = "subview",
-            CanFocus = true
-        };
-        view.Add (subview);
-
-        var leaveInvoked = 0;
-
-        view.HasFocusChanged += (s, e) => leaveInvoked++;
-        subview.HasFocusChanged += (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);
-    }
+    #endregion HasFocusChanged
 }

+ 3 - 3
UnitTests/View/Navigation/HasFocusTests.cs

@@ -6,7 +6,7 @@ public class HasFocusTests (ITestOutputHelper _output) : TestsAllViews
 {
 
     [Fact]
-    public void HasFocus_False_Leaves ()
+    public void HasFocus_False ()
     {
         var view = new View ()
         {
@@ -22,7 +22,7 @@ public class HasFocusTests (ITestOutputHelper _output) : TestsAllViews
     }
 
     [Fact]
-    public void HasFocus_False_WithSuperView_Leaves_All ()
+    public void HasFocus_False_WithSuperView_Does_Not_Change_SuperView ()
     {
         var view = new View ()
         {
@@ -42,8 +42,8 @@ public class HasFocusTests (ITestOutputHelper _output) : TestsAllViews
         Assert.True (subview.HasFocus);
 
         subview.HasFocus = false;
-        Assert.False (view.HasFocus);
         Assert.False (subview.HasFocus);
+        Assert.True (view.HasFocus);
     }
 
     [Fact]

+ 29 - 124
UnitTests/View/Navigation/NavigationTests.cs

@@ -8,7 +8,8 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
 {
     [Theory]
     [MemberData (nameof (AllViewTypes))]
-    public void AllViews_AtLeastOneNavKey_Leaves (Type viewType)
+    [SetupFakeDriver] // SetupFakeDriver resets app state; helps to avoid test pollution
+    public void AllViews_AtLeastOneNavKey_Advances (Type viewType)
     {
         View view = CreateInstanceIfNotGeneric (viewType);
 
@@ -26,9 +27,10 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
             return;
         }
 
-        Application.Init (new FakeDriver ());
 
         Toplevel top = new ();
+        Application.Current = top;
+        Application.Navigation = new ApplicationNavigation ();
 
         View otherView = new ()
         {
@@ -38,12 +40,11 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
         };
 
         top.Add (view, otherView);
-        Application.Begin (top);
 
         // Start with the focus on our test view
         view.SetFocus ();
 
-        Key [] navKeys = { Key.Tab, Key.Tab.WithShift, Key.CursorUp, Key.CursorDown, Key.CursorLeft, Key.CursorRight };
+        Key [] navKeys = [Key.Tab, Key.Tab.WithShift, Key.CursorUp, Key.CursorDown, Key.CursorLeft, Key.CursorRight];
 
         if (view.TabStop == TabBehavior.TabGroup)
         {
@@ -81,14 +82,14 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
         }
 
         top.Dispose ();
-        Application.Shutdown ();
+        Application.ResetState ();
 
         Assert.True (left);
     }
 
     [Theory]
     [MemberData (nameof (AllViewTypes))]
-    [SetupFakeDriver]
+    [SetupFakeDriver] // SetupFakeDriver resets app state; helps to avoid test pollution
     public void AllViews_HasFocus_Changed_Event (Type viewType)
     {
         View view = CreateInstanceIfNotGeneric (viewType);
@@ -114,29 +115,17 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
             return;
         }
 
-        Toplevel top = new ()
-        {
-            Height = 10,
-            Width = 10
-        };
+        Toplevel top = new ();
         Application.Current = top;
-        Application.Navigation = new ApplicationNavigation();
+        Application.Navigation = new ApplicationNavigation ();
 
         View otherView = new ()
         {
             Id = "otherView",
-            X = 0, Y = 0,
-            Height = 1,
-            Width = 1,
             CanFocus = true,
             TabStop = view.TabStop == TabBehavior.NoStop ? TabBehavior.TabStop : view.TabStop
         };
 
-        view.X = Pos.Right (otherView);
-        view.Y = 0;
-        view.Width = 10;
-        view.Height = 1;
-
         var hasFocusTrue = 0;
         var hasFocusFalse = 0;
 
@@ -256,7 +245,8 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
 
     [Theory]
     [MemberData (nameof (AllViewTypes))]
-    public void AllViews_Enter_Leave_Events_Visible_False (Type viewType)
+    [SetupFakeDriver] // SetupFakeDriver resets app state; helps to avoid test pollution
+    public void AllViews_Visible_False_No_HasFocus_Events (Type viewType)
     {
         View view = CreateInstanceIfNotGeneric (viewType);
 
@@ -281,70 +271,46 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
             return;
         }
 
-        Application.Init (new FakeDriver ());
+        Toplevel top = new ();
 
-        Toplevel top = new ()
-        {
-            Height = 10,
-            Width = 10
-        };
+        Application.Current = top;
+        Application.Navigation = new ApplicationNavigation ();
 
         View otherView = new ()
         {
-            X = 0, Y = 0,
-            Height = 1,
-            Width = 1,
             CanFocus = true
         };
 
         view.Visible = false;
-        view.X = Pos.Right (otherView);
-        view.Y = 0;
-        view.Width = 10;
-        view.Height = 1;
 
-        var nEnter = 0;
-        var nLeave = 0;
+        var hasFocusChangingCount = 0;
+        var hasFocusChangedCount = 0;
 
-        view.HasFocusChanging += (s, e) => nEnter++;
-        view.HasFocusChanged += (s, e) => nLeave++;
+        view.HasFocusChanging += (s, e) => hasFocusChangingCount++;
+        view.HasFocusChanged += (s, e) => hasFocusChangedCount++;
 
         top.Add (view, otherView);
-        Application.Begin (top);
 
         // Start with the focus on our test view
         view.SetFocus ();
 
-        Assert.Equal (0, nEnter);
-        Assert.Equal (0, nLeave);
+        Assert.Equal (0, hasFocusChangingCount);
+        Assert.Equal (0, hasFocusChangedCount);
 
-        // Use keyboard to navigate to next view (otherView). 
-        if (view is TextView)
-        {
-            Application.OnKeyDown (Key.F6);
-        }
-        else if (view is DatePicker)
-        {
-            for (var i = 0; i < 4; i++)
-            {
-                Application.OnKeyDown (Key.F6);
-            }
-        }
-        else
-        {
-            Application.OnKeyDown (Key.Tab);
-        }
+        Application.OnKeyDown (Key.Tab);
 
-        Assert.Equal (0, nEnter);
-        Assert.Equal (0, nLeave);
+        Assert.Equal (0, hasFocusChangingCount);
+        Assert.Equal (0, hasFocusChangedCount);
 
-        top.NewKeyDownEvent (Key.Tab);
+        Application.OnKeyDown (Key.F6);
 
-        Assert.Equal (0, nEnter);
-        Assert.Equal (0, nLeave);
+        Assert.Equal (0, hasFocusChangingCount);
+        Assert.Equal (0, hasFocusChangedCount);
 
         top.Dispose ();
-        Application.Shutdown ();
+
+        Application.ResetState ();
+
     }
 
     [Fact]
@@ -444,67 +410,6 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
         Application.Current.Dispose ();
     }
 
-    [Fact]
-    [AutoInitShutdown]
-    public void FocusNext_Does_Not_Throws_If_A_View_Was_Removed_From_The_Collection ()
-    {
-        Toplevel top1 = new ();
-        var view1 = new View { Id = "view1", Width = 10, Height = 5, CanFocus = true };
-        var top2 = new Toplevel { Id = "top2", Y = 1, Width = 10, Height = 5 };
-
-        var view2 = new View
-        {
-            Id = "view2",
-            Y = 1,
-            Width = 10,
-            Height = 5,
-            CanFocus = true
-        };
-        View view3 = null;
-        var removed = false;
-
-        view2.HasFocusChanging += (s, e) =>
-                       {
-                           if (!removed)
-                           {
-                               removed = true;
-                               view3 = new () { Id = "view3", Y = 1, Width = 10, Height = 5 };
-                               Application.Current.Add (view3);
-                               Application.Current.BringSubviewToFront (view3);
-                               Assert.False (view3.HasFocus);
-                           }
-                       };
-
-        view2.HasFocusChanged += (s, e) =>
-                       {
-                           Application.Current.Remove (view3);
-                           view3.Dispose ();
-                           view3 = null;
-                       };
-        top2.Add (view2);
-        top1.Add (view1, top2);
-        Application.Begin (top1);
-
-        Assert.True (top1.HasFocus);
-        Assert.True (view1.HasFocus);
-        Assert.False (view2.HasFocus);
-        Assert.False (removed);
-        Assert.Null (view3);
-
-        Assert.True (Application.OnKeyDown (Key.F6));
-        Assert.True (top1.HasFocus);
-        Assert.False (view1.HasFocus);
-        Assert.True (view2.HasFocus);
-        Assert.True (removed);
-        Assert.NotNull (view3);
-
-        Exception exception = Record.Exception (() => Application.OnKeyDown (Key.F6));
-        Assert.Null (exception);
-        Assert.True (removed);
-        //Assert.Null (view3);
-        top1.Dispose ();
-    }
-
     [Fact]
     public void GetMostFocused_NoSubviews_Returns_Null ()
     {

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

@@ -224,7 +224,7 @@ public class SetFocusTests (ITestOutputHelper _output) : TestsAllViews
     }
 
     [Fact]
-    public void SetFocus_Peer_LeavesOthers_Subviews ()
+    public void SetFocus_On_Peer_Moves_Focus_To_Peer ()
     {
         var top = new View
         {

+ 1 - 1
UnitTests/Views/MenuBarTests.cs

@@ -2560,7 +2560,7 @@ Edit
         top.Draw ();
         TestHelpers.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output);
 
-        Assert.True (menu.NewKeyDownEvent (menu.Key));
+        Assert.True (Application.OnKeyDown(menu.Key));
         Assert.False (menu.IsMenuOpen);
         Assert.True (tf.HasFocus);
         top.Draw ();

+ 1 - 1
UnitTests/Views/ShortcutTests.cs

@@ -611,7 +611,7 @@ public class ShortcutTests
 
         shortcut.HasFocus = true;
 
-        Assert.Null (shortcut.ColorScheme);
+        Assert.NotNull (shortcut.ColorScheme);
 
         Application.Current.Dispose ();
         Application.ResetState ();