mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 17:02:51 +08:00
parent
9f8ca6d648
commit
7471e9d38c
@ -4,7 +4,7 @@ namespace ColorExample
|
|||||||
{
|
{
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static void Main()
|
||||||
{
|
{
|
||||||
if (AnsiConsole.Capabilities.ColorSystem == ColorSystem.NoColors)
|
if (AnsiConsole.Capabilities.ColorSystem == ColorSystem.NoColors)
|
||||||
{
|
{
|
||||||
|
@ -3,9 +3,9 @@ using Spectre.Console;
|
|||||||
|
|
||||||
namespace Diagnostic
|
namespace Diagnostic
|
||||||
{
|
{
|
||||||
public class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static void Main()
|
||||||
{
|
{
|
||||||
AnsiConsole.MarkupLine("Color system: [bold]{0}[/]", AnsiConsole.Capabilities.ColorSystem);
|
AnsiConsole.MarkupLine("Color system: [bold]{0}[/]", AnsiConsole.Capabilities.ColorSystem);
|
||||||
AnsiConsole.MarkupLine("Supports ansi? [bold]{0}[/]", AnsiConsole.Capabilities.SupportsAnsi);
|
AnsiConsole.MarkupLine("Supports ansi? [bold]{0}[/]", AnsiConsole.Capabilities.SupportsAnsi);
|
||||||
|
@ -2,9 +2,9 @@ using Spectre.Console;
|
|||||||
|
|
||||||
namespace GridExample
|
namespace GridExample
|
||||||
{
|
{
|
||||||
public sealed class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
public static void Main()
|
||||||
{
|
{
|
||||||
AnsiConsole.WriteLine();
|
AnsiConsole.WriteLine();
|
||||||
AnsiConsole.MarkupLine("Usage: [grey]dotnet [blue]run[/] [[options]] [[[[--]] <additional arguments>...]]]][/]");
|
AnsiConsole.MarkupLine("Usage: [grey]dotnet [blue]run[/] [[options]] [[[[--]] <additional arguments>...]]]][/]");
|
||||||
|
@ -2,9 +2,9 @@ using Spectre.Console;
|
|||||||
|
|
||||||
namespace PanelExample
|
namespace PanelExample
|
||||||
{
|
{
|
||||||
class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
static void Main(string[] args)
|
public static void Main()
|
||||||
{
|
{
|
||||||
var content = new Markup(
|
var content = new Markup(
|
||||||
"[underline]I[/] heard [underline on blue]you[/] like panels\n\n\n\n" +
|
"[underline]I[/] heard [underline on blue]you[/] like panels\n\n\n\n" +
|
||||||
@ -14,7 +14,7 @@ namespace PanelExample
|
|||||||
new Panel(
|
new Panel(
|
||||||
new Panel(content)
|
new Panel(content)
|
||||||
{
|
{
|
||||||
Border = BorderKind.Rounded
|
Border = BorderKind.Rounded,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Left adjusted panel with text
|
// Left adjusted panel with text
|
||||||
@ -22,6 +22,7 @@ namespace PanelExample
|
|||||||
new Text("Left adjusted\nLeft").LeftAligned())
|
new Text("Left adjusted\nLeft").LeftAligned())
|
||||||
{
|
{
|
||||||
Expand = true,
|
Expand = true,
|
||||||
|
Header = new Header("Left", new Style(foreground: Color.Red)).LeftAligned(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Centered ASCII panel with text
|
// Centered ASCII panel with text
|
||||||
@ -30,6 +31,7 @@ namespace PanelExample
|
|||||||
{
|
{
|
||||||
Expand = true,
|
Expand = true,
|
||||||
Border = BorderKind.Ascii,
|
Border = BorderKind.Ascii,
|
||||||
|
Header = new Header("Center", new Style(foreground: Color.Green)).Centered(),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Right adjusted, rounded panel with text
|
// Right adjusted, rounded panel with text
|
||||||
@ -38,6 +40,7 @@ namespace PanelExample
|
|||||||
{
|
{
|
||||||
Expand = true,
|
Expand = true,
|
||||||
Border = BorderKind.Rounded,
|
Border = BorderKind.Rounded,
|
||||||
|
Header = new Header("Right", new Style(foreground: Color.Blue)).RightAligned(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ namespace TableExample
|
|||||||
{
|
{
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
public static void Main(string[] args)
|
public static void Main()
|
||||||
{
|
{
|
||||||
// A simple table
|
// A simple table
|
||||||
RenderSimpleTable();
|
RenderSimpleTable();
|
||||||
|
@ -40,6 +40,108 @@ namespace Spectre.Console.Tests.Unit
|
|||||||
console.Lines[2].ShouldBe("└───────────────────┘");
|
console.Lines[2].ShouldBe("└───────────────────┘");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Panel_With_Header()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new Panel("Hello World")
|
||||||
|
{
|
||||||
|
Header = new Header("Greeting"),
|
||||||
|
Expand = true,
|
||||||
|
Padding = new Padding(2, 2),
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(3);
|
||||||
|
console.Lines[0].ShouldBe("┌─Greeting─────────────────────────────────────────────────────────────────────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Hello World │");
|
||||||
|
console.Lines[2].ShouldBe("└──────────────────────────────────────────────────────────────────────────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Panel_With_Left_Aligned_Header()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new Panel("Hello World")
|
||||||
|
{
|
||||||
|
Header = new Header("Greeting").LeftAligned(),
|
||||||
|
Expand = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(3);
|
||||||
|
console.Lines[0].ShouldBe("┌─Greeting─────────────────────────────────────────────────────────────────────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Hello World │");
|
||||||
|
console.Lines[2].ShouldBe("└──────────────────────────────────────────────────────────────────────────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Panel_With_Centered_Header()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new Panel("Hello World")
|
||||||
|
{
|
||||||
|
Header = new Header("Greeting").Centered(),
|
||||||
|
Expand = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(3);
|
||||||
|
console.Lines[0].ShouldBe("┌───────────────────────────────────Greeting───────────────────────────────────┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Hello World │");
|
||||||
|
console.Lines[2].ShouldBe("└──────────────────────────────────────────────────────────────────────────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Render_Panel_With_Right_Aligned_Header()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new Panel("Hello World")
|
||||||
|
{
|
||||||
|
Header = new Header("Greeting").RightAligned(),
|
||||||
|
Expand = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(3);
|
||||||
|
console.Lines[0].ShouldBe("┌─────────────────────────────────────────────────────────────────────Greeting─┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Hello World │");
|
||||||
|
console.Lines[2].ShouldBe("└──────────────────────────────────────────────────────────────────────────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Should_Collapse_Header_If_It_Will_Not_Fit()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new PlainConsole(width: 10);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new Panel("Hello World")
|
||||||
|
{
|
||||||
|
Header = new Header("Greeting"),
|
||||||
|
Expand = true,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Then
|
||||||
|
console.Lines.Count.ShouldBe(4);
|
||||||
|
console.Lines[0].ShouldBe("┌─Greet…─┐");
|
||||||
|
console.Lines[1].ShouldBe("│ Hello │");
|
||||||
|
console.Lines[2].ShouldBe("│ World │");
|
||||||
|
console.Lines[3].ShouldBe("└────────┘");
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void Should_Render_Panel_With_Unicode_Correctly()
|
public void Should_Render_Panel_With_Unicode_Correctly()
|
||||||
{
|
{
|
||||||
|
60
src/Spectre.Console/Rendering/Header.cs
Normal file
60
src/Spectre.Console/Rendering/Header.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a header.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class Header : IAlignable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the header text.
|
||||||
|
/// </summary>
|
||||||
|
public string Text { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the header style.
|
||||||
|
/// </summary>
|
||||||
|
public Style? Style { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the header alignment.
|
||||||
|
/// </summary>
|
||||||
|
public Justify? Alignment { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="Header"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="text">The header text.</param>
|
||||||
|
/// <param name="style">The header style.</param>
|
||||||
|
/// <param name="alignment">The header alignment.</param>
|
||||||
|
public Header(string text, Style? style = null, Justify? alignment = null)
|
||||||
|
{
|
||||||
|
Text = text ?? throw new ArgumentNullException(nameof(text));
|
||||||
|
Style = style;
|
||||||
|
Alignment = alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the header style.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="style">The header style.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public Header SetStyle(Style? style)
|
||||||
|
{
|
||||||
|
Style = style ?? Style.Plain;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the header alignment.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="alignment">The header alignment.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public Header SetAlignment(Justify alignment)
|
||||||
|
{
|
||||||
|
Alignment = alignment;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Spectre.Console.Rendering;
|
using Spectre.Console.Rendering;
|
||||||
@ -35,6 +36,11 @@ namespace Spectre.Console
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Padding Padding { get; set; } = new Padding(1, 1);
|
public Padding Padding { get; set; } = new Padding(1, 1);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the header.
|
||||||
|
/// </summary>
|
||||||
|
public Header? Header { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Panel"/> class.
|
/// Initializes a new instance of the <see cref="Panel"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -77,21 +83,16 @@ namespace Spectre.Console
|
|||||||
childWidth = measurement.Max;
|
childWidth = measurement.Max;
|
||||||
}
|
}
|
||||||
|
|
||||||
var panelWidth = childWidth + paddingWidth;
|
var panelWidth = childWidth + EdgeWidth + paddingWidth;
|
||||||
|
panelWidth = Math.Min(panelWidth, maxWidth);
|
||||||
|
|
||||||
|
var result = new List<Segment>();
|
||||||
|
|
||||||
// Panel top
|
// Panel top
|
||||||
var result = new List<Segment>
|
AddTopBorder(result, context, border, borderStyle, panelWidth);
|
||||||
{
|
|
||||||
new Segment(border.GetPart(BorderPart.HeaderTopLeft), borderStyle),
|
|
||||||
new Segment(border.GetPart(BorderPart.HeaderTop, panelWidth), borderStyle),
|
|
||||||
new Segment(border.GetPart(BorderPart.HeaderTopRight), borderStyle),
|
|
||||||
Segment.LineBreak,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Render the child.
|
|
||||||
var childSegments = _child.Render(context, childWidth);
|
|
||||||
|
|
||||||
// Split the child segments into lines.
|
// Split the child segments into lines.
|
||||||
|
var childSegments = _child.Render(context, childWidth);
|
||||||
foreach (var line in Segment.SplitLines(childSegments, panelWidth))
|
foreach (var line in Segment.SplitLines(childSegments, panelWidth))
|
||||||
{
|
{
|
||||||
result.Add(new Segment(border.GetPart(BorderPart.CellLeft), borderStyle));
|
result.Add(new Segment(border.GetPart(BorderPart.CellLeft), borderStyle));
|
||||||
@ -126,12 +127,62 @@ namespace Spectre.Console
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Panel bottom
|
// Panel bottom
|
||||||
result.Add(new Segment(border.GetPart(BorderPart.FooterBottomLeft), borderStyle));
|
AddBottomBorder(result, border, borderStyle, panelWidth);
|
||||||
result.Add(new Segment(border.GetPart(BorderPart.FooterBottom, panelWidth), borderStyle));
|
|
||||||
result.Add(new Segment(border.GetPart(BorderPart.FooterBottomRight), borderStyle));
|
|
||||||
result.Add(Segment.LineBreak);
|
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void AddBottomBorder(List<Segment> result, SpectreBorder 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(Segment.LineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddTopBorder(List<Segment> segments, RenderContext context, SpectreBorder border, Style borderStyle, int panelWidth)
|
||||||
|
{
|
||||||
|
segments.Add(new Segment(border.GetPart(BorderPart.HeaderTopLeft), borderStyle));
|
||||||
|
|
||||||
|
if (Header != null)
|
||||||
|
{
|
||||||
|
var leftSpacing = 0;
|
||||||
|
var rightSpacing = 0;
|
||||||
|
|
||||||
|
var headerWidth = panelWidth - (EdgeWidth * 2);
|
||||||
|
var header = Segment.TruncateWithEllipsis(Header.Text, Header.Style ?? borderStyle, context.Encoding, headerWidth);
|
||||||
|
|
||||||
|
var excessWidth = headerWidth - header.CellLength(context.Encoding);
|
||||||
|
if (excessWidth > 0)
|
||||||
|
{
|
||||||
|
switch (Header.Alignment ?? Justify.Left)
|
||||||
|
{
|
||||||
|
case Justify.Left:
|
||||||
|
leftSpacing = 0;
|
||||||
|
rightSpacing = excessWidth;
|
||||||
|
break;
|
||||||
|
case Justify.Right:
|
||||||
|
leftSpacing = excessWidth;
|
||||||
|
rightSpacing = 0;
|
||||||
|
break;
|
||||||
|
case Justify.Center:
|
||||||
|
leftSpacing = excessWidth / 2;
|
||||||
|
rightSpacing = (excessWidth / 2) + (excessWidth % 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
segments.Add(new Segment(border.GetPart(BorderPart.HeaderTop, leftSpacing + 1), borderStyle));
|
||||||
|
segments.Add(header);
|
||||||
|
segments.Add(new Segment(border.GetPart(BorderPart.HeaderTop, rightSpacing + 1), borderStyle));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
segments.Add(new Segment(border.GetPart(BorderPart.HeaderTop, panelWidth - EdgeWidth), borderStyle));
|
||||||
|
}
|
||||||
|
|
||||||
|
segments.Add(new Segment(border.GetPart(BorderPart.HeaderTopRight), borderStyle));
|
||||||
|
segments.Add(Segment.LineBreak);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
50
src/Spectre.Console/Rendering/PanelExtensions.cs
Normal file
50
src/Spectre.Console/Rendering/PanelExtensions.cs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Rendering
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains extension methods for <see cref="Panel"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class PanelExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the panel header.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="panel">The panel.</param>
|
||||||
|
/// <param name="text">The header text.</param>
|
||||||
|
/// <param name="style">The header style.</param>
|
||||||
|
/// <param name="alignment">The header alignment.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public static Panel SetHeader(this Panel panel, string text, Style? style = null, Justify? alignment = null)
|
||||||
|
{
|
||||||
|
if (panel is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(panel));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (text is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
return SetHeader(panel, new Header(text, style, alignment));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the panel header.
|
||||||
|
/// </summary>
|
||||||
|
/// <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, Header header)
|
||||||
|
{
|
||||||
|
if (panel is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(panel));
|
||||||
|
}
|
||||||
|
|
||||||
|
panel.Header = header;
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -122,7 +122,7 @@ namespace Spectre.Console
|
|||||||
var min = _lines.Max(line => line.Max(segment => segment.CellLength(context.Encoding)));
|
var min = _lines.Max(line => line.Max(segment => segment.CellLength(context.Encoding)));
|
||||||
var max = _lines.Max(x => x.CellWidth(context.Encoding));
|
var max = _lines.Max(x => x.CellWidth(context.Encoding));
|
||||||
|
|
||||||
return new Measurement(min, max);
|
return new Measurement(min, Math.Min(max, maxWidth));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
|
@ -305,13 +305,21 @@ namespace Spectre.Console.Rendering
|
|||||||
}
|
}
|
||||||
else if (overflow == Overflow.Ellipsis)
|
else if (overflow == Overflow.Ellipsis)
|
||||||
{
|
{
|
||||||
result.Add(new Segment(segment.Text.Substring(0, width - 1), segment.Style));
|
result.Add(new Segment(segment.Text.Substring(0, width - 1) + "…", segment.Style));
|
||||||
result.Add(new Segment("…", segment.Style));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static Segment TruncateWithEllipsis(string text, Style style, Encoding encoding, int maxWidth)
|
||||||
|
{
|
||||||
|
return SplitOverflow(
|
||||||
|
new Segment(text, style),
|
||||||
|
Overflow.Ellipsis,
|
||||||
|
encoding,
|
||||||
|
maxWidth).First();
|
||||||
|
}
|
||||||
|
|
||||||
internal static List<List<SegmentLine>> MakeSameHeight(int cellHeight, List<List<SegmentLine>> cells)
|
internal static List<List<SegmentLine>> MakeSameHeight(int cellHeight, List<List<SegmentLine>> cells)
|
||||||
{
|
{
|
||||||
foreach (var cell in cells)
|
foreach (var cell in cells)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user