From d4db58cde467f7a412293e55861d390a7f77b4ad Mon Sep 17 00:00:00 2001 From: aiongg Date: Thu, 22 Oct 2026 16:29:24 +0000 Subject: [PATCH] G3: feedback from a command that switched windows shows in the window it left active after_engine, show_open_warnings and alert bring the per-window state in line with the engine's active window first, so a notice, message or toast from opening a workbook (also by a script on the session) lands in the opened workbook's window instead of being parked with the previous one. --- crates/ui-egui/src/lib.rs | 7 +++++++ crates/ui-egui/tests/doc_windows.rs | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+) diff --git a/crates/ui-egui/src/lib.rs b/crates/ui-egui/src/lib.rs index f405a3d18..19f69a90f 100644 --- a/crates/ui-egui/src/lib.rs +++ b/crates/ui-egui/src/lib.rs @@ -648,6 +648,8 @@ impl SheetApp { /// Shows a command error: a toast for an unavailable command, else a message box. fn alert(&mut self, e: String) { + // Shown in the window the user ends up in, also after a command that switched windows. + self.follow_active_window(); if e.contains("is not available right now") { self.toast = Some((e, now_ms())); } else { @@ -799,6 +801,8 @@ impl SheetApp { /// Picks up requests commands made (dialogs, edit mode…). pub fn after_engine(&mut self) { + // The requests belong to the window the command left active. + self.follow_active_window(); for req in self.session.take_ui_requests() { match req { UiRequest::Dialog(name, params) => self.open_dialog(&name, params), @@ -924,6 +928,9 @@ impl SheetApp { /// most important one (a preserved VBA project outranks, say, a skipped optional part). /// Kept deliberately generic: any open-time warning can reach the user this way. pub fn show_open_warnings(&mut self, r: &Json) { + // The opened workbook's window shows them, however it was opened (a script may have run + // `file.open` on the session directly). + self.follow_active_window(); let Some(list) = r.get("warnings").and_then(Json::as_array) else { self.notice = None; return; diff --git a/crates/ui-egui/tests/doc_windows.rs b/crates/ui-egui/tests/doc_windows.rs index ff8e2d631..b6d518ce2 100644 --- a/crates/ui-egui/tests/doc_windows.rs +++ b/crates/ui-egui/tests/doc_windows.rs @@ -282,3 +282,28 @@ fn menu_commands_follow_the_focused_window_and_leaving_a_window_enters_its_draft desk.app.run("cell.set", json!({"cell": "B1", "input": "menu"})).unwrap(); assert_eq!(desk.app.session.documents()[0].wb.active().unwrap().value(CellRef::new(0, 1)), Value::text("menu")); } + +#[test] +fn feedback_from_opening_a_workbook_shows_in_its_new_window() { + let mut desk = Desk::new(true); + desk.app.run("cell.set", json!({"cell": "A1", "input": "kept"})).unwrap(); + desk.frames(2); + let book1 = desk.window_of(0); + let mut wb = gridcraft_engine::model::Workbook::new(); + wb.vba_project = Some(b"\xd0\xcf\x11\xe0\xa1\xb1\x1a\xe1 fake OLE2 vbaProject".to_vec().into()); + let b64 = gridcraft_engine::io::base64_encode(&gridcraft_engine::io::save_bytes(&wb, "macros.xlsm").unwrap()); + desk.frame_with(|app, _| { + // A script opening the file on the session, then the interface reporting on it. + let r = app.session.run("file.open", json!({"name": "macros.xlsm", "base64": b64})).unwrap(); + app.show_open_warnings(&r); + app.run_or_alert("no.such.command", json!({})); + }); + desk.frames(2); + let opened = desk.window_of(1); + assert_eq!(desk.windows.shown_windows(), [book1, opened]); + assert_eq!(desk.app.session.active_window(), Some(opened)); + assert_eq!(desk.app.notice.as_ref().map(|n| n.kind), Some(gridcraft_ui_egui::NoticeKind::Warning), "the macro notice shows in the opened window"); + assert!(desk.app.message.is_some(), "and so does the alert"); + desk.app.enter_window(book1); + assert!(desk.app.notice.is_none() && desk.app.message.is_none(), "Book1's window shows neither"); +}