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