Skip to main content

lgui_core/core/view/tree/
events.rs

1use super::*;
2
3impl HostTree {
4    pub fn action_handler(&self, target: &UiId, id: &ActionId) -> Option<UiActionHandler> {
5        self.node(target)?
6            .action_handlers
7            .iter()
8            .find(|binding| &binding.id == id)
9            .map(|binding| binding.handler.clone())
10    }
11
12    pub fn hit_test(&self, point: Point) -> Option<HitResult> {
13        let node = self.frontmost_node_at(point, |node| {
14            node.interaction != InteractionRole::None
15                || node.click_handler.is_some()
16                || node.click_capture_handler.is_some()
17                || node.input_event_handlers.iter().any(|binding| {
18                    matches!(
19                        binding.kind,
20                        UiEventKind::Click
21                            | UiEventKind::PointerDown
22                            | UiEventKind::PointerMove
23                            | UiEventKind::PointerUp
24                    )
25                })
26        })?;
27        Some(self.hit_for_node(node, node.click_action.clone()))
28    }
29
30    pub fn wheel_hit_test(&self, point: Point) -> Option<HitResult> {
31        let node = self.frontmost_node_at(point, |node| {
32            node.wheel_action.is_some()
33                || node
34                    .input_event_handlers
35                    .iter()
36                    .any(|binding| binding.kind == UiEventKind::Wheel)
37        })?;
38        let mut hit = self.hit_for_node(node, node.wheel_action.clone());
39        hit.click_handler = None;
40        hit.capture_handlers.clear();
41        hit.bubble_handlers.clear();
42        Some(hit)
43    }
44
45    pub fn focusable_hits(&self) -> Vec<HitResult> {
46        self.nodes
47            .iter()
48            .filter(|node| node.event_policy.focus)
49            .map(|node| self.hit_for_node(node, None))
50            .collect()
51    }
52
53    pub fn focusable_hits_in_scope(&self, scope_id: &UiId) -> Vec<HitResult> {
54        self.nodes
55            .iter()
56            .filter(|node| {
57                node.event_policy.focus
58                    && (&node.id == scope_id || self.node_is_descendant_of(&node.id, scope_id))
59            })
60            .map(|node| self.hit_for_node(node, None))
61            .collect()
62    }
63
64    pub fn focusable_hit(&self, id: &UiId) -> Option<HitResult> {
65        self.nodes
66            .iter()
67            .find(|node| &node.id == id && node.event_policy.focus)
68            .map(|node| self.hit_for_node(node, None))
69    }
70
71    pub fn hit_for_id(&self, id: &UiId) -> Option<HitResult> {
72        self.node(id).map(|node| self.hit_for_node(node, None))
73    }
74
75    pub fn handler_events(&self, event: &UiEvent) -> Vec<UiHandlerEvent> {
76        match event {
77            UiEvent::Clicked(hit) => self
78                .handler_event(&hit.id, UiEventPayload::Click)
79                .into_iter()
80                .collect(),
81            UiEvent::Wheel { hit, delta } => self
82                .handler_event(&hit.id, UiEventPayload::Wheel { delta: *delta })
83                .into_iter()
84                .collect(),
85            UiEvent::TextInput { target, text } => self
86                .handler_event(target, UiEventPayload::Input { text: text.clone() })
87                .into_iter()
88                .collect(),
89            UiEvent::ImeStarted { target } => self
90                .handler_event(target, UiEventPayload::CompositionStart)
91                .into_iter()
92                .collect(),
93            UiEvent::ImeUpdated {
94                target,
95                text,
96                cursor,
97            } => self
98                .handler_event(
99                    target,
100                    UiEventPayload::CompositionUpdate {
101                        text: text.clone(),
102                        cursor: cursor.clone(),
103                    },
104                )
105                .into_iter()
106                .collect(),
107            UiEvent::ImeEnded { target } => self
108                .handler_event(target, UiEventPayload::CompositionEnd)
109                .into_iter()
110                .collect(),
111            UiEvent::Keyboard { target, event } => self
112                .handler_event(
113                    target,
114                    UiEventPayload::Keyboard {
115                        event: event.clone(),
116                    },
117                )
118                .into_iter()
119                .collect(),
120            UiEvent::PointerPressed { hit, pointer } => self
121                .handler_event(&hit.id, UiEventPayload::PointerDown { pointer: *pointer })
122                .into_iter()
123                .collect(),
124            UiEvent::PointerMoved { hit, pointer } | UiEvent::PointerDragged { hit, pointer } => {
125                self.handler_event(&hit.id, UiEventPayload::PointerMove { pointer: *pointer })
126                    .into_iter()
127                    .collect()
128            }
129            UiEvent::PointerReleased { hit, pointer } => self
130                .handler_event(&hit.id, UiEventPayload::PointerUp { pointer: *pointer })
131                .into_iter()
132                .collect(),
133            UiEvent::FocusChanged { previous, current } => {
134                let mut events = previous
135                    .as_ref()
136                    .and_then(|id| self.handler_event(id, UiEventPayload::Blur))
137                    .into_iter()
138                    .collect::<Vec<_>>();
139                events.extend(
140                    current
141                        .as_ref()
142                        .and_then(|hit| self.handler_event(&hit.id, UiEventPayload::Focus)),
143                );
144                events
145            }
146            UiEvent::HoverChanged { .. }
147            | UiEvent::PressedChanged { .. }
148            | UiEvent::PointerLeft { .. }
149            | UiEvent::SemanticValue { .. }
150            | UiEvent::SemanticAction { .. } => Vec::new(),
151        }
152    }
153
154    pub fn handler_event(&self, target: &UiId, payload: UiEventPayload) -> Option<UiHandlerEvent> {
155        let node = self.node(target)?;
156        let kind = payload.kind();
157        let mut lineage = vec![node];
158        let mut parent = node.parent.as_ref();
159        while let Some(parent_id) = parent {
160            let Some(parent_node) = self.node(parent_id) else {
161                break;
162            };
163            lineage.push(parent_node);
164            parent = parent_node.parent.as_ref();
165        }
166        let capture_handlers = lineage
167            .iter()
168            .rev()
169            .flat_map(|node| &node.input_event_handlers)
170            .filter(|binding| binding.capture && binding.kind == kind)
171            .map(|binding| binding.handler.clone())
172            .collect::<Vec<_>>();
173        let bubble_handlers = lineage
174            .iter()
175            .flat_map(|node| &node.input_event_handlers)
176            .filter(|binding| !binding.capture && binding.kind == kind)
177            .map(|binding| binding.handler.clone())
178            .collect::<Vec<_>>();
179        (!capture_handlers.is_empty() || !bubble_handlers.is_empty()).then(|| UiHandlerEvent {
180            target: target.clone(),
181            payload,
182            capture_handlers,
183            bubble_handlers,
184        })
185    }
186
187    pub fn auto_focus_hit(&self) -> Option<HitResult> {
188        self.nodes
189            .iter()
190            .find(|node| node.auto_focus && node.event_policy.focus)
191            .map(|node| self.hit_for_node(node, None))
192    }
193
194    pub fn focus_scope_ids(&self) -> Vec<UiId> {
195        self.nodes
196            .iter()
197            .filter(|node| node.focus_scope)
198            .map(|node| node.id.clone())
199            .collect()
200    }
201
202    pub fn active_focus_scope_id(&self) -> Option<UiId> {
203        self.nodes
204            .iter()
205            .rev()
206            .find(|node| node.focus_scope && node.render_phase == RenderPhase::Popup)
207            .or_else(|| self.nodes.iter().rev().find(|node| node.focus_scope))
208            .map(|node| node.id.clone())
209    }
210
211    pub fn focusable_hit_in_scope(&self, id: &UiId, scope_id: &UiId) -> Option<HitResult> {
212        self.node(id)
213            .filter(|node| {
214                node.event_policy.focus
215                    && (&node.id == scope_id || self.node_is_descendant_of(&node.id, scope_id))
216            })
217            .map(|node| self.hit_for_node(node, None))
218    }
219
220    pub fn auto_focus_hit_in_scope(&self, scope_id: &UiId) -> Option<HitResult> {
221        self.nodes
222            .iter()
223            .find(|node| {
224                node.auto_focus
225                    && node.event_policy.focus
226                    && self.node_is_descendant_of(&node.id, scope_id)
227            })
228            .map(|node| self.hit_for_node(node, None))
229    }
230
231    fn node_is_descendant_of(&self, id: &UiId, ancestor_id: &UiId) -> bool {
232        let mut parent = self.node(id).and_then(|node| node.parent.as_ref());
233        while let Some(parent_id) = parent {
234            if parent_id == ancestor_id {
235                return true;
236            }
237            parent = self.node(parent_id).and_then(|node| node.parent.as_ref());
238        }
239        false
240    }
241
242    fn frontmost_node_at(
243        &self,
244        point: Point,
245        accepts: impl Fn(&UiNode) -> bool,
246    ) -> Option<&UiNode> {
247        let contains = |node: &UiNode| {
248            let offset = self.ancestor_content_offset(node);
249            accepts(node)
250                && node.hit_rect.translate(offset.0, offset.1).contains(point)
251                && self.node_visible_at_point(node, point)
252        };
253        self.nodes
254            .iter()
255            .rev()
256            .find(|node| node.render_phase == RenderPhase::Popup && contains(node))
257            .or_else(|| {
258                self.nodes
259                    .iter()
260                    .rev()
261                    .find(|node| node.render_phase != RenderPhase::Popup && contains(node))
262            })
263            .map(Arc::as_ref)
264    }
265
266    fn node_visible_at_point(&self, node: &UiNode, point: Point) -> bool {
267        if node.render_phase == RenderPhase::Popup {
268            return true;
269        }
270        let mut parent = node.parent.as_ref();
271        while let Some(parent_id) = parent {
272            let Some(parent_node) = self.node(parent_id) else {
273                return true;
274            };
275            let offset = self.ancestor_content_offset(parent_node);
276            if parent_node
277                .clip_rect
278                .is_some_and(|clip| !clip.translate(offset.0, offset.1).contains(point))
279            {
280                return false;
281            }
282            parent = parent_node.parent.as_ref();
283        }
284        true
285    }
286
287    pub(crate) fn ancestor_content_offset(&self, node: &UiNode) -> (f32, f32) {
288        let mut x = 0.0;
289        let mut y = 0.0;
290        let mut parent = node.parent.as_ref();
291        while let Some(parent_id) = parent {
292            let Some(parent_node) = self.node(parent_id) else {
293                break;
294            };
295            x += parent_node.content_offset.0;
296            y += parent_node.content_offset.1;
297            parent = parent_node.parent.as_ref();
298        }
299        (x, y)
300    }
301
302    fn hit_for_node(&self, node: &UiNode, action: Option<UiAction>) -> HitResult {
303        let offset = self.ancestor_content_offset(node);
304        let mut lineage = vec![node];
305        let mut parent = node.parent.as_ref();
306        while let Some(parent_id) = parent {
307            let Some(parent_node) = self.node(parent_id) else {
308                break;
309            };
310            lineage.push(parent_node);
311            parent = parent_node.parent.as_ref();
312        }
313        let capture_handlers = lineage
314            .iter()
315            .rev()
316            .filter_map(|node| node.click_capture_handler.clone())
317            .collect();
318        let bubble_handlers = lineage
319            .iter()
320            .filter_map(|node| node.click_handler.clone())
321            .collect();
322        HitResult {
323            id: node.id.clone(),
324            rect: node.hit_rect.translate(offset.0, offset.1),
325            interaction: node.interaction,
326            policy: node.event_policy,
327            action,
328            action_target: node.action_target.clone(),
329            capture_handlers,
330            bubble_handlers,
331            click_handler: node.click_handler.clone(),
332        }
333    }
334}