diff --git a/crates/ui-egui/src/panel_docking.rs b/crates/ui-egui/src/panel_docking.rs index a90c4bf5c..9b5b23868 100644 --- a/crates/ui-egui/src/panel_docking.rs +++ b/crates/ui-egui/src/panel_docking.rs @@ -504,6 +504,11 @@ struct PanelContent<'a> { impl craft_ui::docking::DockContent for PanelContent<'_> { fn body(&mut self, ui: &mut egui::Ui, panel: &String) { + let t = crate::theme::Tokens::get(ui.ctx()); + let back = ui.max_rect(); + if back.is_positive() { + ui.painter().rect_filled(back, 0.0, t.panel); + } let width = ui.available_width(); let height = (ui.available_height() - 20.0).max(0.0); if crate::state::DockTab::from_id(panel).is_some() { @@ -637,10 +642,13 @@ pub(crate) fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) -> bool { let fills: Vec<_> = display .floating .iter() - .filter_map(|group| group.panels.first()) - .map(|first| { + .filter_map(|group| { + let first = group.panels.first()?; let layer = egui::LayerId::new(egui::Order::Middle, area.with(("floating", first))); - (layer, ui.ctx().layer_painter(layer).add(egui::Shape::Noop)) + let [x, y, w, h] = group.rect; + let layout_rect = egui::Rect::from_min_size(egui::pos2(x, y), egui::vec2(w, h)); + let fill = ui.ctx().layer_painter(layer).add(egui::Shape::Noop); + Some((layer, layout_rect, fill)) }) .collect(); let mut draw = |ui: &mut egui::Ui| { @@ -787,8 +795,13 @@ pub(crate) fn show(app: &mut VectorcraftApp, ui: &mut egui::Ui) -> bool { ui.ctx().data_mut(|data| data.insert_temp(crate::floating::group_rect_id(first), rect)); } } - for (layer, fill) in fills { - if let Some(rect) = ui.ctx().memory(|memory| memory.area_rect(layer.id)) { + for (layer, layout_rect, fill) in fills { + let rect = ui + .ctx() + .memory(|memory| memory.area_rect(layer.id)) + .filter(|rect| rect.is_positive()) + .unwrap_or(layout_rect); + if rect.is_positive() { ui.ctx().layer_painter(layer).set(fill, egui::Shape::rect_filled(rect, 0.0, t.panel)); } } diff --git a/crates/ui-egui/src/panel_docking_tests.rs b/crates/ui-egui/src/panel_docking_tests.rs index 01b6ca9ba..dbd77ed33 100644 --- a/crates/ui-egui/src/panel_docking_tests.rs +++ b/crates/ui-egui/src/panel_docking_tests.rs @@ -616,7 +616,7 @@ fn the_window_menu_item_hides_a_shown_panel_and_shows_it_again() { } /// A floating group lies on the canvas, not on the dock's card, so its tab strip and body paint -/// their own opaque background (#2421). +/// their own opaque background (#2421, #2646). #[test] fn floating_group_paints_an_opaque_background() { let mut app = app(); @@ -641,6 +641,27 @@ fn floating_group_paints_an_opaque_background() { assert!(opaque_at(pos2(690.0, 610.0)), "body is transparent"); } +/// Undocking a tabbed dock panel leaves an opaque backdrop even in the empty part of its body (#2646). +#[test] +fn undocked_layers_panel_body_is_opaque() { + let mut app = app(); + app.run("window.panel.float", json!({"panel":"layers","x":300,"y":160,"width":300,"height":420})).unwrap(); + let h = harness(app, vec2(1280.0, 800.0)); + fn topmost(shape: &egui::Shape, p: Pos2, found: &mut Option) { + match shape { + egui::Shape::Rect(r) if r.rect.contains(p) && r.rect.width() >= 80.0 && r.stroke.is_empty() && r.fill.a() > 0 => *found = Some(r.fill), + egui::Shape::Vec(v) => v.iter().for_each(|s| topmost(s, p, found)), + _ => {} + } + } + let opaque_at = |p: Pos2| { + let mut found = None; + h.output().shapes.iter().for_each(|s| topmost(&s.shape, p, &mut found)); + found.is_some_and(|c| c.a() == 255) + }; + assert!(opaque_at(pos2(580.0, 520.0)), "empty layers body shows the canvas"); +} + /// Library panels (#2603) float in the shared layout titled by their libraries' names, and their /// menu's Persistent item keeps them for the next launch. #[test]