Add grid support

This commit is contained in:
Patrik Svensson 2020-08-05 00:40:45 +02:00 committed by Patrik Svensson
parent f9bd936254
commit 66994cd904
5 changed files with 209 additions and 12 deletions

View File

@ -94,6 +94,17 @@ namespace Sample
table.AddRow("Lorem ipsum dolor sit amet, consectetur [blue]adipiscing[/] elit,sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum", "◀ Strange language");
table.AddRow("Hej 👋", "[green]Världen[/]");
AnsiConsole.Render(table);
AnsiConsole.WriteLine();
AnsiConsole.MarkupLine(" Usage: [grey]dotnet [blue]run[/] [[options] [[[[--] <additional arguments>...]][/]");
AnsiConsole.WriteLine();
var grid = new Grid();
grid.AddColumns(3);
grid.AddRow(" Options", "", "");
grid.AddRow(" [blue]-h[/], [blue]--help[/]", " ", "Show command line help.");
grid.AddRow(" [blue]-c[/], [blue]--configuration[/] <CONFIGURATION>", " ", "The configuration to run for.\nThe default for most projects is [green]Debug[/].");
grid.AddRow(" [blue]-v[/], [blue]--verbosity[/] <LEVEL>", " ", "Set the MSBuild verbosity level.\nAllowed values are q[grey][[uiet][/], m[grey][[inimal][/], n[grey][[ormal][/], d[grey][[etailed][/], and diag[grey][[nostic][/].");
AnsiConsole.Render(grid);
}
}
}

View File

@ -0,0 +1,96 @@
using System;
using Shouldly;
using Xunit;
namespace Spectre.Console.Tests.Unit.Composition
{
public sealed class GridTests
{
public sealed class TheAddRowMethod
{
[Fact]
public void Should_Throw_If_Rows_Are_Null()
{
// Given
var grid = new Grid();
// When
var result = Record.Exception(() => grid.AddRow(null));
// Then
result.ShouldBeOfType<ArgumentNullException>()
.ParamName.ShouldBe("columns");
}
[Fact]
public void Should_Throw_If_Row_Columns_Is_Less_Than_Number_Of_Columns()
{
// Given
var grid = new Grid();
grid.AddColumns(2);
// When
var result = Record.Exception(() => grid.AddRow("Foo"));
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("The number of row columns are less than the number of grid columns.");
}
[Fact]
public void Should_Throw_If_Row_Columns_Are_Greater_Than_Number_Of_Columns()
{
// Given
var grid = new Grid();
grid.AddColumn();
// When
var result = Record.Exception(() => grid.AddRow("Foo", "Bar"));
// Then
result.ShouldBeOfType<InvalidOperationException>();
result.Message.ShouldBe("The number of row columns are greater than the number of grid columns.");
}
}
[Fact]
public void Should_Render_Grid_With_No_Border_Correctly()
{
// Given
var console = new PlainConsole(width: 80);
var grid = new Grid();
grid.AddColumns(3);
grid.AddRow("Qux", "Corgi", "Waldo");
grid.AddRow("Grault", "Garply", "Fred");
// When
console.Render(grid);
// Then
console.Lines.Count.ShouldBe(2);
console.Lines[0].ShouldBe("Qux Corgi Waldo");
console.Lines[1].ShouldBe("Grault Garply Fred ");
}
[Fact]
public void Should_Render_Grid()
{
var console = new PlainConsole(width: 120);
var grid = new Grid();
grid.AddColumns(3);
grid.AddRow("[bold]Options[/]", string.Empty, string.Empty);
grid.AddRow(" [blue]-h[/], [blue]--help[/]", " ", "Show command line help.");
grid.AddRow(" [blue]-c[/], [blue]--configuration[/]", " ", "The configuration to run for.\nThe default for most projects is [green]Debug[/].");
// When
console.Render(grid);
// Then
console.Lines.Count.ShouldBe(4);
console.Lines[0].ShouldBe("Options ");
console.Lines[1].ShouldBe(" -h, --help Show command line help. ");
console.Lines[2].ShouldBe(" -c, --configuration The configuration to run for. ");
console.Lines[3].ShouldBe(" The default for most projects is Debug.");
}
}
}

View File

@ -0,0 +1,79 @@
using System;
using System.Collections.Generic;
using System.Text;
using Spectre.Console.Composition;
namespace Spectre.Console
{
/// <summary>
/// Represents a grid.
/// </summary>
public sealed class Grid : IRenderable
{
private readonly Table _table;
/// <summary>
/// Initializes a new instance of the <see cref="Grid"/> class.
/// </summary>
public Grid()
{
_table = new Table(BorderKind.None, showHeaders: false);
}
/// <inheritdoc/>
public int Measure(Encoding encoding, int maxWidth)
{
return _table.Measure(encoding, maxWidth);
}
/// <inheritdoc/>
public IEnumerable<Segment> Render(Encoding encoding, int width)
{
return _table.Render(encoding, width);
}
/// <summary>
/// Adds a single column to the grid.
/// </summary>
public void AddColumn()
{
_table.AddColumn(string.Empty);
}
/// <summary>
/// Adds the specified number of columns to the grid.
/// </summary>
/// <param name="count">The number of columns.</param>
public void AddColumns(int count)
{
for (var i = 0; i < count; i++)
{
_table.AddColumn(string.Empty);
}
}
/// <summary>
/// Adds a new row to the grid.
/// </summary>
/// <param name="columns">The columns to add.</param>
public void AddRow(params string[] columns)
{
if (columns is null)
{
throw new ArgumentNullException(nameof(columns));
}
if (columns.Length < _table.ColumnCount)
{
throw new InvalidOperationException("The number of row columns are less than the number of grid columns.");
}
if (columns.Length > _table.ColumnCount)
{
throw new InvalidOperationException("The number of row columns are greater than the number of grid columns.");
}
_table.AddRow(columns);
}
}
}

View File

@ -16,17 +16,25 @@ namespace Spectre.Console
private readonly List<List<Text>> _rows;
private readonly Border _border;
private readonly BorderKind _borderKind;
private readonly bool _showHeaders;
/// <summary>
/// Gets the number of columns in the table.
/// </summary>
public int ColumnCount => _columns.Count;
/// <summary>
/// Initializes a new instance of the <see cref="Table"/> class.
/// </summary>
/// <param name="border">The border to use.</param>
public Table(BorderKind border = BorderKind.Square)
/// <param name="showHeaders">Whether or not to show table headers.</param>
public Table(BorderKind border = BorderKind.Square, bool showHeaders = true)
{
_columns = new List<Text>();
_rows = new List<List<Text>>();
_border = Border.GetBorder(border);
_borderKind = border;
_showHeaders = showHeaders;
}
/// <summary>
@ -84,7 +92,7 @@ namespace Spectre.Console
/// <inheritdoc/>
public int Measure(Encoding encoding, int maxWidth)
{
// Calculate the max width for each column.
// Calculate the max width for each column
var maxColumnWidth = (maxWidth - (2 + (_columns.Count * 2) + (_columns.Count - 1))) / _columns.Count;
var columnWidths = _columns.Select(c => c.Measure(encoding, maxColumnWidth)).ToArray();
for (var rowIndex = 0; rowIndex < _rows.Count; rowIndex++)
@ -99,7 +107,7 @@ namespace Spectre.Console
}
}
// We now know the max width of each column, so let's recalculate the width.
// We now know the max width of each column, so let's recalculate the width
return columnWidths.Sum() + 2 + (_columns.Count * 2) + (_columns.Count - 1);
}
@ -128,12 +136,17 @@ namespace Spectre.Console
}
}
// We now know the max width of each column, so let's recalculate the width.
// We now know the max width of each column, so let's recalculate the width
width = columnWidths.Sum() + leftRightBorderWidth + columnPadding + separatorCount;
// Create the rows.
var rows = new List<List<Text>>();
rows.Add(new List<Text>(_columns));
if (_showHeaders)
{
// Add columns to top of rows
rows.Add(new List<Text>(_columns));
}
// Add tows.
rows.AddRange(_rows);
// Iterate all rows.
@ -142,7 +155,7 @@ namespace Spectre.Console
{
var cellHeight = 1;
// Get the list of cells for the row and calculate the cell height.
// Get the list of cells for the row and calculate the cell height
var cells = new List<List<SegmentLine>>();
foreach (var (rowWidth, cell) in columnWidths.Zip(row, (f, s) => (f, s)))
{

View File

@ -140,11 +140,11 @@ namespace Spectre.Console
{
// Creates individual segments of line breaks.
var result = new List<Segment>();
var queue = new Queue<Segment>(segments);
var queue = new Stack<Segment>(segments.Reverse());
while (queue.Count > 0)
{
var segment = queue.Dequeue();
var segment = queue.Pop();
var index = segment.Text.IndexOf("\n", StringComparison.OrdinalIgnoreCase);
if (index == -1)
@ -160,7 +160,7 @@ namespace Spectre.Console
}
result.Add(Segment.LineBreak());
queue.Enqueue(new Segment(second.Text.Substring(1), second.Style));
queue.Push(new Segment(second.Text.Substring(1), second.Style));
}
}
@ -178,10 +178,8 @@ namespace Spectre.Console
// Create a span list.
var spans = new List<(int Offset, bool Leaving, int Style)>();
spans.Add((0, false, 0));
spans.AddRange(_spans.SelectIndex((span, index) => (span.Start, false, index + 1)));
spans.AddRange(_spans.SelectIndex((span, index) => (span.End, true, index + 1)));
spans.Add((_text.Length, true, 0));
spans = spans.OrderBy(x => x.Offset).ThenBy(x => !x.Leaving).ToList();
// Keep track of applied styles using a stack