1use std::cell::{Cell, RefCell};
2use std::sync::atomic::{AtomicBool, Ordering};
3use std::sync::{Arc, Mutex};
4use std::time::Instant;
5
6use baseview::dpi::{LogicalPosition, LogicalSize, Size};
7use baseview::{
8 Event, EventStatus, HandlerError, ParentWindowHandle, Window, WindowContext, WindowHandler,
9 WindowSettings, WindowSize,
10};
11use copypasta::ClipboardProvider;
12use egui::{FullOutput, Pos2, Rect, Rgba, ViewportCommand, pos2, vec2};
13use keyboard_types::Modifiers;
14use raw_window_handle::HasWindowHandle;
15
16use crate::App;
17use crate::{GraphicsConfig, renderer::Renderer};
18
19#[cfg(all(feature = "log", not(feature = "tracing")))]
20use log::{error, warn};
21#[cfg(feature = "tracing")]
22use tracing::{error, warn};
23
24#[derive(Debug, Clone)]
29pub struct RepaintNotifier {
30 repaint: Arc<AtomicBool>,
31}
32
33impl RepaintNotifier {
34 pub fn new() -> Self {
35 Self {
36 repaint: Arc::new(AtomicBool::new(true)),
37 }
38 }
39
40 pub fn request_repaint(&self) {
41 self.repaint.store(true, Ordering::Relaxed);
42 }
43
44 pub fn request_repaint_with(&self, repaint: bool) {
45 if repaint {
46 self.repaint.store(true, Ordering::Relaxed);
47 }
48 }
49
50 fn repaint_requested(&self) -> bool {
51 self.repaint.swap(false, Ordering::Relaxed)
52 }
53}
54
55impl Default for RepaintNotifier {
56 fn default() -> Self {
57 Self::new()
58 }
59}
60
61#[derive(Debug, Clone)]
63pub struct EguiWindowSettings {
64 pub title: String,
66
67 pub size: Size,
69
70 pub min_size: Option<Size>,
72 pub max_size: Option<Size>,
74
75 pub resizable: bool,
77
78 pub zoom_factor: f32,
84
85 pub graphics: GraphicsConfig,
87
88 pub parent: Option<ParentWindowHandle>,
92
93 pub wait_for_parent: bool,
100
101 pub fallback_scale_factor: Option<f64>,
116
117 pub repaint_notifier: Option<RepaintNotifier>,
122}
123
124impl EguiWindowSettings {
125 #[inline]
126 pub fn new() -> Self {
127 Self::default()
128 }
129
130 #[inline]
132 pub fn with_title(mut self, title: impl Into<String>) -> Self {
133 self.title = title.into();
134 self
135 }
136
137 #[inline]
139 pub fn with_size(mut self, size: impl Into<Size>) -> Self {
140 self.size = size.into();
141 self
142 }
143
144 #[inline]
148 pub fn with_resizable(mut self, resizable: bool) -> Self {
149 self.resizable = resizable;
150 self
151 }
152
153 #[inline]
157 pub fn with_min_size<S: Into<Size>>(mut self, min_size: Option<S>) -> Self {
158 self.min_size = min_size.map(|s| s.into());
159 self
160 }
161
162 #[inline]
166 pub fn with_max_size<S: Into<Size>>(mut self, max_size: Option<S>) -> Self {
167 self.max_size = max_size.map(|s| s.into());
168 self
169 }
170
171 #[inline]
179 pub fn with_zoom_factor(mut self, zoom_factor: f32) -> Self {
180 self.zoom_factor = zoom_factor;
181 self
182 }
183
184 #[inline]
188 pub fn with_parent<'a, P: HasWindowHandle + 'a>(
189 mut self,
190 parent: impl Into<Option<&'a P>>,
191 ) -> Self {
192 self.parent = parent.into().map(ParentWindowHandle::from_window);
193 self
194 }
195
196 pub fn with_wait_for_parent(mut self, wait_for_parent: bool) -> Self {
198 self.wait_for_parent = wait_for_parent;
199 self
200 }
201
202 #[inline]
204 pub fn wait_for_parent(mut self) -> Self {
205 self.wait_for_parent = true;
206 self
207 }
208
209 #[inline]
211 pub fn with_fallback_scale_factor(mut self, scale_factor: impl Into<Option<f64>>) -> Self {
212 self.fallback_scale_factor = scale_factor.into();
213 self
214 }
215
216 #[inline]
218 pub fn with_graphics_config(mut self, config: GraphicsConfig) -> Self {
219 self.graphics = config;
220 self
221 }
222
223 #[inline]
228 pub fn with_repaint_notifier(mut self, repaint_notifier: RepaintNotifier) -> Self {
229 self.repaint_notifier = Some(repaint_notifier);
230 self
231 }
232}
233
234impl Default for EguiWindowSettings {
235 fn default() -> Self {
236 Self {
237 title: String::new(),
238 size: Size::Logical(LogicalSize {
239 width: 300.0,
240 height: 200.0,
241 }),
242 min_size: None,
243 max_size: None,
244 resizable: true,
245 zoom_factor: 1.0,
246 graphics: GraphicsConfig::default(),
247 parent: None,
248 wait_for_parent: false,
249 fallback_scale_factor: None,
250 repaint_notifier: None,
251 }
252 }
253}
254
255pub struct Frame {
257 renderer: Renderer,
258 window: WindowContext,
259 clear_color: Rgba,
260 key_capture: KeyCapture,
261}
262
263impl Frame {
264 fn new(renderer: Renderer, window: WindowContext) -> Self {
265 Self {
266 renderer,
267 window,
268 clear_color: Rgba::BLACK,
269 key_capture: Default::default(),
270 }
271 }
272
273 pub fn set_clear_color(&mut self, clear_color: Rgba) {
275 self.clear_color = clear_color;
276 }
277
278 pub fn set_key_capture(&mut self, key_capture: KeyCapture) {
280 self.key_capture = key_capture;
281 }
282
283 pub fn baseview_window(&self) -> &WindowContext {
285 &self.window
286 }
287
288 #[cfg(feature = "opengl")]
302 pub fn gl(&self) -> &std::sync::Arc<egui_glow::glow::Context> {
303 &self.renderer.glow_context
304 }
305}
306
307#[derive(Default, Debug, Clone, PartialEq)]
309pub enum KeyCapture {
310 #[default]
311 CaptureAll,
313 IgnoreAll,
315 CaptureKeys(Vec<keyboard_types::Key>),
317 IgnoreKeys(Vec<keyboard_types::Key>),
319}
320
321struct EguiWindowInner<A: App> {
322 user_app: A,
323 egui_ctx: egui::Context,
324 clipboard_ctx: Option<copypasta::ClipboardContext>,
325 frame: Frame,
326}
327
328pub struct EguiWindow<A: App> {
330 inner: RefCell<EguiWindowInner<A>>,
331 egui_input: RefCell<egui::RawInput>,
332 viewport_id: egui::ViewportId,
333 start_time: Instant,
334 system_scale_factor: Cell<f64>,
335 zoom_factor: Cell<f32>,
336 pointer_logical_pos: Cell<Option<egui::Pos2>>,
337 current_cursor_icon: Cell<baseview::MouseCursor>,
338 repaint_after: Arc<Mutex<Option<Instant>>>,
339 repaint_notifier: Option<RepaintNotifier>,
340 modifiers: Cell<egui::Modifiers>,
341
342 full_output: RefCell<FullOutput>,
344
345 pub window: WindowContext,
346}
347
348impl<A: App> EguiWindow<A> {
349 fn new(
350 window: WindowContext,
351 title: String,
352 graphics_config: GraphicsConfig,
353 mut user_app: A,
354 zoom_factor: f32,
355 repaint_notifier: Option<RepaintNotifier>,
356 ) -> Result<EguiWindow<A>, HandlerError> {
357 let renderer = Renderer::new(window.clone(), graphics_config)?;
358 let egui_ctx = egui::Context::default();
359
360 egui_ctx.set_zoom_factor(zoom_factor);
361
362 let repaint_after = Arc::new(Mutex::new(Some(Instant::now())));
363 let repaint_after_2 = Arc::clone(&repaint_after);
364 egui_ctx.set_request_repaint_callback(move |request_repaint_info| {
365 let repaint_instant = Instant::now() + request_repaint_info.delay;
366
367 let mut repaint_after = repaint_after_2.lock().unwrap();
368 let repaint_after = &mut *repaint_after;
369
370 if let Some(repaint_after) = repaint_after.as_mut() {
371 if repaint_instant < *repaint_after {
372 *repaint_after = repaint_instant;
373 }
374 } else {
375 *repaint_after = Some(repaint_instant);
376 }
377 });
378
379 let size = window.size();
380
381 let system_scale_factor = size.scale_factor;
382 let total_scale_factor = system_scale_factor * zoom_factor as f64;
383
384 let logical_size: LogicalSize<f64> = size.physical.to_logical(total_scale_factor);
385
386 let screen_rect = Rect::from_min_size(
387 Pos2::new(0f32, 0f32),
388 vec2(logical_size.width as f32, logical_size.height as f32),
389 );
390
391 let viewport_info = egui::ViewportInfo {
392 parent: None,
393 title: Some(title),
394 native_pixels_per_point: Some(system_scale_factor as f32),
395 focused: Some(true),
396 inner_rect: Some(screen_rect),
397 ..Default::default()
398 };
399 let viewport_id = egui::ViewportId::default();
400
401 let mut egui_input = egui::RawInput {
402 max_texture_side: Some(renderer.max_texture_side()),
403 screen_rect: Some(screen_rect),
404 ..Default::default()
405 };
406 let _ = egui_input.viewports.insert(viewport_id, viewport_info);
407
408 let mut frame = Frame::new(renderer, window.clone());
409
410 user_app.build(egui_ctx.clone(), &mut frame)?;
411
412 let clipboard_ctx = match copypasta::ClipboardContext::new() {
413 Ok(clipboard_ctx) => Some(clipboard_ctx),
414 Err(e) => {
415 #[cfg(any(feature = "tracing", feature = "log"))]
416 error!("Failed to initialize clipboard: {}", e);
417
418 #[cfg(not(any(feature = "tracing", feature = "log")))]
419 let _ = e;
420
421 None
422 }
423 };
424
425 let start_time = Instant::now();
426
427 Ok(Self {
428 inner: RefCell::new(EguiWindowInner {
429 user_app,
430 egui_ctx,
431 clipboard_ctx,
432 frame,
433 }),
434 viewport_id,
435 start_time,
436 egui_input: egui_input.into(),
437 pointer_logical_pos: None.into(),
438 current_cursor_icon: baseview::MouseCursor::Default.into(),
439 system_scale_factor: system_scale_factor.into(),
440 zoom_factor: zoom_factor.into(),
441 repaint_after,
442 window,
443 repaint_notifier,
444 modifiers: Cell::new(egui::Modifiers::default()),
445 full_output: RefCell::new(FullOutput::default()),
446 })
447 }
448
449 pub fn create(settings: EguiWindowSettings, app: A) -> Result<Window, baseview::Error> {
454 Self::create_with_host(settings, app, None)
455 }
456
457 pub fn create_with_host(
463 settings: EguiWindowSettings,
464 app: A,
465 host: Option<baseview::host::Host>,
466 ) -> Result<Window, baseview::Error> {
467 let size = match settings.size {
468 Size::Logical(size) => Size::Logical(LogicalSize {
469 width: size.width as f64 * settings.zoom_factor as f64,
470 height: size.height as f64 * settings.zoom_factor as f64,
471 }),
472 Size::Physical(size) => Size::Physical(size),
473 };
474
475 let mut options = WindowSettings::new()
476 .with_title(settings.title.clone())
477 .with_size(size)
478 .with_min_size::<Size>(settings.min_size)
479 .with_max_size::<Size>(settings.max_size)
480 .with_resizable(settings.resizable)
481 .with_wait_for_parent(settings.wait_for_parent)
482 .with_fallback_scale_factor(settings.fallback_scale_factor);
483
484 options.parent = settings.parent;
485
486 #[cfg(feature = "opengl")]
487 let options = { options.with_gl_config(Some(settings.graphics.gl_config.clone())) };
488
489 Window::create_with_host(
490 options,
491 move |window| {
492 EguiWindow::new(
493 window,
494 settings.title,
495 settings.graphics,
496 app,
497 settings.zoom_factor,
498 settings.repaint_notifier,
499 )
500 },
501 host,
502 )
503 }
504}
505
506fn update_modifiers(
508 old_modifiers: &Cell<egui::Modifiers>,
509 new_modifiers: &Modifiers,
510 egui_input: &mut egui::RawInput,
511) {
512 let (new_mac_cmd, new_command) = if cfg!(target_os = "macos") {
513 let m = new_modifiers.meta();
514 (m, m)
515 } else {
516 (false, new_modifiers.ctrl())
517 };
518
519 let new_modifiers = egui::Modifiers {
520 alt: new_modifiers.alt(),
521 ctrl: new_modifiers.ctrl(),
522 shift: new_modifiers.shift(),
523 mac_cmd: new_mac_cmd,
524 command: new_command,
525 };
526
527 if old_modifiers.get() != new_modifiers {
528 old_modifiers.set(new_modifiers);
529 egui_input
530 .events
531 .push(egui::Event::ModifiersChanged(new_modifiers));
532 }
533}
534
535impl<A: App> WindowHandler for EguiWindow<A> {
536 fn on_frame(&self) -> Result<(), HandlerError> {
537 let mut do_repaint_now = if let Some(repaint_notifier) = &self.repaint_notifier {
538 repaint_notifier.repaint_requested()
539 } else {
540 false
541 };
542
543 {
544 let mut repaint_after = self.repaint_after.lock().unwrap();
545 let repaint_after = &mut *repaint_after;
546
547 if let Some(instant) = &repaint_after
548 && Instant::now() >= *instant
549 {
550 do_repaint_now = true;
551 *repaint_after = None;
552 }
553 }
554
555 if !do_repaint_now {
556 return Ok(());
557 }
558
559 let (egui_input, logical_size) = {
560 let mut egui_input = self.egui_input.borrow_mut();
561 egui_input.time = Some(self.start_time.elapsed().as_secs_f64());
562
563 let zoom_factor = self.zoom_factor.get();
564
565 let size = self.window.size();
566 let logical_size: LogicalSize<f32> = size
567 .physical
568 .to_logical(size.scale_factor * zoom_factor as f64);
569
570 let screen_rect = Rect::from_min_size(
571 Pos2::new(0f32, 0f32),
572 vec2(logical_size.width, logical_size.height),
573 );
574
575 egui_input.screen_rect = Some(screen_rect);
576 (egui_input.take(), logical_size)
577 };
578
579 let mut full_output = self.full_output.borrow_mut();
581
582 *full_output = {
583 let mut inner = self.inner.borrow_mut();
584 let EguiWindowInner {
585 user_app,
586 egui_ctx,
587 clipboard_ctx: _,
588 frame,
589 } = &mut *inner;
590
591 egui_ctx.run_ui(egui_input, |ui| user_app.ui(ui, frame))
592 };
593
594 let Some(viewport_output) = full_output.viewport_output.get(&self.viewport_id) else {
595 self.window.request_close();
597 return Ok(());
598 };
599
600 let mut new_size = None;
601 let new_zoom = { self.inner.borrow().egui_ctx.zoom_factor() };
602
603 if self.zoom_factor.get() != new_zoom {
604 self.zoom_factor.set(new_zoom);
605
606 let mut inner = self.inner.borrow_mut();
607
608 inner.egui_ctx.set_zoom_factor(new_zoom);
609 inner.user_app.zoom_factor_changed(new_zoom);
610
611 new_size = Some(Size::Logical(LogicalSize::new(
612 (logical_size.width * new_zoom) as f64,
613 (logical_size.height * new_zoom) as f64,
614 )));
615 }
616
617 for command in viewport_output.commands.iter() {
618 match command {
619 ViewportCommand::Close => {
620 self.window.request_close();
621 }
622 ViewportCommand::InnerSize(size) => {
623 new_size = Some(Size::Logical(LogicalSize::new(
624 (size.x * new_zoom) as f64,
625 (size.y * new_zoom) as f64,
626 )));
627 }
628 ViewportCommand::Focus => {
629 self.window.focus()?;
630 }
631 _ => {}
632 }
633 }
634
635 if let Some(new_size) = new_size {
636 if let Err(e) = self.window.resize(new_size) {
637 error!("Failed to resize window: {}", e);
638 }
639 }
640
641 {
642 let mut inner = self.inner.borrow_mut();
643 let EguiWindowInner {
644 user_app: _,
645 egui_ctx,
646 clipboard_ctx,
647 frame,
648 } = &mut *inner;
649
650 let size = self.window.size();
651 frame.renderer.render(
652 &self.window,
653 frame.clear_color,
654 size.physical,
655 size.scale_factor as f32 * egui_ctx.zoom_factor(),
656 egui_ctx,
657 &mut full_output,
658 );
659
660 for command in full_output.platform_output.commands.drain(..) {
661 match command {
662 egui::OutputCommand::CopyText(text) => {
663 if let Some(clipboard_ctx) = clipboard_ctx.as_mut()
664 && let Err(err) = clipboard_ctx.set_contents(text)
665 {
666 #[cfg(any(feature = "tracing", feature = "log"))]
667 error!("Copy/Cut error: {}", err);
668
669 #[cfg(not(any(feature = "tracing", feature = "log")))]
670 let _ = err;
671 }
672 }
673 egui::OutputCommand::CopyImage(_) => {
674 #[cfg(any(feature = "tracing", feature = "log"))]
675 warn!("Copying images is not supported in egui_baseview.");
676 }
677 egui::OutputCommand::OpenUrl(open_url) => {
678 if let Err(err) = open::that_detached(&open_url.url) {
679 #[cfg(any(feature = "tracing", feature = "log"))]
680 error!("Open error: {}", err);
681
682 #[cfg(not(any(feature = "tracing", feature = "log")))]
683 let _ = err;
684 }
685 }
686 }
687 }
688 }
689
690 let cursor_icon =
691 crate::translate::translate_cursor_icon(full_output.platform_output.cursor_icon);
692 if self.current_cursor_icon.get() != cursor_icon {
693 self.current_cursor_icon.set(cursor_icon);
694
695 self.window.set_mouse_cursor(cursor_icon)?;
696 }
697
698 #[cfg(feature = "keyboard_focus_workaround")]
701 {
702 if !full_output.platform_output.events.is_empty()
703 || full_output.platform_output.ime.is_some()
704 {
705 window.focus();
706 }
707 }
708
709 Ok(())
710 }
711
712 fn resized(&self, new_size: WindowSize) -> Result<(), HandlerError> {
713 let zoom_factor = self.inner.borrow().egui_ctx.zoom_factor();
714
715 let total_scale_factor = new_size.scale_factor * zoom_factor as f64;
716 let logical_size: LogicalSize<f64> = new_size.physical.to_logical(total_scale_factor);
717
718 let screen_rect = Rect::from_min_size(
719 Pos2::new(0f32, 0f32),
720 vec2(logical_size.width as f32, logical_size.height as f32),
721 );
722
723 let mut egui_input = self.egui_input.borrow_mut();
724
725 egui_input.screen_rect = Some(screen_rect);
726
727 let viewport_info = egui_input.viewports.get_mut(&self.viewport_id).unwrap();
728 viewport_info.native_pixels_per_point = Some(new_size.scale_factor as f32);
729 viewport_info.inner_rect = Some(screen_rect);
730
731 self.system_scale_factor.set(new_size.scale_factor);
732
733 let mut inner = self.inner.borrow_mut();
734
735 inner.egui_ctx.request_repaint();
736
737 inner.user_app.resized(WindowSize {
738 physical: new_size.physical,
739 logical: logical_size,
740 scale_factor: total_scale_factor,
741 });
742
743 Ok(())
744 }
745
746 fn on_event(&self, event: Event) -> EventStatus {
747 let mut return_status = EventStatus::Captured;
748
749 if matches!(
754 event,
755 baseview::Event::Mouse(baseview::MouseEvent::ButtonPressed { .. })
756 ) && !self.window.has_focus()
757 {
758 self.window.focus().unwrap();
759 }
760
761 let mut egui_input = self.egui_input.borrow_mut();
762
763 let mut do_repaint = true;
764
765 match &event {
766 baseview::Event::Mouse(event) => match event {
767 baseview::MouseEvent::CursorMoved {
768 position,
769 modifiers,
770 } => {
771 update_modifiers(&self.modifiers, modifiers, &mut egui_input);
772
773 let logical_pos: LogicalPosition<f32> = position.to_logical(
774 self.system_scale_factor.get()
775 * self.inner.borrow().egui_ctx.zoom_factor() as f64,
776 );
777 let pos = pos2(logical_pos.x, logical_pos.y);
778
779 self.pointer_logical_pos.set(Some(pos));
780 egui_input.events.push(egui::Event::PointerMoved(pos));
781 }
782 baseview::MouseEvent::ButtonPressed { button, modifiers } => {
783 update_modifiers(&self.modifiers, modifiers, &mut egui_input);
784
785 if let Some(pos) = self.pointer_logical_pos.get()
786 && let Some(button) = crate::translate::translate_mouse_button(*button)
787 {
788 egui_input.events.push(egui::Event::PointerButton {
789 pos,
790 button,
791 pressed: true,
792 modifiers: self.modifiers.get(),
793 });
794 }
795 }
796 baseview::MouseEvent::ButtonReleased { button, modifiers } => {
797 update_modifiers(&self.modifiers, modifiers, &mut egui_input);
798
799 if let Some(pos) = self.pointer_logical_pos.get()
800 && let Some(button) = crate::translate::translate_mouse_button(*button)
801 {
802 egui_input.events.push(egui::Event::PointerButton {
803 pos,
804 button,
805 pressed: false,
806 modifiers: self.modifiers.get(),
807 });
808 }
809 }
810 baseview::MouseEvent::WheelScrolled {
811 delta: scroll_delta,
812 modifiers,
813 } => {
814 update_modifiers(&self.modifiers, modifiers, &mut egui_input);
815
816 #[allow(unused_mut)]
817 let (unit, mut delta) = match scroll_delta {
818 baseview::ScrollDelta::Lines { x, y } => {
819 (egui::MouseWheelUnit::Line, egui::vec2(*x, *y))
820 }
821
822 baseview::ScrollDelta::Pixels { x, y } => (
823 egui::MouseWheelUnit::Point,
824 egui::vec2(*x, *y) * self.window.scale_factor() as f32,
825 ),
826 };
827
828 if cfg!(target_os = "macos") {
829 delta.x *= -1.0;
834 }
835
836 egui_input.events.push(egui::Event::MouseWheel {
837 unit,
838 delta,
839 modifiers: self.modifiers.get(),
840 phase: egui::TouchPhase::Move,
841 });
842 }
843 baseview::MouseEvent::CursorLeft => {
844 self.pointer_logical_pos.set(None);
845 egui_input.events.push(egui::Event::PointerGone);
846 }
847 _ => do_repaint = false,
848 },
849 baseview::Event::Keyboard(event) => {
850 update_modifiers(&self.modifiers, &event.modifiers, &mut egui_input);
851
852 let pressed = event.state == keyboard_types::KeyState::Down;
853
854 let modifiers = self.modifiers.get();
855
856 if let Some(key) = crate::translate::translate_virtual_key(&event.key) {
857 egui_input.events.push(egui::Event::Key {
858 key,
859 physical_key: None,
860 pressed,
861 repeat: event.repeat,
862 modifiers,
863 });
864 }
865
866 if pressed {
867 if is_cut_command(modifiers, event.code) {
872 egui_input.events.push(egui::Event::Cut);
873 } else if is_copy_command(modifiers, event.code) {
874 egui_input.events.push(egui::Event::Copy);
875 } else if is_paste_command(modifiers, event.code) {
876 if let Some(clipboard_ctx) = self.inner.borrow_mut().clipboard_ctx.as_mut()
877 {
878 match clipboard_ctx.get_contents() {
879 Ok(contents) => egui_input.events.push(egui::Event::Text(contents)),
880 Err(err) => {
881 #[cfg(any(feature = "tracing", feature = "log"))]
882 error!("Paste error: {}", err);
883
884 #[cfg(not(any(feature = "tracing", feature = "log")))]
885 let _ = err;
886 }
887 }
888 }
889 } else if let keyboard_types::Key::Character(written) = &event.key
890 && !modifiers.ctrl
891 && !modifiers.command
892 {
893 egui_input.events.push(egui::Event::Text(written.clone()));
894 }
895 }
896
897 match &self.inner.borrow().frame.key_capture {
898 KeyCapture::CaptureAll => {}
899 KeyCapture::IgnoreAll => return_status = EventStatus::Ignored,
900 KeyCapture::CaptureKeys(keys) => {
901 if !keys.contains(&event.key) {
902 return_status = EventStatus::Ignored
903 }
904 }
905 KeyCapture::IgnoreKeys(keys) => {
906 if keys.contains(&event.key) {
907 return_status = EventStatus::Ignored
908 }
909 }
910 }
911 }
912 baseview::Event::Window(event) => match event {
913 baseview::WindowEvent::Focused => {
914 egui_input.events.push(egui::Event::WindowFocused(true));
915 egui_input
916 .viewports
917 .get_mut(&self.viewport_id)
918 .unwrap()
919 .focused = Some(true);
920
921 self.inner.borrow().egui_ctx.request_repaint();
922 }
923 baseview::WindowEvent::Unfocused => {
924 egui_input.events.push(egui::Event::WindowFocused(false));
925 egui_input
926 .viewports
927 .get_mut(&self.viewport_id)
928 .unwrap()
929 .focused = Some(false);
930 }
931 baseview::WindowEvent::WillClose => {}
932 _ => {}
933 },
934 _ => do_repaint = false,
935 }
936
937 if do_repaint {
938 self.inner.borrow().egui_ctx.request_repaint();
939 }
940
941 match &event {
944 baseview::Event::Keyboard(_) => {
945 let egui_ctx = &self.inner.borrow().egui_ctx;
946 if return_status == EventStatus::Captured && !egui_ctx.egui_wants_keyboard_input() {
947 EventStatus::Ignored
948 } else {
949 return_status
950 }
951 }
952 baseview::Event::Mouse(_) => {
953 let egui_ctx = &self.inner.borrow().egui_ctx;
954 if egui_ctx.egui_is_using_pointer() || egui_ctx.egui_wants_pointer_input() {
955 EventStatus::Captured
956 } else {
957 EventStatus::Ignored
958 }
959 }
960 baseview::Event::Window(_) => EventStatus::Captured,
961 _ => EventStatus::Ignored,
962 }
963 }
964}
965
966fn is_cut_command(modifiers: egui::Modifiers, keycode: keyboard_types::Code) -> bool {
967 (modifiers.command && keycode == keyboard_types::Code::KeyX)
968 || (cfg!(target_os = "windows")
969 && modifiers.shift
970 && keycode == keyboard_types::Code::Delete)
971}
972
973fn is_copy_command(modifiers: egui::Modifiers, keycode: keyboard_types::Code) -> bool {
974 (modifiers.command && keycode == keyboard_types::Code::KeyC)
975 || (cfg!(target_os = "windows")
976 && modifiers.ctrl
977 && keycode == keyboard_types::Code::Insert)
978}
979
980fn is_paste_command(modifiers: egui::Modifiers, keycode: keyboard_types::Code) -> bool {
981 (modifiers.command && keycode == keyboard_types::Code::KeyV)
982 || (cfg!(target_os = "windows")
983 && modifiers.shift
984 && keycode == keyboard_types::Code::Insert)
985}