diff --git a/crates/gpui/src/elements.rs b/crates/gpui/src/elements.rs index 3caa91a3b8..f593615ae7 100644 --- a/crates/gpui/src/elements.rs +++ b/crates/gpui/src/elements.rs @@ -196,12 +196,7 @@ pub trait Element: 'static { where Self: 'static + Sized, { - Resizable::new( - self.into_any(), - side, - size, - on_resize - ) + Resizable::new(self.into_any(), side, size, on_resize) } } diff --git a/crates/gpui/src/elements/resizable.rs b/crates/gpui/src/elements/resizable.rs index a3d95893f4..da4b3473b3 100644 --- a/crates/gpui/src/elements/resizable.rs +++ b/crates/gpui/src/elements/resizable.rs @@ -7,8 +7,8 @@ use crate::{ geometry::rect::RectF, platform::{CursorStyle, MouseButton}, scene::MouseDrag, - AnyElement, Axis, Element, LayoutContext, MouseRegion, SceneBuilder, View, - ViewContext, SizeConstraint, + AnyElement, Axis, Element, LayoutContext, MouseRegion, SceneBuilder, SizeConstraint, View, + ViewContext, }; #[derive(Copy, Clone, Debug)] @@ -73,7 +73,7 @@ pub struct Resizable { child: AnyElement, handle_side: HandleSide, handle_size: f32, - on_resize: Rc)>> + on_resize: Rc)>>, } const DEFAULT_HANDLE_SIZE: f32 = 4.0; @@ -83,7 +83,7 @@ impl Resizable { child: AnyElement, handle_side: HandleSide, size: f32, - on_resize: impl 'static + FnMut(&mut V, f32, &mut ViewContext) + on_resize: impl 'static + FnMut(&mut V, f32, &mut ViewContext), ) -> Self { let child = match handle_side.axis() { Axis::Horizontal => child.constrained().with_max_width(size), @@ -133,22 +133,29 @@ impl Element for Resizable { enum ResizeHandle {} scene.push_mouse_region( - MouseRegion::new::(cx.view_id(), self.handle_side as usize, handle_region) - .on_down(MouseButton::Left, |_, _: &mut V, _| {}) // This prevents the mouse down event from being propagated elsewhere - .on_drag(MouseButton::Left, { - let bounds = bounds.clone(); - let side = self.handle_side; - let prev_size = side.relevant_component(bounds.size()); - let min_size = side.relevant_component(constraint.min); - let max_size = side.relevant_component(constraint.max); - let on_resize = self.on_resize.clone(); - move |event, view: &mut V, cx| { - let new_size = min_size.max(prev_size + side.compute_delta(event)).min(max_size).round(); - if new_size != prev_size { - on_resize.borrow_mut()(view, new_size, cx); - } + MouseRegion::new::( + cx.view_id(), + self.handle_side as usize, + handle_region, + ) + .on_down(MouseButton::Left, |_, _: &mut V, _| {}) // This prevents the mouse down event from being propagated elsewhere + .on_drag(MouseButton::Left, { + let bounds = bounds.clone(); + let side = self.handle_side; + let prev_size = side.relevant_component(bounds.size()); + let min_size = side.relevant_component(constraint.min); + let max_size = side.relevant_component(constraint.max); + let on_resize = self.on_resize.clone(); + move |event, view: &mut V, cx| { + let new_size = min_size + .max(prev_size + side.compute_delta(event)) + .min(max_size) + .round(); + if new_size != prev_size { + on_resize.borrow_mut()(view, new_size, cx); } - }), + } + }), ); scene.push_cursor_region(crate::CursorRegion { diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index ac0df343ae..c76d2481ef 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -12,8 +12,8 @@ use gpui::{ geometry::vector::Vector2F, keymap_matcher::KeymapContext, platform::{CursorStyle, MouseButton, PromptLevel}, - AnyElement, AppContext, ClipboardItem, Element, Entity, ModelHandle, Task, View, ViewContext, - ViewHandle, WeakViewHandle, + AnyElement, AppContext, Axis, ClipboardItem, Element, Entity, ModelHandle, Task, View, + ViewContext, ViewHandle, WeakViewHandle, }; use menu::{Confirm, SelectNext, SelectPrev}; use project::{Entry, EntryKind, Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId}; @@ -1347,9 +1347,7 @@ impl Entity for ProjectPanel { impl workspace::dock::Panel for ProjectPanel { fn position(&self, cx: &gpui::WindowContext) -> DockPosition { let settings = cx.global::(); - match settings - .project_panel - .dock { + match settings.project_panel.dock { settings::ProjectPanelDockPosition::Left => DockPosition::Left, settings::ProjectPanelDockPosition::Right => DockPosition::Right, } diff --git a/crates/settings/src/settings.rs b/crates/settings/src/settings.rs index df03d4c1f6..618ccb89ac 100644 --- a/crates/settings/src/settings.rs +++ b/crates/settings/src/settings.rs @@ -599,7 +599,10 @@ impl Settings { } self.editor_overrides = data.editor; merge(&mut self.project_panel.dock, data.project_panel.dock); - merge(&mut self.project_panel.default_width, data.project_panel.default_width); + merge( + &mut self.project_panel.default_width, + data.project_panel.default_width, + ); self.git_overrides = data.git.unwrap_or_default(); self.journal_overrides = data.journal; self.terminal_defaults.font_size = data.terminal.font_size; diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index 6ce1e4f691..cafe8bd5dc 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -2,7 +2,8 @@ use crate::{StatusItemView, Workspace}; use context_menu::{ContextMenu, ContextMenuItem}; use gpui::{ elements::*, impl_actions, platform::CursorStyle, platform::MouseButton, AnyViewHandle, - AppContext, Entity, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, + AppContext, Axis, Entity, Subscription, View, ViewContext, ViewHandle, WeakViewHandle, + WindowContext, }; use serde::Deserialize; use settings::Settings; @@ -117,6 +118,13 @@ impl DockPosition { Self::Right => HandleSide::Left, } } + + pub fn axis(&self) -> Axis { + match self { + Self::Left | Self::Right => Axis::Horizontal, + Self::Bottom => Axis::Vertical, + } + } } struct PanelEntry { @@ -247,6 +255,23 @@ impl Dock { } } + pub fn panel_size(&self, panel: &dyn PanelHandle) -> Option { + self.panel_entries + .iter() + .find(|entry| entry.panel.id() == panel.id()) + .map(|entry| entry.size) + } + + pub fn resize_panel(&mut self, panel: &dyn PanelHandle, size: f32) { + let entry = self + .panel_entries + .iter_mut() + .find(|entry| entry.panel.id() == panel.id()); + if let Some(entry) = entry { + entry.size = size; + } + } + pub fn active_panel_size(&self) -> Option { if self.is_open { self.panel_entries @@ -281,9 +306,13 @@ impl View for Dock { ChildView::new(active_panel.as_any(), cx) .contained() .with_style(style.container) - .resizable(self.position.to_resize_handle_side(), size, |dock: &mut Self, size, cx| { - dock.resize_active_panel(size, cx); - }) + .resizable( + self.position.to_resize_handle_side(), + size, + |dock: &mut Self, size, cx| { + dock.resize_active_panel(size, cx); + }, + ) .into_any() } else { Empty::new().into_any() @@ -487,7 +516,10 @@ pub(crate) mod test { } fn default_size(&self, _: &WindowContext) -> f32 { - 300. + match self.position.axis() { + Axis::Horizontal => 300., + Axis::Vertical => 200., + } } fn icon_path(&self) -> &'static str { diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 791d0d0a44..7bfe180d50 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -852,10 +852,18 @@ impl Workspace { cx.subscribe(&panel, { let mut dock = dock.clone(); + let mut prev_position = panel.position(cx); move |this, panel, event, cx| { if T::should_change_position_on_event(event) { + let new_position = panel.read(cx).position(cx); let mut was_visible = false; + let mut size = None; dock.update(cx, |dock, cx| { + if new_position.axis() == prev_position.axis() { + size = dock.panel_size(&panel); + } + prev_position = new_position; + was_visible = dock.is_open() && dock .active_panel() @@ -869,7 +877,11 @@ impl Workspace { } .clone(); dock.update(cx, |dock, cx| { - dock.add_panel(panel, cx); + dock.add_panel(panel.clone(), cx); + if let Some(size) = size { + dock.resize_panel(&panel, size); + } + if was_visible { dock.set_open(true, cx); dock.activate_panel(dock.panels_len() - 1, cx); @@ -3678,10 +3690,17 @@ mod tests { .right_dock() .update(cx, |right_dock, cx| right_dock.set_open(true, cx)); + let left_dock = workspace.left_dock(); assert_eq!( - workspace.left_dock().read(cx).active_panel().unwrap().id(), + left_dock.read(cx).active_panel().unwrap().id(), panel_1.id() ); + assert_eq!( + left_dock.read(cx).active_panel_size().unwrap(), + panel_1.default_size(cx) + ); + + left_dock.update(cx, |left_dock, cx| left_dock.resize_active_panel(1337., cx)); assert_eq!( workspace.right_dock().read(cx).active_panel().unwrap().id(), panel_2.id() @@ -3700,10 +3719,12 @@ mod tests { // Since it was the only panel on the left, the left dock should now be closed. assert!(!workspace.left_dock().read(cx).is_open()); assert!(workspace.left_dock().read(cx).active_panel().is_none()); + let right_dock = workspace.right_dock(); assert_eq!( - workspace.right_dock().read(cx).active_panel().unwrap().id(), + right_dock.read(cx).active_panel().unwrap().id(), panel_1.id() ); + assert_eq!(right_dock.read(cx).active_panel_size().unwrap(), 1337.); // Now we move panel_2 to the left panel_2.set_position(DockPosition::Left, cx); @@ -3727,13 +3748,33 @@ mod tests { workspace.update(cx, |workspace, cx| { // Since panel_1 was visible on the right, we open the left dock and make panel_1 active. - assert!(workspace.left_dock().read(cx).is_open()); + let left_dock = workspace.left_dock(); + assert!(left_dock.read(cx).is_open()); assert_eq!( - workspace.left_dock().read(cx).active_panel().unwrap().id(), + left_dock.read(cx).active_panel().unwrap().id(), panel_1.id() ); + assert_eq!(left_dock.read(cx).active_panel_size().unwrap(), 1337.); // And right the dock should be closed as it no longer has any panels. assert!(!workspace.right_dock().read(cx).is_open()); + + // Now we move panel_1 to the bottom + panel_1.set_position(DockPosition::Bottom, cx); + }); + + workspace.update(cx, |workspace, cx| { + // Since panel_1 was visible on the left, we close the left dock. + assert!(!workspace.left_dock().read(cx).is_open()); + // The bottom dock is sized based on the panel's default size, + // since the panel orientation changed from vertical to horizontal. + assert_eq!( + workspace + .bottom_dock() + .read(cx) + .active_panel_size() + .unwrap(), + panel_1.default_size(cx), + ); }); } }