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            && let Err(error) = clipboard.write(contents)
29        {
30            tracing::warn!("error writing to clipboard: {}", error)
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                && let Some(c) = text.chars().next()
100                && !c.is_control()
101                && !modifiers.alt
102                && !modifiers.control
103                && !modifiers.logo
104            {
105                return event
106                    .state
107                    .is_pressed()
108                    .then_some(Event::Keyboard(keyboard::Event::CharacterReceived(c)));
109            }
110
111            let key_code = key_code(&event.logical_key)?;
112            match event.state {
113                winit::event::ElementState::Pressed => keyboard::Event::KeyPressed {
114                    key_code,
115                    modifiers,
116                },
117                winit::event::ElementState::Released => keyboard::Event::KeyReleased {
118                    key_code,
119                    modifiers,
120                },
121            }
122        })),
123        WindowEvent::ModifiersChanged(new_modifiers) => Some(Event::Keyboard(
124            keyboard::Event::ModifiersChanged(self::modifiers(new_modifiers.state())),
125        )),
126        WindowEvent::Focused(focused) => Some(Event::Window(if *focused {
127            window::Event::Focused
128        } else {
129            window::Event::Unfocused
130        })),
131        WindowEvent::HoveredFile(path) => {
132            Some(Event::Window(window::Event::FileHovered(path.clone())))
133        },
134        WindowEvent::DroppedFile(path) => {
135            Some(Event::Window(window::Event::FileDropped(path.clone())))
136        },
137        WindowEvent::HoveredFileCancelled => Some(Event::Window(window::Event::FilesHoveredLeft)),
138        WindowEvent::Touch(touch) => Some(Event::Touch(touch_event(*touch, scale_factor))),
139        _ => None,
140    }
141}
142
143/// Converts a `MouseButton` from [`winit`] to an [`iced`] mouse button.
144pub fn mouse_button(mouse_button: winit::event::MouseButton) -> Option<mouse::Button> {
145    Some(match mouse_button {
146        winit::event::MouseButton::Left => mouse::Button::Left,
147        winit::event::MouseButton::Right => mouse::Button::Right,
148        winit::event::MouseButton::Middle => mouse::Button::Middle,
149        winit::event::MouseButton::Other(other) => mouse::Button::Other(other as u8),
150        winit::event::MouseButton::Back | winit::event::MouseButton::Forward => return None,
151    })
152}
153
154/// Converts some `ModifiersState` from [`winit`] to an [`iced`]
155/// modifiers state.
156pub fn modifiers(modifiers: winit::keyboard::ModifiersState) -> keyboard::Modifiers {
157    keyboard::Modifiers {
158        shift: modifiers.shift_key(),
159        control: modifiers.control_key(),
160        alt: modifiers.alt_key(),
161        logo: modifiers.super_key(),
162    }
163}
164
165/// Converts a `Touch` from [`winit`] to an [`iced`] touch event.
166pub fn touch_event(touch: winit::event::Touch, scale_factor: f64) -> touch::Event {
167    let id = touch::Finger(touch.id);
168    let position = {
169        let location = touch.location.to_logical::<f64>(scale_factor);
170
171        Point::new(location.x as f32, location.y as f32)
172    };
173
174    match touch.phase {
175        winit::event::TouchPhase::Started => touch::Event::FingerPressed { id, position },
176        winit::event::TouchPhase::Moved => touch::Event::FingerMoved { id, position },
177        winit::event::TouchPhase::Ended => touch::Event::FingerLifted { id, position },
178        winit::event::TouchPhase::Cancelled => touch::Event::FingerLost { id, position },
179    }
180}
181
182/// Converts a `VirtualKeyCode` from [`winit`] to an [`iced`] key code.
183pub fn key_code(key: &winit::keyboard::Key) -> Option<keyboard::KeyCode> {
184    use keyboard::KeyCode;
185
186    Some(match key {
187        winit::keyboard::Key::Named(key) => match key {
188            NamedKey::Escape => KeyCode::Escape,
189            NamedKey::F1 => KeyCode::F1,
190            NamedKey::F2 => KeyCode::F2,
191            NamedKey::F3 => KeyCode::F3,
192            NamedKey::F4 => KeyCode::F4,
193            NamedKey::F5 => KeyCode::F5,
194            NamedKey::F6 => KeyCode::F6,
195            NamedKey::F7 => KeyCode::F7,
196            NamedKey::F8 => KeyCode::F8,
197            NamedKey::F9 => KeyCode::F9,
198            NamedKey::F10 => KeyCode::F10,
199            NamedKey::F11 => KeyCode::F11,
200            NamedKey::F12 => KeyCode::F12,
201            NamedKey::F13 => KeyCode::F13,
202            NamedKey::F14 => KeyCode::F14,
203            NamedKey::F15 => KeyCode::F15,
204            NamedKey::F16 => KeyCode::F16,
205            NamedKey::F17 => KeyCode::F17,
206            NamedKey::F18 => KeyCode::F18,
207            NamedKey::F19 => KeyCode::F19,
208            NamedKey::F20 => KeyCode::F20,
209            NamedKey::F21 => KeyCode::F21,
210            NamedKey::F22 => KeyCode::F22,
211            NamedKey::F23 => KeyCode::F23,
212            NamedKey::F24 => KeyCode::F24,
213            NamedKey::ScrollLock => KeyCode::Scroll,
214            NamedKey::Pause => KeyCode::Pause,
215            NamedKey::Insert => KeyCode::Insert,
216            NamedKey::Home => KeyCode::Home,
217            NamedKey::Delete => KeyCode::Delete,
218            NamedKey::End => KeyCode::End,
219            NamedKey::PageDown => KeyCode::PageDown,
220            NamedKey::PageUp => KeyCode::PageUp,
221            NamedKey::ArrowLeft => KeyCode::Left,
222            NamedKey::ArrowUp => KeyCode::Up,
223            NamedKey::ArrowRight => KeyCode::Right,
224            NamedKey::ArrowDown => KeyCode::Down,
225            NamedKey::Backspace => KeyCode::Backspace,
226            NamedKey::Enter => KeyCode::Enter,
227            NamedKey::Space => KeyCode::Space,
228            NamedKey::Compose => KeyCode::Compose,
229            NamedKey::NumLock => KeyCode::Numlock,
230            NamedKey::Convert => KeyCode::Convert,
231            NamedKey::KanaMode => KeyCode::Kana,
232            NamedKey::KanjiMode => KeyCode::Kanji,
233            NamedKey::MediaStop => KeyCode::MediaStop,
234            NamedKey::AudioVolumeMute => KeyCode::Mute,
235            NamedKey::MediaTrackNext => KeyCode::NextTrack,
236            NamedKey::NonConvert => KeyCode::NoConvert,
237            NamedKey::MediaPlayPause => KeyCode::PlayPause,
238            NamedKey::Power => KeyCode::Power,
239            NamedKey::MediaTrackPrevious => KeyCode::PrevTrack,
240            NamedKey::Tab => KeyCode::Tab,
241            NamedKey::AudioVolumeDown => KeyCode::VolumeDown,
242            NamedKey::AudioVolumeUp => KeyCode::VolumeUp,
243            NamedKey::WakeUp => KeyCode::Wake,
244            NamedKey::Copy => KeyCode::Copy,
245            NamedKey::Paste => KeyCode::Paste,
246            NamedKey::Cut => KeyCode::Cut,
247            _ => return None,
248        },
249        winit::keyboard::Key::Character(c) => match c.as_str() {
250            "a" | "A" => KeyCode::A,
251            "b" | "B" => KeyCode::B,
252            "c" | "C" => KeyCode::C,
253            "d" | "D" => KeyCode::D,
254            "e" | "E" => KeyCode::E,
255            "f" | "F" => KeyCode::F,
256            "g" | "G" => KeyCode::G,
257            "h" | "H" => KeyCode::H,
258            "i" | "I" => KeyCode::I,
259            "j" | "J" => KeyCode::J,
260            "k" | "K" => KeyCode::K,
261            "l" | "L" => KeyCode::L,
262            "m" | "M" => KeyCode::M,
263            "n" | "N" => KeyCode::N,
264            "o" | "O" => KeyCode::O,
265            "p" | "P" => KeyCode::P,
266            "q" | "Q" => KeyCode::Q,
267            "r" | "R" => KeyCode::R,
268            "s" | "S" => KeyCode::S,
269            "t" | "T" => KeyCode::T,
270            "u" | "U" => KeyCode::U,
271            "v" | "V" => KeyCode::V,
272            "w" | "W" => KeyCode::W,
273            "x" | "X" => KeyCode::X,
274            "y" | "Y" => KeyCode::Y,
275            "z" | "Z" => KeyCode::Z,
276            "0" => KeyCode::Key0,
277            "1" => KeyCode::Key1,
278            "2" => KeyCode::Key2,
279            "3" => KeyCode::Key3,
280            "4" => KeyCode::Key4,
281            "5" => KeyCode::Key5,
282            "6" => KeyCode::Key6,
283            "7" => KeyCode::Key7,
284            "8" => KeyCode::Key8,
285            "9" => KeyCode::Key9,
286            "'" => KeyCode::Apostrophe,
287            "*" => KeyCode::Asterisk,
288            "\\" => KeyCode::Backslash,
289            "^" => KeyCode::Caret,
290            ":" => KeyCode::Colon,
291            "," => KeyCode::Comma,
292            "=" => KeyCode::Equals,
293            "-" => KeyCode::Minus,
294            "." => KeyCode::Period,
295            "+" => KeyCode::Plus,
296            ";" => KeyCode::Semicolon,
297            "/" => KeyCode::Slash,
298            " " => KeyCode::Space,
299            "_" => KeyCode::Underline,
300            _ => return None,
301        },
302        winit::keyboard::Key::Unidentified(_) | winit::keyboard::Key::Dead(_) => return None,
303    })
304}