diff --git a/src/TUI/Engine/Helper.cs b/src/TUI/Engine/Helper.cs index ab871c0..2217059 100644 --- a/src/TUI/Engine/Helper.cs +++ b/src/TUI/Engine/Helper.cs @@ -8,7 +8,7 @@ public static class Helper { private static readonly Queue Colors = new(); - static Helper() + private static void Init() { Colors.Enqueue(ConsoleColor.DarkYellow); Colors.Enqueue(ConsoleColor.DarkMagenta); @@ -22,9 +22,14 @@ public static class Helper public static void ShowBackground(Position position, Size size) { - return; + // return; + if (!Colors.Any()) + { + Init(); + } + var color = Colors.Dequeue(); - + var top = position.Top; var height = 0; diff --git a/src/TUI/Engine/Nodes/Components/Sketch.cs b/src/TUI/Engine/Nodes/Components/Sketch.cs index aba0983..d2c02a5 100644 --- a/src/TUI/Engine/Nodes/Components/Sketch.cs +++ b/src/TUI/Engine/Nodes/Components/Sketch.cs @@ -10,8 +10,10 @@ public sealed class Sketch : IEnumerable public IEnumerator GetEnumerator() => ContentRows.GetEnumerator(); - public IEnumerable Crop(Size maxSize) => - ContentRows.Where(row => maxSize.Width >= row.GetWidth()).Take(maxSize.Height).ToArray(); + public IEnumerable Crop(Size maxSize) => ContentRows + .Select(row => maxSize.Width < row.GetWidth() ? row[..maxSize.Width] : row) + .Take(maxSize.Height) + .ToArray(); public Size GetSize() { diff --git a/src/TUI/Engine/Nodes/Containers/ContainerExtension.cs b/src/TUI/Engine/Nodes/Containers/ContainerExtension.cs index b1ebcb6..02e9b83 100644 --- a/src/TUI/Engine/Nodes/Containers/ContainerExtension.cs +++ b/src/TUI/Engine/Nodes/Containers/ContainerExtension.cs @@ -3,28 +3,84 @@ using TUI.Engine.Nodes.Attributes.Alignments; using TUI.Engine.Nodes.Attributes.Orientations; using TUI.Engine.Nodes.Attributes.Resizing; using TUI.Engine.Nodes.Components; +using TUI.Engine.Rendering; namespace TUI.Engine.Nodes.Containers; public static class ContainerExtension { - public static Size GetSize(this IContainer container, Size allowableSize) + public static Size GetSize(this INode node, IContainer parentContainer, int nodeNumber, Size allowableSize) { - var nodeCount = container.GetNodes().Count; - var width = container.ResizingHorizontal switch - { - Resizing.Adaptive => container.Orientation == Orientation.Horizontal - ? allowableSize.Width / nodeCount - : allowableSize.Width, - Resizing.Fixed => container.GetFixedSize().Width, - _ => throw new ArgumentOutOfRangeException() - }; - var height = container.Orientation == Orientation.Vertical - ? allowableSize.Height / nodeCount - : allowableSize.Height; - + var width = GetWidth(node, parentContainer, allowableSize.Width); + var height = GetHeight(node, parentContainer, allowableSize.Height, nodeNumber); return new Size(width, height); } + + private static IEnumerable GetFixedNodes(this IContainer container, int? takeNodeNumber = null) + { + if (takeNodeNumber is not null) + { + return container + .GetNodes() + .Take(takeNodeNumber.Value + 1) + .Where(n => n.ResizingVertical == Resizing.Fixed); + } + + return container + .GetNodes() + .Where(n => n.ResizingVertical == Resizing.Fixed); + } + + private static int GetHeight(IResizable node, IContainer container, int maxHeight, int nodeIndex) + { + if (node.ResizingVertical == Resizing.Fixed) + { + return node.GetFixedSize().Height; + } + + if (container.Orientation == Orientation.Horizontal) + { + return maxHeight; + } + + var fixedNodes = container.GetFixedNodes().ToArray(); + + var fixedHeight = fixedNodes.Sum(s => s.GetFixedSize().Height); + var allowableHeight = maxHeight - fixedHeight; + + var allowableCount = container.GetNodes().Count - fixedNodes.Length; + var nodeHeight = (allowableHeight / allowableCount).Min(1); + var nodeNumber = nodeIndex + 1 - container.GetFixedNodes(nodeIndex).Sum(c => c.GetFixedSize().Height); + + if (allowableHeight - nodeNumber * nodeHeight < nodeHeight) + { + return allowableHeight + nodeHeight - nodeNumber * nodeHeight; + } + + return nodeHeight; + } + + private static int GetWidth(IResizable node, IContainer container, int maxWidth) + { + if (node.ResizingHorizontal == Resizing.Fixed) + { + return node.GetFixedSize().Width; + } + + if (container.Orientation == Orientation.Vertical) + { + return maxWidth; + } + + var fixedNodes = container + .GetNodes() + .Where(n => n.ResizingHorizontal == Resizing.Fixed).ToArray(); + + var allowableWidth = maxWidth - fixedNodes.Sum(s => s.GetFixedSize().Width); + var allowableCount = container.GetNodes().Count - fixedNodes.Length; + + return allowableWidth / allowableCount; + } } public static class ComponentExtensions diff --git a/src/TUI/Engine/Rendering/ComponentCraftsman.cs b/src/TUI/Engine/Rendering/ComponentCraftsman.cs index 8182efe..27e293c 100644 --- a/src/TUI/Engine/Rendering/ComponentCraftsman.cs +++ b/src/TUI/Engine/Rendering/ComponentCraftsman.cs @@ -21,7 +21,7 @@ public sealed class ComponentCraftsman : CraftsmanBase, IDrawable var correctedPencil = component.CorrectPosition(pencil, maxSize, sketchSize); - Debug(correctedPencil, pencil, maxSize); + Debug(pencil, maxSize); foreach (var line in sketch.Crop(maxSize)) { diff --git a/src/TUI/Engine/Rendering/ContainerCraftsman.cs b/src/TUI/Engine/Rendering/ContainerCraftsman.cs index cad67f9..7999ad0 100644 --- a/src/TUI/Engine/Rendering/ContainerCraftsman.cs +++ b/src/TUI/Engine/Rendering/ContainerCraftsman.cs @@ -18,20 +18,22 @@ public sealed class ContainerCraftsman : CraftsmanBase, IDrawable public Size Draw(IContainer container, Position pencil, Size maxSize) { - var controlNumber = 0; + var nodeNumber = 0; var nextNodePosition = pencil; var nodes = container.GetNodes(); - var sketchSize = container.GetSize(maxSize); - Debug(nextNodePosition, nextNodePosition, maxSize); - while (controlNumber < nodes.Count) + Debug(nextNodePosition, maxSize); + + while (nodeNumber < nodes.Count) { - var node = nodes[controlNumber]; - nextNodePosition = DrawNode(node, container, nextNodePosition, sketchSize); - controlNumber++; + var node = nodes[nodeNumber]; + var nodeSize = node.GetSize(container, nodeNumber, maxSize); + + nextNodePosition = DrawNode(node, container, nextNodePosition, nodeSize); + nodeNumber++; } - return sketchSize; + return maxSize; } private Position DrawNode(INode node, IContainer container, Position nodePosition, Size maxSize) diff --git a/src/TUI/Engine/Rendering/CraftsmanBase.cs b/src/TUI/Engine/Rendering/CraftsmanBase.cs index 20d8ad6..6c50bf9 100644 --- a/src/TUI/Engine/Rendering/CraftsmanBase.cs +++ b/src/TUI/Engine/Rendering/CraftsmanBase.cs @@ -6,9 +6,9 @@ namespace TUI.Engine.Rendering; public abstract class CraftsmanBase { - protected void Debug(Position pencilPosition, Position sketchPosition, Size allowableSize) + protected void Debug(Position pencilPosition, Size allowableSize) { Debugger.Log(0, "Draw", $"{pencilPosition}{GetType().Name}.\n"); - Helper.ShowBackground(sketchPosition, allowableSize); + Helper.ShowBackground(pencilPosition, allowableSize); } } \ No newline at end of file diff --git a/src/TUI/Engine/Rendering/IntegerExtension.cs b/src/TUI/Engine/Rendering/IntegerExtension.cs index d31959d..b142d85 100644 --- a/src/TUI/Engine/Rendering/IntegerExtension.cs +++ b/src/TUI/Engine/Rendering/IntegerExtension.cs @@ -3,4 +3,5 @@ namespace TUI.Engine.Rendering; public static class IntegerExtension { public static int Max(this int value, int maxValue) => value <= maxValue ? value : maxValue; + public static int Min(this int value, int minValue) => value > minValue ? value : minValue; } \ No newline at end of file diff --git a/src/TUI/Pages/DependenciesPage.cs b/src/TUI/Pages/DependenciesPage.cs index 5456d84..03a9713 100644 --- a/src/TUI/Pages/DependenciesPage.cs +++ b/src/TUI/Pages/DependenciesPage.cs @@ -3,6 +3,7 @@ using TUI.Components.Controls; using TUI.Components.Layouts; using TUI.Engine.Nodes; using TUI.Engine.Nodes.Attributes.Alignments; +using TUI.Engine.Nodes.Attributes.Orientations; using TUI.Engine.Rendering; using TUI.Engine.Theme; @@ -21,6 +22,8 @@ public class DependenciesPage var nodeCraftsman = new NodeCraftsman(componentCraftsman, containerCraftsman); var header = new HeaderContainer(); + header.SetFixed(Orientation.Vertical, 6); + var copyright = new Copyright(); copyright.SetPaddingRight(Level.Normal); copyright.SetAlignment(Horizontal.Right); diff --git a/tests/WIdgets/TUI.Tests/ConsoleDrawNodeTests.cs b/tests/WIdgets/TUI.Tests/ConsoleDrawNodeTests.cs index e5a2bfc..b8c969b 100644 --- a/tests/WIdgets/TUI.Tests/ConsoleDrawNodeTests.cs +++ b/tests/WIdgets/TUI.Tests/ConsoleDrawNodeTests.cs @@ -112,8 +112,7 @@ public class NodeCraftsmanTests [Theory] [InlineData(Orientation.Horizontal, 9, 1)] - [InlineData(Orientation.Vertical, 5, 1)] - public void DrawWithOverload(Orientation orientation, int rootWidth, int rootHeight) + public void DrawWithOverloadHorizontal(Orientation orientation, int rootWidth, int rootHeight) { var canvas = Mock.Of(w => w.Width == rootWidth && w.Height == rootHeight); var nodes = new Nodes { _component, _component }; @@ -124,7 +123,30 @@ public class NodeCraftsmanTests containerCraftsman.Draw(root, Position.Default, canvas.GetSize()); Mock.Get(canvas).Verify(w => w.SetPencil(0, 0), Times.Once()); - Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Once()); + Mock.Get(canvas).Verify(w => w.SetPencil(4, 0), Times.Once()); + Mock.Get(canvas).Verify(w => w.Paint("Lore"), Times.Exactly(2)); + } + + + [Theory] + [InlineData(4, 4, new[] { 0, 1, 2, 3 })] + public void DrawWithOverloadVertical(int rootWidth, int rootHeight, int[] expectedTopPositions) + { + var canvas = Mock.Of(w => w.Width == rootWidth && w.Height == rootHeight); + _component.SetContent("Lorem\nLorem\nLorem"); + 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(componentCraftsman); + containerCraftsman.Draw(root, Position.Default, canvas.GetSize()); + + foreach (var expectedTopPosition in expectedTopPositions) + { + Mock.Get(canvas).Verify(w => w.SetPencil(0, expectedTopPosition), Times.Once()); + } + + Mock.Get(canvas).Verify(w => w.Paint("Lorem"), Times.Exactly(rootHeight)); } [Fact] @@ -196,7 +218,6 @@ public class NodeCraftsmanTests [Theory] [InlineData(Resizing.Fixed, 6)] - [InlineData(Resizing.Fixed, 3)] [InlineData(Resizing.Adaptive, 10)] public void DrawResizingContainer(Resizing resizing, int expectedCursorPosition) {