mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Place replaced and non-auto inline size independent FCs next to floats (#29977)
* Place replaced and non-auto inline size independent FCs next to floats The CSS2 specification says that replaced content and independent formatting contexts should be placed next to floats. This change adds support for that, but punts on support for independent formatting contexts that have an auto inline size. With an auto inline size, we which requires a much more complex layout algorithm. Co-authored-by: Oriol Brufau <obrufau@igalia.com> * Fix issue with where last band was taken into account for inline size * adjustment_from_floats should prevent margin collapse * Properly handle elements with 0 height --------- Co-authored-by: Oriol Brufau <obrufau@igalia.com>
This commit is contained in:
parent
64adc98e64
commit
ae3f33b9d0
13 changed files with 421 additions and 131 deletions
|
@ -17,6 +17,7 @@ use crate::style_ext::{ComputedValuesExt, DisplayInside};
|
|||
use crate::ContainingBlock;
|
||||
use euclid::num::Zero;
|
||||
use servo_arc::Arc;
|
||||
use std::collections::VecDeque;
|
||||
use std::fmt::{Debug, Formatter, Result as FmtResult};
|
||||
use std::ops::Range;
|
||||
use std::{f32, mem};
|
||||
|
@ -70,6 +71,113 @@ impl ContainingBlockPositionInfo {
|
|||
}
|
||||
}
|
||||
|
||||
/// This data strucure is used to try to place non-floating content among float content.
|
||||
/// This is used primarily to place replaced content and independent formatting contexts
|
||||
/// next to floats, as the specifcation dictates.
|
||||
pub(crate) struct PlacementAmongFloats<'a> {
|
||||
/// The [FloatContext] to use for this placement.
|
||||
float_context: &'a FloatContext,
|
||||
/// The current bands we are considering for this placement.
|
||||
current_bands: VecDeque<FloatBand>,
|
||||
/// The next band, needed to know the height of the last band in current_bands.
|
||||
next_band: FloatBand,
|
||||
/// The size of the object to place.
|
||||
object_size: Vec2<Length>,
|
||||
/// The minimum position in the block direction for the placement. Objects should not
|
||||
/// be placed before this point.
|
||||
ceiling: Length,
|
||||
}
|
||||
|
||||
impl<'a> PlacementAmongFloats<'a> {
|
||||
pub(crate) fn new(
|
||||
float_context: &'a FloatContext,
|
||||
ceiling: Length,
|
||||
object_size: Vec2<Length>,
|
||||
) -> Self {
|
||||
assert!(!ceiling.px().is_infinite());
|
||||
let current_band = float_context.bands.find(ceiling).unwrap();
|
||||
let current_bands = VecDeque::from([current_band]);
|
||||
let next_band = float_context.bands.find_next(current_band.top).unwrap();
|
||||
PlacementAmongFloats {
|
||||
float_context,
|
||||
current_bands,
|
||||
next_band,
|
||||
object_size,
|
||||
ceiling,
|
||||
}
|
||||
}
|
||||
|
||||
fn top_of_placement_for_current_bands(&self) -> Length {
|
||||
self.ceiling.max(self.current_bands.front().unwrap().top)
|
||||
}
|
||||
|
||||
fn current_bands_height(&self) -> Length {
|
||||
assert!(!self.current_bands.is_empty());
|
||||
self.next_band.top - self.top_of_placement_for_current_bands()
|
||||
}
|
||||
|
||||
fn accumulate_enough_bands_for_block_size(&mut self) {
|
||||
while self.current_bands_height() < self.object_size.block {
|
||||
assert!(!self.next_band.top.px().is_infinite());
|
||||
self.current_bands.push_back(self.next_band);
|
||||
self.next_band = self
|
||||
.float_context
|
||||
.bands
|
||||
.find_next(self.next_band.top)
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_viable_inline_space(&self) -> (Length, Length) {
|
||||
let mut max_inline_start = self.float_context.containing_block_info.inline_start;
|
||||
let mut min_inline_end = self.float_context.containing_block_info.inline_end;
|
||||
assert!(!self.current_bands.is_empty());
|
||||
|
||||
for band in self.current_bands.iter() {
|
||||
if let Some(left) = band.left {
|
||||
max_inline_start = max_inline_start.max(left);
|
||||
}
|
||||
if let Some(right) = band.right {
|
||||
min_inline_end = min_inline_end.min(right);
|
||||
}
|
||||
}
|
||||
return (max_inline_start, min_inline_end);
|
||||
}
|
||||
|
||||
pub(crate) fn try_place_once(&mut self) -> Option<Vec2<Length>> {
|
||||
self.accumulate_enough_bands_for_block_size();
|
||||
let (inline_start, inline_end) = self.calculate_viable_inline_space();
|
||||
if inline_end - inline_start >= self.object_size.inline {
|
||||
return Some(Vec2 {
|
||||
inline: inline_start,
|
||||
block: self.top_of_placement_for_current_bands(),
|
||||
});
|
||||
}
|
||||
|
||||
self.current_bands.pop_front();
|
||||
None
|
||||
}
|
||||
|
||||
/// Run the placement algorithm for this [PlacementAmongFloats].
|
||||
pub(crate) fn place(&mut self) -> Vec2<Length> {
|
||||
while self.current_bands.len() > 0 {
|
||||
if let Some(result) = self.try_place_once() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// We could not fit the object in among the floats, so we place it as if it
|
||||
// cleared all floats.
|
||||
return Vec2 {
|
||||
inline: self.float_context.containing_block_info.inline_start,
|
||||
block: self
|
||||
.ceiling
|
||||
.max(self.float_context.clear_left_position)
|
||||
.max(self.float_context.clear_right_position),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// Data kept during layout about the floats in a given block formatting context.
|
||||
///
|
||||
/// This is a persistent data structure. Each float has its own private copy of the float context,
|
||||
|
@ -133,13 +241,12 @@ impl FloatContext {
|
|||
///
|
||||
/// This should be used for placing inline elements and block formatting contexts so that they
|
||||
/// don't collide with floats.
|
||||
pub fn place_object(&self, object: &PlacementInfo) -> Vec2<Length> {
|
||||
pub(crate) fn place_object(&self, object: &PlacementInfo, ceiling: Length) -> Vec2<Length> {
|
||||
let ceiling = match object.clear {
|
||||
ClearSide::None => self.ceiling,
|
||||
ClearSide::Left => self.ceiling.max(self.clear_left_position),
|
||||
ClearSide::Right => self.ceiling.max(self.clear_right_position),
|
||||
ClearSide::Both => self
|
||||
.ceiling
|
||||
ClearSide::None => ceiling,
|
||||
ClearSide::Left => ceiling.max(self.clear_left_position),
|
||||
ClearSide::Right => ceiling.max(self.clear_right_position),
|
||||
ClearSide::Both => ceiling
|
||||
.max(self.clear_left_position)
|
||||
.max(self.clear_right_position),
|
||||
};
|
||||
|
@ -163,7 +270,7 @@ impl FloatContext {
|
|||
};
|
||||
Vec2 {
|
||||
inline: left_object_edge,
|
||||
block: first_band.top.max(self.ceiling),
|
||||
block: first_band.top.max(ceiling),
|
||||
}
|
||||
},
|
||||
FloatSide::Right => {
|
||||
|
@ -173,16 +280,16 @@ impl FloatContext {
|
|||
};
|
||||
Vec2 {
|
||||
inline: right_object_edge - object.size.inline,
|
||||
block: first_band.top.max(self.ceiling),
|
||||
block: first_band.top.max(ceiling),
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Places a new float and adds it to the list. Returns the start corner of its margin box.
|
||||
pub fn add_float(&mut self, new_float: &PlacementInfo) -> Vec2<Length> {
|
||||
pub(crate) fn add_float(&mut self, new_float: &PlacementInfo) -> Vec2<Length> {
|
||||
// Place the float.
|
||||
let new_float_origin = self.place_object(&new_float);
|
||||
let new_float_origin = self.place_object(&new_float, self.ceiling);
|
||||
let new_float_extent = match new_float.side {
|
||||
FloatSide::Left => new_float_origin.inline + new_float.size.inline,
|
||||
FloatSide::Right => new_float_origin.inline,
|
||||
|
@ -242,7 +349,7 @@ impl FloatContext {
|
|||
|
||||
/// Information needed to place an object so that it doesn't collide with existing floats.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct PlacementInfo {
|
||||
pub(crate) struct PlacementInfo {
|
||||
/// The *margin* box size of the object.
|
||||
pub size: Vec2<Length>,
|
||||
/// Whether the object is (logically) aligned to the left or right.
|
||||
|
@ -909,7 +1016,7 @@ impl SequentialLayoutState {
|
|||
&mut self,
|
||||
box_fragment: &mut BoxFragment,
|
||||
margins_collapsing_with_parent_containing_block: CollapsedMargin,
|
||||
block_offset_from_containining_block_top: CSSPixelLength,
|
||||
block_offset_from_containing_block_top: Length,
|
||||
) {
|
||||
let block_start_of_containing_block_in_bfc = self.floats.containing_block_info.block_start +
|
||||
self.floats
|
||||
|
@ -919,7 +1026,7 @@ impl SequentialLayoutState {
|
|||
.solve();
|
||||
|
||||
self.floats.lower_ceiling(
|
||||
block_start_of_containing_block_in_bfc + block_offset_from_containining_block_top,
|
||||
block_start_of_containing_block_in_bfc + block_offset_from_containing_block_top,
|
||||
);
|
||||
|
||||
let pbm_sums = &(&box_fragment.padding + &box_fragment.border) + &box_fragment.margin;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue