mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-04-21 12:05:15 +00:00
LibWeb: Fix and improve float positioning behavior
Our recent change to get rid of the "move 1px at a time" algorithm in the float positioning logic introduced the issue that potentially intersecting float boxes were not evaluated in order anymore. This could result in float boxes being pushed down further than strictly necessary. By finding the highest point we can move the floating box to and repeating the process until we're no longer intersecting any floating box, we also solve some edge cases like intersecting with very long floating boxes whose edges lay outside the current box' edges. This is by no means the most efficient solution, but it is more correct than what we had until now. Fixes #4110.
This commit is contained in:
parent
32dbd6ab8f
commit
c4bb74f40b
Notes:
github-actions[bot]
2025-03-27 10:57:05 +00:00
Author: https://github.com/gmta Commit: https://github.com/LadybirdBrowser/ladybird/commit/c4bb74f40bb Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4113
5 changed files with 143 additions and 16 deletions
|
@ -129,23 +129,44 @@ CSSPixels LineBuilder::y_for_float_to_be_inserted_here(Box const& box)
|
|||
|
||||
// Then, look for the next Y position where we can fit the new float.
|
||||
auto box_in_root_rect = m_context.parent().content_box_rect_in_ancestor_coordinate_space(box_state, m_context.parent().root());
|
||||
m_context.parent().for_each_floating_box([&](auto const& float_box) {
|
||||
auto candidate_block_offset_in_root = box_in_root_rect.y() + candidate_block_offset;
|
||||
if (float_box.margin_box_rect_in_root_coordinate_space.bottom() < candidate_block_offset_in_root)
|
||||
|
||||
HashMap<CSSPixels, AvailableSize> available_space_cache;
|
||||
for (;;) {
|
||||
Optional<CSSPixels> highest_intersection_bottom;
|
||||
|
||||
auto candidate_block_top_in_root = box_in_root_rect.y() + candidate_block_offset;
|
||||
auto candidate_block_bottom_in_root = candidate_block_top_in_root + height;
|
||||
|
||||
m_context.parent().for_each_floating_box([&](auto const& float_box) {
|
||||
auto float_box_top = float_box.margin_box_rect_in_root_coordinate_space.top();
|
||||
auto float_box_bottom = float_box.margin_box_rect_in_root_coordinate_space.bottom();
|
||||
if (float_box_bottom <= candidate_block_top_in_root)
|
||||
return IterationDecision::Continue;
|
||||
|
||||
auto intersection_test = [&](auto y_coordinate, auto top, auto bottom) {
|
||||
if (y_coordinate < top || y_coordinate > bottom)
|
||||
return;
|
||||
auto available_space = available_space_cache.ensure(y_coordinate, [&]() {
|
||||
return m_context.available_space_for_line(y_coordinate);
|
||||
});
|
||||
if (width > available_space) {
|
||||
auto bottom_relative = float_box_bottom - box_in_root_rect.y();
|
||||
highest_intersection_bottom = min(highest_intersection_bottom.value_or(bottom_relative), bottom_relative);
|
||||
}
|
||||
};
|
||||
|
||||
intersection_test(float_box_top, candidate_block_top_in_root, candidate_block_bottom_in_root);
|
||||
intersection_test(float_box_bottom, candidate_block_top_in_root, candidate_block_bottom_in_root);
|
||||
intersection_test(candidate_block_top_in_root, float_box_top, float_box_bottom);
|
||||
intersection_test(candidate_block_bottom_in_root, float_box_top, float_box_bottom);
|
||||
|
||||
return IterationDecision::Continue;
|
||||
auto space_at_y_top = m_context.available_space_for_line(candidate_block_offset);
|
||||
auto space_at_y_bottom = m_context.available_space_for_line(candidate_block_offset + height);
|
||||
if (width > space_at_y_top || width > space_at_y_bottom) {
|
||||
if (!m_context.any_floats_intrude_at_block_offset(candidate_block_offset) && !m_context.any_floats_intrude_at_block_offset(candidate_block_offset + height)) {
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
} else {
|
||||
return IterationDecision::Break;
|
||||
}
|
||||
// candidate_block_offset needs to stay relative to the current box
|
||||
candidate_block_offset = float_box.margin_box_rect_in_root_coordinate_space.bottom() - box_in_root_rect.y();
|
||||
return IterationDecision::Continue;
|
||||
});
|
||||
});
|
||||
if (!highest_intersection_bottom.has_value() || highest_intersection_bottom.value() == candidate_block_offset)
|
||||
break;
|
||||
candidate_block_offset = highest_intersection_bottom.value();
|
||||
}
|
||||
|
||||
return candidate_block_offset;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
|
||||
BlockContainer <html> at (0,0) content-size 800x413 [BFC] children: not-inline
|
||||
BlockContainer <body> at (8,8) content-size 784x5 children: not-inline
|
||||
BlockContainer <div.a> at (8,8) content-size 100x5 children: inline
|
||||
frag 0 from TextNode start: 1, length: 1, rect: [8,8 4.328125x5] baseline: 4
|
||||
"H"
|
||||
TextNode <#text>
|
||||
BlockContainer <div.b.l> at (8,13) content-size 100x100 floating [BFC] children: not-inline
|
||||
TextNode <#text>
|
||||
BlockContainer <div.c.l> at (8,113) content-size 30x300 floating [BFC] children: not-inline
|
||||
TextNode <#text>
|
||||
BlockContainer <div.c.r> at (78,113) content-size 30x300 floating [BFC] children: not-inline
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,13) content-size 784x0 children: inline
|
||||
TextNode <#text>
|
||||
|
||||
ViewportPaintable (Viewport<#document>) [0,0 800x600]
|
||||
PaintableWithLines (BlockContainer<HTML>) [0,0 800x413]
|
||||
PaintableWithLines (BlockContainer<BODY>) [8,8 784x5]
|
||||
PaintableWithLines (BlockContainer<DIV>.a) [8,8 100x5]
|
||||
TextPaintable (TextNode<#text>)
|
||||
PaintableWithLines (BlockContainer<DIV>.b.l) [8,13 100x100]
|
||||
PaintableWithLines (BlockContainer<DIV>.c.l) [8,113 30x300]
|
||||
PaintableWithLines (BlockContainer<DIV>.c.r) [78,113 30x300]
|
||||
PaintableWithLines (BlockContainer(anonymous)) [8,13 784x0]
|
|
@ -0,0 +1,22 @@
|
|||
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
|
||||
BlockContainer <html> at (0,0) content-size 800x216 [BFC] children: not-inline
|
||||
BlockContainer <body> at (8,8) content-size 784x200 children: not-inline
|
||||
BlockContainer <div.a> at (8,8) content-size 500x200 children: inline
|
||||
TextNode <#text>
|
||||
BlockContainer <div.b> at (8,8) content-size 200x150 floating [BFC] children: not-inline
|
||||
TextNode <#text>
|
||||
BlockContainer <div.c> at (308,8) content-size 200x100 floating [BFC] children: not-inline
|
||||
TextNode <#text>
|
||||
BlockContainer <div.d> at (308,108) content-size 200x100 floating [BFC] children: not-inline
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> at (8,208) content-size 784x0 children: inline
|
||||
TextNode <#text>
|
||||
|
||||
ViewportPaintable (Viewport<#document>) [0,0 800x600]
|
||||
PaintableWithLines (BlockContainer<HTML>) [0,0 800x216]
|
||||
PaintableWithLines (BlockContainer<BODY>) [8,8 784x200]
|
||||
PaintableWithLines (BlockContainer<DIV>.a) [8,8 500x200]
|
||||
PaintableWithLines (BlockContainer<DIV>.b) [8,8 200x150]
|
||||
PaintableWithLines (BlockContainer<DIV>.c) [308,8 200x100]
|
||||
PaintableWithLines (BlockContainer<DIV>.d) [308,108 200x100]
|
||||
PaintableWithLines (BlockContainer(anonymous)) [8,208 784x0]
|
|
@ -0,0 +1,29 @@
|
|||
<!DOCTYPE html>
|
||||
<style>
|
||||
.a {
|
||||
font-size: 5px;
|
||||
width: 100px;
|
||||
}
|
||||
.b {
|
||||
background-color: blue;
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
}
|
||||
.c {
|
||||
background-color: yellow;
|
||||
height: 300px;
|
||||
width: 30px;
|
||||
}
|
||||
.l {
|
||||
float: left;
|
||||
}
|
||||
.r {
|
||||
float: right;
|
||||
}
|
||||
</style>
|
||||
<div class="a">
|
||||
H
|
||||
<div class="b l"></div>
|
||||
<div class="c l"></div>
|
||||
<div class="c r"></div>
|
||||
</div>
|
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html>
|
||||
<style>
|
||||
.a {
|
||||
height: 200px;
|
||||
width: 500px;
|
||||
}
|
||||
.b {
|
||||
float: left;
|
||||
background: red;
|
||||
height: 150px;
|
||||
width: 200px;
|
||||
}
|
||||
.c {
|
||||
float: right;
|
||||
background: green;
|
||||
height: 100px;
|
||||
width: 200px;
|
||||
}
|
||||
.d {
|
||||
float: right;
|
||||
background: blue;
|
||||
height: 100px;
|
||||
width: 200px;
|
||||
}
|
||||
</style>
|
||||
<div class="a">
|
||||
<div class="b"></div>
|
||||
<div class="c"></div>
|
||||
<div class="d"></div>
|
||||
</div>
|
Loading…
Add table
Reference in a new issue