namespace Spectre.Console;
///
/// Represents a renderable used to align content.
///
public sealed class Align : Renderable
{
private readonly IRenderable _renderable;
///
/// Gets or sets the horizontal alignment.
///
public HorizontalAlignment Horizontal { get; set; } = HorizontalAlignment.Left;
///
/// Gets or sets the vertical alignment.
///
public VerticalAlignment? Vertical { get; set; }
///
/// Gets or sets the width.
///
public int? Width { get; set; }
///
/// Gets or sets the height.
///
public int? Height { get; set; }
///
/// Initializes a new instance of the class.
///
/// The renderable to align.
/// The horizontal alignment.
/// The vertical alignment, or null if none.
public Align(IRenderable renderable, HorizontalAlignment horizontal, VerticalAlignment? vertical = null)
{
_renderable = renderable ?? throw new ArgumentNullException(nameof(renderable));
Horizontal = horizontal;
Vertical = vertical;
}
///
/// Initializes a new instance of the class that is left aligned.
///
/// The to align.
/// The vertical alignment, or null if none.
/// A new object.
public static Align Left(IRenderable renderable, VerticalAlignment? vertical = null)
{
return new Align(renderable, HorizontalAlignment.Left, vertical);
}
///
/// Initializes a new instance of the class that is center aligned.
///
/// The to align.
/// The vertical alignment, or null if none.
/// A new object.
public static Align Center(IRenderable renderable, VerticalAlignment? vertical = null)
{
return new Align(renderable, HorizontalAlignment.Center, vertical);
}
///
/// Initializes a new instance of the class that is right aligned.
///
/// The to align.
/// The vertical alignment, or null if none.
/// A new object.
public static Align Right(IRenderable renderable, VerticalAlignment? vertical = null)
{
return new Align(renderable, HorizontalAlignment.Right, vertical);
}
///
protected override IEnumerable Render(RenderOptions options, int maxWidth)
{
var rendered = _renderable.Render(options with { Height = null }, maxWidth);
var lines = Segment.SplitLines(rendered);
var width = Math.Min(Width ?? maxWidth, maxWidth);
var height = Height ?? options.Height;
var blank = new SegmentLine(new[] { new Segment(new string(' ', width)) });
// Align vertically
if (Vertical != null && height != null)
{
switch (Vertical)
{
case VerticalAlignment.Top:
{
var diff = Height - lines.Count;
for (var i = 0; i < diff; i++)
{
lines.Add(blank);
}
break;
}
case VerticalAlignment.Middle:
{
var top = (height - lines.Count) / 2;
var bottom = height - top - lines.Count;
for (var i = 0; i < top; i++)
{
lines.Insert(0, blank);
}
for (var i = 0; i < bottom; i++)
{
lines.Add(blank);
}
break;
}
case VerticalAlignment.Bottom:
{
var diff = Height - lines.Count;
for (var i = 0; i < diff; i++)
{
lines.Insert(0, blank);
}
break;
}
default:
throw new NotSupportedException("Unknown vertical alignment");
}
}
// Align horizontally
foreach (var line in lines)
{
Aligner.AlignHorizontally(line, Horizontal, width);
}
return new SegmentLineEnumerator(lines);
}
}