diff --git a/examples/advanced.rs b/examples/advanced.rs index f972f11..642d392 100644 --- a/examples/advanced.rs +++ b/examples/advanced.rs @@ -203,7 +203,7 @@ impl Default for MyApp { let root = tiles.insert_tab_tile(tabs); - let tree = egui_tiles::Tree::new(root, tiles); + let tree = egui_tiles::Tree::new("my_tree", root, tiles); Self { tree, diff --git a/examples/simple.rs b/examples/simple.rs index 71e7ece..cc1390b 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -76,5 +76,5 @@ fn create_tree() -> egui_tiles::Tree { let root = tiles.insert_tab_tile(tabs); - egui_tiles::Tree::new(root, tiles) + egui_tiles::Tree::new("my_tree", root, tiles) } diff --git a/src/container/grid.rs b/src/container/grid.rs index 4146be7..6d79570 100644 --- a/src/container/grid.rs +++ b/src/container/grid.rs @@ -515,7 +515,7 @@ mod tests { let mut tiles = Tiles::default(); let panes: Vec = vec![tiles.insert_pane(Pane {}), tiles.insert_pane(Pane {})]; let root: TileId = tiles.insert_grid_tile(panes); - Tree::new(root, tiles) + Tree::new("test_tree", root, tiles) }; let style = egui::Style::default(); diff --git a/src/container/linear.rs b/src/container/linear.rs index b89aa68..16e0afc 100644 --- a/src/container/linear.rs +++ b/src/container/linear.rs @@ -461,7 +461,7 @@ fn linear_drop_zones( let preview_thickness = 12.0; let dragged_index = children .iter() - .position(|&child| is_being_dragged(egui_ctx, child)); + .position(|&child| is_being_dragged(egui_ctx, tree.id, child)); let after_rect = |rect: Rect| match dir { LinearDir::Horizontal => Rect::from_min_max( diff --git a/src/container/tabs.rs b/src/container/tabs.rs index 8594e1f..b932f7d 100644 --- a/src/container/tabs.rs +++ b/src/container/tabs.rs @@ -270,7 +270,7 @@ impl Tabs { .drag_started() { behavior.on_edit(); - ui.memory_mut(|mem| mem.set_dragged_id(tile_id.egui_id())); + ui.memory_mut(|mem| mem.set_dragged_id(tile_id.egui_id(tree.id))); } } @@ -281,10 +281,10 @@ impl Tabs { continue; } - let is_being_dragged = is_being_dragged(ui.ctx(), child_id); + let is_being_dragged = is_being_dragged(ui.ctx(), tree.id, child_id); let selected = self.is_active(child_id); - let id = child_id.egui_id(); + let id = child_id.egui_id(tree.id); let response = behavior.tab_ui( &tree.tiles, diff --git a/src/lib.rs b/src/lib.rs index 91b661d..4569fcb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -267,8 +267,8 @@ fn is_possible_drag(ctx: &egui::Context) -> bool { ctx.input(|input| input.pointer.is_decidedly_dragging()) } -fn is_being_dragged(ctx: &egui::Context, tile_id: TileId) -> bool { - ctx.memory(|mem| mem.is_being_dragged(tile_id.egui_id())) && is_possible_drag(ctx) +fn is_being_dragged(ctx: &egui::Context, tree_id: egui::Id, tile_id: TileId) -> bool { + ctx.memory(|mem| mem.is_being_dragged(tile_id.egui_id(tree_id))) && is_possible_drag(ctx) } /// If this tile is currently being dragged, cover it with a semi-transparent overlay ([`Behavior::dragged_overlay_color`]). @@ -278,7 +278,7 @@ fn cover_tile_if_dragged( ui: &mut egui::Ui, tile_id: TileId, ) { - if is_being_dragged(ui.ctx(), tile_id) { + if is_being_dragged(ui.ctx(), tree.id, tile_id) { if let Some(child_rect) = tree.tiles.try_rect(tile_id) { let overlay_color = behavior.dragged_overlay_color(ui.visuals()); ui.painter().rect_filled(child_rect, 0.0, overlay_color); diff --git a/src/tile.rs b/src/tile.rs index c13590d..e3508a6 100644 --- a/src/tile.rs +++ b/src/tile.rs @@ -1,6 +1,8 @@ use crate::{Container, ContainerKind}; /// An identifier for a [`Tile`] in the tree, be it a [`Container`] or a pane. +/// +/// This id is unique within the tree, but not across trees. #[derive(Clone, Copy, Hash, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct TileId(u64); @@ -10,9 +12,9 @@ impl TileId { Self(n) } - /// Corresponding [`egui::Id`], used for dragging. - pub fn egui_id(&self) -> egui::Id { - egui::Id::new(("egui_tile", self)) + /// Corresponding [`egui::Id`], used for tracking dragging of tiles. + pub fn egui_id(&self, tree_id: egui::Id) -> egui::Id { + tree_id.with(("tile", self)) } } diff --git a/src/tiles.rs b/src/tiles.rs index aae3597..be7f8e7 100644 --- a/src/tiles.rs +++ b/src/tiles.rs @@ -16,7 +16,7 @@ use super::{ /// let tabs: Vec = vec![tiles.insert_pane(Pane { }), tiles.insert_pane(Pane { })]; /// let root: TileId = tiles.insert_tab_tile(tabs); /// -/// let tree = Tree::new(root, tiles); +/// let tree = Tree::new("my_tree", root, tiles); /// ``` #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] diff --git a/src/tree.rs b/src/tree.rs index 290efdb..2ba7a32 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -23,11 +23,14 @@ use super::{ /// let tabs: Vec = vec![tiles.insert_pane(Pane { }), tiles.insert_pane(Pane { })]; /// let root: TileId = tiles.insert_tab_tile(tabs); /// -/// let tree = Tree::new(root, tiles); +/// let tree = Tree::new("my_tree", root, tiles); /// ``` #[derive(Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub struct Tree { + /// The constant, globally unique id of this tree. + pub(crate) id: egui::Id, + /// None = empty tree pub root: Option, @@ -35,16 +38,6 @@ pub struct Tree { pub tiles: Tiles, } -impl Default for Tree { - // An empty tree - fn default() -> Self { - Self { - root: None, - tiles: Default::default(), - } - } -} - impl std::fmt::Debug for Tree { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { // Print a hierarchical view of the tree: @@ -92,48 +85,81 @@ impl std::fmt::Debug for Tree { // ---------------------------------------------------------------------------- impl Tree { - pub fn empty() -> Self { - Self::default() + /// Construct an empty tree. + /// + /// The `id` must be _globally_ unique (!). + /// This is so that the same tree can be added to different [`egui::Ui`]s (if you want). + pub fn empty(id: impl Into) -> Self { + Self { + id: id.into(), + root: None, + tiles: Default::default(), + } } /// The most flexible constructor, allowing you to set up the tiles /// however you want. - pub fn new(root: TileId, tiles: Tiles) -> Self { + /// + /// The `id` must be _globally_ unique (!). + /// This is so that the same tree can be added to different [`egui::Ui`]s (if you want). + pub fn new(id: impl Into, root: TileId, tiles: Tiles) -> Self { Self { + id: id.into(), root: Some(root), tiles, } } /// Create a top-level [`crate::Tabs`] container with the given panes. - pub fn new_tabs(panes: Vec) -> Self { - Self::new_container(ContainerKind::Tabs, panes) + /// + /// The `id` must be _globally_ unique (!). + /// This is so that the same tree can be added to different [`egui::Ui`]s (if you want). + pub fn new_tabs(id: impl Into, panes: Vec) -> Self { + Self::new_container(id, ContainerKind::Tabs, panes) } /// Create a top-level horizontal [`crate::Linear`] container with the given panes. - pub fn new_horizontal(panes: Vec) -> Self { - Self::new_container(ContainerKind::Horizontal, panes) + /// + /// The `id` must be _globally_ unique (!). + /// This is so that the same tree can be added to different [`egui::Ui`]s (if you want). + pub fn new_horizontal(id: impl Into, panes: Vec) -> Self { + Self::new_container(id, ContainerKind::Horizontal, panes) } /// Create a top-level vertical [`crate::Linear`] container with the given panes. - pub fn new_vertical(panes: Vec) -> Self { - Self::new_container(ContainerKind::Vertical, panes) + /// + /// The `id` must be _globally_ unique (!). + /// This is so that the same tree can be added to different [`egui::Ui`]s (if you want). + pub fn new_vertical(id: impl Into, panes: Vec) -> Self { + Self::new_container(id, ContainerKind::Vertical, panes) } /// Create a top-level [`crate::Grid`] container with the given panes. - pub fn new_grid(panes: Vec) -> Self { - Self::new_container(ContainerKind::Grid, panes) + /// + /// The `id` must be _globally_ unique (!). + /// This is so that the same tree can be added to different [`egui::Ui`]s (if you want). + pub fn new_grid(id: impl Into, panes: Vec) -> Self { + Self::new_container(id, ContainerKind::Grid, panes) } /// Create a top-level container with the given panes. - pub fn new_container(kind: ContainerKind, panes: Vec) -> Self { + /// + /// The `id` must be _globally_ unique (!). + /// This is so that the same tree can be added to different [`egui::Ui`]s (if you want). + pub fn new_container(id: impl Into, kind: ContainerKind, panes: Vec) -> Self { let mut tiles = Tiles::default(); let tile_ids = panes .into_iter() .map(|pane| tiles.insert_pane(pane)) .collect(); let root = tiles.insert_new(Tile::Container(Container::new(kind, tile_ids))); - Self::new(root, tiles) + Self::new(id, root, tiles) + } + + /// The globally unique id used by this `Tree`. + #[inline] + pub fn id(&self) -> egui::Id { + self.id } /// Check if [`Self::root`] is [`None`]. @@ -147,6 +173,7 @@ impl Tree { self.root } + #[inline] pub fn is_root(&self, tile: TileId) -> bool { self.root == Some(tile) } @@ -234,7 +261,7 @@ impl Tree { match &mut tile { Tile::Pane(pane) => { if behavior.pane_ui(&mut ui, tile_id, pane) == UiResponse::DragStarted { - ui.memory_mut(|mem| mem.set_dragged_id(tile_id.egui_id())); + ui.memory_mut(|mem| mem.set_dragged_id(tile_id.egui_id(self.id))); } } Tile::Container(container) => { @@ -411,7 +438,7 @@ impl Tree { continue; // not allowed to drag root } - let id = tile_id.egui_id(); + let id = tile_id.egui_id(self.id); let is_tile_being_dragged = ctx.memory(|mem| mem.is_being_dragged(id)); if is_tile_being_dragged { // Abort drags on escape: