mirror of
https://github.com/emilk/egui.git
synced 2026-08-29 12:50:04 -04:00
Replace eframe::Frame commands and WindowInfo with egui (#3564)
* Part of https://github.com/emilk/egui/issues/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.
This commit is contained in:
@@ -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.
|
||||
@@ -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>>,
|
||||
|
||||
@@ -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,
|
||||
@@ -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(®ion, 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()
|
||||
@@ -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).
|
||||
@@ -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).
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,61 +4,13 @@ use winit::event_loop::EventLoopWindowTarget;
|
||||
|
||||
use raw_window_handle::{HasRawDisplayHandle as _, HasRawWindowHandle as _};
|
||||
|
||||
use egui::{DeferredViewportUiCallback, NumExt as _, ViewportBuilder, ViewportId, ViewportIdPair};
|
||||
use egui_winit::{native_pixels_per_point, EventResponse, WindowSettings};
|
||||
use egui::{
|
||||
DeferredViewportUiCallback, NumExt as _, ViewportBuilder, ViewportId, ViewportIdPair,
|
||||
ViewportInfo,
|
||||
};
|
||||
use egui_winit::{EventResponse, WindowSettings};
|
||||
|
||||
use crate::{epi, Theme, WindowInfo};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct WindowState {
|
||||
// We cannot simply call `winit::Window::is_minimized/is_maximized`
|
||||
// because that deadlocks on mac.
|
||||
pub minimized: bool,
|
||||
pub maximized: bool,
|
||||
}
|
||||
|
||||
pub fn read_window_info(
|
||||
window: &winit::window::Window,
|
||||
pixels_per_point: f32,
|
||||
window_state: &WindowState,
|
||||
) -> 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 monitor = window.current_monitor().is_some();
|
||||
let monitor_size = if monitor {
|
||||
let size = window
|
||||
.current_monitor()
|
||||
.unwrap()
|
||||
.size()
|
||||
.to_logical::<f32>(pixels_per_point.into());
|
||||
Some(egui::vec2(size.width, size.height))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let size = window
|
||||
.inner_size()
|
||||
.to_logical::<f32>(pixels_per_point.into());
|
||||
|
||||
// NOTE: calling window.is_minimized() or window.is_maximized() deadlocks on Mac.
|
||||
|
||||
WindowInfo {
|
||||
position,
|
||||
fullscreen: window.fullscreen().is_some(),
|
||||
minimized: window_state.minimized,
|
||||
maximized: window_state.maximized,
|
||||
focused: window.has_focus(),
|
||||
size: egui::Vec2 {
|
||||
x: size.width,
|
||||
y: size.height,
|
||||
},
|
||||
monitor_size,
|
||||
}
|
||||
}
|
||||
use crate::{epi, Theme};
|
||||
|
||||
pub fn window_builder<E>(
|
||||
event_loop: &EventLoopWindowTarget<E>,
|
||||
@@ -208,97 +160,6 @@ fn largest_monitor_point_size<E>(event_loop: &EventLoopWindowTarget<E>) -> egui:
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_app_output(
|
||||
window: &winit::window::Window,
|
||||
current_pixels_per_point: f32,
|
||||
app_output: epi::backend::AppOutput,
|
||||
window_state: &mut WindowState,
|
||||
) {
|
||||
crate::profile_function!();
|
||||
|
||||
let epi::backend::AppOutput {
|
||||
close: _,
|
||||
window_size,
|
||||
window_title,
|
||||
decorated,
|
||||
fullscreen,
|
||||
drag_window,
|
||||
window_pos,
|
||||
visible: _, // handled in post_present
|
||||
always_on_top,
|
||||
screenshot_requested: _, // handled by the rendering backend,
|
||||
minimized,
|
||||
maximized,
|
||||
focus,
|
||||
attention,
|
||||
} = app_output;
|
||||
|
||||
if let Some(decorated) = decorated {
|
||||
window.set_decorations(decorated);
|
||||
}
|
||||
|
||||
if let Some(window_size) = window_size {
|
||||
window.set_inner_size(
|
||||
winit::dpi::PhysicalSize {
|
||||
width: (current_pixels_per_point * window_size.x).round(),
|
||||
height: (current_pixels_per_point * window_size.y).round(),
|
||||
}
|
||||
.to_logical::<f32>(native_pixels_per_point(window) as f64),
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(fullscreen) = fullscreen {
|
||||
window.set_fullscreen(fullscreen.then_some(winit::window::Fullscreen::Borderless(None)));
|
||||
}
|
||||
|
||||
if let Some(window_title) = window_title {
|
||||
window.set_title(&window_title);
|
||||
}
|
||||
|
||||
if let Some(window_pos) = window_pos {
|
||||
window.set_outer_position(winit::dpi::LogicalPosition {
|
||||
x: window_pos.x as f64,
|
||||
y: window_pos.y as f64,
|
||||
});
|
||||
}
|
||||
|
||||
if drag_window {
|
||||
window.drag_window().ok();
|
||||
}
|
||||
|
||||
if let Some(always_on_top) = always_on_top {
|
||||
use winit::window::WindowLevel;
|
||||
window.set_window_level(if always_on_top {
|
||||
WindowLevel::AlwaysOnTop
|
||||
} else {
|
||||
WindowLevel::Normal
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(minimized) = minimized {
|
||||
window.set_minimized(minimized);
|
||||
window_state.minimized = minimized;
|
||||
}
|
||||
|
||||
if let Some(maximized) = maximized {
|
||||
window.set_maximized(maximized);
|
||||
window_state.maximized = maximized;
|
||||
}
|
||||
|
||||
if !window.has_focus() {
|
||||
if focus == Some(true) {
|
||||
window.focus_window();
|
||||
} else if let Some(attention) = attention {
|
||||
use winit::window::UserAttentionType;
|
||||
window.request_user_attention(match attention {
|
||||
egui::UserAttentionType::Reset => None,
|
||||
egui::UserAttentionType::Critical => Some(UserAttentionType::Critical),
|
||||
egui::UserAttentionType::Informational => Some(UserAttentionType::Informational),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// For loading/saving app state and/or egui memory to disk.
|
||||
@@ -313,10 +174,13 @@ pub fn create_storage(_app_name: &str) -> Option<Box<dyn epi::Storage>> {
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/// Everything needed to make a winit-based integration for [`epi`].
|
||||
///
|
||||
/// Only one instance per app (not one per viewport).
|
||||
pub struct EpiIntegration {
|
||||
pub frame: epi::Frame,
|
||||
last_auto_save: Instant,
|
||||
pub beginning: Instant,
|
||||
is_first_frame: bool,
|
||||
pub frame_start: Instant,
|
||||
pub egui_ctx: egui::Context,
|
||||
pending_full_output: egui::FullOutput,
|
||||
@@ -325,7 +189,6 @@ pub struct EpiIntegration {
|
||||
close: bool,
|
||||
|
||||
can_drag_window: bool,
|
||||
window_state: WindowState,
|
||||
follow_system_theme: bool,
|
||||
#[cfg(feature = "persistence")]
|
||||
persist_window: bool,
|
||||
@@ -350,30 +213,16 @@ impl EpiIntegration {
|
||||
let memory = load_egui_memory(storage.as_deref()).unwrap_or_default();
|
||||
egui_ctx.memory_mut(|mem| *mem = memory);
|
||||
|
||||
let native_pixels_per_point = window.scale_factor() as f32;
|
||||
|
||||
let window_state = WindowState {
|
||||
minimized: window.is_minimized().unwrap_or(false),
|
||||
maximized: window.is_maximized(),
|
||||
};
|
||||
|
||||
let frame = epi::Frame {
|
||||
info: epi::IntegrationInfo {
|
||||
system_theme,
|
||||
cpu_usage: None,
|
||||
native_pixels_per_point: Some(native_pixels_per_point),
|
||||
window_info: read_window_info(window, egui_ctx.pixels_per_point(), &window_state),
|
||||
},
|
||||
output: epi::backend::AppOutput {
|
||||
visible: Some(true),
|
||||
..Default::default()
|
||||
},
|
||||
storage,
|
||||
#[cfg(feature = "glow")]
|
||||
gl,
|
||||
#[cfg(feature = "wgpu")]
|
||||
wgpu_render_state,
|
||||
screenshot: std::cell::Cell::new(None),
|
||||
raw_display_handle: window.raw_display_handle(),
|
||||
raw_window_handle: window.raw_window_handle(),
|
||||
};
|
||||
@@ -390,12 +239,12 @@ impl EpiIntegration {
|
||||
pending_full_output: Default::default(),
|
||||
close: false,
|
||||
can_drag_window: false,
|
||||
window_state,
|
||||
follow_system_theme: native_options.follow_system_theme,
|
||||
#[cfg(feature = "persistence")]
|
||||
persist_window: native_options.persist_window,
|
||||
app_icon_setter,
|
||||
beginning: Instant::now(),
|
||||
is_first_frame: true,
|
||||
frame_start: Instant::now(),
|
||||
}
|
||||
}
|
||||
@@ -432,10 +281,12 @@ impl EpiIntegration {
|
||||
self.egui_ctx
|
||||
.memory_mut(|mem| mem.set_everything_is_visible(true));
|
||||
|
||||
let raw_input = egui_winit.take_egui_input(window, ViewportIdPair::ROOT);
|
||||
self.pre_update(window);
|
||||
let mut raw_input = egui_winit.take_egui_input(window, ViewportIdPair::ROOT);
|
||||
raw_input.viewports =
|
||||
std::iter::once((ViewportId::ROOT, ViewportInfo::default())).collect();
|
||||
self.pre_update();
|
||||
let full_output = self.update(app, None, raw_input);
|
||||
self.post_update(app, window);
|
||||
self.post_update();
|
||||
self.pending_full_output.append(full_output); // Handle it next frame
|
||||
self.egui_ctx.memory_mut(|mem| *mem = saved_memory); // We don't want to remember that windows were huge.
|
||||
self.egui_ctx.clear_animations();
|
||||
@@ -473,7 +324,7 @@ impl EpiIntegration {
|
||||
..
|
||||
} => self.can_drag_window = true,
|
||||
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
|
||||
self.frame.info.native_pixels_per_point = Some(*scale_factor as _);
|
||||
egui_winit.egui_input_mut().native_pixels_per_point = Some(*scale_factor as _);
|
||||
}
|
||||
WindowEvent::ThemeChanged(winit_theme) if self.follow_system_theme => {
|
||||
let theme = theme_from_winit_theme(*winit_theme);
|
||||
@@ -483,16 +334,12 @@ impl EpiIntegration {
|
||||
_ => {}
|
||||
}
|
||||
|
||||
egui_winit.on_event(&self.egui_ctx, event)
|
||||
egui_winit.on_event(&self.egui_ctx, event, viewport_id)
|
||||
}
|
||||
|
||||
pub fn pre_update(&mut self, window: &winit::window::Window) {
|
||||
pub fn pre_update(&mut self) {
|
||||
self.frame_start = Instant::now();
|
||||
|
||||
self.app_icon_setter.update();
|
||||
|
||||
self.frame.info.window_info =
|
||||
read_window_info(window, self.egui_ctx.pixels_per_point(), &self.window_state);
|
||||
}
|
||||
|
||||
/// Run user code - this can create immediate viewports, so hold no locks over this!
|
||||
@@ -513,6 +360,11 @@ impl EpiIntegration {
|
||||
viewport_ui_cb(egui_ctx);
|
||||
} else {
|
||||
// Root viewport
|
||||
if egui_ctx.input(|i| i.viewport().close_requested) {
|
||||
self.close = app.on_close_event();
|
||||
log::debug!("App::on_close_event returned {}", self.close);
|
||||
}
|
||||
|
||||
crate::profile_scope!("App::update");
|
||||
app.update(egui_ctx, &mut self.frame);
|
||||
}
|
||||
@@ -522,45 +374,16 @@ impl EpiIntegration {
|
||||
std::mem::take(&mut self.pending_full_output)
|
||||
}
|
||||
|
||||
pub fn post_update(&mut self, app: &mut dyn epi::App, window: &winit::window::Window) {
|
||||
let app_output = {
|
||||
let mut app_output = self.frame.take_app_output();
|
||||
app_output.drag_window &= self.can_drag_window; // Necessary on Windows; see https://github.com/emilk/egui/pull/1108
|
||||
self.can_drag_window = false;
|
||||
if app_output.close {
|
||||
self.close = app.on_close_event();
|
||||
log::debug!("App::on_close_event returned {}", self.close);
|
||||
}
|
||||
self.frame.output.visible = app_output.visible; // this is handled by post_present
|
||||
self.frame.output.screenshot_requested = app_output.screenshot_requested;
|
||||
if self.frame.output.attention.is_some() {
|
||||
self.frame.output.attention = None;
|
||||
}
|
||||
app_output
|
||||
};
|
||||
|
||||
handle_app_output(
|
||||
window,
|
||||
self.egui_ctx.pixels_per_point(),
|
||||
app_output,
|
||||
&mut self.window_state,
|
||||
);
|
||||
|
||||
pub fn post_update(&mut self) {
|
||||
let frame_time = self.frame_start.elapsed().as_secs_f64() as f32;
|
||||
self.frame.info.cpu_usage = Some(frame_time);
|
||||
}
|
||||
|
||||
pub fn post_rendering(&mut self, app: &mut dyn epi::App, window: &winit::window::Window) {
|
||||
pub fn post_rendering(&mut self, window: &winit::window::Window) {
|
||||
crate::profile_function!();
|
||||
let inner_size = window.inner_size();
|
||||
let window_size_px = [inner_size.width, inner_size.height];
|
||||
app.post_rendering(window_size_px, &self.frame);
|
||||
}
|
||||
|
||||
pub fn post_present(&mut self, window: &winit::window::Window) {
|
||||
if let Some(visible) = self.frame.output.visible.take() {
|
||||
crate::profile_scope!("window.set_visible");
|
||||
window.set_visible(visible);
|
||||
if std::mem::take(&mut self.is_first_frame) {
|
||||
// We keep hidden until we've painted something. See https://github.com/emilk/egui/pull/2279
|
||||
window.set_visible(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -458,10 +458,12 @@ mod glow_integration {
|
||||
|
||||
use egui::{
|
||||
epaint::ahash::HashMap, DeferredViewportUiCallback, ImmediateViewport, NumExt as _,
|
||||
ViewportClass, ViewportIdMap, ViewportIdPair, ViewportIdSet, ViewportOutput,
|
||||
ViewportClass, ViewportIdMap, ViewportIdPair, ViewportIdSet, ViewportInfo, ViewportOutput,
|
||||
};
|
||||
use egui_winit::{create_winit_window_builder, process_viewport_commands, EventResponse};
|
||||
|
||||
use crate::native::epi_integration::EpiIntegration;
|
||||
|
||||
use super::*;
|
||||
|
||||
// Note: that the current Glutin API design tightly couples the GL context with
|
||||
@@ -480,7 +482,7 @@ mod glow_integration {
|
||||
/// a Resumed event. On Android this ensures that any graphics state is only
|
||||
/// initialized once the application has an associated `SurfaceView`.
|
||||
struct GlowWinitRunning {
|
||||
integration: epi_integration::EpiIntegration,
|
||||
integration: EpiIntegration,
|
||||
app: Box<dyn epi::App>,
|
||||
|
||||
// These needs to be shared with the immediate viewport renderer, hence the Rc/Arc/RefCells:
|
||||
@@ -527,14 +529,23 @@ mod glow_integration {
|
||||
let (raw_input, viewport_ui_cb) = {
|
||||
let mut glutin = self.glutin.borrow_mut();
|
||||
let viewport = glutin.viewports.get_mut(&viewport_id).unwrap();
|
||||
viewport.update_viewport_info();
|
||||
let window = viewport.window.as_ref().unwrap();
|
||||
|
||||
let egui_winit = viewport.egui_winit.as_mut().unwrap();
|
||||
let raw_input = egui_winit.take_egui_input(window, viewport.ids);
|
||||
let mut raw_input = egui_winit.take_egui_input(window, viewport.ids);
|
||||
let viewport_ui_cb = viewport.viewport_ui_cb.clone();
|
||||
|
||||
self.integration.pre_update(window);
|
||||
self.integration.pre_update();
|
||||
|
||||
(raw_input, viewport.viewport_ui_cb.clone())
|
||||
raw_input.time = Some(self.integration.beginning.elapsed().as_secs_f64());
|
||||
raw_input.viewports = glutin
|
||||
.viewports
|
||||
.iter()
|
||||
.map(|(id, viewport)| (*id, viewport.info.clone()))
|
||||
.collect();
|
||||
|
||||
(raw_input, viewport_ui_cb)
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------
|
||||
@@ -577,7 +588,7 @@ mod glow_integration {
|
||||
let gl_surface = viewport.gl_surface.as_ref().unwrap();
|
||||
let egui_winit = viewport.egui_winit.as_mut().unwrap();
|
||||
|
||||
integration.post_update(app.as_mut(), window);
|
||||
integration.post_update();
|
||||
integration.handle_platform_output(window, viewport_id, platform_output, egui_winit);
|
||||
|
||||
let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point);
|
||||
@@ -607,13 +618,18 @@ mod glow_integration {
|
||||
);
|
||||
|
||||
{
|
||||
let screenshot_requested = &mut integration.frame.output.screenshot_requested;
|
||||
if *screenshot_requested {
|
||||
*screenshot_requested = false;
|
||||
let screenshot_requested = std::mem::take(&mut viewport.screenshot_requested);
|
||||
if screenshot_requested {
|
||||
let screenshot = painter.read_screen_rgba(screen_size_in_pixels);
|
||||
integration.frame.screenshot.set(Some(screenshot));
|
||||
egui_winit
|
||||
.egui_input_mut()
|
||||
.events
|
||||
.push(egui::Event::Screenshot {
|
||||
viewport_id,
|
||||
image: screenshot.into(),
|
||||
});
|
||||
}
|
||||
integration.post_rendering(app.as_mut(), window);
|
||||
integration.post_rendering(window);
|
||||
}
|
||||
|
||||
{
|
||||
@@ -627,8 +643,6 @@ mod glow_integration {
|
||||
}
|
||||
}
|
||||
|
||||
integration.post_present(window);
|
||||
|
||||
// give it time to settle:
|
||||
#[cfg(feature = "__screenshot")]
|
||||
if integration.egui_ctx.frame_nr() == 2 {
|
||||
@@ -716,6 +730,7 @@ mod glow_integration {
|
||||
return EventResult::Exit;
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
|
||||
@@ -783,6 +798,8 @@ mod glow_integration {
|
||||
ids: ViewportIdPair,
|
||||
class: ViewportClass,
|
||||
builder: ViewportBuilder,
|
||||
info: ViewportInfo,
|
||||
screenshot_requested: bool,
|
||||
|
||||
/// The user-callback that shows the ui.
|
||||
/// None for immediate viewports.
|
||||
@@ -793,6 +810,19 @@ mod glow_integration {
|
||||
egui_winit: Option<egui_winit::State>,
|
||||
}
|
||||
|
||||
impl Viewport {
|
||||
/// Update the stored `ViewportInfo`.
|
||||
pub fn update_viewport_info(&mut self) {
|
||||
let Some(window) = &self.window else {
|
||||
return;
|
||||
};
|
||||
let Some(egui_winit) = &self.egui_winit else {
|
||||
return;
|
||||
};
|
||||
egui_winit.update_viewport_info(&mut self.info, window);
|
||||
}
|
||||
}
|
||||
|
||||
/// This struct will contain both persistent and temporary glutin state.
|
||||
///
|
||||
/// Platform Quirks:
|
||||
@@ -935,9 +965,12 @@ mod glow_integration {
|
||||
|
||||
let mut viewport_from_window = HashMap::default();
|
||||
let mut window_from_viewport = ViewportIdMap::default();
|
||||
let mut info = ViewportInfo::default();
|
||||
if let Some(window) = &window {
|
||||
viewport_from_window.insert(window.id(), ViewportId::ROOT);
|
||||
window_from_viewport.insert(ViewportId::ROOT, window.id());
|
||||
info.minimized = window.is_minimized();
|
||||
info.maximized = Some(window.is_maximized());
|
||||
}
|
||||
|
||||
let mut viewports = ViewportIdMap::default();
|
||||
@@ -947,6 +980,8 @@ mod glow_integration {
|
||||
ids: ViewportIdPair::ROOT,
|
||||
class: ViewportClass::Root,
|
||||
builder: viewport_builder,
|
||||
info,
|
||||
screenshot_requested: false,
|
||||
viewport_ui_cb: None,
|
||||
gl_surface: None,
|
||||
window: window.map(Rc::new),
|
||||
@@ -1016,13 +1051,14 @@ mod glow_integration {
|
||||
window
|
||||
} else {
|
||||
log::trace!("Window doesn't exist yet. Creating one now with finalize_window");
|
||||
viewport
|
||||
.window
|
||||
.insert(Rc::new(glutin_winit::finalize_window(
|
||||
event_loop,
|
||||
create_winit_window_builder(&viewport.builder),
|
||||
&self.gl_config,
|
||||
)?))
|
||||
let window = glutin_winit::finalize_window(
|
||||
event_loop,
|
||||
create_winit_window_builder(&viewport.builder),
|
||||
&self.gl_config,
|
||||
)?;
|
||||
viewport.info.minimized = window.is_minimized();
|
||||
viewport.info.maximized = Some(window.is_maximized());
|
||||
viewport.window.insert(Rc::new(window))
|
||||
};
|
||||
|
||||
{
|
||||
@@ -1178,7 +1214,7 @@ mod glow_integration {
|
||||
{
|
||||
let ids = ViewportIdPair::from_self_and_parent(viewport_id, parent);
|
||||
|
||||
initialize_or_update_viewport(
|
||||
let viewport = initialize_or_update_viewport(
|
||||
&mut self.viewports,
|
||||
ids,
|
||||
class,
|
||||
@@ -1187,15 +1223,15 @@ mod glow_integration {
|
||||
focused_viewport,
|
||||
);
|
||||
|
||||
if let Some(viewport) = self.viewports.get(&viewport_id) {
|
||||
if let Some(window) = &viewport.window {
|
||||
let is_viewport_focused = focused_viewport == Some(viewport_id);
|
||||
egui_winit::process_viewport_commands(
|
||||
commands,
|
||||
window,
|
||||
is_viewport_focused,
|
||||
);
|
||||
}
|
||||
if let Some(window) = &viewport.window {
|
||||
let is_viewport_focused = focused_viewport == Some(viewport_id);
|
||||
egui_winit::process_viewport_commands(
|
||||
&mut viewport.info,
|
||||
commands,
|
||||
window,
|
||||
is_viewport_focused,
|
||||
&mut viewport.screenshot_requested,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1234,6 +1270,8 @@ mod glow_integration {
|
||||
ids,
|
||||
class,
|
||||
builder,
|
||||
info: Default::default(),
|
||||
screenshot_requested: false,
|
||||
viewport_ui_cb,
|
||||
window: None,
|
||||
egui_winit: None,
|
||||
@@ -1261,7 +1299,13 @@ mod glow_integration {
|
||||
viewport.egui_winit = None;
|
||||
} else if let Some(window) = &viewport.window {
|
||||
let is_viewport_focused = focused_viewport == Some(ids.this);
|
||||
process_viewport_commands(delta_commands, window, is_viewport_focused);
|
||||
process_viewport_commands(
|
||||
&mut viewport.info,
|
||||
delta_commands,
|
||||
window,
|
||||
is_viewport_focused,
|
||||
&mut viewport.screenshot_requested,
|
||||
);
|
||||
}
|
||||
|
||||
entry.into_mut()
|
||||
@@ -1378,7 +1422,7 @@ mod glow_integration {
|
||||
|
||||
let system_theme = system_theme(&glutin.window(ViewportId::ROOT), &self.native_options);
|
||||
|
||||
let mut integration = epi_integration::EpiIntegration::new(
|
||||
let mut integration = EpiIntegration::new(
|
||||
&glutin.window(ViewportId::ROOT),
|
||||
system_theme,
|
||||
&self.app_name,
|
||||
@@ -1548,7 +1592,7 @@ mod glow_integration {
|
||||
let Some(viewport) = glutin.viewports.get_mut(&ids.this) else {
|
||||
return;
|
||||
};
|
||||
|
||||
viewport.update_viewport_info();
|
||||
let Some(winit_state) = &mut viewport.egui_winit else {
|
||||
return;
|
||||
};
|
||||
@@ -1556,9 +1600,14 @@ mod glow_integration {
|
||||
return;
|
||||
};
|
||||
|
||||
let mut input = winit_state.take_egui_input(window, ids);
|
||||
input.time = Some(beginning.elapsed().as_secs_f64());
|
||||
input
|
||||
let mut raw_input = winit_state.take_egui_input(window, ids);
|
||||
raw_input.viewports = glutin
|
||||
.viewports
|
||||
.iter()
|
||||
.map(|(id, viewport)| (*id, viewport.info.clone()))
|
||||
.collect();
|
||||
raw_input.time = Some(beginning.elapsed().as_secs_f64());
|
||||
raw_input
|
||||
};
|
||||
|
||||
// ---------------------------------------------------
|
||||
@@ -1813,18 +1862,20 @@ mod wgpu_integration {
|
||||
|
||||
use egui::{
|
||||
DeferredViewportUiCallback, FullOutput, ImmediateViewport, ViewportClass, ViewportIdMap,
|
||||
ViewportIdPair, ViewportIdSet, ViewportOutput,
|
||||
ViewportIdPair, ViewportIdSet, ViewportInfo, ViewportOutput,
|
||||
};
|
||||
use egui_winit::{create_winit_window_builder, process_viewport_commands};
|
||||
|
||||
use crate::native::epi_integration::EpiIntegration;
|
||||
|
||||
use super::*;
|
||||
|
||||
pub struct Viewport {
|
||||
ids: ViewportIdPair,
|
||||
|
||||
class: ViewportClass,
|
||||
|
||||
builder: ViewportBuilder,
|
||||
info: ViewportInfo,
|
||||
screenshot_requested: bool,
|
||||
|
||||
/// `None` for sync viewports.
|
||||
viewport_ui_cb: Option<Arc<DeferredViewportUiCallback>>,
|
||||
@@ -1849,28 +1900,42 @@ mod wgpu_integration {
|
||||
let viewport_id = self.ids.this;
|
||||
|
||||
match create_winit_window_builder(&self.builder).build(event_loop) {
|
||||
Ok(new_window) => {
|
||||
windows_id.insert(new_window.id(), viewport_id);
|
||||
Ok(window) => {
|
||||
windows_id.insert(window.id(), viewport_id);
|
||||
|
||||
if let Err(err) =
|
||||
pollster::block_on(painter.set_window(viewport_id, Some(&new_window)))
|
||||
pollster::block_on(painter.set_window(viewport_id, Some(&window)))
|
||||
{
|
||||
log::error!("on set_window: viewport_id {viewport_id:?} {err}");
|
||||
}
|
||||
|
||||
self.egui_winit = Some(egui_winit::State::new(
|
||||
event_loop,
|
||||
Some(new_window.scale_factor() as f32),
|
||||
Some(window.scale_factor() as f32),
|
||||
painter.max_texture_side(),
|
||||
));
|
||||
|
||||
self.window = Some(Rc::new(new_window));
|
||||
self.info.minimized = window.is_minimized();
|
||||
self.info.maximized = Some(window.is_maximized());
|
||||
|
||||
self.window = Some(Rc::new(window));
|
||||
}
|
||||
Err(err) => {
|
||||
log::error!("Failed to create window: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Update the stored `ViewportInfo`.
|
||||
pub fn update_viewport_info(&mut self) {
|
||||
let Some(window) = &self.window else {
|
||||
return;
|
||||
};
|
||||
let Some(egui_winit) = &self.egui_winit else {
|
||||
return;
|
||||
};
|
||||
egui_winit.update_viewport_info(&mut self.info, window);
|
||||
}
|
||||
}
|
||||
|
||||
pub type Viewports = ViewportIdMap<Viewport>;
|
||||
@@ -1888,7 +1953,7 @@ mod wgpu_integration {
|
||||
/// a Resumed event. On Android this ensures that any graphics state is only
|
||||
/// initialized once the application has an associated `SurfaceView`.
|
||||
struct WgpuWinitRunning {
|
||||
integration: epi_integration::EpiIntegration,
|
||||
integration: EpiIntegration,
|
||||
|
||||
/// The users application.
|
||||
app: Box<dyn epi::App>,
|
||||
@@ -1988,7 +2053,7 @@ mod wgpu_integration {
|
||||
let wgpu_render_state = painter.render_state();
|
||||
|
||||
let system_theme = system_theme(&window, &self.native_options);
|
||||
let mut integration = epi_integration::EpiIntegration::new(
|
||||
let mut integration = EpiIntegration::new(
|
||||
&window,
|
||||
system_theme,
|
||||
&self.app_name,
|
||||
@@ -2066,6 +2131,12 @@ mod wgpu_integration {
|
||||
ids: ViewportIdPair::ROOT,
|
||||
class: ViewportClass::Root,
|
||||
builder,
|
||||
info: ViewportInfo {
|
||||
minimized: window.is_minimized(),
|
||||
maximized: Some(window.is_maximized()),
|
||||
..Default::default()
|
||||
},
|
||||
screenshot_requested: false,
|
||||
viewport_ui_cb: None,
|
||||
window: Some(Rc::new(window)),
|
||||
egui_winit: Some(egui_winit),
|
||||
@@ -2155,6 +2226,7 @@ mod wgpu_integration {
|
||||
painter,
|
||||
viewport_from_window,
|
||||
} = &mut *shared.borrow_mut();
|
||||
|
||||
let viewport = initialize_or_update_viewport(
|
||||
viewports,
|
||||
ids,
|
||||
@@ -2163,10 +2235,10 @@ mod wgpu_integration {
|
||||
None,
|
||||
None,
|
||||
);
|
||||
|
||||
if viewport.window.is_none() {
|
||||
viewport.init_window(viewport_from_window, painter, event_loop);
|
||||
}
|
||||
viewport.update_viewport_info();
|
||||
|
||||
let (Some(window), Some(winit_state)) = (&viewport.window, &mut viewport.egui_winit)
|
||||
else {
|
||||
@@ -2174,6 +2246,10 @@ mod wgpu_integration {
|
||||
};
|
||||
|
||||
let mut input = winit_state.take_egui_input(window, ids);
|
||||
input.viewports = viewports
|
||||
.iter()
|
||||
.map(|(id, viewport)| (*id, viewport.info.clone()))
|
||||
.collect();
|
||||
input.time = Some(beginning.elapsed().as_secs_f64());
|
||||
input
|
||||
};
|
||||
@@ -2308,20 +2384,6 @@ mod wgpu_integration {
|
||||
Ok(match event {
|
||||
winit::event::Event::Resumed => {
|
||||
let running = if let Some(running) = &self.running {
|
||||
if !running
|
||||
.shared
|
||||
.borrow()
|
||||
.viewports
|
||||
.contains_key(&ViewportId::ROOT)
|
||||
{
|
||||
create_window(
|
||||
event_loop,
|
||||
running.integration.frame.storage(),
|
||||
&self.app_name,
|
||||
&mut self.native_options,
|
||||
)?;
|
||||
running.set_window(ViewportId::ROOT)?;
|
||||
}
|
||||
running
|
||||
} else {
|
||||
let storage = epi_integration::create_storage(
|
||||
@@ -2394,18 +2456,6 @@ mod wgpu_integration {
|
||||
}
|
||||
|
||||
impl WgpuWinitRunning {
|
||||
fn set_window(&self, id: ViewportId) -> Result<(), egui_wgpu::WgpuError> {
|
||||
crate::profile_function!();
|
||||
let mut shared = self.shared.borrow_mut();
|
||||
let SharedState {
|
||||
viewports, painter, ..
|
||||
} = &mut *shared;
|
||||
if let Some(Viewport { window, .. }) = viewports.get(&id) {
|
||||
return pollster::block_on(painter.set_window(id, window.as_deref()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn save_and_destroy(&mut self) {
|
||||
crate::profile_function!();
|
||||
|
||||
@@ -2475,6 +2525,7 @@ mod wgpu_integration {
|
||||
let Some(viewport) = viewports.get_mut(&viewport_id) else {
|
||||
return EventResult::Wait;
|
||||
};
|
||||
viewport.update_viewport_info();
|
||||
|
||||
let Viewport {
|
||||
ids,
|
||||
@@ -2483,6 +2534,7 @@ mod wgpu_integration {
|
||||
egui_winit,
|
||||
..
|
||||
} = viewport;
|
||||
let viewport_ui_cb = viewport_ui_cb.clone();
|
||||
|
||||
let Some(window) = window else {
|
||||
return EventResult::Wait;
|
||||
@@ -2493,14 +2545,20 @@ mod wgpu_integration {
|
||||
log::warn!("Failed to set window: {err}");
|
||||
}
|
||||
|
||||
let raw_input = egui_winit.as_mut().unwrap().take_egui_input(
|
||||
let mut raw_input = egui_winit.as_mut().unwrap().take_egui_input(
|
||||
window,
|
||||
ViewportIdPair::from_self_and_parent(viewport_id, ids.parent),
|
||||
);
|
||||
|
||||
integration.pre_update(window);
|
||||
integration.pre_update();
|
||||
|
||||
(viewport_ui_cb.clone(), raw_input)
|
||||
raw_input.time = Some(integration.beginning.elapsed().as_secs_f64());
|
||||
raw_input.viewports = viewports
|
||||
.iter()
|
||||
.map(|(id, viewport)| (*id, viewport.info.clone()))
|
||||
.collect();
|
||||
|
||||
(viewport_ui_cb, raw_input)
|
||||
};
|
||||
|
||||
// ------------------------------------------------------------
|
||||
@@ -2533,7 +2591,7 @@ mod wgpu_integration {
|
||||
return EventResult::Wait;
|
||||
};
|
||||
|
||||
integration.post_update(app.as_mut(), window);
|
||||
integration.post_update();
|
||||
|
||||
let FullOutput {
|
||||
platform_output,
|
||||
@@ -2548,21 +2606,27 @@ mod wgpu_integration {
|
||||
{
|
||||
let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point);
|
||||
|
||||
let screenshot_requested = &mut integration.frame.output.screenshot_requested;
|
||||
let screenshot_requested = std::mem::take(&mut viewport.screenshot_requested);
|
||||
let screenshot = painter.paint_and_update_textures(
|
||||
viewport_id,
|
||||
pixels_per_point,
|
||||
app.clear_color(&integration.egui_ctx.style().visuals),
|
||||
&clipped_primitives,
|
||||
&textures_delta,
|
||||
*screenshot_requested,
|
||||
screenshot_requested,
|
||||
);
|
||||
*screenshot_requested = false;
|
||||
integration.frame.screenshot.set(screenshot);
|
||||
if let Some(screenshot) = screenshot {
|
||||
egui_winit
|
||||
.egui_input_mut()
|
||||
.events
|
||||
.push(egui::Event::Screenshot {
|
||||
viewport_id,
|
||||
image: screenshot.into(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
integration.post_rendering(app.as_mut(), window);
|
||||
integration.post_present(window);
|
||||
integration.post_rendering(window);
|
||||
|
||||
let active_viewports_ids: ViewportIdSet = viewport_output.keys().copied().collect();
|
||||
|
||||
@@ -2630,6 +2694,7 @@ mod wgpu_integration {
|
||||
winit::event::WindowEvent::Focused(new_focused) => {
|
||||
*focused_viewport = new_focused.then(|| viewport_id).flatten();
|
||||
}
|
||||
|
||||
winit::event::WindowEvent::Resized(physical_size) => {
|
||||
// Resize with 0 width and height is used by winit to signal a minimize event on Windows.
|
||||
// See: https://github.com/rust-windowing/winit/issues/208
|
||||
@@ -2645,6 +2710,7 @@ mod wgpu_integration {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
winit::event::WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
|
||||
use std::num::NonZeroU32;
|
||||
if let (Some(width), Some(height), Some(viewport_id)) = (
|
||||
@@ -2656,10 +2722,12 @@ mod wgpu_integration {
|
||||
shared.painter.on_window_resized(viewport_id, width, height);
|
||||
}
|
||||
}
|
||||
|
||||
winit::event::WindowEvent::CloseRequested if integration.should_close() => {
|
||||
log::debug!("Received WindowEvent::CloseRequested");
|
||||
return EventResult::Exit;
|
||||
}
|
||||
|
||||
_ => {}
|
||||
};
|
||||
|
||||
@@ -2709,7 +2777,7 @@ mod wgpu_integration {
|
||||
{
|
||||
let ids = ViewportIdPair::from_self_and_parent(viewport_id, parent);
|
||||
|
||||
initialize_or_update_viewport(
|
||||
let viewport = initialize_or_update_viewport(
|
||||
viewports,
|
||||
ids,
|
||||
class,
|
||||
@@ -2718,12 +2786,15 @@ mod wgpu_integration {
|
||||
focused_viewport,
|
||||
);
|
||||
|
||||
if let Some(window) = viewports
|
||||
.get(&viewport_id)
|
||||
.and_then(|vp| vp.window.as_ref())
|
||||
{
|
||||
if let Some(window) = viewport.window.as_ref() {
|
||||
let is_viewport_focused = focused_viewport == Some(viewport_id);
|
||||
egui_winit::process_viewport_commands(commands, window, is_viewport_focused);
|
||||
egui_winit::process_viewport_commands(
|
||||
&mut viewport.info,
|
||||
commands,
|
||||
window,
|
||||
is_viewport_focused,
|
||||
&mut viewport.screenshot_requested,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2751,6 +2822,8 @@ mod wgpu_integration {
|
||||
ids,
|
||||
class,
|
||||
builder,
|
||||
info: Default::default(),
|
||||
screenshot_requested: false,
|
||||
viewport_ui_cb,
|
||||
window: None,
|
||||
egui_winit: None,
|
||||
@@ -2777,7 +2850,13 @@ mod wgpu_integration {
|
||||
viewport.egui_winit = None;
|
||||
} else if let Some(window) = &viewport.window {
|
||||
let is_viewport_focused = focused_viewport == Some(ids.this);
|
||||
process_viewport_commands(delta_commands, window, is_viewport_focused);
|
||||
process_viewport_commands(
|
||||
&mut viewport.info,
|
||||
delta_commands,
|
||||
window,
|
||||
is_viewport_focused,
|
||||
&mut viewport.screenshot_requested,
|
||||
);
|
||||
}
|
||||
|
||||
entry.into_mut()
|
||||
|
||||
@@ -49,7 +49,6 @@ impl AppRunner {
|
||||
},
|
||||
system_theme,
|
||||
cpu_usage: None,
|
||||
native_pixels_per_point: Some(super::native_pixels_per_point()),
|
||||
};
|
||||
let storage = LocalStorage::default();
|
||||
|
||||
@@ -78,7 +77,6 @@ impl AppRunner {
|
||||
|
||||
let frame = epi::Frame {
|
||||
info,
|
||||
output: Default::default(),
|
||||
storage: Some(Box::new(storage)),
|
||||
|
||||
#[cfg(feature = "glow")]
|
||||
@@ -114,6 +112,7 @@ impl AppRunner {
|
||||
};
|
||||
|
||||
runner.input.raw.max_texture_side = Some(runner.painter.max_texture_side());
|
||||
runner.input.raw.native_pixels_per_point = Some(super::native_pixels_per_point());
|
||||
|
||||
Ok(runner)
|
||||
}
|
||||
@@ -192,17 +191,19 @@ impl AppRunner {
|
||||
if viewport_output.len() > 1 {
|
||||
log::warn!("Multiple viewports not yet supported on the web");
|
||||
}
|
||||
// TODO(emilk): handle some of the command in `viewport_output`, like setting the title and icon?
|
||||
for viewport_output in viewport_output.values() {
|
||||
for command in &viewport_output.commands {
|
||||
// TODO(emilk): handle some of the commands
|
||||
log::warn!(
|
||||
"Unhandled egui viewport command: {command:?} - not implemented in web backend"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
self.handle_platform_output(platform_output);
|
||||
self.textures_delta.append(textures_delta);
|
||||
let clipped_primitives = self.egui_ctx.tessellate(shapes, pixels_per_point);
|
||||
|
||||
{
|
||||
let app_output = self.frame.take_app_output();
|
||||
let epi::backend::AppOutput {} = app_output;
|
||||
}
|
||||
|
||||
self.frame.info.cpu_usage = Some((now_sec() - frame_start) as f32);
|
||||
|
||||
clipped_primitives
|
||||
|
||||
Reference in New Issue
Block a user