Skip to content

Commit

Permalink
layout_2020: Refactor PositioningContext to be simpler and smaller
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
mrobinson committed Mar 18, 2020
1 parent 5bf45b2 commit 712f934
Showing 1 changed file with 65 additions and 104 deletions.
169 changes: 65 additions & 104 deletions components/layout_2020/positioned.rs
Expand Up @@ -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`.
Expand All @@ -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 rect’s position in its own containing block:
Expand All @@ -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 it’s 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) {
Expand Down Expand Up @@ -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 rect’s 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 {
Expand Down Expand Up @@ -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;
Expand Down

0 comments on commit 712f934

Please sign in to comment.