mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-05 10:20:51 +00:00
Preserve panel size when re-docking between left and right
This commit is contained in:
parent
214354b4da
commit
bd795d7607
6 changed files with 117 additions and 41 deletions
|
@ -196,12 +196,7 @@ pub trait Element<V: View>: 'static {
|
||||||
where
|
where
|
||||||
Self: 'static + Sized,
|
Self: 'static + Sized,
|
||||||
{
|
{
|
||||||
Resizable::new(
|
Resizable::new(self.into_any(), side, size, on_resize)
|
||||||
self.into_any(),
|
|
||||||
side,
|
|
||||||
size,
|
|
||||||
on_resize
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@ use crate::{
|
||||||
geometry::rect::RectF,
|
geometry::rect::RectF,
|
||||||
platform::{CursorStyle, MouseButton},
|
platform::{CursorStyle, MouseButton},
|
||||||
scene::MouseDrag,
|
scene::MouseDrag,
|
||||||
AnyElement, Axis, Element, LayoutContext, MouseRegion, SceneBuilder, View,
|
AnyElement, Axis, Element, LayoutContext, MouseRegion, SceneBuilder, SizeConstraint, View,
|
||||||
ViewContext, SizeConstraint,
|
ViewContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
@ -73,7 +73,7 @@ pub struct Resizable<V: View> {
|
||||||
child: AnyElement<V>,
|
child: AnyElement<V>,
|
||||||
handle_side: HandleSide,
|
handle_side: HandleSide,
|
||||||
handle_size: f32,
|
handle_size: f32,
|
||||||
on_resize: Rc<RefCell<dyn FnMut(&mut V, f32, &mut ViewContext<V>)>>
|
on_resize: Rc<RefCell<dyn FnMut(&mut V, f32, &mut ViewContext<V>)>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_HANDLE_SIZE: f32 = 4.0;
|
const DEFAULT_HANDLE_SIZE: f32 = 4.0;
|
||||||
|
@ -83,7 +83,7 @@ impl<V: View> Resizable<V> {
|
||||||
child: AnyElement<V>,
|
child: AnyElement<V>,
|
||||||
handle_side: HandleSide,
|
handle_side: HandleSide,
|
||||||
size: f32,
|
size: f32,
|
||||||
on_resize: impl 'static + FnMut(&mut V, f32, &mut ViewContext<V>)
|
on_resize: impl 'static + FnMut(&mut V, f32, &mut ViewContext<V>),
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let child = match handle_side.axis() {
|
let child = match handle_side.axis() {
|
||||||
Axis::Horizontal => child.constrained().with_max_width(size),
|
Axis::Horizontal => child.constrained().with_max_width(size),
|
||||||
|
@ -133,22 +133,29 @@ impl<V: View> Element<V> for Resizable<V> {
|
||||||
|
|
||||||
enum ResizeHandle {}
|
enum ResizeHandle {}
|
||||||
scene.push_mouse_region(
|
scene.push_mouse_region(
|
||||||
MouseRegion::new::<ResizeHandle>(cx.view_id(), self.handle_side as usize, handle_region)
|
MouseRegion::new::<ResizeHandle>(
|
||||||
.on_down(MouseButton::Left, |_, _: &mut V, _| {}) // This prevents the mouse down event from being propagated elsewhere
|
cx.view_id(),
|
||||||
.on_drag(MouseButton::Left, {
|
self.handle_side as usize,
|
||||||
let bounds = bounds.clone();
|
handle_region,
|
||||||
let side = self.handle_side;
|
)
|
||||||
let prev_size = side.relevant_component(bounds.size());
|
.on_down(MouseButton::Left, |_, _: &mut V, _| {}) // This prevents the mouse down event from being propagated elsewhere
|
||||||
let min_size = side.relevant_component(constraint.min);
|
.on_drag(MouseButton::Left, {
|
||||||
let max_size = side.relevant_component(constraint.max);
|
let bounds = bounds.clone();
|
||||||
let on_resize = self.on_resize.clone();
|
let side = self.handle_side;
|
||||||
move |event, view: &mut V, cx| {
|
let prev_size = side.relevant_component(bounds.size());
|
||||||
let new_size = min_size.max(prev_size + side.compute_delta(event)).min(max_size).round();
|
let min_size = side.relevant_component(constraint.min);
|
||||||
if new_size != prev_size {
|
let max_size = side.relevant_component(constraint.max);
|
||||||
on_resize.borrow_mut()(view, new_size, cx);
|
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 {
|
scene.push_cursor_region(crate::CursorRegion {
|
||||||
|
|
|
@ -12,8 +12,8 @@ use gpui::{
|
||||||
geometry::vector::Vector2F,
|
geometry::vector::Vector2F,
|
||||||
keymap_matcher::KeymapContext,
|
keymap_matcher::KeymapContext,
|
||||||
platform::{CursorStyle, MouseButton, PromptLevel},
|
platform::{CursorStyle, MouseButton, PromptLevel},
|
||||||
AnyElement, AppContext, ClipboardItem, Element, Entity, ModelHandle, Task, View, ViewContext,
|
AnyElement, AppContext, Axis, ClipboardItem, Element, Entity, ModelHandle, Task, View,
|
||||||
ViewHandle, WeakViewHandle,
|
ViewContext, ViewHandle, WeakViewHandle,
|
||||||
};
|
};
|
||||||
use menu::{Confirm, SelectNext, SelectPrev};
|
use menu::{Confirm, SelectNext, SelectPrev};
|
||||||
use project::{Entry, EntryKind, Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId};
|
use project::{Entry, EntryKind, Project, ProjectEntryId, ProjectPath, Worktree, WorktreeId};
|
||||||
|
@ -1347,9 +1347,7 @@ impl Entity for ProjectPanel {
|
||||||
impl workspace::dock::Panel for ProjectPanel {
|
impl workspace::dock::Panel for ProjectPanel {
|
||||||
fn position(&self, cx: &gpui::WindowContext) -> DockPosition {
|
fn position(&self, cx: &gpui::WindowContext) -> DockPosition {
|
||||||
let settings = cx.global::<Settings>();
|
let settings = cx.global::<Settings>();
|
||||||
match settings
|
match settings.project_panel.dock {
|
||||||
.project_panel
|
|
||||||
.dock {
|
|
||||||
settings::ProjectPanelDockPosition::Left => DockPosition::Left,
|
settings::ProjectPanelDockPosition::Left => DockPosition::Left,
|
||||||
settings::ProjectPanelDockPosition::Right => DockPosition::Right,
|
settings::ProjectPanelDockPosition::Right => DockPosition::Right,
|
||||||
}
|
}
|
||||||
|
|
|
@ -599,7 +599,10 @@ impl Settings {
|
||||||
}
|
}
|
||||||
self.editor_overrides = data.editor;
|
self.editor_overrides = data.editor;
|
||||||
merge(&mut self.project_panel.dock, data.project_panel.dock);
|
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.git_overrides = data.git.unwrap_or_default();
|
||||||
self.journal_overrides = data.journal;
|
self.journal_overrides = data.journal;
|
||||||
self.terminal_defaults.font_size = data.terminal.font_size;
|
self.terminal_defaults.font_size = data.terminal.font_size;
|
||||||
|
|
|
@ -2,7 +2,8 @@ use crate::{StatusItemView, Workspace};
|
||||||
use context_menu::{ContextMenu, ContextMenuItem};
|
use context_menu::{ContextMenu, ContextMenuItem};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
elements::*, impl_actions, platform::CursorStyle, platform::MouseButton, AnyViewHandle,
|
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 serde::Deserialize;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
|
@ -117,6 +118,13 @@ impl DockPosition {
|
||||||
Self::Right => HandleSide::Left,
|
Self::Right => HandleSide::Left,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn axis(&self) -> Axis {
|
||||||
|
match self {
|
||||||
|
Self::Left | Self::Right => Axis::Horizontal,
|
||||||
|
Self::Bottom => Axis::Vertical,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct PanelEntry {
|
struct PanelEntry {
|
||||||
|
@ -247,6 +255,23 @@ impl Dock {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn panel_size(&self, panel: &dyn PanelHandle) -> Option<f32> {
|
||||||
|
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<f32> {
|
pub fn active_panel_size(&self) -> Option<f32> {
|
||||||
if self.is_open {
|
if self.is_open {
|
||||||
self.panel_entries
|
self.panel_entries
|
||||||
|
@ -281,9 +306,13 @@ impl View for Dock {
|
||||||
ChildView::new(active_panel.as_any(), cx)
|
ChildView::new(active_panel.as_any(), cx)
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(style.container)
|
.with_style(style.container)
|
||||||
.resizable(self.position.to_resize_handle_side(), size, |dock: &mut Self, size, cx| {
|
.resizable(
|
||||||
dock.resize_active_panel(size, cx);
|
self.position.to_resize_handle_side(),
|
||||||
})
|
size,
|
||||||
|
|dock: &mut Self, size, cx| {
|
||||||
|
dock.resize_active_panel(size, cx);
|
||||||
|
},
|
||||||
|
)
|
||||||
.into_any()
|
.into_any()
|
||||||
} else {
|
} else {
|
||||||
Empty::new().into_any()
|
Empty::new().into_any()
|
||||||
|
@ -487,7 +516,10 @@ pub(crate) mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_size(&self, _: &WindowContext) -> f32 {
|
fn default_size(&self, _: &WindowContext) -> f32 {
|
||||||
300.
|
match self.position.axis() {
|
||||||
|
Axis::Horizontal => 300.,
|
||||||
|
Axis::Vertical => 200.,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn icon_path(&self) -> &'static str {
|
fn icon_path(&self) -> &'static str {
|
||||||
|
|
|
@ -852,10 +852,18 @@ impl Workspace {
|
||||||
|
|
||||||
cx.subscribe(&panel, {
|
cx.subscribe(&panel, {
|
||||||
let mut dock = dock.clone();
|
let mut dock = dock.clone();
|
||||||
|
let mut prev_position = panel.position(cx);
|
||||||
move |this, panel, event, cx| {
|
move |this, panel, event, cx| {
|
||||||
if T::should_change_position_on_event(event) {
|
if T::should_change_position_on_event(event) {
|
||||||
|
let new_position = panel.read(cx).position(cx);
|
||||||
let mut was_visible = false;
|
let mut was_visible = false;
|
||||||
|
let mut size = None;
|
||||||
dock.update(cx, |dock, cx| {
|
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()
|
was_visible = dock.is_open()
|
||||||
&& dock
|
&& dock
|
||||||
.active_panel()
|
.active_panel()
|
||||||
|
@ -869,7 +877,11 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
.clone();
|
.clone();
|
||||||
dock.update(cx, |dock, cx| {
|
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 {
|
if was_visible {
|
||||||
dock.set_open(true, cx);
|
dock.set_open(true, cx);
|
||||||
dock.activate_panel(dock.panels_len() - 1, cx);
|
dock.activate_panel(dock.panels_len() - 1, cx);
|
||||||
|
@ -3678,10 +3690,17 @@ mod tests {
|
||||||
.right_dock()
|
.right_dock()
|
||||||
.update(cx, |right_dock, cx| right_dock.set_open(true, cx));
|
.update(cx, |right_dock, cx| right_dock.set_open(true, cx));
|
||||||
|
|
||||||
|
let left_dock = workspace.left_dock();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
workspace.left_dock().read(cx).active_panel().unwrap().id(),
|
left_dock.read(cx).active_panel().unwrap().id(),
|
||||||
panel_1.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!(
|
assert_eq!(
|
||||||
workspace.right_dock().read(cx).active_panel().unwrap().id(),
|
workspace.right_dock().read(cx).active_panel().unwrap().id(),
|
||||||
panel_2.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.
|
// 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).is_open());
|
||||||
assert!(workspace.left_dock().read(cx).active_panel().is_none());
|
assert!(workspace.left_dock().read(cx).active_panel().is_none());
|
||||||
|
let right_dock = workspace.right_dock();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
workspace.right_dock().read(cx).active_panel().unwrap().id(),
|
right_dock.read(cx).active_panel().unwrap().id(),
|
||||||
panel_1.id()
|
panel_1.id()
|
||||||
);
|
);
|
||||||
|
assert_eq!(right_dock.read(cx).active_panel_size().unwrap(), 1337.);
|
||||||
|
|
||||||
// Now we move panel_2 to the left
|
// Now we move panel_2 to the left
|
||||||
panel_2.set_position(DockPosition::Left, cx);
|
panel_2.set_position(DockPosition::Left, cx);
|
||||||
|
@ -3727,13 +3748,33 @@ mod tests {
|
||||||
|
|
||||||
workspace.update(cx, |workspace, cx| {
|
workspace.update(cx, |workspace, cx| {
|
||||||
// Since panel_1 was visible on the right, we open the left dock and make panel_1 active.
|
// 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!(
|
assert_eq!(
|
||||||
workspace.left_dock().read(cx).active_panel().unwrap().id(),
|
left_dock.read(cx).active_panel().unwrap().id(),
|
||||||
panel_1.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.
|
// And right the dock should be closed as it no longer has any panels.
|
||||||
assert!(!workspace.right_dock().read(cx).is_open());
|
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),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue