Procházet zdrojové kódy

Table scroll fix (#1627)

* TableView scroll down goes one line at a time

Fixes #1626

* Removed accidentally committed test

* Updated test comments

* Added TableStyle.SmoothHorizontalScrolling

* Add explicit break into while for when scrolled to last column.

* Added tests for SmoothHorizontalScrolling
Thomas Nind před 3 roky
rodič
revize
7e04e31953
2 změnil soubory, kde provedl 197 přidání a 4 odebrání
  1. 37 2
      Terminal.Gui/Views/TableView.cs
  2. 160 2
      UnitTests/TableViewTests.cs

+ 37 - 2
Terminal.Gui/Views/TableView.cs

@@ -1080,12 +1080,31 @@ namespace Terminal.Gui {
 
 			//if we have scrolled too far to the right
 			if (SelectedColumn > columnsToRender.Max (r => r.Column.Ordinal)) {
-				ColumnOffset = SelectedColumn;
+
+				if(Style.SmoothHorizontalScrolling) {
+
+					// Scroll right 1 column at a time until the users selected column is visible
+					while(SelectedColumn > columnsToRender.Max (r => r.Column.Ordinal)) {
+
+						ColumnOffset++;
+						columnsToRender = CalculateViewport (Bounds).ToArray ();
+
+						// if we are already scrolled to the last column then break
+						// this will prevent any theoretical infinite loop
+						if (ColumnOffset >= Table.Columns.Count - 1)
+							break;
+
+					}
+				}
+				else {
+					ColumnOffset = SelectedColumn;
+				}
+				
 			}
 
 			//if we have scrolled too far down
 			if (SelectedRow >= RowOffset + (Bounds.Height - headerHeight)) {
-				RowOffset = SelectedRow;
+				RowOffset = SelectedRow - (Bounds.Height - headerHeight) + 1;
 			}
 			//if we have scrolled too far up
 			if (SelectedRow < RowOffset) {
@@ -1377,6 +1396,22 @@ namespace Terminal.Gui {
 			/// </summary>
 			/// <value></value>
 			public bool ExpandLastColumn {get;set;} = true;
+
+			/// <summary>
+			/// <para>
+			/// Determines how <see cref="TableView.ColumnOffset"/> is updated when scrolling
+			/// right off the end of the currently visible area.
+			/// </para>
+			/// <para>
+			/// If true then when scrolling right the scroll offset is increased the minimum required to show
+			/// the new column.  This may be slow if you have an incredibly large number of columns in
+			/// your table and/or slow <see cref="ColumnStyle.RepresentationGetter"/> implementations
+			/// </para>
+			/// <para>
+			/// If false then scroll offset is set to the currently selected column (i.e. PageRight).
+			/// </para>
+			/// </summary>
+			public bool SmoothHorizontalScrolling { get; set; } = true;
 			
 			/// <summary>
 			/// Returns the entry from <see cref="ColumnStyles"/> for the given <paramref name="col"/> or null if no custom styling is defined for it

+ 160 - 2
UnitTests/TableViewTests.cs

@@ -256,12 +256,14 @@ namespace Terminal.Gui.Views {
 			tableView.ProcessKey (new KeyEvent (Key.PageDown, new KeyModifiers ()));
 
 			// window height is 5 rows 2 are header so page down should give 3 new rows
-			Assert.Equal (3, tableView.RowOffset);
+			Assert.Equal (3, tableView.SelectedRow);
+			Assert.Equal (1, tableView.RowOffset);
 
 			// header is no longer visible so page down should give 5 new rows
 			tableView.ProcessKey (new KeyEvent (Key.PageDown, new KeyModifiers ()));
 
-			Assert.Equal (8, tableView.RowOffset);
+			Assert.Equal (8, tableView.SelectedRow);
+			Assert.Equal (4, tableView.RowOffset);
 		}
 
 		[Fact]
@@ -603,6 +605,162 @@ namespace Terminal.Gui.Views {
 			return tv;
 		}
 
+		[Fact]
+		[AutoInitShutdown]
+		public void ScrollDown_OneLineAtATime ()
+		{
+			var tableView = new TableView ();
+
+			// Set big table
+			tableView.Table = BuildTable (25, 50);
+
+			// 1 header + 4 rows visible
+			tableView.Bounds = new Rect (0, 0, 25, 5);
+			tableView.Style.ShowHorizontalHeaderUnderline = false;
+			tableView.Style.ShowHorizontalHeaderOverline = false;
+			tableView.Style.AlwaysShowHeaders = true;
+
+			// select last row
+			tableView.SelectedRow = 3; // row is 0 indexed so this is the 4th visible row
+
+			// Scroll down
+			tableView.ProcessKey (new KeyEvent () { Key = Key.CursorDown });
+
+			// Scrolled off the page by 1 row so it should only have moved down 1 line of RowOffset
+			Assert.Equal(4,tableView.SelectedRow);
+			Assert.Equal (1, tableView.RowOffset);
+		}
+
+		[Fact]
+		public void ScrollRight_SmoothScrolling ()
+		{
+			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 = false;
+			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);
+
+			string expected = 
+				@"
+│A│B│C│
+│1│2│3│";
+
+			GraphViewTests.AssertDriverContentsAre (expected, output);
+
+
+			// Scroll right
+			tableView.ProcessKey (new KeyEvent () { Key = Key.CursorRight });
+
+
+			tableView.Redraw (tableView.Bounds);
+
+			// Note that with SmoothHorizontalScrolling only a single new column
+			// is exposed when scrolling right.  This is not always the case though
+			// sometimes if the leftmost column is long (i.e. A is a long column)
+			// then when A is pushed off the screen multiple new columns could be exposed
+			// (not just D but also E and F).  This is because TableView never shows
+			// 'half cells' or scrolls by console unit (scrolling is done by table row/column increments).
+
+			expected =
+				@"
+│B│C│D│
+│2│3│4│";
+
+			GraphViewTests.AssertDriverContentsAre (expected, output);
+
+
+			// Shutdown must be called to safely clean up Application if Init has been called
+			Application.Shutdown ();
+		}
+
+		[Fact]
+		public void ScrollRight_WithoutSmoothScrolling ()
+		{
+			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 = false;
+			tableView.Style.ShowHorizontalHeaderOverline = false;
+			tableView.Style.AlwaysShowHeaders = true;
+			tableView.Style.SmoothHorizontalScrolling = false;
+
+			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);
+
+			string expected =
+				@"
+│A│B│C│
+│1│2│3│";
+
+			GraphViewTests.AssertDriverContentsAre (expected, output);
+
+
+			// Scroll right
+			tableView.ProcessKey (new KeyEvent () { Key = Key.CursorRight });
+
+
+			tableView.Redraw (tableView.Bounds);
+
+			// notice that without smooth scrolling we just update the first column
+			// rendered in the table to the newly exposed column (D).  This is fast
+			// since we don't have to worry about repeatedly measuring the content
+			// area as we scroll until the new column (D) is exposed.  But it makes
+			// the view 'jump' to expose all new columns
+
+
+			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 ();
+		}
+
 		/// <summary>
 		/// Builds a simple table of string columns with the requested number of columns and rows
 		/// </summary>