Skip to content

Commit

Permalink
Replace eframe::Frame commands and WindowInfo with egui (#3564)
Browse files Browse the repository at this point in the history
* Part of #3556

## In short
You now almost never need to use `eframe::Frame` - instead use
`ui.input(|i| i.viewport())` for information about the current viewport
(native window), and use `ctx.send_viewport_cmd` to modify it.

## In detail

This PR removes most commands from `eframe::Frame`, and replaces them
with `ViewportCommand`.
So `frame.close()` becomes
`ctx.send_viewport_cmd(ViewportCommand::Close)`, etc.

`frame.info().window_info` is now also gone, replaced with `ui.input(|i|
i.viewport())`.

`frame.info().native_pixels_per_point` is replaced with `ui.input(|i|
i.raw.native_pixels_per_point)`.

`RawInput` now contains one `ViewportInfo` for each viewport.

Screenshots are taken with
`ctx.send_viewport_cmd(ViewportCommand::Screenshots)` and are returned
in `egui::Event` which you can check with:

``` ust
ui.input(|i| {
    for event in &i.raw.events {
        if let egui::Event::Screenshot { viewport_id, image } = event {
            // handle it here
        }
    }
});
```

### Motivation
You no longer need to pass around the `&eframe::Frame` everywhere.
This also opens the door for other integrations to use the same API of
`ViewportCommand`s.
  • Loading branch information
emilk authored Nov 18, 2023
1 parent 3e37e9d commit 1571027
Show file tree
Hide file tree
Showing 29 changed files with 904 additions and 914 deletions.
313 changes: 0 additions & 313 deletions crates/eframe/src/epi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,11 +230,6 @@ pub trait App {
fn warm_up_enabled(&self) -> bool {
false
}

/// Called each time after the rendering the UI.
///
/// Can be used to access pixel data with [`Frame::screenshot`]
fn post_rendering(&mut self, _window_size_px: [u32; 2], _frame: &Frame) {}
}

/// Selects the level of hardware graphics acceleration.
Expand Down Expand Up @@ -732,9 +727,6 @@ pub struct Frame {
/// Information about the integration.
pub(crate) info: IntegrationInfo,

/// Where the app can issue commands back to the integration.
pub(crate) output: backend::AppOutput,

/// A place where you can store custom data in a way that persists when you restart the app.
pub(crate) storage: Option<Box<dyn Storage>>,

Expand All @@ -746,11 +738,6 @@ pub struct Frame {
#[cfg(feature = "wgpu")]
pub(crate) wgpu_render_state: Option<egui_wgpu::RenderState>,

/// If [`Frame::request_screenshot`] was called during a frame, this field will store the screenshot
/// such that it can be retrieved during [`App::post_rendering`] with [`Frame::screenshot`]
#[cfg(not(target_arch = "wasm32"))]
pub(crate) screenshot: std::cell::Cell<Option<egui::ColorImage>>,

/// Raw platform window handle
#[cfg(not(target_arch = "wasm32"))]
pub(crate) raw_window_handle: RawWindowHandle,
Expand Down Expand Up @@ -799,67 +786,6 @@ impl Frame {
self.storage.as_deref()
}

/// Request the current frame's pixel data. Needs to be retrieved by calling [`Frame::screenshot`]
/// during [`App::post_rendering`].
#[cfg(not(target_arch = "wasm32"))]
pub fn request_screenshot(&mut self) {
self.output.screenshot_requested = true;
}

/// Cancel a request made with [`Frame::request_screenshot`].
#[cfg(not(target_arch = "wasm32"))]
pub fn cancel_screenshot_request(&mut self) {
self.output.screenshot_requested = false;
}

/// During [`App::post_rendering`], use this to retrieve the pixel data that was requested during
/// [`App::update`] via [`Frame::request_screenshot`].
///
/// Returns None if:
/// * Called in [`App::update`]
/// * [`Frame::request_screenshot`] wasn't called on this frame during [`App::update`]
/// * The rendering backend doesn't support this feature (yet). Currently implemented for wgpu and glow, but not with wasm as target.
/// * Wgpu's GL target is active (not yet supported)
/// * Retrieving the data was unsuccessful in some way.
///
/// See also [`egui::ColorImage::region`]
///
/// ## Example generating a capture of everything within a square of 100 pixels located at the top left of the app and saving it with the [`image`](crates.io/crates/image) crate:
/// ```
/// struct MyApp;
///
/// impl eframe::App for MyApp {
/// fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
/// // In real code the app would render something here
/// frame.request_screenshot();
/// // Things that are added to the frame after the call to
/// // request_screenshot() will still be included.
/// }
///
/// fn post_rendering(&mut self, _window_size: [u32; 2], frame: &eframe::Frame) {
/// if let Some(screenshot) = frame.screenshot() {
/// let pixels_per_point = frame.info().native_pixels_per_point;
/// let region = egui::Rect::from_two_pos(
/// egui::Pos2::ZERO,
/// egui::Pos2{ x: 100., y: 100. },
/// );
/// let top_left_corner = screenshot.region(&region, pixels_per_point);
/// image::save_buffer(
/// "top_left.png",
/// top_left_corner.as_raw(),
/// top_left_corner.width() as u32,
/// top_left_corner.height() as u32,
/// image::ColorType::Rgba8,
/// ).unwrap();
/// }
/// }
/// }
/// ```
#[cfg(not(target_arch = "wasm32"))]
pub fn screenshot(&self) -> Option<egui::ColorImage> {
self.screenshot.take()
}

/// A place where you can store custom data in a way that persists when you restart the app.
pub fn storage_mut(&mut self) -> Option<&mut (dyn Storage + 'static)> {
self.storage.as_deref_mut()
Expand Down Expand Up @@ -891,141 +817,6 @@ impl Frame {
pub fn wgpu_render_state(&self) -> Option<&egui_wgpu::RenderState> {
self.wgpu_render_state.as_ref()
}

/// Tell `eframe` to close the desktop window.
///
/// The window will not close immediately, but at the end of the this frame.
///
/// Calling this will likely result in the app quitting, unless
/// you have more code after the call to [`crate::run_native`].
#[cfg(not(target_arch = "wasm32"))]
#[doc(alias = "exit")]
#[doc(alias = "quit")]
pub fn close(&mut self) {
log::debug!("eframe::Frame::close called");
self.output.close = true;
}

/// Minimize or unminimize window. (native only)
#[cfg(not(target_arch = "wasm32"))]
pub fn set_minimized(&mut self, minimized: bool) {
self.output.minimized = Some(minimized);
}

/// Bring the window into focus (native only). Has no effect on Wayland, or if the window is minimized or invisible.
///
/// This method puts the window on top of other applications and takes input focus away from them,
/// which, if unexpected, will disturb the user.
#[cfg(not(target_arch = "wasm32"))]
pub fn focus(&mut self) {
self.output.focus = Some(true);
}

/// If the window is unfocused, attract the user's attention (native only).
///
/// Typically, this means that the window will flash on the taskbar, or bounce, until it is interacted with.
///
/// When the window comes into focus, or if `None` is passed, the attention request will be automatically reset.
///
/// See [winit's documentation][user_attention_details] for platform-specific effect details.
///
/// [user_attention_details]: https://docs.rs/winit/latest/winit/window/enum.UserAttentionType.html
#[cfg(not(target_arch = "wasm32"))]
pub fn request_user_attention(&mut self, kind: egui::UserAttentionType) {
self.output.attention = Some(kind);
}

/// Maximize or unmaximize window. (native only)
#[cfg(not(target_arch = "wasm32"))]
pub fn set_maximized(&mut self, maximized: bool) {
self.output.maximized = Some(maximized);
}

/// Tell `eframe` to close the desktop window.
#[cfg(not(target_arch = "wasm32"))]
#[deprecated = "Renamed `close`"]
pub fn quit(&mut self) {
self.close();
}

/// 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);
self.info.window_info.size = size; // so that subsequent calls see the updated value
}

/// 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());
}

/// 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);
self.info.window_info.fullscreen = fullscreen; // so that subsequent calls see the updated value
}

/// 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);
self.info.window_info.position = Some(pos); // so that subsequent calls see the updated value
}

/// When called, the native window will follow the
/// 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);
}

