diff --git a/TUI.Test/BorderElementRenderTests.cs b/TUI.Test/BorderElementRenderTests.cs index 3167c87..3f1f8cc 100644 --- a/TUI.Test/BorderElementRenderTests.cs +++ b/TUI.Test/BorderElementRenderTests.cs @@ -234,6 +234,32 @@ public void TitleWithALineBreakIsDrawnOnOneRow(string title) Assert.AreEqual(0, provider.WritesOf(" ab cd ").Single().Position.Y); } + /// + /// A border whose child is hidden measures as the bare border, so collapsing a section inside a + /// vertical StackPanel gives its rows back to the elements below (ktsu-dev/TUI#190). + /// + [TestMethod] + public void HiddenChildIsMeasuredAsAbsent() + { + // Arrange + BorderElement border = new() { Child = new TextElement("details line one\ndetails line two") { IsVisible = false } }; + StackPanel panel = new(Orientation.Vertical); + panel.AddChild(border); + panel.AddChild(new TextElement("below")); + panel.Position = Position.Origin; + panel.Dimensions = new Dimensions(40, 20); + RecordingConsoleProvider provider = new(); + + // Act + panel.ArrangeChildren(); + panel.Render(provider); + + // Assert + Assert.AreEqual(new Dimensions(2, 2), border.CalculateRequiredDimensions()); + Assert.AreEqual(new Dimensions(2, 2), border.CalculateRequiredDimensions(40)); + Assert.AreEqual(2, provider.WritesOf("below").Single().Position.Y); + } + /// /// A border sized by a layout is measured wide enough for its title, so a title longer than /// the content is drawn in full rather than cut short or dropped (ktsu-dev/TUI#157). diff --git a/TUI.Test/UIContainerBaseMeasureTests.cs b/TUI.Test/UIContainerBaseMeasureTests.cs index 70cb036..389d6ee 100644 --- a/TUI.Test/UIContainerBaseMeasureTests.cs +++ b/TUI.Test/UIContainerBaseMeasureTests.cs @@ -42,6 +42,20 @@ private static OriginContainer CreateContainer(Position position) return container; } + /// + /// A hidden child's extent is left out of the default measurement (ktsu-dev/TUI#190) + /// + [TestMethod] + public void DefaultMeasurementIgnoresHiddenChildren() + { + OriginContainer container = CreateContainer(Position.Origin); + container.AddChild(new TextElement { Text = "a much longer hidden line", IsVisible = false }); + container.ArrangeChildren(); + + Assert.AreEqual(new Dimensions(5, 3), container.CalculateRequiredDimensions()); + Assert.AreEqual(new Dimensions(5, 3), container.CalculateRequiredDimensions(80)); + } + /// /// The required size is the content plus one padding, not the screen position plus two paddings /// diff --git a/TUI/Elements/Primitives/BorderElement.cs b/TUI/Elements/Primitives/BorderElement.cs index ca8a575..d956d1f 100644 --- a/TUI/Elements/Primitives/BorderElement.cs +++ b/TUI/Elements/Primitives/BorderElement.cs @@ -217,15 +217,17 @@ protected override void OnArrangeChildren() // class adds to this result, so only the child's own size belongs here (ktsu-dev/TUI#132). // A title is drawn as " title " between the corners, so the content area must be at least // two wider than the title or a layout sizes the border too narrow to show it (ktsu-dev/TUI#157). + // A hidden child is measured as absent, so a collapsed section shrinks to the bare border + // (ktsu-dev/TUI#190). protected override Dimensions OnCalculateRequiredDimensionsForChildren() => - WithRoomForTitle(Children.Count == 0 ? Dimensions.Empty : Children.First().CalculateRequiredDimensions()); + WithRoomForTitle(GetVisibleChildren().FirstOrDefault()?.CalculateRequiredDimensions() ?? Dimensions.Empty); /// // The base class has already taken the border off availableWidth, so the child is measured // against the width it will be arranged into. Without this a word-wrapped child is measured // unwrapped and given a single row (ktsu-dev/TUI#161). protected override Dimensions OnCalculateRequiredDimensionsForChildren(int availableWidth) => - WithRoomForTitle(Children.Count == 0 ? Dimensions.Empty : Children.First().CalculateRequiredDimensions(availableWidth)); + WithRoomForTitle(GetVisibleChildren().FirstOrDefault()?.CalculateRequiredDimensions(availableWidth) ?? Dimensions.Empty); private Dimensions WithRoomForTitle(Dimensions childDimensions) { diff --git a/TUI/Elements/UIContainerBase.cs b/TUI/Elements/UIContainerBase.cs index 15e5ba7..796690e 100644 --- a/TUI/Elements/UIContainerBase.cs +++ b/TUI/Elements/UIContainerBase.cs @@ -176,7 +176,8 @@ protected virtual Dimensions OnCalculateRequiredDimensionsForChildren() int maxWidth = 0; int maxHeight = 0; - foreach (IUIElement child in _children) + // A hidden child is not drawn, so it takes no room either (ktsu-dev/TUI#190) + foreach (IUIElement child in GetVisibleChildren()) { Dimensions childDimensions = child.CalculateRequiredDimensions(); int childRight = Math.Max(0, child.Position.X - origin.X + childDimensions.Width);