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