Skip to content

Commit 59585a6

Browse files
committed
chore: LineGraph rendering improvements, ControlDefaults updates, remove internal plan doc
1 parent 90289b5 commit 59585a6

4 files changed

Lines changed: 107 additions & 100 deletions

File tree

SharpConsoleUI/Configuration/ControlDefaults.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,21 @@ public static class ControlDefaults
495495
/// </summary>
496496
public static readonly Color LineGraphDefaultReferenceLineColor = Color.Grey50;
497497

498+
/// <summary>
499+
/// Character used for the legend color marker (horizontal line segment).
500+
/// </summary>
501+
public const char LineGraphLegendMarkerChar = '\u2501'; // ━
502+
503+
/// <summary>
504+
/// Width of the legend marker including trailing space (marker char + space).
505+
/// </summary>
506+
public const int LineGraphLegendMarkerWidth = 2;
507+
508+
/// <summary>
509+
/// Gap between legend entries in characters.
510+
/// </summary>
511+
public const int LineGraphLegendEntryGap = 2;
512+
498513
/// <summary>
499514
/// Padding between graph edge and marker arrow/label.
500515
/// </summary>

SharpConsoleUI/Controls/LineGraphControl/LineGraphControl.Rendering.cs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,18 @@ public override void PaintDOM(CharacterBuffer buffer, LayoutRect bounds, LayoutR
169169
}
170170
}
171171

172+
// Draw legend (right-aligned on title row)
173+
if (_showLegend && _series.Count > 0)
174+
{
175+
int legendY = _titlePosition == TitlePosition.Top
176+
? startY + borderSize
177+
: graphStartY + _graphHeight + baselineHeight;
178+
if (legendY >= clipRect.Y && legendY < clipRect.Bottom)
179+
{
180+
PaintLegend(buffer, legendY, startX + borderSize, contentWidth - (borderSize * 2), clipRect, fgColor, bgColor, preserveBg);
181+
}
182+
}
183+
172184
// Snapshot data under lock
173185
List<(LineGraphSeries series, List<double> data)> seriesSnapshots;
174186
lock (_dataLock)
@@ -338,6 +350,75 @@ private void ComputeGlobalMinMaxFromSnapshots(
338350
max = min + 1.0;
339351
}
340352

353+
private void PaintLegend(CharacterBuffer buffer, int y, int areaX, int areaWidth,
354+
LayoutRect clipRect, Color fgColor, Color bgColor, bool preserveBg)
355+
{
356+
// Build legend: "━ Series1 ━ Series2 ━ Series3"
357+
// Each entry: line char + space + name + 2 spaces gap
358+
List<LineGraphSeries> snapshot;
359+
lock (_dataLock) { snapshot = new List<LineGraphSeries>(_series); }
360+
361+
var entries = new List<(string text, Color color)>();
362+
int totalWidth = 0;
363+
foreach (var s in snapshot)
364+
{
365+
// "━ Name" = 2 + name length
366+
int entryWidth = ControlDefaults.LineGraphLegendMarkerWidth + UnicodeWidth.GetStringWidth(s.Name);
367+
if (entries.Count > 0)
368+
totalWidth += ControlDefaults.LineGraphLegendEntryGap;
369+
totalWidth += entryWidth;
370+
entries.Add((s.Name, s.LineColor));
371+
}
372+
373+
int legendX = areaX + areaWidth - totalWidth;
374+
int writeX = legendX;
375+
376+
for (int i = 0; i < entries.Count; i++)
377+
{
378+
var (text, color) = entries[i];
379+
if (i > 0)
380+
writeX += ControlDefaults.LineGraphLegendEntryGap;
381+
382+
// Draw line marker char
383+
if (writeX >= clipRect.X && writeX < clipRect.Right)
384+
{
385+
if (preserveBg)
386+
{
387+
var existingBg = buffer.GetCell(writeX, y).Background;
388+
buffer.SetNarrowCell(writeX, y, ControlDefaults.LineGraphLegendMarkerChar, color, existingBg);
389+
}
390+
else
391+
buffer.SetNarrowCell(writeX, y, ControlDefaults.LineGraphLegendMarkerChar, color, bgColor);
392+
}
393+
writeX++;
394+
395+
// Space after marker
396+
writeX++;
397+
398+
// Draw series name
399+
var nameCells = Parsing.MarkupParser.Parse(text, fgColor, bgColor);
400+
for (int c = 0; c < nameCells.Count && writeX < clipRect.Right; c++)
401+
{
402+
if (writeX >= clipRect.X)
403+
{
404+
if (preserveBg)
405+
{
406+
var existingBg = buffer.GetCell(writeX, y).Background;
407+
var cell = new Cell(nameCells[c].Character, fgColor, existingBg, nameCells[c].Decorations)
408+
{
409+
IsWideContinuation = nameCells[c].IsWideContinuation,
410+
Combiners = nameCells[c].Combiners
411+
};
412+
buffer.SetCell(writeX, y, cell);
413+
}
414+
else
415+
buffer.SetCell(writeX, y, nameCells[c]);
416+
}
417+
writeX++;
418+
}
419+
}
420+
}
421+
341422
#endregion
342423
}
343424
}

SharpConsoleUI/Controls/LineGraphControl/LineGraphControl.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ public partial class LineGraphControl : BaseControl
113113
private Color _baselineColor = Color.Grey50;
114114
private TitlePosition _baselinePosition = TitlePosition.Bottom;
115115
private bool _inlineTitleWithBaseline;
116+
private bool _showLegend;
116117

117118
private BorderStyle _borderStyle = BorderStyle.None;
118119
private Color? _borderColor;
@@ -284,6 +285,16 @@ public TitlePosition BaselinePosition
284285
set => SetProperty(ref _baselinePosition, value);
285286
}
286287

288+
/// <summary>
289+
/// Gets or sets whether to show a legend displaying series names and colors.
290+
/// The legend is rendered on the title row, right-aligned.
291+
/// </summary>
292+
public bool ShowLegend
293+
{
294+
get => _showLegend;
295+
set => SetProperty(ref _showLegend, value);
296+
}
297+
287298
/// <summary>
288299
/// Gets or sets whether the title is rendered inline with the baseline.
289300
/// Only applies when TitlePosition and BaselinePosition are the same.

docs/superpowers/plans/focus-centralization.md

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

0 commit comments

Comments
 (0)