From 8f36a179e22844056b4c1746e3938bb1c8fce464 Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 25 Nov 2024 21:41:09 +0000 Subject: [PATCH 01/18] Add TabSide enum. --- .../UICatalog/Scenarios/TabViewExample.cs | 57 +- Terminal.Gui/Views/TabView/TabRow.cs | 744 ++++++++++-------- Terminal.Gui/Views/TabView/TabSide.cs | 27 + Terminal.Gui/Views/TabView/TabStyle.cs | 6 +- Terminal.Gui/Views/TabView/TabView.cs | 75 +- Tests/UnitTests/Views/TabViewTests.cs | 16 +- 6 files changed, 562 insertions(+), 363 deletions(-) create mode 100644 Terminal.Gui/Views/TabView/TabSide.cs diff --git a/Examples/UICatalog/Scenarios/TabViewExample.cs b/Examples/UICatalog/Scenarios/TabViewExample.cs index 30f55d5f4a..97e0caa8a8 100644 --- a/Examples/UICatalog/Scenarios/TabViewExample.cs +++ b/Examples/UICatalog/Scenarios/TabViewExample.cs @@ -1,4 +1,6 @@ -using System.Linq; +using System; +using System.Collections.Generic; +using System.Linq; using System.Text; namespace UICatalog.Scenarios; @@ -11,7 +13,8 @@ public class TabViewExample : Scenario private MenuItem _miShowBorder; private MenuItem _miShowTabViewBorder; private MenuItem _miShowTopLine; - private MenuItem _miTabsOnBottom; + private MenuItem [] _miTabsSide; + private MenuItem _cachedTabsSide; private TabView _tabView; public override void Main () @@ -22,6 +25,8 @@ public override void Main () // Setup - Create a top-level application window and configure it. Toplevel appWindow = new (); + _miTabsSide = SetTabsSide (); + var menu = new MenuBar { Menus = @@ -53,11 +58,12 @@ public override void Main () { Checked = true, CheckType = MenuItemCheckStyle.Checked }, - _miTabsOnBottom = - new ("_Tabs On Bottom", "", SetTabsOnBottom) - { - Checked = false, CheckType = MenuItemCheckStyle.Checked - }, + null, + _miTabsSide [0], + _miTabsSide [1], + _miTabsSide [2], + _miTabsSide [3], + null, _miShowTabViewBorder = new ( "_Show TabView Border", @@ -240,12 +246,41 @@ private View GetInteractiveTab () private void Quit () { Application.RequestStop (); } - private void SetTabsOnBottom () + private MenuItem [] SetTabsSide () { - _miTabsOnBottom.Checked = !_miTabsOnBottom.Checked; + List menuItems = []; - _tabView.Style.TabsOnBottom = (bool)_miTabsOnBottom.Checked; - _tabView.ApplyStyleChanges (); + foreach (TabSide side in Enum.GetValues (typeof (TabSide))) + { + string sideName = Enum.GetName (typeof (TabSide), side); + var item = new MenuItem { Title = $"_{sideName}", Data = side }; + item.CheckType |= MenuItemCheckStyle.Radio; + + item.Action += () => + { + if (_cachedTabsSide == item) + { + return; + } + + _cachedTabsSide.Checked = false; + item.Checked = true; + _cachedTabsSide = item; + _tabView.Style.TabsSide = (TabSide)item.Data; + _tabView.ApplyStyleChanges (); + }; + item.ShortcutKey = ((Key)sideName! [0].ToString ().ToLower ()).WithCtrl; + + if (sideName == "Top") + { + item.Checked = true; + _cachedTabsSide = item; + } + + menuItems.Add (item); + } + + return menuItems.ToArray (); } private void ShowBorder () diff --git a/Terminal.Gui/Views/TabView/TabRow.cs b/Terminal.Gui/Views/TabView/TabRow.cs index f85db683a4..5f70eb9b8f 100644 --- a/Terminal.Gui/Views/TabView/TabRow.cs +++ b/Terminal.Gui/Views/TabView/TabRow.cs @@ -158,187 +158,249 @@ private void RenderTabLineCanvas () if (i == 0 && _host.TabScrollOffset == 0) { - if (_host.Style.TabsOnBottom) + switch (_host.Style.TabsSide) { - // Upper left vertical line - lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), - -1, - Orientation.Vertical, - tab.BorderStyle - ); - } - else - { - // Lower left vertical line - lc.AddLine ( - new Point (vts.X - 1, vts.Bottom - selectedOffset), - -1, - Orientation.Vertical, - tab.BorderStyle - ); - } - } - else if (i > 0 && i <= tabLocations.Length - 1) - { - if (_host.Style.TabsOnBottom) - { - // URCorner - lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), - 1, - Orientation.Vertical, - tab.BorderStyle - ); - - lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), - -1, - Orientation.Horizontal, - tab.BorderStyle - ); - } - else - { - // LRCorner - lc.AddLine ( - new Point (vts.X - 1, vts.Bottom - selectedOffset), - -1, - Orientation.Vertical, - tab.BorderStyle - ); - - lc.AddLine ( - new Point (vts.X - 1, vts.Bottom - selectedOffset), - -1, - Orientation.Horizontal, - tab.BorderStyle - ); - } - - if (_host.Style.ShowTopLine) - { - if (_host.Style.TabsOnBottom) - { - // Lower left tee + case TabSide.Top: + // Lower left vertical line lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), + new Point (vts.X - 1, vts.Bottom - selectedOffset), -1, Orientation.Vertical, tab.BorderStyle ); - lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), - 0, - Orientation.Horizontal, - tab.BorderStyle - ); - } - else - { - // Upper left tee + break; + case TabSide.Bottom: + // Upper left vertical line lc.AddLine ( new Point (vts.X - 1, vts.Y - 1), - 1, + -1, Orientation.Vertical, tab.BorderStyle ); - lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), - 0, - Orientation.Horizontal, - tab.BorderStyle - ); - } + break; + case TabSide.Left: + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); } } - - if (i < tabLocations.Length - 1) + else if (i > 0 && i <= tabLocations.Length - 1) { - if (_host.Style.ShowTopLine) + switch (_host.Style.TabsSide) { - if (_host.Style.TabsOnBottom) - { - // Lower right tee + case TabSide.Top: + // LRCorner lc.AddLine ( - new Point (vts.Right, vts.Bottom), + new Point (vts.X - 1, vts.Bottom - selectedOffset), -1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.Right, vts.Bottom), - 0, + new Point (vts.X - 1, vts.Bottom - selectedOffset), + -1, Orientation.Horizontal, tab.BorderStyle ); - } - else - { - // Upper right tee + + break; + case TabSide.Bottom: + // URCorner lc.AddLine ( - new Point (vts.Right, vts.Y - 1), + new Point (vts.X - 1, vts.Y - 1), 1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.Right, vts.Y - 1), - 0, + new Point (vts.X - 1, vts.Y - 1), + -1, Orientation.Horizontal, tab.BorderStyle ); + + break; + case TabSide.Left: + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); + } + + if (_host.Style.ShowTopLine) + { + switch (_host.Style.TabsSide) + { + case TabSide.Top: + // Upper left tee + lc.AddLine ( + new Point (vts.X - 1, vts.Y - 1), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new Point (vts.X - 1, vts.Y - 1), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Bottom: + // Lower left tee + lc.AddLine ( + new Point (vts.X - 1, vts.Bottom), + -1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new Point (vts.X - 1, vts.Bottom), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Left: + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); } } } - if (_host.Style.TabsOnBottom) + if (i < tabLocations.Length - 1) { - //URCorner - lc.AddLine ( - new Point (vts.Right, vts.Y - 1), - 1, - Orientation.Vertical, - tab.BorderStyle - ); - - lc.AddLine ( - new Point (vts.Right, vts.Y - 1), - 1, - Orientation.Horizontal, - tab.BorderStyle - ); + if (_host.Style.ShowTopLine) + { + switch (_host.Style.TabsSide) + { + case TabSide.Top: + // Upper right tee + lc.AddLine ( + new Point (vts.Right, vts.Y - 1), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new Point (vts.Right, vts.Y - 1), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Bottom: + // Lower right tee + lc.AddLine ( + new Point (vts.Right, vts.Bottom), + -1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new Point (vts.Right, vts.Bottom), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Left: + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); + } + } } - else + + switch (_host.Style.TabsSide) { - //LLCorner - lc.AddLine ( - new Point (vts.Right, vts.Bottom - selectedOffset), - -1, - Orientation.Vertical, - tab.BorderStyle - ); + case TabSide.Top: + //LLCorner + lc.AddLine ( + new Point (vts.Right, vts.Bottom - selectedOffset), + -1, + Orientation.Vertical, + tab.BorderStyle + ); - lc.AddLine ( - new Point (vts.Right, vts.Bottom - selectedOffset), - 1, - Orientation.Horizontal, - tab.BorderStyle - ); + lc.AddLine ( + new Point (vts.Right, vts.Bottom - selectedOffset), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Bottom: + //URCorner + lc.AddLine ( + new Point (vts.Right, vts.Y - 1), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new Point (vts.Right, vts.Y - 1), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Left: + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); } } else if (selectedTab == -1) { if (i == 0 && string.IsNullOrEmpty (tab.Text)) { - if (_host.Style.TabsOnBottom) + switch (_host.Style.TabsSide) { - if (_host.Style.ShowTopLine) - { + case TabSide.Top: + if (_host.Style.ShowTopLine) + { + // ULCorner + lc.AddLine ( + new Point (vts.X - 1, vts.Y - 1), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new Point (vts.X - 1, vts.Y - 1), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + } + // LLCorner lc.AddLine ( new Point (vts.X - 1, vts.Bottom), @@ -353,27 +415,27 @@ private void RenderTabLineCanvas () Orientation.Horizontal, tab.BorderStyle ); - } - // ULCorner - lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), - 1, - Orientation.Vertical, - tab.BorderStyle - ); + break; + case TabSide.Bottom: + if (_host.Style.ShowTopLine) + { + // LLCorner + lc.AddLine ( + new Point (vts.X - 1, vts.Bottom), + -1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new Point (vts.X - 1, vts.Bottom), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + } - lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), - 1, - Orientation.Horizontal, - tab.BorderStyle - ); - } - else - { - if (_host.Style.ShowTopLine) - { // ULCorner lc.AddLine ( new Point (vts.X - 1, vts.Y - 1), @@ -388,27 +450,19 @@ private void RenderTabLineCanvas () Orientation.Horizontal, tab.BorderStyle ); - } - - // LLCorner - lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), - -1, - Orientation.Vertical, - tab.BorderStyle - ); - lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), - 1, - Orientation.Horizontal, - tab.BorderStyle - ); + break; + case TabSide.Left: + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); } } else if (i > 0) { - if (_host.Style.ShowTopLine || _host.Style.TabsOnBottom) + if (_host.Style.ShowTopLine || _host.Style.TabsSide == TabSide.Bottom) { // Upper left tee lc.AddLine ( @@ -462,7 +516,7 @@ private void RenderTabLineCanvas () ); } - if (_host.Style.ShowTopLine || !_host.Style.TabsOnBottom) + if (_host.Style.ShowTopLine || _host.Style.TabsSide == TabSide.Top) { // Lower right tee lc.AddLine ( @@ -500,77 +554,95 @@ private void RenderTabLineCanvas () if (i == 0 && i != selectedTab && _host is { TabScrollOffset: 0, Style.ShowBorder: true }) { - if (_host.Style.TabsOnBottom) + switch (_host.Style.TabsSide) { - // Upper left vertical line - lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), - 0, - Orientation.Vertical, - tab.BorderStyle - ); + case TabSide.Top: + // Lower left vertical line + lc.AddLine ( + new Point (vts.X - 1, vts.Bottom), + 0, + Orientation.Vertical, + tab.BorderStyle + ); - lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), - 1, - Orientation.Horizontal, - tab.BorderStyle - ); - } - else - { - // Lower left vertical line - lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), - 0, - Orientation.Vertical, - tab.BorderStyle - ); + lc.AddLine ( + new Point (vts.X - 1, vts.Bottom), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); - lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), - 1, - Orientation.Horizontal, - tab.BorderStyle - ); + break; + case TabSide.Bottom: + // Upper left vertical line + lc.AddLine ( + new Point (vts.X - 1, vts.Y - 1), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new Point (vts.X - 1, vts.Y - 1), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Left: + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); } } if (i == tabLocations.Length - 1 && i != selectedTab) { - if (_host.Style.TabsOnBottom) + switch (_host.Style.TabsSide) { - // Upper right tee - lc.AddLine ( - new Point (vts.Right, vts.Y - 1), - 1, - Orientation.Vertical, - tab.BorderStyle - ); + case TabSide.Top: + // Lower right tee + lc.AddLine ( + new Point (vts.Right, vts.Bottom), + -1, + Orientation.Vertical, + tab.BorderStyle + ); - lc.AddLine ( - new Point (vts.Right, vts.Y - 1), - 0, - Orientation.Horizontal, - tab.BorderStyle - ); - } - else - { - // Lower right tee - lc.AddLine ( - new Point (vts.Right, vts.Bottom), - -1, - Orientation.Vertical, - tab.BorderStyle - ); + lc.AddLine ( + new Point (vts.Right, vts.Bottom), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); - lc.AddLine ( - new Point (vts.Right, vts.Bottom), - 0, - Orientation.Horizontal, - tab.BorderStyle - ); + break; + case TabSide.Bottom: + // Upper right tee + lc.AddLine ( + new Point (vts.Right, vts.Y - 1), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new Point (vts.Right, vts.Y - 1), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Left: + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); } } @@ -579,7 +651,7 @@ private void RenderTabLineCanvas () var arrowOffset = 1; int lastSelectedTab = !_host.Style.ShowTopLine && i == selectedTab ? 1 : - _host.Style.TabsOnBottom ? 1 : 0; + _host.Style.TabsSide == TabSide.Bottom ? 1 : 0; Rectangle tabsBarVts = ViewportToScreen (Viewport); int lineLength = tabsBarVts.Right - vts.Right; @@ -588,78 +660,105 @@ private void RenderTabLineCanvas () { if (lineLength - arrowOffset > 0) { - if (_host.Style.TabsOnBottom) - { - lc.AddLine ( - new Point (vts.Right, vts.Y - lastSelectedTab), - lineLength - arrowOffset, - Orientation.Horizontal, - tab.BorderStyle - ); - } - else + switch (_host.Style.TabsSide) { - lc.AddLine ( - new Point ( - vts.Right, - vts.Bottom - lastSelectedTab - ), - lineLength - arrowOffset, - Orientation.Horizontal, - tab.BorderStyle - ); + case TabSide.Top: + lc.AddLine ( + new Point ( + vts.Right, + vts.Bottom - lastSelectedTab + ), + lineLength - arrowOffset, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Bottom: + lc.AddLine ( + new Point (vts.Right, vts.Y - lastSelectedTab), + lineLength - arrowOffset, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Left: + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); } } } else { // Right corner - if (_host.Style.TabsOnBottom) + switch (_host.Style.TabsSide) { - lc.AddLine ( - new Point (vts.Right, vts.Y - lastSelectedTab), - lineLength, - Orientation.Horizontal, - tab.BorderStyle - ); - } - else - { - lc.AddLine ( - new Point (vts.Right, vts.Bottom - lastSelectedTab), - lineLength, - Orientation.Horizontal, - tab.BorderStyle - ); - } - - if (_host.Style.ShowBorder) - { - if (_host.Style.TabsOnBottom) - { - // More LRCorner + case TabSide.Top: lc.AddLine ( - new Point ( - tabsBarVts.Right - 1, - vts.Y - lastSelectedTab - ), - -1, - Orientation.Vertical, + new Point (vts.Right, vts.Bottom - lastSelectedTab), + lineLength, + Orientation.Horizontal, tab.BorderStyle ); - } - else - { - // More URCorner + + break; + case TabSide.Bottom: lc.AddLine ( - new Point ( - tabsBarVts.Right - 1, - vts.Bottom - lastSelectedTab - ), - 1, - Orientation.Vertical, + new Point (vts.Right, vts.Y - lastSelectedTab), + lineLength, + Orientation.Horizontal, tab.BorderStyle ); + + break; + case TabSide.Left: + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); + } + + if (_host.Style.ShowBorder) + { + switch (_host.Style.TabsSide) + { + case TabSide.Top: + // More URCorner + lc.AddLine ( + new Point ( + tabsBarVts.Right - 1, + vts.Bottom - lastSelectedTab + ), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + break; + case TabSide.Bottom: + // More LRCorner + lc.AddLine ( + new Point ( + tabsBarVts.Right - 1, + vts.Y - lastSelectedTab + ), + -1, + Orientation.Vertical, + tab.BorderStyle + ); + + break; + case TabSide.Left: + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); } } } @@ -671,7 +770,7 @@ private void RenderTabLineCanvas () private int GetUnderlineYPosition () { - if (_host.Style.TabsOnBottom) + if (_host.Style.TabsSide == TabSide.Bottom) { return 0; } @@ -698,41 +797,68 @@ private void RenderTabLine () { selected = tab; - if (_host.Style.TabsOnBottom) + switch (_host.Style.TabsSide) { - tab.Border!.Thickness = new (1, 0, 1, topLine); - tab.Margin!.Thickness = new (0, 1, 0, 0); - } - else - { - tab.Border!.Thickness = new (1, topLine, 1, 0); - tab.Margin!.Thickness = new (0, 0, 0, topLine); + case TabSide.Top: + tab.Border!.Thickness = new (1, topLine, 1, 0); + tab.Margin!.Thickness = new (0, 0, 0, topLine); + + break; + case TabSide.Bottom: + tab.Border!.Thickness = new (1, 0, 1, topLine); + tab.Margin!.Thickness = new (0, 1, 0, 0); + + break; + case TabSide.Left: + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); } } else if (selected is null) { - if (_host.Style.TabsOnBottom) - { - tab.Border!.Thickness = new (1, 1, 1, topLine); - tab.Margin!.Thickness = new (0, 0, 0, 0); - } - else + switch (_host.Style.TabsSide) { - tab.Border!.Thickness = new (1, topLine, 1, 1); - tab.Margin!.Thickness = new (0, 0, 0, 0); + case TabSide.Top: + tab.Border!.Thickness = new (1, topLine, 1, 1); + tab.Margin!.Thickness = new (0, 0, 0, 0); + + break; + case TabSide.Bottom: + tab.Border!.Thickness = new (1, 1, 1, topLine); + tab.Margin!.Thickness = new (0, 0, 0, 0); + + break; + case TabSide.Left: + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); } } else { - if (_host.Style.TabsOnBottom) - { - tab.Border!.Thickness = new (1, 1, 1, topLine); - tab.Margin!.Thickness = new (0, 0, 0, 0); - } - else + switch (_host.Style.TabsSide) { - tab.Border!.Thickness = new (1, topLine, 1, 1); - tab.Margin!.Thickness = new (0, 0, 0, 0); + case TabSide.Top: + tab.Border!.Thickness = new (1, topLine, 1, 1); + tab.Margin!.Thickness = new (0, 0, 0, 0); + + break; + case TabSide.Bottom: + tab.Border!.Thickness = new (1, 1, 1, topLine); + tab.Margin!.Thickness = new (0, 0, 0, 0); + + break; + case TabSide.Left: + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); } } diff --git a/Terminal.Gui/Views/TabView/TabSide.cs b/Terminal.Gui/Views/TabView/TabSide.cs new file mode 100644 index 0000000000..eef3a51c39 --- /dev/null +++ b/Terminal.Gui/Views/TabView/TabSide.cs @@ -0,0 +1,27 @@ +namespace Terminal.Gui; + +/// +/// Defines tab side. +/// +public enum TabSide +{ + /// + /// Top side. + /// + Top, + + /// + /// Bottom side. + /// + Bottom, + + /// + /// Left side. + /// + Left, + + /// + /// Right side. + /// + Right +} diff --git a/Terminal.Gui/Views/TabView/TabStyle.cs b/Terminal.Gui/Views/TabView/TabStyle.cs index 07ac128844..8ad8ccd745 100644 --- a/Terminal.Gui/Views/TabView/TabStyle.cs +++ b/Terminal.Gui/Views/TabView/TabStyle.cs @@ -9,10 +9,10 @@ public class TabStyle /// /// True to show the top lip of tabs. False to directly begin with tab text during rendering. When true header /// line occupies 3 rows, when false only 2. Defaults to true. - /// When is enabled this instead applies to the bottommost line of the control + /// When is enabled this instead applies to the bottommost line of the control /// public bool ShowTopLine { get; set; } = true; - /// True to render tabs at the bottom of the view instead of the top - public bool TabsOnBottom { get; set; } = false; + /// Gets or sets the tabs side to render. + public TabSide TabsSide { get; set; } } diff --git a/Terminal.Gui/Views/TabView/TabView.cs b/Terminal.Gui/Views/TabView/TabView.cs index 8d0533cffd..0c514c3121 100644 --- a/Terminal.Gui/Views/TabView/TabView.cs +++ b/Terminal.Gui/Views/TabView/TabView.cs @@ -321,48 +321,59 @@ public void ApplyStyleChanges () _containerView.BorderStyle = Style.ShowBorder ? LineStyle.Single : LineStyle.None; _containerView.Width = Dim.Fill (); - if (Style.TabsOnBottom) + int tabHeight; + + switch (Style.TabsSide) { - // Tabs are along the bottom so just dodge the border - if (Style.ShowBorder) - { - _containerView.Border!.Thickness = new Thickness (1, 1, 1, 0); - } + case TabSide.Top: + // Tabs are along the top + if (Style.ShowBorder) + { + _containerView.Border!.Thickness = new Thickness (1, 0, 1, 1); + } - _containerView.Y = 0; + _tabsBar.Y = 0; - int tabHeight = GetTabHeight (false); + tabHeight = GetTabHeight (true); - // Fill client area leaving space at bottom for tabs - _containerView.Height = Dim.Fill (tabHeight); + //move content down to make space for tabs + _containerView.Y = Pos.Bottom (_tabsBar); - _tabsBar.Height = tabHeight; + // Fill client area leaving space at bottom for border + _containerView.Height = Dim.Fill (); - _tabsBar.Y = Pos.Bottom (_containerView); - } - else - { - // Tabs are along the top - if (Style.ShowBorder) - { - _containerView.Border!.Thickness = new Thickness (1, 0, 1, 1); - } + // The top tab should be 2 or 3 rows high and on the top - _tabsBar.Y = 0; + _tabsBar.Height = tabHeight; - int tabHeight = GetTabHeight (true); + // Should be able to just use 0 but switching between top/bottom tabs repeatedly breaks in ValidatePosDim if just using the absolute value 0 - //move content down to make space for tabs - _containerView.Y = Pos.Bottom (_tabsBar); + break; + case TabSide.Bottom: + // Tabs are along the bottom so just dodge the border + if (Style.ShowBorder) + { + _containerView.Border!.Thickness = new Thickness (1, 1, 1, 0); + } + + _containerView.Y = 0; - // Fill client area leaving space at bottom for border - _containerView.Height = Dim.Fill (); + tabHeight = GetTabHeight (false); - // The top tab should be 2 or 3 rows high and on the top + // Fill client area leaving space at bottom for tabs + _containerView.Height = Dim.Fill (tabHeight); - _tabsBar.Height = tabHeight; + _tabsBar.Height = tabHeight; - // Should be able to just use 0 but switching between top/bottom tabs repeatedly breaks in ValidatePosDim if just using the absolute value 0 + _tabsBar.Y = Pos.Bottom (_containerView); + + break; + case TabSide.Left: + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); } SetNeedsLayout (); @@ -615,19 +626,19 @@ internal IEnumerable CalculateViewport (Rectangle bounds) /// /// Returns the number of rows occupied by rendering the tabs, this depends on - /// and can be 0 (e.g. if and you ask for ). + /// and can be 0 (e.g. if and you ask for ). /// /// True to measure the space required at the top of the control, false to measure space at the bottom. /// . /// private int GetTabHeight (bool top) { - if (top && Style.TabsOnBottom) + if (top && Style.TabsSide == TabSide.Bottom) { return 0; } - if (!top && !Style.TabsOnBottom) + if (!top && Style.TabsSide == TabSide.Top) { return 0; } diff --git a/Tests/UnitTests/Views/TabViewTests.cs b/Tests/UnitTests/Views/TabViewTests.cs index 78ff4135f5..5047bc944b 100644 --- a/Tests/UnitTests/Views/TabViewTests.cs +++ b/Tests/UnitTests/Views/TabViewTests.cs @@ -737,7 +737,7 @@ public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width3 () TabView tv = GetTabView (out _, out _); tv.Width = 3; tv.Height = 5; - tv.Style = new () { ShowTopLine = false, TabsOnBottom = true }; + tv.Style = new () { ShowTopLine = false, TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); tv.Layout (); @@ -761,7 +761,7 @@ public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width4 () TabView tv = GetTabView (out _, out _); tv.Width = 4; tv.Height = 5; - tv.Style = new () { ShowTopLine = false, TabsOnBottom = true }; + tv.Style = new () { ShowTopLine = false, TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); tv.Layout (); @@ -785,7 +785,7 @@ public void ShowTopLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames () TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 10; tv.Height = 5; - tv.Style = new () { ShowTopLine = false, TabsOnBottom = true }; + tv.Style = new () { ShowTopLine = false, TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); tv.Layout (); @@ -1070,7 +1070,7 @@ public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width3 () TabView tv = GetTabView (out _, out _); tv.Width = 3; tv.Height = 5; - tv.Style = new () { TabsOnBottom = true }; + tv.Style = new () { TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); tv.Layout (); @@ -1094,7 +1094,7 @@ public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width4 () TabView tv = GetTabView (out _, out _); tv.Width = 4; tv.Height = 5; - tv.Style = new () { TabsOnBottom = true }; + tv.Style = new () { TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); tv.Layout (); @@ -1118,7 +1118,7 @@ public void ShowTopLine_True_TabsOnBottom_True_TestThinTabView_WithLongNames () TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 10; tv.Height = 5; - tv.Style = new () { TabsOnBottom = true }; + tv.Style = new () { TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); tv.Layout (); @@ -1200,7 +1200,7 @@ public void ShowTopLine_True_TabsOnBottom_True_With_Unicode () TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 20; tv.Height = 5; - tv.Style = new () { TabsOnBottom = true }; + tv.Style = new () { TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); tab1.DisplayText = "Tab0"; @@ -1382,7 +1382,7 @@ public void Add_Three_TabsOnBottom_ChangesTab () tv.Width = 20; tv.Height = 5; - tv.Style = new () { TabsOnBottom = true }; + tv.Style = new () { TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); tv.Layout (); From debd6e0cb593a8121e26615b94a1595bd2f09e66 Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 25 Nov 2024 22:18:28 +0000 Subject: [PATCH 02/18] Rename to ShowInitialLine. --- .../UICatalog/Scenarios/TabViewExample.cs | 2 +- Terminal.Gui/Views/TabView/TabRow.cs | 22 +++++++++---------- Terminal.Gui/Views/TabView/TabStyle.cs | 12 ++++++---- Terminal.Gui/Views/TabView/TabView.cs | 8 +++---- Tests/UnitTests/Views/TabViewTests.cs | 12 +++++----- 5 files changed, 30 insertions(+), 26 deletions(-) diff --git a/Examples/UICatalog/Scenarios/TabViewExample.cs b/Examples/UICatalog/Scenarios/TabViewExample.cs index 97e0caa8a8..81b4cd31d7 100644 --- a/Examples/UICatalog/Scenarios/TabViewExample.cs +++ b/Examples/UICatalog/Scenarios/TabViewExample.cs @@ -305,7 +305,7 @@ private void ShowTopLine () { _miShowTopLine.Checked = !_miShowTopLine.Checked; - _tabView.Style.ShowTopLine = (bool)_miShowTopLine.Checked; + _tabView.Style.ShowInitialLine = (bool)_miShowTopLine.Checked; _tabView.ApplyStyleChanges (); } } diff --git a/Terminal.Gui/Views/TabView/TabRow.cs b/Terminal.Gui/Views/TabView/TabRow.cs index 5f70eb9b8f..c0ab3d7566 100644 --- a/Terminal.Gui/Views/TabView/TabRow.cs +++ b/Terminal.Gui/Views/TabView/TabRow.cs @@ -150,7 +150,7 @@ private void RenderTabLineCanvas () { View tab = tabLocations [i]; Rectangle vts = tab.ViewportToScreen (tab.Viewport); - int selectedOffset = _host.Style.ShowTopLine && tabLocations [i] == _host.SelectedTab ? 0 : 1; + int selectedOffset = _host.Style.ShowInitialLine && tabLocations [i] == _host.SelectedTab ? 0 : 1; if (tabLocations [i] == _host.SelectedTab) { @@ -234,7 +234,7 @@ private void RenderTabLineCanvas () throw new ArgumentOutOfRangeException (); } - if (_host.Style.ShowTopLine) + if (_host.Style.ShowInitialLine) { switch (_host.Style.TabsSide) { @@ -284,7 +284,7 @@ private void RenderTabLineCanvas () if (i < tabLocations.Length - 1) { - if (_host.Style.ShowTopLine) + if (_host.Style.ShowInitialLine) { switch (_host.Style.TabsSide) { @@ -383,7 +383,7 @@ private void RenderTabLineCanvas () switch (_host.Style.TabsSide) { case TabSide.Top: - if (_host.Style.ShowTopLine) + if (_host.Style.ShowInitialLine) { // ULCorner lc.AddLine ( @@ -418,7 +418,7 @@ private void RenderTabLineCanvas () break; case TabSide.Bottom: - if (_host.Style.ShowTopLine) + if (_host.Style.ShowInitialLine) { // LLCorner lc.AddLine ( @@ -462,7 +462,7 @@ private void RenderTabLineCanvas () } else if (i > 0) { - if (_host.Style.ShowTopLine || _host.Style.TabsSide == TabSide.Bottom) + if (_host.Style.ShowInitialLine || _host.Style.TabsSide == TabSide.Bottom) { // Upper left tee lc.AddLine ( @@ -498,7 +498,7 @@ private void RenderTabLineCanvas () } else if (i < tabLocations.Length - 1) { - if (_host.Style.ShowTopLine) + if (_host.Style.ShowInitialLine) { // Upper right tee lc.AddLine ( @@ -516,7 +516,7 @@ private void RenderTabLineCanvas () ); } - if (_host.Style.ShowTopLine || _host.Style.TabsSide == TabSide.Top) + if (_host.Style.ShowInitialLine || _host.Style.TabsSide == TabSide.Top) { // Lower right tee lc.AddLine ( @@ -650,7 +650,7 @@ private void RenderTabLineCanvas () { var arrowOffset = 1; - int lastSelectedTab = !_host.Style.ShowTopLine && i == selectedTab ? 1 : + int lastSelectedTab = !_host.Style.ShowInitialLine && i == selectedTab ? 1 : _host.Style.TabsSide == TabSide.Bottom ? 1 : 0; Rectangle tabsBarVts = ViewportToScreen (Viewport); int lineLength = tabsBarVts.Right - vts.Right; @@ -775,7 +775,7 @@ private int GetUnderlineYPosition () return 0; } - return _host.Style.ShowTopLine ? 2 : 1; + return _host.Style.ShowInitialLine ? 2 : 1; } /// Renders the line with the tab names in it. @@ -787,7 +787,7 @@ private void RenderTabLine () } View? selected = null; - int topLine = _host.Style.ShowTopLine ? 1 : 0; + int topLine = _host.Style.ShowInitialLine ? 1 : 0; foreach (Tab toRender in _host._tabLocations) { diff --git a/Terminal.Gui/Views/TabView/TabStyle.cs b/Terminal.Gui/Views/TabView/TabStyle.cs index 8ad8ccd745..aeff1b3a82 100644 --- a/Terminal.Gui/Views/TabView/TabStyle.cs +++ b/Terminal.Gui/Views/TabView/TabStyle.cs @@ -7,11 +7,15 @@ public class TabStyle public bool ShowBorder { get; set; } = true; /// - /// True to show the top lip of tabs. False to directly begin with tab text during rendering. When true header - /// line occupies 3 rows, when false only 2. Defaults to true. - /// When is enabled this instead applies to the bottommost line of the control + /// True to show the top lip of tabs. False to directly begin with tab text during rendering. Defaults to true. + /// When true and or , header + /// line occupies 3 rows, when false only 2. + /// When is enabled this instead applies to the bottommost line of the control + /// When true and or , header + /// line occupies 1 more column, when false 1 column less. + /// When is enabled this instead applies to the rightmost column of the control /// - public bool ShowTopLine { get; set; } = true; + public bool ShowInitialLine { get; set; } = true; /// Gets or sets the tabs side to render. public TabSide TabsSide { get; set; } diff --git a/Terminal.Gui/Views/TabView/TabView.cs b/Terminal.Gui/Views/TabView/TabView.cs index 0c514c3121..39840752b9 100644 --- a/Terminal.Gui/Views/TabView/TabView.cs +++ b/Terminal.Gui/Views/TabView/TabView.cs @@ -567,7 +567,7 @@ internal IEnumerable CalculateViewport (Rectangle bounds) long maxWidth = Math.Max (0, Math.Min (bounds.Width - 3, MaxTabTextWidth)); tab.Width = 2; - tab.Height = Style.ShowTopLine ? 3 : 2; + tab.Height = Style.ShowInitialLine ? 3 : 2; // if tab view is width <= 3 don't render any tabs if (maxWidth == 0) @@ -592,7 +592,7 @@ internal IEnumerable CalculateViewport (Rectangle bounds) } tab.Width = Math.Max (tabTextWidth + 2, 1); - tab.Height = Style.ShowTopLine ? 3 : 2; + tab.Height = Style.ShowInitialLine ? 3 : 2; // if there is not enough space for this tab if (i + tabTextWidth >= bounds.Width) @@ -625,7 +625,7 @@ internal IEnumerable CalculateViewport (Rectangle bounds) } /// - /// Returns the number of rows occupied by rendering the tabs, this depends on + /// Returns the number of rows occupied by rendering the tabs, this depends on /// and can be 0 (e.g. if and you ask for ). /// /// True to measure the space required at the top of the control, false to measure space at the bottom. @@ -643,7 +643,7 @@ private int GetTabHeight (bool top) return 0; } - return Style.ShowTopLine ? 3 : 2; + return Style.ShowInitialLine ? 3 : 2; } internal void Tab_MouseClick (object sender, MouseEventArgs e) diff --git a/Tests/UnitTests/Views/TabViewTests.cs b/Tests/UnitTests/Views/TabViewTests.cs index 5047bc944b..8227c8fbfe 100644 --- a/Tests/UnitTests/Views/TabViewTests.cs +++ b/Tests/UnitTests/Views/TabViewTests.cs @@ -590,7 +590,7 @@ public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width3 () TabView tv = GetTabView (out _, out _); tv.Width = 3; tv.Height = 5; - tv.Style = new () { ShowTopLine = false }; + tv.Style = new () { ShowInitialLine = false }; tv.ApplyStyleChanges (); tv.Layout (); @@ -614,7 +614,7 @@ public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width4 () TabView tv = GetTabView (out _, out _); tv.Width = 4; tv.Height = 5; - tv.Style = new () { ShowTopLine = false }; + tv.Style = new () { ShowInitialLine = false }; tv.ApplyStyleChanges (); tv.Layout (); @@ -638,7 +638,7 @@ public void ShowTopLine_False_TabsOnBottom_False_TestThinTabView_WithLongNames ( TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 10; tv.Height = 5; - tv.Style = new () { ShowTopLine = false }; + tv.Style = new () { ShowInitialLine = false }; tv.ApplyStyleChanges (); // Test two tab names that fit @@ -737,7 +737,7 @@ public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width3 () TabView tv = GetTabView (out _, out _); tv.Width = 3; tv.Height = 5; - tv.Style = new () { ShowTopLine = false, TabsSide = TabSide.Bottom }; + tv.Style = new () { ShowInitialLine = false, TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); tv.Layout (); @@ -761,7 +761,7 @@ public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width4 () TabView tv = GetTabView (out _, out _); tv.Width = 4; tv.Height = 5; - tv.Style = new () { ShowTopLine = false, TabsSide = TabSide.Bottom }; + tv.Style = new () { ShowInitialLine = false, TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); tv.Layout (); @@ -785,7 +785,7 @@ public void ShowTopLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames () TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 10; tv.Height = 5; - tv.Style = new () { ShowTopLine = false, TabsSide = TabSide.Bottom }; + tv.Style = new () { ShowInitialLine = false, TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); tv.Layout (); From 9da74ecd081d6a3a927bd5238a4e46ada97a844f Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 26 Nov 2024 13:35:33 +0000 Subject: [PATCH 03/18] Replace ShowTopLine with ShowInitialLine. --- Tests/UnitTests/Views/TabViewTests.cs | 28 +++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/Tests/UnitTests/Views/TabViewTests.cs b/Tests/UnitTests/Views/TabViewTests.cs index 8227c8fbfe..e7ae9a36c1 100644 --- a/Tests/UnitTests/Views/TabViewTests.cs +++ b/Tests/UnitTests/Views/TabViewTests.cs @@ -585,7 +585,7 @@ public void SelectedTabChanged_Called () [Fact] [SetupFakeDriver] - public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width3 () + public void ShowInitialLine_False_TabsOnBottom_False_TestTabView_Width3 () { TabView tv = GetTabView (out _, out _); tv.Width = 3; @@ -609,7 +609,7 @@ public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width3 () [Fact] [SetupFakeDriver] - public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width4 () + public void ShowInitialLine_False_TabsOnBottom_False_TestTabView_Width4 () { TabView tv = GetTabView (out _, out _); tv.Width = 4; @@ -633,7 +633,7 @@ public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width4 () [Fact] [SetupFakeDriver] - public void ShowTopLine_False_TabsOnBottom_False_TestThinTabView_WithLongNames () + public void ShowInitialLine_False_TabsOnBottom_False_TestThinTabView_WithLongNames () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 10; @@ -732,7 +732,7 @@ public void ShowTopLine_False_TabsOnBottom_False_TestThinTabView_WithLongNames ( [Fact] [SetupFakeDriver] - public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width3 () + public void ShowInitialLine_False_TabsOnBottom_True_TestTabView_Width3 () { TabView tv = GetTabView (out _, out _); tv.Width = 3; @@ -756,7 +756,7 @@ public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width3 () [Fact] [SetupFakeDriver] - public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width4 () + public void ShowInitialLine_False_TabsOnBottom_True_TestTabView_Width4 () { TabView tv = GetTabView (out _, out _); tv.Width = 4; @@ -780,7 +780,7 @@ public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width4 () [Fact] [SetupFakeDriver] - public void ShowTopLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames () + public void ShowInitialLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 10; @@ -881,7 +881,7 @@ public void ShowTopLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames () [Fact] [SetupFakeDriver] - public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width3 () + public void ShowInitialLine_True_TabsOnBottom_False_TestTabView_Width3 () { TabView tv = GetTabView (out _, out _); tv.Width = 3; @@ -903,7 +903,7 @@ public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width3 () [Fact] [SetupFakeDriver] - public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width4 () + public void ShowInitialLine_True_TabsOnBottom_False_TestTabView_Width4 () { TabView tv = GetTabView (out _, out _); tv.Width = 4; @@ -926,7 +926,7 @@ public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width4 () [Fact] [SetupFakeDriver] - public void ShowTopLine_True_TabsOnBottom_False_TestThinTabView_WithLongNames () + public void ShowInitialLine_True_TabsOnBottom_False_TestThinTabView_WithLongNames () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 10; @@ -1023,7 +1023,7 @@ public void ShowTopLine_True_TabsOnBottom_False_TestThinTabView_WithLongNames () [Fact] [SetupFakeDriver] - public void ShowTopLine_True_TabsOnBottom_False_With_Unicode () + public void ShowInitialLine_True_TabsOnBottom_False_With_Unicode () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 20; @@ -1065,7 +1065,7 @@ public void ShowTopLine_True_TabsOnBottom_False_With_Unicode () [Fact] [SetupFakeDriver] - public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width3 () + public void ShowInitialLine_True_TabsOnBottom_True_TestTabView_Width3 () { TabView tv = GetTabView (out _, out _); tv.Width = 3; @@ -1089,7 +1089,7 @@ public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width3 () [Fact] [SetupFakeDriver] - public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width4 () + public void ShowInitialLine_True_TabsOnBottom_True_TestTabView_Width4 () { TabView tv = GetTabView (out _, out _); tv.Width = 4; @@ -1113,7 +1113,7 @@ public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width4 () [Fact] [SetupFakeDriver] - public void ShowTopLine_True_TabsOnBottom_True_TestThinTabView_WithLongNames () + public void ShowInitialLine_True_TabsOnBottom_True_TestThinTabView_WithLongNames () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 10; @@ -1195,7 +1195,7 @@ public void ShowTopLine_True_TabsOnBottom_True_TestThinTabView_WithLongNames () [Fact] [SetupFakeDriver] - public void ShowTopLine_True_TabsOnBottom_True_With_Unicode () + public void ShowInitialLine_True_TabsOnBottom_True_With_Unicode () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 20; From 1813932bdcbe4719edc71957666de11b5aeabcdc Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 26 Nov 2024 13:48:47 +0000 Subject: [PATCH 04/18] Replace TabsOnBottom_False with TabsSide_Top. --- Tests/UnitTests/Views/TabViewTests.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Tests/UnitTests/Views/TabViewTests.cs b/Tests/UnitTests/Views/TabViewTests.cs index e7ae9a36c1..d3a5433c46 100644 --- a/Tests/UnitTests/Views/TabViewTests.cs +++ b/Tests/UnitTests/Views/TabViewTests.cs @@ -585,7 +585,7 @@ public void SelectedTabChanged_Called () [Fact] [SetupFakeDriver] - public void ShowInitialLine_False_TabsOnBottom_False_TestTabView_Width3 () + public void ShowInitialLine_False_TabsSide_Top_TestTabView_Width3 () { TabView tv = GetTabView (out _, out _); tv.Width = 3; @@ -609,7 +609,7 @@ public void ShowInitialLine_False_TabsOnBottom_False_TestTabView_Width3 () [Fact] [SetupFakeDriver] - public void ShowInitialLine_False_TabsOnBottom_False_TestTabView_Width4 () + public void ShowInitialLine_False_TabsSide_Top_TestTabView_Width4 () { TabView tv = GetTabView (out _, out _); tv.Width = 4; @@ -633,7 +633,7 @@ public void ShowInitialLine_False_TabsOnBottom_False_TestTabView_Width4 () [Fact] [SetupFakeDriver] - public void ShowInitialLine_False_TabsOnBottom_False_TestThinTabView_WithLongNames () + public void ShowInitialLine_False_TabsSide_Top_TestThinTabView_WithLongNames () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 10; @@ -881,7 +881,7 @@ public void ShowInitialLine_False_TabsOnBottom_True_TestThinTabView_WithLongName [Fact] [SetupFakeDriver] - public void ShowInitialLine_True_TabsOnBottom_False_TestTabView_Width3 () + public void ShowInitialLine_True_TabsSide_Top_TestTabView_Width3 () { TabView tv = GetTabView (out _, out _); tv.Width = 3; @@ -903,7 +903,7 @@ public void ShowInitialLine_True_TabsOnBottom_False_TestTabView_Width3 () [Fact] [SetupFakeDriver] - public void ShowInitialLine_True_TabsOnBottom_False_TestTabView_Width4 () + public void ShowInitialLine_True_TabsSide_Top_TestTabView_Width4 () { TabView tv = GetTabView (out _, out _); tv.Width = 4; @@ -926,7 +926,7 @@ public void ShowInitialLine_True_TabsOnBottom_False_TestTabView_Width4 () [Fact] [SetupFakeDriver] - public void ShowInitialLine_True_TabsOnBottom_False_TestThinTabView_WithLongNames () + public void ShowInitialLine_True_TabsSide_Top_TestThinTabView_WithLongNames () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 10; @@ -1023,7 +1023,7 @@ public void ShowInitialLine_True_TabsOnBottom_False_TestThinTabView_WithLongName [Fact] [SetupFakeDriver] - public void ShowInitialLine_True_TabsOnBottom_False_With_Unicode () + public void ShowInitialLine_True_TabsSide_Top_With_Unicode () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 20; From 02e0585868296e7db64b7e8f4727c7f2bffd1071 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 26 Nov 2024 13:50:52 +0000 Subject: [PATCH 05/18] Replace TabsOnBottom_True with TabsSide_Bottom. --- Tests/UnitTests/Views/TabViewTests.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Tests/UnitTests/Views/TabViewTests.cs b/Tests/UnitTests/Views/TabViewTests.cs index d3a5433c46..2eb582ab82 100644 --- a/Tests/UnitTests/Views/TabViewTests.cs +++ b/Tests/UnitTests/Views/TabViewTests.cs @@ -732,7 +732,7 @@ public void ShowInitialLine_False_TabsSide_Top_TestThinTabView_WithLongNames () [Fact] [SetupFakeDriver] - public void ShowInitialLine_False_TabsOnBottom_True_TestTabView_Width3 () + public void ShowInitialLine_False_TabsSide_Bottom_TestTabView_Width3 () { TabView tv = GetTabView (out _, out _); tv.Width = 3; @@ -756,7 +756,7 @@ public void ShowInitialLine_False_TabsOnBottom_True_TestTabView_Width3 () [Fact] [SetupFakeDriver] - public void ShowInitialLine_False_TabsOnBottom_True_TestTabView_Width4 () + public void ShowInitialLine_False_TabsSide_Bottom_TestTabView_Width4 () { TabView tv = GetTabView (out _, out _); tv.Width = 4; @@ -780,7 +780,7 @@ public void ShowInitialLine_False_TabsOnBottom_True_TestTabView_Width4 () [Fact] [SetupFakeDriver] - public void ShowInitialLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames () + public void ShowInitialLine_False_TabsSide_Bottom_TestThinTabView_WithLongNames () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 10; @@ -1065,7 +1065,7 @@ public void ShowInitialLine_True_TabsSide_Top_With_Unicode () [Fact] [SetupFakeDriver] - public void ShowInitialLine_True_TabsOnBottom_True_TestTabView_Width3 () + public void ShowInitialLine_True_TabsSide_Bottom_TestTabView_Width3 () { TabView tv = GetTabView (out _, out _); tv.Width = 3; @@ -1089,7 +1089,7 @@ public void ShowInitialLine_True_TabsOnBottom_True_TestTabView_Width3 () [Fact] [SetupFakeDriver] - public void ShowInitialLine_True_TabsOnBottom_True_TestTabView_Width4 () + public void ShowInitialLine_True_TabsSide_Bottom_TestTabView_Width4 () { TabView tv = GetTabView (out _, out _); tv.Width = 4; @@ -1113,7 +1113,7 @@ public void ShowInitialLine_True_TabsOnBottom_True_TestTabView_Width4 () [Fact] [SetupFakeDriver] - public void ShowInitialLine_True_TabsOnBottom_True_TestThinTabView_WithLongNames () + public void ShowInitialLine_True_TabsSide_Bottom_TestThinTabView_WithLongNames () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 10; @@ -1195,7 +1195,7 @@ public void ShowInitialLine_True_TabsOnBottom_True_TestThinTabView_WithLongNames [Fact] [SetupFakeDriver] - public void ShowInitialLine_True_TabsOnBottom_True_With_Unicode () + public void ShowInitialLine_True_TabsSide_Bottom_With_Unicode () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); tv.Width = 20; From 1686cad18e10e6a822e3584e244334a4da2135c1 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 26 Nov 2024 17:37:25 +0000 Subject: [PATCH 06/18] Fix bug that was passing the TabRow.Viewport instead of the TabView.Viewport. --- Terminal.Gui/Views/TabView/TabRow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Terminal.Gui/Views/TabView/TabRow.cs b/Terminal.Gui/Views/TabView/TabRow.cs index c0ab3d7566..30d9202683 100644 --- a/Terminal.Gui/Views/TabView/TabRow.cs +++ b/Terminal.Gui/Views/TabView/TabRow.cs @@ -118,7 +118,7 @@ protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocus /// protected override void OnSubViewLayout (LayoutEventArgs args) { - _host._tabLocations = _host.CalculateViewport (Viewport).ToArray (); + _host._tabLocations = _host.CalculateViewport (_host.Viewport).ToArray (); RenderTabLine (); From d77bffcf9f8ab7ba6dd3684cac268a6d0245b7c1 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 27 Nov 2024 21:35:34 +0000 Subject: [PATCH 07/18] Add DisplayTextChanged event. --- Terminal.Gui/Views/TabView/Tab.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Terminal.Gui/Views/TabView/Tab.cs b/Terminal.Gui/Views/TabView/Tab.cs index f0025b9831..48041bf65c 100644 --- a/Terminal.Gui/Views/TabView/Tab.cs +++ b/Terminal.Gui/Views/TabView/Tab.cs @@ -23,6 +23,7 @@ public string DisplayText set { _displayText = value; + DisplayTextChanged?.Invoke (this, EventArgs.Empty); SetNeedsLayout (); } } @@ -30,4 +31,9 @@ public string DisplayText /// The control to display when the tab is selected. /// public View? View { get; set; } + + /// + /// Raised when changed. + /// + public event EventHandler? DisplayTextChanged; } From 5f526b2ef140e78ca7f834e6ea0b75511100fdc4 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 27 Nov 2024 21:37:43 +0000 Subject: [PATCH 08/18] Fix unit test. Now F6 focus the select tab. --- Tests/UnitTests/Views/TabViewTests.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/UnitTests/Views/TabViewTests.cs b/Tests/UnitTests/Views/TabViewTests.cs index 2eb582ab82..594178e1e4 100644 --- a/Tests/UnitTests/Views/TabViewTests.cs +++ b/Tests/UnitTests/Views/TabViewTests.cs @@ -379,8 +379,8 @@ public void ProcessKey_Down_Up_Right_Left_Home_End_PageDown_PageUp_F6 () Assert.Equal (tv.MostFocused, top.Focused.MostFocused); Assert.Equal (tv.SelectedTab.View, top.Focused.MostFocused); - // Press the cursor up key to focus the selected tab - Assert.True (Application.RaiseKeyDownEvent (Key.CursorUp)); + // Press F6 key to focus the selected tab + Assert.True (Application.RaiseKeyDownEvent (Key.F6)); Application.LayoutAndDraw (); // Is the selected tab focused From bdd9912b03a1c05e1fd40600a68207da5f4c4808 Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 28 Nov 2024 00:06:11 +0000 Subject: [PATCH 09/18] Add Command.Up and Command.Down. --- Terminal.Gui/Views/TabView/TabRow.cs | 1 + Terminal.Gui/Views/TabView/TabView.cs | 48 ++++++++++++++++++++++++--- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/Terminal.Gui/Views/TabView/TabRow.cs b/Terminal.Gui/Views/TabView/TabRow.cs index 30d9202683..1dda0c2a9b 100644 --- a/Terminal.Gui/Views/TabView/TabRow.cs +++ b/Terminal.Gui/Views/TabView/TabRow.cs @@ -14,6 +14,7 @@ public TabRow (TabView host) Id = "tabRow"; CanFocus = true; + // Because TabRow has focusable subviews, it must be a TabGroup TabStop = TabBehavior.TabGroup; Width = Dim.Fill (); diff --git a/Terminal.Gui/Views/TabView/TabView.cs b/Terminal.Gui/Views/TabView/TabView.cs index 39840752b9..d29aa35a7c 100644 --- a/Terminal.Gui/Views/TabView/TabView.cs +++ b/Terminal.Gui/Views/TabView/TabView.cs @@ -28,8 +28,8 @@ public class TabView : View public TabView () { CanFocus = true; - TabStop = TabBehavior.TabStop; // Because TabView has focusable subviews, it must be a TabGroup - _tabsBar = new TabRow (this); + TabStop = TabBehavior.TabStop; + _tabsBar = new (this); _containerView = new (); ApplyStyleChanges (); @@ -37,9 +37,45 @@ public TabView () base.Add (_containerView); // Things this view knows how to do - AddCommand (Command.Left, () => SwitchTabBy (-1)); - - AddCommand (Command.Right, () => SwitchTabBy (1)); + AddCommand (Command.Left, () => + { + if (Style.TabsSide is TabSide.Top or TabSide.Bottom) + { + return SwitchTabBy (-1); + } + + return false; + }); + + AddCommand (Command.Right, () => + { + if (Style.TabsSide is TabSide.Top or TabSide.Bottom) + { + return SwitchTabBy (1); + } + + return false; + }); + + AddCommand (Command.Up, () => + { + if (Style.TabsSide is TabSide.Left or TabSide.Right) + { + return SwitchTabBy (-1); + } + + return false; + }); + + AddCommand (Command.Down, () => + { + if (Style.TabsSide is TabSide.Left or TabSide.Right) + { + return SwitchTabBy (1); + } + + return false; + }); AddCommand ( Command.LeftStart, @@ -174,6 +210,8 @@ public TabView () // Default keybindings for this view KeyBindings.Add (Key.CursorLeft, Command.Left); KeyBindings.Add (Key.CursorRight, Command.Right); + KeyBindings.Add (Key.CursorUp, Command.Up); + KeyBindings.Add (Key.CursorDown, Command.Down); KeyBindings.Add (Key.Home, Command.LeftStart); KeyBindings.Add (Key.End, Command.RightEnd); KeyBindings.Add (Key.PageDown, Command.PageDown); From 54f9589773ef9d9b5de46c7891f112e4872d658d Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 28 Nov 2024 00:19:40 +0000 Subject: [PATCH 10/18] Starting TabSide.Left feature. --- Terminal.Gui/Views/TabView/TabRow.cs | 111 ++------ Terminal.Gui/Views/TabView/TabView.cs | 376 +++++++++++++++++++++----- 2 files changed, 321 insertions(+), 166 deletions(-) diff --git a/Terminal.Gui/Views/TabView/TabRow.cs b/Terminal.Gui/Views/TabView/TabRow.cs index 1dda0c2a9b..626fda8be9 100644 --- a/Terminal.Gui/Views/TabView/TabRow.cs +++ b/Terminal.Gui/Views/TabView/TabRow.cs @@ -41,6 +41,14 @@ public TabRow (TabView host) Add (_rightScrollIndicator, _leftScrollIndicator); } + /// + public override void EndInit () + { + _host._tabLocations = _host.CalculateViewport (Viewport); + + base.EndInit (); + } + protected override bool OnMouseEvent (MouseEventArgs me) { View? parent = me.View is Adornment adornment ? adornment.Parent : me.View; @@ -119,9 +127,16 @@ protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocus /// protected override void OnSubViewLayout (LayoutEventArgs args) { - _host._tabLocations = _host.CalculateViewport (_host.Viewport).ToArray (); + if (_host._tabLocations is null) + { + return; + } - RenderTabLine (); + if (_host is { SelectedTab: { }, _tabLocations: { } } && !_host._tabLocations!.Contains (_host.SelectedTab)) + { + _host.SelectedTab = _host._tabLocations [0]; + Application.Invoke (() => _host.SetNeedsLayout ()); + } RenderUnderline (); @@ -766,7 +781,7 @@ private void RenderTabLineCanvas () } } - _host.LineCanvas.Merge (lc); + LineCanvas.Merge (lc); } private int GetUnderlineYPosition () @@ -779,96 +794,6 @@ private int GetUnderlineYPosition () return _host.Style.ShowInitialLine ? 2 : 1; } - /// Renders the line with the tab names in it. - private void RenderTabLine () - { - if (_host._tabLocations is null) - { - return; - } - - View? selected = null; - int topLine = _host.Style.ShowInitialLine ? 1 : 0; - - foreach (Tab toRender in _host._tabLocations) - { - Tab tab = toRender; - - if (toRender == _host.SelectedTab) - { - selected = tab; - - switch (_host.Style.TabsSide) - { - case TabSide.Top: - tab.Border!.Thickness = new (1, topLine, 1, 0); - tab.Margin!.Thickness = new (0, 0, 0, topLine); - - break; - case TabSide.Bottom: - tab.Border!.Thickness = new (1, 0, 1, topLine); - tab.Margin!.Thickness = new (0, 1, 0, 0); - - break; - case TabSide.Left: - break; - case TabSide.Right: - break; - default: - throw new ArgumentOutOfRangeException (); - } - } - else if (selected is null) - { - switch (_host.Style.TabsSide) - { - case TabSide.Top: - tab.Border!.Thickness = new (1, topLine, 1, 1); - tab.Margin!.Thickness = new (0, 0, 0, 0); - - break; - case TabSide.Bottom: - tab.Border!.Thickness = new (1, 1, 1, topLine); - tab.Margin!.Thickness = new (0, 0, 0, 0); - - break; - case TabSide.Left: - break; - case TabSide.Right: - break; - default: - throw new ArgumentOutOfRangeException (); - } - } - else - { - switch (_host.Style.TabsSide) - { - case TabSide.Top: - tab.Border!.Thickness = new (1, topLine, 1, 1); - tab.Margin!.Thickness = new (0, 0, 0, 0); - - break; - case TabSide.Bottom: - tab.Border!.Thickness = new (1, 1, 1, topLine); - tab.Margin!.Thickness = new (0, 0, 0, 0); - - break; - case TabSide.Left: - break; - case TabSide.Right: - break; - default: - throw new ArgumentOutOfRangeException (); - } - } - - // Ensures updating TextFormatter constrains - tab.TextFormatter.ConstrainToWidth = tab.GetContentSize ().Width; - tab.TextFormatter.ConstrainToHeight = tab.GetContentSize ().Height; - } - } - /// Renders the line of the tab that adjoins the content of the tab. private void RenderUnderline () { diff --git a/Terminal.Gui/Views/TabView/TabView.cs b/Terminal.Gui/Views/TabView/TabView.cs index d29aa35a7c..f6d9e2d3b5 100644 --- a/Terminal.Gui/Views/TabView/TabView.cs +++ b/Terminal.Gui/Views/TabView/TabView.cs @@ -356,57 +356,71 @@ public void AddTab (Tab tab, bool andSelect) /// public void ApplyStyleChanges () { + _tabLocations = CalculateViewport (Viewport); + _containerView.BorderStyle = Style.ShowBorder ? LineStyle.Single : LineStyle.None; _containerView.Width = Dim.Fill (); - int tabHeight; - switch (Style.TabsSide) { case TabSide.Top: // Tabs are along the top if (Style.ShowBorder) { - _containerView.Border!.Thickness = new Thickness (1, 0, 1, 1); + _containerView.Border!.Thickness = new (1, 0, 1, 1); } + _tabsBar.X = 0; _tabsBar.Y = 0; + _tabsBar.Width = Dim.Fill (); + _tabsBar.Height = GetTabHeight (true); - tabHeight = GetTabHeight (true); - + _containerView.X = 0; //move content down to make space for tabs _containerView.Y = Pos.Bottom (_tabsBar); - - // Fill client area leaving space at bottom for border + _containerView.Width = Dim.Fill (); _containerView.Height = Dim.Fill (); - // The top tab should be 2 or 3 rows high and on the top - - _tabsBar.Height = tabHeight; - - // Should be able to just use 0 but switching between top/bottom tabs repeatedly breaks in ValidatePosDim if just using the absolute value 0 - break; case TabSide.Bottom: // Tabs are along the bottom so just dodge the border if (Style.ShowBorder) { - _containerView.Border!.Thickness = new Thickness (1, 1, 1, 0); + _containerView.Border!.Thickness = new (1, 1, 1, 0); } - _containerView.Y = 0; - - tabHeight = GetTabHeight (false); + _tabsBar.X = 0; + _tabsBar.Width = Dim.Fill (); + int tabHeight = GetTabHeight (false); + _tabsBar.Height = tabHeight; + _containerView.X = 0; + _containerView.Y = 0; + _containerView.Width = Dim.Fill (); // Fill client area leaving space at bottom for tabs _containerView.Height = Dim.Fill (tabHeight); - _tabsBar.Height = tabHeight; - _tabsBar.Y = Pos.Bottom (_containerView); break; case TabSide.Left: + // Tabs are along the left + if (Style.ShowBorder) + { + _containerView.Border!.Thickness = new (0, 1, 1, 1); + } + + _tabsBar.X = 0; + _tabsBar.Y = 0; + _tabsBar.Height = Dim.Fill (); + + //move content right to make space for tabs + _containerView.X = Pos.Right (_tabsBar); + _containerView.Y = 0; + // Fill client area leaving space at left for tabs + _containerView.Width = Dim.Fill (); + _containerView.Height = Dim.Fill (); + break; case TabSide.Right: break; @@ -420,7 +434,7 @@ public void ApplyStyleChanges () /// protected override void OnViewportChanged (DrawEventArgs e) { - _tabLocations = CalculateViewport (Viewport).ToArray (); + _tabLocations = CalculateViewport (Viewport); base.OnViewportChanged (e); } @@ -434,10 +448,15 @@ public void EnsureSelectedTabIsVisible () } // if current viewport does not include the selected tab - if (!CalculateViewport (Viewport).Any (t => Equals (SelectedTab, t))) + if (_tabLocations is null || (_tabLocations is { } && !_tabLocations.Any (t => Equals (SelectedTab, t)))) { // Set scroll offset so the first tab rendered is the TabScrollOffset = Math.Max (0, Tabs.IndexOf (SelectedTab)); + _tabLocations = CalculateViewport (Viewport); + } + else + { + RenderTabLine (_tabLocations); } } @@ -575,81 +594,180 @@ protected virtual void OnSelectedTabChanged (Tab oldTab, Tab newTab) /// Returns which tabs to render at each x location. /// - internal IEnumerable CalculateViewport (Rectangle bounds) + internal Tab []? CalculateViewport (Rectangle bounds) { UnSetCurrentTabs (); + List tabs = []; var i = 1; View? prevTab = null; - // Starting at the first or scrolled to tab - foreach (Tab tab in Tabs.Skip (TabScrollOffset)) + switch (Style.TabsSide) { - if (prevTab is { }) - { - tab.X = Pos.Right (prevTab) - 1; - } - else - { - tab.X = 0; - } + case TabSide.Top: + case TabSide.Bottom: + // Starting at the first or scrolled to tab + foreach (Tab tab in Tabs.Skip (TabScrollOffset)) + { + if (prevTab is { }) + { + tab.X = Pos.Right (prevTab) - 1; + } + else + { + tab.X = 0; + } - tab.Y = 0; + tab.Y = 0; - // while there is space for the tab - int tabTextWidth = tab.DisplayText.EnumerateRunes ().Sum (c => c.GetColumns ()); + // while there is space for the tab + int tabTextWidth = tab.DisplayText.EnumerateRunes ().Sum (c => c.GetColumns ()); - // The maximum number of characters to use for the tab name as specified - // by the user (MaxTabTextWidth). But not more than the width of the view - // or we won't even be able to render a single tab! - long maxWidth = Math.Max (0, Math.Min (bounds.Width - 3, MaxTabTextWidth)); + // The maximum number of characters to use for the tab name as specified + // by the user (MaxTabTextWidth). But not more than the width of the view + // or we won't even be able to render a single tab! + long maxWidth = Math.Max (0, Math.Min (bounds.Width - 3, MaxTabTextWidth)); - tab.Width = 2; - tab.Height = Style.ShowInitialLine ? 3 : 2; + tab.Width = 2; + tab.Height = Style.ShowInitialLine ? 3 : 2; - // if tab view is width <= 3 don't render any tabs - if (maxWidth == 0) - { - tab.Visible = true; - tab.MouseClick += Tab_MouseClick!; - tab.Border!.MouseClick += Tab_MouseClick!; + // if tab view is width <= 3 don't render any tabs + if (maxWidth == 0) + { + tab.Visible = true; + tab.MouseClick += Tab_MouseClick!; + tab.Border!.MouseClick += Tab_MouseClick!; + tab.DisplayTextChanged += Tab_DisplayTextChanged; - yield return tab; + tabs.Add (tab); - break; - } + break; + } - if (tabTextWidth > maxWidth) - { - tab.Text = tab.DisplayText.Substring (0, (int)maxWidth); - tabTextWidth = (int)maxWidth; - } - else - { - tab.Text = tab.DisplayText; - } + if (tabTextWidth > maxWidth) + { + tab.Text = tab.DisplayText.Substring (0, (int)maxWidth); + tabTextWidth = (int)maxWidth; + } + else + { + tab.Text = tab.DisplayText; + } - tab.Width = Math.Max (tabTextWidth + 2, 1); - tab.Height = Style.ShowInitialLine ? 3 : 2; + tab.Width = tabTextWidth + 2; + tab.Height = Style.ShowInitialLine ? 3 : 2; - // if there is not enough space for this tab - if (i + tabTextWidth >= bounds.Width) - { - tab.Visible = false; + // if there is not enough space for this tab + if (i + tabTextWidth >= bounds.Width) + { + tab.Visible = false; + + break; + } + + // there is enough space! + tab.Visible = true; + tab.MouseClick += Tab_MouseClick!; + tab.Border!.MouseClick += Tab_MouseClick!; + tab.DisplayTextChanged += Tab_DisplayTextChanged; + + tabs.Add (tab); + + prevTab = tab; + + i += tabTextWidth + 1; + } break; - } + case TabSide.Left: + case TabSide.Right: + int maxColWidth = 0; - // there is enough space! - tab.Visible = true; - tab.MouseClick += Tab_MouseClick!; - tab.Border!.MouseClick += Tab_MouseClick!; + // Starting at the first or scrolled to tab + foreach (Tab tab in Tabs.Skip (TabScrollOffset)) + { + tab.X = 0; - yield return tab; + if (prevTab is { }) + { + tab.Y = Pos.Bottom (prevTab) - 1; + } + else + { + tab.Y = 0; + } + + // while there is space for the tab + int tabTextWidth = tab.DisplayText.EnumerateRunes ().Sum (c => c.GetColumns ()); + + // The maximum number of characters to use for the tab name as specified + // by the user (MaxTabTextWidth). But not more than the width of the view + // or we won't even be able to render a single tab! + long maxWidth = Math.Max (0, Math.Min (bounds.Width - (Style.ShowInitialLine ? 2 : 1), MaxTabTextWidth)); + + // The maximum height to use for the tab. But not more than the height of the view + // or we won't even be able to render a single tab! + int maxHeight = Math.Max (0, Math.Min (bounds.Height - 3, 3)); + + tab.Height = 2; + + // if tab view is height <= 3 don't render any tabs + if (maxHeight == 0) + { + tab.Width = maxColWidth = Math.Max (Style.ShowInitialLine ? 3 : 2, maxColWidth); + tab.Visible = true; + tab.MouseClick += Tab_MouseClick!; + tab.Border!.MouseClick += Tab_MouseClick!; + tab.DisplayTextChanged += Tab_DisplayTextChanged; + + tabs.Add (tab); + + break; + } + + if (tabTextWidth > maxWidth) + { + tab.Text = tab.DisplayText.Substring (0, (int)maxWidth); + tabTextWidth = (int)maxWidth; + } + else + { + tab.Text = tab.DisplayText; + } - prevTab = tab; + maxColWidth = Math.Max (tabTextWidth + 2, maxColWidth); + tab.Height = 3; - i += tabTextWidth + 1; + // if there is not enough space for this tab + if (i + 1 >= bounds.Height) + { + tab.Visible = false; + + break; + } + + // there is enough space! + tab.Visible = true; + tab.MouseClick += Tab_MouseClick!; + tab.Border!.MouseClick += Tab_MouseClick!; + tab.DisplayTextChanged += Tab_DisplayTextChanged; + + tabs.Add (tab); + + prevTab = tab; + + i += 2; + } + + foreach (Tab t in tabs) + { + t.Width = maxColWidth; + } + _tabsBar.Width = maxColWidth; + + break; + default: + throw new ArgumentOutOfRangeException (); } if (TabCanSetFocus ()) @@ -660,6 +778,116 @@ internal IEnumerable CalculateViewport (Rectangle bounds) { SelectedTab?.View?.SetFocus (); } + + RenderTabLine (tabs.Count == 0 ? null : tabs.ToArray ()); + + SetNeedsLayout (); + + return tabs.Count == 0 ? null : tabs.ToArray (); + } + + private void Tab_DisplayTextChanged (object? sender, EventArgs e) + { + _tabLocations = CalculateViewport (Viewport); + } + + /// Renders the line with the tab names in it. + private void RenderTabLine (Tab []? tabLocations) + { + if (tabLocations is null) + { + return; + } + + View? selected = null; + int topLine = Style.ShowInitialLine ? 1 : 0; + + foreach (Tab toRender in tabLocations) + { + Tab tab = toRender; + + if (toRender == SelectedTab) + { + selected = tab; + + switch (Style.TabsSide) + { + case TabSide.Top: + tab.Border!.Thickness = new (1, topLine, 1, 0); + tab.Margin!.Thickness = new (0, 0, 0, topLine); + + break; + case TabSide.Bottom: + tab.Border!.Thickness = new (1, 0, 1, topLine); + tab.Margin!.Thickness = new (0, 1, 0, 0); + + break; + case TabSide.Left: + tab.Border!.Thickness = new (topLine, 1, 0, 1); + tab.Margin!.Thickness = new (0, 0, topLine, 0); + + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); + } + } + else if (selected is null) + { + switch (Style.TabsSide) + { + case TabSide.Top: + tab.Border!.Thickness = new (1, topLine, 1, 1); + tab.Margin!.Thickness = new (0, 0, 0, 0); + + break; + case TabSide.Bottom: + tab.Border!.Thickness = new (1, 1, 1, topLine); + tab.Margin!.Thickness = new (0, 0, 0, 0); + + break; + case TabSide.Left: + tab.Border!.Thickness = new (topLine, 1, 1, 1); + tab.Margin!.Thickness = new (0, 0, 0, 0); + + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); + } + } + else + { + switch (Style.TabsSide) + { + case TabSide.Top: + tab.Border!.Thickness = new (1, topLine, 1, 1); + tab.Margin!.Thickness = new (0, 0, 0, 0); + + break; + case TabSide.Bottom: + tab.Border!.Thickness = new (1, 1, 1, topLine); + tab.Margin!.Thickness = new (0, 0, 0, 0); + + break; + case TabSide.Left: + tab.Border!.Thickness = new (topLine, 1, 1, 1); + tab.Margin!.Thickness = new (0, 0, 0, 0); + + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); + } + } + + // Ensures updating TextFormatter constrains + tab.TextFormatter.ConstrainToWidth = tab.GetContentSize ().Width; + tab.TextFormatter.ConstrainToHeight = tab.GetContentSize ().Height; + } } /// @@ -702,6 +930,7 @@ private void UnSetCurrentTabs () { tab.MouseClick -= Tab_MouseClick!; tab.Border!.MouseClick -= Tab_MouseClick!; + tab.DisplayTextChanged -= Tab_DisplayTextChanged; tab.Visible = false; } } @@ -712,6 +941,7 @@ private void UnSetCurrentTabs () { tabToRender.MouseClick -= Tab_MouseClick!; tabToRender.Border!.MouseClick -= Tab_MouseClick!; + tabToRender.DisplayTextChanged -= Tab_DisplayTextChanged; tabToRender.Visible = false; } From e99e12daa58014997445df0647a9a65600339498 Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 28 Nov 2024 16:43:48 +0000 Subject: [PATCH 11/18] Add up and down arrows feature. --- Terminal.Gui/Views/TabView/TabRow.cs | 127 +++++++++++++++++++-------- 1 file changed, 92 insertions(+), 35 deletions(-) diff --git a/Terminal.Gui/Views/TabView/TabRow.cs b/Terminal.Gui/Views/TabView/TabRow.cs index 626fda8be9..141a3e3ef1 100644 --- a/Terminal.Gui/Views/TabView/TabRow.cs +++ b/Terminal.Gui/Views/TabView/TabRow.cs @@ -5,8 +5,8 @@ namespace Terminal.Gui.Views; internal class TabRow : View { private readonly TabView _host; - private readonly View _leftScrollIndicator; - private readonly View _rightScrollIndicator; + private readonly View _leftUpScrollIndicator; + private readonly View _rightDownScrollIndicator; public TabRow (TabView host) { @@ -18,27 +18,25 @@ public TabRow (TabView host) TabStop = TabBehavior.TabGroup; Width = Dim.Fill (); - _rightScrollIndicator = new View + _rightDownScrollIndicator = new View { - Id = "rightScrollIndicator", + Id = "rightDownScrollIndicator", Width = 1, Height = 1, - Visible = false, - Text = Glyphs.RightArrow.ToString () + Visible = false }; - _rightScrollIndicator.MouseClick += _host.Tab_MouseClick!; + _rightDownScrollIndicator.MouseClick += _host.Tab_MouseClick!; - _leftScrollIndicator = new View + _leftUpScrollIndicator = new View { - Id = "leftScrollIndicator", + Id = "leftUpScrollIndicator", Width = 1, Height = 1, - Visible = false, - Text = Glyphs.LeftArrow.ToString () + Visible = false }; - _leftScrollIndicator.MouseClick += _host.Tab_MouseClick!; + _leftUpScrollIndicator.MouseClick += _host.Tab_MouseClick!; - Add (_rightScrollIndicator, _leftScrollIndicator); + Add (_rightDownScrollIndicator, _leftUpScrollIndicator); } /// @@ -84,11 +82,11 @@ protected override bool OnMouseEvent (MouseEventArgs me) { var scrollIndicatorHit = 0; - if (me.View is { Id: "rightScrollIndicator" } || me.Flags.HasFlag (MouseFlags.WheeledDown) || me.Flags.HasFlag (MouseFlags.WheeledRight)) + if (me.View is { Id: "rightDownScrollIndicator" } || me.Flags.HasFlag (MouseFlags.WheeledDown) || me.Flags.HasFlag (MouseFlags.WheeledRight)) { scrollIndicatorHit = 1; } - else if (me.View is { Id: "leftScrollIndicator" } || me.Flags.HasFlag (MouseFlags.WheeledUp) || me.Flags.HasFlag (MouseFlags.WheeledLeft)) + else if (me.View is { Id: "leftUpScrollIndicator" } || me.Flags.HasFlag (MouseFlags.WheeledUp) || me.Flags.HasFlag (MouseFlags.WheeledLeft)) { scrollIndicatorHit = -1; } @@ -672,7 +670,7 @@ private void RenderTabLineCanvas () int lineLength = tabsBarVts.Right - vts.Right; // Right horizontal line - if (ShouldDrawRightScrollIndicator ()) + if (ShouldDrawRightDownScrollIndicator ()) { if (lineLength - arrowOffset > 0) { @@ -784,20 +782,30 @@ private void RenderTabLineCanvas () LineCanvas.Merge (lc); } - private int GetUnderlineYPosition () + private int GetUnderlineXOrYPosition () { - if (_host.Style.TabsSide == TabSide.Bottom) + switch (_host.Style.TabsSide) { - return 0; - } + case TabSide.Top: + + return _host.Style.ShowInitialLine ? 2 : 1; + case TabSide.Bottom: + + return 0; + case TabSide.Left: - return _host.Style.ShowInitialLine ? 2 : 1; + return _host.Style.ShowInitialLine ? Frame.Right - 1 : Frame.Right; + case TabSide.Right: + return 0; + default: + throw new ArgumentOutOfRangeException (); + } } /// Renders the line of the tab that adjoins the content of the tab. private void RenderUnderline () { - int y = GetUnderlineYPosition (); + int xOrY = GetUnderlineXOrYPosition (); Tab? selected = _host._tabLocations?.FirstOrDefault (t => t == _host.SelectedTab); @@ -806,42 +814,91 @@ private void RenderUnderline () return; } - // draw scroll indicators + // Set the correct glyphs for scroll indicators + switch (_host.Style.TabsSide) + { + case TabSide.Top: + case TabSide.Bottom: + _rightDownScrollIndicator.Text = Glyphs.RightArrow.ToString (); + _leftUpScrollIndicator.Text = Glyphs.LeftArrow.ToString (); + + break; + case TabSide.Left: + case TabSide.Right: + _rightDownScrollIndicator.Text = Glyphs.DownArrow.ToString (); + _leftUpScrollIndicator.Text = Glyphs.UpArrow.ToString (); + + break; + default: + throw new ArgumentOutOfRangeException (); + } + + // position scroll indicators // if there are more tabs to the left not visible if (_host.TabScrollOffset > 0) { - _leftScrollIndicator.X = 0; - _leftScrollIndicator.Y = y; + switch (_host.Style.TabsSide) + { + case TabSide.Top: + case TabSide.Bottom: + _leftUpScrollIndicator.X = 0; + _leftUpScrollIndicator.Y = xOrY; + + break; + case TabSide.Left: + case TabSide.Right: + _leftUpScrollIndicator.X = xOrY; + _leftUpScrollIndicator.Y = 0; + + break; + default: + throw new ArgumentOutOfRangeException (); + } // indicate that - _leftScrollIndicator.Visible = true; + _leftUpScrollIndicator.Visible = true; // Ensures this is clicked instead of the first tab - MoveSubViewToEnd (_leftScrollIndicator); + MoveSubViewToEnd (_leftUpScrollIndicator); } else { - _leftScrollIndicator.Visible = false; + _leftUpScrollIndicator.Visible = false; } // if there are more tabs to the right not visible - if (ShouldDrawRightScrollIndicator ()) + if (ShouldDrawRightDownScrollIndicator ()) { - _rightScrollIndicator.X = Viewport.Width - 1; - _rightScrollIndicator.Y = y; + switch (_host.Style.TabsSide) + { + case TabSide.Top: + case TabSide.Bottom: + _rightDownScrollIndicator.X = Viewport.Width - 1; + _rightDownScrollIndicator.Y = xOrY; + + break; + case TabSide.Left: + case TabSide.Right: + _rightDownScrollIndicator.X = xOrY; + _rightDownScrollIndicator.Y = Viewport.Height - 1; + + break; + default: + throw new ArgumentOutOfRangeException (); + } // indicate that - _rightScrollIndicator.Visible = true; + _rightDownScrollIndicator.Visible = true; // Ensures this is clicked instead of the last tab if under this - MoveSubViewToStart (_rightScrollIndicator); + MoveSubViewToStart (_rightDownScrollIndicator); } else { - _rightScrollIndicator.Visible = false; + _rightDownScrollIndicator.Visible = false; } } - private bool ShouldDrawRightScrollIndicator () { return _host._tabLocations!.LastOrDefault () != _host.Tabs.LastOrDefault (); } + private bool ShouldDrawRightDownScrollIndicator () { return _host._tabLocations!.LastOrDefault () != _host.Tabs.LastOrDefault (); } } From 17151296cb06ac6d64bb330a39240bfcd782bded Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 28 Nov 2024 16:49:45 +0000 Subject: [PATCH 12/18] Cleanup Point. --- Terminal.Gui/Views/TabView/TabRow.cs | 100 +++++++++++++-------------- 1 file changed, 50 insertions(+), 50 deletions(-) diff --git a/Terminal.Gui/Views/TabView/TabRow.cs b/Terminal.Gui/Views/TabView/TabRow.cs index 141a3e3ef1..931883fefc 100644 --- a/Terminal.Gui/Views/TabView/TabRow.cs +++ b/Terminal.Gui/Views/TabView/TabRow.cs @@ -177,7 +177,7 @@ private void RenderTabLineCanvas () case TabSide.Top: // Lower left vertical line lc.AddLine ( - new Point (vts.X - 1, vts.Bottom - selectedOffset), + new (vts.X - 1, vts.Bottom - selectedOffset), -1, Orientation.Vertical, tab.BorderStyle @@ -187,7 +187,7 @@ private void RenderTabLineCanvas () case TabSide.Bottom: // Upper left vertical line lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), + new (vts.X - 1, vts.Y - 1), -1, Orientation.Vertical, tab.BorderStyle @@ -209,14 +209,14 @@ private void RenderTabLineCanvas () case TabSide.Top: // LRCorner lc.AddLine ( - new Point (vts.X - 1, vts.Bottom - selectedOffset), + new (vts.X - 1, vts.Bottom - selectedOffset), -1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.X - 1, vts.Bottom - selectedOffset), + new (vts.X - 1, vts.Bottom - selectedOffset), -1, Orientation.Horizontal, tab.BorderStyle @@ -226,14 +226,14 @@ private void RenderTabLineCanvas () case TabSide.Bottom: // URCorner lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), + new (vts.X - 1, vts.Y - 1), 1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), + new (vts.X - 1, vts.Y - 1), -1, Orientation.Horizontal, tab.BorderStyle @@ -255,14 +255,14 @@ private void RenderTabLineCanvas () case TabSide.Top: // Upper left tee lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), + new (vts.X - 1, vts.Y - 1), 1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), + new (vts.X - 1, vts.Y - 1), 0, Orientation.Horizontal, tab.BorderStyle @@ -272,14 +272,14 @@ private void RenderTabLineCanvas () case TabSide.Bottom: // Lower left tee lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), + new (vts.X - 1, vts.Bottom), -1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), + new (vts.X - 1, vts.Bottom), 0, Orientation.Horizontal, tab.BorderStyle @@ -305,14 +305,14 @@ private void RenderTabLineCanvas () case TabSide.Top: // Upper right tee lc.AddLine ( - new Point (vts.Right, vts.Y - 1), + new (vts.Right, vts.Y - 1), 1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.Right, vts.Y - 1), + new (vts.Right, vts.Y - 1), 0, Orientation.Horizontal, tab.BorderStyle @@ -322,14 +322,14 @@ private void RenderTabLineCanvas () case TabSide.Bottom: // Lower right tee lc.AddLine ( - new Point (vts.Right, vts.Bottom), + new (vts.Right, vts.Bottom), -1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.Right, vts.Bottom), + new (vts.Right, vts.Bottom), 0, Orientation.Horizontal, tab.BorderStyle @@ -351,14 +351,14 @@ private void RenderTabLineCanvas () case TabSide.Top: //LLCorner lc.AddLine ( - new Point (vts.Right, vts.Bottom - selectedOffset), + new (vts.Right, vts.Bottom - selectedOffset), -1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.Right, vts.Bottom - selectedOffset), + new (vts.Right, vts.Bottom - selectedOffset), 1, Orientation.Horizontal, tab.BorderStyle @@ -368,14 +368,14 @@ private void RenderTabLineCanvas () case TabSide.Bottom: //URCorner lc.AddLine ( - new Point (vts.Right, vts.Y - 1), + new (vts.Right, vts.Y - 1), 1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.Right, vts.Y - 1), + new (vts.Right, vts.Y - 1), 1, Orientation.Horizontal, tab.BorderStyle @@ -401,14 +401,14 @@ private void RenderTabLineCanvas () { // ULCorner lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), + new (vts.X - 1, vts.Y - 1), 1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), + new (vts.X - 1, vts.Y - 1), 1, Orientation.Horizontal, tab.BorderStyle @@ -417,14 +417,14 @@ private void RenderTabLineCanvas () // LLCorner lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), + new (vts.X - 1, vts.Bottom), -1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), + new (vts.X - 1, vts.Bottom), 1, Orientation.Horizontal, tab.BorderStyle @@ -436,14 +436,14 @@ private void RenderTabLineCanvas () { // LLCorner lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), + new (vts.X - 1, vts.Bottom), -1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), + new (vts.X - 1, vts.Bottom), 1, Orientation.Horizontal, tab.BorderStyle @@ -452,14 +452,14 @@ private void RenderTabLineCanvas () // ULCorner lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), + new (vts.X - 1, vts.Y - 1), 1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), + new (vts.X - 1, vts.Y - 1), 1, Orientation.Horizontal, tab.BorderStyle @@ -480,14 +480,14 @@ private void RenderTabLineCanvas () { // Upper left tee lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), + new (vts.X - 1, vts.Y - 1), 1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), + new (vts.X - 1, vts.Y - 1), 0, Orientation.Horizontal, tab.BorderStyle @@ -496,14 +496,14 @@ private void RenderTabLineCanvas () // Lower left tee lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), + new (vts.X - 1, vts.Bottom), -1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), + new (vts.X - 1, vts.Bottom), 0, Orientation.Horizontal, tab.BorderStyle @@ -516,14 +516,14 @@ private void RenderTabLineCanvas () { // Upper right tee lc.AddLine ( - new Point (vts.Right, vts.Y - 1), + new (vts.Right, vts.Y - 1), 1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.Right, vts.Y - 1), + new (vts.Right, vts.Y - 1), 0, Orientation.Horizontal, tab.BorderStyle @@ -534,14 +534,14 @@ private void RenderTabLineCanvas () { // Lower right tee lc.AddLine ( - new Point (vts.Right, vts.Bottom), + new (vts.Right, vts.Bottom), -1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.Right, vts.Bottom), + new (vts.Right, vts.Bottom), 0, Orientation.Horizontal, tab.BorderStyle @@ -551,14 +551,14 @@ private void RenderTabLineCanvas () { // Upper right tee lc.AddLine ( - new Point (vts.Right, vts.Y - 1), + new (vts.Right, vts.Y - 1), 1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.Right, vts.Y - 1), + new (vts.Right, vts.Y - 1), 0, Orientation.Horizontal, tab.BorderStyle @@ -573,14 +573,14 @@ private void RenderTabLineCanvas () case TabSide.Top: // Lower left vertical line lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), + new (vts.X - 1, vts.Bottom), 0, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), + new (vts.X - 1, vts.Bottom), 1, Orientation.Horizontal, tab.BorderStyle @@ -590,14 +590,14 @@ private void RenderTabLineCanvas () case TabSide.Bottom: // Upper left vertical line lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), + new (vts.X - 1, vts.Y - 1), 0, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), + new (vts.X - 1, vts.Y - 1), 1, Orientation.Horizontal, tab.BorderStyle @@ -620,14 +620,14 @@ private void RenderTabLineCanvas () case TabSide.Top: // Lower right tee lc.AddLine ( - new Point (vts.Right, vts.Bottom), + new (vts.Right, vts.Bottom), -1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.Right, vts.Bottom), + new (vts.Right, vts.Bottom), 0, Orientation.Horizontal, tab.BorderStyle @@ -637,14 +637,14 @@ private void RenderTabLineCanvas () case TabSide.Bottom: // Upper right tee lc.AddLine ( - new Point (vts.Right, vts.Y - 1), + new (vts.Right, vts.Y - 1), 1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.Right, vts.Y - 1), + new (vts.Right, vts.Y - 1), 0, Orientation.Horizontal, tab.BorderStyle @@ -678,7 +678,7 @@ private void RenderTabLineCanvas () { case TabSide.Top: lc.AddLine ( - new Point ( + new ( vts.Right, vts.Bottom - lastSelectedTab ), @@ -690,7 +690,7 @@ private void RenderTabLineCanvas () break; case TabSide.Bottom: lc.AddLine ( - new Point (vts.Right, vts.Y - lastSelectedTab), + new (vts.Right, vts.Y - lastSelectedTab), lineLength - arrowOffset, Orientation.Horizontal, tab.BorderStyle @@ -713,7 +713,7 @@ private void RenderTabLineCanvas () { case TabSide.Top: lc.AddLine ( - new Point (vts.Right, vts.Bottom - lastSelectedTab), + new (vts.Right, vts.Bottom - lastSelectedTab), lineLength, Orientation.Horizontal, tab.BorderStyle @@ -722,7 +722,7 @@ private void RenderTabLineCanvas () break; case TabSide.Bottom: lc.AddLine ( - new Point (vts.Right, vts.Y - lastSelectedTab), + new (vts.Right, vts.Y - lastSelectedTab), lineLength, Orientation.Horizontal, tab.BorderStyle @@ -744,7 +744,7 @@ private void RenderTabLineCanvas () case TabSide.Top: // More URCorner lc.AddLine ( - new Point ( + new ( tabsBarVts.Right - 1, vts.Bottom - lastSelectedTab ), @@ -757,7 +757,7 @@ private void RenderTabLineCanvas () case TabSide.Bottom: // More LRCorner lc.AddLine ( - new Point ( + new ( tabsBarVts.Right - 1, vts.Y - lastSelectedTab ), From e6714940454a1156a6dbe4d8e7011538d124dbac Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 30 Nov 2024 19:26:50 +0000 Subject: [PATCH 13/18] Implemented almost left side feature. --- Terminal.Gui/Views/TabView/TabRow.cs | 635 ++++++++++++++++++++------ Terminal.Gui/Views/TabView/TabView.cs | 178 ++++---- Tests/UnitTests/Views/TabViewTests.cs | 543 +++++++++++++++++++++- 3 files changed, 1127 insertions(+), 229 deletions(-) diff --git a/Terminal.Gui/Views/TabView/TabRow.cs b/Terminal.Gui/Views/TabView/TabRow.cs index 931883fefc..933be48c54 100644 --- a/Terminal.Gui/Views/TabView/TabRow.cs +++ b/Terminal.Gui/Views/TabView/TabRow.cs @@ -14,11 +14,12 @@ public TabRow (TabView host) Id = "tabRow"; CanFocus = true; + // Because TabRow has focusable subviews, it must be a TabGroup TabStop = TabBehavior.TabGroup; Width = Dim.Fill (); - _rightDownScrollIndicator = new View + _rightDownScrollIndicator = new () { Id = "rightDownScrollIndicator", Width = 1, @@ -27,7 +28,7 @@ public TabRow (TabView host) }; _rightDownScrollIndicator.MouseClick += _host.Tab_MouseClick!; - _leftUpScrollIndicator = new View + _leftUpScrollIndicator = new () { Id = "leftUpScrollIndicator", Width = 1, @@ -39,7 +40,7 @@ public TabRow (TabView host) Add (_rightDownScrollIndicator, _leftUpScrollIndicator); } - /// + /// public override void EndInit () { _host._tabLocations = _host.CalculateViewport (Viewport); @@ -50,11 +51,11 @@ public override void EndInit () protected override bool OnMouseEvent (MouseEventArgs me) { View? parent = me.View is Adornment adornment ? adornment.Parent : me.View; - Tab? hit = parent as Tab; + var hit = parent as Tab; if (me.IsSingleClicked) { - _host.OnTabClicked (new TabMouseEventArgs (hit!, me)); + _host.OnTabClicked (new (hit!, me)); // user canceled click if (me.Handled) @@ -109,7 +110,7 @@ protected override bool OnMouseEvent (MouseEventArgs me) return false; } - /// + /// protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? focusedView) { if (_host.SelectedTab is { HasFocus: false, CanFocus: true } && focusedView == this) @@ -141,7 +142,7 @@ protected override void OnSubViewLayout (LayoutEventArgs args) base.OnSubViewLayout (args); } - /// + /// protected override bool OnRenderingLineCanvas () { RenderTabLineCanvas (); @@ -164,7 +165,6 @@ private void RenderTabLineCanvas () { View tab = tabLocations [i]; Rectangle vts = tab.ViewportToScreen (tab.Viewport); - int selectedOffset = _host.Style.ShowInitialLine && tabLocations [i] == _host.SelectedTab ? 0 : 1; if (tabLocations [i] == _host.SelectedTab) { @@ -177,7 +177,7 @@ private void RenderTabLineCanvas () case TabSide.Top: // Lower left vertical line lc.AddLine ( - new (vts.X - 1, vts.Bottom - selectedOffset), + new (vts.X - 1, vts.Bottom), -1, Orientation.Vertical, tab.BorderStyle @@ -195,6 +195,14 @@ private void RenderTabLineCanvas () break; case TabSide.Left: + // Upper horizontal line + lc.AddLine ( + new (vts.Right, vts.Y - 1), + -1, + Orientation.Horizontal, + tab.BorderStyle + ); + break; case TabSide.Right: break; @@ -209,14 +217,14 @@ private void RenderTabLineCanvas () case TabSide.Top: // LRCorner lc.AddLine ( - new (vts.X - 1, vts.Bottom - selectedOffset), + new (vts.X - 1, vts.Bottom), -1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new (vts.X - 1, vts.Bottom - selectedOffset), + new (vts.X - 1, vts.Bottom), -1, Orientation.Horizontal, tab.BorderStyle @@ -241,6 +249,24 @@ private void RenderTabLineCanvas () break; case TabSide.Left: + if (Frame.Bottom > tab.Frame.Bottom) + { + // LRCorner + lc.AddLine ( + new (vts.Right, vts.Bottom), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Bottom), + -1, + Orientation.Horizontal, + tab.BorderStyle + ); + } + break; case TabSide.Right: break; @@ -287,6 +313,21 @@ private void RenderTabLineCanvas () break; case TabSide.Left: + // Upper left tee + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + break; case TabSide.Right: break; @@ -337,6 +378,21 @@ private void RenderTabLineCanvas () break; case TabSide.Left: + // Upper right tee + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + break; case TabSide.Right: break; @@ -349,16 +405,16 @@ private void RenderTabLineCanvas () switch (_host.Style.TabsSide) { case TabSide.Top: - //LLCorner + //LRCorner lc.AddLine ( - new (vts.Right, vts.Bottom - selectedOffset), + new (vts.Right, vts.Bottom), -1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new (vts.Right, vts.Bottom - selectedOffset), + new (vts.Right, vts.Bottom), 1, Orientation.Horizontal, tab.BorderStyle @@ -383,6 +439,24 @@ private void RenderTabLineCanvas () break; case TabSide.Left: + if (Frame.Bottom > tab.Frame.Bottom) + { + //LRCorner + lc.AddLine ( + new (vts.Right, vts.Bottom), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Bottom), + -1, + Orientation.Horizontal, + tab.BorderStyle + ); + } + break; case TabSide.Right: break; @@ -467,6 +541,39 @@ private void RenderTabLineCanvas () break; case TabSide.Left: + if (_host.Style.ShowInitialLine) + { + // ULCorner + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + } + + // LLCorner + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + -1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + break; case TabSide.Right: break; @@ -476,93 +583,239 @@ private void RenderTabLineCanvas () } else if (i > 0) { - if (_host.Style.ShowInitialLine || _host.Style.TabsSide == TabSide.Bottom) + switch (_host.Style.TabsSide) { - // Upper left tee - lc.AddLine ( - new (vts.X - 1, vts.Y - 1), - 1, - Orientation.Vertical, - tab.BorderStyle - ); + case TabSide.Top: + case TabSide.Bottom: + if (_host.Style.ShowInitialLine || _host.Style.TabsSide == TabSide.Bottom) + { + // Upper left tee + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + 1, + Orientation.Vertical, + tab.BorderStyle + ); - lc.AddLine ( - new (vts.X - 1, vts.Y - 1), - 0, - Orientation.Horizontal, - tab.BorderStyle - ); - } + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + } + + // Lower left tee + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + -1, + Orientation.Vertical, + tab.BorderStyle + ); - // Lower left tee - lc.AddLine ( - new (vts.X - 1, vts.Bottom), - -1, - Orientation.Vertical, - tab.BorderStyle - ); - - lc.AddLine ( - new (vts.X - 1, vts.Bottom), - 0, - Orientation.Horizontal, - tab.BorderStyle - ); + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Left: + if (_host.Style.ShowInitialLine || _host.Style.TabsSide == TabSide.Right) + { + // Upper left tee + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + } + + // Lower left tee + lc.AddLine ( + new (vts.Right, vts.Y - 1), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Y - 1), + -1, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); + } } } else if (i < tabLocations.Length - 1) { if (_host.Style.ShowInitialLine) { - // Upper right tee - lc.AddLine ( - new (vts.Right, vts.Y - 1), - 1, - Orientation.Vertical, - tab.BorderStyle - ); - - lc.AddLine ( - new (vts.Right, vts.Y - 1), - 0, - Orientation.Horizontal, - tab.BorderStyle - ); + switch (_host.Style.TabsSide) + { + case TabSide.Top: + case TabSide.Bottom: + // Upper right tee + lc.AddLine ( + new (vts.Right, vts.Y - 1), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Y - 1), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Left: + // Upper right tee + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); + } } - if (_host.Style.ShowInitialLine || _host.Style.TabsSide == TabSide.Top) + if (_host.Style.ShowInitialLine) { - // Lower right tee - lc.AddLine ( - new (vts.Right, vts.Bottom), - -1, - Orientation.Vertical, - tab.BorderStyle - ); - - lc.AddLine ( - new (vts.Right, vts.Bottom), - 0, - Orientation.Horizontal, - tab.BorderStyle - ); + switch (_host.Style.TabsSide) + { + case TabSide.Top: + case TabSide.Bottom: + // Lower right tee + lc.AddLine ( + new (vts.Right, vts.Bottom), + -1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Bottom), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Left: + // Lower right tee + lc.AddLine ( + new (vts.Right, vts.Bottom), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Bottom), + -1, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); + } } else { - // Upper right tee - lc.AddLine ( - new (vts.Right, vts.Y - 1), - 1, - Orientation.Vertical, - tab.BorderStyle - ); - - lc.AddLine ( - new (vts.Right, vts.Y - 1), - 0, - Orientation.Horizontal, - tab.BorderStyle - ); + switch (_host.Style.TabsSide) + { + case TabSide.Top: + // Lower right tee + lc.AddLine ( + new (vts.Right, vts.Bottom), + -1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Bottom), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Bottom: + // Upper right tee + lc.AddLine ( + new (vts.Right, vts.Y - 1), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Y - 1), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Left: + // Lower right tee + lc.AddLine ( + new (vts.Right, vts.Bottom), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Bottom), + -1, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Right: + break; + default: + throw new ArgumentOutOfRangeException (); + } } } @@ -605,6 +858,21 @@ private void RenderTabLineCanvas () break; case TabSide.Left: + // Upper horizontal line + lc.AddLine ( + new (vts.Right, vts.Y - 1), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Y - 1), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + break; case TabSide.Right: break; @@ -652,6 +920,24 @@ private void RenderTabLineCanvas () break; case TabSide.Left: + if (Frame.Bottom > tab.Frame.Bottom) + { + // Lower right tee + lc.AddLine ( + new (vts.Right, vts.Bottom), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Bottom), + -1, + Orientation.Horizontal, + tab.BorderStyle + ); + } + break; case TabSide.Right: break; @@ -664,12 +950,26 @@ private void RenderTabLineCanvas () { var arrowOffset = 1; - int lastSelectedTab = !_host.Style.ShowInitialLine && i == selectedTab ? 1 : - _host.Style.TabsSide == TabSide.Bottom ? 1 : 0; Rectangle tabsBarVts = ViewportToScreen (Viewport); - int lineLength = tabsBarVts.Right - vts.Right; + int lineLength; + + switch (_host.Style.TabsSide) + { + case TabSide.Top: + case TabSide.Bottom: + lineLength = tabsBarVts.Right - vts.Right; + + break; + case TabSide.Left: + case TabSide.Right: + lineLength = tabsBarVts.Bottom - vts.Bottom; - // Right horizontal line + break; + default: + throw new ArgumentOutOfRangeException (); + } + + // Right horizontal/vertical line if (ShouldDrawRightDownScrollIndicator ()) { if (lineLength - arrowOffset > 0) @@ -679,9 +979,9 @@ private void RenderTabLineCanvas () case TabSide.Top: lc.AddLine ( new ( - vts.Right, - vts.Bottom - lastSelectedTab - ), + vts.Right, + vts.Bottom + ), lineLength - arrowOffset, Orientation.Horizontal, tab.BorderStyle @@ -690,7 +990,8 @@ private void RenderTabLineCanvas () break; case TabSide.Bottom: lc.AddLine ( - new (vts.Right, vts.Y - lastSelectedTab), + + new (vts.Right, vts.Y - 1), lineLength - arrowOffset, Orientation.Horizontal, tab.BorderStyle @@ -698,6 +999,16 @@ private void RenderTabLineCanvas () break; case TabSide.Left: + lc.AddLine ( + new ( + vts.Right, + vts.Bottom + ), + lineLength - arrowOffset, + Orientation.Vertical, + tab.BorderStyle + ); + break; case TabSide.Right: break; @@ -713,7 +1024,7 @@ private void RenderTabLineCanvas () { case TabSide.Top: lc.AddLine ( - new (vts.Right, vts.Bottom - lastSelectedTab), + new (vts.Right, vts.Bottom), lineLength, Orientation.Horizontal, tab.BorderStyle @@ -722,7 +1033,8 @@ private void RenderTabLineCanvas () break; case TabSide.Bottom: lc.AddLine ( - new (vts.Right, vts.Y - lastSelectedTab), + + new (vts.Right, vts.Y - 1), lineLength, Orientation.Horizontal, tab.BorderStyle @@ -730,6 +1042,65 @@ private void RenderTabLineCanvas () break; case TabSide.Left: + if (i == selectedTab) + { + if (Frame.Bottom == tab.Frame.Bottom) + { + // Lower right horizontal line + lc.AddLine ( + new (vts.Right, vts.Bottom), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + } + else + { + lc.AddLine ( + new (vts.Right, vts.Bottom), + lineLength, + Orientation.Vertical, + tab.BorderStyle + ); + } + } + else + { + if (Frame.Bottom == tab.Frame.Bottom) + { + lc.AddLine ( + new (vts.Right, vts.Bottom), + -1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Bottom), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + + } + else + { + lc.AddLine ( + new (vts.Right, vts.Bottom), + lineLength, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, tabsBarVts.Bottom), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + } + } + break; case TabSide.Right: break; @@ -745,9 +1116,9 @@ private void RenderTabLineCanvas () // More URCorner lc.AddLine ( new ( - tabsBarVts.Right - 1, - vts.Bottom - lastSelectedTab - ), + tabsBarVts.Right - 1, + vts.Bottom + ), 1, Orientation.Vertical, tab.BorderStyle @@ -758,9 +1129,9 @@ private void RenderTabLineCanvas () // More LRCorner lc.AddLine ( new ( - tabsBarVts.Right - 1, - vts.Y - lastSelectedTab - ), + tabsBarVts.Right - 1, + vts.Y - 1 + ), -1, Orientation.Vertical, tab.BorderStyle @@ -768,6 +1139,20 @@ private void RenderTabLineCanvas () break; case TabSide.Left: + if (Frame.Bottom > tab.Frame.Bottom) + { + // More URCorner + lc.AddLine ( + new ( + vts.Right, + tabsBarVts.Bottom - 1 + ), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + } + break; case TabSide.Right: break; @@ -782,31 +1167,9 @@ private void RenderTabLineCanvas () LineCanvas.Merge (lc); } - private int GetUnderlineXOrYPosition () - { - switch (_host.Style.TabsSide) - { - case TabSide.Top: - - return _host.Style.ShowInitialLine ? 2 : 1; - case TabSide.Bottom: - - return 0; - case TabSide.Left: - - return _host.Style.ShowInitialLine ? Frame.Right - 1 : Frame.Right; - case TabSide.Right: - return 0; - default: - throw new ArgumentOutOfRangeException (); - } - } - /// Renders the line of the tab that adjoins the content of the tab. private void RenderUnderline () { - int xOrY = GetUnderlineXOrYPosition (); - Tab? selected = _host._tabLocations?.FirstOrDefault (t => t == _host.SelectedTab); if (selected is null) @@ -841,16 +1204,21 @@ private void RenderUnderline () switch (_host.Style.TabsSide) { case TabSide.Top: + _leftUpScrollIndicator.X = 0; + _leftUpScrollIndicator.Y = Pos.AnchorEnd (1); + + break; case TabSide.Bottom: _leftUpScrollIndicator.X = 0; - _leftUpScrollIndicator.Y = xOrY; + _leftUpScrollIndicator.Y = 0; break; case TabSide.Left: - case TabSide.Right: - _leftUpScrollIndicator.X = xOrY; + _leftUpScrollIndicator.X = Pos.AnchorEnd (1); _leftUpScrollIndicator.Y = 0; + break; + case TabSide.Right: break; default: throw new ArgumentOutOfRangeException (); @@ -873,16 +1241,17 @@ private void RenderUnderline () switch (_host.Style.TabsSide) { case TabSide.Top: + case TabSide.Left: + _rightDownScrollIndicator.X = Pos.AnchorEnd (1); + _rightDownScrollIndicator.Y = Pos.AnchorEnd (1); + + break; case TabSide.Bottom: - _rightDownScrollIndicator.X = Viewport.Width - 1; - _rightDownScrollIndicator.Y = xOrY; + _rightDownScrollIndicator.X = Pos.AnchorEnd (1); + _rightDownScrollIndicator.Y = 0; break; - case TabSide.Left: case TabSide.Right: - _rightDownScrollIndicator.X = xOrY; - _rightDownScrollIndicator.Y = Viewport.Height - 1; - break; default: throw new ArgumentOutOfRangeException (); diff --git a/Terminal.Gui/Views/TabView/TabView.cs b/Terminal.Gui/Views/TabView/TabView.cs index f6d9e2d3b5..250591b98b 100644 --- a/Terminal.Gui/Views/TabView/TabView.cs +++ b/Terminal.Gui/Views/TabView/TabView.cs @@ -37,45 +37,53 @@ public TabView () base.Add (_containerView); // Things this view knows how to do - AddCommand (Command.Left, () => - { - if (Style.TabsSide is TabSide.Top or TabSide.Bottom) - { - return SwitchTabBy (-1); - } - - return false; - }); - - AddCommand (Command.Right, () => - { - if (Style.TabsSide is TabSide.Top or TabSide.Bottom) - { - return SwitchTabBy (1); - } - - return false; - }); - - AddCommand (Command.Up, () => - { - if (Style.TabsSide is TabSide.Left or TabSide.Right) - { - return SwitchTabBy (-1); - } - - return false; - }); - - AddCommand (Command.Down, () => - { - if (Style.TabsSide is TabSide.Left or TabSide.Right) - { - return SwitchTabBy (1); - } - - return false; - }); + AddCommand ( + Command.Left, + () => + { + if (Style.TabsSide is TabSide.Top or TabSide.Bottom) + { + return SwitchTabBy (-1); + } + + return false; + }); + + AddCommand ( + Command.Right, + () => + { + if (Style.TabsSide is TabSide.Top or TabSide.Bottom) + { + return SwitchTabBy (1); + } + + return false; + }); + + AddCommand ( + Command.Up, + () => + { + if (Style.TabsSide is TabSide.Left or TabSide.Right) + { + return SwitchTabBy (-1); + } + + return false; + }); + + AddCommand ( + Command.Down, + () => + { + if (Style.TabsSide is TabSide.Left or TabSide.Right) + { + return SwitchTabBy (1); + } + + return false; + }); AddCommand ( Command.LeftStart, @@ -249,6 +257,7 @@ public Tab? SelectedTab if (_selectedTab.View is { }) { _selectedTab.View.CanFocusChanged -= ContainerViewCanFocus!; + // remove old content _containerView.Remove (_selectedTab.View); } @@ -276,6 +285,7 @@ public Tab? SelectedTab OnSelectedTabChanged (old!, _selectedTab!); } + SetNeedsLayout (); } } @@ -303,6 +313,7 @@ public TabStyle Style { return; } + _style = value; SetNeedsLayout (); } @@ -376,6 +387,7 @@ public void ApplyStyleChanges () _tabsBar.Height = GetTabHeight (true); _containerView.X = 0; + //move content down to make space for tabs _containerView.Y = Pos.Bottom (_tabsBar); _containerView.Width = Dim.Fill (); @@ -397,6 +409,7 @@ public void ApplyStyleChanges () _containerView.X = 0; _containerView.Y = 0; _containerView.Width = Dim.Fill (); + // Fill client area leaving space at bottom for tabs _containerView.Height = Dim.Fill (tabHeight); @@ -417,6 +430,7 @@ public void ApplyStyleChanges () //move content right to make space for tabs _containerView.X = Pos.Right (_tabsBar); _containerView.Y = 0; + // Fill client area leaving space at left for tabs _containerView.Width = Dim.Fill (); _containerView.Height = Dim.Fill (); @@ -431,7 +445,7 @@ public void ApplyStyleChanges () SetNeedsLayout (); } - /// + /// protected override void OnViewportChanged (DrawEventArgs e) { _tabLocations = CalculateViewport (Viewport); @@ -466,7 +480,7 @@ public void EnsureSelectedTabIsVisible () /// 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? focusedView) { if (SelectedTab is { HasFocus: false } && !_containerView.CanFocus && focusedView == this) @@ -545,6 +559,7 @@ public bool SwitchTabBy (int amount) if (currentIdx == -1) { SelectedTab = Tabs.ElementAt (0); + return true; } @@ -587,10 +602,7 @@ protected override void Dispose (bool disposing) } /// Raises the event. - protected virtual void OnSelectedTabChanged (Tab oldTab, Tab newTab) - { - SelectedTabChanged?.Invoke (this, new TabChangedEventArgs (oldTab, newTab)); - } + protected virtual void OnSelectedTabChanged (Tab oldTab, Tab newTab) { SelectedTabChanged?.Invoke (this, new (oldTab, newTab)); } /// Returns which tabs to render at each x location. /// @@ -681,7 +693,7 @@ protected virtual void OnSelectedTabChanged (Tab oldTab, Tab newTab) break; case TabSide.Left: case TabSide.Right: - int maxColWidth = 0; + var maxColWidth = 0; // Starting at the first or scrolled to tab foreach (Tab tab in Tabs.Skip (TabScrollOffset)) @@ -705,16 +717,18 @@ protected virtual void OnSelectedTabChanged (Tab oldTab, Tab newTab) // or we won't even be able to render a single tab! long maxWidth = Math.Max (0, Math.Min (bounds.Width - (Style.ShowInitialLine ? 2 : 1), MaxTabTextWidth)); + maxColWidth = GetMaxColWidth (Math.Min (tabTextWidth, (int)maxWidth)); + // The maximum height to use for the tab. But not more than the height of the view // or we won't even be able to render a single tab! - int maxHeight = Math.Max (0, Math.Min (bounds.Height - 3, 3)); + int maxHeight = Math.Max (0, Math.Min (bounds.Height - 2, 2)); tab.Height = 2; // if tab view is height <= 3 don't render any tabs if (maxHeight == 0) { - tab.Width = maxColWidth = Math.Max (Style.ShowInitialLine ? 3 : 2, maxColWidth); + tab.Width = maxColWidth; tab.Visible = true; tab.MouseClick += Tab_MouseClick!; tab.Border!.MouseClick += Tab_MouseClick!; @@ -735,7 +749,7 @@ protected virtual void OnSelectedTabChanged (Tab oldTab, Tab newTab) tab.Text = tab.DisplayText; } - maxColWidth = Math.Max (tabTextWidth + 2, maxColWidth); + maxColWidth = GetMaxColWidth (tabTextWidth); tab.Height = 3; // if there is not enough space for this tab @@ -763,8 +777,21 @@ protected virtual void OnSelectedTabChanged (Tab oldTab, Tab newTab) { t.Width = maxColWidth; } + _tabsBar.Width = maxColWidth; + int GetMaxColWidth (int textWidth) + { + int maxViewportWidth = Math.Max (0, Viewport.Width - (Style.ShowBorder ? 2 : 0)); + + if (Math.Max (textWidth + (Style.ShowInitialLine ? 2 : 1), maxColWidth) > maxViewportWidth) + { + return maxViewportWidth; + } + + return Math.Max (textWidth + (Style.ShowInitialLine ? 2 : 1), maxColWidth); + } + break; default: throw new ArgumentOutOfRangeException (); @@ -786,10 +813,7 @@ protected virtual void OnSelectedTabChanged (Tab oldTab, Tab newTab) return tabs.Count == 0 ? null : tabs.ToArray (); } - private void Tab_DisplayTextChanged (object? sender, EventArgs e) - { - _tabLocations = CalculateViewport (Viewport); - } + private void Tab_DisplayTextChanged (object? sender, EventArgs e) { _tabLocations = CalculateViewport (Viewport); } /// Renders the line with the tab names in it. private void RenderTabLine (Tab []? tabLocations) @@ -799,7 +823,6 @@ private void RenderTabLine (Tab []? tabLocations) return; } - View? selected = null; int topLine = Style.ShowInitialLine ? 1 : 0; foreach (Tab toRender in tabLocations) @@ -808,13 +831,11 @@ private void RenderTabLine (Tab []? tabLocations) if (toRender == SelectedTab) { - selected = tab; - switch (Style.TabsSide) { case TabSide.Top: tab.Border!.Thickness = new (1, topLine, 1, 0); - tab.Margin!.Thickness = new (0, 0, 0, topLine); + tab.Margin!.Thickness = new (0, 0, 0, 1); break; case TabSide.Bottom: @@ -824,32 +845,7 @@ private void RenderTabLine (Tab []? tabLocations) break; case TabSide.Left: tab.Border!.Thickness = new (topLine, 1, 0, 1); - tab.Margin!.Thickness = new (0, 0, topLine, 0); - - break; - case TabSide.Right: - break; - default: - throw new ArgumentOutOfRangeException (); - } - } - else if (selected is null) - { - switch (Style.TabsSide) - { - case TabSide.Top: - tab.Border!.Thickness = new (1, topLine, 1, 1); - tab.Margin!.Thickness = new (0, 0, 0, 0); - - break; - case TabSide.Bottom: - tab.Border!.Thickness = new (1, 1, 1, topLine); - tab.Margin!.Thickness = new (0, 0, 0, 0); - - break; - case TabSide.Left: - tab.Border!.Thickness = new (topLine, 1, 1, 1); - tab.Margin!.Thickness = new (0, 0, 0, 0); + tab.Margin!.Thickness = new (0, 0, 1, 0); break; case TabSide.Right: @@ -860,21 +856,20 @@ private void RenderTabLine (Tab []? tabLocations) } else { + tab.Margin!.Thickness = new (0, 0, 0, 0); + switch (Style.TabsSide) { case TabSide.Top: tab.Border!.Thickness = new (1, topLine, 1, 1); - tab.Margin!.Thickness = new (0, 0, 0, 0); break; case TabSide.Bottom: tab.Border!.Thickness = new (1, 1, 1, topLine); - tab.Margin!.Thickness = new (0, 0, 0, 0); break; case TabSide.Left: tab.Border!.Thickness = new (topLine, 1, 1, 1); - tab.Margin!.Thickness = new (0, 0, 0, 0); break; case TabSide.Right: @@ -912,17 +907,14 @@ private int GetTabHeight (bool top) return Style.ShowInitialLine ? 3 : 2; } - internal void Tab_MouseClick (object sender, MouseEventArgs e) - { - e.Handled = _tabsBar.NewMouseEvent (e) == true; - } + internal void Tab_MouseClick (object sender, MouseEventArgs e) { e.Handled = _tabsBar.NewMouseEvent (e) == true; } private void UnSetCurrentTabs () { if (_tabLocations is null) { // Ensures unset any visible tab prior to TabScrollOffset - for (int i = 0; i < TabScrollOffset; i++) + for (var i = 0; i < TabScrollOffset; i++) { Tab tab = Tabs.ElementAt (i); @@ -952,6 +944,4 @@ private void UnSetCurrentTabs () /// Raises the event. /// internal virtual void OnTabClicked (TabMouseEventArgs tabMouseEventArgs) { TabClicked?.Invoke (this, tabMouseEventArgs); } - - -} \ No newline at end of file +} diff --git a/Tests/UnitTests/Views/TabViewTests.cs b/Tests/UnitTests/Views/TabViewTests.cs index 594178e1e4..e3f5d65cdd 100644 --- a/Tests/UnitTests/Views/TabViewTests.cs +++ b/Tests/UnitTests/Views/TabViewTests.cs @@ -1237,6 +1237,545 @@ public void ShowInitialLine_True_TabsSide_Bottom_With_Unicode () ); } + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Left_TestTabView_Height3 () + { + TabView tv = GetTabView (out _, out _); + tv.Width = 5; + tv.Height = 3; + tv.Style = new () { TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭───┐ +│T h│ +╰─▼─┘", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Left_TestTabView_Height4 () + { + TabView tv = GetTabView (out _, out _); + tv.Width = 5; + tv.Height = 4; + tv.Style = new () { TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭───┐ +│T h│ +╰─╮ │ + ▼─┘", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Left_TestTabView_Height5_One_Tab () + { + TabView tv = GetTabView (out _, out Tab tab2); + tv.RemoveTab (tab2); + tv.Width = 5; + tv.Height = 5; + tv.Style = new () { TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭───┐ +│T h│ +╰─╮ │ + │ │ + ╰─┘", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Left_TestTabView_Height6_Two_Tab () + { + TabView tv = GetTabView (out _, out Tab tab2); + tv.Width = 5; + tv.Height = 6; + tv.Style = new () { TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭───┐ +│T h│ +├─╮ │ +│T│ │ +╰─┤ │ + ╰─┘", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭─┬─┐ +│T│h│ +├─╯ │ +│T │ +╰─╮ │ + ╰─┘", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Left_TestTabView_Height7_Two_Tab () + { + TabView tv = GetTabView (out _, out Tab tab2); + tv.Width = 5; + tv.Height = 7; + tv.Style = new () { TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭───┐ +│T h│ +├─╮ │ +│T│ │ +╰─┤ │ + │ │ + ╰─┘", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭─┬─┐ +│T│h│ +├─╯ │ +│T │ +╰─╮ │ + │ │ + ╰─┘", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Left_TestThinTabView_WithLongNames () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + tv.Width = 10; + tv.Height = 5; + tv.Style = new () { TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); + tv.Layout (); + + // Test two tab names that fit + tab1.DisplayText = "12"; + tab2.DisplayText = "13"; + + tv.Layout (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭────────┐ +│12 hi │ +├──╮ │ +│13│ │ +╰──┴─────┘", + output + ); + + // Test first tab name too long + tab1.DisplayText = "12345678910"; + tab2.DisplayText = "13"; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭────────┐ +│123456 h│ +├──────╮ │ +│13 │ │ +╰──────┴─┘", + output + ); + + //switch to tab2 + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭──────┬─┐ +│123456│h│ +├──────╯ │ +│13 │ +╰────────┘", + output + ); + + // now make both tabs too long + tab1.DisplayText = "12345678910"; + tab2.DisplayText = "abcdefghijklmnopq"; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭──────┬─┐ +│123456│h│ +├──────╯ │ +│abcdef │ +╰────────┘", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Left_With_Unicode () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + tv.Width = 20; + tv.Height = 5; + tv.Style = new () { TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); + + tab1.DisplayText = "Tab0"; + + tab2.DisplayText = "Les Mise" + char.ConvertFromUtf32 (int.Parse ("0301", NumberStyles.HexNumber)) + "rables"; + + tv.Layout (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭──────────────────┐ +│Tab0 hi │ +├──────────────╮ │ +│Les Misérables│ │ +╰──────────────┴───┘", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭──────────────┬───┐ +│Tab0 │hi2│ +├──────────────╯ │ +│Les Misérables │ +╰──────────────────┘", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_False_TabsSide_Left_TestTabView_Height3 () + { + TabView tv = GetTabView (out _, out _); + tv.Width = 5; + tv.Height = 3; + tv.Style = new () { ShowInitialLine = false, TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +────┐ +Ta h│ +──▼─┘", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_False_TabsSide_Left_TestTabView_Height4 () + { + TabView tv = GetTabView (out _, out _); + tv.Width = 5; + tv.Height = 4; + tv.Style = new () { ShowInitialLine = false, TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +────┐ +Ta h│ +──╮ │ + ▼─┘", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_False_TabsSide_Left_TestTabView_Height5_One_Tab () + { + TabView tv = GetTabView (out _, out Tab tab2); + tv.RemoveTab (tab2); + tv.Width = 5; + tv.Height = 5; + tv.Style = new () { TabsSide = TabSide.Left, ShowInitialLine = false }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +────┐ +Ta h│ +──╮ │ + │ │ + ╰─┘", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_False_TabsSide_Left_TestTabView_Height6_Two_Tab () + { + TabView tv = GetTabView (out _, out Tab tab2); + tv.Width = 5; + tv.Height = 6; + tv.Style = new () { TabsSide = TabSide.Left, ShowInitialLine = false }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +────┐ +Ta h│ +──╮ │ +Ta│ │ +──┤ │ + ╰─┘", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +──┬─┐ +Ta│h│ +──╯ │ +Ta │ +──╮ │ + ╰─┘", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_False_TabsSide_Left_TestTabView_Height7_Two_Tab () + { + TabView tv = GetTabView (out _, out Tab tab2); + tv.Width = 5; + tv.Height = 7; + tv.Style = new () { TabsSide = TabSide.Left, ShowInitialLine = false }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +────┐ +Ta h│ +──╮ │ +Ta│ │ +──┤ │ + │ │ + ╰─┘", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +──┬─┐ +Ta│h│ +──╯ │ +Ta │ +──╮ │ + │ │ + ╰─┘", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_False_TabsSide_Left_TestThinTabView_WithLongNames () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + tv.Width = 10; + tv.Height = 5; + tv.Style = new () { ShowInitialLine = false, TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); + tv.Layout (); + + // Test two tab names that fit + tab1.DisplayText = "12"; + tab2.DisplayText = "13"; + + tv.Layout (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +─────────┐ +12 hi │ +──╮ │ +13│ │ +──┴──────┘", + output + ); + + tv.SelectedTab = tab2; + Assert.Equal (tab2, tv.Subviews.First (v => v.Id.Contains ("tabRow")).MostFocused); + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +──┬──────┐ +12│hi2 │ +──╯ │ +13 │ +─────────┘", + output + ); + + tv.SelectedTab = tab1; + + // Test first tab name too long + tab1.DisplayText = "12345678910"; + tab2.DisplayText = "13"; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +─────────┐ +1234567 h│ +───────╮ │ +13 │ │ +───────┴─┘", + output + ); + + //switch to tab2 + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +───────┬─┐ +1234567│h│ +───────╯ │ +13 │ +─────────┘", + output + ); + + // now make both tabs too long + tab1.DisplayText = "12345678910"; + tab2.DisplayText = "abcdefghijklmnopq"; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +───────┬─┐ +1234567│h│ +───────╯ │ +abcdefg │ +─────────┘", + output + ); + } + [Fact] public void SwitchTabBy_NormalUsage () { @@ -1306,7 +1845,7 @@ public void RemoveTab_ThatHasFocus () [Fact] [SetupFakeDriver] - public void Add_Three_TabsOnTop_ChangesTab () + public void Add_Three_TabsSide_Top_ChangesTab () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); Tab tab3; @@ -1371,7 +1910,7 @@ public void Add_Three_TabsOnTop_ChangesTab () [Fact] [SetupFakeDriver] - public void Add_Three_TabsOnBottom_ChangesTab () + public void Add_Three_TabsSide_Bottom_ChangesTab () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); Tab tab3; From a8fbfa13b81d691f2d76b0127486c8873414e6db Mon Sep 17 00:00:00 2001 From: BDisp Date: Sun, 1 Dec 2024 12:16:43 +0000 Subject: [PATCH 14/18] Implemented right side feature. --- Terminal.Gui/Views/TabView/TabRow.cs | 259 ++++- Terminal.Gui/Views/TabView/TabView.cs | 28 +- Tests/UnitTests/Views/TabViewTests.cs | 1389 +++++++++++++++++++++++-- 3 files changed, 1606 insertions(+), 70 deletions(-) diff --git a/Terminal.Gui/Views/TabView/TabRow.cs b/Terminal.Gui/Views/TabView/TabRow.cs index 933be48c54..d67e62b74e 100644 --- a/Terminal.Gui/Views/TabView/TabRow.cs +++ b/Terminal.Gui/Views/TabView/TabRow.cs @@ -205,6 +205,14 @@ private void RenderTabLineCanvas () break; case TabSide.Right: + // Upper horizontal line + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + -1, + Orientation.Horizontal, + tab.BorderStyle + ); + break; default: throw new ArgumentOutOfRangeException (); @@ -269,6 +277,24 @@ private void RenderTabLineCanvas () break; case TabSide.Right: + if (Frame.Bottom > tab.Frame.Bottom) + { + // LRCorner + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + } + break; default: throw new ArgumentOutOfRangeException (); @@ -330,6 +356,21 @@ private void RenderTabLineCanvas () break; case TabSide.Right: + // Upper left tee + lc.AddLine ( + new (vts.Right, vts.Y - 1), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Y - 1), + -1, + Orientation.Horizontal, + tab.BorderStyle + ); + break; default: throw new ArgumentOutOfRangeException (); @@ -395,6 +436,21 @@ private void RenderTabLineCanvas () break; case TabSide.Right: + // Upper right tee + lc.AddLine ( + new (vts.Right, vts.Bottom), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Bottom), + -1, + Orientation.Horizontal, + tab.BorderStyle + ); + break; default: throw new ArgumentOutOfRangeException (); @@ -459,6 +515,24 @@ private void RenderTabLineCanvas () break; case TabSide.Right: + if (Frame.Bottom > tab.Frame.Bottom) + { + //LRCorner + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + } + break; default: throw new ArgumentOutOfRangeException (); @@ -657,6 +731,39 @@ private void RenderTabLineCanvas () break; case TabSide.Right: + if (_host.Style.ShowInitialLine || _host.Style.TabsSide == TabSide.Right) + { + // Upper left tee + lc.AddLine ( + new (vts.Right, vts.Y - 1), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Y - 1), + -1, + Orientation.Horizontal, + tab.BorderStyle + ); + } + + // Lower left tee + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + break; default: throw new ArgumentOutOfRangeException (); @@ -705,6 +812,21 @@ private void RenderTabLineCanvas () break; case TabSide.Right: + // Upper right tee + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + break; default: throw new ArgumentOutOfRangeException (); @@ -751,6 +873,21 @@ private void RenderTabLineCanvas () break; case TabSide.Right: + // Lower right tee + lc.AddLine ( + new (vts.Right, vts.Bottom), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Bottom), + -1, + Orientation.Horizontal, + tab.BorderStyle + ); + break; default: throw new ArgumentOutOfRangeException (); @@ -875,6 +1012,21 @@ private void RenderTabLineCanvas () break; case TabSide.Right: + // Upper horizontal line + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + break; default: throw new ArgumentOutOfRangeException (); @@ -940,6 +1092,24 @@ private void RenderTabLineCanvas () break; case TabSide.Right: + if (Frame.Bottom > tab.Frame.Bottom) + { + // Lower right tee + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + } + break; default: throw new ArgumentOutOfRangeException (); @@ -1011,6 +1181,16 @@ private void RenderTabLineCanvas () break; case TabSide.Right: + lc.AddLine ( + new ( + vts.X - 1, + vts.Bottom + ), + lineLength - arrowOffset, + Orientation.Vertical, + tab.BorderStyle + ); + break; default: throw new ArgumentOutOfRangeException (); @@ -1103,6 +1283,65 @@ private void RenderTabLineCanvas () break; case TabSide.Right: + if (i == selectedTab) + { + if (Frame.Bottom == tab.Frame.Bottom) + { + // Lower right horizontal line + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + } + else + { + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + lineLength, + Orientation.Vertical, + tab.BorderStyle + ); + } + } + else + { + if (Frame.Bottom == tab.Frame.Bottom) + { + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + -1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + + } + else + { + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + lineLength, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.X - 1, tabsBarVts.Bottom), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + } + } + break; default: throw new ArgumentOutOfRangeException (); @@ -1155,6 +1394,20 @@ private void RenderTabLineCanvas () break; case TabSide.Right: + if (Frame.Bottom > tab.Frame.Bottom) + { + // More URCorner + lc.AddLine ( + new ( + vts.X - 1, + tabsBarVts.Bottom - 1 + ), + -1, + Orientation.Horizontal, + tab.BorderStyle + ); + } + break; default: throw new ArgumentOutOfRangeException (); @@ -1209,6 +1462,7 @@ private void RenderUnderline () break; case TabSide.Bottom: + case TabSide.Right: _leftUpScrollIndicator.X = 0; _leftUpScrollIndicator.Y = 0; @@ -1217,8 +1471,6 @@ private void RenderUnderline () _leftUpScrollIndicator.X = Pos.AnchorEnd (1); _leftUpScrollIndicator.Y = 0; - break; - case TabSide.Right: break; default: throw new ArgumentOutOfRangeException (); @@ -1252,6 +1504,9 @@ private void RenderUnderline () break; case TabSide.Right: + _rightDownScrollIndicator.X = 0; + _rightDownScrollIndicator.Y = Pos.AnchorEnd (1); + break; default: throw new ArgumentOutOfRangeException (); diff --git a/Terminal.Gui/Views/TabView/TabView.cs b/Terminal.Gui/Views/TabView/TabView.cs index 250591b98b..d17e7268a4 100644 --- a/Terminal.Gui/Views/TabView/TabView.cs +++ b/Terminal.Gui/Views/TabView/TabView.cs @@ -370,7 +370,6 @@ public void ApplyStyleChanges () _tabLocations = CalculateViewport (Viewport); _containerView.BorderStyle = Style.ShowBorder ? LineStyle.Single : LineStyle.None; - _containerView.Width = Dim.Fill (); switch (Style.TabsSide) { @@ -437,6 +436,21 @@ public void ApplyStyleChanges () break; case TabSide.Right: + // Tabs are along the right + if (Style.ShowBorder) + { + _containerView.Border!.Thickness = new (1, 1, 0, 1); + } + + _tabsBar.Y = 0; + _tabsBar.Height = Dim.Fill (); + + //move content left to make space for tabs + _containerView.X = 0; + _containerView.Y = 0; + + _containerView.Height = Dim.Fill (); + break; default: throw new ArgumentOutOfRangeException (); @@ -780,6 +794,13 @@ protected override void Dispose (bool disposing) _tabsBar.Width = maxColWidth; + if (Style.TabsSide == TabSide.Right) + { + _tabsBar.X = Pos.AnchorEnd (maxColWidth); + // Fill client area leaving space at right for tabs + _containerView.Width = Dim.Fill (maxColWidth); + } + int GetMaxColWidth (int textWidth) { int maxViewportWidth = Math.Max (0, Viewport.Width - (Style.ShowBorder ? 2 : 0)); @@ -849,6 +870,9 @@ private void RenderTabLine (Tab []? tabLocations) break; case TabSide.Right: + tab.Border!.Thickness = new (0, 1, topLine, 1); + tab.Margin!.Thickness = new (1, 0, 0, 0); + break; default: throw new ArgumentOutOfRangeException (); @@ -873,6 +897,8 @@ private void RenderTabLine (Tab []? tabLocations) break; case TabSide.Right: + tab.Border!.Thickness = new (1, 1, topLine, 1); + break; default: throw new ArgumentOutOfRangeException (); diff --git a/Tests/UnitTests/Views/TabViewTests.cs b/Tests/UnitTests/Views/TabViewTests.cs index e3f5d65cdd..ee58137e3c 100644 --- a/Tests/UnitTests/Views/TabViewTests.cs +++ b/Tests/UnitTests/Views/TabViewTests.cs @@ -1776,76 +1776,1238 @@ 1234567 h│ ); } + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Right_TestTabView_Height3 () + { + TabView tv = GetTabView (out _, out _); + tv.Width = 5; + tv.Height = 3; + tv.Style = new () { TabsSide = TabSide.Right }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌───╮ +│h T│ +└─▼─╯", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Right_TestTabView_Height4 () + { + TabView tv = GetTabView (out _, out _); + tv.Width = 5; + tv.Height = 4; + tv.Style = new () { TabsSide = TabSide.Right }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌───╮ +│h T│ +│ ╭─╯ +└─▼ ", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Right_TestTabView_Height5_One_Tab () + { + TabView tv = GetTabView (out _, out Tab tab2); + tv.RemoveTab (tab2); + tv.Width = 5; + tv.Height = 5; + tv.Style = new () { TabsSide = TabSide.Right }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌───╮ +│h T│ +│ ╭─╯ +│ │ +└─╯ ", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Right_TestTabView_Height6_Two_Tab () + { + TabView tv = GetTabView (out _, out Tab tab2); + tv.Width = 5; + tv.Height = 6; + tv.Style = new () { TabsSide = TabSide.Right }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌───╮ +│h T│ +│ ╭─┤ +│ │T│ +│ ├─╯ +└─╯ ", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌─┬─╮ +│h│T│ +│ ╰─┤ +│ T│ +│ ╭─╯ +└─╯ ", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Right_TestTabView_Height7_Two_Tab () + { + TabView tv = GetTabView (out _, out Tab tab2); + tv.Width = 5; + tv.Height = 7; + tv.Style = new () { TabsSide = TabSide.Right }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌───╮ +│h T│ +│ ╭─┤ +│ │T│ +│ ├─╯ +│ │ +└─╯ ", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌─┬─╮ +│h│T│ +│ ╰─┤ +│ T│ +│ ╭─╯ +│ │ +└─╯ ", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Right_TestThinTabView_WithLongNames () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + tv.Width = 10; + tv.Height = 5; + tv.Style = new () { TabsSide = TabSide.Right }; + tv.ApplyStyleChanges (); + tv.Layout (); + + // Test two tab names that fit + tab1.DisplayText = "12"; + tab2.DisplayText = "13"; + + tv.Layout (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌────────╮ +│hi 12│ +│ ╭──┤ +│ │13│ +└─────┴──╯", + output + ); + + // Test first tab name too long + tab1.DisplayText = "12345678910"; + tab2.DisplayText = "13"; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌────────╮ +│h 123456│ +│ ╭──────┤ +│ │13 │ +└─┴──────╯", + output + ); + + //switch to tab2 + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌─┬──────╮ +│h│123456│ +│ ╰──────┤ +│ 13 │ +└────────╯", + output + ); + + // now make both tabs too long + tab1.DisplayText = "12345678910"; + tab2.DisplayText = "abcdefghijklmnopq"; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌─┬──────╮ +│h│123456│ +│ ╰──────┤ +│ abcdef│ +└────────╯", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Right_With_Unicode () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + tv.Width = 20; + tv.Height = 5; + tv.Style = new () { TabsSide = TabSide.Right }; + tv.ApplyStyleChanges (); + + tab1.DisplayText = "Tab0"; + + tab2.DisplayText = "Les Mise" + char.ConvertFromUtf32 (int.Parse ("0301", NumberStyles.HexNumber)) + "rables"; + + tv.Layout (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──────────────────╮ +│hi Tab0 │ +│ ╭──────────────┤ +│ │Les Misérables│ +└───┴──────────────╯", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌───┬──────────────╮ +│hi2│Tab0 │ +│ ╰──────────────┤ +│ Les Misérables│ +└──────────────────╯", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_False_TabsSide_Right_TestTabView_Height3 () + { + TabView tv = GetTabView (out _, out _); + tv.Width = 5; + tv.Height = 3; + tv.Style = new () { ShowInitialLine = false, TabsSide = TabSide.Right }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──── +│h Ta +└─▼──", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_False_TabsSide_Right_TestTabView_Height4 () + { + TabView tv = GetTabView (out _, out _); + tv.Width = 5; + tv.Height = 4; + tv.Style = new () { ShowInitialLine = false, TabsSide = TabSide.Right }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──── +│h Ta +│ ╭── +└─▼ ", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_False_TabsSide_Right_TestTabView_Height5_One_Tab () + { + TabView tv = GetTabView (out _, out Tab tab2); + tv.RemoveTab (tab2); + tv.Width = 5; + tv.Height = 5; + tv.Style = new () { TabsSide = TabSide.Right, ShowInitialLine = false }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──── +│h Ta +│ ╭── +│ │ +└─╯ ", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_False_TabsSide_Right_TestTabView_Height6_Two_Tab () + { + TabView tv = GetTabView (out _, out Tab tab2); + tv.Width = 5; + tv.Height = 6; + tv.Style = new () { TabsSide = TabSide.Right, ShowInitialLine = false }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──── +│h Ta +│ ╭── +│ │Ta +│ ├── +└─╯ ", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌─┬── +│h│Ta +│ ╰── +│ Ta +│ ╭── +└─╯ ", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_False_TabsSide_Right_TestTabView_Height7_Two_Tab () + { + TabView tv = GetTabView (out _, out Tab tab2); + tv.Width = 5; + tv.Height = 7; + tv.Style = new () { TabsSide = TabSide.Right, ShowInitialLine = false }; + tv.ApplyStyleChanges (); + tv.Layout (); + + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──── +│h Ta +│ ╭── +│ │Ta +│ ├── +│ │ +└─╯ ", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌─┬── +│h│Ta +│ ╰── +│ Ta +│ ╭── +│ │ +└─╯ ", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void ShowInitialLine_False_TabsSide_Right_TestThinTabView_WithLongNames () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + tv.Width = 10; + tv.Height = 5; + tv.Style = new () { ShowInitialLine = false, TabsSide = TabSide.Right }; + tv.ApplyStyleChanges (); + tv.Layout (); + + // Test two tab names that fit + tab1.DisplayText = "12"; + tab2.DisplayText = "13"; + + tv.Layout (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌───────── +│hi 12 +│ ╭── +│ │13 +└──────┴──", + output + ); + + tv.SelectedTab = tab2; + Assert.Equal (tab2, tv.Subviews.First (v => v.Id.Contains ("tabRow")).MostFocused); + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──────┬── +│hi2 │12 +│ ╰── +│ 13 +└─────────", + output + ); + + tv.SelectedTab = tab1; + + // Test first tab name too long + tab1.DisplayText = "12345678910"; + tab2.DisplayText = "13"; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌───────── +│h 1234567 +│ ╭─────── +│ │13 +└─┴───────", + output + ); + + //switch to tab2 + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌─┬─────── +│h│1234567 +│ ╰─────── +│ 13 +└─────────", + output + ); + + // now make both tabs too long + tab1.DisplayText = "12345678910"; + tab2.DisplayText = "abcdefghijklmnopq"; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌─┬─────── +│h│1234567 +│ ╰─────── +│ abcdefg +└─────────", + output + ); + } + [Fact] public void SwitchTabBy_NormalUsage () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); - + + Tab tab3; + Tab tab4; + Tab tab5; + + tv.AddTab (tab3 = new (), false); + tv.AddTab (tab4 = new (), false); + tv.AddTab (tab5 = new (), false); + + tv.SelectedTab = tab1; + + var called = 0; + tv.SelectedTabChanged += (s, e) => { called++; }; + + tv.SwitchTabBy (1); + + Assert.Equal (1, called); + Assert.Equal (tab2, tv.SelectedTab); + + //reset called counter + called = 0; + + // go right 2 + tv.SwitchTabBy (2); + + // even though we go right 2 indexes the event should only be called once + Assert.Equal (1, called); + Assert.Equal (tab4, tv.SelectedTab); + } + + [Fact] + public void SwitchTabBy_OutOfTabsRange () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + + tv.SelectedTab = tab1; + tv.SwitchTabBy (500); + + Assert.Equal (tab2, tv.SelectedTab); + + tv.SwitchTabBy (-500); + + Assert.Equal (tab1, tv.SelectedTab); + } + + [Fact] + public void RemoveTab_ThatHasFocus () + { + TabView tv = GetTabView (out Tab _, out Tab tab2); + + tv.SelectedTab = tab2; + tab2.HasFocus = true; + + Assert.Equal (2, tv.Tabs.Count); + + foreach (Tab t in tv.Tabs.ToArray ()) + { + tv.RemoveTab (t); + } + + Assert.Empty (tv.Tabs); + } + + [Fact] + [SetupFakeDriver] + public void Add_Three_TabsSide_Top_ShowInitialLine_True_ChangesTab () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + Tab tab3; + + tv.AddTab ( + tab3 = new () { Id = "tab3", DisplayText = "Tab3", View = new TextView { Id = "tab3.TextView", Width = 3, Height = 1, Text = "hi3" } }, + false); + + tv.Width = 20; + tv.Height = 5; + + tv.Layout (); + tv.Draw (); + + Assert.Equal (tab1, tv.SelectedTab); + + TestHelpers.AssertDriverContentsAre ( + @" +╭────┬────┬────╮ +│Tab1│Tab2│Tab3│ +│ ╰────┴────┴───╮ +│hi │ +└──────────────────┘ +", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭────┬────┬────╮ +│Tab1│Tab2│Tab3│ +├────╯ ╰────┴───╮ +│hi2 │ +└──────────────────┘ +", + output + ); + + tv.SelectedTab = tab3; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭────┬────┬────╮ +│Tab1│Tab2│Tab3│ +├────┴────╯ ╰───╮ +│hi3 │ +└──────────────────┘ +", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void Add_Three_TabsSide_Top_ShowInitialLine_False_ChangesTab () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + Tab tab3; + + tv.AddTab ( + tab3 = new () { Id = "tab3", DisplayText = "Tab3", View = new TextView { Id = "tab3.TextView", Width = 3, Height = 1, Text = "hi3" } }, + false); + tv.Width = 20; + tv.Height = 5; + tv.Style = new () { ShowInitialLine = false }; + tv.ApplyStyleChanges (); + + tv.Layout (); + tv.Draw (); + + Assert.Equal (tab1, tv.SelectedTab); + + TestHelpers.AssertDriverContentsAre ( + @" +│Tab1│Tab2│Tab3│ +│ ╰────┴────┴───╮ +│hi │ +│ │ +└──────────────────┘ +", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +│Tab1│Tab2│Tab3│ +├────╯ ╰────┴───╮ +│hi2 │ +│ │ +└──────────────────┘ +", + output + ); + + tv.SelectedTab = tab3; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +│Tab1│Tab2│Tab3│ +├────┴────╯ ╰───╮ +│hi3 │ +│ │ +└──────────────────┘ +", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void Add_Three_TabsSide_Bottom_ShowInitialLine_True_ChangesTab () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + Tab tab3; + + tv.AddTab ( + tab3 = new () { Id = "tab3", DisplayText = "Tab3", View = new TextView { Id = "tab3.TextView", Width = 3, Height = 1, Text = "hi3" } }, + false); + + tv.Width = 20; + tv.Height = 5; + tv.Style = new () { TabsSide = TabSide.Bottom }; + tv.ApplyStyleChanges (); + + tv.Layout (); + tv.Draw (); + + Assert.Equal (tab1, tv.SelectedTab); + + TestHelpers.AssertDriverContentsAre ( + @" +┌──────────────────┐ +│hi │ +│ ╭────┬────┬───╯ +│Tab1│Tab2│Tab3│ +╰────┴────┴────╯ +", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──────────────────┐ +│hi2 │ +├────╮ ╭────┬───╯ +│Tab1│Tab2│Tab3│ +╰────┴────┴────╯ +", + output + ); + + tv.SelectedTab = tab3; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──────────────────┐ +│hi3 │ +├────┬────╮ ╭───╯ +│Tab1│Tab2│Tab3│ +╰────┴────┴────╯ +", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void Add_Three_TabsSide_Bottom_ShowInitialLine_False_ChangesTab () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + Tab tab3; + + tv.AddTab ( + tab3 = new () { Id = "tab3", DisplayText = "Tab3", View = new TextView { Id = "tab3.TextView", Width = 3, Height = 1, Text = "hi3" } }, + false); + + tv.Width = 20; + tv.Height = 5; + tv.Style = new () { TabsSide = TabSide.Bottom, ShowInitialLine = false }; + tv.ApplyStyleChanges (); + + tv.Layout (); + tv.Draw (); + + Assert.Equal (tab1, tv.SelectedTab); + + TestHelpers.AssertDriverContentsAre ( + @" +┌──────────────────┐ +│hi │ +│ │ +│ ╭────┬────┬───╯ +│Tab1│Tab2│Tab3│ +", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──────────────────┐ +│hi2 │ +│ │ +├────╮ ╭────┬───╯ +│Tab1│Tab2│Tab3│ +", + output + ); + + tv.SelectedTab = tab3; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──────────────────┐ +│hi3 │ +│ │ +├────┬────╮ ╭───╯ +│Tab1│Tab2│Tab3│ +", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void Add_Three_TabsSide_Left_ShowInitialLine_True_ChangesTab_Height5 () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + Tab tab3; + + tv.AddTab ( + tab3 = new () { Id = "tab3", DisplayText = "Tab3", View = new TextView { Id = "tab3.TextView", Width = 3, Height = 1, Text = "hi3" } }, + false); + + tv.Width = 20; + tv.Height = 5; + tv.Style = new () { TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); + + tv.Layout (); + tv.Draw (); + + Assert.Equal (tab1, tv.SelectedTab); + + TestHelpers.AssertDriverContentsAre ( + @" +╭──────────────────┐ +│Tab1 hi │ +├────╮ │ +│Tab2│ │ +╰────▼─────────────┘ +", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭────┬─────────────┐ +│Tab1│hi2 │ +├────╯ │ +│Tab2 │ +╰────▼─────────────┘ +", + output + ); + + tv.SelectedTab = tab3; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭────▲─────────────┐ +│Tab3 hi3 │ +╰────╮ │ + │ │ + ╰─────────────┘ +", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void Add_Three_TabsSide_Left_ShowInitialLine_False_ChangesTab_Height5 () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + Tab tab3; + + tv.AddTab ( + tab3 = new () { Id = "tab3", DisplayText = "Tab3", View = new TextView { Id = "tab3.TextView", Width = 3, Height = 1, Text = "hi3" } }, + false); + + tv.Width = 20; + tv.Height = 5; + tv.Style = new () { TabsSide = TabSide.Left, ShowInitialLine = false }; + tv.ApplyStyleChanges (); + + tv.Layout (); + tv.Draw (); + + Assert.Equal (tab1, tv.SelectedTab); + + TestHelpers.AssertDriverContentsAre ( + @" +───────────────────┐ +Tab1 hi │ +────╮ │ +Tab2│ │ +────▼──────────────┘ +", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +────┬──────────────┐ +Tab1│hi2 │ +────╯ │ +Tab2 │ +────▼──────────────┘ +", + output + ); + + tv.SelectedTab = tab3; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +────▲──────────────┐ +Tab3 hi3 │ +────╮ │ + │ │ + ╰──────────────┘ +", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void Add_Three_TabsSide_Left_ShowInitialLine_True_ChangesTab_Height9 () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); Tab tab3; - Tab tab4; - Tab tab5; - tv.AddTab (tab3 = new (), false); - tv.AddTab (tab4 = new (), false); - tv.AddTab (tab5 = new (), false); + tv.AddTab ( + tab3 = new () { Id = "tab3", DisplayText = "Tab3", View = new TextView { Id = "tab3.TextView", Width = 3, Height = 1, Text = "hi3" } }, + false); - tv.SelectedTab = tab1; + tv.Width = 20; + tv.Height = 9; + tv.Style = new () { TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); - var called = 0; - tv.SelectedTabChanged += (s, e) => { called++; }; + tv.Layout (); + tv.Draw (); - tv.SwitchTabBy (1); + Assert.Equal (tab1, tv.SelectedTab); - Assert.Equal (1, called); - Assert.Equal (tab2, tv.SelectedTab); + TestHelpers.AssertDriverContentsAre ( + @" +╭──────────────────┐ +│Tab1 hi │ +├────╮ │ +│Tab2│ │ +├────┤ │ +│Tab3│ │ +╰────┤ │ + │ │ + ╰─────────────┘", + output + ); - //reset called counter - called = 0; + tv.SelectedTab = tab2; - // go right 2 - tv.SwitchTabBy (2); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); - // even though we go right 2 indexes the event should only be called once - Assert.Equal (1, called); - Assert.Equal (tab4, tv.SelectedTab); + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭────┬─────────────┐ +│Tab1│hi2 │ +├────╯ │ +│Tab2 │ +├────╮ │ +│Tab3│ │ +╰────┤ │ + │ │ + ╰─────────────┘ +", + output + ); + + tv.SelectedTab = tab3; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭────┬─────────────┐ +│Tab1│hi3 │ +├────┤ │ +│Tab2│ │ +├────╯ │ +│Tab3 │ +╰────╮ │ + │ │ + ╰─────────────┘ +", + output + ); } [Fact] - public void SwitchTabBy_OutOfTabsRange () + [SetupFakeDriver] + public void Add_Three_TabsSide_Left_ShowInitialLine_False_ChangesTab_Height9 () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); + Tab tab3; - tv.SelectedTab = tab1; - tv.SwitchTabBy (500); + tv.AddTab ( + tab3 = new () { Id = "tab3", DisplayText = "Tab3", View = new TextView { Id = "tab3.TextView", Width = 3, Height = 1, Text = "hi3" } }, + false); - Assert.Equal (tab2, tv.SelectedTab); + tv.Width = 20; + tv.Height = 9; + tv.Style = new () { TabsSide = TabSide.Left, ShowInitialLine = false }; + tv.ApplyStyleChanges (); - tv.SwitchTabBy (-500); + tv.Layout (); + tv.Draw (); Assert.Equal (tab1, tv.SelectedTab); + + TestHelpers.AssertDriverContentsAre ( + @" +───────────────────┐ +Tab1 hi │ +────╮ │ +Tab2│ │ +────┤ │ +Tab3│ │ +────┤ │ + │ │ + ╰──────────────┘ +", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +────┬──────────────┐ +Tab1│hi2 │ +────╯ │ +Tab2 │ +────╮ │ +Tab3│ │ +────┤ │ + │ │ + ╰──────────────┘ +", + output + ); + + tv.SelectedTab = tab3; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +────┬──────────────┐ +Tab1│hi3 │ +────┤ │ +Tab2│ │ +────╯ │ +Tab3 │ +────╮ │ + │ │ + ╰──────────────┘ +", + output + ); } [Fact] - public void RemoveTab_ThatHasFocus () + [SetupFakeDriver] + public void Add_Three_TabsSide_Right_ShowInitialLine_True_ChangesTab_Height5 () { - TabView tv = GetTabView (out Tab _, out Tab tab2); + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + Tab tab3; + + tv.AddTab ( + tab3 = new () { Id = "tab3", DisplayText = "Tab3", View = new TextView { Id = "tab3.TextView", Width = 3, Height = 1, Text = "hi3" } }, + false); + + tv.Width = 20; + tv.Height = 5; + tv.Style = new () { TabsSide = TabSide.Right }; + tv.ApplyStyleChanges (); + + tv.Layout (); + tv.Draw (); + + Assert.Equal (tab1, tv.SelectedTab); + + TestHelpers.AssertDriverContentsAre ( + @" +┌──────────────────╮ +│hi Tab1│ +│ ╭────┤ +│ │Tab2│ +└─────────────▼────╯ +", + output + ); tv.SelectedTab = tab2; - tab2.HasFocus = true; - Assert.Equal (2, tv.Tabs.Count); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); - foreach (Tab t in tv.Tabs.ToArray ()) - { - tv.RemoveTab (t); - } + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌─────────────┬────╮ +│hi2 │Tab1│ +│ ╰────┤ +│ Tab2│ +└─────────────▼────╯ +", + output + ); - Assert.Empty (tv.Tabs); + tv.SelectedTab = tab3; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌─────────────▲────╮ +│hi3 Tab3│ +│ ╭────╯ +│ │ +└─────────────╯ +", + output + ); } [Fact] [SetupFakeDriver] - public void Add_Three_TabsSide_Top_ChangesTab () + public void Add_Three_TabsSide_Right_ShowInitialLine_False_ChangesTab_Height5 () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); Tab tab3; @@ -1856,6 +3018,8 @@ public void Add_Three_TabsSide_Top_ChangesTab () tv.Width = 20; tv.Height = 5; + tv.Style = new () { TabsSide = TabSide.Right, ShowInitialLine = false }; + tv.ApplyStyleChanges (); tv.Layout (); tv.Draw (); @@ -1864,11 +3028,11 @@ public void Add_Three_TabsSide_Top_ChangesTab () DriverAssert.AssertDriverContentsAre ( @" -╭────┬────┬────╮ -│Tab1│Tab2│Tab3│ -│ ╰────┴────┴───╮ -│hi │ -└──────────────────┘ +┌─────────────────── +│hi Tab1 +│ ╭──── +│ │Tab2 +└──────────────▼──── ", output ); @@ -1881,11 +3045,11 @@ public void Add_Three_TabsSide_Top_ChangesTab () DriverAssert.AssertDriverContentsWithFrameAre ( @" -╭────┬────┬────╮ -│Tab1│Tab2│Tab3│ -├────╯ ╰────┴───╮ -│hi2 │ -└──────────────────┘ +┌──────────────┬──── +│hi2 │Tab1 +│ ╰──── +│ Tab2 +└──────────────▼──── ", output ); @@ -1898,11 +3062,11 @@ public void Add_Three_TabsSide_Top_ChangesTab () DriverAssert.AssertDriverContentsWithFrameAre ( @" -╭────┬────┬────╮ -│Tab1│Tab2│Tab3│ -├────┴────╯ ╰───╮ -│hi3 │ -└──────────────────┘ +┌──────────────▲──── +│hi3 Tab3 +│ ╭──── +│ │ +└──────────────╯ ", output ); @@ -1910,7 +3074,7 @@ public void Add_Three_TabsSide_Top_ChangesTab () [Fact] [SetupFakeDriver] - public void Add_Three_TabsSide_Bottom_ChangesTab () + public void Add_Three_TabsSide_Right_ShowInitialLine_True_ChangesTab_Height9 () { TabView tv = GetTabView (out Tab tab1, out Tab tab2); Tab tab3; @@ -1920,8 +3084,8 @@ public void Add_Three_TabsSide_Bottom_ChangesTab () false); tv.Width = 20; - tv.Height = 5; - tv.Style = new () { TabsSide = TabSide.Bottom }; + tv.Height = 9; + tv.Style = new () { TabsSide = TabSide.Right }; tv.ApplyStyleChanges (); tv.Layout (); @@ -1931,11 +3095,15 @@ public void Add_Three_TabsSide_Bottom_ChangesTab () DriverAssert.AssertDriverContentsAre ( @" -┌──────────────────┐ -│hi │ -│ ╭────┬────┬───╯ -│Tab1│Tab2│Tab3│ -╰────┴────┴────╯ +┌──────────────────╮ +│hi Tab1│ +│ ╭────┤ +│ │Tab2│ +│ ├────┤ +│ │Tab3│ +│ ├────╯ +│ │ +└─────────────╯ ", output ); @@ -1948,11 +3116,15 @@ public void Add_Three_TabsSide_Bottom_ChangesTab () DriverAssert.AssertDriverContentsWithFrameAre ( @" -┌──────────────────┐ -│hi2 │ -├────╮ ╭────┬───╯ -│Tab1│Tab2│Tab3│ -╰────┴────┴────╯ +┌─────────────┬────╮ +│hi2 │Tab1│ +│ ╰────┤ +│ Tab2│ +│ ╭────┤ +│ │Tab3│ +│ ├────╯ +│ │ +└─────────────╯ ", output ); @@ -1965,11 +3137,94 @@ public void Add_Three_TabsSide_Bottom_ChangesTab () DriverAssert.AssertDriverContentsWithFrameAre ( @" -┌──────────────────┐ -│hi3 │ -├────┬────╮ ╭───╯ -│Tab1│Tab2│Tab3│ -╰────┴────┴────╯ +┌─────────────┬────╮ +│hi3 │Tab1│ +│ ├────┤ +│ │Tab2│ +│ ╰────┤ +│ Tab3│ +│ ╭────╯ +│ │ +└─────────────╯ +", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void Add_Three_TabsSide_Right_ShowInitialLine_False_ChangesTab_Height9 () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + Tab tab3; + + tv.AddTab ( + tab3 = new () { Id = "tab3", DisplayText = "Tab3", View = new TextView { Id = "tab3.TextView", Width = 3, Height = 1, Text = "hi3" } }, + false); + + tv.Width = 20; + tv.Height = 9; + tv.Style = new () { TabsSide = TabSide.Right, ShowInitialLine = false }; + tv.ApplyStyleChanges (); + + tv.Layout (); + tv.Draw (); + + Assert.Equal (tab1, tv.SelectedTab); + + TestHelpers.AssertDriverContentsAre ( + @" +┌─────────────────── +│hi Tab1 +│ ╭──── +│ │Tab2 +│ ╭──── +│ │Tab3 +│ ├──── +│ │ +└──────────────╯ +", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──────────────┬──── +│hi2 │Tab1 +│ ╰──── +│ Tab2 +│ ╭──── +│ │Tab3 +│ ├──── +│ │ +└──────────────╯ +", + output + ); + + tv.SelectedTab = tab3; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──────────────┬──── +│hi3 │Tab1 +│ ├──── +│ │Tab2 +│ ╰──── +│ Tab3 +│ ╭──── +│ │ +└──────────────╯ ", output ); From e012b2bb490e123e1cc3784b8ca5272095401cc8 Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 2 Dec 2024 00:41:24 +0000 Subject: [PATCH 15/18] Fix ShowInitialLine right side bug. --- Terminal.Gui/Views/TabView/TabRow.cs | 15 +++++++++++++++ Tests/UnitTests/Views/TabViewTests.cs | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/Terminal.Gui/Views/TabView/TabRow.cs b/Terminal.Gui/Views/TabView/TabRow.cs index d67e62b74e..ab00ef0e9f 100644 --- a/Terminal.Gui/Views/TabView/TabRow.cs +++ b/Terminal.Gui/Views/TabView/TabRow.cs @@ -949,6 +949,21 @@ private void RenderTabLineCanvas () break; case TabSide.Right: + // Lower right tee + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 0, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + break; default: throw new ArgumentOutOfRangeException (); diff --git a/Tests/UnitTests/Views/TabViewTests.cs b/Tests/UnitTests/Views/TabViewTests.cs index ee58137e3c..68a48125be 100644 --- a/Tests/UnitTests/Views/TabViewTests.cs +++ b/Tests/UnitTests/Views/TabViewTests.cs @@ -3178,7 +3178,7 @@ public void Add_Three_TabsSide_Right_ShowInitialLine_False_ChangesTab_Height9 () │hi Tab1 │ ╭──── │ │Tab2 -│ ╭──── +│ ├──── │ │Tab3 │ ├──── │ │ From c63b16f2ead2bc427ac69032dcd8d19ff0e8f09c Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 2 Dec 2024 01:38:14 +0000 Subject: [PATCH 16/18] Add TabsTextAlignment feature. --- .../UICatalog/Scenarios/TabViewExample.cs | 47 +- Terminal.Gui/Views/TabView/TabStyle.cs | 5 + Terminal.Gui/Views/TabView/TabView.cs | 1 + Tests/UnitTests/Views/TabViewTests.cs | 445 ++++++++++++++++++ 4 files changed, 497 insertions(+), 1 deletion(-) diff --git a/Examples/UICatalog/Scenarios/TabViewExample.cs b/Examples/UICatalog/Scenarios/TabViewExample.cs index 81b4cd31d7..f9b3fdd53e 100644 --- a/Examples/UICatalog/Scenarios/TabViewExample.cs +++ b/Examples/UICatalog/Scenarios/TabViewExample.cs @@ -15,6 +15,8 @@ public class TabViewExample : Scenario private MenuItem _miShowTopLine; private MenuItem [] _miTabsSide; private MenuItem _cachedTabsSide; + private MenuItem [] _miTabsTextAlignment; + private MenuItem _cachedTabsTextAlignment; private TabView _tabView; public override void Main () @@ -26,6 +28,7 @@ public override void Main () Toplevel appWindow = new (); _miTabsSide = SetTabsSide (); + _miTabsTextAlignment = SetTabsTextAlignment (); var menu = new MenuBar { @@ -69,7 +72,12 @@ public override void Main () "_Show TabView Border", "", ShowTabViewBorder - ) { Checked = true, CheckType = MenuItemCheckStyle.Checked } + ) { Checked = true, CheckType = MenuItemCheckStyle.Checked }, + null, + _miTabsTextAlignment [0], + _miTabsTextAlignment [1], + _miTabsTextAlignment [2], + _miTabsTextAlignment [3] } ) ] @@ -283,6 +291,43 @@ private MenuItem [] SetTabsSide () return menuItems.ToArray (); } + private MenuItem [] SetTabsTextAlignment () + { + List menuItems = []; + + foreach (TabSide align in Enum.GetValues (typeof (Alignment))) + { + string alignName = Enum.GetName (typeof (Alignment), align); + var item = new MenuItem { Title = $"_{alignName}", Data = align }; + item.CheckType |= MenuItemCheckStyle.Radio; + + item.Action += () => + { + if (_cachedTabsTextAlignment == item) + { + return; + } + + _cachedTabsTextAlignment.Checked = false; + item.Checked = true; + _cachedTabsTextAlignment = item; + _tabView.Style.TabsTextAlignment = (Alignment)item.Data; + _tabView.ApplyStyleChanges (); + }; + item.ShortcutKey = ((Key)alignName! [0].ToString ().ToLower ()).WithCtrl; + + if (alignName == "Start") + { + item.Checked = true; + _cachedTabsTextAlignment = item; + } + + menuItems.Add (item); + } + + return menuItems.ToArray (); + } + private void ShowBorder () { _miShowBorder.Checked = !_miShowBorder.Checked; diff --git a/Terminal.Gui/Views/TabView/TabStyle.cs b/Terminal.Gui/Views/TabView/TabStyle.cs index aeff1b3a82..3521d4bbb5 100644 --- a/Terminal.Gui/Views/TabView/TabStyle.cs +++ b/Terminal.Gui/Views/TabView/TabStyle.cs @@ -19,4 +19,9 @@ public class TabStyle /// Gets or sets the tabs side to render. public TabSide TabsSide { get; set; } + + /// + /// Gets or sets the tabs text alignments. + /// + public Alignment TabsTextAlignment { get; set; } } diff --git a/Terminal.Gui/Views/TabView/TabView.cs b/Terminal.Gui/Views/TabView/TabView.cs index d17e7268a4..5bebef2b09 100644 --- a/Terminal.Gui/Views/TabView/TabView.cs +++ b/Terminal.Gui/Views/TabView/TabView.cs @@ -738,6 +738,7 @@ protected override void Dispose (bool disposing) int maxHeight = Math.Max (0, Math.Min (bounds.Height - 2, 2)); tab.Height = 2; + tab.TextAlignment = Style.TabsTextAlignment; // if tab view is height <= 3 don't render any tabs if (maxHeight == 0) diff --git a/Tests/UnitTests/Views/TabViewTests.cs b/Tests/UnitTests/Views/TabViewTests.cs index 68a48125be..ca878d8f55 100644 --- a/Tests/UnitTests/Views/TabViewTests.cs +++ b/Tests/UnitTests/Views/TabViewTests.cs @@ -3279,6 +3279,451 @@ public void Mouse_Wheel_Changes_Tab () top.Dispose (); } + [Fact] + [SetupFakeDriver] + public void Tabs_Alignments () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + tv.Width = 20; + tv.Height = 5; + + tab1.DisplayText = "Tab 1"; + tab2.DisplayText = "Long Text"; + + tv.Layout (); + tv.Draw (); + + string top = @" +╭─────┬─────────╮ +│Tab 1│Long Text│ +│ ╰─────────┴──╮ +│hi │ +└──────────────────┘ +"; + + TestHelpers.AssertDriverContentsWithFrameAre (top, output); + + tv.Style = new () { TabsSide = TabSide.Bottom }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + string bottom = @" +┌──────────────────┐ +│hi │ +│ ╭─────────┬──╯ +│Tab 1│Long Text│ +╰─────┴─────────╯ +"; + + TestHelpers.AssertDriverContentsWithFrameAre (bottom, output); + + tv.Style = new () { TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭──────────────────┐ +│Tab 1 hi │ +├─────────╮ │ +│Long Text│ │ +╰─────────┴────────┘ +", + output + ); + + tv.Style = new () { TabsSide = TabSide.Right }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──────────────────╮ +│hi Tab 1 │ +│ ╭─────────┤ +│ │Long Text│ +└────────┴─────────╯ +", + output + ); + + Assert.Equal (Alignment.Start, tv.Style.TabsTextAlignment); + + tv.Style = new () { TabsSide = TabSide.Top, TabsTextAlignment = Alignment.End }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre (top, output); + + tv.Style = new () { TabsSide = TabSide.Bottom, TabsTextAlignment = Alignment.End }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre (bottom, output); + + tv.Style = new () { TabsSide = TabSide.Left, TabsTextAlignment = Alignment.End }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭──────────────────┐ +│ Tab 1 hi │ +├─────────╮ │ +│Long Text│ │ +╰─────────┴────────┘ +", + output + ); + + tv.Style = new () { TabsSide = TabSide.Right, TabsTextAlignment = Alignment.End }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──────────────────╮ +│hi Tab 1│ +│ ╭─────────┤ +│ │Long Text│ +└────────┴─────────╯ +", + output + ); + + tv.Style = new () { TabsSide = TabSide.Top, TabsTextAlignment = Alignment.Center }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre (top, output); + + tv.Style = new () { TabsSide = TabSide.Bottom, TabsTextAlignment = Alignment.Center }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre (bottom, output); + + tv.Style = new () { TabsSide = TabSide.Left, TabsTextAlignment = Alignment.Center }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭──────────────────┐ +│ Tab 1 hi │ +├─────────╮ │ +│Long Text│ │ +╰─────────┴────────┘ +", + output + ); + + tv.Style = new () { TabsSide = TabSide.Right, TabsTextAlignment = Alignment.Center }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──────────────────╮ +│hi Tab 1 │ +│ ╭─────────┤ +│ │Long Text│ +└────────┴─────────╯ +", + output + ); + + tv.Style = new () { TabsSide = TabSide.Top, TabsTextAlignment = Alignment.Fill }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre (top, output); + + tv.Style = new () { TabsSide = TabSide.Bottom, TabsTextAlignment = Alignment.Fill }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre (bottom, output); + + tv.Style = new () { TabsSide = TabSide.Left, TabsTextAlignment = Alignment.Fill }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +╭──────────────────┐ +│Tab 1 hi │ +├─────────╮ │ +│Long Text│ │ +╰─────────┴────────┘ +", + output + ); + + tv.Style = new () { TabsSide = TabSide.Right, TabsTextAlignment = Alignment.Fill }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌──────────────────╮ +│hi Tab 1│ +│ ╭─────────┤ +│ │Long Text│ +└────────┴─────────╯ +", + output + ); + + tv.Style = new () { TabsSide = TabSide.Top, ShowInitialLine = false, TabsTextAlignment = Alignment.Start }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + top = @" +│Tab 1│Long Text│ +│ ╰─────────┴──╮ +│hi │ +│ │ +└──────────────────┘ +"; + + TestHelpers.AssertDriverContentsWithFrameAre (top, output); + + // ShowInitialLine false + + tv.Style = new () { TabsSide = TabSide.Bottom, ShowInitialLine = false, TabsTextAlignment = Alignment.Start }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + bottom = @" +┌──────────────────┐ +│hi │ +│ │ +│ ╭─────────┬──╯ +│Tab 1│Long Text│ +"; + + TestHelpers.AssertDriverContentsWithFrameAre (bottom, output); + + tv.Style = new () { TabsSide = TabSide.Left, ShowInitialLine = false, TabsTextAlignment = Alignment.Start }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +───────────────────┐ +Tab 1 hi │ +─────────╮ │ +Long Text│ │ +─────────┴─────────┘ +", + output + ); + + tv.Style = new () { TabsSide = TabSide.Right, ShowInitialLine = false, TabsTextAlignment = Alignment.Start }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌─────────────────── +│hi Tab 1 +│ ╭───────── +│ │Long Text +└─────────┴───────── +", + output + ); + + tv.Style = new () { TabsSide = TabSide.Top, ShowInitialLine = false, TabsTextAlignment = Alignment.End }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre (top, output); + + tv.Style = new () { TabsSide = TabSide.Bottom, ShowInitialLine = false, TabsTextAlignment = Alignment.End }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre (bottom, output); + + tv.Style = new () { TabsSide = TabSide.Left, ShowInitialLine = false, TabsTextAlignment = Alignment.End }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +───────────────────┐ + Tab 1 hi │ +─────────╮ │ +Long Text│ │ +─────────┴─────────┘ +", + output + ); + + tv.Style = new () { TabsSide = TabSide.Right, ShowInitialLine = false, TabsTextAlignment = Alignment.End }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌─────────────────── +│hi Tab 1 +│ ╭───────── +│ │Long Text +└─────────┴───────── +", + output + ); + + tv.Style = new () { TabsSide = TabSide.Top, ShowInitialLine = false, TabsTextAlignment = Alignment.Center }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre (top, output); + + tv.Style = new () { TabsSide = TabSide.Bottom, ShowInitialLine = false, TabsTextAlignment = Alignment.Center }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre (bottom, output); + + tv.Style = new () { TabsSide = TabSide.Left, ShowInitialLine = false, TabsTextAlignment = Alignment.Center }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +───────────────────┐ + Tab 1 hi │ +─────────╮ │ +Long Text│ │ +─────────┴─────────┘ +", + output + ); + + tv.Style = new () { TabsSide = TabSide.Right, ShowInitialLine = false, TabsTextAlignment = Alignment.Center }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌─────────────────── +│hi Tab 1 +│ ╭───────── +│ │Long Text +└─────────┴───────── +", + output + ); + + tv.Style = new () { TabsSide = TabSide.Top, ShowInitialLine = false, TabsTextAlignment = Alignment.Fill }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre (top, output); + + tv.Style = new () { TabsSide = TabSide.Bottom, ShowInitialLine = false, TabsTextAlignment = Alignment.Fill }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre (bottom, output); + + tv.Style = new () { TabsSide = TabSide.Left, ShowInitialLine = false, TabsTextAlignment = Alignment.Fill }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +───────────────────┐ +Tab 1 hi │ +─────────╮ │ +Long Text│ │ +─────────┴─────────┘ +", + output + ); + + tv.Style = new () { TabsSide = TabSide.Right, ShowInitialLine = false, TabsTextAlignment = Alignment.Fill }; + tv.ApplyStyleChanges (); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +┌─────────────────── +│hi Tab 1 +│ ╭───────── +│ │Long Text +└─────────┴───────── +", + output + ); + } + private TabView GetTabView () { return GetTabView (out _, out _); } private TabView GetTabView (out Tab tab1, out Tab tab2) From 74bd5112d28fa77cddd048b4b955d88612d91e77 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 3 Dec 2024 22:30:08 +0000 Subject: [PATCH 17/18] Fix bug with the _rightDownScrollIndicator. --- Terminal.Gui/Views/TabView/TabRow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Terminal.Gui/Views/TabView/TabRow.cs b/Terminal.Gui/Views/TabView/TabRow.cs index ab00ef0e9f..46bb559c75 100644 --- a/Terminal.Gui/Views/TabView/TabRow.cs +++ b/Terminal.Gui/Views/TabView/TabRow.cs @@ -1531,7 +1531,7 @@ private void RenderUnderline () _rightDownScrollIndicator.Visible = true; // Ensures this is clicked instead of the last tab if under this - MoveSubViewToStart (_rightDownScrollIndicator); + MoveSubViewToEnd (_rightDownScrollIndicator); } else { From 0a8be1082207def91da93ce01f9943941053d8c7 Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 3 Jul 2025 16:39:16 +0100 Subject: [PATCH 18/18] Resolving rebase conflicts --- .../UICatalog/Scenarios/TabViewExample.cs | 5 +- Terminal.Gui/Views/TabView/TabSide.cs | 2 +- Terminal.Gui/Views/TabView/TabView.cs | 6 +- Tests/UnitTests/Views/TabViewTests.cs | 228 +++++++++--------- 4 files changed, 118 insertions(+), 123 deletions(-) diff --git a/Examples/UICatalog/Scenarios/TabViewExample.cs b/Examples/UICatalog/Scenarios/TabViewExample.cs index f9b3fdd53e..f821da2873 100644 --- a/Examples/UICatalog/Scenarios/TabViewExample.cs +++ b/Examples/UICatalog/Scenarios/TabViewExample.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Text; namespace UICatalog.Scenarios; diff --git a/Terminal.Gui/Views/TabView/TabSide.cs b/Terminal.Gui/Views/TabView/TabSide.cs index eef3a51c39..3c25306494 100644 --- a/Terminal.Gui/Views/TabView/TabSide.cs +++ b/Terminal.Gui/Views/TabView/TabSide.cs @@ -1,4 +1,4 @@ -namespace Terminal.Gui; +namespace Terminal.Gui.Views; /// /// Defines tab side. diff --git a/Terminal.Gui/Views/TabView/TabView.cs b/Terminal.Gui/Views/TabView/TabView.cs index 5bebef2b09..a426bfc7a6 100644 --- a/Terminal.Gui/Views/TabView/TabView.cs +++ b/Terminal.Gui/Views/TabView/TabView.cs @@ -133,7 +133,7 @@ public TabView () Command.Up, () => { - if (_style.TabsOnBottom) + if (_style.TabsSide == TabSide.Bottom) { if (_tabsBar is { HasFocus: true } && _containerView.CanFocus) { @@ -176,7 +176,7 @@ public TabView () Command.Down, () => { - if (_style.TabsOnBottom) + if (_style.TabsSide == TabSide.Bottom) { if (_containerView is { HasFocus: true }) { @@ -224,8 +224,6 @@ public TabView () KeyBindings.Add (Key.End, Command.RightEnd); KeyBindings.Add (Key.PageDown, Command.PageDown); KeyBindings.Add (Key.PageUp, Command.PageUp); - KeyBindings.Add (Key.CursorUp, Command.Up); - KeyBindings.Add (Key.CursorDown, Command.Down); } /// diff --git a/Tests/UnitTests/Views/TabViewTests.cs b/Tests/UnitTests/Views/TabViewTests.cs index ca878d8f55..c46ecfe61c 100644 --- a/Tests/UnitTests/Views/TabViewTests.cs +++ b/Tests/UnitTests/Views/TabViewTests.cs @@ -1250,7 +1250,7 @@ public void ShowInitialLine_True_TabsSide_Left_TestTabView_Height3 () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭───┐ │T h│ @@ -1272,7 +1272,7 @@ public void ShowInitialLine_True_TabsSide_Left_TestTabView_Height4 () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭───┐ │T h│ @@ -1296,7 +1296,7 @@ public void ShowInitialLine_True_TabsSide_Left_TestTabView_Height5_One_Tab () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭───┐ │T h│ @@ -1320,7 +1320,7 @@ public void ShowInitialLine_True_TabsSide_Left_TestTabView_Height6_Two_Tab () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭───┐ │T h│ @@ -1337,7 +1337,7 @@ public void ShowInitialLine_True_TabsSide_Left_TestTabView_Height6_Two_Tab () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭─┬─┐ │T│h│ @@ -1362,7 +1362,7 @@ public void ShowInitialLine_True_TabsSide_Left_TestTabView_Height7_Two_Tab () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭───┐ │T h│ @@ -1380,7 +1380,7 @@ public void ShowInitialLine_True_TabsSide_Left_TestTabView_Height7_Two_Tab () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭─┬─┐ │T│h│ @@ -1411,7 +1411,7 @@ public void ShowInitialLine_True_TabsSide_Left_TestThinTabView_WithLongNames () tv.Layout (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭────────┐ │12 hi │ @@ -1429,7 +1429,7 @@ public void ShowInitialLine_True_TabsSide_Left_TestThinTabView_WithLongNames () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭────────┐ │123456 h│ @@ -1446,7 +1446,7 @@ public void ShowInitialLine_True_TabsSide_Left_TestThinTabView_WithLongNames () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭──────┬─┐ │123456│h│ @@ -1464,7 +1464,7 @@ public void ShowInitialLine_True_TabsSide_Left_TestThinTabView_WithLongNames () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭──────┬─┐ │123456│h│ @@ -1492,7 +1492,7 @@ public void ShowInitialLine_True_TabsSide_Left_With_Unicode () tv.Layout (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭──────────────────┐ │Tab0 hi │ @@ -1508,7 +1508,7 @@ public void ShowInitialLine_True_TabsSide_Left_With_Unicode () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭──────────────┬───┐ │Tab0 │hi2│ @@ -1532,7 +1532,7 @@ public void ShowInitialLine_False_TabsSide_Left_TestTabView_Height3 () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ────┐ Ta h│ @@ -1554,7 +1554,7 @@ public void ShowInitialLine_False_TabsSide_Left_TestTabView_Height4 () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ────┐ Ta h│ @@ -1578,7 +1578,7 @@ public void ShowInitialLine_False_TabsSide_Left_TestTabView_Height5_One_Tab () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ────┐ Ta h│ @@ -1602,7 +1602,7 @@ public void ShowInitialLine_False_TabsSide_Left_TestTabView_Height6_Two_Tab () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ────┐ Ta h│ @@ -1619,7 +1619,7 @@ Ta h│ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ──┬─┐ Ta│h│ @@ -1644,7 +1644,7 @@ public void ShowInitialLine_False_TabsSide_Left_TestTabView_Height7_Two_Tab () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ────┐ Ta h│ @@ -1662,7 +1662,7 @@ Ta h│ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ──┬─┐ Ta│h│ @@ -1693,7 +1693,7 @@ public void ShowInitialLine_False_TabsSide_Left_TestThinTabView_WithLongNames () tv.Layout (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ─────────┐ 12 hi │ @@ -1704,13 +1704,13 @@ 12 hi │ ); tv.SelectedTab = tab2; - Assert.Equal (tab2, tv.Subviews.First (v => v.Id.Contains ("tabRow")).MostFocused); + Assert.Equal (tab2, tv.SubViews.First (v => v.Id.Contains ("tabRow")).MostFocused); tv.Layout (); View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ──┬──────┐ 12│hi2 │ @@ -1730,7 +1730,7 @@ 12 hi │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ─────────┐ 1234567 h│ @@ -1747,7 +1747,7 @@ 1234567 h│ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ───────┬─┐ 1234567│h│ @@ -1765,7 +1765,7 @@ 1234567 h│ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ───────┬─┐ 1234567│h│ @@ -1789,7 +1789,7 @@ public void ShowInitialLine_True_TabsSide_Right_TestTabView_Height3 () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌───╮ │h T│ @@ -1811,7 +1811,7 @@ public void ShowInitialLine_True_TabsSide_Right_TestTabView_Height4 () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌───╮ │h T│ @@ -1835,7 +1835,7 @@ public void ShowInitialLine_True_TabsSide_Right_TestTabView_Height5_One_Tab () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌───╮ │h T│ @@ -1859,7 +1859,7 @@ public void ShowInitialLine_True_TabsSide_Right_TestTabView_Height6_Two_Tab () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌───╮ │h T│ @@ -1876,7 +1876,7 @@ public void ShowInitialLine_True_TabsSide_Right_TestTabView_Height6_Two_Tab () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌─┬─╮ │h│T│ @@ -1901,7 +1901,7 @@ public void ShowInitialLine_True_TabsSide_Right_TestTabView_Height7_Two_Tab () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌───╮ │h T│ @@ -1919,7 +1919,7 @@ public void ShowInitialLine_True_TabsSide_Right_TestTabView_Height7_Two_Tab () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌─┬─╮ │h│T│ @@ -1950,7 +1950,7 @@ public void ShowInitialLine_True_TabsSide_Right_TestThinTabView_WithLongNames () tv.Layout (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌────────╮ │hi 12│ @@ -1968,7 +1968,7 @@ public void ShowInitialLine_True_TabsSide_Right_TestThinTabView_WithLongNames () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌────────╮ │h 123456│ @@ -1985,7 +1985,7 @@ public void ShowInitialLine_True_TabsSide_Right_TestThinTabView_WithLongNames () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌─┬──────╮ │h│123456│ @@ -2003,7 +2003,7 @@ public void ShowInitialLine_True_TabsSide_Right_TestThinTabView_WithLongNames () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌─┬──────╮ │h│123456│ @@ -2031,7 +2031,7 @@ public void ShowInitialLine_True_TabsSide_Right_With_Unicode () tv.Layout (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──────────────────╮ │hi Tab0 │ @@ -2047,7 +2047,7 @@ public void ShowInitialLine_True_TabsSide_Right_With_Unicode () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌───┬──────────────╮ │hi2│Tab0 │ @@ -2071,7 +2071,7 @@ public void ShowInitialLine_False_TabsSide_Right_TestTabView_Height3 () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──── │h Ta @@ -2093,7 +2093,7 @@ public void ShowInitialLine_False_TabsSide_Right_TestTabView_Height4 () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──── │h Ta @@ -2117,7 +2117,7 @@ public void ShowInitialLine_False_TabsSide_Right_TestTabView_Height5_One_Tab () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──── │h Ta @@ -2141,7 +2141,7 @@ public void ShowInitialLine_False_TabsSide_Right_TestTabView_Height6_Two_Tab () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──── │h Ta @@ -2158,7 +2158,7 @@ public void ShowInitialLine_False_TabsSide_Right_TestTabView_Height6_Two_Tab () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌─┬── │h│Ta @@ -2183,7 +2183,7 @@ public void ShowInitialLine_False_TabsSide_Right_TestTabView_Height7_Two_Tab () tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──── │h Ta @@ -2201,7 +2201,7 @@ public void ShowInitialLine_False_TabsSide_Right_TestTabView_Height7_Two_Tab () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌─┬── │h│Ta @@ -2232,7 +2232,7 @@ public void ShowInitialLine_False_TabsSide_Right_TestThinTabView_WithLongNames ( tv.Layout (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌───────── │hi 12 @@ -2243,13 +2243,13 @@ public void ShowInitialLine_False_TabsSide_Right_TestThinTabView_WithLongNames ( ); tv.SelectedTab = tab2; - Assert.Equal (tab2, tv.Subviews.First (v => v.Id.Contains ("tabRow")).MostFocused); + Assert.Equal (tab2, tv.SubViews.First (v => v.Id.Contains ("tabRow")).MostFocused); tv.Layout (); View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──────┬── │hi2 │12 @@ -2269,7 +2269,7 @@ public void ShowInitialLine_False_TabsSide_Right_TestThinTabView_WithLongNames ( View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌───────── │h 1234567 @@ -2286,7 +2286,7 @@ public void ShowInitialLine_False_TabsSide_Right_TestThinTabView_WithLongNames ( View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌─┬─────── │h│1234567 @@ -2304,7 +2304,7 @@ public void ShowInitialLine_False_TabsSide_Right_TestThinTabView_WithLongNames ( View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌─┬─────── │h│1234567 @@ -2401,7 +2401,7 @@ public void Add_Three_TabsSide_Top_ShowInitialLine_True_ChangesTab () Assert.Equal (tab1, tv.SelectedTab); - TestHelpers.AssertDriverContentsAre ( + DriverAssert.AssertDriverContentsAre ( @" ╭────┬────┬────╮ │Tab1│Tab2│Tab3│ @@ -2418,7 +2418,7 @@ public void Add_Three_TabsSide_Top_ShowInitialLine_True_ChangesTab () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭────┬────┬────╮ │Tab1│Tab2│Tab3│ @@ -2435,7 +2435,7 @@ public void Add_Three_TabsSide_Top_ShowInitialLine_True_ChangesTab () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭────┬────┬────╮ │Tab1│Tab2│Tab3│ @@ -2467,7 +2467,7 @@ public void Add_Three_TabsSide_Top_ShowInitialLine_False_ChangesTab () Assert.Equal (tab1, tv.SelectedTab); - TestHelpers.AssertDriverContentsAre ( + DriverAssert.AssertDriverContentsAre ( @" │Tab1│Tab2│Tab3│ │ ╰────┴────┴───╮ @@ -2484,7 +2484,7 @@ public void Add_Three_TabsSide_Top_ShowInitialLine_False_ChangesTab () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" │Tab1│Tab2│Tab3│ ├────╯ ╰────┴───╮ @@ -2501,7 +2501,7 @@ public void Add_Three_TabsSide_Top_ShowInitialLine_False_ChangesTab () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" │Tab1│Tab2│Tab3│ ├────┴────╯ ╰───╮ @@ -2534,7 +2534,7 @@ public void Add_Three_TabsSide_Bottom_ShowInitialLine_True_ChangesTab () Assert.Equal (tab1, tv.SelectedTab); - TestHelpers.AssertDriverContentsAre ( + DriverAssert.AssertDriverContentsAre ( @" ┌──────────────────┐ │hi │ @@ -2551,7 +2551,7 @@ public void Add_Three_TabsSide_Bottom_ShowInitialLine_True_ChangesTab () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──────────────────┐ │hi2 │ @@ -2568,7 +2568,7 @@ public void Add_Three_TabsSide_Bottom_ShowInitialLine_True_ChangesTab () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──────────────────┐ │hi3 │ @@ -2601,7 +2601,7 @@ public void Add_Three_TabsSide_Bottom_ShowInitialLine_False_ChangesTab () Assert.Equal (tab1, tv.SelectedTab); - TestHelpers.AssertDriverContentsAre ( + DriverAssert.AssertDriverContentsAre ( @" ┌──────────────────┐ │hi │ @@ -2618,7 +2618,7 @@ public void Add_Three_TabsSide_Bottom_ShowInitialLine_False_ChangesTab () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──────────────────┐ │hi2 │ @@ -2635,7 +2635,7 @@ public void Add_Three_TabsSide_Bottom_ShowInitialLine_False_ChangesTab () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──────────────────┐ │hi3 │ @@ -2668,7 +2668,7 @@ public void Add_Three_TabsSide_Left_ShowInitialLine_True_ChangesTab_Height5 () Assert.Equal (tab1, tv.SelectedTab); - TestHelpers.AssertDriverContentsAre ( + DriverAssert.AssertDriverContentsAre ( @" ╭──────────────────┐ │Tab1 hi │ @@ -2685,7 +2685,7 @@ public void Add_Three_TabsSide_Left_ShowInitialLine_True_ChangesTab_Height5 () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭────┬─────────────┐ │Tab1│hi2 │ @@ -2702,7 +2702,7 @@ public void Add_Three_TabsSide_Left_ShowInitialLine_True_ChangesTab_Height5 () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭────▲─────────────┐ │Tab3 hi3 │ @@ -2735,7 +2735,7 @@ public void Add_Three_TabsSide_Left_ShowInitialLine_False_ChangesTab_Height5 () Assert.Equal (tab1, tv.SelectedTab); - TestHelpers.AssertDriverContentsAre ( + DriverAssert.AssertDriverContentsAre ( @" ───────────────────┐ Tab1 hi │ @@ -2752,7 +2752,7 @@ Tab1 hi │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ────┬──────────────┐ Tab1│hi2 │ @@ -2769,7 +2769,7 @@ Tab1 hi │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ────▲──────────────┐ Tab3 hi3 │ @@ -2802,7 +2802,7 @@ public void Add_Three_TabsSide_Left_ShowInitialLine_True_ChangesTab_Height9 () Assert.Equal (tab1, tv.SelectedTab); - TestHelpers.AssertDriverContentsAre ( + DriverAssert.AssertDriverContentsAre ( @" ╭──────────────────┐ │Tab1 hi │ @@ -2822,7 +2822,7 @@ public void Add_Three_TabsSide_Left_ShowInitialLine_True_ChangesTab_Height9 () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭────┬─────────────┐ │Tab1│hi2 │ @@ -2843,7 +2843,7 @@ public void Add_Three_TabsSide_Left_ShowInitialLine_True_ChangesTab_Height9 () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭────┬─────────────┐ │Tab1│hi3 │ @@ -2880,7 +2880,7 @@ public void Add_Three_TabsSide_Left_ShowInitialLine_False_ChangesTab_Height9 () Assert.Equal (tab1, tv.SelectedTab); - TestHelpers.AssertDriverContentsAre ( + DriverAssert.AssertDriverContentsAre ( @" ───────────────────┐ Tab1 hi │ @@ -2901,7 +2901,7 @@ Tab1 hi │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ────┬──────────────┐ Tab1│hi2 │ @@ -2922,7 +2922,7 @@ Tab1 hi │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ────┬──────────────┐ Tab1│hi3 │ @@ -2959,7 +2959,7 @@ public void Add_Three_TabsSide_Right_ShowInitialLine_True_ChangesTab_Height5 () Assert.Equal (tab1, tv.SelectedTab); - TestHelpers.AssertDriverContentsAre ( + DriverAssert.AssertDriverContentsAre ( @" ┌──────────────────╮ │hi Tab1│ @@ -2976,7 +2976,7 @@ public void Add_Three_TabsSide_Right_ShowInitialLine_True_ChangesTab_Height5 () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌─────────────┬────╮ │hi2 │Tab1│ @@ -2993,7 +2993,7 @@ public void Add_Three_TabsSide_Right_ShowInitialLine_True_ChangesTab_Height5 () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌─────────────▲────╮ │hi3 Tab3│ @@ -3172,7 +3172,7 @@ public void Add_Three_TabsSide_Right_ShowInitialLine_False_ChangesTab_Height9 () Assert.Equal (tab1, tv.SelectedTab); - TestHelpers.AssertDriverContentsAre ( + DriverAssert.AssertDriverContentsAre ( @" ┌─────────────────── │hi Tab1 @@ -3193,7 +3193,7 @@ public void Add_Three_TabsSide_Right_ShowInitialLine_False_ChangesTab_Height9 () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──────────────┬──── │hi2 │Tab1 @@ -3214,7 +3214,7 @@ public void Add_Three_TabsSide_Right_ShowInitialLine_False_ChangesTab_Height9 () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──────────────┬──── │hi3 │Tab1 @@ -3301,7 +3301,7 @@ public void Tabs_Alignments () └──────────────────┘ "; - TestHelpers.AssertDriverContentsWithFrameAre (top, output); + DriverAssert.AssertDriverContentsWithFrameAre (top, output); tv.Style = new () { TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); @@ -3317,7 +3317,7 @@ public void Tabs_Alignments () ╰─────┴─────────╯ "; - TestHelpers.AssertDriverContentsWithFrameAre (bottom, output); + DriverAssert.AssertDriverContentsWithFrameAre (bottom, output); tv.Style = new () { TabsSide = TabSide.Left }; tv.ApplyStyleChanges (); @@ -3325,7 +3325,7 @@ public void Tabs_Alignments () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭──────────────────┐ │Tab 1 hi │ @@ -3342,7 +3342,7 @@ public void Tabs_Alignments () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──────────────────╮ │hi Tab 1 │ @@ -3361,7 +3361,7 @@ public void Tabs_Alignments () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre (top, output); + DriverAssert.AssertDriverContentsWithFrameAre (top, output); tv.Style = new () { TabsSide = TabSide.Bottom, TabsTextAlignment = Alignment.End }; tv.ApplyStyleChanges (); @@ -3369,7 +3369,7 @@ public void Tabs_Alignments () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre (bottom, output); + DriverAssert.AssertDriverContentsWithFrameAre (bottom, output); tv.Style = new () { TabsSide = TabSide.Left, TabsTextAlignment = Alignment.End }; tv.ApplyStyleChanges (); @@ -3377,7 +3377,7 @@ public void Tabs_Alignments () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭──────────────────┐ │ Tab 1 hi │ @@ -3394,7 +3394,7 @@ public void Tabs_Alignments () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──────────────────╮ │hi Tab 1│ @@ -3411,7 +3411,7 @@ public void Tabs_Alignments () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre (top, output); + DriverAssert.AssertDriverContentsWithFrameAre (top, output); tv.Style = new () { TabsSide = TabSide.Bottom, TabsTextAlignment = Alignment.Center }; tv.ApplyStyleChanges (); @@ -3419,7 +3419,7 @@ public void Tabs_Alignments () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre (bottom, output); + DriverAssert.AssertDriverContentsWithFrameAre (bottom, output); tv.Style = new () { TabsSide = TabSide.Left, TabsTextAlignment = Alignment.Center }; tv.ApplyStyleChanges (); @@ -3427,7 +3427,7 @@ public void Tabs_Alignments () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭──────────────────┐ │ Tab 1 hi │ @@ -3444,7 +3444,7 @@ public void Tabs_Alignments () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──────────────────╮ │hi Tab 1 │ @@ -3461,7 +3461,7 @@ public void Tabs_Alignments () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre (top, output); + DriverAssert.AssertDriverContentsWithFrameAre (top, output); tv.Style = new () { TabsSide = TabSide.Bottom, TabsTextAlignment = Alignment.Fill }; tv.ApplyStyleChanges (); @@ -3469,7 +3469,7 @@ public void Tabs_Alignments () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre (bottom, output); + DriverAssert.AssertDriverContentsWithFrameAre (bottom, output); tv.Style = new () { TabsSide = TabSide.Left, TabsTextAlignment = Alignment.Fill }; tv.ApplyStyleChanges (); @@ -3477,7 +3477,7 @@ public void Tabs_Alignments () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ╭──────────────────┐ │Tab 1 hi │ @@ -3494,7 +3494,7 @@ public void Tabs_Alignments () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌──────────────────╮ │hi Tab 1│ @@ -3519,7 +3519,7 @@ public void Tabs_Alignments () └──────────────────┘ "; - TestHelpers.AssertDriverContentsWithFrameAre (top, output); + DriverAssert.AssertDriverContentsWithFrameAre (top, output); // ShowInitialLine false @@ -3537,7 +3537,7 @@ public void Tabs_Alignments () │Tab 1│Long Text│ "; - TestHelpers.AssertDriverContentsWithFrameAre (bottom, output); + DriverAssert.AssertDriverContentsWithFrameAre (bottom, output); tv.Style = new () { TabsSide = TabSide.Left, ShowInitialLine = false, TabsTextAlignment = Alignment.Start }; tv.ApplyStyleChanges (); @@ -3545,7 +3545,7 @@ public void Tabs_Alignments () View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ───────────────────┐ Tab 1 hi │ @@ -3562,7 +3562,7 @@ Long Text│ │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌─────────────────── │hi Tab 1 @@ -3579,7 +3579,7 @@ Long Text│ │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre (top, output); + DriverAssert.AssertDriverContentsWithFrameAre (top, output); tv.Style = new () { TabsSide = TabSide.Bottom, ShowInitialLine = false, TabsTextAlignment = Alignment.End }; tv.ApplyStyleChanges (); @@ -3587,7 +3587,7 @@ Long Text│ │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre (bottom, output); + DriverAssert.AssertDriverContentsWithFrameAre (bottom, output); tv.Style = new () { TabsSide = TabSide.Left, ShowInitialLine = false, TabsTextAlignment = Alignment.End }; tv.ApplyStyleChanges (); @@ -3595,7 +3595,7 @@ Long Text│ │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ───────────────────┐ Tab 1 hi │ @@ -3612,7 +3612,7 @@ Long Text│ │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌─────────────────── │hi Tab 1 @@ -3629,7 +3629,7 @@ Long Text│ │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre (top, output); + DriverAssert.AssertDriverContentsWithFrameAre (top, output); tv.Style = new () { TabsSide = TabSide.Bottom, ShowInitialLine = false, TabsTextAlignment = Alignment.Center }; tv.ApplyStyleChanges (); @@ -3637,7 +3637,7 @@ Long Text│ │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre (bottom, output); + DriverAssert.AssertDriverContentsWithFrameAre (bottom, output); tv.Style = new () { TabsSide = TabSide.Left, ShowInitialLine = false, TabsTextAlignment = Alignment.Center }; tv.ApplyStyleChanges (); @@ -3645,7 +3645,7 @@ Long Text│ │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ───────────────────┐ Tab 1 hi │ @@ -3662,7 +3662,7 @@ Long Text│ │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌─────────────────── │hi Tab 1 @@ -3679,7 +3679,7 @@ Long Text│ │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre (top, output); + DriverAssert.AssertDriverContentsWithFrameAre (top, output); tv.Style = new () { TabsSide = TabSide.Bottom, ShowInitialLine = false, TabsTextAlignment = Alignment.Fill }; tv.ApplyStyleChanges (); @@ -3687,7 +3687,7 @@ Long Text│ │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre (bottom, output); + DriverAssert.AssertDriverContentsWithFrameAre (bottom, output); tv.Style = new () { TabsSide = TabSide.Left, ShowInitialLine = false, TabsTextAlignment = Alignment.Fill }; tv.ApplyStyleChanges (); @@ -3695,7 +3695,7 @@ Long Text│ │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ───────────────────┐ Tab 1 hi │ @@ -3712,7 +3712,7 @@ Long Text│ │ View.SetClipToScreen (); tv.Draw (); - TestHelpers.AssertDriverContentsWithFrameAre ( + DriverAssert.AssertDriverContentsWithFrameAre ( @" ┌─────────────────── │hi Tab 1