Engine: NEW and OPEN end a command left running in the previous drawing (fixes #715) (#780)

A command started in one drawing kept running after a programmatic NEW or OPEN made another
drawing current. Finishing it there recorded an undo step holding the first drawing, so undo in
the new drawing brought the other drawing back. Session::new_drawing and open_drawing now end a
running command before the new drawing becomes current, as CLOSE and document.switch already do
and as starting NEW from the command line did: its undo step lands in the drawing it started in.
With no command running, nothing changes (the previous drawing keeps its selection).
This commit is contained in:
Marco Arnulfo authored and GitHub committed 2026-10-13 04:35:26 -04:00
1 parent 565cadaa51
commit 6ab800e64c
2 files changed
+91

No files matched your search

+9
View File
@@ -660,6 +660,7 @@ impl Session {
let d = if metric { Drawing::new_metric() } else { Drawing::new_imperial() };
let mut st = DocState::new(d, &format!("Drawing{}", self.untitled_counter), None);
st.set_view_quiet(if metric { View { center: Vec2::new(210.0, 148.5), height: 297.0 } } else { View::default() });
self.end_running();
self.docs.push(st);
self.active = self.docs.len() - 1;
self.active
@@ -673,10 +674,18 @@ impl Session {
height: (ext.height().max(ext.width() * self.viewport_px.1 / self.viewport_px.0.max(1.0)) * 1.1).max(1e-6),
});
}
self.end_running();
self.docs.push(st);
self.active = self.docs.len() - 1;
self.active
}
/// A command still waiting for input belongs to the drawing it started in: end it there
/// before another drawing becomes current, so its undo step lands in that drawing.
fn end_running(&mut self) {
if self.running.is_some() {
self.cancel();
}
}
pub fn state(&self) -> Result<&DocState> {
self.docs.get(self.active).ok_or(EngineError::NoDocument)
@@ -0,0 +1,82 @@
//! NEW and OPEN end a command left waiting for input (#715): it belongs to the drawing it started
//! in, so its changes are undone there, and undo in the new drawing never brings back the other.
use cadcraft_engine::Session;
use cadcraft_engine::cmd::file::{IoHooks, set_io};
use cadcraft_engine::doc::Drawing;
use serde_json::json;
fn count(s: &Session) -> usize {
s.doc().unwrap().entity_count()
}
/// LINE with one segment drawn, still asking for the next point.
fn line_running(s: &mut Session) {
s.cmdline("line").unwrap();
s.cmdline("0,0").unwrap();
s.cmdline("1,1").unwrap();
assert!(s.running.is_some());
assert_eq!(count(s), 1);
}
/// Draw a line in the current drawing, undo it and check what is left.
fn line_then_undo(s: &mut Session, title: &str, left: usize) {
s.cmdline("line").unwrap();
s.cmdline("0,0").unwrap();
s.cmdline("5,5").unwrap();
s.cmdline("").unwrap();
assert_eq!(count(s), left + 1);
s.undo().unwrap();
assert_eq!(s.state().unwrap().title, title);
assert_eq!(count(s), left, "undo restores this drawing, not the other one");
}
/// The first drawing kept its segment, and its own undo removes it.
fn first_drawing_keeps_its_line(s: &mut Session) {
s.execute("document.switch", &json!({"index": 0})).unwrap();
assert_eq!(count(s), 1);
s.undo().unwrap();
assert_eq!(count(s), 0);
}
#[test]
fn new_ends_the_command_of_the_previous_drawing() {
let mut s = Session::new();
line_running(&mut s);
s.execute("new", &json!({"metric": true})).unwrap();
assert!(s.running.is_none(), "LINE ends with its drawing");
assert_eq!(count(&s), 0);
line_then_undo(&mut s, "Drawing2", 0);
first_drawing_keeps_its_line(&mut s);
}
fn two_circles(_: &[u8], _: &str) -> Result<Drawing, String> {
let mut s = Session::new();
for x in [0, 10] {
s.execute("circle", &json!({"center": [x, 0], "radius": 1})).unwrap();
}
Ok(s.doc().unwrap().clone())
}
#[test]
fn open_ends_the_command_of_the_previous_drawing() {
set_io(IoHooks { read: two_circles, write: |_, _| Err("no writer in tests".into()), plot: None });
let mut s = Session::new();
line_running(&mut s);
s.execute("open", &json!({"data": "AA==", "name": "Circles.dxf"})).unwrap();
assert!(s.running.is_none(), "LINE ends with its drawing");
assert_eq!(count(&s), 2);
line_then_undo(&mut s, "Circles.dxf", 2);
first_drawing_keeps_its_line(&mut s);
}
#[test]
fn new_without_a_command_keeps_the_selection_of_the_previous_drawing() {
let mut s = Session::new();
s.execute("circle", &json!({"center": [0, 0], "radius": 1})).unwrap();
s.execute("selectall", &json!({})).unwrap();
let selected = s.selection();
assert_eq!(selected.len(), 1);
s.execute("new", &json!({"metric": true})).unwrap();
assert_eq!(s.docs[0].selection, selected);
}