mirror of
https://github.com/servo/servo.git
synced 2025-06-15 03:44:30 +00:00
290 lines
11 KiB
Rust
290 lines
11 KiB
Rust
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
|
|
|
use crate::flow::float::FloatBox;
|
|
use crate::flow::FlowChildren;
|
|
use crate::fragments::{AnonymousFragment, BoxFragment, CollapsedBlockMargins, Fragment};
|
|
use crate::geom::flow_relative::{Rect, Sides, Vec2};
|
|
use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment};
|
|
use crate::replaced::ReplacedContent;
|
|
use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside};
|
|
use crate::{relative_adjustement, take, ContainingBlock};
|
|
use servo_arc::Arc;
|
|
use style::properties::ComputedValues;
|
|
use style::values::computed::Length;
|
|
use style::Zero;
|
|
|
|
#[derive(Debug, Default)]
|
|
pub(crate) struct InlineFormattingContext {
|
|
pub(super) inline_level_boxes: Vec<Arc<InlineLevelBox>>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) enum InlineLevelBox {
|
|
InlineBox(InlineBox),
|
|
TextRun(TextRun),
|
|
OutOfFlowAbsolutelyPositionedBox(AbsolutelyPositionedBox),
|
|
OutOfFlowFloatBox(FloatBox),
|
|
Atomic {
|
|
style: Arc<ComputedValues>,
|
|
// FIXME: this should be IndependentFormattingContext:
|
|
contents: ReplacedContent,
|
|
},
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) struct InlineBox {
|
|
pub style: Arc<ComputedValues>,
|
|
pub first_fragment: bool,
|
|
pub last_fragment: bool,
|
|
pub children: Vec<Arc<InlineLevelBox>>,
|
|
}
|
|
|
|
/// https://www.w3.org/TR/css-display-3/#css-text-run
|
|
#[derive(Debug)]
|
|
pub(crate) struct TextRun {
|
|
pub parent_style: Arc<ComputedValues>,
|
|
pub text: String,
|
|
}
|
|
|
|
struct InlineNestingLevelState<'box_tree> {
|
|
remaining_boxes: std::slice::Iter<'box_tree, Arc<InlineLevelBox>>,
|
|
fragments_so_far: Vec<Fragment>,
|
|
inline_start: Length,
|
|
max_block_size_of_fragments_so_far: Length,
|
|
}
|
|
|
|
struct PartialInlineBoxFragment<'box_tree> {
|
|
style: Arc<ComputedValues>,
|
|
start_corner: Vec2<Length>,
|
|
padding: Sides<Length>,
|
|
border: Sides<Length>,
|
|
margin: Sides<Length>,
|
|
last_box_tree_fragment: bool,
|
|
parent_nesting_level: InlineNestingLevelState<'box_tree>,
|
|
}
|
|
|
|
struct InlineFormattingContextState<'box_tree, 'cb> {
|
|
containing_block: &'cb ContainingBlock,
|
|
line_boxes: LinesBoxes,
|
|
inline_position: Length,
|
|
partial_inline_boxes_stack: Vec<PartialInlineBoxFragment<'box_tree>>,
|
|
current_nesting_level: InlineNestingLevelState<'box_tree>,
|
|
}
|
|
|
|
struct LinesBoxes {
|
|
boxes: Vec<Fragment>,
|
|
next_line_block_position: Length,
|
|
}
|
|
|
|
impl InlineFormattingContext {
|
|
pub(super) fn layout<'a>(
|
|
&'a self,
|
|
containing_block: &ContainingBlock,
|
|
tree_rank: usize,
|
|
absolutely_positioned_fragments: &mut Vec<AbsolutelyPositionedFragment<'a>>,
|
|
) -> FlowChildren {
|
|
let mut ifc = InlineFormattingContextState {
|
|
containing_block,
|
|
partial_inline_boxes_stack: Vec::new(),
|
|
line_boxes: LinesBoxes {
|
|
boxes: Vec::new(),
|
|
next_line_block_position: Length::zero(),
|
|
},
|
|
inline_position: Length::zero(),
|
|
current_nesting_level: InlineNestingLevelState {
|
|
remaining_boxes: self.inline_level_boxes.iter(),
|
|
fragments_so_far: Vec::with_capacity(self.inline_level_boxes.len()),
|
|
inline_start: Length::zero(),
|
|
max_block_size_of_fragments_so_far: Length::zero(),
|
|
},
|
|
};
|
|
loop {
|
|
if let Some(child) = ifc.current_nesting_level.remaining_boxes.next() {
|
|
match &**child {
|
|
InlineLevelBox::InlineBox(inline) => {
|
|
let partial = inline.start_layout(&mut ifc);
|
|
ifc.partial_inline_boxes_stack.push(partial)
|
|
},
|
|
InlineLevelBox::TextRun(run) => run.layout(&mut ifc),
|
|
InlineLevelBox::Atomic { style: _, contents } => {
|
|
// FIXME
|
|
match *contents {}
|
|
},
|
|
InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(box_) => {
|
|
let initial_start_corner =
|
|
match Display::from(box_.style.get_box().original_display) {
|
|
Display::GeneratingBox(DisplayGeneratingBox::OutsideInside {
|
|
outside,
|
|
inside: _,
|
|
}) => Vec2 {
|
|
inline: match outside {
|
|
DisplayOutside::Inline => ifc.inline_position,
|
|
DisplayOutside::Block => Length::zero(),
|
|
},
|
|
block: ifc.line_boxes.next_line_block_position,
|
|
},
|
|
Display::Contents => {
|
|
panic!("display:contents does not generate an abspos box")
|
|
},
|
|
Display::None => {
|
|
panic!("display:none does not generate an abspos box")
|
|
},
|
|
};
|
|
absolutely_positioned_fragments
|
|
.push(box_.layout(initial_start_corner, tree_rank));
|
|
},
|
|
InlineLevelBox::OutOfFlowFloatBox(_box_) => {
|
|
// TODO
|
|
continue;
|
|
},
|
|
}
|
|
} else
|
|
// Reached the end of ifc.remaining_boxes
|
|
if let Some(mut partial) = ifc.partial_inline_boxes_stack.pop() {
|
|
partial.finish_layout(
|
|
&mut ifc.current_nesting_level,
|
|
&mut ifc.inline_position,
|
|
false,
|
|
);
|
|
ifc.current_nesting_level = partial.parent_nesting_level
|
|
} else {
|
|
ifc.line_boxes
|
|
.finish_line(&mut ifc.current_nesting_level, containing_block);
|
|
return FlowChildren {
|
|
fragments: ifc.line_boxes.boxes,
|
|
block_size: ifc.line_boxes.next_line_block_position,
|
|
collapsible_margins_in_children: CollapsedBlockMargins::zero(),
|
|
};
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl LinesBoxes {
|
|
fn finish_line(
|
|
&mut self,
|
|
top_nesting_level: &mut InlineNestingLevelState,
|
|
containing_block: &ContainingBlock,
|
|
) {
|
|
let start_corner = Vec2 {
|
|
inline: Length::zero(),
|
|
block: self.next_line_block_position,
|
|
};
|
|
let size = Vec2 {
|
|
inline: containing_block.inline_size,
|
|
block: std::mem::replace(
|
|
&mut top_nesting_level.max_block_size_of_fragments_so_far,
|
|
Length::zero(),
|
|
),
|
|
};
|
|
self.next_line_block_position += size.block;
|
|
self.boxes.push(Fragment::Anonymous(AnonymousFragment {
|
|
children: take(&mut top_nesting_level.fragments_so_far),
|
|
rect: Rect { start_corner, size },
|
|
mode: containing_block.mode,
|
|
}))
|
|
}
|
|
}
|
|
|
|
impl InlineBox {
|
|
fn start_layout<'box_tree>(
|
|
&'box_tree self,
|
|
ifc: &mut InlineFormattingContextState<'box_tree, '_>,
|
|
) -> PartialInlineBoxFragment<'box_tree> {
|
|
let style = self.style.clone();
|
|
let cbis = ifc.containing_block.inline_size;
|
|
let mut padding = style.padding().percentages_relative_to(cbis);
|
|
let mut border = style.border_width();
|
|
let mut margin = style
|
|
.margin()
|
|
.percentages_relative_to(cbis)
|
|
.auto_is(Length::zero);
|
|
if self.first_fragment {
|
|
ifc.inline_position += padding.inline_start + border.inline_start + margin.inline_start;
|
|
} else {
|
|
padding.inline_start = Length::zero();
|
|
border.inline_start = Length::zero();
|
|
margin.inline_start = Length::zero();
|
|
}
|
|
let mut start_corner = Vec2 {
|
|
block: padding.block_start + border.block_start + margin.block_start,
|
|
inline: ifc.inline_position - ifc.current_nesting_level.inline_start,
|
|
};
|
|
start_corner += &relative_adjustement(
|
|
&style,
|
|
ifc.containing_block.inline_size,
|
|
ifc.containing_block.block_size,
|
|
);
|
|
PartialInlineBoxFragment {
|
|
style,
|
|
start_corner,
|
|
padding,
|
|
border,
|
|
margin,
|
|
last_box_tree_fragment: self.last_fragment,
|
|
parent_nesting_level: std::mem::replace(
|
|
&mut ifc.current_nesting_level,
|
|
InlineNestingLevelState {
|
|
remaining_boxes: self.children.iter(),
|
|
fragments_so_far: Vec::with_capacity(self.children.len()),
|
|
inline_start: ifc.inline_position,
|
|
max_block_size_of_fragments_so_far: Length::zero(),
|
|
},
|
|
),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
|
|
fn finish_layout(
|
|
&mut self,
|
|
nesting_level: &mut InlineNestingLevelState,
|
|
inline_position: &mut Length,
|
|
at_line_break: bool,
|
|
) {
|
|
let mut fragment = BoxFragment {
|
|
style: self.style.clone(),
|
|
children: take(&mut nesting_level.fragments_so_far),
|
|
content_rect: Rect {
|
|
size: Vec2 {
|
|
inline: *inline_position - self.start_corner.inline,
|
|
block: nesting_level.max_block_size_of_fragments_so_far,
|
|
},
|
|
start_corner: self.start_corner.clone(),
|
|
},
|
|
padding: self.padding.clone(),
|
|
border: self.border.clone(),
|
|
margin: self.margin.clone(),
|
|
block_margins_collapsed_with_children: CollapsedBlockMargins::zero(),
|
|
};
|
|
let last_fragment = self.last_box_tree_fragment && !at_line_break;
|
|
if last_fragment {
|
|
*inline_position += fragment.padding.inline_end +
|
|
fragment.border.inline_end +
|
|
fragment.margin.inline_end;
|
|
} else {
|
|
fragment.padding.inline_end = Length::zero();
|
|
fragment.border.inline_end = Length::zero();
|
|
fragment.margin.inline_end = Length::zero();
|
|
}
|
|
self.parent_nesting_level
|
|
.max_block_size_of_fragments_so_far
|
|
.max_assign(
|
|
fragment.content_rect.size.block +
|
|
fragment.padding.block_sum() +
|
|
fragment.border.block_sum() +
|
|
fragment.margin.block_sum(),
|
|
);
|
|
self.parent_nesting_level
|
|
.fragments_so_far
|
|
.push(Fragment::Box(fragment));
|
|
}
|
|
}
|
|
|
|
impl TextRun {
|
|
fn layout(&self, _ifc: &mut InlineFormattingContextState) {
|
|
// TODO
|
|
}
|
|
}
|