Merge pull request #1886 from marcoarnulfo/fix/text-edit-commit-before-transform

Type: a Transform panel edit or a dialog preview while typing keeps what was typed
This commit is contained in:
BFlat
2026-10-16 02:16:45 +07:00
committed by GitHub
2 changed files with 73 additions and 0 deletions
+15
View File
@@ -1108,6 +1108,8 @@ pub struct Session {
/// While the active tool's actions run: their commands aren't "a command from outside the
/// tool" ([`Session::after_command`]).
pub(crate) in_tool_actions: bool,
/// While [`Session::preview`] runs its command: its edits are the interaction's own.
previewing: bool,
/// Envelope Options with no envelope selected: the options and fidelity new envelopes get
/// (`None`: the reference app's defaults, fidelity 50); not saved.
pub(crate) envelope_defaults: Option<(vectorcraft_doc::live::EnvelopeOptions, f64)>,
@@ -1183,6 +1185,7 @@ impl Session {
batch_stash: None,
recovery: Default::default(),
in_tool_actions: false,
previewing: false,
envelope_defaults: None,
liquify_stroke: None,
plane_widget_press: false,
@@ -1479,6 +1482,11 @@ impl Session {
/// Run `f` on a mutable copy of the active document. Outside an interaction this records one
/// undo step labelled `label`; inside one it just mutates (the interaction commits later).
pub fn edit<T>(&mut self, label: &str, f: impl FnOnce(&mut Document, &mut Selection) -> Result<T>) -> Result<T> {
// An edit made while typing (the Transform panel) is a step of its own: inside the typing
// session, the next keystroke's preview would start again from before it (#1881).
if !self.previewing && cmd::typing_in_progress(self) {
cmd::settle_interaction(self)?;
}
let st = self.doc_mut()?;
let before = st.doc.clone();
let before_sel = st.selection.clone();
@@ -1579,6 +1587,11 @@ impl Session {
// ---------- interactions (live drags) ----------
pub fn begin_interaction(&mut self, label: &str) -> Result<()> {
// Typing in progress is kept first: a drag or a dialog's preview would otherwise start
// from the snapshot taken before the typing, and drop it (#1881).
if label != "Typing" && cmd::typing_in_progress(self) {
cmd::settle_interaction(self)?;
}
let st = self.doc_mut()?;
if st.interaction.is_some() {
return Ok(());
@@ -1618,7 +1631,9 @@ impl Session {
self.pending_paint = None;
let rw = cmd::distortcmds::perspective_rewrite(self, cmd, params);
let (cmd, params) = rw.as_ref().map_or((cmd, params), |(c, p)| (c.as_str(), p));
let outer = std::mem::replace(&mut self.previewing, true);
let r = self.execute(cmd, params);
self.previewing = outer;
let params = self.noted(params);
let st = self.doc_mut()?;
if let Some(it) = &mut st.interaction {
+58
View File
@@ -653,3 +653,61 @@ fn a_fill_the_selected_characters_have_records_no_undo_step() {
s.execute("paint.setStroke", &json!({"color": "#ff0000"})).unwrap();
assert_eq!(history(&s), before);
}
fn left(s: &Session, id: NodeId) -> f64 {
s.doc().unwrap().doc.bounds_of(&[id], false).unwrap().x0
}
/// #1881: the Transform panel moving or scaling the type while it is being typed keeps both the
/// typing and the transformation, however the editing goes on.
#[test]
fn a_transform_panel_edit_while_typing_keeps_the_typing_and_the_transform() {
let mut s = session();
let v = ViewInfo::default();
let id = text(&mut s, "Hello");
s.select_tool("type", v).unwrap();
let b = s.doc().unwrap().doc.bounds_of(&[id], false).unwrap();
click(&mut s, b.x1 - 1.0, b.center().y);
s.tool_text(" world", v).unwrap();
s.execute("object.setBounds", &json!({"x": 400, "reference": 0})).unwrap();
assert!((left(&s, id) - 400.0).abs() < 1e-6);
// Typing on.
s.tool_text("!", v).unwrap();
assert_eq!(obj(&s, id).plain_text(), "Hello world!");
assert!((left(&s, id) - 400.0).abs() < 1e-6, "the move stays: {}", left(&s, id));
s.execute("object.setBounds", &json!({"width": 300, "reference": 0, "proportional": true})).unwrap();
let w = |s: &Session| s.doc().unwrap().doc.bounds_of(&[id], false).unwrap().width();
assert!((w(&s) - 300.0).abs() < 1e-6);
key(&mut s, ToolKey::Escape, Mods::default());
assert_eq!(obj(&s, id).plain_text(), "Hello world!");
assert!((w(&s) - 300.0).abs() < 1e-6, "the scale stays: {}", w(&s));
assert!((left(&s, id) - 400.0).abs() < 1e-6);
// Each its own step, the typing first.
let labels = undo_labels(&s);
let n = labels.len();
assert_eq!(labels[n - 4], "Typing", "{labels:?}");
assert_eq!(labels[n - 2], "Typing", "{labels:?}");
}
/// #1881: a dialog previewing a transformation while the type is being typed (Object › Transform
/// with Preview) starts from the typed text, and keeps it when it commits.
#[test]
fn a_previewed_transform_while_typing_keeps_the_typing() {
let mut s = session();
let v = ViewInfo::default();
let id = text(&mut s, "Hello");
s.select_tool("type", v).unwrap();
let b = s.doc().unwrap().doc.bounds_of(&[id], false).unwrap();
click(&mut s, b.x1 - 1.0, b.center().y);
s.tool_text(" world", v).unwrap();
s.begin_interaction("Move").unwrap();
s.preview("object.setBounds", &json!({"x": 400, "reference": 0})).unwrap();
assert_eq!(obj(&s, id).plain_text(), "Hello world", "the preview starts from the typed text");
s.commit_interaction().unwrap();
assert_eq!(obj(&s, id).plain_text(), "Hello world");
assert!((left(&s, id) - 400.0).abs() < 1e-6);
// Typing on starts a new session from the moved text.
s.tool_text("!", v).unwrap();
assert_eq!(obj(&s, id).plain_text(), "Hello world!");
assert!((left(&s, id) - 400.0).abs() < 1e-6);
}