From 44787764a9b159fd2f44863385cbfc37eccd7eeb Mon Sep 17 00:00:00 2001 From: NetterSilver <133535268+Silvertree2010@users.noreply.github.com> Date: Thu, 15 Oct 2026 00:16:35 +0200 Subject: [PATCH] Table Layout: the Cell Size boxes apply to the whole selection (#1242) (#1276) * Table Layout: the Cell Size boxes apply to the whole selection (#1242) table.rowHeight and table.columnWidth read the caret's cell only. With the whole table selected the caret is in the last cell, so only the last row's height and the last column's width changed. Both now use the cells the selection covers (selected_cells): every selected row gets the height and every grid column the selected cells touch gets the width, as one edit. Without a selection that spans cells they still change the caret's row and column. * Docs: update the progress docs for this change * Docs: put the new sentence in the WordCraft / evidence column --------- Co-authored-by: Brandon Thomas --- ROADMAP.md | 2 ++ crates/engine/src/cmd/table.rs | 40 +++++++++++++++++++++++++++------- crates/engine/src/tests.rs | 34 +++++++++++++++++++++++++++++ docs/ui-parity.md | 5 +++-- 4 files changed, 71 insertions(+), 10 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 2fc455192..108da0860 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -195,6 +195,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm | 2026-10-14 | Draw: Ink to Shape recognises arrows. A line and a V-shaped head drawn in one stroke (shaft first or head first, any direction) becomes a straight arrow connector with the head at the tip, in the stroke's colour and width; a lone V, a line with one barb and scribbles stay as they are. Our own recogniser: Word for Mac has no Ink to Shape to compare. Two-stroke arrows and arrows with a closed triangle head not yet. | | 2026-10-12 | A DOCX hyperlink whose relationship has an empty target (as LibreOffice writes) is no link (#1005): no "Ctrl+Click to follow link" tooltip over the text, which keeps its Hyperlink style | | 2026-10-12 | Table eraser beside vertically merged cells (#1039): the border between two cells merged down the same rows merges them into one cell that stays merged down those rows; a border whose two sides can't make one rectangle is hidden on both sides along the whole edge instead of refused | +| 2026-10-13 | Table Layout › Cell Size: the Height and Width boxes apply to every row and column the selection covers (a selected table changed only its last row and column), and to the caret's row and column without a selection (#1242) | | 2026-10-12 | RTF import now uses the RTF spec's defaults where a file names none (#902): no space before or after paragraphs, single line spacing, and the `\deff` default font, instead of WordCraft's Normal style; RTF files that relied on the old defaults open tighter, like in Word | | 2026-10-12 | `--help` and `-h` print the usage and exit (#968): `wordcraft --help` lists the app's options without opening a window (it used to try to open a file named `--help`), and `wordcraft-cli --help` works for every command instead of failing with `unknown option --help` | | 2026-10-11 | Right-clicking a table cell offers the table's commands, and as in Word they follow the selection: with the caret in a cell Insert, Delete Cells, Split Cells, Text Direction and Table Properties…; with several cells Merge Cells instead of Split; with whole columns or rows Delete Columns or Rows, Merge Cells and Distribute Rows and Columns; with the whole table Delete Table, Distribute, AutoFit and Insert Caption…. A right-click beside selected cells moves the caret there instead of keeping them | @@ -393,6 +394,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm | 2026-10-12 | trivial | Progress log: web picture paste respects the edit target (#1349) | | 2026-10-14 | trivial | Progress log: table deletes, Change Case and Unlink Fields respect Track Changes and locked content controls (#1329) | | 2026-10-14 | trivial | Progress log: Ink to Shape recognises single-stroke arrows | +| 2026-10-13 | trivial | Progress log: cell size boxes act on the whole selection (#1242) | | 2026-10-12 | trivial | Progress log: Accept All and Reject All keep the right paragraph marks (#864) | | 2026-10-12 | trivial | Progress log: `--help` for the app and every CLI subcommand (#968) | | 2026-10-12 | trivial | Progress log: Down and Up no longer skip a table (#1007) | diff --git a/crates/engine/src/cmd/table.rs b/crates/engine/src/cmd/table.rs index b7865f43e..12dfee1af 100644 --- a/crates/engine/src/cmd/table.rs +++ b/crates/engine/src/cmd/table.rs @@ -123,10 +123,12 @@ pub fn specs() -> Vec { })), t(CommandSpec::new("table.columnWidth", "Table Column Width", "Table Layout › Cell Size", |s, v| { let w = p::req_f32(v, "width")?.clamp(6.0, 1584.0); - let (_, r, c) = cell(s)?; - with_table(s, |t| set_column_width(t, r, c, w)) + let cells = selected_cells(s); + with_table(s, |t| set_column_widths(t, &cells, w)) }) - .params(r#"{"width": pt} (the caret cell's column; the table's preferred width is cleared so the column gets exactly this)"#)), + .params( + r#"{"width": pt} (every column the selection covers, the caret cell's without one; the table's preferred width is cleared so the columns get exactly this)"#, + )), t(CommandSpec::new("table.rowHeight", "Table Row Height", "Table Layout › Cell Size", |s, v| { let h = p::req_f32(v, "height")?.clamp(1.0, 1584.0); let rule = match p::str(v, "rule") { @@ -134,15 +136,18 @@ pub fn specs() -> Vec { Some("exact") => HeightRule::Exact, Some(x) => return Err(CmdError::Params(format!("unknown height rule `{x}`"))), }; - let (_, r, _) = cell(s)?; + let mut rows: Vec = selected_cells(s).into_iter().map(|(r, _)| r).collect(); + rows.dedup(); with_table(s, |t| { - if let Some(row) = t.rows.get_mut(r) { - row.props.height = Some(h); - row.props.height_rule = rule; + for r in &rows { + if let Some(row) = t.rows.get_mut(*r) { + row.props.height = Some(h); + row.props.height_rule = rule; + } } }) }) - .params(r#"{"height": pt, "rule"?: "atLeast|exact"}"#)), + .params(r#"{"height": pt, "rule"?: "atLeast|exact"} (every row the selection covers, the caret's without one)"#)), t(CommandSpec::new("table.repeatHeader", "Repeat Header Rows", "Table Layout › Data", |s, v| { let (_, r, _) = cell(s)?; let on = p::bool(v, "value"); @@ -256,6 +261,25 @@ pub(crate) fn sync_cell_widths(t: &mut Table) { /// Set the width of the grid column cell `c` of row `r` starts in. The table's preferred width /// goes, so the layout doesn't scale the column back. +/// Give every grid column the `cells` (row, cell) pairs touch the width `w`. +fn set_column_widths(t: &mut Table, cells: &[(usize, usize)], w: f32) { + let mut cols: Vec = cells.iter().map(|&(r, c)| t.grid_col(r, c)).collect(); + cols.sort_unstable(); + cols.dedup(); + let mut any = false; + for g in cols { + if let Some(x) = t.grid.get_mut(g) { + *x = w; + any = true; + } + } + if any { + t.props.width = None; + t.props.width_pct = None; + sync_cell_widths(t); + } +} + fn set_column_width(t: &mut Table, r: usize, c: usize, w: f32) { let g = t.grid_col(r, c); if let Some(x) = t.grid.get_mut(g) { diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index 7f0e7ca00..f9b1d2966 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -1415,6 +1415,40 @@ fn cell_size_boxes_set_row_height_and_column_width() { assert_eq!(s.doc.body.iter().find_map(|b| b.as_table()).unwrap(), &t0); } +/// The Height and Width boxes act on every row and column the selection covers (#1242), not only +/// on the caret's: the whole table, a row, a column, and the caret alone. +#[test] +fn cell_size_boxes_apply_to_the_whole_selection() { + let mut s = s(); + run(&mut s, "insert.table", json!({"rows": 3, "cols": 3})); + run(&mut s, "table.autofit", json!({"mode": "window"})); + let table = |s: &Session| s.doc.body.iter().find_map(|b| b.as_table()).unwrap().clone(); + let heights = |s: &Session| table(s).rows.iter().map(|r| r.props.height).collect::>(); + let widths = |s: &Session| table(s).grid; + let before = widths(&s); + // The caret alone changes its own row and column. + run(&mut s, "table.rowHeight", json!({"height": 20.0})); + run(&mut s, "table.columnWidth", json!({"width": 80.0})); + assert_eq!(heights(&s), vec![Some(20.0), None, None]); + assert_eq!(widths(&s)[0], 80.0); + assert_eq!(&widths(&s)[1..], &before[1..]); + // A selected column: just that column. + run(&mut s, "table.selectColumn", json!({})); + run(&mut s, "table.columnWidth", json!({"width": 70.0})); + assert_eq!(widths(&s)[0], 70.0); + assert_eq!(&widths(&s)[1..], &before[1..]); + // The whole table: every row and every column, one Undo per edit. + let mid = (heights(&s), widths(&s)); + run(&mut s, "table.selectTable", json!({})); + run(&mut s, "table.rowHeight", json!({"height": 40.0})); + assert_eq!(heights(&s), vec![Some(40.0); 3]); + run(&mut s, "table.columnWidth", json!({"width": 90.0})); + assert_eq!(widths(&s), vec![90.0; 3]); + assert!(table(&s).rows.iter().all(|r| r.cells.iter().all(|c| c.props.width == Some(90.0))), "cell widths follow the grid"); + run(&mut s, "edit.undo", json!({})); + run(&mut s, "edit.undo", json!({})); + assert_eq!((heights(&s), widths(&s)), mid); +} /// Table Properties (#44): table, row, column and cell settings land as one undo step, and /// without settings the command reports the current ones (the dialog's source). #[test] diff --git a/docs/ui-parity.md b/docs/ui-parity.md index 02c9f61a3..c77848835 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -1,6 +1,6 @@ # UI parity with Microsoft Word -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-14 · **Change:** trivial (the table context menu's Delete offers Delete Table with the caret in a cell, #1198; previously trivial: Windows Magnifier follows the caret onto a new line, #1119; previously trivial: no document behind the Start screen, #1129; previously trivial: equation pictures share a few textures, #1002; previously trivial: selection bar in the left margin, #856; 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 major: first version; ribbon, dialogs, shortcuts and on-canvas interaction measured from `crates/ui-egui` and the command registry) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4 +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-14 · **Change:** trivial (the table context menu's Delete offers Delete Table with the caret in a cell, #1198; previously trivial: Cell Size Height/Width apply to the whole selection (#1242); previously trivial: selection bar in the left margin, #856; 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 major: first version; ribbon, dialogs, shortcuts and on-canvas interaction measured from `crates/ui-egui` and the command registry) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4 How WordCraft looks and feels next to Word: ribbon, dialogs, panes, keyboard, mouse and touch. Typing behaviour has its own checklist ([`typing-parity.md`](typing-parity.md)). All assets are our @@ -26,7 +26,7 @@ Document title opens Rename Document; the account badge opens Options › Genera | Backstage (Home, New, Open, Info, Save As, Print, Share, Export, Options) | Home and New separate (#254), Info properties kept (#268), Options in Word's panes with working settings (#485), Print page with preview, page range and copies (#641); launched with nothing to open, the app starts on Home (the Start screen: Blank document, templates, Recent; no document is open until one is picked or opened, so Esc or Back shows an empty window with the ribbon greyed out but for Help, #1129), with File › Options › General › Start-up to turn it off (#766); Info › Inspect Document finds and removes hidden text by its effective formatting (direct, character or paragraph style) in every story: body, headers, footers, notes, comments and text boxes, and lists what it couldn't reach (#1344) | | 68% | 4–6 | | Rulers: indents, tabs, margins, table columns | indents draggable (one undo, #62); tabs; table column and row markers drag their borders (one undo) and in a cell the indents are measured from it (#700) | ✅ | 85% | 1–2 | | On-canvas objects: select, drag, Ctrl+drag to copy, resize handles, nudge | ✅ tested (#228, #1014) | ✅ | 75% | — | -| Rotation handle, crop handles on canvas, alignment guides, smart guides | rotation handle (Shift: 15° steps), turned frames resize along their axes (#332); alignment guides, our own design: a moved picture, shape or text box snaps its left, centre or right (top, middle or bottom) to the page's edges and centre, the margins and the other floating objects on the page within 6 screen points, x and y independently, turned objects by their rotated bounds, with a thin line across the page for each guide while dragging; Alt moves freely; File › Options › Advanced › Editing (`ui.alignmentGuides`) turns them off. Crop handles missing | Word for Mac 16.113.4 shows no guide lines and snaps inconsistently (to the page's left edge, not its top, right or bottom; Word check K); Word for Windows' guides not observed | 50% | 2–3 | +| Rotation handle, crop handles on canvas, alignment guides, smart guides | rotation handle (Shift: 15° steps), turned frames resize along their axes (#332); alignment guides, our own design: a moved picture, shape or text box snaps its left, centre or right (top, middle or bottom) to the page's edges and centre, the margins and the other floating objects on the page within 6 screen points, x and y independently, turned objects by their rotated bounds, with a thin line across the page for each guide while dragging; Alt moves freely; File › Options › Advanced › Editing (`ui.alignmentGuides`) turns them off. Crop handles missing | Word for Mac 16.113.4 shows no guide lines and snaps inconsistently (to the page's left edge, not its top, right or bottom; Word check K); Word for Windows' guides not observed | 50% The Cell Size Height and Width boxes set every selected row and column (#1242). | 2–3 | | Tables on the page: drag column and row borders, resize handle past the bottom-right corner; select columns, rows and cells; "+" insert buttons; move handle | ✅ resize cursors, dashed guide, one undo step per drag, selection kept; Shift moves one column; Esc cancels (#217); Ctrl shares the change among the columns on the right, the left edge moves a left-aligned table's indent (a centred or right-aligned one keeps its alignment), a border of cells selected across moves only in their rows, Alt shows the column widths on the ruler (#711); double-click a column border to AutoFit it (a nested table's within the cell around it, #971); selection arrows above a column, left of a row and along a cell's left edge (a drag selects more; the cells are shaded and a column is edited cell by cell); "+" left of a row border and above a column border; move handle past the top-left corner (a click selects the table, a drag floats it where it's dropped); an ordinary drag, Shift+arrows or Shift+click from one cell into another select the rectangle of whole cells between them, and into or out of a table whole rows, their cells shaded whole (#971). Missing: a single selected cell's border | ✅ | 95% | 1 | | Selection: word/sentence/paragraph clicks, dragging after a double-click/triple-click/Ctrl+click extends by whole words/paragraphs/sentences (#549), Shift extend, F8 extend mode, column (Alt+drag, #237) | ✅ | ✅ | 90% | 1 | | Zoom: Ctrl+scroll, pinch (#179), zoom buttons (#232), pages side by side when zoomed out (#251), still one per row after One Page or Page Width (#768) | ✅ | ✅ | 85% | — | @@ -71,6 +71,7 @@ Document title opens Rename Document; the account badge opens Options › Genera | 2026-10-12 | trivial | Web: a pasted picture inserts only when the page is the edit target (page focused; no dialog, Backstage or text field such as a comment or Find box taking input), once; otherwise it is ignored (#1349) | | 2026-10-13 | trivial | Mini toolbar: scrolled with its selection toward the ribbon (or the status bar), it slides under the rulers and ribbon with the text instead of drawing over them, and the part out of view takes no clicks | | 2026-10-12 | trivial | Mini toolbar: a click on the ribbon (or anywhere outside the document) puts it away until the selection changes, so it never covers the ribbon's drop-downs; it now draws below every popup and menu instead of over them | +| 2026-10-13 | trivial | Cell Size Height/Width apply to the whole selection (#1242) | | 2026-10-11 | trivial | Tables on the page: Ctrl shares a column border's change among the columns right of it, the left edge moves a left-aligned table's indent (a centred or right-aligned one keeps its alignment), a border of cells selected across moves only in their rows, Alt shows the column widths on the ruler (#711); 80% → 90% | | 2026-10-11 | trivial | Rulers: table column and row markers drag their borders (one undo step; from the ruler only the column left of a border changes, with Shift the neighbour makes up the difference), and in a cell the indents and tabs are measured from it (#700) | | 2026-10-12 | trivial | Tables on the page: a double-click on a nested table's column border fits the column within the cell around the table; a selection from text into or across a table shades the whole cells of the rows it takes (#971) |