1use repose_app::{ReposeRuntime, TouchGestureState};
4use repose_core::Vec2;
5use repose_core::input::PointerButton;
6use repose_core::shortcuts::{Action, Gesture};
7use winit::event::{ElementState, MouseScrollDelta, Touch};
8use winit::keyboard::{KeyCode, PhysicalKey};
9
10use crate::common::{map_key, winit_key_to_repose};
11
12pub fn on_modifiers_changed(rt: &mut ReposeRuntime, state: &winit::keyboard::ModifiersState) {
14 crate::common::update_modifiers(&mut rt.modifiers, state);
15}
16
17pub fn on_cursor_moved(
19 rt: &mut ReposeRuntime,
20 pos: Vec2,
21 inspector: &mut Option<repose_devtools::Inspector>,
22) -> Option<repose_core::CursorIcon> {
23 let result = rt.handle_pointer_move(pos);
24 if let (Some(inspector), Some(f)) = (inspector, &rt.frame_cache)
25 && inspector.hud.inspector_enabled
26 {
27 let hit = f.hit_regions.iter().find(|h| h.rect.contains(pos));
28 let hover_rect = hit.map(|h| h.rect);
29 let hover_info = hit.and_then(|h| {
30 f.semantics_nodes
31 .iter()
32 .find(|s| s.id == h.id)
33 .map(|s| repose_devtools::HoveredInfo {
34 id: s.id,
35 role: format!("{:?}", s.role),
36 label: s.label.clone(),
37 })
38 });
39 inspector.hud.set_hovered(hover_rect, hover_info);
40 }
41 result.cursor
42}
43
44pub fn on_mouse_wheel(rt: &mut ReposeRuntime, delta: MouseScrollDelta, scale: f32) -> bool {
46 let (dx_px, dy_px) = match delta {
47 MouseScrollDelta::LineDelta(x, y) => {
48 let _ = scale;
49 let unit_px = repose_core::Dp(60.0).to_px().0;
50 (-(x * unit_px), -(y * unit_px))
51 }
52 MouseScrollDelta::PixelDelta(p) => (-(p.x as f32), -(p.y as f32)),
53 };
54 rt.handle_scroll(Vec2 { x: dx_px, y: dy_px })
55}
56
57pub fn map_mouse_button(btn: winit::event::MouseButton) -> Option<PointerButton> {
59 match btn {
60 winit::event::MouseButton::Left => Some(PointerButton::Primary),
61 winit::event::MouseButton::Right => Some(PointerButton::Secondary),
62 winit::event::MouseButton::Middle => Some(PointerButton::Tertiary),
63 _ => None,
64 }
65}
66
67pub struct TouchResult {
69 pub dirty: bool,
70 pub pinch: Option<(f32, Vec2)>,
71 pub pan: Option<(Vec2, Vec2)>,
72 pub rotation: Option<(f32, Vec2)>,
73 pub swipe_right: Option<bool>,
74 pub press: Option<Option<u64>>,
78}
79
80pub fn handle_touch_raw(
81 rt: &mut ReposeRuntime,
82 touch_gestures: &mut TouchGestureState,
83 t: &Touch,
84 scale: f32,
85) -> TouchResult {
86 let pos_px = (t.location.x as f32, t.location.y as f32);
87 let tid = t.id;
88 match t.phase {
89 winit::event::TouchPhase::Started => {
90 touch_gestures.contact_down(tid, pos_px);
95 touch_gestures.touch_started(rt, tid, pos_px);
96 sync_touch_points(rt, touch_gestures);
97 TouchResult {
98 dirty: true,
99 pinch: None,
100 pan: None,
101 rotation: None,
102 swipe_right: None,
103 press: None,
104 }
105 }
106 winit::event::TouchPhase::Moved => {
107 touch_gestures.contact_down(tid, pos_px);
108 let (dirty, pinch, pan, rotation) = touch_gestures.touch_moved(rt, tid, pos_px, scale);
109 sync_touch_points(rt, touch_gestures);
110 TouchResult {
111 dirty,
112 pinch,
113 pan,
114 rotation,
115 swipe_right: None,
116 press: None,
117 }
118 }
119 winit::event::TouchPhase::Ended | winit::event::TouchPhase::Cancelled => {
120 let cancelled = t.phase == winit::event::TouchPhase::Cancelled;
121 let ended = touch_gestures.touch_ended(rt, tid, pos_px, cancelled);
125 touch_gestures.contact_up(tid);
126 sync_touch_points(rt, touch_gestures);
127 TouchResult {
128 dirty: false,
129 pinch: None,
130 pan: None,
131 rotation: None,
132 swipe_right: ended.swipe_right,
133 press: ended.press,
134 }
135 }
136 }
137}
138
139pub fn sync_touch_points(rt: &mut ReposeRuntime, touch_gestures: &TouchGestureState) {
146 rt.sched.touch_points.clear();
147 rt.sched.touch_points.extend(
148 touch_gestures
149 .active_touches()
150 .iter()
151 .map(|(id, (x, y))| (*id, *x, *y)),
152 );
153}
154
155pub fn on_touch(
158 rt: &mut ReposeRuntime,
159 touch_gestures: &mut TouchGestureState,
160 t: &Touch,
161 scale: f32,
162 mut dispatch: impl FnMut(Action) -> bool,
163) -> bool {
164 let r = handle_touch_raw(rt, touch_gestures, t, scale);
165 let mut dirty = r.dirty;
166 if let Some((delta_scale, center)) = r.pinch
167 && dispatch(Action::Gesture(Gesture::PinchWithCenter {
168 delta_scale,
169 center,
170 }))
171 {
172 dirty = true;
173 }
174 if let Some((delta, center)) = r.pan
175 && dispatch(Action::Gesture(Gesture::Pan { delta, center }))
176 {
177 dirty = true;
178 }
179 if let Some((delta_rotation, center)) = r.rotation
180 && dispatch(Action::Gesture(Gesture::Rotate {
181 delta_rotation,
182 center,
183 }))
184 {
185 dirty = true;
186 }
187 if let Some(right) = r.swipe_right {
188 let g = if right {
189 Gesture::SwipeRight
190 } else {
191 Gesture::SwipeLeft
192 };
193 if dispatch(Action::Gesture(g)) {
194 dirty = true;
195 }
196 }
197 dirty
198}
199
200pub fn on_touch_with_ime(
203 rt: &mut ReposeRuntime,
204 touch_gestures: &mut TouchGestureState,
205 t: &Touch,
206 scale: f32,
207 _window: &winit::window::Window,
208 dispatch: impl FnMut(Action) -> bool,
209) -> bool {
210 let pos_px = (t.location.x as f32, t.location.y as f32);
211 let tid = t.id;
212 if t.phase == winit::event::TouchPhase::Started {
213 touch_gestures.contact_down(tid, pos_px);
214 let focused = touch_gestures.touch_started(rt, tid, pos_px);
215 sync_touch_points(rt, touch_gestures);
216 #[cfg(not(target_arch = "wasm32"))]
217 if let Some(fid) = focused {
218 let editable = rt
219 .frame_cache
220 .as_ref()
221 .is_some_and(|frame| crate::common::is_editable_textfield_hit(frame, fid));
222 if rt.sched.window_focused && editable {
223 let (purpose, ac, cap) = rt.focused_keyboard_hints();
224 crate::common::set_ime_for_textfield_ex(_window, true, purpose, ac, cap);
225 } else {
226 crate::common::set_ime_for_textfield(_window, false);
227 }
228 } else {
229 crate::common::set_ime_for_textfield(_window, false);
230 }
231 #[cfg(target_arch = "wasm32")]
232 let _ = focused;
233 return true;
234 }
235 on_touch(rt, touch_gestures, t, scale, dispatch)
236}
237
238pub fn on_keyboard_input(
244 rt: &mut ReposeRuntime,
245 key_event: &winit::event::KeyEvent,
246 inspector: &mut Option<repose_devtools::Inspector>,
247) -> bool {
248 rt.set_physical_key(
249 &physical_key_name(key_event.physical_key),
250 key_event.state == ElementState::Pressed,
251 );
252 {
257 let key = map_physical_key(key_event.physical_key);
258 let sched = &mut rt.sched;
259 if key_event.state == ElementState::Pressed {
260 sched.held_keys.insert(key);
261 } else {
262 sched.held_keys.remove(&key);
263 }
264 }
265 if key_event.state == ElementState::Pressed
266 && !key_event.repeat
267 && rt.modifiers.ctrl
268 && rt.modifiers.shift
269 && key_event.physical_key == PhysicalKey::Code(KeyCode::KeyI)
270 && let Some(inspector) = inspector
271 {
272 inspector.hud.toggle_inspector();
273 return true;
274 }
275 let mapped = if crate::common::is_back_key(key_event) {
276 repose_core::input::Key::Escape
277 } else {
278 map_key(key_event.physical_key, &rt.modifiers)
279 };
280 let ke = winit_key_to_repose(key_event, &mapped, &rt.modifiers);
281 rt.handle_key_with_text(&ke, key_event.text.as_deref())
282}
283
284pub fn physical_key_name(key: PhysicalKey) -> String {
289 match key {
290 PhysicalKey::Code(code) => format!("{code:?}"),
291 PhysicalKey::Unidentified(native) => format!("Unidentified({native:?})"),
292 }
293}
294
295pub fn map_physical_key(key: PhysicalKey) -> repose_core::input::PhysicalKey {
300 repose_core::input::PhysicalKey::from_name(&physical_key_name(key))
301}
302
303pub fn on_ime(rt: &mut ReposeRuntime, ime: &winit::event::Ime) {
305 use winit::event::Ime;
306 let ev = match ime {
307 Ime::Enabled => repose_core::input::ImeEvent::Start,
308 Ime::Preedit(text, cursor) => repose_core::input::ImeEvent::Update {
309 text: text.clone(),
310 cursor: cursor.map(|(a, b)| (a, b)),
311 },
312 Ime::Commit(text) => repose_core::input::ImeEvent::Commit(text.clone()),
313 Ime::Disabled => repose_core::input::ImeEvent::Cancel,
314 };
315 rt.handle_ime(&ev);
316}