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",
|
||||
"icu_segmenter",
|
||||
"ipc-channel",
|
||||
"itertools 0.13.0",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"net_traits",
|
||||
|
|
|
@ -27,6 +27,7 @@ fonts_traits = { workspace = true }
|
|||
html5ever = { workspace = true }
|
||||
icu_segmenter = { workspace = true }
|
||||
ipc-channel = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
log = { workspace = true }
|
||||
net_traits = { workspace = true }
|
||||
parking_lot = { workspace = true }
|
||||
|
|
|
@ -7,6 +7,7 @@ use std::cmp::Ordering;
|
|||
|
||||
use app_units::Au;
|
||||
use atomic_refcell::AtomicRefMut;
|
||||
use itertools::izip;
|
||||
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_self::computed_value::T as AlignSelf;
|
||||
|
@ -804,7 +805,7 @@ impl FlexLine<'_> {
|
|||
self.resolve_flexible_lengths(container_main_size);
|
||||
|
||||
// https://drafts.csswg.org/css-flexbox/#algo-cross-item
|
||||
let item_layout_results = self
|
||||
let mut item_layout_results = self
|
||||
.items
|
||||
.iter_mut()
|
||||
.zip(&item_used_main_sizes)
|
||||
|
@ -828,14 +829,17 @@ impl FlexLine<'_> {
|
|||
|
||||
// Determine the used cross size of each flex item
|
||||
// https://drafts.csswg.org/css-flexbox/#algo-stretch
|
||||
let (item_used_cross_sizes, item_results): (Vec<_>, Vec<_>) = self
|
||||
.items
|
||||
.iter_mut()
|
||||
.zip(item_layout_results)
|
||||
.zip(&item_used_main_sizes)
|
||||
.map(|((item, mut item_result), &used_main_size)| {
|
||||
let item_count = self.items.len();
|
||||
let mut max_baseline = None;
|
||||
let mut item_used_cross_sizes = Vec::with_capacity(item_count);
|
||||
let mut item_cross_margins = Vec::with_capacity(item_count);
|
||||
for (item, item_layout_result, used_main_size) in izip!(
|
||||
self.items.iter_mut(),
|
||||
item_layout_results.iter_mut(),
|
||||
&item_used_main_sizes
|
||||
) {
|
||||
let has_stretch = item.align_self.0.value() == AlignFlags::STRETCH;
|
||||
let cross_size = if has_stretch &&
|
||||
let used_cross_size = if has_stretch &&
|
||||
item.content_box_size.cross.is_auto() &&
|
||||
!(item.margin.cross_start.is_auto() || item.margin.cross_end.is_auto())
|
||||
{
|
||||
|
@ -844,17 +848,41 @@ impl FlexLine<'_> {
|
|||
item.content_max_size.cross,
|
||||
)
|
||||
} else {
|
||||
item_result.hypothetical_cross_size
|
||||
item_layout_result.hypothetical_cross_size
|
||||
};
|
||||
item_used_cross_sizes.push(used_cross_size);
|
||||
|
||||
if has_stretch {
|
||||
// “If the flex item has `align-self: stretch`, redo layout for its contents,
|
||||
// treating this used size as its definite cross size
|
||||
// so that percentage-sized children can be resolved.”
|
||||
item_result = item.layout(used_main_size, flex_context, Some(cross_size));
|
||||
*item_layout_result =
|
||||
item.layout(*used_main_size, flex_context, Some(used_cross_size));
|
||||
}
|
||||
(cross_size, item_result)
|
||||
})
|
||||
.unzip();
|
||||
|
||||
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
|
||||
// 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.
|
||||
let item_count = self.items.len();
|
||||
let layout_is_flex_reversed = flex_context.flex_direction_is_reversed;
|
||||
|
||||
// Implement fallback alignment.
|
||||
|
@ -945,107 +972,73 @@ impl FlexLine<'_> {
|
|||
_ => Au::zero(),
|
||||
};
|
||||
|
||||
// https://drafts.csswg.org/css-flexbox/#algo-cross-margins
|
||||
let item_cross_margins = self.items.iter().zip(&item_used_cross_sizes).map(
|
||||
|(item, &item_cross_content_size)| {
|
||||
item.resolve_auto_cross_margins(
|
||||
flex_context,
|
||||
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(
|
||||
let mut main_position_cursor = main_start_position;
|
||||
let item_fragments = izip!(
|
||||
self.items.iter(),
|
||||
item_main_margins,
|
||||
item_cross_margins,
|
||||
&item_used_main_sizes,
|
||||
&item_margins,
|
||||
main_start_position,
|
||||
item_main_interval,
|
||||
);
|
||||
&item_used_cross_sizes,
|
||||
item_layout_results.into_iter()
|
||||
)
|
||||
.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-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_propagated_baselines = item_results
|
||||
.iter()
|
||||
.zip(&item_used_cross_sizes)
|
||||
.zip(self.items.iter())
|
||||
.map(|((layout_result, used_cross_size), item)| {
|
||||
if matches!(
|
||||
item.align_self.0.value(),
|
||||
AlignFlags::BASELINE | AlignFlags::LAST_BASELINE
|
||||
) {
|
||||
Some(
|
||||
layout_result
|
||||
.baseline_relative_to_margin_box
|
||||
.unwrap_or_else(|| {
|
||||
item.synthesized_baseline_relative_to_margin_box(*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,
|
||||
let item_content_cross_start_position = item.align_along_cross_axis(
|
||||
&item_margin,
|
||||
item_used_cross_size,
|
||||
line_cross_size,
|
||||
propagated_baseline.unwrap_or_default(),
|
||||
max_propagated_baseline,
|
||||
)
|
||||
});
|
||||
item_layout_result
|
||||
.baseline_relative_to_margin_box
|
||||
.unwrap_or_default(),
|
||||
max_baseline.unwrap_or_default(),
|
||||
);
|
||||
|
||||
let item_fragments = self
|
||||
.items
|
||||
.iter()
|
||||
.zip(item_results)
|
||||
.zip(
|
||||
item_used_main_sizes
|
||||
.iter()
|
||||
.zip(&item_used_cross_sizes)
|
||||
.map(|(&main, &cross)| FlexRelativeVec2 { main, cross })
|
||||
.zip(
|
||||
items_content_main_start_positions
|
||||
.zip(item_content_cross_start_posititons)
|
||||
.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 start_corner = FlexRelativeVec2 {
|
||||
main: item_content_main_start_position,
|
||||
cross: item_content_cross_start_position,
|
||||
};
|
||||
let size = FlexRelativeVec2 {
|
||||
main: *item_used_main_size,
|
||||
cross: *item_used_cross_size,
|
||||
};
|
||||
|
||||
let content_rect = flex_context
|
||||
.rect_to_flow_relative(line_size, FlexRelativeRect { start_corner, size });
|
||||
let margin = flex_context.sides_to_flow_relative(item_margin);
|
||||
let collapsed_margin = CollapsedBlockMargins::from_margin(&margin);
|
||||
(
|
||||
|
||||
// TODO: We should likely propagate baselines from `display: flex`.
|
||||
(
|
||||
BoxFragment::new(
|
||||
item.box_.base_fragment_info(),
|
||||
item.box_.style().clone(),
|
||||
item_result.fragments,
|
||||
item_layout_result.fragments,
|
||||
content_rect,
|
||||
flex_context.sides_to_flow_relative(item.padding),
|
||||
flex_context.sides_to_flow_relative(item.border),
|
||||
|
@ -1053,10 +1046,12 @@ impl FlexLine<'_> {
|
|||
None, /* clearance */
|
||||
collapsed_margin,
|
||||
),
|
||||
item_result.positioning_context,
|
||||
item_layout_result.positioning_context,
|
||||
)
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
FlexLineLayoutResult {
|
||||
cross_size: line_cross_size,
|
||||
item_fragments,
|
||||
|
@ -1462,33 +1457,6 @@ impl<'items> FlexLine<'items> {
|
|||
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<'_> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue