diff --git a/src/TUI/Components/Controls/CellsComponentBase.cs b/src/TUI/Components/Controls/CellsComponentBase.cs index 5cdde2a..dff5c6b 100644 --- a/src/TUI/Components/Controls/CellsComponentBase.cs +++ b/src/TUI/Components/Controls/CellsComponentBase.cs @@ -18,7 +18,7 @@ public class CellsComponentBase : ComponentBase, IComponent _cells = cells; } - public void Render(Horizontal horizontal, Size size) + public void Render(AlignmentHorizontal alignmentHorizontal, Size size) { var content = new StringBuilder(); foreach (var cell in _cells) @@ -30,7 +30,7 @@ public class CellsComponentBase : ComponentBase, IComponent // base.Render(content, position, size); } - public override Content Render() + public override Sketch Draw() { throw new NotImplementedException(); } diff --git a/src/TUI/Components/Controls/Dashboard.cs b/src/TUI/Components/Controls/Dashboard.cs index ecdb115..55f10e4 100644 --- a/src/TUI/Components/Controls/Dashboard.cs +++ b/src/TUI/Components/Controls/Dashboard.cs @@ -16,7 +16,7 @@ public class Dashboard : ComponentBase, IComponent _title = title; } - public void Render(Horizontal horizontal, Size size) + public void Render(AlignmentHorizontal alignmentHorizontal, Size size) { var dashboardBuilder = new StringBuilder(); @@ -61,7 +61,7 @@ public class Dashboard : ComponentBase, IComponent dashboardBuilder.Append(Symbols.Angles.RightBottom); } - public override Content Render() + public override Sketch Draw() { throw new NotImplementedException(); } diff --git a/src/TUI/Components/Controls/HeaderContainer.cs b/src/TUI/Components/Controls/HeaderContainer.cs index abe8bef..471e044 100644 --- a/src/TUI/Components/Controls/HeaderContainer.cs +++ b/src/TUI/Components/Controls/HeaderContainer.cs @@ -1,37 +1,48 @@ using TUI.Components.Controls.Statics; using TUI.Components.Controls.Statics.Hints; using TUI.Engine.Nodes; +using TUI.Engine.Nodes.Attributes; using TUI.Engine.Nodes.Attributes.Alignments; +using TUI.Engine.Nodes.Attributes.Orientations; +using TUI.Engine.Nodes.Attributes.Resizings; using TUI.Engine.Nodes.Containers; using TUI.Engine.Theme; namespace TUI.Components.Controls; -public class HeaderContainer : IContainer +public abstract class ContainerBase : IContainer { public Orientation Orientation => Orientation.Horizontal; - public Nodes Nodes - { - get - { - var versionHints = new VersionHints() - .Set(Indentation.Default); + public Resizing ResizingHorizontal => Resizing.Adaptive; - var tagsHints = new TagHints() - .Set(Indentation.Default); + public Resizing ResizingVertical => Resizing.Hug; - var appTypeHints = new AppTypeHints() - .Set(Indentation.Default); + public Size Fixed { get; } - var hotkeysHints = new HotkeysHint() - .Set(Indentation.Default); - - var logo = new Logo() - .Set(Horizontal.Right) - .Set(left: Indentation.Default, bottom: Indentation.Default, right: Indentation.Default); - - return new Nodes { versionHints, tagsHints, appTypeHints, hotkeysHints, logo }; - } - } + public abstract Nodes GetNodes(); } + +public class HeaderContainer : ContainerBase, IContainer +{ + public override Nodes GetNodes() + { + var versionHints = new VersionHints() + .Set(Indentation.Default); + + var tagsHints = new TagHints() + .Set(Indentation.Default); + + var appTypeHints = new AppTypeHints() + .Set(Indentation.Default); + + var hotkeysHints = new HotkeysHint() + .Set(Indentation.Default); + + var logo = new Logo() + .Set(AlignmentHorizontal.Right) + .Set(left: Indentation.Default, bottom: Indentation.Default, right: Indentation.Default); + + return new Nodes { versionHints, tagsHints, appTypeHints, hotkeysHints, logo }; + } +} \ No newline at end of file diff --git a/src/TUI/Components/Controls/Tag.cs b/src/TUI/Components/Controls/Tag.cs index 6969993..ee15f32 100644 --- a/src/TUI/Components/Controls/Tag.cs +++ b/src/TUI/Components/Controls/Tag.cs @@ -14,7 +14,7 @@ public class Tag : ComponentBase private IEnumerable _tags; private string _gitType; - public Tag(IRenderingEngine renderingEngine) + public Tag(NodeCraftsman drawEngine) { } @@ -24,7 +24,7 @@ public class Tag : ComponentBase _gitType = gitType; } - public void Render(Horizontal horizontal, Size size) + public void Render(AlignmentHorizontal alignmentHorizontal, Size size) { var tagBuilder = new StringBuilder(); @@ -59,7 +59,7 @@ public class Tag : ComponentBase _ => Symbols.Git }; - public override Content Render() + public override Sketch Draw() { throw new NotImplementedException(); } diff --git a/src/TUI/Components/Layouts/DashboardLayout.cs b/src/TUI/Components/Layouts/DashboardLayout.cs index 95ab0a1..e8507e7 100644 --- a/src/TUI/Components/Layouts/DashboardLayout.cs +++ b/src/TUI/Components/Layouts/DashboardLayout.cs @@ -1,17 +1,20 @@ +using TUI.Components.Controls; using TUI.Engine.Nodes; +using TUI.Engine.Nodes.Attributes.Orientations; +using TUI.Engine.Nodes.Attributes.Resizings; using TUI.Engine.Nodes.Components; using TUI.Engine.Nodes.Containers; namespace TUI.Components.Layouts; -public class DashboardLayout : IContainer +public class DashboardLayout : ContainerBase, IContainer { - public Orientation Orientation { get; } = Orientation.Vertical; + public new Orientation Orientation => Orientation.Vertical; private INode _header; private INode _footer; - public Nodes Nodes => + public override Nodes GetNodes() => new() { _header, _footer @@ -33,4 +36,8 @@ public class DashboardLayout : IContainer { throw new NotImplementedException(); } + + public Resizing ResizingHorizontal => Resizing.Adaptive; + + public Resizing ResizingVertical => Resizing.Adaptive; } \ No newline at end of file diff --git a/src/TUI/Components/Views/DependenciesView.cs b/src/TUI/Components/Views/DependenciesView.cs index e74bd4e..a965eac 100644 --- a/src/TUI/Components/Views/DependenciesView.cs +++ b/src/TUI/Components/Views/DependenciesView.cs @@ -18,7 +18,7 @@ public class DependenciesView : ComponentBase, IComponent _developmentStack = developmentStack; } - public void Render(Horizontal horizontal, Size size) + public void Render(AlignmentHorizontal alignmentHorizontal, Size size) { var dashboardTitle = _developmentStack.Icon + Symbols.Space + ViewName; var dashboard = new Dashboard(dashboardTitle); @@ -153,7 +153,7 @@ public class DependenciesView : ComponentBase, IComponent // { // _table.Previous(); // } - public override Content Render() + public override Sketch Draw() { throw new NotImplementedException(); } diff --git a/src/TUI/Engine/Helper.cs b/src/TUI/Engine/Helper.cs index 8c7a1ad..2ea4241 100644 --- a/src/TUI/Engine/Helper.cs +++ b/src/TUI/Engine/Helper.cs @@ -20,10 +20,11 @@ public static class Helper Colors.Enqueue(ConsoleColor.Yellow); } - public static void ShowBackground(NodePosition position, Size size) + public static void ShowBackground(Position position, Size size) { return; var color = Colors.Dequeue(); + var top = position.Top; var height = 0; diff --git a/src/TUI/Engine/Nodes/Attributes/Alignments/Alignment.cs b/src/TUI/Engine/Nodes/Attributes/Alignments/Alignment.cs index 1ddb95c..f7e1b2d 100644 --- a/src/TUI/Engine/Nodes/Attributes/Alignments/Alignment.cs +++ b/src/TUI/Engine/Nodes/Attributes/Alignments/Alignment.cs @@ -1,3 +1,3 @@ namespace TUI.Engine.Nodes.Attributes.Alignments; -public record Alignment(Horizontal Horizontal, Vertical Vertical); \ No newline at end of file +public record Alignment(AlignmentHorizontal AlignmentHorizontal, Vertical Vertical); \ No newline at end of file diff --git a/src/TUI/Engine/Nodes/Attributes/Alignments/Horizontal.cs b/src/TUI/Engine/Nodes/Attributes/Alignments/AlignmentHorizontal.cs similarity index 74% rename from src/TUI/Engine/Nodes/Attributes/Alignments/Horizontal.cs rename to src/TUI/Engine/Nodes/Attributes/Alignments/AlignmentHorizontal.cs index cbc839a..b278263 100644 --- a/src/TUI/Engine/Nodes/Attributes/Alignments/Horizontal.cs +++ b/src/TUI/Engine/Nodes/Attributes/Alignments/AlignmentHorizontal.cs @@ -1,6 +1,6 @@ namespace TUI.Engine.Nodes.Attributes.Alignments; -public enum Horizontal +public enum AlignmentHorizontal { Left = 0, Center = 1, diff --git a/src/TUI/Engine/Nodes/Attributes/Alignments/IWithAlignment.cs b/src/TUI/Engine/Nodes/Attributes/Alignments/IWithAlignment.cs index 56067a2..d2fca44 100644 --- a/src/TUI/Engine/Nodes/Attributes/Alignments/IWithAlignment.cs +++ b/src/TUI/Engine/Nodes/Attributes/Alignments/IWithAlignment.cs @@ -4,5 +4,5 @@ public interface IWithAlignment where TNode : INode { public Alignment Alignment { get; } - public TNode Set(Horizontal horizontal = Horizontal.Left, Vertical vertical = Vertical.Top); + public TNode Set(AlignmentHorizontal alignmentHorizontal = AlignmentHorizontal.Left, Vertical vertical = Vertical.Top); } \ No newline at end of file diff --git a/src/TUI/Engine/Nodes/Attributes/Orientations/IWithOrientation.cs b/src/TUI/Engine/Nodes/Attributes/Orientations/IWithOrientation.cs new file mode 100644 index 0000000..2d5fda9 --- /dev/null +++ b/src/TUI/Engine/Nodes/Attributes/Orientations/IWithOrientation.cs @@ -0,0 +1,6 @@ +namespace TUI.Engine.Nodes.Attributes.Orientations; + +public interface IWithOrientation +{ + public Orientation Orientation { get; } +} \ No newline at end of file diff --git a/src/TUI/Engine/Nodes/Containers/Orientation.cs b/src/TUI/Engine/Nodes/Attributes/Orientations/Orientation.cs similarity index 51% rename from src/TUI/Engine/Nodes/Containers/Orientation.cs rename to src/TUI/Engine/Nodes/Attributes/Orientations/Orientation.cs index f53ba11..16b8b41 100644 --- a/src/TUI/Engine/Nodes/Containers/Orientation.cs +++ b/src/TUI/Engine/Nodes/Attributes/Orientations/Orientation.cs @@ -1,4 +1,4 @@ -namespace TUI.Engine.Nodes.Containers; +namespace TUI.Engine.Nodes.Attributes.Orientations; public enum Orientation { diff --git a/src/TUI/Engine/Nodes/Attributes/Resizings/IWithResizing.cs b/src/TUI/Engine/Nodes/Attributes/Resizings/IWithResizing.cs new file mode 100644 index 0000000..6b483b4 --- /dev/null +++ b/src/TUI/Engine/Nodes/Attributes/Resizings/IWithResizing.cs @@ -0,0 +1,10 @@ +namespace TUI.Engine.Nodes.Attributes.Resizings; + +public interface IWithResizing +{ + Resizing ResizingHorizontal { get; } + + Resizing ResizingVertical { get; } + + Size Fixed { get; } +} \ No newline at end of file diff --git a/src/TUI/Engine/Nodes/Attributes/Resizings/Resizing.cs b/src/TUI/Engine/Nodes/Attributes/Resizings/Resizing.cs new file mode 100644 index 0000000..a268d52 --- /dev/null +++ b/src/TUI/Engine/Nodes/Attributes/Resizings/Resizing.cs @@ -0,0 +1,8 @@ +namespace TUI.Engine.Nodes.Attributes.Resizings; + +public enum Resizing +{ + Adaptive, + Fixed, + Hug, +} \ No newline at end of file diff --git a/src/TUI/Engine/Nodes/Components/ComponentBase.cs b/src/TUI/Engine/Nodes/Components/ComponentBase.cs index 05caf8f..da36b46 100644 --- a/src/TUI/Engine/Nodes/Components/ComponentBase.cs +++ b/src/TUI/Engine/Nodes/Components/ComponentBase.cs @@ -6,15 +6,15 @@ namespace TUI.Engine.Nodes.Components; public abstract class ComponentBase : List, IComponent { - public abstract Content Render(); + public abstract Sketch Draw(); #region Alignments - public Alignment Alignment { get; private set; } = new(Horizontal.Center, Vertical.Top); + public Alignment Alignment { get; private set; } = new(AlignmentHorizontal.Center, Vertical.Top); - public IComponent Set(Horizontal horizontal = Horizontal.Left, Vertical vertical = Vertical.Top) + public IComponent Set(AlignmentHorizontal alignmentHorizontal = AlignmentHorizontal.Left, Vertical vertical = Vertical.Top) { - Alignment = new Alignment(horizontal, vertical); + Alignment = new Alignment(alignmentHorizontal, vertical); return this; } diff --git a/src/TUI/Engine/Nodes/Components/ComponentStaticBase.cs b/src/TUI/Engine/Nodes/Components/ComponentStaticBase.cs index 6e9a3ce..67c341f 100644 --- a/src/TUI/Engine/Nodes/Components/ComponentStaticBase.cs +++ b/src/TUI/Engine/Nodes/Components/ComponentStaticBase.cs @@ -4,11 +4,11 @@ namespace TUI.Engine.Nodes.Components; public abstract class ComponentStaticBase : ComponentBase { - private Content? _cache; + private Sketch? _cache; protected abstract void RenderWithCache(StringBuilder builder); - public override Content Render() + public override Sketch Draw() { if (_cache is not null) { @@ -17,7 +17,7 @@ public abstract class ComponentStaticBase : ComponentBase var builder = new StringBuilder(); RenderWithCache(builder); - _cache = new Content(builder.ToString()); + _cache = new Sketch(builder.ToString()); return _cache; } diff --git a/src/TUI/Engine/Nodes/Components/IComponent.cs b/src/TUI/Engine/Nodes/Components/IComponent.cs index 4a841cc..c44f65b 100644 --- a/src/TUI/Engine/Nodes/Components/IComponent.cs +++ b/src/TUI/Engine/Nodes/Components/IComponent.cs @@ -7,5 +7,5 @@ public interface IComponent : INode, IWithAlignment, IWithPadding { - Content Render(); + Sketch Draw(); } \ No newline at end of file diff --git a/src/TUI/Engine/Nodes/Components/Content.cs b/src/TUI/Engine/Nodes/Components/Sketch.cs similarity index 84% rename from src/TUI/Engine/Nodes/Components/Content.cs rename to src/TUI/Engine/Nodes/Components/Sketch.cs index d357bb1..583aea8 100644 --- a/src/TUI/Engine/Nodes/Components/Content.cs +++ b/src/TUI/Engine/Nodes/Components/Sketch.cs @@ -2,11 +2,11 @@ using TUI.Engine.Nodes.Attributes; namespace TUI.Engine.Nodes.Components; -public sealed class Content : IEnumerable +public sealed class Sketch : IEnumerable { private IEnumerable ContentRows { get; } - public Content(string content) => ContentRows = content.Split(Symbols.LineBreak); + public Sketch(string content) => ContentRows = content.Split(Symbols.LineBreak); public IEnumerator GetEnumerator() => ContentRows.GetEnumerator(); diff --git a/src/TUI/Engine/Nodes/Containers/ContainerExtension.cs b/src/TUI/Engine/Nodes/Containers/ContainerExtension.cs new file mode 100644 index 0000000..a938bdc --- /dev/null +++ b/src/TUI/Engine/Nodes/Containers/ContainerExtension.cs @@ -0,0 +1,55 @@ +using TUI.Engine.Nodes.Attributes; +using TUI.Engine.Nodes.Attributes.Alignments; +using TUI.Engine.Nodes.Attributes.Orientations; +using TUI.Engine.Nodes.Components; + +namespace TUI.Engine.Nodes.Containers; + +public static class ContainerExtension +{ + public static Size GetSize(this IContainer container, Size allowableSize) + { + var nodeCount = container.GetNodes().Count; + var width = container.Orientation == Orientation.Horizontal + ? allowableSize.Width / nodeCount + : allowableSize.Width; + var height = container.Orientation == Orientation.Vertical + ? allowableSize.Height / nodeCount + : allowableSize.Height; + + return new Size(width, height); + } +} + +public static class ComponentExtensions +{ + public static Position GetPosition(this IComponent component, Position sketchPosition, Size allowableSize, + Size actualSize) + { + var left = sketchPosition.Left + (int)(component.Padding?.Left ?? 0) + + CompensationLeft(component.Alignment.AlignmentHorizontal, allowableSize, actualSize); + var top = sketchPosition.Top + (int)(component.Padding?.Top ?? 0) + + CompensationTop(component.Alignment.Vertical, allowableSize, actualSize); + return new Position(left, top); + } + + private static int CompensationLeft(AlignmentHorizontal componentAlignmentHorizontal, Size defaultSize, + Size realSize) => + componentAlignmentHorizontal switch + { + AlignmentHorizontal.Left => 0, + AlignmentHorizontal.Center => (defaultSize.Width - realSize.Width) / 2, + AlignmentHorizontal.Right => defaultSize.Width - realSize.Width, + _ => 0 + }; + + private static int CompensationTop(Vertical componentVertical, Size defaultSize, Size realSize) + => + componentVertical switch + { + Vertical.Top => 0, + Vertical.Center => (defaultSize.Height - realSize.Height) / 2, + Vertical.Bottom => defaultSize.Height - realSize.Height, + _ => 0 + }; +} \ No newline at end of file diff --git a/src/TUI/Engine/Nodes/Containers/IContainer.cs b/src/TUI/Engine/Nodes/Containers/IContainer.cs index aac4955..1dfaa7e 100644 --- a/src/TUI/Engine/Nodes/Containers/IContainer.cs +++ b/src/TUI/Engine/Nodes/Containers/IContainer.cs @@ -1,8 +1,11 @@ +using TUI.Engine.Nodes.Attributes.Orientations; +using TUI.Engine.Nodes.Attributes.Resizings; + namespace TUI.Engine.Nodes.Containers; -public interface IContainer : INode +public interface IContainer : INode, + IWithOrientation, + IWithResizing { - public Orientation Orientation { get; } - - public Nodes Nodes { get; } + public Nodes GetNodes(); } \ No newline at end of file diff --git a/src/TUI/Engine/Nodes/NodePosition.cs b/src/TUI/Engine/Nodes/Position.cs similarity index 66% rename from src/TUI/Engine/Nodes/NodePosition.cs rename to src/TUI/Engine/Nodes/Position.cs index b0c9cf5..55600d3 100644 --- a/src/TUI/Engine/Nodes/NodePosition.cs +++ b/src/TUI/Engine/Nodes/Position.cs @@ -1,6 +1,6 @@ namespace TUI.Engine.Nodes; -public record NodePosition(int Left, int Top) +public record Position(int Left, int Top) { public override string ToString() => $"L[{Left}] T[{Top}]"; } \ No newline at end of file diff --git a/src/TUI/Engine/Rendering/ComponentCraftsman.cs b/src/TUI/Engine/Rendering/ComponentCraftsman.cs new file mode 100644 index 0000000..65f5547 --- /dev/null +++ b/src/TUI/Engine/Rendering/ComponentCraftsman.cs @@ -0,0 +1,41 @@ +using System.Diagnostics; +using TUI.Engine.Nodes; +using TUI.Engine.Nodes.Attributes; +using TUI.Engine.Nodes.Components; +using TUI.Engine.Nodes.Containers; + +namespace TUI.Engine.Rendering; + +public sealed class ComponentCraftsman : IDrawable +{ + private readonly ICanvas _canvas; + + public ComponentCraftsman(ICanvas canvas) + { + _canvas = canvas; + } + + public Size Draw(IComponent component, Position sketchPosition, Size allowableSize) + { + var sketch = component.Draw(); + var actualSize = sketch.GetSize(); + + var maxWidth = _canvas.Width - sketchPosition.Left; + var maxHeight = _canvas.Height - sketchPosition.Top; + + var pencilPosition = component.GetPosition(sketchPosition, allowableSize, actualSize); + + Debugger.Log(0, "Render", $"{pencilPosition}{component.GetType().Name}.\n"); + Helper.ShowBackground(sketchPosition, allowableSize); + + foreach (var row in sketch.Rows(maxWidth, maxHeight)) + { + _canvas.SetPencil(pencilPosition.Left, pencilPosition.Top); + _canvas.Paint(row); + + pencilPosition = pencilPosition with { Top = pencilPosition.Top + 1 }; + } + + return actualSize; + } +} \ No newline at end of file diff --git a/src/TUI/Engine/Rendering/ConsoleCanvas.cs b/src/TUI/Engine/Rendering/ConsoleCanvas.cs new file mode 100644 index 0000000..9636c3b --- /dev/null +++ b/src/TUI/Engine/Rendering/ConsoleCanvas.cs @@ -0,0 +1,9 @@ +namespace TUI.Engine.Rendering; + +public class ConsoleCanvas : ICanvas +{ + public int Width => Console.WindowWidth; + public int Height => Console.WindowHeight; + public void SetPencil(int left, int top) => Console.SetCursorPosition(left, top); + public void Paint(string value) => Console.Write(value); +} \ No newline at end of file diff --git a/src/TUI/Engine/Rendering/ConsoleDrawNode.cs b/src/TUI/Engine/Rendering/ConsoleDrawNode.cs new file mode 100644 index 0000000..dc01b01 --- /dev/null +++ b/src/TUI/Engine/Rendering/ConsoleDrawNode.cs @@ -0,0 +1,76 @@ +using System.Diagnostics; +using TUI.Engine.Nodes; +using TUI.Engine.Nodes.Attributes; +using TUI.Engine.Nodes.Attributes.Orientations; +using TUI.Engine.Nodes.Components; +using TUI.Engine.Nodes.Containers; + +namespace TUI.Engine.Rendering; + +public sealed class ContainerCraftsman : IDrawable +{ + private readonly ICanvas _canvas; + private readonly IDrawable _componentCraftsman; + + public ContainerCraftsman( + ICanvas canvas, + IDrawable componentCraftsman) + { + _canvas = canvas; + _componentCraftsman = componentCraftsman; + } + + + public Size Draw(IContainer container, Position sketchPosition, Size allowableSize) + { + var sketchSize = container.GetSize(allowableSize); + + Debugger.Log(0, "Render", $"{sketchPosition} {allowableSize} {container.GetType().Name}\n"); + Helper.ShowBackground(sketchPosition, allowableSize); + + var controlNumber = 0; + + while (controlNumber < container.GetNodes().Count) + { + var node = container.GetNodes()[controlNumber]; + + sketchPosition = RenderNode(node, container.Orientation, sketchSize, sketchPosition); + controlNumber++; + } + + return sketchSize; + } + + private Position RenderNode(INode node, Orientation orientation, Size defaultSize, Position position) + { + switch (node) + { + case IContainer container when orientation == Orientation.Horizontal: + Draw(container, position, defaultSize); + return position with + { + Left = position.Left + defaultSize.Width + }; + case IContainer container when orientation == Orientation.Vertical: + Draw(container, position, defaultSize); + return position with + { + Top = position.Top + defaultSize.Height + }; + case IComponent component when orientation == Orientation.Horizontal: + var componentWidth = _componentCraftsman.Draw(component, position, defaultSize).Width; + return position with + { + Left = position.Left + (defaultSize.Width <= componentWidth ? componentWidth : defaultSize.Width) + }; + case IComponent component when orientation == Orientation.Vertical: + var componentHeight = _componentCraftsman.Draw(component, position, defaultSize).Height; + return position with + { + Top = position.Top + (defaultSize.Height <= componentHeight ? componentHeight : defaultSize.Height) + }; + default: + throw new InvalidCastException(); + } + } +} \ No newline at end of file diff --git a/src/TUI/Engine/Rendering/ConsoleRenderingEngine.cs b/src/TUI/Engine/Rendering/ConsoleRenderingEngine.cs deleted file mode 100644 index 75235dc..0000000 --- a/src/TUI/Engine/Rendering/ConsoleRenderingEngine.cs +++ /dev/null @@ -1,129 +0,0 @@ -using System.Diagnostics; -using TUI.Engine.Nodes; -using TUI.Engine.Nodes.Attributes; -using TUI.Engine.Nodes.Attributes.Alignments; -using TUI.Engine.Nodes.Components; -using TUI.Engine.Nodes.Containers; - -namespace TUI.Engine.Rendering; - -public class ConsoleRenderingEngine : IRenderingEngine -{ - private readonly IWindow _window; - - public ConsoleRenderingEngine(IWindow window) - { - _window = window; - } - - public void Render(IContainer container, Size? defaultSize = null) - { - defaultSize ??= new Size(_window.Width, _window.Height); - - var defaultChildrenSize = new Size( - Width: container.Orientation == Orientation.Horizontal - ? defaultSize.Width / container.Nodes.Count - : defaultSize.Width, - Height: container.Orientation == Orientation.Vertical - ? defaultSize.Height / container.Nodes.Count - : defaultSize.Height - ); - - var controlNumber = 0; - var nodePosition = new NodePosition(Left: 0, Top: 0); - - Debugger.Log(0, "Render", $"{nodePosition} {defaultSize} {container.GetType().Name}\n"); - Helper.ShowBackground(nodePosition, defaultSize); - - while (controlNumber < container.Nodes.Count) - { - var node = container.Nodes[controlNumber]; - - nodePosition = RenderNode(node, container.Orientation, defaultChildrenSize, nodePosition); - controlNumber++; - } - } - - - private NodePosition RenderNode(INode node, Orientation orientation, Size defaultSize, NodePosition position) - { - switch (node) - { - case IContainer container when orientation == Orientation.Horizontal: - Render(container, defaultSize); - return position with - { - Left = position.Left + defaultSize.Width - }; - case IContainer container when orientation == Orientation.Vertical: - Render(container, defaultSize); - return position with - { - Top = position.Top + defaultSize.Height - }; - case IComponent component when orientation == Orientation.Horizontal: - var componentWidth = RenderComponent(component, position, defaultSize).Width; - return position with - { - Left = position.Left + (defaultSize.Width <= componentWidth ? componentWidth : defaultSize.Width) - }; - case IComponent component when orientation == Orientation.Vertical: - var componentHeight = RenderComponent(component, position, defaultSize).Height; - return position with - { - Top = position.Top + (defaultSize.Height <= componentHeight ? componentHeight : defaultSize.Height) - }; - default: - throw new InvalidCastException(); - } - } - - private Size RenderComponent(IComponent component, NodePosition defaultPosition, Size defaultSize) - { - var content = component.Render(); - - var maxWidth = _window.Width - defaultPosition.Left; - var maxHeight = _window.Height - defaultPosition.Top; - - var leftPosition = defaultPosition.Left + (int)(component.Padding?.Left ?? 0) + - CompensationLeft(component.Alignment.Horizontal, defaultSize, content.GetSize()); - var topPosition = defaultPosition.Top + (int)(component.Padding?.Top ?? 0) + - CompensationTop(component.Alignment.Vertical, defaultSize, content.GetSize()); - - - Debugger.Log(0, "Render", $"{component.GetType().Name} with position [{leftPosition},{topPosition}]\n"); - - var rows = content.Rows(maxWidth, maxHeight); - - Helper.ShowBackground(defaultPosition, defaultSize); - - foreach (var row in rows) - { - _window.SetCursorPosition(leftPosition, topPosition); - _window.Write(row); - - topPosition++; - } - - return content.GetSize(); - } - - private static int CompensationLeft(Horizontal componentHorizontal, Size defaultSize, Size realSize) => - componentHorizontal switch - { - Horizontal.Left => 0, - Horizontal.Center => (defaultSize.Width - realSize.Width) / 2, - Horizontal.Right => defaultSize.Width - realSize.Width, - _ => 0 - }; - - private static int CompensationTop(Vertical componentVertical, Size defaultSize, Size realSize) - => - componentVertical switch - { - Vertical.Top => 0, - Vertical.Center => (defaultSize.Height - realSize.Height) / 2, - Vertical.Bottom => defaultSize.Height - realSize.Height, - _ => 0 - }; -} \ No newline at end of file diff --git a/src/TUI/Engine/Rendering/ConsoleWindow.cs b/src/TUI/Engine/Rendering/ConsoleWindow.cs deleted file mode 100644 index 20908ae..0000000 --- a/src/TUI/Engine/Rendering/ConsoleWindow.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace TUI.Engine.Rendering; - -public class ConsoleWindow : IWindow -{ - public int Width => Console.WindowWidth; - public int Height => Console.WindowHeight; - public void SetCursorPosition(int left, int top) => Console.SetCursorPosition(left, top); - public void Write(string value) => Console.Write(value); -} \ No newline at end of file diff --git a/src/TUI/Engine/Rendering/ICanvas.cs b/src/TUI/Engine/Rendering/ICanvas.cs new file mode 100644 index 0000000..88781c1 --- /dev/null +++ b/src/TUI/Engine/Rendering/ICanvas.cs @@ -0,0 +1,9 @@ +namespace TUI.Engine.Rendering; + +public interface ICanvas +{ + int Width { get; } + int Height { get; } + void SetPencil(int left, int top); + void Paint(string value); +} \ No newline at end of file diff --git a/src/TUI/Engine/Rendering/IDrawable.cs b/src/TUI/Engine/Rendering/IDrawable.cs new file mode 100644 index 0000000..a196386 --- /dev/null +++ b/src/TUI/Engine/Rendering/IDrawable.cs @@ -0,0 +1,9 @@ +using TUI.Engine.Nodes; +using TUI.Engine.Nodes.Attributes; + +namespace TUI.Engine.Rendering; + +public interface IDrawable where TItem : INode +{ + Size Draw(TItem item, Position sketchPosition, Size allowableSize); +} \ No newline at end of file diff --git a/src/TUI/Engine/Rendering/IRenderingEngine.cs b/src/TUI/Engine/Rendering/IRenderingEngine.cs deleted file mode 100644 index 9cc91bd..0000000 --- a/src/TUI/Engine/Rendering/IRenderingEngine.cs +++ /dev/null @@ -1,9 +0,0 @@ -using TUI.Engine.Nodes.Attributes; -using TUI.Engine.Nodes.Containers; - -namespace TUI.Engine.Rendering; - -public interface IRenderingEngine -{ - void Render(IContainer container, Size? defaultSize); -} \ No newline at end of file diff --git a/src/TUI/Engine/Rendering/IWindow.cs b/src/TUI/Engine/Rendering/IWindow.cs deleted file mode 100644 index 5fd7dc6..0000000 --- a/src/TUI/Engine/Rendering/IWindow.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace TUI.Engine.Rendering; - -public interface IWindow -{ - int Width { get; } - int Height { get; } - void SetCursorPosition(int left, int top); - void Write(string value); -} \ No newline at end of file diff --git a/src/TUI/Engine/Rendering/NodeCraftsman.cs b/src/TUI/Engine/Rendering/NodeCraftsman.cs new file mode 100644 index 0000000..a124c27 --- /dev/null +++ b/src/TUI/Engine/Rendering/NodeCraftsman.cs @@ -0,0 +1,39 @@ +using TUI.Engine.Nodes; +using TUI.Engine.Nodes.Attributes; +using TUI.Engine.Nodes.Components; +using TUI.Engine.Nodes.Containers; + +namespace TUI.Engine.Rendering; + +public sealed class NodeCraftsman +{ + private readonly ICanvas _canvas; + private readonly IDrawable _componentCraftsman; + private readonly IDrawable _containerCraftsman; + + public NodeCraftsman( + ICanvas canvas, + IDrawable componentCraftsman, + IDrawable containerCraftsman) + { + _canvas = canvas; + _componentCraftsman = componentCraftsman; + _containerCraftsman = containerCraftsman; + } + + public void Draw(INode node) + { + var windowSize = new Size(_canvas.Width, _canvas.Height); + var defaultPosition = new Position(0, 0); + + switch (node) + { + case IContainer container: + _containerCraftsman.Draw(container, defaultPosition, windowSize); + break; + case IComponent component: + _componentCraftsman.Draw(component, defaultPosition, windowSize); + break; + } + } +} \ No newline at end of file diff --git a/src/TUI/Pages/DependenciesPage.cs b/src/TUI/Pages/DependenciesPage.cs index b535af6..e465e23 100644 --- a/src/TUI/Pages/DependenciesPage.cs +++ b/src/TUI/Pages/DependenciesPage.cs @@ -13,17 +13,21 @@ public class DependenciesPage { Debugger.Log(0, "Event", "Open page dependencies\n"); - var window = new ConsoleWindow(); - var renderEngine = new ConsoleRenderingEngine(window); + var canvas = new ConsoleCanvas(); + + var componentCraftsman = new ComponentCraftsman(canvas); + var containerCraftsman = new ContainerCraftsman(canvas, componentCraftsman); + var nodeCraftsman = new NodeCraftsman(canvas, componentCraftsman, containerCraftsman); + var header = new HeaderContainer(); var copyright = new Copyright() .Set(right: Level.Normal) - .Set(Horizontal.Right, Vertical.Bottom); + .Set(AlignmentHorizontal.Right, Vertical.Bottom); var layout = new DashboardLayout().AddHeader(header).AddFooter(copyright); // CommandLine = new CommandLine(); // DependenciesView = new DependenciesView(); - renderEngine.Render(layout); + nodeCraftsman.Draw(layout); } // private bool _commandLineInDisplay; diff --git a/tests/WIdgets/TUI.Tests/ComponentBaseTests.cs b/tests/WIdgets/TUI.Tests/ComponentBaseTests.cs index 77bb47a..9c67ad1 100644 --- a/tests/WIdgets/TUI.Tests/ComponentBaseTests.cs +++ b/tests/WIdgets/TUI.Tests/ComponentBaseTests.cs @@ -12,13 +12,13 @@ public class ComponentBaseTests { var component = new Logo() .Set(Level.Normal) - .Set(vertical: Vertical.Center, horizontal: Horizontal.Center); + .Set(vertical: Vertical.Center, alignmentHorizontal: AlignmentHorizontal.Center); component.Padding.Top.Should().Be(Level.Normal); component.Padding.Left.Should().Be(Level.Normal); component.Padding.Bottom.Should().Be(Level.Normal); component.Padding.Right.Should().Be(Level.Normal); - component.Alignment.Horizontal.Should().Be(Horizontal.Center); + component.Alignment.AlignmentHorizontal.Should().Be(AlignmentHorizontal.Center); component.Alignment.Vertical.Should().Be(Vertical.Center); } @@ -49,15 +49,16 @@ public class ComponentBaseTests } [Theory] - [InlineData(Horizontal.Left)] - [InlineData(Horizontal.Center)] - [InlineData(Horizontal.Right)] - public void WhenSetHorizontalAlignSaveAllChange(Horizontal alignment) + [InlineData(AlignmentHorizontal.Left)] + [InlineData(AlignmentHorizontal.Center)] + [InlineData(AlignmentHorizontal.Right)] + public void WhenSetHorizontalAlignSaveAllChange(AlignmentHorizontal alignment) { var component = new Logo(); - component.Set(horizontal: alignment); + component.Set(alignmentHorizontal: alignment); - component.Alignment.Horizontal.Should().Be(alignment); + component.Alignment.AlignmentHorizontal.Should().Be(alignment); } + } \ No newline at end of file diff --git a/tests/WIdgets/TUI.Tests/ConsoleDrawNodeTests.cs b/tests/WIdgets/TUI.Tests/ConsoleDrawNodeTests.cs new file mode 100644 index 0000000..b90e7e4 --- /dev/null +++ b/tests/WIdgets/TUI.Tests/ConsoleDrawNodeTests.cs @@ -0,0 +1,226 @@ +using Moq; +using TUI.Engine.Nodes; +using TUI.Engine.Nodes.Attributes.Alignments; +using TUI.Engine.Nodes.Attributes.Orientations; +using TUI.Engine.Nodes.Attributes.Resizings; +using TUI.Engine.Nodes.Components; +using TUI.Engine.Nodes.Containers; +using TUI.Engine.Rendering; + +namespace Widgets.Tests; + +public class NodeCraftsmanTests +{ + private readonly IComponent _component; + + public NodeCraftsmanTests() + { + _component = Mock.Of(c => + c.Draw() == new Sketch("Lorem") && + c.Alignment == new Alignment(AlignmentHorizontal.Left, Vertical.Top)); + } + + [Fact] + public void DrawSimple() + { + var canvas = Mock.Of(w => w.Width == 9 && w.Height == 1); + var nodes = new Nodes { _component }; + var root = Mock.Of(r => r.GetNodes() == nodes); + + var componentCraftsman = new ComponentCraftsman(canvas); + var containerCraftsman = new ContainerCraftsman(canvas, componentCraftsman); + new NodeCraftsman(canvas, componentCraftsman, containerCraftsman).Draw(root); + + Mock.Get(canvas).Verify(w => w.SetPencil(0, 0), Times.Once()); + Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Once()); + } + + [Theory] + [InlineData(AlignmentHorizontal.Left, "Lorem", 10, 0)] + [InlineData(AlignmentHorizontal.Center, "Lorem", 10, 2)] + [InlineData(AlignmentHorizontal.Center, "Lo", 10, 4)] + [InlineData(AlignmentHorizontal.Center, "Lorem", 9, 2)] + [InlineData(AlignmentHorizontal.Center, "Lorem", 11, 3)] + [InlineData(AlignmentHorizontal.Right, "Lorem", 10, 5)] + [InlineData(AlignmentHorizontal.Right, "Lo", 10, 8)] + public void DrawWithHorizontalAlignment(AlignmentHorizontal alignment, string content, int canvasSize, + int expectedPosition) + { + var canvas = Mock.Of(w => w.Width == canvasSize && w.Height == canvasSize); + var component = Mock.Of(c => c.Draw() == new Sketch(content) && + c.Alignment == new Alignment(alignment, Vertical.Top)); + var nodes = new Nodes { component }; + var root = Mock.Of(r => r.GetNodes() == nodes); + + var componentCraftsman = new ComponentCraftsman(canvas); + var containerCraftsman = new ContainerCraftsman(canvas, componentCraftsman); + new NodeCraftsman(canvas, componentCraftsman, containerCraftsman).Draw(root); + + Mock.Get(canvas).Verify(w => w.Paint(content), Times.Once()); + Mock.Get(canvas).Verify(w => w.SetPencil(expectedPosition, 0), Times.Once()); + } + + [Theory] + [InlineData(Vertical.Top, "v", 5, new[] { 0 })] + [InlineData(Vertical.Top, "v\nv", 5, new[] { 0, 1 })] + [InlineData(Vertical.Top, "v\nv\nv", 5, new[] { 0, 1, 2 })] + [InlineData(Vertical.Center, "v", 1, new[] { 0 })] + [InlineData(Vertical.Center, "v", 4, new[] { 1 })] + [InlineData(Vertical.Center, "v", 5, new[] { 2 })] + [InlineData(Vertical.Center, "v", 6, new[] { 2 })] + [InlineData(Vertical.Center, "v\nv", 4, new[] { 1, 2 })] + [InlineData(Vertical.Center, "v\nv", 5, new[] { 1, 2 })] + [InlineData(Vertical.Center, "v\nv", 6, new[] { 2, 3 })] + [InlineData(Vertical.Bottom, "v", 5, new[] { 4 })] + [InlineData(Vertical.Bottom, "v\nv", 2, new[] { 0, 1 })] + [InlineData(Vertical.Bottom, "v\nv", 3, new[] { 1, 2 })] + [InlineData(Vertical.Bottom, "v\nv\nv\nv", 5, new[] { 1, 2, 3, 4 })] + public void DrawWithVerticalAlignment(Vertical alignment, string content, int canvasSize, int[] expectedPositions) + { + var canvas = Mock.Of(w => w.Width == canvasSize && w.Height == canvasSize); + var component = Mock.Of(c => c.Draw() == new Sketch(content) && + c.Alignment == new Alignment(AlignmentHorizontal.Left, alignment)); + var nodes = new Nodes { component }; + var root = Mock.Of(r => r.GetNodes() == nodes); + + var componentCraftsman = new ComponentCraftsman(canvas); + var containerCraftsman = new ContainerCraftsman(canvas, componentCraftsman); + new NodeCraftsman(canvas, componentCraftsman, containerCraftsman).Draw(root); + + foreach (var expectedCursorPosition in expectedPositions) + { + Mock.Get(canvas).Verify(w => w.SetPencil(0, expectedCursorPosition), Times.Once()); + } + } + + [Theory] + [InlineData(AlignmentHorizontal.Left, Vertical.Top, 0, 0)] + [InlineData(AlignmentHorizontal.Left, Vertical.Center, 0, 2)] + [InlineData(AlignmentHorizontal.Left, Vertical.Bottom, 0, 4)] + [InlineData(AlignmentHorizontal.Center, Vertical.Top, 2, 0)] + [InlineData(AlignmentHorizontal.Center, Vertical.Center, 2, 2)] + [InlineData(AlignmentHorizontal.Center, Vertical.Bottom, 2, 4)] + [InlineData(AlignmentHorizontal.Right, Vertical.Top, 4, 0)] + [InlineData(AlignmentHorizontal.Right, Vertical.Center, 4, 2)] + [InlineData(AlignmentHorizontal.Right, Vertical.Bottom, 4, 4)] + public void DrawWithAlignment(AlignmentHorizontal alignmentHorizontal, Vertical vertical, int expectedLeft, + int expectedTop) + { + var canvas = Mock.Of(w => w.Width == 6 && w.Height == 5); + var component = Mock.Of(c => c.Draw() == new Sketch("VV") && + c.Alignment == new Alignment(alignmentHorizontal, vertical)); + var nodes = new Nodes { component }; + var root = Mock.Of(r => r.GetNodes() == nodes); + + var componentCraftsman = new ComponentCraftsman(canvas); + var containerCraftsman = new ContainerCraftsman(canvas, componentCraftsman); + new NodeCraftsman(canvas, componentCraftsman, containerCraftsman).Draw(root); + + Mock.Get(canvas).Verify(w => w.SetPencil(expectedLeft, expectedTop), Times.Once()); + } + + [Theory] + [InlineData(Orientation.Horizontal, 9, 1)] + [InlineData(Orientation.Vertical, 5, 1)] + public void DrawWithOverload(Orientation orientation, int rootWidth, int rootHeight) + { + var canvas = Mock.Of(w => w.Width == rootWidth && w.Height == rootHeight); + var nodes = new Nodes { _component, _component }; + var root = Mock.Of(r => r.GetNodes() == nodes && r.Orientation == orientation); + + var componentCraftsman = new ComponentCraftsman(canvas); + var containerCraftsman = new ContainerCraftsman(canvas, componentCraftsman); + new NodeCraftsman(canvas, componentCraftsman, containerCraftsman).Draw(root); + + Mock.Get(canvas).Verify(w => w.SetPencil(0, 0), Times.Once()); + Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Once()); + } + + [Fact] + public void DrawVerticalWithDoubleComponent() + { + var canvas = Mock.Of(w => w.Height == 2 && w.Width == 10); + var nodes = new Nodes { _component, _component }; + var root = Mock.Of(r => r.GetNodes() == nodes && r.Orientation == Orientation.Vertical); + + var componentCraftsman = new ComponentCraftsman(canvas); + var containerCraftsman = new ContainerCraftsman(canvas, componentCraftsman); + new NodeCraftsman(canvas, componentCraftsman, containerCraftsman).Draw(root); + + Mock.Get(canvas).Verify(w => w.SetPencil(0, 0), Times.Once()); + Mock.Get(canvas).Verify(w => w.SetPencil(0, 1), Times.Once()); + Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(2)); + } + + [Fact] + public void DrawHorizontalWithDoubleComponent() + { + var canvas = Mock.Of(w => w.Width == 10 && w.Height == 1); + var nodes = new Nodes { _component, _component }; + var container = Mock.Of(g => g.GetNodes() == nodes); + + var componentCraftsman = new ComponentCraftsman(canvas); + var containerCraftsman = new ContainerCraftsman(canvas, componentCraftsman); + new NodeCraftsman(canvas, componentCraftsman, containerCraftsman).Draw(container); + + Mock.Get(canvas).Verify(w => w.SetPencil(0, 0), Times.Exactly(1)); + Mock.Get(canvas).Verify(w => w.SetPencil(5, 0), Times.Exactly(1)); + Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(2)); + } + + [Fact] + public void DrawWithMultipleComponent() + { + var canvas = Mock.Of(w => w.Width == 24 && w.Height == 1); + var nodes = new Nodes { _component, _component, _component, _component }; + var root = Mock.Of(r => r.GetNodes() == nodes); + + var componentCraftsman = new ComponentCraftsman(canvas); + var containerCraftsman = new ContainerCraftsman(canvas, componentCraftsman); + new NodeCraftsman(canvas, componentCraftsman, containerCraftsman).Draw(root); + + Mock.Get(canvas).Verify(w => w.SetPencil(0, 0), Times.Exactly(1)); + Mock.Get(canvas).Verify(w => w.SetPencil(6, 0), Times.Exactly(1)); + Mock.Get(canvas).Verify(w => w.SetPencil(12, 0), Times.Exactly(1)); + Mock.Get(canvas).Verify(w => w.SetPencil(18, 0), Times.Exactly(1)); + Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(4)); + } + + [Fact] + public void DrawWithContainerAndComponent() + { + var canvas = Mock.Of(w => w.Width == 10 && w.Height == 2); + var container = Mock.Of(c => c.GetNodes() == new Nodes { _component }); + var nodes = new Nodes { container, _component }; + var root = Mock.Of(r => r.GetNodes() == nodes && r.Orientation == Orientation.Vertical); + + var componentCraftsman = new ComponentCraftsman(canvas); + var containerCraftsman = new ContainerCraftsman(canvas, componentCraftsman); + new NodeCraftsman(canvas, componentCraftsman, containerCraftsman).Draw(root); + + Mock.Get(canvas).Verify(w => w.SetPencil(0, 0), Times.Exactly(1)); + Mock.Get(canvas).Verify(w => w.SetPencil(0, 1), Times.Exactly(1)); + Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(2)); + } + + [Theory] + [InlineData(Resizing.Hug, 5)] + [InlineData(Resizing.Fixed, 3)] + [InlineData(Resizing.Adaptive, 10)] + public void DrawWithResizeContainer(Resizing resizing, int expectedCursorPosition) + { + var canvas = Mock.Of(w => w.Width == 20 && w.Height == 2); + var container = + Mock.Of(c => c.GetNodes() == new Nodes { _component } && c.ResizingHorizontal == resizing); + var nodes = new Nodes { container, _component }; + var root = Mock.Of(r => r.GetNodes() == nodes && r.Orientation == Orientation.Horizontal); + + var componentCraftsman = new ComponentCraftsman(canvas); + var containerCraftsman = new ContainerCraftsman(canvas, componentCraftsman); + new NodeCraftsman(canvas, componentCraftsman, containerCraftsman).Draw(root); + + Mock.Get(canvas).Verify(w => w.SetPencil(0, 0), Times.Exactly(1)); + Mock.Get(canvas).Verify(w => w.SetPencil(expectedCursorPosition, 0), Times.Exactly(1)); + Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(2)); + } +} \ No newline at end of file diff --git a/tests/WIdgets/TUI.Tests/ConsoleRenderingEngineTests.cs b/tests/WIdgets/TUI.Tests/ConsoleRenderingEngineTests.cs deleted file mode 100644 index 08ad62a..0000000 --- a/tests/WIdgets/TUI.Tests/ConsoleRenderingEngineTests.cs +++ /dev/null @@ -1,184 +0,0 @@ -using Moq; -using TUI.Engine.Nodes; -using TUI.Engine.Nodes.Attributes.Alignments; -using TUI.Engine.Nodes.Components; -using TUI.Engine.Nodes.Containers; -using TUI.Engine.Rendering; - -namespace Widgets.Tests; - -public class ConsoleRenderingEngineTests -{ - private readonly IComponent _component; - - public ConsoleRenderingEngineTests() - { - _component = Mock.Of(c => - c.Render() == new Content("Lorem") && - c.Alignment == new Alignment(Horizontal.Left, Vertical.Top)); - } - - [Fact] - public void RenderSimple() - { - var window = Mock.Of(w => w.Width == 9 && w.Height == 1); - var nodes = new Nodes { _component }; - var root = Mock.Of(r => r.Nodes == nodes); - - new ConsoleRenderingEngine(window).Render(root); - - Mock.Get(window).Verify(w => w.SetCursorPosition(0, 0), Times.Once()); - Mock.Get(window).Verify(w => w.Write("Lorem"), Times.Once()); - } - - [Theory] - [InlineData(Horizontal.Left, "Lorem", 10, 0)] - [InlineData(Horizontal.Center, "Lorem", 10, 2)] - [InlineData(Horizontal.Center, "Lo", 10, 4)] - [InlineData(Horizontal.Center, "Lorem", 9, 2)] - [InlineData(Horizontal.Center, "Lorem", 11, 3)] - [InlineData(Horizontal.Right, "Lorem", 10, 5)] - [InlineData(Horizontal.Right, "Lo", 10, 8)] - public void RenderWithHorizontalAlignment(Horizontal alignment, string content, int windowSize, - int expectedPosition) - { - var window = Mock.Of(w => w.Width == windowSize && w.Height == windowSize); - var component = Mock.Of(c => c.Render() == new Content(content) && - c.Alignment == new Alignment(alignment, Vertical.Top)); - var nodes = new Nodes { component }; - var root = Mock.Of(r => r.Nodes == nodes); - - new ConsoleRenderingEngine(window).Render(root); - - Mock.Get(window).Verify(w => w.Write(content), Times.Once()); - Mock.Get(window).Verify(w => w.SetCursorPosition(expectedPosition, 0), Times.Once()); - } - - [Theory] - [InlineData(Vertical.Top, "v", 5, new[] { 0 })] - [InlineData(Vertical.Top, "v\nv", 5, new[] { 0, 1 })] - [InlineData(Vertical.Top, "v\nv\nv", 5, new[] { 0, 1, 2 })] - [InlineData(Vertical.Center, "v", 1, new[] { 0 })] - [InlineData(Vertical.Center, "v", 4, new[] { 1 })] - [InlineData(Vertical.Center, "v", 5, new[] { 2 })] - [InlineData(Vertical.Center, "v", 6, new[] { 2 })] - [InlineData(Vertical.Center, "v\nv", 4, new[] { 1, 2 })] - [InlineData(Vertical.Center, "v\nv", 5, new[] { 1, 2 })] - [InlineData(Vertical.Center, "v\nv", 6, new[] { 2, 3 })] - [InlineData(Vertical.Bottom, "v", 5, new[] { 4 })] - [InlineData(Vertical.Bottom, "v\nv", 2, new[] { 0, 1 })] - [InlineData(Vertical.Bottom, "v\nv", 3, new[] { 1, 2 })] - [InlineData(Vertical.Bottom, "v\nv\nv\nv", 5, new[] { 1, 2, 3, 4 })] - public void RenderWithVerticalAlignment(Vertical alignment, string content, int windowSize, int[] expectedPositions) - { - var window = Mock.Of(w => w.Width == windowSize && w.Height == windowSize); - var component = Mock.Of(c => c.Render() == new Content(content) && - c.Alignment == new Alignment(Horizontal.Left, alignment)); - var nodes = new Nodes { component }; - var root = Mock.Of(r => r.Nodes == nodes); - - new ConsoleRenderingEngine(window).Render(root); - - foreach (var expectedCursorPosition in expectedPositions) - { - Mock.Get(window).Verify(w => w.SetCursorPosition(0, expectedCursorPosition), Times.Once()); - } - } - - [Theory] - [InlineData(Horizontal.Left, Vertical.Top, 0, 0)] - [InlineData(Horizontal.Left, Vertical.Center, 0, 2)] - [InlineData(Horizontal.Left, Vertical.Bottom, 0, 4)] - [InlineData(Horizontal.Center, Vertical.Top, 2, 0)] - [InlineData(Horizontal.Center, Vertical.Center, 2, 2)] - [InlineData(Horizontal.Center, Vertical.Bottom, 2, 4)] - [InlineData(Horizontal.Right, Vertical.Top, 4, 0)] - [InlineData(Horizontal.Right, Vertical.Center, 4, 2)] - [InlineData(Horizontal.Right, Vertical.Bottom, 4, 4)] - public void RenderWithAlignment(Horizontal horizontal, Vertical vertical, int expectedLeft, int expectedTop) - { - var window = Mock.Of(w => w.Width == 6 && w.Height == 5); - var component = Mock.Of(c => c.Render() == new Content("VV") && - c.Alignment == new Alignment(horizontal, vertical)); - var nodes = new Nodes { component }; - var root = Mock.Of(r => r.Nodes == nodes); - - new ConsoleRenderingEngine(window).Render(root); - - Mock.Get(window).Verify(w => w.SetCursorPosition(expectedLeft, expectedTop), Times.Once()); - } - - [Theory] - [InlineData(Orientation.Horizontal, 9, 1)] - [InlineData(Orientation.Vertical, 5, 1)] - public void RenderWithOverload(Orientation orientation, int rootWidth, int rootHeight) - { - var window = Mock.Of(w => w.Width == rootWidth && w.Height == rootHeight); - var nodes = new Nodes { _component, _component }; - var root = Mock.Of(r => r.Nodes == nodes && r.Orientation == orientation); - - new ConsoleRenderingEngine(window).Render(root); - - Mock.Get(window).Verify(w => w.SetCursorPosition(0, 0), Times.Once()); - Mock.Get(window).Verify(w => w.Write("Lorem"), Times.Once()); - } - - [Fact] - public void RenderVerticalWithDoubleComponent() - { - var window = Mock.Of(w => w.Height == 2 && w.Width == 10); - var nodes = new Nodes { _component, _component }; - var root = Mock.Of(r => r.Nodes == nodes && r.Orientation == Orientation.Vertical); - - new ConsoleRenderingEngine(window).Render(root); - - Mock.Get(window).Verify(w => w.SetCursorPosition(0, 0), Times.Once()); - Mock.Get(window).Verify(w => w.SetCursorPosition(0, 1), Times.Once()); - Mock.Get(window).Verify(w => w.Write("Lorem"), Times.Exactly(2)); - } - - [Fact] - public void RenderHorizontalWithDoubleComponent() - { - var window = Mock.Of(w => w.Width == 10 && w.Height == 1); - var nodes = new Nodes { _component, _component }; - var container = Mock.Of(g => g.Nodes == nodes); - - new ConsoleRenderingEngine(window).Render(container); - - Mock.Get(window).Verify(w => w.SetCursorPosition(0, 0), Times.Exactly(1)); - Mock.Get(window).Verify(w => w.SetCursorPosition(5, 0), Times.Exactly(1)); - Mock.Get(window).Verify(w => w.Write("Lorem"), Times.Exactly(2)); - } - - [Fact] - public void RenderWithMultipleComponent() - { - var window = Mock.Of(w => w.Width == 24 && w.Height == 1); - var nodes = new Nodes { _component, _component, _component, _component }; - var root = Mock.Of(r => r.Nodes == nodes); - - new ConsoleRenderingEngine(window).Render(root); - - Mock.Get(window).Verify(w => w.SetCursorPosition(0, 0), Times.Exactly(1)); - Mock.Get(window).Verify(w => w.SetCursorPosition(6, 0), Times.Exactly(1)); - Mock.Get(window).Verify(w => w.SetCursorPosition(12, 0), Times.Exactly(1)); - Mock.Get(window).Verify(w => w.SetCursorPosition(18, 0), Times.Exactly(1)); - Mock.Get(window).Verify(w => w.Write("Lorem"), Times.Exactly(4)); - } - - [Fact] - public void RenderWithContainerAndComponent() - { - var window = Mock.Of(w => w.Width == 10 && w.Height == 2); - var container = Mock.Of(c => c.Nodes == new Nodes { _component }); - var nodes = new Nodes { container, _component }; - var root = Mock.Of(r => r.Nodes == nodes && r.Orientation == Orientation.Vertical); - - new ConsoleRenderingEngine(window).Render(root); - - Mock.Get(window).Verify(w => w.SetCursorPosition(0, 0), Times.Exactly(1)); - Mock.Get(window).Verify(w => w.SetCursorPosition(0, 1), Times.Exactly(1)); - Mock.Get(window).Verify(w => w.Write("Lorem"), Times.Exactly(2)); - } -} \ No newline at end of file diff --git a/tests/WIdgets/TUI.Tests/Controls/LogoTests.cs b/tests/WIdgets/TUI.Tests/Controls/LogoTests.cs index cc3273c..306b9ac 100644 --- a/tests/WIdgets/TUI.Tests/Controls/LogoTests.cs +++ b/tests/WIdgets/TUI.Tests/Controls/LogoTests.cs @@ -9,7 +9,7 @@ public class LogoTests { var logo = new Logo(); - var render = logo.Render().ToString(); + var render = logo.Draw().ToString(); Assert.Equal( " \u001b[38;2;132;186;100m\u256d\u2501\u2501\u2501\u2501\u2533\u256e\u001b[0m\u001b[38;2;113;121;126m\u2571\u2571\u001b[0m\u001b[38;2;132;186;100m\u256d\u2501\u2501\u2501\u256e\u001b[0m\n \u001b[38;2;132;186;100m\u2503\u256d\u256e\u256d\u256e\u2503\u2503\u001b[0m\u001b[38;2;113;121;126m\u2571\u2571\u001b[0m\u001b[38;2;132;186;100m\u2570\u256e\u256d\u256e\u2503\u001b[0m\n \u001b[38;2;132;186;100m\u2570\u256f\u2503\u2503\u2570\u252b\u2503\u001b[0m\u001b[38;2;113;121;126m\u2571\u2571\u2571\u001b[0m\u001b[38;2;132;186;100m\u2503\u2503\u2503\u2503\u001b[0m\n \u001b[38;2;113;121;126m\u2571\u2571\u001b[0m\u001b[38;2;132;186;100m\u2503\u2503\u001b[0m\u001b[38;2;113;121;126m\u2571\u001b[0m\u001b[38;2;132;186;100m\u2503\u2503\u001b[0m\u001b[38;2;113;121;126m\u2571\u001b[0m\u001b[38;2;132;186;100m\u256d\u256e\u2503\u2503\u2503\u2503\u001b[0m\n \u001b[38;2;113;121;126m\u2571\u2571\u2571\u001b[0m\u001b[38;2;132;186;100m\u2503\u2503\u001b[0m\u001b[38;2;113;121;126m\u2571\u001b[0m\u001b[38;2;132;186;100m\u2503\u2570\u2501\u256f\u2523\u256f\u2570\u256f\u2503\u001b[0m\n\u001b[38;2;113;121;126m\u2571\u2571\u2571\u2571\u001b[0m\u001b[38;2;132;186;100m\u2570\u256f\u001b[0m\u001b[38;2;113;121;126m\u2571\u001b[0m\u001b[38;2;132;186;100m\u2570\u2501\u2501\u2501\u253b\u2501\u2501\u2501\u256f\u001b[0m\n",