When you need a widget that doesn't exist, implement IRenderable.
Implement IRenderable
To create a custom renderable, implement Measure() and Render().
private readonly string _text;
private readonly Style _style;
public Label(string text)
: this(text, Color.Black, Color.Grey)
{
}
public Label(string text, Color foreground, Color background)
{
_text = text;
_style = new Style(foreground, background);
}
public Measurement Measure(RenderOptions options, int maxWidth)
{
// Add 2 for padding (space on each side)
var width = _text.Length + 2;
return new Measurement(width, width);
}
public IEnumerable<Segment> Render(RenderOptions options, int maxWidth)
{
// Render padded text with our style
yield return new Segment($" {_text} ", _style);
}
Calculate Size Constraints
To report your widget's size requirements, return a Measurement from Measure(). The measurement specifies minimum and maximum width in console cells.
// Add 2 for padding (space on each side)
var width = _text.Length + 2;
return new Measurement(width, width);
Generate Segments
To produce output, yield Segment objects from Render(). Each segment contains text and an optional style.
// Render padded text with our style
yield return new Segment($" {_text} ", _style);
Apply Styles
To add colors and formatting, pass a Style when creating segments.
var success = new Label("OK", Color.White, Color.Green);
var warning = new Label("WARN", Color.Black, Color.Yellow);
var error = new Label("ERROR", Color.White, Color.Red);
AnsiConsole.Write(success);
AnsiConsole.Write(" ");
AnsiConsole.Write(warning);
AnsiConsole.Write(" ");
AnsiConsole.Write(error);
AnsiConsole.WriteLine();
Wrap Other Renderables
To create container widgets, accept IRenderable and delegate rendering.
private readonly IRenderable _label;
private readonly IRenderable _value;
public LabeledValue(IRenderable label, IRenderable value)
{
_label = label;
_value = value;
}
public Measurement Measure(RenderOptions options, int maxWidth)
{
var labelMeasure = _label.Measure(options, maxWidth);
var remaining = maxWidth - labelMeasure.Max - 1; // -1 for space
var valueMeasure = _value.Measure(options, Math.Max(0, remaining));
var min = labelMeasure.Min + 1 + valueMeasure.Min;
var max = labelMeasure.Max + 1 + valueMeasure.Max;
return new Measurement(min, max);
}
public IEnumerable<Segment> Render(RenderOptions options, int maxWidth)
{
// Render label
var labelMeasure = _label.Measure(options, maxWidth);
foreach (var segment in _label.Render(options, labelMeasure.Max))
{
yield return segment;
}
// Space between label and value
yield return new Segment(" ");
// Render value with remaining width
var remaining = maxWidth - labelMeasure.Max - 1;
foreach (var segment in _value.Render(options, Math.Max(0, remaining)))
{
yield return segment;
}
}
See Also
- Understanding Spectre.Console's Rendering Model - How rendering works
- Canvas Widget - Pixel-level rendering example
- Panel Widget - Container widget example