mirror of
https://github.com/servo/servo.git
synced 2025-08-06 14:10:11 +01:00
layout: Reduce the complexity of FlexLine::layout
(#32810)
Instead of a complex combination of iterators, use a flatter iteration design when laying out a flex line. Signed-off-by: Martin Robinson <mrobinson@igalia.com> Co-authored-by: Mukilan Thiyagarajan <mukilan@igalia.com> Co-authored-by: Delan Azabani <dazabani@igalia.com>
This commit is contained in:
parent
8b3c9b744a
commit
5eb77592ea
3 changed files with 121 additions and 151 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -3406,6 +3406,7 @@ dependencies = [
|
||||||
"html5ever",
|
"html5ever",
|
||||||
"icu_segmenter",
|
"icu_segmenter",
|
||||||
"ipc-channel",
|
"ipc-channel",
|
||||||
|
"itertools 0.13.0",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"log",
|
"log",
|
||||||
"net_traits",
|
"net_traits",
|
||||||
|
|
|
@ -27,6 +27,7 @@ fonts_traits = { workspace = true }
|
||||||
html5ever = { workspace = true }
|
html5ever = { workspace = true }
|
||||||
icu_segmenter = { workspace = true }
|
icu_segmenter = { workspace = true }
|
||||||
ipc-channel = { workspace = true }
|
ipc-channel = { workspace = true }
|
||||||
|
itertools = { workspace = true }
|
||||||
log = { workspace = true }
|
log = { workspace = true }
|
||||||
net_traits = { workspace = true }
|
net_traits = { workspace = true }
|
||||||
parking_lot = { workspace = true }
|
parking_lot = { workspace = true }
|
||||||
|
|
|
@ -7,6 +7,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 style::properties::longhands::align_content::computed_value::T as AlignContent;
|
use style::properties::longhands::align_content::computed_value::T as AlignContent;
|
||||||
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;
|
||||||
|
@ -804,7 +805,7 @@ impl FlexLine<'_> {
|
||||||
self.resolve_flexible_lengths(container_main_size);
|
self.resolve_flexible_lengths(container_main_size);
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-flexbox/#algo-cross-item
|
// https://drafts.csswg.org/css-flexbox/#algo-cross-item
|
||||||
let item_layout_results = self
|
let mut item_layout_results = self
|
||||||
.items
|
.items
|
||||||
.iter_mut()
|
.iter_mut()
|
||||||
.zip(&item_used_main_sizes)
|
.zip(&item_used_main_sizes)
|
||||||
|
@ -828,33 +829,60 @@ impl FlexLine<'_> {
|
||||||
|
|
||||||
// Determine the used cross size of each flex item
|
// Determine the used cross size of each flex item
|
||||||
// https://drafts.csswg.org/css-flexbox/#algo-stretch
|
// https://drafts.csswg.org/css-flexbox/#algo-stretch
|
||||||
let (item_used_cross_sizes, item_results): (Vec<_>, Vec<_>) = self
|
let item_count = self.items.len();
|
||||||
.items
|
let mut max_baseline = None;
|
||||||
.iter_mut()
|
let mut item_used_cross_sizes = Vec::with_capacity(item_count);
|
||||||
.zip(item_layout_results)
|
let mut item_cross_margins = Vec::with_capacity(item_count);
|
||||||
.zip(&item_used_main_sizes)
|
for (item, item_layout_result, used_main_size) in izip!(
|
||||||
.map(|((item, mut item_result), &used_main_size)| {
|
self.items.iter_mut(),
|
||||||
let has_stretch = item.align_self.0.value() == AlignFlags::STRETCH;
|
item_layout_results.iter_mut(),
|
||||||
let cross_size = if has_stretch &&
|
&item_used_main_sizes
|
||||||
item.content_box_size.cross.is_auto() &&
|
) {
|
||||||
!(item.margin.cross_start.is_auto() || item.margin.cross_end.is_auto())
|
let has_stretch = item.align_self.0.value() == AlignFlags::STRETCH;
|
||||||
{
|
let used_cross_size = if has_stretch &&
|
||||||
(line_cross_size - item.pbm_auto_is_zero.cross).clamp_between_extremums(
|
item.content_box_size.cross.is_auto() &&
|
||||||
item.content_min_size.cross,
|
!(item.margin.cross_start.is_auto() || item.margin.cross_end.is_auto())
|
||||||
item.content_max_size.cross,
|
{
|
||||||
)
|
(line_cross_size - item.pbm_auto_is_zero.cross).clamp_between_extremums(
|
||||||
} else {
|
item.content_min_size.cross,
|
||||||
item_result.hypothetical_cross_size
|
item.content_max_size.cross,
|
||||||
};
|
)
|
||||||
if has_stretch {
|
} else {
|
||||||
// “If the flex item has `align-self: stretch`, redo layout for its contents,
|
item_layout_result.hypothetical_cross_size
|
||||||
// treating this used size as its definite cross size
|
};
|
||||||
// so that percentage-sized children can be resolved.”
|
item_used_cross_sizes.push(used_cross_size);
|
||||||
item_result = item.layout(used_main_size, flex_context, Some(cross_size));
|
|
||||||
}
|
if has_stretch {
|
||||||
(cross_size, item_result)
|
// “If the flex item has `align-self: stretch`, redo layout for its contents,
|
||||||
})
|
// treating this used size as its definite cross size
|
||||||
.unzip();
|
// so that percentage-sized children can be resolved.”
|
||||||
|
*item_layout_result =
|
||||||
|
item.layout(*used_main_size, flex_context, Some(used_cross_size));
|
||||||
|
}
|
||||||
|
|
||||||
|
item_layout_result.baseline_relative_to_margin_box = match item.align_self.0.value() {
|
||||||
|
AlignFlags::BASELINE | AlignFlags::LAST_BASELINE => {
|
||||||
|
let baseline = item_layout_result
|
||||||
|
.baseline_relative_to_margin_box
|
||||||
|
.unwrap_or_else(|| {
|
||||||
|
item.synthesized_baseline_relative_to_margin_box(used_cross_size)
|
||||||
|
});
|
||||||
|
max_baseline = Some(max_baseline.unwrap_or(baseline).max(baseline));
|
||||||
|
Some(baseline)
|
||||||
|
},
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
// https://drafts.csswg.org/css-flexbox/#algo-cross-margins
|
||||||
|
item_cross_margins.push(item.resolve_auto_cross_margins(
|
||||||
|
flex_context,
|
||||||
|
line_cross_size,
|
||||||
|
used_cross_size,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layout of items is over. These should no longer be mutable.
|
||||||
|
let item_layout_results = item_layout_results;
|
||||||
|
|
||||||
// Distribute any remaining free space
|
// Distribute any remaining free space
|
||||||
// https://drafts.csswg.org/css-flexbox/#algo-main-align
|
// https://drafts.csswg.org/css-flexbox/#algo-main-align
|
||||||
|
@ -865,7 +893,6 @@ impl FlexLine<'_> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Align the items along the main-axis per justify-content.
|
// Align the items along the main-axis per justify-content.
|
||||||
let item_count = self.items.len();
|
|
||||||
let layout_is_flex_reversed = flex_context.flex_direction_is_reversed;
|
let layout_is_flex_reversed = flex_context.flex_direction_is_reversed;
|
||||||
|
|
||||||
// Implement fallback alignment.
|
// Implement fallback alignment.
|
||||||
|
@ -945,107 +972,73 @@ impl FlexLine<'_> {
|
||||||
_ => Au::zero(),
|
_ => Au::zero(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-flexbox/#algo-cross-margins
|
let mut main_position_cursor = main_start_position;
|
||||||
let item_cross_margins = self.items.iter().zip(&item_used_cross_sizes).map(
|
let item_fragments = izip!(
|
||||||
|(item, &item_cross_content_size)| {
|
self.items.iter(),
|
||||||
item.resolve_auto_cross_margins(
|
item_main_margins,
|
||||||
flex_context,
|
item_cross_margins,
|
||||||
line_cross_size,
|
|
||||||
item_cross_content_size,
|
|
||||||
)
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
let item_margins = item_main_margins
|
|
||||||
.zip(item_cross_margins)
|
|
||||||
.map(
|
|
||||||
|((main_start, main_end), (cross_start, cross_end))| FlexRelativeSides {
|
|
||||||
main_start,
|
|
||||||
main_end,
|
|
||||||
cross_start,
|
|
||||||
cross_end,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
// https://drafts.csswg.org/css-flexbox/#algo-main-align
|
|
||||||
let items_content_main_start_positions = self.align_along_main_axis(
|
|
||||||
&item_used_main_sizes,
|
&item_used_main_sizes,
|
||||||
&item_margins,
|
&item_used_cross_sizes,
|
||||||
main_start_position,
|
item_layout_results.into_iter()
|
||||||
item_main_interval,
|
)
|
||||||
);
|
.map(
|
||||||
|
|(
|
||||||
|
item,
|
||||||
|
item_main_margins,
|
||||||
|
item_cross_margins,
|
||||||
|
item_used_main_size,
|
||||||
|
item_used_cross_size,
|
||||||
|
item_layout_result,
|
||||||
|
)| {
|
||||||
|
let item_margin = FlexRelativeSides {
|
||||||
|
main_start: item_main_margins.0,
|
||||||
|
main_end: item_main_margins.1,
|
||||||
|
cross_start: item_cross_margins.0,
|
||||||
|
cross_end: item_cross_margins.1,
|
||||||
|
};
|
||||||
|
|
||||||
// https://drafts.csswg.org/css-flexbox/#algo-cross-align
|
// https://drafts.csswg.org/css-flexbox/#algo-main-align
|
||||||
let item_propagated_baselines = item_results
|
// “Align the items along the main-axis”
|
||||||
.iter()
|
main_position_cursor +=
|
||||||
.zip(&item_used_cross_sizes)
|
item_margin.main_start + item.border.main_start + item.padding.main_start;
|
||||||
.zip(self.items.iter())
|
let item_content_main_start_position = main_position_cursor;
|
||||||
.map(|((layout_result, used_cross_size), item)| {
|
main_position_cursor += *item_used_main_size +
|
||||||
if matches!(
|
item.padding.main_end +
|
||||||
item.align_self.0.value(),
|
item.border.main_end +
|
||||||
AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
|
item_margin.main_end +
|
||||||
) {
|
item_main_interval;
|
||||||
Some(
|
|
||||||
layout_result
|
// https://drafts.csswg.org/css-flexbox/#algo-cross-align
|
||||||
.baseline_relative_to_margin_box
|
let item_content_cross_start_position = item.align_along_cross_axis(
|
||||||
.unwrap_or_else(|| {
|
&item_margin,
|
||||||
item.synthesized_baseline_relative_to_margin_box(*used_cross_size)
|
item_used_cross_size,
|
||||||
}),
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect::<Vec<_>>();
|
|
||||||
let max_propagated_baseline = item_propagated_baselines
|
|
||||||
.iter()
|
|
||||||
.copied()
|
|
||||||
.flatten()
|
|
||||||
.max()
|
|
||||||
.unwrap_or(Au::zero());
|
|
||||||
let item_content_cross_start_posititons = self
|
|
||||||
.items
|
|
||||||
.iter()
|
|
||||||
.zip(&item_margins)
|
|
||||||
.zip(&item_used_cross_sizes)
|
|
||||||
.zip(&item_propagated_baselines)
|
|
||||||
.map(|(((item, margin), size), propagated_baseline)| {
|
|
||||||
item.align_along_cross_axis(
|
|
||||||
margin,
|
|
||||||
size,
|
|
||||||
line_cross_size,
|
line_cross_size,
|
||||||
propagated_baseline.unwrap_or_default(),
|
item_layout_result
|
||||||
max_propagated_baseline,
|
.baseline_relative_to_margin_box
|
||||||
)
|
.unwrap_or_default(),
|
||||||
});
|
max_baseline.unwrap_or_default(),
|
||||||
|
);
|
||||||
|
|
||||||
let item_fragments = self
|
let start_corner = FlexRelativeVec2 {
|
||||||
.items
|
main: item_content_main_start_position,
|
||||||
.iter()
|
cross: item_content_cross_start_position,
|
||||||
.zip(item_results)
|
};
|
||||||
.zip(
|
let size = FlexRelativeVec2 {
|
||||||
item_used_main_sizes
|
main: *item_used_main_size,
|
||||||
.iter()
|
cross: *item_used_cross_size,
|
||||||
.zip(&item_used_cross_sizes)
|
};
|
||||||
.map(|(&main, &cross)| FlexRelativeVec2 { main, cross })
|
|
||||||
.zip(
|
let content_rect = flex_context
|
||||||
items_content_main_start_positions
|
.rect_to_flow_relative(line_size, FlexRelativeRect { start_corner, size });
|
||||||
.zip(item_content_cross_start_posititons)
|
let margin = flex_context.sides_to_flow_relative(item_margin);
|
||||||
.map(|(main, cross)| FlexRelativeVec2 { main, cross }),
|
|
||||||
)
|
|
||||||
.map(|(size, start_corner)| FlexRelativeRect { size, start_corner }),
|
|
||||||
)
|
|
||||||
.zip(&item_margins)
|
|
||||||
.map(|(((item, item_result), content_rect), margin)| {
|
|
||||||
let content_rect = flex_context.rect_to_flow_relative(line_size, content_rect);
|
|
||||||
let margin = flex_context.sides_to_flow_relative(*margin);
|
|
||||||
let collapsed_margin = CollapsedBlockMargins::from_margin(&margin);
|
let collapsed_margin = CollapsedBlockMargins::from_margin(&margin);
|
||||||
|
|
||||||
|
// TODO: We should likely propagate baselines from `display: flex`.
|
||||||
(
|
(
|
||||||
// TODO: We should likely propagate baselines from `display: flex`.
|
|
||||||
BoxFragment::new(
|
BoxFragment::new(
|
||||||
item.box_.base_fragment_info(),
|
item.box_.base_fragment_info(),
|
||||||
item.box_.style().clone(),
|
item.box_.style().clone(),
|
||||||
item_result.fragments,
|
item_layout_result.fragments,
|
||||||
content_rect,
|
content_rect,
|
||||||
flex_context.sides_to_flow_relative(item.padding),
|
flex_context.sides_to_flow_relative(item.padding),
|
||||||
flex_context.sides_to_flow_relative(item.border),
|
flex_context.sides_to_flow_relative(item.border),
|
||||||
|
@ -1053,10 +1046,12 @@ impl FlexLine<'_> {
|
||||||
None, /* clearance */
|
None, /* clearance */
|
||||||
collapsed_margin,
|
collapsed_margin,
|
||||||
),
|
),
|
||||||
item_result.positioning_context,
|
item_layout_result.positioning_context,
|
||||||
)
|
)
|
||||||
})
|
},
|
||||||
.collect();
|
)
|
||||||
|
.collect();
|
||||||
|
|
||||||
FlexLineLayoutResult {
|
FlexLineLayoutResult {
|
||||||
cross_size: line_cross_size,
|
cross_size: line_cross_size,
|
||||||
item_fragments,
|
item_fragments,
|
||||||
|
@ -1462,33 +1457,6 @@ impl<'items> FlexLine<'items> {
|
||||||
each_auto_margin > Au::zero(),
|
each_auto_margin > Au::zero(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the coordinate of the main-start side of the content area of each item
|
|
||||||
fn align_along_main_axis<'a>(
|
|
||||||
&'a self,
|
|
||||||
item_used_main_sizes: &'a [Au],
|
|
||||||
item_margins: &'a [FlexRelativeSides<Au>],
|
|
||||||
main_start_position: Au,
|
|
||||||
item_main_interval: Au,
|
|
||||||
) -> impl Iterator<Item = Au> + 'a {
|
|
||||||
// “Align the items along the main-axis”
|
|
||||||
let mut main_position_cursor = main_start_position;
|
|
||||||
self.items
|
|
||||||
.iter()
|
|
||||||
.zip(item_used_main_sizes)
|
|
||||||
.zip(item_margins)
|
|
||||||
.map(move |((item, &main_content_size), margin)| {
|
|
||||||
main_position_cursor +=
|
|
||||||
margin.main_start + item.border.main_start + item.padding.main_start;
|
|
||||||
let content_main_start_position = main_position_cursor;
|
|
||||||
main_position_cursor += main_content_size +
|
|
||||||
item.padding.main_end +
|
|
||||||
item.border.main_end +
|
|
||||||
margin.main_end +
|
|
||||||
item_main_interval;
|
|
||||||
content_main_start_position
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlexItem<'_> {
|
impl FlexItem<'_> {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue