Layout 2020: implement clearance as Option<Length>

Clearance was implemented as a Length, where zero meant no clearance.
However, having a clearance of 0px should be different than having
no clearance, since the former can still prevent margin collapse.

This patch keeps the existing behavior, so it won't be possible to get
a clearance of Some(Length::zero()), but it prepares the terrain for
a follow-up to fix calculate_clearance to return the proper thing.
This commit is contained in:
Oriol Brufau 2023-06-23 19:56:55 +02:00
parent a725380db0
commit 6b2bbdd02d
6 changed files with 42 additions and 24 deletions

View file

@ -855,7 +855,7 @@ impl FlexLine<'_> {
flex_context.sides_to_flow_relative(item.padding),
flex_context.sides_to_flow_relative(item.border),
margin,
Length::zero(),
None,
collapsed_margin,
),
item_result.positioning_context,

View file

@ -744,7 +744,7 @@ impl FloatBox {
margin,
// Clearance is handled internally by the float placement logic, so there's no need
// to store it explicitly in the fragment.
Length::zero(), // clearance
None, // clearance
CollapsedBlockMargins::zero(),
)
},
@ -826,9 +826,9 @@ impl SequentialLayoutState {
/// needs to have.
///
/// https://www.w3.org/TR/2011/REC-CSS2-20110607/visuren.html#flow-control
pub(crate) fn calculate_clearance(&self, clear_side: ClearSide) -> Length {
pub(crate) fn calculate_clearance(&self, clear_side: ClearSide) -> Option<Length> {
if clear_side == ClearSide::None {
return Length::zero();
return None;
}
let hypothetical_block_position = self.current_block_position_including_margins();
@ -848,7 +848,10 @@ impl SequentialLayoutState {
.max(self.floats.clear_right_position)
.max(hypothetical_block_position),
};
clear_position - hypothetical_block_position
if hypothetical_block_position >= clear_position {
return None;
}
Some(clear_position - hypothetical_block_position)
}
/// Adds a new adjoining margin.

View file

@ -554,7 +554,7 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
self.padding.clone(),
self.border.clone(),
self.margin.clone(),
Length::zero(),
None,
CollapsedBlockMargins::zero(),
);
let last_fragment = self.last_box_tree_fragment && !at_line_break;
@ -619,7 +619,7 @@ fn layout_atomic(
pbm.padding,
pbm.border,
margin,
Length::zero(),
None,
CollapsedBlockMargins::zero(),
)
},
@ -701,7 +701,7 @@ fn layout_atomic(
pbm.padding,
pbm.border,
margin,
Length::zero(),
None,
CollapsedBlockMargins::zero(),
)
},

View file

@ -183,7 +183,7 @@ impl BlockFormattingContext {
// The content height of a BFC root should include any float participating in that BFC
// (https://drafts.csswg.org/css2/#root-height), we implement this by imagining there is
// an element with `clear: both` after the actual contents.
let clearance = sequential_layout_state.map_or(Length::zero(), |sequential_layout_state| {
let clearance = sequential_layout_state.and_then(|sequential_layout_state| {
sequential_layout_state.calculate_clearance(ClearSide::Both)
});
@ -191,7 +191,7 @@ impl BlockFormattingContext {
fragments: flow_layout.fragments,
content_block_size: flow_layout.content_block_size +
flow_layout.collapsible_margins_in_children.end.solve() +
clearance,
clearance.unwrap_or_else(Length::zero),
}
}
}
@ -650,7 +650,7 @@ fn layout_in_flow_non_replaced_block_level(
pbm.border.block_end == Length::zero() &&
block_size == LengthOrAuto::Auto;
let mut clearance = Length::zero();
let mut clearance = None;
let parent_containing_block_position_info;
match sequential_layout_state {
None => parent_containing_block_position_info = None,
@ -667,7 +667,9 @@ fn layout_in_flow_non_replaced_block_level(
// 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,
pbm.padding.block_start +
pbm.border.block_start +
clearance.unwrap_or_else(Length::zero),
);
// We are about to lay out children. Update the offset between the block formatting
@ -776,7 +778,9 @@ fn layout_in_flow_non_replaced_block_level(
let content_rect = Rect {
start_corner: Vec2 {
block: pbm.padding.block_start + pbm.border.block_start + clearance,
block: pbm.padding.block_start +
pbm.border.block_start +
clearance.unwrap_or_else(Length::zero),
inline: pbm.padding.inline_start + pbm.border.inline_start + margin.inline_start,
},
size: Vec2 {
@ -821,20 +825,25 @@ fn layout_in_flow_replaced_block_level<'a>(
};
let fragments = replaced.make_fragments(style, size.clone());
let mut clearance = Length::zero();
let mut clearance = None;
if let Some(ref mut sequential_layout_state) = sequential_layout_state {
sequential_layout_state.adjoin_assign(&CollapsedMargin::new(margin.block_start));
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 + clearance,
pbm.border.block_sum() +
pbm.padding.block_sum() +
size.block +
clearance.unwrap_or_else(Length::zero),
);
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 + clearance,
block: pbm.padding.block_start +
pbm.border.block_start +
clearance.unwrap_or_else(Length::zero),
inline: pbm.padding.inline_start + pbm.border.inline_start + margin.inline_start,
},
size,
@ -915,8 +924,7 @@ impl PlacementState {
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() +
let mut fragment_block_size = fragment.padding.block_sum() +
fragment.border.block_sum() +
fragment.content_rect.size.block;
// We use `last_in_flow_margin_collapses_with_parent_end_margin` to implement
@ -924,7 +932,8 @@ impl PlacementState {
// > If the top and bottom margins of an element with clearance are adjoining,
// > its margins collapse with the adjoining margins of following siblings but that
// > resulting margin does not collapse with the bottom margin of the parent block.
if fragment.clearance != Length::zero() {
if let Some(clearance) = fragment.clearance {
fragment_block_size += clearance;
// Margins can't be adjoining if they are separated by clearance.
// Setting `next_in_flow_margin_collapses_with_parent_start_margin` to false
// prevents collapsing with the start margin of the parent, and will set
@ -957,7 +966,7 @@ impl PlacementState {
if fragment_block_margins.collapsed_through {
// `fragment_block_size` is typically zero when collapsing through,
// but we still need to consider it in case there is clearance.
self.current_block_direction_position += fragment.clearance;
self.current_block_direction_position += fragment_block_size;
self.current_margin
.adjoin_assign(&fragment_block_margins.end);
} else {

View file

@ -31,7 +31,13 @@ pub(crate) struct BoxFragment {
pub border: Sides<Length>,
pub margin: Sides<Length>,
pub clearance: Length,
/// When the `clear` property is not set to `none`, it may introduce clearance.
/// Clearance is some extra spacing that is added above the top margin,
/// so that the element doesn't overlap earlier floats in the same BFC.
/// The presence of clearance prevents the top margin from collapsing with
/// earlier margins or with the bottom margin of the parent block.
/// https://drafts.csswg.org/css2/#clearance
pub clearance: Option<Length>,
pub block_margins_collapsed_with_children: CollapsedBlockMargins,
@ -51,7 +57,7 @@ impl BoxFragment {
padding: Sides<Length>,
border: Sides<Length>,
margin: Sides<Length>,
clearance: Length,
clearance: Option<Length>,
block_margins_collapsed_with_children: CollapsedBlockMargins,
) -> BoxFragment {
let position = style.get_box().position;
@ -85,7 +91,7 @@ impl BoxFragment {
padding: Sides<Length>,
border: Sides<Length>,
margin: Sides<Length>,
clearance: Length,
clearance: Option<Length>,
block_margins_collapsed_with_children: CollapsedBlockMargins,
overconstrained: PhysicalSize<bool>,
) -> BoxFragment {

View file

@ -620,7 +620,7 @@ impl HoistedAbsolutelyPositionedBox {
pbm.padding,
pbm.border,
margin,
Length::zero(),
None,
CollapsedBlockMargins::zero(),
physical_overconstrained,
)