WIP: Add persistence to new docks

Mikayla Maki created

Change summary

crates/gpui/src/platform.rs               |  8 +-
crates/sqlez/src/bindable.rs              |  2 
crates/sqlez/src/statement.rs             |  6 +-
crates/sqlez/src/typed_statements.rs      |  6 +-
crates/workspace/src/persistence.rs       | 52 ++++++++++++++++--
crates/workspace/src/persistence/model.rs | 65 ++++++++++++++++++++++-
crates/workspace/src/workspace.rs         | 68 ++++++++++++++++++++++--
7 files changed, 181 insertions(+), 26 deletions(-)

Detailed changes

crates/gpui/src/platform.rs 🔗

@@ -222,21 +222,21 @@ impl Bind for WindowBounds {
     fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
         let (region, next_index) = match self {
             WindowBounds::Fullscreen => {
-                let next_index = statement.bind("Fullscreen", start_index)?;
+                let next_index = statement.bind(&"Fullscreen", start_index)?;
                 (None, next_index)
             }
             WindowBounds::Maximized => {
-                let next_index = statement.bind("Maximized", start_index)?;
+                let next_index = statement.bind(&"Maximized", start_index)?;
                 (None, next_index)
             }
             WindowBounds::Fixed(region) => {
-                let next_index = statement.bind("Fixed", start_index)?;
+                let next_index = statement.bind(&"Fixed", start_index)?;
                 (Some(*region), next_index)
             }
         };
 
         statement.bind(
-            region.map(|region| {
+            &region.map(|region| {
                 (
                     region.min_x(),
                     region.min_y(),

crates/sqlez/src/bindable.rs 🔗

@@ -27,7 +27,7 @@ impl StaticColumnCount for bool {}
 impl Bind for bool {
     fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
         statement
-            .bind(self.then_some(1).unwrap_or(0), start_index)
+            .bind(&self.then_some(1).unwrap_or(0), start_index)
             .with_context(|| format!("Failed to bind bool at index {start_index}"))
     }
 }

crates/sqlez/src/statement.rs 🔗

@@ -236,7 +236,7 @@ impl<'a> Statement<'a> {
         Ok(str::from_utf8(slice)?)
     }
 
-    pub fn bind<T: Bind>(&self, value: T, index: i32) -> Result<i32> {
+    pub fn bind<T: Bind>(&self, value: &T, index: i32) -> Result<i32> {
         debug_assert!(index > 0);
         Ok(value.bind(self, index)?)
     }
@@ -258,7 +258,7 @@ impl<'a> Statement<'a> {
         }
     }
 
-    pub fn with_bindings(&mut self, bindings: impl Bind) -> Result<&mut Self> {
+    pub fn with_bindings(&mut self, bindings: &impl Bind) -> Result<&mut Self> {
         self.bind(bindings, 1)?;
         Ok(self)
     }
@@ -464,7 +464,7 @@ mod test {
         connection
             .exec(indoc! {"
                 CREATE TABLE texts (
-                    text TEXT 
+                    text TEXT
                 )"})
             .unwrap()()
         .unwrap();

crates/sqlez/src/typed_statements.rs 🔗

@@ -29,7 +29,7 @@ impl Connection {
         query: &str,
     ) -> Result<impl 'a + FnMut(B) -> Result<()>> {
         let mut statement = Statement::prepare(self, query)?;
-        Ok(move |bindings| statement.with_bindings(bindings)?.exec())
+        Ok(move |bindings| statement.with_bindings(&bindings)?.exec())
     }
 
     /// Prepare a statement which has no bindings and returns a `Vec<C>`.
@@ -55,7 +55,7 @@ impl Connection {
         query: &str,
     ) -> Result<impl 'a + FnMut(B) -> Result<Vec<C>>> {
         let mut statement = Statement::prepare(self, query)?;
-        Ok(move |bindings| statement.with_bindings(bindings)?.rows::<C>())
+        Ok(move |bindings| statement.with_bindings(&bindings)?.rows::<C>())
     }
 
     /// Prepare a statement that selects a single row from the database.
@@ -87,7 +87,7 @@ impl Connection {
         let mut statement = Statement::prepare(self, query)?;
         Ok(move |bindings| {
             statement
-                .with_bindings(bindings)
+                .with_bindings(&bindings)
                 .context("Bindings failed")?
                 .maybe_row::<C>()
                 .context("Maybe row failed")

crates/workspace/src/persistence.rs 🔗

@@ -18,6 +18,9 @@ use model::{
     WorkspaceLocation,
 };
 
+use self::model::DockStructure;
+
+
 define_connection! {
     // Current schema shape using pseudo-rust syntax:
     //
@@ -151,6 +154,15 @@ define_connection! {
         INSERT INTO workspaces_2 SELECT * FROM workspaces;
         DROP TABLE workspaces;
         ALTER TABLE workspaces_2 RENAME TO workspaces;
+    ),
+    // Add panels related information
+    sql!(
+        ALTER TABLE workspaces ADD COLUMN left_dock_visible INTEGER; //bool
+        ALTER TABLE workspaces ADD COLUMN left_dock_size REAL;
+        ALTER TABLE workspaces ADD COLUMN right_dock_visible INTEGER; //bool
+        ALTER TABLE workspaces ADD COLUMN right_dock_size REAL;
+        ALTER TABLE workspaces ADD COLUMN bottom_dock_visible INTEGER; //bool
+        ALTER TABLE workspaces ADD COLUMN bottom_dock_size REAL;
     )];
 }
 
@@ -166,12 +178,13 @@ impl WorkspaceDb {
 
         // Note that we re-assign the workspace_id here in case it's empty
         // and we've grabbed the most recent workspace
-        let (workspace_id, workspace_location, left_sidebar_open, bounds, display): (
+        let (workspace_id, workspace_location, left_sidebar_open, bounds, display, docks): (
             WorkspaceId,
             WorkspaceLocation,
             bool,
             Option<WindowBounds>,
             Option<Uuid>,
+            DockStructure
         ) = self
             .select_row_bound(sql! {
                 SELECT
@@ -183,7 +196,13 @@ impl WorkspaceDb {
                     window_y,
                     window_width,
                     window_height,
-                    display
+                    display,
+                    left_dock_visible,
+                    left_dock_size,
+                    right_dock_visible,
+                    right_dock_size,
+                    bottom_dock_visible,
+                    bottom_dock_size
                 FROM workspaces
                 WHERE workspace_location = ?
             })
@@ -202,6 +221,7 @@ impl WorkspaceDb {
             left_sidebar_open,
             bounds,
             display,
+            docks
         })
     }
 
@@ -227,15 +247,27 @@ impl WorkspaceDb {
                         workspace_id,
                         workspace_location,
                         left_sidebar_open,
+                        left_dock_visible,
+                        left_dock_size,
+                        right_dock_visible,
+                        right_dock_size,
+                        bottom_dock_visible,
+                        bottom_dock_size,
                         timestamp
                     )
-                    VALUES (?1, ?2, ?3, CURRENT_TIMESTAMP)
+                    VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, CURRENT_TIMESTAMP)
                     ON CONFLICT DO
                     UPDATE SET
                         workspace_location = ?2,
                         left_sidebar_open = ?3,
+                        left_dock_visible = ?4,
+                        left_dock_size = ?5,
+                        right_dock_visible = ?6,
+                        right_dock_size = ?7,
+                        bottom_dock_visible = ?8,
+                        bottom_dock_size = ?9,
                         timestamp = CURRENT_TIMESTAMP
-                ))?((workspace.id, &workspace.location))
+                ))?((workspace.id, &workspace.location, workspace.left_sidebar_open, workspace.docks))
                 .context("Updating workspace")?;
 
                 // Save center pane group
@@ -549,19 +581,22 @@ mod tests {
         let mut workspace_1 = SerializedWorkspace {
             id: 1,
             location: (["/tmp", "/tmp2"]).into(),
-            center_group: Default::default(),
             left_sidebar_open: true,
+            center_group: Default::default(),
             bounds: Default::default(),
             display: Default::default(),
+            docks: Default::default()
         };
 
         let mut _workspace_2 = SerializedWorkspace {
             id: 2,
             location: (["/tmp"]).into(),
-            center_group: Default::default(),
             left_sidebar_open: false,
+            center_group: Default::default(),
             bounds: Default::default(),
             display: Default::default(),
+            docks: Default::default()
+
         };
 
         db.save_workspace(workspace_1.clone()).await;
@@ -659,6 +694,7 @@ mod tests {
             left_sidebar_open: true,
             bounds: Default::default(),
             display: Default::default(),
+            docks: Default::default()
         };
 
         db.save_workspace(workspace.clone()).await;
@@ -687,6 +723,7 @@ mod tests {
             left_sidebar_open: true,
             bounds: Default::default(),
             display: Default::default(),
+            docks: Default::default()
         };
 
         let mut workspace_2 = SerializedWorkspace {
@@ -696,6 +733,7 @@ mod tests {
             left_sidebar_open: false,
             bounds: Default::default(),
             display: Default::default(),
+            docks: Default::default()
         };
 
         db.save_workspace(workspace_1.clone()).await;
@@ -732,6 +770,7 @@ mod tests {
             left_sidebar_open: false,
             bounds: Default::default(),
             display: Default::default(),
+            docks: Default::default()
         };
 
         db.save_workspace(workspace_3.clone()).await;
@@ -765,6 +804,7 @@ mod tests {
             left_sidebar_open: true,
             bounds: Default::default(),
             display: Default::default(),
+            docks: Default::default()
         }
     }
 

crates/workspace/src/persistence/model.rs 🔗

@@ -63,6 +63,65 @@ pub struct SerializedWorkspace {
     pub left_sidebar_open: bool,
     pub bounds: Option<WindowBounds>,
     pub display: Option<Uuid>,
+    pub docks: DockStructure,
+}
+
+#[derive(Debug, PartialEq, Clone, Default)]
+pub struct DockStructure {
+    pub(crate) left: DockData,
+    pub(crate) right: DockData,
+    pub(crate) bottom: DockData,
+}
+
+impl Column for DockStructure {
+    fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
+        let (left, next_index) = DockData::column(statement, start_index)?;
+        let (right, next_index) = DockData::column(statement, next_index)?;
+        let (bottom, next_index) = DockData::column(statement, next_index)?;
+        Ok((
+            DockStructure {
+                left,
+                right,
+                bottom,
+            },
+            next_index,
+        ))
+    }
+}
+
+impl Bind for DockStructure {
+    fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
+        let next_index = statement.bind(&self.left, start_index)?;
+        let next_index = statement.bind(&self.right, next_index)?;
+        statement.bind(&self.bottom, next_index)
+    }
+}
+
+#[derive(Debug, PartialEq, Clone, Default)]
+pub struct DockData {
+    pub(crate) visible: bool,
+    pub(crate) size: Option<f32>,
+}
+
+impl Column for DockData {
+    fn column(statement: &mut Statement, start_index: i32) -> Result<(Self, i32)> {
+        let (visible, next_index) = Option::<bool>::column(statement, start_index)?;
+        let (size, next_index) = Option::<f32>::column(statement, next_index)?;
+        Ok((
+            DockData {
+                visible: visible.unwrap_or(false),
+                size,
+            },
+            next_index,
+        ))
+    }
+}
+
+impl Bind for DockData {
+    fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
+        let next_index = statement.bind(&self.visible, start_index)?;
+        statement.bind(&self.size, next_index)
+    }
 }
 
 #[derive(Debug, PartialEq, Eq, Clone)]
@@ -251,9 +310,9 @@ impl StaticColumnCount for SerializedItem {
 }
 impl Bind for &SerializedItem {
     fn bind(&self, statement: &Statement, start_index: i32) -> Result<i32> {
-        let next_index = statement.bind(self.kind.clone(), start_index)?;
-        let next_index = statement.bind(self.item_id, next_index)?;
-        statement.bind(self.active, next_index)
+        let next_index = statement.bind(&self.kind, start_index)?;
+        let next_index = statement.bind(&self.item_id, next_index)?;
+        statement.bind(&self.active, next_index)
     }
 }
 

crates/workspace/src/workspace.rs 🔗

@@ -58,7 +58,9 @@ use std::{
 
 use crate::{
     notifications::simple_message_notification::MessageNotification,
-    persistence::model::{SerializedPane, SerializedPaneGroup, SerializedWorkspace},
+    persistence::model::{
+        DockData, DockStructure, SerializedPane, SerializedPaneGroup, SerializedWorkspace,
+    },
 };
 use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle, TogglePanel};
 use lazy_static::lazy_static;
@@ -2575,12 +2577,51 @@ impl Workspace {
             }
         }
 
+        fn build_serialized_docks(this: &Workspace, cx: &AppContext) -> DockStructure {
+            let left_dock = this.left_dock.read(cx);
+            let left_visible = left_dock.is_open();
+            let left_size = left_dock
+                .active_panel()
+                .map(|panel| left_dock.panel_size(panel.as_ref()))
+                .flatten();
+
+            let right_dock = this.right_dock.read(cx);
+            let right_visible = right_dock.is_open();
+            let right_size = right_dock
+                .active_panel()
+                .map(|panel| right_dock.panel_size(panel.as_ref()))
+                .flatten();
+
+            let bottom_dock = this.bottom_dock.read(cx);
+            let bottom_visible = bottom_dock.is_open();
+            let bottom_size = bottom_dock
+                .active_panel()
+                .map(|panel| bottom_dock.panel_size(panel.as_ref()))
+                .flatten();
+
+            DockStructure {
+                left: DockData {
+                    visible: left_visible,
+                    size: left_size,
+                },
+                right: DockData {
+                    visible: right_visible,
+                    size: right_size,
+                },
+                bottom: DockData {
+                    visible: bottom_visible,
+                    size: bottom_size,
+                },
+            }
+        }
+
         if let Some(location) = self.location(cx) {
             // Load bearing special case:
             //  - with_local_workspace() relies on this to not have other stuff open
             //    when you open your log
             if !location.paths().is_empty() {
                 let center_group = build_serialized_pane_group(&self.center.root, cx);
+                let docks = build_serialized_docks(self, cx);
 
                 let serialized_workspace = SerializedWorkspace {
                     id: self.database_id,
@@ -2589,6 +2630,7 @@ impl Workspace {
                     left_sidebar_open: self.left_dock.read(cx).is_open(),
                     bounds: Default::default(),
                     display: Default::default(),
+                    docks,
                 };
 
                 cx.background()
@@ -2642,11 +2684,25 @@ impl Workspace {
                     }
                 }
 
-                if workspace.left_dock().read(cx).is_open()
-                    != serialized_workspace.left_sidebar_open
-                {
-                    workspace.toggle_dock(DockPosition::Left, cx);
-                }
+                let docks = serialized_workspace.docks;
+                workspace.left_dock.update(cx, |dock, cx| {
+                    dock.set_open(docks.left.visible, cx);
+                    if let Some(size) = docks.left.size {
+                        dock.resize_active_panel(size, cx);
+                    }
+                });
+                workspace.right_dock.update(cx, |dock, cx| {
+                    dock.set_open(docks.right.visible, cx);
+                    if let Some(size) = docks.right.size {
+                        dock.resize_active_panel(size, cx);
+                    }
+                });
+                workspace.bottom_dock.update(cx, |dock, cx| {
+                    dock.set_open(docks.bottom.visible, cx);
+                    if let Some(size) = docks.bottom.size {
+                        dock.resize_active_panel(size, cx);
+                    }
+                });
 
                 cx.notify();
             })?;