fret_ui/tree/
ui_tree_focus.rs1use super::*;
2
3impl<H: UiHost> UiTree<H> {
4 pub fn set_window(&mut self, window: AppWindowId) {
5 self.window = Some(window);
6 }
7
8 pub fn focus(&self) -> Option<NodeId> {
9 self.focus
10 }
11
12 pub fn request_focus_element(&mut self, app: &mut H, target: GlobalElementId) {
19 if let Some(node) = self.resolve_live_attached_node_for_element(app, self.window, target) {
20 let before = self.focus;
21 self.set_focus(Some(node));
22 if self.focus == Some(node) {
23 self.pending_focus_target = None;
24 } else if before != Some(node) {
25 self.pending_focus_target = Some(target);
26 }
27 } else {
28 self.pending_focus_target = Some(target);
29 }
30 }
31
32 pub(in crate::tree) fn resolve_pending_focus_target_if_needed(&mut self, app: &mut H) -> bool {
33 let Some(target) = self.pending_focus_target else {
34 return false;
35 };
36
37 if let Some(node) = self.resolve_live_attached_node_for_element(app, self.window, target) {
38 let before = self.focus;
39 self.set_focus(Some(node));
40 if self.focus == Some(node) {
41 self.pending_focus_target = None;
42 return self.focus != before;
43 }
44 return false;
45 }
46
47 if let Some(window) = self.window
48 && !crate::elements::element_identity_is_live_in_current_frame(app, window, target)
49 {
50 self.pending_focus_target = None;
51 }
52
53 false
54 }
55
56 #[track_caller]
57 pub fn set_focus(&mut self, focus: Option<NodeId>) {
58 #[cfg(debug_assertions)]
59 let debug_focus_scope = std::env::var_os("FRET_TEST_DEBUG_FOCUS_SCOPE").is_some();
60 #[cfg(debug_assertions)]
61 if debug_focus_scope && self.focus != focus {
62 let loc = std::panic::Location::caller();
63 eprintln!(
64 "debug: set_focus at {}:{}:{}: {:?} -> {:?}",
65 loc.file(),
66 loc.line(),
67 loc.column(),
68 self.focus,
69 focus
70 );
71 }
72
73 if let Some(focus) = focus {
74 let (active_roots, barrier_root) = self.active_focus_layers();
75 if barrier_root.is_some()
76 && !self.is_reachable_from_any_root_via_children(focus, active_roots.as_slice())
77 {
78 return;
79 }
80 }
81 if self.focus != focus {
82 self.ime_composing = false;
83 }
84 let changed = self.focus != focus;
85 self.focus = focus;
86 if focus.is_some() {
87 self.pending_focus_target = None;
88 }
89 if changed {
90 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
91 }
92 }
93
94 #[track_caller]
99 pub(in crate::tree) fn set_focus_unchecked(
100 &mut self,
101 focus: Option<NodeId>,
102 reason: &'static str,
103 ) {
104 #[cfg(debug_assertions)]
105 {
106 let debug_focus_scope = std::env::var_os("FRET_TEST_DEBUG_FOCUS_SCOPE").is_some();
107 if debug_focus_scope && self.focus != focus {
108 let loc = std::panic::Location::caller();
109 eprintln!(
110 "debug: set_focus_unchecked({reason}) at {}:{}:{}: {:?} -> {:?}",
111 loc.file(),
112 loc.line(),
113 loc.column(),
114 self.focus,
115 focus
116 );
117 }
118 }
119
120 if self.focus != focus {
121 self.ime_composing = false;
122 }
123 let changed = self.focus != focus;
124 self.focus = focus;
125 if focus.is_some() {
126 self.pending_focus_target = None;
127 }
128 if changed {
129 self.request_post_layout_window_runtime_snapshot_refine_if_layout_active();
130 }
131 }
132
133 const TOUCH_POINTER_DOWN_OUTSIDE_SLOP_PX: f32 = 6.0;
134
135 pub(in crate::tree) fn update_touch_pointer_down_outside_move(
136 &mut self,
137 pointer_id: PointerId,
138 position: Point,
139 ) {
140 let Some(candidate) = self
141 .touch_pointer_down_outside_candidates
142 .get_mut(&pointer_id)
143 else {
144 return;
145 };
146 if candidate.moved {
147 return;
148 }
149 let dx = position.x.0 - candidate.start_pos.x.0;
150 let dy = position.y.0 - candidate.start_pos.y.0;
151 if (dx * dx + dy * dy).sqrt() > Self::TOUCH_POINTER_DOWN_OUTSIDE_SLOP_PX {
152 candidate.moved = true;
153 }
154 }
155}