Skip to main content

easy_imgui_window/
window.rs

1use crate::conv::{from_imgui_cursor, to_imgui_button, to_imgui_key};
2use cgmath::Matrix3;
3use easy_imgui::{self as imgui, Vector2, cgmath, mint};
4use easy_imgui_renderer::Renderer;
5use easy_imgui_sys::*;
6use glutin::{
7    context::PossiblyCurrentContext,
8    prelude::*,
9    surface::{Surface, WindowSurface},
10};
11use std::num::NonZeroU32;
12use std::time::{Duration, Instant};
13use winit::{
14    dpi::{LogicalSize, PhysicalSize},
15    event::Ime::Commit,
16    keyboard::PhysicalKey,
17    window::{CursorIcon, Window},
18};
19
20pub use easy_imgui::EventResult;
21
22// Only used with the main-window feature
23#[allow(unused_imports)]
24use winit::dpi::{LogicalPosition, PhysicalPosition, Pixel};
25
26/// This struct maintains basic window info to be kept across events.
27#[derive(Debug, Clone)]
28pub struct MainWindowStatus {
29    last_frame: Instant,
30    current_cursor: Option<CursorIcon>,
31}
32
33impl Default for MainWindowStatus {
34    fn default() -> MainWindowStatus {
35        let now = Instant::now();
36        MainWindowStatus {
37            last_frame: now,
38            current_cursor: Some(CursorIcon::Default),
39        }
40    }
41}
42
43/// This traits grants access to a Window.
44///
45/// Usually you will have a [`MainWindow`], but if you create the `Window` with an external
46/// crate, maybe you don't own it.
47pub trait MainWindowRef {
48    /// Gets the [`Window`].
49    fn window(&self) -> &Window;
50    /// This runs just before rendering.
51    ///
52    /// The intended use is to make the GL context current, if needed.
53    /// Clearing the background is usually done in [`easy_imgui::UiBuilder::pre_render`], or by the renderer if it has a background color.
54    fn pre_render(&mut self) {}
55    /// This runs just after rendering.
56    ///
57    /// The intended use is to present the screen buffer.
58    fn post_render(&mut self) {}
59    /// Notifies of a user interaction, for idling purposes.
60    fn ping_user_input(&mut self) {}
61    /// There are no more messages, going to idle.
62    fn about_to_wait(&mut self, _pinged: bool) {}
63    /// Transform the given `pos` by using the current scale factor.
64    fn transform_position(&self, pos: Vector2) -> Vector2 {
65        pos / self.scale_factor()
66    }
67    /// Gets the scale factor of the window, (HiDPI).
68    fn scale_factor(&self) -> f32 {
69        self.window().scale_factor() as f32
70    }
71    /// Changes the scale factor.
72    ///
73    /// Normally there is nothing to be done here, unless you are doing something fancy with HiDPI.
74    ///
75    /// It returns the real applied scale factor, as it would returned by
76    /// `self.scale_factor()` after this change has been applied.
77    fn set_scale_factor(&self, scale: f32) -> f32 {
78        scale
79    }
80    /// The window has been resized.
81    ///
82    /// Takes the new physical size. It should return the new logical size.
83    fn resize(&mut self, size: PhysicalSize<u32>) -> LogicalSize<f32> {
84        let scale = self.scale_factor();
85        size.to_logical(scale as f64)
86    }
87    /// Changes the mouse cursor.
88    fn set_cursor(&mut self, cursor: Option<CursorIcon>) {
89        let w = self.window();
90        match cursor {
91            None => w.set_cursor_visible(false),
92            Some(c) => {
93                w.set_cursor(c);
94                w.set_cursor_visible(true);
95            }
96        }
97    }
98}
99
100fn transform_position_with_optional_matrix(
101    w: &impl MainWindowRef,
102    pos: Vector2,
103    mx: &Option<Matrix3<f32>>,
104) -> Vector2 {
105    use cgmath::{EuclideanSpace as _, Transform};
106    match mx {
107        Some(mx) => mx.transform_point(cgmath::Point2::from_vec(pos)).to_vec(),
108        None => pos / w.scale_factor(),
109    }
110}
111
112bitflags::bitflags! {
113    /// These flags can be used to customize the [`window_event`] function.
114    #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
115    pub struct EventFlags: u32 {
116        /// Do not render the UI
117        const DoNotRender = 1;
118        /// Do not change the size or scale of the UI
119        const DoNotResize = 4;
120        /// Do not send mouse positions
121        const DoNotMouse = 8;
122    }
123}
124
125/// Helper struct to call [`window_event`] without owning the Window.
126pub struct MainWindowPieces<'a> {
127    window: &'a Window,
128    surface: &'a Surface<WindowSurface>,
129    gl_context: &'a PossiblyCurrentContext,
130    matrix: Option<Matrix3<f32>>,
131}
132
133impl<'a> MainWindowPieces<'a> {
134    /// Creates a value from the pieces.
135    pub fn new(
136        window: &'a Window,
137        surface: &'a Surface<WindowSurface>,
138        gl_context: &'a PossiblyCurrentContext,
139    ) -> Self {
140        MainWindowPieces {
141            window,
142            surface,
143            gl_context,
144            matrix: None,
145        }
146    }
147    /// Sets the matrix that transforms the input mouse coordinates into UI space.
148    ///
149    /// If none, it uses the default transformation.
150    pub fn set_matrix(&mut self, matrix: Option<Matrix3<f32>>) {
151        self.matrix = matrix;
152    }
153}
154
155/// Default implementation if you have all the pieces.
156impl MainWindowRef for MainWindowPieces<'_> {
157    fn window(&self) -> &Window {
158        self.window
159    }
160    fn pre_render(&mut self) {
161        let _ = self
162            .gl_context
163            .make_current(self.surface)
164            .inspect_err(|e| log::error!("{e}"));
165    }
166    fn post_render(&mut self) {
167        self.window.pre_present_notify();
168        let _ = self
169            .surface
170            .swap_buffers(self.gl_context)
171            .inspect_err(|e| log::error!("{e}"));
172    }
173    fn resize(&mut self, size: PhysicalSize<u32>) -> LogicalSize<f32> {
174        let width = NonZeroU32::new(size.width.max(1)).unwrap();
175        let height = NonZeroU32::new(size.height.max(1)).unwrap();
176        self.surface.resize(self.gl_context, width, height);
177        let scale = self.scale_factor();
178        size.to_logical(scale as f64)
179    }
180    fn transform_position(&self, pos: Vector2) -> Vector2 {
181        transform_position_with_optional_matrix(self, pos, &self.matrix)
182    }
183}
184
185/// Simple implementation if you only have a window, no pre/post render, no resize.
186impl MainWindowRef for &Window {
187    fn window(&self) -> &Window {
188        self
189    }
190}
191
192/// NewType to disable the HiDPI scaling.
193pub struct NoScale<'a>(pub &'a Window);
194
195impl MainWindowRef for NoScale<'_> {
196    fn window(&self) -> &Window {
197        self.0
198    }
199    fn scale_factor(&self) -> f32 {
200        1.0
201    }
202    fn set_scale_factor(&self, _scale: f32) -> f32 {
203        1.0
204    }
205}
206
207/// Corresponds to winit's `ApplicationHandler::new_events`.
208pub fn new_events(renderer: &mut Renderer, status: &mut MainWindowStatus) {
209    let now = Instant::now();
210    unsafe {
211        renderer
212            .imgui()
213            .io_mut()
214            .inner()
215            .set_delta_time(now.duration_since(status.last_frame));
216    }
217    status.last_frame = now;
218}
219
220/// Corresponds to winit's `ApplicationHandler::about_to_wait`.
221pub fn about_to_wait(main_window: &mut impl MainWindowRef, renderer: &mut Renderer) {
222    let imgui = unsafe { renderer.imgui().set_current() };
223    let io = imgui.io();
224    if io.WantSetMousePos {
225        let pos = io.MousePos;
226        let pos = winit::dpi::LogicalPosition { x: pos.x, y: pos.y };
227        let _ = main_window.window().set_cursor_position(pos);
228    }
229    // If the mouse is down, redraw all the time, maybe the user is dragging.
230    let mouse = unsafe { ImGui_IsAnyMouseDown() };
231    main_window.about_to_wait(mouse);
232}
233
234/// Corresponds to winit's `ApplicationHandler::window_event`.
235pub fn window_event(
236    main_window: &mut impl MainWindowRef,
237    renderer: &mut Renderer,
238    status: &mut MainWindowStatus,
239    app: &mut impl imgui::UiBuilder,
240    event: &winit::event::WindowEvent,
241    flags: EventFlags,
242) -> EventResult {
243    use winit::event::WindowEvent::*;
244    let mut window_closed = false;
245    match event {
246        CloseRequested => {
247            window_closed = true;
248        }
249        RedrawRequested => unsafe {
250            let imgui = renderer.imgui().set_current();
251            let io = imgui.io();
252            let config_flags = imgui::ConfigFlags::from_bits_truncate(io.ConfigFlags);
253            if !config_flags.contains(imgui::ConfigFlags::NoMouseCursorChange) {
254                let cursor = if io.MouseDrawCursor {
255                    None
256                } else {
257                    let cursor = imgui::MouseCursor::from_bits(ImGui_GetMouseCursor())
258                        .unwrap_or(imgui::MouseCursor::Arrow);
259                    from_imgui_cursor(cursor)
260                };
261                if cursor != status.current_cursor {
262                    main_window.set_cursor(cursor);
263                    status.current_cursor = cursor;
264                }
265            }
266            if !flags.contains(EventFlags::DoNotRender) {
267                main_window.pre_render();
268                renderer.do_frame(app);
269                main_window.post_render();
270            }
271        },
272        Resized(size) => {
273            // Do not skip this line or the gl surface may be wrong in Wayland
274            // GL surface in physical pixels, imgui in logical
275            let size = main_window.resize(*size);
276            if !flags.contains(EventFlags::DoNotResize) {
277                main_window.ping_user_input();
278                // GL surface in physical pixels, imgui in logical
279                let size = Vector2::from(mint::Vector2::from(size));
280                unsafe {
281                    renderer.imgui().io_mut().inner().DisplaySize = imgui::v2_to_im(size);
282                }
283            }
284        }
285        #[allow(clippy::collapsible_match)]
286        ScaleFactorChanged { scale_factor, .. } => {
287            if !flags.contains(EventFlags::DoNotResize) {
288                main_window.ping_user_input();
289                let scale_factor = main_window.set_scale_factor(*scale_factor as f32);
290                unsafe {
291                    let io = renderer.imgui().io_mut().inner();
292                    // Keep the mouse in the same relative position: maybe it is wrong, but it is
293                    // the best guess we can do.
294                    let old_scale_factor = io.DisplayFramebufferScale.x;
295                    if io.MousePos.x.is_finite() && io.MousePos.y.is_finite() {
296                        io.MousePos.x *= scale_factor / old_scale_factor;
297                        io.MousePos.y *= scale_factor / old_scale_factor;
298                    }
299                }
300                let size = renderer.size();
301                renderer.set_size(size, scale_factor);
302            }
303        }
304        ModifiersChanged(mods) => {
305            main_window.ping_user_input();
306            unsafe {
307                let io = renderer.imgui().io_mut().inner();
308                io.AddKeyEvent(imgui::Key::ModCtrl.bits(), mods.state().control_key());
309                io.AddKeyEvent(imgui::Key::ModShift.bits(), mods.state().shift_key());
310                io.AddKeyEvent(imgui::Key::ModAlt.bits(), mods.state().alt_key());
311                io.AddKeyEvent(imgui::Key::ModSuper.bits(), mods.state().super_key());
312            }
313        }
314        KeyboardInput {
315            event:
316                winit::event::KeyEvent {
317                    physical_key,
318                    text,
319                    state,
320                    ..
321                },
322            is_synthetic: false,
323            ..
324        } => {
325            main_window.ping_user_input();
326            let pressed = *state == winit::event::ElementState::Pressed;
327            if let Some(key) = to_imgui_key(*physical_key) {
328                unsafe {
329                    let io = renderer.imgui().io_mut().inner();
330                    io.AddKeyEvent(key.bits(), pressed);
331
332                    use winit::keyboard::KeyCode::*;
333                    if let PhysicalKey::Code(keycode) = physical_key {
334                        let kmod = match keycode {
335                            ControlLeft | ControlRight => Some(imgui::Key::ModCtrl),
336                            ShiftLeft | ShiftRight => Some(imgui::Key::ModShift),
337                            AltLeft | AltRight => Some(imgui::Key::ModAlt),
338                            SuperLeft | SuperRight => Some(imgui::Key::ModSuper),
339                            _ => None,
340                        };
341                        if let Some(kmod) = kmod {
342                            io.AddKeyEvent(kmod.bits(), pressed);
343                        }
344                    }
345                }
346            }
347            if pressed && let Some(text) = text {
348                unsafe {
349                    let io = renderer.imgui().io_mut().inner();
350                    for c in text.chars() {
351                        io.AddInputCharacter(c as u32);
352                    }
353                }
354            }
355        }
356        Ime(Commit(text)) => {
357            main_window.ping_user_input();
358            unsafe {
359                let io = renderer.imgui().io_mut().inner();
360                for c in text.chars() {
361                    io.AddInputCharacter(c as u32);
362                }
363            }
364        }
365        CursorMoved { position, .. } => {
366            main_window.ping_user_input();
367            unsafe {
368                let io = renderer.imgui().io_mut().inner();
369                let position = main_window
370                    .transform_position(Vector2::new(position.x as f32, position.y as f32));
371                io.AddMousePosEvent(position.x, position.y);
372            }
373        }
374        MouseWheel {
375            delta,
376            phase: winit::event::TouchPhase::Moved,
377            ..
378        } => {
379            main_window.ping_user_input();
380            let mut imgui = unsafe { renderer.imgui().set_current() };
381            unsafe {
382                let io = imgui.io_mut().inner();
383                let (h, v) = match delta {
384                    winit::event::MouseScrollDelta::LineDelta(h, v) => (*h, *v),
385                    winit::event::MouseScrollDelta::PixelDelta(d) => {
386                        let scale = io.DisplayFramebufferScale.x;
387                        let f_scale = ImGui_GetFontSize();
388                        let scale = scale * f_scale;
389                        (d.x as f32 / scale, d.y as f32 / scale)
390                    }
391                };
392                io.AddMouseWheelEvent(h, v);
393            }
394        }
395        MouseInput { state, button, .. } => {
396            main_window.ping_user_input();
397            unsafe {
398                let io = renderer.imgui().io_mut().inner();
399                if let Some(btn) = to_imgui_button(*button) {
400                    let pressed = *state == winit::event::ElementState::Pressed;
401                    io.AddMouseButtonEvent(btn.bits(), pressed);
402                }
403            }
404        }
405        CursorLeft { .. } => {
406            main_window.ping_user_input();
407            unsafe {
408                let io = renderer.imgui().io_mut().inner();
409                io.AddMousePosEvent(f32::MAX, f32::MAX);
410            }
411        }
412        Focused(focused) => {
413            main_window.ping_user_input();
414            unsafe {
415                let io = renderer.imgui().io_mut().inner();
416                io.AddFocusEvent(*focused);
417            }
418        }
419        _ => {}
420    }
421    let imgui = renderer.imgui();
422    EventResult::new(imgui, window_closed)
423}
424
425#[cfg(feature = "main-window")]
426mod main_window {
427    use super::*;
428    use std::future::Future;
429    mod fut;
430    use anyhow::{Result, anyhow};
431    use easy_imgui::Idler;
432    use easy_imgui_renderer::glow;
433    pub use fut::FutureBackCaller;
434    use glutin::{
435        config::{Config, ConfigTemplateBuilder},
436        context::{ContextApi, ContextAttributesBuilder},
437        display::GetGlDisplay,
438        surface::SurfaceAttributesBuilder,
439    };
440    use glutin_winit::DisplayBuilder;
441    use raw_window_handle::HasWindowHandle;
442    use winit::event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy};
443    use winit::window::WindowAttributes;
444
445    /// This type represents a `winit` window and an OpenGL context.
446    pub struct MainWindow {
447        gl_context: PossiblyCurrentContext,
448        // The surface must be dropped before the window.
449        surface: Surface<WindowSurface>,
450        window: Window,
451        matrix: Option<Matrix3<f32>>,
452        idler: Idler,
453    }
454
455    /// This is a [`MainWindow`] plus a [`Renderer`]. It is the ultimate `easy-imgui` object.
456    /// Instead of a literal `MainWindow` you can use any type that implements [`MainWindowRef`].
457    pub struct MainWindowWithRenderer {
458        main_window: MainWindow,
459        renderer: Renderer,
460        status: MainWindowStatus,
461    }
462
463    impl MainWindow {
464        /// Creates a `MainWindow` with default values.
465        pub fn new(event_loop: &ActiveEventLoop, wattr: WindowAttributes) -> Result<MainWindow> {
466            // For standard UI, we need as few fancy things as available
467            let score = |c: &Config| (c.num_samples(), c.depth_size(), c.stencil_size());
468            Self::with_gl_chooser(event_loop, wattr, |cfg1, cfg2| {
469                if score(&cfg2) < score(&cfg1) {
470                    cfg2
471                } else {
472                    cfg1
473                }
474            })
475        }
476        /// Creates a `MainWindow` with your own OpenGL context chooser.
477        ///
478        /// If you don't have specific OpenGL needs, prefer using [`MainWindow::new`]. If you do,
479        /// consider using a _FramebufferObject_ and do an offscreen rendering instead.
480        pub fn with_gl_chooser(
481            event_loop: &ActiveEventLoop,
482            wattr: WindowAttributes,
483            f_choose_cfg: impl FnMut(Config, Config) -> Config,
484        ) -> Result<MainWindow> {
485            let template = ConfigTemplateBuilder::new()
486                .prefer_hardware_accelerated(Some(true))
487                .with_depth_size(0)
488                .with_stencil_size(0);
489
490            let display_builder = DisplayBuilder::new().with_window_attributes(Some(wattr));
491            let (window, gl_config) = display_builder
492                .build(event_loop, template, |configs| {
493                    configs.reduce(f_choose_cfg).unwrap()
494                })
495                .map_err(|e| anyhow!("{:#?}", e))?;
496            let window = window.unwrap();
497            window.set_ime_allowed(true);
498            let raw_window_handle = Some(window.window_handle().unwrap().as_raw());
499            let gl_display = gl_config.display();
500            let context_attributes = ContextAttributesBuilder::new().build(raw_window_handle);
501            let fallback_context_attributes = ContextAttributesBuilder::new()
502                .with_context_api(ContextApi::Gles(None))
503                .build(raw_window_handle);
504
505            let mut not_current_gl_context = Some(unsafe {
506                gl_display
507                    .create_context(&gl_config, &context_attributes)
508                    .or_else(|_| {
509                        gl_display.create_context(&gl_config, &fallback_context_attributes)
510                    })?
511            });
512
513            let size = window.inner_size();
514
515            let (width, height): (u32, u32) = size.into();
516            let raw_window_handle = window.window_handle().unwrap().as_raw();
517            let attrs = SurfaceAttributesBuilder::<WindowSurface>::new().build(
518                raw_window_handle,
519                NonZeroU32::new(width).unwrap(),
520                NonZeroU32::new(height).unwrap(),
521            );
522
523            let surface = unsafe {
524                gl_config
525                    .display()
526                    .create_window_surface(&gl_config, &attrs)?
527            };
528            let gl_context = not_current_gl_context
529                .take()
530                .unwrap()
531                .make_current(&surface)?;
532
533            // Enable v-sync to avoid consuming too much CPU
534            let _ = surface.set_swap_interval(
535                &gl_context,
536                glutin::surface::SwapInterval::Wait(NonZeroU32::new(1).unwrap()),
537            );
538
539            Ok(MainWindow {
540                gl_context,
541                window,
542                surface,
543                matrix: None,
544                idler: Idler::default(),
545            })
546        }
547        /// Sets a custom matrix that converts physical mouse coordinates into logical ones.
548        pub fn set_matrix(&mut self, matrix: Option<Matrix3<f32>>) {
549            self.matrix = matrix;
550        }
551
552        /// Splits this window into its parts.
553        ///
554        /// # Safety
555        /// Do not drop the `window` without dropping the `surface` first.
556        pub unsafe fn into_pieces(
557            self,
558        ) -> (PossiblyCurrentContext, Surface<WindowSurface>, Window) {
559            (self.gl_context, self.surface, self.window)
560        }
561        /// Returns the `glutin` context.
562        pub fn glutin_context(&self) -> &PossiblyCurrentContext {
563            &self.gl_context
564        }
565        /// Creates a new `glow` OpenGL context for this window and the selected configuration.
566        pub fn create_gl_context(&self) -> glow::Context {
567            let dsp = self.gl_context.display();
568            unsafe { glow::Context::from_loader_function_cstr(|s| dsp.get_proc_address(s)) }
569        }
570        /// Gets a reference to the `winit` window.
571        pub fn window(&self) -> &Window {
572            &self.window
573        }
574        /// Returns the `glutin` surface.
575        pub fn surface(&self) -> &Surface<WindowSurface> {
576            &self.surface
577        }
578        /// Converts the given physical size to a logical size, using the window scale factor.
579        pub fn to_logical_size<X: Pixel, Y: Pixel>(&self, size: PhysicalSize<X>) -> LogicalSize<Y> {
580            let scale = self.window.scale_factor();
581            size.to_logical(scale)
582        }
583        /// Converts the given logical size to a physical size, using the window scale factor.
584        pub fn to_physical_size<X: Pixel, Y: Pixel>(
585            &self,
586            size: LogicalSize<X>,
587        ) -> PhysicalSize<Y> {
588            let scale = self.window.scale_factor();
589            size.to_physical(scale)
590        }
591        /// Converts the given physical position to a logical position, using the window scale factor.
592        pub fn to_logical_pos<X: Pixel, Y: Pixel>(
593            &self,
594            pos: PhysicalPosition<X>,
595        ) -> LogicalPosition<Y> {
596            let scale = self.window.scale_factor();
597            pos.to_logical(scale)
598        }
599        /// Converts the given logical position to a physical position, using the window scale factor.
600        pub fn to_physical_pos<X: Pixel, Y: Pixel>(
601            &self,
602            pos: LogicalPosition<X>,
603        ) -> PhysicalPosition<Y> {
604            let scale = self.window.scale_factor();
605            pos.to_physical(scale)
606        }
607    }
608
609    impl MainWindowWithRenderer {
610        /// Creates a new [`Renderer`] and attaches it to the given window.
611        pub fn new(main_window: MainWindow) -> Self {
612            Self::with_builder(main_window, &imgui::ContextBuilder::new())
613        }
614        /// Creates a new [`Renderer`] and attaches it to the given window.
615        ///
616        /// The `builder` argument can be used to modify the inner ImGui context.
617        pub fn with_builder(main_window: MainWindow, builder: &imgui::ContextBuilder) -> Self {
618            let gl = main_window.create_gl_context();
619            let renderer = Renderer::with_builder(std::rc::Rc::new(gl), builder).unwrap();
620            Self::new_with_renderer(main_window, renderer)
621        }
622        /// Sets the time after which the UI will stop rendering, if there is no user input.
623        pub fn set_idle_time(&mut self, time: Duration) {
624            self.main_window.idler.set_idle_time(time);
625        }
626        /// Sets the frame count after which the UI will stop rendering, if there is no user input.
627        ///
628        /// Note that by default V-Sync is enabled, and that will affect the frame rate.
629        pub fn set_idle_frame_count(&mut self, frame_count: u32) {
630            self.main_window.idler.set_idle_frame_count(frame_count);
631        }
632        /// Forces a rebuild of the UI.
633        ///
634        /// By default the window will stop rendering the UI after a while without user input. Use this
635        /// function to force a redraw because of some external factor.
636        pub fn ping_user_input(&mut self) {
637            self.main_window.idler.ping_user_input();
638        }
639        /// Gets a reference to the inner renderer.
640        pub fn renderer(&mut self) -> &mut Renderer {
641            &mut self.renderer
642        }
643        /// Gets a reference to the ImGui context by the renderer.
644        ///
645        /// Just like `self.renderer().imgui()`
646        pub fn imgui(&mut self) -> &mut imgui::Context {
647            self.renderer.imgui()
648        }
649        /// Gets a reference to the inner window.
650        pub fn main_window(&mut self) -> &mut MainWindow {
651            &mut self.main_window
652        }
653        /// Attaches the given window and renderer together.
654        pub fn new_with_renderer(main_window: MainWindow, mut renderer: Renderer) -> Self {
655            let w = main_window.window();
656            let size = w.inner_size();
657            let scale = w.scale_factor();
658            let size = size.to_logical::<f32>(scale);
659            renderer.set_size(Vector2::from(mint::Vector2::from(size)), scale as f32);
660
661            MainWindowWithRenderer {
662                main_window,
663                renderer,
664                status: MainWindowStatus::default(),
665            }
666        }
667        /// The main event function. Corresponds to winit's `ApplicationHandler::window_event`.
668        ///
669        /// It returns [`EventResult`]. You can use it to break the main loop, or ignore it, as you see fit.
670        /// It also informs of whether ImGui want the monopoly of the user input.
671        pub fn window_event(
672            &mut self,
673            app: &mut impl imgui::UiBuilder,
674            event: &winit::event::WindowEvent,
675            flags: EventFlags,
676        ) -> EventResult {
677            window_event(
678                &mut self.main_window,
679                &mut self.renderer,
680                &mut self.status,
681                app,
682                event,
683                flags,
684            )
685        }
686
687        /// Corresponds to winit's `ApplicationHandler::new_events`.
688        pub fn new_events(&mut self) {
689            new_events(&mut self.renderer, &mut self.status);
690        }
691        /// Corresponds to winit's `ApplicationHandler::about_to_wait`.
692        pub fn about_to_wait(&mut self) {
693            about_to_wait(&mut self.main_window, &mut self.renderer);
694        }
695    }
696
697    /// Main implementation of the `MainWindowRef` trait for an owned `MainWindow`.
698    impl MainWindowRef for MainWindow {
699        fn window(&self) -> &Window {
700            &self.window
701        }
702        fn pre_render(&mut self) {
703            self.idler.incr_frame();
704            let _ = self
705                .gl_context
706                .make_current(&self.surface)
707                .inspect_err(|e| log::error!("{e}"));
708        }
709        fn post_render(&mut self) {
710            self.window.pre_present_notify();
711            let _ = self
712                .surface
713                .swap_buffers(&self.gl_context)
714                .inspect_err(|e| log::error!("{e}"));
715        }
716        fn resize(&mut self, size: PhysicalSize<u32>) -> LogicalSize<f32> {
717            let width = NonZeroU32::new(size.width.max(1)).unwrap();
718            let height = NonZeroU32::new(size.height.max(1)).unwrap();
719            self.surface.resize(&self.gl_context, width, height);
720            self.to_logical_size::<_, f32>(size)
721        }
722        fn ping_user_input(&mut self) {
723            self.idler.ping_user_input();
724        }
725        fn about_to_wait(&mut self, pinged: bool) {
726            if pinged || self.idler.has_to_render() {
727                // No need to call set_control_flow(): doing a redraw will force extra Poll.
728                // Not doing it will default to Wait.
729                self.window.request_redraw();
730            }
731        }
732        fn transform_position(&self, pos: Vector2) -> Vector2 {
733            transform_position_with_optional_matrix(self, pos, &self.matrix)
734        }
735    }
736
737    /// This type is an aggregate of values retured by [`AppHandler`].
738    ///
739    /// With this you don't need to have a buch of `use` that you probably
740    /// don't care about.
741    ///
742    /// Since this is not `Send` it is always used from the main loop, and it can be
743    /// used to send non `Send` callbacks to the idle loop.
744    #[non_exhaustive]
745    pub struct Args<'a, A: Application> {
746        /// The main window.
747        pub window: &'a mut MainWindowWithRenderer,
748        /// The event loop.
749        pub event_loop: &'a ActiveEventLoop,
750        /// A proxy to send messages to the main loop.
751        pub event_proxy: &'a EventLoopProxy<AppEvent<A>>,
752        /// The custom application data.
753        pub data: &'a mut A::Data,
754    }
755
756    /// This type is a wrapper for `EventLoopProxy` that is not `Send`.
757    ///
758    /// Since it can only be used from the main loop, it can send events that are not `Send`.
759    pub struct LocalProxy<A: Application> {
760        event_proxy: EventLoopProxy<AppEvent<A>>,
761        // !Send + !Sync
762        pd: std::marker::PhantomData<*const ()>,
763    }
764
765    impl<A: Application> Clone for LocalProxy<A> {
766        fn clone(&self) -> Self {
767            LocalProxy {
768                event_proxy: self.event_proxy.clone(),
769                pd: std::marker::PhantomData,
770            }
771        }
772    }
773
774    macro_rules! local_proxy_impl {
775        () => {
776            /// Registers a future to be run during the idle step of the main loop.
777            pub fn spawn_idle<T: 'static, F: Future<Output = T> + 'static>(
778                &self,
779                f: F,
780            ) -> easy_imgui::future::FutureHandle<T> {
781                let idle_runner = fut::MyIdleRunner(self.event_proxy.clone());
782                unsafe { easy_imgui::future::spawn_idle(idle_runner, f) }
783            }
784            /// Registers a callback to be called during the idle step of the main loop.
785            pub fn run_idle<F: FnOnce(&mut A, Args<'_, A>) + 'static>(
786                &self,
787                f: F,
788            ) -> Result<(), winit::event_loop::EventLoopClosed<()>> {
789                // Self is !Send+!Sync, so this must be in the main loop,
790                // and the idle callback will be run in the same loop.
791                let f = send_wrapper::SendWrapper::new(f);
792                // If it fails, drop the message instead of returing it, because the
793                // message is Send but the f inside is not.
794                self.event_proxy
795                    .run_idle(move |app, args| (f.take())(app, args))
796                    .map_err(|_| winit::event_loop::EventLoopClosed(()))
797            }
798            /// Creates a `FutureBackCaller` for this application.
799            pub fn future_back(&self) -> FutureBackCaller<A> {
800                FutureBackCaller::new()
801            }
802        };
803    }
804
805    impl<A: Application> Args<'_, A> {
806        pub fn reborrow(&mut self) -> Args<'_, A> {
807            Args {
808                window: self.window,
809                event_loop: self.event_loop,
810                event_proxy: self.event_proxy,
811                data: self.data,
812            }
813        }
814        /// Creates a `LocalProxy` that is `Clone` but not `Send`.
815        pub fn local_proxy(&self) -> LocalProxy<A> {
816            LocalProxy {
817                event_proxy: self.event_proxy.clone(),
818                pd: std::marker::PhantomData,
819            }
820        }
821        /// Helper function to call `ping_user_input` in the main window.
822        pub fn ping_user_input(&mut self) {
823            self.window.ping_user_input();
824        }
825        local_proxy_impl! {}
826    }
827
828    impl<A: Application> LocalProxy<A> {
829        /// Gets the inner real proxy, that is `Send`.
830        pub fn event_proxy(&self) -> &EventLoopProxy<AppEvent<A>> {
831            &self.event_proxy
832        }
833        local_proxy_impl! {}
834    }
835
836    /// Trait that connects a `UiBuilder` with an `AppHandler`.
837    ///
838    /// Implement this to manage the main loop of your application.
839    pub trait Application: imgui::UiBuilder + Sized + 'static {
840        /// The custom event for the `EventLoop`, usually `()`.
841        type UserEvent: Send + 'static;
842        /// The custom data for your `AppHandler`.
843        type Data;
844
845        /// The `EventFlags` for this application. Usually the default is ok.
846        const EVENT_FLAGS: EventFlags = EventFlags::empty();
847
848        /// The main window has been created, please create the application.
849        fn new(args: Args<'_, Self>) -> Self;
850
851        /// A new window event has been received.
852        ///
853        /// When this is called the event has already been fed to the ImGui
854        /// context. The output is in `res`.
855        /// The default impl will end the application when the window is closed.
856        fn window_event(
857            &mut self,
858            args: Args<'_, Self>,
859            _event: winit::event::WindowEvent,
860            res: EventResult,
861        ) {
862            if res.window_closed {
863                args.event_loop.exit();
864            }
865        }
866
867        /// Advanced handling for window events.
868        ///
869        /// The default impl will pass the event to ImGui and then call `window_event`.
870        fn window_event_full(&mut self, args: Args<'_, Self>, event: winit::event::WindowEvent) {
871            let res = args.window.window_event(self, &event, Self::EVENT_FLAGS);
872            self.window_event(args, event, res);
873        }
874
875        /// A device event has been received.
876        ///
877        /// This event is not handled in any way, just passed laong.
878        fn device_event(
879            &mut self,
880            _args: Args<'_, Self>,
881            _device_id: winit::event::DeviceId,
882            _event: winit::event::DeviceEvent,
883        ) {
884        }
885
886        /// A custom event has been received.
887        fn user_event(&mut self, _args: Args<'_, Self>, _event: Self::UserEvent) {}
888
889        /// Corresponds to `winit` `suspended`` function.
890        fn suspended(&mut self, _args: Args<'_, Self>) {}
891
892        /// Corresponds to `winit` `resumed` function.
893        fn resumed(&mut self, _args: Args<'_, Self>) {}
894    }
895
896    /// The main event type to be used with `winit::EventLoop`.
897    ///
898    /// It is generic on the `Application` type.
899    #[non_exhaustive]
900    pub enum AppEvent<A: Application> {
901        /// Calls `ping_user_input` on the main window.
902        PingUserInput,
903        /// Runs the given callback in the main loop idle step, with the regular arguments.
904        #[allow(clippy::type_complexity)]
905        RunIdle(Box<dyn FnOnce(&mut A, Args<'_, A>) + Send + Sync>),
906        /// Runs the given callback in the main loop idle step, without arguments.
907        RunIdleSimple(Box<dyn FnOnce() + Send + Sync>),
908        /// Sends the custom user event.
909        User(A::UserEvent),
910    }
911
912    impl<A: Application> std::fmt::Debug for AppEvent<A> {
913        fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
914            write!(fmt, "<AppEvent>")
915        }
916    }
917
918    /// Helper trait to extend `winit::EventLoopProxy` with useful functions.
919    pub trait EventLoopExt<A: Application> {
920        /// Sends a `AppEvent::User` event.
921        fn send_user(
922            &self,
923            u: A::UserEvent,
924        ) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>>;
925        /// Sends a `AppEvent::PingUserInput` event.
926        fn ping_user_input(&self) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>>;
927        /// Sends a `AppEvent::RunIdle` event.
928        fn run_idle<F: FnOnce(&mut A, Args<'_, A>) + Send + Sync + 'static>(
929            &self,
930            f: F,
931        ) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>>;
932    }
933
934    impl<A: Application> EventLoopExt<A> for EventLoopProxy<AppEvent<A>> {
935        fn send_user(
936            &self,
937            u: A::UserEvent,
938        ) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>> {
939            self.send_event(AppEvent::User(u))
940        }
941        fn ping_user_input(&self) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>> {
942            self.send_event(AppEvent::PingUserInput)
943        }
944        fn run_idle<F: FnOnce(&mut A, Args<'_, A>) + Send + Sync + 'static>(
945            &self,
946            f: F,
947        ) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>> {
948            self.send_event(AppEvent::RunIdle(Box::new(f)))
949        }
950    }
951
952    /// Default implementation for `winit::application::ApplicationHandler`.
953    ///
954    /// The new `winit` requires an implementation of that trait to be able to use
955    /// an `EventLoop`, and everything, including the main window, will be done from
956    /// that.
957    /// This type implements this trait and does some basic tasks:
958    ///  * Creates the `MainWindowWithRenderer` object.
959    ///  * Forwards the events to your application object.
960    ///
961    /// For that it requires that your application object implements `UiBuilder` and
962    /// `Application`.
963    ///
964    /// If you have special needs you can skip this and write your own implementation
965    /// of `winit::application::ApplicationHandler`.
966    pub struct AppHandler<A: Application> {
967        builder: imgui::ContextBuilder,
968        wattrs: WindowAttributes,
969        event_proxy: EventLoopProxy<AppEvent<A>>,
970        window: Option<MainWindowWithRenderer>,
971        app: Option<A>,
972        app_data: A::Data,
973    }
974
975    impl<A: Application> AppHandler<A> {
976        /// Creates a new `AppHandler`.
977        ///
978        /// It creates an empty handler. It automatically creates an `EventLoopProxy`.
979        pub fn new(event_loop: &EventLoop<AppEvent<A>>, app_data: A::Data) -> Self {
980            AppHandler {
981                builder: imgui::ContextBuilder::new(),
982                wattrs: Window::default_attributes(),
983                event_proxy: event_loop.create_proxy(),
984                window: None,
985                app: None,
986                app_data,
987            }
988        }
989        /// Returns the `ContextBuilder` of this application.
990        ///
991        /// With this you can change the ImGui options before the context is created.
992        pub fn imgui_builder(&mut self) -> &mut imgui::ContextBuilder {
993            &mut self.builder
994        }
995        /// Sets the window attributes that will be used to create the main window.
996        pub fn set_attributes(&mut self, wattrs: WindowAttributes) {
997            self.wattrs = wattrs;
998        }
999        /// Gets the current window attributes.
1000        ///
1001        /// It returns a mutable reference, so you can modify it in-place, which is
1002        /// sometimes more convenient.
1003        pub fn attributes(&mut self) -> &mut WindowAttributes {
1004            &mut self.wattrs
1005        }
1006        /// Gets the custom data.
1007        pub fn data(&self) -> &A::Data {
1008            &self.app_data
1009        }
1010        /// Gets a mutable reference to the custom data.
1011        pub fn data_mut(&mut self) -> &mut A::Data {
1012            &mut self.app_data
1013        }
1014        /// Gets the inner app object.
1015        pub fn app(&self) -> Option<&A> {
1016            self.app.as_ref()
1017        }
1018        /// Gets a mutable reference to the inner app object.
1019        pub fn app_mut(&mut self) -> Option<&mut A> {
1020            self.app.as_mut()
1021        }
1022        /// Extracts the inner values.
1023        ///
1024        /// You may need this after the main loop has finished to get
1025        /// the result of your program execution.
1026        pub fn into_inner(self) -> (Option<A>, A::Data) {
1027            (self.app, self.app_data)
1028        }
1029
1030        /// Gets the inner `EventLoopProxy`.
1031        pub fn event_proxy(&self) -> &EventLoopProxy<AppEvent<A>> {
1032            &self.event_proxy
1033        }
1034    }
1035
1036    impl<A> winit::application::ApplicationHandler<AppEvent<A>> for AppHandler<A>
1037    where
1038        A: Application,
1039    {
1040        fn suspended(&mut self, event_loop: &ActiveEventLoop) {
1041            let Some(window) = self.window.as_mut() else {
1042                return;
1043            };
1044            if let Some(app) = &mut self.app {
1045                let args = Args {
1046                    window,
1047                    event_loop,
1048                    event_proxy: &self.event_proxy,
1049                    data: &mut self.app_data,
1050                };
1051                app.suspended(args);
1052            }
1053            self.window = None;
1054        }
1055        fn resumed(&mut self, event_loop: &ActiveEventLoop) {
1056            let main_window = MainWindow::new(event_loop, self.wattrs.clone()).unwrap();
1057            let mut window = MainWindowWithRenderer::with_builder(main_window, &self.builder);
1058
1059            let args = Args {
1060                window: &mut window,
1061                event_loop,
1062                event_proxy: &self.event_proxy,
1063                data: &mut self.app_data,
1064            };
1065            match &mut self.app {
1066                None => self.app = Some(A::new(args)),
1067                Some(app) => app.resumed(args),
1068            }
1069            self.window = Some(window);
1070        }
1071        fn window_event(
1072            &mut self,
1073            event_loop: &ActiveEventLoop,
1074            window_id: winit::window::WindowId,
1075            event: winit::event::WindowEvent,
1076        ) {
1077            let (Some(window), Some(app)) = (self.window.as_mut(), self.app.as_mut()) else {
1078                return;
1079            };
1080            let w = window.main_window();
1081            if w.window().id() != window_id {
1082                return;
1083            }
1084
1085            let args = Args {
1086                window,
1087                event_loop,
1088                event_proxy: &self.event_proxy,
1089                data: &mut self.app_data,
1090            };
1091            app.window_event_full(args, event);
1092        }
1093        fn device_event(
1094            &mut self,
1095            event_loop: &ActiveEventLoop,
1096            device_id: winit::event::DeviceId,
1097            event: winit::event::DeviceEvent,
1098        ) {
1099            let (Some(window), Some(app)) = (self.window.as_mut(), self.app.as_mut()) else {
1100                return;
1101            };
1102            let args = Args {
1103                window,
1104                event_loop,
1105                event_proxy: &self.event_proxy,
1106                data: &mut self.app_data,
1107            };
1108            app.device_event(args, device_id, event);
1109        }
1110        fn user_event(&mut self, event_loop: &ActiveEventLoop, event: AppEvent<A>) {
1111            let (Some(window), Some(app)) = (self.window.as_mut(), self.app.as_mut()) else {
1112                return;
1113            };
1114            let args = Args {
1115                window,
1116                event_loop,
1117                event_proxy: &self.event_proxy,
1118                data: &mut self.app_data,
1119            };
1120
1121            match event {
1122                AppEvent::PingUserInput => window.ping_user_input(),
1123                AppEvent::RunIdle(f) => f(app, args),
1124                AppEvent::RunIdleSimple(f) => fut::FutureBackCaller::prepare(app, args, f),
1125                AppEvent::User(uevent) => app.user_event(args, uevent),
1126            }
1127        }
1128        fn new_events(&mut self, _event_loop: &ActiveEventLoop, _cause: winit::event::StartCause) {
1129            let Some(window) = self.window.as_mut() else {
1130                return;
1131            };
1132            window.new_events();
1133        }
1134        fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
1135            let Some(window) = self.window.as_mut() else {
1136                return;
1137            };
1138            window.about_to_wait();
1139        }
1140    }
1141}
1142
1143#[cfg(feature = "main-window")]
1144pub use main_window::*;