Skip to main content

lgui_core/core/input/event/
dispatcher.rs

1use super::*;
2
3pub struct UiEventDispatcher {
4    state: UiInteractionState,
5    pressed_hit: Option<HitResult>,
6    active_focus_scope: Option<UiId>,
7    focus_history: HashMap<UiId, Option<UiId>>,
8}
9
10impl UiEventDispatcher {
11    pub fn new() -> Self {
12        Self {
13            state: UiInteractionState::default(),
14            pressed_hit: None,
15            active_focus_scope: None,
16            focus_history: HashMap::new(),
17        }
18    }
19
20    pub fn state(&self) -> UiInteractionState {
21        self.state.clone()
22    }
23
24    pub fn clear_interaction_state(&mut self) -> bool {
25        if self.state == UiInteractionState::default() {
26            return false;
27        }
28        self.state = UiInteractionState::default();
29        self.active_focus_scope = None;
30        self.focus_history.clear();
31        true
32    }
33
34    pub fn dispatch(&mut self, tree: &HostTree, event: InputEvent) -> Vec<UiEvent> {
35        match event {
36            InputEvent::PointerMove(pointer) => self.pointer_move(tree, pointer),
37            InputEvent::PointerDown { pointer, .. } => self.pointer_down(tree, pointer),
38            InputEvent::PointerUp { pointer, .. } => self.pointer_up(tree, pointer),
39            InputEvent::PointerEnter(pointer) => self.pointer_move(tree, pointer),
40            InputEvent::Wheel { point, delta } => self.wheel(tree, point, delta),
41            InputEvent::TextInput(text) => self.text_input(text),
42            InputEvent::Ime(ImeEvent::Enabled) => self.ime_start(),
43            InputEvent::Ime(ImeEvent::Preedit { text, cursor }) => self.ime_update(text, cursor),
44            InputEvent::Ime(ImeEvent::Commit(text)) => self.text_input(text),
45            InputEvent::Ime(ImeEvent::Disabled) => self.ime_end(),
46            InputEvent::Keyboard(event) => self.keyboard(event),
47            InputEvent::PointerLeave(_) => self.pointer_leave(),
48            InputEvent::Touch { pointer, phase } => match phase {
49                TouchPhase::Started => self.pointer_down(tree, pointer),
50                TouchPhase::Moved => self.pointer_move(tree, pointer),
51                TouchPhase::Ended => self.pointer_up(tree, pointer),
52                TouchPhase::Cancelled => self.pointer_leave(),
53            },
54            InputEvent::Platform(_) => Vec::new(),
55            InputEvent::Semantic(input) => self.semantic(tree, input),
56        }
57    }
58
59    fn semantic(&mut self, tree: &HostTree, input: SemanticInput) -> Vec<UiEvent> {
60        match input {
61            SemanticInput::Click(id) => tree
62                .hit_for_id(&id)
63                .map(UiEvent::Clicked)
64                .into_iter()
65                .collect(),
66            SemanticInput::Focus(id) => tree
67                .focusable_hit(&id)
68                .map(|hit| self.set_focus(Some(hit)))
69                .unwrap_or_default(),
70            SemanticInput::Blur(id) => {
71                if self.state.focused.as_ref() == Some(&id) {
72                    self.set_focus(None)
73                } else {
74                    Vec::new()
75                }
76            }
77            SemanticInput::SetValue { target, value } => {
78                vec![UiEvent::SemanticValue { target, value }]
79            }
80            SemanticInput::Action { target, action } => {
81                vec![UiEvent::SemanticAction { target, action }]
82            }
83        }
84    }
85
86    fn pointer_move(&mut self, tree: &HostTree, pointer: PointerData) -> Vec<UiEvent> {
87        let hit = tree.hit_test(pointer.point).filter(|hit| hit.policy.hover);
88        let current = hit.as_ref().map(|hit| hit.id.clone());
89        let drag = self
90            .pressed_hit
91            .clone()
92            .map(|hit| UiEvent::PointerDragged { hit, pointer });
93        if self.state.hovered == current {
94            let mut events = hit
95                .map(|hit| UiEvent::PointerMoved { hit, pointer })
96                .into_iter()
97                .collect::<Vec<_>>();
98            events.extend(drag);
99            return events;
100        }
101        let previous = self.state.hovered.clone();
102        self.state.hovered = current.clone();
103        let mut events = vec![UiEvent::HoverChanged {
104            previous,
105            current: hit,
106        }];
107        if let Some(event) = drag {
108            events.push(event);
109        }
110        if let Some(hit) = current.as_ref().and_then(|id| tree.hit_for_id(id)) {
111            events.push(UiEvent::PointerMoved { hit, pointer });
112        }
113        events
114    }
115
116    fn pointer_down(&mut self, tree: &HostTree, pointer: PointerData) -> Vec<UiEvent> {
117        let hit = tree.hit_test(pointer.point).filter(|hit| hit.policy.press);
118        let current = hit.as_ref().map(|hit| hit.id.clone());
119        let mut events = Vec::new();
120        if self.state.pressed != current {
121            let previous = self.state.pressed.clone();
122            self.state.pressed = current.clone();
123            self.pressed_hit = hit.clone();
124            events.push(UiEvent::PressedChanged {
125                previous,
126                current: hit.clone(),
127            });
128        } else {
129            self.pressed_hit = hit.clone();
130        }
131        if let Some(hit) = hit {
132            if hit.policy.focus {
133                let previous_focus = self.state.focused.clone();
134                self.state.focused = Some(hit.id.clone());
135                if previous_focus != self.state.focused {
136                    events.push(UiEvent::FocusChanged {
137                        previous: previous_focus,
138                        current: Some(hit.clone()),
139                    });
140                }
141            }
142            events.push(UiEvent::PointerPressed { hit, pointer });
143        } else if let Some(previous) = self.state.focused.take() {
144            events.push(UiEvent::FocusChanged {
145                previous: Some(previous),
146                current: None,
147            });
148        }
149        events
150    }
151
152    fn pointer_up(&mut self, tree: &HostTree, pointer: PointerData) -> Vec<UiEvent> {
153        let hit = tree.hit_test(pointer.point).filter(|hit| hit.policy.press);
154        let mut events = Vec::new();
155        let previous = self.state.pressed.clone();
156        let pressed_hit = self.pressed_hit.take();
157        self.state.pressed = None;
158        if previous.is_some() {
159            events.push(UiEvent::PressedChanged {
160                previous: previous.clone(),
161                current: None,
162            });
163        }
164        if let Some(hit) = pressed_hit {
165            events.push(UiEvent::PointerReleased { hit, pointer });
166        }
167        if let Some(hit) = hit {
168            if previous == Some(hit.id.clone()) {
169                if hit.policy.focus {
170                    let previous_focus = self.state.focused.clone();
171                    self.state.focused = Some(hit.id.clone());
172                    if previous_focus != self.state.focused {
173                        events.push(UiEvent::FocusChanged {
174                            previous: previous_focus,
175                            current: Some(hit.clone()),
176                        });
177                    }
178                }
179                events.push(UiEvent::Clicked(hit));
180            }
181        }
182        events
183    }
184
185    fn wheel(&mut self, tree: &HostTree, point: Point, delta: WheelDelta) -> Vec<UiEvent> {
186        tree.wheel_hit_test(point)
187            .map(|hit| vec![UiEvent::Wheel { hit, delta }])
188            .unwrap_or_default()
189    }
190
191    fn text_input(&mut self, text: String) -> Vec<UiEvent> {
192        self.state
193            .focused
194            .clone()
195            .map(|target| vec![UiEvent::TextInput { target, text }])
196            .unwrap_or_default()
197    }
198
199    fn ime_start(&self) -> Vec<UiEvent> {
200        self.state
201            .focused
202            .clone()
203            .map(|target| vec![UiEvent::ImeStarted { target }])
204            .unwrap_or_default()
205    }
206
207    fn ime_update(&self, text: String, cursor: Option<Range<usize>>) -> Vec<UiEvent> {
208        self.state
209            .focused
210            .clone()
211            .map(|target| {
212                vec![UiEvent::ImeUpdated {
213                    target,
214                    text,
215                    cursor,
216                }]
217            })
218            .unwrap_or_default()
219    }
220
221    fn ime_end(&self) -> Vec<UiEvent> {
222        self.state
223            .focused
224            .clone()
225            .map(|target| vec![UiEvent::ImeEnded { target }])
226            .unwrap_or_default()
227    }
228
229    fn keyboard(&self, event: KeyboardEvent) -> Vec<UiEvent> {
230        self.state
231            .focused
232            .clone()
233            .map(|target| vec![UiEvent::Keyboard { target, event }])
234            .unwrap_or_default()
235    }
236
237    pub(crate) fn focus_adjacent(&mut self, tree: &HostTree, reverse: bool) -> Vec<UiEvent> {
238        let focusable = tree
239            .active_focus_scope_id()
240            .map(|scope_id| tree.focusable_hits_in_scope(&scope_id))
241            .unwrap_or_else(|| tree.focusable_hits());
242        if focusable.is_empty() {
243            return Vec::new();
244        }
245        let current_index = self
246            .state
247            .focused
248            .as_ref()
249            .and_then(|focused| focusable.iter().position(|hit| &hit.id == focused));
250        let next_index = match (current_index, reverse) {
251            (Some(index), false) => (index + 1) % focusable.len(),
252            (Some(0), true) => focusable.len() - 1,
253            (Some(index), true) => index - 1,
254            (None, false) => 0,
255            (None, true) => focusable.len() - 1,
256        };
257        let next = focusable[next_index].clone();
258        self.set_focus(Some(next))
259    }
260
261    pub fn sync_focus_for_tree(&mut self, tree: &HostTree) -> Vec<UiEvent> {
262        let focus_scope_ids = tree.focus_scope_ids();
263        let next_scope = tree.active_focus_scope_id();
264
265        if self.active_focus_scope != next_scope {
266            let previous_scope = self.active_focus_scope.take();
267            let restored = previous_scope
268                .as_ref()
269                .filter(|scope_id| !focus_scope_ids.iter().any(|id| id == *scope_id))
270                .and_then(|scope_id| self.focus_history.remove(scope_id))
271                .flatten();
272            self.focus_history
273                .retain(|scope_id, _| focus_scope_ids.iter().any(|id| id == scope_id));
274
275            if let Some(scope_id) = next_scope.clone() {
276                self.focus_history
277                    .entry(scope_id.clone())
278                    .or_insert_with(|| self.state.focused.clone());
279                self.active_focus_scope = Some(scope_id.clone());
280                let target = restored
281                    .as_ref()
282                    .and_then(|id| tree.focusable_hit_in_scope(id, &scope_id))
283                    .or_else(|| tree.auto_focus_hit_in_scope(&scope_id))
284                    .or_else(|| tree.focusable_hits_in_scope(&scope_id).into_iter().next());
285                return self.set_focus(target);
286            }
287
288            let target = restored
289                .as_ref()
290                .and_then(|id| tree.focusable_hit(id))
291                .or_else(|| tree.auto_focus_hit());
292            return self.set_focus(target);
293        }
294
295        self.focus_history
296            .retain(|scope_id, _| focus_scope_ids.iter().any(|id| id == scope_id));
297
298        if let Some(scope_id) = next_scope {
299            if self
300                .state
301                .focused
302                .as_ref()
303                .is_some_and(|id| tree.focusable_hit_in_scope(id, &scope_id).is_some())
304            {
305                return Vec::new();
306            }
307            let target = tree
308                .auto_focus_hit_in_scope(&scope_id)
309                .or_else(|| tree.focusable_hits_in_scope(&scope_id).into_iter().next());
310            return self.set_focus(target);
311        }
312
313        if let Some(focused) = self.state.focused.as_ref() {
314            if tree.focusable_hit(focused).is_some() {
315                return Vec::new();
316            }
317            return self.set_focus(tree.auto_focus_hit());
318        }
319        if let Some(hit) = tree.auto_focus_hit() {
320            return self.set_focus(Some(hit));
321        }
322        Vec::new()
323    }
324
325    pub fn sync_interaction_for_tree(&mut self, tree: &HostTree) -> Vec<UiEvent> {
326        let mut events = Vec::new();
327        if self
328            .state
329            .hovered
330            .as_ref()
331            .is_some_and(|id| tree.node(id).map_or(true, |node| !node.event_policy.hover))
332        {
333            let previous = self.state.hovered.clone();
334            self.state.hovered = None;
335            events.push(UiEvent::HoverChanged {
336                previous,
337                current: None,
338            });
339        }
340        if self
341            .state
342            .pressed
343            .as_ref()
344            .is_some_and(|id| tree.node(id).map_or(true, |node| !node.event_policy.press))
345        {
346            let previous = self.state.pressed.clone();
347            self.state.pressed = None;
348            self.pressed_hit = None;
349            events.push(UiEvent::PressedChanged {
350                previous,
351                current: None,
352            });
353        }
354        events
355    }
356
357    pub fn focus_node(&mut self, tree: &HostTree, id: &UiId) -> Vec<UiEvent> {
358        self.set_focus(tree.focusable_hit(id))
359    }
360
361    fn set_focus(&mut self, current: Option<HitResult>) -> Vec<UiEvent> {
362        let previous = self.state.focused.clone();
363        self.state.focused = current.as_ref().map(|hit| hit.id.clone());
364        if previous == self.state.focused {
365            return Vec::new();
366        }
367        vec![UiEvent::FocusChanged { previous, current }]
368    }
369
370    fn pointer_leave(&mut self) -> Vec<UiEvent> {
371        let previous = self.state.hovered.clone();
372        self.state.hovered = None;
373        self.state.pressed = None;
374        self.pressed_hit = None;
375        vec![UiEvent::PointerLeft { previous }]
376    }
377}
378
379impl Default for UiEventDispatcher {
380    fn default() -> Self {
381        Self::new()
382    }
383}