mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-09-18 02:15:34 +08:00
Use file scoped namespace declarations
This commit is contained in:

committed by
Phil Scott

parent
1dbaf50935
commit
ec1188b837
@@ -3,171 +3,170 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console
|
||||
namespace Spectre.Console;
|
||||
|
||||
/// <summary>
|
||||
/// A renderable table.
|
||||
/// </summary>
|
||||
public sealed class Table : Renderable, IHasTableBorder, IExpandable, IAlignable
|
||||
{
|
||||
private readonly List<TableColumn> _columns;
|
||||
|
||||
/// <summary>
|
||||
/// A renderable table.
|
||||
/// Gets the table columns.
|
||||
/// </summary>
|
||||
public sealed class Table : Renderable, IHasTableBorder, IExpandable, IAlignable
|
||||
public IReadOnlyList<TableColumn> Columns => _columns;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the table rows.
|
||||
/// </summary>
|
||||
public TableRowCollection Rows { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TableBorder Border { get; set; } = TableBorder.Square;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Style? BorderStyle { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool UseSafeBorder { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not table headers should be shown.
|
||||
/// </summary>
|
||||
public bool ShowHeaders { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not table footers should be shown.
|
||||
/// </summary>
|
||||
public bool ShowFooters { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not the table should
|
||||
/// fit the available space. If <c>false</c>, the table width will be
|
||||
/// auto calculated. Defaults to <c>false</c>.
|
||||
/// </summary>
|
||||
public bool Expand { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the table.
|
||||
/// </summary>
|
||||
public int? Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the table title.
|
||||
/// </summary>
|
||||
public TableTitle? Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the table footnote.
|
||||
/// </summary>
|
||||
public TableTitle? Caption { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Table"/> class.
|
||||
/// </summary>
|
||||
public Table()
|
||||
{
|
||||
private readonly List<TableColumn> _columns;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the table columns.
|
||||
/// </summary>
|
||||
public IReadOnlyList<TableColumn> Columns => _columns;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the table rows.
|
||||
/// </summary>
|
||||
public TableRowCollection Rows { get; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public TableBorder Border { get; set; } = TableBorder.Square;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Style? BorderStyle { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool UseSafeBorder { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not table headers should be shown.
|
||||
/// </summary>
|
||||
public bool ShowHeaders { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not table footers should be shown.
|
||||
/// </summary>
|
||||
public bool ShowFooters { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether or not the table should
|
||||
/// fit the available space. If <c>false</c>, the table width will be
|
||||
/// auto calculated. Defaults to <c>false</c>.
|
||||
/// </summary>
|
||||
public bool Expand { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the table.
|
||||
/// </summary>
|
||||
public int? Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the table title.
|
||||
/// </summary>
|
||||
public TableTitle? Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the table footnote.
|
||||
/// </summary>
|
||||
public TableTitle? Caption { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Table"/> class.
|
||||
/// </summary>
|
||||
public Table()
|
||||
{
|
||||
_columns = new List<TableColumn>();
|
||||
Rows = new TableRowCollection(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a column to the table.
|
||||
/// </summary>
|
||||
/// <param name="column">The column to add.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override IEnumerable<Segment> 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<TableRow> GetRenderableRows()
|
||||
{
|
||||
var rows = new List<TableRow>();
|
||||
|
||||
// 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;
|
||||
}
|
||||
_columns = new List<TableColumn>();
|
||||
Rows = new TableRowCollection(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a column to the table.
|
||||
/// </summary>
|
||||
/// <param name="column">The column to add.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override IEnumerable<Segment> 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<TableRow> GetRenderableRows()
|
||||
{
|
||||
var rows = new List<TableRow>();
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
@@ -2,21 +2,20 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console
|
||||
namespace Spectre.Console;
|
||||
|
||||
internal abstract class TableAccessor
|
||||
{
|
||||
internal abstract class TableAccessor
|
||||
private readonly Table _table;
|
||||
|
||||
public RenderContext Options { get; }
|
||||
public IReadOnlyList<TableColumn> Columns => _table.Columns;
|
||||
public virtual IReadOnlyList<TableRow> Rows => _table.Rows;
|
||||
public bool Expand => _table.Expand || _table.Width != null;
|
||||
|
||||
protected TableAccessor(Table table, RenderContext options)
|
||||
{
|
||||
private readonly Table _table;
|
||||
|
||||
public RenderContext Options { get; }
|
||||
public IReadOnlyList<TableColumn> Columns => _table.Columns;
|
||||
public virtual IReadOnlyList<TableRow> Rows => _table.Rows;
|
||||
public bool Expand => _table.Expand || _table.Width != null;
|
||||
|
||||
protected TableAccessor(Table table, RenderContext options)
|
||||
{
|
||||
_table = table ?? throw new ArgumentNullException(nameof(table));
|
||||
Options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
_table = table ?? throw new ArgumentNullException(nameof(table));
|
||||
Options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,66 +1,65 @@
|
||||
using System;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console
|
||||
namespace Spectre.Console;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a table column.
|
||||
/// </summary>
|
||||
public sealed class TableColumn : IColumn
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a table column.
|
||||
/// Gets or sets the column header.
|
||||
/// </summary>
|
||||
public sealed class TableColumn : IColumn
|
||||
public IRenderable Header { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the column footer.
|
||||
/// </summary>
|
||||
public IRenderable? Footer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the column.
|
||||
/// If <c>null</c>, the column will adapt to its contents.
|
||||
/// </summary>
|
||||
public int? Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the padding of the column.
|
||||
/// Vertical padding (top and bottom) is ignored.
|
||||
/// </summary>
|
||||
public Padding? Padding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether wrapping of
|
||||
/// text within the column should be prevented.
|
||||
/// </summary>
|
||||
public bool NoWrap { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alignment of the column.
|
||||
/// </summary>
|
||||
public Justify? Alignment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TableColumn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="header">The table column header.</param>
|
||||
public TableColumn(string header)
|
||||
: this(new Markup(header).Overflow(Overflow.Ellipsis))
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the column header.
|
||||
/// </summary>
|
||||
public IRenderable Header { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the column footer.
|
||||
/// </summary>
|
||||
public IRenderable? Footer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the width of the column.
|
||||
/// If <c>null</c>, the column will adapt to its contents.
|
||||
/// </summary>
|
||||
public int? Width { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the padding of the column.
|
||||
/// Vertical padding (top and bottom) is ignored.
|
||||
/// </summary>
|
||||
public Padding? Padding { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether wrapping of
|
||||
/// text within the column should be prevented.
|
||||
/// </summary>
|
||||
public bool NoWrap { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the alignment of the column.
|
||||
/// </summary>
|
||||
public Justify? Alignment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TableColumn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="header">The table column header.</param>
|
||||
public TableColumn(string header)
|
||||
: this(new Markup(header).Overflow(Overflow.Ellipsis))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TableColumn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="header">The <see cref="IRenderable"/> instance to use as the table column header.</param>
|
||||
public TableColumn(IRenderable header)
|
||||
{
|
||||
Header = header ?? throw new ArgumentNullException(nameof(header));
|
||||
Width = null;
|
||||
Padding = new Padding(1, 0, 1, 0);
|
||||
NoWrap = false;
|
||||
Alignment = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TableColumn"/> class.
|
||||
/// </summary>
|
||||
/// <param name="header">The <see cref="IRenderable"/> instance to use as the table column header.</param>
|
||||
public TableColumn(IRenderable header)
|
||||
{
|
||||
Header = header ?? throw new ArgumentNullException(nameof(header));
|
||||
Width = null;
|
||||
Padding = new Padding(1, 0, 1, 0);
|
||||
NoWrap = false;
|
||||
Alignment = null;
|
||||
}
|
||||
}
|
@@ -3,158 +3,157 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console
|
||||
namespace Spectre.Console;
|
||||
|
||||
internal sealed class TableMeasurer : TableAccessor
|
||||
{
|
||||
internal sealed class TableMeasurer : TableAccessor
|
||||
private const int EdgeCount = 2;
|
||||
|
||||
private readonly int? _explicitWidth;
|
||||
private readonly TableBorder _border;
|
||||
private readonly bool _padRightCell;
|
||||
|
||||
public TableMeasurer(Table table, RenderContext options)
|
||||
: base(table, options)
|
||||
{
|
||||
private const int EdgeCount = 2;
|
||||
_explicitWidth = table.Width;
|
||||
_border = table.Border;
|
||||
_padRightCell = table.PadRightCell;
|
||||
}
|
||||
|
||||
private readonly int? _explicitWidth;
|
||||
private readonly TableBorder _border;
|
||||
private readonly bool _padRightCell;
|
||||
|
||||
public TableMeasurer(Table table, RenderContext options)
|
||||
: base(table, options)
|
||||
public int CalculateTotalCellWidth(int maxWidth)
|
||||
{
|
||||
var totalCellWidth = maxWidth;
|
||||
if (_explicitWidth != null)
|
||||
{
|
||||
_explicitWidth = table.Width;
|
||||
_border = table.Border;
|
||||
_padRightCell = table.PadRightCell;
|
||||
totalCellWidth = Math.Min(_explicitWidth.Value, maxWidth);
|
||||
}
|
||||
|
||||
public int CalculateTotalCellWidth(int maxWidth)
|
||||
{
|
||||
var totalCellWidth = maxWidth;
|
||||
if (_explicitWidth != null)
|
||||
{
|
||||
totalCellWidth = Math.Min(_explicitWidth.Value, maxWidth);
|
||||
}
|
||||
return totalCellWidth - GetNonColumnWidth();
|
||||
}
|
||||
|
||||
return totalCellWidth - GetNonColumnWidth();
|
||||
/// <summary>
|
||||
/// Gets the width of everything that's not a cell.
|
||||
/// That means separators, edges and padding.
|
||||
/// </summary>
|
||||
/// <returns>The width of everything that's not a cell.</returns>
|
||||
public int GetNonColumnWidth()
|
||||
{
|
||||
var hideBorder = !_border.Visible;
|
||||
var separators = hideBorder ? 0 : Columns.Count - 1;
|
||||
var edges = hideBorder ? 0 : EdgeCount;
|
||||
var padding = Columns.Select(x => x.Padding?.GetWidth() ?? 0).Sum();
|
||||
|
||||
if (!_padRightCell)
|
||||
{
|
||||
padding -= Columns.Last().Padding.GetRightSafe();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width of everything that's not a cell.
|
||||
/// That means separators, edges and padding.
|
||||
/// </summary>
|
||||
/// <returns>The width of everything that's not a cell.</returns>
|
||||
public int GetNonColumnWidth()
|
||||
return separators + edges + padding;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the width of all columns minus any padding.
|
||||
/// </summary>
|
||||
/// <param name="maxWidth">The maximum width that the columns may occupy.</param>
|
||||
/// <returns>A list of column widths.</returns>
|
||||
public List<int> CalculateColumnWidths(int maxWidth)
|
||||
{
|
||||
var width_ranges = Columns.Select(column => MeasureColumn(column, maxWidth)).ToArray();
|
||||
var widths = width_ranges.Select(range => range.Max).ToList();
|
||||
|
||||
var tableWidth = widths.Sum();
|
||||
if (tableWidth > maxWidth)
|
||||
{
|
||||
var hideBorder = !_border.Visible;
|
||||
var separators = hideBorder ? 0 : Columns.Count - 1;
|
||||
var edges = hideBorder ? 0 : EdgeCount;
|
||||
var padding = Columns.Select(x => x.Padding?.GetWidth() ?? 0).Sum();
|
||||
var wrappable = Columns.Select(c => !c.NoWrap).ToList();
|
||||
widths = CollapseWidths(widths, wrappable, maxWidth);
|
||||
tableWidth = widths.Sum();
|
||||
|
||||
if (!_padRightCell)
|
||||
{
|
||||
padding -= Columns.Last().Padding.GetRightSafe();
|
||||
}
|
||||
|
||||
return separators + edges + padding;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the width of all columns minus any padding.
|
||||
/// </summary>
|
||||
/// <param name="maxWidth">The maximum width that the columns may occupy.</param>
|
||||
/// <returns>A list of column widths.</returns>
|
||||
public List<int> CalculateColumnWidths(int maxWidth)
|
||||
{
|
||||
var width_ranges = Columns.Select(column => MeasureColumn(column, maxWidth)).ToArray();
|
||||
var widths = width_ranges.Select(range => range.Max).ToList();
|
||||
|
||||
var tableWidth = widths.Sum();
|
||||
// last resort, reduce columns evenly
|
||||
if (tableWidth > maxWidth)
|
||||
{
|
||||
var wrappable = Columns.Select(c => !c.NoWrap).ToList();
|
||||
widths = CollapseWidths(widths, wrappable, maxWidth);
|
||||
var excessWidth = tableWidth - maxWidth;
|
||||
widths = Ratio.Reduce(excessWidth, widths.Select(_ => 1).ToList(), widths, widths);
|
||||
tableWidth = widths.Sum();
|
||||
|
||||
// last resort, reduce columns evenly
|
||||
if (tableWidth > maxWidth)
|
||||
{
|
||||
var excessWidth = tableWidth - maxWidth;
|
||||
widths = Ratio.Reduce(excessWidth, widths.Select(_ => 1).ToList(), widths, widths);
|
||||
tableWidth = widths.Sum();
|
||||
}
|
||||
}
|
||||
|
||||
if (tableWidth < maxWidth && Expand)
|
||||
{
|
||||
var padWidths = Ratio.Distribute(maxWidth - tableWidth, widths);
|
||||
widths = widths.Zip(padWidths, (a, b) => (a, b)).Select(f => f.a + f.b).ToList();
|
||||
}
|
||||
|
||||
return widths;
|
||||
}
|
||||
|
||||
public Measurement MeasureColumn(TableColumn column, int maxWidth)
|
||||
if (tableWidth < maxWidth && Expand)
|
||||
{
|
||||
// Predetermined width?
|
||||
if (column.Width != null)
|
||||
{
|
||||
return new Measurement(column.Width.Value, column.Width.Value);
|
||||
}
|
||||
|
||||
var columnIndex = Columns.IndexOf(column);
|
||||
var rows = Rows.Select(row => row[columnIndex]);
|
||||
|
||||
var minWidths = new List<int>();
|
||||
var maxWidths = new List<int>();
|
||||
|
||||
// Include columns (both header and footer) in measurement
|
||||
var headerMeasure = column.Header.Measure(Options, maxWidth);
|
||||
var footerMeasure = column.Footer?.Measure(Options, maxWidth) ?? headerMeasure;
|
||||
minWidths.Add(Math.Min(headerMeasure.Min, footerMeasure.Min));
|
||||
maxWidths.Add(Math.Max(headerMeasure.Max, footerMeasure.Max));
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var rowMeasure = row.Measure(Options, maxWidth);
|
||||
minWidths.Add(rowMeasure.Min);
|
||||
maxWidths.Add(rowMeasure.Max);
|
||||
}
|
||||
|
||||
var padding = column.Padding?.GetWidth() ?? 0;
|
||||
|
||||
return new Measurement(
|
||||
minWidths.Count > 0 ? minWidths.Max() : padding,
|
||||
maxWidths.Count > 0 ? maxWidths.Max() : maxWidth);
|
||||
var padWidths = Ratio.Distribute(maxWidth - tableWidth, widths);
|
||||
widths = widths.Zip(padWidths, (a, b) => (a, b)).Select(f => f.a + f.b).ToList();
|
||||
}
|
||||
|
||||
// Reduce widths so that the total is less or equal to the max width.
|
||||
// Ported from Rich by Will McGugan, licensed under MIT.
|
||||
// https://github.com/willmcgugan/rich/blob/527475837ebbfc427530b3ee0d4d0741d2d0fc6d/rich/table.py#L442
|
||||
private static List<int> CollapseWidths(List<int> widths, List<bool> wrappable, int maxWidth)
|
||||
{
|
||||
var totalWidth = widths.Sum();
|
||||
var excessWidth = totalWidth - maxWidth;
|
||||
|
||||
if (wrappable.AnyTrue())
|
||||
{
|
||||
while (totalWidth != 0 && excessWidth > 0)
|
||||
{
|
||||
var maxColumn = widths.Zip(wrappable, (first, second) => (width: first, allowWrap: second))
|
||||
.Where(x => x.allowWrap)
|
||||
.Max(x => x.width);
|
||||
|
||||
var secondMaxColumn = widths.Zip(wrappable, (width, allowWrap) => allowWrap && width != maxColumn ? width : 1).Max();
|
||||
var columnDifference = maxColumn - secondMaxColumn;
|
||||
|
||||
var ratios = widths.Zip(wrappable, (width, allowWrap) => width == maxColumn && allowWrap ? 1 : 0).ToList();
|
||||
if (!ratios.Any(x => x != 0) || columnDifference == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var maxReduce = widths.Select(_ => Math.Min(excessWidth, columnDifference)).ToList();
|
||||
widths = Ratio.Reduce(excessWidth, ratios, maxReduce, widths);
|
||||
|
||||
totalWidth = widths.Sum();
|
||||
excessWidth = totalWidth - maxWidth;
|
||||
}
|
||||
}
|
||||
|
||||
return widths;
|
||||
}
|
||||
return widths;
|
||||
}
|
||||
}
|
||||
|
||||
public Measurement MeasureColumn(TableColumn column, int maxWidth)
|
||||
{
|
||||
// Predetermined width?
|
||||
if (column.Width != null)
|
||||
{
|
||||
return new Measurement(column.Width.Value, column.Width.Value);
|
||||
}
|
||||
|
||||
var columnIndex = Columns.IndexOf(column);
|
||||
var rows = Rows.Select(row => row[columnIndex]);
|
||||
|
||||
var minWidths = new List<int>();
|
||||
var maxWidths = new List<int>();
|
||||
|
||||
// Include columns (both header and footer) in measurement
|
||||
var headerMeasure = column.Header.Measure(Options, maxWidth);
|
||||
var footerMeasure = column.Footer?.Measure(Options, maxWidth) ?? headerMeasure;
|
||||
minWidths.Add(Math.Min(headerMeasure.Min, footerMeasure.Min));
|
||||
maxWidths.Add(Math.Max(headerMeasure.Max, footerMeasure.Max));
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
var rowMeasure = row.Measure(Options, maxWidth);
|
||||
minWidths.Add(rowMeasure.Min);
|
||||
maxWidths.Add(rowMeasure.Max);
|
||||
}
|
||||
|
||||
var padding = column.Padding?.GetWidth() ?? 0;
|
||||
|
||||
return new Measurement(
|
||||
minWidths.Count > 0 ? minWidths.Max() : padding,
|
||||
maxWidths.Count > 0 ? maxWidths.Max() : maxWidth);
|
||||
}
|
||||
|
||||
// Reduce widths so that the total is less or equal to the max width.
|
||||
// Ported from Rich by Will McGugan, licensed under MIT.
|
||||
// https://github.com/willmcgugan/rich/blob/527475837ebbfc427530b3ee0d4d0741d2d0fc6d/rich/table.py#L442
|
||||
private static List<int> CollapseWidths(List<int> widths, List<bool> wrappable, int maxWidth)
|
||||
{
|
||||
var totalWidth = widths.Sum();
|
||||
var excessWidth = totalWidth - maxWidth;
|
||||
|
||||
if (wrappable.AnyTrue())
|
||||
{
|
||||
while (totalWidth != 0 && excessWidth > 0)
|
||||
{
|
||||
var maxColumn = widths.Zip(wrappable, (first, second) => (width: first, allowWrap: second))
|
||||
.Where(x => x.allowWrap)
|
||||
.Max(x => x.width);
|
||||
|
||||
var secondMaxColumn = widths.Zip(wrappable, (width, allowWrap) => allowWrap && width != maxColumn ? width : 1).Max();
|
||||
var columnDifference = maxColumn - secondMaxColumn;
|
||||
|
||||
var ratios = widths.Zip(wrappable, (width, allowWrap) => width == maxColumn && allowWrap ? 1 : 0).ToList();
|
||||
if (!ratios.Any(x => x != 0) || columnDifference == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var maxReduce = widths.Select(_ => Math.Min(excessWidth, columnDifference)).ToList();
|
||||
widths = Ratio.Reduce(excessWidth, ratios, maxReduce, widths);
|
||||
|
||||
totalWidth = widths.Sum();
|
||||
excessWidth = totalWidth - maxWidth;
|
||||
}
|
||||
}
|
||||
|
||||
return widths;
|
||||
}
|
||||
}
|
@@ -3,179 +3,178 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console
|
||||
namespace Spectre.Console;
|
||||
|
||||
internal static class TableRenderer
|
||||
{
|
||||
internal static class TableRenderer
|
||||
private static readonly Style _defaultHeadingStyle = new Style(Color.Silver);
|
||||
private static readonly Style _defaultCaptionStyle = new Style(Color.Grey);
|
||||
|
||||
public static List<Segment> Render(TableRendererContext context, List<int> columnWidths)
|
||||
{
|
||||
private static readonly Style _defaultHeadingStyle = new Style(Color.Silver);
|
||||
private static readonly Style _defaultCaptionStyle = new Style(Color.Grey);
|
||||
|
||||
public static List<Segment> Render(TableRendererContext context, List<int> columnWidths)
|
||||
// Can't render the table?
|
||||
if (context.TableWidth <= 0 || context.TableWidth > context.MaxWidth || columnWidths.Any(c => c <= 0))
|
||||
{
|
||||
// Can't render the table?
|
||||
if (context.TableWidth <= 0 || context.TableWidth > context.MaxWidth || columnWidths.Any(c => c <= 0))
|
||||
{
|
||||
return new List<Segment>(new[] { new Segment("…", context.BorderStyle ?? Style.Plain) });
|
||||
}
|
||||
|
||||
var result = new List<Segment>();
|
||||
result.AddRange(RenderAnnotation(context, context.Title, _defaultHeadingStyle));
|
||||
|
||||
// Iterate all rows
|
||||
foreach (var (index, isFirstRow, isLastRow, row) in context.Rows.Enumerate())
|
||||
{
|
||||
var cellHeight = 1;
|
||||
|
||||
// Get the list of cells for the row and calculate the cell height
|
||||
var cells = new List<List<SegmentLine>>();
|
||||
foreach (var (columnIndex, _, _, (rowWidth, cell)) in columnWidths.Zip(row).Enumerate())
|
||||
{
|
||||
var justification = context.Columns[columnIndex].Alignment;
|
||||
var childContext = context.Options.WithJustification(justification);
|
||||
|
||||
var lines = Segment.SplitLines(cell.Render(childContext, rowWidth));
|
||||
cellHeight = Math.Max(cellHeight, lines.Count);
|
||||
cells.Add(lines);
|
||||
}
|
||||
|
||||
// Show top of header?
|
||||
if (isFirstRow && context.ShowBorder)
|
||||
{
|
||||
var separator = Aligner.Align(context.Border.GetColumnRow(TablePart.Top, columnWidths, context.Columns), context.Alignment, context.MaxWidth);
|
||||
result.Add(new Segment(separator, context.BorderStyle));
|
||||
result.Add(Segment.LineBreak);
|
||||
}
|
||||
|
||||
// Show footer separator?
|
||||
if (context.ShowFooters && isLastRow && context.ShowBorder && context.HasFooters)
|
||||
{
|
||||
var textBorder = context.Border.GetColumnRow(TablePart.FooterSeparator, columnWidths, context.Columns);
|
||||
if (!string.IsNullOrEmpty(textBorder))
|
||||
{
|
||||
var separator = Aligner.Align(textBorder, context.Alignment, context.MaxWidth);
|
||||
result.Add(new Segment(separator, context.BorderStyle));
|
||||
result.Add(Segment.LineBreak);
|
||||
}
|
||||
}
|
||||
|
||||
// Make cells the same shape
|
||||
cells = Segment.MakeSameHeight(cellHeight, cells);
|
||||
|
||||
// Iterate through each cell row
|
||||
foreach (var cellRowIndex in Enumerable.Range(0, cellHeight))
|
||||
{
|
||||
var rowResult = new List<Segment>();
|
||||
|
||||
foreach (var (cellIndex, isFirstCell, isLastCell, cell) in cells.Enumerate())
|
||||
{
|
||||
if (isFirstCell && context.ShowBorder)
|
||||
{
|
||||
// Show left column edge
|
||||
var part = isFirstRow && context.ShowHeaders ? TableBorderPart.HeaderLeft : TableBorderPart.CellLeft;
|
||||
rowResult.Add(new Segment(context.Border.GetPart(part), context.BorderStyle));
|
||||
}
|
||||
|
||||
// Pad column on left side.
|
||||
if (context.ShowBorder || context.IsGrid)
|
||||
{
|
||||
var leftPadding = context.Columns[cellIndex].Padding.GetLeftSafe();
|
||||
if (leftPadding > 0)
|
||||
{
|
||||
rowResult.Add(new Segment(new string(' ', leftPadding)));
|
||||
}
|
||||
}
|
||||
|
||||
// Add content
|
||||
rowResult.AddRange(cell[cellRowIndex]);
|
||||
|
||||
// Pad cell content right
|
||||
var length = cell[cellRowIndex].Sum(segment => segment.CellCount());
|
||||
if (length < columnWidths[cellIndex])
|
||||
{
|
||||
rowResult.Add(new Segment(new string(' ', columnWidths[cellIndex] - length)));
|
||||
}
|
||||
|
||||
// Pad column on the right side
|
||||
if (context.ShowBorder || (context.HideBorder && !isLastCell) || (context.HideBorder && isLastCell && context.IsGrid && context.PadRightCell))
|
||||
{
|
||||
var rightPadding = context.Columns[cellIndex].Padding.GetRightSafe();
|
||||
if (rightPadding > 0)
|
||||
{
|
||||
rowResult.Add(new Segment(new string(' ', rightPadding)));
|
||||
}
|
||||
}
|
||||
|
||||
if (isLastCell && context.ShowBorder)
|
||||
{
|
||||
// Add right column edge
|
||||
var part = isFirstRow && context.ShowHeaders ? TableBorderPart.HeaderRight : TableBorderPart.CellRight;
|
||||
rowResult.Add(new Segment(context.Border.GetPart(part), context.BorderStyle));
|
||||
}
|
||||
else if (context.ShowBorder)
|
||||
{
|
||||
// Add column separator
|
||||
var part = isFirstRow && context.ShowHeaders ? TableBorderPart.HeaderSeparator : TableBorderPart.CellSeparator;
|
||||
rowResult.Add(new Segment(context.Border.GetPart(part), context.BorderStyle));
|
||||
}
|
||||
}
|
||||
|
||||
// Align the row result.
|
||||
Aligner.Align(context.Options, rowResult, context.Alignment, context.MaxWidth);
|
||||
|
||||
// Is the row larger than the allowed max width?
|
||||
if (Segment.CellCount(rowResult) > context.MaxWidth)
|
||||
{
|
||||
result.AddRange(Segment.Truncate(rowResult, context.MaxWidth));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.AddRange(rowResult);
|
||||
}
|
||||
|
||||
result.Add(Segment.LineBreak);
|
||||
}
|
||||
|
||||
// Show header separator?
|
||||
if (isFirstRow && context.ShowBorder && context.ShowHeaders && context.HasRows)
|
||||
{
|
||||
var separator = Aligner.Align(context.Border.GetColumnRow(TablePart.HeaderSeparator, columnWidths, context.Columns), context.Alignment, context.MaxWidth);
|
||||
result.Add(new Segment(separator, context.BorderStyle));
|
||||
result.Add(Segment.LineBreak);
|
||||
}
|
||||
|
||||
// Show bottom of footer?
|
||||
if (isLastRow && context.ShowBorder)
|
||||
{
|
||||
var separator = Aligner.Align(context.Border.GetColumnRow(TablePart.Bottom, columnWidths, context.Columns), context.Alignment, context.MaxWidth);
|
||||
result.Add(new Segment(separator, context.BorderStyle));
|
||||
result.Add(Segment.LineBreak);
|
||||
}
|
||||
}
|
||||
|
||||
result.AddRange(RenderAnnotation(context, context.Caption, _defaultCaptionStyle));
|
||||
return result;
|
||||
return new List<Segment>(new[] { new Segment("…", context.BorderStyle ?? Style.Plain) });
|
||||
}
|
||||
|
||||
private static IEnumerable<Segment> RenderAnnotation(TableRendererContext context, TableTitle? header, Style defaultStyle)
|
||||
var result = new List<Segment>();
|
||||
result.AddRange(RenderAnnotation(context, context.Title, _defaultHeadingStyle));
|
||||
|
||||
// Iterate all rows
|
||||
foreach (var (index, isFirstRow, isLastRow, row) in context.Rows.Enumerate())
|
||||
{
|
||||
if (header == null)
|
||||
var cellHeight = 1;
|
||||
|
||||
// Get the list of cells for the row and calculate the cell height
|
||||
var cells = new List<List<SegmentLine>>();
|
||||
foreach (var (columnIndex, _, _, (rowWidth, cell)) in columnWidths.Zip(row).Enumerate())
|
||||
{
|
||||
return Array.Empty<Segment>();
|
||||
var justification = context.Columns[columnIndex].Alignment;
|
||||
var childContext = context.Options.WithJustification(justification);
|
||||
|
||||
var lines = Segment.SplitLines(cell.Render(childContext, rowWidth));
|
||||
cellHeight = Math.Max(cellHeight, lines.Count);
|
||||
cells.Add(lines);
|
||||
}
|
||||
|
||||
var paragraph = new Markup(header.Text, header.Style ?? defaultStyle)
|
||||
.Alignment(Justify.Center)
|
||||
.Overflow(Overflow.Ellipsis);
|
||||
// Show top of header?
|
||||
if (isFirstRow && context.ShowBorder)
|
||||
{
|
||||
var separator = Aligner.Align(context.Border.GetColumnRow(TablePart.Top, columnWidths, context.Columns), context.Alignment, context.MaxWidth);
|
||||
result.Add(new Segment(separator, context.BorderStyle));
|
||||
result.Add(Segment.LineBreak);
|
||||
}
|
||||
|
||||
// Render the paragraphs
|
||||
var segments = new List<Segment>();
|
||||
segments.AddRange(((IRenderable)paragraph).Render(context.Options, context.TableWidth));
|
||||
// Show footer separator?
|
||||
if (context.ShowFooters && isLastRow && context.ShowBorder && context.HasFooters)
|
||||
{
|
||||
var textBorder = context.Border.GetColumnRow(TablePart.FooterSeparator, columnWidths, context.Columns);
|
||||
if (!string.IsNullOrEmpty(textBorder))
|
||||
{
|
||||
var separator = Aligner.Align(textBorder, context.Alignment, context.MaxWidth);
|
||||
result.Add(new Segment(separator, context.BorderStyle));
|
||||
result.Add(Segment.LineBreak);
|
||||
}
|
||||
}
|
||||
|
||||
// Align over the whole buffer area
|
||||
Aligner.Align(context.Options, segments, context.Alignment, context.MaxWidth);
|
||||
// Make cells the same shape
|
||||
cells = Segment.MakeSameHeight(cellHeight, cells);
|
||||
|
||||
segments.Add(Segment.LineBreak);
|
||||
return segments;
|
||||
// Iterate through each cell row
|
||||
foreach (var cellRowIndex in Enumerable.Range(0, cellHeight))
|
||||
{
|
||||
var rowResult = new List<Segment>();
|
||||
|
||||
foreach (var (cellIndex, isFirstCell, isLastCell, cell) in cells.Enumerate())
|
||||
{
|
||||
if (isFirstCell && context.ShowBorder)
|
||||
{
|
||||
// Show left column edge
|
||||
var part = isFirstRow && context.ShowHeaders ? TableBorderPart.HeaderLeft : TableBorderPart.CellLeft;
|
||||
rowResult.Add(new Segment(context.Border.GetPart(part), context.BorderStyle));
|
||||
}
|
||||
|
||||
// Pad column on left side.
|
||||
if (context.ShowBorder || context.IsGrid)
|
||||
{
|
||||
var leftPadding = context.Columns[cellIndex].Padding.GetLeftSafe();
|
||||
if (leftPadding > 0)
|
||||
{
|
||||
rowResult.Add(new Segment(new string(' ', leftPadding)));
|
||||
}
|
||||
}
|
||||
|
||||
// Add content
|
||||
rowResult.AddRange(cell[cellRowIndex]);
|
||||
|
||||
// Pad cell content right
|
||||
var length = cell[cellRowIndex].Sum(segment => segment.CellCount());
|
||||
if (length < columnWidths[cellIndex])
|
||||
{
|
||||
rowResult.Add(new Segment(new string(' ', columnWidths[cellIndex] - length)));
|
||||
}
|
||||
|
||||
// Pad column on the right side
|
||||
if (context.ShowBorder || (context.HideBorder && !isLastCell) || (context.HideBorder && isLastCell && context.IsGrid && context.PadRightCell))
|
||||
{
|
||||
var rightPadding = context.Columns[cellIndex].Padding.GetRightSafe();
|
||||
if (rightPadding > 0)
|
||||
{
|
||||
rowResult.Add(new Segment(new string(' ', rightPadding)));
|
||||
}
|
||||
}
|
||||
|
||||
if (isLastCell && context.ShowBorder)
|
||||
{
|
||||
// Add right column edge
|
||||
var part = isFirstRow && context.ShowHeaders ? TableBorderPart.HeaderRight : TableBorderPart.CellRight;
|
||||
rowResult.Add(new Segment(context.Border.GetPart(part), context.BorderStyle));
|
||||
}
|
||||
else if (context.ShowBorder)
|
||||
{
|
||||
// Add column separator
|
||||
var part = isFirstRow && context.ShowHeaders ? TableBorderPart.HeaderSeparator : TableBorderPart.CellSeparator;
|
||||
rowResult.Add(new Segment(context.Border.GetPart(part), context.BorderStyle));
|
||||
}
|
||||
}
|
||||
|
||||
// Align the row result.
|
||||
Aligner.Align(context.Options, rowResult, context.Alignment, context.MaxWidth);
|
||||
|
||||
// Is the row larger than the allowed max width?
|
||||
if (Segment.CellCount(rowResult) > context.MaxWidth)
|
||||
{
|
||||
result.AddRange(Segment.Truncate(rowResult, context.MaxWidth));
|
||||
}
|
||||
else
|
||||
{
|
||||
result.AddRange(rowResult);
|
||||
}
|
||||
|
||||
result.Add(Segment.LineBreak);
|
||||
}
|
||||
|
||||
// Show header separator?
|
||||
if (isFirstRow && context.ShowBorder && context.ShowHeaders && context.HasRows)
|
||||
{
|
||||
var separator = Aligner.Align(context.Border.GetColumnRow(TablePart.HeaderSeparator, columnWidths, context.Columns), context.Alignment, context.MaxWidth);
|
||||
result.Add(new Segment(separator, context.BorderStyle));
|
||||
result.Add(Segment.LineBreak);
|
||||
}
|
||||
|
||||
// Show bottom of footer?
|
||||
if (isLastRow && context.ShowBorder)
|
||||
{
|
||||
var separator = Aligner.Align(context.Border.GetColumnRow(TablePart.Bottom, columnWidths, context.Columns), context.Alignment, context.MaxWidth);
|
||||
result.Add(new Segment(separator, context.BorderStyle));
|
||||
result.Add(Segment.LineBreak);
|
||||
}
|
||||
}
|
||||
|
||||
result.AddRange(RenderAnnotation(context, context.Caption, _defaultCaptionStyle));
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static IEnumerable<Segment> RenderAnnotation(TableRendererContext context, TableTitle? header, Style defaultStyle)
|
||||
{
|
||||
if (header == null)
|
||||
{
|
||||
return Array.Empty<Segment>();
|
||||
}
|
||||
|
||||
var paragraph = new Markup(header.Text, header.Style ?? defaultStyle)
|
||||
.Alignment(Justify.Center)
|
||||
.Overflow(Overflow.Ellipsis);
|
||||
|
||||
// Render the paragraphs
|
||||
var segments = new List<Segment>();
|
||||
segments.AddRange(((IRenderable)paragraph).Render(context.Options, context.TableWidth));
|
||||
|
||||
// Align over the whole buffer area
|
||||
Aligner.Align(context.Options, segments, context.Alignment, context.MaxWidth);
|
||||
|
||||
segments.Add(Segment.LineBreak);
|
||||
return segments;
|
||||
}
|
||||
}
|
@@ -3,55 +3,54 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console
|
||||
namespace Spectre.Console;
|
||||
|
||||
internal sealed class TableRendererContext : TableAccessor
|
||||
{
|
||||
internal sealed class TableRendererContext : TableAccessor
|
||||
private readonly Table _table;
|
||||
private readonly List<TableRow> _rows;
|
||||
|
||||
public override IReadOnlyList<TableRow> Rows => _rows;
|
||||
|
||||
public TableBorder Border { get; }
|
||||
public Style BorderStyle { get; }
|
||||
public bool ShowBorder { get; }
|
||||
public bool HasRows { get; }
|
||||
public bool HasFooters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the max width of the destination area.
|
||||
/// The table might take up less than this.
|
||||
/// </summary>
|
||||
public int MaxWidth { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width of the table.
|
||||
/// </summary>
|
||||
public int TableWidth { get; }
|
||||
|
||||
public bool HideBorder => !ShowBorder;
|
||||
public bool ShowHeaders => _table.ShowHeaders;
|
||||
public bool ShowFooters => _table.ShowFooters;
|
||||
public bool IsGrid => _table.IsGrid;
|
||||
public bool PadRightCell => _table.PadRightCell;
|
||||
public TableTitle? Title => _table.Title;
|
||||
public TableTitle? Caption => _table.Caption;
|
||||
public Justify? Alignment => _table.Alignment;
|
||||
|
||||
public TableRendererContext(Table table, RenderContext options, IEnumerable<TableRow> rows, int tableWidth, int maxWidth)
|
||||
: base(table, options)
|
||||
{
|
||||
private readonly Table _table;
|
||||
private readonly List<TableRow> _rows;
|
||||
_table = table ?? throw new ArgumentNullException(nameof(table));
|
||||
_rows = new List<TableRow>(rows ?? Enumerable.Empty<TableRow>());
|
||||
|
||||
public override IReadOnlyList<TableRow> Rows => _rows;
|
||||
ShowBorder = _table.Border.Visible;
|
||||
HasRows = Rows.Any(row => !row.IsHeader && !row.IsFooter);
|
||||
HasFooters = Rows.Any(column => column.IsFooter);
|
||||
Border = table.Border.GetSafeBorder(!options.Unicode && table.UseSafeBorder);
|
||||
BorderStyle = table.BorderStyle ?? Style.Plain;
|
||||
|
||||
public TableBorder Border { get; }
|
||||
public Style BorderStyle { get; }
|
||||
public bool ShowBorder { get; }
|
||||
public bool HasRows { get; }
|
||||
public bool HasFooters { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the max width of the destination area.
|
||||
/// The table might take up less than this.
|
||||
/// </summary>
|
||||
public int MaxWidth { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the width of the table.
|
||||
/// </summary>
|
||||
public int TableWidth { get; }
|
||||
|
||||
public bool HideBorder => !ShowBorder;
|
||||
public bool ShowHeaders => _table.ShowHeaders;
|
||||
public bool ShowFooters => _table.ShowFooters;
|
||||
public bool IsGrid => _table.IsGrid;
|
||||
public bool PadRightCell => _table.PadRightCell;
|
||||
public TableTitle? Title => _table.Title;
|
||||
public TableTitle? Caption => _table.Caption;
|
||||
public Justify? Alignment => _table.Alignment;
|
||||
|
||||
public TableRendererContext(Table table, RenderContext options, IEnumerable<TableRow> rows, int tableWidth, int maxWidth)
|
||||
: base(table, options)
|
||||
{
|
||||
_table = table ?? throw new ArgumentNullException(nameof(table));
|
||||
_rows = new List<TableRow>(rows ?? Enumerable.Empty<TableRow>());
|
||||
|
||||
ShowBorder = _table.Border.Visible;
|
||||
HasRows = Rows.Any(row => !row.IsHeader && !row.IsFooter);
|
||||
HasFooters = Rows.Any(column => column.IsFooter);
|
||||
Border = table.Border.GetSafeBorder(!options.Unicode && table.UseSafeBorder);
|
||||
BorderStyle = table.BorderStyle ?? Style.Plain;
|
||||
|
||||
TableWidth = tableWidth;
|
||||
MaxWidth = maxWidth;
|
||||
}
|
||||
TableWidth = tableWidth;
|
||||
MaxWidth = maxWidth;
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,80 +3,79 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console
|
||||
namespace Spectre.Console;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a table row.
|
||||
/// </summary>
|
||||
public sealed class TableRow : IEnumerable<IRenderable>
|
||||
{
|
||||
private readonly List<IRenderable> _items;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a table row.
|
||||
/// Gets the number of columns in the row.
|
||||
/// </summary>
|
||||
public sealed class TableRow : IEnumerable<IRenderable>
|
||||
public int Count => _items.Count;
|
||||
|
||||
internal bool IsHeader { get; }
|
||||
internal bool IsFooter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a row item at the specified table column index.
|
||||
/// </summary>
|
||||
/// <param name="index">The table column index.</param>
|
||||
/// <returns>The row item at the specified table column index.</returns>
|
||||
public IRenderable this[int index]
|
||||
{
|
||||
private readonly List<IRenderable> _items;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of columns in the row.
|
||||
/// </summary>
|
||||
public int Count => _items.Count;
|
||||
|
||||
internal bool IsHeader { get; }
|
||||
internal bool IsFooter { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a row item at the specified table column index.
|
||||
/// </summary>
|
||||
/// <param name="index">The table column index.</param>
|
||||
/// <returns>The row item at the specified table column index.</returns>
|
||||
public IRenderable this[int index]
|
||||
{
|
||||
get => _items[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TableRow"/> class.
|
||||
/// </summary>
|
||||
/// <param name="items">The row items.</param>
|
||||
public TableRow(IEnumerable<IRenderable> items)
|
||||
: this(items, false, false)
|
||||
{
|
||||
}
|
||||
|
||||
private TableRow(IEnumerable<IRenderable> items, bool isHeader, bool isFooter)
|
||||
{
|
||||
_items = new List<IRenderable>(items ?? Array.Empty<IRenderable>());
|
||||
|
||||
IsHeader = isHeader;
|
||||
IsFooter = isFooter;
|
||||
}
|
||||
|
||||
internal static TableRow Header(IEnumerable<IRenderable> items)
|
||||
{
|
||||
return new TableRow(items, true, false);
|
||||
}
|
||||
|
||||
internal static TableRow Footer(IEnumerable<IRenderable> items)
|
||||
{
|
||||
return new TableRow(items, false, true);
|
||||
}
|
||||
|
||||
internal void Add(IRenderable item)
|
||||
{
|
||||
if (item is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
}
|
||||
|
||||
_items.Add(item);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerator<IRenderable> GetEnumerator()
|
||||
{
|
||||
return _items.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
get => _items[index];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TableRow"/> class.
|
||||
/// </summary>
|
||||
/// <param name="items">The row items.</param>
|
||||
public TableRow(IEnumerable<IRenderable> items)
|
||||
: this(items, false, false)
|
||||
{
|
||||
}
|
||||
|
||||
private TableRow(IEnumerable<IRenderable> items, bool isHeader, bool isFooter)
|
||||
{
|
||||
_items = new List<IRenderable>(items ?? Array.Empty<IRenderable>());
|
||||
|
||||
IsHeader = isHeader;
|
||||
IsFooter = isFooter;
|
||||
}
|
||||
|
||||
internal static TableRow Header(IEnumerable<IRenderable> items)
|
||||
{
|
||||
return new TableRow(items, true, false);
|
||||
}
|
||||
|
||||
internal static TableRow Footer(IEnumerable<IRenderable> items)
|
||||
{
|
||||
return new TableRow(items, false, true);
|
||||
}
|
||||
|
||||
internal void Add(IRenderable item)
|
||||
{
|
||||
if (item is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
}
|
||||
|
||||
_items.Add(item);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerator<IRenderable> GetEnumerator()
|
||||
{
|
||||
return _items.GetEnumerator();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
}
|
@@ -4,206 +4,205 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console
|
||||
namespace Spectre.Console;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a collection holding table rows.
|
||||
/// </summary>
|
||||
public sealed class TableRowCollection : IReadOnlyList<TableRow>
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a collection holding table rows.
|
||||
/// </summary>
|
||||
public sealed class TableRowCollection : IReadOnlyList<TableRow>
|
||||
private readonly Table _table;
|
||||
private readonly IList<TableRow> _list;
|
||||
private readonly object _lock;
|
||||
|
||||
/// <inheritdoc/>
|
||||
TableRow IReadOnlyList<TableRow>.this[int index]
|
||||
{
|
||||
private readonly Table _table;
|
||||
private readonly IList<TableRow> _list;
|
||||
private readonly object _lock;
|
||||
|
||||
/// <inheritdoc/>
|
||||
TableRow IReadOnlyList<TableRow>.this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _list[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of rows in the collection.
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _list.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal TableRowCollection(Table table)
|
||||
{
|
||||
_table = table ?? throw new ArgumentNullException(nameof(table));
|
||||
_list = new List<TableRow>();
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new row.
|
||||
/// </summary>
|
||||
/// <param name="columns">The columns that are part of the row to add.</param>
|
||||
/// <returns>The index of the added item.</returns>
|
||||
public int Add(IEnumerable<IRenderable> columns)
|
||||
{
|
||||
if (columns is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(columns));
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
var row = CreateRow(columns);
|
||||
_list.Add(row);
|
||||
return _list.IndexOf(row);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new row at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index to insert the row at.</param>
|
||||
/// <param name="columns">The columns that are part of the row to insert.</param>
|
||||
/// <returns>The index of the inserted item.</returns>
|
||||
public int Insert(int index, IEnumerable<IRenderable> columns)
|
||||
{
|
||||
if (columns is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(columns));
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
var row = CreateRow(columns);
|
||||
_list.Insert(index, row);
|
||||
return _list.IndexOf(row);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update a table cell at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="row">Index of cell row.</param>
|
||||
/// <param name="column">index of cell column.</param>
|
||||
/// <param name="cellData">The new cells details.</param>
|
||||
public void Update(int row, int column, IRenderable cellData)
|
||||
{
|
||||
if (cellData is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(cellData));
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (row < 0)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table row index cannot be negative.");
|
||||
}
|
||||
else if (row >= _list.Count)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table row index cannot exceed the number of rows in the table.");
|
||||
}
|
||||
|
||||
var tableRow = _list.ElementAtOrDefault(row);
|
||||
|
||||
var currentRenderables = tableRow.ToList();
|
||||
|
||||
if (column < 0)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table column index cannot be negative.");
|
||||
}
|
||||
else if (column >= currentRenderables.Count)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table column index cannot exceed the number of rows in the table.");
|
||||
}
|
||||
|
||||
currentRenderables.RemoveAt(column);
|
||||
|
||||
currentRenderables.Insert(column, cellData);
|
||||
|
||||
var newTableRow = new TableRow(currentRenderables);
|
||||
|
||||
_list.RemoveAt(row);
|
||||
|
||||
_list.Insert(row, newTableRow);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a row at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index to remove a row at.</param>
|
||||
public void RemoveAt(int index)
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (index < 0)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table row index cannot be negative.");
|
||||
}
|
||||
else if (index >= _list.Count)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table row index cannot exceed the number of rows in the table.");
|
||||
}
|
||||
|
||||
_list.RemoveAt(index);
|
||||
return _list[index];
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all rows.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_list.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerator<TableRow> GetEnumerator()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var items = new TableRow[_list.Count];
|
||||
_list.CopyTo(items, 0);
|
||||
return new TableRowEnumerator(items);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
private TableRow CreateRow(IEnumerable<IRenderable> columns)
|
||||
{
|
||||
var row = new TableRow(columns);
|
||||
|
||||
if (row.Count > _table.Columns.Count)
|
||||
{
|
||||
throw new InvalidOperationException("The number of row columns are greater than the number of table columns.");
|
||||
}
|
||||
|
||||
// Need to add missing columns
|
||||
if (row.Count < _table.Columns.Count)
|
||||
{
|
||||
var diff = _table.Columns.Count - row.Count;
|
||||
Enumerable.Range(0, diff).ForEach(_ => row.Add(Text.Empty));
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of rows in the collection.
|
||||
/// </summary>
|
||||
public int Count
|
||||
{
|
||||
get
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
return _list.Count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal TableRowCollection(Table table)
|
||||
{
|
||||
_table = table ?? throw new ArgumentNullException(nameof(table));
|
||||
_list = new List<TableRow>();
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new row.
|
||||
/// </summary>
|
||||
/// <param name="columns">The columns that are part of the row to add.</param>
|
||||
/// <returns>The index of the added item.</returns>
|
||||
public int Add(IEnumerable<IRenderable> columns)
|
||||
{
|
||||
if (columns is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(columns));
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
var row = CreateRow(columns);
|
||||
_list.Add(row);
|
||||
return _list.IndexOf(row);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Inserts a new row at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index to insert the row at.</param>
|
||||
/// <param name="columns">The columns that are part of the row to insert.</param>
|
||||
/// <returns>The index of the inserted item.</returns>
|
||||
public int Insert(int index, IEnumerable<IRenderable> columns)
|
||||
{
|
||||
if (columns is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(columns));
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
var row = CreateRow(columns);
|
||||
_list.Insert(index, row);
|
||||
return _list.IndexOf(row);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update a table cell at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="row">Index of cell row.</param>
|
||||
/// <param name="column">index of cell column.</param>
|
||||
/// <param name="cellData">The new cells details.</param>
|
||||
public void Update(int row, int column, IRenderable cellData)
|
||||
{
|
||||
if (cellData is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(cellData));
|
||||
}
|
||||
|
||||
lock (_lock)
|
||||
{
|
||||
if (row < 0)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table row index cannot be negative.");
|
||||
}
|
||||
else if (row >= _list.Count)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table row index cannot exceed the number of rows in the table.");
|
||||
}
|
||||
|
||||
var tableRow = _list.ElementAtOrDefault(row);
|
||||
|
||||
var currentRenderables = tableRow.ToList();
|
||||
|
||||
if (column < 0)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table column index cannot be negative.");
|
||||
}
|
||||
else if (column >= currentRenderables.Count)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table column index cannot exceed the number of rows in the table.");
|
||||
}
|
||||
|
||||
currentRenderables.RemoveAt(column);
|
||||
|
||||
currentRenderables.Insert(column, cellData);
|
||||
|
||||
var newTableRow = new TableRow(currentRenderables);
|
||||
|
||||
_list.RemoveAt(row);
|
||||
|
||||
_list.Insert(row, newTableRow);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a row at the specified index.
|
||||
/// </summary>
|
||||
/// <param name="index">The index to remove a row at.</param>
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (index < 0)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table row index cannot be negative.");
|
||||
}
|
||||
else if (index >= _list.Count)
|
||||
{
|
||||
throw new IndexOutOfRangeException("Table row index cannot exceed the number of rows in the table.");
|
||||
}
|
||||
|
||||
_list.RemoveAt(index);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears all rows.
|
||||
/// </summary>
|
||||
public void Clear()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_list.Clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public IEnumerator<TableRow> GetEnumerator()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var items = new TableRow[_list.Count];
|
||||
_list.CopyTo(items, 0);
|
||||
return new TableRowEnumerator(items);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
return GetEnumerator();
|
||||
}
|
||||
|
||||
private TableRow CreateRow(IEnumerable<IRenderable> columns)
|
||||
{
|
||||
var row = new TableRow(columns);
|
||||
|
||||
if (row.Count > _table.Columns.Count)
|
||||
{
|
||||
throw new InvalidOperationException("The number of row columns are greater than the number of table columns.");
|
||||
}
|
||||
|
||||
// Need to add missing columns
|
||||
if (row.Count < _table.Columns.Count)
|
||||
{
|
||||
var diff = _table.Columns.Count - row.Count;
|
||||
Enumerable.Range(0, diff).ForEach(_ => row.Add(Text.Empty));
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
}
|
@@ -2,35 +2,34 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Spectre.Console
|
||||
namespace Spectre.Console;
|
||||
|
||||
internal sealed class TableRowEnumerator : IEnumerator<TableRow>
|
||||
{
|
||||
internal sealed class TableRowEnumerator : IEnumerator<TableRow>
|
||||
private readonly TableRow[] _items;
|
||||
private int _index;
|
||||
|
||||
public TableRow Current => _items[_index];
|
||||
object? IEnumerator.Current => _items[_index];
|
||||
|
||||
public TableRowEnumerator(TableRow[] items)
|
||||
{
|
||||
private readonly TableRow[] _items;
|
||||
private int _index;
|
||||
|
||||
public TableRow Current => _items[_index];
|
||||
object? IEnumerator.Current => _items[_index];
|
||||
|
||||
public TableRowEnumerator(TableRow[] items)
|
||||
{
|
||||
_items = items ?? throw new ArgumentNullException(nameof(items));
|
||||
_index = -1;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
_index++;
|
||||
return _index < _items.Length;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_index = -1;
|
||||
}
|
||||
_items = items ?? throw new ArgumentNullException(nameof(items));
|
||||
_index = -1;
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public bool MoveNext()
|
||||
{
|
||||
_index++;
|
||||
return _index < _items.Length;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
_index = -1;
|
||||
}
|
||||
}
|
@@ -1,58 +1,57 @@
|
||||
using System;
|
||||
|
||||
namespace Spectre.Console
|
||||
namespace Spectre.Console;
|
||||
|
||||
/// <summary>
|
||||
/// Represents a table title such as a heading or footnote.
|
||||
/// </summary>
|
||||
public sealed class TableTitle
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a table title such as a heading or footnote.
|
||||
/// Gets the title text.
|
||||
/// </summary>
|
||||
public sealed class TableTitle
|
||||
public string Text { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title style.
|
||||
/// </summary>
|
||||
public Style? Style { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TableTitle"/> class.
|
||||
/// </summary>
|
||||
/// <param name="text">The title text.</param>
|
||||
/// <param name="style">The title style.</param>
|
||||
public TableTitle(string text, Style? style = null)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the title text.
|
||||
/// </summary>
|
||||
public string Text { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title style.
|
||||
/// </summary>
|
||||
public Style? Style { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="TableTitle"/> class.
|
||||
/// </summary>
|
||||
/// <param name="text">The title text.</param>
|
||||
/// <param name="style">The title style.</param>
|
||||
public TableTitle(string text, Style? style = null)
|
||||
{
|
||||
Text = text ?? throw new ArgumentNullException(nameof(text));
|
||||
Style = style;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the title style.
|
||||
/// </summary>
|
||||
/// <param name="style">The title style.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public TableTitle SetStyle(Style? style)
|
||||
{
|
||||
Style = style ?? Style.Plain;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the title style.
|
||||
/// </summary>
|
||||
/// <param name="style">The title style.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public TableTitle SetStyle(string style)
|
||||
{
|
||||
if (style is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(style));
|
||||
}
|
||||
|
||||
Style = Style.Parse(style);
|
||||
return this;
|
||||
}
|
||||
Text = text ?? throw new ArgumentNullException(nameof(text));
|
||||
Style = style;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the title style.
|
||||
/// </summary>
|
||||
/// <param name="style">The title style.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public TableTitle SetStyle(Style? style)
|
||||
{
|
||||
Style = style ?? Style.Plain;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the title style.
|
||||
/// </summary>
|
||||
/// <param name="style">The title style.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public TableTitle SetStyle(string style)
|
||||
{
|
||||
if (style is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(style));
|
||||
}
|
||||
|
||||
Style = Style.Parse(style);
|
||||
return this;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user