View: Escape that cancels a ruler or Tools move does not reach the tool

An Escape that cancels a ruler-origin drag or a floating Tools panel move
now stops there: the active tool does not receive it, so a tool's own
Escape action (for example Intertwine returning to the Selection tool)
waits for the next press. The ruler-origin test also covers the drawer
and Global Edit.
This commit is contained in:
dotCooCoo
2026-10-31 06:10:57 +07:00
committed by bflatastic
parent 501ae57fef
commit a072cf16be
2 changed files with 48 additions and 7 deletions
+15 -6
View File
@@ -205,8 +205,9 @@ pub fn handle(app: &mut VectorcraftApp, ctx: &egui::Context) {
presentation_keys(app, ctx, escape_pair);
return;
}
let escape_held_by_drag =
crate::canvas::dragging(ctx) || crate::canvas::ruler_origin_dragging(ctx) || crate::tools_float::escape_cancelled_move(ctx);
// Escape that cancels a ruler-origin drag or floating Tools move skips the tool and later Escape actions.
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) || escape_ended_gesture;
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_held_by_drag && ctx.input_mut(|i| i.consume_key(Modifiers::NONE, Key::Escape)) {
@@ -226,6 +227,10 @@ pub fn handle(app: &mut VectorcraftApp, ctx: &egui::Context) {
if (text || !busy) && ctx.input(|i| runs_a_command(i, k, text)) {
continue;
}
if k == Key::Escape && escape_ended_gesture {
remember_escape_pair(ctx, k, escape_pair);
continue;
}
// A key the tool claims (Esc with a loaded place cursor) is only the tool's.
let claimed = app.session.tool_claims_key(tk, view);
let mods = ctx.input(|i| {
@@ -276,10 +281,7 @@ pub fn handle(app: &mut VectorcraftApp, ctx: &egui::Context) {
}
}
// An Escape that ended something can start a pair too. One that closed a popup cannot.
if k == Key::Escape && escape_pair.is_some() && !egui::Popup::is_any_open(ctx) {
let now = ctx.input(|i| i.time);
ctx.data_mut(|d| d.insert_temp(escape_pair_id(), now));
}
remember_escape_pair(ctx, k, escape_pair);
if k == Key::Enter && !busy && !claimed {
// Enter with a selection tool opens the Move dialog, as a double-click on its button
// does.
@@ -344,6 +346,13 @@ pub fn handle(app: &mut VectorcraftApp, ctx: &egui::Context) {
single_key_shortcuts(app, ctx);
}
fn remember_escape_pair(ctx: &egui::Context, key: Key, escape_pair: Option<bool>) {
if key == Key::Escape && escape_pair.is_some() && !egui::Popup::is_any_open(ctx) {
let now = ctx.input(|i| i.time);
ctx.data_mut(|d| d.insert_temp(escape_pair_id(), now));
}
}
/// Dispatch unmodified text keys to command bindings or tools.
fn single_key_shortcuts(app: &mut VectorcraftApp, ctx: &egui::Context) {
// Single-key tool shortcuts (no Cmd/Ctrl/Alt). With a layout without Latin letters (Persian,
+33 -1
View File
@@ -365,10 +365,26 @@ fn cancelling_tools_move_keeps_the_next_escape_action() {
}
}
#[test]
fn cancelling_tools_move_keeps_the_tools_escape() {
for mode in [0, 1, 2] {
let ctx = egui::Context::default();
let mut app = in_screen_mode(&ctx, mode);
app.select_tool("intertwine");
let (original, to) = begin_tools_move(&mut app, &ctx);
frame(&mut app, &ctx, escape());
assert_eq!(app.ui.toolbar_pos, original, "move canceled in mode {mode}");
assert_eq!(app.session.tool_id(), "intertwine", "move Escape keeps the tool in mode {mode}");
release_drag(&mut app, &ctx, to);
frame(&mut app, &ctx, escape());
assert_eq!(app.session.tool_id(), "selection", "later Escape reaches the tool in mode {mode}");
}
}
#[test]
fn cancelling_ruler_origin_drag_keeps_the_next_escape_action() {
for mode in [0, 1, 2] {
for action in ["pattern", "isolation", "flyout"] {
for action in ["drawer", "pattern", "isolation", "global", "flyout"] {
let ctx = egui::Context::default();
let mut app = in_screen_mode(&ctx, mode);
let to = begin_ruler_origin_drag(&mut app, &ctx);
@@ -384,6 +400,22 @@ fn cancelling_ruler_origin_drag_keeps_the_next_escape_action() {
}
}
#[test]
fn cancelling_ruler_origin_drag_keeps_the_tools_escape() {
for mode in [0, 1, 2] {
let ctx = egui::Context::default();
let mut app = in_screen_mode(&ctx, mode);
app.select_tool("intertwine");
let to = begin_ruler_origin_drag(&mut app, &ctx);
frame(&mut app, &ctx, escape());
assert_eq!(app.session.tool_id(), "intertwine", "drag Escape keeps the tool in mode {mode}");
release_drag(&mut app, &ctx, to);
assert!(!crate::canvas::ruler_origin_dragging(&ctx));
frame(&mut app, &ctx, escape());
assert_eq!(app.session.tool_id(), "selection", "later Escape reaches the tool in mode {mode}");
}
}
#[test]
fn a_popup_takes_escape_before_full_screen_exits() {
let ctx = egui::Context::default();