mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
layout: Add support for flex items with position: relative
(#33151)
Signed-off-by: Martin Robinson <mrobinson@igalia.com>
This commit is contained in:
parent
b9f02cf773
commit
2db9032e72
4 changed files with 207 additions and 81 deletions
|
@ -8,6 +8,7 @@ use std::cmp::Ordering;
|
||||||
use app_units::Au;
|
use app_units::Au;
|
||||||
use atomic_refcell::AtomicRefMut;
|
use atomic_refcell::AtomicRefMut;
|
||||||
use itertools::izip;
|
use itertools::izip;
|
||||||
|
use style::computed_values::position::T as Position;
|
||||||
use style::logical_geometry::WritingMode;
|
use style::logical_geometry::WritingMode;
|
||||||
use style::properties::longhands::align_items::computed_value::T as AlignItems;
|
use style::properties::longhands::align_items::computed_value::T as AlignItems;
|
||||||
use style::properties::longhands::align_self::computed_value::T as AlignSelf;
|
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::formatting_contexts::{Baselines, IndependentFormattingContext, IndependentLayout};
|
||||||
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags};
|
use crate::fragment_tree::{BoxFragment, CollapsedBlockMargins, Fragment, FragmentFlags};
|
||||||
use crate::geom::{AuOrAuto, LogicalRect, LogicalSides, LogicalVec2};
|
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::sizing::{ContentSizes, IntrinsicSizingMode};
|
||||||
use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin};
|
use crate::style_ext::{Clamp, ComputedValuesExt, PaddingBorderMargin};
|
||||||
use crate::ContainingBlock;
|
use crate::ContainingBlock;
|
||||||
|
@ -101,6 +104,109 @@ impl FlexItemLayoutResult {
|
||||||
self.baseline_relative_to_margin_box
|
self.baseline_relative_to_margin_box
|
||||||
.unwrap_or_else(|| item.synthesized_baseline_relative_to_margin_box(block_size))
|
.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> {
|
struct InitialFlexLineLayout<'a> {
|
||||||
|
@ -1408,91 +1514,33 @@ impl InitialFlexLineLayout<'_> {
|
||||||
|
|
||||||
let mut all_baselines = Baselines::default();
|
let mut all_baselines = Baselines::default();
|
||||||
let mut main_position_cursor = main_start_position;
|
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!(
|
let item_fragments = izip!(
|
||||||
self.items.iter(),
|
items,
|
||||||
item_margins,
|
item_margins,
|
||||||
self.item_used_main_sizes.iter(),
|
self.item_used_main_sizes.iter(),
|
||||||
item_used_cross_sizes.iter(),
|
item_used_cross_sizes.iter(),
|
||||||
self.item_layout_results.into_iter()
|
item_layout_results.into_iter(),
|
||||||
)
|
)
|
||||||
.map(
|
.map(
|
||||||
|(item, item_margin, item_used_main_size, item_used_cross_size, item_layout_result)| {
|
|(item, item_margin, item_used_main_size, item_used_cross_size, item_layout_result)| {
|
||||||
// https://drafts.csswg.org/css-flexbox/#algo-main-align
|
let item_used_size = FlexRelativeVec2 {
|
||||||
// “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 {
|
|
||||||
main: *item_used_main_size,
|
main: *item_used_main_size,
|
||||||
cross: *item_used_cross_size,
|
cross: *item_used_cross_size,
|
||||||
};
|
};
|
||||||
|
item_layout_result.collect_fragment(
|
||||||
// Need to collect both baselines from baseline participation and other baselines.
|
&self,
|
||||||
let final_line_size = FlexRelativeVec2 {
|
item,
|
||||||
main: self.line_size.main,
|
item_used_size,
|
||||||
cross: final_line_cross_size,
|
item_margin,
|
||||||
};
|
item_main_interval,
|
||||||
let content_rect = flex_context.rect_to_flow_relative(
|
final_line_cross_size,
|
||||||
final_line_size,
|
&shared_alignment_baseline,
|
||||||
FlexRelativeRect { start_corner, size },
|
flex_context,
|
||||||
);
|
&mut all_baselines,
|
||||||
let margin = flex_context.sides_to_flow_relative(item_margin);
|
&mut main_position_cursor,
|
||||||
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,
|
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
@ -1519,11 +1567,14 @@ impl FlexItem<'_> {
|
||||||
used_cross_size_override: Option<Au>,
|
used_cross_size_override: Option<Au>,
|
||||||
) -> FlexItemLayoutResult {
|
) -> FlexItemLayoutResult {
|
||||||
let containing_block = &flex_context.containing_block;
|
let containing_block = &flex_context.containing_block;
|
||||||
let mut positioning_context = PositioningContext::new_for_subtree(
|
let mut positioning_context = PositioningContext::new_for_style(self.box_.style())
|
||||||
flex_context
|
.unwrap_or_else(|| {
|
||||||
.positioning_context
|
PositioningContext::new_for_subtree(
|
||||||
.collects_for_nearest_positioned_ancestor(),
|
flex_context
|
||||||
);
|
.positioning_context
|
||||||
|
.collects_for_nearest_positioned_ancestor(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
|
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
|
||||||
let container_writing_mode = containing_block.effective_writing_mode();
|
let container_writing_mode = containing_block.effective_writing_mode();
|
||||||
|
|
17
tests/wpt/meta/MANIFEST.json
vendored
17
tests/wpt/meta/MANIFEST.json
vendored
|
@ -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": [
|
"flex-item-transferred-sizes-padding-border-sizing.html": [
|
||||||
"fc1a163e01ee1eaca8186619aee3e8d0e25c3155",
|
"fc1a163e01ee1eaca8186619aee3e8d0e25c3155",
|
||||||
[
|
[
|
||||||
|
@ -400930,6 +400943,10 @@
|
||||||
"2b0f294c3441f1cf25a6678b9ce0748258582ea1",
|
"2b0f294c3441f1cf25a6678b9ce0748258582ea1",
|
||||||
[]
|
[]
|
||||||
],
|
],
|
||||||
|
"flex-item-position-relative-001-ref.html": [
|
||||||
|
"01e078d983d1319199a9356e2da54278009a7418",
|
||||||
|
[]
|
||||||
|
],
|
||||||
"flex-lines": {
|
"flex-lines": {
|
||||||
"multi-line-wrap-reverse-column-reverse-ref.html": [
|
"multi-line-wrap-reverse-column-reverse-ref.html": [
|
||||||
"38366a62f7e6b7df30b2da814cf178ebba5a83fa",
|
"38366a62f7e6b7df30b2da814cf178ebba5a83fa",
|
||||||
|
|
16
tests/wpt/tests/css/css-flexbox/flex-item-position-relative-001-ref.html
vendored
Normal file
16
tests/wpt/tests/css/css-flexbox/flex-item-position-relative-001-ref.html
vendored
Normal 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>
|
42
tests/wpt/tests/css/css-flexbox/flex-item-position-relative-001.html
vendored
Normal file
42
tests/wpt/tests/css/css-flexbox/flex-item-position-relative-001.html
vendored
Normal 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>
|
Loading…
Add table
Add a link
Reference in a new issue