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