mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
Place floats in layout 2020, but don't flow text around the floats yet.
This commit puts floats behind the `layout.floats.enabled` pref, because of the following issues and unimplemented features: * Inline formatting contexts don't take floats into account, so text doesn't flow around the floats yet. * Non-floated block formatting contexts don't take floats into account, so BFCs can overlap floats. * Block formatting contexts that contain floats don't expand vertically to contain all the floats. That is, floats can stick out the bottom of BFCs, contra spec.
This commit is contained in:
parent
053a0aa4fd
commit
cdec48328e
235 changed files with 1018 additions and 623 deletions
|
@ -6,15 +6,13 @@
|
|||
|
||||
use crate::cell::ArcRefCell;
|
||||
use crate::context::LayoutContext;
|
||||
use crate::flow::float::{FloatBox, FloatContext};
|
||||
use crate::flow::float::{ClearSide, FloatBox, SequentialLayoutState};
|
||||
use crate::flow::inline::InlineFormattingContext;
|
||||
use crate::formatting_contexts::{
|
||||
IndependentFormattingContext, IndependentLayout, NonReplacedFormattingContext,
|
||||
};
|
||||
use crate::fragment_tree::BaseFragmentInfo;
|
||||
use crate::fragments::{
|
||||
AnonymousFragment, BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment,
|
||||
};
|
||||
use crate::fragments::{BoxFragment, CollapsedBlockMargins, CollapsedMargin, Fragment};
|
||||
use crate::geom::flow_relative::{Rect, Sides, Vec2};
|
||||
use crate::positioned::{AbsolutelyPositionedBox, PositioningContext};
|
||||
use crate::replaced::ReplacedContent;
|
||||
|
@ -78,26 +76,31 @@ impl BlockFormattingContext {
|
|||
containing_block: &ContainingBlock,
|
||||
tree_rank: usize,
|
||||
) -> IndependentLayout {
|
||||
let mut float_context;
|
||||
let float_context = if self.contains_floats {
|
||||
float_context = FloatContext::new();
|
||||
Some(&mut float_context)
|
||||
let mut sequential_layout_state = if self.contains_floats || !layout_context.use_rayon {
|
||||
Some(SequentialLayoutState::new())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let flow_layout = self.contents.layout(
|
||||
|
||||
let mut flow_layout = self.contents.layout(
|
||||
layout_context,
|
||||
positioning_context,
|
||||
containing_block,
|
||||
tree_rank,
|
||||
float_context,
|
||||
sequential_layout_state.as_mut(),
|
||||
CollapsibleWithParentStartMargin(false),
|
||||
);
|
||||
assert!(
|
||||
debug_assert!(
|
||||
!flow_layout
|
||||
.collapsible_margins_in_children
|
||||
.collapsed_through
|
||||
);
|
||||
|
||||
// FIXME(pcwalton): Relative positioning of ancestors should affect descendant floats.
|
||||
if let Some(ref sequential_layout_state) = sequential_layout_state {
|
||||
sequential_layout_state.add_float_fragments_to_list(&mut flow_layout.fragments);
|
||||
}
|
||||
|
||||
IndependentLayout {
|
||||
fragments: flow_layout.fragments,
|
||||
content_block_size: flow_layout.content_block_size +
|
||||
|
@ -113,7 +116,7 @@ impl BlockContainer {
|
|||
positioning_context: &mut PositioningContext,
|
||||
containing_block: &ContainingBlock,
|
||||
tree_rank: usize,
|
||||
float_context: Option<&mut FloatContext>,
|
||||
sequential_layout_state: Option<&mut SequentialLayoutState>,
|
||||
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
|
||||
) -> FlowLayout {
|
||||
match self {
|
||||
|
@ -123,7 +126,7 @@ impl BlockContainer {
|
|||
child_boxes,
|
||||
containing_block,
|
||||
tree_rank,
|
||||
float_context,
|
||||
sequential_layout_state,
|
||||
collapsible_with_parent_start_margin,
|
||||
),
|
||||
BlockContainer::InlineFormattingContext(ifc) => ifc.layout(
|
||||
|
@ -131,6 +134,7 @@ impl BlockContainer {
|
|||
positioning_context,
|
||||
containing_block,
|
||||
tree_rank,
|
||||
sequential_layout_state,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -169,130 +173,110 @@ fn layout_block_level_children(
|
|||
child_boxes: &[ArcRefCell<BlockLevelBox>],
|
||||
containing_block: &ContainingBlock,
|
||||
tree_rank: usize,
|
||||
mut float_context: Option<&mut FloatContext>,
|
||||
mut sequential_layout_state: Option<&mut SequentialLayoutState>,
|
||||
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
|
||||
) -> FlowLayout {
|
||||
fn place_block_level_fragment(fragment: &mut Fragment, placement_state: &mut PlacementState) {
|
||||
match fragment {
|
||||
Fragment::Box(fragment) => {
|
||||
let fragment_block_margins = &fragment.block_margins_collapsed_with_children;
|
||||
let fragment_block_size = fragment.padding.block_sum() +
|
||||
fragment.border.block_sum() +
|
||||
fragment.content_rect.size.block;
|
||||
|
||||
if placement_state.next_in_flow_margin_collapses_with_parent_start_margin {
|
||||
assert_eq!(placement_state.current_margin.solve(), Length::zero());
|
||||
placement_state
|
||||
.start_margin
|
||||
.adjoin_assign(&fragment_block_margins.start);
|
||||
if fragment_block_margins.collapsed_through {
|
||||
placement_state
|
||||
.start_margin
|
||||
.adjoin_assign(&fragment_block_margins.end);
|
||||
return;
|
||||
}
|
||||
placement_state.next_in_flow_margin_collapses_with_parent_start_margin = false;
|
||||
} else {
|
||||
placement_state
|
||||
.current_margin
|
||||
.adjoin_assign(&fragment_block_margins.start);
|
||||
}
|
||||
fragment.content_rect.start_corner.block += placement_state.current_margin.solve() +
|
||||
placement_state.current_block_direction_position;
|
||||
if fragment_block_margins.collapsed_through {
|
||||
placement_state
|
||||
.current_margin
|
||||
.adjoin_assign(&fragment_block_margins.end);
|
||||
return;
|
||||
}
|
||||
placement_state.current_block_direction_position +=
|
||||
placement_state.current_margin.solve() + fragment_block_size;
|
||||
placement_state.current_margin = fragment_block_margins.end;
|
||||
},
|
||||
Fragment::AbsoluteOrFixedPositioned(fragment) => {
|
||||
let offset = Vec2 {
|
||||
block: placement_state.current_margin.solve() +
|
||||
placement_state.current_block_direction_position,
|
||||
inline: Length::new(0.),
|
||||
};
|
||||
fragment.borrow_mut().adjust_offsets(offset);
|
||||
},
|
||||
Fragment::Anonymous(_) => {},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
match sequential_layout_state {
|
||||
Some(ref mut sequential_layout_state) => layout_block_level_children_sequentially(
|
||||
layout_context,
|
||||
positioning_context,
|
||||
child_boxes,
|
||||
containing_block,
|
||||
tree_rank,
|
||||
sequential_layout_state,
|
||||
collapsible_with_parent_start_margin,
|
||||
),
|
||||
None => layout_block_level_children_in_parallel(
|
||||
layout_context,
|
||||
positioning_context,
|
||||
child_boxes,
|
||||
containing_block,
|
||||
tree_rank,
|
||||
collapsible_with_parent_start_margin,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
struct PlacementState {
|
||||
next_in_flow_margin_collapses_with_parent_start_margin: bool,
|
||||
start_margin: CollapsedMargin,
|
||||
current_margin: CollapsedMargin,
|
||||
current_block_direction_position: Length,
|
||||
}
|
||||
fn layout_block_level_children_in_parallel(
|
||||
layout_context: &LayoutContext,
|
||||
positioning_context: &mut PositioningContext,
|
||||
child_boxes: &[ArcRefCell<BlockLevelBox>],
|
||||
containing_block: &ContainingBlock,
|
||||
tree_rank: usize,
|
||||
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
|
||||
) -> FlowLayout {
|
||||
let mut placement_state = PlacementState::new(collapsible_with_parent_start_margin);
|
||||
|
||||
let mut placement_state = PlacementState {
|
||||
next_in_flow_margin_collapses_with_parent_start_margin:
|
||||
collapsible_with_parent_start_margin.0,
|
||||
start_margin: CollapsedMargin::zero(),
|
||||
current_margin: CollapsedMargin::zero(),
|
||||
current_block_direction_position: Length::zero(),
|
||||
};
|
||||
let fragments = positioning_context.adjust_static_positions(tree_rank, |positioning_context| {
|
||||
if float_context.is_some() || !layout_context.use_rayon {
|
||||
// Because floats are involved, we do layout for this block formatting context
|
||||
// in tree order without parallelism. This enables mutable access
|
||||
// to a `FloatContext` that tracks every float encountered so far (again in tree order).
|
||||
child_boxes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(tree_rank, box_)| {
|
||||
let mut fragment = box_.borrow_mut().layout(
|
||||
let collects_for_nearest_positioned_ancestor =
|
||||
positioning_context.collects_for_nearest_positioned_ancestor();
|
||||
let mut fragments: Vec<Fragment> = child_boxes
|
||||
.par_iter()
|
||||
.enumerate()
|
||||
.mapfold_reduce_into(
|
||||
positioning_context,
|
||||
|positioning_context, (tree_rank, box_)| {
|
||||
box_.borrow_mut().layout(
|
||||
layout_context,
|
||||
positioning_context,
|
||||
containing_block,
|
||||
tree_rank,
|
||||
float_context.as_mut().map(|c| &mut **c),
|
||||
);
|
||||
place_block_level_fragment(&mut fragment, &mut placement_state);
|
||||
fragment
|
||||
})
|
||||
.collect()
|
||||
} else {
|
||||
let collects_for_nearest_positioned_ancestor =
|
||||
positioning_context.collects_for_nearest_positioned_ancestor();
|
||||
let mut fragments = child_boxes
|
||||
.par_iter()
|
||||
.enumerate()
|
||||
.mapfold_reduce_into(
|
||||
positioning_context,
|
||||
|positioning_context, (tree_rank, box_)| {
|
||||
box_.borrow_mut().layout(
|
||||
layout_context,
|
||||
positioning_context,
|
||||
containing_block,
|
||||
tree_rank,
|
||||
/* float_context = */ None,
|
||||
)
|
||||
},
|
||||
|| PositioningContext::new_for_rayon(collects_for_nearest_positioned_ancestor),
|
||||
PositioningContext::append,
|
||||
)
|
||||
.collect();
|
||||
for fragment in &mut fragments {
|
||||
place_block_level_fragment(fragment, &mut placement_state)
|
||||
}
|
||||
fragments
|
||||
/* sequential_layout_state = */ None,
|
||||
)
|
||||
},
|
||||
|| PositioningContext::new_for_rayon(collects_for_nearest_positioned_ancestor),
|
||||
PositioningContext::append,
|
||||
)
|
||||
.collect();
|
||||
for fragment in fragments.iter_mut() {
|
||||
placement_state.place_fragment(fragment);
|
||||
}
|
||||
fragments
|
||||
});
|
||||
|
||||
FlowLayout {
|
||||
fragments,
|
||||
content_block_size: placement_state.current_block_direction_position,
|
||||
collapsible_margins_in_children: CollapsedBlockMargins {
|
||||
collapsed_through: placement_state
|
||||
.next_in_flow_margin_collapses_with_parent_start_margin,
|
||||
start: placement_state.start_margin,
|
||||
end: placement_state.current_margin,
|
||||
},
|
||||
collapsible_margins_in_children: placement_state.collapsible_margins_in_children(),
|
||||
}
|
||||
}
|
||||
|
||||
fn layout_block_level_children_sequentially(
|
||||
layout_context: &LayoutContext,
|
||||
positioning_context: &mut PositioningContext,
|
||||
child_boxes: &[ArcRefCell<BlockLevelBox>],
|
||||
containing_block: &ContainingBlock,
|
||||
tree_rank: usize,
|
||||
sequential_layout_state: &mut SequentialLayoutState,
|
||||
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
|
||||
) -> FlowLayout {
|
||||
let mut placement_state = PlacementState::new(collapsible_with_parent_start_margin);
|
||||
|
||||
let fragments = positioning_context.adjust_static_positions(tree_rank, |positioning_context| {
|
||||
// Because floats are involved, we do layout for this block formatting context in tree
|
||||
// order without parallelism. This enables mutable access to a `SequentialLayoutState` that
|
||||
// tracks every float encountered so far (again in tree order).
|
||||
child_boxes
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(tree_rank, child_box)| {
|
||||
let mut fragment = child_box.borrow_mut().layout(
|
||||
layout_context,
|
||||
positioning_context,
|
||||
containing_block,
|
||||
tree_rank,
|
||||
Some(&mut *sequential_layout_state),
|
||||
);
|
||||
placement_state.place_fragment(&mut fragment);
|
||||
fragment
|
||||
})
|
||||
.collect()
|
||||
});
|
||||
|
||||
FlowLayout {
|
||||
fragments,
|
||||
content_block_size: placement_state.current_block_direction_position,
|
||||
collapsible_margins_in_children: placement_state.collapsible_margins_in_children(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -303,7 +287,7 @@ impl BlockLevelBox {
|
|||
positioning_context: &mut PositioningContext,
|
||||
containing_block: &ContainingBlock,
|
||||
tree_rank: usize,
|
||||
float_context: Option<&mut FloatContext>,
|
||||
sequential_layout_state: Option<&mut SequentialLayoutState>,
|
||||
) -> Fragment {
|
||||
match self {
|
||||
BlockLevelBox::SameFormattingContextBlock {
|
||||
|
@ -323,7 +307,7 @@ impl BlockLevelBox {
|
|||
style,
|
||||
NonReplacedContents::SameFormattingContextBlock(contents),
|
||||
tree_rank,
|
||||
float_context,
|
||||
sequential_layout_state,
|
||||
)
|
||||
},
|
||||
)),
|
||||
|
@ -339,6 +323,7 @@ impl BlockLevelBox {
|
|||
replaced.base_fragment_info,
|
||||
&replaced.style,
|
||||
&replaced.contents,
|
||||
sequential_layout_state,
|
||||
)
|
||||
},
|
||||
))
|
||||
|
@ -359,7 +344,7 @@ impl BlockLevelBox {
|
|||
non_replaced,
|
||||
),
|
||||
tree_rank,
|
||||
float_context,
|
||||
sequential_layout_state,
|
||||
)
|
||||
},
|
||||
))
|
||||
|
@ -379,11 +364,14 @@ impl BlockLevelBox {
|
|||
positioning_context.push(hoisted_box);
|
||||
Fragment::AbsoluteOrFixedPositioned(hoisted_fragment)
|
||||
},
|
||||
BlockLevelBox::OutOfFlowFloatBox(_box_) => {
|
||||
// FIXME: call layout_maybe_position_relative_fragment here
|
||||
Fragment::Anonymous(AnonymousFragment::no_op(
|
||||
containing_block.style.writing_mode,
|
||||
))
|
||||
BlockLevelBox::OutOfFlowFloatBox(box_) => {
|
||||
box_.layout(
|
||||
layout_context,
|
||||
positioning_context,
|
||||
containing_block,
|
||||
sequential_layout_state,
|
||||
);
|
||||
Fragment::Float
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -425,7 +413,7 @@ fn layout_in_flow_non_replaced_block_level(
|
|||
style: &Arc<ComputedValues>,
|
||||
block_level_kind: NonReplacedContents,
|
||||
tree_rank: usize,
|
||||
float_context: Option<&mut FloatContext>,
|
||||
mut sequential_layout_state: Option<&mut SequentialLayoutState>,
|
||||
) -> BoxFragment {
|
||||
let pbm = style.padding_border_margin(containing_block);
|
||||
let box_size = style.content_box_size(containing_block, &pbm);
|
||||
|
@ -479,37 +467,76 @@ fn layout_in_flow_non_replaced_block_level(
|
|||
block_size,
|
||||
style,
|
||||
};
|
||||
|
||||
// https://drafts.csswg.org/css-writing-modes/#orthogonal-flows
|
||||
assert_eq!(
|
||||
containing_block.style.writing_mode, containing_block_for_children.style.writing_mode,
|
||||
"Mixed writing modes are not supported yet"
|
||||
);
|
||||
|
||||
let block_is_same_formatting_context = match block_level_kind {
|
||||
NonReplacedContents::SameFormattingContextBlock(_) => true,
|
||||
NonReplacedContents::EstablishesAnIndependentFormattingContext(_) => false,
|
||||
};
|
||||
|
||||
let start_margin_can_collapse_with_children = block_is_same_formatting_context &&
|
||||
pbm.padding.block_start == Length::zero() &&
|
||||
pbm.border.block_start == Length::zero();
|
||||
let end_margin_can_collapse_with_children = block_is_same_formatting_context &&
|
||||
pbm.padding.block_end == Length::zero() &&
|
||||
pbm.border.block_end == Length::zero() &&
|
||||
block_size == LengthOrAuto::Auto &&
|
||||
min_box_size.block == Length::zero();
|
||||
|
||||
let mut clearance = Length::zero();
|
||||
let old_inline_walls;
|
||||
match sequential_layout_state {
|
||||
None => old_inline_walls = None,
|
||||
Some(ref mut sequential_layout_state) => {
|
||||
sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_start));
|
||||
if !start_margin_can_collapse_with_children {
|
||||
sequential_layout_state.collapse_margins();
|
||||
}
|
||||
|
||||
// Introduce clearance if necessary.
|
||||
let clear_side = ClearSide::from_style(style);
|
||||
clearance = sequential_layout_state.calculate_clearance(clear_side);
|
||||
|
||||
// NB: This will be a no-op if we're collapsing margins with our children since that
|
||||
// can only happen if we have no block-start padding and border.
|
||||
sequential_layout_state.advance_block_position(
|
||||
pbm.padding.block_start + pbm.border.block_start + clearance,
|
||||
);
|
||||
|
||||
// Store our old inline walls so we can reset them later.
|
||||
old_inline_walls = Some(sequential_layout_state.floats.walls);
|
||||
sequential_layout_state.floats.walls.left +=
|
||||
pbm.padding.inline_start + pbm.border.inline_start + margin.inline_start;
|
||||
sequential_layout_state.floats.walls.right =
|
||||
sequential_layout_state.floats.walls.left + inline_size;
|
||||
},
|
||||
};
|
||||
|
||||
let mut block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
|
||||
|
||||
let fragments;
|
||||
let mut content_block_size;
|
||||
match block_level_kind {
|
||||
NonReplacedContents::SameFormattingContextBlock(contents) => {
|
||||
let start_margin_can_collapse_with_children = pbm.padding.block_start == Length::zero() &&
|
||||
pbm.border.block_start == Length::zero();
|
||||
let end_margin_can_collapse_with_children = pbm.padding.block_end == Length::zero() &&
|
||||
pbm.border.block_end == Length::zero() &&
|
||||
block_size == LengthOrAuto::Auto &&
|
||||
min_box_size.block == Length::zero();
|
||||
|
||||
let flow_layout = contents.layout(
|
||||
layout_context,
|
||||
positioning_context,
|
||||
&containing_block_for_children,
|
||||
tree_rank,
|
||||
float_context,
|
||||
sequential_layout_state.as_mut().map(|x| &mut **x),
|
||||
CollapsibleWithParentStartMargin(start_margin_can_collapse_with_children),
|
||||
);
|
||||
|
||||
fragments = flow_layout.fragments;
|
||||
content_block_size = flow_layout.content_block_size;
|
||||
let mut collapsible_margins_in_children = flow_layout.collapsible_margins_in_children;
|
||||
|
||||
// Update margins.
|
||||
let mut collapsible_margins_in_children = flow_layout.collapsible_margins_in_children;
|
||||
if start_margin_can_collapse_with_children {
|
||||
block_margins_collapsed_with_children
|
||||
.start
|
||||
|
@ -546,12 +573,31 @@ fn layout_in_flow_non_replaced_block_level(
|
|||
content_block_size = independent_layout.content_block_size;
|
||||
},
|
||||
};
|
||||
|
||||
let block_size = block_size.auto_is(|| {
|
||||
content_block_size.clamp_between_extremums(min_box_size.block, max_box_size.block)
|
||||
});
|
||||
|
||||
if let Some(ref mut sequential_layout_state) = sequential_layout_state {
|
||||
// Now that we're done laying out our children, we can restore the old inline walls.
|
||||
sequential_layout_state.floats.walls = old_inline_walls.unwrap();
|
||||
|
||||
// Account for padding and border. We also might have to readjust the
|
||||
// `bfc_relative_block_position` if it was different from the content size (i.e. was
|
||||
// non-`auto` and/or was affected by min/max block size).
|
||||
sequential_layout_state.advance_block_position(
|
||||
(block_size - content_block_size) + pbm.padding.block_end + pbm.border.block_end,
|
||||
);
|
||||
|
||||
if !end_margin_can_collapse_with_children {
|
||||
sequential_layout_state.collapse_margins();
|
||||
}
|
||||
sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_end));
|
||||
}
|
||||
|
||||
let content_rect = Rect {
|
||||
start_corner: Vec2 {
|
||||
block: pbm.padding.block_start + pbm.border.block_start,
|
||||
block: pbm.padding.block_start + pbm.border.block_start + clearance,
|
||||
inline: pbm.padding.inline_start + pbm.border.inline_start + margin.inline_start,
|
||||
},
|
||||
size: Vec2 {
|
||||
|
@ -559,6 +605,7 @@ fn layout_in_flow_non_replaced_block_level(
|
|||
inline: inline_size,
|
||||
},
|
||||
};
|
||||
|
||||
BoxFragment::new(
|
||||
base_fragment_info,
|
||||
style.clone(),
|
||||
|
@ -567,6 +614,7 @@ fn layout_in_flow_non_replaced_block_level(
|
|||
pbm.padding,
|
||||
pbm.border,
|
||||
margin,
|
||||
clearance,
|
||||
block_margins_collapsed_with_children,
|
||||
)
|
||||
}
|
||||
|
@ -579,6 +627,7 @@ fn layout_in_flow_replaced_block_level<'a>(
|
|||
base_fragment_info: BaseFragmentInfo,
|
||||
style: &Arc<ComputedValues>,
|
||||
replaced: &ReplacedContent,
|
||||
mut sequential_layout_state: Option<&mut SequentialLayoutState>,
|
||||
) -> BoxFragment {
|
||||
let pbm = style.padding_border_margin(containing_block);
|
||||
let size = replaced.used_size_as_if_inline_element(containing_block, style, None, &pbm);
|
||||
|
@ -592,14 +641,24 @@ fn layout_in_flow_replaced_block_level<'a>(
|
|||
block_end: pbm.margin.block_end.auto_is(Length::zero),
|
||||
};
|
||||
let fragments = replaced.make_fragments(style, size.clone());
|
||||
|
||||
let mut clearance = Length::zero();
|
||||
if let Some(ref mut sequential_layout_state) = sequential_layout_state {
|
||||
sequential_layout_state.collapse_margins();
|
||||
clearance = sequential_layout_state.calculate_clearance(ClearSide::from_style(style));
|
||||
sequential_layout_state
|
||||
.advance_block_position(pbm.border.block_sum() + pbm.padding.block_sum() + size.block);
|
||||
};
|
||||
|
||||
let content_rect = Rect {
|
||||
start_corner: Vec2 {
|
||||
block: pbm.padding.block_start + pbm.border.block_start,
|
||||
block: pbm.padding.block_start + pbm.border.block_start + clearance,
|
||||
inline: pbm.padding.inline_start + pbm.border.inline_start + margin.inline_start,
|
||||
},
|
||||
size,
|
||||
};
|
||||
let block_margins_collapsed_with_children = CollapsedBlockMargins::from_margin(&margin);
|
||||
|
||||
BoxFragment::new(
|
||||
base_fragment_info,
|
||||
style.clone(),
|
||||
|
@ -608,6 +667,7 @@ fn layout_in_flow_replaced_block_level<'a>(
|
|||
pbm.padding,
|
||||
pbm.border,
|
||||
margin,
|
||||
Length::zero(),
|
||||
block_margins_collapsed_with_children,
|
||||
)
|
||||
}
|
||||
|
@ -624,3 +684,81 @@ fn solve_inline_margins_for_in_flow_block_level(
|
|||
(LengthOrAuto::LengthPercentage(start), _) => (start, available - start),
|
||||
}
|
||||
}
|
||||
|
||||
// State that we maintain when placing blocks.
|
||||
//
|
||||
// In parallel mode, this placement is done after all child blocks are laid out. In sequential
|
||||
// mode, this is done right after each block is laid out.
|
||||
pub(crate) struct PlacementState {
|
||||
next_in_flow_margin_collapses_with_parent_start_margin: bool,
|
||||
start_margin: CollapsedMargin,
|
||||
current_margin: CollapsedMargin,
|
||||
current_block_direction_position: Length,
|
||||
}
|
||||
|
||||
impl PlacementState {
|
||||
fn new(
|
||||
collapsible_with_parent_start_margin: CollapsibleWithParentStartMargin,
|
||||
) -> PlacementState {
|
||||
PlacementState {
|
||||
next_in_flow_margin_collapses_with_parent_start_margin:
|
||||
collapsible_with_parent_start_margin.0,
|
||||
start_margin: CollapsedMargin::zero(),
|
||||
current_margin: CollapsedMargin::zero(),
|
||||
current_block_direction_position: Length::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
fn place_fragment(&mut self, fragment: &mut Fragment) {
|
||||
match fragment {
|
||||
Fragment::Box(fragment) => {
|
||||
let fragment_block_margins = &fragment.block_margins_collapsed_with_children;
|
||||
let fragment_block_size = fragment.clearance +
|
||||
fragment.padding.block_sum() +
|
||||
fragment.border.block_sum() +
|
||||
fragment.content_rect.size.block;
|
||||
|
||||
if self.next_in_flow_margin_collapses_with_parent_start_margin {
|
||||
debug_assert_eq!(self.current_margin.solve(), Length::zero());
|
||||
self.start_margin
|
||||
.adjoin_assign(&fragment_block_margins.start);
|
||||
if fragment_block_margins.collapsed_through {
|
||||
self.start_margin.adjoin_assign(&fragment_block_margins.end);
|
||||
return;
|
||||
}
|
||||
self.next_in_flow_margin_collapses_with_parent_start_margin = false;
|
||||
} else {
|
||||
self.current_margin
|
||||
.adjoin_assign(&fragment_block_margins.start);
|
||||
}
|
||||
fragment.content_rect.start_corner.block +=
|
||||
self.current_margin.solve() + self.current_block_direction_position;
|
||||
if fragment_block_margins.collapsed_through {
|
||||
self.current_margin
|
||||
.adjoin_assign(&fragment_block_margins.end);
|
||||
return;
|
||||
}
|
||||
self.current_block_direction_position +=
|
||||
self.current_margin.solve() + fragment_block_size;
|
||||
self.current_margin = fragment_block_margins.end;
|
||||
},
|
||||
Fragment::AbsoluteOrFixedPositioned(fragment) => {
|
||||
let offset = Vec2 {
|
||||
block: self.current_margin.solve() + self.current_block_direction_position,
|
||||
inline: Length::new(0.),
|
||||
};
|
||||
fragment.borrow_mut().adjust_offsets(offset);
|
||||
},
|
||||
Fragment::Anonymous(_) | Fragment::Float => {},
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn collapsible_margins_in_children(&self) -> CollapsedBlockMargins {
|
||||
CollapsedBlockMargins {
|
||||
collapsed_through: self.next_in_flow_margin_collapses_with_parent_start_margin,
|
||||
start: self.start_margin,
|
||||
end: self.current_margin,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue