Add support for table footers

This commit is contained in:
Patrik Svensson
2020-10-24 17:15:19 +02:00
committed by Patrik Svensson
parent c9c0ad733f
commit 03334f693d
31 changed files with 504 additions and 132 deletions

View File

@ -12,7 +12,7 @@ namespace Spectre.Console
/// <param name="obj">The alignable object.</param>
/// <param name="alignment">The alignment.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T Alignment<T>(this T obj, Justify alignment)
public static T Alignment<T>(this T obj, Justify? alignment)
where T : class, IAlignable
{
if (obj is null)

View File

@ -1,7 +1,5 @@
using System;
using System.Linq;
using Spectre.Console.Internal;
using Spectre.Console.Rendering;
using System.ComponentModel;
namespace Spectre.Console
{
@ -17,6 +15,7 @@ namespace Spectre.Console
/// <param name="width">The width.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Width(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table SetWidth(this Table table, int width)
{
if (table is null)
@ -36,6 +35,7 @@ namespace Spectre.Console
/// <param name="style">The style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Heading(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table SetHeading(this Table table, string text, Style? style = null)
{
if (table is null)
@ -58,6 +58,7 @@ namespace Spectre.Console
/// <param name="heading">The heading.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Heading(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table SetHeading(this Table table, TableTitle heading)
{
if (table is null)
@ -65,7 +66,7 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(table));
}
table.Heading = heading;
table.Title = heading;
return table;
}
@ -76,7 +77,8 @@ namespace Spectre.Console
/// <param name="text">The footnote.</param>
/// <param name="style">The style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Footnote(..) instead.")]
[Obsolete("Use Caption(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table SetFootnote(this Table table, string text, Style? style = null)
{
if (table is null)
@ -98,7 +100,8 @@ namespace Spectre.Console
/// <param name="table">The table.</param>
/// <param name="footnote">The footnote.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Footnote(..) instead.")]
[Obsolete("Use Caption(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table SetFootnote(this Table table, TableTitle footnote)
{
if (table is null)
@ -106,7 +109,93 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(table));
}
table.Footnote = footnote;
table.Caption = footnote;
return table;
}
/// <summary>
/// Sets the table footnote.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="text">The footnote.</param>
/// <param name="style">The style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Caption(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table Footnote(this Table table, string text, Style? style = null)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
return Footnote(table, new TableTitle(text, style));
}
/// <summary>
/// Sets the table footnote.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="footnote">The footnote.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Caption(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table Footnote(this Table table, TableTitle footnote)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
table.Caption = footnote;
return table;
}
/// <summary>
/// Sets the table heading.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="text">The heading.</param>
/// <param name="style">The style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Title(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table Heading(this Table table, string text, Style? style = null)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
return Heading(table, new TableTitle(text, style));
}
/// <summary>
/// Sets the table heading.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="heading">The heading.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
[Obsolete("Use Title(..) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public static Table Heading(this Table table, TableTitle heading)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
table.Title = heading;
return table;
}
}

View File

@ -0,0 +1,55 @@
using System;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="TableColumn"/>.
/// </summary>
public static class TableColumnExtensions
{
/// <summary>
/// Sets the table column footer.
/// </summary>
/// <param name="column">The table column.</param>
/// <param name="footer">The table column markup text.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static TableColumn Footer(this TableColumn column, string footer)
{
if (column is null)
{
throw new ArgumentNullException(nameof(column));
}
if (footer is null)
{
throw new ArgumentNullException(nameof(footer));
}
column.Footer = new Markup(footer);
return column;
}
/// <summary>
/// Sets the table column footer.
/// </summary>
/// <param name="column">The table column.</param>
/// <param name="footer">The table column footer.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static TableColumn Footer(this TableColumn column, IRenderable footer)
{
if (column is null)
{
throw new ArgumentNullException(nameof(column));
}
if (footer is null)
{
throw new ArgumentNullException(nameof(footer));
}
column.Footer = footer;
return column;
}
}
}

View File

@ -178,52 +178,45 @@ namespace Spectre.Console
}
/// <summary>
/// Sets the table heading.
/// Shows table footers.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="text">The heading.</param>
/// <param name="style">The style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table Heading(this Table table, string text, Style? style = null)
public static Table ShowFooters(this Table table)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
return Heading(table, new TableTitle(text, style));
}
/// <summary>
/// Sets the table heading.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="heading">The heading.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table Heading(this Table table, TableTitle heading)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
table.Heading = heading;
table.ShowFooters = true;
return table;
}
/// <summary>
/// Sets the table footnote.
/// Hides table footers.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="text">The footnote.</param>
/// <param name="style">The style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table Footnote(this Table table, string text, Style? style = null)
public static Table HideFooters(this Table table)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
table.ShowFooters = false;
return table;
}
/// <summary>
/// Sets the table title.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="text">The table title markup text.</param>
/// <param name="style">The table title style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table Title(this Table table, string text, Style? style = null)
{
if (table is null)
{
@ -235,23 +228,62 @@ namespace Spectre.Console
throw new ArgumentNullException(nameof(text));
}
return Footnote(table, new TableTitle(text, style));
return Title(table, new TableTitle(text, style));
}
/// <summary>
/// Sets the table footnote.
/// Sets the table title.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="footnote">The footnote.</param>
/// <param name="title">The table title.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table Footnote(this Table table, TableTitle footnote)
public static Table Title(this Table table, TableTitle title)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
table.Footnote = footnote;
table.Title = title;
return table;
}
/// <summary>
/// Sets the table caption.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="text">The caption markup text.</param>
/// <param name="style">The style.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table Caption(this Table table, string text, Style? style = null)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
return Caption(table, new TableTitle(text, style));
}
/// <summary>
/// Sets the table caption.
/// </summary>
/// <param name="table">The table.</param>
/// <param name="caption">The caption.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Table Caption(this Table table, TableTitle caption)
{
if (table is null)
{
throw new ArgumentNullException(nameof(table));
}
table.Caption = caption;
return table;
}
}

