mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 00:42:51 +08:00
Add support for aligning tables
This commit is contained in:
parent
b52056ee49
commit
9afc1ea721
@ -1,4 +1,3 @@
|
||||
using System;
|
||||
using Spectre.Console;
|
||||
|
||||
namespace ColorExample
|
||||
|
@ -1,5 +1,4 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net.Http;
|
||||
using System.Threading.Tasks;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
@ -36,6 +36,7 @@ namespace TableExample
|
||||
.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
|
||||
|
||||
return new Table()
|
||||
.Centered()
|
||||
.SetBorder(TableBorder.DoubleEdge)
|
||||
.SetHeading("TABLE [yellow]HEADING[/]")
|
||||
.SetFootnote("TABLE [yellow]FOOTNOTE[/]")
|
||||
|
@ -34,6 +34,93 @@ namespace Spectre.Console.Tests.Unit
|
||||
console.Lines[10].ShouldBe("└─────┴─────┴─────┴─────┴─────┴─────┴─────┘");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Center_Calendar_Correctly()
|
||||
{
|
||||
// Given
|
||||
var console = new PlainConsole(width: 80);
|
||||
var calendar = new Calendar(2020, 10)
|
||||
.Centered()
|
||||
.AddCalendarEvent(new DateTime(2020, 9, 1))
|
||||
.AddCalendarEvent(new DateTime(2020, 10, 3))
|
||||
.AddCalendarEvent(new DateTime(2020, 10, 12));
|
||||
|
||||
// When
|
||||
console.Render(calendar);
|
||||
|
||||
// Then
|
||||
console.Lines.Count.ShouldBe(11);
|
||||
console.Lines[00].ShouldBe(" 2020 October ");
|
||||
console.Lines[01].ShouldBe(" ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐ ");
|
||||
console.Lines[02].ShouldBe(" │ Sun │ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │ ");
|
||||
console.Lines[03].ShouldBe(" ├─────┼─────┼─────┼─────┼─────┼─────┼─────┤ ");
|
||||
console.Lines[04].ShouldBe(" │ │ │ │ │ 1 │ 2 │ 3* │ ");
|
||||
console.Lines[05].ShouldBe(" │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │ ");
|
||||
console.Lines[06].ShouldBe(" │ 11 │ 12* │ 13 │ 14 │ 15 │ 16 │ 17 │ ");
|
||||
console.Lines[07].ShouldBe(" │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │ ");
|
||||
console.Lines[08].ShouldBe(" │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │ ");
|
||||
console.Lines[09].ShouldBe(" │ │ │ │ │ │ │ │ ");
|
||||
console.Lines[10].ShouldBe(" └─────┴─────┴─────┴─────┴─────┴─────┴─────┘ ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Left_Align_Calendar_Correctly()
|
||||
{
|
||||
// Given
|
||||
var console = new PlainConsole(width: 80);
|
||||
var calendar = new Calendar(2020, 10)
|
||||
.LeftAligned()
|
||||
.AddCalendarEvent(new DateTime(2020, 9, 1))
|
||||
.AddCalendarEvent(new DateTime(2020, 10, 3))
|
||||
.AddCalendarEvent(new DateTime(2020, 10, 12));
|
||||
|
||||
// When
|
||||
console.Render(calendar);
|
||||
|
||||
// Then
|
||||
console.Lines.Count.ShouldBe(11);
|
||||
console.Lines[00].ShouldBe(" 2020 October ");
|
||||
console.Lines[01].ShouldBe("┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐");
|
||||
console.Lines[02].ShouldBe("│ Sun │ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │");
|
||||
console.Lines[03].ShouldBe("├─────┼─────┼─────┼─────┼─────┼─────┼─────┤");
|
||||
console.Lines[04].ShouldBe("│ │ │ │ │ 1 │ 2 │ 3* │");
|
||||
console.Lines[05].ShouldBe("│ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │");
|
||||
console.Lines[06].ShouldBe("│ 11 │ 12* │ 13 │ 14 │ 15 │ 16 │ 17 │");
|
||||
console.Lines[07].ShouldBe("│ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │");
|
||||
console.Lines[08].ShouldBe("│ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │");
|
||||
console.Lines[09].ShouldBe("│ │ │ │ │ │ │ │");
|
||||
console.Lines[10].ShouldBe("└─────┴─────┴─────┴─────┴─────┴─────┴─────┘");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Right_Align_Calendar_Correctly()
|
||||
{
|
||||
// Given
|
||||
var console = new PlainConsole(width: 80);
|
||||
var calendar = new Calendar(2020, 10)
|
||||
.RightAligned()
|
||||
.AddCalendarEvent(new DateTime(2020, 9, 1))
|
||||
.AddCalendarEvent(new DateTime(2020, 10, 3))
|
||||
.AddCalendarEvent(new DateTime(2020, 10, 12));
|
||||
|
||||
// When
|
||||
console.Render(calendar);
|
||||
|
||||
// Then
|
||||
console.Lines.Count.ShouldBe(11);
|
||||
console.Lines[00].ShouldBe(" 2020 October ");
|
||||
console.Lines[01].ShouldBe(" ┌─────┬─────┬─────┬─────┬─────┬─────┬─────┐");
|
||||
console.Lines[02].ShouldBe(" │ Sun │ Mon │ Tue │ Wed │ Thu │ Fri │ Sat │");
|
||||
console.Lines[03].ShouldBe(" ├─────┼─────┼─────┼─────┼─────┼─────┼─────┤");
|
||||
console.Lines[04].ShouldBe(" │ │ │ │ │ 1 │ 2 │ 3* │");
|
||||
console.Lines[05].ShouldBe(" │ 4 │ 5 │ 6 │ 7 │ 8 │ 9 │ 10 │");
|
||||
console.Lines[06].ShouldBe(" │ 11 │ 12* │ 13 │ 14 │ 15 │ 16 │ 17 │");
|
||||
console.Lines[07].ShouldBe(" │ 18 │ 19 │ 20 │ 21 │ 22 │ 23 │ 24 │");
|
||||
console.Lines[08].ShouldBe(" │ 25 │ 26 │ 27 │ 28 │ 29 │ 30 │ 31 │");
|
||||
console.Lines[09].ShouldBe(" │ │ │ │ │ │ │ │");
|
||||
console.Lines[10].ShouldBe(" └─────┴─────┴─────┴─────┴─────┴─────┴─────┘");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Render_Calendar_Correctly_For_Specific_Culture()
|
||||
{
|
||||
|
@ -73,7 +73,7 @@ namespace Spectre.Console.Tests.Unit
|
||||
// When
|
||||
console.Render(new Panel("Hello World")
|
||||
{
|
||||
Header = new Title("Greeting"),
|
||||
Header = new PanelHeader("Greeting"),
|
||||
Expand = true,
|
||||
Padding = new Padding(2, 0, 2, 0),
|
||||
});
|
||||
@ -94,7 +94,7 @@ namespace Spectre.Console.Tests.Unit
|
||||
// When
|
||||
console.Render(new Panel("Hello World")
|
||||
{
|
||||
Header = new Title("Greeting").LeftAligned(),
|
||||
Header = new PanelHeader("Greeting").LeftAligned(),
|
||||
Expand = true,
|
||||
});
|
||||
|
||||
@ -114,7 +114,7 @@ namespace Spectre.Console.Tests.Unit
|
||||
// When
|
||||
console.Render(new Panel("Hello World")
|
||||
{
|
||||
Header = new Title("Greeting").Centered(),
|
||||
Header = new PanelHeader("Greeting").Centered(),
|
||||
Expand = true,
|
||||
});
|
||||
|
||||
@ -134,7 +134,7 @@ namespace Spectre.Console.Tests.Unit
|
||||
// When
|
||||
console.Render(new Panel("Hello World")
|
||||
{
|
||||
Header = new Title("Greeting").RightAligned(),
|
||||
Header = new PanelHeader("Greeting").RightAligned(),
|
||||
Expand = true,
|
||||
});
|
||||
|
||||
@ -154,7 +154,7 @@ namespace Spectre.Console.Tests.Unit
|
||||
// When
|
||||
console.Render(new Panel("Hello World")
|
||||
{
|
||||
Header = new Title("Greeting"),
|
||||
Header = new PanelHeader("Greeting"),
|
||||
Expand = true,
|
||||
});
|
||||
|
||||
@ -244,7 +244,7 @@ namespace Spectre.Console.Tests.Unit
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Justify_Child_To_Right()
|
||||
public void Should_Justify_Child_To_Right_Correctly()
|
||||
{
|
||||
// Given
|
||||
var console = new PlainConsole(width: 25);
|
||||
@ -264,7 +264,7 @@ namespace Spectre.Console.Tests.Unit
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Justify_Child_To_Center()
|
||||
public void Should_Center_Child_Correctly()
|
||||
{
|
||||
// Given
|
||||
var console = new PlainConsole(width: 25);
|
||||
|
@ -168,6 +168,78 @@ namespace Spectre.Console.Tests.Unit
|
||||
console.Lines[5].ShouldBe("└────────┴────────┴───────┘");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Left_Align_Table_Correctly()
|
||||
{
|
||||
// Given
|
||||
var console = new PlainConsole(width: 80);
|
||||
var table = new Table();
|
||||
table.Alignment = Justify.Left;
|
||||
table.AddColumns("Foo", "Bar", "Baz");
|
||||
table.AddRow("Qux", "Corgi", "Waldo");
|
||||
table.AddRow("Grault", "Garply", "Fred");
|
||||
|
||||
// When
|
||||
console.Render(table);
|
||||
|
||||
// Then
|
||||
console.Lines.Count.ShouldBe(6);
|
||||
console.Lines[0].ShouldBe("┌────────┬────────┬───────┐");
|
||||
console.Lines[1].ShouldBe("│ Foo │ Bar │ Baz │");
|
||||
console.Lines[2].ShouldBe("├────────┼────────┼───────┤");
|
||||
console.Lines[3].ShouldBe("│ Qux │ Corgi │ Waldo │");
|
||||
console.Lines[4].ShouldBe("│ Grault │ Garply │ Fred │");
|
||||
console.Lines[5].ShouldBe("└────────┴────────┴───────┘");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Center_Table_Correctly()
|
||||
{
|
||||
// Given
|
||||
var console = new PlainConsole(width: 80);
|
||||
var table = new Table();
|
||||
table.Alignment = Justify.Center;
|
||||
table.AddColumns("Foo", "Bar", "Baz");
|
||||
table.AddRow("Qux", "Corgi", "Waldo");
|
||||
table.AddRow("Grault", "Garply", "Fred");
|
||||
|
||||
// When
|
||||
console.Render(table);
|
||||
|
||||
// Then
|
||||
console.Lines.Count.ShouldBe(6);
|
||||
console.Lines[0].ShouldBe(" ┌────────┬────────┬───────┐ ");
|
||||
console.Lines[1].ShouldBe(" │ Foo │ Bar │ Baz │ ");
|
||||
console.Lines[2].ShouldBe(" ├────────┼────────┼───────┤ ");
|
||||
console.Lines[3].ShouldBe(" │ Qux │ Corgi │ Waldo │ ");
|
||||
console.Lines[4].ShouldBe(" │ Grault │ Garply │ Fred │ ");
|
||||
console.Lines[5].ShouldBe(" └────────┴────────┴───────┘ ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Right_Align_Table_Correctly()
|
||||
{
|
||||
// Given
|
||||
var console = new PlainConsole(width: 80);
|
||||
var table = new Table();
|
||||
table.Alignment = Justify.Right;
|
||||
table.AddColumns("Foo", "Bar", "Baz");
|
||||
table.AddRow("Qux", "Corgi", "Waldo");
|
||||
table.AddRow("Grault", "Garply", "Fred");
|
||||
|
||||
// When
|
||||
console.Render(table);
|
||||
|
||||
// Then
|
||||
console.Lines.Count.ShouldBe(6);
|
||||
console.Lines[0].ShouldBe(" ┌────────┬────────┬───────┐");
|
||||
console.Lines[1].ShouldBe(" │ Foo │ Bar │ Baz │");
|
||||
console.Lines[2].ShouldBe(" ├────────┼────────┼───────┤");
|
||||
console.Lines[3].ShouldBe(" │ Qux │ Corgi │ Waldo │");
|
||||
console.Lines[4].ShouldBe(" │ Grault │ Garply │ Fred │");
|
||||
console.Lines[5].ShouldBe(" └────────┴────────┴───────┘");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Render_Table_Nested_In_Panels_Correctly()
|
||||
{
|
||||
@ -393,8 +465,8 @@ namespace Spectre.Console.Tests.Unit
|
||||
// Given
|
||||
var console = new PlainConsole(width: 80);
|
||||
var table = new Table { Border = TableBorder.Rounded };
|
||||
table.Heading = new Title("Hello World");
|
||||
table.Footnote = new Title("Goodbye World");
|
||||
table.Heading = new TableTitle("Hello World");
|
||||
table.Footnote = new TableTitle("Goodbye World");
|
||||
table.AddColumns("Foo", "Bar", "Baz");
|
||||
table.AddRow("Qux", "Corgi", "Waldo");
|
||||
table.AddRow("Grault", "Garply", "Fred");
|
||||
@ -413,5 +485,89 @@ namespace Spectre.Console.Tests.Unit
|
||||
console.Lines[6].ShouldBe("╰────────┴────────┴───────╯");
|
||||
console.Lines[7].ShouldBe(" Goodbye World ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Left_Align_Table_With_Title_And_Footnote_Correctly()
|
||||
{
|
||||
// Given
|
||||
var console = new PlainConsole(width: 80);
|
||||
var table = new Table { Border = TableBorder.Rounded };
|
||||
table.LeftAligned();
|
||||
table.Heading = new TableTitle("Hello World");
|
||||
table.Footnote = new TableTitle("Goodbye World");
|
||||
table.AddColumns("Foo", "Bar", "Baz");
|
||||
table.AddRow("Qux", "Corgi", "Waldo");
|
||||
table.AddRow("Grault", "Garply", "Fred");
|
||||
|
||||
// When
|
||||
console.Render(table);
|
||||
|
||||
// Then
|
||||
console.Lines.Count.ShouldBe(8);
|
||||
console.Lines[0].ShouldBe(" Hello World ");
|
||||
console.Lines[1].ShouldBe("╭────────┬────────┬───────╮");
|
||||
console.Lines[2].ShouldBe("│ Foo │ Bar │ Baz │");
|
||||
console.Lines[3].ShouldBe("├────────┼────────┼───────┤");
|
||||
console.Lines[4].ShouldBe("│ Qux │ Corgi │ Waldo │");
|
||||
console.Lines[5].ShouldBe("│ Grault │ Garply │ Fred │");
|
||||
console.Lines[6].ShouldBe("╰────────┴────────┴───────╯");
|
||||
console.Lines[7].ShouldBe(" Goodbye World ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Center_Table_With_Title_And_Footnote_Correctly()
|
||||
{
|
||||
// Given
|
||||
var console = new PlainConsole(width: 80);
|
||||
var table = new Table { Border = TableBorder.Rounded };
|
||||
table.Centered();
|
||||
table.Heading = new TableTitle("Hello World");
|
||||
table.Footnote = new TableTitle("Goodbye World");
|
||||
table.AddColumns("Foo", "Bar", "Baz");
|
||||
table.AddRow("Qux", "Corgi", "Waldo");
|
||||
table.AddRow("Grault", "Garply", "Fred");
|
||||
|
||||
// When
|
||||
console.Render(table);
|
||||
|
||||
// Then
|
||||
console.Lines.Count.ShouldBe(8);
|
||||
console.Lines[0].ShouldBe(" Hello World ");
|
||||
console.Lines[1].ShouldBe(" ╭────────┬────────┬───────╮ ");
|
||||
console.Lines[2].ShouldBe(" │ Foo │ Bar │ Baz │ ");
|
||||
console.Lines[3].ShouldBe(" ├────────┼────────┼───────┤ ");
|
||||
console.Lines[4].ShouldBe(" │ Qux │ Corgi │ Waldo │ ");
|
||||
console.Lines[5].ShouldBe(" │ Grault │ Garply │ Fred │ ");
|
||||
console.Lines[6].ShouldBe(" ╰────────┴────────┴───────╯ ");
|
||||
console.Lines[7].ShouldBe(" Goodbye World ");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Should_Right_Align_Table_With_Title_And_Footnote_Correctly()
|
||||
{
|
||||
// Given
|
||||
var console = new PlainConsole(width: 80);
|
||||
var table = new Table { Border = TableBorder.Rounded };
|
||||
table.RightAligned();
|
||||
table.Heading = new TableTitle("Hello World");
|
||||
table.Footnote = new TableTitle("Goodbye World");
|
||||
table.AddColumns("Foo", "Bar", "Baz");
|
||||
table.AddRow("Qux", "Corgi", "Waldo");
|
||||
table.AddRow("Grault", "Garply", "Fred");
|
||||
|
||||
// When
|
||||
console.Render(table);
|
||||
|
||||
// Then
|
||||
console.Lines.Count.ShouldBe(8);
|
||||
console.Lines[0].ShouldBe(" Hello World ");
|
||||
console.Lines[1].ShouldBe(" ╭────────┬────────┬───────╮");
|
||||
console.Lines[2].ShouldBe(" │ Foo │ Bar │ Baz │");
|
||||
console.Lines[3].ShouldBe(" ├────────┼────────┼───────┤");
|
||||
console.Lines[4].ShouldBe(" │ Qux │ Corgi │ Waldo │");
|
||||
console.Lines[5].ShouldBe(" │ Grault │ Garply │ Fred │");
|
||||
console.Lines[6].ShouldBe(" ╰────────┴────────┴───────╯");
|
||||
console.Lines[7].ShouldBe(" Goodbye World ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ namespace Spectre.Console
|
||||
throw new ArgumentNullException(nameof(text));
|
||||
}
|
||||
|
||||
return SetHeader(panel, new Title(text, style, alignment));
|
||||
return SetHeader(panel, new PanelHeader(text, style, alignment));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -36,7 +36,7 @@ namespace Spectre.Console
|
||||
/// <param name="panel">The panel.</param>
|
||||
/// <param name="header">The header to use.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Panel SetHeader(this Panel panel, Title header)
|
||||
public static Panel SetHeader(this Panel panel, PanelHeader header)
|
||||
{
|
||||
if (panel is null)
|
||||
{
|
||||
|
@ -183,9 +183,8 @@ namespace Spectre.Console
|
||||
/// <param name="table">The table.</param>
|
||||
/// <param name="text">The heading.</param>
|
||||
/// <param name="style">The style.</param>
|
||||
/// <param name="alignment">The alignment.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Table SetHeading(this Table table, string text, Style? style = null, Justify? alignment = null)
|
||||
public static Table SetHeading(this Table table, string text, Style? style = null)
|
||||
{
|
||||
if (table is null)
|
||||
{
|
||||
@ -197,7 +196,7 @@ namespace Spectre.Console
|
||||
throw new ArgumentNullException(nameof(text));
|
||||
}
|
||||
|
||||
return SetHeading(table, new Title(text, style, alignment));
|
||||
return SetHeading(table, new TableTitle(text, style));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -206,7 +205,7 @@ namespace Spectre.Console
|
||||
/// <param name="table">The table.</param>
|
||||
/// <param name="heading">The heading.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Table SetHeading(this Table table, Title heading)
|
||||
public static Table SetHeading(this Table table, TableTitle heading)
|
||||
{
|
||||
if (table is null)
|
||||
{
|
||||
@ -223,9 +222,8 @@ namespace Spectre.Console
|
||||
/// <param name="table">The table.</param>
|
||||
/// <param name="text">The footnote.</param>
|
||||
/// <param name="style">The style.</param>
|
||||
/// <param name="alignment">The alignment.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Table SetFootnote(this Table table, string text, Style? style = null, Justify? alignment = null)
|
||||
public static Table SetFootnote(this Table table, string text, Style? style = null)
|
||||
{
|
||||
if (table is null)
|
||||
{
|
||||
@ -237,7 +235,7 @@ namespace Spectre.Console
|
||||
throw new ArgumentNullException(nameof(text));
|
||||
}
|
||||
|
||||
return SetFootnote(table, new Title(text, style, alignment));
|
||||
return SetFootnote(table, new TableTitle(text, style));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -246,7 +244,7 @@ namespace Spectre.Console
|
||||
/// <param name="table">The table.</param>
|
||||
/// <param name="footnote">The footnote.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public static Table SetFootnote(this Table table, Title footnote)
|
||||
public static Table SetFootnote(this Table table, TableTitle footnote)
|
||||
{
|
||||
if (table is null)
|
||||
{
|
||||
|
97
src/Spectre.Console/Internal/Aligner.cs
Normal file
97
src/Spectre.Console/Internal/Aligner.cs
Normal file
@ -0,0 +1,97 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console.Internal
|
||||
{
|
||||
internal static class Aligner
|
||||
{
|
||||
public static string Align(RenderContext context, string text, Justify? alignment, int maxWidth)
|
||||
{
|
||||
if (alignment == null || alignment == Justify.Left)
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
var width = Cell.GetCellLength(context, text);
|
||||
if (width >= maxWidth)
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
switch (alignment)
|
||||
{
|
||||
case Justify.Right:
|
||||
{
|
||||
var diff = maxWidth - width;
|
||||
return new string(' ', diff) + text;
|
||||
}
|
||||
|
||||
case Justify.Center:
|
||||
{
|
||||
var diff = (maxWidth - width) / 2;
|
||||
|
||||
var left = new string(' ', diff);
|
||||
var right = new string(' ', diff);
|
||||
|
||||
// Right side
|
||||
var remainder = (maxWidth - width) % 2;
|
||||
if (remainder != 0)
|
||||
{
|
||||
right += new string(' ', remainder);
|
||||
}
|
||||
|
||||
return left + text + right;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("Unknown alignment");
|
||||
}
|
||||
}
|
||||
|
||||
public static void Align<T>(RenderContext context, T segments, Justify? alignment, int maxWidth)
|
||||
where T : List<Segment>
|
||||
{
|
||||
if (alignment == null || alignment == Justify.Left)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var width = Segment.CellLength(context, segments);
|
||||
if (width >= maxWidth)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
switch (alignment)
|
||||
{
|
||||
case Justify.Right:
|
||||
{
|
||||
var diff = maxWidth - width;
|
||||
segments.Insert(0, new Segment(new string(' ', diff)));
|
||||
break;
|
||||
}
|
||||
|
||||
case Justify.Center:
|
||||
{
|
||||
// Left side.
|
||||
var diff = (maxWidth - width) / 2;
|
||||
segments.Insert(0, new Segment(new string(' ', diff)));
|
||||
|
||||
// Right side
|
||||
segments.Add(new Segment(new string(' ', diff)));
|
||||
var remainder = (maxWidth - width) % 2;
|
||||
if (remainder != 0)
|
||||
{
|
||||
segments.Add(new Segment(new string(' ', remainder)));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("Unknown alignment");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using Spectre.Console.Internal;
|
||||
@ -68,12 +67,7 @@ namespace Spectre.Console.Rendering
|
||||
|
||||
private Segment(string text, Style style, bool lineBreak)
|
||||
{
|
||||
if (text is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(text));
|
||||
}
|
||||
|
||||
Text = text.NormalizeLineEndings();
|
||||
Text = text?.NormalizeLineEndings() ?? throw new ArgumentNullException(nameof(text));
|
||||
Style = style ?? throw new ArgumentNullException(nameof(style));
|
||||
IsLineBreak = lineBreak;
|
||||
IsWhiteSpace = string.IsNullOrWhiteSpace(text);
|
||||
@ -121,6 +115,15 @@ namespace Spectre.Console.Rendering
|
||||
new Segment(Text.Substring(offset, Text.Length - offset), Style));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clones the segment.
|
||||
/// </summary>
|
||||
/// <returns>A new segment that's identical to this one.</returns>
|
||||
public Segment Clone()
|
||||
{
|
||||
return new Segment(Text, Style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the number of cells that the segments occupies in the console.
|
||||
/// </summary>
|
||||
@ -238,48 +241,6 @@ namespace Spectre.Console.Rendering
|
||||
return lines;
|
||||
}
|
||||
|
||||
internal static IEnumerable<Segment> Merge(IEnumerable<Segment> segments)
|
||||
{
|
||||
var result = new List<Segment>();
|
||||
|
||||
var previous = (Segment?)null;
|
||||
foreach (var segment in segments)
|
||||
{
|
||||
if (previous == null)
|
||||
{
|
||||
previous = segment;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Same style?
|
||||
if (previous.Style.Equals(segment.Style) && !previous.IsLineBreak)
|
||||
{
|
||||
previous = new Segment(previous.Text + segment.Text, previous.Style);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(previous);
|
||||
previous = segment;
|
||||
}
|
||||
}
|
||||
|
||||
if (previous != null)
|
||||
{
|
||||
result.Add(previous);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clones the segment.
|
||||
/// </summary>
|
||||
/// <returns>A new segment that's identical to this one.</returns>
|
||||
public Segment Clone()
|
||||
{
|
||||
return new Segment(Text, Style);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits an overflowing segment into several new segments.
|
||||
/// </summary>
|
||||
@ -436,6 +397,39 @@ namespace Spectre.Console.Rendering
|
||||
return new Segment(builder.ToString(), segment.Style);
|
||||
}
|
||||
|
||||
internal static IEnumerable<Segment> Merge(IEnumerable<Segment> segments)
|
||||
{
|
||||
var result = new List<Segment>();
|
||||
|
||||
var previous = (Segment?)null;
|
||||
foreach (var segment in segments)
|
||||
{
|
||||
if (previous == null)
|
||||
{
|
||||
previous = segment;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Same style?
|
||||
if (previous.Style.Equals(segment.Style) && !previous.IsLineBreak)
|
||||
{
|
||||
previous = new Segment(previous.Text + segment.Text, previous.Style);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(previous);
|
||||
previous = segment;
|
||||
}
|
||||
}
|
||||
|
||||
if (previous != null)
|
||||
{
|
||||
result.Add(previous);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
internal static Segment TruncateWithEllipsis(string text, Style style, RenderContext context, int maxWidth)
|
||||
{
|
||||
return SplitOverflow(
|
||||
|
@ -11,7 +11,7 @@ namespace Spectre.Console
|
||||
/// <summary>
|
||||
/// A renderable calendar.
|
||||
/// </summary>
|
||||
public sealed class Calendar : Renderable, IHasCulture, IHasTableBorder
|
||||
public sealed class Calendar : Renderable, IHasCulture, IHasTableBorder, IAlignable
|
||||
{
|
||||
private const int NumberOfWeekDays = 7;
|
||||
private const int ExpectedRowCount = 6;
|
||||
@ -30,6 +30,7 @@ namespace Spectre.Console
|
||||
private Style _highlightStyle;
|
||||
private bool _showHeader;
|
||||
private Style? _headerStyle;
|
||||
private Justify? _alignment;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the calendar year.
|
||||
@ -115,6 +116,13 @@ namespace Spectre.Console
|
||||
set => MarkAsDirty(() => _headerStyle = value);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Justify? Alignment
|
||||
{
|
||||
get => _alignment;
|
||||
set => MarkAsDirty(() => _alignment = value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list containing all calendar events.
|
||||
/// </summary>
|
||||
@ -195,12 +203,13 @@ namespace Spectre.Console
|
||||
Border = _border,
|
||||
UseSafeBorder = _useSafeBorder,
|
||||
BorderStyle = _borderStyle,
|
||||
Alignment = _alignment,
|
||||
};
|
||||
|
||||
if (ShowHeader)
|
||||
{
|
||||
var heading = new DateTime(Year, Month, Day).ToString("Y", culture).EscapeMarkup();
|
||||
table.Heading = new Title(heading, HeaderStyle);
|
||||
table.Heading = new TableTitle(heading, HeaderStyle);
|
||||
}
|
||||
|
||||
// Add columns
|
||||
|
@ -7,7 +7,7 @@ namespace Spectre.Console
|
||||
/// <summary>
|
||||
/// A renderable grid.
|
||||
/// </summary>
|
||||
public sealed class Grid : Renderable, IExpandable
|
||||
public sealed class Grid : Renderable, IExpandable, IAlignable
|
||||
{
|
||||
private readonly Table _table;
|
||||
|
||||
@ -28,6 +28,13 @@ namespace Spectre.Console
|
||||
set => _table.Expand = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Justify? Alignment
|
||||
{
|
||||
get => _table.Alignment;
|
||||
set => _table.Alignment = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Grid"/> class.
|
||||
/// </summary>
|
||||
|
@ -39,7 +39,7 @@ namespace Spectre.Console
|
||||
/// <summary>
|
||||
/// Gets or sets the header.
|
||||
/// </summary>
|
||||
public Title? Header { get; set; }
|
||||
public PanelHeader? Header { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Panel"/> class.
|
||||
|
76
src/Spectre.Console/Widgets/PanelHeader.cs
Normal file
76
src/Spectre.Console/Widgets/PanelHeader.cs
Normal file
@ -0,0 +1,76 @@
|
||||
using System;
|
||||
|
||||
namespace Spectre.Console
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a panel header.
|
||||
/// </summary>
|
||||
public sealed class PanelHeader : IAlignable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the panel header text.
|
||||
/// </summary>
|
||||
public string Text { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the panel header style.
|
||||
/// </summary>
|
||||
public Style? Style { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the panel header alignment.
|
||||
/// </summary>
|
||||
public Justify? Alignment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PanelHeader"/> class.
|
||||
/// </summary>
|
||||
/// <param name="text">The panel header text.</param>
|
||||
/// <param name="style">The panel header style.</param>
|
||||
/// <param name="alignment">The panel header alignment.</param>
|
||||
public PanelHeader(string text, Style? style = null, Justify? alignment = null)
|
||||
{
|
||||
Text = text ?? throw new ArgumentNullException(nameof(text));
|
||||
Style = style;
|
||||
Alignment = alignment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the panel header style.
|
||||
/// </summary>
|
||||
/// <param name="style">The panel header style.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public PanelHeader SetStyle(Style? style)
|
||||
{
|
||||
Style = style ?? Style.Plain;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the panel header style.
|
||||
/// </summary>
|
||||
/// <param name="style">The panel header style.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public PanelHeader SetStyle(string style)
|
||||
{
|
||||
if (style is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(style));
|
||||
}
|
||||
|
||||
Style = Style.Parse(style);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the panel header alignment.
|
||||
/// </summary>
|
||||
/// <param name="alignment">The panel header alignment.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public PanelHeader SetAlignment(Justify alignment)
|
||||
{
|
||||
Alignment = alignment;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
@ -144,37 +144,18 @@ namespace Spectre.Console
|
||||
|
||||
// Justify lines
|
||||
var justification = context.Justification ?? Alignment ?? Justify.Left;
|
||||
foreach (var (_, _, last, line) in lines.Enumerate())
|
||||
if (justification != Justify.Left)
|
||||
{
|
||||
var length = line.Sum(l => l.StripLineEndings().CellLength(context));
|
||||
if (length < maxWidth)
|
||||
foreach (var line in lines)
|
||||
{
|
||||
// Justify right side
|
||||
if (justification == Justify.Right)
|
||||
{
|
||||
var diff = maxWidth - length;
|
||||
line.Prepend(new Segment(new string(' ', diff)));
|
||||
}
|
||||
else if (justification == Justify.Center)
|
||||
{
|
||||
// Left side.
|
||||
var diff = (maxWidth - length) / 2;
|
||||
line.Prepend(new Segment(new string(' ', diff)));
|
||||
|
||||
// Right side
|
||||
line.Add(new Segment(new string(' ', diff)));
|
||||
var remainder = (maxWidth - length) % 2;
|
||||
if (remainder != 0)
|
||||
{
|
||||
line.Add(new Segment(new string(' ', remainder)));
|
||||
}
|
||||
}
|
||||
Aligner.Align(context, line, justification, maxWidth);
|
||||
}
|
||||
}
|
||||
|
||||
if (context.SingleLine)
|
||||
{
|
||||
return lines.First().Where(segment => !segment.IsLineBreak);
|
||||
// Return the first line
|
||||
return lines[0].Where(segment => !segment.IsLineBreak);
|
||||
}
|
||||
|
||||
return new SegmentLineEnumerator(lines);
|
||||
|
@ -9,7 +9,7 @@ namespace Spectre.Console
|
||||
/// <summary>
|
||||
/// A renderable table.
|
||||
/// </summary>
|
||||
public sealed class Table : Renderable, IHasTableBorder, IExpandable
|
||||
public sealed class Table : Renderable, IHasTableBorder, IExpandable, IAlignable
|
||||
{
|
||||
private const int EdgeCount = 2;
|
||||
|
||||
@ -58,12 +58,15 @@ namespace Spectre.Console
|
||||
/// <summary>
|
||||
/// Gets or sets the table title.
|
||||
/// </summary>
|
||||
public Title? Heading { get; set; }
|
||||
public TableTitle? Heading { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the table footnote.
|
||||
/// </summary>
|
||||
public Title? Footnote { get; set; }
|
||||
public TableTitle? Footnote { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Justify? Alignment { get; set; }
|
||||
|
||||
// Whether this is a grid or not.
|
||||
internal bool IsGrid { get; set; }
|
||||
@ -200,7 +203,7 @@ namespace Spectre.Console
|
||||
rows.AddRange(_rows);
|
||||
|
||||
var result = new List<Segment>();
|
||||
result.AddRange(RenderAnnotation(context, Heading, tableWidth, _defaultHeadingStyle));
|
||||
result.AddRange(RenderAnnotation(context, Heading, actualMaxWidth, tableWidth, _defaultHeadingStyle));
|
||||
|
||||
// Iterate all rows
|
||||
foreach (var (index, firstRow, lastRow, row) in rows.Enumerate())
|
||||
@ -222,7 +225,7 @@ namespace Spectre.Console
|
||||
// Show top of header?
|
||||
if (firstRow && showBorder)
|
||||
{
|
||||
var separator = border.GetColumnRow(TablePart.Top, columnWidths, _columns);
|
||||
var separator = Aligner.Align(context, border.GetColumnRow(TablePart.Top, columnWidths, _columns), Alignment, actualMaxWidth);
|
||||
result.Add(new Segment(separator, borderStyle));
|
||||
result.Add(Segment.LineBreak);
|
||||
}
|
||||
@ -288,6 +291,9 @@ namespace Spectre.Console
|
||||
}
|
||||
}
|
||||
|
||||
// Align the row result.
|
||||
Aligner.Align(context, rowResult, Alignment, actualMaxWidth);
|
||||
|
||||
// Is the row larger than the allowed max width?
|
||||
if (Segment.CellLength(context, rowResult) > actualMaxWidth)
|
||||
{
|
||||
@ -304,7 +310,7 @@ namespace Spectre.Console
|
||||
// Show header separator?
|
||||
if (firstRow && showBorder && ShowHeaders && hasRows)
|
||||
{
|
||||
var separator = border.GetColumnRow(TablePart.Separator, columnWidths, _columns);
|
||||
var separator = Aligner.Align(context, border.GetColumnRow(TablePart.Separator, columnWidths, _columns), Alignment, actualMaxWidth);
|
||||
result.Add(new Segment(separator, borderStyle));
|
||||
result.Add(Segment.LineBreak);
|
||||
}
|
||||
@ -312,13 +318,13 @@ namespace Spectre.Console
|
||||
// Show bottom of footer?
|
||||
if (lastRow && showBorder)
|
||||
{
|
||||
var separator = border.GetColumnRow(TablePart.Bottom, columnWidths, _columns);
|
||||
var separator = Aligner.Align(context, border.GetColumnRow(TablePart.Bottom, columnWidths, _columns), Alignment, actualMaxWidth);
|
||||
result.Add(new Segment(separator, borderStyle));
|
||||
result.Add(Segment.LineBreak);
|
||||
}
|
||||
}
|
||||
|
||||
result.AddRange(RenderAnnotation(context, Footnote, tableWidth, _defaultFootnoteStyle));
|
||||
result.AddRange(RenderAnnotation(context, Footnote, actualMaxWidth, tableWidth, _defaultFootnoteStyle));
|
||||
|
||||
return result;
|
||||
}
|
||||
@ -392,25 +398,27 @@ namespace Spectre.Console
|
||||
return widths;
|
||||
}
|
||||
|
||||
private static IEnumerable<Segment> RenderAnnotation(
|
||||
RenderContext context, Title? header,
|
||||
int maxWidth, Style defaultStyle)
|
||||
private IEnumerable<Segment> RenderAnnotation(
|
||||
RenderContext context, TableTitle? header,
|
||||
int maxWidth, int tableWidth, Style defaultStyle)
|
||||
{
|
||||
if (header == null)
|
||||
{
|
||||
yield break;
|
||||
return Array.Empty<Segment>();
|
||||
}
|
||||
|
||||
var paragraph = new Markup(header.Text.Capitalize(), header.Style ?? defaultStyle)
|
||||
.SetAlignment(header.Alignment ?? Justify.Center)
|
||||
.SetAlignment(Justify.Center)
|
||||
.SetOverflow(Overflow.Ellipsis);
|
||||
|
||||
foreach (var segment in ((IRenderable)paragraph).Render(context, maxWidth))
|
||||
{
|
||||
yield return segment;
|
||||
}
|
||||
var items = new List<Segment>();
|
||||
items.AddRange(((IRenderable)paragraph).Render(context, tableWidth));
|
||||
|
||||
yield return Segment.LineBreak;
|
||||
// Align over the whole buffer area
|
||||
Aligner.Align(context, items, Alignment, maxWidth);
|
||||
|
||||
items.Add(Segment.LineBreak);
|
||||
return items;
|
||||
}
|
||||
|
||||
private (int Min, int Max) MeasureColumn(TableColumn column, RenderContext options, int maxWidth)
|
||||
|
@ -3,9 +3,9 @@ using System;
|
||||
namespace Spectre.Console
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a title.
|
||||
/// Represents a table title such as a heading or footnote.
|
||||
/// </summary>
|
||||
public sealed class Title : IAlignable
|
||||
public sealed class TableTitle
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the title text.
|
||||
@ -18,21 +18,14 @@ namespace Spectre.Console
|
||||
public Style? Style { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title alignment.
|
||||
/// </summary>
|
||||
public Justify? Alignment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Title"/> class.
|
||||
/// Initializes a new instance of the <see cref="TableTitle"/> class.
|
||||
/// </summary>
|
||||
/// <param name="text">The title text.</param>
|
||||
/// <param name="style">The title style.</param>
|
||||
/// <param name="alignment">The title alignment.</param>
|
||||
public Title(string text, Style? style = null, Justify? alignment = null)
|
||||
public TableTitle(string text, Style? style = null)
|
||||
{
|
||||
Text = text ?? throw new ArgumentNullException(nameof(text));
|
||||
Style = style;
|
||||
Alignment = alignment;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -40,20 +33,25 @@ namespace Spectre.Console
|
||||
/// </summary>
|
||||
/// <param name="style">The title style.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public Title SetStyle(Style? style)
|
||||
public TableTitle SetStyle(Style? style)
|
||||
{
|
||||
Style = style ?? Style.Plain;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the title alignment.
|
||||
/// Sets the title style.
|
||||
/// </summary>
|
||||
/// <param name="alignment">The title alignment.</param>
|
||||
/// <param name="style">The title style.</param>
|
||||
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||
public Title SetAlignment(Justify alignment)
|
||||
public TableTitle SetStyle(string style)
|
||||
{
|
||||
Alignment = alignment;
|
||||
if (style is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(style));
|
||||
}
|
||||
|
||||
Style = Style.Parse(style);
|
||||
return this;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user