mirror of
https://github.com/servo/servo.git
synced 2025-06-06 16:45:39 +00:00
Add intrinsic size computation for inline formatting contexts
This commit is contained in:
parent
8fe37f3ed6
commit
9c5a595044
6 changed files with 188 additions and 46 deletions
|
@ -127,6 +127,7 @@ impl BlockContainer {
|
|||
context: &SharedStyleContext<'style>,
|
||||
block_container_style: &Arc<ComputedValues>,
|
||||
contents: NonReplacedContents<impl NodeExt<'dom>>,
|
||||
// intrinsic_sizes_requested: bool,
|
||||
) -> (BlockContainer, ContainsFloats) {
|
||||
let mut builder = BlockContainerBuilder {
|
||||
context,
|
||||
|
|
|
@ -9,13 +9,17 @@ use crate::formatting_contexts::IndependentFormattingContext;
|
|||
use crate::fragments::CollapsedBlockMargins;
|
||||
use crate::fragments::{AnonymousFragment, BoxFragment, Fragment, TextFragment};
|
||||
use crate::geom::flow_relative::{Rect, Sides, Vec2};
|
||||
use crate::intrinsic::{outer_intrinsic_inline_sizes, IntrinsicSizes};
|
||||
use crate::positioned::{AbsolutelyPositionedBox, AbsolutelyPositionedFragment};
|
||||
use crate::style_ext::{ComputedValuesExt, Display, DisplayGeneratingBox, DisplayOutside};
|
||||
use crate::{relative_adjustement, ContainingBlock};
|
||||
use app_units::Au;
|
||||
use gfx::text::text_run::GlyphRun;
|
||||
use servo_arc::Arc;
|
||||
use style::properties::ComputedValues;
|
||||
use style::values::computed::Length;
|
||||
use style::values::computed::{Length, Percentage};
|
||||
use style::Zero;
|
||||
use webrender_api::FontInstanceKey;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct InlineFormattingContext {
|
||||
|
@ -77,6 +81,119 @@ struct LinesBoxes {
|
|||
}
|
||||
|
||||
impl InlineFormattingContext {
|
||||
// This works on an already-constructed `InlineFormattingContext`,
|
||||
// Which would have to change if/when
|
||||
// `BlockContainer::construct` parallelize their construction.
|
||||
#[allow(unused)]
|
||||
pub(super) fn intrinsic_sizes(&self, layout_context: &LayoutContext) -> IntrinsicSizes {
|
||||
struct Computation {
|
||||
paragraph: IntrinsicSizes,
|
||||
current_line: IntrinsicSizes,
|
||||
current_line_percentages: Percentage,
|
||||
}
|
||||
impl Computation {
|
||||
fn traverse(
|
||||
&mut self,
|
||||
layout_context: &LayoutContext,
|
||||
inline_level_boxes: &[Arc<InlineLevelBox>],
|
||||
) {
|
||||
for inline_level_box in inline_level_boxes {
|
||||
match &**inline_level_box {
|
||||
InlineLevelBox::InlineBox(inline_box) => {
|
||||
let padding = inline_box.style.padding();
|
||||
let border = inline_box.style.border_width();
|
||||
let margin = inline_box.style.margin();
|
||||
macro_rules! add_length {
|
||||
($x: expr) => {{
|
||||
let length = $x;
|
||||
self.current_line.min_content += length;
|
||||
self.current_line.max_content += length;
|
||||
}};
|
||||
}
|
||||
macro_rules! add_lengthpercentage {
|
||||
($x: expr) => {{
|
||||
add_length!($x.length_component());
|
||||
self.current_line_percentages += $x.percentage_component();
|
||||
}};
|
||||
}
|
||||
macro_rules! add {
|
||||
($condition: ident, $side: ident) => {
|
||||
if inline_box.$condition {
|
||||
add_lengthpercentage!(padding.$side);
|
||||
add_length!(border.$side);
|
||||
margin.$side.non_auto().map(|x| add_lengthpercentage!(x));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
add!(first_fragment, inline_start);
|
||||
self.traverse(layout_context, &inline_box.children);
|
||||
add!(last_fragment, inline_end);
|
||||
},
|
||||
InlineLevelBox::TextRun(text_run) => {
|
||||
let (_, _, _, runs, break_at_start) =
|
||||
text_run.break_and_shape(layout_context);
|
||||
if break_at_start {
|
||||
self.line_break_opportunity()
|
||||
}
|
||||
for run in &runs {
|
||||
let advance = Length::from(run.glyph_store.total_advance());
|
||||
if run.glyph_store.is_whitespace() {
|
||||
self.line_break_opportunity()
|
||||
} else {
|
||||
self.current_line.min_content += advance
|
||||
}
|
||||
self.current_line.max_content += advance
|
||||
}
|
||||
},
|
||||
InlineLevelBox::Atomic(atomic) => {
|
||||
let inner = || {
|
||||
// atomic
|
||||
// .intrinsic_inline_sizes
|
||||
// .as_ref()
|
||||
// .expect("Accessing intrinsic size that was not requested")
|
||||
// .clone()
|
||||
todo!()
|
||||
};
|
||||
let (outer, pc) = outer_intrinsic_inline_sizes(&atomic.style, &inner);
|
||||
self.current_line.min_content += outer.min_content;
|
||||
self.current_line.max_content += outer.max_content;
|
||||
self.current_line_percentages += pc;
|
||||
},
|
||||
InlineLevelBox::OutOfFlowFloatBox(_) |
|
||||
InlineLevelBox::OutOfFlowAbsolutelyPositionedBox(_) => {},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn line_break_opportunity(&mut self) {
|
||||
self.paragraph
|
||||
.min_content
|
||||
.max_assign(take(&mut self.current_line.min_content));
|
||||
}
|
||||
|
||||
fn forced_line_break(&mut self) {
|
||||
self.line_break_opportunity();
|
||||
self.current_line
|
||||
.adjust_for_pbm_percentages(take(&mut self.current_line_percentages));
|
||||
self.paragraph
|
||||
.max_content
|
||||
.max_assign(take(&mut self.current_line.max_content));
|
||||
}
|
||||
}
|
||||
fn take<T: Zero>(x: &mut T) -> T {
|
||||
std::mem::replace(x, T::zero())
|
||||
}
|
||||
let mut computation = Computation {
|
||||
paragraph: IntrinsicSizes::zero(),
|
||||
current_line: IntrinsicSizes::zero(),
|
||||
current_line_percentages: Percentage::zero(),
|
||||
};
|
||||
computation.traverse(layout_context, &self.inline_level_boxes);
|
||||
computation.forced_line_break();
|
||||
computation.paragraph
|
||||
}
|
||||
|
||||
pub(super) fn layout<'a>(
|
||||
&'a self,
|
||||
layout_context: &LayoutContext,
|
||||
|
@ -283,11 +400,13 @@ impl<'box_tree> PartialInlineBoxFragment<'box_tree> {
|
|||
}
|
||||
|
||||
impl TextRun {
|
||||
fn layout(&self, layout_context: &LayoutContext, ifc: &mut InlineFormattingContextState) {
|
||||
fn break_and_shape(
|
||||
&self,
|
||||
layout_context: &LayoutContext,
|
||||
) -> (Au, Au, FontInstanceKey, Vec<GlyphRun>, bool) {
|
||||
use gfx::font::ShapingFlags;
|
||||
use style::computed_values::text_rendering::T as TextRendering;
|
||||
use style::computed_values::word_break::T as WordBreak;
|
||||
use style::values::generics::text::LineHeight;
|
||||
|
||||
let font_style = self.parent_style.clone_font();
|
||||
let inherited_text_style = self.parent_style.get_inherited_text();
|
||||
|
@ -316,30 +435,35 @@ impl TextRun {
|
|||
flags,
|
||||
};
|
||||
|
||||
let (font_ascent, font_line_gap, font_key, runs) =
|
||||
crate::context::with_thread_local_font_context(layout_context, |font_context| {
|
||||
let font_group = font_context.font_group(font_style);
|
||||
let font = font_group
|
||||
.borrow_mut()
|
||||
.first(font_context)
|
||||
.expect("could not find font");
|
||||
let mut font = font.borrow_mut();
|
||||
crate::context::with_thread_local_font_context(layout_context, |font_context| {
|
||||
let font_group = font_context.font_group(font_style);
|
||||
let font = font_group
|
||||
.borrow_mut()
|
||||
.first(font_context)
|
||||
.expect("could not find font");
|
||||
let mut font = font.borrow_mut();
|
||||
|
||||
let (runs, _break_at_start) = gfx::text::text_run::TextRun::break_and_shape(
|
||||
&mut font,
|
||||
&self.text,
|
||||
&shaping_options,
|
||||
&mut None,
|
||||
);
|
||||
let (runs, break_at_start) = gfx::text::text_run::TextRun::break_and_shape(
|
||||
&mut font,
|
||||
&self.text,
|
||||
&shaping_options,
|
||||
&mut None,
|
||||
);
|
||||
|
||||
(
|
||||
font.metrics.ascent,
|
||||
font.metrics.line_gap,
|
||||
font.font_key,
|
||||
runs,
|
||||
)
|
||||
});
|
||||
(
|
||||
font.metrics.ascent,
|
||||
font.metrics.line_gap,
|
||||
font.font_key,
|
||||
runs,
|
||||
break_at_start,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn layout(&self, layout_context: &LayoutContext, ifc: &mut InlineFormattingContextState) {
|
||||
use style::values::generics::text::LineHeight;
|
||||
|
||||
let (font_ascent, font_line_gap, font_key, runs, _) = self.break_and_shape(layout_context);
|
||||
let font_size = self.parent_style.get_font().font_size.size.0;
|
||||
let mut runs = runs.iter();
|
||||
loop {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
use crate::style_ext::ComputedValuesExt;
|
||||
use style::Zero;
|
||||
use style::properties::ComputedValues;
|
||||
use style::values::computed::{Length, LengthPercentage};
|
||||
use style::values::computed::{Length, LengthPercentage, Percentage};
|
||||
use style::Zero;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub(crate) struct IntrinsicSizes {
|
||||
|
@ -16,13 +16,26 @@ impl IntrinsicSizes {
|
|||
max_content: Length::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Relevant to outer intrinsic inline sizes, for percentages from padding and margin.
|
||||
pub fn adjust_for_pbm_percentages(&mut self, percentages: Percentage) {
|
||||
// " Note that this may yield an infinite result, but undefined results
|
||||
// (zero divided by zero) must be treated as zero. "
|
||||
if self.max_content.px() == 0. {
|
||||
// Avoid a potential `NaN`.
|
||||
// Zero is already the result we want regardless of `denominator`.
|
||||
} else {
|
||||
let denominator = (1. - percentages.0).max(0.);
|
||||
self.max_content = Length::new(self.max_content.px() / denominator);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// https://dbaron.org/css/intrinsic/#outer-intrinsic
|
||||
pub(crate) fn outer_intrinsic_inline_sizes(
|
||||
style: &ComputedValues,
|
||||
get_inner_intrinsic_sizes: &dyn Fn() -> IntrinsicSizes,
|
||||
) -> IntrinsicSizes {
|
||||
) -> (IntrinsicSizes, Percentage) {
|
||||
// FIXME: account for 'min-width', 'max-width', 'box-sizing'
|
||||
|
||||
let specified = style.box_size().inline;
|
||||
|
@ -34,36 +47,27 @@ pub(crate) fn outer_intrinsic_inline_sizes(
|
|||
Some(length) => IntrinsicSizes {
|
||||
min_content: length,
|
||||
max_content: length,
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
let mut bpm_lengths = Length::zero();
|
||||
let mut bpm_percentages = 0.;
|
||||
let mut pbm_lengths = Length::zero();
|
||||
let mut pbm_percentages = Percentage::zero();
|
||||
let padding = style.padding();
|
||||
let border = style.border_width();
|
||||
let margin = style.margin();
|
||||
bpm_lengths += border.inline_start;
|
||||
bpm_lengths += border.inline_end;
|
||||
pbm_lengths += border.inline_start;
|
||||
pbm_lengths += border.inline_end;
|
||||
let mut add = |x: LengthPercentage| {
|
||||
bpm_lengths += x.length_component();
|
||||
bpm_percentages += x.percentage();
|
||||
pbm_lengths += x.length_component();
|
||||
pbm_percentages += x.percentage_component();
|
||||
};
|
||||
add(padding.inline_start);
|
||||
add(padding.inline_end);
|
||||
margin.inline_start.non_auto().map(&mut add);
|
||||
margin.inline_end.non_auto().map(&mut add);
|
||||
|
||||
outer.min_content += bpm_lengths;
|
||||
outer.max_content += bpm_lengths;
|
||||
// " Note that this may yield an infinite result, but undefined results
|
||||
// (zero divided by zero) must be treated as zero. "
|
||||
if outer.max_content.px() == 0. {
|
||||
// Avoid a potential `NaN`.
|
||||
// Zero is already the result we want regardless of `denominator`.
|
||||
} else {
|
||||
let denominator = (1. - bpm_percentages).max(0.);
|
||||
outer.max_content = Length::new(outer.max_content.px() / denominator);
|
||||
}
|
||||
outer.min_content += pbm_lengths;
|
||||
outer.max_content += pbm_lengths;
|
||||
|
||||
outer
|
||||
(outer, pbm_percentages)
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ mod flow;
|
|||
mod formatting_contexts;
|
||||
mod fragments;
|
||||
mod geom;
|
||||
mod intrinsic;
|
||||
mod opaque_node;
|
||||
mod positioned;
|
||||
pub mod query;
|
||||
|
|
|
@ -170,6 +170,12 @@ impl LengthPercentage {
|
|||
self.length
|
||||
}
|
||||
|
||||
/// Returns the percentage component of this `calc()`
|
||||
#[inline]
|
||||
pub fn percentage_component(&self) -> Percentage {
|
||||
Percentage(self.clamping_mode.clamp(self.percentage.0))
|
||||
}
|
||||
|
||||
/// Return the percentage value as CSSFloat.
|
||||
#[inline]
|
||||
pub fn percentage(&self) -> CSSFloat {
|
||||
|
|
|
@ -64,6 +64,12 @@ impl Zero for Percentage {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::ops::AddAssign for Percentage {
|
||||
fn add_assign(&mut self, other: Self) {
|
||||
self.0 += other.0
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCss for Percentage {
|
||||
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
|
||||
where
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue