diff --git a/src/dock.rs b/src/dock.rs index 088ae8d..a919584 100644 --- a/src/dock.rs +++ b/src/dock.rs @@ -80,6 +80,17 @@ pub trait WorkbenchPanel: Send + Sync + std::any::Any + 'static { false } + /// Background color for this panel's tile. Return `None` for transparent. + fn bg_color(&self) -> Option { + Some(egui::Color32::from_rgb(35, 35, 40)) + } + + /// Whether to hide the tab bar for this panel. + /// Use for panels that act as a viewport (e.g., 3D scene) rather than a tool. + fn hide_tab(&self) -> bool { + false + } + /// Whether the panel tab can be closed (default: true). fn closable(&self) -> bool { true @@ -252,26 +263,26 @@ impl TileLayoutState { } }); - // Build tab containers for each slot (always with tab headers for drag support) - let left_tile = Self::make_tab(&mut tiles, left_panes); - let center_tile = Self::make_tab(&mut tiles, center_panes); - let right_tile = Self::make_tab(&mut tiles, right_panes); + // Build tile containers for each slot + let left_tiles = self.make_tile(&mut tiles, left_panes); + let center_tiles = self.make_tile(&mut tiles, center_panes); + let right_tiles = self.make_tile(&mut tiles, right_panes); let bottom_tile = Self::make_tab(&mut tiles, bottom_panes); // Build main horizontal row: [left? | center | right?] let mut main_children = Vec::new(); let mut main_shares = Vec::new(); - if let Some(left) = left_tile { - main_children.push(left); - main_shares.push((left, 1.0)); + for tile in left_tiles { + main_children.push(tile); + main_shares.push((tile, 1.0)); } - if let Some(center) = center_tile { - main_children.push(center); - main_shares.push((center, 4.0)); // center takes most space + for tile in center_tiles { + main_children.push(tile); + main_shares.push((tile, 4.0)); } - if let Some(right) = right_tile { - main_children.push(right); - main_shares.push((right, 1.5)); + for tile in right_tiles { + main_children.push(tile); + main_shares.push((tile, 1.5)); } let root = if main_children.is_empty() && bottom_tile.is_none() { @@ -294,7 +305,7 @@ impl TileLayoutState { if let Some(bottom) = bottom_tile { // Vertical split: main row on top, bottom panel below let root_id = tiles.insert_vertical_tile(vec![main_row, bottom]); - ui::set_linear_shares(&mut tiles, root_id, &[(main_row, 4.0), (bottom, 1.0)]); + ui::set_linear_shares(&mut tiles, root_id, &[(main_row, 10.0), (bottom, 1.0)]); root_id } else { main_row @@ -311,11 +322,43 @@ impl TileLayoutState { if panes.is_empty() { None } else { - // Always wrap in a tab container so every slot has a draggable tab header. Some(tiles.insert_tab_tile(panes)) } } + /// Build tab containers for panels that show tabs. + /// Panels with `hide_tab()` are inserted as plain panes (no tab bar). + fn make_tile( + &self, + tiles: &mut egui_tiles::Tiles, + panes: Vec, + ) -> Vec { + if panes.is_empty() { + return Vec::new(); + } + + // Separate hidden-tab panels from tabbed panels + let mut hidden: Vec = Vec::new(); + let mut tabbed: Vec = Vec::new(); + + for tile_id in panes { + if let Some(egui_tiles::Tile::Pane(pane)) = tiles.get(tile_id) { + if self.panels.get(&pane.panel_id).map_or(false, |p| p.hide_tab()) { + hidden.push(tile_id); + continue; + } + } + tabbed.push(tile_id); + } + + // Wrap tabbed panes in a tab container + let mut result = hidden; + if !tabbed.is_empty() { + result.push(tiles.insert_tab_tile(tabbed)); + } + result + } + /// Focus an existing panel tab by its string ID, re-inserting if closed. pub fn open_or_focus_panel(&mut self, panel_str_id: &str) { let Some(&panel_id) = self.panel_id_map.get(panel_str_id) else { diff --git a/src/dock/ui.rs b/src/dock/ui.rs index 297a6af..2ef224b 100644 --- a/src/dock/ui.rs +++ b/src/dock/ui.rs @@ -66,6 +66,10 @@ impl egui_tiles::Behavior for WorkbenchBehavior<'_> { pane: &mut PaneEntry, ) -> egui_tiles::UiResponse { if let Some(panel) = self.panels.get_mut(&pane.panel_id) { + // Draw tile background from panel's bg_color (None = transparent for 3D scene) + if let Some(color) = panel.bg_color() { + ui.painter().rect_filled(ui.max_rect(), 0.0, color); + } if panel.needs_world() && let Some(world) = self.world.as_deref_mut() { @@ -111,7 +115,7 @@ impl egui_tiles::Behavior for WorkbenchBehavior<'_> { fn simplification_options(&self) -> egui_tiles::SimplificationOptions { egui_tiles::SimplificationOptions { - all_panes_must_have_tabs: true, + all_panes_must_have_tabs: false, ..Default::default() } } @@ -190,7 +194,10 @@ pub fn tiles_ui_system(world: &mut World) { let mut closed_panel_ids: Vec = Vec::new(); if let Some(ref mut tree) = tree { - egui::CentralPanel::default().show(&ctx, |ui| { + // Transparent CentralPanel — lets 3D scene show through behind tiles + egui::CentralPanel::default() + .frame(egui::Frame::NONE) + .show(&ctx, |ui| { let mut behavior = WorkbenchBehavior { panels: &mut panels, world: Some(world), diff --git a/src/lib.rs b/src/lib.rs index d9d9693..49ec045 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -247,6 +247,13 @@ fn assign_primary_egui_context_system( commands.entity(entity).insert(( bevy_egui::EguiContext::default(), bevy_egui::PrimaryEguiContext, + // Disable egui pointer capture so that gizmo picking + // (transform-gizmo-bevy) can receive pointer events in + // areas not covered by interactive egui widgets. + bevy_egui::EguiContextSettings { + capture_pointer_input: false, + ..default() + }, Name::new("workbench_ui_camera"), inspector::WorkbenchInternal, ));