diff --git a/crates/ui-egui/src/panel_docking.rs b/crates/ui-egui/src/panel_docking.rs index 87f3583d0..ff39d90cf 100644 --- a/crates/ui-egui/src/panel_docking.rs +++ b/crates/ui-egui/src/panel_docking.rs @@ -271,8 +271,25 @@ pub(crate) fn showing(ui: &UiState, panel: &str) -> bool { } /// Can either panel that uses row thumbnails be seen, including in a floating window or flyout? -fn thumbnail_panel_showing(ui: &UiState) -> bool { - ui.dock && ui.screen_mode < 3 && (showing(ui, "layers") || showing(ui, "links")) +/// An iconic group's open flyout is named for the icon that opened it; its active tab can differ. +fn thumbnail_panel_showing(ui: &UiState, ctx: &egui::Context) -> bool { + if !ui.dock || ui.screen_mode >= 3 { + return false; + } + let layout = layout_of(ui); + let regular = ["layers", "links"] + .iter() + .any(|panel| !in_iconic_column(&layout, panel) && layout.panel_state(&(*panel).to_string()) == craft_ui::docking::PanelState::Front); + let flyout = craft_ui::docking::flyout::(ctx, area_id()); + let iconic = flyout.as_ref().and_then(|anchor| { + dock_columns(&layout) + .into_iter() + .filter(|column| column.iconic) + .flat_map(|column| &column.groups) + .find(|group| group.panels.contains(anchor)) + .and_then(|group| group.active_panel()) + }); + regular || iconic.is_some_and(|panel| matches!(panel.as_str(), "layers" | "links")) } /// Where a panel with no other destination goes: a panel of the first expanded dock column, @@ -1178,7 +1195,7 @@ pub(crate) fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) { app.open_dropped_files(window.dropped, None, shift); } forward_keys(app, &ctx, output.forwarded_events, output.forwarded_modifiers); - if !thumbnail_panel_showing(&app.ui) { + if !thumbnail_panel_showing(&app.ui, &ctx) { // No panel can ask for these rows again this pass. Keep the cache of open documents, but // discard worker jobs queued behind the one already rendering. crate::panels::layers::show_thumbs_of(None); diff --git a/crates/ui-egui/src/panel_docking/tests.rs b/crates/ui-egui/src/panel_docking/tests.rs index 3afb41f96..60b59a2a8 100644 --- a/crates/ui-egui/src/panel_docking/tests.rs +++ b/crates/ui-egui/src/panel_docking/tests.rs @@ -165,19 +165,46 @@ fn a_tab_dragged_out_floats_and_comes_back_where_it_was_after_its_window_closes( fn thumbnail_work_is_needed_only_while_layers_or_links_can_be_seen() { let mut h = Harness::new(); h.app.run("window.panel", json!({"panel": "layers"})).unwrap(); - assert!(thumbnail_panel_showing(&h.app.ui)); + assert!(thumbnail_panel_showing(&h.app.ui, &h.ctx)); h.app.ui.dock = false; - assert!(!thumbnail_panel_showing(&h.app.ui), "Tab hides every panel"); + assert!(!thumbnail_panel_showing(&h.app.ui, &h.ctx), "Tab hides every panel"); h.app.ui.dock = true; h.app.ui.screen_mode = 3; - assert!(!thumbnail_panel_showing(&h.app.ui), "presentation mode hides panels"); + assert!(!thumbnail_panel_showing(&h.app.ui, &h.ctx), "presentation mode hides panels"); h.app.ui.screen_mode = 0; h.app.run("window.panel.float", json!({"panel": "layers"})).unwrap(); - assert!(thumbnail_panel_showing(&h.app.ui), "a floating Layers panel still needs thumbnails"); + assert!(thumbnail_panel_showing(&h.app.ui, &h.ctx), "a floating Layers panel still needs thumbnails"); h.app.run("window.panel", json!({"panel": "layers", "toggle": true})).unwrap(); - assert!(!thumbnail_panel_showing(&h.app.ui)); + assert!(!thumbnail_panel_showing(&h.app.ui, &h.ctx)); h.app.run("window.panel", json!({"panel": "links"})).unwrap(); - assert!(thumbnail_panel_showing(&h.app.ui), "the Links flyout also uses row thumbnails"); + h.frame(vec![]); + assert!(thumbnail_panel_showing(&h.app.ui, &h.ctx), "the Links flyout also uses row thumbnails"); +} + +#[test] +fn a_collapsed_dock_uses_the_flyouts_active_tab_for_thumbnail_work() { + let mut h = Harness::new(); + h.app.run("window.collapseDock", json!({"collapsed": true})).unwrap(); + // A customized layout keeps the group's active tab independent of the icon that opened it. + h.app.ui.docking = Some(layout_of(&h.app.ui)); + h.app.run("window.panel", json!({"panel": "properties"})).unwrap(); + h.frame(vec![]); + assert!(!thumbnail_panel_showing(&h.app.ui, &h.ctx), "Properties does not use row thumbnails"); + // A tab press emits Activate without revealing a different flyout anchor. + gesture(&mut h.app, Action::Activate { panel: "layers".into() }).unwrap(); + h.frame(vec![]); + assert_eq!(h.app.ui.open_panel.as_deref(), Some("properties"), "the flyout is still named for its opening icon"); + assert!(thumbnail_panel_showing(&h.app.ui, &h.ctx), "the active Layers tab keeps thumbnail jobs"); + gesture(&mut h.app, Action::Activate { panel: "properties".into() }).unwrap(); + h.frame(vec![]); + assert!(!thumbnail_panel_showing(&h.app.ui, &h.ctx), "returning to Properties drops hidden Layers jobs"); + h.app.run("window.panel", json!({"panel": "layers"})).unwrap(); + h.frame(vec![]); + assert!(thumbnail_panel_showing(&h.app.ui, &h.ctx)); + gesture(&mut h.app, Action::Activate { panel: "properties".into() }).unwrap(); + h.frame(vec![]); + assert_eq!(h.app.ui.open_panel.as_deref(), Some("layers")); + assert!(!thumbnail_panel_showing(&h.app.ui, &h.ctx), "a Properties tab under the Layers icon does not need thumbnails"); } /// A Character field shortcut with the Character panel in front of its floating window brings