From 863f7cbd879398770398cda3f732e0b95b4a2ead Mon Sep 17 00:00:00 2001 From: MarekSpodBiedry <73122968+MarekSpodBiedry@users.noreply.github.com> Date: Tue, 13 Oct 2026 17:34:22 +0200 Subject: [PATCH] Engine: moving the selection to another text body settles the composition (#421) --- crates/engine/src/lib.rs | 6 ++++++ crates/engine/src/tests_keys.rs | 35 +++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index c2b56c7f..981fcf16 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -509,9 +509,15 @@ impl Session { if let Some(t) = st.selection.text.as_mut() { t.line_end = None; } + let owner = |sel: &Selection| sel.text.as_ref().map(|t| (sel.slide, sel.target, t.shape, t.cell, t.notes)); + let before = owner(&st.selection); f(&doc, &mut st.selection); clamp_selection(st); st.revision += 1; + // A composition belongs to the text body it began in: leaving that body settles it. + if st.composition.is_some() && owner(&st.selection) != before { + self.end_composition(); + } Ok(()) } } diff --git a/crates/engine/src/tests_keys.rs b/crates/engine/src/tests_keys.rs index 926c4289..19a91ae8 100644 --- a/crates/engine/src/tests_keys.rs +++ b/crates/engine/src/tests_keys.rs @@ -402,3 +402,38 @@ fn keys_never_panic_on_any_text() { } } } + +#[test] +fn select_into_another_body_settles_the_composition() { + let (mut s, a) = editing("hello", (0, 0)); + s.execute("insert.textBox", &json!({"rect": [60.0, 400.0, 600.0, 100.0], "text": "x"})).unwrap(); + let b = s.doc().unwrap().selection.shapes[0].0; + s.execute("text.set", &json!({"id": b, "text": "0123456789"})).unwrap(); + s.execute("text.edit", &json!({"id": a, "at": [0, 0]})).unwrap(); + s.compose_text("ab", None).unwrap(); + s.select(|_, sel| { + sel.text = Some(crate::TextSel { shape: deckcraft_model::ShapeId(b), ..Default::default() }); + }) + .unwrap(); + assert!(s.doc().unwrap().composition.is_none()); + s.commit_text("X").unwrap(); + assert_eq!(text(&mut s, b), "X0123456789"); + assert_eq!(text(&mut s, a), "abhello"); +} + +#[test] +fn select_into_another_table_cell_settles_the_composition() { + let (mut s, _) = editing("x", (0, 0)); + let data = json!([["hello", "0123456789"]]); + let r = s.execute("insert.table", &json!({"rows": 1, "cols": 2, "rect": [60.0, 400.0, 600.0, 60.0], "data": data})).unwrap(); + let id = deckcraft_model::ShapeId(r["id"].as_u64().unwrap() as u32); + s.select(|_, sel| sel.text = Some(crate::TextSel { shape: id, cell: Some((0, 0)), ..Default::default() })).unwrap(); + s.compose_text("ab", None).unwrap(); + s.select(|_, sel| sel.text = Some(crate::TextSel { shape: id, cell: Some((0, 1)), ..Default::default() })).unwrap(); + assert!(s.doc().unwrap().composition.is_none()); + s.commit_text("X").unwrap(); + let st = s.doc().unwrap(); + let Some(deckcraft_model::ShapeKind::Table(t)) = st.shape(id).map(|sh| &sh.kind) else { panic!("not a table") }; + let cell = |c| t.cell(0, c).map(|x| x.text.text()).unwrap_or_default(); + assert_eq!((cell(0), cell(1)), ("abhello".to_string(), "X0123456789".to_string())); +}