1use crate::color::{mix::ColorInterpolationMethod, AbsoluteColor, ColorFunction};
8use crate::values::{specified::percentage::ToPercentage, computed::ToComputedValue, Parser, ParseError};
9use std::fmt::{self, Write};
10use style_traits::{CssWriter, ToCss};
11
12#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem)]
15#[repr(C)]
16pub enum GenericColor<Percentage> {
17 Absolute(AbsoluteColor),
19 ColorFunction(Box<ColorFunction<Self>>),
21 CurrentColor,
23 ColorMix(Box<GenericColorMix<Self, Percentage>>),
25}
26
27#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]
29#[repr(C)]
30pub struct ColorMixFlags(u8);
31bitflags! {
32 impl ColorMixFlags : u8 {
33 const NORMALIZE_WEIGHTS = 1 << 0;
35 const RESULT_IN_MODERN_SYNTAX = 1 << 1;
37 }
38}
39
40#[derive(
45 Clone,
46 Debug,
47 MallocSizeOf,
48 PartialEq,
49 ToAnimatedValue,
50 ToComputedValue,
51 ToResolvedValue,
52 ToShmem,
53)]
54#[allow(missing_docs)]
55#[repr(C)]
56pub struct GenericColorMix<Color, Percentage> {
57 pub interpolation: ColorInterpolationMethod,
58 pub left: Color,
59 pub left_percentage: Percentage,
60 pub right: Color,
61 pub right_percentage: Percentage,
62 pub flags: ColorMixFlags,
63}
64
65pub use self::GenericColorMix as ColorMix;
66
67impl<Color: ToCss, Percentage: ToCss + ToPercentage> ToCss for ColorMix<Color, Percentage> {
68 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
69 where
70 W: Write,
71 {
72 fn can_omit<Percentage: ToPercentage>(
73 percent: &Percentage,
74 other: &Percentage,
75 is_left: bool,
76 ) -> bool {
77 if percent.is_calc() {
78 return false;
79 }
80 if percent.to_percentage() == 0.5 {
81 return other.to_percentage() == 0.5;
82 }
83 if is_left {
84 return false;
85 }
86 (1.0 - percent.to_percentage() - other.to_percentage()).abs() <= f32::EPSILON
87 }
88
89 dest.write_str("color-mix(")?;
90 self.interpolation.to_css(dest)?;
91 dest.write_str(", ")?;
92 self.left.to_css(dest)?;
93 if !can_omit(&self.left_percentage, &self.right_percentage, true) {
94 dest.write_char(' ')?;
95 self.left_percentage.to_css(dest)?;
96 }
97 dest.write_str(", ")?;
98 self.right.to_css(dest)?;
99 if !can_omit(&self.right_percentage, &self.left_percentage, false) {
100 dest.write_char(' ')?;
101 self.right_percentage.to_css(dest)?;
102 }
103 dest.write_char(')')
104 }
105}
106
107impl<Percentage> ColorMix<GenericColor<Percentage>, Percentage> {
108 pub fn mix_to_absolute(&self) -> Option<AbsoluteColor>
111 where
112 Percentage: ToPercentage,
113 {
114 let left = self.left.as_absolute()?;
115 let right = self.right.as_absolute()?;
116
117 Some(crate::color::mix::mix(
118 self.interpolation,
119 &left,
120 self.left_percentage.to_percentage(),
121 &right,
122 self.right_percentage.to_percentage(),
123 self.flags,
124 ))
125 }
126}
127
128pub use self::GenericColor as Color;
129
130impl<Percentage> Color<Percentage> {
131 pub fn as_absolute(&self) -> Option<&AbsoluteColor> {
133 match *self {
134 Self::Absolute(ref absolute) => Some(absolute),
135 _ => None,
136 }
137 }
138
139 pub fn currentcolor() -> Self {
141 Self::CurrentColor
142 }
143
144 pub fn is_currentcolor(&self) -> bool {
146 matches!(*self, Self::CurrentColor)
147 }
148
149 pub fn is_absolute(&self) -> bool {
151 matches!(*self, Self::Absolute(..))
152 }
153}
154
155#[derive(
157 Animate,
158 Clone,
159 ComputeSquaredDistance,
160 Copy,
161 Debug,
162 MallocSizeOf,
163 PartialEq,
164 Parse,
165 SpecifiedValueInfo,
166 ToAnimatedValue,
167 ToAnimatedZero,
168 ToComputedValue,
169 ToResolvedValue,
170 ToCss,
171 ToShmem,
172)]
173#[repr(C, u8)]
174pub enum GenericColorOrAuto<C> {
175 Color(C),
177 Auto,
179}
180
181pub use self::GenericColorOrAuto as ColorOrAuto;
182
183#[derive(
186 Animate,
187 Clone,
188 ComputeSquaredDistance,
189 Copy,
190 Debug,
191 MallocSizeOf,
192 PartialEq,
193 SpecifiedValueInfo,
194 ToAnimatedValue,
195 ToAnimatedZero,
196 ToComputedValue,
197 ToCss,
198 ToShmem,
199)]
200#[repr(transparent)]
201pub struct GenericCaretColor<C>(pub GenericColorOrAuto<C>);
202
203impl<C> GenericCaretColor<C> {
204 pub fn auto() -> Self {
206 GenericCaretColor(GenericColorOrAuto::Auto)
207 }
208}
209
210pub use self::GenericCaretColor as CaretColor;
211
212#[derive(Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem, ToCss, ToResolvedValue)]
214#[css(function, comma)]
215#[repr(C)]
216pub struct GenericLightDark<T> {
217 pub light: T,
219 pub dark: T,
221}
222
223impl<T> GenericLightDark<T> {
224 pub fn parse_args_with<'i>(
226 input: &mut Parser<'i, '_>,
227 mut parse_one: impl FnMut(&mut Parser<'i, '_>) -> Result<T, ParseError<'i>>,
228 ) -> Result<Self, ParseError<'i>> {
229 let light = parse_one(input)?;
230 input.expect_comma()?;
231 let dark = parse_one(input)?;
232 Ok(Self { light, dark })
233 }
234
235 pub fn parse_with<'i>(
237 input: &mut Parser<'i, '_>,
238 parse_one: impl FnMut(&mut Parser<'i, '_>) -> Result<T, ParseError<'i>>,
239 ) -> Result<Self, ParseError<'i>> {
240 input.expect_function_matching("light-dark")?;
241 input.parse_nested_block(|input| Self::parse_args_with(input, parse_one))
242 }
243}
244
245impl<T: ToComputedValue> GenericLightDark<T> {
246 pub fn compute(&self, cx: &crate::values::computed::Context) -> T::ComputedValue {
248 let dark = cx.device().is_dark_color_scheme(cx.builder.color_scheme);
249 if cx.for_non_inherited_property {
250 cx.rule_cache_conditions
251 .borrow_mut()
252 .set_color_scheme_dependency(cx.builder.color_scheme);
253 }
254 let chosen = if dark { &self.dark } else { &self.light };
255 chosen.to_computed_value(cx)
256 }
257}