From 10e947cb5f8948817036d6302c32dd9a0a043d41 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Mon, 22 May 2023 15:55:44 +0200 Subject: [PATCH] Persist project and terminal panel sizes --- Cargo.lock | 1 + crates/project_panel/Cargo.toml | 2 + crates/project_panel/src/project_panel.rs | 77 ++++++++++++++++++++-- crates/terminal/src/terminal.rs | 1 + crates/terminal_view/src/terminal_panel.rs | 36 ++++++++-- crates/workspace/src/dock.rs | 47 +++++++------ crates/workspace/src/workspace.rs | 20 ++---- crates/zed/src/zed.rs | 9 +-- 8 files changed, 139 insertions(+), 54 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 2aea69e90b7cdda61afabba7988d569189b36de2..b8b57e8b8dde611aae3689ae1d30dd0a9ec1567a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4888,6 +4888,7 @@ version = "0.1.0" dependencies = [ "client", "context_menu", + "db", "drag_and_drop", "editor", "futures 0.3.28", diff --git a/crates/project_panel/Cargo.toml b/crates/project_panel/Cargo.toml index 8cd5a06675318ff3f48730cd360ac6deaa599469..aab5b8a41c2d00a5d445576c07cc952c0d020b73 100644 --- a/crates/project_panel/Cargo.toml +++ b/crates/project_panel/Cargo.toml @@ -10,6 +10,7 @@ doctest = false [dependencies] context_menu = { path = "../context_menu" } +db = { path = "../db" } drag_and_drop = { path = "../drag_and_drop" } editor = { path = "../editor" } gpui = { path = "../gpui" } @@ -23,6 +24,7 @@ postage.workspace = true futures.workspace = true schemars.workspace = true serde.workspace = true +serde_json.workspace = true unicase = "2.6" [dev-dependencies] diff --git a/crates/project_panel/src/project_panel.rs b/crates/project_panel/src/project_panel.rs index 761d80308466632a8ea43fc93105ea92d0eda656..010a305f161cc966c43fb88c1970c3dcd06e966b 100644 --- a/crates/project_panel/src/project_panel.rs +++ b/crates/project_panel/src/project_panel.rs @@ -1,10 +1,11 @@ use context_menu::{ContextMenu, ContextMenuItem}; +use db::kvp::KEY_VALUE_STORE; use drag_and_drop::{DragAndDrop, Draggable}; use editor::{Cancel, Editor}; use futures::stream::StreamExt; use gpui::{ actions, - anyhow::{anyhow, Result}, + anyhow::{self, anyhow, Result}, elements::{ AnchorCorner, ChildView, ComponentHost, ContainerStyle, Empty, Flex, MouseEventHandler, ParentElement, ScrollTarget, Stack, Svg, UniformList, UniformListState, @@ -12,8 +13,8 @@ use gpui::{ geometry::vector::Vector2F, keymap_matcher::KeymapContext, platform::{CursorStyle, MouseButton, PromptLevel}, - AnyElement, AppContext, ClipboardItem, Element, Entity, ModelHandle, Task, View, ViewContext, - ViewHandle, WeakViewHandle, WindowContext, + AnyElement, AppContext, AsyncAppContext, ClipboardItem, Element, Entity, ModelHandle, Task, + View, ViewContext, ViewHandle, WeakViewHandle, WindowContext, }; use menu::{Confirm, SelectNext, SelectPrev}; use project::{ @@ -33,11 +34,13 @@ use std::{ }; use theme::{ui::FileName, ProjectPanelEntry}; use unicase::UniCase; +use util::{ResultExt, TryFutureExt}; use workspace::{ dock::{DockPosition, Panel}, Workspace, }; +const PROJECT_PANEL_KEY: &'static str = "ProjectPanel"; const NEW_ENTRY_ID: ProjectEntryId = ProjectEntryId::MAX; #[derive(Deserialize)] @@ -67,6 +70,7 @@ pub struct ProjectPanelSettingsContent { } #[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)] +#[serde(rename_all = "snake_case")] pub enum ProjectPanelDockPosition { Left, Right, @@ -87,6 +91,8 @@ pub struct ProjectPanel { dragged_entry_destination: Option>, workspace: WeakViewHandle, has_focus: bool, + width: Option, + pending_serialization: Task>, } #[derive(Copy, Clone)] @@ -183,8 +189,13 @@ pub enum Event { Focus, } +#[derive(Serialize, Deserialize)] +struct SerializedProjectPanel { + width: Option, +} + impl ProjectPanel { - pub fn new(workspace: &mut Workspace, cx: &mut ViewContext) -> ViewHandle { + fn new(workspace: &mut Workspace, cx: &mut ViewContext) -> ViewHandle { let project = workspace.project().clone(); let project_panel = cx.add_view(|cx: &mut ViewContext| { cx.observe(&project, |this, _, cx| { @@ -258,6 +269,8 @@ impl ProjectPanel { dragged_entry_destination: None, workspace: workspace.weak_handle(), has_focus: false, + width: None, + pending_serialization: Task::ready(None), }; this.update_visible_entries(None, cx); @@ -311,6 +324,51 @@ impl ProjectPanel { project_panel } + pub fn load( + workspace: WeakViewHandle, + cx: AsyncAppContext, + ) -> Task>> { + cx.spawn(|mut cx| async move { + let serialized_panel = if let Some(panel) = cx + .background() + .spawn(async move { KEY_VALUE_STORE.read_kvp(PROJECT_PANEL_KEY) }) + .await + .log_err() + .flatten() + { + Some(serde_json::from_str::(&panel)?) + } else { + None + }; + workspace.update(&mut cx, |workspace, cx| { + let panel = ProjectPanel::new(workspace, cx); + if let Some(serialized_panel) = serialized_panel { + panel.update(cx, |panel, cx| { + panel.width = serialized_panel.width; + cx.notify(); + }); + } + panel + }) + }) + } + + fn serialize(&mut self, cx: &mut ViewContext) { + let width = self.width; + self.pending_serialization = cx.background().spawn( + async move { + KEY_VALUE_STORE + .write_kvp( + PROJECT_PANEL_KEY.into(), + serde_json::to_string(&SerializedProjectPanel { width })?, + ) + .await?; + anyhow::Ok(()) + } + .log_err(), + ); + } + fn deploy_context_menu( &mut self, position: Vector2F, @@ -1435,8 +1493,15 @@ impl workspace::dock::Panel for ProjectPanel { ); } - fn default_size(&self, cx: &WindowContext) -> f32 { - settings::get::(cx).default_width + fn size(&self, cx: &WindowContext) -> f32 { + self.width + .unwrap_or_else(|| settings::get::(cx).default_width) + } + + fn set_size(&mut self, size: f32, cx: &mut ViewContext) { + self.width = Some(size); + self.serialize(cx); + cx.notify(); } fn should_zoom_in_on_event(_: &Self::Event) -> bool { diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 9cf3f529249e6c6725f499ed535ed779694d3fc7..576719526da0104cb92b6e6c05b2f768c042da84 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -120,6 +120,7 @@ pub fn init(cx: &mut AppContext) { } #[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] pub enum TerminalDockPosition { Left, Bottom, diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index afd564fb56d5b4cecb19e43cf22f52b1ff9eb7dd..9c8073eada819f26ca944140025394c93023be3f 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -37,12 +37,14 @@ pub struct TerminalPanel { pane: ViewHandle, fs: Arc, workspace: WeakViewHandle, + width: Option, + height: Option, pending_serialization: Task>, _subscriptions: Vec, } impl TerminalPanel { - pub fn new(workspace: &Workspace, cx: &mut ViewContext) -> Self { + fn new(workspace: &Workspace, cx: &mut ViewContext) -> Self { let weak_self = cx.weak_handle(); let pane = cx.add_view(|cx| { let window_id = cx.window_id(); @@ -90,6 +92,8 @@ impl TerminalPanel { fs: workspace.app_state().fs.clone(), workspace: workspace.weak_handle(), pending_serialization: Task::ready(None), + width: None, + height: None, _subscriptions: subscriptions, }; let mut old_dock_position = this.position(cx); @@ -112,7 +116,9 @@ impl TerminalPanel { let serialized_panel = if let Some(panel) = cx .background() .spawn(async move { KEY_VALUE_STORE.read_kvp(TERMINAL_PANEL_KEY) }) - .await? + .await + .log_err() + .flatten() { Some(serde_json::from_str::(&panel)?) } else { @@ -122,6 +128,9 @@ impl TerminalPanel { let panel = cx.add_view(|cx| TerminalPanel::new(workspace, cx)); let items = if let Some(serialized_panel) = serialized_panel.as_ref() { panel.update(cx, |panel, cx| { + cx.notify(); + panel.height = serialized_panel.height; + panel.width = serialized_panel.width; panel.pane.update(cx, |_, cx| { serialized_panel .items @@ -226,6 +235,8 @@ impl TerminalPanel { .map(|item| item.id()) .collect::>(); let active_item_id = self.pane.read(cx).active_item().map(|item| item.id()); + let height = self.height; + let width = self.width; self.pending_serialization = cx.background().spawn( async move { KEY_VALUE_STORE @@ -234,6 +245,8 @@ impl TerminalPanel { serde_json::to_string(&SerializedTerminalPanel { items, active_item_id, + height, + width, })?, ) .await?; @@ -288,12 +301,23 @@ impl Panel for TerminalPanel { }); } - fn default_size(&self, cx: &WindowContext) -> f32 { + fn size(&self, cx: &WindowContext) -> f32 { let settings = settings::get::(cx); match self.position(cx) { - DockPosition::Left | DockPosition::Right => settings.default_width, - DockPosition::Bottom => settings.default_height, + DockPosition::Left | DockPosition::Right => { + self.width.unwrap_or_else(|| settings.default_width) + } + DockPosition::Bottom => self.height.unwrap_or_else(|| settings.default_height), + } + } + + fn set_size(&mut self, size: f32, cx: &mut ViewContext) { + match self.position(cx) { + DockPosition::Left | DockPosition::Right => self.width = Some(size), + DockPosition::Bottom => self.height = Some(size), } + self.serialize(cx); + cx.notify(); } fn should_zoom_in_on_event(event: &Event) -> bool { @@ -360,4 +384,6 @@ impl Panel for TerminalPanel { struct SerializedTerminalPanel { items: Vec, active_item_id: Option, + width: Option, + height: Option, } diff --git a/crates/workspace/src/dock.rs b/crates/workspace/src/dock.rs index c327c4ded8a7e58f13ca1b436ce0dac3eeff2d03..c4693b340325994147b6da6d2d9ba67946efdbc5 100644 --- a/crates/workspace/src/dock.rs +++ b/crates/workspace/src/dock.rs @@ -13,7 +13,8 @@ pub trait Panel: View { fn position(&self, cx: &WindowContext) -> DockPosition; fn position_is_valid(&self, position: DockPosition) -> bool; fn set_position(&mut self, position: DockPosition, cx: &mut ViewContext); - fn default_size(&self, cx: &WindowContext) -> f32; + fn size(&self, cx: &WindowContext) -> f32; + fn set_size(&mut self, size: f32, cx: &mut ViewContext); fn icon_path(&self) -> &'static str; fn icon_tooltip(&self) -> String; fn icon_label(&self, _: &WindowContext) -> Option { @@ -39,7 +40,8 @@ pub trait PanelHandle { fn is_zoomed(&self, cx: &WindowContext) -> bool; fn set_zoomed(&self, zoomed: bool, cx: &mut WindowContext); fn set_active(&self, active: bool, cx: &mut WindowContext); - fn default_size(&self, cx: &WindowContext) -> f32; + fn size(&self, cx: &WindowContext) -> f32; + fn set_size(&self, size: f32, cx: &mut WindowContext); fn icon_path(&self, cx: &WindowContext) -> &'static str; fn icon_tooltip(&self, cx: &WindowContext) -> String; fn icon_label(&self, cx: &WindowContext) -> Option; @@ -67,8 +69,12 @@ where self.update(cx, |this, cx| this.set_position(position, cx)) } - fn default_size(&self, cx: &WindowContext) -> f32 { - self.read(cx).default_size(cx) + fn size(&self, cx: &WindowContext) -> f32 { + self.read(cx).size(cx) + } + + fn set_size(&self, size: f32, cx: &mut WindowContext) { + self.update(cx, |this, cx| this.set_size(size, cx)) } fn is_zoomed(&self, cx: &WindowContext) -> bool { @@ -151,7 +157,6 @@ impl DockPosition { struct PanelEntry { panel: Rc, - size: f32, context_menu: ViewHandle, _subscriptions: [Subscription; 2], } @@ -271,10 +276,8 @@ impl Dock { ]; let dock_view_id = cx.view_id(); - let size = panel.default_size(cx); self.panel_entries.push(PanelEntry { panel: Rc::new(panel), - size, context_menu: cx.add_view(|cx| { let mut menu = ContextMenu::new(dock_view_id, cx); menu.set_position_mode(OverlayPositionMode::Local); @@ -343,28 +346,18 @@ impl Dock { } } - pub fn panel_size(&self, panel: &dyn PanelHandle) -> Option { + pub fn panel_size(&self, panel: &dyn PanelHandle, cx: &WindowContext) -> Option { self.panel_entries .iter() .find(|entry| entry.panel.id() == panel.id()) - .map(|entry| entry.size) + .map(|entry| entry.panel.size(cx)) } - 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 { + pub fn active_panel_size(&self, cx: &WindowContext) -> Option { if self.is_open { self.panel_entries .get(self.active_panel_index) - .map(|entry| entry.size) + .map(|entry| entry.panel.size(cx)) } else { None } @@ -372,7 +365,7 @@ impl Dock { pub fn resize_active_panel(&mut self, size: f32, cx: &mut ViewContext) { if let Some(entry) = self.panel_entries.get_mut(self.active_panel_index) { - entry.size = size; + entry.panel.set_size(size, cx); cx.notify(); } } @@ -386,7 +379,7 @@ impl Dock { .with_style(style.container) .resizable( self.position.to_resize_handle_side(), - active_entry.size, + active_entry.panel.size(cx), |_, _, _| {}, ) .into_any() @@ -413,7 +406,7 @@ impl View for Dock { .with_style(style.container) .resizable( self.position.to_resize_handle_side(), - active_entry.size, + active_entry.panel.size(cx), |dock: &mut Self, size, cx| dock.resize_active_panel(size, cx), ) .into_any() @@ -630,13 +623,17 @@ pub(crate) mod test { unimplemented!() } - fn default_size(&self, _: &WindowContext) -> f32 { + fn size(&self, _: &WindowContext) -> f32 { match self.position.axis() { Axis::Horizontal => 300., Axis::Vertical => 200., } } + fn set_size(&mut self, _: f32, _: &mut ViewContext) { + unimplemented!() + } + fn icon_path(&self) -> &'static str { "icons/test_panel.svg" } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 78900eb0021f0545bfb06199fd23efcce0dea52b..6c7d61fef9625e9a5030ca0505114d37ba981de3 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -853,11 +853,7 @@ impl Workspace { 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() @@ -874,10 +870,6 @@ impl Workspace { .clone(); dock.update(cx, |dock, 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); @@ -3961,8 +3953,8 @@ mod tests { panel_1.id() ); assert_eq!( - left_dock.read(cx).active_panel_size().unwrap(), - panel_1.default_size(cx) + left_dock.read(cx).active_panel_size(cx).unwrap(), + panel_1.size(cx) ); left_dock.update(cx, |left_dock, cx| left_dock.resize_active_panel(1337., cx)); @@ -3989,7 +3981,7 @@ mod tests { right_dock.read(cx).active_panel().unwrap().id(), panel_1.id() ); - assert_eq!(right_dock.read(cx).active_panel_size().unwrap(), 1337.); + assert_eq!(right_dock.read(cx).active_panel_size(cx).unwrap(), 1337.); // Now we move panel_2 to the left panel_2.set_position(DockPosition::Left, cx); @@ -4019,7 +4011,7 @@ mod tests { left_dock.read(cx).active_panel().unwrap().id(), panel_1.id() ); - assert_eq!(left_dock.read(cx).active_panel_size().unwrap(), 1337.); + assert_eq!(left_dock.read(cx).active_panel_size(cx).unwrap(), 1337.); // And right the dock should be closed as it no longer has any panels. assert!(!workspace.right_dock().read(cx).is_open()); @@ -4034,8 +4026,8 @@ mod tests { // since the panel orientation changed from vertical to horizontal. let bottom_dock = workspace.bottom_dock(); assert_eq!( - bottom_dock.read(cx).active_panel_size().unwrap(), - panel_1.default_size(cx), + bottom_dock.read(cx).active_panel_size(cx).unwrap(), + panel_1.size(cx), ); // Close bottom dock and move panel_1 back to the left. bottom_dock.update(cx, |bottom_dock, cx| bottom_dock.set_open(false, cx)); diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index 0c4b63081c0ebb1fbb9343fa2d9f95fbc83d7c17..be0c566bb136aaf2ba6bc97f921f85f91fa8abef 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -335,8 +335,12 @@ pub fn initialize_workspace( } false }); + })?; - let project_panel = ProjectPanel::new(workspace, cx); + let project_panel = ProjectPanel::load(workspace_handle.clone(), cx.clone()); + let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone()); + let (project_panel, terminal_panel) = futures::try_join!(project_panel, terminal_panel)?; + workspace_handle.update(&mut cx, |workspace, cx| { let project_panel_position = project_panel.position(cx); workspace.add_panel(project_panel, cx); if !was_deserialized @@ -352,10 +356,7 @@ pub fn initialize_workspace( { workspace.toggle_dock(project_panel_position, cx); } - })?; - let terminal_panel = TerminalPanel::load(workspace_handle.clone(), cx.clone()).await?; - workspace_handle.update(&mut cx, |workspace, cx| { workspace.add_panel(terminal_panel, cx) })?; Ok(())