@@ -308,7 +308,7 @@ impl Member {
pub(crate) struct PaneAxis {
pub axis: Axis,
pub members: Vec<Member>,
- flexes: Rc<RefCell<Vec<f32>>>,
+ pub flexes: Rc<RefCell<Vec<f32>>>,
}
impl PaneAxis {
@@ -321,6 +321,18 @@ impl PaneAxis {
}
}
+ 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>,
@@ -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<V: View> {
+ pub struct PaneAxisElement {
axis: Axis,
basis: usize,
active_pane_ix: Option<usize>,
flexes: Rc<RefCell<Vec<f32>>>,
- children: Vec<AnyElement<V>>,
+ children: Vec<AnyElement<Workspace>>,
}
- impl<V: View> PaneAxisElement<V> {
+ impl PaneAxisElement {
pub fn new(axis: Axis, basis: usize, flexes: Rc<RefCell<Vec<f32>>>) -> 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<V>,
+ view: &mut Workspace,
+ cx: &mut LayoutContext<Workspace>,
) {
let flexes = self.flexes.borrow();
let cross_axis = self.axis.invert();
@@ -602,21 +614,21 @@ mod element {
}
}
- impl<V: View> Extend<AnyElement<V>> for PaneAxisElement<V> {
- fn extend<T: IntoIterator<Item = AnyElement<V>>>(&mut self, children: T) {
+ impl Extend<AnyElement<Workspace>> for PaneAxisElement {
+ fn extend<T: IntoIterator<Item = AnyElement<Workspace>>>(&mut self, children: T) {
self.children.extend(children);
}
}
- impl<V: View> Element<V> for PaneAxisElement<V> {
+ impl Element<Workspace> for PaneAxisElement {
type LayoutState = f32;
type PaintState = ();
fn layout(
&mut self,
constraint: SizeConstraint,
- view: &mut V,
- cx: &mut LayoutContext<V>,
+ view: &mut Workspace,
+ cx: &mut LayoutContext<Workspace>,
) -> (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<V>,
+ 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();
@@ -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<V>,
+ view: &Workspace,
+ cx: &ViewContext<Workspace>,
) -> Option<RectF> {
self.children
.iter()
@@ -822,8 +837,8 @@ mod element {
bounds: RectF,
_: &Self::LayoutState,
_: &Self::PaintState,
- view: &V,
- cx: &ViewContext<V>,
+ view: &Workspace,
+ cx: &ViewContext<Workspace>,
) -> json::Value {
serde_json::json!({
"type": "PaneAxis",
@@ -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,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 {
+ 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;
@@ -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,7 @@ 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,7 +185,7 @@ impl SerializedPaneGroup {
}
Some((
- Member::Axis(PaneAxis::new(*axis, members)),
+ Member::Axis(PaneAxis::load(axis, members, flexes)),
current_active_pane,
items,
))
@@ -504,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>,
}
@@ -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>) {
+ 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>,
@@ -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::<Vec<_>>(),
+ flexes: Some(flexes.borrow().clone()),
},
Member::Pane(pane_handle) => {
SerializedPaneGroup::Pane(serialize_pane_handle(&pane_handle, cx))