1
0
mirror of https://github.com/emilk/egui.git synced 2026-09-01 14:20:04 -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

@@ -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,