diff --git a/docs/input/appendix/spinners.md b/docs/input/appendix/spinners.md new file mode 100644 index 0000000..50b95bd --- /dev/null +++ b/docs/input/appendix/spinners.md @@ -0,0 +1,43 @@ +Title: Spinners +Order: 4 +--- + +For all available spinners, see https://jsfiddle.net/sindresorhus/2eLtsbey/embedded/result/ + +# Usage + +Spinners can be used with [Progress](xref:progress) and [Status](xref:status). + +```csharp +AnsiConsole.Status() + .Spinner(Spinner.Known.Star) + .Start("Thinking...", ctx => { + // Omitted + }); +``` + +# Implementing a spinner + +To implement your own spinner, all you have to do is +inherit from the `Spinner` base class. + +In the example below, the spinner will alterate between +the characters `A`, `B` and `C` every 100 ms. + +```csharp +public sealed class MySpinner : Spinner +{ + // The interval for each frame + public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); + + // Whether or not the spinner contains unicode characters + public override bool IsUnicode => false; + + // The individual frames of the spinner + public override IReadOnlyList Frames => + new List + { + "A", "B", "C", + }; +} +``` \ No newline at end of file diff --git a/docs/input/assets/images/status.gif b/docs/input/assets/images/status.gif new file mode 100644 index 0000000..2cf40f5 Binary files /dev/null and b/docs/input/assets/images/status.gif differ diff --git a/docs/input/assets/js/table-search.js b/docs/input/assets/js/table-search.js index e60256d..8ae2e2e 100644 --- a/docs/input/assets/js/table-search.js +++ b/docs/input/assets/js/table-search.js @@ -31,5 +31,4 @@ $(document).ready(function () { }; // keyup }) - }); // ready \ No newline at end of file diff --git a/docs/input/status.md b/docs/input/status.md new file mode 100644 index 0000000..8ad0307 --- /dev/null +++ b/docs/input/status.md @@ -0,0 +1,60 @@ +Title: Status +Order: 6 +--- + +Spectre.Console can display information about long running tasks in the console. + + + +If the current terminal isn't considered "interactive", such as when running +in a continuous integration system, or the terminal can't display +ANSI control sequence, any progress will be displayed in a simpler way. + +# Usage + +```csharp +// Synchronous +AnsiConsole.Status() + .Start("Thinking...", ctx => + { + // Simulate some work + AnsiConsole.MarkupLine("Doing some work..."); + Thread.Sleep(1000); + + // Update the status and spinner + ctx.Status("Thinking some more"); + ctx.Spinner(Spinner.Known.Star); + ctx.SpinnerStyle(Style.Parse("green")); + + // Simulate some work + AnsiConsole.MarkupLine("Doing some more work..."); + Thread.Sleep(2000); + }); +``` + +## Asynchronous progress + +If you prefer to use async/await, you can use `StartAsync` instead of `Start`. + +```csharp +// Asynchronous +await AnsiConsole.Status() + .StartAsync("Thinking...", async ctx => + { + // Omitted + }); +``` + +# Configure + +```csharp +AnsiConsole.Status() + .AutoRefresh(false) + .Spinner(Spinner.Known.Star) + .SpinnerStyle(Style.Parse("green bold")) + .Start("Thinking...", ctx => + { + // Omitted + ctx.Refresh(); + }); +``` \ No newline at end of file diff --git a/examples/Status/Program.cs b/examples/Status/Program.cs new file mode 100644 index 0000000..36fde30 --- /dev/null +++ b/examples/Status/Program.cs @@ -0,0 +1,70 @@ +using System.Threading; +using Spectre.Console; + +namespace ProgressExample +{ + public static class Program + { + public static void Main() + { + AnsiConsole.Status() + .AutoRefresh(true) + .Spinner(Spinner.Known.Default) + .Start("[yellow]Initializing warp drive[/]", ctx => + { + // Initialize + Thread.Sleep(3000); + WriteLogMessage("Starting gravimetric field displacement manifold"); + Thread.Sleep(1000); + WriteLogMessage("Warming up deuterium chamber"); + Thread.Sleep(2000); + WriteLogMessage("Generating antideuterium"); + + // Warp nacelles + Thread.Sleep(3000); + ctx.Spinner(Spinner.Known.BouncingBar); + ctx.Status("[bold blue]Unfolding warp nacelles[/]"); + WriteLogMessage("Unfolding left warp nacelle"); + Thread.Sleep(2000); + WriteLogMessage("Left warp nacelle [green]online[/]"); + WriteLogMessage("Unfolding right warp nacelle"); + Thread.Sleep(1000); + WriteLogMessage("Right warp nacelle [green]online[/]"); + + // Warp bubble + Thread.Sleep(3000); + ctx.Spinner(Spinner.Known.Star2); + ctx.Status("[bold blue]Generating warp bubble[/]"); + Thread.Sleep(3000); + ctx.Spinner(Spinner.Known.Star); + ctx.Status("[bold blue]Stabilizing warp bubble[/]"); + + // Safety + ctx.Spinner(Spinner.Known.Monkey); + ctx.Status("[bold blue]Performing safety checks[/]"); + WriteLogMessage("Enabling interior dampening"); + Thread.Sleep(2000); + WriteLogMessage("Interior dampening [green]enabled[/]"); + + // Warp! + Thread.Sleep(3000); + ctx.Spinner(Spinner.Known.Moon); + WriteLogMessage("Preparing for warp"); + Thread.Sleep(1000); + for (var warp = 1; warp < 10; warp++) + { + ctx.Status($"[bold blue]Warp {warp}[/]"); + Thread.Sleep(500); + } + }); + + // Done + AnsiConsole.MarkupLine("[bold green]Crusing at Warp 9.8[/]"); + } + + private static void WriteLogMessage(string message) + { + AnsiConsole.MarkupLine($"[grey]LOG:[/] {message}[grey]...[/]"); + } + } +} diff --git a/examples/Status/Status.csproj b/examples/Status/Status.csproj new file mode 100644 index 0000000..16128da --- /dev/null +++ b/examples/Status/Status.csproj @@ -0,0 +1,19 @@ + + + + Exe + net5.0 + false + Status + Demonstrates how to show status updates. + + + + + + + + + + + diff --git a/resources/scripts/Generate-Spinners.ps1 b/resources/scripts/Generate-Spinners.ps1 index 8e08fdf..557cb44 100644 --- a/resources/scripts/Generate-Spinners.ps1 +++ b/resources/scripts/Generate-Spinners.ps1 @@ -19,4 +19,4 @@ if(!$?) { Pop-Location # Copy the files to the correct location -Copy-Item (Join-Path "$Output" "ProgressSpinner.Generated.cs") -Destination "$Source/Progress/ProgressSpinner.Generated.cs" +Copy-Item (Join-Path "$Output" "Spinner.Generated.cs") -Destination "$Source/Progress/Spinner.Generated.cs" diff --git a/resources/scripts/Generator/Commands/SpinnerGeneratorCommand.cs b/resources/scripts/Generator/Commands/SpinnerGeneratorCommand.cs index 1fb03eb..ddad247 100644 --- a/resources/scripts/Generator/Commands/SpinnerGeneratorCommand.cs +++ b/resources/scripts/Generator/Commands/SpinnerGeneratorCommand.cs @@ -21,8 +21,8 @@ namespace Generator.Commands { // Read the spinner model. var spinners = new List(); - spinners.AddRange(Spinner.Parse(File.ReadAllText("Data/spinners.json"))); spinners.AddRange(Spinner.Parse(File.ReadAllText("Data/spinners_default.json"))); + spinners.AddRange(Spinner.Parse(File.ReadAllText("Data/spinners_sindresorhus.json"))); var output = new DirectoryPath(settings.Output); if (!_fileSystem.Directory.Exists(settings.Output)) @@ -31,7 +31,7 @@ namespace Generator.Commands } // Parse the Scriban template. - var templatePath = new FilePath("Templates/ProgressSpinner.Generated.template"); + var templatePath = new FilePath("Templates/Spinner.Generated.template"); var template = Template.Parse(File.ReadAllText(templatePath.FullPath)); // Render the template with the model. diff --git a/resources/scripts/Generator/Data/spinners.json b/resources/scripts/Generator/Data/spinners_sindresorhus.json similarity index 100% rename from resources/scripts/Generator/Data/spinners.json rename to resources/scripts/Generator/Data/spinners_sindresorhus.json diff --git a/resources/scripts/Generator/Generator.csproj b/resources/scripts/Generator/Generator.csproj index c20e1b6..54c08fc 100644 --- a/resources/scripts/Generator/Generator.csproj +++ b/resources/scripts/Generator/Generator.csproj @@ -15,7 +15,10 @@ Always - + + Always + + Always @@ -27,7 +30,7 @@ Always - + Always diff --git a/resources/scripts/Generator/Templates/Color.Generated.template b/resources/scripts/Generator/Templates/Color.Generated.template index aaf51da..7f3d18f 100644 --- a/resources/scripts/Generator/Templates/Color.Generated.template +++ b/resources/scripts/Generator/Templates/Color.Generated.template @@ -2,7 +2,6 @@ // // This code was generated by a tool. // Generated {{ date.now | date.to_string `%F %R` }} -// Generated from https://github.com/sindresorhus/cli-spinners/blob/master/spinners.json // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/resources/scripts/Generator/Templates/ProgressSpinner.Generated.template b/resources/scripts/Generator/Templates/Spinner.Generated.template similarity index 82% rename from resources/scripts/Generator/Templates/ProgressSpinner.Generated.template rename to resources/scripts/Generator/Templates/Spinner.Generated.template index 340199d..ffc12a0 100644 --- a/resources/scripts/Generator/Templates/ProgressSpinner.Generated.template +++ b/resources/scripts/Generator/Templates/Spinner.Generated.template @@ -3,6 +3,9 @@ // This code was generated by a tool. // Generated {{ date.now | date.to_string `%F %R` }} // +// Partly generated from +// https://github.com/sindresorhus/cli-spinners/blob/master/spinners.json +// // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. // @@ -13,10 +16,10 @@ using System.Collections.Generic; namespace Spectre.Console { - public abstract partial class ProgressSpinner + public abstract partial class Spinner { {{~ for spinner in spinners ~}} - private sealed class {{ spinner.normalized_name }}Spinner : ProgressSpinner + private sealed class {{ spinner.normalized_name }}Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds({{ spinner.interval }}); public override bool IsUnicode => {{ spinner.unicode }}; @@ -38,7 +41,7 @@ namespace Spectre.Console /// /// Gets the "{{ spinner.name }}" spinner. /// - public static ProgressSpinner {{ spinner.normalized_name }} { get; } = new {{ spinner.normalized_name }}Spinner(); + public static Spinner {{ spinner.normalized_name }} { get; } = new {{ spinner.normalized_name }}Spinner(); {{~ end ~}} } } diff --git a/src/Spectre.Console.Tests/Tools/DummySpinners.cs b/src/Spectre.Console.Tests/Tools/DummySpinners.cs new file mode 100644 index 0000000..63444c9 --- /dev/null +++ b/src/Spectre.Console.Tests/Tools/DummySpinners.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; + +namespace Spectre.Console.Tests +{ + public sealed class DummySpinner1 : Spinner + { + public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); + public override bool IsUnicode => true; + public override IReadOnlyList Frames => new List + { + "*", + }; + } + + public sealed class DummySpinner2 : Spinner + { + public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); + public override bool IsUnicode => true; + public override IReadOnlyList Frames => new List + { + "-", + }; + } +} diff --git a/src/Spectre.Console.Tests/Unit/StatusTests.cs b/src/Spectre.Console.Tests/Unit/StatusTests.cs new file mode 100644 index 0000000..ed191ef --- /dev/null +++ b/src/Spectre.Console.Tests/Unit/StatusTests.cs @@ -0,0 +1,42 @@ +using Shouldly; +using Xunit; + +namespace Spectre.Console.Tests.Unit +{ + public sealed partial class StatusTests + { + [Fact] + public void Should_Render_Status_Correctly() + { + // Given + var console = new TestableAnsiConsole(ColorSystem.TrueColor, width: 10); + + var status = new Status(console); + status.AutoRefresh = false; + status.Spinner = new DummySpinner1(); + + // When + status.Start("foo", ctx => + { + ctx.Refresh(); + ctx.Spinner(new DummySpinner2()); + ctx.Status("bar"); + ctx.Refresh(); + ctx.Spinner(new DummySpinner1()); + ctx.Status("baz"); + }); + + // Then + console.Output + .NormalizeLineEndings() + .ShouldBe( + "[?25l \n" + + "* foo\n" + + "  \n" + + "- bar\n" + + "  \n" + + "* baz\n" + + " [?25h"); + } + } +} diff --git a/src/Spectre.Console.sln b/src/Spectre.Console.sln index c4ef646..43fdbfa 100644 --- a/src/Spectre.Console.sln +++ b/src/Spectre.Console.sln @@ -60,6 +60,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.ImageSharp" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Progress", "..\examples\Progress\Progress.csproj", "{2B712A52-40F1-4C1C-833E-7C869ACA91F3}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Status", "..\examples\Status\Status.csproj", "{3716AFDF-0904-4635-8422-86E6B9356840}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -310,6 +312,18 @@ Global {2B712A52-40F1-4C1C-833E-7C869ACA91F3}.Release|x64.Build.0 = Release|Any CPU {2B712A52-40F1-4C1C-833E-7C869ACA91F3}.Release|x86.ActiveCfg = Release|Any CPU {2B712A52-40F1-4C1C-833E-7C869ACA91F3}.Release|x86.Build.0 = Release|Any CPU + {3716AFDF-0904-4635-8422-86E6B9356840}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3716AFDF-0904-4635-8422-86E6B9356840}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3716AFDF-0904-4635-8422-86E6B9356840}.Debug|x64.ActiveCfg = Debug|Any CPU + {3716AFDF-0904-4635-8422-86E6B9356840}.Debug|x64.Build.0 = Debug|Any CPU + {3716AFDF-0904-4635-8422-86E6B9356840}.Debug|x86.ActiveCfg = Debug|Any CPU + {3716AFDF-0904-4635-8422-86E6B9356840}.Debug|x86.Build.0 = Debug|Any CPU + {3716AFDF-0904-4635-8422-86E6B9356840}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3716AFDF-0904-4635-8422-86E6B9356840}.Release|Any CPU.Build.0 = Release|Any CPU + {3716AFDF-0904-4635-8422-86E6B9356840}.Release|x64.ActiveCfg = Release|Any CPU + {3716AFDF-0904-4635-8422-86E6B9356840}.Release|x64.Build.0 = Release|Any CPU + {3716AFDF-0904-4635-8422-86E6B9356840}.Release|x86.ActiveCfg = Release|Any CPU + {3716AFDF-0904-4635-8422-86E6B9356840}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -333,6 +347,7 @@ Global {45BF6302-6553-4E52-BF0F-B10D1AA9A6D1} = {F0575243-121F-4DEE-9F6B-246E26DC0844} {5693761A-754A-40A8-9144-36510D6A4D69} = {F0575243-121F-4DEE-9F6B-246E26DC0844} {2B712A52-40F1-4C1C-833E-7C869ACA91F3} = {F0575243-121F-4DEE-9F6B-246E26DC0844} + {3716AFDF-0904-4635-8422-86E6B9356840} = {F0575243-121F-4DEE-9F6B-246E26DC0844} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C} diff --git a/src/Spectre.Console/AnsiConsole.Progress.cs b/src/Spectre.Console/AnsiConsole.Progress.cs index 835fcec..c73d592 100644 --- a/src/Spectre.Console/AnsiConsole.Progress.cs +++ b/src/Spectre.Console/AnsiConsole.Progress.cs @@ -13,5 +13,14 @@ namespace Spectre.Console { return Console.Progress(); } + + /// + /// Creates a new instance. + /// + /// A instance. + public static Status Status() + { + return Console.Status(); + } } } diff --git a/src/Spectre.Console/Extensions/AnsiConsoleExtensions.Progress.cs b/src/Spectre.Console/Extensions/AnsiConsoleExtensions.Progress.cs index 69a82dc..1868d4c 100644 --- a/src/Spectre.Console/Extensions/AnsiConsoleExtensions.Progress.cs +++ b/src/Spectre.Console/Extensions/AnsiConsoleExtensions.Progress.cs @@ -21,5 +21,20 @@ namespace Spectre.Console return new Progress(console); } + + /// + /// Creates a new instance for the console. + /// + /// The console. + /// A instance. + public static Status Status(this IAnsiConsole console) + { + if (console is null) + { + throw new ArgumentNullException(nameof(console)); + } + + return new Status(console); + } } } diff --git a/src/Spectre.Console/Extensions/Columns/SpinnerColumnExtensions.cs b/src/Spectre.Console/Extensions/Columns/SpinnerColumnExtensions.cs index c33c792..bfa6a13 100644 --- a/src/Spectre.Console/Extensions/Columns/SpinnerColumnExtensions.cs +++ b/src/Spectre.Console/Extensions/Columns/SpinnerColumnExtensions.cs @@ -13,19 +13,18 @@ namespace Spectre.Console /// The column. /// The style. /// The same instance so that multiple calls can be chained. - public static SpinnerColumn Style(this SpinnerColumn column, Style style) + public static SpinnerColumn Style(this SpinnerColumn column, Style? style) { if (column is null) { throw new ArgumentNullException(nameof(column)); } - if (style is null) + if (style != null) { - throw new ArgumentNullException(nameof(style)); + column.Style = style; } - column.Style = style; return column; } } diff --git a/src/Spectre.Console/Extensions/StatusContextExtensions.cs b/src/Spectre.Console/Extensions/StatusContextExtensions.cs new file mode 100644 index 0000000..a4d4f95 --- /dev/null +++ b/src/Spectre.Console/Extensions/StatusContextExtensions.cs @@ -0,0 +1,61 @@ +using System; + +namespace Spectre.Console +{ + /// + /// Contains extension methods for . + /// + public static class StatusContextExtensions + { + /// + /// Sets the status message. + /// + /// The status context. + /// The status message. + /// The same instance so that multiple calls can be chained. + public static StatusContext Status(this StatusContext context, string status) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + context.Status = status; + return context; + } + + /// + /// Sets the spinner. + /// + /// The status context. + /// The spinner. + /// The same instance so that multiple calls can be chained. + public static StatusContext Spinner(this StatusContext context, Spinner spinner) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + context.Spinner = spinner; + return context; + } + + /// + /// Sets the spinner style. + /// + /// The status context. + /// The spinner style. + /// The same instance so that multiple calls can be chained. + public static StatusContext SpinnerStyle(this StatusContext context, Style? style) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + context.SpinnerStyle = style; + return context; + } + } +} diff --git a/src/Spectre.Console/Extensions/StatusExtensions.cs b/src/Spectre.Console/Extensions/StatusExtensions.cs new file mode 100644 index 0000000..ab0a996 --- /dev/null +++ b/src/Spectre.Console/Extensions/StatusExtensions.cs @@ -0,0 +1,62 @@ +using System; + +namespace Spectre.Console +{ + /// + /// Contains extension methods for . + /// + public static class StatusExtensions + { + /// + /// Sets whether or not auto refresh is enabled. + /// If disabled, you will manually have to refresh the progress. + /// + /// The instance. + /// Whether or not auto refresh is enabled. + /// The same instance so that multiple calls can be chained. + public static Status AutoRefresh(this Status status, bool enabled) + { + if (status is null) + { + throw new ArgumentNullException(nameof(status)); + } + + status.AutoRefresh = enabled; + return status; + } + + /// + /// Sets the spinner. + /// + /// The instance. + /// The spinner. + /// The same instance so that multiple calls can be chained. + public static Status Spinner(this Status status, Spinner spinner) + { + if (status is null) + { + throw new ArgumentNullException(nameof(status)); + } + + status.Spinner = spinner; + return status; + } + + /// + /// Sets the spinner style. + /// + /// The instance. + /// The spinner style. + /// The same instance so that multiple calls can be chained. + public static Status SpinnerStyle(this Status status, Style? style) + { + if (status is null) + { + throw new ArgumentNullException(nameof(status)); + } + + status.SpinnerStyle = style; + return status; + } + } +} diff --git a/src/Spectre.Console/Progress/Columns/PercentageColumn.cs b/src/Spectre.Console/Progress/Columns/PercentageColumn.cs index 382751c..58d24ef 100644 --- a/src/Spectre.Console/Progress/Columns/PercentageColumn.cs +++ b/src/Spectre.Console/Progress/Columns/PercentageColumn.cs @@ -8,9 +8,6 @@ namespace Spectre.Console /// public sealed class PercentageColumn : ProgressColumn { - /// - protected internal override int? ColumnWidth => 4; - /// /// Gets or sets the style for a non-complete task. /// @@ -28,5 +25,11 @@ namespace Spectre.Console var style = percentage == 100 ? CompletedStyle : Style ?? Style.Plain; return new Text($"{percentage}%", style).RightAligned(); } + + /// + public override int? GetColumnWidth(RenderContext context) + { + return 4; + } } } diff --git a/src/Spectre.Console/Progress/Columns/RemainingTimeColumn.cs b/src/Spectre.Console/Progress/Columns/RemainingTimeColumn.cs index 3b008d3..2864de1 100644 --- a/src/Spectre.Console/Progress/Columns/RemainingTimeColumn.cs +++ b/src/Spectre.Console/Progress/Columns/RemainingTimeColumn.cs @@ -8,9 +8,6 @@ namespace Spectre.Console /// public sealed class RemainingTimeColumn : ProgressColumn { - /// - protected internal override int? ColumnWidth => 7; - /// protected internal override bool NoWrap => true; @@ -30,5 +27,11 @@ namespace Spectre.Console return new Text($"{remaining.Value:h\\:mm\\:ss}", Style ?? Style.Plain); } + + /// + public override int? GetColumnWidth(RenderContext context) + { + return 7; + } } } diff --git a/src/Spectre.Console/Progress/Columns/SpinnerColumn.cs b/src/Spectre.Console/Progress/Columns/SpinnerColumn.cs index ab265b0..480c3af 100644 --- a/src/Spectre.Console/Progress/Columns/SpinnerColumn.cs +++ b/src/Spectre.Console/Progress/Columns/SpinnerColumn.cs @@ -13,25 +13,39 @@ namespace Spectre.Console private const string ACCUMULATED = "SPINNER_ACCUMULATED"; private const string INDEX = "SPINNER_INDEX"; - private readonly ProgressSpinner _spinner; - private int? _maxLength; - - /// - protected internal override int? ColumnWidth => 1; + private readonly object _lock; + private Spinner _spinner; + private int? _maxWidth; /// 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 style of the spinner. /// - public Style Style { get; set; } = new Style(foreground: Color.Yellow); + public Style? Style { get; set; } = new Style(foreground: Color.Yellow); /// /// Initializes a new instance of the class. /// public SpinnerColumn() - : this(ProgressSpinner.Known.Default) + : this(Spinner.Known.Default) { } @@ -39,36 +53,55 @@ namespace Spectre.Console /// Initializes a new instance of the class. /// /// The spinner to use. - public SpinnerColumn(ProgressSpinner spinner) + public SpinnerColumn(Spinner spinner) { _spinner = spinner ?? throw new ArgumentNullException(nameof(spinner)); + _lock = new object(); } /// public override IRenderable Render(RenderContext context, ProgressTask task, TimeSpan deltaTime) { var useAscii = (context.LegacyConsole || !context.Unicode) && _spinner.IsUnicode; - var spinner = useAscii ? ProgressSpinner.Known.Ascii : _spinner; + var spinner = useAscii ? Spinner.Known.Ascii : _spinner ?? Spinner.Known.Default; if (!task.IsStarted || task.IsFinished) { - if (_maxLength == null) - { - _maxLength = _spinner.Frames.Max(frame => Cell.GetCellLength(context, frame)); - } - - return new Markup(new string(' ', _maxLength.Value)); + return new Markup(new string(' ', GetMaxWidth(context))); } var accumulated = task.State.Update(ACCUMULATED, acc => acc + deltaTime.TotalMilliseconds); - if (accumulated >= _spinner.Interval.TotalMilliseconds) + if (accumulated >= spinner.Interval.TotalMilliseconds) { task.State.Update(ACCUMULATED, _ => 0); task.State.Update(INDEX, index => index + 1); } var index = task.State.Get(INDEX); - return new Markup(spinner.Frames[index % spinner.Frames.Count], Style ?? Style.Plain); + var frame = spinner.Frames[index % spinner.Frames.Count]; + return new Markup(frame.EscapeMarkup(), Style ?? Style.Plain); + } + + /// + public override int? GetColumnWidth(RenderContext context) + { + return GetMaxWidth(context); + } + + private int GetMaxWidth(RenderContext context) + { + lock (_lock) + { + if (_maxWidth == null) + { + var useAscii = (context.LegacyConsole || !context.Unicode) && _spinner.IsUnicode; + var spinner = useAscii ? Spinner.Known.Ascii : _spinner ?? Spinner.Known.Default; + + _maxWidth = spinner.Frames.Max(frame => Cell.GetCellLength(context, frame)); + } + + return _maxWidth.Value; + } } } } diff --git a/src/Spectre.Console/Progress/Progress.cs b/src/Spectre.Console/Progress/Progress.cs index 27e5c2b..76d6d0e 100644 --- a/src/Spectre.Console/Progress/Progress.cs +++ b/src/Spectre.Console/Progress/Progress.cs @@ -34,6 +34,8 @@ namespace Spectre.Console internal List Columns { get; } + internal ProgressRenderer? FallbackRenderer { get; set; } + /// /// Initializes a new instance of the class. /// @@ -116,11 +118,11 @@ namespace Spectre.Console if (interactive) { var columns = new List(Columns); - return new InteractiveProgressRenderer(_console, columns, RefreshRate); + return new DefaultProgressRenderer(_console, columns, RefreshRate); } else { - return new NonInteractiveProgressRenderer(); + return FallbackRenderer ?? new FallbackProgressRenderer(); } } } diff --git a/src/Spectre.Console/Progress/ProgressColumn.cs b/src/Spectre.Console/Progress/ProgressColumn.cs index afd3b9a..5807260 100644 --- a/src/Spectre.Console/Progress/ProgressColumn.cs +++ b/src/Spectre.Console/Progress/ProgressColumn.cs @@ -13,11 +13,6 @@ namespace Spectre.Console /// protected internal virtual bool NoWrap { get; } - /// - /// Gets the requested column width for the column. - /// - protected internal virtual int? ColumnWidth { get; } - /// /// Gets a renderable representing the column. /// @@ -26,5 +21,15 @@ namespace Spectre.Console /// The elapsed time since last call. /// A renderable representing the column. public abstract IRenderable Render(RenderContext context, ProgressTask task, TimeSpan deltaTime); + + /// + /// Gets the width of the column. + /// + /// The context. + /// The width of the column, or null to calculate. + public virtual int? GetColumnWidth(RenderContext context) + { + return null; + } } } diff --git a/src/Spectre.Console/Progress/ProgressContext.cs b/src/Spectre.Console/Progress/ProgressContext.cs index 4b57c2c..b85fe37 100644 --- a/src/Spectre.Console/Progress/ProgressContext.cs +++ b/src/Spectre.Console/Progress/ProgressContext.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using Spectre.Console.Internal; namespace Spectre.Console @@ -21,6 +22,8 @@ namespace Spectre.Console /// public bool IsFinished => _tasks.All(task => task.IsFinished); + internal Encoding Encoding => _console.Encoding; + internal ProgressContext(IAnsiConsole console, ProgressRenderer renderer) { _tasks = new List(); @@ -56,14 +59,11 @@ namespace Spectre.Console _console.Render(new ControlSequence(string.Empty)); } - internal void EnumerateTasks(Action action) + internal IReadOnlyList GetTasks() { lock (_taskLock) { - foreach (var task in _tasks) - { - action(task); - } + return new List(_tasks); } } } diff --git a/src/Spectre.Console/Progress/Renderers/InteractiveProgressRenderer.cs b/src/Spectre.Console/Progress/Renderers/DefaultProgressRenderer.cs similarity index 87% rename from src/Spectre.Console/Progress/Renderers/InteractiveProgressRenderer.cs rename to src/Spectre.Console/Progress/Renderers/DefaultProgressRenderer.cs index 85c0f1c..586ded4 100644 --- a/src/Spectre.Console/Progress/Renderers/InteractiveProgressRenderer.cs +++ b/src/Spectre.Console/Progress/Renderers/DefaultProgressRenderer.cs @@ -6,7 +6,7 @@ using Spectre.Console.Rendering; namespace Spectre.Console.Internal { - internal sealed class InteractiveProgressRenderer : ProgressRenderer + internal sealed class DefaultProgressRenderer : ProgressRenderer { private readonly IAnsiConsole _console; private readonly List _columns; @@ -17,7 +17,7 @@ namespace Spectre.Console.Internal public override TimeSpan RefreshRate { get; } - public InteractiveProgressRenderer(IAnsiConsole console, List columns, TimeSpan refreshRate) + public DefaultProgressRenderer(IAnsiConsole console, List columns, TimeSpan refreshRate) { _console = console ?? throw new ArgumentNullException(nameof(console)); _columns = columns ?? throw new ArgumentNullException(nameof(columns)); @@ -60,6 +60,8 @@ namespace Spectre.Console.Internal _stopwatch.Start(); } + var renderContext = new RenderContext(_console.Encoding, _console.Capabilities.LegacyConsole); + var delta = _stopwatch.Elapsed - _lastUpdate; _lastUpdate = _stopwatch.Elapsed; @@ -68,9 +70,10 @@ namespace Spectre.Console.Internal { var column = new GridColumn().PadRight(1); - if (_columns[columnIndex].ColumnWidth != null) + var columnWidth = _columns[columnIndex].GetColumnWidth(renderContext); + if (columnWidth != null) { - column.Width = _columns[columnIndex].ColumnWidth; + column.Width = columnWidth; } if (_columns[columnIndex].NoWrap) @@ -88,12 +91,11 @@ namespace Spectre.Console.Internal } // Add rows - var renderContext = new RenderContext(_console.Encoding, _console.Capabilities.LegacyConsole); - context.EnumerateTasks(task => + foreach (var task in context.GetTasks()) { var columns = _columns.Select(column => column.Render(renderContext, task, delta)); grid.AddRow(columns.ToArray()); - }); + } _live.SetRenderable(new Padder(grid, new Padding(0, 1))); } diff --git a/src/Spectre.Console/Progress/Renderers/NonInteractiveProgressRenderer.cs b/src/Spectre.Console/Progress/Renderers/FallbackProgressRenderer.cs similarity index 90% rename from src/Spectre.Console/Progress/Renderers/NonInteractiveProgressRenderer.cs rename to src/Spectre.Console/Progress/Renderers/FallbackProgressRenderer.cs index a15f09c..c4dae38 100644 --- a/src/Spectre.Console/Progress/Renderers/NonInteractiveProgressRenderer.cs +++ b/src/Spectre.Console/Progress/Renderers/FallbackProgressRenderer.cs @@ -4,7 +4,7 @@ using Spectre.Console.Rendering; namespace Spectre.Console.Internal { - internal sealed class NonInteractiveProgressRenderer : ProgressRenderer + internal sealed class FallbackProgressRenderer : ProgressRenderer { private const double FirstMilestone = 25; private static readonly double?[] _milestones = new double?[] { FirstMilestone, 50, 75, 95, 96, 97, 98, 99, 100 }; @@ -16,7 +16,7 @@ namespace Spectre.Console.Internal public override TimeSpan RefreshRate => TimeSpan.FromSeconds(1); - public NonInteractiveProgressRenderer() + public FallbackProgressRenderer() { _taskMilestones = new Dictionary(); _lock = new object(); @@ -29,7 +29,7 @@ namespace Spectre.Console.Internal var hasStartedTasks = false; var updates = new List<(string, double)>(); - context.EnumerateTasks(task => + foreach (var task in context.GetTasks()) { if (!task.IsStarted || task.IsFinished) { @@ -42,12 +42,15 @@ namespace Spectre.Console.Internal { updates.Add((task.Description, task.Percentage)); } - }); + } // Got started tasks but no updates for 30 seconds? if (hasStartedTasks && updates.Count == 0 && (DateTime.Now - _lastUpdate) > TimeSpan.FromSeconds(30)) { - context.EnumerateTasks(task => updates.Add((task.Description, task.Percentage))); + foreach (var task in context.GetTasks()) + { + updates.Add((task.Description, task.Percentage)); + } } if (updates.Count > 0) diff --git a/src/Spectre.Console/Progress/Renderers/StatusFallbackRenderer.cs b/src/Spectre.Console/Progress/Renderers/StatusFallbackRenderer.cs new file mode 100644 index 0000000..dd07a2c --- /dev/null +++ b/src/Spectre.Console/Progress/Renderers/StatusFallbackRenderer.cs @@ -0,0 +1,60 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Spectre.Console.Rendering; + +namespace Spectre.Console.Internal +{ + internal sealed class StatusFallbackRenderer : ProgressRenderer + { + private readonly object _lock; + private IRenderable? _renderable; + private string? _lastStatus; + + public override TimeSpan RefreshRate => TimeSpan.FromSeconds(1); + + public StatusFallbackRenderer() + { + _lock = new object(); + } + + public override void Update(ProgressContext context) + { + lock (_lock) + { + var task = context.GetTasks().SingleOrDefault(); + if (task != null) + { + // Not same description? + if (_lastStatus != task.Description) + { + _lastStatus = task.Description; + _renderable = new Markup(task.Description + Environment.NewLine); + return; + } + } + + _renderable = null; + return; + } + } + + public override IEnumerable Process(RenderContext context, IEnumerable renderables) + { + lock (_lock) + { + var result = new List(); + result.AddRange(renderables); + + if (_renderable != null) + { + result.Add(_renderable); + } + + _renderable = null; + + return result; + } + } + } +} diff --git a/src/Spectre.Console/Progress/ProgressSpinner.Generated.cs b/src/Spectre.Console/Progress/Spinner.Generated.cs similarity index 84% rename from src/Spectre.Console/Progress/ProgressSpinner.Generated.cs rename to src/Spectre.Console/Progress/Spinner.Generated.cs index 13cc828..ba1b45e 100644 --- a/src/Spectre.Console/Progress/ProgressSpinner.Generated.cs +++ b/src/Spectre.Console/Progress/Spinner.Generated.cs @@ -1,7 +1,10 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Generated 2020-12-05 10:31 +// Generated 2020-12-08 23:55 +// +// Partly generated from +// https://github.com/sindresorhus/cli-spinners/blob/master/spinners.json // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. @@ -13,9 +16,41 @@ using System.Collections.Generic; namespace Spectre.Console { - public abstract partial class ProgressSpinner + public abstract partial class Spinner { - private sealed class DotsSpinner : ProgressSpinner + private sealed class DefaultSpinner : Spinner + { + public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); + public override bool IsUnicode => true; + public override IReadOnlyList Frames => new List + { + "⣷", + "⣯", + "⣟", + "⡿", + "⢿", + "⣻", + "⣽", + "⣾", + }; + } + private sealed class AsciiSpinner : Spinner + { + public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); + public override bool IsUnicode => true; + public override IReadOnlyList Frames => new List + { + "-", + "\\", + "|", + "/", + "-", + "\\", + "|", + "/", + }; + } + private sealed class DotsSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -33,7 +68,7 @@ namespace Spectre.Console "⠏", }; } - private sealed class Dots2Spinner : ProgressSpinner + private sealed class Dots2Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -49,7 +84,7 @@ namespace Spectre.Console "⣷", }; } - private sealed class Dots3Spinner : ProgressSpinner + private sealed class Dots3Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -67,7 +102,7 @@ namespace Spectre.Console "⠓", }; } - private sealed class Dots4Spinner : ProgressSpinner + private sealed class Dots4Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -89,7 +124,7 @@ namespace Spectre.Console "⠆", }; } - private sealed class Dots5Spinner : ProgressSpinner + private sealed class Dots5Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -114,7 +149,7 @@ namespace Spectre.Console "⠋", }; } - private sealed class Dots6Spinner : ProgressSpinner + private sealed class Dots6Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -146,7 +181,7 @@ namespace Spectre.Console "⠁", }; } - private sealed class Dots7Spinner : ProgressSpinner + private sealed class Dots7Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -178,7 +213,7 @@ namespace Spectre.Console "⠈", }; } - private sealed class Dots8Spinner : ProgressSpinner + private sealed class Dots8Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -215,7 +250,7 @@ namespace Spectre.Console "⠈", }; } - private sealed class Dots9Spinner : ProgressSpinner + private sealed class Dots9Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -231,7 +266,7 @@ namespace Spectre.Console "⡏", }; } - private sealed class Dots10Spinner : ProgressSpinner + private sealed class Dots10Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -246,7 +281,7 @@ namespace Spectre.Console "⡠", }; } - private sealed class Dots11Spinner : ProgressSpinner + private sealed class Dots11Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => true; @@ -262,7 +297,7 @@ namespace Spectre.Console "⠈", }; } - private sealed class Dots12Spinner : ProgressSpinner + private sealed class Dots12Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -326,7 +361,7 @@ namespace Spectre.Console "⠀⡀", }; } - private sealed class Dots8BitSpinner : ProgressSpinner + private sealed class Dots8BitSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -590,7 +625,7 @@ namespace Spectre.Console "⣿", }; } - private sealed class LineSpinner : ProgressSpinner + private sealed class LineSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(130); public override bool IsUnicode => false; @@ -602,7 +637,7 @@ namespace Spectre.Console "/", }; } - private sealed class Line2Spinner : ProgressSpinner + private sealed class Line2Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => false; @@ -616,7 +651,7 @@ namespace Spectre.Console "-", }; } - private sealed class PipeSpinner : ProgressSpinner + private sealed class PipeSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => false; @@ -632,7 +667,7 @@ namespace Spectre.Console "┐", }; } - private sealed class SimpleDotsSpinner : ProgressSpinner + private sealed class SimpleDotsSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(400); public override bool IsUnicode => false; @@ -644,7 +679,7 @@ namespace Spectre.Console " ", }; } - private sealed class SimpleDotsScrollingSpinner : ProgressSpinner + private sealed class SimpleDotsScrollingSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(200); public override bool IsUnicode => false; @@ -658,7 +693,7 @@ namespace Spectre.Console " ", }; } - private sealed class StarSpinner : ProgressSpinner + private sealed class StarSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(70); public override bool IsUnicode => true; @@ -672,7 +707,7 @@ namespace Spectre.Console "✷", }; } - private sealed class Star2Spinner : ProgressSpinner + private sealed class Star2Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => false; @@ -683,7 +718,7 @@ namespace Spectre.Console "*", }; } - private sealed class FlipSpinner : ProgressSpinner + private sealed class FlipSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(70); public override bool IsUnicode => false; @@ -703,7 +738,7 @@ namespace Spectre.Console "_", }; } - private sealed class HamburgerSpinner : ProgressSpinner + private sealed class HamburgerSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => true; @@ -714,7 +749,7 @@ namespace Spectre.Console "☴", }; } - private sealed class GrowVerticalSpinner : ProgressSpinner + private sealed class GrowVerticalSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(120); public override bool IsUnicode => true; @@ -732,7 +767,7 @@ namespace Spectre.Console "▃", }; } - private sealed class GrowHorizontalSpinner : ProgressSpinner + private sealed class GrowHorizontalSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(120); public override bool IsUnicode => true; @@ -752,7 +787,7 @@ namespace Spectre.Console "▎", }; } - private sealed class BalloonSpinner : ProgressSpinner + private sealed class BalloonSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(140); public override bool IsUnicode => false; @@ -767,7 +802,7 @@ namespace Spectre.Console " ", }; } - private sealed class Balloon2Spinner : ProgressSpinner + private sealed class Balloon2Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(120); public override bool IsUnicode => false; @@ -782,7 +817,7 @@ namespace Spectre.Console ".", }; } - private sealed class NoiseSpinner : ProgressSpinner + private sealed class NoiseSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => true; @@ -793,7 +828,7 @@ namespace Spectre.Console "░", }; } - private sealed class BounceSpinner : ProgressSpinner + private sealed class BounceSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(120); public override bool IsUnicode => true; @@ -805,7 +840,7 @@ namespace Spectre.Console "⠂", }; } - private sealed class BoxBounceSpinner : ProgressSpinner + private sealed class BoxBounceSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(120); public override bool IsUnicode => true; @@ -817,7 +852,7 @@ namespace Spectre.Console "▗", }; } - private sealed class BoxBounce2Spinner : ProgressSpinner + private sealed class BoxBounce2Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => true; @@ -829,7 +864,7 @@ namespace Spectre.Console "▄", }; } - private sealed class TriangleSpinner : ProgressSpinner + private sealed class TriangleSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(50); public override bool IsUnicode => true; @@ -841,7 +876,7 @@ namespace Spectre.Console "◥", }; } - private sealed class ArcSpinner : ProgressSpinner + private sealed class ArcSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => true; @@ -855,7 +890,7 @@ namespace Spectre.Console "◟", }; } - private sealed class CircleSpinner : ProgressSpinner + private sealed class CircleSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(120); public override bool IsUnicode => true; @@ -866,7 +901,7 @@ namespace Spectre.Console "◠", }; } - private sealed class SquareCornersSpinner : ProgressSpinner + private sealed class SquareCornersSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(180); public override bool IsUnicode => true; @@ -878,7 +913,7 @@ namespace Spectre.Console "◱", }; } - private sealed class CircleQuartersSpinner : ProgressSpinner + private sealed class CircleQuartersSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(120); public override bool IsUnicode => true; @@ -890,7 +925,7 @@ namespace Spectre.Console "◵", }; } - private sealed class CircleHalvesSpinner : ProgressSpinner + private sealed class CircleHalvesSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(50); public override bool IsUnicode => true; @@ -902,7 +937,7 @@ namespace Spectre.Console "◒", }; } - private sealed class SquishSpinner : ProgressSpinner + private sealed class SquishSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => true; @@ -912,7 +947,7 @@ namespace Spectre.Console "╪", }; } - private sealed class ToggleSpinner : ProgressSpinner + private sealed class ToggleSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(250); public override bool IsUnicode => true; @@ -922,7 +957,7 @@ namespace Spectre.Console "⊷", }; } - private sealed class Toggle2Spinner : ProgressSpinner + private sealed class Toggle2Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -932,7 +967,7 @@ namespace Spectre.Console "▪", }; } - private sealed class Toggle3Spinner : ProgressSpinner + private sealed class Toggle3Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(120); public override bool IsUnicode => true; @@ -942,7 +977,7 @@ namespace Spectre.Console "■", }; } - private sealed class Toggle4Spinner : ProgressSpinner + private sealed class Toggle4Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => true; @@ -954,7 +989,7 @@ namespace Spectre.Console "▫", }; } - private sealed class Toggle5Spinner : ProgressSpinner + private sealed class Toggle5Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => true; @@ -964,7 +999,7 @@ namespace Spectre.Console "▯", }; } - private sealed class Toggle6Spinner : ProgressSpinner + private sealed class Toggle6Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(300); public override bool IsUnicode => true; @@ -974,7 +1009,7 @@ namespace Spectre.Console "၀", }; } - private sealed class Toggle7Spinner : ProgressSpinner + private sealed class Toggle7Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -984,7 +1019,7 @@ namespace Spectre.Console "⦿", }; } - private sealed class Toggle8Spinner : ProgressSpinner + private sealed class Toggle8Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => true; @@ -994,7 +1029,7 @@ namespace Spectre.Console "◌", }; } - private sealed class Toggle9Spinner : ProgressSpinner + private sealed class Toggle9Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => true; @@ -1004,7 +1039,7 @@ namespace Spectre.Console "◎", }; } - private sealed class Toggle10Spinner : ProgressSpinner + private sealed class Toggle10Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => true; @@ -1015,7 +1050,7 @@ namespace Spectre.Console "㊁", }; } - private sealed class Toggle11Spinner : ProgressSpinner + private sealed class Toggle11Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(50); public override bool IsUnicode => true; @@ -1025,7 +1060,7 @@ namespace Spectre.Console "⧆", }; } - private sealed class Toggle12Spinner : ProgressSpinner + private sealed class Toggle12Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(120); public override bool IsUnicode => true; @@ -1035,7 +1070,7 @@ namespace Spectre.Console "☖", }; } - private sealed class Toggle13Spinner : ProgressSpinner + private sealed class Toggle13Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => false; @@ -1046,7 +1081,7 @@ namespace Spectre.Console "-", }; } - private sealed class ArrowSpinner : ProgressSpinner + private sealed class ArrowSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => true; @@ -1062,7 +1097,7 @@ namespace Spectre.Console "↙", }; } - private sealed class Arrow2Spinner : ProgressSpinner + private sealed class Arrow2Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -1078,7 +1113,7 @@ namespace Spectre.Console "↖️ ", }; } - private sealed class Arrow3Spinner : ProgressSpinner + private sealed class Arrow3Spinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(120); public override bool IsUnicode => true; @@ -1092,7 +1127,7 @@ namespace Spectre.Console "▹▹▹▹▸", }; } - private sealed class BouncingBarSpinner : ProgressSpinner + private sealed class BouncingBarSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -1115,7 +1150,7 @@ namespace Spectre.Console "[= ]", }; } - private sealed class BouncingBallSpinner : ProgressSpinner + private sealed class BouncingBallSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -1133,7 +1168,7 @@ namespace Spectre.Console "(● )", }; } - private sealed class SmileySpinner : ProgressSpinner + private sealed class SmileySpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(200); public override bool IsUnicode => true; @@ -1143,7 +1178,7 @@ namespace Spectre.Console "😝 ", }; } - private sealed class MonkeySpinner : ProgressSpinner + private sealed class MonkeySpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(300); public override bool IsUnicode => true; @@ -1155,7 +1190,7 @@ namespace Spectre.Console "🙊 ", }; } - private sealed class HeartsSpinner : ProgressSpinner + private sealed class HeartsSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => true; @@ -1168,7 +1203,7 @@ namespace Spectre.Console "❤️ ", }; } - private sealed class ClockSpinner : ProgressSpinner + private sealed class ClockSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => true; @@ -1188,7 +1223,7 @@ namespace Spectre.Console "🕚 ", }; } - private sealed class EarthSpinner : ProgressSpinner + private sealed class EarthSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(180); public override bool IsUnicode => true; @@ -1199,7 +1234,7 @@ namespace Spectre.Console "🌏 ", }; } - private sealed class MaterialSpinner : ProgressSpinner + private sealed class MaterialSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(17); public override bool IsUnicode => true; @@ -1299,7 +1334,7 @@ namespace Spectre.Console "▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁▁", }; } - private sealed class MoonSpinner : ProgressSpinner + private sealed class MoonSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -1315,7 +1350,7 @@ namespace Spectre.Console "🌘 ", }; } - private sealed class RunnerSpinner : ProgressSpinner + private sealed class RunnerSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(140); public override bool IsUnicode => true; @@ -1325,7 +1360,7 @@ namespace Spectre.Console "🏃 ", }; } - private sealed class PongSpinner : ProgressSpinner + private sealed class PongSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -1363,7 +1398,7 @@ namespace Spectre.Console "▐⠠ ▌", }; } - private sealed class SharkSpinner : ProgressSpinner + private sealed class SharkSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(120); public override bool IsUnicode => true; @@ -1397,7 +1432,7 @@ namespace Spectre.Console "▐/|____________▌", }; } - private sealed class DqpbSpinner : ProgressSpinner + private sealed class DqpbSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => false; @@ -1409,7 +1444,7 @@ namespace Spectre.Console "b", }; } - private sealed class WeatherSpinner : ProgressSpinner + private sealed class WeatherSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(100); public override bool IsUnicode => true; @@ -1440,7 +1475,7 @@ namespace Spectre.Console "☀️ ", }; } - private sealed class ChristmasSpinner : ProgressSpinner + private sealed class ChristmasSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(400); public override bool IsUnicode => true; @@ -1450,7 +1485,7 @@ namespace Spectre.Console "🎄", }; } - private sealed class GrenadeSpinner : ProgressSpinner + private sealed class GrenadeSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -1472,7 +1507,7 @@ namespace Spectre.Console " ", }; } - private sealed class PointSpinner : ProgressSpinner + private sealed class PointSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(125); public override bool IsUnicode => true; @@ -1485,7 +1520,7 @@ namespace Spectre.Console "∙∙∙", }; } - private sealed class LayerSpinner : ProgressSpinner + private sealed class LayerSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(150); public override bool IsUnicode => true; @@ -1496,7 +1531,7 @@ namespace Spectre.Console "≡", }; } - private sealed class BetaWaveSpinner : ProgressSpinner + private sealed class BetaWaveSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -1511,7 +1546,7 @@ namespace Spectre.Console "ββββββρ", }; } - private sealed class AestheticSpinner : ProgressSpinner + private sealed class AestheticSpinner : Spinner { public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); public override bool IsUnicode => true; @@ -1527,344 +1562,312 @@ namespace Spectre.Console "▰▱▱▱▱▱▱", }; } - private sealed class DefaultSpinner : ProgressSpinner - { - public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); - public override bool IsUnicode => true; - public override IReadOnlyList Frames => new List - { - "⣷", - "⣯", - "⣟", - "⡿", - "⢿", - "⣻", - "⣽", - "⣾", - }; - } - private sealed class AsciiSpinner : ProgressSpinner - { - public override TimeSpan Interval => TimeSpan.FromMilliseconds(80); - public override bool IsUnicode => true; - public override IReadOnlyList Frames => new List - { - "-", - "\\", - "|", - "/", - "-", - "\\", - "|", - "/", - }; - } /// /// Contains all predefined spinners. /// public static class Known { - /// - /// Gets the "dots" spinner. - /// - public static ProgressSpinner Dots { get; } = new DotsSpinner(); - /// - /// Gets the "dots2" spinner. - /// - public static ProgressSpinner Dots2 { get; } = new Dots2Spinner(); - /// - /// Gets the "dots3" spinner. - /// - public static ProgressSpinner Dots3 { get; } = new Dots3Spinner(); - /// - /// Gets the "dots4" spinner. - /// - public static ProgressSpinner Dots4 { get; } = new Dots4Spinner(); - /// - /// Gets the "dots5" spinner. - /// - public static ProgressSpinner Dots5 { get; } = new Dots5Spinner(); - /// - /// Gets the "dots6" spinner. - /// - public static ProgressSpinner Dots6 { get; } = new Dots6Spinner(); - /// - /// Gets the "dots7" spinner. - /// - public static ProgressSpinner Dots7 { get; } = new Dots7Spinner(); - /// - /// Gets the "dots8" spinner. - /// - public static ProgressSpinner Dots8 { get; } = new Dots8Spinner(); - /// - /// Gets the "dots9" spinner. - /// - public static ProgressSpinner Dots9 { get; } = new Dots9Spinner(); - /// - /// Gets the "dots10" spinner. - /// - public static ProgressSpinner Dots10 { get; } = new Dots10Spinner(); - /// - /// Gets the "dots11" spinner. - /// - public static ProgressSpinner Dots11 { get; } = new Dots11Spinner(); - /// - /// Gets the "dots12" spinner. - /// - public static ProgressSpinner Dots12 { get; } = new Dots12Spinner(); - /// - /// Gets the "dots8Bit" spinner. - /// - public static ProgressSpinner Dots8Bit { get; } = new Dots8BitSpinner(); - /// - /// Gets the "line" spinner. - /// - public static ProgressSpinner Line { get; } = new LineSpinner(); - /// - /// Gets the "line2" spinner. - /// - public static ProgressSpinner Line2 { get; } = new Line2Spinner(); - /// - /// Gets the "pipe" spinner. - /// - public static ProgressSpinner Pipe { get; } = new PipeSpinner(); - /// - /// Gets the "simpleDots" spinner. - /// - public static ProgressSpinner SimpleDots { get; } = new SimpleDotsSpinner(); - /// - /// Gets the "simpleDotsScrolling" spinner. - /// - public static ProgressSpinner SimpleDotsScrolling { get; } = new SimpleDotsScrollingSpinner(); - /// - /// Gets the "star" spinner. - /// - public static ProgressSpinner Star { get; } = new StarSpinner(); - /// - /// Gets the "star2" spinner. - /// - public static ProgressSpinner Star2 { get; } = new Star2Spinner(); - /// - /// Gets the "flip" spinner. - /// - public static ProgressSpinner Flip { get; } = new FlipSpinner(); - /// - /// Gets the "hamburger" spinner. - /// - public static ProgressSpinner Hamburger { get; } = new HamburgerSpinner(); - /// - /// Gets the "growVertical" spinner. - /// - public static ProgressSpinner GrowVertical { get; } = new GrowVerticalSpinner(); - /// - /// Gets the "growHorizontal" spinner. - /// - public static ProgressSpinner GrowHorizontal { get; } = new GrowHorizontalSpinner(); - /// - /// Gets the "balloon" spinner. - /// - public static ProgressSpinner Balloon { get; } = new BalloonSpinner(); - /// - /// Gets the "balloon2" spinner. - /// - public static ProgressSpinner Balloon2 { get; } = new Balloon2Spinner(); - /// - /// Gets the "noise" spinner. - /// - public static ProgressSpinner Noise { get; } = new NoiseSpinner(); - /// - /// Gets the "bounce" spinner. - /// - public static ProgressSpinner Bounce { get; } = new BounceSpinner(); - /// - /// Gets the "boxBounce" spinner. - /// - public static ProgressSpinner BoxBounce { get; } = new BoxBounceSpinner(); - /// - /// Gets the "boxBounce2" spinner. - /// - public static ProgressSpinner BoxBounce2 { get; } = new BoxBounce2Spinner(); - /// - /// Gets the "triangle" spinner. - /// - public static ProgressSpinner Triangle { get; } = new TriangleSpinner(); - /// - /// Gets the "arc" spinner. - /// - public static ProgressSpinner Arc { get; } = new ArcSpinner(); - /// - /// Gets the "circle" spinner. - /// - public static ProgressSpinner Circle { get; } = new CircleSpinner(); - /// - /// Gets the "squareCorners" spinner. - /// - public static ProgressSpinner SquareCorners { get; } = new SquareCornersSpinner(); - /// - /// Gets the "circleQuarters" spinner. - /// - public static ProgressSpinner CircleQuarters { get; } = new CircleQuartersSpinner(); - /// - /// Gets the "circleHalves" spinner. - /// - public static ProgressSpinner CircleHalves { get; } = new CircleHalvesSpinner(); - /// - /// Gets the "squish" spinner. - /// - public static ProgressSpinner Squish { get; } = new SquishSpinner(); - /// - /// Gets the "toggle" spinner. - /// - public static ProgressSpinner Toggle { get; } = new ToggleSpinner(); - /// - /// Gets the "toggle2" spinner. - /// - public static ProgressSpinner Toggle2 { get; } = new Toggle2Spinner(); - /// - /// Gets the "toggle3" spinner. - /// - public static ProgressSpinner Toggle3 { get; } = new Toggle3Spinner(); - /// - /// Gets the "toggle4" spinner. - /// - public static ProgressSpinner Toggle4 { get; } = new Toggle4Spinner(); - /// - /// Gets the "toggle5" spinner. - /// - public static ProgressSpinner Toggle5 { get; } = new Toggle5Spinner(); - /// - /// Gets the "toggle6" spinner. - /// - public static ProgressSpinner Toggle6 { get; } = new Toggle6Spinner(); - /// - /// Gets the "toggle7" spinner. - /// - public static ProgressSpinner Toggle7 { get; } = new Toggle7Spinner(); - /// - /// Gets the "toggle8" spinner. - /// - public static ProgressSpinner Toggle8 { get; } = new Toggle8Spinner(); - /// - /// Gets the "toggle9" spinner. - /// - public static ProgressSpinner Toggle9 { get; } = new Toggle9Spinner(); - /// - /// Gets the "toggle10" spinner. - /// - public static ProgressSpinner Toggle10 { get; } = new Toggle10Spinner(); - /// - /// Gets the "toggle11" spinner. - /// - public static ProgressSpinner Toggle11 { get; } = new Toggle11Spinner(); - /// - /// Gets the "toggle12" spinner. - /// - public static ProgressSpinner Toggle12 { get; } = new Toggle12Spinner(); - /// - /// Gets the "toggle13" spinner. - /// - public static ProgressSpinner Toggle13 { get; } = new Toggle13Spinner(); - /// - /// Gets the "arrow" spinner. - /// - public static ProgressSpinner Arrow { get; } = new ArrowSpinner(); - /// - /// Gets the "arrow2" spinner. - /// - public static ProgressSpinner Arrow2 { get; } = new Arrow2Spinner(); - /// - /// Gets the "arrow3" spinner. - /// - public static ProgressSpinner Arrow3 { get; } = new Arrow3Spinner(); - /// - /// Gets the "bouncingBar" spinner. - /// - public static ProgressSpinner BouncingBar { get; } = new BouncingBarSpinner(); - /// - /// Gets the "bouncingBall" spinner. - /// - public static ProgressSpinner BouncingBall { get; } = new BouncingBallSpinner(); - /// - /// Gets the "smiley" spinner. - /// - public static ProgressSpinner Smiley { get; } = new SmileySpinner(); - /// - /// Gets the "monkey" spinner. - /// - public static ProgressSpinner Monkey { get; } = new MonkeySpinner(); - /// - /// Gets the "hearts" spinner. - /// - public static ProgressSpinner Hearts { get; } = new HeartsSpinner(); - /// - /// Gets the "clock" spinner. - /// - public static ProgressSpinner Clock { get; } = new ClockSpinner(); - /// - /// Gets the "earth" spinner. - /// - public static ProgressSpinner Earth { get; } = new EarthSpinner(); - /// - /// Gets the "material" spinner. - /// - public static ProgressSpinner Material { get; } = new MaterialSpinner(); - /// - /// Gets the "moon" spinner. - /// - public static ProgressSpinner Moon { get; } = new MoonSpinner(); - /// - /// Gets the "runner" spinner. - /// - public static ProgressSpinner Runner { get; } = new RunnerSpinner(); - /// - /// Gets the "pong" spinner. - /// - public static ProgressSpinner Pong { get; } = new PongSpinner(); - /// - /// Gets the "shark" spinner. - /// - public static ProgressSpinner Shark { get; } = new SharkSpinner(); - /// - /// Gets the "dqpb" spinner. - /// - public static ProgressSpinner Dqpb { get; } = new DqpbSpinner(); - /// - /// Gets the "weather" spinner. - /// - public static ProgressSpinner Weather { get; } = new WeatherSpinner(); - /// - /// Gets the "christmas" spinner. - /// - public static ProgressSpinner Christmas { get; } = new ChristmasSpinner(); - /// - /// Gets the "grenade" spinner. - /// - public static ProgressSpinner Grenade { get; } = new GrenadeSpinner(); - /// - /// Gets the "point" spinner. - /// - public static ProgressSpinner Point { get; } = new PointSpinner(); - /// - /// Gets the "layer" spinner. - /// - public static ProgressSpinner Layer { get; } = new LayerSpinner(); - /// - /// Gets the "betaWave" spinner. - /// - public static ProgressSpinner BetaWave { get; } = new BetaWaveSpinner(); - /// - /// Gets the "aesthetic" spinner. - /// - public static ProgressSpinner Aesthetic { get; } = new AestheticSpinner(); /// /// Gets the "Default" spinner. /// - public static ProgressSpinner Default { get; } = new DefaultSpinner(); + public static Spinner Default { get; } = new DefaultSpinner(); /// /// Gets the "Ascii" spinner. /// - public static ProgressSpinner Ascii { get; } = new AsciiSpinner(); + public static Spinner Ascii { get; } = new AsciiSpinner(); + /// + /// Gets the "dots" spinner. + /// + public static Spinner Dots { get; } = new DotsSpinner(); + /// + /// Gets the "dots2" spinner. + /// + public static Spinner Dots2 { get; } = new Dots2Spinner(); + /// + /// Gets the "dots3" spinner. + /// + public static Spinner Dots3 { get; } = new Dots3Spinner(); + /// + /// Gets the "dots4" spinner. + /// + public static Spinner Dots4 { get; } = new Dots4Spinner(); + /// + /// Gets the "dots5" spinner. + /// + public static Spinner Dots5 { get; } = new Dots5Spinner(); + /// + /// Gets the "dots6" spinner. + /// + public static Spinner Dots6 { get; } = new Dots6Spinner(); + /// + /// Gets the "dots7" spinner. + /// + public static Spinner Dots7 { get; } = new Dots7Spinner(); + /// + /// Gets the "dots8" spinner. + /// + public static Spinner Dots8 { get; } = new Dots8Spinner(); + /// + /// Gets the "dots9" spinner. + /// + public static Spinner Dots9 { get; } = new Dots9Spinner(); + /// + /// Gets the "dots10" spinner. + /// + public static Spinner Dots10 { get; } = new Dots10Spinner(); + /// + /// Gets the "dots11" spinner. + /// + public static Spinner Dots11 { get; } = new Dots11Spinner(); + /// + /// Gets the "dots12" spinner. + /// + public static Spinner Dots12 { get; } = new Dots12Spinner(); + /// + /// Gets the "dots8Bit" spinner. + /// + public static Spinner Dots8Bit { get; } = new Dots8BitSpinner(); + /// + /// Gets the "line" spinner. + /// + public static Spinner Line { get; } = new LineSpinner(); + /// + /// Gets the "line2" spinner. + /// + public static Spinner Line2 { get; } = new Line2Spinner(); + /// + /// Gets the "pipe" spinner. + /// + public static Spinner Pipe { get; } = new PipeSpinner(); + /// + /// Gets the "simpleDots" spinner. + /// + public static Spinner SimpleDots { get; } = new SimpleDotsSpinner(); + /// + /// Gets the "simpleDotsScrolling" spinner. + /// + public static Spinner SimpleDotsScrolling { get; } = new SimpleDotsScrollingSpinner(); + /// + /// Gets the "star" spinner. + /// + public static Spinner Star { get; } = new StarSpinner(); + /// + /// Gets the "star2" spinner. + /// + public static Spinner Star2 { get; } = new Star2Spinner(); + /// + /// Gets the "flip" spinner. + /// + public static Spinner Flip { get; } = new FlipSpinner(); + /// + /// Gets the "hamburger" spinner. + /// + public static Spinner Hamburger { get; } = new HamburgerSpinner(); + /// + /// Gets the "growVertical" spinner. + /// + public static Spinner GrowVertical { get; } = new GrowVerticalSpinner(); + /// + /// Gets the "growHorizontal" spinner. + /// + public static Spinner GrowHorizontal { get; } = new GrowHorizontalSpinner(); + /// + /// Gets the "balloon" spinner. + /// + public static Spinner Balloon { get; } = new BalloonSpinner(); + /// + /// Gets the "balloon2" spinner. + /// + public static Spinner Balloon2 { get; } = new Balloon2Spinner(); + /// + /// Gets the "noise" spinner. + /// + public static Spinner Noise { get; } = new NoiseSpinner(); + /// + /// Gets the "bounce" spinner. + /// + public static Spinner Bounce { get; } = new BounceSpinner(); + /// + /// Gets the "boxBounce" spinner. + /// + public static Spinner BoxBounce { get; } = new BoxBounceSpinner(); + /// + /// Gets the "boxBounce2" spinner. + /// + public static Spinner BoxBounce2 { get; } = new BoxBounce2Spinner(); + /// + /// Gets the "triangle" spinner. + /// + public static Spinner Triangle { get; } = new TriangleSpinner(); + /// + /// Gets the "arc" spinner. + /// + public static Spinner Arc { get; } = new ArcSpinner(); + /// + /// Gets the "circle" spinner. + /// + public static Spinner Circle { get; } = new CircleSpinner(); + /// + /// Gets the "squareCorners" spinner. + /// + public static Spinner SquareCorners { get; } = new SquareCornersSpinner(); + /// + /// Gets the "circleQuarters" spinner. + /// + public static Spinner CircleQuarters { get; } = new CircleQuartersSpinner(); + /// + /// Gets the "circleHalves" spinner. + /// + public static Spinner CircleHalves { get; } = new CircleHalvesSpinner(); + /// + /// Gets the "squish" spinner. + /// + public static Spinner Squish { get; } = new SquishSpinner(); + /// + /// Gets the "toggle" spinner. + /// + public static Spinner Toggle { get; } = new ToggleSpinner(); + /// + /// Gets the "toggle2" spinner. + /// + public static Spinner Toggle2 { get; } = new Toggle2Spinner(); + /// + /// Gets the "toggle3" spinner. + /// + public static Spinner Toggle3 { get; } = new Toggle3Spinner(); + /// + /// Gets the "toggle4" spinner. + /// + public static Spinner Toggle4 { get; } = new Toggle4Spinner(); + /// + /// Gets the "toggle5" spinner. + /// + public static Spinner Toggle5 { get; } = new Toggle5Spinner(); + /// + /// Gets the "toggle6" spinner. + /// + public static Spinner Toggle6 { get; } = new Toggle6Spinner(); + /// + /// Gets the "toggle7" spinner. + /// + public static Spinner Toggle7 { get; } = new Toggle7Spinner(); + /// + /// Gets the "toggle8" spinner. + /// + public static Spinner Toggle8 { get; } = new Toggle8Spinner(); + /// + /// Gets the "toggle9" spinner. + /// + public static Spinner Toggle9 { get; } = new Toggle9Spinner(); + /// + /// Gets the "toggle10" spinner. + /// + public static Spinner Toggle10 { get; } = new Toggle10Spinner(); + /// + /// Gets the "toggle11" spinner. + /// + public static Spinner Toggle11 { get; } = new Toggle11Spinner(); + /// + /// Gets the "toggle12" spinner. + /// + public static Spinner Toggle12 { get; } = new Toggle12Spinner(); + /// + /// Gets the "toggle13" spinner. + /// + public static Spinner Toggle13 { get; } = new Toggle13Spinner(); + /// + /// Gets the "arrow" spinner. + /// + public static Spinner Arrow { get; } = new ArrowSpinner(); + /// + /// Gets the "arrow2" spinner. + /// + public static Spinner Arrow2 { get; } = new Arrow2Spinner(); + /// + /// Gets the "arrow3" spinner. + /// + public static Spinner Arrow3 { get; } = new Arrow3Spinner(); + /// + /// Gets the "bouncingBar" spinner. + /// + public static Spinner BouncingBar { get; } = new BouncingBarSpinner(); + /// + /// Gets the "bouncingBall" spinner. + /// + public static Spinner BouncingBall { get; } = new BouncingBallSpinner(); + /// + /// Gets the "smiley" spinner. + /// + public static Spinner Smiley { get; } = new SmileySpinner(); + /// + /// Gets the "monkey" spinner. + /// + public static Spinner Monkey { get; } = new MonkeySpinner(); + /// + /// Gets the "hearts" spinner. + /// + public static Spinner Hearts { get; } = new HeartsSpinner(); + /// + /// Gets the "clock" spinner. + /// + public static Spinner Clock { get; } = new ClockSpinner(); + /// + /// Gets the "earth" spinner. + /// + public static Spinner Earth { get; } = new EarthSpinner(); + /// + /// Gets the "material" spinner. + /// + public static Spinner Material { get; } = new MaterialSpinner(); + /// + /// Gets the "moon" spinner. + /// + public static Spinner Moon { get; } = new MoonSpinner(); + /// + /// Gets the "runner" spinner. + /// + public static Spinner Runner { get; } = new RunnerSpinner(); + /// + /// Gets the "pong" spinner. + /// + public static Spinner Pong { get; } = new PongSpinner(); + /// + /// Gets the "shark" spinner. + /// + public static Spinner Shark { get; } = new SharkSpinner(); + /// + /// Gets the "dqpb" spinner. + /// + public static Spinner Dqpb { get; } = new DqpbSpinner(); + /// + /// Gets the "weather" spinner. + /// + public static Spinner Weather { get; } = new WeatherSpinner(); + /// + /// Gets the "christmas" spinner. + /// + public static Spinner Christmas { get; } = new ChristmasSpinner(); + /// + /// Gets the "grenade" spinner. + /// + public static Spinner Grenade { get; } = new GrenadeSpinner(); + /// + /// Gets the "point" spinner. + /// + public static Spinner Point { get; } = new PointSpinner(); + /// + /// Gets the "layer" spinner. + /// + public static Spinner Layer { get; } = new LayerSpinner(); + /// + /// Gets the "betaWave" spinner. + /// + public static Spinner BetaWave { get; } = new BetaWaveSpinner(); + /// + /// Gets the "aesthetic" spinner. + /// + public static Spinner Aesthetic { get; } = new AestheticSpinner(); } } } diff --git a/src/Spectre.Console/Progress/ProgressSpinner.cs b/src/Spectre.Console/Progress/Spinner.cs similarity index 93% rename from src/Spectre.Console/Progress/ProgressSpinner.cs rename to src/Spectre.Console/Progress/Spinner.cs index e231216..3583978 100644 --- a/src/Spectre.Console/Progress/ProgressSpinner.cs +++ b/src/Spectre.Console/Progress/Spinner.cs @@ -6,7 +6,7 @@ namespace Spectre.Console /// /// Represents a spinner used in a . /// - public abstract partial class ProgressSpinner + public abstract partial class Spinner { /// /// Gets the update interval for the spinner. diff --git a/src/Spectre.Console/Progress/Status.cs b/src/Spectre.Console/Progress/Status.cs new file mode 100644 index 0000000..0a98bcb --- /dev/null +++ b/src/Spectre.Console/Progress/Status.cs @@ -0,0 +1,89 @@ +using System; +using System.Threading.Tasks; +using Spectre.Console.Internal; + +namespace Spectre.Console +{ + /// + /// Represents a status display. + /// + public sealed class Status + { + private readonly IAnsiConsole _console; + + /// + /// Gets or sets the spinner. + /// + public Spinner? Spinner { get; set; } + + /// + /// Gets or sets the spinner style. + /// + public Style? SpinnerStyle { get; set; } = new Style(foreground: Color.Yellow); + + /// + /// Gets or sets a value indicating whether or not status + /// should auto refresh. Defaults to true. + /// + public bool AutoRefresh { get; set; } = true; + + /// + /// Initializes a new instance of the class. + /// + /// The console. + public Status(IAnsiConsole console) + { + _console = console ?? throw new ArgumentNullException(nameof(console)); + } + + /// + /// Starts a new status display. + /// + /// The status to display. + /// he action to execute. + public void Start(string status, Action action) + { + var task = StartAsync(status, ctx => + { + action(ctx); + return Task.CompletedTask; + }); + + task.GetAwaiter().GetResult(); + } + + /// + /// Starts a new status display. + /// + /// The status to display. + /// he action to execute. + /// A representing the asynchronous operation. + public async Task StartAsync(string status, Func action) + { + // Set the progress columns + var spinnerColumn = new SpinnerColumn(Spinner ?? Spinner.Known.Default) + { + Style = SpinnerStyle ?? Style.Plain, + }; + + var progress = new Progress(_console) + { + FallbackRenderer = new StatusFallbackRenderer(), + AutoClear = true, + AutoRefresh = AutoRefresh, + }; + + progress.Columns(new ProgressColumn[] + { + spinnerColumn, + new TaskDescriptionColumn(), + }); + + await progress.StartAsync(async ctx => + { + var statusContext = new StatusContext(ctx, ctx.AddTask(status), spinnerColumn); + await action(statusContext).ConfigureAwait(false); + }).ConfigureAwait(false); + } + } +} diff --git a/src/Spectre.Console/Progress/StatusContext.cs b/src/Spectre.Console/Progress/StatusContext.cs new file mode 100644 index 0000000..3a7a04c --- /dev/null +++ b/src/Spectre.Console/Progress/StatusContext.cs @@ -0,0 +1,76 @@ +using System; + +namespace Spectre.Console +{ + /// + /// Represents a context that can be used to interact with a . + /// + public sealed class StatusContext + { + private readonly ProgressContext _context; + private readonly ProgressTask _task; + private readonly SpinnerColumn _spinnerColumn; + + /// + /// Gets or sets the current status. + /// + public string Status + { + get => _task.Description; + set => SetStatus(value); + } + + /// + /// Gets or sets the current spinner. + /// + public Spinner Spinner + { + get => _spinnerColumn.Spinner; + set => SetSpinner(value); + } + + /// + /// Gets or sets the current spinner style. + /// + public Style? SpinnerStyle + { + get => _spinnerColumn.Style; + set => _spinnerColumn.Style = value; + } + + internal StatusContext(ProgressContext context, ProgressTask task, SpinnerColumn spinnerColumn) + { + _context = context ?? throw new ArgumentNullException(nameof(context)); + _task = task ?? throw new ArgumentNullException(nameof(task)); + _spinnerColumn = spinnerColumn ?? throw new ArgumentNullException(nameof(spinnerColumn)); + } + + /// + /// Refreshes the status. + /// + public void Refresh() + { + _context.Refresh(); + } + + private void SetStatus(string status) + { + if (status is null) + { + throw new ArgumentNullException(nameof(status)); + } + + _task.Description = status; + } + + private void SetSpinner(Spinner spinner) + { + if (spinner is null) + { + throw new ArgumentNullException(nameof(spinner)); + } + + _spinnerColumn.Spinner = spinner; + } + } +} diff --git a/src/Spectre.Console/Rendering/LiveRenderable.cs b/src/Spectre.Console/Rendering/LiveRenderable.cs index 340f03b..e8c163f 100644 --- a/src/Spectre.Console/Rendering/LiveRenderable.cs +++ b/src/Spectre.Console/Rendering/LiveRenderable.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using Spectre.Console.Internal; namespace Spectre.Console.Rendering @@ -8,7 +7,7 @@ namespace Spectre.Console.Rendering { private readonly object _lock = new object(); private IRenderable? _renderable; - private int? _height; + private SegmentShape? _shape; public void SetRenderable(IRenderable renderable) { @@ -22,12 +21,12 @@ namespace Spectre.Console.Rendering { lock (_lock) { - if (_height == null) + if (_shape == null) { return new ControlSequence(string.Empty); } - return new ControlSequence("\r" + "\u001b[1A".Repeat(_height.Value - 1)); + return new ControlSequence("\r" + "\u001b[1A".Repeat(_shape.Value.Height - 1)); } } @@ -35,12 +34,12 @@ namespace Spectre.Console.Rendering { lock (_lock) { - if (_height == null) + if (_shape == null) { return new ControlSequence(string.Empty); } - return new ControlSequence("\r\u001b[2K" + "\u001b[1A\u001b[2K".Repeat(_height.Value - 1)); + return new ControlSequence("\r\u001b[2K" + "\u001b[1A\u001b[2K".Repeat(_shape.Value.Height - 1)); } } @@ -53,27 +52,27 @@ namespace Spectre.Console.Rendering var segments = _renderable.Render(context, maxWidth); var lines = Segment.SplitLines(context, segments); - _height = lines.Count; + var shape = SegmentShape.Calculate(context, lines); + _shape = _shape == null ? shape : _shape.Value.Inflate(shape); + _shape.Value.SetShape(context, lines); - var result = new List(); foreach (var (_, _, last, line) in lines.Enumerate()) { foreach (var item in line) { - result.Add(item); + yield return item; } if (!last) { - result.Add(Segment.LineBreak); + yield return Segment.LineBreak; } } - return result; + yield break; } - _height = 0; - return Enumerable.Empty(); + _shape = null; } } } diff --git a/src/Spectre.Console/Rendering/Segment.cs b/src/Spectre.Console/Rendering/Segment.cs index 46fcacd..3d03417 100644 --- a/src/Spectre.Console/Rendering/Segment.cs +++ b/src/Spectre.Console/Rendering/Segment.cs @@ -599,6 +599,7 @@ namespace Spectre.Console.Rendering return stack.ToList(); } + // TODO: Move this to Table internal static List> MakeSameHeight(int cellHeight, List> cells) { if (cells is null) @@ -619,5 +620,49 @@ namespace Spectre.Console.Rendering return cells; } + + internal static (int Width, int Height) GetShape(RenderContext context, List lines) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (lines is null) + { + throw new ArgumentNullException(nameof(lines)); + } + + var height = lines.Count; + var width = lines.Max(l => CellCount(context, l)); + + return (width, height); + } + + internal static List SetShape(RenderContext context, List lines, (int Width, int Height) shape) + { + foreach (var line in lines) + { + var length = CellCount(context, line); + var missing = shape.Width - length; + if (missing > 0) + { + line.Add(new Segment(new string(' ', missing))); + } + } + + if (lines.Count < shape.Height) + { + var missing = shape.Height - lines.Count; + for (int i = 0; i < missing; i++) + { + var line = new SegmentLine(); + line.Add(new Segment(new string(' ', shape.Width))); + lines.Add(line); + } + } + + return lines; + } } } diff --git a/src/Spectre.Console/Rendering/SegmentShape.cs b/src/Spectre.Console/Rendering/SegmentShape.cs new file mode 100644 index 0000000..d6d7a0f --- /dev/null +++ b/src/Spectre.Console/Rendering/SegmentShape.cs @@ -0,0 +1,67 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Spectre.Console.Rendering +{ + internal readonly struct SegmentShape + { + public int Width { get; } + public int Height { get; } + + public SegmentShape(int width, int height) + { + Width = width; + Height = height; + } + + public static SegmentShape Calculate(RenderContext context, List lines) + { + if (context is null) + { + throw new ArgumentNullException(nameof(context)); + } + + if (lines is null) + { + throw new ArgumentNullException(nameof(lines)); + } + + var height = lines.Count; + var width = lines.Max(l => Segment.CellCount(context, l)); + + return new SegmentShape(width, height); + } + + public SegmentShape Inflate(SegmentShape other) + { + return new SegmentShape( + Math.Max(Width, other.Width), + Math.Max(Height, other.Height)); + } + + public void SetShape(RenderContext context, List lines) + { + foreach (var line in lines) + { + var length = Segment.CellCount(context, line); + var missing = Width - length; + if (missing > 0) + { + line.Add(new Segment(new string(' ', missing))); + } + } + + if (lines.Count < Height) + { + var missing = Height - lines.Count; + for (var i = 0; i < missing; i++) + { + var line = new SegmentLine(); + line.Add(new Segment(new string(' ', Width))); + lines.Add(line); + } + } + } + } +}