using System; using System.Collections.Generic; using System.Linq; using Spectre.Console.Internal; using Spectre.Console.Rendering; namespace Spectre.Console { /// /// A renderable horizontal rule. /// public sealed class Rule : Renderable, IAlignable { /// /// Gets or sets the rule title markup text. /// public string? Title { get; set; } /// /// Gets or sets the rule style. /// public Style? Style { get; set; } /// /// Gets or sets the rule's title alignment. /// public Justify? Alignment { get; set; } /// /// Initializes a new instance of the class. /// public Rule() { } /// /// Initializes a new instance of the class. /// /// The rule title markup text. public Rule(string title) { Title = title ?? throw new ArgumentNullException(nameof(title)); } /// protected override IEnumerable Render(RenderContext context, int maxWidth) { if (Title == null || maxWidth <= 6) { return GetLineWithoutTitle(maxWidth); } // Get the title and make sure it fits. var title = GetTitleSegments(context, Title, maxWidth - 6); if (Segment.CellLength(context, title) > maxWidth - 6) { // Truncate the title title = Segment.TruncateWithEllipsis(title, context, maxWidth - 6); if (!title.Any()) { // We couldn't fit the title at all. return GetLineWithoutTitle(maxWidth); } } var (left, right) = GetLineSegments(context, maxWidth, title); var segments = new List(); segments.Add(left); segments.AddRange(title); segments.Add(right); segments.Add(Segment.LineBreak); return segments; } private IEnumerable GetLineWithoutTitle(int maxWidth) { var text = new string('─', maxWidth); return new[] { new Segment(text, Style ?? Style.Plain), Segment.LineBreak, }; } private (Segment Left, Segment Right) GetLineSegments(RenderContext context, int maxWidth, IEnumerable title) { var alignment = Alignment ?? Justify.Center; var titleLength = Segment.CellLength(context, title); if (alignment == Justify.Left) { var left = new Segment(new string('─', 2) + " ", Style ?? Style.Plain); var rightLength = maxWidth - titleLength - left.CellLength(context) - 1; var right = new Segment(" " + new string('─', rightLength), Style ?? Style.Plain); return (left, right); } else if (alignment == Justify.Center) { var leftLength = ((maxWidth - titleLength) / 2) - 1; var left = new Segment(new string('─', leftLength) + " ", Style ?? Style.Plain); var rightLength = maxWidth - titleLength - left.CellLength(context) - 1; var right = new Segment(" " + new string('─', rightLength), Style ?? Style.Plain); return (left, right); } else if (alignment == Justify.Right) { var right = new Segment(" " + new string('─', 2), Style ?? Style.Plain); var leftLength = maxWidth - titleLength - right.CellLength(context) - 1; var left = new Segment(new string('─', leftLength) + " ", Style ?? Style.Plain); return (left, right); } throw new NotSupportedException("Unsupported alignment."); } private IEnumerable GetTitleSegments(RenderContext context, string title, int width) { title = title.NormalizeLineEndings().Replace("\n", " ").Trim(); var markup = new Markup(title, Style); return ((IRenderable)markup).Render(context.WithSingleLine(), width - 6); } } }