style: Add resolution support to calc()

Differential Revision: https://phabricator.services.mozilla.com/D172338
This commit is contained in:
Emilio Cobos Álvarez 2023-03-13 11:33:22 +00:00 committed by Martin Robinson
parent 76e8eeda72
commit 2c986f0005
6 changed files with 171 additions and 47 deletions

View file

@ -7,67 +7,135 @@
//! https://drafts.csswg.org/css-values/#resolution
use crate::parser::{Parse, ParserContext};
use crate::values::specified::CalcNode;
use crate::values::CSSFloat;
use cssparser::{Parser, Token};
use style_traits::{ParseError, StyleParseErrorKind};
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};
/// A specified resolution.
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
pub enum Resolution {
#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem)]
pub struct Resolution {
value: CSSFloat,
unit: ResolutionUnit,
was_calc: bool,
}
#[derive(Clone, Copy, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToCss, ToShmem)]
enum ResolutionUnit {
/// Dots per inch.
#[css(dimension)]
Dpi(CSSFloat),
Dpi,
/// An alias unit for dots per pixel.
#[css(dimension)]
X(CSSFloat),
X,
/// Dots per pixel.
#[css(dimension)]
Dppx(CSSFloat),
Dppx,
/// Dots per centimeter.
#[css(dimension)]
Dpcm(CSSFloat),
Dpcm,
}
impl ResolutionUnit {
fn as_str(self) -> &'static str {
match self {
Self::Dpi => "dpi",
Self::X => "x",
Self::Dppx => "dppx",
Self::Dpcm => "dpcm",
}
}
}
impl Resolution {
/// Returns a resolution value from dppx units.
pub fn from_dppx(value: CSSFloat) -> Self {
Self {
value,
unit: ResolutionUnit::Dppx,
was_calc: false,
}
}
/// Returns a resolution value from dppx units.
pub fn from_x(value: CSSFloat) -> Self {
Self {
value,
unit: ResolutionUnit::X,
was_calc: false,
}
}
/// Returns a resolution value from dppx units.
pub fn from_dppx_calc(value: CSSFloat) -> Self {
Self {
value,
unit: ResolutionUnit::Dppx,
was_calc: true,
}
}
/// Convert this resolution value to dppx units.
pub fn to_dppx(&self) -> CSSFloat {
match *self {
Resolution::X(f) | Resolution::Dppx(f) => f,
_ => self.to_dpi() / 96.0,
pub fn dppx(&self) -> CSSFloat {
match self.unit {
ResolutionUnit::X | ResolutionUnit::Dppx => self.value,
_ => self.dpi() / 96.0,
}
}
/// Convert this resolution value to dpi units.
pub fn to_dpi(&self) -> CSSFloat {
match *self {
Resolution::Dpi(f) => f,
Resolution::X(f) | Resolution::Dppx(f) => f * 96.0,
Resolution::Dpcm(f) => f * 2.54,
pub fn dpi(&self) -> CSSFloat {
match self.unit {
ResolutionUnit::Dpi => self.value,
ResolutionUnit::X | ResolutionUnit::Dppx => self.value * 96.0,
ResolutionUnit::Dpcm => self.value * 2.54,
}
}
/// Parse a resolution given a value and unit.
pub fn parse_dimension<'i, 't>(value: CSSFloat, unit: &str) -> Result<Self, ()> {
let unit = match_ignore_ascii_case! { &unit,
"dpi" => ResolutionUnit::Dpi,
"dppx" => ResolutionUnit::Dppx,
"dpcm" => ResolutionUnit::Dpcm,
"x" => ResolutionUnit::X,
_ => return Err(())
};
Ok(Self {
value,
unit,
was_calc: false,
})
}
}
impl ToCss for Resolution {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
crate::values::serialize_specified_dimension(
self.value,
self.unit.as_str(),
self.was_calc,
dest,
)
}
}
impl Parse for Resolution {
fn parse<'i, 't>(
_: &ParserContext,
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
let location = input.current_source_location();
let (value, unit) = match *input.next()? {
match *input.next()? {
Token::Dimension {
value, ref unit, ..
} => (value, unit),
} => Self::parse_dimension(value, unit)
.map_err(|()| location.new_custom_error(StyleParseErrorKind::UnspecifiedError)),
Token::Function(ref name) => {
let function = CalcNode::math_function(name, location)?;
CalcNode::parse_resolution(context, input, function)
},
ref t => return Err(location.new_unexpected_token_error(t.clone())),
};
match_ignore_ascii_case! { &unit,
"dpi" => Ok(Resolution::Dpi(value)),
"dppx" => Ok(Resolution::Dppx(value)),
"dpcm" => Ok(Resolution::Dpcm(value)),
"x" => Ok(Resolution::X(value)),
_ => Err(location.new_custom_error(
StyleParseErrorKind::UnexpectedDimension(unit.clone())
)),
}
}
}