mirror of
https://github.com/emilk/egui.git
synced 2026-09-01 14:20:04 -04:00
Add helpers for zooming an app using Ctrl+Plus and Ctrl+Minus (#2239)
* Using tracing-subscriber in hello_world example * Add Key::Plus/Minus/Equals * Warn if failing to guess OS from User-Agent * Remove jitter when using Context::set_pixels_per_point * Demo app: zoom in/out using ⌘+ and ⌘- * Demo app: make backend panel GUI scale slider better * Optimize debug builds a bit * typo * Update changelog * Add helper module `egui::gui_zoom` for zooming an app * Better names, and update changelog * Combine Plus and Equals keys * Last fix * Fix docs
This commit is contained in:
@@ -65,18 +65,25 @@ struct ContextImpl {
|
||||
}
|
||||
|
||||
impl ContextImpl {
|
||||
fn begin_frame_mut(&mut self, new_raw_input: RawInput) {
|
||||
fn begin_frame_mut(&mut self, mut new_raw_input: RawInput) {
|
||||
self.has_requested_repaint_this_frame = false; // allow new calls during the frame
|
||||
|
||||
if let Some(new_pixels_per_point) = self.memory.new_pixels_per_point.take() {
|
||||
new_raw_input.pixels_per_point = Some(new_pixels_per_point);
|
||||
|
||||
// This is a bit hacky, but is required to avoid jitter:
|
||||
let ratio = self.input.pixels_per_point / new_pixels_per_point;
|
||||
let mut rect = self.input.screen_rect;
|
||||
rect.min = (ratio * rect.min.to_vec2()).to_pos2();
|
||||
rect.max = (ratio * rect.max.to_vec2()).to_pos2();
|
||||
new_raw_input.screen_rect = Some(rect);
|
||||
}
|
||||
|
||||
self.memory.begin_frame(&self.input, &new_raw_input);
|
||||
|
||||
self.input = std::mem::take(&mut self.input)
|
||||
.begin_frame(new_raw_input, self.requested_repaint_last_frame);
|
||||
|
||||
if let Some(new_pixels_per_point) = self.memory.new_pixels_per_point.take() {
|
||||
self.input.pixels_per_point = new_pixels_per_point;
|
||||
}
|
||||
|
||||
self.frame_state.begin_frame(&self.input);
|
||||
|
||||
self.update_fonts_mut();
|
||||
|
||||
@@ -13,10 +13,10 @@ use crate::emath::*;
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub struct RawInput {
|
||||
/// Position and size of the area that egui should use.
|
||||
/// Position and size of the area that egui should use, in points.
|
||||
/// Usually you would set this to
|
||||
///
|
||||
/// `Some(Rect::from_pos_size(Default::default(), screen_size))`.
|
||||
/// `Some(Rect::from_pos_size(Default::default(), screen_size_in_points))`.
|
||||
///
|
||||
/// but you could also constrain egui to some smaller portion of your window if you like.
|
||||
///
|
||||
@@ -516,13 +516,13 @@ impl ModifierNames<'static> {
|
||||
concat: "",
|
||||
};
|
||||
|
||||
/// Alt, Ctrl, Shift, Command
|
||||
/// Alt, Ctrl, Shift, Cmd
|
||||
pub const NAMES: Self = Self {
|
||||
is_short: false,
|
||||
alt: "Alt",
|
||||
ctrl: "Ctrl",
|
||||
shift: "Shift",
|
||||
mac_cmd: "Command",
|
||||
mac_cmd: "Cmd",
|
||||
concat: "+",
|
||||
};
|
||||
}
|
||||
@@ -585,6 +585,11 @@ pub enum Key {
|
||||
PageUp,
|
||||
PageDown,
|
||||
|
||||
/// The virtual keycode for the Minus key.
|
||||
Minus,
|
||||
/// The virtual keycode for the Plus/Equals key.
|
||||
PlusEquals,
|
||||
|
||||
/// Either from the main row or from the numpad.
|
||||
Num0,
|
||||
/// Either from the main row or from the numpad.
|
||||
@@ -667,6 +672,8 @@ impl Key {
|
||||
Key::ArrowLeft => "⏴",
|
||||
Key::ArrowRight => "⏵",
|
||||
Key::ArrowUp => "⏶",
|
||||
Key::Minus => "-",
|
||||
Key::PlusEquals => "+",
|
||||
_ => self.name(),
|
||||
}
|
||||
}
|
||||
@@ -689,6 +696,8 @@ impl Key {
|
||||
Key::End => "End",
|
||||
Key::PageUp => "PageUp",
|
||||
Key::PageDown => "PageDown",
|
||||
Key::Minus => "Minus",
|
||||
Key::PlusEquals => "Plus",
|
||||
Key::Num0 => "0",
|
||||
Key::Num1 => "1",
|
||||
Key::Num2 => "2",
|
||||
|
||||
117
crates/egui/src/gui_zoom.rs
Normal file
117
crates/egui/src/gui_zoom.rs
Normal file
@@ -0,0 +1,117 @@
|
||||
//! Helpers for zooming the whole GUI of an app (changing [`Context::pixels_per_point`].
|
||||
//!
|
||||
use crate::*;
|
||||
|
||||
/// The suggested keyboard shortcuts for global gui zooming.
|
||||
pub mod kb_shortcuts {
|
||||
use super::*;
|
||||
|
||||
pub const ZOOM_IN: KeyboardShortcut =
|
||||
KeyboardShortcut::new(Modifiers::COMMAND, Key::PlusEquals);
|
||||
pub const ZOOM_OUT: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::Minus);
|
||||
pub const ZOOM_RESET: KeyboardShortcut = KeyboardShortcut::new(Modifiers::COMMAND, Key::Num0);
|
||||
}
|
||||
|
||||
/// Let the user scale the GUI (change `Context::pixels_per_point`) by pressing
|
||||
/// Cmd+Plus, Cmd+Minus or Cmd+0, just like in a browser.
|
||||
///
|
||||
/// When using [`eframe`](https://github.com/emilk/egui/tree/master/crates/eframe), you want to call this as:
|
||||
/// ```ignore
|
||||
/// // On web, the browser controls the gui zoom.
|
||||
/// if !frame.is_web() {
|
||||
/// egui::gui_zoom::zoom_with_keyboard_shortcuts(
|
||||
/// ctx,
|
||||
/// frame.info().native_pixels_per_point,
|
||||
/// );
|
||||
/// }
|
||||
/// ```
|
||||
pub fn zoom_with_keyboard_shortcuts(ctx: &Context, native_pixels_per_point: Option<f32>) {
|
||||
if ctx.input_mut().consume_shortcut(&kb_shortcuts::ZOOM_RESET) {
|
||||
if let Some(native_pixels_per_point) = native_pixels_per_point {
|
||||
ctx.set_pixels_per_point(native_pixels_per_point);
|
||||
}
|
||||
} else {
|
||||
if ctx.input_mut().consume_shortcut(&kb_shortcuts::ZOOM_IN) {
|
||||
zoom_in(ctx);
|
||||
}
|
||||
if ctx.input_mut().consume_shortcut(&kb_shortcuts::ZOOM_OUT) {
|
||||
zoom_out(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const MIN_PIXELS_PER_POINT: f32 = 0.2;
|
||||
const MAX_PIXELS_PER_POINT: f32 = 4.0;
|
||||
|
||||
/// Make everything larger.
|
||||
pub fn zoom_in(ctx: &Context) {
|
||||
let mut pixels_per_point = ctx.pixels_per_point();
|
||||
pixels_per_point += 0.1;
|
||||
pixels_per_point = pixels_per_point.clamp(MIN_PIXELS_PER_POINT, MAX_PIXELS_PER_POINT);
|
||||
pixels_per_point = (pixels_per_point * 10.).round() / 10.;
|
||||
ctx.set_pixels_per_point(pixels_per_point);
|
||||
}
|
||||
|
||||
/// Make everything smaller.
|
||||
pub fn zoom_out(ctx: &Context) {
|
||||
let mut pixels_per_point = ctx.pixels_per_point();
|
||||
pixels_per_point -= 0.1;
|
||||
pixels_per_point = pixels_per_point.clamp(MIN_PIXELS_PER_POINT, MAX_PIXELS_PER_POINT);
|
||||
pixels_per_point = (pixels_per_point * 10.).round() / 10.;
|
||||
ctx.set_pixels_per_point(pixels_per_point);
|
||||
}
|
||||
|
||||
/// Show buttons for zooming the ui.
|
||||
///
|
||||
/// This is meant to be called from within a menu (See [`Ui::menu_button`]).
|
||||
///
|
||||
/// When using [`eframe`](https://github.com/emilk/egui/tree/master/crates/eframe), you want to call this as:
|
||||
/// ```ignore
|
||||
/// // On web, the browser controls the gui zoom.
|
||||
/// if !frame.is_web() {
|
||||
/// ui.menu_button("View", |ui| {
|
||||
/// egui::gui_zoom::zoom_menu_buttons(
|
||||
/// ui,
|
||||
/// frame.info().native_pixels_per_point,
|
||||
/// );
|
||||
/// });
|
||||
/// }
|
||||
/// ```
|
||||
pub fn zoom_menu_buttons(ui: &mut Ui, native_pixels_per_point: Option<f32>) {
|
||||
if ui
|
||||
.add_enabled(
|
||||
ui.ctx().pixels_per_point() < MAX_PIXELS_PER_POINT,
|
||||
Button::new("Zoom In").shortcut_text(ui.ctx().format_shortcut(&kb_shortcuts::ZOOM_IN)),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
zoom_in(ui.ctx());
|
||||
ui.close_menu();
|
||||
}
|
||||
|
||||
if ui
|
||||
.add_enabled(
|
||||
ui.ctx().pixels_per_point() > MIN_PIXELS_PER_POINT,
|
||||
Button::new("Zoom Out")
|
||||
.shortcut_text(ui.ctx().format_shortcut(&kb_shortcuts::ZOOM_OUT)),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
zoom_out(ui.ctx());
|
||||
ui.close_menu();
|
||||
}
|
||||
|
||||
if let Some(native_pixels_per_point) = native_pixels_per_point {
|
||||
if ui
|
||||
.add_enabled(
|
||||
ui.ctx().pixels_per_point() != native_pixels_per_point,
|
||||
Button::new("Reset Zoom")
|
||||
.shortcut_text(ui.ctx().format_shortcut(&kb_shortcuts::ZOOM_RESET)),
|
||||
)
|
||||
.clicked()
|
||||
{
|
||||
ui.ctx().set_pixels_per_point(native_pixels_per_point);
|
||||
ui.close_menu();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,6 +305,7 @@ mod context;
|
||||
mod data;
|
||||
mod frame_state;
|
||||
pub(crate) mod grid;
|
||||
pub mod gui_zoom;
|
||||
mod id;
|
||||
mod input_state;
|
||||
pub mod introspection;
|
||||
|
||||
@@ -65,6 +65,11 @@ impl OperatingSystem {
|
||||
{
|
||||
Self::Nix
|
||||
} else {
|
||||
#[cfg(feature = "tracing")]
|
||||
tracing::warn!(
|
||||
"egui: Failed to guess operating system from User-Agent {:?}. Please file an issue at https://github.com/emilk/egui/issues",
|
||||
user_agent);
|
||||
|
||||
Self::Unknown
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user