use crate::parser::{Parse, ParserContext};
use crate::values::computed::percentage::Percentage as ComputedPercentage;
use crate::values::computed::{Context, ToComputedValue};
use crate::values::generics::NonNegative;
use crate::values::specified::calc::CalcNode;
use crate::values::specified::Number;
use crate::values::{normalize, serialize_percentage, CSSFloat};
use cssparser::{Parser, Token};
use std::fmt::{self, Write};
use style_traits::values::specified::AllowedNumericType;
use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, ToCss};
#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]
pub struct Percentage {
value: CSSFloat,
calc_clamping_mode: Option<AllowedNumericType>,
}
impl ToCss for Percentage {
fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
where
W: Write,
{
if self.calc_clamping_mode.is_some() {
dest.write_str("calc(")?;
}
serialize_percentage(self.value, dest)?;
if self.calc_clamping_mode.is_some() {
dest.write_char(')')?;
}
Ok(())
}
}
impl Percentage {
pub(super) fn new_with_clamping_mode(
value: CSSFloat,
calc_clamping_mode: Option<AllowedNumericType>,
) -> Self {
Self {
value,
calc_clamping_mode,
}
}
pub fn new(value: CSSFloat) -> Self {
Self::new_with_clamping_mode(value, None)
}
#[inline]
pub fn zero() -> Self {
Percentage {
value: 0.,
calc_clamping_mode: None,
}
}
#[inline]
pub fn hundred() -> Self {
Percentage {
value: 1.,
calc_clamping_mode: None,
}
}
pub fn get(&self) -> CSSFloat {
self.calc_clamping_mode
.map_or(self.value, |mode| mode.clamp(self.value))
}
pub fn to_number(&self) -> Number {
Number::new_with_clamping_mode(self.value, self.calc_clamping_mode)
}
pub fn calc_clamping_mode(&self) -> Option<AllowedNumericType> {
self.calc_clamping_mode
}
pub fn reverse(&mut self) {
let new_value = 1. - self.value;
self.value = new_value;
}
pub fn parse_with_clamping_mode<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
num_context: AllowedNumericType,
) -> Result<Self, ParseError<'i>> {
let location = input.current_source_location();
match *input.next()? {
Token::Percentage { unit_value, .. }
if num_context.is_ok(context.parsing_mode, unit_value) =>
{
Ok(Percentage::new(unit_value))
},
Token::Function(ref name) => {
let function = CalcNode::math_function(context, name, location)?;
let value = CalcNode::parse_percentage(context, input, function)?;
Ok(Percentage {
value,
calc_clamping_mode: Some(num_context),
})
},
ref t => Err(location.new_unexpected_token_error(t.clone())),
}
}
pub fn parse_non_negative<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
}
pub fn parse_zero_to_a_hundred<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
Self::parse_with_clamping_mode(context, input, AllowedNumericType::ZeroToOne)
}
#[inline]
pub fn clamp_to_hundred(self) -> Self {
Percentage {
value: self.value.min(1.),
calc_clamping_mode: self.calc_clamping_mode,
}
}
}
impl Parse for Percentage {
#[inline]
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 ToComputedValue for Percentage {
type ComputedValue = ComputedPercentage;
#[inline]
fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
ComputedPercentage(normalize(self.get()))
}
#[inline]
fn from_computed_value(computed: &Self::ComputedValue) -> Self {
Percentage::new(computed.0)
}
}
impl SpecifiedValueInfo for Percentage {}
pub trait ToPercentage {
fn is_calc(&self) -> bool {
false
}
fn to_percentage(&self) -> CSSFloat;
}
impl ToPercentage for Percentage {
fn is_calc(&self) -> bool {
self.calc_clamping_mode.is_some()
}
fn to_percentage(&self) -> CSSFloat {
self.get()
}
}
pub type NonNegativePercentage = NonNegative<Percentage>;
impl Parse for NonNegativePercentage {
#[inline]
fn parse<'i, 't>(
context: &ParserContext,
input: &mut Parser<'i, 't>,
) -> Result<Self, ParseError<'i>> {
Ok(NonNegative(Percentage::parse_non_negative(context, input)?))
}
}
impl NonNegativePercentage {
#[inline]
pub fn compute(&self) -> ComputedPercentage {
ComputedPercentage(self.0.get())
}
}