diff --git a/Terminal.Gui/Views/TabView.cs b/Terminal.Gui/Views/TabView.cs index 2ea48c006..4905912d9 100644 --- a/Terminal.Gui/Views/TabView.cs +++ b/Terminal.Gui/Views/TabView.cs @@ -98,7 +98,7 @@ namespace Terminal.Gui { /// - /// Initialzies a class using layout. + /// Initializes a class using layout. /// public TabView () : base () { @@ -182,7 +182,7 @@ namespace Terminal.Gui { if (Style.ShowBorder) { - // How muc space do we need to leave at the bottom to show the tabs + // How much space do we need to leave at the bottom to show the tabs int spaceAtBottom = Math.Max (0, GetTabHeight (false) - 1); int startAtY = Math.Max (0, GetTabHeight (true) - 1); @@ -347,8 +347,10 @@ namespace Terminal.Gui { var maxWidth = Math.Max (0, Math.Min (bounds.Width - 3, MaxTabTextWidth)); // if tab view is width <= 3 don't render any tabs - if (maxWidth == 0) - yield break; + if (maxWidth == 0) { + yield return new TabToRender (i, tab, string.Empty, Equals (SelectedTab, tab), 0); + break; + } if (tabTextWidth > maxWidth) { text = tab.Text.ToString ().Substring (0, (int)maxWidth); @@ -412,7 +414,7 @@ namespace Terminal.Gui { // if the currently selected tab is no longer a member of Tabs if (SelectedTab == null || !Tabs.Contains (SelectedTab)) { - // select the tab closest to the one that disapeared + // select the tab closest to the one that disappeared var toSelect = Math.Max (idx - 1, 0); if (toSelect < Tabs.Count) { @@ -657,7 +659,7 @@ namespace Terminal.Gui { Driver.AddRune (Driver.LeftArrow); } - // if there are mmore tabs to the right not visible + // if there are more tabs to the right not visible if (ShouldDrawRightScrollIndicator (tabLocations)) { Move (width - 1, y); diff --git a/UnitTests/TabViewTests.cs b/UnitTests/TabViewTests.cs index d69459d05..b1da15291 100644 --- a/UnitTests/TabViewTests.cs +++ b/UnitTests/TabViewTests.cs @@ -242,7 +242,7 @@ namespace Terminal.Gui.Views { } [Fact, AutoInitShutdown] - public void TestThinTabView_WithLongNames () + public void ShowTopLine_True_TabsOnBottom_False_TestThinTabView_WithLongNames () { var tv = GetTabView (out var tab1, out var tab2, false); tv.Width = 10; @@ -257,23 +257,34 @@ namespace Terminal.Gui.Views { tv.Redraw (tv.Bounds); - GraphViewTests.AssertDriverContentsAre (@" -┌──┐ -│12│13 + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌──┐ +│12│13 │ └─────┐ │hi │ └────────┘", output); + tv.SelectedTab = tab2; + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" + ┌──┐ + 12│13│ +┌──┘ └──┐ +│hi2 │ +└────────┘", output); + + tv.SelectedTab = tab1; // Test first tab name too long tab1.Text = "12345678910"; tab2.Text = "13"; tv.Redraw (tv.Bounds); - GraphViewTests.AssertDriverContentsAre (@" -┌───────┐ -│1234567│ + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌───────┐ +│1234567│ │ └► │hi │ └────────┘", output); @@ -282,9 +293,9 @@ namespace Terminal.Gui.Views { tv.SelectedTab = tab2; tv.Redraw (tv.Bounds); - GraphViewTests.AssertDriverContentsAre (@" -┌──┐ -│13│ + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌──┐ +│13│ ◄ └─────┐ │hi2 │ └────────┘", output); @@ -296,16 +307,94 @@ namespace Terminal.Gui.Views { tv.Redraw (tv.Bounds); - GraphViewTests.AssertDriverContentsAre (@" -┌───────┐ -│abcdefg│ + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌───────┐ +│abcdefg│ ◄ └┐ │hi2 │ └────────┘", output); } [Fact, AutoInitShutdown] - public void TestTabView_Width4 () + public void ShowTopLine_False_TabsOnBottom_False_TestThinTabView_WithLongNames () + { + var tv = GetTabView (out var tab1, out var tab2, false); + tv.Width = 10; + tv.Height = 5; + tv.Style = new TabView.TabStyle { ShowTopLine = false }; + tv.ApplyStyleChanges (); + + // Ensures that the tab bar subview gets the bounds of the parent TabView + tv.LayoutSubviews (); + + // Test two tab names that fit + tab1.Text = "12"; + tab2.Text = "13"; + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +│12│13 +│ └─────┐ +│hi │ +│ │ +└────────┘", output); + + + tv.SelectedTab = tab2; + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" + 12│13│ +┌──┘ └──┐ +│hi2 │ +│ │ +└────────┘", output); + + tv.SelectedTab = tab1; + + // Test first tab name too long + tab1.Text = "12345678910"; + tab2.Text = "13"; + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +│1234567│ +│ └► +│hi │ +│ │ +└────────┘", output); + + //switch to tab2 + tv.SelectedTab = tab2; + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +│13│ +◄ └─────┐ +│hi2 │ +│ │ +└────────┘", output); + + + // now make both tabs too long + tab1.Text = "12345678910"; + tab2.Text = "abcdefghijklmnopq"; + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +│abcdefg│ +◄ └┐ +│hi2 │ +│ │ +└────────┘", output); + } + + [Fact, AutoInitShutdown] + public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width4 () { var tv = GetTabView (out _, out _, false); tv.Width = 4; @@ -314,16 +403,36 @@ namespace Terminal.Gui.Views { tv.Redraw (tv.Bounds); - GraphViewTests.AssertDriverContentsAre (@" -┌─┐ -│T│ + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌─┐ +│T│ │ └► │hi│ └──┘", output); } [Fact, AutoInitShutdown] - public void TestTabView_Width3 () + public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width4 () + { + var tv = GetTabView (out _, out _, false); + tv.Width = 4; + tv.Height = 5; + tv.Style = new TabView.TabStyle { ShowTopLine = false }; + tv.ApplyStyleChanges (); + tv.LayoutSubviews (); + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +│T│ +│ └► +│hi│ +│ │ +└──┘", output); + } + + [Fact, AutoInitShutdown] + public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width3 () { var tv = GetTabView (out _, out _, false); tv.Width = 3; @@ -332,12 +441,325 @@ namespace Terminal.Gui.Views { tv.Redraw (tv.Bounds); - GraphViewTests.AssertDriverContentsAre (@" -┌─┐ + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌┐ +││ +│└► │h│ └─┘", output); } + [Fact, AutoInitShutdown] + public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width3 () + { + var tv = GetTabView (out _, out _, false); + tv.Width = 3; + tv.Height = 5; + tv.Style = new TabView.TabStyle { ShowTopLine = false }; + tv.ApplyStyleChanges (); + tv.LayoutSubviews (); + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +││ +│└► +│h│ +│ │ +└─┘", output); + } + + [Fact, AutoInitShutdown] + public void ShowTopLine_True_TabsOnBottom_True_TestThinTabView_WithLongNames () + { + var tv = GetTabView (out var tab1, out var tab2, false); + tv.Width = 10; + tv.Height = 5; + tv.Style = new TabView.TabStyle { TabsOnBottom = true }; + tv.ApplyStyleChanges (); + + // Ensures that the tab bar subview gets the bounds of the parent TabView + tv.LayoutSubviews (); + + // Test two tab names that fit + tab1.Text = "12"; + tab2.Text = "13"; + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌────────┐ +│hi │ +│ ┌─────┘ +│12│13 +└──┘ ", output); + + + // Test first tab name too long + tab1.Text = "12345678910"; + tab2.Text = "13"; + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌────────┐ +│hi │ +│ ┌► +│1234567│ +└───────┘ ", output); + + //switch to tab2 + tv.SelectedTab = tab2; + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌────────┐ +│hi2 │ +◄ ┌─────┘ +│13│ +└──┘ ", output); + + + // now make both tabs too long + tab1.Text = "12345678910"; + tab2.Text = "abcdefghijklmnopq"; + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌────────┐ +│hi2 │ +◄ ┌┘ +│abcdefg│ +└───────┘ ", output); + } + + [Fact, AutoInitShutdown] + public void ShowTopLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames () + { + var tv = GetTabView (out var tab1, out var tab2, false); + tv.Width = 10; + tv.Height = 5; + tv.Style = new TabView.TabStyle { ShowTopLine = false, TabsOnBottom = true }; + tv.ApplyStyleChanges (); + + // Ensures that the tab bar subview gets the bounds of the parent TabView + tv.LayoutSubviews (); + + // Test two tab names that fit + tab1.Text = "12"; + tab2.Text = "13"; + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌────────┐ +│hi │ +│ │ +│ ┌─────┘ +│12│13 ", output); + + + tv.SelectedTab = tab2; + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌────────┐ +│hi2 │ +│ │ +└──┐ ┌──┘ + 12│13│ ", output); + + tv.SelectedTab = tab1; + + // Test first tab name too long + tab1.Text = "12345678910"; + tab2.Text = "13"; + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌────────┐ +│hi │ +│ │ +│ ┌► +│1234567│ ", output); + + //switch to tab2 + tv.SelectedTab = tab2; + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌────────┐ +│hi2 │ +│ │ +◄ ┌─────┘ +│13│ ", output); + + + // now make both tabs too long + tab1.Text = "12345678910"; + tab2.Text = "abcdefghijklmnopq"; + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌────────┐ +│hi2 │ +│ │ +◄ ┌┘ +│abcdefg│ ", output); + } + + [Fact, AutoInitShutdown] + public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width4 () + { + var tv = GetTabView (out _, out _, false); + tv.Width = 4; + tv.Height = 5; + tv.Style = new TabView.TabStyle { TabsOnBottom = true }; + tv.ApplyStyleChanges (); + tv.LayoutSubviews (); + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌──┐ +│hi│ +│ ┌► +│T│ +└─┘ ", output); + } + + [Fact, AutoInitShutdown] + public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width4 () + { + var tv = GetTabView (out _, out _, false); + tv.Width = 4; + tv.Height = 5; + tv.Style = new TabView.TabStyle { ShowTopLine = false, TabsOnBottom = true }; + tv.ApplyStyleChanges (); + tv.LayoutSubviews (); + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌──┐ +│hi│ +│ │ +│ ┌► +│T│ ", output); + } + + [Fact, AutoInitShutdown] + public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width3 () + { + var tv = GetTabView (out _, out _, false); + tv.Width = 3; + tv.Height = 5; + tv.Style = new TabView.TabStyle { TabsOnBottom = true }; + tv.ApplyStyleChanges (); + tv.LayoutSubviews (); + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌─┐ +│h│ +│┌► +││ +└┘ ", output); + } + + [Fact, AutoInitShutdown] + public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width3 () + { + var tv = GetTabView (out _, out _, false); + tv.Width = 3; + tv.Height = 5; + tv.Style = new TabView.TabStyle { ShowTopLine = false, TabsOnBottom = true }; + tv.ApplyStyleChanges (); + tv.LayoutSubviews (); + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌─┐ +│h│ +│ │ +│┌► +││ ", output); + } + + [Fact, AutoInitShutdown] + public void ShowTopLine_True_TabsOnBottom_False_With_Unicode () + { + var tv = GetTabView (out var tab1, out var tab2, false); + tv.Width = 20; + tv.Height = 5; + + tv.LayoutSubviews (); + + tab1.Text = "Tab0"; + tab2.Text = "Les Mise" + Char.ConvertFromUtf32 (Int32.Parse ("0301", NumberStyles.HexNumber)) + "rables"; + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌────┐ +│Tab0│ +│ └─────────────► +│hi │ +└──────────────────┘", output); + + tv.SelectedTab = tab2; + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌──────────────┐ +│Les Misérables│ +◄ └───┐ +│hi2 │ +└──────────────────┘", output); + } + + [Fact, AutoInitShutdown] + public void ShowTopLine_True_TabsOnBottom_True_With_Unicode () + { + var tv = GetTabView (out var tab1, out var tab2, false); + tv.Width = 20; + tv.Height = 5; + tv.Style = new TabView.TabStyle { TabsOnBottom = true }; + tv.ApplyStyleChanges (); + + tv.LayoutSubviews (); + + tab1.Text = "Tab0"; + tab2.Text = "Les Mise" + Char.ConvertFromUtf32 (Int32.Parse ("0301", NumberStyles.HexNumber)) + "rables"; + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌──────────────────┐ +│hi │ +│ ┌─────────────► +│Tab0│ +└────┘ ", output); + + tv.SelectedTab = tab2; + + tv.Redraw (tv.Bounds); + + GraphViewTests.AssertDriverContentsWithFrameAre (@" +┌──────────────────┐ +│hi2 │ +◄ ┌───┘ +│Les Misérables│ +└──────────────┘ ", output); + } + private void InitFakeDriver () { var driver = new FakeDriver ();