View File

@ -60,6 +60,26 @@ namespace Spectre.Console.Rendering
/// </summary>
HeaderBottomRight,
/// <summary>
/// The top left part of a footer.
/// </summary>
FooterTopLeft,
/// <summary>
/// The top part of a footer.
/// </summary>
FooterTop,
/// <summary>
/// The top separator part of a footer.
/// </summary>
FooterTopSeparator,
/// <summary>
/// The top right part of a footer.
/// </summary>
FooterTopRight,
/// <summary>
/// The left part of a cell.
/// </summary>

View File

@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "|",
TableBorderPart.CellSeparator => "|",
TableBorderPart.CellRight => "|",
TableBorderPart.FooterTopLeft => "|",
TableBorderPart.FooterTop => "-",
TableBorderPart.FooterTopSeparator => "+",
TableBorderPart.FooterTopRight => "|",
TableBorderPart.FooterBottomLeft => "+",
TableBorderPart.FooterBottom => "-",
TableBorderPart.FooterBottomSeparator => "+",

View File

@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "|",
TableBorderPart.CellSeparator => "|",
TableBorderPart.CellRight => "|",
TableBorderPart.FooterTopLeft => "+",
TableBorderPart.FooterTop => "-",
TableBorderPart.FooterTopSeparator => "+",
TableBorderPart.FooterTopRight => "+",
TableBorderPart.FooterBottomLeft => "+",
TableBorderPart.FooterBottom => "-",
TableBorderPart.FooterBottomSeparator => "+",

View File

@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "|",
TableBorderPart.CellSeparator => "|",
TableBorderPart.CellRight => "|",
TableBorderPart.FooterTopLeft => "|",
TableBorderPart.FooterTop => "-",
TableBorderPart.FooterTopSeparator => "+",
TableBorderPart.FooterTopRight => "|",
TableBorderPart.FooterBottomLeft => "+",
TableBorderPart.FooterBottom => "-",
TableBorderPart.FooterBottomSeparator => "-",

View File

@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "║",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "║",
TableBorderPart.FooterTopLeft => "╟",
TableBorderPart.FooterTop => "─",
TableBorderPart.FooterTopSeparator => "┼",
TableBorderPart.FooterTopRight => "╢",
TableBorderPart.FooterBottomLeft => "╚",
TableBorderPart.FooterBottom => "═",
TableBorderPart.FooterBottomSeparator => "╧",

