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