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;
887 fn scroll_reveal(&mut self, node: RNode, animated: bool) -> bool;
890 fn patch(&mut self, node: RNode, patch: Box<dyn Any>, affects_size: bool);
891 fn replay(&mut self, node: RNode, ops: Vec<DrawOp>);
892 fn request_frame(&mut self, root: RNode, cb: day_spec::FrameCallback) -> day_spec::CancelFrame;
893 fn set_implicit_anim(&mut self, node: RNode, anim: Option<day_spec::AnimSpec>);
897 fn set_node_opacity(&mut self, node: RNode, opacity: f64);
900 fn set_node_transform(&mut self, node: RNode, t: day_spec::Transform);
903 fn set_node_selectable(&mut self, node: RNode, selectable: bool);
906 fn set_node_input_traits(&mut self, node: RNode, traits: &day_spec::InputTraits);
910 fn set_node_cursor(&mut self, node: RNode, cursor: day_spec::Cursor);
913 fn set_node_drag_region(&mut self, node: RNode, drag: bool);
916 fn note_node_tweaked(&mut self, node: RNode) {
920 let _ = node;
921 }
922 fn mark_needs_measure(&mut self, node: RNode);
923 fn mark_layout_dirty(&mut self);
924 fn invalidate_all_measurements(&mut self);
928 fn layout_if_needed(&mut self);
929 fn set_window_size(&mut self, s: Size);
930 fn set_route(&mut self, route: &str);
934 fn child_count(&self, node: RNode) -> usize;
935 fn first_child(&self, node: RNode) -> Option<RNode>;
936 fn parent_of(&self, node: RNode) -> Option<RNode>;
939 fn node_kind(&self, node: RNode) -> Option<PieceKind>;
940 fn node_id(&self, node: RNode) -> Option<String>;
943 fn node_handle_any(&self, node: RNode) -> Option<Box<dyn Any>>;
949 fn node_frame(&self, node: RNode) -> Option<Rect>;
950 fn node_probe(&self, node: RNode) -> Option<NodeProbe>;
951 fn node_a11y(&self, node: RNode) -> Option<A11yProps>;
953 fn read_native(&self, node: RNode) -> Option<day_spec::NativeSnapshot>;
956 fn a11y_nodes(&self) -> Vec<(String, PieceKind, A11yProps, day_spec::NativeSnapshot)>;
959 fn find_by_id(&self, id: &str) -> Option<RNode>;
960 fn toggle_sidebar(&mut self, _host: RNode) -> bool {
965 false
966 }
967 fn native_back(&mut self) -> bool {
971 false
972 }
973 fn prepare_snapshot(
974 &mut self,
975 root: Option<RNode>,
976 revision: u32,
977 ) -> Result<day_spec::capture::Readiness, String>;
978 fn snapshot(&mut self) -> Result<Vec<u8>, String>;
979 fn snapshot_origin(&mut self) -> Option<Point>;
982 fn snapshot_chrome(&mut self) -> Result<Vec<u8>, String>;
984
985 fn decode_image(&mut self, req: u64, id: day_spec::BitmapId, bytes: &[u8]);
991 fn image_info(&mut self, id: day_spec::BitmapId) -> Option<day_spec::BitmapInfo>;
993 fn image_properties(&mut self, id: day_spec::BitmapId) -> Option<day_spec::ImageProperties>;
995 fn encode_image(&mut self, req: u64, id: day_spec::BitmapId, spec: &day_spec::EncodeSpec);
997 fn image_encode_formats(&mut self) -> Vec<day_spec::ImageFormat>;
999 fn release_image(&mut self, id: day_spec::BitmapId);
1001 fn render_bitmap(
1004 &mut self,
1005 req: u64,
1006 id: day_spec::BitmapId,
1007 ops: &[day_spec::DrawOp],
1008 size: Size,
1009 scale: f64,
1010 );
1011 fn render_pdf(&mut self, req: u64, pages: &[day_spec::PdfPage], info: &day_spec::PdfInfo);
1014 fn window_image_support(&mut self) -> day_spec::Support;
1016 fn font_families(&mut self) -> Vec<day_spec::FontFamilyInfo>;
1018 fn measure_text(
1020 &mut self,
1021 text: &str,
1022 size: f64,
1023 font: &day_spec::CanvasFont,
1024 ) -> Option<day_spec::TextMetrics>;
1025 fn ui_idle(&mut self) -> bool;
1027 fn root_node(&self) -> RNode;
1028
1029 fn open_window_root(&mut self, options: &WindowOptions, kind: WindowKind) -> WindowRootReply;
1038 fn adopt_window_root(&mut self, root: RNode, raw: day_spec::RawHandle, size: Size) -> bool;
1042 fn remove_window_root(&mut self, root: RNode);
1045 fn set_root_size(&mut self, root: RNode, s: Size);
1047
1048 fn fit_window(&mut self, root: RNode, size: Size);
1052 fn add_extra_layout_root(&mut self, node: RNode, size: Size);
1058 fn drop_extra_layout_root(&mut self, node: RNode);
1061 fn close_native_window(&mut self, root: RNode);
1064 fn quit_app(&mut self);
1066 fn focus_native_window(&mut self, root: RNode);
1068 fn set_native_window_title(&mut self, root: RNode, title: &str);
1070 fn apply_window(&mut self, root: RNode, change: &day_spec::WindowChange);
1073 fn set_dock_menu(&mut self, items: &[day_spec::MenuItem]);
1075 fn set_status_items(&mut self, items: &[day_spec::StatusItemSpec]);
1077 fn set_app_progress(&mut self, progress: day_spec::AppProgress);
1079 fn set_dock_visible(&mut self, visible: bool);
1081 fn set_keep_running(&mut self, keep: bool);
1083 fn native_window_frame(&self, root: RNode) -> Option<day_spec::Rect>;
1085 fn monitors(&self) -> Vec<day_spec::Monitor>;
1087 fn set_launcher_shortcuts(&mut self, shortcuts: &[day_spec::LauncherShortcut]);
1089 fn snapshot_of(&mut self, root: RNode) -> Result<Vec<u8>, String>;
1091 fn capability(&self, cap: Cap) -> Support;
1093 fn attach_refresh(&mut self, node: RNode, host: NodeId);
1095 fn set_presented(&mut self, node: RNode, visible: bool);
1096 fn supports_lifecycle(&self, phase: day_spec::Lifecycle) -> bool;
1098 fn present(&mut self, req: u64, spec: &present::PresentSpec);
1100 fn dismiss(&mut self, req: u64);
1102 fn open_url(&mut self, url: &str);
1104 fn share_support(&self) -> Support;
1105 fn share_url(&mut self, url: &str, title: &str) -> bool;
1106 fn application_handlers(
1107 &mut self,
1108 query: &day_spec::applications::HandlerQuery,
1109 ) -> Result<day_spec::applications::ApplicationHandlers, day_spec::applications::ApplicationError>;
1110 fn open_url_with_application(
1111 &mut self,
1112 url: &str,
1113 application: &str,
1114 completion: day_spec::applications::OpenApplicationCompletion,
1115 );
1116
1117 fn defer_system_gestures(&mut self, edges: day_spec::Edges);
1119 fn set_status_bar_hidden(&mut self, hidden: bool);
1122
1123 fn install_list(&mut self, node: RNode, driver: crate::list::ListDriver);
1127 fn list_prepare_cell(
1131 &mut self,
1132 node: RNode,
1133 key: usize,
1134 cell: RawHandle,
1135 header: bool,
1136 ) -> crate::list::CellStep;
1137 fn list_store_cell(
1139 &mut self,
1140 node: RNode,
1141 key: usize,
1142 anchor: RNode,
1143 built: crate::list::BuiltRow,
1144 header: bool,
1145 );
1146 fn list_layout_cell(&mut self, node: RNode, key: usize);
1148 fn list_layout_cell_width(&mut self, node: RNode, key: usize, width: f64);
1152 fn list_cell_height(&self, node: RNode, key: usize) -> f64;
1155 fn list_cell_keys(&self, node: RNode) -> Vec<usize>;
1159 fn list_recycle_cell(&mut self, node: RNode, key: usize);
1163 fn list_release_cell(&mut self, node: RNode, key: usize);
1165 fn list_pin_cell(&mut self, node: RNode, key: usize);
1168 fn list_reload(&mut self, node: RNode);
1170 fn list_splice(&mut self, node: RNode, deltas: Vec<day_spec::props::RowDelta>);
1171 fn set_undo_state(&mut self, state: &day_spec::UndoState);
1172 fn set_edit_state(&mut self, state: &day_spec::EditState);
1173 fn modifiers(&mut self) -> day_spec::Modifiers;
1174 fn list_scroll_to_end(&mut self, node: RNode);
1176 fn list_scroll_to_row(&mut self, node: RNode, row: usize);
1178 fn list_set_selected(&mut self, node: RNode, rows: Vec<usize>);
1181 fn list_set_editing(&mut self, node: RNode, on: bool);
1184 fn list_driver(&mut self, node: RNode) -> Option<std::rc::Rc<crate::list::ListDriver>>;
1187
1188 fn install_tree(&mut self, node: RNode, driver: crate::tree_driver::TreeDriver);
1192 fn tree_prepare_cell(
1194 &mut self,
1195 node: RNode,
1196 key: usize,
1197 cell: RawHandle,
1198 ) -> crate::tree_driver::TreeCellStep;
1199 fn tree_store_cell(
1201 &mut self,
1202 node: RNode,
1203 key: usize,
1204 anchor: RNode,
1205 built: crate::tree_driver::TreeBuiltRow,
1206 );
1207 fn tree_layout_cell(&mut self, node: RNode, key: usize);
1209 fn tree_layout_cell_width(&mut self, node: RNode, key: usize, width: f64);
1211 fn tree_layout_cell_size(&mut self, node: RNode, key: usize, size: Size);
1212 fn tree_cell_height(&self, node: RNode, key: usize) -> f64;
1215 fn tree_recycle_cell(&mut self, node: RNode, key: usize);
1219 fn tree_reload(&mut self, node: RNode);
1222 fn tree_set_expanded(&mut self, node: RNode, token: u64, expanded: bool);
1224 fn tree_set_selected(&mut self, node: RNode, tokens: Vec<u64>);
1226 fn tree_reveal(&mut self, node: RNode, token: u64);
1228 fn tree_driver(&mut self, node: RNode) -> Option<std::rc::Rc<crate::tree_driver::TreeDriver>>;
1231 fn set_drag_source(&mut self, node: RNode, source: day_spec::transfer::Source);
1233 fn set_drop_target(&mut self, node: RNode, target: day_spec::transfer::Target);
1234 fn set_context_menu_fn(&mut self, node: RNode, f: day_spec::ContextMenuFn);
1236 fn context_menu_of(&mut self, node: RNode) -> Option<day_spec::ContextMenuFn>;
1241}
1242
1243impl<B: Toolkit> TreeOps for Tree<B> {
1244 fn set_presented(&mut self, node: RNode, visible: bool) {
1245 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
1246 self.toolkit.set_presented(&handle, visible);
1247 }
1248 }
1249
1250 fn attach_refresh(&mut self, node: RNode, host: NodeId) {
1251 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
1252 self.toolkit.attach_refresh(&handle, host);
1253 }
1254 }
1255
1256 fn capability(&self, cap: Cap) -> Support {
1257 self.toolkit.capability(cap)
1258 }
1259
1260 fn supports_lifecycle(&self, phase: day_spec::Lifecycle) -> bool {
1261 self.toolkit.supports_lifecycle(phase)
1262 }
1263
1264 fn present(&mut self, req: u64, spec: &present::PresentSpec) {
1265 self.toolkit.present(req, spec);
1266 }
1267
1268 fn dismiss(&mut self, req: u64) {
1269 self.toolkit.dismiss(req);
1270 }
1271
1272 fn open_url(&mut self, url: &str) {
1273 self.toolkit.open_url(url);
1274 }
1275 fn share_support(&self) -> Support {
1276 self.toolkit.share_support()
1277 }
1278 fn share_url(&mut self, url: &str, title: &str) -> bool {
1279 self.toolkit.share_url(url, title)
1280 }
1281
1282 fn application_handlers(
1283 &mut self,
1284 query: &day_spec::applications::HandlerQuery,
1285 ) -> Result<day_spec::applications::ApplicationHandlers, day_spec::applications::ApplicationError>
1286 {
1287 self.toolkit.application_handlers(query)
1288 }
1289
1290 fn open_url_with_application(
1291 &mut self,
1292 url: &str,
1293 application: &str,
1294 completion: day_spec::applications::OpenApplicationCompletion,
1295 ) {
1296 self.toolkit
1297 .open_url_with_application(url, application, completion);
1298 }
1299
1300 fn set_appearance(&mut self, dark: Option<bool>) {
1301 self.toolkit.set_appearance(dark);
1302 }
1303
1304 fn set_app_badge(&mut self, badge: &day_spec::AppBadge) {
1305 self.toolkit.set_app_badge(badge);
1306 }
1307
1308 fn defer_system_gestures(&mut self, edges: day_spec::Edges) {
1309 self.toolkit.defer_system_gestures(edges);
1310 }
1311
1312 fn set_status_bar_hidden(&mut self, hidden: bool) {
1313 self.toolkit.set_status_bar_hidden(hidden);
1314 }
1315
1316 fn create_node(
1317 &mut self,
1318 kind: PieceKind,
1319 props: &dyn Any,
1320 layout: Rc<dyn Layout>,
1321 flex: Flex,
1322 native: bool,
1323 is_boundary: bool,
1324 scope: Scope,
1325 ) -> RNode {
1326 let mut probe = NodeProbe {
1327 enabled: true,
1328 ..Default::default()
1329 };
1330 {
1331 use day_spec::props::*;
1332 if let Some(p) = props.downcast_ref::<LabelProps>() {
1333 probe.text = p.text.clone();
1334 } else if let Some(p) = props.downcast_ref::<NavMenuProps>() {
1335 probe.selected = p.selected.map(|i| i as i64).unwrap_or(-1);
1336 probe.value = probe.selected as f64;
1337 } else if let Some(p) = props.downcast_ref::<ButtonProps>() {
1338 probe.text = p.title.clone();
1339 probe.enabled = p.enabled;
1340 probe.flag = p.selected;
1342 } else if let Some(p) = props.downcast_ref::<ToggleProps>() {
1343 probe.flag = p.on;
1344 probe.enabled = p.enabled;
1345 } else if let Some(p) = props.downcast_ref::<SliderProps>() {
1346 probe.value = p.value;
1347 probe.enabled = p.enabled;
1348 } else if let Some(p) = props.downcast_ref::<TextFieldProps>() {
1349 probe.text = p.text.clone();
1350 probe.enabled = p.enabled;
1351 } else if let Some(p) = props.downcast_ref::<ProgressProps>() {
1352 probe.flag = p.value.is_none();
1354 probe.value = p.value.unwrap_or(0.0);
1355 } else if let Some(p) = props.downcast_ref::<PickerProps>() {
1356 probe.selected = p.selected as i64;
1357 probe.value = p.selected as f64;
1358 probe.text = p.options.get(p.selected).cloned().unwrap_or_default();
1361 probe.options = p.options.clone();
1362 probe.enabled = p.enabled;
1363 } else if let Some(p) = props.downcast_ref::<TextAreaProps>() {
1364 probe.text = p.text.clone();
1365 }
1366 }
1367 let node = self.nodes.insert(NodeData {
1368 kind,
1369 handle: None,
1370 parent: RNode::null(),
1371 children: Vec::new(),
1372 layout,
1373 flex,
1374 scope,
1375 id: None,
1376 parked_id: None,
1377 a11y: Default::default(),
1378 cache: Vec::new(),
1379 baseline_cache: None,
1380 probe,
1381 needs_measure: true,
1382 needs_layout: true,
1383 last_native_frame: None,
1384 scroll_content: None,
1385 implicit_anim: None,
1386 tweaked: false,
1387 is_boundary,
1388 });
1389 if native {
1390 let h = self.toolkit.realize(kind, props, rnode_to_id(node));
1391 self.nodes[node].handle = Some(h);
1392 }
1393 node
1394 }
1395
1396 fn attach(&mut self, parent: RNode, child: RNode) {
1397 let index = self
1398 .nodes
1399 .get(parent)
1400 .map(|p| p.children.len())
1401 .unwrap_or(0);
1402 self.attach_impl(parent, child, index);
1403 }
1404
1405 fn native_window_tabs(&self) -> bool {
1406 self.toolkit.native_window_tabs()
1407 }
1408 fn native_document_tabs(&self) -> bool {
1409 self.toolkit.native_document_tabs()
1410 }
1411 fn group_windows(&mut self, roots: &[RNode]) {
1412 let hosts = roots
1413 .iter()
1414 .filter_map(|r| self.nodes.get(*r)?.handle.clone())
1415 .collect::<Vec<_>>();
1416 self.toolkit.group_windows(&hosts);
1417 }
1418 fn window_tab_order(&self, root: RNode) -> Vec<day_spec::NodeId> {
1419 self.nodes
1420 .get(root)
1421 .and_then(|n| n.handle.as_ref())
1422 .map(|h| self.toolkit.window_tab_order(h))
1423 .unwrap_or_default()
1424 }
1425 fn reparent(&mut self, child: RNode, parent: RNode) -> bool {
1426 if !self.nodes.contains_key(child) || !self.nodes.contains_key(parent) {
1427 return false;
1428 }
1429 let old = self.nodes[child].parent;
1430 if old == parent {
1431 return true;
1432 }
1433 if self.windows.iter().any(|w| w.root == child) {
1435 return false;
1436 }
1437 let mut up = parent;
1438 while let Some(n) = self.nodes.get(up) {
1439 if up == child {
1440 return false;
1441 }
1442 up = n.parent;
1443 }
1444 let ancestor = self.native_ancestor(old);
1445 let handle = self.nodes.get(ancestor).and_then(|n| n.handle.clone());
1446 let mut roots = Vec::new();
1447 if self.nodes[child].handle.is_some() {
1448 roots.push(child);
1449 } else {
1450 self.native_descendants(child, &mut roots);
1451 }
1452 if let Some(h) = handle {
1453 for r in roots {
1454 self.toolkit
1456 .remove(&h, self.nodes[r].handle.as_ref().unwrap());
1457 }
1458 }
1459 if let Some(p) = self.nodes.get_mut(old) {
1460 p.children.retain(|n| *n != child);
1461 }
1462 self.mark_needs_measure_impl(old);
1463 self.attach(parent, child);
1464 self.layout_dirty = true;
1465 true
1466 }
1467
1468 fn attach_at(&mut self, parent: RNode, child: RNode, index: usize) {
1469 self.attach_impl(parent, child, index);
1470 }
1471
1472 fn reorder_children(&mut self, parent: RNode, order: Vec<RNode>) {
1473 if self.nodes.get(parent).is_some_and(|p| p.children == order) {
1481 return;
1482 }
1483 if let Some(p) = self.nodes.get_mut(parent) {
1484 p.children = order;
1485 }
1486 let ancestor = self.native_ancestor(parent);
1488 let anc_handle = self.nodes[ancestor]
1489 .handle
1490 .clone()
1491 .expect("native ancestor");
1492 let mut desired = Vec::new();
1493 self.native_descendants(ancestor, &mut desired);
1494 for (i, r) in desired.iter().enumerate() {
1495 let h = self.nodes[*r].handle.clone().unwrap();
1496 self.toolkit.move_child(&anc_handle, &h, i);
1497 }
1498 self.mark_needs_measure_impl(parent);
1499 }
1500
1501 fn remove_subtree(&mut self, node: RNode) {
1502 debug_assert!(
1505 !self.windows.iter().any(|w| w.root == node),
1506 "remove_subtree called with a window root"
1507 );
1508 self.remove_subtree_impl(node);
1509 }
1510
1511 fn on_event(&mut self, node: RNode, h: EventHandler) {
1512 self.handlers.entry(node).or_default().push(h);
1513 }
1514
1515 fn handlers_for(&self, node: RNode) -> Vec<EventHandler> {
1516 self.handlers.get(&node).cloned().unwrap_or_default()
1517 }
1518 fn native_view_of(&self, node: RNode) -> Option<RNode> {
1519 let mut cur = node;
1520 for _ in 0..16 {
1521 let n = self.nodes.get(cur)?;
1522 if n.handle.is_some() {
1523 return Some(cur);
1524 }
1525 match n.children.as_slice() {
1526 [only] => cur = *only,
1527 _ => return None,
1528 }
1529 }
1530 None
1531 }
1532 fn gesture_target(&self, node: RNode, kind: day_spec::GestureKind) -> Option<RNode> {
1533 let has = |n: RNode| self.gestures.get(&n).is_some_and(|k| k.contains(&kind));
1534 if has(node) {
1535 return Some(node);
1536 }
1537 let mut cur = node;
1540 let mut bound = self.element_outer.get(&node).copied();
1541 while let Some(outer) = bound {
1542 if cur == outer {
1543 bound = self.element_outer.get(&outer).copied();
1544 continue;
1545 }
1546 let Some(parent) = self.nodes.get(cur).map(|n| n.parent) else {
1547 break;
1548 };
1549 if parent == cur || !self.nodes.contains_key(parent) {
1550 break;
1551 }
1552 cur = parent;
1553 if has(cur) {
1554 return Some(cur);
1555 }
1556 }
1557 self.wrappers_above(cur).into_iter().find(|w| has(*w))
1558 }
1559
1560 fn set_element_outer(&mut self, inner: RNode, outer: RNode) {
1561 if inner != outer {
1562 self.element_outer.insert(inner, outer);
1563 }
1564 }
1565
1566 fn wrappers_above(&self, node: RNode) -> Vec<RNode> {
1567 let mut out = Vec::new();
1568 let mut cur = node;
1569 for _ in 0..16 {
1570 let Some(parent) = self.nodes.get(cur).map(|n| n.parent) else {
1571 break;
1572 };
1573 match self.nodes.get(parent) {
1574 Some(p) if p.handle.is_none() && p.children.len() == 1 && parent != cur => {
1575 out.push(parent);
1576 cur = parent;
1577 }
1578 _ => break,
1579 }
1580 }
1581 out
1582 }
1583 fn node_exists(&self, node: RNode) -> bool {
1584 self.nodes.contains_key(node)
1585 }
1586 fn dark_mode(&mut self) -> bool {
1587 self.toolkit.dark_mode()
1588 }
1589 fn reduce_motion(&mut self) -> bool {
1590 self.toolkit.reduce_motion()
1591 }
1592
1593 fn mounted_id_families(&self) -> Vec<String> {
1594 self.mounted_id_families.iter().cloned().collect()
1595 }
1596 fn set_id(&mut self, node: RNode, id: String) {
1597 if self.nodes.contains_key(node) {
1598 self.mounted_id_families
1599 .insert(crate::element_id::family_of(&id).to_owned());
1600 }
1601 if let Some(n) = self.nodes.get_mut(node) {
1602 n.id = Some(id.clone());
1603 n.a11y.merge(&A11yProps {
1604 identifier: Some(id),
1605 ..Default::default()
1606 });
1607 if let Some(h) = n.handle.clone() {
1608 self.toolkit.set_a11y(&h, &n.a11y);
1609 }
1610 }
1611 }
1612
1613 fn forward_whole_row(&mut self, wrapper: RNode, inner: RNode) {
1614 let whole = self.nodes.get(inner).is_some_and(|n| n.flex.whole_row);
1615 if let Some(n) = self.nodes.get_mut(wrapper) {
1616 n.flex.whole_row = whole;
1617 }
1618 }
1619
1620 fn set_grid_facts(&mut self, node: RNode, facts: GridFacts) {
1621 if let Some(n) = self.nodes.get_mut(node) {
1622 let g = &mut n.flex.grid;
1623 if facts.is_row {
1624 g.is_row = true;
1625 }
1626 if facts.row_valign.is_some() {
1627 g.row_valign = facts.row_valign;
1628 }
1629 if facts.col_span != 0 {
1630 g.col_span = facts.col_span;
1631 }
1632 if facts.align.is_some() {
1633 g.align = facts.align;
1634 }
1635 }
1636 }
1637
1638 fn announce(&mut self, text: &str, urgent: bool) {
1639 self.toolkit.announce(text, urgent);
1640 }
1641
1642 fn set_a11y(&mut self, node: RNode, a11y: A11yProps) {
1643 if let Some(n) = self.nodes.get_mut(node) {
1644 n.a11y.merge(&a11y);
1647 if n.kind == kinds::CANVAS
1651 && let Some(text) = n.a11y.value.clone().or_else(|| n.a11y.label.clone())
1652 {
1653 n.probe.text = text;
1654 }
1655 if let Some(h) = n.handle.clone() {
1656 self.toolkit.set_a11y(&h, &n.a11y);
1657 }
1658 }
1659 }
1660
1661 fn enable_gesture(&mut self, node: RNode, kind: day_spec::GestureKind) {
1662 let kinds = self.gestures.entry(node).or_default();
1663 if !kinds.contains(&kind) {
1664 kinds.push(kind);
1665 }
1666 let mut cur = node;
1672 for _ in 0..16 {
1673 let Some(n) = self.nodes.get(cur) else { return };
1674 if let Some(h) = n.handle.clone() {
1675 if kind == day_spec::GestureKind::Tap && n.kind == kinds::BUTTON {
1676 log::warn!(
1677 "tap gesture attached to native button {:?} (id: {:?}): \
1678 use .action(...) instead of .on_tap(...) or .on_tap_at(...). \
1679 A separate gesture recognizer can delay pressed feedback and \
1680 bypass native keyboard, accessibility, and disabled behavior",
1681 rnode_to_id(cur),
1682 n.id,
1683 );
1684 }
1685 self.toolkit.enable_gesture(&h, rnode_to_id(node), kind);
1686 return;
1687 }
1688 match n.children.as_slice() {
1689 [only] => cur = *only,
1690 _ => break,
1691 }
1692 }
1693 log::warn!(
1694 "enable_gesture({kind:?}) found no native view under the target node — \
1695 the gesture will not fire natively (attach it to a piece with a native handle)"
1696 );
1697 }
1698
1699 fn focus_node(&mut self, node: RNode, focused: bool) {
1700 let mut cur = node;
1703 for _ in 0..16 {
1704 let Some(n) = self.nodes.get(cur) else { return };
1705 if let Some(h) = n.handle.clone() {
1706 self.toolkit.focus(&h, rnode_to_id(node), focused);
1707 return;
1708 }
1709 match n.children.as_slice() {
1710 [only] => cur = *only,
1711 _ => return,
1712 }
1713 }
1714 }
1715
1716 fn set_focusable(&mut self, node: RNode, focusable: bool) {
1717 let mut cur = node;
1721 for _ in 0..16 {
1722 let Some(n) = self.nodes.get(cur) else { return };
1723 if let Some(h) = n.handle.clone() {
1724 self.toolkit.set_focusable(&h, rnode_to_id(node), focusable);
1725 return;
1726 }
1727 match n.children.as_slice() {
1728 [only] => cur = *only,
1729 _ => break,
1730 }
1731 }
1732 log::warn!(
1733 "set_focusable found no native view under the target node — \
1734 the piece will not join the focus order"
1735 );
1736 }
1737
1738 fn set_probe_focused(&mut self, node: RNode, focused: bool) {
1739 if let Some(n) = self.nodes.get_mut(node) {
1740 n.probe.focused = focused;
1741 }
1742 for w in self.wrappers_above(node) {
1747 if let Some(p) = self.nodes.get_mut(w) {
1748 p.probe.focused = focused;
1749 }
1750 }
1751 let mut cur = node;
1757 for _ in 0..16 {
1758 let Some(n) = self.nodes.get(cur) else { break };
1759 if n.handle.is_some() && cur != node {
1760 if let Some(p) = self.nodes.get_mut(cur) {
1761 p.probe.focused = focused;
1762 }
1763 break;
1764 }
1765 let next = match n.children.as_slice() {
1766 [only] => *only,
1767 _ => break,
1768 };
1769 if let Some(p) = self.nodes.get_mut(next) {
1770 p.probe.focused = focused;
1771 }
1772 cur = next;
1773 }
1774 FOCUSED.with(|f| match focused {
1779 true => f.set(Some(node)),
1780 false if f.get() == Some(node) => f.set(None),
1781 false => {}
1782 });
1783 }
1784
1785 fn set_probe_selected(&mut self, node: RNode, selected: Option<usize>) {
1786 if let Some(n) = self.nodes.get_mut(node) {
1787 n.probe.selected = selected.map(|i| i as i64).unwrap_or(-1);
1790 n.probe.value = n.probe.selected as f64;
1791 }
1792 }
1793
1794 fn set_probe_value(&mut self, node: RNode, value: f64, text: String) {
1795 if let Some(n) = self.nodes.get_mut(node) {
1796 n.probe.value = value;
1797 n.probe.text = text;
1798 }
1799 }
1800
1801 fn set_app_menu(&mut self, items: Vec<day_spec::MenuItem>) {
1802 collect_menu_families(&items, &mut self.mounted_id_families);
1803 self.toolkit.set_app_menu(&items);
1804 }
1805
1806 fn edit_window_toolbar(&mut self, root: RNode, ops: Vec<day_spec::ToolbarOp>) -> bool {
1807 let Some(h) = self.nodes.get(root).and_then(|n| n.handle.clone()) else {
1808 return false;
1809 };
1810 for op in &ops {
1811 if let day_spec::ToolbarOp::Insert { item, .. } = op {
1812 self.mounted_id_families
1813 .insert(crate::element_id::family_of(&item.id).to_owned());
1814 }
1815 }
1816 self.toolkit.edit_toolbar(&h, &ops)
1817 }
1818
1819 fn patch_window_toolbar(&mut self, root: RNode, patch: day_spec::ToolbarPatch) -> bool {
1820 let Some(h) = self.nodes.get(root).and_then(|n| n.handle.clone()) else {
1821 return false;
1822 };
1823 self.toolkit.update_toolbar(&h, &patch);
1824 true
1825 }
1826
1827 fn set_context_menu(&mut self, node: RNode, items: Vec<day_spec::MenuItem>) {
1828 let target = if self.nodes.get(node).is_some_and(|n| n.handle.is_some()) {
1833 Some(node)
1834 } else {
1835 let mut roots = Vec::new();
1836 self.native_descendants(node, &mut roots);
1837 roots.first().copied()
1838 };
1839 let Some(t) = target else {
1840 log::warn!("context_menu on a subtree with no native view — menu dropped");
1841 return;
1842 };
1843 collect_menu_families(&items, &mut self.mounted_id_families);
1844 let model = ContextMenuModel::Items(items.clone());
1845 self.context_menus.insert(node, model.clone());
1846 self.context_menus.insert(t, model);
1847 if let Some(h) = self.nodes.get(t).and_then(|n| n.handle.clone()) {
1848 self.toolkit.set_context_menu(&h, rnode_to_id(t), &items);
1849 }
1850 }
1851
1852 fn scroll_to_target(&mut self, node: RNode, target: &ScrollTarget, animated: bool) -> bool {
1853 if let ScrollTarget::Id(id) = target {
1855 let Some(el) = self.find_by_id(id) else {
1856 return false;
1857 };
1858 return self.scroll_reveal(el, animated);
1859 }
1860 let Some(n) = self.nodes.get(node) else {
1861 return false;
1862 };
1863 if n.kind == kinds::NAV_MENU {
1864 return match (n.handle.clone(), target) {
1865 (Some(h), ScrollTarget::Top | ScrollTarget::Bottom) => self
1866 .toolkit
1867 .nav_scroll_to_edge(&h, matches!(target, ScrollTarget::Bottom)),
1868 _ => false,
1869 };
1870 }
1871 if n.kind != kinds::SCROLL {
1872 return false;
1873 }
1874 let Some(h) = n.handle.clone() else {
1875 return false;
1876 };
1877 let viewport = n.last_native_frame.map(|f| f.size).unwrap_or(Size::ZERO);
1878 let content = n.scroll_content.unwrap_or(viewport);
1879 let rect = match target {
1882 ScrollTarget::Top | ScrollTarget::Leading => Rect::new(0.0, 0.0, 1.0, 1.0),
1883 ScrollTarget::Bottom => Rect::new(0.0, (content.height - 1.0).max(0.0), 1.0, 1.0),
1884 ScrollTarget::Trailing => Rect::new((content.width - 1.0).max(0.0), 0.0, 1.0, 1.0),
1885 ScrollTarget::Offset(p) => Rect::new(p.x, p.y, viewport.width, viewport.height),
1888 ScrollTarget::Id(_) => unreachable!("routed to scroll_reveal above"),
1889 };
1890 self.toolkit
1891 .scroll_to(&h, rect, animated && !crate::reduce_motion_now());
1892 true
1893 }
1894
1895 fn scroll_reveal(&mut self, node: RNode, animated: bool) -> bool {
1896 let Some(mut rect) = self.nodes.get(node).and_then(|n| n.last_native_frame) else {
1900 return false;
1901 };
1902 let mut anc = match self.nodes.get(node) {
1903 Some(n) => n.parent,
1904 None => return false,
1905 };
1906 loop {
1907 let Some(a) = self.nodes.get(anc) else {
1908 return false; };
1910 if a.kind == kinds::SCROLL {
1911 let Some(h) = a.handle.clone() else {
1912 return false;
1913 };
1914 self.toolkit
1915 .scroll_to(&h, rect, animated && !crate::reduce_motion_now());
1916 return true;
1917 }
1918 if a.handle.is_some()
1919 && let Some(f) = a.last_native_frame
1920 {
1921 rect.origin.x += f.origin.x;
1922 rect.origin.y += f.origin.y;
1923 }
1924 anc = a.parent;
1925 }
1926 }
1927
1928 fn patch(&mut self, node: RNode, patch: Box<dyn Any>, affects_size: bool) {
1929 {
1930 use day_spec::props::*;
1931 if let Some(n) = self.nodes.get_mut(node) {
1932 if let Some(p) = patch.downcast_ref::<LabelPatch>() {
1933 match p {
1937 LabelPatch::Text(t) => n.probe.text = t.clone(),
1938 LabelPatch::Runs(t, _) => n.probe.text = t.clone(),
1939 LabelPatch::Font(_) | LabelPatch::Color(_) => {}
1940 }
1941 } else if let Some(p) = patch.downcast_ref::<ButtonPatch>() {
1942 match p {
1943 ButtonPatch::Title(t) => n.probe.text = t.clone(),
1944 ButtonPatch::Content(c) => n.probe.text = c.title.clone(),
1945 ButtonPatch::Enabled(e) => n.probe.enabled = *e,
1946 ButtonPatch::Selected(on) => n.probe.flag = *on,
1947 ButtonPatch::Style(_) => {}
1949 }
1950 } else if let Some(p) = patch.downcast_ref::<TogglePatch>() {
1951 match p {
1952 TogglePatch::On(v) => n.probe.flag = *v,
1953 TogglePatch::Enabled(e) => n.probe.enabled = *e,
1954 }
1955 } else if let Some(p) = patch.downcast_ref::<SliderPatch>() {
1956 match p {
1957 SliderPatch::Value(v) => n.probe.value = *v,
1958 SliderPatch::Enabled(e) => n.probe.enabled = *e,
1959 }
1960 } else if let Some(p) = patch.downcast_ref::<PickerPatch>() {
1961 match p {
1962 PickerPatch::Selected(i) => {
1963 n.probe.selected = *i as i64;
1964 n.probe.value = *i as f64;
1965 }
1966 PickerPatch::Options(opts) => n.probe.options = opts.clone(),
1967 PickerPatch::Enabled(e) => n.probe.enabled = *e,
1968 }
1969 let i = n.probe.selected.max(0) as usize;
1970 n.probe.text = n.probe.options.get(i).cloned().unwrap_or_default();
1971 } else if let Some(TextAreaPatch::SetText(t)) =
1972 patch.downcast_ref::<TextAreaPatch>()
1973 {
1974 n.probe.text = t.clone();
1975 } else if let Some(ProgressPatch::Value(v)) = patch.downcast_ref::<ProgressPatch>()
1976 {
1977 n.probe.flag = v.is_none();
1978 n.probe.value = v.unwrap_or(0.0);
1979 } else if let Some(p) = patch.downcast_ref::<TextFieldPatch>() {
1980 match p {
1981 TextFieldPatch::Text { text, .. } => n.probe.text = text.clone(),
1982 TextFieldPatch::Enabled(e) => n.probe.enabled = *e,
1983 _ => {}
1984 }
1985 } else if let Some(NavMenuPatch::Selected(sel)) =
1986 patch.downcast_ref::<NavMenuPatch>()
1987 {
1988 n.probe.selected = sel.map(|i| i as i64).unwrap_or(-1);
1989 n.probe.value = n.probe.selected as f64;
1990 }
1991 }
1992 }
1993 let Some(n) = self.nodes.get(node) else {
1994 return;
1995 };
1996 let kind = n.kind;
1997 if let Some(h) = n.handle.clone() {
1998 let anim = self.resolve_anim(node);
1999 self.toolkit.update(&h, kind, patch.as_ref(), anim.as_ref());
2000 }
2001 if affects_size {
2002 self.mark_needs_measure_impl(node);
2003 }
2004 }
2005
2006 fn set_implicit_anim(&mut self, node: RNode, anim: Option<day_spec::AnimSpec>) {
2007 let had = self
2008 .nodes
2009 .get(node)
2010 .map(|n| n.implicit_anim.is_some())
2011 .unwrap_or(false);
2012 if let Some(n) = self.nodes.get_mut(node) {
2013 n.implicit_anim = anim;
2014 } else {
2015 return;
2016 }
2017 match (had, anim.is_some()) {
2018 (false, true) => self.implicit_anim_count += 1,
2019 (true, false) => self.implicit_anim_count = self.implicit_anim_count.saturating_sub(1),
2020 _ => {}
2021 }
2022 }
2023
2024 fn set_node_opacity(&mut self, node: RNode, opacity: f64) {
2025 let Some(h) = self.nodes.get(node).and_then(|n| n.handle.clone()) else {
2026 return;
2027 };
2028 let anim = self.resolve_anim(node);
2029 self.toolkit.set_opacity(&h, opacity, anim.as_ref());
2030 }
2031
2032 fn set_node_transform(&mut self, node: RNode, t: day_spec::Transform) {
2033 let Some(n) = self.nodes.get(node) else {
2034 return;
2035 };
2036 let Some(h) = n.handle.clone() else {
2037 return;
2038 };
2039 let size = n.last_native_frame.map(|f| f.size).unwrap_or(Size::ZERO);
2042 let anim = self.resolve_anim(node);
2043 self.toolkit.set_transform(&h, t, size, anim.as_ref());
2044 }
2045
2046 fn set_node_selectable(&mut self, node: RNode, selectable: bool) {
2047 let Some(h) = self.nodes.get(node).and_then(|n| n.handle.clone()) else {
2048 return;
2049 };
2050 if let Some(new) = self.toolkit.set_selectable(&h, selectable)
2053 && let Some(n) = self.nodes.get_mut(node)
2054 {
2055 if n.tweaked {
2056 log::warn!(
2057 "`.selectable()` rebuilt this widget as a different native class, \
2058 discarding an earlier tweak's changes — apply `.selectable()` BEFORE the \
2059 tweak so it runs against the widget that ships (docs/tweaks.md)."
2060 );
2061 }
2062 n.handle = Some(new);
2063 }
2064 }
2065
2066 fn set_node_input_traits(&mut self, node: RNode, traits: &day_spec::InputTraits) {
2067 let Some(h) = self.nodes.get(node).and_then(|n| n.handle.clone()) else {
2068 return;
2069 };
2070 if let Some(new) = self.toolkit.set_input_traits(&h, traits)
2073 && let Some(n) = self.nodes.get_mut(node)
2074 {
2075 if n.tweaked {
2076 log::warn!(
2077 "a text field's `.secure()` rebuilt this widget as a different native \
2078 class, discarding an earlier tweak's changes (docs/tweaks.md)."
2079 );
2080 }
2081 n.handle = Some(new);
2082 n.last_native_frame = None;
2083 self.mark_needs_measure_impl(node);
2084 }
2085 }
2086
2087 fn set_node_cursor(&mut self, node: RNode, cursor: day_spec::Cursor) {
2088 let Some(h) = self
2092 .native_view_of(node)
2093 .and_then(|n| self.nodes.get(n))
2094 .and_then(|n| n.handle.clone())
2095 else {
2096 return;
2097 };
2098 self.toolkit.set_cursor(&h, cursor);
2099 }
2100
2101 fn set_node_drag_region(&mut self, node: RNode, drag: bool) {
2102 let Some(h) = self
2104 .native_view_of(node)
2105 .and_then(|n| self.nodes.get(n))
2106 .and_then(|n| n.handle.clone())
2107 else {
2108 log::warn!(
2109 "window_drag_region found no native view under the target node; give the \
2110 region a piece with a native view (a background, a row)"
2111 );
2112 return;
2113 };
2114 self.toolkit.set_drag_region(&h, drag);
2115 }
2116
2117 fn note_node_tweaked(&mut self, node: RNode) {
2118 if let Some(n) = self.nodes.get_mut(node) {
2119 n.tweaked = true;
2120 }
2121 }
2122
2123 fn request_frame(&mut self, root: RNode, cb: day_spec::FrameCallback) -> day_spec::CancelFrame {
2124 if let Some(h) = self.nodes.get(root).and_then(|n| n.handle.clone()) {
2125 self.toolkit.request_frame(&h, cb)
2126 } else {
2127 Box::new(|| {})
2128 }
2129 }
2130
2131 fn replay(&mut self, node: RNode, ops: Vec<DrawOp>) {
2132 let Some(n) = self.nodes.get(node) else {
2133 return;
2134 };
2135 let size = n.last_native_frame.map(|f| f.size).unwrap_or(Size::ZERO);
2136 if let Some(h) = n.handle.clone() {
2137 self.toolkit.replay(&h, &ops, size);
2138 }
2139 }
2140
2141 fn mark_needs_measure(&mut self, node: RNode) {
2142 self.mark_needs_measure_impl(node);
2143 }
2144
2145 fn mark_layout_dirty(&mut self) {
2146 self.layout_dirty = true;
2147 }
2148
2149 fn invalidate_all_measurements(&mut self) {
2150 for (_, n) in self.nodes.iter_mut() {
2151 n.needs_measure = true;
2152 n.needs_layout = true;
2153 n.cache.clear();
2154 }
2155 for state in self.lists.values_mut() {
2159 for cell in state.cells.values_mut() {
2160 cell.laid_width = None;
2161 }
2162 }
2163 for state in self.trees.values_mut() {
2164 for cell in state.cells.values_mut() {
2165 cell.laid_width = None;
2166 }
2167 }
2168 self.layout_dirty = true;
2169 }
2170
2171 fn layout_if_needed(&mut self) {
2172 if !self.layout_dirty {
2173 return;
2174 }
2175 self.layout_dirty = false;
2176 self.layout_now();
2177 }
2178
2179 fn set_route(&mut self, route: &str) {
2180 self.toolkit.set_route(route);
2181 }
2182
2183 fn set_window_size(&mut self, s: Size) {
2184 if s != self.windows[0].size {
2187 self.windows[0].size = s;
2188 let root = self.windows[0].root;
2189 self.mark_needs_measure_impl(root);
2190 }
2191 }
2192
2193 fn child_count(&self, node: RNode) -> usize {
2194 self.nodes.get(node).map(|n| n.children.len()).unwrap_or(0)
2195 }
2196
2197 fn first_child(&self, node: RNode) -> Option<RNode> {
2198 self.nodes
2199 .get(node)
2200 .and_then(|n| n.children.first().copied())
2201 }
2202
2203 fn parent_of(&self, node: RNode) -> Option<RNode> {
2204 self.nodes
2205 .get(node)
2206 .map(|n| n.parent)
2207 .filter(|p| !p.is_null() && *p != node)
2208 }
2209
2210 fn node_kind(&self, node: RNode) -> Option<PieceKind> {
2211 self.nodes.get(node).map(|n| n.kind)
2212 }
2213
2214 fn node_id(&self, node: RNode) -> Option<String> {
2215 self.nodes.get(node).and_then(|n| n.id.clone())
2216 }
2217
2218 fn node_handle_any(&self, node: RNode) -> Option<Box<dyn Any>> {
2219 self.nodes
2220 .get(node)
2221 .and_then(|n| n.handle.clone())
2222 .map(|h| Box::new(h) as Box<dyn Any>)
2223 }
2224
2225 fn node_frame(&self, node: RNode) -> Option<Rect> {
2226 self.nodes.get(node).and_then(|n| n.last_native_frame)
2227 }
2228
2229 fn node_probe(&self, node: RNode) -> Option<NodeProbe> {
2230 self.nodes.get(node).map(|n| n.probe.clone())
2231 }
2232
2233 fn node_a11y(&self, node: RNode) -> Option<A11yProps> {
2234 self.nodes.get(node).map(|n| n.a11y.clone())
2235 }
2236
2237 fn read_native(&self, node: RNode) -> Option<day_spec::NativeSnapshot> {
2238 let n = self.nodes.get(node)?;
2239 let h = n.handle.as_ref()?;
2240 Some(self.toolkit.read_native(h))
2241 }
2242
2243 fn a11y_nodes(&self) -> Vec<(String, PieceKind, A11yProps, day_spec::NativeSnapshot)> {
2244 self.nodes
2245 .values()
2246 .filter_map(|n| {
2247 let id = n.id.clone()?;
2248 let h = n.handle.as_ref()?;
2249 Some((id, n.kind, n.a11y.clone(), self.toolkit.read_native(h)))
2250 })
2251 .collect()
2252 }
2253
2254 fn find_by_id(&self, id: &str) -> Option<RNode> {
2255 let focused = crate::windows::focused_window()
2258 .map(|w| id_to_rnode(crate::windows::window_node_id(&w)))
2259 .unwrap_or_else(|| self.root());
2260 let mut fallback = None;
2261 for (key, node) in &self.nodes {
2262 if node.id.as_deref() != Some(id) {
2263 continue;
2264 }
2265 fallback.get_or_insert(key);
2266 let mut ancestor = key;
2267 while let Some(node) = self.nodes.get(ancestor) {
2268 if self.windows.iter().any(|w| w.root == ancestor) {
2271 if ancestor == focused {
2272 return Some(key);
2273 }
2274 break;
2275 }
2276 ancestor = if node.kind == kinds::LIST_CELL && node.parent.is_null() {
2277 self.lists
2280 .iter()
2281 .find_map(|(&owner, state)| {
2282 state
2283 .cells
2284 .values()
2285 .any(|c| c.anchor == ancestor)
2286 .then_some(owner)
2287 })
2288 .or_else(|| {
2289 self.trees.iter().find_map(|(&owner, state)| {
2290 state
2291 .cells
2292 .values()
2293 .any(|c| c.anchor == ancestor)
2294 .then_some(owner)
2295 })
2296 })
2297 .unwrap_or(node.parent)
2298 } else {
2299 node.parent
2300 };
2301 }
2302 }
2303 fallback
2304 }
2305
2306 fn toggle_sidebar(&mut self, host: RNode) -> bool {
2307 let Some(h) = self.nodes.get(host).and_then(|n| n.handle.as_ref()) else {
2308 return false;
2309 };
2310 self.toolkit.toggle_sidebar(h)
2311 }
2312
2313 fn native_back(&mut self) -> bool {
2314 self.toolkit.native_back()
2315 }
2316
2317 fn prepare_snapshot(
2318 &mut self,
2319 root: Option<RNode>,
2320 revision: u32,
2321 ) -> Result<day_spec::capture::Readiness, String> {
2322 let host = match root {
2323 Some(root) if self.windows[0].root != root => Some(
2324 self.nodes
2325 .get(root)
2326 .and_then(|n| n.handle.clone())
2327 .ok_or("window is gone")?,
2328 ),
2329 _ => None,
2330 };
2331 self.toolkit.prepare_snapshot(host.as_ref(), revision)
2332 }
2333
2334 fn snapshot(&mut self) -> Result<Vec<u8>, String> {
2335 self.toolkit.snapshot_window()
2336 }
2337
2338 fn snapshot_origin(&mut self) -> Option<Point> {
2339 let root = self.windows[0].root;
2340 let h = self.nodes.get(root).and_then(|n| n.handle.clone())?;
2341 self.toolkit.snapshot_origin(&h)
2342 }
2343
2344 fn snapshot_chrome(&mut self) -> Result<Vec<u8>, String> {
2345 self.toolkit.snapshot_window_chrome()
2346 }
2347
2348 fn decode_image(&mut self, req: u64, id: day_spec::BitmapId, bytes: &[u8]) {
2349 self.toolkit.decode_image(req, id, bytes);
2350 }
2351
2352 fn image_info(&mut self, id: day_spec::BitmapId) -> Option<day_spec::BitmapInfo> {
2353 self.toolkit.image_info(id)
2354 }
2355
2356 fn image_properties(&mut self, id: day_spec::BitmapId) -> Option<day_spec::ImageProperties> {
2357 self.toolkit.image_properties(id)
2358 }
2359
2360 fn encode_image(&mut self, req: u64, id: day_spec::BitmapId, spec: &day_spec::EncodeSpec) {
2361 self.toolkit.encode_image(req, id, spec);
2362 }
2363
2364 fn image_encode_formats(&mut self) -> Vec<day_spec::ImageFormat> {
2365 self.toolkit.encode_formats()
2366 }
2367
2368 fn release_image(&mut self, id: day_spec::BitmapId) {
2369 self.toolkit.release_image(id);
2370 }
2371
2372 fn render_bitmap(
2373 &mut self,
2374 req: u64,
2375 id: day_spec::BitmapId,
2376 ops: &[day_spec::DrawOp],
2377 size: Size,
2378 scale: f64,
2379 ) {
2380 self.toolkit.render_bitmap(req, id, ops, size, scale);
2381 }
2382
2383 fn render_pdf(&mut self, req: u64, pages: &[day_spec::PdfPage], info: &day_spec::PdfInfo) {
2384 self.toolkit.render_pdf(req, pages, info);
2385 }
2386
2387 fn window_image_support(&mut self) -> day_spec::Support {
2388 self.toolkit.capability(day_spec::Cap::Snapshot)
2389 }
2390
2391 fn font_families(&mut self) -> Vec<day_spec::FontFamilyInfo> {
2392 self.toolkit.font_families()
2393 }
2394
2395 fn measure_text(
2396 &mut self,
2397 text: &str,
2398 size: f64,
2399 font: &day_spec::CanvasFont,
2400 ) -> Option<day_spec::TextMetrics> {
2401 self.toolkit.measure_text(text, size, font)
2402 }
2403
2404 fn ui_idle(&mut self) -> bool {
2405 self.toolkit.ui_idle()
2406 }
2407
2408 fn root_node(&self) -> RNode {
2409 self.windows[0].root
2410 }
2411
2412 fn open_window_root(&mut self, options: &WindowOptions, kind: WindowKind) -> WindowRootReply {
2413 let root = self.nodes.insert(NodeData {
2416 kind: kinds::CONTAINER,
2417 handle: None,
2418 parent: RNode::null(),
2419 children: Vec::new(),
2420 layout: Rc::new(PassThrough),
2421 flex: Flex::default(),
2422 scope: Scope::root(),
2423 id: None,
2424 parked_id: None,
2425 a11y: Default::default(),
2426 cache: Vec::new(),
2427 baseline_cache: None,
2428 probe: NodeProbe::default(),
2429 needs_measure: true,
2430 needs_layout: true,
2431 last_native_frame: None,
2432 scroll_content: None,
2433 implicit_anim: None,
2434 tweaked: false,
2435 is_boundary: true,
2436 });
2437 match self.toolkit.open_window(rnode_to_id(root), options, kind) {
2438 day_spec::WindowOpenReply::Open(h) => {
2439 self.nodes[root].handle = Some(h);
2440 self.windows.push(WindowEntry {
2441 root,
2442 size: options.size,
2443 });
2444 self.layout_dirty = true;
2445 WindowRootReply::Open(root)
2446 }
2447 day_spec::WindowOpenReply::Pending => WindowRootReply::Pending(root),
2448 day_spec::WindowOpenReply::Unsupported => {
2449 self.nodes.remove(root);
2450 WindowRootReply::Unsupported
2451 }
2452 }
2453 }
2454
2455 fn adopt_window_root(&mut self, root: RNode, raw: day_spec::RawHandle, size: Size) -> bool {
2456 let Some(node) = self.nodes.get_mut(root) else {
2457 return false; };
2459 let handle = self.toolkit.adopt(raw);
2460 node.handle = Some(handle);
2461 node.needs_measure = true;
2462 self.windows.push(WindowEntry { root, size });
2463 self.layout_dirty = true;
2464 true
2465 }
2466
2467 fn remove_window_root(&mut self, root: RNode) {
2468 if self.windows.len() <= 1 {
2476 self.collect_and_release(root);
2477 self.layout_dirty = true;
2478 return;
2479 }
2480 self.windows.retain(|w| w.root != root);
2481 self.collect_and_release(root);
2484 self.layout_dirty = true;
2485 }
2486
2487 fn set_root_size(&mut self, root: RNode, s: Size) {
2488 if let Some(w) = self.windows.iter_mut().find(|w| w.root == root)
2489 && w.size != s
2490 {
2491 w.size = s;
2492 self.mark_needs_measure_impl(root);
2493 }
2494 }
2495
2496 fn fit_window(&mut self, root: RNode, size: Size) {
2497 if let Some(h) = self.nodes.get(root).and_then(|n| n.handle.clone()) {
2498 self.toolkit.fit_window(&h, size);
2499 }
2500 self.set_root_size(root, size);
2501 }
2502
2503 fn add_extra_layout_root(&mut self, node: RNode, size: Size) {
2504 if !self.windows.iter().any(|w| w.root == node) {
2505 self.windows.push(WindowEntry { root: node, size });
2506 self.layout_dirty = true;
2507 }
2508 }
2509
2510 fn drop_extra_layout_root(&mut self, node: RNode) {
2511 debug_assert!(
2512 self.windows.first().map(|w| w.root) != Some(node),
2513 "drop_extra_layout_root called with the primary root"
2514 );
2515 self.windows.retain(|w| w.root != node);
2516 }
2517
2518 fn close_native_window(&mut self, root: RNode) {
2519 if let Some(h) = self.nodes.get(root).and_then(|n| n.handle.clone()) {
2520 self.toolkit.close_window(&h);
2521 }
2522 }
2523
2524 fn quit_app(&mut self) {
2525 self.toolkit.quit_app();
2526 }
2527
2528 fn focus_native_window(&mut self, root: RNode) {
2529 if let Some(h) = self.nodes.get(root).and_then(|n| n.handle.clone()) {
2530 self.toolkit.focus_window(&h);
2531 }
2532 }
2533
2534 fn set_native_window_title(&mut self, root: RNode, title: &str) {
2535 if let Some(h) = self.nodes.get(root).and_then(|n| n.handle.clone()) {
2536 self.toolkit.set_window_title(&h, title);
2537 }
2538 }
2539
2540 fn apply_window(&mut self, root: RNode, change: &day_spec::WindowChange) {
2541 if let Some(h) = self.nodes.get(root).and_then(|n| n.handle.clone()) {
2542 self.toolkit.apply_window(&h, change);
2543 }
2544 }
2545
2546 fn set_dock_menu(&mut self, items: &[day_spec::MenuItem]) {
2547 self.toolkit.set_dock_menu(items);
2548 }
2549
2550 fn set_status_items(&mut self, items: &[day_spec::StatusItemSpec]) {
2551 self.toolkit.set_status_items(items);
2552 }
2553
2554 fn set_app_progress(&mut self, progress: day_spec::AppProgress) {
2555 self.toolkit.set_app_progress(progress);
2556 }
2557
2558 fn set_dock_visible(&mut self, visible: bool) {
2559 self.toolkit.set_dock_visible(visible);
2560 }
2561
2562 fn set_keep_running(&mut self, keep: bool) {
2563 self.toolkit.set_keep_running(keep);
2564 }
2565
2566 fn native_window_frame(&self, root: RNode) -> Option<day_spec::Rect> {
2567 let h = self.nodes.get(root).and_then(|n| n.handle.clone())?;
2568 self.toolkit.window_frame(&h)
2569 }
2570
2571 fn monitors(&self) -> Vec<day_spec::Monitor> {
2572 self.toolkit.monitors()
2573 }
2574
2575 fn set_launcher_shortcuts(&mut self, shortcuts: &[day_spec::LauncherShortcut]) {
2576 self.toolkit.set_launcher_shortcuts(shortcuts);
2577 }
2578
2579 fn snapshot_of(&mut self, root: RNode) -> Result<Vec<u8>, String> {
2580 if self.windows[0].root == root {
2581 return self.toolkit.snapshot_window();
2582 }
2583 match self.nodes.get(root).and_then(|n| n.handle.clone()) {
2584 Some(h) => self.toolkit.snapshot_window_of(&h),
2585 None => Err("window is gone".into()),
2586 }
2587 }
2588
2589 fn install_list(&mut self, node: RNode, driver: crate::list::ListDriver) {
2590 let driver = Rc::new(driver);
2591 let sections = driver
2592 .sections
2593 .is_some()
2594 .then(|| crate::list::register_sections(node, driver.clone()));
2595 self.lists.insert(
2596 node,
2597 crate::list::ListState {
2598 driver: driver.clone(),
2599 cells: HashMap::new(),
2600 sections: sections.clone(),
2601 },
2602 );
2603 let source = crate::list::make_source(node, driver, sections);
2604 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
2605 self.toolkit.attach_list(&handle, source);
2606 }
2607 }
2608
2609 fn list_prepare_cell(
2610 &mut self,
2611 node: RNode,
2612 key: usize,
2613 cell: RawHandle,
2614 header: bool,
2615 ) -> crate::list::CellStep {
2616 if let Some(state) = self.lists.get_mut(&node)
2620 && state.cells.get(&key).is_some_and(|b| b.header != header)
2621 && let Some(old) = state.cells.remove(&key)
2622 {
2623 old.scope.dispose();
2624 self.remove_subtree_impl(old.anchor);
2625 }
2626 if let Some(state) = self.lists.get_mut(&node)
2627 && let Some(bound) = state.cells.get_mut(&key)
2628 {
2629 bound.active = true;
2630 let (rebind, anchor, pinned) = (bound.rebind.clone(), bound.anchor, bound.pinned);
2631 if !pinned {
2635 self.restore_subtree_ids(anchor);
2636 }
2637 return crate::list::CellStep::Rebind { rebind, anchor };
2638 }
2639 let mut metrics = match self.nodes.get(node).and_then(|n| n.handle.clone()) {
2642 Some(list) => self.toolkit.list_row_metrics(&list),
2643 None => day_spec::RowMetrics::default(),
2644 };
2645 if header {
2649 metrics.insets.top = 0.0;
2650 metrics.insets.bottom = 0.0;
2651 metrics.min_height = 0.0;
2652 }
2653 let handle = self.toolkit.adopt(cell);
2654 let anchor = self.create_cell_anchor(
2655 handle,
2656 Scope::child(),
2657 Rc::new(crate::layout::CellMetrics(metrics)),
2658 );
2659 crate::list::CellStep::Build { anchor }
2660 }
2661
2662 fn list_store_cell(
2663 &mut self,
2664 node: RNode,
2665 key: usize,
2666 anchor: RNode,
2667 built: crate::list::BuiltRow,
2668 header: bool,
2669 ) {
2670 if let Some(state) = self.lists.get_mut(&node) {
2671 state.cells.insert(
2672 key,
2673 crate::list::BoundCell {
2674 anchor,
2675 active: true,
2676 scope: built.scope,
2677 rebind: built.rebind,
2678 native_width: None,
2679 laid_width: None,
2680 laid_height: None,
2681 header,
2682 pinned: false,
2683 },
2684 );
2685 }
2686 }
2687
2688 fn list_layout_cell(&mut self, node: RNode, key: usize) {
2689 let native = self
2695 .lists
2696 .get(&node)
2697 .and_then(|s| s.cells.get(&key))
2698 .and_then(|c| c.native_width);
2699 let width = native.unwrap_or_else(|| {
2700 self.nodes
2701 .get(node)
2702 .and_then(|n| n.last_native_frame)
2703 .map(|f| f.size.width)
2704 .unwrap_or(self.windows[0].size.width)
2705 });
2706 self.layout_list_cell_at(node, key, width);
2707 }
2708
2709 fn list_layout_cell_width(&mut self, node: RNode, key: usize, width: f64) {
2710 if let Some(cell) = self
2713 .lists
2714 .get_mut(&node)
2715 .and_then(|s| s.cells.get_mut(&key))
2716 {
2717 cell.native_width = Some(width);
2718 }
2719 self.layout_list_cell_at(node, key, width);
2720 }
2721
2722 fn list_cell_height(&self, node: RNode, key: usize) -> f64 {
2723 self.lists
2724 .get(&node)
2725 .and_then(|s| s.cells.get(&key))
2726 .filter(|b| b.active)
2727 .and_then(|b| b.laid_height)
2728 .unwrap_or(0.0)
2729 }
2730
2731 fn list_cell_keys(&self, node: RNode) -> Vec<usize> {
2732 self.lists
2733 .get(&node)
2734 .map(|s| {
2735 s.cells
2736 .iter()
2737 .filter_map(|(key, cell)| cell.active.then_some(*key))
2738 .collect()
2739 })
2740 .unwrap_or_default()
2741 }
2742
2743 fn list_reload(&mut self, node: RNode) {
2744 if let Some(map) = self.lists.get(&node).and_then(|s| s.sections.as_ref()) {
2745 map.invalidate();
2746 }
2747 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
2748 self.toolkit.update(
2749 &handle,
2750 kinds::LIST,
2751 &day_spec::props::ListPatch::Reload as &dyn Any,
2752 None,
2753 );
2754 }
2755 }
2756
2757 fn list_splice(&mut self, node: RNode, deltas: Vec<day_spec::props::RowDelta>) {
2758 if self.lists.get(&node).is_some_and(|s| s.sections.is_some()) {
2761 self.list_reload(node);
2762 return;
2763 }
2764 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
2765 self.toolkit.update(
2766 &handle,
2767 kinds::LIST,
2768 &day_spec::props::ListPatch::Splice(deltas) as &dyn Any,
2769 None,
2770 );
2771 }
2772 }
2773
2774 fn set_undo_state(&mut self, state: &day_spec::UndoState) {
2775 self.toolkit.set_undo_state(state);
2776 }
2777
2778 fn set_edit_state(&mut self, state: &day_spec::EditState) {
2779 self.toolkit.set_edit_state(state);
2780 }
2781
2782 fn modifiers(&mut self) -> day_spec::Modifiers {
2783 self.toolkit.modifiers()
2784 }
2785
2786 fn list_scroll_to_end(&mut self, node: RNode) {
2787 if self.lists.get(&node).map(|s| (s.driver.len)()).unwrap_or(0) == 0 {
2790 return;
2791 }
2792 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
2793 self.toolkit.update(
2794 &handle,
2795 kinds::LIST,
2796 &day_spec::props::ListPatch::ScrollToEnd as &dyn Any,
2797 None,
2798 );
2799 }
2800 }
2801
2802 fn list_scroll_to_row(&mut self, node: RNode, row: usize) {
2803 let len = self.lists.get(&node).map(|s| (s.driver.len)()).unwrap_or(0);
2804 if len == 0 {
2805 return;
2806 }
2807 let mut row = row.min(len - 1);
2808 if let Some(map) = self.lists.get(&node).and_then(|s| s.sections.as_ref()) {
2809 row = map.display_of(row).unwrap_or(row);
2810 }
2811 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
2812 self.toolkit.update(
2813 &handle,
2814 kinds::LIST,
2815 &day_spec::props::ListPatch::ScrollToRow(row) as &dyn Any,
2816 None,
2817 );
2818 }
2819 }
2820
2821 fn list_set_editing(&mut self, node: RNode, on: bool) {
2822 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
2823 self.toolkit.update(
2824 &handle,
2825 kinds::LIST,
2826 &day_spec::props::ListPatch::Editing(on) as &dyn Any,
2827 None,
2828 );
2829 }
2830 }
2831
2832 fn list_set_selected(&mut self, node: RNode, rows: Vec<usize>) {
2833 let rows = match self.lists.get(&node).and_then(|s| s.sections.as_ref()) {
2834 Some(map) => rows.iter().filter_map(|i| map.display_of(*i)).collect(),
2835 None => rows,
2836 };
2837 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
2838 self.toolkit.update(
2839 &handle,
2840 kinds::LIST,
2841 &day_spec::props::ListPatch::Selected(rows) as &dyn Any,
2842 None,
2843 );
2844 }
2845 }
2846
2847 fn list_driver(&mut self, node: RNode) -> Option<std::rc::Rc<crate::list::ListDriver>> {
2848 self.lists.get(&node).map(|s| s.driver.clone())
2849 }
2850
2851 fn install_tree(&mut self, node: RNode, driver: crate::tree_driver::TreeDriver) {
2852 let driver = Rc::new(driver);
2853 self.trees.insert(
2854 node,
2855 crate::tree_driver::TreeState {
2856 driver: driver.clone(),
2857 cells: HashMap::new(),
2858 },
2859 );
2860 let source = crate::tree_driver::make_tree_source(node, driver);
2861 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
2862 self.toolkit.attach_tree(&handle, source);
2863 }
2864 }
2865
2866 fn tree_prepare_cell(
2867 &mut self,
2868 node: RNode,
2869 key: usize,
2870 cell: RawHandle,
2871 ) -> crate::tree_driver::TreeCellStep {
2872 if let Some(state) = self.trees.get(&node)
2873 && let Some(bound) = state.cells.get(&key)
2874 {
2875 let (rebind, anchor) = (bound.rebind.clone(), bound.anchor);
2876 self.restore_subtree_ids(anchor);
2878 return crate::tree_driver::TreeCellStep::Rebind { rebind, anchor };
2879 }
2880 let handle = self.toolkit.adopt(cell);
2884 let anchor =
2885 self.create_cell_anchor(handle, Scope::child(), Rc::new(crate::layout::CellCenter));
2886 crate::tree_driver::TreeCellStep::Build { anchor }
2887 }
2888
2889 fn tree_store_cell(
2890 &mut self,
2891 node: RNode,
2892 key: usize,
2893 anchor: RNode,
2894 built: crate::tree_driver::TreeBuiltRow,
2895 ) {
2896 if let Some(state) = self.trees.get_mut(&node) {
2897 state.cells.insert(
2898 key,
2899 crate::tree_driver::TreeBoundCell {
2900 anchor,
2901 scope: built.scope,
2902 rebind: built.rebind,
2903 native_width: None,
2904 native_height: None,
2905 laid_width: None,
2906 laid_height: None,
2907 },
2908 );
2909 }
2910 }
2911
2912 fn tree_layout_cell(&mut self, node: RNode, key: usize) {
2913 let native = self
2917 .trees
2918 .get(&node)
2919 .and_then(|s| s.cells.get(&key))
2920 .and_then(|c| c.native_width);
2921 let width = native.unwrap_or_else(|| {
2922 self.nodes
2923 .get(node)
2924 .and_then(|n| n.last_native_frame)
2925 .map(|f| f.size.width)
2926 .unwrap_or(self.windows[0].size.width)
2927 });
2928 self.layout_tree_cell_at(node, key, width);
2929 }
2930
2931 fn tree_layout_cell_width(&mut self, node: RNode, key: usize, width: f64) {
2932 if let Some(cell) = self
2935 .trees
2936 .get_mut(&node)
2937 .and_then(|s| s.cells.get_mut(&key))
2938 {
2939 cell.native_width = Some(width);
2940 }
2941 self.layout_tree_cell_at(node, key, width);
2942 }
2943
2944 fn tree_cell_height(&self, node: RNode, key: usize) -> f64 {
2945 self.trees
2946 .get(&node)
2947 .and_then(|s| s.cells.get(&key))
2948 .and_then(|b| b.laid_height)
2949 .unwrap_or(0.0)
2950 }
2951
2952 fn tree_layout_cell_size(&mut self, node: RNode, key: usize, size: Size) {
2953 let changed = self
2954 .trees
2955 .get_mut(&node)
2956 .and_then(|s| s.cells.get_mut(&key))
2957 .and_then(|cell| {
2958 let changed = cell.native_height != Some(size.height);
2959 cell.native_height = Some(size.height);
2960 changed.then_some(cell.anchor)
2961 });
2962 if let Some(anchor) = changed {
2963 self.invalidate_subtree(anchor);
2964 }
2965 self.tree_layout_cell_width(node, key, size.width);
2966 }
2967
2968 fn tree_recycle_cell(&mut self, node: RNode, key: usize) {
2969 let Some(anchor) = self
2970 .trees
2971 .get(&node)
2972 .and_then(|s| s.cells.get(&key))
2973 .map(|b| b.anchor)
2974 else {
2975 return;
2976 };
2977 self.clear_subtree_ids(anchor);
2978 }
2979
2980 fn list_recycle_cell(&mut self, node: RNode, key: usize) {
2981 let Some(anchor) = self
2982 .lists
2983 .get_mut(&node)
2984 .and_then(|s| s.cells.get_mut(&key))
2985 .map(|b| {
2986 b.active = false;
2987 b.laid_height = None;
2992 b.anchor
2993 })
2994 else {
2995 return;
2996 };
2997 self.clear_subtree_ids(anchor);
2998 }
2999
3000 fn list_release_cell(&mut self, node: RNode, key: usize) {
3001 let Some(cell) = self.lists.get_mut(&node).and_then(|s| s.cells.remove(&key)) else {
3003 return;
3004 };
3005 cell.scope.dispose();
3010 self.remove_subtree_impl(cell.anchor);
3011 }
3012
3013 fn list_pin_cell(&mut self, node: RNode, key: usize) {
3014 let Some(anchor) = self
3015 .lists
3016 .get_mut(&node)
3017 .and_then(|s| s.cells.get_mut(&key))
3018 .map(|b| {
3019 b.pinned = true;
3020 b.anchor
3021 })
3022 else {
3023 return;
3024 };
3025 self.clear_subtree_ids(anchor);
3026 }
3027
3028 fn tree_reload(&mut self, node: RNode) {
3029 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
3030 self.toolkit.update(
3031 &handle,
3032 kinds::TREE,
3033 &day_spec::props::TreePatch::Reload as &dyn Any,
3034 None,
3035 );
3036 }
3037 }
3038
3039 fn tree_set_expanded(&mut self, node: RNode, token: u64, expanded: bool) {
3040 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
3041 self.toolkit.update(
3042 &handle,
3043 kinds::TREE,
3044 &day_spec::props::TreePatch::Expand(token, expanded) as &dyn Any,
3045 None,
3046 );
3047 }
3048 }
3049
3050 fn tree_set_selected(&mut self, node: RNode, tokens: Vec<u64>) {
3051 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
3052 self.toolkit.update(
3053 &handle,
3054 kinds::TREE,
3055 &day_spec::props::TreePatch::Selected(tokens) as &dyn Any,
3056 None,
3057 );
3058 }
3059 }
3060
3061 fn tree_reveal(&mut self, node: RNode, token: u64) {
3062 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
3063 self.toolkit.update(
3064 &handle,
3065 kinds::TREE,
3066 &day_spec::props::TreePatch::Reveal(token) as &dyn Any,
3067 None,
3068 );
3069 }
3070 }
3071
3072 fn tree_driver(&mut self, node: RNode) -> Option<std::rc::Rc<crate::tree_driver::TreeDriver>> {
3073 self.trees.get(&node).map(|s| s.driver.clone())
3074 }
3075
3076 fn set_drag_source(&mut self, node: RNode, source: day_spec::transfer::Source) {
3077 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
3078 self.toolkit.set_drag_source(&handle, source);
3079 }
3080 }
3081 fn set_drop_target(&mut self, node: RNode, target: day_spec::transfer::Target) {
3082 if let Some(handle) = self.nodes.get(node).and_then(|n| n.handle.clone()) {
3083 self.toolkit.set_drop_target(&handle, target);
3084 }
3085 }
3086 fn set_context_menu_fn(&mut self, node: RNode, f: day_spec::ContextMenuFn) {
3087 let target = if self.nodes.get(node).is_some_and(|n| n.handle.is_some()) {
3090 Some(node)
3091 } else {
3092 let mut roots = Vec::new();
3093 self.native_descendants(node, &mut roots);
3094 roots.first().copied()
3095 };
3096 let Some(target) = target else {
3097 log::warn!("context_menu_fn on a subtree with no native view — menu dropped");
3098 return;
3099 };
3100 let model = ContextMenuModel::Provider(f.clone());
3101 self.context_menus.insert(node, model.clone());
3102 self.context_menus.insert(target, model);
3103 if let Some(handle) = self.nodes.get(target).and_then(|n| n.handle.clone()) {
3104 self.toolkit
3105 .set_context_menu_fn(&handle, rnode_to_id(target), f);
3106 }
3107 }
3108
3109 fn context_menu_of(&mut self, node: RNode) -> Option<day_spec::ContextMenuFn> {
3110 let mut cur = node;
3113 loop {
3114 if let Some(model) = self.context_menus.get(&cur) {
3115 return Some(match model {
3116 ContextMenuModel::Items(items) => {
3117 let items = items.clone();
3118 Rc::new(move |_| items.clone())
3119 }
3120 ContextMenuModel::Provider(f) => f.clone(),
3121 });
3122 }
3123 let parent = self.nodes.get(cur)?.parent;
3126 if parent == cur {
3127 return None;
3128 }
3129 cur = parent;
3130 }
3131 }
3132}
3133
3134day_reactive::tls_slots! {
3139 tree;
3140 static TREE: std::mem::ManuallyDrop<RefCell<Option<Box<dyn TreeOps>>>> =
3149 const { std::mem::ManuallyDrop::new(RefCell::new(None)) };
3150 static EVENTS: RefCell<VecDeque<(NodeId, Event)>> = const { RefCell::new(VecDeque::new()) };
3151 static PUMP_PENDING: Cell<bool> = const { Cell::new(false) };
3152 static PUMP_ACTIVE: Cell<bool> = const { Cell::new(false) };
3153 static DEFERRED: RefCell<Vec<Box<dyn FnOnce() + Send>>> = const { RefCell::new(Vec::new()) };
3156 static EVENT_OBSERVER: RefCell<Option<Rc<EventObserver>>> = const { RefCell::new(None) };
3160 static PUMP_GEN: std::cell::Cell<u64> = const { std::cell::Cell::new(0) };
3165
3166 static FOCUSED: std::cell::Cell<Option<RNode>> = const { std::cell::Cell::new(None) };
3168}
3169
3170pub fn install_tree(tree: Box<dyn TreeOps>) {
3171 crate::nav::clear_controllers();
3173 TREE.with(|t| *t.borrow_mut() = Some(tree));
3174 let _ = crate::image::canvas_exports();
3177}
3178
3179pub fn prepare_remount() {
3197 day_reactive::recover_from_panic();
3203 uninstall_tree();
3204 day_reactive::recover_from_panic();
3206 crate::lifecycle::reset_handlers();
3207 crate::menu::reset_menus();
3208}
3209
3210pub fn is_mounted() -> bool {
3216 TREE.with(|t| t.borrow().is_some())
3217}
3218
3219pub fn uninstall_tree() {
3221 crate::image::reset();
3223 crate::nav::clear_controllers();
3224 crate::windows::reset_windows();
3225 crate::status::reset_status();
3226 crate::reset_ambient();
3229 TREE.with(|t| *t.borrow_mut() = None);
3230 EVENTS.with(|e| e.borrow_mut().clear());
3231 PUMP_PENDING.with(|c| c.set(false));
3232 EVENT_OBSERVER.with(|o| *o.borrow_mut() = None);
3235}
3236
3237pub fn with_tree<R>(f: impl FnOnce(&mut dyn TreeOps) -> R) -> R {
3241 let r = TREE.with(|t| {
3242 let mut opt = t.borrow_mut();
3243 let ops = opt.as_mut().expect("day: no tree installed on this thread");
3244 f(ops.as_mut())
3245 });
3246 if PUMP_PENDING.with(|c| c.replace(false)) {
3247 pump_events();
3248 }
3249 run_deferred();
3250 r
3251}
3252
3253pub(crate) fn run_posted_or_defer(f: Box<dyn FnOnce() + Send>) {
3260 let borrowed = TREE.with(|t| t.try_borrow_mut().is_err());
3261 if borrowed {
3262 DEFERRED.with(|d| d.borrow_mut().push(f));
3263 } else {
3264 crate::contain_posted_panic(f);
3265 }
3266}
3267
3268fn run_deferred() {
3272 loop {
3273 let batch = DEFERRED.with(|d| std::mem::take(&mut *d.borrow_mut()));
3274 if batch.is_empty() {
3275 return;
3276 }
3277 for f in batch {
3278 crate::contain_posted_panic(f);
3279 }
3280 }
3281}
3282
3283pub fn capability(cap: day_spec::Cap) -> day_spec::Support {
3287 with_tree(|t| t.capability(cap))
3288}
3289
3290pub fn open_url(url: &str) {
3295 if crate::conformance::intercept_open_url(url) {
3298 return;
3299 }
3300 with_tree(|t| t.open_url(url));
3301}
3302
3303pub fn announce(text: &str) {
3309 crate::conformance::note_announcement(text);
3310 with_tree(|t| t.announce(text, false));
3311}
3312
3313pub fn announce_urgent(text: &str) {
3316 crate::conformance::note_announcement(text);
3317 with_tree(|t| t.announce(text, true));
3318}
3319
3320pub fn share_support() -> Support {
3322 with_tree(|t| t.share_support())
3323}
3324pub fn share_url(url: &str, title: &str) -> bool {
3328 with_tree(|t| t.share_url(url, title))
3329}
3330
3331pub fn application_handlers(
3334 query: &day_spec::applications::HandlerQuery,
3335) -> Result<day_spec::applications::ApplicationHandlers, day_spec::applications::ApplicationError> {
3336 with_tree(|t| t.application_handlers(query))
3337}
3338
3339pub fn default_browser()
3341-> Result<Option<day_spec::applications::Application>, day_spec::applications::ApplicationError> {
3342 application_handlers(&day_spec::applications::HandlerQuery::Scheme(
3343 "https".into(),
3344 ))
3345 .map(|h| h.default)
3346}
3347
3348pub async fn open_url_with_application(
3351 url: &str,
3352 application: &str,
3353) -> Result<(), day_spec::applications::ApplicationError> {
3354 let (send, receive) = day_async::oneshot();
3355 with_tree(|t| {
3356 t.open_url_with_application(url, application, Box::new(move |result| send.send(result)))
3357 });
3358 receive
3359 .await
3360 .unwrap_or(Err(day_spec::applications::ApplicationError::LaunchFailed))
3361}
3362
3363pub fn invalidate_size(node: RNode) {
3369 with_tree(|t| {
3370 t.mark_needs_measure(node);
3371 t.mark_layout_dirty();
3372 });
3373}
3374
3375pub fn try_with_tree<R>(f: impl FnOnce(&mut dyn TreeOps) -> R) -> Option<R> {
3385 let r = TREE.with(|t| {
3386 let mut opt = t.try_borrow_mut().ok()?;
3387 let ops = opt.as_mut()?;
3388 Some(f(ops.as_mut()))
3389 });
3390 if r.is_some() {
3391 if PUMP_PENDING.with(|c| c.replace(false)) {
3392 pump_events();
3393 }
3394 run_deferred();
3395 }
3396 r
3397}
3398
3399pub(crate) fn with_tree_if_free<R>(f: impl FnOnce(&mut dyn TreeOps) -> R) -> Option<R> {
3402 TREE.with(|t| {
3403 let mut opt = t.try_borrow_mut().ok()?;
3404 let ops = opt.as_mut()?;
3405 Some(f(ops.as_mut()))
3406 })
3407}
3408
3409pub(crate) fn tree_absent() -> bool {
3412 TREE.try_with(|t| t.try_borrow().map(|o| o.is_none()).unwrap_or(false))
3414 .unwrap_or(true)
3415}
3416
3417pub(crate) fn request_pump() {
3420 PUMP_PENDING.with(|c| c.set(true));
3421}
3422
3423pub fn has_tree() -> bool {
3424 !tree_absent()
3427}
3428
3429pub fn enqueue_event(id: NodeId, ev: Event) {
3432 enqueue_events([(id, ev)]);
3433}
3434
3435pub(crate) fn enqueue_native_event(id: NodeId, ev: Event) {
3440 if let Some(ev) = crate::list::translate_native_event(id, ev) {
3441 enqueue_event(id, ev);
3442 }
3443}
3444
3445pub fn enqueue_events(evs: impl IntoIterator<Item = (NodeId, Event)>) {
3450 let observer = EVENT_OBSERVER.with(|o| o.borrow().clone());
3459 match observer {
3460 Some(obs) => {
3461 let batch: Vec<(NodeId, Event)> = evs.into_iter().collect();
3462 for (id, ev) in &batch {
3463 obs(*id, ev);
3464 }
3465 EVENTS.with(|e| e.borrow_mut().extend(batch));
3466 }
3467 None => EVENTS.with(|e| e.borrow_mut().extend(evs)),
3468 }
3469 let tree_free = TREE.with(|t| t.try_borrow_mut().is_ok());
3470 if tree_free {
3471 pump_events();
3472 } else {
3473 PUMP_PENDING.with(|c| c.set(true));
3474 }
3475}
3476
3477pub fn pump_generation() -> u64 {
3479 PUMP_GEN.with(|g| g.get())
3480}
3481
3482pub fn set_event_observer(observer: Option<Box<EventObserver>>) {
3491 EVENT_OBSERVER.with(|o| *o.borrow_mut() = observer.map(Rc::from));
3492}
3493
3494pub fn id_of(node: NodeId) -> Option<String> {
3501 try_with_tree(|t| t.node_id(id_to_rnode(node))).flatten()
3502}
3503
3504pub fn label_of(node: NodeId) -> Option<String> {
3508 try_with_tree(|t| {
3509 let rnode = id_to_rnode(node);
3510 let a11y = t
3511 .node_a11y(rnode)
3512 .and_then(|a| a.label)
3513 .filter(|s| !s.is_empty());
3514 a11y.or_else(|| {
3515 t.node_probe(rnode)
3516 .map(|p| p.text)
3517 .filter(|s| !s.is_empty())
3518 })
3519 })
3520 .flatten()
3521}
3522
3523pub fn pump_events() {
3531 if PUMP_ACTIVE.with(|active| active.replace(true)) {
3535 return;
3536 }
3537 struct PumpGuard;
3538 impl Drop for PumpGuard {
3539 fn drop(&mut self) {
3540 PUMP_ACTIVE.with(|active| active.set(false));
3541 }
3542 }
3543 let _guard = PumpGuard;
3544 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
3545 loop {
3546 pump_events_inner();
3547 if EVENTS.with(|events| events.borrow().is_empty()) {
3550 break;
3551 }
3552 }
3553 }));
3554 if let Err(payload) = result {
3555 let msg = payload
3556 .downcast_ref::<&str>()
3557 .map(|s| (*s).to_string())
3558 .or_else(|| payload.downcast_ref::<String>().cloned())
3559 .unwrap_or_else(|| "unknown panic".to_string());
3560 log::warn!(
3561 "a native event handler panicked and was contained — the app continues, but \
3562 reactive/UI state may be inconsistent until the next interaction. Cause: {msg}"
3563 );
3564 EVENTS.with(|e| e.borrow_mut().clear());
3566 PUMP_PENDING.with(|c| c.set(false));
3567 day_reactive::recover_from_panic();
3568 crate::notify_contained_panic();
3569 }
3570}
3571
3572fn pump_events_inner() {
3573 crate::image::flush_deferred_releases();
3575 PUMP_GEN.with(|g| g.set(g.get() + 1));
3580 crate::nav::maybe_notify_route_change();
3581 loop {
3582 let item = EVENTS.with(|e| e.borrow_mut().pop_front());
3583 let Some((id, ev)) = item else { break };
3584 if let Event::PresentResult { req, result } = ev {
3586 crate::present::resolve_presentation(req, result);
3587 continue;
3588 }
3589 if let Event::ImageDecoded { req, result } = ev {
3592 crate::image::resolve_image_decode(req, result);
3593 continue;
3594 }
3595 if let Event::ImageEncoded { req, result } = ev {
3596 crate::image::resolve_image_encode(req, result);
3597 continue;
3598 }
3599 if let Event::BitmapRendered { req, result } = ev {
3601 crate::image::resolve_bitmap_render(req, result);
3602 continue;
3603 }
3604 if let Event::PdfRendered { req, result } = ev {
3605 crate::image::resolve_pdf_render(req, result);
3606 continue;
3607 }
3608 if let Event::MenuAction(action) = ev {
3610 crate::menu::dispatch_menu_action(action);
3611 continue;
3612 }
3613 if let Event::ToolbarChanged { action, value } = &ev {
3616 crate::toolbar::dispatch_toolbar_value(*action, value);
3617 continue;
3618 }
3619 if let Event::Lifecycle(phase) = ev {
3621 crate::lifecycle::dispatch_lifecycle(phase);
3622 continue;
3623 }
3624 if ev == Event::FocusChanged(false) {
3629 let paired = EVENTS.with(|e| {
3630 let mut q = e.borrow_mut();
3631 let gain = q
3632 .iter()
3633 .position(|(gid, gev)| *gid != id && *gev == Event::FocusChanged(true));
3634 gain.map(|i| q.remove(i).expect("indexed event"))
3635 });
3636 if let Some((gid, gev)) = paired {
3637 dispatch_focus_probe(gid, &gev);
3638 dispatch_to_node(gid, &gev);
3639 }
3640 }
3641 if let Event::FocusChanged(_) = ev {
3642 dispatch_focus_probe(id, &ev);
3643 }
3644 dispatch_to_node(id, &ev);
3645 }
3646 day_reactive::flush_sync();
3647 crate::nav::maybe_notify_route_change();
3654}
3655
3656pub fn focused_node() -> Option<RNode> {
3660 FOCUSED.with(|f| f.get())
3661}
3662
3663fn dispatch_focus_probe(id: NodeId, ev: &Event) {
3665 if let Event::FocusChanged(f) = ev {
3666 let node = id_to_rnode(id);
3667 with_tree(|t| t.set_probe_focused(node, *f));
3668 }
3669}
3670
3671pub fn scroll_to(node: RNode, target: ScrollTarget) {
3676 with_tree(|t| {
3677 t.scroll_to_target(node, &target, true);
3678 });
3679}
3680
3681fn dispatch_to_node(id: NodeId, ev: &Event) {
3683 let node = if id == day_spec::WINDOW_NODE {
3684 with_tree(|t| t.root_node())
3685 } else {
3686 id_to_rnode(id)
3687 };
3688 let gesture = match ev {
3694 Event::Tap(_) => Some(day_spec::GestureKind::Tap),
3695 Event::LongPress(_) => Some(day_spec::GestureKind::LongPress),
3696 Event::Drag { .. } => Some(day_spec::GestureKind::Drag),
3697 Event::Pan { .. } => Some(day_spec::GestureKind::Pan),
3698 Event::Pinch { .. } => Some(day_spec::GestureKind::Pinch),
3699 Event::Hover { .. } => Some(day_spec::GestureKind::Hover),
3700 _ => None,
3701 };
3702 let node = match gesture.and_then(|k| with_tree(|t| t.gesture_target(node, k))) {
3703 Some(target) => target,
3704 None => node,
3705 };
3706 let mut handlers = with_tree(|t| t.handlers_for(node));
3707 if matches!(ev, Event::Key(_) | Event::FocusChanged(_)) {
3712 with_tree(|t| {
3713 for w in t.wrappers_above(node) {
3714 handlers.extend(t.handlers_for(w));
3715 }
3716 });
3717 }
3718 if handlers.is_empty() {
3719 if cfg!(debug_assertions)
3723 && matches!(ev, Event::Pressed | Event::Tap(_))
3724 && !with_tree(|t| t.node_exists(node))
3725 {
3726 log::warn!("{ev:?} dropped — node {id:?} no longer exists (stale reference)");
3727 }
3728 return;
3729 }
3730 day_reactive::batch(|| {
3731 for h in &handlers {
3732 h(ev);
3733 }
3734 });
3735}
3736
3737fn collect_menu_families(
3738 items: &[day_spec::MenuItem],
3739 families: &mut std::collections::BTreeSet<String>,
3740) {
3741 for item in items {
3742 match item {
3743 day_spec::MenuItem::Action { id: Some(id), .. } => {
3744 families.insert(crate::element_id::family_of(id).to_owned());
3745 }
3746 day_spec::MenuItem::Submenu { items, .. } => collect_menu_families(items, families),
3747 _ => {}
3748 }
3749 }
3750}
3751
3752#[cfg(test)]
3753mod thread_exit_tests {
3754 use super::*;
3755 use std::sync::Arc;
3756 use std::sync::atomic::{AtomicBool, Ordering};
3757
3758 struct Probe(Arc<AtomicBool>);
3760
3761 impl Drop for Probe {
3762 fn drop(&mut self) {
3763 self.0.store(true, Ordering::SeqCst);
3764 }
3765 }
3766
3767 impl Toolkit for Probe {
3768 type Handle = ();
3769 fn realize(&mut self, _: PieceKind, _: &dyn Any, _: NodeId) -> Self::Handle {}
3770 fn release(&mut self, _: Self::Handle) {}
3771 fn insert(&mut self, _: &Self::Handle, _: &Self::Handle, _: usize) {}
3772 fn remove(&mut self, _: &Self::Handle, _: &Self::Handle) {
3773 panic!("test probe has no attached children to remove");
3774 }
3775 fn move_child(&mut self, _: &Self::Handle, _: &Self::Handle, _: usize) {}
3776 fn measure(&mut self, _: &Self::Handle, _: PieceKind, _: Proposal) -> Size {
3777 Size::new(0.0, 0.0)
3778 }
3779 fn set_frame(&mut self, _: &Self::Handle, _: Rect, _: Option<&AnimSpec>) {}
3780 fn set_input_traits(&mut self, _: &Self::Handle, _: &InputTraits) -> Option<Self::Handle> {
3781 None
3782 }
3783 fn set_event_sink(&mut self, _: EventSink) {}
3784 fn update(&mut self, _: &Self::Handle, _: &'static str, _: &dyn Any, _: Option<&AnimSpec>) {
3785 }
3786 }
3787
3788 fn install_probe(dropped: &Arc<AtomicBool>) {
3789 let tree = Tree::new(Probe(dropped.clone()), (), Size::new(0.0, 0.0));
3790 install_tree(Box::new(tree));
3791 }
3792
3793 #[test]
3798 fn the_tree_is_not_dropped_when_its_thread_ends() {
3799 let dropped = Arc::new(AtomicBool::new(false));
3800 let d = dropped.clone();
3801 std::thread::spawn(move || install_probe(&d))
3802 .join()
3803 .expect("thread");
3804 assert!(
3805 !dropped.load(Ordering::SeqCst),
3806 "the thread-local destructor dropped the tree, and with it the native widgets"
3807 );
3808 }
3809
3810 #[test]
3813 fn uninstall_still_drops_the_tree() {
3814 let dropped = Arc::new(AtomicBool::new(false));
3815 let d = dropped.clone();
3816 std::thread::spawn(move || {
3817 install_probe(&d);
3818 uninstall_tree();
3819 assert!(
3820 d.load(Ordering::SeqCst),
3821 "uninstall_tree must drop the tree"
3822 );
3823 })
3824 .join()
3825 .expect("thread");
3826 }
3827
3828 #[test]
3829 fn removing_an_unattached_node_releases_without_native_detach() {
3830 let dropped = Arc::new(AtomicBool::new(false));
3831 let mut tree = Tree::new(Probe(dropped), (), Size::new(0.0, 0.0));
3832 let scope = Scope::detached();
3833 let node = tree.create_node(
3834 "fixture.detached",
3835 &(),
3836 Rc::new(crate::LeafLayout),
3837 Flex::default(),
3838 true,
3839 false,
3840 scope,
3841 );
3842 tree.remove_subtree(node);
3843 assert!(!tree.node_exists(node));
3844 assert_eq!(
3845 tree.release_queue.len(),
3846 1,
3847 "the unattached native handle must still be released"
3848 );
3849 scope.dispose();
3850 }
3851
3852 #[test]
3857 fn a_post_during_a_tree_borrow_runs_at_the_safe_point() {
3858 std::thread::spawn(|| {
3859 install_probe(&Arc::new(AtomicBool::new(false)));
3860 let ran = Arc::new(AtomicBool::new(false));
3861 let r = ran.clone();
3862 with_tree(|_| {
3863 run_posted_or_defer(Box::new(move || {
3864 with_tree(|_| r.store(true, Ordering::SeqCst));
3867 }));
3868 assert!(!ran.load(Ordering::SeqCst), "ran inside the borrow");
3869 });
3870 assert!(
3871 ran.load(Ordering::SeqCst),
3872 "never ran after the borrow was released"
3873 );
3874 let now = Arc::new(AtomicBool::new(false));
3876 let n = now.clone();
3877 run_posted_or_defer(Box::new(move || n.store(true, Ordering::SeqCst)));
3878 assert!(
3879 now.load(Ordering::SeqCst),
3880 "a post with the tree free must run at once"
3881 );
3882 uninstall_tree();
3883 })
3884 .join()
3885 .expect("thread");
3886 }
3887}