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        ScaleFactorChanged { scale_factor, .. } => {
286            if !flags.contains(EventFlags::DoNotResize) {
287                main_window.ping_user_input();
288                let scale_factor = main_window.set_scale_factor(*scale_factor as f32);
289                unsafe {
290                    let io = renderer.imgui().io_mut().inner();
291                    // Keep the mouse in the same relative position: maybe it is wrong, but it is
292                    // the best guess we can do.
293                    let old_scale_factor = io.DisplayFramebufferScale.x;
294                    if io.MousePos.x.is_finite() && io.MousePos.y.is_finite() {
295                        io.MousePos.x *= scale_factor / old_scale_factor;
296                        io.MousePos.y *= scale_factor / old_scale_factor;
297                    }
298                }
299                let size = renderer.size();
300                renderer.set_size(size, scale_factor);
301            }
302        }
303        ModifiersChanged(mods) => {
304            main_window.ping_user_input();
305            unsafe {
306                let io = renderer.imgui().io_mut().inner();
307                io.AddKeyEvent(imgui::Key::ModCtrl.bits(), mods.state().control_key());
308                io.AddKeyEvent(imgui::Key::ModShift.bits(), mods.state().shift_key());
309                io.AddKeyEvent(imgui::Key::ModAlt.bits(), mods.state().alt_key());
310                io.AddKeyEvent(imgui::Key::ModSuper.bits(), mods.state().super_key());
311            }
312        }
313        KeyboardInput {
314            event:
315                winit::event::KeyEvent {
316                    physical_key,
317                    text,
318                    state,
319                    ..
320                },
321            is_synthetic: false,
322            ..
323        } => {
324            main_window.ping_user_input();
325            let pressed = *state == winit::event::ElementState::Pressed;
326            if let Some(key) = to_imgui_key(*physical_key) {
327                unsafe {
328                    let io = renderer.imgui().io_mut().inner();
329                    io.AddKeyEvent(key.bits(), pressed);
330
331                    use winit::keyboard::KeyCode::*;
332                    if let PhysicalKey::Code(keycode) = physical_key {
333                        let kmod = match keycode {
334                            ControlLeft | ControlRight => Some(imgui::Key::ModCtrl),
335                            ShiftLeft | ShiftRight => Some(imgui::Key::ModShift),
336                            AltLeft | AltRight => Some(imgui::Key::ModAlt),
337                            SuperLeft | SuperRight => Some(imgui::Key::ModSuper),
338                            _ => None,
339                        };
340                        if let Some(kmod) = kmod {
341                            io.AddKeyEvent(kmod.bits(), pressed);
342                        }
343                    }
344                }
345            }
346            if pressed && let Some(text) = text {
347                unsafe {
348                    let io = renderer.imgui().io_mut().inner();
349                    for c in text.chars() {
350                        io.AddInputCharacter(c as u32);
351                    }
352                }
353            }
354        }
355        Ime(Commit(text)) => {
356            main_window.ping_user_input();
357            unsafe {
358                let io = renderer.imgui().io_mut().inner();
359                for c in text.chars() {
360                    io.AddInputCharacter(c as u32);
361                }
362            }
363        }
364        CursorMoved { position, .. } => {
365            main_window.ping_user_input();
366            unsafe {
367                let io = renderer.imgui().io_mut().inner();
368                let position = main_window
369                    .transform_position(Vector2::new(position.x as f32, position.y as f32));
370                io.AddMousePosEvent(position.x, position.y);
371            }
372        }
373        MouseWheel {
374            delta,
375            phase: winit::event::TouchPhase::Moved,
376            ..
377        } => {
378            main_window.ping_user_input();
379            let mut imgui = unsafe { renderer.imgui().set_current() };
380            unsafe {
381                let io = imgui.io_mut().inner();
382                let (h, v) = match delta {
383                    winit::event::MouseScrollDelta::LineDelta(h, v) => (*h, *v),
384                    winit::event::MouseScrollDelta::PixelDelta(d) => {
385                        let scale = io.DisplayFramebufferScale.x;
386                        let f_scale = ImGui_GetFontSize();
387                        let scale = scale * f_scale;
388                        (d.x as f32 / scale, d.y as f32 / scale)
389                    }
390                };
391                io.AddMouseWheelEvent(h, v);
392            }
393        }
394        MouseInput { state, button, .. } => {
395            main_window.ping_user_input();
396            unsafe {
397                let io = renderer.imgui().io_mut().inner();
398                if let Some(btn) = to_imgui_button(*button) {
399                    let pressed = *state == winit::event::ElementState::Pressed;
400                    io.AddMouseButtonEvent(btn.bits(), pressed);
401                }
402            }
403        }
404        CursorLeft { .. } => {
405            main_window.ping_user_input();
406            unsafe {
407                let io = renderer.imgui().io_mut().inner();
408                io.AddMousePosEvent(f32::MAX, f32::MAX);
409            }
410        }
411        Focused(focused) => {
412            main_window.ping_user_input();
413            unsafe {
414                let io = renderer.imgui().io_mut().inner();
415                io.AddFocusEvent(*focused);
416            }
417        }
418        _ => {}
419    }
420    let imgui = renderer.imgui();
421    EventResult::new(imgui, window_closed)
422}
423
424#[cfg(feature = "main-window")]
425mod main_window {
426    use super::*;
427    use std::future::Future;
428    mod fut;
429    use anyhow::{Result, anyhow};
430    use easy_imgui::Idler;
431    use easy_imgui_renderer::glow;
432    pub use fut::FutureBackCaller;
433    use glutin::{
434        config::{Config, ConfigTemplateBuilder},
435        context::{ContextApi, ContextAttributesBuilder},
436        display::GetGlDisplay,
437        surface::SurfaceAttributesBuilder,
438    };
439    use glutin_winit::DisplayBuilder;
440    use raw_window_handle::HasWindowHandle;
441    use winit::event_loop::{ActiveEventLoop, EventLoop, EventLoopProxy};
442    use winit::window::WindowAttributes;
443
444    /// This type represents a `winit` window and an OpenGL context.
445    pub struct MainWindow {
446        gl_context: PossiblyCurrentContext,
447        // The surface must be dropped before the window.
448        surface: Surface<WindowSurface>,
449        window: Window,
450        matrix: Option<Matrix3<f32>>,
451        idler: Idler,
452    }
453
454    /// This is a [`MainWindow`] plus a [`Renderer`]. It is the ultimate `easy-imgui` object.
455    /// Instead of a literal `MainWindow` you can use any type that implements [`MainWindowRef`].
456    pub struct MainWindowWithRenderer {
457        main_window: MainWindow,
458        renderer: Renderer,
459        status: MainWindowStatus,
460    }
461
462    impl MainWindow {
463        /// Creates a `MainWindow` with default values.
464        pub fn new(event_loop: &ActiveEventLoop, wattr: WindowAttributes) -> Result<MainWindow> {
465            // For standard UI, we need as few fancy things as available
466            let score = |c: &Config| (c.num_samples(), c.depth_size(), c.stencil_size());
467            Self::with_gl_chooser(event_loop, wattr, |cfg1, cfg2| {
468                if score(&cfg2) < score(&cfg1) {
469                    cfg2
470                } else {
471                    cfg1
472                }
473            })
474        }
475        /// Creates a `MainWindow` with your own OpenGL context chooser.
476        ///
477        /// If you don't have specific OpenGL needs, prefer using [`MainWindow::new`]. If you do,
478        /// consider using a _FramebufferObject_ and do an offscreen rendering instead.
479        pub fn with_gl_chooser(
480            event_loop: &ActiveEventLoop,
481            wattr: WindowAttributes,
482            f_choose_cfg: impl FnMut(Config, Config) -> Config,
483        ) -> Result<MainWindow> {
484            let template = ConfigTemplateBuilder::new()
485                .prefer_hardware_accelerated(Some(true))
486                .with_depth_size(0)
487                .with_stencil_size(0);
488
489            let display_builder = DisplayBuilder::new().with_window_attributes(Some(wattr));
490            let (window, gl_config) = display_builder
491                .build(event_loop, template, |configs| {
492                    configs.reduce(f_choose_cfg).unwrap()
493                })
494                .map_err(|e| anyhow!("{:#?}", e))?;
495            let window = window.unwrap();
496            window.set_ime_allowed(true);
497            let raw_window_handle = Some(window.window_handle().unwrap().as_raw());
498            let gl_display = gl_config.display();
499            let context_attributes = ContextAttributesBuilder::new().build(raw_window_handle);
500            let fallback_context_attributes = ContextAttributesBuilder::new()
501                .with_context_api(ContextApi::Gles(None))
502                .build(raw_window_handle);
503
504            let mut not_current_gl_context = Some(unsafe {
505                gl_display
506                    .create_context(&gl_config, &context_attributes)
507                    .or_else(|_| {
508                        gl_display.create_context(&gl_config, &fallback_context_attributes)
509                    })?
510            });
511
512            let size = window.inner_size();
513
514            let (width, height): (u32, u32) = size.into();
515            let raw_window_handle = window.window_handle().unwrap().as_raw();
516            let attrs = SurfaceAttributesBuilder::<WindowSurface>::new().build(
517                raw_window_handle,
518                NonZeroU32::new(width).unwrap(),
519                NonZeroU32::new(height).unwrap(),
520            );
521
522            let surface = unsafe {
523                gl_config
524                    .display()
525                    .create_window_surface(&gl_config, &attrs)?
526            };
527            let gl_context = not_current_gl_context
528                .take()
529                .unwrap()
530                .make_current(&surface)?;
531
532            // Enable v-sync to avoid consuming too much CPU
533            let _ = surface.set_swap_interval(
534                &gl_context,
535                glutin::surface::SwapInterval::Wait(NonZeroU32::new(1).unwrap()),
536            );
537
538            Ok(MainWindow {
539                gl_context,
540                window,
541                surface,
542                matrix: None,
543                idler: Idler::default(),
544            })
545        }
546        /// Sets a custom matrix that converts physical mouse coordinates into logical ones.
547        pub fn set_matrix(&mut self, matrix: Option<Matrix3<f32>>) {
548            self.matrix = matrix;
549        }
550
551        /// Splits this window into its parts.
552        ///
553        /// # Safety
554        /// Do not drop the `window` without dropping the `surface` first.
555        pub unsafe fn into_pieces(
556            self,
557        ) -> (PossiblyCurrentContext, Surface<WindowSurface>, Window) {
558            (self.gl_context, self.surface, self.window)
559        }
560        /// Returns the `glutin` context.
561        pub fn glutin_context(&self) -> &PossiblyCurrentContext {
562            &self.gl_context
563        }
564        /// Creates a new `glow` OpenGL context for this window and the selected configuration.
565        pub fn create_gl_context(&self) -> glow::Context {
566            let dsp = self.gl_context.display();
567            unsafe { glow::Context::from_loader_function_cstr(|s| dsp.get_proc_address(s)) }
568        }
569        /// Gets a reference to the `winit` window.
570        pub fn window(&self) -> &Window {
571            &self.window
572        }
573        /// Returns the `glutin` surface.
574        pub fn surface(&self) -> &Surface<WindowSurface> {
575            &self.surface
576        }
577        /// Converts the given physical size to a logical size, using the window scale factor.
578        pub fn to_logical_size<X: Pixel, Y: Pixel>(&self, size: PhysicalSize<X>) -> LogicalSize<Y> {
579            let scale = self.window.scale_factor();
580            size.to_logical(scale)
581        }
582        /// Converts the given logical size to a physical size, using the window scale factor.
583        pub fn to_physical_size<X: Pixel, Y: Pixel>(
584            &self,
585            size: LogicalSize<X>,
586        ) -> PhysicalSize<Y> {
587            let scale = self.window.scale_factor();
588            size.to_physical(scale)
589        }
590        /// Converts the given physical position to a logical position, using the window scale factor.
591        pub fn to_logical_pos<X: Pixel, Y: Pixel>(
592            &self,
593            pos: PhysicalPosition<X>,
594        ) -> LogicalPosition<Y> {
595            let scale = self.window.scale_factor();
596            pos.to_logical(scale)
597        }
598        /// Converts the given logical position to a physical position, using the window scale factor.
599        pub fn to_physical_pos<X: Pixel, Y: Pixel>(
600            &self,
601            pos: LogicalPosition<X>,
602        ) -> PhysicalPosition<Y> {
603            let scale = self.window.scale_factor();
604            pos.to_physical(scale)
605        }
606    }
607
608    impl MainWindowWithRenderer {
609        /// Creates a new [`Renderer`] and attaches it to the given window.
610        pub fn new(main_window: MainWindow) -> Self {
611            Self::with_builder(main_window, &imgui::ContextBuilder::new())
612        }
613        /// Creates a new [`Renderer`] and attaches it to the given window.
614        ///
615        /// The `builder` argument can be used to modify the inner ImGui context.
616        pub fn with_builder(main_window: MainWindow, builder: &imgui::ContextBuilder) -> Self {
617            let gl = main_window.create_gl_context();
618            let renderer = Renderer::with_builder(std::rc::Rc::new(gl), builder).unwrap();
619            Self::new_with_renderer(main_window, renderer)
620        }
621        /// Sets the time after which the UI will stop rendering, if there is no user input.
622        pub fn set_idle_time(&mut self, time: Duration) {
623            self.main_window.idler.set_idle_time(time);
624        }
625        /// Sets the frame count after which the UI will stop rendering, if there is no user input.
626        ///
627        /// Note that by default V-Sync is enabled, and that will affect the frame rate.
628        pub fn set_idle_frame_count(&mut self, frame_count: u32) {
629            self.main_window.idler.set_idle_frame_count(frame_count);
630        }
631        /// Forces a rebuild of the UI.
632        ///
633        /// By default the window will stop rendering the UI after a while without user input. Use this
634        /// function to force a redraw because of some external factor.
635        pub fn ping_user_input(&mut self) {
636            self.main_window.idler.ping_user_input();
637        }
638        /// Gets a reference to the inner renderer.
639        pub fn renderer(&mut self) -> &mut Renderer {
640            &mut self.renderer
641        }
642        /// Gets a reference to the ImGui context by the renderer.
643        ///
644        /// Just like `self.renderer().imgui()`
645        pub fn imgui(&mut self) -> &mut imgui::Context {
646            self.renderer.imgui()
647        }
648        /// Gets a reference to the inner window.
649        pub fn main_window(&mut self) -> &mut MainWindow {
650            &mut self.main_window
651        }
652        /// Attaches the given window and renderer together.
653        pub fn new_with_renderer(main_window: MainWindow, mut renderer: Renderer) -> Self {
654            let w = main_window.window();
655            let size = w.inner_size();
656            let scale = w.scale_factor();
657            let size = size.to_logical::<f32>(scale);
658            renderer.set_size(Vector2::from(mint::Vector2::from(size)), scale as f32);
659
660            MainWindowWithRenderer {
661                main_window,
662                renderer,
663                status: MainWindowStatus::default(),
664            }
665        }
666        /// The main event function. Corresponds to winit's `ApplicationHandler::window_event`.
667        ///
668        /// It returns [`EventResult`]. You can use it to break the main loop, or ignore it, as you see fit.
669        /// It also informs of whether ImGui want the monopoly of the user input.
670        pub fn window_event(
671            &mut self,
672            app: &mut impl imgui::UiBuilder,
673            event: &winit::event::WindowEvent,
674            flags: EventFlags,
675        ) -> EventResult {
676            window_event(
677                &mut self.main_window,
678                &mut self.renderer,
679                &mut self.status,
680                app,
681                event,
682                flags,
683            )
684        }
685
686        /// Corresponds to winit's `ApplicationHandler::new_events`.
687        pub fn new_events(&mut self) {
688            new_events(&mut self.renderer, &mut self.status);
689        }
690        /// Corresponds to winit's `ApplicationHandler::about_to_wait`.
691        pub fn about_to_wait(&mut self) {
692            about_to_wait(&mut self.main_window, &mut self.renderer);
693        }
694    }
695
696    /// Main implementation of the `MainWindowRef` trait for an owned `MainWindow`.
697    impl MainWindowRef for MainWindow {
698        fn window(&self) -> &Window {
699            &self.window
700        }
701        fn pre_render(&mut self) {
702            self.idler.incr_frame();
703            let _ = self
704                .gl_context
705                .make_current(&self.surface)
706                .inspect_err(|e| log::error!("{e}"));
707        }
708        fn post_render(&mut self) {
709            self.window.pre_present_notify();
710            let _ = self
711                .surface
712                .swap_buffers(&self.gl_context)
713                .inspect_err(|e| log::error!("{e}"));
714        }
715        fn resize(&mut self, size: PhysicalSize<u32>) -> LogicalSize<f32> {
716            let width = NonZeroU32::new(size.width.max(1)).unwrap();
717            let height = NonZeroU32::new(size.height.max(1)).unwrap();
718            self.surface.resize(&self.gl_context, width, height);
719            self.to_logical_size::<_, f32>(size)
720        }
721        fn ping_user_input(&mut self) {
722            self.idler.ping_user_input();
723        }
724        fn about_to_wait(&mut self, pinged: bool) {
725            if pinged || self.idler.has_to_render() {
726                // No need to call set_control_flow(): doing a redraw will force extra Poll.
727                // Not doing it will default to Wait.
728                self.window.request_redraw();
729            }
730        }
731        fn transform_position(&self, pos: Vector2) -> Vector2 {
732            transform_position_with_optional_matrix(self, pos, &self.matrix)
733        }
734    }
735
736    /// This type is an aggregate of values retured by [`AppHandler`].
737    ///
738    /// With this you don't need to have a buch of `use` that you probably
739    /// don't care about.
740    ///
741    /// Since this is not `Send` it is always used from the main loop, and it can be
742    /// used to send non `Send` callbacks to the idle loop.
743    #[non_exhaustive]
744    pub struct Args<'a, A: Application> {
745        /// The main window.
746        pub window: &'a mut MainWindowWithRenderer,
747        /// The event loop.
748        pub event_loop: &'a ActiveEventLoop,
749        /// A proxy to send messages to the main loop.
750        pub event_proxy: &'a EventLoopProxy<AppEvent<A>>,
751        /// The custom application data.
752        pub data: &'a mut A::Data,
753    }
754
755    /// This type is a wrapper for `EventLoopProxy` that is not `Send`.
756    ///
757    /// Since it can only be used from the main loop, it can send events that are not `Send`.
758    pub struct LocalProxy<A: Application> {
759        event_proxy: EventLoopProxy<AppEvent<A>>,
760        // !Send + !Sync
761        pd: std::marker::PhantomData<*const ()>,
762    }
763
764    impl<A: Application> Clone for LocalProxy<A> {
765        fn clone(&self) -> Self {
766            LocalProxy {
767                event_proxy: self.event_proxy.clone(),
768                pd: std::marker::PhantomData,
769            }
770        }
771    }
772
773    macro_rules! local_proxy_impl {
774        () => {
775            /// Registers a future to be run during the idle step of the main loop.
776            pub fn spawn_idle<T: 'static, F: Future<Output = T> + 'static>(
777                &self,
778                f: F,
779            ) -> easy_imgui::future::FutureHandle<T> {
780                let idle_runner = fut::MyIdleRunner(self.event_proxy.clone());
781                unsafe { easy_imgui::future::spawn_idle(idle_runner, f) }
782            }
783            /// Registers a callback to be called during the idle step of the main loop.
784            pub fn run_idle<F: FnOnce(&mut A, Args<'_, A>) + 'static>(
785                &self,
786                f: F,
787            ) -> Result<(), winit::event_loop::EventLoopClosed<()>> {
788                // Self is !Send+!Sync, so this must be in the main loop,
789                // and the idle callback will be run in the same loop.
790                let f = send_wrapper::SendWrapper::new(f);
791                // If it fails, drop the message instead of returing it, because the
792                // message is Send but the f inside is not.
793                self.event_proxy
794                    .run_idle(move |app, args| (f.take())(app, args))
795                    .map_err(|_| winit::event_loop::EventLoopClosed(()))
796            }
797            /// Creates a `FutureBackCaller` for this application.
798            pub fn future_back(&self) -> FutureBackCaller<A> {
799                FutureBackCaller::new()
800            }
801        };
802    }
803
804    impl<A: Application> Args<'_, A> {
805        pub fn reborrow(&mut self) -> Args<'_, A> {
806            Args {
807                window: self.window,
808                event_loop: self.event_loop,
809                event_proxy: self.event_proxy,
810                data: self.data,
811            }
812        }
813        /// Creates a `LocalProxy` that is `Clone` but not `Send`.
814        pub fn local_proxy(&self) -> LocalProxy<A> {
815            LocalProxy {
816                event_proxy: self.event_proxy.clone(),
817                pd: std::marker::PhantomData,
818            }
819        }
820        /// Helper function to call `ping_user_input` in the main window.
821        pub fn ping_user_input(&mut self) {
822            self.window.ping_user_input();
823        }
824        local_proxy_impl! {}
825    }
826
827    impl<A: Application> LocalProxy<A> {
828        /// Gets the inner real proxy, that is `Send`.
829        pub fn event_proxy(&self) -> &EventLoopProxy<AppEvent<A>> {
830            &self.event_proxy
831        }
832        local_proxy_impl! {}
833    }
834
835    /// Trait that connects a `UiBuilder` with an `AppHandler`.
836    ///
837    /// Implement this to manage the main loop of your application.
838    pub trait Application: imgui::UiBuilder + Sized + 'static {
839        /// The custom event for the `EventLoop`, usually `()`.
840        type UserEvent: Send + 'static;
841        /// The custom data for your `AppHandler`.
842        type Data;
843
844        /// The `EventFlags` for this application. Usually the default is ok.
845        const EVENT_FLAGS: EventFlags = EventFlags::empty();
846
847        /// The main window has been created, please create the application.
848        fn new(args: Args<'_, Self>) -> Self;
849
850        /// A new window event has been received.
851        ///
852        /// When this is called the event has already been fed to the ImGui
853        /// context. The output is in `res`.
854        /// The default impl will end the application when the window is closed.
855        fn window_event(
856            &mut self,
857            args: Args<'_, Self>,
858            _event: winit::event::WindowEvent,
859            res: EventResult,
860        ) {
861            if res.window_closed {
862                args.event_loop.exit();
863            }
864        }
865
866        /// Advanced handling for window events.
867        ///
868        /// The default impl will pass the event to ImGui and then call `window_event`.
869        fn window_event_full(&mut self, args: Args<'_, Self>, event: winit::event::WindowEvent) {
870            let res = args.window.window_event(self, &event, Self::EVENT_FLAGS);
871            self.window_event(args, event, res);
872        }
873
874        /// A device event has been received.
875        ///
876        /// This event is not handled in any way, just passed laong.
877        fn device_event(
878            &mut self,
879            _args: Args<'_, Self>,
880            _device_id: winit::event::DeviceId,
881            _event: winit::event::DeviceEvent,
882        ) {
883        }
884
885        /// A custom event has been received.
886        fn user_event(&mut self, _args: Args<'_, Self>, _event: Self::UserEvent) {}
887
888        /// Corresponds to `winit` `suspended`` function.
889        fn suspended(&mut self, _args: Args<'_, Self>) {}
890
891        /// Corresponds to `winit` `resumed` function.
892        fn resumed(&mut self, _args: Args<'_, Self>) {}
893    }
894
895    /// The main event type to be used with `winit::EventLoop`.
896    ///
897    /// It is generic on the `Application` type.
898    #[non_exhaustive]
899    pub enum AppEvent<A: Application> {
900        /// Calls `ping_user_input` on the main window.
901        PingUserInput,
902        /// Runs the given callback in the main loop idle step, with the regular arguments.
903        #[allow(clippy::type_complexity)]
904        RunIdle(Box<dyn FnOnce(&mut A, Args<'_, A>) + Send + Sync>),
905        /// Runs the given callback in the main loop idle step, without arguments.
906        RunIdleSimple(Box<dyn FnOnce() + Send + Sync>),
907        /// Sends the custom user event.
908        User(A::UserEvent),
909    }
910
911    impl<A: Application> std::fmt::Debug for AppEvent<A> {
912        fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
913            write!(fmt, "<AppEvent>")
914        }
915    }
916
917    /// Helper trait to extend `winit::EventLoopProxy` with useful functions.
918    pub trait EventLoopExt<A: Application> {
919        /// Sends a `AppEvent::User` event.
920        fn send_user(
921            &self,
922            u: A::UserEvent,
923        ) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>>;
924        /// Sends a `AppEvent::PingUserInput` event.
925        fn ping_user_input(&self) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>>;
926        /// Sends a `AppEvent::RunIdle` event.
927        fn run_idle<F: FnOnce(&mut A, Args<'_, A>) + Send + Sync + 'static>(
928            &self,
929            f: F,
930        ) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>>;
931    }
932
933    impl<A: Application> EventLoopExt<A> for EventLoopProxy<AppEvent<A>> {
934        fn send_user(
935            &self,
936            u: A::UserEvent,
937        ) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>> {
938            self.send_event(AppEvent::User(u))
939        }
940        fn ping_user_input(&self) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>> {
941            self.send_event(AppEvent::PingUserInput)
942        }
943        fn run_idle<F: FnOnce(&mut A, Args<'_, A>) + Send + Sync + 'static>(
944            &self,
945            f: F,
946        ) -> Result<(), winit::event_loop::EventLoopClosed<AppEvent<A>>> {
947            self.send_event(AppEvent::RunIdle(Box::new(f)))
948        }
949    }
950
951    /// Default implementation for `winit::application::ApplicationHandler`.
952    ///
953    /// The new `winit` requires an implementation of that trait to be able to use
954    /// an `EventLoop`, and everything, including the main window, will be done from
955    /// that.
956    /// This type implements this trait and does some basic tasks:
957    ///  * Creates the `MainWindowWithRenderer` object.
958    ///  * Forwards the events to your application object.
959    ///
960    /// For that it requires that your application object implements `UiBuilder` and
961    /// `Application`.
962    ///
963    /// If you have special needs you can skip this and write your own implementation
964    /// of `winit::application::ApplicationHandler`.
965    pub struct AppHandler<A: Application> {
966        builder: imgui::ContextBuilder,
967        wattrs: WindowAttributes,
968        event_proxy: EventLoopProxy<AppEvent<A>>,
969        window: Option<MainWindowWithRenderer>,
970        app: Option<A>,
971        app_data: A::Data,
972    }
973
974    impl<A: Application> AppHandler<A> {
975        /// Creates a new `AppHandler`.
976        ///
977        /// It creates an empty handler. It automatically creates an `EventLoopProxy`.
978        pub fn new(event_loop: &EventLoop<AppEvent<A>>, app_data: A::Data) -> Self {
979            AppHandler {
980                builder: imgui::ContextBuilder::new(),
981                wattrs: Window::default_attributes(),
982                event_proxy: event_loop.create_proxy(),
983                window: None,
984                app: None,
985                app_data,
986            }
987        }
988        /// Returns the `ContextBuilder` of this application.
989        ///
990        /// With this you can change the ImGui options before the context is created.
991        pub fn imgui_builder(&mut self) -> &mut imgui::ContextBuilder {
992            &mut self.builder
993        }
994        /// Sets the window attributes that will be used to create the main window.
995        pub fn set_attributes(&mut self, wattrs: WindowAttributes) {
996            self.wattrs = wattrs;
997        }
998        /// Gets the current window attributes.
999        ///
1000        /// It returns a mutable reference, so you can modify it in-place, which is
1001        /// sometimes more convenient.
1002        pub fn attributes(&mut self) -> &mut WindowAttributes {
1003            &mut self.wattrs
1004        }
1005        /// Gets the custom data.
1006        pub fn data(&self) -> &A::Data {
1007            &self.app_data
1008        }
1009        /// Gets a mutable reference to the custom data.
1010        pub fn data_mut(&mut self) -> &mut A::Data {
1011            &mut self.app_data
1012        }
1013        /// Gets the inner app object.
1014        pub fn app(&self) -> Option<&A> {
1015            self.app.as_ref()
1016        }
1017        /// Gets a mutable reference to the inner app object.
1018        pub fn app_mut(&mut self) -> Option<&mut A> {
1019            self.app.as_mut()
1020        }
1021        /// Extracts the inner values.
1022        ///
1023        /// You may need this after the main loop has finished to get
1024        /// the result of your program execution.
1025        pub fn into_inner(self) -> (Option<A>, A::Data) {
1026            (self.app, self.app_data)
1027        }
1028
1029        /// Gets the inner `EventLoopProxy`.
1030        pub fn event_proxy(&self) -> &EventLoopProxy<AppEvent<A>> {
1031            &self.event_proxy
1032        }
1033    }
1034
1035    impl<A> winit::application::ApplicationHandler<AppEvent<A>> for AppHandler<A>
1036    where
1037        A: Application,
1038    {
1039        fn suspended(&mut self, event_loop: &ActiveEventLoop) {
1040            let Some(window) = self.window.as_mut() else {
1041                return;
1042            };
1043            if let Some(app) = &mut self.app {
1044                let args = Args {
1045                    window,
1046                    event_loop,
1047                    event_proxy: &self.event_proxy,
1048                    data: &mut self.app_data,
1049                };
1050                app.suspended(args);
1051            }
1052            self.window = None;
1053        }
1054        fn resumed(&mut self, event_loop: &ActiveEventLoop) {
1055            let main_window = MainWindow::new(event_loop, self.wattrs.clone()).unwrap();
1056            let mut window = MainWindowWithRenderer::with_builder(main_window, &self.builder);
1057
1058            let args = Args {
1059                window: &mut window,
1060                event_loop,
1061                event_proxy: &self.event_proxy,
1062                data: &mut self.app_data,
1063            };
1064            match &mut self.app {
1065                None => self.app = Some(A::new(args)),
1066                Some(app) => app.resumed(args),
1067            }
1068            self.window = Some(window);
1069        }
1070        fn window_event(
1071            &mut self,
1072            event_loop: &ActiveEventLoop,
1073            window_id: winit::window::WindowId,
1074            event: winit::event::WindowEvent,
1075        ) {
1076            let (Some(window), Some(app)) = (self.window.as_mut(), self.app.as_mut()) else {
1077                return;
1078            };
1079            let w = window.main_window();
1080            if w.window().id() != window_id {
1081                return;
1082            }
1083
1084            let args = Args {
1085                window,
1086                event_loop,
1087                event_proxy: &self.event_proxy,
1088                data: &mut self.app_data,
1089            };
1090            app.window_event_full(args, event);
1091        }
1092        fn device_event(
1093            &mut self,
1094            event_loop: &ActiveEventLoop,
1095            device_id: winit::event::DeviceId,
1096            event: winit::event::DeviceEvent,
1097        ) {
1098            let (Some(window), Some(app)) = (self.window.as_mut(), self.app.as_mut()) else {
1099                return;
1100            };
1101            let args = Args {
1102                window,
1103                event_loop,
1104                event_proxy: &self.event_proxy,
1105                data: &mut self.app_data,
1106            };
1107            app.device_event(args, device_id, event);
1108        }
1109        fn user_event(&mut self, event_loop: &ActiveEventLoop, event: AppEvent<A>) {
1110            let (Some(window), Some(app)) = (self.window.as_mut(), self.app.as_mut()) else {
1111                return;
1112            };
1113            let args = Args {
1114                window,
1115                event_loop,
1116                event_proxy: &self.event_proxy,
1117                data: &mut self.app_data,
1118            };
1119
1120            match event {
1121                AppEvent::PingUserInput => window.ping_user_input(),
1122                AppEvent::RunIdle(f) => f(app, args),
1123                AppEvent::RunIdleSimple(f) => fut::FutureBackCaller::prepare(app, args, f),
1124                AppEvent::User(uevent) => app.user_event(args, uevent),
1125            }
1126        }
1127        fn new_events(&mut self, _event_loop: &ActiveEventLoop, _cause: winit::event::StartCause) {
1128            let Some(window) = self.window.as_mut() else {
1129                return;
1130            };
1131            window.new_events();
1132        }
1133        fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
1134            let Some(window) = self.window.as_mut() else {
1135                return;
1136            };
1137            window.about_to_wait();
1138        }
1139    }
1140}
1141
1142#[cfg(feature = "main-window")]
1143pub use main_window::*;