Consolidate pane axis state into PaneAxisState struct

Max Brunsfeld and Eric Holk created

Co-authored-by: Eric Holk <eric@zed.dev>

Change summary

crates/debugger_ui/src/persistence.rs   |   5 
crates/terminal_view/src/persistence.rs |   5 
crates/workspace/src/pane_group.rs      | 143 +++++++++++++++++---------
crates/workspace/src/workspace.rs       |  32 +----
4 files changed, 108 insertions(+), 77 deletions(-)

Detailed changes

crates/debugger_ui/src/persistence.rs 🔗

@@ -152,15 +152,14 @@ pub(crate) fn build_serialized_pane_layout(pane_group: &Member, cx: &App) -> Ser
         Member::Axis(PaneAxis {
             axis,
             members,
-            flexes,
-            bounding_boxes: _,
+            state,
         }) => SerializedPaneLayout::Group {
             axis: *axis,
             children: members
                 .iter()
                 .map(|member| build_serialized_pane_layout(member, cx))
                 .collect::<Vec<_>>(),
-            flexes: Some(flexes.lock().clone()),
+            flexes: Some(state.flexes()),
         },
         Member::Pane(pane_handle) => SerializedPaneLayout::Pane(serialize_pane(pane_handle, cx)),
     }

crates/terminal_view/src/persistence.rs 🔗

