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 let a = a.to_percentage().unwrap();
131 let b = b.to_percentage().unwrap();
132 if a == 0.5 {
133 return b == 0.5;
134 }
135 if is_left {
136 return false;
137 }
138 (1.0 - a - b).abs() <= f32::EPSILON
139 };
140
141 let other = &self.items[1 - index].percentage;
142 can_omit(&item.percentage, other, index == 0)
143 } else {
144 !item.percentage.is_calc()
145 && uniform
146 && item.percentage.to_percentage() == Some(uniform_value)
147 };
148
149 if !omit {
150 dest.write_char(' ')?;
151 item.percentage.to_css(dest)?;
152 }
153 }
154
155 dest.write_char(')')
156 }
157}
158
159impl<Percentage> ColorMix<GenericColor<Percentage>, Percentage> {
160 pub fn mix_to_absolute(&self) -> Option<AbsoluteColor>
163 where
164 Percentage: ToPercentage,
165 {
166 use crate::color::mix;
167
168 let mut items = ColorMixItemList::with_capacity(self.items.len());
169 for item in self.items.iter() {
170 items.push(mix::ColorMixItem::new(
171 *item.color.as_absolute()?,
172 item.percentage.to_percentage()?,
173 ))
174 }
175
176 Some(mix::mix_many(self.interpolation, items, self.flags))
177 }
178}
179
180pub use self::GenericColor as Color;
181
182impl<Percentage> Color<Percentage> {
183 pub fn as_absolute(&self) -> Option<&AbsoluteColor> {
185 match *self {
186 Self::Absolute(ref absolute) => Some(absolute),
187 _ => None,
188 }
189 }
190
191 pub fn currentcolor() -> Self {
193 Self::CurrentColor
194 }
195
196 pub fn is_currentcolor(&self) -> bool {
198 matches!(*self, Self::CurrentColor)
199 }
200
201 pub fn is_absolute(&self) -> bool {
203 matches!(*self, Self::Absolute(..))
204 }
205}
206
207#[derive(
209 Animate,
210 Clone,
211 ComputeSquaredDistance,
212 Copy,
213 Debug,
214 MallocSizeOf,
215 PartialEq,
216 Parse,
217 SpecifiedValueInfo,
218 ToAnimatedValue,
219 ToAnimatedZero,
220 ToComputedValue,
221 ToResolvedValue,
222 ToCss,
223 ToShmem,
224 ToTyped,
225)]
226#[repr(C, u8)]
227pub enum GenericColorOrAuto<C> {
228 Color(C),
230 Auto,
232}
233
234pub use self::GenericColorOrAuto as ColorOrAuto;
235
236#[derive(
239 Animate,
240 Clone,
241 ComputeSquaredDistance,
242 Copy,
243 Debug,
244 MallocSizeOf,
245 PartialEq,
246 SpecifiedValueInfo,
247 ToAnimatedValue,
248 ToAnimatedZero,
249 ToComputedValue,
250 ToCss,
251 ToShmem,
252 ToTyped,
253)]
254#[repr(transparent)]
255pub struct GenericCaretColor<C>(pub GenericColorOrAuto<C>);
256
257impl<C> GenericCaretColor<C> {
258 pub fn auto() -> Self {
260 GenericCaretColor(GenericColorOrAuto::Auto)
261 }
262}
263
264pub use self::GenericCaretColor as CaretColor;
265
266#[derive(
268 Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem, ToCss, ToResolvedValue,
269)]
270#[css(function = "light-dark", comma)]
271#[repr(C)]
272pub struct GenericLightDark<T> {
273 pub light: T,
275 pub dark: T,
277}
278
279impl<T> GenericLightDark<T> {
280 pub fn parse_args_with<'i>(
282 input: &mut Parser<'i, '_>,
283 mut parse_one: impl FnMut(&mut Parser<'i, '_>) -> Result<T, ParseError<'i>>,
284 ) -> Result<Self, ParseError<'i>> {
285 let light = parse_one(input)?;
286 input.expect_comma()?;
287 let dark = parse_one(input)?;
288 Ok(Self { light, dark })
289 }
290
291 pub fn parse_with<'i>(
293 input: &mut Parser<'i, '_>,
294 parse_one: impl FnMut(&mut Parser<'i, '_>) -> Result<T, ParseError<'i>>,
295 ) -> Result<Self, ParseError<'i>> {
296 input.expect_function_matching("light-dark")?;
297 input.parse_nested_block(|input| Self::parse_args_with(input, parse_one))
298 }
299}
300
301impl<T: ToComputedValue> GenericLightDark<T> {
302 pub fn compute(&self, cx: &crate::values::computed::Context) -> T::ComputedValue {
304 let dark = cx.device().is_dark_color_scheme(cx.builder.color_scheme);
305 if cx.for_non_inherited_property {
306 cx.rule_cache_conditions
307 .borrow_mut()
308 .set_color_scheme_dependency(cx.builder.color_scheme);
309 }
310 let chosen = if dark { &self.dark } else { &self.light };
311 chosen.to_computed_value(cx)
312 }
313}