diff --git a/Terminal.Gui/Views/TabView.cs b/Terminal.Gui/Views/TabView.cs index 4f6461e93..02ea6d8b0 100644 --- a/Terminal.Gui/Views/TabView.cs +++ b/Terminal.Gui/Views/TabView.cs @@ -117,6 +117,7 @@ public class TabView : View { if (_selectedTab.View is { }) { + _selectedTab.View.CanFocusChanged -= ContentViewCanFocus; // remove old content _contentView.Remove (_selectedTab.View); } @@ -129,12 +130,13 @@ public class TabView : View // add new content if (_selectedTab.View is { }) { + _selectedTab.View.CanFocusChanged += ContentViewCanFocus; _contentView.Add (_selectedTab.View); // _contentView.Id = $"_contentView for {_selectedTab.DisplayText}"; } } - _contentView.CanFocus = _contentView.Subviews.Count (v => v.CanFocus) > 0; + ContentViewCanFocus (null, null); EnsureSelectedTabIsVisible (); @@ -151,6 +153,11 @@ public class TabView : View } } + private void ContentViewCanFocus (object sender, EventArgs eventArgs) + { + _contentView.CanFocus = _contentView.Subviews.Count (v => v.CanFocus) > 0; + } + private TabStyle _style = new (); /// Render choices for how to display tabs. After making changes, call . @@ -289,6 +296,19 @@ public class TabView : View /// The valid for the given value. public int EnsureValidScrollOffsets (int value) { return Math.Max (Math.Min (value, Tabs.Count - 1), 0); } + /// + protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View focusedVew) + { + if (SelectedTab is { } && !_contentView.CanFocus && focusedVew == this) + { + SelectedTab?.SetFocus (); + + return; + } + + base.OnHasFocusChanged (newHasFocus, previousFocusedView, focusedVew); + } + /// protected override bool OnDrawingContent (Rectangle viewport) { @@ -652,14 +672,20 @@ public class TabView : View { _host._tabLocations = _host.CalculateViewport (Viewport).ToArray (); - RenderTabLine (); - RenderUnderline (); Driver?.SetAttribute (HasFocus ? GetFocusColor () : GetNormalColor ()); return true; } + /// + protected override bool OnDrawingSubviews (Rectangle viewport) + { + RenderTabLine (); + + return true; + } + protected override void OnDrawComplete () { if (_host._tabLocations is null) @@ -1190,10 +1216,10 @@ public class TabView : View } tab.LineCanvas.Merge (lc); - tab.DrawAdornments (); - } + tab.RenderLineCanvas (); - return; + RenderUnderline (); + } } private int GetUnderlineYPosition () diff --git a/UnitTests/Views/TabViewTests.cs b/UnitTests/Views/TabViewTests.cs index c470020e6..dc66cc298 100644 --- a/UnitTests/Views/TabViewTests.cs +++ b/UnitTests/Views/TabViewTests.cs @@ -103,7 +103,7 @@ public class TabViewTests (ITestOutputHelper output) Application.Shutdown (); } - [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")] + [Fact] [AutoInitShutdown] public void MouseClick_ChangesTab () { @@ -188,7 +188,7 @@ public class TabViewTests (ITestOutputHelper output) top.Dispose (); } - [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")] + [Fact] [AutoInitShutdown] public void MouseClick_Right_Left_Arrows_ChangesTab () { @@ -274,7 +274,7 @@ public class TabViewTests (ITestOutputHelper output) top.Dispose (); } - [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")] + [Fact] [AutoInitShutdown] public void MouseClick_Right_Left_Arrows_ChangesTab_With_Border () { @@ -369,7 +369,7 @@ public class TabViewTests (ITestOutputHelper output) top.Dispose (); } - [Fact (Skip="#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")] + [Fact] [AutoInitShutdown] public void ProcessKey_Down_Up_Right_Left_Home_End_PageDown_PageUp () { @@ -429,7 +429,7 @@ public class TabViewTests (ITestOutputHelper output) Assert.Equal (tab2, tv.SelectedTab); Assert.Equal (btn, top.MostFocused); - // Add a focusable subview to Selected Tab + // Add a focusable subview to Selected Tab View which is a Label with CanFocus as false var btnSubView = new View () { Id = "btnSubView", @@ -438,9 +438,17 @@ public class TabViewTests (ITestOutputHelper output) }; tv.SelectedTab.View.Add (btnSubView); + Assert.False (tv.SelectedTab.View.CanFocus); + // Press cursor up. Should focus the subview in the selected tab. Application.RaiseKeyDownEvent (Key.CursorUp); Assert.Equal (tab2, tv.SelectedTab); + Assert.NotEqual (btnSubView, top.MostFocused); + Assert.Equal (tab2, top.MostFocused); + + tv.SelectedTab.View.CanFocus = true; + Application.RaiseKeyDownEvent (Key.CursorDown); + Assert.Equal (tab2, tv.SelectedTab); Assert.Equal (btnSubView, top.MostFocused); Application.RaiseKeyDownEvent (Key.CursorUp); @@ -451,24 +459,18 @@ public class TabViewTests (ITestOutputHelper output) Application.RaiseKeyDownEvent (Key.CursorDown); Assert.Equal (btn, top.MostFocused); - // Press the cursor down key again will focus next view in the toplevel, whic is the TabView + // Press the cursor down key again will focus next view in the toplevel, which is the TabView Application.RaiseKeyDownEvent (Key.CursorDown); Assert.Equal (tab2, tv.SelectedTab); Assert.Equal (tv, top.Focused); - Assert.Equal (tab1, tv.MostFocused); + // Due to the RestoreFocus method prioritize the _previouslyFocused, so btnSubView will be focused again + Assert.Equal (btnSubView, tv.MostFocused); - // Press the cursor down key to focus the selected tab view hosting again - Application.RaiseKeyDownEvent (Key.CursorDown); - Assert.Equal (tab2, tv.SelectedTab); - Assert.Equal (btnSubView, top.MostFocused); - - // Press the cursor up key to focus the selected tab + // Press the cursor up key to focus the selected tab which it's the only way to do that Application.RaiseKeyDownEvent (Key.CursorUp); - Application.Refresh (); - - // Is the selected tab focused Assert.Equal (tab2, tv.SelectedTab); Assert.Equal (tv, top.Focused); + Assert.Equal (tab2, top.Focused.MostFocused); Assert.Equal (tv.MostFocused, top.Focused.MostFocused); // Press the cursor left key to select the previous tab @@ -479,6 +481,7 @@ public class TabViewTests (ITestOutputHelper output) Assert.Equal (tab1, tv.SelectedTab); Assert.Equal (tv, top.Focused); Assert.Equal (tv.MostFocused, top.Focused.MostFocused); + Assert.Equal (tab1, top.Focused.MostFocused); // Press the end key to select the last tab Application.RaiseKeyDownEvent (Key.End); @@ -595,7 +598,7 @@ public class TabViewTests (ITestOutputHelper output) Application.Shutdown (); } - [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")] + [Fact] [SetupFakeDriver] public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width3 () { @@ -643,7 +646,7 @@ public class TabViewTests (ITestOutputHelper output) ); } - [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")] + [Fact] [SetupFakeDriver] public void ShowTopLine_False_TabsOnBottom_False_TestThinTabView_WithLongNames () { @@ -735,7 +738,7 @@ public class TabViewTests (ITestOutputHelper output) ); } - [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")] + [Fact] [SetupFakeDriver] public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width3 () { @@ -783,7 +786,7 @@ public class TabViewTests (ITestOutputHelper output) ); } - [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")] + [Fact] [SetupFakeDriver] public void ShowTopLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames () { @@ -875,7 +878,7 @@ public class TabViewTests (ITestOutputHelper output) ); } - [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")] + [Fact] [SetupFakeDriver] public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width3 () { @@ -919,7 +922,7 @@ public class TabViewTests (ITestOutputHelper output) ); } - [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")] + [Fact] [SetupFakeDriver] public void ShowTopLine_True_TabsOnBottom_False_TestThinTabView_WithLongNames () { @@ -1009,7 +1012,7 @@ public class TabViewTests (ITestOutputHelper output) ); } - [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")] + [Fact] [SetupFakeDriver] public void ShowTopLine_True_TabsOnBottom_False_With_Unicode () { @@ -1050,7 +1053,7 @@ public class TabViewTests (ITestOutputHelper output) ); } - [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")] + [Fact] [SetupFakeDriver] public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width3 () { @@ -1098,7 +1101,7 @@ public class TabViewTests (ITestOutputHelper output) ); } - [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")] + [Fact] [SetupFakeDriver] public void ShowTopLine_True_TabsOnBottom_True_TestThinTabView_WithLongNames () { @@ -1174,7 +1177,7 @@ public class TabViewTests (ITestOutputHelper output) ); } - [Fact (Skip = "#3789 Broke. The right way to fix is to refactor TabView to separate Layout and Draw")] + [Fact] [SetupFakeDriver] public void ShowTopLine_True_TabsOnBottom_True_With_Unicode () {