Out of flow positioning fixes.

This commit is contained in:
Martín Lucas Golini
2026-05-01 11:30:22 -03:00
parent 717f3c1b9b
commit d64638d989
3 changed files with 131 additions and 21 deletions

View File

@@ -50,7 +50,8 @@ void UIHTMLWidget::setDisplay( CSSDisplay display ) {
if ( getLayoutWidthPolicy() == SizePolicy::MatchParent )
setLayoutWidthPolicy( SizePolicy::WrapContent );
} else if ( mDisplay == CSSDisplay::Block || mDisplay == CSSDisplay::ListItem ) {
if ( getLayoutWidthPolicy() == SizePolicy::WrapContent )
if ( getLayoutWidthPolicy() == SizePolicy::WrapContent &&
mPosition != CSSPosition::Absolute && mPosition != CSSPosition::Fixed )
setLayoutWidthPolicy( SizePolicy::MatchParent );
}
onDisplayChange();
@@ -60,6 +61,10 @@ void UIHTMLWidget::setDisplay( CSSDisplay display ) {
void UIHTMLWidget::setCSSPosition( CSSPosition position ) {
if ( mPosition != position ) {
mPosition = position;
if ( position == CSSPosition::Absolute || position == CSSPosition::Fixed ) {
if ( getLayoutWidthPolicy() == SizePolicy::MatchParent )
setLayoutWidthPolicy( SizePolicy::WrapContent );
}
onPositionChange();
}
}
@@ -203,22 +208,51 @@ void UIHTMLWidget::positionOutOfFlowChildren() {
if ( pos == CSSPosition::Absolute || pos == CSSPosition::Fixed ) {
UIWidget* cb = htmlChild->getContainingBlock();
if ( cb ) {
Float top = htmlChild->mTopEq == "auto"
? 0
: htmlChild->lengthFromValue(
htmlChild->mTopEq,
CSS::PropertyRelativeTarget::ContainingBlockHeight, 0 );
Float left = htmlChild->mLeftEq == "auto"
? 0
: htmlChild->lengthFromValue(
htmlChild->mLeftEq,
CSS::PropertyRelativeTarget::ContainingBlockWidth, 0 );
Rectf cbContentOffset = cb->getPixelsContentOffset();
Float cbContentWidth = cb->getPixelsSize().getWidth() - cbContentOffset.Left -
cbContentOffset.Right;
Float cbContentHeight = cb->getPixelsSize().getHeight() - cbContentOffset.Top -
cbContentOffset.Bottom;
top += htmlChild->getLayoutPixelsMargin().Top;
left += htmlChild->getLayoutPixelsMargin().Left;
Rectf margin = htmlChild->getLayoutPixelsMargin();
Float childWidth = htmlChild->getPixelsSize().getWidth();
Float childHeight = htmlChild->getPixelsSize().getHeight();
Vector2f cbPos( cb->getPixelsContentOffset().Left,
cb->getPixelsContentOffset().Top );
Float top = 0;
Float left = 0;
bool useTop = htmlChild->mTopEq != "auto";
bool useBottom = htmlChild->mBottomEq != "auto";
bool useLeft = htmlChild->mLeftEq != "auto";
bool useRight = htmlChild->mRightEq != "auto";
if ( useLeft ) {
left = htmlChild->lengthFromValue(
htmlChild->mLeftEq, CSS::PropertyRelativeTarget::ContainingBlockWidth,
0 );
} else if ( useRight ) {
Float rightVal = htmlChild->lengthFromValue(
htmlChild->mRightEq, CSS::PropertyRelativeTarget::ContainingBlockWidth,
0 );
left = cbContentWidth - childWidth - margin.Left - margin.Right - rightVal;
}
if ( useTop ) {
top = htmlChild->lengthFromValue(
htmlChild->mTopEq, CSS::PropertyRelativeTarget::ContainingBlockHeight,
0 );
} else if ( useBottom ) {
Float bottomVal = htmlChild->lengthFromValue(
htmlChild->mBottomEq,
CSS::PropertyRelativeTarget::ContainingBlockHeight, 0 );
top =
cbContentHeight - childHeight - margin.Top - margin.Bottom - bottomVal;
}
top += margin.Top;
left += margin.Left;
Vector2f cbPos( cbContentOffset.Left, cbContentOffset.Top );
cbPos.x += left;
cbPos.y += top;

View File

@@ -270,3 +270,84 @@ UTEST( UIHTMLWidget, positionOutOfFlow_ComplexHTML ) {
Engine::destroySingleton();
}
UTEST( UIHTMLWidget, positionOutOfFlow_ShrinkToFit ) {
init_ui_test();
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
UIRichText* relContainer = UIRichText::New();
relContainer->setParent( sceneNode->getRoot() );
relContainer->setCSSPosition( CSSPosition::Relative );
relContainer->setPixelsSize( 800, 600 );
relContainer->setPixelsPosition( 0, 0 );
UIRichText* absoluteChild = UIRichText::New();
absoluteChild->setParent( relContainer );
absoluteChild->applyProperty( StyleSheetProperty( "position", "absolute" ) );
absoluteChild->applyProperty( StyleSheetProperty( "display", "block" ) );
absoluteChild->applyProperty( StyleSheetProperty( "padding", "4px" ) );
sceneNode->updateDirtyLayouts();
EXPECT_EQ( SizePolicy::WrapContent, absoluteChild->getLayoutWidthPolicy() );
// With no text content, the element should shrink to just padding
Float childWidth = absoluteChild->getPixelsSize().getWidth();
EXPECT_LT( childWidth, 20.f );
Engine::destroySingleton();
}
UTEST( UIHTMLWidget, positionOutOfFlow_RightBottomPositioning ) {
init_ui_test();
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
UIHTMLWidget* relContainer = UIHTMLWidget::New();
relContainer->setParent( sceneNode->getRoot() );
relContainer->setCSSPosition( CSSPosition::Relative );
relContainer->setPixelsSize( 800, 600 );
relContainer->setPixelsPosition( 0, 0 );
UIHTMLWidget* absoluteChild = UIHTMLWidget::New();
absoluteChild->setParent( relContainer );
absoluteChild->setPixelsSize( 100, 50 );
absoluteChild->applyProperty( StyleSheetProperty( "position", "absolute" ) );
absoluteChild->applyProperty( StyleSheetProperty( "right", "0px" ) );
absoluteChild->applyProperty( StyleSheetProperty( "bottom", "0px" ) );
sceneNode->updateDirtyLayouts();
Vector2f worldPos = absoluteChild->convertToWorldSpace( { 0, 0 } );
EXPECT_NEAR( 700.f, worldPos.x, 1.f );
EXPECT_NEAR( 550.f, worldPos.y, 1.f );
Engine::destroySingleton();
}
UTEST( UIHTMLWidget, positionOutOfFlow_RightBottomWithMargin ) {
init_ui_test();
UISceneNode* sceneNode = SceneManager::instance()->getUISceneNode();
UIHTMLWidget* relContainer = UIHTMLWidget::New();
relContainer->setParent( sceneNode->getRoot() );
relContainer->setCSSPosition( CSSPosition::Relative );
relContainer->setPixelsSize( 800, 600 );
relContainer->setPixelsPosition( 0, 0 );
UIHTMLWidget* absoluteChild = UIHTMLWidget::New();
absoluteChild->setParent( relContainer );
absoluteChild->setPixelsSize( 100, 50 );
absoluteChild->applyProperty( StyleSheetProperty( "position", "absolute" ) );
absoluteChild->applyProperty( StyleSheetProperty( "right", "10px" ) );
absoluteChild->applyProperty( StyleSheetProperty( "bottom", "20px" ) );
absoluteChild->applyProperty( StyleSheetProperty( "margin-right", "5px" ) );
absoluteChild->applyProperty( StyleSheetProperty( "margin-bottom", "5px" ) );
sceneNode->updateDirtyLayouts();
Vector2f worldPos = absoluteChild->convertToWorldSpace( { 0, 0 } );
EXPECT_NEAR( 685.f, worldPos.x, 1.f );
EXPECT_NEAR( 525.f, worldPos.y, 1.f );
Engine::destroySingleton();
}