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)).
* 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)).

View file

@ -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<Box<dyn Storage>>,
pub(crate) storage: Option<Box<dyn Storage>>,
/// A reference to the underlying [`glow`] (OpenGL) context.
#[cfg(feature = "glow")]
#[doc(hidden)]
pub gl: Option<std::sync::Arc<glow::Context>>,
pub(crate) gl: Option<std::sync::Arc<glow::Context>>,
/// 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<egui::Pos2>,
/// 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<WebInfo>,
/// 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<f32>,
/// Window-specific geometry information, if provided by the platform.
pub window_info: Option<WindowInfo>,
/// 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<egui::Vec2>,
/// 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>,
/// Set to some bool to change window decorations.
#[cfg(not(target_arch = "wasm32"))]
pub decorated: Option<bool>,
/// Set to some bool to change window fullscreen.
#[cfg(not(target_arch = "wasm32"))] // TODO: implement fullscreen on web
pub fullscreen: Option<bool>,
/// 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<egui::Pos2>,
/// Set to some bool to change window visibility.
#[cfg(not(target_arch = "wasm32"))]
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(
window: &winit::window::Window,
pixels_per_point: f32,
) -> Option<WindowInfo> {
match window.outer_position() {
Ok(pos) => {
let pos = pos.to_logical::<f32>(pixels_per_point.into());
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::<f32>(pixels_per_point.into()))
.map(|pos| egui::Pos2 { x: pos.x, y: pos.y });
let size = window
.inner_size()
.to_logical::<f32>(pixels_per_point.into());
Some(WindowInfo {
position: egui::Pos2 { x: pos.x, y: pos.y },
WindowInfo {
position,
fullscreen: window.fullscreen().is_some(),
size: egui::Vec2 {
x: size.width,
y: size.height,
},
})
}
Err(_) => None,
}
}
@ -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)),

View file

@ -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);

View file

@ -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();
},
)?;

View file

@ -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 {
#[cfg(target_arch = "wasm32")]
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
// 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);

View file

@ -168,11 +168,10 @@ 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('#') {
#[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() {
let selected_anchor = self.apps_iter_mut().next().unwrap().0.to_owned();