TableViewTests.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Data;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. using Terminal.Gui;
  7. using Xunit;
  8. using System.Globalization;
  9. using Xunit.Abstractions;
  10. namespace Terminal.Gui.Views {
  11. public class TableViewTests {
  12. readonly ITestOutputHelper output;
  13. public TableViewTests (ITestOutputHelper output)
  14. {
  15. this.output = output;
  16. }
  17. [Fact]
  18. public void EnsureValidScrollOffsets_WithNoCells ()
  19. {
  20. var tableView = new TableView ();
  21. Assert.Equal (0, tableView.RowOffset);
  22. Assert.Equal (0, tableView.ColumnOffset);
  23. // Set empty table
  24. tableView.Table = new DataTable ();
  25. // Since table has no rows or columns scroll offset should default to 0
  26. tableView.EnsureValidScrollOffsets ();
  27. Assert.Equal (0, tableView.RowOffset);
  28. Assert.Equal (0, tableView.ColumnOffset);
  29. }
  30. [Fact]
  31. public void EnsureValidScrollOffsets_LoadSmallerTable ()
  32. {
  33. var tableView = new TableView ();
  34. tableView.Bounds = new Rect (0, 0, 25, 10);
  35. Assert.Equal (0, tableView.RowOffset);
  36. Assert.Equal (0, tableView.ColumnOffset);
  37. // Set big table
  38. tableView.Table = BuildTable (25, 50);
  39. // Scroll down and along
  40. tableView.RowOffset = 20;
  41. tableView.ColumnOffset = 10;
  42. tableView.EnsureValidScrollOffsets ();
  43. // The scroll should be valid at the moment
  44. Assert.Equal (20, tableView.RowOffset);
  45. Assert.Equal (10, tableView.ColumnOffset);
  46. // Set small table
  47. tableView.Table = BuildTable (2, 2);
  48. // Setting a small table should automatically trigger fixing the scroll offsets to ensure valid cells
  49. Assert.Equal (0, tableView.RowOffset);
  50. Assert.Equal (0, tableView.ColumnOffset);
  51. // Trying to set invalid indexes should not be possible
  52. tableView.RowOffset = 20;
  53. tableView.ColumnOffset = 10;
  54. Assert.Equal (1, tableView.RowOffset);
  55. Assert.Equal (1, tableView.ColumnOffset);
  56. }
  57. [Fact]
  58. public void SelectedCellChanged_NotFiredForSameValue ()
  59. {
  60. var tableView = new TableView () {
  61. Table = BuildTable (25, 50)
  62. };
  63. bool called = false;
  64. tableView.SelectedCellChanged += (e) => { called = true; };
  65. Assert.Equal (0, tableView.SelectedColumn);
  66. Assert.False (called);
  67. // Changing value to same as it already was should not raise an event
  68. tableView.SelectedColumn = 0;
  69. Assert.False (called);
  70. tableView.SelectedColumn = 10;
  71. Assert.True (called);
  72. }
  73. [Fact]
  74. public void SelectedCellChanged_SelectedColumnIndexesCorrect ()
  75. {
  76. var tableView = new TableView () {
  77. Table = BuildTable (25, 50)
  78. };
  79. bool called = false;
  80. tableView.SelectedCellChanged += (e) => {
  81. called = true;
  82. Assert.Equal (0, e.OldCol);
  83. Assert.Equal (10, e.NewCol);
  84. };
  85. tableView.SelectedColumn = 10;
  86. Assert.True (called);
  87. }
  88. [Fact]
  89. public void SelectedCellChanged_SelectedRowIndexesCorrect ()
  90. {
  91. var tableView = new TableView () {
  92. Table = BuildTable (25, 50)
  93. };
  94. bool called = false;
  95. tableView.SelectedCellChanged += (e) => {
  96. called = true;
  97. Assert.Equal (0, e.OldRow);
  98. Assert.Equal (10, e.NewRow);
  99. };
  100. tableView.SelectedRow = 10;
  101. Assert.True (called);
  102. }
  103. [Fact]
  104. public void Test_SumColumnWidth_UnicodeLength ()
  105. {
  106. Assert.Equal (11, "hello there".Sum (c => Rune.ColumnWidth (c)));
  107. // Creates a string with the peculiar (french?) r symbol
  108. String surrogate = "Les Mise" + Char.ConvertFromUtf32 (Int32.Parse ("0301", NumberStyles.HexNumber)) + "rables";
  109. // The unicode width of this string is shorter than the string length!
  110. Assert.Equal (14, surrogate.Sum (c => Rune.ColumnWidth (c)));
  111. Assert.Equal (15, surrogate.Length);
  112. }
  113. [Fact]
  114. public void IsSelected_MultiSelectionOn_Vertical ()
  115. {
  116. var tableView = new TableView () {
  117. Table = BuildTable (25, 50),
  118. MultiSelect = true
  119. };
  120. // 3 cell vertical selection
  121. tableView.SetSelection (1, 1, false);
  122. tableView.SetSelection (1, 3, true);
  123. Assert.False (tableView.IsSelected (0, 0));
  124. Assert.False (tableView.IsSelected (1, 0));
  125. Assert.False (tableView.IsSelected (2, 0));
  126. Assert.False (tableView.IsSelected (0, 1));
  127. Assert.True (tableView.IsSelected (1, 1));
  128. Assert.False (tableView.IsSelected (2, 1));
  129. Assert.False (tableView.IsSelected (0, 2));
  130. Assert.True (tableView.IsSelected (1, 2));
  131. Assert.False (tableView.IsSelected (2, 2));
  132. Assert.False (tableView.IsSelected (0, 3));
  133. Assert.True (tableView.IsSelected (1, 3));
  134. Assert.False (tableView.IsSelected (2, 3));
  135. Assert.False (tableView.IsSelected (0, 4));
  136. Assert.False (tableView.IsSelected (1, 4));
  137. Assert.False (tableView.IsSelected (2, 4));
  138. }
  139. [Fact]
  140. public void IsSelected_MultiSelectionOn_Horizontal ()
  141. {
  142. var tableView = new TableView () {
  143. Table = BuildTable (25, 50),
  144. MultiSelect = true
  145. };
  146. // 2 cell horizontal selection
  147. tableView.SetSelection (1, 0, false);
  148. tableView.SetSelection (2, 0, true);
  149. Assert.False (tableView.IsSelected (0, 0));
  150. Assert.True (tableView.IsSelected (1, 0));
  151. Assert.True (tableView.IsSelected (2, 0));
  152. Assert.False (tableView.IsSelected (3, 0));
  153. Assert.False (tableView.IsSelected (0, 1));
  154. Assert.False (tableView.IsSelected (1, 1));
  155. Assert.False (tableView.IsSelected (2, 1));
  156. Assert.False (tableView.IsSelected (3, 1));
  157. }
  158. [Fact]
  159. public void IsSelected_MultiSelectionOn_BoxSelection ()
  160. {
  161. var tableView = new TableView () {
  162. Table = BuildTable (25, 50),
  163. MultiSelect = true
  164. };
  165. // 4 cell horizontal in box 2x2
  166. tableView.SetSelection (0, 0, false);
  167. tableView.SetSelection (1, 1, true);
  168. Assert.True (tableView.IsSelected (0, 0));
  169. Assert.True (tableView.IsSelected (1, 0));
  170. Assert.False (tableView.IsSelected (2, 0));
  171. Assert.True (tableView.IsSelected (0, 1));
  172. Assert.True (tableView.IsSelected (1, 1));
  173. Assert.False (tableView.IsSelected (2, 1));
  174. Assert.False (tableView.IsSelected (0, 2));
  175. Assert.False (tableView.IsSelected (1, 2));
  176. Assert.False (tableView.IsSelected (2, 2));
  177. }
  178. [AutoInitShutdown]
  179. [Fact]
  180. public void PageDown_ExcludesHeaders ()
  181. {
  182. var tableView = new TableView () {
  183. Table = BuildTable (25, 50),
  184. MultiSelect = true,
  185. Bounds = new Rect (0, 0, 10, 5)
  186. };
  187. // Header should take up 2 lines
  188. tableView.Style.ShowHorizontalHeaderOverline = false;
  189. tableView.Style.ShowHorizontalHeaderUnderline = true;
  190. tableView.Style.AlwaysShowHeaders = false;
  191. // ensure that TableView has the input focus
  192. Application.Top.Add (tableView);
  193. Application.Top.FocusFirst ();
  194. Assert.True (tableView.HasFocus);
  195. Assert.Equal (0, tableView.RowOffset);
  196. tableView.ProcessKey (new KeyEvent (Key.PageDown, new KeyModifiers ()));
  197. // window height is 5 rows 2 are header so page down should give 3 new rows
  198. Assert.Equal (3, tableView.SelectedRow);
  199. Assert.Equal (1, tableView.RowOffset);
  200. // header is no longer visible so page down should give 5 new rows
  201. tableView.ProcessKey (new KeyEvent (Key.PageDown, new KeyModifiers ()));
  202. Assert.Equal (8, tableView.SelectedRow);
  203. Assert.Equal (4, tableView.RowOffset);
  204. }
  205. [Fact]
  206. public void DeleteRow_SelectAll_AdjustsSelectionToPreventOverrun ()
  207. {
  208. // create a 4 by 4 table
  209. var tableView = new TableView () {
  210. Table = BuildTable (4, 4),
  211. MultiSelect = true,
  212. Bounds = new Rect (0, 0, 10, 5)
  213. };
  214. tableView.SelectAll ();
  215. Assert.Equal (16, tableView.GetAllSelectedCells ().Count ());
  216. // delete one of the columns
  217. tableView.Table.Columns.RemoveAt (2);
  218. // table should now be 3x4
  219. Assert.Equal (12, tableView.GetAllSelectedCells ().Count ());
  220. // remove a row
  221. tableView.Table.Rows.RemoveAt (1);
  222. // table should now be 3x3
  223. Assert.Equal (9, tableView.GetAllSelectedCells ().Count ());
  224. }
  225. [Fact]
  226. public void DeleteRow_SelectLastRow_AdjustsSelectionToPreventOverrun ()
  227. {
  228. // create a 4 by 4 table
  229. var tableView = new TableView () {
  230. Table = BuildTable (4, 4),
  231. MultiSelect = true,
  232. Bounds = new Rect (0, 0, 10, 5)
  233. };
  234. // select the last row
  235. tableView.MultiSelectedRegions.Clear ();
  236. tableView.MultiSelectedRegions.Push (new TableView.TableSelection (new Point (0, 3), new Rect (0, 3, 4, 1)));
  237. Assert.Equal (4, tableView.GetAllSelectedCells ().Count ());
  238. // remove a row
  239. tableView.Table.Rows.RemoveAt (0);
  240. tableView.EnsureValidSelection ();
  241. // since the selection no longer exists it should be removed
  242. Assert.Empty (tableView.MultiSelectedRegions);
  243. }
  244. [Theory]
  245. [InlineData (true)]
  246. [InlineData (false)]
  247. public void GetAllSelectedCells_SingleCellSelected_ReturnsOne (bool multiSelect)
  248. {
  249. var tableView = new TableView () {
  250. Table = BuildTable (3, 3),
  251. MultiSelect = multiSelect,
  252. Bounds = new Rect (0, 0, 10, 5)
  253. };
  254. tableView.SetSelection (1, 1, false);
  255. Assert.Single (tableView.GetAllSelectedCells ());
  256. Assert.Equal (new Point (1, 1), tableView.GetAllSelectedCells ().Single ());
  257. }
  258. [Fact]
  259. public void GetAllSelectedCells_SquareSelection_ReturnsFour ()
  260. {
  261. var tableView = new TableView () {
  262. Table = BuildTable (3, 3),
  263. MultiSelect = true,
  264. Bounds = new Rect (0, 0, 10, 5)
  265. };
  266. // move cursor to 1,1
  267. tableView.SetSelection (1, 1, false);
  268. // spread selection across to 2,2 (e.g. shift+right then shift+down)
  269. tableView.SetSelection (2, 2, true);
  270. var selected = tableView.GetAllSelectedCells ().ToArray ();
  271. Assert.Equal (4, selected.Length);
  272. Assert.Equal (new Point (1, 1), selected [0]);
  273. Assert.Equal (new Point (2, 1), selected [1]);
  274. Assert.Equal (new Point (1, 2), selected [2]);
  275. Assert.Equal (new Point (2, 2), selected [3]);
  276. }
  277. [Fact]
  278. public void GetAllSelectedCells_SquareSelection_FullRowSelect ()
  279. {
  280. var tableView = new TableView () {
  281. Table = BuildTable (3, 3),
  282. MultiSelect = true,
  283. FullRowSelect = true,
  284. Bounds = new Rect (0, 0, 10, 5)
  285. };
  286. // move cursor to 1,1
  287. tableView.SetSelection (1, 1, false);
  288. // spread selection across to 2,2 (e.g. shift+right then shift+down)
  289. tableView.SetSelection (2, 2, true);
  290. var selected = tableView.GetAllSelectedCells ().ToArray ();
  291. Assert.Equal (6, selected.Length);
  292. Assert.Equal (new Point (0, 1), selected [0]);
  293. Assert.Equal (new Point (1, 1), selected [1]);
  294. Assert.Equal (new Point (2, 1), selected [2]);
  295. Assert.Equal (new Point (0, 2), selected [3]);
  296. Assert.Equal (new Point (1, 2), selected [4]);
  297. Assert.Equal (new Point (2, 2), selected [5]);
  298. }
  299. [Fact]
  300. public void GetAllSelectedCells_TwoIsolatedSelections_ReturnsSix ()
  301. {
  302. var tableView = new TableView () {
  303. Table = BuildTable (20, 20),
  304. MultiSelect = true,
  305. Bounds = new Rect (0, 0, 10, 5)
  306. };
  307. /*
  308. Sets up disconnected selections like:
  309. 00000000000
  310. 01100000000
  311. 01100000000
  312. 00000001100
  313. 00000000000
  314. */
  315. tableView.MultiSelectedRegions.Clear ();
  316. tableView.MultiSelectedRegions.Push (new TableView.TableSelection (new Point (1, 1), new Rect (1, 1, 2, 2)));
  317. tableView.MultiSelectedRegions.Push (new TableView.TableSelection (new Point (7, 3), new Rect (7, 3, 2, 1)));
  318. tableView.SelectedColumn = 8;
  319. tableView.SelectedRow = 3;
  320. var selected = tableView.GetAllSelectedCells ().ToArray ();
  321. Assert.Equal (6, selected.Length);
  322. Assert.Equal (new Point (1, 1), selected [0]);
  323. Assert.Equal (new Point (2, 1), selected [1]);
  324. Assert.Equal (new Point (1, 2), selected [2]);
  325. Assert.Equal (new Point (2, 2), selected [3]);
  326. Assert.Equal (new Point (7, 3), selected [4]);
  327. Assert.Equal (new Point (8, 3), selected [5]);
  328. }
  329. [Fact]
  330. public void TableView_ExpandLastColumn_True ()
  331. {
  332. var tv = SetUpMiniTable ();
  333. // the thing we are testing
  334. tv.Style.ExpandLastColumn = true;
  335. tv.Redraw (tv.Bounds);
  336. string expected = @"
  337. ┌─┬──────┐
  338. │A│B │
  339. ├─┼──────┤
  340. │1│2 │
  341. ";
  342. GraphViewTests.AssertDriverContentsAre (expected, output);
  343. // Shutdown must be called to safely clean up Application if Init has been called
  344. Application.Shutdown ();
  345. }
  346. [Fact]
  347. public void TableView_ExpandLastColumn_False ()
  348. {
  349. var tv = SetUpMiniTable ();
  350. // the thing we are testing
  351. tv.Style.ExpandLastColumn = false;
  352. tv.Redraw (tv.Bounds);
  353. string expected = @"
  354. ┌─┬─┬────┐
  355. │A│B│ │
  356. ├─┼─┼────┤
  357. │1│2│ │
  358. ";
  359. GraphViewTests.AssertDriverContentsAre (expected, output);
  360. // Shutdown must be called to safely clean up Application if Init has been called
  361. Application.Shutdown ();
  362. }
  363. [Fact]
  364. public void TableView_ExpandLastColumn_False_ExactBounds ()
  365. {
  366. var tv = SetUpMiniTable ();
  367. // the thing we are testing
  368. tv.Style.ExpandLastColumn = false;
  369. // width exactly matches the max col widths
  370. tv.Bounds = new Rect (0, 0, 5, 4);
  371. tv.Redraw (tv.Bounds);
  372. string expected = @"
  373. ┌─┬─┐
  374. │A│B│
  375. ├─┼─┤
  376. │1│2│
  377. ";
  378. GraphViewTests.AssertDriverContentsAre (expected, output);
  379. // Shutdown must be called to safely clean up Application if Init has been called
  380. Application.Shutdown ();
  381. }
  382. [Fact]
  383. [AutoInitShutdown]
  384. public void TableView_Activate()
  385. {
  386. string activatedValue = null;
  387. var tv = new TableView (BuildTable(1,1));
  388. tv.CellActivated += (c) => activatedValue = c.Table.Rows[c.Row][c.Col].ToString();
  389. Application.Top.Add (tv);
  390. Application.Begin (Application.Top);
  391. // pressing enter should activate the first cell (selected cell)
  392. tv.ProcessKey (new KeyEvent (Key.Enter, new KeyModifiers ()));
  393. Assert.Equal ("R0C0",activatedValue);
  394. // reset the test
  395. activatedValue = null;
  396. // clear keybindings and ensure that Enter does not trigger the event anymore
  397. tv.ClearKeybindings ();
  398. tv.ProcessKey (new KeyEvent (Key.Enter, new KeyModifiers ()));
  399. Assert.Null(activatedValue);
  400. // New method for changing the activation key
  401. tv.AddKeyBinding (Key.z, Command.Accept);
  402. tv.ProcessKey (new KeyEvent (Key.z, new KeyModifiers ()));
  403. Assert.Equal ("R0C0", activatedValue);
  404. // reset the test
  405. activatedValue = null;
  406. tv.ClearKeybindings ();
  407. // Old method for changing the activation key
  408. tv.CellActivationKey = Key.z;
  409. tv.ProcessKey (new KeyEvent (Key.z, new KeyModifiers ()));
  410. Assert.Equal ("R0C0", activatedValue);
  411. }
  412. [Fact]
  413. public void TableView_ColorsTest_ColorGetter ()
  414. {
  415. var tv = SetUpMiniTable ();
  416. tv.Style.ExpandLastColumn = false;
  417. tv.Style.InvertSelectedCellFirstCharacter = true;
  418. // width exactly matches the max col widths
  419. tv.Bounds = new Rect (0, 0, 5, 4);
  420. // Create a style for column B
  421. var bStyle = tv.Style.GetOrCreateColumnStyle (tv.Table.Columns ["B"]);
  422. // when B is 2 use the custom highlight colour
  423. ColorScheme cellHighlight = new ColorScheme () { Normal = Attribute.Make (Color.BrightCyan, Color.DarkGray) };
  424. bStyle.ColorGetter = (a) => Convert.ToInt32(a.CellValue) == 2 ? cellHighlight : null;
  425. tv.Redraw (tv.Bounds);
  426. string expected = @"
  427. ┌─┬─┐
  428. │A│B│
  429. ├─┼─┤
  430. │1│2│
  431. ";
  432. GraphViewTests.AssertDriverContentsAre (expected, output);
  433. string expectedColors = @"
  434. 00000
  435. 00000
  436. 00000
  437. 01020
  438. ";
  439. var invertedNormalColor = Application.Driver.MakeAttribute (tv.ColorScheme.Normal.Background, tv.ColorScheme.Normal.Foreground);
  440. GraphViewTests.AssertDriverColorsAre (expectedColors, new Attribute [] {
  441. // 0
  442. tv.ColorScheme.Normal,
  443. // 1
  444. invertedNormalColor,
  445. // 2
  446. cellHighlight.Normal});
  447. // Shutdown must be called to safely clean up Application if Init has been called
  448. Application.Shutdown ();
  449. }
  450. private TableView SetUpMiniTable ()
  451. {
  452. var tv = new TableView ();
  453. tv.Bounds = new Rect (0, 0, 10, 4);
  454. var dt = new DataTable ();
  455. var colA = dt.Columns.Add ("A");
  456. var colB = dt.Columns.Add ("B");
  457. dt.Rows.Add (1, 2);
  458. tv.Table = dt;
  459. tv.Style.GetOrCreateColumnStyle (colA).MinWidth = 1;
  460. tv.Style.GetOrCreateColumnStyle (colA).MinWidth = 1;
  461. tv.Style.GetOrCreateColumnStyle (colB).MaxWidth = 1;
  462. tv.Style.GetOrCreateColumnStyle (colB).MaxWidth = 1;
  463. GraphViewTests.InitFakeDriver ();
  464. tv.ColorScheme = new ColorScheme () {
  465. Normal = Application.Driver.MakeAttribute (Color.White, Color.Black),
  466. HotFocus = Application.Driver.MakeAttribute (Color.White, Color.Black)
  467. };
  468. return tv;
  469. }
  470. [Fact]
  471. [AutoInitShutdown]
  472. public void ScrollDown_OneLineAtATime ()
  473. {
  474. var tableView = new TableView ();
  475. // Set big table
  476. tableView.Table = BuildTable (25, 50);
  477. // 1 header + 4 rows visible
  478. tableView.Bounds = new Rect (0, 0, 25, 5);
  479. tableView.Style.ShowHorizontalHeaderUnderline = false;
  480. tableView.Style.ShowHorizontalHeaderOverline = false;
  481. tableView.Style.AlwaysShowHeaders = true;
  482. // select last row
  483. tableView.SelectedRow = 3; // row is 0 indexed so this is the 4th visible row
  484. // Scroll down
  485. tableView.ProcessKey (new KeyEvent () { Key = Key.CursorDown });
  486. // Scrolled off the page by 1 row so it should only have moved down 1 line of RowOffset
  487. Assert.Equal(4,tableView.SelectedRow);
  488. Assert.Equal (1, tableView.RowOffset);
  489. }
  490. [Fact]
  491. public void ScrollRight_SmoothScrolling ()
  492. {
  493. GraphViewTests.InitFakeDriver ();
  494. var tableView = new TableView ();
  495. tableView.ColorScheme = Colors.TopLevel;
  496. // 3 columns are visibile
  497. tableView.Bounds = new Rect (0, 0, 7, 5);
  498. tableView.Style.ShowHorizontalHeaderUnderline = false;
  499. tableView.Style.ShowHorizontalHeaderOverline = false;
  500. tableView.Style.AlwaysShowHeaders = true;
  501. tableView.Style.SmoothHorizontalScrolling = true;
  502. var dt = new DataTable ();
  503. dt.Columns.Add ("A");
  504. dt.Columns.Add ("B");
  505. dt.Columns.Add ("C");
  506. dt.Columns.Add ("D");
  507. dt.Columns.Add ("E");
  508. dt.Columns.Add ("F");
  509. dt.Rows.Add (1, 2, 3, 4, 5, 6);
  510. tableView.Table = dt;
  511. // select last visible column
  512. tableView.SelectedColumn = 2; // column C
  513. tableView.Redraw (tableView.Bounds);
  514. string expected =
  515. @"
  516. │A│B│C│
  517. │1│2│3│";
  518. GraphViewTests.AssertDriverContentsAre (expected, output);
  519. // Scroll right
  520. tableView.ProcessKey (new KeyEvent () { Key = Key.CursorRight });
  521. tableView.Redraw (tableView.Bounds);
  522. // Note that with SmoothHorizontalScrolling only a single new column
  523. // is exposed when scrolling right. This is not always the case though
  524. // sometimes if the leftmost column is long (i.e. A is a long column)
  525. // then when A is pushed off the screen multiple new columns could be exposed
  526. // (not just D but also E and F). This is because TableView never shows
  527. // 'half cells' or scrolls by console unit (scrolling is done by table row/column increments).
  528. expected =
  529. @"
  530. │B│C│D│
  531. │2│3│4│";
  532. GraphViewTests.AssertDriverContentsAre (expected, output);
  533. // Shutdown must be called to safely clean up Application if Init has been called
  534. Application.Shutdown ();
  535. }
  536. [Fact]
  537. public void ScrollRight_WithoutSmoothScrolling ()
  538. {
  539. GraphViewTests.InitFakeDriver ();
  540. var tableView = new TableView ();
  541. tableView.ColorScheme = Colors.TopLevel;
  542. // 3 columns are visibile
  543. tableView.Bounds = new Rect (0, 0, 7, 5);
  544. tableView.Style.ShowHorizontalHeaderUnderline = false;
  545. tableView.Style.ShowHorizontalHeaderOverline = false;
  546. tableView.Style.AlwaysShowHeaders = true;
  547. tableView.Style.SmoothHorizontalScrolling = false;
  548. var dt = new DataTable ();
  549. dt.Columns.Add ("A");
  550. dt.Columns.Add ("B");
  551. dt.Columns.Add ("C");
  552. dt.Columns.Add ("D");
  553. dt.Columns.Add ("E");
  554. dt.Columns.Add ("F");
  555. dt.Rows.Add (1, 2, 3, 4, 5, 6);
  556. tableView.Table = dt;
  557. // select last visible column
  558. tableView.SelectedColumn = 2; // column C
  559. tableView.Redraw (tableView.Bounds);
  560. string expected =
  561. @"
  562. │A│B│C│
  563. │1│2│3│";
  564. GraphViewTests.AssertDriverContentsAre (expected, output);
  565. // Scroll right
  566. tableView.ProcessKey (new KeyEvent () { Key = Key.CursorRight });
  567. tableView.Redraw (tableView.Bounds);
  568. // notice that without smooth scrolling we just update the first column
  569. // rendered in the table to the newly exposed column (D). This is fast
  570. // since we don't have to worry about repeatedly measuring the content
  571. // area as we scroll until the new column (D) is exposed. But it makes
  572. // the view 'jump' to expose all new columns
  573. expected =
  574. @"
  575. │D│E│F│
  576. │4│5│6│";
  577. GraphViewTests.AssertDriverContentsAre (expected, output);
  578. // Shutdown must be called to safely clean up Application if Init has been called
  579. Application.Shutdown ();
  580. }
  581. /// <summary>
  582. /// Builds a simple table of string columns with the requested number of columns and rows
  583. /// </summary>
  584. /// <param name="cols"></param>
  585. /// <param name="rows"></param>
  586. /// <returns></returns>
  587. public static DataTable BuildTable (int cols, int rows)
  588. {
  589. var dt = new DataTable ();
  590. for (int c = 0; c < cols; c++) {
  591. dt.Columns.Add ("Col" + c);
  592. }
  593. for (int r = 0; r < rows; r++) {
  594. var newRow = dt.NewRow ();
  595. for (int c = 0; c < cols; c++) {
  596. newRow [c] = $"R{r}C{c}";
  597. }
  598. dt.Rows.Add (newRow);
  599. }
  600. return dt;
  601. }
  602. }
  603. }