diff --git a/src/Spectre.Console.Tests/Unit/Composition/TableTests.cs b/src/Spectre.Console.Tests/Unit/Composition/TableTests.cs index 3370540..d293fc4 100644 --- a/src/Spectre.Console.Tests/Unit/Composition/TableTests.cs +++ b/src/Spectre.Console.Tests/Unit/Composition/TableTests.cs @@ -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() { diff --git a/src/Spectre.Console/Composition/Border.cs b/src/Spectre.Console/Composition/Border.cs index ec42b6a..2b4659f 100644 --- a/src/Spectre.Console/Composition/Border.cs +++ b/src/Spectre.Console/Composition/Border.cs @@ -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 /// public abstract class Border { - private readonly Dictionary _lookup; + private readonly Dictionary _lookup; private static readonly Dictionary _borders = new Dictionary { + { BorderKind.None, new NoBorder() }, { BorderKind.Ascii, new AsciiBorder() }, { BorderKind.Square, new SquareBorder() }, }; @@ -40,11 +42,17 @@ namespace Spectre.Console.Composition return border; } - private Dictionary Initialize() + private Dictionary Initialize() { - var lookup = new Dictionary(); + var lookup = new Dictionary(); 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 /// A string representation of the specified border part. 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)); } /// @@ -77,6 +86,6 @@ namespace Spectre.Console.Composition /// /// The part to get the character representation for. /// A character representation of the specified border part. - protected abstract char GetBoxPart(BorderPart part); + protected abstract string GetBoxPart(BorderPart part); } } diff --git a/src/Spectre.Console/Composition/BorderKind.cs b/src/Spectre.Console/Composition/BorderKind.cs index 4d3bac9..010d7c5 100644 --- a/src/Spectre.Console/Composition/BorderKind.cs +++ b/src/Spectre.Console/Composition/BorderKind.cs @@ -5,14 +5,19 @@ namespace Spectre.Console /// public enum BorderKind { + /// + /// No border. + /// + None = 0, + /// /// A square border. /// - Square = 0, + Square = 1, /// /// An old school ASCII border. /// - Ascii = 1, + Ascii = 2, } } diff --git a/src/Spectre.Console/Composition/Borders/AsciiBorder.cs b/src/Spectre.Console/Composition/Borders/AsciiBorder.cs index 75bf931..06bc5cb 100644 --- a/src/Spectre.Console/Composition/Borders/AsciiBorder.cs +++ b/src/Spectre.Console/Composition/Borders/AsciiBorder.cs @@ -8,28 +8,28 @@ namespace Spectre.Console.Composition public sealed class AsciiBorder : Border { /// - 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."), }; } diff --git a/src/Spectre.Console/Composition/Borders/NoBorder.cs b/src/Spectre.Console/Composition/Borders/NoBorder.cs new file mode 100644 index 0000000..121336c --- /dev/null +++ b/src/Spectre.Console/Composition/Borders/NoBorder.cs @@ -0,0 +1,10 @@ +namespace Spectre.Console.Composition +{ + internal sealed class NoBorder : Border + { + protected override string GetBoxPart(BorderPart part) + { + return " "; + } + } +} diff --git a/src/Spectre.Console/Composition/Borders/SquareBorder.cs b/src/Spectre.Console/Composition/Borders/SquareBorder.cs index e449229..8213d23 100644 --- a/src/Spectre.Console/Composition/Borders/SquareBorder.cs +++ b/src/Spectre.Console/Composition/Borders/SquareBorder.cs @@ -8,28 +8,28 @@ namespace Spectre.Console.Composition public sealed class SquareBorder : Border { /// - 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."), }; } diff --git a/src/Spectre.Console/Composition/Table.cs b/src/Spectre.Console/Composition/Table.cs index acd9592..6e8648b 100644 --- a/src/Spectre.Console/Composition/Table.cs +++ b/src/Spectre.Console/Composition/Table.cs @@ -15,6 +15,7 @@ namespace Spectre.Console private readonly List _columns; private readonly List> _rows; private readonly Border _border; + private readonly BorderKind _borderKind; /// /// Initializes a new instance of the class. @@ -25,6 +26,7 @@ namespace Spectre.Console _columns = new List(); _rows = new List>(); _border = Border.GetBorder(border); + _borderKind = border; } /// @@ -104,8 +106,15 @@ namespace Spectre.Console /// public IEnumerable 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>(); @@ -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>(); - 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())