diff --git a/examples/Tables/Program.cs b/examples/Tables/Program.cs
index 495b21a..539d673 100644
--- a/examples/Tables/Program.cs
+++ b/examples/Tables/Program.cs
@@ -19,7 +19,7 @@ namespace TableExample
private static void RenderSimpleTable()
{
- // Create the table.
+ // Create the table
var table = new Table();
table.AddColumn(new TableColumn("[u]Foo[/]"));
table.AddColumn(new TableColumn("[u]Bar[/]"));
@@ -35,7 +35,7 @@ namespace TableExample
private static void RenderBigTable()
{
- // Create the table.
+ // Create the table
var table = new Table().SetBorder(TableBorder.Rounded);
table.AddColumn("[red underline]Foo[/]");
table.AddColumn(new TableColumn("[blue]Bar[/]") { Alignment = Justify.Right, NoWrap = true });
@@ -57,16 +57,16 @@ namespace TableExample
private static void RenderNestedTable()
{
- // Create simple table.
+ // Create simple table
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[/]"));
+ simple.AddColumn(new TableColumn("[u]CDE[/]").Centered());
+ simple.AddColumn(new TableColumn("[u]FED[/]"));
+ simple.AddColumn(new TableColumn("[u]IHG[/]"));
simple.AddRow("Hello", "[red]World![/]", "");
simple.AddRow("[blue]Bonjour[/]", "[white]le[/]", "[red]monde![/]");
simple.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
- // Create other table.
+ // Create other table
var second = new Table().SetBorder(TableBorder.Square).SetBorderColor(Color.Green);
second.AddColumn(new TableColumn("[u]Foo[/]"));
second.AddColumn(new TableColumn("[u]Bar[/]"));
@@ -75,12 +75,11 @@ namespace TableExample
second.AddRow(simple, new Text("Whaaat"), new Text("Lolz"));
second.AddRow("[blue]Hej[/]", "[yellow]Världen![/]", "");
+ // Create the outer most table
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)));
-
- // Add some rows
+ table.AddColumn(new TableColumn(new Panel("[u]ABC[/]").SetBorderColor(Color.Red)));
+ table.AddColumn(new TableColumn(new Panel("[u]DEF[/]").SetBorderColor(Color.Green)));
+ table.AddColumn(new TableColumn(new Panel("[u]GHI[/]").SetBorderColor(Color.Blue)));
table.AddRow(new Text("Hello").Centered(), new Markup("[red]World![/]"), Text.Empty);
table.AddRow(second, new Text("Whaaat"), new Text("Lol"));
table.AddRow(new Markup("[blue]Hej[/]").Centered(), new Markup("[yellow]Världen![/]"), Text.Empty);
diff --git a/src/Spectre.Console.Tests/Unit/TableTests.cs b/src/Spectre.Console.Tests/Unit/TableTests.cs
index fa3cca2..662c316 100644
--- a/src/Spectre.Console.Tests/Unit/TableTests.cs
+++ b/src/Spectre.Console.Tests/Unit/TableTests.cs
@@ -383,5 +383,54 @@ namespace Spectre.Console.Tests.Unit
console.Lines[1].ShouldBe("│ Foo │ Bar │ Baz │");
console.Lines[2].ShouldBe("└─────┴─────┴────────┘");
}
+
+ [Fact]
+ public void Should_Not_Draw_Tables_That_Are_Impossible_To_Draw()
+ {
+ // Given
+ var console = new PlainConsole(width: 25);
+
+ var first = new Table().SetBorder(TableBorder.Rounded).SetBorderColor(Color.Red);
+ first.AddColumn(new TableColumn("[u]PS1[/]").Centered());
+ first.AddColumn(new TableColumn("[u]PS2[/]"));
+ first.AddColumn(new TableColumn("[u]PS3[/]"));
+ first.AddRow("Hello", "[red]World[/]", string.Empty);
+ first.AddRow("[blue]Bonjour[/]", "[white]le[/]", "[red]monde![/]");
+ first.AddRow("[blue]Hej[/]", "[yellow]Världen[/]", string.Empty);
+
+ 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[/]"));
+ second.AddRow("Hello", "[red]World[/]", string.Empty);
+ second.AddRow(first, new Text("Whaaat"), new Text("Lolz"));
+ second.AddRow("[blue]Hej[/]", "[yellow]Världen[/]", string.Empty);
+
+ var table = new Table().SetBorder(TableBorder.Rounded);
+ table.AddColumn(new TableColumn(new Panel("[u]ABC[/]").SetBorderColor(Color.Red)));
+ table.AddColumn(new TableColumn(new Panel("[u]DEF[/]").SetBorderColor(Color.Green)));
+ table.AddColumn(new TableColumn(new Panel("[u]GHI[/]").SetBorderColor(Color.Blue)));
+ table.AddRow(new Text("Hello").Centered(), new Markup("[red]World[/]"), Text.Empty);
+ table.AddRow(second, new Text("Whaat"), new Text("Lol").RightAligned());
+ table.AddRow(new Markup("[blue]Hej[/]"), new Markup("[yellow]Världen[/]"), Text.Empty);
+
+ // When
+ console.Render(table);
+
+ // Then
+ console.Lines.Count.ShouldBe(12);
+ console.Lines[00].ShouldBe("╭───────┬───────┬───────╮");
+ console.Lines[01].ShouldBe("│ ┌───┐ │ ┌───┐ │ ┌───┐ │");
+ console.Lines[02].ShouldBe("│ │ A │ │ │ D │ │ │ G │ │");
+ console.Lines[03].ShouldBe("│ │ B │ │ │ E │ │ │ H │ │");
+ console.Lines[04].ShouldBe("│ │ C │ │ │ F │ │ │ I │ │");
+ console.Lines[05].ShouldBe("│ └───┘ │ └───┘ │ └───┘ │");
+ console.Lines[06].ShouldBe("├───────┼───────┼───────┤");
+ console.Lines[07].ShouldBe("│ Hello │ World │ │");
+ console.Lines[08].ShouldBe("│ … │ Whaat │ Lol │");
+ console.Lines[09].ShouldBe("│ Hej │ Värld │ │");
+ console.Lines[10].ShouldBe("│ │ en │ │");
+ console.Lines[11].ShouldBe("╰───────┴───────┴───────╯");
+ }
}
}
diff --git a/src/Spectre.Console/Rendering/Segment.cs b/src/Spectre.Console/Rendering/Segment.cs
index aa74d5e..0bcd925 100644
--- a/src/Spectre.Console/Rendering/Segment.cs
+++ b/src/Spectre.Console/Rendering/Segment.cs
@@ -119,6 +119,54 @@ namespace Spectre.Console.Rendering
new Segment(Text.Substring(offset, Text.Length - offset), Style));
}
+ ///
+ /// Gets the number of cells that the segments occupies in the console.
+ ///
+ /// The render context.
+ /// The segments to measure.
+ /// The number of cells that the segments occupies in the console.
+ public static int CellLength(RenderContext context, List segments)
+ {
+ return segments.Sum(segment => segment.CellLength(context));
+ }
+
+ ///
+ /// Truncates the segments to the specified width.
+ ///
+ /// The render context.
+ /// The segments to truncate.
+ /// The maximum width that the segments may occupy.
+ /// A list of segments that has been truncated.
+ public static List Truncate(RenderContext context, IEnumerable segments, int maxWidth)
+ {
+ if (context is null)
+ {
+ throw new ArgumentNullException(nameof(context));
+ }
+
+ if (segments is null)
+ {
+ throw new ArgumentNullException(nameof(segments));
+ }
+
+ var result = new List();
+
+ var totalWidth = 0;
+ foreach (var segment in segments)
+ {
+ var segmentWidth = segment.CellLength(context);
+ if (totalWidth + segmentWidth > maxWidth)
+ {
+ break;
+ }
+
+ result.Add(segment);
+ totalWidth += segmentWidth;
+ }
+
+ return result;
+ }
+
///
/// Splits the provided segments into lines.
///
@@ -315,11 +363,25 @@ namespace Spectre.Console.Rendering
}
else if (overflow == Overflow.Crop)
{
- result.Add(new Segment(segment.Text.Substring(0, width), segment.Style));
+ if (Math.Max(0, width - 1) == 0)
+ {
+ result.Add(new Segment(string.Empty, segment.Style));
+ }
+ else
+ {
+ result.Add(new Segment(segment.Text.Substring(0, width), segment.Style));
+ }
}
else if (overflow == Overflow.Ellipsis)
{
- result.Add(new Segment(segment.Text.Substring(0, width - 1) + "…", segment.Style));
+ if (Math.Max(0, width - 1) == 0)
+ {
+ result.Add(new Segment("…", segment.Style));
+ }
+ else
+ {
+ result.Add(new Segment(segment.Text.Substring(0, width - 1) + "…", segment.Style));
+ }
}
return result;
diff --git a/src/Spectre.Console/Widgets/Paragraph.cs b/src/Spectre.Console/Widgets/Paragraph.cs
index de4bbf9..f8efbbd 100644
--- a/src/Spectre.Console/Widgets/Paragraph.cs
+++ b/src/Spectre.Console/Widgets/Paragraph.cs
@@ -193,6 +193,12 @@ namespace Spectre.Console
private List SplitLines(RenderContext context, int maxWidth)
{
+ if (maxWidth <= 0)
+ {
+ // Nothing fits, so return an empty line.
+ return new List();
+ }
+
if (_lines.Max(x => x.CellWidth(context)) <= maxWidth)
{
return Clone();
diff --git a/src/Spectre.Console/Widgets/Table.cs b/src/Spectre.Console/Widgets/Table.cs
index 673e4fe..b7291c0 100644
--- a/src/Spectre.Console/Widgets/Table.cs
+++ b/src/Spectre.Console/Widgets/Table.cs
@@ -153,6 +153,7 @@ namespace Spectre.Console
var borderStyle = BorderStyle ?? Style.Plain;
var tableWidth = maxWidth;
+ var actualMaxWidth = maxWidth;
var showBorder = Border.Visible;
var hideBorder = !Border.Visible;
@@ -170,6 +171,10 @@ namespace Spectre.Console
// Update the table width.
tableWidth = columnWidths.Sum() + GetExtraWidth(includePadding: true);
+ if (tableWidth <= 0 || tableWidth > actualMaxWidth || columnWidths.Any(c => c <= 0))
+ {
+ return new List(new[] { new Segment("…", BorderStyle ?? Style.Plain) });
+ }
var rows = new List>();
if (ShowHeaders)
@@ -213,13 +218,15 @@ namespace Spectre.Console
// Iterate through each cell row
foreach (var cellRowIndex in Enumerable.Range(0, cellHeight))
{
+ var rowResult = new List();
+
foreach (var (cellIndex, firstCell, lastCell, cell) in cells.Enumerate())
{
if (firstCell && showBorder)
{
// Show left column edge
var part = firstRow && ShowHeaders ? TableBorderPart.HeaderLeft : TableBorderPart.CellLeft;
- result.Add(new Segment(border.GetPart(part), borderStyle));
+ rowResult.Add(new Segment(border.GetPart(part), borderStyle));
}
// Pad column on left side.
@@ -228,18 +235,18 @@ namespace Spectre.Console
var leftPadding = _columns[cellIndex].Padding.Left;
if (leftPadding > 0)
{
- result.Add(new Segment(new string(' ', leftPadding)));
+ rowResult.Add(new Segment(new string(' ', leftPadding)));
}
}
// Add content
- result.AddRange(cell[cellRowIndex]);
+ rowResult.AddRange(cell[cellRowIndex]);
// Pad cell content right
var length = cell[cellRowIndex].Sum(segment => segment.CellLength(context));
if (length < columnWidths[cellIndex])
{
- result.Add(new Segment(new string(' ', columnWidths[cellIndex] - length)));
+ rowResult.Add(new Segment(new string(' ', columnWidths[cellIndex] - length)));
}
// Pad column on the right side
@@ -248,7 +255,7 @@ namespace Spectre.Console
var rightPadding = _columns[cellIndex].Padding.Right;
if (rightPadding > 0)
{
- result.Add(new Segment(new string(' ', rightPadding)));
+ rowResult.Add(new Segment(new string(' ', rightPadding)));
}
}
@@ -256,16 +263,26 @@ namespace Spectre.Console
{
// Add right column edge
var part = firstRow && ShowHeaders ? TableBorderPart.HeaderRight : TableBorderPart.CellRight;
- result.Add(new Segment(border.GetPart(part), borderStyle));
+ rowResult.Add(new Segment(border.GetPart(part), borderStyle));
}
else if (showBorder)
{
// Add column separator
var part = firstRow && ShowHeaders ? TableBorderPart.HeaderSeparator : TableBorderPart.CellSeparator;
- result.Add(new Segment(border.GetPart(part), borderStyle));
+ rowResult.Add(new Segment(border.GetPart(part), borderStyle));
}
}
+ // Is the row larger than the allowed max width?
+ if (Segment.CellLength(context, rowResult) > actualMaxWidth)
+ {
+ result.AddRange(Segment.Truncate(context, rowResult, actualMaxWidth));
+ }
+ else
+ {
+ result.AddRange(rowResult);
+ }
+
result.Add(Segment.LineBreak);
}
diff --git a/src/Spectre.Console/Widgets/TableColumn.cs b/src/Spectre.Console/Widgets/TableColumn.cs
index 30dbe0f..29b045f 100644
--- a/src/Spectre.Console/Widgets/TableColumn.cs
+++ b/src/Spectre.Console/Widgets/TableColumn.cs
@@ -41,7 +41,7 @@ namespace Spectre.Console
///
/// The table column text.
public TableColumn(string text)
- : this(new Markup(text))
+ : this(new Markup(text).SetOverflow(Overflow.Ellipsis))
{
}