diff --git a/crates/eframe/src/native/epi_integration.rs b/crates/eframe/src/native/epi_integration.rs index 0768106bbda..47dae7e931d 100644 --- a/crates/eframe/src/native/epi_integration.rs +++ b/crates/eframe/src/native/epi_integration.rs @@ -4,7 +4,7 @@ use winit::event_loop::EventLoopWindowTarget; use raw_window_handle::{HasRawDisplayHandle as _, HasRawWindowHandle as _}; -use egui::{NumExt as _, ViewportBuilder, ViewportId, ViewportIdPair, ViewportRender}; +use egui::{NumExt as _, ViewportBuilder, ViewportId, ViewportIdPair, ViewportUiCallback}; #[cfg(feature = "accesskit")] use egui_winit::accesskit_winit; use egui_winit::{native_pixels_per_point, EventResponse, WindowSettings}; @@ -480,7 +480,7 @@ impl EpiIntegration { app: &mut dyn epi::App, window: &winit::window::Window, egui_winit: &mut egui_winit::State, - render: &Option>>, + viewport_ui_cb: &Option>>, pair: ViewportIdPair, ) -> egui::FullOutput { let frame_start = std::time::Instant::now(); @@ -495,9 +495,11 @@ impl EpiIntegration { // Run user code: let full_output = self.egui_ctx.run(raw_input, pair, |egui_ctx| { crate::profile_scope!("App::update"); - if let Some(render) = render { - render(egui_ctx); + if let Some(viewport_ui_cb) = viewport_ui_cb { + // Child viewport + viewport_ui_cb(egui_ctx); } else { + // Root viewport app.update(egui_ctx, &mut self.frame); } }); diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index 3d842f01e83..b6ffe555eb0 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -459,7 +459,7 @@ fn run_and_exit(event_loop: EventLoop, mut winit_app: impl WinitApp + mod glow_integration { use egui::{ - epaint::ahash::HashMap, NumExt as _, ViewportIdPair, ViewportOutput, ViewportRender, + epaint::ahash::HashMap, NumExt as _, ViewportIdPair, ViewportOutput, ViewportUiCallback, }; use egui_winit::{ changes_between_builders, create_winit_window_builder, process_viewport_commands, @@ -503,7 +503,10 @@ mod glow_integration { gl_surface: Option>, window: Option>>, pair: ViewportIdPair, - render: Option>>, + + /// The user-callback that shows the ui. + viewport_ui_cb: Option>>, + egui_winit: Option, } @@ -669,7 +672,7 @@ mod glow_integration { gl_surface: None, window: window.map(|w| Rc::new(RefCell::new(w))), egui_winit: None, - render: None, + viewport_ui_cb: None, pair: ViewportIdPair::MAIN, })), ); @@ -1079,7 +1082,7 @@ mod glow_integration { let beginning = integration.beginning; integration.egui_ctx.set_render_sync_callback( - move |egui_ctx, viewport_builder, pair, render| { + move |egui_ctx, viewport_builder, pair, viewport_ui_cb| { if let (Some(glutin), Some(gl), Some(painter)) = (glutin.upgrade(), gl.upgrade(), painter.upgrade()) { @@ -1087,7 +1090,7 @@ mod glow_integration { egui_ctx, viewport_builder, pair, - render, + viewport_ui_cb, &glutin, &gl, &painter, @@ -1117,7 +1120,7 @@ mod glow_integration { egui_ctx: &egui::Context, mut viewport_builder: ViewportBuilder, pair: ViewportIdPair, - render: Box, + viewport_ui_cb: Box, glutin: &RefCell, gl: &glow::Context, painter: &RefCell, @@ -1149,7 +1152,7 @@ mod glow_integration { gl_surface: None, window: None, pair, - render: None, + viewport_ui_cb: None, egui_winit: None, }))); glutin.builders.entry(pair.this).or_insert(viewport_builder); @@ -1180,7 +1183,7 @@ mod glow_integration { let mut input = winit_state.take_egui_input(&win); input.time = Some(beginning.elapsed().as_secs_f64()); let output = egui_ctx.run(input, pair, |ctx| { - render(ctx); + viewport_ui_cb(ctx); }); let screen_size_in_pixels: [u32; 2] = win.inner_size().into(); @@ -1247,7 +1250,7 @@ mod glow_integration { |ViewportOutput { builder, pair: ViewportIdPair { this: id, .. }, - render, + viewport_ui_cb, }| { let mut glutin = glutin_ctx.borrow_mut(); let last_builder = glutin.builders.entry(*id).or_insert(builder.clone()); @@ -1258,7 +1261,7 @@ mod glow_integration { if recreate { window.window = None; window.gl_surface = None; - window.render = render.clone(); + window.viewport_ui_cb = viewport_ui_cb.clone(); window.pair.parent = *id; } if let Some(w) = window.window.clone() { @@ -1275,7 +1278,7 @@ mod glow_integration { for ViewportOutput { mut builder, pair, - render, + viewport_ui_cb, } in viewports { let default_icon = glutin_ctx @@ -1295,7 +1298,7 @@ mod glow_integration { gl_surface: None, window: None, egui_winit: None, - render, + viewport_ui_cb, pair, })), ); @@ -1426,7 +1429,8 @@ mod glow_integration { { let glutin = glutin.borrow(); let viewport = &glutin.viewports[&viewport_id].clone(); - if viewport.borrow().render.is_none() && viewport_id != ViewportId::MAIN { + if viewport.borrow().viewport_ui_cb.is_none() && viewport_id != ViewportId::MAIN + { if let Some(parent_viewport) = glutin.viewports.get(&viewport.borrow().pair.parent) { @@ -1475,7 +1479,7 @@ mod glow_integration { app.borrow_mut().as_mut(), &window, egui_winit, - &viewport.render.clone(), + &viewport.viewport_ui_cb.clone(), viewport.pair, ); @@ -1870,7 +1874,7 @@ pub use glow_integration::run_glow; #[cfg(feature = "wgpu")] mod wgpu_integration { - use egui::{ViewportIdPair, ViewportOutput, ViewportRender}; + use egui::{ViewportIdPair, ViewportOutput, ViewportUiCallback}; use egui_winit::create_winit_window_builder; use parking_lot::Mutex; @@ -1880,7 +1884,7 @@ mod wgpu_integration { pub struct Viewport { window: Option>>, state: Rc>>, - render: Option>>, + viewport_ui_cb: Option>>, parent_id: ViewportId, } @@ -2149,7 +2153,7 @@ mod wgpu_integration { Viewport { window: Some(Rc::new(RefCell::new(window))), state: Rc::new(RefCell::new(Some(state))), - render: None, + viewport_ui_cb: None, parent_id: ViewportId::MAIN, }, ); @@ -2169,7 +2173,7 @@ mod wgpu_integration { let beginning = integration.beginning; integration.egui_ctx.set_render_sync_callback( - move |egui_ctx, viewport_builder, pair, render| { + move |egui_ctx, viewport_builder, pair, viewport_ui_cb| { if let ( Some(viewports), Some(builders), @@ -2185,7 +2189,7 @@ mod wgpu_integration { egui_ctx, viewport_builder, pair, - render, + viewport_ui_cb, &viewports, &builders, beginning, @@ -2217,7 +2221,7 @@ mod wgpu_integration { egui_ctx: &egui::Context, mut viewport_builder: ViewportBuilder, pair: ViewportIdPair, - render: Box, + viewport_ui_cb: Box, viewports: &RefCell, builders: &RefCell>, beginning: Instant, @@ -2243,7 +2247,7 @@ mod wgpu_integration { viewports.entry(pair.this).or_insert(Viewport { window: None, state: Rc::new(RefCell::new(None)), - render: None, + viewport_ui_cb: None, parent_id: pair.parent, }); builders @@ -2277,7 +2281,7 @@ mod wgpu_integration { let mut input = winit_state.take_egui_input(&win); input.time = Some(beginning.elapsed().as_secs_f64()); let output = egui_ctx.run(input, pair, |ctx| { - render(ctx); + viewport_ui_cb(ctx); }); let mut painter = painter.borrow_mut(); @@ -2393,12 +2397,12 @@ mod wgpu_integration { viewport_commands, }; { - let Some((viewport_id, Viewport{window: Some(window), state, render, parent_id })) = viewport_maps.borrow() + let Some((viewport_id, Viewport{window: Some(window), state, viewport_ui_cb, parent_id })) = viewport_maps.borrow() .get(&window_id) .and_then(|id|(viewports.borrow().get(id).map(|w|(*id, w.clone())))) else{ return EventResult::Wait }; // This is used to not render a viewport if is sync - if viewport_id != ViewportId::MAIN && render.is_none() { + if viewport_id != ViewportId::MAIN && viewport_ui_cb.is_none() { if let Some(viewport) = running.viewports.borrow().get(&parent_id) { if let Some(window) = viewport.window.as_ref() { return EventResult::RepaintNext(window.borrow().id()); @@ -2423,8 +2427,11 @@ mod wgpu_integration { app.as_mut(), &window.borrow(), state.borrow_mut().as_mut().unwrap(), - &render.clone(), - ViewportIdPair::new(viewport_id, parent_id), + &viewport_ui_cb.clone(), + ViewportIdPair { + this: viewport_id, + parent: parent_id, + }, ); integration.borrow_mut().handle_platform_output( @@ -2469,11 +2476,11 @@ mod wgpu_integration { out_viewports.retain_mut( |ViewportOutput { pair: ViewportIdPair { this: id, parent }, - render, + viewport_ui_cb, .. }| { if let Some(window) = viewports.borrow_mut().get_mut(id) { - window.render = render.clone(); + window.viewport_ui_cb = viewport_ui_cb.clone(); window.parent_id = *parent; active_viewports_ids.push(*id); false @@ -2490,7 +2497,7 @@ mod wgpu_integration { this: id, parent: parent_id, }, - render, + viewport_ui_cb, } in out_viewports { if builder.icon.is_none() { @@ -2505,7 +2512,7 @@ mod wgpu_integration { Viewport { window: None, state: Rc::new(RefCell::new(None)), - render, + viewport_ui_cb, parent_id, }, ); diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 27b33982da9..e54e6804dfc 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -159,8 +159,8 @@ struct ContextImpl { /// State that is collected during a frame and then cleared frame_state: HashMap, - /// Viewport Id, Parent Viewport Id - frame_stack: Vec, + /// How deeply nested are we? + viewport_stack: Vec, // The output of a frame: graphics: HashMap, @@ -173,7 +173,7 @@ struct ContextImpl { viewports: HashMap, viewport_commands: Vec<(ViewportId, ViewportCommand)>, - viewport_counter: u64, + viewport_id_generator: u64, is_desktop: bool, force_embedding: bool, @@ -196,7 +196,7 @@ struct ContextImpl { impl ContextImpl { fn begin_frame_mut(&mut self, mut new_raw_input: RawInput, pair: ViewportIdPair) { // This is used to pause the last frame - if !self.frame_stack.is_empty() { + if !self.viewport_stack.is_empty() { let viewport_id = self.viewport_id(); self.memory.pause_frame(viewport_id); @@ -210,7 +210,7 @@ impl ContextImpl { ); } - self.frame_stack.push(pair); + self.viewport_stack.push(pair); self.output.entry(self.viewport_id()).or_default(); self.repaint.start_frame(self.viewport_id()); @@ -349,14 +349,18 @@ impl ContextImpl { /// /// In the case of this viewport is the main viewport will be `ViewportId::MAIN` pub(crate) fn viewport_id(&self) -> ViewportId { - self.frame_stack.last().copied().unwrap_or_default().this + self.viewport_stack.last().copied().unwrap_or_default().this } /// Return the `ViewportId` of his parent /// /// In the case of this viewport is the main viewport will be `ViewportId::MAIN` pub(crate) fn parent_viewport_id(&self) -> ViewportId { - self.frame_stack.last().copied().unwrap_or_default().parent + self.viewport_stack + .last() + .copied() + .unwrap_or_default() + .parent } } @@ -1580,7 +1584,7 @@ impl Context { builder, pair, used, - render, + viewport_ui_cb, }| { let retain = *used; @@ -1591,7 +1595,7 @@ impl Context { viewports.push(ViewportOutput { builder: builder.clone(), pair: *pair, - render: render.clone(), + viewport_ui_cb: viewport_ui_cb.clone(), }); (retain || viewport_id != pair.parent) && available_viewports.contains(&pair.parent) @@ -1601,8 +1605,8 @@ impl Context { // This is used to resume the last frame! let is_last = self.write(|ctx| { - ctx.frame_stack.pop(); - ctx.frame_stack.is_empty() + ctx.viewport_stack.pop(); + ctx.viewport_stack.is_empty() }); if !is_last { @@ -2534,14 +2538,14 @@ impl Context { self.read(|ctx| ctx.viewports.get(&id.into()).map(|v| v.pair)) } - /// For integrations: Is used to render a sync viewport! + /// For integrations: Is used to render a sync viewport. /// - /// This will only be set for the current thread! - /// Can be set only one callback per thread! + /// This will only be set for the current thread. + /// Can be set only one callback per thread. /// /// When a viewport sync is created will be rendered by this function /// - /// Look in `crates/eframe/native/run.rs` and search for `set_render_sync_callback` to see for what is used! + /// Look in `crates/eframe/native/run.rs` and search for `set_render_sync_callback` to see for what is used. #[allow(clippy::unused_self)] pub fn set_render_sync_callback( &self, @@ -2584,19 +2588,19 @@ impl Context { /// /// You will need to wrap your viewport state in an `Arc>` or `Arc>`. /// When this is called again with the same id in `ViewportBuilder` the render function for that viewport will be updated. - /// * `render`: will be called when the viewport receives a event or is requested to be rendered + /// * `viewport_ui_cb`: will be called when the viewport receives a event or is requested to be rendered /// /// If this is no more called that viewport will be destroyed. /// /// If you use a [`egui::CentralPanel`] you need to check if the viewport is a new window like: - /// `ctx.viewport_id() != ctx.parent_viewport_id` if false you should create a `egui::Window` + /// `ctx.viewport_id() != ctx.parent_viewport_id` if false you should create a [`egui::Window`]. pub fn create_viewport( &self, viewport_builder: ViewportBuilder, - render: impl Fn(&Context) + Send + Sync + 'static, + viewport_ui_cb: impl Fn(&Context) + Send + Sync + 'static, ) { if self.force_embedding() { - render(self); + viewport_ui_cb(self); } else { self.write(|ctx| { let viewport_id = ctx.viewport_id(); @@ -2604,10 +2608,10 @@ impl Context { window.builder = viewport_builder; window.pair.parent = viewport_id; window.used = true; - window.render = Some(Arc::new(Box::new(render))); + window.viewport_ui_cb = Some(Arc::new(Box::new(viewport_ui_cb))); } else { - let id = ViewportId(ctx.viewport_counter + 1); - ctx.viewport_counter += 1; + let id = ViewportId(ctx.viewport_id_generator + 1); + ctx.viewport_id_generator += 1; ctx.viewports.insert( viewport_builder.id, Viewport { @@ -2617,7 +2621,7 @@ impl Context { parent: viewport_id, }, used: true, - render: Some(Arc::new(Box::new(render))), + viewport_ui_cb: Some(Arc::new(Box::new(viewport_ui_cb))), }, ); } @@ -2627,7 +2631,8 @@ impl Context { /// This creates a new native window, if possible. /// - /// This can only be called in the main thread. + /// The given ui function will be called immediately. + /// This can only be called from the main thread. /// /// When this is called the current viewport will be paused /// This will render in a native window if is possible. @@ -2639,39 +2644,43 @@ impl Context { /// If this is no more called that viewport will be destroyed. /// /// If you use a `egui::CentralPanel` you need to check if the viewport is a new window like: - /// `ctx.viewport_id() != ctx.parent_viewport_id` if false you should create a `egui::Window` + /// `ctx.viewport_id() != ctx.parent_viewport_id` if false you should create a [`egui::Window`]. pub fn create_viewport_sync( &self, viewport_builder: ViewportBuilder, - func: impl FnOnce(&Context) -> T, + viewport_ui_cb: impl FnOnce(&Context) -> T, ) -> T { if self.force_embedding() { - func(self) + viewport_ui_cb(self) } else { - let mut id_pair = ViewportIdPair::MAIN; - self.write(|ctx| { - id_pair.parent = ctx.viewport_id(); + let id_pair = self.write(|ctx| { + let parent = ctx.viewport_id(); + if let Some(window) = ctx.viewports.get_mut(&viewport_builder.id) { + // Existing window.builder = viewport_builder.clone(); - window.pair.parent = id_pair.parent; + window.pair.parent = parent; window.used = true; - window.render = None; - id_pair = window.pair; + window.viewport_ui_cb = None; + window.pair } else { - let id = ViewportId(ctx.viewport_counter + 1); - ctx.viewport_counter += 1; - id_pair.this = id; + // New + let id = ViewportId(ctx.viewport_id_generator + 1); + ctx.viewport_id_generator += 1; + let id_pair = ViewportIdPair { this: id, parent }; ctx.viewports.insert( viewport_builder.id, Viewport { builder: viewport_builder.clone(), pair: id_pair, used: true, - render: None, + viewport_ui_cb: None, }, ); + id_pair } }); + let mut out = None; { let out = &mut out; @@ -2682,12 +2691,14 @@ impl Context { self, viewport_builder, id_pair, - Box::new(move |context| *out = Some(func(context))), + Box::new(move |context| *out = Some(viewport_ui_cb(context))), ); }); } - out.expect("egui backend is implemented incorrectly! Context::set_render_sync_callback") + out.expect( + "egui backend is implemented incorrectly - the user calback was never called", + ) } } } diff --git a/crates/egui/src/viewport.rs b/crates/egui/src/viewport.rs index 93dee79de9f..33b8717d8da 100644 --- a/crates/egui/src/viewport.rs +++ b/crates/egui/src/viewport.rs @@ -34,10 +34,6 @@ impl ViewportIdPair { this: ViewportId::MAIN, parent: ViewportId::MAIN, }; - - pub fn new(this: ViewportId, parent: ViewportId) -> Self { - Self { this, parent } - } } impl std::ops::Deref for ViewportIdPair { @@ -48,9 +44,10 @@ impl std::ops::Deref for ViewportIdPair { } } -/// This is used to render an async viewport -pub type ViewportRender = dyn Fn(&Context) + Sync + Send; +/// The user-code that shows the ui in the viewport. +pub type ViewportUiCallback = dyn Fn(&Context) + Sync + Send; +/// Render the given viewport, calling the given ui callback. pub type ViewportRenderSyncCallback = dyn for<'a> Fn(&Context, ViewportBuilder, ViewportIdPair, Box); @@ -429,12 +426,16 @@ pub(crate) struct Viewport { pub(crate) builder: ViewportBuilder, pub(crate) pair: ViewportIdPair, pub(crate) used: bool, - pub(crate) render: Option>>, + + /// The user-code that shows the GUI. + pub(crate) viewport_ui_cb: Option>>, } #[derive(Clone)] pub struct ViewportOutput { pub builder: ViewportBuilder, pub pair: ViewportIdPair, - pub render: Option>>, + + /// The user-code that shows the GUI. + pub viewport_ui_cb: Option>>, }