1#![warn(clippy::all)]
2
3pub use egui;
5pub use gl;
6pub use glfw;
7
8pub mod painter;
9
10pub use painter::Painter;
11
12use egui::*;
13
14#[cfg(not(feature = "clipboard"))]
15mod clipboard;
16
17#[cfg(not(feature = "clipboard"))]
18use clipboard::{ClipboardContext, ClipboardProvider};
19
20#[cfg(feature = "clipboard")]
21use clipboard::Clipboard as ClipboardContext;
22
23pub struct EguiInputState {
24 pub pointer_pos: Pos2,
25 pub clipboard: Option<ClipboardContext>,
26 pub input: RawInput,
27 pub modifiers: Modifiers,
28 pub pixels_per_point: f32,
29}
30
31impl EguiInputState {
32 pub fn new(input: RawInput, pixels_per_point: f32) -> Self {
33 EguiInputState {
34 pointer_pos: Pos2::new(0f32, 0f32),
35 clipboard: init_clipboard(),
36 input,
37 modifiers: Modifiers::default(),
38 pixels_per_point,
39 }
40 }
41}
42
43pub fn handle_event(event: glfw::WindowEvent, state: &mut EguiInputState) {
44 use glfw::WindowEvent::*;
45
46 match event {
47 FramebufferSize(width, height) => {
48 state.input.screen_rect = Some(Rect::from_min_size(
49 Pos2::new(0f32, 0f32),
50 egui::vec2(width as f32, height as f32) / state.pixels_per_point,
51 ));
52 }
53
54 MouseButton(mouse_btn, glfw::Action::Press, _) => {
55 state.input.events.push(egui::Event::PointerButton {
56 pos: state.pointer_pos,
57 button: match mouse_btn {
58 glfw::MouseButtonLeft => egui::PointerButton::Primary,
59 glfw::MouseButtonRight => egui::PointerButton::Secondary,
60 glfw::MouseButtonMiddle => egui::PointerButton::Middle,
61 glfw::MouseButton::Button4 => egui::PointerButton::Extra2,
62 glfw::MouseButton::Button5 => egui::PointerButton::Extra1,
63 _ => egui::PointerButton::Primary,
64 },
65 pressed: true,
66 modifiers: state.modifiers,
67 })
68 }
69
70 MouseButton(mouse_btn, glfw::Action::Release, _) => {
71 state.input.events.push(egui::Event::PointerButton {
72 pos: state.pointer_pos,
73 button: match mouse_btn {
74 glfw::MouseButtonLeft => egui::PointerButton::Primary,
75 glfw::MouseButtonRight => egui::PointerButton::Secondary,
76 glfw::MouseButtonMiddle => egui::PointerButton::Middle,
77 glfw::MouseButton::Button4 => egui::PointerButton::Extra2,
78 glfw::MouseButton::Button5 => egui::PointerButton::Extra1,
79 _ => egui::PointerButton::Primary,
80 },
81 pressed: false,
82 modifiers: state.modifiers,
83 })
84 }
85
86 CursorPos(x, y) => {
87 state.pointer_pos = pos2(
88 x as f32 / state.pixels_per_point,
89 y as f32 / state.pixels_per_point,
90 );
91 state
92 .input
93 .events
94 .push(egui::Event::PointerMoved(state.pointer_pos))
95 }
96
97 Key(keycode, _scancode, glfw::Action::Release, keymod) => {
98 use glfw::Modifiers as Mod;
99 if let Some(key) = translate_virtual_key_code(keycode) {
100 state.modifiers = Modifiers {
101 alt: (keymod & Mod::Alt == Mod::Alt),
102 ctrl: (keymod & Mod::Control == Mod::Control),
103 shift: (keymod & Mod::Shift == Mod::Shift),
104
105 command: (keymod & Mod::Control == Mod::Control),
108
109 ..Default::default()
110 };
111
112 state.input.events.push(Event::Key {
113 key,
114 physical_key: None,
115 pressed: false,
116 modifiers: state.modifiers,
117 repeat: false,
118 });
119 }
120 }
121
122 Key(keycode, _scancode, glfw::Action::Press, keymod) => {
123 use glfw::Modifiers as Mod;
124 if let Some(key) = translate_virtual_key_code(keycode) {
125 state.modifiers = Modifiers {
126 alt: (keymod & Mod::Alt == Mod::Alt),
127 ctrl: (keymod & Mod::Control == Mod::Control),
128 shift: (keymod & Mod::Shift == Mod::Shift),
129
130 command: (keymod & Mod::Control == Mod::Control),
133
134 ..Default::default()
135 };
136
137 if state.modifiers.command && key == egui::Key::X {
138 state.input.events.push(egui::Event::Cut);
139 } else if state.modifiers.command && key == egui::Key::C {
140 state.input.events.push(egui::Event::Copy);
141 } else if state.modifiers.command && key == egui::Key::V {
142 if let Some(clipboard_ctx) = state.clipboard.as_mut() {
143 state.input.events.push(egui::Event::Text(
144 clipboard_ctx.get_text().unwrap_or_else(|_| "".to_string()),
145 ));
146 }
147 } else {
148 state.input.events.push(Event::Key {
149 key,
150 physical_key: None,
151 pressed: true,
152 modifiers: state.modifiers,
153 repeat: false,
154 });
155 }
156 }
157 }
158
159 Key(keycode, _scancode, glfw::Action::Repeat, keymod) => {
160 use glfw::Modifiers as Mod;
161 if let Some(key) = translate_virtual_key_code(keycode) {
162 state.modifiers = Modifiers {
163 alt: (keymod & Mod::Alt == Mod::Alt),
164 ctrl: (keymod & Mod::Control == Mod::Control),
165 shift: (keymod & Mod::Shift == Mod::Shift),
166
167 command: (keymod & Mod::Control == Mod::Control),
170
171 ..Default::default()
172 };
173
174 if state.modifiers.command && key == egui::Key::X {
175 state.input.events.push(egui::Event::Cut);
176 } else if state.modifiers.command && key == egui::Key::C {
177 state.input.events.push(egui::Event::Copy);
178 } else if state.modifiers.command && key == egui::Key::V {
179 if let Some(clipboard_ctx) = state.clipboard.as_mut() {
180 state.input.events.push(egui::Event::Text(
181 clipboard_ctx.get_text().unwrap_or_else(|_| "".to_string()),
182 ));
183 }
184 } else {
185 state.input.events.push(Event::Key {
186 key,
187 physical_key: None,
188 pressed: true,
189 modifiers: state.modifiers,
190 repeat: true,
191 });
192 }
193 }
194 }
195
196 Char(c) => {
197 state.input.events.push(Event::Text(c.to_string()));
198 }
199
200 Scroll(x, y) => {
201 state.input.events.push(Event::MouseWheel {
202 unit: MouseWheelUnit::Point,
203 delta: vec2(x as f32, y as f32),
204 modifiers: state.modifiers,
205 });
206 }
207
208 _ => {}
209 }
210}
211
212pub fn translate_virtual_key_code(key: glfw::Key) -> Option<egui::Key> {
213 use glfw::Key::*;
214
215 Some(match key {
216 Left => Key::ArrowLeft,
217 Up => Key::ArrowUp,
218 Right => Key::ArrowRight,
219 Down => Key::ArrowDown,
220
221 Escape => Key::Escape,
222 Tab => Key::Tab,
223 Backspace => Key::Backspace,
224 Space => Key::Space,
225
226 Enter => Key::Enter,
227
228 Insert => Key::Insert,
229 Home => Key::Home,
230 Delete => Key::Delete,
231 End => Key::End,
232 PageDown => Key::PageDown,
233 PageUp => Key::PageUp,
234
235 A => Key::A,
236 B => Key::B,
237 C => Key::C,
238 D => Key::D,
239 E => Key::E,
240 F => Key::F,
241 G => Key::G,
242 H => Key::H,
243 I => Key::I,
244 J => Key::J,
245 K => Key::K,
246 L => Key::L,
247 M => Key::M,
248 N => Key::N,
249 O => Key::O,
250 P => Key::P,
251 Q => Key::Q,
252 R => Key::R,
253 S => Key::S,
254 T => Key::T,
255 U => Key::U,
256 V => Key::V,
257 W => Key::W,
258 X => Key::X,
259 Y => Key::Y,
260 Z => Key::Z,
261
262 _ => {
263 return None;
264 }
265 })
266}
267
268pub fn translate_cursor(cursor_icon: egui::CursorIcon) -> glfw::StandardCursor {
269 match cursor_icon {
270 CursorIcon::Default => glfw::StandardCursor::Arrow,
271
272 CursorIcon::PointingHand => glfw::StandardCursor::Hand,
273
274 CursorIcon::ResizeHorizontal => glfw::StandardCursor::HResize,
275 CursorIcon::ResizeVertical => glfw::StandardCursor::VResize,
276 CursorIcon::ResizeNeSw => glfw::StandardCursor::HResize,
278 CursorIcon::ResizeNwSe => glfw::StandardCursor::VResize,
279
280 CursorIcon::Text => glfw::StandardCursor::IBeam,
281 CursorIcon::Crosshair => glfw::StandardCursor::Crosshair,
282
283 CursorIcon::Grab | CursorIcon::Grabbing => glfw::StandardCursor::Hand,
284
285 CursorIcon::NotAllowed | CursorIcon::NoDrop => glfw::StandardCursor::Arrow,
287 CursorIcon::Wait => glfw::StandardCursor::Arrow,
288 _ => glfw::StandardCursor::Arrow,
289 }
290}
291
292pub fn init_clipboard() -> Option<ClipboardContext> {
293 match ClipboardContext::new() {
294 Ok(clipboard) => Some(clipboard),
295 Err(err) => {
296 eprintln!("Failed to initialize clipboard: {}", err);
297 None
298 }
299 }
300}
301
302pub fn copy_to_clipboard(egui_state: &mut EguiInputState, copy_text: String) {
303 if let Some(clipboard) = egui_state.clipboard.as_mut() {
304 let result = clipboard.set_text(copy_text);
305 if result.is_err() {
306 dbg!("Unable to set clipboard content.");
307 }
308 }
309}