From 880b58729b242fd04bcb75cad84e7f57e987c598 Mon Sep 17 00:00:00 2001 From: dotCooCoo Date: Wed, 11 Nov 2026 14:40:14 -0800 Subject: [PATCH] Panels: a panel floated while the floating panels are hidden opens hidden The floating panels' windows hide while a dialog, a menu, a tool flyout or the All Tools drawer is open, but the commands went out only when that state changed. A panel floated meanwhile opened shown at its topmost level, over the dialog or drawer. A window that floats while the others are hidden now gets the hide commands in its first frame. --- crates/ui-egui/src/panel_docking.rs | 24 ++++++++++----- .../ui-egui/src/panel_docking/native_tests.rs | 30 +++++++++++++++++++ 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/crates/ui-egui/src/panel_docking.rs b/crates/ui-egui/src/panel_docking.rs index 3447cd1e1..ef502761d 100644 --- a/crates/ui-egui/src/panel_docking.rs +++ b/crates/ui-egui/src/panel_docking.rs @@ -1238,18 +1238,28 @@ fn palette_window_state(modal: bool, popup: bool, active: bool) -> (egui::Window /// Sends the palette windows of `layout` the level and visibility [`palette_window_state`] gives /// when they change. craft-ui varies a palette's level through the viewport builder, which egui /// applies when the window opens and not to one already on screen, so the commands go explicitly to -/// the windows there are. +/// the windows there are. A window that opens while the others are hidden (a panel floated under a +/// dialog, a menu, a flyout or the drawer) opens shown at its palette level, so it gets the commands +/// in its first frame. #[cfg(not(target_arch = "wasm32"))] fn palette_window_levels(ctx: &egui::Context, layout: &Layout, modal: bool, popup: bool) { let active = ctx.input(|i| i.focused || i.raw.viewports.values().any(|v| v.focused == Some(true))); let key = area_id().with("palette-levels-state"); - if ctx.data(|d| d.get_temp::<(bool, bool, bool)>(key)) == Some((modal, popup, active)) { - return; - } - ctx.data_mut(|d| d.insert_temp(key, (modal, popup, active))); - + let state = (modal, popup, active); + let windows: Vec = layout.floating.iter().map(|window| window.id).collect(); + let sent = ctx.data(|d| d.get_temp::<((bool, bool, bool), Vec)>(key)); let (level, visible) = palette_window_state(modal, popup, active); - for window in &layout.floating { + // The windows that already have this state's commands. + let known: &[u64] = match &sent { + Some((was, ids)) if *was == state && !visible => ids, + Some((was, _)) if *was == state => &windows, + _ => &[], + }; + let fresh: Vec = windows.iter().copied().filter(|id| !known.contains(id)).collect(); + if sent.as_ref().is_none_or(|(was, ids)| *was != state || *ids != windows) { + ctx.data_mut(|d| d.insert_temp(key, (state, windows))); + } + for window in layout.floating.iter().filter(|window| fresh.contains(&window.id)) { let viewport = craft_ui::docking::DockArea::::window_viewport(area_id(), window.id); ctx.send_viewport_cmd_to(viewport, egui::ViewportCommand::WindowLevel(level)); ctx.send_viewport_cmd_to(viewport, egui::ViewportCommand::Visible(visible)); diff --git a/crates/ui-egui/src/panel_docking/native_tests.rs b/crates/ui-egui/src/panel_docking/native_tests.rs index db1e6be36..2f1d0d536 100644 --- a/crates/ui-egui/src/panel_docking/native_tests.rs +++ b/crates/ui-egui/src/panel_docking/native_tests.rs @@ -842,6 +842,36 @@ fn a_torn_off_strip_or_a_new_toolbar_leaves_the_palette_windows_shown() { assert_eq!(take_palette_commands(&h, viewport).0, [false], "still shown: the drawer hides them again"); } +/// A panel floated while the palette windows are hidden opens hidden too, and shows with the others +/// once the drawer or the dialog over them closes. +#[test] +fn a_panel_floated_while_the_palette_windows_are_hidden_opens_hidden() { + let mut h = Harness::new(); + let navigator = h.float("navigator"); + h.host.borrow_mut().focus = ViewportId::ROOT; + h.frames(2); + take_palette_commands(&h, navigator); + h.app.run("window.editToolbar", json!({"open": true})).unwrap(); + h.frames(2); + assert_eq!(take_palette_commands(&h, navigator).0, [false], "hidden under the drawer"); + let layers = h.float("layers"); + h.host.borrow_mut().focus = ViewportId::ROOT; + assert!(h.app.ui.tools_drawer, "the drawer stays open"); + let (shown, levels) = take_palette_commands(&h, layers); + assert_eq!(shown, [false], "the new palette window is hidden under the drawer"); + assert_eq!(levels, [egui::WindowLevel::Normal], "and out of the top level"); + h.app.run("window.editToolbar", json!({"open": false})).unwrap(); + h.frames(2); + assert_eq!(take_palette_commands(&h, layers).0, [true], "shown when the drawer closes"); + + h.app.ui.palette_open = true; + h.frames(2); + assert_eq!(take_palette_commands(&h, layers).0, [false], "hidden under the command palette"); + let swatches = h.float("swatches"); + h.host.borrow_mut().focus = ViewportId::ROOT; + assert_eq!(take_palette_commands(&h, swatches).0, [false], "a panel floated under it is hidden too"); +} + /// The frame in which a click elsewhere closes a flyout or the drawer shows the palette windows, as /// no later frame need come while the app is idle. #[test]