Add support for showing no border

This commit is contained in:
Patrik Svensson 2020-08-04 23:20:52 +02:00 committed by Patrik Svensson
parent a068fc68c3
commit f9bd936254
7 changed files with 128 additions and 62 deletions

View File

@ -111,7 +111,7 @@ namespace Spectre.Console.Tests.Unit.Composition
}
[Fact]
public void Should_Render_Table_With_Specified_Border()
public void Should_Render_Table_With_Specified_Border_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
@ -132,6 +132,26 @@ namespace Spectre.Console.Tests.Unit.Composition
console.Lines[5].ShouldBe("+-------------------------+");
}
[Fact]
public void Should_Render_Table_With_No_Border_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table(BorderKind.None);
table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe("Foo Bar Baz ");
console.Lines[1].ShouldBe("Qux Corgi Waldo");
console.Lines[2].ShouldBe("Grault Garply Fred ");
}
[Fact]
public void Should_Render_Table_With_Multiple_Rows_In_Cell_Correctly()
{

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace Spectre.Console.Composition
{
@ -9,10 +10,11 @@ namespace Spectre.Console.Composition
/// </summary>
public abstract class Border
{
private readonly Dictionary<BorderPart, char> _lookup;
private readonly Dictionary<BorderPart, string> _lookup;
private static readonly Dictionary<BorderKind, Border> _borders = new Dictionary<BorderKind, Border>
{
{ BorderKind.None, new NoBorder() },
{ BorderKind.Ascii, new AsciiBorder() },
{ BorderKind.Square, new SquareBorder() },
};
@ -40,11 +42,17 @@ namespace Spectre.Console.Composition
return border;
}
private Dictionary<BorderPart, char> Initialize()
private Dictionary<BorderPart, string> Initialize()
{
var lookup = new Dictionary<BorderPart, char>();
var lookup = new Dictionary<BorderPart, string>();
foreach (BorderPart part in Enum.GetValues(typeof(BorderPart)))
{
var text = GetBoxPart(part);
if (text.Length > 1)
{
throw new InvalidOperationException("A box part cannot contain more than one character.");
}
lookup.Add(part, GetBoxPart(part));
}
@ -59,7 +67,8 @@ namespace Spectre.Console.Composition
/// <returns>A string representation of the specified border part.</returns>
public string GetPart(BorderPart part, int count)
{
return new string(GetBoxPart(part), count);
// TODO: This need some optimization...
return string.Join(string.Empty, Enumerable.Repeat(GetBoxPart(part)[0], count));
}
/// <summary>
@ -77,6 +86,6 @@ namespace Spectre.Console.Composition
/// </summary>
/// <param name="part">The part to get the character representation for.</param>
/// <returns>A character representation of the specified border part.</returns>
protected abstract char GetBoxPart(BorderPart part);
protected abstract string GetBoxPart(BorderPart part);
}
}

View File

@ -5,14 +5,19 @@ namespace Spectre.Console
/// </summary>
public enum BorderKind
{
/// <summary>
/// No border.
/// </summary>
None = 0,
/// <summary>
/// A square border.
/// </summary>
Square = 0,
Square = 1,
/// <summary>
/// An old school ASCII border.
/// </summary>
Ascii = 1,
Ascii = 2,
}
}

View File

