Skip to content

Commit e8102bf

Browse files
committed
fix: column-wrap flex container collapses to zero height without explicit height
A flex container with direction: column + wrap: wrap and no explicit height collapsed to ~0 height, clipping all content. The wrapped strategy only set the cross-axis auto dimension (width for column-wrap), never the main-axis (height), and the engine skipped fallback height calculation for all wrap containers. - WrappedFlexLayoutStrategy now records resolved trailing margins (MarginRight/MarginBottom) on wrap children so CalculateTotalHeight/ Width account for them, matching the non-wrap strategies. - LayoutEngine computes auto height whenever the strategy left node.Height unset (node.Height == 0f), covering no-wrap and column-wrap; row-wrap already sets height and is skipped.
1 parent f3b46e3 commit e8102bf

3 files changed

Lines changed: 46 additions & 2 deletions

File tree

src/FlexRender.Core/Layout/LayoutEngine.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -401,8 +401,10 @@ private LayoutNode LayoutFlexElement(FlexElement flex, LayoutContext context)
401401
MirrorRowXPositions(node, effectivePadding);
402402
}
403403

404-
// Calculate height if not specified (skip for wrapped containers — they set height in LayoutWrappedFlex)
405-
if (height == 0f && node.Children.Count > 0 && flex.Wrap.Value == FlexWrap.NoWrap)
404+
// Compute auto height when the strategy did not set it — covers no-wrap and
405+
// column-wrap. Row-wrap already sets height (its cross axis), so node.Height is
406+
// non-zero there and this is skipped.
407+
if (height == 0f && node.Children.Count > 0 && node.Height == 0f)
406408
{
407409
node.Height = LayoutHelpers.CalculateTotalHeight(node) + effectivePadding.Bottom;
408410
}

src/FlexRender.Core/Layout/WrappedFlexLayoutStrategy.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,11 @@ private static void ResolveFlexForLine(LayoutNode node, FlexElement flex, Layout
511511
var child = lineChildren[i];
512512
var childMargin = PaddingParser.Parse(child.Element.Margin.Value, context.ContainerWidth, context.FontSize).ClampNegatives();
513513

514+
// Record resolved trailing margins so CalculateTotalHeight/Width (used for
515+
// auto-sizing the container) accounts for them, matching the non-wrap strategies.
516+
child.MarginRight = childMargin.Right;
517+
child.MarginBottom = childMargin.Bottom;
518+
514519
if (isColumn)
515520
{
516521
child.Y = pos + childMargin.Top;

tests/FlexRender.Tests/Layout/LayoutEngineWrapTests.cs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,4 +418,41 @@ public void ComputeLayout_RowWrap_EmptyChildren_NoWrap()
418418
Assert.Equal(100f, flexNode.Height, 0.1f);
419419
Assert.Empty(flexNode.Children);
420420
}
421+
422+
[Fact]
423+
public void ComputeLayout_ColumnWrap_NoExplicitHeight_SizesToContentIncludingBottomMargin()
424+
{
425+
// Arrange: Column wrap, no explicit height, two children W=120 H=40 margin=20.
426+
// Single column (no main-axis constraint): both stack on the main axis (Y).
427+
// Item A: Y=20 (top margin), H=40 -> bottom edge 60, +bottom margin 20 -> 80.
428+
// Item B: Y=100 (60 + bottom margin 20 + top margin 20), H=40 -> bottom edge 140, +bottom margin 20 -> 160.
429+
// Container auto-height must size to content = 160 (previously collapsed to ~0).
430+
var flex = new FlexElement
431+
{
432+
Direction = FlexDirection.Column,
433+
Wrap = FlexWrap.Wrap
434+
};
435+
flex.AddChild(new TextElement { Content = "AAAA", Width = "120", Height = "40", Margin = "20" });
436+
flex.AddChild(new TextElement { Content = "BBBB", Width = "120", Height = "40", Margin = "20" });
437+
438+
var template = new Template
439+
{
440+
Canvas = new CanvasSettings { Width = 300 },
441+
Elements = new List<TemplateElement> { flex }
442+
};
443+
444+
// Act
445+
var root = _engine.ComputeLayout(template);
446+
447+
// Assert
448+
var flexNode = root.Children[0];
449+
450+
Assert.Equal(20f, flexNode.Children[0].Y, 0.1f);
451+
Assert.Equal(40f, flexNode.Children[0].Height, 0.1f);
452+
Assert.Equal(100f, flexNode.Children[1].Y, 0.1f);
453+
Assert.Equal(40f, flexNode.Children[1].Height, 0.1f);
454+
455+
// Container auto-height includes the trailing bottom margin of the last child.
456+
Assert.Equal(160f, flexNode.Height, 0.1f);
457+
}
421458
}

0 commit comments

Comments
 (0)