layout: Add support for flex items with position: relative (#33151)

Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
Martin Robinson 2024-08-23 11:11:22 -07:00 committed by GitHub
parent b9f02cf773
commit 2db9032e72
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 207 additions and 81 deletions

View file

@ -8,6 +8,7 @@ use std::cmp::Ordering;
use app_units::Au;
use atomic_refcell::AtomicRefMut;
use itertools::izip;
use style::computed_values::position::T as Position;
use style::logical_geometry::WritingMode;
use style::properties::longhands::align_items::computed_value::T as AlignItems;
use style::properties::longhands::align_self::computed_value::T as AlignSelf;
@ -29,7 +30,9 @@ use crate::context::LayoutContext;
use crate::formatting_contexts::{Baselines, IndependentFormattingContext, IndependentLayout};
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags};
use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2};
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext, PositioningContextLength};
use crate::positioned::{
relative_adjustement, AbsolutelyPositionedBox, PositioningContext, PositioningContextLength,
};
use crate::sizing::{ContentSizes, IntrinsicSizingMode};
use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin};
use crate::ContainingBlock;
@ -101,6 +104,109 @@ impl FlexItemLayoutResult {
self.baseline_relative_to_margin_box
.unwrap_or_else(|| item.synthesized_baseline_relative_to_margin_box(block_size))
}
fn collect_fragment(
mut self,
initial_flex_layout: &InitialFlexLineLayout,
item: &FlexItem,
item_used_size: FlexRelativeVec2<Au>,
item_margin: FlexRelativeSides<Au>,
item_main_interval: Au,
final_line_cross_size: Au,
shared_alignment_baseline: &Option<Au>,
flex_context: &mut FlexContext,
all_baselines: &mut Baselines,
main_position_cursor: &mut Au,
) -> (BoxFragment, PositioningContext) {
// https://drafts.csswg.org/css-flexbox/#algo-main-align
// “Align the items along the main-axis”
*main_position_cursor +=
item_margin.main_start + item.border.main_start + item.padding.main_start;
let item_content_main_start_position = *main_position_cursor;
*main_position_cursor += item_used_size.main +
item.padding.main_end +
item.border.main_end +
item_margin.main_end +
item_main_interval;
// https://drafts.csswg.org/css-flexbox/#algo-cross-align
let item_content_cross_start_position = item.align_along_cross_axis(
&item_margin,
&item_used_size.cross,
final_line_cross_size,
self.baseline_relative_to_margin_box.unwrap_or_default(),
shared_alignment_baseline.unwrap_or_default(),
flex_context.config.flex_wrap_reverse,
);
let start_corner = FlexRelativeVec2 {
main: item_content_main_start_position,
cross: item_content_cross_start_position,
};
// Need to collect both baselines from baseline participation and other baselines.
let final_line_size = FlexRelativeVec2 {
main: initial_flex_layout.line_size.main,
cross: final_line_cross_size,
};
let content_rect = flex_context.rect_to_flow_relative(
final_line_size,
FlexRelativeRect {
start_corner,
size: item_used_size,
},
);
let margin = flex_context.sides_to_flow_relative(item_margin);
let collapsed_margin = CollapsedBlockMargins::from_margin(&margin);
if let Some(item_baseline) = self.baseline_relative_to_margin_box.as_ref() {
let item_baseline = *item_baseline + item_content_cross_start_position -
item.border.cross_start -
item.padding.cross_start -
item_margin.cross_start;
all_baselines.first.get_or_insert(item_baseline);
all_baselines.last = Some(item_baseline);
}
let mut fragment_info = item.box_.base_fragment_info();
fragment_info.flags.insert(FragmentFlags::IS_FLEX_ITEM);
let flags = fragment_info.flags;
let containing_block = flex_context.containing_block;
let container_writing_mode = containing_block.effective_writing_mode();
let style = item.box_.style();
let mut fragment = BoxFragment::new(
fragment_info,
style.clone(),
self.fragments,
content_rect.to_physical(container_writing_mode),
flex_context
.sides_to_flow_relative(item.padding)
.to_physical(container_writing_mode),
flex_context
.sides_to_flow_relative(item.border)
.to_physical(container_writing_mode),
margin.to_physical(container_writing_mode),
None, /* clearance */
collapsed_margin,
);
// If this flex item establishes a containing block for absolutely-positioned
// descendants, then lay out any relevant absolutely-positioned children. This
// will remove those children from `self.positioning_context`.
if style.establishes_containing_block_for_absolute_descendants(flags) {
self.positioning_context
.layout_collected_children(flex_context.layout_context, &mut fragment);
}
if style.clone_position() == Position::Relative {
fragment.content_rect.origin += relative_adjustement(style, containing_block)
.to_physical_size(containing_block.effective_writing_mode())
}
(fragment, self.positioning_context)
}
}
struct InitialFlexLineLayout<'a> {
@ -1408,91 +1514,33 @@ impl InitialFlexLineLayout<'_> {
let mut all_baselines = Baselines::default();
let mut main_position_cursor = main_start_position;
let items = std::mem::take(&mut self.items);
let item_layout_results = std::mem::take(&mut self.item_layout_results);
let item_fragments = izip!(
self.items.iter(),
items,
item_margins,
self.item_used_main_sizes.iter(),
item_used_cross_sizes.iter(),
self.item_layout_results.into_iter()
item_layout_results.into_iter(),
)
.map(
|(item, item_margin, item_used_main_size, item_used_cross_size, item_layout_result)| {
// https://drafts.csswg.org/css-flexbox/#algo-main-align
// “Align the items along the main-axis”
main_position_cursor +=
item_margin.main_start + item.border.main_start + item.padding.main_start;
let item_content_main_start_position = main_position_cursor;
main_position_cursor += *item_used_main_size +
item.padding.main_end +
item.border.main_end +
item_margin.main_end +
item_main_interval;
// https://drafts.csswg.org/css-flexbox/#algo-cross-align
let item_content_cross_start_position = item.align_along_cross_axis(
&item_margin,
item_used_cross_size,
final_line_cross_size,
item_layout_result
.baseline_relative_to_margin_box
.unwrap_or_default(),
shared_alignment_baseline.unwrap_or_default(),
flex_context.config.flex_wrap_reverse,
);
let start_corner = FlexRelativeVec2 {
main: item_content_main_start_position,
cross: item_content_cross_start_position,
};
let size = FlexRelativeVec2 {
let item_used_size = FlexRelativeVec2 {
main: *item_used_main_size,
cross: *item_used_cross_size,
};
// Need to collect both baselines from baseline participation and other baselines.
let final_line_size = FlexRelativeVec2 {
main: self.line_size.main,
cross: final_line_cross_size,
};
let content_rect = flex_context.rect_to_flow_relative(
final_line_size,
FlexRelativeRect { start_corner, size },
);
let margin = flex_context.sides_to_flow_relative(item_margin);
let collapsed_margin = CollapsedBlockMargins::from_margin(&margin);
if let Some(item_baseline) =
item_layout_result.baseline_relative_to_margin_box.as_ref()
{
let item_baseline = *item_baseline + item_content_cross_start_position -
item.border.cross_start -
item.padding.cross_start -
item_margin.cross_start;
all_baselines.first.get_or_insert(item_baseline);
all_baselines.last = Some(item_baseline);
}
let mut fragment_info = item.box_.base_fragment_info();
fragment_info.flags.insert(FragmentFlags::IS_FLEX_ITEM);
let container_writing_mode = flex_context.containing_block.effective_writing_mode();
(
BoxFragment::new(
fragment_info,
item.box_.style().clone(),
item_layout_result.fragments,
content_rect.to_physical(container_writing_mode),
flex_context
.sides_to_flow_relative(item.padding)
.to_physical(container_writing_mode),
flex_context
.sides_to_flow_relative(item.border)
.to_physical(container_writing_mode),
margin.to_physical(container_writing_mode),
None, /* clearance */
collapsed_margin,
),
item_layout_result.positioning_context,
item_layout_result.collect_fragment(
&self,
item,
item_used_size,
item_margin,
item_main_interval,
final_line_cross_size,
&shared_alignment_baseline,
flex_context,
&mut all_baselines,
&mut main_position_cursor,
)
},
)
@ -1519,11 +1567,14 @@ impl FlexItem<'_> {
used_cross_size_override: Option<Au>,
) -> FlexItemLayoutResult {
let containing_block = &flex_context.containing_block;
let mut positioning_context = PositioningContext::new_for_subtree(
flex_context
.positioning_context
.collects_for_nearest_positioned_ancestor(),
);
let mut positioning_context = PositioningContext::new_for_style(self.box_.style())
.unwrap_or_else(|| {
PositioningContext::new_for_subtree(
flex_context
.positioning_context
.collects_for_nearest_positioned_ancestor(),
)
});
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
let container_writing_mode = containing_block.effective_writing_mode();

View file

@ -167867,6 +167867,19 @@
{}
]
],
"flex-item-position-relative-001.html": [
"7e6907e0e8d4801776d581474a29d5f5b5743b53",
[
null,
[
[
"/css/css-flexbox/flex-item-position-relative-001-ref.html",
"=="
]
],
{}
]
],
"flex-item-transferred-sizes-padding-border-sizing.html": [
"fc1a163e01ee1eaca8186619aee3e8d0e25c3155",
[
@ -400930,6 +400943,10 @@
"2b0f294c3441f1cf25a6678b9ce0748258582ea1",
[]
],
"flex-item-position-relative-001-ref.html": [
"01e078d983d1319199a9356e2da54278009a7418",
[]
],
"flex-lines": {
"multi-line-wrap-reverse-column-reverse-ref.html": [
"38366a62f7e6b7df30b2da814cf178ebba5a83fa",

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<title>Flex items and `position: relative`</title>
<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
<link rel="help" href="https://drafts.csswg.org/css-position/#position-property">
<meta name="assert"
content="Flex items can be positioned with `position: relative` and establish containing blocks for absolutely-positioned descendants." />
<style>
body {
margin: 0;
}
</style>
<div style="position: absolute; background: green; width: 50px; height: 50px; left: 50px; top: 50px"></div>
<div style="position: absolute; background: green; width: 50px; height:50px; left: 100px; top: 100px"></div>

View file

@ -0,0 +1,42 @@
<!DOCTYPE html>
<title>Flex items and `position: relative`</title>
<link rel="author" title="Martin Robinson" href="mailto:mrobinson@igalia.com">
<link rel="help" href="https://drafts.csswg.org/css-position/#position-property">
<meta name="assert"
content="Flex items can be positioned with `position: relative` and establish containing blocks for absolutely-positioned descendants." />
<link rel="match" href="flex-item-position-relative-001-ref.html">
<style>
body {
margin: 0;
}
#flex {
display: flex;
width: 100px;
height: 100px;
}
#flex > div {
width: 50px;
height: 50px;
background: green;
position: relative;
top: 50px;
left: 50px;
}
#flex > div > div {
width: 50px;
height: 50px;
background: green;
position: absolute;
left: 100%;
top: 100%;
}
</style>
<div style="position: absolute; background: red; width: 50px; height: 50px; left: 50px; top: 50px"></div>
<div style="position: absolute; background: red; width: 50px; height:50px; left: 100px; top: 100px"></div>
<div id="flex">
<div><div style="background: green"></div></div>
</div>