@ -8,28 +8,28 @@ namespace Spectre.Console.Composition
public sealed class AsciiBorder : Border
{
/// <inheritdoc/>
protected override char GetBoxPart(BorderPart part)
protected override string GetBoxPart(BorderPart part)
{
return part switch
{
BorderPart.HeaderTopLeft => '+',
BorderPart.HeaderTop => '-',
BorderPart.HeaderTopSeparator => '-',
BorderPart.HeaderTopRight => '+',
BorderPart.HeaderLeft => '|',
BorderPart.HeaderSeparator => '|',
BorderPart.HeaderRight => '|',
BorderPart.HeaderBottomLeft => '|',
BorderPart.HeaderBottom => '-',
BorderPart.HeaderBottomSeparator => '+',
BorderPart.HeaderBottomRight => '|',
BorderPart.CellLeft => '|',
BorderPart.CellSeparator => '|',
BorderPart.ColumnRight => '|',
BorderPart.FooterBottomLeft => '+',
BorderPart.FooterBottom => '-',
BorderPart.FooterBottomSeparator => '-',
BorderPart.FooterBottomRight => '+',
BorderPart.HeaderTopLeft => "+",
BorderPart.HeaderTop => "-",
BorderPart.HeaderTopSeparator => "-",
BorderPart.HeaderTopRight => "+",
BorderPart.HeaderLeft => "|",
BorderPart.HeaderSeparator => "|",
BorderPart.HeaderRight => "|",
BorderPart.HeaderBottomLeft => "|",
BorderPart.HeaderBottom => "-",
BorderPart.HeaderBottomSeparator => "+",
BorderPart.HeaderBottomRight => "|",
BorderPart.CellLeft => "|",
BorderPart.CellSeparator => "|",
BorderPart.ColumnRight => "|",
BorderPart.FooterBottomLeft => "+",
BorderPart.FooterBottom => "-",
BorderPart.FooterBottomSeparator => "-",
BorderPart.FooterBottomRight => "+",
_ => throw new InvalidOperationException("Unknown box part."),
};
}

View File

@ -0,0 +1,10 @@
namespace Spectre.Console.Composition
{
internal sealed class NoBorder : Border
{
protected override string GetBoxPart(BorderPart part)
{
return " ";
}
}
}

View File

@ -8,28 +8,28 @@ namespace Spectre.Console.Composition
public sealed class SquareBorder : Border
{
/// <inheritdoc/>
protected override char GetBoxPart(BorderPart part)
protected override string GetBoxPart(BorderPart part)
{
return part switch
{
BorderPart.HeaderTopLeft => '┌',
BorderPart.HeaderTop => '─',
BorderPart.HeaderTopSeparator => '┬',
BorderPart.HeaderTopRight => '┐',
BorderPart.HeaderLeft => '│',
BorderPart.HeaderSeparator => '│',
BorderPart.HeaderRight => '│',
BorderPart.HeaderBottomLeft => '├',
BorderPart.HeaderBottom => '─',
BorderPart.HeaderBottomSeparator => '┼',
BorderPart.HeaderBottomRight => '┤',
BorderPart.CellLeft => '│',
BorderPart.CellSeparator => '│',
BorderPart.ColumnRight => '│',
BorderPart.FooterBottomLeft => '└',
BorderPart.FooterBottom => '─',
BorderPart.FooterBottomSeparator => '┴',
BorderPart.FooterBottomRight => '┘',
BorderPart.HeaderTopLeft => "┌",
BorderPart.HeaderTop => "─",
BorderPart.HeaderTopSeparator => "┬",
BorderPart.HeaderTopRight => "┐",
BorderPart.HeaderLeft => "│",
BorderPart.HeaderSeparator => "│",
BorderPart.HeaderRight => "│",
BorderPart.HeaderBottomLeft => "├",
BorderPart.HeaderBottom => "─",
BorderPart.HeaderBottomSeparator => "┼",
BorderPart.HeaderBottomRight => "┤",
BorderPart.CellLeft => "│",
BorderPart.CellSeparator => "│",
BorderPart.ColumnRight => "│",
BorderPart.FooterBottomLeft => "└",
BorderPart.FooterBottom => "─",
BorderPart.FooterBottomSeparator => "┴",
BorderPart.FooterBottomRight => "┘",
_ => throw new InvalidOperationException("Unknown box part."),
};
}

View File

@ -15,6 +15,7 @@ namespace Spectre.Console
private readonly List<Text> _columns;
private readonly List<List<Text>> _rows;
private readonly Border _border;
private readonly BorderKind _borderKind;
/// <summary>
/// Initializes a new instance of the <see cref="Table"/> class.
@ -25,6 +26,7 @@ namespace Spectre.Console
_columns = new List<Text>();
_rows = new List<List<Text>>();
_border = Border.GetBorder(border);
_borderKind = border;
}
/// <summary>
@ -104,8 +106,15 @@ namespace Spectre.Console
/// <inheritdoc/>
public IEnumerable<Segment> Render(Encoding encoding, int width)
{
var showBorder = _borderKind != BorderKind.None;
var hideBorder = _borderKind == BorderKind.None;
var leftRightBorderWidth = _borderKind == BorderKind.None ? 0 : 2;
var columnPadding = _borderKind == BorderKind.None ? _columns.Count : _columns.Count * 2;
var separatorCount = _borderKind == BorderKind.None ? 0 : _columns.Count - 1;
// Calculate the max width for each column.
var maxColumnWidth = (width - (2 + (_columns.Count * 2) + (_columns.Count - 1))) / _columns.Count;
var maxColumnWidth = (width - (leftRightBorderWidth + columnPadding + separatorCount)) / _columns.Count;
var columnWidths = _columns.Select(c => c.Measure(encoding, maxColumnWidth)).ToArray();
for (var rowIndex = 0; rowIndex < _rows.Count; rowIndex++)
{
@ -120,7 +129,7 @@ namespace Spectre.Console
}
// We now know the max width of each column, so let's recalculate the width.
width = columnWidths.Sum() + 2 + (_columns.Count * 2) + (_columns.Count - 1);
width = columnWidths.Sum() + leftRightBorderWidth + columnPadding + separatorCount;
// Create the rows.
var rows = new List<List<Text>>();
@ -133,9 +142,8 @@ namespace Spectre.Console
{
var cellHeight = 1;
// Get the list of cells for the row.
// Get the list of cells for the row and calculate the cell height.
var cells = new List<List<SegmentLine>>();
foreach (var (rowWidth, cell) in columnWidths.Zip(row, (f, s) => (f, s)))
{
var lines = Segment.SplitLines(cell.Render(encoding, rowWidth));
@ -143,7 +151,8 @@ namespace Spectre.Console
cells.Add(lines);
}
if (firstRow)
// Show top of header?
if (firstRow && showBorder)
{
result.Add(new Segment(_border.GetPart(BorderPart.HeaderTopLeft)));
foreach (var (columnIndex, _, lastColumn, columnWidth) in columnWidths.Enumerate())
@ -171,31 +180,42 @@ namespace Spectre.Console
var w00t = cells.Enumerate().ToArray();
foreach (var (cellIndex, firstCell, lastCell, cell) in w00t)
{
if (firstCell)
if (firstCell && showBorder)
{
// Show left column edge
result.Add(new Segment(_border.GetPart(BorderPart.CellLeft)));
}
result.Add(new Segment(" "));
// Pad column on left side.
if (showBorder)
{
result.Add(new Segment(" "));
}
// Add content
result.AddRange(cell[cellRowIndex]);
// Pad cell right
// Pad cell content right
var length = cell[cellRowIndex].Sum(segment => segment.CellLength(encoding));
if (length < columnWidths[cellIndex])
{
result.Add(new Segment(new string(' ', columnWidths[cellIndex] - length)));
}
result.Add(new Segment(" "));
if (lastCell)
// Pad column on the right side
if (showBorder || (hideBorder && !lastCell))
{
// Separator
result.Add(new Segment(" "));
}
if (lastCell && showBorder)
{
// Add right column edge
result.Add(new Segment(_border.GetPart(BorderPart.ColumnRight)));
}
else
else if (showBorder || (hideBorder && !lastCell))
{
// Separator
// Add column separator
result.Add(new Segment(_border.GetPart(BorderPart.CellSeparator)));
}
}
@ -203,7 +223,8 @@ namespace Spectre.Console
result.Add(Segment.LineBreak());
}
if (firstRow)
// Show bottom of header?
if (firstRow && showBorder)
{
result.Add(new Segment(_border.GetPart(BorderPart.HeaderBottomLeft)));
foreach (var (columnIndex, first, lastColumn, columnWidth) in columnWidths.Enumerate())
@ -222,7 +243,8 @@ namespace Spectre.Console
result.Add(Segment.LineBreak());
}
if (lastRow)
// Show bottom of footer?
if (lastRow && showBorder)
{
result.Add(new Segment(_border.GetPart(BorderPart.FooterBottomLeft)));
foreach (var (columnIndex, first, lastColumn, columnWidth) in columnWidths.Enumerate())