1use crate::color::ColorMixItemList;
8use crate::color::{mix::ColorInterpolationMethod, AbsoluteColor, ColorFunction};
9use crate::derives::*;
10use crate::values::generics::Optional;
11use crate::values::{
12 computed::ToComputedValue, specified::percentage::ToPercentage, ParseError, Parser,
13};
14use std::fmt::{self, Write};
15use style_traits::{owned_slice::OwnedSlice, CssWriter, ToCss};
16
17#[derive(Clone, Debug, MallocSizeOf, PartialEq, ToAnimatedValue, ToShmem)]
20#[repr(C)]
21pub enum GenericColor<Percentage> {
22 Absolute(AbsoluteColor),
24 ColorFunction(Box<ColorFunction<Self>>),
26 CurrentColor,
28 ColorMix(Box<GenericColorMix<Self, Percentage>>),
30 ContrastColor(Box<Self>),
32}
33
34#[derive(Clone, Copy, Debug, Default, MallocSizeOf, PartialEq, ToShmem)]
36#[repr(C)]
37pub struct ColorMixFlags(u8);
38bitflags! {
39 impl ColorMixFlags : u8 {
40 const NORMALIZE_WEIGHTS = 1 << 0;
42 const RESULT_IN_MODERN_SYNTAX = 1 << 1;
44 }
45}
46
47#[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 GenericColorMixItem<Color, Percentage> {
61 pub color: Color,
62 pub percentage: Optional<Percentage>,
64}
65
66#[derive(
71 Clone,
72 Debug,
73 MallocSizeOf,
74 PartialEq,
75 ToAnimatedValue,
76 ToComputedValue,
77 ToResolvedValue,
78 ToShmem,
79)]
80#[allow(missing_docs)]
81#[repr(C)]
82pub struct GenericColorMix<Color, Percentage> {
83 pub interpolation: ColorInterpolationMethod,
84 pub items: OwnedSlice<GenericColorMixItem<Color, Percentage>>,
85 pub flags: ColorMixFlags,
86}
87
88pub use self::GenericColorMix as ColorMix;
89
90impl<Color: ToCss, Percentage: ToCss + ToPercentage> ToCss for ColorMix<Color, Percentage> {
91 fn to_css<W>(&self, dest: &mut CssWriter<W>) -> fmt::Result
92 where
93 W: Write,
94 {
95 dest.write_str("color-mix(")?;
96
97 if !self.interpolation.is_default() {
101 self.interpolation.to_css(dest)?;
102 dest.write_str(", ")?;
103 }
104
105 let uniform_value = 1.0 / self.items.len() as f32;
106
107 let omit_all = self.items.iter().all(|item| {
110 item.percentage
111 .as_ref()
112 .is_some_and(|p| !p.is_calc() && p.to_percentage() == Some(uniform_value))
113 });
114
115 for (index, item) in self.items.iter().enumerate() {
116 if index != 0 {
117 dest.write_str(", ")?;
118 }
119
120 item.color.to_css(dest)?;
121
122 if omit_all {
123 continue;
124 }
125
126 if let Some(percentage) = item.percentage.as_ref() {
128 dest.write_char(' ')?;
129 percentage.to_css(dest)?;
130 }
131 }
132
133 dest.write_char(')')
134 }
135}
136
137impl<Color, Percentage> ColorMix<Color, Percentage> {
138 pub(crate) fn omitted_weight(&self) -> Option<f32>
142 where
143 Percentage: ToPercentage,
144 {
145 let mut specified_sum = 0.0;
146 let mut omitted = 0;
147 for item in self.items.iter() {
148 match item.percentage.as_ref() {
149 Some(p) => specified_sum += p.to_percentage()?,
150 None => omitted += 1,
151 }
152 }
153 Some(if omitted > 0 {
154 (1.0 - specified_sum) / omitted as f32
155 } else {
156 0.0
157 })
158 }
159}
160
161impl<Percentage> ColorMix<GenericColor<Percentage>, Percentage> {
162 pub fn mix_to_absolute(&self) -> Option<AbsoluteColor>
165 where
166 Percentage: ToPercentage,
167 {
168 use crate::color::mix;
169
170 let fill = self.omitted_weight()?;
171
172 let mut items = ColorMixItemList::with_capacity(self.items.len());
173 for item in self.items.iter() {
174 let weight = match item.percentage.as_ref() {
175 Some(percentage) => percentage.to_percentage()?,
176 None => fill,
177 };
178 items.push(mix::ColorMixItem::new(*item.color.as_absolute()?, weight))
179 }
180
181 Some(mix::mix_many(self.interpolation, items, self.flags))
182 }
183}
184
185pub use self::GenericColor as Color;
186
187impl<Percentage> Color<Percentage> {
188 pub fn as_absolute(&self) -> Option<&AbsoluteColor> {
190 match *self {
191 Self::Absolute(ref absolute) => Some(absolute),
192 _ => None,
193 }
194 }
195
196 pub fn currentcolor() -> Self {
198 Self::CurrentColor
199 }
200
201 pub fn is_currentcolor(&self) -> bool {
203 matches!(*self, Self::CurrentColor)
204 }
205
206 pub fn is_absolute(&self) -> bool {
208 matches!(*self, Self::Absolute(..))
209 }
210}
211
212#[derive(
214 Animate,
215 Clone,
216 ComputeSquaredDistance,
217 Copy,
218 Debug,
219 MallocSizeOf,
220 PartialEq,
221 Parse,
222 SpecifiedValueInfo,
223 ToAnimatedValue,
224 ToAnimatedZero,
225 ToComputedValue,
226 ToResolvedValue,
227 ToCss,
228 ToShmem,
229 ToTyped,
230)]
231#[repr(C, u8)]
232pub enum GenericColorOrAuto<C> {
233 Color(C),
235 Auto,
237}
238
239pub use self::GenericColorOrAuto as ColorOrAuto;
240
241#[derive(
244 Animate,
245 Clone,
246 ComputeSquaredDistance,
247 Copy,
248 Debug,
249 MallocSizeOf,
250 PartialEq,
251 SpecifiedValueInfo,
252 ToAnimatedValue,
253 ToAnimatedZero,
254 ToComputedValue,
255 ToCss,
256 ToShmem,
257 ToTyped,
258)]
259#[repr(transparent)]
260pub struct GenericCaretColor<C>(pub GenericColorOrAuto<C>);
261
262impl<C> GenericCaretColor<C> {
263 pub fn auto() -> Self {
265 GenericCaretColor(GenericColorOrAuto::Auto)
266 }
267}
268
269pub use self::GenericCaretColor as CaretColor;
270
271#[derive(
273 Clone, Debug, MallocSizeOf, PartialEq, SpecifiedValueInfo, ToShmem, ToCss, ToResolvedValue,
274)]
275#[css(function = "light-dark", comma)]
276#[repr(C)]
277pub struct GenericLightDark<T> {
278 pub light: T,
280 pub dark: T,
282}
283
284impl<T> GenericLightDark<T> {
285 pub fn parse_args_with(
287 input: &mut Parser,
288 mut parse_one: impl FnMut(&mut Parser) -> Result<T, ParseError>,
289 ) -> Result<Self, ParseError> {
290 let light = parse_one(input)?;
291 input.expect_comma()?;
292 let dark = parse_one(input)?;
293 Ok(Self { light, dark })
294 }
295
296 pub fn parse_with(
298 input: &mut Parser,
299 parse_one: impl FnMut(&mut Parser) -> Result<T, ParseError>,
300 ) -> Result<Self, ParseError> {
301 input.expect_function_matching("light-dark")?;
302 input.parse_nested_block(|input| Self::parse_args_with(input, parse_one))
303 }
304}
305
306impl<T: ToComputedValue> GenericLightDark<T> {
307 pub fn compute(&self, cx: &crate::values::computed::Context) -> T::ComputedValue {
309 let dark = cx.device().is_dark_color_scheme(cx.builder.color_scheme);
310 if cx.for_non_inherited_property {
311 cx.rule_cache_conditions
312 .borrow_mut()
313 .set_color_scheme_dependency(cx.builder.color_scheme);
314 }
315 let chosen = if dark { &self.dark } else { &self.light };
316 chosen.to_computed_value(cx)
317 }
318}