Add support for markdown tables

Closes #85

* Split Border into BoxBorder and TableBorder
* Change how different table parts are composed
* Add markdown table border
This commit is contained in:
Patrik Svensson 2020-09-30 23:37:28 +02:00 committed by Patrik Svensson
parent 697273917e
commit 93ec7401c8
73 changed files with 2117 additions and 1076 deletions

View File

@ -4,16 +4,30 @@ Order: 2
There is different built-in borders you can use for tables and panels.
# Built-in borders
# Table borders
<img src="../assets/images/borders.png" style="max-width: 100%;">
<img src="../assets/images/borders/table.png" style="max-width: 100%;">
# Usage
## Example
To create a table and set it's border to `SimpleHeavy` as seen in the
image above:
To set a table border to `SimpleHeavy`:
```csharp
var table = new Table();
table.Border = Border.SimpleHeavy;
table.Border = TableBorder.SimpleHeavy;
```
---
# Panel borders
<img src="../assets/images/borders/panel.png" style="max-width: 100%;">
## Example
To set a panel border to `Rounded`:
```csharp
var panel = new Panel("Hello World");
panel.Border = BoxBorder.Rounded;
```

Binary file not shown.

Before

Width:  |  Height:  |  Size: 415 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 503 KiB

View File

@ -7,41 +7,83 @@ namespace Borders
{
public static void Main()
{
var items = new[]
{
Create("Ascii", Border.Ascii),
Create("Ascii2", Border.Ascii2),
Create("AsciiDoubleHead", Border.AsciiDoubleHead),
Create("Horizontal", Border.Horizontal),
Create("Simple", Border.Simple),
Create("SimpleHeavy", Border.SimpleHeavy),
Create("Minimal", Border.Minimal),
Create("MinimalHeavyHead", Border.MinimalHeavyHead),
Create("MinimalDoubleHead", Border.MinimalDoubleHead),
Create("Square", Border.Square),
Create("Rounded", Border.Rounded),
Create("Heavy", Border.Heavy),
Create("HeavyEdge", Border.HeavyEdge),
Create("HeavyHead", Border.HeavyHead),
Create("Double", Border.Double),
Create("DoubleEdge", Border.DoubleEdge),
};
// Render panel borders
AnsiConsole.WriteLine();
AnsiConsole.Render(new Columns(items).Collapse());
AnsiConsole.MarkupLine("[white bold underline]PANEL BORDERS[/]");
AnsiConsole.WriteLine();
RenderPanelBorders();
// Render table borders
AnsiConsole.WriteLine();
AnsiConsole.MarkupLine("[white bold underline]TABLE BORDERS[/]");
AnsiConsole.WriteLine();
RenderTableBorders();
}
private static IRenderable Create(string name, Border border)
private static void RenderPanelBorders()
{
var table = new Table().SetBorder(border);
table.AddColumns("[yellow]Header 1[/]", "[yellow]Header 2[/]");
table.AddRow("Cell", "Cell");
table.AddRow("Cell", "Cell");
static IRenderable CreatePanel(string name, BoxBorder border)
{
return new Panel($"This is a panel with\nthe [yellow]{name}[/] border.")
.SetHeader($" {name} ", Style.Parse("blue"), Justify.Center)
.SetBorderStyle(Style.Parse("grey"))
.SetBorder(border);
}
return new Panel(table)
.SetHeader($" {name} ", Style.Parse("blue"), Justify.Center)
.SetBorderStyle(Style.Parse("grey"))
.NoBorder();
var items = new[]
{
CreatePanel("Ascii", BoxBorder.Ascii),
CreatePanel("Square", BoxBorder.Square),
CreatePanel("Rounded", BoxBorder.Rounded),
CreatePanel("Heavy", BoxBorder.Heavy),
CreatePanel("Double", BoxBorder.Double),
CreatePanel("None", BoxBorder.None),
};
AnsiConsole.Render(
new Padder(
new Columns(items).PadRight(2),
new Padding(2,0,0,0)));
}
private static void RenderTableBorders()
{
static IRenderable CreateTable(string name, TableBorder border)
{
var table = new Table().SetBorder(border);
table.AddColumn("[yellow]Header 1[/]");
table.AddColumn("[yellow]Header 2[/]", col => col.RightAligned());
table.AddRow("Cell", "Cell");
table.AddRow("Cell", "Cell");
return new Panel(table)
.SetHeader($" {name} ", Style.Parse("blue"), Justify.Center)
.SetBorderStyle(Style.Parse("grey"))
.NoBorder();
}
var items = new[]
{
CreateTable("Ascii", TableBorder.Ascii),
CreateTable("Ascii2", TableBorder.Ascii2),
CreateTable("AsciiDoubleHead", TableBorder.AsciiDoubleHead),
CreateTable("Horizontal", TableBorder.Horizontal),
CreateTable("Simple", TableBorder.Simple),
CreateTable("SimpleHeavy", TableBorder.SimpleHeavy),
CreateTable("Minimal", TableBorder.Minimal),
CreateTable("MinimalHeavyHead", TableBorder.MinimalHeavyHead),
CreateTable("MinimalDoubleHead", TableBorder.MinimalDoubleHead),
CreateTable("Square", TableBorder.Square),
CreateTable("Rounded", TableBorder.Rounded),
CreateTable("Heavy", TableBorder.Heavy),
CreateTable("HeavyEdge", TableBorder.HeavyEdge),
CreateTable("HeavyHead", TableBorder.HeavyHead),
CreateTable("Double", TableBorder.Double),
CreateTable("DoubleEdge", TableBorder.DoubleEdge),
CreateTable("Markdown", TableBorder.Markdown),
};
AnsiConsole.Render(new Columns(items).Collapse());
}
}
}

View File

@ -1,4 +1,3 @@
using System;
using Spectre.Console;
namespace Emojis

View File

@ -13,7 +13,7 @@ namespace PanelExample
AnsiConsole.Render(
new Panel(
new Panel(content)
.SetBorder(Border.Rounded)));
.SetBorder(BoxBorder.Rounded)));
// Left adjusted panel with text
AnsiConsole.Render(

View File

@ -35,7 +35,7 @@ namespace TableExample
private static void RenderBigTable()
{
// Create the table.
var table = new Table().SetBorder(Border.Rounded);
var table = new Table().SetBorder(TableBorder.Rounded);
table.AddColumn("[red underline]Foo[/]");
table.AddColumn(new TableColumn("[blue]Bar[/]") { Alignment = Justify.Right, NoWrap = true });
@ -57,7 +57,7 @@ namespace TableExample
private static void RenderNestedTable()
{
// Create simple table.
var simple = new Table().SetBorder(Border.Rounded).SetBorderColor(Color.Red);
var simple = new Table().SetBorder(TableBorder.Rounded).SetBorderColor(Color.Red);
simple.AddColumn(new TableColumn("[u]Foo[/]").Centered());
simple.AddColumn(new TableColumn("[u]Bar[/]"));
simple.AddColumn(new TableColumn("[u]Baz[/]"));
@ -66,7 +66,7 @@ namespace TableExample
simple.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
// Create other table.
var second = new Table().SetBorder(Border.Square).SetBorderColor(Color.Green);
var second = new Table().SetBorder(TableBorder.Square).SetBorderColor(Color.Green);
second.AddColumn(new TableColumn("[u]Foo[/]"));
second.AddColumn(new TableColumn("[u]Bar[/]"));
second.AddColumn(new TableColumn("[u]Baz[/]"));
@ -74,7 +74,7 @@ namespace TableExample
second.AddRow(simple, new Text("Whaaat"), new Text("Lolz"));
second.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
var table = new Table().SetBorder(Border.Rounded);
var table = new Table().SetBorder(TableBorder.Rounded);
table.AddColumn(new TableColumn(new Panel("[u]Foo[/]").SetBorderColor(Color.Red)));
table.AddColumn(new TableColumn(new Panel("[u]Bar[/]").SetBorderColor(Color.Green)));
table.AddColumn(new TableColumn(new Panel("[u]Baz[/]").SetBorderColor(Color.Blue)));

View File

@ -2,6 +2,6 @@
"projects": [ "src" ],
"sdk": {
"version": "3.1.301",
"rollForward": "latestMajor"
"rollForward": "latestPatch"
}
}

View File

@ -0,0 +1,210 @@
using Shouldly;
using Spectre.Console.Rendering;
using Xunit;
namespace Spectre.Console.Tests.Unit
{
public sealed class BoxBorderTests
{
public sealed class NoBorder
{
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.None.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.None);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var panel = Fixture.GetPanel().NoBorder();
// When
console.Render(panel);
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe(" Greeting ");
console.Lines[1].ShouldBe(" Hello World ");
console.Lines[2].ShouldBe(" ");
}
}
public sealed class AsciiBorder
{
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.Ascii.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Ascii);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var panel = Fixture.GetPanel().AsciiBorder();
// When
console.Render(panel);
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe("+-Greeting----+");
console.Lines[1].ShouldBe("| Hello World |");
console.Lines[2].ShouldBe("+-------------+");
}
}
public sealed class DoubleBorder
{
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.Double.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Double);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var panel = Fixture.GetPanel().DoubleBorder();
// When
console.Render(panel);
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe("╔═Greeting════╗");
console.Lines[1].ShouldBe("║ Hello World ║");
console.Lines[2].ShouldBe("╚═════════════╝");
}
}
public sealed class HeavyBorder
{
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.Heavy.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Square);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var panel = Fixture.GetPanel().HeavyBorder();
// When
console.Render(panel);
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe("┏━Greeting━━━━┓");
console.Lines[1].ShouldBe("┃ Hello World ┃");
console.Lines[2].ShouldBe("┗━━━━━━━━━━━━━┛");
}
}
public sealed class RoundedBorder
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.Rounded.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Square);
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var panel = Fixture.GetPanel().RoundedBorder();
// When
console.Render(panel);
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe("╭─Greeting────╮");
console.Lines[1].ShouldBe("│ Hello World │");
console.Lines[2].ShouldBe("╰─────────────╯");
}
}
public sealed class SquareBorder
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = BoxBorder.Square.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(BoxBorder.Square);
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var panel = Fixture.GetPanel().SquareBorder();
// When
console.Render(panel);
// Then
console.Lines.Count.ShouldBe(3);
console.Lines[0].ShouldBe("┌─Greeting────┐");
console.Lines[1].ShouldBe("│ Hello World │");
console.Lines[2].ShouldBe("└─────────────┘");
}
}
private static class Fixture
{
public static Panel GetPanel()
{
return new Panel("Hello World")
.SetHeader("Greeting");
}
}
}
}

