mirror of
https://github.com/nsnail/spectre.console.git
synced 2025-04-16 17:02:51 +08:00
Add breakdown chart support
This also cleans up the bar chart code slightly and fixes some minor bugs that were detected in related code. Closes #244
This commit is contained in:
parent
58400fe74e
commit
b64e016e8c
@ -1,21 +1,42 @@
|
|||||||
using Spectre.Console;
|
using Spectre.Console;
|
||||||
|
using Spectre.Console.Rendering;
|
||||||
|
|
||||||
namespace InfoExample
|
namespace Charts
|
||||||
{
|
{
|
||||||
public static class Program
|
public static class Program
|
||||||
{
|
{
|
||||||
public static void Main()
|
public static void Main()
|
||||||
{
|
{
|
||||||
var chart = new BarChart()
|
// Render a bar chart
|
||||||
|
AnsiConsole.WriteLine();
|
||||||
|
Render("Fruits per month", new BarChart()
|
||||||
.Width(60)
|
.Width(60)
|
||||||
.Label("[green bold underline]Number of fruits[/]")
|
.Label("[green bold underline]Number of fruits[/]")
|
||||||
.CenterLabel()
|
.CenterLabel()
|
||||||
.AddItem("Apple", 12, Color.Yellow)
|
.AddItem("Apple", 12, Color.Yellow)
|
||||||
.AddItem("Orange", 54, Color.Green)
|
.AddItem("Orange", 54, Color.Green)
|
||||||
.AddItem("Banana", 33, Color.Red);
|
.AddItem("Banana", 33, Color.Red));
|
||||||
|
|
||||||
|
// Render a breakdown chart
|
||||||
AnsiConsole.WriteLine();
|
AnsiConsole.WriteLine();
|
||||||
AnsiConsole.Render(chart);
|
Render("Languages", new BreakdownChart()
|
||||||
|
.FullSize()
|
||||||
|
.Width(60)
|
||||||
|
.ShowAsPercentages()
|
||||||
|
.AddItem("SCSS", 37, Color.Red)
|
||||||
|
.AddItem("HTML", 28.3, Color.Blue)
|
||||||
|
.AddItem("C#", 22.6, Color.Green)
|
||||||
|
.AddItem("JavaScript", 6, Color.Yellow)
|
||||||
|
.AddItem("Ruby", 6, Color.LightGreen)
|
||||||
|
.AddItem("Shell", 0.1, Color.Aqua));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Render(string title, IRenderable chart)
|
||||||
|
{
|
||||||
|
AnsiConsole.Render(
|
||||||
|
new Panel(chart)
|
||||||
|
.Padding(1, 1)
|
||||||
|
.Header(title));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1 @@
|
|||||||
Greeting
|
|
||||||
Hello World
|
Hello World
|
||||||
|
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
Number of fruits
|
||||||
|
Apple 0
|
||||||
|
Orange █████████████████████████████████████████████████ 54
|
||||||
|
Banana ████████████████████████████ 33
|
@ -0,0 +1,4 @@
|
|||||||
|
[38;5;9m████████████████████████[0m[38;5;12m█████████████████[0m[38;5;2m█████████████[0m[38;5;11m███[0m[38;5;119m███[0m
|
||||||
|
|
||||||
|
[38;5;9m■[0m SCSS [38;5;8m37[0m [38;5;12m■[0m HTML [38;5;8m28.3[0m [38;5;2m■[0m C# [38;5;8m22.6[0m [38;5;11m■[0m JavaScript [38;5;8m6[0m
|
||||||
|
[38;5;119m■[0m Ruby [38;5;8m6[0m [38;5;14m■[0m Shell [38;5;8m0.1[0m
|
@ -0,0 +1,3 @@
|
|||||||
|
████████████████████████████████████████████████████████████
|
||||||
|
■ SCSS 37 ■ HTML 28,3 ■ C# 22,6 ■ JavaScript 6
|
||||||
|
■ Ruby 6 ■ Shell 0,1
|
@ -0,0 +1,2 @@
|
|||||||
|
████████████████████████████████████████████████████████████████████████████████
|
||||||
|
■ SCSS 37 ■ HTML 28.3 ■ C# 22.6 ■ JavaScript 6 ■ Ruby 6 ■ Shell 0.1
|
@ -0,0 +1,4 @@
|
|||||||
|
████████████████████████████████████████████████████████████
|
||||||
|
|
||||||
|
■ SCSS 37 ■ HTML 28.3 ■ C# 22.6 ■ JavaScript 6
|
||||||
|
■ Ruby 6 ■ Shell 0.1
|
@ -0,0 +1,2 @@
|
|||||||
|
████████████████████████████████████████████████████████████
|
||||||
|
■ SCSS ■ HTML ■ C# ■ JavaScript ■ Ruby ■ Shell
|
@ -0,0 +1 @@
|
|||||||
|
████████████████████████████████████████████████████████████
|
@ -0,0 +1,3 @@
|
|||||||
|
████████████████████████████████████████████████████████████
|
||||||
|
■ SCSS 37% ■ HTML 28.3% ■ C# 22.6% ■ JavaScript 6%
|
||||||
|
■ Ruby 6% ■ Shell 0.1%
|
@ -0,0 +1,3 @@
|
|||||||
|
████████████████████████████████████████████████████████████
|
||||||
|
■ SCSS 37 ■ HTML 28.3 ■ C# 22.6 ■ JavaScript 6
|
||||||
|
■ Ruby 6 ■ Shell 0.1
|
@ -19,7 +19,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
|
||||||
<PackageReference Include="Shouldly" Version="4.0.3" />
|
<PackageReference Include="Shouldly" Version="4.0.3" />
|
||||||
<PackageReference Include="Spectre.Verify.Extensions" Version="0.1.0" />
|
<PackageReference Include="Spectre.Verify.Extensions" Version="0.3.0" />
|
||||||
<PackageReference Include="Verify.Xunit" Version="9.0.0-beta.1" />
|
<PackageReference Include="Verify.Xunit" Version="9.0.0-beta.1" />
|
||||||
<PackageReference Include="xunit" Version="2.4.1" />
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
|
||||||
|
@ -28,5 +28,24 @@ namespace Spectre.Console.Tests.Unit
|
|||||||
// Then
|
// Then
|
||||||
await Verifier.Verify(console.Output);
|
await Verifier.Verify(console.Output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Expectation("Zero_Value")]
|
||||||
|
public async Task Should_Render_Correctly_2()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new FakeConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new BarChart()
|
||||||
|
.Width(60)
|
||||||
|
.Label("Number of fruits")
|
||||||
|
.AddItem("Apple", 0)
|
||||||
|
.AddItem("Orange", 54)
|
||||||
|
.AddItem("Banana", 33));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
await Verifier.Verify(console.Output);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
186
src/Spectre.Console.Tests/Unit/BreakdownChartTests.cs
Normal file
186
src/Spectre.Console.Tests/Unit/BreakdownChartTests.cs
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Spectre.Console.Testing;
|
||||||
|
using Spectre.Verify.Extensions;
|
||||||
|
using VerifyXunit;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Spectre.Console.Tests.Unit
|
||||||
|
{
|
||||||
|
[UsesVerify]
|
||||||
|
[ExpectationPath("Widgets/BreakdownChart")]
|
||||||
|
public sealed class BreakdownChartTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
[Expectation("Default")]
|
||||||
|
public async Task Should_Render_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new FakeConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new BreakdownChart()
|
||||||
|
.AddItem("SCSS", 37, Color.Red)
|
||||||
|
.AddItem("HTML", 28.3, Color.Blue)
|
||||||
|
.AddItem("C#", 22.6, Color.Green)
|
||||||
|
.AddItem("JavaScript", 6, Color.Yellow)
|
||||||
|
.AddItem("Ruby", 6, Color.LightGreen)
|
||||||
|
.AddItem("Shell", 0.1, Color.Aqua));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
await Verifier.Verify(console.Output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Expectation("Width")]
|
||||||
|
public async Task Should_Render_With_Specific_Width()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new FakeConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new BreakdownChart()
|
||||||
|
.Width(60)
|
||||||
|
.AddItem("SCSS", 37, Color.Red)
|
||||||
|
.AddItem("HTML", 28.3, Color.Blue)
|
||||||
|
.AddItem("C#", 22.6, Color.Green)
|
||||||
|
.AddItem("JavaScript", 6, Color.Yellow)
|
||||||
|
.AddItem("Ruby", 6, Color.LightGreen)
|
||||||
|
.AddItem("Shell", 0.1, Color.Aqua));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
await Verifier.Verify(console.Output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Expectation("ShowAsPercentages")]
|
||||||
|
public async Task Should_Render_Correctly_With_Specific_Width()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new FakeConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new BreakdownChart()
|
||||||
|
.Width(60)
|
||||||
|
.ShowAsPercentages()
|
||||||
|
.AddItem("SCSS", 37, Color.Red)
|
||||||
|
.AddItem("HTML", 28.3, Color.Blue)
|
||||||
|
.AddItem("C#", 22.6, Color.Green)
|
||||||
|
.AddItem("JavaScript", 6, Color.Yellow)
|
||||||
|
.AddItem("Ruby", 6, Color.LightGreen)
|
||||||
|
.AddItem("Shell", 0.1, Color.Aqua));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
await Verifier.Verify(console.Output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Expectation("HideTags")]
|
||||||
|
public async Task Should_Render_Correctly_Without_Tags()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new FakeConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new BreakdownChart()
|
||||||
|
.Width(60)
|
||||||
|
.HideTags()
|
||||||
|
.AddItem("SCSS", 37, Color.Red)
|
||||||
|
.AddItem("HTML", 28.3, Color.Blue)
|
||||||
|
.AddItem("C#", 22.6, Color.Green)
|
||||||
|
.AddItem("JavaScript", 6, Color.Yellow)
|
||||||
|
.AddItem("Ruby", 6, Color.LightGreen)
|
||||||
|
.AddItem("Shell", 0.1, Color.Aqua));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
await Verifier.Verify(console.Output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Expectation("HideTagValues")]
|
||||||
|
public async Task Should_Render_Correctly_Without_Tag_Values()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new FakeConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new BreakdownChart()
|
||||||
|
.Width(60)
|
||||||
|
.HideTagValues()
|
||||||
|
.AddItem("SCSS", 37, Color.Red)
|
||||||
|
.AddItem("HTML", 28.3, Color.Blue)
|
||||||
|
.AddItem("C#", 22.6, Color.Green)
|
||||||
|
.AddItem("JavaScript", 6, Color.Yellow)
|
||||||
|
.AddItem("Ruby", 6, Color.LightGreen)
|
||||||
|
.AddItem("Shell", 0.1, Color.Aqua));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
await Verifier.Verify(console.Output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Expectation("Culture")]
|
||||||
|
public async Task Should_Render_Correctly_With_Specific_Culture()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new FakeConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new BreakdownChart()
|
||||||
|
.Width(60)
|
||||||
|
.Culture("sv-SE")
|
||||||
|
.AddItem("SCSS", 37, Color.Red)
|
||||||
|
.AddItem("HTML", 28.3, Color.Blue)
|
||||||
|
.AddItem("C#", 22.6, Color.Green)
|
||||||
|
.AddItem("JavaScript", 6, Color.Yellow)
|
||||||
|
.AddItem("Ruby", 6, Color.LightGreen)
|
||||||
|
.AddItem("Shell", 0.1, Color.Aqua));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
await Verifier.Verify(console.Output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Expectation("FullSize")]
|
||||||
|
public async Task Should_Render_FullSize_Mode_Correctly()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new FakeConsole(width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new BreakdownChart()
|
||||||
|
.Width(60)
|
||||||
|
.FullSize()
|
||||||
|
.AddItem("SCSS", 37, Color.Red)
|
||||||
|
.AddItem("HTML", 28.3, Color.Blue)
|
||||||
|
.AddItem("C#", 22.6, Color.Green)
|
||||||
|
.AddItem("JavaScript", 6, Color.Yellow)
|
||||||
|
.AddItem("Ruby", 6, Color.LightGreen)
|
||||||
|
.AddItem("Shell", 0.1, Color.Aqua));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
await Verifier.Verify(console.Output);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
[Expectation("Ansi")]
|
||||||
|
public async Task Should_Render_Correct_Ansi()
|
||||||
|
{
|
||||||
|
// Given
|
||||||
|
var console = new FakeAnsiConsole(ColorSystem.EightBit, width: 80);
|
||||||
|
|
||||||
|
// When
|
||||||
|
console.Render(new BreakdownChart()
|
||||||
|
.Width(60)
|
||||||
|
.FullSize()
|
||||||
|
.AddItem("SCSS", 37, Color.Red)
|
||||||
|
.AddItem("HTML", 28.3, Color.Blue)
|
||||||
|
.AddItem("C#", 22.6, Color.Green)
|
||||||
|
.AddItem("JavaScript", 6, Color.Yellow)
|
||||||
|
.AddItem("Ruby", 6, Color.LightGreen)
|
||||||
|
.AddItem("Shell", 0.1, Color.Aqua));
|
||||||
|
|
||||||
|
// Then
|
||||||
|
await Verifier.Verify(console.Output);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -6,7 +6,7 @@ namespace Spectre.Console
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Contains extension methods for <see cref="BarChart"/>.
|
/// Contains extension methods for <see cref="BarChart"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static class BarGraphExtensions
|
public static class BarChartExtensions
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds an item to the bar chart.
|
/// Adds an item to the bar chart.
|
||||||
@ -42,10 +42,18 @@ namespace Spectre.Console
|
|||||||
throw new ArgumentNullException(nameof(chart));
|
throw new ArgumentNullException(nameof(chart));
|
||||||
}
|
}
|
||||||
|
|
||||||
chart.Data.Add(new BarChartItem(
|
if (item is BarChartItem barChartItem)
|
||||||
|
{
|
||||||
|
chart.Data.Add(barChartItem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chart.Data.Add(
|
||||||
|
new BarChartItem(
|
||||||
item.Label,
|
item.Label,
|
||||||
item.Value,
|
item.Value,
|
||||||
item.Color));
|
item.Color));
|
||||||
|
}
|
||||||
|
|
||||||
return chart;
|
return chart;
|
||||||
}
|
}
|
266
src/Spectre.Console/Extensions/BreakdownChartExtensions.cs
Normal file
266
src/Spectre.Console/Extensions/BreakdownChartExtensions.cs
Normal file
@ -0,0 +1,266 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Contains extension methods for <see cref="BreakdownChart"/>.
|
||||||
|
/// </summary>
|
||||||
|
public static class BreakdownChartExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Adds an item to the breakdown chart.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chart">The breakdown chart.</param>
|
||||||
|
/// <param name="label">The item label.</param>
|
||||||
|
/// <param name="value">The item value.</param>
|
||||||
|
/// <param name="color">The item color.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public static BreakdownChart AddItem(this BreakdownChart chart, string label, double value, Color color)
|
||||||
|
{
|
||||||
|
if (chart is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(chart));
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.Data.Add(new BreakdownChartItem(label, value, color));
|
||||||
|
return chart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds an item to the breakdown chart.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">A type that implements <see cref="IBreakdownChartItem"/>.</typeparam>
|
||||||
|
/// <param name="chart">The breakdown chart.</param>
|
||||||
|
/// <param name="item">The item.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public static BreakdownChart AddItem<T>(this BreakdownChart chart, T item)
|
||||||
|
where T : IBreakdownChartItem
|
||||||
|
{
|
||||||
|
if (chart is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(chart));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item is BreakdownChartItem chartItem)
|
||||||
|
{
|
||||||
|
chart.Data.Add(chartItem);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
chart.Data.Add(
|
||||||
|
new BreakdownChartItem(
|
||||||
|
item.Label,
|
||||||
|
item.Value,
|
||||||
|
item.Color));
|
||||||
|
}
|
||||||
|
|
||||||
|
return chart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds multiple items to the breakdown chart.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">A type that implements <see cref="IBreakdownChartItem"/>.</typeparam>
|
||||||
|
/// <param name="chart">The breakdown chart.</param>
|
||||||
|
/// <param name="items">The items.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public static BreakdownChart AddItems<T>(this BreakdownChart chart, IEnumerable<T> items)
|
||||||
|
where T : IBreakdownChartItem
|
||||||
|
{
|
||||||
|
if (chart is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(chart));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(items));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
AddItem(chart, item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return chart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds multiple items to the breakdown chart.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">A type that implements <see cref="IBarChartItem"/>.</typeparam>
|
||||||
|
/// <param name="chart">The breakdown chart.</param>
|
||||||
|
/// <param name="items">The items.</param>
|
||||||
|
/// <param name="converter">The converter that converts instances of <c>T</c> to <see cref="IBreakdownChartItem"/>.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public static BreakdownChart AddItems<T>(this BreakdownChart chart, IEnumerable<T> items, Func<T, IBreakdownChartItem> converter)
|
||||||
|
{
|
||||||
|
if (chart is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(chart));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (items is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(items));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (converter is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(converter));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var item in items)
|
||||||
|
{
|
||||||
|
chart.Data.Add(converter(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
return chart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the width of the breakdown chart.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chart">The breakdown chart.</param>
|
||||||
|
/// <param name="width">The breakdown chart width.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public static BreakdownChart Width(this BreakdownChart chart, int? width)
|
||||||
|
{
|
||||||
|
if (chart is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(chart));
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.Width = width;
|
||||||
|
return chart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All values will be shown as percentages.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chart">The breakdown chart.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public static BreakdownChart ShowAsPercentages(this BreakdownChart chart)
|
||||||
|
{
|
||||||
|
if (chart is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(chart));
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.ShowAsPercentages = true;
|
||||||
|
return chart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tags will be shown.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chart">The breakdown chart.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public static BreakdownChart ShowTags(this BreakdownChart chart)
|
||||||
|
{
|
||||||
|
return ShowTags(chart, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tags will be not be shown.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chart">The breakdown chart.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public static BreakdownChart HideTags(this BreakdownChart chart)
|
||||||
|
{
|
||||||
|
return ShowTags(chart, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets whether or not tags will be shown.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chart">The breakdown chart.</param>
|
||||||
|
/// <param name="show">Whether or not tags will be shown.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public static BreakdownChart ShowTags(this BreakdownChart chart, bool show)
|
||||||
|
{
|
||||||
|
if (chart is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(chart));
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.ShowTags = show;
|
||||||
|
return chart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tag values will be shown.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chart">The breakdown chart.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public static BreakdownChart ShowTagValues(this BreakdownChart chart)
|
||||||
|
{
|
||||||
|
return ShowTagValues(chart, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tag values will be not be shown.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chart">The breakdown chart.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public static BreakdownChart HideTagValues(this BreakdownChart chart)
|
||||||
|
{
|
||||||
|
return ShowTagValues(chart, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets whether or not tag values will be shown.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chart">The breakdown chart.</param>
|
||||||
|
/// <param name="show">Whether or not tag values will be shown.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public static BreakdownChart ShowTagValues(this BreakdownChart chart, bool show)
|
||||||
|
{
|
||||||
|
if (chart is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(chart));
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.ShowTagValues = show;
|
||||||
|
return chart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Chart and tags is rendered in compact mode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chart">The breakdown chart.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public static BreakdownChart Compact(this BreakdownChart chart)
|
||||||
|
{
|
||||||
|
return Compact(chart, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Chart and tags is rendered in full size mode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chart">The breakdown chart.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public static BreakdownChart FullSize(this BreakdownChart chart)
|
||||||
|
{
|
||||||
|
return Compact(chart, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets whether or not the chart and tags should be rendered in compact mode.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="chart">The breakdown chart.</param>
|
||||||
|
/// <param name="compact">Whether or not the chart and tags should be rendered in compact mode.</param>
|
||||||
|
/// <returns>The same instance so that multiple calls can be chained.</returns>
|
||||||
|
public static BreakdownChart Compact(this BreakdownChart chart, bool compact)
|
||||||
|
{
|
||||||
|
if (chart is null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(chart));
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.Compact = compact;
|
||||||
|
return chart;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,7 +10,7 @@ namespace Spectre.Console
|
|||||||
culture ??= CultureInfo.InvariantCulture;
|
culture ??= CultureInfo.InvariantCulture;
|
||||||
return culture.DateTimeFormat
|
return culture.DateTimeFormat
|
||||||
.GetAbbreviatedDayName(day)
|
.GetAbbreviatedDayName(day)
|
||||||
.Capitalize(culture);
|
.CapitalizeFirstLetter(culture);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static DayOfWeek GetNextWeekDay(this DayOfWeek day)
|
public static DayOfWeek GetNextWeekDay(this DayOfWeek day)
|
||||||
|
@ -44,7 +44,7 @@ namespace Spectre.Console
|
|||||||
return Cell.GetCellLength(context, text);
|
return Cell.GetCellLength(context, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static string Capitalize(this string? text, CultureInfo? culture = null)
|
internal static string CapitalizeFirstLetter(this string? text, CultureInfo? culture = null)
|
||||||
{
|
{
|
||||||
if (text == null)
|
if (text == null)
|
||||||
{
|
{
|
||||||
|
@ -10,6 +10,6 @@ namespace Spectre.Console
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the culture.
|
/// Gets or sets the culture.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
CultureInfo Culture { get; set; }
|
CultureInfo? Culture { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,7 +40,7 @@ namespace Spectre.Console
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<int> Distribute(int total, List<int> ratios, List<int>? minimums = null)
|
public static List<int> Distribute(int total, IList<int> ratios, IList<int>? minimums = null)
|
||||||
{
|
{
|
||||||
if (minimums != null)
|
if (minimums != null)
|
||||||
{
|
{
|
||||||
|
@ -22,7 +22,7 @@ namespace Spectre.Console
|
|||||||
private TableBorder _border;
|
private TableBorder _border;
|
||||||
private bool _useSafeBorder;
|
private bool _useSafeBorder;
|
||||||
private Style? _borderStyle;
|
private Style? _borderStyle;
|
||||||
private CultureInfo _culture;
|
private CultureInfo? _culture;
|
||||||
private Style _highlightStyle;
|
private Style _highlightStyle;
|
||||||
private bool _showHeader;
|
private bool _showHeader;
|
||||||
private Style? _headerStyle;
|
private Style? _headerStyle;
|
||||||
@ -79,7 +79,7 @@ namespace Spectre.Console
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the calendar's <see cref="CultureInfo"/>.
|
/// Gets or sets the calendar's <see cref="CultureInfo"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public CultureInfo Culture
|
public CultureInfo? Culture
|
||||||
{
|
{
|
||||||
get => _culture;
|
get => _culture;
|
||||||
set => MarkAsDirty(() => _culture = value);
|
set => MarkAsDirty(() => _culture = value);
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using Spectre.Console.Rendering;
|
using Spectre.Console.Rendering;
|
||||||
|
|
||||||
@ -7,12 +9,12 @@ namespace Spectre.Console
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// A renderable (horizontal) bar chart.
|
/// A renderable (horizontal) bar chart.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class BarChart : Renderable
|
public sealed class BarChart : Renderable, IHasCulture
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the bar chart data.
|
/// Gets the bar chart data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<BarChartItem> Data { get; }
|
public List<IBarChartItem> Data { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the width of the bar chart.
|
/// Gets or sets the width of the bar chart.
|
||||||
@ -35,24 +37,38 @@ namespace Spectre.Console
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool ShowValues { get; set; } = true;
|
public bool ShowValues { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the culture that's used to format values.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Defaults to invariant culture.</remarks>
|
||||||
|
public CultureInfo? Culture { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="BarChart"/> class.
|
/// Initializes a new instance of the <see cref="BarChart"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public BarChart()
|
public BarChart()
|
||||||
{
|
{
|
||||||
Data = new List<BarChartItem>();
|
Data = new List<IBarChartItem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override Measurement Measure(RenderContext context, int maxWidth)
|
||||||
|
{
|
||||||
|
var width = Math.Min(Width ?? maxWidth, maxWidth);
|
||||||
|
return new Measurement(width, width);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
|
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
|
||||||
{
|
{
|
||||||
|
var width = Math.Min(Width ?? maxWidth, maxWidth);
|
||||||
var maxValue = Data.Max(item => item.Value);
|
var maxValue = Data.Max(item => item.Value);
|
||||||
|
|
||||||
var grid = new Grid();
|
var grid = new Grid();
|
||||||
grid.Collapse();
|
grid.Collapse();
|
||||||
grid.AddColumn(new GridColumn().PadRight(2).RightAligned());
|
grid.AddColumn(new GridColumn().PadRight(2).RightAligned());
|
||||||
grid.AddColumn(new GridColumn().PadLeft(0));
|
grid.AddColumn(new GridColumn().PadLeft(0));
|
||||||
grid.Width = Width;
|
grid.Width = width;
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(Label))
|
if (!string.IsNullOrWhiteSpace(Label))
|
||||||
{
|
{
|
||||||
@ -73,10 +89,11 @@ namespace Spectre.Console
|
|||||||
UnicodeBar = '█',
|
UnicodeBar = '█',
|
||||||
AsciiBar = '█',
|
AsciiBar = '█',
|
||||||
ShowValue = ShowValues,
|
ShowValue = ShowValues,
|
||||||
|
Culture = Culture,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return ((IRenderable)grid).Render(context, maxWidth);
|
return ((IRenderable)grid).Render(context, width);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
42
src/Spectre.Console/Widgets/Charts/BreakdownBar.cs
Normal file
42
src/Spectre.Console/Widgets/Charts/BreakdownBar.cs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Spectre.Console.Rendering;
|
||||||
|
|
||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
internal sealed class BreakdownBar : Renderable
|
||||||
|
{
|
||||||
|
private readonly List<IBreakdownChartItem> _data;
|
||||||
|
|
||||||
|
public int? Width { get; set; }
|
||||||
|
|
||||||
|
public BreakdownBar(List<IBreakdownChartItem> data)
|
||||||
|
{
|
||||||
|
_data = data ?? throw new ArgumentNullException(nameof(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Measurement Measure(RenderContext context, int maxWidth)
|
||||||
|
{
|
||||||
|
var width = Math.Min(Width ?? maxWidth, maxWidth);
|
||||||
|
return new Measurement(width, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
|
||||||
|
{
|
||||||
|
var width = Math.Min(Width ?? maxWidth, maxWidth);
|
||||||
|
|
||||||
|
// Chart
|
||||||
|
var maxValue = _data.Sum(i => i.Value);
|
||||||
|
var items = _data.ToArray();
|
||||||
|
var bars = Ratio.Distribute(width, items.Select(i => Math.Max(0, (int)(width * (i.Value / maxValue)))).ToArray());
|
||||||
|
|
||||||
|
for (var index = 0; index < items.Length; index++)
|
||||||
|
{
|
||||||
|
yield return new Segment(new string('█', bars[index]), new Style(items[index].Color));
|
||||||
|
}
|
||||||
|
|
||||||
|
yield return Segment.LineBreak;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
102
src/Spectre.Console/Widgets/Charts/BreakdownChart.cs
Normal file
102
src/Spectre.Console/Widgets/Charts/BreakdownChart.cs
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using Spectre.Console.Rendering;
|
||||||
|
|
||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A renderable breakdown chart.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class BreakdownChart : Renderable, IHasCulture
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the breakdown chart data.
|
||||||
|
/// </summary>
|
||||||
|
public List<IBreakdownChartItem> Data { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the width of the breakdown chart.
|
||||||
|
/// </summary>
|
||||||
|
public int? Width { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether or not
|
||||||
|
/// to show values as percentages or not.
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowAsPercentages { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether or not to show tags.
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowTags { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether or not to show tag values.
|
||||||
|
/// </summary>
|
||||||
|
public bool ShowTagValues { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether or not the
|
||||||
|
/// chart and tags should be rendered in compact mode.
|
||||||
|
/// </summary>
|
||||||
|
public bool Compact { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the <see cref="CultureInfo"/> to use
|
||||||
|
/// when rendering values.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>Defaults to invariant culture.</remarks>
|
||||||
|
public CultureInfo? Culture { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="BreakdownChart"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public BreakdownChart()
|
||||||
|
{
|
||||||
|
Data = new List<IBreakdownChartItem>();
|
||||||
|
Culture = CultureInfo.InvariantCulture;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override Measurement Measure(RenderContext context, int maxWidth)
|
||||||
|
{
|
||||||
|
var width = Math.Min(Width ?? maxWidth, maxWidth);
|
||||||
|
return new Measurement(width, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
|
||||||
|
{
|
||||||
|
var width = Math.Min(Width ?? maxWidth, maxWidth);
|
||||||
|
|
||||||
|
var grid = new Grid().Width(width);
|
||||||
|
grid.AddColumn(new GridColumn().NoWrap());
|
||||||
|
|
||||||
|
// Bar
|
||||||
|
grid.AddRow(new BreakdownBar(Data)
|
||||||
|
{
|
||||||
|
Width = width,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ShowTags)
|
||||||
|
{
|
||||||
|
if (!Compact)
|
||||||
|
{
|
||||||
|
grid.AddEmptyRow();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tags
|
||||||
|
grid.AddRow(new BreakdownTags(Data)
|
||||||
|
{
|
||||||
|
Width = width,
|
||||||
|
Culture = Culture,
|
||||||
|
ShowPercentages = ShowAsPercentages,
|
||||||
|
ShowTagValues = ShowTagValues,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return ((IRenderable)grid).Render(context, width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
38
src/Spectre.Console/Widgets/Charts/BreakdownChartItem.cs
Normal file
38
src/Spectre.Console/Widgets/Charts/BreakdownChartItem.cs
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An item that's shown in a breakdown chart.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class BreakdownChartItem : IBreakdownChartItem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the item label.
|
||||||
|
/// </summary>
|
||||||
|
public string Label { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the item value.
|
||||||
|
/// </summary>
|
||||||
|
public double Value { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the item color.
|
||||||
|
/// </summary>
|
||||||
|
public Color Color { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="BreakdownChartItem"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="label">The item label.</param>
|
||||||
|
/// <param name="value">The item value.</param>
|
||||||
|
/// <param name="color">The item color.</param>
|
||||||
|
public BreakdownChartItem(string label, double value, Color color)
|
||||||
|
{
|
||||||
|
Label = label ?? throw new ArgumentNullException(nameof(label));
|
||||||
|
Value = value;
|
||||||
|
Color = color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
69
src/Spectre.Console/Widgets/Charts/BreakdownTags.cs
Normal file
69
src/Spectre.Console/Widgets/Charts/BreakdownTags.cs
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using Spectre.Console.Rendering;
|
||||||
|
|
||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
internal sealed class BreakdownTags : Renderable
|
||||||
|
{
|
||||||
|
private readonly List<IBreakdownChartItem> _data;
|
||||||
|
|
||||||
|
public int? Width { get; set; }
|
||||||
|
public CultureInfo? Culture { get; set; }
|
||||||
|
public bool ShowPercentages { get; set; }
|
||||||
|
public bool ShowTagValues { get; set; } = true;
|
||||||
|
|
||||||
|
public BreakdownTags(List<IBreakdownChartItem> data)
|
||||||
|
{
|
||||||
|
_data = data ?? throw new ArgumentNullException(nameof(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Measurement Measure(RenderContext context, int maxWidth)
|
||||||
|
{
|
||||||
|
var width = Math.Min(Width ?? maxWidth, maxWidth);
|
||||||
|
return new Measurement(width, width);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
|
||||||
|
{
|
||||||
|
var culture = Culture ?? CultureInfo.InvariantCulture;
|
||||||
|
|
||||||
|
var panels = new List<Panel>();
|
||||||
|
foreach (var item in _data)
|
||||||
|
{
|
||||||
|
var panel = new Panel(GetTag(item, culture));
|
||||||
|
panel.Inline = true;
|
||||||
|
panel.Padding = new Padding(0, 0);
|
||||||
|
panel.NoBorder();
|
||||||
|
|
||||||
|
panels.Add(panel);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var segment in ((IRenderable)new Columns(panels).Padding(0, 0)).Render(context, maxWidth))
|
||||||
|
{
|
||||||
|
yield return segment;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetTag(IBreakdownChartItem item, CultureInfo culture)
|
||||||
|
{
|
||||||
|
return string.Format(
|
||||||
|
culture, "[{0}]■[/] {1}",
|
||||||
|
item.Color.ToMarkup() ?? "default",
|
||||||
|
FormatValue(item, culture)).Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private string FormatValue(IBreakdownChartItem item, CultureInfo culture)
|
||||||
|
{
|
||||||
|
if (ShowTagValues)
|
||||||
|
{
|
||||||
|
return string.Format(culture, "{0} [grey]{1}{2}[/]",
|
||||||
|
item.Label.EscapeMarkup(), item.Value,
|
||||||
|
ShowPercentages ? "%" : string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item.Label.EscapeMarkup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
src/Spectre.Console/Widgets/Charts/IBreakdownChartItem.cs
Normal file
23
src/Spectre.Console/Widgets/Charts/IBreakdownChartItem.cs
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
namespace Spectre.Console
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a breakdown chart item.
|
||||||
|
/// </summary>
|
||||||
|
public interface IBreakdownChartItem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the item label.
|
||||||
|
/// </summary>
|
||||||
|
string Label { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the item value.
|
||||||
|
/// </summary>
|
||||||
|
double Value { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the item color.
|
||||||
|
/// </summary>
|
||||||
|
Color Color { get; }
|
||||||
|
}
|
||||||
|
}
|
@ -40,6 +40,11 @@ namespace Spectre.Console
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public PanelHeader? Header { get; set; }
|
public PanelHeader? Header { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets a value indicating whether or not the panel is inlined.
|
||||||
|
/// </summary>
|
||||||
|
internal bool Inline { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="Panel"/> class.
|
/// Initializes a new instance of the <see cref="Panel"/> class.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -71,29 +76,41 @@ namespace Spectre.Console
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
|
protected override IEnumerable<Segment> Render(RenderContext context, int maxWidth)
|
||||||
{
|
{
|
||||||
|
var edgeWidth = EdgeWidth;
|
||||||
|
|
||||||
var border = BoxExtensions.GetSafeBorder(Border, (context.LegacyConsole || !context.Unicode) && UseSafeBorder);
|
var border = BoxExtensions.GetSafeBorder(Border, (context.LegacyConsole || !context.Unicode) && UseSafeBorder);
|
||||||
var borderStyle = BorderStyle ?? Style.Plain;
|
var borderStyle = BorderStyle ?? Style.Plain;
|
||||||
|
|
||||||
|
var showBorder = true;
|
||||||
|
if (border is NoBoxBorder)
|
||||||
|
{
|
||||||
|
showBorder = false;
|
||||||
|
edgeWidth = 0;
|
||||||
|
}
|
||||||
|
|
||||||
var child = new Padder(_child, Padding);
|
var child = new Padder(_child, Padding);
|
||||||
var childWidth = maxWidth - EdgeWidth;
|
var childWidth = maxWidth - edgeWidth;
|
||||||
|
|
||||||
if (!Expand)
|
if (!Expand)
|
||||||
{
|
{
|
||||||
var measurement = ((IRenderable)child).Measure(context, maxWidth - EdgeWidth);
|
var measurement = ((IRenderable)child).Measure(context, maxWidth - edgeWidth);
|
||||||
childWidth = measurement.Max;
|
childWidth = measurement.Max;
|
||||||
}
|
}
|
||||||
|
|
||||||
var panelWidth = childWidth + EdgeWidth;
|
var panelWidth = childWidth + edgeWidth;
|
||||||
panelWidth = Math.Min(panelWidth, maxWidth);
|
panelWidth = Math.Min(panelWidth, maxWidth);
|
||||||
|
|
||||||
var result = new List<Segment>();
|
var result = new List<Segment>();
|
||||||
|
|
||||||
|
if (showBorder)
|
||||||
|
{
|
||||||
// Panel top
|
// Panel top
|
||||||
AddTopBorder(result, context, border, borderStyle, panelWidth);
|
AddTopBorder(result, context, border, borderStyle, panelWidth);
|
||||||
|
}
|
||||||
|
|
||||||
// Split the child segments into lines.
|
// Split the child segments into lines.
|
||||||
var childSegments = ((IRenderable)child).Render(context, childWidth);
|
var childSegments = ((IRenderable)child).Render(context, childWidth);
|
||||||
foreach (var line in Segment.SplitLines(context, childSegments, childWidth))
|
foreach (var (_, _, last, line) in Segment.SplitLines(context, childSegments, childWidth).Enumerate())
|
||||||
{
|
{
|
||||||
if (line.Count == 1 && line[0].IsWhiteSpace)
|
if (line.Count == 1 && line[0].IsWhiteSpace)
|
||||||
{
|
{
|
||||||
@ -102,7 +119,10 @@ namespace Spectre.Console
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (showBorder)
|
||||||
|
{
|
||||||
result.Add(new Segment(border.GetPart(BoxBorderPart.Left), borderStyle));
|
result.Add(new Segment(border.GetPart(BoxBorderPart.Left), borderStyle));
|
||||||
|
}
|
||||||
|
|
||||||
var content = new List<Segment>();
|
var content = new List<Segment>();
|
||||||
content.AddRange(line);
|
content.AddRange(line);
|
||||||
@ -117,20 +137,45 @@ namespace Spectre.Console
|
|||||||
|
|
||||||
result.AddRange(content);
|
result.AddRange(content);
|
||||||
|
|
||||||
|
if (showBorder)
|
||||||
|
{
|
||||||
result.Add(new Segment(border.GetPart(BoxBorderPart.Right), borderStyle));
|
result.Add(new Segment(border.GetPart(BoxBorderPart.Right), borderStyle));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't emit a line break if this is the last
|
||||||
|
// line, we're not showing the border, and we're
|
||||||
|
// not rendering this inline.
|
||||||
|
var emitLinebreak = !(last && !showBorder && !Inline);
|
||||||
|
if (!emitLinebreak)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
result.Add(Segment.LineBreak);
|
result.Add(Segment.LineBreak);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Panel bottom
|
// Panel bottom
|
||||||
|
if (showBorder)
|
||||||
|
{
|
||||||
result.Add(new Segment(border.GetPart(BoxBorderPart.BottomLeft), borderStyle));
|
result.Add(new Segment(border.GetPart(BoxBorderPart.BottomLeft), borderStyle));
|
||||||
result.Add(new Segment(border.GetPart(BoxBorderPart.Bottom).Repeat(panelWidth - EdgeWidth), borderStyle));
|
result.Add(new Segment(border.GetPart(BoxBorderPart.Bottom).Repeat(panelWidth - EdgeWidth), borderStyle));
|
||||||
result.Add(new Segment(border.GetPart(BoxBorderPart.BottomRight), borderStyle));
|
result.Add(new Segment(border.GetPart(BoxBorderPart.BottomRight), borderStyle));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Need a better name for this?
|
||||||
|
// If we're rendering this as part of an inline parent renderable,
|
||||||
|
// such as columns, we should not emit the last line break.
|
||||||
|
if (!Inline)
|
||||||
|
{
|
||||||
result.Add(Segment.LineBreak);
|
result.Add(Segment.LineBreak);
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddTopBorder(List<Segment> result, RenderContext context, BoxBorder border, Style borderStyle, int panelWidth)
|
private void AddTopBorder(
|
||||||
|
List<Segment> result, RenderContext context, BoxBorder border,
|
||||||
|
Style borderStyle, int panelWidth)
|
||||||
{
|
{
|
||||||
var rule = new Rule
|
var rule = new Rule
|
||||||
{
|
{
|
||||||
|
@ -5,7 +5,7 @@ using Spectre.Console.Rendering;
|
|||||||
|
|
||||||
namespace Spectre.Console
|
namespace Spectre.Console
|
||||||
{
|
{
|
||||||
internal sealed class ProgressBar : Renderable
|
internal sealed class ProgressBar : Renderable, IHasCulture
|
||||||
{
|
{
|
||||||
public double Value { get; set; }
|
public double Value { get; set; }
|
||||||
public double MaxValue { get; set; } = 100;
|
public double MaxValue { get; set; } = 100;
|
||||||
@ -15,6 +15,7 @@ namespace Spectre.Console
|
|||||||
public char UnicodeBar { get; set; } = '━';
|
public char UnicodeBar { get; set; } = '━';
|
||||||
public char AsciiBar { get; set; } = '-';
|
public char AsciiBar { get; set; } = '-';
|
||||||
public bool ShowValue { get; set; }
|
public bool ShowValue { get; set; }
|
||||||
|
public CultureInfo? Culture { get; set; }
|
||||||
|
|
||||||
public Style CompletedStyle { get; set; } = new Style(foreground: Color.Yellow);
|
public Style CompletedStyle { get; set; } = new Style(foreground: Color.Yellow);
|
||||||
public Style FinishedStyle { get; set; } = new Style(foreground: Color.Green);
|
public Style FinishedStyle { get; set; } = new Style(foreground: Color.Green);
|
||||||
@ -36,18 +37,27 @@ namespace Spectre.Console
|
|||||||
|
|
||||||
var bars = Math.Max(0, (int)(width * (completed / MaxValue)));
|
var bars = Math.Max(0, (int)(width * (completed / MaxValue)));
|
||||||
|
|
||||||
var value = completed.ToString(CultureInfo.InvariantCulture);
|
var value = completed.ToString(Culture ?? CultureInfo.InvariantCulture);
|
||||||
if (ShowValue)
|
if (ShowValue)
|
||||||
{
|
{
|
||||||
bars = bars - value.Length - 1;
|
bars = bars - value.Length - 1;
|
||||||
|
bars = Math.Max(0, bars);
|
||||||
}
|
}
|
||||||
|
|
||||||
yield return new Segment(new string(token, bars), style);
|
yield return new Segment(new string(token, bars), style);
|
||||||
|
|
||||||
if (ShowValue)
|
if (ShowValue)
|
||||||
|
{
|
||||||
|
// TODO: Fix this at some point
|
||||||
|
if (bars == 0)
|
||||||
|
{
|
||||||
|
yield return new Segment(value, style);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
yield return new Segment(" " + value, style);
|
yield return new Segment(" " + value, style);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (bars < width)
|
if (bars < width)
|
||||||
{
|
{
|
||||||
|
@ -163,7 +163,7 @@ namespace Spectre.Console
|
|||||||
return Array.Empty<Segment>();
|
return Array.Empty<Segment>();
|
||||||
}
|
}
|
||||||
|
|
||||||
var paragraph = new Markup(header.Text.Capitalize(), header.Style ?? defaultStyle)
|
var paragraph = new Markup(header.Text.CapitalizeFirstLetter(), header.Style ?? defaultStyle)
|
||||||
.Alignment(Justify.Center)
|
.Alignment(Justify.Center)
|
||||||
.Overflow(Overflow.Ellipsis);
|
.Overflow(Overflow.Ellipsis);
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user