mirror of
https://github.com/servo/servo.git
synced 2025-10-04 02:29:12 +01:00
style: Respect calc for percentages.
This commit is contained in:
parent
465e6f14fe
commit
310be02ba8
28 changed files with 308 additions and 176 deletions
|
@ -20,7 +20,7 @@ use stylesheets::CssRuleType;
|
|||
use super::{AllowQuirks, Number, ToComputedValue};
|
||||
use values::{Auto, CSSFloat, Either, FONT_MEDIUM_PX, None_, Normal};
|
||||
use values::ExtremumLength;
|
||||
use values::computed::{ComputedValueAsSpecified, Context};
|
||||
use values::computed::{self, Context};
|
||||
use values::specified::calc::CalcNode;
|
||||
|
||||
pub use values::specified::calc::CalcLengthOrPercentage;
|
||||
|
@ -701,39 +701,81 @@ impl<T: Parse> Either<Length, T> {
|
|||
}
|
||||
|
||||
/// A percentage value.
|
||||
///
|
||||
/// [0 .. 100%] maps to [0.0 .. 1.0]
|
||||
///
|
||||
/// FIXME(emilio): There's no standard property that requires a `<percentage>`
|
||||
/// without requiring also a `<length>`. If such a property existed, we'd need
|
||||
/// to add special handling for `calc()` and percentages in here in the same way
|
||||
/// as for `Angle` and `Time`, but the lack of this this is otherwise
|
||||
/// undistinguishable (we handle it correctly from `CalcLengthOrPercentage`).
|
||||
///
|
||||
/// As of today, only `-moz-image-rect` supports percentages without length.
|
||||
/// This is not a regression, and that's a non-standard extension anyway, so I'm
|
||||
/// not implementing it for now.
|
||||
#[derive(Clone, Copy, Debug, Default, HasViewportPercentage, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(Deserialize, HeapSizeOf, Serialize))]
|
||||
pub struct Percentage(pub CSSFloat);
|
||||
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
pub struct Percentage {
|
||||
/// The percentage value as a float.
|
||||
///
|
||||
/// [0 .. 100%] maps to [0.0 .. 1.0]
|
||||
value: CSSFloat,
|
||||
/// If this percentage came from a calc() expression, this tells how
|
||||
/// clamping should be done on the value.
|
||||
calc_clamping_mode: Option<AllowedNumericType>,
|
||||
}
|
||||
|
||||
no_viewport_percentage!(Percentage);
|
||||
|
||||
impl ToCss for Percentage {
|
||||
fn to_css<W>(&self, dest: &mut W) -> fmt::Result
|
||||
where W: fmt::Write,
|
||||
{
|
||||
write!(dest, "{}%", self.0 * 100.)
|
||||
if self.calc_clamping_mode.is_some() {
|
||||
dest.write_str("calc(")?;
|
||||
}
|
||||
|
||||
write!(dest, "{}%", self.value * 100.)?;
|
||||
|
||||
if self.calc_clamping_mode.is_some() {
|
||||
dest.write_str(")")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Percentage {
|
||||
/// Create a percentage from a numeric value.
|
||||
pub fn new(value: CSSFloat) -> Self {
|
||||
Self {
|
||||
value,
|
||||
calc_clamping_mode: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the underlying value for this float.
|
||||
pub fn get(&self) -> CSSFloat {
|
||||
self.calc_clamping_mode.map_or(self.value, |mode| mode.clamp(self.value))
|
||||
}
|
||||
|
||||
/// Reverse this percentage, preserving calc-ness.
|
||||
///
|
||||
/// For example: If it was 20%, convert it into 80%.
|
||||
pub fn reverse(&mut self) {
|
||||
let new_value = 1. - self.value;
|
||||
self.value = new_value;
|
||||
}
|
||||
|
||||
|
||||
/// Parse a specific kind of percentage.
|
||||
pub fn parse_with_clamping_mode<'i, 't>(context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
num_context: AllowedNumericType)
|
||||
-> Result<Self, ParseError<'i>> {
|
||||
pub fn parse_with_clamping_mode<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>,
|
||||
num_context: AllowedNumericType,
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
match input.next()? {
|
||||
Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
|
||||
Ok(Percentage(unit_value))
|
||||
Ok(Percentage::new(unit_value))
|
||||
}
|
||||
Token::Function(ref name) if name.eq_ignore_ascii_case("calc") => {
|
||||
let result = input.parse_nested_block(|i| {
|
||||
CalcNode::parse_percentage(context, i)
|
||||
})?;
|
||||
|
||||
// TODO(emilio): -moz-image-rect is the only thing that uses
|
||||
// the clamping mode... I guess we could disallow it...
|
||||
Ok(Percentage {
|
||||
value: result,
|
||||
calc_clamping_mode: Some(num_context),
|
||||
})
|
||||
}
|
||||
t => Err(BasicParseError::UnexpectedToken(t).into())
|
||||
}
|
||||
|
@ -749,32 +791,39 @@ impl Percentage {
|
|||
/// 0%
|
||||
#[inline]
|
||||
pub fn zero() -> Self {
|
||||
Percentage(0.)
|
||||
Percentage {
|
||||
value: 0.,
|
||||
calc_clamping_mode: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 100%
|
||||
#[inline]
|
||||
pub fn hundred() -> Self {
|
||||
Percentage(1.)
|
||||
Percentage {
|
||||
value: 1.,
|
||||
calc_clamping_mode: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for Percentage {
|
||||
#[inline]
|
||||
fn parse<'i, 't>(context: &ParserContext, input: &mut Parser<'i, 't>) -> Result<Self, ParseError<'i>> {
|
||||
fn parse<'i, 't>(
|
||||
context: &ParserContext,
|
||||
input: &mut Parser<'i, 't>
|
||||
) -> Result<Self, ParseError<'i>> {
|
||||
Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
|
||||
}
|
||||
}
|
||||
|
||||
impl ComputedValueAsSpecified for Percentage {}
|
||||
|
||||
/// A length or a percentage value.
|
||||
#[allow(missing_docs)]
|
||||
#[cfg_attr(feature = "servo", derive(HeapSizeOf))]
|
||||
#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
|
||||
pub enum LengthOrPercentage {
|
||||
Length(NoCalcLength),
|
||||
Percentage(Percentage),
|
||||
Percentage(computed::Percentage),
|
||||
Calc(Box<CalcLengthOrPercentage>),
|
||||
}
|
||||
|
||||
|
@ -797,6 +846,20 @@ impl From<NoCalcLength> for LengthOrPercentage {
|
|||
impl From<Percentage> for LengthOrPercentage {
|
||||
#[inline]
|
||||
fn from(pc: Percentage) -> Self {
|
||||
if pc.calc_clamping_mode.is_some() {
|
||||
LengthOrPercentage::Calc(Box::new(CalcLengthOrPercentage {
|
||||
percentage: Some(computed::Percentage(pc.get())),
|
||||
.. Default::default()
|
||||
}))
|
||||
} else {
|
||||
LengthOrPercentage::Percentage(computed::Percentage(pc.get()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<computed::Percentage> for LengthOrPercentage {
|
||||
#[inline]
|
||||
fn from(pc: computed::Percentage) -> Self {
|
||||
LengthOrPercentage::Percentage(pc)
|
||||
}
|
||||
}
|
||||
|
@ -820,7 +883,7 @@ impl LengthOrPercentage {
|
|||
NoCalcLength::parse_dimension(context, value, unit).map(LengthOrPercentage::Length)
|
||||
}
|
||||
Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
|
||||
return Ok(LengthOrPercentage::Percentage(Percentage(unit_value)))
|
||||
return Ok(LengthOrPercentage::Percentage(computed::Percentage(unit_value)))
|
||||
}
|
||||
Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
|
||||
if value != 0. &&
|
||||
|
@ -925,7 +988,7 @@ impl LengthOrPercentage {
|
|||
#[derive(Clone, Debug, HasViewportPercentage, PartialEq, ToCss)]
|
||||
pub enum LengthOrPercentageOrAuto {
|
||||
Length(NoCalcLength),
|
||||
Percentage(Percentage),
|
||||
Percentage(computed::Percentage),
|
||||
Auto,
|
||||
Calc(Box<CalcLengthOrPercentage>),
|
||||
}
|
||||
|
@ -937,9 +1000,9 @@ impl From<NoCalcLength> for LengthOrPercentageOrAuto {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<Percentage> for LengthOrPercentageOrAuto {
|
||||
impl From<computed::Percentage> for LengthOrPercentageOrAuto {
|
||||
#[inline]
|
||||
fn from(pc: Percentage) -> Self {
|
||||
fn from(pc: computed::Percentage) -> Self {
|
||||
LengthOrPercentageOrAuto::Percentage(pc)
|
||||
}
|
||||
}
|
||||
|
@ -956,7 +1019,7 @@ impl LengthOrPercentageOrAuto {
|
|||
NoCalcLength::parse_dimension(context, value, unit).map(LengthOrPercentageOrAuto::Length)
|
||||
}
|
||||
Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
|
||||
Ok(LengthOrPercentageOrAuto::Percentage(Percentage(unit_value)))
|
||||
Ok(LengthOrPercentageOrAuto::Percentage(computed::Percentage(unit_value)))
|
||||
}
|
||||
Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
|
||||
if value != 0. &&
|
||||
|
@ -1009,7 +1072,7 @@ impl LengthOrPercentageOrAuto {
|
|||
|
||||
/// Returns a value representing `0%`.
|
||||
pub fn zero_percent() -> Self {
|
||||
LengthOrPercentageOrAuto::Percentage(Percentage::zero())
|
||||
LengthOrPercentageOrAuto::Percentage(computed::Percentage::zero())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1037,7 +1100,7 @@ impl LengthOrPercentageOrAuto {
|
|||
#[allow(missing_docs)]
|
||||
pub enum LengthOrPercentageOrNone {
|
||||
Length(NoCalcLength),
|
||||
Percentage(Percentage),
|
||||
Percentage(computed::Percentage),
|
||||
Calc(Box<CalcLengthOrPercentage>),
|
||||
None,
|
||||
}
|
||||
|
@ -1055,7 +1118,7 @@ impl LengthOrPercentageOrNone {
|
|||
NoCalcLength::parse_dimension(context, value, unit).map(LengthOrPercentageOrNone::Length)
|
||||
}
|
||||
Token::Percentage { unit_value, .. } if num_context.is_ok(context.parsing_mode, unit_value) => {
|
||||
Ok(LengthOrPercentageOrNone::Percentage(Percentage(unit_value)))
|
||||
Ok(LengthOrPercentageOrNone::Percentage(computed::Percentage(unit_value)))
|
||||
}
|
||||
Token::Number { value, .. } if num_context.is_ok(context.parsing_mode, value) => {
|
||||
if value != 0. && !context.parsing_mode.allows_unitless_lengths() &&
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue