ListTableSource.cs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. using System;
  2. using System.Collections;
  3. using System.Data;
  4. using System.Linq;
  5. namespace Terminal.Gui {
  6. /// <summary>
  7. /// <see cref="ITableSource"/> implementation that wraps
  8. /// a <see cref="System.Collections.IList"/>. This class is
  9. /// mutable: changes are permitted to the wrapped <see cref="IList"/>.
  10. /// </summary>
  11. public class ListTableSource : ITableSource {
  12. /// <summary>
  13. /// The list this source wraps.
  14. /// </summary>
  15. public IList List;
  16. /// <summary>
  17. /// The style this source uses.
  18. /// </summary>
  19. public ListColumnStyle Style;
  20. /// <summary>
  21. /// The data table this source wraps.
  22. /// </summary>
  23. public DataTable DataTable { get; private set; }
  24. private TableView _tableView;
  25. private Rect _lastBounds;
  26. private int _lastMaxCellWidth;
  27. private int _lastMinCellWidth;
  28. private ListColumnStyle _lastStyle;
  29. private IList _lastList;
  30. /// <summary>
  31. /// Creates a new columned list table instance based on the data in <paramref name="list"/>
  32. /// and dimensions from <paramref name="tableView"/>.
  33. /// </summary>
  34. /// <param name="list"></param>
  35. /// <param name="tableView"></param>
  36. /// <param name="style"></param>
  37. public ListTableSource (IList list, TableView tableView, ListColumnStyle style)
  38. {
  39. this.List = list;
  40. this._tableView = tableView;
  41. Style = style;
  42. this.DataTable = CreateTable (CalculateColumns ());
  43. // TODO: Determine the best event for this
  44. tableView.DrawContent += TableView_DrawContent;
  45. }
  46. /// <inheritdoc/>
  47. public ListTableSource (IList list, TableView tableView) : this (list, tableView, new ListColumnStyle ()) { }
  48. private void TableView_DrawContent (object sender, DrawEventArgs e)
  49. {
  50. if ((!_tableView.Bounds.Equals (_lastBounds)) ||
  51. _tableView.MaxCellWidth != _lastMaxCellWidth ||
  52. _tableView.MinCellWidth != _lastMinCellWidth ||
  53. Style != _lastStyle ||
  54. this.List != _lastList) {
  55. this.DataTable = CreateTable (CalculateColumns ());
  56. }
  57. _lastBounds = _tableView.Bounds;
  58. _lastMinCellWidth = _tableView.MaxCellWidth;
  59. _lastMaxCellWidth = _tableView.MaxCellWidth;
  60. _lastStyle = Style;
  61. _lastList = this.List;
  62. }
  63. /// <inheritdoc/>
  64. public object this [int row, int col] {
  65. get {
  66. int idx;
  67. if (Style.Orientation == Orientation.Vertical) {
  68. idx = (col * Rows) + row;
  69. } else {
  70. idx = (row * Columns) + col;
  71. }
  72. if (idx < 0 || idx >= Count) {
  73. return null;
  74. }
  75. return this.List [idx];
  76. }
  77. }
  78. /// <summary>
  79. /// The number of items in the IList source
  80. /// </summary>
  81. public int Count => this.List.Count;
  82. /// <inheritdoc/>
  83. public int Rows => this.DataTable.Rows.Count;
  84. /// <inheritdoc/>
  85. public int Columns => this.DataTable.Columns.Count;
  86. /// <inheritdoc/>
  87. public string [] ColumnNames => Enumerable.Range (0, Columns).Select (n => n.ToString ()).ToArray ();
  88. /// <summary>
  89. /// Creates a DataTable from an IList to display in a <see cref="TableView"/>
  90. /// </summary>
  91. private DataTable CreateTable (int cols = 1)
  92. {
  93. var table = new DataTable ();
  94. for (int col = 0; col < cols; col++) {
  95. table.Columns.Add (new DataColumn (col.ToString ()));
  96. }
  97. for (int row = 0; row < (Count / table.Columns.Count); row++) {
  98. table.Rows.Add ();
  99. }
  100. // return partial row
  101. if (Count % table.Columns.Count != 0) {
  102. table.Rows.Add ();
  103. }
  104. return table;
  105. }
  106. /// <summary>
  107. /// Returns the size in characters of the longest value read from <see cref="ListTableSource.List"/>
  108. /// </summary>
  109. /// <returns></returns>
  110. private int CalculateMaxLength ()
  111. {
  112. if (List == null || Count == 0) {
  113. return 0;
  114. }
  115. int maxLength = 0;
  116. foreach (var t in List) {
  117. int l;
  118. if (t is string s) {
  119. l = s.GetColumns ();
  120. } else {
  121. l = t.ToString ().Length;
  122. }
  123. if (l > maxLength) {
  124. maxLength = l;
  125. }
  126. }
  127. return maxLength;
  128. }
  129. private int CalculateColumns ()
  130. {
  131. int cols;
  132. int colWidth = CalculateMaxLength ();
  133. if (colWidth > _tableView.MaxCellWidth) {
  134. colWidth = _tableView.MaxCellWidth;
  135. }
  136. if (_tableView.MinCellWidth > 0 && colWidth < _tableView.MinCellWidth) {
  137. if (_tableView.MinCellWidth > _tableView.MaxCellWidth) {
  138. colWidth = _tableView.MaxCellWidth;
  139. } else {
  140. colWidth = _tableView.MinCellWidth;
  141. }
  142. }
  143. if ((Style.Orientation == Orientation.Vertical) != Style.ScrollParallel) {
  144. float f = (float)_tableView.Bounds.Height - _tableView.GetHeaderHeight ();
  145. cols = (int)Math.Ceiling (Count / f);
  146. } else {
  147. cols = ((int)Math.Ceiling (((float)_tableView.Bounds.Width - 1) / colWidth)) - 2;
  148. }
  149. return (cols > 1) ? cols : 1;
  150. }
  151. /// <summary>
  152. /// Defines rendering options that affect how the view is displayed.
  153. /// </summary>
  154. public class ListColumnStyle {
  155. /// <summary>
  156. /// Gets or sets an Orientation enum indicating whether to populate data down each column
  157. /// rather than across each row. Defaults to <see cref="Orientation.Horizontal"/>.
  158. /// </summary>
  159. public Orientation Orientation { get; set; } = Orientation.Horizontal;
  160. /// <summary>
  161. /// Gets or sets a flag indicating whether to scroll in the same direction as <see cref="ListTableSource.ListColumnStyle.Orientation"/>.
  162. /// Defaults to <see langword="false"/>.
  163. /// </summary>
  164. public bool ScrollParallel { get; set; } = false;
  165. }
  166. }
  167. }