/// On desktop: Set the window always on top.
///
/// (Wayland desktop currently not supported)
#[cfg(not(target_arch = "wasm32"))]
pub fn set_always_on_top(&mut self, always_on_top: bool) {
self.output.always_on_top = Some(always_on_top);
}

/// On desktop: Set the window to be centered.
///
/// (Wayland desktop currently not supported)
#[cfg(not(target_arch = "wasm32"))]
pub fn set_centered(&mut self) {
if let Some(monitor_size) = self.info.window_info.monitor_size {
let inner_size = self.info.window_info.size;
if monitor_size.x > 1.0 && monitor_size.y > 1.0 {
let x = (monitor_size.x - inner_size.x) / 2.0;
let y = (monitor_size.y - inner_size.y) / 2.0;
self.set_window_pos(egui::Pos2 { x, y });
}
}
}

/// for integrations only: call once per frame
#[cfg(any(feature = "glow", feature = "wgpu"))]
pub(crate) fn take_app_output(&mut self) -> backend::AppOutput {
std::mem::take(&mut self.output)
}
}

/// Information about the web environment (if applicable).
Expand All @@ -1039,38 +830,6 @@ pub struct WebInfo {
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).
///
/// `None` = unknown.
pub position: Option<egui::Pos2>,

/// Are we in fullscreen mode?
pub fullscreen: bool,

/// Are we minimized?
pub minimized: bool,

/// Are we maximized?
pub maximized: bool,

/// Is the window focused and able to receive input?
///
/// This should be the same as [`egui::InputState::focused`].
pub focused: bool,

/// Window inner size in egui points (logical pixels).
pub size: egui::Vec2,

/// Current monitor size in egui points (logical pixels)
pub monitor_size: Option<egui::Vec2>,
}

/// Information about the URL.
///
/// Everything has been percent decoded (`%20` -> ` ` etc).
Expand Down Expand Up @@ -1141,13 +900,6 @@ pub struct IntegrationInfo {
/// Seconds of cpu usage (in seconds) of UI code on the previous frame.
/// `None` if this is the first frame.
pub cpu_usage: Option<f32>,

/// The OS native pixels-per-point
pub native_pixels_per_point: Option<f32>,

/// The position and size of the native window.
#[cfg(not(target_arch = "wasm32"))]
pub window_info: WindowInfo,
}

// ----------------------------------------------------------------------------
Expand Down Expand Up @@ -1211,68 +963,3 @@ pub fn set_value<T: serde::Serialize>(storage: &mut dyn Storage, key: &str, valu

/// [`Storage`] key used for app
pub const APP_KEY: &str = "app";

// ----------------------------------------------------------------------------

/// You only need to look here if you are writing a backend for `epi`.
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 close the native window (which often quits the app).
#[cfg(not(target_arch = "wasm32"))]
pub close: 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>,

/// Set to some bool to tell the window always on top.
#[cfg(not(target_arch = "wasm32"))]
pub always_on_top: Option<bool>,

/// Set to some bool to minimize or unminimize window.
#[cfg(not(target_arch = "wasm32"))]
pub minimized: Option<bool>,

/// Set to some bool to maximize or unmaximize window.
#[cfg(not(target_arch = "wasm32"))]
pub maximized: Option<bool>,

/// Set to some bool to focus window.
#[cfg(not(target_arch = "wasm32"))]
pub focus: Option<bool>,

/// Set to request a user's attention to the native window.
#[cfg(not(target_arch = "wasm32"))]
pub attention: Option<egui::UserAttentionType>,

#[cfg(not(target_arch = "wasm32"))]
pub screenshot_requested: bool,
}
}
Loading

0 comments on commit 1571027

Please sign in to comment.