diff --git a/Terminal.Gui/Views/TabView/Tab.cs b/Terminal.Gui/Views/TabView/Tab.cs index 52fb0bdf9d..8451b74136 100644 --- a/Terminal.Gui/Views/TabView/Tab.cs +++ b/Terminal.Gui/Views/TabView/Tab.cs @@ -22,6 +22,7 @@ public string DisplayText set { _displayText = value; + DisplayTextChanged?.Invoke (this, EventArgs.Empty); SetNeedsLayout (); } } @@ -29,4 +30,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; } diff --git a/Terminal.Gui/Views/TabView/TabRow.cs b/Terminal.Gui/Views/TabView/TabRow.cs index 2ca80a69ae..d1a72b4322 100644 --- a/Terminal.Gui/Views/TabView/TabRow.cs +++ b/Terminal.Gui/Views/TabView/TabRow.cs @@ -4,8 +4,8 @@ namespace Terminal.Gui; 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) { @@ -13,40 +13,48 @@ public TabRow (TabView host) Id = "tabRow"; CanFocus = true; + + // Because TabRow has focusable subviews, it must be a TabGroup TabStop = TabBehavior.TabGroup; Width = Dim.Fill (); - _rightScrollIndicator = new View + _rightDownScrollIndicator = new () { - 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 () { - 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 (_rightDownScrollIndicator, _leftUpScrollIndicator); + } + + /// + public override void EndInit () + { + _host._tabLocations = _host.CalculateViewport (Viewport); - Add (_rightScrollIndicator, _leftScrollIndicator); + base.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) @@ -74,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; } @@ -101,7 +109,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) @@ -117,16 +125,23 @@ protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocus /// protected override void OnSubviewLayout (LayoutEventArgs args) { - _host._tabLocations = _host.CalculateViewport (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 (); base.OnSubviewLayout (args); } - /// + /// protected override bool OnRenderingLineCanvas () { RenderTabLineCanvas (); @@ -149,7 +164,6 @@ private void RenderTabLineCanvas () { View tab = tabLocations [i]; Rectangle vts = tab.ViewportToScreen (tab.Viewport); - int selectedOffset = _host.Style.ShowTopLine && tabLocations [i] == _host.SelectedTab ? 0 : 1; if (tabLocations [i] == _host.SelectedTab) { @@ -157,595 +171,1272 @@ private void RenderTabLineCanvas () if (i == 0 && _host.TabScrollOffset == 0) { - if (_host.Style.TabsOnBottom) - { - // Upper left vertical line - lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), - -1, - Orientation.Vertical, - tab.BorderStyle - ); - } - else + switch (_host.Style.TabsSide) { - // Lower left vertical line - lc.AddLine ( - new Point (vts.X - 1, vts.Bottom - selectedOffset), - -1, - Orientation.Vertical, - tab.BorderStyle - ); + case TabSide.Top: + // Lower left vertical line + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + -1, + Orientation.Vertical, + tab.BorderStyle + ); + + break; + case TabSide.Bottom: + // Upper left vertical line + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + -1, + Orientation.Vertical, + tab.BorderStyle + ); + + break; + case TabSide.Left: + // Upper horizontal line + lc.AddLine ( + new (vts.Right, vts.Y - 1), + -1, + Orientation.Horizontal, + tab.BorderStyle + ); + + 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 (); } } 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) + switch (_host.Style.TabsSide) { - if (_host.Style.TabsOnBottom) - { - // Lower left tee + case TabSide.Top: + // LRCorner 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), - 0, + new (vts.X - 1, vts.Bottom), + -1, Orientation.Horizontal, tab.BorderStyle ); - } - else - { - // Upper left tee + + break; + 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), - 0, + new (vts.X - 1, vts.Y - 1), + -1, Orientation.Horizontal, tab.BorderStyle ); + + 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: + 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 (); + } + + if (_host.Style.ShowInitialLine) + { + switch (_host.Style.TabsSide) + { + case TabSide.Top: + // 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 + ); + + break; + case TabSide.Bottom: + // 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 + ); + + 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: + // 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 (); } } } if (i < tabLocations.Length - 1) { - if (_host.Style.ShowTopLine) + if (_host.Style.ShowInitialLine) { - if (_host.Style.TabsOnBottom) + switch (_host.Style.TabsSide) { - // Lower right tee + case TabSide.Top: + // 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.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: + // 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: + // 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 (); + } + } + } + + switch (_host.Style.TabsSide) + { + case TabSide.Top: + //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.Bottom: + //URCorner + lc.AddLine ( + new (vts.Right, vts.Y - 1), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + lc.AddLine ( + new (vts.Right, vts.Y - 1), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Left: + if (Frame.Bottom > tab.Frame.Bottom) + { + //LRCorner lc.AddLine ( - new Point (vts.Right, vts.Bottom), - -1, + new (vts.Right, vts.Bottom), + 1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.Right, vts.Bottom), - 0, + new (vts.Right, vts.Bottom), + -1, Orientation.Horizontal, tab.BorderStyle ); } - else + + break; + case TabSide.Right: + if (Frame.Bottom > tab.Frame.Bottom) { - // Upper right tee + //LRCorner lc.AddLine ( - new Point (vts.Right, vts.Y - 1), + new (vts.X - 1, vts.Bottom), 1, Orientation.Vertical, tab.BorderStyle ); lc.AddLine ( - new Point (vts.Right, vts.Y - 1), - 0, + new (vts.X - 1, vts.Bottom), + 1, Orientation.Horizontal, tab.BorderStyle ); } - } - } - if (_host.Style.TabsOnBottom) - { - //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 - ); - } - else - { - //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 - ); + 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.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 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 ); - } - // ULCorner - lc.AddLine ( - new Point (vts.X - 1, vts.Y - 1), - 1, - Orientation.Vertical, - tab.BorderStyle - ); + break; + case TabSide.Bottom: + if (_host.Style.ShowInitialLine) + { + // 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 + ); + } - 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), + 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 ); - } - // LLCorner - lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), - -1, - Orientation.Vertical, - tab.BorderStyle - ); + 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 + ); + } - lc.AddLine ( - new Point (vts.X - 1, vts.Bottom), - 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; + default: + throw new ArgumentOutOfRangeException (); } } else if (i > 0) { - if (_host.Style.ShowTopLine || _host.Style.TabsOnBottom) + switch (_host.Style.TabsSide) { - // 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 - ); - } + 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 + ); + } - // 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 - ); - } - } - else if (i < tabLocations.Length - 1) - { - if (_host.Style.ShowTopLine) - { - // 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 - ); - } + // Lower left tee + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + -1, + Orientation.Vertical, + tab.BorderStyle + ); - if (_host.Style.ShowTopLine || !_host.Style.TabsOnBottom) - { - // 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 - ); - } - else - { - // 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 - ); - } - } + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); - if (i == 0 && i != selectedTab && _host is { TabScrollOffset: 0, Style.ShowBorder: true }) - { - if (_host.Style.TabsOnBottom) - { - // 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 - ); - } - 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 - ); - } - } + 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 + ); + } - if (i == tabLocations.Length - 1 && i != selectedTab) - { - if (_host.Style.TabsOnBottom) - { - // 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 - ); - } - 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 - ); - } - } + // Lower left tee + lc.AddLine ( + new (vts.Right, vts.Y - 1), + 0, + Orientation.Vertical, + tab.BorderStyle + ); - if (i == tabLocations.Length - 1) - { - var arrowOffset = 1; + lc.AddLine ( + new (vts.Right, vts.Y - 1), + -1, + Orientation.Horizontal, + tab.BorderStyle + ); - int lastSelectedTab = !_host.Style.ShowTopLine && i == selectedTab ? 1 : - _host.Style.TabsOnBottom ? 1 : 0; - Rectangle tabsBarVts = ViewportToScreen (Viewport); - int lineLength = tabsBarVts.Right - vts.Right; + 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 + ); + } - // Right horizontal line - if (ShouldDrawRightScrollIndicator ()) - { - if (lineLength - arrowOffset > 0) - { - if (_host.Style.TabsOnBottom) - { + // Lower left tee lc.AddLine ( - new Point (vts.Right, vts.Y - lastSelectedTab), - lineLength - arrowOffset, - Orientation.Horizontal, + new (vts.X - 1, vts.Y - 1), + 0, + Orientation.Vertical, tab.BorderStyle ); - } - else - { + lc.AddLine ( - new Point ( - vts.Right, - vts.Bottom - lastSelectedTab - ), - lineLength - arrowOffset, + new (vts.X - 1, vts.Y - 1), + 1, Orientation.Horizontal, tab.BorderStyle ); - } + + break; + default: + throw new ArgumentOutOfRangeException (); } } - else + } + else if (i < tabLocations.Length - 1) + { + if (_host.Style.ShowInitialLine) { - // Right corner - if (_host.Style.TabsOnBottom) + switch (_host.Style.TabsSide) { - lc.AddLine ( - new Point (vts.Right, vts.Y - lastSelectedTab), - lineLength, - Orientation.Horizontal, - tab.BorderStyle - ); + 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: + // 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 (); } - else + } + + if (_host.Style.ShowInitialLine) + { + switch (_host.Style.TabsSide) { - lc.AddLine ( - new Point (vts.Right, vts.Bottom - lastSelectedTab), - lineLength, - Orientation.Horizontal, - tab.BorderStyle - ); - } + case TabSide.Top: + case TabSide.Bottom: + // Lower right tee + lc.AddLine ( + new (vts.Right, vts.Bottom), + -1, + Orientation.Vertical, + tab.BorderStyle + ); - if (_host.Style.ShowBorder) + 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: + // 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 (); + } + } + else + { + switch (_host.Style.TabsSide) { - if (_host.Style.TabsOnBottom) - { - // More LRCorner + case TabSide.Top: + // Lower right tee lc.AddLine ( - new Point ( - tabsBarVts.Right - 1, - vts.Y - lastSelectedTab - ), + new (vts.Right, vts.Bottom), -1, Orientation.Vertical, tab.BorderStyle ); - } - else - { - // More URCorner + lc.AddLine ( - new Point ( - tabsBarVts.Right - 1, - vts.Bottom - lastSelectedTab - ), + 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: + // 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 (); } } } - } - _host.LineCanvas.Merge (lc); - } + if (i == 0 && i != selectedTab && _host is { TabScrollOffset: 0, Style.ShowBorder: true }) + { + switch (_host.Style.TabsSide) + { + case TabSide.Top: + // Lower left vertical line + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 0, + Orientation.Vertical, + tab.BorderStyle + ); - private int GetUnderlineYPosition () - { - if (_host.Style.TabsOnBottom) - { - return 0; - } + lc.AddLine ( + new (vts.X - 1, vts.Bottom), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); - return _host.Style.ShowTopLine ? 2 : 1; - } + break; + case TabSide.Bottom: + // Upper left vertical line + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + 0, + Orientation.Vertical, + tab.BorderStyle + ); - /// Renders the line with the tab names in it. - private void RenderTabLine () - { - if (_host._tabLocations is null) - { - return; - } + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + 1, + Orientation.Horizontal, + tab.BorderStyle + ); - View? selected = null; - int topLine = _host.Style.ShowTopLine ? 1 : 0; + break; + case TabSide.Left: + // Upper horizontal line + lc.AddLine ( + new (vts.Right, vts.Y - 1), + 1, + Orientation.Vertical, + tab.BorderStyle + ); - foreach (Tab toRender in _host._tabLocations) - { - Tab tab = toRender; + lc.AddLine ( + new (vts.Right, vts.Y - 1), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); - if (toRender == _host.SelectedTab) - { - selected = tab; + break; + case TabSide.Right: + // Upper horizontal line + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + 1, + Orientation.Vertical, + tab.BorderStyle + ); - if (_host.Style.TabsOnBottom) - { - 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); + lc.AddLine ( + new (vts.X - 1, vts.Y - 1), + 0, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + default: + throw new ArgumentOutOfRangeException (); } } - else if (selected is null) + + if (i == tabLocations.Length - 1 && i != selectedTab) { - if (_host.Style.TabsOnBottom) + switch (_host.Style.TabsSide) { - tab.Border!.Thickness = new (1, 1, 1, topLine); - tab.Margin!.Thickness = new (0, 0, 0, 0); - } - else - { - tab.Border!.Thickness = new (1, topLine, 1, 1); - tab.Margin!.Thickness = new (0, 0, 0, 0); + 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: + 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: + 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 (); } } - else + + if (i == tabLocations.Length - 1) { - if (_host.Style.TabsOnBottom) + var arrowOffset = 1; + + Rectangle tabsBarVts = ViewportToScreen (Viewport); + int lineLength; + + switch (_host.Style.TabsSide) { - tab.Border!.Thickness = new (1, 1, 1, topLine); - tab.Margin!.Thickness = new (0, 0, 0, 0); + case TabSide.Top: + case TabSide.Bottom: + lineLength = tabsBarVts.Right - vts.Right; + + break; + case TabSide.Left: + case TabSide.Right: + lineLength = tabsBarVts.Bottom - vts.Bottom; + + break; + default: + throw new ArgumentOutOfRangeException (); + } + + // Right horizontal/vertical line + if (ShouldDrawRightDownScrollIndicator ()) + { + if (lineLength - arrowOffset > 0) + { + switch (_host.Style.TabsSide) + { + case TabSide.Top: + lc.AddLine ( + new ( + vts.Right, + vts.Bottom + ), + lineLength - arrowOffset, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Bottom: + lc.AddLine ( + + new (vts.Right, vts.Y - 1), + lineLength - arrowOffset, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Left: + lc.AddLine ( + new ( + vts.Right, + vts.Bottom + ), + lineLength - arrowOffset, + Orientation.Vertical, + tab.BorderStyle + ); + + break; + case TabSide.Right: + lc.AddLine ( + new ( + vts.X - 1, + vts.Bottom + ), + lineLength - arrowOffset, + Orientation.Vertical, + tab.BorderStyle + ); + + break; + default: + throw new ArgumentOutOfRangeException (); + } + } } else { - tab.Border!.Thickness = new (1, topLine, 1, 1); - tab.Margin!.Thickness = new (0, 0, 0, 0); + // Right corner + switch (_host.Style.TabsSide) + { + case TabSide.Top: + lc.AddLine ( + new (vts.Right, vts.Bottom), + lineLength, + Orientation.Horizontal, + tab.BorderStyle + ); + + break; + case TabSide.Bottom: + lc.AddLine ( + + new (vts.Right, vts.Y - 1), + lineLength, + Orientation.Horizontal, + tab.BorderStyle + ); + + 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: + 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 (); + } + + if (_host.Style.ShowBorder) + { + switch (_host.Style.TabsSide) + { + case TabSide.Top: + // More URCorner + lc.AddLine ( + new ( + tabsBarVts.Right - 1, + vts.Bottom + ), + 1, + Orientation.Vertical, + tab.BorderStyle + ); + + break; + case TabSide.Bottom: + // More LRCorner + lc.AddLine ( + new ( + tabsBarVts.Right - 1, + vts.Y - 1 + ), + -1, + Orientation.Vertical, + tab.BorderStyle + ); + + 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: + 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 (); + } + } } } - - // Ensures updating TextFormatter constrains - tab.TextFormatter.ConstrainToWidth = tab.GetContentSize ().Width; - tab.TextFormatter.ConstrainToHeight = tab.GetContentSize ().Height; } + + LineCanvas.Merge (lc); } /// Renders the line of the tab that adjoins the content of the tab. private void RenderUnderline () { - int y = GetUnderlineYPosition (); - Tab? selected = _host._tabLocations?.FirstOrDefault (t => t == _host.SelectedTab); if (selected is null) @@ -753,42 +1444,99 @@ 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: + _leftUpScrollIndicator.X = 0; + _leftUpScrollIndicator.Y = Pos.AnchorEnd (1); + + break; + case TabSide.Bottom: + case TabSide.Right: + _leftUpScrollIndicator.X = 0; + _leftUpScrollIndicator.Y = 0; + + break; + case TabSide.Left: + _leftUpScrollIndicator.X = Pos.AnchorEnd (1); + _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.Left: + _rightDownScrollIndicator.X = Pos.AnchorEnd (1); + _rightDownScrollIndicator.Y = Pos.AnchorEnd (1); + + break; + case TabSide.Bottom: + _rightDownScrollIndicator.X = Pos.AnchorEnd (1); + _rightDownScrollIndicator.Y = 0; + + break; + case TabSide.Right: + _rightDownScrollIndicator.X = 0; + _rightDownScrollIndicator.Y = Pos.AnchorEnd (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); + MoveSubviewToEnd (_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 (); } } 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 85404d05d9..74107d2c1e 100644 --- a/Terminal.Gui/Views/TabView/TabStyle.cs +++ b/Terminal.Gui/Views/TabView/TabStyle.cs @@ -1,4 +1,5 @@ -๏ปฟnamespace Terminal.Gui; +๏ปฟ#nullable enable +namespace Terminal.Gui; /// Describes render stylistic selections of a public class TabStyle @@ -7,12 +8,21 @@ 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; - /// 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; } + + /// + /// 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 73f286b564..6a910a16f1 100644 --- a/Terminal.Gui/Views/TabView/TabView.cs +++ b/Terminal.Gui/Views/TabView/TabView.cs @@ -27,8 +27,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 (); @@ -36,9 +36,53 @@ public TabView () base.Add (_containerView); // Things this view knows how to do - AddCommand (Command.Left, () => 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); + } - AddCommand (Command.Right, () => SwitchTabBy (1)); + return false; + }); + + AddCommand ( + Command.Down, + () => + { + if (Style.TabsSide is TabSide.Left or TabSide.Right) + { + return SwitchTabBy (1); + } + + return false; + }); AddCommand ( Command.LeftStart, @@ -87,6 +131,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); @@ -122,6 +168,7 @@ public Tab? SelectedTab if (_selectedTab.View is { }) { _selectedTab.View.CanFocusChanged -= ContainerViewCanFocus!; + // remove old content _containerView.Remove (_selectedTab.View); } @@ -149,19 +196,14 @@ public Tab? SelectedTab OnSelectedTabChanged (old!, _selectedTab!); } + SetNeedsLayout (); } } - private bool TabCanSetFocus () - { - return IsInitialized && SelectedTab is { } && (_selectedTabHasFocus || !_containerView.CanFocus); - } + private bool TabCanSetFocus () { return IsInitialized && SelectedTab is { } && (_selectedTabHasFocus || !_containerView.CanFocus); } - private void ContainerViewCanFocus (object sender, EventArgs eventArgs) - { - _containerView.CanFocus = _containerView.Subviews.Count (v => v.CanFocus) > 0; - } + private void ContainerViewCanFocus (object sender, EventArgs eventArgs) { _containerView.CanFocus = _containerView.Subviews.Count (v => v.CanFocus) > 0; } private TabStyle _style = new (); @@ -176,6 +218,7 @@ public TabStyle Style { return; } + _style = value; SetNeedsLayout (); } @@ -229,60 +272,102 @@ 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 (); - if (Style.TabsOnBottom) + 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 (1, 0, 1, 1); + } - _containerView.Y = 0; + _tabsBar.X = 0; + _tabsBar.Y = 0; + _tabsBar.Width = Dim.Fill (); + _tabsBar.Height = GetTabHeight (true); - int tabHeight = GetTabHeight (false); + _containerView.X = 0; - // 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); + _containerView.Width = Dim.Fill (); + _containerView.Height = Dim.Fill (); - _tabsBar.Height = tabHeight; + break; + case TabSide.Bottom: + // Tabs are along the bottom so just dodge the border + if (Style.ShowBorder) + { + _containerView.Border!.Thickness = new (1, 1, 1, 0); + } - _tabsBar.Y = Pos.Bottom (_containerView); - } - else - { - // Tabs are along the top - if (Style.ShowBorder) - { - _containerView.Border!.Thickness = new Thickness (1, 0, 1, 1); - } + _tabsBar.X = 0; + _tabsBar.Width = Dim.Fill (); + int tabHeight = GetTabHeight (false); + _tabsBar.Height = tabHeight; - _tabsBar.Y = 0; + _containerView.X = 0; + _containerView.Y = 0; + _containerView.Width = Dim.Fill (); - int 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.Y = Pos.Bottom (_containerView); - // Fill client area leaving space at bottom for border - _containerView.Height = Dim.Fill (); + break; + case TabSide.Left: + // Tabs are along the left + if (Style.ShowBorder) + { + _containerView.Border!.Thickness = new (0, 1, 1, 1); + } - // The top tab should be 2 or 3 rows high and on the top + _tabsBar.X = 0; + _tabsBar.Y = 0; + _tabsBar.Height = Dim.Fill (); - _tabsBar.Height = tabHeight; + //move content right to make space for tabs + _containerView.X = Pos.Right (_tabsBar); + _containerView.Y = 0; - // Should be able to just use 0 but switching between top/bottom tabs repeatedly breaks in ValidatePosDim if just using the absolute value 0 + // Fill client area leaving space at left for tabs + _containerView.Width = Dim.Fill (); + _containerView.Height = Dim.Fill (); + + 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 (); } SetNeedsLayout (); } - /// + /// protected override void OnViewportChanged (DrawEventArgs e) { - _tabLocations = CalculateViewport (Viewport).ToArray (); + _tabLocations = CalculateViewport (Viewport); base.OnViewportChanged (e); } @@ -296,10 +381,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); } } @@ -309,7 +399,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) @@ -388,6 +478,7 @@ public bool SwitchTabBy (int amount) if (currentIdx == -1) { SelectedTab = Tabs.ElementAt (0); + return true; } @@ -430,88 +521,207 @@ 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. /// - 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.ShowTopLine ? 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.ShowTopLine ? 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: + var maxColWidth = 0; + + // Starting at the first or scrolled to tab + foreach (Tab tab in Tabs.Skip (TabScrollOffset)) + { + tab.X = 0; + + 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)); + + 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 - 2, 2)); + + tab.Height = 2; + tab.TextAlignment = Style.TabsTextAlignment; + + // if tab view is height <= 3 don't render any tabs + if (maxHeight == 0) + { + tab.Width = 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; + } + + maxColWidth = GetMaxColWidth (tabTextWidth); + tab.Height = 3; + + // 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; + + if (Style.TabsSide == TabSide.Right) + { + _tabsBar.X = Pos.AnchorEnd (maxColWidth); + // Fill client area leaving space at right for tabs + _containerView.Width = Dim.Fill (maxColWidth); + } - // there is enough space! - tab.Visible = true; - tab.MouseClick += Tab_MouseClick!; - tab.Border!.MouseClick += Tab_MouseClick!; + int GetMaxColWidth (int textWidth) + { + int maxViewportWidth = Math.Max (0, Viewport.Width - (Style.ShowBorder ? 2 : 0)); - yield return tab; + if (Math.Max (textWidth + (Style.ShowInitialLine ? 2 : 1), maxColWidth) > maxViewportWidth) + { + return maxViewportWidth; + } - prevTab = tab; + return Math.Max (textWidth + (Style.ShowInitialLine ? 2 : 1), maxColWidth); + } - i += tabTextWidth + 1; + break; + default: + throw new ArgumentOutOfRangeException (); } if (TabCanSetFocus ()) @@ -522,41 +732,121 @@ 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; + } + + int topLine = Style.ShowInitialLine ? 1 : 0; + + foreach (Tab toRender in tabLocations) + { + Tab tab = toRender; + + if (toRender == SelectedTab) + { + switch (Style.TabsSide) + { + case TabSide.Top: + tab.Border!.Thickness = new (1, topLine, 1, 0); + tab.Margin!.Thickness = new (0, 0, 0, 1); + + 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, 1, 0); + + 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 (); + } + } + else + { + tab.Margin!.Thickness = new (0, 0, 0, 0); + + switch (Style.TabsSide) + { + case TabSide.Top: + tab.Border!.Thickness = new (1, topLine, 1, 1); + + break; + case TabSide.Bottom: + tab.Border!.Thickness = new (1, 1, 1, topLine); + + break; + case TabSide.Left: + tab.Border!.Thickness = new (topLine, 1, 1, 1); + + break; + case TabSide.Right: + tab.Border!.Thickness = new (1, 1, topLine, 1); + + break; + default: + throw new ArgumentOutOfRangeException (); + } + } + + // Ensures updating TextFormatter constrains + tab.TextFormatter.ConstrainToWidth = tab.GetContentSize ().Width; + tab.TextFormatter.ConstrainToHeight = tab.GetContentSize ().Height; + } } /// - /// Returns the number of rows occupied by rendering the tabs, this depends on - /// and can be 0 (e.g. if and you ask for ). + /// 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. /// . /// 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; } - return Style.ShowTopLine ? 3 : 2; + 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); @@ -564,6 +854,7 @@ private void UnSetCurrentTabs () { tab.MouseClick -= Tab_MouseClick!; tab.Border!.MouseClick -= Tab_MouseClick!; + tab.DisplayTextChanged -= Tab_DisplayTextChanged; tab.Visible = false; } } @@ -574,6 +865,7 @@ private void UnSetCurrentTabs () { tabToRender.MouseClick -= Tab_MouseClick!; tabToRender.Border!.MouseClick -= Tab_MouseClick!; + tabToRender.DisplayTextChanged -= Tab_DisplayTextChanged; tabToRender.Visible = false; } @@ -584,6 +876,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/UICatalog/Scenarios/TabViewExample.cs b/UICatalog/Scenarios/TabViewExample.cs index 9d48c05685..6a6f574d99 100644 --- a/UICatalog/Scenarios/TabViewExample.cs +++ b/UICatalog/Scenarios/TabViewExample.cs @@ -1,4 +1,6 @@ -๏ปฟusing System.Linq; +๏ปฟusing System; +using System.Collections.Generic; +using System.Linq; using System.Text; using Terminal.Gui; @@ -12,7 +14,10 @@ public class TabViewExample : Scenario private MenuItem _miShowBorder; private MenuItem _miShowTabViewBorder; private MenuItem _miShowTopLine; - private MenuItem _miTabsOnBottom; + private MenuItem [] _miTabsSide; + private MenuItem _cachedTabsSide; + private MenuItem [] _miTabsTextAlignment; + private MenuItem _cachedTabsTextAlignment; private TabView _tabView; public override void Main () @@ -23,6 +28,9 @@ public override void Main () // Setup - Create a top-level application window and configure it. Toplevel appWindow = new (); + _miTabsSide = SetTabsSide (); + _miTabsTextAlignment = SetTabsTextAlignment (); + var menu = new MenuBar { Menus = @@ -54,17 +62,23 @@ 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", "", ShowTabViewBorder - ) { Checked = true, CheckType = MenuItemCheckStyle.Checked } + ) { Checked = true, CheckType = MenuItemCheckStyle.Checked }, + null, + _miTabsTextAlignment [0], + _miTabsTextAlignment [1], + _miTabsTextAlignment [2], + _miTabsTextAlignment [3] } ) ] @@ -241,12 +255,78 @@ 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 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 () @@ -271,7 +351,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/UnitTests/Views/TabViewTests.cs b/UnitTests/Views/TabViewTests.cs index ac12a55564..ef39655b6b 100644 --- a/UnitTests/Views/TabViewTests.cs +++ b/UnitTests/Views/TabViewTests.cs @@ -378,8 +378,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 @@ -584,12 +584,12 @@ public void SelectedTabChanged_Called () [Fact] [SetupFakeDriver] - public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width3 () + public void ShowInitialLine_False_TabsSide_Top_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 (); @@ -608,12 +608,12 @@ public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width3 () [Fact] [SetupFakeDriver] - public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width4 () + public void ShowInitialLine_False_TabsSide_Top_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 (); @@ -632,12 +632,12 @@ public void ShowTopLine_False_TabsOnBottom_False_TestTabView_Width4 () [Fact] [SetupFakeDriver] - public void ShowTopLine_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; tv.Height = 5; - tv.Style = new () { ShowTopLine = false }; + tv.Style = new () { ShowInitialLine = false }; tv.ApplyStyleChanges (); // Test two tab names that fit @@ -731,12 +731,12 @@ public void ShowTopLine_False_TabsOnBottom_False_TestThinTabView_WithLongNames ( [Fact] [SetupFakeDriver] - public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width3 () + public void ShowInitialLine_False_TabsSide_Bottom_TestTabView_Width3 () { TabView tv = GetTabView (out _, out _); tv.Width = 3; tv.Height = 5; - tv.Style = new () { ShowTopLine = false, TabsOnBottom = true }; + tv.Style = new () { ShowInitialLine = false, TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); tv.Layout (); @@ -755,12 +755,12 @@ public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width3 () [Fact] [SetupFakeDriver] - public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width4 () + public void ShowInitialLine_False_TabsSide_Bottom_TestTabView_Width4 () { TabView tv = GetTabView (out _, out _); tv.Width = 4; tv.Height = 5; - tv.Style = new () { ShowTopLine = false, TabsOnBottom = true }; + tv.Style = new () { ShowInitialLine = false, TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); tv.Layout (); @@ -779,12 +779,12 @@ public void ShowTopLine_False_TabsOnBottom_True_TestTabView_Width4 () [Fact] [SetupFakeDriver] - public void ShowTopLine_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; tv.Height = 5; - tv.Style = new () { ShowTopLine = false, TabsOnBottom = true }; + tv.Style = new () { ShowInitialLine = false, TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); tv.Layout (); @@ -880,7 +880,7 @@ public void ShowTopLine_False_TabsOnBottom_True_TestThinTabView_WithLongNames () [Fact] [SetupFakeDriver] - public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width3 () + public void ShowInitialLine_True_TabsSide_Top_TestTabView_Width3 () { TabView tv = GetTabView (out _, out _); tv.Width = 3; @@ -902,7 +902,7 @@ public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width3 () [Fact] [SetupFakeDriver] - public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width4 () + public void ShowInitialLine_True_TabsSide_Top_TestTabView_Width4 () { TabView tv = GetTabView (out _, out _); tv.Width = 4; @@ -925,7 +925,7 @@ public void ShowTopLine_True_TabsOnBottom_False_TestTabView_Width4 () [Fact] [SetupFakeDriver] - public void ShowTopLine_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; @@ -1022,7 +1022,7 @@ public void ShowTopLine_True_TabsOnBottom_False_TestThinTabView_WithLongNames () [Fact] [SetupFakeDriver] - public void ShowTopLine_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; @@ -1064,12 +1064,12 @@ public void ShowTopLine_True_TabsOnBottom_False_With_Unicode () [Fact] [SetupFakeDriver] - public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width3 () + public void ShowInitialLine_True_TabsSide_Bottom_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 (); @@ -1088,12 +1088,12 @@ public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width3 () [Fact] [SetupFakeDriver] - public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width4 () + public void ShowInitialLine_True_TabsSide_Bottom_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 (); @@ -1112,12 +1112,12 @@ public void ShowTopLine_True_TabsOnBottom_True_TestTabView_Width4 () [Fact] [SetupFakeDriver] - public void ShowTopLine_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; tv.Height = 5; - tv.Style = new () { TabsOnBottom = true }; + tv.Style = new () { TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); tv.Layout (); @@ -1194,12 +1194,12 @@ public void ShowTopLine_True_TabsOnBottom_True_TestThinTabView_WithLongNames () [Fact] [SetupFakeDriver] - public void ShowTopLine_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; tv.Height = 5; - tv.Style = new () { TabsOnBottom = true }; + tv.Style = new () { TabsSide = TabSide.Bottom }; tv.ApplyStyleChanges (); tab1.DisplayText = "Tab0"; @@ -1237,102 +1237,208 @@ public void ShowTopLine_True_TabsOnBottom_True_With_Unicode () } [Fact] - public void SwitchTabBy_NormalUsage () + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Left_TestTabView_Height3 () { - TabView tv = GetTabView (out Tab tab1, out Tab tab2); - - Tab tab3; - Tab tab4; - Tab tab5; + TabView tv = GetTabView (out _, out _); + tv.Width = 5; + tv.Height = 3; + tv.Style = new () { TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); + tv.Layout (); - tv.AddTab (tab3 = new (), false); - tv.AddTab (tab4 = new (), false); - tv.AddTab (tab5 = new (), false); + tv.Draw (); - tv.SelectedTab = tab1; + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +โ•ญโ”€โ”€โ”€โ” +โ”‚T hโ”‚ +โ•ฐโ”€โ–ผโ”€โ”˜", + output + ); + } - var called = 0; - tv.SelectedTabChanged += (s, e) => { called++; }; + [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.SwitchTabBy (1); + tv.Draw (); - Assert.Equal (1, called); - Assert.Equal (tab2, tv.SelectedTab); + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +โ•ญโ”€โ”€โ”€โ” +โ”‚T hโ”‚ +โ•ฐโ”€โ•ฎ โ”‚ + โ–ผโ”€โ”˜", + output + ); + } - //reset called counter - called = 0; + [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 (); - // go right 2 - tv.SwitchTabBy (2); + 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 ( + @" +โ•ญโ”€โ”€โ”€โ” +โ”‚T hโ”‚ +โ•ฐโ”€โ•ฎ โ”‚ + โ”‚ โ”‚ + โ•ฐโ”€โ”˜", + output + ); } [Fact] - public void SwitchTabBy_OutOfTabsRange () + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Left_TestTabView_Height6_Two_Tab () { - TabView tv = GetTabView (out Tab tab1, out Tab tab2); + TabView tv = GetTabView (out _, out Tab tab2); + tv.Width = 5; + tv.Height = 6; + tv.Style = new () { TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); + tv.Layout (); - tv.SelectedTab = tab1; - tv.SwitchTabBy (500); + tv.Draw (); - Assert.Equal (tab2, tv.SelectedTab); + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +โ•ญโ”€โ”€โ”€โ” +โ”‚T hโ”‚ +โ”œโ”€โ•ฎ โ”‚ +โ”‚Tโ”‚ โ”‚ +โ•ฐโ”€โ”ค โ”‚ + โ•ฐโ”€โ”˜", + output + ); - tv.SwitchTabBy (-500); + tv.SelectedTab = tab2; - Assert.Equal (tab1, tv.SelectedTab); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +โ•ญโ”€โ”ฌโ”€โ” +โ”‚Tโ”‚hโ”‚ +โ”œโ”€โ•ฏ โ”‚ +โ”‚T โ”‚ +โ•ฐโ”€โ•ฎ โ”‚ + โ•ฐโ”€โ”˜", + output + ); } [Fact] - public void RemoveTab_ThatHasFocus () + [SetupFakeDriver] + public void ShowInitialLine_True_TabsSide_Left_TestTabView_Height7_Two_Tab () { - TabView tv = GetTabView (out Tab _, out Tab tab2); + TabView tv = GetTabView (out _, out Tab tab2); + tv.Width = 5; + tv.Height = 7; + tv.Style = new () { TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); + tv.Layout (); - tv.SelectedTab = tab2; - tab2.HasFocus = true; + tv.Draw (); - Assert.Equal (2, tv.Tabs.Count); + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +โ•ญโ”€โ”€โ”€โ” +โ”‚T hโ”‚ +โ”œโ”€โ•ฎ โ”‚ +โ”‚Tโ”‚ โ”‚ +โ•ฐโ”€โ”ค โ”‚ + โ”‚ โ”‚ + โ•ฐโ”€โ”˜", + output + ); - foreach (Tab t in tv.Tabs.ToArray ()) - { - tv.RemoveTab (t); - } + tv.SelectedTab = tab2; - Assert.Empty (tv.Tabs); + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +โ•ญโ”€โ”ฌโ”€โ” +โ”‚Tโ”‚hโ”‚ +โ”œโ”€โ•ฏ โ”‚ +โ”‚T โ”‚ +โ•ฐโ”€โ•ฎ โ”‚ + โ”‚ โ”‚ + โ•ฐโ”€โ”˜", + output + ); } [Fact] [SetupFakeDriver] - public void Add_Three_TabsOnTop_ChangesTab () + public void ShowInitialLine_True_TabsSide_Left_TestThinTabView_WithLongNames () { 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.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 (); - Assert.Equal (tab1, tv.SelectedTab); + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚12 hi โ”‚ +โ”œโ”€โ”€โ•ฎ โ”‚ +โ”‚13โ”‚ โ”‚ +โ•ฐโ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”˜", + output + ); - TestHelpers.AssertDriverContentsAre ( - @" -โ•ญโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ•ฎ -โ”‚Tab1โ”‚Tab2โ”‚Tab3โ”‚ -โ”‚ โ•ฐโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ•ฎ -โ”‚hi โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -", - 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 (); @@ -1341,16 +1447,17 @@ public void Add_Three_TabsOnTop_ChangesTab () TestHelpers.AssertDriverContentsWithFrameAre ( @" -โ•ญโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ•ฎ -โ”‚Tab1โ”‚Tab2โ”‚Tab3โ”‚ -โ”œโ”€โ”€โ”€โ”€โ•ฏ โ•ฐโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ•ฎ -โ”‚hi2 โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -", +โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ” +โ”‚123456โ”‚hโ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ โ”‚ +โ”‚13 โ”‚ +โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜", output ); - tv.SelectedTab = tab3; + // now make both tabs too long + tab1.DisplayText = "12345678910"; + tab2.DisplayText = "abcdefghijklmnopq"; tv.Layout (); View.SetClipToScreen (); @@ -1358,47 +1465,41 @@ public void Add_Three_TabsOnTop_ChangesTab () TestHelpers.AssertDriverContentsWithFrameAre ( @" -โ•ญโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ•ฎ -โ”‚Tab1โ”‚Tab2โ”‚Tab3โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ•ฏ โ•ฐโ”€โ”€โ”€โ•ฎ -โ”‚hi3 โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ -", +โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ” +โ”‚123456โ”‚hโ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ โ”‚ +โ”‚abcdef โ”‚ +โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜", output ); } [Fact] [SetupFakeDriver] - public void Add_Three_TabsOnBottom_ChangesTab () + public void ShowInitialLine_True_TabsSide_Left_With_Unicode () { 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 () { TabsOnBottom = true }; + 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 (); - Assert.Equal (tab1, tv.SelectedTab); - - TestHelpers.AssertDriverContentsAre ( - @" -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚hi โ”‚ -โ”‚ โ•ญโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ•ฏ -โ”‚Tab1โ”‚Tab2โ”‚Tab3โ”‚ -โ•ฐโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ•ฏ -", - output - ); + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚Tab0 hi โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ โ”‚ +โ”‚Les Misรฉrablesโ”‚ โ”‚ +โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”˜", + output + ); tv.SelectedTab = tab2; @@ -1408,80 +1509,2218 @@ public void Add_Three_TabsOnBottom_ChangesTab () TestHelpers.AssertDriverContentsWithFrameAre ( @" -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚hi2 โ”‚ -โ”œโ”€โ”€โ”€โ”€โ•ฎ โ•ญโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ•ฏ -โ”‚Tab1โ”‚Tab2โ”‚Tab3โ”‚ -โ•ฐโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ•ฏ -", +โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ” +โ”‚Tab0 โ”‚hi2โ”‚ +โ”œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ โ”‚ +โ”‚Les Misรฉrables โ”‚ +โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜", output ); + } - tv.SelectedTab = tab3; - + [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 (); - View.SetClipToScreen (); + tv.Draw (); TestHelpers.AssertDriverContentsWithFrameAre ( @" -โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” -โ”‚hi3 โ”‚ -โ”œโ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ•ฎ โ•ญโ”€โ”€โ”€โ•ฏ -โ”‚Tab1โ”‚Tab2โ”‚Tab3โ”‚ -โ•ฐโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ•ฏ -", +โ”€โ”€โ”€โ”€โ” +Ta hโ”‚ +โ”€โ”€โ–ผโ”€โ”˜", output ); } [Fact] - [AutoInitShutdown] - public void Tab_Get_Focus_By_Press_F6 () + [SetupFakeDriver] + public void ShowInitialLine_False_TabsSide_Left_TestTabView_Height4 () { - TabView tv = GetTabView (out Tab tab1, out Tab tab2); - - tv.Width = 20; - tv.Height = 5; - - Toplevel top = new (); - top.Add (tv); - Application.Begin (top); + TabView tv = GetTabView (out _, out _); + tv.Width = 5; + tv.Height = 4; + tv.Style = new () { ShowInitialLine = false, TabsSide = TabSide.Left }; + tv.ApplyStyleChanges (); + tv.Layout (); - Assert.False (tab1.HasFocus); + tv.Draw (); - Assert.True (Application.RaiseKeyDownEvent (Key.F6)); - Assert.True (tab1.HasFocus); - top.Dispose (); + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +โ”€โ”€โ”€โ”€โ” +Ta hโ”‚ +โ”€โ”€โ•ฎ โ”‚ + โ–ผโ”€โ”˜", + output + ); } [Fact] [SetupFakeDriver] - public void Mouse_Wheel_Changes_Tab () + public void ShowInitialLine_False_TabsSide_Left_TestTabView_Height5_One_Tab () { - TabView tv = GetTabView (out Tab tab1, out Tab tab2); - - tv.Width = 20; + 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 (); - Toplevel top = new (); - top.Add (tv); - Application.Begin (top); + tv.Draw (); - Assert.False (tab1.HasFocus); + 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] + [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; + + 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.Left }; + tv.ApplyStyleChanges (); + + 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] + [SetupFakeDriver] + public void Add_Three_TabsSide_Left_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.Left, ShowInitialLine = false }; + tv.ApplyStyleChanges (); + + 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] + [SetupFakeDriver] + public void Add_Three_TabsSide_Right_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.Right }; + tv.ApplyStyleChanges (); + + tv.Layout (); + tv.Draw (); + + Assert.Equal (tab1, tv.SelectedTab); + + TestHelpers.AssertDriverContentsAre ( + @" +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ +โ”‚hi Tab1โ”‚ +โ”‚ โ•ญโ”€โ”€โ”€โ”€โ”ค +โ”‚ โ”‚Tab2โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ•ฏ +", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ•ฎ +โ”‚hi2 โ”‚Tab1โ”‚ +โ”‚ โ•ฐโ”€โ”€โ”€โ”€โ”ค +โ”‚ Tab2โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€โ•ฏ +", + output + ); + + tv.SelectedTab = tab3; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ฒโ”€โ”€โ”€โ”€โ•ฎ +โ”‚hi3 Tab3โ”‚ +โ”‚ โ•ญโ”€โ”€โ”€โ”€โ•ฏ +โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ +", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void Add_Three_TabsSide_Right_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.Right, ShowInitialLine = false }; + tv.ApplyStyleChanges (); + + tv.Layout (); + tv.Draw (); + + Assert.Equal (tab1, tv.SelectedTab); + + TestHelpers.AssertDriverContentsAre ( + @" +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ +โ”‚hi Tab1 +โ”‚ โ•ญโ”€โ”€โ”€โ”€ +โ”‚ โ”‚Tab2 +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€ +", + output + ); + + tv.SelectedTab = tab2; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€ +โ”‚hi2 โ”‚Tab1 +โ”‚ โ•ฐโ”€โ”€โ”€โ”€ +โ”‚ Tab2 +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ผโ”€โ”€โ”€โ”€ +", + output + ); + + tv.SelectedTab = tab3; + + tv.Layout (); + View.SetClipToScreen (); + tv.Draw (); + + TestHelpers.AssertDriverContentsWithFrameAre ( + @" +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ–ฒโ”€โ”€โ”€โ”€ +โ”‚hi3 Tab3 +โ”‚ โ•ญโ”€โ”€โ”€โ”€ +โ”‚ โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ +", + output + ); + } + + [Fact] + [SetupFakeDriver] + public void Add_Three_TabsSide_Right_ShowInitialLine_True_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 }; + 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_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 + ); + } + + [Fact] + [AutoInitShutdown] + public void Tab_Get_Focus_By_Press_F6 () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + + tv.Width = 20; + tv.Height = 5; + + Toplevel top = new (); + top.Add (tv); + Application.Begin (top); + + Assert.False (tab1.HasFocus); + + Assert.True (Application.RaiseKeyDownEvent (Key.F6)); + Assert.True (tab1.HasFocus); + top.Dispose (); + } + + [Fact] + [SetupFakeDriver] + public void Mouse_Wheel_Changes_Tab () + { + TabView tv = GetTabView (out Tab tab1, out Tab tab2); + + tv.Width = 20; + tv.Height = 5; + + Toplevel top = new (); + top.Add (tv); + Application.Begin (top); + + Assert.False (tab1.HasFocus); Application.RaiseMouseEvent (new () { Position = new (1, 1), Flags = MouseFlags.WheeledDown }); Assert.True (tab2.HasFocus); - Application.RaiseMouseEvent (new () { Position = new (1, 1), Flags = MouseFlags.WheeledUp }); - Assert.True (tab1.HasFocus); + Application.RaiseMouseEvent (new () { Position = new (1, 1), Flags = MouseFlags.WheeledUp }); + Assert.True (tab1.HasFocus); + + Application.RaiseMouseEvent (new () { Position = new (1, 1), Flags = MouseFlags.WheeledRight }); + Assert.True (tab2.HasFocus); + + Application.RaiseMouseEvent (new () { Position = new (1, 1), Flags = MouseFlags.WheeledLeft }); + Assert.True (tab1.HasFocus); + 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 + ); - Application.RaiseMouseEvent (new () { Position = new (1, 1), Flags = MouseFlags.WheeledRight }); - Assert.True (tab2.HasFocus); + Assert.Equal (Alignment.Start, tv.Style.TabsTextAlignment); - Application.RaiseMouseEvent (new () { Position = new (1, 1), Flags = MouseFlags.WheeledLeft }); - Assert.True (tab1.HasFocus); - top.Dispose (); + 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 _); }