Import victor's layout system 🍷

This commit is contained in:
Anthony Ramine 2019-09-09 17:21:26 +02:00
parent 86904757e6
commit 4444c5a2ad
25 changed files with 1270 additions and 712 deletions

View file

@ -1,14 +1,27 @@
use super::*;
use crate::fonts::BITSTREAM_VERA_SANS;
use crate::text::ShapedSegment;
/* 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(in crate::layout) struct InlineFormattingContext {
pub(crate) struct InlineFormattingContext {
pub(super) inline_level_boxes: Vec<Arc<InlineLevelBox>>,
}
#[derive(Debug)]
pub(in crate::layout) enum InlineLevelBox {
pub(crate) enum InlineLevelBox {
InlineBox(InlineBox),
TextRun(TextRun),
OutOfFlowAbsolutelyPositionedBox(AbsolutelyPositionedBox),
@ -21,7 +34,7 @@ pub(in crate::layout) enum InlineLevelBox {
}
#[derive(Debug)]
pub(in crate::layout) struct InlineBox {
pub(crate) struct InlineBox {
pub style: Arc<ComputedValues>,
pub first_fragment: bool,
pub last_fragment: bool,
@ -30,7 +43,7 @@ pub(in crate::layout) struct InlineBox {
/// https://www.w3.org/TR/css-display-3/#css-text-run
#[derive(Debug)]
pub(in crate::layout) struct TextRun {
pub(crate) struct TextRun {
pub parent_style: Arc<ComputedValues>,
pub text: String,
}
@ -93,36 +106,40 @@ impl InlineFormattingContext {
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 box_.style.specified_display {
Display::GeneratingBox(DisplayGeneratingBox::OutsideInside {
outside,
inside: _,
}) => Vec2 {
inline: match outside {
DisplayOutside::Inline => ifc.inline_position,
DisplayOutside::Block => Length::zero(),
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(),
DisplayOutside::None => unreachable!(":("),
},
block: ifc.line_boxes.next_line_block_position,
},
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"),
};
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
@ -180,7 +197,7 @@ impl InlineBox {
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().percentages_relative_to(cbis);
let mut border = style.border_width();
let mut margin = style
.margin()
.percentages_relative_to(cbis)
@ -245,9 +262,9 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
};
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;
*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();
@ -256,10 +273,10 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
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(),
fragment.content_rect.size.block +
fragment.padding.block_sum() +
fragment.border.block_sum() +
fragment.margin.block_sum(),
);
self.parent_nesting_level
.fragments_so_far
@ -268,78 +285,7 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
}
impl TextRun {
fn layout(&self, ifc: &mut InlineFormattingContextState) {
let available = ifc.containing_block.inline_size - ifc.inline_position;
let mut chars = self.text.chars();
loop {
let mut shaped = ShapedSegment::new_with_naive_shaping(BITSTREAM_VERA_SANS.clone());
let mut last_break_opportunity = None;
loop {
let next = chars.next();
if matches!(next, Some(' ') | None) {
let inline_size = self.parent_style.font.font_size * shaped.advance_width;
if inline_size > available {
if let Some((state, iter)) = last_break_opportunity.take() {
shaped.restore(&state);
chars = iter;
}
break;
}
}
if let Some(ch) = next {
if ch == ' ' {
last_break_opportunity = Some((shaped.save(), chars.clone()))
}
shaped.append_char(ch).unwrap()
} else {
break;
}
}
let inline_size = self.parent_style.font.font_size * shaped.advance_width;
// https://www.w3.org/TR/CSS2/visudet.html#propdef-line-height
// 'normal':
// “set the used value to a "reasonable" value based on the font of the element.”
let line_height = self.parent_style.font.font_size.0 * 1.2;
let content_rect = Rect {
start_corner: Vec2 {
block: Length::zero(),
inline: ifc.inline_position - ifc.current_nesting_level.inline_start,
},
size: Vec2 {
block: line_height,
inline: inline_size,
},
};
ifc.inline_position += inline_size;
ifc.current_nesting_level
.max_block_size_of_fragments_so_far
.max_assign(line_height);
ifc.current_nesting_level
.fragments_so_far
.push(Fragment::Text(TextFragment {
parent_style: self.parent_style.clone(),
content_rect,
text: shaped,
}));
if chars.as_str().is_empty() {
break;
} else {
// New line
ifc.current_nesting_level.inline_start = Length::zero();
let mut nesting_level = &mut ifc.current_nesting_level;
for partial in ifc.partial_inline_boxes_stack.iter_mut().rev() {
partial.finish_layout(nesting_level, &mut ifc.inline_position, true);
partial.start_corner.inline = Length::zero();
partial.padding.inline_start = Length::zero();
partial.border.inline_start = Length::zero();
partial.margin.inline_start = Length::zero();
partial.parent_nesting_level.inline_start = Length::zero();
nesting_level = &mut partial.parent_nesting_level;
}
ifc.line_boxes
.finish_line(nesting_level, ifc.containing_block);
ifc.inline_position = Length::zero();
}
}
fn layout(&self, _ifc: &mut InlineFormattingContextState) {
// TODO
}
}