Detailed changes
@@ -1182,8 +1182,10 @@ impl EditorElement {
});
scene.push_mouse_region(
MouseRegion::new::<ScrollbarMouseHandlers>(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();
@@ -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,
@@ -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;
@@ -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<Workspace> {
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<Pane>),
@@ -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<Pane>) -> bool {
@@ -132,6 +139,7 @@ impl Member {
pub fn render(
&self,
project: &ModelHandle<Project>,
+ basis: usize,
theme: &Theme,
follower_states: &FollowerStatesByLeader,
active_call: Option<&ModelHandle<ActiveCall>>,
@@ -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<Member>,
+ pub flexes: Rc<RefCell<Vec<f32>>>,
}
impl PaneAxis {
+ pub fn new(axis: Axis, members: Vec<Member>) -> Self {
+ let flexes = Rc::new(RefCell::new(vec![1.; members.len()]));
+ Self {
+ axis,
+ members,
+ flexes,
+ }
+ }
+
+ pub fn load(axis: Axis, members: Vec<Member>, flexes: Option<Vec<f32>>) -> 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<Pane>,
@@ -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<Project>,
+ basis: usize,
theme: &Theme,
follower_state: &FollowerStatesByLeader,
active_call: Option<&ModelHandle<ActiveCall>>,
@@ -385,40 +419,50 @@ impl PaneAxis {
app_state: &Arc<AppState>,
cx: &mut ViewContext<Workspace>,
) -> AnyElement<Workspace> {
- 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::<WorkspaceSettings>(cx).active_pane_magnification;
- }
+ debug_assert!(self.members.len() == self.flexes.borrow().len());
- 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();
+ 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,
}
- FlexItem::new(member).flex(flex, true)
- }))
- .into_any()
+ member = member.contained().with_border(border).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<usize>,
+ flexes: Rc<RefCell<Vec<f32>>>,
+ children: Vec<AnyElement<Workspace>>,
+ }
+
+ impl PaneAxisElement {
+ pub fn new(axis: Axis, basis: usize, flexes: Rc<RefCell<Vec<f32>>>) -> Self {
+ Self {
+ axis,
+ basis,
+ flexes,
+ active_pane_ix: None,
+ children: Default::default(),
+ }
+ }
+
+ pub fn set_active_pane(&mut self, active_pane_ix: Option<usize>) {
+ 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<Workspace>,
+ ) {
+ 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<AnyElement<Workspace>> for PaneAxisElement {
+ fn extend<T: IntoIterator<Item = AnyElement<Workspace>>>(&mut self, children: T) {
+ self.children.extend(children);
+ }
+ }
+
+ impl Element<Workspace> for PaneAxisElement {
+ type LayoutState = f32;
+ type PaintState = ();
+
+ fn layout(
+ &mut self,
+ constraint: SizeConstraint,
+ view: &mut Workspace,
+ cx: &mut LayoutContext<Workspace>,
+ ) -> (Vector2F, Self::LayoutState) {
+ debug_assert!(self.children.len() == self.flexes.borrow().len());
+
+ let active_pane_magnification =
+ settings::get::<WorkspaceSettings>(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<Workspace>,
+ ) -> Self::PaintState {
+ let can_resize = settings::get::<WorkspaceSettings>(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::<ResizeHandle>(
+ 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<usize>,
+ _: RectF,
+ _: RectF,
+ _: &Self::LayoutState,
+ _: &Self::PaintState,
+ view: &Workspace,
+ cx: &ViewContext<Workspace>,
+ ) -> Option<RectF> {
+ 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<Workspace>,
+ ) -> 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::<Vec<json::Value>>()
+ })
+ }
+ }
+}
@@ -45,6 +45,7 @@ define_connection! {
// parent_group_id: Option<usize>, // None indicates that this is the root node
// position: Optiopn<usize>, // None indicates that this is the root node
// axis: Option<Axis>, // 'Vertical', 'Horizontal'
+ // flexes: Option<Vec<f32>>, // 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<GroupId>,
) -> Result<Vec<SerializedPaneGroup>> {
type GroupKey = (Option<GroupId>, WorkspaceId);
- type GroupOrPane = (Option<GroupId>, Option<Axis>, Option<PaneId>, Option<bool>);
+ type GroupOrPane = (
+ Option<GroupId>,
+ Option<Axis>,
+ Option<PaneId>,
+ Option<bool>,
+ Option<String>,
+ );
self.select_bound::<GroupKey, GroupOrPane>(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::<Vec<f32>>(&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 {
+ 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;
@@ -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<Vec<f32>>,
children: Vec<SerializedPaneGroup>,
},
Pane(SerializedPane),
@@ -149,7 +150,7 @@ impl Default for SerializedPaneGroup {
impl SerializedPaneGroup {
#[async_recursion(?Send)]
pub(crate) async fn deserialize(
- &self,
+ self,
project: &ModelHandle<Project>,
workspace_id: WorkspaceId,
workspace: &WeakViewHandle<Workspace>,
@@ -160,7 +161,11 @@ impl SerializedPaneGroup {
Vec<Option<Box<dyn ItemHandle>>>,
)> {
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,
))
@@ -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<Subscription>,
_apply_leader_updates: Task<Result<()>>,
_observe_current_user: Task<Result<()>>,
+ _schedule_serialize: Option<Task<()>>,
pane_history_timestamp: Arc<AtomicUsize>,
}
@@ -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>) {
+ 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<Self>) {
fn serialize_pane_handle(
pane_handle: &ViewHandle<Pane>,
@@ -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::<Vec<_>>(),
+ flexes: Some(flexes.borrow().clone()),
},
Member::Pane(pane_handle) => {
SerializedPaneGroup::Pane(serialize_pane_handle(&pane_handle, cx))