From 13eb808939def206cad4dd3edac48b97bee33aa7 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 29 Jul 2022 14:26:27 +0200 Subject: [PATCH 1/2] `eframe`: selectively expose parts of the API based on compile target A lot of the `eframe` API is native-only or web-only. With this PR, only the parts that are implemented for each platform is exposed. This means you'll need to add `#[cfg(target_arch = "wasm32")]` around code that uses the web-parts of the eframe API, and add `#[cfg(not(target_arch = "wasm32"))]` around the parts that are for native/desktop. --- eframe/src/epi.rs | 59 +++++++++++++++++++--------- eframe/src/native/epi_integration.rs | 39 +++++++++--------- eframe/src/web/backend.rs | 16 ++------ eframe/src/web/events.rs | 4 +- egui_demo_app/src/backend_panel.rs | 19 ++++----- egui_demo_app/src/wrap_app.rs | 7 ++-- 6 files changed, 75 insertions(+), 69 deletions(-) diff --git a/eframe/src/epi.rs b/eframe/src/epi.rs index 7d7ac8e8ee8..70c77c5e1dd 100644 --- a/eframe/src/epi.rs +++ b/eframe/src/epi.rs @@ -476,21 +476,17 @@ pub struct IconData { /// allocate textures, and change settings (e.g. window size). pub struct Frame { /// Information about the integration. - #[doc(hidden)] - pub info: IntegrationInfo, + pub(crate) info: IntegrationInfo, /// Where the app can issue commands back to the integration. - #[doc(hidden)] - pub output: backend::AppOutput, + pub(crate) output: backend::AppOutput, /// A place where you can store custom data in a way that persists when you restart the app. - #[doc(hidden)] - pub storage: Option>, + pub(crate) storage: Option>, /// A reference to the underlying [`glow`] (OpenGL) context. #[cfg(feature = "glow")] - #[doc(hidden)] - pub gl: Option>, + pub(crate) gl: Option>, /// Can be used to manage GPU resources for custom rendering with WGPU using /// [`egui::PaintCallback`]s. @@ -500,8 +496,11 @@ pub struct Frame { impl Frame { /// True if you are in a web environment. + /// + /// Equivalent to `cfg!(target_arch = "wasm32")` + #[allow(clippy::unused_self)] pub fn is_web(&self) -> bool { - self.info.web_info.is_some() + cfg!(target_arch = "wasm32") } /// Information about the integration. @@ -538,16 +537,19 @@ impl Frame { /// Signal the app to stop/exit/quit the app (only works for native apps, not web apps). /// The framework will not quit immediately, but at the end of the this frame. + #[cfg(not(target_arch = "wasm32"))] pub fn quit(&mut self) { self.output.quit = true; } /// Set the desired inner size of the window (in egui points). + #[cfg(not(target_arch = "wasm32"))] pub fn set_window_size(&mut self, size: egui::Vec2) { self.output.window_size = Some(size); } /// Set the desired title of the window. + #[cfg(not(target_arch = "wasm32"))] pub fn set_window_title(&mut self, title: &str) { self.output.window_title = Some(title.to_owned()); } @@ -555,16 +557,19 @@ impl Frame { /// Set whether to show window decorations (i.e. a frame around you app). /// /// If false it will be difficult to move and resize the app. + #[cfg(not(target_arch = "wasm32"))] pub fn set_decorations(&mut self, decorated: bool) { self.output.decorated = Some(decorated); } /// Turn borderless fullscreen on/off (native only). + #[cfg(not(target_arch = "wasm32"))] pub fn set_fullscreen(&mut self, fullscreen: bool) { self.output.fullscreen = Some(fullscreen); } - /// set the position of the outer window + /// set the position of the outer window. + #[cfg(not(target_arch = "wasm32"))] pub fn set_window_pos(&mut self, pos: egui::Pos2) { self.output.window_pos = Some(pos); } @@ -573,35 +578,41 @@ impl Frame { /// movement of the cursor while the primary mouse button is down. /// /// Does not work on the web. + #[cfg(not(target_arch = "wasm32"))] pub fn drag_window(&mut self) { self.output.drag_window = true; } /// Set the visibility of the window. + #[cfg(not(target_arch = "wasm32"))] pub fn set_visible(&mut self, visible: bool) { self.output.visible = Some(visible); } /// for integrations only: call once per frame - #[doc(hidden)] - pub fn take_app_output(&mut self) -> backend::AppOutput { + pub(crate) fn take_app_output(&mut self) -> backend::AppOutput { std::mem::take(&mut self.output) } } /// Information about the web environment (if applicable). #[derive(Clone, Debug)] +#[cfg(target_arch = "wasm32")] pub struct WebInfo { /// Information about the URL. pub location: Location, } /// Information about the application's main window, if available. +#[cfg(not(target_arch = "wasm32"))] #[derive(Clone, Debug)] pub struct WindowInfo { /// Coordinates of the window's outer top left corner, relative to the top left corner of the first display. + /// /// Unit: egui points (logical pixels). - pub position: egui::Pos2, + /// + /// `None` = unknown. + pub position: Option, /// Are we in fullscreen mode? pub fullscreen: bool, @@ -613,6 +624,7 @@ pub struct WindowInfo { /// Information about the URL. /// /// Everything has been percent decoded (`%20` -> ` ` etc). +#[cfg(target_arch = "wasm32")] #[derive(Clone, Debug)] pub struct Location { /// The full URL (`location.href`) without the hash. @@ -667,8 +679,9 @@ pub struct Location { /// Information about the integration passed to the use app each frame. #[derive(Clone, Debug)] pub struct IntegrationInfo { - /// If the app is running in a Web context, this returns information about the environment. - pub web_info: Option, + /// Information about the surrounding web environment. + #[cfg(target_arch = "wasm32")] + pub web_info: WebInfo, /// Does the OS use dark or light mode? /// @@ -682,8 +695,9 @@ pub struct IntegrationInfo { /// The OS native pixels-per-point pub native_pixels_per_point: Option, - /// Window-specific geometry information, if provided by the platform. - pub window_info: Option, + /// The position and size of the native window. + #[cfg(not(target_arch = "wasm32"))] + pub window_info: WindowInfo, } // ---------------------------------------------------------------------------- @@ -736,35 +750,42 @@ pub const APP_KEY: &str = "app"; // ---------------------------------------------------------------------------- /// You only need to look here if you are writing a backend for `epi`. -#[doc(hidden)] -pub mod backend { +pub(crate) mod backend { /// Action that can be taken by the user app. #[derive(Clone, Debug, Default)] #[must_use] pub struct AppOutput { /// Set to `true` to stop the app. /// This does nothing for web apps. + #[cfg(not(target_arch = "wasm32"))] pub quit: bool, /// Set to some size to resize the outer window (e.g. glium window) to this size. + #[cfg(not(target_arch = "wasm32"))] pub window_size: Option, /// Set to some string to rename the outer window (e.g. glium window) to this title. + #[cfg(not(target_arch = "wasm32"))] pub window_title: Option, /// Set to some bool to change window decorations. + #[cfg(not(target_arch = "wasm32"))] pub decorated: Option, /// Set to some bool to change window fullscreen. + #[cfg(not(target_arch = "wasm32"))] // TODO: implement fullscreen on web pub fullscreen: Option, /// Set to true to drag window while primary mouse button is down. + #[cfg(not(target_arch = "wasm32"))] pub drag_window: bool, /// Set to some position to move the outer window (e.g. glium window) to this position + #[cfg(not(target_arch = "wasm32"))] pub window_pos: Option, /// Set to some bool to change window visibility. + #[cfg(not(target_arch = "wasm32"))] pub visible: Option, } } diff --git a/eframe/src/native/epi_integration.rs b/eframe/src/native/epi_integration.rs index 0c4ae5ad024..f85912138cc 100644 --- a/eframe/src/native/epi_integration.rs +++ b/eframe/src/native/epi_integration.rs @@ -9,26 +9,24 @@ pub fn points_to_size(points: egui::Vec2) -> winit::dpi::LogicalSize { } } -pub fn read_window_info( - window: &winit::window::Window, - pixels_per_point: f32, -) -> Option { - match window.outer_position() { - Ok(pos) => { - let pos = pos.to_logical::(pixels_per_point.into()); - let size = window - .inner_size() - .to_logical::(pixels_per_point.into()); - Some(WindowInfo { - position: egui::Pos2 { x: pos.x, y: pos.y }, - fullscreen: window.fullscreen().is_some(), - size: egui::Vec2 { - x: size.width, - y: size.height, - }, - }) - } - Err(_) => None, +pub fn read_window_info(window: &winit::window::Window, pixels_per_point: f32) -> WindowInfo { + let position = window + .outer_position() + .ok() + .map(|pos| pos.to_logical::(pixels_per_point.into())) + .map(|pos| egui::Pos2 { x: pos.x, y: pos.y }); + + let size = window + .inner_size() + .to_logical::(pixels_per_point.into()); + + WindowInfo { + position, + fullscreen: window.fullscreen().is_some(), + size: egui::Vec2 { + x: size.width, + y: size.height, + }, } } @@ -206,7 +204,6 @@ impl EpiIntegration { let frame = epi::Frame { info: epi::IntegrationInfo { - web_info: None, system_theme, cpu_usage: None, native_pixels_per_point: Some(native_pixels_per_point(window)), diff --git a/eframe/src/web/backend.rs b/eframe/src/web/backend.rs index 4c445d29ec2..301369cee71 100644 --- a/eframe/src/web/backend.rs +++ b/eframe/src/web/backend.rs @@ -170,13 +170,12 @@ impl AppRunner { }; let info = epi::IntegrationInfo { - web_info: Some(epi::WebInfo { + web_info: epi::WebInfo { location: web_location(), - }), + }, system_theme, cpu_usage: None, native_pixels_per_point: Some(native_pixels_per_point()), - window_info: None, }; let storage = LocalStorage::default(); @@ -293,16 +292,7 @@ impl AppRunner { { let app_output = self.frame.take_app_output(); - let epi::backend::AppOutput { - quit: _, // Can't quit a web page - window_size: _, // Can't resize a web page - window_title: _, // TODO(emilk): change title of window - decorated: _, // Can't toggle decorations - fullscreen: _, // TODO(emilk): fullscreen web window - drag_window: _, // Can't be dragged - window_pos: _, // Can't set position of a web page - visible: _, // Can't hide a web page - } = app_output; + let epi::backend::AppOutput {} = app_output; } self.frame.info.cpu_usage = Some((now_sec() - frame_start) as f32); diff --git a/eframe/src/web/events.rs b/eframe/src/web/events.rs index 27c1cc2e19c..bfbf1e3b669 100644 --- a/eframe/src/web/events.rs +++ b/eframe/src/web/events.rs @@ -181,9 +181,7 @@ pub fn install_document_events(runner_container: &AppRunnerContainer) -> Result< "hashchange", |_: web_sys::Event, mut runner_lock| { // `epi::Frame::info(&self)` clones `epi::IntegrationInfo`, but we need to modify the original here - if let Some(web_info) = &mut runner_lock.frame.info.web_info { - web_info.location.hash = location_hash(); - } + runner_lock.frame.info.web_info.location.hash = location_hash(); }, )?; diff --git a/egui_demo_app/src/backend_panel.rs b/egui_demo_app/src/backend_panel.rs index bb9e9a0269d..f9acba54c31 100644 --- a/egui_demo_app/src/backend_panel.rs +++ b/egui_demo_app/src/backend_panel.rs @@ -136,7 +136,8 @@ impl BackendPanel { ui.ctx().options().screen_reader = screen_reader; } - if !frame.is_web() { + #[cfg(not(target_arch = "wasm32"))] + { ui.separator(); if ui.button("Quit").clicked() { frame.quit(); @@ -159,11 +160,10 @@ impl BackendPanel { ui.label("."); }); - if let Some(web_info) = &frame.info().web_info { - ui.collapsing("Web info (location)", |ui| { - ui.monospace(format!("{:#?}", web_info.location)); - }); - } + #[cfg(target_arch = "wasm32")] + ui.collapsing("Web info (location)", |ui| { + ui.monospace(format!("{:#?}", frame.info().web_info.location)); + }); // For instance: `eframe` web sets `pixels_per_point` every frame to force // egui to use the same scale as the web zoom factor. @@ -174,10 +174,11 @@ impl BackendPanel { } } - if !frame.is_web() { + #[cfg(not(target_arch = "wasm32"))] + { ui.horizontal(|ui| { - if let Some(window_info) = &frame.info().window_info { - let mut fullscreen = window_info.fullscreen; + { + let mut fullscreen = frame.info().window_info.fullscreen; ui.checkbox(&mut fullscreen, "🗖 Fullscreen") .on_hover_text("Fullscreen the window"); frame.set_fullscreen(fullscreen); diff --git a/egui_demo_app/src/wrap_app.rs b/egui_demo_app/src/wrap_app.rs index ffdb8cccbc5..1a844b96608 100644 --- a/egui_demo_app/src/wrap_app.rs +++ b/egui_demo_app/src/wrap_app.rs @@ -168,10 +168,9 @@ impl eframe::App for WrapApp { } fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { - if let Some(web_info) = frame.info().web_info.as_ref() { - if let Some(anchor) = web_info.location.hash.strip_prefix('#') { - self.state.selected_anchor = anchor.to_owned(); - } + #[cfg(target_arch = "wasm32")] + if let Some(anchor) = frame.info().web_info.location.hash.strip_prefix('#') { + self.state.selected_anchor = anchor.to_owned(); } if self.state.selected_anchor.is_empty() { From 1d26701807f507b9d99000d1c82e8866f5b20a9f Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Fri, 29 Jul 2022 14:27:49 +0200 Subject: [PATCH 2/2] Update changelog --- eframe/CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/eframe/CHANGELOG.md b/eframe/CHANGELOG.md index 3cdffbea48d..b61a4a256f4 100644 --- a/eframe/CHANGELOG.md +++ b/eframe/CHANGELOG.md @@ -14,6 +14,7 @@ NOTE: [`egui-winit`](../egui-winit/CHANGELOG.md), [`egui_glium`](../egui_glium/C * Use `Arc` for `glow::Context` instead of `Rc` ([#1640](https://github.com/emilk/egui/pull/1640)). * Fixed bug where the result returned from `App::on_exit_event` would sometimes be ignored ([#1696](https://github.com/emilk/egui/pull/1696)). * Added `NativeOptions::follow_system_theme` and `NativeOptions::default_theme` ([#1726](https://github.com/emilk/egui/pull/1726)). +* Selectively expose parts of the API based on target arch (`wasm32` or not) ([#1867](https://github.com/emilk/egui/pull/1867)). #### Desktop/Native: * Fixed clipboard on Wayland ([#1613](https://github.com/emilk/egui/pull/1613)).