Place: Escape drops the current loaded image and loads the next (place.skip)

This commit is contained in:
aiongg committed 2026-11-14 05:54:59 +00:00
1 parent 7f0c2818f2
commit 91f78b2db9
4 files changed
+48 -3

No files matched your search

+33
View File
@@ -425,6 +425,13 @@ fn advance_place_cursor(s: &mut Session) {
}
}
/// Drop the current loaded image without placing it and move to the next; the last one ends placing.
pub(super) fn place_skip(s: &mut Session, _p: &Value) -> Result<Value> {
validate_place_cursor(s, "place.skip")?;
advance_place_cursor(s);
Ok(json!({"remaining": loaded_count(s)}))
}
/// Validate bounded grid dimensions and a finite nonnegative gutter.
fn grid_dimensions(p: &Value, command: &str, default_columns: usize, default_rows: usize) -> Result<(usize, usize, f64)> {
let dimension = |key, default| -> Result<usize> {
@@ -651,6 +658,32 @@ pub(super) mod image_place_tests {
}
}
fn test_png(shade: u8) -> String {
let png = designcraft_render::Rendered { width: 4, height: 4, pixels: vec![shade; 4 * 4 * 4] }.to_png();
crate::cmd::base64_encode(&png)
}
fn loaded_names(s: &Session) -> Vec<String> {
let d = &s.doc().unwrap().doc;
s.loaded.iter().chain(s.loaded_queue.iter()).map(|g| d.assets[&g.asset].name.clone()).collect()
}
#[test]
fn skip_drops_the_current_image_and_the_last_one_ends_placing() {
let mut s = Session::new();
s.execute("file.new", &json!({})).unwrap();
let files: Vec<_> = (0..3).map(|i| json!({"base64": test_png(i as u8), "name": format!("{i}.png")})).collect();
s.execute("place.load", &json!({"files": files})).unwrap();
assert_eq!(s.execute("place.skip", &json!({})).unwrap()["remaining"], 2);
assert_eq!(loaded_names(&s), ["1.png", "2.png"]);
s.execute("place.skip", &json!({})).unwrap();
assert_eq!(s.tool_id(), "placeGun");
s.execute("place.skip", &json!({})).unwrap();
assert!(s.loaded.is_none());
assert_eq!(s.tool_id(), "selection");
assert!(s.execute("place.skip", &json!({})).is_err());
}
#[test]
fn consecutive_path_placements_create_distinct_graphics() {
let images = [image_file_with_color(30).unwrap(), image_file_with_color(31).unwrap(), image_file_with_color(32).unwrap()];
+3 -2
View File
@@ -3,7 +3,7 @@ use super::EngineError;
use super::bytes::{base64_encode, to_bytes, to_bytes_with_preview, with_document_extension};
use super::new::{file_new, file_sample, save_preset};
use super::open::{file_open, file_open_bytes, file_open_recent, has_recent, recent_entry};
use super::place::{file_place, place_contact_sheet, place_drop, place_grid, place_load, read_source};
use super::place::{file_place, place_contact_sheet, place_drop, place_grid, place_load, place_skip, read_source};
use super::save::{FolderAccess, can_revert, file_revert, file_save, recovery_dir};
use super::snippets::{snippet_export, snippet_place};
use super::{CommandSpec, PRESETS, Value, always, bad, cmd, has_doc, json, ok, str_param};
@@ -160,7 +160,8 @@ pub fn specs() -> Vec<CommandSpec> {
has_doc,
place_contact_sheet
),
cmd!(noundo "place.cancel", "Cancel Image Placement", [], None, "{}", has_doc, |s, _| {
cmd!(noundo "place.skip", "Skip Loaded Image", [], None, "{} — drops the current loaded image without placing it and loads the next; the last one ends placing", has_doc, place_skip),
cmd!(noundo "place.cancel", "Cancel Image Placement", [], None, "{} — clears every loaded image", has_doc, |s, _| {
s.loaded = None;
s.loaded_queue.clear();
s.set_tool("selection");
+2 -1
View File
@@ -173,9 +173,10 @@ impl Tool for PlaceGun {
}
fn key(&mut self, _cx: &ToolContext, key: crate::ToolKey, _mods: Mods) -> Vec<Action> {
use crate::ToolKey;
// Escape drops the current image and loads the next; `place.cancel` clears them all.
if key == ToolKey::Escape {
self.start = None;
return vec![Action::Exec("place.cancel".into(), json!({}))];
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
// at its limit, so they don't go on to nudge the selection.
+10
View File
@@ -2527,6 +2527,16 @@ fn place_gun_grid_arrow_keys_are_handled_during_a_drag() {
assert!(t.key(&cx, ToolKey::Delete, Mods::default()).is_empty());
}
/// Escape drops only the current loaded image; `place.cancel` clears them all.
#[test]
fn place_gun_escape_skips_the_current_image() {
let d = Document::new(&NewDocument::default());
let (s, c, l) = (Selection::default(), Cache::new(), CanvasLayout::new(&d, false));
let cx = ctx(&d, &s, &c, &l);
let mut t = create("placeGun");
assert_eq!(t.key(&cx, ToolKey::Escape, Mods::default()), vec![Action::Exec("place.skip".into(), serde_json::json!({}))]);
}
/// A place-cursor grid drag too thin for its cells shows and places the single frame an ordinary
/// drag would; one with room shows its cells and places the grid.
#[test]