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//!
23//! Transitions and context mappings don't hold any extra information, they are just used to know how to downcast the `Rc<dyn Any>`.
24//!
25//! [StyleSelectors] is a bit mask of which selectors are active.
26//!
27//! [StyleClassInfo] holds a function pointer that returns the name of the class as a String.
28//! The function pointer is basically used as a vtable for the class.
29//! If classes needed more methods other than `name`, those methods would be added to `StyleClassInfo`.
30//!
31//! [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.
32//!
33//! Props store props.
34//! Transitions store transition values.
35//! Classes, context mappings, and selectors store nested [Style] maps.
36//!
37//! ## Applying `Style`s to `View`s
38//!
39//! A style can be applied to a view in two different ways.
40//! 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.
41//!
42//! 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.
43//!
44//! 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.
45//! 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.
46//!
47//! `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.
48//! 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.
49//!
50//! ## Style Resolution
51//!
52//! A final `computed_style` is resolved in the `style_pass` of the `View` trait.
53//!
54//! ### Context
55//!
56//! It first received a `Style` map that is used as context.
57//! The context is passed down the view tree and carries the inherited properties that were applied to any parent.
58//! Inherited properties include all classes and any prop that has been marked as `inherited`.
59//!
60//! ### View Style
61//!
62//! The `style` first gets the `Style` (if any) from the `view_style` method.
63//!
64//! ### Style
65//!
66//! Then it gets the style from any calls to `style` from the decorators trait.
67//! It starts with the first index in the style `Stack` and applies each successive `Style` over the combination of any previous ones.
68//!
69//! Then the style from the `Decorators` / `ViewState` is applied over (overriding any matching props) the style from `view_style`.
70//!
71//!
72//! ### Nested map resolution
73//!
74//! Then any classes that have been applied to the view, and the active selector set are used to resolve nested maps.
75//!
76//! Nested maps such as classes and selectors are recursively applied, breadth first. So, deeper / more nested style maps take precendence.
77//!
78//! This style map is the combined style of the `View`.
79//!
80//! ### Updated context
81//!
82//! 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.
83//!
84//! ## Prop Extraction
85//!
86//! The final computed style of a view will be passed to the `style_pass` method from the `View` trait.
87//!
88//! Views will store fields that are struct that are prop extractors.
89//! These structs are created using the `prop_extractor!` macro.
90//!
91//! 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.
92//!
93//! 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.
94//!
95//! ### Transition interpolation
96//!
97//! 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.
98//!
99//!
100//! ## Custom Style Props, Classes, and Extractors.
101//!
102//!
103//! You can create custom style props with the [prop!] macro, classes with the [style_class!] macro, and extractors with the [prop_extractor!] macro.
104//!
105//!
106//! ### Custom Props
107//!
108//! You can create custom props.
109//!
110//! Doing this allows you to store arbitrary values in the style system.
111//!
112//! You can use these to style the view, change it's behavior, update it's state, or anything else.
113//!
114//! By implementing the [StylePropValue] trait for your prop (which you must do) you can
115//!
116//! - optionally set how the prop should be interpolated (allowing you to customize what interpolating means in the context of your prop)
117//!
118//! - 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.)
119//!
120//! - 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.
121//!
122//! ### Custom Classes
123//!
124//! 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.
125//!
126//! ### Custom Extractors
127//!
128//! 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`.
129
130use floem_renderer::text::FontWeight as FontWeightProp;
131use peniko::color::palette;
132use peniko::kurbo::{self, Affine, RoundedRect, Stroke, Vec2};
133use peniko::{Brush, Color};
134use rustc_hash::FxHashMap;
135use smallvec::SmallVec;
136use std::any::Any;
137use std::collections::HashMap;
138use std::fmt::{self, Debug};
139use std::marker::PhantomData;
140use std::rc::Rc;
141use std::sync::atomic::{AtomicU64, Ordering};
142use taffy::GridTemplateComponent;
143
144pub use taffy::style::{
145    AlignContent, AlignItems, BoxSizing, Dimension, Display, FlexDirection, FlexWrap,
146    JustifyContent, JustifyItems, Position,
147};
148use taffy::{
149    geometry::{MinMax, Size},
150    prelude::{GridPlacement, Line, Rect},
151    style::{
152        LengthPercentage, MaxTrackSizingFunction, MinTrackSizingFunction, Overflow,
153        Style as TaffyStyle,
154    },
155};
156
157use crate::layout::responsive::{GridBreakpoints, ScreenSize, ScreenSizeBp};
158
159use crate::style::components::Focus;
160use crate::text::{OverflowWrap, WordBreakStrength};
161use crate::views::editor::SelectionColor;
162// Import macros from crate root (they are #[macro_export] in props.rs)
163use crate::{prop, prop_extractor};
164
165mod cache;
166mod components;
167mod custom;
168mod cx;
169mod props;
170pub mod recalc;
171mod selectors;
172#[cfg(test)]
173mod tests;
174pub mod theme;
175mod transition;
176pub mod unit;
177mod values;
178
179pub use components::{
180    Border, BorderColor, BorderRadius, BoxShadow, CursorStyle, Margin, NoWrapOverflow, Padding,
181    PointerEvents, TextOverflow,
182};
183pub use custom::{CustomStylable, CustomStyle};
184pub use cx::{InheritedInteractionCx, InteractionState, StyleCx};
185pub use props::{
186    ExtractorField, StyleClass, StyleClassInfo, StyleClassRef, StyleDebugGroup,
187    StyleDebugGroupInfo, StyleDebugGroupRef, StyleKey, StyleKeyInfo, StyleProp, StylePropInfo,
188    StylePropReader, StylePropRef,
189};
190pub use selectors::{NthChild, StructuralSelector, StyleSelector, StyleSelectors};
191pub use theme::{DesignSystem, StyleThemeExt};
192pub use transition::{DirectTransition, Transition, TransitionState};
193pub use unit::{
194    AnchorAbout, Angle, Auto, DurationUnitExt, Em, FontSizeCx, Length, LengthAuto, Lh,
195    LineHeightValue, Pct, Pt, UnitExt,
196};
197pub use values::{
198    ContextValue, ObjectFit, ObjectPosition, StrokeWrap, StyleMapValue, StylePropValue, StyleValue,
199};
200
201pub use cache::{StyleCache, StyleCacheKey};
202
203pub(crate) use props::{RESPONSIVE_SELECTORS_INFO, STRUCTURAL_SELECTORS_INFO, style_key_selector};
204
205static NEXT_STYLE_MERGE_ID: AtomicU64 = AtomicU64::new(1);
206const MERGE_MIX_CONST: u64 = 0x9E3779B97F4A7C15;
207static DEFERRED_EFFECTS_INFO: StyleKeyInfo = StyleKeyInfo::DeferredEffects;
208const DEFERRED_EFFECTS_KEY: StyleKey = StyleKey {
209    info: &DEFERRED_EFFECTS_INFO,
210};
211
212fn next_style_merge_id() -> u64 {
213    NEXT_STYLE_MERGE_ID.fetch_add(1, Ordering::Relaxed)
214}
215
216fn combine_merge_ids(a: u64, b: u64) -> u64 {
217    a.rotate_left(13) ^ b.wrapping_mul(MERGE_MIX_CONST)
218}
219
220type StructuralSelectorRules = SmallVec<[(StructuralSelector, Rc<Style>); 2]>;
221type ResponsiveSelectorRules = SmallVec<[(ResponsiveSelector, Rc<Style>); 2]>;
222
223#[derive(Clone)]
224pub(crate) struct DeferredStyleEffect {
225    run: Rc<dyn Fn(&Style)>,
226}
227
228impl DeferredStyleEffect {
229    fn new(run: impl Fn(&Style) + 'static) -> Self {
230        Self { run: Rc::new(run) }
231    }
232
233    fn run(&self, style: &Style) {
234        (self.run)(style);
235    }
236}
237
238#[derive(Clone, Default, Debug)]
239pub struct ExprStyle {
240    style: Style,
241}
242
243impl From<Style> for ExprStyle {
244    fn from(style: Style) -> Self {
245        Self { style }
246    }
247}
248
249impl From<ExprStyle> for Style {
250    fn from(style: ExprStyle) -> Self {
251        style.style
252    }
253}
254
255#[derive(Debug, Clone, Copy, Default)]
256pub struct ContextRef<P: StyleProp> {
257    _marker: PhantomData<P>,
258}
259
260impl<P: StyleProp> ContextRef<P> {
261    /// Create a deferred value from the current context prop.
262    ///
263    /// This is for producing another style value from context, such as
264    /// `font_size(fs.def(|fs| fs * 0.85))` or `background(t.def(|t| t.bg_base()))`.
265    ///
266    /// You can read `def` either as "function definition" or as shorthand for
267    /// "defer": it packages a closure that will be evaluated later when the
268    /// resulting value is needed.
269    ///
270    /// The closure is evaluated only if the property using the returned [`ContextValue`]
271    /// is actually read later. It does not run eagerly during `resolve_nested_maps`, so
272    /// it should not be used for side effects that must always happen during style
273    /// resolution. Use [`Style::defer`] for that.
274    pub fn def<T>(self, f: impl Fn(P::Type) -> T + 'static) -> ContextValue<T>
275    where
276        P::Type: 'static,
277        T: 'static,
278    {
279        ContextValue::new(move |style| {
280            let value = style.get_prop::<P>().unwrap_or_else(P::default_value);
281            f(value)
282        })
283    }
284
285    pub fn map<T>(self, f: impl Fn(P::Type) -> T + 'static) -> ContextValue<T>
286    where
287        P::Type: 'static,
288        T: 'static,
289    {
290        self.def(f)
291    }
292
293    pub(crate) fn into_deferred(self, f: impl Fn(P::Type) + 'static) -> DeferredStyleEffect
294    where
295        P::Type: 'static,
296    {
297        let effect = self.def(move |value| {
298            f(value);
299        });
300        DeferredStyleEffect::new(move |style| {
301            effect.resolve(style);
302        })
303    }
304}
305
306impl ExprStyle {
307    pub fn new() -> Self {
308        Self {
309            style: Style::new(),
310        }
311    }
312
313    pub fn build(self) -> Style {
314        self.style
315    }
316
317    pub fn map(self, over: impl FnOnce(Self) -> Self) -> Self {
318        over(self)
319    }
320
321    fn merge(mut self, over: Style) -> Self {
322        self.style.apply_mut(&over);
323        self
324    }
325
326    pub fn apply_if(self, cond: bool, f: impl FnOnce(Self) -> Self) -> Self {
327        if cond { f(self) } else { self }
328    }
329
330    pub fn set<P: StyleProp>(mut self, prop: P, value: impl Into<StyleValue<P::Type>>) -> Self {
331        self.style = self.style.set_style_value(prop, value.into());
332        self
333    }
334
335    pub fn set_context<P: StyleProp>(mut self, prop: P, value: ContextValue<P::Type>) -> Self {
336        self.style = self.style.set_context(prop, value);
337        self
338    }
339
340    pub fn set_from_context<C: StyleProp, P: StyleProp>(
341        mut self,
342        prop: P,
343        context: ContextRef<C>,
344        f: impl Fn(C::Type) -> P::Type + 'static,
345    ) -> Self
346    where
347        C::Type: 'static,
348        P::Type: 'static,
349    {
350        self.style = self.style.set_from_context(prop, context, f);
351        self
352    }
353
354    pub fn set_context_opt<P: StyleProp<Type = Option<T>>, T: 'static>(
355        mut self,
356        prop: P,
357        value: ContextValue<Option<T>>,
358    ) -> Self {
359        self.style = self.style.set_context_opt(prop, value);
360        self
361    }
362
363    pub fn set_from_context_opt<C: StyleProp, P: StyleProp<Type = Option<T>>, T: 'static>(
364        mut self,
365        prop: P,
366        context: ContextRef<C>,
367        f: impl Fn(C::Type) -> Option<T> + 'static,
368    ) -> Self
369    where
370        C::Type: 'static,
371    {
372        self.style = self.style.set_from_context_opt(prop, context, f);
373        self
374    }
375
376    pub fn with<P: StyleProp>(self, f: impl FnOnce(ExprStyle, ContextRef<P>) -> ExprStyle) -> Self {
377        f(self, ContextRef::default())
378    }
379
380    pub fn hover(self, style: impl FnOnce(ExprStyle) -> ExprStyle) -> Self {
381        self.merge(Style::default().hover(|s| style(s.into()).into()))
382    }
383
384    pub fn focus(self, style: impl FnOnce(ExprStyle) -> ExprStyle) -> Self {
385        self.merge(Style::default().focus(|s| style(s.into()).into()))
386    }
387
388    pub fn focus_visible(self, style: impl FnOnce(ExprStyle) -> ExprStyle) -> Self {
389        self.merge(Style::default().focus_visible(|s| style(s.into()).into()))
390    }
391
392    pub fn focus_within(self, style: impl FnOnce(ExprStyle) -> ExprStyle) -> Self {
393        self.merge(Style::default().focus_within(|s| style(s.into()).into()))
394    }
395
396    pub fn active(self, style: impl FnOnce(ExprStyle) -> ExprStyle) -> Self {
397        self.merge(Style::default().active(|s| style(s.into()).into()))
398    }
399
400    pub fn disabled(self, style: impl FnOnce(ExprStyle) -> ExprStyle) -> Self {
401        self.merge(Style::default().disabled(|s| style(s.into()).into()))
402    }
403
404    pub fn class<C: StyleClass>(
405        self,
406        class: C,
407        style: impl FnOnce(ExprStyle) -> ExprStyle,
408    ) -> Self {
409        self.merge(Style::default().class(class, |s| style(s.into()).into()))
410    }
411
412    /// Sets the font size for text content.
413    pub fn font_size<T>(self, size: ContextValue<T>) -> Self
414    where
415        T: Into<Pt> + 'static,
416    {
417        let px = size.map(|s| s.into().0);
418        self.set_context(FontSize, px)
419    }
420
421    pub fn size<W, H>(self, width: ContextValue<W>, height: ContextValue<H>) -> Self
422    where
423        W: Into<LengthAuto> + 'static,
424        H: Into<LengthAuto> + 'static,
425    {
426        self.width(width).height(height)
427    }
428
429    pub fn absolute(self) -> Self {
430        self.set(PositionProp, Position::Absolute)
431    }
432
433    pub fn flex_row(self) -> Self {
434        self.set(FlexDirectionProp, FlexDirection::Row)
435    }
436
437    pub fn margin<T>(self, margin: ContextValue<T>) -> Self
438    where
439        T: Into<LengthAuto> + 'static,
440    {
441        let margin = margin.map(Into::into);
442        self.set(MarginLeft, margin.clone())
443            .set(MarginTop, margin.clone())
444            .set(MarginRight, margin.clone())
445            .set(MarginBottom, margin)
446    }
447
448    pub fn border_color<T>(self, color: ContextValue<T>) -> Self
449    where
450        T: Into<Brush> + 'static,
451    {
452        let color = color.map(|color| Some(color.into()));
453        self.set(BorderLeftColor, color.clone())
454            .set(BorderTopColor, color.clone())
455            .set(BorderRightColor, color.clone())
456            .set(BorderBottomColor, color)
457    }
458
459    pub fn border_radius<T>(self, radius: ContextValue<T>) -> Self
460    where
461        T: Into<Length> + 'static,
462    {
463        let radius = radius.map(Into::into);
464        self.set(BorderTopLeftRadius, radius.clone())
465            .set(BorderTopRightRadius, radius.clone())
466            .set(BorderBottomLeftRadius, radius.clone())
467            .set(BorderBottomRightRadius, radius)
468    }
469
470    pub fn border<T>(self, width: ContextValue<T>) -> Self
471    where
472        T: Into<Pt> + 'static,
473    {
474        let stroke = width.map(|width| Stroke::new(width.into().0));
475        self.set(BorderLeft, stroke.clone())
476            .set(BorderTop, stroke.clone())
477            .set(BorderRight, stroke.clone())
478            .set(BorderBottom, stroke)
479    }
480
481    pub fn border_top<T>(self, width: ContextValue<T>) -> Self
482    where
483        T: Into<Pt> + 'static,
484    {
485        self.set(BorderTop, width.map(|width| Stroke::new(width.into().0)))
486    }
487
488    pub fn items_center(self) -> Self {
489        self.set(AlignItemsProp, Some(AlignItems::Center))
490    }
491
492    pub fn justify_center(self) -> Self {
493        self.set(
494            JustifyContentProp,
495            Some(taffy::style::JustifyContent::Center),
496        )
497    }
498
499    pub fn selected(self, style: impl FnOnce(ExprStyle) -> ExprStyle) -> Self {
500        self.merge(Style::default().selected(|s| style(s.into()).into()))
501    }
502
503    pub fn drag(self, style: impl FnOnce(ExprStyle) -> ExprStyle) -> Self {
504        self.merge(Style::default().drag(|s| style(s.into()).into()))
505    }
506
507    pub fn file_hover(self, style: impl FnOnce(ExprStyle) -> ExprStyle) -> Self {
508        self.merge(Style::default().file_hover(|s| style(s.into()).into()))
509    }
510
511    pub fn padding<T>(self, padding: ContextValue<T>) -> Self
512    where
513        T: Into<Length> + 'static,
514    {
515        let padding = padding.map(Into::into);
516        self.set(PaddingLeft, padding.clone())
517            .set(PaddingTop, padding.clone())
518            .set(PaddingRight, padding.clone())
519            .set(PaddingBottom, padding)
520    }
521
522    pub fn padding_horiz<T>(self, padding: ContextValue<T>) -> Self
523    where
524        T: Into<Length> + 'static,
525    {
526        let padding = padding.map(Into::into);
527        self.set(PaddingLeft, padding.clone())
528            .set(PaddingRight, padding)
529    }
530
531    pub fn padding_vert<T>(self, padding: ContextValue<T>) -> Self
532    where
533        T: Into<Length> + 'static,
534    {
535        let padding = padding.map(Into::into);
536        self.set(PaddingTop, padding.clone())
537            .set(PaddingBottom, padding)
538    }
539
540    pub fn gap<T>(self, gap: ContextValue<T>) -> Self
541    where
542        T: Into<Length> + 'static,
543    {
544        let gap = gap.map(Into::into);
545        self.set(ColGap, gap.clone()).set(RowGap, gap)
546    }
547
548    pub fn custom<CS>(self, custom: impl FnOnce(CS) -> CS) -> Self
549    where
550        CS: Default + Clone + Into<Style> + From<Style>,
551    {
552        self.merge(custom(CS::default()).into())
553    }
554
555    pub fn apply_border_radius(self, border_radius: BorderRadius) -> Self {
556        self.merge(Style::new().apply_border_radius(border_radius))
557    }
558
559    pub fn apply_box_shadows(self, shadow: impl Into<SmallVec<[BoxShadow; 3]>>) -> Self {
560        self.set(BoxShadowProp, shadow.into())
561    }
562
563    pub fn transition_background(self, transition: Transition) -> Self {
564        self.merge(Style::new().transition_background(transition))
565    }
566
567    pub fn border_bottom(self, width: impl Into<Pt>) -> Self {
568        self.set(BorderBottom, Stroke::new(width.into().0))
569    }
570
571    pub fn outline(self, width: impl Into<Pt>) -> Self {
572        self.set(Outline, Stroke::new(width.into().0))
573    }
574}
575
576fn fx_hash_map_with_capacity<K, V>(capacity: usize) -> FxHashMap<K, V> {
577    FxHashMap::with_capacity_and_hasher(capacity, Default::default())
578}
579
580fn take_any<T: Any + Clone>(value: Rc<dyn Any>) -> T {
581    Rc::downcast::<T>(value)
582        .map(|rc| Rc::try_unwrap(rc).unwrap_or_else(|rc| (*rc).clone()))
583        .unwrap_or_else(|_| panic!("unexpected style map payload type"))
584}
585
586#[derive(Clone)]
587struct StructuralSelectors(StructuralSelectorRules);
588
589#[derive(Clone)]
590struct ResponsiveSelectors(ResponsiveSelectorRules);
591
592#[derive(Clone, Copy, Debug, PartialEq)]
593enum ResponsiveSelector {
594    ScreenSize(ScreenSize),
595    MinWidth(Pt),
596    MaxWidth(Pt),
597    WidthRange { min: Pt, max: Pt },
598}
599
600impl ResponsiveSelector {
601    fn matches(&self, width: f64) -> bool {
602        match self {
603            ResponsiveSelector::ScreenSize(size) => {
604                let bp = GridBreakpoints::default().get_width_bp(width);
605                size.breakpoints().contains(&bp)
606            }
607            ResponsiveSelector::MinWidth(min) => width >= min.0,
608            ResponsiveSelector::MaxWidth(max) => width <= max.0,
609            ResponsiveSelector::WidthRange { min, max } => width >= min.0 && width <= max.0,
610        }
611    }
612}
613
614style_key_selector!(selector_xs, StyleSelectors::empty().responsive());
615style_key_selector!(selector_sm, StyleSelectors::empty().responsive());
616style_key_selector!(selector_md, StyleSelectors::empty().responsive());
617style_key_selector!(selector_lg, StyleSelectors::empty().responsive());
618style_key_selector!(selector_xl, StyleSelectors::empty().responsive());
619style_key_selector!(selector_xxl, StyleSelectors::empty().responsive());
620
621pub(crate) fn screen_size_bp_to_key(breakpoint: ScreenSizeBp) -> StyleKey {
622    match breakpoint {
623        ScreenSizeBp::Xs => selector_xs(),
624        ScreenSizeBp::Sm => selector_sm(),
625        ScreenSizeBp::Md => selector_md(),
626        ScreenSizeBp::Lg => selector_lg(),
627        ScreenSizeBp::Xl => selector_xl(),
628        ScreenSizeBp::Xxl => selector_xxl(),
629    }
630}
631
632fn structural_selectors_key() -> StyleKey {
633    StyleKey {
634        info: &STRUCTURAL_SELECTORS_INFO,
635    }
636}
637
638fn responsive_selectors_key() -> StyleKey {
639    StyleKey {
640        info: &RESPONSIVE_SELECTORS_INFO,
641    }
642}
643
644/// 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
645pub fn resolve_nested_maps(
646    style: Style,
647    interact_state: &mut InteractionState,
648    screen_size_bp: ScreenSizeBp,
649    classes: &[StyleClassRef],
650    inherited_context: &Style,
651    class_context: &Style,
652) -> (Style, StyleSelectors) {
653    // TODO: update interact state as each map is resolved
654
655    let mut selectors = StyleSelectors::empty();
656
657    let effect_context = style.effect_context.clone();
658
659    let class_style = resolve_classes(
660        classes,
661        interact_state,
662        screen_size_bp,
663        class_context,
664        &mut selectors,
665    );
666    selectors |= class_style.selectors();
667    let view_style = resolve_style(style, interact_state, screen_size_bp, &mut selectors);
668    let result = class_style
669        .apply(view_style)
670        .with_inherited_context(inherited_context);
671    result.run_deferred_effects();
672    let _ = effect_context;
673    (result, selectors)
674}
675
676fn resolve_classes(
677    classes: &[StyleClassRef],
678    interact_state: &InteractionState,
679    screen_size_bp: ScreenSizeBp,
680    class_context: &Style,
681    selectors: &mut StyleSelectors,
682) -> Style {
683    let mut result = Style::with_capacity(classes.len());
684
685    for class in classes {
686        if let Some(map) = class_context.get_nested_map(class.key) {
687            let resolved = resolve_style(map.clone(), interact_state, screen_size_bp, selectors);
688            result.apply_mut(&resolved);
689        }
690    }
691
692    result
693}
694
695fn resolve_style(
696    style: Style,
697    interact_state: &InteractionState,
698    screen_size_bp: ScreenSizeBp,
699    selectors: &mut StyleSelectors,
700) -> Style {
701    resolve_selectors(style, interact_state, screen_size_bp, selectors)
702}
703
704fn resolve_selectors(
705    mut style: Style,
706    interact_state: &InteractionState,
707    screen_size_bp: ScreenSizeBp,
708    selectors: &mut StyleSelectors,
709) -> Style {
710    *selectors |= style.selectors();
711
712    // Validate cached selectors in debug builds
713    #[cfg(debug_assertions)]
714    debug_assert!(
715        style
716            .cached_selectors
717            .contains(style.compute_selectors_slow()),
718        "cached_selectors {:?} missing bits from computed {:?}",
719        style.cached_selectors,
720        style.compute_selectors_slow()
721    );
722
723    const MAX_DEPTH: u32 = 20;
724    let mut depth = 0;
725
726    loop {
727        if depth >= MAX_DEPTH {
728            break;
729        }
730        depth += 1;
731
732        let mut changed = false;
733
734        // Apply structural selectors (:first-child, :last-child, :nth-child(...))
735        // before state selectors so nested :hover/:focus inside structural maps can
736        // be discovered and applied in the same resolution loop.
737        if let Some(structural_rules) = extract_structural_selectors(&mut style) {
738            for (selector, map) in structural_rules {
739                if selector.matches(interact_state.child_index, interact_state.sibling_count) {
740                    style.apply_mut(map.as_ref());
741                    changed = true;
742                }
743            }
744        }
745
746        // Apply responsive selectors (parameterized)
747        if let Some(responsive_rules) = extract_responsive_selectors(&mut style) {
748            for (selector, map) in responsive_rules {
749                if selector.matches(interact_state.window_width) {
750                    style.apply_mut(map.as_ref());
751                    changed = true;
752                }
753            }
754        }
755
756        // Helper to apply a nested map and collect any context mappings from it
757        let apply_nested = |style: &mut Style, key: StyleKey| -> bool {
758            if let Some(map) = style.get_nested_map(key) {
759                style.apply_mut(&map);
760                style.remove_nested_map(key);
761                true
762            } else {
763                false
764            }
765        };
766
767        // Apply screen size breakpoints
768        if apply_nested(&mut style, screen_size_bp_to_key(screen_size_bp)) {
769            changed = true;
770        }
771
772        // DarkMode
773        if interact_state.is_dark_mode && apply_nested(&mut style, StyleSelector::DarkMode.to_key())
774        {
775            changed = true;
776        }
777
778        // Disabled state
779        if interact_state.is_disabled || style.get(Disabled) {
780            if apply_nested(&mut style, StyleSelector::Disabled.to_key()) {
781                changed = true;
782            }
783        } else {
784            // Selected
785            if (interact_state.is_selected || style.get(Selected))
786                && apply_nested(&mut style, StyleSelector::Selected.to_key())
787            {
788                changed = true;
789            }
790
791            // Hover
792            if interact_state.is_hovered && apply_nested(&mut style, StyleSelector::Hover.to_key())
793            {
794                changed = true;
795            }
796
797            // File Hover
798            if interact_state.is_file_hover
799                && apply_nested(&mut style, StyleSelector::FileHover.to_key())
800            {
801                changed = true;
802            }
803
804            // Focus states
805            if interact_state.is_focused && apply_nested(&mut style, StyleSelector::Focus.to_key())
806            {
807                changed = true;
808            }
809
810            if interact_state.is_focus_within
811                && apply_nested(&mut style, StyleSelector::FocusWithin.to_key())
812            {
813                changed = true;
814            }
815
816            if interact_state.is_focused && interact_state.using_keyboard_navigation {
817                if apply_nested(&mut style, StyleSelector::FocusVisible.to_key()) {
818                    changed = true;
819                }
820
821                if interact_state.is_active
822                    && apply_nested(&mut style, StyleSelector::Active.to_key())
823                {
824                    changed = true;
825                }
826            }
827
828            // Active (mouse)
829            if interact_state.is_active
830                && !interact_state.using_keyboard_navigation
831                && apply_nested(&mut style, StyleSelector::Active.to_key())
832            {
833                changed = true;
834            }
835        }
836
837        if !changed {
838            break;
839        }
840    }
841
842    style
843}
844
845fn extract_structural_selectors(style: &mut Style) -> Option<StructuralSelectorRules> {
846    let key = structural_selectors_key();
847    style
848        .map_mut()
849        .remove(&key)
850        .map(|rc| take_any::<StructuralSelectors>(rc).0)
851}
852
853fn extract_responsive_selectors(style: &mut Style) -> Option<ResponsiveSelectorRules> {
854    let key = responsive_selectors_key();
855    style
856        .map_mut()
857        .remove(&key)
858        .map(|rc| take_any::<ResponsiveSelectors>(rc).0)
859}
860
861#[derive(Clone)]
862pub struct Style {
863    pub(crate) map: Rc<FxHashMap<StyleKey, Rc<dyn Any>>>,
864    inherited_context: Option<Rc<FxHashMap<StyleKey, Rc<dyn Any>>>>,
865    /// Deterministic identity for style merges.
866    merge_id: u64,
867    /// Cached flag indicating whether this style contains any class maps.
868    /// This enables O(1) early-exit in `apply_only_class_maps` for the common case
869    /// where a view's style has no class definitions.
870    has_class_maps: bool,
871    /// Cached flag indicating whether this style contains any inherited properties.
872    /// This enables O(1) early-exit in `apply_only_inherited` for the common case
873    /// where a view's style has no inherited properties.
874    has_inherited: bool,
875    /// Cached bitmask of which selectors are present in this style (including nested).
876    /// Updated incrementally when selectors are added via `apply_iter`, `set_selector`, etc.
877    /// Enables O(1) checks in `resolve_selectors` to skip absent selectors.
878    cached_selectors: StyleSelectors,
879    /// Cached flag indicating whether this style contains any context-dependent values.
880    /// Styles with context values cannot be reliably cached because their content_hash()
881    /// is constant (all context values hash to 1), so different context values produce
882    /// the same cache key despite resolving to different output.
883    has_context_values: bool,
884    /// The effect context that was active when this style was created.
885    /// This is restored when evaluating context mappings and selectors to ensure
886    /// reactive dependencies are tracked correctly.
887    effect_context: Option<Rc<dyn floem_reactive::EffectTrait>>,
888}
889impl Default for Style {
890    fn default() -> Self {
891        Self::with_capacity(0)
892    }
893}
894
895impl Style {
896    fn with_capacity(capacity: usize) -> Self {
897        let effect_context = floem_reactive::Runtime::get_current_effect();
898        let map = Rc::new(fx_hash_map_with_capacity(capacity));
899        Self {
900            merge_id: next_style_merge_id(),
901            map,
902            inherited_context: None,
903            has_class_maps: false,
904            has_inherited: false,
905            cached_selectors: StyleSelectors::empty(),
906            has_context_values: false,
907            effect_context,
908        }
909    }
910
911    fn map_mut(&mut self) -> &mut FxHashMap<StyleKey, Rc<dyn Any>> {
912        Rc::make_mut(&mut self.map)
913    }
914
915    pub fn new() -> Self {
916        Self::with_capacity(0)
917    }
918
919    pub(crate) fn with_inherited_context(mut self, inherited: &Style) -> Self {
920        self.inherited_context = Some(inherited.map.clone());
921        self
922    }
923
924    /// Apply only inherited properties from `from` style to `to` style.
925    /// This is used during style propagation to pass inherited values to children.
926    ///
927    /// Only properties marked as `inherited: true` in their `StylePropInfo` are applied.
928    /// This is more efficient than `apply_mut` when we only need to propagate
929    /// inherited properties like font-size, color, etc.
930    pub fn apply_only_inherited(to: &mut Style, from: &Style) {
931        if from.any_inherited() {
932            for (k, v) in from.map.iter().filter(|(p, _)| p.inherited()) {
933                let StyleKeyInfo::Prop(info) = k.info else {
934                    continue;
935                };
936                to.map_mut()
937                    .insert(*k, (info.resolve_inherited_any)(&**v, from));
938                to.has_inherited = true;
939            }
940            to.merge_id = combine_merge_ids(to.merge_id, from.merge_id);
941        }
942    }
943
944    /// Apply inherited properties and class nested maps from `from` style to `to` style.
945    ///
946    /// This is used during style propagation to pass both inherited values and
947    /// class definitions to children. Class nested maps (like `.class(ListItemClass, ...)`)
948    /// need to flow to descendants so they can apply the styling when they have matching classes.
949    pub fn apply_inherited_and_class_maps(to: &mut Rc<Style>, from: &Style) {
950        let has_inherited = from.any_inherited();
951        // O(1) check using cached flag
952        let has_class_maps = from.has_class_maps;
953
954        if has_inherited || has_class_maps {
955            let mut new_style = (**to).clone();
956
957            // Apply inherited properties
958            if has_inherited {
959                for (k, v) in from.map.iter().filter(|(p, _)| p.inherited()) {
960                    let StyleKeyInfo::Prop(info) = k.info else {
961                        continue;
962                    };
963                    new_style
964                        .map_mut()
965                        .insert(*k, (info.resolve_inherited_any)(&**v, from));
966                    new_style.has_inherited = true;
967                }
968                new_style.merge_id = combine_merge_ids(new_style.merge_id, from.merge_id);
969            }
970
971            // Apply class nested maps so they flow to descendants
972            if has_class_maps {
973                let class_maps = from
974                    .map
975                    .iter()
976                    .filter(|(k, _)| matches!(k.info, StyleKeyInfo::Class(..)));
977                new_style.apply_iter(class_maps, None);
978                new_style.merge_id = combine_merge_ids(new_style.merge_id, from.merge_id);
979            }
980
981            *to = Rc::new(new_style);
982        }
983    }
984
985    /// Apply only class nested maps from `from` style to `to` style.
986    /// This is used during style propagation to pass class definitions to children.
987    ///
988    /// Only class nested maps (`.class(SomeClass, ...)`) are applied, not inherited props.
989    pub fn apply_only_class_maps(to: &mut Style, from: &Style) {
990        if !from.has_class_maps {
991            return;
992        }
993        let class_maps = from
994            .map
995            .iter()
996            .filter(|(k, _)| matches!(k.info, StyleKeyInfo::Class(..)));
997        to.apply_iter(class_maps, None);
998        to.merge_id = combine_merge_ids(to.merge_id, from.merge_id);
999    }
1000
1001    pub(crate) fn merge_id(&self) -> u64 {
1002        self.merge_id
1003    }
1004
1005    /// Returns the raw pointer of the inner `Rc<FxHashMap>` as a `usize`.
1006    /// Used by the style cache for O(1) identity comparison.
1007    pub(crate) fn map_ptr(&self) -> usize {
1008        Rc::as_ptr(&self.map) as usize
1009    }
1010
1011    /// Whether this style contains any context-dependent values.
1012    pub(crate) fn has_context_values(&self) -> bool {
1013        self.has_context_values
1014    }
1015
1016    /// Whether this style contains structural selectors (`:first-child`, `:nth-child`, etc.).
1017    /// Styles with structural selectors depend on `child_index`/`sibling_count` which are
1018    /// per-position values not captured in the cache key, so they must be excluded from caching.
1019    pub(crate) fn has_structural_selectors(&self) -> bool {
1020        self.map.contains_key(&structural_selectors_key())
1021    }
1022
1023    pub fn class_maps_eq(&self, other: &Style) -> SmallVec<[StyleClassRef; 4]> {
1024        // Pass 1: every Class entry in self must exist in other
1025        let mut changed = SmallVec::new();
1026        for (k, v) in self.map.iter() {
1027            let StyleKeyInfo::Class(_) = k.info else {
1028                continue;
1029            };
1030
1031            match other.map.get(k) {
1032                Some(other_v) => {
1033                    let v_style = v.downcast_ref::<Style>().unwrap();
1034                    let other_v_style = other_v.downcast_ref::<Style>().unwrap();
1035
1036                    if v_style.merge_id != other_v_style.merge_id {
1037                        changed.push(StyleClassRef { key: *k });
1038                    }
1039                }
1040                None => {
1041                    changed.push(StyleClassRef { key: *k });
1042                }
1043            }
1044        }
1045
1046        // Pass 2: ensure other does not contain extra Class entries
1047        for k in other.map.keys() {
1048            if !matches!(k.info, StyleKeyInfo::Class(..)) {
1049                continue;
1050            }
1051
1052            if !self.map.contains_key(k) {
1053                changed.push(StyleClassRef { key: *k });
1054            }
1055        }
1056
1057        changed
1058    }
1059
1060    pub(crate) fn get_transition<P: StyleProp>(&self) -> Option<Transition> {
1061        self.map
1062            .get(&P::prop_ref().info().transition_key)
1063            .map(|v| v.downcast_ref::<Transition>().unwrap().clone())
1064    }
1065
1066    fn get_prop_from_map<P: StyleProp>(
1067        map: &FxHashMap<StyleKey, Rc<dyn Any>>,
1068        context_style: &Style,
1069    ) -> Option<P::Type> {
1070        map.get(&P::key()).and_then(
1071            |v| match v.downcast_ref::<StyleMapValue<P::Type>>().unwrap() {
1072                StyleMapValue::Animated(v) | StyleMapValue::Val(v) => Some(v.clone()),
1073                StyleMapValue::Context(context_value) => Some(context_value.resolve(context_style)),
1074                StyleMapValue::Unset => None,
1075            },
1076        )
1077    }
1078
1079    fn get_style_value_from_map<P: StyleProp>(
1080        map: &FxHashMap<StyleKey, Rc<dyn Any>>,
1081        context_style: &Style,
1082    ) -> Option<StyleValue<P::Type>> {
1083        map.get(&P::key()).map(
1084            |v| match v.downcast_ref::<StyleMapValue<P::Type>>().unwrap() {
1085                StyleMapValue::Val(v) => StyleValue::Val(v.clone()),
1086                StyleMapValue::Animated(v) => StyleValue::Animated(v.clone()),
1087                StyleMapValue::Context(v) => StyleValue::Val(v.resolve(context_style)),
1088                StyleMapValue::Unset => StyleValue::Unset,
1089            },
1090        )
1091    }
1092
1093    pub(crate) fn get_prop_or_default<P: StyleProp>(&self) -> P::Type {
1094        self.get_prop::<P>().unwrap_or_else(|| P::default_value())
1095    }
1096
1097    pub(crate) fn get_prop<P: StyleProp>(&self) -> Option<P::Type> {
1098        Self::get_prop_from_map::<P>(&self.map, self).or_else(|| {
1099            self.inherited_context
1100                .as_ref()
1101                .and_then(|map| Self::get_prop_from_map::<P>(map, self))
1102        })
1103    }
1104
1105    pub(crate) fn get_prop_style_value<P: StyleProp>(&self) -> StyleValue<P::Type> {
1106        Self::get_style_value_from_map::<P>(&self.map, self)
1107            .or_else(|| {
1108                self.inherited_context
1109                    .as_ref()
1110                    .and_then(|map| Self::get_style_value_from_map::<P>(map, self))
1111            })
1112            .unwrap_or(StyleValue::Base)
1113    }
1114
1115    pub(crate) fn style_props(&self) -> impl Iterator<Item = StylePropRef> + '_ {
1116        self.map.keys().filter_map(|p| match p.info {
1117            StyleKeyInfo::Prop(..) => Some(StylePropRef { key: *p }),
1118            _ => None,
1119        })
1120    }
1121
1122    pub(crate) fn selectors(&self) -> StyleSelectors {
1123        self.cached_selectors
1124    }
1125
1126    /// Recompute selectors by traversing the map. Used for debug assertions.
1127    #[cfg(debug_assertions)]
1128    fn compute_selectors_slow(&self) -> StyleSelectors {
1129        let mut result = StyleSelectors::empty();
1130
1131        for (k, v) in self.map.iter() {
1132            match k.info {
1133                StyleKeyInfo::Selector(selector) => {
1134                    result = result
1135                        .union(*selector)
1136                        .union(v.downcast_ref::<Style>().unwrap().selectors());
1137                }
1138                StyleKeyInfo::StructuralSelectors => {
1139                    let rules = &v.downcast_ref::<StructuralSelectors>().unwrap().0;
1140                    for (_, nested_style) in rules {
1141                        result = result.union(nested_style.as_ref().selectors());
1142                    }
1143                }
1144                StyleKeyInfo::ResponsiveSelectors => {
1145                    result = result.responsive();
1146                    let rules = &v.downcast_ref::<ResponsiveSelectors>().unwrap().0;
1147                    for (_, nested_style) in rules {
1148                        result = result.union(nested_style.as_ref().selectors());
1149                    }
1150                }
1151                StyleKeyInfo::DebugGroup(..) => {}
1152                _ => {}
1153            }
1154        }
1155
1156        result
1157    }
1158
1159    pub fn apply_class<C: StyleClass>(mut self, _class: C) -> Style {
1160        if let Some(map) = self.map.get(&C::key()).cloned() {
1161            self.apply_mut(map.downcast_ref::<Style>().unwrap());
1162        }
1163        self
1164    }
1165
1166    pub fn apply_selectors(mut self, selectors: &[StyleSelector]) -> Style {
1167        for selector in selectors {
1168            if let Some(map) = self.get_nested_map(selector.to_key()) {
1169                let resolved = map.apply_selectors(selectors);
1170                self.apply_mut(&resolved);
1171            }
1172        }
1173        if self.get(Selected)
1174            && let Some(map) = self.get_nested_map(StyleSelector::Selected.to_key())
1175        {
1176            let resolved = map.apply_selectors(&[StyleSelector::Selected]);
1177            self.apply_mut(&resolved);
1178        }
1179        self
1180    }
1181
1182    /// Build style values from a context prop without introducing a deferred style map pass.
1183    ///
1184    /// The closure runs immediately and must produce an ordinary style map. Any deferred
1185    /// context work is captured at the individual property-value level through
1186    /// [`ContextRef::map`].
1187    pub fn with<P: StyleProp>(self, f: impl FnOnce(ExprStyle, ContextRef<P>) -> ExprStyle) -> Self {
1188        f(ExprStyle { style: self }, ContextRef::default()).style
1189    }
1190
1191    pub(crate) fn get_nested_map(&self, key: StyleKey) -> Option<Style> {
1192        self.map
1193            .get(&key)
1194            .map(|map| map.downcast_ref::<Style>().unwrap().clone())
1195    }
1196
1197    pub(crate) fn debug_group_enabled(&self, key: StyleKey) -> bool {
1198        self.map
1199            .get(&key)
1200            .and_then(|value| value.downcast_ref::<bool>().copied())
1201            .unwrap_or(false)
1202    }
1203
1204    pub(crate) fn remove_nested_map(&mut self, key: StyleKey) -> Option<Style> {
1205        let removed = self.map_mut().remove(&key).map(take_any::<Style>);
1206        if removed.is_some() {
1207            self.merge_id = next_style_merge_id();
1208        }
1209        removed
1210    }
1211
1212    /// Check if this style has any inherited properties.
1213    /// Used to determine if children should be re-styled when this view's style changes.
1214    /// O(1) using cached flag.
1215    pub(crate) fn any_inherited(&self) -> bool {
1216        self.has_inherited
1217    }
1218
1219    pub(crate) fn inherited(&self) -> Style {
1220        let mut new = Style::new();
1221        if self.any_inherited() {
1222            let inherited = self.map.iter().filter(|(p, _)| p.inherited());
1223
1224            new.apply_iter(inherited, None);
1225            new.merge_id = combine_merge_ids(new.merge_id, self.merge_id);
1226        }
1227        new
1228    }
1229
1230    fn set_selector(&mut self, selector: StyleSelector, map: Style) {
1231        self.set_map_selector(selector.to_key(), map)
1232    }
1233
1234    fn set_structural_selector(&mut self, selector: StructuralSelector, map: Style) {
1235        self.cached_selectors |= map.cached_selectors;
1236        let key = structural_selectors_key();
1237        let mut rules = self
1238            .map_mut()
1239            .remove(&key)
1240            .map(|current| take_any::<StructuralSelectors>(current).0)
1241            .unwrap_or_default();
1242        rules.push((selector, Rc::new(map)));
1243        self.map_mut()
1244            .insert(key, Rc::new(StructuralSelectors(rules)));
1245        self.merge_id = next_style_merge_id();
1246    }
1247
1248    fn set_responsive_selector(&mut self, selector: ResponsiveSelector, map: Style) {
1249        self.cached_selectors |= StyleSelectors::RESPONSIVE;
1250        self.cached_selectors |= map.cached_selectors;
1251        let key = responsive_selectors_key();
1252        let mut rules = self
1253            .map_mut()
1254            .remove(&key)
1255            .map(|current| take_any::<ResponsiveSelectors>(current).0)
1256            .unwrap_or_default();
1257        rules.push((selector, Rc::new(map)));
1258        self.map_mut()
1259            .insert(key, Rc::new(ResponsiveSelectors(rules)));
1260        self.merge_id = next_style_merge_id();
1261    }
1262
1263    fn set_map_selector(&mut self, key: StyleKey, map: Style) {
1264        // Track selector presence
1265        if let StyleKeyInfo::Selector(sel) = key.info {
1266            self.cached_selectors |= *sel;
1267            self.cached_selectors |= map.cached_selectors;
1268        }
1269        let value = if let Some(current) = self.map_mut().remove(&key) {
1270            let mut current: Style = take_any(current);
1271            current.apply_mut(&map);
1272            Rc::new(current)
1273        } else {
1274            Rc::new(map)
1275        };
1276        self.map_mut().insert(key, value);
1277        self.merge_id = next_style_merge_id();
1278    }
1279
1280    fn set_class(&mut self, class: StyleClassRef, map: Style) {
1281        self.has_class_maps = true;
1282        self.set_map_selector(class.key, map)
1283    }
1284
1285    pub fn debug_group<G: StyleDebugGroup>(mut self, _group: G) -> Self {
1286        self.map_mut().insert(G::key(), Rc::new(true));
1287        self.merge_id = next_style_merge_id();
1288        self
1289    }
1290
1291    pub fn unset_debug_group<G: StyleDebugGroup>(mut self, _group: G) -> Self {
1292        self.map_mut().insert(G::key(), Rc::new(false));
1293        self.merge_id = next_style_merge_id();
1294        self
1295    }
1296
1297    pub fn builtin(&self) -> BuiltinStyle<'_> {
1298        BuiltinStyle { style: self }
1299    }
1300
1301    pub(crate) fn apply_iter<'a>(
1302        &mut self,
1303        iter: impl Iterator<Item = (&'a StyleKey, &'a Rc<dyn Any>)>,
1304        source_effect_context: Option<Rc<dyn floem_reactive::EffectTrait>>,
1305    ) {
1306        if self.effect_context.is_none() && source_effect_context.is_some() {
1307            self.effect_context = source_effect_context;
1308        }
1309        for (k, v) in iter {
1310            match k.info {
1311                StyleKeyInfo::Class(..) | StyleKeyInfo::Selector(..) => {
1312                    // Track class maps for O(1) early-exit in apply_only_class_maps
1313                    if matches!(k.info, StyleKeyInfo::Class(..)) {
1314                        self.has_class_maps = true;
1315                    }
1316                    // Track selectors for O(1) selector presence checks
1317                    if let StyleKeyInfo::Selector(sel) = k.info {
1318                        self.cached_selectors |= *sel;
1319                        if let Some(nested) = v.downcast_ref::<Style>() {
1320                            self.cached_selectors |= nested.cached_selectors;
1321                        }
1322                    }
1323                    if let Some(existing_rc) = self.map_mut().remove(k) {
1324                        if Rc::ptr_eq(&existing_rc, v) {
1325                            self.map_mut().insert(*k, existing_rc);
1326                            continue;
1327                        }
1328
1329                        let mut current: Style = take_any(existing_rc);
1330                        current.apply_mut(v.downcast_ref::<Style>().unwrap());
1331                        self.map_mut().insert(*k, Rc::new(current));
1332                    } else {
1333                        self.map_mut().insert(*k, v.clone());
1334                    }
1335                }
1336                StyleKeyInfo::StructuralSelectors => {
1337                    // Propagate nested selectors from structural rules
1338                    let rules = &v.downcast_ref::<StructuralSelectors>().unwrap().0;
1339                    for (_, nested) in rules {
1340                        self.cached_selectors |= nested.cached_selectors;
1341                    }
1342                    let merged = if let Some(current) = self.map_mut().remove(k) {
1343                        let new_rules = &v.downcast_ref::<StructuralSelectors>().unwrap().0;
1344                        let current: StructuralSelectors = take_any(current);
1345                        let mut merged: StructuralSelectorRules = current.0;
1346                        merged.extend(new_rules.iter().cloned());
1347                        Rc::new(StructuralSelectors(merged))
1348                    } else {
1349                        v.clone()
1350                    };
1351                    self.map_mut().insert(*k, merged);
1352                }
1353                StyleKeyInfo::ResponsiveSelectors => {
1354                    self.cached_selectors |= StyleSelectors::RESPONSIVE;
1355                    // Propagate nested selectors from responsive rules
1356                    let rules = &v.downcast_ref::<ResponsiveSelectors>().unwrap().0;
1357                    for (_, nested) in rules {
1358                        self.cached_selectors |= nested.cached_selectors;
1359                    }
1360                    let merged = if let Some(current) = self.map_mut().remove(k) {
1361                        let new_rules = &v.downcast_ref::<ResponsiveSelectors>().unwrap().0;
1362                        let current: ResponsiveSelectors = take_any(current);
1363                        let mut merged: ResponsiveSelectorRules = current.0;
1364                        merged.extend(new_rules.iter().cloned());
1365                        Rc::new(ResponsiveSelectors(merged))
1366                    } else {
1367                        v.clone()
1368                    };
1369                    self.map_mut().insert(*k, merged);
1370                }
1371                StyleKeyInfo::DeferredEffects => {
1372                    let merged = if let Some(current) = self.map_mut().remove(k) {
1373                        let mut current: Vec<DeferredStyleEffect> = take_any(current);
1374                        current.extend(
1375                            v.downcast_ref::<Vec<DeferredStyleEffect>>()
1376                                .unwrap()
1377                                .iter()
1378                                .cloned(),
1379                        );
1380                        Rc::new(current)
1381                    } else {
1382                        v.clone()
1383                    };
1384                    self.map_mut().insert(*k, merged);
1385                }
1386                StyleKeyInfo::Transition | StyleKeyInfo::DebugGroup(..) => {
1387                    self.map_mut().insert(*k, v.clone());
1388                }
1389                StyleKeyInfo::Prop(info) => {
1390                    // Track inherited props for O(1) early-exit in apply_only_inherited
1391                    if info.inherited {
1392                        self.has_inherited = true;
1393                    }
1394                    self.map_mut().insert(*k, v.clone());
1395                }
1396            }
1397        }
1398    }
1399
1400    pub(crate) fn apply_mut(&mut self, over: &Style) {
1401        // FAST PATH: identical semantic payload identity
1402        if self.merge_id == over.merge_id {
1403            return;
1404        }
1405        let over_merge_id = over.merge_id;
1406        let effect_context = over.effect_context.clone();
1407        self.apply_iter(over.map.iter(), effect_context);
1408        self.has_context_values |= over.has_context_values;
1409        self.merge_id = combine_merge_ids(self.merge_id, over_merge_id);
1410    }
1411
1412    /// Apply another `Style` to this style, returning a new `Style` with the overrides
1413    ///
1414    /// `StyleValue::Val` will override the value with the given value
1415    /// `StyleValue::Unset` will unset the value, causing it to fall back to the default.
1416    /// `StyleValue::Base` will leave the value as-is, whether falling back to the default
1417    /// or using the value in the `Style`.
1418    pub fn apply(mut self, over: Style) -> Style {
1419        self.apply_mut(&over);
1420        self
1421    }
1422
1423    pub fn map(self, over: impl FnOnce(Self) -> Self) -> Self {
1424        over(self)
1425    }
1426
1427    /// Apply multiple `Style`s to this style, returning a new `Style` with the overrides.
1428    /// Later styles take precedence over earlier styles.
1429    pub fn apply_overriding_styles(self, overrides: impl Iterator<Item = Style>) -> Style {
1430        overrides.fold(self, |acc, x| acc.apply(x))
1431    }
1432}
1433
1434impl Debug for Style {
1435    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1436        f.debug_struct("Style")
1437            .field(
1438                "map",
1439                &self
1440                    .map
1441                    .iter()
1442                    .map(|(p, v)| (*p, (p.debug_any(&**v))))
1443                    .collect::<HashMap<StyleKey, String>>(),
1444            )
1445            .finish()
1446    }
1447}
1448
1449style_key_selector!(
1450    hover,
1451    StyleSelectors::empty().set_selector(StyleSelector::Hover, true)
1452);
1453style_key_selector!(
1454    file_hover,
1455    StyleSelectors::empty().set_selector(StyleSelector::FileHover, true)
1456);
1457style_key_selector!(
1458    focus,
1459    StyleSelectors::empty().set_selector(StyleSelector::Focus, true)
1460);
1461style_key_selector!(
1462    focus_visible,
1463    StyleSelectors::empty().set_selector(StyleSelector::FocusVisible, true)
1464);
1465style_key_selector!(
1466    focus_within,
1467    StyleSelectors::empty().set_selector(StyleSelector::FocusWithin, true)
1468);
1469style_key_selector!(
1470    disabled,
1471    StyleSelectors::empty().set_selector(StyleSelector::Disabled, true)
1472);
1473style_key_selector!(
1474    active,
1475    StyleSelectors::empty().set_selector(StyleSelector::Active, true)
1476);
1477style_key_selector!(
1478    dragging,
1479    StyleSelectors::empty().set_selector(StyleSelector::Dragging, true)
1480);
1481style_key_selector!(
1482    selected,
1483    StyleSelectors::empty().set_selector(StyleSelector::Selected, true)
1484);
1485style_key_selector!(
1486    darkmode,
1487    StyleSelectors::empty().set_selector(StyleSelector::DarkMode, true)
1488);
1489
1490impl StyleSelector {
1491    fn to_key(self) -> StyleKey {
1492        match self {
1493            StyleSelector::Hover => hover(),
1494            StyleSelector::Focus => focus(),
1495            StyleSelector::FocusVisible => focus_visible(),
1496            StyleSelector::FocusWithin => focus_within(),
1497            StyleSelector::Disabled => disabled(),
1498            StyleSelector::Active => active(),
1499            StyleSelector::Dragging => dragging(),
1500            StyleSelector::Selected => selected(),
1501            StyleSelector::DarkMode => darkmode(),
1502            StyleSelector::FileHover => file_hover(),
1503        }
1504    }
1505}
1506
1507/// Defines built-in style properties with optional builder methods.
1508///
1509/// Properties can be marked with flags in braces:
1510/// - `nocb` (no callback/no chain builder) - no fluent builder method generated
1511/// - `tr` (transition) - generates a `transition_property_name()` method
1512///
1513/// For `Option<T>` properties, specify the inner type in brackets after the full type:
1514///
1515/// ```text
1516/// Color color { tr }: Option<Color> [Color] { inherited } = None,
1517/// ```
1518///
1519/// This generates a setter that accepts `impl Into<Color>` and wraps in `Some`,
1520/// rather than the confusing `impl Into<Option<Color>>`. Use `unset_*()` to clear.
1521///
1522/// Examples:
1523/// - `name: Type {}`                           plain prop, setter takes Into<Type>
1524/// - `name {nocb}: Type {}`                    no setter generated
1525/// - `name {tr}: Type {}`                      setter + transition_name() generated
1526/// - `name {nocb, tr}: Type {}`                no setter, but transition_name() generated
1527/// - `name {tr}: Option<Type> [Type] {}`       setter takes Into<Type>, wraps in Some
1528/// - `name {nocb}: Option<Type> [Type] {}`     no setter generated
1529///
1530/// All properties get:
1531/// - A getter method in `BuiltinStyle`
1532/// - An `unset_property_name()` method
1533macro_rules! define_builtin_props {
1534    (
1535        $(
1536            $(#[$meta:meta])*
1537            $type_name:ident $name:ident $({ $($flags:ident),* })? :
1538            $typ:ty $( [$inner:ty] )? { $($options:tt)* } = $val:expr
1539        ),*
1540        $(,)?
1541    ) => {
1542        $(
1543            prop!($(#[$meta])* pub $type_name: $typ { $($options)* } = $val);
1544        )*
1545        impl Style {
1546            $(
1547                define_builtin_props!(decl: $(#[$meta])* $type_name $name $({ $($flags),* })? : $typ $( [$inner] )? = $val);
1548            )*
1549            $(
1550                define_builtin_props!(unset: $(#[$meta])* $type_name $name);
1551            )*
1552            $(
1553                define_builtin_props!(transition: $(#[$meta])* $type_name $name $({ $($flags),* })?);
1554            )*
1555        }
1556        impl BuiltinStyle<'_> {
1557            $(
1558                $(#[$meta])*
1559                pub fn $name(&self) -> $typ {
1560                    self.style.get($type_name)
1561                }
1562            )*
1563        }
1564        impl ExprStyle {
1565            $(
1566                define_builtin_props!(expr_decl: $(#[$meta])* $type_name $name $({ $($flags),* })? : $typ $( [$inner] )? = $val);
1567            )*
1568            $(
1569                define_builtin_props!(expr_unset: $(#[$meta])* $type_name $name);
1570            )*
1571        }
1572    };
1573
1574    // Built-in setters for `Option<T> [T]` take `Into<T>` and wrap in `Some`.
1575    (decl: $(#[$meta:meta])* $type_name:ident $name:ident { $($flags:ident),* } : $typ:ty [$inner:ty] = $val:expr) => {
1576        define_builtin_props!(@opt_check_nocb $(#[$meta])* $type_name $name [$($flags)*]: $inner);
1577    };
1578    (decl: $(#[$meta:meta])* $type_name:ident $name:ident : $typ:ty [$inner:ty] = $val:expr) => {
1579        $(#[$meta])*
1580        pub fn $name(self, v: impl Into<$inner>) -> Self {
1581            self.set($type_name, Some(v.into()))
1582        }
1583    };
1584    (decl: $(#[$meta:meta])* $type_name:ident $name:ident { $($flags:ident),* } : $typ:ty = $val:expr) => {
1585        define_builtin_props!(@check_nocb $(#[$meta])* $type_name $name [$($flags)*]: $typ);
1586    };
1587    (decl: $(#[$meta:meta])* $type_name:ident $name:ident : $typ:ty = $val:expr) => {
1588        $(#[$meta])*
1589        pub fn $name(self, v: impl Into<$typ>) -> Self {
1590            self.set($type_name, v.into())
1591        }
1592    };
1593
1594    (expr_decl: $(#[$meta:meta])* $type_name:ident $name:ident { $($flags:ident),* } : $typ:ty [$inner:ty] = $val:expr) => {
1595        define_builtin_props!(@opt_check_nocb_expr $(#[$meta])* $type_name $name [$($flags)*]: $inner);
1596    };
1597    (expr_decl: $(#[$meta:meta])* $type_name:ident $name:ident : $typ:ty [$inner:ty] = $val:expr) => {
1598        $(#[$meta])*
1599        pub fn $name<T>(self, v: $crate::style::ContextValue<T>) -> Self
1600        where
1601            T: Into<$inner> + 'static,
1602        {
1603            self.set($type_name, v.map(|x| Some(x.into())))
1604        }
1605    };
1606    (expr_decl: $(#[$meta:meta])* $type_name:ident $name:ident { $($flags:ident),* } : $typ:ty = $val:expr) => {
1607        define_builtin_props!(@check_nocb_expr $(#[$meta])* $type_name $name [$($flags)*]: $typ);
1608    };
1609    (expr_decl: $(#[$meta:meta])* $type_name:ident $name:ident : $typ:ty = $val:expr) => {
1610        $(#[$meta])*
1611        pub fn $name<T>(self, v: $crate::style::ContextValue<T>) -> Self
1612        where
1613            T: Into<$typ> + 'static,
1614        {
1615            self.set($type_name, v.map(Into::into))
1616        }
1617    };
1618
1619    (@opt_check_nocb $(#[$meta:meta])* $type_name:ident $name:ident [nocb $($rest:ident)*]: $inner:ty) => {};
1620    (@opt_check_nocb $(#[$meta:meta])* $type_name:ident $name:ident [$first:ident $($rest:ident)*]: $inner:ty) => {
1621        define_builtin_props!(@opt_check_nocb $(#[$meta])* $type_name $name [$($rest)*]: $inner);
1622    };
1623    (@opt_check_nocb $(#[$meta:meta])* $type_name:ident $name:ident []: $inner:ty) => {
1624        $(#[$meta])*
1625        pub fn $name(self, v: impl Into<$inner>) -> Self {
1626            self.set($type_name, Some(v.into()))
1627        }
1628    };
1629
1630    (@opt_check_nocb_expr $(#[$meta:meta])* $type_name:ident $name:ident [nocb $($rest:ident)*]: $inner:ty) => {};
1631    (@opt_check_nocb_expr $(#[$meta:meta])* $type_name:ident $name:ident [$first:ident $($rest:ident)*]: $inner:ty) => {
1632        define_builtin_props!(@opt_check_nocb_expr $(#[$meta])* $type_name $name [$($rest)*]: $inner);
1633    };
1634    (@opt_check_nocb_expr $(#[$meta:meta])* $type_name:ident $name:ident []: $inner:ty) => {
1635        $(#[$meta])*
1636        pub fn $name<T>(self, v: $crate::style::ContextValue<T>) -> Self
1637        where
1638            T: Into<$inner> + 'static,
1639        {
1640            self.set($type_name, v.map(|x| Some(x.into())))
1641        }
1642    };
1643
1644    // -------------------------------------------------------------------------
1645    // @check_nocb — plain (non-Option) setter, respects nocb flag
1646    // -------------------------------------------------------------------------
1647
1648    (@check_nocb $(#[$meta:meta])* $type_name:ident $name:ident [nocb $($rest:ident)*]: $typ:ty) => {};
1649    (@check_nocb $(#[$meta:meta])* $type_name:ident $name:ident [$first:ident $($rest:ident)*]: $typ:ty) => {
1650        define_builtin_props!(@check_nocb $(#[$meta])* $type_name $name [$($rest)*]: $typ);
1651    };
1652    (@check_nocb $(#[$meta:meta])* $type_name:ident $name:ident []: $typ:ty) => {
1653        $(#[$meta])*
1654        pub fn $name(self, v: impl Into<$typ>) -> Self {
1655            self.set($type_name, v.into())
1656        }
1657    };
1658
1659    (@check_nocb_expr $(#[$meta:meta])* $type_name:ident $name:ident [nocb $($rest:ident)*]: $typ:ty) => {};
1660    (@check_nocb_expr $(#[$meta:meta])* $type_name:ident $name:ident [$first:ident $($rest:ident)*]: $typ:ty) => {
1661        define_builtin_props!(@check_nocb_expr $(#[$meta])* $type_name $name [$($rest)*]: $typ);
1662    };
1663    (@check_nocb_expr $(#[$meta:meta])* $type_name:ident $name:ident []: $typ:ty) => {
1664        $(#[$meta])*
1665        pub fn $name<T>(self, v: $crate::style::ContextValue<T>) -> Self
1666        where
1667            T: Into<$typ> + 'static,
1668        {
1669            self.set($type_name, v.map(Into::into))
1670        }
1671    };
1672
1673    // -------------------------------------------------------------------------
1674    // unset — generated for all properties
1675    // -------------------------------------------------------------------------
1676
1677    (unset: $(#[$meta:meta])* $type_name:ident $name:ident) => {
1678        paste::paste! {
1679            #[doc = "Unsets the `" $name "` property."]
1680            pub fn [<unset_ $name>](self) -> Self {
1681                self.set_style_value($type_name, $crate::style::StyleValue::Unset)
1682            }
1683        }
1684    };
1685
1686    (expr_unset: $(#[$meta:meta])* $type_name:ident $name:ident) => {
1687        paste::paste! {
1688            #[doc = "Unsets the `" $name "` property."]
1689            pub fn [<unset_ $name>](self) -> Self {
1690                self.set($type_name, $crate::style::StyleValue::Unset)
1691            }
1692        }
1693    };
1694
1695    // -------------------------------------------------------------------------
1696    // transition — generated when `tr` flag is present
1697    // -------------------------------------------------------------------------
1698
1699    // With flags — check for tr
1700    (transition: $(#[$meta:meta])* $type_name:ident $name:ident { $($flags:ident),* }) => {
1701        define_builtin_props!(@check_tr $(#[$meta])* $type_name $name [$($flags)*]);
1702    };
1703    // Without flags — never generate
1704    (transition: $(#[$meta:meta])* $type_name:ident $name:ident) => {};
1705
1706    (@check_tr $(#[$meta:meta])* $type_name:ident $name:ident [tr $($rest:ident)*]) => {
1707        paste::paste! {
1708            #[doc = "Sets a transition for the `" $name "` property."]
1709            $(#[$meta])*
1710            pub fn [<transition_ $name>](self, transition: impl Into<Transition>) -> Self {
1711                self.transition($type_name, transition.into())
1712            }
1713        }
1714    };
1715    (@check_tr $(#[$meta:meta])* $type_name:ident $name:ident [$first:ident $($rest:ident)*]) => {
1716        define_builtin_props!(@check_tr $(#[$meta])* $type_name $name [$($rest)*]);
1717    };
1718    (@check_tr $(#[$meta:meta])* $type_name:ident $name:ident []) => {};
1719}
1720
1721pub struct BuiltinStyle<'a> {
1722    style: &'a Style,
1723}
1724
1725define_builtin_props!(
1726    /// Controls the display type of the view.
1727    ///
1728    /// This determines how the view participates in layout.
1729    DisplayProp display {}: Display {} = Display::Flex,
1730
1731    /// Sets the positioning scheme for the view.
1732    ///
1733    /// This affects how the view is positioned relative to its normal position in the document flow.
1734    PositionProp position {}: Position {} = Position::Relative,
1735
1736    /// Enables fixed positioning relative to the viewport.
1737    ///
1738    /// When true, the view is positioned relative to the window viewport rather than
1739    /// its parent. This is similar to CSS `position: fixed`. The view will:
1740    /// - Use `inset` properties relative to the viewport
1741    /// - Have percentage sizes relative to the viewport
1742    /// - Be painted above all other content (like overlays)
1743    ///
1744    /// Note: This works in conjunction with `position: absolute` internally.
1745    IsFixed is_fixed {}: bool {} = false,
1746
1747    /// Sets the width of the view.
1748    ///
1749    /// Can be specified in pixels, percentages, or auto.
1750    Width width {tr}: LengthAuto {} = LengthAuto::Auto,
1751
1752    /// Sets the height of the view.
1753    ///
1754    /// Can be specified in pixels, percentages, or auto.
1755    Height height {tr}: LengthAuto {} = LengthAuto::Auto,
1756
1757    /// Sets the minimum width of the view.
1758    ///
1759    /// The view will not shrink below this width.
1760    MinWidth min_width {tr}: LengthAuto {} = LengthAuto::Auto,
1761
1762    /// Sets the minimum height of the view.
1763    ///
1764    /// The view will not shrink below this height.
1765    MinHeight min_height {tr}: LengthAuto {} = LengthAuto::Auto,
1766
1767    /// Sets the maximum width of the view.
1768    ///
1769    /// The view will not grow beyond this width.
1770    MaxWidth max_width {tr}: LengthAuto {} = LengthAuto::Auto,
1771
1772    /// Sets the maximum height of the view.
1773    ///
1774    /// The view will not grow beyond this height.
1775    MaxHeight max_height {tr}: LengthAuto {} = LengthAuto::Auto,
1776
1777    /// Sets the direction of the main axis for flex items.
1778    ///
1779    /// Determines whether flex items are laid out in rows or columns.
1780    FlexDirectionProp flex_direction {}: FlexDirection {} = FlexDirection::Row,
1781
1782    /// Controls whether flex items wrap to new lines.
1783    ///
1784    /// When enabled, items that don't fit will wrap to the next line.
1785    FlexWrapProp flex_wrap {}: FlexWrap {} = FlexWrap::NoWrap,
1786
1787    /// Sets the flex grow factor for the flex item.
1788    ///
1789    /// Determines how much the item should grow relative to other items.
1790    FlexGrow flex_grow {}: f32 {} = 0.0,
1791
1792    /// Sets the flex shrink factor for the flex item.
1793    ///
1794    /// Determines how much the item should shrink relative to other items.
1795    FlexShrink flex_shrink {}: f32 {} = 1.0,
1796
1797    /// Sets the initial main size of a flex item.
1798    ///
1799    /// This is the size of the item before free space is distributed.
1800    FlexBasis flex_basis {tr}: LengthAuto {} = LengthAuto::Auto,
1801
1802    /// Controls alignment of flex items along the main axis.
1803    ///
1804    /// Determines how extra space is distributed between and around items.
1805    JustifyContentProp justify_content {}: Option<JustifyContent> [JustifyContent] {} = None,
1806
1807    /// Controls default alignment of grid items along the inline axis.
1808    ///
1809    /// Sets the default justify-self value for all items in the container.
1810    JustifyItemsProp justify_items {}: Option<JustifyItems> [JustifyItems] {} = None,
1811
1812    /// Controls how the total width and height are calculated.
1813    ///
1814    /// Determines whether borders and padding are included in the view's size.
1815    BoxSizingProp box_sizing {}: Option<BoxSizing> [BoxSizing] {} = None,
1816
1817    /// Controls individual alignment along the inline axis.
1818    ///
1819    /// Overrides the container's justify-items value for this specific item.
1820    JustifySelf justify_self {}: Option<AlignItems> [AlignItems] {} = None,
1821
1822    /// Controls alignment of flex items along the cross axis.
1823    ///
1824    /// Determines how items are aligned when they don't fill the container's cross axis.
1825    AlignItemsProp align_items {}: Option<AlignItems> [AlignItems] {} = None,
1826
1827    /// Controls alignment of wrapped flex lines.
1828    ///
1829    /// Only has an effect when flex-wrap is enabled and there are multiple lines.
1830    AlignContentProp align_content {}: Option<AlignContent> [AlignContent] {} = None,
1831
1832    /// Defines the line names and track sizing functions of the grid rows.
1833    ///
1834    /// Specifies the size and names of the rows in a grid layout.
1835    GridTemplateRows grid_template_rows {}: Vec<GridTemplateComponent<String>> {} = Vec::new(),
1836
1837    /// Defines the line names and track sizing functions of the grid columns.
1838    ///
1839    /// Specifies the size and names of the columns in a grid layout.
1840    GridTemplateColumns grid_template_columns {}: Vec<GridTemplateComponent<String>> {} = Vec::new(),
1841
1842    /// Specifies the size of implicitly-created grid rows.
1843    ///
1844    /// Sets the default size for rows that are created automatically.
1845    GridAutoRows grid_auto_rows {}: Vec<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> {} = Vec::new(),
1846
1847    /// Specifies the size of implicitly-created grid columns.
1848    ///
1849    /// Sets the default size for columns that are created automatically.
1850    GridAutoColumns grid_auto_columns {}: Vec<MinMax<MinTrackSizingFunction, MaxTrackSizingFunction>> {} = Vec::new(),
1851
1852    /// Controls how auto-placed items get flowed into the grid.
1853    ///
1854    /// Determines the direction that grid items are placed when not explicitly positioned.
1855    GridAutoFlow grid_auto_flow {}: taffy::GridAutoFlow {} = taffy::GridAutoFlow::Row,
1856
1857    /// Specifies a grid item's location within the grid row.
1858    ///
1859    /// Determines which grid rows the item spans.
1860    GridRow grid_row {}: Line<GridPlacement> {} = Line::default(),
1861
1862    /// Specifies a grid item's location within the grid column.
1863    ///
1864    /// Determines which grid columns the item spans.
1865    GridColumn grid_column {}: Line<GridPlacement> {} = Line::default(),
1866
1867    /// Controls individual alignment along the cross axis.
1868    ///
1869    /// Overrides the container's align-items value for this specific item.
1870    AlignSelf align_self {}: Option<AlignItems> [AlignItems] {} = None,
1871
1872    /// Sets the color of the view's outline.
1873    ///
1874    /// The outline is drawn outside the border and doesn't affect layout.
1875    OutlineColor outline_color {tr}: Brush {} = Brush::Solid(palette::css::TRANSPARENT),
1876
1877    /// Sets the outline stroke properties.
1878    ///
1879    /// Defines the width, style, and other properties of the outline.
1880    Outline outline {nocb, tr}: Stroke {} = Stroke::new(0.),
1881
1882    /// Controls the progress/completion of the outline animation.
1883    ///
1884    /// Useful for creating animated outline effects.
1885    OutlineProgress outline_progress {tr}: Pct {} = Pct(100.),
1886
1887    /// Controls the progress/completion of the border animation.
1888    ///
1889    /// Useful for creating animated border effects.
1890    BorderProgress border_progress {tr}: Pct {} = Pct(100.),
1891
1892    /// Sets the left border.
1893    BorderLeft border_left {nocb, tr}: Stroke {} = Stroke::new(0.),
1894    /// Sets the top border.
1895    BorderTop border_top {nocb, tr}: Stroke {} = Stroke::new(0.),
1896    /// Sets the right border.
1897    BorderRight border_right {nocb, tr}: Stroke {} = Stroke::new(0.),
1898    /// Sets the bottom border.
1899    BorderBottom border_bottom {nocb, tr}: Stroke {} = Stroke::new(0.),
1900
1901    /// Sets the left border color.
1902    BorderLeftColor border_left_color { tr }: Option<Brush> [Brush] {} = None,
1903    /// Sets the top border color.
1904    BorderTopColor border_top_color {  tr }: Option<Brush> [Brush] {} = None,
1905    /// Sets the right border color.
1906    BorderRightColor border_right_color { tr }: Option<Brush> [Brush] {} = None,
1907    /// Sets the bottom border color.
1908    BorderBottomColor border_bottom_color { tr }: Option<Brush> [Brush] {} = None,
1909
1910    /// Sets the top-left border radius.
1911    BorderTopLeftRadius border_top_left_radius { tr }: Length {} = Length::Pt(0.),
1912    /// Sets the top-right border radius.
1913    BorderTopRightRadius border_top_right_radius { tr }: Length {} = Length::Pt(0.),
1914    /// Sets the bottom-left border radius.
1915    BorderBottomLeftRadius border_bottom_left_radius { tr }: Length {} = Length::Pt(0.),
1916    /// Sets the bottom-right border radius.
1917    BorderBottomRightRadius border_bottom_right_radius { tr }: Length {} = Length::Pt(0.),
1918
1919    /// Sets the left padding.
1920    PaddingLeft padding_left { tr }: Length {} = Length::Pt(0.),
1921    /// Sets the top padding.
1922    PaddingTop padding_top { tr }: Length {} = Length::Pt(0.),
1923    /// Sets the right padding.
1924    PaddingRight padding_right { tr }: Length {} = Length::Pt(0.),
1925    /// Sets the bottom padding.
1926    PaddingBottom padding_bottom { tr }: Length {} = Length::Pt(0.),
1927
1928    /// Sets the left margin.
1929    MarginLeft margin_left { tr }: LengthAuto {} = LengthAuto::Pt(0.),
1930    /// Sets the top margin.
1931    MarginTop margin_top { tr }: LengthAuto {} = LengthAuto::Pt(0.),
1932    /// Sets the right margin.
1933    MarginRight margin_right { tr }: LengthAuto {} = LengthAuto::Pt(0.),
1934    /// Sets the bottom margin.
1935    MarginBottom margin_bottom { tr }: LengthAuto {} = LengthAuto::Pt(0.),
1936
1937    /// Sets the left offset for positioned views.
1938    InsetLeft inset_left {tr}: LengthAuto {} = LengthAuto::Auto,
1939
1940    /// Sets the top offset for positioned views.
1941    InsetTop inset_top {tr}: LengthAuto {} = LengthAuto::Auto,
1942
1943    /// Sets the right offset for positioned views.
1944    InsetRight inset_right {tr}: LengthAuto {} = LengthAuto::Auto,
1945
1946    /// Sets the bottom offset for positioned views.
1947    InsetBottom inset_bottom {tr}: LengthAuto {} = LengthAuto::Auto,
1948
1949    /// Controls whether the view can be the target of mouse events.
1950    ///
1951    /// When disabled, mouse events pass through to views behind.
1952    PointerEventsProp pointer_events {}: Option<PointerEvents> [PointerEvents] { inherited } = None,
1953
1954    /// Controls the stack order of positioned views.
1955    ///
1956    /// This is not a global z-index and will only be used as an override to the sorted order of sibling elements.
1957    /// If you want a view positioned above others, use an overlay.
1958    ///
1959    /// Higher values appear in front of lower values.
1960    ZIndex z_index {  tr }: Option<i32> [i32] {} = None,
1961
1962    /// Sets the cursor style when hovering over the view.
1963    ///
1964    /// Changes the appearance of the mouse cursor.
1965    Cursor cursor { }: Option<CursorStyle> [CursorStyle] {} = None,
1966
1967    /// Sets the text color.
1968    ///
1969    /// This property is inherited by child views.
1970    TextColor color { tr }: Option<Color> [Color] { inherited } = None,
1971
1972    /// Sets the background color or image.
1973    ///
1974    /// Can be a solid color, gradient, or image.
1975    Background background { tr }: Option<Brush> [Brush] {} = None,
1976
1977    /// Sets the foreground color or pattern.
1978    ///
1979    /// Used for drawing content like icons or shapes.
1980    Foreground foreground { tr }: Option<Brush> [Brush] {} = None,
1981
1982    /// Adds one or more drop shadows to the view.
1983    ///
1984    /// Can create depth and visual separation effects.
1985    BoxShadowProp box_shadow {  tr }: SmallVec<[BoxShadow; 3]> {} = SmallVec::new(),
1986
1987    /// Sets the font size for text content.
1988    ///
1989    /// This property is inherited by child views.
1990    FontSize font_size { nocb, tr }: f64 { inherited } = 14.,
1991
1992    /// Sets the font family for text content.
1993    ///
1994    /// This property is inherited by child views.
1995    FontFamily font_family { }: Option<String> [String] { inherited } = None,
1996
1997    /// Sets the font weight (boldness) for text content.
1998    ///
1999    /// This property is inherited by child views.
2000    FontWeight font_weight { }: Option<FontWeightProp> [FontWeightProp] { inherited } = None,
2001
2002    /// Sets the font style (italic, normal) for text content.
2003    ///
2004    /// This property is inherited by child views.
2005    FontStyle font_style { }: Option<crate::text::FontStyle> [crate::text::FontStyle] { inherited } = None,
2006
2007    /// Sets the color of the text cursor.
2008    ///
2009    /// Visible when text input views have focus.
2010    CursorColor cursor_color { tr }: Brush {} = Brush::Solid(palette::css::BLACK.with_alpha(0.3)),
2011
2012    /// Sets the corner radius of text selections.
2013    ///
2014    /// Controls how rounded the corners of selected text appear.
2015    SelectionCornerRadius selection_corer_radius { nocb, tr }: f64 {} = 1.,
2016
2017    /// Controls whether the view's text can be selected.
2018    ///
2019    /// This property is inherited by child views.
2020    // TODO: rename this TextSelectable
2021    Selectable selectable {}: bool { inherited } = true,
2022
2023    /// Controls how overflowed text content is handled.
2024    ///
2025    /// Determines whether text wraps or gets clipped.
2026    TextOverflowProp text_overflow {}: TextOverflow { inherited } = TextOverflow::NoWrap(NoWrapOverflow::Clip),
2027
2028    /// Sets text alignment within the view.
2029    ///
2030    /// Controls horizontal alignment of text content.
2031    TextAlignProp text_align {}: Option<crate::text::Alignment> [crate::text::Alignment] {} = None,
2032
2033    /// Sets the line height for text content.
2034    ///
2035    /// This property is inherited by child views.
2036    LineHeight line_height { tr }: LineHeightValue { inherited } = LineHeightValue::Normal(1.),
2037
2038    /// Sets the preferred aspect ratio for the view.
2039    ///
2040    /// Maintains width-to-height proportions during layout.
2041    AspectRatio aspect_ratio {tr}: Option<f32> [f32] {} = None,
2042
2043    /// Controls how replaced content (like images) should be resized to fit its container.
2044    ///
2045    /// This property specifies how an image or other replaced element should be resized
2046    /// to fit within its container while potentially preserving its aspect ratio.
2047    /// Corresponds to the CSS `object-fit` property.
2048    ObjectFitProp object_fit {}: ObjectFit {} = ObjectFit::Fill,
2049
2050    /// Controls where replaced content is anchored inside its content box.
2051    ///
2052    /// This property affects paint-time placement for images and other replaced content.
2053    /// Corresponds to common CSS `object-position` keyword combinations.
2054    ObjectPositionProp object_position {}: ObjectPosition {} = ObjectPosition::Center,
2055
2056    /// Sets the gap between columns in grid or flex layouts.
2057    ///
2058    /// Creates space between items in the horizontal direction.
2059    ColGap col_gap { tr }: Length {} = Length::Pt(0.),
2060
2061    /// Sets the gap between rows in grid or flex layouts.
2062    ///
2063    /// Creates space between items in the vertical direction.
2064    RowGap row_gap { tr }: Length {} = Length::Pt(0.),
2065
2066    /// Width of the scrollbar track in pixels.
2067    ///
2068    /// This property reserves space for scrollbars when `overflow_x` or `overflow_y` is set to `Scroll`.
2069    /// The reserved space reduces the available content area but ensures content doesn't flow under the scrollbar.
2070    ///
2071    /// **Layout behavior:**
2072    /// - When `overflow_y: Scroll`, reserves `scrollbar_width` from the right side of the container
2073    /// - When `overflow_x: Scroll`, reserves `scrollbar_width` from the bottom of the container
2074    /// - Space is reserved inside the container's bounds, reducing the content rect size
2075    /// - No space is reserved for `overflow: Hidden`, `Visible`, or `Clip`
2076    ///
2077    /// **Example:**
2078    /// ```rust,ignore
2079    /// // Reserve 10px for scrollbar
2080    /// .scrollbar_width(10.0)
2081    ///
2082    /// // Thinner scrollbar for compact UI
2083    /// .scrollbar_width(6.0)
2084    /// ```
2085    ///
2086    /// **Default:** `8px`
2087    ScrollbarWidth scrollbar_width {tr}: Pt {} = Pt(8.),
2088
2089    /// How children overflowing their container in X axis should affect layout
2090    OverflowX overflow_x {}: Overflow {} = Overflow::default(),
2091
2092    /// How children overflowing their container in Y axis should affect layout
2093    OverflowY overflow_y {}: Overflow {} = Overflow::default(),
2094
2095    /// Sets the horizontal scale transform.
2096    ///
2097    /// Values less than 100% shrink the view, greater than 100% enlarge it.
2098    /// Scale is applied last in the transform sequence, after translation and rotation.
2099    /// The scaling occurs around the anchor point specified by `scale_about`.
2100    /// Transform order: translate → rotate → scale (matches CSS individual transform properties).
2101    ScaleX scale_x {tr}: Pct {} = Pct(100.),
2102
2103    /// Sets the vertical scale transform.
2104    ///
2105    /// Values less than 100% shrink the view, greater than 100% enlarge it.
2106    /// Scale is applied last in the transform sequence, after translation and rotation.
2107    /// The scaling occurs around the anchor point specified by `scale_about`.
2108    /// Transform order: translate → rotate → scale (matches CSS individual transform properties).
2109    ScaleY scale_y {tr}: Pct {} = Pct(100.),
2110
2111    /// Sets the horizontal translation transform.
2112    ///
2113    /// Moves the view left (negative) or right (positive).
2114    /// Translation is applied first in the transform sequence, in the element's local coordinate space.
2115    /// This matches CSS individual transform properties behavior.
2116    /// Transform order: translate → rotate → scale.
2117    TranslateX translate_x {tr}: Length {} = Length::Pt(0.),
2118
2119    /// Sets the vertical translation transform.
2120    ///
2121    /// Moves the view up (negative) or down (positive).
2122    /// Translation is applied first in the transform sequence, in the element's local coordinate space.
2123    /// This matches CSS individual transform properties behavior.
2124    /// Transform order: translate → rotate → scale.
2125    TranslateY translate_y {tr}: Length {} = Length::Pt(0.),
2126
2127    /// Sets the rotation transform angle.
2128    ///
2129    /// Positive values rotate clockwise, negative values rotate counter-clockwise.
2130    /// Use `.deg()` or `.rad()` methods to specify the angle unit.
2131    /// Rotation is applied after translation but before scaling, around the anchor point
2132    /// specified by `rotate_about`.
2133    /// Transform order: translate → rotate → scale (matches CSS individual transform properties).
2134    Rotation rotate {tr}: Angle {} = Angle::Rad(0.0),
2135
2136    /// Sets the anchor point for rotation transformations.
2137    ///
2138    /// Determines the point around which the view rotates. Use predefined constants
2139    /// like `AnchorAbout::CENTER` or create custom anchor points with pixel or percentage values.
2140    /// The anchor point is specified in the element's local coordinate space (before any transforms).
2141    RotateAbout rotate_about {}: AnchorAbout {} = AnchorAbout::CENTER,
2142
2143    /// Sets the anchor point for scaling transformations.
2144    ///
2145    /// Determines the point around which the view scales. Use predefined constants
2146    /// like `AnchorAbout::CENTER` or create custom anchor points with pixel or percentage values.
2147    /// The anchor point is specified in the element's local coordinate space (before any transforms).
2148    /// Transform order: translate → rotate → scale (matches CSS individual transform properties).
2149    ScaleAbout scale_about {tr}: AnchorAbout {} = AnchorAbout::CENTER,
2150
2151    /// Sets a custom affine transformation matrix.
2152    ///
2153    /// This property allows you to specify an arbitrary 2D affine transformation that will be
2154    /// applied in addition to the individual transform properties (translate_x, translate_y,
2155    /// scale_x, scale_y, rotate).
2156    ///
2157    /// **Transform application order:**
2158    /// 1. Individual `translate_x` and `translate_y` properties
2159    /// 2. Individual `rotate` property
2160    /// 3. Individual `scale_x` and `scale_y` properties
2161    /// 4. **This `transform` property (applied last)**
2162    ///
2163    /// This matches CSS behavior where individual transform properties are applied before
2164    /// the `transform` property. The `transform` matrix is applied in the final coordinate
2165    /// space after all individual transforms.
2166    ///
2167    /// **Example:**
2168    /// ```rust
2169    /// # use floem::peniko::kurbo::Affine;
2170    /// # use floem::style::Style;
2171    /// let _style = Style::new()
2172    ///     .translate_x(10.0) // Applied first
2173    ///     .scale(1.5) // Applied second
2174    ///     .transform(Affine::rotate(0.5)); // Applied last
2175    /// ```
2176    Transform transform {tr}: Affine {} = Affine::IDENTITY,
2177
2178    /// Sets the opacity of the view.
2179    ///
2180    /// Values range from 0.0 (fully transparent) to 1.0 (fully opaque).
2181    /// This affects the entire view including its children.
2182    Opacity opacity {tr}: f32 {} = 1.0,
2183
2184    /// Sets the selected state of the view.
2185    ///
2186    /// This property is inherited by child views.
2187    Selected set_selected {}: bool { inherited } = false,
2188
2189    /// Controls the disabled state of the view.
2190    ///
2191    /// This property is inherited by child views.
2192    Disabled set_disabled {}: bool { inherited } = false,
2193
2194    /// Controls whether the view can receive focus during navigation such as tab or arrow navigation.
2195    Focusable set_focus {}: Focus { } = Focus::None,
2196);
2197
2198prop_extractor! {
2199    pub FontProps {
2200        pub size: FontSize,
2201        pub family: FontFamily,
2202        pub weight: FontWeight,
2203        pub style: FontStyle,
2204    }
2205}
2206
2207prop_extractor! {
2208    pub(crate) LayoutProps {
2209        // display is used here to just to properly trigger transitions on layout change. it is not transitioned here
2210        pub border_left: BorderLeft,
2211        pub border_top: BorderTop,
2212        pub border_right: BorderRight,
2213        pub border_bottom: BorderBottom,
2214
2215        pub padding_left: PaddingLeft,
2216        pub padding_top: PaddingTop,
2217        pub padding_right: PaddingRight,
2218        pub padding_bottom: PaddingBottom,
2219
2220        pub margin_left: MarginLeft,
2221        pub margin_top: MarginTop,
2222        pub margin_right: MarginRight,
2223        pub margin_bottom: MarginBottom,
2224
2225        pub width: Width,
2226        pub height: Height,
2227
2228        pub min_width: MinWidth,
2229        pub min_height: MinHeight,
2230
2231        pub max_width: MaxWidth,
2232        pub max_height: MaxHeight,
2233
2234        pub flex_grow: FlexGrow,
2235        pub flex_shrink: FlexShrink,
2236        pub flex_basis: FlexBasis ,
2237
2238        pub inset_left: InsetLeft,
2239        pub inset_top: InsetTop,
2240        pub inset_right: InsetRight,
2241        pub inset_bottom: InsetBottom,
2242
2243        pub row_gap: RowGap,
2244        pub col_gap: ColGap,
2245
2246        // these are part of layout props because of em/lh units
2247        pub font_size: FontSize,
2248        pub line_height: LineHeight,
2249    }
2250}
2251
2252prop_extractor! {
2253    /// These are properties that when changed the box tree needs committed.
2254    pub TransformProps {
2255        pub scale_x: ScaleX,
2256        pub scale_y: ScaleY,
2257
2258        pub translate_x: TranslateX,
2259        pub translate_y: TranslateY,
2260
2261        pub rotation: Rotation,
2262        pub rotate_about: RotateAbout,
2263        pub scale_about: ScaleAbout,
2264
2265        pub transform: Transform,
2266
2267        pub overflow_x: OverflowX,
2268        pub overflow_y: OverflowY,
2269        pub border_top_left_radius: BorderTopLeftRadius,
2270        pub border_top_right_radius: BorderTopRightRadius,
2271        pub border_bottom_left_radius: BorderBottomLeftRadius,
2272        pub border_bottom_right_radius: BorderBottomRightRadius,
2273    }
2274}
2275impl TransformProps {
2276    pub fn border_radius(&self) -> BorderRadius {
2277        BorderRadius {
2278            top_left: Some(self.border_top_left_radius()),
2279            top_right: Some(self.border_top_right_radius()),
2280            bottom_left: Some(self.border_bottom_left_radius()),
2281            bottom_right: Some(self.border_bottom_right_radius()),
2282        }
2283    }
2284
2285    pub fn affine(&self, size: kurbo::Size, resolve_cx: &FontSizeCx) -> Affine {
2286        let mut result = Affine::IDENTITY;
2287        // CANONICAL ORDER (matches CSS individual properties):
2288        // 1. translate → 2. rotate → 3. scale → 4. transform property
2289
2290        // 1. Translate
2291        let transform_x = self.translate_x().resolve(size.width, resolve_cx);
2292        let transform_y = self.translate_y().resolve(size.height, resolve_cx);
2293        result *= Affine::translate(Vec2 {
2294            x: transform_x,
2295            y: transform_y,
2296        });
2297
2298        // 2. Rotate (around rotate_about anchor)
2299        let rotation = self.rotation().to_radians();
2300        if rotation != 0.0 {
2301            let rotate_about = self.rotate_about();
2302            let (rotate_x_frac, rotate_y_frac) = rotate_about.as_fractions();
2303            let rotate_point = Vec2 {
2304                x: rotate_x_frac * size.width,
2305                y: rotate_y_frac * size.height,
2306            };
2307            result *= Affine::translate(rotate_point)
2308                * Affine::rotate(rotation)
2309                * Affine::translate(-rotate_point);
2310        }
2311
2312        // 3. Scale (around scale_about anchor)
2313        let scale_x = self.scale_x().0 / 100.;
2314        let scale_y = self.scale_y().0 / 100.;
2315        if scale_x != 1.0 || scale_y != 1.0 {
2316            let scale_about = self.scale_about();
2317            let (scale_x_frac, scale_y_frac) = scale_about.as_fractions();
2318            let scale_point = Vec2 {
2319                x: scale_x_frac * size.width,
2320                y: scale_y_frac * size.height,
2321            };
2322            result *= Affine::translate(scale_point)
2323                * Affine::scale_non_uniform(scale_x, scale_y)
2324                * Affine::translate(-scale_point);
2325        }
2326
2327        // 4. Apply custom transform property last
2328        result *= self.transform();
2329        result
2330    }
2331
2332    pub fn clip_rect(
2333        &self,
2334        mut local_rect: kurbo::Rect,
2335        resolve_cx: &FontSizeCx,
2336    ) -> Option<RoundedRect> {
2337        use Overflow::*;
2338
2339        let (overflow_x, overflow_y) = (self.overflow_x(), self.overflow_y());
2340
2341        // No clipping if both are visible
2342        if overflow_x == Visible && overflow_y == Visible {
2343            return None;
2344        }
2345
2346        let border_radius = self
2347            .border_radius()
2348            .resolve_border_radii(local_rect.size().min_side(), resolve_cx);
2349
2350        // Extend to infinity on visible axes
2351        if overflow_x == Visible {
2352            local_rect.x0 = f64::NEG_INFINITY;
2353            local_rect.x1 = f64::INFINITY;
2354        }
2355        if overflow_y == Visible {
2356            local_rect.y0 = f64::NEG_INFINITY;
2357            local_rect.y1 = f64::INFINITY;
2358        }
2359
2360        Some(RoundedRect::from_rect(local_rect, border_radius))
2361    }
2362}
2363
2364impl LayoutProps {
2365    pub fn border(&self) -> Border {
2366        Border {
2367            left: Some(self.border_left()),
2368            top: Some(self.border_top()),
2369            right: Some(self.border_right()),
2370            bottom: Some(self.border_bottom()),
2371        }
2372    }
2373
2374    pub fn font_size_cx(&self) -> FontSizeCx {
2375        {
2376            let font_size = self.font_size();
2377            let line_height = self.line_height();
2378            let line_height = line_height.resolve(font_size as f32);
2379            FontSizeCx::new(font_size, line_height as f64)
2380        }
2381    }
2382
2383    pub fn apply_to_taffy_style(&self, style: &mut TaffyStyle) {
2384        let resolve_cx = &self.font_size_cx();
2385        style.size = taffy::prelude::Size {
2386            width: self.width().to_taffy_dim(resolve_cx),
2387            height: self.height().to_taffy_dim(resolve_cx),
2388        };
2389        style.min_size = taffy::prelude::Size {
2390            width: self.min_width().to_taffy_dim(resolve_cx),
2391            height: self.min_height().to_taffy_dim(resolve_cx),
2392        };
2393        style.max_size = taffy::prelude::Size {
2394            width: self.max_width().to_taffy_dim(resolve_cx),
2395            height: self.max_height().to_taffy_dim(resolve_cx),
2396        };
2397        style.flex_grow = self.flex_grow();
2398        style.flex_shrink = self.flex_shrink();
2399        style.flex_basis = self.flex_basis().to_taffy_dim(resolve_cx);
2400        style.border = Rect {
2401            left: LengthPercentage::length(self.border_left().width as f32),
2402            top: LengthPercentage::length(self.border_top().width as f32),
2403            right: LengthPercentage::length(self.border_right().width as f32),
2404            bottom: LengthPercentage::length(self.border_bottom().width as f32),
2405        };
2406        style.padding = Rect {
2407            left: self.padding_left().to_taffy(resolve_cx),
2408            top: self.padding_top().to_taffy(resolve_cx),
2409            right: self.padding_right().to_taffy(resolve_cx),
2410            bottom: self.padding_bottom().to_taffy(resolve_cx),
2411        };
2412        style.margin = Rect {
2413            left: self.margin_left().to_taffy_len_perc_auto(resolve_cx),
2414            top: self.margin_top().to_taffy_len_perc_auto(resolve_cx),
2415            right: self.margin_right().to_taffy_len_perc_auto(resolve_cx),
2416            bottom: self.margin_bottom().to_taffy_len_perc_auto(resolve_cx),
2417        };
2418        style.inset = Rect {
2419            left: self.inset_left().to_taffy_len_perc_auto(resolve_cx),
2420            top: self.inset_top().to_taffy_len_perc_auto(resolve_cx),
2421            right: self.inset_right().to_taffy_len_perc_auto(resolve_cx),
2422            bottom: self.inset_bottom().to_taffy_len_perc_auto(resolve_cx),
2423        };
2424        style.gap = Size {
2425            width: self.col_gap().to_taffy(resolve_cx),
2426            height: self.row_gap().to_taffy(resolve_cx),
2427        };
2428    }
2429}
2430
2431prop_extractor! {
2432    pub SelectionStyle {
2433        pub corner_radius: SelectionCornerRadius,
2434        pub selection_color: SelectionColor,
2435    }
2436}
2437
2438impl Style {
2439    fn deferred_effects(&self) -> impl Iterator<Item = &DeferredStyleEffect> {
2440        self.map
2441            .get(&DEFERRED_EFFECTS_KEY)
2442            .into_iter()
2443            .flat_map(|effects| {
2444                effects
2445                    .downcast_ref::<Vec<DeferredStyleEffect>>()
2446                    .into_iter()
2447                    .flat_map(|effects| effects.iter())
2448            })
2449    }
2450
2451    fn run_deferred_effects(&self) {
2452        for effect in self.deferred_effects() {
2453            effect.run(self);
2454        }
2455    }
2456
2457    fn push_deferred_effect(mut self, effect: DeferredStyleEffect) -> Self {
2458        let mut effects = self
2459            .map
2460            .get(&DEFERRED_EFFECTS_KEY)
2461            .and_then(|effects| effects.downcast_ref::<Vec<DeferredStyleEffect>>())
2462            .cloned()
2463            .unwrap_or_default();
2464        effects.push(effect);
2465        self.map_mut()
2466            .insert(DEFERRED_EFFECTS_KEY, Rc::new(effects));
2467        self.merge_id = next_style_merge_id();
2468        self
2469    }
2470
2471    /// Gets the value of a style property, returning the default if not set.
2472    pub fn get<P: StyleProp>(&self, _prop: P) -> P::Type {
2473        self.get_prop_or_default::<P>()
2474    }
2475
2476    /// Gets the raw style value of a property, including unset and base states.
2477    pub fn get_style_value<P: StyleProp>(&self, _prop: P) -> StyleValue<P::Type> {
2478        self.get_prop_style_value::<P>()
2479    }
2480
2481    /// Sets a style property to a specific value.
2482    pub fn set<P: StyleProp>(self, prop: P, value: impl Into<P::Type>) -> Self {
2483        self.set_style_value(prop, StyleValue::Val(value.into()))
2484    }
2485
2486    /// Sets a property to a deferred context-derived value.
2487    pub fn set_context<P: StyleProp>(self, prop: P, value: ContextValue<P::Type>) -> Self {
2488        self.set_style_value(prop, StyleValue::Context(value))
2489    }
2490
2491    /// Register a deferred side effect for a context prop.
2492    ///
2493    /// Unlike [`ContextRef::def`], this does not produce a property value. The closure is
2494    /// guaranteed to run during nested-style resolution after inherited context has been
2495    /// attached, even if no property later reads the context value.
2496    ///
2497    /// Use this for debug-view setup and similar side effects:
2498    /// `Style::new().defer::<Theme>(|t| color.set(t.primary()))`.
2499    pub fn defer<P: StyleProp>(self, f: impl Fn(P::Type) + 'static) -> Self
2500    where
2501        P::Type: 'static,
2502    {
2503        self.push_deferred_effect(ContextRef::<P>::default().into_deferred(f))
2504    }
2505
2506    pub fn set_from_context<C: StyleProp, P: StyleProp>(
2507        self,
2508        prop: P,
2509        context: ContextRef<C>,
2510        f: impl Fn(C::Type) -> P::Type + 'static,
2511    ) -> Self
2512    where
2513        C::Type: 'static,
2514        P::Type: 'static,
2515    {
2516        self.set_context(prop, context.def(f))
2517    }
2518
2519    /// Sets a property to a deferred optional context-derived value.
2520    ///
2521    /// `None` resolves to an unset property, allowing the base/fallback style to win.
2522    pub fn set_context_opt<P: StyleProp<Type = Option<T>>, T: 'static>(
2523        self,
2524        prop: P,
2525        value: ContextValue<Option<T>>,
2526    ) -> Self {
2527        self.set_style_value(prop, StyleValue::Context(value))
2528    }
2529
2530    pub fn set_from_context_opt<C: StyleProp, P: StyleProp<Type = Option<T>>, T: 'static>(
2531        self,
2532        prop: P,
2533        context: ContextRef<C>,
2534        f: impl Fn(C::Type) -> Option<T> + 'static,
2535    ) -> Self
2536    where
2537        C::Type: 'static,
2538    {
2539        self.set_context_opt(prop, context.def(f))
2540    }
2541
2542    pub fn set_style_value<P: StyleProp>(mut self, _prop: P, value: StyleValue<P::Type>) -> Self {
2543        let previous_value = self.map.get(&P::key()).cloned();
2544        let insert = match value {
2545            StyleValue::Val(value) => StyleMapValue::Val(value),
2546            StyleValue::Animated(value) => StyleMapValue::Animated(value),
2547            StyleValue::Context(value) => {
2548                self.has_context_values = true;
2549                let previous_value = previous_value.clone();
2550                StyleMapValue::Context(ContextValue::new(move |style| {
2551                    let mut base_style = style.clone();
2552                    base_style.map_mut().remove(&P::key());
2553                    // A deferred value for property `P` is allowed to read `P` from context,
2554                    // e.g. `FontSize := FontSize * 0.8`. If we resolved against the current
2555                    // style as-is, that lookup would see the context expression we are
2556                    // installing right now and recurse forever.
2557                    //
2558                    // Instead, same-prop context reads resolve against the previous effective
2559                    // value for `P`: the previous local value if there was one, otherwise the
2560                    // inherited fallback carried by `Style`.
2561                    if let Some(previous_value) = previous_value.clone() {
2562                        let previous_value = match previous_value
2563                            .downcast_ref::<StyleMapValue<P::Type>>()
2564                            .unwrap()
2565                        {
2566                            StyleMapValue::Val(value) | StyleMapValue::Animated(value) => {
2567                                Some(value.clone())
2568                            }
2569                            StyleMapValue::Context(context_value) => {
2570                                Some(context_value.resolve(&base_style))
2571                            }
2572                            StyleMapValue::Unset => None,
2573                        };
2574
2575                        if let Some(previous_value) = previous_value {
2576                            base_style
2577                                .map_mut()
2578                                .insert(P::key(), Rc::new(StyleMapValue::Val(previous_value)));
2579                        }
2580                    }
2581                    value.resolve(&base_style)
2582                }))
2583            }
2584            StyleValue::Unset => StyleMapValue::Unset,
2585            StyleValue::Base => {
2586                if self.map_mut().remove(&P::key()).is_some() {
2587                    self.merge_id = next_style_merge_id();
2588                }
2589                return self;
2590            }
2591        };
2592        // Track inherited props for O(1) early-exit in apply_only_inherited
2593        if P::prop_ref().info().inherited {
2594            self.has_inherited = true;
2595        }
2596        self.map_mut().insert(P::key(), Rc::new(insert));
2597        self.merge_id = next_style_merge_id();
2598        self
2599    }
2600
2601    /// Sets a transition animation for a specific style property.
2602    pub fn transition<P: StyleProp>(mut self, _prop: P, transition: Transition) -> Self {
2603        self.map_mut()
2604            .insert(P::prop_ref().info().transition_key, Rc::new(transition));
2605        self.merge_id = next_style_merge_id();
2606        self
2607    }
2608
2609    fn selector(mut self, selector: StyleSelector, style: impl FnOnce(Style) -> Style) -> Self {
2610        let over = style(Style::default());
2611        self.set_selector(selector, over);
2612        self
2613    }
2614
2615    pub(crate) fn structural_selector(
2616        mut self,
2617        selector: StructuralSelector,
2618        style: impl FnOnce(Style) -> Style,
2619    ) -> Self {
2620        let over = style(Style::default());
2621        self.set_structural_selector(selector, over);
2622        self
2623    }
2624
2625    /// The visual style to apply when the mouse hovers over the view
2626    pub fn hover(self, style: impl FnOnce(Style) -> Style) -> Self {
2627        self.selector(StyleSelector::Hover, style)
2628    }
2629
2630    /// The visual style to apply when the view has keyboard focus.
2631    pub fn focus(self, style: impl FnOnce(Style) -> Style) -> Self {
2632        self.selector(StyleSelector::Focus, style)
2633    }
2634
2635    /// Similar to the `:focus-visible` css selector, this style only activates when the view was focused via tab or arrow navigation.
2636    pub fn focus_visible(self, style: impl FnOnce(Style) -> Style) -> Self {
2637        self.selector(StyleSelector::FocusVisible, style)
2638    }
2639
2640    /// Similar to the `:focus-within` css selector, this style activates when this
2641    /// view or any descendant is in the focus path.
2642    pub fn focus_within(self, style: impl FnOnce(Style) -> Style) -> Self {
2643        self.selector(StyleSelector::FocusWithin, style)
2644    }
2645
2646    /// Similar to the `:first-child` css selector.
2647    pub fn first_child(self, style: impl FnOnce(Style) -> Style) -> Self {
2648        self.structural_selector(StructuralSelector::FirstChild, style)
2649    }
2650
2651    /// Similar to the `:last-child` css selector.
2652    pub fn last_child(self, style: impl FnOnce(Style) -> Style) -> Self {
2653        self.structural_selector(StructuralSelector::LastChild, style)
2654    }
2655
2656    /// Similar to the `:nth-child(...)` css selector.
2657    pub fn nth_child(self, nth: NthChild, style: impl FnOnce(Style) -> Style) -> Self {
2658        self.structural_selector(StructuralSelector::NthChild(nth), style)
2659    }
2660
2661    /// Convenience for `:nth-child(odd)`.
2662    pub fn odd(self, style: impl FnOnce(Style) -> Style) -> Self {
2663        self.nth_child(NthChild::odd(), style)
2664    }
2665
2666    /// Convenience for `:nth-child(even)`.
2667    pub fn even(self, style: impl FnOnce(Style) -> Style) -> Self {
2668        self.nth_child(NthChild::even(), style)
2669    }
2670
2671    /// The visual style to apply when the view is in a selected state.
2672    pub fn selected(self, style: impl FnOnce(Style) -> Style) -> Self {
2673        self.selector(StyleSelector::Selected, style)
2674    }
2675
2676    /// The visual style to apply when the view is being dragged
2677    pub fn drag(self, style: impl FnOnce(Style) -> Style) -> Self {
2678        self.selector(StyleSelector::Dragging, style)
2679    }
2680
2681    /// The visual style to apply when the view is disabled.
2682    pub fn disabled(self, style: impl FnOnce(Style) -> Style) -> Self {
2683        self.selector(StyleSelector::Disabled, style)
2684    }
2685
2686    /// The visual style to apply when the application is in dark mode.
2687    pub fn dark_mode(self, style: impl FnOnce(Style) -> Style) -> Self {
2688        self.selector(StyleSelector::DarkMode, style)
2689    }
2690
2691    /// The visual style to apply when a file is being dragged over the view.
2692    pub fn file_hover(self, style: impl FnOnce(Style) -> Style) -> Self {
2693        self.selector(StyleSelector::FileHover, style)
2694    }
2695
2696    /// The visual style to apply when the view is being actively pressed.
2697    pub fn active(self, style: impl FnOnce(Style) -> Style) -> Self {
2698        self.selector(StyleSelector::Active, style)
2699    }
2700
2701    /// Applies styles that activate at specific screen sizes (responsive design).
2702    pub fn responsive(mut self, size: ScreenSize, style: impl FnOnce(Style) -> Style) -> Self {
2703        let over = style(Style::default());
2704        self.set_responsive_selector(ResponsiveSelector::ScreenSize(size), over);
2705        self
2706    }
2707
2708    /// Applies styles when window width is at least `min`.
2709    pub fn min_window_width(
2710        mut self,
2711        min: impl Into<Pt>,
2712        style: impl FnOnce(Style) -> Style,
2713    ) -> Self {
2714        let over = style(Style::default());
2715        self.set_responsive_selector(ResponsiveSelector::MinWidth(min.into()), over);
2716        self
2717    }
2718
2719    /// Applies styles when window width is at most `max`.
2720    pub fn max_window_width(
2721        mut self,
2722        max: impl Into<Pt>,
2723        style: impl FnOnce(Style) -> Style,
2724    ) -> Self {
2725        let over = style(Style::default());
2726        self.set_responsive_selector(ResponsiveSelector::MaxWidth(max.into()), over);
2727        self
2728    }
2729
2730    /// Applies styles when window width is within `[min, max]` (inclusive).
2731    pub fn window_width_range(
2732        mut self,
2733        min: impl Into<Pt>,
2734        max: impl Into<Pt>,
2735        style: impl FnOnce(Style) -> Style,
2736    ) -> Self {
2737        let over = style(Style::default());
2738        self.set_responsive_selector(
2739            ResponsiveSelector::WidthRange {
2740                min: min.into(),
2741                max: max.into(),
2742            },
2743            over,
2744        );
2745        self
2746    }
2747
2748    /// Applies styles to views with a specific CSS class.
2749    pub fn class<C: StyleClass>(mut self, _class: C, style: impl FnOnce(Style) -> Style) -> Self {
2750        let over = style(Style::default());
2751        self.set_class(C::class_ref(), over);
2752        self
2753    }
2754
2755    /// Applies a `CustomStyle` type to the `CustomStyle`'s associated style class.
2756    ///
2757    /// For example: if the `CustomStyle` you use is `DropdownCustomStyle` then it
2758    /// will apply the custom style to that custom style type's associated style class
2759    /// which, in this example, is `DropdownClass`.
2760    ///
2761    /// This is especially useful when building a stylesheet or targeting a child view.
2762    ///
2763    /// # Examples
2764    /// ```
2765    /// // In a style sheet or on a parent view
2766    /// use floem::prelude::*;
2767    /// use floem::style::Style;
2768    /// Style::new().custom_style_class(|s: dropdown::DropdownCustomStyle| s.close_on_accept(false));
2769    /// // This property is now set on the `DropdownClass` class and will be applied to any dropdowns that are children of this view.
2770    /// ```
2771    ///
2772    /// See also: [`Style::custom`](Self::custom) and [`Style::apply_custom`](Self::apply_custom).
2773    pub fn custom_style_class<CS: CustomStyle>(mut self, style: impl FnOnce(CS) -> CS) -> Self {
2774        let over = style(CS::default());
2775        self.set_class(CS::StyleClass::class_ref(), over.into());
2776        self
2777    }
2778
2779    /// Sets the width to 100% of the parent container.
2780    pub fn width_full(self) -> Self {
2781        self.width_pct(100.0)
2782    }
2783
2784    /// Sets the width as a percentage of the parent container.
2785    pub fn width_pct(self, width: f64) -> Self {
2786        self.width(width.pct())
2787    }
2788
2789    /// Sets the height to 100% of the parent container.
2790    pub fn height_full(self) -> Self {
2791        self.height_pct(100.0)
2792    }
2793
2794    /// Sets the height as a percentage of the parent container.
2795    pub fn height_pct(self, height: f64) -> Self {
2796        self.height(height.pct())
2797    }
2798
2799    /// Makes the view fully keyboard navigable.
2800    ///
2801    /// The view can receive focus via Tab/Shift+Tab navigation, arrow keys,
2802    /// pointer clicks, and programmatic focus calls. This is the recommended
2803    /// setting for interactive controls like buttons, inputs, and links.
2804    /// Keyboard navigable is a strict superset of focusable.
2805    ///
2806    /// Equivalent to `focus(Focus::Keyboard)`.
2807    pub fn keyboard_navigable(self) -> Self {
2808        self.set(Focusable, Focus::Keyboard)
2809    }
2810
2811    /// Makes the view focusable by pointer and programmatically, but excludes it
2812    /// from keyboard navigation. For many elements (especially buttons) you should
2813    /// probably use [Self::keyboard_navigable].
2814    ///
2815    /// The view can be clicked to receive focus or focused via `request_focus()`,
2816    /// but will not be included in Tab order or arrow key navigation. Useful for
2817    /// scroll containers, modal backdrops, or roving tabindex patterns.
2818    /// If you need keyboard traversal, use [Self::keyboard_navigable], which
2819    /// also enables focusability automatically.
2820    ///
2821    /// Equivalent to `focus(Focus::PointerAndProgrammatic)`.
2822    pub fn focusable(self) -> Self {
2823        self.set(Focusable, Focus::PointerAndProgrammatic)
2824    }
2825
2826    /// Sets the font size for text content.
2827    pub fn font_size(self, size: impl Into<Pt>) -> Self {
2828        let px = size.into();
2829        self.set_style_value(FontSize, StyleValue::Val(px.0))
2830    }
2831
2832    /// Makes the view non-focusable through any means.
2833    ///
2834    /// The view cannot receive focus via keyboard, pointer, or programmatic calls.
2835    /// Use this for decorative elements or containers that should never be interactive.
2836    ///
2837    /// Equivalent to `focus(Focus::None)`.
2838    pub fn focus_none(self) -> Self {
2839        self.set(Focusable, Focus::None)
2840    }
2841
2842    /// Sets different gaps for rows and columns in grid or flex layouts.
2843    pub fn row_col_gap(self, width: impl Into<Length>, height: impl Into<Length>) -> Self {
2844        self.col_gap(width).row_gap(height)
2845    }
2846
2847    /// Sets the same gap for both rows and columns in grid or flex layouts.
2848    pub fn gap(self, gap: impl Into<Length>) -> Self {
2849        let gap = gap.into();
2850        self.col_gap(gap).row_gap(gap)
2851    }
2852
2853    /// Sets both width and height of the view.
2854    pub fn size(self, width: impl Into<LengthAuto>, height: impl Into<LengthAuto>) -> Self {
2855        self.width(width).height(height)
2856    }
2857
2858    /// Sets both width and height to 100% of the parent container.
2859    pub fn size_full(self) -> Self {
2860        self.size_pct(100.0, 100.0)
2861    }
2862
2863    /// Sets both width and height as percentages of the parent container.
2864    pub fn size_pct(self, width: f64, height: f64) -> Self {
2865        self.width(width.pct()).height(height.pct())
2866    }
2867
2868    /// Sets the minimum width to 100% of the parent container.
2869    pub fn min_width_full(self) -> Self {
2870        self.min_width_pct(100.0)
2871    }
2872
2873    /// Sets the minimum width as a percentage of the parent container.
2874    pub fn min_width_pct(self, min_width: f64) -> Self {
2875        self.min_width(min_width.pct())
2876    }
2877
2878    /// Sets the minimum height to 100% of the parent container.
2879    pub fn min_height_full(self) -> Self {
2880        self.min_height_pct(100.0)
2881    }
2882
2883    /// Sets the minimum height as a percentage of the parent container.
2884    pub fn min_height_pct(self, min_height: f64) -> Self {
2885        self.min_height(min_height.pct())
2886    }
2887
2888    /// Sets both minimum width and height to 100% of the parent container.
2889    pub fn min_size_full(self) -> Self {
2890        self.min_size_pct(100.0, 100.0)
2891    }
2892
2893    /// Sets both minimum width and height of the view.
2894    pub fn min_size(
2895        self,
2896        min_width: impl Into<LengthAuto>,
2897        min_height: impl Into<LengthAuto>,
2898    ) -> Self {
2899        self.min_width(min_width).min_height(min_height)
2900    }
2901
2902    /// Sets both minimum width and height as percentages of the parent container.
2903    pub fn min_size_pct(self, min_width: f64, min_height: f64) -> Self {
2904        self.min_size(min_width.pct(), min_height.pct())
2905    }
2906
2907    /// Sets the maximum width to 100% of the parent container.
2908    pub fn max_width_full(self) -> Self {
2909        self.max_width_pct(100.0)
2910    }
2911
2912    /// Sets the maximum width as a percentage of the parent container.
2913    pub fn max_width_pct(self, max_width: f64) -> Self {
2914        self.max_width(max_width.pct())
2915    }
2916
2917    /// Sets the maximum height to 100% of the parent container.
2918    pub fn max_height_full(self) -> Self {
2919        self.max_height_pct(100.0)
2920    }
2921
2922    /// Sets the maximum height as a percentage of the parent container.
2923    pub fn max_height_pct(self, max_height: f64) -> Self {
2924        self.max_height(max_height.pct())
2925    }
2926
2927    /// Sets both maximum width and height of the view.
2928    pub fn max_size(
2929        self,
2930        max_width: impl Into<LengthAuto>,
2931        max_height: impl Into<LengthAuto>,
2932    ) -> Self {
2933        self.max_width(max_width).max_height(max_height)
2934    }
2935
2936    /// Sets both maximum width and height to 100% of the parent container.
2937    pub fn max_size_full(self) -> Self {
2938        self.max_size_pct(100.0, 100.0)
2939    }
2940
2941    /// Sets both maximum width and height as percentages of the parent container.
2942    pub fn max_size_pct(self, max_width: f64, max_height: f64) -> Self {
2943        self.max_size(max_width.pct(), max_height.pct())
2944    }
2945
2946    /// Sets the border color for all sides of the view.
2947    pub fn border_color(self, color: impl Into<Brush>) -> Self {
2948        let color = color.into();
2949        self.set(BorderLeftColor, Some(color.clone()))
2950            .set(BorderTopColor, Some(color.clone()))
2951            .set(BorderRightColor, Some(color.clone()))
2952            .set(BorderBottomColor, Some(color))
2953    }
2954
2955    /// Sets the border properties for all sides of the view.
2956    pub fn border(self, border: impl Into<StrokeWrap>) -> Self {
2957        let border = border.into();
2958        self.set(BorderLeft, border.0.clone())
2959            .set(BorderTop, border.0.clone())
2960            .set(BorderRight, border.0.clone())
2961            .set(BorderBottom, border.0)
2962    }
2963
2964    /// Sets the outline properties of the view.
2965    pub fn outline(self, outline: impl Into<StrokeWrap>) -> Self {
2966        self.set_style_value(Outline, StyleValue::Val(outline.into().0))
2967    }
2968
2969    /// Sets the left border.
2970    pub fn border_left(self, border: impl Into<StrokeWrap>) -> Self {
2971        self.set(BorderLeft, border.into().0)
2972    }
2973
2974    /// Sets the top border.
2975    pub fn border_top(self, border: impl Into<StrokeWrap>) -> Self {
2976        self.set(BorderTop, border.into().0)
2977    }
2978
2979    /// Sets the right border.
2980    pub fn border_right(self, border: impl Into<StrokeWrap>) -> Self {
2981        self.set(BorderRight, border.into().0)
2982    }
2983
2984    /// Sets the bottom border.
2985    pub fn border_bottom(self, border: impl Into<StrokeWrap>) -> Self {
2986        self.set(BorderBottom, border.into().0)
2987    }
2988
2989    /// Sets `border_left` and `border_right` to `border`
2990    pub fn border_horiz(self, border: impl Into<StrokeWrap>) -> Self {
2991        let border = border.into();
2992        self.set(BorderLeft, border.0.clone())
2993            .set(BorderRight, border.0)
2994    }
2995
2996    /// Sets `border_top` and `border_bottom` to `border`
2997    pub fn border_vert(self, border: impl Into<StrokeWrap>) -> Self {
2998        let border = border.into();
2999        self.set(BorderTop, border.0.clone())
3000            .set(BorderBottom, border.0)
3001    }
3002
3003    /// Sets the left padding as a percentage of the parent container width.
3004    pub fn padding_left_pct(self, padding: f64) -> Self {
3005        self.padding_left(padding.pct())
3006    }
3007
3008    /// Sets the right padding as a percentage of the parent container width.
3009    pub fn padding_right_pct(self, padding: f64) -> Self {
3010        self.padding_right(padding.pct())
3011    }
3012
3013    /// Sets the top padding as a percentage of the parent container width.
3014    pub fn padding_top_pct(self, padding: f64) -> Self {
3015        self.padding_top(padding.pct())
3016    }
3017
3018    /// Sets the bottom padding as a percentage of the parent container width.
3019    pub fn padding_bottom_pct(self, padding: f64) -> Self {
3020        self.padding_bottom(padding.pct())
3021    }
3022
3023    /// Set padding on all directions
3024    pub fn padding(self, padding: impl Into<Length>) -> Self {
3025        let padding = padding.into();
3026        self.set(PaddingLeft, padding)
3027            .set(PaddingTop, padding)
3028            .set(PaddingRight, padding)
3029            .set(PaddingBottom, padding)
3030    }
3031
3032    /// Sets padding on all sides as a percentage of the parent container width.
3033    pub fn padding_pct(self, padding: f64) -> Self {
3034        self.padding(padding.pct())
3035    }
3036
3037    /// Sets `padding_left` and `padding_right` to `padding`
3038    pub fn padding_horiz(self, padding: impl Into<Length>) -> Self {
3039        let padding = padding.into();
3040        self.set(PaddingLeft, padding).set(PaddingRight, padding)
3041    }
3042
3043    /// Sets horizontal padding as a percentage of the parent container width.
3044    pub fn padding_horiz_pct(self, padding: f64) -> Self {
3045        self.padding_horiz(padding.pct())
3046    }
3047
3048    /// Sets `padding_top` and `padding_bottom` to `padding`
3049    pub fn padding_vert(self, padding: impl Into<Length>) -> Self {
3050        let padding = padding.into();
3051        self.set(PaddingTop, padding).set(PaddingBottom, padding)
3052    }
3053
3054    /// Sets vertical padding as a percentage of the parent container width.
3055    pub fn padding_vert_pct(self, padding: f64) -> Self {
3056        self.padding_vert(padding.pct())
3057    }
3058
3059    /// Sets the left margin as a percentage of the parent container width.
3060    pub fn margin_left_pct(self, margin: f64) -> Self {
3061        self.margin_left(margin.pct())
3062    }
3063
3064    /// Sets the right margin as a percentage of the parent container width.
3065    pub fn margin_right_pct(self, margin: f64) -> Self {
3066        self.margin_right(margin.pct())
3067    }
3068
3069    /// Sets the top margin as a percentage of the parent container width.
3070    pub fn margin_top_pct(self, margin: f64) -> Self {
3071        self.margin_top(margin.pct())
3072    }
3073
3074    /// Sets the bottom margin as a percentage of the parent container width.
3075    pub fn margin_bottom_pct(self, margin: f64) -> Self {
3076        self.margin_bottom(margin.pct())
3077    }
3078
3079    /// Sets margin on all sides of the view.
3080    pub fn margin(self, margin: impl Into<LengthAuto>) -> Self {
3081        let margin = margin.into();
3082        self.set(MarginLeft, margin)
3083            .set(MarginTop, margin)
3084            .set(MarginRight, margin)
3085            .set(MarginBottom, margin)
3086    }
3087
3088    /// Sets margin on all sides as a percentage of the parent container width.
3089    pub fn margin_pct(self, margin: f64) -> Self {
3090        self.margin(margin.pct())
3091    }
3092
3093    /// Sets `margin_left` and `margin_right` to `margin`
3094    pub fn margin_horiz(self, margin: impl Into<LengthAuto>) -> Self {
3095        let margin = margin.into();
3096        self.set(MarginLeft, margin).set(MarginRight, margin)
3097    }
3098
3099    /// Sets horizontal margin as a percentage of the parent container width.
3100    pub fn margin_horiz_pct(self, margin: f64) -> Self {
3101        self.margin_horiz(margin.pct())
3102    }
3103
3104    /// Sets `margin_top` and `margin_bottom` to `margin`
3105    pub fn margin_vert(self, margin: impl Into<LengthAuto>) -> Self {
3106        let margin = margin.into();
3107        self.set(MarginTop, margin).set(MarginBottom, margin)
3108    }
3109
3110    /// Sets vertical margin as a percentage of the parent container width.
3111    pub fn margin_vert_pct(self, margin: f64) -> Self {
3112        self.margin_vert(margin.pct())
3113    }
3114
3115    /// Applies a complete padding configuration to the view.
3116    pub fn apply_padding(self, padding: Padding) -> Self {
3117        let mut style = self;
3118        if let Some(left) = padding.left {
3119            style = style.set(PaddingLeft, left);
3120        }
3121        if let Some(top) = padding.top {
3122            style = style.set(PaddingTop, top);
3123        }
3124        if let Some(right) = padding.right {
3125            style = style.set(PaddingRight, right);
3126        }
3127        if let Some(bottom) = padding.bottom {
3128            style = style.set(PaddingBottom, bottom);
3129        }
3130        style
3131    }
3132    /// Applies a complete margin configuration to the view.
3133    pub fn apply_margin(self, margin: Margin) -> Self {
3134        let mut style = self;
3135        if let Some(left) = margin.left {
3136            style = style.set(MarginLeft, left);
3137        }
3138        if let Some(top) = margin.top {
3139            style = style.set(MarginTop, top);
3140        }
3141        if let Some(right) = margin.right {
3142            style = style.set(MarginRight, right);
3143        }
3144        if let Some(bottom) = margin.bottom {
3145            style = style.set(MarginBottom, bottom);
3146        }
3147        style
3148    }
3149
3150    /// Sets the border radius for all corners of the view.
3151    pub fn border_radius(self, radius: impl Into<Length>) -> Self {
3152        let radius = radius.into();
3153        self.set(BorderTopLeftRadius, radius)
3154            .set(BorderTopRightRadius, radius)
3155            .set(BorderBottomLeftRadius, radius)
3156            .set(BorderBottomRightRadius, radius)
3157    }
3158
3159    /// Applies a complete border configuration to the view.
3160    pub fn apply_border(self, border: Border) -> Self {
3161        let mut style = self;
3162        if let Some(left) = border.left {
3163            style = style.set(BorderLeft, left);
3164        }
3165        if let Some(top) = border.top {
3166            style = style.set(BorderTop, top);
3167        }
3168        if let Some(right) = border.right {
3169            style = style.set(BorderRight, right);
3170        }
3171        if let Some(bottom) = border.bottom {
3172            style = style.set(BorderBottom, bottom);
3173        }
3174        style
3175    }
3176    /// Applies a complete border color configuration to the view.
3177    pub fn apply_border_color(self, border_color: BorderColor) -> Self {
3178        let mut style = self;
3179        if let Some(left) = border_color.left {
3180            style = style.set(BorderLeftColor, Some(left));
3181        }
3182        if let Some(top) = border_color.top {
3183            style = style.set(BorderTopColor, Some(top));
3184        }
3185        if let Some(right) = border_color.right {
3186            style = style.set(BorderRightColor, Some(right));
3187        }
3188        if let Some(bottom) = border_color.bottom {
3189            style = style.set(BorderBottomColor, Some(bottom));
3190        }
3191        style
3192    }
3193    /// Applies a complete border radius configuration to the view.
3194    pub fn apply_border_radius(self, border_radius: BorderRadius) -> Self {
3195        let mut style = self;
3196        if let Some(top_left) = border_radius.top_left {
3197            style = style.set(BorderTopLeftRadius, top_left);
3198        }
3199        if let Some(top_right) = border_radius.top_right {
3200            style = style.set(BorderTopRightRadius, top_right);
3201        }
3202        if let Some(bottom_left) = border_radius.bottom_left {
3203            style = style.set(BorderBottomLeftRadius, bottom_left);
3204        }
3205        if let Some(bottom_right) = border_radius.bottom_right {
3206            style = style.set(BorderBottomRightRadius, bottom_right);
3207        }
3208        style
3209    }
3210
3211    /// Sets the left inset as a percentage of the parent container width.
3212    pub fn inset_left_pct(self, inset: f64) -> Self {
3213        self.inset_left(inset.pct())
3214    }
3215
3216    /// Sets the right inset as a percentage of the parent container width.
3217    pub fn inset_right_pct(self, inset: f64) -> Self {
3218        self.inset_right(inset.pct())
3219    }
3220
3221    /// Sets the top inset as a percentage of the parent container height.
3222    pub fn inset_top_pct(self, inset: f64) -> Self {
3223        self.inset_top(inset.pct())
3224    }
3225
3226    /// Sets the bottom inset as a percentage of the parent container height.
3227    pub fn inset_bottom_pct(self, inset: f64) -> Self {
3228        self.inset_bottom(inset.pct())
3229    }
3230
3231    /// Sets all insets (left, top, right, bottom) to the same value.
3232    pub fn inset(self, inset: impl Into<LengthAuto>) -> Self {
3233        let inset = inset.into();
3234        self.inset_left(inset)
3235            .inset_top(inset)
3236            .inset_right(inset)
3237            .inset_bottom(inset)
3238    }
3239
3240    /// Sets all insets as percentages of the parent container.
3241    pub fn inset_pct(self, inset: f64) -> Self {
3242        let inset = inset.pct();
3243        self.inset_left(inset)
3244            .inset_top(inset)
3245            .inset_right(inset)
3246            .inset_bottom(inset)
3247    }
3248
3249    /// Specifies shadow blur. The larger this value, the bigger the blur,
3250    /// so the shadow becomes bigger and lighter.
3251    pub fn box_shadow_blur(self, blur_radius: impl Into<Length>) -> Self {
3252        let mut value = self.get(BoxShadowProp);
3253        if let Some(v) = value.first_mut() {
3254            v.blur_radius = blur_radius.into();
3255        } else {
3256            value.push(BoxShadow {
3257                blur_radius: blur_radius.into(),
3258                ..Default::default()
3259            });
3260        }
3261        self.set(BoxShadowProp, value)
3262    }
3263
3264    /// Specifies color for the shadow.
3265    pub fn box_shadow_color(self, color: Color) -> Self {
3266        let mut value = self.get(BoxShadowProp);
3267        if let Some(v) = value.first_mut() {
3268            v.color = color;
3269        } else {
3270            value.push(BoxShadow {
3271                color,
3272                ..Default::default()
3273            });
3274        }
3275        self.set(BoxShadowProp, value)
3276    }
3277
3278    /// Specifies shadow blur spread. Positive values will cause the shadow
3279    /// to expand and grow bigger, negative values will cause the shadow to shrink.
3280    pub fn box_shadow_spread(self, spread: impl Into<Length>) -> Self {
3281        let mut value = self.get(BoxShadowProp);
3282        if let Some(v) = value.first_mut() {
3283            v.spread = spread.into();
3284        } else {
3285            value.push(BoxShadow {
3286                spread: spread.into(),
3287                ..Default::default()
3288            });
3289        }
3290        self.set(BoxShadowProp, value)
3291    }
3292
3293    /// Applies a shadow for the stylized view. Use [BoxShadow] builder
3294    /// to construct each shadow.
3295    /// ```rust
3296    /// use floem::prelude::*;
3297    /// use floem::prelude::palette::css;
3298    /// use floem::style::BoxShadow;
3299    ///
3300    /// empty().style(|s| s.apply_box_shadows(vec![
3301    ///    BoxShadow::new()
3302    ///        .color(css::BLACK)
3303    ///        .top_offset(5.)
3304    ///        .bottom_offset(-30.)
3305    ///        .right_offset(-20.)
3306    ///        .left_offset(10.)
3307    ///        .blur_radius(5.)
3308    ///        .spread(10.)
3309    /// ]));
3310    /// ```
3311    /// ### Info
3312    /// If you only specify one shadow on the view, use standard style methods directly
3313    /// on [Style] struct:
3314    /// ```rust
3315    /// use floem::prelude::*;
3316    /// empty().style(|s| s
3317    ///     .box_shadow_top_offset(-5.)
3318    ///     .box_shadow_bottom_offset(30.)
3319    ///     .box_shadow_right_offset(20.)
3320    ///     .box_shadow_left_offset(-10.)
3321    ///     .box_shadow_spread(1.)
3322    ///     .box_shadow_blur(3.)
3323    /// );
3324    /// ```
3325    pub fn apply_box_shadows(self, shadow: impl Into<SmallVec<[BoxShadow; 3]>>) -> Self {
3326        self.set(BoxShadowProp, shadow.into())
3327    }
3328
3329    /// Specifies the offset on horizontal axis.
3330    /// Negative offset value places the shadow to the left of the view.
3331    pub fn box_shadow_h_offset(self, h_offset: impl Into<Length>) -> Self {
3332        let mut value = self.get(BoxShadowProp);
3333        let offset = h_offset.into();
3334        if let Some(v) = value.first_mut() {
3335            v.left_offset = -offset;
3336            v.right_offset = offset;
3337        } else {
3338            value.push(BoxShadow {
3339                left_offset: -offset,
3340                right_offset: offset,
3341                ..Default::default()
3342            });
3343        }
3344        self.set(BoxShadowProp, value)
3345    }
3346
3347    /// Specifies the offset on vertical axis.
3348    /// Negative offset value places the shadow above the view.
3349    pub fn box_shadow_v_offset(self, v_offset: impl Into<Length>) -> Self {
3350        let mut value = self.get(BoxShadowProp);
3351        let offset = v_offset.into();
3352        if let Some(v) = value.first_mut() {
3353            v.top_offset = -offset;
3354            v.bottom_offset = offset;
3355        } else {
3356            value.push(BoxShadow {
3357                top_offset: -offset,
3358                bottom_offset: offset,
3359                ..Default::default()
3360            });
3361        }
3362        self.set(BoxShadowProp, value)
3363    }
3364
3365    /// Specifies the offset of the left edge.
3366    pub fn box_shadow_left_offset(self, left_offset: impl Into<Length>) -> Self {
3367        let mut value = self.get(BoxShadowProp);
3368        if let Some(v) = value.first_mut() {
3369            v.left_offset = left_offset.into();
3370        } else {
3371            value.push(BoxShadow {
3372                left_offset: left_offset.into(),
3373                ..Default::default()
3374            });
3375        }
3376        self.set(BoxShadowProp, value)
3377    }
3378
3379    /// Specifies the offset of the right edge.
3380    pub fn box_shadow_right_offset(self, right_offset: impl Into<Length>) -> Self {
3381        let mut value = self.get(BoxShadowProp);
3382        if let Some(v) = value.first_mut() {
3383            v.right_offset = right_offset.into();
3384        } else {
3385            value.push(BoxShadow {
3386                right_offset: right_offset.into(),
3387                ..Default::default()
3388            });
3389        }
3390        self.set(BoxShadowProp, value)
3391    }
3392
3393    /// Specifies the offset of the top edge.
3394    pub fn box_shadow_top_offset(self, top_offset: impl Into<Length>) -> Self {
3395        let mut value = self.get(BoxShadowProp);
3396        if let Some(v) = value.first_mut() {
3397            v.top_offset = top_offset.into();
3398        } else {
3399            value.push(BoxShadow {
3400                top_offset: top_offset.into(),
3401                ..Default::default()
3402            });
3403        }
3404        self.set(BoxShadowProp, value)
3405    }
3406
3407    /// Specifies the offset of the bottom edge.
3408    pub fn box_shadow_bottom_offset(self, bottom_offset: impl Into<Length>) -> Self {
3409        let mut value = self.get(BoxShadowProp);
3410        if let Some(v) = value.first_mut() {
3411            v.bottom_offset = bottom_offset.into();
3412        } else {
3413            value.push(BoxShadow {
3414                bottom_offset: bottom_offset.into(),
3415                ..Default::default()
3416            });
3417        }
3418        self.set(BoxShadowProp, value)
3419    }
3420
3421    /// Sets the font weight to bold.
3422    pub fn font_bold(self) -> Self {
3423        self.font_weight(FontWeightProp::BOLD)
3424    }
3425
3426    /// Enables pointer events for the view (allows mouse interaction).
3427    pub fn pointer_events_auto(self) -> Self {
3428        self.pointer_events(PointerEvents::Auto)
3429    }
3430
3431    /// Disables pointer events for the view (mouse events pass through).
3432    pub fn pointer_events_none(self) -> Self {
3433        self.pointer_events(PointerEvents::None)
3434    }
3435
3436    /// Sets text overflow to show ellipsis (...) when text is clipped.
3437    pub fn text_ellipsis(self) -> Self {
3438        self.text_overflow(TextOverflow::NoWrap(NoWrapOverflow::Ellipsis))
3439    }
3440
3441    /// Sets text overflow to clip text without showing ellipsis.
3442    pub fn text_clip(self) -> Self {
3443        self.text_overflow(TextOverflow::NoWrap(NoWrapOverflow::Clip))
3444    }
3445
3446    /// Sets text to wrap using Parley's normal overflow-wrap behavior.
3447    pub fn text_wrap(self) -> Self {
3448        self.text_overflow(TextOverflow::Wrap {
3449            overflow_wrap: OverflowWrap::Normal,
3450            word_break: WordBreakStrength::Normal,
3451        })
3452    }
3453
3454    /// Sets the view to absolute positioning.
3455    pub fn absolute(self) -> Self {
3456        self.position(taffy::style::Position::Absolute)
3457    }
3458
3459    /// Sets the view to fixed positioning relative to the viewport.
3460    ///
3461    /// This is similar to CSS `position: fixed`. The view will:
3462    /// - Be positioned relative to the window viewport
3463    /// - Use `inset` properties relative to the viewport
3464    /// - Have percentage sizes relative to the viewport
3465    /// - Be painted above all other content
3466    ///
3467    /// # Example
3468    /// ```rust
3469    /// use floem::style::Style;
3470    ///
3471    /// // Create a full-screen overlay
3472    /// Style::new().fixed().inset(0.0);
3473    /// ```
3474    pub fn fixed(self) -> Self {
3475        self.position(taffy::style::Position::Absolute)
3476            .is_fixed(true)
3477    }
3478
3479    /// Aligns flex items to stretch and fill the cross axis.
3480    pub fn items_stretch(self) -> Self {
3481        self.align_items(taffy::style::AlignItems::Stretch)
3482    }
3483
3484    /// Aligns flex items to the start of the cross axis.
3485    pub fn items_start(self) -> Self {
3486        self.align_items(taffy::style::AlignItems::FlexStart)
3487    }
3488
3489    /// Defines the alignment along the cross axis as Centered
3490    pub fn items_center(self) -> Self {
3491        self.align_items(taffy::style::AlignItems::Center)
3492    }
3493
3494    /// Aligns flex items to the end of the cross axis.
3495    pub fn items_end(self) -> Self {
3496        self.align_items(taffy::style::AlignItems::FlexEnd)
3497    }
3498
3499    /// Aligns flex items along their baselines.
3500    pub fn items_baseline(self) -> Self {
3501        self.align_items(taffy::style::AlignItems::Baseline)
3502    }
3503
3504    /// Aligns flex items to the start of the main axis.
3505    pub fn justify_start(self) -> Self {
3506        self.justify_content(taffy::style::JustifyContent::FlexStart)
3507    }
3508
3509    /// Aligns flex items to the end of the main axis.
3510    pub fn justify_end(self) -> Self {
3511        self.justify_content(taffy::style::JustifyContent::FlexEnd)
3512    }
3513
3514    /// Defines the alignment along the main axis as Centered
3515    pub fn justify_center(self) -> Self {
3516        self.justify_content(taffy::style::JustifyContent::Center)
3517    }
3518
3519    /// Distributes flex items with space between them.
3520    pub fn justify_between(self) -> Self {
3521        self.justify_content(taffy::style::JustifyContent::SpaceBetween)
3522    }
3523
3524    /// Distributes flex items with space around them.
3525    pub fn justify_around(self) -> Self {
3526        self.justify_content(taffy::style::JustifyContent::SpaceAround)
3527    }
3528
3529    /// Distributes flex items with equal space around them.
3530    pub fn justify_evenly(self) -> Self {
3531        self.justify_content(taffy::style::JustifyContent::SpaceEvenly)
3532    }
3533
3534    /// Hides the view from view and layout.
3535    pub fn hide(self) -> Self {
3536        self.set(DisplayProp, Display::None)
3537    }
3538
3539    /// Sets the view to use flexbox layout.
3540    pub fn flex(self) -> Self {
3541        self.display(taffy::style::Display::Flex)
3542    }
3543
3544    /// Sets the view to use grid layout.
3545    pub fn grid(self) -> Self {
3546        self.display(taffy::style::Display::Grid)
3547    }
3548
3549    /// Sets flex direction to row (horizontal).
3550    pub fn flex_row(self) -> Self {
3551        self.flex_direction(taffy::style::FlexDirection::Row)
3552    }
3553
3554    /// Sets flex direction to column (vertical).
3555    pub fn flex_col(self) -> Self {
3556        self.flex_direction(taffy::style::FlexDirection::Column)
3557    }
3558
3559    /// Sets uniform scaling for both X and Y axes.
3560    pub fn scale(self, scale: impl Into<Pct>) -> Self {
3561        let val = scale.into();
3562        self.scale_x(val).scale_y(val)
3563    }
3564
3565    /// Allow the application of a function if the option exists.
3566    /// This is useful for chaining together a bunch of optional style changes.
3567    /// ```rust
3568    /// use floem::style::Style;
3569    /// let maybe_none: Option<i32> = None;
3570    /// let style = Style::default()
3571    ///     .apply_opt(Some(5.0), Style::padding) // ran
3572    ///     .apply_opt(maybe_none, Style::margin) // not ran
3573    ///     .apply_opt(Some(5.0), |s, v| s.border_right(v * 2.0))
3574    ///     .border_left(5.0); // ran, obviously
3575    /// ```
3576    pub fn apply_opt<T>(self, opt: Option<T>, f: impl FnOnce(Self, T) -> Self) -> Self {
3577        if let Some(t) = opt { f(self, t) } else { self }
3578    }
3579
3580    /// Allow the application of a function if the condition holds.
3581    /// This is useful for chaining together optional style changes.
3582    /// ```rust
3583    /// use floem::style::Style;
3584    /// let style = Style::default()
3585    ///     .apply_if(true, |s| s.padding(5.0)) // ran
3586    ///     .apply_if(false, |s| s.margin(5.0)); // not ran
3587    /// ```
3588    pub fn apply_if(self, cond: bool, f: impl FnOnce(Self) -> Self) -> Self {
3589        if cond { f(self) } else { self }
3590    }
3591
3592    /// Applies a `CustomStyle` type into this style.
3593    ///
3594    /// # Examples
3595    /// ```
3596    /// use floem::prelude::*;
3597    /// text("test").style(|s| s.custom(|s: LabelCustomStyle| s.selectable(false)));
3598    /// ```
3599    ///
3600    /// See also: [`apply_custom`](Self::apply_custom), [`custom_style_class`](Self::custom_style_class)
3601    pub fn custom<CS: CustomStyle>(self, custom: impl FnOnce(CS) -> CS) -> Self {
3602        self.apply(custom(CS::default()).into())
3603    }
3604
3605    /// Applies a `CustomStyle` type into this style.
3606    ///
3607    /// # Examples
3608    /// ```
3609    /// use floem::prelude::*;
3610    /// text("test").style(|s| s.apply_custom(LabelCustomStyle::new().selectable(false)));
3611    /// ```
3612    ///
3613    /// See also: [`custom`](Self::custom), [`custom_style_class`](Self::custom_style_class)
3614    pub fn apply_custom<CS: Into<Style>>(self, custom_style: CS) -> Self {
3615        self.apply(custom_style.into())
3616    }
3617}
3618
3619impl Style {
3620    pub(crate) fn font_size_cx(&self) -> FontSizeCx {
3621        let builtin = self.builtin();
3622        let font_size = builtin.font_size();
3623        let line_height = builtin.line_height();
3624        let line_height = line_height.resolve(font_size as f32);
3625        FontSizeCx::new(font_size, line_height as f64)
3626    }
3627
3628    pub fn to_taffy_style(&self) -> TaffyStyle {
3629        let style = self.builtin();
3630        let font_size_cx = self.font_size_cx();
3631        TaffyStyle {
3632            display: style.display(),
3633            overflow: taffy::Point {
3634                x: self.get(OverflowX),
3635                y: self.get(OverflowY),
3636            },
3637            position: style.position(),
3638            size: taffy::prelude::Size {
3639                width: style.width().to_taffy_dim(&font_size_cx),
3640                height: style.height().to_taffy_dim(&font_size_cx),
3641            },
3642            min_size: taffy::prelude::Size {
3643                width: style.min_width().to_taffy_dim(&font_size_cx),
3644                height: style.min_height().to_taffy_dim(&font_size_cx),
3645            },
3646            max_size: taffy::prelude::Size {
3647                width: style.max_width().to_taffy_dim(&font_size_cx),
3648                height: style.max_height().to_taffy_dim(&font_size_cx),
3649            },
3650            flex_direction: style.flex_direction(),
3651            flex_grow: style.flex_grow(),
3652            flex_shrink: style.flex_shrink(),
3653            flex_basis: style.flex_basis().to_taffy_dim(&font_size_cx),
3654            flex_wrap: style.flex_wrap(),
3655            justify_content: style.justify_content(),
3656            justify_self: style.justify_self(),
3657            justify_items: style.justify_items(),
3658            align_items: style.align_items(),
3659            align_content: style.align_content(),
3660            align_self: style.align_self(),
3661            aspect_ratio: style.aspect_ratio(),
3662            border: {
3663                Rect {
3664                    left: LengthPercentage::length(style.border_left().width as f32),
3665                    top: LengthPercentage::length(style.border_top().width as f32),
3666                    right: LengthPercentage::length(style.border_right().width as f32),
3667                    bottom: LengthPercentage::length(style.border_bottom().width as f32),
3668                }
3669            },
3670            padding: {
3671                Rect {
3672                    left: style.padding_left().to_taffy(&font_size_cx),
3673                    top: style.padding_top().to_taffy(&font_size_cx),
3674                    right: style.padding_right().to_taffy(&font_size_cx),
3675                    bottom: style.padding_bottom().to_taffy(&font_size_cx),
3676                }
3677            },
3678            margin: {
3679                Rect {
3680                    left: style.margin_left().to_taffy_len_perc_auto(&font_size_cx),
3681                    top: style.margin_top().to_taffy_len_perc_auto(&font_size_cx),
3682                    right: style.margin_right().to_taffy_len_perc_auto(&font_size_cx),
3683                    bottom: style.margin_bottom().to_taffy_len_perc_auto(&font_size_cx),
3684                }
3685            },
3686            inset: Rect {
3687                left: style.inset_left().to_taffy_len_perc_auto(&font_size_cx),
3688                top: style.inset_top().to_taffy_len_perc_auto(&font_size_cx),
3689                right: style.inset_right().to_taffy_len_perc_auto(&font_size_cx),
3690                bottom: style.inset_bottom().to_taffy_len_perc_auto(&font_size_cx),
3691            },
3692            gap: Size {
3693                width: style.col_gap().to_taffy(&font_size_cx),
3694                height: style.row_gap().to_taffy(&font_size_cx),
3695            },
3696            grid_template_rows: style.grid_template_rows(),
3697            grid_template_columns: style.grid_template_columns(),
3698            grid_row: style.grid_row(),
3699            grid_column: style.grid_column(),
3700            grid_auto_rows: style.grid_auto_rows(),
3701            grid_auto_columns: style.grid_auto_columns(),
3702            grid_auto_flow: style.grid_auto_flow(),
3703            scrollbar_width: style.scrollbar_width().0 as f32,
3704            ..Default::default()
3705        }
3706    }
3707}