diff --git a/CHANGELOG.md b/CHANGELOG.md index d1fb46a8..b2b995b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # egui_dock changelog +## 0.11.2 - 2024-02-16 + +### Fixed +From [#225](https://github.com/Adanos020/egui_dock/pull/225): +- Tabs now always appear at the pointer position while being dragged. +- Retaining tabs no longer breaks the binary tree leading to a panic. +- Filtering tabs no longer leaves some leaves empty and now correctly rearranges the tree. + ## 0.11.1 - 2024-02-09 ### Fixed diff --git a/Cargo.toml b/Cargo.toml index 47bd1009..24b0be5a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "egui_dock" description = "Docking system for egui - an immediate-mode GUI library for Rust" authors = ["lain-dono", "Adam Gąsior (Adanos020)"] -version = "0.11.1" +version = "0.11.2" edition = "2021" rust-version = "1.72" license = "MIT" diff --git a/src/dock_state/tree/mod.rs b/src/dock_state/tree/mod.rs index 2a52f25c..3bf861a1 100644 --- a/src/dock_state/tree/mod.rs +++ b/src/dock_state/tree/mod.rs @@ -25,6 +25,7 @@ pub use node_index::NodeIndex; pub use tab_index::TabIndex; pub use tab_iter::TabIter; +use egui::ahash::HashSet; use egui::Rect; use std::{ fmt, @@ -744,20 +745,24 @@ impl Tree { focused_node, nodes, } = self; + let mut emptied_nodes = HashSet::default(); let nodes = nodes .iter() - .filter_map(|node| { + .enumerate() + .map(|(index, node)| { let node = node.filter_map_tabs(function.clone()); - match node { - Node::Leaf { ref tabs, .. } => (!tabs.is_empty()).then_some(node), - _ => Some(node), + if node.is_empty() { + emptied_nodes.insert(NodeIndex(index)); } + node }) .collect(); - Tree { + let mut new_tree = Tree { nodes, focused_node: *focused_node, - } + }; + new_tree.balance(emptied_nodes); + new_tree } /// Returns a new [`Tree`] while mapping the tab type. @@ -784,10 +789,33 @@ impl Tree { where F: Clone + FnMut(&mut Tab) -> bool, { - self.nodes.retain_mut(|node| { + let mut emptied_nodes = HashSet::default(); + for (index, node) in self.nodes.iter_mut().enumerate() { node.retain_tabs(predicate.clone()); - !node.is_empty() - }); + if node.is_empty() { + emptied_nodes.insert(NodeIndex(index)); + } + } + self.balance(emptied_nodes); + } + + fn balance(&mut self, emptied_nodes: HashSet) { + let mut emptied_parents = HashSet::default(); + for parent_index in emptied_nodes.into_iter().filter_map(|ni| ni.parent()) { + if self[parent_index.left()].is_empty() && self[parent_index.right()].is_empty() { + self[parent_index] = Node::Empty; + emptied_parents.insert(parent_index); + } else if self[parent_index.left()].is_empty() { + self.nodes.swap(parent_index.0, parent_index.right().0); + self[parent_index.right()] = Node::Empty; + } else if self[parent_index.right()].is_empty() { + self.nodes.swap(parent_index.0, parent_index.left().0); + self[parent_index.left()] = Node::Empty; + } + } + if !emptied_parents.is_empty() { + self.balance(emptied_parents); + } } } diff --git a/src/widgets/dock_area/show/leaf.rs b/src/widgets/dock_area/show/leaf.rs index 8d0cec52..569758c2 100644 --- a/src/widgets/dock_area/show/leaf.rs +++ b/src/widgets/dock_area/show/leaf.rs @@ -210,7 +210,7 @@ impl<'tree, Tab> DockArea<'tree, Tab> { .with((tab_index, "tab")); let tab_index = TabIndex(tab_index); let is_being_dragged = tabs_ui.memory(|mem| mem.is_being_dragged(id)) - && tabs_ui.input(|i| i.pointer.primary_down() || i.pointer.primary_released()) + && tabs_ui.input(|i| i.pointer.is_decidedly_dragging()) && self.draggable_tabs; if is_being_dragged { @@ -255,8 +255,7 @@ impl<'tree, Tab> DockArea<'tree, Tab> { .response; let title_id = response.id; - let sense = Sense::click_and_drag(); - let response = tabs_ui.interact(response.rect, id, sense); + let response = tabs_ui.interact(response.rect, id, Sense::click_and_drag()); if let Some(pointer_pos) = tabs_ui.ctx().pointer_interact_pos() { let start = *state.drag_start.get_or_insert(pointer_pos); @@ -369,6 +368,7 @@ impl<'tree, Tab> DockArea<'tree, Tab> { // the underlying tab if state.drag_start.is_some() && response.rect.contains(pos) { self.tab_hover_rect = Some((response.rect, tab_index)); + state.drag_start = None; } }