View File

@ -4,7 +4,7 @@ using Xunit;
namespace Spectre.Console.Tests.Unit
{
public sealed class BorderTests
public sealed class TableBorderTests
{
public sealed class NoBorder
{
@ -12,7 +12,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.None.Visible;
var visibility = TableBorder.None.Visible;
// Then
visibility.ShouldBeFalse();
@ -24,10 +24,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.None.GetSafeBorder(safe: true);
var border = TableBorder.None.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.None);
border.ShouldBeSameAs(TableBorder.None);
}
}
@ -55,7 +55,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Ascii.Visible;
var visibility = TableBorder.Ascii.Visible;
// Then
visibility.ShouldBeTrue();
@ -67,10 +67,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Ascii.GetSafeBorder(safe: true);
var border = TableBorder.Ascii.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Ascii);
border.ShouldBeSameAs(TableBorder.Ascii);
}
}
@ -101,7 +101,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Ascii2.Visible;
var visibility = TableBorder.Ascii2.Visible;
// Then
visibility.ShouldBeTrue();
@ -113,10 +113,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Ascii2.GetSafeBorder(safe: true);
var border = TableBorder.Ascii2.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Ascii2);
border.ShouldBeSameAs(TableBorder.Ascii2);
}
}
@ -147,7 +147,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.AsciiDoubleHead.Visible;
var visibility = TableBorder.AsciiDoubleHead.Visible;
// Then
visibility.ShouldBeTrue();
@ -159,10 +159,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.AsciiDoubleHead.GetSafeBorder(safe: true);
var border = TableBorder.AsciiDoubleHead.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.AsciiDoubleHead);
border.ShouldBeSameAs(TableBorder.AsciiDoubleHead);
}
}
@ -193,7 +193,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Square.Visible;
var visibility = TableBorder.Square.Visible;
// Then
visibility.ShouldBeTrue();
@ -205,10 +205,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Square.GetSafeBorder(safe: true);
var border = TableBorder.Square.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Square);
border.ShouldBeSameAs(TableBorder.Square);
}
}
@ -239,7 +239,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Rounded.Visible;
var visibility = TableBorder.Rounded.Visible;
// Then
visibility.ShouldBeTrue();
@ -251,10 +251,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Rounded.GetSafeBorder(safe: true);
var border = TableBorder.Rounded.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Square);
border.ShouldBeSameAs(TableBorder.Square);
}
}
@ -285,7 +285,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Minimal.Visible;
var visibility = TableBorder.Minimal.Visible;
// Then
visibility.ShouldBeTrue();
@ -297,10 +297,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Minimal.GetSafeBorder(safe: true);
var border = TableBorder.Minimal.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Minimal);
border.ShouldBeSameAs(TableBorder.Minimal);
}
}
@ -331,7 +331,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.MinimalHeavyHead.Visible;
var visibility = TableBorder.MinimalHeavyHead.Visible;
// Then
visibility.ShouldBeTrue();
@ -343,10 +343,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.MinimalHeavyHead.GetSafeBorder(safe: true);
var border = TableBorder.MinimalHeavyHead.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Minimal);
border.ShouldBeSameAs(TableBorder.Minimal);
}
}
@ -377,7 +377,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.MinimalDoubleHead.Visible;
var visibility = TableBorder.MinimalDoubleHead.Visible;
// Then
visibility.ShouldBeTrue();
@ -389,10 +389,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.MinimalDoubleHead.GetSafeBorder(safe: true);
var border = TableBorder.MinimalDoubleHead.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.MinimalDoubleHead);
border.ShouldBeSameAs(TableBorder.MinimalDoubleHead);
}
}
@ -423,7 +423,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Simple.Visible;
var visibility = TableBorder.Simple.Visible;
// Then
visibility.ShouldBeTrue();
@ -435,10 +435,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Simple.GetSafeBorder(safe: true);
var border = TableBorder.Simple.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Simple);
border.ShouldBeSameAs(TableBorder.Simple);
}
}
@ -469,7 +469,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Horizontal.Visible;
var visibility = TableBorder.Horizontal.Visible;
// Then
visibility.ShouldBeTrue();
@ -481,10 +481,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Horizontal.GetSafeBorder(safe: true);
var border = TableBorder.Horizontal.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Horizontal);
border.ShouldBeSameAs(TableBorder.Horizontal);
}
}
@ -515,7 +515,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.SimpleHeavy.Visible;
var visibility = TableBorder.SimpleHeavy.Visible;
// Then
visibility.ShouldBeTrue();
@ -527,10 +527,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.SimpleHeavy.GetSafeBorder(safe: true);
var border = TableBorder.SimpleHeavy.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Simple);
border.ShouldBeSameAs(TableBorder.Simple);
}
}
@ -561,7 +561,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Heavy.Visible;
var visibility = TableBorder.Heavy.Visible;
// Then
visibility.ShouldBeTrue();
@ -573,10 +573,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Heavy.GetSafeBorder(safe: true);
var border = TableBorder.Heavy.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Square);
border.ShouldBeSameAs(TableBorder.Square);
}
}
@ -607,7 +607,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.HeavyEdge.Visible;
var visibility = TableBorder.HeavyEdge.Visible;
// Then
visibility.ShouldBeTrue();
@ -619,10 +619,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.HeavyEdge.GetSafeBorder(safe: true);
var border = TableBorder.HeavyEdge.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Square);
border.ShouldBeSameAs(TableBorder.Square);
}
}
@ -653,7 +653,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.HeavyHead.Visible;
var visibility = TableBorder.HeavyHead.Visible;
// Then
visibility.ShouldBeTrue();
@ -665,10 +665,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.HeavyHead.GetSafeBorder(safe: true);
var border = TableBorder.HeavyHead.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Square);
border.ShouldBeSameAs(TableBorder.Square);
}
}
@ -699,7 +699,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.Double.Visible;
var visibility = TableBorder.Double.Visible;
// Then
visibility.ShouldBeTrue();
@ -711,10 +711,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.Double.GetSafeBorder(safe: true);
var border = TableBorder.Double.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.Double);
border.ShouldBeSameAs(TableBorder.Double);
}
}
@ -745,7 +745,7 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = Border.DoubleEdge.Visible;
var visibility = TableBorder.DoubleEdge.Visible;
// Then
visibility.ShouldBeTrue();
@ -757,10 +757,10 @@ namespace Spectre.Console.Tests.Unit
public void Should_Return_Safe_Border()
{
// Given, When
var border = Border.DoubleEdge.GetSafeBorder(safe: true);
var border = TableBorder.DoubleEdge.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(Border.DoubleEdge);
border.ShouldBeSameAs(TableBorder.DoubleEdge);
}
}
@ -785,12 +785,119 @@ namespace Spectre.Console.Tests.Unit
}
}
public sealed class MarkdownBorder
{
[Fact]
public void Should_Return_Correct_Visibility()
{
// Given, When
var visibility = TableBorder.Markdown.Visible;
// Then
visibility.ShouldBeTrue();
}
public sealed class TheSafeGetBorderMethod
{
[Fact]
public void Should_Return_Safe_Border()
{
// Given, When
var border = TableBorder.Markdown.GetSafeBorder(safe: true);
// Then
border.ShouldBeSameAs(TableBorder.Markdown);
}
}
[Fact]
public void Should_Render_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable().MarkdownBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe("| Header 1 | Header 2 |");
console.Lines[2].ShouldBe("| -------- | -------- |");
console.Lines[3].ShouldBe("| Cell | Cell |");
console.Lines[4].ShouldBe("| Cell | Cell |");
console.Lines[5].ShouldBe(" ");
}
[Fact]
public void Should_Render_Left_Aligned_Table_Columns_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable(header2: Justify.Left).MarkdownBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe("| Header 1 | Header 2 |");
console.Lines[2].ShouldBe("| -------- | :------- |");
console.Lines[3].ShouldBe("| Cell | Cell |");
console.Lines[4].ShouldBe("| Cell | Cell |");
console.Lines[5].ShouldBe(" ");
}
[Fact]
public void Should_Render_Center_Aligned_Table_Columns_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable(header2: Justify.Center).MarkdownBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe("| Header 1 | Header 2 |");
console.Lines[2].ShouldBe("| -------- | :------: |");
console.Lines[3].ShouldBe("| Cell | Cell |");
console.Lines[4].ShouldBe("| Cell | Cell |");
console.Lines[5].ShouldBe(" ");
}
[Fact]
public void Should_Render_Right_Aligned_Table_Columns_As_Expected()
{
// Given
var console = new PlainConsole();
var table = Fixture.GetTable(header2: Justify.Right).MarkdownBorder();
// When
console.Render(table);
// Then
console.Lines.Count.ShouldBe(6);
console.Lines[0].ShouldBe(" ");
console.Lines[1].ShouldBe("| Header 1 | Header 2 |");
console.Lines[2].ShouldBe("| -------- | -------: |");
console.Lines[3].ShouldBe("| Cell | Cell |");
console.Lines[4].ShouldBe("| Cell | Cell |");
console.Lines[5].ShouldBe(" ");
}
}
private static class Fixture
{
public static Table GetTable()
public static Table GetTable(Justify? header1 = null, Justify? header2 = null)
{
var table = new Table();
table.AddColumns("Header 1", "Header 2");
table.AddColumn("Header 1", c => c.Alignment = header1);
table.AddColumn("Header 2", c => c.Alignment = header2);
table.AddRow("Cell", "Cell");
table.AddRow("Cell", "Cell");
return table;

View File

@ -173,7 +173,7 @@ namespace Spectre.Console.Tests.Unit
{
// A simple table
var console = new PlainConsole(width: 80);
var table = new Table() { Border = Border.Rounded };
var table = new Table() { Border = TableBorder.Rounded };
table.AddColumn("Foo");
table.AddColumn("Bar");
table.AddColumn(new TableColumn("Baz") { Alignment = Justify.Right });
@ -183,7 +183,7 @@ namespace Spectre.Console.Tests.Unit
// Render a table in some panels.
console.Render(new Panel(new Panel(table)
{
Border = Border.Ascii,
Border = BoxBorder.Ascii,
}));
// Then
@ -255,7 +255,7 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table { Border = Border.Ascii };
var table = new Table { Border = TableBorder.Ascii };
table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");
@ -278,7 +278,7 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table { Border = Border.Rounded };
var table = new Table { Border = TableBorder.Rounded };
table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");
@ -301,7 +301,7 @@ namespace Spectre.Console.Tests.Unit
{
// Given
var console = new PlainConsole(width: 80);
var table = new Table { Border = Border.None };
var table = new Table { Border = TableBorder.None };
table.AddColumns("Foo", "Bar", "Baz");
table.AddRow("Qux", "Corgi", "Waldo");
table.AddRow("Grault", "Garply", "Fred");

View File

@ -1,37 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents another old school ASCII border.
/// </summary>
public sealed class Ascii2Border : Border
{
/// <inheritdoc/>
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.CellRight => "|",
BorderPart.FooterBottomLeft => "+",
BorderPart.FooterBottom => "-",
BorderPart.FooterBottomSeparator => "+",
BorderPart.FooterBottomRight => "+",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@ -1,37 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents an old school ASCII border.
/// </summary>
public sealed class AsciiBorder : Border
{
/// <inheritdoc/>
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.CellRight => "|",
BorderPart.FooterBottomLeft => "+",
BorderPart.FooterBottom => "-",
BorderPart.FooterBottomSeparator => "-",
BorderPart.FooterBottomRight => "+",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@ -1,37 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents an old school ASCII border with a double header border.
/// </summary>
public sealed class AsciiDoubleHeadBorder : Border
{
/// <inheritdoc/>
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.CellRight => "|",
BorderPart.FooterBottomLeft => "+",
BorderPart.FooterBottom => "-",
BorderPart.FooterBottomSeparator => "+",
BorderPart.FooterBottomRight => "+",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@ -1,37 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a double border.
/// </summary>
public sealed class DoubleBorder : Border
{
/// <inheritdoc/>
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.CellRight => "║",
BorderPart.FooterBottomLeft => "╚",
BorderPart.FooterBottom => "═",
BorderPart.FooterBottomSeparator => "╩",
BorderPart.FooterBottomRight => "╝",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@ -1,37 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a border with a double edge.
/// </summary>
public sealed class DoubleEdgeBorder : Border
{
/// <inheritdoc/>
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.CellRight => "║",
BorderPart.FooterBottomLeft => "╚",
BorderPart.FooterBottom => "═",
BorderPart.FooterBottomSeparator => "╧",
BorderPart.FooterBottomRight => "╝",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@ -1,40 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a heavy border.
/// </summary>
public sealed class HeavyBorder : Border
{
/// <inheritdoc/>
public override Border? SafeBorder => Border.Square;
/// <inheritdoc/>
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.CellRight => "┃",
BorderPart.FooterBottomLeft => "┗",
BorderPart.FooterBottom => "━",
BorderPart.FooterBottomSeparator => "┻",
BorderPart.FooterBottomRight => "┛",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@ -1,40 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a border with a heavy edge.
/// </summary>
public sealed class HeavyEdgeBorder : Border
{
/// <inheritdoc/>
public override Border? SafeBorder => Border.Square;
/// <inheritdoc/>
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.CellRight => "┃",
BorderPart.FooterBottomLeft => "┗",
BorderPart.FooterBottom => "━",
BorderPart.FooterBottomSeparator => "┷",
BorderPart.FooterBottomRight => "┛",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@ -1,40 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a border with a heavy header.
/// </summary>
public sealed class HeavyHeadBorder : Border
{
/// <inheritdoc/>
public override Border? SafeBorder => Border.Square;
/// <inheritdoc/>
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.CellRight => "│",
BorderPart.FooterBottomLeft => "└",
BorderPart.FooterBottom => "─",
BorderPart.FooterBottomSeparator => "┴",
BorderPart.FooterBottomRight => "┘",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@ -1,37 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a horizontal border.
/// </summary>
public sealed class HorizontalBorder : Border
{
/// <inheritdoc/>
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.CellRight => " ",
BorderPart.FooterBottomLeft => "─",
BorderPart.FooterBottom => "─",
BorderPart.FooterBottomSeparator => "─",
BorderPart.FooterBottomRight => "─",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@ -1,37 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a minimal border.
/// </summary>
public sealed class MinimalBorder : Border
{
/// <inheritdoc/>
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.CellRight => " ",
BorderPart.FooterBottomLeft => " ",
BorderPart.FooterBottom => " ",
BorderPart.FooterBottomSeparator => " ",
BorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@ -1,37 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a minimal border with a double header border.
/// </summary>
public sealed class MinimalDoubleHeadBorder : Border
{
/// <inheritdoc/>
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.CellRight => " ",
BorderPart.FooterBottomLeft => " ",
BorderPart.FooterBottom => " ",
BorderPart.FooterBottomSeparator => " ",
BorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@ -1,40 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a minimal border with a heavy header.
/// </summary>
public sealed class MinimalHeavyHeadBorder : Border
{
/// <inheritdoc/>
public override Border? SafeBorder => Border.Minimal;
/// <inheritdoc/>
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.CellRight => " ",
BorderPart.FooterBottomLeft => " ",
BorderPart.FooterBottom => " ",
BorderPart.FooterBottomSeparator => " ",
BorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@ -1,40 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a rounded border.
/// </summary>
public sealed class RoundedBorder : Border
{
/// <inheritdoc/>
public override Border? SafeBorder { get; } = Border.Square;
/// <inheritdoc/>
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.CellRight => "│",
BorderPart.FooterBottomLeft => "╰",
BorderPart.FooterBottom => "─",
BorderPart.FooterBottomSeparator => "┴",
BorderPart.FooterBottomRight => "╯",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@ -1,37 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a simple border.
/// </summary>
public sealed class SimpleBorder : Border
{
/// <inheritdoc/>
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.CellRight => " ",
BorderPart.FooterBottomLeft => " ",
BorderPart.FooterBottom => " ",
BorderPart.FooterBottomSeparator => " ",
BorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@ -1,40 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a simple border with heavy lines.
/// </summary>
public sealed class SimpleHeavyBorder : Border
{
/// <inheritdoc/>
public override Border? SafeBorder => Border.Simple;
/// <inheritdoc/>
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.CellRight => " ",
BorderPart.FooterBottomLeft => " ",
BorderPart.FooterBottom => " ",
BorderPart.FooterBottomSeparator => " ",
BorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@ -1,37 +0,0 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a square border.
/// </summary>
public sealed class SquareBorder : Border
{
/// <inheritdoc/>
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.CellRight => "│",
BorderPart.FooterBottomLeft => "└",
BorderPart.FooterBottom => "─",
BorderPart.FooterBottomSeparator => "┴",
BorderPart.FooterBottomRight => "┘",
_ => throw new InvalidOperationException("Unknown box part."),
};
}
}
}

View File

@ -0,0 +1,42 @@
using System.Diagnostics.CodeAnalysis;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
/// <summary>
/// Represents a border.
/// </summary>
public abstract partial class BoxBorder
{
/// <summary>
/// Gets an invisible border.
/// </summary>
public static BoxBorder None { get; } = new NoBoxBorder();
/// <summary>
/// Gets an ASCII border.
/// </summary>
public static BoxBorder Ascii { get; } = new AsciiBoxBorder();
/// <summary>
/// Gets a double border.
/// </summary>
[SuppressMessage("Naming", "CA1720:Identifier contains type name")]
public static BoxBorder Double { get; } = new DoubleBoxBorder();
/// <summary>
/// Gets a heavy border.
/// </summary>
public static BoxBorder Heavy { get; } = new HeavyBoxBorder();
/// <summary>
/// Gets a rounded border.
/// </summary>
public static BoxBorder Rounded { get; } = new RoundedBoxBorder();
/// <summary>
/// Gets a square border.
/// </summary>
public static BoxBorder Square { get; } = new SquareBoxBorder();
}
}

View File

@ -9,45 +9,40 @@ namespace Spectre.Console
/// <summary>
/// Represents a border.
/// </summary>
public abstract partial class Border
public abstract partial class BoxBorder
{
private readonly Dictionary<BorderPart, string> _lookup;
/// <summary>
/// Gets a value indicating whether or not the border is visible.
/// </summary>
public virtual bool Visible { get; } = true;
private readonly Dictionary<BoxBorderPart, string> _lookup;
/// <summary>
/// Gets the safe border for this border or <c>null</c> if none exist.
/// </summary>
public virtual Border? SafeBorder { get; }
public virtual BoxBorder? SafeBorder { get; }
/// <summary>
/// Initializes a new instance of the <see cref="Border"/> class.
/// Initializes a new instance of the <see cref="BoxBorder"/> class.
/// </summary>
protected Border()
protected BoxBorder()
{
_lookup = Initialize();
}
private Dictionary<BorderPart, string> Initialize()
private Dictionary<BoxBorderPart, string> Initialize()
{
var lookup = new Dictionary<BorderPart, string>();
foreach (BorderPart? part in Enum.GetValues(typeof(BorderPart)))
var lookup = new Dictionary<BoxBorderPart, string>();
foreach (BoxBorderPart? part in Enum.GetValues(typeof(BoxBorderPart)))
{
if (part == null)
{
continue;
}
var text = GetBoxPart(part.Value);
var text = GetBorderPart(part.Value);
if (text.Length > 1)
{
throw new InvalidOperationException("A box part cannot contain more than one character.");
}
lookup.Add(part.Value, GetBoxPart(part.Value));
lookup.Add(part.Value, GetBorderPart(part.Value));
}
return lookup;
@ -59,10 +54,10 @@ namespace Spectre.Console
/// <param name="part">The part to get a string representation for.</param>
/// <param name="count">The number of repetitions.</param>
/// <returns>A string representation of the specified border part.</returns>
public string GetPart(BorderPart part, int count)
public string GetPart(BoxBorderPart part, int count)
{
// TODO: This need some optimization...
return string.Join(string.Empty, Enumerable.Repeat(GetBoxPart(part)[0], count));
return string.Join(string.Empty, Enumerable.Repeat(GetBorderPart(part)[0], count));
}
/// <summary>
@ -70,7 +65,7 @@ namespace Spectre.Console
/// </summary>
/// <param name="part">The part to get a string representation for.</param>
/// <returns>A string representation of the specified border part.</returns>
public string GetPart(BorderPart part)
public string GetPart(BoxBorderPart part)
{
return _lookup[part].ToString(CultureInfo.InvariantCulture);
}
@ -80,6 +75,6 @@ namespace Spectre.Console
/// </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 string GetBoxPart(BorderPart part);
protected abstract string GetBorderPart(BoxBorderPart part);
}
}

View File

@ -1,7 +1,6 @@
using System;
using System.Diagnostics;
using System.Globalization;
using System.Security.Cryptography;
using Spectre.Console.Internal;
namespace Spectre.Console

View File

@ -3,7 +3,7 @@ using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Contains extension methods for <see cref="Border"/>.
/// Contains extension methods for <see cref="TableBorder"/>.
/// </summary>
public static class BorderExtensions
{
@ -13,7 +13,7 @@ namespace Spectre.Console.Rendering
/// <param name="border">The border to get the safe border for.</param>
/// <param name="safe">Whether or not to return the safe border.</param>
/// <returns>The safe border if one exist, otherwise the original border.</returns>
public static Border GetSafeBorder(this Border border, bool safe)
public static TableBorder GetSafeBorder(this TableBorder border, bool safe)
{
if (border is null)
{

View File

@ -0,0 +1,31 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Contains extension methods for <see cref="BoxBorder"/>.
/// </summary>
public static class BoxExtensions
{
/// <summary>
/// Gets the safe border for a border.
/// </summary>
/// <param name="border">The border to get the safe border for.</param>
/// <param name="safe">Whether or not to return the safe border.</param>
/// <returns>The safe border if one exist, otherwise the original border.</returns>
public static BoxBorder GetSafeBorder(this BoxBorder border, bool safe)
{
if (border is null)
{
throw new ArgumentNullException(nameof(border));
}
if (safe && border.SafeBorder != null)
{
border = border.SafeBorder;
}
return border;
}
}
}

View File

@ -7,229 +7,6 @@ namespace Spectre.Console
/// </summary>
public static class HasBorderExtensions
{
/// <summary>
/// Do not display a border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T NoBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.None);
}
/// <summary>
/// Display a square border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SquareBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Square);
}
/// <summary>
/// Display an ASCII border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T AsciiBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Ascii);
}
/// <summary>
/// Display another ASCII border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T Ascii2Border<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Ascii2);
}
/// <summary>
/// Display an ASCII border with a double header border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T AsciiDoubleHeadBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.AsciiDoubleHead);
}
/// <summary>
/// Display a rounded border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T RoundedBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Rounded);
}
/// <summary>
/// Display a minimal border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T MinimalBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Minimal);
}
/// <summary>
/// Display a minimal border with a heavy head.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T MinimalHeavyHeadBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.MinimalHeavyHead);
}
/// <summary>
/// Display a minimal border with a double header border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T MinimalDoubleHeadBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.MinimalDoubleHead);
}
/// <summary>
/// Display a simple border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SimpleBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Simple);
}
/// <summary>
/// Display a simple border with heavy lines.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SimpleHeavyBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.SimpleHeavy);
}
/// <summary>
/// Display a simple border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T HorizontalBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Horizontal);
}
/// <summary>
/// Display a heavy border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T HeavyBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Heavy);
}
/// <summary>
/// Display a border with a heavy edge.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T HeavyEdgeBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.HeavyEdge);
}
/// <summary>
/// Display a border with a heavy header.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T HeavyHeadBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.HeavyHead);
}
/// <summary>
/// Display a double border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T DoubleBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.Double);
}
/// <summary>
/// Display a border with a double edge.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T DoubleEdgeBorder<T>(this T obj)
where T : class, IHasBorder
{
return SetBorder(obj, Border.DoubleEdge);
}
/// <summary>
/// Sets the border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <param name="border">The border to use.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetBorder<T>(this T obj, Border border)
where T : class, IHasBorder
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Border = border;
return obj;
}
/// <summary>
/// Disables the safe border.
/// </summary>

View File

@ -0,0 +1,101 @@
using System;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IHasBoxBorder"/>.
/// </summary>
public static class HasBoxBorderExtensions
{
/// <summary>
/// Do not display a border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T NoBorder<T>(this T obj)
where T : class, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.None);
}
/// <summary>
/// Display a square border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SquareBorder<T>(this T obj)
where T : class, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.Square);
}
/// <summary>
/// Display an ASCII border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T AsciiBorder<T>(this T obj)
where T : class, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.Ascii);
}
/// <summary>
/// Display a rounded border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T RoundedBorder<T>(this T obj)
where T : class, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.Rounded);
}
/// <summary>
/// Display a heavy border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T HeavyBorder<T>(this T obj)
where T : class, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.Heavy);
}
/// <summary>
/// Display a double border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T DoubleBorder<T>(this T obj)
where T : class, IHasBoxBorder
{
return SetBorder(obj, BoxBorder.Double);
}
/// <summary>
/// Sets the border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <param name="border">The border to use.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetBorder<T>(this T obj, BoxBorder border)
where T : class, IHasBoxBorder
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Border = border;
return obj;
}
}
}

