From 501db5d287cb4870de8f63554a6bd59d09813019 Mon Sep 17 00:00:00 2001 From: Patrik Svensson Date: Tue, 8 Dec 2020 00:43:24 +0100 Subject: [PATCH] Add status support --- docs/input/appendix/spinners.md | 43 + docs/input/assets/images/status.gif | Bin 0 -> 263097 bytes docs/input/assets/js/table-search.js | 1 - docs/input/status.md | 60 ++ examples/Status/Program.cs | 70 ++ examples/Status/Status.csproj | 19 + resources/scripts/Generate-Spinners.ps1 | 2 +- .../Commands/SpinnerGeneratorCommand.cs | 4 +- ...inners.json => spinners_sindresorhus.json} | 0 resources/scripts/Generator/Generator.csproj | 7 +- .../Templates/Color.Generated.template | 1 - ...ed.template => Spinner.Generated.template} | 9 +- .../Tools/DummySpinners.cs | 25 + src/Spectre.Console.Tests/Unit/StatusTests.cs | 42 + src/Spectre.Console.sln | 15 + src/Spectre.Console/AnsiConsole.Progress.cs | 9 + .../AnsiConsoleExtensions.Progress.cs | 15 + .../Columns/SpinnerColumnExtensions.cs | 7 +- .../Extensions/StatusContextExtensions.cs | 61 ++ .../Extensions/StatusExtensions.cs | 62 ++ .../Progress/Columns/PercentageColumn.cs | 9 +- .../Progress/Columns/RemainingTimeColumn.cs | 9 +- .../Progress/Columns/SpinnerColumn.cs | 67 +- src/Spectre.Console/Progress/Progress.cs | 6 +- .../Progress/ProgressColumn.cs | 15 +- .../Progress/ProgressContext.cs | 10 +- ...Renderer.cs => DefaultProgressRenderer.cs} | 16 +- ...enderer.cs => FallbackProgressRenderer.cs} | 13 +- .../Renderers/StatusFallbackRenderer.cs | 60 ++ ...nner.Generated.cs => Spinner.Generated.cs} | 805 +++++++++--------- .../{ProgressSpinner.cs => Spinner.cs} | 2 +- src/Spectre.Console/Progress/Status.cs | 89 ++ src/Spectre.Console/Progress/StatusContext.cs | 76 ++ .../Rendering/LiveRenderable.cs | 25 +- src/Spectre.Console/Rendering/Segment.cs | 45 + src/Spectre.Console/Rendering/SegmentShape.cs | 67 ++ 36 files changed, 1290 insertions(+), 476 deletions(-) create mode 100644 docs/input/appendix/spinners.md create mode 100644 docs/input/assets/images/status.gif create mode 100644 docs/input/status.md create mode 100644 examples/Status/Program.cs create mode 100644 examples/Status/Status.csproj rename resources/scripts/Generator/Data/{spinners.json => spinners_sindresorhus.json} (100%) rename resources/scripts/Generator/Templates/{ProgressSpinner.Generated.template => Spinner.Generated.template} (82%) create mode 100644 src/Spectre.Console.Tests/Tools/DummySpinners.cs create mode 100644 src/Spectre.Console.Tests/Unit/StatusTests.cs create mode 100644 src/Spectre.Console/Extensions/StatusContextExtensions.cs create mode 100644 src/Spectre.Console/Extensions/StatusExtensions.cs rename src/Spectre.Console/Progress/Renderers/{InteractiveProgressRenderer.cs => DefaultProgressRenderer.cs} (87%) rename src/Spectre.Console/Progress/Renderers/{NonInteractiveProgressRenderer.cs => FallbackProgressRenderer.cs} (90%) create mode 100644 src/Spectre.Console/Progress/Renderers/StatusFallbackRenderer.cs rename src/Spectre.Console/Progress/{ProgressSpinner.Generated.cs => Spinner.Generated.cs} (84%) rename src/Spectre.Console/Progress/{ProgressSpinner.cs => Spinner.cs} (93%) create mode 100644 src/Spectre.Console/Progress/Status.cs create mode 100644 src/Spectre.Console/Progress/StatusContext.cs create mode 100644 src/Spectre.Console/Rendering/SegmentShape.cs 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 0000000000000000000000000000000000000000..2cf40f515bbeec1ccd98320e6080be45bc77e3b4 GIT binary patch literal 263097 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≥4nUK6jqGh?@{0IgHQ! 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 literal 0 HcmV?d00001 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); + } + } + } + } +}