View File

@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "║",
TableBorderPart.CellSeparator => "║",
TableBorderPart.CellRight => "║",
TableBorderPart.FooterTopLeft => "╠",
TableBorderPart.FooterTop => "═",
TableBorderPart.FooterTopSeparator => "╬",
TableBorderPart.FooterTopRight => "╣",
TableBorderPart.FooterBottomLeft => "╚",
TableBorderPart.FooterBottom => "═",
TableBorderPart.FooterBottomSeparator => "╩",

View File

@ -29,6 +29,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "┃",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "┃",
TableBorderPart.FooterTopLeft => "┠",
TableBorderPart.FooterTop => "─",
TableBorderPart.FooterTopSeparator => "┼",
TableBorderPart.FooterTopRight => "┨",
TableBorderPart.FooterBottomLeft => "┗",
TableBorderPart.FooterBottom => "━",
TableBorderPart.FooterBottomSeparator => "┷",

View File

@ -29,6 +29,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "│",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "│",
TableBorderPart.FooterTopLeft => "├",
TableBorderPart.FooterTop => "─",
TableBorderPart.FooterTopSeparator => "┼",
TableBorderPart.FooterTopRight => "┤",
TableBorderPart.FooterBottomLeft => "└",
TableBorderPart.FooterBottom => "─",
TableBorderPart.FooterBottomSeparator => "┴",

View File

@ -29,6 +29,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "┃",
TableBorderPart.CellSeparator => "┃",
TableBorderPart.CellRight => "┃",
TableBorderPart.FooterTopLeft => "┣",
TableBorderPart.FooterTop => "━",
TableBorderPart.FooterTopSeparator => "╋",
TableBorderPart.FooterTopRight => "┫",
TableBorderPart.FooterBottomLeft => "┗",
TableBorderPart.FooterBottom => "━",
TableBorderPart.FooterBottomSeparator => "┻",

View File

@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => " ",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterTopLeft => "─",
TableBorderPart.FooterTop => "─",
TableBorderPart.FooterTopSeparator => "─",
TableBorderPart.FooterTopRight => "─",
TableBorderPart.FooterBottomLeft => "─",
TableBorderPart.FooterBottom => "─",
TableBorderPart.FooterBottomSeparator => "─",

View File

