diff --git a/bin/unit_tests/assets/html/flex_li_width.html b/bin/unit_tests/assets/html/flex_li_width.html
new file mode 100644
index 000000000..674c73b50
--- /dev/null
+++ b/bin/unit_tests/assets/html/flex_li_width.html
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/include/eepp/ui/flexlayouter.hpp b/include/eepp/ui/flexlayouter.hpp
index 561ae4c28..53b4253b6 100644
--- a/include/eepp/ui/flexlayouter.hpp
+++ b/include/eepp/ui/flexlayouter.hpp
@@ -33,6 +33,15 @@ class EE_API FlexLayouter : public UILayouter {
Float mainPos;
Float crossPos;
+ // Margins along the flex axes
+ Float marginMainStart{ 0.f };
+ Float marginMainEnd{ 0.f };
+ Float marginCrossStart{ 0.f };
+ Float marginCrossEnd{ 0.f };
+
+ Float minMainSize{ 0.f };
+ Float maxMainSize{ std::numeric_limits::max() };
+
FlexItem() :
widget( nullptr ),
order( 0 ),
@@ -76,8 +85,7 @@ class EE_API FlexLayouter : public UILayouter {
void readContainerStyle( CSSFlexDirection& direction, CSSFlexWrap& wrap,
CSSJustifyContent& justify, CSSAlignItems& alignItems,
- CSSAlignContent& alignContent, Float& columnGap,
- Float& rowGap );
+ CSSAlignContent& alignContent, Float& columnGap, Float& rowGap );
void readItemStyle( UIWidget* child, FlexItem& item );
@@ -100,8 +108,7 @@ class EE_API FlexLayouter : public UILayouter {
void alignMainAxis( FlexLine& line, const Float containerMainSize, const Float columnGap );
- void resolveCrossSizes( FlexLine& line, const Axis& crossAxis,
- const Float containerCrossSize );
+ void resolveCrossSizes( FlexLine& line, const Axis& crossAxis, const Axis& mainAxis );
void alignCrossAxis( const std::vector& lines, const Float containerCrossSize,
const Float rowGap, const Axis& crossAxis );
@@ -114,7 +121,7 @@ class EE_API FlexLayouter : public UILayouter {
void measureFlexItems( const Axis& mainAxis, const Axis& crossAxis,
const Float containerCrossSize, const Float containerWidth,
const Float containerHeight, const Rectf& containerPadding,
- bool indefiniteMainSize );
+ bool indefiniteMainSize, bool indefiniteCrossSize );
std::vector mItems;
CSSFlexDirection mDirection{ CSSFlexDirection::Row };
diff --git a/src/eepp/ui/blocklayouter.cpp b/src/eepp/ui/blocklayouter.cpp
index 90e74f221..b392cb3f6 100644
--- a/src/eepp/ui/blocklayouter.cpp
+++ b/src/eepp/ui/blocklayouter.cpp
@@ -88,7 +88,7 @@ void BlockLayouter::updateLayout() {
bool preserveFloatConstrainedBFCWidth =
widget->establishesBlockFormattingContext() && !rt->getExternalFloatExclusions().empty();
- if ( !preserveFloatConstrainedBFCWidth )
+ if ( !preserveFloatConstrainedBFCWidth && !parentIsFlex )
setMatchParentIfNeededVerticalGrowth();
const StyleSheetProperty* prop = nullptr;
diff --git a/src/eepp/ui/flexlayouter.cpp b/src/eepp/ui/flexlayouter.cpp
index 8b998f9d7..d7c8cf39a 100644
--- a/src/eepp/ui/flexlayouter.cpp
+++ b/src/eepp/ui/flexlayouter.cpp
@@ -166,13 +166,16 @@ Float FlexLayouter::getItemCrossSize( UIWidget* child, const Axis& crossAxis ) c
void FlexLayouter::measureFlexItems( const Axis& mainAxis, const Axis& crossAxis,
const Float containerCrossSize, const Float containerWidth,
const Float containerHeight, const Rectf& containerPadding,
- bool indefiniteMainSize ) {
+ bool indefiniteMainSize, bool indefiniteCrossSize ) {
for ( auto& item : mItems ) {
Float tentativeCrossSize = containerCrossSize;
- if ( tentativeCrossSize <= 0.f && crossAxis.horizontal )
+ if ( indefiniteCrossSize ) {
+ tentativeCrossSize = 0.f;
+ } else if ( tentativeCrossSize <= 0.f && crossAxis.horizontal ) {
tentativeCrossSize = containerWidth - containerPadding.Left - containerPadding.Right;
- if ( tentativeCrossSize <= 0.f && !crossAxis.horizontal )
+ } else if ( tentativeCrossSize <= 0.f && !crossAxis.horizontal ) {
tentativeCrossSize = containerHeight - containerPadding.Top - containerPadding.Bottom;
+ }
if ( tentativeCrossSize < 0.f )
tentativeCrossSize = 0.f;
@@ -207,6 +210,22 @@ void FlexLayouter::measureFlexItems( const Axis& mainAxis, const Axis& crossAxis
item.widget->setLayoutHeightPolicy( oldMainPolicy );
}
+ // Resolve margins along the flex axes.
+ // Auto margins on the main axis are handled by the flex algorithm
+ // (CSS Flexbox §8.1), so we treat them as 0 here.
+ Rectf margin = item.widget->getLayoutPixelsMargin();
+ if ( mainAxis.horizontal ) {
+ item.marginMainStart = item.widget->hasLayoutMarginLeftAuto() ? 0.f : margin.Left;
+ item.marginMainEnd = item.widget->hasLayoutMarginRightAuto() ? 0.f : margin.Right;
+ item.marginCrossStart = margin.Top;
+ item.marginCrossEnd = margin.Bottom;
+ } else {
+ item.marginMainStart = item.widget->hasLayoutMarginTopAuto() ? 0.f : margin.Top;
+ item.marginMainEnd = item.widget->hasLayoutMarginBottomAuto() ? 0.f : margin.Bottom;
+ item.marginCrossStart = margin.Left;
+ item.marginCrossEnd = margin.Right;
+ }
+
item.targetMainSize = resolveFlexBasis( item.widget, mDirection, item.flexBasisValue,
item.flexBasisAuto, mainAxis );
@@ -227,6 +246,38 @@ void FlexLayouter::measureFlexItems( const Axis& mainAxis, const Axis& crossAxis
if ( hprop )
item.targetMainSize = item.widget->lengthFromValue( *hprop );
}
+
+ // Compute min/max main size (CSS §4.5: min-width/min-height default is auto for flex items)
+ if ( mainAxis.horizontal ) {
+ if ( item.widget->isType( UI_TYPE_HTML_WIDGET ) &&
+ item.widget->asType()->getLayouter() )
+ item.minMainSize =
+ item.widget->asType()->getLayouter()->getMinIntrinsicWidth();
+ else
+ item.minMainSize = item.widget->getPixelsSize().getWidth();
+ if ( item.minMainSize < 0.f )
+ item.minMainSize = 0.f;
+
+ if ( item.widget->getUIStyle() ) {
+ const auto* maxW = item.widget->getUIStyle()->getProperty( PropertyId::MaxWidth );
+ if ( maxW )
+ item.maxMainSize = item.widget->lengthFromValue( *maxW );
+ else
+ item.maxMainSize = std::numeric_limits::max();
+ }
+ } else {
+ item.minMainSize = item.widget->getPixelsSize().getHeight();
+ if ( item.minMainSize < 0.f )
+ item.minMainSize = 0.f;
+
+ if ( item.widget->getUIStyle() ) {
+ const auto* maxH = item.widget->getUIStyle()->getProperty( PropertyId::MaxHeight );
+ if ( maxH )
+ item.maxMainSize = item.widget->lengthFromValue( *maxH );
+ else
+ item.maxMainSize = std::numeric_limits::max();
+ }
+ }
}
}
@@ -245,13 +296,15 @@ void FlexLayouter::generateFlexLines( std::vector& lines
std::vector nextRemaining;
for ( size_t idx : remaining ) {
- Float itemSize = mItems[idx].targetMainSize;
+ const auto& item = mItems[idx];
+ Float itemOuterSize = item.targetMainSize + item.marginMainStart + item.marginMainEnd;
Float gap = line.itemIndices.empty() ? 0.f : columnGap;
- if ( mWrap == CSSFlexWrap::NoWrap || ( usedMain + gap + itemSize <= containerMainSize &&
- usedMain + gap + itemSize >= 0.f ) ) {
+ if ( mWrap == CSSFlexWrap::NoWrap ||
+ ( usedMain + gap + itemOuterSize <= containerMainSize &&
+ usedMain + gap + itemOuterSize >= 0.f ) ) {
line.itemIndices.push_back( idx );
- usedMain += gap + itemSize;
+ usedMain += gap + itemOuterSize;
} else {
nextRemaining.push_back( idx );
}
@@ -296,9 +349,10 @@ void FlexLayouter::resolveFlexibleLengths( FlexLine& line, const Float container
Float totalFlexShrink = 0.f;
for ( size_t idx : line.itemIndices ) {
- totalFlexBasis += mItems[idx].targetMainSize;
- totalFlexGrow += mItems[idx].flexGrow;
- totalFlexShrink += mItems[idx].flexShrink;
+ const auto& item = mItems[idx];
+ totalFlexBasis += item.targetMainSize + item.marginMainStart + item.marginMainEnd;
+ totalFlexGrow += item.flexGrow;
+ totalFlexShrink += item.flexShrink;
}
size_t itemCount = line.itemIndices.size();
@@ -353,6 +407,15 @@ void FlexLayouter::resolveFlexibleLengths( FlexLine& line, const Float container
}
}
}
+
+ // Clamp each item by its min/max main size (CSS Flexbox §9.7 step 6)
+ for ( size_t idx : line.itemIndices ) {
+ auto& item = mItems[idx];
+ if ( item.targetMainSize < item.minMainSize )
+ item.targetMainSize = item.minMainSize;
+ if ( item.targetMainSize > item.maxMainSize )
+ item.targetMainSize = item.maxMainSize;
+ }
}
void FlexLayouter::alignMainAxis( FlexLine& line, const Float containerMainSize,
@@ -362,13 +425,13 @@ void FlexLayouter::alignMainAxis( FlexLine& line, const Float containerMainSize,
size_t itemCount = line.itemIndices.size();
Float totalItemMain = 0.f;
- for ( size_t idx : line.itemIndices )
- totalItemMain += mItems[idx].targetMainSize;
+ for ( size_t idx : line.itemIndices ) {
+ const auto& item = mItems[idx];
+ totalItemMain += item.targetMainSize + item.marginMainStart + item.marginMainEnd;
+ }
Float totalGap = ( itemCount > 1 ) ? (Float)( itemCount - 1 ) * columnGap : 0.f;
Float freeSpaceMain = containerMainSize - totalItemMain - totalGap;
- if ( freeSpaceMain < 0.f )
- freeSpaceMain = 0.f;
Float mainOffset = 0.f;
Float spacing = columnGap;
@@ -381,19 +444,19 @@ void FlexLayouter::alignMainAxis( FlexLine& line, const Float containerMainSize,
mainOffset = freeSpaceMain * 0.5f;
break;
case CSSJustifyContent::SpaceBetween:
- if ( itemCount > 1 )
+ if ( freeSpaceMain > 0.f && itemCount > 1 )
spacing = columnGap + freeSpaceMain / (Float)( itemCount - 1 );
else
mainOffset = 0.f;
break;
case CSSJustifyContent::SpaceAround:
- if ( itemCount > 0 ) {
+ if ( freeSpaceMain > 0.f && itemCount > 0 ) {
spacing = columnGap + freeSpaceMain / (Float)itemCount;
mainOffset = spacing * 0.5f;
}
break;
case CSSJustifyContent::SpaceEvenly:
- if ( itemCount > 0 ) {
+ if ( freeSpaceMain > 0.f && itemCount > 0 ) {
Float slot = columnGap + freeSpaceMain / (Float)( itemCount + 1 );
mainOffset = slot;
spacing = slot;
@@ -407,32 +470,40 @@ void FlexLayouter::alignMainAxis( FlexLine& line, const Float containerMainSize,
Float pos = mainOffset;
for ( size_t idx : line.itemIndices ) {
- mItems[idx].mainPos = pos;
- pos += mItems[idx].targetMainSize + spacing;
+ const auto& item = mItems[idx];
+ mItems[idx].mainPos = pos + item.marginMainStart;
+ pos += item.targetMainSize + item.marginMainStart + item.marginMainEnd + spacing;
}
}
void FlexLayouter::resolveCrossSizes( FlexLine& line, const Axis& crossAxis,
- const Float containerCrossSize ) {
+ const Axis& mainAxis ) {
if ( line.itemIndices.empty() ) {
line.crossSize = 0.f;
return;
}
+ // Set each item's main-axis size to its resolved targetMainSize so that
+ // getItemCrossSize reflects the correct cross size for the final width.
+ for ( size_t idx : line.itemIndices ) {
+ auto& item = mItems[idx];
+ if ( mainAxis.horizontal )
+ item.widget->setInternalPixelsWidth( item.targetMainSize );
+ else
+ item.widget->setInternalPixelsHeight( item.targetMainSize );
+ if ( item.widget->isType( UI_TYPE_HTML_WIDGET ) ) {
+ auto* htmlWidget = item.widget->asType();
+ if ( htmlWidget->getLayouter() && htmlWidget->getLayouter() != this )
+ htmlWidget->updateLayout();
+ }
+ }
+
Float maxCross = 0.f;
for ( size_t idx : line.itemIndices ) {
auto& item = mItems[idx];
- CSSAlignSelf resolved = resolveAlignSelf( item.alignSelf, mAlignItems );
- bool canStretch = crossAxis.horizontal
- ? ( item.widget->getLayoutWidthPolicy() != SizePolicy::Fixed )
- : ( item.widget->getLayoutHeightPolicy() != SizePolicy::Fixed );
- if ( resolved == CSSAlignSelf::Stretch && canStretch && containerCrossSize > 0.f ) {
- item.crossSize = containerCrossSize;
- } else {
- item.crossSize = getItemCrossSize( item.widget, crossAxis );
- }
- item.outerCrossSize = item.crossSize;
- maxCross = eemax( maxCross, item.crossSize );
+ item.crossSize = getItemCrossSize( item.widget, crossAxis );
+ item.outerCrossSize = item.crossSize + item.marginCrossStart + item.marginCrossEnd;
+ maxCross = eemax( maxCross, item.outerCrossSize );
}
line.crossSize = maxCross;
}
@@ -456,6 +527,7 @@ void FlexLayouter::alignCrossAxis( const std::vector& lines,
Float lineGap = rowGap;
if ( lineCount > 1 ) {
+ Float fsc = freeSpaceCross;
switch ( mAlignContent ) {
case CSSAlignContent::FlexEnd:
crossOffset = freeSpaceCross;
@@ -464,20 +536,24 @@ void FlexLayouter::alignCrossAxis( const std::vector& lines,
crossOffset = freeSpaceCross * 0.5f;
break;
case CSSAlignContent::SpaceBetween:
- if ( lineCount > 1 )
- lineGap = freeSpaceCross / (Float)( lineCount - 1 );
+ if ( fsc > 0.f && lineCount > 1 )
+ lineGap = fsc / (Float)( lineCount - 1 );
break;
case CSSAlignContent::SpaceAround:
- lineGap = freeSpaceCross / (Float)lineCount;
- crossOffset = lineGap * 0.5f;
+ if ( fsc > 0.f ) {
+ lineGap = fsc / (Float)lineCount;
+ crossOffset = lineGap * 0.5f;
+ }
break;
case CSSAlignContent::SpaceEvenly:
- lineGap = freeSpaceCross / (Float)( lineCount + 1 );
- crossOffset = lineGap;
+ if ( fsc > 0.f ) {
+ lineGap = fsc / (Float)( lineCount + 1 );
+ crossOffset = lineGap;
+ }
break;
case CSSAlignContent::Stretch:
- if ( lineCount > 0 )
- addPerLine = freeSpaceCross / (Float)lineCount;
+ if ( fsc > 0.f && lineCount > 0 )
+ addPerLine = fsc / (Float)lineCount;
break;
case CSSAlignContent::FlexStart:
default:
@@ -491,6 +567,8 @@ void FlexLayouter::alignCrossAxis( const std::vector& lines,
line.crossPos = pos;
Float lineCrossSize = line.crossSize + addPerLine;
+ if ( lineCrossSize < line.crossSize )
+ lineCrossSize = line.crossSize;
if ( lines.size() == 1 && containerCrossSize > 0.f )
lineCrossSize = containerCrossSize;
if ( lineCrossSize > containerCrossSize && containerCrossSize > 0.f )
@@ -500,30 +578,31 @@ void FlexLayouter::alignCrossAxis( const std::vector& lines,
auto& item = mItems[idx];
CSSAlignSelf resolved = resolveAlignSelf( item.alignSelf, mAlignItems );
- Float itemFreeSpace = lineCrossSize - item.crossSize;
+ Float itemFreeSpace = lineCrossSize - item.outerCrossSize;
switch ( resolved ) {
case CSSAlignSelf::FlexStart:
- item.crossPos = pos;
+ item.crossPos = pos + item.marginCrossStart;
break;
case CSSAlignSelf::FlexEnd:
- item.crossPos = pos + itemFreeSpace;
+ item.crossPos = pos + itemFreeSpace + item.marginCrossStart;
break;
case CSSAlignSelf::Center:
- item.crossPos = pos + itemFreeSpace * 0.5f;
+ item.crossPos = pos + itemFreeSpace * 0.5f + item.marginCrossStart;
break;
case CSSAlignSelf::Stretch:
- item.crossPos = pos;
+ item.crossPos = pos + item.marginCrossStart;
if ( !( crossAxis.horizontal
? ( item.widget->getLayoutWidthPolicy() == SizePolicy::Fixed )
: ( item.widget->getLayoutHeightPolicy() == SizePolicy::Fixed ) ) )
- item.crossSize = lineCrossSize;
+ item.crossSize =
+ lineCrossSize - item.marginCrossStart - item.marginCrossEnd;
break;
case CSSAlignSelf::Baseline:
- item.crossPos = pos;
+ item.crossPos = pos + item.marginCrossStart;
break;
case CSSAlignSelf::Auto:
default:
- item.crossPos = pos;
+ item.crossPos = pos + item.marginCrossStart;
break;
}
}
@@ -554,8 +633,10 @@ void FlexLayouter::applyLayout( const std::vector& lines, const Axis&
widgetY = containerPadding.Top + item.crossPos;
widgetW = item.targetMainSize;
widgetH = item.crossSize;
- contentW = eemax( contentW, widgetX + widgetW - containerPadding.Left );
- contentH = eemax( contentH, widgetY + widgetH - containerPadding.Top );
+ contentW = eemax( contentW,
+ widgetX + widgetW + item.marginMainEnd - containerPadding.Left );
+ contentH = eemax( contentH,
+ widgetY + widgetH + item.marginCrossEnd - containerPadding.Top );
} else {
if ( mainAxis.reversed ) {
widgetY = containerHeight - containerPadding.Bottom - item.mainPos -
@@ -566,8 +647,10 @@ void FlexLayouter::applyLayout( const std::vector& lines, const Axis&
widgetX = containerPadding.Left + item.crossPos;
widgetW = item.crossSize;
widgetH = item.targetMainSize;
- contentW = eemax( contentW, widgetX + widgetW - containerPadding.Left );
- contentH = eemax( contentH, widgetY + widgetH - containerPadding.Top );
+ contentW = eemax( contentW,
+ widgetX + widgetW + item.marginCrossEnd - containerPadding.Left );
+ contentH = eemax( contentH,
+ widgetY + widgetH + item.marginMainEnd - containerPadding.Top );
}
item.widget->setPixelsPosition( widgetX, widgetY );
@@ -669,6 +752,16 @@ void FlexLayouter::updateLayout() {
if ( containerCrossSize < 0.f )
containerCrossSize = 0.f;
+ // Detect indefinite cross size: WrapContent in the cross axis direction
+ bool indefiniteCrossSize = ( crossAxis.horizontal && widthPolicy == SizePolicy::WrapContent ) ||
+ ( !crossAxis.horizontal && heightPolicy == SizePolicy::WrapContent );
+
+ // When cross size is indefinite, zero out containerCrossSize so that
+ // the flex algorithm sizes lines to content rather than using a stale
+ // container size from a previous layout pass.
+ if ( indefiniteCrossSize )
+ containerCrossSize = 0.f;
+
collectFlexItems( mItems );
if ( mItems.empty() ) {
@@ -735,15 +828,15 @@ void FlexLayouter::updateLayout() {
( !mainAxis.horizontal && heightPolicy == SizePolicy::WrapContent );
measureFlexItems( mainAxis, crossAxis, containerCrossSize, containerWidth, containerHeight,
- containerPadding, indefiniteMainSize );
+ containerPadding, indefiniteMainSize, indefiniteCrossSize );
std::vector lines;
if ( indefiniteMainSize ) {
- // Compute container main size from item content sizes (CSS §9.3)
+ // Compute container main size from item outer sizes (CSS §9.3)
Float naturalMainSize = 0.f;
for ( auto& item : mItems )
- naturalMainSize += item.targetMainSize;
+ naturalMainSize += item.targetMainSize + item.marginMainStart + item.marginMainEnd;
if ( mItems.size() > 1 )
naturalMainSize += (Float)( mItems.size() - 1 ) * mColumnGap;
containerMainSize = naturalMainSize;
@@ -755,7 +848,7 @@ void FlexLayouter::updateLayout() {
if ( !indefiniteMainSize )
resolveFlexibleLengths( line, containerMainSize, mColumnGap );
alignMainAxis( line, containerMainSize, mColumnGap );
- resolveCrossSizes( line, crossAxis, containerCrossSize );
+ resolveCrossSizes( line, crossAxis, mainAxis );
}
alignCrossAxis( lines, containerCrossSize, mRowGap, crossAxis );
@@ -798,6 +891,19 @@ void FlexLayouter::computeIntrinsicWidths() {
for ( auto& item : items ) {
item.targetMainSize = resolveFlexBasis( item.widget, mDirection, item.flexBasisValue,
item.flexBasisAuto, mainAxis );
+
+ Rectf margin = item.widget->getLayoutPixelsMargin();
+ if ( mainAxis.horizontal ) {
+ item.marginMainStart = item.widget->hasLayoutMarginLeftAuto() ? 0.f : margin.Left;
+ item.marginMainEnd = item.widget->hasLayoutMarginRightAuto() ? 0.f : margin.Right;
+ item.marginCrossStart = margin.Top;
+ item.marginCrossEnd = margin.Bottom;
+ } else {
+ item.marginMainStart = item.widget->hasLayoutMarginTopAuto() ? 0.f : margin.Top;
+ item.marginMainEnd = item.widget->hasLayoutMarginBottomAuto() ? 0.f : margin.Bottom;
+ item.marginCrossStart = margin.Left;
+ item.marginCrossEnd = margin.Right;
+ }
}
const Rectf containerPadding = mContainer->getPixelsContentOffset();
@@ -805,7 +911,7 @@ void FlexLayouter::computeIntrinsicWidths() {
if ( mWrap == CSSFlexWrap::NoWrap ) {
Float maxContentMain = 0.f;
for ( auto& item : items )
- maxContentMain += item.targetMainSize;
+ maxContentMain += item.targetMainSize + item.marginMainStart + item.marginMainEnd;
if ( items.size() > 1 )
maxContentMain += (Float)( items.size() - 1 ) * mColumnGap;
@@ -815,7 +921,8 @@ void FlexLayouter::computeIntrinsicWidths() {
} else {
Float maxItemWidth = 0.f;
for ( auto& item : items )
- maxItemWidth = eemax( maxItemWidth, item.targetMainSize );
+ maxItemWidth = eemax( maxItemWidth, item.targetMainSize + item.marginCrossStart +
+ item.marginCrossEnd );
mMaxIntrinsicWidth = maxItemWidth + containerPadding.Left + containerPadding.Right;
mMinIntrinsicWidth = containerPadding.Left + containerPadding.Right;
}
@@ -824,7 +931,8 @@ void FlexLayouter::computeIntrinsicWidths() {
Float maxLineWidth = 0.f;
Float lineSum = 0.f;
for ( auto& item : items ) {
- lineSum += ( lineSum > 0.f ? mColumnGap : 0.f ) + item.targetMainSize;
+ lineSum += ( lineSum > 0.f ? mColumnGap : 0.f ) + item.targetMainSize +
+ item.marginMainStart + item.marginMainEnd;
}
maxLineWidth = lineSum;
mMaxIntrinsicWidth = maxLineWidth + containerPadding.Left + containerPadding.Right;
@@ -832,7 +940,8 @@ void FlexLayouter::computeIntrinsicWidths() {
} else {
Float maxItemWidth = 0.f;
for ( auto& item : items )
- maxItemWidth = eemax( maxItemWidth, item.targetMainSize );
+ maxItemWidth = eemax( maxItemWidth, item.targetMainSize + item.marginCrossStart +
+ item.marginCrossEnd );
mMaxIntrinsicWidth = maxItemWidth + containerPadding.Left + containerPadding.Right;
mMinIntrinsicWidth = containerPadding.Left + containerPadding.Right;
}
diff --git a/src/tests/unit_tests/uihtml_tests.cpp b/src/tests/unit_tests/uihtml_tests.cpp
index 1b0dca233..40058db2e 100644
--- a/src/tests/unit_tests/uihtml_tests.cpp
+++ b/src/tests/unit_tests/uihtml_tests.cpp
@@ -3366,3 +3366,57 @@ UTEST( UIHTML, FlexAnchorInFlexNavVisible ) {
Engine::destroySingleton();
}
+
+UTEST( UIHTML, FlexLiItemsWrapContentWidth ) {
+ Engine::instance()->createWindow( WindowSettings( 1024, 768, "Flex LI Width Test",
+ WindowStyle::Default, WindowBackend::Default,
+ 32, {}, 1, false, true ),
+ ContextSettings( false, 0, 0, GLv_default, true, false ) );
+
+ UISceneNode* sceneNode = init_test_inline_block();
+ sceneNode->setURI( "file://" + Sys::getProcessPath() + "assets/html/" );
+
+ std::string html;
+ FileSystem::fileGet( "assets/html/flex_li_width.html", html );
+ sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( html ) );
+
+ sceneNode->update( Seconds( 1 ) );
+ sceneNode->updateDirtyLayouts();
+
+ auto menuContainer = sceneNode->getRoot()->findByClass( "menu-container" );
+ ASSERT_TRUE( menuContainer != nullptr );
+ auto menuWidget = menuContainer->asType();
+
+ // The menu container is a flex row; its children should wrap to
+ // their content width, not span the full container width.
+ Float containerWidth = menuWidget->getPixelsSize().getWidth();
+ Float containerPadding =
+ menuWidget->getPixelsPadding().Left + menuWidget->getPixelsPadding().Right;
+ Float contentWidth = containerWidth - containerPadding;
+
+ Float totalLiWidth = 0.f;
+ Uint32 liCount = 0;
+ for ( Uint32 i = 0; i < menuWidget->getChildCount(); ++i ) {
+ auto c = menuWidget->getChildAt( i );
+ if ( !c->isWidget() )
+ continue;
+ auto cw = c->asType();
+ if ( cw->getElementTag() != "li" )
+ continue;
+
+ // Each should be narrower than the container content width
+ EXPECT_LT( cw->getPixelsSize().getWidth(), contentWidth );
+
+ // Accumulate total width (including margin)
+ Rectf margin = cw->getLayoutPixelsMargin();
+ totalLiWidth += cw->getPixelsSize().getWidth() + margin.Left + margin.Right;
+ liCount++;
+ }
+
+ ASSERT_GT( liCount, (Uint32)0 );
+ // With 9 items and horizontal margins, the total should still fit without
+ // each item spanning the full container.
+ EXPECT_LT( totalLiWidth, contentWidth * (Float)liCount );
+
+ Engine::destroySingleton();
+}