Skip to main content

floem/style/
mod.rs

1//! # Style
2//! Traits and functions that allow for styling `Views`.
3//!
4//! # The Floem Style System
5//!
6//! ## The [Style] struct
7//!
8//! The style system is centered around a [Style] struct.
9//! `Style` internally is just a hashmap (although one from the im crate so it is cheap to clone).
10//! It maps from a [StyleKey] to `Rc<dyn Any>`.
11//!
12//! ## The [StyleKey]
13//!
14//! [StyleKey] holds a static reference (that is used as the hash value) to a [StyleKeyInfo] enum which enumerates the different kinds of values that can be in the map.
15//! Which value is in the `StyleKeyInfo` enum is used to know how to downcast the `Rc<dyn Any`.
16//!
17//! The key types from the [StyleKeyInfo] are: (these are all of the different things that can be added to a [Style]).
18//! - Transition,
19//! - Prop(StylePropInfo),
20//! - Selector(StyleSelectors),
21//! - Class(StyleClassInfo),
22//! - ContextMappings,
23//!
24//! Transitions and context mappings don't hold any extra information, they are just used to know how to downcast the `Rc<dyn Any>`.
25//!
26//! [StyleSelectors] is a bit mask of which selectors are active.
27//!
28//! [StyleClassInfo] holds a function pointer that returns the name of the class as a String.
29//! The function pointer is basically used as a vtable for the class.
30//! If classes needed more methods other than `name`, those methods would be added to `StyleClassInfo`.
31//!
32//! [StylePropInfo] is another vtable, similar to `StyleClassInfo` and holds function pointers for getting the name of a prop, the props interpolation function from the [StylePropValue] trait, the associated transition key for the prop, and others.
33//!
34//! Props store props.
35//! Transitions store transition values.
36//! Classes, context mappings, and selectors store nested [Style] maps.
37//!
38//! ## Applying `Style`s to `View`s
39//!
40//! A style can be applied to a view in two different ways.
41//! A single `Style` can be added to the [view_style](crate::view::View::view_style) method of the view trait or multiple `Style`s can be added by calling [style](crate::views::Decorators::style) on an `IntoView` from the [Decorators](crate::views::Decorators) trait.
42//!
43//! Calls to `style` from the decorators trait have a higher precedence than the `view_style` method, meaning calls to `style` will override any matching `StyleKeyInfo` that came from the `view_style` method.
44//!
45//! If you make repeated calls to `style` from the decorators trait, each will be added separately to the `ViewState` that is managed by Floem and associated with the `ViewId` of the view that `style` was called on.
46//! The `ViewState` stores a `Stack` of styles and later calls to `style` (and thus larger indicies in the style stack) will take precedence over earlier calls.
47//!
48//! `style` from the deocrators trait is reactive and the function that returns the style map with be re-run in response to any reactive updates that it depends on.
49//! If it gets a reactive update, it will have tracked which index into the style stack it had when it was first called and will overrite that index and only that index so that other calls to `style` are not affected.
50//!
51//! ## Style Resolution
52//!
53//! A final `computed_style` is resolved in the `style_pass` of the `View` trait.
54//!
55//! ### Context
56//!
57//! It first received a `Style` map that is used as context.
58//! The context is passed down the view tree and carries the inherited properties that were applied to any parent.
59//! Inherited properties include all classes and any prop that has been marked as `inherited`.
60//!
61//! ### View Style
62//!
63//! The `style` first gets the `Style` (if any) from the `view_style` method.
64//!
65//! ### Style
66//!
67//! Then it gets the style from any calls to `style` from the decorators trait.
68//! It starts with the first index in the style `Stack` and applies each successive `Style` over the combination of any previous ones.
69//!
70//! Then the style from the `Decorators` / `ViewState` is applied over (overriding any matching props) the style from `view_style`.
71//!
72//!
73//! ### Nested map resolution
74//!
75//! Then any classes that have been applied to the view, and the active selector set are used to resolve nested maps.
76//!
77//! Nested maps such as classes and selectors are recursively applied, breadth first. So, deeper / more nested style maps take precendence.
78//!
79//! This style map is the combined style of the `View`.
80//!
81//! ### Updated context
82//!
83//! Finally, the context style is updated using the combined style, applying any style key that is `inherited` to the context so that the children will have acces to them.
84//!
85//! ## Prop Extraction
86//!
87//! The final computed style of a view will be passed to the `style_pass` method from the `View` trait.
88//!
89//! Views will store fields that are struct that are prop extractors.
90//! These structs are created using the `prop_extractor!` macro.
91//!
92//! These structs can then be used from in the `style_pass` to extract props using the `read` (or `read_exact`) methods that are created by the `prop_extractor` macro.
93//!
94//! The read methods will take in the combined style for that `View` and will automatically extract any matching prop values and transitions for those props.
95//!
96//! ### Transition interpolation
97//!
98//! If there is a transition for a prop, the extractor will keep track of the current time and transition state and will set the final extracted value to a properly interpolated value using the state and current time.
99//!
100//!
101//! ## Custom Style Props, Classes, and Extractors.
102//!
103//!
104//! You can create custom style props with the [prop!] macro, classes with the [style_class!] macro, and extractors with the [prop_extractor!] macro.
105//!
106//!
107//! ### Custom Props
108//!
109//! You can create custom props.
110//!
111//! Doing this allows you to store arbitrary values in the style system.
112//!
113//! You can use these to style the view, change it's behavior, update it's state, or anything else.
114//!
115//! By implementing the [StylePropValue] trait for your prop (which you must do) you can
116//!
117//! - optionally set how the prop should be interpolated (allowing you to customize what interpolating means in the context of your prop)
118//!
119//! - optionally provide a `debug_view` for your prop, which debug view will be used in the Floem inspector. This means that you can customize a complex debug experience for your prop with very little effort (and it really can be any arbitrary view. no restrictions.)
120//!
121//! - optionally add a custom implementation of how a prop should be combined with another prop. This is different from interpolation and is useful when you want to specify how properties should override each other. The default implementation just replaces the old value with a new value, but if you have a prop with multiple optional fields, you might want to only replace the fields that have a `Some` value.
122//!
123//! ### Custom Classes
124//!
125//! If you create a custom class, you can apply that class to any view, and when the final style for that view is being resolved, if the style has that class as a nested map, it will be applied, overriding any prviously set values.
126//!
127//! ### Custom Extractors
128//!
129//! You can create custom extractors and embed them in your custom views so that you can get out any built in prop, or any of your custom props from the final combined style that is applied to your `View`.
130
131use floem_renderer::text::{LineHeightValue, Weight};
132use imbl::hashmap::Entry;
133use peniko::color::palette;
134use peniko::kurbo::{self, Affine, RoundedRect, Vec2};
135use peniko::{Brush, Color};
136use smallvec::SmallVec;
137use std::any::Any;
138use std::collections::HashMap;
139use std::fmt::{self, Debug};
140use std::rc::Rc;
141use taffy::GridTemplateComponent;
142
143pub use taffy::style::{
144    AlignContent, AlignItems, BoxSizing, Dimension, Display, FlexDirection, FlexWrap,
145    JustifyContent, JustifyItems, Position,
146};
147use taffy::{
148    geometry::{MinMax, Size},
149    prelude::{GridPlacement, Line, Rect},
150    style::{
151        LengthPercentage, MaxTrackSizingFunction, MinTrackSizingFunction, Overflow,
152        Style as TaffyStyle,
153    },
154};
155
156use crate::layout::responsive::{ScreenSize, ScreenSizeBp};
157
158// Import macros from crate root (they are #[macro_export] in props.rs)
159use crate::{prop, prop_extractor};
160
161mod cache;
162mod components;
163mod custom;
164mod cx;
165mod props;
166pub mod recalc;
167mod selectors;
168#[cfg(test)]
169mod tests;
170pub mod theme;
171mod transition;
172pub mod unit;
173mod values;
174
175pub use components::{
176    Border, BorderColor, BorderRadius, BoxShadow, CursorStyle, Margin, Padding, PointerEvents,
177    TextOverflow,
178};
179pub use custom::{CustomStylable, CustomStyle};
180pub use cx::{InheritedInteractionCx, InteractionState, StyleCx};
181pub use props::{
182    ExtractorField, StyleClass, StyleClassInfo, StyleClassRef, StyleKey, StyleKeyInfo, StyleProp,
183    StylePropInfo, StylePropReader, StylePropRef,
184};
185pub use selectors::{StyleSelector, StyleSelectors};
186pub use theme::{DesignSystem, StyleThemeExt};
187pub use transition::{DirectTransition, Transition, TransitionState};
188pub use unit::{AnchorAbout, Angle, Auto, DurationUnitExt, Pct, Px, PxPct, PxPctAuto, UnitExt};
189pub use values::{CombineResult, StrokeWrap, StyleMapValue, StylePropValue, StyleValue};
190
191pub use cache::{StyleCache, StyleCacheKey};
192pub use recalc::{InheritedChanges, InheritedGroups, Propagate, RecalcFlags, StyleRecalcChange};
193
194pub(crate) use props::{CONTEXT_MAPPINGS_INFO, ImHashMap, style_key_selector};
195
196/// A closure that maps context values to style properties.
197type ContextMapFn = Rc<dyn Fn(Style, &Style) -> Style>;
198
199/// Simple storage for context mapping closures.
200/// Unlike the old ContextMappings, this only stores the closures themselves -
201/// selector and inherited prop discovery happens via immediate evaluation.
202///
203/// Uses `Rc<Vec>` for O(1) clone - avoids copying the entire Vec when reading.
204#[derive(Clone)]
205pub(crate) struct ContextMappings(pub Rc<Vec<ContextMapFn>>);
206
207style_key_selector!(selector_xs, StyleSelectors::new().responsive());
208style_key_selector!(selector_sm, StyleSelectors::new().responsive());
209style_key_selector!(selector_md, StyleSelectors::new().responsive());
210style_key_selector!(selector_lg, StyleSelectors::new().responsive());
211style_key_selector!(selector_xl, StyleSelectors::new().responsive());
212style_key_selector!(selector_xxl, StyleSelectors::new().responsive());
213
214pub(crate) fn screen_size_bp_to_key(breakpoint: ScreenSizeBp) -> StyleKey {
215    match breakpoint {
216        ScreenSizeBp::Xs => selector_xs(),
217        ScreenSizeBp::Sm => selector_sm(),
218        ScreenSizeBp::Md => selector_md(),
219        ScreenSizeBp::Lg => selector_lg(),
220        ScreenSizeBp::Xl => selector_xl(),
221        ScreenSizeBp::Xxl => selector_xxl(),
222    }
223}
224
225/// the bool in the return is a classes_applied flag. if a new class has been applied, we need to do a request_style_recursive
226pub fn resolve_nested_maps(
227    style: Style,
228    interact_state: &InteractionState,
229    screen_size_bp: ScreenSizeBp,
230    classes: &[StyleClassRef],
231    inherited_context: &Style,
232    class_context: &Style,
233) -> (Style, bool) {
234    let mut classes_applied = false;
235
236    // Phase_ 1: Resolve class styles (with selectors, collecting context mappings)
237    let (class_style, mut class_context_mappings) = resolve_classes_collecting_mappings(
238        classes,
239        interact_state,
240        screen_size_bp,
241        class_context,
242        &mut classes_applied,
243    );
244
245    // Phase 2: Resolve view's inline style (with selectors, collecting context mappings)
246    let (view_style, mut view_context_mappings) =
247        resolve_style_collecting_mappings(style, interact_state, screen_size_bp);
248
249    // Phase 3: Apply class context mappings (with recursive resolution)
250    // Use class_style as the base, context includes class_style + view_style
251    let mut context_result = class_style.clone();
252    let mut i = 0;
253    while i < class_context_mappings.len() {
254        let mapping = class_context_mappings[i].clone();
255        let combined_context = inherited_context
256            .clone()
257            .apply(view_style.clone())
258            .apply(context_result.clone());
259        let mapped = mapping(context_result.clone(), &combined_context);
260        let (resolved, new_mappings) =
261            resolve_selectors_collecting_mappings(mapped, interact_state, screen_size_bp);
262        context_result.apply_mut_no_mappings(resolved);
263        class_context_mappings.splice(i + 1..i + 1, new_mappings);
264        i += 1;
265    }
266
267    // Apply view style over the context result (view style wins)
268    let mut result = context_result.apply(view_style);
269
270    // Phase 4: Apply view context mappings over result (with recursive resolution)
271    let mut i = 0;
272    while i < view_context_mappings.len() {
273        let mapping = view_context_mappings[i].clone();
274        let combined_context = inherited_context.clone().apply(result.clone());
275        let mapped = mapping(result.clone(), &combined_context);
276        let (resolved, new_mappings) =
277            resolve_selectors_collecting_mappings(mapped, interact_state, screen_size_bp);
278        result.apply_mut_no_mappings(resolved);
279        view_context_mappings.splice(i + 1..i + 1, new_mappings);
280        i += 1;
281    }
282
283    (result, classes_applied)
284}
285
286fn resolve_classes_collecting_mappings(
287    classes: &[StyleClassRef],
288    interact_state: &InteractionState,
289    screen_size_bp: ScreenSizeBp,
290    class_context: &Style,
291    classes_applied: &mut bool,
292) -> (Style, Vec<ContextMapFn>) {
293    let mut result = Style::new();
294    let mut mappings = Vec::new();
295
296    for class in classes {
297        if let Some(map) = class_context.get_nested_map(class.key) {
298            *classes_applied = true;
299            let (resolved, class_mappings) =
300                resolve_style_collecting_mappings(map.clone(), interact_state, screen_size_bp);
301            result.apply_mut(resolved);
302            mappings.extend(class_mappings);
303        }
304    }
305
306    (result, mappings)
307}
308
309fn resolve_style_collecting_mappings(
310    style: Style,
311    interact_state: &InteractionState,
312    screen_size_bp: ScreenSizeBp,
313) -> (Style, Vec<ContextMapFn>) {
314    let mut mappings = Vec::new();
315
316    // Extract context mappings from style before resolving
317    if let Some(style_mappings) = extract_context_mappings(&style) {
318        mappings.extend(style_mappings);
319    }
320
321    // Resolve all selectors (and collect any new mappings found)
322    let (resolved, selector_mappings) =
323        resolve_selectors_collecting_mappings(style, interact_state, screen_size_bp);
324    mappings.extend(selector_mappings);
325
326    (resolved, mappings)
327}
328
329fn resolve_selectors_collecting_mappings(
330    mut style: Style,
331    interact_state: &InteractionState,
332    screen_size_bp: ScreenSizeBp,
333) -> (Style, Vec<ContextMapFn>) {
334    const MAX_DEPTH: u32 = 20;
335    let mut depth = 0;
336    let mut all_mappings = Vec::new();
337
338    loop {
339        if depth >= MAX_DEPTH {
340            break;
341        }
342        depth += 1;
343
344        let mut changed = false;
345
346        // Helper to apply a nested map and collect any context mappings from it
347        let mut apply_nested = |style: &mut Style, key: StyleKey| -> bool {
348            if let Some(map) = style.get_nested_map(key) {
349                // Extract mappings before applying
350                if let Some(mappings) = extract_context_mappings(&map) {
351                    all_mappings.extend(mappings);
352                }
353                style.apply_mut_no_mappings(map);
354                style.remove_nested_map(key);
355                true
356            } else {
357                false
358            }
359        };
360
361        // Apply screen size breakpoints
362        if apply_nested(&mut style, screen_size_bp_to_key(screen_size_bp)) {
363            changed = true;
364        }
365
366        // DarkMode
367        if interact_state.is_dark_mode && apply_nested(&mut style, StyleSelector::DarkMode.to_key())
368        {
369            changed = true;
370        }
371
372        // Disabled state
373        if interact_state.is_disabled || style.get(Disabled) {
374            if apply_nested(&mut style, StyleSelector::Disabled.to_key()) {
375                changed = true;
376            }
377        } else {
378            // Selected
379            if (interact_state.is_selected || style.get(Selected))
380                && apply_nested(&mut style, StyleSelector::Selected.to_key())
381            {
382                changed = true;
383            }
384
385            // Hover
386            if interact_state.is_hovered && apply_nested(&mut style, StyleSelector::Hover.to_key())
387            {
388                changed = true;
389            }
390
391            // File Hover
392            if interact_state.is_file_hover
393                && apply_nested(&mut style, StyleSelector::FileHover.to_key())
394            {
395                changed = true;
396            }
397
398            // Focus states
399            if interact_state.is_focused {
400                if apply_nested(&mut style, StyleSelector::Focus.to_key()) {
401                    changed = true;
402                }
403
404                if interact_state.using_keyboard_navigation {
405                    if apply_nested(&mut style, StyleSelector::FocusVisible.to_key()) {
406                        changed = true;
407                    }
408
409                    if interact_state.is_clicking
410                        && apply_nested(&mut style, StyleSelector::Active.to_key())
411                    {
412                        changed = true;
413                    }
414                }
415            }
416
417            // Active (mouse)
418            if interact_state.is_clicking
419                && !interact_state.using_keyboard_navigation
420                && apply_nested(&mut style, StyleSelector::Active.to_key())
421            {
422                changed = true;
423            }
424        }
425
426        if !changed {
427            break;
428        }
429    }
430
431    (style, all_mappings)
432}
433
434fn extract_context_mappings(style: &Style) -> Option<Vec<ContextMapFn>> {
435    let key = StyleKey {
436        info: &CONTEXT_MAPPINGS_INFO,
437    };
438    style.map.get(&key).map(|rc| {
439        let mappings = rc.downcast_ref::<ContextMappings>().unwrap();
440        mappings.0.iter().cloned().collect()
441    })
442}
443
444#[derive(Default, Clone)]
445pub struct Style {
446    pub(crate) map: ImHashMap<StyleKey, Rc<dyn Any>>,
447    /// Cached flag indicating whether this style contains any class maps.
448    /// This enables O(1) early-exit in `apply_only_class_maps` for the common case
449    /// where a view's style has no class definitions.
450    has_class_maps: bool,
451    /// Cached flag indicating whether this style contains any inherited properties.
452    /// This enables O(1) early-exit in `apply_only_inherited` for the common case
453    /// where a view's style has no inherited properties.
454    has_inherited: bool,
455}
456
457impl Style {
458    pub fn new() -> Self {
459        Self::default()
460    }
461
462    /// Apply only inherited properties from `from` style to `to` style.
463    /// This is used during style propagation to pass inherited values to children.
464    ///
465    /// Only properties marked as `inherited: true` in their `StylePropInfo` are applied.
466    /// This is more efficient than `apply_mut` when we only need to propagate
467    /// inherited properties like font-size, color, etc.
468    pub fn apply_only_inherited(to: &mut Rc<Style>, from: &Style) {
469        if from.any_inherited() {
470            let mut new_style = (**to).clone();
471            // Only apply properties marked as inherited, not all properties
472            let inherited = from.map.iter().filter(|(p, _)| p.inherited());
473            new_style.apply_iter(inherited);
474            *to = Rc::new(new_style);
475        }
476    }
477
478    /// Apply inherited properties and class nested maps from `from` style to `to` style.
479    ///
480    /// This is used during style propagation to pass both inherited values and
481    /// class definitions to children. Class nested maps (like `.class(ListItemClass, ...)`)
482    /// need to flow to descendants so they can apply the styling when they have matching classes.
483    pub fn apply_inherited_and_class_maps(to: &mut Rc<Style>, from: &Style) {
484        let has_inherited = from.any_inherited();
485        // O(1) check using cached flag
486        let has_class_maps = from.has_class_maps;
487
488        if has_inherited || has_class_maps {
489            let mut new_style = (**to).clone();
490
491            // Apply inherited properties
492            if has_inherited {
493                let inherited = from.map.iter().filter(|(p, _)| p.inherited());
494                new_style.apply_iter(inherited);
495            }
496
497            // Apply class nested maps so they flow to descendants
498            if has_class_maps {
499                let class_maps = from
500                    .map
501                    .iter()
502                    .filter(|(k, _)| matches!(k.info, StyleKeyInfo::Class(..)));
503                new_style.apply_iter(class_maps);
504            }
505
506            *to = Rc::new(new_style);
507        }
508    }
509
510    /// Apply only class nested maps from `from` style to `to` style.
511    /// This is used during style propagation to pass class definitions to children.
512    ///
513    /// Only class nested maps (`.class(SomeClass, ...)`) are applied, not inherited props.
514    pub fn apply_only_class_maps(to: &mut Rc<Style>, from: &Style) {
515        // O(1) early exit for the common case where the style has no class maps
516        if !from.has_class_maps {
517            return;
518        }
519        let mut new_style = (**to).clone();
520        let class_maps = from
521            .map
522            .iter()
523            .filter(|(k, _)| matches!(k.info, StyleKeyInfo::Class(..)));
524        new_style.apply_iter(class_maps);
525        *to = Rc::new(new_style);
526    }
527
528    pub(crate) fn get_transition<P: StyleProp>(&self) -> Option<Transition> {
529        self.map
530            .get(&P::prop_ref().info().transition_key)
531            .map(|v| v.downcast_ref::<Transition>().unwrap().clone())
532    }
533
534    pub(crate) fn get_prop_or_default<P: StyleProp>(&self) -> P::Type {
535        self.get_prop::<P>().unwrap_or_else(|| P::default_value())
536    }
537
538    pub(crate) fn get_prop<P: StyleProp>(&self) -> Option<P::Type> {
539        self.map.get(&P::key()).and_then(|v| {
540            v.downcast_ref::<StyleMapValue<P::Type>>()
541                .unwrap()
542                .as_ref()
543                .cloned()
544        })
545    }
546
547    pub(crate) fn get_prop_style_value<P: StyleProp>(&self) -> StyleValue<P::Type> {
548        self.map
549            .get(&P::key())
550            .map(
551                |v| match v.downcast_ref::<StyleMapValue<P::Type>>().unwrap() {
552                    StyleMapValue::Val(v) => StyleValue::Val(v.clone()),
553                    StyleMapValue::Animated(v) => StyleValue::Animated(v.clone()),
554                    StyleMapValue::Unset => StyleValue::Unset,
555                },
556            )
557            .unwrap_or(StyleValue::Base)
558    }
559
560    pub(crate) fn style_props(&self) -> impl Iterator<Item = StylePropRef> + '_ {
561        self.map.keys().filter_map(|p| match p.info {
562            StyleKeyInfo::Prop(..) => Some(StylePropRef { key: *p }),
563            _ => None,
564        })
565    }
566
567    pub(crate) fn selectors(&self) -> StyleSelectors {
568        let mut result = StyleSelectors::new();
569
570        // Check for direct selectors
571        for (k, v) in &self.map {
572            if let StyleKeyInfo::Selector(selector) = k.info {
573                result = result
574                    .union(*selector)
575                    .union(v.downcast_ref::<Style>().unwrap().selectors());
576            }
577        }
578        result
579    }
580
581    /// Applies class styling from the context (inherited from ancestors) to this style.
582    ///
583    /// The view's own explicit styles take precedence over context class styles.
584    /// This is achieved by applying context class styles first, then applying
585    /// the view's own styles on top.
586    ///
587    /// Context mappings (from `with_context`/`with_theme`) from class styles are
588    /// merged with the view's own context mappings. Class mappings run first (as
589    /// defaults), then view's own mappings run (allowing overrides). This ensures
590    /// that theme class styles can use `with_context` for context-aware styling
591    /// (like toggle button height based on font size) while still allowing views
592    /// to override via their own `with_context` closures.
593    ///
594    /// The returned boolean is true if a nested map was applied.
595    pub fn apply_classes_from_context(
596        self,
597        classes: &[StyleClassRef],
598        class_context: &std::rc::Rc<Style>,
599    ) -> (Style, bool) {
600        // Fast path: if no classes or no class maps in context, return unchanged
601        if classes.is_empty() || !class_context.has_class_maps {
602            return (self, false);
603        }
604
605        // Check if any of the classes actually have definitions in the context
606        let has_matching_classes = classes
607            .iter()
608            .any(|class| class_context.get_nested_map(class.key).is_some());
609        if !has_matching_classes {
610            return (self, false);
611        }
612
613        let mut changed = false;
614
615        let context_mappings_key = StyleKey {
616            info: &CONTEXT_MAPPINGS_INFO,
617        };
618
619        // CSS-like specificity: inline styles (the view's own styles) have higher
620        // specificity than class styles from ancestors. We achieve this by:
621        // 1. Building up class styles as a base
622        // 2. Applying the view's own styles on top
623        //
624        // Context mappings (from `with_context`/`with_theme`) from class styles are
625        // merged with the view's own context mappings. Class mappings run first (as
626        // defaults), then view's own mappings run (allowing overrides).
627
628        // Save the view's own styles and context mappings
629        let view_style = self;
630        let view_ctx_mappings = view_style.map.get(&context_mappings_key).cloned();
631
632        // Start with an empty style and build up from class styles
633        let mut result = Style::new();
634        let mut all_class_mappings: Vec<ContextMapFn> = Vec::new();
635
636        for class in classes {
637            if let Some(map) = class_context.get_nested_map(class.key) {
638                let mut class_style = map.clone();
639                // Extract class style's context mappings before applying other props
640                if let Some(class_mappings_rc) = class_style.map.remove(&context_mappings_key) {
641                    let class_mappings =
642                        class_mappings_rc.downcast_ref::<ContextMappings>().unwrap();
643                    all_class_mappings.extend(class_mappings.0.iter().cloned());
644                }
645                // Apply class style to result (later classes override earlier ones)
646                result.apply_mut(class_style);
647                changed = true;
648            }
649        }
650
651        // Now apply view's own styles ON TOP of class styles (inline styles win)
652        // We move the view_style into result to avoid cloning when possible
653        result.apply_mut(view_style.clone());
654
655        // Merge context mappings: class mappings FIRST (defaults), view's SECOND (overrides)
656        // Only do selector extraction if there are class context mappings that could override them
657        if !all_class_mappings.is_empty() {
658            // CSS-like specificity fix: The view's inline selector styles (like .selected())
659            // should override class styles, but class context mappings (like with_theme) run
660            // AFTER this and might override them. To fix this, we wrap the view's selector
661            // nested maps in a context mapping that runs LAST, after all class context mappings.
662            let view_selector_keys: Vec<_> = view_style
663                .map
664                .keys()
665                .filter(|k| matches!(k.info, StyleKeyInfo::Selector(..)))
666                .cloned()
667                .collect();
668
669            if !view_selector_keys.is_empty() {
670                let view_selectors: HashMap<StyleKey, Rc<dyn Any>> = view_selector_keys
671                    .iter()
672                    .filter_map(|k| view_style.map.get(k).map(|v| (*k, v.clone())))
673                    .collect();
674
675                // Add a context mapping that re-applies the view's selector styles LAST
676                let restore_selectors: ContextMapFn = Rc::new(move |mut s: Style, _ctx: &Style| {
677                    for (k, v) in view_selectors.iter() {
678                        s.apply_iter(std::iter::once((k, v)));
679                    }
680                    s
681                });
682                all_class_mappings.push(restore_selectors);
683            }
684
685            // Add view's own context mappings AFTER class mappings (so view's override)
686            if let Some(view_mappings_rc) = view_ctx_mappings {
687                let view_mappings = view_mappings_rc.downcast_ref::<ContextMappings>().unwrap();
688                all_class_mappings.extend(view_mappings.0.iter().cloned());
689            }
690            result.map.insert(
691                context_mappings_key,
692                Rc::new(ContextMappings(Rc::new(all_class_mappings))),
693            );
694        } else if let Some(view_mappings_rc) = view_ctx_mappings {
695            // No class mappings, just preserve view's own context mappings
696            result.map.insert(context_mappings_key, view_mappings_rc);
697        }
698
699        (result, changed)
700    }
701
702    pub fn apply_class<C: StyleClass>(mut self, _class: C) -> Style {
703        if let Some(map) = self.map.get(&C::key()) {
704            self.apply_mut(map.downcast_ref::<Style>().unwrap().clone());
705        }
706        self
707    }
708
709    pub fn apply_selectors(mut self, selectors: &[StyleSelector]) -> Style {
710        for selector in selectors {
711            if let Some(map) = self.get_nested_map(selector.to_key()) {
712                self.apply_mut(map.apply_selectors(selectors));
713            }
714        }
715        if self.get(Selected) {
716            if let Some(map) = self.get_nested_map(StyleSelector::Selected.to_key()) {
717                self.apply_mut(map.apply_selectors(&[StyleSelector::Selected]));
718            }
719        }
720        self
721    }
722
723    /// Apply a context-based style transformation.
724    ///
725    /// This evaluates the closure immediately with the context prop's default value
726    /// (for selector and inherited prop discovery), and also stores the closure to
727    /// be re-evaluated with actual ancestor context values during style resolution.
728    ///
729    /// Signal reactivity works because the outer style closure re-runs when signals change.
730    pub fn with_context<P: StyleProp>(self, f: impl Fn(Self, &P::Type) -> Self + 'static) -> Self {
731        // Evaluate immediately with default value for selector/inherited discovery.
732        // This ensures selectors defined inside with_context are visible.
733        let default_value = P::default_value();
734        let result = f(self.clone(), &default_value);
735
736        // Create a closure for context-aware re-evaluation during style resolution.
737        // Cache default_value inside closure to avoid repeated allocations.
738        let mapper: ContextMapFn = Rc::new(move |style: Style, context: &Style| {
739            // Try getting the property from style first, then from context if not found
740            let value = style.get_prop::<P>().or_else(|| {
741                let prop_key = P::key();
742                if let StyleKeyInfo::Prop(_) = prop_key.info {
743                    context.get_prop::<P>()
744                } else {
745                    None
746                }
747            });
748
749            if let Some(value) = value {
750                f(style, &value)
751            } else {
752                f(style, &P::default_value())
753            }
754        });
755
756        // Store the closure for later context resolution
757        let key = StyleKey {
758            info: &CONTEXT_MAPPINGS_INFO,
759        };
760
761        // Build new mappings vec - use Rc::make_mut for efficient copy-on-write
762        let mut mappings_vec = result
763            .map
764            .get(&key)
765            .and_then(|v| v.downcast_ref::<ContextMappings>())
766            .map(|cm| (*cm.0).clone())
767            .unwrap_or_default();
768        mappings_vec.push(mapper);
769
770        // Start with the immediate result (has selectors/inherited props)
771        // but add our closure storage
772        let mut final_result = result;
773        final_result
774            .map
775            .insert(key, Rc::new(ContextMappings(Rc::new(mappings_vec))));
776        final_result
777    }
778
779    /// Apply a context-based style transformation for optional props.
780    ///
781    /// Like `with_context`, this evaluates immediately with defaults for discovery,
782    /// and stores the closure for context-aware re-evaluation.
783    pub fn with_context_opt<P: StyleProp<Type = Option<T>>, T: 'static + Default>(
784        self,
785        f: impl Fn(Self, T) -> Self + 'static,
786    ) -> Self {
787        // Evaluate immediately with default T value for selector/inherited discovery
788        let result = f(self.clone(), T::default());
789
790        // Create a closure for context-aware re-evaluation
791        let mapper: ContextMapFn = Rc::new(move |style: Style, context: &Style| {
792            let value = style.get_prop::<P>().or_else(|| {
793                let prop_key = P::key();
794                if let StyleKeyInfo::Prop(_) = prop_key.info {
795                    context.get_prop::<P>()
796                } else {
797                    None
798                }
799            });
800
801            match value {
802                Some(Some(value)) => f(style, value),
803                _ => style,
804            }
805        });
806
807        // Store the closure
808        let key = StyleKey {
809            info: &CONTEXT_MAPPINGS_INFO,
810        };
811
812        // Build new mappings vec efficiently
813        let mut mappings_vec = result
814            .map
815            .get(&key)
816            .and_then(|v| v.downcast_ref::<ContextMappings>())
817            .map(|cm| (*cm.0).clone())
818            .unwrap_or_default();
819        mappings_vec.push(mapper);
820
821        let mut final_result = result;
822        final_result
823            .map
824            .insert(key, Rc::new(ContextMappings(Rc::new(mappings_vec))));
825        final_result
826    }
827
828    pub(crate) fn get_nested_map(&self, key: StyleKey) -> Option<Style> {
829        self.map
830            .get(&key)
831            .map(|map| map.downcast_ref::<Style>().unwrap().clone())
832    }
833
834    pub(crate) fn remove_nested_map(&mut self, key: StyleKey) -> Option<Style> {
835        self.map
836            .remove(&key)
837            .map(|map| map.downcast_ref::<Style>().unwrap().clone())
838    }
839
840    /// Check if this style has any inherited properties.
841    /// Used to determine if children should be re-styled when this view's style changes.
842    /// O(1) using cached flag.
843    pub(crate) fn any_inherited(&self) -> bool {
844        self.has_inherited
845    }
846
847    pub(crate) fn inherited(&self) -> Style {
848        let mut new = Style::new();
849        if self.any_inherited() {
850            let inherited = self.map.iter().filter(|(p, _)| p.inherited());
851
852            new.apply_iter(inherited);
853        }
854        new
855    }
856
857    fn set_selector(&mut self, selector: StyleSelector, map: Style) {
858        self.set_map_selector(selector.to_key(), map)
859    }
860
861    fn set_map_selector(&mut self, key: StyleKey, map: Style) {
862        match self.map.entry(key) {
863            Entry::Occupied(mut e) => {
864                let mut current = e.get_mut().downcast_ref::<Style>().unwrap().clone();
865                current.apply_mut(map);
866                *e.get_mut() = Rc::new(current);
867            }
868            Entry::Vacant(e) => {
869                e.insert(Rc::new(map));
870            }
871        }
872    }
873
874    fn set_breakpoint(&mut self, breakpoint: ScreenSizeBp, map: Style) {
875        self.set_map_selector(screen_size_bp_to_key(breakpoint), map)
876    }
877
878    fn set_class(&mut self, class: StyleClassRef, map: Style) {
879        self.has_class_maps = true;
880        self.set_map_selector(class.key, map)
881    }
882
883    pub fn builtin(&self) -> BuiltinStyle<'_> {
884        BuiltinStyle { style: self }
885    }
886
887    pub(crate) fn apply_iter<'a>(
888        &mut self,
889        iter: impl Iterator<Item = (&'a StyleKey, &'a Rc<dyn Any>)>,
890    ) {
891        for (k, v) in iter {
892            match k.info {
893                StyleKeyInfo::Class(..) | StyleKeyInfo::Selector(..) => {
894                    // Track class maps for O(1) early-exit in apply_only_class_maps
895                    if matches!(k.info, StyleKeyInfo::Class(..)) {
896                        self.has_class_maps = true;
897                    }
898                    match self.map.entry(*k) {
899                        Entry::Occupied(mut e) => {
900                            // We need to merge the new map with the existing map.
901
902                            let v = v.downcast_ref::<Style>().unwrap();
903                            match Rc::get_mut(e.get_mut()) {
904                                Some(current) => {
905                                    current
906                                        .downcast_mut::<Style>()
907                                        .unwrap()
908                                        .apply_mut(v.clone());
909                                }
910                                None => {
911                                    let mut current =
912                                        e.get_mut().downcast_ref::<Style>().unwrap().clone();
913                                    current.apply_mut(v.clone());
914                                    *e.get_mut() = Rc::new(current);
915                                }
916                            }
917                        }
918                        Entry::Vacant(e) => {
919                            e.insert(v.clone());
920                        }
921                    }
922                }
923                StyleKeyInfo::ContextMappings => match self.map.entry(*k) {
924                    Entry::Occupied(mut e) => {
925                        // Merge the new ContextMappings with existing ones
926                        let new_ctx = v.downcast_ref::<ContextMappings>().unwrap();
927                        let current = e.get().downcast_ref::<ContextMappings>().unwrap();
928                        // Build merged Vec - can't mutate through Rc, so create new
929                        let mut merged: Vec<_> = (*current.0).clone();
930                        merged.extend(new_ctx.0.iter().cloned());
931                        *e.get_mut() = Rc::new(ContextMappings(Rc::new(merged)));
932                    }
933                    Entry::Vacant(e) => {
934                        e.insert(v.clone());
935                    }
936                },
937                StyleKeyInfo::Transition => {
938                    self.map.insert(*k, v.clone());
939                }
940                StyleKeyInfo::Prop(info) => {
941                    // Track inherited props for O(1) early-exit in apply_only_inherited
942                    if info.inherited {
943                        self.has_inherited = true;
944                    }
945                    match self.map.entry(*k) {
946                        Entry::Occupied(mut e) => {
947                            // We need to merge the new map with the existing map.
948                            e.insert((info.combine)(e.get().clone(), v.clone()));
949                        }
950                        Entry::Vacant(e) => {
951                            e.insert(v.clone());
952                        }
953                    }
954                }
955            }
956        }
957    }
958
959    pub(crate) fn apply_iter_no_mappings<'a>(
960        &mut self,
961        iter: impl Iterator<Item = (&'a StyleKey, &'a Rc<dyn Any>)>,
962    ) {
963        for (k, v) in iter {
964            match k.info {
965                StyleKeyInfo::Class(..) | StyleKeyInfo::Selector(..) => {
966                    // Track class maps for O(1) early-exit in apply_only_class_maps
967                    if matches!(k.info, StyleKeyInfo::Class(..)) {
968                        self.has_class_maps = true;
969                    }
970                    match self.map.entry(*k) {
971                        Entry::Occupied(mut e) => {
972                            // We need to merge the new map with the existing map.
973
974                            let v = v.downcast_ref::<Style>().unwrap();
975                            match Rc::get_mut(e.get_mut()) {
976                                Some(current) => {
977                                    current
978                                        .downcast_mut::<Style>()
979                                        .unwrap()
980                                        .apply_mut(v.clone());
981                                }
982                                None => {
983                                    let mut current =
984                                        e.get_mut().downcast_ref::<Style>().unwrap().clone();
985                                    current.apply_mut(v.clone());
986                                    *e.get_mut() = Rc::new(current);
987                                }
988                            }
989                        }
990                        Entry::Vacant(e) => {
991                            e.insert(v.clone());
992                        }
993                    }
994                }
995                StyleKeyInfo::Transition => {
996                    self.map.insert(*k, v.clone());
997                }
998                StyleKeyInfo::Prop(info) => {
999                    // Track inherited props for O(1) early-exit in apply_only_inherited
1000                    if info.inherited {
1001                        self.has_inherited = true;
1002                    }
1003                    match self.map.entry(*k) {
1004                        Entry::Occupied(mut e) => {
1005                            // We need to merge the new map with the existing map.
1006                            e.insert((info.combine)(e.get().clone(), v.clone()));
1007                        }
1008                        Entry::Vacant(e) => {
1009                            e.insert(v.clone());
1010                        }
1011                    }
1012                }
1013                _ => {}
1014            }
1015        }
1016    }
1017
1018    pub(crate) fn apply_mut(&mut self, over: Style) {
1019        self.apply_iter(over.map.iter());
1020    }
1021
1022    pub(crate) fn apply_mut_no_mappings(&mut self, over: Style) {
1023        self.apply_iter_no_mappings(over.map.iter());
1024    }
1025
1026    /// Apply another `Style` to this style, returning a new `Style` with the overrides
1027    ///
1028    /// `StyleValue::Val` will override the value with the given value
1029    /// `StyleValue::Unset` will unset the value, causing it to fall back to the default.
1030    /// `StyleValue::Base` will leave the value as-is, whether falling back to the default
1031    /// or using the value in the `Style`.
1032    pub fn apply(mut self, over: Style) -> Style {
1033        self.apply_mut(over);
1034        self
1035    }
1036
1037    pub fn map(self, over: impl FnOnce(Self) -> Self) -> Self {
1038        over(self)
1039    }
1040
1041    /// Apply multiple `Style`s to this style, returning a new `Style` with the overrides.
1042    /// Later styles take precedence over earlier styles.
1043    pub fn apply_overriding_styles(self, overrides: impl Iterator<Item = Style>) -> Style {
1044        overrides.fold(self, |acc, x| acc.apply(x))
1045    }
1046
1047    // /// Apply context mappings with the given context (inherited props from ancestors).
1048    // /// Returns the style with context values applied and a flag indicating if changes were made.
1049    // ///
1050    // /// Uses iterative approach instead of recursion for better performance.
1051    // pub(crate) fn apply_context_mappings(mut self, context: &Style) -> (Self, bool) {
1052    //     let key = StyleKey {
1053    //         info: &CONTEXT_MAPPINGS_INFO,
1054    //     };
1055    //     let mut changed = false;
1056
1057    //     // Iterative approach: keep processing until no more context mappings
1058    //     loop {
1059    //         // Single lookup: use remove directly instead of get + remove
1060    //         let ctx_mappings = self
1061    //             .map
1062    //             .remove(&key)
1063    //             .and_then(|v| v.downcast_ref::<ContextMappings>().cloned());
1064
1065    //         match ctx_mappings {
1066    //             Some(mappings) => {
1067    //                 changed = true;
1068    //                 // Iterate over Rc<Vec> - no allocation needed
1069    //                 for mapping in mappings.0.iter() {
1070    //                     self = mapping(self, context);
1071    //                 }
1072    //             }
1073    //             None => break,
1074    //         }
1075    //     }
1076
1077    //     (self, changed)
1078    // }
1079}
1080
1081impl Debug for Style {
1082    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1083        f.debug_struct("Style")
1084            .field(
1085                "map",
1086                &self
1087                    .map
1088                    .iter()
1089                    .map(|(p, v)| (*p, (p.debug_any(&**v))))
1090                    .collect::<HashMap<StyleKey, String>>(),
1091            )
1092            .finish()
1093    }
1094}
1095
1096style_key_selector!(hover, StyleSelectors::new().set(StyleSelector::Hover, true));
1097style_key_selector!(
1098    file_hover,
1099    StyleSelectors::new().set(StyleSelector::FileHover, true)
1100);
1101style_key_selector!(focus, StyleSelectors::new().set(StyleSelector::Focus, true));
1102style_key_selector!(
1103    focus_visible,
1104    StyleSelectors::new().set(StyleSelector::FocusVisible, true)
1105);
1106style_key_selector!(
1107    disabled,
1108    StyleSelectors::new().set(StyleSelector::Disabled, true)
1109);
1110style_key_selector!(
1111    active,
1112    StyleSelectors::new().set(StyleSelector::Active, true)
1113);
1114style_key_selector!(
1115    dragging,
1116    StyleSelectors::new().set(StyleSelector::Dragging, true)
1117);
1118style_key_selector!(
1119    selected,
1120    StyleSelectors::new().set(StyleSelector::Selected, true)
1121);
1122style_key_selector!(
1123    darkmode,
1124    StyleSelectors::new().set(StyleSelector::DarkMode, true)
1125);
1126
1127impl StyleSelector {
1128    fn to_key(self) -> StyleKey {
1129        match self {
1130            StyleSelector::Hover => hover(),
1131            StyleSelector::Focus => focus(),
1132            StyleSelector::FocusVisible => focus_visible(),
1133            StyleSelector::Disabled => disabled(),
1134            StyleSelector::Active => active(),
1135            StyleSelector::Dragging => dragging(),
1136            StyleSelector::Selected => selected(),
1137            StyleSelector::DarkMode => darkmode(),
1138            StyleSelector::FileHover => file_hover(),
1139        }
1140    }
1141}
1142
1143/// Defines built-in style properties with optional builder methods.
1144///
1145/// Properties can be marked with flags in braces:
1146/// - `nocb` (no callback/no chain builder) - no fluent builder method generated
1147/// - `tr` (transition) - generates a `transition_property_name()` method
1148///
1149/// Examples: `name: Type {}`, `name {nocb}: Type {}`, `name {tr}: Type {}`, `name {nocb, tr}: Type {}`
1150///
1151/// All properties get:
1152/// - A getter method in `BuiltinStyle`
1153/// - An `unset_property_name()` method
1154macro_rules! define_builtin_props {
1155    (
1156        $(
1157            $(#[$meta:meta])*
1158            $type_name:ident $name:ident $({ $($flags:ident),* })? :
1159            $typ:ty { $($options:tt)* } = $val:expr
1160        ),*
1161        $(,)?
1162    ) => {
1163        $(
1164            prop!($(#[$meta])* pub $type_name: $typ { $($options)* } = $val);
1165        )*
1166        impl Style {
1167            $(
1168                define_builtin_props!(decl: $(#[$meta])* $type_name $name $({ $($flags),* })?: $typ = $val);
1169            )*
1170            $(
1171                define_builtin_props!(unset: $(#[$meta])* $type_name $name);
1172            )*
1173            $(
1174                define_builtin_props!(transition: $(#[$meta])* $type_name $name $({ $($flags),* })?);
1175            )*
1176        }
1177        impl BuiltinStyle<'_> {
1178            $(
1179                $(#[$meta])*
1180                pub fn $name(&self) -> $typ {
1181                    self.style.get($type_name)
1182                }
1183            )*
1184        }
1185    };
1186
1187    // With flags - check if nocb is present
1188    (decl: $(#[$meta:meta])* $type_name:ident $name:ident { $($flags:ident),* }: $typ:ty = $val:expr) => {
1189        define_builtin_props!(@check_nocb $(#[$meta])* $type_name $name [$($flags)*]: $typ);
1190    };
1191
1192    // Without flags - always generate setter
1193    (decl: $(#[$meta:meta])* $type_name:ident $name:ident: $typ:ty = $val:expr) => {
1194        $(#[$meta])*
1195        pub fn $name(self, v: impl Into<$typ>) -> Self {
1196            self.set($type_name, v.into())
1197        }
1198    };
1199
1200    // Helper: if nocb found, don't generate setter
1201    (@check_nocb $(#[$meta:meta])* $type_name:ident $name:ident [nocb $($rest:ident)*]: $typ:ty) => {};
1202    (@check_nocb $(#[$meta:meta])* $type_name:ident $name:ident [$first:ident $($rest:ident)*]: $typ:ty) => {
1203        define_builtin_props!(@check_nocb $(#[$meta])* $type_name $name [$($rest)*]: $typ);
1204    };
1205    (@check_nocb $(#[$meta:meta])* $type_name:ident $name:ident []: $typ:ty) => {
1206        // No nocb found, generate the setter
1207        $(#[$meta])*
1208        pub fn $name(self, v: impl Into<$typ>) -> Self {
1209            self.set($type_name, v.into())
1210        }
1211    };
1212
1213    // Unset method - generated for all properties
1214    (unset: $(#[$meta:meta])* $type_name:ident $name:ident) => {
1215        paste::paste! {
1216            #[doc = "Unsets the `" $name "` property."]
1217            pub fn [<unset_ $name>](self) -> Self {
1218                self.set_style_value($type_name, $crate::style::StyleValue::Unset)
1219            }
1220        }
1221    };
1222
1223    // Transition method - with flags, check if 'tr' is present
1224    (transition: $(#[$meta:meta])* $type_name:ident $name:ident { $($flags:ident),* }) => {
1225        define_builtin_props!(@check_tr $(#[$meta])* $type_name $name [$($flags)*]);
1226    };
1227
1228    // Transition method - without flags, don't generate
1229    (transition: $(#[$meta:meta])* $type_name:ident $name:ident) => {};
1230
1231    // Helper: if tr found, generate transition method
1232    (@check_tr $(#[$meta:meta])* $type_name:ident $name:ident [tr $($rest:ident)*]) => {
1233        paste::paste! {
1234            #[doc = "Sets a transition for the `" $name "` property."]
1235            $(#[$meta])*
1236            pub fn [<transition_ $name>](self, transition: impl Into<Transition>) -> Self {
1237                self.transition($type_name, transition.into())
1238            }
1239        }
1240    };
1241    (@check_tr $(#[$meta:meta])* $type_name:ident $name:ident [$first:ident $($rest:ident)*]) => {
1242        define_builtin_props!(@check_tr $(#[$meta])* $type_name $name [$($rest)*]);
1243    };
1244    (@check_tr $(#[$meta:meta])* $type_name:ident $name:ident []) => {
1245        // No tr flag found, don't generate transition method
1246    };
1247}
1248
1249pub struct BuiltinStyle<'a> {
1250    style: &'a Style,
1251}
1252
1253define_builtin_props!(
1254    /// Controls the display type of the view.
1255    ///
1256    /// This determines how the view participates in layout.
1257    DisplayProp display {}: Display {} = Display::Flex,
1258
1259    /// Sets the positioning scheme for the view.
1260    ///
1261    /// This affects how the view is positioned relative to its normal position in the document flow.
1262    PositionProp position {}: Position {} = Position::Relative,
1263
1264    /// Enables fixed positioning relative to the viewport.
1265    ///
1266    /// When true, the view is positioned relative to the window viewport rather than
1267    /// its parent. This is similar to CSS `position: fixed`. The view will:
1268    /// - Use `inset` properties relative to the viewport
1269    /// - Have percentage sizes relative to the viewport
1270    /// - Be painted above all other content (like overlays)
1271    ///
1272    /// Note: This works in conjunction with `position: absolute` internally.
1273    IsFixed is_fixed {}: bool {} = false,
1274
1275    /// Sets the width of the view.
1276    ///
1277    /// Can be specified in pixels, percentages, or auto.
1278    Width width {tr}: PxPctAuto {} = PxPctAuto::Auto,
1279
1280    /// Sets the height of the view.
1281    ///
1282    /// Can be specified in pixels, percentages, or auto.
1283    Height height {tr}: PxPctAuto {} = PxPctAuto::Auto,
1284
1285    /// Sets the minimum width of the view.
1286    ///
1287    /// The view will not shrink below this width.
1288    MinWidth min_width {tr}: PxPctAuto {} = PxPctAuto::Auto,
1289
1290    /// Sets the minimum height of the view.
1291    ///
1292    /// The view will not shrink below this height.
1293    MinHeight min_height {tr}: PxPctAuto {} = PxPctAuto::Auto,
1294
1295    /// Sets the maximum width of the view.
1296    ///
1297    /// The view will not grow beyond this width.
1298    MaxWidth max_width {tr}: PxPctAuto {} = PxPctAuto::Auto,
1299
1300    /// Sets the maximum height of the view.
1301    ///
1302    /// The view will not grow beyond this height.
1303    MaxHeight max_height {tr}: PxPctAuto {} = PxPctAuto::Auto,
1304
1305    /// Sets the direction of the main axis for flex items.
1306    ///
1307    /// Determines whether flex items are laid out in rows or columns.
1308    FlexDirectionProp flex_direction {}: FlexDirection {} = FlexDirection::Row,
1309
1310    /// Controls whether flex items wrap to new lines.
1311    ///
1312    /// When enabled, items that don't fit will wrap to the next line.
1313    FlexWrapProp flex_wrap {}: FlexWrap {} = FlexWrap::NoWrap,
1314
1315    /// Sets the flex grow factor for the flex item.
1316    ///
1317    /// Determines how much the item should grow relative to other items.
1318    FlexGrow flex_grow {}: f32 {} = 0.0,
1319
1320    /// Sets the flex shrink factor for the flex item.
1321    ///
1322    /// Determines how much the item should shrink relative to other items.
1323    FlexShrink flex_shrink {}: f32 {} = 1.0,
1324
1325    /// Sets the initial main size of a flex item.
1326    ///
1327    /// This is the size of the item before free space is distributed.
1328    FlexBasis flex_basis {tr}: PxPctAuto {} = PxPctAuto::Auto,
1329
1330    /// Controls alignment of flex items along the main axis.
1331    ///
1332    /// Determines how extra space is distributed between and around items.
1333    JustifyContentProp justify_content {}: Option<JustifyContent> {} = None,
1334
1335    /// Controls default alignment of grid items along the inline axis.
1336    ///
1337    /// Sets the default justify-self value for all items in the container.
1338    JustifyItemsProp justify_items {}: Option<JustifyItems> {} = None,
1339
1340    /// Controls how the total width and height are calculated.
1341    ///
1342    /// Determines whether borders and padding are included in the view's size.
1343    BoxSizingProp box_sizing {}: Option<BoxSizing> {} = None,
1344
1345    /// Controls individual alignment along the inline axis.
1346    ///
1347    /// Overrides the container's justify-items value for this specific item.
1348    JustifySelf justify_self {}: Option<AlignItems> {} = None,
1349
1350    /// Controls alignment of flex items along the cross axis.
1351    ///
1352    /// Determines how items are aligned when they don't fill the container's cross axis.
1353    AlignItemsProp align_items {}: Option<AlignItems> {} = None,
1354
1355    /// Controls alignment of wrapped flex lines.
1356    ///
1357    /// Only has an effect when flex-wrap is enabled and there are multiple lines.
1358    AlignContentProp align_content {}: Option<AlignContent> {} = None,
1359
1360    /// Defines the line names and track sizing functions of the grid rows.
1361    ///
1362    /// Specifies the size and names of the rows in a grid layout.
1363    GridTemplateRows grid_template_rows {}: Vec<GridTemplateComponent<String>> {} = Vec::new(),
1364
1365    /// Defines the line names and track sizing functions of the grid columns.
1366    ///
1367    /// Specifies the size and names of the columns in a grid layout.
1368    GridTemplateColumns grid_template_columns {}: Vec<GridTemplateComponent<String>> {} = Vec::new(),
1369
1370    /// Specifies the size of implicitly-created grid rows.
1371    ///
1372    /// Sets the default size for rows that are created automatically.
1373    GridAutoRows grid_auto_rows {}: Vec<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> {} = Vec::new(),
1374
1375    /// Specifies the size of implicitly-created grid columns.
1376    ///
1377    /// Sets the default size for columns that are created automatically.
1378    GridAutoColumns grid_auto_columns {}: Vec<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> {} = Vec::new(),
1379
1380    /// Controls how auto-placed items get flowed into the grid.
1381    ///
1382    /// Determines the direction that grid items are placed when not explicitly positioned.
1383    GridAutoFlow grid_auto_flow {}: taffy::GridAutoFlow {} = taffy::GridAutoFlow::Row,
1384
1385    /// Specifies a grid item's location within the grid row.
1386    ///
1387    /// Determines which grid rows the item spans.
1388    GridRow grid_row {}: Line<GridPlacement> {} = Line::default(),
1389
1390    /// Specifies a grid item's location within the grid column.
1391    ///
1392    /// Determines which grid columns the item spans.
1393    GridColumn grid_column {}: Line<GridPlacement> {} = Line::default(),
1394
1395    /// Controls individual alignment along the cross axis.
1396    ///
1397    /// Overrides the container's align-items value for this specific item.
1398    AlignSelf align_self {}: Option<AlignItems> {} = None,
1399
1400    /// Sets the color of the view's outline.
1401    ///
1402    /// The outline is drawn outside the border and doesn't affect layout.
1403    OutlineColor outline_color {tr}: Brush {} = Brush::Solid(palette::css::TRANSPARENT),
1404
1405    /// Sets the outline stroke properties.
1406    ///
1407    /// Defines the width, style, and other properties of the outline.
1408    Outline outline {nocb, tr}: StrokeWrap {} = StrokeWrap::new(0.),
1409
1410    /// Controls the progress/completion of the outline animation.
1411    ///
1412    /// Useful for creating animated outline effects.
1413    OutlineProgress outline_progress {tr}: Pct {} = Pct(100.),
1414
1415    /// Controls the progress/completion of the border animation.
1416    ///
1417    /// Useful for creating animated border effects.
1418    BorderProgress border_progress {tr}: Pct {} = Pct(100.),
1419
1420    /// Sets the border properties for all sides.
1421    ///
1422    /// Defines width, style, and other border characteristics.
1423    BorderProp border_combined {nocb, tr}: Border {} = Border::default(),
1424
1425    /// Sets the border color for all sides.
1426    ///
1427    /// Can be set individually for each side or all at once.
1428    BorderColorProp border_color_combined { nocb, tr }: BorderColor {} = BorderColor::default(),
1429
1430    /// Sets the border radius for all corners.
1431    ///
1432    /// Controls how rounded the corners of the view are.
1433    BorderRadiusProp border_radius_combined { nocb, tr }: BorderRadius {} = BorderRadius::default(),
1434
1435    /// Sets the padding for all sides.
1436    ///
1437    /// Padding is the space between the view's content and its border.
1438    PaddingProp padding_combined { nocb, tr }: Padding {} = Padding::default(),
1439
1440    /// Sets the margin for all sides.
1441    ///
1442    /// Margin is the space outside the view's border.
1443    MarginProp margin_combined { nocb, tr }: Margin {} = Margin::default(),
1444
1445    /// Sets the left offset for positioned views.
1446    InsetLeft inset_left {tr}: PxPctAuto {} = PxPctAuto::Auto,
1447
1448    /// Sets the top offset for positioned views.
1449    InsetTop inset_top {tr}: PxPctAuto {} = PxPctAuto::Auto,
1450
1451    /// Sets the right offset for positioned views.
1452    InsetRight inset_right {tr}: PxPctAuto {} = PxPctAuto::Auto,
1453
1454    /// Sets the bottom offset for positioned views.
1455    InsetBottom inset_bottom {tr}: PxPctAuto {} = PxPctAuto::Auto,
1456
1457    /// Controls whether the view can be the target of mouse events.
1458    ///
1459    /// When disabled, mouse events pass through to views behind.
1460    PointerEventsProp pointer_events {}: Option<PointerEvents> { inherited } = None,
1461
1462    /// Controls the stack order of positioned views.
1463    ///
1464    /// Higher values appear in front of lower values.
1465    ZIndex z_index { nocb, tr }: Option<i32> {} = None,
1466
1467    /// Sets the cursor style when hovering over the view.
1468    ///
1469    /// Changes the appearance of the mouse cursor.
1470    Cursor cursor { nocb }: Option<CursorStyle> {} = None,
1471
1472    /// Sets the text color.
1473    ///
1474    /// This property is inherited by child views.
1475    TextColor color { nocb, tr }: Option<Color> { inherited } = None,
1476
1477    /// Sets the background color or image.
1478    ///
1479    /// Can be a solid color, gradient, or image.
1480    Background background { nocb, tr }: Option<Brush> {} = None,
1481
1482    /// Sets the foreground color or pattern.
1483    ///
1484    /// Used for drawing content like icons or shapes.
1485    Foreground foreground { nocb, tr }: Option<Brush> {} = None,
1486
1487    /// Adds one or more drop shadows to the view.
1488    ///
1489    /// Can create depth and visual separation effects.
1490    BoxShadowProp box_shadow { nocb, tr }: SmallVec<[BoxShadow; 3]> {} = SmallVec::new(),
1491
1492    /// Sets the font size for text content.
1493    ///
1494    /// This property is inherited by child views.
1495    FontSize font_size { nocb, tr }: Option<f32> { inherited } = None,
1496
1497    /// Sets the font family for text content.
1498    ///
1499    /// This property is inherited by child views.
1500    FontFamily font_family { nocb }: Option<String> { inherited } = None,
1501
1502    /// Sets the font weight (boldness) for text content.
1503    ///
1504    /// This property is inherited by child views.
1505    FontWeight font_weight { nocb }: Option<Weight> { inherited } = None,
1506
1507    /// Sets the font style (italic, normal) for text content.
1508    ///
1509    /// This property is inherited by child views.
1510    FontStyle font_style { nocb }: Option<crate::text::Style> { inherited } = None,
1511
1512    /// Sets the color of the text cursor.
1513    ///
1514    /// Visible when text input views have focus.
1515    CursorColor cursor_color { nocb, tr }: Brush {} = Brush::Solid(palette::css::BLACK.with_alpha(0.3)),
1516
1517    /// Sets the corner radius of text selections.
1518    ///
1519    /// Controls how rounded the corners of selected text appear.
1520    SelectionCornerRadius selection_corer_radius { nocb, tr }: f64 {} = 1.,
1521
1522    /// Controls whether the view's text can be selected.
1523    ///
1524    /// This property is inherited by child views.
1525    Selectable selectable {}: bool { inherited } = true,
1526
1527    /// Controls how overflowed text content is handled.
1528    ///
1529    /// Determines whether text wraps or gets clipped.
1530    TextOverflowProp text_overflow {}: TextOverflow {} = TextOverflow::Wrap,
1531
1532    /// Sets text alignment within the view.
1533    ///
1534    /// Controls horizontal alignment of text content.
1535    TextAlignProp text_align {}: Option<crate::text::Align> {} = None,
1536
1537    /// Sets the line height for text content.
1538    ///
1539    /// This property is inherited by child views.
1540    LineHeight line_height { nocb, tr }: Option<LineHeightValue> { inherited } = None,
1541
1542    /// Sets the preferred aspect ratio for the view.
1543    ///
1544    /// Maintains width-to-height proportions during layout.
1545    AspectRatio aspect_ratio {tr}: Option<f32> {} = None,
1546
1547    /// Sets the gap between columns in grid or flex layouts.
1548    ///
1549    /// Creates space between items in the horizontal direction.
1550    ColGap col_gap { nocb, tr }: PxPct {} = PxPct::Px(0.),
1551
1552    /// Sets the gap between rows in grid or flex layouts.
1553    ///
1554    /// Creates space between items in the vertical direction.
1555    RowGap row_gap { nocb, tr }: PxPct {} = PxPct::Px(0.),
1556
1557    /// Sets the horizontal scale transform.
1558    ///
1559    /// Values less than 100% shrink the view, greater than 100% enlarge it.
1560    ScaleX scale_x {tr}: Pct {} = Pct(100.),
1561
1562    /// Sets the vertical scale transform.
1563    ///
1564    /// Values less than 100% shrink the view, greater than 100% enlarge it.
1565    ScaleY scale_y {tr}: Pct {} = Pct(100.),
1566
1567    /// Sets the horizontal translation transform.
1568    ///
1569    /// Moves the view left (negative) or right (positive).
1570    TranslateX translate_x {tr}: PxPct {} = PxPct::Px(0.),
1571
1572    /// Sets the vertical translation transform.
1573    ///
1574    /// Moves the view up (negative) or down (positive).
1575    TranslateY translate_y {tr}: PxPct {} = PxPct::Px(0.),
1576
1577    /// Sets the rotation transform angle.
1578    ///
1579    /// Positive values rotate clockwise, negative values rotate counter-clockwise.
1580    /// Use `.deg()` or `.rad()` methods to specify the angle unit.
1581    Rotation rotate {tr}: Angle {} = Angle::Rad(0.0),
1582
1583    /// Sets the anchor point for rotation transformations.
1584    ///
1585    /// Determines the point around which the view rotates. Use predefined constants
1586    /// like `AnchorAbout::CENTER` or create custom anchor points with pixel or percentage values.
1587    RotateAbout rotate_about {}: AnchorAbout {} = AnchorAbout::CENTER,
1588
1589    /// Sets the anchor point for scaling transformations.
1590    ///
1591    /// Determines the point around which the view scales. Use predefined constants
1592    /// like `AnchorAbout::CENTER` or create custom anchor points with pixel or percentage values.
1593    ScaleAbout scale_about {tr}: AnchorAbout {} = AnchorAbout::CENTER,
1594
1595    /// Sets the opacity of the view.
1596    ///
1597    /// Values range from 0.0 (fully transparent) to 1.0 (fully opaque).
1598    /// This affects the entire view including its children.
1599    Opacity opacity {tr}: f32 {} = 1.0,
1600
1601    /// Sets the selected state of the view.
1602    ///
1603    /// This property is inherited by child views.
1604    Selected set_selected {}: bool { inherited } = false,
1605
1606    /// Controls the disabled state of the view.
1607    ///
1608    /// This property is inherited by child views.
1609    Disabled set_disabled {}: bool { inherited } = false,
1610
1611    /// Controls whether the view can receive focus.
1612    ///
1613    /// Focus is necessary for keyboard interaction.
1614    Focusable focusable {}: bool { } = false,
1615
1616    /// Controls whether the view can be dragged.
1617    ///
1618    /// Enables drag-and-drop functionality for the view.
1619    Draggable draggable {}: bool { } = false,
1620);
1621
1622impl BuiltinStyle<'_> {
1623    // Individual padding accessors
1624    pub fn padding_left(&self) -> PxPct {
1625        self.style.get(PaddingProp).left.unwrap_or(PxPct::Px(0.0))
1626    }
1627    pub fn padding_top(&self) -> PxPct {
1628        self.style.get(PaddingProp).top.unwrap_or(PxPct::Px(0.0))
1629    }
1630    pub fn padding_right(&self) -> PxPct {
1631        self.style.get(PaddingProp).right.unwrap_or(PxPct::Px(0.0))
1632    }
1633    pub fn padding_bottom(&self) -> PxPct {
1634        self.style.get(PaddingProp).bottom.unwrap_or(PxPct::Px(0.0))
1635    }
1636
1637    // Individual margin accessors
1638    pub fn margin_left(&self) -> PxPctAuto {
1639        self.style
1640            .get(MarginProp)
1641            .left
1642            .unwrap_or(PxPctAuto::Px(0.0))
1643    }
1644    pub fn margin_top(&self) -> PxPctAuto {
1645        self.style.get(MarginProp).top.unwrap_or(PxPctAuto::Px(0.0))
1646    }
1647    pub fn margin_right(&self) -> PxPctAuto {
1648        self.style
1649            .get(MarginProp)
1650            .right
1651            .unwrap_or(PxPctAuto::Px(0.0))
1652    }
1653    pub fn margin_bottom(&self) -> PxPctAuto {
1654        self.style
1655            .get(MarginProp)
1656            .bottom
1657            .unwrap_or(PxPctAuto::Px(0.0))
1658    }
1659}
1660
1661prop!(
1662    /// How children overflowing their container in Y axis should affect layout
1663    pub OverflowX: Overflow {} = Overflow::default()
1664);
1665
1666prop!(
1667    /// How children overflowing their container in X axis should affect layout
1668    pub OverflowY: Overflow {} = Overflow::default()
1669);
1670
1671prop_extractor! {
1672    pub FontProps {
1673        pub size: FontSize,
1674        pub family: FontFamily,
1675        pub weight: FontWeight,
1676        pub style: FontStyle,
1677    }
1678}
1679
1680prop_extractor! {
1681    pub(crate) LayoutProps {
1682        pub border: BorderProp,
1683        pub padding: PaddingProp,
1684        pub margin: MarginProp,
1685
1686        pub width: Width,
1687        pub height: Height,
1688
1689        pub min_width: MinWidth,
1690        pub min_height: MinHeight,
1691
1692        pub max_width: MaxWidth,
1693        pub max_height: MaxHeight,
1694
1695        pub flex_grow: FlexGrow,
1696        pub flex_shrink: FlexShrink,
1697        pub flex_basis: FlexBasis ,
1698
1699        pub inset_left: InsetLeft,
1700        pub inset_top: InsetTop,
1701        pub inset_right: InsetRight,
1702        pub inset_bottom: InsetBottom,
1703
1704        pub row_gap: RowGap,
1705        pub col_gap: ColGap,
1706    }
1707}
1708
1709prop_extractor! {
1710    pub TransformProps {
1711        pub scale_x: ScaleX,
1712        pub scale_y: ScaleY,
1713
1714        pub translate_x: TranslateX,
1715        pub translate_y: TranslateY,
1716
1717        pub rotation: Rotation,
1718        pub rotate_about: RotateAbout,
1719        pub scale_about: ScaleAbout,
1720    }
1721}
1722impl TransformProps {
1723    pub fn affine(&self, size: kurbo::Size) -> Affine {
1724        let mut transform = Affine::IDENTITY;
1725
1726        let transform_x = match self.translate_x() {
1727            crate::unit::PxPct::Px(px) => px,
1728            crate::unit::PxPct::Pct(pct) => pct / 100.,
1729        };
1730        let transform_y = match self.translate_y() {
1731            crate::unit::PxPct::Px(px) => px,
1732            crate::unit::PxPct::Pct(pct) => pct / 100.,
1733        };
1734        transform *= Affine::translate(Vec2 {
1735            x: transform_x,
1736            y: transform_y,
1737        });
1738
1739        let scale_x = self.scale_x().0 / 100.;
1740        let scale_y = self.scale_y().0 / 100.;
1741        let rotation = self.rotation().to_radians();
1742
1743        // Get rotation and scale anchor points
1744        let rotate_about = self.rotate_about();
1745        let scale_about = self.scale_about();
1746
1747        // Convert anchor points to fractional positions
1748        let (rotate_x_frac, rotate_y_frac) = rotate_about.as_fractions();
1749        let (scale_x_frac, scale_y_frac) = scale_about.as_fractions();
1750
1751        let rotate_point = Vec2 {
1752            x: rotate_x_frac * size.width,
1753            y: rotate_y_frac * size.height,
1754        };
1755
1756        let scale_point = Vec2 {
1757            x: scale_x_frac * size.width,
1758            y: scale_y_frac * size.height,
1759        };
1760
1761        // Apply transformations using the specified anchor points
1762        if scale_x != 1.0 || scale_y != 1.0 {
1763            // Manual non-uniform scaling about a point: translate -> scale -> translate back
1764            let scale_center = scale_point;
1765            transform = transform
1766                .then_translate(-scale_center)
1767                .then_scale_non_uniform(scale_x, scale_y)
1768                .then_translate(scale_center);
1769        }
1770        if rotation != 0.0 {
1771            // Manual rotation about a point: translate -> rotate -> translate back
1772            let rotate_center = rotate_point;
1773            transform = transform
1774                .then_translate(-rotate_center)
1775                .then_rotate(rotation)
1776                .then_translate(rotate_center);
1777        }
1778
1779        transform
1780    }
1781}
1782
1783prop_extractor! {
1784    pub BoxTreeProps {
1785        pub scale_about: ScaleAbout,
1786        pub z_index: ZIndex,
1787        pub pointer_events: PointerEventsProp,
1788        pub focusable: Focusable,
1789        pub disabled: Disabled,
1790        pub display: DisplayProp,
1791        pub overflow_x: OverflowX,
1792        pub overflow_y: OverflowY,
1793        pub border_radius: BorderRadiusProp,
1794    }
1795}
1796impl BoxTreeProps {
1797    pub fn pickable(&self) -> bool {
1798        self.pointer_events() != Some(PointerEvents::None)
1799    }
1800
1801    // pub fn set_box_tree(
1802    //     &self,
1803    //     node_id: understory_box_tree::NodeId,
1804    //     box_tree: &mut understory_box_tree::Tree,
1805    // ) {
1806    //     box_tree.set_z_index(node_id, self.z_index().unwrap_or(0));
1807    //     let mut flags = NodeFlags::empty();
1808    //     if self.pickable() {
1809    //         flags |= NodeFlags::PICKABLE;
1810    //     }
1811    //     if self.focusable() && !self.hidden() && self.display() != Display::None && !self.disabled()
1812    //     {
1813    //         flags |= NodeFlags::FOCUSABLE;
1814    //     }
1815    //     if !self.hidden() {
1816    //         flags |= NodeFlags::VISIBLE;
1817    //     }
1818    //     box_tree.set_flags(node_id, flags);
1819    // }
1820
1821    pub fn clip_rect(&self, mut rect: kurbo::Rect) -> Option<RoundedRect> {
1822        use Overflow::*;
1823
1824        let (overflow_x, overflow_y) = (self.overflow_x(), self.overflow_y());
1825
1826        // No clipping if both are visible
1827        if overflow_x == Visible && overflow_y == Visible {
1828            return None;
1829        }
1830
1831        let border_radius = self
1832            .border_radius()
1833            .resolve_border_radii(rect.size().min_side());
1834
1835        // Extend to infinity on visible axes
1836        if overflow_x == Visible {
1837            rect.x0 = f64::NEG_INFINITY;
1838            rect.x1 = f64::INFINITY;
1839        }
1840        if overflow_y == Visible {
1841            rect.y0 = f64::NEG_INFINITY;
1842            rect.y1 = f64::INFINITY;
1843        }
1844
1845        Some(RoundedRect::from_rect(rect, border_radius))
1846    }
1847}
1848
1849impl LayoutProps {
1850    pub fn to_style(&self) -> Style {
1851        let border = self.border();
1852        let padding = self.padding();
1853        let margin = self.margin();
1854        Style::new()
1855            .width(self.width())
1856            .height(self.height())
1857            .apply_border(border)
1858            .apply_padding(padding)
1859            .min_width(self.min_width())
1860            .min_height(self.min_height())
1861            .max_width(self.max_width())
1862            .max_height(self.max_height())
1863            .flex_grow(self.flex_grow())
1864            .flex_shrink(self.flex_shrink())
1865            .flex_basis(self.flex_basis())
1866            .inset_left(self.inset_left())
1867            .inset_top(self.inset_top())
1868            .inset_right(self.inset_right())
1869            .inset_bottom(self.inset_bottom())
1870            .apply_margin(margin)
1871            .col_gap(self.col_gap())
1872            .row_gap(self.row_gap())
1873    }
1874}
1875
1876prop_extractor! {
1877    pub SelectionStyle {
1878        pub corner_radius: SelectionCornerRadius,
1879        pub selection_color: CursorColor,
1880    }
1881}
1882
1883impl Style {
1884    /// Gets the value of a style property, returning the default if not set.
1885    pub fn get<P: StyleProp>(&self, _prop: P) -> P::Type {
1886        self.get_prop_or_default::<P>()
1887    }
1888
1889    /// Gets the raw style value of a property, including unset and base states.
1890    pub fn get_style_value<P: StyleProp>(&self, _prop: P) -> StyleValue<P::Type> {
1891        self.get_prop_style_value::<P>()
1892    }
1893
1894    /// Sets a style property to a specific value.
1895    pub fn set<P: StyleProp>(self, prop: P, value: impl Into<P::Type>) -> Self {
1896        self.set_style_value(prop, StyleValue::Val(value.into()))
1897    }
1898
1899    pub fn set_style_value<P: StyleProp>(mut self, _prop: P, value: StyleValue<P::Type>) -> Self {
1900        let insert = match value {
1901            StyleValue::Val(value) => StyleMapValue::Val(value),
1902            StyleValue::Animated(value) => StyleMapValue::Animated(value),
1903            StyleValue::Unset => StyleMapValue::Unset,
1904            StyleValue::Base => {
1905                self.map.remove(&P::key());
1906                return self;
1907            }
1908        };
1909        // Track inherited props for O(1) early-exit in apply_only_inherited
1910        if P::prop_ref().info().inherited {
1911            self.has_inherited = true;
1912        }
1913        self.map.insert(P::key(), Rc::new(insert));
1914        self
1915    }
1916
1917    /// Sets a transition animation for a specific style property.
1918    pub fn transition<P: StyleProp>(mut self, _prop: P, transition: Transition) -> Self {
1919        self.map
1920            .insert(P::prop_ref().info().transition_key, Rc::new(transition));
1921        self
1922    }
1923
1924    fn selector(mut self, selector: StyleSelector, style: impl FnOnce(Style) -> Style) -> Self {
1925        let over = style(Style::default());
1926        self.set_selector(selector, over);
1927        self
1928    }
1929
1930    /// The visual style to apply when the mouse hovers over the view
1931    pub fn hover(self, style: impl FnOnce(Style) -> Style) -> Self {
1932        self.selector(StyleSelector::Hover, style)
1933    }
1934
1935    /// The visual style to apply when the view has keyboard focus.
1936    pub fn focus(self, style: impl FnOnce(Style) -> Style) -> Self {
1937        self.selector(StyleSelector::Focus, style)
1938    }
1939
1940    /// Similar to the `:focus-visible` css selector, this style only activates when tab navigation is used.
1941    pub fn focus_visible(self, style: impl FnOnce(Style) -> Style) -> Self {
1942        self.selector(StyleSelector::FocusVisible, style)
1943    }
1944
1945    /// The visual style to apply when the view is in a selected state.
1946    pub fn selected(self, style: impl FnOnce(Style) -> Style) -> Self {
1947        self.selector(StyleSelector::Selected, style)
1948    }
1949
1950    /// The visual style to apply when the view is being dragged
1951    pub fn drag(self, style: impl FnOnce(Style) -> Style) -> Self {
1952        self.selector(StyleSelector::Dragging, style)
1953    }
1954
1955    /// The visual style to apply when the view is disabled.
1956    pub fn disabled(self, style: impl FnOnce(Style) -> Style) -> Self {
1957        self.selector(StyleSelector::Disabled, style)
1958    }
1959
1960    /// The visual style to apply when the application is in dark mode.
1961    pub fn dark_mode(self, style: impl FnOnce(Style) -> Style) -> Self {
1962        self.selector(StyleSelector::DarkMode, style)
1963    }
1964
1965    /// The visual style to apply when a file is being dragged over the view.
1966    pub fn file_hover(self, style: impl FnOnce(Style) -> Style) -> Self {
1967        self.selector(StyleSelector::FileHover, style)
1968    }
1969
1970    /// The visual style to apply when the view is being actively pressed.
1971    pub fn active(self, style: impl FnOnce(Style) -> Style) -> Self {
1972        self.selector(StyleSelector::Active, style)
1973    }
1974
1975    /// Applies styles that activate at specific screen sizes (responsive design).
1976    pub fn responsive(mut self, size: ScreenSize, style: impl FnOnce(Style) -> Style) -> Self {
1977        let over = style(Style::default());
1978        for breakpoint in size.breakpoints() {
1979            self.set_breakpoint(breakpoint, over.clone());
1980        }
1981        self
1982    }
1983
1984    /// Applies styles to views with a specific CSS class.
1985    pub fn class<C: StyleClass>(mut self, _class: C, style: impl FnOnce(Style) -> Style) -> Self {
1986        let over = style(Style::default());
1987        self.set_class(C::class_ref(), over);
1988        self
1989    }
1990
1991    /// Applies a `CustomStyle` type to the `CustomStyle`'s associated style class.
1992    ///
1993    /// For example: if the `CustomStyle` you use is `DropdownCustomStyle` then it
1994    /// will apply the custom style to that custom style type's associated style class
1995    /// which, in this example, is `DropdownClass`.
1996    ///
1997    /// This is especially useful when building a stylesheet or targeting a child view.
1998    ///
1999    /// # Examples
2000    /// ```
2001    /// // In a style sheet or on a parent view
2002    /// use floem::prelude::*;
2003    /// use floem::style::Style;
2004    /// Style::new().custom_style_class(|s: dropdown::DropdownCustomStyle| s.close_on_accept(false));
2005    /// // This property is now set on the `DropdownClass` class and will be applied to any dropdowns that are children of this view.
2006    /// ```
2007    ///
2008    /// See also: [`Style::custom`](Self::custom) and [`Style::apply_custom`](Self::apply_custom).
2009    pub fn custom_style_class<CS: CustomStyle>(mut self, style: impl FnOnce(CS) -> CS) -> Self {
2010        let over = style(CS::default());
2011        self.set_class(CS::StyleClass::class_ref(), over.into());
2012        self
2013    }
2014
2015    /// Sets the width to 100% of the parent container.
2016    pub fn width_full(self) -> Self {
2017        self.width_pct(100.0)
2018    }
2019
2020    /// Sets the width as a percentage of the parent container.
2021    pub fn width_pct(self, width: f64) -> Self {
2022        self.width(width.pct())
2023    }
2024
2025    /// Sets the height to 100% of the parent container.
2026    pub fn height_full(self) -> Self {
2027        self.height_pct(100.0)
2028    }
2029
2030    /// Sets the height as a percentage of the parent container.
2031    pub fn height_pct(self, height: f64) -> Self {
2032        self.height(height.pct())
2033    }
2034
2035    /// Sets the gap between columns in grid or flex layouts.
2036    pub fn col_gap(self, width: impl Into<PxPct>) -> Self {
2037        self.set(ColGap, width.into())
2038    }
2039
2040    /// Sets the gap between rows in grid or flex layouts.
2041    pub fn row_gap(self, height: impl Into<PxPct>) -> Self {
2042        self.set(RowGap, height.into())
2043    }
2044
2045    /// Sets different gaps for rows and columns in grid or flex layouts.
2046    pub fn row_col_gap(self, width: impl Into<PxPct>, height: impl Into<PxPct>) -> Self {
2047        self.col_gap(width).row_gap(height)
2048    }
2049
2050    /// Sets the same gap for both rows and columns in grid or flex layouts.
2051    pub fn gap(self, gap: impl Into<PxPct>) -> Self {
2052        let gap = gap.into();
2053        self.col_gap(gap).row_gap(gap)
2054    }
2055
2056    /// Sets both width and height of the view.
2057    pub fn size(self, width: impl Into<PxPctAuto>, height: impl Into<PxPctAuto>) -> Self {
2058        self.width(width).height(height)
2059    }
2060
2061    /// Sets both width and height to 100% of the parent container.
2062    pub fn size_full(self) -> Self {
2063        self.size_pct(100.0, 100.0)
2064    }
2065
2066    /// Sets both width and height as percentages of the parent container.
2067    pub fn size_pct(self, width: f64, height: f64) -> Self {
2068        self.width(width.pct()).height(height.pct())
2069    }
2070
2071    /// Sets the minimum width to 100% of the parent container.
2072    pub fn min_width_full(self) -> Self {
2073        self.min_width_pct(100.0)
2074    }
2075
2076    /// Sets the minimum width as a percentage of the parent container.
2077    pub fn min_width_pct(self, min_width: f64) -> Self {
2078        self.min_width(min_width.pct())
2079    }
2080
2081    /// Sets the minimum height to 100% of the parent container.
2082    pub fn min_height_full(self) -> Self {
2083        self.min_height_pct(100.0)
2084    }
2085
2086    /// Sets the minimum height as a percentage of the parent container.
2087    pub fn min_height_pct(self, min_height: f64) -> Self {
2088        self.min_height(min_height.pct())
2089    }
2090
2091    /// Sets both minimum width and height to 100% of the parent container.
2092    pub fn min_size_full(self) -> Self {
2093        self.min_size_pct(100.0, 100.0)
2094    }
2095
2096    /// Sets both minimum width and height of the view.
2097    pub fn min_size(
2098        self,
2099        min_width: impl Into<PxPctAuto>,
2100        min_height: impl Into<PxPctAuto>,
2101    ) -> Self {
2102        self.min_width(min_width).min_height(min_height)
2103    }
2104
2105    /// Sets both minimum width and height as percentages of the parent container.
2106    pub fn min_size_pct(self, min_width: f64, min_height: f64) -> Self {
2107        self.min_size(min_width.pct(), min_height.pct())
2108    }
2109
2110    /// Sets the maximum width to 100% of the parent container.
2111    pub fn max_width_full(self) -> Self {
2112        self.max_width_pct(100.0)
2113    }
2114
2115    /// Sets the maximum width as a percentage of the parent container.
2116    pub fn max_width_pct(self, max_width: f64) -> Self {
2117        self.max_width(max_width.pct())
2118    }
2119
2120    /// Sets the maximum height to 100% of the parent container.
2121    pub fn max_height_full(self) -> Self {
2122        self.max_height_pct(100.0)
2123    }
2124
2125    /// Sets the maximum height as a percentage of the parent container.
2126    pub fn max_height_pct(self, max_height: f64) -> Self {
2127        self.max_height(max_height.pct())
2128    }
2129
2130    /// Sets both maximum width and height of the view.
2131    pub fn max_size(
2132        self,
2133        max_width: impl Into<PxPctAuto>,
2134        max_height: impl Into<PxPctAuto>,
2135    ) -> Self {
2136        self.max_width(max_width).max_height(max_height)
2137    }
2138
2139    /// Sets both maximum width and height to 100% of the parent container.
2140    pub fn max_size_full(self) -> Self {
2141        self.max_size_pct(100.0, 100.0)
2142    }
2143
2144    /// Sets both maximum width and height as percentages of the parent container.
2145    pub fn max_size_pct(self, max_width: f64, max_height: f64) -> Self {
2146        self.max_size(max_width.pct(), max_height.pct())
2147    }
2148
2149    /// Sets the border color for all sides of the view.
2150    pub fn border_color(self, color: impl Into<Brush>) -> Self {
2151        self.set(BorderColorProp, BorderColor::all(color))
2152    }
2153
2154    /// Sets the border properties for all sides of the view.
2155    pub fn border(self, border: impl Into<StrokeWrap>) -> Self {
2156        self.set(BorderProp, Border::all(border))
2157    }
2158
2159    /// Sets the outline properties of the view.
2160    pub fn outline(self, outline: impl Into<StrokeWrap>) -> Self {
2161        self.set_style_value(Outline, StyleValue::Val(outline.into()))
2162    }
2163
2164    /// Sets `border_left` and `border_right` to `border`
2165    pub fn border_horiz(self, border: impl Into<StrokeWrap>) -> Self {
2166        let mut current = self.get(BorderProp);
2167        let border = border.into();
2168        current.left = Some(border.clone());
2169        current.right = Some(border);
2170        self.set(BorderProp, current)
2171    }
2172
2173    /// Sets `border_top` and `border_bottom` to `border`
2174    pub fn border_vert(self, border: impl Into<StrokeWrap>) -> Self {
2175        let mut current = self.get(BorderProp);
2176        let border = border.into();
2177        current.top = Some(border.clone());
2178        current.bottom = Some(border);
2179        self.set(BorderProp, current)
2180    }
2181
2182    /// Sets the left padding as a percentage of the parent container width.
2183    pub fn padding_left_pct(self, padding: f64) -> Self {
2184        self.padding_left(padding.pct())
2185    }
2186
2187    /// Sets the right padding as a percentage of the parent container width.
2188    pub fn padding_right_pct(self, padding: f64) -> Self {
2189        self.padding_right(padding.pct())
2190    }
2191
2192    /// Sets the top padding as a percentage of the parent container width.
2193    pub fn padding_top_pct(self, padding: f64) -> Self {
2194        self.padding_top(padding.pct())
2195    }
2196
2197    /// Sets the bottom padding as a percentage of the parent container width.
2198    pub fn padding_bottom_pct(self, padding: f64) -> Self {
2199        self.padding_bottom(padding.pct())
2200    }
2201
2202    /// Set padding on all directions
2203    pub fn padding(self, padding: impl Into<PxPct>) -> Self {
2204        self.set(PaddingProp, Padding::all(padding))
2205    }
2206
2207    /// Sets padding on all sides as a percentage of the parent container width.
2208    pub fn padding_pct(self, padding: f64) -> Self {
2209        self.set(PaddingProp, Padding::all(padding.pct()))
2210    }
2211
2212    /// Sets `padding_left` and `padding_right` to `padding`
2213    pub fn padding_horiz(self, padding: impl Into<PxPct>) -> Self {
2214        let mut current = self.get(PaddingProp);
2215        let padding = padding.into();
2216        current.left = Some(padding);
2217        current.right = Some(padding);
2218        self.set(PaddingProp, current)
2219    }
2220
2221    /// Sets horizontal padding as a percentage of the parent container width.
2222    pub fn padding_horiz_pct(self, padding: f64) -> Self {
2223        self.padding_horiz(padding.pct())
2224    }
2225
2226    /// Sets `padding_top` and `padding_bottom` to `padding`
2227    pub fn padding_vert(self, padding: impl Into<PxPct>) -> Self {
2228        let mut current = self.get(PaddingProp);
2229        let padding = padding.into();
2230        current.top = Some(padding);
2231        current.bottom = Some(padding);
2232        self.set(PaddingProp, current)
2233    }
2234
2235    /// Sets vertical padding as a percentage of the parent container width.
2236    pub fn padding_vert_pct(self, padding: f64) -> Self {
2237        self.padding_vert(padding.pct())
2238    }
2239
2240    /// Sets the left margin as a percentage of the parent container width.
2241    pub fn margin_left_pct(self, margin: f64) -> Self {
2242        self.margin_left(margin.pct())
2243    }
2244
2245    /// Sets the right margin as a percentage of the parent container width.
2246    pub fn margin_right_pct(self, margin: f64) -> Self {
2247        self.margin_right(margin.pct())
2248    }
2249
2250    /// Sets the top margin as a percentage of the parent container width.
2251    pub fn margin_top_pct(self, margin: f64) -> Self {
2252        self.margin_top(margin.pct())
2253    }
2254
2255    /// Sets the bottom margin as a percentage of the parent container width.
2256    pub fn margin_bottom_pct(self, margin: f64) -> Self {
2257        self.margin_bottom(margin.pct())
2258    }
2259
2260    /// Sets margin on all sides of the view.
2261    pub fn margin(self, margin: impl Into<PxPctAuto>) -> Self {
2262        self.set(MarginProp, Margin::all(margin))
2263    }
2264
2265    /// Sets margin on all sides as a percentage of the parent container width.
2266    pub fn margin_pct(self, margin: f64) -> Self {
2267        self.set(MarginProp, Margin::all(margin.pct()))
2268    }
2269
2270    /// Sets `margin_left` and `margin_right` to `margin`
2271    pub fn margin_horiz(self, margin: impl Into<PxPctAuto>) -> Self {
2272        let mut current = self.get(MarginProp);
2273        let margin = margin.into();
2274        current.left = Some(margin);
2275        current.right = Some(margin);
2276        self.set(MarginProp, current)
2277    }
2278
2279    /// Sets horizontal margin as a percentage of the parent container width.
2280    pub fn margin_horiz_pct(self, margin: f64) -> Self {
2281        self.margin_horiz(margin.pct())
2282    }
2283
2284    /// Sets `margin_top` and `margin_bottom` to `margin`
2285    pub fn margin_vert(self, margin: impl Into<PxPctAuto>) -> Self {
2286        let mut current = self.get(MarginProp);
2287        let margin = margin.into();
2288        current.top = Some(margin);
2289        current.bottom = Some(margin);
2290        self.set(MarginProp, current)
2291    }
2292
2293    /// Sets vertical margin as a percentage of the parent container width.
2294    pub fn margin_vert_pct(self, margin: f64) -> Self {
2295        self.margin_vert(margin.pct())
2296    }
2297
2298    /// Sets the left padding of the view.
2299    pub fn padding_left(self, padding: impl Into<PxPct>) -> Self {
2300        let mut current = self.get(PaddingProp);
2301        current.left = Some(padding.into());
2302        self.set(PaddingProp, current)
2303    }
2304    /// Sets the right padding of the view.
2305    pub fn padding_right(self, padding: impl Into<PxPct>) -> Self {
2306        let mut current = self.get(PaddingProp);
2307        current.right = Some(padding.into());
2308        self.set(PaddingProp, current)
2309    }
2310    /// Sets the top padding of the view.
2311    pub fn padding_top(self, padding: impl Into<PxPct>) -> Self {
2312        let mut current = self.get(PaddingProp);
2313        current.top = Some(padding.into());
2314        self.set(PaddingProp, current)
2315    }
2316    /// Sets the bottom padding of the view.
2317    pub fn padding_bottom(self, padding: impl Into<PxPct>) -> Self {
2318        let mut current = self.get(PaddingProp);
2319        current.bottom = Some(padding.into());
2320        self.set(PaddingProp, current)
2321    }
2322
2323    /// Sets the left margin of the view.
2324    pub fn margin_left(self, margin: impl Into<PxPctAuto>) -> Self {
2325        let mut current = self.get(MarginProp);
2326        current.left = Some(margin.into());
2327        self.set(MarginProp, current)
2328    }
2329    /// Sets the right margin of the view.
2330    pub fn margin_right(self, margin: impl Into<PxPctAuto>) -> Self {
2331        let mut current = self.get(MarginProp);
2332        current.right = Some(margin.into());
2333        self.set(MarginProp, current)
2334    }
2335    /// Sets the top margin of the view.
2336    pub fn margin_top(self, margin: impl Into<PxPctAuto>) -> Self {
2337        let mut current = self.get(MarginProp);
2338        current.top = Some(margin.into());
2339        self.set(MarginProp, current)
2340    }
2341    /// Sets the bottom margin of the view.
2342    pub fn margin_bottom(self, margin: impl Into<PxPctAuto>) -> Self {
2343        let mut current = self.get(MarginProp);
2344        current.bottom = Some(margin.into());
2345        self.set(MarginProp, current)
2346    }
2347
2348    /// Applies a complete padding configuration to the view.
2349    pub fn apply_padding(self, padding: Padding) -> Self {
2350        self.set(PaddingProp, padding)
2351    }
2352    /// Applies a complete margin configuration to the view.
2353    pub fn apply_margin(self, margin: Margin) -> Self {
2354        self.set(MarginProp, margin)
2355    }
2356
2357    /// Sets the border radius for all corners of the view.
2358    pub fn border_radius(self, radius: impl Into<PxPct>) -> Self {
2359        self.set(BorderRadiusProp, BorderRadius::all(radius))
2360    }
2361
2362    /// Sets the left border of the view.
2363    pub fn border_left(self, border: impl Into<StrokeWrap>) -> Self {
2364        let mut current = self.get(BorderProp);
2365        current.left = Some(border.into());
2366        self.set(BorderProp, current)
2367    }
2368    /// Sets the right border of the view.
2369    pub fn border_right(self, border: impl Into<StrokeWrap>) -> Self {
2370        let mut current = self.get(BorderProp);
2371        current.right = Some(border.into());
2372        self.set(BorderProp, current)
2373    }
2374    /// Sets the top border of the view.
2375    pub fn border_top(self, border: impl Into<StrokeWrap>) -> Self {
2376        let mut current = self.get(BorderProp);
2377        current.top = Some(border.into());
2378        self.set(BorderProp, current)
2379    }
2380    /// Sets the bottom border of the view.
2381    pub fn border_bottom(self, border: impl Into<StrokeWrap>) -> Self {
2382        let mut current = self.get(BorderProp);
2383        current.bottom = Some(border.into());
2384        self.set(BorderProp, current)
2385    }
2386
2387    /// Sets the left border color of the view.
2388    pub fn border_left_color(self, color: impl Into<Brush>) -> Self {
2389        let mut current = self.get(BorderColorProp);
2390        current.left = Some(color.into());
2391        self.set(BorderColorProp, current)
2392    }
2393    /// Sets the right border color of the view.
2394    pub fn border_right_color(self, color: impl Into<Brush>) -> Self {
2395        let mut current = self.get(BorderColorProp);
2396        current.right = Some(color.into());
2397        self.set(BorderColorProp, current)
2398    }
2399    /// Sets the top border color of the view.
2400    pub fn border_top_color(self, color: impl Into<Brush>) -> Self {
2401        let mut current = self.get(BorderColorProp);
2402        current.top = Some(color.into());
2403        self.set(BorderColorProp, current)
2404    }
2405    /// Sets the bottom border color of the view.
2406    pub fn border_bottom_color(self, color: impl Into<Brush>) -> Self {
2407        let mut current = self.get(BorderColorProp);
2408        current.bottom = Some(color.into());
2409        self.set(BorderColorProp, current)
2410    }
2411
2412    /// Sets the top-left border radius of the view.
2413    pub fn border_top_left_radius(self, radius: impl Into<PxPct>) -> Self {
2414        let mut current = self.get(BorderRadiusProp);
2415        current.top_left = Some(radius.into());
2416        self.set(BorderRadiusProp, current)
2417    }
2418    /// Sets the top-right border radius of the view.
2419    pub fn border_top_right_radius(self, radius: impl Into<PxPct>) -> Self {
2420        let mut current = self.get(BorderRadiusProp);
2421        current.top_right = Some(radius.into());
2422        self.set(BorderRadiusProp, current)
2423    }
2424    /// Sets the bottom-left border radius of the view.
2425    pub fn border_bottom_left_radius(self, radius: impl Into<PxPct>) -> Self {
2426        let mut current = self.get(BorderRadiusProp);
2427        current.bottom_left = Some(radius.into());
2428        self.set(BorderRadiusProp, current)
2429    }
2430    /// Sets the bottom-right border radius of the view.
2431    pub fn border_bottom_right_radius(self, radius: impl Into<PxPct>) -> Self {
2432        let mut current = self.get(BorderRadiusProp);
2433        current.bottom_right = Some(radius.into());
2434        self.set(BorderRadiusProp, current)
2435    }
2436
2437    /// Applies a complete border configuration to the view.
2438    pub fn apply_border(self, border: Border) -> Self {
2439        self.set(BorderProp, border)
2440    }
2441    /// Applies a complete border color configuration to the view.
2442    pub fn apply_border_color(self, border_color: BorderColor) -> Self {
2443        self.set(BorderColorProp, border_color)
2444    }
2445    /// Applies a complete border radius configuration to the view.
2446    pub fn apply_border_radius(self, border_radius: BorderRadius) -> Self {
2447        self.set(BorderRadiusProp, border_radius)
2448    }
2449
2450    /// Sets the left inset as a percentage of the parent container width.
2451    pub fn inset_left_pct(self, inset: f64) -> Self {
2452        self.inset_left(inset.pct())
2453    }
2454
2455    /// Sets the right inset as a percentage of the parent container width.
2456    pub fn inset_right_pct(self, inset: f64) -> Self {
2457        self.inset_right(inset.pct())
2458    }
2459
2460    /// Sets the top inset as a percentage of the parent container height.
2461    pub fn inset_top_pct(self, inset: f64) -> Self {
2462        self.inset_top(inset.pct())
2463    }
2464
2465    /// Sets the bottom inset as a percentage of the parent container height.
2466    pub fn inset_bottom_pct(self, inset: f64) -> Self {
2467        self.inset_bottom(inset.pct())
2468    }
2469
2470    /// Sets all insets (left, top, right, bottom) to the same value.
2471    pub fn inset(self, inset: impl Into<PxPctAuto>) -> Self {
2472        let inset = inset.into();
2473        self.inset_left(inset)
2474            .inset_top(inset)
2475            .inset_right(inset)
2476            .inset_bottom(inset)
2477    }
2478
2479    /// Sets all insets as percentages of the parent container.
2480    pub fn inset_pct(self, inset: f64) -> Self {
2481        let inset = inset.pct();
2482        self.inset_left(inset)
2483            .inset_top(inset)
2484            .inset_right(inset)
2485            .inset_bottom(inset)
2486    }
2487
2488    /// Sets the cursor style when hovering over the view.
2489    pub fn cursor(self, cursor: impl Into<StyleValue<CursorStyle>>) -> Self {
2490        self.set_style_value(Cursor, cursor.into().map(Some))
2491    }
2492
2493    /// Specifies text color for the view.
2494    pub fn color(self, color: impl Into<StyleValue<Color>>) -> Self {
2495        self.set_style_value(TextColor, color.into().map(Some))
2496    }
2497
2498    /// Sets the background color or pattern of the view.
2499    pub fn background(self, color: impl Into<Brush>) -> Self {
2500        let brush = StyleValue::Val(Some(color.into()));
2501        self.set_style_value(Background, brush)
2502    }
2503
2504    /// Specifies shadow blur. The larger this value, the bigger the blur,
2505    /// so the shadow becomes bigger and lighter.
2506    pub fn box_shadow_blur(self, blur_radius: impl Into<PxPct>) -> Self {
2507        let mut value = self.get(BoxShadowProp);
2508        if let Some(v) = value.first_mut() {
2509            v.blur_radius = blur_radius.into();
2510        } else {
2511            value.push(BoxShadow {
2512                blur_radius: blur_radius.into(),
2513                ..Default::default()
2514            });
2515        }
2516        self.set(BoxShadowProp, value)
2517    }
2518
2519    /// Specifies color for the shadow.
2520    pub fn box_shadow_color(self, color: Color) -> Self {
2521        let mut value = self.get(BoxShadowProp);
2522        if let Some(v) = value.first_mut() {
2523            v.color = color;
2524        } else {
2525            value.push(BoxShadow {
2526                color,
2527                ..Default::default()
2528            });
2529        }
2530        self.set(BoxShadowProp, value)
2531    }
2532
2533    /// Specifies shadow blur spread. Positive values will cause the shadow
2534    /// to expand and grow bigger, negative values will cause the shadow to shrink.
2535    pub fn box_shadow_spread(self, spread: impl Into<PxPct>) -> Self {
2536        let mut value = self.get(BoxShadowProp);
2537        if let Some(v) = value.first_mut() {
2538            v.spread = spread.into();
2539        } else {
2540            value.push(BoxShadow {
2541                spread: spread.into(),
2542                ..Default::default()
2543            });
2544        }
2545        self.set(BoxShadowProp, value)
2546    }
2547
2548    /// Applies a shadow for the stylized view. Use [BoxShadow] builder
2549    /// to construct each shadow.
2550    /// ```rust
2551    /// use floem::prelude::*;
2552    /// use floem::prelude::palette::css;
2553    /// use floem::style::BoxShadow;
2554    ///
2555    /// empty().style(|s| s.apply_box_shadows(vec![
2556    ///    BoxShadow::new()
2557    ///        .color(css::BLACK)
2558    ///        .top_offset(5.)
2559    ///        .bottom_offset(-30.)
2560    ///        .right_offset(-20.)
2561    ///        .left_offset(10.)
2562    ///        .blur_radius(5.)
2563    ///        .spread(10.)
2564    /// ]));
2565    /// ```
2566    /// ### Info
2567    /// If you only specify one shadow on the view, use standard style methods directly
2568    /// on [Style] struct:
2569    /// ```rust
2570    /// use floem::prelude::*;
2571    /// empty().style(|s| s
2572    ///     .box_shadow_top_offset(-5.)
2573    ///     .box_shadow_bottom_offset(30.)
2574    ///     .box_shadow_right_offset(20.)
2575    ///     .box_shadow_left_offset(-10.)
2576    ///     .box_shadow_spread(1.)
2577    ///     .box_shadow_blur(3.)
2578    /// );
2579    /// ```
2580    pub fn apply_box_shadows(self, shadow: impl Into<SmallVec<[BoxShadow; 3]>>) -> Self {
2581        self.set(BoxShadowProp, shadow.into())
2582    }
2583
2584    /// Specifies the offset on horizontal axis.
2585    /// Negative offset value places the shadow to the left of the view.
2586    pub fn box_shadow_h_offset(self, h_offset: impl Into<PxPct>) -> Self {
2587        let mut value = self.get(BoxShadowProp);
2588        let offset = h_offset.into();
2589        if let Some(v) = value.first_mut() {
2590            v.left_offset = -offset;
2591            v.right_offset = offset;
2592        } else {
2593            value.push(BoxShadow {
2594                left_offset: -offset,
2595                right_offset: offset,
2596                ..Default::default()
2597            });
2598        }
2599        self.set(BoxShadowProp, value)
2600    }
2601
2602    /// Specifies the offset on vertical axis.
2603    /// Negative offset value places the shadow above the view.
2604    pub fn box_shadow_v_offset(self, v_offset: impl Into<PxPct>) -> Self {
2605        let mut value = self.get(BoxShadowProp);
2606        let offset = v_offset.into();
2607        if let Some(v) = value.first_mut() {
2608            v.top_offset = -offset;
2609            v.bottom_offset = offset;
2610        } else {
2611            value.push(BoxShadow {
2612                top_offset: -offset,
2613                bottom_offset: offset,
2614                ..Default::default()
2615            });
2616        }
2617        self.set(BoxShadowProp, value)
2618    }
2619
2620    /// Specifies the offset of the left edge.
2621    pub fn box_shadow_left_offset(self, left_offset: impl Into<PxPct>) -> Self {
2622        let mut value = self.get(BoxShadowProp);
2623        if let Some(v) = value.first_mut() {
2624            v.left_offset = left_offset.into();
2625        } else {
2626            value.push(BoxShadow {
2627                left_offset: left_offset.into(),
2628                ..Default::default()
2629            });
2630        }
2631        self.set(BoxShadowProp, value)
2632    }
2633
2634    /// Specifies the offset of the right edge.
2635    pub fn box_shadow_right_offset(self, right_offset: impl Into<PxPct>) -> Self {
2636        let mut value = self.get(BoxShadowProp);
2637        if let Some(v) = value.first_mut() {
2638            v.right_offset = right_offset.into();
2639        } else {
2640            value.push(BoxShadow {
2641                right_offset: right_offset.into(),
2642                ..Default::default()
2643            });
2644        }
2645        self.set(BoxShadowProp, value)
2646    }
2647
2648    /// Specifies the offset of the top edge.
2649    pub fn box_shadow_top_offset(self, top_offset: impl Into<PxPct>) -> Self {
2650        let mut value = self.get(BoxShadowProp);
2651        if let Some(v) = value.first_mut() {
2652            v.top_offset = top_offset.into();
2653        } else {
2654            value.push(BoxShadow {
2655                top_offset: top_offset.into(),
2656                ..Default::default()
2657            });
2658        }
2659        self.set(BoxShadowProp, value)
2660    }
2661
2662    /// Specifies the offset of the bottom edge.
2663    pub fn box_shadow_bottom_offset(self, bottom_offset: impl Into<PxPct>) -> Self {
2664        let mut value = self.get(BoxShadowProp);
2665        if let Some(v) = value.first_mut() {
2666            v.bottom_offset = bottom_offset.into();
2667        } else {
2668            value.push(BoxShadow {
2669                bottom_offset: bottom_offset.into(),
2670                ..Default::default()
2671            });
2672        }
2673        self.set(BoxShadowProp, value)
2674    }
2675
2676    /// Sets the font size for text content.
2677    pub fn font_size(self, size: impl Into<Px>) -> Self {
2678        let px = size.into();
2679        self.set_style_value(FontSize, StyleValue::Val(Some(px.0 as f32)))
2680    }
2681
2682    /// Sets the font family for text content.
2683    pub fn font_family(self, family: impl Into<StyleValue<String>>) -> Self {
2684        self.set_style_value(FontFamily, family.into().map(Some))
2685    }
2686
2687    /// Sets the font weight (boldness) for text content.
2688    pub fn font_weight(self, weight: impl Into<StyleValue<Weight>>) -> Self {
2689        self.set_style_value(FontWeight, weight.into().map(Some))
2690    }
2691
2692    /// Sets the font weight to bold.
2693    pub fn font_bold(self) -> Self {
2694        self.font_weight(Weight::BOLD)
2695    }
2696
2697    /// Sets the font style (italic, normal) for text content.
2698    pub fn font_style(self, style: impl Into<StyleValue<crate::text::Style>>) -> Self {
2699        self.set_style_value(FontStyle, style.into().map(Some))
2700    }
2701
2702    /// Sets the color of the text cursor.
2703    pub fn cursor_color(self, color: impl Into<Brush>) -> Self {
2704        let brush = StyleValue::Val(color.into());
2705        self.set_style_value(CursorColor, brush)
2706    }
2707
2708    /// Sets the line height for text content.
2709    pub fn line_height(self, normal: f32) -> Self {
2710        self.set(LineHeight, Some(LineHeightValue::Normal(normal)))
2711    }
2712
2713    /// Enables pointer events for the view (allows mouse interaction).
2714    pub fn pointer_events_auto(self) -> Self {
2715        self.pointer_events(PointerEvents::Auto)
2716    }
2717
2718    /// Disables pointer events for the view (mouse events pass through).
2719    pub fn pointer_events_none(self) -> Self {
2720        self.pointer_events(PointerEvents::None)
2721    }
2722
2723    /// Sets text overflow to show ellipsis (...) when text is clipped.
2724    pub fn text_ellipsis(self) -> Self {
2725        self.text_overflow(TextOverflow::Ellipsis)
2726    }
2727
2728    /// Sets text overflow to clip text without showing ellipsis.
2729    pub fn text_clip(self) -> Self {
2730        self.text_overflow(TextOverflow::Clip)
2731    }
2732
2733    /// Sets the view to absolute positioning.
2734    pub fn absolute(self) -> Self {
2735        self.position(taffy::style::Position::Absolute)
2736    }
2737
2738    /// Sets the view to fixed positioning relative to the viewport.
2739    ///
2740    /// This is similar to CSS `position: fixed`. The view will:
2741    /// - Be positioned relative to the window viewport
2742    /// - Use `inset` properties relative to the viewport
2743    /// - Have percentage sizes relative to the viewport
2744    /// - Be painted above all other content
2745    ///
2746    /// # Example
2747    /// ```rust
2748    /// use floem::style::Style;
2749    ///
2750    /// // Create a full-screen overlay
2751    /// Style::new().fixed().inset(0.0);
2752    /// ```
2753    pub fn fixed(self) -> Self {
2754        self.position(taffy::style::Position::Absolute)
2755            .is_fixed(true)
2756    }
2757
2758    /// Aligns flex items to stretch and fill the cross axis.
2759    pub fn items_stretch(self) -> Self {
2760        self.align_items(Some(taffy::style::AlignItems::Stretch))
2761    }
2762
2763    /// Aligns flex items to the start of the cross axis.
2764    pub fn items_start(self) -> Self {
2765        self.align_items(Some(taffy::style::AlignItems::FlexStart))
2766    }
2767
2768    /// Defines the alignment along the cross axis as Centered
2769    pub fn items_center(self) -> Self {
2770        self.align_items(Some(taffy::style::AlignItems::Center))
2771    }
2772
2773    /// Aligns flex items to the end of the cross axis.
2774    pub fn items_end(self) -> Self {
2775        self.align_items(Some(taffy::style::AlignItems::FlexEnd))
2776    }
2777
2778    /// Aligns flex items along their baselines.
2779    pub fn items_baseline(self) -> Self {
2780        self.align_items(Some(taffy::style::AlignItems::Baseline))
2781    }
2782
2783    /// Aligns flex items to the start of the main axis.
2784    pub fn justify_start(self) -> Self {
2785        self.justify_content(Some(taffy::style::JustifyContent::FlexStart))
2786    }
2787
2788    /// Aligns flex items to the end of the main axis.
2789    pub fn justify_end(self) -> Self {
2790        self.justify_content(Some(taffy::style::JustifyContent::FlexEnd))
2791    }
2792
2793    /// Defines the alignment along the main axis as Centered
2794    pub fn justify_center(self) -> Self {
2795        self.justify_content(Some(taffy::style::JustifyContent::Center))
2796    }
2797
2798    /// Distributes flex items with space between them.
2799    pub fn justify_between(self) -> Self {
2800        self.justify_content(Some(taffy::style::JustifyContent::SpaceBetween))
2801    }
2802
2803    /// Distributes flex items with space around them.
2804    pub fn justify_around(self) -> Self {
2805        self.justify_content(Some(taffy::style::JustifyContent::SpaceAround))
2806    }
2807
2808    /// Distributes flex items with equal space around them.
2809    pub fn justify_evenly(self) -> Self {
2810        self.justify_content(Some(taffy::style::JustifyContent::SpaceEvenly))
2811    }
2812
2813    /// Hides the view from view and layout.
2814    pub fn hide(self) -> Self {
2815        self.set(DisplayProp, Display::None)
2816    }
2817
2818    /// Sets the view to use flexbox layout.
2819    pub fn flex(self) -> Self {
2820        self.display(taffy::style::Display::Flex)
2821    }
2822
2823    /// Sets the view to use grid layout.
2824    pub fn grid(self) -> Self {
2825        self.display(taffy::style::Display::Grid)
2826    }
2827
2828    /// Sets flex direction to row (horizontal).
2829    pub fn flex_row(self) -> Self {
2830        self.flex_direction(taffy::style::FlexDirection::Row)
2831    }
2832
2833    /// Sets flex direction to column (vertical).
2834    pub fn flex_col(self) -> Self {
2835        self.flex_direction(taffy::style::FlexDirection::Column)
2836    }
2837
2838    /// Sets the stack order of the view.
2839    pub fn z_index(self, z_index: i32) -> Self {
2840        self.set(ZIndex, Some(z_index))
2841    }
2842
2843    /// Sets uniform scaling for both X and Y axes.
2844    pub fn scale(self, scale: impl Into<Pct>) -> Self {
2845        let val = scale.into();
2846        self.scale_x(val).scale_y(val)
2847    }
2848
2849    /// Allow the application of a function if the option exists.
2850    /// This is useful for chaining together a bunch of optional style changes.
2851    /// ```rust
2852    /// use floem::style::Style;
2853    /// let maybe_none: Option<i32> = None;
2854    /// let style = Style::default()
2855    ///     .apply_opt(Some(5.0), Style::padding) // ran
2856    ///     .apply_opt(maybe_none, Style::margin) // not ran
2857    ///     .apply_opt(Some(5.0), |s, v| s.border_right(v * 2.0))
2858    ///     .border_left(5.0); // ran, obviously
2859    /// ```
2860    pub fn apply_opt<T>(self, opt: Option<T>, f: impl FnOnce(Self, T) -> Self) -> Self {
2861        if let Some(t) = opt { f(self, t) } else { self }
2862    }
2863
2864    /// Allow the application of a function if the condition holds.
2865    /// This is useful for chaining together optional style changes.
2866    /// ```rust
2867    /// use floem::style::Style;
2868    /// let style = Style::default()
2869    ///     .apply_if(true, |s| s.padding(5.0)) // ran
2870    ///     .apply_if(false, |s| s.margin(5.0)); // not ran
2871    /// ```
2872    pub fn apply_if(self, cond: bool, f: impl FnOnce(Self) -> Self) -> Self {
2873        if cond { f(self) } else { self }
2874    }
2875
2876    /// Applies a `CustomStyle` type into this style.
2877    ///
2878    /// # Examples
2879    /// ```
2880    /// use floem::prelude::*;
2881    /// text("test").style(|s| s.custom(|s: LabelCustomStyle| s.selectable(false)));
2882    /// ```
2883    ///
2884    /// See also: [`apply_custom`](Self::apply_custom), [`custom_style_class`](Self::custom_style_class)
2885    pub fn custom<CS: CustomStyle>(self, custom: impl FnOnce(CS) -> CS) -> Self {
2886        self.apply(custom(CS::default()).into())
2887    }
2888
2889    /// Applies a `CustomStyle` type into this style.
2890    ///
2891    /// # Examples
2892    /// ```
2893    /// use floem::prelude::*;
2894    /// text("test").style(|s| s.apply_custom(LabelCustomStyle::new().selectable(false)));
2895    /// ```
2896    ///
2897    /// See also: [`custom`](Self::custom), [`custom_style_class`](Self::custom_style_class)
2898    pub fn apply_custom<CS: Into<Style>>(self, custom_style: CS) -> Self {
2899        self.apply(custom_style.into())
2900    }
2901}
2902
2903impl Style {
2904    pub fn to_taffy_style(&self) -> TaffyStyle {
2905        let style = self.builtin();
2906        TaffyStyle {
2907            display: style.display(),
2908            overflow: taffy::Point {
2909                x: self.get(OverflowX),
2910                y: self.get(OverflowY),
2911            },
2912            position: style.position(),
2913            size: taffy::prelude::Size {
2914                width: style.width().into(),
2915                height: style.height().into(),
2916            },
2917            min_size: taffy::prelude::Size {
2918                width: style.min_width().into(),
2919                height: style.min_height().into(),
2920            },
2921            max_size: taffy::prelude::Size {
2922                width: style.max_width().into(),
2923                height: style.max_height().into(),
2924            },
2925            flex_direction: style.flex_direction(),
2926            flex_grow: style.flex_grow(),
2927            flex_shrink: style.flex_shrink(),
2928            flex_basis: style.flex_basis().into(),
2929            flex_wrap: style.flex_wrap(),
2930            justify_content: style.justify_content(),
2931            justify_self: style.justify_self(),
2932            justify_items: style.justify_items(),
2933            align_items: style.align_items(),
2934            align_content: style.align_content(),
2935            align_self: style.align_self(),
2936            aspect_ratio: style.aspect_ratio(),
2937            border: {
2938                let border = style.style.get(BorderProp);
2939                Rect {
2940                    left: LengthPercentage::length(border.left.map_or(0.0, |b| b.0.width) as f32),
2941                    top: LengthPercentage::length(border.top.map_or(0.0, |b| b.0.width) as f32),
2942                    right: LengthPercentage::length(border.right.map_or(0.0, |b| b.0.width) as f32),
2943                    bottom: LengthPercentage::length(
2944                        border.bottom.map_or(0.0, |b| b.0.width) as f32
2945                    ),
2946                }
2947            },
2948            padding: {
2949                let padding = style.style.get(PaddingProp);
2950                Rect {
2951                    left: padding.left.unwrap_or(PxPct::Px(0.0)).into(),
2952                    top: padding.top.unwrap_or(PxPct::Px(0.0)).into(),
2953                    right: padding.right.unwrap_or(PxPct::Px(0.0)).into(),
2954                    bottom: padding.bottom.unwrap_or(PxPct::Px(0.0)).into(),
2955                }
2956            },
2957            margin: {
2958                let margin = style.style.get(MarginProp);
2959                Rect {
2960                    left: margin.left.unwrap_or(PxPctAuto::Px(0.0)).into(),
2961                    top: margin.top.unwrap_or(PxPctAuto::Px(0.0)).into(),
2962                    right: margin.right.unwrap_or(PxPctAuto::Px(0.0)).into(),
2963                    bottom: margin.bottom.unwrap_or(PxPctAuto::Px(0.0)).into(),
2964                }
2965            },
2966            inset: Rect {
2967                left: style.inset_left().into(),
2968                top: style.inset_top().into(),
2969                right: style.inset_right().into(),
2970                bottom: style.inset_bottom().into(),
2971            },
2972            gap: Size {
2973                width: style.col_gap().into(),
2974                height: style.row_gap().into(),
2975            },
2976            grid_template_rows: style.grid_template_rows(),
2977            grid_template_columns: style.grid_template_columns(),
2978            grid_row: style.grid_row(),
2979            grid_column: style.grid_column(),
2980            grid_auto_rows: style.grid_auto_rows(),
2981            grid_auto_columns: style.grid_auto_columns(),
2982            grid_auto_flow: style.grid_auto_flow(),
2983            ..Default::default()
2984        }
2985    }
2986}