mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-06-19 13:28:16 +08:00
Add progress task list support
This commit is contained in:

committed by
Patrik Svensson

parent
c61e386440
commit
ae32785f21
18
src/Spectre.Console/Rendering/IRenderHook.cs
Normal file
18
src/Spectre.Console/Rendering/IRenderHook.cs
Normal file
@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Spectre.Console.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a render hook.
|
||||
/// </summary>
|
||||
public interface IRenderHook
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes the specified renderables.
|
||||
/// </summary>
|
||||
/// <param name="context">The render context.</param>
|
||||
/// <param name="renderables">The renderables to process.</param>
|
||||
/// <returns>The processed renderables.</returns>
|
||||
IEnumerable<IRenderable> Process(RenderContext context, IEnumerable<IRenderable> renderables);
|
||||
}
|
||||
}
|
81
src/Spectre.Console/Rendering/LiveRenderable.cs
Normal file
81
src/Spectre.Console/Rendering/LiveRenderable.cs
Normal file
@ -0,0 +1,81 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Spectre.Console.Internal;
|
||||
using Spectre.Console.Rendering;
|
||||
|
||||
namespace Spectre.Console.Rendering
|
||||
{
|
||||
internal sealed class LiveRenderable : Renderable
|
||||
{
|
||||
private readonly object _lock = new object();
|
||||
private IRenderable? _renderable;
|
||||
private int? _height;
|
||||
|
||||
public void SetRenderable(IRenderable renderable)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_renderable = renderable;
|
||||
}
|
||||
}
|
||||
|
||||
public IRenderable PositionCursor()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_height == null)
|
||||
{
|
||||
return new ControlSequence(string.Empty);
|
||||
}
|
||||
|
||||
return new ControlSequence("\r" + "\u001b[1A".Repeat(_height.Value - 1));
|
||||
}
|
||||
}
|
||||
|
||||
public IRenderable RestoreCursor()
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_height == null)
|
||||
{
|
||||
return new ControlSequence(string.Empty);
|
||||
}
|
||||
|
||||
return new ControlSequence("\r\u001b[2K" + "\u001b[1A\u001b[2K".Repeat(_height.Value - 1));
|
||||
}
|
||||
}
|
||||
|
||||
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
if (_renderable != null)
|
||||
{
|
||||
var segments = _renderable.Render(context, maxWidth);
|
||||
var lines = Segment.SplitLines(context, segments);
|
||||
|
||||
_height = lines.Count;
|
||||
|
||||
var result = new List<Segment>();
|
||||
foreach (var (_, _, last, line) in lines.Enumerate())
|
||||
{
|
||||
foreach (var item in line)
|
||||
{
|
||||
result.Add(item);
|
||||
}
|
||||
|
||||
if (!last)
|
||||
{
|
||||
result.Add(Segment.LineBreak);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
_height = 0;
|
||||
return Enumerable.Empty<Segment>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -49,7 +49,7 @@ namespace Spectre.Console.Rendering
|
||||
Encoding = encoding ?? throw new System.ArgumentNullException(nameof(encoding));
|
||||
LegacyConsole = legacyConsole;
|
||||
Justification = justification;
|
||||
Unicode = Encoding == Encoding.UTF8 || Encoding == Encoding.Unicode;
|
||||
Unicode = Encoding.EncodingName.ContainsExact("Unicode");
|
||||
SingleLine = singleLine;
|
||||
}
|
||||
|
||||
|
31
src/Spectre.Console/Rendering/RenderHookScope.cs
Normal file
31
src/Spectre.Console/Rendering/RenderHookScope.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace Spectre.Console.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a render hook scope.
|
||||
/// </summary>
|
||||
public sealed class RenderHookScope : IDisposable
|
||||
{
|
||||
private readonly IAnsiConsole _console;
|
||||
private readonly IRenderHook _hook;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RenderHookScope"/> class.
|
||||
/// </summary>
|
||||
/// <param name="console">The console to attach the render hook to.</param>
|
||||
/// <param name="hook">The render hook.</param>
|
||||
public RenderHookScope(IAnsiConsole console, IRenderHook hook)
|
||||
{
|
||||
_console = console ?? throw new ArgumentNullException(nameof(console));
|
||||
_hook = hook ?? throw new ArgumentNullException(nameof(hook));
|
||||
_console.Pipeline.Attach(_hook);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
_console.Pipeline.Detach(_hook);
|
||||
}
|
||||
}
|
||||
}
|
66
src/Spectre.Console/Rendering/RenderPipeline.cs
Normal file
66
src/Spectre.Console/Rendering/RenderPipeline.cs
Normal file
@ -0,0 +1,66 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Spectre.Console.Rendering
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the render pipeline.
|
||||
/// </summary>
|
||||
public sealed class RenderPipeline
|
||||
{
|
||||
private readonly List<IRenderHook> _hooks;
|
||||
private readonly object _lock;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RenderPipeline"/> class.
|
||||
/// </summary>
|
||||
public RenderPipeline()
|
||||
{
|
||||
_hooks = new List<IRenderHook>();
|
||||
_lock = new object();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attaches a new render hook onto the pipeline.
|
||||
/// </summary>
|
||||
/// <param name="hook">The render hook to attach.</param>
|
||||
public void Attach(IRenderHook hook)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_hooks.Add(hook);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detaches a render hook from the pipeline.
|
||||
/// </summary>
|
||||
/// <param name="hook">The render hook to detach.</param>
|
||||
public void Detach(IRenderHook hook)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
_hooks.Remove(hook);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Processes the specified renderables.
|
||||
/// </summary>
|
||||
/// <param name="context">The render context.</param>
|
||||
/// <param name="renderables">The renderables to process.</param>
|
||||
/// <returns>The processed renderables.</returns>
|
||||
public IEnumerable<IRenderable> Process(RenderContext context, IEnumerable<IRenderable> renderables)
|
||||
{
|
||||
lock (_lock)
|
||||
{
|
||||
var current = renderables;
|
||||
for (var index = _hooks.Count - 1; index >= 0; index--)
|
||||
{
|
||||
current = _hooks[index].Process(context, current);
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -72,7 +72,7 @@ namespace Spectre.Console.Rendering
|
||||
|
||||
private Segment(string text, Style style, bool lineBreak, bool control)
|
||||
{
|
||||
Text = text?.NormalizeLineEndings() ?? throw new ArgumentNullException(nameof(text));
|
||||
Text = text?.NormalizeNewLines() ?? throw new ArgumentNullException(nameof(text));
|
||||
Style = style ?? throw new ArgumentNullException(nameof(style));
|
||||
IsLineBreak = lineBreak;
|
||||
IsWhiteSpace = string.IsNullOrWhiteSpace(text);
|
||||
@ -102,6 +102,11 @@ namespace Spectre.Console.Rendering
|
||||
throw new ArgumentNullException(nameof(context));
|
||||
}
|
||||
|
||||
if (IsControlCode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Text.CellLength(context);
|
||||
}
|
||||
|
||||
@ -477,16 +482,22 @@ namespace Spectre.Console.Rendering
|
||||
continue;
|
||||
}
|
||||
|
||||
// Both control codes?
|
||||
if (segment.IsControlCode && previous.IsControlCode)
|
||||
{
|
||||
previous = Control(previous.Text + segment.Text);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Same style?
|
||||
if (previous.Style.Equals(segment.Style) && !previous.IsLineBreak)
|
||||
if (previous.Style.Equals(segment.Style) && !previous.IsLineBreak && !previous.IsControlCode)
|
||||
{
|
||||
previous = new Segment(previous.Text + segment.Text, previous.Style);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Add(previous);
|
||||
previous = segment;
|
||||
}
|
||||
|
||||
result.Add(previous);
|
||||
previous = segment;
|
||||
}
|
||||
|
||||
if (previous != null)
|
||||
|
Reference in New Issue
Block a user