Implement Calc for LengthOrPercentage

This commit is contained in:
David Zbarsky 2015-08-11 17:20:23 -04:00
parent fa5ad1c6b4
commit 9556141e57
6 changed files with 165 additions and 13 deletions

View file

@ -331,9 +331,8 @@ impl CandidateBSizeIterator {
(LengthOrPercentageOrAuto::Length(length), _) => MaybeAuto::Specified(length),
};
let max_block_size = match (fragment.style.max_block_size(), block_container_block_size) {
(LengthOrPercentageOrNone::Percentage(percent), Some(block_container_block_size)) => {
Some(block_container_block_size.scale_by(percent))
}
(LengthOrPercentageOrNone::Percentage(percent), Some(block_container_block_size)) =>
Some(block_container_block_size.scale_by(percent)),
(LengthOrPercentageOrNone::Percentage(_), None) |
(LengthOrPercentageOrNone::None, _) => None,
(LengthOrPercentageOrNone::Length(length), _) => Some(length),
@ -342,6 +341,10 @@ impl CandidateBSizeIterator {
(LengthOrPercentage::Percentage(percent), Some(block_container_block_size)) => {
block_container_block_size.scale_by(percent)
}
(LengthOrPercentage::Calc(calc), Some(block_container_block_size)) => {
calc.length() + block_container_block_size.scale_by(calc.percentage())
}
(LengthOrPercentage::Calc(calc), None) => calc.length(),
(LengthOrPercentage::Percentage(_), None) => Au(0),
(LengthOrPercentage::Length(length), _) => length,
};

View file

@ -1955,6 +1955,8 @@ fn position_to_offset(position: LengthOrPercentage, Au(total_length): Au) -> f32
fmin(1.0, (length as f32) / (total_length as f32))
}
LengthOrPercentage::Percentage(percentage) => percentage as f32,
LengthOrPercentage::Calc(calc) =>
fmin(1.0, calc.percentage() + (calc.length().0 as f32) / (total_length as f32)),
}
}

View file

@ -988,6 +988,11 @@ impl InlineFlow {
let percent_offset = line_height.scale_by(p);
offset_from_baseline = offset_from_baseline - percent_offset
}
vertical_align::T::Calc(calc) => {
let line_height = fragment.calculate_line_height(layout_context);
let percent_offset = line_height.scale_by(calc.percentage());
offset_from_baseline = offset_from_baseline - percent_offset - calc.length()
}
}
}
(offset_from_baseline - ascent, largest_size_updated)

View file

