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}
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 ToTyped,
175)]
176#[repr(C, u8)]
177pub enum GenericColorOrAuto<C> {
178 Color(C),
180 Auto,
182}
183
184pub use self::GenericColorOrAuto as ColorOrAuto;
185
186#[derive(
189 Animate,
190 Clone,
191 ComputeSquaredDistance,
192 Copy,
193 Debug,
194 MallocSizeOf,
195 PartialEq,
196 SpecifiedValueInfo,
197 ToAnimatedValue,
198 ToAnimatedZero,
199 ToComputedValue,
200 ToCss,
201 ToShmem,
202 ToTyped,
203)]
204#[repr(transparent)]
205pub struct GenericCaretColor<C>(pub GenericColorOrAuto<C>);
206
207impl<C> GenericCaretColor<C> {
208 pub fn auto() -> Self {
210 GenericCaretColor(GenericColorOrAuto::Auto)
211 }
212}
213
214pub use self::GenericCaretColor as CaretColor;
215
216#[derive(
218 Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem, ToCss, ToResolvedValue,
219)]
220#[css(function = "light-dark", comma)]
221#[repr(C)]
222pub struct GenericLightDark<T> {
223 pub light: T,
225 pub dark: T,
227}
228
229impl<T> GenericLightDark<T> {
230 pub fn parse_args_with<'i>(
232 input: &mut Parser<'i, '_>,
233 mut parse_one: impl FnMut(&mut Parser<'i, '_>) -> Result<T, ParseError<'i>>,
234 ) -> Result<Self, ParseError<'i>> {
235 let light = parse_one(input)?;
236 input.expect_comma()?;
237 let dark = parse_one(input)?;
238 Ok(Self { light, dark })
239 }
240
241 pub fn parse_with<'i>(
243 input: &mut Parser<'i, '_>,
244 parse_one: impl FnMut(&mut Parser<'i, '_>) -> Result<T, ParseError<'i>>,
245 ) -> Result<Self, ParseError<'i>> {
246 input.expect_function_matching("light-dark")?;
247 input.parse_nested_block(|input| Self::parse_args_with(input, parse_one))
248 }
249}
250
251impl<T: ToComputedValue> GenericLightDark<T> {
252 pub fn compute(&self, cx: &crate::values::computed::Context) -> T::ComputedValue {
254 let dark = cx.device().is_dark_color_scheme(cx.builder.color_scheme);
255 if cx.for_non_inherited_property {
256 cx.rule_cache_conditions
257 .borrow_mut()
258 .set_color_scheme_dependency(cx.builder.color_scheme);
259 }
260 let chosen = if dark { &self.dark } else { &self.light };
261 chosen.to_computed_value(cx)
262 }
263}