From adf6f70185995fb41b70e95595bda33e08419dd8 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Thu, 5 Sep 2024 19:33:32 +0800 Subject: [PATCH] dock: Define a better way to init Dock layout. (#218) --- crates/app/src/story_workspace.rs | 390 ++++++++++++----------------- crates/story/src/dropdown_story.rs | 6 +- crates/story/src/lib.rs | 9 + crates/ui/src/dock/mod.rs | 252 +++++++++++++++++-- crates/ui/src/dock/stack_panel.rs | 112 +++------ crates/ui/src/dock/tab_panel.rs | 37 ++- 6 files changed, 476 insertions(+), 330 deletions(-) diff --git a/crates/app/src/story_workspace.rs b/crates/app/src/story_workspace.rs index 83ce0bdf..5865c93c 100644 --- a/crates/app/src/story_workspace.rs +++ b/crates/app/src/story_workspace.rs @@ -12,13 +12,13 @@ use std::sync::Arc; use ui::{ button::Button, color_picker::{ColorPicker, ColorPickerEvent}, - dock::{DockArea, StackPanel, TabPanel}, + dock::{DockArea, DockItem}, drawer::Drawer, h_flex, modal::Modal, popup_menu::PopupMenuExt, theme::{ActiveTheme, Colorize as _, Theme}, - ContextModal, IconName, Placement, Root, Sizable, + ContextModal, IconName, Root, Sizable, }; use crate::app_state::AppState; @@ -51,240 +51,168 @@ impl StoryWorkspace { }) .detach(); - let stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Horizontal, cx)); - let dock_area = cx.new_view(|cx| DockArea::new("main-dock", stack_panel.clone(), cx)); - let weak_dock_area = dock_area.downgrade(); + let dock_area = cx.new_view(|cx| DockArea::new("main-dock", cx)); - let center_tab_panel = cx.new_view(|cx| { - let stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Vertical, cx)); - TabPanel::new(Some(stack_panel), weak_dock_area.clone(), cx) - }); - let left_tab_panel = cx.new_view(|cx| { - let stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Vertical, cx)); - TabPanel::new(Some(stack_panel), weak_dock_area.clone(), cx) - }); + let dock_item = DockItem::split_with_sizes( + Axis::Horizontal, + vec![ + DockItem::split( + Axis::Vertical, + vec![ + DockItem::panel(StoryContainer::panel( + "Icon", + "Icon use examples", + IconStory::view(cx).into(), + cx, + )), + DockItem::panel(StoryContainer::panel( + "Calendar", + "A calendar component.", + CalendarStory::view(cx).into(), + cx, + )), + ], + &dock_area, + cx, + ), + DockItem::split_with_sizes( + Axis::Vertical, + vec![ + DockItem::tabs( + vec![ + DockItem::panel(StoryContainer::panel( + "Button", + "Displays a button or a component that looks like a button.", + ButtonStory::view(cx).into(), + cx, + )), + DockItem::panel(StoryContainer::panel( + "Input", + "A control that allows the user to input text.", + InputStory::view(cx).into(), + cx, + )), + DockItem::panel(StoryContainer::panel( + "Dropdown", + "Displays a list of options for the user to pick from—triggered by a button.", + DropdownStory::view(cx).into(), + cx, + )), + DockItem::panel(StoryContainer::panel( + "Modal", + "Modal & Drawer use examples", + ModalStory::view(cx).into(), + cx, + )), + DockItem::panel(StoryContainer::panel( + "Popup", + "A popup displays content on top of the main page.", + PopupStory::view(cx).into(), + cx, + )), + DockItem::tabs( + vec![DockItem::panel(StoryContainer::panel( + "List", + "A list displays a series of items.", + ListStory::view(cx).into(), + cx, + ))], + None, + &dock_area, + cx, + ), + DockItem::panel(StoryContainer::panel( + "Switch", + "A control that allows the user to toggle between two states.", + SwitchStory::view(cx).into(), + cx, + )), + DockItem::panel(StoryContainer::panel( + "Progress", + "Progress use examples", + ProgressStory::view(cx).into(), + cx, + )), + DockItem::panel(StoryContainer::panel( + "Table", + "Table use examples", + TableStory::view(cx).into(), + cx, + )), + DockItem::panel(StoryContainer::panel( + "Image", + "Image use examples", + ImageStory::view(cx).into(), + cx, + )), + DockItem::panel(StoryContainer::panel( + "Resizable", + "Resizable use examples", + ResizableStory::view(cx).into(), + cx, + )), + DockItem::panel(StoryContainer::panel( + "Scrollable", + "Scrollable use examples", + ScrollableStory::view(cx).into(), + cx, + )), + ], + None, + &dock_area, + cx, + ), + DockItem::tabs( + vec![ + DockItem::panel(StoryContainer::panel( + "Progress", + "Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.", + ProgressStory::view(cx).into(), + cx, + )), + DockItem::panel(StoryContainer::panel( + "Text", + "Links, paragraphs, checkboxes, and more.", + TextStory::view(cx).into(), + cx, + ))], + None, + &dock_area, + cx, + ), + ], + vec![None, Some(px(300.))], + &dock_area, + cx, + ), + DockItem::split_with_sizes(Axis::Vertical, vec![ + DockItem::panel(StoryContainer::panel( + "Tooltip", + "Displays a short message when users hover over an element.", + TooltipStory::view(cx).into(), + cx, + )), + DockItem::panel(StoryContainer::panel( + "Calendar", + "A calendar component.", + CalendarStory::view(cx).into(), + cx, + )), + DockItem::panel(StoryContainer::panel( + "Image", + "Render SVG image and Chart", + ImageStory::view(cx).into(), + cx, + )), + ], vec![None, None, Some(px(300.))], &dock_area, cx), - let right_tab_panel = cx.new_view(|cx| { - let stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Vertical, cx)); - TabPanel::new(Some(stack_panel), weak_dock_area.clone(), cx) - }); - - stack_panel.update(cx, |view, cx| { - view.add_panel( - left_tab_panel.clone(), - Some(px(300.)), - weak_dock_area.clone(), - cx, - ); - - view.add_panel(center_tab_panel.clone(), None, weak_dock_area.clone(), cx); - view.add_panel( - right_tab_panel.clone(), - Some(px(350.)), - weak_dock_area.clone(), - cx, - ); - }); - - StoryContainer::add_panel( - "Buttons", - "Displays a button or a component that looks like a button.", - ButtonStory::view(cx).into(), - center_tab_panel.clone(), - None, - None, - false, - None, + ], + vec![Some(px(300.)), None, Some(px(350.))], + &dock_area, cx, ); - StoryContainer::add_panel( - "Input", - "A control that allows the user to input text.", - InputStory::view(cx).into(), - center_tab_panel.clone(), - None, - None, - false, - None, - cx, - ); - - StoryContainer::add_panel( - "Text", - "Links, paragraphs, checkboxes, and more.", - TextStory::view(cx).into(), - center_tab_panel.clone(), - Some(Placement::Bottom), - Some(px(200.)), - true, - None, - cx, - ); - - StoryContainer::add_panel( - "Switch", - "A control that allows the user to toggle between two states.", - SwitchStory::view(cx).into(), - center_tab_panel.clone(), - None, - None, - true, - None, - cx, - ); - - StoryContainer::add_panel( - "Dropdowns", - "Displays a list of options for the user to pick from—triggered by a button.", - DropdownStory::new(cx).into(), - center_tab_panel.clone(), - None, - None, - true, - None, - cx, - ); - - StoryContainer::add_panel( - "Modal", - "Modal & Drawer use examples", - ModalStory::view(cx).into(), - center_tab_panel.clone(), - None, - None, - true, - None, - cx, - ); - - StoryContainer::add_panel( - "Popup", - "A popup displays content on top of the main page.", - PopupStory::view(cx).into(), - center_tab_panel.clone(), - None, - None, - true, - None, - cx, - ); - - StoryContainer::add_panel( - "Tooltip", - "Displays a short message when users hover over an element.", - TooltipStory::view(cx).into(), - right_tab_panel.clone(), - None, - None, - true, - None, - cx, - ); - - StoryContainer::add_panel( - "List", - "A list displays a series of items.", - ListStory::view(cx).into(), - left_tab_panel.clone(), - None, - None, - true, - None, - cx, - ); - - StoryContainer::add_panel( - "Icon", - "Icon use examples", - IconStory::view(cx).into(), - left_tab_panel.clone(), - Some(Placement::Bottom), - Some(px(200.)), - true, - None, - cx, - ); - - StoryContainer::add_panel( - "Image", - "Render SVG image and Chart", - ImageStory::view(cx).into(), - right_tab_panel.clone(), - Some(Placement::Bottom), - None, - true, - Some(cx.theme().muted), - cx, - ); - - // StoryContainer::add_panel( - // WebViewStory::view(cx).into(), - // stack_panel.clone(), - // DockPosition::Right, - // px(450.), - // cx, - // ); - - StoryContainer::add_panel( - "Table", - "Powerful table and datagrids built.", - TableStory::view(cx).into(), - center_tab_panel.clone(), - None, - None, - true, - None, - cx, - ); - - StoryContainer::add_panel( - "Progress", - "Displays an indicator showing the completion progress of a task, typically displayed as a progress bar.", - ProgressStory::view(cx).into(), - center_tab_panel.clone(), - Some(Placement::Bottom), - Some(px(200.)), - true, - None, - cx, - ); - - StoryContainer::add_panel( - "Resizable", - "Accessible resizable panel groups and layouts with keyboard support.", - ResizableStory::view(cx).into(), - center_tab_panel.clone(), - None, - None, - true, - None, - cx, - ); - - StoryContainer::add_panel( - "Scrollable", - "A scrollable area with scroll bar.", - ScrollableStory::view(cx).into(), - center_tab_panel.clone(), - None, - None, - true, - None, - cx, - ); - - StoryContainer::add_panel( - "Calendar", - "A calendar component.", - CalendarStory::view(cx).into(), - right_tab_panel.clone(), - Some(Placement::Bottom), - None, - true, - None, - cx, - ); + dock_area.update(cx, |view, cx| view.set_root(dock_item, cx)); let locale_selector = cx.new_view(LocaleSelector::new); diff --git a/crates/story/src/dropdown_story.rs b/crates/story/src/dropdown_story.rs index aa2b014c..b3799d9f 100644 --- a/crates/story/src/dropdown_story.rs +++ b/crates/story/src/dropdown_story.rs @@ -56,7 +56,7 @@ pub struct DropdownStory { } impl DropdownStory { - pub fn new(cx: &mut WindowContext) -> View { + fn new(cx: &mut WindowContext) -> View { let countries = vec![ Country::new("United States", "US"), Country::new("Canada", "CA"), @@ -144,6 +144,10 @@ impl DropdownStory { }) } + pub fn view(cx: &mut WindowContext) -> View { + Self::new(cx) + } + fn on_dropdown_event( &mut self, _: View>>, diff --git a/crates/story/src/lib.rs b/crates/story/src/lib.rs index 8435eeca..29f91ab0 100644 --- a/crates/story/src/lib.rs +++ b/crates/story/src/lib.rs @@ -118,6 +118,15 @@ impl StoryContainer { } } + pub fn panel( + name: impl Into, + description: impl Into, + story: AnyView, + cx: &mut WindowContext, + ) -> View { + cx.new_view(|cx| Self::new(name, description, true, cx).story(story)) + } + #[allow(clippy::too_many_arguments)] pub fn add_panel( name: impl Into, diff --git a/crates/ui/src/dock/mod.rs b/crates/ui/src/dock/mod.rs index e0630821..8f63050f 100644 --- a/crates/ui/src/dock/mod.rs +++ b/crates/ui/src/dock/mod.rs @@ -2,9 +2,12 @@ mod panel; mod stack_panel; mod tab_panel; +use std::sync::Arc; + use gpui::{ - actions, div, prelude::FluentBuilder, AnyView, InteractiveElement as _, IntoElement, - ParentElement as _, Render, SharedString, Styled, View, ViewContext, + actions, div, prelude::FluentBuilder, AnyElement, AnyView, Axis, InteractiveElement as _, + IntoElement, ParentElement as _, Pixels, Render, SharedString, Styled, View, ViewContext, + VisualContext, WindowContext, }; pub use panel::*; pub use stack_panel::*; @@ -15,23 +18,241 @@ actions!(dock, [ToggleZoom, ClosePanel]); /// The main area of the dock. pub struct DockArea { id: SharedString, - root: View, + items: DockItem, zoom_view: Option, } -impl DockArea { - pub fn new( - id: impl Into, - root: View, - _cx: &mut ViewContext, +/// DockItem is a tree structure that represents the layout of the dock. +#[derive(Clone)] +pub enum DockItem { + Split { + axis: gpui::Axis, + items: Vec, + sizes: Vec>, + view: View, + }, + Tabs { + items: Vec, + active_ix: usize, + view: View, + }, + Panel { + view: Arc, + }, +} + +impl DockItem { + /// Create DockItem with split layout, each item of panel have equal size. + pub fn split( + axis: Axis, + items: Vec, + dock_area: &View, + cx: &mut WindowContext, ) -> Self { + let sizes = vec![None; items.len()]; + Self::split_with_sizes(axis, items, sizes, dock_area, cx) + } + + /// Create DockItem with split layout, each item of panel have specified size. + /// + /// Please note that the `items` and `sizes` must have the same length. + /// Set `None` in `sizes` to make the index of panel have auto size. + pub fn split_with_sizes( + axis: Axis, + items: Vec, + sizes: Vec>, + dock_area: &View, + cx: &mut WindowContext, + ) -> Self { + let mut items = items; + let stack_panel = cx.new_view(|cx| { + let mut stack_panel = StackPanel::new(axis, cx); + for (i, item) in items.iter_mut().enumerate() { + // Always convert DockItem::Panel to DockItem::Tabs in split layout. + // This makes they can subscribe the Zoom event. + match item { + DockItem::Panel { .. } => { + *item = DockItem::tabs(vec![item.clone()], None, &dock_area, cx); + } + _ => {} + } + + let view = item.view(); + let size = sizes.get(i).copied().flatten(); + stack_panel.add_panel(view.clone(), size, dock_area.downgrade(), cx) + } + + for (i, item) in items.iter().enumerate() { + let item = match item { + DockItem::Panel { .. } => Self::tabs(vec![item.clone()], None, &dock_area, cx), + _ => item.clone(), + }; + + let view = item.view(); + let size = sizes.get(i).copied().flatten(); + stack_panel.add_panel(view.clone(), size, dock_area.downgrade(), cx) + } + + stack_panel + }); + Self::Split { + axis, + items, + sizes, + view: stack_panel, + } + } + + /// Create DockItem with tabs layout, items are displayed as tabs. + /// + /// The `active_ix` is the index of the active tab, if `None` the first tab is active. + pub fn tabs( + items: Vec, + active_ix: Option, + dock_area: &View, + cx: &mut WindowContext, + ) -> Self { + let active_ix = active_ix.unwrap_or(0); + let tab_panel = cx.new_view(|cx| { + let mut tab_panel = TabPanel::new(None, dock_area.downgrade(), cx); + + for item in items.iter() { + let view = item.view(); + tab_panel.add_panel(view, cx) + } + + tab_panel + }); + + Self::Tabs { + items, + active_ix, + view: tab_panel, + } + } + + /// Create DockItem with a single panel, the `view` must implement `Panel`. + pub fn panel

(view: View

) -> Self + where + P: Panel, + { + Self::Panel { + view: Arc::new(view), + } + } + + /// Returns the views of the dock item. + fn view(&self) -> Arc { + match self { + Self::Split { view, .. } => Arc::new(view.clone()), + Self::Tabs { view, .. } => Arc::new(view.clone()), + Self::Panel { view } => view.clone(), + } + } + + /// Find existing panel in the dock item. + pub fn find_panel(&self, panel: Arc) -> Option> { + match self { + Self::Split { items, .. } => { + items.iter().find_map(|item| item.find_panel(panel.clone())) + } + Self::Tabs { items, .. } => { + items.iter().find_map(|item| item.find_panel(panel.clone())) + } + Self::Panel { view } => { + if view == &panel { + Some(view.clone()) + } else { + None + } + } + } + } +} + +impl DockArea { + pub fn new(id: impl Into, cx: &mut WindowContext) -> Self { + let stack_panel = cx.new_view(|cx| StackPanel::new(Axis::Horizontal, cx)); + let dock_item = DockItem::Split { + axis: Axis::Horizontal, + items: vec![], + sizes: vec![], + view: stack_panel.clone(), + }; + Self { id: id.into(), - root, + items: dock_item, zoom_view: None, } } + /// The the DockItem as the root of the dock area. + #[must_use] + pub fn set_root(&mut self, item: DockItem, cx: &mut ViewContext) { + self.subscribe_item(&item, cx); + self.items = item; + + cx.notify(); + } + + /// Subscribe event on the panels + #[allow(clippy::only_used_in_recursion)] + fn subscribe_item(&self, item: &DockItem, cx: &mut ViewContext) { + let dock_area = cx.view(); + + /// Subscribe zoom event on the panel + fn subscribe_zoom( + view: &View

, + dock_area: View, + cx: &mut ViewContext, + ) { + cx.subscribe(view, move |_, panel, event, cx| match event { + PanelEvent::ZoomIn => { + let dock_area = dock_area.clone(); + let panel = panel.clone(); + cx.spawn(|_, mut cx| async move { + let _ = cx.update(|cx| { + let _ = dock_area.update(cx, |dock, cx| { + dock.set_zoomed_in(panel, cx); + cx.notify(); + }); + }); + }) + .detach(); + } + PanelEvent::ZoomOut => { + let dock_area = dock_area.clone(); + cx.spawn(|_, mut cx| async move { + let _ = cx.update(|cx| { + let _ = dock_area.update(cx, |view, cx| view.set_zoomed_out(cx)); + }); + }) + .detach() + } + }) + .detach(); + } + + match item { + DockItem::Split { items, .. } => { + for item in items { + self.subscribe_item(item, cx); + } + } + DockItem::Tabs { view, .. } => { + // We need, only subscribe to the zoom events on the TabPanel + // Because we always wrap the DockItem::Panel in a DockItem::Tabs + subscribe_zoom(view, dock_area.clone(), cx); + } + DockItem::Panel { .. } => { + // The DockItem::Panel is not need to handle the zoom events + // Because the DockItem::Panel is always wrapped in a DockItem::Tabs + // So we only need to subscribe the zoom events on the TabPanel + } + } + } + /// Returns the ID of the dock area. pub fn id(&self) -> SharedString { self.id.clone() @@ -47,14 +268,17 @@ impl DockArea { cx.notify(); } - /// Returns the root stack panel. - pub fn root(&self) -> View { - self.root.clone() + fn render_items(&self, _cx: &mut ViewContext) -> AnyElement { + match &self.items { + DockItem::Split { view, .. } => view.clone().into_any_element(), + DockItem::Tabs { view, .. } => view.clone().into_any_element(), + DockItem::Panel { view } => view.view().into_any_element(), + } } } impl Render for DockArea { - fn render(&mut self, _: &mut ViewContext) -> impl IntoElement { + fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { // println!("Rendering dock area"); div() .id("dock-area") @@ -64,7 +288,7 @@ impl Render for DockArea { if let Some(zoom_view) = self.zoom_view.clone() { this.child(zoom_view) } else { - this.child(self.root.clone()) + this.child(self.render_items(cx)) } }) } diff --git a/crates/ui/src/dock/stack_panel.rs b/crates/ui/src/dock/stack_panel.rs index 3595d52d..ba4ff04e 100644 --- a/crates/ui/src/dock/stack_panel.rs +++ b/crates/ui/src/dock/stack_panel.rs @@ -9,7 +9,7 @@ use crate::{ use super::{DockArea, Panel, PanelEvent, PanelView, TabPanel}; use gpui::{ - prelude::FluentBuilder as _, AppContext, Axis, DismissEvent, Entity, EventEmitter, FocusHandle, + prelude::FluentBuilder as _, AppContext, Axis, DismissEvent, EventEmitter, FocusHandle, FocusableView, IntoElement, ParentElement, Pixels, Render, Styled, View, ViewContext, VisualContext, WeakView, }; @@ -19,7 +19,7 @@ pub struct StackPanel { pub(super) parent: Option>, pub(super) axis: Axis, focus_handle: FocusHandle, - panels: SmallVec<[Arc; 2]>, + pub(crate) panels: SmallVec<[Arc; 2]>, panel_group: View, } @@ -56,53 +56,41 @@ impl StackPanel { } /// Return the index of the panel. - pub(crate) fn index_of_panel

(&self, panel: &View

) -> Option - where - P: Panel, - { - let entity_id = panel.entity_id(); - self.panels - .iter() - .position(|p| p.view().entity_id() == entity_id) + pub(crate) fn index_of_panel(&self, panel: Arc) -> Option { + self.panels.iter().position(|p| p == &panel) } /// Add a panel at the end of the stack. - pub fn add_panel

( + pub fn add_panel( &mut self, - panel: View

, + panel: Arc, size: Option, dock_area: WeakView, cx: &mut ViewContext, - ) where - P: Panel, - { + ) { self.insert_panel(panel, self.panels.len(), size, dock_area, cx); } - pub fn add_panel_at

( + pub fn add_panel_at( &mut self, - panel: View

, + panel: Arc, placement: Placement, size: Option, dock_area: WeakView, cx: &mut ViewContext, - ) where - P: Panel, - { + ) { self.insert_panel_at(panel, self.panels_len(), placement, size, dock_area, cx); } - pub fn insert_panel_at

( + pub fn insert_panel_at( &mut self, - panel: View

, + panel: Arc, ix: usize, placement: Placement, size: Option, dock_area: WeakView, cx: &mut ViewContext, - ) where - P: Panel, - { + ) { match placement { Placement::Top | Placement::Left => { self.insert_panel_before(panel, ix, size, dock_area, cx) @@ -114,69 +102,48 @@ impl StackPanel { } /// Insert a panel at the index. - pub fn insert_panel_before

( + pub fn insert_panel_before( &mut self, - panel: View

, + panel: Arc, ix: usize, size: Option, dock_area: WeakView, cx: &mut ViewContext, - ) where - P: Panel, - { + ) { self.insert_panel(panel, ix, size, dock_area, cx); } /// Insert a panel after the index. - pub fn insert_panel_after

( + pub fn insert_panel_after( &mut self, - panel: View

, + panel: Arc, ix: usize, size: Option, dock_area: WeakView, cx: &mut ViewContext, - ) where - P: Panel, - { + ) { self.insert_panel(panel, ix + 1, size, dock_area, cx); } - fn new_resizable_panel

(panel: View

, size: Option) -> ResizablePanel - where - P: Panel, - { + fn new_resizable_panel(panel: Arc, size: Option) -> ResizablePanel { resizable_panel() - .content_view(panel.into()) + .content_view(panel.view()) .when_some(size, |this, size| this.size(size)) } - fn insert_panel

( + fn insert_panel( &mut self, - panel: View

, + panel: Arc, ix: usize, size: Option, - dock_area: WeakView, + _dock_area: WeakView, cx: &mut ViewContext, - ) where - P: Panel, - { + ) { // If the panel is already in the stack, return. - if let Some(_) = self.index_of_panel(&panel) { + if let Some(_) = self.index_of_panel(panel.clone()) { return; } - cx.subscribe(&panel, move |_, panel, event, cx| match event { - PanelEvent::ZoomIn => { - let _ = dock_area.update(cx, |dock, cx| { - dock.set_zoomed_in(panel.clone(), cx); - }); - } - PanelEvent::ZoomOut => { - let _ = dock_area.update(cx, |dock, cx| dock.set_zoomed_out(cx)); - } - }) - .detach(); - let view = cx.view().clone(); cx.window_context().defer({ let panel = panel.clone(); @@ -197,7 +164,7 @@ impl StackPanel { ix }; - self.panels.insert(ix, Arc::new(panel.clone())); + self.panels.insert(ix, panel.clone()); self.panel_group.update(cx, |view, cx| { view.insert_child(Self::new_resizable_panel(panel, size), ix, cx) }); @@ -206,11 +173,8 @@ impl StackPanel { } /// Remove panel from the stack. - pub fn remove_panel

(&mut self, panel: View

, cx: &mut ViewContext) - where - P: Panel, - { - if let Some(ix) = self.index_of_panel(&panel) { + pub fn remove_panel(&mut self, panel: Arc, cx: &mut ViewContext) { + if let Some(ix) = self.index_of_panel(panel.clone()) { self.panels.remove(ix); self.panel_group.update(cx, |view, cx| { view.remove_child(ix, cx); @@ -223,18 +187,20 @@ impl StackPanel { } /// Replace the old panel with the new panel at same index. - pub(super) fn replace_panel

( + pub(super) fn replace_panel( &mut self, - old_panel: View

, + old_panel: Arc, new_panel: View, cx: &mut ViewContext, - ) where - P: Panel, - { - if let Some(ix) = self.index_of_panel(&old_panel) { + ) { + if let Some(ix) = self.index_of_panel(old_panel.clone()) { self.panels[ix] = Arc::new(new_panel.clone()); self.panel_group.update(cx, |view, cx| { - view.replace_child(Self::new_resizable_panel(new_panel.clone(), None), ix, cx); + view.replace_child( + Self::new_resizable_panel(Arc::new(new_panel.clone()), None), + ix, + cx, + ); }); } } @@ -252,7 +218,7 @@ impl StackPanel { let view = cx.view().clone(); if let Some(parent) = self.parent.as_ref() { parent.update(cx, |parent, cx| { - parent.remove_panel(view, cx); + parent.remove_panel(Arc::new(view), cx); }); } diff --git a/crates/ui/src/dock/tab_panel.rs b/crates/ui/src/dock/tab_panel.rs index d87c24e8..2d5aba1d 100644 --- a/crates/ui/src/dock/tab_panel.rs +++ b/crates/ui/src/dock/tab_panel.rs @@ -62,8 +62,8 @@ pub struct TabPanel { focus_handle: FocusHandle, dock_area: WeakView, stack_panel: Option>, - panels: Vec>, - active_ix: usize, + pub(crate) panels: Vec>, + pub(crate) active_ix: usize, tab_bar_scroll_handle: ScrollHandle, is_zoomed: bool, @@ -185,7 +185,7 @@ impl TabPanel { let tab_view = cx.view().clone(); if let Some(stack_panel) = self.stack_panel.as_ref() { stack_panel.update(cx, |view, cx| { - view.remove_panel(tab_view, cx); + view.remove_panel(Arc::new(tab_view), cx); }) } } @@ -462,18 +462,33 @@ impl TabPanel { let stack_panel = self.stack_panel.as_ref().unwrap(); let parent_axis = stack_panel.read(cx).axis; + let ix = stack_panel .read(cx) - .index_of_panel(&cx.view()) + .index_of_panel(Arc::new(cx.view().clone())) .unwrap_or_default(); if parent_axis.is_vertical() && placement.is_vertical() { stack_panel.update(cx, |view, cx| { - view.insert_panel_at(new_tab_panel, ix, placement, size, dock_area.clone(), cx); + view.insert_panel_at( + Arc::new(new_tab_panel), + ix, + placement, + size, + dock_area.clone(), + cx, + ); }); } else if parent_axis.is_horizontal() && placement.is_horizontal() { stack_panel.update(cx, |view, cx| { - view.insert_panel_at(new_tab_panel, ix, placement, size, dock_area.clone(), cx); + view.insert_panel_at( + Arc::new(new_tab_panel), + ix, + placement, + size, + dock_area.clone(), + cx, + ); }); } else { // 1. Create new StackPanel with new axis @@ -499,18 +514,18 @@ impl TabPanel { new_stack_panel.update(cx, |view, cx| match placement { Placement::Left | Placement::Top => { - view.add_panel(new_tab_panel, size, dock_area.clone(), cx); - view.add_panel(tab_panel.clone(), None, dock_area.clone(), cx); + view.add_panel(Arc::new(new_tab_panel), size, dock_area.clone(), cx); + view.add_panel(Arc::new(tab_panel.clone()), None, dock_area.clone(), cx); } Placement::Right | Placement::Bottom => { - view.add_panel(tab_panel.clone(), None, dock_area.clone(), cx); - view.add_panel(new_tab_panel, size, dock_area.clone(), cx); + view.add_panel(Arc::new(tab_panel.clone()), None, dock_area.clone(), cx); + view.add_panel(Arc::new(new_tab_panel), size, dock_area.clone(), cx); } }); if *stack_panel != new_stack_panel { stack_panel.update(cx, |view, cx| { - view.replace_panel(tab_panel.clone(), new_stack_panel.clone(), cx); + view.replace_panel(Arc::new(tab_panel.clone()), new_stack_panel.clone(), cx); }); }