(
- item: Entity,
- dock_area: &WeakEntity,
- window: &mut Window,
- cx: &mut App,
- ) -> Self {
- Self::new_tabs(vec![Arc::new(item.clone())], None, dock_area, window, cx)
- }
-
- fn new_tabs(
- items: Vec>,
- active_ix: Option,
- dock_area: &WeakEntity,
- window: &mut Window,
- cx: &mut App,
- ) -> Self {
- let active_ix = active_ix.unwrap_or(0);
- let tab_panel = cx.new(|cx| {
- let mut tab_panel = TabPanel::new(None, dock_area.clone(), window, cx);
- for item in items.iter() {
- tab_panel.add_panel(item.clone(), window, cx)
- }
- tab_panel.active_ix = active_ix;
- tab_panel
- });
-
- Self::Tabs {
- items,
- active_ix,
- view: tab_panel,
- }
- }
-
- /// Returns all panel ids
- pub fn panel_ids(&self, cx: &App) -> Vec {
- match self {
- Self::Panel { .. } => vec![],
- Self::Tabs { view, .. } => view.read(cx).panel_ids(cx),
- Self::Split { items, .. } => items
- .iter()
- .filter_map(|item| match item {
- DockItem::Tabs { view, .. } => Some(view.read(cx).panel_ids(cx)),
- _ => None,
- })
- .flatten()
- .collect(),
- }
- }
-
- /// Returns the views of the dock item.
- pub 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(|item| *item == &panel).cloned(),
- Self::Panel { view } => Some(view.clone()),
- }
- }
-
- /// Add a panel to the dock item.
- pub fn add_panel(
- &mut self,
- panel: Arc,
- dock_area: &WeakEntity,
- window: &mut Window,
- cx: &mut App,
- ) {
- match self {
- Self::Tabs { view, items, .. } => {
- items.push(panel.clone());
- view.update(cx, |tab_panel, cx| {
- tab_panel.add_panel(panel, window, cx);
- });
- }
- Self::Split { view, items, .. } => {
- // Iter items to add panel to the first tabs
- for item in items.iter_mut() {
- if let DockItem::Tabs { view, .. } = item {
- view.update(cx, |tab_panel, cx| {
- tab_panel.add_panel(panel.clone(), window, cx);
- });
- return;
- }
- }
-
- // Unable to find tabs, create new tabs
- let new_item = Self::tabs(vec![panel.clone()], None, dock_area, window, cx);
- items.push(new_item.clone());
- view.update(cx, |stack_panel, cx| {
- stack_panel.add_panel(new_item.view(), None, dock_area.clone(), window, cx);
- });
- }
- Self::Panel { .. } => {}
- }
- }
-
- /// Set the collapsed state of the dock area
- pub fn set_collapsed(&self, collapsed: bool, window: &mut Window, cx: &mut App) {
- match self {
- DockItem::Tabs { view, .. } => {
- view.update(cx, |tab_panel, cx| {
- tab_panel.set_collapsed(collapsed, window, cx);
- });
- }
- DockItem::Split { items, .. } => {
- // For each child item, set collapsed state
- for item in items {
- item.set_collapsed(collapsed, window, cx);
- }
- }
- DockItem::Panel { .. } => {}
- }
- }
-
- /// Recursively traverses to find the left-most and top-most TabPanel.
- pub(crate) fn left_top_tab_panel(&self, cx: &App) -> Option> {
- match self {
- DockItem::Tabs { view, .. } => Some(view.clone()),
- DockItem::Split { view, .. } => view.read(cx).left_top_tab_panel(true, cx),
- DockItem::Panel { .. } => None,
- }
- }
-
- /// Recursively traverses to find the right-most and top-most TabPanel.
- pub(crate) fn right_top_tab_panel(&self, cx: &App) -> Option> {
- match self {
- DockItem::Tabs { view, .. } => Some(view.clone()),
- DockItem::Split { view, .. } => view.read(cx).right_top_tab_panel(true, cx),
- DockItem::Panel { .. } => None,
- }
- }
-
- pub(crate) fn focus_tab_panel(&self, window: &mut Window, cx: &mut App) {
- if let DockItem::Tabs { view, .. } = self {
- window.focus(&view.read(cx).focus_handle(cx), cx);
- }
- }
-}
-
-impl DockArea {
- pub fn new(window: &mut Window, cx: &mut Context) -> Self {
- let stack_panel = cx.new(|cx| StackPanel::new(Axis::Horizontal, window, cx));
-
- let dock_item = DockItem::Split {
- axis: Axis::Horizontal,
- items: vec![],
- sizes: vec![],
- view: stack_panel.clone(),
- };
-
- let mut this = Self {
- bounds: Bounds::default(),
- items: dock_item,
- zoom_view: None,
- toggle_button_panels: Edges::default(),
- toggle_button_visible: true,
- left_dock: None,
- right_dock: None,
- bottom_dock: None,
- is_locked: false,
- panel_style: PanelStyle::Default,
- subscriptions: vec![],
- };
-
- this.subscribe_panel(&stack_panel, window, cx);
-
- this
- }
-
- /// Set the panel style of the dock area.
- pub fn style(mut self, style: PanelStyle) -> Self {
- self.panel_style = style;
- self
- }
-
- /// The DockItem as the center of the dock area.
- ///
- /// This is used to render at the Center of the DockArea.
- pub fn set_center(&mut self, item: DockItem, window: &mut Window, cx: &mut Context) {
- self.subscribe_item(&item, window, cx);
- self.items = item;
- self.update_toggle_button_tab_panels(window, cx);
- cx.notify();
- }
-
- pub fn set_left_dock(
- &mut self,
- panel: DockItem,
- size: Option,
- open: bool,
- window: &mut Window,
- cx: &mut Context,
- ) {
- self.subscribe_item(&panel, window, cx);
- let weak_self = cx.entity().downgrade();
- self.left_dock = Some(cx.new(|cx| {
- let mut dock = Dock::left(weak_self.clone(), window, cx);
- if let Some(size) = size {
- dock.set_size(size, window, cx);
- }
- dock.set_panel(panel, window, cx);
- dock.set_open(open, window, cx);
- dock
- }));
- self.update_toggle_button_tab_panels(window, cx);
- }
-
- pub fn set_bottom_dock(
- &mut self,
- panel: DockItem,
- size: Option,
- open: bool,
- window: &mut Window,
- cx: &mut Context,
- ) {
- self.subscribe_item(&panel, window, cx);
- let weak_self = cx.entity().downgrade();
- self.bottom_dock = Some(cx.new(|cx| {
- let mut dock = Dock::bottom(weak_self.clone(), window, cx);
- if let Some(size) = size {
- dock.set_size(size, window, cx);
- }
- dock.set_panel(panel, window, cx);
- dock.set_open(open, window, cx);
- dock
- }));
- self.update_toggle_button_tab_panels(window, cx);
- }
-
- pub fn set_right_dock(
- &mut self,
- panel: DockItem,
- size: Option,
- open: bool,
- window: &mut Window,
- cx: &mut Context,
- ) {
- self.subscribe_item(&panel, window, cx);
- let weak_self = cx.entity().downgrade();
- self.right_dock = Some(cx.new(|cx| {
- let mut dock = Dock::right(weak_self.clone(), window, cx);
- if let Some(size) = size {
- dock.set_size(size, window, cx);
- }
- dock.set_panel(panel, window, cx);
- dock.set_open(open, window, cx);
- dock
- }));
- self.update_toggle_button_tab_panels(window, cx);
- }
-
- /// Reset all docks
- pub fn reset(&mut self, _window: &mut Window, cx: &mut Context) {
- self.left_dock = None;
- self.right_dock = None;
- self.bottom_dock = None;
- cx.notify();
- }
-
- /// Set locked state of the dock area, if locked, the dock area cannot be split or move, but allows to resize panels.
- pub fn set_locked(&mut self, locked: bool, _window: &mut Window, _cx: &mut App) {
- self.is_locked = locked;
- }
-
- /// Determine if the dock area is locked.
- pub fn is_locked(&self) -> bool {
- self.is_locked
- }
-
- /// Determine if the dock area has a dock at the given placement.
- pub fn has_dock(&self, placement: DockPlacement) -> bool {
- match placement {
- DockPlacement::Left => self.left_dock.is_some(),
- DockPlacement::Bottom => self.bottom_dock.is_some(),
- DockPlacement::Right => self.right_dock.is_some(),
- DockPlacement::Center => false,
- }
- }
-
- /// Determine if the dock at the given placement is open.
- pub fn is_dock_open(&self, placement: DockPlacement, cx: &App) -> bool {
- match placement {
- DockPlacement::Left => self
- .left_dock
- .as_ref()
- .map(|dock| dock.read(cx).is_open())
- .unwrap_or(false),
- DockPlacement::Bottom => self
- .bottom_dock
- .as_ref()
- .map(|dock| dock.read(cx).is_open())
- .unwrap_or(false),
- DockPlacement::Right => self
- .right_dock
- .as_ref()
- .map(|dock| dock.read(cx).is_open())
- .unwrap_or(false),
- DockPlacement::Center => false,
- }
- }
-
- /// Set the dock at the given placement to be open or closed.
- ///
- /// Only the left, bottom, right dock can be toggled.
- pub fn set_dock_collapsible(
- &mut self,
- collapsible_edges: Edges,
- window: &mut Window,
- cx: &mut Context,
- ) {
- if let Some(left_dock) = self.left_dock.as_ref() {
- left_dock.update(cx, |dock, cx| {
- dock.set_collapsible(collapsible_edges.left, window, cx);
- });
- }
-
- if let Some(bottom_dock) = self.bottom_dock.as_ref() {
- bottom_dock.update(cx, |dock, cx| {
- dock.set_collapsible(collapsible_edges.bottom, window, cx);
- });
- }
-
- if let Some(right_dock) = self.right_dock.as_ref() {
- right_dock.update(cx, |dock, cx| {
- dock.set_collapsible(collapsible_edges.right, window, cx);
- });
- }
- }
-
- /// Determine if the dock at the given placement is collapsible.
- pub fn is_dock_collapsible(&self, placement: DockPlacement, cx: &App) -> bool {
- match placement {
- DockPlacement::Left => self
- .left_dock
- .as_ref()
- .map(|dock| dock.read(cx).collapsible)
- .unwrap_or(false),
- DockPlacement::Bottom => self
- .bottom_dock
- .as_ref()
- .map(|dock| dock.read(cx).collapsible)
- .unwrap_or(false),
- DockPlacement::Right => self
- .right_dock
- .as_ref()
- .map(|dock| dock.read(cx).collapsible)
- .unwrap_or(false),
- DockPlacement::Center => false,
- }
- }
-
- pub fn toggle_dock(
- &self,
- placement: DockPlacement,
- window: &mut Window,
- cx: &mut Context,
- ) {
- let dock = match placement {
- DockPlacement::Left => &self.left_dock,
- DockPlacement::Bottom => &self.bottom_dock,
- DockPlacement::Right => &self.right_dock,
- DockPlacement::Center => return,
- };
-
- if let Some(dock) = dock {
- dock.update(cx, |view, cx| {
- view.toggle_open(window, cx);
- })
- }
- }
-
- /// 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,
- placement: DockPlacement,
- window: &mut Window,
- cx: &mut Context,
- ) {
- let weak_self = cx.entity().downgrade();
-
- match placement {
- DockPlacement::Left => {
- if let Some(dock) = self.left_dock.as_ref() {
- dock.update(cx, |dock, cx| dock.add_panel(panel, window, cx))
- } else {
- self.set_left_dock(
- DockItem::tabs(vec![panel], None, &weak_self, window, cx),
- Some(px(320.)),
- true,
- window,
- cx,
- );
- }
- }
- DockPlacement::Bottom => {
- if let Some(dock) = self.bottom_dock.as_ref() {
- dock.update(cx, |dock, cx| dock.add_panel(panel, window, cx))
- } else {
- self.set_bottom_dock(
- DockItem::tabs(vec![panel], None, &weak_self, window, cx),
- None,
- true,
- window,
- cx,
- );
- }
- }
- DockPlacement::Right => {
- self.set_right_dock(
- DockItem::tabs(vec![panel], None, &weak_self, window, cx),
- Some(px(320.)),
- true,
- window,
- cx,
- );
- }
- DockPlacement::Center => {
- self.items
- .add_panel(panel, &cx.entity().downgrade(), window, cx);
- }
- }
- }
-
- /// Subscribe event on the panels
- fn subscribe_item(&mut self, item: &DockItem, window: &mut Window, cx: &mut Context) {
- match item {
- DockItem::Split { items, view, .. } => {
- for item in items {
- self.subscribe_item(item, window, cx);
- }
-
- self.subscriptions.push(cx.subscribe_in(
- view,
- window,
- move |_, _, event, window, cx| {
- if let PanelEvent::LayoutChanged = event {
- cx.spawn_in(window, async move |view, window| {
- _ = view.update_in(window, |view, window, cx| {
- view.update_toggle_button_tab_panels(window, cx)
- });
- })
- .detach();
-
- cx.emit(DockEvent::LayoutChanged);
- }
- },
- ));
- }
- DockItem::Tabs { .. } => {
- // We subscribe to the tab panel event in StackPanel's insert_panel
- }
- DockItem::Panel { .. } => {
- // Not supported
- }
- }
- }
-
- /// Subscribe zoom event on the panel
- pub(crate) fn subscribe_panel(
- &mut self,
- view: &Entity,
- window: &mut Window,
- cx: &mut Context,
- ) {
- let subscription =
- cx.subscribe_in(
- view,
- window,
- move |_this, panel, event, window, cx| match event {
- PanelEvent::ZoomIn => {
- let panel = panel.clone();
- cx.spawn_in(window, async move |view, window| {
- view.update_in(window, |view, window, cx| {
- view.set_zoomed_in(panel, window, cx);
- cx.notify();
- })
- .ok();
- })
- .detach();
- }
- PanelEvent::ZoomOut => {
- cx.spawn_in(window, async move |view, window| {
- _ = view.update_in(window, |view, window, cx| {
- view.set_zoomed_out(window, cx);
- });
- })
- .detach();
- }
- PanelEvent::LayoutChanged => {
- cx.spawn_in(window, async move |view, window| {
- view.update_in(window, |view, window, cx| {
- view.update_toggle_button_tab_panels(window, cx)
- })
- .ok();
- })
- .detach();
- // Emit layout changed event for dock
- cx.emit(DockEvent::LayoutChanged);
- }
- },
- );
-
- self.subscriptions.push(subscription);
- }
-
- pub fn set_zoomed_in(
- &mut self,
- panel: Entity,
- _: &mut Window,
- cx: &mut Context,
- ) {
- self.zoom_view = Some(panel.into());
- cx.notify();
- }
-
- pub fn set_zoomed_out(&mut self, _window: &mut Window, cx: &mut Context) {
- self.zoom_view = None;
- cx.notify();
- }
-
- fn render_items(&self, _window: &mut Window, _cx: &mut Context) -> AnyElement {
- match &self.items {
- DockItem::Split { view, .. } => view.clone().into_any_element(),
- DockItem::Tabs { view, .. } => view.clone().into_any_element(),
- DockItem::Panel { view, .. } => view.clone().view().into_any_element(),
- }
- }
-
- pub fn update_toggle_button_tab_panels(
- &mut self,
- _window: &mut Window,
- cx: &mut Context,
- ) {
- // Left toggle button
- self.toggle_button_panels.left = self
- .items
- .left_top_tab_panel(cx)
- .map(|view| view.entity_id());
-
- // Right toggle button
- self.toggle_button_panels.right = self
- .items
- .right_top_tab_panel(cx)
- .map(|view| view.entity_id());
-
- // Bottom toggle button
- self.toggle_button_panels.bottom = self
- .bottom_dock
- .as_ref()
- .and_then(|dock| dock.read(cx).panel.left_top_tab_panel(cx))
- .map(|view| view.entity_id());
- }
-
- pub fn focus_tab_panel(&mut self, window: &mut Window, cx: &mut App) {
- self.items.focus_tab_panel(window, cx);
- }
-}
-
-impl EventEmitter for DockArea {}
-
-impl Render for DockArea {
- fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement {
- let view = cx.entity().clone();
- let decorations = window.window_decorations();
-
- div()
- .id("dock-area")
- .relative()
- .size_full()
- .overflow_hidden()
- .on_prepaint(move |bounds, _, cx| view.update(cx, |r, _| r.bounds = bounds))
- .map(|this| {
- if let Some(zoom_view) = self.zoom_view.clone() {
- this.map(|this| match decorations {
- Decorations::Server => this,
- Decorations::Client { tiling } => this
- .when(!(tiling.top || tiling.right), |div| {
- div.rounded_br(CLIENT_SIDE_DECORATION_ROUNDING)
- })
- .when(!(tiling.top || tiling.left), |div| {
- div.rounded_bl(CLIENT_SIDE_DECORATION_ROUNDING)
- }),
- })
- .child(zoom_view)
- } else {
- // render dock
- this.child(
- div()
- .flex()
- .flex_row()
- .h_full()
- // Left dock
- .when_some(self.left_dock.clone(), |this, dock| {
- this.child(div().flex().flex_none().child(dock))
- })
- // Center
- .child(
- div()
- .flex()
- .flex_1()
- .flex_col()
- .overflow_hidden()
- // Top center
- .child(
- div()
- .flex_1()
- .overflow_hidden()
- .child(self.render_items(window, cx)),
- )
- // Bottom Dock
- .when_some(self.bottom_dock.clone(), |this, dock| {
- this.child(dock)
- }),
- )
- // Right Dock
- .when_some(self.right_dock.clone(), |this, dock| {
- this.child(div().flex().flex_none().child(dock))
- }),
- )
- }
- })
- }
-}
diff --git a/crates/ui/src/dock/panel.rs b/crates/ui/src/dock/panel.rs
deleted file mode 100644
index 0f7e101..0000000
--- a/crates/ui/src/dock/panel.rs
+++ /dev/null
@@ -1,158 +0,0 @@
-use gpui::{
- AnyElement, AnyView, App, Element, Entity, EventEmitter, FocusHandle, Focusable, Render,
- SharedString, Window,
-};
-
-use crate::button::Button;
-use crate::menu::PopupMenu;
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum PanelEvent {
- ZoomIn,
- ZoomOut,
- LayoutChanged,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-pub enum PanelStyle {
- /// Display the TabBar when there are multiple tabs, otherwise display the simple title.
- Default,
- /// Always display the tab bar.
- TabBar,
-}
-
-pub trait Panel: EventEmitter + Render + Focusable {
- /// The name of the panel used to serialize, deserialize and identify the panel.
- ///
- /// This is used to identify the panel when deserializing the panel.
- /// Once you have defined a panel id, this must not be changed.
- fn panel_id(&self) -> SharedString;
-
- /// The title of the panel
- fn title(&self, _cx: &App) -> AnyElement {
- SharedString::from("Unnamed").into_any()
- }
-
- /// Whether the panel can be closed, default is `true`.
- fn closable(&self, _cx: &App) -> bool {
- true
- }
-
- /// Return true if the panel is zoomable, default is `false`.
- fn zoomable(&self, _cx: &App) -> bool {
- true
- }
-
- /// Return false to hide panel, true to show panel, default is `true`.
- ///
- /// This method called in Panel render, we should make sure it is fast.
- fn visible(&self, _cx: &App) -> bool {
- true
- }
-
- /// Set active state of the panel.
- ///
- /// This method will be called when the panel is active or inactive.
- ///
- /// The last_active_panel and current_active_panel will be touched when the panel is active.
- fn set_active(&self, _active: bool, _cx: &mut App) {}
-
- /// Set zoomed state of the panel.
- ///
- /// This method will be called when the panel is zoomed or unzoomed.
- ///
- /// Only current Panel will touch this method.
- fn set_zoomed(&self, _zoomed: bool, _cx: &mut App) {}
-
- /// The addition popup menu of the panel, default is `None`.
- fn popup_menu(&self, this: PopupMenu, _cx: &App) -> PopupMenu {
- this
- }
-
- /// The addition toolbar buttons of the panel used to show in the right of the title bar, default is `None`.
- fn toolbar_buttons(&self, _window: &Window, _cx: &App) -> Vec