diff --git a/crates/tools/src/nav.rs b/crates/tools/src/nav.rs index 6af9761df..6fadf8bbf 100644 --- a/crates/tools/src/nav.rs +++ b/crates/tools/src/nav.rs @@ -173,9 +173,13 @@ impl Tool for PlaceGun { } fn key(&mut self, cx: &ToolContext, key: crate::ToolKey, _mods: Mods) -> Vec { use crate::ToolKey; - // Escape drops the current image and loads the next; `place.cancel` clears them all. + // Escape drops the current image and loads the next; `place.cancel` clears them all. With + // nothing loaded there is nothing to drop: Escape only leaves the tool. if key == ToolKey::Escape { self.start = None; + if cx.loaded.is_none() { + return vec![Action::SwitchTool("selection".into())]; + } return vec![Action::Exec("place.skip".into(), json!({}))]; } // During a drag the arrow keys change the grid, and are handled even when it is already diff --git a/crates/tools/src/tests.rs b/crates/tools/src/tests.rs index 1e898cb26..acbef71fa 100644 --- a/crates/tools/src/tests.rs +++ b/crates/tools/src/tests.rs @@ -2544,6 +2544,9 @@ fn place_gun_escape_skips_and_arrows_cycle_between_drags() { let _ = t.pointer(&cx, &PointerEvent::new(PointerKind::Down, 10.0, 10.0)); let _ = t.pointer(&cx, &PointerEvent::new(PointerKind::Drag, 110.0, 60.0)); assert_eq!(t.key(&cx, ToolKey::Right, Mods::default()), vec![Action::Handled]); + // Nothing loaded: Escape leaves the tool instead of asking to skip an image there isn't. + cx.loaded = None; + assert_eq!(t.key(&cx, ToolKey::Escape, Mods::default()), vec![Action::SwitchTool("selection".into())]); } /// A place-cursor grid drag too thin for its cells shows and places the single frame an ordinary