1use iced::{Event, Point, keyboard, mouse, touch, window};
5use winit::{event::WindowEvent, keyboard::NamedKey};
6
7pub struct Clipboard {
10 connection: Option<window_clipboard::Clipboard>,
11}
12
13impl Clipboard {
14 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 pub fn read(&self) -> Option<String> { self.connection.as_ref()?.read().ok() }
24
25 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
41pub 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 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
143pub 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
154pub 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
165pub 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
182pub 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}