@ -416,7 +416,9 @@ pub fn specified_or_none(length: LengthOrPercentageOrNone, containing_length: Au
pub fn specified(length: LengthOrPercentage, containing_length: Au) -> Au {
match length {
LengthOrPercentage::Length(length) => length,
LengthOrPercentage::Percentage(p) => containing_length.scale_by(p)
LengthOrPercentage::Percentage(p) => containing_length.scale_by(p),
LengthOrPercentage::Calc(calc) =>
containing_length.scale_by(calc.percentage()) + calc.length(),
}
}

View file

@ -756,7 +756,7 @@ pub mod longhands {
pub mod computed_value {
use std::fmt;
use util::geometry::Au;
use values::CSSFloat;
use values::{CSSFloat, computed};
#[allow(non_camel_case_types)]
#[derive(PartialEq, Copy, Clone, HeapSizeOf)]
pub enum T {
@ -765,6 +765,7 @@ pub mod longhands {
% endfor
Length(Au),
Percentage(CSSFloat),
Calc(computed::Calc),
}
impl fmt::Debug for T {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
@ -774,6 +775,8 @@ pub mod longhands {
% endfor
&T::Length(length) => write!(f, "{:?}", length),
&T::Percentage(number) => write!(f, "{}%", number),
// XXX HACK WRONG
&T::Calc(calc) => write!(f, "{}%", 10.),
}
}
}
@ -785,6 +788,7 @@ pub mod longhands {
% endfor
T::Length(value) => value.to_css(dest),
T::Percentage(percentage) => write!(dest, "{}%", percentage * 100.),
T::Calc(calc) => calc.to_css(dest),
}
}
}
@ -805,12 +809,12 @@ pub mod longhands {
% endfor
SpecifiedValue::LengthOrPercentage(value) => {
match value.to_computed_value(context) {
computed::LengthOrPercentage::Length(value) => {
computed_value::T::Length(value)
}
computed::LengthOrPercentage::Percentage(value) => {
computed_value::T::Percentage(value)
}
computed::LengthOrPercentage::Length(value) =>
computed_value::T::Length(value),
computed::LengthOrPercentage::Percentage(value) =>
computed_value::T::Percentage(value),
computed::LengthOrPercentage::Calc(value) =>
computed_value::T::Calc(value),
}
}
}
@ -1909,7 +1913,10 @@ pub mod longhands {
.map(|value| match value {
specified::LengthOrPercentage::Length(value) => value,
specified::LengthOrPercentage::Percentage(value) =>
specified::Length::FontRelative(specified::FontRelativeLength::Em(value))
specified::Length::FontRelative(specified::FontRelativeLength::Em(value)),
// XXX WRONG HACK
specified::LengthOrPercentage::Calc(calc) =>
specified::Length::FontRelative(specified::FontRelativeLength::Em(20.)),
})
.or_else(|()| {
match_ignore_ascii_case! { try!(input.expect_ident()),

View file

@ -353,11 +353,93 @@ pub mod specified {
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub struct Calc {
pub absolute: Option<Au>,
pub font_relative: Option<FontRelativeLength>,
pub viewport_percentage: Option<ViewportPercentageLength>,
pub percentage: Option<CSSFloat>,
}
impl Calc {
pub fn parse_component(&mut self, input: &mut Parser) -> Result<(), ()> {
match try!(input.next()) {
Token::Dimension(ref value, ref unit) => {
let value = value.value;
match_ignore_ascii_case! { unit,
"px" => self.absolute =
Some(self.absolute.unwrap_or(Au(0)) + Au((value * AU_PER_PX) as i32)),
"in" => self.absolute =
Some(self.absolute.unwrap_or(Au(0)) + Au((value * AU_PER_IN) as i32)),
"cm" => self.absolute =
Some(self.absolute.unwrap_or(Au(0)) + Au((value * AU_PER_CM) as i32)),
"mm" => self.absolute =
Some(self.absolute.unwrap_or(Au(0)) + Au((value * AU_PER_MM) as i32)),
"pt" => self.absolute =
Some(self.absolute.unwrap_or(Au(0)) + Au((value * AU_PER_PT) as i32)),
"pc" => self.absolute =
Some(self.absolute.unwrap_or(Au(0)) + Au((value * AU_PER_PC) as i32))
// font-relative
/*"em" => Ok(Length::FontRelative(FontRelativeLength::Em(value))),
"ex" => Ok(Length::FontRelative(FontRelativeLength::Ex(value))),
"rem" => Ok(Length::FontRelative(FontRelativeLength::Rem(value))),
// viewport percentages
"vw" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vw(value))),
"vh" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vh(value))),
"vmin" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vmin(value))),
"vmax" => Ok(Length::ViewportPercentage(ViewportPercentageLength::Vmax(value)))*/
// Handle em, ex, rem, vw, vh, vmin, vmax
_ => return Err(())
}
},
Token::Percentage(ref value) =>
self.percentage = Some(self.percentage.unwrap_or(0.) + value.unit_value),
Token::Number(ref value) if value.value == 0. =>
self.absolute = self.absolute.or(Some(Au(0))),
_ => return Err(())
};
Ok(())
}
pub fn parse(input: &mut Parser) -> Result<Calc, ()> {
let mut calc = Calc {
absolute: None,
font_relative: None,
viewport_percentage: None,
percentage: None,
};
try!(calc.parse_component(input));
let operator = try!(input.next());
match operator {
Token::Delim('+') => (),
_ => return Err(())
};
try!(calc.parse_component(input));
Ok(calc)
}
}
impl ToCss for Calc {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
// XXX WRONG HACK
try!(write!(dest, "calc("));
if let Some(absolute) = self.absolute {
try!(write!(dest, "{}px", Au::to_px(absolute)));
}
if let Some(FontRelativeLength::Em(font_relative)) = self.font_relative {
try!(write!(dest, "{}em", font_relative));
}
write!(dest, ")")
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub enum LengthOrPercentage {
Length(Length),
Percentage(CSSFloat), // [0 .. 100%] maps to [0.0 .. 1.0]
Calc(Calc),
}
impl ToCss for LengthOrPercentage {
@ -366,6 +448,7 @@ pub mod specified {
&LengthOrPercentage::Length(length) => length.to_css(dest),
&LengthOrPercentage::Percentage(percentage)
=> write!(dest, "{}%", percentage * 100.),
&LengthOrPercentage::Calc(calc) => calc.to_css(dest),
}
}
}
@ -384,6 +467,10 @@ pub mod specified {
Ok(LengthOrPercentage::Percentage(value.unit_value)),
Token::Number(ref value) if value.value == 0. =>
Ok(LengthOrPercentage::Length(Length::Absolute(Au(0)))),
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
let calc = try!(input.parse_nested_block(Calc::parse));
Ok(LengthOrPercentage::Calc(calc))
},
_ => Err(())
}
}
@ -938,10 +1025,50 @@ pub mod computed {
}
}
#[derive(Clone, PartialEq, Copy, Debug, HeapSizeOf)]
pub struct Calc {
length: Option<Au>,
percentage: Option<CSSFloat>,
}
impl Calc {
pub fn length(&self) -> Au {
self.length.unwrap_or(Au(0))
}
pub fn percentage(&self) -> CSSFloat {
self.percentage.unwrap_or(0.)
}
}
impl ::cssparser::ToCss for Calc {
fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
match (self.length, self.percentage) {
(None, Some(p)) => write!(dest, "{}%", p),
(Some(l), None) => write!(dest, "{}px", Au::to_px(l)),
(Some(l), Some(p)) => write!(dest, "calc({}px + {}%)", Au::to_px(l), p * 100.),
_ => unreachable!()
}
}
}
impl ToComputedValue for specified::Calc {
type ComputedValue = Calc;
fn to_computed_value(&self, context: &Context) -> Calc {
let length = self.absolute;
let percentage = self.percentage;
Calc { length: length, percentage: percentage }
}
}
#[derive(PartialEq, Clone, Copy, HeapSizeOf)]
pub enum LengthOrPercentage {
Length(Au),
Percentage(CSSFloat),
Calc(Calc),
}
impl LengthOrPercentage {
@ -955,6 +1082,8 @@ pub mod computed {
match self {
&LengthOrPercentage::Length(length) => write!(f, "{:?}", length),
&LengthOrPercentage::Percentage(percentage) => write!(f, "{}%", percentage * 100.),
// XXX HACK WRONG
&LengthOrPercentage::Calc(calc) => write!(f, "{}%", 100.),
}
}
}
@ -970,6 +1099,9 @@ pub mod computed {
specified::LengthOrPercentage::Percentage(value) => {
LengthOrPercentage::Percentage(value)
}
specified::LengthOrPercentage::Calc(calc) => {
LengthOrPercentage::Calc(calc.to_computed_value(context))
}
}
}
}
@ -980,6 +1112,7 @@ pub mod computed {
&LengthOrPercentage::Length(length) => length.to_css(dest),
&LengthOrPercentage::Percentage(percentage)
=> write!(dest, "{}%", percentage * 100.),
&LengthOrPercentage::Calc(calc) => calc.to_css(dest),
}
}
}