using System; using System.Collections.Generic; using System.Linq; using Spectre.Console.Rendering; namespace Spectre.Console { /// /// A renderable table. /// public sealed class Table : Renderable, IHasTableBorder, IExpandable, IAlignable { private readonly List _columns; /// /// Gets the table columns. /// public IReadOnlyList Columns => _columns; /// /// Gets the table rows. /// public TableRowCollection Rows { get; } /// public TableBorder Border { get; set; } = TableBorder.Square; /// public Style? BorderStyle { get; set; } /// public bool UseSafeBorder { get; set; } = true; /// /// Gets or sets a value indicating whether or not table headers should be shown. /// public bool ShowHeaders { get; set; } = true; /// /// Gets or sets a value indicating whether or not table footers should be shown. /// public bool ShowFooters { get; set; } = true; /// /// Gets or sets a value indicating whether or not the table should /// fit the available space. If false, the table width will be /// auto calculated. Defaults to false. /// public bool Expand { get; set; } /// /// Gets or sets the width of the table. /// public int? Width { get; set; } /// /// Gets or sets the table title. /// public TableTitle? Title { get; set; } /// /// Gets or sets the table footnote. /// public TableTitle? Caption { get; set; } /// public Justify? Alignment { get; set; } // Whether this is a grid or not. internal bool IsGrid { get; set; } // Whether or not the most right cell should be padded. // This is almost always the case, unless we're rendering // a grid without explicit padding in the last cell. internal bool PadRightCell { get; set; } = true; /// /// Initializes a new instance of the class. /// public Table() { _columns = new List(); Rows = new TableRowCollection(this); } /// /// Adds a column to the table. /// /// The column to add. /// The same instance so that multiple calls can be chained. public Table AddColumn(TableColumn column) { if (column is null) { throw new ArgumentNullException(nameof(column)); } if (Rows.Count > 0) { throw new InvalidOperationException("Cannot add new columns to table with existing rows."); } _columns.Add(column); return this; } /// protected override Measurement Measure(RenderContext context, int maxWidth) { if (context is null) { throw new ArgumentNullException(nameof(context)); } var measurer = new TableMeasurer(this, context); // Calculate the total cell width var totalCellWidth = measurer.CalculateTotalCellWidth(maxWidth); // Calculate the minimum and maximum table width var measurements = _columns.Select(column => measurer.MeasureColumn(column, totalCellWidth)); var minTableWidth = measurements.Sum(x => x.Min) + measurer.GetNonColumnWidth(); var maxTableWidth = Width ?? measurements.Sum(x => x.Max) + measurer.GetNonColumnWidth(); return new Measurement(minTableWidth, maxTableWidth); } /// protected override IEnumerable Render(RenderContext context, int maxWidth) { if (context is null) { throw new ArgumentNullException(nameof(context)); } var measurer = new TableMeasurer(this, context); // Calculate the column and table width var totalCellWidth = measurer.CalculateTotalCellWidth(maxWidth); var columnWidths = measurer.CalculateColumnWidths(totalCellWidth); var tableWidth = columnWidths.Sum() + measurer.GetNonColumnWidth(); // Get the rows to render var rows = GetRenderableRows(); // Render the table return TableRenderer.Render( new TableRendererContext(this, context, rows, tableWidth, maxWidth), columnWidths); } private List GetRenderableRows() { var rows = new List(); // Show headers? if (ShowHeaders) { rows.Add(TableRow.Header(_columns.Select(c => c.Header))); } // Add rows rows.AddRange(Rows); // Show footers? if (ShowFooters && _columns.Any(c => c.Footer != null)) { rows.Add(TableRow.Footer(_columns.Select(c => c.Footer ?? Text.Empty))); } return rows; } } }