Skip to main content

sequoia_view/
runtime.rs

1use std::any::Any;
2use std::cell::{Cell, RefCell};
3use std::collections::{HashMap, HashSet};
4use std::panic::AssertUnwindSafe;
5use std::rc::{Rc, Weak};
6
7use crate::dnd::{
8    ActiveDrag, DragEnd, DragId, DragOptions, DragPayload, DragSession, DropPosition, PendingDrag,
9    exceeded_threshold, grab_offset, preview_origin,
10};
11use crate::overlay::{
12    Anchor, InputMode, OverlayEntry, OverlayId, OverlayLayer, OverlayStore, compute_position,
13};
14use sequoia_core::{
15    Constraints, Dirty, Edges, Length, Point, Rect, RenderList, Size, Transform, Vec2, WindowId,
16};
17use sequoia_input::{
18    CursorIcon, Event, EventPhase, ImeEvent, Key, KeyEvent, Modifiers, PenTool, PointerButton,
19    PointerEvent,
20};
21use sequoia_layout::{
22    Align, Axis, ContainerStyle, Dock, DockStyle, FlexItem, GridPlacement, GridStyle, LayoutCtx,
23    LayoutStyle, MasonryStyle, Overflow, Track, WrapStyle, clamp_to, distribute_flex,
24    dock_layout_order, dock_thickness, masonry_assign, masonry_column_width, resolve_cross,
25    resolve_dock, resolve_flex_basis, resolve_main, resolve_tracks, sanitize_gap, track_offsets,
26    wrap_lines,
27};
28use sequoia_reactive::{Environment, ReadSet, Signal, Watcher, capture_reads, flush_effects};
29use sequoia_style::{Theme, VisualState};
30use sequoia_text::TextSystem;
31
32use crate::any::{AnyView, ErasedView};
33use crate::clock::FrameClock;
34use crate::live::{AnimSpec, AnimState, Live, Prop, SpringSpec};
35use crate::surface::SurfaceDraw;
36use crate::view::{EventCtx, PaintCtx, View};
37
38#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
39pub struct ElementId {
40    pub(crate) index: u32,
41    pub(crate) generation: u32,
42}
43
44pub trait ViewState: 'static + Default {}
45impl<T: Default + 'static> ViewState for T {}
46
47const MAX_ERRORS: usize = 64;
48
49#[derive(Clone, Copy, Debug, Default, PartialEq)]
50pub struct Stats {
51    pub mounts: u64,
52
53    pub unmounts: u64,
54
55    pub rebuilds: u64,
56
57    pub measure_invalidations: u64,
58
59    pub errors: u64,
60}
61
62#[derive(Clone, Debug, PartialEq)]
63pub struct RuntimeError {
64    pub element: ElementId,
65
66    pub type_name: String,
67
68    pub message: String,
69}
70
71#[derive(Clone, Copy, Debug, Default, PartialEq)]
72pub struct InteractState {
73    pub hovered: Option<ElementId>,
74
75    pub pressed: Option<ElementId>,
76
77    pub captured: Option<ElementId>,
78
79    pub focus: Option<ElementId>,
80
81    pub cursor: Point,
82
83    pub pointer_seen: bool,
84}
85
86struct Element {
87    parent: Option<ElementId>,
88    key: u64,
89    children: Vec<ElementId>,
90    view: Option<Rc<dyn ErasedView>>,
91    watcher: Option<Watcher>,
92    reads: ReadSet,
93    dirty: Dirty,
94    rect: Cell<Rect>,
95}
96
97#[derive(Default)]
98struct Arena {
99    slots: Vec<Option<Element>>,
100    generations: Vec<u32>,
101    free: Vec<u32>,
102}
103
104impl Arena {
105    fn get(&self, id: ElementId) -> Option<&Element> {
106        let i = id.index as usize;
107        if self.generations.get(i)? != &id.generation {
108            return None;
109        }
110        self.slots.get(i)?.as_ref()
111    }
112    fn get_mut(&mut self, id: ElementId) -> Option<&mut Element> {
113        let i = id.index as usize;
114        if self.generations.get(i)? != &id.generation {
115            return None;
116        }
117        self.slots.get_mut(i)?.as_mut()
118    }
119    fn alloc_slot(&mut self) -> (u32, u32) {
120        match self.free.pop() {
121            Some(i) => (i, self.generations[i as usize]),
122            None => {
123                let i = self.slots.len() as u32;
124                self.slots.push(None);
125                self.generations.push(0);
126                (i, 0)
127            }
128        }
129    }
130    fn free_slot(&mut self, slot: u32) {
131        self.slots[slot as usize] = None;
132        self.generations[slot as usize] = self.generations[slot as usize].wrapping_add(1);
133        self.free.push(slot);
134    }
135    fn alive(&self) -> usize {
136        self.slots.iter().filter(|s| s.is_some()).count()
137    }
138}
139
140#[derive(Clone)]
141struct MeasureEntry {
142    constraints: Constraints,
143    style: LayoutStyle,
144    container: Option<ContainerStyle>,
145
146    grid: Option<GridStyle>,
147    placements: Vec<GridPlacement>,
148
149    dock: Option<DockStyle>,
150
151    wrap: Option<WrapStyle>,
152    masonry: Option<MasonryStyle>,
153    children_len: usize,
154    size: Size,
155}
156
157struct DispatchCells {
158    stopped: Cell<bool>,
159    handled: Cell<bool>,
160    capture_req: Cell<Option<ElementId>>,
161    release_req: Cell<bool>,
162    focus_req: Cell<bool>,
163}
164
165impl DispatchCells {
166    fn new() -> Self {
167        Self {
168            stopped: Cell::new(false),
169            handled: Cell::new(false),
170            capture_req: Cell::new(None),
171            release_req: Cell::new(false),
172            focus_req: Cell::new(false),
173        }
174    }
175}
176
177struct Inner {
178    arena: RefCell<Arena>,
179    queue: RefCell<Vec<ElementId>>,
180    root: Cell<Option<ElementId>>,
181    stats: Cell<Stats>,
182    measures: RefCell<HashMap<ElementId, MeasureEntry>>,
183    interact: Cell<InteractState>,
184    environment: RefCell<Environment>,
185
186    env_listener: RefCell<Option<sequoia_reactive::Listener>>,
187
188    redraw: RefCell<Option<Box<dyn Fn()>>>,
189    surfaces: RefCell<Vec<SurfaceDraw>>,
190    overlays: RefCell<OverlayStore>,
191
192    pending_drag: RefCell<Option<PendingDrag>>,
193
194    active_drag: RefCell<Option<ActiveDrag>>,
195
196    next_drag_id: Cell<u64>,
197
198    viewport: Cell<Rect>,
199
200    root_fill: Cell<bool>,
201
202    window_size: Signal<Size>,
203
204    scale: Cell<f32>,
205
206    safe_insets: Cell<Edges>,
207
208    errors: RefCell<Vec<RuntimeError>>,
209
210    errors_seq: Signal<u64>,
211
212    stylus_pressure: Cell<f32>,
213
214    stylus_tool: Cell<PenTool>,
215
216    touches: RefCell<HashMap<u64, Point>>,
217
218    shapes: sequoia_draw::ShapeCache,
219
220    window: Cell<WindowId>,
221
222    wrap_cache: RefCell<HashMap<(u32, u32, u8), bool>>,
223
224    live_anims: RefCell<HashMap<(ElementId, u64), AnimState>>,
225
226    clock: Cell<FrameClock>,
227
228    states: RefCell<HashMap<ElementId, Box<dyn Any>>>,
229
230    layout_epoch: Cell<u64>,
231
232    scoped_cancels: RefCell<HashMap<ElementId, Vec<Box<dyn FnOnce()>>>>,
233}
234
235impl Inner {
236    fn schedule(&self, id: ElementId) {
237        self.mark(id, Dirty::LAYOUT | Dirty::CHILDREN);
238    }
239
240    fn mark(&self, id: ElementId, flags: Dirty) {
241        if flags.is_empty() {
242            return;
243        }
244        let newly = {
245            let mut arena = self.arena.borrow_mut();
246            match arena.get_mut(id) {
247                Some(e) => {
248                    let was = e.dirty;
249                    e.dirty |= flags;
250                    was.is_empty()
251                }
252                None => return,
253            }
254        };
255        if newly {
256            self.queue.borrow_mut().push(id);
257        }
258
259        if let Some(h) = self.redraw.borrow().as_ref() {
260            h();
261        }
262    }
263}
264
265#[derive(Clone)]
266pub struct Runtime {
267    inner: Rc<Inner>,
268}
269
270impl Default for Runtime {
271    fn default() -> Self {
272        Self::new()
273    }
274}
275
276impl Runtime {
277    pub fn new() -> Self {
278        Self {
279            inner: Rc::new(Inner {
280                arena: RefCell::default(),
281                queue: RefCell::default(),
282                root: Cell::new(None),
283                stats: Cell::new(Stats::default()),
284                measures: RefCell::new(HashMap::new()),
285                interact: Cell::new(InteractState::default()),
286                environment: RefCell::new(Environment::new()),
287                env_listener: RefCell::new(None),
288                shapes: sequoia_draw::ShapeCache::default(),
289                redraw: RefCell::new(None),
290                surfaces: RefCell::new(Vec::new()),
291                overlays: RefCell::default(),
292                pending_drag: RefCell::new(None),
293                active_drag: RefCell::new(None),
294                next_drag_id: Cell::new(0),
295                viewport: Cell::new(Rect::ZERO),
296                root_fill: Cell::new(false),
297                window_size: Signal::new(Size::ZERO),
298                scale: Cell::new(1.0),
299                safe_insets: Cell::new(Edges::ZERO),
300                errors: RefCell::new(Vec::new()),
301                errors_seq: Signal::new(0),
302                stylus_pressure: Cell::new(1.0),
303                stylus_tool: Cell::new(PenTool::Mouse),
304                touches: RefCell::new(HashMap::new()),
305                window: Cell::new(WindowId::PRIMARY),
306                wrap_cache: RefCell::new(HashMap::new()),
307                live_anims: RefCell::new(HashMap::new()),
308                clock: Cell::new(FrameClock::new()),
309                states: RefCell::new(HashMap::new()),
310                layout_epoch: Cell::new(0),
311                scoped_cancels: RefCell::new(HashMap::new()),
312            }),
313        }
314    }
315
316    pub fn window_id(&self) -> WindowId {
317        self.inner.window.get()
318    }
319
320    pub fn set_window_id(&self, id: WindowId) {
321        self.inner.window.set(id);
322    }
323
324    pub fn window_size(&self) -> Signal<Size> {
325        self.inner.window_size.clone()
326    }
327
328    pub fn window_size_value(&self) -> Size {
329        self.inner.window_size.get_untracked()
330    }
331
332    pub fn window_rect(&self) -> Rect {
333        self.inner.viewport.get()
334    }
335
336    pub fn viewport_rect(&self) -> Rect {
337        self.inner.viewport.get()
338    }
339
340    pub fn set_root_fill(&self, enabled: bool) {
341        self.inner.root_fill.set(enabled);
342    }
343
344    pub fn scale(&self) -> f32 {
345        self.inner.scale.get()
346    }
347
348    pub fn set_scale(&self, scale: f32) {
349        let s = if scale.is_finite() && scale > 0.0 {
350            scale
351        } else {
352            1.0
353        };
354        self.inner.scale.set(s);
355    }
356
357    pub fn safe_insets(&self) -> Edges {
358        self.inner.safe_insets.get()
359    }
360
361    pub fn set_safe_insets(&self, insets: Edges) {
362        self.inner.safe_insets.set(insets);
363    }
364
365    pub fn set_environment(&self, env: Environment) {
366        *self.inner.environment.borrow_mut() = env.clone();
367        let weak = Rc::downgrade(&self.inner);
368        *self.inner.env_listener.borrow_mut() = Some(env.on_change(move || {
369            if let Some(inner) = weak.upgrade()
370                && let Some(h) = inner.redraw.borrow().as_ref()
371            {
372                h();
373            }
374        }));
375    }
376
377    pub fn set_redraw_hook(&self, hook: impl Fn() + 'static) {
378        *self.inner.redraw.borrow_mut() = Some(Box::new(hook));
379    }
380
381    fn request_redraw(&self) {
382        if let Some(h) = self.inner.redraw.borrow().as_ref() {
383            h();
384        }
385    }
386
387    pub fn request_frame(&self) {
388        self.request_redraw();
389    }
390
391    /// Snapshot of the frame clock used by every view animation.
392    pub fn clock(&self) -> FrameClock {
393        self.inner.clock.get()
394    }
395
396    pub fn now(&self) -> std::time::Instant {
397        self.inner.clock.get().now()
398    }
399
400    pub fn elapsed_secs(&self) -> f32 {
401        self.inner.clock.get().elapsed_secs()
402    }
403
404    pub fn frame_dt_secs(&self) -> f32 {
405        self.inner.clock.get().dt_secs()
406    }
407
408    pub fn reduced_motion(&self) -> bool {
409        self.inner.clock.get().reduced_motion()
410    }
411
412    pub fn set_reduced_motion(&self, reduced: bool) {
413        let mut c = self.inner.clock.get();
414        if c.reduced_motion() == reduced {
415            return;
416        }
417        c.set_reduced_motion(reduced);
418        self.inner.clock.set(c);
419        self.request_redraw();
420    }
421
422    pub fn set_animation_speed(&self, speed: f32) {
423        let mut c = self.inner.clock.get();
424        c.set_speed(speed);
425        self.inner.clock.set(c);
426        self.request_redraw();
427    }
428
429    pub fn animation_speed(&self) -> f32 {
430        self.inner.clock.get().speed()
431    }
432
433    /// Switch to explicit time control. Hosts and tests can then call
434    /// [`Runtime::advance_for_test`] without coupling to wall time.
435    pub fn set_manual_clock(&self, manual: bool) {
436        let mut c = self.inner.clock.get();
437        c.set_manual(manual);
438        self.inner.clock.set(c);
439    }
440
441    /// Advance the shared clock by an exact duration.
442    ///
443    /// This also flips the clock into manual mode, so subsequent `tick` calls
444    /// do not advance it from the wall clock.
445    pub fn advance_for_test(&self, dt: std::time::Duration) {
446        let mut c = self.inner.clock.get();
447        c.set_manual(true);
448        c.advance_by(dt);
449        self.inner.clock.set(c);
450    }
451
452    pub fn reset_clock(&self) {
453        let mut c = self.inner.clock.get();
454        c.reset();
455        self.inner.clock.set(c);
456    }
457
458    fn advance_clock(&self) {
459        let mut c = self.inner.clock.get();
460        c.advance();
461        self.inner.clock.set(c);
462    }
463
464    pub fn live_f32(
465        &self,
466        el: ElementId,
467        prop: Prop,
468        target: f32,
469        tween: Option<&AnimSpec>,
470        spring: Option<&SpringSpec>,
471    ) -> (f32, bool) {
472        let key = (el, prop.0);
473        if self.reduced_motion() || (tween.is_none() && spring.is_none()) {
474            self.inner.live_anims.borrow_mut().remove(&key);
475            return (target, false);
476        }
477        let now = self.now();
478        let mut map = self.inner.live_anims.borrow_mut();
479        let entry = map.entry(key).or_insert_with(|| {
480            AnimState::F32(crate::live::F32State {
481                current: target,
482                from: target,
483                start: now,
484                last_target: target,
485                tween: tween.copied(),
486                spring: spring.copied(),
487                velocity: 0.0,
488                last_tick: now,
489                initialized: false,
490            })
491        });
492
493        if !matches!(entry, AnimState::F32(_)) {
494            *entry = AnimState::F32(crate::live::F32State {
495                current: target,
496                from: target,
497                start: now,
498                last_target: target,
499                tween: tween.copied(),
500                spring: spring.copied(),
501                velocity: 0.0,
502                last_tick: now,
503                initialized: false,
504            });
505        }
506        let AnimState::F32(st) = entry else {
507            return (target, false);
508        };
509        let (v, running) = AnimState::sample_f32(st, target, tween, spring, now);
510        drop(map);
511        if running {
512            self.request_frame();
513        }
514        (v, running)
515    }
516
517    pub fn live_color(
518        &self,
519        el: ElementId,
520        prop: Prop,
521        target: sequoia_core::Color,
522        tween: Option<&AnimSpec>,
523    ) -> (sequoia_core::Color, bool) {
524        let key = (el, prop.0);
525        if self.reduced_motion() || tween.is_none() {
526            self.inner.live_anims.borrow_mut().remove(&key);
527            return (target, false);
528        }
529        let now = self.now();
530        let mut map = self.inner.live_anims.borrow_mut();
531        let entry = map.entry(key).or_insert_with(|| {
532            AnimState::Color(crate::live::ColorState {
533                current: target,
534                from: target,
535                start: now,
536                last_target: target,
537                tween: tween.copied(),
538                smooth_rate: None,
539                last_tick: now,
540                initialized: false,
541            })
542        });
543        if !matches!(entry, AnimState::Color(_)) {
544            *entry = AnimState::Color(crate::live::ColorState {
545                current: target,
546                from: target,
547                start: now,
548                last_target: target,
549                tween: tween.copied(),
550                smooth_rate: None,
551                last_tick: now,
552                initialized: false,
553            });
554        }
555        let AnimState::Color(st) = entry else {
556            return (target, false);
557        };
558        let (v, running) = AnimState::sample_color(st, target, tween, None, now);
559        drop(map);
560        if running {
561            self.request_frame();
562        }
563        (v, running)
564    }
565
566    pub fn live_color_exp(
567        &self,
568        el: ElementId,
569        prop: Prop,
570        target: sequoia_core::Color,
571        rate: f32,
572    ) -> (sequoia_core::Color, bool) {
573        let key = (el, prop.0);
574        if self.reduced_motion() || !(rate > 0.0) {
575            self.inner.live_anims.borrow_mut().remove(&key);
576            return (target, false);
577        }
578        let now = self.now();
579        let mut map = self.inner.live_anims.borrow_mut();
580        let entry = map.entry(key).or_insert_with(|| {
581            AnimState::Color(crate::live::ColorState {
582                current: target,
583                from: target,
584                start: now,
585                last_target: target,
586                tween: None,
587                smooth_rate: Some(rate),
588                last_tick: now,
589                initialized: false,
590            })
591        });
592        if !matches!(entry, AnimState::Color(_)) {
593            *entry = AnimState::Color(crate::live::ColorState {
594                current: target,
595                from: target,
596                start: now,
597                last_target: target,
598                tween: None,
599                smooth_rate: Some(rate),
600                last_tick: now,
601                initialized: false,
602            });
603        }
604        let AnimState::Color(st) = entry else {
605            return (target, false);
606        };
607        let (v, running) = AnimState::sample_color(st, target, None, Some(rate), now);
608        drop(map);
609        if running {
610            self.request_frame();
611        }
612        (v, running)
613    }
614
615    pub fn live_vec2(
616        &self,
617        el: ElementId,
618        prop: Prop,
619        target: sequoia_core::Vec2,
620        tween: Option<&AnimSpec>,
621        spring: Option<&SpringSpec>,
622    ) -> (sequoia_core::Vec2, bool) {
623        let key = (el, prop.0);
624        if self.reduced_motion() || (tween.is_none() && spring.is_none()) {
625            self.inner.live_anims.borrow_mut().remove(&key);
626            return (target, false);
627        }
628        let now = self.now();
629        let mut map = self.inner.live_anims.borrow_mut();
630        let entry = map.entry(key).or_insert_with(|| {
631            AnimState::Vec2(crate::live::Vec2State {
632                current: target,
633                from: target,
634                start: now,
635                last_target: target,
636                tween: tween.copied(),
637                spring: spring.copied(),
638                velocity: sequoia_core::Vec2::ZERO,
639                last_tick: now,
640                initialized: false,
641            })
642        });
643        if !matches!(entry, AnimState::Vec2(_)) {
644            *entry = AnimState::Vec2(crate::live::Vec2State {
645                current: target,
646                from: target,
647                start: now,
648                last_target: target,
649                tween: tween.copied(),
650                spring: spring.copied(),
651                velocity: sequoia_core::Vec2::ZERO,
652                last_tick: now,
653                initialized: false,
654            });
655        }
656        let AnimState::Vec2(st) = entry else {
657            return (target, false);
658        };
659        let (v, running) = AnimState::sample_vec2(st, target, tween, spring, now);
660        drop(map);
661        if running {
662            self.request_frame();
663        }
664        (v, running)
665    }
666
667    pub fn resolve_live_f32(&self, el: ElementId, prop: Prop, live: &Live<f32>) -> f32 {
668        let target = live.target_untracked();
669        let (v, _) = self.live_f32(
670            el,
671            prop,
672            target,
673            live.tween_spec().as_ref(),
674            live.spring_spec().as_ref(),
675        );
676        v
677    }
678
679    pub fn resolve_live_vec2(
680        &self,
681        el: ElementId,
682        prop: Prop,
683        live: &Live<sequoia_core::Vec2>,
684    ) -> sequoia_core::Vec2 {
685        let target = live.target_untracked();
686        let (v, _) = self.live_vec2(
687            el,
688            prop,
689            target,
690            live.tween_spec().as_ref(),
691            live.spring_spec().as_ref(),
692        );
693        v
694    }
695
696    pub fn live_anim_count(&self) -> usize {
697        self.inner.live_anims.borrow().len()
698    }
699
700    pub fn shapes(&self) -> &sequoia_draw::ShapeCache {
701        &self.inner.shapes
702    }
703
704    pub fn mount(&self, root: impl View + 'static) {
705        self.mount_any(AnyView::new(root));
706    }
707
708    pub fn mount_any(&self, root: AnyView) {
709        if let Some(old) = self.inner.root.get() {
710            self.unmount_subtree(old);
711        }
712        let value = unwrap_any(root.into_inner());
713        let id = self.alloc(value, None, 0, 0);
714        if let Some(v) = self.view_rc(id) {
715            self.build_with(id, v);
716        }
717        if let Some(v) = self.view_rc(id) {
718            let type_name = v.type_name().to_string();
719            if std::panic::catch_unwind(AssertUnwindSafe(|| {
720                v.on_mount();
721            }))
722            .is_err()
723            {
724                self.record_error(id, type_name, "on_mount panicked".to_string());
725            }
726        }
727        self.inner.root.set(Some(id));
728    }
729
730    pub fn with_state<S: Default + 'static, R>(
731        &self,
732        el: ElementId,
733        f: impl FnOnce(&mut S) -> R,
734    ) -> R {
735        struct Reinsert<'a> {
736            map: &'a RefCell<HashMap<ElementId, Box<dyn Any>>>,
737            el: ElementId,
738            slot: Option<Box<dyn Any>>,
739        }
740        impl Drop for Reinsert<'_> {
741            fn drop(&mut self) {
742                if let Some(slot) = self.slot.take() {
743                    self.map.borrow_mut().insert(self.el, slot);
744                }
745            }
746        }
747
748        let slot = match self.inner.states.borrow_mut().remove(&el) {
749            Some(b) if b.is::<S>() => b,
750
751            Some(_) | None => Box::new(S::default()),
752        };
753        let mut guard = Reinsert {
754            map: &self.inner.states,
755            el,
756            slot: Some(slot),
757        };
758        let s = guard
759            .slot
760            .as_mut()
761            .and_then(|b| b.downcast_mut::<S>())
762            .expect("slot ma typ S po sprawdzeniu is::<S>()");
763        f(s)
764    }
765
766    pub fn read_state<S: Default + 'static, R>(&self, el: ElementId, f: impl FnOnce(&S) -> R) -> R {
767        self.with_state::<S, R>(el, |s| f(s))
768    }
769
770    pub fn state_count(&self) -> usize {
771        self.inner.states.borrow().len()
772    }
773
774    pub fn has_state(&self, el: ElementId) -> bool {
775        self.inner.states.borrow().contains_key(&el)
776    }
777
778    pub fn track_scoped(&self, el: ElementId, cancel: impl FnOnce() + 'static) {
779        if !self.is_alive(el) {
780            cancel();
781            return;
782        }
783        self.inner
784            .scoped_cancels
785            .borrow_mut()
786            .entry(el)
787            .or_default()
788            .push(Box::new(cancel));
789    }
790
791    pub fn scoped_count(&self) -> usize {
792        self.inner
793            .scoped_cancels
794            .borrow()
795            .values()
796            .map(|v| v.len())
797            .sum()
798    }
799
800    pub fn request_rebuild(&self, el: ElementId) {
801        self.inner.schedule(el);
802    }
803
804    pub fn tick(&self) {
805        self.advance_clock();
806        self.settle();
807    }
808
809    fn settle(&self) {
810        flush_effects();
811        loop {
812            let next = self.inner.queue.borrow_mut().pop();
813            let Some(id) = next else { break };
814            let flags = {
815                self.inner
816                    .arena
817                    .borrow()
818                    .get(id)
819                    .map(|e| e.dirty)
820                    .unwrap_or_else(Dirty::empty)
821            };
822            if flags.is_empty() {
823                continue;
824            }
825            if let Some(e) = self.inner.arena.borrow_mut().get_mut(id) {
826                e.dirty = Dirty::empty();
827            }
828            if !flags.needs_layout() {
829                continue;
830            }
831            let Some(v) = self.view_rc(id) else { continue };
832            self.build_with(id, v);
833            let mut s = self.inner.stats.get();
834            s.rebuilds += 1;
835            self.inner.stats.set(s);
836        }
837    }
838
839    #[must_use]
840    pub fn root(&self) -> Option<ElementId> {
841        self.inner.root.get()
842    }
843
844    pub fn is_alive(&self, id: ElementId) -> bool {
845        self.inner.arena.borrow().get(id).is_some()
846    }
847
848    pub fn children(&self, id: ElementId) -> Vec<ElementId> {
849        self.inner
850            .arena
851            .borrow()
852            .get(id)
853            .map(|e| e.children.clone())
854            .unwrap_or_default()
855    }
856
857    pub fn children_len(&self, id: ElementId) -> usize {
858        self.inner
859            .arena
860            .borrow()
861            .get(id)
862            .map(|e| e.children.len())
863            .unwrap_or(0)
864    }
865
866    pub fn key_of(&self, id: ElementId) -> Option<u64> {
867        self.inner.arena.borrow().get(id).map(|e| e.key)
868    }
869
870    pub fn rect_of(&self, id: ElementId) -> Option<Rect> {
871        self.inner.arena.borrow().get(id).map(|e| e.rect.get())
872    }
873    pub fn find_by_test_id(&self, test_id: &str) -> Option<ElementId> {
874        let arena = self.inner.arena.borrow();
875        for (i, slot) in arena.slots.iter().enumerate() {
876            let Some(el) = slot else { continue };
877            let id = ElementId {
878                index: i as u32,
879                generation: arena.generations[i],
880            };
881            if arena.get(id).is_none() {
882                continue;
883            }
884            let Some(view) = el.view.clone() else {
885                continue;
886            };
887            if view.test_id_erased().as_deref() == Some(test_id) {
888                return Some(id);
889            }
890        }
891        None
892    }
893
894    pub fn element_count(&self) -> usize {
895        self.inner.arena.borrow().alive()
896    }
897
898    pub fn stats(&self) -> Stats {
899        self.inner.stats.get()
900    }
901
902    pub fn errors(&self) -> Vec<RuntimeError> {
903        self.inner.errors.borrow().clone()
904    }
905
906    pub fn errors_seq(&self) -> Signal<u64> {
907        self.inner.errors_seq.clone()
908    }
909
910    pub fn clear_errors(&self) {
911        self.inner.errors.borrow_mut().clear();
912    }
913
914    pub fn is_descendant_of(&self, el: ElementId, ancestor: ElementId) -> bool {
915        let mut cur = Some(el);
916        while let Some(e) = cur {
917            if e == ancestor {
918                return true;
919            }
920            cur = self.parent_of(e);
921        }
922        false
923    }
924
925    fn record_error(&self, element: ElementId, type_name: String, message: String) {
926        let err = RuntimeError {
927            element,
928            type_name,
929            message,
930        };
931        {
932            let mut errors = self.inner.errors.borrow_mut();
933            if errors.len() >= MAX_ERRORS {
934                errors.remove(0);
935            }
936            errors.push(err.clone());
937        }
938        let mut s = self.inner.stats.get();
939        s.errors += 1;
940        self.inner.stats.set(s);
941        self.inner.errors_seq.update(|n| *n += 1);
942        self.notify_boundaries(element, &err);
943    }
944
945    fn notify_boundaries(&self, element: ElementId, err: &RuntimeError) {
946        let chain: Vec<ElementId> = {
947            let Ok(arena) = self.inner.arena.try_borrow() else {
948                return;
949            };
950            let mut out = Vec::new();
951            let mut cur = arena.get(element).and_then(|_| self.parent_of(element));
952            while let Some(id) = cur {
953                out.push(id);
954                cur = arena.get(id).and_then(|e| e.parent);
955            }
956            out
957        };
958        for id in chain {
959            if let Some(boundary) = self.view::<crate::error_boundary::ErrorBoundary>(id) {
960                boundary.notify(self, id, err);
961                return;
962            }
963        }
964    }
965
966    pub fn interact(&self) -> InteractState {
967        self.inner.interact.get()
968    }
969
970    pub fn pointer_pos(&self) -> Option<Point> {
971        let st = self.inner.interact.get();
972        st.pointer_seen.then_some(st.cursor)
973    }
974
975    fn subtree_wants_hover(&self, mut el: Option<ElementId>) -> bool {
976        while let Some(e) = el {
977            if self
978                .view_rc(e)
979                .map(|v| v.wants_hover_updates_erased())
980                .unwrap_or(false)
981            {
982                return true;
983            }
984            el = self.parent_of(e);
985        }
986        false
987    }
988
989    pub fn cursor_icon(&self) -> CursorIcon {
990        if self.inner.active_drag.borrow().is_some() {
991            return CursorIcon::Grabbing;
992        }
993        let st = self.inner.interact.get();
994        let mut cur = st.captured.or(st.hovered);
995        while let Some(id) = cur {
996            let Some(v) = self.view_rc(id) else {
997                break;
998            };
999            let (state, _) = self.view_state(id);
1000            let icon = v.cursor_icon_erased(state);
1001            if icon != CursorIcon::Default {
1002                return icon;
1003            }
1004            cur = self.parent_of(id);
1005        }
1006        CursorIcon::Default
1007    }
1008
1009    pub fn type_name_of(&self, id: ElementId) -> Option<String> {
1010        self.inner
1011            .arena
1012            .borrow()
1013            .get(id)?
1014            .view
1015            .as_ref()
1016            .map(|v| v.type_name().to_string())
1017    }
1018
1019    pub fn view<T: View + 'static>(&self, id: ElementId) -> Option<Rc<T>> {
1020        let rc = self.view_rc(id)?;
1021        let any: Rc<dyn Any> = rc.as_any_rc();
1022        any.downcast::<T>().ok()
1023    }
1024
1025    pub fn layout(&self, bounds: Rect, text: &mut TextSystem) {
1026        const MAX_SETTLE_PASSES: u8 = 4;
1027        for pass in 0..MAX_SETTLE_PASSES {
1028            self.layout_once(bounds, text);
1029            if !self.has_pending_layout() {
1030                return;
1031            }
1032            if pass + 1 < MAX_SETTLE_PASSES {
1033                self.settle();
1034            }
1035        }
1036        log::warn!(
1037            "sequoia-view: layout still dirty after {MAX_SETTLE_PASSES} passes; \
1038             a view is scheduling layout while being laid out"
1039        );
1040    }
1041
1042    fn has_pending_layout(&self) -> bool {
1043        let queue = self.inner.queue.borrow();
1044        if queue.is_empty() {
1045            return false;
1046        }
1047        let arena = self.inner.arena.borrow();
1048        queue.iter().any(|id| {
1049            arena
1050                .get(*id)
1051                .map(|e| e.dirty.needs_layout())
1052                .unwrap_or(false)
1053        })
1054    }
1055
1056    fn layout_once(&self, bounds: Rect, text: &mut TextSystem) {
1057        let Some(root) = self.inner.root.get() else {
1058            return;
1059        };
1060        self.inner.wrap_cache.borrow_mut().clear();
1061        self.inner.viewport.set(bounds);
1062
1063        self.inner.window_size.set(bounds.size);
1064        let default_font_size = self
1065            .inner
1066            .environment
1067            .borrow()
1068            .get::<Theme>()
1069            .unwrap_or_else(Theme::dark)
1070            .font_size;
1071        let mut ctx = LayoutCtx::new(text, default_font_size);
1072        let c = Constraints::loose(Size::new(bounds.size.width, bounds.size.height));
1073        let measured = self.measure(root, c, &mut ctx);
1074        let root_size = if self.inner.root_fill.get() {
1075            filled_root_size(self.child_layout_style(root), bounds.size, measured)
1076        } else {
1077            measured
1078        };
1079        self.warn_if_root_not_fill(root, bounds, root_size);
1080        let root_rect = Rect::new(bounds.origin, root_size);
1081        self.arrange(root, root_rect, &mut ctx);
1082
1083        self.layout_overlays(bounds, &mut ctx);
1084    }
1085
1086    pub fn paint(&self, list: &mut RenderList, text: &mut TextSystem) {
1087        self.inner.surfaces.borrow_mut().clear();
1088
1089        let doomed = self.inner.overlays.borrow().transient_ids();
1090        for id in doomed {
1091            self.overlay_hide(id);
1092        }
1093        let Some(root) = self.inner.root.get() else {
1094            return;
1095        };
1096        self.paint_rec(root, Vec2::ZERO, 1.0, Transform::IDENTITY, list, text);
1097
1098        {
1099            let viewport = self.inner.viewport.get();
1100            let default_font_size = self
1101                .inner
1102                .environment
1103                .borrow()
1104                .get::<Theme>()
1105                .unwrap_or_else(Theme::dark)
1106                .font_size;
1107            let mut ctx = LayoutCtx::new(&mut *text, default_font_size);
1108            self.layout_overlays(viewport, &mut ctx);
1109        }
1110
1111        let ids = self.inner.overlays.borrow().ordered_ids();
1112        for id in ids {
1113            let el = self
1114                .inner
1115                .overlays
1116                .borrow()
1117                .get(id)
1118                .and_then(|e| e.element.get());
1119            if let Some(el) = el {
1120                self.paint_rec(el, Vec2::ZERO, 1.0, Transform::IDENTITY, list, text);
1121            }
1122        }
1123
1124        self.inner
1125            .live_anims
1126            .borrow_mut()
1127            .retain(|(el, _), _| self.inner.arena.borrow().get(*el).is_some());
1128    }
1129
1130    pub fn take_surface_draws(&self) -> Vec<SurfaceDraw> {
1131        std::mem::take(&mut *self.inner.surfaces.borrow_mut())
1132    }
1133
1134    pub fn hit_test(&self, pos: Point) -> Option<ElementId> {
1135        if let Some(hit) = self.hit_overlays(pos) {
1136            return Some(hit);
1137        }
1138
1139        let root = self.inner.root.get()?;
1140        self.hit_rec(root, pos)
1141    }
1142
1143    fn hit_rec(&self, el: ElementId, pos: Point) -> Option<ElementId> {
1144        let rect = {
1145            let arena = self.inner.arena.borrow();
1146            arena.get(el)?.rect.get()
1147        };
1148        if !rect.contains(pos) {
1149            return None;
1150        }
1151        for c in self.children(el).iter().rev() {
1152            if let Some(h) = self.hit_rec(*c, pos) {
1153                return Some(h);
1154            }
1155        }
1156        let hitable = self
1157            .view_rc(el)
1158            .map(|v| v.hit_testable_erased())
1159            .unwrap_or(true);
1160        if hitable { Some(el) } else { None }
1161    }
1162
1163    pub fn pointer_move(&self, pos: Point, text: &mut TextSystem) {
1164        let mut st = self.inner.interact.get();
1165        st.cursor = pos;
1166        st.pointer_seen = true;
1167        let old_hover = st.hovered;
1168        if st.captured.is_none() {
1169            st.hovered = self.hit_test(pos);
1170            self.inner.interact.set(st);
1171        }
1172        if old_hover != st.hovered {
1173            let needs = [old_hover, st.hovered].iter().flatten().any(|&el| {
1174                let mut cur = Some(el);
1175                while let Some(e) = cur {
1176                    if self
1177                        .view_rc(e)
1178                        .map(|v| v.has_visual_state_erased())
1179                        .unwrap_or(false)
1180                    {
1181                        return true;
1182                    }
1183                    cur = self.parent_of(e);
1184                }
1185                false
1186            });
1187            if needs {
1188                for el in [old_hover, st.hovered].iter().flatten() {
1189                    self.inner.mark(*el, Dirty::PAINT);
1190                }
1191                self.request_redraw();
1192            }
1193        }
1194
1195        if self.subtree_wants_hover(st.hovered) {
1196            self.request_redraw();
1197        }
1198
1199        if self.inner.pending_drag.borrow().is_some() || self.inner.active_drag.borrow().is_some() {
1200            self.poll_pending(pos, Modifiers::NONE, text);
1201        }
1202        let ev = Event::Pointer(PointerEvent::Move {
1203            pos,
1204            modifiers: Modifiers::NONE,
1205        });
1206        match st.captured {
1207            Some(c) => {
1208                let _ = self.dispatch_single(c, &ev, pos, text);
1209            }
1210            None => {
1211                if let Some(t) = st.hovered {
1212                    let _ = self.dispatch_single(t, &ev, pos, text);
1213                }
1214            }
1215        }
1216        if let Event::Pointer(pe) = &ev {
1217            match st.captured {
1218                Some(c) => self.forward_surface_pointer(c, pe),
1219                None => {
1220                    if let Some(t) = st.hovered {
1221                        self.forward_surface_pointer(t, pe);
1222                    }
1223                }
1224            }
1225        }
1226    }
1227
1228    fn forward_surface_pointer(&self, el: ElementId, event: &PointerEvent) {
1229        let Some(v) = self.view_rc(el) else { return };
1230        let Some(mut binding) = v.surface_input_erased() else {
1231            return;
1232        };
1233        let Some(rect) = self.rect_of(el) else { return };
1234        (binding.on_pointer)(event, rect);
1235    }
1236
1237    fn forward_surface_key(&self, el: ElementId, event: &KeyEvent) {
1238        let Some(v) = self.view_rc(el) else { return };
1239        let Some(mut binding) = v.surface_input_erased() else {
1240            return;
1241        };
1242        (binding.on_key)(event);
1243    }
1244
1245    pub fn pointer_down(
1246        &self,
1247        pos: Point,
1248        button: PointerButton,
1249        mods: Modifiers,
1250        text: &mut TextSystem,
1251    ) {
1252        if button != PointerButton::Left && self.inner.active_drag.borrow().is_some() {
1253            self.cancel_drag(text);
1254            return;
1255        }
1256
1257        let doomed = self.inner.overlays.borrow().outside_dismiss_ids(pos);
1258        for id in doomed {
1259            self.overlay_hide(id);
1260        }
1261        let target = self.hit_test(pos);
1262        {
1263            let mut st = self.inner.interact.get();
1264            st.cursor = pos;
1265            st.pressed = target;
1266
1267            st.focus = match target {
1268                None => None,
1269                Some(t) => self.nearest_focusable(t).or(st.focus),
1270            };
1271
1272            if st.focus.is_some_and(|f| !self.is_alive(f)) {
1273                st.focus = None;
1274            }
1275            self.inner.interact.set(st);
1276        }
1277        self.dismiss_focus_lost(target);
1278        if let Some(t) = target {
1279            let ev = Event::Pointer(PointerEvent::Down {
1280                button,
1281                pos,
1282                modifiers: mods,
1283            });
1284            self.dispatch_chain(t, &ev, pos, text);
1285            if let Event::Pointer(pe) = &ev {
1286                self.forward_surface_pointer(t, pe);
1287            }
1288            if let Some(cap) = self.auto_capture(t) {
1289                let mut st = self.inner.interact.get();
1290                st.captured = Some(cap);
1291                self.inner.interact.set(st);
1292            }
1293        }
1294        self.request_redraw();
1295    }
1296
1297    pub fn pointer_up(
1298        &self,
1299        pos: Point,
1300        button: PointerButton,
1301        mods: Modifiers,
1302        text: &mut TextSystem,
1303    ) {
1304        if self.inner.active_drag.borrow().is_some() {
1305            if button == PointerButton::Left {
1306                self.finish_drop(pos, mods, text);
1307            } else {
1308                self.cancel_drag(text);
1309            }
1310            return;
1311        }
1312
1313        if self.inner.pending_drag.borrow().is_some() {
1314            self.inner.pending_drag.borrow_mut().take();
1315            let mut st = self.inner.interact.get();
1316            st.captured = None;
1317            self.inner.interact.set(st);
1318        }
1319        let mut st = self.inner.interact.get();
1320        st.cursor = pos;
1321        st.hovered = self.hit_test(pos);
1322        self.inner.interact.set(st);
1323        let ev = Event::Pointer(PointerEvent::Up {
1324            button,
1325            pos,
1326            modifiers: mods,
1327        });
1328        match st.captured {
1329            Some(c) => {
1330                let handled = self.dispatch_single(c, &ev, pos, text);
1331
1332                if !handled
1333                    && let Some(p) = st.pressed
1334                    && p != c
1335                    && self.is_alive(p)
1336                {
1337                    self.dispatch_chain(p, &ev, pos, text);
1338                }
1339            }
1340            None => {
1341                if let Some(p) = st.pressed {
1342                    self.dispatch_chain(p, &ev, pos, text);
1343                }
1344            }
1345        }
1346        if let Event::Pointer(pe) = &ev {
1347            match st.captured {
1348                Some(c) => self.forward_surface_pointer(c, pe),
1349                None => {
1350                    if let Some(p) = st.pressed {
1351                        self.forward_surface_pointer(p, pe);
1352                    }
1353                }
1354            }
1355        }
1356        let mut st2 = self.inner.interact.get();
1357        st2.pressed = None;
1358        st2.captured = None;
1359        self.inner.interact.set(st2);
1360        self.request_redraw();
1361    }
1362
1363    pub fn wheel(&self, pos: Point, delta: Vec2, modifiers: Modifiers, text: &mut TextSystem) {
1364        let Some(target) = self.hit_test(pos) else {
1365            return;
1366        };
1367        let ev = Event::Pointer(PointerEvent::Wheel {
1368            delta,
1369            pos,
1370            modifiers,
1371        });
1372        self.dispatch_chain(target, &ev, pos, text);
1373        if let Event::Pointer(pe) = &ev {
1374            self.forward_surface_pointer(target, pe);
1375        }
1376        self.request_redraw();
1377    }
1378
1379    pub fn pointer_pressure(&self) -> f32 {
1380        self.inner.stylus_pressure.get()
1381    }
1382
1383    pub fn pointer_tool(&self) -> PenTool {
1384        self.inner.stylus_tool.get()
1385    }
1386
1387    pub fn active_touches(&self) -> Vec<(u64, Point)> {
1388        self.inner
1389            .touches
1390            .borrow()
1391            .iter()
1392            .map(|(k, v)| (*k, *v))
1393            .collect()
1394    }
1395
1396    fn set_stylus(&self, pressure: f32, tool: PenTool) {
1397        self.inner.stylus_pressure.set(pressure.clamp(0.0, 1.0));
1398        self.inner.stylus_tool.set(tool);
1399    }
1400
1401    pub fn pen_down(
1402        &self,
1403        pos: Point,
1404        button: PointerButton,
1405        pressure: f32,
1406        tilt: Vec2,
1407        tool: PenTool,
1408        mods: Modifiers,
1409        text: &mut TextSystem,
1410    ) {
1411        let _ = tilt;
1412        self.set_stylus(pressure, tool);
1413        self.pointer_down(pos, button, mods, text);
1414    }
1415
1416    pub fn pen_move(
1417        &self,
1418        pos: Point,
1419        pressure: f32,
1420        tilt: Vec2,
1421        tool: PenTool,
1422        text: &mut TextSystem,
1423    ) {
1424        let _ = tilt;
1425        self.set_stylus(pressure, tool);
1426        self.pointer_move(pos, text);
1427    }
1428
1429    pub fn pen_up(
1430        &self,
1431        pos: Point,
1432        button: PointerButton,
1433        pressure: f32,
1434        mods: Modifiers,
1435        text: &mut TextSystem,
1436    ) {
1437        self.set_stylus(pressure, self.inner.stylus_tool.get());
1438        self.pointer_up(pos, button, mods, text);
1439    }
1440
1441    pub fn touch_down(
1442        &self,
1443        id: u64,
1444        pos: Point,
1445        force: Option<f32>,
1446        mods: Modifiers,
1447        text: &mut TextSystem,
1448    ) {
1449        let first = self.inner.touches.borrow().is_empty();
1450        self.inner.touches.borrow_mut().insert(id, pos);
1451        self.set_stylus(force.unwrap_or(1.0), PenTool::Touch);
1452        if first {
1453            self.pointer_down(pos, PointerButton::Left, mods, text);
1454        }
1455        if let Some(t) = self.hit_test(pos) {
1456            let ev = Event::Pointer(PointerEvent::TouchDown {
1457                id,
1458                pos,
1459                force,
1460                modifiers: mods,
1461            });
1462            self.dispatch_chain(t, &ev, pos, text);
1463            if let Event::Pointer(pe) = &ev {
1464                self.forward_surface_pointer(t, pe);
1465            }
1466        }
1467        self.request_redraw();
1468    }
1469
1470    pub fn touch_move(&self, id: u64, pos: Point, force: Option<f32>, text: &mut TextSystem) {
1471        let is_first = self
1472            .inner
1473            .touches
1474            .borrow()
1475            .keys()
1476            .next()
1477            .is_some_and(|first| *first == id);
1478        self.inner.touches.borrow_mut().insert(id, pos);
1479        self.set_stylus(force.unwrap_or(1.0), PenTool::Touch);
1480        if is_first {
1481            self.pointer_move(pos, text);
1482        }
1483        let ev = Event::Pointer(PointerEvent::TouchMove {
1484            id,
1485            pos,
1486            force,
1487            modifiers: Modifiers::NONE,
1488        });
1489
1490        match self.inner.interact.get().captured {
1491            Some(c) => {
1492                self.dispatch_single(c, &ev, pos, text);
1493                if let Event::Pointer(pe) = &ev {
1494                    self.forward_surface_pointer(c, pe);
1495                }
1496            }
1497            None => {
1498                if let Some(t) = self.hit_test(pos) {
1499                    self.dispatch_chain(t, &ev, pos, text);
1500                    if let Event::Pointer(pe) = &ev {
1501                        self.forward_surface_pointer(t, pe);
1502                    }
1503                }
1504            }
1505        }
1506        self.request_redraw();
1507    }
1508
1509    pub fn touch_up(&self, id: u64, pos: Point, mods: Modifiers, text: &mut TextSystem) {
1510        let was_last = {
1511            let mut touches = self.inner.touches.borrow_mut();
1512            touches.remove(&id);
1513            touches.is_empty()
1514        };
1515        let ev = Event::Pointer(PointerEvent::TouchUp {
1516            id,
1517            pos,
1518            modifiers: mods,
1519        });
1520
1521        match self.inner.interact.get().captured {
1522            Some(c) => {
1523                self.dispatch_single(c, &ev, pos, text);
1524                if let Event::Pointer(pe) = &ev {
1525                    self.forward_surface_pointer(c, pe);
1526                }
1527            }
1528            None => {
1529                if let Some(t) = self.hit_test(pos) {
1530                    self.dispatch_chain(t, &ev, pos, text);
1531                    if let Event::Pointer(pe) = &ev {
1532                        self.forward_surface_pointer(t, pe);
1533                    }
1534                }
1535            }
1536        }
1537        if was_last {
1538            self.pointer_up(pos, PointerButton::Left, mods, text);
1539        } else {
1540            self.request_redraw();
1541        }
1542    }
1543
1544    pub fn pinch(&self, pos: Point, delta: f32, mods: Modifiers, text: &mut TextSystem) {
1545        let Some(target) = self.hit_test(pos) else {
1546            return;
1547        };
1548        let ev = Event::Pointer(PointerEvent::Pinch {
1549            delta,
1550            pos,
1551            modifiers: mods,
1552        });
1553        self.dispatch_chain(target, &ev, pos, text);
1554        if let Event::Pointer(pe) = &ev {
1555            self.forward_surface_pointer(target, pe);
1556        }
1557        self.request_redraw();
1558    }
1559
1560    pub fn key(&self, key: Key, mods: Modifiers, text: &mut TextSystem) {
1561        if key == Key::ESCAPE && self.inner.active_drag.borrow().is_some() {
1562            self.cancel_drag(text);
1563            return;
1564        }
1565        let esc = self.inner.overlays.borrow().escape_dismiss_id();
1566        if key == Key::ESCAPE
1567            && let Some(id) = esc
1568        {
1569            self.overlay_hide(id);
1570            return;
1571        }
1572        if key == Key::TAB && !self.focused_accepts_tab() {
1573            self.focus_next(mods.contains(Modifiers::SHIFT));
1574            self.request_redraw();
1575            return;
1576        }
1577        let st = self.inner.interact.get();
1578        let Some(f) = st.focus else { return };
1579        if !self.is_alive(f) {
1580            return;
1581        }
1582        let ev = Event::Key(KeyEvent {
1583            key,
1584            modifiers: mods,
1585        });
1586        self.dispatch_chain(f, &ev, Point::ZERO, text);
1587        if let Event::Key(ke) = &ev {
1588            self.forward_surface_key(f, ke);
1589        }
1590        self.request_redraw();
1591    }
1592
1593    pub fn ime(&self, event: ImeEvent, text: &mut TextSystem) {
1594        let st = self.inner.interact.get();
1595        let Some(f) = st.focus else { return };
1596        if !self.is_alive(f) {
1597            return;
1598        }
1599        let ev = Event::Ime(event);
1600
1601        let cursor = st.cursor;
1602        self.dispatch_chain(f, &ev, cursor, text);
1603        self.request_redraw();
1604    }
1605
1606    pub fn files_hovered(&self, paths: Vec<std::path::PathBuf>, pos: Point, text: &mut TextSystem) {
1607        let Some(target) = self.hit_test(pos) else {
1608            return;
1609        };
1610        let ev = Event::FilesHovered {
1611            paths,
1612            position: pos,
1613        };
1614        self.dispatch_chain(target, &ev, pos, text);
1615        self.request_redraw();
1616    }
1617
1618    pub fn files_dropped(&self, paths: Vec<std::path::PathBuf>, pos: Point, text: &mut TextSystem) {
1619        let Some(target) = self.hit_test(pos) else {
1620            return;
1621        };
1622        let ev = Event::FilesDropped {
1623            paths,
1624            position: pos,
1625        };
1626        self.dispatch_chain(target, &ev, pos, text);
1627        self.request_redraw();
1628    }
1629
1630    pub fn files_cancelled(&self, text: &mut TextSystem) {
1631        let pos = self.inner.interact.get().cursor;
1632        let ev = Event::FilesCancelled;
1633        if let Some(target) = self.hit_test(pos) {
1634            self.dispatch_chain(target, &ev, pos, text);
1635        }
1636        self.request_redraw();
1637    }
1638
1639    pub fn ime_cursor_rect(&self) -> Option<Rect> {
1640        let st = self.inner.interact.get();
1641        let f = st.focus?;
1642        self.rect_of(f)
1643    }
1644
1645    pub fn overlay_show(&self, entry: OverlayEntry) -> OverlayId {
1646        let transient = entry.transient;
1647        let view = entry.view.clone_inner();
1648        let value = unwrap_any(view);
1649        let el = self.alloc(value, None, 0, 0);
1650        if let Some(v) = self.view_rc(el) {
1651            self.build_with(el, v);
1652        }
1653        if let Some(v) = self.view_rc(el) {
1654            let type_name = v.type_name().to_string();
1655            if std::panic::catch_unwind(AssertUnwindSafe(|| {
1656                v.on_mount();
1657            }))
1658            .is_err()
1659            {
1660                self.record_error(el, type_name, "on_mount panicked".to_string());
1661            }
1662        }
1663        entry.element.set(Some(el));
1664        let id = self.inner.overlays.borrow_mut().insert(entry);
1665        if !transient {
1666            self.request_redraw();
1667        }
1668        id
1669    }
1670
1671    pub fn overlay_hide(&self, id: OverlayId) -> bool {
1672        let Some(entry) = self.inner.overlays.borrow_mut().remove(id) else {
1673            return false;
1674        };
1675        if let Some(el) = entry.element.get() {
1676            self.unmount_subtree(el);
1677        }
1678        self.request_redraw();
1679        true
1680    }
1681
1682    pub fn overlay_rect(&self, id: OverlayId) -> Option<Rect> {
1683        self.inner.overlays.borrow().get(id).map(|e| e.rect.get())
1684    }
1685
1686    pub fn overlay_exists(&self, id: OverlayId) -> bool {
1687        self.inner.overlays.borrow().get(id).is_some()
1688    }
1689
1690    pub fn overlay_element(&self, id: OverlayId) -> Option<ElementId> {
1691        self.inner
1692            .overlays
1693            .borrow()
1694            .get(id)
1695            .and_then(|e| e.element.get())
1696    }
1697
1698    pub fn accessibility_tree(&self) -> Vec<crate::a11y::AccessibleNode> {
1699        let Some(root) = self.inner.root.get() else {
1700            return Vec::new();
1701        };
1702        self.accessibility_children(root)
1703    }
1704
1705    fn accessibility_children(&self, el: ElementId) -> Vec<crate::a11y::AccessibleNode> {
1706        let mut out = Vec::new();
1707        for child in self.children(el) {
1708            if !self.is_alive(child) {
1709                continue;
1710            }
1711            let kids = self.accessibility_children(child);
1712            match self.view_rc(child).and_then(|v| v.accessibility_erased()) {
1713                Some(a) => out.push(crate::a11y::AccessibleNode {
1714                    element: child,
1715                    role: a.role,
1716                    label: a.label,
1717                    children: kids,
1718                }),
1719                None => out.extend(kids),
1720            }
1721        }
1722        out
1723    }
1724
1725    pub fn overlay_ids(&self) -> Vec<OverlayId> {
1726        self.inner.overlays.borrow().ordered_ids()
1727    }
1728
1729    pub fn overlay_count(&self) -> usize {
1730        self.inner.overlays.borrow().len()
1731    }
1732
1733    pub fn overlay_set_position(&self, id: OverlayId, x: f32, y: f32) -> bool {
1734        let ok = self.inner.overlays.borrow_mut().set_offset(id, (x, y));
1735        if ok {
1736            self.request_redraw();
1737        }
1738        ok
1739    }
1740
1741    pub fn begin_pending(
1742        &self,
1743        source: ElementId,
1744        payload: DragPayload,
1745        origin: Point,
1746        source_rect: Rect,
1747        options: DragOptions,
1748    ) {
1749        if self.inner.active_drag.borrow().is_some() || !self.is_alive(source) {
1750            return;
1751        }
1752        *self.inner.pending_drag.borrow_mut() = Some(PendingDrag {
1753            source,
1754            origin,
1755            source_rect,
1756            payload,
1757            preview: options.preview,
1758            threshold: options.threshold,
1759        });
1760    }
1761
1762    pub fn current_drag(&self) -> Option<DragSession> {
1763        self.inner
1764            .active_drag
1765            .borrow()
1766            .as_ref()
1767            .map(ActiveDrag::snapshot)
1768    }
1769
1770    pub fn has_pending_drag(&self) -> bool {
1771        self.inner.pending_drag.borrow().is_some()
1772    }
1773
1774    pub fn drag_target_contains(&self, el: ElementId) -> bool {
1775        let Some(target) = self
1776            .inner
1777            .active_drag
1778            .borrow()
1779            .as_ref()
1780            .and_then(|a| a.target)
1781        else {
1782            return false;
1783        };
1784        let mut cur = Some(el);
1785        while let Some(e) = cur {
1786            if e == target {
1787                return true;
1788            }
1789            cur = self.parent_of(e);
1790        }
1791        false
1792    }
1793
1794    pub fn drop_target_at(&self, pos: Point, payload: &DragPayload) -> Option<ElementId> {
1795        let mut cur = self.hit_test(pos);
1796        while let Some(el) = cur {
1797            if self.is_alive(el)
1798                && self
1799                    .view_rc(el)
1800                    .is_some_and(|v| v.accepts_drop_erased(payload))
1801            {
1802                return Some(el);
1803            }
1804            cur = self.parent_of(el);
1805        }
1806        None
1807    }
1808
1809    pub fn cancel_drag(&self, text: &mut TextSystem) {
1810        let (source, current) = {
1811            let guard = self.inner.active_drag.borrow();
1812            let Some(act) = guard.as_ref() else {
1813                drop(guard);
1814                self.inner.pending_drag.borrow_mut().take();
1815                return;
1816            };
1817            (act.source, act.current)
1818        };
1819        if let Some(src) = source.filter(|s| self.is_alive(*s)) {
1820            self.call_drag_end(
1821                src,
1822                DragEnd {
1823                    dropped: false,
1824                    cancelled: true,
1825                },
1826                current,
1827                text,
1828            );
1829        }
1830        let active = self.inner.active_drag.borrow_mut().take();
1831        self.inner.pending_drag.borrow_mut().take();
1832        if let Some(act) = active
1833            && let Some(pv) = act.preview
1834        {
1835            self.overlay_hide(pv);
1836        }
1837        let mut st = self.inner.interact.get();
1838        st.pressed = None;
1839        st.captured = None;
1840        self.inner.interact.set(st);
1841        self.request_redraw();
1842    }
1843
1844    fn poll_pending(&self, current: Point, modifiers: Modifiers, text: &mut TextSystem) {
1845        if self.inner.active_drag.borrow().is_some() {
1846            self.update_active(current, modifiers, text);
1847            return;
1848        }
1849        let pending = self.inner.pending_drag.borrow_mut().take();
1850        let Some(p) = pending else { return };
1851        if !self.is_alive(p.source) {
1852            return;
1853        }
1854        if !exceeded_threshold(p.origin, current, p.threshold) {
1855            *self.inner.pending_drag.borrow_mut() = Some(p);
1856            return;
1857        }
1858        let grab = grab_offset(p.origin, p.source_rect);
1859        let id = DragId(self.inner.next_drag_id.get());
1860        self.inner.next_drag_id.set(id.0 + 1);
1861        let preview = p.preview.map(|view| {
1862            let at = preview_origin(current, grab);
1863            self.overlay_show(
1864                OverlayEntry::new(view)
1865                    .at(at.x, at.y)
1866                    .layer(OverlayLayer::DragPreview)
1867                    .input_mode(InputMode::Passthrough),
1868            )
1869        });
1870        let target = self.drop_target_at(current, &p.payload);
1871        *self.inner.active_drag.borrow_mut() = Some(ActiveDrag {
1872            id,
1873            payload: p.payload,
1874            source: Some(p.source),
1875            origin: p.origin,
1876            grab_offset: grab,
1877            current,
1878            modifiers,
1879            target,
1880            preview,
1881        });
1882
1883        let mut st = self.inner.interact.get();
1884        st.captured = Some(p.source);
1885        self.inner.interact.set(st);
1886        self.call_drag_start(p.source, current, text);
1887        if let Some(t) = target {
1888            self.call_drag_over(t, current, modifiers, text);
1889        }
1890        self.request_redraw();
1891    }
1892
1893    fn update_active(&self, current: Point, modifiers: Modifiers, text: &mut TextSystem) {
1894        let (target, preview_move) = {
1895            let mut guard = self.inner.active_drag.borrow_mut();
1896            let Some(act) = guard.as_mut() else {
1897                return;
1898            };
1899            act.current = current;
1900            act.modifiers = modifiers;
1901            let t = self.drop_target_at(current, &act.payload);
1902            act.target = t;
1903            let mv = act
1904                .preview
1905                .map(|id| (id, preview_origin(current, act.grab_offset)));
1906            (t, mv)
1907        };
1908        if let Some((id, at)) = preview_move {
1909            self.overlay_set_position(id, at.x, at.y);
1910        }
1911        if let Some(t) = target {
1912            self.call_drag_over(t, current, modifiers, text);
1913        }
1914        self.request_redraw();
1915    }
1916
1917    fn finish_drop(&self, current: Point, modifiers: Modifiers, text: &mut TextSystem) -> bool {
1918        let payload = {
1919            let guard = self.inner.active_drag.borrow();
1920            let Some(act) = guard.as_ref() else {
1921                return false;
1922            };
1923            act.payload.clone()
1924        };
1925
1926        let target = self.drop_target_at(current, &payload);
1927        let mut dropped = false;
1928        if let Some(t) = target {
1929            let rect = self.rect_of(t).unwrap_or(Rect::ZERO);
1930            let pos = DropPosition::in_rect(current, rect, modifiers);
1931            dropped = self.call_drop(t, pos, text);
1932        }
1933        let (source, preview) = {
1934            let mut guard = self.inner.active_drag.borrow_mut();
1935            match guard.take() {
1936                Some(act) => (act.source, act.preview),
1937
1938                None => return true,
1939            }
1940        };
1941        self.inner.pending_drag.borrow_mut().take();
1942        if let Some(pv) = preview {
1943            self.overlay_hide(pv);
1944        }
1945        let mut st = self.inner.interact.get();
1946        st.pressed = None;
1947        st.captured = None;
1948        st.hovered = self.hit_test(current);
1949        self.inner.interact.set(st);
1950        if let Some(src) = source.filter(|s| self.is_alive(*s)) {
1951            self.call_drag_end(
1952                src,
1953                DragEnd {
1954                    dropped,
1955                    cancelled: false,
1956                },
1957                current,
1958                text,
1959            );
1960        }
1961        self.request_redraw();
1962        true
1963    }
1964
1965    fn drag_ctx_parts(&self, el: ElementId) -> (VisualState, bool, Rect, Environment) {
1966        let (state, hover_self) = self.view_state(el);
1967        let rect = {
1968            let arena = self.inner.arena.borrow();
1969            arena.get(el).map(|e| e.rect.get()).unwrap_or(Rect::ZERO)
1970        };
1971        let env = self.inner.environment.borrow().clone();
1972        (state, hover_self, rect, env)
1973    }
1974
1975    fn call_drag_start(&self, el: ElementId, pos: Point, text: &mut TextSystem) {
1976        let Some(v) = self.view_rc(el) else { return };
1977        let cells = DispatchCells::new();
1978        let (state, hover_self, rect, env) = self.drag_ctx_parts(el);
1979        let mut ctx = EventCtx {
1980            phase: EventPhase::Target,
1981            element: el,
1982            state,
1983            hover_self,
1984            pos,
1985            rect,
1986            environment: &env,
1987            window: self.inner.window.get(),
1988            stopped: &cells.stopped,
1989            handled: &cells.handled,
1990            capture_req: &cells.capture_req,
1991            release_req: &cells.release_req,
1992            focus_req: &cells.focus_req,
1993            text,
1994            runtime: self,
1995        };
1996        let type_name = v.type_name().to_string();
1997        if std::panic::catch_unwind(AssertUnwindSafe(|| {
1998            v.on_drag_start_erased(&mut ctx);
1999        }))
2000        .is_err()
2001        {
2002            self.record_error(el, type_name, "on_drag_start panicked".to_string());
2003        }
2004    }
2005
2006    fn call_drag_over(
2007        &self,
2008        el: ElementId,
2009        current: Point,
2010        modifiers: Modifiers,
2011        text: &mut TextSystem,
2012    ) {
2013        let Some(v) = self.view_rc(el) else { return };
2014        let cells = DispatchCells::new();
2015        let (state, hover_self, rect, env) = self.drag_ctx_parts(el);
2016        let mut ctx = EventCtx {
2017            phase: EventPhase::Target,
2018            element: el,
2019            state,
2020            hover_self,
2021            pos: current,
2022            rect,
2023            environment: &env,
2024            window: self.inner.window.get(),
2025            stopped: &cells.stopped,
2026            handled: &cells.handled,
2027            capture_req: &cells.capture_req,
2028            release_req: &cells.release_req,
2029            focus_req: &cells.focus_req,
2030            text,
2031            runtime: self,
2032        };
2033        let pos = DropPosition::in_rect(current, rect, modifiers);
2034        let type_name = v.type_name().to_string();
2035        if std::panic::catch_unwind(AssertUnwindSafe(|| {
2036            v.on_drag_over_erased(pos, &mut ctx);
2037        }))
2038        .is_err()
2039        {
2040            self.record_error(el, type_name, "on_drag_over panicked".to_string());
2041        }
2042    }
2043
2044    fn call_drop(&self, el: ElementId, pos: DropPosition, text: &mut TextSystem) -> bool {
2045        let Some(v) = self.view_rc(el) else {
2046            return false;
2047        };
2048        let cells = DispatchCells::new();
2049        let (state, hover_self, rect, env) = self.drag_ctx_parts(el);
2050        let mut ctx = EventCtx {
2051            phase: EventPhase::Target,
2052            element: el,
2053            state,
2054            hover_self,
2055            pos: pos.global,
2056            rect,
2057            environment: &env,
2058            window: self.inner.window.get(),
2059            stopped: &cells.stopped,
2060            handled: &cells.handled,
2061            capture_req: &cells.capture_req,
2062            release_req: &cells.release_req,
2063            focus_req: &cells.focus_req,
2064            text,
2065            runtime: self,
2066        };
2067        let type_name = v.type_name().to_string();
2068        if std::panic::catch_unwind(AssertUnwindSafe(|| {
2069            v.on_drop_erased(pos, &mut ctx);
2070        }))
2071        .is_err()
2072        {
2073            self.record_error(el, type_name, "on_drop panicked".to_string());
2074            return false;
2075        }
2076        cells.handled.get()
2077    }
2078
2079    fn call_drag_end(&self, el: ElementId, end: DragEnd, current: Point, text: &mut TextSystem) {
2080        let Some(v) = self.view_rc(el) else { return };
2081        let cells = DispatchCells::new();
2082        let (state, hover_self, rect, env) = self.drag_ctx_parts(el);
2083        let mut ctx = EventCtx {
2084            phase: EventPhase::Target,
2085            element: el,
2086            state,
2087            hover_self,
2088            pos: current,
2089            rect,
2090            environment: &env,
2091            window: self.inner.window.get(),
2092            stopped: &cells.stopped,
2093            handled: &cells.handled,
2094            capture_req: &cells.capture_req,
2095            release_req: &cells.release_req,
2096            focus_req: &cells.focus_req,
2097            text,
2098            runtime: self,
2099        };
2100        let type_name = v.type_name().to_string();
2101        if std::panic::catch_unwind(AssertUnwindSafe(|| {
2102            v.on_drag_end_erased(end, &mut ctx);
2103        }))
2104        .is_err()
2105        {
2106            self.record_error(el, type_name, "on_drag_end panicked".to_string());
2107        }
2108    }
2109
2110    fn layout_overlays(&self, viewport: Rect, ctx: &mut LayoutCtx) {
2111        for id in self.inner.overlays.borrow().ordered_ids() {
2112            self.layout_one_overlay(id, viewport, ctx);
2113        }
2114    }
2115
2116    fn layout_one_overlay(&self, id: OverlayId, viewport: Rect, ctx: &mut LayoutCtx) {
2117        let (anchor, placement, position, offset, overflow, el) = {
2118            let store = self.inner.overlays.borrow();
2119            let Some(e) = store.get(id) else { return };
2120            let Some(el) = e.element.get() else { return };
2121            (e.anchor, e.placement, e.position, e.offset, e.overflow, el)
2122        };
2123
2124        let anchor_rect = match anchor {
2125            Anchor::View(aid) => match self.rect_of(aid) {
2126                Some(r) => r,
2127
2128                None => {
2129                    self.record_error(
2130                        el,
2131                        "Overlay".to_string(),
2132                        "dead Anchor::View: overlay falls back to (0,0); recreate entry with live ctx.element".to_string(),
2133                    );
2134                    Rect::ZERO
2135                }
2136            },
2137            _ => self.resolve_anchor(anchor, viewport),
2138        };
2139
2140        let measured = self.measure(el, Constraints::UNBOUNDED, ctx);
2141
2142        let pos = compute_position(
2143            position,
2144            placement,
2145            anchor_rect,
2146            measured,
2147            viewport,
2148            offset,
2149            overflow,
2150        );
2151        let rect = Rect::new(pos, measured);
2152
2153        self.arrange(el, rect, ctx);
2154
2155        if let Some(e) = self.inner.overlays.borrow().get(id) {
2156            e.rect.set(rect);
2157        }
2158    }
2159
2160    fn resolve_anchor(&self, anchor: Anchor, viewport: Rect) -> Rect {
2161        match anchor {
2162            Anchor::View(id) => self.rect_of(id).unwrap_or(Rect::ZERO),
2163            Anchor::Rect(r) => r,
2164            Anchor::Point(p) => Rect::new(p, Size::ZERO),
2165            Anchor::Cursor => Rect::new(self.inner.interact.get().cursor, Size::ZERO),
2166            Anchor::Viewport => viewport,
2167        }
2168    }
2169
2170    fn hit_overlays(&self, pos: Point) -> Option<ElementId> {
2171        for id in self.inner.overlays.borrow().ordered_ids().into_iter().rev() {
2172            let (mode, el, rect) = {
2173                let store = self.inner.overlays.borrow();
2174                let Some(e) = store.get(id) else { continue };
2175                let Some(el) = e.element.get() else { continue };
2176                (e.input_mode, el, e.rect.get())
2177            };
2178            match mode {
2179                InputMode::Passthrough => continue,
2180                InputMode::Modal => {
2181                    if rect.contains(pos)
2182                        && let Some(hit) = self.hit_rec(el, pos)
2183                    {
2184                        return Some(hit);
2185                    }
2186                    return Some(el);
2187                }
2188                InputMode::Interactive => {
2189                    if rect.contains(pos)
2190                        && let Some(hit) = self.hit_rec(el, pos)
2191                    {
2192                        return Some(hit);
2193                    }
2194                }
2195            }
2196        }
2197        None
2198    }
2199
2200    fn hover_in_subtree(&self, el: ElementId) -> bool {
2201        let st = self.inner.interact.get();
2202        let Some(h) = st.hovered else { return false };
2203        let mut cur = Some(h);
2204        while let Some(e) = cur {
2205            if e == el {
2206                return true;
2207            }
2208            cur = self.parent_of(e);
2209        }
2210        false
2211    }
2212
2213    pub fn focus_in_subtree(&self, el: ElementId) -> bool {
2214        let st = self.inner.interact.get();
2215        let Some(f) = st.focus else { return false };
2216        if f == el {
2217            return true;
2218        }
2219        let mut cur = self.parent_of(f);
2220        while let Some(e) = cur {
2221            if e == el {
2222                return true;
2223            }
2224            cur = self.parent_of(e);
2225        }
2226        false
2227    }
2228
2229    pub fn parent_of(&self, el: ElementId) -> Option<ElementId> {
2230        self.inner.arena.borrow().get(el)?.parent
2231    }
2232
2233    fn focused_accepts_tab(&self) -> bool {
2234        let st = self.inner.interact.get();
2235        let Some(f) = st.focus else { return false };
2236        if !self.is_alive(f) {
2237            return false;
2238        }
2239        self.view_rc(f).is_some_and(|v| v.accepts_tab_erased())
2240    }
2241
2242    fn nearest_focusable(&self, mut el: ElementId) -> Option<ElementId> {
2243        loop {
2244            if let Some(v) = self.view_rc(el)
2245                && v.focusable_erased()
2246            {
2247                return Some(el);
2248            }
2249            {
2250                let p = self.parent_of(el)?;
2251                el = p
2252            }
2253        }
2254    }
2255
2256    fn nearest_trap_scope(&self, mut el: ElementId) -> Option<ElementId> {
2257        loop {
2258            if let Some(v) = self.view_rc(el)
2259                && v.focus_trap_erased()
2260            {
2261                return Some(el);
2262            }
2263            {
2264                let p = self.parent_of(el)?;
2265                el = p
2266            }
2267        }
2268    }
2269
2270    fn collect_focusables(&self, el: ElementId, out: &mut Vec<ElementId>) {
2271        if let Some(v) = self.view_rc(el)
2272            && v.focusable_erased()
2273        {
2274            out.push(el);
2275        }
2276        for c in self.children(el) {
2277            self.collect_focusables(c, out);
2278        }
2279    }
2280
2281    pub fn focus_next(&self, backward: bool) {
2282        let mut st = self.inner.interact.get();
2283
2284        let mut f = Vec::new();
2285        let scope = st.focus.and_then(|c| self.nearest_trap_scope(c));
2286        match scope {
2287            Some(s) => self.collect_focusables(s, &mut f),
2288            None => {
2289                if let Some(root) = self.inner.root.get() {
2290                    self.collect_focusables(root, &mut f);
2291                }
2292            }
2293        }
2294        if f.is_empty() {
2295            return;
2296        }
2297        let len = f.len();
2298        let next = match st.focus.and_then(|c| f.iter().position(|&e| e == c)) {
2299            Some(i) if backward => (i + len - 1) % len,
2300            Some(i) => (i + 1) % len,
2301            None => 0,
2302        };
2303        st.focus = Some(f[next]);
2304        self.inner.interact.set(st);
2305        self.dismiss_focus_lost(st.focus);
2306    }
2307
2308    pub fn set_focus(&self, el: Option<ElementId>) {
2309        let mut st = self.inner.interact.get();
2310        st.focus = el;
2311        self.inner.interact.set(st);
2312        self.dismiss_focus_lost(el);
2313    }
2314
2315    fn target_in_overlay(&self, target: ElementId, overlay_el: Option<ElementId>) -> bool {
2316        let Some(root) = overlay_el else { return false };
2317        let mut cur = Some(target);
2318        while let Some(el) = cur {
2319            if el == root {
2320                return true;
2321            }
2322            cur = self.parent_of(el);
2323        }
2324        false
2325    }
2326
2327    fn dismiss_focus_lost(&self, target: Option<ElementId>) {
2328        let ids = self.inner.overlays.borrow().focus_lost_ids(target, |el| {
2329            target.is_some_and(|t| self.target_in_overlay(t, el))
2330        });
2331        for id in ids {
2332            self.overlay_hide(id);
2333        }
2334    }
2335
2336    fn auto_capture(&self, target: ElementId) -> Option<ElementId> {
2337        let mut cur = Some(target);
2338        while let Some(el) = cur {
2339            if let Some(v) = self.view_rc(el)
2340                && v.captures_pointer_ctx_erased(self, el)
2341            {
2342                return Some(el);
2343            }
2344            cur = self.parent_of(el);
2345        }
2346        None
2347    }
2348
2349    fn view_state(&self, el: ElementId) -> (VisualState, bool) {
2350        let st = self.inner.interact.get();
2351        let mut vs = VisualState::default();
2352        vs.hovered = st.hovered == Some(el);
2353        vs.pressed = st.pressed == Some(el);
2354        vs.focused = st.focus == Some(el);
2355        (vs, st.hovered == Some(el))
2356    }
2357
2358    pub fn visual_state(&self, el: ElementId) -> VisualState {
2359        self.view_state(el).0
2360    }
2361
2362    fn call_on_event(
2363        &self,
2364        el: ElementId,
2365        event: &Event,
2366        phase: EventPhase,
2367        pos: Point,
2368        cells: &DispatchCells,
2369        text: &mut TextSystem,
2370    ) {
2371        let Some(v) = self.view_rc(el) else { return };
2372        let (state, hover_self) = self.view_state(el);
2373        let rect = {
2374            let arena = self.inner.arena.borrow();
2375            arena.get(el).map(|e| e.rect.get()).unwrap_or(Rect::ZERO)
2376        };
2377
2378        let env = self.inner.environment.borrow().clone();
2379        let mut ctx = EventCtx {
2380            phase,
2381            element: el,
2382            state,
2383            hover_self,
2384            pos,
2385            rect,
2386            environment: &env,
2387            window: self.inner.window.get(),
2388            stopped: &cells.stopped,
2389            handled: &cells.handled,
2390            capture_req: &cells.capture_req,
2391            release_req: &cells.release_req,
2392            focus_req: &cells.focus_req,
2393            text,
2394            runtime: self,
2395        };
2396
2397        let type_name = v.type_name().to_string();
2398        if std::panic::catch_unwind(AssertUnwindSafe(|| {
2399            v.on_event_erased(event, &mut ctx);
2400        }))
2401        .is_err()
2402        {
2403            let msg = format!("on_event panicked ({phase:?})");
2404            self.record_error(el, type_name, msg);
2405        }
2406    }
2407
2408    fn dispatch_chain(&self, target: ElementId, event: &Event, pos: Point, text: &mut TextSystem) {
2409        let mut path = vec![target];
2410        while let Some(last) = path.last() {
2411            let Some(p) = self.parent_of(*last) else {
2412                break;
2413            };
2414            path.push(p);
2415        }
2416        path.reverse();
2417        let n = path.len();
2418        let cells = DispatchCells::new();
2419
2420        for &el in &path[..n - 1] {
2421            if cells.stopped.get() || cells.handled.get() {
2422                self.apply_dispatch_requests(target, &cells);
2423                return;
2424            }
2425            self.call_on_event(el, event, EventPhase::Capture, pos, &cells, text);
2426        }
2427        if !(cells.stopped.get() || cells.handled.get()) {
2428            self.call_on_event(target, event, EventPhase::Target, pos, &cells, text);
2429        }
2430        for &el in path[..n - 1].iter().rev() {
2431            if cells.stopped.get() || cells.handled.get() {
2432                break;
2433            }
2434            self.call_on_event(el, event, EventPhase::Bubble, pos, &cells, text);
2435        }
2436        self.apply_dispatch_requests(target, &cells);
2437    }
2438
2439    fn dispatch_single(
2440        &self,
2441        el: ElementId,
2442        event: &Event,
2443        pos: Point,
2444        text: &mut TextSystem,
2445    ) -> bool {
2446        let cells = DispatchCells::new();
2447        self.call_on_event(el, event, EventPhase::Target, pos, &cells, text);
2448        self.apply_dispatch_requests(el, &cells);
2449        cells.handled.get() || cells.stopped.get()
2450    }
2451
2452    fn apply_dispatch_requests(&self, el: ElementId, cells: &DispatchCells) {
2453        let mut st = self.inner.interact.get();
2454        if cells.release_req.get() {
2455            if st.captured == Some(el) {
2456                st.captured = None;
2457            }
2458        } else if let Some(req) = cells.capture_req.get() {
2459            st.captured = Some(req);
2460        }
2461        if cells.focus_req.get() {
2462            st.focus = Some(el);
2463        }
2464        self.inner.interact.set(st);
2465    }
2466
2467    pub fn pointer_cancel(&self, text: &mut TextSystem) {
2468        let captured = self.inner.interact.get().captured;
2469        if let Some(c) = captured {
2470            let pos = self.inner.interact.get().cursor;
2471            let ev = Event::Pointer(PointerEvent::Cancel);
2472            let _ = self.dispatch_single(c, &ev, pos, text);
2473        }
2474        self.inner.pending_drag.borrow_mut().take();
2475        if self.inner.active_drag.borrow().is_some() {
2476            self.cancel_drag(text);
2477            return;
2478        }
2479        let mut st = self.inner.interact.get();
2480        st.pressed = None;
2481        st.captured = None;
2482        st.hovered = None;
2483        self.inner.interact.set(st);
2484        self.request_redraw();
2485    }
2486
2487    pub(crate) fn measure_node(
2488        &self,
2489        el: ElementId,
2490        constraints: Constraints,
2491        ctx: &mut LayoutCtx,
2492    ) -> Size {
2493        self.measure(el, constraints, ctx)
2494    }
2495
2496    pub(crate) fn arrange_node(&self, el: ElementId, rect: Rect, ctx: &mut LayoutCtx) {
2497        self.arrange(el, rect, ctx)
2498    }
2499
2500    pub(crate) fn layout_epoch(&self) -> u64 {
2501        self.inner.layout_epoch.get()
2502    }
2503
2504    pub(crate) fn translate_subtree(&self, root: ElementId, delta: Vec2) {
2505        if delta == Vec2::ZERO {
2506            return;
2507        }
2508        let mut stack = vec![root];
2509        while let Some(id) = stack.pop() {
2510            let children = {
2511                let mut arena = self.inner.arena.borrow_mut();
2512                let Some(e) = arena.get_mut(id) else {
2513                    continue;
2514                };
2515                e.rect.set(e.rect.get().translate(delta));
2516                e.children.clone()
2517            };
2518            stack.extend(children);
2519        }
2520    }
2521
2522    pub(crate) fn child_layout_style(&self, el: ElementId) -> LayoutStyle {
2523        self.view_rc(el)
2524            .map(|v| v.layout_style_ctx_erased(self, el))
2525            .unwrap_or_default()
2526    }
2527
2528    pub fn check_root_fill(&self, root: ElementId, bounds: Rect, measured: Size) -> bool {
2529        let style = self.child_layout_style(root);
2530        let fill_w = style.width.is_fill();
2531        let fill_h = style.height.is_fill();
2532        if fill_w && fill_h {
2533            return true;
2534        }
2535        let gap_w = bounds.size.width - measured.width;
2536        let gap_h = bounds.size.height - measured.height;
2537
2538        if gap_w <= 1.0 && gap_h <= 1.0 {
2539            return true;
2540        }
2541        let type_name = self.type_name_of(root).unwrap_or_else(|| "?".into());
2542        log::warn!(
2543            "sequoia-view: root `{type_name}` has no fill_size (width_fill={fill_w}, height_fill={fill_h}); \
2544             measured {measured:?} with window {:?} — unpainted background. Add `.fill_size()` to the root.",
2545            bounds.size
2546        );
2547        false
2548    }
2549
2550    fn warn_if_root_not_fill(&self, root: ElementId, bounds: Rect, measured: Size) {
2551        let _ = self.check_root_fill(root, bounds, measured);
2552    }
2553
2554    fn measure(&self, el: ElementId, constraints: Constraints, ctx: &mut LayoutCtx) -> Size {
2555        match std::panic::catch_unwind(AssertUnwindSafe(|| {
2556            self.measure_inner(el, constraints, ctx)
2557        })) {
2558            Ok(s) => s,
2559            Err(_) => {
2560                let type_name = self.type_name_of(el).unwrap_or_else(|| "?".into());
2561                self.record_error(el, type_name, "measure panicked".to_string());
2562                Size::ZERO
2563            }
2564        }
2565    }
2566
2567    fn measure_inner(&self, el: ElementId, constraints: Constraints, ctx: &mut LayoutCtx) -> Size {
2568        {
2569            let arena = self.inner.arena.borrow();
2570            if arena.get(el).map(|e| e.dirty.is_empty()).unwrap_or(false) {
2571                drop(arena);
2572                let measures = self.inner.measures.borrow();
2573                if let Some(e) = measures.get(&el)
2574                    && e.constraints == constraints
2575                {
2576                    return e.size;
2577                }
2578            }
2579        }
2580        let Some(v) = self.view_rc(el) else {
2581            return Size::ZERO;
2582        };
2583        let style = v.layout_style_ctx_erased(self, el);
2584        let container = v.container();
2585        let grid = v.grid();
2586
2587        let dock = if grid.is_some() { None } else { v.dock() };
2588
2589        let wrap = if grid.is_some() || dock.is_some() {
2590            None
2591        } else {
2592            v.wrap()
2593        };
2594
2595        let masonry = if grid.is_some() || dock.is_some() || wrap.is_some() {
2596            None
2597        } else {
2598            v.masonry()
2599        };
2600
2601        let flex = v.flex_weights().unwrap_or_default();
2602        let pre_placements = v.grid_placements().unwrap_or_default();
2603        let children_len = self.children_len(el);
2604
2605        {
2606            let measures = self.inner.measures.borrow();
2607            if let Some(e) = measures.get(&el)
2608                && e.constraints == constraints
2609                && e.style == style
2610                && e.container == container
2611                && e.grid == grid
2612                && e.placements == pre_placements
2613                && e.dock == dock
2614                && e.wrap == wrap
2615                && e.masonry == masonry
2616                && e.children_len == children_len
2617            {
2618                return e.size;
2619            }
2620        }
2621
2622        let children = self.children(el);
2623
2624        let intrinsic = v.intrinsic_size(ctx);
2625        let avail = constraints.max;
2626        let mut w = match style.width {
2627            Length::Px(p) => p,
2628            Length::Percent(p) => {
2629                if avail.width.is_finite() && p.is_finite() {
2630                    (avail.width * p / 100.0).max(0.0)
2631                } else {
2632                    intrinsic.map(|i| i.width).unwrap_or(0.0)
2633                }
2634            }
2635            Length::Fill(_) => {
2636                if avail.width.is_finite() {
2637                    avail.width
2638                } else {
2639                    intrinsic.map(|i| i.width).unwrap_or(0.0)
2640                }
2641            }
2642            Length::Fit => intrinsic.map(|i| i.width).unwrap_or(0.0),
2643        };
2644        let mut h = match style.height {
2645            Length::Px(p) => p,
2646            Length::Percent(p) => {
2647                if avail.height.is_finite() && p.is_finite() {
2648                    (avail.height * p / 100.0).max(0.0)
2649                } else {
2650                    intrinsic.map(|i| i.height).unwrap_or(0.0)
2651                }
2652            }
2653            Length::Fill(_) => {
2654                if avail.height.is_finite() {
2655                    avail.height
2656                } else {
2657                    intrinsic.map(|i| i.height).unwrap_or(0.0)
2658                }
2659            }
2660            Length::Fit => intrinsic.map(|i| i.height).unwrap_or(0.0),
2661        };
2662
2663        let content_width = matches!(style.width, Length::Fit)
2664            || (style.width.is_fill() && !avail.width.is_finite());
2665        let content_height = matches!(style.height, Length::Fit)
2666            || (style.height.is_fill() && !avail.height.is_finite());
2667
2668        let apply_aspect = |mut w: f32, mut h: f32| -> (f32, f32) {
2669            if let Some(r) = style.aspect_ratio
2670                && r.is_finite()
2671                && r > 0.0
2672            {
2673                match (content_width, content_height) {
2674                    (true, false) => w = (h * r).max(0.0),
2675                    (false, true) => h = (w / r).max(0.0),
2676                    (true, true) => {
2677                        if w <= 0.0 && h > 0.0 {
2678                            w = (h * r).max(0.0);
2679                        } else if h <= 0.0 && w > 0.0 {
2680                            h = (w / r).max(0.0);
2681                        }
2682                    }
2683                    (false, false) => {}
2684                }
2685            }
2686            (w, h)
2687        };
2688
2689        let eff_container: Option<ContainerStyle> =
2690            if grid.is_some() || dock.is_some() || wrap.is_some() || masonry.is_some() {
2691                None
2692            } else {
2693                container.or_else(|| {
2694                    if children.is_empty()
2695                        || intrinsic.is_some()
2696                        || (!content_width && !content_height)
2697                    {
2698                        None
2699                    } else {
2700                        Some(ContainerStyle::new(Axis::Vertical, 0.0))
2701                    }
2702                })
2703            };
2704
2705        if let Some(cstyle) = eff_container
2706            && !children.is_empty()
2707            && !(intrinsic.is_some() && !content_width && !content_height)
2708            && !(v.contain_size_erased() && !content_width && !content_height)
2709        {
2710            let pad = style.padding;
2711            let inner_avail = Size::new(
2712                ((if content_width { avail.width } else { w }) - pad.horizontal_sum()).max(0.0),
2713                ((if content_height { avail.height } else { h }) - pad.vertical_sum()).max(0.0),
2714            );
2715            let (total_main, cross_max) = self.measure_children(
2716                &children,
2717                &cstyle,
2718                inner_avail,
2719                &flex,
2720                ctx,
2721                match cstyle.axis {
2722                    Axis::Horizontal => content_height,
2723                    Axis::Vertical => content_width,
2724                },
2725            );
2726            let (mw, mh) = match cstyle.axis {
2727                Axis::Horizontal => (total_main, cross_max),
2728                Axis::Vertical => (cross_max, total_main),
2729            };
2730            if content_width && intrinsic.is_none() {
2731                w = mw + pad.horizontal_sum();
2732            }
2733            if content_height && intrinsic.is_none() {
2734                h = mh + pad.vertical_sum();
2735            }
2736        }
2737
2738        let placements = v.grid_placements().unwrap_or_default();
2739        if let Some(ref gstyle) = grid
2740            && !children.is_empty()
2741            && !(intrinsic.is_some() && !content_width && !content_height)
2742            && !(v.contain_size_erased() && !content_width && !content_height)
2743        {
2744            let pad = style.padding;
2745            let inner_avail = Size::new(
2746                ((if content_width { avail.width } else { w }) - pad.horizontal_sum()).max(0.0),
2747                ((if content_height { avail.height } else { h }) - pad.vertical_sum()).max(0.0),
2748            );
2749            let (gw, gh) = self.measure_grid(&children, gstyle, &placements, inner_avail, ctx);
2750            if content_width && intrinsic.is_none() {
2751                w = gw + pad.horizontal_sum();
2752            }
2753            if content_height && intrinsic.is_none() {
2754                h = gh + pad.vertical_sum();
2755            }
2756        }
2757
2758        if let Some(ref dstyle) = dock
2759            && !children.is_empty()
2760            && !(intrinsic.is_some() && !content_width && !content_height)
2761            && !(v.contain_size_erased() && !content_width && !content_height)
2762        {
2763            let pad = style.padding;
2764            let inner_avail = Size::new(
2765                ((if content_width { avail.width } else { w }) - pad.horizontal_sum()).max(0.0),
2766                ((if content_height { avail.height } else { h }) - pad.vertical_sum()).max(0.0),
2767            );
2768            let (dw, dh) = self.measure_dock(&children, dstyle, inner_avail, ctx);
2769            if content_width && intrinsic.is_none() {
2770                w = dw + pad.horizontal_sum();
2771            }
2772            if content_height && intrinsic.is_none() {
2773                h = dh + pad.vertical_sum();
2774            }
2775        }
2776
2777        if let Some(wstyle) = wrap
2778            && !children.is_empty()
2779            && !(intrinsic.is_some() && !content_width && !content_height)
2780            && !(v.contain_size_erased() && !content_width && !content_height)
2781        {
2782            let pad = style.padding;
2783            let inner_avail = Size::new(
2784                ((if content_width { avail.width } else { w }) - pad.horizontal_sum()).max(0.0),
2785                ((if content_height { avail.height } else { h }) - pad.vertical_sum()).max(0.0),
2786            );
2787            let (ww, wh) = self.measure_wrap(&children, &wstyle, inner_avail, ctx);
2788            if content_width && intrinsic.is_none() {
2789                w = ww + pad.horizontal_sum();
2790            }
2791            if content_height && intrinsic.is_none() {
2792                h = wh + pad.vertical_sum();
2793            }
2794        }
2795
2796        if let Some(mstyle) = masonry
2797            && !children.is_empty()
2798            && !(intrinsic.is_some() && !content_width && !content_height)
2799            && !(v.contain_size_erased() && !content_width && !content_height)
2800        {
2801            let pad = style.padding;
2802            let inner_avail = Size::new(
2803                ((if content_width { avail.width } else { w }) - pad.horizontal_sum()).max(0.0),
2804                ((if content_height { avail.height } else { h }) - pad.vertical_sum()).max(0.0),
2805            );
2806            let (mw, mh) =
2807                self.measure_masonry(&children, &mstyle, inner_avail, content_width, ctx);
2808            if content_width && intrinsic.is_none() {
2809                w = mw + pad.horizontal_sum();
2810            }
2811            if content_height && intrinsic.is_none() {
2812                h = mh + pad.vertical_sum();
2813            }
2814        }
2815
2816        let (w, h) = apply_aspect(w, h);
2817        let size = clamp_to(style.clamp_size(Size::new(w, h)), constraints);
2818        self.inner.measures.borrow_mut().insert(
2819            el,
2820            MeasureEntry {
2821                constraints,
2822                style,
2823                container,
2824                grid,
2825                placements,
2826                dock,
2827                wrap,
2828                masonry,
2829                children_len: children.len(),
2830                size,
2831            },
2832        );
2833        size
2834    }
2835
2836    fn measure_children(
2837        &self,
2838        children: &[ElementId],
2839        cstyle: &ContainerStyle,
2840        inner_avail: Size,
2841        flex: &[f32],
2842        ctx: &mut LayoutCtx,
2843        need_cross: bool,
2844    ) -> (f32, f32) {
2845        let flow: Vec<(usize, ElementId)> = children
2846            .iter()
2847            .copied()
2848            .enumerate()
2849            .filter(|(_, c)| !self.child_layout_style(*c).is_absolute())
2850            .collect();
2851        if flow.is_empty() {
2852            return (0.0, 0.0);
2853        }
2854
2855        if cstyle.wrap {
2856            return self.measure_children_wrapped(
2857                &flow,
2858                cstyle,
2859                inner_avail,
2860                flex,
2861                ctx,
2862                need_cross,
2863            );
2864        }
2865        let (avail_main, avail_cross) = match cstyle.axis {
2866            Axis::Horizontal => (inner_avail.width, inner_avail.height),
2867            Axis::Vertical => (inner_avail.height, inner_avail.width),
2868        };
2869
2870        let mut bases = Vec::with_capacity(flow.len());
2871        let mut crosses = if need_cross {
2872            Vec::with_capacity(flow.len())
2873        } else {
2874            Vec::new()
2875        };
2876        let mut items = Vec::with_capacity(flow.len());
2877        let mut child_styles = Vec::with_capacity(flow.len());
2878
2879        for &(orig_i, child) in &flow {
2880            let extra = flex.get(orig_i).copied().unwrap_or(0.0);
2881            let (base, grow, shrink, cross, mn, mx) =
2882                self.child_layout_info_full(child, cstyle, avail_main, avail_cross, extra, ctx);
2883            bases.push(base);
2884            if need_cross {
2885                crosses.push(cross);
2886            }
2887            items.push(FlexItem::new(base, grow, shrink, mn, mx));
2888            child_styles.push(self.child_layout_style(child));
2889        }
2890        let gaps = Self::indents_main_total(&child_styles, cstyle.axis, cstyle.gap);
2891
2892        {
2893            let mut sizes = bases.clone();
2894            distribute_flex(&items, &mut sizes, gaps, avail_main);
2895            bases = sizes;
2896        }
2897
2898        if need_cross && avail_main.is_finite() {
2899            for (k, &(_, child)) in flow.iter().enumerate() {
2900                if self.subtree_has_wrap_axis(child, cstyle.axis) {
2901                    crosses[k] =
2902                        self.constrained_cross(child, cstyle.axis, bases[k], avail_cross, ctx);
2903                }
2904            }
2905        }
2906
2907        let total_main = bases.iter().sum::<f32>() + gaps;
2908        let cross_max = if need_cross {
2909            crosses.iter().cloned().fold(0.0_f32, f32::max)
2910        } else {
2911            0.0
2912        };
2913        (total_main, cross_max)
2914    }
2915
2916    fn measure_children_wrapped(
2917        &self,
2918        flow: &[(usize, ElementId)],
2919        cstyle: &ContainerStyle,
2920        inner_avail: Size,
2921        flex: &[f32],
2922        ctx: &mut LayoutCtx,
2923        need_cross: bool,
2924    ) -> (f32, f32) {
2925        let (avail_main, avail_cross) = match cstyle.axis {
2926            Axis::Horizontal => (inner_avail.width, inner_avail.height),
2927            Axis::Vertical => (inner_avail.height, inner_avail.width),
2928        };
2929
2930        let mut mains = Vec::with_capacity(flow.len());
2931        let mut crosses = Vec::with_capacity(flow.len());
2932        let mut grows = Vec::with_capacity(flow.len());
2933        let mut shrinks = Vec::with_capacity(flow.len());
2934        let mut mins = Vec::with_capacity(flow.len());
2935        let mut maxs = Vec::with_capacity(flow.len());
2936        for &(orig_i, child) in flow {
2937            let extra = flex.get(orig_i).copied().unwrap_or(0.0);
2938            let (base, grow, shrink, cross, mn, mx) =
2939                self.child_layout_info_full(child, cstyle, avail_main, avail_cross, extra, ctx);
2940            mains.push(base);
2941            crosses.push(cross);
2942            grows.push(grow);
2943            shrinks.push(shrink);
2944            mins.push(mn);
2945            maxs.push(mx);
2946        }
2947        let lines = wrap_lines(&mains, avail_main, cstyle.gap);
2948        if lines.is_empty() {
2949            return (0.0, 0.0);
2950        }
2951        let mut main_max = 0.0f32;
2952        let mut cross_total = 0.0f32;
2953        for (li, &(s, e)) in lines.iter().enumerate() {
2954            let mut line_sizes = mains[s..e].to_vec();
2955            let items: Vec<FlexItem> = (s..e)
2956                .map(|i| FlexItem::new(mains[i], grows[i], shrinks[i], mins[i], maxs[i]))
2957                .collect();
2958            let line_gaps = cstyle.main_gaps_total(e - s);
2959            distribute_flex(&items, &mut line_sizes, line_gaps, avail_main);
2960            let used: f32 = line_sizes.iter().sum::<f32>() + line_gaps;
2961            main_max = main_max.max(used);
2962            if need_cross {
2963                let line_cross = crosses[s..e].iter().cloned().fold(0.0f32, f32::max);
2964                if li > 0 {
2965                    cross_total += cstyle.cross_gap;
2966                }
2967                cross_total += line_cross;
2968            }
2969        }
2970
2971        (main_max.max(0.0), cross_total.max(0.0))
2972    }
2973
2974    fn subtree_has_wrap_axis(&self, root: ElementId, axis: Axis) -> bool {
2975        let axis_u8 = match axis {
2976            Axis::Horizontal => 0,
2977            Axis::Vertical => 1,
2978        };
2979        let key = (root.index, root.generation, axis_u8);
2980        if let Some(hit) = self.inner.wrap_cache.borrow().get(&key).copied() {
2981            return hit;
2982        }
2983        let mut stack = vec![root];
2984        let mut found = false;
2985        while let Some(id) = stack.pop() {
2986            if let Some(v) = self.view_rc(id) {
2987                let grid = v.grid();
2988                let dock = if grid.is_some() { None } else { v.dock() };
2989                let wrap = if grid.is_some() || dock.is_some() {
2990                    None
2991                } else {
2992                    v.wrap()
2993                };
2994                if let Some(w) = wrap
2995                    && w.axis == axis
2996                {
2997                    found = true;
2998                    break;
2999                }
3000                if axis == Axis::Horizontal
3001                    && wrap.is_none()
3002                    && dock.is_none()
3003                    && grid.is_none()
3004                    && v.masonry().is_some()
3005                {
3006                    found = true;
3007                    break;
3008                }
3009            }
3010            {
3011                let arena = self.inner.arena.borrow();
3012                if let Some(e) = arena.get(id) {
3013                    stack.extend(e.children.iter().copied());
3014                }
3015            }
3016        }
3017        self.inner.wrap_cache.borrow_mut().insert(key, found);
3018        found
3019    }
3020
3021    fn subtree_redistributes(&self, root: ElementId) -> bool {
3022        let key = (root.index, root.generation, 2u8);
3023        if let Some(hit) = self.inner.wrap_cache.borrow().get(&key).copied() {
3024            return hit;
3025        }
3026        let mut found = false;
3027        let mut stack = vec![root];
3028        while let Some(id) = stack.pop() {
3029            let Some(v) = self.view_rc(id) else {
3030                continue;
3031            };
3032            if v.flex_weights().is_some_and(|w| w.iter().any(|f| *f > 0.0)) {
3033                found = true;
3034                break;
3035            }
3036            let cs = v.layout_style_ctx_erased(self, id);
3037            if cs.flex_weight() > 0.0
3038                || matches!(cs.width, Length::Fill(_))
3039                || matches!(cs.height, Length::Fill(_))
3040            {
3041                found = true;
3042                break;
3043            }
3044            {
3045                let arena = self.inner.arena.borrow();
3046                if let Some(e) = arena.get(id) {
3047                    stack.extend(e.children.iter().copied());
3048                }
3049            }
3050        }
3051        self.inner.wrap_cache.borrow_mut().insert(key, found);
3052        found
3053    }
3054
3055    fn constrained_cross(
3056        &self,
3057        child: ElementId,
3058        container_axis: Axis,
3059        allocated_main: f32,
3060        avail_cross: f32,
3061        ctx: &mut LayoutCtx,
3062    ) -> f32 {
3063        let Some(v) = self.view_rc(child) else {
3064            return 0.0;
3065        };
3066        let cs = v.layout_style_ctx_erased(self, child);
3067
3068        let margin_main = cs.margin_main(container_axis);
3069        let allocated = (allocated_main - margin_main).max(0.0);
3070        let max = match container_axis {
3071            Axis::Horizontal => Size::new(allocated, avail_cross),
3072            Axis::Vertical => Size::new(avail_cross, allocated),
3073        };
3074        let m = self.measure(
3075            child,
3076            Constraints {
3077                min: Size::ZERO,
3078                max,
3079            },
3080            ctx,
3081        );
3082        let cross_margin = cs.margin_cross(container_axis);
3083        match container_axis {
3084            Axis::Horizontal => {
3085                cs.clamp_height(resolve_cross(cs.height, m.height, avail_cross))
3086                    .max(0.0)
3087                    + cross_margin
3088            }
3089            Axis::Vertical => {
3090                cs.clamp_width(resolve_cross(cs.width, m.width, avail_cross))
3091                    .max(0.0)
3092                    + cross_margin
3093            }
3094        }
3095    }
3096
3097    fn child_limits_full(&self, child: ElementId, axis: Axis) -> (f32, f32, f32, f32, f32) {
3098        match self.view_rc(child) {
3099            Some(v) => {
3100                let cs = v.layout_style_ctx_erased(self, child);
3101                let margin_main = cs.margin_main(axis);
3102                let margin_cross = cs.margin_cross(axis);
3103                let px_floor_main = match axis {
3104                    Axis::Horizontal => match cs.width {
3105                        Length::Px(w) => w.max(0.0),
3106                        _ => 0.0,
3107                    },
3108                    Axis::Vertical => match cs.height {
3109                        Length::Px(h) => h.max(0.0),
3110                        _ => 0.0,
3111                    },
3112                };
3113                let px_floor_cross = match axis {
3114                    Axis::Horizontal => match cs.height {
3115                        Length::Px(h) => h.max(0.0),
3116                        _ => 0.0,
3117                    },
3118                    Axis::Vertical => match cs.width {
3119                        Length::Px(w) => w.max(0.0),
3120                        _ => 0.0,
3121                    },
3122                };
3123                let (min_c, max_c) = match axis {
3124                    Axis::Horizontal => (cs.min_height, cs.max_height),
3125                    Axis::Vertical => (cs.min_width, cs.max_width),
3126                };
3127                let min_c = {
3128                    let m = if min_c.is_finite() {
3129                        min_c.max(0.0)
3130                    } else {
3131                        0.0
3132                    };
3133                    m.max(px_floor_cross)
3134                };
3135                let max_c = if max_c.is_nan() {
3136                    f32::INFINITY
3137                } else if max_c.is_infinite() {
3138                    if max_c > 0.0 { f32::INFINITY } else { 0.0 }
3139                } else {
3140                    max_c.max(0.0)
3141                };
3142                (
3143                    cs.min_main(axis).max(px_floor_main) + margin_main,
3144                    cs.max_main(axis) + margin_main,
3145                    min_c + margin_cross,
3146                    max_c + margin_cross,
3147                    cs.flex_shrink_weight(),
3148                )
3149            }
3150            None => (0.0, f32::INFINITY, 0.0, f32::INFINITY, 1.0),
3151        }
3152    }
3153
3154    fn child_layout_info_full(
3155        &self,
3156        child: ElementId,
3157        cstyle: &ContainerStyle,
3158        avail_main: f32,
3159        avail_cross: f32,
3160        flex_extra: f32,
3161        ctx: &mut LayoutCtx,
3162    ) -> (f32, f32, f32, f32, f32, f32) {
3163        let Some(v) = self.view_rc(child) else {
3164            return (0.0, 0.0, 1.0, 0.0, 0.0, f32::INFINITY);
3165        };
3166        let cs = v.layout_style_ctx_erased(self, child);
3167        let (main_len, cross_len) = match cstyle.axis {
3168            Axis::Horizontal => (cs.width, cs.height),
3169            Axis::Vertical => (cs.height, cs.width),
3170        };
3171
3172        let (child_max_w, child_max_h) = match cstyle.axis {
3173            Axis::Horizontal => (f32::INFINITY, avail_cross),
3174            Axis::Vertical => (avail_cross, f32::INFINITY),
3175        };
3176        let m = self.measure(
3177            child,
3178            Constraints {
3179                min: Size::ZERO,
3180                max: Size::new(child_max_w, child_max_h),
3181            },
3182            ctx,
3183        );
3184        let (main_intrinsic, cross_intrinsic) = match cstyle.axis {
3185            Axis::Horizontal => (m.width, m.height),
3186            Axis::Vertical => (m.height, m.width),
3187        };
3188
3189        let (mut base, fill_grow) = resolve_main(main_len, main_intrinsic, avail_main);
3190
3191        let (basis_base, basis_grow) =
3192            resolve_flex_basis(cs.flex_basis, (base, fill_grow), main_intrinsic, avail_main);
3193        base = basis_base;
3194        let mut grow_basis = fill_grow.max(basis_grow);
3195        let mut cross = resolve_cross(cross_len, cross_intrinsic, avail_cross);
3196
3197        if matches!(cross_len, Length::Fit) && !self.subtree_redistributes(child) {
3198            let m_true = self.measure(
3199                child,
3200                Constraints {
3201                    min: Size::ZERO,
3202                    max: Size::new(f32::INFINITY, f32::INFINITY),
3203                },
3204                ctx,
3205            );
3206            let true_cross_intrinsic = match cstyle.axis {
3207                Axis::Horizontal => m_true.height,
3208                Axis::Vertical => m_true.width,
3209            };
3210            cross = cross.max(resolve_cross(cross_len, true_cross_intrinsic, avail_cross));
3211        }
3212
3213        let main_auto =
3214            matches!(main_len, Length::Fit) || (main_len.is_fill() && !avail_main.is_finite());
3215        let cross_auto =
3216            matches!(cross_len, Length::Fit) || (cross_len.is_fill() && !avail_cross.is_finite());
3217
3218        let (bw, bh) = match cstyle.axis {
3219            Axis::Horizontal => cs.apply_aspect(base, cross, main_auto, cross_auto),
3220            Axis::Vertical => {
3221                let (h, w) = (base, cross);
3222                let (w2, h2) = cs.apply_aspect(w, h, cross_auto, main_auto);
3223                (h2, w2)
3224            }
3225        };
3226        base = bw;
3227        cross = bh;
3228
3229        let extra = if flex_extra.is_finite() && flex_extra > 0.0 {
3230            flex_extra
3231        } else {
3232            0.0
3233        };
3234        let grow = grow_basis.max(cs.flex_weight()).max(extra);
3235        grow_basis = grow;
3236        let shrink = cs.flex_shrink_weight();
3237
3238        let (mut mn, mx, _, _, _) = self.child_limits_full(child, cstyle.axis);
3239        let margin_main = cs.margin_main(cstyle.axis);
3240        let margin_cross = cs.margin_cross(cstyle.axis);
3241        let (base, cross) = match cstyle.axis {
3242            Axis::Horizontal => (
3243                cs.clamp_width(base).max(0.0) + margin_main,
3244                cs.clamp_height(cross).max(0.0) + margin_cross,
3245            ),
3246            Axis::Vertical => (
3247                cs.clamp_height(base).max(0.0) + margin_main,
3248                cs.clamp_width(cross).max(0.0) + margin_cross,
3249            ),
3250        };
3251
3252        if grow <= 0.0 && matches!(main_len, Length::Fit) {
3253            mn = mn.max(base);
3254        }
3255        let _ = grow_basis;
3256        (base.max(0.0), grow, shrink, cross.max(0.0), mn, mx)
3257    }
3258
3259    fn child_grid_placement(&self, child: ElementId) -> GridPlacement {
3260        self.view_rc(child)
3261            .and_then(|v| v.grid_placement())
3262            .unwrap_or(GridPlacement::AUTO)
3263    }
3264
3265    fn grid_cells(
3266        &self,
3267        children: &[ElementId],
3268        grid: &GridStyle,
3269        placements: &[GridPlacement],
3270    ) -> Vec<(usize, usize, usize, usize)> {
3271        let ncols = grid.columns.len().max(1);
3272        let mut cells = Vec::with_capacity(children.len());
3273        let mut occupied: Vec<Vec<bool>> = Vec::new();
3274        let ensure_row = |occupied: &mut Vec<Vec<bool>>, r: usize| {
3275            while occupied.len() <= r {
3276                occupied.push(vec![false; ncols]);
3277            }
3278        };
3279        let mut cursor = (0usize, 0usize);
3280        for (idx, &child) in children.iter().enumerate() {
3281            if self.child_layout_style(child).is_absolute() {
3282                cells.push((0, 0, 0, 0));
3283                continue;
3284            }
3285            let p = placements
3286                .get(idx)
3287                .copied()
3288                .filter(|p| !p.is_auto())
3289                .or_else(|| Some(self.child_grid_placement(child)))
3290                .unwrap_or(GridPlacement::AUTO);
3291            let cs = p.col_span.max(1).min(ncols);
3292            let rs = p.row_span.max(1);
3293            if let (Some(c), Some(r)) = (p.column, p.row) {
3294                let c = c.min(ncols.saturating_sub(1));
3295                ensure_row(&mut occupied, r + rs - 1);
3296                for row in occupied.iter_mut().skip(r).take(rs) {
3297                    for cell in row.iter_mut().take((c + cs).min(ncols)).skip(c) {
3298                        *cell = true;
3299                    }
3300                }
3301                cells.push((c, r, cs, rs));
3302                continue;
3303            }
3304
3305            match grid.auto_flow {
3306                sequoia_layout::GridFlow::Row => {
3307                    let (mut c, mut r) = cursor;
3308                    loop {
3309                        ensure_row(&mut occupied, r + rs - 1);
3310
3311                        let mut placed: Option<usize> = None;
3312                        let start_c = if r == cursor.1 { c } else { 0 };
3313                        for cc in start_c..ncols {
3314                            if cc + cs > ncols {
3315                                break;
3316                            }
3317                            let free =
3318                                (cc..cc + cs).all(|x| (r..r + rs).all(|rr| !occupied[rr][x]));
3319                            if free {
3320                                placed = Some(cc);
3321                                break;
3322                            }
3323                        }
3324                        if let Some(cc) = placed {
3325                            for row in occupied.iter_mut().skip(r).take(rs) {
3326                                for cell in row.iter_mut().skip(cc).take(cs) {
3327                                    *cell = true;
3328                                }
3329                            }
3330                            cells.push((cc, r, cs, rs));
3331                            cursor = (cc + cs, r);
3332                            if cursor.0 >= ncols {
3333                                cursor = (0, r + 1);
3334                            }
3335                            break;
3336                        }
3337
3338                        c = 0;
3339                        r += 1;
3340                    }
3341                }
3342                sequoia_layout::GridFlow::Column => {
3343                    let idx = cells.iter().filter(|(_, _, cs, _)| *cs > 0).count();
3344                    let nrows_est = children.len().div_ceil(ncols).max(1);
3345                    let col = (idx / nrows_est) % ncols;
3346                    let row = idx % nrows_est;
3347                    ensure_row(&mut occupied, row + rs - 1);
3348                    cells.push((col.min(ncols - 1), row, cs, rs));
3349                }
3350            }
3351        }
3352        cells
3353    }
3354
3355    fn track_cap(&self, track: Option<&Track>, avail: f32) -> f32 {
3356        match track {
3357            Some(Track::Px(v)) => v.max(0.0),
3358            Some(Track::Percent(p)) if avail.is_finite() && p.is_finite() => {
3359                (avail * p / 100.0).max(0.0)
3360            }
3361            Some(Track::MinMax { max, .. }) => self.track_cap(Some(max), avail),
3362            _ => f32::INFINITY,
3363        }
3364    }
3365
3366    fn grid_content_sizes(
3367        &self,
3368        children: &[ElementId],
3369        grid: &GridStyle,
3370        placements: &[GridPlacement],
3371        inner_avail: Size,
3372        ctx: &mut LayoutCtx,
3373    ) -> (Vec<f32>, Vec<f32>, Vec<(usize, usize, usize, usize)>, usize) {
3374        let ncols = grid.columns.len().max(1);
3375        let cells = self.grid_cells(children, grid, placements);
3376        let nrows_needed = cells
3377            .iter()
3378            .map(|(_, r, _, rs)| if *rs > 0 { r + rs } else { 0 })
3379            .max()
3380            .unwrap_or(0)
3381            .max(grid.rows.len())
3382            .max(1);
3383
3384        let mut content_col = vec![0.0_f32; ncols];
3385        let mut content_row = vec![0.0_f32; nrows_needed];
3386
3387        let tracks_fixed = grid.columns.iter().all(|t| {
3388            matches!(
3389                t,
3390                sequoia_layout::Track::Px(_)
3391                    | sequoia_layout::Track::Percent(_)
3392                    | sequoia_layout::Track::Fill(_)
3393            )
3394        }) && grid.rows.len() >= nrows_needed
3395            && grid.rows.iter().all(|t| {
3396                matches!(
3397                    t,
3398                    sequoia_layout::Track::Px(_)
3399                        | sequoia_layout::Track::Percent(_)
3400                        | sequoia_layout::Track::Fill(_)
3401                )
3402            });
3403        if tracks_fixed && inner_avail.width.is_finite() && inner_avail.height.is_finite() {
3404            return (content_col, content_row, cells, nrows_needed);
3405        }
3406
3407        for (i, &child) in children.iter().enumerate() {
3408            let (col, row, cs, _rs) = cells[i];
3409            if cs == 0 {
3410                continue;
3411            }
3412            let col_fixed = matches!(
3413                grid.columns.get(col),
3414                Some(sequoia_layout::Track::Px(_)) | Some(sequoia_layout::Track::Percent(_))
3415            );
3416            let row_fixed = matches!(
3417                grid.rows.get(row),
3418                Some(sequoia_layout::Track::Px(_)) | Some(sequoia_layout::Track::Percent(_))
3419            );
3420            if col_fixed && row_fixed {
3421                continue;
3422            }
3423            let col_max = self.track_cap(grid.columns.get(col), inner_avail.width);
3424            let m = self.measure(
3425                child,
3426                Constraints {
3427                    min: Size::ZERO,
3428                    max: Size::new(col_max, f32::INFINITY),
3429                },
3430                ctx,
3431            );
3432
3433            let style = self.child_layout_style(child);
3434            let outer = style.outer_size(m);
3435            content_col[col] = content_col[col].max(outer.width);
3436            if row < content_row.len() {
3437                content_row[row] = content_row[row].max(outer.height);
3438            }
3439        }
3440
3441        (content_col, content_row, cells, nrows_needed)
3442    }
3443
3444    fn grid_rows(grid: &GridStyle, nrows_needed: usize) -> Vec<Track> {
3445        let mut rows: Vec<Track> = grid.rows.clone();
3446        rows.resize(nrows_needed, Track::Fit);
3447        rows
3448    }
3449
3450    fn measure_grid(
3451        &self,
3452        children: &[ElementId],
3453        grid: &GridStyle,
3454        placements: &[GridPlacement],
3455        inner_avail: Size,
3456        ctx: &mut LayoutCtx,
3457    ) -> (f32, f32) {
3458        let (content_col, content_row, _cells, nrows_needed) =
3459            self.grid_content_sizes(children, grid, placements, inner_avail, ctx);
3460        let widths = resolve_tracks(
3461            &grid.columns,
3462            &content_col,
3463            inner_avail.width,
3464            grid.column_gap,
3465        );
3466        let rows = Self::grid_rows(grid, nrows_needed);
3467        let heights = resolve_tracks(&rows, &content_row, inner_avail.height, grid.row_gap);
3468
3469        let gaps_x = grid.column_gap * widths.len().saturating_sub(1) as f32;
3470        let gaps_y = grid.row_gap * heights.len().saturating_sub(1) as f32;
3471        (
3472            widths.iter().sum::<f32>() + gaps_x,
3473            heights.iter().sum::<f32>() + gaps_y,
3474        )
3475    }
3476
3477    fn measure_wrap(
3478        &self,
3479        children: &[ElementId],
3480        wstyle: &WrapStyle,
3481        inner_avail: Size,
3482        ctx: &mut LayoutCtx,
3483    ) -> (f32, f32) {
3484        let avail_main = match wstyle.axis {
3485            Axis::Horizontal => inner_avail.width,
3486            Axis::Vertical => inner_avail.height,
3487        };
3488        let (mains, crosses, _) = self.wrap_child_sizes(children, wstyle, ctx);
3489        let lines = wrap_lines(&mains, avail_main, wstyle.main_gap());
3490
3491        let mut main_max = 0.0_f32;
3492        let mut cross_total = 0.0_f32;
3493        for (line_idx, &(start, end)) in lines.iter().enumerate() {
3494            let mut used = 0.0_f32;
3495            let mut line_cross = 0.0_f32;
3496            for i in start..end {
3497                if i > start {
3498                    used += wstyle.main_gap();
3499                }
3500                used += mains[i];
3501                line_cross = line_cross.max(crosses[i]);
3502            }
3503            main_max = main_max.max(used);
3504            if line_idx > 0 {
3505                cross_total += wstyle.cross_gap();
3506            }
3507            cross_total += line_cross;
3508        }
3509
3510        match wstyle.axis {
3511            Axis::Horizontal => (main_max, cross_total),
3512            Axis::Vertical => (cross_total, main_max),
3513        }
3514    }
3515
3516    fn measure_masonry(
3517        &self,
3518        children: &[ElementId],
3519        mstyle: &MasonryStyle,
3520        inner_avail: Size,
3521        content_width: bool,
3522        ctx: &mut LayoutCtx,
3523    ) -> (f32, f32) {
3524        let flow: Vec<ElementId> = children
3525            .iter()
3526            .copied()
3527            .filter(|c| !self.child_layout_style(*c).is_absolute())
3528            .collect();
3529        if flow.is_empty() {
3530            return (0.0, 0.0);
3531        }
3532
3533        let columns = mstyle.columns.max(1);
3534        let column_gap = sanitize_gap(mstyle.column_gap);
3535        let row_gap = sanitize_gap(mstyle.row_gap);
3536
3537        let column_width = if content_width || !inner_avail.width.is_finite() {
3538            flow.iter()
3539                .map(|&child| {
3540                    let m = self.measure(
3541                        child,
3542                        Constraints {
3543                            min: Size::ZERO,
3544                            max: Size::new(f32::INFINITY, f32::INFINITY),
3545                        },
3546                        ctx,
3547                    );
3548                    let cs = self.child_layout_style(child);
3549                    (m.width + cs.margin.horizontal_sum()).max(0.0)
3550                })
3551                .fold(0.0_f32, f32::max)
3552        } else {
3553            masonry_column_width(inner_avail.width, columns, column_gap)
3554        };
3555
3556        let mut heights = Vec::with_capacity(flow.len());
3557        for child in flow {
3558            let cs = self.child_layout_style(child);
3559            let max_width = (column_width - cs.margin.horizontal_sum()).max(0.0);
3560            let m = self.measure(
3561                child,
3562                Constraints {
3563                    min: Size::ZERO,
3564                    max: Size::new(max_width, f32::INFINITY),
3565                },
3566                ctx,
3567            );
3568            heights.push((m.height + cs.margin.vertical_sum()).max(0.0));
3569        }
3570
3571        let (_, height) = masonry_assign(&heights, columns, row_gap);
3572        let width = column_width * columns as f32 + column_gap * columns.saturating_sub(1) as f32;
3573        (width.max(0.0), height.max(0.0))
3574    }
3575
3576    fn wrap_child_sizes(
3577        &self,
3578        children: &[ElementId],
3579        wstyle: &WrapStyle,
3580        ctx: &mut LayoutCtx,
3581    ) -> (Vec<f32>, Vec<f32>, Vec<ElementId>) {
3582        let mut mains = Vec::with_capacity(children.len());
3583        let mut crosses = Vec::with_capacity(children.len());
3584        let mut ids = Vec::with_capacity(children.len());
3585        for &child in children {
3586            if self.child_layout_style(child).is_absolute() {
3587                continue;
3588            }
3589            let m = self.measure(
3590                child,
3591                Constraints {
3592                    min: Size::ZERO,
3593                    max: Size::new(f32::INFINITY, f32::INFINITY),
3594                },
3595                ctx,
3596            );
3597            let cs = self.child_layout_style(child);
3598            let (main, cross) = match wstyle.axis {
3599                Axis::Horizontal => (
3600                    m.width + cs.margin.horizontal_sum(),
3601                    m.height + cs.margin.vertical_sum(),
3602                ),
3603                Axis::Vertical => (
3604                    m.height + cs.margin.vertical_sum(),
3605                    m.width + cs.margin.horizontal_sum(),
3606                ),
3607            };
3608            mains.push(main.max(0.0));
3609            crosses.push(cross.max(0.0));
3610            ids.push(child);
3611        }
3612        (mains, crosses, ids)
3613    }
3614
3615    fn arrange(&self, el: ElementId, rect: Rect, ctx: &mut LayoutCtx) {
3616        if std::panic::catch_unwind(AssertUnwindSafe(|| self.arrange_inner(el, rect, ctx))).is_err()
3617        {
3618            let type_name = self.type_name_of(el).unwrap_or_else(|| "?".into());
3619            self.record_error(el, type_name, "arrange panicked".to_string());
3620        }
3621    }
3622
3623    fn arrange_inner(&self, el: ElementId, rect: Rect, ctx: &mut LayoutCtx) {
3624        if let Some(e) = self.inner.arena.borrow_mut().get_mut(el) {
3625            e.rect.set(rect);
3626        }
3627
3628        if let Some(v) = self.view_rc(el)
3629            && v.arrange_override_erased(self, el, rect, ctx)
3630        {
3631            return;
3632        }
3633
3634        let Some(v) = self.view_rc(el) else { return };
3635        let style = v.layout_style_ctx_erased(self, el);
3636        let content = Rect::from_xywh(
3637            rect.left() + style.padding.left,
3638            rect.top() + style.padding.top,
3639            (rect.size.width - style.padding.horizontal_sum()).max(0.0),
3640            (rect.size.height - style.padding.vertical_sum()).max(0.0),
3641        );
3642
3643        if let Some(gstyle) = v.grid() {
3644            let children = self.children(el);
3645            if !children.is_empty() {
3646                let placements = v.grid_placements().unwrap_or_default();
3647                self.arrange_grid(&children, &gstyle, &placements, content, ctx);
3648            } else {
3649                for &child in &self.children(el) {
3650                    if self.child_layout_style(child).is_absolute() {
3651                        self.arrange_absolute(child, content, ctx);
3652                    }
3653                }
3654            }
3655            return;
3656        }
3657
3658        if let Some(dstyle) = v.dock() {
3659            let children = self.children(el);
3660            if !children.is_empty() {
3661                self.arrange_dock(&children, &dstyle, content, ctx);
3662            }
3663            return;
3664        }
3665
3666        if let Some(wstyle) = v.wrap() {
3667            let children = self.children(el);
3668            if !children.is_empty() {
3669                self.arrange_wrap(&children, &wstyle, content, ctx);
3670            }
3671            return;
3672        }
3673
3674        if let Some(mstyle) = v.masonry() {
3675            let children = self.children(el);
3676            if !children.is_empty() {
3677                self.arrange_masonry(&children, &mstyle, content, ctx);
3678            }
3679            return;
3680        }
3681
3682        if let Some(cstyle) = v.container() {
3683            let children = self.children(el);
3684            if !children.is_empty() {
3685                let flex = v.flex_weights().unwrap_or_default();
3686                self.arrange_children(&children, &cstyle, content, &flex, ctx);
3687                return;
3688            }
3689        }
3690
3691        let children = self.children(el);
3692        if children.len() == 1 {
3693            let child = children[0];
3694            let cs = self.child_layout_style(child);
3695            if cs.is_absolute() {
3696                self.arrange_absolute(child, content, ctx);
3697                return;
3698            }
3699            let avail = Size::new(
3700                (content.size.width - cs.margin.horizontal_sum()).max(0.0),
3701                (content.size.height - cs.margin.vertical_sum()).max(0.0),
3702            );
3703            let m = self.measure(
3704                child,
3705                Constraints {
3706                    min: Size::ZERO,
3707                    max: avail,
3708                },
3709                ctx,
3710            );
3711            let (cw, ch) = match self.view_rc(child) {
3712                Some(v) => {
3713                    let cs = v.layout_style_ctx_erased(self, child);
3714                    let w_auto = matches!(cs.width, Length::Fit)
3715                        || (cs.width.is_fill() && !avail.width.is_finite());
3716                    let h_auto = matches!(cs.height, Length::Fit)
3717                        || (cs.height.is_fill() && !avail.height.is_finite());
3718                    let w = resolve_cross(cs.width, m.width, avail.width);
3719                    let h = resolve_cross(cs.height, m.height, avail.height);
3720                    let (w, h) = cs.apply_aspect(w, h, w_auto, h_auto);
3721                    (cs.clamp_width(w).max(0.0), cs.clamp_height(h).max(0.0))
3722                }
3723                None => (avail.width, avail.height),
3724            };
3725            self.arrange(
3726                child,
3727                Rect::from_xywh(
3728                    content.left() + cs.margin.left,
3729                    content.top() + cs.margin.top,
3730                    cw,
3731                    ch,
3732                ),
3733                ctx,
3734            );
3735        } else if children.len() > 1 {
3736            let flow: Vec<ElementId> = children
3737                .iter()
3738                .copied()
3739                .filter(|c| !self.child_layout_style(*c).is_absolute())
3740                .collect();
3741            if flow.len() == 1 {
3742                let child = flow[0];
3743                let cs = self.child_layout_style(child);
3744                let avail = Size::new(
3745                    (content.size.width - cs.margin.horizontal_sum()).max(0.0),
3746                    (content.size.height - cs.margin.vertical_sum()).max(0.0),
3747                );
3748                let m = self.measure(child, Constraints::loose(avail), ctx);
3749                let w = cs
3750                    .clamp_width(resolve_cross(cs.width, m.width, avail.width))
3751                    .max(0.0);
3752                let h = cs
3753                    .clamp_height(resolve_cross(cs.height, m.height, avail.height))
3754                    .max(0.0);
3755                self.arrange(
3756                    child,
3757                    Rect::from_xywh(
3758                        content.left() + cs.margin.left,
3759                        content.top() + cs.margin.top,
3760                        w,
3761                        h,
3762                    ),
3763                    ctx,
3764                );
3765            } else if !flow.is_empty() {
3766                let cs = ContainerStyle::new(Axis::Vertical, 0.0);
3767                self.arrange_children(&flow, &cs, content, &[], ctx);
3768            }
3769            for &child in &children {
3770                if self.child_layout_style(child).is_absolute() {
3771                    self.arrange_absolute(child, content, ctx);
3772                }
3773            }
3774        }
3775    }
3776
3777    fn arrange_wrap(
3778        &self,
3779        children: &[ElementId],
3780        wstyle: &WrapStyle,
3781        content: Rect,
3782        ctx: &mut LayoutCtx,
3783    ) {
3784        let avail_main = match wstyle.axis {
3785            Axis::Horizontal => content.size.width,
3786            Axis::Vertical => content.size.height,
3787        };
3788        let (mains, crosses, ids) = self.wrap_child_sizes(children, wstyle, ctx);
3789        let lines = wrap_lines(&mains, avail_main, wstyle.main_gap());
3790        let (main_gap, cross_gap) = (wstyle.main_gap(), wstyle.cross_gap());
3791
3792        let mut cross_cursor = 0.0_f32;
3793        for (line_idx, &(start, end)) in lines.iter().enumerate() {
3794            let line_cross = crosses[start..end].iter().copied().fold(0.0_f32, f32::max);
3795            if line_idx > 0 {
3796                cross_cursor += cross_gap;
3797            }
3798            let mut main_cursor = 0.0_f32;
3799            for i in start..end {
3800                let child = ids[i];
3801                if i > start {
3802                    main_cursor += main_gap;
3803                }
3804                let cs = self.child_layout_style(child);
3805                let (m_main_start, m_main_end, m_cross_start, m_cross_end) = match wstyle.axis {
3806                    Axis::Horizontal => (
3807                        cs.margin.left,
3808                        cs.margin.right,
3809                        cs.margin.top,
3810                        cs.margin.bottom,
3811                    ),
3812                    Axis::Vertical => (
3813                        cs.margin.top,
3814                        cs.margin.bottom,
3815                        cs.margin.left,
3816                        cs.margin.right,
3817                    ),
3818                };
3819                let main_outer = mains[i];
3820                let cross_outer = crosses[i];
3821                let main_inner = (main_outer - m_main_start - m_main_end).max(0.0);
3822                let mut cross_inner = (cross_outer - m_cross_start - m_cross_end).max(0.0);
3823                let cross_off = match wstyle.align {
3824                    Align::Start | Align::Stretch => 0.0,
3825                    Align::Center => ((line_cross - cross_outer) * 0.5).max(0.0),
3826                    Align::End => (line_cross - cross_outer).max(0.0),
3827                };
3828                if wstyle.align == Align::Stretch {
3829                    let cross_len = match wstyle.axis {
3830                        Axis::Horizontal => cs.height,
3831                        Axis::Vertical => cs.width,
3832                    };
3833                    if matches!(cross_len, Length::Fit) {
3834                        cross_inner = (line_cross - m_cross_start - m_cross_end).max(0.0);
3835                    }
3836                }
3837                let rect = match wstyle.axis {
3838                    Axis::Horizontal => Rect::from_xywh(
3839                        content.left() + main_cursor + m_main_start,
3840                        content.top() + cross_cursor + cross_off + m_cross_start,
3841                        main_inner,
3842                        cross_inner,
3843                    ),
3844                    Axis::Vertical => Rect::from_xywh(
3845                        content.left() + cross_cursor + cross_off + m_cross_start,
3846                        content.top() + main_cursor + m_main_start,
3847                        cross_inner,
3848                        main_inner,
3849                    ),
3850                };
3851                self.arrange(child, rect, ctx);
3852                main_cursor += main_outer;
3853            }
3854            cross_cursor += line_cross;
3855        }
3856
3857        for &child in children {
3858            if self.child_layout_style(child).is_absolute() {
3859                self.arrange_absolute(child, content, ctx);
3860            }
3861        }
3862    }
3863
3864    fn arrange_masonry(
3865        &self,
3866        children: &[ElementId],
3867        mstyle: &MasonryStyle,
3868        content: Rect,
3869        ctx: &mut LayoutCtx,
3870    ) {
3871        let flow: Vec<ElementId> = children
3872            .iter()
3873            .copied()
3874            .filter(|c| !self.child_layout_style(*c).is_absolute())
3875            .collect();
3876        let columns = mstyle.columns.max(1);
3877        let column_gap = sanitize_gap(mstyle.column_gap);
3878        let row_gap = sanitize_gap(mstyle.row_gap);
3879
3880        let column_width = if content.size.width.is_finite() {
3881            masonry_column_width(content.size.width, columns, column_gap)
3882        } else {
3883            flow.iter()
3884                .map(|&child| {
3885                    let m = self.measure(
3886                        child,
3887                        Constraints {
3888                            min: Size::ZERO,
3889                            max: Size::new(f32::INFINITY, f32::INFINITY),
3890                        },
3891                        ctx,
3892                    );
3893                    let cs = self.child_layout_style(child);
3894                    (m.width + cs.margin.horizontal_sum()).max(0.0)
3895                })
3896                .fold(0.0_f32, f32::max)
3897        };
3898
3899        let mut infos = Vec::with_capacity(flow.len());
3900        let mut heights = Vec::with_capacity(flow.len());
3901        for &child in &flow {
3902            let cs = self.child_layout_style(child);
3903            let max_width = (column_width - cs.margin.horizontal_sum()).max(0.0);
3904            let m = self.measure(
3905                child,
3906                Constraints {
3907                    min: Size::ZERO,
3908                    max: Size::new(max_width, f32::INFINITY),
3909                },
3910                ctx,
3911            );
3912            heights.push((m.height + cs.margin.vertical_sum()).max(0.0));
3913            infos.push((child, cs, m));
3914        }
3915
3916        let (placements, _) = masonry_assign(&heights, columns, row_gap);
3917        for (i, (child, cs, m)) in infos.into_iter().enumerate() {
3918            let placement = placements.get(i).copied().unwrap_or_default();
3919            let avail_w = (column_width - cs.margin.horizontal_sum()).max(0.0);
3920            let align = cs.effective_align(mstyle.align);
3921            let mut w = cs
3922                .clamp_width(resolve_cross(cs.width, m.width, avail_w))
3923                .max(0.0);
3924            if align == Align::Stretch && matches!(cs.width, Length::Fit) {
3925                w = avail_w.min(cs.max_width).max(cs.min_width).max(0.0);
3926            }
3927            let h = cs.clamp_height(m.height).max(0.0);
3928            let ox = match align {
3929                Align::Start | Align::Stretch => 0.0,
3930                Align::Center => ((avail_w - w).max(0.0)) * 0.5,
3931                Align::End => (avail_w - w).max(0.0),
3932            };
3933            let column_x = content.left()
3934                + placement.column as f32 * (column_width + column_gap)
3935                + cs.margin.left
3936                + ox;
3937            let y = content.top() + placement.top + cs.margin.top;
3938            self.arrange(
3939                child,
3940                Rect::from_xywh(column_x, y, w.max(0.0), h.max(0.0)),
3941                ctx,
3942            );
3943        }
3944
3945        for &child in children {
3946            if self.child_layout_style(child).is_absolute() {
3947                self.arrange_absolute(child, content, ctx);
3948            }
3949        }
3950    }
3951
3952    fn arrange_children(
3953        &self,
3954        children: &[ElementId],
3955        cstyle: &ContainerStyle,
3956        content: Rect,
3957        flex: &[f32],
3958        ctx: &mut LayoutCtx,
3959    ) {
3960        let flow_idx: Vec<usize> = children
3961            .iter()
3962            .enumerate()
3963            .filter(|(_, c)| !self.child_layout_style(**c).is_absolute())
3964            .map(|(i, _)| i)
3965            .collect();
3966        if cstyle.wrap {
3967            self.arrange_children_wrapped(children, &flow_idx, cstyle, content, flex, ctx);
3968        } else {
3969            self.arrange_children_linear(children, &flow_idx, cstyle, content, flex, ctx);
3970        }
3971
3972        for &child in children {
3973            if self.child_layout_style(child).is_absolute() {
3974                self.arrange_absolute(child, content, ctx);
3975            }
3976        }
3977    }
3978
3979    fn arrange_children_linear(
3980        &self,
3981        children: &[ElementId],
3982        flow_idx: &[usize],
3983        cstyle: &ContainerStyle,
3984        content: Rect,
3985        flex: &[f32],
3986        ctx: &mut LayoutCtx,
3987    ) {
3988        let (avail_main, avail_cross) = match cstyle.axis {
3989            Axis::Horizontal => (content.size.width, content.size.height),
3990            Axis::Vertical => (content.size.height, content.size.width),
3991        };
3992
3993        let mut infos: Vec<(ElementId, f32, f32, f32, f32, f32, f32, LayoutStyle)> =
3994            Vec::with_capacity(flow_idx.len());
3995        for &orig in flow_idx {
3996            let child = children[orig];
3997            let extra = flex.get(orig).copied().unwrap_or(0.0);
3998            let (base, grow, shrink, cross, mn, mx) =
3999                self.child_layout_info_full(child, cstyle, avail_main, avail_cross, extra, ctx);
4000            let cs = self.child_layout_style(child);
4001            infos.push((child, base, grow, shrink, cross, mn, mx, cs));
4002        }
4003        let child_styles: Vec<LayoutStyle> = infos.iter().map(|info| info.7).collect();
4004        let gaps = Self::indents_main_total(&child_styles, cstyle.axis, cstyle.gap);
4005
4006        {
4007            let items: Vec<FlexItem> = infos
4008                .iter()
4009                .map(|(_, b, g, s, _, mn, mx, _)| FlexItem::new(*b, *g, *s, *mn, *mx))
4010                .collect();
4011            let mut bases: Vec<f32> = infos.iter().map(|(_, b, ..)| *b).collect();
4012            distribute_flex(&items, &mut bases, gaps, avail_main);
4013            for (info, base) in infos.iter_mut().zip(bases) {
4014                info.1 = base;
4015            }
4016        }
4017
4018        if avail_main.is_finite() {
4019            for info in infos.iter_mut() {
4020                if self.subtree_has_wrap_axis(info.0, cstyle.axis) {
4021                    info.4 = self.constrained_cross(info.0, cstyle.axis, info.1, avail_cross, ctx);
4022                }
4023            }
4024        }
4025
4026        let used_main: f32 = infos.iter().map(|(_, b, ..)| *b).sum::<f32>() + gaps;
4027        let extra = Self::extra_main(avail_main, used_main);
4028        let (mut cursor, gap_step) = cstyle.justify.distribute(infos.len(), cstyle.gap, extra);
4029        for (i, info) in infos.iter().enumerate() {
4030            let (child, base, _, _, cross, _, _, cs) = *info;
4031            if i > 0 {
4032                cursor += Self::indent_leading(&cs, cstyle.axis, cstyle.gap, false) + gap_step;
4033            }
4034            let (margin_main_start, margin_cross_start) = match cstyle.axis {
4035                Axis::Horizontal => (cs.margin.left, cs.margin.top),
4036                Axis::Vertical => (cs.margin.top, cs.margin.left),
4037            };
4038            let (cross_off, cross_size) = self.place_cross(child, cstyle, cross, avail_cross);
4039            let shift = Self::indent_cross_shift(&cs, cstyle.axis);
4040            let base_inner = (base - cs.margin_main(cstyle.axis)).max(0.0);
4041            let (rx, ry, rw, rh) = match cstyle.axis {
4042                Axis::Horizontal => (
4043                    content.left() + cursor + margin_main_start,
4044                    content.top() + cross_off + shift,
4045                    base_inner,
4046                    cross_size,
4047                ),
4048                Axis::Vertical => (
4049                    content.left() + cross_off + shift,
4050                    content.top() + cursor + margin_main_start,
4051                    cross_size,
4052                    base_inner,
4053                ),
4054            };
4055            let _ = margin_cross_start;
4056            cursor += base + Self::indent_trailing(&cs, cstyle.axis);
4057            self.arrange(
4058                child,
4059                Rect::from_xywh(rx, ry, rw.max(0.0), rh.max(0.0)),
4060                ctx,
4061            );
4062        }
4063    }
4064
4065    fn arrange_children_wrapped(
4066        &self,
4067        children: &[ElementId],
4068        flow_idx: &[usize],
4069        cstyle: &ContainerStyle,
4070        content: Rect,
4071        flex: &[f32],
4072        ctx: &mut LayoutCtx,
4073    ) {
4074        let (avail_main, avail_cross) = match cstyle.axis {
4075            Axis::Horizontal => (content.size.width, content.size.height),
4076            Axis::Vertical => (content.size.height, content.size.width),
4077        };
4078        let n = flow_idx.len();
4079        if n == 0 {
4080            return;
4081        }
4082        let mut bases = Vec::with_capacity(n);
4083        let mut crosses = Vec::with_capacity(n);
4084        let mut items = Vec::with_capacity(n);
4085        let mut ids = Vec::with_capacity(n);
4086        for &orig in flow_idx {
4087            let child = children[orig];
4088            let extra = flex.get(orig).copied().unwrap_or(0.0);
4089            let (base, grow, shrink, cross, mn, mx) =
4090                self.child_layout_info_full(child, cstyle, avail_main, avail_cross, extra, ctx);
4091            ids.push(child);
4092            bases.push(base);
4093            crosses.push(cross);
4094            items.push(FlexItem::new(base, grow, shrink, mn, mx));
4095        }
4096        let lines = wrap_lines(&bases, avail_main, cstyle.gap);
4097
4098        let mut line_crosses: Vec<f32> = Vec::with_capacity(lines.len());
4099        for &(s, e) in &lines {
4100            let mut line_sizes = bases[s..e].to_vec();
4101            distribute_flex(
4102                &items[s..e],
4103                &mut line_sizes,
4104                cstyle.main_gaps_total(e - s),
4105                avail_main,
4106            );
4107            for (k, v) in (s..e).zip(line_sizes) {
4108                bases[k] = v;
4109            }
4110
4111            if avail_main.is_finite() {
4112                for k in s..e {
4113                    if self.subtree_has_wrap_axis(ids[k], cstyle.axis) {
4114                        crosses[k] =
4115                            self.constrained_cross(ids[k], cstyle.axis, bases[k], avail_cross, ctx);
4116                    }
4117                }
4118            }
4119            line_crosses.push(crosses[s..e].iter().cloned().fold(0.0f32, f32::max));
4120        }
4121        let total_cross: f32 = line_crosses.iter().sum::<f32>()
4122            + cstyle.cross_gap * lines.len().saturating_sub(1) as f32;
4123        let cross_extra = if avail_cross.is_finite() {
4124            (avail_cross - total_cross).max(0.0)
4125        } else {
4126            0.0
4127        };
4128        let (mut cross_cursor, cross_step) = match cstyle.align_content {
4129            Align::Start => (0.0, 0.0),
4130            Align::Center => (cross_extra * 0.5, 0.0),
4131            Align::End => (cross_extra, 0.0),
4132            Align::Stretch if line_crosses.len() > 1 => {
4133                (0.0, cross_extra / (line_crosses.len() - 1) as f32)
4134            }
4135            Align::Stretch => (0.0, 0.0),
4136        };
4137
4138        for (li, &(s, e)) in lines.iter().enumerate() {
4139            let line_cross = line_crosses[li];
4140            if li > 0 {
4141                cross_cursor += cstyle.cross_gap + cross_step;
4142            }
4143            let used: f32 = bases[s..e].iter().sum::<f32>() + cstyle.main_gaps_total(e - s);
4144            let line_extra = if avail_main.is_finite() {
4145                (avail_main - used).max(0.0)
4146            } else {
4147                0.0
4148            };
4149            let (mut cursor, gap_step) = cstyle.justify.distribute(e - s, cstyle.gap, line_extra);
4150            for k in s..e {
4151                if k > s {
4152                    cursor += cstyle.gap + gap_step;
4153                }
4154                let child = ids[k];
4155                let cs = self.child_layout_style(child);
4156                let margin_main_start = match cstyle.axis {
4157                    Axis::Horizontal => cs.margin.left,
4158                    Axis::Vertical => cs.margin.top,
4159                };
4160                let (cross_off, cross_size) =
4161                    self.place_cross(child, cstyle, crosses[k], line_cross);
4162                let base_inner = (bases[k] - cs.margin_main(cstyle.axis)).max(0.0);
4163                let (rx, ry, rw, rh) = match cstyle.axis {
4164                    Axis::Horizontal => (
4165                        content.left() + cursor + margin_main_start,
4166                        content.top() + cross_cursor + cross_off,
4167                        base_inner,
4168                        cross_size,
4169                    ),
4170                    Axis::Vertical => (
4171                        content.left() + cross_cursor + cross_off,
4172                        content.top() + cursor + margin_main_start,
4173                        cross_size,
4174                        base_inner,
4175                    ),
4176                };
4177                cursor += bases[k];
4178                self.arrange(
4179                    child,
4180                    Rect::from_xywh(rx, ry, rw.max(0.0), rh.max(0.0)),
4181                    ctx,
4182                );
4183            }
4184            cross_cursor += line_cross;
4185        }
4186    }
4187
4188    fn arrange_grid(
4189        &self,
4190        children: &[ElementId],
4191        grid: &GridStyle,
4192        placements: &[GridPlacement],
4193        content: Rect,
4194        ctx: &mut LayoutCtx,
4195    ) {
4196        let (content_col, content_row, cells, nrows_needed) =
4197            self.grid_content_sizes(children, grid, placements, content.size, ctx);
4198        let widths = resolve_tracks(
4199            &grid.columns,
4200            &content_col,
4201            content.size.width,
4202            grid.column_gap,
4203        );
4204        let rows = Self::grid_rows(grid, nrows_needed);
4205        let heights = resolve_tracks(&rows, &content_row, content.size.height, grid.row_gap);
4206        let xs = track_offsets(&widths, grid.column_gap);
4207        let ys = track_offsets(&heights, grid.row_gap);
4208
4209        for (i, &child) in children.iter().enumerate() {
4210            let cs = self.child_layout_style(child);
4211            if cs.is_absolute() {
4212                self.arrange_absolute(child, content, ctx);
4213                continue;
4214            }
4215            let (col, row, col_span, row_span) = cells.get(i).copied().unwrap_or((0, 0, 1, 1));
4216            if col_span == 0 {
4217                continue;
4218            }
4219
4220            let cw = (col..(col + col_span).min(widths.len()))
4221                .map(|c| widths[c])
4222                .sum::<f32>()
4223                + grid.column_gap * col_span.saturating_sub(1) as f32;
4224            let ch = (row..(row + row_span).min(heights.len()))
4225                .map(|r| heights[r])
4226                .sum::<f32>()
4227                + grid.row_gap * row_span.saturating_sub(1) as f32;
4228            let col_max = self.track_cap(grid.columns.get(col), content.size.width);
4229            let m = self.measure(
4230                child,
4231                Constraints {
4232                    min: Size::ZERO,
4233                    max: Size::new(col_max, f32::INFINITY),
4234                },
4235                ctx,
4236            );
4237            let eff_h = cs.effective_align(grid.align_items);
4238            let eff_w = cs.effective_align(grid.justify_items);
4239
4240            let mut w = cs
4241                .clamp_width(resolve_cross(cs.width, m.width, cw))
4242                .max(0.0);
4243            let mut h = cs
4244                .clamp_height(resolve_cross(cs.height, m.height, ch))
4245                .max(0.0);
4246            if eff_w == Align::Stretch && matches!(cs.width, Length::Fit) && cw.is_finite() {
4247                w = (cw - cs.margin.horizontal_sum())
4248                    .max(0.0)
4249                    .min(cs.max_width)
4250                    .max(cs.min_width);
4251            }
4252            if eff_h == Align::Stretch && matches!(cs.height, Length::Fit) && ch.is_finite() {
4253                h = (ch - cs.margin.vertical_sum())
4254                    .max(0.0)
4255                    .min(cs.max_height)
4256                    .max(cs.min_height);
4257            }
4258            let cell_x = content.left() + xs.get(col).copied().unwrap_or(0.0);
4259            let cell_y = content.top() + ys.get(row).copied().unwrap_or(0.0);
4260            let avail_w = (cw - cs.margin.horizontal_sum()).max(0.0);
4261            let avail_h = (ch - cs.margin.vertical_sum()).max(0.0);
4262            let ox = match eff_w {
4263                Align::Start | Align::Stretch => 0.0,
4264                Align::Center => ((avail_w - w).max(0.0)) * 0.5,
4265                Align::End => (avail_w - w).max(0.0),
4266            };
4267            let oy = match eff_h {
4268                Align::Start | Align::Stretch => 0.0,
4269                Align::Center => ((avail_h - h).max(0.0)) * 0.5,
4270                Align::End => (avail_h - h).max(0.0),
4271            };
4272            self.arrange(
4273                child,
4274                Rect::from_xywh(
4275                    cell_x + cs.margin.left + ox,
4276                    cell_y + cs.margin.top + oy,
4277                    w,
4278                    h,
4279                ),
4280                ctx,
4281            );
4282        }
4283    }
4284
4285    fn measure_dock(
4286        &self,
4287        children: &[ElementId],
4288        dstyle: &DockStyle,
4289        inner_avail: Size,
4290        ctx: &mut LayoutCtx,
4291    ) -> (f32, f32) {
4292        let flow: Vec<ElementId> = children
4293            .iter()
4294            .copied()
4295            .filter(|c| !self.child_layout_style(*c).is_absolute())
4296            .collect();
4297        let n = flow.len();
4298        if n == 0 {
4299            return (0.0, 0.0);
4300        }
4301        let gap = sanitize_gap(dstyle.gap);
4302
4303        let order = dock_layout_order(&dstyle.docks, dstyle.last_child_fill, dstyle.ordered, n);
4304        let mut remaining = Size::new(inner_avail.width.max(0.0), inner_avail.height.max(0.0));
4305        let mut measured = vec![Size::ZERO; n];
4306        let mut thick = vec![0.0_f32; n];
4307
4308        for (pos, &i) in order.iter().enumerate() {
4309            let child = flow[i];
4310            let d = resolve_dock(&dstyle.docks, dstyle.last_child_fill, dstyle.ordered, i, n);
4311            let max = match d {
4312                Dock::Left | Dock::Right => Size::new(f32::INFINITY, remaining.height),
4313                Dock::Top | Dock::Bottom => Size::new(remaining.width, f32::INFINITY),
4314                Dock::Fill => remaining,
4315            };
4316            let m = self.measure(child, Constraints::loose(max), ctx);
4317            let cs = self.child_layout_style(child);
4318            let t = match d {
4319                Dock::Left | Dock::Right => Self::clamp_dock_thickness(
4320                    cs.clamp_width(dock_thickness(cs.width, m.width, remaining.width)),
4321                    remaining.width,
4322                ),
4323                Dock::Top | Dock::Bottom => Self::clamp_dock_thickness(
4324                    cs.clamp_height(dock_thickness(cs.height, m.height, remaining.height)),
4325                    remaining.height,
4326                ),
4327                Dock::Fill => 0.0,
4328            };
4329            measured[i] = m;
4330            thick[i] = t;
4331            remaining = Self::shrink_dock_remaining(remaining, d, t, gap, pos, n);
4332        }
4333
4334        let mut cw = 0.0_f32;
4335        let mut ch = 0.0_f32;
4336        for &i in order.iter().rev() {
4337            let m = measured[i];
4338            match resolve_dock(&dstyle.docks, dstyle.last_child_fill, dstyle.ordered, i, n) {
4339                Dock::Fill => {
4340                    cw = cw.max(m.width);
4341                    ch = ch.max(m.height);
4342                }
4343                Dock::Left | Dock::Right => {
4344                    cw += thick[i];
4345
4346                    if order.iter().position(|&x| x == i).unwrap_or(0) + 1 < n {
4347                        cw += gap;
4348                    }
4349                    ch = ch.max(m.height);
4350                }
4351                Dock::Top | Dock::Bottom => {
4352                    cw = cw.max(m.width);
4353                    ch += thick[i];
4354                    if order.iter().position(|&x| x == i).unwrap_or(0) + 1 < n {
4355                        ch += gap;
4356                    }
4357                }
4358            }
4359        }
4360        (cw.max(0.0), ch.max(0.0))
4361    }
4362
4363    fn arrange_dock(
4364        &self,
4365        children: &[ElementId],
4366        dstyle: &DockStyle,
4367        content: Rect,
4368        ctx: &mut LayoutCtx,
4369    ) {
4370        let flow: Vec<ElementId> = children
4371            .iter()
4372            .copied()
4373            .filter(|c| !self.child_layout_style(*c).is_absolute())
4374            .collect();
4375        let n = flow.len();
4376        let gap = sanitize_gap(dstyle.gap);
4377        let order = dock_layout_order(&dstyle.docks, dstyle.last_child_fill, dstyle.ordered, n);
4378        let mut remaining = content;
4379        for (pos, &i) in order.iter().enumerate() {
4380            let child = flow[i];
4381            let d = resolve_dock(&dstyle.docks, dstyle.last_child_fill, dstyle.ordered, i, n);
4382            let max = match d {
4383                Dock::Left | Dock::Right => {
4384                    Size::new(f32::INFINITY, remaining.size.height.max(0.0))
4385                }
4386                Dock::Top | Dock::Bottom => Size::new(remaining.size.width.max(0.0), f32::INFINITY),
4387                Dock::Fill => Size::new(
4388                    remaining.size.width.max(0.0),
4389                    remaining.size.height.max(0.0),
4390                ),
4391            };
4392            let m = self.measure(child, Constraints::loose(max), ctx);
4393            let cs = self.child_layout_style(child);
4394            let rect = match d {
4395                Dock::Left => {
4396                    let t = Self::clamp_dock_thickness(
4397                        cs.clamp_width(dock_thickness(cs.width, m.width, remaining.size.width)),
4398                        remaining.size.width,
4399                    );
4400                    let h = cs.clamp_height(remaining.size.height.max(0.0)).max(0.0);
4401                    Rect::from_xywh(remaining.left(), remaining.top(), t, h)
4402                }
4403                Dock::Right => {
4404                    let t = Self::clamp_dock_thickness(
4405                        cs.clamp_width(dock_thickness(cs.width, m.width, remaining.size.width)),
4406                        remaining.size.width,
4407                    );
4408                    let h = cs.clamp_height(remaining.size.height.max(0.0)).max(0.0);
4409                    Rect::from_xywh(remaining.right() - t, remaining.top(), t, h)
4410                }
4411                Dock::Top => {
4412                    let t = Self::clamp_dock_thickness(
4413                        cs.clamp_height(dock_thickness(cs.height, m.height, remaining.size.height)),
4414                        remaining.size.height,
4415                    );
4416                    let w = cs.clamp_width(remaining.size.width.max(0.0)).max(0.0);
4417                    Rect::from_xywh(remaining.left(), remaining.top(), w, t)
4418                }
4419                Dock::Bottom => {
4420                    let t = Self::clamp_dock_thickness(
4421                        cs.clamp_height(dock_thickness(cs.height, m.height, remaining.size.height)),
4422                        remaining.size.height,
4423                    );
4424                    let w = cs.clamp_width(remaining.size.width.max(0.0)).max(0.0);
4425                    Rect::from_xywh(remaining.left(), remaining.bottom() - t, w, t)
4426                }
4427                Dock::Fill => {
4428                    let w = cs.clamp_width(remaining.size.width.max(0.0)).max(0.0);
4429                    let h = cs.clamp_height(remaining.size.height.max(0.0)).max(0.0);
4430                    Rect::from_xywh(remaining.left(), remaining.top(), w, h)
4431                }
4432            };
4433            self.arrange(
4434                child,
4435                Rect::from_xywh(
4436                    rect.left(),
4437                    rect.top(),
4438                    rect.size.width.max(0.0),
4439                    rect.size.height.max(0.0),
4440                ),
4441                ctx,
4442            );
4443            remaining = Self::shrink_dock_rect(
4444                remaining,
4445                d,
4446                Self::dock_rect_thickness(rect, d),
4447                gap,
4448                pos,
4449                n,
4450            );
4451        }
4452        for &child in children {
4453            if self.child_layout_style(child).is_absolute() {
4454                self.arrange_absolute(child, content, ctx);
4455            }
4456        }
4457    }
4458
4459    fn dock_rect_thickness(rect: Rect, d: Dock) -> f32 {
4460        match d {
4461            Dock::Left | Dock::Right => rect.size.width.max(0.0),
4462            Dock::Top | Dock::Bottom => rect.size.height.max(0.0),
4463            Dock::Fill => 0.0,
4464        }
4465    }
4466
4467    fn clamp_dock_thickness(thickness: f32, remaining: f32) -> f32 {
4468        let t = if thickness.is_finite() {
4469            thickness.max(0.0)
4470        } else {
4471            0.0
4472        };
4473        if remaining.is_finite() {
4474            t.min(remaining.max(0.0))
4475        } else {
4476            t
4477        }
4478    }
4479
4480    fn shrink_dock_remaining(
4481        remaining: Size,
4482        d: Dock,
4483        thickness: f32,
4484        gap: f32,
4485        index: usize,
4486        count: usize,
4487    ) -> Size {
4488        let extra = if d.is_side() && index + 1 < count {
4489            gap
4490        } else {
4491            0.0
4492        };
4493        let sub = thickness + extra;
4494        match d {
4495            Dock::Left | Dock::Right => {
4496                Size::new(shrink_dock_axis(remaining.width, sub), remaining.height)
4497            }
4498            Dock::Top | Dock::Bottom => {
4499                Size::new(remaining.width, shrink_dock_axis(remaining.height, sub))
4500            }
4501            Dock::Fill => Size::ZERO,
4502        }
4503    }
4504
4505    fn shrink_dock_rect(
4506        remaining: Rect,
4507        d: Dock,
4508        thickness: f32,
4509        gap: f32,
4510        index: usize,
4511        count: usize,
4512    ) -> Rect {
4513        let extra = if d.is_side() && index + 1 < count {
4514            gap
4515        } else {
4516            0.0
4517        };
4518        let sub = thickness + extra;
4519        match d {
4520            Dock::Left => Rect::from_xywh(
4521                remaining.left() + sub,
4522                remaining.top(),
4523                shrink_dock_axis(remaining.size.width, sub),
4524                remaining.size.height.max(0.0),
4525            ),
4526            Dock::Right => Rect::from_xywh(
4527                remaining.left(),
4528                remaining.top(),
4529                shrink_dock_axis(remaining.size.width, sub),
4530                remaining.size.height.max(0.0),
4531            ),
4532            Dock::Top => Rect::from_xywh(
4533                remaining.left(),
4534                remaining.top() + sub,
4535                remaining.size.width.max(0.0),
4536                shrink_dock_axis(remaining.size.height, sub),
4537            ),
4538            Dock::Bottom => Rect::from_xywh(
4539                remaining.left(),
4540                remaining.top(),
4541                remaining.size.width.max(0.0),
4542                shrink_dock_axis(remaining.size.height, sub),
4543            ),
4544            Dock::Fill => Rect::from_xywh(remaining.left(), remaining.top(), 0.0, 0.0),
4545        }
4546    }
4547
4548    fn extra_main(avail_main: f32, used_main: f32) -> f32 {
4549        if avail_main.is_finite() {
4550            (avail_main - used_main).max(0.0)
4551        } else {
4552            0.0
4553        }
4554    }
4555
4556    fn sanitize_gap_value(v: f32) -> f32 {
4557        if v.is_finite() { v.max(0.0) } else { 0.0 }
4558    }
4559
4560    fn indent_leading(cs: &LayoutStyle, axis: Axis, default_gap: f32, is_first: bool) -> f32 {
4561        if is_first {
4562            return 0.0;
4563        }
4564        match cs.indent_main_start(axis) {
4565            Some(v) => Self::sanitize_gap_value(v),
4566            None => Self::sanitize_gap_value(default_gap),
4567        }
4568    }
4569
4570    fn indent_trailing(cs: &LayoutStyle, axis: Axis) -> f32 {
4571        match cs.indent_main_end(axis) {
4572            Some(v) => Self::sanitize_gap_value(v),
4573            None => 0.0,
4574        }
4575    }
4576
4577    fn indents_main_total(styles: &[LayoutStyle], axis: Axis, default_gap: f32) -> f32 {
4578        let mut total = 0.0;
4579        for (i, cs) in styles.iter().enumerate() {
4580            total += Self::indent_leading(cs, axis, default_gap, i == 0);
4581            total += Self::indent_trailing(cs, axis);
4582        }
4583        total
4584    }
4585
4586    fn indent_cross_shift(cs: &LayoutStyle, axis: Axis) -> f32 {
4587        let start = cs.indent_cross_start(axis).map(Self::sanitize_gap_value);
4588        let end = cs.indent_cross_end(axis).map(Self::sanitize_gap_value);
4589        start.unwrap_or(0.0) - end.unwrap_or(0.0)
4590    }
4591
4592    fn place_cross(
4593        &self,
4594        child: ElementId,
4595        cstyle: &ContainerStyle,
4596        cross: f32,
4597        avail_cross: f32,
4598    ) -> (f32, f32) {
4599        let Some(v) = self.view_rc(child) else {
4600            return (0.0, 0.0);
4601        };
4602        let cs = v.layout_style_ctx_erased(self, child);
4603        let eff = cs.effective_align(cstyle.align);
4604        let (m_start, m_end) = match cstyle.axis {
4605            Axis::Horizontal => (cs.margin.top, cs.margin.bottom),
4606            Axis::Vertical => (cs.margin.left, cs.margin.right),
4607        };
4608        let m_total = m_start + m_end;
4609        let cross_inner = (cross - m_total).max(0.0);
4610
4611        let floor = {
4612            let (cross_len, explicit_min, explicit_max) = match cstyle.axis {
4613                Axis::Horizontal => (cs.height, cs.min_height, cs.max_height),
4614                Axis::Vertical => (cs.width, cs.min_width, cs.max_width),
4615            };
4616            let px = match cross_len {
4617                Length::Px(v) if v.is_finite() => v.max(0.0),
4618                _ => 0.0,
4619            };
4620            let explicit_min = if explicit_min.is_finite() {
4621                explicit_min.max(0.0)
4622            } else {
4623                0.0
4624            };
4625            let mx = if explicit_max.is_nan() || explicit_max == f32::INFINITY {
4626                f32::INFINITY
4627            } else {
4628                explicit_max.max(0.0)
4629            };
4630
4631            let content_floor = match cross_len {
4632                Length::Fit => cross_inner,
4633                _ => 0.0,
4634            };
4635            px.max(explicit_min).max(content_floor).min(mx)
4636        };
4637
4638        let avail_inner = if avail_cross.is_finite() {
4639            (avail_cross - m_total).max(0.0)
4640        } else {
4641            f32::INFINITY
4642        };
4643        let placed_inner = cross_inner.min(avail_inner).max(floor);
4644        let slack = if avail_inner.is_finite() {
4645            (avail_inner - placed_inner).max(0.0)
4646        } else {
4647            0.0
4648        };
4649        match eff {
4650            Align::Start => (m_start, placed_inner),
4651            Align::Center => (m_start + slack * 0.5, placed_inner),
4652            Align::End => (m_start + slack, placed_inner),
4653            Align::Stretch => {
4654                let (auto_cross, max_cross) = match cstyle.axis {
4655                    Axis::Horizontal => (matches!(cs.height, Length::Fit), cs.max_height),
4656                    Axis::Vertical => (matches!(cs.width, Length::Fit), cs.max_width),
4657                };
4658
4659                if auto_cross && avail_inner.is_finite() {
4660                    let stretched = avail_inner.min(max_cross).max(floor).max(0.0);
4661                    (m_start, stretched)
4662                } else {
4663                    (m_start, placed_inner)
4664                }
4665            }
4666        }
4667    }
4668
4669    pub(crate) fn arrange_absolute(&self, child: ElementId, content: Rect, ctx: &mut LayoutCtx) {
4670        let cs = self.child_layout_style(child);
4671        let Some(inset) = cs.absolute_inset_of() else {
4672            let m = self.measure(child, Constraints::loose(content.size), ctx);
4673            let w = cs
4674                .clamp_width(resolve_cross(cs.width, m.width, content.size.width))
4675                .max(0.0);
4676            let h = cs
4677                .clamp_height(resolve_cross(cs.height, m.height, content.size.height))
4678                .max(0.0);
4679            self.arrange(
4680                child,
4681                Rect::from_xywh(
4682                    content.left() + cs.margin.left,
4683                    content.top() + cs.margin.top,
4684                    w,
4685                    h,
4686                ),
4687                ctx,
4688            );
4689            return;
4690        };
4691        let ml = cs.margin.left;
4692        let mt = cs.margin.top;
4693        let mr = cs.margin.right;
4694        let mb = cs.margin.bottom;
4695        let avail_w = (content.size.width - ml - mr).max(0.0);
4696        let avail_h = (content.size.height - mt - mb).max(0.0);
4697        let m = self.measure(child, Constraints::loose(Size::new(avail_w, avail_h)), ctx);
4698
4699        let mut w = resolve_cross(cs.width, m.width, avail_w);
4700        let mut h = resolve_cross(cs.height, m.height, avail_h);
4701        if let (Some(l), Some(r)) = (inset.left, inset.right)
4702            && (matches!(cs.width, Length::Fit) || matches!(cs.width, Length::Fill(_)))
4703        {
4704            w = (content.size.width - l.max(0.0) - r.max(0.0) - ml - mr).max(0.0);
4705        }
4706        if let (Some(t), Some(b)) = (inset.top, inset.bottom)
4707            && (matches!(cs.height, Length::Fit) || matches!(cs.height, Length::Fill(_)))
4708        {
4709            h = (content.size.height - t.max(0.0) - b.max(0.0) - mt - mb).max(0.0);
4710        }
4711        w = cs.clamp_width(w).max(0.0);
4712        h = cs.clamp_height(h).max(0.0);
4713        let x = match (inset.left, inset.right) {
4714            (Some(l), _) => content.left() + l + ml,
4715            (None, Some(r)) => content.right() - r - mr - w,
4716            (None, None) => content.left() + ml,
4717        };
4718        let y = match (inset.top, inset.bottom) {
4719            (Some(t), _) => content.top() + t + mt,
4720            (None, Some(b)) => content.bottom() - b - mb - h,
4721            (None, None) => content.top() + mt,
4722        };
4723        self.arrange(child, Rect::from_xywh(x, y, w, h), ctx);
4724    }
4725
4726    fn paint_rec(
4727        &self,
4728        id: ElementId,
4729        parent_offset: Vec2,
4730        parent_scale: f32,
4731        parent_transform: Transform,
4732        list: &mut RenderList,
4733        text: &mut TextSystem,
4734    ) {
4735        let depth = list.clip_depth();
4736        let opacity_depth = list.opacity_depth();
4737        let transform_depth = list.transform_depth();
4738        match std::panic::catch_unwind(AssertUnwindSafe(|| {
4739            self.paint_rec_inner(
4740                id,
4741                parent_offset,
4742                parent_scale,
4743                parent_transform,
4744                &mut *list,
4745                &mut *text,
4746            )
4747        })) {
4748            Ok(()) => {}
4749            Err(_) => {
4750                list.truncate_clips(depth);
4751                list.truncate_opacity(opacity_depth);
4752                list.truncate_transforms(transform_depth);
4753                let type_name = self.type_name_of(id).unwrap_or_else(|| "?".into());
4754                self.record_error(id, type_name, "paint panicked".to_string());
4755            }
4756        }
4757    }
4758
4759    fn paint_rec_inner(
4760        &self,
4761        id: ElementId,
4762        parent_offset: Vec2,
4763        parent_scale: f32,
4764        parent_transform: Transform,
4765        list: &mut RenderList,
4766        text: &mut TextSystem,
4767    ) {
4768        let Some(v) = self.view_rc(id) else { return };
4769        let base_rect = {
4770            let arena = self.inner.arena.borrow();
4771            arena.get(id).map(|e| e.rect.get()).unwrap_or(Rect::ZERO)
4772        };
4773
4774        let own = v.paint_offset_ctx_erased(self, id);
4775        let acc = Vec2::new(parent_offset.x + own.x, parent_offset.y + own.y);
4776        let rect = if acc.x == 0.0 && acc.y == 0.0 {
4777            base_rect
4778        } else {
4779            base_rect.translate(acc)
4780        };
4781
4782        let scale = {
4783            let own = v.paint_scale_ctx_erased(self, id);
4784            let own = if own.is_finite() && own > 0.0 {
4785                own
4786            } else {
4787                1.0
4788            };
4789            (parent_scale * own).max(0.0)
4790        };
4791        let rect = if (scale - 1.0).abs() > f32::EPSILON {
4792            let cx = rect.origin.x + rect.size.width * 0.5;
4793            let cy = rect.origin.y + rect.size.height * 0.5;
4794            let w = (rect.size.width * scale).max(0.0);
4795            let h = (rect.size.height * scale).max(0.0);
4796            Rect::from_xywh(cx - w * 0.5, cy - h * 0.5, w, h)
4797        } else {
4798            rect
4799        };
4800
4801        let own_rotation = {
4802            let d = v.paint_rotation_ctx_erased(self, id);
4803            if d.is_finite() { d } else { 0.0 }
4804        };
4805        let own_transform = if own_rotation.abs() > 0.001 {
4806            Transform::rotate_around_degrees(own_rotation, rect.center())
4807        } else {
4808            Transform::IDENTITY
4809        };
4810        let total_transform = parent_transform.then(own_transform);
4811        let pushes_transform = !own_transform.is_identity();
4812        if pushes_transform {
4813            list.push_transform(own_transform);
4814        }
4815
4816        let paint_bounds = total_transform.apply_rect_aabb(rect);
4817
4818        let opacity = v
4819            .paint_opacity_ctx_erased(self, id)
4820            .map(|o| {
4821                if o.is_finite() {
4822                    o.clamp(0.0, 1.0)
4823                } else {
4824                    1.0
4825                }
4826            })
4827            .unwrap_or(1.0);
4828        let pushes_opacity = opacity < 1.0 - f32::EPSILON;
4829        if pushes_opacity {
4830            list.push_opacity(opacity);
4831        }
4832
4833        const CULL_MARGIN: f32 = 32.0;
4834        let culled = match list.active_clip() {
4835            Some(c) => !c.intersects(paint_bounds.inflate(CULL_MARGIN, CULL_MARGIN)),
4836            None => false,
4837        };
4838        if culled {
4839            if v.clip_self_erased() {
4840                if pushes_opacity {
4841                    list.pop_opacity();
4842                }
4843                if pushes_transform {
4844                    list.pop_transform();
4845                }
4846                return;
4847            }
4848            for c in self.children(id) {
4849                self.paint_rec(c, acc, scale, total_transform, list, text);
4850            }
4851            if pushes_opacity {
4852                list.pop_opacity();
4853            }
4854            if pushes_transform {
4855                list.pop_transform();
4856            }
4857            return;
4858        }
4859
4860        let should_clip = v.clip_self_erased()
4861            || v.layout_style_ctx_erased(self, id).overflow == Overflow::Hidden;
4862        if should_clip {
4863            let clip_rect = if pushes_transform { paint_bounds } else { rect };
4864            let r = v.clip_radius_erased();
4865            if r > 0.0 {
4866                list.push_rounded_clip(clip_rect, r);
4867            } else {
4868                list.push_clip(clip_rect);
4869            }
4870        }
4871
4872        if let Some(target) = v.surface_target_erased() {
4873            let clipped = match list.active_clip() {
4874                Some(c) => c.intersect(rect),
4875                None => rect,
4876            };
4877            if !clipped.is_empty() {
4878                self.inner.surfaces.borrow_mut().push(SurfaceDraw {
4879                    rect: clipped,
4880                    full: rect,
4881                    target,
4882                });
4883            }
4884        }
4885
4886        let filter_handle = v
4887            .foreground_filter_erased()
4888            .filter(|f| !f.is_empty())
4889            .and_then(|f| {
4890                let r = if pushes_transform { paint_bounds } else { rect };
4891                list.push_filter_begin(r, 0.0, f.blur_px(), f.span_mask())
4892            });
4893
4894        let depth = list.clip_depth();
4895
4896        let (state, _) = self.view_state(id);
4897        let env = self.inner.environment.borrow().clone();
4898        {
4899            let type_name = v.type_name().to_string();
4900
4901            let res = std::panic::catch_unwind(AssertUnwindSafe(|| {
4902                let mut ctx = PaintCtx {
4903                    list: &mut *list,
4904                    text: &mut *text,
4905                    environment: &env,
4906                    rect,
4907                    element: id,
4908                    state,
4909                    window: self.inner.window.get(),
4910                    hovered_subtree: self.hover_in_subtree(id),
4911                    runtime: self,
4912                    shapes: &self.inner.shapes,
4913                    draw_offset: Vec2::ZERO,
4914                    offset_stack: Vec::new(),
4915                };
4916                v.paint_self_erased(&mut ctx);
4917            }));
4918            if res.is_err() {
4919                self.record_error(id, type_name, "paint_self panicked".to_string());
4920            }
4921        }
4922        for c in self.children(id) {
4923            self.paint_rec(c, acc, scale, total_transform, list, text);
4924        }
4925
4926        {
4927            let type_name = v.type_name().to_string();
4928            let res = std::panic::catch_unwind(AssertUnwindSafe(|| {
4929                let mut ctx = PaintCtx {
4930                    list: &mut *list,
4931                    text: &mut *text,
4932                    environment: &env,
4933                    rect,
4934                    element: id,
4935                    state,
4936                    window: self.inner.window.get(),
4937                    hovered_subtree: self.hover_in_subtree(id),
4938                    runtime: self,
4939                    shapes: &self.inner.shapes,
4940                    draw_offset: Vec2::ZERO,
4941                    offset_stack: Vec::new(),
4942                };
4943                v.paint_foreground_erased(&mut ctx);
4944            }));
4945            if res.is_err() {
4946                self.record_error(id, type_name, "paint_foreground panicked".to_string());
4947            }
4948        }
4949        if let Some(h) = filter_handle {
4950            list.push_filter_end(h);
4951        }
4952
4953        list.truncate_clips(depth);
4954        if should_clip {
4955            list.pop_clip();
4956        }
4957        if pushes_opacity {
4958            list.pop_opacity();
4959        }
4960        if pushes_transform {
4961            list.pop_transform();
4962        }
4963    }
4964
4965    fn view_rc(&self, id: ElementId) -> Option<Rc<dyn ErasedView>> {
4966        self.inner.arena.borrow().get(id)?.view.clone()
4967    }
4968
4969    fn invalidate_measure_chain(&self, id: ElementId) {
4970        self.inner
4971            .layout_epoch
4972            .set(self.inner.layout_epoch.get().wrapping_add(1));
4973        let mut measures = self.inner.measures.borrow_mut();
4974        let arena = self.inner.arena.borrow();
4975        let mut cur = Some(id);
4976        let mut n = 0u32;
4977        while let Some(c) = cur {
4978            if measures.remove(&c).is_some() {
4979                n += 1;
4980            }
4981            cur = arena.get(c).and_then(|e| e.parent);
4982            if n > 4096 {
4983                break;
4984            }
4985        }
4986        drop(arena);
4987        drop(measures);
4988        if n > 0 {
4989            let mut s = self.inner.stats.get();
4990            s.measure_invalidations += u64::from(n);
4991            self.inner.stats.set(s);
4992        }
4993    }
4994
4995    fn alloc(
4996        &self,
4997        value: Rc<dyn ErasedView>,
4998        parent: Option<ElementId>,
4999        index: usize,
5000        key: u64,
5001    ) -> ElementId {
5002        let (slot, generation) = { self.inner.arena.borrow_mut().alloc_slot() };
5003        let id = ElementId {
5004            index: slot,
5005            generation,
5006        };
5007        let weak: Weak<Inner> = Rc::downgrade(&self.inner);
5008        let watcher = Watcher::new(move || {
5009            if let Some(inner) = weak.upgrade() {
5010                inner.schedule(id);
5011            }
5012        });
5013        {
5014            let mut arena = self.inner.arena.borrow_mut();
5015            arena.slots[slot as usize] = Some(Element {
5016                parent,
5017                key,
5018                children: Vec::new(),
5019                view: Some(value),
5020                watcher: Some(watcher),
5021                reads: ReadSet::empty(),
5022                dirty: Dirty::empty(),
5023                rect: Cell::new(Rect::ZERO),
5024            });
5025            if let Some(p) = parent
5026                && let Some(pe) = arena.get_mut(p)
5027            {
5028                let at = index.min(pe.children.len());
5029                pe.children.insert(at, id);
5030            }
5031        }
5032        let mut s = self.inner.stats.get();
5033        s.mounts += 1;
5034        self.inner.stats.set(s);
5035        id
5036    }
5037
5038    fn build_with(&self, el: ElementId, value: Rc<dyn ErasedView>) {
5039        self.invalidate_measure_chain(el);
5040        let v = value;
5041
5042        let type_name = v.type_name().to_string();
5043        let fail = |rt: &Self| {
5044            rt.rebind_reads(el, sequoia_reactive::ReadSet::empty());
5045            rt.truncate_children(el, 0);
5046            rt.record_error(el, type_name.clone(), "body panicked".to_string());
5047        };
5048
5049        let overridden_res =
5050            std::panic::catch_unwind(AssertUnwindSafe(|| capture_reads(|| v.children_override())));
5051        let Ok((overridden, override_reads)) = overridden_res else {
5052            fail(self);
5053            return;
5054        };
5055        if let Some(children) = overridden {
5056            let reads = match std::panic::catch_unwind(AssertUnwindSafe(|| {
5057                capture_reads(|| v.call_body())
5058            })) {
5059                Ok((_, body_reads)) => override_reads.union(body_reads),
5060                Err(_) => override_reads,
5061            };
5062            self.rebind_reads(el, reads);
5063            let list = children
5064                .into_iter()
5065                .enumerate()
5066                .map(|(i, av)| (i as u64, av.into_inner()))
5067                .collect();
5068            self.reconcile_keyed(el, list);
5069            return;
5070        }
5071
5072        {
5073            let keyed_res =
5074                std::panic::catch_unwind(AssertUnwindSafe(|| capture_reads(|| v.keyed_children())));
5075            let Ok((keyed, reads)) = keyed_res else {
5076                fail(self);
5077                return;
5078            };
5079            if let Some(children) = keyed {
5080                let reads = match std::panic::catch_unwind(AssertUnwindSafe(|| {
5081                    capture_reads(|| v.call_body())
5082                })) {
5083                    Ok((_, body_reads)) => reads.union(body_reads),
5084                    Err(_) => reads,
5085                };
5086                self.rebind_reads(el, reads);
5087                let list = children
5088                    .into_iter()
5089                    .map(|(k, av)| (k, av.into_inner()))
5090                    .collect();
5091                self.reconcile_keyed(el, list);
5092                return;
5093            }
5094        }
5095
5096        let is_leaf = match std::panic::catch_unwind(AssertUnwindSafe(|| v.is_leaf())) {
5097            Ok(b) => b,
5098            Err(_) => {
5099                self.record_error(el, type_name.clone(), "is_leaf panicked".to_string());
5100                false
5101            }
5102        };
5103        if is_leaf {
5104            let leaf_res =
5105                std::panic::catch_unwind(AssertUnwindSafe(|| capture_reads(|| v.call_body())));
5106            let Ok((_, reads)) = leaf_res else {
5107                fail(self);
5108                return;
5109            };
5110            self.rebind_reads(el, reads);
5111            self.truncate_children(el, 0);
5112            return;
5113        }
5114
5115        let body_res =
5116            std::panic::catch_unwind(AssertUnwindSafe(|| capture_reads(|| v.call_body())));
5117        let Ok((body, reads)) = body_res else {
5118            fail(self);
5119            return;
5120        };
5121        self.rebind_reads(el, reads);
5122        self.reconcile_keyed(el, vec![(0, body.into_inner())]);
5123    }
5124
5125    fn reconcile_keyed(&self, parent: ElementId, new: Vec<(u64, Rc<dyn ErasedView>)>) {
5126        let old: Vec<(u64, ElementId)> = {
5127            let arena = self.inner.arena.borrow();
5128            arena
5129                .get(parent)
5130                .map(|p| {
5131                    p.children
5132                        .iter()
5133                        .filter_map(|&c| arena.get(c).map(|e| (e.key, c)))
5134                        .collect()
5135                })
5136                .unwrap_or_default()
5137        };
5138
5139        let new_keys: HashSet<u64> = new.iter().map(|(k, _)| *k).collect();
5140
5141        for (k, id) in &old {
5142            if !new_keys.contains(k) {
5143                self.unmount_subtree(*id);
5144            }
5145        }
5146
5147        let mut buckets: HashMap<u64, Vec<ElementId>> = HashMap::new();
5148        for (k, id) in old {
5149            if new_keys.contains(&k) {
5150                buckets.entry(k).or_default().push(id);
5151            }
5152        }
5153        for ids in buckets.values_mut() {
5154            ids.reverse();
5155        }
5156
5157        let mut order: Vec<ElementId> = Vec::with_capacity(new.len());
5158        for (key, view) in new {
5159            let view = unwrap_any(view);
5160            let reused = buckets.get_mut(&key).and_then(|ids| ids.pop());
5161            match reused {
5162                Some(el) => {
5163                    let same_type = {
5164                        let arena = self.inner.arena.borrow();
5165                        arena
5166                            .get(el)
5167                            .and_then(|e| e.view.as_ref())
5168                            .map(|v| v.view_type())
5169                            == Some(view.view_type())
5170                    };
5171                    if same_type {
5172                        let old_view = self
5173                            .inner
5174                            .arena
5175                            .borrow()
5176                            .get(el)
5177                            .and_then(|e| e.view.clone());
5178                        if let Some(old) = old_view {
5179                            view.reuse_state_erased(&*old);
5180                        }
5181                        if let Some(e) = self.inner.arena.borrow_mut().get_mut(el) {
5182                            e.key = key;
5183                            e.view = Some(view);
5184                        }
5185                        self.invalidate_measure_chain(el);
5186                        if let Some(v) = self.view_rc(el) {
5187                            self.build_with(el, v);
5188                        }
5189                        order.push(el);
5190                    } else {
5191                        self.unmount_subtree(el);
5192                        order.push(self.mount_fresh_detached(view, key));
5193                    }
5194                }
5195                None => order.push(self.mount_fresh_detached(view, key)),
5196            }
5197        }
5198
5199        for (_, leftover) in buckets {
5200            for id in leftover {
5201                self.unmount_subtree(id);
5202            }
5203        }
5204
5205        self.invalidate_measure_chain(parent);
5206        self.set_children(parent, order);
5207    }
5208
5209    fn mount_fresh_detached(&self, value: Rc<dyn ErasedView>, key: u64) -> ElementId {
5210        let id = self.alloc(value, None, 0, key);
5211        if let Some(v) = self.view_rc(id) {
5212            self.build_with(id, v);
5213        }
5214        if let Some(v) = self.view_rc(id) {
5215            let type_name = v.type_name().to_string();
5216            if std::panic::catch_unwind(AssertUnwindSafe(|| {
5217                v.on_mount();
5218            }))
5219            .is_err()
5220            {
5221                self.record_error(id, type_name, "on_mount panicked".to_string());
5222            }
5223        }
5224        id
5225    }
5226
5227    fn set_children(&self, parent: ElementId, order: Vec<ElementId>) {
5228        {
5229            let mut arena = self.inner.arena.borrow_mut();
5230            for &c in &order {
5231                if let Some(e) = arena.get_mut(c) {
5232                    e.parent = Some(parent);
5233                }
5234            }
5235            if let Some(p) = arena.get_mut(parent) {
5236                p.children = order;
5237            }
5238        }
5239        self.invalidate_measure_chain(parent);
5240    }
5241
5242    fn truncate_children(&self, el: ElementId, keep: usize) {
5243        let doomed: Vec<ElementId> = {
5244            let arena = self.inner.arena.borrow();
5245            arena
5246                .get(el)
5247                .map(|e| e.children.iter().skip(keep).copied().collect())
5248                .unwrap_or_default()
5249        };
5250        for d in doomed {
5251            self.unmount_subtree(d);
5252        }
5253        if let Some(e) = self.inner.arena.borrow_mut().get_mut(el) {
5254            e.children.truncate(keep);
5255        }
5256    }
5257
5258    fn rebind_reads(&self, el: ElementId, reads: ReadSet) {
5259        if let Some(e) = self.inner.arena.borrow_mut().get_mut(el) {
5260            if let Some(w) = e.watcher.clone() {
5261                e.reads.unbind(&w);
5262            }
5263            e.reads = reads;
5264            if let Some(w) = e.watcher.clone() {
5265                e.reads.bind(&w);
5266            }
5267        }
5268    }
5269
5270    fn unmount_subtree(&self, id: ElementId) {
5271        self.invalidate_measure_chain(id);
5272        let children: Vec<ElementId> = {
5273            let arena = self.inner.arena.borrow();
5274            arena
5275                .get(id)
5276                .map(|e| e.children.clone())
5277                .unwrap_or_default()
5278        };
5279        for c in children {
5280            self.unmount_subtree(c);
5281        }
5282        let (parent, slot, view) = {
5283            let mut arena = self.inner.arena.borrow_mut();
5284            let Some(e) = arena.get_mut(id) else { return };
5285            if let Some(w) = e.watcher.take() {
5286                e.reads.unbind(&w);
5287            }
5288            (e.parent, id.index, e.view.take())
5289        };
5290        if let Some(v) = view {
5291            let type_name = v.type_name().to_string();
5292            if std::panic::catch_unwind(AssertUnwindSafe(|| {
5293                v.on_unmount();
5294            }))
5295            .is_err()
5296            {
5297                self.record_error(id, type_name, "on_unmount panicked".to_string());
5298            }
5299        }
5300        if let Some(p) = parent {
5301            let mut arena = self.inner.arena.borrow_mut();
5302            if let Some(pe) = arena.get_mut(p)
5303                && let Some(pos) = pe.children.iter().position(|&c| c == id)
5304            {
5305                pe.children.remove(pos);
5306            }
5307        }
5308        self.inner.measures.borrow_mut().remove(&id);
5309        self.inner
5310            .live_anims
5311            .borrow_mut()
5312            .retain(|(eid, _), _| *eid != id);
5313
5314        self.inner.states.borrow_mut().remove(&id);
5315        if let Some(cancels) = self.inner.scoped_cancels.borrow_mut().remove(&id) {
5316            for cancel in cancels {
5317                let _ = std::panic::catch_unwind(AssertUnwindSafe(cancel));
5318            }
5319        }
5320        self.inner.arena.borrow_mut().free_slot(slot);
5321
5322        let anchored = self.inner.overlays.borrow().anchored_to(id);
5323        for oid in anchored {
5324            self.overlay_hide(oid);
5325        }
5326
5327        if self
5328            .inner
5329            .pending_drag
5330            .borrow()
5331            .as_ref()
5332            .is_some_and(|p| p.source == id)
5333        {
5334            self.inner.pending_drag.borrow_mut().take();
5335        }
5336        {
5337            let mut guard = self.inner.active_drag.borrow_mut();
5338            if let Some(act) = guard.as_mut() {
5339                if act.source == Some(id) {
5340                    act.source = None;
5341                }
5342                if act.target == Some(id) {
5343                    act.target = None;
5344                }
5345            }
5346        }
5347        let mut s = self.inner.stats.get();
5348        s.unmounts += 1;
5349        self.inner.stats.set(s);
5350    }
5351}
5352
5353fn shrink_dock_axis(remaining: f32, used: f32) -> f32 {
5354    if remaining.is_finite() {
5355        (remaining - used).max(0.0)
5356    } else {
5357        remaining
5358    }
5359}
5360
5361fn filled_root_size(style: LayoutStyle, bounds: Size, measured: Size) -> Size {
5362    Size::new(
5363        if style.width.is_fit() {
5364            style.clamp_width(bounds.width)
5365        } else {
5366            measured.width
5367        },
5368        if style.height.is_fit() {
5369            style.clamp_height(bounds.height)
5370        } else {
5371            measured.height
5372        },
5373    )
5374}
5375
5376fn unwrap_any(mut v: Rc<dyn ErasedView>) -> Rc<dyn ErasedView> {
5377    for _ in 0..64 {
5378        let any: Rc<dyn Any> = v.clone().as_any_rc();
5379        match any.downcast::<AnyView>() {
5380            Ok(av) => {
5381                let inner = av.clone_inner();
5382                if Rc::ptr_eq(&inner, &v) {
5383                    return v;
5384                }
5385                v = inner;
5386            }
5387            Err(_) => return v,
5388        }
5389    }
5390    v
5391}
5392
5393#[cfg(test)]
5394mod tests {
5395    use super::*;
5396
5397    fn id(index: u32) -> ElementId {
5398        ElementId {
5399            index,
5400            generation: 0,
5401        }
5402    }
5403
5404    #[test]
5405    fn filled_root_size_stretches_only_fit_axes() {
5406        let bounds = Size::new(800.0, 600.0);
5407        let measured = Size::new(120.0, 40.0);
5408        assert_eq!(
5409            filled_root_size(LayoutStyle::default(), bounds, measured),
5410            bounds
5411        );
5412
5413        let style = LayoutStyle {
5414            width: Length::px(120.0),
5415            height: Length::percent(50.0),
5416            ..LayoutStyle::default()
5417        };
5418        assert_eq!(filled_root_size(style, bounds, measured), measured);
5419
5420        let capped = LayoutStyle {
5421            max_width: 400.0,
5422            max_height: 300.0,
5423            ..LayoutStyle::default()
5424        };
5425        assert_eq!(
5426            filled_root_size(capped, bounds, measured),
5427            Size::new(400.0, 300.0)
5428        );
5429    }
5430
5431    #[test]
5432    fn root_fill_stretches_root_to_viewport() {
5433        let rt = Runtime::new();
5434        rt.set_root_fill(true);
5435        rt.mount(crate::Unit);
5436        let mut text = TextSystem::new();
5437        let bounds = Rect::from_xywh(0.0, 0.0, 800.0, 600.0);
5438        rt.layout(bounds, &mut text);
5439        assert_eq!(rt.rect_of(rt.root().unwrap()).unwrap().size, bounds.size);
5440    }
5441
5442    #[test]
5443    fn clock_advances_exactly_in_manual_mode() {
5444        let rt = Runtime::new();
5445        rt.set_manual_clock(true);
5446        rt.advance_for_test(std::time::Duration::from_millis(500));
5447        assert!((rt.elapsed_secs() - 0.5).abs() < 1e-6);
5448        assert!((rt.frame_dt_secs() - 0.5).abs() < 1e-6);
5449
5450        rt.set_animation_speed(2.0);
5451        rt.advance_for_test(std::time::Duration::from_millis(250));
5452        assert!((rt.elapsed_secs() - 1.0).abs() < 1e-6);
5453        assert!((rt.frame_dt_secs() - 0.5).abs() < 1e-6);
5454    }
5455
5456    #[test]
5457    fn reduced_motion_freezes_the_shared_clock() {
5458        let rt = Runtime::new();
5459        rt.set_manual_clock(true);
5460        rt.set_reduced_motion(true);
5461        rt.advance_for_test(std::time::Duration::from_millis(500));
5462        assert_eq!(rt.elapsed_secs(), 0.0);
5463        assert_eq!(rt.frame_dt_secs(), 0.0);
5464    }
5465
5466    #[test]
5467    fn live_anims_are_evicted_with_their_elements() {
5468        let rt = Runtime::new();
5469        rt.mount(crate::Unit);
5470        let root = rt.root().unwrap();
5471        let spec = AnimSpec::FAST;
5472        rt.live_f32(root, Prop::OPACITY, 1.0, Some(&spec), None);
5473        assert_eq!(rt.live_anim_count(), 1);
5474
5475        rt.live_f32(id(9999), Prop::OPACITY, 1.0, Some(&spec), None);
5476        assert_eq!(rt.live_anim_count(), 2);
5477        let mut list = RenderList::default();
5478        let mut text = TextSystem::new();
5479        rt.paint(&mut list, &mut text);
5480        assert_eq!(rt.live_anim_count(), 1, "detached element evicted on paint");
5481
5482        rt.mount(crate::Unit);
5483        assert_eq!(rt.live_anim_count(), 0, "unmount evicts animations");
5484    }
5485
5486    #[test]
5487    fn with_state_allows_reentrant_access() {
5488        let rt = Runtime::new();
5489        let (a, b) = (id(0), id(1));
5490        rt.with_state::<u32, _>(a, |x| *x += 1);
5491        let out = rt.with_state::<u32, _>(a, |x| {
5492            *x += 10;
5493            let inner = rt.with_state::<String, _>(b, |s| {
5494                s.push_str("inner");
5495                s.clone()
5496            });
5497            format!("{x}:{inner}")
5498        });
5499        assert_eq!(out, "11:inner");
5500        assert_eq!(rt.with_state::<u32, _>(a, |x| *x), 11);
5501        assert_eq!(rt.state_count(), 2);
5502        assert!(rt.has_state(a) && rt.has_state(b));
5503    }
5504
5505    #[test]
5506    fn with_state_survives_panic_and_keeps_slot() {
5507        let rt = Runtime::new();
5508        let a = id(0);
5509        rt.with_state::<u32, _>(a, |x| *x = 7);
5510        let hook = std::panic::take_hook();
5511        std::panic::set_hook(Box::new(|_| {}));
5512        let r = std::panic::catch_unwind(AssertUnwindSafe(|| {
5513            rt.with_state::<u32, _>(a, |x| {
5514                *x = 99;
5515                panic!("boom");
5516            });
5517        }));
5518        std::panic::set_hook(hook);
5519        assert!(r.is_err(), "panic went through with_state");
5520        assert_eq!(
5521            rt.with_state::<u32, _>(a, |x| *x),
5522            99,
5523            "the slot is back after the panic"
5524        );
5525        assert_eq!(rt.state_count(), 1);
5526    }
5527}