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                if let Some(c) = text.chars().next() {
155                    if !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            }
164
165            let key = input::Button::Keyboard(conrod_convert_key(&event.logical_key));
166
167            match event.state {
168                event::ElementState::Pressed => Input::Press(key),
169                event::ElementState::Released => Input::Release(key),
170            }
171        },
172        WindowEvent::Touch(event::Touch {
173            phase,
174            location,
175            id,
176            ..
177        }) => {
178            let winit::dpi::LogicalPosition { x, y } = location.to_logical::<f64>(hidpi);
179            let phase = match phase {
180                event::TouchPhase::Started => input::touch::Phase::Start,
181                event::TouchPhase::Moved => input::touch::Phase::Move,
182                event::TouchPhase::Cancelled => input::touch::Phase::Cancel,
183                event::TouchPhase::Ended => input::touch::Phase::End,
184            };
185            let xy = [tx(x), ty(y)];
186            let id = input::touch::Id::new(*id);
187            let touch = input::Touch { phase, id, xy };
188            Input::Touch(touch)
189        },
190        WindowEvent::CursorMoved { position, .. } => {
191            let winit::dpi::LogicalPosition { x, y } = position.to_logical::<f64>(hidpi);
192            let x = tx(x);
193            let y = ty(y);
194            let motion = input::Motion::MouseCursor { x, y };
195            Input::Motion(motion)
196        },
197        WindowEvent::MouseWheel { delta, .. } => match delta {
198            event::MouseScrollDelta::PixelDelta(physical_position) => {
199                let winit::dpi::LogicalPosition { x, y } =
200                    physical_position.to_logical::<f64>(hidpi);
201                let x = x as conrod_core::Scalar;
202                let y = -y as conrod_core::Scalar;
203                let motion = input::Motion::Scroll { x, y };
204                Input::Motion(motion)
205            },
206            event::MouseScrollDelta::LineDelta(x, y) => {
207                const ARBITRARY_POINTS_PER_LINE_FACTOR: conrod_core::Scalar = 10.0;
208                let x = ARBITRARY_POINTS_PER_LINE_FACTOR * *x as conrod_core::Scalar;
209                let y = ARBITRARY_POINTS_PER_LINE_FACTOR * -y as conrod_core::Scalar;
210                Input::Motion(input::Motion::Scroll { x, y })
211            },
212        },
213        WindowEvent::MouseInput { state, button, .. } => {
214            let button = conrod_convert_mouse_button(button);
215            match state {
216                event::ElementState::Pressed => Input::Press(button),
217                event::ElementState::Released => Input::Release(button),
218            }
219        },
220        WindowEvent::RedrawRequested => Input::Redraw,
221        _ => return None,
222    })
223}
224
225#[derive(Clone, Debug)]
226pub struct Event(pub Input);
227
228impl Event {
229    pub fn try_from(
230        event: &event::Event<()>,
231        window: &winit::window::Window,
232        modifiers: winit::keyboard::ModifiersState,
233    ) -> Option<Self> {
234        match event {
235            event::Event::WindowEvent { event, .. } => {
236                conrod_convert_event(event, window, modifiers).map(Self)
237            },
238            _ => None,
239        }
240    }
241
242    pub fn is_keyboard_or_mouse(&self) -> bool {
243        matches!(
244            self.0,
245            Input::Press(_)
246                | Input::Release(_)
247                | Input::Motion(_)
248                | Input::Touch(_)
249                | Input::Text(_)
250        )
251    }
252
253    pub fn is_keyboard(&self) -> bool {
254        matches!(
255            self.0,
256            Input::Press(Button::Keyboard(_))
257                | Input::Release(Button::Keyboard(_))
258                | Input::Text(_)
259        )
260    }
261
262    pub fn new_resize(dims: Vec2<f64>) -> Self { Self(Input::Resize(dims.x, dims.y)) }
263}