diff --git a/crates/debugger_ui/src/session/running.rs b/crates/debugger_ui/src/session/running.rs index 1df442ef88fada109b6b7ad6e3bb5cf63f0ea453..626511aad00e1f224a12619181bdc8fe2991e8a3 100644 --- a/crates/debugger_ui/src/session/running.rs +++ b/crates/debugger_ui/src/session/running.rs @@ -109,18 +109,17 @@ impl Render for RunningState { .into_iter() .find(|pane| pane.read(cx).is_zoomed()); - let active = self.panes.panes().into_iter().next(); + let active = self.panes.panes().into_iter().next().cloned(); let pane = if let Some(zoomed_pane) = zoomed_pane { zoomed_pane.update(cx, |pane, cx| pane.render(window, cx).into_any_element()) } else if let Some(active) = active { - self.panes - .render( - None, - &ActivePaneDecorator::new(active, &self.workspace), - window, - cx, - ) - .into_any_element() + self.panes.render( + None, + None, + &ActivePaneDecorator::new(&active, &self.workspace), + window, + cx, + ) } else { div().into_any_element() }; diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index 93b9e651191e791da8bbda35600c3db001b46d90..2a0cab5d7596c027cf5462cea303fe5fe53869e2 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -1362,6 +1362,7 @@ impl Render for TerminalPanel { .update(cx, |workspace, cx| { registrar.size_full().child(self.center.render( workspace.zoomed_item(), + None, &workspace::PaneRenderContext { follower_states: &HashMap::default(), active_call: workspace.active_call(), diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 0921a19486718c5375ed17ebbb3d7e314546f8d7..aecd1aeeb087e8c241ed5069bfa69accf81ca461 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -29,6 +29,8 @@ const VERTICAL_MIN_SIZE: f32 = 100.; pub struct PaneGroup { pub root: Member, pub is_center: bool, + left_item_flexes: Arc>>, + left_item_bounding_boxes: Arc>>>>, } pub struct PaneRenderResult { @@ -41,6 +43,8 @@ impl PaneGroup { Self { root, is_center: false, + left_item_flexes: Arc::new(Mutex::new(Vec::new())), + left_item_bounding_boxes: Arc::new(Mutex::new(Vec::new())), } } @@ -48,6 +52,8 @@ impl PaneGroup { Self { root: Member::Pane(pane), is_center: false, + left_item_flexes: Arc::new(Mutex::new(Vec::new())), + left_item_bounding_boxes: Arc::new(Mutex::new(Vec::new())), } } @@ -221,13 +227,67 @@ impl PaneGroup { } pub fn render( - &self, + &mut self, zoomed: Option<&AnyWeakView>, + left_item: Option, render_cx: &dyn PaneLeaderDecorator, window: &mut Window, cx: &mut App, - ) -> impl IntoElement { - self.root.render(0, zoomed, render_cx, window, cx).element + ) -> gpui::AnyElement { + let Some(left_item) = left_item else { + return self.root.render(0, zoomed, render_cx, window, cx).element; + }; + + let left_element = div() + .relative() + .flex_1() + .size_full() + .child(left_item) + .into_any_element(); + + let mut children: Vec = vec![left_element]; + let mut is_leaf_pane_mask: Vec = vec![false]; + + match &self.root { + Member::Axis(axis) if axis.axis == Axis::Horizontal => { + for (ix, member) in axis.members.iter().enumerate() { + let result = member.render((0 + ix) * 10, zoomed, render_cx, window, cx); + children.push(result.element.into_any_element()); + is_leaf_pane_mask.push(matches!(member, Member::Pane(_))); + } + } + _ => { + let result = self.root.render(0, zoomed, render_cx, window, cx); + children.push(result.element.into_any_element()); + is_leaf_pane_mask.push(false); + } + } + + let child_count = children.len(); + + { + let mut flexes = self.left_item_flexes.lock(); + if flexes.len() != child_count { + *flexes = vec![1.0; child_count]; + } + } + { + let mut bounding_boxes = self.left_item_bounding_boxes.lock(); + if bounding_boxes.len() != child_count { + *bounding_boxes = vec![None; child_count]; + } + } + + pane_axis( + Axis::Horizontal, + usize::MAX / 2, + self.left_item_flexes.clone(), + self.left_item_bounding_boxes.clone(), + render_cx.workspace().clone(), + ) + .with_is_leaf_pane_mask(is_leaf_pane_mask) + .children(children) + .into_any_element() } pub fn panes(&self) -> Vec<&Entity> { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index f4726323049ebd11727a4dfa50279551c0a41065..e055471859a101105cecb3c81f16b4553526c4a3 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1336,6 +1336,7 @@ pub struct Workspace { last_open_dock_positions: Vec, removing: bool, _panels_task: Option>>, + left_item: Option>, } impl EventEmitter for Workspace {} @@ -1741,6 +1742,7 @@ impl Workspace { scheduled_tasks: Vec::new(), last_open_dock_positions: Vec::new(), removing: false, + left_item: None, } } @@ -2009,6 +2011,15 @@ impl Workspace { &self.left_dock } + pub fn set_left_item(&mut self, view: Option>, cx: &mut Context) { + self.left_item = view; + cx.notify(); + } + + pub fn left_item(&self) -> Option> { + self.left_item.clone() + } + pub fn bottom_dock(&self) -> &Entity { &self.bottom_dock } @@ -7653,6 +7664,35 @@ impl Render for Workspace { (None, None) }; let ui_font = theme::setup_ui_font(window, cx); + let left_item = self.left_item.as_ref().map(|v| v.to_any()); + let render_left_dock = left_item.is_none(); + + let Self { + ref mut center, + ref zoomed, + ref follower_states, + ref active_call, + ref active_pane, + ref app_state, + ref project, + ref weak_self, + .. + } = *self; + let active_call = active_call.as_ref().map(|(call, _)| &*call.0); + let center_element = center.render( + zoomed.as_ref(), + left_item, + &PaneRenderContext { + follower_states, + active_call, + active_pane, + app_state, + project, + workspace: weak_self, + }, + window, + cx, + ); let theme = cx.theme().clone(); let colors = theme.colors(); @@ -7670,402 +7710,377 @@ 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( + 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() + .when(render_left_dock, |this| { + this.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(center_element) + .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::LeftAligned => div() + .flex() + .flex_row() + .h_full() + .child( + div() + .flex() + .flex_col() + .flex_1() + .h_full() + .child( + div() + .flex() + .flex_row() + .flex_1() + .when(render_left_dock, |this| { + this.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(center_element) + .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() + .when(render_left_dock, |this| { + this.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( + }) + .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(center_element) + .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() + .when(render_left_dock, |this| { + this.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(center_element) + .when_some(paddings.1, |this, p| { + this.child(p.border_l_1()) + }), + ) + .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); + } - .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::Bottom, - &self.bottom_dock, - window, - cx, - )), - ) - - .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()), + ) } } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index abe89990a2bb28255d8989b6a659c2ec65f20bb9..8659990183a3996e45c792bda721607c9184f675 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -674,11 +674,12 @@ fn interpret_panel_dock_position( preferred: DockPosition, agent_mode: bool, ) -> DockPosition { - let is_agent_panel = panel.panel_key() == agent_ui::AgentPanel::panel_key(); if agent_mode { + let is_agent_panel = panel.panel_key() == agent_ui::AgentPanel::panel_key(); if is_agent_panel { return DockPosition::Left; - } else if preferred == DockPosition::Left { + } + if preferred == DockPosition::Left { return DockPosition::Right; } } @@ -762,6 +763,13 @@ fn update_panel_positions( agent_mode: bool, cx: &mut Context, ) { + if agent_mode { + let agent_panel_view = find_agent_panel_view(workspace, cx); + workspace.set_left_item(agent_panel_view, cx); + } else { + workspace.set_left_item(None, cx); + } + let panels_and_positions = workspace.all_panel_ids_and_positions(cx); for (panel_id, current_position) in panels_and_positions { let panel_handle = workspace @@ -784,6 +792,25 @@ fn update_panel_positions( } } +fn find_agent_panel_view(workspace: &Workspace, cx: &App) -> Option> { + for dock in workspace.all_docks() { + let dock = dock.read(cx); + for panel_id in dock.panel_ids() { + if let Some(panel) = dock.panel_for_id(panel_id) { + if panel.panel_key() == agent_ui::AgentPanel::panel_key() { + return Some(panel.clone()); + } + } + } + } + if let Some(left_item) = workspace.left_item() { + if left_item.panel_key() == agent_ui::AgentPanel::panel_key() { + return Some(left_item.clone()); + } + } + None +} + fn setup_or_teardown_ai_panel( workspace: &mut Workspace, window: &mut Window,