@ -29,6 +29,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "|",
TableBorderPart.CellSeparator => "|",
TableBorderPart.CellRight => "|",
TableBorderPart.FooterTopLeft => " ",
TableBorderPart.FooterTop => " ",
TableBorderPart.FooterTopSeparator => " ",
TableBorderPart.FooterTopRight => " ",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",
@ -40,7 +44,12 @@ namespace Spectre.Console.Rendering
/// <inheritdoc/>
public override string GetColumnRow(TablePart part, IReadOnlyList<int> widths, IReadOnlyList<IColumn> columns)
{
if (part != TablePart.Separator)
if (part == TablePart.FooterSeparator)
{
return string.Empty;
}
if (part != TablePart.HeaderSeparator)
{
return base.GetColumnRow(part, widths, columns);
}

View File

@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterTopLeft => " ",
TableBorderPart.FooterTop => "═",
TableBorderPart.FooterTopSeparator => "╪",
TableBorderPart.FooterTopRight => " ",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",

View File

@ -29,6 +29,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterTopLeft => " ",
TableBorderPart.FooterTop => "━",
TableBorderPart.FooterTopSeparator => "┿",
TableBorderPart.FooterTopRight => " ",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",

View File

@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterTopLeft => " ",
TableBorderPart.FooterTop => "─",
TableBorderPart.FooterTopSeparator => "┼",
TableBorderPart.FooterTopRight => " ",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",

View File

@ -29,6 +29,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "│",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "│",
TableBorderPart.FooterTopLeft => "├",
TableBorderPart.FooterTop => "─",
TableBorderPart.FooterTopSeparator => "┼",
TableBorderPart.FooterTopRight => "┤",
TableBorderPart.FooterBottomLeft => "╰",
TableBorderPart.FooterBottom => "─",
TableBorderPart.FooterBottomSeparator => "┴",

View File

@ -29,6 +29,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => " ",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterTopLeft => "━",
TableBorderPart.FooterTop => "━",
TableBorderPart.FooterTopSeparator => "━",
TableBorderPart.FooterTopRight => "━",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",

View File

@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => " ",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterTopLeft => "─",
TableBorderPart.FooterTop => "─",
TableBorderPart.FooterTopSeparator => "─",
TableBorderPart.FooterTopRight => "─",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",

View File

@ -26,6 +26,10 @@ namespace Spectre.Console.Rendering
TableBorderPart.CellLeft => "│",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "│",
TableBorderPart.FooterTopLeft => "├",
TableBorderPart.FooterTop => "─",
TableBorderPart.FooterTopSeparator => "┼",
TableBorderPart.FooterTopRight => "┤",
TableBorderPart.FooterBottomLeft => "└",
TableBorderPart.FooterBottom => "─",
TableBorderPart.FooterBottomSeparator => "┴",

View File

@ -13,7 +13,12 @@ namespace Spectre.Console.Rendering
/// <summary>
/// The separator between the header and the cells.
/// </summary>
Separator,
HeaderSeparator,
/// <summary>
/// The separator between the footer and the cells.
/// </summary>
FooterSeparator,
/// <summary>
/// The bottom of a table.

View File

@ -83,10 +83,15 @@ namespace Spectre.Console
GetPart(TableBorderPart.HeaderTopSeparator), GetPart(TableBorderPart.HeaderTopRight)),
// Separator between header and cells
TablePart.Separator =>
TablePart.HeaderSeparator =>
(GetPart(TableBorderPart.HeaderBottomLeft), GetPart(TableBorderPart.HeaderBottom),
GetPart(TableBorderPart.HeaderBottomSeparator), GetPart(TableBorderPart.HeaderBottomRight)),
// Separator between footer and cells
TablePart.FooterSeparator =>
(GetPart(TableBorderPart.FooterTopLeft), GetPart(TableBorderPart.FooterTop),
GetPart(TableBorderPart.FooterTopSeparator), GetPart(TableBorderPart.FooterTopRight)),
// Bottom part
TablePart.Bottom =>
(GetPart(TableBorderPart.FooterBottomLeft), GetPart(TableBorderPart.FooterBottom),

View File

@ -209,7 +209,7 @@ namespace Spectre.Console
if (ShowHeader)
{
var heading = new DateTime(Year, Month, Day).ToString("Y", culture).EscapeMarkup();
table.Heading = new TableTitle(heading, HeaderStyle);
table.Title = new TableTitle(heading, HeaderStyle);
}
// Add columns

View File

@ -17,7 +17,7 @@ namespace Spectre.Console
private readonly List<List<IRenderable>> _rows;
private static Style _defaultHeadingStyle = new Style(Color.Silver);
private static Style _defaultFootnoteStyle = new Style(Color.Grey);
private static Style _defaultCaptionStyle = new Style(Color.Grey);
/// <summary>
/// Gets the number of columns in the table.
@ -43,6 +43,11 @@ namespace Spectre.Console
/// </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
@ -58,12 +63,12 @@ namespace Spectre.Console
/// <summary>
/// Gets or sets the table title.
/// </summary>
public TableTitle? Heading { get; set; }
public TableTitle? Title { get; set; }
/// <summary>
/// Gets or sets the table footnote.
/// </summary>
public TableTitle? Footnote { get; set; }
public TableTitle? Caption { get; set; }
/// <inheritdoc/>
public Justify? Alignment { get; set; }
@ -174,6 +179,7 @@ namespace Spectre.Console
var showBorder = Border.Visible;
var hideBorder = !Border.Visible;
var hasRows = _rows.Count > 0;
var hasFooters = _columns.Any(c => c.Footer != null);
if (Width != null)
{
@ -196,14 +202,19 @@ namespace Spectre.Console
if (ShowHeaders)
{
// Add columns to top of rows
rows.Add(new List<IRenderable>(_columns.Select(c => c.Text)));
rows.Add(new List<IRenderable>(_columns.Select(c => c.Header)));
}
// Add rows.
rows.AddRange(_rows);
if (hasFooters)
{
rows.Add(new List<IRenderable>(_columns.Select(c => c.Footer ?? Text.Empty)));
}
var result = new List<Segment>();
result.AddRange(RenderAnnotation(context, Heading, actualMaxWidth, tableWidth, _defaultHeadingStyle));
result.AddRange(RenderAnnotation(context, Title, actualMaxWidth, tableWidth, _defaultHeadingStyle));
// Iterate all rows
foreach (var (index, firstRow, lastRow, row) in rows.Enumerate())
@ -230,6 +241,18 @@ namespace Spectre.Console
result.Add(Segment.LineBreak);
}
// Show footer separator?
if (ShowFooters && lastRow && showBorder && hasFooters)
{
var textBorder = border.GetColumnRow(TablePart.FooterSeparator, columnWidths, _columns);
if (!string.IsNullOrEmpty(textBorder))
{
var separator = Aligner.Align(context, textBorder, Alignment, actualMaxWidth);
result.Add(new Segment(separator, borderStyle));
result.Add(Segment.LineBreak);
}
}
// Make cells the same shape
cells = Segment.MakeSameHeight(cellHeight, cells);
@ -310,7 +333,7 @@ namespace Spectre.Console
// Show header separator?
if (firstRow && showBorder && ShowHeaders && hasRows)
{
var separator = Aligner.Align(context, border.GetColumnRow(TablePart.Separator, columnWidths, _columns), Alignment, actualMaxWidth);
var separator = Aligner.Align(context, border.GetColumnRow(TablePart.HeaderSeparator, columnWidths, _columns), Alignment, actualMaxWidth);
result.Add(new Segment(separator, borderStyle));
result.Add(Segment.LineBreak);
}
@ -324,7 +347,7 @@ namespace Spectre.Console
}
}
result.AddRange(RenderAnnotation(context, Footnote, actualMaxWidth, tableWidth, _defaultFootnoteStyle));
result.AddRange(RenderAnnotation(context, Caption, actualMaxWidth, tableWidth, _defaultCaptionStyle));
return result;
}
@ -437,16 +460,17 @@ namespace Spectre.Console
var minWidths = new List<int>();
var maxWidths = new List<int>();
// Include columns in measurement
var measure = column.Text.Measure(options, maxWidth);
minWidths.Add(measure.Min);
maxWidths.Add(measure.Max);
// 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)
{
measure = row.Measure(options, maxWidth);
minWidths.Add(measure.Min);
maxWidths.Add(measure.Max);
var rowMeasure = row.Measure(options, maxWidth);
minWidths.Add(rowMeasure.Min);
maxWidths.Add(rowMeasure.Max);
}
return (minWidths.Count > 0 ? minWidths.Max() : padding,

View File

@ -9,9 +9,14 @@ namespace Spectre.Console
public sealed class TableColumn : IColumn
{
/// <summary>
/// Gets the text associated with the column.
/// Gets the column header.
/// </summary>
public IRenderable Text { get; }
public IRenderable Header { get; }
/// <summary>
/// Gets or sets the column footer.
/// </summary>
public IRenderable? Footer { get; set; }
/// <summary>
/// Gets or sets the width of the column.
@ -39,19 +44,19 @@ namespace Spectre.Console
/// <summary>
/// Initializes a new instance of the <see cref="TableColumn"/> class.
/// </summary>
/// <param name="text">The table column text.</param>
public TableColumn(string text)
: this(new Markup(text).Overflow(Overflow.Ellipsis))
/// <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="renderable">The <see cref="IRenderable"/> instance to use as the table column.</param>
public TableColumn(IRenderable renderable)
/// <param name="header">The <see cref="IRenderable"/> instance to use as the table column header.</param>
public TableColumn(IRenderable header)
{
Text = renderable ?? throw new ArgumentNullException(nameof(renderable));
Header = header ?? throw new ArgumentNullException(nameof(header));
Width = null;
Padding = new Padding(1, 0, 1, 0);
NoWrap = false;