diff --git a/TUI.Test/StackPanelTests.cs b/TUI.Test/StackPanelTests.cs
index 9d8eea8..f8128ec 100644
--- a/TUI.Test/StackPanelTests.cs
+++ b/TUI.Test/StackPanelTests.cs
@@ -628,6 +628,43 @@ public void StackPanelVerticalDrawsEveryWrappedLineOfTextInsideABorder()
Assert.AreEqual(new Position(1, 2), Assert.ContainsSingle(provider.WritesOf("world foo")).Position);
}
+ ///
+ /// Tests that a horizontal panel does not ask for the height of a child it collapses for lack of
+ /// width, so a tall child that no longer fits leaves no blank rows and later siblings move up
+ /// (ktsu-dev/TUI#224).
+ ///
+ [TestMethod]
+ public void StackPanelHorizontalRequiredHeightIgnoresChildrenThatDoNotFit()
+ {
+ TextElement wide = new(new string('A', 20));
+ TextElement tall = new("l1\nl2\nl3\nl4\nl5");
+ StackPanel row = new(Orientation.Horizontal) { wide, tall };
+ TextElement footer = new("footer");
+ StackPanel root = [row, footer];
+ root.Dimensions = new Dimensions(20, 10);
+
+ root.ArrangeChildren();
+ row.ArrangeChildren();
+
+ Assert.AreEqual(new Dimensions(20, 1), row.CalculateRequiredDimensions(20));
+ Assert.AreEqual(Dimensions.Empty, tall.Dimensions);
+ Assert.AreEqual(new Dimensions(20, 1), row.Dimensions);
+ Assert.AreEqual(1, footer.Position.Y);
+ }
+
+ ///
+ /// Tests that a horizontal panel still measures every child when no width constrains it.
+ ///
+ [TestMethod]
+ public void StackPanelHorizontalUnconstrainedMeasureCountsEveryChild()
+ {
+ StackPanel row = new(Orientation.Horizontal) { Spacing = 1 };
+ row.Add(new TextElement(new string('A', 20)));
+ row.Add(new TextElement("l1\nl2\nl3\nl4\nl5"));
+
+ Assert.AreEqual(new Dimensions(23, 5), row.CalculateRequiredDimensions());
+ }
+
///
/// Tests that a horizontal panel measures word-wrapped text against the width it has, so it
/// reports the wrapped height to a parent rather than one row (ktsu-dev/TUI#161).
diff --git a/TUI/Elements/Layouts/StackPanel.cs b/TUI/Elements/Layouts/StackPanel.cs
index 4a4a666..b7eee60 100644
--- a/TUI/Elements/Layouts/StackPanel.cs
+++ b/TUI/Elements/Layouts/StackPanel.cs
@@ -165,11 +165,20 @@ private Dimensions CalculateRequiredDimensionsForChildren(int? availableWidth)
int maxHeight = 0;
IUIElement[] visibleChildren = [.. GetVisibleChildren()];
- int totalSpacing = Math.Max(0, (visibleChildren.Length - 1) * Spacing);
+ int measuredChildren = 0;
int consumedWidth = 0;
foreach (IUIElement? child in visibleChildren)
{
+ // A horizontal child that starts where the width has run out is collapsed when arranged,
+ // so it adds neither width nor height, matching the arrange pass's break (ktsu-dev/TUI#224).
+ if (Orientation == Orientation.Horizontal && availableWidth is int limit && consumedWidth >= limit)
+ {
+ break;
+ }
+
+ measuredChildren++;
+
// A horizontal child gets the width its earlier siblings and the spacing between them
// leave, as it does when arranged (ktsu-dev/TUI#161).
Dimensions childDimensions = availableWidth switch
@@ -192,6 +201,8 @@ private Dimensions CalculateRequiredDimensionsForChildren(int? availableWidth)
}
}
+ int totalSpacing = Math.Max(0, (measuredChildren - 1) * Spacing);
+
return Orientation == Orientation.Vertical
? new Dimensions(maxWidth, totalHeight + totalSpacing)
: new Dimensions(totalWidth + totalSpacing, maxHeight);