From 85bfe358dce1bdb9905befd208b019a6c1b0a9c7 Mon Sep 17 00:00:00 2001 From: Max Murphy Date: Sat, 31 Oct 2026 16:57:28 +0100 Subject: [PATCH 1/2] Ribbon: each window keeps its own tab, so Table Design stays active (#2903) --- crates/ui-egui/src/window_ui.rs | 12 +++-- .../ui-egui/tests/it/contextual_ribbon_tab.rs | 46 +++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/crates/ui-egui/src/window_ui.rs b/crates/ui-egui/src/window_ui.rs index bc6f31a3d..dbabe9b28 100644 --- a/crates/ui-egui/src/window_ui.rs +++ b/crates/ui-egui/src/window_ui.rs @@ -26,15 +26,19 @@ pub struct WindowUi { selected_chart: Option, tab_run: Option<(u64, usize, u32, CellRef)>, open_switch_windows: bool, + /// The ribbon tab this window shows. A contextual tab depends on this window's selection, so + /// another window's frame must not reset it. + ribbon_tab: String, /// The status bar's Ready/Enter/Edit, which follows the window's cell draft. mode: gridcraft_engine::Mode, } impl WindowUi { /// A new window's state. The last border, fill and font colour picked carry over, as they are - /// the user's recent choices rather than the window's. - fn fresh(like: &grid::GridState) -> WindowUi { + /// the user's recent choices rather than the window's; so does the ribbon tab. + fn fresh(like: &grid::GridState, ribbon_tab: &str) -> WindowUi { WindowUi { + ribbon_tab: ribbon_tab.to_string(), grid: grid::GridState { last_border: like.last_border.clone(), last_fill: like.last_fill.clone(), @@ -64,6 +68,7 @@ impl SheetApp { selected_chart, tab_run, open_switch_windows, + ribbon_tab, mode, } = w; std::mem::swap(&mut self.editor, editor); @@ -79,6 +84,7 @@ impl SheetApp { std::mem::swap(&mut self.selected_chart, selected_chart); std::mem::swap(&mut self.tab_run, tab_run); std::mem::swap(&mut self.open_switch_windows, open_switch_windows); + std::mem::swap(&mut self.ui.ribbon_tab, ribbon_tab); std::mem::swap(&mut self.session.mode, mode); } @@ -100,7 +106,7 @@ impl SheetApp { self.ui_window = active; return; }; - let mut outgoing = WindowUi::fresh(&self.grid); + let mut outgoing = WindowUi::fresh(&self.grid, &self.ui.ribbon_tab); self.swap_window_ui(&mut outgoing); if self.session.window(previous).is_some() { self.parked.insert(previous, outgoing); diff --git a/crates/ui-egui/tests/it/contextual_ribbon_tab.rs b/crates/ui-egui/tests/it/contextual_ribbon_tab.rs index ae318a22a..7ca8e8a21 100644 --- a/crates/ui-egui/tests/it/contextual_ribbon_tab.rs +++ b/crates/ui-egui/tests/it/contextual_ribbon_tab.rs @@ -74,3 +74,49 @@ fn an_ordinary_tab_stays_put_in_a_session() { h.run_steps(3); assert_eq!(h.state().ui.ribbon_tab, "Data"); } + +#[test] +fn a_second_window_does_not_reset_the_first_windows_table_design_tab() { + use egui::{Event, PointerButton, Shape}; + + let mut h = harness(session_with_table()); + let first = h.state().session.active_window().unwrap(); + // A second window on the same workbook, with its active cell outside the table. + h.state_mut().run("view.newWindow", json!({})).unwrap(); + let second = h.state().session.active_window().unwrap(); + h.state_mut().run("selection.set", json!({"cell": "D10"})).unwrap(); + h.state_mut().run("window.activate", json!({"window": first})).unwrap(); + h.run_steps(2); + let tab_center = h + .output() + .shapes + .iter() + .find_map(|s| match &s.shape { + Shape::Text(t) if t.galley.job.text == "Table Design" => Some(t.pos + t.galley.size() / 2.0), + _ => None, + }) + .expect("the Table Design tab is painted while a cell is in the table"); + for pressed in [true, false] { + h.input_mut().events.push(Event::PointerButton { + pos: tab_center, + button: PointerButton::Primary, + pressed, + modifiers: egui::Modifiers::NONE, + }); + h.run_steps(1); + } + h.run_steps(2); + assert_eq!(h.state().ui.ribbon_tab, "Table Design"); + // DocWindows draws every window each frame. The second window's cell is outside the table, + // so its frame falls back to Home; the first window's Table Design tab must survive it. + h.state_mut().show_window(second); + h.run_steps(2); + assert_eq!(h.state().ui.ribbon_tab, "Home", "the second window shows Home for its own selection"); + h.state_mut().enter_window(first); + h.run_steps(2); + assert_eq!(h.state().session.active_window(), Some(first)); + assert_eq!(h.state().ui.ribbon_tab, "Table Design", "the first window's tab survives the second window's frame"); + let d = h.state().session.active().unwrap(); + assert_eq!(d.selection.active.a1(), "A2", "the first window still selects a cell in the table"); + assert!(d.wb.active().unwrap().table_at(d.selection.active).is_some(), "the table is still there"); +} From aacdec4138c37a818d58ccdb364b4bdd28987664 Mon Sep 17 00:00:00 2001 From: Max Murphy Date: Sat, 31 Oct 2026 20:00:24 +0100 Subject: [PATCH 2/2] contributors: add maxm11 entry (sync GitHub name) --- contributors/people.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contributors/people.toml b/contributors/people.toml index 0e5b14ca4..00f6ce76a 100644 --- a/contributors/people.toml +++ b/contributors/people.toml @@ -106,3 +106,6 @@ sync_github_name = true [people.Plutonium-fream] sync_github_name = true + +[people.maxm11] +sync_github_name = true