veloren_voxygen/ui/
event.rs

1use conrod_core::{
2    event::Input,
3    input::{self, Button},
4};
5use vek::*;
6use winit::event::{self, WindowEvent};
7
8// All `conrod_convert_*` functions were copied from `conrod_winit` (MIT
9// licensed).
10// Original version found at https://gitlab.com/veloren/conrod/-/blob/copypasta_0.7/backends/conrod_winit/src/macros.rs
11
12fn conrod_convert_key(key: &winit::keyboard::Key) -> input::Key {
13    match key {
14        winit::keyboard::Key::Named(key) => match key {
15            winit::keyboard::NamedKey::Escape => input::keyboard::Key::Escape,
16            winit::keyboard::NamedKey::F1 => input::keyboard::Key::F1,
17            winit::keyboard::NamedKey::F2 => input::keyboard::Key::F2,
18            winit::keyboard::NamedKey::F3 => input::keyboard::Key::F3,
19            winit::keyboard::NamedKey::F4 => input::keyboard::Key::F4,
20            winit::keyboard::NamedKey::F5 => input::keyboard::Key::F5,
21            winit::keyboard::NamedKey::F6 => input::keyboard::Key::F6,
22            winit::keyboard::NamedKey::F7 => input::keyboard::Key::F7,
23            winit::keyboard::NamedKey::F8 => input::keyboard::Key::F8,
24            winit::keyboard::NamedKey::F9 => input::keyboard::Key::F9,
25            winit::keyboard::NamedKey::F10 => input::keyboard::Key::F10,
26            winit::keyboard::NamedKey::F11 => input::keyboard::Key::F11,
27            winit::keyboard::NamedKey::F12 => input::keyboard::Key::F12,
28            winit::keyboard::NamedKey::F13 => input::keyboard::Key::F13,
29            winit::keyboard::NamedKey::F14 => input::keyboard::Key::F14,
30            winit::keyboard::NamedKey::F15 => input::keyboard::Key::F15,
31            winit::keyboard::NamedKey::F16 => input::keyboard::Key::F16,
32            winit::keyboard::NamedKey::F17 => input::keyboard::Key::F17,
33            winit::keyboard::NamedKey::F18 => input::keyboard::Key::F18,
34            winit::keyboard::NamedKey::F19 => input::keyboard::Key::F19,
35            winit::keyboard::NamedKey::F20 => input::keyboard::Key::F20,
36            winit::keyboard::NamedKey::F21 => input::keyboard::Key::F21,
37            winit::keyboard::NamedKey::F22 => input::keyboard::Key::F22,
38            winit::keyboard::NamedKey::F23 => input::keyboard::Key::F23,
39            winit::keyboard::NamedKey::F24 => input::keyboard::Key::F24,
40            winit::keyboard::NamedKey::ScrollLock => input::keyboard::Key::ScrollLock,
41            winit::keyboard::NamedKey::Pause => input::keyboard::Key::Pause,
42            winit::keyboard::NamedKey::Insert => input::keyboard::Key::Insert,
43            winit::keyboard::NamedKey::Home => input::keyboard::Key::Home,
44            winit::keyboard::NamedKey::Delete => input::keyboard::Key::Delete,
45            winit::keyboard::NamedKey::End => input::keyboard::Key::End,
46            winit::keyboard::NamedKey::PageDown => input::keyboard::Key::PageDown,
47            winit::keyboard::NamedKey::PageUp => input::keyboard::Key::PageUp,
48            winit::keyboard::NamedKey::ArrowLeft => input::keyboard::Key::Left,
49            winit::keyboard::NamedKey::ArrowUp => input::keyboard::Key::Up,
50            winit::keyboard::NamedKey::ArrowRight => input::keyboard::Key::Right,
51            winit::keyboard::NamedKey::ArrowDown => input::keyboard::Key::Down,
52            winit::keyboard::NamedKey::Backspace => input::keyboard::Key::Backspace,
53            winit::keyboard::NamedKey::Enter => input::keyboard::Key::Return,
54            winit::keyboard::NamedKey::Space => input::keyboard::Key::Space,
55            winit::keyboard::NamedKey::AudioVolumeMute => input::keyboard::Key::AudioMute,
56            winit::keyboard::NamedKey::MediaTrackNext => input::keyboard::Key::AudioNext,
57            winit::keyboard::NamedKey::Power => input::keyboard::Key::Power,
58            winit::keyboard::NamedKey::MediaTrackPrevious => input::keyboard::Key::AudioPrev,
59            winit::keyboard::NamedKey::Tab => input::keyboard::Key::Tab,
60            winit::keyboard::NamedKey::AudioVolumeDown => input::keyboard::Key::VolumeDown,
61            winit::keyboard::NamedKey::AudioVolumeUp => input::keyboard::Key::VolumeUp,
62            winit::keyboard::NamedKey::Copy => input::keyboard::Key::Copy,
63            winit::keyboard::NamedKey::Paste => input::keyboard::Key::Paste,
64            winit::keyboard::NamedKey::Cut => input::keyboard::Key::Cut,
65            // `winit` doesn't differenciate between left and right so we map everything to the
66            // right.
67            winit::keyboard::NamedKey::Shift => input::keyboard::Key::RShift,
68            winit::keyboard::NamedKey::Control => input::keyboard::Key::RCtrl,
69            winit::keyboard::NamedKey::Alt => input::keyboard::Key::RAlt,
70            _ => input::keyboard::Key::Unknown,
71        },
72        winit::keyboard::Key::Character(c) => match c.as_str() {
73            "a" | "A" => input::keyboard::Key::A,
74            "b" | "B" => input::keyboard::Key::B,
75            "c" | "C" => input::keyboard::Key::C,
76            "d" | "D" => input::keyboard::Key::D,
77            "e" | "E" => input::keyboard::Key::E,
78            "f" | "F" => input::keyboard::Key::F,
79            "g" | "G" => input::keyboard::Key::G,
80            "h" | "H" => input::keyboard::Key::H,
81            "i" | "I" => input::keyboard::Key::I,
82            "j" | "J" => input::keyboard::Key::J,
83            "k" | "K" => input::keyboard::Key::K,
84            "l" | "L" => input::keyboard::Key::L,
85            "m" | "M" => input::keyboard::Key::M,
86            "n" | "N" => input::keyboard::Key::N,
87            "o" | "O" => input::keyboard::Key::O,
88            "p" | "P" => input::keyboard::Key::P,
89            "q" | "Q" => input::keyboard::Key::Q,
90            "r" | "R" => input::keyboard::Key::R,
91            "s" | "S" => input::keyboard::Key::S,
92            "t" | "T" => input::keyboard::Key::T,
93            "u" | "U" => input::keyboard::Key::U,
94            "v" | "V" => input::keyboard::Key::V,
95            "w" | "W" => input::keyboard::Key::W,
96            "x" | "X" => input::keyboard::Key::X,
97            "y" | "Y" => input::keyboard::Key::Y,
98            "z" | "Z" => input::keyboard::Key::Z,
99            "*" => input::keyboard::Key::Asterisk,
100            "\\" => input::keyboard::Key::Backslash,
101            "^" => input::keyboard::Key::Caret,
102            ":" => input::keyboard::Key::Colon,
103            "," => input::keyboard::Key::Comma,
104            "=" => input::keyboard::Key::Equals,
105            "-" => input::keyboard::Key::Minus,
106            "+" => input::keyboard::Key::Plus,
107            ";" => input::keyboard::Key::Semicolon,
108            "/" => input::keyboard::Key::Slash,
109            " " => input::keyboard::Key::Space,
110            "_" => input::keyboard::Key::Underscore,
111            _ => input::keyboard::Key::Unknown,
112        },
113        winit::keyboard::Key::Unidentified(_) | winit::keyboard::Key::Dead(_) => {
114            input::keyboard::Key::Unknown
115        },
116    }
117}
118
119fn conrod_convert_mouse_button(button: &event::MouseButton) -> input::Button {
120    input::Button::Mouse(match button {
121        event::MouseButton::Left => input::MouseButton::Left,
122        event::MouseButton::Right => input::MouseButton::Right,
123        event::MouseButton::Middle => input::MouseButton::Middle,
124        event::MouseButton::Other(0) => input::MouseButton::X1,
125        event::MouseButton::Other(1) => input::MouseButton::X2,
126        event::MouseButton::Other(2) => input::MouseButton::Button6,
127        event::MouseButton::Other(3) => input::MouseButton::Button7,
128        event::MouseButton::Other(4) => input::MouseButton::Button8,
129        _ => input::MouseButton::Unknown,
130    })
131}
132
133fn conrod_convert_event(
134    event: &WindowEvent,
135    window: &winit::window::Window,
136    modifiers: winit::keyboard::ModifiersState,
137) -> Option<Input> {
138    let hidpi = window.scale_factor();
139    let winit::dpi::LogicalSize { width, height } = window.inner_size().to_logical::<f64>(hidpi);
140    let tx = |x: f64| x - width / 2.0;
141    let ty = |y: f64| -(y - height / 2.0);
142
143    Some(match event {
144        WindowEvent::Resized(physical_size) => {
145            let winit::dpi::LogicalSize { width, height } = physical_size.to_logical::<f64>(hidpi);
146            Input::Resize(width as _, height as _)
147        },
148        WindowEvent::Focused(focused) => Input::Focus(*focused),
149        WindowEvent::KeyboardInput { event, .. } => {
150            // `conrod` expects different events for text input and pressed keys.
151            // We work around that by sending the key as text but only if no modifier is
152            // pressed, so shortcuts still work.
153            if let Some(text) = &event.text
154                && let Some(c) = text.chars().next()
155                && !c.is_control()
156                && !modifiers.alt_key()
157                && !modifiers.control_key()
158                && !modifiers.super_key()
159            {
160                return event.state.is_pressed().then(|| Input::Text(c.to_string()));
161            }
162
163            let key = input::Button::Keyboard(conrod_convert_key(&event.logical_key));
164
165            match event.state {
166                event::ElementState::Pressed => Input::Press(key),
167                event::ElementState::Released => Input::Release(key),
168            }
169        },
170        WindowEvent::Touch(event::Touch {
171            phase,
172            location,
173            id,
174            ..
175        }) => {
176            let winit::dpi::LogicalPosition { x, y } = location.to_logical::<f64>(hidpi);
177            let phase = match phase {
178                event::TouchPhase::Started => input::touch::Phase::Start,
179                event::TouchPhase::Moved => input::touch::Phase::Move,
180                event::TouchPhase::Cancelled => input::touch::Phase::Cancel,
181                event::TouchPhase::Ended => input::touch::Phase::End,
182            };
183            let xy = [tx(x), ty(y)];
184            let id = input::touch::Id::new(*id);
185            let touch = input::Touch { phase, id, xy };
186            Input::Touch(touch)
187        },
188        WindowEvent::CursorMoved { position, .. } => {
189            let winit::dpi::LogicalPosition { x, y } = position.to_logical::<f64>(hidpi);
190            let x = tx(x);
191            let y = ty(y);
192            let motion = input::Motion::MouseCursor { x, y };
193            Input::Motion(motion)
194        },
195        WindowEvent::MouseWheel { delta, .. } => match delta {
196            event::MouseScrollDelta::PixelDelta(physical_position) => {
197                let winit::dpi::LogicalPosition { x, y } =
198                    physical_position.to_logical::<f64>(hidpi);
199                let x = x as conrod_core::Scalar;
200                let y = -y as conrod_core::Scalar;
201                let motion = input::Motion::Scroll { x, y };
202                Input::Motion(motion)
203            },
204            event::MouseScrollDelta::LineDelta(x, y) => {
205                const ARBITRARY_POINTS_PER_LINE_FACTOR: conrod_core::Scalar = 10.0;
206                let x = ARBITRARY_POINTS_PER_LINE_FACTOR * *x as conrod_core::Scalar;
207                let y = ARBITRARY_POINTS_PER_LINE_FACTOR * -y as conrod_core::Scalar;
208                Input::Motion(input::Motion::Scroll { x, y })
209            },
210        },
211        WindowEvent::MouseInput { state, button, .. } => {
212            let button = conrod_convert_mouse_button(button);
213            match state {
214                event::ElementState::Pressed => Input::Press(button),
215                event::ElementState::Released => Input::Release(button),
216            }
217        },
218        WindowEvent::RedrawRequested => Input::Redraw,
219        _ => return None,
220    })
221}
222
223#[derive(Clone, Debug)]
224pub struct Event(pub Input);
225
226impl Event {
227    pub fn try_from(
228        event: &event::Event<()>,
229        window: &winit::window::Window,
230        modifiers: winit::keyboard::ModifiersState,
231    ) -> Option<Self> {
232        match event {
233            event::Event::WindowEvent { event, .. } => {
234                conrod_convert_event(event, window, modifiers).map(Self)
235            },
236            _ => None,
237        }
238    }
239
240    pub fn is_keyboard_or_mouse(&self) -> bool {
241        matches!(
242            self.0,
243            Input::Press(_)
244                | Input::Release(_)
245                | Input::Motion(_)
246                | Input::Touch(_)
247                | Input::Text(_)
248        )
249    }
250
251    pub fn is_keyboard(&self) -> bool {
252        matches!(
253            self.0,
254            Input::Press(Button::Keyboard(_))
255                | Input::Release(Button::Keyboard(_))
256                | Input::Text(_)
257        )
258    }
259
260    pub fn new_resize(dims: Vec2<f64>) -> Self { Self(Input::Resize(dims.x, dims.y)) }
261}