View File

@ -0,0 +1,245 @@
using System;
namespace Spectre.Console
{
/// <summary>
/// Contains extension methods for <see cref="IHasTableBorder"/>.
/// </summary>
public static class HasTableBorderExtensions
{
/// <summary>
/// Do not display a border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T NoBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.None);
}
/// <summary>
/// Display a square border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SquareBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Square);
}
/// <summary>
/// Display an ASCII border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T AsciiBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Ascii);
}
/// <summary>
/// Display another ASCII border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T Ascii2Border<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Ascii2);
}
/// <summary>
/// Display an ASCII border with a double header border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T AsciiDoubleHeadBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.AsciiDoubleHead);
}
/// <summary>
/// Display a rounded border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T RoundedBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Rounded);
}
/// <summary>
/// Display a minimal border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T MinimalBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Minimal);
}
/// <summary>
/// Display a minimal border with a heavy head.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T MinimalHeavyHeadBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.MinimalHeavyHead);
}
/// <summary>
/// Display a minimal border with a double header border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T MinimalDoubleHeadBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.MinimalDoubleHead);
}
/// <summary>
/// Display a simple border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SimpleBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Simple);
}
/// <summary>
/// Display a simple border with heavy lines.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SimpleHeavyBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.SimpleHeavy);
}
/// <summary>
/// Display a simple border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T HorizontalBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Horizontal);
}
/// <summary>
/// Display a heavy border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T HeavyBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Heavy);
}
/// <summary>
/// Display a border with a heavy edge.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T HeavyEdgeBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.HeavyEdge);
}
/// <summary>
/// Display a border with a heavy header.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T HeavyHeadBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.HeavyHead);
}
/// <summary>
/// Display a double border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T DoubleBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Double);
}
/// <summary>
/// Display a border with a double edge.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T DoubleEdgeBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.DoubleEdge);
}
/// <summary>
/// Display a markdown border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T MarkdownBorder<T>(this T obj)
where T : class, IHasTableBorder
{
return SetBorder(obj, TableBorder.Markdown);
}
/// <summary>
/// Sets the border.
/// </summary>
/// <typeparam name="T">An object type with a border.</typeparam>
/// <param name="obj">The object to set the border for.</param>
/// <param name="border">The border to use.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T SetBorder<T>(this T obj, TableBorder border)
where T : class, IHasTableBorder
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.Border = border;
return obj;
}
}
}

