diff --git a/ROADMAP.md b/ROADMAP.md index 9cdde5674..86f8421a6 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-11 · **Change:** trivial (large-table scrolling, #811; 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-11 · **Change:** 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 WordCraft aims for complete parity with Microsoft Word, then goes further on speed, openness and agent control. This page is the summary; the assessment is @@ -172,6 +172,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm |---|---| | 2026-10-11 | Table border drags like Word's (#711): Ctrl shares a column border's change among the columns right of it (the table keeps its width), the table's left edge moves a left-aligned table's indent (a centred or right-aligned table keeps its alignment, as in Word), a border of cells selected across moves only in their rows, and Alt shows the column widths on the ruler | | 2026-10-11 | Tables on the rulers (#700): with the caret in a table the horizontal ruler marks its row's column borders and the vertical ruler its rows' bottoms; dragging a marker resizes the column (the columns right of it move along; with Shift the neighbour makes up the difference) or the row, one undo step, Escape cancels; in a cell the indents and tabs are measured from the cell | +| 2026-10-12 | Save Changes prompt answers from the keyboard: Save has focus when it opens; arrow keys and Tab move between Save, Don't Save and Cancel; Enter answers (#861) | | 2026-10-12 | Builds with craft-fonts draw simplified-Chinese characters in documents from the bundled Noto Sans CJK SC (it was only used by the interface), so a CJK run gets one face covering it; fixes the FreeBSD job's `east_asian_run_uses_one_face_covering_the_whole_run` failure after #567 | | 2026-10-12 | AutoCorrect capitals (#544): Correct TWo INitial CApitals (with exceptions), accidental cAPS LOCK, first letter of table cells; each undoable and switchable | | 2026-10-11 | Turkish (`tr`) interface completed (#774): 2,325 entries, every key of the Russian catalog (equation gallery, built-in style names, watermarks, paper sizes, preview tiles); the interface audit checks Turkish too | @@ -271,6 +272,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm | 2026-10-12 | trivial | Progress log: Start screen at launch (#766) | | 2026-10-12 | trivial | Progress log: Alt+Q command search (#819); log table header fixed | | 2026-10-12 | trivial | Progress log: ribbon groups fit above their labels | +| 2026-10-12 | trivial | Progress log: save prompt from the keyboard (#861) | | 2026-10-12 | trivial | Progress log: craft-fonts Simplified Chinese in documents | | 2026-10-11 | trivial | Progress log and shipped languages: Turkish interface completed, 2,325 entries (#774) | | 2026-10-12 | trivial | Progress log: Navigation heading truncation (#759) | diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index edff600cf..975ff9012 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -2359,6 +2359,14 @@ fn body(app: &mut WordApp, ui: &mut Ui, d: &mut Dialog) -> bool { .inner }); let buttons = buttons.inner; + // The keyboard alone answers, as in Word (#861): Save has focus when the prompt opens, + // so Enter saves, and the arrow keys (egui's focus navigation) or Tab move between the + // buttons, shown as Save, Don't Save, Cancel. + if ui.memory(|m| m.focused().is_none()) + && let Some((save, _)) = buttons.get(2) + { + save.request_focus(); + } // Enter clicks the focused button; it means Save only when none of them has focus. let focused = buttons.iter().any(|(b, _)| b.has_focus()); let answer = if ui.input(|i| i.key_pressed(egui::Key::Escape)) { diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index ff17bc413..88c74041d 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -2838,6 +2838,59 @@ mod tests { } } + /// #861: the save prompt answers from the keyboard alone. Save has focus when it opens, and + /// Left/Right move the focus between Save, Don't Save and Cancel (stopping at the ends). + #[test] + fn arrow_keys_choose_the_save_prompt_answer() { + use egui_kittest::kittest::Queryable; + for (arrows, focused, quits, saves) in [ + (vec![egui::Key::ArrowLeft], "Save", true, true), + (vec![egui::Key::ArrowRight], "Don't Save", true, false), + (vec![egui::Key::ArrowRight, egui::Key::ArrowRight, egui::Key::ArrowRight], "Cancel", false, false), + (vec![egui::Key::ArrowRight, egui::Key::ArrowRight, egui::Key::ArrowLeft], "Don't Save", true, false), + ] { + let dir = scratch(&format!("arrows-{}-{}", arrows.len(), focused.len())); + let path = docx_on_disk(&dir, "Draft"); + let before = std::fs::read(&path).unwrap(); + let mut a = app(); + a.run("file.open", json!({"path": path.to_string_lossy()})).unwrap(); + a.run("text.insert", json!({"text": "Revised "})).unwrap(); + a.run("file.close", json!({})).unwrap(); + assert_eq!(prompt(&a), Some("saveChanges")); + let mut fonts = false; + let mut h = egui_kittest::Harness::builder().build_ui_state( + move |ui, app: &mut WordApp| { + let ctx = ui.ctx().clone(); + if fonts { + dialogs::show(app, &ctx); + } else { + theme::install_fonts_for(&ctx, false); + fonts = true; + } + }, + a, + ); + for _ in 0..3 { + h.step(); + } + assert!(h.get_by_label("Save").is_focused(), "Save has focus when the prompt opens"); + for key in &arrows { + h.key_press(*key); + h.step(); + } + assert!(h.get_by_label(focused).is_focused(), "{arrows:?}: {focused} has focus"); + h.key_press(egui::Key::Enter); + for _ in 0..3 { + h.step(); + } + let a = h.state(); + assert_eq!(prompt(a), None, "{arrows:?}: answered"); + assert_eq!(a.quit_requested, quits, "{arrows:?}: quit"); + assert_eq!(std::fs::read(&path).unwrap() != before, saves, "{arrows:?}: file written"); + let _ = std::fs::remove_dir_all(&dir); + } + } + /// `ui.print` hands the document's PDF to the host's print hook, and is an error (with /// nothing printed) when the host has none. #[test] diff --git a/docs/ui-parity.md b/docs/ui-parity.md index deeaef2f1..37e32999d 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-11 · **Change:** trivial (table border drags with Ctrl and Alt, from the left edge and of selected cells, #711; previously trivial: table column and row markers on the rulers, #700; previously trivial: table borders and the resize handle drag on the page, #217; previously 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-11 · **Change:** 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 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 @@ -54,6 +54,7 @@ own (`AGENTS.md`: no Microsoft iconography, ever). | 2026-10-12 | trivial | Alt+Q moves to "Tell me" command search, as in Word for Windows (#819) | | 2026-10-12 | trivial | Ribbon groups fit above their labels: View › Show puts Hide White Space (#645) in a second column instead of a fourth row over the "Show" label; Developer › Controls drops the gaps that pushed it into its label; Layout › Paragraph (#654) puts its column gap inside cells (no `add_space` in the grid, a debug assertion) and 1.5 pt between rows so it fits; `ribbon_groups_fit_above_their_labels` checks every tab in every language | | 2026-10-12 | trivial | Window caption: a file name in a script the interface fonts lack (Hebrew, Arabic, and the rest; CJK via the CJK UI font) is drawn with a fallback face instead of `?` | +| 2026-10-12 | trivial | Save Changes prompt: Save has focus when it opens, so Enter, Tab and the arrow keys answer without the mouse (#861) | | 2026-10-12 | trivial | Navigation headings truncate with an ellipsis within the pane width, preserving resizing and full-text hover (#759) | | 2026-10-11 | minor | Reading Mode: viewport-height horizontal columns, animated paging, transient display controls, heading navigation and responsive anchored resizing | | 2026-10-11 | trivial | Alt activation handles native key/modifier events in either order, including key-up with the old Alt flag; taps toggle once, chords and focus loss do not activate KeyTips |