diff --git a/Terminal.Gui/Application/Application.Keyboard.cs b/Terminal.Gui/Application/Application.Keyboard.cs index 4653f5419..d795075eb 100644 --- a/Terminal.Gui/Application/Application.Keyboard.cs +++ b/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; } ); diff --git a/Terminal.Gui/Application/Application.Run.cs b/Terminal.Gui/Application/Application.Run.cs index c29c0272f..51e0ac6c7 100644 --- a/Terminal.Gui/Application/Application.Run.cs +++ b/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) diff --git a/Terminal.Gui/Application/ApplicationNavigation.cs b/Terminal.Gui/Application/ApplicationNavigation.cs index a7c13bcf8..6e6afe915 100644 --- a/Terminal.Gui/Application/ApplicationNavigation.cs +++ b/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 (); - } - } } diff --git a/Terminal.Gui/View/View.Navigation.cs b/Terminal.Gui/View/View.Navigation.cs index df8577396..8f06fbb19 100644 --- a/Terminal.Gui/View/View.Navigation.cs +++ b/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 /// public bool SetFocus () { - return SetHasFocusTrue (Application.Navigation?.GetFocused ()); + (bool focusSet, bool _) = SetHasFocusTrue (Application.Navigation?.GetFocused ()); + return focusSet; } /// @@ -78,82 +80,58 @@ public partial class View // Focus and cross-view navigation management (TabStop /// /// if was changed to . /// - 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) + bool cancelled = NotifyFocusChanging (false, true, previousFocusedView, this); + + if (cancelled) { - - // 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; - } + return (false, true); } - if (NotifyFocusChanging (false, true, previousFocusedView, this)) - { - return false; - } + //// 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'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); - - // 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 /// /// Invoked when is about to change. This method is called before the event is raised. /// + /// + /// + /// Use to be notified after the focus has changed. + /// + /// /// The current value of . - /// The value will have if the event is not cancelled. + /// The value will have if the focus change happens. /// The view that is currently Focused. May be . /// The view that will be focused. May be . /// , if the change to is to be cancelled, otherwise. @@ -214,57 +209,70 @@ public partial class View // Focus and cross-view navigation management (TabStop return false; } - /// Raised when the view is gaining (entering) focus. Can be cancelled. + /// + /// Raised when is about to change. + /// + /// + /// + /// Cancel the event to prevent the focus from changing. + /// + /// + /// Use to be notified after the focus has changed. + /// + /// public event EventHandler HasFocusChanging; /// - /// Called when focus has changed to another view. + /// Called when this view should stop being focused. /// - /// The view that now has focus. If there is no view that has focus. + /// The new focused view. If it is not known which view will be focused. /// - 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; } diff --git a/Terminal.Gui/Views/ComboBox.cs b/Terminal.Gui/Views/ComboBox.cs index 67648948a..70223c4cf 100644 --- a/Terminal.Gui/Views/ComboBox.cs +++ b/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); } - /// - 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; - } /// Virtual method which invokes the event. public virtual void OnExpanded () { Expanded?.Invoke (this, EventArgs.Empty); } @@ -319,25 +307,34 @@ public class ComboBox : View, IDesignable /// 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 (); + if (_autoHide && IsShow && view != this && view != _search && view != _listview) + { + IsShow = false; + HideList (); + } + else if (_listview.TabStop?.HasFlag (TabBehavior.TabStop) ?? false) + { + _listview.TabStop = TabBehavior.NoStop; + } } - else if (_listview.TabStop?.HasFlag (TabBehavior.TabStop) ?? false) - { - _listview.TabStop = TabBehavior.NoStop; - } - - return; } /// Invokes the OnOpenSelectedItem event if it is defined. @@ -565,7 +562,7 @@ public class ComboBox : View, IDesignable { if (HasItems ()) { - return _listview.MoveUp (); + return _listview.MoveUp (); } return false; diff --git a/Terminal.Gui/Views/FileDialog.cs b/Terminal.Gui/Views/FileDialog.cs index 6251c12fb..655359135 100644 --- a/Terminal.Gui/Views/FileDialog.cs +++ b/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) => { diff --git a/Terminal.Gui/Views/HexView.cs b/Terminal.Gui/Views/HexView.cs index e96eceb17..49228ecc9 100644 --- a/Terminal.Gui/Views/HexView.cs +++ b/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; diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index 955a21499..2c1571780 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -739,14 +739,12 @@ public class ListView : View, IDesignable } /// - 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 diff --git a/Terminal.Gui/Views/Menu/MenuBar.cs b/Terminal.Gui/Views/Menu/MenuBar.cs index 611da8112..fcded5f35 100644 --- a/Terminal.Gui/Views/Menu/MenuBar.cs +++ b/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 } /// Opens the Menu programatically, as though the F9 key were pressed. - 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; } /// @@ -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 (); } } } diff --git a/UnitTests/Application/Application.NavigationTests.cs b/UnitTests/Application/Application.NavigationTests.cs index 6f3a1edc4..879eda872 100644 --- a/UnitTests/Application/Application.NavigationTests.cs +++ b/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 (); + //} } diff --git a/UnitTests/View/Navigation/AddRemoveTests.cs b/UnitTests/View/Navigation/AddRemoveTests.cs index 7fbfddabe..d52222e3d 100644 --- a/UnitTests/View/Navigation/AddRemoveTests.cs +++ b/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); } - } diff --git a/UnitTests/View/Navigation/HasFocusChangeEventTests.cs b/UnitTests/View/Navigation/HasFocusChangeEventTests.cs index 4a6167182..84fb4a8ec 100644 --- a/UnitTests/View/Navigation/HasFocusChangeEventTests.cs +++ b/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); - Assert.False (subview.HasFocus); + 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 RemoveFocus_Raises_HasFocusChanged () + public void HasFocusChanging_RemoveFocus_Raises () { - var nEnter = 0; - var nLeave = 0; + var hasFocusTrueCount = 0; + var hasFocusFalseCount = 0; var view = new View { Id = "view", CanFocus = true }; - view.HasFocusChanging += (s, e) => nEnter++; - view.HasFocusChanged += (s, e) => nLeave++; + 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, nEnter); - Assert.Equal (0, nLeave); + Assert.Equal (1, hasFocusTrueCount); + Assert.Equal (0, hasFocusFalseCount); view.HasFocus = false; - Assert.Equal (1, nEnter); - Assert.Equal (1, nLeave); + Assert.False (view.HasFocus); + Assert.Equal (1, hasFocusTrueCount); + Assert.Equal (1, hasFocusFalseCount); } [Fact] - public void RemoveFocus_SubView_Raises_HasFocusChanged () + public void HasFocusChanging_RemoveFocus_SubView_SetFocus_Raises () { - var viewEnterCount = 0; - var viewLeaveCount = 0; + var hasFocusTrueCount = 0; + var hasFocusFalseCount = 0; var view = new View { Id = "view", CanFocus = true }; - view.HasFocusChanging += (s, e) => viewEnterCount++; - view.HasFocusChanged += (s, e) => viewLeaveCount++; + view.HasFocusChanging += (s, e) => + { + if (e.NewValue) + { + hasFocusTrueCount++; + } + else + { + hasFocusFalseCount++; + } + }; - var subviewEnterCount = 0; - var subviewLeaveCount = 0; + var subviewHasFocusTrueCount = 0; + var subviewHasFocusFalseCount = 0; var subview = new View { Id = "subview", CanFocus = true }; - subview.HasFocusChanging += (s, e) => subviewEnterCount++; - subview.HasFocusChanged += (s, e) => subviewLeaveCount++; + 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 (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 HasFocusChanged_RemoveFocus_Raises () + { + var newValueTrueCount = 0; + var newValueFalseCount = 0; + + var view = new View + { + Id = "view", + CanFocus = true + }; + 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, newValueTrueCount); + Assert.Equal (0, newValueFalseCount); + + view.HasFocus = false; + Assert.Equal (1, newValueTrueCount); + Assert.Equal (1, newValueFalseCount); + } + + + [Fact] + public void HasFocusChanged_With_SubView_Raises () + { + var newValueTrueCount = 0; + var newValueFalseCount = 0; + + var view = new View + { + Id = "view", + CanFocus = true + }; + view.HasFocusChanged += (s, e) => + { + if (e.NewValue) + { + newValueTrueCount++; + } + else + { + newValueFalseCount++; + } + }; + + var subviewNewValueTrueCount = 0; + var subviewNewValueFalseCount = 0; + + var subview = new View + { + Id = "subview", + CanFocus = true + }; + 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 } diff --git a/UnitTests/View/Navigation/HasFocusTests.cs b/UnitTests/View/Navigation/HasFocusTests.cs index 393a231e2..fb6b1beef 100644 --- a/UnitTests/View/Navigation/HasFocusTests.cs +++ b/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] diff --git a/UnitTests/View/Navigation/NavigationTests.cs b/UnitTests/View/Navigation/NavigationTests.cs index d89b4e0f8..cdc639b53 100644 --- a/UnitTests/View/Navigation/NavigationTests.cs +++ b/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 () { diff --git a/UnitTests/View/Navigation/SetFocusTests.cs b/UnitTests/View/Navigation/SetFocusTests.cs index 77cffe72f..2d3a5abd3 100644 --- a/UnitTests/View/Navigation/SetFocusTests.cs +++ b/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 { diff --git a/UnitTests/Views/MenuBarTests.cs b/UnitTests/Views/MenuBarTests.cs index 9b1067290..13fea73ab 100644 --- a/UnitTests/Views/MenuBarTests.cs +++ b/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 (); diff --git a/UnitTests/Views/ShortcutTests.cs b/UnitTests/Views/ShortcutTests.cs index 55b6df739..1d6ac98ce 100644 --- a/UnitTests/Views/ShortcutTests.cs +++ b/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 ();