veloren_voxygen/ui/ice/
winit.rs

1// Copied and adapted from `iced_winit` (MIT licensed)
2// Original version at https://github.com/Imberflur/iced/tree/veloren-winit-0.28/winit
3
4use iced::{Event, Point, keyboard, mouse, touch, window};
5use winit::{event::WindowEvent, keyboard::NamedKey};
6
7/// A buffer for short-term storage and transfer within and between
8/// applications.
9pub struct Clipboard {
10    connection: Option<window_clipboard::Clipboard>,
11}
12
13impl Clipboard {
14    /// Creates a new [`Clipboard`] for the given window.
15    pub fn connect(window: &winit::window::Window) -> Clipboard {
16        #[expect(unsafe_code)]
17        let connection = unsafe { window_clipboard::Clipboard::connect(window) }.ok();
18
19        Clipboard { connection }
20    }
21
22    /// Reads the current content of the [`Clipboard`] as text.
23    pub fn read(&self) -> Option<String> { self.connection.as_ref()?.read().ok() }
24
25    /// Writes the given text contents to the [`Clipboard`].
26    pub fn write(&mut self, contents: String) {
27        if let Some(clipboard) = &mut self.connection {
28            if let Err(error) = clipboard.write(contents) {
29                tracing::warn!("error writing to clipboard: {}", error)
30            }
31        }
32    }
33}
34
35impl iced::Clipboard for Clipboard {
36    fn read(&self) -> Option<String> { self.read() }
37
38    fn write(&mut self, contents: String) { self.write(contents) }
39}
40
41/// Converts a winit window event into an iced event.
42pub fn window_event(
43    event: &WindowEvent,
44    scale_factor: f64,
45    modifiers: winit::keyboard::ModifiersState,
46) -> Option<Event> {
47    match event {
48        WindowEvent::Resized(new_size) => {
49            let logical_size = new_size.to_logical(scale_factor);
50
51            Some(Event::Window(window::Event::Resized {
52                width: logical_size.width,
53                height: logical_size.height,
54            }))
55        },
56        WindowEvent::CloseRequested => Some(Event::Window(window::Event::CloseRequested)),
57        WindowEvent::CursorMoved { position, .. } => {
58            let position = position.to_logical::<f64>(scale_factor);
59
60            Some(Event::Mouse(mouse::Event::CursorMoved {
61                position: Point::new(position.x as f32, position.y as f32),
62            }))
63        },
64        WindowEvent::CursorEntered { .. } => Some(Event::Mouse(mouse::Event::CursorEntered)),
65        WindowEvent::CursorLeft { .. } => Some(Event::Mouse(mouse::Event::CursorLeft)),
66        WindowEvent::MouseInput { button, state, .. } => {
67            let button = mouse_button(*button)?;
68
69            Some(Event::Mouse(match state {
70                winit::event::ElementState::Pressed => mouse::Event::ButtonPressed(button),
71                winit::event::ElementState::Released => mouse::Event::ButtonReleased(button),
72            }))
73        },
74        WindowEvent::MouseWheel { delta, .. } => match delta {
75            winit::event::MouseScrollDelta::LineDelta(delta_x, delta_y) => {
76                Some(Event::Mouse(mouse::Event::WheelScrolled {
77                    delta: mouse::ScrollDelta::Lines {
78                        x: *delta_x,
79                        y: *delta_y,
80                    },
81                }))
82            },
83            winit::event::MouseScrollDelta::PixelDelta(position) => {
84                Some(Event::Mouse(mouse::Event::WheelScrolled {
85                    delta: mouse::ScrollDelta::Pixels {
86                        x: position.x as f32,
87                        y: position.y as f32,
88                    },
89                }))
90            },
91        },
92        WindowEvent::KeyboardInput { event, .. } => Some(Event::Keyboard({
93            let modifiers = self::modifiers(modifiers);
94
95            // `iced` expects different events for text input and pressed keys.
96            // We work around that by sending the key as text but only if no modifier is
97            // pressed, so shortcuts still work.
98            if let Some(text) = &event.text {
99                if let Some(c) = text.chars().next() {
100                    if !c.is_control() && !modifiers.alt && !modifiers.control && !modifiers.logo {
101                        return event
102                            .state
103                            .is_pressed()
104                            .then_some(Event::Keyboard(keyboard::Event::CharacterReceived(c)));
105                    }
106                }
107            }
108
109            let key_code = key_code(&event.logical_key)?;
110            match event.state {
111                winit::event::ElementState::Pressed => keyboard::Event::KeyPressed {
112                    key_code,
113                    modifiers,
114                },
115                winit::event::ElementState::Released => keyboard::Event::KeyReleased {
116                    key_code,
117                    modifiers,
118                },
119            }
120        })),
121        WindowEvent::ModifiersChanged(new_modifiers) => Some(Event::Keyboard(
122            keyboard::Event::ModifiersChanged(self::modifiers(new_modifiers.state())),
123        )),
124        WindowEvent::Focused(focused) => Some(Event::Window(if *focused {
125            window::Event::Focused
126        } else {
127            window::Event::Unfocused
128        })),
129        WindowEvent::HoveredFile(path) => {
130            Some(Event::Window(window::Event::FileHovered(path.clone())))
131        },
132        WindowEvent::DroppedFile(path) => {
133            Some(Event::Window(window::Event::FileDropped(path.clone())))
134        },
135        WindowEvent::HoveredFileCancelled => Some(Event::Window(window::Event::FilesHoveredLeft)),
136        WindowEvent::Touch(touch) => Some(Event::Touch(touch_event(*touch, scale_factor))),
137        _ => None,
138    }
139}
140
141/// Converts a `MouseButton` from [`winit`] to an [`iced`] mouse button.
142pub fn mouse_button(mouse_button: winit::event::MouseButton) -> Option<mouse::Button> {
143    Some(match mouse_button {
144        winit::event::MouseButton::Left => mouse::Button::Left,
145        winit::event::MouseButton::Right => mouse::Button::Right,
146        winit::event::MouseButton::Middle => mouse::Button::Middle,
147        winit::event::MouseButton::Other(other) => mouse::Button::Other(other as u8),
148        winit::event::MouseButton::Back | winit::event::MouseButton::Forward => return None,
149    })
150}
151
152/// Converts some `ModifiersState` from [`winit`] to an [`iced`]
153/// modifiers state.
154pub fn modifiers(modifiers: winit::keyboard::ModifiersState) -> keyboard::Modifiers {
155    keyboard::Modifiers {
156        shift: modifiers.shift_key(),
157        control: modifiers.control_key(),
158        alt: modifiers.alt_key(),
159        logo: modifiers.super_key(),
160    }
161}
162
163/// Converts a `Touch` from [`winit`] to an [`iced`] touch event.
164pub fn touch_event(touch: winit::event::Touch, scale_factor: f64) -> touch::Event {
165    let id = touch::Finger(touch.id);
166    let position = {
167        let location = touch.location.to_logical::<f64>(scale_factor);
168
169        Point::new(location.x as f32, location.y as f32)
170    };
171
172    match touch.phase {
173        winit::event::TouchPhase::Started => touch::Event::FingerPressed { id, position },
174        winit::event::TouchPhase::Moved => touch::Event::FingerMoved { id, position },
175        winit::event::TouchPhase::Ended => touch::Event::FingerLifted { id, position },
176        winit::event::TouchPhase::Cancelled => touch::Event::FingerLost { id, position },
177    }
178}
179
180/// Converts a `VirtualKeyCode` from [`winit`] to an [`iced`] key code.
181pub fn key_code(key: &winit::keyboard::Key) -> Option<keyboard::KeyCode> {
182    use keyboard::KeyCode;
183
184    Some(match key {
185        winit::keyboard::Key::Named(key) => match key {
186            NamedKey::Escape => KeyCode::Escape,
187            NamedKey::F1 => KeyCode::F1,
188            NamedKey::F2 => KeyCode::F2,
189            NamedKey::F3 => KeyCode::F3,
190            NamedKey::F4 => KeyCode::F4,
191            NamedKey::F5 => KeyCode::F5,
192            NamedKey::F6 => KeyCode::F6,
193            NamedKey::F7 => KeyCode::F7,
194            NamedKey::F8 => KeyCode::F8,
195            NamedKey::F9 => KeyCode::F9,
196            NamedKey::F10 => KeyCode::F10,
197            NamedKey::F11 => KeyCode::F11,
198            NamedKey::F12 => KeyCode::F12,
199            NamedKey::F13 => KeyCode::F13,
200            NamedKey::F14 => KeyCode::F14,
201            NamedKey::F15 => KeyCode::F15,
202            NamedKey::F16 => KeyCode::F16,
203            NamedKey::F17 => KeyCode::F17,
204            NamedKey::F18 => KeyCode::F18,
205            NamedKey::F19 => KeyCode::F19,
206            NamedKey::F20 => KeyCode::F20,
207            NamedKey::F21 => KeyCode::F21,
208            NamedKey::F22 => KeyCode::F22,
209            NamedKey::F23 => KeyCode::F23,
210            NamedKey::F24 => KeyCode::F24,
211            NamedKey::ScrollLock => KeyCode::Scroll,
212            NamedKey::Pause => KeyCode::Pause,
213            NamedKey::Insert => KeyCode::Insert,
214            NamedKey::Home => KeyCode::Home,
215            NamedKey::Delete => KeyCode::Delete,
216            NamedKey::End => KeyCode::End,
217            NamedKey::PageDown => KeyCode::PageDown,
218            NamedKey::PageUp => KeyCode::PageUp,
219            NamedKey::ArrowLeft => KeyCode::Left,
220            NamedKey::ArrowUp => KeyCode::Up,
221            NamedKey::ArrowRight => KeyCode::Right,
222            NamedKey::ArrowDown => KeyCode::Down,
223            NamedKey::Backspace => KeyCode::Backspace,
224            NamedKey::Enter => KeyCode::Enter,
225            NamedKey::Space => KeyCode::Space,
226            NamedKey::Compose => KeyCode::Compose,
227            NamedKey::NumLock => KeyCode::Numlock,
228            NamedKey::Convert => KeyCode::Convert,
229            NamedKey::KanaMode => KeyCode::Kana,
230            NamedKey::KanjiMode => KeyCode::Kanji,
231            NamedKey::MediaStop => KeyCode::MediaStop,
232            NamedKey::AudioVolumeMute => KeyCode::Mute,
233            NamedKey::MediaTrackNext => KeyCode::NextTrack,
234            NamedKey::NonConvert => KeyCode::NoConvert,
235            NamedKey::MediaPlayPause => KeyCode::PlayPause,
236            NamedKey::Power => KeyCode::Power,
237            NamedKey::MediaTrackPrevious => KeyCode::PrevTrack,
238            NamedKey::Tab => KeyCode::Tab,
239            NamedKey::AudioVolumeDown => KeyCode::VolumeDown,
240            NamedKey::AudioVolumeUp => KeyCode::VolumeUp,
241            NamedKey::WakeUp => KeyCode::Wake,
242            NamedKey::Copy => KeyCode::Copy,
243            NamedKey::Paste => KeyCode::Paste,
244            NamedKey::Cut => KeyCode::Cut,
245            _ => return None,
246        },
247        winit::keyboard::Key::Character(c) => match c.as_str() {
248            "a" | "A" => KeyCode::A,
249            "b" | "B" => KeyCode::B,
250            "c" | "C" => KeyCode::C,
251            "d" | "D" => KeyCode::D,
252            "e" | "E" => KeyCode::E,
253            "f" | "F" => KeyCode::F,
254            "g" | "G" => KeyCode::G,
255            "h" | "H" => KeyCode::H,
256            "i" | "I" => KeyCode::I,
257            "j" | "J" => KeyCode::J,
258            "k" | "K" => KeyCode::K,
259            "l" | "L" => KeyCode::L,
260            "m" | "M" => KeyCode::M,
261            "n" | "N" => KeyCode::N,
262            "o" | "O" => KeyCode::O,
263            "p" | "P" => KeyCode::P,
264            "q" | "Q" => KeyCode::Q,
265            "r" | "R" => KeyCode::R,
266            "s" | "S" => KeyCode::S,
267            "t" | "T" => KeyCode::T,
268            "u" | "U" => KeyCode::U,
269            "v" | "V" => KeyCode::V,
270            "w" | "W" => KeyCode::W,
271            "x" | "X" => KeyCode::X,
272            "y" | "Y" => KeyCode::Y,
273            "z" | "Z" => KeyCode::Z,
274            "0" => KeyCode::Key0,
275            "1" => KeyCode::Key1,
276            "2" => KeyCode::Key2,
277            "3" => KeyCode::Key3,
278            "4" => KeyCode::Key4,
279            "5" => KeyCode::Key5,
280            "6" => KeyCode::Key6,
281            "7" => KeyCode::Key7,
282            "8" => KeyCode::Key8,
283            "9" => KeyCode::Key9,
284            "'" => KeyCode::Apostrophe,
285            "*" => KeyCode::Asterisk,
286            "\\" => KeyCode::Backslash,
287            "^" => KeyCode::Caret,
288            ":" => KeyCode::Colon,
289            "," => KeyCode::Comma,
290            "=" => KeyCode::Equals,
291            "-" => KeyCode::Minus,
292            "." => KeyCode::Period,
293            "+" => KeyCode::Plus,
294            ";" => KeyCode::Semicolon,
295            "/" => KeyCode::Slash,
296            " " => KeyCode::Space,
297            "_" => KeyCode::Underline,
298            _ => return None,
299        },
300        winit::keyboard::Key::Unidentified(_) | winit::keyboard::Key::Dead(_) => return None,
301    })
302}