diff --git a/crates/agent_ui/src/agent_panel.rs b/crates/agent_ui/src/agent_panel.rs index 630411c2400ee925f980b5d3a410cb3574e81cd6..da961ded454aa4433366e6ce742694f7bad29a89 100644 --- a/crates/agent_ui/src/agent_panel.rs +++ b/crates/agent_ui/src/agent_panel.rs @@ -65,10 +65,9 @@ use extension_host::ExtensionStore; use fs::Fs; use git::repository::validate_worktree_directory; use gpui::{ - Action, Animation, AnimationExt, AnyElement, AnyView, App, AsyncWindowContext, ClipboardItem, - Corner, DismissEvent, DragMoveEvent, Entity, EventEmitter, ExternalPaths, FocusHandle, - Focusable, KeyContext, MouseButton, Pixels, Subscription, Task, UpdateGlobal, WeakEntity, - deferred, prelude::*, pulsating_between, + Action, Animation, AnimationExt, AnyElement, App, AsyncWindowContext, ClipboardItem, Corner, + DismissEvent, Empty, Entity, EventEmitter, ExternalPaths, FocusHandle, Focusable, KeyContext, + Pixels, Subscription, Task, UpdateGlobal, WeakEntity, prelude::*, pulsating_between, }; use language::LanguageRegistry; use language_model::{ConfigurationError, LanguageModelRegistry}; @@ -86,9 +85,8 @@ use ui::{ }; use util::ResultExt as _; use workspace::{ - CollaboratorId, DraggedSelection, DraggedSidebar, DraggedTab, FocusWorkspaceSidebar, - MultiWorkspace, SIDEBAR_RESIZE_HANDLE_SIZE, ToggleWorkspaceSidebar, ToggleZoom, - ToolbarItemView, Workspace, WorkspaceId, + CollaboratorId, DraggedSelection, DraggedTab, FocusWorkspaceSidebar, MultiWorkspace, + ToggleWorkspaceSidebar, ToggleZoom, ToolbarItemView, Workspace, WorkspaceId, dock::{DockPosition, Panel, PanelEvent}, multi_workspace_enabled, }; @@ -487,6 +485,9 @@ pub fn init(cx: &mut App) { sidebar.toggle(window, cx); }); } + // Explicitly notify the panel so the dock picks up + // the change to `has_main_element` via its observer. + panel.update(cx, |_, cx| cx.notify()); } }) .register_action(|workspace, _: &FocusWorkspaceSidebar, window, cx| { @@ -499,6 +500,9 @@ pub fn init(cx: &mut App) { sidebar.focus_or_unfocus(workspace, window, cx); }); } + // Explicitly notify the panel so the dock picks up + // any change to `has_main_element` via its observer. + panel.update(cx, |_, cx| cx.notify()); } }); }, @@ -896,6 +900,7 @@ pub struct AgentPanel { last_configuration_error_telemetry: Option, on_boarding_upsell_dismissed: AtomicBool, _active_view_observation: Option, + _sidebar_observation: Option, pub(crate) sidebar: Option>, } @@ -1225,14 +1230,21 @@ impl AgentPanel { last_configuration_error_telemetry: None, on_boarding_upsell_dismissed: AtomicBool::new(OnboardingUpsell::dismissed()), _active_view_observation: None, + _sidebar_observation: None, sidebar: None, }; // Initial sync of agent servers from extensions panel.sync_agent_servers_from_extensions(cx); - cx.defer_in(window, move |this, window, cx| { - this.sidebar = find_or_create_sidebar_for_window(window, cx); + cx.defer_in(window, move |this, _window, cx| { + this.sidebar = find_or_create_sidebar_for_window(_window, cx); + // Observe the sidebar so that when its open state changes, + // the panel (and thus the dock) is notified and re-rendered. + this._sidebar_observation = this + .sidebar + .as_ref() + .map(|sidebar| cx.observe(sidebar, |_, _, cx| cx.notify())); cx.notify(); }); @@ -3103,6 +3115,9 @@ impl Panel for AgentPanel { } fn size(&self, window: &Window, cx: &App) -> Pixels { + if let Some(sidebar) = &self.sidebar { + return sidebar.read(cx).width(cx); + } let settings = AgentSettings::get_global(cx); match self.position(window, cx) { DockPosition::Left | DockPosition::Right => { @@ -3113,6 +3128,10 @@ impl Panel for AgentPanel { } fn set_size(&mut self, size: Option, window: &mut Window, cx: &mut Context) { + if let Some(sidebar) = &self.sidebar { + sidebar.update(cx, |sidebar, cx| sidebar.set_width(size, cx)); + return; + } match self.position(window, cx) { DockPosition::Left | DockPosition::Right => self.width = size, DockPosition::Bottom => self.height = size, @@ -3166,6 +3185,22 @@ impl Panel for AgentPanel { self.zoomed = zoomed; cx.notify(); } + + fn render_center_element( + &mut self, + window: &mut Window, + cx: &mut Context, + ) -> Option { + Some(self.render_content(window, cx).into_any_element()) + } + + fn has_center_element(&self, _window: &Window, _: &App) -> bool { + true + } + + fn has_main_element(&self, window: &Window, cx: &App) -> bool { + sidebar_is_open(window, cx) + } } impl AgentPanel { @@ -3609,17 +3644,6 @@ impl AgentPanel { }) } - fn sidebar_info(&self, cx: &App) -> Option<(AnyView, Pixels, bool)> { - if !multi_workspace_enabled(cx) { - return None; - } - let sidebar = self.sidebar.as_ref()?; - let is_open = sidebar.read(cx).is_open(); - let width = sidebar.read(cx).width(cx); - let view: AnyView = sidebar.clone().into(); - Some((view, width, is_open)) - } - fn render_sidebar_toggle(&self, cx: &Context) -> Option { if !multi_workspace_enabled(cx) { return None; @@ -3649,65 +3673,6 @@ impl AgentPanel { ) } - fn render_sidebar(&self, cx: &Context) -> Option { - let (sidebar_view, sidebar_width, is_open) = self.sidebar_info(cx)?; - if !is_open { - return None; - } - - let docked_right = agent_panel_dock_position(cx) == DockPosition::Right; - let sidebar = self.sidebar.as_ref()?.downgrade(); - - let resize_handle = deferred( - div() - .id("sidebar-resize-handle") - .absolute() - .when(docked_right, |this| { - this.left(-SIDEBAR_RESIZE_HANDLE_SIZE / 2.) - }) - .when(!docked_right, |this| { - this.right(-SIDEBAR_RESIZE_HANDLE_SIZE / 2.) - }) - .top(px(0.)) - .h_full() - .w(SIDEBAR_RESIZE_HANDLE_SIZE) - .cursor_col_resize() - .on_drag(DraggedSidebar, |dragged, _, _, cx| { - cx.stop_propagation(); - cx.new(|_| dragged.clone()) - }) - .on_mouse_down(MouseButton::Left, |_, _, cx| { - cx.stop_propagation(); - }) - .on_mouse_up(MouseButton::Left, move |event, _, cx| { - if event.click_count == 2 { - sidebar - .update(cx, |sidebar, cx| { - sidebar.set_width(None, cx); - }) - .ok(); - cx.stop_propagation(); - } - }) - .occlude(), - ); - - Some( - div() - .id("sidebar-container") - .relative() - .h_full() - .w(sidebar_width) - .flex_shrink_0() - .when(docked_right, |this| this.border_l_1()) - .when(!docked_right, |this| this.border_r_1()) - .border_color(cx.theme().colors().border) - .child(sidebar_view) - .child(resize_handle) - .into_any_element(), - ) - } - fn render_toolbar(&self, window: &mut Window, cx: &mut Context) -> impl IntoElement { let agent_server_store = self.project.read(cx).agent_server_store().clone(); let focus_handle = self.focus_handle(cx); @@ -4704,17 +4669,17 @@ impl AgentPanel { } } -impl Render for AgentPanel { - fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { - // WARNING: Changes to this element hierarchy can have - // non-obvious implications to the layout of children. - // - // If you need to change it, please confirm: - // - The message editor expands (cmd-option-esc) correctly - // - When expanded, the buttons at the bottom of the panel are displayed correctly - // - Font size works as expected and can be changed with cmd-+/cmd- - // - Scrolling in all views works as expected - // - Files can be dropped into the panel +impl AgentPanel { + // WARNING: Changes to this element hierarchy can have + // non-obvious implications to the layout of children. + // + // If you need to change it, please confirm: + // - The message editor expands (cmd-option-esc) correctly + // - When expanded, the buttons at the bottom of the panel are displayed correctly + // - Font size works as expected and can be changed with cmd-+/cmd- + // - Scrolling in all views works as expected + // - Files can be dropped into the panel + fn render_content(&mut self, window: &mut Window, cx: &mut Context) -> AnyElement { let content = v_flex() .relative() .size_full() @@ -4802,44 +4767,33 @@ impl Render for AgentPanel { }) .children(self.render_trial_end_upsell(window, cx)); - let sidebar = self.render_sidebar(cx); - let has_sidebar = sidebar.is_some(); - let docked_right = agent_panel_dock_position(cx) == DockPosition::Right; - - let panel = h_flex() - .size_full() - .when(has_sidebar, |this| { - this.on_drag_move(cx.listener( - move |this, e: &DragMoveEvent, _window, cx| { - if let Some(sidebar) = &this.sidebar { - let width = if docked_right { - e.bounds.right() - e.event.position.x - } else { - e.event.position.x - }; - sidebar.update(cx, |sidebar, cx| { - sidebar.set_width(Some(width), cx); - }); - } - }, - )) - }) - .map(|this| { - if docked_right { - this.child(content).children(sidebar) - } else { - this.children(sidebar).child(content) - } - }); - match self.active_view.which_font_size_used() { WhichFontSize::AgentFont => { WithRemSize::new(ThemeSettings::get_global(cx).agent_ui_font_size(cx)) .size_full() - .child(panel) + .child(content) .into_any() } - _ => panel.into_any(), + _ => content.into_any(), + } + } +} + +impl Render for AgentPanel { + fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { + if multi_workspace_enabled(cx) { + // In multi-workspace mode, the main content is rendered as a + // center element via `render_center_element`. The dock only + // shows the sidebar when it is open. + if let Some(sidebar) = &self.sidebar { + if sidebar.read(cx).is_open() { + return sidebar.clone().into_any_element(); + } + } + Empty.into_any_element() + } else { + // In classic mode, the panel renders its full content in the dock. + self.render_content(window, cx) } } } diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 439c6df5ee45938368895a67834d57df695fde89..0c35f5d17333c4b0b7d7466d3af57b2717a2bf2a 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -5,10 +5,10 @@ use anyhow::Context as _; use client::proto; use gpui::{ - Action, AnyView, App, Axis, Context, Corner, Entity, EntityId, EventEmitter, FocusHandle, - Focusable, IntoElement, KeyContext, MouseButton, MouseDownEvent, MouseUpEvent, ParentElement, - Render, SharedString, StyleRefinement, Styled, Subscription, WeakEntity, Window, deferred, div, - px, + Action, AnyElement, AnyView, App, Axis, Context, Corner, Entity, EntityId, EventEmitter, + FocusHandle, Focusable, IntoElement, KeyContext, MouseButton, MouseDownEvent, MouseUpEvent, + ParentElement, Render, SharedString, StyleRefinement, Styled, Subscription, WeakEntity, Window, + deferred, div, px, }; use settings::SettingsStore; use std::sync::Arc; @@ -59,6 +59,19 @@ pub trait Panel: Focusable + EventEmitter + Render + Sized { fn enabled(&self, _cx: &App) -> bool { true } + fn render_center_element( + &mut self, + _window: &mut Window, + _cx: &mut Context, + ) -> Option { + None + } + fn has_center_element(&self, _window: &Window, _cx: &App) -> bool { + false + } + fn has_main_element(&self, _window: &Window, _cx: &App) -> bool { + true + } } pub trait PanelHandle: Send + Sync { @@ -83,6 +96,9 @@ pub trait PanelHandle: Send + Sync { fn to_any(&self) -> AnyView; fn activation_priority(&self, cx: &App) -> u32; fn enabled(&self, cx: &App) -> bool; + fn center_element(&self, window: &Window, cx: &mut App) -> Option; + fn has_center_element(&self, window: &Window, cx: &App) -> bool; + fn has_main_element(&self, window: &Window, cx: &App) -> bool; fn move_to_next_position(&self, window: &mut Window, cx: &mut App) { let current_position = self.position(window, cx); let next_position = [ @@ -187,6 +203,32 @@ where fn enabled(&self, cx: &App) -> bool { self.read(cx).enabled(cx) } + + fn center_element(&self, window: &Window, cx: &mut App) -> Option { + if !self.read(cx).has_center_element(window, cx) { + return None; + } + Some(cx.new(|_| PanelCenterView(self.clone())).into()) + } + + fn has_center_element(&self, window: &Window, cx: &App) -> bool { + self.read(cx).has_center_element(window, cx) + } + + fn has_main_element(&self, window: &Window, cx: &App) -> bool { + self.read(cx).has_main_element(window, cx) + } +} + +struct PanelCenterView(Entity); + +impl Render for PanelCenterView { + fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { + self.0.update(cx, |this, cx| { + this.render_center_element(window, cx) + .unwrap_or_else(|| gpui::Empty.into_any_element()) + }) + } } impl From<&dyn PanelHandle> for AnyView { @@ -690,6 +732,11 @@ impl Dock { Some(&entry.panel) } + pub fn visible_panel_center_element(&self, window: &Window, cx: &mut App) -> Option { + let entry = self.visible_entry()?; + entry.panel.center_element(window, cx) + } + pub fn active_panel(&self) -> Option<&Arc> { let panel_entry = self.active_panel_entry()?; Some(&panel_entry.panel) @@ -783,7 +830,10 @@ impl Dock { impl Render for Dock { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { let dispatch_context = Self::dispatch_context(); - if let Some(entry) = self.visible_entry() { + if let Some(entry) = self + .visible_entry() + .filter(|entry| entry.panel.has_main_element(window, cx)) + { let size = entry.panel.size(window, cx); let position = self.position; diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 0921a19486718c5375ed17ebbb3d7e314546f8d7..439eb35a94ded5582b4330492a59e111c4f0da0c 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -1,7 +1,7 @@ +pub use crate::pane_group::element::{PaneAxisElement, pane_axis}; use crate::{ AnyActiveCall, AppState, CollaboratorId, FollowerState, Pane, ParticipantLocation, Workspace, WorkspaceSettings, - pane_group::element::pane_axis, workspace_settings::{PaneSplitDirectionHorizontal, PaneSplitDirectionVertical}, }; use anyhow::Result; @@ -1035,7 +1035,7 @@ impl SplitDirection { } } -mod element { +pub mod element { use std::mem; use std::{cell::RefCell, iter, rc::Rc, sync::Arc}; @@ -1059,7 +1059,7 @@ mod element { const DIVIDER_SIZE: f32 = 1.0; - pub(super) fn pane_axis( + pub fn pane_axis( axis: Axis, basis: usize, flexes: Arc>>, diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index b57b5028a4e5558b1f90c715463165ba68d914e3..4eb5e237f33feb92e8ca29b54499829508377bc5 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -51,12 +51,12 @@ use futures::{ future::{Shared, try_join_all}, }; use gpui::{ - Action, AnyEntity, AnyView, AnyWeakView, App, AsyncApp, AsyncWindowContext, Bounds, Context, - CursorStyle, Decorations, DragMoveEvent, Entity, EntityId, EventEmitter, FocusHandle, - Focusable, Global, HitboxBehavior, Hsla, KeyContext, Keystroke, ManagedView, MouseButton, - PathPromptOptions, Point, PromptLevel, Render, ResizeEdge, Size, Stateful, Subscription, - SystemWindowTabController, Task, Tiling, WeakEntity, WindowBounds, WindowHandle, WindowId, - WindowOptions, actions, canvas, point, relative, size, transparent_black, + Action, AnyElement, AnyEntity, AnyView, AnyWeakView, App, AsyncApp, AsyncWindowContext, Axis, + Bounds, Context, CursorStyle, Decorations, DragMoveEvent, Entity, EntityId, EventEmitter, + FocusHandle, Focusable, Global, HitboxBehavior, Hsla, KeyContext, Keystroke, ManagedView, + MouseButton, PathPromptOptions, Point, PromptLevel, Render, ResizeEdge, Size, Stateful, + Subscription, SystemWindowTabController, Task, Tiling, WeakEntity, WindowBounds, WindowHandle, + WindowId, WindowOptions, actions, canvas, point, relative, size, transparent_black, }; pub use history_manager::*; pub use item::{ @@ -74,8 +74,9 @@ use notifications::{ pub use pane::*; pub use pane_group::{ ActivePaneDecorator, HANDLE_HITBOX_SIZE, Member, PaneAxis, PaneGroup, PaneRenderContext, - SplitDirection, + SplitDirection, pane_axis, }; +use parking_lot::Mutex; use persistence::{DB, SerializedWindowBounds, model::SerializedWorkspace}; pub use persistence::{ DB as WORKSPACE_DB, WorkspaceDb, delete_unloaded_items, @@ -1336,6 +1337,8 @@ pub struct Workspace { last_open_dock_positions: Vec, removing: bool, _panels_task: Option>>, + center_element_flexes: Arc>>, + center_element_bounding_boxes: Arc>>>>, } impl EventEmitter for Workspace {} @@ -1741,6 +1744,8 @@ impl Workspace { scheduled_tasks: Vec::new(), last_open_dock_positions: Vec::new(), removing: false, + center_element_flexes: Arc::new(Mutex::new(Vec::new())), + center_element_bounding_boxes: Arc::new(Mutex::new(Vec::new())), } } @@ -6996,6 +7001,113 @@ impl Workspace { ) } + fn render_center_with_panel_elements( + &mut self, + paddings: (Option
, Option
), + window: &mut Window, + cx: &mut App, + ) -> AnyElement { + let left_visible_panel = if self.zoomed_position != Some(DockPosition::Left) { + self.left_dock.read(cx).visible_panel().cloned() + } else { + None + }; + let left_center_element = + left_visible_panel.and_then(|panel| panel.center_element(window, cx)); + + let right_visible_panel = if self.zoomed_position != Some(DockPosition::Right) { + self.right_dock.read(cx).visible_panel().cloned() + } else { + None + }; + let right_center_element = + right_visible_panel.and_then(|panel| panel.center_element(window, cx)); + + let center_pane_group = h_flex() + .flex_1() + .when_some(paddings.0, |this, p| this.child(p.border_r_1())) + .child(self.center.render( + self.zoomed.as_ref(), + &PaneRenderContext { + follower_states: &self.follower_states, + active_call: self.active_call(), + active_pane: &self.active_pane, + app_state: &self.app_state, + project: &self.project, + workspace: &self.weak_self, + }, + window, + cx, + )) + .when_some(paddings.1, |this, p| this.child(p.border_l_1())) + .into_any_element(); + + if left_center_element.is_none() && right_center_element.is_none() { + return h_flex() + .flex_1() + .child(center_pane_group) + .into_any_element(); + } + + let member_count = + 1 + left_center_element.is_some() as usize + right_center_element.is_some() as usize; + + { + let mut flexes = self.center_element_flexes.lock(); + if flexes.len() != member_count { + *flexes = vec![1.; member_count]; + } + } + { + let mut bounding_boxes = self.center_element_bounding_boxes.lock(); + if bounding_boxes.len() != member_count { + *bounding_boxes = vec![None; member_count]; + } + } + + let axis_element = pane_axis( + Axis::Horizontal, + 0, + self.center_element_flexes.clone(), + self.center_element_bounding_boxes.clone(), + self.weak_self.clone(), + ); + + let axis_element = if let Some(left_element) = left_center_element { + axis_element.child( + div() + .flex_1() + .size_full() + .overflow_hidden() + .child(left_element), + ) + } else { + axis_element + }; + + let axis_element = axis_element.child(center_pane_group); + + let axis_element = if let Some(right_element) = right_center_element { + axis_element.child( + div() + .flex_1() + .size_full() + .overflow_hidden() + .child(right_element), + ) + } else { + axis_element + }; + + div() + .flex() + .flex_row() + .flex_1() + .size_full() + .child(axis_element) + .into_any_element() + } + pub fn for_window(window: &Window, cx: &App) -> Option> { window .root::() @@ -7584,6 +7696,8 @@ impl Render for Workspace { .collect::>(); let bottom_dock_layout = WorkspaceSettings::get_global(cx).bottom_dock_layout; + let mut center_area = Some(self.render_center_with_panel_elements(paddings, window, cx)); + div() .relative() .size_full() @@ -7591,402 +7705,306 @@ impl Render for Workspace { .flex_col() .font(ui_font) .gap_0() - .justify_start() - .items_start() - .text_color(colors.text) - .overflow_hidden() - .children(self.titlebar_item.clone()) - .on_modifiers_changed(move |_, _, cx| { - for &id in ¬ification_entities { - cx.notify(id); - } - }) - .child( - div() - .size_full() - .relative() - .flex_1() - .flex() - .flex_col() - .child( - div() - .id("workspace") - .bg(colors.background) - .relative() - .flex_1() - .w_full() - .flex() - .flex_col() - .overflow_hidden() - .border_t_1() - .border_b_1() - .border_color(colors.border) - .child({ - let this = cx.entity(); - canvas( - move |bounds, window, cx| { - this.update(cx, |this, cx| { - let bounds_changed = this.bounds != bounds; - this.bounds = bounds; - - if bounds_changed { - this.left_dock.update(cx, |dock, cx| { - dock.clamp_panel_size( - bounds.size.width, - window, - cx, - ) - }); - - this.right_dock.update(cx, |dock, cx| { - dock.clamp_panel_size( - bounds.size.width, - window, - cx, - ) - }); - - this.bottom_dock.update(cx, |dock, cx| { - dock.clamp_panel_size( - bounds.size.height, - window, - cx, - ) - }); - } - }) - }, - |_, _, _, _| {}, - ) - .absolute() - .size_full() - }) - .when(self.zoomed.is_none(), |this| { - this.on_drag_move(cx.listener( - move |workspace, - e: &DragMoveEvent, - window, - cx| { - if workspace.previous_dock_drag_coordinates - != Some(e.event.position) - { - workspace.previous_dock_drag_coordinates = - Some(e.event.position); - - match e.drag(cx).0 { - DockPosition::Left => { - workspace.resize_left_dock( - e.event.position.x - - workspace.bounds.left(), - window, - cx, - ); - } - DockPosition::Right => { - workspace.resize_right_dock( - workspace.bounds.right() - - e.event.position.x, - window, - cx, - ); - } - DockPosition::Bottom => { - workspace.resize_bottom_dock( - workspace.bounds.bottom() - - e.event.position.y, - window, - cx, - ); - } - }; - workspace.serialize_workspace(window, cx); - } - }, - )) - - }) - .child({ - match bottom_dock_layout { - BottomDockLayout::Full => div() - .flex() - .flex_col() - .h_full() - .child( - div() - .flex() - .flex_row() - .flex_1() - .overflow_hidden() - .children(self.render_dock( - DockPosition::Left, - &self.left_dock, + .justify_start() + .items_start() + .text_color(colors.text) + .overflow_hidden() + .children(self.titlebar_item.clone()) + .on_modifiers_changed(move |_, _, cx| { + for &id in ¬ification_entities { + cx.notify(id); + } + }) + .child( + div() + .size_full() + .relative() + .flex_1() + .flex() + .flex_col() + .child( + div() + .id("workspace") + .bg(colors.background) + .relative() + .flex_1() + .w_full() + .flex() + .flex_col() + .overflow_hidden() + .border_t_1() + .border_b_1() + .border_color(colors.border) + .child({ + let this = cx.entity(); + canvas( + move |bounds, window, cx| { + this.update(cx, |this, cx| { + let bounds_changed = this.bounds != bounds; + this.bounds = bounds; + + if bounds_changed { + this.left_dock.update(cx, |dock, cx| { + dock.clamp_panel_size( + bounds.size.width, window, cx, - )) - - .child( - div() - .flex() - .flex_col() - .flex_1() - .overflow_hidden() - .child( - h_flex() - .flex_1() - .when_some( - paddings.0, - |this, p| { - this.child( - p.border_r_1(), - ) - }, - ) - .child(self.center.render( - self.zoomed.as_ref(), - &PaneRenderContext { - follower_states: - &self.follower_states, - active_call: self.active_call(), - active_pane: &self.active_pane, - app_state: &self.app_state, - project: &self.project, - workspace: &self.weak_self, - }, - window, - cx, - )) - .when_some( - paddings.1, - |this, p| { - this.child( - p.border_l_1(), - ) - }, - ), - ), ) + }); - .children(self.render_dock( - DockPosition::Right, - &self.right_dock, + this.right_dock.update(cx, |dock, cx| { + dock.clamp_panel_size( + bounds.size.width, window, cx, - )), - ) - .child(div().w_full().children(self.render_dock( - DockPosition::Bottom, - &self.bottom_dock, - window, - cx - ))), - - BottomDockLayout::LeftAligned => div() - .flex() - .flex_row() - .h_full() - .child( - div() - .flex() - .flex_col() - .flex_1() - .h_full() - .child( - div() - .flex() - .flex_row() - .flex_1() - .children(self.render_dock(DockPosition::Left, &self.left_dock, window, cx)) - - .child( - div() - .flex() - .flex_col() - .flex_1() - .overflow_hidden() - .child( - h_flex() - .flex_1() - .when_some(paddings.0, |this, p| this.child(p.border_r_1())) - .child(self.center.render( - self.zoomed.as_ref(), - &PaneRenderContext { - follower_states: - &self.follower_states, - active_call: self.active_call(), - active_pane: &self.active_pane, - app_state: &self.app_state, - project: &self.project, - workspace: &self.weak_self, - }, - window, - cx, - )) - .when_some(paddings.1, |this, p| this.child(p.border_l_1())), - ) - ) - - ) - .child( - div() - .w_full() - .children(self.render_dock(DockPosition::Bottom, &self.bottom_dock, window, cx)) - ), - ) - .children(self.render_dock( - DockPosition::Right, - &self.right_dock, - window, - cx, - )), - - BottomDockLayout::RightAligned => div() - .flex() - .flex_row() - .h_full() - .children(self.render_dock( - DockPosition::Left, - &self.left_dock, - window, - cx, - )) - - .child( - div() - .flex() - .flex_col() - .flex_1() - .h_full() - .child( - div() - .flex() - .flex_row() - .flex_1() - .child( - div() - .flex() - .flex_col() - .flex_1() - .overflow_hidden() - .child( - h_flex() - .flex_1() - .when_some(paddings.0, |this, p| this.child(p.border_r_1())) - .child(self.center.render( - self.zoomed.as_ref(), - &PaneRenderContext { - follower_states: - &self.follower_states, - active_call: self.active_call(), - active_pane: &self.active_pane, - app_state: &self.app_state, - project: &self.project, - workspace: &self.weak_self, - }, - window, - cx, - )) - .when_some(paddings.1, |this, p| this.child(p.border_l_1())), - ) - ) - - .children(self.render_dock(DockPosition::Right, &self.right_dock, window, cx)) ) - .child( - div() - .w_full() - .children(self.render_dock(DockPosition::Bottom, &self.bottom_dock, window, cx)) - ), - ), - - BottomDockLayout::Contained => div() - .flex() - .flex_row() - .h_full() - .children(self.render_dock( - DockPosition::Left, - &self.left_dock, - window, - cx, - )) - - .child( - div() - .flex() - .flex_col() - .flex_1() - .overflow_hidden() - .child( - h_flex() - .flex_1() - .when_some(paddings.0, |this, p| { - this.child(p.border_r_1()) - }) - .child(self.center.render( - self.zoomed.as_ref(), - &PaneRenderContext { - follower_states: - &self.follower_states, - active_call: self.active_call(), - active_pane: &self.active_pane, - app_state: &self.app_state, - project: &self.project, - workspace: &self.weak_self, - }, - window, - cx, - )) - .when_some(paddings.1, |this, p| { - this.child(p.border_l_1()) - }), + }); + + this.bottom_dock.update(cx, |dock, cx| { + dock.clamp_panel_size( + bounds.size.height, + window, + cx, ) - .children(self.render_dock( - DockPosition::Bottom, - &self.bottom_dock, + }); + } + }) + }, + |_, _, _, _| {}, + ) + .absolute() + .size_full() + }) + .when(self.zoomed.is_none(), |this| { + this.on_drag_move(cx.listener( + move |workspace, e: &DragMoveEvent, window, cx| { + if workspace.previous_dock_drag_coordinates + != Some(e.event.position) + { + workspace.previous_dock_drag_coordinates = + Some(e.event.position); + + match e.drag(cx).0 { + DockPosition::Left => { + workspace.resize_left_dock( + e.event.position.x + - workspace.bounds.left(), window, cx, - )), - ) + ); + } + DockPosition::Right => { + workspace.resize_right_dock( + workspace.bounds.right() + - e.event.position.x, + window, + cx, + ); + } + DockPosition::Bottom => { + workspace.resize_bottom_dock( + workspace.bounds.bottom() + - e.event.position.y, + window, + cx, + ); + } + }; + workspace.serialize_workspace(window, cx); + } + }, + )) + }) + .child({ + match bottom_dock_layout { + BottomDockLayout::Full => div() + .flex() + .flex_col() + .h_full() + .child( + div() + .flex() + .flex_row() + .flex_1() + .overflow_hidden() + .children(self.render_dock( + DockPosition::Left, + &self.left_dock, + window, + cx, + )) + .child( + div() + .flex() + .flex_col() + .flex_1() + .overflow_hidden() + .children(center_area.take()), + ) + .children(self.render_dock( + DockPosition::Right, + &self.right_dock, + window, + cx, + )), + ) + .child(div().w_full().children(self.render_dock( + DockPosition::Bottom, + &self.bottom_dock, + window, + cx, + ))), + + BottomDockLayout::LeftAligned => div() + .flex() + .flex_row() + .h_full() + .child( + div() + .flex() + .flex_col() + .flex_1() + .h_full() + .child( + div() + .flex() + .flex_row() + .flex_1() + .children(self.render_dock( + DockPosition::Left, + &self.left_dock, + window, + cx, + )) + .child( + div() + .flex() + .flex_col() + .flex_1() + .overflow_hidden() + .children(center_area.take()), + ), + ) + .child(div().w_full().children(self.render_dock( + DockPosition::Bottom, + &self.bottom_dock, + window, + cx, + ))), + ) + .children(self.render_dock( + DockPosition::Right, + &self.right_dock, + window, + cx, + )), + + BottomDockLayout::RightAligned => div() + .flex() + .flex_row() + .h_full() + .children(self.render_dock( + DockPosition::Left, + &self.left_dock, + window, + cx, + )) + .child( + div() + .flex() + .flex_col() + .flex_1() + .h_full() + .child( + div() + .flex() + .flex_row() + .flex_1() + .child( + div() + .flex() + .flex_col() + .flex_1() + .overflow_hidden() + .children(center_area.take()), + ) + .children(self.render_dock( + DockPosition::Right, + &self.right_dock, + window, + cx, + )), + ) + .child(div().w_full().children(self.render_dock( + DockPosition::Bottom, + &self.bottom_dock, + window, + cx, + ))), + ), + + BottomDockLayout::Contained => div() + .flex() + .flex_row() + .h_full() + .children(self.render_dock( + DockPosition::Left, + &self.left_dock, + window, + cx, + )) + .child( + div() + .flex() + .flex_col() + .flex_1() + .overflow_hidden() + .children(center_area.take()) + .children(self.render_dock( + DockPosition::Bottom, + &self.bottom_dock, + window, + cx, + )), + ) + .children(self.render_dock( + DockPosition::Right, + &self.right_dock, + window, + cx, + )), + } + }) + .children(self.zoomed.as_ref().and_then(|view| { + let zoomed_view = view.upgrade()?; + let div = div() + .occlude() + .absolute() + .overflow_hidden() + .border_color(colors.border) + .bg(colors.background) + .child(zoomed_view) + .inset_0() + .shadow_lg(); + + if !WorkspaceSettings::get_global(cx).zoomed_padding { + return Some(div); + } - .children(self.render_dock( - DockPosition::Right, - &self.right_dock, - window, - cx, - )), - } + Some(match self.zoomed_position { + Some(DockPosition::Left) => div.right_2().border_r_1(), + Some(DockPosition::Right) => div.left_2().border_l_1(), + Some(DockPosition::Bottom) => div.top_2().border_t_1(), + None => div.top_2().bottom_2().left_2().right_2().border_1(), }) - .children(self.zoomed.as_ref().and_then(|view| { - let zoomed_view = view.upgrade()?; - let div = div() - .occlude() - .absolute() - .overflow_hidden() - .border_color(colors.border) - .bg(colors.background) - .child(zoomed_view) - .inset_0() - .shadow_lg(); - - if !WorkspaceSettings::get_global(cx).zoomed_padding { - return Some(div); - } - - Some(match self.zoomed_position { - Some(DockPosition::Left) => div.right_2().border_r_1(), - Some(DockPosition::Right) => div.left_2().border_l_1(), - Some(DockPosition::Bottom) => div.top_2().border_t_1(), - None => { - div.top_2().bottom_2().left_2().right_2().border_1() - } - }) - })) - .children(self.render_notifications(window, cx)), - ) - .when(self.status_bar_visible(cx), |parent| { - parent.child(self.status_bar.clone()) - }) - .child(self.toast_layer.clone()), - ) + })) + .children(self.render_notifications(window, cx)), + ) + .when(self.status_bar_visible(cx), |parent| { + parent.child(self.status_bar.clone()) + }) + .child(self.toast_layer.clone()), + ) } }