Add live display support

This commit also adds functionality to LiveRenderable that should
fix some problems related to vertical overflow.

Closes #316
Closes #415
This commit is contained in:
Patrik Svensson
2021-05-20 12:05:47 +02:00
committed by Phil Scott
parent 5d68020abb
commit 3dea412785
22 changed files with 756 additions and 20 deletions

View File

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using static Spectre.Console.AnsiSequences;
namespace Spectre.Console.Rendering
@ -6,12 +8,33 @@ namespace Spectre.Console.Rendering
internal sealed class LiveRenderable : Renderable
{
private readonly object _lock = new object();
private readonly IAnsiConsole _console;
private IRenderable? _renderable;
private SegmentShape? _shape;
public bool HasRenderable => _renderable != null;
public IRenderable? Target => _renderable;
public bool DidOverflow { get; private set; }
public void SetRenderable(IRenderable renderable)
[MemberNotNullWhen(true, nameof(Target))]
public bool HasRenderable => _renderable != null;
public VerticalOverflow Overflow { get; set; }
public VerticalOverflowCropping OverflowCropping { get; set; }
public LiveRenderable(IAnsiConsole console)
{
_console = console ?? throw new ArgumentNullException(nameof(console));
Overflow = VerticalOverflow.Ellipsis;
OverflowCropping = VerticalOverflowCropping.Top;
}
public LiveRenderable(IAnsiConsole console, IRenderable renderable)
: this(console)
{
_renderable = renderable ?? throw new ArgumentNullException(nameof(renderable));
}
public void SetRenderable(IRenderable? renderable)
{
lock (_lock)
{
@ -51,12 +74,61 @@ namespace Spectre.Console.Rendering
{
lock (_lock)
{
DidOverflow = false;
if (_renderable != null)
{
var segments = _renderable.Render(context, maxWidth);
var lines = Segment.SplitLines(segments);
var shape = SegmentShape.Calculate(context, lines);
if (shape.Height > _console.Profile.Height)
{
if (Overflow == VerticalOverflow.Crop)
{
if (OverflowCropping == VerticalOverflowCropping.Bottom)
{
// Remove bottom lines
var index = Math.Min(_console.Profile.Height, lines.Count);
var count = lines.Count - index;
lines.RemoveRange(index, count);
}
else
{
// Remove top lines
var start = lines.Count - _console.Profile.Height;
lines.RemoveRange(0, start);
}
shape = SegmentShape.Calculate(context, lines);
}
else if (Overflow == VerticalOverflow.Ellipsis)
{
var ellipsisText = _console.Profile.Capabilities.Unicode ? "…" : "...";
var ellipsis = new SegmentLine(((IRenderable)new Markup($"[yellow]{ellipsisText}[/]")).Render(context, maxWidth));
if (OverflowCropping == VerticalOverflowCropping.Bottom)
{
// Remove bottom lines
var index = Math.Min(_console.Profile.Height - 1, lines.Count);
var count = lines.Count - index;
lines.RemoveRange(index, count);
lines.Add(ellipsis);
}
else
{
// Remove top lines
var start = lines.Count - _console.Profile.Height;
lines.RemoveRange(0, start + 1);
lines.Insert(0, ellipsis);
}
shape = SegmentShape.Calculate(context, lines);
}
DidOverflow = true;
}
_shape = _shape == null ? shape : _shape.Value.Inflate(shape);
_shape.Value.Apply(context, ref lines);

View File

@ -13,6 +13,22 @@ namespace Spectre.Console.Rendering
/// </summary>
public int Length => this.Sum(line => line.Text.Length);
/// <summary>
/// Initializes a new instance of the <see cref="SegmentLine"/> class.
/// </summary>
public SegmentLine()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="SegmentLine"/> class.
/// </summary>
/// <param name="segments">The segments.</param>
public SegmentLine(IEnumerable<Segment> segments)
: base(segments)
{
}
/// <summary>
/// Gets the number of cells the segment line occupies.
/// </summary>