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] {