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