diff --git a/crates/ui-egui/src/menus.rs b/crates/ui-egui/src/menus.rs index 8e1d8c9c5..b1270f834 100644 --- a/crates/ui-egui/src/menus.rs +++ b/crates/ui-egui/src/menus.rs @@ -988,6 +988,15 @@ pub const MENUS: &[(&str, &[&str])] = &[ pub const WORKSPACES: &[&str] = &["Advanced", "Book", "Digital Publishing", "Essentials", "Essentials Classic", "Interactive for PDF", "Printing and Proofing", "Typography"]; +/// The built-in workspaces that open a panel below the Properties · Pages · Layers group. +pub const WORKSPACE_OPEN_PANELS: &[(&str, &str)] = + &[("Printing and Proofing", "swatches"), ("Interactive for PDF", "buttons"), ("Digital Publishing", "liquid")]; + +/// The panel the built-in workspace `name` opens below the Properties · Pages · Layers group. +pub fn workspace_open_panel(name: &str) -> Option<&'static str> { + WORKSPACE_OPEN_PANELS.iter().find(|(workspace, _)| *workspace == name).map(|(_, panel)| *panel) +} + /// Essentials Classic's iconic column: InDesign's iconic panel groups, top to bottom. pub const CLASSIC_GROUPS: &[&[&str]] = &[ &["pages", "layers", "links"], @@ -1988,12 +1997,7 @@ fn run_ui_inner(app: &mut DesignApp, id: &str, p: &Value) -> Option Some("swatches".into()), - "Interactive for PDF" => Some("buttons".into()), - "Digital Publishing" => Some("liquid".into()), - _ => None, - }; + app.ui.open_panel = workspace_open_panel(name).map(str::to_string); app.ui.workspace = name.to_string(); Ok(Value::Null) } diff --git a/crates/ui-egui/src/prefs.rs b/crates/ui-egui/src/prefs.rs index fd7f198a2..756532fd5 100644 --- a/crates/ui-egui/src/prefs.rs +++ b/crates/ui-egui/src/prefs.rs @@ -32,6 +32,7 @@ impl UiState { } } let mut ui: UiState = serde_json::from_value(saved)?; + clear_stale_open_panels(&mut ui); intact &= migrate(&mut ui.docking, &mut ui.floating, &ui.docking_hidden, ui.dock_expanded, ui.icon_groups.as_deref()); for workspace in &mut ui.custom_workspaces { let SavedWorkspace { docking, floating, docking_hidden, dock_expanded, icon_groups, .. } = workspace; @@ -41,6 +42,27 @@ impl UiState { } } +/// Files from before the canvas joined the panel layout saved the flyout open at quit as +/// `openPanel`, which is the panel a workspace opens below Properties · Pages · Layers. Only a +/// workspace's own panel stays: a built-in workspace's (Printing and Proofing: Swatches), and in a +/// saved workspace one a built-in workspace opens (saving copies it). The current workspace keeps +/// a saved workspace's panel. +fn clear_stale_open_panels(ui: &mut UiState) { + let built_in = |panel: &str| crate::menus::WORKSPACE_OPEN_PANELS.iter().any(|(_, p)| *p == panel); + for workspace in &mut ui.custom_workspaces { + if workspace.open_panel.as_deref().is_some_and(|panel| !built_in(panel)) { + workspace.open_panel = None; + } + } + let own = match ui.custom_workspaces.iter().find(|w| w.name == ui.workspace) { + Some(saved) => saved.open_panel.as_deref(), + None => crate::menus::workspace_open_panel(&ui.workspace), + }; + if ui.open_panel.as_deref() != own { + ui.open_panel = None; + } +} + /// Drop docking data this version cannot read. True when nothing was dropped. fn recover_docking(saved: &mut Value) -> bool { let Some(fields) = saved.as_object_mut() else { return true }; @@ -206,6 +228,25 @@ mod tests { assert_eq!(&layout.floating[0].rect[..2], &[100.0, 120.0]); } + /// An `openPanel` that was the flyout open at quit is not docked below Properties. + #[test] + fn a_flyout_open_at_quit_is_not_docked() { + let ui = load(json!({ + "workspace": "Essentials", "dockExpanded": true, "openPanel": "stroke", + "customWorkspaces": [ + {"name": "Mine", "dockExpanded": true, "openPanel": "effects"}, + {"name": "Proof", "dockExpanded": true, "openPanel": "swatches"}, + ], + })); + assert_eq!(ui.open_panel, None); + let layout = crate::panel_docking::default_layout(&ui); + assert_eq!(panels(&dock(&layout)[0]), [vec!["properties", "pages", "layers"]], "Stroke stays in its icon group"); + assert_eq!(ui.custom_workspaces[0].open_panel, None); + assert_eq!(ui.custom_workspaces[1].open_panel.as_deref(), Some("swatches"), "saved from Printing and Proofing"); + let ui = load(json!({"workspace": "Proof", "openPanel": "swatches", "customWorkspaces": [{"name": "Proof", "openPanel": "swatches"}]})); + assert_eq!(ui.open_panel.as_deref(), Some("swatches"), "the saved workspace's own panel"); + } + #[test] fn dock_only_layout_becomes_the_main_column() { let ui = load(json!({