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