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}