@@ -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<String>,
on_boarding_upsell_dismissed: AtomicBool,
_active_view_observation: Option<Subscription>,
+ _sidebar_observation: Option<Subscription>,
pub(crate) sidebar: Option<Entity<crate::sidebar::Sidebar>>,
}
@@ -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<Pixels>, window: &mut Window, cx: &mut Context<Self>) {
+ 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<Self>,
+ ) -> Option<AnyElement> {
+ 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<Self>) -> Option<AnyElement> {
if !multi_workspace_enabled(cx) {
return None;
@@ -3649,65 +3673,6 @@ impl AgentPanel {
)
}
- fn render_sidebar(&self, cx: &Context<Self>) -> Option<AnyElement> {
- 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<DraggedSidebar>, _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<Self>) -> 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)
}
}
}
@@ -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<PanelEvent> + Render + Sized {
fn enabled(&self, _cx: &App) -> bool {
true
}
+ fn render_center_element(
+ &mut self,
+ _window: &mut Window,
+ _cx: &mut Context<Self>,
+ ) -> Option<AnyElement> {
+ 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<AnyView>;
+ 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<AnyView> {
+ 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<T: Panel>(Entity<T>);
+
+impl<T: Panel> Render for PanelCenterView<T> {
+ fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> 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<AnyView> {
+ let entry = self.visible_entry()?;
+ entry.panel.center_element(window, cx)
+ }
+
pub fn active_panel(&self) -> Option<&Arc<dyn PanelHandle>> {
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<Self>) -> 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;
@@ -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<DockPosition>,
removing: bool,
_panels_task: Option<Task<Result<()>>>,
+ center_element_flexes: Arc<Mutex<Vec<f32>>>,
+ center_element_bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
}
impl EventEmitter<Event> 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<Div>, Option<Div>),
+ 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<Entity<Workspace>> {
window
.root::<MultiWorkspace>()
@@ -7584,6 +7696,8 @@ impl Render for Workspace {
.collect::<Vec<_>>();
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<DraggedDock>,
- 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<DraggedDock>, 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()),
+ )
}
}