mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-07 23:00:35 +00:00
Place: the place cursor reads its files within the roots it was loaded in
An agent confined to automation roots could load the place cursor, with file.place.queue or tool.setOption, and the files were placed later, by a click of the person at the keyboard, outside any roots. tool.setOption checked a queued path but not files given inline, whose linked files (an SVG's referenced images) were then read with the keyboard user's unrestricted access. The session now keeps the automation roots in force when the cursor's queue is loaded, and both the engine's own placement and the desktop app's background placement read the queued file within them. A queue loaded outside any roots places as before.
This commit is contained in:
committed by
bflatastic
parent
a4aa1960cf
commit
2fec613353
@@ -163,6 +163,11 @@ pub struct Session {
|
||||
/// While [`Session::place_queue_read`] runs `file.place.queue`: the files read for it, which
|
||||
/// the command loads the place cursor with instead of reading them; `None` otherwise.
|
||||
pub(crate) queue_hand_off: Option<cmd::place::QueueRead>,
|
||||
/// The automation roots in force when the place cursor was last loaded (`None`: none were).
|
||||
/// The cursor places its files later, on a click that may come from the person at the
|
||||
/// keyboard: those files, and the files they link to, are read under these roots, so a queue
|
||||
/// an agent loaded never reads beyond what it was given.
|
||||
pub(crate) place_queue_roots: Option<std::sync::Arc<file_access::AutomationRoots>>,
|
||||
/// While [`Session::import_image_read`] runs `clipboard.importImage`: the image read for it,
|
||||
/// which the command loads instead of reading `dataBase64`; `None` otherwise.
|
||||
pub(crate) image_hand_off: Option<cmd::clipboard::ImageRead>,
|
||||
|
||||
@@ -79,6 +79,7 @@ impl Session {
|
||||
batch_steps: None,
|
||||
place_hand_off: None,
|
||||
queue_hand_off: None,
|
||||
place_queue_roots: None,
|
||||
image_hand_off: None,
|
||||
trace_hand_off: None,
|
||||
place_cursor_requests: false,
|
||||
|
||||
@@ -722,6 +722,39 @@ fn a_queue_of_3_with_click_drag_and_esc_places_2() {
|
||||
assert_eq!(s.doc().unwrap().history.undo.len(), 2, "one undo step per placement");
|
||||
}
|
||||
|
||||
/// A place cursor loaded within automation roots places its files within them, even on a later
|
||||
/// click outside any roots (the person at the keyboard's): a queued file outside the roots, or
|
||||
/// one it links to, is not read then. One loaded outside any roots places as before.
|
||||
#[test]
|
||||
fn the_place_cursor_places_within_the_roots_it_was_loaded_in() {
|
||||
let base = std::env::temp_dir().join(format!("vectorcraft-place-roots-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&base);
|
||||
std::fs::create_dir_all(base.join("in")).unwrap();
|
||||
let outside = base.join("outside.png");
|
||||
std::fs::write(&outside, png(40, 20, [9, 9, 9, 255], None)).unwrap();
|
||||
let entry = json!({"params": {"path": outside.to_string_lossy()}, "name": "outside.png", "width": 40, "height": 20});
|
||||
let roots = crate::file_access::AutomationRoots::new(&[&base.join("in")], &[]).unwrap().unwrap();
|
||||
let view = ViewInfo::default();
|
||||
let click = |s: &mut Session| {
|
||||
s.pointer(&PointerEvent::new(PointerKind::Down, 10.0, 10.0), view)?;
|
||||
s.pointer(&PointerEvent::new(PointerKind::Up, 10.0, 10.0), view)
|
||||
};
|
||||
let mut s = session();
|
||||
s.select_tool("place", view).unwrap();
|
||||
// Loaded within the roots (as a tool option, past the check tool.setOption makes of paths).
|
||||
crate::file_access::confine(Some(&roots), || s.set_tool_option("queue", &json!({"entries": [entry]})));
|
||||
assert!(s.place_queue_roots().is_some());
|
||||
let placed = crate::file_access::unconfined(|| click(&mut s));
|
||||
assert!(top_children(&s).is_empty(), "{placed:?}");
|
||||
// Loaded outside any roots: the click places it.
|
||||
s.select_tool("place", view).unwrap();
|
||||
crate::file_access::unconfined(|| s.set_tool_option("queue", &json!({"entries": [entry]})));
|
||||
assert!(s.place_queue_roots().is_none());
|
||||
crate::file_access::unconfined(|| click(&mut s)).unwrap();
|
||||
assert_eq!(top_children(&s).len(), 1);
|
||||
let _ = std::fs::remove_dir_all(base);
|
||||
}
|
||||
|
||||
/// With [`Session::place_cursor_requests`] set, a click of the place cursor asks the frontend to
|
||||
/// place its file ([`UiRequest::Place`]) and places nothing itself; the params it asks with place
|
||||
/// the file as the click places it without the flag.
|
||||
|
||||
@@ -658,11 +658,27 @@ impl Session {
|
||||
self.tool.options()
|
||||
}
|
||||
pub fn set_tool_option(&mut self, key: &str, v: &Value) {
|
||||
if key == "queue" && self.tool.id() == "place" {
|
||||
self.note_place_queue_roots();
|
||||
}
|
||||
self.tool.set_option(key, v);
|
||||
self.tool_set_since_reset = true;
|
||||
self.keep_tool_settings();
|
||||
}
|
||||
|
||||
/// Keep the automation roots in force now as those the place cursor's queue, being loaded,
|
||||
/// is placed under ([`Session::place_queue_roots`]).
|
||||
fn note_place_queue_roots(&mut self) {
|
||||
self.place_queue_roots = crate::file_access::current();
|
||||
}
|
||||
|
||||
/// The automation roots the place cursor's files are placed under: those in force when it
|
||||
/// was loaded. A frontend that places them itself ([`UiRequest::Place`]) reads them within
|
||||
/// these roots ([`crate::file_access::confine`]).
|
||||
pub fn place_queue_roots(&self) -> Option<std::sync::Arc<crate::file_access::AutomationRoots>> {
|
||||
self.place_queue_roots.clone()
|
||||
}
|
||||
|
||||
/// The options of tool `id`: the active tool's, or those it would have if chosen now.
|
||||
pub fn tool_options_of(&self, id: &str) -> Value {
|
||||
if id == self.tool.id() { self.tool.options() } else { self.make_tool(id).options() }
|
||||
@@ -673,6 +689,9 @@ impl Session {
|
||||
/// Liquify tools' Global Brush Dimensions, the Smooth tool's Fidelity for Pencil, Paintbrush
|
||||
/// and Blob Brush Alt smoothing). Returns the tool's options.
|
||||
pub fn set_tool_options(&mut self, id: Option<&str>, values: &Map<String, Value>) -> Value {
|
||||
if values.contains_key("queue") && id.unwrap_or(self.tool.id()) == "place" {
|
||||
self.note_place_queue_roots();
|
||||
}
|
||||
let active = id.is_none_or(|id| id == self.tool.id());
|
||||
self.tool_set_since_reset |= active;
|
||||
let mut other = (!active).then(|| self.make_tool(id.unwrap_or_default()));
|
||||
@@ -842,6 +861,13 @@ impl Session {
|
||||
}
|
||||
// The place cursor's file, placed by the frontend that asked for it.
|
||||
Action::Exec(cmd, p) if cmd == "file.place" && self.place_cursor_requests => ui.push(UiRequest::Place(p)),
|
||||
// The place cursor's file, read within the roots of whoever loaded the cursor.
|
||||
Action::Exec(cmd, p) if cmd == "file.place" => {
|
||||
let roots = self.place_queue_roots.clone();
|
||||
if let Some(w) = crate::file_access::confine(roots.as_ref(), || self.execute(&cmd, &p))?.get("warning").and_then(Value::as_str) {
|
||||
ui.push(UiRequest::Status(w.to_string()));
|
||||
}
|
||||
}
|
||||
Action::Exec(cmd, p) => {
|
||||
// A command's warning (Crop Image's when the image couldn't be decoded) goes to the
|
||||
// status bar too.
|
||||
|
||||
@@ -376,13 +376,18 @@ fn queue_files(app: &mut VectorcraftApp, p: &Value, read: Read) -> Result<Value,
|
||||
/// read off the UI thread where the files the user places are ([`crate::placing::start`]), else at
|
||||
/// once. An error shows in the status bar.
|
||||
pub(crate) fn place_from_cursor(app: &mut VectorcraftApp, p: &Value) {
|
||||
if crate::placing::available(app) {
|
||||
if let Err(e) = crate::placing::start(app, p, false) {
|
||||
app.status(e);
|
||||
// Read within the automation roots of whoever loaded the cursor, even on a click of the person
|
||||
// at the keyboard: an agent's queue reaches no file it wasn't given.
|
||||
let roots = app.session.place_queue_roots();
|
||||
vectorcraft_engine::file_access::confine(roots.as_ref(), || {
|
||||
if crate::placing::available(app) {
|
||||
if let Err(e) = crate::placing::start(app, p, false) {
|
||||
app.status(e);
|
||||
}
|
||||
} else {
|
||||
let _ = engine(app, "file.place", p);
|
||||
}
|
||||
} else {
|
||||
let _ = engine(app, "file.place", p);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/// The cursor's thumbnails from `file.place.queue`'s reply `r`.
|
||||
|
||||
Reference in New Issue
Block a user