diff --git a/Terminal.Gui/View/View.Drawing.cs b/Terminal.Gui/View/View.Drawing.cs index d1a0f7734..3ec493b57 100644 --- a/Terminal.Gui/View/View.Drawing.cs +++ b/Terminal.Gui/View/View.Drawing.cs @@ -505,7 +505,7 @@ public partial class View // Drawing APIs if (TabStop == TabBehavior.TabGroup && _subviews.Count(v => v.Arrangement.HasFlag (ViewArrangement.Overlapped)) > 0) { // TODO: This is a temporary hack to make overlapped non-Toplevels have a zorder. See also View.SetFocus - subviewsNeedingDraw = _tabIndexes.Where ( + subviewsNeedingDraw = _subviews.Where ( view => view.Visible && (view.NeedsDisplay || view.SubViewNeedsDisplay || view.LayoutNeeded) ).Reverse (); diff --git a/Terminal.Gui/View/View.Hierarchy.cs b/Terminal.Gui/View/View.Hierarchy.cs index e43ef7a9c..5e899e809 100644 --- a/Terminal.Gui/View/View.Hierarchy.cs +++ b/Terminal.Gui/View/View.Hierarchy.cs @@ -52,21 +52,10 @@ public partial class View // SuperView/SubView hierarchy management (SuperView, _subviews = new (); } - if (_tabIndexes is null) - { - _tabIndexes = new (); - } - Debug.WriteLineIf (_subviews.Contains (view), $"BUGBUG: {view} has already been added to {this}."); _subviews.Add (view); - _tabIndexes.Add (view); view._superView = this; - if (view.CanFocus) - { - view._tabIndex = _tabIndexes.IndexOf (view); - } - if (view.Enabled && view.Visible && view.CanFocus) { // Add will cause the newly added subview to gain focus if it's focusable @@ -179,16 +168,14 @@ public partial class View // SuperView/SubView hierarchy management (SuperView, } Rectangle touched = view.Frame; - _subviews.Remove (view); - _tabIndexes!.Remove (view); - view._superView = null; - //view._tabIndex = -1; // If a view being removed is focused, it should lose focus. if (view.HasFocus) { view.HasFocus = false; } + _subviews.Remove (view); + view._superView = null; // Null this AFTER removing focus SetNeedsLayout (); SetNeedsDisplay (); diff --git a/Terminal.Gui/View/View.Navigation.cs b/Terminal.Gui/View/View.Navigation.cs index 255b5a307..315b53488 100644 --- a/Terminal.Gui/View/View.Navigation.cs +++ b/Terminal.Gui/View/View.Navigation.cs @@ -8,7 +8,7 @@ public partial class View // Focus and cross-view navigation management (TabStop private bool _canFocus; /// - /// Advances the focus to the next or previous view in , based on + /// Advances the focus to the next or previous view in the focus chain, based on /// . /// itself. /// @@ -30,11 +30,6 @@ public partial class View // Focus and cross-view navigation management (TabStop return false; } - if (TabIndexes is null || TabIndexes.Count == 0) - { - return false; - } - View? focused = Focused; if (focused is { } && focused.AdvanceFocus (direction, behavior)) @@ -42,14 +37,14 @@ public partial class View // Focus and cross-view navigation management (TabStop return true; } - View [] index = GetScopedTabIndexes (direction, behavior); + View [] index = GetSubviewFocusChain (direction, behavior); if (index.Length == 0) { return false; } - int focusedIndex = index.IndexOf (Focused); + int focusedIndex = index.IndexOf (Focused); // Will return -1 if Focused can't be found or is null var next = 0; if (focusedIndex < index.Length - 1) @@ -69,7 +64,7 @@ public partial class View // Focus and cross-view navigation management (TabStop //} // - If we are TabStop and our SuperView has at least one other TabStop subview, move to the SuperView's chain - if (TabStop == TabBehavior.TabStop && SuperView is { } && SuperView.GetScopedTabIndexes (direction, behavior).Length > 1) + if (TabStop == TabBehavior.TabStop && SuperView is { } && SuperView.GetSubviewFocusChain (direction, behavior).Length > 1) { return false; } @@ -81,7 +76,7 @@ public partial class View // Focus and cross-view navigation management (TabStop { // Wrap to first focusable views // BUGBUG: This should do a Restore Focus instead - index = GetScopedTabIndexes (direction, null); + index = GetSubviewFocusChain (direction, null); } } } @@ -110,10 +105,7 @@ public partial class View // Focus and cross-view navigation management (TabStop /// the next focusable view. /// /// - /// When set to , the will be set to -1. - /// - /// - /// When set to , the values of and for all + /// When set to , the value of for all /// subviews will be cached so that when is set back to , the subviews /// will be restored to their previous values. /// @@ -163,8 +155,7 @@ public partial class View // Focus and cross-view navigation management (TabStop public event EventHandler CanFocusChanged; /// - /// Focuses the deepest focusable view in if one exists. If there are no views in - /// then the focus is set to the view itself. + /// Focuses the deepest focusable Subview if one exists. If there are no focusable Subviews then the focus is set to the view itself. /// /// /// @@ -244,15 +235,10 @@ public partial class View // Focus and cross-view navigation management (TabStop private View? FindDeepestFocusableView (NavigationDirection direction, TabBehavior? behavior) { - View [] indicies = GetScopedTabIndexes (direction, behavior); + View [] indicies = GetSubviewFocusChain (direction, behavior); foreach (View v in indicies) { - if (v.TabIndexes.Count == 0) - { - return v; - } - return v.FindDeepestFocusableView (direction, behavior); } @@ -580,6 +566,16 @@ public partial class View // Focus and cross-view navigation management (TabStop View? focusedPeer = SuperView?.Focused; _hasFocus = false; + if (Application.Navigation is { }) + { + View? appFocused = Application.Navigation.GetFocused (); + + if (appFocused is { } || appFocused == this) + { + Application.Navigation.SetFocused (SuperView); + } + } + NotifyFocusChanged (HasFocus, this, newFocusedVew); if (_hasFocus) @@ -643,14 +639,6 @@ public partial class View // Focus and cross-view navigation management (TabStop #region Tab/Focus Handling - private List? _tabIndexes; - - // TODO: This should be a get-only property? - // BUGBUG: This returns an AsReadOnly list, but isn't declared as such. - /// Gets a list of the subviews that are a . - /// The tabIndexes. - public IList TabIndexes => _tabIndexes?.AsReadOnly () ?? _empty; - /// /// Gets TabIndexes that are scoped to the specified behavior and direction. If behavior is null, all TabIndexes are /// returned. @@ -659,138 +647,25 @@ public partial class View // Focus and cross-view navigation management (TabStop /// /// /// GetScopedTabIndexes - private View [] GetScopedTabIndexes (NavigationDirection direction, TabBehavior? behavior) + private View [] GetSubviewFocusChain (NavigationDirection direction, TabBehavior? behavior) { - IEnumerable? indicies; + IEnumerable? fitleredSubviews; if (behavior.HasValue) { - indicies = _tabIndexes?.Where (v => v.TabStop == behavior && v is { CanFocus: true, Visible: true, Enabled: true }); + fitleredSubviews = _subviews?.Where (v => v.TabStop == behavior && v is { CanFocus: true, Visible: true, Enabled: true }); } else { - indicies = _tabIndexes?.Where (v => v is { CanFocus: true, Visible: true, Enabled: true }); + fitleredSubviews = _subviews?.Where (v => v is { CanFocus: true, Visible: true, Enabled: true }); } if (direction == NavigationDirection.Backward) { - indicies = indicies?.Reverse (); + fitleredSubviews = fitleredSubviews?.Reverse (); } - return indicies?.ToArray () ?? Array.Empty (); - } - - private int? _tabIndex; // null indicates the view has not yet been added to TabIndexes - - /// - /// Indicates the order of the current in list. - /// - /// - /// - /// If , the view is not part of the tab order. - /// - /// - /// On set, if is or has not TabStops, will - /// be set to 0. - /// - /// - /// On set, if has only one TabStop, will be set to 0. - /// - /// - /// See also . - /// - /// - public int? TabIndex - { - get => _tabIndex; - - // TOOD: This should be a get-only property. Introduce SetTabIndex (int value) (or similar). - set - { - // Once a view is in the tab order, it should not be removed from the tab order; set TabStop to NoStop instead. - Debug.Assert (value >= 0); - Debug.Assert (value is { }); - - if (SuperView?._tabIndexes is null || SuperView?._tabIndexes.Count == 1) - { - // BUGBUG: Property setters should set the property to the value passed in and not have side effects. - _tabIndex = 0; - - return; - } - - if (_tabIndex == value && TabIndexes.IndexOf (this) == value) - { - return; - } - - _tabIndex = value > SuperView!.TabIndexes.Count - 1 ? SuperView._tabIndexes.Count - 1 : - value < 0 ? 0 : value; - _tabIndex = GetGreatestTabIndexInSuperView ((int)_tabIndex); - - if (SuperView._tabIndexes.IndexOf (this) != _tabIndex) - { - // BUGBUG: we have to use _tabIndexes and not TabIndexes because TabIndexes returns is a read-only version of _tabIndexes - SuperView._tabIndexes.Remove (this); - SuperView._tabIndexes.Insert ((int)_tabIndex, this); - UpdatePeerTabIndexes (); - } - - return; - - // Updates the s of the views in the 's to match their order in . - void UpdatePeerTabIndexes () - { - if (SuperView is null) - { - return; - } - - var i = 0; - - foreach (View superViewTabStop in SuperView._tabIndexes) - { - if (superViewTabStop._tabIndex is null) - { - continue; - } - - superViewTabStop._tabIndex = i; - i++; - } - } - } - } - - /// - /// Gets the greatest of the 's that is less - /// than or equal to . - /// - /// - /// The minimum of and the 's . - private int GetGreatestTabIndexInSuperView (int idx) - { - if (SuperView is null) - { - return 0; - } - - var i = 0; - - if (SuperView._tabIndexes is { }) - { - foreach (View superViewTabStop in SuperView._tabIndexes) - { - if (superViewTabStop._tabIndex is null || superViewTabStop == this) - { - continue; - } - - i++; - } - } - - return Math.Min (i, idx); + return fitleredSubviews?.ToArray () ?? Array.Empty (); } private TabBehavior? _tabStop; @@ -828,17 +703,6 @@ public partial class View // Focus and cross-view navigation management (TabStop return; } - Debug.Assert (value is { }); - - if (_tabStop is null && TabIndex is null) - { - // This view has not yet been added to TabIndexes (TabStop has not been set previously). - if (SuperView?._tabIndexes is { }) - { - TabIndex = GetGreatestTabIndexInSuperView (SuperView is { } ? SuperView._tabIndexes.Count : 0); - } - } - _tabStop = value; } } diff --git a/Terminal.Gui/Views/FileDialog.cs b/Terminal.Gui/Views/FileDialog.cs index 655359135..a45243a89 100644 --- a/Terminal.Gui/Views/FileDialog.cs +++ b/Terminal.Gui/Views/FileDialog.cs @@ -463,12 +463,12 @@ public class FileDialog : Dialog _btnCancel.X = Pos.Func (CalculateOkButtonPosX); _btnOk.X = Pos.Right (_btnCancel) + 1; - // Flip tab order too for consistency - int? p1 = _btnOk.TabIndex; - int? p2 = _btnCancel.TabIndex; + //// Flip tab order too for consistency + //int? p1 = _btnOk.TabIndex; + //int? p2 = _btnCancel.TabIndex; - _btnOk.TabIndex = p2; - _btnCancel.TabIndex = p1; + //_btnOk.TabIndex = p2; + //_btnCancel.TabIndex = p1; } _tbPath.Caption = Style.PathCaption; diff --git a/UICatalog/Scenarios/Dialogs.cs b/UICatalog/Scenarios/Dialogs.cs index ded47a5fa..5872cd093 100644 --- a/UICatalog/Scenarios/Dialogs.cs +++ b/UICatalog/Scenarios/Dialogs.cs @@ -311,10 +311,10 @@ public class Dialogs : Scenario buttons.Add (button); dialog.AddButton (button); - if (buttons.Count > 1) - { - button.TabIndex = buttons [buttons.Count - 2].TabIndex + 1; - } + //if (buttons.Count > 1) + //{ + // button.TabIndex = buttons [buttons.Count - 2].TabIndex + 1; + //} }; dialog.Add (add); diff --git a/UICatalog/Scenarios/DynamicMenuBar.cs b/UICatalog/Scenarios/DynamicMenuBar.cs index 25152ecd8..a65536cb1 100644 --- a/UICatalog/Scenarios/DynamicMenuBar.cs +++ b/UICatalog/Scenarios/DynamicMenuBar.cs @@ -640,10 +640,10 @@ public class DynamicMenuBar : Scenario }; frmMenu.Add (_lstMenus); - lblMenuBar.TabIndex = btnPrevious.TabIndex + 1; - _lstMenus.TabIndex = lblMenuBar.TabIndex + 1; - btnNext.TabIndex = _lstMenus.TabIndex + 1; - btnAdd.TabIndex = btnNext.TabIndex + 1; + //lblMenuBar.TabIndex = btnPrevious.TabIndex + 1; + //_lstMenus.TabIndex = lblMenuBar.TabIndex + 1; + //btnNext.TabIndex = _lstMenus.TabIndex + 1; + //btnAdd.TabIndex = btnNext.TabIndex + 1; var btnRemove = new Button { X = Pos.Left (btnAdd), Y = Pos.Top (btnAdd) + 1, Text = "Remove" }; frmMenu.Add (btnRemove); diff --git a/UnitTests/Application/Application.NavigationTests.cs b/UnitTests/Application/Application.NavigationTests.cs index 879eda872..d5d62383a 100644 --- a/UnitTests/Application/Application.NavigationTests.cs +++ b/UnitTests/Application/Application.NavigationTests.cs @@ -13,16 +13,16 @@ public class ApplicationNavigationTests (ITestOutputHelper output) { bool raised = false; - Application.Navigation = new ApplicationNavigation(); + Application.Navigation = new ApplicationNavigation (); Application.Navigation.FocusedChanged += ApplicationNavigationOnFocusedChanged; - Application.Navigation.SetFocused(new View ()); + Application.Navigation.SetFocused (new View ()); Assert.True (raised); - Application.Navigation.GetFocused().Dispose (); - Application.Navigation.SetFocused(null); + Application.Navigation.GetFocused ().Dispose (); + Application.Navigation.SetFocused (null); Application.Navigation.FocusedChanged -= ApplicationNavigationOnFocusedChanged; @@ -30,10 +30,7 @@ public class ApplicationNavigationTests (ITestOutputHelper output) return; - void ApplicationNavigationOnFocusedChanged (object sender, EventArgs e) - { - raised = true; - } + void ApplicationNavigationOnFocusedChanged (object sender, EventArgs e) { raised = true; } } [Fact] @@ -50,7 +47,8 @@ public class ApplicationNavigationTests (ITestOutputHelper output) public void GetDeepestFocusedSubview_ShouldReturnSameView_WhenNoSubviewsHaveFocus () { // Arrange - var view = new View () { Id = "view", CanFocus = true }; ; + var view = new View () { Id = "view", CanFocus = true }; + ; // Act var result = ApplicationNavigation.GetDeepestFocusedSubview (view); @@ -63,10 +61,14 @@ public class ApplicationNavigationTests (ITestOutputHelper output) public void GetDeepestFocusedSubview_ShouldReturnFocusedSubview () { // Arrange - var parentView = new View () { Id = "parentView", CanFocus = true }; ; - var childView1 = new View () { Id = "childView1", CanFocus = true }; ; - var childView2 = new View () { Id = "childView2", CanFocus = true }; ; - var grandChildView = new View () { Id = "grandChildView", CanFocus = true }; ; + var parentView = new View () { Id = "parentView", CanFocus = true }; + ; + var childView1 = new View () { Id = "childView1", CanFocus = true }; + ; + var childView2 = new View () { Id = "childView2", CanFocus = true }; + ; + var grandChildView = new View () { Id = "grandChildView", CanFocus = true }; + ; parentView.Add (childView1, childView2); childView2.Add (grandChildView); @@ -84,11 +86,16 @@ public class ApplicationNavigationTests (ITestOutputHelper output) public void GetDeepestFocusedSubview_ShouldReturnDeepestFocusedSubview () { // Arrange - var parentView = new View () { Id = "parentView", CanFocus = true }; ; - var childView1 = new View () { Id = "childView1", CanFocus = true }; ; - var childView2 = new View () { Id = "childView2", CanFocus = true }; ; - var grandChildView = new View () { Id = "grandChildView", CanFocus = true }; ; - var greatGrandChildView = new View () { Id = "greatGrandChildView", CanFocus = true }; ; + var parentView = new View () { Id = "parentView", CanFocus = true }; + ; + var childView1 = new View () { Id = "childView1", CanFocus = true }; + ; + var childView2 = new View () { Id = "childView2", CanFocus = true }; + ; + var grandChildView = new View () { Id = "grandChildView", CanFocus = true }; + ; + var greatGrandChildView = new View () { Id = "greatGrandChildView", CanFocus = true }; + ; parentView.Add (childView1, childView2); childView2.Add (grandChildView); @@ -113,48 +120,75 @@ public class ApplicationNavigationTests (ITestOutputHelper output) Assert.Equal (grandChildView, result); } + [Fact] + public void GetFocused_Returns_Null_If_No_Focused_View () + { + Application.Navigation = new (); - //[Fact] - //public void MoveNextViewOrTop_ShouldMoveFocusToNextViewOrTop () - //{ - // // Arrange - // var top = new Toplevel (); - // var view1 = new View () { Id = "view1", CanFocus = true }; - // var view2 = new View () { Id = "view2", CanFocus = true }; - // top.Add (view1, view2); - // Application.Top = top; - // Application.Current = top; - // view1.SetFocus (); + Application.Current = new Toplevel() + { + Id = "top", + CanFocus = true + }; - // // Act - // ApplicationNavigation.MoveNextViewOrTop (); + View subView1 = new View () + { + Id = "subView1", + CanFocus = true + }; - // // Assert - // Assert.True (view2.HasFocus); + Application.Current.Add (subView1); + Assert.False (Application.Current.HasFocus); - // top.Dispose (); - //} + Application.Current.SetFocus (); + Assert.True (subView1.HasFocus); + Assert.Equal (subView1, Application.Navigation.GetFocused ()); + + subView1.HasFocus = false; + Assert.False (subView1.HasFocus); + Assert.True (Application.Current.HasFocus); + Assert.Equal (Application.Current, Application.Navigation.GetFocused ()); + + Application.Current.HasFocus = false; + Assert.False (Application.Current.HasFocus); + Assert.Null (Application.Navigation.GetFocused ()); + + Application.ResetState (); + } + [Fact] + public void GetFocused_Returns_Focused_View () + { + Application.Navigation = new (); - //[Fact] - //public void MovePreviousViewOrTop_ShouldMoveFocusToPreviousViewOrTop () - //{ - // // Arrange - // var top = new Toplevel (); - // var view1 = new View () { Id = "view1", CanFocus = true, TabStop = TabBehavior.TabGroup }; - // var view2 = new View () { Id = "view2", CanFocus = true, TabStop = TabBehavior.TabGroup }; - // top.Add (view1, view2); - // Application.Top = top; - // Application.Current = top; - // view2.SetFocus (); + Application.Current = new Toplevel () + { + Id = "top", + CanFocus = true + }; - // // Act - // ApplicationNavigation.MovePreviousViewOrTop (); + View subView1 = new View () + { + Id = "subView1", + CanFocus = true + }; - // // Assert - // Assert.True (view1.HasFocus); + View subView2 = new View () + { + Id = "subView2", + CanFocus = true + }; + Application.Current.Add (subView1, subView2); + Assert.False (Application.Current.HasFocus); - // top.Dispose (); - //} + Application.Current.SetFocus (); + Assert.True (subView1.HasFocus); + Assert.Equal(subView1, Application.Navigation.GetFocused()); + + Application.Current.AdvanceFocus (NavigationDirection.Forward, null); + Assert.Equal (subView2, Application.Navigation.GetFocused ()); + + Application.ResetState (); + } } diff --git a/UnitTests/View/Navigation/AddRemoveTests.cs b/UnitTests/View/Navigation/AddRemoveTests.cs index e2182179f..cb1cd66fa 100644 --- a/UnitTests/View/Navigation/AddRemoveTests.cs +++ b/UnitTests/View/Navigation/AddRemoveTests.cs @@ -98,6 +98,83 @@ public class AddRemoveNavigationTests (ITestOutputHelper _output) : TestsAllView Assert.True (subSubView.HasFocus); } + + [Fact] + public void Remove_Subview_Raises_HasFocusChanged () + { + var top = new View + { + Id = "top", + CanFocus = true + }; + + var subView1 = new View + { + Id = "subView1", + CanFocus = true + }; + + var subView2 = new View + { + Id = "subView2", + CanFocus = true + }; + top.Add (subView1, subView2); + + var subView1HasFocusChangedTrueCount = 0; + var subView1HasFocusChangedFalseCount = 0; + + subView1.HasFocusChanged += (s, e) => + { + if (e.NewValue) + { + subView1HasFocusChangedTrueCount++; + } + else + { + subView1HasFocusChangedFalseCount++; + } + }; + + var subView2HasFocusChangedTrueCount = 0; + var subView2HasFocusChangedFalseCount = 0; + + subView2.HasFocusChanged += (s, e) => + { + if (e.NewValue) + { + subView2HasFocusChangedTrueCount++; + } + else + { + subView2HasFocusChangedFalseCount++; + } + }; + + top.SetFocus (); + Assert.True (top.HasFocus); + Assert.True (subView1.HasFocus); + Assert.False (subView2.HasFocus); + + Assert.Equal (1, subView1HasFocusChangedTrueCount); + Assert.Equal (0, subView1HasFocusChangedFalseCount); + + Assert.Equal (0, subView2HasFocusChangedTrueCount); + Assert.Equal (0, subView2HasFocusChangedFalseCount); + + top.Remove (subView1); // this should have the same resuilt as top.AdvanceFocus (NavigationDirection.Forward, null); + + Assert.False (subView1.HasFocus); + Assert.True (subView2.HasFocus); + + Assert.Equal (1, subView1HasFocusChangedTrueCount); + Assert.Equal (1, subView1HasFocusChangedFalseCount); + + Assert.Equal (1, subView2HasFocusChangedTrueCount); + Assert.Equal (0, subView2HasFocusChangedFalseCount); + } + + [Fact] public void Remove_Focused_Subview_Keeps_Focus_And_SubView_Looses_Focus () { @@ -155,6 +232,7 @@ public class AddRemoveNavigationTests (ITestOutputHelper _output) : TestsAllView Assert.False (subView2.HasFocus); top.Remove (subView1); + Assert.True (top.HasFocus); Assert.True (subView2.HasFocus); Assert.Equal (subView2, top.Focused); diff --git a/UnitTests/View/Navigation/AdvanceFocusTests.cs b/UnitTests/View/Navigation/AdvanceFocusTests.cs index ffdea4c10..53f657d89 100644 --- a/UnitTests/View/Navigation/AdvanceFocusTests.cs +++ b/UnitTests/View/Navigation/AdvanceFocusTests.cs @@ -4,296 +4,6 @@ namespace Terminal.Gui.ViewTests; public class AdvanceFocusTests (ITestOutputHelper _output) { - [Fact] - public void Subviews_TabIndexes_AreEqual () - { - var r = new View (); - var v1 = new View { CanFocus = true }; - var v2 = new View { CanFocus = true }; - var v3 = new View { CanFocus = true }; - - r.Add (v1, v2, v3); - - Assert.True (r.Subviews.IndexOf (v1) == 0); - Assert.True (r.Subviews.IndexOf (v2) == 1); - Assert.True (r.Subviews.IndexOf (v3) == 2); - - Assert.True (r.TabIndexes.IndexOf (v1) == 0); - Assert.True (r.TabIndexes.IndexOf (v2) == 1); - Assert.True (r.TabIndexes.IndexOf (v3) == 2); - - Assert.Equal (r.Subviews.IndexOf (v1), r.TabIndexes.IndexOf (v1)); - Assert.Equal (r.Subviews.IndexOf (v2), r.TabIndexes.IndexOf (v2)); - Assert.Equal (r.Subviews.IndexOf (v3), r.TabIndexes.IndexOf (v3)); - r.Dispose (); - } - - [Fact] - public void TabIndex_Invert_Order () - { - var r = new View (); - var v1 = new View { Id = "1", CanFocus = true }; - var v2 = new View { Id = "2", CanFocus = true }; - var v3 = new View { Id = "3", CanFocus = true }; - - r.Add (v1, v2, v3); - - v1.TabIndex = 2; - v2.TabIndex = 1; - v3.TabIndex = 0; - Assert.True (r.TabIndexes.IndexOf (v1) == 2); - Assert.True (r.TabIndexes.IndexOf (v2) == 1); - Assert.True (r.TabIndexes.IndexOf (v3) == 0); - - Assert.True (r.Subviews.IndexOf (v1) == 0); - Assert.True (r.Subviews.IndexOf (v2) == 1); - Assert.True (r.Subviews.IndexOf (v3) == 2); - } - - [Fact] - public void TabIndex_Invert_Order_Added_One_By_One_Does_Not_Do_What_Is_Expected () - { - var r = new View (); - var v1 = new View { Id = "1", CanFocus = true }; - r.Add (v1); - v1.TabIndex = 2; - var v2 = new View { Id = "2", CanFocus = true }; - r.Add (v2); - v2.TabIndex = 1; - var v3 = new View { Id = "3", CanFocus = true }; - r.Add (v3); - v3.TabIndex = 0; - - Assert.False (r.TabIndexes.IndexOf (v1) == 2); - Assert.True (r.TabIndexes.IndexOf (v1) == 1); - Assert.False (r.TabIndexes.IndexOf (v2) == 1); - Assert.True (r.TabIndexes.IndexOf (v2) == 2); - - // Only the last is in the expected index - Assert.True (r.TabIndexes.IndexOf (v3) == 0); - - Assert.True (r.Subviews.IndexOf (v1) == 0); - Assert.True (r.Subviews.IndexOf (v2) == 1); - Assert.True (r.Subviews.IndexOf (v3) == 2); - } - - [Fact] - public void TabIndex_Invert_Order_Mixed () - { - var r = new View (); - var vl1 = new View { Id = "vl1" }; - var v1 = new View { Id = "v1", CanFocus = true }; - var vl2 = new View { Id = "vl2" }; - var v2 = new View { Id = "v2", CanFocus = true }; - var vl3 = new View { Id = "vl3" }; - var v3 = new View { Id = "v3", CanFocus = true }; - - r.Add (vl1, v1, vl2, v2, vl3, v3); - - v1.TabIndex = 2; - v2.TabIndex = 1; - v3.TabIndex = 0; - Assert.True (r.TabIndexes.IndexOf (v1) == 4); - Assert.True (r.TabIndexes.IndexOf (v2) == 2); - Assert.True (r.TabIndexes.IndexOf (v3) == 0); - - Assert.True (r.Subviews.IndexOf (v1) == 1); - Assert.True (r.Subviews.IndexOf (v2) == 3); - Assert.True (r.Subviews.IndexOf (v3) == 5); - } - - [Fact] - public void TabIndex_Set_CanFocus_False () - { - var r = new View (); - var v1 = new View { CanFocus = true }; - var v2 = new View { CanFocus = true }; - var v3 = new View { CanFocus = true }; - - r.Add (v1, v2, v3); - - v1.CanFocus = false; - v1.TabIndex = 0; - Assert.True (r.Subviews.IndexOf (v1) == 0); - Assert.True (r.TabIndexes.IndexOf (v1) == 0); - Assert.NotEqual (-1, v1.TabIndex); - r.Dispose (); - } - - [Fact] - public void TabIndex_Set_CanFocus_False_To_True () - { - var r = new View (); - var v1 = new View (); - var v2 = new View { CanFocus = true }; - var v3 = new View { CanFocus = true }; - - r.Add (v1, v2, v3); - - v1.CanFocus = true; - v1.TabIndex = 1; - Assert.True (r.Subviews.IndexOf (v1) == 0); - Assert.True (r.TabIndexes.IndexOf (v1) == 1); - r.Dispose (); - } - - [Fact] - public void TabIndex_Set_CanFocus_HigherValues () - { - var r = new View (); - var v1 = new View { CanFocus = true }; - var v2 = new View { CanFocus = true }; - var v3 = new View { CanFocus = true }; - - r.Add (v1, v2, v3); - - v1.TabIndex = 3; - Assert.True (r.Subviews.IndexOf (v1) == 0); - Assert.True (r.TabIndexes.IndexOf (v1) == 2); - r.Dispose (); - } - - [Fact] - public void TabIndex_Set_CanFocus_LowerValues () - { - var r = new View (); - var v1 = new View { CanFocus = true }; - var v2 = new View { CanFocus = true }; - var v3 = new View { CanFocus = true }; - - r.Add (v1, v2, v3); - - //v1.TabIndex = -1; - Assert.True (r.Subviews.IndexOf (v1) == 0); - Assert.True (r.TabIndexes.IndexOf (v1) == 0); - r.Dispose (); - } - - [Fact] - public void TabIndex_Set_CanFocus_ValidValues () - { - var r = new View (); - var v1 = new View { CanFocus = true }; - var v2 = new View { CanFocus = true }; - var v3 = new View { CanFocus = true }; - - r.Add (v1, v2, v3); - - v1.TabIndex = 1; - Assert.True (r.Subviews.IndexOf (v1) == 0); - Assert.True (r.TabIndexes.IndexOf (v1) == 1); - - v1.TabIndex = 2; - Assert.True (r.Subviews.IndexOf (v1) == 0); - Assert.True (r.TabIndexes.IndexOf (v1) == 2); - r.Dispose (); - } - - - [Theory] - [CombinatorialData] - public void TabStop_And_CanFocus_Are_Decoupled (bool canFocus, TabBehavior tabStop) - { - var view = new View { CanFocus = canFocus, TabStop = tabStop }; - - Assert.Equal (canFocus, view.CanFocus); - Assert.Equal (tabStop, view.TabStop); - } - - - [Fact] - public void AdvanceFocus_Compound_Subview () - { - var top = new View () { Id = "top", CanFocus = true }; - - var compoundSubview = new View () - { - CanFocus = true, - Id = "compoundSubview", - }; - var v1 = new View { Id = "v1", CanFocus = true }; - var v2 = new View { Id = "v2", CanFocus = true }; - var v3 = new View { Id = "v3", CanFocus = false }; - - compoundSubview.Add (v1, v2, v3); - - top.Add (compoundSubview); - - // Cycle through v1 & v2 - top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); - Assert.True (v1.HasFocus); - Assert.False (v2.HasFocus); - Assert.False (v3.HasFocus); - top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); - Assert.False (v1.HasFocus); - Assert.True (v2.HasFocus); - Assert.False (v3.HasFocus); - top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); - Assert.True (v1.HasFocus); - Assert.False (v2.HasFocus); - Assert.False (v3.HasFocus); - - // Add another subview - View otherSubview = new () - { - CanFocus = true, - Id = "otherSubview", - }; - - top.Add (otherSubview); - // Adding a focusable subview causes advancefocus - Assert.True (otherSubview.HasFocus); - Assert.False (v1.HasFocus); - - // Cycle through v1 & v2 - top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); - Assert.True (v1.HasFocus); - Assert.False (v2.HasFocus); - Assert.False (v3.HasFocus); - top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); - Assert.False (v1.HasFocus); - Assert.True (v2.HasFocus); - Assert.False (v3.HasFocus); - top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); - Assert.False (v1.HasFocus); - Assert.False (v2.HasFocus); - Assert.False (v3.HasFocus); - - Assert.True (otherSubview.HasFocus); - // v2 was previously focused down the compoundSubView focus chain - top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); - Assert.False (v1.HasFocus); - Assert.True (v2.HasFocus); - Assert.False (v3.HasFocus); - - top.Dispose (); - } - - [Fact] - public void AdvanceFocus_With_CanFocus_Are_All_True () - { - var top = new View () { Id = "top", CanFocus = true }; - var v1 = new View { Id = "v1", CanFocus = true }; - var v2 = new View { Id = "v2", CanFocus = true }; - var v3 = new View { Id = "v3", CanFocus = true }; - - top.Add (v1, v2, v3); - - top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); - Assert.True (v1.HasFocus); - Assert.False (v2.HasFocus); - Assert.False (v3.HasFocus); - top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); - Assert.False (v1.HasFocus); - Assert.True (v2.HasFocus); - Assert.False (v3.HasFocus); - top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); - Assert.False (v1.HasFocus); - Assert.False (v2.HasFocus); - Assert.True (v3.HasFocus); - top.Dispose (); - } - [Fact] public void AdvanceFocus_CanFocus_Mixed () { @@ -323,7 +33,7 @@ public class AdvanceFocusTests (ITestOutputHelper _output) [CombinatorialData] public void AdvanceFocus_Change_CanFocus_Works ([CombinatorialValues (TabBehavior.NoStop, TabBehavior.TabStop, TabBehavior.TabGroup)] TabBehavior behavior) { - var r = new View () { CanFocus = true }; + var r = new View { CanFocus = true }; var v1 = new View (); var v2 = new View (); var v3 = new View (); @@ -362,6 +72,76 @@ public class AdvanceFocusTests (ITestOutputHelper _output) r.Dispose (); } + [Fact] + public void AdvanceFocus_Compound_Subview () + { + var top = new View { Id = "top", CanFocus = true }; + + var compoundSubview = new View + { + CanFocus = true, + Id = "compoundSubview" + }; + var v1 = new View { Id = "v1", CanFocus = true }; + var v2 = new View { Id = "v2", CanFocus = true }; + var v3 = new View { Id = "v3", CanFocus = false }; + + compoundSubview.Add (v1, v2, v3); + + top.Add (compoundSubview); + + // Cycle through v1 & v2 + top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); + Assert.True (v1.HasFocus); + Assert.False (v2.HasFocus); + Assert.False (v3.HasFocus); + top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); + Assert.False (v1.HasFocus); + Assert.True (v2.HasFocus); + Assert.False (v3.HasFocus); + top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); + Assert.True (v1.HasFocus); + Assert.False (v2.HasFocus); + Assert.False (v3.HasFocus); + + // Add another subview + View otherSubview = new () + { + CanFocus = true, + Id = "otherSubview" + }; + + top.Add (otherSubview); + + // Adding a focusable subview causes advancefocus + Assert.True (otherSubview.HasFocus); + Assert.False (v1.HasFocus); + + // Cycle through v1 & v2 + top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); + Assert.True (v1.HasFocus); + Assert.False (v2.HasFocus); + Assert.False (v3.HasFocus); + top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); + Assert.False (v1.HasFocus); + Assert.True (v2.HasFocus); + Assert.False (v3.HasFocus); + top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); + Assert.False (v1.HasFocus); + Assert.False (v2.HasFocus); + Assert.False (v3.HasFocus); + + Assert.True (otherSubview.HasFocus); + + // v2 was previously focused down the compoundSubView focus chain + top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); + Assert.False (v1.HasFocus); + Assert.True (v2.HasFocus); + Assert.False (v3.HasFocus); + + top.Dispose (); + } + [Fact] public void AdvanceFocus_NoStop_And_CanFocus_True_No_Focus () { @@ -459,4 +239,207 @@ public class AdvanceFocusTests (ITestOutputHelper _output) Assert.False (v3.HasFocus); r.Dispose (); } + + [Fact] + public void AdvanceFocus_Subviews_Raises_HasFocusChanged () + { + var top = new View + { + Id = "top", + CanFocus = true + }; + + var subView1 = new View + { + Id = "subView1", + CanFocus = true + }; + + var subView2 = new View + { + Id = "subView2", + CanFocus = true + }; + top.Add (subView1, subView2); + + var subView1HasFocusChangedTrueCount = 0; + var subView1HasFocusChangedFalseCount = 0; + + subView1.HasFocusChanged += (s, e) => + { + if (e.NewValue) + { + subView1HasFocusChangedTrueCount++; + } + else + { + subView1HasFocusChangedFalseCount++; + } + }; + + var subView2HasFocusChangedTrueCount = 0; + var subView2HasFocusChangedFalseCount = 0; + + subView2.HasFocusChanged += (s, e) => + { + if (e.NewValue) + { + subView2HasFocusChangedTrueCount++; + } + else + { + subView2HasFocusChangedFalseCount++; + } + }; + + top.SetFocus (); + Assert.True (top.HasFocus); + Assert.True (subView1.HasFocus); + Assert.False (subView2.HasFocus); + + Assert.Equal (1, subView1HasFocusChangedTrueCount); + Assert.Equal (0, subView1HasFocusChangedFalseCount); + + Assert.Equal (0, subView2HasFocusChangedTrueCount); + Assert.Equal (0, subView2HasFocusChangedFalseCount); + + top.AdvanceFocus (NavigationDirection.Forward, null); + Assert.False (subView1.HasFocus); + Assert.True (subView2.HasFocus); + + Assert.Equal (1, subView1HasFocusChangedTrueCount); + Assert.Equal (1, subView1HasFocusChangedFalseCount); + + Assert.Equal (1, subView2HasFocusChangedTrueCount); + Assert.Equal (0, subView2HasFocusChangedFalseCount); + + top.AdvanceFocus (NavigationDirection.Forward, null); + Assert.True (subView1.HasFocus); + Assert.False (subView2.HasFocus); + + Assert.Equal (2, subView1HasFocusChangedTrueCount); + Assert.Equal (1, subView1HasFocusChangedFalseCount); + + Assert.Equal (1, subView2HasFocusChangedTrueCount); + Assert.Equal (1, subView2HasFocusChangedFalseCount); + } + + [Fact] + public void AdvanceFocus_Subviews_Raises_HasFocusChanging () + { + var top = new View + { + Id = "top", + CanFocus = true + }; + + var subView1 = new View + { + Id = "subView1", + CanFocus = true + }; + + var subView2 = new View + { + Id = "subView2", + CanFocus = true + }; + top.Add (subView1, subView2); + + var subView1HasFocusChangingTrueCount = 0; + var subView1HasFocusChangingFalseCount = 0; + + subView1.HasFocusChanging += (s, e) => + { + if (e.NewValue) + { + subView1HasFocusChangingTrueCount++; + } + else + { + subView1HasFocusChangingFalseCount++; + } + }; + + var subView2HasFocusChangingTrueCount = 0; + var subView2HasFocusChangingFalseCount = 0; + + subView2.HasFocusChanging += (s, e) => + { + if (e.NewValue) + { + subView2HasFocusChangingTrueCount++; + } + else + { + subView2HasFocusChangingFalseCount++; + } + }; + + top.SetFocus (); + Assert.True (top.HasFocus); + Assert.True (subView1.HasFocus); + Assert.False (subView2.HasFocus); + + Assert.Equal (1, subView1HasFocusChangingTrueCount); + Assert.Equal (0, subView1HasFocusChangingFalseCount); + + Assert.Equal (0, subView2HasFocusChangingTrueCount); + Assert.Equal (0, subView2HasFocusChangingFalseCount); + + top.AdvanceFocus (NavigationDirection.Forward, null); + Assert.False (subView1.HasFocus); + Assert.True (subView2.HasFocus); + + Assert.Equal (1, subView1HasFocusChangingTrueCount); + Assert.Equal (1, subView1HasFocusChangingFalseCount); + + Assert.Equal (1, subView2HasFocusChangingTrueCount); + Assert.Equal (0, subView2HasFocusChangingFalseCount); + + top.AdvanceFocus (NavigationDirection.Forward, null); + Assert.True (subView1.HasFocus); + Assert.False (subView2.HasFocus); + + Assert.Equal (2, subView1HasFocusChangingTrueCount); + Assert.Equal (1, subView1HasFocusChangingFalseCount); + + Assert.Equal (1, subView2HasFocusChangingTrueCount); + Assert.Equal (1, subView2HasFocusChangingFalseCount); + } + + [Fact] + public void AdvanceFocus_With_CanFocus_Are_All_True () + { + var top = new View { Id = "top", CanFocus = true }; + var v1 = new View { Id = "v1", CanFocus = true }; + var v2 = new View { Id = "v2", CanFocus = true }; + var v3 = new View { Id = "v3", CanFocus = true }; + + top.Add (v1, v2, v3); + + top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); + Assert.True (v1.HasFocus); + Assert.False (v2.HasFocus); + Assert.False (v3.HasFocus); + top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); + Assert.False (v1.HasFocus); + Assert.True (v2.HasFocus); + Assert.False (v3.HasFocus); + top.AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop); + Assert.False (v1.HasFocus); + Assert.False (v2.HasFocus); + Assert.True (v3.HasFocus); + top.Dispose (); + } + + [Theory] + [CombinatorialData] + public void TabStop_And_CanFocus_Are_Decoupled (bool canFocus, TabBehavior tabStop) + { + var view = new View { CanFocus = canFocus, TabStop = tabStop }; + + Assert.Equal (canFocus, view.CanFocus); + Assert.Equal (tabStop, view.TabStop); + } } diff --git a/UnitTests/View/Navigation/CanFocusTests.cs b/UnitTests/View/Navigation/CanFocusTests.cs index f275e710e..5d3bbc1e8 100644 --- a/UnitTests/View/Navigation/CanFocusTests.cs +++ b/UnitTests/View/Navigation/CanFocusTests.cs @@ -244,48 +244,48 @@ public class CanFocusTests (ITestOutputHelper _output) : TestsAllViews Application.Shutdown (); } - [Fact] - public void CanFocus_Set_Changes_TabIndex_And_TabStop () - { - var r = new View (); - var v1 = new View { Text = "1" }; - var v2 = new View { Text = "2" }; - var v3 = new View { Text = "3" }; + //[Fact] + //public void CanFocus_Set_Changes_TabIndex_And_TabStop () + //{ + // var r = new View (); + // var v1 = new View { Text = "1" }; + // var v2 = new View { Text = "2" }; + // var v3 = new View { Text = "3" }; - r.Add (v1, v2, v3); + // r.Add (v1, v2, v3); - v2.CanFocus = true; - Assert.Equal (r.TabIndexes.IndexOf (v2), v2.TabIndex); - Assert.Equal (0, v2.TabIndex); - Assert.Equal (TabBehavior.TabStop, v2.TabStop); + // v2.CanFocus = true; + // Assert.Equal (r.TabIndexes.IndexOf (v2), v2.TabIndex); + // Assert.Equal (0, v2.TabIndex); + // Assert.Equal (TabBehavior.TabStop, v2.TabStop); - v1.CanFocus = true; - Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex); - Assert.Equal (1, v1.TabIndex); - Assert.Equal (TabBehavior.TabStop, v1.TabStop); + // v1.CanFocus = true; + // Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex); + // Assert.Equal (1, v1.TabIndex); + // Assert.Equal (TabBehavior.TabStop, v1.TabStop); - v1.TabIndex = 2; - Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex); - Assert.Equal (1, v1.TabIndex); - v3.CanFocus = true; - Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex); - Assert.Equal (1, v1.TabIndex); - Assert.Equal (r.TabIndexes.IndexOf (v3), v3.TabIndex); - Assert.Equal (2, v3.TabIndex); - Assert.Equal (TabBehavior.TabStop, v3.TabStop); + // v1.TabIndex = 2; + // Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex); + // Assert.Equal (1, v1.TabIndex); + // v3.CanFocus = true; + // Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex); + // Assert.Equal (1, v1.TabIndex); + // Assert.Equal (r.TabIndexes.IndexOf (v3), v3.TabIndex); + // Assert.Equal (2, v3.TabIndex); + // Assert.Equal (TabBehavior.TabStop, v3.TabStop); - v2.CanFocus = false; - Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex); - Assert.Equal (1, v1.TabIndex); - Assert.Equal (TabBehavior.TabStop, v1.TabStop); - Assert.Equal (r.TabIndexes.IndexOf (v2), v2.TabIndex); // TabIndex is not changed - Assert.NotEqual (-1, v2.TabIndex); - Assert.Equal (TabBehavior.TabStop, v2.TabStop); // TabStop is not changed - Assert.Equal (r.TabIndexes.IndexOf (v3), v3.TabIndex); - Assert.Equal (2, v3.TabIndex); - Assert.Equal (TabBehavior.TabStop, v3.TabStop); - r.Dispose (); - } + // v2.CanFocus = false; + // Assert.Equal (r.TabIndexes.IndexOf (v1), v1.TabIndex); + // Assert.Equal (1, v1.TabIndex); + // Assert.Equal (TabBehavior.TabStop, v1.TabStop); + // Assert.Equal (r.TabIndexes.IndexOf (v2), v2.TabIndex); // TabIndex is not changed + // Assert.NotEqual (-1, v2.TabIndex); + // Assert.Equal (TabBehavior.TabStop, v2.TabStop); // TabStop is not changed + // Assert.Equal (r.TabIndexes.IndexOf (v3), v3.TabIndex); + // Assert.Equal (2, v3.TabIndex); + // Assert.Equal (TabBehavior.TabStop, v3.TabStop); + // r.Dispose (); + //} [Fact] public void CanFocus_True_Focuses () diff --git a/UnitTests/View/Navigation/HasFocusChangeEventTests.cs b/UnitTests/View/Navigation/HasFocusChangeEventTests.cs index 662021c55..86d11ddd1 100644 --- a/UnitTests/View/Navigation/HasFocusChangeEventTests.cs +++ b/UnitTests/View/Navigation/HasFocusChangeEventTests.cs @@ -5,6 +5,7 @@ namespace Terminal.Gui.ViewTests; public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllViews { #region HasFocusChanging_NewValue_True + [Fact] public void HasFocusChanging_SetFocus_Raises () { @@ -16,6 +17,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "view", CanFocus = true }; + view.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -49,6 +51,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "view", CanFocus = true }; + view.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -69,17 +72,18 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subview", CanFocus = true }; + subview.HasFocusChanging += (s, e) => - { - if (e.NewValue) - { - subviewHasFocusTrueCount++; - } - else - { - subviewHasFocusFalseCount++; - } - }; + { + if (e.NewValue) + { + subviewHasFocusTrueCount++; + } + else + { + subviewHasFocusFalseCount++; + } + }; view.Add (subview); @@ -104,6 +108,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "view", CanFocus = true }; + view.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -124,6 +129,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subview", CanFocus = true }; + subview.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -158,6 +164,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "view", CanFocus = true }; + view.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -178,6 +185,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subView", CanFocus = true }; + subView.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -198,6 +206,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subViewSubView1", CanFocus = false }; + subViewSubView1.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -218,6 +227,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subViewSubView2", CanFocus = true }; + subViewSubView2.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -238,6 +248,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subViewSubView3", CanFocus = false }; + subViewSubView3.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -288,6 +299,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "view", CanFocus = true }; + view.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -309,6 +321,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subview", CanFocus = true }; + subview.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -346,6 +359,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "view", CanFocus = true }; + view.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -367,6 +381,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subview", CanFocus = true }; + subview.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -404,6 +419,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "view", CanFocus = true }; + view.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -424,6 +440,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subview", CanFocus = true }; + subview.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -463,6 +480,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "view", CanFocus = true }; + view.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -483,6 +501,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subview", CanFocus = true }; + subview.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -537,6 +556,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "view", CanFocus = true }; + view.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -575,6 +595,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "view", CanFocus = true }; + view.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -595,6 +616,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subview", CanFocus = true }; + subview.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -618,7 +640,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Assert.Equal (0, subviewHasFocusFalseCount); view.HasFocus = false; - Assert.False(view.HasFocus); + Assert.False (view.HasFocus); Assert.False (subview.HasFocus); Assert.Equal (1, hasFocusTrueCount); Assert.Equal (1, hasFocusFalseCount); @@ -640,6 +662,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "view", CanFocus = true }; + view.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -660,6 +683,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subview", CanFocus = true }; + subview.HasFocusChanging += (s, e) => { if (e.NewValue) @@ -691,6 +715,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Assert.Equal (1, subviewHasFocusFalseCount); } + #endregion HasFocusChanging_NewValue_False #region HasFocusChanged @@ -706,6 +731,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "view", CanFocus = true }; + view.HasFocusChanged += (s, e) => { if (e.NewValue) @@ -743,6 +769,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "view", CanFocus = true }; + view.HasFocusChanged += (s, e) => { if (e.NewValue) @@ -763,6 +790,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subview", CanFocus = true }; + subview.HasFocusChanged += (s, e) => { if (e.NewValue) @@ -821,6 +849,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "view", CanFocus = true }; + view.HasFocusChanged += (s, e) => { if (e.NewValue) @@ -841,6 +870,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subView", CanFocus = true }; + subView.HasFocusChanged += (s, e) => { if (e.NewValue) @@ -861,6 +891,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subViewSubView1", CanFocus = false }; + subViewSubView1.HasFocusChanged += (s, e) => { if (e.NewValue) @@ -881,6 +912,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subViewSubView2", CanFocus = true }; + subViewSubView2.HasFocusChanged += (s, e) => { if (e.NewValue) @@ -900,6 +932,7 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "subViewSubView3", CanFocus = false }; + subViewSubView3.HasFocusChanged += (s, e) => { if (e.NewValue) @@ -967,32 +1000,33 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Id = "view", CanFocus = true }; + view.HasFocusChanged += (s, e) => - { - if (e.NewValue) - { - Assert.True (view.HasFocus); - Assert.True (subView1.HasFocus); - Assert.False (subView2.HasFocus); + { + if (e.NewValue) + { + Assert.True (view.HasFocus); + Assert.True (subView1.HasFocus); + Assert.False (subView2.HasFocus); - subView1.Visible = true; - subView2.Visible = false; + subView1.Visible = true; + subView2.Visible = false; - Assert.True (view.HasFocus); - Assert.True (subView1.HasFocus); - Assert.False (subView2.HasFocus); + Assert.True (view.HasFocus); + Assert.True (subView1.HasFocus); + Assert.False (subView2.HasFocus); - } - else - { - Assert.False (view.HasFocus); - Assert.False (subView1.HasFocus); - Assert.False (subView2.HasFocus); + } + else + { + Assert.False (view.HasFocus); + Assert.False (subView1.HasFocus); + Assert.False (subView2.HasFocus); - subView1.Visible = false; - subView2.Visible = true; - } - }; + subView1.Visible = false; + subView2.Visible = true; + } + }; view.Add (subView1, subView2); @@ -1006,5 +1040,6 @@ public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllView Assert.False (subView1.HasFocus); Assert.False (subView2.HasFocus); } + #endregion HasFocusChanged } diff --git a/UnitTests/View/Navigation/HasFocusTests.cs b/UnitTests/View/Navigation/HasFocusTests.cs index fb6b1beef..b078f5501 100644 --- a/UnitTests/View/Navigation/HasFocusTests.cs +++ b/UnitTests/View/Navigation/HasFocusTests.cs @@ -155,4 +155,79 @@ public class HasFocusTests (ITestOutputHelper _output) : TestsAllViews Assert.False (subViewSubView2.HasFocus); Assert.False (subViewSubView3.HasFocus); } + + [Fact] + public void HasFocus_False_Subview_Raises_HasFocusChanged () + { + var top = new View + { + Id = "top", + CanFocus = true + }; + + var subView1 = new View + { + Id = "subView1", + CanFocus = true + }; + + var subView2 = new View + { + Id = "subView2", + CanFocus = true + }; + top.Add (subView1, subView2); + + var subView1HasFocusChangedTrueCount = 0; + var subView1HasFocusChangedFalseCount = 0; + + subView1.HasFocusChanged += (s, e) => + { + if (e.NewValue) + { + subView1HasFocusChangedTrueCount++; + } + else + { + subView1HasFocusChangedFalseCount++; + } + }; + + var subView2HasFocusChangedTrueCount = 0; + var subView2HasFocusChangedFalseCount = 0; + + subView2.HasFocusChanged += (s, e) => + { + if (e.NewValue) + { + subView2HasFocusChangedTrueCount++; + } + else + { + subView2HasFocusChangedFalseCount++; + } + }; + + top.SetFocus (); + Assert.True (top.HasFocus); + Assert.True (subView1.HasFocus); + Assert.False (subView2.HasFocus); + + Assert.Equal (1, subView1HasFocusChangedTrueCount); + Assert.Equal (0, subView1HasFocusChangedFalseCount); + + Assert.Equal (0, subView2HasFocusChangedTrueCount); + Assert.Equal (0, subView2HasFocusChangedFalseCount); + + subView1.HasFocus = false; // this should have the same resuilt as top.AdvanceFocus (NavigationDirection.Forward, null); + + Assert.False (subView1.HasFocus); + Assert.True (subView2.HasFocus); + + Assert.Equal (1, subView1HasFocusChangedTrueCount); + Assert.Equal (1, subView1HasFocusChangedFalseCount); + + Assert.Equal (1, subView2HasFocusChangedTrueCount); + Assert.Equal (0, subView2HasFocusChangedFalseCount); + } } diff --git a/UnitTests/View/Navigation/NavigationTests.cs b/UnitTests/View/Navigation/NavigationTests.cs index 5ddec1bef..27d343f30 100644 --- a/UnitTests/View/Navigation/NavigationTests.cs +++ b/UnitTests/View/Navigation/NavigationTests.cs @@ -313,48 +313,6 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews } - [Fact] - public void BringSubviewForward_Subviews_vs_TabIndexes () - { - var r = new View (); - var v1 = new View { CanFocus = true }; - var v2 = new View { CanFocus = true }; - var v3 = new View { CanFocus = true }; - - r.Add (v1, v2, v3); - - r.BringSubviewForward (v1); - Assert.True (r.Subviews.IndexOf (v1) == 1); - Assert.True (r.Subviews.IndexOf (v2) == 0); - Assert.True (r.Subviews.IndexOf (v3) == 2); - - Assert.True (r.TabIndexes.IndexOf (v1) == 0); - Assert.True (r.TabIndexes.IndexOf (v2) == 1); - Assert.True (r.TabIndexes.IndexOf (v3) == 2); - r.Dispose (); - } - - [Fact] - public void BringSubviewToFront_Subviews_vs_TabIndexes () - { - var r = new View (); - var v1 = new View { CanFocus = true }; - var v2 = new View { CanFocus = true }; - var v3 = new View { CanFocus = true }; - - r.Add (v1, v2, v3); - - r.BringSubviewToFront (v1); - Assert.True (r.Subviews.IndexOf (v1) == 2); - Assert.True (r.Subviews.IndexOf (v2) == 0); - Assert.True (r.Subviews.IndexOf (v3) == 1); - - Assert.True (r.TabIndexes.IndexOf (v1) == 0); - Assert.True (r.TabIndexes.IndexOf (v2) == 1); - Assert.True (r.TabIndexes.IndexOf (v3) == 2); - r.Dispose (); - } - // View.Focused & View.MostFocused tests // View.Focused - No subviews diff --git a/UnitTests/View/ViewTests.cs b/UnitTests/View/ViewTests.cs index a2b4af816..e4f0616ed 100644 --- a/UnitTests/View/ViewTests.cs +++ b/UnitTests/View/ViewTests.cs @@ -1064,7 +1064,6 @@ At 0,0 Assert.True (win.Visible); Assert.True (win.CanFocus); Assert.True (win.HasFocus); - Assert.True (RunesCount () > 0); win.Visible = false; Assert.True (button.Visible); @@ -1073,21 +1072,18 @@ At 0,0 Assert.False (win.Visible); Assert.True (win.CanFocus); Assert.False (win.HasFocus); + button.SetFocus (); Assert.False (button.HasFocus); Assert.False (win.HasFocus); + win.SetFocus (); Assert.False (button.HasFocus); Assert.False (win.HasFocus); - top.Draw (); - Assert.True (RunesCount () == 0); win.Visible = true; - win.FocusDeepest (NavigationDirection.Forward, null); Assert.True (button.HasFocus); Assert.True (win.HasFocus); - top.Draw (); - Assert.True (RunesCount () > 0); Application.RequestStop (); }; @@ -1095,25 +1091,6 @@ At 0,0 Application.Run (top); top.Dispose (); Assert.Equal (1, iterations); - - int RunesCount () - { - Cell [,] contents = ((FakeDriver)Application.Driver).Contents; - var runesCount = 0; - - for (var i = 0; i < Application.Driver!.Rows; i++) - { - for (var j = 0; j < Application.Driver!.Cols; j++) - { - if (contents [i, j].Rune != (Rune)' ') - { - runesCount++; - } - } - } - - return runesCount; - } } public class DerivedView : View diff --git a/UnitTests/Views/ComboBoxTests.cs b/UnitTests/Views/ComboBoxTests.cs index 293a739d1..366c9d42a 100644 --- a/UnitTests/Views/ComboBoxTests.cs +++ b/UnitTests/Views/ComboBoxTests.cs @@ -506,7 +506,7 @@ public class ComboBoxTests (ITestOutputHelper output) top.Add (otherView, cb); Application.Begin (top); - Assert.False (cb.HasFocus); + Assert.True (cb.HasFocus); Assert.True (cb.HideDropdownListOnClick); Assert.False (cb.IsShow); @@ -857,7 +857,7 @@ Three ", Assert.True (Application.OnKeyDown (Key.CursorDown)); // losing focus Assert.False (cb.IsShow); Assert.False (cb.HasFocus); - top.FocusDeepest (NavigationDirection.Forward, null); // Gets focus again + cb.SetFocus (); Assert.False (cb.IsShow); Assert.True (cb.HasFocus); cb.Expand (); @@ -969,7 +969,8 @@ Three Assert.False (cb.IsShow); Assert.Equal (-1, cb.SelectedItem); Assert.Equal ("One", cb.Text); - top.FocusDeepest (NavigationDirection.Forward, null); // Gets focus again + + cb.SetFocus (); Assert.True (cb.HasFocus); Assert.False (cb.IsShow); Assert.Equal (-1, cb.SelectedItem); diff --git a/UnitTests/Views/OverlappedTests.cs b/UnitTests/Views/OverlappedTests.cs index 0ce8c16b2..467718656 100644 --- a/UnitTests/Views/OverlappedTests.cs +++ b/UnitTests/Views/OverlappedTests.cs @@ -1232,7 +1232,7 @@ public class OverlappedTests Assert.Equal (superView.MostFocused, current); // Act - ApplicationOverlapped.SetFocusToNextViewWithWrap (Application.Current.SuperView.TabIndexes, NavigationDirection.Forward); + ApplicationOverlapped.SetFocusToNextViewWithWrap (Application.Current.SuperView.Subviews, NavigationDirection.Forward); // Assert Assert.True (view1.HasFocus);