diff --git a/Terminal.Gui/Views/TableView.cs b/Terminal.Gui/Views/TableView.cs index 1082f68c5..07c563e11 100644 --- a/Terminal.Gui/Views/TableView.cs +++ b/Terminal.Gui/Views/TableView.cs @@ -60,6 +60,9 @@ namespace Terminal.Gui { private TableStyle style = new TableStyle (); private Key cellActivationKey = Key.Enter; + Point? scrollLeftPoint; + Point? scrollRightPoint; + /// /// The default maximum cell width for and /// @@ -261,6 +264,9 @@ namespace Terminal.Gui { Move (0, 0); var frame = Frame; + scrollRightPoint = null; + scrollLeftPoint = null; + // What columns to render at what X offset in viewport var columnsToRender = CalculateViewport (bounds).ToArray (); @@ -420,11 +426,25 @@ namespace Terminal.Gui { for (int c = 0; c < availableWidth; c++) { + // Start by assuming we just draw a straight line the + // whole way but update to instead draw a header indicator + // or scroll arrow etc var rune = Driver.HLine; if (Style.ShowVerticalHeaderLines) { if (c == 0) { + // for first character render line rune = Style.ShowVerticalCellLines ? Driver.LeftTee : Driver.LLCorner; + + // unless we have horizontally scrolled along + // in which case render an arrow, to indicate user + // can scroll left + if(Style.ShowHorizontalScrollIndicators && ColumnOffset > 0) + { + rune = Driver.LeftArrow; + scrollLeftPoint = new Point(c,row); + } + } // if the next column is the start of a header else if (columnsToRender.Any (r => r.X == c + 1)) { @@ -432,7 +452,20 @@ namespace Terminal.Gui { /*TODO: is ┼ symbol in Driver?*/ rune = Style.ShowVerticalCellLines ? '┼' : Driver.BottomTee; } else if (c == availableWidth - 1) { + + // for the last character in the table rune = Style.ShowVerticalCellLines ? Driver.RightTee : Driver.LRCorner; + + // unless there is more of the table we could horizontally + // scroll along to see. In which case render an arrow, + // to indicate user can scroll right + if(Style.ShowHorizontalScrollIndicators && + ColumnOffset + columnsToRender.Length < Table.Columns.Count) + { + rune = Driver.RightArrow; + scrollRightPoint = new Point(c,row); + } + } // if the next console column is the lastcolumns end else if (Style.ExpandLastColumn == false && @@ -898,6 +931,24 @@ namespace Terminal.Gui { if (me.Flags.HasFlag (MouseFlags.Button1Clicked)) { + if (scrollLeftPoint != null + && scrollLeftPoint.Value.X == me.X + && scrollLeftPoint.Value.Y == me.Y) + { + ColumnOffset--; + EnsureValidScrollOffsets (); + SetNeedsDisplay (); + } + + if (scrollRightPoint != null + && scrollRightPoint.Value.X == me.X + && scrollRightPoint.Value.Y == me.Y) + { + ColumnOffset++; + EnsureValidScrollOffsets (); + SetNeedsDisplay (); + } + var hit = ScreenToCell (me.X, me.Y); if (hit != null) { @@ -1369,6 +1420,14 @@ namespace Terminal.Gui { /// public bool ShowVerticalHeaderLines { get; set; } = true; + /// + /// True to render a arrows on the right/left of the table when + /// there are more column(s) that can be scrolled to. Requires + /// to be true. + /// Defaults to true + /// + public bool ShowHorizontalScrollIndicators { get; set; } = true; + /// /// True to invert the colors of the first symbol of the selected cell in the . /// This gives the appearance of a cursor for when the doesn't otherwise show diff --git a/UICatalog/Scenarios/TableEditor.cs b/UICatalog/Scenarios/TableEditor.cs index 224837f74..988bae7fd 100644 --- a/UICatalog/Scenarios/TableEditor.cs +++ b/UICatalog/Scenarios/TableEditor.cs @@ -20,6 +20,7 @@ namespace UICatalog.Scenarios { private MenuItem miHeaderOverline; private MenuItem miHeaderMidline; private MenuItem miHeaderUnderline; + private MenuItem miShowHorizontalScrollIndicators; private MenuItem miCellLines; private MenuItem miFullRowSelect; private MenuItem miExpandLastColumn; @@ -55,7 +56,8 @@ namespace UICatalog.Scenarios { miAlwaysShowHeaders = new MenuItem ("_AlwaysShowHeaders", "", () => ToggleAlwaysShowHeader()){Checked = tableView.Style.AlwaysShowHeaders, CheckType = MenuItemCheckStyle.Checked }, miHeaderOverline = new MenuItem ("_HeaderOverLine", "", () => ToggleOverline()){Checked = tableView.Style.ShowHorizontalHeaderOverline, CheckType = MenuItemCheckStyle.Checked }, miHeaderMidline = new MenuItem ("_HeaderMidLine", "", () => ToggleHeaderMidline()){Checked = tableView.Style.ShowVerticalHeaderLines, CheckType = MenuItemCheckStyle.Checked }, - miHeaderUnderline =new MenuItem ("_HeaderUnderLine", "", () => ToggleUnderline()){Checked = tableView.Style.ShowHorizontalHeaderUnderline, CheckType = MenuItemCheckStyle.Checked }, + miHeaderUnderline = new MenuItem ("_HeaderUnderLine", "", () => ToggleUnderline()){Checked = tableView.Style.ShowHorizontalHeaderUnderline, CheckType = MenuItemCheckStyle.Checked }, + miShowHorizontalScrollIndicators = new MenuItem ("_HorizontalScrollIndicators", "", () => ToggleHorizontalScrollIndicators()){Checked = tableView.Style.ShowHorizontalScrollIndicators, CheckType = MenuItemCheckStyle.Checked }, miFullRowSelect =new MenuItem ("_FullRowSelect", "", () => ToggleFullRowSelect()){Checked = tableView.FullRowSelect, CheckType = MenuItemCheckStyle.Checked }, miCellLines =new MenuItem ("_CellLines", "", () => ToggleCellLines()){Checked = tableView.Style.ShowVerticalCellLines, CheckType = MenuItemCheckStyle.Checked }, miExpandLastColumn = new MenuItem ("_ExpandLastColumn", "", () => ToggleExpandLastColumn()){Checked = tableView.Style.ExpandLastColumn, CheckType = MenuItemCheckStyle.Checked }, @@ -205,6 +207,12 @@ namespace UICatalog.Scenarios { tableView.Style.ShowHorizontalHeaderUnderline = miHeaderUnderline.Checked; tableView.Update(); } + private void ToggleHorizontalScrollIndicators () + { + miShowHorizontalScrollIndicators.Checked = !miShowHorizontalScrollIndicators.Checked; + tableView.Style.ShowHorizontalScrollIndicators = miShowHorizontalScrollIndicators.Checked; + tableView.Update(); + } private void ToggleFullRowSelect () { miFullRowSelect.Checked = !miFullRowSelect.Checked; diff --git a/UICatalog/Scenarios/TreeUseCases.cs b/UICatalog/Scenarios/TreeUseCases.cs index b067001d2..4a63c25aa 100644 --- a/UICatalog/Scenarios/TreeUseCases.cs +++ b/UICatalog/Scenarios/TreeUseCases.cs @@ -75,6 +75,7 @@ namespace UICatalog.Scenarios { if (currentTree != null) { Win.Remove (currentTree); + currentTree.Dispose (); } @@ -148,6 +149,7 @@ namespace UICatalog.Scenarios { if (currentTree != null) { Win.Remove (currentTree); + currentTree.Dispose (); } @@ -180,6 +182,7 @@ namespace UICatalog.Scenarios { { if (currentTree != null) { Win.Remove (currentTree); + currentTree.Dispose (); } diff --git a/UnitTests/TableViewTests.cs b/UnitTests/TableViewTests.cs index 5ccfdbbcd..a31985994 100644 --- a/UnitTests/TableViewTests.cs +++ b/UnitTests/TableViewTests.cs @@ -778,6 +778,85 @@ namespace Terminal.Gui.Views { Application.Shutdown (); } + + [Fact] + public void ScrollIndicators () + { + GraphViewTests.InitFakeDriver (); + + var tableView = new TableView (); + tableView.ColorScheme = Colors.TopLevel; + + // 3 columns are visibile + tableView.Bounds = new Rect (0, 0, 7, 5); + tableView.Style.ShowHorizontalHeaderUnderline = true; + tableView.Style.ShowHorizontalHeaderOverline = false; + tableView.Style.AlwaysShowHeaders = true; + tableView.Style.SmoothHorizontalScrolling = true; + + var dt = new DataTable (); + dt.Columns.Add ("A"); + dt.Columns.Add ("B"); + dt.Columns.Add ("C"); + dt.Columns.Add ("D"); + dt.Columns.Add ("E"); + dt.Columns.Add ("F"); + + dt.Rows.Add (1, 2, 3, 4, 5, 6); + + tableView.Table = dt; + + // select last visible column + tableView.SelectedColumn = 2; // column C + + tableView.Redraw (tableView.Bounds); + + // user can only scroll right so sees right indicator + // Because first column in table is A + string expected = + @" +│A│B│C│ +├─┼─┼─► +│1│2│3│"; + + GraphViewTests.AssertDriverContentsAre (expected, output); + + + // Scroll right + tableView.ProcessKey (new KeyEvent () { Key = Key.CursorRight }); + + + // since A is now pushed off screen we get indicator showing + // that user can scroll left to see first column + tableView.Redraw (tableView.Bounds); + + expected = + @" +│B│C│D│ +◄─┼─┼─► +│2│3│4│"; + + GraphViewTests.AssertDriverContentsAre (expected, output); + + + // Scroll right twice more (to end of columns) + tableView.ProcessKey (new KeyEvent () { Key = Key.CursorRight }); + tableView.ProcessKey (new KeyEvent () { Key = Key.CursorRight }); + + tableView.Redraw (tableView.Bounds); + + expected = + @" +│D│E│F│ +◄─┼─┼─┤ +│4│5│6│"; + + GraphViewTests.AssertDriverContentsAre (expected, output); + + // Shutdown must be called to safely clean up Application if Init has been called + Application.Shutdown (); + } + /// /// Builds a simple table of string columns with the requested number of columns and rows ///