diff --git a/crates/eframe/src/native/run.rs b/crates/eframe/src/native/run.rs index 16a743968..033c6df1e 100644 --- a/crates/eframe/src/native/run.rs +++ b/crates/eframe/src/native/run.rs @@ -31,7 +31,7 @@ pub const IS_DESKTOP: bool = cfg!(any( // ---------------------------------------------------------------------------- thread_local! { - /// This makes `Context::create_viewport_sync` to have a native window in the same frame! + /// This makes [`Context::show_viewport_immediate`] to have a native window in the same frame! pub static WINIT_EVENT_LOOP: RefCell<*const EventLoopWindowTarget> = RefCell::new(std::ptr::null()); } @@ -1171,7 +1171,7 @@ mod glow_integration { }; glutin .init_window(&win, event_loop) - .expect("Cannot init window on egui::Context::create_viewport_sync"); + .expect("Cannot init window on egui::Context::show_viewport_immediate"); } // Rendering the sync viewport @@ -1218,7 +1218,7 @@ mod glow_integration { .is_current(glutin.current_gl_context.as_ref().unwrap()) { let builder = &&glutin.builders[&window.id_pair.this]; - log::error!("egui::create_viewport_sync with title: `{:?}` is not created in main thread, try to use wgpu!", builder.title.clone().unwrap_or_default()); + log::error!("egui::show_viewport_immediate with title: `{:?}` is not created in main thread, try to use wgpu!", builder.title.clone().unwrap_or_default()); } egui_glow::painter::clear(gl, screen_size_in_pixels, [0.0, 0.0, 0.0, 0.0]); diff --git a/crates/egui/src/context.rs b/crates/egui/src/context.rs index 9c4efaa12..763f10461 100644 --- a/crates/egui/src/context.rs +++ b/crates/egui/src/context.rs @@ -2525,7 +2525,7 @@ impl Context { }); } - /// If `true`, [`Self::create_viewport_async`] and [`Self::create_viewport_sync`] will + /// If `true`, [`Self::show_viewport`] and [`Self::show_viewport_immediate`] will /// embed the new viewports as [`crate::Window`]s instead of spawning a new native window. /// /// `eframe` sets this to `false` on supported platforms, @@ -2534,7 +2534,7 @@ impl Context { self.read(|ctx| ctx.embed_viewports) } - /// If `true`, [`Self::create_viewport_async`] and [`Self::create_viewport_sync`] will + /// If `true`, [`Self::show_viewport`] and [`Self::show_viewport_immediate`] will /// embed the new viewports as [`crate::Window`]s instead of spawning a new native window. /// /// `eframe` sets this to `false` on supported platforms, @@ -2555,17 +2555,24 @@ impl Context { /// This creates a new native window, if possible. /// - /// You should call this each frame when the viewport should be visible. + /// You need to call this each frame when the child viewport should exist. + /// + /// The given callback will be called whenever the child viewport needs repainting, + /// e.g. on an event or when [`Self::request_repaint`] is called. + /// This means it may be called multiple times, for instance while the + /// parent viewport (the caller) is sleeping but the child viewport is animating. /// /// 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. - /// * `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. + /// You can also use [`Self::show_viewport_immediate`], which uses a simpler `FnOnce` + /// with no need for `Send` or `Sync`. The downside is that it will require + /// the parent viewport (the caller) to repaint anytime the child is repainted, + /// and vice versa. /// /// If you use a [`crate::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 [`crate::Window`]. - pub fn create_viewport_async( + pub fn show_viewport( &self, viewport_builder: ViewportBuilder, viewport_ui_cb: impl Fn(&Context) + Send + Sync + 'static, @@ -2600,25 +2607,23 @@ impl Context { /// This creates a new native window, if possible. /// + /// You need to call this each frame when the child viewport should exist. + /// /// The given ui function will be called immediately. - /// This can only be called from the main thread. + /// This may only be called on the main thread. /// - /// If [`Context::embed_viewports`] is true, or if the current egui - /// backend does not support sync viewports, the given callback - /// will be called immediately and the function will return. + /// This call will pause the current viewport and render the child viewport in its own window. + /// This means that the child viewport will not be repainted when the parent viewport is repainted, and vice versa. + /// This can lead to unnecessary repaint. + /// To avoid this, use [`Self::show_viewport`] instead. /// - /// When this is called the current viewport will be paused - /// This will render in a native window if is possible. - /// When this finishes then the last viewport will continue drawing - /// This is bad for performance but easy to use. - /// - /// For better performance use `Self::create_viewport` - /// - /// If this is no more called that viewport will be destroyed. + /// If [`Context::embed_viewports`] is `true` (e.g. if the current egui + /// backend does not support multiple viewports), the given callback + /// will be called immediately, embedding the new viewport in the current one. /// /// 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 [`crate::Window`]. - pub fn create_viewport_sync( + pub fn show_viewport_immediate( &self, viewport_builder: ViewportBuilder, viewport_ui_cb: impl FnOnce(&Context) -> T, diff --git a/crates/egui/src/viewport.rs b/crates/egui/src/viewport.rs index 6ede603ad..200992ff0 100644 --- a/crates/egui/src/viewport.rs +++ b/crates/egui/src/viewport.rs @@ -1,9 +1,11 @@ //! egui supports multiple viewports, corresponding to multiple native windows. //! -//! Viewports come in two flavors: "sync" and "async". +//! Viewports come in two flavors: "deferred" (the default) and "immediate". //! -//! * Sync viewports are executed immediately. -//! * Async viewports are executed later. +//! * Deferred viewports have callbacks that are called multiple +//! times as the viewport receives events, or need repaitning. +//! * Immediate viewports are executed immediately with an [`FnOnce`] callback, +//! locking the parent and child viewports together so that they both must update at the same time. use std::sync::Arc; @@ -83,7 +85,7 @@ impl ViewportIdPair { }; } -/// The user-code that shows the ui in the viewport, used for "async" viewports. +/// The user-code that shows the ui in the viewport, used for deferred viewports. pub type ViewportUiCallback = dyn Fn(&Context) + Sync + Send; /// Render the given viewport, calling the given ui callback. @@ -655,9 +657,9 @@ pub(crate) struct Viewport { /// Has this viewport been updated this frame? pub(crate) used: bool, - /// The user-code that shows the GUI, used for "async" viewports. + /// The user-code that shows the GUI, used for deferred viewports. /// - /// `None` for "sync" viewports. + /// `None` for immediate viewports. pub(crate) viewport_ui_cb: Option>>, } @@ -668,8 +670,8 @@ pub struct ViewportOutput { /// Id of us and our parent. pub id_pair: ViewportIdPair, - /// The user-code that shows the GUI, used for "async" viewports. + /// The user-code that shows the GUI, used for deferred viewports. /// - /// `None` for "sync" viewports. + /// `None` for immediate viewports. pub viewport_ui_cb: Option>>, } diff --git a/examples/test_viewports/src/main.rs b/examples/test_viewports/src/main.rs index 81f78bd6f..178514f6f 100644 --- a/examples/test_viewports/src/main.rs +++ b/examples/test_viewports/src/main.rs @@ -25,33 +25,33 @@ fn main() { pub struct ViewportState { pub id: ViewportId, pub visible: bool, - pub sync: bool, + pub immediate: bool, pub title: String, pub children: Vec>>, } impl ViewportState { - pub fn new_async( + pub fn new_deferred( title: &'static str, children: Vec>>, ) -> Arc> { Arc::new(RwLock::new(Self { id: ViewportId::from_hash_of(title), visible: false, - sync: false, + immediate: false, title: title.into(), children, })) } - pub fn new_sync( + pub fn new_immediate( title: &'static str, children: Vec>>, ) -> Arc> { Arc::new(RwLock::new(Self { id: ViewportId::from_hash_of(title), visible: false, - sync: true, + immediate: true, title: title.into(), children, })) @@ -62,23 +62,23 @@ impl ViewportState { return; } let vp_id = vp_state.read().id; - let sync = vp_state.read().sync; + let immediate = vp_state.read().immediate; let title = vp_state.read().title.clone(); let vp_builder = ViewportBuilder::new(vp_id) .with_title(&title) .with_inner_size(Some(egui::vec2(450.0, 400.0))); - if sync { + if immediate { let mut vp_state = vp_state.write(); - ctx.create_viewport_sync(vp_builder, move |ctx| { + ctx.show_viewport_immediate(vp_builder, move |ctx| { show_as_popup(ctx, &title, vp_id.into(), |ui: &mut egui::Ui| { generic_child_ui(ui, &mut vp_state); }); }); } else { let count = Arc::new(RwLock::new(0)); - ctx.create_viewport_async(vp_builder, move |ctx| { + ctx.show_viewport(vp_builder, move |ctx| { let mut vp_state = vp_state.write(); let count = count.clone(); show_as_popup(ctx, &title, vp_id.into(), move |ui: &mut egui::Ui| { @@ -101,18 +101,30 @@ impl Default for App { fn default() -> Self { Self { top: vec![ - ViewportState::new_async( - "Top Async Viewport", + ViewportState::new_deferred( + "Top Deferred Viewport", vec![ - ViewportState::new_async("AA: Async Viewport in Async Viewport", vec![]), - ViewportState::new_sync("AS: Sync Viewport in Async Viewport", vec![]), + ViewportState::new_deferred( + "DD: Deferred Viewport in Deferred Viewport", + vec![], + ), + ViewportState::new_immediate( + "DS: Immediate Viewport in Deferred Viewport", + vec![], + ), ], ), - ViewportState::new_sync( - "Top Sync Viewport", + ViewportState::new_immediate( + "Top Immediate Viewport", vec![ - ViewportState::new_async("SA: Async Viewport in Sync Viewport", vec![]), - ViewportState::new_sync("SS: Sync Viewport in Sync Viewport", vec![]), + ViewportState::new_deferred( + "SD: Deferred Viewport in Immediate Viewport", + vec![], + ), + ViewportState::new_immediate( + "SS: Immediate Viewport in Immediate Viewport", + vec![], + ), ], ), ],