diff --git a/contributors/people.toml b/contributors/people.toml index 23ff0917..6dbb3c6a 100644 --- a/contributors/people.toml +++ b/contributors/people.toml @@ -32,6 +32,9 @@ display_name = "insanityxoff" [people.moppeeer] sync_github_name = true +[people.PiMinusMax] +display_name = "PiMinusMax" + [people.thaynej] real_name = "Jeffrey Thayne" diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index c56f5b30..f15e2b4d 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -924,6 +924,9 @@ impl DesignApp { ctx.set_zoom_factor(scale); } if !self.styled { + // The UI scale is a preference (applied above); egui's own Cmd+Plus/Minus/0 would rescale the + // whole window instead of the canvas, only to be reset next frame. + ctx.options_mut(|o| o.zoom_with_keyboard = false); self.fonts_system = theme::install_fonts(ctx, &self.ui.language); self.fonts_lang = self.ui.language.clone(); self.styled = true; diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index fa1cfe5a..ec0b7e5d 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -2344,10 +2344,12 @@ pub fn shortcuts(app: &mut DesignApp, ctx: &egui::Context) { let all = effective_shortcuts(app); for (id, sc) in &all { let id = id.as_str(); + // On layouts with a dedicated + key (German) or Shift+= (US), Cmd+= arrives as Plus. + let plus_for_equals = key == egui::Key::Plus; if let Some((m, k)) = parse_shortcut(sc) - && k == key + && (k == key || (plus_for_equals && k == egui::Key::Equals)) && m.command == modifiers.command - && m.shift == modifiers.shift + && (m.shift == modifiers.shift || (plus_for_equals && !m.shift)) && m.alt == modifiers.alt && (m.command || m.alt || !typing) && !(id == "edit.clear" && typing) @@ -3322,6 +3324,49 @@ mod tests { assert_eq!(app.session.tool_id(), "rectangleFrame"); } + #[test] + fn cmd_plus_zooms_the_canvas_not_the_window() { + // A German keyboard has its own + key, so Cmd+= arrives as Plus; it must zoom the canvas like + // Cmd+- does, not hand the key to egui's whole-window zoom. + let mut app = crate::DesignApp::new(designcraft_engine::Session::new(), crate::Services::default()); + app.session.execute("file.new", &json!({})).unwrap(); + let ctx = egui::Context::default(); + let frame = |app: &mut crate::DesignApp, events: Vec| { + let input = egui::RawInput { + screen_rect: Some(egui::Rect::from_min_size(egui::Pos2::ZERO, egui::vec2(1400.0, 900.0))), + events, + ..Default::default() + }; + let mut out = ctx.run_ui(input, |ui| { + app.logic(&ui.ctx().clone()); + app.ui(ui); + }); + out.textures_delta.clear(); + }; + let key = |k: egui::Key, modifiers: egui::Modifiers| { + vec![ + egui::Event::Key { key: k, physical_key: None, pressed: true, repeat: false, modifiers }, + egui::Event::Key { key: k, physical_key: None, pressed: false, repeat: false, modifiers }, + ] + }; + frame(&mut app, vec![]); + frame(&mut app, vec![]); + let scale = ctx.zoom_factor(); + for (k, m, zooms_in) in [ + (egui::Key::Plus, egui::Modifiers::COMMAND, true), + (egui::Key::Plus, egui::Modifiers::COMMAND | egui::Modifiers::SHIFT, true), + (egui::Key::Equals, egui::Modifiers::COMMAND, true), + (egui::Key::Minus, egui::Modifiers::COMMAND, false), + ] { + let z0 = app.view().unwrap().zoom; + frame(&mut app, key(k, m)); + frame(&mut app, vec![]); + let z = app.view().unwrap().zoom; + assert!(if zooms_in { z > z0 } else { z < z0 }, "{k:?} {m:?}: {z0} -> {z}"); + assert_eq!(ctx.zoom_factor(), scale, "{k:?} {m:?} leaves the UI scale alone"); + } + } + #[test] fn the_quick_apply_shortcut_opens_it_without_running_an_entry() { let mut app = crate::DesignApp::new(designcraft_engine::Session::new(), crate::Services::default());