@@ -41,15 +41,14 @@ fn build_serialized_pane_group(
         Member::Axis(PaneAxis {
             axis,
             members,
-            flexes,
-            bounding_boxes: _,
+            state,
         }) => SerializedPaneGroup::Group {
             axis: SerializedAxis(*axis),
             children: members
                 .iter()
                 .map(|member| build_serialized_pane_group(member, active_pane, cx))
                 .collect::<Vec<_>>(),
-            flexes: Some(flexes.lock().clone()),
+            flexes: Some(state.flexes()),
         },
         Member::Pane(pane_handle) => {
             SerializedPaneGroup::Pane(serialize_pane(pane_handle, pane_handle == active_pane, cx))

crates/workspace/src/pane_group.rs 🔗

@@ -10,12 +10,11 @@ use gpui::{
     Along, AnyView, AnyWeakView, Axis, Bounds, Entity, Hsla, IntoElement, MouseButton, Pixels,
     Point, StyleRefinement, WeakEntity, Window, point, size,
 };
-use parking_lot::Mutex;
 use project::Project;
 use schemars::JsonSchema;
 use serde::Deserialize;
 use settings::Settings;
-use std::sync::Arc;
+use std::{cell::RefCell, rc::Rc, sync::Arc};
 use ui::prelude::*;
 
 pub const HANDLE_HITBOX_SIZE: f32 = 4.0;
@@ -580,23 +579,76 @@ impl Member {
     }
 }
 
+#[derive(Debug, Clone)]
+pub struct PaneAxisState(Rc<RefCell<PaneAxisStateInner>>);
+
+#[derive(Debug)]
+struct PaneAxisStateInner {
+    flexes: Vec<f32>,
+    bounding_boxes: Vec<Option<Bounds<Pixels>>>,
+}
+
+impl PaneAxisState {
+    pub fn new(member_count: usize) -> Self {
+        Self(Rc::new(RefCell::new(PaneAxisStateInner {
+            flexes: vec![1.; member_count],
+            bounding_boxes: vec![None; member_count],
+        })))
+    }
+
+    fn with_flexes(flexes: Vec<f32>) -> Self {
+        let member_count = flexes.len();
+        Self(Rc::new(RefCell::new(PaneAxisStateInner {
+            flexes,
+            bounding_boxes: vec![None; member_count],
+        })))
+    }
+
+    pub fn flexes(&self) -> Vec<f32> {
+        self.0.borrow().flexes.clone()
+    }
+
+    pub fn len(&self) -> usize {
+        self.0.borrow().flexes.len()
+    }
+
+    fn resize(&self, len: usize) {
+        let mut inner = self.0.borrow_mut();
+        inner.flexes.clear();
+        inner.flexes.resize(len, 1.);
+        inner.bounding_boxes.clear();
+        inner.bounding_boxes.resize(len, None);
+    }
+
+    fn reset_flexes(&self) {
+        let mut inner = self.0.borrow_mut();
+        inner.flexes.iter_mut().for_each(|f| *f = 1.);
+        inner.bounding_boxes.iter_mut().for_each(|f| *f = None);
+    }
+
+    fn bounding_boxes(&self) -> Vec<Option<Bounds<Pixels>>> {
+        self.0.borrow().bounding_boxes.clone()
+    }
+
+    fn bounding_box_at(&self, index: usize) -> Option<Bounds<Pixels>> {
+        self.0.borrow().bounding_boxes[index]
+    }
+}
+
 #[derive(Debug, Clone)]
 pub struct PaneAxis {
     pub axis: Axis,
     pub members: Vec<Member>,
-    pub flexes: Arc<Mutex<Vec<f32>>>,
-    pub bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
+    pub state: PaneAxisState,
 }
 
 impl PaneAxis {
     pub fn new(axis: Axis, members: Vec<Member>) -> Self {
-        let flexes = Arc::new(Mutex::new(vec![1.; members.len()]));
-        let bounding_boxes = Arc::new(Mutex::new(vec![None; members.len()]));
+        let state = PaneAxisState::new(members.len());
         Self {
             axis,
             members,
-            flexes,
-            bounding_boxes,
+            state,
         }
     }
 
@@ -608,13 +660,11 @@ impl PaneAxis {
             flexes = vec![1.; members.len()];
         }
 
-        let flexes = Arc::new(Mutex::new(flexes));
-        let bounding_boxes = Arc::new(Mutex::new(vec![None; members.len()]));
+        let state = PaneAxisState::with_flexes(flexes);
         Self {
             axis,
             members,
-            flexes,
-            bounding_boxes,
+            state,
         }
     }
 
@@ -652,7 +702,7 @@ impl PaneAxis {
 
     fn insert_pane(&mut self, idx: usize, new_pane: &Entity<Pane>) {
         self.members.insert(idx, Member::Pane(new_pane.clone()));
-        *self.flexes.lock() = vec![1.; self.members.len()];
+        self.state.resize(self.members.len());
     }
 
     fn find_pane_at_border(&self, direction: SplitDirection) -> Option<&Entity<Pane>> {
@@ -697,12 +747,12 @@ impl PaneAxis {
         if found_pane {
             if let Some(idx) = remove_member {
                 self.members.remove(idx);
-                *self.flexes.lock() = vec![1.; self.members.len()];
+                self.state.resize(self.members.len());
             }
 
             if self.members.len() == 1 {
                 let result = self.members.pop();
-                *self.flexes.lock() = vec![1.; self.members.len()];
+                self.state.resize(self.members.len());
                 Ok(result)
             } else {
                 Ok(None)
@@ -713,7 +763,7 @@ impl PaneAxis {
     }
 
     fn reset_pane_sizes(&self) {
-        *self.flexes.lock() = vec![1.; self.members.len()];
+        self.state.resize(self.members.len());
         for member in self.members.iter() {
             if let Member::Axis(axis) = member {
                 axis.reset_pane_sizes();
@@ -729,8 +779,8 @@ impl PaneAxis {
         bounds: &Bounds<Pixels>,
     ) -> Option<bool> {
         let container_size = self
-            .bounding_boxes
-            .lock()
+            .state
+            .bounding_boxes()
             .iter()
             .filter_map(|e| *e)
             .reduce(|acc, e| acc.union(&e))
@@ -767,7 +817,8 @@ impl PaneAxis {
             Axis::Horizontal => px(HORIZONTAL_MIN_SIZE),
             Axis::Vertical => px(VERTICAL_MIN_SIZE),
         };
-        let mut flexes = self.flexes.lock();
+        let mut state = self.state.0.borrow_mut();
+        let flexes = &mut state.flexes;
 
         let ix = if found_pane {
             self.members.iter().position(|m| {
@@ -847,13 +898,13 @@ impl PaneAxis {
     }
 
     fn bounding_box_for_pane(&self, pane: &Entity<Pane>) -> Option<Bounds<Pixels>> {
-        debug_assert!(self.members.len() == self.bounding_boxes.lock().len());
+        debug_assert!(self.members.len() == self.state.len());
 
         for (idx, member) in self.members.iter().enumerate() {
             match member {
                 Member::Pane(found) => {
                     if pane == found {
-                        return self.bounding_boxes.lock()[idx];
+                        return self.state.bounding_box_at(idx);
                     }
                 }
                 Member::Axis(axis) => {
@@ -867,9 +918,9 @@ impl PaneAxis {
     }
 
     fn pane_at_pixel_position(&self, coordinate: Point<Pixels>) -> Option<&Entity<Pane>> {
-        debug_assert!(self.members.len() == self.bounding_boxes.lock().len());
+        debug_assert!(self.members.len() == self.state.len());
 
-        let bounding_boxes = self.bounding_boxes.lock();
+        let bounding_boxes = self.state.bounding_boxes();
 
         for (idx, member) in self.members.iter().enumerate() {
             if let Some(coordinates) = bounding_boxes[idx]
@@ -892,7 +943,7 @@ impl PaneAxis {
         window: &mut Window,
         cx: &mut App,
     ) -> PaneRenderResult {
-        debug_assert!(self.members.len() == self.flexes.lock().len());
+        debug_assert!(self.members.len() == self.state.len());
         let mut active_pane_ix = None;
         let mut contains_active_pane = false;
         let mut is_leaf_pane = vec![false; self.members.len()];
@@ -926,8 +977,7 @@ impl PaneAxis {
         let element = pane_axis(
             self.axis,
             basis,
-            self.flexes.clone(),
-            self.bounding_boxes.clone(),
+            self.state.clone(),
             render_cx.workspace().clone(),
         )
         .with_is_leaf_pane_mask(is_leaf_pane)
@@ -1037,7 +1087,7 @@ impl SplitDirection {
 
 pub mod element {
     use std::mem;
-    use std::{cell::RefCell, iter, rc::Rc, sync::Arc};
+    use std::{cell::RefCell, iter, rc::Rc};
 
     use gpui::{
         Along, AnyElement, App, Axis, BorderStyle, Bounds, Element, GlobalElementId,
@@ -1045,7 +1095,6 @@ pub mod element {
         Pixels, Point, Size, Style, WeakEntity, Window, px, relative, size,
     };
     use gpui::{CursorStyle, Hitbox};
-    use parking_lot::Mutex;
     use settings::Settings;
     use smallvec::SmallVec;
     use ui::prelude::*;
@@ -1055,22 +1104,20 @@ pub mod element {
 
     use crate::WorkspaceSettings;
 
-    use super::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE};
+    use super::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, PaneAxisState, VERTICAL_MIN_SIZE};
 
     const DIVIDER_SIZE: f32 = 1.0;
 
     pub fn pane_axis(
         axis: Axis,
         basis: usize,
-        flexes: Arc<Mutex<Vec<f32>>>,
-        bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
+        state: PaneAxisState,
         workspace: WeakEntity<Workspace>,
     ) -> PaneAxisElement {
         PaneAxisElement {
             axis,
             basis,
-            flexes,
-            bounding_boxes,
+            state,
             children: SmallVec::new(),
             active_pane_ix: None,
             workspace,
@@ -1081,10 +1128,7 @@ pub mod element {
     pub struct PaneAxisElement {
         axis: Axis,
         basis: usize,
-        /// Equivalent to ColumnWidths (but in terms of flexes instead of percentages)
-        /// For example, flexes "1.33, 1, 1", instead of "40%, 30%, 30%"
-        flexes: Arc<Mutex<Vec<f32>>>,
-        bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
+        state: PaneAxisState,
         children: SmallVec<[AnyElement; 2]>,
         active_pane_ix: Option<usize>,
         workspace: WeakEntity<Workspace>,
@@ -1121,7 +1165,7 @@ pub mod element {
         }
 
         fn compute_resize(
-            flexes: &Arc<Mutex<Vec<f32>>>,
+            state: &PaneAxisState,
             e: &MouseMoveEvent,
             ix: usize,
             axis: Axis,
@@ -1135,7 +1179,8 @@ pub mod element {
                 Axis::Horizontal => px(HORIZONTAL_MIN_SIZE),
                 Axis::Vertical => px(VERTICAL_MIN_SIZE),
             };
-            let mut flexes = flexes.lock();
+            let mut inner = state.0.borrow_mut();
+            let flexes = &mut inner.flexes;
             debug_assert!(flex_values_in_bounds(flexes.as_slice()));
 
             // Math to convert a flex value to a pixel value
@@ -1299,7 +1344,7 @@ pub mod element {
                     (state.clone(), state)
                 },
             );
-            let flexes = self.flexes.lock().clone();
+            let flexes = self.state.flexes();
             let len = self.children.len();
             debug_assert!(flexes.len() == len);
             debug_assert!(flex_values_in_bounds(flexes.as_slice()));
@@ -1309,8 +1354,7 @@ pub mod element {
             let mut origin = bounds.origin;
             let space_per_flex = bounds.size.along(self.axis) / total_flex;
 
-            let mut bounding_boxes = self.bounding_boxes.lock();
-            bounding_boxes.clear();
+            self.state.0.borrow_mut().bounding_boxes.clear();
 
             let mut layout = PaneAxisLayout {
                 dragged_handle,
@@ -1329,7 +1373,11 @@ pub mod element {
                     size: child_size,
                 };
 
-                bounding_boxes.push(Some(child_bounds));
+                self.state
+                    .0
+                    .borrow_mut()
+                    .bounding_boxes
+                    .push(Some(child_bounds));
                 child.layout_as_root(child_size.into(), window, cx);
                 child.prepaint_at(origin, window, cx);
 
@@ -1449,15 +1497,14 @@ pub mod element {
 
                     window.on_mouse_event({
                         let dragged_handle = layout.dragged_handle.clone();
-                        let flexes = self.flexes.clone();
+                        let state = self.state.clone();
                         let workspace = self.workspace.clone();
                         let handle_hitbox = handle.hitbox.clone();
                         move |e: &MouseDownEvent, phase, window, cx| {
                             if phase.bubble() && handle_hitbox.is_hovered(window) {
                                 dragged_handle.replace(Some(ix));
                                 if e.click_count >= 2 {
-                                    let mut borrow = flexes.lock();
-                                    *borrow = vec![1.; borrow.len()];
+                                    state.reset_flexes();
                                     workspace
                                         .update(cx, |this, cx| this.serialize_workspace(window, cx))
                                         .log_err();
@@ -1471,14 +1518,14 @@ pub mod element {
                     window.on_mouse_event({
                         let workspace = self.workspace.clone();
                         let dragged_handle = layout.dragged_handle.clone();
-                        let flexes = self.flexes.clone();
+                        let state = self.state.clone();
                         let child_bounds = child.bounds;
                         let axis = self.axis;
                         move |e: &MouseMoveEvent, phase, window, cx| {
                             let dragged_handle = dragged_handle.borrow();
                             if phase.bubble() && *dragged_handle == Some(ix) {
                                 Self::compute_resize(
-                                    &flexes,
+                                    &state,
                                     e,
                                     ix,
                                     axis,

crates/workspace/src/workspace.rs 🔗

@@ -73,10 +73,9 @@ use notifications::{
 };
 pub use pane::*;
 pub use pane_group::{
-    ActivePaneDecorator, HANDLE_HITBOX_SIZE, Member, PaneAxis, PaneGroup, PaneRenderContext,
-    SplitDirection, pane_axis,
+    ActivePaneDecorator, HANDLE_HITBOX_SIZE, Member, PaneAxis, PaneAxisState, PaneGroup,
+    PaneRenderContext, SplitDirection, pane_axis,
 };
-use parking_lot::Mutex;
 use persistence::{DB, SerializedWindowBounds, model::SerializedWorkspace};
 pub use persistence::{
     DB as WORKSPACE_DB, WorkspaceDb, delete_unloaded_items,
@@ -1337,8 +1336,7 @@ pub struct Workspace {
     last_open_dock_positions: Vec<DockPosition>,
     removing: bool,
     _panels_task: Option<Task<Result<()>>>,
-    center_element_flexes: Arc<Mutex<Vec<f32>>>,
-    center_element_bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
+    center_element_state: PaneAxisState,
 }
 
 impl EventEmitter<Event> for Workspace {}
@@ -1744,8 +1742,7 @@ impl Workspace {
             scheduled_tasks: Vec::new(),
             last_open_dock_positions: Vec::new(),
             removing: false,
-            center_element_flexes: Arc::new(Mutex::new(Vec::new())),
-            center_element_bounding_boxes: Arc::new(Mutex::new(Vec::new())),
+            center_element_state: PaneAxisState::new(0),
         }
     }
 
@@ -6139,15 +6136,14 @@ impl Workspace {
                 Member::Axis(PaneAxis {
                     axis,
                     members,
-                    flexes,
-                    bounding_boxes: _,
+                    state,
                 }) => SerializedPaneGroup::Group {
                     axis: SerializedAxis(*axis),
                     children: members
                         .iter()
                         .map(|member| build_serialized_pane_group(member, window, cx))
                         .collect::<Vec<_>>(),
-                    flexes: Some(flexes.lock().clone()),
+                    flexes: Some(state.flexes()),
                 },
                 Member::Pane(pane_handle) => {
                     SerializedPaneGroup::Pane(serialize_pane_handle(pane_handle, window, cx))
@@ -7052,24 +7048,14 @@ impl Workspace {
         let member_count =
             1 + left_center_element.is_some() as usize + right_center_element.is_some() as usize;
 
-        {
-            let mut flexes = self.center_element_flexes.lock();
-            if flexes.len() != member_count {
-                *flexes = vec![1.; member_count];
-            }
-        }
-        {
-            let mut bounding_boxes = self.center_element_bounding_boxes.lock();
-            if bounding_boxes.len() != member_count {
-                *bounding_boxes = vec![None; member_count];
-            }
+        if self.center_element_state.len() != member_count {
+            self.center_element_state = PaneAxisState::new(member_count);
         }
 
         let axis_element = pane_axis(
             Axis::Horizontal,
             0,
-            self.center_element_flexes.clone(),
-            self.center_element_bounding_boxes.clone(),
+            self.center_element_state.clone(),
             self.weak_self.clone(),
         );