1use crate::color::{mix::ColorInterpolationMethod, AbsoluteColor, ColorFunction};
8use crate::values::{
9 computed::ToComputedValue, specified::percentage::ToPercentage, ParseError, Parser,
10};
11use std::fmt::{self, Write};
12use style_traits::{CssWriter, ToCss};
13
14#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem, ToTyped)]
17#[repr(C)]
18pub enum GenericColor<Percentage> {
19 Absolute(AbsoluteColor),
21 ColorFunction(Box<ColorFunction<Self>>),
23 CurrentColor,
25 ColorMix(Box<GenericColorMix<Self, Percentage>>),
27 ContrastColor(Box<Self>),
29}
30
31#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]
33#[repr(C)]
34pub struct ColorMixFlags(u8);
35bitflags! {
36 impl ColorMixFlags : u8 {
37 const NORMALIZE_WEIGHTS = 1 << 0;
39 const RESULT_IN_MODERN_SYNTAX = 1 << 1;
41 }
42}
43
44#[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 GenericColorMix<Color, Percentage> {
61 pub interpolation: ColorInterpolationMethod,
62 pub left: Color,
63 pub left_percentage: Percentage,
64 pub right: Color,
65 pub right_percentage: Percentage,
66 pub flags: ColorMixFlags,
67}
68
69pub use self::GenericColorMix as ColorMix;
70
71impl<Color: ToCss, Percentage: ToCss + ToPercentage> ToCss for ColorMix<Color, Percentage> {
72 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
73 where
74 W: Write,
75 {
76 fn can_omit<Percentage: ToPercentage>(
77 percent: &Percentage,
78 other: &Percentage,
79 is_left: bool,
80 ) -> bool {
81 if percent.is_calc() {
82 return false;
83 }
84 if percent.to_percentage() == 0.5 {
85 return other.to_percentage() == 0.5;
86 }
87 if is_left {
88 return false;
89 }
90 (1.0 - percent.to_percentage() - other.to_percentage()).abs() <= f32::EPSILON
91 }
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 self.left.to_css(dest)?;
104 if !can_omit(&self.left_percentage, &self.right_percentage, true) {
105 dest.write_char(' ')?;
106 self.left_percentage.to_css(dest)?;
107 }
108 dest.write_str(", ")?;
109 self.right.to_css(dest)?;
110 if !can_omit(&self.right_percentage, &self.left_percentage, false) {
111 dest.write_char(' ')?;
112 self.right_percentage.to_css(dest)?;
113 }
114 dest.write_char(')')
115 }
116}
117
118impl<Percentage> ColorMix<GenericColor<Percentage>, Percentage> {
119 pub fn mix_to_absolute(&self) -> Option<AbsoluteColor>
122 where
123 Percentage: ToPercentage,
124 {
125 let left = self.left.as_absolute()?;
126 let right = self.right.as_absolute()?;
127
128 Some(crate::color::mix::mix(
129 self.interpolation,
130 &left,
131 self.left_percentage.to_percentage(),
132 &right,
133 self.right_percentage.to_percentage(),
134 self.flags,
135 ))
136 }
137}
138
139pub use self::GenericColor as Color;
140
141impl<Percentage> Color<Percentage> {
142 pub fn as_absolute(&self) -> Option<&AbsoluteColor> {
144 match *self {
145 Self::Absolute(ref absolute) => Some(absolute),
146 _ => None,
147 }
148 }
149
150 pub fn currentcolor() -> Self {
152 Self::CurrentColor
153 }
154
155 pub fn is_currentcolor(&self) -> bool {
157 matches!(*self, Self::CurrentColor)
158 }
159
160 pub fn is_absolute(&self) -> bool {
162 matches!(*self, Self::Absolute(..))
163 }
164}
165
166#[derive(
168 Animate,
169 Clone,
170 ComputeSquaredDistance,
171 Copy,
172 Debug,
173 MallocSizeOf,
174 PartialEq,
175 Parse,
176 SpecifiedValueInfo,
177 ToAnimatedValue,
178 ToAnimatedZero,
179 ToComputedValue,
180 ToResolvedValue,
181 ToCss,
182 ToShmem,
183 ToTyped,
184)]
185#[repr(C, u8)]
186pub enum GenericColorOrAuto<C> {
187 Color(C),
189 Auto,
191}
192
193pub use self::GenericColorOrAuto as ColorOrAuto;
194
195#[derive(
198 Animate,
199 Clone,
200 ComputeSquaredDistance,
201 Copy,
202 Debug,
203 MallocSizeOf,
204 PartialEq,
205 SpecifiedValueInfo,
206 ToAnimatedValue,
207 ToAnimatedZero,
208 ToComputedValue,
209 ToCss,
210 ToShmem,
211 ToTyped,
212)]
213#[repr(transparent)]
214pub struct GenericCaretColor<C>(pub GenericColorOrAuto<C>);
215
216impl<C> GenericCaretColor<C> {
217 pub fn auto() -> Self {
219 GenericCaretColor(GenericColorOrAuto::Auto)
220 }
221}
222
223pub use self::GenericCaretColor as CaretColor;
224
225#[derive(
227 Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem, ToCss, ToResolvedValue,
228)]
229#[css(function = "light-dark", comma)]
230#[repr(C)]
231pub struct GenericLightDark<T> {
232 pub light: T,
234 pub dark: T,
236}
237
238impl<T> GenericLightDark<T> {
239 pub fn parse_args_with<'i>(
241 input: &mut Parser<'i, '_>,
242 mut parse_one: impl FnMut(&mut Parser<'i, '_>) -> Result<T, ParseError<'i>>,
243 ) -> Result<Self, ParseError<'i>> {
244 let light = parse_one(input)?;
245 input.expect_comma()?;
246 let dark = parse_one(input)?;
247 Ok(Self { light, dark })
248 }
249
250 pub fn parse_with<'i>(
252 input: &mut Parser<'i, '_>,
253 parse_one: impl FnMut(&mut Parser<'i, '_>) -> Result<T, ParseError<'i>>,
254 ) -> Result<Self, ParseError<'i>> {
255 input.expect_function_matching("light-dark")?;
256 input.parse_nested_block(|input| Self::parse_args_with(input, parse_one))
257 }
258}
259
260impl<T: ToComputedValue> GenericLightDark<T> {
261 pub fn compute(&self, cx: &crate::values::computed::Context) -> T::ComputedValue {
263 let dark = cx.device().is_dark_color_scheme(cx.builder.color_scheme);
264 if cx.for_non_inherited_property {
265 cx.rule_cache_conditions
266 .borrow_mut()
267 .set_color_scheme_dependency(cx.builder.color_scheme);
268 }
269 let chosen = if dark { &self.dark } else { &self.light };
270 chosen.to_computed_value(cx)
271 }
272}