Skip to content

Commit 1af5c7e

Browse files
committed
Various fixes
This also reverts commit a0833eb.
1 parent cbdce06 commit 1af5c7e

File tree

19 files changed

+605
-212
lines changed

19 files changed

+605
-212
lines changed

doc/controls/barchart.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# BarChart
22

3-
`BarChart` renders a horizontal bar chart with a label column, a bar column and an optional value column.
3+
`BarChart` renders a horizontal bar chart with a label column and a bar column. When enabled, the value text is
4+
rendered near the end of the filled portion of each bar (instead of being flush-right at the end of the chart).
45

56
> Screenshots: TODO
67
@@ -24,9 +25,9 @@ var chart = new BarChart()
2425

2526
Items are stored in `BarChart.Items` as `BarChartItem` objects:
2627

27-
- `BarChartItem.Label`: a visual displayed in the left column (you can use `TextBlock`, `Markup`, an `HStack` with an icon, etc.).
28+
- `BarChartItem.Label`: a visual displayed in the left column (you can use `"Text"`, `Markup`, an `HStack` with an icon, etc.).
2829
- `BarChartItem.Value`: numeric value used to compute bar fill.
29-
- `BarChartItem.ValueLabel`: optional explicit value label visual for the right column.
30+
- `BarChartItem.ValueLabel`: optional explicit value label visual. When set, it is positioned near the end of the bar.
3031
- `BarChartItem.BarColor`: optional bar color override.
3132

3233
## Range
@@ -45,3 +46,4 @@ The chart is styled via `BarChartStyle`:
4546
- `BarStyle`: progress bar style used for each bar row.
4647
- `DefaultBarColors`: optional palette used when items do not provide `BarColor`.
4748

49+
`BarChartStyle.ValueTextStyle` can be used to customize the default value text (when `BarChartItem.ValueLabel` is not set).
Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
# Breakdown
1+
# BreakdownChart
22

3-
`Breakdown` renders a segmented proportional bar (a “breakdown”) with an optional legend. It is useful for showing how
3+
`BreakdownChart` renders a segmented proportional bar (a “breakdown”) with an optional legend. It is useful for showing how
44
a total value is distributed across categories (disk usage, budgets, KPIs, resource usage, etc.).
55

66
> Screenshots: TODO
77
88
## Basic usage
99

