diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index f0bae9533b..074a96dfc1 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -1182,8 +1182,10 @@ impl EditorElement { }); scene.push_mouse_region( MouseRegion::new::(cx.view_id(), cx.view_id(), track_bounds) - .on_move(move |_, editor: &mut Editor, cx| { - editor.scroll_manager.show_scrollbar(cx); + .on_move(move |event, editor: &mut Editor, cx| { + if event.pressed_button.is_none() { + editor.scroll_manager.show_scrollbar(cx); + } }) .on_down(MouseButton::Left, { let row_range = row_range.clone(); diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs index 58d7bb4c40..49b12d823e 100644 --- a/crates/gpui/src/app/window.rs +++ b/crates/gpui/src/app/window.rs @@ -1268,6 +1268,19 @@ impl Vector2FExt for Vector2F { } } +pub trait RectFExt { + fn length_along(self, axis: Axis) -> f32; +} + +impl RectFExt for RectF { + fn length_along(self, axis: Axis) -> f32 { + match axis { + Axis::Horizontal => self.width(), + Axis::Vertical => self.height(), + } + } +} + #[derive(Copy, Clone, Debug)] pub struct SizeConstraint { pub min: Vector2F, diff --git a/crates/gpui/src/gpui.rs b/crates/gpui/src/gpui.rs index 3442934b3a..c79c793dda 100644 --- a/crates/gpui/src/gpui.rs +++ b/crates/gpui/src/gpui.rs @@ -27,7 +27,7 @@ pub mod json; pub mod keymap_matcher; pub mod platform; pub use gpui_macros::{test, Element}; -pub use window::{Axis, SizeConstraint, Vector2FExt, WindowContext}; +pub use window::{Axis, RectFExt, SizeConstraint, Vector2FExt, WindowContext}; pub use anyhow; pub use serde_json; diff --git a/crates/workspace/src/pane_group.rs b/crates/workspace/src/pane_group.rs index 5e5a5a98ba..2ece5030f3 100644 --- a/crates/workspace/src/pane_group.rs +++ b/crates/workspace/src/pane_group.rs @@ -1,6 +1,8 @@ -use std::sync::Arc; +use std::{cell::RefCell, rc::Rc, sync::Arc}; -use crate::{AppState, FollowerStatesByLeader, Pane, Workspace, WorkspaceSettings}; +use crate::{ + pane_group::element::PaneAxisElement, AppState, FollowerStatesByLeader, Pane, Workspace, +}; use anyhow::{anyhow, Result}; use call::{ActiveCall, ParticipantLocation}; use gpui::{ @@ -13,7 +15,11 @@ use project::Project; use serde::Deserialize; use theme::Theme; -#[derive(Clone, Debug, Eq, PartialEq)] +const HANDLE_HITBOX_SIZE: f32 = 4.0; +const HORIZONTAL_MIN_SIZE: f32 = 80.; +const VERTICAL_MIN_SIZE: f32 = 100.; + +#[derive(Clone, Debug, PartialEq)] pub struct PaneGroup { pub(crate) root: Member, } @@ -77,6 +83,7 @@ impl PaneGroup { ) -> AnyElement { self.root.render( project, + 0, theme, follower_states, active_call, @@ -94,7 +101,7 @@ impl PaneGroup { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub(crate) enum Member { Axis(PaneAxis), Pane(ViewHandle), @@ -119,7 +126,7 @@ impl Member { Down | Right => vec![Member::Pane(old_pane), Member::Pane(new_pane)], }; - Member::Axis(PaneAxis { axis, members }) + Member::Axis(PaneAxis::new(axis, members)) } fn contains(&self, needle: &ViewHandle) -> bool { @@ -132,6 +139,7 @@ impl Member { pub fn render( &self, project: &ModelHandle, + basis: usize, theme: &Theme, follower_states: &FollowerStatesByLeader, active_call: Option<&ModelHandle>, @@ -272,6 +280,7 @@ impl Member { } Member::Axis(axis) => axis.render( project, + basis + 1, theme, follower_states, active_call, @@ -295,13 +304,35 @@ impl Member { } } -#[derive(Clone, Debug, Eq, PartialEq)] +#[derive(Clone, Debug, PartialEq)] pub(crate) struct PaneAxis { pub axis: Axis, pub members: Vec, + pub flexes: Rc>>, } impl PaneAxis { + pub fn new(axis: Axis, members: Vec) -> Self { + let flexes = Rc::new(RefCell::new(vec![1.; members.len()])); + Self { + axis, + members, + flexes, + } + } + + 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, @@ -323,6 +354,7 @@ impl PaneAxis { } self.members.insert(idx, Member::Pane(new_pane.clone())); + *self.flexes.borrow_mut() = vec![1.; self.members.len()]; } else { *member = Member::new_axis(old_pane.clone(), new_pane.clone(), direction); @@ -362,6 +394,7 @@ impl PaneAxis { if found_pane { if let Some(idx) = remove_member { self.members.remove(idx); + *self.flexes.borrow_mut() = vec![1.; self.members.len()]; } if self.members.len() == 1 { @@ -377,6 +410,7 @@ impl PaneAxis { fn render( &self, project: &ModelHandle, + basis: usize, theme: &Theme, follower_state: &FollowerStatesByLeader, active_call: Option<&ModelHandle>, @@ -385,40 +419,50 @@ impl PaneAxis { app_state: &Arc, cx: &mut ViewContext, ) -> AnyElement { - let last_member_ix = self.members.len() - 1; - Flex::new(self.axis) - .with_children(self.members.iter().enumerate().map(|(ix, member)| { - let mut flex = 1.0; - if member.contains(active_pane) { - flex = settings::get::(cx).active_pane_magnification; + debug_assert!(self.members.len() == self.flexes.borrow().len()); + + let mut pane_axis = PaneAxisElement::new(self.axis, basis, self.flexes.clone()); + let mut active_pane_ix = None; + + let mut members = self.members.iter().enumerate().peekable(); + while let Some((ix, member)) = members.next() { + let last = members.peek().is_none(); + + if member.contains(active_pane) { + active_pane_ix = Some(ix); + } + + let mut member = member.render( + project, + (basis + ix) * 10, + theme, + follower_state, + active_call, + active_pane, + zoomed, + app_state, + cx, + ); + + if !last { + let mut border = theme.workspace.pane_divider; + border.left = false; + border.right = false; + border.top = false; + border.bottom = false; + + match self.axis { + Axis::Vertical => border.bottom = true, + Axis::Horizontal => border.right = true, } - let mut member = member.render( - project, - theme, - follower_state, - active_call, - active_pane, - zoomed, - app_state, - cx, - ); - if ix < last_member_ix { - let mut border = theme.workspace.pane_divider; - border.left = false; - border.right = false; - border.top = false; - border.bottom = false; - match self.axis { - Axis::Vertical => border.bottom = true, - Axis::Horizontal => border.right = true, - } - member = member.contained().with_border(border).into_any(); - } + member = member.contained().with_border(border).into_any(); + } - FlexItem::new(member).flex(flex, true) - })) - .into_any() + pane_axis = pane_axis.with_child(member.into_any()); + } + pane_axis.set_active_pane(active_pane_ix); + pane_axis.into_any() } } @@ -474,3 +518,336 @@ impl SplitDirection { } } } + +mod element { + use std::{cell::RefCell, ops::Range, rc::Rc}; + + use gpui::{ + geometry::{ + rect::RectF, + vector::{vec2f, Vector2F}, + }, + json::{self, ToJson}, + platform::{CursorStyle, MouseButton}, + AnyElement, Axis, CursorRegion, Element, LayoutContext, MouseRegion, RectFExt, + SceneBuilder, SizeConstraint, Vector2FExt, ViewContext, + }; + + use crate::{ + pane_group::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE}, + Workspace, WorkspaceSettings, + }; + + pub struct PaneAxisElement { + axis: Axis, + basis: usize, + active_pane_ix: Option, + flexes: Rc>>, + children: Vec>, + } + + impl PaneAxisElement { + pub fn new(axis: Axis, basis: usize, flexes: Rc>>) -> Self { + Self { + axis, + basis, + flexes, + active_pane_ix: None, + children: Default::default(), + } + } + + pub fn set_active_pane(&mut self, active_pane_ix: Option) { + self.active_pane_ix = active_pane_ix; + } + + fn layout_children( + &mut self, + active_pane_magnification: f32, + constraint: SizeConstraint, + remaining_space: &mut f32, + remaining_flex: &mut f32, + cross_axis_max: &mut f32, + view: &mut Workspace, + cx: &mut LayoutContext, + ) { + let flexes = self.flexes.borrow(); + let cross_axis = self.axis.invert(); + for (ix, child) in self.children.iter_mut().enumerate() { + let flex = if active_pane_magnification != 1. { + if let Some(active_pane_ix) = self.active_pane_ix { + if ix == active_pane_ix { + active_pane_magnification + } else { + 1. + } + } else { + 1. + } + } else { + flexes[ix] + }; + + let child_size = if *remaining_flex == 0.0 { + *remaining_space + } else { + let space_per_flex = *remaining_space / *remaining_flex; + space_per_flex * flex + }; + + let child_constraint = match self.axis { + Axis::Horizontal => SizeConstraint::new( + vec2f(child_size, constraint.min.y()), + vec2f(child_size, constraint.max.y()), + ), + Axis::Vertical => SizeConstraint::new( + vec2f(constraint.min.x(), child_size), + vec2f(constraint.max.x(), child_size), + ), + }; + let child_size = child.layout(child_constraint, view, cx); + *remaining_space -= child_size.along(self.axis); + *remaining_flex -= flex; + *cross_axis_max = cross_axis_max.max(child_size.along(cross_axis)); + } + } + } + + impl Extend> for PaneAxisElement { + fn extend>>(&mut self, children: T) { + self.children.extend(children); + } + } + + impl Element for PaneAxisElement { + type LayoutState = f32; + type PaintState = (); + + fn layout( + &mut self, + constraint: SizeConstraint, + view: &mut Workspace, + cx: &mut LayoutContext, + ) -> (Vector2F, Self::LayoutState) { + debug_assert!(self.children.len() == self.flexes.borrow().len()); + + let active_pane_magnification = + settings::get::(cx).active_pane_magnification; + + let mut remaining_flex = 0.; + + if active_pane_magnification != 1. { + let active_pane_flex = self + .active_pane_ix + .map(|_| active_pane_magnification) + .unwrap_or(1.); + remaining_flex += self.children.len() as f32 - 1. + active_pane_flex; + } else { + for flex in self.flexes.borrow().iter() { + remaining_flex += flex; + } + } + + let mut cross_axis_max: f32 = 0.0; + let mut remaining_space = constraint.max_along(self.axis); + + if remaining_space.is_infinite() { + panic!("flex contains flexible children but has an infinite constraint along the flex axis"); + } + + self.layout_children( + active_pane_magnification, + constraint, + &mut remaining_space, + &mut remaining_flex, + &mut cross_axis_max, + view, + cx, + ); + + let mut size = match self.axis { + Axis::Horizontal => vec2f(constraint.max.x() - remaining_space, cross_axis_max), + Axis::Vertical => vec2f(cross_axis_max, constraint.max.y() - remaining_space), + }; + + if constraint.min.x().is_finite() { + size.set_x(size.x().max(constraint.min.x())); + } + if constraint.min.y().is_finite() { + size.set_y(size.y().max(constraint.min.y())); + } + + if size.x() > constraint.max.x() { + size.set_x(constraint.max.x()); + } + if size.y() > constraint.max.y() { + size.set_y(constraint.max.y()); + } + + (size, remaining_space) + } + + fn paint( + &mut self, + scene: &mut SceneBuilder, + bounds: RectF, + visible_bounds: RectF, + remaining_space: &mut Self::LayoutState, + 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(); + + let overflowing = *remaining_space < 0.; + if overflowing { + scene.push_layer(Some(visible_bounds)); + } + + let mut child_origin = bounds.origin(); + + let mut children_iter = self.children.iter_mut().enumerate().peekable(); + while let Some((ix, child)) = children_iter.next() { + let child_start = child_origin.clone(); + child.paint(scene, child_origin, visible_bounds, view, cx); + + match self.axis { + Axis::Horizontal => child_origin += vec2f(child.size().x(), 0.0), + Axis::Vertical => child_origin += vec2f(0.0, child.size().y()), + } + + if let Some(Some((next_ix, next_child))) = can_resize.then(|| children_iter.peek()) + { + scene.push_stacking_context(None, None); + + let handle_origin = match self.axis { + Axis::Horizontal => child_origin - vec2f(HANDLE_HITBOX_SIZE / 2., 0.0), + Axis::Vertical => child_origin - vec2f(0.0, HANDLE_HITBOX_SIZE / 2.), + }; + + let handle_bounds = match self.axis { + Axis::Horizontal => RectF::new( + handle_origin, + vec2f(HANDLE_HITBOX_SIZE, visible_bounds.height()), + ), + Axis::Vertical => RectF::new( + handle_origin, + vec2f(visible_bounds.width(), HANDLE_HITBOX_SIZE), + ), + }; + + let style = match self.axis { + Axis::Horizontal => CursorStyle::ResizeLeftRight, + Axis::Vertical => CursorStyle::ResizeUpDown, + }; + + scene.push_cursor_region(CursorRegion { + bounds: handle_bounds, + style, + }); + + let axis = self.axis; + let child_size = child.size(); + let next_child_size = next_child.size(); + let drag_bounds = visible_bounds.clone(); + let flexes = self.flexes.clone(); + let current_flex = flexes.borrow()[ix]; + let next_ix = *next_ix; + let next_flex = flexes.borrow()[next_ix]; + enum ResizeHandle {} + let mut mouse_region = MouseRegion::new::( + cx.view_id(), + self.basis + ix, + handle_bounds, + ); + mouse_region = 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, + }; + // Don't allow resizing to less than the minimum size, if elements are already too small + if min_size - 1. > child_size.along(axis) + || min_size - 1. > next_child_size.along(axis) + { + return; + } + + let mut current_target_size = (drag.position - child_start).along(axis); + + let proposed_current_pixel_change = + current_target_size - child_size.along(axis); + + if proposed_current_pixel_change < 0. { + current_target_size = f32::max(current_target_size, min_size); + } else if proposed_current_pixel_change > 0. { + // 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, + ); + } + + let current_pixel_change = current_target_size - child_size.along(axis); + let flex_change = current_pixel_change / drag_bounds.length_along(axis); + let current_target_flex = current_flex + flex_change; + let next_target_flex = next_flex - flex_change; + + let mut borrow = flexes.borrow_mut(); + *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); + + scene.pop_stacking_context(); + } + } + + if overflowing { + scene.pop_layer(); + } + } + + fn rect_for_text_range( + &self, + range_utf16: Range, + _: RectF, + _: RectF, + _: &Self::LayoutState, + _: &Self::PaintState, + view: &Workspace, + cx: &ViewContext, + ) -> Option { + self.children + .iter() + .find_map(|child| child.rect_for_text_range(range_utf16.clone(), view, cx)) + } + + fn debug( + &self, + bounds: RectF, + _: &Self::LayoutState, + _: &Self::PaintState, + view: &Workspace, + cx: &ViewContext, + ) -> json::Value { + serde_json::json!({ + "type": "PaneAxis", + "bounds": bounds.to_json(), + "axis": self.axis.to_json(), + "flexes": *self.flexes.borrow(), + "children": self.children.iter().map(|child| child.debug(view, cx)).collect::>() + }) + } + } +} diff --git a/crates/workspace/src/persistence.rs b/crates/workspace/src/persistence.rs index dd2aa5a818..2a4062c079 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,34 @@ 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 = flexes + .as_ref() + .map(|flexes| 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 +680,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 +699,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 +720,7 @@ mod tests { false, )), ], - }, + ), SerializedPaneGroup::Pane(SerializedPane::new( vec![ SerializedItem::new("Terminal", 9, false), @@ -682,7 +729,7 @@ mod tests { false, )), ], - }; + ); let workspace = SerializedWorkspace { id: 5, @@ -811,12 +858,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 +879,7 @@ mod tests { true, )), ], - }, + ), SerializedPaneGroup::Pane(SerializedPane::new( vec![ SerializedItem::new("Terminal", 5, true), @@ -841,7 +888,7 @@ mod tests { false, )), ], - }; + ); let workspace = default_workspace(&["/tmp"], ¢er_pane); @@ -858,12 +905,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 +926,7 @@ mod tests { true, )), ], - }, + ), SerializedPaneGroup::Pane(SerializedPane::new( vec![ SerializedItem::new("Terminal", 5, false), @@ -888,7 +935,7 @@ mod tests { false, )), ], - }; + ); let id = &["/tmp"]; @@ -896,9 +943,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 +961,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 1075061853..5f4c29cd5b 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,11 @@ 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,10 +189,7 @@ impl SerializedPaneGroup { } Some(( - Member::Axis(PaneAxis { - axis: *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 01d80d141c..885c686ddc 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1,8 +1,4 @@ pub mod dock; -/// NOTE: Focus only 'takes' after an update has flushed_effects. -/// -/// This may cause issues when you're trying to write tests that use workspace focus to add items at -/// specific locations. pub mod item; pub mod notifications; pub mod pane; @@ -508,6 +504,7 @@ pub struct Workspace { subscriptions: Vec, _apply_leader_updates: Task>, _observe_current_user: Task>, + _schedule_serialize: Option>, pane_history_timestamp: Arc, } @@ -722,6 +719,7 @@ impl Workspace { app_state, _observe_current_user, _apply_leader_updates, + _schedule_serialize: None, leader_updates_tx, subscriptions, pane_history_timestamp, @@ -2897,6 +2895,14 @@ 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, @@ -2927,12 +2933,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))