fix(ui): keep Page Setup fields bounded after long paste (#5696)

* M14: keep Page Setup margin and grid editors within their width (#5650)

Use the pinned egui DragValue clipping option for four margins and both grid counts. Canonical actual WordApp regression and 12 Page Setup controls pass; strict all-target UI Clippy and scoped rustfmt pass.

* M14: keep Page Setup paper-size editors within their width (#5650)

Apply the same bounded text editing to paper width and height while preserving their existing limits, units and speed. Actual Page Setup regression and existing controls pass.

* M14: regress Page Setup paste, blur and reopen widths (#5650)

Exercise all eight numeric fields through the actual WordApp harness. Baseline Bottom paste reproduced width growth from 354 to 2973 pt; the fixed canonical 12-test Page Setup suite passes. Private rendering hooks are excluded.

* M14: record bounded Page Setup number editors (#5650)

Add the focused UI fix to the progress and revision logs and status line. Readiness estimates remain unchanged.

* M14: note the focused Page Setup paste fix in gaps (#5650)

Record bounded numeric editing in the gap note, status and revision history; no ranked gap or readiness estimate changes.

* M14: document bounded Page Setup numeric input (#5650)

Update the UI parity note, status and revision history for long input scrolling inside the existing fields. Readiness estimates remain unchanged.

* M14: bound unfocused Page Setup margin values (#5650)

Keep the unfocused DragValue inside an allocated, clipped field without changing numeric parsing, units, speed or accepted values. Regress a finite long paste followed by Tab in all four margins. The actual baseline grew from 354 to 458.53125 pt after Tab; the canonical 13 Page Setup tests and scoped formatting checks pass, and the inspected fixed renders stay 354 pt. Hosted validation remains pending in draft PR #5696.

* M14: record unfocused Page Setup bounds (#5650)

Update the gap and UI parity notes for the accepted-value paste followed by Tab regression. Retain the existing progress history, value ranges and readiness estimates.

* M14: log the Page Setup blur regression (#5650)

Record the bounded unfocused margin display and accepted-value regression. Preserve every upstream progress entry and the previous validation history.

* M14: regress RTL margin re-entry (#5650)

Cover re-editing the visible portion of a wide blurred margin in the real Page Setup dialog under an asserted RTL fixture. Raw pointer events restore editing and accept a normal value; the existing thirteen tests remain unchanged. Fourteen Page Setup tests, package formatting, strict all-target UI Clippy and source-integrity checks passed. No production or dependency changes.
This commit is contained in:
Mex authored and GitHub committed 2026-11-08 01:51:01 +01:00
1 parent b29f3a8a2e
commit fa59ed0e8a
6 files changed
+176 -6

No files matched your search

+6
View File
@@ -9,6 +9,8 @@
> **Recipient CSV update:** 2026-10-27 · Complete quoted header records choose the delimiter (#4249); detection and regressions follow the current mailings module split. Recipient limits and readiness estimates are unchanged.
>
> **Headered CSV prefix update:** 2026-10-31 · Unquoted whitespace-only prefix lines are skipped before choosing recipient headers (#4249); quoted whitespace headers, headerless records, the shared CSV reader and recipient limits remain unchanged.
> **Page Setup update:** 2026-11-07 · Page Setup number boxes keep long input inside the field, including accepted margin values after focus leaves (#5650); accepted ranges and readiness estimates are unchanged.
> **Printing update:** 2026-10-12 · direct desktop printing through craft-print; details below.
> **Legacy form update:** 2026-10-22 · trivial: numeric XML references decode in field names and labels; readiness estimates unchanged.
@@ -464,6 +466,8 @@ Imported Word 97–2003 `.doc`/`.dot` files now ask for a supported DOCX target
| 2026-10-17 | Non-overlapping body floating tables clear header tables. Original native controls distinguish never-overlap from allowed overlap. |
| 2026-10-14 | Comment text being typed is no longer lost or misplaced (#1348): an edit or reply the document refuses (for example read-only protection) keeps the typed text in its field and says why on the status line; drafts belong to their document, so after New or Open a comment with the same number shows its own text; Save, Export, Print, Close, New and Open write pending comment text first; if the document refuses it, Save, Export and Print carry on with the stored comments and show why on the status line (a refused draft is dropped when Save is the only change), while Close, New and Open show the Save Changes prompt (Don't Save drops text the document refuses); Escape discards what was typed; in a document protected as read only or for forms the comment fields can't be typed in |
|---|---|
| 2026-11-07 | Page Setup also bounds the unfocused margin display after a long numeric paste; the accepted value is retained (#5650). |
| 2026-11-05 | Page Setup's margin, paper-size and document-grid number boxes scroll long text inside the field instead of enlarging the dialog (#5650). Readiness estimates unchanged. |
| 2026-10-29 | Print Markup discovers comment balloon anchors in one forward paragraph-object walk; anchor positions, filtering and the existing cap stay unchanged (#3614) |
| 2026-10-26 | Spelling & Grammar opens a right-side Proofing task pane observed in Word for Windows: sentence context, directly clickable suggestion cards and ignore/dictionary actions. Current-story traversal and stale-target checks are retained. Right-click keeps its compact menu. |
| 2026-10-22 | Documents that keep content in another format (`w:altChunk`: a web page, RTF, text or another Word document, as generators and mail-merge tools write them) show it where it stands, as Word does, instead of leaving it out; one such file in the docxide-pdf corpus opened as an empty page and now matches Word's 8 pages. `.mht` web archives open, and web pages open the way Word opens them (style sheets, paragraph margins, Times New Roman, single lines, automatic spacing). Insert › Text from File keeps the inserted file's pictures |
@@ -812,6 +816,8 @@ MATH glyphs that reference the same construction now share its immutable variant
| Date | Change | Summary |
|---|---|---|
| 2026-11-07 | trivial | Page Setup also bounds the unfocused margin display after a long numeric paste; the accepted value is retained (#5650). |
| 2026-11-05 | trivial | Page Setup's margin, paper-size and document-grid number boxes scroll long text inside the field instead of enlarging the dialog (#5650). Readiness estimates unchanged. |
| 2026-11-06 | trivial | Print helpers remove loaded PDF/settings files before driver work, tolerate prior removal and report cleanup errors; a held-driver regression checks the files are already absent (#5710) |
| 2026-11-06 | trivial | Windows print submissions isolated per helper process; cancelled driver work cannot block a retry (#5710) |
| 2026-11-06 | trivial | Progress log: print cancellation before sending, including without a document, and the Windows repeated-conversion limitation (#5710) |
+16 -4
View File
@@ -581,8 +581,20 @@ fn insert_table_dialog(app: &mut WordApp, ui: &mut Ui, d: &mut Dialog) -> bool {
}
/// A margin's number box, in the unit the interface measures in.
fn margin(value: &mut f32) -> egui::DragValue<'_> {
egui::DragValue::new(value).speed(3.6 / crate::options::unit().pt_per_unit()).measure(crate::options::unit()).max_decimals(2)
fn margin(value: &mut f32) -> impl egui::Widget + '_ {
move |ui: &mut Ui| {
// DragValue's unfocused button extends to fit its value; keep it inside the field too.
let (_, rect) = ui.allocate_space(egui::vec2(80.0, ui.spacing().interact_size.y));
let mut field = ui.new_child(egui::UiBuilder::new().max_rect(rect).layout(crate::rtl::row_layout(ui, egui::Align::Center)));
field.shrink_clip_rect(rect);
field.add(
egui::DragValue::new(value)
.speed(3.6 / crate::options::unit().pt_per_unit())
.measure(crate::options::unit())
.max_decimals(2)
.clip_text(true),
)
}
}
fn page_setup_dialog(app: &mut WordApp, ui: &mut Ui, d: &mut Dialog) -> bool {
@@ -640,9 +652,9 @@ fn page_setup_dialog(app: &mut WordApp, ui: &mut Ui, d: &mut Dialog) -> bool {
crate::rtl::Table::new("ps_grid").columns(4).show(ui, |t| {
t.row(|r| {
let chars_label = r.cell(|ui| ui.add_enabled(grid.0 >= 2, egui::Label::new(tl!("Characters per line:"))));
r.cell(|ui| ui.add_enabled(grid.0 >= 2, egui::DragValue::new(&mut grid.2).range(1..=1000)).labelled_by(chars_label.id));
r.cell(|ui| ui.add_enabled(grid.0 >= 2, egui::DragValue::new(&mut grid.2).range(1..=1000).clip_text(true)).labelled_by(chars_label.id));
let lines_label = r.cell(|ui| ui.add_enabled(grid.0 >= 1, egui::Label::new(tl!("Lines per page:"))));
r.cell(|ui| ui.add_enabled(grid.0 >= 1, egui::DragValue::new(&mut grid.1).range(1..=1000)).labelled_by(lines_label.id));
r.cell(|ui| ui.add_enabled(grid.0 >= 1, egui::DragValue::new(&mut grid.1).range(1..=1000).clip_text(true)).labelled_by(lines_label.id));
});
});
ui.horizontal(|ui| {
+3 -2
View File
@@ -112,12 +112,13 @@ pub(super) fn page_paper_group(ui: &mut Ui, paper: &mut (f32, f32), landscape: &
t.row(|r| {
let width_label = r.cell(|ui| ui.label(tl!(l.width)));
let w = r.cell(|ui| {
ui.add(egui::DragValue::new(&mut paper.0).speed(3.6 / ppu).range(range.clone()).measure(unit).max_decimals(2))
ui.add(egui::DragValue::new(&mut paper.0).speed(3.6 / ppu).range(range.clone()).measure(unit).max_decimals(2).clip_text(true))
.labelled_by(width_label.id)
});
let height_label = r.cell(|ui| ui.label(tl!(l.height)));
let h = r.cell(|ui| {
ui.add(egui::DragValue::new(&mut paper.1).speed(3.6 / ppu).range(range).measure(unit).max_decimals(2)).labelled_by(height_label.id)
ui.add(egui::DragValue::new(&mut paper.1).speed(3.6 / ppu).range(range).measure(unit).max_decimals(2).clip_text(true))
.labelled_by(height_label.id)
});
if (w.changed() || h.changed()) && paper.0 != paper.1 {
*landscape = paper.0 > paper.1;
+139
View File
@@ -985,6 +985,145 @@ fn opened_paper(app: &mut WordApp) -> ((f32, f32), Option<&'static str>) {
((paper.0 * ppu, paper.1 * ppu), paper_entry(paper))
}
/// #5650: a long paste scrolls inside Page Setup's number boxes instead of enlarging the
/// dialog, including after leaving the field and reopening the dialog.
#[test]
fn page_setup_number_fields_do_not_expand_after_paste() {
use egui::accesskit::Role;
use egui_kittest::kittest::Queryable;
for label in ["Bottom:", "Top:", "Left:", "Right:", "Width:", "Height:", "Characters per line:", "Lines per page:"] {
let mut h = app_harness(app_with("Page"));
h.state_mut().run("ui.dialog", json!({"name": "pageSetup"})).unwrap();
if let Some(Dialog::PageSetup { grid, .. }) = h.state_mut().dialog.as_mut() {
grid.0 = 2; // Enable both document-grid number boxes.
}
h.run_steps(3);
let width =
|h: &egui_kittest::Harness<'_, WordApp>| h.ctx.memory(|m| m.area_rect(egui::Id::unique(("dialog", "Page Setup")))).unwrap().width();
let before = width(&h);
h.get_by_role_and_label(Role::SpinButton, label).click();
h.run_steps(2);
h.key_press_modifiers(egui::Modifiers::COMMAND, egui::Key::A);
h.event(egui::Event::Paste("a".repeat(400)));
h.run_steps(3);
assert!(width(&h) <= before + 2.0, "{label}: pasted text grew the dialog from {before} to {}", width(&h));
h.key_press(egui::Key::Tab);
h.run_steps(3);
assert!(width(&h) <= before + 2.0, "{label}: leaving the field kept a larger dialog");
h.get_by_role_and_label(Role::Button, "Cancel").click();
h.run_steps(3);
assert!(h.state().dialog.is_none());
h.state_mut().run("ui.dialog", json!({"name": "pageSetup"})).unwrap();
h.run_steps(3);
assert!(width(&h) <= before + 2.0, "{label}: reopening kept a larger dialog");
}
}
/// A finite value can outgrow DragValue's unfocused button even when its editor clips.
#[test]
fn page_setup_large_numeric_margins_do_not_expand_after_leaving_field() {
use egui::accesskit::Role;
use egui_kittest::kittest::Queryable;
for label in ["Bottom:", "Top:", "Left:", "Right:"] {
let mut h = app_harness(app_with("Page"));
h.state_mut().run("ui.dialog", json!({"name": "pageSetup"})).unwrap();
h.run_steps(3);
let width =
|h: &egui_kittest::Harness<'_, WordApp>| h.ctx.memory(|m| m.area_rect(egui::Id::unique(("dialog", "Page Setup")))).unwrap().width();
let before = width(&h);
h.get_by_role_and_label(Role::SpinButton, label).click();
h.run_steps(2);
h.key_press_modifiers(egui::Modifiers::COMMAND, egui::Key::A);
h.event(egui::Event::Paste(format!("1{}", "0".repeat(36))));
h.run_steps(3);
let Some(Dialog::PageSetup { top, bottom, left, right, .. }) = h.state().dialog.as_ref() else { panic!("no page setup dialog") };
let value = match label {
"Bottom:" => *bottom,
"Top:" => *top,
"Left:" => *left,
_ => *right,
};
assert!(value.is_finite() && value > 1e35, "{label}: the large finite paste was accepted: {value}");
assert!(width(&h) <= before + 2.0, "{label}: editing grew the dialog from {before} to {}", width(&h));
h.key_press(egui::Key::Tab);
h.run_steps(3);
let Some(Dialog::PageSetup { top, bottom, left, right, .. }) = h.state().dialog.as_ref() else { panic!("no page setup dialog") };
let after = match label {
"Bottom:" => *bottom,
"Top:" => *top,
"Left:" => *left,
_ => *right,
};
assert_eq!(after, value, "{label}: leaving the field keeps the accepted value");
assert!(width(&h) <= before + 2.0, "{label}: leaving the large numeric field grew the dialog from {before} to {}", width(&h));
h.get_by_role_and_label(Role::Button, "Cancel").click();
h.run_steps(3);
assert!(h.state().dialog.is_none());
}
}
/// A clipped wide number in a mirrored form can still be clicked where it is visible.
#[test]
fn page_setup_rtl_large_margin_can_be_edited_again_after_blur() {
use egui::accesskit::Role;
use egui_kittest::kittest::Queryable;
let mut app = app_with("Page");
app.run("ui.dialog", json!({"name": "pageSetup"})).unwrap();
let ar = crate::i18n::fixture_lang("ar", "\tMargins\tMargins\n");
let mut h = egui_kittest::Harness::builder().with_size(vec2(1440.0, 900.0)).build_ui_state(
move |ui, app: &mut WordApp| {
let ctx = ui.ctx().clone();
app.logic(&ctx);
// Draw the actual dialog in a fixture language even without embedded catalogs.
crate::i18n::set_current(ar);
crate::rtl::sync(&ctx);
assert!(crate::rtl::on() && ctx.global_style().right_to_left);
show(app, &ctx);
crate::i18n::set_current(crate::i18n::Lang::EN);
},
app,
);
h.run_steps(4);
let initial = h.get_by_role_and_label(Role::SpinButton, "Bottom:").rect();
assert!(h.get_by_role_and_label(Role::SpinButton, "Top:").rect().center().x > initial.center().x, "the form is mirrored");
let window = |h: &egui_kittest::Harness<'_, WordApp>| h.ctx.memory(|m| m.area_rect(egui::Id::unique(("dialog", "Page Setup")))).unwrap();
let before = window(&h).width();
h.get_by_role_and_label(Role::SpinButton, "Bottom:").click();
h.run_steps(2);
h.key_press_modifiers(egui::Modifiers::COMMAND, egui::Key::A);
h.event(egui::Event::Paste(format!("1{}", "0".repeat(36))));
h.run_steps(3);
let value = match h.state().dialog.as_ref() {
Some(Dialog::PageSetup { bottom, .. }) => *bottom,
_ => panic!("no page setup dialog"),
};
assert!(value.is_finite() && value > 1e35);
h.key_press(egui::Key::Tab);
h.run_steps(3);
assert!(matches!(h.state().dialog.as_ref(), Some(Dialog::PageSetup { bottom, .. }) if *bottom == value));
let field = h.get_by_role_and_label(Role::SpinButton, "Bottom:");
assert!(!field.is_focused());
let wide = field.rect();
let visible = wide.intersect(initial).intersect(window(&h));
assert!(wide.width() > 80.0 && visible.is_positive() && !visible.contains(wide.center()));
assert!(window(&h).width() <= before + 2.0);
// The AccessKit bounds include clipped text: click the visible part, not their centre.
let pos = visible.center();
h.event(egui::Event::PointerMoved(pos));
for pressed in [true, false] {
h.event(egui::Event::PointerButton { pos, button: egui::PointerButton::Primary, pressed, modifiers: egui::Modifiers::NONE });
}
h.run_steps(3);
assert!(h.get_by_role_and_label(Role::SpinButton, "Bottom:").is_focused(), "the visible field enters editing");
h.key_press_modifiers(egui::Modifiers::COMMAND, egui::Key::A);
h.event(egui::Event::Paste("2.5".into()));
h.run_steps(3);
h.key_press(egui::Key::Tab);
h.run_steps(3);
assert!(matches!(h.state().dialog.as_ref(), Some(Dialog::PageSetup { bottom, .. }) if *bottom == 2.5), "the field accepts a normal value again");
assert!(window(&h).width() <= before + 2.0);
}
/// #1433: Page Setup's Paper size shows the named size the section has, in either
/// orientation, and Custom size (None) for anything else.
#[test]
+6
View File
@@ -1,6 +1,8 @@
# Where WordCraft falls short of Word
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-06 · **Change:** trivial (Protected typing boundary lookup counts preceding objects once and indexes editable permission IDs (#3614); caret movement and enforcement policy are unchanged; previously trivial: View windows: several open documents and Switch Windows in the engine; previously trivial: explicit documents keep the editor when recovery discovery finishes; readable recovery controls; previously trivial: text mini toolbar available with the right-click menu, #2959; previously trivial: personal templates in File › New and Save as Word Template, #2852; previously trivial: tracked table/row/cell/section property changes record in WordCraft, narrowing gap 5; previously trivial: a document with many content controls saves in the time the controls take, #13; previously trivial: Format Page Numbers dialog carries Word's number formats and Continue from previous section / Start at, #1113; previously trivial: centred list labels after leading page breaks, #2964; previously trivial: fixed font inputs in four layout controls; previously trivial: a right, centre or decimal tab whose text doesn't fit, or a left tab past the right indent, goes to the next line with its text, #1840; previously trivial: Bullets and Numbering toggle off style-inherited lists at their effective level, including basedOn chains and numbered ListParagraph styles; other paragraphs and list definitions stay unchanged (related to #3459); previously trivial (gap 4: a float anchored right after a page break, before text, is drawn on the new page, #4262); previously trivial (Drawing Canvas members picked and moved on their own, #2637); previously trivial (gap 4: a float anchored on a later page of a long paragraph is drawn there, #4250); previously trivial (#29: typed `\int`, `\sum`… + Space wait for their limits, observed in Word for Windows on #4938, #4917); previously trivial (only the PDF sent to the printer leaves out link annotations; Save these pages as PDF keeps them, #4734; no ranked gap changed); previously trivial (print helpers erase loaded plaintext inputs before driver work, #5710); previously trivial (Windows print submissions use isolated helpers so a cancelled driver cannot block a retry, #5710); previously trivial (print cancellation remains available without a document; sending-boundary limitation clarified, #5710); previously trivial (gap 6: a print job waiting on its driver can be cancelled, #5710; no ranked gap changed); previously trivial (gap 6: the printer list no longer waits on printer drivers, #5710; no ranked gap changed); previously trivial (Page drawing indexes text-box warp lookups per paragraph (#3614)); previously trivial (built-in citation source lookups, #4888; current-main progress records retained); previously trivial (MATH construction reuse, #3630); previously trivial (DOCX `w:adjustRightInd` defaults true and is written in schema order); previously trivial (ink page lookups reuse scanned paragraph prefixes, #3614); previously trivial (#19: ODT settings for what the standard leaves open removed); previously trivial (#19: ODT element audit; files from other applications checked); previously trivial (#11: the dialogs of the mirrored Arabic interface read right to left: label-and-field rows, tables (list columns, the Models table), tile galleries and the Add/Remove arrows follow the reading direction, through one `rtl::Table` helper that draws exactly as before in English; no ranked gap closed, readiness estimates unchanged); previously trivial (#4: fonts that aren't installed are flagged in the font box and status bar); previously trivial (#19: ODT sections inside text boxes kept as read); previously trivial (#19: an ODT saved without edits keeps what WordCraft doesn't model); previously trivial (#19: ODT interpretation profile, WordCraft / LibreOffice / custom); previously trivial (#19: ODT form controls placed and multi-line; custom shapes keep their geometry); previously trivial (#19: ODT in-line frames placed against the text; template instantiation listed as left); previously trivial (#19: ODT charts kept as read; frame outlines; default header height); previously trivial (#19: ODT tables no longer gain repeated header rows); previously trivial (#19: ODT padding without a border, unequal columns, column breaks, picture-only lines); previously trivial (#19: ODT tables' margins and spacing, header spacing, pictures alone in a paragraph); previously trivial (#19: no LibreOffice template changes page count through a save); previously trivial (#19: ODT variables, emphasis marks, page backgrounds, fixed tables, default size); previously trivial (#19: ODT checked against LibreOffice's own templates: placeholders, first-page styles, unsized pages, metafile logos, `.ott`); previously trivial (#19: ODT at ~90%: custom shapes, cell formulas, macros kept, turned text, control sizes and buttons); previously trivial (#19 narrowed: ODT read and writtLine truncated
> **Page Setup update:** 2026-11-07 · Page Setup number boxes keep long input inside the field, including accepted margin values after focus leaves (#5650); accepted ranges and readiness estimates are unchanged.
> **Highlight update:** 2026-10-25 · Highlight shortcut toggles the last chosen colour off when the whole selection has it; explicit colour commands remain setters (#4085). Readiness estimates unchanged.
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-06 · **Change:** trivial (only the PDF sent to the printer leaves out link annotations; Save these pages as PDF keeps them, #4734; no ranked gap changed); previously trivial (Windows print submissions use isolated helpers so a cancelled driver cannot block a retry, #5710); previously trivial (print cancellation remains available without a document; sending-boundary limitation clarified, #5710); previously trivial (built-in citation source lookups, #4888; current-main progress records retained); previously trivial (aligned-copy review fixes retain the document axis through scrolling and include secondary selections (#4764)); previously trivial (#15: bibliography sources keep a corporate author, #4848); previously trivial (Table Properties applies to the selected rows, columns and cells, #4857); previously trivial (Relative font-size commands preserve mixed sizes in the selection (#4903); existing button steps and one-point shortcuts are unchanged; readiness estimates unchanged); previously trivial (Arabic Backspace removes one combining mark and keeps its base letter (#4757); previously trivial (Recipient labels continue across product-sized sheets instead of stopping after the first sheet (#4880)); previously trivial (#7: Custom Document Properties dialog, #2982; previously trivial: egui 0.37 migration: the interface catalogs stay shared and pinned by `craft-i18n.rev` at `9205108cfdced5177706264038848b68b2422d2b` (27 languages, English fallback); input-method handling has new regressions but managed CI passed, and native IME acceptance plus native-speaker catalog review remain open; previously trivial: Markdown code spans keep line-broken text literal, #3738; previously trivial: small follow-ups from the field, table and AutoFormat batch, #4232; previously trivial: highlight target includes selected paragraph marks, #4085; previously trivial: full-target highlight check beyond 10,000 runs, #4085; previously trivial: Exact-spacing table contact uses the specified line height rather than paragraph-mark metrics (#3977); previously trivial: cu20we: restrict pre-spacing float origins to paragraph anchors; document cap helpers and regressions; previously trivial: a command that changes nothing is not an edit: no undo step, no unsaved changes, no relayout, and the redo stack survives it; previously minor: #8 narrowed: Spanish proofing (Spain, Mexico, Latin America, Uruguay) from the installed Hunspell dictionaries, #3360, #3553; previously trivial: gap 10: group connector rerouting preserves transformed free and unresolved endpoints under nonuniform scaling; obstacle routing still missing; readiness estimates unchanged; previously trivial: a word with no break opportunity in it no longer makes the layout quadratic, #3412; previously trivial: the ranked gap list held two rows each for #19, #20 and #21; each gap appears once now; a document with many relationships opens in the time its part takes, #3411; previously trivial: the file commands that change the document are checked on a protected document, the protection of a protected document needs an explicit stop, the session author can't change through `file.setAuthor` under protection (the CLI's `--author` uses the same protection check), and the post-check compares the settings and the metadata; previously trivial: explicit documents keep the editor when recovery discovery finishes; readable recovery controls; previously trivial: text mini toolbar available with the right-click menu, #2959; previously trivial: personal templates in File › New and Save as Word Template, #2852; previously trivial: tracked table/row/cell/section property changes record in WordCraft, narrowing gap 5; previously trivial: a document with many content controls saves in the time the controls take, #13; previously trivial: Format Page Numbers dialog carries Word's number formats and Continue from previous section / Start at, #1113; previously trivial: centred list labels after leading page breaks, #2964; previously trivial: fixed font inputs in four layout controls; previously trivial: a right, centre or decimal tab whose text doesn't fit, or a left tab past the right indent, goes to the next line with its text, #1840; previously trivial: Bullets and Numbering toggle off style-inherited lists at their effective level, including basedOn chains and numbered ListParagraph styles; other paragraphs and list definitions stay unchanged (related to #3459); previously trivial: Russian sorting keeps ё with е and recognises decomposed ё/й; previously trivial: #8: English dialects — en-GB, en-CA, en-AU… flag each other's spellings, #1281; previously trivial: #1: shape gradients and transparency, #1500; previously trivial: text typed into a content control takes the control's formatting; localized placeholder styles recognised; previously trivial: forms protection keeps its content controls: no inserting, Properties or Remove ConLine truncated
>
@@ -162,6 +164,8 @@ File > Print offers 2, 4, 6, 9 or 16 logical pages per physical sheet side and l
Table rows and cells inside `mc:AlternateContent` use the same selected-branch policy as paragraphs and runs (#5219), rather than disappearing on open and save. Original synthetic fixtures cover Choice/Fallback selection, namespace aliases, mixed requirements, row/cell order and nested content controls through two DOCX saves. This fixes a focused preservation case; native Word and broader real-world corpus qualification remain open.
Page Setup's margin, paper-size and document-grid number boxes scroll long text inside the field instead of enlarging the dialog (#5650). Readiness estimates unchanged. Accepted long margin values also stay inside the field after focus leaves; their accepted range, units and parsing are unchanged.
## Revision history
> 2026-11-03 · Section `w:chapStyle` values now preserve the complete unsigned-byte range, including 10; values above 255 are rejected.
@@ -178,6 +182,8 @@ Table rows and cells inside `mc:AlternateContent` use the same selected-branch p
| Date | Change | Summary |
|---|---|---|
| 2026-11-07 | trivial | Page Setup also bounds the unfocused margin display after a long numeric paste; the accepted value is retained (#5650). |
| 2026-11-05 | trivial | Page Setup's margin, paper-size and document-grid number boxes scroll long text inside the field instead of enlarging the dialog (#5650). Readiness estimates unchanged. |
| 2026-11-06 | trivial | Print helpers remove loaded PDF/settings files before driver work, tolerate prior removal and report cleanup errors; a held-driver regression checks the files are already absent (#5710) |
| 2026-11-06 | trivial | Windows print submissions use a separate helper per job, preserving provider serialization inside the helper. An acknowledged cancellation stops that helper, so a stalled cancelled driver cannot block a later job; normal shutdown reaps helpers and removes temporary PDFs. The helper erases its plaintext inputs before driver work; a hard app crash during startup can still leave private temporary files. Startup requires an explicit start grant; after sending begins, jobs complete (#5710) |
| 2026-11-06 | trivial | Gap 6: File › Print: before sending starts, Cancel printing (`ui.printCancel`) stops a waiting job and frees the Print page while keeping its settings, including after the last document closes; a successfully cancelled job is never sent. Quitting cancels waiting jobs and joins jobs already sending (#5710). On Windows, winprint repeats the DEVMODE conversion after sending starts. A stall during that repeat prevents cancellation and can keep shutdown waiting, even before a native job exists. No ranked gap changed; readiness estimates unchanged |
+6
View File
@@ -2,6 +2,8 @@
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-11-04 · **Change:** trivial (Protected typing boundary lookup counts preceding objects once and indexes editable permission IDs (#3614); caret movement and enforcement policy are unchanged; previously trivial: Custom Document Properties dialog from File › Info › Properties › Advanced Properties…, #2982; previously trivial: View › Window › New Window opens another window on the document, each with its own caret and view; previously trivial: mail merge: First Record, Last Record and a record number box in Preview Results; Finish & Merge asks which records to merge or print, #3028; previously trivial: selection bar for right-to-left paragraphs and table rows; previously trivial: read only and comments-only protection grey out edits; previously trivial: the Font dialog previews "NaN" or "inf" in the Size box at 12 pt, #3663; previously trivial: File › Options disables the User name field while Restrict Editing is active; previously trivial: while editing a header or footer, a click in the other one edits it there without scrolling; previously trivial: explicit documents keep the editor when recovery discovery finishes; readable recovery controls; previously trivial: Insert Table of Figures dialog: any caption label, #3013; previously trivial: Home › Underline drop-down shows its lines, #2738; previously trivial: the text context menu includes the formatting mini toolbar, #2959; previously trivial: personal templates in File › New and Save as Word Template, #2852; previously trivial: the scrollbar page tip stays on one line as the page number grows, #3341; previously trivial: File › Home and New: templates and recent documents are buttons named for screen readers, #3176; previously trivial: forms protection greys out the Developer tab's control buttons and Properties; previously trivial: AltGr doesn't open KeyTips, #2388; previously trivial: closing the header or footer puts the caret back where it was in the body, #3331; previously trivial: Format Page Numbers dialog: number format and Continue from previous section / Start at, #1113; previously trivial: inline pictures drag to a dashed insertion cursor and stay inline, #2233, #2234; previously trivial: Save Changes prompt: a text file saved over itself closes, #2376; previously trivial: the equation being edited is framed as in Word: an outline as tall as its line, a grip, an options strip that opens its menu, and its current argument shaded; previously trivial: the table handles show while the caret is in the table, #2428; previously trivial: legacy menu keys, #1697; previously trivial: Shape Fill › Gradient and the Format Shape pane, #1500; previously trivial: Focus fills the screen, and Full Screen View shows the pages alone, #1678; previously trivial: the text pointer over the page is drawn black or white on Windows, #1746; previously trivial: a click in the header/footer band from the body no longer scrolls to the body text, #2103; previously trivial: File › Recover follows the app theme, #3243; previously trivial: long navigation headings wrap onto further lines within the pane, #3015; previously trivial: clearing a no-match font query keeps the recent heading visible, #2068; previously trivial: dragging the vertical scrollbar names the page at the top of the window, #2551; previously trivial: Add Font imports every selected font file, #1789); previously trivial (dragging an In Line with Text object shows a bold insertion cursor, as in Word, instead of a faint dashed one, #4195); previously trivial (the shapes, pictures and text boxes inside a Drawing Canvas are picked, moved and resized on their own, #2637); previously trivial (Shape Format › Size shows the interface unit and takes any typed unit, #4196); previously trivial (over a turned picture, shape or text box the resize arrows turn with it, #3932); previously trivial (the status bar's count is words by default and can be characters with or without spaces, chosen from its right-click menu or File › Options › Display and kept between runs, #2797); previously trivial (Linux display server setting in File › Options › Advanced, #2184); previously trivial (dialogs are modal except Find and Replace, Symbol, Mark Index Entry, Cross-reference and Insert Merge Field, and a press beside one shakes it, #3859); previously trivial (showing the Format Shape pane leaves a gradient angle above 359.9° as it is, #3955); previously trivial (Return on File › Print prints, as Word's default Print button does); previously trivial (N-up and booklet printing, #641; readiness unchanged); previously trivial (missing fonts flagged in the font box and status bar, read from what layout drew; metric-compatible substitutes and symbol fonts not flagged); previously minor (Options › Language redesign: Your languages, Add a language…, Set as default; the Dictionaries folder moved from Proofing); previously trivial (a long comment's Line truncated
> **Page Setup update:** 2026-11-07 · Page Setup number boxes keep long input inside the field, including accepted margin values after focus leaves (#5650); accepted ranges and readiness estimates are unchanged.
Recovery discovery no longer interrupts an explicitly created or opened document. Older copies remain in File › Recover, whose buttons use the interface palette. See [local recovery](recovery.md).
How WordCraft looks and feels next to Word: ribbon, dialogs, panes, keyboard, mouse and touch.
@@ -97,10 +99,14 @@ Review › Spelling & Grammar, Home › Editor and F7 open a right-side Proofing
Right-click and Shift+F10 keep the compact suggestions menu. The pane is shared by all proofing languages; it adds no dictionary or grammar rule. See [spelling-review.md](spelling-review.md) for scope, observation and verification.
Page Setup's margin, paper-size and document-grid number boxes scroll long text inside the field instead of enlarging the dialog (#5650). Readiness estimates unchanged. Accepted long margin values also stay inside the field after focus leaves; their accepted range, units and parsing are unchanged.
## Revision history
| Date | Change | Summary |
|---|---|---|
| 2026-11-07 | trivial | Page Setup also bounds the unfocused margin display after a long numeric paste; the accepted value is retained (#5650). |
| 2026-11-05 | trivial | Page Setup's margin, paper-size and document-grid number boxes scroll long text inside the field instead of enlarging the dialog (#5650). Readiness estimates unchanged. |
| 2026-10-17 | trivial | Drawing Canvas members are selected with a click and dragged, resized, turned, nudged, formatted, deleted and copied on their own inside the canvas; a click in a text box member's text puts the caret there (#2637) |
| 2026-11-05 | trivial | On-canvas objects: dragging an In Line with Text picture, shape or text box shows a bold, solid insertion cursor, twice the caret's width, as in Word; the dashed one was hard to see (#4195) |
| 2026-10-28 | trivial | Protected typing boundary lookup counts preceding objects once and indexes editable permission IDs (#3614); caret movement and enforcement policy are unchanged. |