lgui_core/core/component/runtime/
input.rs1use super::*;
2
3impl UiRuntime {
4 pub fn handle_input(&mut self, tree: &HostTree, input: InputEvent) -> RuntimeOutput {
5 let focus_traversal = match &input {
6 InputEvent::Keyboard(event)
7 if event.state == KeyState::Down
8 && event.key == LogicalKey::Named(NamedKey::Tab) =>
9 {
10 Some(event.modifiers.shift())
11 }
12 _ => None,
13 };
14 let mut events = self.events.dispatch(tree, input);
15 let action_events = Vec::new();
16 let mut default_actions = Vec::new();
17 events.retain(|event| {
18 match event {
19 UiEvent::Wheel { hit, delta } => {
20 let Some(action) = hit.action.as_ref() else {
21 return true;
22 };
23 let action = action.clone().payload(delta.y.to_string());
24 let target = hit.action_target.as_ref().unwrap_or(&hit.id);
25 default_actions.push(UiDefaultAction {
26 event_target: hit.id.clone(),
27 action_target: target.clone(),
28 action,
29 });
30 }
31 UiEvent::Clicked(hit) => {
32 if let Some(action) = hit.action.as_ref() {
33 let target = hit.action_target.as_ref().unwrap_or(&hit.id);
34 default_actions.push(UiDefaultAction {
35 event_target: hit.id.clone(),
36 action_target: target.clone(),
37 action: action.clone(),
38 });
39 }
40 }
41 UiEvent::TextInput { target, text } => {
42 default_actions.push(UiDefaultAction {
43 event_target: target.clone(),
44 action_target: target.clone(),
45 action: UiAction::new("text.input").payload(text.clone()),
46 });
47 }
48 UiEvent::SemanticValue { target, value } => {
49 default_actions.push(UiDefaultAction {
50 event_target: target.clone(),
51 action_target: target.clone(),
52 action: UiAction::new("semantic.set_value").payload(value.clone()),
53 });
54 }
55 UiEvent::SemanticAction { target, action } => {
56 let id = match action {
57 super::SemanticAction::Increment => "semantic.increment",
58 super::SemanticAction::Decrement => "semantic.decrement",
59 super::SemanticAction::ScrollIntoView => "semantic.scroll_into_view",
60 super::SemanticAction::ScrollUp => "semantic.scroll_up",
61 super::SemanticAction::ScrollDown => "semantic.scroll_down",
62 super::SemanticAction::ScrollLeft => "semantic.scroll_left",
63 super::SemanticAction::ScrollRight => "semantic.scroll_right",
64 super::SemanticAction::SetTextSelection => "semantic.set_text_selection",
65 super::SemanticAction::Click
66 | super::SemanticAction::Focus
67 | super::SemanticAction::Blur
68 | super::SemanticAction::SetValue => return true,
69 };
70 default_actions.push(UiDefaultAction {
71 event_target: target.clone(),
72 action_target: target.clone(),
73 action: UiAction::new(id),
74 });
75 }
76 UiEvent::Keyboard { target, event } if event.state == KeyState::Down => {
77 let action = match &event.key {
78 LogicalKey::Named(NamedKey::Backspace) => {
79 Some(UiAction::new("text.backspace"))
80 }
81 LogicalKey::Named(NamedKey::ArrowLeft) => Some(
82 UiAction::new("text.move.left").payload(if event.modifiers.shift() {
83 "extend"
84 } else {
85 "collapse"
86 }),
87 ),
88 LogicalKey::Named(NamedKey::ArrowRight) => Some(
89 UiAction::new("text.move.right").payload(if event.modifiers.shift() {
90 "extend"
91 } else {
92 "collapse"
93 }),
94 ),
95 LogicalKey::Named(NamedKey::ArrowUp) => Some(
96 UiAction::new("text.move.up").payload(if event.modifiers.shift() {
97 "extend"
98 } else {
99 "collapse"
100 }),
101 ),
102 LogicalKey::Named(NamedKey::ArrowDown) => Some(
103 UiAction::new("text.move.down").payload(if event.modifiers.shift() {
104 "extend"
105 } else {
106 "collapse"
107 }),
108 ),
109 LogicalKey::Named(NamedKey::Enter) => {
110 Some(UiAction::new("text.input").payload("\n"))
111 }
112 LogicalKey::Character(key)
113 if event.modifiers.ctrl() && key.eq_ignore_ascii_case("a") =>
114 {
115 Some(UiAction::new("text.select.all"))
116 }
117 LogicalKey::Character(key)
118 if event.modifiers.ctrl() && key.eq_ignore_ascii_case("c") =>
119 {
120 Some(UiAction::new("text.copy"))
121 }
122 LogicalKey::Character(key)
123 if event.modifiers.ctrl() && key.eq_ignore_ascii_case("v") =>
124 {
125 Some(UiAction::new("text.paste"))
126 }
127 _ => None,
128 };
129 if let Some(action) = action {
130 default_actions.push(UiDefaultAction {
131 event_target: target.clone(),
132 action_target: target.clone(),
133 action,
134 });
135 }
136 }
137 UiEvent::Keyboard { .. } => {}
138 UiEvent::PointerPressed { hit, pointer } => {
139 let point = pointer.point;
140 let payload = format!("{},{}", point.x - hit.rect.left, point.y - hit.rect.top);
141 let target = hit.action_target.as_ref().unwrap_or(&hit.id);
142 if self.component_states.contains(target) {
143 default_actions.push(UiDefaultAction {
144 event_target: hit.id.clone(),
145 action_target: target.clone(),
146 action: UiAction::new(super::POINTER_DOWN_ACTION).payload(payload),
147 });
148 }
149 }
150 UiEvent::PointerDragged { hit, pointer } => {
151 let point = pointer.point;
152 let payload = format!("{},{}", point.x - hit.rect.left, point.y - hit.rect.top);
153 let target = hit.action_target.as_ref().unwrap_or(&hit.id);
154 if self.component_states.contains(target) {
155 default_actions.push(UiDefaultAction {
156 event_target: hit.id.clone(),
157 action_target: target.clone(),
158 action: UiAction::new(super::POINTER_DRAG_ACTION).payload(payload),
159 });
160 }
161 }
162 UiEvent::PointerReleased { hit, pointer } => {
163 let point = pointer.point;
164 let payload = format!("{},{}", point.x - hit.rect.left, point.y - hit.rect.top);
165 let target = hit.action_target.as_ref().unwrap_or(&hit.id);
166 if self.component_states.contains(target) {
167 default_actions.push(UiDefaultAction {
168 event_target: hit.id.clone(),
169 action_target: target.clone(),
170 action: UiAction::new(super::POINTER_UP_ACTION).payload(payload),
171 });
172 }
173 }
174 _ => {}
175 }
176 true
177 });
178 if let Some(reverse) = focus_traversal {
179 let event_target = self
180 .events
181 .state()
182 .focused
183 .or_else(|| tree.active_focus_scope_id())
184 .or_else(|| tree.focusable_hits().into_iter().next().map(|hit| hit.id));
185 if let Some(event_target) = event_target {
186 default_actions.push(UiDefaultAction {
187 event_target: event_target.clone(),
188 action_target: event_target,
189 action: UiAction::new(FOCUS_TRAVERSAL_ACTION).payload(if reverse {
190 "reverse"
191 } else {
192 "forward"
193 }),
194 });
195 }
196 }
197 let handler_events = events
198 .iter()
199 .flat_map(|event| tree.handler_events(event))
200 .collect();
201 for event in events.iter().cloned() {
202 self.dirty.mark_event(event);
203 }
204 self.mark_component_owners(tree, events.iter().flat_map(interaction_state_target_ids));
208 let animation_changed =
209 apply_events_to_animations(tree, &mut self.animations, events.iter().cloned());
210 if animation_changed {
211 self.mark_component_owners(tree, events.iter().flat_map(event_target_ids));
212 }
213 let dirty_bounds = self.dirty.take().bounds(tree);
214 RuntimeOutput {
215 events,
216 handler_events,
217 action_events,
218 default_actions,
219 dirty_bounds,
220 animation_changed,
221 route_changed: false,
222 }
223 }
224}
225
226pub(super) fn event_target_ids(event: &UiEvent) -> Vec<&UiId> {
227 match event {
228 UiEvent::HoverChanged { previous, current }
229 | UiEvent::PressedChanged { previous, current } => previous
230 .iter()
231 .chain(current.iter().map(|hit| &hit.id))
232 .collect(),
233 UiEvent::Clicked(hit)
234 | UiEvent::Wheel { hit, .. }
235 | UiEvent::PointerPressed { hit, .. }
236 | UiEvent::PointerMoved { hit, .. }
237 | UiEvent::PointerDragged { hit, .. }
238 | UiEvent::PointerReleased { hit, .. } => vec![&hit.id],
239 UiEvent::TextInput { target, .. }
240 | UiEvent::ImeStarted { target }
241 | UiEvent::ImeUpdated { target, .. }
242 | UiEvent::ImeEnded { target }
243 | UiEvent::Keyboard { target, .. }
244 | UiEvent::SemanticValue { target, .. }
245 | UiEvent::SemanticAction { target, .. } => vec![target],
246 UiEvent::FocusChanged { current, previous } => previous
247 .iter()
248 .chain(current.iter().map(|hit| &hit.id))
249 .collect(),
250 UiEvent::PointerLeft { previous } => previous.iter().collect(),
251 }
252}
253
254pub(super) fn interaction_state_target_ids(event: &UiEvent) -> Vec<&UiId> {
255 match event {
256 UiEvent::HoverChanged { previous, current }
257 | UiEvent::PressedChanged { previous, current } => previous
258 .iter()
259 .chain(current.iter().map(|hit| &hit.id))
260 .collect(),
261 UiEvent::FocusChanged { previous, current } => previous
262 .iter()
263 .chain(current.iter().map(|hit| &hit.id))
264 .collect(),
265 UiEvent::PointerLeft { previous } => previous.iter().collect(),
266 UiEvent::Clicked(_)
267 | UiEvent::Wheel { .. }
268 | UiEvent::TextInput { .. }
269 | UiEvent::ImeStarted { .. }
270 | UiEvent::ImeUpdated { .. }
271 | UiEvent::ImeEnded { .. }
272 | UiEvent::Keyboard { .. }
273 | UiEvent::PointerPressed { .. }
274 | UiEvent::PointerMoved { .. }
275 | UiEvent::PointerDragged { .. }
276 | UiEvent::PointerReleased { .. } => Vec::new(),
277 UiEvent::SemanticValue { .. } | UiEvent::SemanticAction { .. } => Vec::new(),
278 }
279}