diff --git a/crates/egui_extras/src/dock/branch/tabs.rs b/crates/egui_extras/src/dock/branch/tabs.rs index 20998dde0..9726b5b49 100644 --- a/crates/egui_extras/src/dock/branch/tabs.rs +++ b/crates/egui_extras/src/dock/branch/tabs.rs @@ -28,12 +28,17 @@ impl Tabs { } pub fn layout( - &self, + &mut self, nodes: &mut Nodes, style: &egui::Style, behavior: &mut dyn Behavior, rect: Rect, ) { + if !self.children.iter().any(|&child| child == self.active) { + // Make sure something is active: + self.active = self.children.first().copied().unwrap_or_default(); + } + let mut active_rect = rect; active_rect.min.y += behavior.tab_bar_height(style); @@ -50,11 +55,6 @@ impl Tabs { rect: Rect, node_id: NodeId, ) { - if !self.children.iter().any(|&child| child == self.active) { - // Make sure something is active: - self.active = self.children.first().copied().unwrap_or_default(); - } - let next_active = self.tab_bar_ui(behavior, ui, rect, nodes, drop_context, node_id); // When dragged, don't show it (it is "being held") diff --git a/crates/egui_extras/src/dock/mod.rs b/crates/egui_extras/src/dock/mod.rs index b0dd680db..8005fe6d5 100644 --- a/crates/egui_extras/src/dock/mod.rs +++ b/crates/egui_extras/src/dock/mod.rs @@ -44,7 +44,7 @@ impl std::fmt::Debug for NodeId { } /// The top level type. Contains all persistent state, including layouts and sizes. -#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] +#[derive(Clone, serde::Serialize, serde::Deserialize)] pub struct Dock { pub root: NodeId, pub nodes: Nodes, @@ -64,6 +64,45 @@ impl Default for Dock { } } +impl std::fmt::Debug for Dock { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn format_node( + f: &mut std::fmt::Formatter<'_>, + nodes: &Nodes, + indent: usize, + node_id: NodeId, + ) -> std::fmt::Result { + write!(f, "{} {node_id:?} ", " ".repeat(indent))?; + if let Some(node) = nodes.get(node_id) { + match node { + Node::Leaf(leaf) => writeln!(f, "Leaf {leaf:?}"), + Node::Branch(branch) => { + writeln!( + f, + "{}", + match branch { + Branch::Tabs(_) => "Tabs", + Branch::Linear(_) => "Linear", + Branch::Grid(_) => "Grid", + } + )?; + for &child in branch.children() { + format_node(f, nodes, indent + 1, child)?; + } + Ok(()) + } + } + } else { + write!(f, "DANGLING {node_id:?}") + } + } + + writeln!(f, "Dock {{")?; + format_node(f, &self.nodes, 1, self.root)?; + write!(f, "\n}}") + } +} + /// Contains all node state, but no root. #[derive(Clone, Debug, serde::Serialize, serde::Deserialize)] pub struct Nodes { @@ -362,6 +401,7 @@ impl Dock { self.nodes .make_all_leaves_children_of_tabs(false, self.root); } + self.nodes.gc_root(behavior, self.root); self.nodes.rects.clear(); @@ -702,7 +742,13 @@ impl Nodes { ui: &mut Ui, node_id: NodeId, ) { - let (Some(rect), Some(mut node)) = (self.try_rect(node_id), self.nodes.remove(&node_id)) else { + // NOTE: important that we get thr rect and node in two steps, + // otherwise we could loose the node when there is no rect. + let Some(rect) = self.try_rect(node_id) else { + log::warn!("Failed to find rect for node {node_id:?} during ui"); + return + }; + let Some(mut node) = self.nodes.remove(&node_id) else { log::warn!("Failed to find node {node_id:?} during ui"); return }; @@ -755,7 +801,10 @@ enum SimplifyAction { impl Nodes { fn simplify(&mut self, options: &SimplificationOptions, it: NodeId) -> SimplifyAction { - let Some(mut node) = self.nodes.remove(&it) else { return SimplifyAction::Remove; }; + let Some(mut node) = self.nodes.remove(&it) else { + log::warn!("Failed to find node {it:?} during simplify"); + return SimplifyAction::Remove; + }; if let Node::Branch(branch) = &mut node { // TODO: join nested versions of the same horizontal/vertical layouts diff --git a/examples/dock/src/main.rs b/examples/dock/src/main.rs index e2e27f522..33ae2e9e3 100644 --- a/examples/dock/src/main.rs +++ b/examples/dock/src/main.rs @@ -1,7 +1,6 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release -use eframe::egui::{self, Style}; -use egui::Color32; +use eframe::egui; use egui_extras::dock; @@ -28,20 +27,23 @@ fn main() -> Result<(), eframe::Error> { #[derive(serde::Deserialize, serde::Serialize)] pub struct View { - title: String, - color: Color32, + nr: usize, +} + +impl std::fmt::Debug for View { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("View").field("nr", &self.nr).finish() + } } impl View { - pub fn with_nr(i: usize) -> Self { - Self { - title: format!("View {i}"), - color: egui::epaint::Hsva::new(0.1 * i as f32, 0.5, 0.5, 1.0).into(), - } + pub fn with_nr(nr: usize) -> Self { + Self { nr } } pub fn ui(&mut self, ui: &mut egui::Ui) -> dock::UiResponse { - ui.painter().rect_filled(ui.max_rect(), 0.0, self.color); + let color = egui::epaint::Hsva::new(0.1 * self.nr as f32, 0.5, 0.5, 1.0); + ui.painter().rect_filled(ui.max_rect(), 0.0, color); let dragged = ui .allocate_rect(ui.max_rect(), egui::Sense::drag()) .on_hover_cursor(egui::CursorIcon::Grab) @@ -118,7 +120,7 @@ impl dock::Behavior for DockBehavior { } fn tab_text_for_leaf(&mut self, view: &View) -> egui::WidgetText { - view.title.clone().into() + format!("View {}", view.nr).into() } fn top_bar_rtl_ui(&mut self, ui: &mut egui::Ui, node_id: dock::NodeId) { @@ -130,11 +132,11 @@ impl dock::Behavior for DockBehavior { // --- // Settings: - fn tab_bar_height(&self, _style: &Style) -> f32 { + fn tab_bar_height(&self, _style: &egui::Style) -> f32 { self.tab_bar_height } - fn gap_width(&self, _style: &Style) -> f32 { + fn gap_width(&self, _style: &egui::Style) -> f32 { self.gap_width } @@ -147,8 +149,11 @@ impl dock::Behavior for DockBehavior { struct MyApp { dock: dock::Dock, - #[serde(skip, default)] + #[serde(skip)] behavior: DockBehavior, + + #[serde(skip)] + last_dock_debug: String, } impl Default for MyApp { @@ -163,38 +168,24 @@ impl Default for MyApp { let mut nodes = dock::Nodes::default(); let mut tabs = vec![]; - tabs.push(nodes.insert_leaf(gen_view())); + let tab_node = { + let children = (0..7).map(|_| nodes.insert_leaf(gen_view())).collect(); + nodes.insert_tab_node(children) + }; + tabs.push(tab_node); tabs.push({ - let a = nodes.insert_leaf(gen_view()); - let b = nodes.insert_leaf(gen_view()); - let c = nodes.insert_leaf(gen_view()); - let d = nodes.insert_leaf(gen_view()); - let e = nodes.insert_leaf(gen_view()); - nodes.insert_tab_node(vec![a, b, c, d, e]) + let children = (0..7).map(|_| nodes.insert_leaf(gen_view())).collect(); + nodes.insert_horizontal_node(children) }); tabs.push({ - let a = nodes.insert_leaf(gen_view()); - let b = nodes.insert_leaf(gen_view()); - let c = nodes.insert_leaf(gen_view()); - let d = nodes.insert_leaf(gen_view()); - let e = nodes.insert_leaf(gen_view()); - nodes.insert_horizontal_node(vec![a, b, c, d, e]) + let children = (0..7).map(|_| nodes.insert_leaf(gen_view())).collect(); + nodes.insert_vertical_node(children) }); tabs.push({ - let a = nodes.insert_leaf(gen_view()); - let b = nodes.insert_leaf(gen_view()); - let c = nodes.insert_leaf(gen_view()); - let d = nodes.insert_leaf(gen_view()); - let e = nodes.insert_leaf(gen_view()); - nodes.insert_vertical_node(vec![a, b, c, d, e]) - }); - tabs.push({ - let mut cells = vec![]; - for _ in 0..12 { - cells.push(nodes.insert_leaf(gen_view())); - } + let cells = (0..12).map(|_| nodes.insert_leaf(gen_view())).collect(); nodes.insert_grid_node(cells) }); + tabs.push(nodes.insert_leaf(gen_view())); let root = nodes.insert_tab_node(tabs); @@ -203,6 +194,7 @@ impl Default for MyApp { Self { dock, behavior: Default::default(), + last_dock_debug: Default::default(), } } } @@ -217,8 +209,9 @@ impl eframe::App for MyApp { ui.separator(); tree_ui(ui, &mut self.behavior, &mut self.dock.nodes, self.dock.root); + if let Some(parent) = self.behavior.add_child_to.take() { - let new_child = self.dock.nodes.insert_leaf(View::with_nr(666)); + let new_child = self.dock.nodes.insert_leaf(View::with_nr(100)); if let Some(dock::Node::Branch(dock::Branch::Tabs(tabs))) = self.dock.nodes.get_mut(parent) { @@ -226,6 +219,15 @@ impl eframe::App for MyApp { tabs.set_active(new_child); } } + + ui.separator(); + ui.style_mut().wrap = Some(false); + let dock_debug = format!("{:#?}", self.dock); + ui.monospace(&dock_debug); + if self.last_dock_debug != dock_debug { + self.last_dock_debug = dock_debug; + log::debug!("{}", self.last_dock_debug); + } }); egui::CentralPanel::default().show(ctx, |ui| { @@ -244,7 +246,7 @@ fn tree_ui( nodes: &mut dock::Nodes, node_id: dock::NodeId, ) { - // Get the name BEFORE we remove the node below + // Get the name BEFORE we remove the node below! let text = format!( "{} - {node_id:?}", behavior.tab_text_for_node(nodes, node_id).text()