style/values/specified/
percentage.rs1use crate::derives::*;
8use crate::parser::{Parse, ParserContext};
9use crate::values::computed::percentage::Percentage as ComputedPercentage;
10use crate::values::computed::{Context, ToComputedValue};
11use crate::values::generics::NonNegative;
12use crate::values::specified::calc::CalcNode;
13use crate::values::specified::Number;
14use crate::values::{normalize, serialize_percentage, CSSFloat};
15use cssparser::{Parser, Token};
16use std::fmt::{self, Write};
17use style_traits::values::specified::AllowedNumericType;
18use style_traits::{CssWriter, ParseError, SpecifiedValueInfo, ToCss};
19
20#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem, ToTyped)]
22pub struct Percentage {
23 value: CSSFloat,
27 calc_clamping_mode: Option<AllowedNumericType>,
30}
31
32impl ToCss for Percentage {
33 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
34 where
35 W: Write,
36 {
37 if self.calc_clamping_mode.is_some() {
38 dest.write_str("calc(")?;
39 }
40
41 serialize_percentage(self.value, dest)?;
42
43 if self.calc_clamping_mode.is_some() {
44 dest.write_char(')')?;
45 }
46 Ok(())
47 }
48}
49
50impl Percentage {
51 pub(super) fn new_with_clamping_mode(
53 value: CSSFloat,
54 calc_clamping_mode: Option<AllowedNumericType>,
55 ) -> Self {
56 Self {
57 value,
58 calc_clamping_mode,
59 }
60 }
61
62 pub fn new(value: CSSFloat) -> Self {
64 Self::new_with_clamping_mode(value, None)
65 }
66
67 #[inline]
69 pub fn zero() -> Self {
70 Percentage {
71 value: 0.,
72 calc_clamping_mode: None,
73 }
74 }
75
76 #[inline]
78 pub fn hundred() -> Self {
79 Percentage {
80 value: 1.,
81 calc_clamping_mode: None,
82 }
83 }
84
85 pub fn get(&self) -> CSSFloat {
87 self.calc_clamping_mode
88 .map_or(self.value, |mode| mode.clamp(self.value))
89 }
90
91 pub fn to_number(&self) -> Number {
93 Number::new_with_clamping_mode(self.value, self.calc_clamping_mode)
94 }
95
96 pub fn calc_clamping_mode(&self) -> Option<AllowedNumericType> {
98 self.calc_clamping_mode
99 }
100
101 pub fn reverse(&mut self) {
105 let new_value = 1. - self.value;
106 self.value = new_value;
107 }
108
109 pub fn parse_with_clamping_mode<'i, 't>(
111 context: &ParserContext,
112 input: &mut Parser<'i, 't>,
113 num_context: AllowedNumericType,
114 ) -> Result<Self, ParseError<'i>> {
115 let location = input.current_source_location();
116 match *input.next()? {
117 Token::Percentage { unit_value, .. }
118 if num_context.is_ok(context.parsing_mode, unit_value) =>
119 {
120 Ok(Percentage::new(unit_value))
121 },
122 Token::Function(ref name) => {
123 let function = CalcNode::math_function(context, name, location)?;
124 let value = CalcNode::parse_percentage(context, input, function)?;
125 Ok(Percentage {
126 value,
127 calc_clamping_mode: Some(num_context),
128 })
129 },
130 ref t => Err(location.new_unexpected_token_error(t.clone())),
131 }
132 }
133
134 pub fn parse_non_negative<'i, 't>(
136 context: &ParserContext,
137 input: &mut Parser<'i, 't>,
138 ) -> Result<Self, ParseError<'i>> {
139 Self::parse_with_clamping_mode(context, input, AllowedNumericType::NonNegative)
140 }
141
142 pub fn parse_zero_to_a_hundred<'i, 't>(
145 context: &ParserContext,
146 input: &mut Parser<'i, 't>,
147 ) -> Result<Self, ParseError<'i>> {
148 Self::parse_with_clamping_mode(context, input, AllowedNumericType::ZeroToOne)
149 }
150
151 #[inline]
153 pub fn clamp_to_hundred(self) -> Self {
154 Percentage {
155 value: self.value.min(1.),
156 calc_clamping_mode: self.calc_clamping_mode,
157 }
158 }
159}
160
161impl Parse for Percentage {
162 #[inline]
163 fn parse<'i, 't>(
164 context: &ParserContext,
165 input: &mut Parser<'i, 't>,
166 ) -> Result<Self, ParseError<'i>> {
167 Self::parse_with_clamping_mode(context, input, AllowedNumericType::All)
168 }
169}
170
171impl ToComputedValue for Percentage {
172 type ComputedValue = ComputedPercentage;
173
174 #[inline]
175 fn to_computed_value(&self, _: &Context) -> Self::ComputedValue {
176 ComputedPercentage(normalize(self.get()))
177 }
178
179 #[inline]
180 fn from_computed_value(computed: &Self::ComputedValue) -> Self {
181 Percentage::new(computed.0)
182 }
183}
184
185impl SpecifiedValueInfo for Percentage {}
186
187pub trait ToPercentage {
189 fn is_calc(&self) -> bool {
191 false
192 }
193 fn to_percentage(&self) -> CSSFloat;
195}
196
197impl ToPercentage for Percentage {
198 fn is_calc(&self) -> bool {
199 self.calc_clamping_mode.is_some()
200 }
201
202 fn to_percentage(&self) -> CSSFloat {
203 self.get()
204 }
205}
206
207pub type NonNegativePercentage = NonNegative<Percentage>;
209
210impl Parse for NonNegativePercentage {
211 #[inline]
212 fn parse<'i, 't>(
213 context: &ParserContext,
214 input: &mut Parser<'i, 't>,
215 ) -> Result<Self, ParseError<'i>> {
216 Ok(NonNegative(Percentage::parse_non_negative(context, input)?))
217 }
218}
219
220impl NonNegativePercentage {
221 #[inline]
223 pub fn compute(&self) -> ComputedPercentage {
224 ComputedPercentage(self.0.get())
225 }
226}