G4: two windows on one workbook without copying it; Arrange All

- The workbook's active sheet is an atomic set in place (Workbook::active_sheet /
  set_active_sheet), so a window swap or sheet switch never clones the
  Arc<Workbook> or marks it dirty. Selections already live in DocState and the
  window views. Save records the sheet of the most recently active window.
- A dialog taken out of its window while it runs goes back to that window when
  a command run from it switched windows (dialogs::show, ui.dialog.confirm).
- view.arrangeAll {arrange?: tiled|horizontal|vertical|cascade, activeWorkbook?}
  returns the visible windows, active first; the desktop moves and sizes the
  native windows over the main window's monitor. New Window and Arrange All
  buttons on the View ribbon; Arrange Windows dialog.
- Docs: ui-parity, hardware-parity, gaps #20, target-app-parity, ROADMAP.
This commit is contained in:
aiongg
2026-10-22 16:50:10 +00:00
parent d4db58cde4
commit 2ed3df2bd6
56 changed files with 620 additions and 224 deletions
+3 -3
View File
@@ -373,8 +373,8 @@ fn structure_problem(wb: &Workbook, sel: &gridcraft_engine::Selection) -> Option
if wb.sheets.is_empty() {
return Some("no sheets left".into());
}
if wb.active_sheet >= wb.sheets.len() {
return Some(format!("active sheet {} of {}", wb.active_sheet, wb.sheets.len()));
if wb.active_sheet() >= wb.sheets.len() {
return Some(format!("active sheet {} of {}", wb.active_sheet(), wb.sheets.len()));
}
let mut tables = BTreeSet::new();
for (i, sh) in wb.sheets.iter().enumerate() {
@@ -490,7 +490,7 @@ fn watch_list(wb: &Workbook) -> Option<&str> {
/// reset: undo restores view state as it was, but switching sheets isn't an undoable edit.
fn canonical(wb: &Workbook) -> Workbook {
let mut wb = wb.clone();
wb.active_sheet = 0;
wb.set_active_sheet(0);
wb.names.retain(|n| n.name != WATCH_NAME);
for sh in wb.sheets.iter_mut() {
let sh = Arc::make_mut(sh);
@@ -25,7 +25,7 @@ fn next_stays_on_the_last_visible_sheet() {
for _ in 0..2 {
session.execute("sheet.next", json!({})).unwrap();
let doc = session.active().unwrap();
assert_eq!(doc.wb.active_sheet, 1);
assert_eq!(doc.wb.active_sheet(), 1);
assert_eq!(doc.selection.active.a1(), "C7");
}
}
@@ -36,7 +36,7 @@ fn previous_stays_on_the_first_visible_sheet() {
for _ in 0..2 {
session.execute("sheet.previous", json!({})).unwrap();
let doc = session.active().unwrap();
assert_eq!(doc.wb.active_sheet, 2);
assert_eq!(doc.wb.active_sheet(), 2);
assert_eq!(doc.selection.active.a1(), "C7");
}
}
@@ -45,10 +45,10 @@ fn previous_stays_on_the_first_visible_sheet() {
fn navigation_skips_hidden_sheets_in_both_directions() {
let mut session = workbook(&[1, 2], 0);
session.execute("sheet.next", json!({})).unwrap();
assert_eq!(session.active().unwrap().wb.active_sheet, 3);
assert_eq!(session.active().unwrap().wb.active_sheet(), 3);
session.execute("sheet.previous", json!({})).unwrap();
let doc = session.active().unwrap();
assert_eq!(doc.wb.active_sheet, 0);
assert_eq!(doc.wb.active_sheet(), 0);
assert_eq!(doc.selection.active.a1(), "C7");
}
@@ -58,7 +58,7 @@ fn navigation_with_one_visible_sheet_is_a_noop() {
for command in ["sheet.previous", "sheet.next"] {
session.execute(command, json!({})).unwrap();
let doc = session.active().unwrap();
assert_eq!(doc.wb.active_sheet, 1);
assert_eq!(doc.wb.active_sheet(), 1);
assert_eq!(doc.selection.active.a1(), "C7");
}
}
@@ -73,6 +73,6 @@ fn very_hidden_sheets_are_skipped_at_interior_and_boundary_positions() {
}
for (command, expected) in [("sheet.next", 2), ("sheet.next", 2), ("sheet.previous", 0)] {
session.execute(command, json!({})).unwrap();
assert_eq!(session.active().unwrap().wb.active_sheet, expected);
assert_eq!(session.active().unwrap().wb.active_sheet(), expected);
}
}