From d792557bbe14588e79f61dc3d2698f663bba9464 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Fri, 30 Oct 2026 11:53:07 -0700 Subject: [PATCH] View: Escape during a canvas pan or zoom drag keeps full screen A Hand or Space drag, a middle-button pan, a Zoom tool drag, a Rotate View drag or a touch gesture is held in the canvas's drag state, which the tool busy check does not see. Escape pressed while one is held no longer leaves Full Screen Mode, closes the Edit Toolbar drawer or ends another action; an Escape after the release does. --- crates/ui-egui/src/canvas.rs | 5 +++ crates/ui-egui/src/shortcuts.rs | 7 ++-- crates/ui-egui/src/tests/screenmode.rs | 48 ++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/crates/ui-egui/src/canvas.rs b/crates/ui-egui/src/canvas.rs index 58815f2e7..cd2a142be 100644 --- a/crates/ui-egui/src/canvas.rs +++ b/crates/ui-egui/src/canvas.rs @@ -164,6 +164,11 @@ enum Drag { Present, } +/// Whether the canvas is holding a pointer drag across frames. +pub(crate) fn dragging(ctx: &egui::Context) -> bool { + ctx.data(|d| d.get_temp::(drag_id()).is_some()) +} + /// Paint the document canvas, complete or cached artwork, navigation and tool overlays. pub fn show(app: &mut VectorcraftApp, ui: &mut Ui) { let t = Tokens::get(ui.ctx()); diff --git a/crates/ui-egui/src/shortcuts.rs b/crates/ui-egui/src/shortcuts.rs index d8a238ed1..9f3a70515 100644 --- a/crates/ui-egui/src/shortcuts.rs +++ b/crates/ui-egui/src/shortcuts.rs @@ -205,10 +205,11 @@ pub fn handle(app: &mut VectorcraftApp, ctx: &egui::Context) { presentation_keys(app, ctx, escape_pair); return; } - let escape_ended_gesture = crate::canvas::ruler_origin_dragging(ctx) || crate::tools_float::escape_cancelled_move(ctx); + let escape_held_by_drag = + crate::canvas::dragging(ctx) || crate::canvas::ruler_origin_dragging(ctx) || crate::tools_float::escape_cancelled_move(ctx); let typing = ctx.egui_wants_keyboard_input(); // Escape closes Edit Toolbar's drawer first (egui drops a drag from it on Escape already). - if app.ui.tools_drawer && !typing && !escape_ended_gesture && ctx.input_mut(|i| i.consume_key(Modifiers::NONE, Key::Escape)) { + if app.ui.tools_drawer && !typing && !escape_held_by_drag && ctx.input_mut(|i| i.consume_key(Modifiers::NONE, Key::Escape)) { app.ui.tools_drawer = false; } let view = app.view_info(); @@ -249,7 +250,7 @@ pub fn handle(app: &mut VectorcraftApp, ctx: &egui::Context) { ctx.input_mut(|i| i.consume_key(Modifiers::NONE, k)); ctx.input_mut(|i| i.consume_key(Modifiers::SHIFT, k)); } - if k == Key::Escape && !busy && !claimed && !escape_ended_gesture { + if k == Key::Escape && !busy && !claimed && !escape_held_by_drag { if app.session.active().is_some_and(|d| d.doc.pattern_edit.is_some()) { let _ = app.run("object.pattern.done", json!({})); } else if app.session.active().is_some_and(|d| d.isolation.is_some()) { diff --git a/crates/ui-egui/src/tests/screenmode.rs b/crates/ui-egui/src/tests/screenmode.rs index 3d6cc8dd6..783577ed4 100644 --- a/crates/ui-egui/src/tests/screenmode.rs +++ b/crates/ui-egui/src/tests/screenmode.rs @@ -223,6 +223,54 @@ fn a_tool_drag_takes_escape_before_full_screen_exits() { assert_eq!(app.ui.screen_mode, 0); } +fn navigation_drag_keeps_screen_mode(kind: &str) { + for mode in [1, 2] { + let ctx = egui::Context::default(); + let mut app = in_screen_mode(&ctx, mode); + let button = match kind { + "hand" => { + app.select_tool("hand"); + egui::PointerButton::Primary + } + "middle" => egui::PointerButton::Middle, + "zoom_box" => { + app.select_tool("zoom"); + app.session.prefs.animated_zoom = false; + egui::PointerButton::Primary + } + _ => panic!("unknown navigation drag"), + }; + frame(&mut app, &ctx, vec![]); + let start = app.canvas_rect.unwrap().center(); + let end = start + egui::vec2(35.0, 20.0); + let pointer = |pos, pressed| egui::Event::PointerButton { pos, button, pressed, modifiers: Default::default() }; + frame(&mut app, &ctx, vec![egui::Event::PointerMoved(start), pointer(start, true)]); + frame(&mut app, &ctx, vec![egui::Event::PointerMoved(end)]); + assert!(crate::canvas::dragging(&ctx), "{kind} drag began in mode {mode}"); + frame(&mut app, &ctx, escape()); + assert_eq!(app.ui.screen_mode, mode, "{kind} drag holds Escape in mode {mode}"); + frame(&mut app, &ctx, vec![pointer(end, false)]); + assert!(!crate::canvas::dragging(&ctx), "{kind} drag ended in mode {mode}"); + frame(&mut app, &ctx, escape()); + assert_eq!(app.ui.screen_mode, 0, "Escape after {kind} drag in mode {mode}"); + } +} + +#[test] +fn a_hand_drag_keeps_both_full_screen_modes_until_release() { + navigation_drag_keeps_screen_mode("hand"); +} + +#[test] +fn a_middle_button_pan_keeps_both_full_screen_modes_until_release() { + navigation_drag_keeps_screen_mode("middle"); +} + +#[test] +fn a_zoom_box_drag_keeps_both_full_screen_modes_until_release() { + navigation_drag_keeps_screen_mode("zoom_box"); +} + #[test] fn a_ruler_origin_drag_takes_escape_before_full_screen_exits() { for mode in [1, 2] {