Insert › Shapes keeps the selected shape and text (#2019) (#2030)

Inserting a second shape removed the first: after an insert the new
shape stays selected, and `insert.shape` replaced the selection, so each
shape replaced the one before. Word draws a new shape beside whatever is
selected and never deletes it (checked by hand in Word for Windows: a
second shape is drawn over the first, which stays).

The new shape now goes after the selection, which is left alone, and
becomes the selection. Selected text stays too. A picture still replaces
a selected picture, which is what Word does as well (checked in Word for
Windows), so `insert.picture` is unchanged.

Tests: three shapes inserted in a row are all kept, which fails with the
old code (1 of 3 kept); a shape inserted over selected text keeps the
text and is selected itself; a picture inserted over a selected picture
replaces it.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
Diego Guerini
2026-10-18 11:32:07 -04:00
committed by GitHub
co-authored by Claude Opus 5.5 Brandon Thomas
parent 7333a302f5
commit 1b45615940
3 changed files with 42 additions and 2 deletions
+4 -1
View File
@@ -348,8 +348,11 @@ fn shape(s: &mut Session, v: &Value) -> CmdResult {
if super::canvas::add_member(s, obj.clone())?.is_some() {
return sel_result(s);
}
// Word draws a new shape beside what is selected and never deletes it: the shape just
// inserted (which is selected) stays, and so does selected text (#2019). It goes after the
// selection and becomes the selection. (A picture does replace the selection, as in Word.)
let props = s.typing_props();
let at = delete_selection(s)?;
let (_, at) = s.sel.ordered();
let end = s.doc.insert_object(&at, obj, &props)?;
s.sel = Selection { anchor: at, focus: end };
sel_result(s)
+35
View File
@@ -1594,6 +1594,41 @@ mod tests {
assert_eq!(s.run("arrange.selectionPane", &json!({})).unwrap().as_array().map(Vec::len), Some(2));
}
/// #2019: Insert › Shapes adds each new shape beside the selected one, as Word draws it, so
/// shapes inserted in a row are all kept (the second used to replace the first, which stays
/// selected after its insert). Selected text stays too, and the new shape is selected.
#[test]
fn shapes_inserted_in_a_row_are_all_kept_and_keep_selected_text() {
let objects = |s: &mut Session| s.run("arrange.selectionPane", &json!({})).unwrap().as_array().map(Vec::len);
let mut s = Session::new(wordcraft_doc::Document::new());
for kind in ["rectangle", "ellipse", "star"] {
s.run("insert.shape", &json!({"kind": kind})).unwrap();
}
assert_eq!(objects(&mut s), Some(3));
let mut s = Session::new(wordcraft_doc::Document::from_text("Kiln room"));
s.run("select.text", &json!({"text": "Kiln"})).unwrap();
s.run("insert.shape", &json!({"kind": "rectangle"})).unwrap();
let text = s.doc.plain_text(StoryRef::Body);
assert!(text.starts_with("Kiln") && text.ends_with(" room"), "{text:?}");
assert_eq!(objects(&mut s), Some(1));
let (a, b) = s.sel.ordered();
let obj = wordcraft_doc::para::OBJ.len_utf8();
assert_eq!((a.off, b.off), (4, 4 + obj), "the new shape, after the text, is selected");
}
/// Insert › Pictures does replace a selected picture, as Word does (checked in Word for
/// Windows while fixing #2019): only shapes are added beside the selection.
#[test]
fn an_inserted_picture_replaces_a_selected_picture() {
let data = super::super::insert::base64_encode(&png(20, 10));
let mut s = Session::new(wordcraft_doc::Document::new());
s.run("insert.picture", &json!({"data": data})).unwrap();
s.sel = crate::Selection { anchor: Pos::body(0, 0), focus: Pos::body(0, wordcraft_doc::para::OBJ.len_utf8()) };
s.run("insert.picture", &json!({"data": data})).unwrap();
assert_eq!(s.run("arrange.selectionPane", &json!({})).unwrap().as_array().map(Vec::len), Some(1));
}
#[test]
fn picture_pipeline() {
let mut s = Session::new(wordcraft_doc::Document::new());
+3 -1
View File
@@ -1,6 +1,6 @@
# WordCraft parity with Microsoft Word
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-16 · **Change:** trivial (East Asian typography: `w:eastAsianLayout` kept on save, not laid out; previously trivial: Ink to Shape recognises arrows; previously trivial: Windows Magnifier follows the caret onto a new line, #1119; previously trivial: table border drags with Ctrl and Alt, from the left edge and of selected cells, #711; previously trivial: table column and row markers on the rulers, #700; previously trivial: table borders and the resize handle drag on the page, #217; previously trivial: all 12 bibliography styles, #383; previously trivial: formatting revisions landed, #41; previously trivial: Draw Table and Eraser landed, #303; previously minor: readiness table with hours per audience; full number back to the additive weighted sum, 60%; mainstream and essentials numbers added; alpha gate checked; previously major: full re-measure against Word for Mac 16.113.4; replaces the parity tables that lived in ROADMAP.md) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-16 · **Change:** trivial (shapes inserted in a row are all kept, #2019; previously trivial: East Asian typography: `w:eastAsianLayout` kept on save, not laid out; previously trivial: Ink to Shape recognises arrows; previously trivial: Windows Magnifier follows the caret onto a new line, #1119; previously trivial: table border drags with Ctrl and Alt, from the left edge and of selected cells, #711; previously trivial: table column and row markers on the rulers, #700; previously trivial: table borders and the resize handle drag on the page, #217; previously trivial: all 12 bibliography styles, #383; previously trivial: formatting revisions landed, #41; previously trivial: Draw Table and Eraser landed, #303; previously minor: readiness table with hours per audience; full number back to the additive weighted sum, 60%; mainstream and essentials numbers added; alpha gate checked; previously major: full re-measure against Word for Mac 16.113.4; replaces the parity tables that lived in ROADMAP.md) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4
The authoritative assessment of how close WordCraft is to Microsoft Word. [`ROADMAP.md`](../ROADMAP.md)
summarizes it; [`gaps.md`](gaps.md) itemizes every shortfall; the deep checklists are
@@ -251,9 +251,11 @@ can be used without committing Word output.
| Date | Change | Summary |
|---|---|---|
| 2026-10-15 | trivial | Pictures, shapes, text boxes: Insert › Shapes adds the new shape beside the selection, as Word draws it, so a second shape no longer replaces the first (still selected after its insert) and selected text stays; a picture still replaces a selected picture, as in Word (#2019) |
| 2026-10-16 | trivial | East Asian typography row: `w:eastAsianLayout` kept on save, not laid out; no percentage change |
| 2026-10-15 | trivial | Equations: a script or limit after `_` or `^` is the whole run of letters and digits (`V_dd`, `x^ab`), as in Word for Mac 16.113.4, and Linear format writes it bare (#1609) |
| 2026-10-15 | trivial | Tables: Cell Margins (#1960) opens initialized from table defaults and applies edits only after confirmation; per-cell margins remain intact |
| 2026-10-15 | trivial | Pictures, shapes, text boxes: Insert › Shapes adds the new shape beside the selection, as Word draws it, so a second shape no longer replaces the first (still selected after its insert) and selected text stays; a picture still replaces a selected picture, as in Word (#2019) |
| 2026-10-12 | minor | Czech proofing review: short-word splits rank after deletion, NOSUGGEST excludes suggestions while accepting typed forms, affix comments apply only to headers, ratios keep spaces and repeated-word messages use Czech quotes; synthetic engine end-to-end and latency checks |
| 2026-10-12 | minor | Czech spelling from the installed Hunspell dictionary, runtime-table suggestions and generic checks; Czech interface writes cs-CZ documents; no Czech hyphenation patterns bundled; estimates unchanged |
| 2026-10-13 | trivial | Windows Magnifier follows the caret onto a new line (#1119): Accessibility row updated; no percentage change |