Ribbon: each window keeps its own tab, so Table Design stays active (#2903)

This commit is contained in:
Max Murphy
2026-10-31 16:58:14 +01:00
parent 67e39faeea
commit 85bfe358dc
2 changed files with 55 additions and 3 deletions
+9 -3
View File
@@ -26,15 +26,19 @@ pub struct WindowUi {
selected_chart: Option<u32>,
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);
@@ -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");
}