Skip to main content

vizia_core/style/
mod.rs

1//! Styling determines the appearance of a view.
2//!
3//! # Styling Views
4//! Vizia provides two ways to style views:
5//! - Inline
6//! - Shared
7//!
8//! ## Inline Styling
9//! Inline styling refers to setting the style and layout properties of a view using view [modifiers](crate::modifiers).
10//! ```
11//! # use vizia_core::prelude::*;
12//! # let cx = &mut Context::default();
13//! Element::new(cx).background_color(Color::red());
14//! ```
15//! Properties set inline affect only the modified view and override any shared styling for the same property.
16//!
17//! ## Shared Styling
18//! Shared styling refers to setting the style and layout properties using css rules.
19//! ```
20//! # use vizia_core::prelude::*;
21//! # let cx = &mut Context::default();
22//! Element::new(cx).class("foo");
23//! ```
24//! ```css
25//! .foo {
26//!     background-color: red;
27//! }
28//! ```
29//! Rules defined in css can apply to many views but are overridden by inline properties on a view.
30//!
31//! ### Adding Stylesheets
32//! To add a css string to an application, use [`add_theme()`](crate::context::Context::add_theme()) on [`Context`].
33//! This can be used with the `include_str!()` macro to embed an external stylesheet file into the application binary when compiled.
34//! Alternatively a constant string literal can be used to embed the CSS in the application.
35//!
36//! ```
37//! # use vizia_core::prelude::*;
38//! # let cx = &mut Context::default();
39//!
40//! const STYLE: &str = r#"
41//!     .foo {
42//!         background-color: red;
43//!     }
44//! "#;
45//!
46//! cx.add_stylesheet(STYLE);
47//!
48//! Element::new(cx).class("foo");
49//! ```
50//!
51//! To add an external css stylesheet which is read from a file at runtime, use [`add_stylesheet()`](crate::context::Context::add_stylesheet()) on [`Context`].
52//! Stylesheets added this way can be hot-reloaded by pressing the F5 key in the application window.
53//!
54//! ```
55//! # use vizia_core::prelude::*;
56//! # let cx = &mut Context::default();
57//!
58//! cx.add_stylesheet("path/to/stylesheet.css");
59//!
60//! Element::new(cx).class("foo");
61//! ```
62
63use hashbrown::{HashMap, HashSet};
64use indexmap::IndexMap;
65use log::warn;
66use std::fmt::Debug;
67use std::hash::{DefaultHasher, Hash, Hasher};
68use std::ops::Range;
69use vizia_style::selectors::parser::{AncestorHashes, Selector};
70
71use crate::prelude::*;
72use crate::storage::animatable_var_set::AnimatableVarSet;
73
74pub use vizia_style::{
75    Alignment, Angle, BackgroundImage, BackgroundSize, BorderStyleKeyword, ClipPath, Color,
76    CornerShape, CssRule, CursorIcon, Direction, Display, Filter, FontFamily, FontSize,
77    FontSizeKeyword, FontSlant, FontVariation, FontWeight, FontWeightKeyword, FontWidth,
78    GenericFontFamily, Gradient, HorizontalPosition, HorizontalPositionKeyword, LayoutWrap, Length,
79    LengthOrPercentage, LengthValue, LineClamp, LineDirection, LineHeight, LinearGradient, Matrix,
80    Opacity, Overflow, PointerEvents, Position, PositionType, RGBA, Scale, Shadow, TextAlign,
81    TextDecorationLine, TextDecorationStyle, TextOverflow, TextStroke, TextStrokeStyle, Transform,
82    Transition, Translate, VerticalPosition, VerticalPositionKeyword, Visibility,
83};
84
85use cssparser::Token as CssToken;
86use vizia_style::{
87    BlendMode, EasingFunction, KeyframeSelector, ParserOptions, Property, Selectors, StyleSheet,
88    TokenList, TokenOrValue, Variable,
89};
90
91mod rule;
92pub(crate) use rule::Rule;
93
94mod pseudoclass;
95pub(crate) use pseudoclass::*;
96
97mod transform;
98pub(crate) use transform::*;
99
100use crate::animation::{AnimationState, Interpolator, Keyframe, TimingFunction};
101use crate::storage::animatable_set::AnimatableSet;
102use crate::storage::style_set::StyleSet;
103use bitflags::bitflags;
104use vizia_id::IdManager;
105use vizia_storage::SparseSet;
106
107bitflags! {
108    /// Describes the capabilities of a view with respect to user interaction.
109    #[derive(Debug, Clone, Copy)]
110    pub(crate) struct Abilities: u8 {
111        // Whether a view will be included in hit tests and receive mouse input events.
112        const HOVERABLE = 1 << 0;
113        // Whether a view can be focused to receive keyboard events.
114        const FOCUSABLE = 1 << 1;
115        // Whether a view can be checked.
116        const CHECKABLE = 1 << 2;
117        // Whether a view can be focused via keyboard navigation.
118        const NAVIGABLE = 1 << 3;
119        // Whether a view can be dragged during a drag and drop.
120        const DRAGGABLE = 1 << 4;
121    }
122}
123
124impl Default for Abilities {
125    fn default() -> Abilities {
126        Abilities::HOVERABLE
127    }
128}
129
130bitflags! {
131    pub(crate) struct SystemFlags: u8 {
132        const RELAYOUT = 1;
133        const RESTYLE = 1 << 1;
134        const REFLOW = 1 << 2;
135        const REDRAW = 1 << 3;
136        const RETRANSFORM = 1 << 4;
137        const RECLIP = 1 << 5;
138        const REACCESS = 1 << 6;
139    }
140}
141
142impl Default for SystemFlags {
143    fn default() -> Self {
144        SystemFlags::all()
145    }
146}
147
148/// An enum which represents an image or a gradient.
149#[derive(Debug, Clone, PartialEq)]
150pub enum ImageOrGradient {
151    /// Represents an image by name.
152    Image(String),
153    /// A gradient.
154    Gradient(Gradient),
155}
156
157/// A font-family.
158#[derive(Debug, Clone, PartialEq, Eq)]
159pub enum FamilyOwned {
160    /// A generic font-family.
161    Generic(GenericFontFamily),
162    /// A named front-family.
163    Named(String),
164}
165
166impl AsRef<str> for FamilyOwned {
167    fn as_ref(&self) -> &str {
168        match self {
169            FamilyOwned::Generic(generic) => match generic {
170                GenericFontFamily::Serif => "serif",
171                GenericFontFamily::SansSerif => "sans-serif",
172                GenericFontFamily::Cursive => todo!(),
173                GenericFontFamily::Fantasy => todo!(),
174                GenericFontFamily::Monospace => "Cascadia Mono",
175            },
176            FamilyOwned::Named(family) => family.as_str(),
177        }
178    }
179}
180
181pub(crate) struct StyleRule {
182    pub(crate) selector: Selector<Selectors>,
183    /// The ancestor hashes associated with the selector.
184    pub(crate) hashes: AncestorHashes,
185}
186
187impl StyleRule {
188    pub(crate) fn new(selector: Selector<Selectors>) -> Self {
189        let hashes = AncestorHashes::new(&selector, vizia_style::QuirksMode::NoQuirks);
190        Self { selector, hashes }
191    }
192}
193
194/// Stores the style properties of all entities in the application.
195#[derive(Default)]
196pub struct Style {
197    pub(crate) rule_manager: IdManager<Rule>,
198
199    // Creates and destroys animation ids
200    pub(crate) animation_manager: IdManager<Animation>,
201    pub(crate) animations: HashMap<String, Animation>,
202    // List of animations to be started on the next frame
203    pub(crate) pending_animations: Vec<(Entity, Animation, Duration, Duration)>,
204
205    // List of rules
206    pub(crate) rules: IndexMap<Rule, StyleRule>,
207
208    pub(crate) default_font: Vec<FamilyOwned>,
209
210    // CSS Selector Properties
211    pub(crate) element: SparseSet<u32>,
212    pub(crate) ids: SparseSet<String>,
213    pub(crate) classes: SparseSet<HashSet<String>>,
214    pub(crate) pseudo_classes: SparseSet<PseudoClassFlags>,
215    pub(crate) disabled: StyleSet<bool>,
216    pub(crate) abilities: SparseSet<Abilities>,
217
218    // Accessibility Properties
219    pub(crate) name: StyleSet<String>,
220    pub(crate) role: SparseSet<Role>,
221    pub(crate) live: SparseSet<Live>,
222    pub(crate) labelled_by: SparseSet<String>,
223    pub(crate) described_by: SparseSet<String>,
224    pub(crate) controls: SparseSet<String>,
225    pub(crate) active_descendant: SparseSet<String>,
226    pub(crate) expanded: SparseSet<bool>,
227    pub(crate) selected: SparseSet<bool>,
228    pub(crate) hidden: SparseSet<bool>,
229    pub(crate) orientation: SparseSet<Orientation>,
230    pub(crate) text_value: SparseSet<String>,
231    pub(crate) numeric_value: SparseSet<f64>,
232
233    // Visibility
234    pub(crate) visibility: StyleSet<Visibility>,
235
236    // Opacity
237    pub(crate) opacity: AnimatableVarSet<Opacity>,
238
239    // Z Order
240    pub(crate) z_index: StyleSet<i32>,
241
242    // Controls whether an entity should ignore ancestor clipping during drawing.
243    pub(crate) ignore_clipping: StyleSet<bool>,
244
245    // Clipping
246    pub(crate) clip_path: AnimatableSet<ClipPath>,
247
248    // Overflow
249    pub(crate) overflowx: StyleSet<Overflow>,
250    pub(crate) overflowy: StyleSet<Overflow>,
251
252    // Filters
253    pub(crate) filter: AnimatableSet<Filter>,
254    pub(crate) backdrop_filter: AnimatableSet<Filter>,
255
256    pub(crate) blend_mode: StyleSet<BlendMode>,
257
258    // Transform
259    pub(crate) transform: AnimatableSet<Vec<Transform>>,
260    pub(crate) transform_origin: AnimatableSet<Translate>,
261    pub(crate) translate: AnimatableSet<Translate>,
262    pub(crate) rotate: AnimatableSet<Angle>,
263    pub(crate) scale: AnimatableSet<Scale>,
264
265    // Border
266    pub(crate) border_width: AnimatableVarSet<LengthOrPercentage>,
267    pub(crate) border_color: AnimatableVarSet<Color>,
268    pub(crate) border_style: StyleSet<BorderStyleKeyword>,
269
270    // Corner Shape
271    pub(crate) corner_top_left_shape: StyleSet<CornerShape>,
272    pub(crate) corner_top_right_shape: StyleSet<CornerShape>,
273    pub(crate) corner_bottom_left_shape: StyleSet<CornerShape>,
274    pub(crate) corner_bottom_right_shape: StyleSet<CornerShape>,
275
276    // Corner Radius
277    pub(crate) corner_top_left_radius: AnimatableVarSet<LengthOrPercentage>,
278    pub(crate) corner_top_right_radius: AnimatableVarSet<LengthOrPercentage>,
279    pub(crate) corner_bottom_left_radius: AnimatableVarSet<LengthOrPercentage>,
280    pub(crate) corner_bottom_right_radius: AnimatableVarSet<LengthOrPercentage>,
281
282    // Corner Smoothing
283    pub(crate) corner_top_left_smoothing: AnimatableSet<f32>,
284    pub(crate) corner_top_right_smoothing: AnimatableSet<f32>,
285    pub(crate) corner_bottom_left_smoothing: AnimatableSet<f32>,
286    pub(crate) corner_bottom_right_smoothing: AnimatableSet<f32>,
287
288    // Outline
289    pub(crate) outline_width: AnimatableVarSet<LengthOrPercentage>,
290    pub(crate) outline_color: AnimatableVarSet<Color>,
291    pub(crate) outline_offset: AnimatableVarSet<LengthOrPercentage>,
292
293    // Background
294    pub(crate) background_color: AnimatableVarSet<Color>,
295    pub(crate) background_image: AnimatableSet<Vec<ImageOrGradient>>,
296    pub(crate) background_size: AnimatableSet<Vec<BackgroundSize>>,
297
298    // Shadow
299    pub(crate) shadow: AnimatableVarSet<Vec<Shadow>>,
300
301    // Text
302    pub(crate) text: SparseSet<String>,
303    pub(crate) text_wrap: StyleSet<bool>,
304    pub(crate) text_overflow: StyleSet<TextOverflow>,
305    pub(crate) line_height: AnimatableVarSet<LineHeight>,
306    pub(crate) line_clamp: StyleSet<LineClamp>,
307    pub(crate) text_align: StyleSet<TextAlign>,
308    pub(crate) text_decoration_line: StyleSet<TextDecorationLine>,
309    pub(crate) text_decoration_style: StyleSet<TextDecorationStyle>,
310    pub(crate) text_decoration_color: AnimatableVarSet<Color>,
311    pub(crate) text_stroke_width: StyleSet<Length>,
312    pub(crate) text_stroke_style: StyleSet<TextStrokeStyle>,
313    pub(crate) font_family: StyleSet<Vec<FamilyOwned>>,
314    pub(crate) font_color: AnimatableVarSet<Color>,
315    pub(crate) font_size: AnimatableVarSet<FontSize>,
316    pub(crate) font_weight: StyleSet<FontWeight>,
317    pub(crate) font_slant: StyleSet<FontSlant>,
318    pub(crate) font_width: StyleSet<FontWidth>,
319    pub(crate) font_variation_settings: StyleSet<Vec<FontVariation>>,
320    pub(crate) caret_color: AnimatableVarSet<Color>,
321    pub(crate) selection_color: AnimatableVarSet<Color>,
322
323    pub(crate) fill: AnimatableVarSet<Color>,
324
325    // cursor Icon
326    pub(crate) cursor: StyleSet<CursorIcon>,
327
328    pub(crate) pointer_events: StyleSet<PointerEvents>,
329
330    // LAYOUT
331
332    // Display
333    pub(crate) display: AnimatableSet<Display>,
334
335    // Layout Type
336    pub(crate) layout_type: StyleSet<LayoutType>,
337
338    // Position
339    pub(crate) position_type: StyleSet<PositionType>,
340
341    pub(crate) alignment: StyleSet<Alignment>,
342    pub(crate) direction: StyleSet<Direction>,
343    pub(crate) wrap: StyleSet<LayoutWrap>,
344
345    // Grid
346    pub(crate) grid_columns: StyleSet<Vec<Units>>,
347    pub(crate) grid_rows: StyleSet<Vec<Units>>,
348
349    pub(crate) column_start: StyleSet<usize>,
350    pub(crate) column_span: StyleSet<usize>,
351    pub(crate) row_start: StyleSet<usize>,
352    pub(crate) row_span: StyleSet<usize>,
353
354    // Spacing
355    pub(crate) left: AnimatableVarSet<Units>,
356    pub(crate) right: AnimatableVarSet<Units>,
357    pub(crate) top: AnimatableVarSet<Units>,
358    pub(crate) bottom: AnimatableVarSet<Units>,
359
360    // Padding
361    pub(crate) padding_left: AnimatableVarSet<Units>,
362    pub(crate) padding_right: AnimatableVarSet<Units>,
363    pub(crate) padding_top: AnimatableVarSet<Units>,
364    pub(crate) padding_bottom: AnimatableVarSet<Units>,
365    pub(crate) vertical_gap: AnimatableVarSet<Units>,
366    pub(crate) horizontal_gap: AnimatableVarSet<Units>,
367
368    // Scrolling
369    pub(crate) vertical_scroll: AnimatableSet<f32>,
370    pub(crate) horizontal_scroll: AnimatableSet<f32>,
371
372    // Size
373    pub(crate) width: AnimatableVarSet<Units>,
374    pub(crate) height: AnimatableVarSet<Units>,
375
376    // Size Constraints
377    pub(crate) min_width: AnimatableVarSet<Units>,
378    pub(crate) max_width: AnimatableVarSet<Units>,
379    pub(crate) min_height: AnimatableVarSet<Units>,
380    pub(crate) max_height: AnimatableVarSet<Units>,
381
382    // Gap Constraints
383    pub(crate) min_horizontal_gap: AnimatableVarSet<Units>,
384    pub(crate) max_horizontal_gap: AnimatableVarSet<Units>,
385    pub(crate) min_vertical_gap: AnimatableVarSet<Units>,
386    pub(crate) max_vertical_gap: AnimatableVarSet<Units>,
387
388    pub(crate) system_flags: SystemFlags,
389
390    pub(crate) restyle: HashSet<Entity>,
391    pub(crate) text_construction: HashSet<Entity>,
392    pub(crate) text_layout: HashSet<Entity>,
393    pub(crate) reaccess: HashSet<Entity>,
394    pub(crate) retransform: HashSet<Entity>,
395    pub(crate) reclip: HashSet<Entity>,
396
397    pub(crate) text_range: SparseSet<Range<usize>>,
398    pub(crate) text_span: SparseSet<bool>,
399
400    /// This includes both the system's HiDPI scaling factor as well as `cx.user_scale_factor`.
401    pub(crate) dpi_factor: f64,
402
403    pub(crate) custom_color_props: HashMap<u64, AnimatableVarSet<Color>>,
404    pub(crate) custom_length_props: HashMap<u64, AnimatableVarSet<LengthOrPercentage>>,
405    pub(crate) custom_font_size_props: HashMap<u64, AnimatableVarSet<FontSize>>,
406    pub(crate) custom_line_height_props: HashMap<u64, AnimatableVarSet<LineHeight>>,
407    pub(crate) custom_units_props: HashMap<u64, AnimatableVarSet<Units>>,
408    pub(crate) custom_opacity_props: HashMap<u64, AnimatableVarSet<Opacity>>,
409    pub(crate) custom_shadow_props: HashMap<u64, AnimatableVarSet<Vec<Shadow>>>,
410}
411
412impl Style {
413    /// Returns the scale factor of the application.
414    pub fn scale_factor(&self) -> f32 {
415        self.dpi_factor as f32
416    }
417
418    /// Function to convert logical points to physical pixels.
419    pub fn logical_to_physical(&self, logical: f32) -> f32 {
420        (logical * self.dpi_factor as f32).round()
421    }
422
423    /// Function to convert physical pixels to logical points.
424    pub fn physical_to_logical(&self, physical: f32) -> f32 {
425        physical / self.dpi_factor as f32
426    }
427
428    pub(crate) fn remove_rules(&mut self) {
429        self.rule_manager.reset();
430        self.rules.clear();
431    }
432
433    pub(crate) fn get_animation(&self, name: &str) -> Option<&Animation> {
434        self.animations.get(name)
435    }
436
437    pub(crate) fn add_keyframe(
438        &mut self,
439        animation_id: Animation,
440        time: f32,
441        properties: &[Property],
442    ) {
443        fn insert_keyframe<T: 'static + Interpolator + Debug + Clone + PartialEq + Default>(
444            storage: &mut AnimatableSet<T>,
445            animation_id: Animation,
446            time: f32,
447            value: T,
448        ) {
449            let keyframe = Keyframe { time, value, timing_function: TimingFunction::linear() };
450
451            if let Some(anim_state) = storage.get_animation_mut(animation_id) {
452                anim_state.keyframes.push(keyframe)
453            } else {
454                let anim_state = AnimationState::new(animation_id).with_keyframe(keyframe);
455                storage.insert_animation(animation_id, anim_state);
456            }
457        }
458
459        fn insert_keyframe2<T: 'static + Interpolator + Debug + Clone + PartialEq + Default>(
460            storage: &mut AnimatableVarSet<T>,
461            animation_id: Animation,
462            time: f32,
463            value: T,
464        ) {
465            let keyframe = Keyframe { time, value, timing_function: TimingFunction::linear() };
466
467            if let Some(anim_state) = storage.get_animation_mut(animation_id) {
468                anim_state.keyframes.push(keyframe)
469            } else {
470                let anim_state = AnimationState::new(animation_id).with_keyframe(keyframe);
471                storage.insert_animation(animation_id, anim_state);
472            }
473        }
474
475        for property in properties.iter() {
476            match property {
477                // DISPLAY
478                Property::Display(value) => {
479                    insert_keyframe(&mut self.display, animation_id, time, *value);
480                }
481
482                Property::Opacity(value) => {
483                    insert_keyframe2(&mut self.opacity, animation_id, time, *value);
484                }
485
486                Property::ClipPath(value) => {
487                    insert_keyframe(&mut self.clip_path, animation_id, time, value.clone());
488                }
489
490                // TRANSFORM
491                Property::Transform(value) => {
492                    insert_keyframe(&mut self.transform, animation_id, time, value.clone());
493                }
494
495                Property::TransformOrigin(transform_origin) => {
496                    let x = transform_origin.x.to_length_or_percentage();
497                    let y = transform_origin.y.to_length_or_percentage();
498                    let value = Translate { x, y };
499                    insert_keyframe(&mut self.transform_origin, animation_id, time, value);
500                }
501
502                Property::Translate(value) => {
503                    insert_keyframe(&mut self.translate, animation_id, time, value.clone());
504                }
505
506                Property::Rotate(value) => {
507                    insert_keyframe(&mut self.rotate, animation_id, time, *value);
508                }
509
510                Property::Scale(value) => {
511                    insert_keyframe(&mut self.scale, animation_id, time, *value);
512                }
513
514                // BORDER
515                Property::BorderWidth(value) => {
516                    insert_keyframe2(
517                        &mut self.border_width,
518                        animation_id,
519                        time,
520                        value.left.0.clone(),
521                    );
522                }
523
524                Property::BorderColor(value) => {
525                    insert_keyframe2(&mut self.border_color, animation_id, time, *value);
526                }
527
528                Property::CornerTopLeftRadius(value) => {
529                    insert_keyframe2(
530                        &mut self.corner_top_left_radius,
531                        animation_id,
532                        time,
533                        value.clone(),
534                    );
535                }
536
537                Property::CornerTopRightRadius(value) => {
538                    insert_keyframe2(
539                        &mut self.corner_top_right_radius,
540                        animation_id,
541                        time,
542                        value.clone(),
543                    );
544                }
545
546                Property::CornerBottomLeftRadius(value) => {
547                    insert_keyframe2(
548                        &mut self.corner_bottom_left_radius,
549                        animation_id,
550                        time,
551                        value.clone(),
552                    );
553                }
554
555                Property::CornerBottomRightRadius(value) => {
556                    insert_keyframe2(
557                        &mut self.corner_bottom_right_radius,
558                        animation_id,
559                        time,
560                        value.clone(),
561                    );
562                }
563
564                // OUTLINE
565                Property::OutlineWidth(value) => {
566                    insert_keyframe2(
567                        &mut self.outline_width,
568                        animation_id,
569                        time,
570                        value.left.0.clone(),
571                    );
572                }
573
574                Property::OutlineColor(value) => {
575                    insert_keyframe2(&mut self.outline_color, animation_id, time, *value);
576                }
577
578                Property::OutlineOffset(value) => {
579                    insert_keyframe2(&mut self.outline_offset, animation_id, time, value.clone());
580                }
581
582                // BACKGROUND
583                Property::BackgroundColor(value) => {
584                    insert_keyframe2(&mut self.background_color, animation_id, time, *value);
585                }
586
587                Property::BackgroundImage(images) => {
588                    let images = images
589                        .iter()
590                        .filter_map(|img| match img {
591                            BackgroundImage::None => None,
592                            BackgroundImage::Gradient(gradient) => {
593                                Some(ImageOrGradient::Gradient(*gradient.clone()))
594                            }
595                            BackgroundImage::Url(url) => {
596                                Some(ImageOrGradient::Image(url.url.to_string()))
597                            }
598                        })
599                        .collect::<Vec<_>>();
600                    insert_keyframe(&mut self.background_image, animation_id, time, images);
601                }
602
603                Property::BackgroundSize(value) => {
604                    insert_keyframe(&mut self.background_size, animation_id, time, value.clone());
605                }
606
607                // BOX SHADOW
608                Property::Shadow(value) => {
609                    insert_keyframe2(&mut self.shadow, animation_id, time, value.clone());
610                }
611
612                // TEXT
613                Property::FontColor(value) => {
614                    insert_keyframe2(&mut self.font_color, animation_id, time, *value);
615                }
616
617                Property::FontSize(value) => {
618                    insert_keyframe2(&mut self.font_size, animation_id, time, value.clone());
619                }
620
621                Property::LineHeight(value) => {
622                    insert_keyframe2(&mut self.line_height, animation_id, time, value.clone());
623                }
624
625                Property::CaretColor(value) => {
626                    insert_keyframe2(&mut self.caret_color, animation_id, time, *value);
627                }
628
629                Property::SelectionColor(value) => {
630                    insert_keyframe2(&mut self.selection_color, animation_id, time, *value);
631                }
632
633                // SPACE
634                Property::Left(value) => {
635                    insert_keyframe2(&mut self.left, animation_id, time, *value);
636                }
637
638                Property::Right(value) => {
639                    insert_keyframe2(&mut self.right, animation_id, time, *value);
640                }
641
642                Property::Top(value) => {
643                    insert_keyframe2(&mut self.top, animation_id, time, *value);
644                }
645
646                Property::Bottom(value) => {
647                    insert_keyframe2(&mut self.bottom, animation_id, time, *value);
648                }
649
650                // Padding
651                Property::PaddingLeft(value) => {
652                    insert_keyframe2(&mut self.padding_left, animation_id, time, *value);
653                }
654
655                Property::PaddingRight(value) => {
656                    insert_keyframe2(&mut self.padding_right, animation_id, time, *value);
657                }
658
659                Property::PaddingTop(value) => {
660                    insert_keyframe2(&mut self.padding_top, animation_id, time, *value);
661                }
662
663                Property::PaddingBottom(value) => {
664                    insert_keyframe2(&mut self.padding_bottom, animation_id, time, *value);
665                }
666
667                Property::HorizontalGap(value) => {
668                    insert_keyframe2(&mut self.horizontal_gap, animation_id, time, *value);
669                }
670
671                Property::VerticalGap(value) => {
672                    insert_keyframe2(&mut self.vertical_gap, animation_id, time, *value);
673                }
674
675                Property::Gap(value) => {
676                    insert_keyframe2(&mut self.horizontal_gap, animation_id, time, *value);
677                    insert_keyframe2(&mut self.vertical_gap, animation_id, time, *value);
678                }
679
680                // GAP CONSSTRAINTS
681                Property::MinGap(value) => {
682                    insert_keyframe2(&mut self.min_horizontal_gap, animation_id, time, *value);
683                    insert_keyframe2(&mut self.min_vertical_gap, animation_id, time, *value);
684                }
685
686                Property::MaxGap(value) => {
687                    insert_keyframe2(&mut self.max_horizontal_gap, animation_id, time, *value);
688                    insert_keyframe2(&mut self.max_vertical_gap, animation_id, time, *value);
689                }
690
691                Property::MinHorizontalGap(value) => {
692                    insert_keyframe2(&mut self.min_horizontal_gap, animation_id, time, *value);
693                }
694
695                Property::MaxHorizontalGap(value) => {
696                    insert_keyframe2(&mut self.max_horizontal_gap, animation_id, time, *value);
697                }
698
699                Property::MinVerticalGap(value) => {
700                    insert_keyframe2(&mut self.min_vertical_gap, animation_id, time, *value);
701                }
702
703                Property::MaxVerticalGap(value) => {
704                    insert_keyframe2(&mut self.max_vertical_gap, animation_id, time, *value);
705                }
706
707                // SIZE
708                Property::Width(value) => {
709                    insert_keyframe2(&mut self.width, animation_id, time, *value);
710                }
711
712                Property::Height(value) => {
713                    insert_keyframe2(&mut self.height, animation_id, time, *value);
714                }
715
716                // SIZE CONSTRAINTS
717                Property::MinWidth(value) => {
718                    insert_keyframe2(&mut self.min_width, animation_id, time, *value);
719                }
720
721                Property::MaxWidth(value) => {
722                    insert_keyframe2(&mut self.max_width, animation_id, time, *value);
723                }
724
725                Property::MinHeight(value) => {
726                    insert_keyframe2(&mut self.min_height, animation_id, time, *value);
727                }
728
729                Property::MaxHeight(value) => {
730                    insert_keyframe2(&mut self.max_height, animation_id, time, *value);
731                }
732
733                Property::TextDecorationColor(value) => {
734                    insert_keyframe2(&mut self.text_decoration_color, animation_id, time, *value);
735                }
736
737                Property::Fill(value) => {
738                    insert_keyframe2(&mut self.fill, animation_id, time, *value);
739                }
740
741                _ => {}
742            }
743        }
744    }
745
746    pub(crate) fn add_animation(&mut self, animation: AnimationBuilder) -> Animation {
747        let animation_id = self.animation_manager.create();
748        for keyframe in animation.keyframes.iter() {
749            self.add_keyframe(animation_id, keyframe.time, &keyframe.properties);
750        }
751
752        animation_id
753    }
754
755    pub(crate) fn enqueue_animation(
756        &mut self,
757        entity: Entity,
758        animation: Animation,
759        duration: Duration,
760        delay: Duration,
761    ) {
762        self.pending_animations.push((entity, animation, duration, delay));
763    }
764
765    pub(crate) fn play_pending_animations(&mut self) {
766        let start_time = Instant::now();
767
768        let pending_animations = self.pending_animations.drain(..).collect::<Vec<_>>();
769
770        for (entity, animation, duration, delay) in pending_animations {
771            self.play_animation(entity, animation, start_time + delay, duration, delay)
772        }
773    }
774
775    pub(crate) fn play_animation(
776        &mut self,
777        entity: Entity,
778        animation: Animation,
779        start_time: Instant,
780        duration: Duration,
781        delay: Duration,
782    ) {
783        self.display.play_animation(entity, animation, start_time, duration, delay);
784        self.opacity.play_animation(entity, animation, start_time, duration, delay);
785        self.clip_path.play_animation(entity, animation, start_time, duration, delay);
786
787        self.transform.play_animation(entity, animation, start_time, duration, delay);
788        self.transform_origin.play_animation(entity, animation, start_time, duration, delay);
789        self.translate.play_animation(entity, animation, start_time, duration, delay);
790        self.rotate.play_animation(entity, animation, start_time, duration, delay);
791        self.scale.play_animation(entity, animation, start_time, duration, delay);
792
793        self.border_width.play_animation(entity, animation, start_time, duration, delay);
794        self.border_color.play_animation(entity, animation, start_time, duration, delay);
795
796        self.corner_top_left_radius.play_animation(entity, animation, start_time, duration, delay);
797        self.corner_top_right_radius.play_animation(entity, animation, start_time, duration, delay);
798        self.corner_bottom_left_radius
799            .play_animation(entity, animation, start_time, duration, delay);
800        self.corner_bottom_right_radius
801            .play_animation(entity, animation, start_time, duration, delay);
802
803        self.outline_width.play_animation(entity, animation, start_time, duration, delay);
804        self.outline_color.play_animation(entity, animation, start_time, duration, delay);
805        self.outline_offset.play_animation(entity, animation, start_time, duration, delay);
806
807        self.background_color.play_animation(entity, animation, start_time, duration, delay);
808        self.background_image.play_animation(entity, animation, start_time, duration, delay);
809        self.background_size.play_animation(entity, animation, start_time, duration, delay);
810
811        self.shadow.play_animation(entity, animation, start_time, duration, delay);
812
813        self.font_color.play_animation(entity, animation, start_time, duration, delay);
814        self.font_size.play_animation(entity, animation, start_time, duration, delay);
815        self.line_height.play_animation(entity, animation, start_time, duration, delay);
816        self.caret_color.play_animation(entity, animation, start_time, duration, delay);
817        self.selection_color.play_animation(entity, animation, start_time, duration, delay);
818
819        self.left.play_animation(entity, animation, start_time, duration, delay);
820        self.right.play_animation(entity, animation, start_time, duration, delay);
821        self.top.play_animation(entity, animation, start_time, duration, delay);
822        self.bottom.play_animation(entity, animation, start_time, duration, delay);
823
824        self.padding_left.play_animation(entity, animation, start_time, duration, delay);
825        self.padding_right.play_animation(entity, animation, start_time, duration, delay);
826        self.padding_top.play_animation(entity, animation, start_time, duration, delay);
827        self.padding_bottom.play_animation(entity, animation, start_time, duration, delay);
828        self.horizontal_gap.play_animation(entity, animation, start_time, duration, delay);
829        self.vertical_gap.play_animation(entity, animation, start_time, duration, delay);
830
831        self.width.play_animation(entity, animation, start_time, duration, delay);
832        self.height.play_animation(entity, animation, start_time, duration, delay);
833
834        self.min_width.play_animation(entity, animation, start_time, duration, delay);
835        self.max_width.play_animation(entity, animation, start_time, duration, delay);
836        self.min_height.play_animation(entity, animation, start_time, duration, delay);
837        self.max_height.play_animation(entity, animation, start_time, duration, delay);
838
839        self.min_horizontal_gap.play_animation(entity, animation, start_time, duration, delay);
840        self.max_horizontal_gap.play_animation(entity, animation, start_time, duration, delay);
841        self.min_vertical_gap.play_animation(entity, animation, start_time, duration, delay);
842        self.max_vertical_gap.play_animation(entity, animation, start_time, duration, delay);
843
844        self.text_decoration_color.play_animation(entity, animation, start_time, duration, delay);
845
846        self.fill.play_animation(entity, animation, start_time, duration, delay);
847
848        // Play animations on custom color properties
849        for store in self.custom_color_props.values_mut() {
850            store.play_animation(entity, animation, start_time, duration, delay);
851        }
852        // Play animations on custom length properties
853        for store in self.custom_length_props.values_mut() {
854            store.play_animation(entity, animation, start_time, duration, delay);
855        }
856        // Play animations on custom font-size properties
857        for store in self.custom_font_size_props.values_mut() {
858            store.play_animation(entity, animation, start_time, duration, delay);
859        }
860        // Play animations on custom line-height properties
861        for store in self.custom_line_height_props.values_mut() {
862            store.play_animation(entity, animation, start_time, duration, delay);
863        }
864        // Play animations on custom units properties
865        for store in self.custom_units_props.values_mut() {
866            store.play_animation(entity, animation, start_time, duration, delay);
867        }
868        // Play animations on custom opacity properties
869        for store in self.custom_opacity_props.values_mut() {
870            store.play_animation(entity, animation, start_time, duration, delay);
871        }
872        // Play animations on custom shadow properties
873        for store in self.custom_shadow_props.values_mut() {
874            store.play_animation(entity, animation, start_time, duration, delay);
875        }
876    }
877
878    pub(crate) fn is_animating(&self, entity: Entity, animation: Animation) -> bool {
879        self.display.has_active_animation(entity, animation)
880            | self.opacity.has_active_animation(entity, animation)
881            | self.clip_path.has_active_animation(entity, animation)
882            | self.transform.has_active_animation(entity, animation)
883            | self.transform_origin.has_active_animation(entity, animation)
884            | self.translate.has_active_animation(entity, animation)
885            | self.rotate.has_active_animation(entity, animation)
886            | self.scale.has_active_animation(entity, animation)
887            | self.border_width.has_active_animation(entity, animation)
888            | self.border_color.has_active_animation(entity, animation)
889            | self.corner_top_left_radius.has_active_animation(entity, animation)
890            | self.corner_top_right_radius.has_active_animation(entity, animation)
891            | self.corner_bottom_left_radius.has_active_animation(entity, animation)
892            | self.corner_bottom_right_radius.has_active_animation(entity, animation)
893            | self.outline_width.has_active_animation(entity, animation)
894            | self.outline_color.has_active_animation(entity, animation)
895            | self.outline_offset.has_active_animation(entity, animation)
896            | self.background_color.has_active_animation(entity, animation)
897            | self.background_image.has_active_animation(entity, animation)
898            | self.background_size.has_active_animation(entity, animation)
899            | self.shadow.has_active_animation(entity, animation)
900            | self.font_color.has_active_animation(entity, animation)
901            | self.font_size.has_active_animation(entity, animation)
902            | self.line_height.has_active_animation(entity, animation)
903            | self.caret_color.has_active_animation(entity, animation)
904            | self.selection_color.has_active_animation(entity, animation)
905            | self.left.has_active_animation(entity, animation)
906            | self.right.has_active_animation(entity, animation)
907            | self.top.has_active_animation(entity, animation)
908            | self.bottom.has_active_animation(entity, animation)
909            | self.padding_left.has_active_animation(entity, animation)
910            | self.padding_right.has_active_animation(entity, animation)
911            | self.padding_top.has_active_animation(entity, animation)
912            | self.padding_bottom.has_active_animation(entity, animation)
913            | self.horizontal_gap.has_active_animation(entity, animation)
914            | self.vertical_gap.has_active_animation(entity, animation)
915            | self.width.has_active_animation(entity, animation)
916            | self.height.has_active_animation(entity, animation)
917            | self.min_width.has_active_animation(entity, animation)
918            | self.max_width.has_active_animation(entity, animation)
919            | self.min_height.has_active_animation(entity, animation)
920            | self.max_height.has_active_animation(entity, animation)
921            | self.min_horizontal_gap.has_active_animation(entity, animation)
922            | self.max_horizontal_gap.has_active_animation(entity, animation)
923            | self.min_vertical_gap.has_active_animation(entity, animation)
924            | self.max_vertical_gap.has_active_animation(entity, animation)
925            | self.text_decoration_color.has_active_animation(entity, animation)
926            | self.fill.has_active_animation(entity, animation)
927    }
928
929    pub(crate) fn parse_theme(&mut self, stylesheet: &str) {
930        if let Ok(stylesheet) = StyleSheet::parse(stylesheet, ParserOptions::new()) {
931            let rules = stylesheet.rules.0;
932
933            for rule in rules {
934                match rule {
935                    CssRule::Style(style_rule) => {
936                        // let selectors = style_rule.selectors;
937
938                        for selector in style_rule.selectors.slice() {
939                            let rule_id = self.rule_manager.create();
940
941                            for property in style_rule.declarations.declarations.iter() {
942                                match property {
943                                    Property::Transition(transitions) => {
944                                        for transition in transitions.iter() {
945                                            self.insert_transition(rule_id, transition);
946                                        }
947                                    }
948
949                                    _ => {
950                                        self.insert_property(rule_id, property);
951                                    }
952                                }
953                            }
954
955                            self.rules.insert(rule_id, StyleRule::new(selector.clone()));
956                        }
957                    }
958
959                    CssRule::Keyframes(keyframes_rule) => {
960                        let name = keyframes_rule.name.as_string();
961
962                        let animation_id = self.animation_manager.create();
963
964                        for keyframes in keyframes_rule.keyframes {
965                            for selector in keyframes.selectors.iter() {
966                                let time = match selector {
967                                    KeyframeSelector::From => 0.0,
968                                    KeyframeSelector::To => 1.0,
969                                    KeyframeSelector::Percentage(percentage) => {
970                                        percentage.0 / 100.0
971                                    }
972                                };
973
974                                self.add_keyframe(
975                                    animation_id,
976                                    time,
977                                    &keyframes.declarations.declarations,
978                                );
979                            }
980                        }
981
982                        self.animations.insert(name, animation_id);
983                    }
984
985                    _ => {}
986                }
987            }
988        } else {
989            println!("Failed to parse stylesheet");
990        }
991    }
992
993    fn insert_transition(&mut self, rule_id: Rule, transition: &Transition) {
994        let animation = self.animation_manager.create();
995        match transition.property.as_ref() {
996            "display" => {
997                self.display.insert_animation(animation, self.add_transition(transition));
998                self.display.insert_transition(rule_id, animation);
999            }
1000
1001            "opacity" => {
1002                self.opacity.insert_animation(animation, self.add_transition(transition));
1003                self.opacity.insert_transition(rule_id, animation);
1004            }
1005
1006            "clip-path" => {
1007                self.clip_path.insert_animation(animation, self.add_transition(transition));
1008                self.clip_path.insert_transition(rule_id, animation);
1009            }
1010
1011            "transform" => {
1012                self.transform.insert_animation(animation, self.add_transition(transition));
1013                self.transform.insert_transition(rule_id, animation);
1014            }
1015
1016            "transform-origin" => {
1017                self.transform_origin.insert_animation(animation, self.add_transition(transition));
1018                self.transform_origin.insert_transition(rule_id, animation);
1019            }
1020
1021            "translate" => {
1022                self.translate.insert_animation(animation, self.add_transition(transition));
1023                self.translate.insert_transition(rule_id, animation);
1024            }
1025
1026            "rotate" => {
1027                self.rotate.insert_animation(animation, self.add_transition(transition));
1028                self.rotate.insert_transition(rule_id, animation);
1029            }
1030
1031            "scale" => {
1032                self.scale.insert_animation(animation, self.add_transition(transition));
1033                self.scale.insert_transition(rule_id, animation);
1034            }
1035
1036            "border" => {
1037                self.border_width.insert_animation(animation, self.add_transition(transition));
1038                self.border_width.insert_transition(rule_id, animation);
1039                self.border_color.insert_animation(animation, self.add_transition(transition));
1040                self.border_color.insert_transition(rule_id, animation);
1041            }
1042
1043            "border-width" => {
1044                self.border_width.insert_animation(animation, self.add_transition(transition));
1045                self.border_width.insert_transition(rule_id, animation);
1046            }
1047
1048            "border-color" => {
1049                self.border_color.insert_animation(animation, self.add_transition(transition));
1050                self.border_color.insert_transition(rule_id, animation);
1051            }
1052
1053            "corner-radius" => {
1054                self.corner_bottom_left_radius
1055                    .insert_animation(animation, self.add_transition(transition));
1056                self.corner_bottom_left_radius.insert_transition(rule_id, animation);
1057                self.corner_bottom_right_radius
1058                    .insert_animation(animation, self.add_transition(transition));
1059                self.corner_bottom_right_radius.insert_transition(rule_id, animation);
1060                self.corner_top_left_radius
1061                    .insert_animation(animation, self.add_transition(transition));
1062                self.corner_top_left_radius.insert_transition(rule_id, animation);
1063                self.corner_top_right_radius
1064                    .insert_animation(animation, self.add_transition(transition));
1065                self.corner_top_right_radius.insert_transition(rule_id, animation);
1066            }
1067
1068            "corner-top-left-radius" => {
1069                self.corner_top_left_radius
1070                    .insert_animation(animation, self.add_transition(transition));
1071                self.corner_top_left_radius.insert_transition(rule_id, animation);
1072            }
1073
1074            "corner-top-right-radius" => {
1075                self.corner_top_right_radius
1076                    .insert_animation(animation, self.add_transition(transition));
1077                self.corner_top_right_radius.insert_transition(rule_id, animation);
1078            }
1079
1080            "corner-bottom-left-radius" => {
1081                self.corner_bottom_left_radius
1082                    .insert_animation(animation, self.add_transition(transition));
1083                self.corner_bottom_left_radius.insert_transition(rule_id, animation);
1084            }
1085
1086            "corner-bottom-right-radius" => {
1087                self.corner_bottom_right_radius
1088                    .insert_animation(animation, self.add_transition(transition));
1089                self.corner_bottom_right_radius.insert_transition(rule_id, animation);
1090            }
1091
1092            "outline" => {
1093                self.outline_width.insert_animation(animation, self.add_transition(transition));
1094                self.outline_width.insert_transition(rule_id, animation);
1095                self.outline_color.insert_animation(animation, self.add_transition(transition));
1096                self.outline_color.insert_transition(rule_id, animation);
1097            }
1098
1099            "outline-width" => {
1100                self.outline_width.insert_animation(animation, self.add_transition(transition));
1101                self.outline_width.insert_transition(rule_id, animation);
1102            }
1103
1104            "outline-color" => {
1105                self.outline_color.insert_animation(animation, self.add_transition(transition));
1106                self.outline_color.insert_transition(rule_id, animation);
1107            }
1108
1109            "outline-offset" => {
1110                self.outline_offset.insert_animation(animation, self.add_transition(transition));
1111                self.outline_offset.insert_transition(rule_id, animation);
1112            }
1113
1114            "background-color" => {
1115                self.background_color.insert_animation(animation, self.add_transition(transition));
1116                self.background_color.insert_transition(rule_id, animation);
1117            }
1118
1119            "background-image" => {
1120                self.background_image.insert_animation(animation, self.add_transition(transition));
1121                self.background_image.insert_transition(rule_id, animation);
1122            }
1123
1124            "background-size" => {
1125                self.background_size.insert_animation(animation, self.add_transition(transition));
1126                self.background_size.insert_transition(rule_id, animation);
1127            }
1128
1129            "shadow" => {
1130                self.shadow.insert_animation(animation, self.add_transition(transition));
1131                self.shadow.insert_transition(rule_id, animation);
1132            }
1133
1134            "color" => {
1135                self.font_color.insert_animation(animation, self.add_transition(transition));
1136                self.font_color.insert_transition(rule_id, animation);
1137            }
1138
1139            "font-size" => {
1140                self.font_size.insert_animation(animation, self.add_transition(transition));
1141                self.font_size.insert_transition(rule_id, animation);
1142            }
1143
1144            "line-height" => {
1145                self.line_height.insert_animation(animation, self.add_transition(transition));
1146                self.line_height.insert_transition(rule_id, animation);
1147            }
1148
1149            "caret-color" => {
1150                self.caret_color.insert_animation(animation, self.add_transition(transition));
1151                self.caret_color.insert_transition(rule_id, animation);
1152            }
1153
1154            "selection-color" => {
1155                self.selection_color.insert_animation(animation, self.add_transition(transition));
1156                self.selection_color.insert_transition(rule_id, animation);
1157            }
1158
1159            "left" => {
1160                self.left.insert_animation(animation, self.add_transition(transition));
1161                self.left.insert_transition(rule_id, animation);
1162            }
1163
1164            "right" => {
1165                self.right.insert_animation(animation, self.add_transition(transition));
1166                self.right.insert_transition(rule_id, animation);
1167            }
1168
1169            "top" => {
1170                self.top.insert_animation(animation, self.add_transition(transition));
1171                self.top.insert_transition(rule_id, animation);
1172            }
1173
1174            "bottom" => {
1175                self.bottom.insert_animation(animation, self.add_transition(transition));
1176                self.bottom.insert_transition(rule_id, animation);
1177            }
1178
1179            "padding-left" => {
1180                self.padding_left.insert_animation(animation, self.add_transition(transition));
1181                self.padding_left.insert_transition(rule_id, animation);
1182            }
1183
1184            "padding-right" => {
1185                self.padding_right.insert_animation(animation, self.add_transition(transition));
1186                self.padding_right.insert_transition(rule_id, animation);
1187            }
1188
1189            "padding-top" => {
1190                self.padding_top.insert_animation(animation, self.add_transition(transition));
1191                self.padding_top.insert_transition(rule_id, animation);
1192            }
1193
1194            "padding-bottom" => {
1195                self.padding_bottom.insert_animation(animation, self.add_transition(transition));
1196                self.padding_bottom.insert_transition(rule_id, animation);
1197            }
1198
1199            "horizontal-gap" => {
1200                self.horizontal_gap.insert_animation(animation, self.add_transition(transition));
1201                self.horizontal_gap.insert_transition(rule_id, animation);
1202            }
1203
1204            "vertical-gap" => {
1205                self.vertical_gap.insert_animation(animation, self.add_transition(transition));
1206                self.vertical_gap.insert_transition(rule_id, animation);
1207            }
1208
1209            "gap" => {
1210                self.horizontal_gap.insert_animation(animation, self.add_transition(transition));
1211                self.horizontal_gap.insert_transition(rule_id, animation);
1212                self.vertical_gap.insert_animation(animation, self.add_transition(transition));
1213                self.vertical_gap.insert_transition(rule_id, animation);
1214            }
1215
1216            "width" => {
1217                self.width.insert_animation(animation, self.add_transition(transition));
1218                self.width.insert_transition(rule_id, animation);
1219            }
1220
1221            "height" => {
1222                self.height.insert_animation(animation, self.add_transition(transition));
1223                self.height.insert_transition(rule_id, animation);
1224            }
1225
1226            "min-width" => {
1227                self.min_width.insert_animation(animation, self.add_transition(transition));
1228                self.min_width.insert_transition(rule_id, animation);
1229            }
1230
1231            "max-width" => {
1232                self.max_width.insert_animation(animation, self.add_transition(transition));
1233                self.max_width.insert_transition(rule_id, animation);
1234            }
1235
1236            "min-height" => {
1237                self.min_height.insert_animation(animation, self.add_transition(transition));
1238                self.min_height.insert_transition(rule_id, animation);
1239            }
1240
1241            "max-height" => {
1242                self.max_height.insert_animation(animation, self.add_transition(transition));
1243                self.max_height.insert_transition(rule_id, animation);
1244            }
1245
1246            "min-horizontal-gap" => {
1247                self.min_horizontal_gap
1248                    .insert_animation(animation, self.add_transition(transition));
1249                self.min_horizontal_gap.insert_transition(rule_id, animation);
1250            }
1251
1252            "max-horizontal-gap" => {
1253                self.max_horizontal_gap
1254                    .insert_animation(animation, self.add_transition(transition));
1255                self.max_horizontal_gap.insert_transition(rule_id, animation);
1256            }
1257
1258            "min-vertical-gap" => {
1259                self.min_vertical_gap.insert_animation(animation, self.add_transition(transition));
1260                self.min_vertical_gap.insert_transition(rule_id, animation);
1261            }
1262
1263            "max-vertical-gap" => {
1264                self.max_vertical_gap.insert_animation(animation, self.add_transition(transition));
1265                self.max_vertical_gap.insert_transition(rule_id, animation);
1266            }
1267
1268            "text-decoration-color" => {
1269                self.text_decoration_color
1270                    .insert_animation(animation, self.add_transition(transition));
1271                self.text_decoration_color.insert_transition(rule_id, animation);
1272            }
1273
1274            "fill" => {
1275                self.fill.insert_animation(animation, self.add_transition(transition));
1276                self.fill.insert_transition(rule_id, animation);
1277            }
1278
1279            property_name if property_name.starts_with("--") => {
1280                let mut s = DefaultHasher::new();
1281                property_name.hash(&mut s);
1282                let variable_name_hash = s.finish();
1283                // Pre-compute one typed AnimationState per store before taking any mutable
1284                // borrows (add_transition takes &self so this is fine).
1285                let anim_color: AnimationState<Color> = self.add_transition(transition);
1286                let anim_length: AnimationState<LengthOrPercentage> =
1287                    self.add_transition(transition);
1288                let anim_font_size: AnimationState<FontSize> = self.add_transition(transition);
1289                let anim_line_height: AnimationState<LineHeight> = self.add_transition(transition);
1290                let anim_units: AnimationState<Units> = self.add_transition(transition);
1291                let anim_opacity: AnimationState<Opacity> = self.add_transition(transition);
1292
1293                // Register in every custom-property store so whichever store the variable's
1294                // concrete value ends up in will actually animate.
1295                if let Some(store) = self.custom_color_props.get_mut(&variable_name_hash) {
1296                    store.insert_animation(animation, anim_color);
1297                    store.insert_transition(rule_id, animation);
1298                } else {
1299                    let mut store = AnimatableVarSet::default();
1300                    store.insert_animation(animation, anim_color);
1301                    store.insert_transition(rule_id, animation);
1302                    self.custom_color_props.insert(variable_name_hash, store);
1303                }
1304
1305                if let Some(store) = self.custom_length_props.get_mut(&variable_name_hash) {
1306                    store.insert_animation(animation, anim_length);
1307                    store.insert_transition(rule_id, animation);
1308                } else {
1309                    let mut store = AnimatableVarSet::default();
1310                    store.insert_animation(animation, anim_length);
1311                    store.insert_transition(rule_id, animation);
1312                    self.custom_length_props.insert(variable_name_hash, store);
1313                }
1314
1315                if let Some(store) = self.custom_font_size_props.get_mut(&variable_name_hash) {
1316                    store.insert_animation(animation, anim_font_size);
1317                    store.insert_transition(rule_id, animation);
1318                } else {
1319                    let mut store = AnimatableVarSet::default();
1320                    store.insert_animation(animation, anim_font_size);
1321                    store.insert_transition(rule_id, animation);
1322                    self.custom_font_size_props.insert(variable_name_hash, store);
1323                }
1324
1325                if let Some(store) = self.custom_line_height_props.get_mut(&variable_name_hash) {
1326                    store.insert_animation(animation, anim_line_height);
1327                    store.insert_transition(rule_id, animation);
1328                } else {
1329                    let mut store = AnimatableVarSet::default();
1330                    store.insert_animation(animation, anim_line_height);
1331                    store.insert_transition(rule_id, animation);
1332                    self.custom_line_height_props.insert(variable_name_hash, store);
1333                }
1334
1335                if let Some(store) = self.custom_units_props.get_mut(&variable_name_hash) {
1336                    store.insert_animation(animation, anim_units);
1337                    store.insert_transition(rule_id, animation);
1338                } else {
1339                    let mut store = AnimatableVarSet::default();
1340                    store.insert_animation(animation, anim_units);
1341                    store.insert_transition(rule_id, animation);
1342                    self.custom_units_props.insert(variable_name_hash, store);
1343                }
1344
1345                if let Some(store) = self.custom_opacity_props.get_mut(&variable_name_hash) {
1346                    store.insert_animation(animation, anim_opacity);
1347                    store.insert_transition(rule_id, animation);
1348                } else {
1349                    let mut store = AnimatableVarSet::default();
1350                    store.insert_animation(animation, anim_opacity);
1351                    store.insert_transition(rule_id, animation);
1352                    self.custom_opacity_props.insert(variable_name_hash, store);
1353                }
1354            }
1355
1356            _ => {}
1357        }
1358    }
1359
1360    fn insert_property(&mut self, rule_id: Rule, property: &Property) {
1361        fn variable_hash(var: &Variable<'_>) -> u64 {
1362            let mut s = DefaultHasher::new();
1363            var.name.hash(&mut s);
1364            s.finish()
1365        }
1366
1367        fn first_fallback_token<'i>(var: &'i Variable<'i>) -> Option<&'i TokenOrValue<'i>> {
1368            var.fallback.as_ref().and_then(|TokenList(tokens)| tokens.first())
1369        }
1370
1371        fn color_fallback(var: &Variable<'_>) -> Option<Color> {
1372            match first_fallback_token(var) {
1373                Some(TokenOrValue::Color(color)) => Some(*color),
1374                _ => None,
1375            }
1376        }
1377
1378        fn length_fallback(var: &Variable<'_>) -> Option<LengthOrPercentage> {
1379            match first_fallback_token(var) {
1380                Some(TokenOrValue::Token(CssToken::Dimension { value, unit, .. }))
1381                    if unit.as_ref().eq_ignore_ascii_case("px") =>
1382                {
1383                    Some(LengthOrPercentage::Length(Length::Value(LengthValue::Px(*value))))
1384                }
1385
1386                Some(TokenOrValue::Token(CssToken::Percentage { unit_value, .. })) => {
1387                    Some(LengthOrPercentage::Percentage(*unit_value * 100.0))
1388                }
1389
1390                _ => None,
1391            }
1392        }
1393
1394        fn font_size_fallback(var: &Variable<'_>) -> Option<FontSize> {
1395            match first_fallback_token(var) {
1396                Some(TokenOrValue::Token(CssToken::Dimension { value, unit, .. }))
1397                    if unit.as_ref().eq_ignore_ascii_case("px") =>
1398                {
1399                    Some(FontSize(Length::Value(LengthValue::Px(*value))))
1400                }
1401
1402                _ => None,
1403            }
1404        }
1405
1406        fn line_height_fallback(var: &Variable<'_>) -> Option<LineHeight> {
1407            match first_fallback_token(var) {
1408                Some(TokenOrValue::Token(CssToken::Dimension { value, unit, .. }))
1409                    if unit.as_ref().eq_ignore_ascii_case("px") =>
1410                {
1411                    Some(LineHeight::Length(Length::Value(LengthValue::Px(*value))))
1412                }
1413
1414                Some(TokenOrValue::Token(CssToken::Percentage { unit_value, .. })) => {
1415                    Some(LineHeight::Percentage(*unit_value * 100.0))
1416                }
1417
1418                Some(TokenOrValue::Token(CssToken::Number { value, .. })) => {
1419                    Some(LineHeight::Number(*value))
1420                }
1421
1422                Some(TokenOrValue::Token(CssToken::Ident(ident)))
1423                    if ident.as_ref().eq_ignore_ascii_case("normal") =>
1424                {
1425                    Some(LineHeight::Normal)
1426                }
1427
1428                _ => None,
1429            }
1430        }
1431
1432        fn units_fallback(var: &Variable<'_>) -> Option<Units> {
1433            match first_fallback_token(var) {
1434                Some(TokenOrValue::Token(CssToken::Dimension { value, unit, .. }))
1435                    if unit.as_ref().eq_ignore_ascii_case("px") =>
1436                {
1437                    Some(Units::Pixels(*value))
1438                }
1439
1440                Some(TokenOrValue::Token(CssToken::Dimension { value, unit, .. }))
1441                    if unit.as_ref().eq_ignore_ascii_case("s") =>
1442                {
1443                    Some(Units::Stretch(*value))
1444                }
1445
1446                Some(TokenOrValue::Token(CssToken::Ident(ident)))
1447                    if ident.as_ref().eq_ignore_ascii_case("auto") =>
1448                {
1449                    Some(Units::Auto)
1450                }
1451
1452                Some(TokenOrValue::Token(CssToken::Percentage { unit_value, .. })) => {
1453                    Some(Units::Percentage(*unit_value * 100.0))
1454                }
1455
1456                _ => None,
1457            }
1458        }
1459
1460        fn opacity_fallback(var: &Variable<'_>) -> Option<Opacity> {
1461            match first_fallback_token(var) {
1462                Some(TokenOrValue::Token(CssToken::Percentage { unit_value, .. })) => {
1463                    Some(Opacity(*unit_value))
1464                }
1465
1466                Some(TokenOrValue::Token(CssToken::Number { value, .. })) => Some(Opacity(*value)),
1467
1468                _ => None,
1469            }
1470        }
1471
1472        fn parse_shadow_list(tokens: &[TokenOrValue<'_>]) -> Option<Vec<Shadow>> {
1473            fn parse_shadow_length(token: &TokenOrValue<'_>) -> Option<Length> {
1474                match token {
1475                    TokenOrValue::Token(CssToken::Dimension { value, unit, .. })
1476                        if unit.as_ref().eq_ignore_ascii_case("px") =>
1477                    {
1478                        Some(Length::Value(LengthValue::Px(*value)))
1479                    }
1480
1481                    TokenOrValue::Token(CssToken::Number { value, .. }) if *value == 0.0 => {
1482                        Some(Length::Value(LengthValue::Px(0.0)))
1483                    }
1484
1485                    _ => None,
1486                }
1487            }
1488
1489            fn parse_single_shadow(tokens: &[TokenOrValue<'_>]) -> Option<Shadow> {
1490                let mut lengths: Vec<Length> = Vec::new();
1491                let mut color = None;
1492                let mut inset = false;
1493
1494                for token in tokens {
1495                    match token {
1496                        TokenOrValue::Token(CssToken::WhiteSpace(_)) => {}
1497
1498                        TokenOrValue::Color(c) => {
1499                            if color.is_some() {
1500                                return None;
1501                            }
1502                            color = Some(*c);
1503                        }
1504
1505                        TokenOrValue::Token(CssToken::Ident(ident))
1506                            if ident.as_ref().eq_ignore_ascii_case("inset") =>
1507                        {
1508                            if inset {
1509                                return None;
1510                            }
1511                            inset = true;
1512                        }
1513
1514                        other => {
1515                            if let Some(length) = parse_shadow_length(other) {
1516                                lengths.push(length);
1517                            } else {
1518                                return None;
1519                            }
1520                        }
1521                    }
1522                }
1523
1524                if !(2..=4).contains(&lengths.len()) {
1525                    return None;
1526                }
1527
1528                let x_offset = lengths[0].clone();
1529                let y_offset = lengths[1].clone();
1530                let blur_radius = lengths.get(2).cloned();
1531                let spread_radius = lengths.get(3).cloned();
1532
1533                Some(Shadow::new(x_offset, y_offset, blur_radius, spread_radius, color, inset))
1534            }
1535
1536            let mut parts = Vec::<&[TokenOrValue<'_>]>::new();
1537            let mut start = 0usize;
1538            for (idx, token) in tokens.iter().enumerate() {
1539                if matches!(token, TokenOrValue::Token(CssToken::Comma)) {
1540                    parts.push(&tokens[start..idx]);
1541                    start = idx + 1;
1542                }
1543            }
1544            parts.push(&tokens[start..]);
1545
1546            let mut parsed = Vec::new();
1547            for part in parts {
1548                let shadow = parse_single_shadow(part)?;
1549                parsed.push(shadow);
1550            }
1551
1552            Some(parsed)
1553        }
1554
1555        fn shadow_fallback(var: &Variable<'_>) -> Option<Vec<Shadow>> {
1556            var.fallback.as_ref().and_then(|TokenList(tokens)| parse_shadow_list(tokens))
1557        }
1558
1559        match property.clone() {
1560            // Display
1561            Property::Display(display) => {
1562                self.display.insert_rule(rule_id, display);
1563            }
1564
1565            // Visibility
1566            Property::Visibility(visibility) => {
1567                self.visibility.insert_rule(rule_id, visibility);
1568            }
1569
1570            // Opacity
1571            Property::Opacity(opacity) => {
1572                self.opacity.insert_rule(rule_id, opacity);
1573            }
1574
1575            // Clipping
1576            Property::ClipPath(clip) => {
1577                self.clip_path.insert_rule(rule_id, clip);
1578            }
1579
1580            // Filters
1581            Property::Filter(filter) => {
1582                self.filter.insert_rule(rule_id, filter);
1583            }
1584
1585            Property::BackdropFilter(filter) => {
1586                self.backdrop_filter.insert_rule(rule_id, filter);
1587            }
1588
1589            // Blend Mode
1590            Property::BlendMode(blend_mode) => {
1591                self.blend_mode.insert_rule(rule_id, blend_mode);
1592            }
1593
1594            // Layout Type
1595            Property::LayoutType(layout_type) => {
1596                self.layout_type.insert_rule(rule_id, layout_type);
1597            }
1598
1599            // Position Type
1600            Property::PositionType(position) => {
1601                self.position_type.insert_rule(rule_id, position);
1602            }
1603
1604            Property::Alignment(alignment) => {
1605                self.alignment.insert_rule(rule_id, alignment);
1606            }
1607
1608            Property::Direction(direction) => {
1609                self.direction.insert_rule(rule_id, direction);
1610            }
1611
1612            Property::Wrap(value) => {
1613                self.wrap.insert_rule(rule_id, value);
1614            }
1615            Property::GridColumns(columns) => {
1616                self.grid_columns.insert_rule(rule_id, columns);
1617            }
1618
1619            Property::GridRows(rows) => {
1620                self.grid_rows.insert_rule(rule_id, rows);
1621            }
1622
1623            Property::ColumnStart(start) => {
1624                self.column_start.insert_rule(rule_id, start);
1625            }
1626
1627            Property::ColumnSpan(span) => {
1628                self.column_span.insert_rule(rule_id, span);
1629            }
1630
1631            Property::RowStart(start) => {
1632                self.row_start.insert_rule(rule_id, start);
1633            }
1634
1635            Property::RowSpan(span) => {
1636                self.row_span.insert_rule(rule_id, span);
1637            }
1638
1639            // Space
1640            Property::Space(space) => {
1641                self.left.insert_rule(rule_id, space);
1642                self.right.insert_rule(rule_id, space);
1643                self.top.insert_rule(rule_id, space);
1644                self.bottom.insert_rule(rule_id, space);
1645            }
1646
1647            Property::Left(left) => {
1648                self.left.insert_rule(rule_id, left);
1649            }
1650
1651            Property::Right(right) => {
1652                self.right.insert_rule(rule_id, right);
1653            }
1654
1655            Property::Top(top) => {
1656                self.top.insert_rule(rule_id, top);
1657            }
1658
1659            Property::Bottom(bottom) => {
1660                self.bottom.insert_rule(rule_id, bottom);
1661            }
1662
1663            // Size
1664            Property::Size(size) => {
1665                self.width.insert_rule(rule_id, size);
1666                self.height.insert_rule(rule_id, size);
1667            }
1668
1669            Property::Width(width) => {
1670                self.width.insert_rule(rule_id, width);
1671            }
1672
1673            Property::Height(height) => {
1674                self.height.insert_rule(rule_id, height);
1675            }
1676
1677            // Padding
1678            Property::Padding(padding) => {
1679                self.padding_left.insert_rule(rule_id, padding);
1680                self.padding_right.insert_rule(rule_id, padding);
1681                self.padding_top.insert_rule(rule_id, padding);
1682                self.padding_bottom.insert_rule(rule_id, padding);
1683            }
1684
1685            Property::PaddingLeft(padding_left) => {
1686                self.padding_left.insert_rule(rule_id, padding_left);
1687            }
1688
1689            Property::PaddingRight(padding_right) => {
1690                self.padding_right.insert_rule(rule_id, padding_right);
1691            }
1692
1693            Property::PaddingTop(padding_top) => {
1694                self.padding_top.insert_rule(rule_id, padding_top);
1695            }
1696
1697            Property::PaddingBottom(padding_bottom) => {
1698                self.padding_bottom.insert_rule(rule_id, padding_bottom);
1699            }
1700
1701            Property::VerticalGap(vertical_gap) => {
1702                self.vertical_gap.insert_rule(rule_id, vertical_gap);
1703            }
1704
1705            Property::HorizontalGap(horizontal_gap) => {
1706                self.horizontal_gap.insert_rule(rule_id, horizontal_gap);
1707            }
1708
1709            Property::Gap(gap) => {
1710                self.horizontal_gap.insert_rule(rule_id, gap);
1711                self.vertical_gap.insert_rule(rule_id, gap);
1712            }
1713
1714            // Size Constraints
1715            Property::MinSize(min_size) => {
1716                self.min_width.insert_rule(rule_id, min_size);
1717                self.min_height.insert_rule(rule_id, min_size);
1718            }
1719
1720            Property::MinWidth(min_width) => {
1721                self.min_width.insert_rule(rule_id, min_width);
1722            }
1723
1724            Property::MinHeight(min_height) => {
1725                self.min_height.insert_rule(rule_id, min_height);
1726            }
1727
1728            Property::MaxSize(max_size) => {
1729                self.max_width.insert_rule(rule_id, max_size);
1730                self.max_height.insert_rule(rule_id, max_size);
1731            }
1732
1733            Property::MaxWidth(max_width) => {
1734                self.max_width.insert_rule(rule_id, max_width);
1735            }
1736
1737            Property::MaxHeight(max_height) => {
1738                self.max_height.insert_rule(rule_id, max_height);
1739            }
1740
1741            // Gap Constraints
1742            Property::MinGap(min_gap) => {
1743                self.min_horizontal_gap.insert_rule(rule_id, min_gap);
1744                self.min_vertical_gap.insert_rule(rule_id, min_gap);
1745            }
1746
1747            Property::MinHorizontalGap(min_gap) => {
1748                self.min_horizontal_gap.insert_rule(rule_id, min_gap);
1749            }
1750
1751            Property::MinVerticalGap(min_gap) => {
1752                self.min_vertical_gap.insert_rule(rule_id, min_gap);
1753            }
1754
1755            Property::MaxGap(max_gap) => {
1756                self.max_horizontal_gap.insert_rule(rule_id, max_gap);
1757                self.max_vertical_gap.insert_rule(rule_id, max_gap);
1758            }
1759
1760            Property::MaxHorizontalGap(max_gap) => {
1761                self.max_horizontal_gap.insert_rule(rule_id, max_gap);
1762            }
1763
1764            Property::MaxVerticalGap(max_gap) => {
1765                self.max_vertical_gap.insert_rule(rule_id, max_gap);
1766            }
1767
1768            // Background Colour
1769            Property::BackgroundColor(color) => {
1770                self.background_color.insert_rule(rule_id, color);
1771            }
1772
1773            // Border
1774            Property::Border(border) => {
1775                if let Some(border_color) = border.color {
1776                    self.border_color.insert_rule(rule_id, border_color);
1777                }
1778
1779                if let Some(border_width) = border.width {
1780                    self.border_width.insert_rule(rule_id, border_width.into());
1781                }
1782
1783                if let Some(border_style) = border.style {
1784                    self.border_style.insert_rule(rule_id, border_style.top);
1785                }
1786            }
1787
1788            // Border
1789            Property::BorderWidth(border_width) => {
1790                self.border_width.insert_rule(rule_id, border_width.top.0);
1791            }
1792
1793            Property::BorderColor(color) => {
1794                self.border_color.insert_rule(rule_id, color);
1795            }
1796
1797            Property::BorderStyle(style) => {
1798                self.border_style.insert_rule(rule_id, style.top);
1799            }
1800
1801            // Border Radius
1802            Property::CornerRadius(corner_radius) => {
1803                self.corner_bottom_left_radius.insert_rule(rule_id, corner_radius.bottom_left);
1804                self.corner_bottom_right_radius.insert_rule(rule_id, corner_radius.bottom_right);
1805                self.corner_top_left_radius.insert_rule(rule_id, corner_radius.top_left);
1806                self.corner_top_right_radius.insert_rule(rule_id, corner_radius.top_right);
1807            }
1808
1809            Property::CornerBottomLeftRadius(corner_radius) => {
1810                self.corner_bottom_left_radius.insert_rule(rule_id, corner_radius);
1811            }
1812
1813            Property::CornerTopLeftRadius(corner_radius) => {
1814                self.corner_top_left_radius.insert_rule(rule_id, corner_radius);
1815            }
1816
1817            Property::CornerBottomRightRadius(corner_radius) => {
1818                self.corner_bottom_right_radius.insert_rule(rule_id, corner_radius);
1819            }
1820
1821            Property::CornerTopRightRadius(corner_radius) => {
1822                self.corner_top_right_radius.insert_rule(rule_id, corner_radius);
1823            }
1824
1825            // Corner Shape
1826            Property::CornerShape(corner_shape) => {
1827                self.corner_top_left_shape.insert_rule(rule_id, corner_shape.0);
1828                self.corner_top_right_shape.insert_rule(rule_id, corner_shape.1);
1829                self.corner_bottom_right_shape.insert_rule(rule_id, corner_shape.2);
1830                self.corner_bottom_left_shape.insert_rule(rule_id, corner_shape.3);
1831            }
1832
1833            Property::CornerTopLeftShape(corner_shape) => {
1834                self.corner_top_left_shape.insert_rule(rule_id, corner_shape);
1835            }
1836
1837            Property::CornerTopRightShape(corner_shape) => {
1838                self.corner_top_right_shape.insert_rule(rule_id, corner_shape);
1839            }
1840
1841            Property::CornerBottomLeftShape(corner_shape) => {
1842                self.corner_bottom_left_shape.insert_rule(rule_id, corner_shape);
1843            }
1844
1845            Property::CornerBottomRightShape(corner_shape) => {
1846                self.corner_bottom_right_shape.insert_rule(rule_id, corner_shape);
1847            }
1848
1849            // Font Family
1850            Property::FontFamily(font_family) => {
1851                self.font_family.insert_rule(
1852                    rule_id,
1853                    font_family
1854                        .iter()
1855                        .map(|family| match family {
1856                            FontFamily::Named(name) => FamilyOwned::Named(name.to_string()),
1857                            FontFamily::Generic(generic) => FamilyOwned::Generic(*generic),
1858                        })
1859                        .collect::<Vec<_>>(),
1860                );
1861            }
1862
1863            // Font Color
1864            Property::FontColor(font_color) => {
1865                self.font_color.insert_rule(rule_id, font_color);
1866            }
1867
1868            // Font Size
1869            Property::FontSize(font_size) => {
1870                self.font_size.insert_rule(rule_id, font_size);
1871            }
1872
1873            // Font Weight
1874            Property::FontWeight(font_weight) => {
1875                self.font_weight.insert_rule(rule_id, font_weight);
1876            }
1877
1878            // Font Slant
1879            Property::FontSlant(font_slant) => {
1880                self.font_slant.insert_rule(rule_id, font_slant);
1881            }
1882
1883            // Font Width
1884            Property::FontWidth(font_width) => {
1885                self.font_width.insert_rule(rule_id, font_width);
1886            }
1887
1888            // Font Variation Settings
1889            Property::FontVariationSettings(font_variation_settings) => {
1890                self.font_variation_settings.insert_rule(rule_id, font_variation_settings);
1891            }
1892
1893            // Caret Color
1894            Property::CaretColor(caret_color) => {
1895                self.caret_color.insert_rule(rule_id, caret_color);
1896            }
1897
1898            // Selection Color
1899            Property::SelectionColor(selection_color) => {
1900                self.selection_color.insert_rule(rule_id, selection_color);
1901            }
1902
1903            // Transform
1904            Property::Transform(transforms) => {
1905                self.transform.insert_rule(rule_id, transforms);
1906            }
1907
1908            Property::TransformOrigin(transform_origin) => {
1909                let x = transform_origin.x.to_length_or_percentage();
1910                let y = transform_origin.y.to_length_or_percentage();
1911                self.transform_origin.insert_rule(rule_id, Translate { x, y });
1912            }
1913
1914            Property::Translate(translate) => {
1915                self.translate.insert_rule(rule_id, translate);
1916            }
1917
1918            Property::Rotate(rotate) => {
1919                self.rotate.insert_rule(rule_id, rotate);
1920            }
1921
1922            Property::Scale(scale) => {
1923                self.scale.insert_rule(rule_id, scale);
1924            }
1925
1926            // Overflow
1927            Property::Overflow(overflow) => {
1928                self.overflowx.insert_rule(rule_id, overflow);
1929                self.overflowy.insert_rule(rule_id, overflow);
1930            }
1931
1932            Property::OverflowX(overflow) => {
1933                self.overflowx.insert_rule(rule_id, overflow);
1934            }
1935
1936            Property::OverflowY(overflow) => {
1937                self.overflowy.insert_rule(rule_id, overflow);
1938            }
1939
1940            // Z Index
1941            Property::ZIndex(z_index) => self.z_index.insert_rule(rule_id, z_index),
1942
1943            // Outline
1944            Property::Outline(outline) => {
1945                if let Some(outline_color) = outline.color {
1946                    self.outline_color.insert_rule(rule_id, outline_color);
1947                }
1948
1949                if let Some(outline_width) = outline.width {
1950                    self.outline_width.insert_rule(rule_id, outline_width.into());
1951                }
1952            }
1953
1954            Property::OutlineColor(outline_color) => {
1955                self.outline_color.insert_rule(rule_id, outline_color);
1956            }
1957
1958            Property::OutlineWidth(outline_width) => {
1959                self.outline_width.insert_rule(rule_id, outline_width.left.0);
1960            }
1961
1962            Property::OutlineOffset(outline_offset) => {
1963                self.outline_offset.insert_rule(rule_id, outline_offset);
1964            }
1965
1966            // Background Images & Gradients
1967            Property::BackgroundImage(images) => {
1968                let images = images
1969                    .into_iter()
1970                    .filter_map(|img| match img {
1971                        BackgroundImage::None => None,
1972                        BackgroundImage::Gradient(gradient) => {
1973                            Some(ImageOrGradient::Gradient(*gradient))
1974                        }
1975                        BackgroundImage::Url(url) => {
1976                            Some(ImageOrGradient::Image(url.url.to_string()))
1977                        }
1978                    })
1979                    .collect::<Vec<_>>();
1980
1981                self.background_image.insert_rule(rule_id, images);
1982            }
1983
1984            // Background Size
1985            Property::BackgroundSize(sizes) => {
1986                self.background_size.insert_rule(rule_id, sizes);
1987            }
1988
1989            // Text Wrapping
1990            Property::TextWrap(text_wrap) => {
1991                self.text_wrap.insert_rule(rule_id, text_wrap);
1992            }
1993
1994            // Text Alignment
1995            Property::TextAlign(text_align) => {
1996                self.text_align.insert_rule(rule_id, text_align);
1997            }
1998
1999            // Box Shadows
2000            Property::Shadow(shadows) => {
2001                self.shadow.insert_rule(rule_id, shadows);
2002            }
2003
2004            // Cursor Icon
2005            Property::Cursor(cursor) => {
2006                self.cursor.insert_rule(rule_id, cursor);
2007            }
2008
2009            Property::PointerEvents(pointer_events) => {
2010                self.pointer_events.insert_rule(rule_id, pointer_events);
2011            }
2012
2013            Property::TextOverflow(text_overflow) => {
2014                self.text_overflow.insert_rule(rule_id, text_overflow);
2015            }
2016            Property::LineHeight(line_height) => {
2017                self.line_height.insert_rule(rule_id, line_height);
2018            }
2019            Property::LineClamp(line_clamp) => {
2020                self.line_clamp.insert_rule(rule_id, line_clamp);
2021            }
2022            Property::TextDecoration(decoration) => {
2023                self.text_decoration_line.insert_rule(rule_id, decoration.line);
2024                self.text_decoration_style.insert_rule(rule_id, decoration.style);
2025                self.text_decoration_color.insert_rule(rule_id, decoration.color.into());
2026            }
2027            Property::TextDecorationLine(line) => {
2028                self.text_decoration_line.insert_rule(rule_id, line);
2029            }
2030            Property::TextDecorationColor(decoration_color) => {
2031                self.text_decoration_color.insert_rule(rule_id, decoration_color);
2032            }
2033            Property::TextDecorationStyle(decoration_style) => {
2034                self.text_decoration_style.insert_rule(rule_id, decoration_style);
2035            }
2036            Property::TextStroke(stroke) => {
2037                self.text_stroke_width.insert_rule(rule_id, stroke.width);
2038                self.text_stroke_style.insert_rule(rule_id, stroke.style);
2039            }
2040            Property::TextStrokeWidth(stroke_width) => {
2041                self.text_stroke_width.insert_rule(rule_id, stroke_width);
2042            }
2043            Property::TextStrokeStyle(stroke_style) => {
2044                self.text_stroke_style.insert_rule(rule_id, stroke_style);
2045            }
2046            Property::Fill(fill) => {
2047                self.fill.insert_rule(rule_id, fill);
2048            }
2049
2050            // Unparsed. TODO: Log the error.
2051            Property::Unparsed(unparsed) => {
2052                macro_rules! parse_color_var {
2053                    ($prop:expr) => {
2054                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2055                            $prop.insert_variable_rule(
2056                                rule_id,
2057                                variable_hash(var),
2058                                color_fallback(var),
2059                            );
2060                        }
2061                    };
2062                }
2063                macro_rules! parse_length_var {
2064                    ($($prop:expr),+) => {
2065                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2066                            let hash = variable_hash(var);
2067                            let fallback = length_fallback(var);
2068                            $($prop.insert_variable_rule(rule_id, hash, fallback.clone());)+
2069                        }
2070                    };
2071                }
2072                macro_rules! parse_font_size_var {
2073                    ($prop:expr) => {
2074                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2075                            $prop.insert_variable_rule(
2076                                rule_id,
2077                                variable_hash(var),
2078                                font_size_fallback(var),
2079                            );
2080                        }
2081                    };
2082                }
2083                macro_rules! parse_line_height_var {
2084                    ($prop:expr) => {
2085                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2086                            $prop.insert_variable_rule(
2087                                rule_id,
2088                                variable_hash(var),
2089                                line_height_fallback(var),
2090                            );
2091                        }
2092                    };
2093                }
2094                macro_rules! parse_units_var {
2095                    ($($prop:expr),+) => {
2096                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2097                            let hash = variable_hash(var);
2098                            let fallback = units_fallback(var);
2099                            $($prop.insert_variable_rule(rule_id, hash, fallback.clone());)+
2100                        }
2101                    };
2102                }
2103                match unparsed.name.as_ref() {
2104                    "background-color" => parse_color_var!(self.background_color),
2105                    "border-color" => parse_color_var!(self.border_color),
2106                    "outline-color" => parse_color_var!(self.outline_color),
2107                    "color" => parse_color_var!(self.font_color),
2108                    "caret-color" => parse_color_var!(self.caret_color),
2109                    "selection-color" => parse_color_var!(self.selection_color),
2110                    "fill" => parse_color_var!(self.fill),
2111                    "text-decoration-color" => parse_color_var!(self.text_decoration_color),
2112                    "font-size" => parse_font_size_var!(self.font_size),
2113                    "line-height" => parse_line_height_var!(self.line_height),
2114                    "corner-radius" => parse_length_var!(
2115                        self.corner_top_left_radius,
2116                        self.corner_top_right_radius,
2117                        self.corner_bottom_left_radius,
2118                        self.corner_bottom_right_radius
2119                    ),
2120                    "corner-top-left-radius" => parse_length_var!(self.corner_top_left_radius),
2121                    "corner-top-right-radius" => parse_length_var!(self.corner_top_right_radius),
2122                    "corner-bottom-left-radius" => {
2123                        parse_length_var!(self.corner_bottom_left_radius)
2124                    }
2125                    "corner-bottom-right-radius" => {
2126                        parse_length_var!(self.corner_bottom_right_radius)
2127                    }
2128                    "border-width" => parse_length_var!(self.border_width),
2129                    "border" => {
2130                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2131                            let hash = variable_hash(var);
2132                            self.border_width.insert_variable_rule(
2133                                rule_id,
2134                                hash,
2135                                length_fallback(var),
2136                            );
2137                            self.border_color.insert_variable_rule(
2138                                rule_id,
2139                                hash,
2140                                color_fallback(var),
2141                            );
2142                        }
2143                    }
2144                    "outline" => {
2145                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2146                            let hash = variable_hash(var);
2147                            self.outline_width.insert_variable_rule(
2148                                rule_id,
2149                                hash,
2150                                length_fallback(var),
2151                            );
2152                            self.outline_color.insert_variable_rule(
2153                                rule_id,
2154                                hash,
2155                                color_fallback(var),
2156                            );
2157                        }
2158                    }
2159                    "outline-width" => parse_length_var!(self.outline_width),
2160                    "outline-offset" => parse_length_var!(self.outline_offset),
2161                    "left" => parse_units_var!(self.left),
2162                    "right" => parse_units_var!(self.right),
2163                    "top" => parse_units_var!(self.top),
2164                    "bottom" => parse_units_var!(self.bottom),
2165                    "space" => parse_units_var!(self.left, self.right, self.top, self.bottom),
2166                    "width" => parse_units_var!(self.width),
2167                    "height" => parse_units_var!(self.height),
2168                    "size" => parse_units_var!(self.width, self.height),
2169                    "min-width" => parse_units_var!(self.min_width),
2170                    "max-width" => parse_units_var!(self.max_width),
2171                    "min-height" => parse_units_var!(self.min_height),
2172                    "max-height" => parse_units_var!(self.max_height),
2173                    "min-size" => parse_units_var!(self.min_width, self.min_height),
2174                    "max-size" => parse_units_var!(self.max_width, self.max_height),
2175                    "padding-left" => parse_units_var!(self.padding_left),
2176                    "padding-right" => parse_units_var!(self.padding_right),
2177                    "padding-top" => parse_units_var!(self.padding_top),
2178                    "padding-bottom" => parse_units_var!(self.padding_bottom),
2179                    "padding" => parse_units_var!(
2180                        self.padding_left,
2181                        self.padding_right,
2182                        self.padding_top,
2183                        self.padding_bottom
2184                    ),
2185                    "row-gap" | "vertical-gap" => parse_units_var!(self.vertical_gap),
2186                    "column-gap" | "horizontal-gap" => parse_units_var!(self.horizontal_gap),
2187                    "gap" => parse_units_var!(self.vertical_gap, self.horizontal_gap),
2188                    "min-gap" => {
2189                        parse_units_var!(self.min_horizontal_gap, self.min_vertical_gap)
2190                    }
2191                    "max-gap" => {
2192                        parse_units_var!(self.max_horizontal_gap, self.max_vertical_gap)
2193                    }
2194                    "opacity" => {
2195                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2196                            self.opacity.insert_variable_rule(
2197                                rule_id,
2198                                variable_hash(var),
2199                                opacity_fallback(var),
2200                            );
2201                        }
2202                    }
2203                    "shadow" => {
2204                        if let Some(TokenOrValue::Var(var)) = unparsed.value.0.first() {
2205                            self.shadow.insert_variable_rule(
2206                                rule_id,
2207                                variable_hash(var),
2208                                shadow_fallback(var),
2209                            );
2210                        }
2211                    }
2212                    n => warn!("Unparsed {} {:?}", n, unparsed.value),
2213                }
2214            }
2215
2216            Property::Custom(custom) => {
2217                let mut s = DefaultHasher::new();
2218                custom.name.hash(&mut s);
2219                let variable_name_hash = s.finish();
2220
2221                if let Some(shadows) = parse_shadow_list(&custom.value.0) {
2222                    if let Some(store) = self.custom_shadow_props.get_mut(&variable_name_hash) {
2223                        store.insert_rule(rule_id, shadows);
2224                    } else {
2225                        let mut store = AnimatableVarSet::default();
2226                        store.insert_rule(rule_id, shadows);
2227                        self.custom_shadow_props.insert(variable_name_hash, store);
2228                    }
2229                }
2230
2231                // Parse custom properties and store them
2232                for token in custom.value.0.iter() {
2233                    // Try parsing colors
2234                    if let TokenOrValue::Color(color) = token {
2235                        if let Some(store) = self.custom_color_props.get_mut(&variable_name_hash) {
2236                            store.insert_rule(rule_id, *color);
2237                        } else {
2238                            let mut store = AnimatableVarSet::default();
2239                            store.insert_rule(rule_id, *color);
2240                            self.custom_color_props.insert(variable_name_hash, store);
2241                        }
2242                    }
2243
2244                    // Parse length/percentage tokens into custom_length_props
2245                    match token {
2246                        TokenOrValue::Token(CssToken::Dimension { value, unit, .. }) => {
2247                            let lop = if unit.as_ref().eq_ignore_ascii_case("px") {
2248                                Some(LengthOrPercentage::Length(Length::Value(LengthValue::Px(
2249                                    *value,
2250                                ))))
2251                            } else {
2252                                None
2253                            };
2254                            if let Some(lop) = lop {
2255                                if let Some(store) =
2256                                    self.custom_length_props.get_mut(&variable_name_hash)
2257                                {
2258                                    store.insert_rule(rule_id, lop.clone());
2259                                } else {
2260                                    let mut store = AnimatableVarSet::default();
2261                                    store.insert_rule(rule_id, lop.clone());
2262                                    self.custom_length_props.insert(variable_name_hash, store);
2263                                }
2264                                // Also try storing as FontSize
2265                                let fs = FontSize(Length::Value(LengthValue::Px(*value)));
2266                                if let Some(store) =
2267                                    self.custom_font_size_props.get_mut(&variable_name_hash)
2268                                {
2269                                    store.insert_rule(rule_id, fs);
2270                                } else {
2271                                    let mut store = AnimatableVarSet::default();
2272                                    store.insert_rule(rule_id, fs);
2273                                    self.custom_font_size_props.insert(variable_name_hash, store);
2274                                }
2275                                // Also try storing as LineHeight::Length
2276                                let line_height =
2277                                    LineHeight::Length(Length::Value(LengthValue::Px(*value)));
2278                                if let Some(store) =
2279                                    self.custom_line_height_props.get_mut(&variable_name_hash)
2280                                {
2281                                    store.insert_rule(rule_id, line_height);
2282                                } else {
2283                                    let mut store = AnimatableVarSet::default();
2284                                    store.insert_rule(rule_id, line_height);
2285                                    self.custom_line_height_props.insert(variable_name_hash, store);
2286                                }
2287                                // Also store as Units::Pixels
2288                                let units_val = Units::Pixels(*value);
2289                                if let Some(store) =
2290                                    self.custom_units_props.get_mut(&variable_name_hash)
2291                                {
2292                                    store.insert_rule(rule_id, units_val);
2293                                } else {
2294                                    let mut store = AnimatableVarSet::default();
2295                                    store.insert_rule(rule_id, units_val);
2296                                    self.custom_units_props.insert(variable_name_hash, store);
2297                                }
2298                            } else if unit.as_ref().eq_ignore_ascii_case("s") {
2299                                // "1s" => Units::Stretch(1.0)
2300                                let units_val = Units::Stretch(*value);
2301                                if let Some(store) =
2302                                    self.custom_units_props.get_mut(&variable_name_hash)
2303                                {
2304                                    store.insert_rule(rule_id, units_val);
2305                                } else {
2306                                    let mut store = AnimatableVarSet::default();
2307                                    store.insert_rule(rule_id, units_val);
2308                                    self.custom_units_props.insert(variable_name_hash, store);
2309                                }
2310                            }
2311                        }
2312                        TokenOrValue::Token(CssToken::Ident(ident))
2313                            if ident.as_ref().eq_ignore_ascii_case("auto") =>
2314                        {
2315                            let units_val = Units::Auto;
2316                            if let Some(store) =
2317                                self.custom_units_props.get_mut(&variable_name_hash)
2318                            {
2319                                store.insert_rule(rule_id, units_val);
2320                            } else {
2321                                let mut store = AnimatableVarSet::default();
2322                                store.insert_rule(rule_id, units_val);
2323                                self.custom_units_props.insert(variable_name_hash, store);
2324                            }
2325                        }
2326                        TokenOrValue::Token(CssToken::Percentage { unit_value, .. }) => {
2327                            let lop = LengthOrPercentage::Percentage(*unit_value * 100.0);
2328                            if let Some(store) =
2329                                self.custom_length_props.get_mut(&variable_name_hash)
2330                            {
2331                                store.insert_rule(rule_id, lop);
2332                            } else {
2333                                let mut store = AnimatableVarSet::default();
2334                                store.insert_rule(rule_id, lop);
2335                                self.custom_length_props.insert(variable_name_hash, store);
2336                            }
2337                            // Also store as LineHeight::Percentage
2338                            let line_height = LineHeight::Percentage(*unit_value * 100.0);
2339                            if let Some(store) =
2340                                self.custom_line_height_props.get_mut(&variable_name_hash)
2341                            {
2342                                store.insert_rule(rule_id, line_height);
2343                            } else {
2344                                let mut store = AnimatableVarSet::default();
2345                                store.insert_rule(rule_id, line_height);
2346                                self.custom_line_height_props.insert(variable_name_hash, store);
2347                            }
2348                            // Also store as Units::Percentage
2349                            let units_val = Units::Percentage(*unit_value * 100.0);
2350                            if let Some(store) =
2351                                self.custom_units_props.get_mut(&variable_name_hash)
2352                            {
2353                                store.insert_rule(rule_id, units_val);
2354                            } else {
2355                                let mut store = AnimatableVarSet::default();
2356                                store.insert_rule(rule_id, units_val);
2357                                self.custom_units_props.insert(variable_name_hash, store);
2358                            }
2359                            // Also store as Opacity (percentage as 0..1)
2360                            let opacity_val = Opacity(*unit_value);
2361                            if let Some(store) =
2362                                self.custom_opacity_props.get_mut(&variable_name_hash)
2363                            {
2364                                store.insert_rule(rule_id, opacity_val);
2365                            } else {
2366                                let mut store = AnimatableVarSet::default();
2367                                store.insert_rule(rule_id, opacity_val);
2368                                self.custom_opacity_props.insert(variable_name_hash, store);
2369                            }
2370                        }
2371                        TokenOrValue::Var(var) => {
2372                            let name_hash = variable_hash(var);
2373                            // Store var reference in all maps (type is unknown at parse time)
2374                            if let Some(store) =
2375                                self.custom_color_props.get_mut(&variable_name_hash)
2376                            {
2377                                store.insert_variable_rule(rule_id, name_hash, color_fallback(var));
2378                            } else {
2379                                let mut store = AnimatableVarSet::default();
2380                                store.insert_variable_rule(rule_id, name_hash, color_fallback(var));
2381                                self.custom_color_props.insert(variable_name_hash, store);
2382                            }
2383                            if let Some(store) =
2384                                self.custom_length_props.get_mut(&variable_name_hash)
2385                            {
2386                                store.insert_variable_rule(
2387                                    rule_id,
2388                                    name_hash,
2389                                    length_fallback(var),
2390                                );
2391                            } else {
2392                                let mut store: AnimatableVarSet<LengthOrPercentage> =
2393                                    AnimatableVarSet::default();
2394                                store.insert_variable_rule(
2395                                    rule_id,
2396                                    name_hash,
2397                                    length_fallback(var),
2398                                );
2399                                self.custom_length_props.insert(variable_name_hash, store);
2400                            }
2401                            if let Some(store) =
2402                                self.custom_font_size_props.get_mut(&variable_name_hash)
2403                            {
2404                                store.insert_variable_rule(
2405                                    rule_id,
2406                                    name_hash,
2407                                    font_size_fallback(var),
2408                                );
2409                            } else {
2410                                let mut store: AnimatableVarSet<FontSize> =
2411                                    AnimatableVarSet::default();
2412                                store.insert_variable_rule(
2413                                    rule_id,
2414                                    name_hash,
2415                                    font_size_fallback(var),
2416                                );
2417                                self.custom_font_size_props.insert(variable_name_hash, store);
2418                            }
2419                            if let Some(store) =
2420                                self.custom_line_height_props.get_mut(&variable_name_hash)
2421                            {
2422                                store.insert_variable_rule(
2423                                    rule_id,
2424                                    name_hash,
2425                                    line_height_fallback(var),
2426                                );
2427                            } else {
2428                                let mut store: AnimatableVarSet<LineHeight> =
2429                                    AnimatableVarSet::default();
2430                                store.insert_variable_rule(
2431                                    rule_id,
2432                                    name_hash,
2433                                    line_height_fallback(var),
2434                                );
2435                                self.custom_line_height_props.insert(variable_name_hash, store);
2436                            }
2437                            if let Some(store) =
2438                                self.custom_units_props.get_mut(&variable_name_hash)
2439                            {
2440                                store.insert_variable_rule(rule_id, name_hash, units_fallback(var));
2441                            } else {
2442                                let mut store: AnimatableVarSet<Units> =
2443                                    AnimatableVarSet::default();
2444                                store.insert_variable_rule(rule_id, name_hash, units_fallback(var));
2445                                self.custom_units_props.insert(variable_name_hash, store);
2446                            }
2447                            if let Some(store) =
2448                                self.custom_opacity_props.get_mut(&variable_name_hash)
2449                            {
2450                                store.insert_variable_rule(
2451                                    rule_id,
2452                                    name_hash,
2453                                    opacity_fallback(var),
2454                                );
2455                            } else {
2456                                let mut store: AnimatableVarSet<Opacity> =
2457                                    AnimatableVarSet::default();
2458                                store.insert_variable_rule(
2459                                    rule_id,
2460                                    name_hash,
2461                                    opacity_fallback(var),
2462                                );
2463                                self.custom_opacity_props.insert(variable_name_hash, store);
2464                            }
2465
2466                            if let Some(store) =
2467                                self.custom_shadow_props.get_mut(&variable_name_hash)
2468                            {
2469                                store.insert_variable_rule(
2470                                    rule_id,
2471                                    name_hash,
2472                                    shadow_fallback(var),
2473                                );
2474                            } else {
2475                                let mut store: AnimatableVarSet<Vec<Shadow>> =
2476                                    AnimatableVarSet::default();
2477                                store.insert_variable_rule(
2478                                    rule_id,
2479                                    name_hash,
2480                                    shadow_fallback(var),
2481                                );
2482                                self.custom_shadow_props.insert(variable_name_hash, store);
2483                            }
2484                        }
2485                        TokenOrValue::Token(CssToken::Number { value, .. }) => {
2486                            // Plain number like 0.5 → Opacity
2487                            let opacity_val = Opacity(*value);
2488                            if let Some(store) =
2489                                self.custom_opacity_props.get_mut(&variable_name_hash)
2490                            {
2491                                store.insert_rule(rule_id, opacity_val);
2492                            } else {
2493                                let mut store = AnimatableVarSet::default();
2494                                store.insert_rule(rule_id, opacity_val);
2495                                self.custom_opacity_props.insert(variable_name_hash, store);
2496                            }
2497
2498                            // Plain number like 1.2 -> LineHeight::Number
2499                            let line_height = LineHeight::Number(*value);
2500                            if let Some(store) =
2501                                self.custom_line_height_props.get_mut(&variable_name_hash)
2502                            {
2503                                store.insert_rule(rule_id, line_height);
2504                            } else {
2505                                let mut store = AnimatableVarSet::default();
2506                                store.insert_rule(rule_id, line_height);
2507                                self.custom_line_height_props.insert(variable_name_hash, store);
2508                            }
2509                        }
2510                        TokenOrValue::Token(CssToken::Ident(ident))
2511                            if ident.as_ref().eq_ignore_ascii_case("normal") =>
2512                        {
2513                            let line_height = LineHeight::Normal;
2514                            if let Some(store) =
2515                                self.custom_line_height_props.get_mut(&variable_name_hash)
2516                            {
2517                                store.insert_rule(rule_id, line_height);
2518                            } else {
2519                                let mut store = AnimatableVarSet::default();
2520                                store.insert_rule(rule_id, line_height);
2521                                self.custom_line_height_props.insert(variable_name_hash, store);
2522                            }
2523                        }
2524                        _ => {}
2525                    }
2526                }
2527            }
2528            _ => {}
2529        }
2530    }
2531
2532    // Helper function for generating AnimationState from a transition definition.
2533    fn add_transition<T: Default + Interpolator>(
2534        &self,
2535        transition: &Transition,
2536    ) -> AnimationState<T> {
2537        let timing_function = transition
2538            .timing_function
2539            .map(|easing| match easing {
2540                EasingFunction::Linear => TimingFunction::linear(),
2541                EasingFunction::Ease => TimingFunction::ease(),
2542                EasingFunction::EaseIn => TimingFunction::ease_in(),
2543                EasingFunction::EaseOut => TimingFunction::ease_out(),
2544                EasingFunction::EaseInOut => TimingFunction::ease_in_out(),
2545                EasingFunction::CubicBezier(x1, y1, x2, y2) => TimingFunction::new(x1, y1, x2, y2),
2546            })
2547            .unwrap_or_default();
2548
2549        AnimationState::new(Animation::null())
2550            .with_duration(transition.duration)
2551            .with_delay(transition.delay.unwrap_or_default())
2552            .with_keyframe(Keyframe { time: 0.0, value: Default::default(), timing_function })
2553            .with_keyframe(Keyframe { time: 1.0, value: Default::default(), timing_function })
2554    }
2555
2556    // Add style data for the given entity.
2557    pub(crate) fn add(&mut self, entity: Entity) {
2558        self.pseudo_classes.insert(entity, PseudoClassFlags::VALID);
2559        self.classes.insert(entity, HashSet::new());
2560        self.abilities.insert(entity, Abilities::default());
2561        self.system_flags = SystemFlags::RELAYOUT;
2562        self.restyle.insert(entity);
2563        self.reaccess.insert(entity);
2564        self.retransform.insert(entity);
2565        self.reclip.insert(entity);
2566    }
2567
2568    // Remove style data for the given entity.
2569    pub(crate) fn remove(&mut self, entity: Entity) {
2570        self.ids.remove(entity);
2571        self.classes.remove(entity);
2572        self.pseudo_classes.remove(entity);
2573        self.disabled.remove(entity);
2574        self.abilities.remove(entity);
2575
2576        self.name.remove(entity);
2577        self.role.remove(entity);
2578        // self.default_action_verb.remove(entity);
2579        self.live.remove(entity);
2580        self.labelled_by.remove(entity);
2581        self.described_by.remove(entity);
2582        self.controls.remove(entity);
2583        self.active_descendant.remove(entity);
2584        self.expanded.remove(entity);
2585        self.selected.remove(entity);
2586        self.hidden.remove(entity);
2587        self.orientation.remove(entity);
2588        self.text_value.remove(entity);
2589        self.numeric_value.remove(entity);
2590
2591        // Display
2592        self.display.remove(entity);
2593        // Visibility
2594        self.visibility.remove(entity);
2595        // Opacity
2596        self.opacity.remove(entity);
2597        // Z Order
2598        self.z_index.remove(entity);
2599        self.ignore_clipping.remove(entity);
2600        // Clipping
2601        self.clip_path.remove(entity);
2602
2603        self.overflowx.remove(entity);
2604        self.overflowy.remove(entity);
2605
2606        // Filters
2607        self.filter.remove(entity);
2608        self.backdrop_filter.remove(entity);
2609
2610        // Blend Mode
2611        self.blend_mode.remove(entity);
2612
2613        // Transform
2614        self.transform.remove(entity);
2615        self.transform_origin.remove(entity);
2616        self.translate.remove(entity);
2617        self.rotate.remove(entity);
2618        self.scale.remove(entity);
2619
2620        // Border
2621        self.border_width.remove(entity);
2622        self.border_color.remove(entity);
2623        self.border_style.remove(entity);
2624
2625        // Corner Shape
2626        self.corner_bottom_left_shape.remove(entity);
2627        self.corner_bottom_right_shape.remove(entity);
2628        self.corner_top_left_shape.remove(entity);
2629        self.corner_top_right_shape.remove(entity);
2630
2631        // Corner Radius
2632        self.corner_bottom_left_radius.remove(entity);
2633        self.corner_bottom_right_radius.remove(entity);
2634        self.corner_top_left_radius.remove(entity);
2635        self.corner_top_right_radius.remove(entity);
2636
2637        // Corner Smoothing
2638        self.corner_bottom_left_smoothing.remove(entity);
2639        self.corner_bottom_right_smoothing.remove(entity);
2640        self.corner_top_left_smoothing.remove(entity);
2641        self.corner_top_right_smoothing.remove(entity);
2642
2643        // Outline
2644        self.outline_width.remove(entity);
2645        self.outline_color.remove(entity);
2646        self.outline_offset.remove(entity);
2647
2648        // Background
2649        self.background_color.remove(entity);
2650        self.background_image.remove(entity);
2651        self.background_size.remove(entity);
2652
2653        // Box Shadow
2654        self.shadow.remove(entity);
2655
2656        // Text and Font
2657        self.text.remove(entity);
2658        self.text_wrap.remove(entity);
2659        self.text_overflow.remove(entity);
2660        self.line_height.remove(entity);
2661        self.line_clamp.remove(entity);
2662        self.text_align.remove(entity);
2663        self.font_family.remove(entity);
2664        self.font_color.remove(entity);
2665        self.font_size.remove(entity);
2666        self.font_weight.remove(entity);
2667        self.font_slant.remove(entity);
2668        self.font_width.remove(entity);
2669        self.font_variation_settings.remove(entity);
2670        self.caret_color.remove(entity);
2671        self.selection_color.remove(entity);
2672        self.text_decoration_line.remove(entity);
2673        self.text_decoration_style.remove(entity);
2674        self.text_decoration_color.remove(entity);
2675        self.text_stroke_width.remove(entity);
2676        self.text_stroke_style.remove(entity);
2677
2678        // Cursor
2679        self.cursor.remove(entity);
2680
2681        self.pointer_events.remove(entity);
2682
2683        // Layout Type
2684        self.layout_type.remove(entity);
2685
2686        // Position Type
2687        self.position_type.remove(entity);
2688
2689        self.alignment.remove(entity);
2690        self.direction.remove(entity);
2691        self.wrap.remove(entity);
2692
2693        // Grid
2694        self.grid_columns.remove(entity);
2695        self.grid_rows.remove(entity);
2696        self.column_start.remove(entity);
2697        self.column_span.remove(entity);
2698        self.row_start.remove(entity);
2699        self.row_span.remove(entity);
2700
2701        // Space
2702        self.left.remove(entity);
2703        self.right.remove(entity);
2704        self.top.remove(entity);
2705        self.bottom.remove(entity);
2706
2707        // Padding
2708        self.padding_left.remove(entity);
2709        self.padding_right.remove(entity);
2710        self.padding_top.remove(entity);
2711        self.padding_bottom.remove(entity);
2712        self.vertical_gap.remove(entity);
2713        self.horizontal_gap.remove(entity);
2714
2715        // Scrolling
2716        self.vertical_scroll.remove(entity);
2717        self.horizontal_scroll.remove(entity);
2718
2719        // Size
2720        self.width.remove(entity);
2721        self.height.remove(entity);
2722
2723        // Size Constraints
2724        self.min_width.remove(entity);
2725        self.max_width.remove(entity);
2726        self.min_height.remove(entity);
2727        self.max_height.remove(entity);
2728
2729        self.min_horizontal_gap.remove(entity);
2730        self.max_horizontal_gap.remove(entity);
2731        self.min_vertical_gap.remove(entity);
2732        self.max_vertical_gap.remove(entity);
2733
2734        self.text_range.remove(entity);
2735        self.text_span.remove(entity);
2736
2737        self.fill.remove(entity);
2738
2739        // Remove per-entity data from custom property stores
2740        for store in self.custom_color_props.values_mut() {
2741            store.remove(entity);
2742        }
2743        for store in self.custom_length_props.values_mut() {
2744            store.remove(entity);
2745        }
2746        for store in self.custom_font_size_props.values_mut() {
2747            store.remove(entity);
2748        }
2749        for store in self.custom_line_height_props.values_mut() {
2750            store.remove(entity);
2751        }
2752        for store in self.custom_units_props.values_mut() {
2753            store.remove(entity);
2754        }
2755        for store in self.custom_opacity_props.values_mut() {
2756            store.remove(entity);
2757        }
2758    }
2759
2760    pub(crate) fn needs_restyle(&mut self, entity: Entity) {
2761        if entity == Entity::null() || self.restyle.contains(&entity) {
2762            return;
2763        }
2764        self.restyle.insert(entity);
2765    }
2766
2767    pub(crate) fn needs_relayout(&mut self) {
2768        self.system_flags.set(SystemFlags::RELAYOUT, true);
2769    }
2770
2771    pub(crate) fn needs_access_update(&mut self, entity: Entity) {
2772        self.reaccess.insert(entity);
2773    }
2774
2775    pub(crate) fn needs_text_update(&mut self, entity: Entity) {
2776        self.text_construction.insert(entity);
2777        self.text_layout.insert(entity);
2778    }
2779
2780    pub(crate) fn needs_text_layout(&mut self, entity: Entity) {
2781        self.text_layout.insert(entity);
2782    }
2783
2784    pub(crate) fn needs_retransform(&mut self, entity: Entity) {
2785        self.retransform.insert(entity);
2786    }
2787
2788    pub(crate) fn needs_reclip(&mut self, entity: Entity) {
2789        self.reclip.insert(entity);
2790    }
2791
2792    // pub fn should_redraw<F: FnOnce()>(&mut self, f: F) {
2793    //     if !self.redraw_list.is_empty() {
2794    //         f();
2795    //     }
2796    // }
2797
2798    // Remove all shared style data.
2799    pub(crate) fn clear_style_rules(&mut self) {
2800        self.disabled.clear_rules();
2801        // Display
2802        self.display.clear_rules();
2803        // Visibility
2804        self.visibility.clear_rules();
2805        // Opacity
2806        self.opacity.clear_rules();
2807        // Z Order
2808        self.z_index.clear_rules();
2809
2810        // Clipping
2811        self.clip_path.clear_rules();
2812
2813        // Filters
2814        self.filter.clear_rules();
2815        self.backdrop_filter.clear_rules();
2816
2817        // Blend Mode
2818        self.blend_mode.clear_rules();
2819
2820        // Transform
2821        self.transform.clear_rules();
2822        self.transform_origin.clear_rules();
2823        self.translate.clear_rules();
2824        self.rotate.clear_rules();
2825        self.scale.clear_rules();
2826
2827        self.overflowx.clear_rules();
2828        self.overflowy.clear_rules();
2829
2830        // Border
2831        self.border_width.clear_rules();
2832        self.border_color.clear_rules();
2833        self.border_style.clear_rules();
2834
2835        // Corner Shape
2836        self.corner_bottom_left_shape.clear_rules();
2837        self.corner_bottom_right_shape.clear_rules();
2838        self.corner_top_left_shape.clear_rules();
2839        self.corner_top_right_shape.clear_rules();
2840
2841        // Corner Radius
2842        self.corner_bottom_left_radius.clear_rules();
2843        self.corner_bottom_right_radius.clear_rules();
2844        self.corner_top_left_radius.clear_rules();
2845        self.corner_top_right_radius.clear_rules();
2846
2847        // Corner Smoothing
2848        self.corner_bottom_left_smoothing.clear_rules();
2849        self.corner_bottom_right_smoothing.clear_rules();
2850        self.corner_top_left_smoothing.clear_rules();
2851        self.corner_top_right_smoothing.clear_rules();
2852
2853        // Outline
2854        self.outline_width.clear_rules();
2855        self.outline_color.clear_rules();
2856        self.outline_offset.clear_rules();
2857
2858        // Background
2859        self.background_color.clear_rules();
2860        self.background_image.clear_rules();
2861        self.background_size.clear_rules();
2862
2863        self.shadow.clear_rules();
2864
2865        self.layout_type.clear_rules();
2866        self.position_type.clear_rules();
2867        self.alignment.clear_rules();
2868        self.direction.clear_rules();
2869        self.wrap.clear_rules();
2870
2871        // Grid
2872        self.grid_columns.clear_rules();
2873        self.grid_rows.clear_rules();
2874        self.column_start.clear_rules();
2875        self.column_span.clear_rules();
2876
2877        // Space
2878        self.left.clear_rules();
2879        self.right.clear_rules();
2880        self.top.clear_rules();
2881        self.bottom.clear_rules();
2882
2883        // Size
2884        self.width.clear_rules();
2885        self.height.clear_rules();
2886
2887        // Size Constraints
2888        self.min_width.clear_rules();
2889        self.max_width.clear_rules();
2890        self.min_height.clear_rules();
2891        self.max_height.clear_rules();
2892
2893        self.min_horizontal_gap.clear_rules();
2894        self.max_horizontal_gap.clear_rules();
2895        self.min_vertical_gap.clear_rules();
2896        self.max_vertical_gap.clear_rules();
2897
2898        // Padding
2899        self.padding_left.clear_rules();
2900        self.padding_right.clear_rules();
2901        self.padding_top.clear_rules();
2902        self.padding_bottom.clear_rules();
2903        self.horizontal_gap.clear_rules();
2904        self.vertical_gap.clear_rules();
2905
2906        // Scrolling
2907        self.horizontal_scroll.clear_rules();
2908        self.vertical_scroll.clear_rules();
2909
2910        // Text and Font
2911        self.text_wrap.clear_rules();
2912        self.text_overflow.clear_rules();
2913        self.line_height.clear_rules();
2914        self.line_clamp.clear_rules();
2915        self.text_align.clear_rules();
2916        self.font_family.clear_rules();
2917        self.font_weight.clear_rules();
2918        self.font_slant.clear_rules();
2919        self.font_color.clear_rules();
2920        self.font_size.clear_rules();
2921        self.font_variation_settings.clear_rules();
2922        self.selection_color.clear_rules();
2923        self.caret_color.clear_rules();
2924        self.text_decoration_line.clear_rules();
2925        self.text_decoration_style.clear_rules();
2926        self.text_decoration_color.clear_rules();
2927        self.text_stroke_width.clear_rules();
2928        self.text_stroke_style.clear_rules();
2929
2930        self.cursor.clear_rules();
2931
2932        self.pointer_events.clear_rules();
2933
2934        self.name.clear_rules();
2935
2936        self.fill.clear_rules();
2937
2938        // Clear all custom property rule data on stylesheet reload
2939        for store in self.custom_color_props.values_mut() {
2940            store.clear_rules();
2941        }
2942        self.custom_color_props
2943            .retain(|_, store| !store.shared_data.is_empty() || !store.inline_data.is_empty());
2944        for store in self.custom_length_props.values_mut() {
2945            store.clear_rules();
2946        }
2947        self.custom_length_props
2948            .retain(|_, store| !store.shared_data.is_empty() || !store.inline_data.is_empty());
2949        for store in self.custom_font_size_props.values_mut() {
2950            store.clear_rules();
2951        }
2952        self.custom_font_size_props
2953            .retain(|_, store| !store.shared_data.is_empty() || !store.inline_data.is_empty());
2954        for store in self.custom_line_height_props.values_mut() {
2955            store.clear_rules();
2956        }
2957        self.custom_line_height_props
2958            .retain(|_, store| !store.shared_data.is_empty() || !store.inline_data.is_empty());
2959        for store in self.custom_units_props.values_mut() {
2960            store.clear_rules();
2961        }
2962        self.custom_units_props
2963            .retain(|_, store| !store.shared_data.is_empty() || !store.inline_data.is_empty());
2964        for store in self.custom_opacity_props.values_mut() {
2965            store.clear_rules();
2966        }
2967        self.custom_opacity_props
2968            .retain(|_, store| !store.shared_data.is_empty() || !store.inline_data.is_empty());
2969    }
2970}