Skip to main content

blinc_layout/
div.rs

1//! GPUI-style div builder with tailwind-style methods
2//!
3//! Provides a fluent builder API for creating layout elements:
4//! ```rust
5//! use blinc_layout::prelude::*;
6//! use blinc_core::Color;
7//!
8//! let ui = div()
9//!     .flex_row()
10//!     .gap(4.0)
11//!     .p(2.0)
12//!     .bg(Color::RED)
13//!     .child(text("Hello"));
14//! ```
15
16use std::sync::{
17    atomic::{AtomicBool, Ordering},
18    Arc, Mutex,
19};
20
21use blinc_core::{
22    BlurQuality, BlurStyle, Brush, ClipPath, Color, CornerRadius, CornerShape, LayerEffect,
23    OverflowFade, Shadow, Transform,
24};
25use blinc_theme::ThemeState;
26use taffy::prelude::*;
27use taffy::Overflow;
28
29use crate::element::{
30    ElementBounds, GlassMaterial, Material, MetallicMaterial, RenderLayer, RenderProps,
31    WoodMaterial,
32};
33use crate::element_style::ElementStyle;
34use crate::tree::{LayoutNodeId, LayoutTree};
35
36// ============================================================================
37// ElementRef - Generic reference binding for external access
38// ============================================================================
39
40/// Shared storage for element references
41type RefStorage<T> = Arc<Mutex<Option<T>>>;
42
43/// Shared dirty flag for automatic rebuild triggering
44type DirtyFlag = Arc<AtomicBool>;
45
46/// A generic reference binding to an element that can be accessed externally
47///
48/// Similar to React's `useRef`, this allows capturing a reference to an element
49/// for external manipulation while maintaining the fluent API flow.
50///
51/// # Example
52///
53/// ```ignore
54/// use blinc_layout::prelude::*;
55///
56/// // Create a reference
57/// let button_ref = ElementRef::<StatefulButton>::new();
58///
59/// // Build UI - .bind() works seamlessly in the fluent chain
60/// let ui = div()
61///     .flex_col()
62///     .child(
63///         stateful_button()
64///             .bind(&button_ref)  // Binds AND continues the chain
65///             .on_state(|state, div| { ... })
66///     );
67///
68/// // Later, access the bound element's full API
69/// button_ref.with_mut(|btn| {
70///     btn.dispatch_state(ButtonState::Pressed);
71/// });
72/// ```
73/// Storage for computed layout bounds
74type LayoutBoundsStorage = Arc<Mutex<Option<ElementBounds>>>;
75
76pub struct ElementRef<T> {
77    inner: RefStorage<T>,
78    /// Shared dirty flag - when set, signals that the UI needs to be rebuilt
79    dirty_flag: DirtyFlag,
80    /// Computed layout bounds (set after layout is computed)
81    layout_bounds: LayoutBoundsStorage,
82}
83
84impl<T> Clone for ElementRef<T> {
85    fn clone(&self) -> Self {
86        Self {
87            inner: Arc::clone(&self.inner),
88            dirty_flag: Arc::clone(&self.dirty_flag),
89            layout_bounds: Arc::clone(&self.layout_bounds),
90        }
91    }
92}
93
94impl<T> Default for ElementRef<T> {
95    fn default() -> Self {
96        Self::new()
97    }
98}
99
100impl<T> ElementRef<T> {
101    /// Create a new empty ElementRef
102    pub fn new() -> Self {
103        Self {
104            inner: Arc::new(Mutex::new(None)),
105            dirty_flag: Arc::new(AtomicBool::new(false)),
106            layout_bounds: Arc::new(Mutex::new(None)),
107        }
108    }
109
110    /// Create an ElementRef with a shared dirty flag
111    ///
112    /// This is used internally to share the same dirty flag across
113    /// multiple refs, allowing the windowed app to check for changes.
114    pub fn with_dirty_flag(dirty_flag: DirtyFlag) -> Self {
115        Self {
116            inner: Arc::new(Mutex::new(None)),
117            dirty_flag,
118            layout_bounds: Arc::new(Mutex::new(None)),
119        }
120    }
121
122    /// Get the dirty flag handle (for sharing with other refs)
123    pub fn dirty_flag(&self) -> DirtyFlag {
124        Arc::clone(&self.dirty_flag)
125    }
126
127    /// Check if the element was modified and clear the flag
128    ///
129    /// Returns `true` if the element was modified since the last check.
130    pub fn take_dirty(&self) -> bool {
131        self.dirty_flag.swap(false, Ordering::SeqCst)
132    }
133
134    /// Check if the element was modified (without clearing)
135    pub fn is_dirty(&self) -> bool {
136        self.dirty_flag.load(Ordering::SeqCst)
137    }
138
139    /// Mark the element as dirty (needs rebuild)
140    pub fn mark_dirty(&self) {
141        self.dirty_flag.store(true, Ordering::SeqCst);
142    }
143
144    /// Check if an element is bound to this reference
145    pub fn is_bound(&self) -> bool {
146        self.inner.lock().unwrap().is_some()
147    }
148
149    /// Get the internal storage handle for shared access
150    ///
151    /// This is used by `.bind()` implementations to share storage
152    /// between the bound element wrapper and this ref.
153    pub fn storage(&self) -> RefStorage<T> {
154        Arc::clone(&self.inner)
155    }
156
157    /// Set the element in storage (used by bind implementations)
158    pub fn set(&self, elem: T) {
159        *self.inner.lock().unwrap() = Some(elem);
160    }
161
162    /// Access the bound element immutably with a callback
163    ///
164    /// Returns `Some(result)` if an element is bound, `None` otherwise.
165    ///
166    /// # Example
167    ///
168    /// ```ignore
169    /// let state = button_ref.with(|btn| *btn.state());
170    /// ```
171    pub fn with<F, R>(&self, f: F) -> Option<R>
172    where
173        F: FnOnce(&T) -> R,
174    {
175        self.inner.lock().unwrap().as_ref().map(f)
176    }
177
178    /// Access the bound element mutably with a callback
179    ///
180    /// Returns `Some(result)` if an element is bound, `None` otherwise.
181    /// This is the primary way to call methods on the bound element.
182    ///
183    /// # Example
184    ///
185    /// ```ignore
186    /// // Dispatch state changes
187    /// button_ref.with_mut(|btn| {
188    ///     btn.dispatch_state(ButtonState::Pressed);
189    /// });
190    ///
191    /// // Modify element styling
192    /// div_ref.with_mut(|div| {
193    ///     *div = div.swap().bg(Color::RED).rounded(8.0);
194    /// });
195    /// ```
196    ///
197    /// **Note:** This automatically marks the element as dirty after the callback,
198    /// triggering a UI rebuild.
199    pub fn with_mut<F, R>(&self, f: F) -> Option<R>
200    where
201        F: FnOnce(&mut T) -> R,
202    {
203        let result = self.inner.lock().unwrap().as_mut().map(f);
204        if result.is_some() {
205            // Mark dirty after successful mutation
206            self.dirty_flag.store(true, Ordering::SeqCst);
207        }
208        result
209    }
210
211    /// Get a clone of the bound element, if any
212    pub fn get(&self) -> Option<T>
213    where
214        T: Clone,
215    {
216        self.inner.lock().unwrap().clone()
217    }
218
219    /// Replace the bound element with a new one, returning the old value
220    ///
221    /// **Note:** This automatically marks the element as dirty, triggering a UI rebuild.
222    pub fn replace(&self, new_elem: T) -> Option<T> {
223        let old = self.inner.lock().unwrap().replace(new_elem);
224        self.dirty_flag.store(true, Ordering::SeqCst);
225        old
226    }
227
228    /// Take the bound element out of the reference, leaving None
229    pub fn take(&self) -> Option<T> {
230        self.inner.lock().unwrap().take()
231    }
232
233    /// Borrow the bound element immutably
234    ///
235    /// Returns a guard that dereferences to &T. Panics if not bound.
236    /// For fallible access, use `with()` instead.
237    ///
238    /// # Example
239    ///
240    /// ```ignore
241    /// let state = button_ref.borrow().state();
242    /// ```
243    ///
244    /// # Panics
245    ///
246    /// Panics if no element is bound to this reference.
247    pub fn borrow(&self) -> ElementRefGuard<'_, T> {
248        ElementRefGuard {
249            guard: self.inner.lock().unwrap(),
250        }
251    }
252
253    /// Borrow the bound element mutably
254    ///
255    /// Returns a guard that dereferences to &mut T. Panics if not bound.
256    /// **When the guard is dropped, the element is automatically marked dirty**,
257    /// triggering a UI rebuild.
258    ///
259    /// For fallible access, use `with_mut()` instead.
260    ///
261    /// # Example
262    ///
263    /// ```ignore
264    /// // This automatically triggers a rebuild when the guard is dropped
265    /// button_ref.borrow_mut().dispatch_state(ButtonState::Hovered);
266    /// ```
267    ///
268    /// # Panics
269    ///
270    /// Panics if no element is bound to this reference.
271    pub fn borrow_mut(&self) -> ElementRefGuardMut<'_, T> {
272        ElementRefGuardMut {
273            guard: self.inner.lock().unwrap(),
274            dirty_flag: Arc::clone(&self.dirty_flag),
275        }
276    }
277
278    // =========================================================================
279    // Layout Bounds
280    // =========================================================================
281
282    /// Get the computed layout bounds for this element
283    ///
284    /// Returns `None` if layout hasn't been computed yet or if the element
285    /// is not part of the layout tree.
286    ///
287    /// # Example
288    ///
289    /// ```ignore
290    /// if let Some(bounds) = input_ref.get_layout_bounds() {
291    ///     println!("Width: {}, Height: {}", bounds.width, bounds.height);
292    /// }
293    /// ```
294    pub fn get_layout_bounds(&self) -> Option<ElementBounds> {
295        *self.layout_bounds.lock().unwrap()
296    }
297
298    /// Set the computed layout bounds for this element
299    ///
300    /// This is called internally after layout is computed to store
301    /// the element's position and dimensions.
302    pub fn set_layout_bounds(&self, bounds: ElementBounds) {
303        *self.layout_bounds.lock().unwrap() = Some(bounds);
304    }
305
306    /// Clear the stored layout bounds
307    ///
308    /// Called when the element is removed from the layout tree or
309    /// when layout needs to be recomputed.
310    pub fn clear_layout_bounds(&self) {
311        *self.layout_bounds.lock().unwrap() = None;
312    }
313
314    /// Get the layout bounds storage handle for sharing
315    ///
316    /// This allows other parts of the system to update the layout bounds
317    /// when layout is computed.
318    pub fn layout_bounds_storage(&self) -> LayoutBoundsStorage {
319        Arc::clone(&self.layout_bounds)
320    }
321}
322
323/// Guard for immutable access to a bound element
324pub struct ElementRefGuard<'a, T> {
325    guard: std::sync::MutexGuard<'a, Option<T>>,
326}
327
328impl<T> std::ops::Deref for ElementRefGuard<'_, T> {
329    type Target = T;
330
331    fn deref(&self) -> &Self::Target {
332        self.guard.as_ref().expect("ElementRef not bound")
333    }
334}
335
336/// Guard for mutable access to a bound element
337///
338/// When this guard is dropped, the dirty flag is automatically set,
339/// signaling that the UI needs to be rebuilt.
340pub struct ElementRefGuardMut<'a, T> {
341    guard: std::sync::MutexGuard<'a, Option<T>>,
342    dirty_flag: DirtyFlag,
343}
344
345impl<T> std::ops::Deref for ElementRefGuardMut<'_, T> {
346    type Target = T;
347
348    fn deref(&self) -> &Self::Target {
349        self.guard.as_ref().expect("ElementRef not bound")
350    }
351}
352
353impl<T> std::ops::DerefMut for ElementRefGuardMut<'_, T> {
354    fn deref_mut(&mut self) -> &mut Self::Target {
355        self.guard.as_mut().expect("ElementRef not bound")
356    }
357}
358
359impl<T> Drop for ElementRefGuardMut<'_, T> {
360    fn drop(&mut self) {
361        // Mark dirty when the mutable borrow ends - user modified the element
362        self.dirty_flag.store(true, Ordering::SeqCst);
363    }
364}
365
366/// Type alias for Div references
367pub type DivRef = ElementRef<Div>;
368
369/// A div element builder with GPUI/Tailwind-style methods
370pub struct Div {
371    pub(crate) style: Style,
372    pub(crate) children: Vec<Box<dyn ElementBuilder>>,
373    pub(crate) background: Option<Brush>,
374    pub(crate) border_radius: CornerRadius,
375    /// Tracks whether border_radius was explicitly set (distinguishes "set to 0" from "not set" in merges)
376    pub(crate) border_radius_explicit: bool,
377    pub(crate) corner_shape: CornerShape,
378    pub(crate) border_color: Option<Color>,
379    pub(crate) border_width: f32,
380    pub(crate) border_sides: crate::element::BorderSides,
381    pub(crate) render_layer: RenderLayer,
382    pub(crate) material: Option<Material>,
383    pub(crate) shadow: Option<Shadow>,
384    pub(crate) transform: Option<Transform>,
385    pub(crate) opacity: f32,
386    pub(crate) cursor: Option<crate::element::CursorStyle>,
387    pub(crate) pointer_events_none: bool,
388    /// Layer effects (blur, drop shadow, glow, color matrix) applied to this element
389    pub(crate) layer_effects: Vec<LayerEffect>,
390    /// Marks this as a stack layer for z-ordering (increments z_layer for interleaved rendering)
391    pub(crate) is_stack_layer: bool,
392    pub(crate) event_handlers: crate::event_handler::EventHandlers,
393    /// Element ID for selector API queries
394    pub(crate) element_id: Option<String>,
395    /// CSS class names for selector matching
396    pub(crate) classes: Vec<String>,
397    // 3D transform properties (stored separately to flow through to render_props)
398    pub(crate) rotate_x: Option<f32>,
399    pub(crate) rotate_y: Option<f32>,
400    pub(crate) perspective_3d: Option<f32>,
401    pub(crate) shape_3d: Option<f32>,
402    pub(crate) depth: Option<f32>,
403    pub(crate) light_direction: Option<[f32; 3]>,
404    pub(crate) light_intensity: Option<f32>,
405    pub(crate) ambient: Option<f32>,
406    pub(crate) specular: Option<f32>,
407    pub(crate) translate_z: Option<f32>,
408    pub(crate) op_3d: Option<f32>,
409    pub(crate) blend_3d: Option<f32>,
410    /// Overflow fade distances (smooth alpha fade at clip edges)
411    pub(crate) overflow_fade: OverflowFade,
412    /// CSS clip-path shape function
413    pub(crate) clip_path: Option<ClipPath>,
414    /// @flow shader name (references a FlowGraph in the stylesheet)
415    pub(crate) flow_name: Option<String>,
416    /// Direct @flow graph (from `flow!` macro), bypasses stylesheet lookup
417    pub(crate) flow_graph: Option<std::sync::Arc<blinc_core::FlowGraph>>,
418    /// Fixed positioning (stays in place when ancestors scroll)
419    pub(crate) is_fixed: bool,
420    /// Sticky positioning (sticks when scrolled past threshold)
421    pub(crate) is_sticky: bool,
422    /// Sticky top threshold
423    pub(crate) sticky_top: Option<f32>,
424    /// Outline width in pixels
425    pub(crate) outline_width: f32,
426    /// Outline color
427    pub(crate) outline_color: Option<Color>,
428    /// Outline offset in pixels (gap between border and outline)
429    pub(crate) outline_offset: f32,
430    /// CSS z-index for stacking order
431    pub(crate) z_index: i32,
432    /// Scroll physics for overflow:scroll containers
433    pub(crate) scroll_physics: Option<crate::scroll::SharedScrollPhysics>,
434    /// Layout animation configuration for FLIP-style bounds animation
435    pub(crate) layout_animation: Option<crate::layout_animation::LayoutAnimationConfig>,
436    /// Visual animation configuration (new FLIP-style system, read-only layout)
437    pub(crate) visual_animation: Option<crate::visual_animation::VisualAnimationConfig>,
438    /// Ancestor stateful context key for automatic key derivation
439    ///
440    /// When set, motion containers and layout animations will use this key
441    /// as a prefix for auto-generated stable keys.
442    pub(crate) stateful_context_key: Option<String>,
443}
444
445impl Default for Div {
446    fn default() -> Self {
447        Self::new()
448    }
449}
450
451impl Div {
452    /// Create a new div element
453    pub fn new() -> Self {
454        Self {
455            style: Style::default(),
456            children: Vec::new(),
457            background: None,
458            border_radius: CornerRadius::default(),
459            border_radius_explicit: false,
460            corner_shape: CornerShape::default(),
461            border_color: None,
462            border_width: 0.0,
463            border_sides: crate::element::BorderSides::default(),
464            render_layer: RenderLayer::default(),
465            material: None,
466            shadow: None,
467            transform: None,
468            opacity: 1.0,
469            cursor: None,
470            pointer_events_none: false,
471            layer_effects: Vec::new(),
472            is_stack_layer: false,
473            event_handlers: crate::event_handler::EventHandlers::new(),
474            element_id: None,
475            classes: Vec::new(),
476            rotate_x: None,
477            rotate_y: None,
478            perspective_3d: None,
479            shape_3d: None,
480            depth: None,
481            light_direction: None,
482            light_intensity: None,
483            ambient: None,
484            specular: None,
485            translate_z: None,
486            op_3d: None,
487            blend_3d: None,
488            overflow_fade: OverflowFade::default(),
489            clip_path: None,
490            flow_name: None,
491            flow_graph: None,
492            outline_width: 0.0,
493            outline_color: None,
494            outline_offset: 0.0,
495            is_fixed: false,
496            is_sticky: false,
497            sticky_top: None,
498            z_index: 0,
499            scroll_physics: None,
500            layout_animation: None,
501            visual_animation: None,
502            stateful_context_key: None,
503        }
504    }
505
506    /// Create a new div element with a pre-configured taffy Style
507    ///
508    /// This is useful for preserving layout properties (width, height, overflow, etc.)
509    /// when rebuilding subtrees in Stateful elements.
510    pub fn with_style(style: Style) -> Self {
511        Self {
512            style,
513            children: Vec::new(),
514            background: None,
515            border_radius: CornerRadius::default(),
516            border_radius_explicit: false,
517            corner_shape: CornerShape::default(),
518            border_color: None,
519            border_width: 0.0,
520            border_sides: crate::element::BorderSides::default(),
521            render_layer: RenderLayer::default(),
522            material: None,
523            shadow: None,
524            transform: None,
525            opacity: 1.0,
526            cursor: None,
527            pointer_events_none: false,
528            layer_effects: Vec::new(),
529            is_stack_layer: false,
530            event_handlers: crate::event_handler::EventHandlers::new(),
531            element_id: None,
532            classes: Vec::new(),
533            rotate_x: None,
534            rotate_y: None,
535            perspective_3d: None,
536            shape_3d: None,
537            depth: None,
538            light_direction: None,
539            light_intensity: None,
540            ambient: None,
541            specular: None,
542            translate_z: None,
543            op_3d: None,
544            blend_3d: None,
545            overflow_fade: OverflowFade::default(),
546            clip_path: None,
547            flow_name: None,
548            flow_graph: None,
549            outline_width: 0.0,
550            outline_color: None,
551            outline_offset: 0.0,
552            is_fixed: false,
553            is_sticky: false,
554            sticky_top: None,
555            z_index: 0,
556            scroll_physics: None,
557            layout_animation: None,
558            visual_animation: None,
559            stateful_context_key: None,
560        }
561    }
562
563    /// Set an element ID for selector API queries
564    ///
565    /// Elements with IDs can be looked up programmatically:
566    /// ```rust,ignore
567    /// div().id("my-container").child(...)
568    ///
569    /// // Later:
570    /// if let Some(handle) = ctx.query("my-container") {
571    ///     handle.scroll_into_view();
572    /// }
573    /// ```
574    pub fn id(mut self, id: impl Into<String>) -> Self {
575        self.element_id = Some(id.into());
576        self
577    }
578
579    /// Get the element ID if set
580    pub fn element_id(&self) -> Option<&str> {
581        self.element_id.as_deref()
582    }
583
584    /// Add a CSS class name for selector matching
585    ///
586    /// Classes can be used with `.class` selectors in stylesheets.
587    /// Multiple classes can be added by chaining `.class()` calls.
588    pub fn class(mut self, name: impl Into<String>) -> Self {
589        self.classes.push(name.into());
590        self
591    }
592
593    /// Get the element's class list
594    pub fn classes(&self) -> &[String] {
595        &self.classes
596    }
597
598    /// Set the stateful context key for automatic key derivation
599    ///
600    /// This is typically set automatically by `stateful()` callbacks.
601    /// When set, motion containers and layout animations will use this key
602    /// as a prefix for auto-generated stable keys.
603    pub(crate) fn with_stateful_context(mut self, key: impl Into<String>) -> Self {
604        self.stateful_context_key = Some(key.into());
605        self
606    }
607
608    /// Get the stateful context key if set
609    pub fn stateful_context_key(&self) -> Option<&str> {
610        self.stateful_context_key.as_deref()
611    }
612
613    /// Enable layout animation for this element
614    ///
615    /// # Deprecated
616    ///
617    /// Use [`animate_bounds()`](Self::animate_bounds) instead. The new system:
618    /// - Never modifies taffy (read-only layout)
619    /// - Tracks visual offsets that animate back to zero (FLIP technique)
620    /// - Properly propagates parent animation offsets to children
621    ///
622    /// When enabled, changes to the element's bounds (position/size) after layout
623    /// computation will be smoothly animated using spring physics instead of
624    /// snapping instantly.
625    ///
626    /// This is useful for accordion/collapsible content, list reordering,
627    /// and any UI where elements change size or position dynamically.
628    ///
629    /// # Example
630    ///
631    /// ```rust,ignore
632    /// // Animate height changes with default spring
633    /// div()
634    ///     .animate_layout(LayoutAnimationConfig::height())
635    ///     .child(content)
636    ///
637    /// // Animate all bounds with custom spring
638    /// div()
639    ///     .animate_layout(LayoutAnimationConfig::all().with_spring(SpringConfig::gentle()))
640    ///     .child(content)
641    /// ```
642    #[deprecated(
643        since = "0.3.0",
644        note = "Use animate_bounds() instead. The old system modifies taffy which causes parent-child misalignment issues."
645    )]
646    pub fn animate_layout(
647        mut self,
648        config: crate::layout_animation::LayoutAnimationConfig,
649    ) -> Self {
650        // Auto-apply stable key if inside a stateful context
651        let config = if let Some(ref ctx_key) = self.stateful_context_key {
652            if config.stable_key.is_none() {
653                let auto_key = format!("{}:layout_anim", ctx_key);
654                config.with_key(auto_key)
655            } else {
656                config
657            }
658        } else {
659            config
660        };
661        self.layout_animation = Some(config);
662        self
663    }
664
665    /// Enable visual bounds animation using the new FLIP-style system
666    ///
667    /// Unlike `animate_layout()`, this system never modifies the layout tree.
668    /// Instead, it tracks visual offsets and animates them back to zero.
669    ///
670    /// Key differences:
671    /// - Layout runs once with final positions (taffy is read-only)
672    /// - Animation tracks visual offset from layout position
673    /// - Parent offsets propagate to children hierarchically
674    /// - Pre-computed bounds used during rendering
675    ///
676    /// # Example
677    ///
678    /// ```ignore
679    /// // Animate height changes with FLIP-style animation
680    /// div()
681    ///     .animate_bounds(VisualAnimationConfig::height().with_key("my-content"))
682    ///     .overflow_clip()
683    ///     .child(content)
684    /// ```
685    pub fn animate_bounds(
686        mut self,
687        config: crate::visual_animation::VisualAnimationConfig,
688    ) -> Self {
689        // Auto-apply stable key if inside a stateful context
690        let config = if let Some(ref ctx_key) = self.stateful_context_key {
691            if config.key.is_none() {
692                let auto_key = format!("{}:visual_anim", ctx_key);
693                config.with_key(auto_key)
694            } else {
695                config
696            }
697        } else {
698            config
699        };
700        self.visual_animation = Some(config);
701        self
702    }
703
704    /// Wrap this Div in a Motion container with automatic key derivation
705    ///
706    /// If this Div is inside a stateful context (has `stateful_context_key` set),
707    /// the Motion will use an auto-derived stable key. Otherwise, it falls back
708    /// to a location-based key.
709    ///
710    /// # Example
711    ///
712    /// ```ignore
713    /// stateful::<AccordionState>()
714    ///     .on_state(|ctx| {
715    ///         // This motion gets an auto-derived key like "stateful:...:motion"
716    ///         div()
717    ///             .motion()  // Auto-keyed!
718    ///             .fade_in(200)
719    ///             .child(content)
720    ///     })
721    /// ```
722    #[track_caller]
723    pub fn motion(self) -> crate::motion::Motion {
724        if let Some(ref ctx_key) = self.stateful_context_key {
725            // Auto-derive key from stateful context
726            let motion_key = format!("{}:motion", ctx_key);
727            crate::motion::motion_derived(&motion_key).child(self)
728        } else {
729            // Fallback to normal motion with location-based key
730            crate::motion::motion().child(self)
731        }
732    }
733
734    /// Swap this Div with a default, returning the original
735    ///
736    /// This is a convenience method for use in state callbacks where you need
737    /// to consume `self` to chain builder methods, then assign back.
738    ///
739    /// **Note**: This takes ownership of the current Div and leaves a default in its place.
740    /// All properties are preserved in the returned Div. You must assign the result back
741    /// to complete the update.
742    ///
743    /// For updating specific properties without the swap pattern, consider using
744    /// the setter methods directly (e.g., `set_bg()`, `set_transform()`).
745    ///
746    /// # Example
747    ///
748    /// ```ignore
749    /// .on_state(|state, div| match state {
750    ///     ButtonState::Idle => {
751    ///         *div = div.swap().bg(Color::BLUE).rounded(4.0);
752    ///     }
753    ///     ButtonState::Hovered => {
754    ///         *div = div.swap().bg(Color::CYAN).rounded(8.0);
755    ///     }
756    /// })
757    /// ```
758    #[inline]
759    pub fn swap(&mut self) -> Self {
760        std::mem::take(self)
761    }
762
763    /// Conditionally apply modifications based on a boolean condition
764    ///
765    /// This is useful for conditional styling without verbose if-else blocks.
766    ///
767    /// # Example
768    ///
769    /// ```rust,ignore
770    /// // Instead of:
771    /// let mut d = div();
772    /// if is_collapsed {
773    ///     d = d.h(0.0);
774    /// }
775    ///
776    /// // Write:
777    /// div().when(is_collapsed, |d| d.h(0.0))
778    /// ```
779    #[inline]
780    pub fn when<F>(self, condition: bool, f: F) -> Self
781    where
782        F: FnOnce(Self) -> Self,
783    {
784        if condition {
785            f(self)
786        } else {
787            self
788        }
789    }
790
791    /// Set the background color/brush without consuming self
792    ///
793    /// This is useful in state callbacks where you want to update
794    /// properties without using the swap pattern.
795    #[inline]
796    pub fn set_bg(&mut self, color: impl Into<Brush>) {
797        self.background = Some(color.into());
798    }
799
800    /// Set the corner radius without consuming self
801    #[inline]
802    pub fn set_rounded(&mut self, radius: f32) {
803        self.border_radius = CornerRadius::uniform(radius);
804        self.border_radius_explicit = true;
805    }
806
807    /// Set the transform without consuming self
808    #[inline]
809    pub fn set_transform(&mut self, transform: Transform) {
810        self.transform = Some(transform);
811    }
812
813    /// Set the shadow without consuming self
814    #[inline]
815    pub fn set_shadow(&mut self, shadow: Shadow) {
816        self.shadow = Some(shadow);
817    }
818
819    /// Set the opacity without consuming self
820    #[inline]
821    pub fn set_opacity(&mut self, opacity: f32) {
822        self.opacity = opacity;
823    }
824
825    /// Set border with width and color without consuming self
826    #[inline]
827    pub fn set_border(&mut self, width: f32, color: Color) {
828        self.border_width = width;
829        self.border_color = Some(color);
830    }
831
832    /// Set overflow clip without consuming self
833    #[inline]
834    pub fn set_overflow_clip(&mut self, clip: bool) {
835        if clip {
836            self.style.overflow.x = taffy::Overflow::Hidden;
837            self.style.overflow.y = taffy::Overflow::Hidden;
838        } else {
839            self.style.overflow.x = taffy::Overflow::Visible;
840            self.style.overflow.y = taffy::Overflow::Visible;
841        }
842    }
843
844    /// Set horizontal padding without consuming self
845    #[inline]
846    pub fn set_padding_x(&mut self, px: f32) {
847        self.style.padding.left = taffy::LengthPercentage::Length(px);
848        self.style.padding.right = taffy::LengthPercentage::Length(px);
849    }
850
851    /// Set vertical padding without consuming self
852    #[inline]
853    pub fn set_padding_y(&mut self, px: f32) {
854        self.style.padding.top = taffy::LengthPercentage::Length(px);
855        self.style.padding.bottom = taffy::LengthPercentage::Length(px);
856    }
857
858    /// Clear all children and add a single child
859    #[inline]
860    pub fn set_child(&mut self, child: impl ElementBuilder + 'static) {
861        self.children.clear();
862        self.children.push(Box::new(child));
863    }
864
865    /// Clear all children
866    #[inline]
867    pub fn clear_children(&mut self) {
868        self.children.clear();
869    }
870
871    /// Set width in pixels without consuming self
872    ///
873    /// This is useful in state callbacks where you want to update
874    /// layout properties without using the swap pattern.
875    #[inline]
876    pub fn set_w(&mut self, px: f32) {
877        self.style.size.width = taffy::Dimension::Length(px);
878    }
879
880    /// Set height in pixels without consuming self
881    #[inline]
882    pub fn set_h(&mut self, px: f32) {
883        self.style.size.height = taffy::Dimension::Length(px);
884    }
885
886    /// Set height to auto without consuming self
887    #[inline]
888    pub fn set_h_auto(&mut self) {
889        self.style.size.height = taffy::Dimension::Auto;
890    }
891
892    // =========================================================================
893    // ElementStyle Application
894    // =========================================================================
895
896    /// Apply an ElementStyle to this div
897    ///
898    /// Only properties that are set (Some) in the style will be applied.
899    /// This allows for style composition and sharing reusable styles.
900    ///
901    /// # Example
902    ///
903    /// ```ignore
904    /// use blinc_layout::prelude::*;
905    ///
906    /// // Define a reusable card style
907    /// let card_style = ElementStyle::new()
908    ///     .bg_surface()
909    ///     .rounded_lg()
910    ///     .shadow_md();
911    ///
912    /// // Apply to multiple elements
913    /// div().style(&card_style).child(text("Card 1"))
914    /// div().style(&card_style).child(text("Card 2"))
915    /// ```
916    pub fn style(mut self, style: &ElementStyle) -> Self {
917        self.set_style(style);
918        self
919    }
920
921    /// Apply an ElementStyle without consuming self
922    ///
923    /// This is useful in state callbacks where you want to update
924    /// styling without using the swap pattern.
925    ///
926    /// # Example
927    ///
928    /// ```ignore
929    /// .on_state(|state, div| {
930    ///     div.set_style(&hover_style);
931    /// })
932    /// ```
933    #[inline]
934    pub fn set_style(&mut self, style: &ElementStyle) {
935        use crate::element_style::{
936            SpacingRect, StyleAlign, StyleDisplay, StyleFlexDirection, StyleJustify, StyleOverflow,
937        };
938
939        // Visual properties
940        if let Some(ref bg) = style.background {
941            self.background = Some(bg.clone());
942        }
943        if let Some(radius) = style.corner_radius {
944            self.border_radius = radius;
945        }
946        if let Some(cs) = style.corner_shape {
947            self.corner_shape = cs;
948        }
949        if let Some(fade) = style.overflow_fade {
950            self.overflow_fade = fade;
951        }
952        if let Some(ref shadow) = style.shadow {
953            self.shadow = Some(*shadow);
954        }
955        if let Some(ref transform) = style.transform {
956            self.transform = Some(transform.clone());
957        }
958        if let Some(ref material) = style.material {
959            self.material = Some(material.clone());
960        }
961        if let Some(layer) = style.render_layer {
962            self.render_layer = layer;
963        }
964        if let Some(opacity) = style.opacity {
965            self.opacity = opacity;
966        }
967
968        // Layout: sizing
969        if let Some(w) = style.width {
970            match w {
971                crate::element_style::StyleDimension::Length(px) => {
972                    self.style.size.width = Dimension::Length(px);
973                }
974                crate::element_style::StyleDimension::Percent(p) => {
975                    self.style.size.width = Dimension::Percent(p);
976                }
977                crate::element_style::StyleDimension::Auto => {
978                    self.style.size.width = Dimension::Auto;
979                    self.style.flex_basis = Dimension::Auto;
980                    self.style.flex_grow = 0.0;
981                    self.style.flex_shrink = 0.0;
982                }
983            }
984        }
985        if let Some(h) = style.height {
986            match h {
987                crate::element_style::StyleDimension::Length(px) => {
988                    self.style.size.height = Dimension::Length(px);
989                }
990                crate::element_style::StyleDimension::Percent(p) => {
991                    self.style.size.height = Dimension::Percent(p);
992                }
993                crate::element_style::StyleDimension::Auto => {
994                    self.style.size.height = Dimension::Auto;
995                    self.style.flex_basis = Dimension::Auto;
996                    self.style.flex_grow = 0.0;
997                    self.style.flex_shrink = 0.0;
998                }
999            }
1000        }
1001        if let Some(w) = style.min_width {
1002            self.style.min_size.width = Dimension::Length(w);
1003        }
1004        if let Some(h) = style.min_height {
1005            self.style.min_size.height = Dimension::Length(h);
1006        }
1007        if let Some(w) = style.max_width {
1008            self.style.max_size.width = Dimension::Length(w);
1009        }
1010        if let Some(h) = style.max_height {
1011            self.style.max_size.height = Dimension::Length(h);
1012        }
1013
1014        // Layout: display & flex direction
1015        if let Some(display) = style.display {
1016            self.style.display = match display {
1017                StyleDisplay::Flex => Display::Flex,
1018                StyleDisplay::Block => Display::Block,
1019                StyleDisplay::None => Display::None,
1020            };
1021        }
1022        if let Some(dir) = style.flex_direction {
1023            self.style.flex_direction = match dir {
1024                StyleFlexDirection::Row => FlexDirection::Row,
1025                StyleFlexDirection::Column => FlexDirection::Column,
1026                StyleFlexDirection::RowReverse => FlexDirection::RowReverse,
1027                StyleFlexDirection::ColumnReverse => FlexDirection::ColumnReverse,
1028            };
1029        }
1030        if let Some(wrap) = style.flex_wrap {
1031            self.style.flex_wrap = if wrap {
1032                FlexWrap::Wrap
1033            } else {
1034                FlexWrap::NoWrap
1035            };
1036        }
1037        if let Some(grow) = style.flex_grow {
1038            self.style.flex_grow = grow;
1039        }
1040        if let Some(shrink) = style.flex_shrink {
1041            self.style.flex_shrink = shrink;
1042        }
1043
1044        // Layout: alignment
1045        if let Some(align) = style.align_items {
1046            self.style.align_items = Some(match align {
1047                StyleAlign::Start => AlignItems::Start,
1048                StyleAlign::Center => AlignItems::Center,
1049                StyleAlign::End => AlignItems::End,
1050                StyleAlign::Stretch => AlignItems::Stretch,
1051                StyleAlign::Baseline => AlignItems::Baseline,
1052            });
1053        }
1054        if let Some(justify) = style.justify_content {
1055            self.style.justify_content = Some(match justify {
1056                StyleJustify::Start => JustifyContent::Start,
1057                StyleJustify::Center => JustifyContent::Center,
1058                StyleJustify::End => JustifyContent::End,
1059                StyleJustify::SpaceBetween => JustifyContent::SpaceBetween,
1060                StyleJustify::SpaceAround => JustifyContent::SpaceAround,
1061                StyleJustify::SpaceEvenly => JustifyContent::SpaceEvenly,
1062            });
1063        }
1064        if let Some(align) = style.align_self {
1065            self.style.align_self = Some(match align {
1066                StyleAlign::Start => AlignSelf::Start,
1067                StyleAlign::Center => AlignSelf::Center,
1068                StyleAlign::End => AlignSelf::End,
1069                StyleAlign::Stretch => AlignSelf::Stretch,
1070                StyleAlign::Baseline => AlignSelf::Baseline,
1071            });
1072        }
1073
1074        // Layout: spacing
1075        if let Some(SpacingRect {
1076            top,
1077            right,
1078            bottom,
1079            left,
1080        }) = style.padding
1081        {
1082            self.style.padding = Rect {
1083                top: LengthPercentage::Length(top),
1084                right: LengthPercentage::Length(right),
1085                bottom: LengthPercentage::Length(bottom),
1086                left: LengthPercentage::Length(left),
1087            };
1088        }
1089        if let Some(SpacingRect {
1090            top,
1091            right,
1092            bottom,
1093            left,
1094        }) = style.margin
1095        {
1096            self.style.margin = Rect {
1097                top: LengthPercentageAuto::Length(top),
1098                right: LengthPercentageAuto::Length(right),
1099                bottom: LengthPercentageAuto::Length(bottom),
1100                left: LengthPercentageAuto::Length(left),
1101            };
1102        }
1103        if let Some(gap) = style.gap {
1104            self.style.gap = taffy::Size {
1105                width: LengthPercentage::Length(gap),
1106                height: LengthPercentage::Length(gap),
1107            };
1108        }
1109
1110        // Layout: overflow
1111        if let Some(overflow) = style.overflow {
1112            let val = match overflow {
1113                StyleOverflow::Visible => Overflow::Visible,
1114                StyleOverflow::Clip => Overflow::Clip,
1115                StyleOverflow::Scroll => Overflow::Scroll,
1116            };
1117            self.style.overflow.x = val;
1118            self.style.overflow.y = val;
1119            if overflow == StyleOverflow::Scroll {
1120                self.ensure_scroll_physics(crate::scroll::ScrollDirection::Both);
1121            }
1122        }
1123        // Per-axis overflow
1124        if let Some(ox) = style.overflow_x {
1125            let val = match ox {
1126                StyleOverflow::Visible => Overflow::Visible,
1127                StyleOverflow::Clip => Overflow::Clip,
1128                StyleOverflow::Scroll => Overflow::Scroll,
1129            };
1130            self.style.overflow.x = val;
1131            if ox == StyleOverflow::Scroll {
1132                self.ensure_scroll_physics(crate::scroll::ScrollDirection::Horizontal);
1133            }
1134        }
1135        if let Some(oy) = style.overflow_y {
1136            let val = match oy {
1137                StyleOverflow::Visible => Overflow::Visible,
1138                StyleOverflow::Clip => Overflow::Clip,
1139                StyleOverflow::Scroll => Overflow::Scroll,
1140            };
1141            self.style.overflow.y = val;
1142            if oy == StyleOverflow::Scroll {
1143                self.ensure_scroll_physics(crate::scroll::ScrollDirection::Vertical);
1144            }
1145        }
1146
1147        // Layout: border
1148        if let Some(width) = style.border_width {
1149            self.border_width = width;
1150        }
1151        if let Some(color) = style.border_color {
1152            self.border_color = Some(color);
1153        }
1154
1155        // 3D properties
1156        if let Some(v) = style.rotate_x {
1157            self.rotate_x = Some(v);
1158        }
1159        if let Some(v) = style.rotate_y {
1160            self.rotate_y = Some(v);
1161        }
1162        if let Some(v) = style.perspective {
1163            self.perspective_3d = Some(v);
1164        }
1165        if let Some(ref s) = style.shape_3d {
1166            self.shape_3d = Some(crate::css_parser::shape_3d_to_float(s));
1167        }
1168        if let Some(v) = style.depth {
1169            self.depth = Some(v);
1170        }
1171        if let Some(dir) = style.light_direction {
1172            self.light_direction = Some(dir);
1173        }
1174        if let Some(v) = style.light_intensity {
1175            self.light_intensity = Some(v);
1176        }
1177        if let Some(v) = style.ambient {
1178            self.ambient = Some(v);
1179        }
1180        if let Some(v) = style.specular {
1181            self.specular = Some(v);
1182        }
1183        if let Some(v) = style.translate_z {
1184            self.translate_z = Some(v);
1185        }
1186        if let Some(ref op) = style.op_3d {
1187            self.op_3d = Some(crate::css_parser::op_3d_to_float(op));
1188        }
1189        if let Some(v) = style.blend_3d {
1190            self.blend_3d = Some(v);
1191        }
1192        if let Some(ref cp) = style.clip_path {
1193            self.clip_path = Some(cp.clone());
1194        }
1195        if let Some(ref f) = style.flow {
1196            self.flow_name = Some(f.clone());
1197        }
1198    }
1199
1200    /// Merge properties from another Div into this one
1201    ///
1202    /// This applies the other Div's non-default properties on top of this one.
1203    /// Useful in `on_state` callbacks to apply changes without reassignment:
1204    ///
1205    /// ```ignore
1206    /// .on_state(|state, div| {
1207    ///     div.merge(div().bg(color).child(label));
1208    /// })
1209    /// ```
1210    #[inline]
1211    pub fn merge(&mut self, other: Div) {
1212        // Create a default for comparison
1213        let default = Div::new();
1214
1215        // Merge style if it differs from default
1216        // Style is complex, so we merge it field by field via taffy
1217        self.merge_style(&other.style, &default.style);
1218
1219        // Merge render properties - take other's value if non-default
1220        if other.background.is_some() {
1221            self.background = other.background;
1222        }
1223        if other.border_radius_explicit || other.border_radius != default.border_radius {
1224            self.border_radius = other.border_radius;
1225            self.border_radius_explicit = true;
1226        }
1227        if other.border_color.is_some() {
1228            self.border_color = other.border_color;
1229        }
1230        if other.border_width != default.border_width {
1231            self.border_width = other.border_width;
1232        }
1233        if other.render_layer != default.render_layer {
1234            self.render_layer = other.render_layer;
1235        }
1236        if other.material.is_some() {
1237            self.material = other.material;
1238        }
1239        if other.shadow.is_some() {
1240            self.shadow = other.shadow;
1241        }
1242        if other.transform.is_some() {
1243            self.transform = other.transform;
1244        }
1245        if other.opacity != default.opacity {
1246            self.opacity = other.opacity;
1247        }
1248        if other.cursor.is_some() {
1249            self.cursor = other.cursor;
1250        }
1251
1252        // Merge element identity (ID and CSS classes)
1253        // These are critical for event routing (click-outside detection) and
1254        // CSS selector matching. Without this, Stateful containers lose the
1255        // element_id and classes set on the Div returned from on_state.
1256        if other.element_id.is_some() {
1257            self.element_id = other.element_id;
1258        }
1259        if !other.classes.is_empty() {
1260            self.classes = other.classes;
1261        }
1262
1263        // Merge children - if other has children, replace ours
1264        if !other.children.is_empty() {
1265            self.children = other.children;
1266        }
1267
1268        // Merge stateful context key - take other's if set
1269        if other.stateful_context_key.is_some() {
1270            self.stateful_context_key = other.stateful_context_key;
1271        }
1272
1273        // Merge visual animation config - take other's if set
1274        if other.visual_animation.is_some() {
1275            self.visual_animation = other.visual_animation;
1276        }
1277
1278        // Merge layout animation config (deprecated) - take other's if set
1279        if other.layout_animation.is_some() {
1280            self.layout_animation = other.layout_animation;
1281        }
1282
1283        // Merge 3D properties
1284        if other.rotate_x.is_some() {
1285            self.rotate_x = other.rotate_x;
1286        }
1287        if other.rotate_y.is_some() {
1288            self.rotate_y = other.rotate_y;
1289        }
1290        if other.perspective_3d.is_some() {
1291            self.perspective_3d = other.perspective_3d;
1292        }
1293        if other.shape_3d.is_some() {
1294            self.shape_3d = other.shape_3d;
1295        }
1296        if other.depth.is_some() {
1297            self.depth = other.depth;
1298        }
1299        if other.light_direction.is_some() {
1300            self.light_direction = other.light_direction;
1301        }
1302        if other.light_intensity.is_some() {
1303            self.light_intensity = other.light_intensity;
1304        }
1305        if other.ambient.is_some() {
1306            self.ambient = other.ambient;
1307        }
1308        if other.specular.is_some() {
1309            self.specular = other.specular;
1310        }
1311        if other.translate_z.is_some() {
1312            self.translate_z = other.translate_z;
1313        }
1314        if other.op_3d.is_some() {
1315            self.op_3d = other.op_3d;
1316        }
1317        if other.blend_3d.is_some() {
1318            self.blend_3d = other.blend_3d;
1319        }
1320        if other.clip_path.is_some() {
1321            self.clip_path = other.clip_path;
1322        }
1323        if other.flow_name.is_some() {
1324            self.flow_name = other.flow_name;
1325            self.flow_graph = other.flow_graph;
1326        }
1327
1328        // Merge scroll physics - take other's if set
1329        if other.scroll_physics.is_some() {
1330            self.scroll_physics = other.scroll_physics;
1331        }
1332
1333        // Merge event handlers - combine both sets so scroll handlers etc. survive
1334        if !other.event_handlers.is_empty() {
1335            self.event_handlers.merge(other.event_handlers);
1336        }
1337    }
1338
1339    /// Merge taffy Style fields from other if they differ from default
1340    fn merge_style(&mut self, other: &Style, default: &Style) {
1341        // Display & position
1342        if other.display != default.display {
1343            self.style.display = other.display;
1344        }
1345        if other.position != default.position {
1346            self.style.position = other.position;
1347        }
1348        if other.overflow != default.overflow {
1349            self.style.overflow = other.overflow;
1350        }
1351
1352        // Flex container properties
1353        if other.flex_direction != default.flex_direction {
1354            self.style.flex_direction = other.flex_direction;
1355        }
1356        if other.flex_wrap != default.flex_wrap {
1357            self.style.flex_wrap = other.flex_wrap;
1358        }
1359        if other.justify_content != default.justify_content {
1360            self.style.justify_content = other.justify_content;
1361        }
1362        if other.align_items != default.align_items {
1363            self.style.align_items = other.align_items;
1364        }
1365        if other.align_content != default.align_content {
1366            self.style.align_content = other.align_content;
1367        }
1368        // Gap - merge per axis
1369        if other.gap.width != default.gap.width {
1370            self.style.gap.width = other.gap.width;
1371        }
1372        if other.gap.height != default.gap.height {
1373            self.style.gap.height = other.gap.height;
1374        }
1375
1376        // Flex item properties
1377        if other.flex_grow != default.flex_grow {
1378            self.style.flex_grow = other.flex_grow;
1379        }
1380        if other.flex_shrink != default.flex_shrink {
1381            self.style.flex_shrink = other.flex_shrink;
1382        }
1383        if other.flex_basis != default.flex_basis {
1384            self.style.flex_basis = other.flex_basis;
1385        }
1386        if other.align_self != default.align_self {
1387            self.style.align_self = other.align_self;
1388        }
1389
1390        // Size constraints - merge per dimension to allow w() then h() separately
1391        if other.size.width != default.size.width {
1392            self.style.size.width = other.size.width;
1393        }
1394        if other.size.height != default.size.height {
1395            self.style.size.height = other.size.height;
1396        }
1397        if other.min_size.width != default.min_size.width {
1398            self.style.min_size.width = other.min_size.width;
1399        }
1400        if other.min_size.height != default.min_size.height {
1401            self.style.min_size.height = other.min_size.height;
1402        }
1403        if other.max_size.width != default.max_size.width {
1404            self.style.max_size.width = other.max_size.width;
1405        }
1406        if other.max_size.height != default.max_size.height {
1407            self.style.max_size.height = other.max_size.height;
1408        }
1409        if other.aspect_ratio != default.aspect_ratio {
1410            self.style.aspect_ratio = other.aspect_ratio;
1411        }
1412
1413        // Spacing - merge per side to allow partial updates (e.g., px then py)
1414        // Margin
1415        if other.margin.left != default.margin.left {
1416            self.style.margin.left = other.margin.left;
1417        }
1418        if other.margin.right != default.margin.right {
1419            self.style.margin.right = other.margin.right;
1420        }
1421        if other.margin.top != default.margin.top {
1422            self.style.margin.top = other.margin.top;
1423        }
1424        if other.margin.bottom != default.margin.bottom {
1425            self.style.margin.bottom = other.margin.bottom;
1426        }
1427        // Padding
1428        if other.padding.left != default.padding.left {
1429            self.style.padding.left = other.padding.left;
1430        }
1431        if other.padding.right != default.padding.right {
1432            self.style.padding.right = other.padding.right;
1433        }
1434        if other.padding.top != default.padding.top {
1435            self.style.padding.top = other.padding.top;
1436        }
1437        if other.padding.bottom != default.padding.bottom {
1438            self.style.padding.bottom = other.padding.bottom;
1439        }
1440        // Border
1441        if other.border.left != default.border.left {
1442            self.style.border.left = other.border.left;
1443        }
1444        if other.border.right != default.border.right {
1445            self.style.border.right = other.border.right;
1446        }
1447        if other.border.top != default.border.top {
1448            self.style.border.top = other.border.top;
1449        }
1450        if other.border.bottom != default.border.bottom {
1451            self.style.border.bottom = other.border.bottom;
1452        }
1453
1454        // Inset (for absolute positioning) - merge per side
1455        if other.inset.left != default.inset.left {
1456            self.style.inset.left = other.inset.left;
1457        }
1458        if other.inset.right != default.inset.right {
1459            self.style.inset.right = other.inset.right;
1460        }
1461        if other.inset.top != default.inset.top {
1462            self.style.inset.top = other.inset.top;
1463        }
1464        if other.inset.bottom != default.inset.bottom {
1465            self.style.inset.bottom = other.inset.bottom;
1466        }
1467    }
1468
1469    // =========================================================================
1470    // Display & Flex Direction
1471    // =========================================================================
1472
1473    /// Set display to flex (default)
1474    pub fn flex(mut self) -> Self {
1475        self.style.display = Display::Flex;
1476        self
1477    }
1478
1479    /// Set display to block
1480    pub fn block(mut self) -> Self {
1481        self.style.display = Display::Block;
1482        self
1483    }
1484
1485    /// Set display to grid
1486    pub fn grid(mut self) -> Self {
1487        self.style.display = Display::Grid;
1488        self
1489    }
1490
1491    /// Set display to none
1492    pub fn hidden(mut self) -> Self {
1493        self.style.display = Display::None;
1494        self
1495    }
1496
1497    /// Set flex direction to row (horizontal)
1498    pub fn flex_row(mut self) -> Self {
1499        self.style.display = Display::Flex;
1500        self.style.flex_direction = FlexDirection::Row;
1501        self
1502    }
1503
1504    /// Set flex direction to column (vertical)
1505    pub fn flex_col(mut self) -> Self {
1506        self.style.display = Display::Flex;
1507        self.style.flex_direction = FlexDirection::Column;
1508        self
1509    }
1510
1511    /// Set flex direction to row-reverse
1512    pub fn flex_row_reverse(mut self) -> Self {
1513        self.style.display = Display::Flex;
1514        self.style.flex_direction = FlexDirection::RowReverse;
1515        self
1516    }
1517
1518    /// Set flex direction to column-reverse
1519    pub fn flex_col_reverse(mut self) -> Self {
1520        self.style.display = Display::Flex;
1521        self.style.flex_direction = FlexDirection::ColumnReverse;
1522        self
1523    }
1524
1525    // =========================================================================
1526    // Flex Properties
1527    // =========================================================================
1528
1529    /// Set flex-grow to 1 (element will grow to fill space)
1530    pub fn flex_grow(mut self) -> Self {
1531        self.style.flex_grow = 1.0;
1532        self
1533    }
1534
1535    /// Set flex-grow to a specific value
1536    ///
1537    /// Use this for proportional sizing. For example, an element with flex_grow_value(2.0)
1538    /// will grow to twice the size of an element with flex_grow_value(1.0).
1539    pub fn flex_grow_value(mut self, value: f32) -> Self {
1540        self.style.flex_grow = value;
1541        self
1542    }
1543
1544    /// Set flex-shrink to 1 (element will shrink if needed)
1545    pub fn flex_shrink(mut self) -> Self {
1546        self.style.flex_shrink = 1.0;
1547        self
1548    }
1549
1550    /// Set flex-shrink to 0 (element won't shrink)
1551    pub fn flex_shrink_0(mut self) -> Self {
1552        self.style.flex_shrink = 0.0;
1553        self
1554    }
1555
1556    /// Set flex-basis to auto
1557    pub fn flex_auto(mut self) -> Self {
1558        self.style.flex_grow = 1.0;
1559        self.style.flex_shrink = 1.0;
1560        self.style.flex_basis = Dimension::Auto;
1561        self
1562    }
1563
1564    /// Set flex: 1 1 0% (grow, shrink, basis 0)
1565    pub fn flex_1(mut self) -> Self {
1566        self.style.flex_grow = 1.0;
1567        self.style.flex_shrink = 1.0;
1568        self.style.flex_basis = Dimension::Length(0.0);
1569        self
1570    }
1571
1572    /// Allow wrapping
1573    pub fn flex_wrap(mut self) -> Self {
1574        self.style.flex_wrap = FlexWrap::Wrap;
1575        self
1576    }
1577
1578    // =========================================================================
1579    // Alignment & Justification
1580    // =========================================================================
1581
1582    /// Center items both horizontally and vertically
1583    pub fn items_center(mut self) -> Self {
1584        self.style.align_items = Some(AlignItems::Center);
1585        self
1586    }
1587
1588    /// Align items to start
1589    pub fn items_start(mut self) -> Self {
1590        self.style.align_items = Some(AlignItems::Start);
1591        self
1592    }
1593
1594    /// Align items to end
1595    pub fn items_end(mut self) -> Self {
1596        self.style.align_items = Some(AlignItems::End);
1597        self
1598    }
1599
1600    /// Stretch items to fill (default)
1601    pub fn items_stretch(mut self) -> Self {
1602        self.style.align_items = Some(AlignItems::Stretch);
1603        self
1604    }
1605
1606    /// Align items to baseline
1607    pub fn items_baseline(mut self) -> Self {
1608        self.style.align_items = Some(AlignItems::Baseline);
1609        self
1610    }
1611
1612    /// Align self to start (overrides parent's align_items for this element)
1613    pub fn align_self_start(mut self) -> Self {
1614        self.style.align_self = Some(AlignSelf::Start);
1615        self
1616    }
1617
1618    /// Align self to center (overrides parent's align_items for this element)
1619    pub fn align_self_center(mut self) -> Self {
1620        self.style.align_self = Some(AlignSelf::Center);
1621        self
1622    }
1623
1624    /// Align self to end (overrides parent's align_items for this element)
1625    pub fn align_self_end(mut self) -> Self {
1626        self.style.align_self = Some(AlignSelf::End);
1627        self
1628    }
1629
1630    /// Stretch self to fill (overrides parent's align_items for this element)
1631    pub fn align_self_stretch(mut self) -> Self {
1632        self.style.align_self = Some(AlignSelf::Stretch);
1633        self
1634    }
1635
1636    /// Justify content to start
1637    pub fn justify_start(mut self) -> Self {
1638        self.style.justify_content = Some(JustifyContent::Start);
1639        self
1640    }
1641
1642    /// Justify content to center
1643    pub fn justify_center(mut self) -> Self {
1644        self.style.justify_content = Some(JustifyContent::Center);
1645        self
1646    }
1647
1648    /// Justify content to end
1649    pub fn justify_end(mut self) -> Self {
1650        self.style.justify_content = Some(JustifyContent::End);
1651        self
1652    }
1653
1654    /// Space between items
1655    pub fn justify_between(mut self) -> Self {
1656        self.style.justify_content = Some(JustifyContent::SpaceBetween);
1657        self
1658    }
1659
1660    /// Space around items
1661    pub fn justify_around(mut self) -> Self {
1662        self.style.justify_content = Some(JustifyContent::SpaceAround);
1663        self
1664    }
1665
1666    /// Space evenly between items
1667    pub fn justify_evenly(mut self) -> Self {
1668        self.style.justify_content = Some(JustifyContent::SpaceEvenly);
1669        self
1670    }
1671
1672    /// Align content to start (for multi-line flex containers)
1673    pub fn content_start(mut self) -> Self {
1674        self.style.align_content = Some(taffy::AlignContent::Start);
1675        self
1676    }
1677
1678    /// Align content to center (for multi-line flex containers)
1679    pub fn content_center(mut self) -> Self {
1680        self.style.align_content = Some(taffy::AlignContent::Center);
1681        self
1682    }
1683
1684    /// Align content to end (for multi-line flex containers)
1685    pub fn content_end(mut self) -> Self {
1686        self.style.align_content = Some(taffy::AlignContent::End);
1687        self
1688    }
1689
1690    /// Align this element to start on cross-axis (overrides parent's align-items)
1691    ///
1692    /// In a flex-row parent, this prevents height stretching.
1693    /// In a flex-col parent, this prevents width stretching.
1694    pub fn self_start(mut self) -> Self {
1695        self.style.align_self = Some(AlignSelf::FlexStart);
1696        self
1697    }
1698
1699    /// Align this element to center on cross-axis (overrides parent's align-items)
1700    pub fn self_center(mut self) -> Self {
1701        self.style.align_self = Some(AlignSelf::Center);
1702        self
1703    }
1704
1705    /// Align this element to end on cross-axis (overrides parent's align-items)
1706    pub fn self_end(mut self) -> Self {
1707        self.style.align_self = Some(AlignSelf::FlexEnd);
1708        self
1709    }
1710
1711    /// Stretch this element on cross-axis (overrides parent's align-items)
1712    pub fn self_stretch(mut self) -> Self {
1713        self.style.align_self = Some(AlignSelf::Stretch);
1714        self
1715    }
1716
1717    // =========================================================================
1718    // Sizing (pixel values)
1719    // =========================================================================
1720
1721    /// Set width in pixels
1722    pub fn w(mut self, px: f32) -> Self {
1723        self.style.size.width = Dimension::Length(px);
1724        self
1725    }
1726
1727    /// Set width to 100%
1728    pub fn w_full(mut self) -> Self {
1729        self.style.size.width = Dimension::Percent(1.0);
1730        self
1731    }
1732
1733    /// Set width to auto
1734    pub fn w_auto(mut self) -> Self {
1735        self.style.size.width = Dimension::Auto;
1736        self
1737    }
1738
1739    /// Set width to fit content (shrink-wrap to children)
1740    ///
1741    /// This sets width to auto with flex_basis auto and prevents flex growing/shrinking,
1742    /// so the element will size exactly to fit its content.
1743    pub fn w_fit(mut self) -> Self {
1744        self.style.size.width = Dimension::Auto;
1745        self.style.flex_basis = Dimension::Auto;
1746        self.style.flex_grow = 0.0;
1747        self.style.flex_shrink = 0.0;
1748        self
1749    }
1750
1751    /// Set height in pixels
1752    pub fn h(mut self, px: f32) -> Self {
1753        self.style.size.height = Dimension::Length(px);
1754        self
1755    }
1756
1757    /// Set height to 100%
1758    pub fn h_full(mut self) -> Self {
1759        self.style.size.height = Dimension::Percent(1.0);
1760        self
1761    }
1762
1763    /// Set height to auto
1764    pub fn h_auto(mut self) -> Self {
1765        self.style.size.height = Dimension::Auto;
1766        self
1767    }
1768
1769    /// Set height to fit content (shrink-wrap to children)
1770    ///
1771    /// This sets height to auto and prevents flex growing/shrinking, so the element
1772    /// will size exactly to fit its content.
1773    pub fn h_fit(mut self) -> Self {
1774        self.style.size.height = Dimension::Auto;
1775        self.style.flex_basis = Dimension::Auto;
1776        self.style.flex_grow = 0.0;
1777        self.style.flex_shrink = 0.0;
1778        self
1779    }
1780
1781    /// Set both width and height to fit content
1782    ///
1783    /// This makes the element shrink-wrap to its content in both dimensions.
1784    pub fn size_fit(mut self) -> Self {
1785        self.style.size.width = Dimension::Auto;
1786        self.style.size.height = Dimension::Auto;
1787        self.style.flex_basis = Dimension::Auto;
1788        self.style.flex_grow = 0.0;
1789        self.style.flex_shrink = 0.0;
1790        self
1791    }
1792
1793    /// Set both width and height in pixels
1794    pub fn size(mut self, w: f32, h: f32) -> Self {
1795        self.style.size.width = Dimension::Length(w);
1796        self.style.size.height = Dimension::Length(h);
1797        self
1798    }
1799
1800    /// Set square size (width and height equal)
1801    pub fn square(mut self, size: f32) -> Self {
1802        self.style.size.width = Dimension::Length(size);
1803        self.style.size.height = Dimension::Length(size);
1804        self
1805    }
1806
1807    /// Set min-width in pixels
1808    pub fn min_w(mut self, px: f32) -> Self {
1809        self.style.min_size.width = Dimension::Length(px);
1810        self
1811    }
1812
1813    /// Set min-height in pixels
1814    pub fn min_h(mut self, px: f32) -> Self {
1815        self.style.min_size.height = Dimension::Length(px);
1816        self
1817    }
1818
1819    /// Set max-width in pixels
1820    pub fn max_w(mut self, px: f32) -> Self {
1821        self.style.max_size.width = Dimension::Length(px);
1822        self
1823    }
1824
1825    /// Set max-height in pixels
1826    pub fn max_h(mut self, px: f32) -> Self {
1827        self.style.max_size.height = Dimension::Length(px);
1828        self
1829    }
1830
1831    // =========================================================================
1832    // Spacing (4px base unit like Tailwind)
1833    // =========================================================================
1834
1835    /// Set gap between children (in 4px units)
1836    /// gap(4) = 16px
1837    pub fn gap(mut self, units: f32) -> Self {
1838        let px = units * 4.0;
1839        self.style.gap = taffy::Size {
1840            width: LengthPercentage::Length(px),
1841            height: LengthPercentage::Length(px),
1842        };
1843        self
1844    }
1845
1846    /// Set gap in pixels directly
1847    pub fn gap_px(mut self, px: f32) -> Self {
1848        self.style.gap = taffy::Size {
1849            width: LengthPercentage::Length(px),
1850            height: LengthPercentage::Length(px),
1851        };
1852        self
1853    }
1854
1855    /// Set column gap (horizontal spacing between items)
1856    pub fn gap_x(mut self, units: f32) -> Self {
1857        self.style.gap.width = LengthPercentage::Length(units * 4.0);
1858        self
1859    }
1860
1861    /// Set row gap (vertical spacing between items)
1862    pub fn gap_y(mut self, units: f32) -> Self {
1863        self.style.gap.height = LengthPercentage::Length(units * 4.0);
1864        self
1865    }
1866
1867    // -------------------------------------------------------------------------
1868    // Theme-based gap spacing
1869    // -------------------------------------------------------------------------
1870
1871    /// Set gap using theme spacing scale (space_1 = 4px)
1872    pub fn gap_1(self) -> Self {
1873        self.gap_px(ThemeState::get().spacing().space_1)
1874    }
1875
1876    /// Set gap using theme spacing scale (space_2 = 8px)
1877    pub fn gap_2(self) -> Self {
1878        self.gap_px(ThemeState::get().spacing().space_2)
1879    }
1880
1881    /// Set gap using theme spacing scale (space_3 = 12px)
1882    pub fn gap_3(self) -> Self {
1883        self.gap_px(ThemeState::get().spacing().space_3)
1884    }
1885
1886    /// Set gap using theme spacing scale (space_4 = 16px)
1887    pub fn gap_4(self) -> Self {
1888        self.gap_px(ThemeState::get().spacing().space_4)
1889    }
1890
1891    /// Set gap using theme spacing scale (space_5 = 20px)
1892    pub fn gap_5(self) -> Self {
1893        self.gap_px(ThemeState::get().spacing().space_5)
1894    }
1895
1896    /// Set gap using theme spacing scale (space_6 = 24px)
1897    pub fn gap_6(self) -> Self {
1898        self.gap_px(ThemeState::get().spacing().space_6)
1899    }
1900
1901    /// Set gap using theme spacing scale (space_8 = 32px)
1902    pub fn gap_8(self) -> Self {
1903        self.gap_px(ThemeState::get().spacing().space_8)
1904    }
1905
1906    /// Set gap using theme spacing scale (space_10 = 40px)
1907    pub fn gap_10(self) -> Self {
1908        self.gap_px(ThemeState::get().spacing().space_10)
1909    }
1910
1911    /// Set gap using theme spacing scale (space_12 = 48px)
1912    pub fn gap_12(self) -> Self {
1913        self.gap_px(ThemeState::get().spacing().space_12)
1914    }
1915
1916    // -------------------------------------------------------------------------
1917    // Padding
1918    // -------------------------------------------------------------------------
1919
1920    /// Set padding on all sides (in 4px units)
1921    /// p(4) = 16px padding
1922    pub fn p(mut self, units: f32) -> Self {
1923        let px = LengthPercentage::Length(units * 4.0);
1924        self.style.padding = Rect {
1925            left: px,
1926            right: px,
1927            top: px,
1928            bottom: px,
1929        };
1930        self
1931    }
1932
1933    /// Set padding in pixels
1934    pub fn p_px(mut self, px: f32) -> Self {
1935        let val = LengthPercentage::Length(px);
1936        self.style.padding = Rect {
1937            left: val,
1938            right: val,
1939            top: val,
1940            bottom: val,
1941        };
1942        self
1943    }
1944
1945    /// Set horizontal padding (in 4px units)
1946    pub fn px(mut self, units: f32) -> Self {
1947        let px = LengthPercentage::Length(units * 4.0);
1948        self.style.padding.left = px;
1949        self.style.padding.right = px;
1950        self
1951    }
1952
1953    /// Set vertical padding (in 4px units)
1954    pub fn py(mut self, units: f32) -> Self {
1955        let px = LengthPercentage::Length(units * 4.0);
1956        self.style.padding.top = px;
1957        self.style.padding.bottom = px;
1958        self
1959    }
1960
1961    /// Set left padding (in 4px units)
1962    pub fn pl(mut self, units: f32) -> Self {
1963        self.style.padding.left = LengthPercentage::Length(units * 4.0);
1964        self
1965    }
1966
1967    /// Set right padding (in 4px units)
1968    pub fn pr(mut self, units: f32) -> Self {
1969        self.style.padding.right = LengthPercentage::Length(units * 4.0);
1970        self
1971    }
1972
1973    /// Set top padding (in 4px units)
1974    pub fn pt(mut self, units: f32) -> Self {
1975        self.style.padding.top = LengthPercentage::Length(units * 4.0);
1976        self
1977    }
1978
1979    /// Set bottom padding (in 4px units)
1980    pub fn pb(mut self, units: f32) -> Self {
1981        self.style.padding.bottom = LengthPercentage::Length(units * 4.0);
1982        self
1983    }
1984
1985    // -------------------------------------------------------------------------
1986    // Theme-based padding
1987    // -------------------------------------------------------------------------
1988
1989    /// Set padding using theme spacing scale (space_1 = 4px)
1990    pub fn p_1(self) -> Self {
1991        let px = ThemeState::get().spacing().space_1;
1992        self.padding(crate::units::Length::Px(px))
1993    }
1994
1995    /// Set padding using theme spacing scale (space_2 = 8px)
1996    pub fn p_2(self) -> Self {
1997        let px = ThemeState::get().spacing().space_2;
1998        self.padding(crate::units::Length::Px(px))
1999    }
2000
2001    /// Set padding using theme spacing scale (space_3 = 12px)
2002    pub fn p_3(self) -> Self {
2003        let px = ThemeState::get().spacing().space_3;
2004        self.padding(crate::units::Length::Px(px))
2005    }
2006
2007    /// Set padding using theme spacing scale (space_4 = 16px)
2008    pub fn p_4(self) -> Self {
2009        let px = ThemeState::get().spacing().space_4;
2010        self.padding(crate::units::Length::Px(px))
2011    }
2012
2013    /// Set padding using theme spacing scale (space_5 = 20px)
2014    pub fn p_5(self) -> Self {
2015        let px = ThemeState::get().spacing().space_5;
2016        self.padding(crate::units::Length::Px(px))
2017    }
2018
2019    /// Set padding using theme spacing scale (space_6 = 24px)
2020    pub fn p_6(self) -> Self {
2021        let px = ThemeState::get().spacing().space_6;
2022        self.padding(crate::units::Length::Px(px))
2023    }
2024
2025    /// Set padding using theme spacing scale (space_8 = 32px)
2026    pub fn p_8(self) -> Self {
2027        let px = ThemeState::get().spacing().space_8;
2028        self.padding(crate::units::Length::Px(px))
2029    }
2030
2031    /// Set padding using theme spacing scale (space_10 = 40px)
2032    pub fn p_10(self) -> Self {
2033        let px = ThemeState::get().spacing().space_10;
2034        self.padding(crate::units::Length::Px(px))
2035    }
2036
2037    /// Set padding using theme spacing scale (space_12 = 48px)
2038    pub fn p_12(self) -> Self {
2039        let px = ThemeState::get().spacing().space_12;
2040        self.padding(crate::units::Length::Px(px))
2041    }
2042
2043    // =========================================================================
2044    // Raw Pixel Padding (for internal/widget use)
2045    // =========================================================================
2046
2047    /// Set horizontal padding in raw pixels (no unit conversion)
2048    pub fn padding_x_px(mut self, pixels: f32) -> Self {
2049        let px = LengthPercentage::Length(pixels);
2050        self.style.padding.left = px;
2051        self.style.padding.right = px;
2052        self
2053    }
2054
2055    /// Set vertical padding in raw pixels (no unit conversion)
2056    pub fn padding_y_px(mut self, pixels: f32) -> Self {
2057        let px = LengthPercentage::Length(pixels);
2058        self.style.padding.top = px;
2059        self.style.padding.bottom = px;
2060        self
2061    }
2062
2063    // =========================================================================
2064    // Semantic Length-based API (uses Length type for clarity)
2065    // =========================================================================
2066
2067    /// Set padding using a semantic Length value
2068    ///
2069    /// # Examples
2070    /// ```rust,ignore
2071    /// use blinc_layout::prelude::*;
2072    ///
2073    /// div().padding(px(16.0))  // 16 raw pixels
2074    /// div().padding(sp(4.0))   // 4 spacing units = 16px
2075    /// ```
2076    pub fn padding(mut self, len: crate::units::Length) -> Self {
2077        let val: LengthPercentage = len.into();
2078        self.style.padding = Rect {
2079            left: val,
2080            right: val,
2081            top: val,
2082            bottom: val,
2083        };
2084        self
2085    }
2086
2087    /// Set horizontal padding using a semantic Length value
2088    pub fn padding_x(mut self, len: crate::units::Length) -> Self {
2089        let val: LengthPercentage = len.into();
2090        self.style.padding.left = val;
2091        self.style.padding.right = val;
2092        self
2093    }
2094
2095    /// Set vertical padding using a semantic Length value
2096    pub fn padding_y(mut self, len: crate::units::Length) -> Self {
2097        let val: LengthPercentage = len.into();
2098        self.style.padding.top = val;
2099        self.style.padding.bottom = val;
2100        self
2101    }
2102
2103    /// Set margin on all sides (in 4px units)
2104    pub fn m(mut self, units: f32) -> Self {
2105        let px = LengthPercentageAuto::Length(units * 4.0);
2106        self.style.margin = Rect {
2107            left: px,
2108            right: px,
2109            top: px,
2110            bottom: px,
2111        };
2112        self
2113    }
2114
2115    /// Set margin in pixels
2116    pub fn m_px(mut self, px: f32) -> Self {
2117        let val = LengthPercentageAuto::Length(px);
2118        self.style.margin = Rect {
2119            left: val,
2120            right: val,
2121            top: val,
2122            bottom: val,
2123        };
2124        self
2125    }
2126
2127    /// Set horizontal margin (in 4px units)
2128    pub fn mx(mut self, units: f32) -> Self {
2129        let px = LengthPercentageAuto::Length(units * 4.0);
2130        self.style.margin.left = px;
2131        self.style.margin.right = px;
2132        self
2133    }
2134
2135    /// Set vertical margin (in 4px units)
2136    pub fn my(mut self, units: f32) -> Self {
2137        let px = LengthPercentageAuto::Length(units * 4.0);
2138        self.style.margin.top = px;
2139        self.style.margin.bottom = px;
2140        self
2141    }
2142
2143    /// Set auto horizontal margin (centering)
2144    pub fn mx_auto(mut self) -> Self {
2145        self.style.margin.left = LengthPercentageAuto::Auto;
2146        self.style.margin.right = LengthPercentageAuto::Auto;
2147        self
2148    }
2149
2150    /// Set left margin (in 4px units)
2151    pub fn ml(mut self, units: f32) -> Self {
2152        self.style.margin.left = LengthPercentageAuto::Length(units * 4.0);
2153        self
2154    }
2155
2156    /// Set right margin (in 4px units)
2157    pub fn mr(mut self, units: f32) -> Self {
2158        self.style.margin.right = LengthPercentageAuto::Length(units * 4.0);
2159        self
2160    }
2161
2162    /// Set top margin (in 4px units)
2163    pub fn mt(mut self, units: f32) -> Self {
2164        self.style.margin.top = LengthPercentageAuto::Length(units * 4.0);
2165        self
2166    }
2167
2168    /// Set bottom margin (in 4px units)
2169    pub fn mb(mut self, units: f32) -> Self {
2170        self.style.margin.bottom = LengthPercentageAuto::Length(units * 4.0);
2171        self
2172    }
2173
2174    // -------------------------------------------------------------------------
2175    // Theme-based margin
2176    // -------------------------------------------------------------------------
2177
2178    /// Set margin using theme spacing scale (space_1 = 4px)
2179    pub fn m_1(self) -> Self {
2180        self.m_px(ThemeState::get().spacing().space_1)
2181    }
2182
2183    /// Set margin using theme spacing scale (space_2 = 8px)
2184    pub fn m_2(self) -> Self {
2185        self.m_px(ThemeState::get().spacing().space_2)
2186    }
2187
2188    /// Set margin using theme spacing scale (space_3 = 12px)
2189    pub fn m_3(self) -> Self {
2190        self.m_px(ThemeState::get().spacing().space_3)
2191    }
2192
2193    /// Set margin using theme spacing scale (space_4 = 16px)
2194    pub fn m_4(self) -> Self {
2195        self.m_px(ThemeState::get().spacing().space_4)
2196    }
2197
2198    /// Set margin using theme spacing scale (space_5 = 20px)
2199    pub fn m_5(self) -> Self {
2200        self.m_px(ThemeState::get().spacing().space_5)
2201    }
2202
2203    /// Set margin using theme spacing scale (space_6 = 24px)
2204    pub fn m_6(self) -> Self {
2205        self.m_px(ThemeState::get().spacing().space_6)
2206    }
2207
2208    /// Set margin using theme spacing scale (space_8 = 32px)
2209    pub fn m_8(self) -> Self {
2210        self.m_px(ThemeState::get().spacing().space_8)
2211    }
2212
2213    // =========================================================================
2214    // Position
2215    // =========================================================================
2216
2217    /// Set position to absolute
2218    pub fn absolute(mut self) -> Self {
2219        self.style.position = Position::Absolute;
2220        self
2221    }
2222
2223    /// Set position to relative (default)
2224    pub fn relative(mut self) -> Self {
2225        self.style.position = Position::Relative;
2226        self
2227    }
2228
2229    /// Set position to fixed (stays in place when ancestors scroll)
2230    pub fn fixed(mut self) -> Self {
2231        self.style.position = Position::Absolute;
2232        self.is_fixed = true;
2233        self.is_sticky = false;
2234        self
2235    }
2236
2237    /// Set position to sticky with a top threshold
2238    pub fn sticky(mut self, top: f32) -> Self {
2239        self.style.position = Position::Relative;
2240        self.is_sticky = true;
2241        self.is_fixed = false;
2242        self.sticky_top = Some(top);
2243        self
2244    }
2245
2246    /// Set inset (position from all edges)
2247    pub fn inset(mut self, px: f32) -> Self {
2248        let val = LengthPercentageAuto::Length(px);
2249        self.style.inset = Rect {
2250            left: val,
2251            right: val,
2252            top: val,
2253            bottom: val,
2254        };
2255        self
2256    }
2257
2258    /// Set top position
2259    pub fn top(mut self, px: f32) -> Self {
2260        self.style.inset.top = LengthPercentageAuto::Length(px);
2261        self
2262    }
2263
2264    /// Set bottom position
2265    pub fn bottom(mut self, px: f32) -> Self {
2266        self.style.inset.bottom = LengthPercentageAuto::Length(px);
2267        self
2268    }
2269
2270    /// Set left position
2271    pub fn left(mut self, px: f32) -> Self {
2272        self.style.inset.left = LengthPercentageAuto::Length(px);
2273        self
2274    }
2275
2276    /// Set right position
2277    pub fn right(mut self, px: f32) -> Self {
2278        self.style.inset.right = LengthPercentageAuto::Length(px);
2279        self
2280    }
2281
2282    // =========================================================================
2283    // Overflow
2284    // =========================================================================
2285
2286    /// Set overflow to hidden (clip content)
2287    ///
2288    /// Content that extends beyond the element's bounds will be clipped.
2289    /// This is essential for scroll containers.
2290    pub fn overflow_clip(mut self) -> Self {
2291        self.style.overflow.x = Overflow::Clip;
2292        self.style.overflow.y = Overflow::Clip;
2293        self
2294    }
2295
2296    /// Set overflow to visible (default, content can extend beyond bounds)
2297    pub fn overflow_visible(mut self) -> Self {
2298        self.style.overflow.x = Overflow::Visible;
2299        self.style.overflow.y = Overflow::Visible;
2300        self
2301    }
2302
2303    /// Set overflow to scroll (enable scrolling with full physics)
2304    ///
2305    /// Creates a scroll container with momentum scrolling, bounce, and scrollbar.
2306    /// This is equivalent to using the `scroll()` widget but configured via the builder API.
2307    pub fn overflow_scroll(mut self) -> Self {
2308        self.style.overflow.x = Overflow::Scroll;
2309        self.style.overflow.y = Overflow::Scroll;
2310        self.ensure_scroll_physics(crate::scroll::ScrollDirection::Both);
2311        self
2312    }
2313
2314    /// Set overflow to scroll (taffy style only, no physics)
2315    ///
2316    /// Used internally by the `Scroll` widget which manages its own physics.
2317    pub(crate) fn overflow_scroll_style_only(mut self) -> Self {
2318        self.style.overflow.x = Overflow::Scroll;
2319        self.style.overflow.y = Overflow::Scroll;
2320        self
2321    }
2322
2323    /// Set horizontal overflow only (X-axis)
2324    pub fn overflow_x(mut self, overflow: Overflow) -> Self {
2325        self.style.overflow.x = overflow;
2326        if overflow == Overflow::Scroll {
2327            self.ensure_scroll_physics(crate::scroll::ScrollDirection::Horizontal);
2328        }
2329        self
2330    }
2331
2332    /// Set vertical overflow only (Y-axis)
2333    pub fn overflow_y(mut self, overflow: Overflow) -> Self {
2334        self.style.overflow.y = overflow;
2335        if overflow == Overflow::Scroll {
2336            self.ensure_scroll_physics(crate::scroll::ScrollDirection::Vertical);
2337        }
2338        self
2339    }
2340
2341    /// Set overflow to scroll on X-axis only
2342    pub fn overflow_x_scroll(mut self) -> Self {
2343        self.style.overflow.x = Overflow::Scroll;
2344        self.style.overflow.y = Overflow::Clip;
2345        self.ensure_scroll_physics(crate::scroll::ScrollDirection::Horizontal);
2346        self
2347    }
2348
2349    /// Set overflow to scroll on Y-axis only
2350    pub fn overflow_y_scroll(mut self) -> Self {
2351        self.style.overflow.x = Overflow::Clip;
2352        self.style.overflow.y = Overflow::Scroll;
2353        self.ensure_scroll_physics(crate::scroll::ScrollDirection::Vertical);
2354        self
2355    }
2356
2357    /// Ensure scroll physics are created for this div
2358    fn ensure_scroll_physics(&mut self, direction: crate::scroll::ScrollDirection) {
2359        if self.scroll_physics.is_none() {
2360            let config = crate::scroll::ScrollConfig {
2361                direction,
2362                ..Default::default()
2363            };
2364            let physics = std::sync::Arc::new(std::sync::Mutex::new(
2365                crate::scroll::ScrollPhysics::new(config),
2366            ));
2367            let handlers =
2368                crate::scroll::Scroll::create_internal_handlers(std::sync::Arc::clone(&physics));
2369            // Merge scroll handlers into existing event handlers
2370            self.event_handlers.merge(handlers);
2371            self.scroll_physics = Some(physics);
2372        }
2373    }
2374
2375    // =========================================================================
2376    // Visual Properties
2377    // =========================================================================
2378
2379    /// Set background color
2380    pub fn bg(mut self, color: Color) -> Self {
2381        self.background = Some(Brush::Solid(color));
2382        self
2383    }
2384
2385    /// Set background brush (for gradients)
2386    pub fn background(mut self, brush: impl Into<Brush>) -> Self {
2387        self.background = Some(brush.into());
2388        self
2389    }
2390
2391    // -------------------------------------------------------------------------
2392    // Backdrop Blur (CSS backdrop-filter: blur())
2393    // -------------------------------------------------------------------------
2394
2395    /// Apply backdrop blur effect to content behind this element
2396    ///
2397    /// This blurs whatever is rendered behind this element, similar to
2398    /// CSS `backdrop-filter: blur()`. For a full frosted glass effect with
2399    /// tinting and noise, use `.glass()` instead.
2400    ///
2401    /// # Example
2402    ///
2403    /// ```ignore
2404    /// div()
2405    ///     .w(200.0).h(100.0)
2406    ///     .backdrop_blur(10.0) // 10px blur radius
2407    ///     .bg(Color::rgba(255, 255, 255, 128)) // Semi-transparent overlay
2408    /// ```
2409    pub fn backdrop_blur(self, radius: f32) -> Self {
2410        self.background(Brush::Blur(BlurStyle::with_radius(radius)))
2411    }
2412
2413    /// Apply backdrop blur with full style control
2414    ///
2415    /// # Example
2416    ///
2417    /// ```ignore
2418    /// div()
2419    ///     .backdrop_blur_style(
2420    ///         BlurStyle::new()
2421    ///             .radius(20.0)
2422    ///             .quality(BlurQuality::High)
2423    ///             .tint(Color::rgba(255, 255, 255, 50))
2424    ///     )
2425    /// ```
2426    pub fn backdrop_blur_style(self, style: BlurStyle) -> Self {
2427        self.background(Brush::Blur(style))
2428    }
2429
2430    /// Light backdrop blur (5px, subtle)
2431    pub fn backdrop_blur_light(self) -> Self {
2432        self.background(Brush::Blur(BlurStyle::light()))
2433    }
2434
2435    /// Medium backdrop blur (10px, default)
2436    pub fn backdrop_blur_medium(self) -> Self {
2437        self.background(Brush::Blur(BlurStyle::medium()))
2438    }
2439
2440    /// Heavy backdrop blur (20px, strong)
2441    pub fn backdrop_blur_heavy(self) -> Self {
2442        self.background(Brush::Blur(BlurStyle::heavy()))
2443    }
2444
2445    /// Maximum backdrop blur (50px)
2446    pub fn backdrop_blur_max(self) -> Self {
2447        self.background(Brush::Blur(BlurStyle::max()))
2448    }
2449
2450    // -------------------------------------------------------------------------
2451    // Theme-based background colors
2452    // -------------------------------------------------------------------------
2453
2454    /// Set background to theme primary color
2455    pub fn bg_primary(self) -> Self {
2456        self.bg(ThemeState::get().color(blinc_theme::ColorToken::Primary))
2457    }
2458
2459    /// Set background to theme secondary color
2460    pub fn bg_secondary(self) -> Self {
2461        self.bg(ThemeState::get().color(blinc_theme::ColorToken::Secondary))
2462    }
2463
2464    /// Set background to theme background color
2465    pub fn bg_background(self) -> Self {
2466        self.bg(ThemeState::get().color(blinc_theme::ColorToken::Background))
2467    }
2468
2469    /// Set background to theme surface color
2470    pub fn bg_surface(self) -> Self {
2471        self.bg(ThemeState::get().color(blinc_theme::ColorToken::Surface))
2472    }
2473
2474    /// Set background to theme elevated surface color
2475    pub fn bg_surface_elevated(self) -> Self {
2476        self.bg(ThemeState::get().color(blinc_theme::ColorToken::SurfaceElevated))
2477    }
2478
2479    /// Set background to theme success color
2480    pub fn bg_success(self) -> Self {
2481        self.bg(ThemeState::get().color(blinc_theme::ColorToken::SuccessBg))
2482    }
2483
2484    /// Set background to theme warning color
2485    pub fn bg_warning(self) -> Self {
2486        self.bg(ThemeState::get().color(blinc_theme::ColorToken::WarningBg))
2487    }
2488
2489    /// Set background to theme error color
2490    pub fn bg_error(self) -> Self {
2491        self.bg(ThemeState::get().color(blinc_theme::ColorToken::ErrorBg))
2492    }
2493
2494    /// Set background to theme info color
2495    pub fn bg_info(self) -> Self {
2496        self.bg(ThemeState::get().color(blinc_theme::ColorToken::InfoBg))
2497    }
2498
2499    /// Set background to theme accent color
2500    pub fn bg_accent(self) -> Self {
2501        self.bg(ThemeState::get().color(blinc_theme::ColorToken::Accent))
2502    }
2503
2504    // -------------------------------------------------------------------------
2505    // Corner Radius
2506    // -------------------------------------------------------------------------
2507
2508    /// Set corner radius (all corners)
2509    pub fn rounded(mut self, radius: f32) -> Self {
2510        self.border_radius = CornerRadius::uniform(radius);
2511        self.border_radius_explicit = true;
2512        self
2513    }
2514
2515    /// Set corner radius with full pill shape (radius = min(w,h)/2)
2516    pub fn rounded_full(mut self) -> Self {
2517        // Use a large value; actual pill shape depends on element size
2518        self.border_radius = CornerRadius::uniform(9999.0);
2519        self.border_radius_explicit = true;
2520        self
2521    }
2522
2523    /// Set individual corner radii
2524    pub fn rounded_corners(mut self, tl: f32, tr: f32, br: f32, bl: f32) -> Self {
2525        self.border_radius = CornerRadius::new(tl, tr, br, bl);
2526        self.border_radius_explicit = true;
2527        self
2528    }
2529
2530    // -------------------------------------------------------------------------
2531    // Theme-based corner radii
2532    // -------------------------------------------------------------------------
2533
2534    /// Set corner radius to theme's small radius
2535    pub fn rounded_sm(self) -> Self {
2536        self.rounded(ThemeState::get().radii().radius_sm)
2537    }
2538
2539    /// Set corner radius to theme's default radius
2540    pub fn rounded_default(self) -> Self {
2541        self.rounded(ThemeState::get().radii().radius_default)
2542    }
2543
2544    /// Set corner radius to theme's medium radius
2545    pub fn rounded_md(self) -> Self {
2546        self.rounded(ThemeState::get().radii().radius_md)
2547    }
2548
2549    /// Set corner radius to theme's large radius
2550    pub fn rounded_lg(self) -> Self {
2551        self.rounded(ThemeState::get().radii().radius_lg)
2552    }
2553
2554    /// Set corner radius to theme's extra large radius
2555    pub fn rounded_xl(self) -> Self {
2556        self.rounded(ThemeState::get().radii().radius_xl)
2557    }
2558
2559    /// Set corner radius to theme's 2xl radius
2560    pub fn rounded_2xl(self) -> Self {
2561        self.rounded(ThemeState::get().radii().radius_2xl)
2562    }
2563
2564    /// Set corner radius to theme's 3xl radius
2565    pub fn rounded_3xl(self) -> Self {
2566        self.rounded(ThemeState::get().radii().radius_3xl)
2567    }
2568
2569    /// Set corner radius to none (0)
2570    pub fn rounded_none(self) -> Self {
2571        self.rounded(0.0)
2572    }
2573
2574    // =========================================================================
2575    // Corner Shape (superellipse)
2576    // =========================================================================
2577
2578    /// Set uniform corner shape (superellipse exponent)
2579    ///
2580    /// Values: 0.0 = bevel, 1.0 = round (default), 2.0 = squircle, -1.0 = scoop
2581    pub fn corner_shape(mut self, n: f32) -> Self {
2582        self.corner_shape = CornerShape::uniform(n);
2583        self
2584    }
2585
2586    /// Set per-corner shape values (top-left, top-right, bottom-right, bottom-left)
2587    pub fn corner_shapes(mut self, tl: f32, tr: f32, br: f32, bl: f32) -> Self {
2588        self.corner_shape = CornerShape::new(tl, tr, br, bl);
2589        self
2590    }
2591
2592    /// Set corners to bevel (diamond/L1 norm)
2593    pub fn corner_bevel(self) -> Self {
2594        self.corner_shape(0.0)
2595    }
2596
2597    /// Set corners to squircle (L4 superellipse)
2598    pub fn corner_squircle(self) -> Self {
2599        self.corner_shape(2.0)
2600    }
2601
2602    /// Set corners to scoop (concave)
2603    pub fn corner_scoop(self) -> Self {
2604        self.corner_shape(-1.0)
2605    }
2606
2607    // =========================================================================
2608    // Overflow Fade
2609    // =========================================================================
2610
2611    /// Set uniform overflow fade distance on all edges (in CSS pixels)
2612    pub fn overflow_fade(mut self, distance: f32) -> Self {
2613        self.overflow_fade = OverflowFade::uniform(distance);
2614        self
2615    }
2616
2617    /// Set horizontal overflow fade (left and right edges)
2618    pub fn overflow_fade_x(mut self, distance: f32) -> Self {
2619        self.overflow_fade = OverflowFade::horizontal(distance);
2620        self
2621    }
2622
2623    /// Set vertical overflow fade (top and bottom edges)
2624    pub fn overflow_fade_y(mut self, distance: f32) -> Self {
2625        self.overflow_fade = OverflowFade::vertical(distance);
2626        self
2627    }
2628
2629    /// Set per-edge overflow fade distances (top, right, bottom, left)
2630    pub fn overflow_fade_edges(mut self, top: f32, right: f32, bottom: f32, left: f32) -> Self {
2631        self.overflow_fade = OverflowFade::new(top, right, bottom, left);
2632        self
2633    }
2634
2635    // =========================================================================
2636    // Border
2637    // =========================================================================
2638
2639    /// Set border with color and width
2640    pub fn border(mut self, width: f32, color: Color) -> Self {
2641        self.border_width = width;
2642        self.border_color = Some(color);
2643        self
2644    }
2645
2646    /// Set border color only
2647    pub fn border_color(mut self, color: Color) -> Self {
2648        self.border_color = Some(color);
2649        self
2650    }
2651
2652    /// Set border width only
2653    pub fn border_width(mut self, width: f32) -> Self {
2654        self.border_width = width;
2655        self
2656    }
2657
2658    /// Set outline width in pixels
2659    pub fn outline_width(mut self, width: f32) -> Self {
2660        self.outline_width = width;
2661        self
2662    }
2663
2664    /// Set outline color
2665    pub fn outline_color(mut self, color: Color) -> Self {
2666        self.outline_color = Some(color);
2667        self
2668    }
2669
2670    /// Set outline offset in pixels (gap between border and outline)
2671    pub fn outline_offset(mut self, offset: f32) -> Self {
2672        self.outline_offset = offset;
2673        self
2674    }
2675
2676    /// Set left border only (useful for blockquotes)
2677    ///
2678    /// If a uniform border was previously set, other sides will inherit from it.
2679    ///
2680    /// # Example
2681    /// ```ignore
2682    /// div().border_left(4.0, Color::BLUE).pl(16.0)
2683    /// ```
2684    pub fn border_left(mut self, width: f32, color: Color) -> Self {
2685        self.inherit_uniform_border_to_sides();
2686        self.border_sides.left = Some(crate::element::BorderSide::new(width, color));
2687        self
2688    }
2689
2690    /// Set right border only
2691    ///
2692    /// If a uniform border was previously set, other sides will inherit from it.
2693    pub fn border_right(mut self, width: f32, color: Color) -> Self {
2694        self.inherit_uniform_border_to_sides();
2695        self.border_sides.right = Some(crate::element::BorderSide::new(width, color));
2696        self
2697    }
2698
2699    /// Set top border only
2700    ///
2701    /// If a uniform border was previously set, other sides will inherit from it.
2702    pub fn border_top(mut self, width: f32, color: Color) -> Self {
2703        self.inherit_uniform_border_to_sides();
2704        self.border_sides.top = Some(crate::element::BorderSide::new(width, color));
2705        self
2706    }
2707
2708    /// Set bottom border only
2709    ///
2710    /// If a uniform border was previously set, other sides will inherit from it.
2711    pub fn border_bottom(mut self, width: f32, color: Color) -> Self {
2712        self.inherit_uniform_border_to_sides();
2713        self.border_sides.bottom = Some(crate::element::BorderSide::new(width, color));
2714        self
2715    }
2716
2717    /// Set horizontal borders (left and right)
2718    ///
2719    /// If a uniform border was previously set, vertical sides will inherit from it.
2720    pub fn border_x(mut self, width: f32, color: Color) -> Self {
2721        self.inherit_uniform_border_to_sides();
2722        let side = crate::element::BorderSide::new(width, color);
2723        self.border_sides.left = Some(side);
2724        self.border_sides.right = Some(side);
2725        self
2726    }
2727
2728    /// Set vertical borders (top and bottom)
2729    ///
2730    /// If a uniform border was previously set, horizontal sides will inherit from it.
2731    pub fn border_y(mut self, width: f32, color: Color) -> Self {
2732        self.inherit_uniform_border_to_sides();
2733        let side = crate::element::BorderSide::new(width, color);
2734        self.border_sides.top = Some(side);
2735        self.border_sides.bottom = Some(side);
2736        self
2737    }
2738
2739    /// Helper to inherit uniform border to per-side borders before modifying a specific side
2740    ///
2741    /// This ensures that when you do `.border(1.0, color).border_bottom(3.0, color)`,
2742    /// the other three sides get the uniform border instead of being empty.
2743    fn inherit_uniform_border_to_sides(&mut self) {
2744        // Only inherit if we have a uniform border and no per-side borders yet
2745        if self.border_width > 0.0 && !self.border_sides.has_any() {
2746            if let Some(color) = self.border_color {
2747                let side = crate::element::BorderSide::new(self.border_width, color);
2748                self.border_sides.top = Some(side);
2749                self.border_sides.right = Some(side);
2750                self.border_sides.bottom = Some(side);
2751                self.border_sides.left = Some(side);
2752            }
2753        }
2754    }
2755
2756    /// Configure borders using a builder pattern
2757    ///
2758    /// This provides a fluent API for setting multiple border sides at once
2759    /// without accidentally overriding previously set sides.
2760    ///
2761    /// # Example
2762    ///
2763    /// ```ignore
2764    /// // Set left and bottom borders
2765    /// div().borders(|b| b.left(4.0, Color::BLUE).bottom(1.0, Color::gray(0.3)))
2766    ///
2767    /// // Set all borders, then override one
2768    /// div().borders(|b| b.all(1.0, Color::gray(0.2)).left(4.0, Color::BLUE))
2769    ///
2770    /// // Set horizontal borders only
2771    /// div().borders(|b| b.x(1.0, Color::RED))
2772    /// ```
2773    pub fn borders<F>(mut self, f: F) -> Self
2774    where
2775        F: FnOnce(crate::element::BorderBuilder) -> crate::element::BorderBuilder,
2776    {
2777        let builder = f(crate::element::BorderBuilder::new());
2778        self.border_sides = builder.build();
2779        self
2780    }
2781
2782    // =========================================================================
2783    // Layer (for rendering order)
2784    // =========================================================================
2785
2786    /// Set the render layer
2787    pub fn layer(mut self, layer: RenderLayer) -> Self {
2788        self.render_layer = layer;
2789        self
2790    }
2791
2792    /// Render in foreground (on top of glass)
2793    pub fn foreground(self) -> Self {
2794        self.layer(RenderLayer::Foreground)
2795    }
2796
2797    // =========================================================================
2798    // Material System
2799    // =========================================================================
2800
2801    /// Apply a material to this element
2802    pub fn material(mut self, material: Material) -> Self {
2803        // Glass materials also set the render layer to Glass
2804        if matches!(material, Material::Glass(_)) {
2805            self.render_layer = RenderLayer::Glass;
2806        }
2807        self.material = Some(material);
2808        self
2809    }
2810
2811    /// Apply a visual effect to this element
2812    ///
2813    /// Effects include glass (blur), metallic (reflection), wood (texture), etc.
2814    /// This is the general-purpose method for applying any material effect.
2815    ///
2816    /// Example:
2817    /// ```ignore
2818    /// // Glass effect
2819    /// div().effect(GlassMaterial::thick().tint_rgba(1.0, 0.9, 0.9, 0.5))
2820    ///
2821    /// // Metallic effect
2822    /// div().effect(MetallicMaterial::chrome())
2823    /// ```
2824    pub fn effect(self, effect: impl Into<Material>) -> Self {
2825        self.material(effect.into())
2826    }
2827
2828    /// Apply glass material with default settings (shorthand for common case)
2829    ///
2830    /// Creates a frosted glass effect that blurs content behind the element.
2831    pub fn glass(self) -> Self {
2832        self.material(Material::Glass(GlassMaterial::new()))
2833    }
2834
2835    /// Apply metallic material with default settings
2836    pub fn metallic(self) -> Self {
2837        self.material(Material::Metallic(MetallicMaterial::new()))
2838    }
2839
2840    /// Apply chrome metallic preset
2841    pub fn chrome(self) -> Self {
2842        self.material(Material::Metallic(MetallicMaterial::chrome()))
2843    }
2844
2845    /// Apply gold metallic preset
2846    pub fn gold(self) -> Self {
2847        self.material(Material::Metallic(MetallicMaterial::gold()))
2848    }
2849
2850    /// Apply wood material with default settings
2851    pub fn wood(self) -> Self {
2852        self.material(Material::Wood(WoodMaterial::new()))
2853    }
2854
2855    // =========================================================================
2856    // Shadow
2857    // =========================================================================
2858
2859    /// Apply a drop shadow to this element
2860    pub fn shadow(mut self, shadow: Shadow) -> Self {
2861        self.shadow = Some(shadow);
2862        self
2863    }
2864
2865    /// Apply a drop shadow with the given parameters
2866    pub fn shadow_params(self, offset_x: f32, offset_y: f32, blur: f32, color: Color) -> Self {
2867        self.shadow(Shadow::new(offset_x, offset_y, blur, color))
2868    }
2869
2870    /// Apply a small drop shadow using theme colors
2871    pub fn shadow_sm(self) -> Self {
2872        self.shadow(ThemeState::get().shadows().shadow_sm.into())
2873    }
2874
2875    /// Apply a medium drop shadow using theme colors
2876    pub fn shadow_md(self) -> Self {
2877        self.shadow(ThemeState::get().shadows().shadow_md.into())
2878    }
2879
2880    /// Apply a large drop shadow using theme colors
2881    pub fn shadow_lg(self) -> Self {
2882        self.shadow(ThemeState::get().shadows().shadow_lg.into())
2883    }
2884
2885    /// Apply an extra large drop shadow using theme colors
2886    pub fn shadow_xl(self) -> Self {
2887        self.shadow(ThemeState::get().shadows().shadow_xl.into())
2888    }
2889
2890    // =========================================================================
2891    // Transform
2892    // =========================================================================
2893
2894    /// Apply a transform to this element
2895    pub fn transform(mut self, transform: Transform) -> Self {
2896        self.transform = Some(transform);
2897        self
2898    }
2899
2900    /// Translate this element by the given x and y offset
2901    pub fn translate(self, x: f32, y: f32) -> Self {
2902        self.transform(Transform::translate(x, y))
2903    }
2904
2905    /// Scale this element uniformly
2906    pub fn scale(self, factor: f32) -> Self {
2907        self.transform(Transform::scale(factor, factor))
2908    }
2909
2910    /// Scale this element with different x and y factors
2911    pub fn scale_xy(self, sx: f32, sy: f32) -> Self {
2912        self.transform(Transform::scale(sx, sy))
2913    }
2914
2915    /// Rotate this element by the given angle in radians
2916    pub fn rotate(self, angle: f32) -> Self {
2917        self.transform(Transform::rotate(angle))
2918    }
2919
2920    /// Rotate this element by the given angle in degrees
2921    pub fn rotate_deg(self, degrees: f32) -> Self {
2922        self.rotate(degrees * std::f32::consts::PI / 180.0)
2923    }
2924
2925    // =========================================================================
2926    // Opacity
2927    // =========================================================================
2928
2929    /// Set opacity (0.0 = transparent, 1.0 = opaque)
2930    pub fn opacity(mut self, opacity: f32) -> Self {
2931        self.opacity = opacity.clamp(0.0, 1.0);
2932        self
2933    }
2934
2935    /// Fully opaque (opacity = 1.0)
2936    pub fn opaque(self) -> Self {
2937        self.opacity(1.0)
2938    }
2939
2940    /// Set a @flow shader on this element.
2941    ///
2942    /// Accepts either a `FlowGraph` (from the `flow!` macro) or a `&str` name
2943    /// referencing a flow defined in a CSS stylesheet.
2944    ///
2945    /// ```rust,ignore
2946    /// // Direct flow graph
2947    /// div().flow(flow!(ripple, fragment, { ... }))
2948    ///
2949    /// // Name reference to CSS-defined flow
2950    /// div().flow("ripple")
2951    /// ```
2952    pub fn flow(mut self, f: impl Into<crate::element::FlowRef>) -> Self {
2953        match f.into() {
2954            crate::element::FlowRef::Name(name) => {
2955                self.flow_name = Some(name);
2956            }
2957            crate::element::FlowRef::Graph(g) => {
2958                self.flow_name = Some(g.name.clone());
2959                self.flow_graph = Some(g);
2960            }
2961        }
2962        self
2963    }
2964
2965    /// Semi-transparent (opacity = 0.5)
2966    pub fn translucent(self) -> Self {
2967        self.opacity(0.5)
2968    }
2969
2970    /// Invisible (opacity = 0.0)
2971    pub fn invisible(self) -> Self {
2972        self.opacity(0.0)
2973    }
2974
2975    // =========================================================================
2976    // Layer Effects (Post-Processing)
2977    // =========================================================================
2978
2979    /// Add a layer effect to this element
2980    ///
2981    /// Layer effects are applied during layer composition and include:
2982    /// - Blur (Gaussian blur)
2983    /// - DropShadow (offset shadow behind element)
2984    /// - Glow (outer glow)
2985    /// - ColorMatrix (color transformation)
2986    ///
2987    /// # Example
2988    ///
2989    /// ```ignore
2990    /// div()
2991    ///     .w(100.0).h(100.0)
2992    ///     .bg(Color::BLUE)
2993    ///     .layer_effect(LayerEffect::blur(8.0))
2994    /// ```
2995    pub fn layer_effect(mut self, effect: LayerEffect) -> Self {
2996        self.layer_effects.push(effect);
2997        self
2998    }
2999
3000    /// Add a blur effect
3001    ///
3002    /// # Example
3003    ///
3004    /// ```ignore
3005    /// div()
3006    ///     .w(100.0).h(100.0)
3007    ///     .bg(Color::BLUE)
3008    ///     .blur(8.0) // 8px blur radius
3009    /// ```
3010    pub fn blur(self, radius: f32) -> Self {
3011        self.layer_effect(LayerEffect::blur(radius))
3012    }
3013
3014    /// Add a blur effect with specific quality
3015    ///
3016    /// Quality options:
3017    /// - `BlurQuality::Low` - Single-pass box blur (fastest)
3018    /// - `BlurQuality::Medium` - Two-pass separable Gaussian (balanced)
3019    /// - `BlurQuality::High` - Multi-pass Kawase blur (highest quality)
3020    pub fn blur_with_quality(self, radius: f32, quality: BlurQuality) -> Self {
3021        self.layer_effect(LayerEffect::blur_with_quality(radius, quality))
3022    }
3023
3024    /// Add a drop shadow effect
3025    ///
3026    /// This is a layer-level shadow effect (post-processing), different from
3027    /// the element shadow set via `.shadow()` which is rendered inline.
3028    ///
3029    /// # Example
3030    ///
3031    /// ```ignore
3032    /// div()
3033    ///     .w(100.0).h(100.0)
3034    ///     .bg(Color::WHITE)
3035    ///     .drop_shadow_effect(4.0, 4.0, 8.0, Color::rgba(0, 0, 0, 128))
3036    /// ```
3037    pub fn drop_shadow_effect(self, offset_x: f32, offset_y: f32, blur: f32, color: Color) -> Self {
3038        self.layer_effect(LayerEffect::drop_shadow(offset_x, offset_y, blur, color))
3039    }
3040
3041    /// Add a glow effect
3042    ///
3043    /// Creates an outer glow around the element.
3044    ///
3045    /// # Example
3046    ///
3047    /// ```ignore
3048    /// div()
3049    ///     .w(100.0).h(100.0)
3050    ///     .bg(Color::BLUE)
3051    ///     .glow_effect(Color::CYAN, 8.0, 4.0, 0.8)
3052    /// ```
3053    ///
3054    /// ## Parameters
3055    /// - `color`: Glow color
3056    /// - `blur`: Blur softness (higher = softer edges), typically 4-24
3057    /// - `range`: How far the glow extends from the element, typically 0-20
3058    /// - `opacity`: Glow visibility (0.0 to 1.0)
3059    pub fn glow_effect(self, color: Color, blur: f32, range: f32, opacity: f32) -> Self {
3060        self.layer_effect(LayerEffect::glow(color, blur, range, opacity))
3061    }
3062
3063    /// Apply grayscale filter
3064    pub fn grayscale(self) -> Self {
3065        self.layer_effect(LayerEffect::grayscale())
3066    }
3067
3068    /// Apply sepia filter
3069    pub fn sepia(self) -> Self {
3070        self.layer_effect(LayerEffect::sepia())
3071    }
3072
3073    /// Adjust brightness (1.0 = normal, <1.0 = darker, >1.0 = brighter)
3074    pub fn brightness(self, factor: f32) -> Self {
3075        self.layer_effect(LayerEffect::brightness(factor))
3076    }
3077
3078    /// Adjust contrast (1.0 = normal, <1.0 = less contrast, >1.0 = more contrast)
3079    pub fn contrast(self, factor: f32) -> Self {
3080        self.layer_effect(LayerEffect::contrast(factor))
3081    }
3082
3083    /// Adjust saturation (0.0 = grayscale, 1.0 = normal, >1.0 = oversaturated)
3084    pub fn saturation(self, factor: f32) -> Self {
3085        self.layer_effect(LayerEffect::saturation(factor))
3086    }
3087
3088    // =========================================================================
3089    // Cursor Style
3090    // =========================================================================
3091
3092    /// Set the cursor style when hovering over this element
3093    ///
3094    /// # Example
3095    ///
3096    /// ```ignore
3097    /// div()
3098    ///     .w(100.0).h(50.0)
3099    ///     .bg(Color::BLUE)
3100    ///     .cursor(CursorStyle::Pointer) // Hand cursor for clickable elements
3101    ///     .on_click(|_| println!("Clicked!"))
3102    /// ```
3103    pub fn cursor(mut self, cursor: crate::element::CursorStyle) -> Self {
3104        self.cursor = Some(cursor);
3105        self
3106    }
3107
3108    /// Set cursor to pointer (hand) - convenience for clickable elements
3109    pub fn cursor_pointer(self) -> Self {
3110        self.cursor(crate::element::CursorStyle::Pointer)
3111    }
3112
3113    /// Set cursor to text (I-beam) - for text input areas
3114    pub fn cursor_text(self) -> Self {
3115        self.cursor(crate::element::CursorStyle::Text)
3116    }
3117
3118    /// Set cursor to move - for draggable elements
3119    pub fn cursor_move(self) -> Self {
3120        self.cursor(crate::element::CursorStyle::Move)
3121    }
3122
3123    /// Set cursor to grab (open hand) - for grabbable elements
3124    pub fn cursor_grab(self) -> Self {
3125        self.cursor(crate::element::CursorStyle::Grab)
3126    }
3127
3128    /// Set cursor to grabbing (closed hand) - while dragging
3129    pub fn cursor_grabbing(self) -> Self {
3130        self.cursor(crate::element::CursorStyle::Grabbing)
3131    }
3132
3133    /// Set cursor to not-allowed - for disabled elements
3134    pub fn cursor_not_allowed(self) -> Self {
3135        self.cursor(crate::element::CursorStyle::NotAllowed)
3136    }
3137
3138    /// Make this element transparent to hit-testing (pointer-events: none)
3139    ///
3140    /// When set, this element will not capture mouse clicks or hover events.
3141    /// Only its children can receive events. Useful for wrapper elements that
3142    /// should not block interaction with elements behind them.
3143    pub fn pointer_events_none(mut self) -> Self {
3144        self.pointer_events_none = true;
3145        self
3146    }
3147
3148    /// Mark this element as a stack layer for z-ordering
3149    ///
3150    /// When set, entering this element increments the z_layer counter,
3151    /// ensuring its content renders above previous siblings in the
3152    /// interleaved primitive/text rendering. This is used internally
3153    /// by Stack and can be used for overlay containers that need to
3154    /// render above other content.
3155    pub fn stack_layer(mut self) -> Self {
3156        self.is_stack_layer = true;
3157        self
3158    }
3159
3160    /// Set CSS z-index for stacking order (higher values render on top)
3161    pub fn z_index(mut self, z: i32) -> Self {
3162        self.z_index = z;
3163        self
3164    }
3165
3166    // =========================================================================
3167    // Children
3168    // =========================================================================
3169
3170    /// Add a child element
3171    pub fn child(mut self, child: impl ElementBuilder + 'static) -> Self {
3172        self.children.push(Box::new(child));
3173        self
3174    }
3175
3176    /// Add a boxed child element (for dynamic element types)
3177    pub fn child_box(mut self, child: Box<dyn ElementBuilder>) -> Self {
3178        self.children.push(child);
3179        self
3180    }
3181
3182    /// Add multiple children
3183    pub fn children<I>(mut self, children: I) -> Self
3184    where
3185        I: IntoIterator,
3186        I::Item: ElementBuilder + 'static,
3187    {
3188        for child in children {
3189            self.children.push(Box::new(child));
3190        }
3191        self
3192    }
3193
3194    /// Get direct access to the taffy style for advanced configuration
3195    pub fn style_mut(&mut self) -> &mut Style {
3196        &mut self.style
3197    }
3198
3199    // =========================================================================
3200    // Event Handlers
3201    // =========================================================================
3202
3203    /// Get a reference to the event handlers
3204    pub fn event_handlers(&self) -> &crate::event_handler::EventHandlers {
3205        &self.event_handlers
3206    }
3207
3208    /// Get a mutable reference to the event handlers
3209    pub fn event_handlers_mut(&mut self) -> &mut crate::event_handler::EventHandlers {
3210        &mut self.event_handlers
3211    }
3212
3213    /// Register a click handler (fired on POINTER_UP)
3214    ///
3215    /// # Example
3216    ///
3217    /// ```ignore
3218    /// div()
3219    ///     .w(100.0).h(50.0)
3220    ///     .bg(Color::BLUE)
3221    ///     .on_click(|ctx| {
3222    ///         println!("Clicked at ({}, {})", ctx.local_x, ctx.local_y);
3223    ///     })
3224    /// ```
3225    pub fn on_click<F>(mut self, handler: F) -> Self
3226    where
3227        F: Fn(&crate::event_handler::EventContext) + 'static,
3228    {
3229        self.event_handlers.on_click(handler);
3230        self
3231    }
3232
3233    /// Register a mouse down handler
3234    pub fn on_mouse_down<F>(mut self, handler: F) -> Self
3235    where
3236        F: Fn(&crate::event_handler::EventContext) + 'static,
3237    {
3238        self.event_handlers.on_mouse_down(handler);
3239        self
3240    }
3241
3242    /// Register a mouse up handler
3243    pub fn on_mouse_up<F>(mut self, handler: F) -> Self
3244    where
3245        F: Fn(&crate::event_handler::EventContext) + 'static,
3246    {
3247        self.event_handlers.on_mouse_up(handler);
3248        self
3249    }
3250
3251    /// Register a hover enter handler
3252    ///
3253    /// # Example
3254    ///
3255    /// ```ignore
3256    /// div()
3257    ///     .on_hover_enter(|_| println!("Mouse entered!"))
3258    ///     .on_hover_leave(|_| println!("Mouse left!"))
3259    /// ```
3260    pub fn on_hover_enter<F>(mut self, handler: F) -> Self
3261    where
3262        F: Fn(&crate::event_handler::EventContext) + 'static,
3263    {
3264        self.event_handlers.on_hover_enter(handler);
3265        self
3266    }
3267
3268    /// Register a hover leave handler
3269    pub fn on_hover_leave<F>(mut self, handler: F) -> Self
3270    where
3271        F: Fn(&crate::event_handler::EventContext) + 'static,
3272    {
3273        self.event_handlers.on_hover_leave(handler);
3274        self
3275    }
3276
3277    /// Register a focus handler
3278    pub fn on_focus<F>(mut self, handler: F) -> Self
3279    where
3280        F: Fn(&crate::event_handler::EventContext) + 'static,
3281    {
3282        self.event_handlers.on_focus(handler);
3283        self
3284    }
3285
3286    /// Register a blur handler
3287    pub fn on_blur<F>(mut self, handler: F) -> Self
3288    where
3289        F: Fn(&crate::event_handler::EventContext) + 'static,
3290    {
3291        self.event_handlers.on_blur(handler);
3292        self
3293    }
3294
3295    /// Register a mount handler (element added to tree)
3296    pub fn on_mount<F>(mut self, handler: F) -> Self
3297    where
3298        F: Fn(&crate::event_handler::EventContext) + 'static,
3299    {
3300        self.event_handlers.on_mount(handler);
3301        self
3302    }
3303
3304    /// Register an unmount handler (element removed from tree)
3305    pub fn on_unmount<F>(mut self, handler: F) -> Self
3306    where
3307        F: Fn(&crate::event_handler::EventContext) + 'static,
3308    {
3309        self.event_handlers.on_unmount(handler);
3310        self
3311    }
3312
3313    /// Register a key down handler (requires focus)
3314    pub fn on_key_down<F>(mut self, handler: F) -> Self
3315    where
3316        F: Fn(&crate::event_handler::EventContext) + 'static,
3317    {
3318        self.event_handlers.on_key_down(handler);
3319        self
3320    }
3321
3322    /// Register a key up handler (requires focus)
3323    pub fn on_key_up<F>(mut self, handler: F) -> Self
3324    where
3325        F: Fn(&crate::event_handler::EventContext) + 'static,
3326    {
3327        self.event_handlers.on_key_up(handler);
3328        self
3329    }
3330
3331    /// Register a scroll handler
3332    pub fn on_scroll<F>(mut self, handler: F) -> Self
3333    where
3334        F: Fn(&crate::event_handler::EventContext) + 'static,
3335    {
3336        self.event_handlers.on_scroll(handler);
3337        self
3338    }
3339
3340    /// Register a mouse move handler (pointer movement over this element)
3341    pub fn on_mouse_move<F>(mut self, handler: F) -> Self
3342    where
3343        F: Fn(&crate::event_handler::EventContext) + 'static,
3344    {
3345        self.event_handlers
3346            .on(blinc_core::events::event_types::POINTER_MOVE, handler);
3347        self
3348    }
3349
3350    /// Register a drag handler (pointer movement while button is pressed)
3351    pub fn on_drag<F>(mut self, handler: F) -> Self
3352    where
3353        F: Fn(&crate::event_handler::EventContext) + 'static,
3354    {
3355        self.event_handlers
3356            .on(blinc_core::events::event_types::DRAG, handler);
3357        self
3358    }
3359
3360    /// Register a drag end handler (pointer movement while button is pressed)
3361    pub fn on_drag_end<F>(mut self, handler: F) -> Self
3362    where
3363        F: Fn(&crate::event_handler::EventContext) + 'static,
3364    {
3365        self.event_handlers
3366            .on(blinc_core::events::event_types::DRAG_END, handler);
3367        self
3368    }
3369
3370    /// Register a text input handler (receives character input when focused)
3371    pub fn on_text_input<F>(mut self, handler: F) -> Self
3372    where
3373        F: Fn(&crate::event_handler::EventContext) + 'static,
3374    {
3375        self.event_handlers.on_text_input(handler);
3376        self
3377    }
3378
3379    /// Register a resize handler
3380    pub fn on_resize<F>(mut self, handler: F) -> Self
3381    where
3382        F: Fn(&crate::event_handler::EventContext) + 'static,
3383    {
3384        self.event_handlers.on_resize(handler);
3385        self
3386    }
3387
3388    /// Register a handler for a specific event type
3389    ///
3390    /// This is the low-level method for registering handlers for any event type.
3391    ///
3392    /// # Example
3393    ///
3394    /// ```ignore
3395    /// use blinc_core::events::event_types;
3396    ///
3397    /// div().on_event(event_types::POINTER_MOVE, |ctx| {
3398    ///     println!("Mouse moved to ({}, {})", ctx.mouse_x, ctx.mouse_y);
3399    /// })
3400    /// ```
3401    pub fn on_event<F>(mut self, event_type: blinc_core::events::EventType, handler: F) -> Self
3402    where
3403        F: Fn(&crate::event_handler::EventContext) + 'static,
3404    {
3405        self.event_handlers.on(event_type, handler);
3406        self
3407    }
3408}
3409
3410/// Element type identifier for downcasting
3411#[derive(Clone, Copy, Debug, PartialEq, Eq)]
3412pub enum ElementTypeId {
3413    Div,
3414    Text,
3415    /// Styled text with inline formatting spans
3416    StyledText,
3417    Svg,
3418    Image,
3419    Canvas,
3420    /// Motion container (transparent wrapper for animations)
3421    Motion,
3422}
3423
3424/// Text alignment options
3425#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
3426pub enum TextAlign {
3427    /// Align text to the left (default)
3428    #[default]
3429    Left,
3430    /// Center text
3431    Center,
3432    /// Align text to the right
3433    Right,
3434}
3435
3436/// Font weight options
3437#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
3438pub enum FontWeight {
3439    /// Thin (100)
3440    Thin,
3441    /// Extra Light (200)
3442    ExtraLight,
3443    /// Light (300)
3444    Light,
3445    /// Normal/Regular (400)
3446    #[default]
3447    Normal,
3448    /// Medium (500)
3449    Medium,
3450    /// Semi Bold (600)
3451    SemiBold,
3452    /// Bold (700)
3453    Bold,
3454    /// Extra Bold (800)
3455    ExtraBold,
3456    /// Black (900)
3457    Black,
3458}
3459
3460impl FontWeight {
3461    /// Get the numeric weight value (100-900)
3462    pub fn weight(&self) -> u16 {
3463        match self {
3464            FontWeight::Thin => 100,
3465            FontWeight::ExtraLight => 200,
3466            FontWeight::Light => 300,
3467            FontWeight::Normal => 400,
3468            FontWeight::Medium => 500,
3469            FontWeight::SemiBold => 600,
3470            FontWeight::Bold => 700,
3471            FontWeight::ExtraBold => 800,
3472            FontWeight::Black => 900,
3473        }
3474    }
3475}
3476
3477/// Vertical alignment for text within its bounding box
3478#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
3479pub enum TextVerticalAlign {
3480    /// Text is positioned at the top of its bounding box (default for multi-line text).
3481    /// Glyphs are centered within the line height for proper vertical centering.
3482    #[default]
3483    Top,
3484    /// Text is optically centered within its bounding box (for single-line centered text).
3485    /// Uses cap-height based centering for better visual appearance.
3486    Center,
3487    /// Text is positioned by its baseline. Use this for inline text that should
3488    /// align by baseline with other text elements (e.g., mixing fonts in a paragraph).
3489    /// The baseline position is at a standardized offset from the top of the bounding box.
3490    Baseline,
3491}
3492
3493/// Generic font category for fallback when a named font isn't available
3494#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
3495pub enum GenericFont {
3496    /// Default system UI font
3497    #[default]
3498    System,
3499    /// Monospace font for code (Menlo, Consolas, Monaco, etc.)
3500    Monospace,
3501    /// Serif font (Times, Georgia, etc.)
3502    Serif,
3503    /// Sans-serif font (Helvetica, Arial, etc.)
3504    SansSerif,
3505}
3506
3507/// Font family specification
3508///
3509/// Allows specifying either a named font (e.g., "Fira Code", "Inter") or
3510/// a generic category. When a named font is specified, the generic category
3511/// serves as a fallback if the font isn't available.
3512///
3513/// # Example
3514///
3515/// ```ignore
3516/// // Use a specific font with monospace fallback
3517/// text("code").font("Fira Code")
3518///
3519/// // Use system monospace
3520/// text("code").monospace()
3521/// ```
3522#[derive(Debug, Clone, PartialEq, Eq, Default)]
3523pub struct FontFamily {
3524    /// Specific font name (e.g., "Fira Code", "Inter", "SF Pro")
3525    pub name: Option<String>,
3526    /// Generic fallback category
3527    pub generic: GenericFont,
3528}
3529
3530impl FontFamily {
3531    /// Create a font family with just a generic category
3532    pub fn generic(generic: GenericFont) -> Self {
3533        Self {
3534            name: None,
3535            generic,
3536        }
3537    }
3538
3539    /// Create a font family with a specific font name
3540    pub fn named(name: impl Into<String>) -> Self {
3541        Self {
3542            name: Some(name.into()),
3543            generic: GenericFont::System,
3544        }
3545    }
3546
3547    /// Create a font family with a specific name and fallback category
3548    pub fn named_with_fallback(name: impl Into<String>, generic: GenericFont) -> Self {
3549        Self {
3550            name: Some(name.into()),
3551            generic,
3552        }
3553    }
3554
3555    /// System UI font
3556    pub fn system() -> Self {
3557        Self::generic(GenericFont::System)
3558    }
3559
3560    /// Monospace font
3561    pub fn monospace() -> Self {
3562        Self::generic(GenericFont::Monospace)
3563    }
3564
3565    /// Serif font
3566    pub fn serif() -> Self {
3567        Self::generic(GenericFont::Serif)
3568    }
3569
3570    /// Sans-serif font
3571    pub fn sans_serif() -> Self {
3572        Self::generic(GenericFont::SansSerif)
3573    }
3574}
3575
3576/// Text render data extracted from element
3577#[derive(Clone)]
3578pub struct TextRenderInfo {
3579    pub content: String,
3580    pub font_size: f32,
3581    pub color: [f32; 4],
3582    pub align: TextAlign,
3583    pub weight: FontWeight,
3584    /// Whether to use italic style
3585    pub italic: bool,
3586    pub v_align: TextVerticalAlign,
3587    /// Whether to wrap text at container bounds (default: true for text())
3588    pub wrap: bool,
3589    /// Line height multiplier (default: 1.2)
3590    pub line_height: f32,
3591    /// Measured width of the text (before any layout constraints)
3592    /// Used to determine if wrapping is actually needed at render time
3593    pub measured_width: f32,
3594    /// Font family category
3595    pub font_family: FontFamily,
3596    /// Word spacing in pixels (0.0 = normal)
3597    pub word_spacing: f32,
3598    /// Letter spacing in pixels (0.0 = normal)
3599    pub letter_spacing: f32,
3600    /// Font ascender in pixels (distance from baseline to top)
3601    /// Used for accurate baseline alignment across different fonts
3602    pub ascender: f32,
3603    /// Whether text has strikethrough decoration
3604    pub strikethrough: bool,
3605    /// Whether text has underline decoration
3606    pub underline: bool,
3607}
3608
3609/// A span within styled text (for rich_text element)
3610#[derive(Clone)]
3611pub struct StyledTextSpanInfo {
3612    /// Start byte index
3613    pub start: usize,
3614    /// End byte index (exclusive)
3615    pub end: usize,
3616    /// RGBA color
3617    pub color: [f32; 4],
3618    /// Whether span is bold
3619    pub bold: bool,
3620    /// Whether span is italic
3621    pub italic: bool,
3622    /// Whether span has underline
3623    pub underline: bool,
3624    /// Whether span has strikethrough
3625    pub strikethrough: bool,
3626    /// Optional link URL
3627    pub link_url: Option<String>,
3628}
3629
3630/// Styled text render data (for rich_text element with inline formatting)
3631#[derive(Clone)]
3632pub struct StyledTextRenderInfo {
3633    /// Plain text content (tags stripped)
3634    pub content: String,
3635    /// Style spans
3636    pub spans: Vec<StyledTextSpanInfo>,
3637    /// Font size
3638    pub font_size: f32,
3639    /// Default color for unspanned regions
3640    pub default_color: [f32; 4],
3641    /// Text alignment
3642    pub align: TextAlign,
3643    /// Vertical alignment
3644    pub v_align: TextVerticalAlign,
3645    /// Font family
3646    pub font_family: FontFamily,
3647    /// Line height multiplier
3648    pub line_height: f32,
3649    /// Default font weight (for unspanned regions or spans without explicit weight)
3650    pub weight: FontWeight,
3651    /// Default italic style (for unspanned regions or spans without explicit italic)
3652    pub italic: bool,
3653    /// Measured ascender from font metrics (for consistent baseline alignment)
3654    pub ascender: f32,
3655}
3656
3657/// SVG render data extracted from element
3658#[derive(Clone)]
3659pub struct SvgRenderInfo {
3660    pub source: Arc<str>,
3661    pub tint: Option<blinc_core::Color>,
3662    pub fill: Option<blinc_core::Color>,
3663    pub stroke: Option<blinc_core::Color>,
3664    pub stroke_width: Option<f32>,
3665}
3666
3667/// Image render data extracted from element
3668#[derive(Clone)]
3669pub struct ImageRenderInfo {
3670    /// Image source (file path, URL, or base64 data)
3671    pub source: String,
3672    /// Object-fit mode (cover, contain, fill, scale-down, none)
3673    pub object_fit: u8,
3674    /// Object position (x: 0.0-1.0, y: 0.0-1.0)
3675    pub object_position: [f32; 2],
3676    /// Opacity (0.0 - 1.0)
3677    pub opacity: f32,
3678    /// Border radius for rounded corners
3679    pub border_radius: f32,
3680    /// Tint color [r, g, b, a]
3681    pub tint: [f32; 4],
3682    /// Filter: [grayscale, sepia, brightness, contrast, saturate, hue_rotate, invert, blur]
3683    pub filter: [f32; 8],
3684    /// Loading strategy: 0 = Eager (default), 1 = Lazy
3685    pub loading_strategy: u8,
3686    /// Placeholder type: 0 = None, 1 = Color, 2 = Image, 3 = Skeleton
3687    pub placeholder_type: u8,
3688    /// Placeholder color (only used when placeholder_type == 1)
3689    pub placeholder_color: [f32; 4],
3690    /// Placeholder image source (only used when placeholder_type == 2)
3691    pub placeholder_image: Option<String>,
3692    /// Fade-in duration in milliseconds
3693    pub fade_duration_ms: u32,
3694}
3695
3696impl Default for ImageRenderInfo {
3697    fn default() -> Self {
3698        Self {
3699            source: String::new(),
3700            object_fit: 0,               // Cover
3701            object_position: [0.5, 0.5], // Center
3702            opacity: 1.0,
3703            border_radius: 0.0,
3704            tint: [1.0, 1.0, 1.0, 1.0],
3705            filter: [0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0], // identity filter
3706            loading_strategy: 0,                              // Eager
3707            placeholder_type: 1,                              // Color (default)
3708            placeholder_color: [0.15, 0.15, 0.15, 0.5],       // Default gray
3709            placeholder_image: None,
3710            fade_duration_ms: 200,
3711        }
3712    }
3713}
3714
3715/// Trait for types that can build into layout elements
3716///
3717/// Note: No Send/Sync requirement - UI is single-threaded.
3718pub trait ElementBuilder {
3719    /// Build this element into a layout tree, returning the node ID
3720    fn build(&self, tree: &mut LayoutTree) -> LayoutNodeId;
3721
3722    /// Get the render properties for this element
3723    fn render_props(&self) -> RenderProps;
3724
3725    /// Get children builders (for recursive traversal)
3726    fn children_builders(&self) -> &[Box<dyn ElementBuilder>];
3727
3728    /// Get the element type identifier
3729    fn element_type_id(&self) -> ElementTypeId {
3730        ElementTypeId::Div
3731    }
3732
3733    /// Get text render info if this is a text element
3734    fn text_render_info(&self) -> Option<TextRenderInfo> {
3735        None
3736    }
3737
3738    /// Get styled text render info if this is a styled text element (rich_text)
3739    fn styled_text_render_info(&self) -> Option<StyledTextRenderInfo> {
3740        None
3741    }
3742
3743    /// Get SVG render info if this is an SVG element
3744    fn svg_render_info(&self) -> Option<SvgRenderInfo> {
3745        None
3746    }
3747
3748    /// Get image render info if this is an image element
3749    fn image_render_info(&self) -> Option<ImageRenderInfo> {
3750        None
3751    }
3752
3753    /// Get canvas render info if this is a canvas element
3754    fn canvas_render_info(&self) -> Option<crate::canvas::CanvasRenderFn> {
3755        None
3756    }
3757
3758    /// Get event handlers for this element
3759    ///
3760    /// Returns a reference to the element's event handlers for registration
3761    /// with the handler registry during tree building.
3762    fn event_handlers(&self) -> Option<&crate::event_handler::EventHandlers> {
3763        None
3764    }
3765
3766    /// Get scroll render info if this is a scroll element
3767    fn scroll_info(&self) -> Option<crate::scroll::ScrollRenderInfo> {
3768        None
3769    }
3770
3771    /// Get scroll physics handle if this is a scroll element
3772    fn scroll_physics(&self) -> Option<crate::scroll::SharedScrollPhysics> {
3773        None
3774    }
3775
3776    /// Get motion animation config for a child at given index
3777    ///
3778    /// This is only implemented by Motion containers. The index corresponds
3779    /// to the order of children as returned by children_builders().
3780    fn motion_animation_for_child(
3781        &self,
3782        _child_index: usize,
3783    ) -> Option<crate::element::MotionAnimation> {
3784        None
3785    }
3786
3787    /// Get motion bindings for continuous animation
3788    ///
3789    /// If this element has bound animated values (e.g., translate_y, opacity),
3790    /// they are returned here so the RenderTree can sample them each frame.
3791    fn motion_bindings(&self) -> Option<crate::motion::MotionBindings> {
3792        None
3793    }
3794
3795    /// Get stable ID for motion animation tracking
3796    ///
3797    /// When set, the motion animation state is tracked by this stable key
3798    /// instead of node ID, allowing animations to persist across tree rebuilds.
3799    /// This is essential for overlays which are rebuilt every frame.
3800    fn motion_stable_id(&self) -> Option<&str> {
3801        None
3802    }
3803
3804    /// Check if this motion should replay its animation
3805    ///
3806    /// When true, the animation restarts from the beginning even if
3807    /// the motion already exists with the same stable key.
3808    fn motion_should_replay(&self) -> bool {
3809        false
3810    }
3811
3812    /// Check if this motion should start in suspended state
3813    ///
3814    /// When true, the motion starts with opacity 0 and waits for
3815    /// `query_motion(key).start()` to trigger the enter animation.
3816    fn motion_is_suspended(&self) -> bool {
3817        false
3818    }
3819
3820    /// DEPRECATED: Check if this motion should start its exit animation
3821    ///
3822    /// This method is deprecated. Motion exit is now triggered explicitly via
3823    /// `MotionHandle.exit()` / `query_motion(key).exit()` instead of capturing
3824    /// the is_overlay_closing() flag at build time.
3825    ///
3826    /// The old mechanism was flawed because the flag reset after build_content(),
3827    /// breaking multi-frame exit animations.
3828    #[deprecated(
3829        since = "0.1.0",
3830        note = "Use query_motion(key).exit() to explicitly trigger motion exit"
3831    )]
3832    fn motion_is_exiting(&self) -> bool {
3833        false
3834    }
3835
3836    /// Get the layout style for this element
3837    ///
3838    /// This is used for hashing layout-affecting properties like size,
3839    /// padding, margin, flex properties, etc.
3840    fn layout_style(&self) -> Option<&taffy::Style> {
3841        None
3842    }
3843
3844    /// Get layout bounds storage for this element
3845    ///
3846    /// If this element wants to be notified of its computed layout bounds
3847    /// after layout is calculated, it returns a storage that will be updated.
3848    fn layout_bounds_storage(&self) -> Option<crate::renderer::LayoutBoundsStorage> {
3849        None
3850    }
3851
3852    /// Get layout bounds change callback for this element
3853    ///
3854    /// If this element needs to react when its layout bounds change (e.g., TextInput
3855    /// needs to recalculate scroll when width changes), it returns a callback that
3856    /// will be invoked when the bounds differ from the previous layout.
3857    fn layout_bounds_callback(&self) -> Option<crate::renderer::LayoutBoundsCallback> {
3858        None
3859    }
3860
3861    /// Semantic HTML-like tag name for CSS type selectors (e.g., "button", "a", "ul")
3862    ///
3863    /// Widgets override this to declare their semantic type, enabling global CSS
3864    /// rules like `button { background: blue; }` to match all instances.
3865    fn semantic_type_name(&self) -> Option<&'static str> {
3866        None
3867    }
3868
3869    /// Get the element ID for selector API queries
3870    ///
3871    /// Elements with IDs can be looked up programmatically via `ctx.query("id")`.
3872    fn element_id(&self) -> Option<&str> {
3873        None
3874    }
3875
3876    /// Set an auto-generated element ID (used by stateful containers for stable child IDs).
3877    ///
3878    /// Only sets the ID if one hasn't been explicitly assigned by the user.
3879    /// Returns true if the ID was set, false if an explicit ID already existed.
3880    fn set_auto_id(&mut self, _id: String) -> bool {
3881        false
3882    }
3883
3884    /// Get mutable access to children for auto-ID assignment
3885    fn children_builders_mut(&mut self) -> &mut [Box<dyn ElementBuilder>] {
3886        &mut []
3887    }
3888
3889    /// Get the element's CSS class list for selector matching
3890    fn element_classes(&self) -> &[String] {
3891        &[]
3892    }
3893
3894    /// Get the bound ScrollRef for programmatic scroll control
3895    ///
3896    /// Only scroll containers return a ScrollRef. This is used by the renderer
3897    /// to bind the ScrollRef to the node and process pending scroll commands.
3898    fn bound_scroll_ref(&self) -> Option<&crate::selector::ScrollRef> {
3899        None
3900    }
3901
3902    /// Get the on_ready callback for this motion container
3903    ///
3904    /// Motion containers can register a callback that fires once after the
3905    /// motion is laid out. Used with suspended animations to start the
3906    /// animation after content is mounted.
3907    fn motion_on_ready_callback(
3908        &self,
3909    ) -> Option<std::sync::Arc<dyn Fn(crate::element::ElementBounds) + Send + Sync>> {
3910        None
3911    }
3912
3913    /// Get layout animation configuration for this element
3914    ///
3915    /// If this element should animate its layout bounds changes (position, size),
3916    /// it returns a configuration specifying which properties to animate
3917    /// and the spring physics settings.
3918    ///
3919    /// Layout animation uses FLIP-style animation: layout settles instantly
3920    /// while the visual rendering interpolates from old bounds to new bounds.
3921    fn layout_animation_config(&self) -> Option<crate::layout_animation::LayoutAnimationConfig> {
3922        None
3923    }
3924
3925    /// Get visual animation config for new FLIP-style animations (read-only layout)
3926    ///
3927    /// Unlike layout_animation_config, this system never modifies taffy.
3928    /// The animation tracks visual offsets that get animated back to zero.
3929    fn visual_animation_config(&self) -> Option<crate::visual_animation::VisualAnimationConfig> {
3930        None
3931    }
3932}
3933
3934impl ElementBuilder for Div {
3935    fn build(&self, tree: &mut LayoutTree) -> LayoutNodeId {
3936        let node = tree.create_node(self.style.clone());
3937
3938        // Build and add children
3939        for child in &self.children {
3940            let child_node = child.build(tree);
3941            tree.add_child(node, child_node);
3942        }
3943
3944        node
3945    }
3946
3947    #[allow(deprecated)]
3948    fn render_props(&self) -> RenderProps {
3949        // Check if overflow is set to clip content (Clip or Scroll)
3950        // Overflow::Visible is the only mode that doesn't clip
3951        let clips_content = !matches!(self.style.overflow.x, Overflow::Visible)
3952            || !matches!(self.style.overflow.y, Overflow::Visible);
3953
3954        RenderProps {
3955            background: self.background.clone(),
3956            border_radius: self.border_radius,
3957            border_radius_explicit: self.border_radius_explicit,
3958            corner_shape: self.corner_shape,
3959            border_color: self.border_color,
3960            border_width: self.border_width,
3961            border_sides: self.border_sides,
3962            layer: self.render_layer,
3963            material: self.material.clone(),
3964            shadow: self.shadow,
3965            transform: self.transform.clone(),
3966            opacity: self.opacity,
3967            clips_content,
3968            is_stack_layer: self.is_stack_layer,
3969            pointer_events_none: self.pointer_events_none,
3970            cursor: self.cursor,
3971            layer_effects: self.layer_effects.clone(),
3972            rotate_x: self.rotate_x,
3973            rotate_y: self.rotate_y,
3974            perspective: self.perspective_3d,
3975            shape_3d: self.shape_3d,
3976            depth: self.depth,
3977            light_direction: self.light_direction,
3978            light_intensity: self.light_intensity,
3979            ambient: self.ambient,
3980            specular: self.specular,
3981            translate_z: self.translate_z,
3982            op_3d: self.op_3d,
3983            blend_3d: self.blend_3d,
3984            clip_path: self.clip_path.clone(),
3985            overflow_fade: self.overflow_fade,
3986            flow: self.flow_name.clone(),
3987            flow_graph: self.flow_graph.clone(),
3988            outline_color: self.outline_color,
3989            outline_width: self.outline_width,
3990            outline_offset: self.outline_offset,
3991            is_fixed: self.is_fixed,
3992            is_sticky: self.is_sticky,
3993            sticky_top: self.sticky_top,
3994            z_index: self.z_index,
3995            ..Default::default()
3996        }
3997    }
3998
3999    fn children_builders(&self) -> &[Box<dyn ElementBuilder>] {
4000        &self.children
4001    }
4002
4003    fn event_handlers(&self) -> Option<&crate::event_handler::EventHandlers> {
4004        if self.event_handlers.is_empty() {
4005            None
4006        } else {
4007            Some(&self.event_handlers)
4008        }
4009    }
4010
4011    fn layout_style(&self) -> Option<&taffy::Style> {
4012        Some(&self.style)
4013    }
4014
4015    fn element_id(&self) -> Option<&str> {
4016        self.element_id.as_deref()
4017    }
4018
4019    fn set_auto_id(&mut self, id: String) -> bool {
4020        if self.element_id.is_none() {
4021            self.element_id = Some(id);
4022            true
4023        } else {
4024            false
4025        }
4026    }
4027
4028    fn children_builders_mut(&mut self) -> &mut [Box<dyn ElementBuilder>] {
4029        &mut self.children
4030    }
4031
4032    fn element_classes(&self) -> &[String] {
4033        &self.classes
4034    }
4035
4036    fn layout_animation_config(&self) -> Option<crate::layout_animation::LayoutAnimationConfig> {
4037        self.layout_animation.clone()
4038    }
4039
4040    fn visual_animation_config(&self) -> Option<crate::visual_animation::VisualAnimationConfig> {
4041        self.visual_animation.clone()
4042    }
4043
4044    fn scroll_physics(&self) -> Option<crate::scroll::SharedScrollPhysics> {
4045        self.scroll_physics.clone()
4046    }
4047}
4048
4049/// Convenience function to create a new div
4050pub fn div() -> Div {
4051    Div::new()
4052}
4053
4054// Stack has been moved to stack.rs
4055
4056#[cfg(test)]
4057mod tests {
4058    use super::*;
4059    use crate::renderer::RenderTree;
4060
4061    #[test]
4062    fn test_div_builder() {
4063        let d = div().w(100.0).h(50.0).flex_row().gap(2.0).p(4.0);
4064
4065        assert!(matches!(d.style.display, Display::Flex));
4066        assert!(matches!(d.style.flex_direction, FlexDirection::Row));
4067    }
4068
4069    #[test]
4070    fn test_div_with_children() {
4071        let parent = div().flex_col().child(div().h(20.0)).child(div().h(30.0));
4072
4073        assert_eq!(parent.children.len(), 2);
4074    }
4075
4076    #[test]
4077    fn test_build_tree() {
4078        let ui = div().flex_col().child(div().h(20.0)).child(div().h(30.0));
4079
4080        let mut tree = LayoutTree::new();
4081        let root = ui.build(&mut tree);
4082
4083        assert_eq!(tree.len(), 3);
4084        assert_eq!(tree.children(root).len(), 2);
4085    }
4086
4087    #[test]
4088    fn test_layout_flex_row_with_fixed_children() {
4089        // Three fixed-width children in a row
4090        let ui = div()
4091            .w(300.0)
4092            .h(100.0)
4093            .flex_row()
4094            .child(div().w(50.0).h(100.0))
4095            .child(div().w(100.0).h(100.0))
4096            .child(div().w(50.0).h(100.0));
4097
4098        let mut tree = RenderTree::from_element(&ui);
4099        tree.compute_layout(300.0, 100.0);
4100
4101        let root = tree.root().unwrap();
4102        let children: Vec<_> = tree.layout_tree.children(root);
4103
4104        // First child at x=0
4105        let first = tree
4106            .layout_tree
4107            .get_bounds(children[0], (0.0, 0.0))
4108            .unwrap();
4109        assert_eq!(first.x, 0.0);
4110        assert_eq!(first.width, 50.0);
4111
4112        // Second child at x=50
4113        let second = tree
4114            .layout_tree
4115            .get_bounds(children[1], (0.0, 0.0))
4116            .unwrap();
4117        assert_eq!(second.x, 50.0);
4118        assert_eq!(second.width, 100.0);
4119
4120        // Third child at x=150
4121        let third = tree
4122            .layout_tree
4123            .get_bounds(children[2], (0.0, 0.0))
4124            .unwrap();
4125        assert_eq!(third.x, 150.0);
4126        assert_eq!(third.width, 50.0);
4127    }
4128
4129    #[test]
4130    fn test_layout_flex_col_with_gap() {
4131        // Column with gap between children (10px gap using gap_px)
4132        let ui = div()
4133            .w(100.0)
4134            .h(200.0)
4135            .flex_col()
4136            .gap_px(10.0) // 10px gap
4137            .child(div().w_full().h(40.0))
4138            .child(div().w_full().h(40.0))
4139            .child(div().w_full().h(40.0));
4140
4141        let mut tree = RenderTree::from_element(&ui);
4142        tree.compute_layout(100.0, 200.0);
4143
4144        let root = tree.root().unwrap();
4145        let children: Vec<_> = tree.layout_tree.children(root);
4146
4147        // First child at y=0
4148        let first = tree
4149            .layout_tree
4150            .get_bounds(children[0], (0.0, 0.0))
4151            .unwrap();
4152        assert_eq!(first.y, 0.0);
4153        assert_eq!(first.height, 40.0);
4154
4155        // Second child at y=50 (40 + 10 gap)
4156        let second = tree
4157            .layout_tree
4158            .get_bounds(children[1], (0.0, 0.0))
4159            .unwrap();
4160        assert_eq!(second.y, 50.0);
4161        assert_eq!(second.height, 40.0);
4162
4163        // Third child at y=100 (50 + 40 + 10 gap)
4164        let third = tree
4165            .layout_tree
4166            .get_bounds(children[2], (0.0, 0.0))
4167            .unwrap();
4168        assert_eq!(third.y, 100.0);
4169        assert_eq!(third.height, 40.0);
4170    }
4171
4172    #[test]
4173    fn test_layout_flex_grow() {
4174        // One fixed child, one growing child
4175        let ui = div()
4176            .w(200.0)
4177            .h(100.0)
4178            .flex_row()
4179            .child(div().w(50.0).h(100.0))
4180            .child(div().flex_grow().h(100.0));
4181
4182        let mut tree = RenderTree::from_element(&ui);
4183        tree.compute_layout(200.0, 100.0);
4184
4185        let root = tree.root().unwrap();
4186        let children: Vec<_> = tree.layout_tree.children(root);
4187
4188        // Fixed child
4189        let fixed = tree
4190            .layout_tree
4191            .get_bounds(children[0], (0.0, 0.0))
4192            .unwrap();
4193        assert_eq!(fixed.width, 50.0);
4194
4195        // Growing child should fill remaining space
4196        let growing = tree
4197            .layout_tree
4198            .get_bounds(children[1], (0.0, 0.0))
4199            .unwrap();
4200        assert_eq!(growing.x, 50.0);
4201        assert_eq!(growing.width, 150.0);
4202    }
4203
4204    #[test]
4205    fn test_layout_padding() {
4206        // Container with padding
4207        let ui = div()
4208            .w(100.0)
4209            .h(100.0)
4210            .p(2.0) // 8px padding (2 * 4px base unit)
4211            .child(div().w_full().h_full());
4212
4213        let mut tree = RenderTree::from_element(&ui);
4214        tree.compute_layout(100.0, 100.0);
4215
4216        let root = tree.root().unwrap();
4217        let children: Vec<_> = tree.layout_tree.children(root);
4218
4219        // Child should be inset by padding
4220        let child = tree
4221            .layout_tree
4222            .get_bounds(children[0], (0.0, 0.0))
4223            .unwrap();
4224        assert_eq!(child.x, 8.0);
4225        assert_eq!(child.y, 8.0);
4226        assert_eq!(child.width, 84.0); // 100 - 8 - 8
4227        assert_eq!(child.height, 84.0);
4228    }
4229
4230    #[test]
4231    fn test_layout_justify_between() {
4232        // Three children with space between
4233        let ui = div()
4234            .w(200.0)
4235            .h(50.0)
4236            .flex_row()
4237            .justify_between()
4238            .child(div().w(30.0).h(50.0))
4239            .child(div().w(30.0).h(50.0))
4240            .child(div().w(30.0).h(50.0));
4241
4242        let mut tree = RenderTree::from_element(&ui);
4243        tree.compute_layout(200.0, 50.0);
4244
4245        let root = tree.root().unwrap();
4246        let children: Vec<_> = tree.layout_tree.children(root);
4247
4248        // First at start
4249        let first = tree
4250            .layout_tree
4251            .get_bounds(children[0], (0.0, 0.0))
4252            .unwrap();
4253        assert_eq!(first.x, 0.0);
4254
4255        // Last at end
4256        let third = tree
4257            .layout_tree
4258            .get_bounds(children[2], (0.0, 0.0))
4259            .unwrap();
4260        assert_eq!(third.x, 170.0); // 200 - 30
4261
4262        // Middle should be centered between first and third
4263        let second = tree
4264            .layout_tree
4265            .get_bounds(children[1], (0.0, 0.0))
4266            .unwrap();
4267        assert_eq!(second.x, 85.0); // (170 - 0) / 2 - 30/2 + 30/2 = 85
4268    }
4269
4270    #[test]
4271    fn test_nested_layout() {
4272        // Nested flex containers
4273        let ui = div()
4274            .w(200.0)
4275            .h(200.0)
4276            .flex_col()
4277            .child(
4278                div()
4279                    .w_full()
4280                    .h(50.0)
4281                    .flex_row()
4282                    .child(div().w(50.0).h(50.0))
4283                    .child(div().flex_grow().h(50.0)),
4284            )
4285            .child(div().w_full().flex_grow());
4286
4287        let mut tree = RenderTree::from_element(&ui);
4288        tree.compute_layout(200.0, 200.0);
4289
4290        let root = tree.root().unwrap();
4291        let root_bounds = tree.get_bounds(root).unwrap();
4292        assert_eq!(root_bounds.width, 200.0);
4293        assert_eq!(root_bounds.height, 200.0);
4294
4295        let root_children: Vec<_> = tree.layout_tree.children(root);
4296
4297        // First row
4298        let row = tree
4299            .layout_tree
4300            .get_bounds(root_children[0], (0.0, 0.0))
4301            .unwrap();
4302        assert_eq!(row.height, 50.0);
4303        assert_eq!(row.width, 200.0);
4304
4305        // Second element should fill remaining height
4306        let bottom = tree
4307            .layout_tree
4308            .get_bounds(root_children[1], (0.0, 0.0))
4309            .unwrap();
4310        assert_eq!(bottom.y, 50.0);
4311        assert_eq!(bottom.height, 150.0);
4312    }
4313
4314    #[test]
4315    fn test_element_ref_basic() {
4316        // Create a ref
4317        let div_ref: ElementRef<Div> = ElementRef::new();
4318
4319        assert!(!div_ref.is_bound());
4320
4321        // Set a div
4322        div_ref.set(div().w(100.0).h(50.0).bg(Color::BLUE));
4323
4324        assert!(div_ref.is_bound());
4325
4326        // Read from the ref
4327        let width = div_ref.with(|d| d.style.size.width.clone());
4328        assert!(matches!(width, Some(Dimension::Length(100.0))));
4329    }
4330
4331    #[test]
4332    fn test_element_ref_with_mut() {
4333        let div_ref: ElementRef<Div> = ElementRef::new();
4334
4335        div_ref.set(div().w(100.0));
4336
4337        // Modify through the ref
4338        div_ref.with_mut(|d| {
4339            *d = d.swap().h(200.0).bg(Color::RED);
4340        });
4341
4342        // Verify modification
4343        let height = div_ref.with(|d| d.style.size.height.clone());
4344        assert!(matches!(height, Some(Dimension::Length(200.0))));
4345    }
4346
4347    #[test]
4348    fn test_element_ref_clone() {
4349        let div_ref: ElementRef<Div> = ElementRef::new();
4350        let div_ref_clone = div_ref.clone();
4351
4352        // Set on original
4353        div_ref.set(div().w(100.0));
4354
4355        // Should be visible on clone (shared storage)
4356        assert!(div_ref_clone.is_bound());
4357        let width = div_ref_clone.with(|d| d.style.size.width.clone());
4358        assert!(matches!(width, Some(Dimension::Length(100.0))));
4359    }
4360}