Skip to main content

sequoia_view/
runtime.rs

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