diff --git a/CHANGELOG.md b/CHANGELOG.md index 3612db70..66d8be0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ NOTE: [`epaint`](epaint/CHANGELOG.md), [`eframe`](eframe/CHANGELOG.md), [`egui_w ### Added ⭐ * Added `Shape::Callback` for backend-specific painting ([#1351](https://github.com/emilk/egui/pull/1351)). * Added `Frame::canvas` ([#1362](https://github.com/emilk/egui/pull/1362)). -* `Context::request_repaint` will wake up UI thread, if integrations has called `Context::set_request_repaint_callback` ([#1366](https://github.com/emilk/egui/pull/1366)). +* `Context::request_repaint` will wake up UI thread, if integrations has called `Context::with_repaint_callback` ([#1366](https://github.com/emilk/egui/pull/1366)). * Added `Ui::push_id` ([#1374](https://github.com/emilk/egui/pull/1374)). ### Changed 🔧 diff --git a/egui-winit/src/epi.rs b/egui-winit/src/epi.rs index 2674db33..f4d7eab7 100644 --- a/egui-winit/src/epi.rs +++ b/egui-winit/src/epi.rs @@ -229,12 +229,11 @@ pub struct EpiIntegration { impl EpiIntegration { pub fn new( integration_name: &'static str, + egui_ctx: egui::Context, max_texture_side: usize, window: &winit::window::Window, persistence: crate::epi::Persistence, ) -> Self { - let egui_ctx = egui::Context::default(); - *egui_ctx.memory() = persistence.load_memory().unwrap_or_default(); let prefer_dark_mode = prefer_dark_mode(); diff --git a/egui/src/context.rs b/egui/src/context.rs index 536461de..20eb35c0 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -1,5 +1,8 @@ // #![warn(missing_docs)] +use std::sync::atomic::AtomicU32; +use std::sync::atomic::Ordering::SeqCst; + use crate::{ animation_manager::AnimationManager, data::output::PlatformOutput, frame_state::FrameState, input_state::*, layers::GraphicLayers, memory::Options, output::FullOutput, TextureHandle, *, @@ -27,6 +30,25 @@ impl Default for WrappedTextureManager { // ---------------------------------------------------------------------------- +struct RepaintInfo { + /// While positive, keep requesting repaints. Decrement at the end of each frame. + repaint_requests: AtomicU32, + request_repaint_callbacks: Option>, +} + +impl Default for RepaintInfo { + fn default() -> Self { + Self { + // Start with painting an extra frame to compensate for some widgets + // that take two frames before they "settle": + repaint_requests: 1.into(), + request_repaint_callbacks: None, + } + } +} + +// ---------------------------------------------------------------------------- + #[derive(Default)] struct ContextImpl { /// `None` until the start of the first frame. @@ -45,10 +67,6 @@ struct ContextImpl { output: PlatformOutput, paint_stats: PaintStats, - - /// While positive, keep requesting repaints. Decrement at the end of each frame. - repaint_requests: u32, - request_repaint_callbacks: Option>, } impl ContextImpl { @@ -142,33 +160,41 @@ impl ContextImpl { /// paint(full_output.textures_delta, clipped_primitives); /// } /// ``` -#[derive(Clone)] -pub struct Context(Arc>); +#[derive(Clone, Default)] +pub struct Context { + ctx: Arc>, + repaint_info: Arc, +} impl std::cmp::PartialEq for Context { fn eq(&self, other: &Context) -> bool { - Arc::ptr_eq(&self.0, &other.0) - } -} - -impl Default for Context { - fn default() -> Self { - Self(Arc::new(RwLock::new(ContextImpl { - // Start with painting an extra frame to compensate for some widgets - // that take two frames before they "settle": - repaint_requests: 1, - ..ContextImpl::default() - }))) + Arc::ptr_eq(&self.ctx, &other.ctx) } } impl Context { + pub fn new() -> Self { + Self::default() + } + + /// Construct a [`Context`] with a callback that is called when the user calls + /// [`Context::request_repaint`]. + pub fn with_repaint_callback(callback: impl Fn() + Send + Sync + 'static) -> Self { + Self { + ctx: Default::default(), + repaint_info: Arc::new(RepaintInfo { + request_repaint_callbacks: Some(Box::new(callback)), + ..Default::default() + }), + } + } + fn read(&self) -> RwLockReadGuard<'_, ContextImpl> { - self.0.read() + self.ctx.read() } fn write(&self) -> RwLockWriteGuard<'_, ContextImpl> { - self.0.write() + self.ctx.write() } /// Run the ui code for one frame. @@ -539,25 +565,16 @@ impl Context { /// Call as many times as you wish, only one repaint will be issued. /// /// If called from outside the UI thread, the UI thread will wake up and run, - /// provided the egui integration has set that up via [`Self::set_request_repaint_callback`] + /// provided the egui integration has set that up via [`Self::with_repaint_callback`] /// (this will work on `eframe`). pub fn request_repaint(&self) { // request two frames of repaint, just to cover some corner cases (frame delays): - let mut ctx = self.write(); - ctx.repaint_requests = 2; - if let Some(callback) = &ctx.request_repaint_callbacks { + self.repaint_info.repaint_requests.store(2, SeqCst); + if let Some(callback) = &self.repaint_info.request_repaint_callbacks { (callback)(); } } - /// For integrations: this callback will be called when an egui user calls [`Self::request_repaint`]. - /// - /// This lets you wake up a sleeping UI thread. - pub fn set_request_repaint_callback(&self, callback: impl Fn() + Send + Sync + 'static) { - let callback = Box::new(callback); - self.write().request_repaint_callbacks = Some(callback); - } - /// Tell `egui` which fonts to use. /// /// The default `egui` fonts only support latin and cyrillic alphabets, @@ -776,8 +793,8 @@ impl Context { let platform_output: PlatformOutput = std::mem::take(&mut self.output()); - let needs_repaint = if self.read().repaint_requests > 0 { - self.write().repaint_requests -= 1; + let needs_repaint = if self.repaint_info.repaint_requests.load(SeqCst) > 0 { + self.repaint_info.repaint_requests.fetch_sub(1, SeqCst); true } else { false diff --git a/egui_glow/src/epi_backend.rs b/egui_glow/src/epi_backend.rs index 72611673..b5cf294e 100644 --- a/egui_glow/src/epi_backend.rs +++ b/egui_glow/src/epi_backend.rs @@ -46,20 +46,21 @@ pub fn run(app_name: &str, native_options: &epi::NativeOptions, app_creator: epi let mut painter = crate::Painter::new(gl.clone(), None, "") .unwrap_or_else(|error| panic!("some OpenGL error occurred {}\n", error)); + let event_loop_proxy = parking_lot::Mutex::new(event_loop.create_proxy()); + let egui_ctx = egui::Context::with_repaint_callback({ + move || { + event_loop_proxy.lock().send_event(RequestRepaintEvent).ok(); + } + }); + let mut integration = egui_winit::epi::EpiIntegration::new( "egui_glow", + egui_ctx, painter.max_texture_side(), gl_window.window(), persistence, ); - { - let event_loop_proxy = parking_lot::Mutex::new(event_loop.create_proxy()); - integration.egui_ctx.set_request_repaint_callback(move || { - event_loop_proxy.lock().send_event(RequestRepaintEvent).ok(); - }); - } - let mut app = app_creator(&epi::CreationContext { egui_ctx: integration.egui_ctx.clone(), integration_info: integration.frame.info(), diff --git a/egui_web/src/backend.rs b/egui_web/src/backend.rs index 62424350..44eb30f2 100644 --- a/egui_web/src/backend.rs +++ b/egui_web/src/backend.rs @@ -159,14 +159,12 @@ impl AppRunner { let needs_repaint: std::sync::Arc = Default::default(); - let egui_ctx = egui::Context::default(); - - { + let egui_ctx = egui::Context::with_repaint_callback({ let needs_repaint = needs_repaint.clone(); - egui_ctx.set_request_repaint_callback(move || { + move || { needs_repaint.0.store(true, SeqCst); - }); - } + } + }); load_memory(&egui_ctx); if prefer_dark_mode == Some(true) {