1use std::any::Any;
10use std::cell::{Cell, RefCell};
11use std::collections::{HashMap, VecDeque};
12use std::rc::Rc;
13
14use day_reactive::Scope;
15use day_spec::*;
16use slotmap::{Key, KeyData, SlotMap, new_key_type};
17
18use crate::layout::{Alignment, CrossAlign, Layout, PassThrough};
19
20new_key_type! {
21 pub struct RNode;
23}
24
25pub fn rnode_to_id(n: RNode) -> NodeId {
26 NodeId(n.data().as_ffi())
27}
28pub fn id_to_rnode(id: NodeId) -> RNode {
29 RNode::from(KeyData::from_ffi(id.0))
30}
31
32#[derive(Clone, Copy, Debug, Default, PartialEq)]
34pub struct Flex {
35 pub grow_w: bool,
37 pub grow_h: bool,
38 pub yields_h: bool,
42 pub is_spacer: bool,
44 pub is_group: bool,
46 pub grid: GridFacts,
48 pub whole_row: bool,
52}
53
54#[derive(Clone, Copy, Debug, Default, PartialEq)]
58pub struct GridFacts {
59 pub is_row: bool,
61 pub row_valign: Option<CrossAlign>,
63 pub col_span: u16,
65 pub align: Option<Alignment>,
67}
68
69#[derive(Clone, Debug, Default)]
71pub struct NodeProbe {
72 pub text: String,
73 pub value: f64,
74 pub flag: bool,
75 pub selected: i64,
76 pub options: Vec<String>,
79 pub enabled: bool,
80 pub focused: bool,
82}
83
84pub struct NodeData<H> {
85 pub kind: PieceKind,
86 pub handle: Option<H>,
87 pub parent: RNode,
88 pub children: Vec<RNode>,
89 pub layout: Rc<dyn Layout>,
90 pub flex: Flex,
91 pub scope: Scope,
92 pub id: Option<String>,
93 pub parked_id: Option<String>,
99 pub a11y: day_spec::A11yProps,
103 pub cache: Vec<((u64, u64), Size)>,
105 pub baseline_cache: Option<((u64, u64), Option<f64>)>,
109 pub probe: NodeProbe,
110 pub needs_measure: bool,
111 pub needs_layout: bool,
114 pub last_native_frame: Option<Rect>,
115 pub is_boundary: bool,
116 pub scroll_content: Option<Size>,
119 pub implicit_anim: Option<day_spec::AnimSpec>,
123 pub tweaked: bool,
127}
128
129pub type EventHandler = Rc<dyn Fn(&Event)>;
131
132pub type EventObserver = dyn Fn(NodeId, &Event);
135
136#[derive(Clone, Copy, PartialEq, Eq, Debug)]
139pub enum WindowRootReply {
140 Open(RNode),
142 Pending(RNode),
145 Unsupported,
147}
148
149struct WindowEntry {
157 root: RNode,
158 size: Size,
159}
160
161#[derive(Clone)]
163enum ContextMenuModel {
164 Items(Vec<day_spec::MenuItem>),
165 Provider(day_spec::ContextMenuFn),
166}
167
168pub struct Tree<B: Toolkit> {
169 mounted_id_families: std::collections::BTreeSet<String>,
170 pub toolkit: B,
171 nodes: SlotMap<RNode, NodeData<B::Handle>>,
172 windows: Vec<WindowEntry>,
173 layout_dirty: bool,
174 handlers: HashMap<RNode, Vec<EventHandler>>,
175 gestures: HashMap<RNode, Vec<day_spec::GestureKind>>,
178 element_outer: HashMap<RNode, RNode>,
181 release_queue: Vec<(day_spec::PieceKind, B::Handle)>,
184 lists: HashMap<RNode, crate::list::ListState>,
186 trees: HashMap<RNode, crate::tree_driver::TreeState>,
187 context_menus: HashMap<RNode, ContextMenuModel>,
191 implicit_anim_count: usize,
194}
195
196impl<B: Toolkit> Tree<B> {
197 fn clear_subtree_ids(&mut self, anchor: RNode) {
202 let mut stack = vec![anchor];
203 while let Some(n) = stack.pop() {
204 if let Some(d) = self.nodes.get_mut(n) {
205 if let Some(id) = d.id.take() {
206 d.parked_id = Some(id);
207 }
208 stack.extend(d.children.iter().copied());
209 }
210 }
211 }
212
213 fn restore_subtree_ids(&mut self, anchor: RNode) {
217 let mut stack = vec![anchor];
218 while let Some(n) = stack.pop() {
219 if let Some(d) = self.nodes.get_mut(n) {
220 if let Some(id) = d.parked_id.take()
221 && d.id.is_none()
222 {
223 d.id = Some(id);
224 }
225 stack.extend(d.children.iter().copied());
226 }
227 }
228 }
229
230 pub fn new(toolkit: B, root_handle: B::Handle, window_size: Size) -> Self {
231 let mut nodes = SlotMap::with_key();
232 let root = nodes.insert(NodeData {
233 kind: kinds::CONTAINER,
234 handle: Some(root_handle),
235 parent: RNode::null(),
236 children: Vec::new(),
237 layout: Rc::new(PassThrough),
238 flex: Flex::default(),
239 scope: Scope::root(),
240 id: None,
241 parked_id: None,
242 a11y: Default::default(),
243 cache: Vec::new(),
244 baseline_cache: None,
245 probe: NodeProbe::default(),
246 needs_measure: true,
247 needs_layout: true,
248 last_native_frame: None,
249 scroll_content: None,
250 implicit_anim: None,
251 tweaked: false,
252 is_boundary: true,
253 });
254 Tree {
255 mounted_id_families: Default::default(),
256 toolkit,
257 nodes,
258 windows: vec![WindowEntry {
259 root,
260 size: window_size,
261 }],
262 layout_dirty: true,
263 handlers: HashMap::new(),
264 gestures: HashMap::new(),
265 element_outer: HashMap::new(),
266 release_queue: Vec::new(),
267 lists: HashMap::new(),
268 trees: HashMap::new(),
269 context_menus: HashMap::new(),
270 implicit_anim_count: 0,
271 }
272 }
273
274 pub(crate) fn create_cell_anchor(
277 &mut self,
278 handle: B::Handle,
279 scope: Scope,
280 layout: Rc<dyn Layout>,
281 ) -> RNode {
282 self.nodes.insert(NodeData {
283 kind: kinds::LIST_CELL,
284 handle: Some(handle),
285 parent: RNode::null(),
286 children: Vec::new(),
287 layout,
288 flex: Flex::default(),
289 scope,
290 id: None,
291 parked_id: None,
292 a11y: Default::default(),
293 cache: Vec::new(),
294 baseline_cache: None,
295 probe: NodeProbe::default(),
296 needs_measure: true,
297 needs_layout: true,
298 last_native_frame: None,
299 scroll_content: None,
300 implicit_anim: None,
301 tweaked: false,
302 is_boundary: true,
303 })
304 }
305
306 pub fn root(&self) -> RNode {
307 self.windows[0].root
308 }
309
310 pub(crate) fn node(&self, n: RNode) -> Option<&NodeData<B::Handle>> {
311 self.nodes.get(n)
312 }
313 pub(crate) fn node_mut(&mut self, n: RNode) -> Option<&mut NodeData<B::Handle>> {
314 self.nodes.get_mut(n)
315 }
316
317 pub(crate) fn resolve_anim(&self, node: RNode) -> Option<day_spec::AnimSpec> {
322 if crate::reduce_motion_now() {
325 return None;
326 }
327 if let Some(a) = crate::anim::current_anim() {
328 return Some(a);
329 }
330 if self.implicit_anim_count == 0 {
331 return None;
332 }
333 let mut cur = node;
334 loop {
335 let n = self.nodes.get(cur)?;
336 if n.implicit_anim.is_some() {
337 return n.implicit_anim;
338 }
339 if n.parent == cur {
340 return None;
341 }
342 cur = n.parent;
343 }
344 }
345
346 fn native_ancestor(&self, mut n: RNode) -> RNode {
348 loop {
349 let Some(node) = self.nodes.get(n) else {
350 return self.windows[0].root;
353 };
354 if node.handle.is_some() {
355 return n;
356 }
357 n = node.parent;
358 }
359 }
360
361 fn native_descendants(&self, container: RNode, out: &mut Vec<RNode>) {
363 let Some(node) = self.nodes.get(container) else {
364 return;
365 };
366 for &c in &node.children {
367 match self.nodes.get(c) {
368 Some(cd) if cd.handle.is_some() => out.push(c),
369 Some(_) => self.native_descendants(c, out),
370 None => {}
371 }
372 }
373 }
374
375 fn native_index_for(&self, ancestor: RNode, target: RNode) -> usize {
378 fn walk<B: Toolkit>(tree: &Tree<B>, n: RNode, target: RNode, count: &mut usize) -> bool {
379 if n == target {
380 return true;
381 }
382 let Some(node) = tree.nodes.get(n) else {
383 return false;
384 };
385 if node.handle.is_some() && n != target {
386 *count += 1;
388 return false;
389 }
390 for &c in &node.children {
391 if walk(tree, c, target, count) {
392 return true;
393 }
394 }
395 false
396 }
397 let mut count = 0;
398 let Some(anc) = self.nodes.get(ancestor) else {
399 return 0;
400 };
401 for &c in &anc.children {
402 if c == target || self.subtree_contains(c, target) {
403 let mut cnt = count;
405 walk(self, c, target, &mut cnt);
406 return cnt;
407 }
408 let mut roots = Vec::new();
409 match self.nodes.get(c) {
410 Some(cd) if cd.handle.is_some() => count += 1,
411 Some(_) => {
412 self.native_descendants(c, &mut roots);
413 count += roots.len();
414 }
415 None => {}
416 }
417 }
418 count
419 }
420
421 fn subtree_contains(&self, root: RNode, target: RNode) -> bool {
422 if root == target {
423 return true;
424 }
425 let Some(node) = self.nodes.get(root) else {
426 return false;
427 };
428 node.children
429 .iter()
430 .any(|&c| self.subtree_contains(c, target))
431 }
432
433 fn attach_impl(&mut self, parent: RNode, child: RNode, index: usize) {
435 {
436 let p = self
437 .nodes
438 .get_mut(parent)
439 .expect("attach to missing parent");
440 let idx = index.min(p.children.len());
441 p.children.insert(idx, child);
442 }
443 self.nodes
444 .get_mut(child)
445 .expect("attach missing child")
446 .parent = parent;
447 let ancestor = self.native_ancestor(parent);
450 let anc_handle = self.nodes[ancestor]
451 .handle
452 .clone()
453 .expect("native ancestor");
454 let mut roots = Vec::new();
455 match self.nodes.get(child) {
456 Some(cd) if cd.handle.is_some() => roots.push(child),
457 Some(_) => self.native_descendants(child, &mut roots),
458 None => {}
459 }
460 for r in roots {
461 let idx = self.native_index_for(ancestor, r);
462 let h = self.nodes[r].handle.clone().unwrap();
463 self.toolkit.insert(&anc_handle, &h, idx);
464 }
465 self.mark_needs_measure_impl(parent);
466 }
467
468 fn remove_subtree_impl(&mut self, node: RNode) {
469 let parent = self
472 .nodes
473 .get(node)
474 .map(|n| n.parent)
475 .unwrap_or(RNode::null());
476 if let Some(p) = self.nodes.get_mut(parent) {
477 p.children.retain(|&c| c != node);
478 }
479 let anc_handle = if parent.is_null() {
483 None
484 } else {
485 let ancestor = self.native_ancestor(parent);
486 self.nodes.get(ancestor).and_then(|n| n.handle.clone())
487 };
488 let mut roots = Vec::new();
489 match self.nodes.get(node) {
490 Some(nd) if nd.handle.is_some() => roots.push(node),
491 Some(_) => self.native_descendants(node, &mut roots),
492 None => {}
493 }
494 if let Some(anc_handle) = anc_handle {
495 for r in &roots {
496 let h = self.nodes[*r].handle.clone().unwrap();
497 self.toolkit.remove(&anc_handle, &h);
498 }
499 }
500 self.collect_and_release(node);
501 if parent.is_null() {
502 self.layout_dirty = true;
503 } else {
504 self.mark_needs_measure_impl(parent);
505 }
506 }
507
508 fn collect_and_release(&mut self, start: RNode) {
513 let mut stack = vec![start];
514 while let Some(n) = stack.pop() {
515 self.context_menus.remove(&n);
520 if let Some(list) = self.lists.remove(&n) {
521 for (_, cell) in list.cells {
522 cell.scope.dispose();
523 stack.push(cell.anchor);
528 }
529 }
530 if let Some(tree) = self.trees.remove(&n) {
532 for (_, cell) in tree.cells {
533 cell.scope.dispose();
534 stack.push(cell.anchor);
535 }
536 }
537 let Some(data) = self.nodes.remove(n) else {
538 continue;
539 };
540 self.handlers.remove(&n);
541 self.gestures.remove(&n);
542 self.element_outer.remove(&n);
543 if data.implicit_anim.is_some() {
544 self.implicit_anim_count = self.implicit_anim_count.saturating_sub(1);
545 }
546 if let Some(h) = data.handle {
547 if data.kind != kinds::LIST_CELL {
553 self.release_queue.push((data.kind, h));
554 }
555 }
556 stack.extend(data.children);
557 }
558 }
559
560 fn mark_needs_measure_impl(&mut self, node: RNode) {
561 let mut cur = node;
562 let mut measure = true;
563 while let Some(n) = self.nodes.get_mut(cur) {
564 n.needs_layout = true;
565 if measure {
566 n.needs_measure = true;
567 n.cache.clear();
568 }
569 if n.is_boundary {
570 measure = false;
571 }
572 if n.parent.is_null() {
573 break;
574 }
575 cur = n.parent;
576 }
577 self.layout_dirty = true;
578 }
579
580 fn invalidate_subtree(&mut self, node: RNode) {
586 let mut stack = vec![node];
587 while let Some(cur) = stack.pop() {
588 if let Some(n) = self.nodes.get_mut(cur) {
589 n.needs_measure = true;
590 n.cache.clear();
591 stack.extend(n.children.iter().copied());
592 }
593 }
594 }
595
596 fn layout_list_cell_at(&mut self, node: RNode, key: usize, width: f64) {
600 let Some(state) = self.lists.get(&node) else {
601 return;
602 };
603 let (anchor, stale, header) = match state.cells.get(&key) {
604 Some(b) if b.active => (b.anchor, b.laid_width != Some(width), b.header),
605 _ => return,
606 };
607 let row_height = if header {
610 day_spec::props::RowHeight::Automatic
611 } else {
612 state.driver.row_height
613 };
614 if stale {
619 self.invalidate_subtree(anchor);
620 if let Some(b) = self
621 .lists
622 .get_mut(&node)
623 .and_then(|s| s.cells.get_mut(&key))
624 {
625 b.laid_width = Some(width);
626 }
627 }
628 let height = match row_height {
629 day_spec::props::RowHeight::Uniform(h) => h,
630 day_spec::props::RowHeight::Automatic => {
631 crate::layout::measure_node(self, anchor, Proposal::new(Some(width), None)).height
632 }
633 };
634 let previous = self
635 .lists
636 .get_mut(&node)
637 .and_then(|s| s.cells.get_mut(&key))
638 .and_then(|b| b.laid_height.replace(height));
639 if previous.is_some_and(|p| (p - height).abs() > 0.25)
643 && let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone())
644 {
645 self.toolkit.update(
646 &handle,
647 kinds::LIST,
648 &day_spec::props::ListPatch::RowSizeInvalidated { cell: key, height } as &dyn Any,
649 None,
650 );
651 }
652 crate::layout::place_node(
653 self,
654 anchor,
655 Rect::new(0.0, 0.0, width, height),
656 Point::ZERO,
657 true,
658 );
659 self.nodes[anchor].needs_measure = false;
663 }
664
665 fn layout_tree_cell_at(&mut self, node: RNode, key: usize, width: f64) {
668 let Some(state) = self.trees.get(&node) else {
669 return;
670 };
671 let (anchor, stale) = match state.cells.get(&key) {
672 Some(b) => (b.anchor, b.laid_width != Some(width)),
673 None => return,
674 };
675 let row_height = state.driver.row_height;
676 let native_height = state.cells.get(&key).and_then(|b| b.native_height);
677 if stale {
678 self.invalidate_subtree(anchor);
679 if let Some(b) = self
680 .trees
681 .get_mut(&node)
682 .and_then(|s| s.cells.get_mut(&key))
683 {
684 b.laid_width = Some(width);
685 }
686 }
687 let height = native_height.unwrap_or_else(|| match row_height {
688 day_spec::props::RowHeight::Uniform(h) => h,
689 day_spec::props::RowHeight::Automatic => {
690 crate::layout::measure_node(self, anchor, Proposal::new(Some(width), None)).height
691 }
692 });
693 if let Some(b) = self
694 .trees
695 .get_mut(&node)
696 .and_then(|s| s.cells.get_mut(&key))
697 {
698 b.laid_height = Some(height);
699 }
700 crate::layout::place_node(
701 self,
702 anchor,
703 Rect::new(0.0, 0.0, width, height),
704 Point::ZERO,
705 true,
706 );
707 self.nodes[anchor].needs_measure = false;
708 }
709
710 fn layout_now(&mut self) {
711 for i in 0..self.windows.len() {
714 let (root, size) = (self.windows[i].root, self.windows[i].size);
715 let p = Proposal::exact(size);
716 crate::layout::measure_node(self, root, p);
717 crate::layout::place_node(self, root, Rect::from_size(size), Point::ZERO, true);
718 }
719 let dirty_cells: Vec<(RNode, usize)> = self
725 .lists
726 .iter()
727 .flat_map(|(list, state)| {
728 state.cells.iter().filter_map(|(key, cell)| {
729 if !cell.active {
730 return None;
731 }
732 self.nodes
733 .get(cell.anchor)
734 .filter(|n| n.needs_layout)
735 .map(|_| (*list, *key))
736 })
737 })
738 .collect();
739 let dirty_tree_cells: Vec<(RNode, usize)> = self
740 .trees
741 .iter()
742 .flat_map(|(tree, state)| {
743 state.cells.iter().filter_map(|(key, cell)| {
744 self.nodes
745 .get(cell.anchor)
746 .filter(|n| n.needs_layout)
747 .map(|_| (*tree, *key))
748 })
749 })
750 .collect();
751 for (tree, key) in dirty_tree_cells {
752 self.tree_layout_cell(tree, key);
753 }
754 for (list, key) in dirty_cells {
755 self.list_layout_cell(list, key);
756 }
757 let queue = std::mem::take(&mut self.release_queue);
758 for (kind, h) in queue {
759 self.toolkit.release_piece(kind, &h);
762 self.toolkit.release(h);
763 }
764 }
765}
766
767#[derive(Clone, Debug, PartialEq)]
776pub enum ScrollTarget {
777 Top,
778 Bottom,
779 Leading,
780 Trailing,
781 Offset(Point),
782 Id(String),
783}
784
785pub trait TreeOps {
786 #[allow(clippy::too_many_arguments)]
789 fn create_node(
790 &mut self,
791 kind: PieceKind,
792 props: &dyn Any,
793 layout: Rc<dyn Layout>,
794 flex: Flex,
795 native: bool,
796 is_boundary: bool,
797 scope: Scope,
798 ) -> RNode;
799 fn attach(&mut self, parent: RNode, child: RNode);
800 fn reparent(&mut self, child: RNode, parent: RNode) -> bool;
802 fn native_window_tabs(&self) -> bool;
803 fn native_document_tabs(&self) -> bool;
804 fn group_windows(&mut self, roots: &[RNode]);
805 fn window_tab_order(&self, root: RNode) -> Vec<day_spec::NodeId>;
806
807 fn attach_at(&mut self, parent: RNode, child: RNode, index: usize);
808 fn reorder_children(&mut self, parent: RNode, order: Vec<RNode>);
809 fn remove_subtree(&mut self, node: RNode);
810 fn node_exists(&self, node: RNode) -> bool;
812 fn dark_mode(&mut self) -> bool;
814 fn reduce_motion(&mut self) -> bool;
816 fn set_appearance(&mut self, dark: Option<bool>);
818 fn set_app_badge(&mut self, badge: &day_spec::AppBadge);
820 fn on_event(&mut self, node: RNode, h: EventHandler);
821 fn handlers_for(&self, node: RNode) -> Vec<EventHandler>;
822 fn native_view_of(&self, node: RNode) -> Option<RNode>;
826 fn wrappers_above(&self, node: RNode) -> Vec<RNode>;
829 fn gesture_target(&self, node: RNode, kind: day_spec::GestureKind) -> Option<RNode>;
837 fn set_element_outer(&mut self, inner: RNode, outer: RNode);
841 fn set_id(&mut self, node: RNode, id: String);
842 fn mounted_id_families(&self) -> Vec<String>;
844 fn set_grid_facts(&mut self, node: RNode, facts: GridFacts);
847 fn forward_whole_row(&mut self, wrapper: RNode, inner: RNode);
850 fn set_a11y(&mut self, node: RNode, a11y: A11yProps);
851 fn announce(&mut self, text: &str, urgent: bool);
853 fn enable_gesture(&mut self, node: RNode, kind: day_spec::GestureKind);
856 fn focus_node(&mut self, node: RNode, focused: bool);
858 fn set_focusable(&mut self, node: RNode, focusable: bool);
861 fn set_probe_focused(&mut self, node: RNode, focused: bool);
863 fn set_probe_selected(&mut self, node: RNode, selected: Option<usize>);
868 fn set_probe_value(&mut self, node: RNode, value: f64, text: String);
873 fn set_app_menu(&mut self, items: Vec<day_spec::MenuItem>);
874 fn set_context_menu(&mut self, node: RNode, items: Vec<day_spec::MenuItem>);
875 fn edit_window_toolbar(&mut self, root: RNode, ops: Vec<day_spec::ToolbarOp>) -> bool;
880 fn patch_window_toolbar(&mut self, root: RNode, patch: day_spec::ToolbarPatch) -> bool;
882 fn scroll_to_target(&mut self, node: RNode, target: &ScrollTarget, animated: bool) -> bool;
886 fn scroll_reveal(&mut self, node: RNode, animated: bool) -> bool;
889 fn patch(&mut self, node: RNode, patch: Box<dyn Any>, affects_size: bool);
890 fn replay(&mut self, node: RNode, ops: Vec<DrawOp>);
891 fn request_frame(&mut self, root: RNode, cb: day_spec::FrameCallback) -> day_spec::CancelFrame;
892 fn set_implicit_anim(&mut self, node: RNode, anim: Option<day_spec::AnimSpec>);
896 fn set_node_opacity(&mut self, node: RNode, opacity: f64);
899 fn set_node_transform(&mut self, node: RNode, t: day_spec::Transform);
902 fn set_node_selectable(&mut self, node: RNode, selectable: bool);
905 fn set_node_input_traits(&mut self, node: RNode, traits: &day_spec::InputTraits);
909 fn set_node_cursor(&mut self, node: RNode, cursor: day_spec::Cursor);
912 fn set_node_drag_region(&mut self, node: RNode, drag: bool);
915 fn note_node_tweaked(&mut self, node: RNode) {
919 let _ = node;
920 }
921 fn mark_needs_measure(&mut self, node: RNode);
922 fn mark_layout_dirty(&mut self);
923 fn invalidate_all_measurements(&mut self);
927 fn layout_if_needed(&mut self);
928 fn set_window_size(&mut self, s: Size);
929 fn set_route(&mut self, route: &str);
933 fn child_count(&self, node: RNode) -> usize;
934 fn first_child(&self, node: RNode) -> Option<RNode>;
935 fn parent_of(&self, node: RNode) -> Option<RNode>;
938 fn node_kind(&self, node: RNode) -> Option<PieceKind>;
939 fn node_id(&self, node: RNode) -> Option<String>;
942 fn node_handle_any(&self, node: RNode) -> Option<Box<dyn Any>>;
948 fn node_frame(&self, node: RNode) -> Option<Rect>;
949 fn node_probe(&self, node: RNode) -> Option<NodeProbe>;
950 fn node_a11y(&self, node: RNode) -> Option<A11yProps>;
952 fn read_native(&self, node: RNode) -> Option<day_spec::NativeSnapshot>;
955 fn a11y_nodes(&self) -> Vec<(String, PieceKind, A11yProps, day_spec::NativeSnapshot)>;
958 fn find_by_id(&self, id: &str) -> Option<RNode>;
959 fn toggle_sidebar(&mut self, _host: RNode) -> bool {
964 false
965 }
966 fn native_back(&mut self) -> bool {
970 false
971 }
972 fn prepare_snapshot(
973 &mut self,
974 root: Option<RNode>,
975 revision: u32,
976 ) -> Result<day_spec::capture::Readiness, String>;
977 fn snapshot(&mut self) -> Result<Vec<u8>, String>;
978 fn snapshot_origin(&mut self) -> Option<Point>;
981 fn snapshot_chrome(&mut self) -> Result<Vec<u8>, String>;
983
984 fn decode_image(&mut self, req: u64, id: day_spec::BitmapId, bytes: &[u8]);
990 fn image_info(&mut self, id: day_spec::BitmapId) -> Option<day_spec::BitmapInfo>;
992 fn image_properties(&mut self, id: day_spec::BitmapId) -> Option<day_spec::ImageProperties>;
994 fn encode_image(&mut self, req: u64, id: day_spec::BitmapId, spec: &day_spec::EncodeSpec);
996 fn image_encode_formats(&mut self) -> Vec<day_spec::ImageFormat>;
998 fn release_image(&mut self, id: day_spec::BitmapId);
1000 fn render_bitmap(
1003 &mut self,
1004 req: u64,
1005 id: day_spec::BitmapId,
1006 ops: &[day_spec::DrawOp],
1007 size: Size,
1008 scale: f64,
1009 );
1010 fn render_pdf(&mut self, req: u64, pages: &[day_spec::PdfPage], info: &day_spec::PdfInfo);
1013 fn window_image_support(&mut self) -> day_spec::Support;
1015 fn font_families(&mut self) -> Vec<day_spec::FontFamilyInfo>;
1017 fn measure_text(
1019 &mut self,
1020 text: &str,
1021 size: f64,
1022 font: &day_spec::CanvasFont,
1023 ) -> Option<day_spec::TextMetrics>;
1024 fn ui_idle(&mut self) -> bool;
1026 fn root_node(&self) -> RNode;
1027
1028 fn open_window_root(&mut self, options: &WindowOptions, kind: WindowKind) -> WindowRootReply;
1037 fn adopt_window_root(&mut self, root: RNode, raw: day_spec::RawHandle, size: Size) -> bool;
1041 fn remove_window_root(&mut self, root: RNode);
1044 fn set_root_size(&mut self, root: RNode, s: Size);
1046
1047 fn fit_window(&mut self, root: RNode, size: Size);
1051 fn add_extra_layout_root(&mut self, node: RNode, size: Size);
1057 fn drop_extra_layout_root(&mut self, node: RNode);
1060 fn close_native_window(&mut self, root: RNode);
1063 fn quit_app(&mut self);
1065 fn focus_native_window(&mut self, root: RNode);
1067 fn set_native_window_title(&mut self, root: RNode, title: &str);
1069 fn apply_window(&mut self, root: RNode, change: &day_spec::WindowChange);
1072 fn set_dock_menu(&mut self, items: &[day_spec::MenuItem]);
1074 fn set_status_items(&mut self, items: &[day_spec::StatusItemSpec]);
1076 fn set_app_progress(&mut self, progress: day_spec::AppProgress);
1078 fn set_dock_visible(&mut self, visible: bool);
1080 fn set_keep_running(&mut self, keep: bool);
1082 fn native_window_frame(&self, root: RNode) -> Option<day_spec::Rect>;
1084 fn monitors(&self) -> Vec<day_spec::Monitor>;
1086 fn set_launcher_shortcuts(&mut self, shortcuts: &[day_spec::LauncherShortcut]);
1088 fn snapshot_of(&mut self, root: RNode) -> Result<Vec<u8>, String>;
1090 fn capability(&self, cap: Cap) -> Support;
1092 fn attach_refresh(&mut self, node: RNode, host: NodeId);
1094 fn set_presented(&mut self, node: RNode, visible: bool);
1095 fn supports_lifecycle(&self, phase: day_spec::Lifecycle) -> bool;
1097 fn present(&mut self, req: u64, spec: &present::PresentSpec);
1099 fn dismiss(&mut self, req: u64);
1101 fn open_url(&mut self, url: &str);
1103 fn share_support(&self) -> Support;
1104 fn share_url(&mut self, url: &str, title: &str) -> bool;
1105 fn application_handlers(
1106 &mut self,
1107 query: &day_spec::applications::HandlerQuery,
1108 ) -> Result<day_spec::applications::ApplicationHandlers, day_spec::applications::ApplicationError>;
1109 fn open_url_with_application(
1110 &mut self,
1111 url: &str,
1112 application: &str,
1113 completion: day_spec::applications::OpenApplicationCompletion,
1114 );
1115
1116 fn defer_system_gestures(&mut self, edges: day_spec::Edges);
1118 fn set_status_bar_hidden(&mut self, hidden: bool);
1121
1122 fn install_list(&mut self, node: RNode, driver: crate::list::ListDriver);
1126 fn list_prepare_cell(
1130 &mut self,
1131 node: RNode,
1132 key: usize,
1133 cell: RawHandle,
1134 header: bool,
1135 ) -> crate::list::CellStep;
1136 fn list_store_cell(
1138 &mut self,
1139 node: RNode,
1140 key: usize,
1141 anchor: RNode,
1142 built: crate::list::BuiltRow,
1143 header: bool,
1144 );
1145 fn list_layout_cell(&mut self, node: RNode, key: usize);
1147 fn list_layout_cell_width(&mut self, node: RNode, key: usize, width: f64);
1151 fn list_cell_height(&self, node: RNode, key: usize) -> f64;
1154 fn list_cell_keys(&self, node: RNode) -> Vec<usize>;
1158 fn list_recycle_cell(&mut self, node: RNode, key: usize);
1162 fn list_release_cell(&mut self, node: RNode, key: usize);
1164 fn list_pin_cell(&mut self, node: RNode, key: usize);
1167 fn list_reload(&mut self, node: RNode);
1169 fn list_splice(&mut self, node: RNode, deltas: Vec<day_spec::props::RowDelta>);
1170 fn set_undo_state(&mut self, state: &day_spec::UndoState);
1171 fn set_edit_state(&mut self, state: &day_spec::EditState);
1172 fn modifiers(&mut self) -> day_spec::Modifiers;
1173 fn list_scroll_to_end(&mut self, node: RNode);
1175 fn list_scroll_to_row(&mut self, node: RNode, row: usize);
1177 fn list_set_selected(&mut self, node: RNode, rows: Vec<usize>);
1180 fn list_set_editing(&mut self, node: RNode, on: bool);
1183 fn list_driver(&mut self, node: RNode) -> Option<std::rc::Rc<crate::list::ListDriver>>;
1186
1187 fn install_tree(&mut self, node: RNode, driver: crate::tree_driver::TreeDriver);
1191 fn tree_prepare_cell(
1193 &mut self,
1194 node: RNode,
1195 key: usize,
1196 cell: RawHandle,
1197 ) -> crate::tree_driver::TreeCellStep;
1198 fn tree_store_cell(
1200 &mut self,
1201 node: RNode,
1202 key: usize,
1203 anchor: RNode,
1204 built: crate::tree_driver::TreeBuiltRow,
1205 );
1206 fn tree_layout_cell(&mut self, node: RNode, key: usize);
1208 fn tree_layout_cell_width(&mut self, node: RNode, key: usize, width: f64);
1210 fn tree_layout_cell_size(&mut self, node: RNode, key: usize, size: Size);
1211 fn tree_cell_height(&self, node: RNode, key: usize) -> f64;
1214 fn tree_recycle_cell(&mut self, node: RNode, key: usize);
1218 fn tree_reload(&mut self, node: RNode);
1221 fn tree_set_expanded(&mut self, node: RNode, token: u64, expanded: bool);
1223 fn tree_set_selected(&mut self, node: RNode, tokens: Vec<u64>);
1225 fn tree_reveal(&mut self, node: RNode, token: u64);
1227 fn tree_driver(&mut self, node: RNode) -> Option<std::rc::Rc<crate::tree_driver::TreeDriver>>;
1230 fn set_drag_source(&mut self, node: RNode, source: day_spec::transfer::Source);
1232 fn set_drop_target(&mut self, node: RNode, target: day_spec::transfer::Target);
1233 fn set_context_menu_fn(&mut self, node: RNode, f: day_spec::ContextMenuFn);
1235 fn context_menu_of(&mut self, node: RNode) -> Option<day_spec::ContextMenuFn>;
1240}
1241
1242impl<B: Toolkit> TreeOps for Tree<B> {
1243 fn set_presented(&mut self, node: RNode, visible: bool) {
1244 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
1245 self.toolkit.set_presented(&handle, visible);
1246 }
1247 }
1248
1249 fn attach_refresh(&mut self, node: RNode, host: NodeId) {
1250 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
1251 self.toolkit.attach_refresh(&handle, host);
1252 }
1253 }
1254
1255 fn capability(&self, cap: Cap) -> Support {
1256 self.toolkit.capability(cap)
1257 }
1258
1259 fn supports_lifecycle(&self, phase: day_spec::Lifecycle) -> bool {
1260 self.toolkit.supports_lifecycle(phase)
1261 }
1262
1263 fn present(&mut self, req: u64, spec: &present::PresentSpec) {
1264 self.toolkit.present(req, spec);
1265 }
1266
1267 fn dismiss(&mut self, req: u64) {
1268 self.toolkit.dismiss(req);
1269 }
1270
1271 fn open_url(&mut self, url: &str) {
1272 self.toolkit.open_url(url);
1273 }
1274 fn share_support(&self) -> Support {
1275 self.toolkit.share_support()
1276 }
1277 fn share_url(&mut self, url: &str, title: &str) -> bool {
1278 self.toolkit.share_url(url, title)
1279 }
1280
1281 fn application_handlers(
1282 &mut self,
1283 query: &day_spec::applications::HandlerQuery,
1284 ) -> Result<day_spec::applications::ApplicationHandlers, day_spec::applications::ApplicationError>
1285 {
1286 self.toolkit.application_handlers(query)
1287 }
1288
1289 fn open_url_with_application(
1290 &mut self,
1291 url: &str,
1292 application: &str,
1293 completion: day_spec::applications::OpenApplicationCompletion,
1294 ) {
1295 self.toolkit
1296 .open_url_with_application(url, application, completion);
1297 }
1298
1299 fn set_appearance(&mut self, dark: Option<bool>) {
1300 self.toolkit.set_appearance(dark);
1301 }
1302
1303 fn set_app_badge(&mut self, badge: &day_spec::AppBadge) {
1304 self.toolkit.set_app_badge(badge);
1305 }
1306
1307 fn defer_system_gestures(&mut self, edges: day_spec::Edges) {
1308 self.toolkit.defer_system_gestures(edges);
1309 }
1310
1311 fn set_status_bar_hidden(&mut self, hidden: bool) {
1312 self.toolkit.set_status_bar_hidden(hidden);
1313 }
1314
1315 fn create_node(
1316 &mut self,
1317 kind: PieceKind,
1318 props: &dyn Any,
1319 layout: Rc<dyn Layout>,
1320 flex: Flex,
1321 native: bool,
1322 is_boundary: bool,
1323 scope: Scope,
1324 ) -> RNode {
1325 let mut probe = NodeProbe {
1326 enabled: true,
1327 ..Default::default()
1328 };
1329 {
1330 use day_spec::props::*;
1331 if let Some(p) = props.downcast_ref::<LabelProps>() {
1332 probe.text = p.text.clone();
1333 } else if let Some(p) = props.downcast_ref::<NavMenuProps>() {
1334 probe.selected = p.selected.map(|i| i as i64).unwrap_or(-1);
1335 probe.value = probe.selected as f64;
1336 } else if let Some(p) = props.downcast_ref::<ButtonProps>() {
1337 probe.text = p.title.clone();
1338 probe.enabled = p.enabled;
1339 probe.flag = p.selected;
1341 } else if let Some(p) = props.downcast_ref::<ToggleProps>() {
1342 probe.flag = p.on;
1343 probe.enabled = p.enabled;
1344 } else if let Some(p) = props.downcast_ref::<SliderProps>() {
1345 probe.value = p.value;
1346 probe.enabled = p.enabled;
1347 } else if let Some(p) = props.downcast_ref::<TextFieldProps>() {
1348 probe.text = p.text.clone();
1349 probe.enabled = p.enabled;
1350 } else if let Some(p) = props.downcast_ref::<ProgressProps>() {
1351 probe.flag = p.value.is_none();
1353 probe.value = p.value.unwrap_or(0.0);
1354 } else if let Some(p) = props.downcast_ref::<PickerProps>() {
1355 probe.selected = p.selected as i64;
1356 probe.value = p.selected as f64;
1357 probe.text = p.options.get(p.selected).cloned().unwrap_or_default();
1360 probe.options = p.options.clone();
1361 probe.enabled = p.enabled;
1362 } else if let Some(p) = props.downcast_ref::<TextAreaProps>() {
1363 probe.text = p.text.clone();
1364 }
1365 }
1366 let node = self.nodes.insert(NodeData {
1367 kind,
1368 handle: None,
1369 parent: RNode::null(),
1370 children: Vec::new(),
1371 layout,
1372 flex,
1373 scope,
1374 id: None,
1375 parked_id: None,
1376 a11y: Default::default(),
1377 cache: Vec::new(),
1378 baseline_cache: None,
1379 probe,
1380 needs_measure: true,
1381 needs_layout: true,
1382 last_native_frame: None,
1383 scroll_content: None,
1384 implicit_anim: None,
1385 tweaked: false,
1386 is_boundary,
1387 });
1388 if native {
1389 let h = self.toolkit.realize(kind, props, rnode_to_id(node));
1390 self.nodes[node].handle = Some(h);
1391 }
1392 node
1393 }
1394
1395 fn attach(&mut self, parent: RNode, child: RNode) {
1396 let index = self
1397 .nodes
1398 .get(parent)
1399 .map(|p| p.children.len())
1400 .unwrap_or(0);
1401 self.attach_impl(parent, child, index);
1402 }
1403
1404 fn native_window_tabs(&self) -> bool {
1405 self.toolkit.native_window_tabs()
1406 }
1407 fn native_document_tabs(&self) -> bool {
1408 self.toolkit.native_document_tabs()
1409 }
1410 fn group_windows(&mut self, roots: &[RNode]) {
1411 let hosts = roots
1412 .iter()
1413 .filter_map(|r| self.nodes.get(*r)?.handle.clone())
1414 .collect::<Vec<_>>();
1415 self.toolkit.group_windows(&hosts);
1416 }
1417 fn window_tab_order(&self, root: RNode) -> Vec<day_spec::NodeId> {
1418 self.nodes
1419 .get(root)
1420 .and_then(|n| n.handle.as_ref())
1421 .map(|h| self.toolkit.window_tab_order(h))
1422 .unwrap_or_default()
1423 }
1424 fn reparent(&mut self, child: RNode, parent: RNode) -> bool {
1425 if !self.nodes.contains_key(child) || !self.nodes.contains_key(parent) {
1426 return false;
1427 }
1428 let old = self.nodes[child].parent;
1429 if old == parent {
1430 return true;
1431 }
1432 if self.windows.iter().any(|w| w.root == child) {
1434 return false;
1435 }
1436 let mut up = parent;
1437 while let Some(n) = self.nodes.get(up) {
1438 if up == child {
1439 return false;
1440 }
1441 up = n.parent;
1442 }
1443 let ancestor = self.native_ancestor(old);
1444 let handle = self.nodes.get(ancestor).and_then(|n| n.handle.clone());
1445 let mut roots = Vec::new();
1446 if self.nodes[child].handle.is_some() {
1447 roots.push(child);
1448 } else {
1449 self.native_descendants(child, &mut roots);
1450 }
1451 if let Some(h) = handle {
1452 for r in roots {
1453 self.toolkit
1455 .remove(&h, self.nodes[r].handle.as_ref().unwrap());
1456 }
1457 }
1458 if let Some(p) = self.nodes.get_mut(old) {
1459 p.children.retain(|n| *n != child);
1460 }
1461 self.mark_needs_measure_impl(old);
1462 self.attach(parent, child);
1463 self.layout_dirty = true;
1464 true
1465 }
1466
1467 fn attach_at(&mut self, parent: RNode, child: RNode, index: usize) {
1468 self.attach_impl(parent, child, index);
1469 }
1470
1471 fn reorder_children(&mut self, parent: RNode, order: Vec<RNode>) {
1472 if self.nodes.get(parent).is_some_and(|p| p.children == order) {
1480 return;
1481 }
1482 if let Some(p) = self.nodes.get_mut(parent) {
1483 p.children = order;
1484 }
1485 let ancestor = self.native_ancestor(parent);
1487 let anc_handle = self.nodes[ancestor]
1488 .handle
1489 .clone()
1490 .expect("native ancestor");
1491 let mut desired = Vec::new();
1492 self.native_descendants(ancestor, &mut desired);
1493 for (i, r) in desired.iter().enumerate() {
1494 let h = self.nodes[*r].handle.clone().unwrap();
1495 self.toolkit.move_child(&anc_handle, &h, i);
1496 }
1497 self.mark_needs_measure_impl(parent);
1498 }
1499
1500 fn remove_subtree(&mut self, node: RNode) {
1501 debug_assert!(
1504 !self.windows.iter().any(|w| w.root == node),
1505 "remove_subtree called with a window root"
1506 );
1507 self.remove_subtree_impl(node);
1508 }
1509
1510 fn on_event(&mut self, node: RNode, h: EventHandler) {
1511 self.handlers.entry(node).or_default().push(h);
1512 }
1513
1514 fn handlers_for(&self, node: RNode) -> Vec<EventHandler> {
1515 self.handlers.get(&node).cloned().unwrap_or_default()
1516 }
1517 fn native_view_of(&self, node: RNode) -> Option<RNode> {
1518 let mut cur = node;
1519 for _ in 0..16 {
1520 let n = self.nodes.get(cur)?;
1521 if n.handle.is_some() {
1522 return Some(cur);
1523 }
1524 match n.children.as_slice() {
1525 [only] => cur = *only,
1526 _ => return None,
1527 }
1528 }
1529 None
1530 }
1531 fn gesture_target(&self, node: RNode, kind: day_spec::GestureKind) -> Option<RNode> {
1532 let has = |n: RNode| self.gestures.get(&n).is_some_and(|k| k.contains(&kind));
1533 if has(node) {
1534 return Some(node);
1535 }
1536 let mut cur = node;
1539 let mut bound = self.element_outer.get(&node).copied();
1540 while let Some(outer) = bound {
1541 if cur == outer {
1542 bound = self.element_outer.get(&outer).copied();
1543 continue;
1544 }
1545 let Some(parent) = self.nodes.get(cur).map(|n| n.parent) else {
1546 break;
1547 };
1548 if parent == cur || !self.nodes.contains_key(parent) {
1549 break;
1550 }
1551 cur = parent;
1552 if has(cur) {
1553 return Some(cur);
1554 }
1555 }
1556 self.wrappers_above(cur).into_iter().find(|w| has(*w))
1557 }
1558
1559 fn set_element_outer(&mut self, inner: RNode, outer: RNode) {
1560 if inner != outer {
1561 self.element_outer.insert(inner, outer);
1562 }
1563 }
1564
1565 fn wrappers_above(&self, node: RNode) -> Vec<RNode> {
1566 let mut out = Vec::new();
1567 let mut cur = node;
1568 for _ in 0..16 {
1569 let Some(parent) = self.nodes.get(cur).map(|n| n.parent) else {
1570 break;
1571 };
1572 match self.nodes.get(parent) {
1573 Some(p) if p.handle.is_none() && p.children.len() == 1 && parent != cur => {
1574 out.push(parent);
1575 cur = parent;
1576 }
1577 _ => break,
1578 }
1579 }
1580 out
1581 }
1582 fn node_exists(&self, node: RNode) -> bool {
1583 self.nodes.contains_key(node)
1584 }
1585 fn dark_mode(&mut self) -> bool {
1586 self.toolkit.dark_mode()
1587 }
1588 fn reduce_motion(&mut self) -> bool {
1589 self.toolkit.reduce_motion()
1590 }
1591
1592 fn mounted_id_families(&self) -> Vec<String> {
1593 self.mounted_id_families.iter().cloned().collect()
1594 }
1595 fn set_id(&mut self, node: RNode, id: String) {
1596 if self.nodes.contains_key(node) {
1597 self.mounted_id_families
1598 .insert(crate::element_id::family_of(&id).to_owned());
1599 }
1600 if let Some(n) = self.nodes.get_mut(node) {
1601 n.id = Some(id.clone());
1602 n.a11y.merge(&A11yProps {
1603 identifier: Some(id),
1604 ..Default::default()
1605 });
1606 if let Some(h) = n.handle.clone() {
1607 self.toolkit.set_a11y(&h, &n.a11y);
1608 }
1609 }
1610 }
1611
1612 fn forward_whole_row(&mut self, wrapper: RNode, inner: RNode) {
1613 let whole = self.nodes.get(inner).is_some_and(|n| n.flex.whole_row);
1614 if let Some(n) = self.nodes.get_mut(wrapper) {
1615 n.flex.whole_row = whole;
1616 }
1617 }
1618
1619 fn set_grid_facts(&mut self, node: RNode, facts: GridFacts) {
1620 if let Some(n) = self.nodes.get_mut(node) {
1621 let g = &mut n.flex.grid;
1622 if facts.is_row {
1623 g.is_row = true;
1624 }
1625 if facts.row_valign.is_some() {
1626 g.row_valign = facts.row_valign;
1627 }
1628 if facts.col_span != 0 {
1629 g.col_span = facts.col_span;
1630 }
1631 if facts.align.is_some() {
1632 g.align = facts.align;
1633 }
1634 }
1635 }
1636
1637 fn announce(&mut self, text: &str, urgent: bool) {
1638 self.toolkit.announce(text, urgent);
1639 }
1640
1641 fn set_a11y(&mut self, node: RNode, a11y: A11yProps) {
1642 if let Some(n) = self.nodes.get_mut(node) {
1643 n.a11y.merge(&a11y);
1646 if n.kind == kinds::CANVAS
1650 && let Some(text) = n.a11y.value.clone().or_else(|| n.a11y.label.clone())
1651 {
1652 n.probe.text = text;
1653 }
1654 if let Some(h) = n.handle.clone() {
1655 self.toolkit.set_a11y(&h, &n.a11y);
1656 }
1657 }
1658 }
1659
1660 fn enable_gesture(&mut self, node: RNode, kind: day_spec::GestureKind) {
1661 let kinds = self.gestures.entry(node).or_default();
1662 if !kinds.contains(&kind) {
1663 kinds.push(kind);
1664 }
1665 let mut cur = node;
1671 for _ in 0..16 {
1672 let Some(n) = self.nodes.get(cur) else { return };
1673 if let Some(h) = n.handle.clone() {
1674 if kind == day_spec::GestureKind::Tap && n.kind == kinds::BUTTON {
1675 log::warn!(
1676 "tap gesture attached to native button {:?} (id: {:?}): \
1677 use .action(...) instead of .on_tap(...) or .on_tap_at(...). \
1678 A separate gesture recognizer can delay pressed feedback and \
1679 bypass native keyboard, accessibility, and disabled behavior",
1680 rnode_to_id(cur),
1681 n.id,
1682 );
1683 }
1684 self.toolkit.enable_gesture(&h, rnode_to_id(node), kind);
1685 return;
1686 }
1687 match n.children.as_slice() {
1688 [only] => cur = *only,
1689 _ => break,
1690 }
1691 }
1692 log::warn!(
1693 "enable_gesture({kind:?}) found no native view under the target node — \
1694 the gesture will not fire natively (attach it to a piece with a native handle)"
1695 );
1696 }
1697
1698 fn focus_node(&mut self, node: RNode, focused: bool) {
1699 let mut cur = node;
1702 for _ in 0..16 {
1703 let Some(n) = self.nodes.get(cur) else { return };
1704 if let Some(h) = n.handle.clone() {
1705 self.toolkit.focus(&h, rnode_to_id(node), focused);
1706 return;
1707 }
1708 match n.children.as_slice() {
1709 [only] => cur = *only,
1710 _ => return,
1711 }
1712 }
1713 }
1714
1715 fn set_focusable(&mut self, node: RNode, focusable: bool) {
1716 let mut cur = node;
1720 for _ in 0..16 {
1721 let Some(n) = self.nodes.get(cur) else { return };
1722 if let Some(h) = n.handle.clone() {
1723 self.toolkit.set_focusable(&h, rnode_to_id(node), focusable);
1724 return;
1725 }
1726 match n.children.as_slice() {
1727 [only] => cur = *only,
1728 _ => break,
1729 }
1730 }
1731 log::warn!(
1732 "set_focusable found no native view under the target node — \
1733 the piece will not join the focus order"
1734 );
1735 }
1736
1737 fn set_probe_focused(&mut self, node: RNode, focused: bool) {
1738 if let Some(n) = self.nodes.get_mut(node) {
1739 n.probe.focused = focused;
1740 }
1741 for w in self.wrappers_above(node) {
1746 if let Some(p) = self.nodes.get_mut(w) {
1747 p.probe.focused = focused;
1748 }
1749 }
1750 let mut cur = node;
1756 for _ in 0..16 {
1757 let Some(n) = self.nodes.get(cur) else { break };
1758 if n.handle.is_some() && cur != node {
1759 if let Some(p) = self.nodes.get_mut(cur) {
1760 p.probe.focused = focused;
1761 }
1762 break;
1763 }
1764 let next = match n.children.as_slice() {
1765 [only] => *only,
1766 _ => break,
1767 };
1768 if let Some(p) = self.nodes.get_mut(next) {
1769 p.probe.focused = focused;
1770 }
1771 cur = next;
1772 }
1773 FOCUSED.with(|f| match focused {
1778 true => f.set(Some(node)),
1779 false if f.get() == Some(node) => f.set(None),
1780 false => {}
1781 });
1782 }
1783
1784 fn set_probe_selected(&mut self, node: RNode, selected: Option<usize>) {
1785 if let Some(n) = self.nodes.get_mut(node) {
1786 n.probe.selected = selected.map(|i| i as i64).unwrap_or(-1);
1789 n.probe.value = n.probe.selected as f64;
1790 }
1791 }
1792
1793 fn set_probe_value(&mut self, node: RNode, value: f64, text: String) {
1794 if let Some(n) = self.nodes.get_mut(node) {
1795 n.probe.value = value;
1796 n.probe.text = text;
1797 }
1798 }
1799
1800 fn set_app_menu(&mut self, items: Vec<day_spec::MenuItem>) {
1801 collect_menu_families(&items, &mut self.mounted_id_families);
1802 self.toolkit.set_app_menu(&items);
1803 }
1804
1805 fn edit_window_toolbar(&mut self, root: RNode, ops: Vec<day_spec::ToolbarOp>) -> bool {
1806 let Some(h) = self.nodes.get(root).and_then(|n| n.handle.clone()) else {
1807 return false;
1808 };
1809 for op in &ops {
1810 if let day_spec::ToolbarOp::Insert { item, .. } = op {
1811 self.mounted_id_families
1812 .insert(crate::element_id::family_of(&item.id).to_owned());
1813 }
1814 }
1815 self.toolkit.edit_toolbar(&h, &ops)
1816 }
1817
1818 fn patch_window_toolbar(&mut self, root: RNode, patch: day_spec::ToolbarPatch) -> bool {
1819 let Some(h) = self.nodes.get(root).and_then(|n| n.handle.clone()) else {
1820 return false;
1821 };
1822 self.toolkit.update_toolbar(&h, &patch);
1823 true
1824 }
1825
1826 fn set_context_menu(&mut self, node: RNode, items: Vec<day_spec::MenuItem>) {
1827 let target = if self.nodes.get(node).is_some_and(|n| n.handle.is_some()) {
1832 Some(node)
1833 } else {
1834 let mut roots = Vec::new();
1835 self.native_descendants(node, &mut roots);
1836 roots.first().copied()
1837 };
1838 let Some(t) = target else {
1839 log::warn!("context_menu on a subtree with no native view — menu dropped");
1840 return;
1841 };
1842 collect_menu_families(&items, &mut self.mounted_id_families);
1843 let model = ContextMenuModel::Items(items.clone());
1844 self.context_menus.insert(node, model.clone());
1845 self.context_menus.insert(t, model);
1846 if let Some(h) = self.nodes.get(t).and_then(|n| n.handle.clone()) {
1847 self.toolkit.set_context_menu(&h, rnode_to_id(t), &items);
1848 }
1849 }
1850
1851 fn scroll_to_target(&mut self, node: RNode, target: &ScrollTarget, animated: bool) -> bool {
1852 if let ScrollTarget::Id(id) = target {
1854 let Some(el) = self.find_by_id(id) else {
1855 return false;
1856 };
1857 return self.scroll_reveal(el, animated);
1858 }
1859 let Some(n) = self.nodes.get(node) else {
1860 return false;
1861 };
1862 if n.kind != kinds::SCROLL {
1863 return false;
1864 }
1865 let Some(h) = n.handle.clone() else {
1866 return false;
1867 };
1868 let viewport = n.last_native_frame.map(|f| f.size).unwrap_or(Size::ZERO);
1869 let content = n.scroll_content.unwrap_or(viewport);
1870 let rect = match target {
1873 ScrollTarget::Top | ScrollTarget::Leading => Rect::new(0.0, 0.0, 1.0, 1.0),
1874 ScrollTarget::Bottom => Rect::new(0.0, (content.height - 1.0).max(0.0), 1.0, 1.0),
1875 ScrollTarget::Trailing => Rect::new((content.width - 1.0).max(0.0), 0.0, 1.0, 1.0),
1876 ScrollTarget::Offset(p) => Rect::new(p.x, p.y, viewport.width, viewport.height),
1879 ScrollTarget::Id(_) => unreachable!("routed to scroll_reveal above"),
1880 };
1881 self.toolkit
1882 .scroll_to(&h, rect, animated && !crate::reduce_motion_now());
1883 true
1884 }
1885
1886 fn scroll_reveal(&mut self, node: RNode, animated: bool) -> bool {
1887 let Some(mut rect) = self.nodes.get(node).and_then(|n| n.last_native_frame) else {
1891 return false;
1892 };
1893 let mut anc = match self.nodes.get(node) {
1894 Some(n) => n.parent,
1895 None => return false,
1896 };
1897 loop {
1898 let Some(a) = self.nodes.get(anc) else {
1899 return false; };
1901 if a.kind == kinds::SCROLL {
1902 let Some(h) = a.handle.clone() else {
1903 return false;
1904 };
1905 self.toolkit
1906 .scroll_to(&h, rect, animated && !crate::reduce_motion_now());
1907 return true;
1908 }
1909 if a.handle.is_some()
1910 && let Some(f) = a.last_native_frame
1911 {
1912 rect.origin.x += f.origin.x;
1913 rect.origin.y += f.origin.y;
1914 }
1915 anc = a.parent;
1916 }
1917 }
1918
1919 fn patch(&mut self, node: RNode, patch: Box<dyn Any>, affects_size: bool) {
1920 {
1921 use day_spec::props::*;
1922 if let Some(n) = self.nodes.get_mut(node) {
1923 if let Some(p) = patch.downcast_ref::<LabelPatch>() {
1924 match p {
1928 LabelPatch::Text(t) => n.probe.text = t.clone(),
1929 LabelPatch::Runs(t, _) => n.probe.text = t.clone(),
1930 LabelPatch::Font(_) | LabelPatch::Color(_) => {}
1931 }
1932 } else if let Some(p) = patch.downcast_ref::<ButtonPatch>() {
1933 match p {
1934 ButtonPatch::Title(t) => n.probe.text = t.clone(),
1935 ButtonPatch::Content(c) => n.probe.text = c.title.clone(),
1936 ButtonPatch::Enabled(e) => n.probe.enabled = *e,
1937 ButtonPatch::Selected(on) => n.probe.flag = *on,
1938 ButtonPatch::Style(_) => {}
1940 }
1941 } else if let Some(p) = patch.downcast_ref::<TogglePatch>() {
1942 match p {
1943 TogglePatch::On(v) => n.probe.flag = *v,
1944 TogglePatch::Enabled(e) => n.probe.enabled = *e,
1945 }
1946 } else if let Some(p) = patch.downcast_ref::<SliderPatch>() {
1947 match p {
1948 SliderPatch::Value(v) => n.probe.value = *v,
1949 SliderPatch::Enabled(e) => n.probe.enabled = *e,
1950 }
1951 } else if let Some(p) = patch.downcast_ref::<PickerPatch>() {
1952 match p {
1953 PickerPatch::Selected(i) => {
1954 n.probe.selected = *i as i64;
1955 n.probe.value = *i as f64;
1956 }
1957 PickerPatch::Options(opts) => n.probe.options = opts.clone(),
1958 PickerPatch::Enabled(e) => n.probe.enabled = *e,
1959 }
1960 let i = n.probe.selected.max(0) as usize;
1961 n.probe.text = n.probe.options.get(i).cloned().unwrap_or_default();
1962 } else if let Some(TextAreaPatch::SetText(t)) =
1963 patch.downcast_ref::<TextAreaPatch>()
1964 {
1965 n.probe.text = t.clone();
1966 } else if let Some(ProgressPatch::Value(v)) = patch.downcast_ref::<ProgressPatch>()
1967 {
1968 n.probe.flag = v.is_none();
1969 n.probe.value = v.unwrap_or(0.0);
1970 } else if let Some(p) = patch.downcast_ref::<TextFieldPatch>() {
1971 match p {
1972 TextFieldPatch::Text { text, .. } => n.probe.text = text.clone(),
1973 TextFieldPatch::Enabled(e) => n.probe.enabled = *e,
1974 _ => {}
1975 }
1976 } else if let Some(NavMenuPatch::Selected(sel)) =
1977 patch.downcast_ref::<NavMenuPatch>()
1978 {
1979 n.probe.selected = sel.map(|i| i as i64).unwrap_or(-1);
1980 n.probe.value = n.probe.selected as f64;
1981 }
1982 }
1983 }
1984 let Some(n) = self.nodes.get(node) else {
1985 return;
1986 };
1987 let kind = n.kind;
1988 if let Some(h) = n.handle.clone() {
1989 let anim = self.resolve_anim(node);
1990 self.toolkit.update(&h, kind, patch.as_ref(), anim.as_ref());
1991 }
1992 if affects_size {
1993 self.mark_needs_measure_impl(node);
1994 }
1995 }
1996
1997 fn set_implicit_anim(&mut self, node: RNode, anim: Option<day_spec::AnimSpec>) {
1998 let had = self
1999 .nodes
2000 .get(node)
2001 .map(|n| n.implicit_anim.is_some())
2002 .unwrap_or(false);
2003 if let Some(n) = self.nodes.get_mut(node) {
2004 n.implicit_anim = anim;
2005 } else {
2006 return;
2007 }
2008 match (had, anim.is_some()) {
2009 (false, true) => self.implicit_anim_count += 1,
2010 (true, false) => self.implicit_anim_count = self.implicit_anim_count.saturating_sub(1),
2011 _ => {}
2012 }
2013 }
2014
2015 fn set_node_opacity(&mut self, node: RNode, opacity: f64) {
2016 let Some(h) = self.nodes.get(node).and_then(|n| n.handle.clone()) else {
2017 return;
2018 };
2019 let anim = self.resolve_anim(node);
2020 self.toolkit.set_opacity(&h, opacity, anim.as_ref());
2021 }
2022
2023 fn set_node_transform(&mut self, node: RNode, t: day_spec::Transform) {
2024 let Some(n) = self.nodes.get(node) else {
2025 return;
2026 };
2027 let Some(h) = n.handle.clone() else {
2028 return;
2029 };
2030 let size = n.last_native_frame.map(|f| f.size).unwrap_or(Size::ZERO);
2033 let anim = self.resolve_anim(node);
2034 self.toolkit.set_transform(&h, t, size, anim.as_ref());
2035 }
2036
2037 fn set_node_selectable(&mut self, node: RNode, selectable: bool) {
2038 let Some(h) = self.nodes.get(node).and_then(|n| n.handle.clone()) else {
2039 return;
2040 };
2041 if let Some(new) = self.toolkit.set_selectable(&h, selectable)
2044 && let Some(n) = self.nodes.get_mut(node)
2045 {
2046 if n.tweaked {
2047 log::warn!(
2048 "`.selectable()` rebuilt this widget as a different native class, \
2049 discarding an earlier tweak's changes — apply `.selectable()` BEFORE the \
2050 tweak so it runs against the widget that ships (docs/tweaks.md)."
2051 );
2052 }
2053 n.handle = Some(new);
2054 }
2055 }
2056
2057 fn set_node_input_traits(&mut self, node: RNode, traits: &day_spec::InputTraits) {
2058 let Some(h) = self.nodes.get(node).and_then(|n| n.handle.clone()) else {
2059 return;
2060 };
2061 if let Some(new) = self.toolkit.set_input_traits(&h, traits)
2064 && let Some(n) = self.nodes.get_mut(node)
2065 {
2066 if n.tweaked {
2067 log::warn!(
2068 "a text field's `.secure()` rebuilt this widget as a different native \
2069 class, discarding an earlier tweak's changes (docs/tweaks.md)."
2070 );
2071 }
2072 n.handle = Some(new);
2073 n.last_native_frame = None;
2074 self.mark_needs_measure_impl(node);
2075 }
2076 }
2077
2078 fn set_node_cursor(&mut self, node: RNode, cursor: day_spec::Cursor) {
2079 let Some(h) = self
2083 .native_view_of(node)
2084 .and_then(|n| self.nodes.get(n))
2085 .and_then(|n| n.handle.clone())
2086 else {
2087 return;
2088 };
2089 self.toolkit.set_cursor(&h, cursor);
2090 }
2091
2092 fn set_node_drag_region(&mut self, node: RNode, drag: bool) {
2093 let Some(h) = self
2095 .native_view_of(node)
2096 .and_then(|n| self.nodes.get(n))
2097 .and_then(|n| n.handle.clone())
2098 else {
2099 log::warn!(
2100 "window_drag_region found no native view under the target node; give the \
2101 region a piece with a native view (a background, a row)"
2102 );
2103 return;
2104 };
2105 self.toolkit.set_drag_region(&h, drag);
2106 }
2107
2108 fn note_node_tweaked(&mut self, node: RNode) {
2109 if let Some(n) = self.nodes.get_mut(node) {
2110 n.tweaked = true;
2111 }
2112 }
2113
2114 fn request_frame(&mut self, root: RNode, cb: day_spec::FrameCallback) -> day_spec::CancelFrame {
2115 if let Some(h) = self.nodes.get(root).and_then(|n| n.handle.clone()) {
2116 self.toolkit.request_frame(&h, cb)
2117 } else {
2118 Box::new(|| {})
2119 }
2120 }
2121
2122 fn replay(&mut self, node: RNode, ops: Vec<DrawOp>) {
2123 let Some(n) = self.nodes.get(node) else {
2124 return;
2125 };
2126 let size = n.last_native_frame.map(|f| f.size).unwrap_or(Size::ZERO);
2127 if let Some(h) = n.handle.clone() {
2128 self.toolkit.replay(&h, &ops, size);
2129 }
2130 }
2131
2132 fn mark_needs_measure(&mut self, node: RNode) {
2133 self.mark_needs_measure_impl(node);
2134 }
2135
2136 fn mark_layout_dirty(&mut self) {
2137 self.layout_dirty = true;
2138 }
2139
2140 fn invalidate_all_measurements(&mut self) {
2141 for (_, n) in self.nodes.iter_mut() {
2142 n.needs_measure = true;
2143 n.needs_layout = true;
2144 n.cache.clear();
2145 }
2146 for state in self.lists.values_mut() {
2150 for cell in state.cells.values_mut() {
2151 cell.laid_width = None;
2152 }
2153 }
2154 for state in self.trees.values_mut() {
2155 for cell in state.cells.values_mut() {
2156 cell.laid_width = None;
2157 }
2158 }
2159 self.layout_dirty = true;
2160 }
2161
2162 fn layout_if_needed(&mut self) {
2163 if !self.layout_dirty {
2164 return;
2165 }
2166 self.layout_dirty = false;
2167 self.layout_now();
2168 }
2169
2170 fn set_route(&mut self, route: &str) {
2171 self.toolkit.set_route(route);
2172 }
2173
2174 fn set_window_size(&mut self, s: Size) {
2175 if s != self.windows[0].size {
2178 self.windows[0].size = s;
2179 let root = self.windows[0].root;
2180 self.mark_needs_measure_impl(root);
2181 }
2182 }
2183
2184 fn child_count(&self, node: RNode) -> usize {
2185 self.nodes.get(node).map(|n| n.children.len()).unwrap_or(0)
2186 }
2187
2188 fn first_child(&self, node: RNode) -> Option<RNode> {
2189 self.nodes
2190 .get(node)
2191 .and_then(|n| n.children.first().copied())
2192 }
2193
2194 fn parent_of(&self, node: RNode) -> Option<RNode> {
2195 self.nodes
2196 .get(node)
2197 .map(|n| n.parent)
2198 .filter(|p| !p.is_null() && *p != node)
2199 }
2200
2201 fn node_kind(&self, node: RNode) -> Option<PieceKind> {
2202 self.nodes.get(node).map(|n| n.kind)
2203 }
2204
2205 fn node_id(&self, node: RNode) -> Option<String> {
2206 self.nodes.get(node).and_then(|n| n.id.clone())
2207 }
2208
2209 fn node_handle_any(&self, node: RNode) -> Option<Box<dyn Any>> {
2210 self.nodes
2211 .get(node)
2212 .and_then(|n| n.handle.clone())
2213 .map(|h| Box::new(h) as Box<dyn Any>)
2214 }
2215
2216 fn node_frame(&self, node: RNode) -> Option<Rect> {
2217 self.nodes.get(node).and_then(|n| n.last_native_frame)
2218 }
2219
2220 fn node_probe(&self, node: RNode) -> Option<NodeProbe> {
2221 self.nodes.get(node).map(|n| n.probe.clone())
2222 }
2223
2224 fn node_a11y(&self, node: RNode) -> Option<A11yProps> {
2225 self.nodes.get(node).map(|n| n.a11y.clone())
2226 }
2227
2228 fn read_native(&self, node: RNode) -> Option<day_spec::NativeSnapshot> {
2229 let n = self.nodes.get(node)?;
2230 let h = n.handle.as_ref()?;
2231 Some(self.toolkit.read_native(h))
2232 }
2233
2234 fn a11y_nodes(&self) -> Vec<(String, PieceKind, A11yProps, day_spec::NativeSnapshot)> {
2235 self.nodes
2236 .values()
2237 .filter_map(|n| {
2238 let id = n.id.clone()?;
2239 let h = n.handle.as_ref()?;
2240 Some((id, n.kind, n.a11y.clone(), self.toolkit.read_native(h)))
2241 })
2242 .collect()
2243 }
2244
2245 fn find_by_id(&self, id: &str) -> Option<RNode> {
2246 let focused = crate::windows::focused_window()
2249 .map(|w| id_to_rnode(crate::windows::window_node_id(&w)))
2250 .unwrap_or_else(|| self.root());
2251 let mut fallback = None;
2252 for (key, node) in &self.nodes {
2253 if node.id.as_deref() != Some(id) {
2254 continue;
2255 }
2256 fallback.get_or_insert(key);
2257 let mut ancestor = key;
2258 while let Some(node) = self.nodes.get(ancestor) {
2259 if self.windows.iter().any(|w| w.root == ancestor) {
2262 if ancestor == focused {
2263 return Some(key);
2264 }
2265 break;
2266 }
2267 ancestor = if node.kind == kinds::LIST_CELL && node.parent.is_null() {
2268 self.lists
2271 .iter()
2272 .find_map(|(&owner, state)| {
2273 state
2274 .cells
2275 .values()
2276 .any(|c| c.anchor == ancestor)
2277 .then_some(owner)
2278 })
2279 .or_else(|| {
2280 self.trees.iter().find_map(|(&owner, state)| {
2281 state
2282 .cells
2283 .values()
2284 .any(|c| c.anchor == ancestor)
2285 .then_some(owner)
2286 })
2287 })
2288 .unwrap_or(node.parent)
2289 } else {
2290 node.parent
2291 };
2292 }
2293 }
2294 fallback
2295 }
2296
2297 fn toggle_sidebar(&mut self, host: RNode) -> bool {
2298 let Some(h) = self.nodes.get(host).and_then(|n| n.handle.as_ref()) else {
2299 return false;
2300 };
2301 self.toolkit.toggle_sidebar(h)
2302 }
2303
2304 fn native_back(&mut self) -> bool {
2305 self.toolkit.native_back()
2306 }
2307
2308 fn prepare_snapshot(
2309 &mut self,
2310 root: Option<RNode>,
2311 revision: u32,
2312 ) -> Result<day_spec::capture::Readiness, String> {
2313 let host = match root {
2314 Some(root) if self.windows[0].root != root => Some(
2315 self.nodes
2316 .get(root)
2317 .and_then(|n| n.handle.clone())
2318 .ok_or("window is gone")?,
2319 ),
2320 _ => None,
2321 };
2322 self.toolkit.prepare_snapshot(host.as_ref(), revision)
2323 }
2324
2325 fn snapshot(&mut self) -> Result<Vec<u8>, String> {
2326 self.toolkit.snapshot_window()
2327 }
2328
2329 fn snapshot_origin(&mut self) -> Option<Point> {
2330 let root = self.windows[0].root;
2331 let h = self.nodes.get(root).and_then(|n| n.handle.clone())?;
2332 self.toolkit.snapshot_origin(&h)
2333 }
2334
2335 fn snapshot_chrome(&mut self) -> Result<Vec<u8>, String> {
2336 self.toolkit.snapshot_window_chrome()
2337 }
2338
2339 fn decode_image(&mut self, req: u64, id: day_spec::BitmapId, bytes: &[u8]) {
2340 self.toolkit.decode_image(req, id, bytes);
2341 }
2342
2343 fn image_info(&mut self, id: day_spec::BitmapId) -> Option<day_spec::BitmapInfo> {
2344 self.toolkit.image_info(id)
2345 }
2346
2347 fn image_properties(&mut self, id: day_spec::BitmapId) -> Option<day_spec::ImageProperties> {
2348 self.toolkit.image_properties(id)
2349 }
2350
2351 fn encode_image(&mut self, req: u64, id: day_spec::BitmapId, spec: &day_spec::EncodeSpec) {
2352 self.toolkit.encode_image(req, id, spec);
2353 }
2354
2355 fn image_encode_formats(&mut self) -> Vec<day_spec::ImageFormat> {
2356 self.toolkit.encode_formats()
2357 }
2358
2359 fn release_image(&mut self, id: day_spec::BitmapId) {
2360 self.toolkit.release_image(id);
2361 }
2362
2363 fn render_bitmap(
2364 &mut self,
2365 req: u64,
2366 id: day_spec::BitmapId,
2367 ops: &[day_spec::DrawOp],
2368 size: Size,
2369 scale: f64,
2370 ) {
2371 self.toolkit.render_bitmap(req, id, ops, size, scale);
2372 }
2373
2374 fn render_pdf(&mut self, req: u64, pages: &[day_spec::PdfPage], info: &day_spec::PdfInfo) {
2375 self.toolkit.render_pdf(req, pages, info);
2376 }
2377
2378 fn window_image_support(&mut self) -> day_spec::Support {
2379 self.toolkit.capability(day_spec::Cap::Snapshot)
2380 }
2381
2382 fn font_families(&mut self) -> Vec<day_spec::FontFamilyInfo> {
2383 self.toolkit.font_families()
2384 }
2385
2386 fn measure_text(
2387 &mut self,
2388 text: &str,
2389 size: f64,
2390 font: &day_spec::CanvasFont,
2391 ) -> Option<day_spec::TextMetrics> {
2392 self.toolkit.measure_text(text, size, font)
2393 }
2394
2395 fn ui_idle(&mut self) -> bool {
2396 self.toolkit.ui_idle()
2397 }
2398
2399 fn root_node(&self) -> RNode {
2400 self.windows[0].root
2401 }
2402
2403 fn open_window_root(&mut self, options: &WindowOptions, kind: WindowKind) -> WindowRootReply {
2404 let root = self.nodes.insert(NodeData {
2407 kind: kinds::CONTAINER,
2408 handle: None,
2409 parent: RNode::null(),
2410 children: Vec::new(),
2411 layout: Rc::new(PassThrough),
2412 flex: Flex::default(),
2413 scope: Scope::root(),
2414 id: None,
2415 parked_id: None,
2416 a11y: Default::default(),
2417 cache: Vec::new(),
2418 baseline_cache: None,
2419 probe: NodeProbe::default(),
2420 needs_measure: true,
2421 needs_layout: true,
2422 last_native_frame: None,
2423 scroll_content: None,
2424 implicit_anim: None,
2425 tweaked: false,
2426 is_boundary: true,
2427 });
2428 match self.toolkit.open_window(rnode_to_id(root), options, kind) {
2429 day_spec::WindowOpenReply::Open(h) => {
2430 self.nodes[root].handle = Some(h);
2431 self.windows.push(WindowEntry {
2432 root,
2433 size: options.size,
2434 });
2435 self.layout_dirty = true;
2436 WindowRootReply::Open(root)
2437 }
2438 day_spec::WindowOpenReply::Pending => WindowRootReply::Pending(root),
2439 day_spec::WindowOpenReply::Unsupported => {
2440 self.nodes.remove(root);
2441 WindowRootReply::Unsupported
2442 }
2443 }
2444 }
2445
2446 fn adopt_window_root(&mut self, root: RNode, raw: day_spec::RawHandle, size: Size) -> bool {
2447 let Some(node) = self.nodes.get_mut(root) else {
2448 return false; };
2450 let handle = self.toolkit.adopt(raw);
2451 node.handle = Some(handle);
2452 node.needs_measure = true;
2453 self.windows.push(WindowEntry { root, size });
2454 self.layout_dirty = true;
2455 true
2456 }
2457
2458 fn remove_window_root(&mut self, root: RNode) {
2459 if self.windows.len() <= 1 {
2467 self.collect_and_release(root);
2468 self.layout_dirty = true;
2469 return;
2470 }
2471 self.windows.retain(|w| w.root != root);
2472 self.collect_and_release(root);
2475 self.layout_dirty = true;
2476 }
2477
2478 fn set_root_size(&mut self, root: RNode, s: Size) {
2479 if let Some(w) = self.windows.iter_mut().find(|w| w.root == root)
2480 && w.size != s
2481 {
2482 w.size = s;
2483 self.mark_needs_measure_impl(root);
2484 }
2485 }
2486
2487 fn fit_window(&mut self, root: RNode, size: Size) {
2488 if let Some(h) = self.nodes.get(root).and_then(|n| n.handle.clone()) {
2489 self.toolkit.fit_window(&h, size);
2490 }
2491 self.set_root_size(root, size);
2492 }
2493
2494 fn add_extra_layout_root(&mut self, node: RNode, size: Size) {
2495 if !self.windows.iter().any(|w| w.root == node) {
2496 self.windows.push(WindowEntry { root: node, size });
2497 self.layout_dirty = true;
2498 }
2499 }
2500
2501 fn drop_extra_layout_root(&mut self, node: RNode) {
2502 debug_assert!(
2503 self.windows.first().map(|w| w.root) != Some(node),
2504 "drop_extra_layout_root called with the primary root"
2505 );
2506 self.windows.retain(|w| w.root != node);
2507 }
2508
2509 fn close_native_window(&mut self, root: RNode) {
2510 if let Some(h) = self.nodes.get(root).and_then(|n| n.handle.clone()) {
2511 self.toolkit.close_window(&h);
2512 }
2513 }
2514
2515 fn quit_app(&mut self) {
2516 self.toolkit.quit_app();
2517 }
2518
2519 fn focus_native_window(&mut self, root: RNode) {
2520 if let Some(h) = self.nodes.get(root).and_then(|n| n.handle.clone()) {
2521 self.toolkit.focus_window(&h);
2522 }
2523 }
2524
2525 fn set_native_window_title(&mut self, root: RNode, title: &str) {
2526 if let Some(h) = self.nodes.get(root).and_then(|n| n.handle.clone()) {
2527 self.toolkit.set_window_title(&h, title);
2528 }
2529 }
2530
2531 fn apply_window(&mut self, root: RNode, change: &day_spec::WindowChange) {
2532 if let Some(h) = self.nodes.get(root).and_then(|n| n.handle.clone()) {
2533 self.toolkit.apply_window(&h, change);
2534 }
2535 }
2536
2537 fn set_dock_menu(&mut self, items: &[day_spec::MenuItem]) {
2538 self.toolkit.set_dock_menu(items);
2539 }
2540
2541 fn set_status_items(&mut self, items: &[day_spec::StatusItemSpec]) {
2542 self.toolkit.set_status_items(items);
2543 }
2544
2545 fn set_app_progress(&mut self, progress: day_spec::AppProgress) {
2546 self.toolkit.set_app_progress(progress);
2547 }
2548
2549 fn set_dock_visible(&mut self, visible: bool) {
2550 self.toolkit.set_dock_visible(visible);
2551 }
2552
2553 fn set_keep_running(&mut self, keep: bool) {
2554 self.toolkit.set_keep_running(keep);
2555 }
2556
2557 fn native_window_frame(&self, root: RNode) -> Option<day_spec::Rect> {
2558 let h = self.nodes.get(root).and_then(|n| n.handle.clone())?;
2559 self.toolkit.window_frame(&h)
2560 }
2561
2562 fn monitors(&self) -> Vec<day_spec::Monitor> {
2563 self.toolkit.monitors()
2564 }
2565
2566 fn set_launcher_shortcuts(&mut self, shortcuts: &[day_spec::LauncherShortcut]) {
2567 self.toolkit.set_launcher_shortcuts(shortcuts);
2568 }
2569
2570 fn snapshot_of(&mut self, root: RNode) -> Result<Vec<u8>, String> {
2571 if self.windows[0].root == root {
2572 return self.toolkit.snapshot_window();
2573 }
2574 match self.nodes.get(root).and_then(|n| n.handle.clone()) {
2575 Some(h) => self.toolkit.snapshot_window_of(&h),
2576 None => Err("window is gone".into()),
2577 }
2578 }
2579
2580 fn install_list(&mut self, node: RNode, driver: crate::list::ListDriver) {
2581 let driver = Rc::new(driver);
2582 let sections = driver
2583 .sections
2584 .is_some()
2585 .then(|| crate::list::register_sections(node, driver.clone()));
2586 self.lists.insert(
2587 node,
2588 crate::list::ListState {
2589 driver: driver.clone(),
2590 cells: HashMap::new(),
2591 sections: sections.clone(),
2592 },
2593 );
2594 let source = crate::list::make_source(node, driver, sections);
2595 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
2596 self.toolkit.attach_list(&handle, source);
2597 }
2598 }
2599
2600 fn list_prepare_cell(
2601 &mut self,
2602 node: RNode,
2603 key: usize,
2604 cell: RawHandle,
2605 header: bool,
2606 ) -> crate::list::CellStep {
2607 if let Some(state) = self.lists.get_mut(&node)
2611 && state.cells.get(&key).is_some_and(|b| b.header != header)
2612 && let Some(old) = state.cells.remove(&key)
2613 {
2614 old.scope.dispose();
2615 self.remove_subtree_impl(old.anchor);
2616 }
2617 if let Some(state) = self.lists.get_mut(&node)
2618 && let Some(bound) = state.cells.get_mut(&key)
2619 {
2620 bound.active = true;
2621 let (rebind, anchor, pinned) = (bound.rebind.clone(), bound.anchor, bound.pinned);
2622 if !pinned {
2626 self.restore_subtree_ids(anchor);
2627 }
2628 return crate::list::CellStep::Rebind { rebind, anchor };
2629 }
2630 let mut metrics = match self.nodes.get(node).and_then(|n| n.handle.clone()) {
2633 Some(list) => self.toolkit.list_row_metrics(&list),
2634 None => day_spec::RowMetrics::default(),
2635 };
2636 if header {
2640 metrics.insets.top = 0.0;
2641 metrics.insets.bottom = 0.0;
2642 metrics.min_height = 0.0;
2643 }
2644 let handle = self.toolkit.adopt(cell);
2645 let anchor = self.create_cell_anchor(
2646 handle,
2647 Scope::child(),
2648 Rc::new(crate::layout::CellMetrics(metrics)),
2649 );
2650 crate::list::CellStep::Build { anchor }
2651 }
2652
2653 fn list_store_cell(
2654 &mut self,
2655 node: RNode,
2656 key: usize,
2657 anchor: RNode,
2658 built: crate::list::BuiltRow,
2659 header: bool,
2660 ) {
2661 if let Some(state) = self.lists.get_mut(&node) {
2662 state.cells.insert(
2663 key,
2664 crate::list::BoundCell {
2665 anchor,
2666 active: true,
2667 scope: built.scope,
2668 rebind: built.rebind,
2669 native_width: None,
2670 laid_width: None,
2671 laid_height: None,
2672 header,
2673 pinned: false,
2674 },
2675 );
2676 }
2677 }
2678
2679 fn list_layout_cell(&mut self, node: RNode, key: usize) {
2680 let native = self
2686 .lists
2687 .get(&node)
2688 .and_then(|s| s.cells.get(&key))
2689 .and_then(|c| c.native_width);
2690 let width = native.unwrap_or_else(|| {
2691 self.nodes
2692 .get(node)
2693 .and_then(|n| n.last_native_frame)
2694 .map(|f| f.size.width)
2695 .unwrap_or(self.windows[0].size.width)
2696 });
2697 self.layout_list_cell_at(node, key, width);
2698 }
2699
2700 fn list_layout_cell_width(&mut self, node: RNode, key: usize, width: f64) {
2701 if let Some(cell) = self
2704 .lists
2705 .get_mut(&node)
2706 .and_then(|s| s.cells.get_mut(&key))
2707 {
2708 cell.native_width = Some(width);
2709 }
2710 self.layout_list_cell_at(node, key, width);
2711 }
2712
2713 fn list_cell_height(&self, node: RNode, key: usize) -> f64 {
2714 self.lists
2715 .get(&node)
2716 .and_then(|s| s.cells.get(&key))
2717 .filter(|b| b.active)
2718 .and_then(|b| b.laid_height)
2719 .unwrap_or(0.0)
2720 }
2721
2722 fn list_cell_keys(&self, node: RNode) -> Vec<usize> {
2723 self.lists
2724 .get(&node)
2725 .map(|s| {
2726 s.cells
2727 .iter()
2728 .filter_map(|(key, cell)| cell.active.then_some(*key))
2729 .collect()
2730 })
2731 .unwrap_or_default()
2732 }
2733
2734 fn list_reload(&mut self, node: RNode) {
2735 if let Some(map) = self.lists.get(&node).and_then(|s| s.sections.as_ref()) {
2736 map.invalidate();
2737 }
2738 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
2739 self.toolkit.update(
2740 &handle,
2741 kinds::LIST,
2742 &day_spec::props::ListPatch::Reload as &dyn Any,
2743 None,
2744 );
2745 }
2746 }
2747
2748 fn list_splice(&mut self, node: RNode, deltas: Vec<day_spec::props::RowDelta>) {
2749 if self.lists.get(&node).is_some_and(|s| s.sections.is_some()) {
2752 self.list_reload(node);
2753 return;
2754 }
2755 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
2756 self.toolkit.update(
2757 &handle,
2758 kinds::LIST,
2759 &day_spec::props::ListPatch::Splice(deltas) as &dyn Any,
2760 None,
2761 );
2762 }
2763 }
2764
2765 fn set_undo_state(&mut self, state: &day_spec::UndoState) {
2766 self.toolkit.set_undo_state(state);
2767 }
2768
2769 fn set_edit_state(&mut self, state: &day_spec::EditState) {
2770 self.toolkit.set_edit_state(state);
2771 }
2772
2773 fn modifiers(&mut self) -> day_spec::Modifiers {
2774 self.toolkit.modifiers()
2775 }
2776
2777 fn list_scroll_to_end(&mut self, node: RNode) {
2778 if self.lists.get(&node).map(|s| (s.driver.len)()).unwrap_or(0) == 0 {
2781 return;
2782 }
2783 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
2784 self.toolkit.update(
2785 &handle,
2786 kinds::LIST,
2787 &day_spec::props::ListPatch::ScrollToEnd as &dyn Any,
2788 None,
2789 );
2790 }
2791 }
2792
2793 fn list_scroll_to_row(&mut self, node: RNode, row: usize) {
2794 let len = self.lists.get(&node).map(|s| (s.driver.len)()).unwrap_or(0);
2795 if len == 0 {
2796 return;
2797 }
2798 let mut row = row.min(len - 1);
2799 if let Some(map) = self.lists.get(&node).and_then(|s| s.sections.as_ref()) {
2800 row = map.display_of(row).unwrap_or(row);
2801 }
2802 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
2803 self.toolkit.update(
2804 &handle,
2805 kinds::LIST,
2806 &day_spec::props::ListPatch::ScrollToRow(row) as &dyn Any,
2807 None,
2808 );
2809 }
2810 }
2811
2812 fn list_set_editing(&mut self, node: RNode, on: bool) {
2813 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
2814 self.toolkit.update(
2815 &handle,
2816 kinds::LIST,
2817 &day_spec::props::ListPatch::Editing(on) as &dyn Any,
2818 None,
2819 );
2820 }
2821 }
2822
2823 fn list_set_selected(&mut self, node: RNode, rows: Vec<usize>) {
2824 let rows = match self.lists.get(&node).and_then(|s| s.sections.as_ref()) {
2825 Some(map) => rows.iter().filter_map(|i| map.display_of(*i)).collect(),
2826 None => rows,
2827 };
2828 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
2829 self.toolkit.update(
2830 &handle,
2831 kinds::LIST,
2832 &day_spec::props::ListPatch::Selected(rows) as &dyn Any,
2833 None,
2834 );
2835 }
2836 }
2837
2838 fn list_driver(&mut self, node: RNode) -> Option<std::rc::Rc<crate::list::ListDriver>> {
2839 self.lists.get(&node).map(|s| s.driver.clone())
2840 }
2841
2842 fn install_tree(&mut self, node: RNode, driver: crate::tree_driver::TreeDriver) {
2843 let driver = Rc::new(driver);
2844 self.trees.insert(
2845 node,
2846 crate::tree_driver::TreeState {
2847 driver: driver.clone(),
2848 cells: HashMap::new(),
2849 },
2850 );
2851 let source = crate::tree_driver::make_tree_source(node, driver);
2852 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
2853 self.toolkit.attach_tree(&handle, source);
2854 }
2855 }
2856
2857 fn tree_prepare_cell(
2858 &mut self,
2859 node: RNode,
2860 key: usize,
2861 cell: RawHandle,
2862 ) -> crate::tree_driver::TreeCellStep {
2863 if let Some(state) = self.trees.get(&node)
2864 && let Some(bound) = state.cells.get(&key)
2865 {
2866 let (rebind, anchor) = (bound.rebind.clone(), bound.anchor);
2867 self.restore_subtree_ids(anchor);
2869 return crate::tree_driver::TreeCellStep::Rebind { rebind, anchor };
2870 }
2871 let handle = self.toolkit.adopt(cell);
2875 let anchor =
2876 self.create_cell_anchor(handle, Scope::child(), Rc::new(crate::layout::CellCenter));
2877 crate::tree_driver::TreeCellStep::Build { anchor }
2878 }
2879
2880 fn tree_store_cell(
2881 &mut self,
2882 node: RNode,
2883 key: usize,
2884 anchor: RNode,
2885 built: crate::tree_driver::TreeBuiltRow,
2886 ) {
2887 if let Some(state) = self.trees.get_mut(&node) {
2888 state.cells.insert(
2889 key,
2890 crate::tree_driver::TreeBoundCell {
2891 anchor,
2892 scope: built.scope,
2893 rebind: built.rebind,
2894 native_width: None,
2895 native_height: None,
2896 laid_width: None,
2897 laid_height: None,
2898 },
2899 );
2900 }
2901 }
2902
2903 fn tree_layout_cell(&mut self, node: RNode, key: usize) {
2904 let native = self
2908 .trees
2909 .get(&node)
2910 .and_then(|s| s.cells.get(&key))
2911 .and_then(|c| c.native_width);
2912 let width = native.unwrap_or_else(|| {
2913 self.nodes
2914 .get(node)
2915 .and_then(|n| n.last_native_frame)
2916 .map(|f| f.size.width)
2917 .unwrap_or(self.windows[0].size.width)
2918 });
2919 self.layout_tree_cell_at(node, key, width);
2920 }
2921
2922 fn tree_layout_cell_width(&mut self, node: RNode, key: usize, width: f64) {
2923 if let Some(cell) = self
2926 .trees
2927 .get_mut(&node)
2928 .and_then(|s| s.cells.get_mut(&key))
2929 {
2930 cell.native_width = Some(width);
2931 }
2932 self.layout_tree_cell_at(node, key, width);
2933 }
2934
2935 fn tree_cell_height(&self, node: RNode, key: usize) -> f64 {
2936 self.trees
2937 .get(&node)
2938 .and_then(|s| s.cells.get(&key))
2939 .and_then(|b| b.laid_height)
2940 .unwrap_or(0.0)
2941 }
2942
2943 fn tree_layout_cell_size(&mut self, node: RNode, key: usize, size: Size) {
2944 let changed = self
2945 .trees
2946 .get_mut(&node)
2947 .and_then(|s| s.cells.get_mut(&key))
2948 .and_then(|cell| {
2949 let changed = cell.native_height != Some(size.height);
2950 cell.native_height = Some(size.height);
2951 changed.then_some(cell.anchor)
2952 });
2953 if let Some(anchor) = changed {
2954 self.invalidate_subtree(anchor);
2955 }
2956 self.tree_layout_cell_width(node, key, size.width);
2957 }
2958
2959 fn tree_recycle_cell(&mut self, node: RNode, key: usize) {
2960 let Some(anchor) = self
2961 .trees
2962 .get(&node)
2963 .and_then(|s| s.cells.get(&key))
2964 .map(|b| b.anchor)
2965 else {
2966 return;
2967 };
2968 self.clear_subtree_ids(anchor);
2969 }
2970
2971 fn list_recycle_cell(&mut self, node: RNode, key: usize) {
2972 let Some(anchor) = self
2973 .lists
2974 .get_mut(&node)
2975 .and_then(|s| s.cells.get_mut(&key))
2976 .map(|b| {
2977 b.active = false;
2978 b.laid_height = None;
2983 b.anchor
2984 })
2985 else {
2986 return;
2987 };
2988 self.clear_subtree_ids(anchor);
2989 }
2990
2991 fn list_release_cell(&mut self, node: RNode, key: usize) {
2992 let Some(cell) = self.lists.get_mut(&node).and_then(|s| s.cells.remove(&key)) else {
2994 return;
2995 };
2996 cell.scope.dispose();
3001 self.remove_subtree_impl(cell.anchor);
3002 }
3003
3004 fn list_pin_cell(&mut self, node: RNode, key: usize) {
3005 let Some(anchor) = self
3006 .lists
3007 .get_mut(&node)
3008 .and_then(|s| s.cells.get_mut(&key))
3009 .map(|b| {
3010 b.pinned = true;
3011 b.anchor
3012 })
3013 else {
3014 return;
3015 };
3016 self.clear_subtree_ids(anchor);
3017 }
3018
3019 fn tree_reload(&mut self, node: RNode) {
3020 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
3021 self.toolkit.update(
3022 &handle,
3023 kinds::TREE,
3024 &day_spec::props::TreePatch::Reload as &dyn Any,
3025 None,
3026 );
3027 }
3028 }
3029
3030 fn tree_set_expanded(&mut self, node: RNode, token: u64, expanded: bool) {
3031 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
3032 self.toolkit.update(
3033 &handle,
3034 kinds::TREE,
3035 &day_spec::props::TreePatch::Expand(token, expanded) as &dyn Any,
3036 None,
3037 );
3038 }
3039 }
3040
3041 fn tree_set_selected(&mut self, node: RNode, tokens: Vec<u64>) {
3042 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
3043 self.toolkit.update(
3044 &handle,
3045 kinds::TREE,
3046 &day_spec::props::TreePatch::Selected(tokens) as &dyn Any,
3047 None,
3048 );
3049 }
3050 }
3051
3052 fn tree_reveal(&mut self, node: RNode, token: u64) {
3053 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
3054 self.toolkit.update(
3055 &handle,
3056 kinds::TREE,
3057 &day_spec::props::TreePatch::Reveal(token) as &dyn Any,
3058 None,
3059 );
3060 }
3061 }
3062
3063 fn tree_driver(&mut self, node: RNode) -> Option<std::rc::Rc<crate::tree_driver::TreeDriver>> {
3064 self.trees.get(&node).map(|s| s.driver.clone())
3065 }
3066
3067 fn set_drag_source(&mut self, node: RNode, source: day_spec::transfer::Source) {
3068 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
3069 self.toolkit.set_drag_source(&handle, source);
3070 }
3071 }
3072 fn set_drop_target(&mut self, node: RNode, target: day_spec::transfer::Target) {
3073 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
3074 self.toolkit.set_drop_target(&handle, target);
3075 }
3076 }
3077 fn set_context_menu_fn(&mut self, node: RNode, f: day_spec::ContextMenuFn) {
3078 let target = if self.nodes.get(node).is_some_and(|n| n.handle.is_some()) {
3081 Some(node)
3082 } else {
3083 let mut roots = Vec::new();
3084 self.native_descendants(node, &mut roots);
3085 roots.first().copied()
3086 };
3087 let Some(target) = target else {
3088 log::warn!("context_menu_fn on a subtree with no native view — menu dropped");
3089 return;
3090 };
3091 let model = ContextMenuModel::Provider(f.clone());
3092 self.context_menus.insert(node, model.clone());
3093 self.context_menus.insert(target, model);
3094 if let Some(handle) = self.nodes.get(target).and_then(|n| n.handle.clone()) {
3095 self.toolkit
3096 .set_context_menu_fn(&handle, rnode_to_id(target), f);
3097 }
3098 }
3099
3100 fn context_menu_of(&mut self, node: RNode) -> Option<day_spec::ContextMenuFn> {
3101 let mut cur = node;
3104 loop {
3105 if let Some(model) = self.context_menus.get(&cur) {
3106 return Some(match model {
3107 ContextMenuModel::Items(items) => {
3108 let items = items.clone();
3109 Rc::new(move |_| items.clone())
3110 }
3111 ContextMenuModel::Provider(f) => f.clone(),
3112 });
3113 }
3114 let parent = self.nodes.get(cur)?.parent;
3117 if parent == cur {
3118 return None;
3119 }
3120 cur = parent;
3121 }
3122 }
3123}
3124
3125day_reactive::tls_slots! {
3130 tree;
3131 static TREE: std::mem::ManuallyDrop<RefCell<Option<Box<dyn TreeOps>>>> =
3140 const { std::mem::ManuallyDrop::new(RefCell::new(None)) };
3141 static EVENTS: RefCell<VecDeque<(NodeId, Event)>> = const { RefCell::new(VecDeque::new()) };
3142 static PUMP_PENDING: Cell<bool> = const { Cell::new(false) };
3143 static PUMP_ACTIVE: Cell<bool> = const { Cell::new(false) };
3144 static DEFERRED: RefCell<Vec<Box<dyn FnOnce() + Send>>> = const { RefCell::new(Vec::new()) };
3147 static EVENT_OBSERVER: RefCell<Option<Rc<EventObserver>>> = const { RefCell::new(None) };
3151 static PUMP_GEN: std::cell::Cell<u64> = const { std::cell::Cell::new(0) };
3156
3157 static FOCUSED: std::cell::Cell<Option<RNode>> = const { std::cell::Cell::new(None) };
3159}
3160
3161pub fn install_tree(tree: Box<dyn TreeOps>) {
3162 crate::nav::clear_controllers();
3164 TREE.with(|t| *t.borrow_mut() = Some(tree));
3165 let _ = crate::image::canvas_exports();
3168}
3169
3170pub fn prepare_remount() {
3188 day_reactive::recover_from_panic();
3194 uninstall_tree();
3195 day_reactive::recover_from_panic();
3197 crate::lifecycle::reset_handlers();
3198 crate::menu::reset_menus();
3199}
3200
3201pub fn is_mounted() -> bool {
3207 TREE.with(|t| t.borrow().is_some())
3208}
3209
3210pub fn uninstall_tree() {
3212 crate::image::reset();
3214 crate::nav::clear_controllers();
3215 crate::windows::reset_windows();
3216 crate::status::reset_status();
3217 crate::reset_ambient();
3220 TREE.with(|t| *t.borrow_mut() = None);
3221 EVENTS.with(|e| e.borrow_mut().clear());
3222 PUMP_PENDING.with(|c| c.set(false));
3223 EVENT_OBSERVER.with(|o| *o.borrow_mut() = None);
3226}
3227
3228pub fn with_tree<R>(f: impl FnOnce(&mut dyn TreeOps) -> R) -> R {
3232 let r = TREE.with(|t| {
3233 let mut opt = t.borrow_mut();
3234 let ops = opt.as_mut().expect("day: no tree installed on this thread");
3235 f(ops.as_mut())
3236 });
3237 if PUMP_PENDING.with(|c| c.replace(false)) {
3238 pump_events();
3239 }
3240 run_deferred();
3241 r
3242}
3243
3244pub(crate) fn run_posted_or_defer(f: Box<dyn FnOnce() + Send>) {
3251 let borrowed = TREE.with(|t| t.try_borrow_mut().is_err());
3252 if borrowed {
3253 DEFERRED.with(|d| d.borrow_mut().push(f));
3254 } else {
3255 crate::contain_posted_panic(f);
3256 }
3257}
3258
3259fn run_deferred() {
3263 loop {
3264 let batch = DEFERRED.with(|d| std::mem::take(&mut *d.borrow_mut()));
3265 if batch.is_empty() {
3266 return;
3267 }
3268 for f in batch {
3269 crate::contain_posted_panic(f);
3270 }
3271 }
3272}
3273
3274pub fn capability(cap: day_spec::Cap) -> day_spec::Support {
3278 with_tree(|t| t.capability(cap))
3279}
3280
3281pub fn open_url(url: &str) {
3286 if crate::conformance::intercept_open_url(url) {
3289 return;
3290 }
3291 with_tree(|t| t.open_url(url));
3292}
3293
3294pub fn announce(text: &str) {
3300 crate::conformance::note_announcement(text);
3301 with_tree(|t| t.announce(text, false));
3302}
3303
3304pub fn announce_urgent(text: &str) {
3307 crate::conformance::note_announcement(text);
3308 with_tree(|t| t.announce(text, true));
3309}
3310
3311pub fn share_support() -> Support {
3313 with_tree(|t| t.share_support())
3314}
3315pub fn share_url(url: &str, title: &str) -> bool {
3319 with_tree(|t| t.share_url(url, title))
3320}
3321
3322pub fn application_handlers(
3325 query: &day_spec::applications::HandlerQuery,
3326) -> Result<day_spec::applications::ApplicationHandlers, day_spec::applications::ApplicationError> {
3327 with_tree(|t| t.application_handlers(query))
3328}
3329
3330pub fn default_browser()
3332-> Result<Option<day_spec::applications::Application>, day_spec::applications::ApplicationError> {
3333 application_handlers(&day_spec::applications::HandlerQuery::Scheme(
3334 "https".into(),
3335 ))
3336 .map(|h| h.default)
3337}
3338
3339pub async fn open_url_with_application(
3342 url: &str,
3343 application: &str,
3344) -> Result<(), day_spec::applications::ApplicationError> {
3345 let (send, receive) = day_async::oneshot();
3346 with_tree(|t| {
3347 t.open_url_with_application(url, application, Box::new(move |result| send.send(result)))
3348 });
3349 receive
3350 .await
3351 .unwrap_or(Err(day_spec::applications::ApplicationError::LaunchFailed))
3352}
3353
3354pub fn invalidate_size(node: RNode) {
3360 with_tree(|t| {
3361 t.mark_needs_measure(node);
3362 t.mark_layout_dirty();
3363 });
3364}
3365
3366pub fn try_with_tree<R>(f: impl FnOnce(&mut dyn TreeOps) -> R) -> Option<R> {
3376 let r = TREE.with(|t| {
3377 let mut opt = t.try_borrow_mut().ok()?;
3378 let ops = opt.as_mut()?;
3379 Some(f(ops.as_mut()))
3380 });
3381 if r.is_some() {
3382 if PUMP_PENDING.with(|c| c.replace(false)) {
3383 pump_events();
3384 }
3385 run_deferred();
3386 }
3387 r
3388}
3389
3390pub(crate) fn with_tree_if_free<R>(f: impl FnOnce(&mut dyn TreeOps) -> R) -> Option<R> {
3393 TREE.with(|t| {
3394 let mut opt = t.try_borrow_mut().ok()?;
3395 let ops = opt.as_mut()?;
3396 Some(f(ops.as_mut()))
3397 })
3398}
3399
3400pub(crate) fn tree_absent() -> bool {
3403 TREE.try_with(|t| t.try_borrow().map(|o| o.is_none()).unwrap_or(false))
3405 .unwrap_or(true)
3406}
3407
3408pub(crate) fn request_pump() {
3411 PUMP_PENDING.with(|c| c.set(true));
3412}
3413
3414pub fn has_tree() -> bool {
3415 !tree_absent()
3418}
3419
3420pub fn enqueue_event(id: NodeId, ev: Event) {
3423 enqueue_events([(id, ev)]);
3424}
3425
3426pub(crate) fn enqueue_native_event(id: NodeId, ev: Event) {
3431 if let Some(ev) = crate::list::translate_native_event(id, ev) {
3432 enqueue_event(id, ev);
3433 }
3434}
3435
3436pub fn enqueue_events(evs: impl IntoIterator<Item = (NodeId, Event)>) {
3441 let observer = EVENT_OBSERVER.with(|o| o.borrow().clone());
3450 match observer {
3451 Some(obs) => {
3452 let batch: Vec<(NodeId, Event)> = evs.into_iter().collect();
3453 for (id, ev) in &batch {
3454 obs(*id, ev);
3455 }
3456 EVENTS.with(|e| e.borrow_mut().extend(batch));
3457 }
3458 None => EVENTS.with(|e| e.borrow_mut().extend(evs)),
3459 }
3460 let tree_free = TREE.with(|t| t.try_borrow_mut().is_ok());
3461 if tree_free {
3462 pump_events();
3463 } else {
3464 PUMP_PENDING.with(|c| c.set(true));
3465 }
3466}
3467
3468pub fn pump_generation() -> u64 {
3470 PUMP_GEN.with(|g| g.get())
3471}
3472
3473pub fn set_event_observer(observer: Option<Box<EventObserver>>) {
3482 EVENT_OBSERVER.with(|o| *o.borrow_mut() = observer.map(Rc::from));
3483}
3484
3485pub fn id_of(node: NodeId) -> Option<String> {
3492 try_with_tree(|t| t.node_id(id_to_rnode(node))).flatten()
3493}
3494
3495pub fn label_of(node: NodeId) -> Option<String> {
3499 try_with_tree(|t| {
3500 let rnode = id_to_rnode(node);
3501 let a11y = t
3502 .node_a11y(rnode)
3503 .and_then(|a| a.label)
3504 .filter(|s| !s.is_empty());
3505 a11y.or_else(|| {
3506 t.node_probe(rnode)
3507 .map(|p| p.text)
3508 .filter(|s| !s.is_empty())
3509 })
3510 })
3511 .flatten()
3512}
3513
3514pub fn pump_events() {
3522 if PUMP_ACTIVE.with(|active| active.replace(true)) {
3526 return;
3527 }
3528 struct PumpGuard;
3529 impl Drop for PumpGuard {
3530 fn drop(&mut self) {
3531 PUMP_ACTIVE.with(|active| active.set(false));
3532 }
3533 }
3534 let _guard = PumpGuard;
3535 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3536 loop {
3537 pump_events_inner();
3538 if EVENTS.with(|events| events.borrow().is_empty()) {
3541 break;
3542 }
3543 }
3544 }));
3545 if let Err(payload) = result {
3546 let msg = payload
3547 .downcast_ref::<&str>()
3548 .map(|s| (*s).to_string())
3549 .or_else(|| payload.downcast_ref::<String>().cloned())
3550 .unwrap_or_else(|| "unknown panic".to_string());
3551 log::warn!(
3552 "a native event handler panicked and was contained — the app continues, but \
3553 reactive/UI state may be inconsistent until the next interaction. Cause: {msg}"
3554 );
3555 EVENTS.with(|e| e.borrow_mut().clear());
3557 PUMP_PENDING.with(|c| c.set(false));
3558 day_reactive::recover_from_panic();
3559 crate::notify_contained_panic();
3560 }
3561}
3562
3563fn pump_events_inner() {
3564 crate::image::flush_deferred_releases();
3566 PUMP_GEN.with(|g| g.set(g.get() + 1));
3571 crate::nav::maybe_notify_route_change();
3572 loop {
3573 let item = EVENTS.with(|e| e.borrow_mut().pop_front());
3574 let Some((id, ev)) = item else { break };
3575 if let Event::PresentResult { req, result } = ev {
3577 crate::present::resolve_presentation(req, result);
3578 continue;
3579 }
3580 if let Event::ImageDecoded { req, result } = ev {
3583 crate::image::resolve_image_decode(req, result);
3584 continue;
3585 }
3586 if let Event::ImageEncoded { req, result } = ev {
3587 crate::image::resolve_image_encode(req, result);
3588 continue;
3589 }
3590 if let Event::BitmapRendered { req, result } = ev {
3592 crate::image::resolve_bitmap_render(req, result);
3593 continue;
3594 }
3595 if let Event::PdfRendered { req, result } = ev {
3596 crate::image::resolve_pdf_render(req, result);
3597 continue;
3598 }
3599 if let Event::MenuAction(action) = ev {
3601 crate::menu::dispatch_menu_action(action);
3602 continue;
3603 }
3604 if let Event::ToolbarChanged { action, value } = &ev {
3607 crate::toolbar::dispatch_toolbar_value(*action, value);
3608 continue;
3609 }
3610 if let Event::Lifecycle(phase) = ev {
3612 crate::lifecycle::dispatch_lifecycle(phase);
3613 continue;
3614 }
3615 if ev == Event::FocusChanged(false) {
3620 let paired = EVENTS.with(|e| {
3621 let mut q = e.borrow_mut();
3622 let gain = q
3623 .iter()
3624 .position(|(gid, gev)| *gid != id && *gev == Event::FocusChanged(true));
3625 gain.map(|i| q.remove(i).expect("indexed event"))
3626 });
3627 if let Some((gid, gev)) = paired {
3628 dispatch_focus_probe(gid, &gev);
3629 dispatch_to_node(gid, &gev);
3630 }
3631 }
3632 if let Event::FocusChanged(_) = ev {
3633 dispatch_focus_probe(id, &ev);
3634 }
3635 dispatch_to_node(id, &ev);
3636 }
3637 day_reactive::flush_sync();
3638 crate::nav::maybe_notify_route_change();
3645}
3646
3647pub fn focused_node() -> Option<RNode> {
3651 FOCUSED.with(|f| f.get())
3652}
3653
3654fn dispatch_focus_probe(id: NodeId, ev: &Event) {
3656 if let Event::FocusChanged(f) = ev {
3657 let node = id_to_rnode(id);
3658 with_tree(|t| t.set_probe_focused(node, *f));
3659 }
3660}
3661
3662pub fn scroll_to(node: RNode, target: ScrollTarget) {
3667 with_tree(|t| {
3668 t.scroll_to_target(node, &target, true);
3669 });
3670}
3671
3672fn dispatch_to_node(id: NodeId, ev: &Event) {
3674 let node = if id == day_spec::WINDOW_NODE {
3675 with_tree(|t| t.root_node())
3676 } else {
3677 id_to_rnode(id)
3678 };
3679 let gesture = match ev {
3685 Event::Tap(_) => Some(day_spec::GestureKind::Tap),
3686 Event::LongPress(_) => Some(day_spec::GestureKind::LongPress),
3687 Event::Drag { .. } => Some(day_spec::GestureKind::Drag),
3688 Event::Pan { .. } => Some(day_spec::GestureKind::Pan),
3689 Event::Pinch { .. } => Some(day_spec::GestureKind::Pinch),
3690 Event::Hover { .. } => Some(day_spec::GestureKind::Hover),
3691 _ => None,
3692 };
3693 let node = match gesture.and_then(|k| with_tree(|t| t.gesture_target(node, k))) {
3694 Some(target) => target,
3695 None => node,
3696 };
3697 let mut handlers = with_tree(|t| t.handlers_for(node));
3698 if matches!(ev, Event::Key(_) | Event::FocusChanged(_)) {
3703 with_tree(|t| {
3704 for w in t.wrappers_above(node) {
3705 handlers.extend(t.handlers_for(w));
3706 }
3707 });
3708 }
3709 if handlers.is_empty() {
3710 if cfg!(debug_assertions)
3714 && matches!(ev, Event::Pressed | Event::Tap(_))
3715 && !with_tree(|t| t.node_exists(node))
3716 {
3717 log::warn!("{ev:?} dropped — node {id:?} no longer exists (stale reference)");
3718 }
3719 return;
3720 }
3721 day_reactive::batch(|| {
3722 for h in &handlers {
3723 h(ev);
3724 }
3725 });
3726}
3727
3728fn collect_menu_families(
3729 items: &[day_spec::MenuItem],
3730 families: &mut std::collections::BTreeSet<String>,
3731) {
3732 for item in items {
3733 match item {
3734 day_spec::MenuItem::Action { id: Some(id), .. } => {
3735 families.insert(crate::element_id::family_of(id).to_owned());
3736 }
3737 day_spec::MenuItem::Submenu { items, .. } => collect_menu_families(items, families),
3738 _ => {}
3739 }
3740 }
3741}
3742
3743#[cfg(test)]
3744mod thread_exit_tests {
3745 use super::*;
3746 use std::sync::Arc;
3747 use std::sync::atomic::{AtomicBool, Ordering};
3748
3749 struct Probe(Arc<AtomicBool>);
3751
3752 impl Drop for Probe {
3753 fn drop(&mut self) {
3754 self.0.store(true, Ordering::SeqCst);
3755 }
3756 }
3757
3758 impl Toolkit for Probe {
3759 type Handle = ();
3760 fn realize(&mut self, _: PieceKind, _: &dyn Any, _: NodeId) -> Self::Handle {}
3761 fn release(&mut self, _: Self::Handle) {}
3762 fn insert(&mut self, _: &Self::Handle, _: &Self::Handle, _: usize) {}
3763 fn remove(&mut self, _: &Self::Handle, _: &Self::Handle) {
3764 panic!("test probe has no attached children to remove");
3765 }
3766 fn move_child(&mut self, _: &Self::Handle, _: &Self::Handle, _: usize) {}
3767 fn measure(&mut self, _: &Self::Handle, _: PieceKind, _: Proposal) -> Size {
3768 Size::new(0.0, 0.0)
3769 }
3770 fn set_frame(&mut self, _: &Self::Handle, _: Rect, _: Option<&AnimSpec>) {}
3771 fn set_input_traits(&mut self, _: &Self::Handle, _: &InputTraits) -> Option<Self::Handle> {
3772 None
3773 }
3774 fn set_event_sink(&mut self, _: EventSink) {}
3775 fn update(&mut self, _: &Self::Handle, _: &'static str, _: &dyn Any, _: Option<&AnimSpec>) {
3776 }
3777 }
3778
3779 fn install_probe(dropped: &Arc<AtomicBool>) {
3780 let tree = Tree::new(Probe(dropped.clone()), (), Size::new(0.0, 0.0));
3781 install_tree(Box::new(tree));
3782 }
3783
3784 #[test]
3789 fn the_tree_is_not_dropped_when_its_thread_ends() {
3790 let dropped = Arc::new(AtomicBool::new(false));
3791 let d = dropped.clone();
3792 std::thread::spawn(move || install_probe(&d))
3793 .join()
3794 .expect("thread");
3795 assert!(
3796 !dropped.load(Ordering::SeqCst),
3797 "the thread-local destructor dropped the tree, and with it the native widgets"
3798 );
3799 }
3800
3801 #[test]
3804 fn uninstall_still_drops_the_tree() {
3805 let dropped = Arc::new(AtomicBool::new(false));
3806 let d = dropped.clone();
3807 std::thread::spawn(move || {
3808 install_probe(&d);
3809 uninstall_tree();
3810 assert!(
3811 d.load(Ordering::SeqCst),
3812 "uninstall_tree must drop the tree"
3813 );
3814 })
3815 .join()
3816 .expect("thread");
3817 }
3818
3819 #[test]
3820 fn removing_an_unattached_node_releases_without_native_detach() {
3821 let dropped = Arc::new(AtomicBool::new(false));
3822 let mut tree = Tree::new(Probe(dropped), (), Size::new(0.0, 0.0));
3823 let scope = Scope::detached();
3824 let node = tree.create_node(
3825 "fixture.detached",
3826 &(),
3827 Rc::new(crate::LeafLayout),
3828 Flex::default(),
3829 true,
3830 false,
3831 scope,
3832 );
3833 tree.remove_subtree(node);
3834 assert!(!tree.node_exists(node));
3835 assert_eq!(
3836 tree.release_queue.len(),
3837 1,
3838 "the unattached native handle must still be released"
3839 );
3840 scope.dispose();
3841 }
3842
3843 #[test]
3848 fn a_post_during_a_tree_borrow_runs_at_the_safe_point() {
3849 std::thread::spawn(|| {
3850 install_probe(&Arc::new(AtomicBool::new(false)));
3851 let ran = Arc::new(AtomicBool::new(false));
3852 let r = ran.clone();
3853 with_tree(|_| {
3854 run_posted_or_defer(Box::new(move || {
3855 with_tree(|_| r.store(true, Ordering::SeqCst));
3858 }));
3859 assert!(!ran.load(Ordering::SeqCst), "ran inside the borrow");
3860 });
3861 assert!(
3862 ran.load(Ordering::SeqCst),
3863 "never ran after the borrow was released"
3864 );
3865 let now = Arc::new(AtomicBool::new(false));
3867 let n = now.clone();
3868 run_posted_or_defer(Box::new(move || n.store(true, Ordering::SeqCst)));
3869 assert!(
3870 now.load(Ordering::SeqCst),
3871 "a post with the tree free must run at once"
3872 );
3873 uninstall_tree();
3874 })
3875 .join()
3876 .expect("thread");
3877 }
3878}