View File

@ -13,12 +13,7 @@ namespace Spectre.Console
bool UseSafeBorder { get; set; }
/// <summary>
/// Gets or sets the border.
/// </summary>
public Border Border { get; set; }
/// <summary>
/// Gets or sets the border style.
/// Gets or sets the box style.
/// </summary>
public Style? BorderStyle { get; set; }
}

View File

@ -0,0 +1,13 @@
namespace Spectre.Console
{
/// <summary>
/// Represents something that has a box border.
/// </summary>
public interface IHasBoxBorder : IHasBorder
{
/// <summary>
/// Gets or sets the box.
/// </summary>
public BoxBorder Border { get; set; }
}
}

View File

@ -0,0 +1,13 @@
namespace Spectre.Console
{
/// <summary>
/// Represents something that has a border.
/// </summary>
public interface IHasTableBorder : IHasBorder
{
/// <summary>
/// Gets or sets the border.
/// </summary>
public TableBorder Border { get; set; }
}
}

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Spectre.Console.Rendering;
@ -80,27 +81,24 @@ namespace Spectre.Console.Internal
return result.ToArray();
}
// https://andrewlock.net/why-is-string-gethashcode-different-each-time-i-run-my-program-in-net-core/
public static int GetDeterministicHashCode(this string str)
public static string Multiply(this string text, int count)
{
unchecked
if (text is null)
{
var hash1 = (5381 << 16) + 5381;
var hash2 = hash1;
for (var i = 0; i < str.Length; i += 2)
{
hash1 = ((hash1 << 5) + hash1) ^ str[i];
if (i == str.Length - 1)
{
break;
}
hash2 = ((hash2 << 5) + hash2) ^ str[i + 1];
}
return hash1 + (hash2 * 1566083941);
throw new ArgumentNullException(nameof(text));
}
if (count <= 0)
{
return string.Empty;
}
if (count == 1)
{
return text;
}
return string.Concat(Enumerable.Repeat(text, count));
}
}
}

View File

@ -0,0 +1,48 @@
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents the different parts of a box border.
/// </summary>
public enum BoxBorderPart
{
/// <summary>
/// The top left part of a box.
/// </summary>
TopLeft,
/// <summary>
/// The top part of a box.
/// </summary>
Top,
/// <summary>
/// The top right part of a box.
/// </summary>
TopRight,
/// <summary>
/// The left part of a box.
/// </summary>
Left,
/// <summary>
/// The right part of a box.
/// </summary>
Right,
/// <summary>
/// The bottom left part of a box.
/// </summary>
BottomLeft,
/// <summary>
/// The bottom part of a box.
/// </summary>
Bottom,
/// <summary>
/// The bottom right part of a box.
/// </summary>
BottomRight,
}
}

