1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-30 13:20:05 -04:00

Introduce global zoom_factor (#3608)

* Closes https://github.com/emilk/egui/issues/3602

You can now zoom any egui app by pressing Cmd+Plus, Cmd+Minus or Cmd+0,
just like in a browser. This will change the current `zoom_factor`
(default 1.0) which is persisted in the egui memory, and is the same for
all viewports.
You can turn off the keyboard shortcuts with `ctx.options_mut(|o|
o.zoom_with_keyboard = false);`

`zoom_factor` can also be explicitly read/written with
`ctx.zoom_factor()` and `ctx.set_zoom_factor()`.

This redefines `pixels_per_point` as `zoom_factor *
native_pixels_per_point`, where `native_pixels_per_point` is whatever is
the native scale factor for the monitor that the current viewport is in.

This adds some complexity to the interaction with winit, since we need
to know the current `zoom_factor` in a lot of places, because all egui
IO is done in ui points. I'm pretty sure this PR fixes a bunch of subtle
bugs though that used to be in this code.

`egui::gui_zoom::zoom_with_keyboard_shortcuts` is now gone, and is no
longer needed, as this is now the default behavior.

`Context::set_pixels_per_point` is still there, but it is recommended
you use `Context::set_zoom_factor` instead.
This commit is contained in:
Emil Ernerfeldt
2023-11-22 20:34:51 +01:00
committed by GitHub
parent ea53246c60
commit 63e48dc855
26 changed files with 750 additions and 579 deletions

View File

@@ -12,6 +12,7 @@ use egui_winit::{EventResponse, WindowSettings};
use crate::{epi, Theme};
pub fn viewport_builder<E>(
egui_zoom_factor: f32,
event_loop: &EventLoopWindowTarget<E>,
native_options: &mut epi::NativeOptions,
window_settings: Option<WindowSettings>,
@@ -26,8 +27,9 @@ pub fn viewport_builder<E>(
let inner_size_points = if let Some(mut window_settings) = window_settings {
// Restore pos/size from previous session
window_settings.clamp_size_to_sane_values(largest_monitor_point_size(event_loop));
window_settings.clamp_position_to_monitors(event_loop);
window_settings
.clamp_size_to_sane_values(largest_monitor_point_size(egui_zoom_factor, event_loop));
window_settings.clamp_position_to_monitors(egui_zoom_factor, event_loop);
viewport_builder = window_settings.initialize_viewport_builder(viewport_builder);
window_settings.inner_size_points()
@@ -37,8 +39,8 @@ pub fn viewport_builder<E>(
}
if let Some(initial_window_size) = viewport_builder.inner_size {
let initial_window_size =
initial_window_size.at_most(largest_monitor_point_size(event_loop));
let initial_window_size = initial_window_size
.at_most(largest_monitor_point_size(egui_zoom_factor, event_loop));
viewport_builder = viewport_builder.with_inner_size(initial_window_size);
}
@@ -49,9 +51,11 @@ pub fn viewport_builder<E>(
if native_options.centered {
crate::profile_scope!("center");
if let Some(monitor) = event_loop.available_monitors().next() {
let monitor_size = monitor.size().to_logical::<f32>(monitor.scale_factor());
let monitor_size = monitor
.size()
.to_logical::<f32>(egui_zoom_factor as f64 * monitor.scale_factor());
let inner_size = inner_size_points.unwrap_or(egui::Vec2 { x: 800.0, y: 600.0 });
if monitor_size.width > 0.0 && monitor_size.height > 0.0 {
if 0.0 < monitor_size.width && 0.0 < monitor_size.height {
let x = (monitor_size.width - inner_size.x) / 2.0;
let y = (monitor_size.height - inner_size.y) / 2.0;
viewport_builder = viewport_builder.with_position([x, y]);
@@ -76,7 +80,10 @@ pub fn apply_window_settings(
}
}
fn largest_monitor_point_size<E>(event_loop: &EventLoopWindowTarget<E>) -> egui::Vec2 {
fn largest_monitor_point_size<E>(
egui_zoom_factor: f32,
event_loop: &EventLoopWindowTarget<E>,
) -> egui::Vec2 {
crate::profile_function!();
let mut max_size = egui::Vec2::ZERO;
@@ -87,7 +94,9 @@ fn largest_monitor_point_size<E>(event_loop: &EventLoopWindowTarget<E>) -> egui:
};
for monitor in available_monitors {
let size = monitor.size().to_logical::<f32>(monitor.scale_factor());
let size = monitor
.size()
.to_logical::<f32>(egui_zoom_factor as f64 * monitor.scale_factor());
let size = egui::vec2(size.width, size.height);
max_size = max_size.max(size);
}
@@ -137,21 +146,15 @@ pub struct EpiIntegration {
impl EpiIntegration {
#[allow(clippy::too_many_arguments)]
pub fn new(
egui_ctx: egui::Context,
window: &winit::window::Window,
system_theme: Option<Theme>,
app_name: &str,
native_options: &crate::NativeOptions,
storage: Option<Box<dyn epi::Storage>>,
is_desktop: bool,
#[cfg(feature = "glow")] gl: Option<std::rc::Rc<glow::Context>>,
#[cfg(feature = "wgpu")] wgpu_render_state: Option<egui_wgpu::RenderState>,
) -> Self {
let egui_ctx = egui::Context::default();
egui_ctx.set_embed_viewports(!is_desktop);
let memory = load_egui_memory(storage.as_deref()).unwrap_or_default();
egui_ctx.memory_mut(|mem| *mem = memory);
let frame = epi::Frame {
info: epi::IntegrationInfo {
system_theme,
@@ -245,9 +248,6 @@ impl EpiIntegration {
state: ElementState::Pressed,
..
} => self.can_drag_window = true,
WindowEvent::ScaleFactorChanged { scale_factor, .. } => {
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);
self.frame.info.system_theme = Some(theme);
@@ -336,7 +336,7 @@ impl EpiIntegration {
epi::set_value(
storage,
STORAGE_WINDOW_KEY,
&WindowSettings::from_display(window),
&WindowSettings::from_window(self.egui_ctx.zoom_factor(), window),
);
}
}

View File

@@ -24,8 +24,8 @@ use egui_winit::{
};
use crate::{
native::epi_integration::EpiIntegration, App, AppCreator, CreationContext, NativeOptions,
Result, Storage,
native::{epi_integration::EpiIntegration, winit_integration::create_egui_context},
App, AppCreator, CreationContext, NativeOptions, Result, Storage,
};
use super::{
@@ -86,6 +86,8 @@ struct GlowWinitRunning {
/// The setup is divided between the `new` fn and `on_resume` fn. we can just assume that `on_resume` is a continuation of
/// `new` fn on all platforms. only on android, do we get multiple resumed events because app can be suspended.
struct GlutinWindowContext {
egui_ctx: egui::Context,
swap_interval: glutin::surface::SwapInterval,
gl_config: glutin::config::Config,
@@ -138,6 +140,7 @@ impl GlowWinitApp {
#[allow(unsafe_code)]
fn create_glutin_windowed_context(
egui_ctx: &egui::Context,
event_loop: &EventLoopWindowTarget<UserEvent>,
storage: Option<&dyn Storage>,
native_options: &mut NativeOptions,
@@ -146,11 +149,16 @@ impl GlowWinitApp {
let window_settings = epi_integration::load_window_settings(storage);
let winit_window_builder =
epi_integration::viewport_builder(event_loop, native_options, window_settings);
let winit_window_builder = epi_integration::viewport_builder(
egui_ctx.zoom_factor(),
event_loop,
native_options,
window_settings,
);
let mut glutin_window_context =
unsafe { GlutinWindowContext::new(winit_window_builder, native_options, event_loop)? };
let mut glutin_window_context = unsafe {
GlutinWindowContext::new(egui_ctx, winit_window_builder, native_options, event_loop)?
};
// Creates the window - must come before we create our glow context
glutin_window_context.on_resume(event_loop)?;
@@ -190,7 +198,10 @@ impl GlowWinitApp {
.unwrap_or(&self.app_name),
);
let egui_ctx = create_egui_context(storage.as_deref());
let (mut glutin, painter) = Self::create_glutin_windowed_context(
&egui_ctx,
event_loop,
storage.as_deref(),
&mut self.native_options,
@@ -209,12 +220,12 @@ impl GlowWinitApp {
winit_integration::system_theme(&glutin.window(ViewportId::ROOT), &self.native_options);
let integration = EpiIntegration::new(
egui_ctx,
&glutin.window(ViewportId::ROOT),
system_theme,
&self.app_name,
&self.native_options,
storage,
winit_integration::IS_DESKTOP,
Some(gl.clone()),
#[cfg(feature = "wgpu")]
None,
@@ -640,7 +651,7 @@ impl GlowWinitRunning {
std::thread::sleep(std::time::Duration::from_millis(10));
}
glutin.handle_viewport_output(viewport_output);
glutin.handle_viewport_output(&integration.egui_ctx, viewport_output);
if integration.should_close() {
EventResult::Exit
@@ -761,6 +772,7 @@ impl GlowWinitRunning {
impl GlutinWindowContext {
#[allow(unsafe_code)]
unsafe fn new(
egui_ctx: &egui::Context,
viewport_builder: ViewportBuilder,
native_options: &NativeOptions,
event_loop: &EventLoopWindowTarget<UserEvent>,
@@ -812,7 +824,11 @@ impl GlutinWindowContext {
let display_builder = glutin_winit::DisplayBuilder::new()
// we might want to expose this option to users in the future. maybe using an env var or using native_options.
.with_preference(glutin_winit::ApiPrefence::FallbackEgl) // https://github.com/emilk/egui/issues/2520#issuecomment-1367841150
.with_window_builder(Some(create_winit_window_builder(viewport_builder.clone())));
.with_window_builder(Some(create_winit_window_builder(
egui_ctx,
event_loop,
viewport_builder.clone(),
)));
let (window, gl_config) = {
crate::profile_scope!("DisplayBuilder::build");
@@ -908,6 +924,7 @@ impl GlutinWindowContext {
// https://github.com/emilk/egui/pull/2541#issuecomment-1370767582
let mut slf = GlutinWindowContext {
egui_ctx: egui_ctx.clone(),
swap_interval,
gl_config,
current_gl_context: None,
@@ -967,7 +984,7 @@ impl GlutinWindowContext {
log::trace!("Window doesn't exist yet. Creating one now with finalize_window");
let window = glutin_winit::finalize_window(
event_loop,
create_winit_window_builder(viewport.builder.clone()),
create_winit_window_builder(&self.egui_ctx, event_loop, viewport.builder.clone()),
&self.gl_config,
)?;
apply_viewport_builder_to_new_window(&window, &viewport.builder);
@@ -1095,7 +1112,11 @@ impl GlutinWindowContext {
self.gl_config.display().get_proc_address(addr)
}
fn handle_viewport_output(&mut self, viewport_output: ViewportIdMap<ViewportOutput>) {
fn handle_viewport_output(
&mut self,
egui_ctx: &egui::Context,
viewport_output: ViewportIdMap<ViewportOutput>,
) {
crate::profile_function!();
let active_viewports_ids: ViewportIdSet = viewport_output.keys().copied().collect();
@@ -1115,6 +1136,7 @@ impl GlutinWindowContext {
let ids = ViewportIdPair::from_self_and_parent(viewport_id, parent);
let viewport = initialize_or_update_viewport(
egui_ctx,
&mut self.viewports,
ids,
class,
@@ -1126,6 +1148,7 @@ impl GlutinWindowContext {
if let Some(window) = &viewport.window {
let is_viewport_focused = self.focused_viewport == Some(viewport_id);
egui_winit::process_viewport_commands(
egui_ctx,
&mut viewport.info,
commands,
window,
@@ -1158,14 +1181,15 @@ impl Viewport {
}
}
fn initialize_or_update_viewport(
viewports: &mut ViewportIdMap<Viewport>,
fn initialize_or_update_viewport<'vp>(
egu_ctx: &'_ egui::Context,
viewports: &'vp mut ViewportIdMap<Viewport>,
ids: ViewportIdPair,
class: ViewportClass,
mut builder: ViewportBuilder,
viewport_ui_cb: Option<Arc<dyn Fn(&egui::Context) + Send + Sync>>,
focused_viewport: Option<ViewportId>,
) -> &mut Viewport {
) -> &'vp mut Viewport {
crate::profile_function!();
if builder.icon.is_none() {
@@ -1213,6 +1237,7 @@ fn initialize_or_update_viewport(
} else if let Some(window) = &viewport.window {
let is_viewport_focused = focused_viewport == Some(ids.this);
process_viewport_commands(
egu_ctx,
&mut viewport.info,
delta_commands,
window,
@@ -1248,6 +1273,7 @@ fn render_immediate_viewport(
let mut glutin = glutin.borrow_mut();
let viewport = initialize_or_update_viewport(
egui_ctx,
&mut glutin.viewports,
ids,
ViewportClass::Immediate,
@@ -1363,7 +1389,7 @@ fn render_immediate_viewport(
winit_state.handle_platform_output(window, egui_ctx, platform_output);
glutin.handle_viewport_output(viewport_output);
glutin.handle_viewport_output(egui_ctx, viewport_output);
}
#[cfg(feature = "__screenshot")]

View File

@@ -57,6 +57,7 @@ struct WgpuWinitRunning {
///
/// Wrapped in an `Rc<RefCell<…>>` so it can be re-entrantly shared via a weak-pointer.
pub struct SharedState {
egui_ctx: egui::Context,
viewports: Viewports,
painter: egui_wgpu::winit::Painter,
viewport_from_window: HashMap<WindowId, ViewportId>,
@@ -123,7 +124,12 @@ impl WgpuWinitApp {
for viewport in viewports.values_mut() {
if viewport.window.is_none() {
viewport.init_window(viewport_from_window, painter, event_loop);
viewport.init_window(
&running.integration.egui_ctx,
viewport_from_window,
painter,
event_loop,
);
}
}
}
@@ -140,6 +146,7 @@ impl WgpuWinitApp {
fn init_run_state(
&mut self,
egui_ctx: egui::Context,
event_loop: &EventLoopWindowTarget<UserEvent>,
storage: Option<Box<dyn Storage>>,
window: Window,
@@ -163,12 +170,12 @@ impl WgpuWinitApp {
let system_theme = winit_integration::system_theme(&window, &self.native_options);
let integration = EpiIntegration::new(
egui_ctx.clone(),
&window,
system_theme,
&self.app_name,
&self.native_options,
storage,
winit_integration::IS_DESKTOP,
#[cfg(feature = "glow")]
None,
wgpu_render_state.clone(),
@@ -177,22 +184,20 @@ impl WgpuWinitApp {
{
let event_loop_proxy = self.repaint_proxy.clone();
integration
.egui_ctx
.set_request_repaint_callback(move |info| {
log::trace!("request_repaint_callback: {info:?}");
let when = Instant::now() + info.delay;
let frame_nr = info.current_frame_nr;
egui_ctx.set_request_repaint_callback(move |info| {
log::trace!("request_repaint_callback: {info:?}");
let when = Instant::now() + info.delay;
let frame_nr = info.current_frame_nr;
event_loop_proxy
.lock()
.send_event(UserEvent::RequestRepaint {
when,
frame_nr,
viewport_id: info.viewport_id,
})
.ok();
});
event_loop_proxy
.lock()
.send_event(UserEvent::RequestRepaint {
when,
frame_nr,
viewport_id: info.viewport_id,
})
.ok();
});
}
let mut egui_winit = egui_winit::State::new(
@@ -208,12 +213,12 @@ impl WgpuWinitApp {
integration.init_accesskit(&mut egui_winit, &window, event_loop_proxy);
}
let theme = system_theme.unwrap_or(self.native_options.default_theme);
integration.egui_ctx.set_visuals(theme.egui_visuals());
egui_ctx.set_visuals(theme.egui_visuals());
let app_creator = std::mem::take(&mut self.app_creator)
.expect("Single-use AppCreator has unexpectedly already been taken");
let cc = CreationContext {
egui_ctx: integration.egui_ctx.clone(),
egui_ctx: egui_ctx.clone(),
integration_info: integration.frame.info().clone(),
storage: integration.frame.storage(),
#[cfg(feature = "glow")]
@@ -250,6 +255,7 @@ impl WgpuWinitApp {
);
let shared = Rc::new(RefCell::new(SharedState {
egui_ctx,
viewport_from_window,
viewports,
painter,
@@ -263,20 +269,14 @@ impl WgpuWinitApp {
let event_loop: *const EventLoopWindowTarget<UserEvent> = event_loop;
egui::Context::set_immediate_viewport_renderer(move |egui_ctx, immediate_viewport| {
egui::Context::set_immediate_viewport_renderer(move |_egui_ctx, immediate_viewport| {
if let Some(shared) = shared.upgrade() {
// SAFETY: the event loop lives longer than
// the Rc:s we just upgraded above.
#[allow(unsafe_code)]
let event_loop = unsafe { event_loop.as_ref().unwrap() };
render_immediate_viewport(
event_loop,
egui_ctx,
beginning,
&shared,
immediate_viewport,
);
render_immediate_viewport(event_loop, beginning, &shared, immediate_viewport);
} else {
log::warn!("render_sync_callback called after window closed");
}
@@ -374,9 +374,14 @@ impl WinitApp for WgpuWinitApp {
.as_ref()
.unwrap_or(&self.app_name),
);
let (window, builder) =
create_window(event_loop, storage.as_deref(), &mut self.native_options)?;
self.init_run_state(event_loop, storage, window, builder)?
let egui_ctx = winit_integration::create_egui_context(storage.as_deref());
let (window, builder) = create_window(
&egui_ctx,
event_loop,
storage.as_deref(),
&mut self.native_options,
)?;
self.init_run_state(egui_ctx, event_loop, storage, window, builder)?
};
EventResult::RepaintNow(
@@ -549,6 +554,7 @@ impl WgpuWinitRunning {
let mut shared = shared.borrow_mut();
let SharedState {
egui_ctx,
viewports,
painter,
viewport_from_window,
@@ -578,16 +584,16 @@ impl WgpuWinitRunning {
viewport_output,
} = full_output;
egui_winit.handle_platform_output(window, &integration.egui_ctx, platform_output);
egui_winit.handle_platform_output(window, egui_ctx, platform_output);
{
let clipped_primitives = integration.egui_ctx.tessellate(shapes, pixels_per_point);
let clipped_primitives = egui_ctx.tessellate(shapes, pixels_per_point);
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),
app.clear_color(&egui_ctx.style().visuals),
&clipped_primitives,
&textures_delta,
screenshot_requested,
@@ -607,7 +613,12 @@ impl WgpuWinitRunning {
let active_viewports_ids: ViewportIdSet = viewport_output.keys().copied().collect();
handle_viewport_output(viewport_output, viewports, *focused_viewport);
handle_viewport_output(
&integration.egui_ctx,
viewport_output,
viewports,
*focused_viewport,
);
// Prune dead viewports:
viewports.retain(|id, _| active_viewports_ids.contains(id));
@@ -755,6 +766,7 @@ impl WgpuWinitRunning {
impl Viewport {
fn init_window(
&mut self,
egui_ctx: &egui::Context,
windows_id: &mut HashMap<WindowId, ViewportId>,
painter: &mut egui_wgpu::winit::Painter,
event_loop: &EventLoopWindowTarget<UserEvent>,
@@ -763,7 +775,9 @@ impl Viewport {
let viewport_id = self.ids.this;
match create_winit_window_builder(self.builder.clone()).build(event_loop) {
match create_winit_window_builder(egui_ctx, event_loop, self.builder.clone())
.build(event_loop)
{
Ok(window) => {
apply_viewport_builder_to_new_window(&window, &self.builder);
@@ -806,6 +820,7 @@ impl Viewport {
}
fn create_window(
egui_ctx: &egui::Context,
event_loop: &EventLoopWindowTarget<UserEvent>,
storage: Option<&dyn Storage>,
native_options: &mut NativeOptions,
@@ -813,11 +828,16 @@ fn create_window(
crate::profile_function!();
let window_settings = epi_integration::load_window_settings(storage);
let viewport_builder =
epi_integration::viewport_builder(event_loop, native_options, window_settings);
let viewport_builder = epi_integration::viewport_builder(
egui_ctx.zoom_factor(),
event_loop,
native_options,
window_settings,
);
let window = {
crate::profile_scope!("WindowBuilder::build");
create_winit_window_builder(viewport_builder.clone()).build(event_loop)?
create_winit_window_builder(egui_ctx, event_loop, viewport_builder.clone())
.build(event_loop)?
};
apply_viewport_builder_to_new_window(&window, &viewport_builder);
epi_integration::apply_window_settings(&window, window_settings);
@@ -826,7 +846,6 @@ fn create_window(
fn render_immediate_viewport(
event_loop: &EventLoopWindowTarget<UserEvent>,
egui_ctx: &egui::Context,
beginning: Instant,
shared: &RefCell<SharedState>,
immediate_viewport: ImmediateViewport<'_>,
@@ -841,6 +860,7 @@ fn render_immediate_viewport(
let input = {
let SharedState {
egui_ctx,
viewports,
painter,
viewport_from_window,
@@ -848,6 +868,7 @@ fn render_immediate_viewport(
} = &mut *shared.borrow_mut();
let viewport = initialize_or_update_viewport(
egui_ctx,
viewports,
ids,
ViewportClass::Immediate,
@@ -856,7 +877,7 @@ fn render_immediate_viewport(
None,
);
if viewport.window.is_none() {
viewport.init_window(viewport_from_window, painter, event_loop);
viewport.init_window(egui_ctx, viewport_from_window, painter, event_loop);
}
viewport.update_viewport_info();
@@ -873,6 +894,8 @@ fn render_immediate_viewport(
input
};
let egui_ctx = shared.borrow().egui_ctx.clone();
// ------------------------------------------
// Run the user code, which could re-entrantly call this function again (!).
@@ -924,13 +947,14 @@ fn render_immediate_viewport(
false,
);
winit_state.handle_platform_output(window, egui_ctx, platform_output);
winit_state.handle_platform_output(window, &egui_ctx, platform_output);
handle_viewport_output(viewport_output, viewports, *focused_viewport);
handle_viewport_output(&egui_ctx, viewport_output, viewports, *focused_viewport);
}
/// Add new viewports, and update existing ones:
fn handle_viewport_output(
egui_ctx: &egui::Context,
viewport_output: ViewportIdMap<ViewportOutput>,
viewports: &mut ViewportIdMap<Viewport>,
focused_viewport: Option<ViewportId>,
@@ -950,6 +974,7 @@ fn handle_viewport_output(
let ids = ViewportIdPair::from_self_and_parent(viewport_id, parent);
let viewport = initialize_or_update_viewport(
egui_ctx,
viewports,
ids,
class,
@@ -961,6 +986,7 @@ fn handle_viewport_output(
if let Some(window) = viewport.window.as_ref() {
let is_viewport_focused = focused_viewport == Some(viewport_id);
egui_winit::process_viewport_commands(
egui_ctx,
&mut viewport.info,
commands,
window,
@@ -971,14 +997,15 @@ fn handle_viewport_output(
}
}
fn initialize_or_update_viewport(
viewports: &mut Viewports,
fn initialize_or_update_viewport<'vp>(
egui_ctx: &egui::Context,
viewports: &'vp mut Viewports,
ids: ViewportIdPair,
class: ViewportClass,
mut builder: ViewportBuilder,
viewport_ui_cb: Option<Arc<dyn Fn(&egui::Context) + Send + Sync>>,
focused_viewport: Option<ViewportId>,
) -> &mut Viewport {
) -> &'vp mut Viewport {
if builder.icon.is_none() {
// Inherit icon from parent
builder.icon = viewports
@@ -1023,6 +1050,7 @@ fn initialize_or_update_viewport(
} else if let Some(window) = &viewport.window {
let is_viewport_focused = focused_viewport == Some(ids.this);
process_viewport_commands(
egui_ctx,
&mut viewport.info,
delta_commands,
window,

View File

@@ -11,13 +11,27 @@ use egui_winit::accesskit_winit;
use super::epi_integration::EpiIntegration;
pub const IS_DESKTOP: bool = cfg!(any(
target_os = "freebsd",
target_os = "linux",
target_os = "macos",
target_os = "openbsd",
target_os = "windows",
));
/// Create an egui context, restoring it from storage if possible.
pub fn create_egui_context(storage: Option<&dyn crate::Storage>) -> egui::Context {
crate::profile_function!();
pub const IS_DESKTOP: bool = cfg!(any(
target_os = "freebsd",
target_os = "linux",
target_os = "macos",
target_os = "openbsd",
target_os = "windows",
));
let egui_ctx = egui::Context::default();
egui_ctx.set_embed_viewports(!IS_DESKTOP);
let memory = crate::native::epi_integration::load_egui_memory(storage).unwrap_or_default();
egui_ctx.memory_mut(|mem| *mem = memory);
egui_ctx
}
/// The custom even `eframe` uses with the [`winit`] event loop.
#[derive(Debug)]