mirror of
https://github.com/storytold/wordcraft.git
synced 2026-10-19 02:10:15 -05:00
* #1230: Insert Caption on pictures and shapes; Page Setup header and footer options - Right-clicking a picture, shape, text box or chart offers Insert Caption…, as in Word. - references.caption with a picture or shape selected puts the caption next to the paragraph holding it, in the same cell or text box (it used to go after the whole table). - The Caption dialog proposes Figure for a picture selected in a table, not Table. - Page Setup: Headers and footers check boxes (Different Odd & Even Pages, Different First Page), applied on OK only if changed, in one undo step; a title page can go unnumbered. - Tests: engine caption_for_an_object_in_a_cell_stays_in_the_cell; kittest right_click_on_a_picture_offers_insert_caption and page_setup_different_first_page_leaves_the_title_page_unnumbered. - Docs: ui-parity, target-app-parity, ROADMAP progress log. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01P8veLveuUsJJm6UEcQPEuW * ui-parity: keep Insert Caption on the context-menu row --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
7 files changed
+179
-11
No files matched your search
@@ -181,6 +181,7 @@ Imported Word 97–2003 `.doc`/`.dot` files now ask for a supported DOCX target
|
||||
| 2026-10-17 | Insert › Drop Cap is a menu (None, Dropped, In margin, Drop Cap Options…); the dialog sets position, font, lines to drop and distance; letters in the margin; DOCX keeps them (#2601). |
|
||||
| 2026-10-16 | An otherwise empty section starting a new page retains its paragraph line before a continuous section, matching an original native Word control. |
|
||||
| 2026-10-16 | Equations from Word files keep all their content (#1273): math straight in the body or a table cell, math behind `mc:AlternateContent Requires="m"`, runs inside hyperlinks, simple fields and markup-compatibility blocks within an equation, and `w:sym`/Symbol-font characters in math; operators, delimiters and accents the math font lacks come from a fallback font. Every ECMA-376 §22.1 element is pinned by a coverage test. |
|
||||
| 2026-10-16 | Captions and title pages (#1230): Insert Caption… on the right-click menu of pictures, shapes, text boxes and charts, the caption going under (or over) the object in its own cell or text box; Page Setup's Different First Page and Different Odd & Even Pages check boxes, so a title page can go without its page number |
|
||||
|---|---|
|
||||
| 2026-10-16 | Save Changes gives assistive technology a named alert dialog with the loss warning and any save failure; visual layout and save policy unchanged (#1015). Native screen-reader verification remains pending. |
|
||||
| 2026-10-15 | The font family caches are bounded (4096 families for text, 1024 for math, names over 256 bytes aren't remembered), so a document naming thousands of missing fonts can't grow memory; equation fonts are looked up again after a font is added or removed instead of keeping the old choice (#1354) |
|
||||
@@ -547,6 +548,7 @@ Imported Word 97–2003 `.doc`/`.dot` files now ask for a supported DOCX target
|
||||
| 2026-10-17 | minor | Progress log: Drop Cap Options (#2601) |
|
||||
| 2026-10-16 | trivial | An otherwise empty section at page start retains its line before continuous content; later closing marks stay quiet |
|
||||
| 2026-10-16 | trivial | Progress log: equations from Word files keep their content (#1273) |
|
||||
| 2026-10-16 | trivial | Progress log: Insert Caption… on objects and Page Setup header and footer options (#1230) |
|
||||
| 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 |
|
||||
|
||||
@@ -837,14 +837,24 @@ fn caption(s: &mut Session, v: &Value) -> CmdResult {
|
||||
if !text.is_empty() {
|
||||
para.insert_text(para.len(), &format!(": {text}"), &CharProps::default())?;
|
||||
}
|
||||
// Next to the caret's top-level block: a paragraph, or the whole table it is in.
|
||||
// A selected picture, shape or chart: next to the paragraph holding it, in the same cell or
|
||||
// text box (#1230). Otherwise next to the caret's top-level block: a paragraph, or the whole
|
||||
// table it is in.
|
||||
let f = s.sel.focus.clone();
|
||||
let top = f.path.0.first().copied().unwrap_or(0) as usize;
|
||||
let i = if above { top } else { top.saturating_add(1) };
|
||||
s.doc.insert_block(f.story, &Path::top(i), Block::Para(para))?;
|
||||
let at = match super::objects::object_selection(s) {
|
||||
Some((pos, _)) => {
|
||||
let k = pos.path.last();
|
||||
pos.path.with_last(if above { k } else { k.saturating_add(1) })
|
||||
}
|
||||
None => {
|
||||
let top = f.path.0.first().copied().unwrap_or(0) as usize;
|
||||
Path::top(if above { top } else { top.saturating_add(1) })
|
||||
}
|
||||
};
|
||||
s.doc.insert_block(f.story, &at, Block::Para(para))?;
|
||||
update_seq(s)?;
|
||||
let off = s.doc.para(f.story, &Path::top(i)).map_or(0, |p| p.len());
|
||||
s.sel = Selection::caret(Pos { story: f.story, path: Path::top(i), off });
|
||||
let off = s.doc.para(f.story, &at).map_or(0, |p| p.len());
|
||||
s.sel = Selection::caret(Pos { story: f.story, path: at, off });
|
||||
sel_result(s)
|
||||
}
|
||||
|
||||
@@ -1377,4 +1387,37 @@ mod tests {
|
||||
.unwrap();
|
||||
assert_eq!(cached_toc_tabs(&s), vec![("TOC2".into(), vec![stop(24.0)])]);
|
||||
}
|
||||
|
||||
/// #1230: a selected picture or shape in a table cell gets its caption in the cell, under
|
||||
/// (or over) the paragraph that holds it, not after the whole table; the number counts on.
|
||||
#[test]
|
||||
fn caption_for_an_object_in_a_cell_stays_in_the_cell() {
|
||||
let mut s = Session::new(wordcraft_doc::Document::new());
|
||||
s.run("insert.table", &json!({"rows": 1, "cols": 2})).unwrap();
|
||||
s.run("insert.shape", &json!({"kind": "ellipse", "width": 60, "height": 40})).unwrap();
|
||||
let (at, _) = crate::cmd::objects::selected(&s).expect("the shape is selected");
|
||||
let (table, r, c) = at.path.cell().expect("the shape is in a cell");
|
||||
s.run("references.caption", &json!({"label": "Figure", "text": "Kiln"})).unwrap();
|
||||
let cell = |s: &Session| s.doc.table(StoryRef::Body, &table).unwrap().rows[r].cells[c].blocks.clone();
|
||||
let texts: Vec<String> = cell(&s).iter().filter_map(|b| b.as_para()).map(|p| p.plain_text()).collect();
|
||||
assert_eq!(texts.len(), 2, "{texts:?}");
|
||||
assert_eq!(texts[1], "Figure 1: Kiln", "the caption is under the shape, in the cell");
|
||||
assert_eq!(s.doc.body.iter().filter(|b| b.as_para().is_some_and(|p| p.props.style.as_deref() == Some("Caption"))).count(), 0);
|
||||
// Above: over the shape's paragraph, and it is Figure 1 (the one below becomes 2).
|
||||
let end = Pos { off: at.off + wordcraft_doc::para::OBJ.len_utf8(), ..at.clone() };
|
||||
s.sel = Selection { anchor: at.clone(), focus: end };
|
||||
assert!(crate::cmd::objects::object_selection(&s).is_some());
|
||||
s.run("references.caption", &json!({"label": "Figure", "text": "Lid", "position": "above"})).unwrap();
|
||||
let texts: Vec<String> = cell(&s).iter().filter_map(|b| b.as_para()).map(|p| p.plain_text()).collect();
|
||||
assert_eq!(texts.len(), 3, "{texts:?}");
|
||||
assert_eq!((texts[0].as_str(), texts[2].as_str()), ("Figure 1: Lid", "Figure 2: Kiln"));
|
||||
// A picture outside a table: the caption goes right after its paragraph.
|
||||
let mut s = Session::new(wordcraft_doc::Document::from_text("Before\nAfter"));
|
||||
s.run("select.text", &json!({"text": "Before"})).unwrap();
|
||||
s.run("caret.end", &json!({})).unwrap();
|
||||
s.run("insert.shape", &json!({"kind": "ellipse", "width": 60, "height": 40})).unwrap();
|
||||
s.run("references.caption", &json!({})).unwrap();
|
||||
let texts: Vec<String> = s.doc.body.iter().filter_map(|b| b.as_para()).map(|p| p.plain_text()).collect();
|
||||
assert_eq!(texts.get(1).map(String::as_str), Some("Figure 1"), "{texts:?}");
|
||||
}
|
||||
}
|
||||
@@ -3121,6 +3121,10 @@ fn context_menu(app: &mut WordApp, ui: &mut Ui) {
|
||||
item(ui, app, "Reset Picture", "picture.reset", json!({}));
|
||||
item(ui, app, "Size and Crop…", "ui.tab", json!({"tab": "Picture Format"}));
|
||||
}
|
||||
if picture || object {
|
||||
// A numbered caption under (or over) the picture, shape, text box or chart (#1230).
|
||||
item(ui, app, "Insert Caption…", "references.caption", json!({}));
|
||||
}
|
||||
if object {
|
||||
ui.menu_button(tl!("Wrap Text"), |ui| crate::ribbon::wrap_text_items(ui, app));
|
||||
}
|
||||
@@ -4827,6 +4831,46 @@ mod tests {
|
||||
assert!(h.state().session.doc.body.iter().all(|b| b.as_table().is_none()));
|
||||
}
|
||||
|
||||
/// #1230: right-clicking a picture offers Insert Caption…, as in Word. For a picture in a
|
||||
/// table cell the dialog proposes a Figure caption (not Table), and the caption goes under
|
||||
/// the picture in that cell.
|
||||
#[test]
|
||||
fn right_click_on_a_picture_offers_insert_caption() {
|
||||
let mut h = table_window();
|
||||
h.state_mut().run("caret.end", json!({})).unwrap();
|
||||
let mut png = Vec::new();
|
||||
let img = image::RgbaImage::from_pixel(40, 20, image::Rgba([30, 90, 200, 255]));
|
||||
image::DynamicImage::ImageRgba8(img).write_to(&mut std::io::Cursor::new(&mut png), image::ImageFormat::Png).unwrap();
|
||||
h.state_mut().run("insert.picture", json!({"data": wordcraft_engine::cmd::insert::base64_encode(&png), "width": 60})).unwrap();
|
||||
let pos = wordcraft_engine::cmd::objects::selected(&h.state().session).unwrap().0;
|
||||
h.state_mut().session.sel = wordcraft_engine::Selection::caret(Pos::body(1, 0));
|
||||
steps(&mut h, 4);
|
||||
let rect = h.state_mut().session.layout().object(&pos, 0).expect("the picture is laid out").rect;
|
||||
let at = crate::frame::to_screen(h.state().canvas.page_rects[0], h.state().canvas.scale, rect).center();
|
||||
right_click(&mut h, at);
|
||||
assert!(wordcraft_engine::cmd::objects::object_selection(&h.state().session).is_some(), "the right-click selected the picture");
|
||||
assert_menu(&h, &["Insert Caption…", "Wrap Text"], true);
|
||||
pick(&mut h, &["Insert Caption…"]);
|
||||
let form = match &h.state().dialog {
|
||||
Some(crate::dialogs::Dialog::Refs(d)) => match &**d {
|
||||
crate::dialogs_refs::RefsDialog::Caption(f) => f.clone(),
|
||||
d => panic!("not the caption dialog: {d:?}"),
|
||||
},
|
||||
d => panic!("no caption dialog: {d:?}"),
|
||||
};
|
||||
assert_eq!(form.label, "Figure", "a picture in a table gets a figure caption");
|
||||
h.state_mut().dialog = None;
|
||||
h.state_mut().run("references.caption", form.params()).unwrap();
|
||||
let (table, r, c) = pos.path.cell().unwrap();
|
||||
let cell: Vec<String> = h.state().session.doc.table(StoryRef::Body, &table).unwrap().rows[r].cells[c]
|
||||
.blocks
|
||||
.iter()
|
||||
.filter_map(|b| b.as_para())
|
||||
.map(|p| p.plain_text())
|
||||
.collect();
|
||||
assert_eq!(cell.last().map(String::as_str), Some("Figure 1"), "{cell:?}");
|
||||
}
|
||||
|
||||
/// With just the caret in a cell, right-click › Delete › Delete Table removes the table, in
|
||||
/// one undo step (#1198: the menu offered only Delete Cells there).
|
||||
#[test]
|
||||
|
||||
@@ -269,6 +269,10 @@ pub enum Dialog {
|
||||
message: String,
|
||||
/// Vertical alignment (`layout.verticalAlign`, applied only if changed).
|
||||
valign: wordcraft_doc::section::PageVAlign,
|
||||
/// Headers and footers: different odd and even pages (the document's) and a different
|
||||
/// first page (the section's), applied only if changed (#1230).
|
||||
odd_even: bool,
|
||||
first_page: bool,
|
||||
},
|
||||
Link {
|
||||
url: String,
|
||||
@@ -1063,6 +1067,8 @@ impl Dialog {
|
||||
grid_was: grid,
|
||||
rtl: sp.rtl,
|
||||
valign: sp.valign,
|
||||
odd_even: app.session.doc.settings.even_odd_headers,
|
||||
first_page: sp.title_page,
|
||||
}
|
||||
}
|
||||
"link" => {
|
||||
@@ -2083,7 +2089,24 @@ fn body(app: &mut WordApp, ui: &mut Ui, d: &mut Dialog) -> bool {
|
||||
}
|
||||
ok || cancel
|
||||
}
|
||||
Dialog::PageSetup { top, bottom, left, right, landscape, mirror, rtl, grid, grid_was, paper, paper_was, paper_custom, message, valign } => {
|
||||
Dialog::PageSetup {
|
||||
top,
|
||||
bottom,
|
||||
left,
|
||||
right,
|
||||
landscape,
|
||||
mirror,
|
||||
rtl,
|
||||
grid,
|
||||
grid_was,
|
||||
paper,
|
||||
paper_was,
|
||||
paper_custom,
|
||||
message,
|
||||
valign,
|
||||
odd_even,
|
||||
first_page,
|
||||
} => {
|
||||
ui.label(egui::RichText::new(tl!("Margins")).font(semibold(12.5)));
|
||||
egui::Grid::new("ps").num_columns(4).show(ui, |ui| {
|
||||
ui.label(tl!("Top:"));
|
||||
@@ -2130,6 +2153,10 @@ fn body(app: &mut WordApp, ui: &mut Ui, d: &mut Dialog) -> bool {
|
||||
ui.label(egui::RichText::new(message.as_str()).small().color(ui.visuals().error_fg_color));
|
||||
}
|
||||
page_valign_combo(ui, valign);
|
||||
// Word's Page Setup › Layout: a title page without the page number (#1230).
|
||||
ui.label(egui::RichText::new(tl!("Headers and footers")).font(semibold(12.5)));
|
||||
ui.checkbox(odd_even, tl!("Different Odd & Even Pages"));
|
||||
ui.checkbox(first_page, tl!("Different First Page"));
|
||||
let (mut ok, cancel) = buttons(ui, tl!("OK"));
|
||||
if ok {
|
||||
// Everything OK changes is one undo step; a refused command applies nothing.
|
||||
@@ -2157,6 +2184,12 @@ fn body(app: &mut WordApp, ui: &mut Ui, d: &mut Dialog) -> bool {
|
||||
if *valign != wordcraft_engine::cmd::page::sect(&app.session).valign {
|
||||
commands.push(("layout.verticalAlign", json!({"value": page_valign_value(*valign)})));
|
||||
}
|
||||
if *odd_even != app.session.doc.settings.even_odd_headers {
|
||||
commands.push(("layout.differentOddEven", json!({"value": *odd_even})));
|
||||
}
|
||||
if *first_page != wordcraft_engine::cmd::page::sect(&app.session).title_page {
|
||||
commands.push(("layout.differentFirstPage", json!({"value": *first_page})));
|
||||
}
|
||||
run_as_one_step(app, commands)
|
||||
});
|
||||
if let Err(e) = result {
|
||||
@@ -4795,4 +4828,47 @@ mod tests {
|
||||
}
|
||||
assert_eq!(wordcraft_engine::cmd::page::sect(&h.state().session).valign, PageVAlign::Justify);
|
||||
}
|
||||
|
||||
/// #1230: Page Setup's Different first page check box gives the title page no page number
|
||||
/// (its footer is the empty first-page one), in one undo step.
|
||||
#[test]
|
||||
fn page_setup_different_first_page_leaves_the_title_page_unnumbered() {
|
||||
use egui_kittest::kittest::Queryable;
|
||||
let mut app = app_with("Title");
|
||||
app.run("insert.pageBreak", json!({})).unwrap();
|
||||
app.run("text.insert", json!({"text": "Body"})).unwrap();
|
||||
app.run("insert.pageNumber", json!({"position": "bottom", "align": "center"})).unwrap();
|
||||
let _ = app.run("insert.closeHeader", json!({}));
|
||||
assert!(app.session.layout().pages[0].footer_story.is_some(), "page 1 is numbered");
|
||||
app.dialog = Dialog::open("pageSetup", &mut app);
|
||||
assert!(matches!(app.dialog, Some(Dialog::PageSetup { first_page: false, odd_even: false, .. })));
|
||||
let depth = app.session.undo_depth();
|
||||
let mut h = egui_kittest::Harness::builder().with_size(egui::vec2(1440.0, 900.0)).build_ui_state(
|
||||
|ui, app: &mut WordApp| {
|
||||
let ctx = ui.ctx().clone();
|
||||
app.logic(&ctx);
|
||||
app.ui(ui);
|
||||
},
|
||||
app,
|
||||
);
|
||||
for _ in 0..4 {
|
||||
h.step();
|
||||
}
|
||||
assert!(h.query_by_label("Different Odd & Even Pages").is_some(), "the dialog has both check boxes");
|
||||
h.get_by_label("Different First Page").click();
|
||||
for _ in 0..2 {
|
||||
h.step();
|
||||
}
|
||||
h.get_by_role_and_label(egui::accesskit::Role::Button, "OK").click();
|
||||
for _ in 0..4 {
|
||||
h.step();
|
||||
}
|
||||
let s = &mut h.state_mut().session;
|
||||
assert!(wordcraft_engine::cmd::page::sect(s).title_page);
|
||||
assert!(!s.doc.settings.even_odd_headers, "left as it was");
|
||||
assert_eq!(s.undo_depth(), depth + 1);
|
||||
let pages = &s.layout().pages;
|
||||
assert!(pages[0].footer_story.is_none(), "the title page shows no page number");
|
||||
assert!(pages[1].footer_story.is_some(), "the next page keeps its number");
|
||||
}
|
||||
}
|
||||
@@ -240,8 +240,9 @@ impl CaptionForm {
|
||||
labels.push(l.clone());
|
||||
}
|
||||
}
|
||||
// In a table the caption is for the table.
|
||||
let in_table = app.session.sel.focus.path.depth() > 0
|
||||
// In a table the caption is for the table, unless a picture or shape in it is selected.
|
||||
let in_table = wordcraft_engine::cmd::objects::object_selection(&app.session).is_none()
|
||||
&& app.session.sel.focus.path.depth() > 0
|
||||
&& app
|
||||
.session
|
||||
.doc
|
||||
|
||||
@@ -64,7 +64,7 @@ full parity for the area.
|
||||
| Page layout and sections | 7 | 70% | 72% | 12–20 | Margins, size, columns, breaks, page borders, line numbers, vertical alignment, drop caps, hyphenation. Columns don't balance; RTL sections missing. Vertical sections (tbRl/btLr) landed (#491). Detail: [layout-parity.md](layout-parity.md). |
|
||||
| Headers, footers, page numbers, fields | 5 | 70% | 70% | 8–12 | First/even/odd, link to previous, TOC as an updatable field (#52), cross-references to real bookmarks (#227). Field coverage is a subset of Word's ~90 field codes; Field dialog missing. |
|
||||
| Footnotes and endnotes | 3 | 60% | 60% | 3–5 | Placed and editable, numbers from Word files (#103); long notes continue onto the next page under the continuation separator (#352). Footnote and Endnote dialog with options per section: below text, end of section, number format, start at, restart each section or page, custom mark, convert (#385). |
|
||||
| References (TOC, citations, bibliography, captions, index, TOA) | 5 | 65% | 60% | 12–20 | Working first versions, Zotero integration (#189), Zotero/Mendeley `ADDIN` fields round-trip. Caption, Index, Mark Entry, Custom TOC and Create/Edit Source dialogs; updates honour the TOC (`\o \u \t \f \n \p \h`), INDEX (`\c \e \h \r`), XE (`\b \i \t \r`) and SEQ/STYLEREF chapter switches (#402). All 12 of Word's bibliography styles (APA, MLA, Chicago, IEEE, Harvard, ISO 690 author-date and numerical, Turabian, GB/T 7714, GOST name and title sort, SIST02; #383); bibliography sources not yet in DOCX (#169 open). |
|
||||
| References (TOC, citations, bibliography, captions, index, TOA) | 5 | 65% | 60% | 12–20 | Working first versions, Zotero integration (#189), Zotero/Mendeley `ADDIN` fields round-trip. Caption, Index, Mark Entry, Custom TOC and Create/Edit Source dialogs; updates honour the TOC (`\o \u \t \f \n \p \h`), INDEX (`\c \e \h \r`), XE (`\b \i \t \r`) and SEQ/STYLEREF chapter switches (#402); Insert Caption… on the right-click menu of pictures, shapes, text boxes and charts (#1230). All 12 of Word's bibliography styles (APA, MLA, Chicago, IEEE, Harvard, ISO 690 author-date and numerical, Turabian, GB/T 7714, GOST name and title sort, SIST02; #383); bibliography sources not yet in DOCX (#169 open). |
|
||||
| Review (proofing UI, comments, track changes, compare, protect) | 8 | 72% | 75% | 15–25 | Comment balloons, tracked insert/delete incl. paragraph marks (#244, #125), Compare / Combine with comparison settings (case, white space, formatting, tables, word or character level, #412), Restrict Editing pane with formatting and editing restrictions, exceptions for everyone and a password (#412). Formatting revisions (#41): kept in DOCX, recorded while tracking, accepted/rejected, "Formatted: …" balloons and change bars. Moves (#400): kept in DOCX, recorded on a tracked cut and paste, accepted/rejected at either end, green markup and "Moved from/to" balloons. Show revisions in balloons takes tracked deletions in the body out of the text into "Deleted: …" balloons (insertions stay inline, as in Word; deletions in headers, footers, notes and text boxes, and in print and PDF, stay inline). Missing: drag-and-drop moves, insert/delete balloons, Translate, Block Authors, modern comment threads/mentions. Lowered: these gaps are confirmed by source (no `rPrChange` anywhere). |
|
||||
| Proofing (spelling, grammar, thesaurus, languages) | 5 | 44% | (in Review) | 20–40 | English, Polish, Portuguese, Italian and Czech (system Hunspell, #1048), chosen per run from its language (`w:lang`): English word list (Moby plus our supplement of modern words such as "desktop", #520) and rule-based grammar; Polish against the installed Hunspell dictionary through a Hunspell-style affix engine, Polish grammar rules and TeX hyphenation patterns (2026-10-10); Portuguese (pt-BR) against the installed VERO Hunspell dictionary — the affix engine reads its `FLAG UTF-8` flags, continuation classes and forbidden spellings — with accent-first suggestions, conservative grammar rules and TeX patterns (2026-10-12); Italian (it-IT) against the installed Hunspell dictionary through the same affix engine, with accent-first suggestions and conservative grammar rules. Other languages are left unmarked. The custom dictionary persists across sessions (#40). Word ships 120 proofing tools for ~50 languages plus Editor (style refinements, similarity). Issues #25, #40, #100. |
|
||||
| Mailings | 2 | 70% | 70% | 6–10 | Mail merge from CSV, fields, rules (IF, SKIPIF, NEXT, NEXTIF, evaluated alike in preview and merge), fields in headers/footers (one section per letter), preview, envelopes, labels. Select Recipients takes a typed list or CSV (#247); no Excel/Outlook/Contacts data sources, no email merge. |
|
||||
@@ -258,6 +258,7 @@ can be used without committing Word output.
|
||||
| 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-15 | trivial | Pictures, shapes, text boxes: Picture Styles, Picture Border and Artistic Effects draw again from the picture under them, so a second style replaces the first instead of framing the frame (Thick Frame then Simple Frame is a simple frame), a border replaces the border and keeps the style, a style replaces the border, and an effect replaces the effect (None removes it), as in Word; transparency was already absolute (#2052) |
|
||||
| 2026-10-16 | trivial | References: Insert Caption… on the right-click menu of pictures, shapes, text boxes and charts, captions in the object's own cell (#1230); no percentage change |
|
||||
| 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 |
|
||||
|
||||
+2
-1
@@ -28,7 +28,7 @@ Document title opens Rename Document; the account badge opens Options › Genera
|
||||
| Keyboard shortcuts | **152** commands bound in `CommandSpec::key`, 172 key combinations (counted from the registry on Linux, 2026-10-13); styles, spacing, symbols, fields, views, mail merge (#330); field keys Alt+F9, Shift+F9, Ctrl+Shift+F9, Ctrl+F11, Ctrl+Shift+F11, Shift+F11 (#339), Next Field on Alt+F1 and Alt+Shift+F1 (Word's other keys for it) since F11 went to full screen (#980; GNOME and KDE may keep Alt+F1 for their own overview or application menu, and then Next Field has no key there; Previous Field keeps Shift+F11); Alt+Shift+arrows (move paragraphs, promote/demote), Shift+F5 Go Back and Alt+X (#343); Outline view: Alt+Shift+Plus/Minus expand/collapse, Alt+Shift+1–9 and Alt+Shift+A Show Level (#493); Alt+Q to "Tell me" command search (#819, not on macOS); on macOS, Word for Mac's own keys: ⌘⇧S Save As, ⌘⌥E Insert Endnote, ⌘⌥A New Comment, ⌃H Replace, ⌘⌥R collapse the ribbon. The Spike: Ctrl+F3 cuts to it, Ctrl+Shift+F3 inserts it and empties it (⌘F3, ⌘⇧F3 on macOS). Missing: en dash on the numeric keypad Insert Hyperlink has Word's Link to: Existing File or Web Page / Place in This Document (top of the document, headings, bookmarks) (#1234). | ~250 default shortcuts | ~70% | 2–4 |
|
||||
| Modal dialogs | **59** (`Dialog` enum: Discard Recovery, Font, Enclose Characters, Paragraph, Tabs, Borders and Shading, Find, Go To, Insert Table, Table Properties, Page Setup, Link, Bookmark, Word Count, Zoom, Watermark, New Style, Modify Style, Manage Styles, Table Style, Commands, Icons, Templates and Add-ins, Organizer, Compare and Combine, AutoCorrect Options, Paste Special, About, Save Changes, Password, Encrypt with Password, Define New Multilevel List, Track Changes Options; Columns, Symbol with Special Characters, Field (#321); Field code (#339); mail merge Recipients, Insert Merge Field, Find Recipient, Rules, Match Fields, Check for Errors; Caption with New Label and Numbering, Index, Mark Index Entry, Custom Table of Contents with Options and TOC styles, Source Manager, Create/Edit Source (#402); Cross-reference (#916); Drop Cap with Position, Font, Lines to drop and Distance from text, opened from Insert › Drop Cap › Drop Cap Options… (#2601); Line Numbers, Hyphenation with Manual Hyphenation, Language, Envelopes, Labels (#407); Page Setup has a Paper size list and custom width and height, opened from Layout › Size › More Paper Sizes… (#1433); every dialog's close control is a box at the title bar's right end, the full title-bar height and 1.6× as wide, all of it clickable, red with a white ✕ on hover as a Windows caption button (#1885); Split Cells (columns, rows, merge before split; defaults follow the selection as in Word: twice its columns, its rows, Merge offered for several cells or one merged across columns; a merged cell splits into 1 to its height rows, its height or 1 × 1 cutting it back into rows, more refused with Word's message; parts split across columns are equal and other rows keep their grid lines) and Convert Text to Table (live column count, read-only rows; Initial column width Auto or 0 to 22 in, AutoFit to contents/window; Paragraphs/Commas/Tabs/Other "-" separators) (#2592)) plus panes and menus for the rest | ~100 (Options panes…) | ~45% | 11–19 |
|
||||
| Task panes (Navigation, Styles, Comments, Reviewing, Format Picture/Shape, Thesaurus, Accessibility, Clipboard, Selection) | Navigation (long headings truncate with an ellipsis instead of expanding the pane, #759), Styles, Comments (balloons and cards read in both themes, #2045), Thesaurus, Restrict Editing, Accessibility (grouped findings, jump-to, fixes) and Alt Text (#386), Read Aloud, Zotero; Style Inspector (#236); Clipboard pane (#264) open | all | 70% | 4–6 |
|
||||
| Context menus (right-click) | text, spelling suggestions, fields (Update Field, Edit Field…, Toggle Field Codes, #339; Update Field on a table of contents entry updates the table, #1197, and after a save and reopen, #2182); right-click selects a picture, shape or text box and offers Wrap Text (styles, Move with Text / Fix Position on Page, #513); tables, the items following the selection as in Word (Insert, Delete › Cells, Columns, Rows or Table whatever is selected (#1198), Merge and Split Cells, Distribute, AutoFit, Text Direction, Insert Caption…, Table Properties…; no Border Styles yet) | rich per context | 65% | 2–4 |
|
||||
| Context menus (right-click) | text, spelling suggestions, fields (Update Field, Edit Field…, Toggle Field Codes, #339; Update Field on a table of contents entry updates the table, #1197, and after a save and reopen, #2182); right-click selects a picture, shape or text box and offers Insert Caption… (#1230) and Wrap Text (styles, Move with Text / Fix Position on Page, #513); tables, the items following the selection as in Word (Insert, Delete › Cells, Columns, Rows or Table whatever is selected (#1198), Merge and Split Cells, Distribute, AutoFit, Text Direction, Insert Caption…, Table Properties…; no Border Styles yet) | rich per context | 65% | 2–4 |
|
||||
| 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); File › Close (Mod+W, Mod+F4) closes the document and keeps the window, showing no page, as Word does; the window's close button quits (#1712) | | 68% | 4–6 |
|
||||
| Rulers: indents, tabs, margins, table columns | indents draggable (one undo, #62); tab stops: corner selector (Left/Center/Right/Decimal/Bar), click to set, drag to move, drag off to clear, per-kind markers, Alt skips the snap, one undo step per gesture, double-click opens the Tabs dialog (#320); table column and row markers drag their borders (one undo) and in a cell the indents are measured from it (#700); the numbers thin out as the page shrinks on screen (every 1, 2, 4… units, never closer than 30 px, the small ticks never closer than 4 px, the text keeps its size), give way to an indent, tab or column marker on them, and the margins get ticks only; both rulers span the caret's page at its own scale in every layout (one column, One Page, Multiple Pages) and follow the caret to another page (#1878) | ✅ | 92% | 1 |
|
||||
| On-canvas objects: select, drag, resize handles, nudge | ✅ tested (#228); a picture or shape behind text can be dragged across the overlapping text (body and header/footer), whether or not it was selected first, while a click without movement still edits that text | ✅ | 75% | — |
|
||||
@@ -105,6 +105,7 @@ Footnote and endnote editing keeps body and note text at normal opacity, on ligh
|
||||
| 2026-10-15 | trivial | Spelling as you type: the word being typed has no red squiggle until the caret leaves it, by a key or a click; misspelled words already there keep theirs (#1552) |
|
||||
| 2026-10-15 | trivial | Text Highlight Color: No Color becomes the Highlight button's choice, as in Word, so the next click on the button removes highlighting instead of applying the colour picked before; the ribbon and the mini toolbar share one menu (#2105) |
|
||||
| 2026-10-16 | trivial | Body and active note text keep normal opacity while editing footnotes or endnotes (#2545); header/footer dimming is unchanged |
|
||||
| 2026-10-16 | trivial | Right-clicking a picture, shape, text box or chart offers Insert Caption…; a picture in a table gets a Figure caption in its cell. Page Setup has Headers and footers check boxes (Different Odd & Even Pages, Different First Page), applied on OK only if changed (#1230) |
|
||||
| 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-17 | minor | Insert › Drop Cap is a menu (None, Dropped, In margin, Drop Cap Options…) with a Drop Cap dialog (position, font, lines 1–10, distance 0–22 in), #2601; Modal dialogs 56 → 57 |
|
||||
| 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% |
|
||||
|
||||
Reference in new issue
Block a user