namespace Spectre.Console;
///
/// A column showing a spinner.
///
public sealed class SpinnerColumn : ProgressColumn
{
private const string ACCUMULATED = "SPINNER_ACCUMULATED";
private const string INDEX = "SPINNER_INDEX";
private readonly object _lock;
private Spinner _spinner;
private int? _maxWidth;
private string? _completed;
private string? _pending;
///
protected internal override bool NoWrap => true;
///
/// Gets or sets the .
///
public Spinner Spinner
{
get => _spinner;
set
{
lock (_lock)
{
_spinner = value ?? Spinner.Known.Default;
_maxWidth = null;
}
}
}
///
/// Gets or sets the text that should be shown instead
/// of the spinner once a task completes.
///
public string? CompletedText
{
get => _completed;
set
{
_completed = value;
_maxWidth = null;
}
}
///
/// Gets or sets the text that should be shown instead
/// of the spinner before a task begins.
///
public string? PendingText
{
get => _pending;
set
{
_pending = value;
_maxWidth = null;
}
}
///
/// Gets or sets the completed style.
///
public Style? CompletedStyle { get; set; }
///
/// Gets or sets the pending style.
///
public Style? PendingStyle { get; set; }
///
/// Gets or sets the style of the spinner.
///
public Style? Style { get; set; } = Color.Yellow;
///
/// Initializes a new instance of the class.
///
public SpinnerColumn()
: this(Spinner.Known.Default)
{
}
///
/// Initializes a new instance of the class.
///
/// The spinner to use.
public SpinnerColumn(Spinner spinner)
{
_spinner = spinner ?? throw new ArgumentNullException(nameof(spinner));
_lock = new object();
}
///
public override IRenderable Render(RenderOptions options, ProgressTask task, TimeSpan deltaTime)
{
var useAscii = !options.Unicode && _spinner.IsUnicode;
var spinner = useAscii ? Spinner.Known.Ascii : _spinner ?? Spinner.Known.Default;
if (!task.IsStarted)
{
return new Markup(PendingText ?? " ", PendingStyle ?? Style.Plain);
}
if (task.IsFinished)
{
return new Markup(CompletedText ?? " ", CompletedStyle ?? Style.Plain);
}
var accumulated = task.State.Update(ACCUMULATED, acc => acc + deltaTime.TotalMilliseconds);
if (accumulated >= spinner.Interval.TotalMilliseconds)
{
task.State.Update(ACCUMULATED, _ => 0);
task.State.Update(INDEX, index => index + 1);
}
var index = task.State.Get(INDEX);
var frame = spinner.Frames[index % spinner.Frames.Count];
return new Markup(frame.EscapeMarkup(), Style ?? Style.Plain);
}
///
public override int? GetColumnWidth(RenderOptions options)
{
return GetMaxWidth(options);
}
private int GetMaxWidth(RenderOptions options)
{
lock (_lock)
{
if (_maxWidth == null)
{
var useAscii = !options.Unicode && _spinner.IsUnicode;
var spinner = useAscii ? Spinner.Known.Ascii : _spinner ?? Spinner.Known.Default;
_maxWidth = Math.Max(
Math.Max(
((IRenderable)new Markup(PendingText ?? " ")).Measure(options, int.MaxValue).Max,
((IRenderable)new Markup(CompletedText ?? " ")).Measure(options, int.MaxValue).Max),
spinner.Frames.Max(frame => Cell.GetCellLength(frame)));
}
return _maxWidth.Value;
}
}
}