1#![warn(clippy::all)]
58#![allow(clippy::single_match)]
59
60pub use egui;
62
63use crate::*;
64
65use egui::*;
66
67#[cfg(not(feature = "clipboard"))]
68
69use super::clipboard::{
70 ClipboardContext, ClipboardProvider,
72};
73
74pub struct EguiInputState {
75 pub pointer_pos: Pos2,
76 pub clipboard: Option<ClipboardContext>,
77 pub input: RawInput,
78 pub modifiers: Modifiers,
79}
80
81impl EguiInputState {
82 pub fn new(input: RawInput) -> Self {
83 EguiInputState {
84 pointer_pos: Pos2::new(0f32, 0f32),
85 clipboard: init_clipboard(),
86 input,
87 modifiers: Modifiers::default(),
88 }
89 }
90}
91
92pub fn handle_event(event: glfw::WindowEvent, state: &mut EguiInputState) {
93 use glfw::WindowEvent::*;
94
95 match event {
96 FramebufferSize(width, height) => {
97 state.input.screen_rect = Some(epaint::emath::Rect::from_min_size(
98 Pos2::new(0f32, 0f32),
99 egui::vec2(width as f32, height as f32) / state.input.pixels_per_point.unwrap(),
100 ))
101 }
102
103 MouseButton (mouse_btn, glfw::Action::Press, _) => state.input.events.push(egui::Event::PointerButton {
104 pos: state.pointer_pos,
105 button: match mouse_btn {
106 glfw::MouseButtonLeft => egui::PointerButton::Primary,
107 glfw::MouseButtonRight => egui::PointerButton::Secondary,
108 glfw::MouseButtonMiddle => egui::PointerButton::Middle,
109 _ => unreachable!(),
110 },
111 pressed: true,
112 modifiers: state.modifiers,
113 }),
114
115 MouseButton (mouse_btn, glfw::Action::Release, _) => state.input.events.push(egui::Event::PointerButton {
116 pos: state.pointer_pos,
117 button: match mouse_btn {
118 glfw::MouseButtonLeft => egui::PointerButton::Primary,
119 glfw::MouseButtonRight => egui::PointerButton::Secondary,
120 glfw::MouseButtonMiddle => egui::PointerButton::Middle,
121 _ => unreachable!(),
122 },
123 pressed: false,
124 modifiers: state.modifiers,
125 }),
126
127 CursorPos(x, y) => {
128 state.pointer_pos = pos2(
129 x as f32 / state.input.pixels_per_point.unwrap(),
130 y as f32 / state.input.pixels_per_point.unwrap(),
131 );
132 state
133 .input
134 .events
135 .push(egui::Event::PointerMoved(state.pointer_pos))
136 }
137
138 Key(keycode, _scancode, glfw::Action::Release, keymod) => {
139 use glfw::Modifiers as Mod;
140 if let Some(key) = translate_virtual_key_code(keycode) {
141 state.modifiers = Modifiers {
142 alt: (keymod & Mod::Alt == Mod::Alt),
143 ctrl: (keymod & Mod::Control == Mod::Control),
144 shift: (keymod & Mod::Shift == Mod::Shift),
145
146 command: (keymod & Mod::Control == Mod::Control),
149
150 ..Default::default()
151 };
152
153 state.input.events.push(Event::Key {
154 key,
155 pressed: false,
156 modifiers: state.modifiers,
157 });
158 }
159 }
160
161 Key(keycode, _scancode, glfw::Action::Press | glfw::Action::Repeat, keymod) => {
162 use glfw::Modifiers as Mod;
163 if let Some(key) = translate_virtual_key_code(keycode) {
164 state.modifiers = Modifiers {
165 alt: (keymod & Mod::Alt == Mod::Alt),
166 ctrl: (keymod & Mod::Control == Mod::Control),
167 shift: (keymod & Mod::Shift == Mod::Shift),
168
169 command: (keymod & Mod::Control == Mod::Control),
172
173 ..Default::default()
174 };
175
176 if state.modifiers.command && key == egui::Key::X {
177 state.input.events.push(egui::Event::Cut);
178 } else if state.modifiers.command && key == egui::Key::C {
179 state.input.events.push(egui::Event::Copy);
180 } else if state.modifiers.command && key == egui::Key::V {
181 if let Some(clipboard_ctx) = state.clipboard.as_mut() {
182 state.input.events.push(egui::Event::Text(clipboard_ctx.get_contents().unwrap_or("".to_string())));
183 }
184 } else {
185 state.input.events.push(Event::Key {
186 key,
187 pressed: true,
188 modifiers: state.modifiers,
189 });
190 }
191 }
192 }
193
194 Char(c) => {
195 state.input.events.push(Event::Text(c.to_string()));
196 }
197
198 Scroll (x, y) => {
199 state.input.scroll_delta = vec2(x as f32, y as f32);
200 }
201
202 _ => {}
203 }
204}
205
206pub fn translate_virtual_key_code(key: glfw::Key) -> Option<egui::Key> {
207
208 Some(match key {
209 glfw::Key::Left => egui::Key::ArrowLeft,
210 glfw::Key::Up => egui::Key::ArrowUp,
211 glfw::Key::Right => egui::Key::ArrowRight,
212 glfw::Key::Down => egui::Key::ArrowDown,
213
214 glfw::Key::Escape => egui::Key::Escape,
215 glfw::Key::Tab => egui::Key::Tab,
216 glfw::Key::Backspace => egui::Key::Backspace,
217 glfw::Key::Space => egui::Key::Space,
218
219 glfw::Key::Enter => egui::Key::Enter,
220
221 glfw::Key::Insert => egui::Key::Insert,
222 glfw::Key::Home => egui::Key::Home,
223 glfw::Key::Delete => egui::Key::Delete,
224 glfw::Key::End => egui::Key::End,
225 glfw::Key::PageDown => egui::Key::PageDown,
226 glfw::Key::PageUp => egui::Key::PageUp,
227
228
229 glfw::Key::A => egui::Key::A,
230 glfw::Key::B => egui::Key::B,
231 glfw::Key::C => egui::Key::C,
232 glfw::Key::D => egui::Key::D,
233 glfw::Key::E => egui::Key::E,
234 glfw::Key::F => egui::Key::F,
235 glfw::Key::G => egui::Key::G,
236 glfw::Key::H => egui::Key::H,
237 glfw::Key::I => egui::Key::I,
238 glfw::Key::J => egui::Key::J,
239 glfw::Key::K => egui::Key::K,
240 glfw::Key::L => egui::Key::L,
241 glfw::Key::M => egui::Key::M,
242 glfw::Key::N => egui::Key::N,
243 glfw::Key::O => egui::Key::O,
244 glfw::Key::P => egui::Key::P,
245 glfw::Key::Q => egui::Key::Q,
246 glfw::Key::R => egui::Key::R,
247 glfw::Key::S => egui::Key::S,
248 glfw::Key::T => egui::Key::T,
249 glfw::Key::U => egui::Key::U,
250 glfw::Key::V => egui::Key::V,
251 glfw::Key::W => egui::Key::W,
252 glfw::Key::X => egui::Key::X,
253 glfw::Key::Y => egui::Key::Y,
254 glfw::Key::Z => egui::Key::Z,
255
256 _ => {
257 return None;
258 }
259 })
260}
261
262pub fn translate_cursor(cursor_icon: egui::CursorIcon) -> glfw::StandardCursor {
263 match cursor_icon {
264 CursorIcon::Default => glfw::StandardCursor::Arrow,
265 CursorIcon::PointingHand => glfw::StandardCursor::Hand,
266 CursorIcon::ResizeHorizontal => glfw::StandardCursor::HResize,
267 CursorIcon::ResizeVertical => glfw::StandardCursor::VResize,
268 CursorIcon::ResizeNeSw => glfw::StandardCursor::HResize,
270 CursorIcon::ResizeNwSe => glfw::StandardCursor::VResize,
271 CursorIcon::Text => glfw::StandardCursor::IBeam,
272 CursorIcon::Crosshair => glfw::StandardCursor::Crosshair,
273 CursorIcon::NotAllowed | CursorIcon::NoDrop => glfw::StandardCursor::Arrow,
275 CursorIcon::Wait => glfw::StandardCursor::Arrow,
276 CursorIcon::Grab | CursorIcon::Grabbing => glfw::StandardCursor::Hand,
277
278 _ => glfw::StandardCursor::Arrow,
279 }
280}
281
282pub fn init_clipboard() -> Option<ClipboardContext> {
283 match ClipboardContext::new() {
284 Ok(clipboard) => Some(clipboard),
285 Err(err) => {
286 eprintln!("Failed to initialize clipboard: {}", err);
287 None
288 }
289 }
290}
291
292pub fn copy_to_clipboard(egui_state: &mut EguiInputState, copy_text: String) {
293 if let Some(clipboard) = egui_state.clipboard.as_mut() {
294 let result = clipboard.set_contents(copy_text);
295 if result.is_err() {
296 dbg!("Unable to set clipboard content.");
297 }
298 }
299}