From 9732c3b7e8663b5caeab7c79cb2e927e2e5da66d Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Fri, 28 Feb 2025 18:36:58 +0800 Subject: [PATCH] tiles: To support close panel. (#674) - Add `remove_panel` method to DockArea. --- crates/ui/src/dock/dock.rs | 11 +++++ crates/ui/src/dock/mod.rs | 79 ++++++++++++++++++++++++++++++++- crates/ui/src/dock/tab_panel.rs | 27 ++++++++++- crates/ui/src/dock/tiles.rs | 11 +++-- 4 files changed, 121 insertions(+), 7 deletions(-) diff --git a/crates/ui/src/dock/dock.rs b/crates/ui/src/dock/dock.rs index 893aef32..b57b719b 100644 --- a/crates/ui/src/dock/dock.rs +++ b/crates/ui/src/dock/dock.rs @@ -276,6 +276,17 @@ impl Dock { cx.notify(); } + /// Remove item from the Dock. + pub fn remove_panel( + &mut self, + panel: Arc, + window: &mut Window, + cx: &mut Context, + ) { + self.panel.remove_panel(panel, window, cx); + cx.notify(); + } + fn render_resize_handle(&mut self, _: &mut Window, cx: &mut Context) -> impl IntoElement { let axis = self.placement.axis(); let view = cx.entity().clone(); diff --git a/crates/ui/src/dock/mod.rs b/crates/ui/src/dock/mod.rs index f7bb5e32..ec6f259e 100644 --- a/crates/ui/src/dock/mod.rs +++ b/crates/ui/src/dock/mod.rs @@ -368,6 +368,32 @@ impl DockItem { } } + /// Remove a panel from the dock item. + pub fn remove_panel(&self, panel: Arc, window: &mut Window, cx: &mut App) { + match self { + DockItem::Tabs { view, .. } => { + view.update(cx, |tab_panel, cx| { + tab_panel.remove_panel(panel, window, cx); + }); + } + DockItem::Split { items, view, .. } => { + // For each child item, set collapsed state + for item in items { + item.remove_panel(panel.clone(), window, cx); + } + view.update(cx, |split, cx| { + split.remove_panel(panel, window, cx); + }); + } + DockItem::Tiles { view, .. } => { + view.update(cx, |tiles, cx| { + tiles.remove(panel, window, cx); + }); + } + DockItem::Panel { .. } => {} + } + } + pub fn set_collapsed(&self, collapsed: bool, window: &mut Window, cx: &mut App) { match self { DockItem::Tabs { view, .. } => { @@ -567,6 +593,7 @@ impl DockArea { } /// Determine if the dock area is locked. + #[inline] pub fn is_locked(&self) -> bool { self.locked } @@ -674,8 +701,6 @@ impl DockArea { } /// Add a panel item to the dock area at the given placement. - /// - /// If the left, bottom, right dock is not present, it will set the dock at the placement. pub fn add_panel( &mut self, panel: Arc, @@ -732,6 +757,56 @@ impl DockArea { } } + /// Remove panel from the DockArea at the given placement. + pub fn remove_panel( + &mut self, + panel: Arc, + placement: DockPlacement, + window: &mut Window, + cx: &mut Context, + ) { + match placement { + DockPlacement::Left => { + if let Some(dock) = self.left_dock.as_mut() { + dock.update(cx, |dock, cx| { + dock.remove_panel(panel, window, cx); + }); + } + } + DockPlacement::Right => { + if let Some(dock) = self.right_dock.as_mut() { + dock.update(cx, |dock, cx| { + dock.remove_panel(panel, window, cx); + }); + } + } + DockPlacement::Bottom => { + if let Some(dock) = self.bottom_dock.as_mut() { + dock.update(cx, |dock, cx| { + dock.remove_panel(panel, window, cx); + }); + } + } + DockPlacement::Center => { + self.items.remove_panel(panel, window, cx); + } + } + cx.notify(); + } + + /// Remove a panel from all docks. + pub fn remove_panel_from_all_docks( + &mut self, + panel: Arc, + window: &mut Window, + cx: &mut Context, + ) { + self.remove_panel(panel.clone(), DockPlacement::Center, window, cx); + self.remove_panel(panel.clone(), DockPlacement::Left, window, cx); + self.remove_panel(panel.clone(), DockPlacement::Right, window, cx); + self.remove_panel(panel.clone(), DockPlacement::Bottom, window, cx); + } + /// Load the state of the DockArea from the DockAreaState. /// /// See also [DockeArea::dump]. diff --git a/crates/ui/src/dock/tab_panel.rs b/crates/ui/src/dock/tab_panel.rs index 59c80a08..5a599949 100644 --- a/crates/ui/src/dock/tab_panel.rs +++ b/crates/ui/src/dock/tab_panel.rs @@ -81,6 +81,8 @@ pub struct TabPanel { collapsed: bool, /// When drag move, will get the placement of the panel to be split will_split_placement: Option, + /// Is TabPanel used in Tiles. + in_tiles: bool, } impl Panel for TabPanel { @@ -158,9 +160,15 @@ impl TabPanel { zoomed: false, collapsed: false, closable: true, + in_tiles: false, } } + /// Mark the TabPanel as being used in Tiles. + pub(super) fn set_in_tiles(&mut self, in_tiles: bool) { + self.in_tiles = in_tiles; + } + pub(super) fn set_parent(&mut self, view: WeakEntity) { self.stack_panel = Some(view); } @@ -1063,6 +1071,20 @@ impl TabPanel { if let Some(panel) = self.active_panel(cx) { self.remove_panel(panel, window, cx); } + + // Remove self from the parent DockArea. + // This is ensure to remove from Tiles + if self.panels.is_empty() && self.in_tiles { + let tab_panel = Arc::new(cx.entity()); + window.defer(cx, { + let dock_area = self.dock_area.clone(); + move |window, cx| { + _ = dock_area.update(cx, |this, cx| { + this.remove_panel_from_all_docks(tab_panel, window, cx); + }); + } + }); + } } } @@ -1088,7 +1110,10 @@ impl Render for TabPanel { zoomable: self.zoomable(cx), active_panel, }; - if !state.draggable { + + // 1. When is the final panel in the dock, it will not able to close. + // 2. When is in the Tiles, it will always able to close (by active panel state). + if !state.draggable && !self.in_tiles { state.closable = false; } diff --git a/crates/ui/src/dock/tiles.rs b/crates/ui/src/dock/tiles.rs index f20bbc67..686a4975 100644 --- a/crates/ui/src/dock/tiles.rs +++ b/crates/ui/src/dock/tiles.rs @@ -361,10 +361,13 @@ impl Tiles { window: &mut Window, cx: &mut Context, ) { - assert!( - item.panel.view().downcast::().is_ok(), - "only allows to add TabPanel type" - ); + let Ok(tab_panel) = item.panel.view().downcast::() else { + panic!("only allows to add TabPanel type") + }; + + tab_panel.update(cx, |tab_panel, _| { + tab_panel.set_in_tiles(true); + }); self.panels.push(item.clone()); window.defer(cx, {