Add Layout widget (#1041)

* Add width to panels
* Add height to panels
* Replace RenderContext with RenderOptions
* Remove exclusivity from alternative buffer
* Add Layout widget
* Add Align widget
This commit is contained in:
Patrik Svensson
2022-11-15 10:12:17 +01:00
committed by GitHub
parent 9ce3b99cd6
commit c3ec6a7363
137 changed files with 2651 additions and 387 deletions

View File

@ -0,0 +1,106 @@
namespace Spectre.Console.Extensions;
/// <summary>
/// Contains extension methods for <see cref="Align"/>.
/// </summary>
public static class AlignExtensions
{
/// <summary>
/// Sets the width.
/// </summary>
/// <param name="align">The <see cref="Align"/> object.</param>
/// <param name="width">The width, or <c>null</c> for no explicit width.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Align Width(this Align align, int? width)
{
if (align is null)
{
throw new ArgumentNullException(nameof(align));
}
align.Width = width;
return align;
}
/// <summary>
/// Sets the height.
/// </summary>
/// <param name="align">The <see cref="Align"/> object.</param>
/// <param name="height">The height, or <c>null</c> for no explicit height.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Align Height(this Align align, int? height)
{
if (align is null)
{
throw new ArgumentNullException(nameof(align));
}
align.Height = height;
return align;
}
/// <summary>
/// Sets the vertical alignment.
/// </summary>
/// <param name="align">The <see cref="Align"/> object.</param>
/// <param name="vertical">The vertical alignment, or <c>null</c> for no vertical alignment.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Align VerticalAlignment(this Align align, VerticalAlignment? vertical)
{
if (align is null)
{
throw new ArgumentNullException(nameof(align));
}
align.Vertical = vertical;
return align;
}
/// <summary>
/// Sets the <see cref="Align"/> object to be top aligned.
/// </summary>
/// <param name="align">The <see cref="Align"/> object.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Align TopAligned(this Align align)
{
if (align is null)
{
throw new ArgumentNullException(nameof(align));
}
align.Vertical = Console.VerticalAlignment.Top;
return align;
}
/// <summary>
/// Sets the <see cref="Align"/> object to be middle aligned.
/// </summary>
/// <param name="align">The <see cref="Align"/> object.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Align MiddleAligned(this Align align)
{
if (align is null)
{
throw new ArgumentNullException(nameof(align));
}
align.Vertical = Console.VerticalAlignment.Middle;
return align;
}
/// <summary>
/// Sets the <see cref="Align"/> object to be bottom aligned.
/// </summary>
/// <param name="align">The <see cref="Align"/> object.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Align BottomAligned(this Align align)
{
if (align is null)
{
throw new ArgumentNullException(nameof(align));
}
align.Vertical = Console.VerticalAlignment.Bottom;
return align;
}
}

View File

@ -77,4 +77,4 @@ public static class AlignableExtensions
obj.Alignment = Justify.Right;
return obj;
}
}
}

View File

@ -27,19 +27,18 @@ public static partial class AnsiConsoleExtensions
throw new NotSupportedException("Alternate buffers are not supported by your terminal.");
}
console.ExclusivityMode.Run<object?>(() =>
{
// Switch to alternate screen
console.Write(new ControlCode("\u001b[?1049h\u001b[H"));
// Switch to alternate screen
console.Write(new ControlCode("\u001b[?1049h\u001b[H"));
try
{
// Execute custom action
action();
}
finally
{
// Switch back to primary screen
console.Write(new ControlCode("\u001b[?1049l"));
// Dummy result
return null;
});
}
}
}

View File

@ -0,0 +1,80 @@
namespace Spectre.Console;
/// <summary>
/// Contains extension methods for <see cref="IHasJustification"/>.
/// </summary>
public static class HasJustificationExtensions
{
/// <summary>
/// Sets the justification for an <see cref="IHasJustification"/> object.
/// </summary>
/// <typeparam name="T">The type that can be justified.</typeparam>
/// <param name="obj">The alignable object.</param>
/// <param name="alignment">The alignment.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T Justify<T>(this T obj, Justify? alignment)
where T : class, IHasJustification
{
if (obj is null)
{
throw new System.ArgumentNullException(nameof(obj));
}
obj.Justification = alignment;
return obj;
}
/// <summary>
/// Sets the <see cref="IHasJustification"/> object to be left justified.
/// </summary>
/// <typeparam name="T">The type that can be justified.</typeparam>
/// <param name="obj">The alignable object.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T LeftJustified<T>(this T obj)
where T : class, IHasJustification
{
if (obj is null)
{
throw new System.ArgumentNullException(nameof(obj));
}
obj.Justification = Console.Justify.Left;
return obj;
}
/// <summary>
/// Sets the <see cref="IHasJustification"/> object to be centered.
/// </summary>
/// <typeparam name="T">The type that can be justified.</typeparam>
/// <param name="obj">The alignable object.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T Centered<T>(this T obj)
where T : class, IHasJustification
{
if (obj is null)
{
throw new System.ArgumentNullException(nameof(obj));
}
obj.Justification = Console.Justify.Center;
return obj;
}
/// <summary>
/// Sets the <see cref="IHasJustification"/> object to be right justified.
/// </summary>
/// <typeparam name="T">The type that can be justified.</typeparam>
/// <param name="obj">The alignable object.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T RightJustified<T>(this T obj)
where T : class, IHasJustification
{
if (obj is null)
{
throw new System.ArgumentNullException(nameof(obj));
}
obj.Justification = Console.Justify.Right;
return obj;
}
}

View File

@ -0,0 +1,58 @@
namespace Spectre.Console;
/// <summary>
/// Contains extension methods for <see cref="Layout"/>.
/// </summary>
public static class LayoutExtensions
{
/// <summary>
/// Sets the ratio of the layout.
/// </summary>
/// <param name="layout">The layout.</param>
/// <param name="ratio">The ratio.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Layout Ratio(this Layout layout, int ratio)
{
if (layout is null)
{
throw new ArgumentNullException(nameof(layout));
}
layout.Ratio = ratio;
return layout;
}
/// <summary>
/// Sets the size of the layout.
/// </summary>
/// <param name="layout">The layout.</param>
/// <param name="size">The size.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Layout Size(this Layout layout, int size)
{
if (layout is null)
{
throw new ArgumentNullException(nameof(layout));
}
layout.Size = size;
return layout;
}
/// <summary>
/// Sets the minimum width of the layout.
/// </summary>
/// <param name="layout">The layout.</param>
/// <param name="size">The size.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static Layout MinimumSize(this Layout layout, int size)
{
if (layout is null)
{
throw new ArgumentNullException(nameof(layout));
}
layout.MinimumSize = size;
return layout;
}
}

View File

@ -24,7 +24,7 @@ public static class PanelExtensions
throw new ArgumentNullException(nameof(text));
}
alignment ??= panel.Header?.Alignment;
alignment ??= panel.Header?.Justification;
return Header(panel, new PanelHeader(text, alignment));
}
@ -44,7 +44,7 @@ public static class PanelExtensions
if (panel.Header != null)
{
// Update existing style
panel.Header.Alignment = alignment;
panel.Header.Justification = alignment;
}
else
{

View File

@ -0,0 +1,10 @@
namespace Spectre.Console;
internal static class RenderOptionsExtensions
{
public static BoxBorder GetSafeBorder<T>(this RenderOptions options, T border)
where T : IHasBoxBorder, IHasBorder
{
return BoxExtensions.GetSafeBorder(border.Border, !options.Unicode && border.UseSafeBorder);
}
}

View File

@ -23,13 +23,13 @@ public static class RenderableExtensions
throw new ArgumentNullException(nameof(renderable));
}
var context = new RenderContext(console.Profile.Capabilities);
var context = RenderOptions.Create(console, console.Profile.Capabilities);
var renderables = console.Pipeline.Process(context, new[] { renderable });
return GetSegments(console, context, renderables);
}
private static IEnumerable<Segment> GetSegments(IAnsiConsole console, RenderContext options, IEnumerable<IRenderable> renderables)
private static IEnumerable<Segment> GetSegments(IAnsiConsole console, RenderOptions options, IEnumerable<IRenderable> renderables)
{
var result = new List<Segment>();
foreach (var renderable in renderables)

View File

@ -0,0 +1,43 @@
namespace Spectre.Console;
/// <summary>
/// Contains extension methods for <see cref="IHasVisibility"/>.
/// </summary>
public static class VisibilityExtensions
{
/// <summary>
/// Marks the specified object as being invisible.
/// </summary>
/// <typeparam name="T">An object implementing <see cref="IHasVisibility"/>.</typeparam>
/// <param name="obj">The object to hide.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T Invisible<T>(this T obj)
where T : class, IHasVisibility
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.IsVisible = false;
return obj;
}
/// <summary>
/// Marks the specified object as being visible.
/// </summary>
/// <typeparam name="T">An object implementing <see cref="IHasVisibility"/>.</typeparam>
/// <param name="obj">The object to show.</param>
/// <returns>The same instance so that multiple calls can be chained.</returns>
public static T Visible<T>(this T obj)
where T : class, IHasVisibility
{
if (obj is null)
{
throw new ArgumentNullException(nameof(obj));
}
obj.IsVisible = true;
return obj;
}
}