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