mirror of
https://github.com/SerenityOS/serenity
synced 2024-09-06 17:06:31 +00:00
LibWeb: Support more kinds of indefinite widths on flex column items
This stuff is pretty hairy since the specifications don't give any guidance on which widths to use when calculating the intrinsic height of flex items in a column layout. However, our old behavior of "treat anything indefinite as fit-content" was definitely not good enough, so this patch improves the situation by considering values like `min-content`, `max-content` and `fit-content` separately from `auto`, and making the whole flex layout pipeline aware of them (in the cross axis context).
This commit is contained in:
parent
17b363b596
commit
41e7c5766e
|
@ -0,0 +1,77 @@
|
|||
Viewport <#document> at (0,0) content-size 800x600 children: not-inline
|
||||
BlockContainer <html> at (1,1) content-size 798x142.8125 [BFC] children: not-inline
|
||||
Box <body> at (10,10) content-size 780x124.8125 flex-container(column) [FFC] children: not-inline
|
||||
BlockContainer <(anonymous)> (not painted) [BFC] children: inline
|
||||
TextNode <#text>
|
||||
BlockContainer <div.px> at (11,11) content-size 200x19.46875 flex-item [BFC] children: not-inline
|
||||
Box <div.inner> at (12,12) content-size 198x17.46875 flex-container(row) [FFC] children: not-inline
|
||||
BlockContainer <(anonymous)> at (12,12) content-size 19.125x17.46875 flex-item [BFC] children: inline
|
||||
line 0 width: 19.125, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||
frag 0 from TextNode start: 0, length: 2, rect: [12,12 19.125x17.46875]
|
||||
"px"
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> (not painted) [BFC] children: inline
|
||||
TextNode <#text>
|
||||
BlockContainer <div.percentage> at (11,32.46875) content-size 50x19.46875 flex-item [BFC] children: not-inline
|
||||
Box <div.inner> at (12,33.46875) content-size 48x17.46875 flex-container(row) [FFC] children: not-inline
|
||||
BlockContainer <(anonymous)> at (12,33.46875) content-size 86.671875x17.46875 flex-item [BFC] children: inline
|
||||
line 0 width: 86.671875, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||
frag 0 from TextNode start: 0, length: 10, rect: [12,33.46875 86.671875x17.46875]
|
||||
"percentage"
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> (not painted) [BFC] children: inline
|
||||
TextNode <#text>
|
||||
BlockContainer <div.fit-content> at (11,53.9375) content-size 88.765625x19.46875 flex-item [BFC] children: not-inline
|
||||
Box <div.inner> at (12,54.9375) content-size 86.765625x17.46875 flex-container(row) [FFC] children: not-inline
|
||||
BlockContainer <(anonymous)> at (12,54.9375) content-size 86.765625x17.46875 flex-item [BFC] children: inline
|
||||
line 0 width: 86.765625, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||
frag 0 from TextNode start: 0, length: 11, rect: [12,54.9375 86.765625x17.46875]
|
||||
"fit content"
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> (not painted) [BFC] children: inline
|
||||
TextNode <#text>
|
||||
BlockContainer <div.max-content> at (11,75.40625) content-size 102.15625x19.46875 flex-item [BFC] children: not-inline
|
||||
Box <div.inner> at (12,76.40625) content-size 100.15625x17.46875 flex-container(row) [FFC] children: not-inline
|
||||
BlockContainer <(anonymous)> at (12,76.40625) content-size 100.15625x17.46875 flex-item [BFC] children: inline
|
||||
line 0 width: 100.15625, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||
frag 0 from TextNode start: 0, length: 11, rect: [12,76.40625 100.15625x17.46875]
|
||||
"max content"
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> (not painted) [BFC] children: inline
|
||||
TextNode <#text>
|
||||
BlockContainer <div.min-content> at (11,96.875) content-size 62.90625x36.9375 flex-item [BFC] children: not-inline
|
||||
Box <div.inner> at (12,97.875) content-size 60.90625x34.9375 flex-container(row) [FFC] children: not-inline
|
||||
BlockContainer <(anonymous)> at (12,97.875) content-size 60.90625x34.9375 flex-item [BFC] children: inline
|
||||
line 0 width: 26.375, height: 17.46875, bottom: 17.46875, baseline: 13.53125
|
||||
frag 0 from TextNode start: 0, length: 3, rect: [12,97.875 26.375x17.46875]
|
||||
"min"
|
||||
line 1 width: 60.90625, height: 17.46875, bottom: 34.9375, baseline: 13.53125
|
||||
frag 0 from TextNode start: 4, length: 7, rect: [12,115.34375 60.90625x17.46875]
|
||||
"content"
|
||||
TextNode <#text>
|
||||
BlockContainer <(anonymous)> (not painted) [BFC] children: inline
|
||||
TextNode <#text>
|
||||
|
||||
PaintableWithLines (Viewport<#document>) [0,0 800x600]
|
||||
PaintableWithLines (BlockContainer<HTML>) [0,0 800x144.8125]
|
||||
PaintableBox (Box<BODY>) [9,9 782x126.8125]
|
||||
PaintableWithLines (BlockContainer<DIV>.px) [10,10 202x21.46875]
|
||||
PaintableBox (Box<DIV>.inner) [11,11 200x19.46875]
|
||||
PaintableWithLines (BlockContainer(anonymous)) [12,12 19.125x17.46875]
|
||||
TextPaintable (TextNode<#text>)
|
||||
PaintableWithLines (BlockContainer<DIV>.percentage) [10,31.46875 52x21.46875] overflow: [11,32.46875 87.671875x19.46875]
|
||||
PaintableBox (Box<DIV>.inner) [11,32.46875 50x19.46875] overflow: [12,33.46875 86.671875x17.46875]
|
||||
PaintableWithLines (BlockContainer(anonymous)) [12,33.46875 86.671875x17.46875]
|
||||
TextPaintable (TextNode<#text>)
|
||||
PaintableWithLines (BlockContainer<DIV>.fit-content) [10,52.9375 90.765625x21.46875]
|
||||
PaintableBox (Box<DIV>.inner) [11,53.9375 88.765625x19.46875]
|
||||
PaintableWithLines (BlockContainer(anonymous)) [12,54.9375 86.765625x17.46875]
|
||||
TextPaintable (TextNode<#text>)
|
||||
PaintableWithLines (BlockContainer<DIV>.max-content) [10,74.40625 104.15625x21.46875]
|
||||
PaintableBox (Box<DIV>.inner) [11,75.40625 102.15625x19.46875]
|
||||
PaintableWithLines (BlockContainer(anonymous)) [12,76.40625 100.15625x17.46875]
|
||||
TextPaintable (TextNode<#text>)
|
||||
PaintableWithLines (BlockContainer<DIV>.min-content) [10,95.875 64.90625x38.9375]
|
||||
PaintableBox (Box<DIV>.inner) [11,96.875 62.90625x36.9375]
|
||||
PaintableWithLines (BlockContainer(anonymous)) [12,97.875 60.90625x34.9375]
|
||||
TextPaintable (TextNode<#text>)
|
|
@ -0,0 +1,19 @@
|
|||
<!doctype html><style>
|
||||
* { border: 1px solid black; }
|
||||
body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.auto { width: auto; }
|
||||
.px { width: 200px; }
|
||||
.percentage { width: 50px; }
|
||||
.max-content { width: max-content; }
|
||||
.min-content { width: min-content; }
|
||||
.fit-content { width: fit-content; }
|
||||
.inner { display: flex; }
|
||||
</style><body>
|
||||
<div class="px"><div class="inner">px</div></div>
|
||||
<div class="percentage"><div class="inner">percentage</div></div>
|
||||
<div class="fit-content"><div class="inner">fit content</div></div>
|
||||
<div class="max-content"><div class="inner">max content</div></div>
|
||||
<div class="min-content"><div class="inner">min content</div></div>
|
|
@ -665,13 +665,12 @@ void FlexFormattingContext::determine_flex_base_size_and_hypothetical_main_size(
|
|||
// NOTE: This is one of many situations where that causes trouble: if this is a flex column layout,
|
||||
// we may need to calculate the intrinsic height of a flex item. This requires a width, but a
|
||||
// width won't be determined until later on in the flex layout algorithm.
|
||||
// In the specific case above (E), the spec mentions using `fit-content` if "a cross size is
|
||||
// needed to determine the main size", so that's exactly what we do.
|
||||
// In the specific case above (E), the spec mentions using `fit-content` in place of `auto`
|
||||
// if "a cross size is needed to determine the main size", so that's exactly what we do.
|
||||
|
||||
// NOTE: Substituting the fit-content size actually happens elsewhere, in the various helpers that
|
||||
// calculate the intrinsic sizes of a flex item, e.g. calculate_min_content_main_size().
|
||||
// This means that *all* intrinsic heights computed within a flex formatting context will
|
||||
// automatically use the fit-content width in case a used width is not known yet.
|
||||
// NOTE: Finding a suitable width for intrinsic height determination actually happens elsewhere,
|
||||
// in the various helpers that calculate the intrinsic sizes of a flex item,
|
||||
// e.g. calculate_min_content_main_size().
|
||||
|
||||
if (item.used_flex_basis->has<CSS::FlexBasisContent>()) {
|
||||
return calculate_max_content_main_size(item);
|
||||
|
@ -1138,14 +1137,29 @@ void FlexFormattingContext::determine_hypothetical_cross_size_of_item(FlexItem&
|
|||
return;
|
||||
}
|
||||
|
||||
if (should_treat_cross_size_as_auto(item.box)) {
|
||||
// Item has automatic cross size, layout with "fit-content"
|
||||
if (item.box->has_preferred_aspect_ratio() && item.main_size.has_value()) {
|
||||
item.hypothetical_cross_size = calculate_cross_size_from_main_size_and_aspect_ratio(item.main_size.value(), item.box->preferred_aspect_ratio().value());
|
||||
return;
|
||||
}
|
||||
|
||||
if (item.box->has_preferred_aspect_ratio() && item.main_size.has_value()) {
|
||||
item.hypothetical_cross_size = calculate_cross_size_from_main_size_and_aspect_ratio(item.main_size.value(), item.box->preferred_aspect_ratio().value());
|
||||
return;
|
||||
}
|
||||
auto computed_cross_size = [&]() -> CSS::Size {
|
||||
// "... treating auto as fit-content"
|
||||
if (should_treat_cross_size_as_auto(item.box))
|
||||
return CSS::Size::make_fit_content();
|
||||
return this->computed_cross_size(item.box);
|
||||
}();
|
||||
|
||||
if (computed_cross_size.is_min_content()) {
|
||||
item.hypothetical_cross_size = css_clamp(calculate_min_content_cross_size(item), clamp_min, clamp_max);
|
||||
return;
|
||||
}
|
||||
|
||||
if (computed_cross_size.is_max_content()) {
|
||||
item.hypothetical_cross_size = css_clamp(calculate_max_content_cross_size(item), clamp_min, clamp_max);
|
||||
return;
|
||||
}
|
||||
|
||||
if (computed_cross_size.is_fit_content()) {
|
||||
CSSPixels fit_content_cross_size = 0;
|
||||
if (is_row_layout()) {
|
||||
auto available_width = item.main_size.has_value() ? AvailableSize::make_definite(item.main_size.value()) : AvailableSize::make_indefinite();
|
||||
|
@ -1969,14 +1983,24 @@ CSSPixels FlexFormattingContext::calculate_cross_max_content_contribution(FlexIt
|
|||
return item.add_cross_margin_box_sizes(clamped_inner_size);
|
||||
}
|
||||
|
||||
CSSPixels FlexFormattingContext::calculate_clamped_fit_content_width(Box const& box, AvailableSpace const& available_space) const
|
||||
CSSPixels FlexFormattingContext::calculate_width_to_use_when_determining_intrinsic_height_of_item(FlexItem const& item) const
|
||||
{
|
||||
auto const& computed_min_size = box.computed_values().min_width();
|
||||
auto const& computed_max_size = box.computed_values().max_width();
|
||||
auto clamp_min = (!computed_min_size.is_auto() && (!computed_min_size.contains_percentage())) ? specified_cross_min_size(box) : 0;
|
||||
auto clamp_max = (!computed_max_size.is_none() && (!computed_max_size.contains_percentage())) ? specified_cross_max_size(box) : NumericLimits<float>::max();
|
||||
auto size = FormattingContext::calculate_fit_content_width(box, available_space);
|
||||
return css_clamp(size, clamp_min, clamp_max);
|
||||
auto const& box = *item.box;
|
||||
auto computed_width = box.computed_values().width();
|
||||
auto const& computed_min_width = box.computed_values().min_width();
|
||||
auto const& computed_max_width = box.computed_values().max_width();
|
||||
auto clamp_min = (!computed_min_width.is_auto() && (!computed_min_width.contains_percentage())) ? specified_cross_min_size(box) : 0;
|
||||
auto clamp_max = (!computed_max_width.is_none() && (!computed_max_width.contains_percentage())) ? specified_cross_max_size(box) : NumericLimits<float>::max();
|
||||
|
||||
CSSPixels width;
|
||||
if (should_treat_width_as_auto(box, m_available_space_for_items->space) || computed_width.is_fit_content())
|
||||
width = calculate_fit_content_width(box, m_available_space_for_items->space);
|
||||
else if (computed_width.is_min_content())
|
||||
width = calculate_min_content_width(box);
|
||||
else if (computed_width.is_max_content())
|
||||
width = calculate_max_content_width(box);
|
||||
|
||||
return css_clamp(width, clamp_min, clamp_max);
|
||||
}
|
||||
|
||||
CSSPixels FlexFormattingContext::calculate_min_content_main_size(FlexItem const& item) const
|
||||
|
@ -1986,7 +2010,7 @@ CSSPixels FlexFormattingContext::calculate_min_content_main_size(FlexItem const&
|
|||
}
|
||||
auto available_space = m_state.get(item.box).available_inner_space_or_constraints_from(m_available_space_for_items->space);
|
||||
if (available_space.width.is_indefinite()) {
|
||||
available_space.width = AvailableSize::make_definite(calculate_clamped_fit_content_width(item.box, m_available_space_for_items->space));
|
||||
available_space.width = AvailableSize::make_definite(calculate_width_to_use_when_determining_intrinsic_height_of_item(item));
|
||||
}
|
||||
return calculate_min_content_height(item.box, available_space.width);
|
||||
}
|
||||
|
@ -1998,7 +2022,7 @@ CSSPixels FlexFormattingContext::calculate_max_content_main_size(FlexItem const&
|
|||
}
|
||||
auto available_space = m_state.get(item.box).available_inner_space_or_constraints_from(m_available_space_for_items->space);
|
||||
if (available_space.width.is_indefinite()) {
|
||||
available_space.width = AvailableSize::make_definite(calculate_clamped_fit_content_width(item.box, m_available_space_for_items->space));
|
||||
available_space.width = AvailableSize::make_definite(calculate_width_to_use_when_determining_intrinsic_height_of_item(item));
|
||||
}
|
||||
return calculate_max_content_height(item.box, available_space.width);
|
||||
}
|
||||
|
@ -2022,7 +2046,7 @@ CSSPixels FlexFormattingContext::calculate_min_content_cross_size(FlexItem const
|
|||
if (is_row_layout()) {
|
||||
auto available_space = m_state.get(item.box).available_inner_space_or_constraints_from(m_available_space_for_flex_container->space);
|
||||
if (available_space.width.is_indefinite()) {
|
||||
available_space.width = AvailableSize::make_definite(calculate_clamped_fit_content_width(item.box, m_available_space_for_items->space));
|
||||
available_space.width = AvailableSize::make_definite(calculate_width_to_use_when_determining_intrinsic_height_of_item(item));
|
||||
}
|
||||
return calculate_min_content_height(item.box, available_space.width);
|
||||
}
|
||||
|
@ -2034,7 +2058,7 @@ CSSPixels FlexFormattingContext::calculate_max_content_cross_size(FlexItem const
|
|||
if (is_row_layout()) {
|
||||
auto available_space = m_state.get(item.box).available_inner_space_or_constraints_from(m_available_space_for_flex_container->space);
|
||||
if (available_space.width.is_indefinite()) {
|
||||
available_space.width = AvailableSize::make_definite(calculate_clamped_fit_content_width(item.box, m_available_space_for_items->space));
|
||||
available_space.width = AvailableSize::make_definite(calculate_width_to_use_when_determining_intrinsic_height_of_item(item));
|
||||
}
|
||||
return calculate_max_content_height(item.box, available_space.width);
|
||||
}
|
||||
|
|
|
@ -211,7 +211,7 @@ private:
|
|||
[[nodiscard]] CSSPixels calculate_fit_content_main_size(FlexItem const&) const;
|
||||
[[nodiscard]] CSSPixels calculate_fit_content_cross_size(FlexItem const&) const;
|
||||
|
||||
CSSPixels calculate_clamped_fit_content_width(Box const&, AvailableSpace const&) const;
|
||||
[[nodiscard]] CSSPixels calculate_width_to_use_when_determining_intrinsic_height_of_item(FlexItem const&) const;
|
||||
|
||||
virtual void parent_context_did_dimension_child_root_box() override;
|
||||
|
||||
|
|
Loading…
Reference in a new issue