layout_2020: Refactor PositioningContext to be simpler and smaller

Add a few helper methods which allow removing duplicate code in
PositioningContext. These methods will also be used to properly
implement hoisting in inline layout.
This commit is contained in:
Martin Robinson 2020-03-18 09:28:21 +01:00
parent 5bf45b2622
commit 712f9340e8

View file

@ -172,6 +172,19 @@ impl PositioningContext {
self.for_nearest_positioned_ancestor.is_some()
}
fn new_for_style(style: &ComputedValues) -> Option<Self> {
if style.establishes_containing_block_for_all_descendants() {
Some(Self::new_for_containing_block_for_all_descendants())
} else if style.establishes_containing_block() {
Some(Self {
for_nearest_positioned_ancestor: Some(Vec::new()),
for_nearest_containing_block_for_all_descendants: Vec::new(),
})
} else {
None
}
}
/// Given `fragment_layout_fn`, a closure which lays out a fragment in a provided
/// `PositioningContext`, create a new positioning context if necessary for the fragment and
/// lay out the fragment and all its children. Returns the newly created `BoxFragment`.
@ -182,82 +195,59 @@ impl PositioningContext {
style: &ComputedValues,
fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment,
) -> BoxFragment {
debug_assert!(style.clone_position() != Position::Fixed);
debug_assert!(style.clone_position() != Position::Absolute);
// Try to create a context, but if one isn't necessary, simply create the fragment
// using the given closure and the current `PositioningContext`.
let mut new_context = match Self::new_for_style(style) {
Some(new_context) => new_context,
None => return fragment_layout_fn(self),
};
if style.establishes_containing_block_for_all_descendants() {
let mut fragment = Self::layout_containing_block_for_all_descendants(
layout_context,
fragment_layout_fn,
);
if style.clone_position() == Position::Relative {
fragment.content_rect.start_corner +=
&relative_adjustement(style, containing_block);
}
return fragment;
}
let mut new_fragment = fragment_layout_fn(&mut new_context);
new_context.layout_collected_children(layout_context, &mut new_fragment);
// If the new context has any hoisted boxes for the nearest containing block for
// all descendants than collect them and pass them up the tree.
vec_append_owned(
&mut self.for_nearest_containing_block_for_all_descendants,
new_context.for_nearest_containing_block_for_all_descendants,
);
if style.clone_position() == Position::Relative {
let mut fragment = Self::create_and_layout_positioned(
layout_context,
style,
&mut self.for_nearest_containing_block_for_all_descendants,
fragment_layout_fn,
);
fragment.content_rect.start_corner += &relative_adjustement(style, containing_block);
return fragment;
new_fragment.content_rect.start_corner +=
&relative_adjustement(style, containing_block);
}
// We don't need to create a new PositioningContext for this Fragment, so
// we pass in the current one to the fragment layout closure.
fragment_layout_fn(self)
new_fragment
}
/// Given `fragment_layout_fn`, a closure which lays out a fragment in a provided
/// `PositioningContext`, create a positioning context a positioned fragment and lay out the
/// fragment and all its children. Returns the resulting `BoxFragment`.
/// `PositioningContext`, create a positioning context for a positioned fragment and lay out
/// the fragment and all its children. Returns the resulting `BoxFragment`.
fn create_and_layout_positioned(
layout_context: &LayoutContext,
style: &ComputedValues,
for_nearest_containing_block_for_all_descendants: &mut Vec<HoistedAbsolutelyPositionedBox>,
fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment,
) -> BoxFragment {
if style.establishes_containing_block_for_all_descendants() {
return Self::layout_containing_block_for_all_descendants(
layout_context,
fragment_layout_fn,
);
}
let mut new = Self {
for_nearest_positioned_ancestor: Some(Vec::new()),
for_nearest_containing_block_for_all_descendants: std::mem::take(
for_nearest_containing_block_for_all_descendants,
),
let mut new_context = match Self::new_for_style(style) {
Some(new_context) => new_context,
None => unreachable!(),
};
let mut positioned_box_fragment = fragment_layout_fn(&mut new);
new.layout_positioned_fragment_children(layout_context, &mut positioned_box_fragment);
let mut new_fragment = fragment_layout_fn(&mut new_context);
new_context.layout_collected_children(layout_context, &mut new_fragment);
*for_nearest_containing_block_for_all_descendants =
new.for_nearest_containing_block_for_all_descendants;
positioned_box_fragment
new_context.for_nearest_containing_block_for_all_descendants;
new_fragment
}
/// Given `fragment_layout_fn`, a closure which lays out a fragment in a provided
/// `PositioningContext`, create a positioning context for a fragment that establishes a
/// containing block for all descendants and lay out the fragment and all its children using
/// the new positioning context. Returns the resulting `BoxFragment`.
fn layout_containing_block_for_all_descendants(
// Lay out the hoisted boxes collected into this `PositioningContext` and add them
// to the given `BoxFragment`.
fn layout_collected_children(
&mut self,
layout_context: &LayoutContext,
fragment_layout_fn: impl FnOnce(&mut Self) -> BoxFragment,
) -> BoxFragment {
let mut containing_block_for_all_descendants =
Self::new_for_containing_block_for_all_descendants();
debug_assert!(containing_block_for_all_descendants
.for_nearest_positioned_ancestor
.is_none());
let mut new_fragment = fragment_layout_fn(&mut containing_block_for_all_descendants);
new_fragment: &mut BoxFragment,
) {
let padding_rect = Rect {
size: new_fragment.content_rect.size.clone(),
// Ignore the content rects position in its own containing block:
@ -269,28 +259,30 @@ impl PositioningContext {
style: &new_fragment.style,
};
let take_hoisted_boxes_pending_layout = |context: &mut Self| match context
.for_nearest_positioned_ancestor
.as_mut()
{
Some(fragments) => std::mem::take(fragments),
None => std::mem::take(&mut context.for_nearest_containing_block_for_all_descendants),
};
// Loop because its possible that we discover (the static position of)
// more absolutely-positioned boxes while doing layout for others.
let mut new_child_fragments = Vec::new();
while !containing_block_for_all_descendants
.for_nearest_containing_block_for_all_descendants
.is_empty()
{
let mut hoisted_boxes = take_hoisted_boxes_pending_layout(self);
let mut laid_out_child_fragments = Vec::new();
while !hoisted_boxes.is_empty() {
HoistedAbsolutelyPositionedBox::layout_many(
layout_context,
&std::mem::take(
&mut containing_block_for_all_descendants
.for_nearest_containing_block_for_all_descendants,
),
&mut new_child_fragments,
&mut containing_block_for_all_descendants
.for_nearest_containing_block_for_all_descendants,
&hoisted_boxes,
&mut laid_out_child_fragments,
&mut self.for_nearest_containing_block_for_all_descendants,
&containing_block,
)
);
hoisted_boxes = take_hoisted_boxes_pending_layout(self);
}
new_fragment.children.extend(new_child_fragments);
new_fragment
new_fragment.children.extend(laid_out_child_fragments);
}
pub(crate) fn push(&mut self, box_: HoistedAbsolutelyPositionedBox) {
@ -378,35 +370,6 @@ impl PositioningContext {
)
}
}
fn layout_positioned_fragment_children(
&mut self,
layout_context: &LayoutContext,
positioned_box_fragment: &mut BoxFragment,
) {
let for_here = self.for_nearest_positioned_ancestor.take().unwrap();
if !for_here.is_empty() {
let padding_rect = Rect {
size: positioned_box_fragment.content_rect.size.clone(),
// Ignore the content rects position in its own containing block:
start_corner: Vec2::zero(),
}
.inflate(&positioned_box_fragment.padding);
let containing_block = DefiniteContainingBlock {
size: padding_rect.size.clone(),
style: &positioned_box_fragment.style,
};
let mut children = Vec::new();
HoistedAbsolutelyPositionedBox::layout_many(
layout_context,
&for_here,
&mut children,
&mut self.for_nearest_containing_block_for_all_descendants,
&containing_block,
);
positioned_box_fragment.children.extend(children);
}
}
}
impl HoistedAbsolutelyPositionedBox {
@ -506,12 +469,10 @@ impl HoistedAbsolutelyPositionedBox {
block_end: block_axis.margin_end,
};
let for_containing_block_for_all_descendants =
for_nearest_containing_block_for_all_descendants;
PositioningContext::create_and_layout_positioned(
layout_context,
style,
for_containing_block_for_all_descendants,
for_nearest_containing_block_for_all_descendants,
|positioning_context| {
let size;
let fragments;