mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 17:02:51 +08:00
Add column support
Adds support for rendering arbitrary data into columns. Closes #67
This commit is contained in:
parent
e946289bd9
commit
ae6d2c63a3
17
examples/Columns/Columns.csproj
Normal file
17
examples/Columns/Columns.csproj
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>Exe</OutputType>
|
||||||
|
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\Spectre.Console\Spectre.Console.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
42
examples/Columns/Program.cs
Normal file
42
examples/Columns/Program.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Spectre.Console;
|
||||||
|
using Spectre.Console.Rendering;
|
||||||
|
|
||||||
|
namespace ColumnsExample
|
||||||
|
{
|
||||||
|
public static class Program
|
||||||
|
{
|
||||||
|
public static async Task Main()
|
||||||
|
{
|
||||||
|
// Download some random users
|
||||||
|
using var client = new HttpClient();
|
||||||
|
dynamic users = JObject.Parse(
|
||||||
|
await client.GetStringAsync("https://randomuser.me/api/?results=15"));
|
||||||
|
|
||||||
|
// Create a card for each user
|
||||||
|
var cards = new List<Panel>();
|
||||||
|
foreach(var user in users.results)
|
||||||
|
{
|
||||||
|
cards.Add(new Panel(GetCard(user))
|
||||||
|
.SetHeader($"{user.location.country}")
|
||||||
|
.RoundedBorder().Expand());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render all cards in columns
|
||||||
|
AnsiConsole.Render(new Columns(cards));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetCard(dynamic user)
|
||||||
|
{
|
||||||
|
var name = $"{user.name.first} {user.name.last}";
|
||||||
|
var country = $"{user.location.city}";
|
||||||
|
|
||||||
|
return $"[b]{name}[/]\n[yellow]{country}[/]";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,3 @@
|
|||||||
using System;
|
|
||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
|
|
||||||
namespace Diagnostic
|
namespace Diagnostic
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
using Spectre.Console.Rendering;
|
|
||||||
|
|
||||||
namespace TableExample
|
namespace TableExample
|
||||||
{
|
{
|
||||||
|
@ -82,5 +82,8 @@ dotnet_diagnostic.RCS1079.severity = warning
|
|||||||
# RCS1057: Add empty line between declarations.
|
# RCS1057: Add empty line between declarations.
|
||||||
dotnet_diagnostic.RCS1057.severity = none
|
dotnet_diagnostic.RCS1057.severity = none
|
||||||
|
|
||||||
|
# RCS1057: Validate arguments correctly
|
||||||
|
dotnet_diagnostic.RCS1227.severity = none
|
||||||
|
|
||||||
# IDE0004: Remove Unnecessary Cast
|
# IDE0004: Remove Unnecessary Cast
|
||||||
dotnet_diagnostic.IDE0004.severity = warning
|
dotnet_diagnostic.IDE0004.severity = warning
|
46
src/Spectre.Console.Tests/Unit/ColumnsTests.cs
Normal file
46
src/Spectre.Console.Tests/Unit/ColumnsTests.cs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Shouldly;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.Unit
|
||||||
|
{
|
||||||
|
public sealed class ColumnsTests
|
||||||
|
{
|
||||||
|
private sealed class User
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string Country { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Columns_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 61);
|
||||||
|
var users = new[]
|
||||||
|
{
|
||||||
|
new User { Name = "Savannah Thompson", Country = "Australia" },
|
||||||
|
new User { Name = "Sophie Ramos", Country = "United States" },
|
||||||
|
new User { Name = "Katrin Goldberg", Country = "Germany" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var cards = new List<Panel>();
|
||||||
|
foreach (var user in users)
|
||||||
|
{
|
||||||
|
cards.Add(
|
||||||
|
new Panel($"[b]{user.Name}[/]\n[yellow]{user.Country}[/]")
|
||||||
|
.RoundedBorder().Expand());
|
||||||
|
}
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new Columns(cards));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(4);
|
||||||
|
console.Lines[0].ShouldBe("╭────────────────────╮ ╭────────────────╮ ╭─────────────────╮");
|
||||||
|
console.Lines[1].ShouldBe("│ Savannah Thompson │ │ Sophie Ramos │ │ Katrin Goldberg │");
|
||||||
|
console.Lines[2].ShouldBe("│ Australia │ │ United States │ │ Germany │");
|
||||||
|
console.Lines[3].ShouldBe("╰────────────────────╯ ╰────────────────╯ ╰─────────────────╯");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -42,7 +42,7 @@ namespace Spectre.Console.Tests.Unit
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Throw_If_Row_Columns_Is_Less_Than_Number_Of_Columns()
|
public void Should_Add_Empty_Items_If_User_Provides_Less_Row_Items_Than_Columns()
|
||||||
{
|
{
|
||||||
// Given
|
// Given
|
||||||
var grid = new Grid();
|
var grid = new Grid();
|
||||||
@ -50,11 +50,10 @@ namespace Spectre.Console.Tests.Unit
|
|||||||
grid.AddColumn();
|
grid.AddColumn();
|
||||||
|
|
||||||
// When
|
// When
|
||||||
var result = Record.Exception(() => grid.AddRow("Foo"));
|
grid.AddRow("Foo");
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
result.ShouldBeOfType<InvalidOperationException>();
|
grid.RowCount.ShouldBe(1);
|
||||||
result.Message.ShouldBe("The number of row columns are less than the number of grid columns.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -87,7 +87,7 @@ namespace Spectre.Console.Tests.Unit
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Throw_If_Row_Columns_Is_Less_Than_Number_Of_Columns()
|
public void Should_Add_Empty_Items_If_User_Provides_Less_Row_Items_Than_Columns()
|
||||||
{
|
{
|
||||||
// Given
|
// Given
|
||||||
var table = new Table();
|
var table = new Table();
|
||||||
@ -95,11 +95,10 @@ namespace Spectre.Console.Tests.Unit
|
|||||||
table.AddColumn("World");
|
table.AddColumn("World");
|
||||||
|
|
||||||
// When
|
// When
|
||||||
var result = Record.Exception(() => table.AddRow("Foo"));
|
table.AddRow("Foo");
|
||||||
|
|
||||||
// Then
|
// Then
|
||||||
result.ShouldBeOfType<InvalidOperationException>();
|
table.RowCount.ShouldBe(1);
|
||||||
result.Message.ShouldBe("The number of row columns are less than the number of table columns.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
@ -65,6 +65,21 @@ namespace Spectre.Console.Tests.Unit
|
|||||||
fixture.RawOutput.ShouldBe("Hello\n\nWorld\n\n");
|
fixture.RawOutput.ShouldBe("Hello\n\nWorld\n\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Panel_2()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new Markup("[b]Hello World[/]\n[yellow]Hello World[/]"));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(2);
|
||||||
|
console.Lines[0].ShouldBe("Hello World");
|
||||||
|
console.Lines[1].ShouldBe("Hello World");
|
||||||
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
[InlineData(5, "Hello World", "Hello\nWorld")]
|
[InlineData(5, "Hello World", "Hello\nWorld")]
|
||||||
[InlineData(10, "Hello Sweet Nice World", "Hello \nSweet Nice\nWorld")]
|
[InlineData(10, "Hello Sweet Nice World", "Hello \nSweet Nice\nWorld")]
|
||||||
|
@ -27,6 +27,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Colors", "..\examples\Color
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Diagnostic", "..\examples\Diagnostic\Diagnostic.csproj", "{4337F255-88E9-4408-81A3-DF1AF58AC753}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Diagnostic", "..\examples\Diagnostic\Diagnostic.csproj", "{4337F255-88E9-4408-81A3-DF1AF58AC753}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Columns", "..\examples\Columns\Columns.csproj", "{33357599-C79D-4299-888F-634E2C3EACEF}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@ -121,6 +123,18 @@ Global
|
|||||||
{4337F255-88E9-4408-81A3-DF1AF58AC753}.Release|x64.Build.0 = Release|Any CPU
|
{4337F255-88E9-4408-81A3-DF1AF58AC753}.Release|x64.Build.0 = Release|Any CPU
|
||||||
{4337F255-88E9-4408-81A3-DF1AF58AC753}.Release|x86.ActiveCfg = Release|Any CPU
|
{4337F255-88E9-4408-81A3-DF1AF58AC753}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
{4337F255-88E9-4408-81A3-DF1AF58AC753}.Release|x86.Build.0 = Release|Any CPU
|
{4337F255-88E9-4408-81A3-DF1AF58AC753}.Release|x86.Build.0 = Release|Any CPU
|
||||||
|
{33357599-C79D-4299-888F-634E2C3EACEF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{33357599-C79D-4299-888F-634E2C3EACEF}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{33357599-C79D-4299-888F-634E2C3EACEF}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||||
|
{33357599-C79D-4299-888F-634E2C3EACEF}.Debug|x64.Build.0 = Debug|Any CPU
|
||||||
|
{33357599-C79D-4299-888F-634E2C3EACEF}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||||
|
{33357599-C79D-4299-888F-634E2C3EACEF}.Debug|x86.Build.0 = Debug|Any CPU
|
||||||
|
{33357599-C79D-4299-888F-634E2C3EACEF}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{33357599-C79D-4299-888F-634E2C3EACEF}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{33357599-C79D-4299-888F-634E2C3EACEF}.Release|x64.ActiveCfg = Release|Any CPU
|
||||||
|
{33357599-C79D-4299-888F-634E2C3EACEF}.Release|x64.Build.0 = Release|Any CPU
|
||||||
|
{33357599-C79D-4299-888F-634E2C3EACEF}.Release|x86.ActiveCfg = Release|Any CPU
|
||||||
|
{33357599-C79D-4299-888F-634E2C3EACEF}.Release|x86.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
@ -131,6 +145,7 @@ Global
|
|||||||
{C7FF6FDB-FB59-4517-8669-521C96AB7323} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
{C7FF6FDB-FB59-4517-8669-521C96AB7323} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
||||||
{1F51C55C-BA4C-4856-9001-0F7924FFB179} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
{1F51C55C-BA4C-4856-9001-0F7924FFB179} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
||||||
{4337F255-88E9-4408-81A3-DF1AF58AC753} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
{4337F255-88E9-4408-81A3-DF1AF58AC753} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
||||||
|
{33357599-C79D-4299-888F-634E2C3EACEF} = {F0575243-121F-4DEE-9F6B-246E26DC0844}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}
|
SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using Spectre.Console.Internal;
|
using Spectre.Console.Internal;
|
||||||
using Spectre.Console.Rendering;
|
using Spectre.Console.Rendering;
|
||||||
|
|
||||||
@ -30,8 +31,8 @@ namespace Spectre.Console
|
|||||||
|
|
||||||
using (console.PushStyle(Style.Plain))
|
using (console.PushStyle(Style.Plain))
|
||||||
{
|
{
|
||||||
var segments = renderable.Render(options, console.Width);
|
var segments = renderable.Render(options, console.Width).Where(x => !(x.Text.Length == 0 && !x.IsLineBreak)).ToArray();
|
||||||
segments = Segment.Merge(segments);
|
segments = Segment.Merge(segments).ToArray();
|
||||||
|
|
||||||
var current = Style.Plain;
|
var current = Style.Plain;
|
||||||
foreach (var segment in segments)
|
foreach (var segment in segments)
|
||||||
|
141
src/Spectre.Console/Rendering/Columns.cs
Normal file
141
src/Spectre.Console/Rendering/Columns.cs
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Spectre.Console.Rendering;
|
||||||
|
|
||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Renders things in columns.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class Columns : Renderable, IPaddable, IExpandable
|
||||||
|
{
|
||||||
|
private readonly List<IRenderable> _items;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public Padding Padding { get; set; } = new Padding(0, 1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether or not the columns should
|
||||||
|
/// expand to the available space. If <c>false</c>, the column
|
||||||
|
/// width will be auto calculated. Defaults to <c>true</c>.
|
||||||
|
/// </summary>
|
||||||
|
public bool Expand { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Columns"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="items">The items to render.</param>
|
||||||
|
public Columns(IEnumerable<IRenderable> items)
|
||||||
|
{
|
||||||
|
if (items is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(items));
|
||||||
|
}
|
||||||
|
|
||||||
|
_items = new List<IRenderable>(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Columns"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="items">The items to render.</param>
|
||||||
|
public Columns(IEnumerable<string> items)
|
||||||
|
{
|
||||||
|
if (items is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(items));
|
||||||
|
}
|
||||||
|
|
||||||
|
_items = new List<IRenderable>(items.Select(item => new Markup(item)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
|
||||||
|
{
|
||||||
|
var maxPadding = Math.Max(Padding.Left, Padding.Right);
|
||||||
|
|
||||||
|
var itemWidths = _items.Select(item => item.Measure(context, maxWidth).Max).ToArray();
|
||||||
|
var columnCount = CalculateColumnCount(maxWidth, itemWidths, _items.Count, maxPadding);
|
||||||
|
|
||||||
|
var table = new Table();
|
||||||
|
table.NoBorder();
|
||||||
|
table.HideHeaders();
|
||||||
|
table.PadRightCell = false;
|
||||||
|
|
||||||
|
if (Expand)
|
||||||
|
{
|
||||||
|
table.Expand();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add columns
|
||||||
|
for (var index = 0; index < columnCount; index++)
|
||||||
|
{
|
||||||
|
table.AddColumn(new TableColumn(string.Empty)
|
||||||
|
{
|
||||||
|
Padding = Padding,
|
||||||
|
NoWrap = true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add rows
|
||||||
|
for (var start = 0; start < _items.Count; start += columnCount)
|
||||||
|
{
|
||||||
|
table.AddRow(_items.Skip(start).Take(columnCount).ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((IRenderable)table).Render(context, maxWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Algorithm borrowed from https://github.com/willmcgugan/rich/blob/master/rich/columns.py
|
||||||
|
private int CalculateColumnCount(int maxWidth, int[] itemWidths, int columnCount, int padding)
|
||||||
|
{
|
||||||
|
var widths = new Dictionary<int, int>();
|
||||||
|
while (columnCount > 1)
|
||||||
|
{
|
||||||
|
var columnIndex = 0;
|
||||||
|
widths.Clear();
|
||||||
|
|
||||||
|
var exceededTotalWidth = false;
|
||||||
|
foreach (var renderableWidth in IterateWidths(itemWidths, columnCount))
|
||||||
|
{
|
||||||
|
widths[columnIndex] = Math.Max(widths.ContainsKey(columnIndex) ? widths[columnIndex] : 0, renderableWidth);
|
||||||
|
var totalWidth = widths.Values.Sum() + (padding * (widths.Count - 1));
|
||||||
|
if (totalWidth > maxWidth)
|
||||||
|
{
|
||||||
|
columnCount = widths.Count - 1;
|
||||||
|
exceededTotalWidth = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
columnIndex = (columnIndex + 1) % columnCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!exceededTotalWidth)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return columnCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<int> IterateWidths(int[] itemWidths, int columnCount)
|
||||||
|
{
|
||||||
|
foreach (var width in itemWidths)
|
||||||
|
{
|
||||||
|
yield return width;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_items.Count % columnCount != 0)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < columnCount - (_items.Count % columnCount) - 1; i++)
|
||||||
|
{
|
||||||
|
yield return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -7,7 +7,7 @@ namespace Spectre.Console
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A renderable grid.
|
/// A renderable grid.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class Grid : Renderable
|
public sealed class Grid : Renderable, IExpandable
|
||||||
{
|
{
|
||||||
private readonly Table _table;
|
private readonly Table _table;
|
||||||
|
|
||||||
@ -21,6 +21,13 @@ namespace Spectre.Console
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int RowCount => _table.RowCount;
|
public int RowCount => _table.RowCount;
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public bool Expand
|
||||||
|
{
|
||||||
|
get => _table.Expand;
|
||||||
|
set => _table.Expand = value;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Grid"/> class.
|
/// Initializes a new instance of the <see cref="Grid"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -94,11 +101,6 @@ namespace Spectre.Console
|
|||||||
throw new ArgumentNullException(nameof(columns));
|
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)
|
if (columns.Length > _table.ColumnCount)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("The number of row columns are greater than the number of grid columns.");
|
throw new InvalidOperationException("The number of row columns are greater than the number of grid columns.");
|
||||||
|
@ -64,8 +64,8 @@ namespace Spectre.Console
|
|||||||
{
|
{
|
||||||
var childWidth = _child.Measure(context, maxWidth);
|
var childWidth = _child.Measure(context, maxWidth);
|
||||||
return new Measurement(
|
return new Measurement(
|
||||||
childWidth.Min + 2 + Padding.GetHorizontalPadding(),
|
childWidth.Min + EdgeWidth + Padding.GetHorizontalPadding(),
|
||||||
childWidth.Max + 2 + Padding.GetHorizontalPadding());
|
childWidth.Max + EdgeWidth + Padding.GetHorizontalPadding());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
@ -13,10 +13,25 @@ namespace Spectre.Console.Rendering
|
|||||||
[DebuggerDisplay("{Text,nq}")]
|
[DebuggerDisplay("{Text,nq}")]
|
||||||
public class Segment
|
public class Segment
|
||||||
{
|
{
|
||||||
|
private readonly bool _mutable;
|
||||||
|
private string _text;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the segment text.
|
/// Gets the segment text.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Text { get; internal set; }
|
public string Text
|
||||||
|
{
|
||||||
|
get => _text;
|
||||||
|
private set
|
||||||
|
{
|
||||||
|
if (!_mutable)
|
||||||
|
{
|
||||||
|
throw new NotSupportedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
_text = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a value indicating whether or not this is an expicit line break
|
/// Gets a value indicating whether or not this is an expicit line break
|
||||||
@ -39,12 +54,12 @@ namespace Spectre.Console.Rendering
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a segment representing a line break.
|
/// Gets a segment representing a line break.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Segment LineBreak { get; } = new Segment(Environment.NewLine, Style.Plain, true);
|
public static Segment LineBreak { get; } = new Segment(Environment.NewLine, Style.Plain, true, false);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets an empty segment.
|
/// Gets an empty segment.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static Segment Empty { get; } = new Segment(string.Empty, Style.Plain);
|
public static Segment Empty { get; } = new Segment(string.Empty, Style.Plain, false, false);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Segment"/> class.
|
/// Initializes a new instance of the <see cref="Segment"/> class.
|
||||||
@ -65,14 +80,16 @@ namespace Spectre.Console.Rendering
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
private Segment(string text, Style style, bool lineBreak)
|
private Segment(string text, Style style, bool lineBreak, bool mutable = true)
|
||||||
{
|
{
|
||||||
if (text is null)
|
if (text is null)
|
||||||
{
|
{
|
||||||
throw new ArgumentNullException(nameof(text));
|
throw new ArgumentNullException(nameof(text));
|
||||||
}
|
}
|
||||||
|
|
||||||
Text = text.NormalizeLineEndings();
|
_mutable = mutable;
|
||||||
|
_text = text.NormalizeLineEndings();
|
||||||
|
|
||||||
Style = style;
|
Style = style;
|
||||||
IsLineBreak = lineBreak;
|
IsLineBreak = lineBreak;
|
||||||
IsWhiteSpace = string.IsNullOrWhiteSpace(text);
|
IsWhiteSpace = string.IsNullOrWhiteSpace(text);
|
||||||
|
@ -18,7 +18,7 @@ namespace Spectre.Console
|
|||||||
// https://github.com/willmcgugan/rich/blob/527475837ebbfc427530b3ee0d4d0741d2d0fc6d/rich/table.py#L394
|
// https://github.com/willmcgugan/rich/blob/527475837ebbfc427530b3ee0d4d0741d2d0fc6d/rich/table.py#L394
|
||||||
private List<int> CalculateColumnWidths(RenderContext options, int maxWidth)
|
private List<int> CalculateColumnWidths(RenderContext options, int maxWidth)
|
||||||
{
|
{
|
||||||
var width_ranges = _columns.Select(column => MeasureColumn(column, options, maxWidth));
|
var width_ranges = _columns.Select(column => MeasureColumn(column, options, maxWidth)).ToArray();
|
||||||
var widths = width_ranges.Select(range => range.Max).ToList();
|
var widths = width_ranges.Select(range => range.Max).ToList();
|
||||||
|
|
||||||
var tableWidth = widths.Sum();
|
var tableWidth = widths.Sum();
|
||||||
@ -117,9 +117,17 @@ namespace Spectre.Console
|
|||||||
|
|
||||||
private int GetExtraWidth(bool includePadding)
|
private int GetExtraWidth(bool includePadding)
|
||||||
{
|
{
|
||||||
var separators = _columns.Count - 1;
|
var hideBorder = BorderKind == BorderKind.None;
|
||||||
|
var separators = hideBorder ? 0 : _columns.Count - 1;
|
||||||
|
var edges = hideBorder ? 0 : EdgeCount;
|
||||||
var padding = includePadding ? _columns.Select(x => x.Padding.GetHorizontalPadding()).Sum() : 0;
|
var padding = includePadding ? _columns.Select(x => x.Padding.GetHorizontalPadding()).Sum() : 0;
|
||||||
return separators + EdgeCount + padding;
|
|
||||||
|
if (!PadRightCell)
|
||||||
|
{
|
||||||
|
padding -= _columns.Last().Padding.Right;
|
||||||
|
}
|
||||||
|
|
||||||
|
return separators + edges + padding;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -125,17 +125,19 @@ namespace Spectre.Console
|
|||||||
throw new ArgumentNullException(nameof(columns));
|
throw new ArgumentNullException(nameof(columns));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (columns.Length < _columns.Count)
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("The number of row columns are less than the number of table columns.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (columns.Length > _columns.Count)
|
if (columns.Length > _columns.Count)
|
||||||
{
|
{
|
||||||
throw new InvalidOperationException("The number of row columns are greater than the number of table columns.");
|
throw new InvalidOperationException("The number of row columns are greater than the number of table columns.");
|
||||||
}
|
}
|
||||||
|
|
||||||
_rows.Add(columns.ToList());
|
_rows.Add(columns.ToList());
|
||||||
|
|
||||||
|
// Need to add missing columns?
|
||||||
|
if (columns.Length < _columns.Count)
|
||||||
|
{
|
||||||
|
var diff = _columns.Count - columns.Length;
|
||||||
|
Enumerable.Range(0, diff).ForEach(_ => _rows.Last().Add(Text.Empty));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Spectre.Console.Rendering
|
namespace Spectre.Console
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains extension methods for <see cref="IHasBorder"/>.
|
/// Contains extension methods for <see cref="IHasBorder"/>.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Spectre.Console.Rendering
|
namespace Spectre.Console
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains extension methods for <see cref="IExpandable"/>.
|
/// Contains extension methods for <see cref="IExpandable"/>.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user