1010
```csharp
11-
var breakdown = new Breakdown()
11+
var breakdown = new BreakdownChart()
1212
.Title("Disk usage")
1313
.ShowValues(true)
1414
.ShowPercentages(true)
@@ -20,18 +20,18 @@ var breakdown = new Breakdown()
2020

2121
## Segments
2222

23-
Segments are stored in `Breakdown.Segments` as `BreakdownSegment` objects.
23+
Segments are stored in `BreakdownChart.Segments` as `BreakdownSegment` objects.
2424

2525
- `BreakdownSegment.Value`: numeric value used to compute proportions.
2626
- `BreakdownSegment.Label`: a visual shown in the legend (use a `TextBlock`, `Markup`, an `HStack` with an icon, etc.).
2727
- `BreakdownSegment.Color`: optional segment fill color; when not provided, the control cycles through theme tones.
2828
- `BreakdownSegment.Tooltip`: optional tooltip content shown when hovering the segment in the bar.
2929

30-
For convenience, you can append segments fluently using `BreakdownExtensions.Segment(...)` as shown above.
30+
For convenience, you can append segments fluently using `BreakdownChartExtensions.Segment(...)` as shown above.
3131

3232
## Interaction
3333

34-
`Breakdown` raises a routed `SegmentClicked` event when the user clicks a segment:
34+
`BreakdownChart` raises a routed `SegmentClicked` event when the user clicks a segment:
3535

3636
```csharp
3737
breakdown.SegmentClicked((_, e) =>
@@ -42,16 +42,18 @@ breakdown.SegmentClicked((_, e) =>
4242

4343
## Layout
4444

45-
- `Breakdown.Title`: optional title shown above the bar.
46-
- `Breakdown.LegendPlacement`: `Above` or `Below` (default: `Below`).
47-
- `Breakdown.ShowValues` / `Breakdown.ShowPercentages`: controls legend value display.
45+
- `BreakdownChart.Title`: optional title shown above the bar.
46+
- `BreakdownChart.LegendPlacement`: `Above` or `Below` (default: `Below`).
47+
- `BreakdownChart.ShowValues` / `BreakdownChart.ShowPercentages`: controls legend value display.
4848

4949
## Styling
5050

51-
`Breakdown` is styled via `BreakdownStyle`:
51+
`BreakdownChart` is styled via `BreakdownStyle`:
5252

5353
- `FillRune`: rune used to fill the bar (default is a space with colored backgrounds).
5454
- `SegmentGap`: number of empty cells between segments.
55+
- `LegendLayout`: compact (wrap) or expanded (one item per line).
56+
- `LegendItemSpacing`: spacing between legend items when using the compact layout.
5557
- `BarStyle`: optional base style applied to bar cells.
5658
- `DefaultSegmentColors`: optional palette used when a segment does not provide an explicit `Color`.
5759

@@ -63,4 +65,3 @@ breakdown.Style(new BreakdownStyle
6365
SegmentGap = 0,
6466
});
6567
```
66-

doc/controls/index.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ This section documents the built-in controls provided by XenoAtom.Terminal.UI.
6666
- `doc/controls/progressbar.md`
6767
- `doc/controls/progresstaskgroup.md`
6868
- `doc/controls/spinner.md`
69-
- `doc/controls/breakdown.md`
69+
- `doc/controls/breakdownchart.md`
7070
- `doc/controls/barchart.md`
7171
- `doc/controls/linechart.md`
7272
- `doc/controls/sparkline.md`

doc/specs/controls_todo.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ Following this list, you will find compact specs for high and medium priority co
1919
| [x] | **High** | **TextArea (multiline editor)** | Input | Multi-line editing | "Real app" workflows beyond TextBox |
2020
| [x] | **High** | **TreeView** | Navigation | Hierarchical navigation | Big UX upgrade over flat lists |
2121
| [x] | **High** | **TooltipHost / Tooltip** | Overlays | Contextual help everywhere | Modern UX + pairs well with complex UIs |
22-
| [x] | **High** | **Breakdown (segmented bar)** | Visualization | Proportional parts of a whole | Great dashboards + interactive UX |
22+
| [x] | **High** | **BreakdownChart (segmented bar)** | Visualization | Proportional parts of a whole | Great dashboards + interactive UX |
2323
| [x] | **High** | **Inline prompt API** | Input/API | Interactive prompts (inline) | Great CLI UX without full apps |
2424
| [x] | **High** | **ColorPicker** | Input | Picking colors + palettes | Essential for theming/tools + great demo value |
2525
| [x] | **Medium** | **LoadingIndicator / Spinner** | Status | Busy state (unknown duration) | Complements ProgressBar |

doc/specs/remaining_controls_specs.md

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -173,18 +173,18 @@ Introduce `TooltipStyle`:
173173
- Add tests verifying:
174174
- Tooltip appears after delay and closes on leave.
175175
- Tooltip does not steal focus and does not intercept clicks.
176-
- Add ControlsDemo usage (e.g. tooltips in Button demo and in the Breakdown control when implemented).
176+
- Add ControlsDemo usage (e.g. tooltips in Button demo and in the BreakdownChart control when implemented).
177177

178178
---
179179

180-
## Breakdown Control (Segmented Proportional Bar)
180+
## BreakdownChart Control (Segmented Proportional Bar)
181181

182182
Goal: show proportional parts of a whole (resource usage, KPI breakdown, category proportions), with interactivity.
183183

184184
### Public API
185185

186186
```csharp
187-
public sealed class Breakdown : Visual
187+
public sealed class BreakdownChart : Visual
188188
{
189189
[Bindable] public BindableList<BreakdownSegment> Segments { get; }
190190

@@ -218,10 +218,10 @@ In addition to the generator-provided fluent methods for bindable properties (e.
218218
the list replacement methods for `Segments(...)`, provide a convenience helper that appends a segment in one call:
219219

220220
```csharp
221-
public static partial class BreakdownExtensions
221+
public static partial class BreakdownChartExtensions
222222
{
223-
public static Breakdown Segment(
224-
this Breakdown breakdown,
223+
public static BreakdownChart Segment(
224+
this BreakdownChart breakdown,
225225
double value,
226226
Visual? label = null,
227227
Color? color = null,
@@ -246,6 +246,8 @@ Introduce `BreakdownStyle` (record):
246246

247247
- `Rune FillRune` (default `' '`)
248248
- `int SegmentGap` (default 0 or 1)
249+
- `BreakdownLegendLayout LegendLayout` (default: Compact)
250+
- `int LegendItemSpacing` (spacing between items in Compact mode)
249251
- `Style? BarStyle` (base style used for segments)
250252
- `Style? LegendStyle` / `Style? LegendMutedStyle`
251253
- `IReadOnlyList<Color?>? DefaultSegmentColors` (optional override; otherwise derived from scheme)

samples/ControlsDemo/Demos/BarChartDemo.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ public override Visual Build(DemoContext context)
3131
new BarChartItem("Delta", 1) { BarColor = Colors.IndianRed });
3232

3333
return new VStack(
34-
DemoUi.Hint("BarChart displays items as horizontal bars. Use Value/Percentage toggles to control the right column."),
34+
DemoUi.Hint("BarChart displays items as horizontal bars. Values are displayed near the end of each bar."),
3535
new HStack(
3636
new CheckBox().Text("Show values").IsChecked(showValues),
3737
new CheckBox().Text("Show %").IsChecked(showPercentages))

samples/ControlsDemo/Demos/BreakdownDemo.cs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace XenoAtom.Terminal.UI.ControlsDemo.Demos;
66

7-
[Demo("Breakdown", "Visualization", Description = "Segmented proportional bar with optional legend and tooltips.")]
7+
[Demo("BreakdownChart", "Visualization", Description = "Segmented proportional bar with optional legend and tooltips.")]
88
public sealed class BreakdownDemo : ControlsDemoBase
99
{
1010
public BreakdownDemo() : base(DemoSource.Get())
@@ -17,7 +17,7 @@ public override Visual Build(DemoContext context)
1717

1818
var clicked = new State<string>("(none)");
1919

20-
var breakdown = new Breakdown()
20+
var breakdown = new BreakdownChart()
2121
.Title("Disk usage")
2222
.ShowValues(true)
2323
.ShowPercentages(true)
@@ -29,11 +29,10 @@ public override Visual Build(DemoContext context)
2929
breakdown.SegmentClicked((_, e) => clicked.Value = $"Clicked segment {e.Index}: {breakdown.ToStringValue(e.Segment.Value)}");
3030

3131
return new VStack(
32-
DemoUi.Hint("Breakdown shows the proportional distribution of values as colored segments."),
32+
DemoUi.Hint("BreakdownChart shows the proportional distribution of values as colored segments."),
3333
breakdown.Style(new BreakdownStyle { SegmentGap = 1 }),
3434
new Rule(),
3535
new TextBlock(() => $"Last click: {clicked.Value}"))
3636
.Spacing(1);
3737
}
3838
}
39-

src/XenoAtom.Terminal.UI.Tests/BreakdownTests.cs

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using XenoAtom.Terminal.UI.Controls;
66
using XenoAtom.Terminal.UI.Hosting;
7+
using XenoAtom.Terminal.UI.Layout;
78
using XenoAtom.Terminal.UI.Styling;
89
using System.Text;
910

@@ -12,10 +13,30 @@ namespace XenoAtom.Terminal.UI.Tests;
1213
[TestClass]
1314
public sealed class BreakdownTests
1415
{
16+
[TestMethod]
17+
public void Breakdown_Legend_Reflow_DoesNot_Reparent_Segment_Label()
18+
{
19+
// This is a regression test for a crash where the legend was rebuilt by creating new visuals
20+
// while reusing the same segment label instances, causing a "visual already has a parent" exception.
21+
var breakdown = new BreakdownChart()
22+
.ShowValues(true)
23+
.ShowPercentages(true)
24+
.Style(new BreakdownStyle { LegendLayout = BreakdownLegendLayout.Compact })
25+
.Segment(42, "🗃️ Data")
26+
.Segment(18, "📦 Packages")
27+
.Segment(9, "🧹 Temp")
28+
.Segment(3, "🧯 Other");
29+
30+
// Measure once with an unbounded width (common when hosted inside a ScrollViewer)...
31+
breakdown.Measure(new LayoutConstraints(0, LayoutConstants.Infinite, 0, LayoutConstants.Infinite));
32+
// ...then with a finite width (during arrange/layout pass).
33+
breakdown.Measure(new LayoutConstraints(0, 40, 0, LayoutConstants.Infinite));
34+
}
35+
1536
[TestMethod]
1637
public void Breakdown_Distributes_Segment_Widths_LeftToRight()
1738
{
18-
var breakdown = new Breakdown()
39+
var breakdown = new BreakdownChart()
1940
.ShowValues(false)
2041
.ShowPercentages(false)
2142
.Style(new BreakdownStyle { FillRune = new Rune('#'), SegmentGap = 1 })
@@ -39,7 +60,7 @@ public void Breakdown_Raises_SegmentClicked()
3960
{
4061
var clickedIndex = -1;
4162

42-
var breakdown = new Breakdown()
63+
var breakdown = new BreakdownChart()
4364
.ShowValues(false)
4465
.ShowPercentages(false)
4566
.Style(new BreakdownStyle { FillRune = new Rune('#'), SegmentGap = 1 })

src/XenoAtom.Terminal.UI.Tests/MarkupTextParserTests.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ public void MarkupTextParser_Extracts_PlainText_And_Runs()
2727

2828
Assert.AreEqual(1, runs[1].Start);
2929
Assert.AreEqual(1, runs[1].Length);
30-
Assert.IsTrue(runs[1].Style.TryGetForeground(out var fgDefault));
31-
Assert.AreEqual(ColorKind.Default, fgDefault.Kind);
32-
Assert.IsTrue(runs[1].Style.TryGetBackground(out var bgDefault));
33-
Assert.AreEqual(ColorKind.Default, bgDefault.Kind);
30+
Assert.IsFalse(runs[1].Style.TryGetForeground(out _));
31+
Assert.IsFalse(runs[1].Style.TryGetBackground(out _));
3432
}
3533
}

src/XenoAtom.Terminal.UI.Tests/StyleDefaultColorTests.cs

Lines changed: 0 additions & 52 deletions
This file was deleted.

0 commit comments

Comments
 (0)