#3035: let Esc leave File-page fields before closing

This commit is contained in:
Zarvinx
2026-10-31 18:09:27 +01:00
parent 67e39faeea
commit f2bef898e8
4 changed files with 77 additions and 4 deletions
+3 -1
View File
@@ -2,7 +2,7 @@
**Stage: alpha** · next: beta, ~25 points of "ready for real work" and ~160–240 h away
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-25 · **Change:** minor (AutoSum over a selected row puts its total to the right, and fills a selection's empty last row and column; earlier: macro engine and `macro.*` commands; macro interface on the Automate tab) · **Target:** Microsoft Excel (Microsoft 365)
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-31 · **Change:** minor (Esc leaves a File-page text field before closing the view; earlier: AutoSum over a selected row puts its total to the right, and fills a selection's empty last row and column; earlier: macro engine and `macro.*` commands; macro interface on the Automate tab) · **Target:** Microsoft Excel (Microsoft 365)
GridCraft aims at full Microsoft Excel parity, and at being better in places: faster, open (XLSX
is the native format), fully scriptable by agents (MCP, control channel, CLI), and on the web.
@@ -135,6 +135,7 @@ Ranked; estimates in Opus 5.5 agent-hours. Detail and the full beta-gate list in
| Date | What landed |
|---|---|
| 2026-10-31 | Esc in File › Info, Print or Options first leaves the text field; the next Esc returns to the sheet (#3035) |
| 2026-10-25 | AutoSum over a selection does what Excel does: a selected row gets one total to its right (in its last cell if that is empty), not a one-cell total under each cell; a block whose last column is empty gets each row's total there, one whose last row is empty each column's, and with both the corner gets the total of the block; otherwise totals go under each column as before |
| 2026-10-24 | Macro interface ([macros.md](docs/macros.md#in-the-window)): an Automate › Macros group (Record Macro, which turns into Stop Recording, Use Relative References, Run Macro with recent macros); "Recording · N steps" with Stop in the status bar; a Record Macro dialog (checked name, free Ctrl+Shift shortcut, description); the Macros dialog lists macros beside Automate scripts and a VBA project, with Run, Step Into, Edit, Create, Delete and Options; Ctrl+Shift+letter macro shortcuts; questions that offer Run, Overwrite, Shift Down or Shift Right; a step editor with a breakpoint gutter, drag to reorder, a command picker, reference boxes that switch between relative tokens and absolute references, Inputs and Output tabs, a debug bar (Check, Run, Step, Continue, Run to Here, Stop with keep or undo) and grid overlays for inputs, writes, conflicts and the current step |
| 2026-10-24 | Go To lists the named ranges of every sheet (`Sheet2!Name` for a name scoped to another sheet; hidden names, constants and formulas left out) and the references entered before, a double-click goes to an entry, and the Reference box takes several names or references separated by commas (`Price, Type`, `B14:C22,F19:G30`) |
@@ -244,6 +245,7 @@ Ranked; estimates in Opus 5.5 agent-hours. Detail and the full beta-gate list in
| Date | Change | Summary |
|---|---|---|
| 2026-10-31 | minor | File-page text fields keep the view open when Esc cancels an edit (#3035) |
| 2026-10-25 | minor | Progress log: AutoSum over a selected row, and a selection's empty last row and column |
| 2026-10-24 | minor | Macro interface on the Automate tab; progress log row |
| 2026-10-24 | minor | Macro engine and `macro.*` commands; catalog 275 / 291 → 276 / 292 (View › Macros › Record Macro); progress log row |
+5 -1
View File
@@ -114,6 +114,8 @@ pub struct Backstage {
pub page: Page,
/// Info: property drafts while a field is being edited (committed when it loses focus).
props: [String; 6],
/// Focus at the end of the previous frame, before egui clears it on Esc.
text_edit_focused: bool,
/// Print: the whole workbook rather than the active sheet.
pub print_all: bool,
/// Print: fit each sheet on one page.
@@ -138,6 +140,7 @@ impl Backstage {
Backstage {
page,
props: Default::default(),
text_edit_focused: false,
print_all: false,
fit_to_page: false,
preview_page: 0,
@@ -160,7 +163,7 @@ pub fn show(app: &mut SheetApp, ui: &mut Ui) {
let Some(mut bs) = app.backstage.take() else { return };
let ctx = ui.ctx().clone();
// Esc inside a text field leaves the field; it closes the view only from outside one.
let typing = ctx.text_edit_focused();
let typing = bs.text_edit_focused;
let t = Tokens::get(&ctx);
let lang = app.ui.language;
let mut out = Outcome::default();
@@ -229,6 +232,7 @@ pub fn show(app: &mut SheetApp, ui: &mut Ui) {
});
});
});
bs.text_edit_focused = ctx.text_edit_focused();
if !typing
&& app.dialog.is_none()
&& app.message.is_none()
+66
View File
@@ -62,6 +62,72 @@ fn new_and_open_from_the_file_tab_keep_the_ribbon_as_it_was() {
assert!(h.state().ui.ribbon_collapsed);
}
/// Esc first cancels the focused File-page field, then closes File on the next press.
#[test]
fn escape_leaves_file_fields_before_closing_the_view() {
use egui::accesskit::Role;
for (page_id, label) in [
("info", "Title"),
("info", "Subject"),
("info", "Author"),
("info", "Company"),
("info", "Keywords"),
("info", "Description"),
("options", "Maximum Iterations:"),
("options", "Maximum Change:"),
("print", "Copies"),
] {
let services = Services {
printers: Some(Box::new(|| {
let (tx, rx) = std::sync::mpsc::channel();
tx.send(Vec::new()).unwrap();
rx
})),
..Default::default()
};
let mut session = Session::new();
session.new_workbook();
let mut h = crate::common::harness_with(
crate::common::builder().with_size(egui::vec2(1200.0, 1400.0)),
crate::common::prepare(SheetApp::new(session, services)),
4,
);
h.state_mut().run("formulas.calculationOptions", serde_json::json!({"iterative": true})).unwrap();
h.state_mut().run("ui.backstage", serde_json::json!({"page": page_id})).unwrap();
h.run_steps(3);
let role = if page_id == "print" { Role::SpinButton } else { Role::TextInput };
h.get_by_role_and_label(role, label).click();
h.run_steps(2);
assert!(h.ctx.text_edit_focused(), "{label} has the caret");
let original = h.get_by_role_and_label(role, label).value();
let revision = h.state().session.active().unwrap().revision;
h.key_press_modifiers(egui::Modifiers::COMMAND, egui::Key::A);
h.run_steps(1);
h.input_mut().events.push(egui::Event::Text("7".into()));
h.run_steps(2);
if page_id != "print" {
assert_eq!(h.get_by_role_and_label(Role::TextInput, label).value().as_deref(), Some("7"));
}
let copies = h.state().printing.settings.copies;
h.key_press(egui::Key::Escape);
h.run_steps(3);
assert_eq!(page(&h), Some(page_id), "Esc in {label} keeps File open");
assert!(!h.ctx.text_edit_focused(), "Esc leaves {label}");
assert_eq!(h.state().session.active().unwrap().revision, revision, "Esc does not commit {label}");
if page_id == "print" {
assert_eq!(h.state().printing.settings.copies, copies, "leaving Copies preserves its existing live value");
} else {
assert_eq!(h.get_by_role_and_label(Role::TextInput, label).value(), original, "Esc discards the {label} draft");
}
h.key_press(egui::Key::Escape);
h.run_steps(3);
assert_eq!(page(&h), None, "the next Esc returns to the sheet");
}
}
#[test]
fn save_actions_include_the_cell_draft_and_close_keeps_the_unsaved_prompt() {
let downloads = Arc::new(Mutex::new(Vec::<(String, Vec<u8>)>::new()));
+3 -2
View File
@@ -1,6 +1,6 @@
# UI parity
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-30 · **Change:** minor (checkbox cells print as boxes; earlier: Ctrl+arrow and Ctrl+Shift+arrow while pointing a formula reference; earlier: editing a hyperlink loads its existing fields; earlier: AutoSum over a selected row puts its total to the right; ribbon catalog count 276 / 292) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-31 · **Change:** minor (Esc leaves a File-page text field before closing the view; earlier: checkbox cells print as boxes; earlier: Ctrl+arrow and Ctrl+Shift+arrow while pointing a formula reference; earlier: editing a hyperlink loads its existing fields; earlier: AutoSum over a selected row puts its total to the right; ribbon catalog count 276 / 292) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
Interaction, controls and feel, against Excel. Part of [target-app-parity.md](target-app-parity.md).
**UI/UX fidelity: ~55% ready, 50–80 h** (estimated). Ribbon command coverage is measured
@@ -31,7 +31,7 @@ Sources: `crates/ui-egui/src` (ribbon, grid, editor, formula bar, dialogs, panes
| Task panes (Format Chart, Comments, Watch, Selection, PivotTable fields) | yes | yes; PivotTable source-field list scrollbar sits at the right edge of its pane | element-level Format pane for shapes and pictures missing |
| Backstage (File) view: Info, New with templates, Open recent, Export, Options | yes | partial | template gallery missing |
| Dark mode, system appearance | yes | yes | done (10-09); follows the system on first run, title-bar switch and File › Display theme (#194, #239); icons, reference colours, primary buttons and the comments pane follow the theme, and a unit test holds every token pair to WCAG 2 contrast in both themes |
| Backstage (File) view: Info, New with templates, Open recent, Export, Options | yes | yes (#174, 10-11): the File tab opens a full-window view (Esc or the back arrow return) with Home, New (blank + the three samples), Open (recent + Browse; the recent list tells when each workbook was last changed, 10-12), Info (location, properties, Protect Workbook), Save, Save As, Print (page preview, active sheet or workbook, fit to page, Page Setup), Export (PDF, CSV, web page), Close and Options (`file.options`: theme, language, formula bar, AutoSave, calculation mode and iterative calculation with Maximum Iterations and Maximum Change, 10-15) | no Account, Share or template gallery beyond the samples; Options applies at once (no OK/Cancel dialog) and holds only the settings GridCraft has |
| Backstage (File) view: Info, New with templates, Open recent, Export, Options | yes | yes (#174, 10-11): the File tab opens a full-window view (Esc or the back arrow return; Esc in a text field first cancels editing and leaves the field, #3035) with Home, New (blank + the three samples), Open (recent + Browse; the recent list tells when each workbook was last changed, 10-12), Info (location, properties, Protect Workbook), Save, Save As, Print (page preview, active sheet or workbook, fit to page, Page Setup), Export (PDF, CSV, web page), Close and Options (`file.options`: theme, language, formula bar, AutoSave, calculation mode and iterative calculation with Maximum Iterations and Maximum Change, 10-15) | no Account, Share or template gallery beyond the samples; Options applies at once (no OK/Cancel dialog) and holds only the settings GridCraft has |
| Dark mode, system appearance | yes | yes | done (10-09) |
| Page Layout view (paper view with rulers and header editing) | yes | yes (#329, 10-11): per-sheet view saved in XLSX, pages with margins and gaps on the print pagination, "Click to add data" pages, rulers (inches or centimetres), headers and footers drawn and edited in place with a Header & Footer tab, whose Different First Page and Different Odd & Even Pages give the first page and the even pages their own header and footer (#344), and whose Scale with Document and Align with Page Margins set whether header and footer text follows the print scale and the side margins, on paper and in the view | margins drag on the rulers (#792, with header and footer margins); header/footer pictures missing (1–3 h). Unverified: with Align with Page Margins off, header and footer text sits 0.7 in from the paper edge, a value not yet measured in the target app |
| Multiple windows per workbook | yes | yes (10-22, desktop): each workbook window is a native window of its own (New, Open and New Window open one; titles "Book1", or "Book1:1" / "Book1:2" for a workbook's second window, with " — Edited" while unsaved). Each window keeps its own cell draft, dialog, Backstage view, message and grid state, and its ribbon acts on it; on macOS the menu bar acts on the window focused last. Leaving a window enters its draft as Enter would. A window's close button closes that window: one of several on a workbook closes without asking, a workbook's last asks Save / Don't Save / Cancel when unsaved. Quit asks about each unsaved workbook in its window, and Cancel stops it. The last window closed quits on Windows and Linux; macOS keeps running with the menu bar until New or Open. View › Arrange All (dialog: Tiled, Horizontal, Vertical, Cascade, Windows of active workbook; `view.arrangeAll` tiles by default) lays the visible windows out over the main window's monitor, the active one first. Web: one window | Arrange All uses one monitor; a hidden window (View › Hide) has no native window; every window repaints when one does; see [hardware-parity.md](hardware-parity.md) |
@@ -194,6 +194,7 @@ scrollbar bugs 2–4 · pixel/feel pass against observed Excel 7–12.
| Date | Change | Summary |
|---|---|---|
| 2026-10-31 | minor | Esc in a File-page text field leaves the field and cancels editing; the next Esc returns to the sheet (#3035) |
| 2026-10-30 | minor | Checkbox cells print and export to PDF as boxes, as on screen: unchecked white with a grey outline, checked green with a white tick (#2972). They printed their value as TRUE or FALSE; other values in a checkbox cell still print as text |
| 2026-10-29 | minor | Formula pointing from the keyboard: Ctrl+arrow (⌘arrow) moves the pointed cell to the edge of its data and Ctrl+Shift+arrow (⌘⇧arrow) extends the pointed range there, as the same keys move and extend the selection, so typing `=COUNTIF(`, clicking the top of a column and pressing ⌘⇧↓ points at the whole column of data; the keys used to move or extend by one cell. A range reaching the sheet edge is written as whole columns or rows (`A:B`). The editor reads the modifiers of the arrow's own key event, so a keystroke pressed and released within one frame (a control client's `ui.key`, an automation tool) keeps its Shift and Ctrl |
| 2026-10-29 | minor | Insert Hyperlink opens with an existing link's address, display text and ScreenTip (#2876). An address-only edit preserves the cell's content and formula; Cancel leaves the link unchanged, and Undo restores the edit |