egui_multiwin/
multi_window.rs

1//! This defines the MultiWindow struct. This is the main struct used in the main function of a user application.
2
3use std::{collections::HashMap, sync::Mutex};
4
5use winit::window::WindowId;
6
7lazy_static::lazy_static! {
8    static ref WINDOW_REQUEST_ID: Mutex<u32> = Mutex::new(0u32);
9    /// The table that is used to obtain window ids
10    pub static ref WINDOW_TABLE: Mutex<HashMap<u32, Option<WindowId>>> = Mutex::new(HashMap::new());
11}
12
13/// Creates a new id for a window request that the user program can do things with
14pub fn new_id() -> u32 {
15    let mut l = WINDOW_REQUEST_ID.lock().unwrap();
16    let mut table = WINDOW_TABLE.lock().unwrap();
17    loop {
18        *l = l.wrapping_add(1);
19        if !table.contains_key(&l) {
20            table.insert(*l, None);
21            break;
22        }
23    }
24    *l
25}
26
27/// Retrieve a window id
28pub fn get_window_id(id: u32) -> Option<WindowId> {
29    let table = WINDOW_TABLE.lock().unwrap();
30    if let Some(id) = table.get(&id) {
31        *id
32    } else {
33        None
34    }
35}
36
37/// Create the dynamic tracked_window module for a egui_multiwin application. Takes three arguments. First argument is the type name of the common data structure for your application.
38/// Second argument is the type for custom events (or egui_multiwin::NoEvent if that functionality is not desired). Third argument is the enum of all windows. It needs to be enum_dispatch.
39#[macro_export]
40macro_rules! tracked_window {
41    ($common:ty,$event:ty, $window:ty) => {
42        pub mod tracked_window {
43            //! This module covers definition and functionality for an individual window.
44
45            use std::collections::HashMap;
46            use std::{mem, sync::{Arc, Mutex, MutexGuard}};
47
48            use super::multi_window::NewWindowRequest;
49
50            use egui_multiwin::egui;
51            use egui::viewport::{DeferredViewportUiCallback, ViewportBuilder, ViewportId, ViewportIdSet};
52            use egui_multiwin::egui_glow::EguiGlow;
53            use egui_multiwin::egui_glow::{self, glow};
54            use egui_multiwin::glutin::context::{NotCurrentContext, PossiblyCurrentContext};
55            use egui_multiwin::glutin::prelude::{GlConfig, GlDisplay};
56            use egui_multiwin::glutin::surface::SurfaceAttributesBuilder;
57            use egui_multiwin::glutin::surface::WindowSurface;
58            use egui_multiwin::raw_window_handle_5::{HasRawDisplayHandle, HasRawWindowHandle};
59            use egui_multiwin::tracked_window::{ContextHolder, TrackedWindowOptions};
60            use egui_multiwin::winit::window::WindowId;
61            use egui_multiwin::winit::{
62                event::Event,
63                event_loop::{ControlFlow, EventLoopWindowTarget},
64            };
65            use egui_multiwin::{arboard, glutin, winit};
66
67            use $window;
68
69            /// The return value of the redraw function of trait `TrackedWindow`
70            pub struct RedrawResponse {
71                /// Should the window exit?
72                pub quit: bool,
73                /// A list of windows that the window desires to have created.
74                pub new_windows: Vec<NewWindowRequest>,
75            }
76
77            impl Default for RedrawResponse {
78                fn default() -> Self {
79                    Self {
80                        quit: false,
81                        new_windows: Vec::new(),
82                    }
83                }
84            }
85
86            /// A window being tracked by a `MultiWindow`. All tracked windows will be forwarded all events
87            /// received on the `MultiWindow`'s event loop.
88            #[egui_multiwin::enum_dispatch::enum_dispatch]
89            pub trait TrackedWindow {
90                /// Returns true if the window is a root window. Root windows will close all other windows when closed. Windows are not root windows by default.
91                /// It is completely valid to have more than one root window open at the same time. The program will exit when all root windows are closed.
92                fn is_root(&self) -> bool {
93                    false
94                }
95
96                /// Returns true when the window is allowed to close. Default is windows are always allowed to close. Override to change this behavior.
97                fn can_quit(&mut self, _c: &mut $common) -> bool {
98                    true
99                }
100
101                /// Sets whether or not the window is a root window. Does nothing by default
102                fn set_root(&mut self, _root: bool) {}
103
104                /// Handles a custom event sent specifically to this window.
105                fn custom_event(
106                    &mut self,
107                    _event: &$event,
108                    _c: &mut $common,
109                    _egui: &mut EguiGlow,
110                    _window: &egui_multiwin::winit::window::Window,
111                    _clipboard: &mut egui_multiwin::arboard::Clipboard,
112                ) -> RedrawResponse {
113                    RedrawResponse {
114                        quit: false,
115                        new_windows: vec![],
116                    }
117                }
118
119                /// Runs the redraw for the window. See RedrawResponse for the return value.
120                fn redraw(
121                    &mut self,
122                    c: &mut $common,
123                    egui: &mut EguiGlow,
124                    window: &egui_multiwin::winit::window::Window,
125                    clipboard: &mut egui_multiwin::arboard::Clipboard,
126                ) -> RedrawResponse;
127                /// Allows opengl rendering to be done underneath all of the egui stuff of the window
128                /// # Safety
129                ///
130                /// opengl functions are unsafe. This function would require calling opengl functions.
131                unsafe fn opengl_before(
132                    &mut self,
133                    _c: &mut $common,
134                    _gl: &Arc<egui_multiwin::egui_glow::painter::Context>,
135                ) {
136                }
137                /// Allows opengl rendering to be done on top of all of the egui stuff of the window
138                /// # Safety
139                ///
140                /// opengl functions are unsafe. This function would require calling opengl functions.
141                unsafe fn opengl_after(
142                    &mut self,
143                    _c: &mut $common,
144                    _gl: &Arc<egui_multiwin::egui_glow::painter::Context>,
145                ) {
146                }
147            }
148
149            /// Contains the differences between window types
150            pub enum WindowInstanceThings<'a> {
151                /// A root window
152                PlainWindow {
153                    /// The contents of the window
154                    window: &'a mut $window,
155                },
156                /// A viewport window
157                Viewport {
158                    /// A placeholder value
159                    b: u8,
160                },
161            }
162
163            impl<'a> WindowInstanceThings<'a> {
164                /// Get an optional mutable reference to the window data
165                fn window_data(&mut self) -> Option<&mut $window> {
166                    match self {
167                        WindowInstanceThings::PlainWindow{window} => {
168                            Some(window)
169                        }
170                        WindowInstanceThings::Viewport{b} => None,
171                    }
172                }
173            }
174
175            /// This structure is for dispatching events
176            pub struct TrackedWindowContainerInstance<'a> {
177                /// The egui reference
178                egui: &'a mut EguiGlow,
179                /// The window differences
180                window: WindowInstanceThings<'a>,
181                /// The viewport set
182                viewportset: &'a Arc<Mutex<ViewportIdSet>>,
183                /// The viewport id
184                viewportid: &'a ViewportId,
185                /// The optional callback for the window
186                viewport_callback: &'a Option<Arc<DeferredViewportUiCallback>>,
187            }
188
189            impl<'a> TrackedWindowContainerInstance<'a> {
190                /// Handles one event from the event loop. Returns true if the window needs to be kept alive,
191                /// otherwise it will be closed. Window events should be checked to ensure that their ID is one
192                /// that the TrackedWindow is interested in.
193                fn handle_event(
194                    &mut self,
195                    event: &egui_multiwin::winit::event::Event<$event>,
196                    el: &EventLoopWindowTarget<$event>,
197                    c: &mut $common,
198                    root_window_exists: bool,
199                    gl_window: &mut egui_multiwin::tracked_window::ContextHolder<
200                        PossiblyCurrentContext,
201                    >,
202                    clipboard: &mut egui_multiwin::arboard::Clipboard,
203                ) -> TrackedWindowControl {
204                    // Child window's requested control flow.
205                    let mut viewportset = self.viewportset.lock().unwrap();
206
207                    let mut redraw = || {
208                        let input = self.egui.egui_winit.take_egui_input(&gl_window.window);
209                        let ppp = self.egui.egui_ctx.pixels_per_point();
210                        self.egui.egui_ctx.begin_frame(input);
211                        let mut rr = RedrawResponse::default();
212                        if let Some(cb) = self.viewport_callback {
213                            cb(&self.egui.egui_ctx);
214                        }
215                        else if let Some(window) = self.window.window_data() {
216                            rr = window.redraw(c, self.egui, &gl_window.window, clipboard);
217                        }
218                        let full_output = self.egui.egui_ctx.end_frame();
219
220                        if self.viewport_callback.is_none() {
221                            let mut remove_id = Vec::new();
222                            for id in viewportset.iter() {
223                                if !full_output.viewport_output.contains_key(&id) {
224                                    remove_id.push(id.to_owned());
225                                }
226                            }
227                            for id in remove_id {
228                                viewportset.remove(&id);
229                            }
230                        }
231                        else {
232                            if !viewportset.contains(self.viewportid) {
233                                rr.quit = true;
234                            }
235                        }
236
237                        for (viewport_id, viewport_output) in &full_output.viewport_output {
238                            if viewport_id != &egui::viewport::ViewportId::ROOT && !viewportset.contains(viewport_id) {
239                                let builder =
240                                    egui_multiwin::egui_glow::egui_winit::create_winit_window_builder(
241                                        &self.egui.egui_ctx,
242                                        el,
243                                        viewport_output.builder.to_owned(),
244                                    );
245                                let options = TrackedWindowOptions {
246                                    shader: None,
247                                    vsync: false,
248                                };
249                                let vp = NewWindowRequest::new_viewport(
250                                    builder,
251                                    options,
252                                    egui_multiwin::multi_window::new_id(),
253                                    viewport_output.builder.clone(),
254                                    viewport_id.to_owned(),
255                                    self.viewportset.to_owned(),
256                                    viewport_output.viewport_ui_cb.to_owned(),
257                                );
258                                viewportset.insert(viewport_id.to_owned());
259                                rr.new_windows.push(vp);
260                            }
261                        }
262
263                        let vp_output = full_output
264                            .viewport_output
265                            .get(self.viewportid);
266                        let repaint_after = vp_output.map(|v| v.repaint_delay).unwrap_or(std::time::Duration::from_millis(1000));
267
268                        if rr.quit {
269                            gl_window.control_flow = None;
270                        } else if repaint_after.is_zero() {
271                            gl_window.window.request_redraw();
272                            gl_window.control_flow = Some(egui_multiwin::winit::event_loop::ControlFlow::Poll);
273                        } else if repaint_after.as_millis() > 0 && repaint_after.as_millis() < 10000 {
274                            gl_window.control_flow =
275                                Some(egui_multiwin::winit::event_loop::ControlFlow::WaitUntil(
276                                    std::time::Instant::now() + repaint_after,
277                                ));
278                        } else {
279                            gl_window.control_flow = Some(egui_multiwin::winit::event_loop::ControlFlow::Wait);
280                        };
281
282                        {
283                            let color = egui_multiwin::egui::Rgba::from_white_alpha(0.0);
284                            unsafe {
285                                use glow::HasContext as _;
286                                self.egui.painter
287                                    .gl()
288                                    .clear_color(color[0], color[1], color[2], color[3]);
289                                self.egui.painter.gl().clear(glow::COLOR_BUFFER_BIT);
290                            }
291
292                            // draw things behind egui here
293                            if let Some(window) = self.window.window_data() {
294                                unsafe { window.opengl_before(c, self.egui.painter.gl()) };
295                            }
296
297                            let prim = self.egui
298                                .egui_ctx
299                                .tessellate(full_output.shapes, self.egui.egui_ctx.pixels_per_point());
300                            self.egui.painter.paint_and_update_textures(
301                                gl_window.window.inner_size().into(),
302                                ppp,
303                                &prim[..],
304                                &full_output.textures_delta,
305                            );
306
307                            // draw things on top of egui here
308                            if let Some(window) = self.window.window_data() {
309                                unsafe { window.opengl_after(c, self.egui.painter.gl()) };
310                            }
311
312                            gl_window.swap_buffers().unwrap();
313                        }
314                        rr
315                    };
316
317                    let response = match event {
318                        egui_multiwin::winit::event::Event::UserEvent(ue) => {
319                            if let Some(window) = self.window.window_data() {
320                                Some(window.custom_event(ue, c, self.egui, &gl_window.window, clipboard))
321                            }
322                            else {
323                                None
324                            }
325                        }
326
327                        egui_multiwin::winit::event::Event::NewEvents(sc) => {
328                            match sc {
329                                egui_multiwin::winit::event::StartCause::ResumeTimeReached{..} => {
330                                    gl_window.window.request_redraw();
331                                    gl_window.control_flow = Some(ControlFlow::Poll); //?
332                                }
333                                egui_multiwin::winit::event::StartCause::WaitCancelled{start, requested_resume} => {
334                                    if let Some(resume) = requested_resume {
335                                        gl_window.control_flow = Some(ControlFlow::WaitUntil(resume.to_owned()));
336                                    }
337                                }
338                                egui_multiwin::winit::event::StartCause::Poll => {
339                                    
340                                }
341                                egui_multiwin::winit::event::StartCause::Init => {
342
343                                }
344                            }
345                            None
346                        }
347
348                        egui_multiwin::winit::event::Event::WindowEvent { event, window_id } => {
349                            let mut redraw_thing = None;
350                            match event {
351                                egui_multiwin::winit::event::WindowEvent::Resized(physical_size) => {
352                                    gl_window.resize(*physical_size);
353                                }
354                                egui_multiwin::winit::event::WindowEvent::CloseRequested => {
355                                    gl_window.control_flow = None;
356                                }
357                                egui_multiwin::winit::event::WindowEvent::RedrawRequested => {
358                                    redraw_thing = Some(redraw());
359                                }
360                                _ => {}
361                            }
362
363                            match event {
364                                egui_multiwin::winit::event::WindowEvent::RedrawRequested => {
365                                }
366                                _ => {
367                                    let resp = self.egui.on_window_event(&gl_window.window, event);
368                                    if resp.repaint {
369                                        gl_window.window.request_redraw();
370                                    }
371                                }
372                            }
373
374                            redraw_thing
375                        }
376                        egui_multiwin::winit::event::Event::LoopExiting => {
377                            self.egui.destroy();
378                            None
379                        }
380
381                        _ => None,
382                    };
383
384                    if let Some(window) = self.window.window_data() {
385                        if !root_window_exists && !window.is_root() {
386                            gl_window.control_flow = None;
387                        }
388                    }
389
390                    TrackedWindowControl {
391                        requested_control_flow: gl_window.control_flow,
392                        windows_to_create: if let Some(a) = response {
393                            a.new_windows
394                        } else {
395                            Vec::new()
396                        },
397                    }
398                }
399            }
400
401            /// Defines a window
402            pub enum TrackedWindowContainer {
403                /// A root window
404                PlainWindow(PlainWindowContainer),
405                /// A viewport window
406                Viewport(ViewportWindowContainer),
407            }
408
409            /// The common data for all window types
410            pub struct CommonWindowData {
411                /// The context for the window
412                pub gl_window: IndeterminateWindowedContext,
413                /// The egui instance for this window, each window has a separate egui instance.
414                pub egui: Option<EguiGlow>,
415                /// The viewport set
416                viewportset: Arc<Mutex<ViewportIdSet>>,
417                /// The viewport id for the window
418                viewportid: ViewportId,
419                /// The optional shader version for the window
420                pub shader: Option<egui_multiwin::egui_glow::ShaderVersion>,
421                /// The viewport builder
422                pub vb: Option<ViewportBuilder>,
423                /// The viewport callback
424                viewportcb: Option<std::sync::Arc<DeferredViewportUiCallback>>,
425            }
426
427            /// The container for a viewport window
428            pub struct ViewportWindowContainer {
429                /// The common data
430                common: CommonWindowData,
431            }
432
433            /// The main container for a root window.
434            pub struct PlainWindowContainer {
435                /// The common data
436                common: CommonWindowData,
437                /// The actual window
438                pub window: $window,
439            }
440
441            impl TrackedWindowContainer {
442                /// Get the optional window data contained by the window
443                pub fn get_window_data(&self) -> Option<& $window> {
444                    match self {
445                        Self::PlainWindow(w) => Some(&w.window),
446                        Self::Viewport(_) => None,
447                    }
448                }
449
450                /// Get the optional window data, mutable, contained by the window
451                pub fn get_window_data_mut(&mut self) -> Option<&mut $window> {
452                    match self {
453                        Self::PlainWindow(w) => Some(&mut w.window),
454                        Self::Viewport(_) => None,
455                    }
456                }
457
458                /// Get the common data for the window
459                fn common(&self) -> &CommonWindowData {
460                    match self {
461                        Self::PlainWindow(w) => &w.common,
462                        Self::Viewport(w) => &w.common,
463                    }
464                }
465
466                /// Get the common data, mutably, for the window
467                fn common_mut(&mut self) -> &mut CommonWindowData {
468                    match self {
469                        Self::PlainWindow(w) => &mut w.common,
470                        Self::Viewport(w) => &mut w.common,
471                    }
472                }
473
474                /// Get the gl window for the container
475                fn gl_window(&self) -> &IndeterminateWindowedContext {
476                    match self {
477                        Self::PlainWindow(w) => &w.common.gl_window,
478                        Self::Viewport(w) => &w.common.gl_window,
479                    }
480                }
481
482                /// Get the gl window, mutably for the container
483                fn gl_window_mut(&mut self) -> &mut IndeterminateWindowedContext {
484                    match self {
485                        Self::PlainWindow(w) => &mut w.common.gl_window,
486                        Self::Viewport(w) => &mut w.common.gl_window,
487                    }
488                }
489
490                /// Retrieve the window id for the container
491                pub fn get_window_id(&self) -> Option<WindowId> {
492                    match self.gl_window() {
493                        IndeterminateWindowedContext::PossiblyCurrent(w) => Some(w.window.id()),
494                        IndeterminateWindowedContext::NotCurrent(w) => Some(w.window.id()),
495                        IndeterminateWindowedContext::None => {
496                            None
497                        }
498                    }
499                }
500
501                /// Create a new window.
502                pub fn create<TE>(
503                    window: Option<$window>,
504                    viewportset: Arc<Mutex<ViewportIdSet>>,
505                    viewportid: &ViewportId,
506                    viewportcb: Option<std::sync::Arc<DeferredViewportUiCallback>>,
507                    window_builder: egui_multiwin::winit::window::WindowBuilder,
508                    event_loop: &egui_multiwin::winit::event_loop::EventLoopWindowTarget<TE>,
509                    options: &TrackedWindowOptions,
510                    vb: Option<ViewportBuilder>
511                ) -> Result<TrackedWindowContainer, DisplayCreationError> {
512                    let rdh = event_loop.raw_display_handle();
513                    let winitwindow = window_builder.build(event_loop).unwrap();
514                    let rwh = winitwindow.raw_window_handle();
515                    #[cfg(target_os = "windows")]
516                    let pref = glutin::display::DisplayApiPreference::Wgl(Some(rwh));
517                    #[cfg(target_os = "linux")]
518                    let pref = egui_multiwin::glutin::display::DisplayApiPreference::Egl;
519                    #[cfg(target_os = "macos")]
520                    let pref = glutin::display::DisplayApiPreference::Cgl;
521                    let display = unsafe { glutin::display::Display::new(rdh, pref) };
522                    if let Ok(display) = display {
523                        let configt = glutin::config::ConfigTemplateBuilder::default().build();
524                        let mut configs: Vec<glutin::config::Config> =
525                            unsafe { display.find_configs(configt) }.unwrap().collect();
526                        configs.sort_by(|a, b| a.num_samples().cmp(&b.num_samples()));
527                        // Try all configurations until one works
528                        for config in configs {
529                            let sab: SurfaceAttributesBuilder<WindowSurface> =
530                                egui_multiwin::glutin::surface::SurfaceAttributesBuilder::default();
531                            let sa = sab.build(
532                                rwh,
533                                std::num::NonZeroU32::new(winitwindow.inner_size().width).unwrap(),
534                                std::num::NonZeroU32::new(winitwindow.inner_size().height).unwrap(),
535                            );
536                            let ws = unsafe { display.create_window_surface(&config, &sa) };
537                            if let Ok(ws) = ws {
538                                let attr =
539                                    egui_multiwin::glutin::context::ContextAttributesBuilder::new()
540                                        .build(Some(rwh));
541
542                                let gl_window =
543                                    unsafe { display.create_context(&config, &attr) }.unwrap();
544
545                                let wcommon = CommonWindowData {
546                                    viewportid: viewportid.to_owned(),
547                                    viewportset: viewportset.clone(),
548                                    gl_window: IndeterminateWindowedContext::NotCurrent(
549                                        egui_multiwin::tracked_window::ContextHolder::new(
550                                            gl_window,
551                                            winitwindow,
552                                            ws,
553                                            display,
554                                            *options,
555                                        ),
556                                    ),
557                                    vb,
558                                    viewportcb,
559                                    egui: None,
560                                    shader: options.shader,
561                                };
562                                if let Some(window) = window {
563                                    let w = PlainWindowContainer {
564                                        window,
565                                        common: wcommon,
566                                    };
567                                    return Ok(TrackedWindowContainer::PlainWindow(w));
568                                }
569                                else {
570                                    let w = ViewportWindowContainer {
571                                        common: wcommon,
572                                    };
573                                    return Ok(TrackedWindowContainer::Viewport(w));
574                                }
575                            }
576                        }
577                    }
578                    panic!("No window created");
579                }
580
581                /// Returns true if the specified event is for this window. A UserEvent (one generated by the EventLoopProxy) is not for any window.
582                pub fn is_event_for_window(&self, event: &winit::event::Event<$event>) -> bool {
583                    // Check if the window ID matches, if not then this window can pass on the event.
584                    match (event, self.gl_window()) {
585                        (
586                            Event::UserEvent(ev),
587                            IndeterminateWindowedContext::PossiblyCurrent(gl_window),
588                        ) => {
589                            if let Some(wid) = ev.window_id() {
590                                wid == gl_window.window.id()
591                            } else {
592                                false
593                            }
594                        }
595                        (
596                            Event::UserEvent(ev),
597                            IndeterminateWindowedContext::NotCurrent(gl_window),
598                        ) => {
599                            if let Some(wid) = ev.window_id() {
600                                wid == gl_window.window.id()
601                            } else {
602                                false
603                            }
604                        }
605                        (
606                            Event::WindowEvent {
607                                window_id: id,
608                                event: _,
609                                ..
610                            },
611                            IndeterminateWindowedContext::PossiblyCurrent(gl_window),
612                        ) => gl_window.window.id() == *id,
613                        (
614                            Event::WindowEvent {
615                                window_id: id,
616                                event: _,
617                                ..
618                            },
619                            IndeterminateWindowedContext::NotCurrent(gl_window),
620                        ) => gl_window.window.id() == *id,
621                        _ => true, // we weren't able to check the window ID, maybe this window is not initialized yet. we should run it.
622                    }
623                }
624
625                /// Build an instance that can have events dispatched to it
626                fn prepare_for_events(&mut self) -> Option<TrackedWindowContainerInstance> {
627                    match self {
628                        Self::PlainWindow(w) => {
629                            if let Some(egui) = &mut w.common.egui {
630                                let w2 = WindowInstanceThings::PlainWindow { window: &mut w.window, };
631                                Some(TrackedWindowContainerInstance { egui,
632                                    window: w2,
633                                    viewportset: &w.common.viewportset,
634                                    viewportid: &w.common.viewportid,
635                                    viewport_callback: &w.common.viewportcb,
636                                })
637                            }
638                            else {
639                                None
640                            }
641                        }
642                        Self::Viewport(w) => {
643                            if let Some(egui) = &mut w.common.egui {
644                                let w2 = WindowInstanceThings::Viewport { b: 42, };
645                                Some(TrackedWindowContainerInstance { egui,
646                                    window: w2,
647                                    viewportset: &w.common.viewportset,
648                                    viewportid: &w.common.viewportid,
649                                    viewport_callback: &w.common.viewportcb,
650                                })
651                            }
652                            else {
653                                None
654                            }
655                        }
656                    }
657                }
658
659                /// The outer event handler for a window. Responsible for activating the context, creating the egui context if required, and calling handle_event.
660                pub fn handle_event_outer(
661                    &mut self,
662                    c: &mut $common,
663                    event: &winit::event::Event<$event>,
664                    el: &EventLoopWindowTarget<$event>,
665                    root_window_exists: bool,
666                    fontmap: &HashMap<String, egui::FontData>,
667                    clipboard: &mut arboard::Clipboard,
668                ) -> TrackedWindowControl {
669                    // Activate this gl_window so we can use it.
670                    // We cannot activate it without full ownership, so temporarily move the gl_window into the current scope.
671                    // It *must* be returned at the end.
672                    let gl_window =
673                        mem::replace(self.gl_window_mut(), IndeterminateWindowedContext::None);
674                    let mut gl_window = match gl_window {
675                        IndeterminateWindowedContext::PossiblyCurrent(w) => {
676                            let _e = w.make_current();
677                            w
678                        }
679                        IndeterminateWindowedContext::NotCurrent(w) => w.make_current().unwrap(),
680                        IndeterminateWindowedContext::None => {
681                            panic!("there's no window context???")
682                        }
683                    };
684
685                    // Now that the window is active, create a context if it is missing.
686                    match self.common().egui.as_ref() {
687                        None => {
688                            let gl = Arc::new(unsafe {
689                                glow::Context::from_loader_function(|s| {
690                                    gl_window.get_proc_address(s)
691                                })
692                            });
693
694                            unsafe {
695                                use glow::HasContext as _;
696                                gl.enable(glow::FRAMEBUFFER_SRGB);
697                            }
698
699                            let egui = egui_glow::EguiGlow::new(el, gl, self.common().shader, None);
700                            {
701                                let mut fonts = egui::FontDefinitions::default();
702                                for (name, font) in fontmap {
703                                    fonts.font_data.insert(name.clone(), font.clone());
704                                    fonts.families.insert(
705                                        egui::FontFamily::Name(name.to_owned().into()),
706                                        vec![name.to_owned()],
707                                    );
708                                }
709                                egui.egui_ctx.set_fonts(fonts)
710                            }
711                            if let Some(vb) = &self.common().vb {
712                                egui_multiwin::egui_glow::egui_winit::apply_viewport_builder_to_window(
713                                    &egui.egui_ctx,
714                                    gl_window.window(),
715                                    vb,
716                                );
717                            }
718                            egui.egui_ctx.set_embed_viewports(false);
719                            self.common_mut().egui = Some(egui);
720                        }
721                        Some(_) => (),
722                    };
723
724                    let result = if let Some(mut thing) = self.prepare_for_events() {
725                        let result = thing.handle_event(
726                            event,
727                            el,
728                            c,
729                            root_window_exists,
730                            &mut gl_window,
731                            clipboard,
732                        );
733                        result
734                    } else {
735                        panic!("Window wasn't fully initialized");
736                    };
737
738                    if result.requested_control_flow.is_none() {
739                        self.try_quit(c);
740                    };
741
742                    match mem::replace(
743                        self.gl_window_mut(),
744                        IndeterminateWindowedContext::PossiblyCurrent(gl_window),
745                    ) {
746                        IndeterminateWindowedContext::None => (),
747                        _ => {
748                            panic!("Window had a GL context while we were borrowing it?");
749                        }
750                    }
751                    result
752                }
753
754                fn try_quit(&mut self, c: &mut $common) {
755                    match self {
756                        Self::PlainWindow(w) => {
757                            if w.window.can_quit(c) {
758                                if let Some(egui) = &mut w.common.egui {
759                                    egui.destroy();
760                                }
761                            }
762                        }
763                        Self::Viewport(w) => {
764                            w.common.egui = None;
765                        }
766                    }
767                }
768            }
769
770            /// Enum of the potential options for a window context
771            pub enum IndeterminateWindowedContext {
772                /// The window context is possibly current
773                PossiblyCurrent(ContextHolder<PossiblyCurrentContext>),
774                /// The window context is not current
775                NotCurrent(ContextHolder<NotCurrentContext>),
776                /// The window context is empty
777                None,
778            }
779
780            impl IndeterminateWindowedContext {
781                /// Get the window handle
782                pub fn window(&self) -> &winit::window::Window {
783                    match self {
784                        IndeterminateWindowedContext::PossiblyCurrent(pc) => pc.window(),
785                        IndeterminateWindowedContext::NotCurrent(nc) => nc.window(),
786                        IndeterminateWindowedContext::None => panic!("No window"),
787                    }
788                }
789            }
790
791            /// The eventual return struct of the `TrackedWindow` trait update function. Used internally for window management.
792            pub struct TrackedWindowControl {
793                /// Indicates how the window desires to respond to future events
794                pub requested_control_flow: Option<ControlFlow>,
795                /// A list of windows to be created
796                pub windows_to_create: Vec<NewWindowRequest>,
797            }
798
799            #[derive(egui_multiwin::thiserror::Error, Debug)]
800            /// Enumerates the kinds of errors that display creation can have.
801            pub enum DisplayCreationError {}
802        }
803    };
804}
805
806/// This macro creates a dynamic definition of the multi_window module. It has the same arguments as the [`tracked_window`](macro.tracked_window.html) macro.
807#[macro_export]
808macro_rules! multi_window {
809    ($common:ty,$event:ty, $window:ty) => {
810        pub mod multi_window {
811            //! This defines the MultiWindow struct. This is the main struct used in the main function of a user application.
812
813            use std::collections::HashMap;
814            use std::sync::{Arc, Mutex};
815
816            use egui_multiwin::{
817                tracked_window::TrackedWindowOptions,
818                winit::{
819                    self,
820                    error::EventLoopError,
821                    event_loop::{ControlFlow, EventLoop},
822                },
823            };
824
825            use egui::viewport::{DeferredViewportUiCallback, ViewportId, ViewportIdSet};
826            use egui_multiwin::egui;
827
828            use super::tracked_window::{
829                DisplayCreationError, TrackedWindow, TrackedWindowContainer,
830            };
831
832            /// The main struct of the crate. Manages multiple `TrackedWindow`s by forwarding events to them.
833            /// `T` represents the common data struct for the user program. `U` is the type representing custom events.
834            pub struct MultiWindow {
835                /// The windows for the application.
836                windows: Vec<TrackedWindowContainer>,
837                /// A list of fonts to install on every egui instance
838                fonts: HashMap<String, egui_multiwin::egui::FontData>,
839                /// The clipboard
840                clipboard: egui_multiwin::arboard::Clipboard,
841            }
842
843            impl Default for MultiWindow {
844                fn default() -> Self {
845                    Self::new()
846                }
847            }
848
849            impl MultiWindow {
850                /// Creates a new `MultiWindow`.
851                pub fn new() -> Self {
852                    MultiWindow {
853                        windows: vec![],
854                        fonts: HashMap::new(),
855                        clipboard: egui_multiwin::arboard::Clipboard::new().unwrap(),
856                    }
857                }
858
859                /// A simpler way to start up a user application. The provided closure should initialize the root window, add any fonts desired, store the proxy if it is needed, and return the common app struct.
860                pub fn start(
861                    t: impl FnOnce(
862                        &mut Self,
863                        &EventLoop<$event>,
864                        egui_multiwin::winit::event_loop::EventLoopProxy<$event>,
865                    ) -> $common,
866                ) -> Result<(), EventLoopError> {
867                    let mut event_loop =
868                        egui_multiwin::winit::event_loop::EventLoopBuilder::with_user_event();
869                    let event_loop = event_loop.build().unwrap();
870                    let proxy = event_loop.create_proxy();
871                    let mut multi_window = Self::new();
872
873                    let ac = t(&mut multi_window, &event_loop, proxy);
874
875                    multi_window.run(event_loop, ac)
876                }
877
878                /// Add a font that is applied to every window. Be sure to call this before calling [add](crate::multi_window::MultiWindow::add)
879                /// multi_window is an instance of [MultiWindow](crate::multi_window::MultiWindow), DATA is a static `&[u8]` - most like defined with a `include_bytes!()` macro
880                /// ```
881                /// use egui_multiwin::multi_window::NewWindowRequest;
882                /// struct Custom {}
883                ///
884                /// impl egui_multiwin::multi_window::CommonEventHandler for Custom {
885                ///     fn process_event(&mut self, _event: egui_multiwin::multi_window::DefaultCustomEvent)  -> Vec<NewWindowRequest>{
886                ///         vec!()
887                ///     }
888                /// }
889                ///
890                /// let mut multi_window: egui_multiwin::multi_window::MultiWindow = egui_multiwin::multi_window::MultiWindow::new();
891                /// let DATA = include_bytes!("cmunbtl.ttf");
892                /// multi_window.add_font("my_font".to_string(), egui_multiwin::egui::FontData::from_static(DATA));
893                /// ```
894                pub fn add_font(&mut self, name: String, fd: egui_multiwin::egui::FontData) {
895                    self.fonts.insert(name, fd);
896                }
897
898                /// Adds a new `TrackedWindow` to the `MultiWindow`. If custom fonts are desired, call [add_font](crate::multi_window::MultiWindow::add_font) first.
899                pub fn add<TE>(
900                    &mut self,
901                    window: NewWindowRequest,
902                    _c: &mut $common,
903                    event_loop: &egui_multiwin::winit::event_loop::EventLoopWindowTarget<TE>,
904                ) -> Result<(), DisplayCreationError> {
905                    let twc = TrackedWindowContainer::create::<TE>(
906                        window.window_state,
907                        window.viewportset,
908                        &window
909                            .viewport_id
910                            .unwrap_or(egui::viewport::ViewportId::ROOT),
911                        window.viewport_callback,
912                        window.builder,
913                        event_loop,
914                        &window.options,
915                        window.viewport,
916                    )?;
917                    let w = twc.get_window_id();
918                    let mut table = egui_multiwin::multi_window::WINDOW_TABLE.lock().unwrap();
919                    if let Some(id) = table.get_mut(&window.id) {
920                        *id = w;
921                    }
922                    self.windows.push(twc);
923                    Ok(())
924                }
925
926                /// Process the given event for the applicable window(s)
927                pub fn do_window_events(
928                    &mut self,
929                    c: &mut $common,
930                    event: &winit::event::Event<$event>,
931                    event_loop_window_target: &winit::event_loop::EventLoopWindowTarget<$event>,
932                ) -> Vec<Option<ControlFlow>> {
933                    let mut handled_windows = vec![];
934                    let mut window_control_flow = vec![];
935
936                    let mut root_window_exists = false;
937                    for other in &self.windows {
938                        if let Some(window) = other.get_window_data() {
939                            if window.is_root() {
940                                root_window_exists = true;
941                            }
942                        }
943                    }
944
945                    while let Some(mut window) = self.windows.pop() {
946                        if window.is_event_for_window(event) {
947                            let window_control = window.handle_event_outer(
948                                c,
949                                event,
950                                event_loop_window_target,
951                                root_window_exists,
952                                &self.fonts,
953                                &mut self.clipboard,
954                            );
955                            match window_control.requested_control_flow {
956                                None => {
957                                    //println!("window requested exit. Instead of sending the exit for everyone, just get rid of this one.");
958                                    if let Some(window) = window.get_window_data_mut() {
959                                        if window.can_quit(c) {
960                                            window_control_flow.push(None);
961                                            continue;
962                                        } else {
963                                            window_control_flow.push(Some(ControlFlow::Wait));
964                                        }
965                                    } else {
966                                        window_control_flow.push(None);
967                                        continue;
968                                    }
969                                    // *flow = ControlFlow::Exit
970                                }
971                                Some(requested_flow) => {
972                                    window_control_flow.push(Some(requested_flow));
973                                }
974                            }
975
976                            for new_window_request in window_control.windows_to_create {
977                                let _e = self.add(new_window_request, c, event_loop_window_target);
978                            }
979                        }
980                        handled_windows.push(window);
981                    }
982
983                    // Move them back.
984                    handled_windows.reverse();
985                    self.windows.append(&mut handled_windows);
986
987                    window_control_flow
988                }
989
990                /// Runs the event loop until all `TrackedWindow`s are closed.
991                pub fn run(
992                    mut self,
993                    event_loop: EventLoop<$event>,
994                    mut c: $common,
995                ) -> Result<(), EventLoopError> {
996                    event_loop.run(move |event, event_loop_window_target| {
997                        let c = &mut c;
998                        //println!("handling event {:?}", event);
999                        let window_try = if let winit::event::Event::UserEvent(uevent) = &event {
1000                            uevent.window_id().is_some()
1001                        } else {
1002                            true
1003                        };
1004                        let window_control_flow = if window_try {
1005                            self.do_window_events(c, &event, event_loop_window_target)
1006                        } else {
1007                            if let winit::event::Event::UserEvent(uevent) = event {
1008                                for w in c.process_event(uevent) {
1009                                    let _e = self.add(w, c, event_loop_window_target);
1010                                }
1011                            }
1012                            vec![Some(ControlFlow::Poll)]
1013                        };
1014
1015                        let mut flow = Some(event_loop_window_target.control_flow());
1016
1017                        // If any window requested polling, we should poll.
1018                        // Precedence: Poll > WaitUntil(smallest) > Wait.
1019                        if flow.is_none() {
1020                        } else if let Some(flow) = &mut flow {
1021                            *flow = ControlFlow::Wait;
1022                            for flow_request in window_control_flow {
1023                                if let Some(flow_request) = flow_request {
1024                                    match flow_request {
1025                                        ControlFlow::Poll => {
1026                                            *flow = ControlFlow::Poll;
1027                                        }
1028                                        ControlFlow::Wait => (), // do nothing, if untouched it will be wait
1029                                        ControlFlow::WaitUntil(when_new) => {
1030                                            if let ControlFlow::Poll = *flow {
1031                                                continue; // Polling takes precedence, so ignore this.
1032                                            }
1033
1034                                            // The current flow is already WaitUntil. If this one is sooner, use it instead.
1035                                            if let ControlFlow::WaitUntil(when_current) = *flow {
1036                                                if when_new < when_current {
1037                                                    *flow = ControlFlow::WaitUntil(when_new);
1038                                                }
1039                                            } else {
1040                                                // The current flow is lower precedence, so replace it with this.
1041                                                *flow = ControlFlow::WaitUntil(when_new);
1042                                            }
1043                                        }
1044                                    }
1045                                }
1046                            }
1047                        }
1048
1049                        if self.windows.is_empty() {
1050                            //println!("no more windows running, exiting event loop.");
1051                            flow = None;
1052                        }
1053
1054                        if let Some(flow) = flow {
1055                            event_loop_window_target.set_control_flow(flow);
1056                        } else {
1057                            event_loop_window_target.exit();
1058                        }
1059                    })
1060                }
1061            }
1062
1063            /// A struct defining how a new window is to be created.
1064            pub struct NewWindowRequest {
1065                /// The actual struct containing window data. The struct must implement the `TrackedWindow` trait.
1066                pub window_state: Option<$window>,
1067                /// Specifies how to build the window with a WindowBuilder
1068                pub builder: egui_multiwin::winit::window::WindowBuilder,
1069                /// Other options for the window.
1070                pub options: TrackedWindowOptions,
1071                /// An id to allow a user program to translate window requests into actual window ids.
1072                pub id: u32,
1073                /// The viewport options
1074                viewport: Option<egui_multiwin::egui::ViewportBuilder>,
1075                /// The viewport id
1076                viewport_id: Option<ViewportId>,
1077                /// The viewport set, shared among the set of related windows
1078                viewportset: Arc<Mutex<ViewportIdSet>>,
1079                /// The viewport callback
1080                viewport_callback: Option<std::sync::Arc<DeferredViewportUiCallback>>,
1081            }
1082
1083            impl NewWindowRequest {
1084                /// Create a new root window
1085                pub fn new(
1086                    window_state: $window,
1087                    builder: egui_multiwin::winit::window::WindowBuilder,
1088                    options: TrackedWindowOptions,
1089                    id: u32,
1090                ) -> Self {
1091                    Self {
1092                        window_state: Some(window_state),
1093                        builder,
1094                        options,
1095                        id,
1096                        viewport: None,
1097                        viewport_id: None,
1098                        viewportset: Arc::new(Mutex::new(egui::viewport::ViewportIdSet::default())),
1099                        viewport_callback: None,
1100                    }
1101                }
1102
1103                /// Construct a new viewport window
1104                pub fn new_viewport(
1105                    builder: egui_multiwin::winit::window::WindowBuilder,
1106                    options: TrackedWindowOptions,
1107                    id: u32,
1108                    vp_builder: egui_multiwin::egui::ViewportBuilder,
1109                    vp_id: ViewportId,
1110                    viewportset: Arc<Mutex<ViewportIdSet>>,
1111                    vpcb: Option<std::sync::Arc<DeferredViewportUiCallback>>,
1112                ) -> Self {
1113                    Self {
1114                        window_state: None,
1115                        builder,
1116                        options,
1117                        id,
1118                        viewport: Some(vp_builder),
1119                        viewport_id: Some(vp_id),
1120                        viewport_callback: vpcb,
1121                        viewportset,
1122                    }
1123                }
1124            }
1125        }
1126    };
1127}