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 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
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 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
141pub 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
152pub 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
163pub 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
180pub 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}