From 5797282b981ddc8b527f03ef70793d1b6a534457 Mon Sep 17 00:00:00 2001 From: Mikayla Maki Date: Thu, 13 Jul 2023 14:21:14 -0700 Subject: [PATCH] Add resising serialization --- crates/workspace/src/pane_group.rs | 67 ++++++---- crates/workspace/src/persistence.rs | 152 ++++++++++++++-------- crates/workspace/src/persistence/model.rs | 9 +- crates/workspace/src/workspace.rs | 16 ++- 4 files changed, 159 insertions(+), 85 deletions(-) diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 3e4ce21694..e58b95d6b3 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -308,7 +308,7 @@ impl Member { pub(crate) struct PaneAxis { pub axis: Axis, pub members: Vec, - flexes: Rc>>, + pub flexes: Rc>>, } impl PaneAxis { @@ -321,6 +321,18 @@ impl PaneAxis { } } + pub fn load(axis: Axis, members: Vec, flexes: Option>) -> Self { + let flexes = flexes.unwrap_or_else(|| vec![1.; members.len()]); + debug_assert!(members.len() == flexes.len()); + + let flexes = Rc::new(RefCell::new(flexes)); + Self { + axis, + members, + flexes, + } + } + fn split( &mut self, old_pane: &ViewHandle, @@ -519,23 +531,23 @@ mod element { json::{self, ToJson}, platform::{CursorStyle, MouseButton}, AnyElement, Axis, CursorRegion, Element, LayoutContext, MouseRegion, RectFExt, - SceneBuilder, SizeConstraint, Vector2FExt, View, ViewContext, + SceneBuilder, SizeConstraint, Vector2FExt, ViewContext, }; use crate::{ pane_group::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE}, - WorkspaceSettings, + WorkspaceSettings, Workspace, }; - pub struct PaneAxisElement { + pub struct PaneAxisElement { axis: Axis, basis: usize, active_pane_ix: Option, flexes: Rc>>, - children: Vec>, + children: Vec>, } - impl PaneAxisElement { + impl PaneAxisElement { pub fn new(axis: Axis, basis: usize, flexes: Rc>>) -> Self { Self { axis, @@ -557,8 +569,8 @@ mod element { remaining_space: &mut f32, remaining_flex: &mut f32, cross_axis_max: &mut f32, - view: &mut V, - cx: &mut LayoutContext, + view: &mut Workspace, + cx: &mut LayoutContext, ) { let flexes = self.flexes.borrow(); let cross_axis = self.axis.invert(); @@ -602,21 +614,21 @@ mod element { } } - impl Extend> for PaneAxisElement { - fn extend>>(&mut self, children: T) { + impl Extend> for PaneAxisElement { + fn extend>>(&mut self, children: T) { self.children.extend(children); } } - impl Element for PaneAxisElement { + impl Element for PaneAxisElement { type LayoutState = f32; type PaintState = (); fn layout( &mut self, constraint: SizeConstraint, - view: &mut V, - cx: &mut LayoutContext, + view: &mut Workspace, + cx: &mut LayoutContext, ) -> (Vector2F, Self::LayoutState) { debug_assert!(self.children.len() == self.flexes.borrow().len()); @@ -682,8 +694,8 @@ mod element { bounds: RectF, visible_bounds: RectF, remaining_space: &mut Self::LayoutState, - view: &mut V, - cx: &mut ViewContext, + view: &mut Workspace, + cx: &mut ViewContext, ) -> Self::PaintState { let can_resize = settings::get::(cx).active_pane_magnification == 1.; let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default(); @@ -750,7 +762,7 @@ mod element { handle_bounds, ); mouse_region = - mouse_region.on_drag(MouseButton::Left, move |drag, _: &mut V, cx| { + mouse_region.on_drag(MouseButton::Left, move |drag, workspace: &mut Workspace, cx| { let min_size = match axis { Axis::Horizontal => HORIZONTAL_MIN_SIZE, Axis::Vertical => VERTICAL_MIN_SIZE, @@ -768,13 +780,15 @@ mod element { current_target_size - child_size.along(axis); if proposed_current_pixel_change < 0. { - current_target_size = current_target_size.max(min_size); + current_target_size = f32::max(current_target_size, min_size); } else if proposed_current_pixel_change > 0. { - // TODO: cascade this size change down, collect all changes into a vec - let next_target_size = (next_child_size.along(axis) - - proposed_current_pixel_change) - .max(min_size); - current_target_size = current_target_size.min( + // TODO: cascade this change to other children if current item is at min size + let next_target_size = f32::max( + next_child_size.along(axis) - proposed_current_pixel_change, + min_size, + ); + current_target_size = f32::min( + current_target_size, child_size.along(axis) + next_child_size.along(axis) - next_target_size, ); @@ -789,6 +803,7 @@ mod element { *borrow.get_mut(ix).unwrap() = current_target_flex; *borrow.get_mut(next_ix).unwrap() = next_target_flex; + workspace.schedule_serialize(cx); cx.notify(); }); scene.push_mouse_region(mouse_region); @@ -809,8 +824,8 @@ mod element { _: RectF, _: &Self::LayoutState, _: &Self::PaintState, - view: &V, - cx: &ViewContext, + view: &Workspace, + cx: &ViewContext, ) -> Option { self.children .iter() @@ -822,8 +837,8 @@ mod element { bounds: RectF, _: &Self::LayoutState, _: &Self::PaintState, - view: &V, - cx: &ViewContext, + view: &Workspace, + cx: &ViewContext, ) -> json::Value { serde_json::json!({ "type": "PaneAxis", diff --git a/crates/workspace/src/persistence.rs b/crates/workspace/src/persistence.rs index dd2aa5a818..0d7784093a 100644 --- a/crates/workspace/src/persistence.rs +++ b/crates/workspace/src/persistence.rs @@ -45,6 +45,7 @@ define_connection! { // parent_group_id: Option, // None indicates that this is the root node // position: Optiopn, // None indicates that this is the root node // axis: Option, // 'Vertical', 'Horizontal' + // flexes: Option>, // A JSON array of floats // ) // // panes( @@ -168,7 +169,12 @@ define_connection! { ALTER TABLE workspaces ADD COLUMN left_dock_zoom INTEGER; //bool ALTER TABLE workspaces ADD COLUMN right_dock_zoom INTEGER; //bool ALTER TABLE workspaces ADD COLUMN bottom_dock_zoom INTEGER; //bool - )]; + ), + // Add pane group flex data + sql!( + ALTER TABLE pane_groups ADD COLUMN flexes TEXT; + ) + ]; } impl WorkspaceDb { @@ -359,38 +365,51 @@ impl WorkspaceDb { group_id: Option, ) -> Result> { type GroupKey = (Option, WorkspaceId); - type GroupOrPane = (Option, Option, Option, Option); + type GroupOrPane = ( + Option, + Option, + Option, + Option, + Option, + ); self.select_bound::(sql!( - SELECT group_id, axis, pane_id, active + SELECT group_id, axis, pane_id, active, flexes FROM (SELECT - group_id, - axis, - NULL as pane_id, - NULL as active, - position, - parent_group_id, - workspace_id - FROM pane_groups + group_id, + axis, + NULL as pane_id, + NULL as active, + position, + parent_group_id, + workspace_id, + flexes + FROM pane_groups UNION - SELECT - NULL, - NULL, - center_panes.pane_id, - panes.active as active, - position, - parent_group_id, - panes.workspace_id as workspace_id - FROM center_panes - JOIN panes ON center_panes.pane_id = panes.pane_id) + SELECT + NULL, + NULL, + center_panes.pane_id, + panes.active as active, + position, + parent_group_id, + panes.workspace_id as workspace_id, + NULL + FROM center_panes + JOIN panes ON center_panes.pane_id = panes.pane_id) WHERE parent_group_id IS ? AND workspace_id = ? ORDER BY position ))?((group_id, workspace_id))? .into_iter() - .map(|(group_id, axis, pane_id, active)| { + .map(|(group_id, axis, pane_id, active, flexes)| { if let Some((group_id, axis)) = group_id.zip(axis) { + let flexes = flexes + .map(|flexes| serde_json::from_str::>(&flexes)) + .transpose()?; + Ok(SerializedPaneGroup::Group { axis, children: self.get_pane_group(workspace_id, Some(group_id))?, + flexes, }) } else if let Some((pane_id, active)) = pane_id.zip(active) { Ok(SerializedPaneGroup::Pane(SerializedPane::new( @@ -417,14 +436,31 @@ impl WorkspaceDb { parent: Option<(GroupId, usize)>, ) -> Result<()> { match pane_group { - SerializedPaneGroup::Group { axis, children } => { + SerializedPaneGroup::Group { + axis, + children, + flexes, + } => { let (parent_id, position) = unzip_option(parent); + let flex_string = serde_json::json!(flexes).to_string(); let group_id = conn.select_row_bound::<_, i64>(sql!( - INSERT INTO pane_groups(workspace_id, parent_group_id, position, axis) - VALUES (?, ?, ?, ?) + INSERT INTO pane_groups( + workspace_id, + parent_group_id, + position, + axis, + flexes + ) + VALUES (?, ?, ?, ?, ?) RETURNING group_id - ))?((workspace_id, parent_id, position, *axis))? + ))?(( + workspace_id, + parent_id, + position, + *axis, + flex_string, + ))? .ok_or_else(|| anyhow!("Couldn't retrieve group_id from inserted pane_group"))?; for (position, group) in children.iter().enumerate() { @@ -641,6 +677,14 @@ mod tests { assert_eq!(test_text_1, "test-text-1"); } + fn group(axis: gpui::Axis, children: Vec) -> SerializedPaneGroup { + SerializedPaneGroup::Group { + axis, + flexes: None, + children, + } + } + #[gpui::test] async fn test_full_workspace_serialization() { env_logger::try_init().ok(); @@ -652,12 +696,12 @@ mod tests { // | - - - | | // | 3,4 | | // ----------------- - let center_group = SerializedPaneGroup::Group { - axis: gpui::Axis::Horizontal, - children: vec![ - SerializedPaneGroup::Group { - axis: gpui::Axis::Vertical, - children: vec![ + let center_group = group( + gpui::Axis::Horizontal, + vec![ + group( + gpui::Axis::Vertical, + vec![ SerializedPaneGroup::Pane(SerializedPane::new( vec![ SerializedItem::new("Terminal", 5, false), @@ -673,7 +717,7 @@ mod tests { false, )), ], - }, + ), SerializedPaneGroup::Pane(SerializedPane::new( vec![ SerializedItem::new("Terminal", 9, false), @@ -682,7 +726,7 @@ mod tests { false, )), ], - }; + ); let workspace = SerializedWorkspace { id: 5, @@ -811,12 +855,12 @@ mod tests { // | - - - | | // | 3,4 | | // ----------------- - let center_pane = SerializedPaneGroup::Group { - axis: gpui::Axis::Horizontal, - children: vec![ - SerializedPaneGroup::Group { - axis: gpui::Axis::Vertical, - children: vec![ + let center_pane = group( + gpui::Axis::Horizontal, + vec![ + group( + gpui::Axis::Vertical, + vec![ SerializedPaneGroup::Pane(SerializedPane::new( vec![ SerializedItem::new("Terminal", 1, false), @@ -832,7 +876,7 @@ mod tests { true, )), ], - }, + ), SerializedPaneGroup::Pane(SerializedPane::new( vec![ SerializedItem::new("Terminal", 5, true), @@ -841,7 +885,7 @@ mod tests { false, )), ], - }; + ); let workspace = default_workspace(&["/tmp"], ¢er_pane); @@ -858,12 +902,12 @@ mod tests { let db = WorkspaceDb(open_test_db("test_cleanup_panes").await); - let center_pane = SerializedPaneGroup::Group { - axis: gpui::Axis::Horizontal, - children: vec![ - SerializedPaneGroup::Group { - axis: gpui::Axis::Vertical, - children: vec![ + let center_pane = group( + gpui::Axis::Horizontal, + vec![ + group( + gpui::Axis::Vertical, + vec![ SerializedPaneGroup::Pane(SerializedPane::new( vec![ SerializedItem::new("Terminal", 1, false), @@ -879,7 +923,7 @@ mod tests { true, )), ], - }, + ), SerializedPaneGroup::Pane(SerializedPane::new( vec![ SerializedItem::new("Terminal", 5, false), @@ -888,7 +932,7 @@ mod tests { false, )), ], - }; + ); let id = &["/tmp"]; @@ -896,9 +940,9 @@ mod tests { db.save_workspace(workspace.clone()).await; - workspace.center_group = SerializedPaneGroup::Group { - axis: gpui::Axis::Vertical, - children: vec![ + workspace.center_group = group( + gpui::Axis::Vertical, + vec![ SerializedPaneGroup::Pane(SerializedPane::new( vec![ SerializedItem::new("Terminal", 1, false), @@ -914,7 +958,7 @@ mod tests { true, )), ], - }; + ); db.save_workspace(workspace.clone()).await; diff --git a/crates/workspace/src/persistence/model.rs b/crates/workspace/src/persistence/model.rs index 762d7171de..aa184dbb56 100644 --- a/crates/workspace/src/persistence/model.rs +++ b/crates/workspace/src/persistence/model.rs @@ -127,10 +127,11 @@ impl Bind for DockData { } } -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, PartialEq, Clone)] pub enum SerializedPaneGroup { Group { axis: Axis, + flexes: Option>, children: Vec, }, Pane(SerializedPane), @@ -149,7 +150,7 @@ impl Default for SerializedPaneGroup { impl SerializedPaneGroup { #[async_recursion(?Send)] pub(crate) async fn deserialize( - &self, + self, project: &ModelHandle, workspace_id: WorkspaceId, workspace: &WeakViewHandle, @@ -160,7 +161,7 @@ impl SerializedPaneGroup { Vec>>, )> { match self { - SerializedPaneGroup::Group { axis, children } => { + SerializedPaneGroup::Group { axis, children, flexes } => { let mut current_active_pane = None; let mut members = Vec::new(); let mut items = Vec::new(); @@ -184,7 +185,7 @@ impl SerializedPaneGroup { } Some(( - Member::Axis(PaneAxis::new(*axis, members)), + Member::Axis(PaneAxis::load(axis, members, flexes)), current_active_pane, items, )) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index f91204c51a..e31e0d924f 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -504,6 +504,7 @@ pub struct Workspace { subscriptions: Vec, _apply_leader_updates: Task>, _observe_current_user: Task>, + _schedule_serialize: Option>, pane_history_timestamp: Arc, } @@ -718,6 +719,7 @@ impl Workspace { app_state, _observe_current_user, _apply_leader_updates, + _schedule_serialize: None, leader_updates_tx, subscriptions, pane_history_timestamp, @@ -2893,6 +2895,13 @@ impl Workspace { cx.notify(); } + fn schedule_serialize(&mut self, cx: &mut ViewContext) { + self._schedule_serialize = Some(cx.spawn(|this, cx| async move { + cx.background().timer(Duration::from_millis(100)).await; + this.read_with(&cx, |this, cx| this.serialize_workspace(cx)).ok(); + })); + } + fn serialize_workspace(&self, cx: &ViewContext) { fn serialize_pane_handle( pane_handle: &ViewHandle, @@ -2923,12 +2932,17 @@ impl Workspace { cx: &AppContext, ) -> SerializedPaneGroup { match pane_group { - Member::Axis(PaneAxis { axis, members, .. }) => SerializedPaneGroup::Group { + Member::Axis(PaneAxis { + axis, + members, + flexes, + }) => SerializedPaneGroup::Group { axis: *axis, children: members .iter() .map(|member| build_serialized_pane_group(member, cx)) .collect::>(), + flexes: Some(flexes.borrow().clone()), }, Member::Pane(pane_handle) => { SerializedPaneGroup::Pane(serialize_pane_handle(&pane_handle, cx))