mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 00:42:51 +08:00
parent
9f8ca6d648
commit
7471e9d38c
@ -4,7 +4,7 @@ namespace ColorExample
|
||||
{
|
||||
public static class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
public static void Main()
|
||||
{
|
||||
if (AnsiConsole.Capabilities.ColorSystem == ColorSystem.NoColors)
|
||||
{
|
||||
|
@ -3,9 +3,9 @@ using Spectre.Console;
|
||||
|
||||
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("Supports ansi? [bold]{0}[/]", AnsiConsole.Capabilities.SupportsAnsi);
|
||||
|
@ -2,9 +2,9 @@ using Spectre.Console;
|
||||
|
||||
namespace GridExample
|
||||
{
|
||||
public sealed class Program
|
||||
public static class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
public static void Main()
|
||||
{
|
||||
AnsiConsole.WriteLine();
|
||||
AnsiConsole.MarkupLine("Usage: [grey]dotnet [blue]run[/] [[options]] [[[[--]] <additional arguments>...]]]][/]");
|
||||
|
@ -2,9 +2,9 @@ using Spectre.Console;
|
||||
|
||||
namespace PanelExample
|
||||
{
|
||||
class Program
|
||||
public static class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
public static void Main()
|
||||
{
|
||||
var content = new Markup(
|
||||
"[underline]I[/] heard [underline on blue]you[/] like panels\n\n\n\n" +
|
||||
@ -14,7 +14,7 @@ namespace PanelExample
|
||||
new Panel(
|
||||
new Panel(content)
|
||||
{
|
||||
Border = BorderKind.Rounded
|
||||
Border = BorderKind.Rounded,
|
||||
}));
|
||||
|
||||
// Left adjusted panel with text
|
||||
@ -22,6 +22,7 @@ namespace PanelExample
|
||||
new Text("Left adjusted\nLeft").LeftAligned())
|
||||
{
|
||||
Expand = true,
|
||||
Header = new Header("Left", new Style(foreground: Color.Red)).LeftAligned(),
|
||||
});
|
||||
|
||||
// Centered ASCII panel with text
|
||||
@ -30,6 +31,7 @@ namespace PanelExample
|
||||
{
|
||||
Expand = true,
|
||||
Border = BorderKind.Ascii,
|
||||
Header = new Header("Center", new Style(foreground: Color.Green)).Centered(),
|
||||
});
|
||||
|
||||
// Right adjusted, rounded panel with text
|
||||
@ -38,6 +40,7 @@ namespace PanelExample
|
||||
{
|
||||
Expand = true,
|
||||
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 void Main(string[] args)
|
||||
public static void Main()
|
||||
{
|
||||
// A simple table
|
||||
RenderSimpleTable();
|
||||
|
@ -40,6 +40,108 @@ namespace Spectre.Console.Tests.Unit
|
||||
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]
|
||||
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.Linq;
|
||||
using Spectre.Console.Rendering;
|
||||
@ -35,6 +36,11 @@ namespace Spectre.Console
|
||||
/// </summary>
|
||||
public Padding Padding { get; set; } = new Padding(1, 1);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the header.
|
||||
/// </summary>
|
||||
public Header? Header { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Panel"/> class.
|
||||
/// </summary>
|
||||
@ -77,21 +83,16 @@ namespace Spectre.Console
|
||||
childWidth = measurement.Max;
|
||||
}
|
||||
|
||||
var panelWidth = childWidth + paddingWidth;
|
||||
var panelWidth = childWidth + EdgeWidth + paddingWidth;
|
||||
panelWidth = Math.Min(panelWidth, maxWidth);
|
||||
|
||||
var result = new List<Segment>();
|
||||
|
||||
// Panel top
|
||||
var result = new List<Segment>
|
||||
{
|
||||
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);
|
||||
AddTopBorder(result, context, border, borderStyle, panelWidth);
|
||||
|
||||
// Split the child segments into lines.
|
||||
var childSegments = _child.Render(context, childWidth);
|
||||
foreach (var line in Segment.SplitLines(childSegments, panelWidth))
|
||||
{
|
||||
result.Add(new Segment(border.GetPart(BorderPart.CellLeft), borderStyle));
|
||||
@ -126,12 +127,62 @@ namespace Spectre.Console
|
||||
}
|
||||
|
||||
// Panel bottom
|
||||
result.Add(new Segment(border.GetPart(BorderPart.FooterBottomLeft), borderStyle));
|
||||
result.Add(new Segment(border.GetPart(BorderPart.FooterBottom, panelWidth), borderStyle));
|
||||
result.Add(new Segment(border.GetPart(BorderPart.FooterBottomRight), borderStyle));
|
||||
result.Add(Segment.LineBreak);
|
||||
AddBottomBorder(result, border, borderStyle, panelWidth);
|
||||
|
||||
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 max = _lines.Max(x => x.CellWidth(context.Encoding));
|
||||
|
||||
return new Measurement(min, max);
|
||||
return new Measurement(min, Math.Min(max, maxWidth));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
@ -305,13 +305,21 @@ namespace Spectre.Console.Rendering
|
||||
}
|
||||
else if (overflow == Overflow.Ellipsis)
|
||||
{
|
||||
result.Add(new Segment(segment.Text.Substring(0, width - 1), segment.Style));
|
||||
result.Add(new Segment("…", segment.Style));
|
||||
result.Add(new Segment(segment.Text.Substring(0, width - 1) + "…", segment.Style));
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
foreach (var cell in cells)
|
||||
|
Loading…
x
Reference in New Issue
Block a user