1use crate::color::ColorMixItemList;
8use crate::color::{mix::ColorInterpolationMethod, AbsoluteColor, ColorFunction};
9use crate::derives::*;
10use crate::values::{
11 computed::ToComputedValue, specified::percentage::ToPercentage, ParseError, Parser,
12};
13use std::fmt::{self, Write};
14use style_traits::{owned_slice::OwnedSlice, CssWriter, ToCss};
15
16#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem)]
19#[repr(C)]
20pub enum GenericColor<Percentage> {
21 Absolute(AbsoluteColor),
23 ColorFunction(Box<ColorFunction<Self>>),
25 CurrentColor,
27 ColorMix(Box<GenericColorMix<Self, Percentage>>),
29 ContrastColor(Box<Self>),
31}
32
33#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]
35#[repr(C)]
36pub struct ColorMixFlags(u8);
37bitflags! {
38 impl ColorMixFlags : u8 {
39 const NORMALIZE_WEIGHTS = 1 << 0;
41 const RESULT_IN_MODERN_SYNTAX = 1 << 1;
43 }
44}
45
46#[derive(
48 Clone,
49 Debug,
50 MallocSizeOf,
51 PartialEq,
52 ToAnimatedValue,
53 ToComputedValue,
54 ToResolvedValue,
55 ToShmem,
56)]
57#[allow(missing_docs)]
58#[repr(C)]
59pub struct GenericColorMixItem<Color, Percentage> {
60 pub color: Color,
61 pub percentage: Percentage,
62}
63
64#[derive(
69 Clone,
70 Debug,
71 MallocSizeOf,
72 PartialEq,
73 ToAnimatedValue,
74 ToComputedValue,
75 ToResolvedValue,
76 ToShmem,
77)]
78#[allow(missing_docs)]
79#[repr(C)]
80pub struct GenericColorMix<Color, Percentage> {
81 pub interpolation: ColorInterpolationMethod,
82 pub items: OwnedSlice<GenericColorMixItem<Color, Percentage>>,
83 pub flags: ColorMixFlags,
84}
85
86pub use self::GenericColorMix as ColorMix;
87
88impl<Color: ToCss, Percentage: ToCss + ToPercentage> ToCss for ColorMix<Color, Percentage> {
89 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
90 where
91 W: Write,
92 {
93 dest.write_str("color-mix(")?;
94
95 if !self.interpolation.is_default() {
99 self.interpolation.to_css(dest)?;
100 dest.write_str(", ")?;
101 }
102
103 let uniform = self
104 .items
105 .split_first()
106 .map(|(first, rest)| {
107 rest.iter()
108 .all(|item| item.percentage.to_percentage() == first.percentage.to_percentage())
109 })
110 .unwrap_or(false);
111 let uniform_value = 1.0 / self.items.len() as f32;
112
113 let is_pair = self.items.len() == 2;
114
115 for (index, item) in self.items.iter().enumerate() {
116 if index != 0 {
117 dest.write_str(", ")?;
118 }
119
120 item.color.to_css(dest)?;
121
122 let omit = if is_pair {
123 let can_omit = |a: &Percentage, b: &Percentage, is_left| {
124 if a.is_calc() {
125 return false;
126 }
127 let a = a.to_percentage().unwrap();
130 let b = b.to_percentage().unwrap();
131 if a == 0.5 {
132 return b == 0.5;
133 }
134 if is_left {
135 return false;
136 }
137 (1.0 - a - b).abs() <= f32::EPSILON
138 };
139
140 let other = &self.items[1 - index].percentage;
141 can_omit(&item.percentage, other, index == 0)
142 } else {
143 !item.percentage.is_calc()
144 && uniform
145 && item.percentage.to_percentage() == Some(uniform_value)
146 };
147
148 if !omit {
149 dest.write_char(' ')?;
150 item.percentage.to_css(dest)?;
151 }
152 }
153
154 dest.write_char(')')
155 }
156}
157
158impl<Percentage> ColorMix<GenericColor<Percentage>, Percentage> {
159 pub fn mix_to_absolute(&self) -> Option<AbsoluteColor>
162 where
163 Percentage: ToPercentage,
164 {
165 use crate::color::mix;
166
167 let mut items = ColorMixItemList::with_capacity(self.items.len());
168 for item in self.items.iter() {
169 items.push(mix::ColorMixItem::new(
170 *item.color.as_absolute()?,
171 item.percentage.to_percentage()?,
172 ))
173 }
174
175 Some(mix::mix_many(self.interpolation, items, self.flags))
176 }
177}
178
179pub use self::GenericColor as Color;
180
181impl<Percentage> Color<Percentage> {
182 pub fn as_absolute(&self) -> Option<&AbsoluteColor> {
184 match *self {
185 Self::Absolute(ref absolute) => Some(absolute),
186 _ => None,
187 }
188 }
189
190 pub fn currentcolor() -> Self {
192 Self::CurrentColor
193 }
194
195 pub fn is_currentcolor(&self) -> bool {
197 matches!(*self, Self::CurrentColor)
198 }
199
200 pub fn is_absolute(&self) -> bool {
202 matches!(*self, Self::Absolute(..))
203 }
204}
205
206#[derive(
208 Animate,
209 Clone,
210 ComputeSquaredDistance,
211 Copy,
212 Debug,
213 MallocSizeOf,
214 PartialEq,
215 Parse,
216 SpecifiedValueInfo,
217 ToAnimatedValue,
218 ToAnimatedZero,
219 ToComputedValue,
220 ToResolvedValue,
221 ToCss,
222 ToShmem,
223 ToTyped,
224)]
225#[repr(C, u8)]
226pub enum GenericColorOrAuto<C> {
227 Color(C),
229 Auto,
231}
232
233pub use self::GenericColorOrAuto as ColorOrAuto;
234
235#[derive(
238 Animate,
239 Clone,
240 ComputeSquaredDistance,
241 Copy,
242 Debug,
243 MallocSizeOf,
244 PartialEq,
245 SpecifiedValueInfo,
246 ToAnimatedValue,
247 ToAnimatedZero,
248 ToComputedValue,
249 ToCss,
250 ToShmem,
251 ToTyped,
252)]
253#[repr(transparent)]
254pub struct GenericCaretColor<C>(pub GenericColorOrAuto<C>);
255
256impl<C> GenericCaretColor<C> {
257 pub fn auto() -> Self {
259 GenericCaretColor(GenericColorOrAuto::Auto)
260 }
261}
262
263pub use self::GenericCaretColor as CaretColor;
264
265#[derive(
267 Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem, ToCss, ToResolvedValue,
268)]
269#[css(function = "light-dark", comma)]
270#[repr(C)]
271pub struct GenericLightDark<T> {
272 pub light: T,
274 pub dark: T,
276}
277
278impl<T> GenericLightDark<T> {
279 pub fn parse_args_with<'i>(
281 input: &mut Parser<'i, '_>,
282 mut parse_one: impl FnMut(&mut Parser<'i, '_>) -> Result<T, ParseError<'i>>,
283 ) -> Result<Self, ParseError<'i>> {
284 let light = parse_one(input)?;
285 input.expect_comma()?;
286 let dark = parse_one(input)?;
287 Ok(Self { light, dark })
288 }
289
290 pub fn parse_with<'i>(
292 input: &mut Parser<'i, '_>,
293 parse_one: impl FnMut(&mut Parser<'i, '_>) -> Result<T, ParseError<'i>>,
294 ) -> Result<Self, ParseError<'i>> {
295 input.expect_function_matching("light-dark")?;
296 input.parse_nested_block(|input| Self::parse_args_with(input, parse_one))
297 }
298}
299
300impl<T: ToComputedValue> GenericLightDark<T> {
301 pub fn compute(&self, cx: &crate::values::computed::Context) -> T::ComputedValue {
303 let dark = cx.device().is_dark_color_scheme(cx.builder.color_scheme);
304 if cx.for_non_inherited_property {
305 cx.rule_cache_conditions
306 .borrow_mut()
307 .set_color_scheme_dependency(cx.builder.color_scheme);
308 }
309 let chosen = if dark { &self.dark } else { &self.light };
310 chosen.to_computed_value(cx)
311 }
312}