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:
@@ -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),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")]
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -58,6 +58,12 @@ impl AppRunner {
|
||||
));
|
||||
super::storage::load_memory(&egui_ctx);
|
||||
|
||||
egui_ctx.options_mut(|o| {
|
||||
// On web, the browser controls the zoom factor:
|
||||
o.zoom_with_keyboard = false;
|
||||
o.zoom_factor = 1.0;
|
||||
});
|
||||
|
||||
let theme = system_theme.unwrap_or(web_options.default_theme);
|
||||
egui_ctx.set_visuals(theme.egui_visuals());
|
||||
|
||||
@@ -112,7 +118,13 @@ 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());
|
||||
runner
|
||||
.input
|
||||
.raw
|
||||
.viewports
|
||||
.entry(egui::ViewportId::ROOT)
|
||||
.or_default()
|
||||
.native_pixels_per_point = Some(super::native_pixels_per_point());
|
||||
|
||||
Ok(runner)
|
||||
}
|
||||
|
||||
@@ -23,12 +23,17 @@ pub(crate) struct WebInput {
|
||||
|
||||
impl WebInput {
|
||||
pub fn new_frame(&mut self, canvas_size: egui::Vec2) -> egui::RawInput {
|
||||
egui::RawInput {
|
||||
let mut raw_input = egui::RawInput {
|
||||
screen_rect: Some(egui::Rect::from_min_size(Default::default(), canvas_size)),
|
||||
pixels_per_point: Some(super::native_pixels_per_point()), // We ALWAYS use the native pixels-per-point
|
||||
time: Some(super::now_sec()),
|
||||
..self.raw.take()
|
||||
}
|
||||
};
|
||||
raw_input
|
||||
.viewports
|
||||
.entry(egui::ViewportId::ROOT)
|
||||
.or_default()
|
||||
.native_pixels_per_point = Some(super::native_pixels_per_point());
|
||||
raw_input
|
||||
}
|
||||
|
||||
pub fn on_web_page_focus_change(&mut self, focused: bool) {
|
||||
|
||||
Reference in New Issue
Block a user