View File

@ -0,0 +1,27 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents an old school ASCII border.
/// </summary>
public sealed class AsciiBoxBorder : BoxBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(BoxBorderPart part)
{
return part switch
{
BoxBorderPart.TopLeft => "+",
BoxBorderPart.Top => "-",
BoxBorderPart.TopRight => "+",
BoxBorderPart.Left => "|",
BoxBorderPart.Right => "|",
BoxBorderPart.BottomLeft => "+",
BoxBorderPart.Bottom => "-",
BoxBorderPart.BottomRight => "+",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,27 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a double border.
/// </summary>
public sealed class DoubleBoxBorder : BoxBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(BoxBorderPart part)
{
return part switch
{
BoxBorderPart.TopLeft => "╔",
BoxBorderPart.Top => "═",
BoxBorderPart.TopRight => "╗",
BoxBorderPart.Left => "║",
BoxBorderPart.Right => "║",
BoxBorderPart.BottomLeft => "╚",
BoxBorderPart.Bottom => "═",
BoxBorderPart.BottomRight => "╝",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,30 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a heavy border.
/// </summary>
public sealed class HeavyBoxBorder : BoxBorder
{
/// <inheritdoc/>
public override BoxBorder? SafeBorder => BoxBorder.Square;
/// <inheritdoc/>
protected override string GetBorderPart(BoxBorderPart part)
{
return part switch
{
BoxBorderPart.TopLeft => "┏",
BoxBorderPart.Top => "━",
BoxBorderPart.TopRight => "┓",
BoxBorderPart.Left => "┃",
BoxBorderPart.Right => "┃",
BoxBorderPart.BottomLeft => "┗",
BoxBorderPart.Bottom => "━",
BoxBorderPart.BottomRight => "┛",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,14 @@
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents an invisible border.
/// </summary>
public sealed class NoBoxBorder : BoxBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(BoxBorderPart part)
{
return " ";
}
}
}

View File

@ -0,0 +1,30 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a rounded border.
/// </summary>
public sealed class RoundedBoxBorder : BoxBorder
{
/// <inheritdoc/>
public override BoxBorder? SafeBorder => BoxBorder.Square;
/// <inheritdoc/>
protected override string GetBorderPart(BoxBorderPart part)
{
return part switch
{
BoxBorderPart.TopLeft => "╭",
BoxBorderPart.Top => "─",
BoxBorderPart.TopRight => "╮",
BoxBorderPart.Left => "│",
BoxBorderPart.Right => "│",
BoxBorderPart.BottomLeft => "╰",
BoxBorderPart.Bottom => "─",
BoxBorderPart.BottomRight => "╯",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,27 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a square border.
/// </summary>
public sealed class SquareBoxBorder : BoxBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(BoxBorderPart part)
{
return part switch
{
BoxBorderPart.TopLeft => "┌",
BoxBorderPart.Top => "─",
BoxBorderPart.TopRight => "┐",
BoxBorderPart.Left => "│",
BoxBorderPart.Right => "│",
BoxBorderPart.BottomLeft => "└",
BoxBorderPart.Bottom => "─",
BoxBorderPart.BottomRight => "┘",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -1,9 +1,9 @@
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents the different border parts.
/// Represents the different parts of a table border.
/// </summary>
public enum BorderPart
public enum TableBorderPart
{
/// <summary>
/// The top left part of a header.

View File

@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents another old school ASCII border.
/// </summary>
public sealed class Ascii2TableBorder : TableBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => "+",
TableBorderPart.HeaderTop => "-",
TableBorderPart.HeaderTopSeparator => "+",
TableBorderPart.HeaderTopRight => "+",
TableBorderPart.HeaderLeft => "|",
TableBorderPart.HeaderSeparator => "|",
TableBorderPart.HeaderRight => "|",
TableBorderPart.HeaderBottomLeft => "|",
TableBorderPart.HeaderBottom => "-",
TableBorderPart.HeaderBottomSeparator => "+",
TableBorderPart.HeaderBottomRight => "|",
TableBorderPart.CellLeft => "|",
TableBorderPart.CellSeparator => "|",
TableBorderPart.CellRight => "|",
TableBorderPart.FooterBottomLeft => "+",
TableBorderPart.FooterBottom => "-",
TableBorderPart.FooterBottomSeparator => "+",
TableBorderPart.FooterBottomRight => "+",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents an old school ASCII border with a double header border.
/// </summary>
public sealed class AsciiDoubleHeadTableBorder : TableBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => "+",
TableBorderPart.HeaderTop => "-",
TableBorderPart.HeaderTopSeparator => "+",
TableBorderPart.HeaderTopRight => "+",
TableBorderPart.HeaderLeft => "|",
TableBorderPart.HeaderSeparator => "|",
TableBorderPart.HeaderRight => "|",
TableBorderPart.HeaderBottomLeft => "|",
TableBorderPart.HeaderBottom => "=",
TableBorderPart.HeaderBottomSeparator => "+",
TableBorderPart.HeaderBottomRight => "|",
TableBorderPart.CellLeft => "|",
TableBorderPart.CellSeparator => "|",
TableBorderPart.CellRight => "|",
TableBorderPart.FooterBottomLeft => "+",
TableBorderPart.FooterBottom => "-",
TableBorderPart.FooterBottomSeparator => "+",
TableBorderPart.FooterBottomRight => "+",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents an old school ASCII border.
/// </summary>
public sealed class AsciiTableBorder : TableBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => "+",
TableBorderPart.HeaderTop => "-",
TableBorderPart.HeaderTopSeparator => "-",
TableBorderPart.HeaderTopRight => "+",
TableBorderPart.HeaderLeft => "|",
TableBorderPart.HeaderSeparator => "|",
TableBorderPart.HeaderRight => "|",
TableBorderPart.HeaderBottomLeft => "|",
TableBorderPart.HeaderBottom => "-",
TableBorderPart.HeaderBottomSeparator => "+",
TableBorderPart.HeaderBottomRight => "|",
TableBorderPart.CellLeft => "|",
TableBorderPart.CellSeparator => "|",
TableBorderPart.CellRight => "|",
TableBorderPart.FooterBottomLeft => "+",
TableBorderPart.FooterBottom => "-",
TableBorderPart.FooterBottomSeparator => "-",
TableBorderPart.FooterBottomRight => "+",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a border with a double edge.
/// </summary>
public sealed class DoubleEdgeTableBorder : TableBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => "╔",
TableBorderPart.HeaderTop => "═",
TableBorderPart.HeaderTopSeparator => "╤",
TableBorderPart.HeaderTopRight => "╗",
TableBorderPart.HeaderLeft => "║",
TableBorderPart.HeaderSeparator => "│",
TableBorderPart.HeaderRight => "║",
TableBorderPart.HeaderBottomLeft => "╟",
TableBorderPart.HeaderBottom => "─",
TableBorderPart.HeaderBottomSeparator => "┼",
TableBorderPart.HeaderBottomRight => "╢",
TableBorderPart.CellLeft => "║",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "║",
TableBorderPart.FooterBottomLeft => "╚",
TableBorderPart.FooterBottom => "═",
TableBorderPart.FooterBottomSeparator => "╧",
TableBorderPart.FooterBottomRight => "╝",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a double border.
/// </summary>
public sealed class DoubleTableBorder : TableBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => "╔",
TableBorderPart.HeaderTop => "═",
TableBorderPart.HeaderTopSeparator => "╦",
TableBorderPart.HeaderTopRight => "╗",
TableBorderPart.HeaderLeft => "║",
TableBorderPart.HeaderSeparator => "║",
TableBorderPart.HeaderRight => "║",
TableBorderPart.HeaderBottomLeft => "╠",
TableBorderPart.HeaderBottom => "═",
TableBorderPart.HeaderBottomSeparator => "╬",
TableBorderPart.HeaderBottomRight => "╣",
TableBorderPart.CellLeft => "║",
TableBorderPart.CellSeparator => "║",
TableBorderPart.CellRight => "║",
TableBorderPart.FooterBottomLeft => "╚",
TableBorderPart.FooterBottom => "═",
TableBorderPart.FooterBottomSeparator => "╩",
TableBorderPart.FooterBottomRight => "╝",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,40 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a border with a heavy edge.
/// </summary>
public sealed class HeavyEdgeTableBorder : TableBorder
{
/// <inheritdoc/>
public override TableBorder? SafeBorder => TableBorder.Square;
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => "┏",
TableBorderPart.HeaderTop => "━",
TableBorderPart.HeaderTopSeparator => "┯",
TableBorderPart.HeaderTopRight => "┓",
TableBorderPart.HeaderLeft => "┃",
TableBorderPart.HeaderSeparator => "│",
TableBorderPart.HeaderRight => "┃",
TableBorderPart.HeaderBottomLeft => "┠",
TableBorderPart.HeaderBottom => "─",
TableBorderPart.HeaderBottomSeparator => "┼",
TableBorderPart.HeaderBottomRight => "┨",
TableBorderPart.CellLeft => "┃",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "┃",
TableBorderPart.FooterBottomLeft => "┗",
TableBorderPart.FooterBottom => "━",
TableBorderPart.FooterBottomSeparator => "┷",
TableBorderPart.FooterBottomRight => "┛",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,40 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a border with a heavy header.
/// </summary>
public sealed class HeavyHeadTableBorder : TableBorder
{
/// <inheritdoc/>
public override TableBorder? SafeBorder => TableBorder.Square;
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => "┏",
TableBorderPart.HeaderTop => "━",
TableBorderPart.HeaderTopSeparator => "┳",
TableBorderPart.HeaderTopRight => "┓",
TableBorderPart.HeaderLeft => "┃",
TableBorderPart.HeaderSeparator => "┃",
TableBorderPart.HeaderRight => "┃",
TableBorderPart.HeaderBottomLeft => "┡",
TableBorderPart.HeaderBottom => "━",
TableBorderPart.HeaderBottomSeparator => "╇",
TableBorderPart.HeaderBottomRight => "┩",
TableBorderPart.CellLeft => "│",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "│",
TableBorderPart.FooterBottomLeft => "└",
TableBorderPart.FooterBottom => "─",
TableBorderPart.FooterBottomSeparator => "┴",
TableBorderPart.FooterBottomRight => "┘",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,40 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a heavy border.
/// </summary>
public sealed class HeavyTableBorder : TableBorder
{
/// <inheritdoc/>
public override TableBorder? SafeBorder => TableBorder.Square;
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => "┏",
TableBorderPart.HeaderTop => "━",
TableBorderPart.HeaderTopSeparator => "┳",
TableBorderPart.HeaderTopRight => "┓",
TableBorderPart.HeaderLeft => "┃",
TableBorderPart.HeaderSeparator => "┃",
TableBorderPart.HeaderRight => "┃",
TableBorderPart.HeaderBottomLeft => "┣",
TableBorderPart.HeaderBottom => "━",
TableBorderPart.HeaderBottomSeparator => "╋",
TableBorderPart.HeaderBottomRight => "┫",
TableBorderPart.CellLeft => "┃",
TableBorderPart.CellSeparator => "┃",
TableBorderPart.CellRight => "┃",
TableBorderPart.FooterBottomLeft => "┗",
TableBorderPart.FooterBottom => "━",
TableBorderPart.FooterBottomSeparator => "┻",
TableBorderPart.FooterBottomRight => "┛",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a horizontal border.
/// </summary>
public sealed class HorizontalTableBorder : TableBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => "─",
TableBorderPart.HeaderTop => "─",
TableBorderPart.HeaderTopSeparator => "─",
TableBorderPart.HeaderTopRight => "─",
TableBorderPart.HeaderLeft => " ",
TableBorderPart.HeaderSeparator => " ",
TableBorderPart.HeaderRight => " ",
TableBorderPart.HeaderBottomLeft => "─",
TableBorderPart.HeaderBottom => "─",
TableBorderPart.HeaderBottomSeparator => "─",
TableBorderPart.HeaderBottomRight => "─",
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => " ",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterBottomLeft => "─",
TableBorderPart.FooterBottom => "─",
TableBorderPart.FooterBottomSeparator => "─",
TableBorderPart.FooterBottomRight => "─",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,105 @@
using System;
using System.Collections.Generic;
using System.Text;
using Spectre.Console.Internal;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a Markdown border.
/// </summary>
public sealed class MarkdownTableBorder : TableBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => " ",
TableBorderPart.HeaderTop => " ",
TableBorderPart.HeaderTopSeparator => " ",
TableBorderPart.HeaderTopRight => " ",
TableBorderPart.HeaderLeft => "|",
TableBorderPart.HeaderSeparator => "|",
TableBorderPart.HeaderRight => "|",
TableBorderPart.HeaderBottomLeft => "|",
TableBorderPart.HeaderBottom => "-",
TableBorderPart.HeaderBottomSeparator => "|",
TableBorderPart.HeaderBottomRight => "|",
TableBorderPart.CellLeft => "|",
TableBorderPart.CellSeparator => "|",
TableBorderPart.CellRight => "|",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",
TableBorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
/// <inheritdoc/>
public override string GetColumnRow(TablePart part, IReadOnlyList<int> widths, IReadOnlyList<IColumn> columns)
{
if (part != TablePart.Separator)
{
return base.GetColumnRow(part, widths, columns);
}
var (left, center, separator, right) = GetTableParts(part);
var builder = new StringBuilder();
builder.Append(left);
foreach (var (columnIndex, _, lastColumn, columnWidth) in widths.Enumerate())
{
var padding = columns[columnIndex].Padding;
if (padding.Left > 0)
{
// Left padding
builder.Append(" ".Multiply(padding.Left));
}
var justification = columns[columnIndex].Alignment;
if (justification == null)
{
// No alignment
builder.Append(center.Multiply(columnWidth));
}
else if (justification.Value == Justify.Left)
{
// Left
builder.Append(':');
builder.Append(center.Multiply(columnWidth - 1));
}
else if (justification.Value == Justify.Center)
{
// Centered
builder.Append(':');
builder.Append(center.Multiply(columnWidth - 2));
builder.Append(':');
}
else if (justification.Value == Justify.Right)
{
// Right
builder.Append(center.Multiply(columnWidth - 1));
builder.Append(':');
}
// Right padding
if (padding.Right > 0)
{
builder.Append(" ".Multiply(padding.Right));
}
if (!lastColumn)
{
builder.Append(separator);
}
}
builder.Append(right);
return builder.ToString();
}
}
}

View File

@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a minimal border with a double header border.
/// </summary>
public sealed class MinimalDoubleHeadTableBorder : TableBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => " ",
TableBorderPart.HeaderTop => " ",
TableBorderPart.HeaderTopSeparator => " ",
TableBorderPart.HeaderTopRight => " ",
TableBorderPart.HeaderLeft => " ",
TableBorderPart.HeaderSeparator => "│",
TableBorderPart.HeaderRight => " ",
TableBorderPart.HeaderBottomLeft => " ",
TableBorderPart.HeaderBottom => "═",
TableBorderPart.HeaderBottomSeparator => "╪",
TableBorderPart.HeaderBottomRight => " ",
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",
TableBorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,40 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a minimal border with a heavy header.
/// </summary>
public sealed class MinimalHeavyHeadTableBorder : TableBorder
{
/// <inheritdoc/>
public override TableBorder? SafeBorder => TableBorder.Minimal;
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => " ",
TableBorderPart.HeaderTop => " ",
TableBorderPart.HeaderTopSeparator => " ",
TableBorderPart.HeaderTopRight => " ",
TableBorderPart.HeaderLeft => " ",
TableBorderPart.HeaderSeparator => "│",
TableBorderPart.HeaderRight => " ",
TableBorderPart.HeaderBottomLeft => " ",
TableBorderPart.HeaderBottom => "━",
TableBorderPart.HeaderBottomSeparator => "┿",
TableBorderPart.HeaderBottomRight => " ",
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",
TableBorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a minimal border.
/// </summary>
public sealed class MinimalTableBorder : TableBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => " ",
TableBorderPart.HeaderTop => " ",
TableBorderPart.HeaderTopSeparator => " ",
TableBorderPart.HeaderTopRight => " ",
TableBorderPart.HeaderLeft => " ",
TableBorderPart.HeaderSeparator => "│",
TableBorderPart.HeaderRight => " ",
TableBorderPart.HeaderBottomLeft => " ",
TableBorderPart.HeaderBottom => "─",
TableBorderPart.HeaderBottomSeparator => "┼",
TableBorderPart.HeaderBottomRight => " ",
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",
TableBorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -3,13 +3,13 @@ namespace Spectre.Console.Rendering
/// <summary>
/// Represents an invisible border.
/// </summary>
public sealed class NoBorder : Border
public sealed class NoTableBorder : TableBorder
{
/// <inheritdoc/>
public override bool Visible => false;
/// <inheritdoc/>
protected override string GetBoxPart(BorderPart part)
protected override string GetBorderPart(TableBorderPart part)
{
return " ";
}

View File

@ -0,0 +1,40 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a rounded border.
/// </summary>
public sealed class RoundedTableBorder : TableBorder
{
/// <inheritdoc/>
public override TableBorder? SafeBorder => TableBorder.Square;
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => "╭",
TableBorderPart.HeaderTop => "─",
TableBorderPart.HeaderTopSeparator => "┬",
TableBorderPart.HeaderTopRight => "╮",
TableBorderPart.HeaderLeft => "│",
TableBorderPart.HeaderSeparator => "│",
TableBorderPart.HeaderRight => "│",
TableBorderPart.HeaderBottomLeft => "├",
TableBorderPart.HeaderBottom => "─",
TableBorderPart.HeaderBottomSeparator => "┼",
TableBorderPart.HeaderBottomRight => "┤",
TableBorderPart.CellLeft => "│",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "│",
TableBorderPart.FooterBottomLeft => "╰",
TableBorderPart.FooterBottom => "─",
TableBorderPart.FooterBottomSeparator => "┴",
TableBorderPart.FooterBottomRight => "╯",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,40 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a simple border with heavy lines.
/// </summary>
public sealed class SimpleHeavyTableBorder : TableBorder
{
/// <inheritdoc/>
public override TableBorder? SafeBorder => TableBorder.Simple;
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => " ",
TableBorderPart.HeaderTop => " ",
TableBorderPart.HeaderTopSeparator => " ",
TableBorderPart.HeaderTopRight => " ",
TableBorderPart.HeaderLeft => " ",
TableBorderPart.HeaderSeparator => " ",
TableBorderPart.HeaderRight => " ",
TableBorderPart.HeaderBottomLeft => "━",
TableBorderPart.HeaderBottom => "━",
TableBorderPart.HeaderBottomSeparator => "━",
TableBorderPart.HeaderBottomRight => "━",
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => " ",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",
TableBorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a simple border.
/// </summary>
public sealed class SimpleTableBorder : TableBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => " ",
TableBorderPart.HeaderTop => " ",
TableBorderPart.HeaderTopSeparator => " ",
TableBorderPart.HeaderTopRight => " ",
TableBorderPart.HeaderLeft => " ",
TableBorderPart.HeaderSeparator => " ",
TableBorderPart.HeaderRight => " ",
TableBorderPart.HeaderBottomLeft => "─",
TableBorderPart.HeaderBottom => "─",
TableBorderPart.HeaderBottomSeparator => "─",
TableBorderPart.HeaderBottomRight => "─",
TableBorderPart.CellLeft => " ",
TableBorderPart.CellSeparator => " ",
TableBorderPart.CellRight => " ",
TableBorderPart.FooterBottomLeft => " ",
TableBorderPart.FooterBottom => " ",
TableBorderPart.FooterBottomSeparator => " ",
TableBorderPart.FooterBottomRight => " ",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,37 @@
using System;
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents a square border.
/// </summary>
public sealed class SquareTableBorder : TableBorder
{
/// <inheritdoc/>
protected override string GetBorderPart(TableBorderPart part)
{
return part switch
{
TableBorderPart.HeaderTopLeft => "┌",
TableBorderPart.HeaderTop => "─",
TableBorderPart.HeaderTopSeparator => "┬",
TableBorderPart.HeaderTopRight => "┐",
TableBorderPart.HeaderLeft => "│",
TableBorderPart.HeaderSeparator => "│",
TableBorderPart.HeaderRight => "│",
TableBorderPart.HeaderBottomLeft => "├",
TableBorderPart.HeaderBottom => "─",
TableBorderPart.HeaderBottomSeparator => "┼",
TableBorderPart.HeaderBottomRight => "┤",
TableBorderPart.CellLeft => "│",
TableBorderPart.CellSeparator => "│",
TableBorderPart.CellRight => "│",
TableBorderPart.FooterBottomLeft => "└",
TableBorderPart.FooterBottom => "─",
TableBorderPart.FooterBottomSeparator => "┴",
TableBorderPart.FooterBottomRight => "┘",
_ => throw new InvalidOperationException("Unknown border part."),
};
}
}
}

View File

@ -0,0 +1,23 @@
namespace Spectre.Console.Rendering
{
/// <summary>
/// Represents different parts of a table.
/// </summary>
public enum TablePart
{
/// <summary>
/// The top of a table.
/// </summary>
Top,
/// <summary>
/// The separator between the header and the cells.
/// </summary>
Separator,
/// <summary>
/// The bottom of a table.
/// </summary>
Bottom,
}
}

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
@ -14,12 +14,15 @@
<Compile Update="AnsiConsole.*.cs">
<DependentUpon>AnsiConsole.cs</DependentUpon>
</Compile>
<Compile Update="Color.*.cs">
<DependentUpon>Color.cs</DependentUpon>
</Compile>
<Compile Update="Border.*.cs">
<DependentUpon>Border.cs</DependentUpon>
</Compile>
<Compile Update="BoxBorder.*.cs">
<DependentUpon>BoxBorder.cs</DependentUpon>
</Compile>
<Compile Update="Color.*.cs">
<DependentUpon>Color.cs</DependentUpon>
</Compile>
<Compile Update="Emoji.*.cs">
<DependentUpon>Emoji.cs</DependentUpon>
</Compile>
@ -39,6 +42,12 @@
</ItemGroup>
<ItemGroup>
<Compile Update="BoxBorder.Known.cs">
<DependentUpon>BoxBorder.cs</DependentUpon>
</Compile>
<Compile Update="TableBorder.Known.cs">
<DependentUpon>TableBorder.cs</DependentUpon>
</Compile>
<Compile Update="Extensions\AnsiConsoleExtensions.Markup.cs">
<DependentUpon>AnsiConsoleExtensions.cs</DependentUpon>
</Compile>

View File

@ -6,92 +6,97 @@ namespace Spectre.Console
/// <summary>
/// Represents a border.
/// </summary>
public abstract partial class Border
public abstract partial class TableBorder
{
/// <summary>
/// Gets an invisible border.
/// </summary>
public static Border None { get; } = new NoBorder();
public static TableBorder None { get; } = new NoTableBorder();
/// <summary>
/// Gets an ASCII border.
/// </summary>
public static Border Ascii { get; } = new AsciiBorder();
public static TableBorder Ascii { get; } = new AsciiTableBorder();
/// <summary>
/// Gets another ASCII border.
/// Gets an ASCII border.
/// </summary>
public static Border Ascii2 { get; } = new Ascii2Border();
public static TableBorder Ascii2 { get; } = new Ascii2TableBorder();
/// <summary>
/// Gets an ASCII border with a double header border.
/// </summary>
public static Border AsciiDoubleHead { get; } = new AsciiDoubleHeadBorder();
public static TableBorder AsciiDoubleHead { get; } = new AsciiDoubleHeadTableBorder();
/// <summary>
/// Gets a square border.
/// </summary>
public static Border Square { get; } = new SquareBorder();
public static TableBorder Square { get; } = new SquareTableBorder();
/// <summary>
/// Gets a rounded border.
/// </summary>
public static Border Rounded { get; } = new RoundedBorder();
public static TableBorder Rounded { get; } = new RoundedTableBorder();
/// <summary>
/// Gets a minimal border.
/// </summary>
public static Border Minimal { get; } = new MinimalBorder();
public static TableBorder Minimal { get; } = new MinimalTableBorder();
/// <summary>
/// Gets a minimal border with a heavy head.
/// </summary>
public static Border MinimalHeavyHead { get; } = new MinimalHeavyHeadBorder();
public static TableBorder MinimalHeavyHead { get; } = new MinimalHeavyHeadTableBorder();
/// <summary>
/// Gets a minimal border with a double header border.
/// </summary>
public static Border MinimalDoubleHead { get; } = new MinimalDoubleHeadBorder();
public static TableBorder MinimalDoubleHead { get; } = new MinimalDoubleHeadTableBorder();
/// <summary>
/// Gets a simple border.
/// </summary>
public static Border Simple { get; } = new SimpleBorder();
public static TableBorder Simple { get; } = new SimpleTableBorder();
/// <summary>
/// Gets a simple border with heavy lines.
/// </summary>
public static Border SimpleHeavy { get; } = new SimpleHeavyBorder();
public static TableBorder SimpleHeavy { get; } = new SimpleHeavyTableBorder();
/// <summary>
/// Gets a horizontal border.
/// </summary>
public static Border Horizontal { get; } = new HorizontalBorder();
public static TableBorder Horizontal { get; } = new HorizontalTableBorder();
/// <summary>
/// Gets a heavy border.
/// </summary>
public static Border Heavy { get; } = new HeavyBorder();
public static TableBorder Heavy { get; } = new HeavyTableBorder();
/// <summary>
/// Gets a border with a heavy edge.
/// </summary>
public static Border HeavyEdge { get; } = new HeavyEdgeBorder();
public static TableBorder HeavyEdge { get; } = new HeavyEdgeTableBorder();
/// <summary>
/// Gets a border with a heavy header.
/// </summary>
public static Border HeavyHead { get; } = new HeavyHeadBorder();
public static TableBorder HeavyHead { get; } = new HeavyHeadTableBorder();
/// <summary>
/// Gets a double border.
/// </summary>
[SuppressMessage("Naming", "CA1720:Identifier contains type name")]
public static Border Double { get; } = new DoubleBorder();
public static TableBorder Double { get; } = new DoubleTableBorder();
/// <summary>
/// Gets a border with a double edge.
/// </summary>
public static Border DoubleEdge { get; } = new DoubleEdgeBorder();
public static TableBorder DoubleEdge { get; } = new DoubleEdgeTableBorder();
/// <summary>
/// Gets a markdown border.
/// </summary>
public static TableBorder Markdown { get; } = new MarkdownTableBorder();
}
}

View File

@ -0,0 +1,157 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using Spectre.Console.Internal;
using Spectre.Console.Rendering;
namespace Spectre.Console
{
/// <summary>
/// Represents a border.
/// </summary>
public abstract partial class TableBorder
{
private readonly Dictionary<TableBorderPart, string> _lookup;
/// <summary>
/// Gets a value indicating whether or not the border is visible.
/// </summary>
public virtual bool Visible { get; } = true;
/// <summary>
/// Gets the safe border for this border or <c>null</c> if none exist.
/// </summary>
public virtual TableBorder? SafeBorder { get; }
/// <summary>
/// Initializes a new instance of the <see cref="TableBorder"/> class.
/// </summary>
protected TableBorder()
{
_lookup = Initialize();
}
private Dictionary<TableBorderPart, string> Initialize()
{
var lookup = new Dictionary<TableBorderPart, string>();
foreach (TableBorderPart? part in Enum.GetValues(typeof(TableBorderPart)))
{
if (part == null)
{
continue;
}
var text = GetBorderPart(part.Value);
if (text.Length > 1)
{
throw new InvalidOperationException("A box part cannot contain more than one character.");
}
lookup.Add(part.Value, GetBorderPart(part.Value));
}
return lookup;
}
/// <summary>
/// Gets the string representation of a specific border part.
/// </summary>
/// <param name="part">The part to get a string representation for.</param>
/// <param name="count">The number of repetitions.</param>
/// <returns>A string representation of the specified border part.</returns>
public string GetPart(TableBorderPart part, int count)
{
// TODO: This need some optimization...
return string.Join(string.Empty, Enumerable.Repeat(GetBorderPart(part)[0], count));
}
/// <summary>
/// Gets a whole column row for the specific column row part.
/// </summary>
/// <param name="part">The column row part.</param>
/// <param name="widths">The column widths.</param>
/// <param name="columns">The columns.</param>
/// <returns>A string representing the column row.</returns>
public virtual string GetColumnRow(TablePart part, IReadOnlyList<int> widths, IReadOnlyList<IColumn> columns)
{
if (widths is null)
{
throw new ArgumentNullException(nameof(widths));
}
if (columns is null)
{
throw new ArgumentNullException(nameof(columns));
}
var (left, center, separator, right) = GetTableParts(part);
var builder = new StringBuilder();
builder.Append(left);
foreach (var (columnIndex, _, lastColumn, columnWidth) in widths.Enumerate())
{
var padding = columns[columnIndex].Padding;
var centerWidth = padding.Left + columnWidth + padding.Right;
builder.Append(center.Multiply(centerWidth));
if (!lastColumn)
{
builder.Append(separator);
}
}
builder.Append(right);
return builder.ToString();
}
/// <summary>
/// Gets the string representation of a specific border part.
/// </summary>
/// <param name="part">The part to get a string representation for.</param>
/// <returns>A string representation of the specified border part.</returns>
public string GetPart(TableBorderPart part)
{
return _lookup[part].ToString(CultureInfo.InvariantCulture);
}
/// <summary>
/// Gets the character representing the specified border part.
/// </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 string GetBorderPart(TableBorderPart part);
/// <summary>
/// Gets the table parts used to render a specific table row.
/// </summary>
/// <param name="part">The table part.</param>
/// <returns>The table parts used to render the specific table row.</returns>
protected (string Left, string Center, string Separator, string Right)
GetTableParts(TablePart part)
{
return part switch
{
// Top part
TablePart.Top =>
(GetPart(TableBorderPart.HeaderTopLeft), GetPart(TableBorderPart.HeaderTop),
GetPart(TableBorderPart.HeaderTopSeparator), GetPart(TableBorderPart.HeaderTopRight)),
// Separator between header and cells
TablePart.Separator =>
(GetPart(TableBorderPart.HeaderBottomLeft), GetPart(TableBorderPart.HeaderBottom),
GetPart(TableBorderPart.HeaderBottomSeparator), GetPart(TableBorderPart.HeaderBottomRight)),
// Bottom part
TablePart.Bottom =>
(GetPart(TableBorderPart.FooterBottomLeft), GetPart(TableBorderPart.FooterBottom),
GetPart(TableBorderPart.FooterBottomSeparator), GetPart(TableBorderPart.FooterBottomRight)),
// Unknown
_ => throw new NotSupportedException("Unknown column row part"),
};
}
}
}

View File

@ -35,7 +35,7 @@ namespace Spectre.Console
{
_table = new Table
{
Border = Border.None,
Border = TableBorder.None,
ShowHeaders = false,
IsGrid = true,
PadRightCell = false,

View File

@ -8,14 +8,14 @@ namespace Spectre.Console
/// <summary>
/// A renderable panel.
/// </summary>
public sealed class Panel : Renderable, IHasBorder, IExpandable, IPaddable
public sealed class Panel : Renderable, IHasBoxBorder, IExpandable, IPaddable
{
private const int EdgeWidth = 2;
private readonly IRenderable _child;
/// <inheritdoc/>
public Border Border { get; set; } = Border.Square;
public BoxBorder Border { get; set; } = BoxBorder.Square;
/// <inheritdoc/>
public bool UseSafeBorder { get; set; } = true;
@ -95,7 +95,7 @@ namespace Spectre.Console
var childSegments = ((IRenderable)child).Render(context, childWidth);
foreach (var line in Segment.SplitLines(childSegments, panelWidth))
{
result.Add(new Segment(border.GetPart(BorderPart.CellLeft), borderStyle));
result.Add(new Segment(border.GetPart(BoxBorderPart.Left), borderStyle));
var content = new List<Segment>();
content.AddRange(line);
@ -110,7 +110,7 @@ namespace Spectre.Console
result.AddRange(content);
result.Add(new Segment(border.GetPart(BorderPart.CellRight), borderStyle));
result.Add(new Segment(border.GetPart(BoxBorderPart.Right), borderStyle));
result.Add(Segment.LineBreak);
}
@ -120,17 +120,17 @@ namespace Spectre.Console
return result;
}
private static void AddBottomBorder(List<Segment> result, Border border, Style borderStyle, int panelWidth)
private static void AddBottomBorder(List<Segment> result, BoxBorder border, Style borderStyle, int panelWidth)
{
result.Add(new Segment(border.GetPart(BorderPart.FooterBottomLeft), borderStyle));
result.Add(new Segment(border.GetPart(BorderPart.FooterBottom, panelWidth - EdgeWidth), borderStyle));
result.Add(new Segment(border.GetPart(BorderPart.FooterBottomRight), borderStyle));
result.Add(new Segment(border.GetPart(BoxBorderPart.BottomLeft), borderStyle));
result.Add(new Segment(border.GetPart(BoxBorderPart.Bottom, panelWidth - EdgeWidth), borderStyle));
result.Add(new Segment(border.GetPart(BoxBorderPart.BottomRight), borderStyle));
result.Add(Segment.LineBreak);
}
private void AddTopBorder(List<Segment> segments, RenderContext context, Border border, Style borderStyle, int panelWidth)
private void AddTopBorder(List<Segment> segments, RenderContext context, BoxBorder border, Style borderStyle, int panelWidth)
{
segments.Add(new Segment(border.GetPart(BorderPart.HeaderTopLeft), borderStyle));
segments.Add(new Segment(border.GetPart(BoxBorderPart.TopLeft), borderStyle));
if (Header != null)
{
@ -160,16 +160,16 @@ namespace Spectre.Console
}
}
segments.Add(new Segment(border.GetPart(BorderPart.HeaderTop, leftSpacing + 1), borderStyle));
segments.Add(new Segment(border.GetPart(BoxBorderPart.Top, leftSpacing + 1), borderStyle));
segments.Add(header);
segments.Add(new Segment(border.GetPart(BorderPart.HeaderTop, rightSpacing + 1), borderStyle));
segments.Add(new Segment(border.GetPart(BoxBorderPart.Top, rightSpacing + 1), borderStyle));
}
else
{
segments.Add(new Segment(border.GetPart(BorderPart.HeaderTop, panelWidth - EdgeWidth), borderStyle));
segments.Add(new Segment(border.GetPart(BoxBorderPart.Top, panelWidth - EdgeWidth), borderStyle));
}
segments.Add(new Segment(border.GetPart(BorderPart.HeaderTopRight), borderStyle));
segments.Add(new Segment(border.GetPart(BoxBorderPart.TopRight), borderStyle));
segments.Add(Segment.LineBreak);
}
}

View File

@ -9,7 +9,7 @@ namespace Spectre.Console
/// <summary>
/// A renderable table.
/// </summary>
public sealed class Table : Renderable, IHasBorder, IExpandable
public sealed class Table : Renderable, IHasTableBorder, IExpandable
{
private const int EdgeCount = 2;
@ -27,7 +27,7 @@ namespace Spectre.Console
public int RowCount => _rows.Count;
/// <inheritdoc/>
public Border Border { get; set; } = Border.Square;
public TableBorder Border { get; set; } = TableBorder.Square;
/// <inheritdoc/>
public Style? BorderStyle { get; set; }
@ -202,22 +202,8 @@ namespace Spectre.Console
// Show top of header?
if (firstRow && showBorder)
{
result.Add(new Segment(border.GetPart(BorderPart.HeaderTopLeft), borderStyle));
foreach (var (columnIndex, _, lastColumn, columnWidth) in columnWidths.Enumerate())
{
var padding = _columns[columnIndex].Padding;
result.Add(new Segment(border.GetPart(BorderPart.HeaderTop, padding.Left), borderStyle)); // Left padding
result.Add(new Segment(border.GetPart(BorderPart.HeaderTop, columnWidth), borderStyle));
result.Add(new Segment(border.GetPart(BorderPart.HeaderTop, padding.Right), borderStyle)); // Right padding
if (!lastColumn)
{
result.Add(new Segment(border.GetPart(BorderPart.HeaderTopSeparator), borderStyle));
}
}
result.Add(new Segment(border.GetPart(BorderPart.HeaderTopRight), borderStyle));
var separator = border.GetColumnRow(TablePart.Top, columnWidths, _columns);
result.Add(new Segment(separator, borderStyle));
result.Add(Segment.LineBreak);
}
@ -232,7 +218,7 @@ namespace Spectre.Console
if (firstCell && showBorder)
{
// Show left column edge
var part = firstRow && ShowHeaders ? BorderPart.HeaderLeft : BorderPart.CellLeft;
var part = firstRow && ShowHeaders ? TableBorderPart.HeaderLeft : TableBorderPart.CellLeft;
result.Add(new Segment(border.GetPart(part), borderStyle));
}
@ -269,13 +255,13 @@ namespace Spectre.Console
if (lastCell && showBorder)
{
// Add right column edge
var part = firstRow && ShowHeaders ? BorderPart.HeaderRight : BorderPart.CellRight;
var part = firstRow && ShowHeaders ? TableBorderPart.HeaderRight : TableBorderPart.CellRight;
result.Add(new Segment(border.GetPart(part), borderStyle));
}
else if (showBorder)
{
// Add column separator
var part = firstRow && ShowHeaders ? BorderPart.HeaderSeparator : BorderPart.CellSeparator;
var part = firstRow && ShowHeaders ? TableBorderPart.HeaderSeparator : TableBorderPart.CellSeparator;
result.Add(new Segment(border.GetPart(part), borderStyle));
}
}
@ -286,44 +272,16 @@ namespace Spectre.Console
// Show header separator?
if (firstRow && showBorder && ShowHeaders && hasRows)
{
result.Add(new Segment(border.GetPart(BorderPart.HeaderBottomLeft), borderStyle));
foreach (var (columnIndex, first, lastColumn, columnWidth) in columnWidths.Enumerate())
{
var padding = _columns[columnIndex].Padding;
result.Add(new Segment(border.GetPart(BorderPart.HeaderBottom, padding.Left), borderStyle)); // Left padding
result.Add(new Segment(border.GetPart(BorderPart.HeaderBottom, columnWidth), borderStyle));
result.Add(new Segment(border.GetPart(BorderPart.HeaderBottom, padding.Right), borderStyle)); // Right padding
if (!lastColumn)
{
result.Add(new Segment(border.GetPart(BorderPart.HeaderBottomSeparator), borderStyle));
}
}
result.Add(new Segment(border.GetPart(BorderPart.HeaderBottomRight), borderStyle));
var separator = border.GetColumnRow(TablePart.Separator, columnWidths, _columns);
result.Add(new Segment(separator, borderStyle));
result.Add(Segment.LineBreak);
}
// Show bottom of footer?
if (lastRow && showBorder)
{
result.Add(new Segment(border.GetPart(BorderPart.FooterBottomLeft), borderStyle));
foreach (var (columnIndex, first, lastColumn, columnWidth) in columnWidths.Enumerate())
{
var padding = _columns[columnIndex].Padding;
result.Add(new Segment(border.GetPart(BorderPart.FooterBottom, padding.Left), borderStyle)); // Left padding
result.Add(new Segment(border.GetPart(BorderPart.FooterBottom, columnWidth), borderStyle));
result.Add(new Segment(border.GetPart(BorderPart.FooterBottom, padding.Right), borderStyle)); // Right padding
if (!lastColumn)
{
result.Add(new Segment(border.GetPart(BorderPart.FooterBottomSeparator), borderStyle));
}
}
result.Add(new Segment(border.GetPart(BorderPart.FooterBottomRight), borderStyle));
var separator = border.GetColumnRow(TablePart.Bottom, columnWidths, _columns);
result.Add(new Segment(separator, borderStyle));
result.Add(Segment.LineBreak);
}
}