fix rich text propagated float placement loop

- require propagated float placement to make forward progress
  - avoid zero-displacement loops when float edges touch
  - recompute collisions after each vertical adjustment
  - add regression coverage for touching propagated and active floats
This commit is contained in:
Martín Lucas Golini
2026-07-30 20:39:07 -03:00
parent 774e09efc2
commit 58cd2ec327
2 changed files with 27 additions and 8 deletions

View File

@@ -1379,24 +1379,25 @@ class RichTextInlineLayouter {
result.lines.back().y = curY;
}
Float placedY = curY;
bool moved;
do {
moved = false;
while ( true ) {
Float nextPlacedY = placedY;
for ( const auto& propagated : *payload.propagatedFloats ) {
Rectf rect = propagated.rect;
rect.move( { curX, placedY } );
auto avoidActiveFloat = [&]( const Rectf& active ) {
if ( rect.intersect( active ) && active.Bottom > placedY ) {
placedY += active.Bottom - rect.Top;
moved = true;
}
if ( rect.intersect( active ) )
nextPlacedY = std::max(
nextPlacedY, placedY + active.Bottom - rect.Top );
};
for ( const auto& active : leftFloats )
avoidActiveFloat( active );
for ( const auto& active : rightFloats )
avoidActiveFloat( active );
}
} while ( moved );
if ( nextPlacedY <= placedY + 0.01f )
break;
placedY = nextPlacedY;
}
if ( placedY > curY ) {
curY = placedY;
result.lines.back().y = curY;

View File

@@ -61,6 +61,24 @@ UTEST( UIHTMLFloat, structure_FloatAndClearEnums ) {
EXPECT_EQ( (int)CSSClear::None, (int)CSSClearHelper::fromString( "garbage" ) );
}
UTEST( UIHTMLFloat, propagatedFloatTouchingActiveFloatMakesForwardProgress ) {
RichText richText;
richText.setMaxWidth( 200.f );
richText.setExternalFloatExclusions(
{ { Rectf( 0.f, 0.f, 100.f, 20.f ), RichText::InlineFloat::Left } } );
auto propagated = std::make_shared<std::vector<RichText::FloatExclusion>>();
propagated->push_back( { Rectf( -100.f, 20.f, 10.f, 30.f ), RichText::InlineFloat::Left } );
richText.addCustomSize( { 20.f, 10.f }, RichText::InlineFloat::None,
RichText::InlineClear::None, -1.f, {}, {}, false, false, propagated );
const Sizef size = richText.getSize();
EXPECT_TRUE( std::isfinite( size.getWidth() ) );
EXPECT_TRUE( std::isfinite( size.getHeight() ) );
EXPECT_GT( size.getWidth(), 0.f );
EXPECT_GT( size.getHeight(), 0.f );
}
UTEST( UIHTMLFloat, property_DefaultsAreNone ) {
UIHTMLWidget* w = UIHTMLWidget::New();
EXPECT_EQ( CSSFloat::None, w->getCSSFloat() );