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