eframe: selectively expose parts of the API based on compile target (#1867)

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.
This commit is contained in:
Emil Ernerfeldt 2022-07-29 14:37:23 +02:00 committed by GitHub
parent 51052c08e9
commit 8c09804abd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 76 additions and 69 deletions

View file

@ -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)). * 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)). * 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)). * 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: #### Desktop/Native:
* Fixed clipboard on Wayland ([#1613](https://github.com/emilk/egui/pull/1613)). * Fixed clipboard on Wayland ([#1613](https://github.com/emilk/egui/pull/1613)).

View file

@ -476,21 +476,17 @@ pub struct IconData {
/// allocate textures, and change settings (e.g. window size). /// allocate textures, and change settings (e.g. window size).
pub struct Frame { pub struct Frame {
/// Information about the integration. /// Information about the integration.
#[doc(hidden)] pub(crate) info: IntegrationInfo,
pub info: IntegrationInfo,
/// Where the app can issue commands back to the integration. /// Where the app can issue commands back to the integration.
#[doc(hidden)] pub(crate) output: backend::AppOutput,
pub output: backend::AppOutput,
/// A place where you can store custom data in a way that persists when you restart the app. /// A place where you can store custom data in a way that persists when you restart the app.
#[doc(hidden)] pub(crate) storage: Option<Box<dyn Storage>>,
pub storage: Option<Box<dyn Storage>>,
/// A reference to the underlying [`glow`] (OpenGL) context. /// A reference to the underlying [`glow`] (OpenGL) context.
#[cfg(feature = "glow")] #[cfg(feature = "glow")]
#[doc(hidden)] pub(crate) gl: Option<std::sync::Arc<glow::Context>>,
pub gl: Option<std::sync::Arc<glow::Context>>,
/// Can be used to manage GPU resources for custom rendering with WGPU using /// Can be used to manage GPU resources for custom rendering with WGPU using
/// [`egui::PaintCallback`]s. /// [`egui::PaintCallback`]s.
@ -500,8 +496,11 @@ pub struct Frame {
impl Frame { impl Frame {
/// True if you are in a web environment. /// True if you are in a web environment.
///
/// Equivalent to `cfg!(target_arch = "wasm32")`
#[allow(clippy::unused_self)]
pub fn is_web(&self) -> bool { pub fn is_web(&self) -> bool {
self.info.web_info.is_some() cfg!(target_arch = "wasm32")
} }
/// Information about the integration. /// 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). /// 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. /// The framework will not quit immediately, but at the end of the this frame.
#[cfg(not(target_arch = "wasm32"))]
pub fn quit(&mut self) { pub fn quit(&mut self) {
self.output.quit = true; self.output.quit = true;
} }
/// Set the desired inner size of the window (in egui points). /// 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) { pub fn set_window_size(&mut self, size: egui::Vec2) {
self.output.window_size = Some(size); self.output.window_size = Some(size);
} }
/// Set the desired title of the window. /// Set the desired title of the window.
#[cfg(not(target_arch = "wasm32"))]
pub fn set_window_title(&mut self, title: &str) { pub fn set_window_title(&mut self, title: &str) {
self.output.window_title = Some(title.to_owned()); 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). /// 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. /// 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) { pub fn set_decorations(&mut self, decorated: bool) {
self.output.decorated = Some(decorated); self.output.decorated = Some(decorated);
} }
/// Turn borderless fullscreen on/off (native only). /// Turn borderless fullscreen on/off (native only).
#[cfg(not(target_arch = "wasm32"))]
pub fn set_fullscreen(&mut self, fullscreen: bool) { pub fn set_fullscreen(&mut self, fullscreen: bool) {
self.output.fullscreen = Some(fullscreen); 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) { pub fn set_window_pos(&mut self, pos: egui::Pos2) {
self.output.window_pos = Some(pos); self.output.window_pos = Some(pos);
} }
@ -573,35 +578,41 @@ impl Frame {
/// movement of the cursor while the primary mouse button is down. /// movement of the cursor while the primary mouse button is down.
/// ///
/// Does not work on the web. /// Does not work on the web.
#[cfg(not(target_arch = "wasm32"))]
pub fn drag_window(&mut self) { pub fn drag_window(&mut self) {
self.output.drag_window = true; self.output.drag_window = true;
} }
/// Set the visibility of the window. /// Set the visibility of the window.
#[cfg(not(target_arch = "wasm32"))]
pub fn set_visible(&mut self, visible: bool) { pub fn set_visible(&mut self, visible: bool) {
self.output.visible = Some(visible); self.output.visible = Some(visible);
} }
/// for integrations only: call once per frame /// for integrations only: call once per frame
#[doc(hidden)] pub(crate) fn take_app_output(&mut self) -> backend::AppOutput {
pub fn take_app_output(&mut self) -> backend::AppOutput {
std::mem::take(&mut self.output) std::mem::take(&mut self.output)
} }
} }
/// Information about the web environment (if applicable). /// Information about the web environment (if applicable).
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
#[cfg(target_arch = "wasm32")]
pub struct WebInfo { pub struct WebInfo {
/// Information about the URL. /// Information about the URL.
pub location: Location, pub location: Location,
} }
/// Information about the application's main window, if available. /// Information about the application's main window, if available.
#[cfg(not(target_arch = "wasm32"))]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct WindowInfo { pub struct WindowInfo {
/// Coordinates of the window's outer top left corner, relative to the top left corner of the first display. /// Coordinates of the window's outer top left corner, relative to the top left corner of the first display.
///
/// Unit: egui points (logical pixels). /// Unit: egui points (logical pixels).
pub position: egui::Pos2, ///
/// `None` = unknown.
pub position: Option<egui::Pos2>,
/// Are we in fullscreen mode? /// Are we in fullscreen mode?
pub fullscreen: bool, pub fullscreen: bool,
@ -613,6 +624,7 @@ pub struct WindowInfo {
/// Information about the URL. /// Information about the URL.
/// ///
/// Everything has been percent decoded (`%20` -> ` ` etc). /// Everything has been percent decoded (`%20` -> ` ` etc).
#[cfg(target_arch = "wasm32")]
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct Location { pub struct Location {
/// The full URL (`location.href`) without the hash. /// 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. /// Information about the integration passed to the use app each frame.
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct IntegrationInfo { pub struct IntegrationInfo {
/// If the app is running in a Web context, this returns information about the environment. /// Information about the surrounding web environment.
pub web_info: Option<WebInfo>, #[cfg(target_arch = "wasm32")]
pub web_info: WebInfo,
/// Does the OS use dark or light mode? /// Does the OS use dark or light mode?
/// ///
@ -682,8 +695,9 @@ pub struct IntegrationInfo {
/// The OS native pixels-per-point /// The OS native pixels-per-point
pub native_pixels_per_point: Option<f32>, pub native_pixels_per_point: Option<f32>,
/// Window-specific geometry information, if provided by the platform. /// The position and size of the native window.
pub window_info: Option<WindowInfo>, #[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`. /// You only need to look here if you are writing a backend for `epi`.
#[doc(hidden)] pub(crate) mod backend {
pub mod backend {
/// Action that can be taken by the user app. /// Action that can be taken by the user app.
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
#[must_use] #[must_use]
pub struct AppOutput { pub struct AppOutput {
/// Set to `true` to stop the app. /// Set to `true` to stop the app.
/// This does nothing for web apps. /// This does nothing for web apps.
#[cfg(not(target_arch = "wasm32"))]
pub quit: bool, pub quit: bool,
/// Set to some size to resize the outer window (e.g. glium window) to this size. /// 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<egui::Vec2>, pub window_size: Option<egui::Vec2>,
/// Set to some string to rename the outer window (e.g. glium window) to this title. /// 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<String>, pub window_title: Option<String>,
/// Set to some bool to change window decorations. /// Set to some bool to change window decorations.
#[cfg(not(target_arch = "wasm32"))]
pub decorated: Option<bool>, pub decorated: Option<bool>,
/// Set to some bool to change window fullscreen. /// Set to some bool to change window fullscreen.
#[cfg(not(target_arch = "wasm32"))] // TODO: implement fullscreen on web
pub fullscreen: Option<bool>, pub fullscreen: Option<bool>,
/// Set to true to drag window while primary mouse button is down. /// Set to true to drag window while primary mouse button is down.
#[cfg(not(target_arch = "wasm32"))]
pub drag_window: bool, pub drag_window: bool,
/// Set to some position to move the outer window (e.g. glium window) to this position /// 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<egui::Pos2>, pub window_pos: Option<egui::Pos2>,
/// Set to some bool to change window visibility. /// Set to some bool to change window visibility.
#[cfg(not(target_arch = "wasm32"))]
pub visible: Option<bool>, pub visible: Option<bool>,
} }
} }

View file

@ -9,26 +9,24 @@ pub fn points_to_size(points: egui::Vec2) -> winit::dpi::LogicalSize<f64> {
} }
} }
pub fn read_window_info( pub fn read_window_info(window: &winit::window::Window, pixels_per_point: f32) -> WindowInfo {
window: &winit::window::Window, let position = window
pixels_per_point: f32, .outer_position()
) -> Option<WindowInfo> { .ok()
match window.outer_position() { .map(|pos| pos.to_logical::<f32>(pixels_per_point.into()))
Ok(pos) => { .map(|pos| egui::Pos2 { x: pos.x, y: pos.y });
let pos = pos.to_logical::<f32>(pixels_per_point.into());
let size = window let size = window
.inner_size() .inner_size()
.to_logical::<f32>(pixels_per_point.into()); .to_logical::<f32>(pixels_per_point.into());
Some(WindowInfo {
position: egui::Pos2 { x: pos.x, y: pos.y }, WindowInfo {
fullscreen: window.fullscreen().is_some(), position,
size: egui::Vec2 { fullscreen: window.fullscreen().is_some(),
x: size.width, size: egui::Vec2 {
y: size.height, x: size.width,
}, y: size.height,
}) },
}
Err(_) => None,
} }
} }
@ -206,7 +204,6 @@ impl EpiIntegration {
let frame = epi::Frame { let frame = epi::Frame {
info: epi::IntegrationInfo { info: epi::IntegrationInfo {
web_info: None,
system_theme, system_theme,
cpu_usage: None, cpu_usage: None,
native_pixels_per_point: Some(native_pixels_per_point(window)), native_pixels_per_point: Some(native_pixels_per_point(window)),

View file

@ -170,13 +170,12 @@ impl AppRunner {
}; };
let info = epi::IntegrationInfo { let info = epi::IntegrationInfo {
web_info: Some(epi::WebInfo { web_info: epi::WebInfo {
location: web_location(), location: web_location(),
}), },
system_theme, system_theme,
cpu_usage: None, cpu_usage: None,
native_pixels_per_point: Some(native_pixels_per_point()), native_pixels_per_point: Some(native_pixels_per_point()),
window_info: None,
}; };
let storage = LocalStorage::default(); let storage = LocalStorage::default();
@ -293,16 +292,7 @@ impl AppRunner {
{ {
let app_output = self.frame.take_app_output(); let app_output = self.frame.take_app_output();
let epi::backend::AppOutput { let epi::backend::AppOutput {} = app_output;
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;
} }
self.frame.info.cpu_usage = Some((now_sec() - frame_start) as f32); self.frame.info.cpu_usage = Some((now_sec() - frame_start) as f32);

View file

@ -181,9 +181,7 @@ pub fn install_document_events(runner_container: &AppRunnerContainer) -> Result<
"hashchange", "hashchange",
|_: web_sys::Event, mut runner_lock| { |_: web_sys::Event, mut runner_lock| {
// `epi::Frame::info(&self)` clones `epi::IntegrationInfo`, but we need to modify the original here // `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 { runner_lock.frame.info.web_info.location.hash = location_hash();
web_info.location.hash = location_hash();
}
}, },
)?; )?;

View file

@ -136,7 +136,8 @@ impl BackendPanel {
ui.ctx().options().screen_reader = screen_reader; ui.ctx().options().screen_reader = screen_reader;
} }
if !frame.is_web() { #[cfg(not(target_arch = "wasm32"))]
{
ui.separator(); ui.separator();
if ui.button("Quit").clicked() { if ui.button("Quit").clicked() {
frame.quit(); frame.quit();
@ -159,11 +160,10 @@ impl BackendPanel {
ui.label("."); ui.label(".");
}); });
if let Some(web_info) = &frame.info().web_info { #[cfg(target_arch = "wasm32")]
ui.collapsing("Web info (location)", |ui| { ui.collapsing("Web info (location)", |ui| {
ui.monospace(format!("{:#?}", web_info.location)); ui.monospace(format!("{:#?}", frame.info().web_info.location));
}); });
}
// For instance: `eframe` web sets `pixels_per_point` every frame to force // For instance: `eframe` web sets `pixels_per_point` every frame to force
// egui to use the same scale as the web zoom factor. // 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| { 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") ui.checkbox(&mut fullscreen, "🗖 Fullscreen")
.on_hover_text("Fullscreen the window"); .on_hover_text("Fullscreen the window");
frame.set_fullscreen(fullscreen); frame.set_fullscreen(fullscreen);

View file

@ -168,10 +168,9 @@ impl eframe::App for WrapApp {
} }
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) { fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
if let Some(web_info) = frame.info().web_info.as_ref() { #[cfg(target_arch = "wasm32")]
if let Some(anchor) = web_info.location.hash.strip_prefix('#') { if let Some(anchor) = frame.info().web_info.location.hash.strip_prefix('#') {
self.state.selected_anchor = anchor.to_owned(); self.state.selected_anchor = anchor.to_owned();
}
} }
if self.state.selected_anchor.is_empty() { if self.state.selected_anchor.is_empty() {