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