Change IAnsiConsole to render IRenderable

This makes it possible for encoders to output better representation
of the actual objects instead of working with chopped up segments.

* IAnsiConsole.Write now takes an IRenderable instead of segments
* Calculating cell width does no longer require a render context
* Removed RenderContext.LegacyConsole
* Removed RenderContext.Encoding
* Added Capabilities.Unicode
This commit is contained in:
Patrik Svensson
2021-03-24 23:09:24 +01:00
committed by Phil Scott
parent 2ba6da3514
commit 20650f1e7e
75 changed files with 492 additions and 553 deletions

View File

@ -53,7 +53,7 @@ namespace Spectre.Console
{
var line = new Segment(string.Concat(row.Select(x => x.Lines[index])), style);
var lineWidth = line.CellCount(context);
var lineWidth = line.CellCount();
if (alignment == Justify.Left)
{
yield return line;

View File

@ -70,7 +70,7 @@ namespace Spectre.Console
}
var child = _child.Render(context, maxWidth - paddingWidth);
foreach (var (_, _, _, line) in Segment.SplitLines(context, child).Enumerate())
foreach (var line in Segment.SplitLines(child))
{
// Left padding
if (Padding.GetLeftSafe() != 0)
@ -87,7 +87,7 @@ namespace Spectre.Console
}
// Missing space on right side?
var lineWidth = line.CellCount(context);
var lineWidth = line.CellCount();
var diff = width - lineWidth - Padding.GetLeftSafe() - Padding.GetRightSafe();
if (diff > 0)
{

View File

@ -78,7 +78,7 @@ namespace Spectre.Console
{
var edgeWidth = EdgeWidth;
var border = BoxExtensions.GetSafeBorder(Border, (context.LegacyConsole || !context.Unicode) && UseSafeBorder);
var border = BoxExtensions.GetSafeBorder(Border, !context.Unicode && UseSafeBorder);
var borderStyle = BorderStyle ?? Style.Plain;
var showBorder = true;
@ -110,7 +110,7 @@ namespace Spectre.Console
// Split the child segments into lines.
var childSegments = ((IRenderable)child).Render(context, childWidth);
foreach (var (_, _, last, line) in Segment.SplitLines(context, childSegments, childWidth).Enumerate())
foreach (var (_, _, last, line) in Segment.SplitLines(childSegments, childWidth).Enumerate())
{
if (line.Count == 1 && line[0].IsWhiteSpace)
{
@ -128,7 +128,7 @@ namespace Spectre.Console
content.AddRange(line);
// Do we need to pad the panel?
var length = line.Sum(segment => segment.CellCount(context));
var length = line.Sum(segment => segment.CellCount());
if (length < childWidth)
{
var diff = childWidth - length;

View File

@ -118,8 +118,8 @@ namespace Spectre.Console
return new Measurement(0, 0);
}
var min = _lines.Max(line => line.Max(segment => segment.CellCount(context)));
var max = _lines.Max(x => x.CellCount(context));
var min = _lines.Max(line => line.Max(segment => segment.CellCount()));
var max = _lines.Max(x => x.CellCount());
return new Measurement(min, Math.Min(max, maxWidth));
}
@ -139,7 +139,7 @@ namespace Spectre.Console
var lines = context.SingleLine
? new List<SegmentLine>(_lines)
: SplitLines(context, maxWidth);
: SplitLines(maxWidth);
// Justify lines
var justification = context.Justification ?? Alignment ?? Justify.Left;
@ -178,7 +178,7 @@ namespace Spectre.Console
return result;
}
private List<SegmentLine> SplitLines(RenderContext context, int maxWidth)
private List<SegmentLine> SplitLines(int maxWidth)
{
if (maxWidth <= 0)
{
@ -186,7 +186,7 @@ namespace Spectre.Console
return new List<SegmentLine>();
}
if (_lines.Max(x => x.CellCount(context)) <= maxWidth)
if (_lines.Max(x => x.CellCount()) <= maxWidth)
{
return Clone();
}
@ -230,15 +230,15 @@ namespace Spectre.Console
continue;
}
var length = current.CellCount(context);
var length = current.CellCount();
if (length > maxWidth)
{
// The current segment is longer than the width of the console,
// so we will need to crop it up, into new segments.
var segments = Segment.SplitOverflow(current, Overflow, context, maxWidth);
var segments = Segment.SplitOverflow(current, Overflow, maxWidth);
if (segments.Count > 0)
{
if (line.CellCount(context) + segments[0].CellCount(context) > maxWidth)
if (line.CellCount() + segments[0].CellCount() > maxWidth)
{
lines.Add(line);
line = new SegmentLine();
@ -258,7 +258,7 @@ namespace Spectre.Console
}
else
{
if (line.CellCount(context) + length > maxWidth)
if (line.CellCount() + length > maxWidth)
{
line.Add(Segment.Empty);
lines.Add(line);

View File

@ -101,7 +101,7 @@ namespace Spectre.Console
/// <inheritdoc/>
public override IRenderable Render(RenderContext context, ProgressTask task, TimeSpan deltaTime)
{
var useAscii = (context.LegacyConsole || !context.Unicode) && _spinner.IsUnicode;
var useAscii = !context.Unicode && _spinner.IsUnicode;
var spinner = useAscii ? Spinner.Known.Ascii : _spinner ?? Spinner.Known.Default;
if (!task.IsStarted)
@ -138,14 +138,14 @@ namespace Spectre.Console
{
if (_maxWidth == null)
{
var useAscii = (context.LegacyConsole || !context.Unicode) && _spinner.IsUnicode;
var useAscii = !context.Unicode && _spinner.IsUnicode;
var spinner = useAscii ? Spinner.Known.Ascii : _spinner ?? Spinner.Known.Default;
_maxWidth = Math.Max(
Math.Max(
((IRenderable)new Markup(PendingText ?? " ")).Measure(context, int.MaxValue).Max,
((IRenderable)new Markup(CompletedText ?? " ")).Measure(context, int.MaxValue).Max),
spinner.Frames.Max(frame => Cell.GetCellLength(context, frame)));
spinner.Frames.Max(frame => Cell.GetCellLength(frame)));
}
return _maxWidth.Value;

View File

@ -52,7 +52,7 @@ namespace Spectre.Console
public void Refresh()
{
_renderer.Update(this);
_console.Render(new ControlSequence(string.Empty));
_console.Write(new ControlSequence(string.Empty));
}
internal IReadOnlyList<ProgressTask> GetTasks()

View File

@ -42,7 +42,7 @@ namespace Spectre.Console
{
if (clear)
{
_console.Render(_live.RestoreCursor());
_console.Write(_live.RestoreCursor());
}
else
{
@ -62,7 +62,7 @@ namespace Spectre.Console
_stopwatch.Start();
}
var renderContext = new RenderContext(_console.Profile.Encoding, _console.Profile.Capabilities.Legacy);
var renderContext = new RenderContext(_console.Profile.Capabilities);
var delta = _stopwatch.Elapsed - _lastUpdate;
_lastUpdate = _stopwatch.Elapsed;

View File

@ -32,7 +32,7 @@ namespace Spectre.Console
var width = Math.Min(Width ?? maxWidth, maxWidth);
var completed = Math.Min(MaxValue, Math.Max(0, Value));
var token = !context.Unicode || context.LegacyConsole ? AsciiBar : UnicodeBar;
var token = !context.Unicode ? AsciiBar : UnicodeBar;
var style = completed >= MaxValue ? FinishedStyle : CompletedStyle;
var bars = Math.Max(0, (int)(width * (completed / MaxValue)));

View File

@ -0,0 +1,67 @@
namespace Spectre.Console
{
/// <summary>
/// A prompt that is answered with a yes or no.
/// </summary>
public sealed class ConfirmationPrompt : IPrompt<bool>
{
private readonly string _prompt;
/// <summary>
/// Gets or sets the character that represents "yes".
/// </summary>
public char Yes { get; set; } = 'y';
/// <summary>
/// Gets or sets the character that represents "no".
/// </summary>
public char No { get; set; } = 'n';
/// <summary>
/// Gets or sets a value indicating whether "yes" is the default answer.
/// </summary>
public bool DefaultValue { get; set; } = true;
/// <summary>
/// Gets or sets the message for invalid choices.
/// </summary>
public string InvalidChoiceMessage { get; set; } = "[red]Please select one of the available options[/]";
/// <summary>
/// Gets or sets a value indicating whether or not
/// choices should be shown.
/// </summary>
public bool ShowChoices { get; set; } = true;
/// <summary>
/// Gets or sets a value indicating whether or not
/// default values should be shown.
/// </summary>
public bool ShowDefaultValue { get; set; } = true;
/// <summary>
/// Initializes a new instance of the <see cref="ConfirmationPrompt"/> class.
/// </summary>
/// <param name="prompt">The prompt markup text.</param>
public ConfirmationPrompt(string prompt)
{
_prompt = prompt ?? throw new System.ArgumentNullException(nameof(prompt));
}
/// <inheritdoc/>
public bool Show(IAnsiConsole console)
{
var prompt = new TextPrompt<char>(_prompt)
.InvalidChoiceMessage(InvalidChoiceMessage)
.ValidationErrorMessage(InvalidChoiceMessage)
.ShowChoices(ShowChoices)
.ShowDefaultValue(ShowDefaultValue)
.DefaultValue(DefaultValue ? Yes : No)
.AddChoice(Yes)
.AddChoice(No);
var result = prompt.Show(console);
return result == Yes;
}
}
}

View File

@ -33,12 +33,12 @@ namespace Spectre.Console
public void Clear()
{
_console.Render(_live.RestoreCursor());
_console.Write(_live.RestoreCursor());
}
public void Redraw()
{
_console.Render(new ControlSequence(string.Empty));
_console.Write(new ControlSequence(string.Empty));
}
public bool Update(ConsoleKey key)

View File

@ -59,10 +59,10 @@ namespace Spectre.Console
// Get the title and make sure it fits.
var title = GetTitleSegments(context, Title, maxWidth - extraLength);
if (Segment.CellCount(context, title) > maxWidth - extraLength)
if (Segment.CellCount(title) > maxWidth - extraLength)
{
// Truncate the title
title = Segment.TruncateWithEllipsis(title, context, maxWidth - extraLength);
title = Segment.TruncateWithEllipsis(title, maxWidth - extraLength);
if (!title.Any())
{
// We couldn't fit the title at all.
@ -83,7 +83,7 @@ namespace Spectre.Console
private IEnumerable<Segment> GetLineWithoutTitle(RenderContext context, int maxWidth)
{
var border = Border.GetSafeBorder(context.LegacyConsole || !context.Unicode);
var border = Border.GetSafeBorder(safe: !context.Unicode);
var text = border.GetPart(BoxBorderPart.Top).Repeat(maxWidth);
return new[]
@ -102,9 +102,9 @@ namespace Spectre.Console
private (Segment Left, Segment Right) GetLineSegments(RenderContext context, int width, IEnumerable<Segment> title)
{
var titleLength = Segment.CellCount(context, title);
var titleLength = Segment.CellCount(title);
var border = Border.GetSafeBorder(context.LegacyConsole || !context.Unicode);
var border = Border.GetSafeBorder(safe: !context.Unicode);
var borderPart = border.GetPart(BoxBorderPart.Top);
var alignment = Alignment ?? Justify.Center;
@ -112,7 +112,7 @@ namespace Spectre.Console
{
var left = new Segment(borderPart.Repeat(TitlePadding) + new string(' ', TitleSpacing), Style ?? Style.Plain);
var rightLength = width - titleLength - left.CellCount(context) - TitleSpacing;
var rightLength = width - titleLength - left.CellCount() - TitleSpacing;
var right = new Segment(new string(' ', TitleSpacing) + borderPart.Repeat(rightLength), Style ?? Style.Plain);
return (left, right);
@ -122,7 +122,7 @@ namespace Spectre.Console
var leftLength = ((width - titleLength) / 2) - TitleSpacing;
var left = new Segment(borderPart.Repeat(leftLength) + new string(' ', TitleSpacing), Style ?? Style.Plain);
var rightLength = width - titleLength - left.CellCount(context) - TitleSpacing;
var rightLength = width - titleLength - left.CellCount() - TitleSpacing;
var right = new Segment(new string(' ', TitleSpacing) + borderPart.Repeat(rightLength), Style ?? Style.Plain);
return (left, right);
@ -131,7 +131,7 @@ namespace Spectre.Console
{
var right = new Segment(new string(' ', TitleSpacing) + borderPart.Repeat(TitlePadding), Style ?? Style.Plain);
var leftLength = width - titleLength - right.CellCount(context) - TitleSpacing;
var leftLength = width - titleLength - right.CellCount() - TitleSpacing;
var left = new Segment(borderPart.Repeat(leftLength) + new string(' ', TitleSpacing), Style ?? Style.Plain);
return (left, right);

View File

@ -33,7 +33,7 @@ namespace Spectre.Console
var justification = context.Columns[columnIndex].Alignment;
var childContext = context.Options.WithJustification(justification);
var lines = Segment.SplitLines(context.Options, cell.Render(childContext, rowWidth));
var lines = Segment.SplitLines(cell.Render(childContext, rowWidth));
cellHeight = Math.Max(cellHeight, lines.Count);
cells.Add(lines);
}
@ -41,7 +41,7 @@ namespace Spectre.Console
// Show top of header?
if (isFirstRow && context.ShowBorder)
{
var separator = Aligner.Align(context.Options, context.Border.GetColumnRow(TablePart.Top, columnWidths, context.Columns), context.Alignment, context.MaxWidth);
var separator = Aligner.Align(context.Border.GetColumnRow(TablePart.Top, columnWidths, context.Columns), context.Alignment, context.MaxWidth);
result.Add(new Segment(separator, context.BorderStyle));
result.Add(Segment.LineBreak);
}
@ -52,7 +52,7 @@ namespace Spectre.Console
var textBorder = context.Border.GetColumnRow(TablePart.FooterSeparator, columnWidths, context.Columns);
if (!string.IsNullOrEmpty(textBorder))
{
var separator = Aligner.Align(context.Options, textBorder, context.Alignment, context.MaxWidth);
var separator = Aligner.Align(textBorder, context.Alignment, context.MaxWidth);
result.Add(new Segment(separator, context.BorderStyle));
result.Add(Segment.LineBreak);
}
@ -89,7 +89,7 @@ namespace Spectre.Console
rowResult.AddRange(cell[cellRowIndex]);
// Pad cell content right
var length = cell[cellRowIndex].Sum(segment => segment.CellCount(context.Options));
var length = cell[cellRowIndex].Sum(segment => segment.CellCount());
if (length < columnWidths[cellIndex])
{
rowResult.Add(new Segment(new string(' ', columnWidths[cellIndex] - length)));
@ -123,9 +123,9 @@ namespace Spectre.Console
Aligner.Align(context.Options, rowResult, context.Alignment, context.MaxWidth);
// Is the row larger than the allowed max width?
if (Segment.CellCount(context.Options, rowResult) > context.MaxWidth)
if (Segment.CellCount(rowResult) > context.MaxWidth)
{
result.AddRange(Segment.Truncate(context.Options, rowResult, context.MaxWidth));
result.AddRange(Segment.Truncate(rowResult, context.MaxWidth));
}
else
{
@ -138,7 +138,7 @@ namespace Spectre.Console
// Show header separator?
if (isFirstRow && context.ShowBorder && context.ShowHeaders && context.HasRows)
{
var separator = Aligner.Align(context.Options, context.Border.GetColumnRow(TablePart.HeaderSeparator, columnWidths, context.Columns), context.Alignment, context.MaxWidth);
var separator = Aligner.Align(context.Border.GetColumnRow(TablePart.HeaderSeparator, columnWidths, context.Columns), context.Alignment, context.MaxWidth);
result.Add(new Segment(separator, context.BorderStyle));
result.Add(Segment.LineBreak);
}
@ -146,7 +146,7 @@ namespace Spectre.Console
// Show bottom of footer?
if (isLastRow && context.ShowBorder)
{
var separator = Aligner.Align(context.Options, context.Border.GetColumnRow(TablePart.Bottom, columnWidths, context.Columns), context.Alignment, context.MaxWidth);
var separator = Aligner.Align(context.Border.GetColumnRow(TablePart.Bottom, columnWidths, context.Columns), context.Alignment, context.MaxWidth);
result.Add(new Segment(separator, context.BorderStyle));
result.Add(Segment.LineBreak);
}

View File

@ -47,7 +47,7 @@ namespace Spectre.Console
ShowBorder = _table.Border.Visible;
HasRows = Rows.Any(row => !row.IsHeader && !row.IsFooter);
HasFooters = Rows.Any(column => column.IsFooter);
Border = table.Border.GetSafeBorder((options.LegacyConsole || !options.Unicode) && table.UseSafeBorder);
Border = table.Border.GetSafeBorder(!options.Unicode && table.UseSafeBorder);
BorderStyle = table.BorderStyle ?? Style.Plain;
TableWidth = tableWidth;

View File

@ -91,7 +91,7 @@ namespace Spectre.Console
}
var prefix = levels.Skip(1).ToList();
var renderableLines = Segment.SplitLines(context, current.Renderable.Render(context, maxWidth - Segment.CellCount(context, prefix)));
var renderableLines = Segment.SplitLines(current.Renderable.Render(context, maxWidth - Segment.CellCount(prefix)));
foreach (var (_, isFirstLine, _, line) in renderableLines.Enumerate())
{
@ -124,7 +124,7 @@ namespace Spectre.Console
private Segment GetGuide(RenderContext context, TreeGuidePart part)
{
var guide = Guide.GetSafeTreeGuide(context.LegacyConsole || !context.Unicode);
var guide = Guide.GetSafeTreeGuide(safe: !context.Unicode);
return new Segment(guide.GetPart(part), Style ?? Style.Plain);
}
}