Another project
1use bone_app::{InputEvent, KeyDown, NavKey, ScrollDelta, WindowPoint};
2use bone_render::{ViewportExtent, ViewportPx};
3use bone_ui::input::{KeyChar, KeyCode as UiKeyCode, ModifierMask, NamedKey, PointerButton};
4use winit::{
5 dpi::PhysicalSize,
6 event::{ElementState, KeyEvent, MouseButton, MouseScrollDelta, WindowEvent},
7 keyboard::{Key, KeyCode, ModifiersState, NamedKey as WinitNamed, PhysicalKey, SmolStr},
8};
9
10pub enum AppEvent {
11 Input(InputEvent),
12 Redraw,
13 Close,
14 Ignored,
15}
16
17impl AppEvent {
18 pub fn from_winit(event: WindowEvent) -> Self {
19 match event {
20 WindowEvent::CloseRequested => Self::Close,
21 WindowEvent::RedrawRequested => Self::Redraw,
22 WindowEvent::Resized(size) => Self::Input(InputEvent::Resize(viewport_extent(size))),
23 WindowEvent::Focused(focused) => Self::Input(InputEvent::Focus(focused)),
24 WindowEvent::ModifiersChanged(mods) => {
25 Self::Input(InputEvent::Modifiers(modifier_mask(mods.state())))
26 }
27 WindowEvent::CursorMoved { position, .. } => Self::Input(InputEvent::CursorMove(
28 WindowPoint::new(position.x, position.y),
29 )),
30 WindowEvent::CursorLeft { .. } => Self::Input(InputEvent::CursorLeft),
31 WindowEvent::CursorEntered { .. } => Self::Input(InputEvent::CursorEntered),
32 WindowEvent::MouseInput { state, button, .. } => match pointer_button(button) {
33 Some(button) => Self::Input(InputEvent::Pointer {
34 button,
35 pressed: state == ElementState::Pressed,
36 }),
37 None => Self::Ignored,
38 },
39 WindowEvent::MouseWheel { delta, .. } => {
40 Self::Input(InputEvent::Wheel(scroll_delta(delta)))
41 }
42 WindowEvent::KeyboardInput {
43 event:
44 KeyEvent {
45 state: ElementState::Pressed,
46 physical_key,
47 logical_key,
48 text,
49 repeat,
50 ..
51 },
52 ..
53 } => Self::Input(InputEvent::KeyDown(key_down(
54 physical_key,
55 &logical_key,
56 text,
57 repeat,
58 ))),
59 WindowEvent::KeyboardInput { .. }
60 | WindowEvent::ActivationTokenDone { .. }
61 | WindowEvent::Moved(_)
62 | WindowEvent::Destroyed
63 | WindowEvent::DroppedFile(_)
64 | WindowEvent::HoveredFile(_)
65 | WindowEvent::HoveredFileCancelled
66 | WindowEvent::Ime(_)
67 | WindowEvent::PinchGesture { .. }
68 | WindowEvent::PanGesture { .. }
69 | WindowEvent::DoubleTapGesture { .. }
70 | WindowEvent::RotationGesture { .. }
71 | WindowEvent::TouchpadPressure { .. }
72 | WindowEvent::AxisMotion { .. }
73 | WindowEvent::Touch(_)
74 | WindowEvent::ScaleFactorChanged { .. }
75 | WindowEvent::ThemeChanged(_)
76 | WindowEvent::Occluded(_) => Self::Ignored,
77 }
78 }
79}
80
81pub fn viewport_extent(size: PhysicalSize<u32>) -> ViewportExtent {
82 ViewportExtent::new(
83 ViewportPx::new(size.width.max(1)),
84 ViewportPx::new(size.height.max(1)),
85 )
86}
87
88fn modifier_mask(state: ModifiersState) -> ModifierMask {
89 let bits = [
90 (state.control_key(), ModifierMask::CTRL),
91 (state.shift_key(), ModifierMask::SHIFT),
92 (state.alt_key(), ModifierMask::ALT),
93 (state.super_key(), ModifierMask::META),
94 ];
95 bits.iter()
96 .filter(|(active, _)| *active)
97 .fold(ModifierMask::NONE, |acc, (_, m)| acc.union(*m))
98}
99
100const fn pointer_button(button: MouseButton) -> Option<PointerButton> {
101 match button {
102 MouseButton::Left => Some(PointerButton::Primary),
103 MouseButton::Right => Some(PointerButton::Secondary),
104 MouseButton::Middle => Some(PointerButton::Middle),
105 MouseButton::Back | MouseButton::Forward | MouseButton::Other(_) => None,
106 }
107}
108
109fn scroll_delta(delta: MouseScrollDelta) -> ScrollDelta {
110 match delta {
111 MouseScrollDelta::LineDelta(x, y) => ScrollDelta::Lines { x, y },
112 MouseScrollDelta::PixelDelta(p) => ScrollDelta::Pixels { x: p.x, y: p.y },
113 }
114}
115
116fn key_down(
117 physical_key: PhysicalKey,
118 logical_key: &Key,
119 text: Option<SmolStr>,
120 repeat: bool,
121) -> KeyDown {
122 let physical_code = match physical_key {
123 PhysicalKey::Code(c) => Some(c),
124 PhysicalKey::Unidentified(_) => None,
125 };
126 let logical_named = match logical_key {
127 Key::Named(nk) => winit_named_to_ui(*nk),
128 _ => None,
129 };
130 let named = logical_named.or_else(|| physical_code.and_then(keycode_to_named));
131 let code = named.map(UiKeyCode::Named).or_else(|| {
132 physical_code
133 .and_then(keycode_to_char)
134 .map(|c| UiKeyCode::Char(KeyChar::from_char(c)))
135 });
136 KeyDown {
137 code,
138 nav: physical_code.and_then(keycode_to_nav),
139 text: text.map(|t| t.to_string()),
140 repeat,
141 }
142}
143
144const fn keycode_to_nav(code: KeyCode) -> Option<NavKey> {
145 match code {
146 KeyCode::ArrowLeft => Some(NavKey::Left),
147 KeyCode::ArrowRight => Some(NavKey::Right),
148 KeyCode::ArrowUp => Some(NavKey::Up),
149 KeyCode::ArrowDown => Some(NavKey::Down),
150 KeyCode::KeyZ => Some(NavKey::Zoom),
151 KeyCode::Equal => Some(NavKey::ZoomIn),
152 KeyCode::Minus => Some(NavKey::ZoomOut),
153 _ => None,
154 }
155}
156
157fn keycode_to_named(code: KeyCode) -> Option<NamedKey> {
158 match code {
159 KeyCode::Tab => Some(NamedKey::Tab),
160 KeyCode::Enter | KeyCode::NumpadEnter => Some(NamedKey::Enter),
161 KeyCode::Escape => Some(NamedKey::Escape),
162 KeyCode::Backspace => Some(NamedKey::Backspace),
163 KeyCode::Delete => Some(NamedKey::Delete),
164 KeyCode::Space => Some(NamedKey::Space),
165 KeyCode::ArrowUp => Some(NamedKey::ArrowUp),
166 KeyCode::ArrowDown => Some(NamedKey::ArrowDown),
167 KeyCode::ArrowLeft => Some(NamedKey::ArrowLeft),
168 KeyCode::ArrowRight => Some(NamedKey::ArrowRight),
169 KeyCode::Home => Some(NamedKey::Home),
170 KeyCode::End => Some(NamedKey::End),
171 KeyCode::PageUp => Some(NamedKey::PageUp),
172 KeyCode::PageDown => Some(NamedKey::PageDown),
173 KeyCode::F2 => Some(NamedKey::F2),
174 _ => None,
175 }
176}
177
178fn winit_named_to_ui(named: WinitNamed) -> Option<NamedKey> {
179 match named {
180 WinitNamed::Tab => Some(NamedKey::Tab),
181 WinitNamed::Enter => Some(NamedKey::Enter),
182 WinitNamed::Escape => Some(NamedKey::Escape),
183 WinitNamed::Backspace => Some(NamedKey::Backspace),
184 WinitNamed::Delete => Some(NamedKey::Delete),
185 WinitNamed::Space => Some(NamedKey::Space),
186 WinitNamed::ArrowUp => Some(NamedKey::ArrowUp),
187 WinitNamed::ArrowDown => Some(NamedKey::ArrowDown),
188 WinitNamed::ArrowLeft => Some(NamedKey::ArrowLeft),
189 WinitNamed::ArrowRight => Some(NamedKey::ArrowRight),
190 WinitNamed::Home => Some(NamedKey::Home),
191 WinitNamed::End => Some(NamedKey::End),
192 WinitNamed::PageUp => Some(NamedKey::PageUp),
193 WinitNamed::PageDown => Some(NamedKey::PageDown),
194 WinitNamed::F2 => Some(NamedKey::F2),
195 _ => None,
196 }
197}
198
199fn keycode_to_char(code: KeyCode) -> Option<char> {
200 match code {
201 KeyCode::KeyA => Some('a'),
202 KeyCode::KeyB => Some('b'),
203 KeyCode::KeyC => Some('c'),
204 KeyCode::KeyD => Some('d'),
205 KeyCode::KeyE => Some('e'),
206 KeyCode::KeyF => Some('f'),
207 KeyCode::KeyG => Some('g'),
208 KeyCode::KeyH => Some('h'),
209 KeyCode::KeyI => Some('i'),
210 KeyCode::KeyJ => Some('j'),
211 KeyCode::KeyK => Some('k'),
212 KeyCode::KeyL => Some('l'),
213 KeyCode::KeyM => Some('m'),
214 KeyCode::KeyN => Some('n'),
215 KeyCode::KeyO => Some('o'),
216 KeyCode::KeyP => Some('p'),
217 KeyCode::KeyQ => Some('q'),
218 KeyCode::KeyR => Some('r'),
219 KeyCode::KeyS => Some('s'),
220 KeyCode::KeyT => Some('t'),
221 KeyCode::KeyU => Some('u'),
222 KeyCode::KeyV => Some('v'),
223 KeyCode::KeyW => Some('w'),
224 KeyCode::KeyX => Some('x'),
225 KeyCode::KeyY => Some('y'),
226 KeyCode::KeyZ => Some('z'),
227 KeyCode::Digit0 => Some('0'),
228 KeyCode::Digit1 => Some('1'),
229 KeyCode::Digit2 => Some('2'),
230 KeyCode::Digit3 => Some('3'),
231 KeyCode::Digit4 => Some('4'),
232 KeyCode::Digit5 => Some('5'),
233 KeyCode::Digit6 => Some('6'),
234 KeyCode::Digit7 => Some('7'),
235 KeyCode::Digit8 => Some('8'),
236 KeyCode::Digit9 => Some('9'),
237 _ => None,
238 }
239}