Fix in Flex layout interacting with float elements.

This commit is contained in:
Martín Lucas Golini
2026-07-06 12:27:53 -03:00
parent 3f304e9c80
commit bf0c6e8b11
2 changed files with 84 additions and 1 deletions
+20 -1
View File
@@ -1222,6 +1222,14 @@ void FlexLayouter::updateLayout() {
if ( widget->isInline() || mPacking ) if ( widget->isInline() || mPacking )
return; return;
RichText* richText = widget->isType( UI_TYPE_RICHTEXT )
? widget->asType<UIRichText>()->getRichTextPtr()
: nullptr;
bool preserveFloatConstrainedBFCWidth =
widget->establishesBlockFormattingContext() &&
widget->getLayoutWidthPolicy() == SizePolicy::MatchParent && richText != nullptr &&
!richText->getExternalFloatExclusions().empty();
mPacking = true; mPacking = true;
mContainer->beginAttributesTransaction(); mContainer->beginAttributesTransaction();
@@ -1230,7 +1238,7 @@ void FlexLayouter::updateLayout() {
auto* parent = mContainer->getParent(); auto* parent = mContainer->getParent();
bool parentIsFlexContainer = parent && parent->isType( UI_TYPE_HTML_WIDGET ) && bool parentIsFlexContainer = parent && parent->isType( UI_TYPE_HTML_WIDGET ) &&
static_cast<UIHTMLWidget*>( parent )->isFlex(); static_cast<UIHTMLWidget*>( parent )->isFlex();
if ( !parentIsFlexContainer ) if ( !parentIsFlexContainer && !preserveFloatConstrainedBFCWidth )
setMatchParentIfNeededVerticalGrowth(); setMatchParentIfNeededVerticalGrowth();
} }
@@ -1333,6 +1341,8 @@ void FlexLayouter::updateLayout() {
containerPadding.Right ); containerPadding.Right );
else if ( widthPolicy == SizePolicy::Fixed ) else if ( widthPolicy == SizePolicy::Fixed )
mContainer->setInternalPixelsWidth( containerWidth ); mContainer->setInternalPixelsWidth( containerWidth );
else if ( preserveFloatConstrainedBFCWidth )
mContainer->setInternalPixelsWidth( containerWidth );
else else
mContainer->setInternalPixelsWidth( mContainer->setInternalPixelsWidth(
mContainer->getParent() mContainer->getParent()
@@ -1355,6 +1365,8 @@ void FlexLayouter::updateLayout() {
mContainer->setInternalPixelsWidth( totW ); mContainer->setInternalPixelsWidth( totW );
else if ( widthPolicy == SizePolicy::Fixed ) else if ( widthPolicy == SizePolicy::Fixed )
mContainer->setInternalPixelsWidth( containerWidth ); mContainer->setInternalPixelsWidth( containerWidth );
else if ( preserveFloatConstrainedBFCWidth )
mContainer->setInternalPixelsWidth( containerWidth );
else else
mContainer->setInternalPixelsWidth( mContainer->setInternalPixelsWidth(
mContainer->getParent() ? mContainer->getParent()->getPixelsSize().getWidth() mContainer->getParent() ? mContainer->getParent()->getPixelsSize().getWidth()
@@ -1403,6 +1415,13 @@ void FlexLayouter::updateLayout() {
applyLayout( lines, mainAxis, crossAxis, containerPadding, containerWidth, containerHeight, applyLayout( lines, mainAxis, crossAxis, containerPadding, containerWidth, containerHeight,
widthPolicy, heightPolicy ); widthPolicy, heightPolicy );
if ( preserveFloatConstrainedBFCWidth ) {
// The parent RichText stream has already resolved this match-parent BFC against active
// floats and placed it into a narrowed atomic fragment. Flex layout still needs to run
// using that used width, but its normal match-parent finalization would expand back to
// the full parent content box and overlap the float.
mContainer->setInternalPixelsWidth( containerWidth );
}
// CSS Flexbox §8.5: store the container's baseline for use by outer flex containers. // CSS Flexbox §8.5: store the container's baseline for use by outer flex containers.
// For row/row-reverse, baseline = first flex line's baseline offset from content top. // For row/row-reverse, baseline = first flex line's baseline offset from content top.
+64
View File
@@ -3745,6 +3745,70 @@ UTEST( UIHTML, PaddedHeaderWithNestedFloatsDoesNotOverlapClearedMain ) {
Engine::destroySingleton(); Engine::destroySingleton();
} }
UTEST( UIHTML, FlexInfobarAvoidsRightFloat ) {
auto win = Engine::instance()->createWindow(
WindowSettings( 800, 600, "flex infobar avoids right float", WindowStyle::Default,
WindowBackend::Default, 32, {}, 1, false, true ),
ContextSettings( false, 0, 0, GLv_default, true, false ) );
FileSystem::changeWorkingDirectory( Sys::getProcessPath() );
UI::UISceneNode* sceneNode = init_test_inline_block();
sceneNode->loadLayoutFromString( HTMLFormatter::HTMLtoXML( R"html(
<html>
<head>
<style>
body {
margin: 0;
font: normal 12px verdana;
}
.side {
float: right;
width: 300px;
height: 120px;
margin: 0 5px;
}
.content {
margin: 7px 5px 0 5px;
}
.infobar {
display: flex;
overflow: hidden;
box-sizing: border-box;
height: 124px;
margin: 5px;
padding: 0;
}
</style>
</head>
<body>
<div class="side" id="side"></div>
<div class="content" id="content">
<section class="infobar" id="infobar">
<a>Welcome to Reddit.</a>
</section>
</div>
</body>
</html>
)html" ) );
win->getInput()->update();
SceneManager::instance()->update();
sceneNode->updateDirtyLayouts();
sceneNode->updateDirtyLayouts();
auto* side = sceneNode->getRoot()->find( "side" )->asType<UIHTMLWidget>();
auto* infobar = sceneNode->getRoot()->find( "infobar" )->asType<UIHTMLWidget>();
ASSERT_TRUE( side != nullptr );
ASSERT_TRUE( infobar != nullptr );
const Float sideLeft = side->convertToWorldSpace( { 0, 0 } ).x;
const Float infobarRight =
infobar->convertToWorldSpace( { 0, 0 } ).x + infobar->getPixelsSize().getWidth();
EXPECT_LE( infobarRight, sideLeft + 0.5f );
Engine::destroySingleton();
}
UTEST( UIHTML, AnchorsSizing ) { UTEST( UIHTML, AnchorsSizing ) {
auto win = Engine::instance()->createWindow( auto win = Engine::instance()->createWindow(
WindowSettings( 1024, 653, "anchors sizing", WindowStyle::Default, WindowBackend::Default, WindowSettings( 1024, 653, "anchors sizing", WindowStyle::Default, WindowBackend::Default,