diff --git a/CHANGELOG.md b/CHANGELOG.md index 0413b3c1..94d66d85 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,7 +13,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Add `egui::plot::Plot` to plot some 2D data. * Add `Ui::hyperlink_to(label, url)`. -* Sliders can now have a value prefix and suffix (e.g. "°" as a unit). +* Sliders can now have a value prefix and suffix (e.g. the suffix `"°"` works like a unit). +* `Context::set_pixels_per_point` to control the scale of the UI. ### Changed 🔧 diff --git a/egui/src/context.rs b/egui/src/context.rs index 81038adc..25d950e3 100644 --- a/egui/src/context.rs +++ b/egui/src/context.rs @@ -388,7 +388,6 @@ impl Context { } /// Will become active at the start of the next frame. - /// `pixels_per_point` will be ignored (overwritten at start of each frame with the contents of input) pub fn set_fonts(&self, font_definitions: FontDefinitions) { self.memory().options.font_definitions = font_definitions; } @@ -429,6 +428,15 @@ impl Context { self.input.pixels_per_point() } + /// Set the number of physical pixels for each logical point. + /// Will become active at the start of the next frame. + /// + /// Note that this may be overwritten by input from the integration via [`RawInput::pixels_per_point`]. + /// For instance, when using `egui_web` the browsers native zoom level will always be used. + pub fn set_pixels_per_point(&self, pixels_per_point: f32) { + self.memory().new_pixels_per_point = Some(pixels_per_point); + } + /// Useful for pixel-perfect rendering pub(crate) fn round_to_pixel(&self, point: f32) -> f32 { let pixels_per_point = self.pixels_per_point(); @@ -493,7 +501,12 @@ impl Context { fn begin_frame_mut(&mut self, new_raw_input: RawInput) { self.memory().begin_frame(&self.input, &new_raw_input); - self.input = std::mem::take(&mut self.input).begin_frame(new_raw_input); + let mut input = std::mem::take(&mut self.input); + if let Some(new_pixels_per_point) = self.memory().new_pixels_per_point.take() { + input.pixels_per_point = new_pixels_per_point; + } + + self.input = input.begin_frame(new_raw_input); self.frame_state.lock().begin_frame(&self.input); let font_definitions = self.memory().options.font_definitions.clone(); diff --git a/egui/src/data/input.rs b/egui/src/data/input.rs index 8795f7a1..10f7f7a7 100644 --- a/egui/src/data/input.rs +++ b/egui/src/data/input.rs @@ -27,6 +27,7 @@ pub struct RawInput { /// Also known as device pixel ratio, > 1 for HDPI screens. /// If text looks blurry on high resolution screens, you probably forgot to set this. + /// Set this the first frame, whenever it changes, or just on every frame. pub pixels_per_point: Option, /// Monotonically increasing time, in seconds. Relative to whatever. Used for animations. @@ -68,9 +69,9 @@ impl RawInput { RawInput { scroll_delta: std::mem::take(&mut self.scroll_delta), screen_size: self.screen_size, - screen_rect: self.screen_rect, - pixels_per_point: self.pixels_per_point, - time: self.time, + screen_rect: self.screen_rect.take(), + pixels_per_point: self.pixels_per_point.take(), + time: self.time.take(), predicted_dt: self.predicted_dt, modifiers: self.modifiers, events: std::mem::take(&mut self.events), diff --git a/egui/src/memory.rs b/egui/src/memory.rs index c2e4f6aa..5bfde7e1 100644 --- a/egui/src/memory.rs +++ b/egui/src/memory.rs @@ -20,6 +20,9 @@ use epaint::color::{Color32, Hsva}; pub struct Memory { pub(crate) options: Options, + /// new scale that will be applied at the start of the next frame + pub(crate) new_pixels_per_point: Option, + #[cfg_attr(feature = "persistence", serde(skip))] pub(crate) interaction: Interaction, diff --git a/egui_demo_lib/src/wrap_app.rs b/egui_demo_lib/src/wrap_app.rs index d3200db9..29b4ec71 100644 --- a/egui_demo_lib/src/wrap_app.rs +++ b/egui_demo_lib/src/wrap_app.rs @@ -251,11 +251,13 @@ impl BackendPanel { self.frame_history.ui(ui); - if !frame.is_web() { - // web browsers have their own way of zooming, which egui_web respects + // For instance: `egui_web` sets `pixels_per_point` every frame to force + // egui to use the same scale as the web zoom factor. + let integration_controls_pixels_per_point = ui.input().raw.pixels_per_point.is_some(); + if !integration_controls_pixels_per_point { ui.separator(); if let Some(new_pixels_per_point) = self.pixels_per_point_ui(ui, frame.info()) { - frame.set_pixels_per_point(new_pixels_per_point); + ui.ctx().set_pixels_per_point(new_pixels_per_point); } } diff --git a/egui_glium/src/backend.rs b/egui_glium/src/backend.rs index c87c555a..dcf177c7 100644 --- a/egui_glium/src/backend.rs +++ b/egui_glium/src/backend.rs @@ -192,11 +192,16 @@ pub fn run(mut app: Box) -> ! { event_loop.run(move |event, _, control_flow| { let mut redraw = || { + let pixels_per_point = input_state + .raw + .pixels_per_point + .unwrap_or_else(|| ctx.pixels_per_point()); + let frame_start = Instant::now(); input_state.raw.time = Some(start_time.elapsed().as_nanos() as f64 * 1e-9); input_state.raw.screen_rect = Some(Rect::from_min_size( Default::default(), - screen_size_in_pixels(&display) / input_state.raw.pixels_per_point.unwrap(), + screen_size_in_pixels(&display) / pixels_per_point, )); ctx.begin_frame(input_state.raw.take()); @@ -225,16 +230,7 @@ pub fn run(mut app: Box) -> ! { ); { - let epi::backend::AppOutput { - quit, - window_size, - pixels_per_point, - } = app_output; - - if let Some(pixels_per_point) = pixels_per_point { - // User changed GUI scale - input_state.raw.pixels_per_point = Some(pixels_per_point); - } + let epi::backend::AppOutput { quit, window_size } = app_output; if let Some(window_size) = window_size { display.gl_window().window().set_inner_size( @@ -283,7 +279,13 @@ pub fn run(mut app: Box) -> ! { glutin::event::Event::RedrawRequested(_) if !cfg!(windows) => redraw(), glutin::event::Event::WindowEvent { event, .. } => { - input_to_egui(event, clipboard.as_mut(), &mut input_state, control_flow); + input_to_egui( + ctx.pixels_per_point(), + event, + clipboard.as_mut(), + &mut input_state, + control_flow, + ); display.gl_window().window().request_redraw(); // TODO: ask egui if the events warrants a repaint instead } glutin::event::Event::LoopDestroyed => { diff --git a/egui_glium/src/lib.rs b/egui_glium/src/lib.rs index 842daaaf..482659f9 100644 --- a/egui_glium/src/lib.rs +++ b/egui_glium/src/lib.rs @@ -46,6 +46,7 @@ impl GliumInputState { } pub fn input_to_egui( + pixels_per_point: f32, event: glutin::event::WindowEvent<'_>, clipboard: Option<&mut ClipboardContext>, input_state: &mut GliumInputState, @@ -54,6 +55,9 @@ pub fn input_to_egui( use glutin::event::WindowEvent; match event { WindowEvent::CloseRequested | WindowEvent::Destroyed => *control_flow = ControlFlow::Exit, + WindowEvent::ScaleFactorChanged { scale_factor, .. } => { + input_state.raw.pixels_per_point = Some(scale_factor as f32); + } WindowEvent::MouseInput { state, button, .. } => { if let Some(pos_in_points) = input_state.pointer_pos_in_points { if let Some(button) = translate_mouse_button(button) { @@ -71,8 +75,8 @@ pub fn input_to_egui( .. } => { let pos_in_points = pos2( - pos_in_pixels.x as f32 / input_state.raw.pixels_per_point.unwrap(), - pos_in_pixels.y as f32 / input_state.raw.pixels_per_point.unwrap(), + pos_in_pixels.x as f32 / pixels_per_point, + pos_in_pixels.y as f32 / pixels_per_point, ); input_state.pointer_pos_in_points = Some(pos_in_points); input_state diff --git a/egui_web/src/backend.rs b/egui_web/src/backend.rs index d4cbf688..3fc37deb 100644 --- a/egui_web/src/backend.rs +++ b/egui_web/src/backend.rs @@ -91,7 +91,7 @@ impl WebInput { pub fn new_frame(&mut self, canvas_size: egui::Vec2) -> egui::RawInput { egui::RawInput { screen_rect: Some(egui::Rect::from_min_size(Default::default(), canvas_size)), - pixels_per_point: Some(native_pixels_per_point()), + pixels_per_point: Some(native_pixels_per_point()), // We ALWAYS use the native pixels-per-point time: Some(now_sec()), ..self.raw.take() } @@ -221,9 +221,8 @@ impl AppRunner { { let epi::backend::AppOutput { - quit: _, // Can't quit a web page - window_size: _, // Can't resize a web page - pixels_per_point: _, // Can't zoom from within the app (we respect the web browser's zoom level) + quit: _, // Can't quit a web page + window_size: _, // Can't resize a web page } = app_output; } diff --git a/epi/src/lib.rs b/epi/src/lib.rs index b6359f1d..7e348a1a 100644 --- a/epi/src/lib.rs +++ b/epi/src/lib.rs @@ -144,10 +144,9 @@ impl<'a> Frame<'a> { self.0.output.window_size = Some(size); } - /// Change the `pixels_per_point` of [`egui`] to this next frame. - pub fn set_pixels_per_point(&mut self, pixels_per_point: f32) { - self.0.output.pixels_per_point = Some(pixels_per_point); - } + /// Use [`egui::Context::set_pixels_per_point`] instead + #[deprecated = "Use egui::Context::set_pixels_per_point instead"] + pub fn set_pixels_per_point(&mut self, _: f32) {} /// If you need to request a repaint from another thread, clone this and send it to that other thread. pub fn repaint_signal(&self) -> std::sync::Arc { @@ -367,8 +366,5 @@ pub mod backend { /// Set to some size to resize the outer window (e.g. glium window) to this size. pub window_size: Option, - - /// If the app sets this, change the `pixels_per_point` of [`egui`] to this next frame. - pub pixels_per_point: Option, } }