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 self.interpolation.to_css(dest)?;
95 dest.write_str(", ")?;
96 self.left.to_css(dest)?;
97 if !can_omit(&self.left_percentage, &self.right_percentage, true) {
98 dest.write_char(' ')?;
99 self.left_percentage.to_css(dest)?;
100 }
101 dest.write_str(", ")?;
102 self.right.to_css(dest)?;
103 if !can_omit(&self.right_percentage, &self.left_percentage, false) {
104 dest.write_char(' ')?;
105 self.right_percentage.to_css(dest)?;
106 }
107 dest.write_char(')')
108 }
109}
110
111impl<Percentage> ColorMix<GenericColor<Percentage>, Percentage> {
112 pub fn mix_to_absolute(&self) -> Option<AbsoluteColor>
115 where
116 Percentage: ToPercentage,
117 {
118 let left = self.left.as_absolute()?;
119 let right = self.right.as_absolute()?;
120
121 Some(crate::color::mix::mix(
122 self.interpolation,
123 &left,
124 self.left_percentage.to_percentage(),
125 &right,
126 self.right_percentage.to_percentage(),
127 self.flags,
128 ))
129 }
130}
131
132pub use self::GenericColor as Color;
133
134impl<Percentage> Color<Percentage> {
135 pub fn as_absolute(&self) -> Option<&AbsoluteColor> {
137 match *self {
138 Self::Absolute(ref absolute) => Some(absolute),
139 _ => None,
140 }
141 }
142
143 pub fn currentcolor() -> Self {
145 Self::CurrentColor
146 }
147
148 pub fn is_currentcolor(&self) -> bool {
150 matches!(*self, Self::CurrentColor)
151 }
152
153 pub fn is_absolute(&self) -> bool {
155 matches!(*self, Self::Absolute(..))
156 }
157}
158
159#[derive(
161 Animate,
162 Clone,
163 ComputeSquaredDistance,
164 Copy,
165 Debug,
166 MallocSizeOf,
167 PartialEq,
168 Parse,
169 SpecifiedValueInfo,
170 ToAnimatedValue,
171 ToAnimatedZero,
172 ToComputedValue,
173 ToResolvedValue,
174 ToCss,
175 ToShmem,
176 ToTyped,
177)]
178#[repr(C, u8)]
179pub enum GenericColorOrAuto<C> {
180 Color(C),
182 Auto,
184}
185
186pub use self::GenericColorOrAuto as ColorOrAuto;
187
188#[derive(
191 Animate,
192 Clone,
193 ComputeSquaredDistance,
194 Copy,
195 Debug,
196 MallocSizeOf,
197 PartialEq,
198 SpecifiedValueInfo,
199 ToAnimatedValue,
200 ToAnimatedZero,
201 ToComputedValue,
202 ToCss,
203 ToShmem,
204 ToTyped,
205)]
206#[repr(transparent)]
207pub struct GenericCaretColor<C>(pub GenericColorOrAuto<C>);
208
209impl<C> GenericCaretColor<C> {
210 pub fn auto() -> Self {
212 GenericCaretColor(GenericColorOrAuto::Auto)
213 }
214}
215
216pub use self::GenericCaretColor as CaretColor;
217
218#[derive(
220 Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem, ToCss, ToResolvedValue,
221)]
222#[css(function = "light-dark", comma)]
223#[repr(C)]
224pub struct GenericLightDark<T> {
225 pub light: T,
227 pub dark: T,
229}
230
231impl<T> GenericLightDark<T> {
232 pub fn parse_args_with<'i>(
234 input: &mut Parser<'i, '_>,
235 mut parse_one: impl FnMut(&mut Parser<'i, '_>) -> Result<T, ParseError<'i>>,
236 ) -> Result<Self, ParseError<'i>> {
237 let light = parse_one(input)?;
238 input.expect_comma()?;
239 let dark = parse_one(input)?;
240 Ok(Self { light, dark })
241 }
242
243 pub fn parse_with<'i>(
245 input: &mut Parser<'i, '_>,
246 parse_one: impl FnMut(&mut Parser<'i, '_>) -> Result<T, ParseError<'i>>,
247 ) -> Result<Self, ParseError<'i>> {
248 input.expect_function_matching("light-dark")?;
249 input.parse_nested_block(|input| Self::parse_args_with(input, parse_one))
250 }
251}
252
253impl<T: ToComputedValue> GenericLightDark<T> {
254 pub fn compute(&self, cx: &crate::values::computed::Context) -> T::ComputedValue {
256 let dark = cx.device().is_dark_color_scheme(cx.builder.color_scheme);
257 if cx.for_non_inherited_property {
258 cx.rule_cache_conditions
259 .borrow_mut()
260 .set_color_scheme_dependency(cx.builder.color_scheme);
261 }
262 let chosen = if dark { &self.dark } else { &self.light };
263 chosen.to_computed_value(cx)
264 }
265}