From d8b11c69d3de233e38d484d01c19167d56d12ffe Mon Sep 17 00:00:00 2001 From: Alik <86338858+alik654zx@users.noreply.github.com> Date: Sun, 18 Oct 2026 12:59:01 +0500 Subject: [PATCH] Columns on selected text go in their own section (#2024) (#2071) With text selected, Layout > Columns used to change the whole section. As in Word (observed black-box), a continuous section break now goes where the selection starts and one where it ends, splitting a paragraph there, unless that end already meets a section boundary (the document's start or end, or an existing break); the columns go on the section between, in one undo step. `layout.columns` gains `apply: "selection"`, its default when text is selected in the main text. More Columns... offers "Selected text" and picks it then. Co-authored-by: Brandon Thomas --- ROADMAP.md | 4 +- crates/engine/src/cmd/page.rs | 137 ++++++++++++++++++++++++++- crates/ui-egui/src/dialogs_insert.rs | 31 +++++- crates/ui-egui/src/i18n/bg.tsv | 1 + crates/ui-egui/src/i18n/cs.tsv | 1 + crates/ui-egui/src/i18n/de.tsv | 1 + crates/ui-egui/src/i18n/es.tsv | 1 + crates/ui-egui/src/i18n/et.tsv | 1 + crates/ui-egui/src/i18n/fr.tsv | 1 + crates/ui-egui/src/i18n/hr.tsv | 1 + crates/ui-egui/src/i18n/hu.tsv | 1 + crates/ui-egui/src/i18n/it.tsv | 1 + crates/ui-egui/src/i18n/ja.tsv | 1 + crates/ui-egui/src/i18n/ko.tsv | 1 + crates/ui-egui/src/i18n/pl.tsv | 1 + crates/ui-egui/src/i18n/pt-br.tsv | 1 + crates/ui-egui/src/i18n/ru.tsv | 1 + crates/ui-egui/src/i18n/sr-latn.tsv | 1 + crates/ui-egui/src/i18n/sr.tsv | 1 + crates/ui-egui/src/i18n/sv.tsv | 1 + crates/ui-egui/src/i18n/tr.tsv | 1 + crates/ui-egui/src/i18n/uk.tsv | 1 + crates/ui-egui/src/i18n/vi.tsv | 1 + crates/ui-egui/src/i18n/zh-hans.tsv | 1 + crates/ui-egui/src/i18n/zh-hant.tsv | 1 + docs/ui-parity.md | 3 +- 26 files changed, 190 insertions(+), 7 deletions(-) diff --git a/ROADMAP.md b/ROADMAP.md index 0ce3a3cc4..ed3ab30d9 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -2,7 +2,7 @@ **Stage: alpha** · next: beta, ~15 points (60% → 75% ready for real work) and ~150–230 h away -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-18 · **Change:** trivial (progress log: legacy outline, shadow, emboss and imprint text effects drawn, #2534; previously trivial: progress log: Indonesian proofing, #498; previously trivial: progress log: DOCX glossary documents (building blocks); previously trivial: progress log: Read Mode keeps the keyboard and shows whole, unobscured columns, #1772); previously trivial: progress log: font list entries of tall-ascent fonts no longer cut off, #2570); previously trivial: progress log: Split Cells and Convert Text to Table dialogs follow the Word check, #2592; previously trivial: a new source's Tag is made like Word's, three letters of the author and two digits of the year with a number added when taken, and an edit keeps the Tag, #2094; previously trivial: Web Layout fills the window width, #1876; previously trivial: progress log: Different First Page headers reachable from page 1, #2826; previously trivial: Language dialog keeps inherited proofing language and no-proof choices (#2717)); previously trivial (table style editor formats every part, #714); previously trivial: VML freeforms read and kept on save; previously trivial: progress log: consistent preferred cell widths; previously trivial: multi-paragraph field instructions no longer add blank lines; previously trivial: page-break-only list paragraphs hide their labels; previously trivial: progress log: grouped `flowChartOr` shapes draw their circle and crossing spokes; previously trivial: progress log: copies keep their formatting in other programs, #1523; previously trivial: progress log: privacy options on save, #1062; previously trivial: progress log: equation pictures in theme colours on Windows, #1002; previously trivial: symbol fonts, #776; previously trivial: the save prompt answers from the keyboard, #861; 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: formatting revisions landed, #41; previously trivial: Draw tab ink, #307, and Draw Table and Eraser, #303, landed: catalog 402/434; 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; restructured to the craftrules progress-docs standard) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4 +> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-18 · **Change:** trivial (progress log: Columns on selected text, #2024; previously trivial: progress log: legacy outline, shadow, emboss and imprint text effects drawn, #2534; previously trivial: progress log: Indonesian proofing, #498; previously trivial: progress log: DOCX glossary documents (building blocks); previously trivial: progress log: Read Mode keeps the keyboard and shows whole, unobscured columns, #1772); previously trivial: progress log: font list entries of tall-ascent fonts no longer cut off, #2570); previously trivial: progress log: Split Cells and Convert Text to Table dialogs follow the Word check, #2592; previously trivial: a new source's Tag is made like Word's, three letters of the author and two digits of the year with a number added when taken, and an edit keeps the Tag, #2094; previously trivial: Web Layout fills the window width, #1876; previously trivial: progress log: Different First Page headers reachable from page 1, #2826; previously trivial: Language dialog keeps inherited proofing language and no-proof choices (#2717)); previously trivial (table style editor formats every part, #714); previously trivial: VML freeforms read and kept on save; previously trivial: progress log: consistent preferred cell widths; previously trivial: multi-paragraph field instructions no longer add blank lines; previously trivial: page-break-only list paragraphs hide their labels; previously trivial: progress log: grouped `flowChartOr` shapes draw their circle and crossing spokes; previously trivial: progress log: copies keep their formatting in other programs, #1523; previously trivial: progress log: privacy options on save, #1062; previously trivial: progress log: equation pictures in theme colours on Windows, #1002; previously trivial: symbol fonts, #776; previously trivial: the save prompt answers from the keyboard, #861; 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: formatting revisions landed, #41; previously trivial: Draw tab ink, #307, and Draw Table and Eraser, #303, landed: catalog 402/434; 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; restructured to the craftrules progress-docs standard) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4 > **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-17 · **Change:** trivial (progress log: pasted blockquotes are indented paragraphs, #1270; previously trivial: progress log: print-area strings translated in every catalog; previously trivial: progress log: comments readable in the dark theme, #2045; previously trivial: progress log: ODT sections, #1379; previously trivial: progress log: table deletes, Change Case and Unlink Fields respect Track Changes and locked content controls, #1329; previously trivial: progress log: rounded rectangle corners, #1476; previously trivial: progress log: Save As type list, #1399; previously trivial: progress log: privacy options on save, #1062; previously trivial: progress log: equation pictures in theme colours on Windows, #1002; previously trivial: symbol fonts, #776; previously trivial: the save prompt answers from the keyboard, #861; 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: formatting revisions landed, #41; previously trivial: Draw tab ink, #307, and Draw Table and Eraser, #303, landed: catalog 402/434; 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; restructured to the craftrules progress-docs standard) · **Target:** Microsoft Word (Microsoft 365) for Mac 16.113.4 > **Printing update:** 2026-10-12 · direct desktop printing through craft-print; details below. @@ -149,6 +149,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm | 2026-10-16 | Indonesian proofing (#498): id-ID spelling reads the installed Hunspell `id_ID` dictionary (`hunspell-id` on Debian/Ubuntu, LibreOffice's `dict-id`, or the Dictionaries folder), suggestions use its MAP/REP/TRY/KEY tables, generic repeated-word (offering `anak-anak`) and spacing checks explain in Indonesian; Review › Language, Set Proofing Language and File › Options › Proofing offer Indonesian. No dictionary data bundled; hyphenation keeps the English fallback; parity estimates unchanged | | 2026-10-17 | A table inserted in a right-to-left paragraph is a right-to-left table, like Word, so Tab moves right to left through its cells (#2634) | | 2026-10-14 | DOCX row table border exceptions render and survive saving. Explicit cell borders win; a row exception does not change other rows. | +| 2026-10-15 | Layout › Columns with text selected puts only that text in columns, as in Word (#2024): a continuous section break goes where the selection starts and one where it ends (splitting a paragraph there; none at the document's start or end, or where a section already begins or ends), and the columns go on the section between, one undo step; More Columns… offers "Selected text" and picks it. Before, the whole section took the columns | | 2026-10-14 | Tab stops edited on the ruler: the corner selector picks Left/Center/Right/Decimal/Bar, a click sets a stop (Alt skips the snap), dragging moves it with a guide line and position tooltip, dragging off the ruler clears it; markers draw per kind and mirror for RTL; one `para.tabs` call and one undo step per gesture | | 2026-10-12 | Table formulas: cell references and ranges (`=B2+B3`, `=SUM(B2:C3)`), arithmetic and comparisons, IF, AND, OR, NOT, TRUE, FALSE, DEFINED and the `\#` number format; ABOVE and LEFT sum the run of numbers next to the cell as Word does; an unknown function or a syntax error is an error instead of a sum; Update Field (F9) recalculates formula fields | | 2026-10-12 | Fields keep `w:dirty` through DOCX saves (#877): a begin marker flagged by a generator that couldn't compute a result (pandoc-style TOC placeholders) keeps the flag on open/save — as a buffered field or a range field — so Word still refreshes it on open; computing a result (Update Field, Update Table) clears it, like Word's F9 | @@ -432,6 +433,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm | 2026-10-16 | trivial | Progress log: comments readable in the dark theme (#2045) | | 2026-10-16 | trivial | Progress log: Indonesian proofing (#498) | | 2026-10-16 | trivial | Progress log: legacy outline, shadow, emboss and imprint text effects drawn (#2534) | +| 2026-10-15 | trivial | Progress log: Columns on selected text (#2024) | | 2026-10-14 | trivial | Progress log: row table border exceptions | | 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 | diff --git a/crates/engine/src/cmd/page.rs b/crates/engine/src/cmd/page.rs index 4cee3bd96..a521fc610 100644 --- a/crates/engine/src/cmd/page.rs +++ b/crates/engine/src/cmd/page.rs @@ -27,7 +27,7 @@ pub fn specs() -> Vec { CommandSpec::new("layout.size", "Size", "Layout › Page Setup", size).params(r#"{"name"?: "Letter|Legal|A4|…", "width"?: pt, "height"?: pt} (each side clamped to 7.2–1584 pt, 0.1–22 in; the section keeps its orientation)"#), CommandSpec::new("layout.columns", "Columns", "Layout › Page Setup", columns) .params( - r#"{"count": 1-12, "space"?: pt, "separator"?: bool, "preset"?: "left|right", "widths"?: [[width pt, space after pt], …] (unequal columns; sets the count), "apply"?: "section|document|forward"}"#, + r#"{"count": 1-12, "space"?: pt, "separator"?: bool, "preset"?: "left|right", "widths"?: [[width pt, space after pt], …] (unequal columns; sets the count), "apply"?: "section|document|forward|selection"} (default: selection when text is selected in the main text — section breaks around it — else section)"#, ), CommandSpec::new("layout.break", "Breaks", "Layout › Page Setup", breaks) .params(r#"{"kind": "page|column|textWrapping|nextPage|continuous|evenPage|oddPage"}"#), @@ -569,7 +569,9 @@ fn columns(s: &mut Session, v: &Value) -> CmdResult { // Equal columns keep one spacing; unequal ones remember the first gap for a later switch back. let space = widths.first().map(|w| w.1).unwrap_or(space); let cols = Columns { count: count as u32, space, separator: sep, widths }; - match p::str(v, "apply").unwrap_or("section") { + // With text selected the gallery works on that text, as in Word (#2024). + let default = if selection_sectionable(s) { "selection" } else { "section" }; + match p::str(v, "apply").unwrap_or(default) { "section" => with_sect(s, |x| x.columns = cols.clone()), "document" => { let ends: Vec = s.doc.sections().iter().map(|(e, _)| *e).collect(); @@ -585,10 +587,61 @@ fn columns(s: &mut Session, v: &Value) -> CmdResult { breaks(s, &json!({"kind": "continuous"}))?; with_sect(s, |x| x.columns = cols.clone()) } - x => Err(CmdError::Params(format!("unknown apply `{x}` (section, document or forward)"))), + "selection" => { + selection_to_sections(s)?; + with_sect(s, |x| x.columns = cols.clone()) + } + x => Err(CmdError::Params(format!("unknown apply `{x}` (section, document, forward or selection)"))), } } +/// Whether the selection can become sections of its own: text selected in the main text, outside +/// tables. +pub fn selection_sectionable(s: &Session) -> bool { + let (a, b) = s.sel.ordered(); + !s.sel.is_collapsed() && [a, b].iter().all(|p| p.story == StoryRef::Body && p.path.depth() == 0) +} + +/// Columns for the selected text (#2024), as Word does it: a continuous section break where the +/// selection starts and one where it ends, splitting paragraphs there, unless that end already +/// meets a section boundary (the document's start or end, or a section break). The selection keeps +/// its text, which is then the section(s) between. +fn selection_to_sections(s: &mut Session) -> Result<(), CmdError> { + if !selection_sectionable(s) { + return Err(CmdError::Params("selected text in the main text, outside tables, is needed".into())); + } + let old = s.sel.clone(); + let (a, mut b) = old.ordered(); + let block = |p: &Pos| p.path.0.first().copied().unwrap_or(0) as usize; + let len = |s: &Session, p: &Pos| s.doc.para_at(p).map(|q| q.len()).unwrap_or(0); + let ends: Vec = s.doc.sections().iter().map(|(e, _)| *e).collect(); + let after_break = |i: usize| i.checked_sub(1).is_some_and(|j| ends.contains(&j)); + // A selection that took a section break with it ends before that break. + if b.off == 0 && block(&b) > block(&a) && after_break(block(&b)) { + let prev = Pos { path: wordcraft_doc::Path::top(block(&b) - 1), off: 0, ..b.clone() }; + b = Pos { off: len(s, &prev), ..prev }; + } + let last_block = s.doc.body.len().saturating_sub(1); + let end_break = !(b.off == len(s, &b) && (ends.contains(&block(&b)) || block(&b) == last_block)); + let start_break = !(a.off == 0 && (block(&a) == 0 || after_break(block(&a)))); + // The end first: the start's position doesn't move. + if end_break { + s.sel = Selection::caret(b.clone()); + breaks(s, &json!({"kind": "continuous"}))?; + } + let (first, last) = if start_break { + s.sel = Selection::caret(a.clone()); + breaks(s, &json!({"kind": "continuous"}))?; + let first = s.sel.focus.clone(); + let last = shifted_by_split(&b, &a, &first); + (first, last) + } else { + (a, b) + }; + s.sel = if old.anchor <= old.focus { Selection { anchor: first, focus: last } } else { Selection { anchor: last, focus: first } }; + Ok(()) +} + /// `[[width, space after], …]` in points: 1–12 columns, each at least 0.25", that fit the text /// width (`tw`). fn column_widths(v: &Value, tw: f32) -> Result, CmdError> { @@ -956,6 +1009,84 @@ mod tests { assert!(s.run("layout.columns", &json!({"count": 2, "apply": "elsewhere"})).is_err()); } + /// Each section's paragraphs (a section break's own paragraph shows as "") and column count. + fn sections_of(s: &Session) -> Vec<(Vec, u32)> { + let mut from = 0; + let mut out = Vec::new(); + for (end, x) in s.doc.sections() { + let paras = (from..=end).map(|i| s.doc.para_at(&Pos::body(i, 0)).map(|p| p.text.clone()).unwrap_or_default()).collect(); + out.push((paras, x.columns.count)); + from = end + 1; + } + out + } + + fn select(s: &mut Session, from: (usize, usize), to: (usize, usize)) { + s.sel = Selection { anchor: Pos::body(from.0, from.1), focus: Pos::body(to.0, to.1) }; + } + + fn strs(v: &[&str]) -> Vec { + v.iter().map(|x| x.to_string()).collect() + } + + /// Columns on selected text (#2024), as observed in Word: a continuous section break before + /// and after the selection, the columns on the section between, the rest left alone. + #[test] + fn columns_apply_to_the_selected_text() { + let five = || Session::new(wordcraft_doc::Document::from_text("p1\np2\nalpha beta gamma\np4\np5")); + // A whole paragraph (a triple-click takes its mark): each break gets a paragraph of its own. + let mut s = five(); + select(&mut s, (2, 0), (3, 0)); + let whole = s.selected_text(); + s.run("layout.columns", &json!({"count": 2})).unwrap(); + assert_eq!(sections_of(&s), vec![(strs(&["p1", "p2", ""]), 1), (strs(&["alpha beta gamma", ""]), 2), (strs(&["p4", "p5"]), 1)]); + assert!(s.doc.sections().iter().skip(1).all(|(_, x)| x.start == SectionStart::Continuous)); + assert_eq!(s.selected_text(), whole, "the paragraph stays selected"); + // The same paragraph again: its section changes, no new breaks. + select(&mut s, (3, 0), (4, 0)); + s.run("layout.columns", &json!({"count": 3})).unwrap(); + assert_eq!(sections_of(&s).iter().map(|(_, n)| *n).collect::>(), vec![1, 3, 1]); + // Taking the section break along changes nothing more. + select(&mut s, (3, 0), (5, 0)); + s.run("layout.columns", &json!({"count": 2})).unwrap(); + assert_eq!(sections_of(&s).iter().map(|(_, n)| *n).collect::>(), vec![1, 2, 1]); + // One undo step takes the breaks and the columns away. + let mut s = five(); + select(&mut s, (2, 0), (3, 0)); + s.run("layout.columns", &json!({"count": 2})).unwrap(); + s.run("edit.undo", &json!({})).unwrap(); + assert_eq!(sections_of(&s), vec![(strs(&["p1", "p2", "alpha beta gamma", "p4", "p5"]), 1)]); + + // Words inside a paragraph: the breaks split it there. + let mut s = five(); + select(&mut s, (2, 10), (2, 6)); + s.run("layout.columns", &json!({"count": 2})).unwrap(); + assert_eq!(sections_of(&s), vec![(strs(&["p1", "p2", "alpha "]), 1), (strs(&["beta"]), 2), (strs(&[" gamma", "p4", "p5"]), 1)]); + assert_eq!(s.selected_text(), "beta"); + assert!(s.sel.anchor > s.sel.focus, "a backward selection stays backward"); + + // At the document's start no break goes before it, at its end none after. + let mut s = five(); + select(&mut s, (0, 0), (1, 0)); + s.run("layout.columns", &json!({"count": 2})).unwrap(); + assert_eq!(sections_of(&s), vec![(strs(&["p1", ""]), 2), (strs(&["p2", "alpha beta gamma", "p4", "p5"]), 1)]); + let mut s = five(); + select(&mut s, (4, 0), (4, 2)); + s.run("layout.columns", &json!({"count": 2})).unwrap(); + assert_eq!(sections_of(&s), vec![(strs(&["p1", "p2", "alpha beta gamma", "p4", ""]), 1), (strs(&["p5"]), 2)]); + // Select All: the whole document, no breaks. + let mut s = five(); + s.run("select.all", &json!({})).unwrap(); + s.run("layout.columns", &json!({"count": 2})).unwrap(); + assert_eq!(sections_of(&s), vec![(strs(&["p1", "p2", "alpha beta gamma", "p4", "p5"]), 2)]); + + // Without a selection the caret's section changes; "selection" then needs one. + let mut s = five(); + s.run("layout.columns", &json!({"count": 2})).unwrap(); + assert_eq!(sections_of(&s).len(), 1); + assert!(s.run("layout.columns", &json!({"count": 2, "apply": "selection"})).is_err()); + } + /// #407 Line Numbers options: start, count by, distance and restart; options alone keep the /// restart mode, `none` turns numbering off. #[test] diff --git a/crates/ui-egui/src/dialogs_insert.rs b/crates/ui-egui/src/dialogs_insert.rs index c774777f4..a0181e1f2 100644 --- a/crates/ui-egui/src/dialogs_insert.rs +++ b/crates/ui-egui/src/dialogs_insert.rs @@ -112,12 +112,14 @@ pub struct ColumnsForm { pub cols: Vec<(f32, f32)>, /// Line between columns. pub separator: bool, - /// `section`, `document` or `forward` (this point forward). + /// `section`, `document`, `forward` (this point forward) or `selection` (selected text). pub apply: String, /// The section's text width, in the interface unit. pub text_width: f32, /// The document has more than one section (offer "This section"). pub sections: bool, + /// Text is selected that can take columns of its own (offer "Selected text", #2024). + pub selection: bool, } impl ColumnsForm { @@ -131,14 +133,21 @@ impl ColumnsForm { let c = &sp.columns; let count = c.count.clamp(1, 12); let sections = app.session.doc.sections().len() > 1; + let selection = wordcraft_engine::cmd::page::selection_sectionable(&app.session); + let apply = match (selection, sections) { + (true, _) => "selection", + (false, true) => "section", + (false, false) => "document", + }; let mut f = ColumnsForm { count, equal: true, cols: Vec::new(), separator: c.separator, - apply: if sections { "section" } else { "document" }.into(), + apply: apply.into(), text_width: sp.text_width() / k, sections, + selection, }; if c.widths.len() == count as usize && count > 1 { f.equal = false; @@ -438,6 +447,9 @@ fn columns_ui(app: &mut WordApp, ui: &mut Ui, f: &mut ColumnsForm) -> bool { if f.sections { options.insert(0, ("section", "This section")); } + if f.selection { + options.push(("selection", "Selected text")); + } let shown = options.iter().find(|(k, _)| *k == f.apply).map(|(_, l)| *l).unwrap_or("This section"); egui::ComboBox::from_id_salt("columns_apply").selected_text(tl!(shown)).width(180.0).show_ui(ui, |ui| { for (k, l) in &options { @@ -1357,6 +1369,21 @@ mod tests { assert_eq!(wordcraft_engine::cmd::page::sect(&a.session).columns.count, 1); } + /// With text selected, More Columns applies to the selected text by default, as in Word + /// (#2024): the other paragraphs keep one column. + #[test] + fn columns_dialog_applies_to_the_selected_text() { + let mut a = app(); + assert!(!ColumnsForm::read(&a).selection, "no selection, no Selected text"); + a.run("select.paragraph", json!({})).unwrap(); + let mut f = ColumnsForm::read(&a); + assert_eq!((f.selection, f.apply.as_str()), (true, "selection")); + f.preset("two"); + a.run("layout.columns", f.params()).unwrap(); + let counts: Vec = a.session.doc.sections().iter().map(|(_, x)| x.columns.count).collect(); + assert_eq!(counts, vec![2, 1]); + } + /// The Symbol dialog inserts in the chosen font and remembers the symbol. #[test] fn symbol_dialog_inserts_with_its_font() { diff --git a/crates/ui-egui/src/i18n/bg.tsv b/crates/ui-egui/src/i18n/bg.tsv index fab612e47..047804f60 100644 --- a/crates/ui-egui/src/i18n/bg.tsv +++ b/crates/ui-egui/src/i18n/bg.tsv @@ -147,6 +147,7 @@ Apply to: Прилагане към: Whole document Целия документ This section Този раздел + Selected text Избрания текст Fill Запълване Setting: Настройка: 3-D 3-D diff --git a/crates/ui-egui/src/i18n/cs.tsv b/crates/ui-egui/src/i18n/cs.tsv index 5bf712b7a..a2a7d8f8b 100644 --- a/crates/ui-egui/src/i18n/cs.tsv +++ b/crates/ui-egui/src/i18n/cs.tsv @@ -1242,6 +1242,7 @@ This font has no characters to show. Toto písmo neobsahuje žádné znaky k zobrazení. This point forward Od tohoto místa dále This section Tento oddíl + Selected text Vybraný text Three Tři Tight Těsné Title from the document properties Název z vlastností dokumentu diff --git a/crates/ui-egui/src/i18n/de.tsv b/crates/ui-egui/src/i18n/de.tsv index 125cc95f5..fe4711242 100644 --- a/crates/ui-egui/src/i18n/de.tsv +++ b/crates/ui-egui/src/i18n/de.tsv @@ -159,6 +159,7 @@ Apply to: Übernehmen für: Whole document Gesamtes Dokument This section Dieser Abschnitt + Selected text Markierter Text Fill Füllung Setting: Einstellung: 3-D 3D diff --git a/crates/ui-egui/src/i18n/es.tsv b/crates/ui-egui/src/i18n/es.tsv index 122f371a9..037f988b6 100644 --- a/crates/ui-egui/src/i18n/es.tsv +++ b/crates/ui-egui/src/i18n/es.tsv @@ -198,6 +198,7 @@ Use the Insert key to switch overtype on and off Usar la tecla Insert para activar y desactivar la sobrescritura Whole document Todo el documento This section Esta sección + Selected text Texto seleccionado Fill Relleno de color Setting: Valor: 3-D 3D diff --git a/crates/ui-egui/src/i18n/et.tsv b/crates/ui-egui/src/i18n/et.tsv index 1ffe88188..678fc05e5 100644 --- a/crates/ui-egui/src/i18n/et.tsv +++ b/crates/ui-egui/src/i18n/et.tsv @@ -1331,6 +1331,7 @@ Apply to: Rakenda: Whole document Kogu dokument This section See jaotis + Selected text Valitud tekst Fill Täide Setting: Säte: 3-D Ruumiline diff --git a/crates/ui-egui/src/i18n/fr.tsv b/crates/ui-egui/src/i18n/fr.tsv index 8180c9900..7b43de9c0 100644 --- a/crates/ui-egui/src/i18n/fr.tsv +++ b/crates/ui-egui/src/i18n/fr.tsv @@ -145,6 +145,7 @@ Apply to: Appliquer à : Whole document Tout le document This section Cette section + Selected text Texte sélectionné Fill Remplissage Setting: Type : 3-D 3-D diff --git a/crates/ui-egui/src/i18n/hr.tsv b/crates/ui-egui/src/i18n/hr.tsv index b317a812c..0a28fdfaa 100644 --- a/crates/ui-egui/src/i18n/hr.tsv +++ b/crates/ui-egui/src/i18n/hr.tsv @@ -2257,6 +2257,7 @@ template Letter Pismo This password type isn't supported. Ova vrsta lozinke nije podržana. This point forward Od ove točke nadalje This section Ovaj odjeljak + Selected text Odabrani tekst Three Tri Through Kroz Tight Usko diff --git a/crates/ui-egui/src/i18n/hu.tsv b/crates/ui-egui/src/i18n/hu.tsv index 3a5ddb7d5..1a6e6c9bc 100644 --- a/crates/ui-egui/src/i18n/hu.tsv +++ b/crates/ui-egui/src/i18n/hu.tsv @@ -1642,6 +1642,7 @@ Apply to: Alkalmazás erre: Whole document Teljes dokumentum This section Ez a szakasz + Selected text Kijelölt szöveg Fill Kitöltés Setting: Beállítás: 3-D Térhatású diff --git a/crates/ui-egui/src/i18n/it.tsv b/crates/ui-egui/src/i18n/it.tsv index 4106fbee6..094452921 100644 --- a/crates/ui-egui/src/i18n/it.tsv +++ b/crates/ui-egui/src/i18n/it.tsv @@ -144,6 +144,7 @@ Apply to: Applica a: Whole document Tutto il documento This section Questa sezione + Selected text Testo selezionato Fill Riempimento Setting: Impostazione: 3-D 3D diff --git a/crates/ui-egui/src/i18n/ja.tsv b/crates/ui-egui/src/i18n/ja.tsv index 1790b3685..b32fcbc34 100644 --- a/crates/ui-egui/src/i18n/ja.tsv +++ b/crates/ui-egui/src/i18n/ja.tsv @@ -213,6 +213,7 @@ Use the Insert key to switch overtype on and off Insert キーで上書きのオンとオフを切り替える Whole document 文書全体 This section このセクション + Selected text 選択したテキスト Fill 塗りつぶし Setting: 種類: 3-D 立体 diff --git a/crates/ui-egui/src/i18n/ko.tsv b/crates/ui-egui/src/i18n/ko.tsv index c04327f54..f3b547b98 100644 --- a/crates/ui-egui/src/i18n/ko.tsv +++ b/crates/ui-egui/src/i18n/ko.tsv @@ -176,6 +176,7 @@ Use the Insert key to switch overtype on and off Insert 키를 눌러 겹쳐쓰기 켜기/끄기 Whole document 문서 전체 This section 이 구역 + Selected text 선택한 텍스트 Fill 채우기 Setting: 설정: 3-D 3차원 diff --git a/crates/ui-egui/src/i18n/pl.tsv b/crates/ui-egui/src/i18n/pl.tsv index 708abec1f..c548ad784 100644 --- a/crates/ui-egui/src/i18n/pl.tsv +++ b/crates/ui-egui/src/i18n/pl.tsv @@ -1329,6 +1329,7 @@ This font has no characters to show. Ta czcionka nie zawiera znaków do pokazania. This point forward Od tego miejsca This section Ta sekcja + Selected text Zaznaczony tekst Title from the document properties Tytuł z właściwości dokumentu Total Razem Track Changes Options Opcje śledzenia zmian diff --git a/crates/ui-egui/src/i18n/pt-br.tsv b/crates/ui-egui/src/i18n/pt-br.tsv index 3d0707257..d09943ac3 100644 --- a/crates/ui-egui/src/i18n/pt-br.tsv +++ b/crates/ui-egui/src/i18n/pt-br.tsv @@ -193,6 +193,7 @@ Use the Insert key to switch overtype on and off Usar a tecla Insert para ativar e desativar a sobrescrita Whole document Documento inteiro This section Esta seção + Selected text Texto selecionado Fill Cor de fundo Setting: Tipo: 3-D 3D diff --git a/crates/ui-egui/src/i18n/ru.tsv b/crates/ui-egui/src/i18n/ru.tsv index ee3b2a954..989ede1b9 100644 --- a/crates/ui-egui/src/i18n/ru.tsv +++ b/crates/ui-egui/src/i18n/ru.tsv @@ -1620,6 +1620,7 @@ preview Title Пример Apply to: Применить к: Whole document Ко всему документу This section К этому разделу + Selected text К выделенному тексту Fill Заливка Setting: Тип: 3-D Объёмная diff --git a/crates/ui-egui/src/i18n/sr-latn.tsv b/crates/ui-egui/src/i18n/sr-latn.tsv index 4f2c81e60..667269747 100644 --- a/crates/ui-egui/src/i18n/sr-latn.tsv +++ b/crates/ui-egui/src/i18n/sr-latn.tsv @@ -270,6 +270,7 @@ Use the Insert key to switch overtype on and off Taster Insert uključuje i isključuje prepisivanje Whole document Ceo dokument This section Ovaj odeljak + Selected text Izabrani tekst Fill Ispuna Setting: Vrsta: 3-D 3D diff --git a/crates/ui-egui/src/i18n/sr.tsv b/crates/ui-egui/src/i18n/sr.tsv index b625c5c92..e2c42abe6 100644 --- a/crates/ui-egui/src/i18n/sr.tsv +++ b/crates/ui-egui/src/i18n/sr.tsv @@ -274,6 +274,7 @@ Use the Insert key to switch overtype on and off Тастер Insert укључује и искључује преписивање Whole document Цео документ This section Овај одељак + Selected text Изабрани текст Fill Испуна Setting: Врста: 3-D 3D diff --git a/crates/ui-egui/src/i18n/sv.tsv b/crates/ui-egui/src/i18n/sv.tsv index ce3ad8003..e3c6cf85a 100644 --- a/crates/ui-egui/src/i18n/sv.tsv +++ b/crates/ui-egui/src/i18n/sv.tsv @@ -191,6 +191,7 @@ Use the Insert key to switch overtype on and off Använd Insert-tangenten för att slå på och av överskrivning Whole document Hela dokumentet This section Det här avsnittet + Selected text Markerad text Fill Fyllning Setting: Inställning: 3-D 3D diff --git a/crates/ui-egui/src/i18n/tr.tsv b/crates/ui-egui/src/i18n/tr.tsv index 45313d948..01b35e7de 100644 --- a/crates/ui-egui/src/i18n/tr.tsv +++ b/crates/ui-egui/src/i18n/tr.tsv @@ -1619,6 +1619,7 @@ preview Title Örnek Apply to: Uygula: Whole document Tüm belge This section Bu bölüm + Selected text Seçili metin Fill Dolgu Setting: Ayar: 3-D 3B diff --git a/crates/ui-egui/src/i18n/uk.tsv b/crates/ui-egui/src/i18n/uk.tsv index 81789a053..6ead0ad94 100644 --- a/crates/ui-egui/src/i18n/uk.tsv +++ b/crates/ui-egui/src/i18n/uk.tsv @@ -195,6 +195,7 @@ Use the Insert key to switch overtype on and off Клавіша Insert вмикає та вимикає режим заміни Whole document Увесь документ This section Цей розділ + Selected text До виділеного тексту Fill Колір заливки Setting: Тип: 3-D Об'ємна diff --git a/crates/ui-egui/src/i18n/vi.tsv b/crates/ui-egui/src/i18n/vi.tsv index c511d4d4c..f23a2ba8a 100644 --- a/crates/ui-egui/src/i18n/vi.tsv +++ b/crates/ui-egui/src/i18n/vi.tsv @@ -141,6 +141,7 @@ Apply to: Áp dụng cho: Whole document Toàn bộ tài liệu This section Phần này + Selected text Văn bản đã chọn Fill Tô nền Setting: Thiết lập: 3-D 3-D diff --git a/crates/ui-egui/src/i18n/zh-hans.tsv b/crates/ui-egui/src/i18n/zh-hans.tsv index e420d2bcd..be03d290f 100644 --- a/crates/ui-egui/src/i18n/zh-hans.tsv +++ b/crates/ui-egui/src/i18n/zh-hans.tsv @@ -225,6 +225,7 @@ Use the Insert key to switch overtype on and off 用 Insert 键切换改写模式 Whole document 整篇文档 This section 本节 + Selected text 所选文字 Fill 填充 Setting: 设置: 3-D 三维 diff --git a/crates/ui-egui/src/i18n/zh-hant.tsv b/crates/ui-egui/src/i18n/zh-hant.tsv index 0ef0c9dba..cd1ec4dc5 100644 --- a/crates/ui-egui/src/i18n/zh-hant.tsv +++ b/crates/ui-egui/src/i18n/zh-hant.tsv @@ -214,6 +214,7 @@ Use the Insert key to switch overtype on and off 使用 Insert 鍵切換取代模式 Whole document 整份文件 This section 本節 + Selected text 選取的文字 Fill 填滿 Setting: 設定: 3-D 立體 diff --git a/docs/ui-parity.md b/docs/ui-parity.md index eb3fbb08b..ef08f2f0d 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-18 · **Change:** trivial (Different First Page headers reachable from page 1, #2826; previously trivial: a copied equation also goes in the clipboard's MathML formats, #1297; previously trivial: Read Mode keeps the keyboard, shows whole columns and keeps text clear of the page-turn bar, #1772); previously trivial: font list entries of tall-ascent fonts sit in their row, not cut off, #2570); previously trivial: Split Cells and Convert Text to Table dialogs follow Word's merged-cell limits and Initial column width, #2592; previously trivial: a new source's Tag is made like Word's, three letters of the author and two digits of the year with a number added when taken, and an edit keeps the Tag, #2094; previously trivial: Web Layout text fills the window width, #1876; previously trivial: Language dialog keeps inherited proofing language and no-proof choices (#2717)); previously trivial: comment balloons readable in the dark theme, #2045; previously trivial (Modify Table Style formats every table style part, #714); previously trivial: a click on the ribbon chrome hands the keyboard to the page in the same frame, #1093 follow-up; previously trivial: Page Setup vertical alignment, #1320; previously trivial: the app opens on the Home tab every launch, as in Word; 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-18 · **Change:** trivial (Columns on selected text, #2024; previously trivial: Different First Page headers reachable from page 1, #2826; previously trivial: a copied equation also goes in the clipboard's MathML formats, #1297; previously trivial: Read Mode keeps the keyboard, shows whole columns and keeps text clear of the page-turn bar, #1772); previously trivial: font list entries of tall-ascent fonts sit in their row, not cut off, #2570); previously trivial: Split Cells and Convert Text to Table dialogs follow Word's merged-cell limits and Initial column width, #2592; previously trivial: a new source's Tag is made like Word's, three letters of the author and two digits of the year with a number added when taken, and an edit keeps the Tag, #2094; previously trivial: Web Layout text fills the window width, #1876; previously trivial: Language dialog keeps inherited proofing language and no-proof choices (#2717)); previously trivial: comment balloons readable in the dark theme, #2045; previously trivial (Modify Table Style formats every table style part, #714); previously trivial: a click on the ribbon chrome hands the keyboard to the page in the same frame, #1093 follow-up; previously trivial: Page Setup vertical alignment, #1320; previously trivial: the app opens on the Home tab every launch, as in Word; 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 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 @@ -71,6 +71,7 @@ The Language dialog reads effective character formatting, including document def | 2026-10-16 | trivial | Font list: each entry's capitals are centred in its row, so a font with a very tall ascent (Amiri, Amiri Quran) no longer sits low or is cut in half, and the list reads as one even list (#2570) | | 2026-10-16 | trivial | Read Mode keeps the keyboard on entering, shows only whole columns, centred, and ends them above the page-turn bar (#1772) | | 2026-10-16 | trivial | Comment and revision balloons are readable in the dark theme: the card, the markup area, the author stripes and the leader lines come from theme tokens, and the text reaches 4.5:1 (#2045) | +| 2026-10-15 | trivial | Layout › Columns works on the selected text, as in Word: section breaks around it, the rest of the document unchanged; More Columns… adds "Selected text" to Apply to and picks it when text is selected (#2024) | | 2026-10-13 | trivial | New/Modify Table Style: No Border on a part that inherits its lines hides them; emptying the font box makes the part's font inherited again (#1288) | | 2026-10-14 | trivial | Tab stops edited on the ruler: corner kind selector, click to set, drag to move or off to clear, per-kind markers mirrored for RTL; each gesture one `para.tabs` call and one undo step; Rulers 85% → 90% | | 2026-10-12 | trivial | Tables on the page: one selected cell (Select Cell, or a click along its left edge) is a cell selection when its border is dragged: the border moves in its row only (#962) |