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