Add back semi-funcitonal pane resizing code

Mikayla created

Change summary

crates/editor2/src/element.rs                 |  16 
crates/gpui2/src/elements/div.rs              |  22 
crates/gpui2/src/elements/text.rs             |   2 
crates/gpui2/src/geometry.rs                  |  31 +
crates/gpui2/src/interactive.rs               |   6 
crates/gpui2/src/window.rs                    |  12 
crates/ui2/src/components/right_click_menu.rs |   2 
crates/workspace2/src/pane.rs                 |   5 
crates/workspace2/src/pane_group.rs           | 309 +++++++++++++++-----
crates/workspace2/src/workspace2.rs           |   2 
10 files changed, 299 insertions(+), 108 deletions(-)

Detailed changes

crates/editor2/src/element.rs 🔗

@@ -389,9 +389,9 @@ impl EditorElement {
         let mut click_count = event.click_count;
         let modifiers = event.modifiers;
 
-        if gutter_bounds.contains_point(&event.position) {
+        if gutter_bounds.contains(&event.position) {
             click_count = 3; // Simulate triple-click when clicking the gutter to select lines
-        } else if !text_bounds.contains_point(&event.position) {
+        } else if !text_bounds.contains(&event.position) {
             return false;
         }
         if !cx.was_top_layer(&event.position, stacking_order) {
@@ -437,7 +437,7 @@ impl EditorElement {
         text_bounds: Bounds<Pixels>,
         cx: &mut ViewContext<Editor>,
     ) -> bool {
-        if !text_bounds.contains_point(&event.position) {
+        if !text_bounds.contains(&event.position) {
             return false;
         }
         let point_for_position = position_map.point_for_position(text_bounds, event.position);
@@ -467,7 +467,7 @@ impl EditorElement {
 
         if !pending_nonempty_selections
             && event.modifiers.command
-            && text_bounds.contains_point(&event.position)
+            && text_bounds.contains(&event.position)
             && cx.was_top_layer(&event.position, stacking_order)
         {
             let point = position_map.point_for_position(text_bounds, event.position);
@@ -529,8 +529,8 @@ impl EditorElement {
             );
         }
 
-        let text_hovered = text_bounds.contains_point(&event.position);
-        let gutter_hovered = gutter_bounds.contains_point(&event.position);
+        let text_hovered = text_bounds.contains(&event.position);
+        let gutter_hovered = gutter_bounds.contains(&event.position);
         let was_top = cx.was_top_layer(&event.position, stacking_order);
 
         editor.set_gutter_hovered(gutter_hovered, cx);
@@ -894,7 +894,7 @@ impl EditorElement {
                 bounds: text_bounds,
             }),
             |cx| {
-                if text_bounds.contains_point(&cx.mouse_position()) {
+                if text_bounds.contains(&cx.mouse_position()) {
                     if self
                         .editor
                         .read(cx)
@@ -960,7 +960,7 @@ impl EditorElement {
                                     |fold_element_state, cx| {
                                         if fold_element_state.is_active() {
                                             gpui::blue()
-                                        } else if fold_bounds.contains_point(&cx.mouse_position()) {
+                                        } else if fold_bounds.contains(&cx.mouse_position()) {
                                             gpui::black()
                                         } else {
                                             gpui::red()

crates/gpui2/src/elements/div.rs 🔗

@@ -761,7 +761,7 @@ pub struct InteractiveBounds {
 
 impl InteractiveBounds {
     pub fn visibly_contains(&self, point: &Point<Pixels>, cx: &WindowContext) -> bool {
-        self.bounds.contains_point(point) && cx.was_top_layer(&point, &self.stacking_order)
+        self.bounds.contains(point) && cx.was_top_layer(&point, &self.stacking_order)
     }
 }
 
@@ -860,10 +860,10 @@ impl Interactivity {
             .and_then(|group_hover| GroupBounds::get(&group_hover.group, cx));
 
         if let Some(group_bounds) = hover_group_bounds {
-            let hovered = group_bounds.contains_point(&cx.mouse_position());
+            let hovered = group_bounds.contains(&cx.mouse_position());
             cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
                 if phase == DispatchPhase::Capture {
-                    if group_bounds.contains_point(&event.position) != hovered {
+                    if group_bounds.contains(&event.position) != hovered {
                         cx.notify();
                     }
                 }
@@ -875,10 +875,10 @@ impl Interactivity {
             || cx.active_drag.is_some() && !self.drag_over_styles.is_empty()
         {
             let bounds = bounds.intersect(&cx.content_mask().bounds);
-            let hovered = bounds.contains_point(&cx.mouse_position());
+            let hovered = bounds.contains(&cx.mouse_position());
             cx.on_mouse_event(move |event: &MouseMoveEvent, phase, cx| {
                 if phase == DispatchPhase::Capture {
-                    if bounds.contains_point(&event.position) != hovered {
+                    if bounds.contains(&event.position) != hovered {
                         cx.notify();
                     }
                 }
@@ -1067,8 +1067,8 @@ impl Interactivity {
             let interactive_bounds = interactive_bounds.clone();
             cx.on_mouse_event(move |down: &MouseDownEvent, phase, cx| {
                 if phase == DispatchPhase::Bubble {
-                    let group = active_group_bounds
-                        .map_or(false, |bounds| bounds.contains_point(&down.position));
+                    let group =
+                        active_group_bounds.map_or(false, |bounds| bounds.contains(&down.position));
                     let element = interactive_bounds.visibly_contains(&down.position, cx);
                     if group || element {
                         *active_state.borrow_mut() = ElementClickedState { group, element };
@@ -1182,7 +1182,7 @@ impl Interactivity {
             let mouse_position = cx.mouse_position();
             if let Some(group_hover) = self.group_hover_style.as_ref() {
                 if let Some(group_bounds) = GroupBounds::get(&group_hover.group, cx) {
-                    if group_bounds.contains_point(&mouse_position)
+                    if group_bounds.contains(&mouse_position)
                         && cx.was_top_layer(&mouse_position, cx.stacking_order())
                     {
                         style.refine(&group_hover.style);
@@ -1192,7 +1192,7 @@ impl Interactivity {
             if self.hover_style.is_some() {
                 if bounds
                     .intersect(&cx.content_mask().bounds)
-                    .contains_point(&mouse_position)
+                    .contains(&mouse_position)
                     && cx.was_top_layer(&mouse_position, cx.stacking_order())
                 {
                     style.refine(&self.hover_style);
@@ -1203,7 +1203,7 @@ impl Interactivity {
                 for (state_type, group_drag_style) in &self.group_drag_over_styles {
                     if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
                         if *state_type == drag.view.entity_type()
-                            && group_bounds.contains_point(&mouse_position)
+                            && group_bounds.contains(&mouse_position)
                         {
                             style.refine(&group_drag_style.style);
                         }
@@ -1214,7 +1214,7 @@ impl Interactivity {
                     if *state_type == drag.view.entity_type()
                         && bounds
                             .intersect(&cx.content_mask().bounds)
-                            .contains_point(&mouse_position)
+                            .contains(&mouse_position)
                     {
                         style.refine(drag_over_style);
                     }

crates/gpui2/src/elements/text.rs 🔗

@@ -253,7 +253,7 @@ impl TextState {
     }
 
     fn index_for_position(&self, bounds: Bounds<Pixels>, position: Point<Pixels>) -> Option<usize> {
-        if !bounds.contains_point(&position) {
+        if !bounds.contains(&position) {
             return None;
         }
 

crates/gpui2/src/geometry.rs 🔗

@@ -23,6 +23,14 @@ impl Axis {
     }
 }
 
+pub trait Along {
+    type Unit;
+
+    fn along(&self, axis: Axis) -> Self::Unit;
+
+    fn apply_along(&self, axis: Axis, f: impl FnOnce(Self::Unit) -> Self::Unit) -> Self;
+}
+
 impl sqlez::bindable::StaticColumnCount for Axis {}
 impl sqlez::bindable::Bind for Axis {
     fn bind(
@@ -142,8 +150,19 @@ impl<T: Clone + Debug + Default> Point<T> {
             y: f(self.y.clone()),
         }
     }
+}
+
+impl<T: Clone + Debug + Default> Along for Point<T> {
+    type Unit = T;
+
+    fn along(&self, axis: Axis) -> T {
+        match axis {
+            Axis::Horizontal => self.x.clone(),
+            Axis::Vertical => self.y.clone(),
+        }
+    }
 
-    pub fn apply_along(&self, axis: Axis, f: impl FnOnce(T) -> T) -> Point<T> {
+    fn apply_along(&self, axis: Axis, f: impl FnOnce(T) -> T) -> Point<T> {
         match axis {
             Axis::Horizontal => Point {
                 x: f(self.x.clone()),
@@ -434,11 +453,13 @@ impl Size<Pixels> {
     }
 }
 
-impl<T> Size<T>
+impl<T> Along for Size<T>
 where
     T: Clone + Default + Debug,
 {
-    pub fn along(&self, axis: Axis) -> T {
+    type Unit = T;
+
+    fn along(&self, axis: Axis) -> T {
         match axis {
             Axis::Horizontal => self.width.clone(),
             Axis::Vertical => self.height.clone(),
@@ -446,7 +467,7 @@ where
     }
 
     /// Returns the value of this size along the given axis.
-    pub fn apply_along(&self, axis: Axis, f: impl FnOnce(T) -> T) -> Self {
+    fn apply_along(&self, axis: Axis, f: impl FnOnce(T) -> T) -> Self {
         match axis {
             Axis::Horizontal => Size {
                 width: f(self.width.clone()),
@@ -1079,7 +1100,7 @@ where
     /// assert!(bounds.contains_point(&inside_point));
     /// assert!(!bounds.contains_point(&outside_point));
     /// ```
-    pub fn contains_point(&self, point: &Point<T>) -> bool {
+    pub fn contains(&self, point: &Point<T>) -> bool {
         point.x >= self.origin.x
             && point.x <= self.origin.x.clone() + self.size.width.clone()
             && point.y >= self.origin.y

crates/gpui2/src/interactive.rs 🔗

@@ -131,6 +131,12 @@ pub struct MouseMoveEvent {
     pub modifiers: Modifiers,
 }
 
+impl MouseMoveEvent {
+    pub fn dragging(&self) -> bool {
+        self.pressed_button == Some(MouseButton::Left)
+    }
+}
+
 #[derive(Clone, Debug)]
 pub struct ScrollWheelEvent {
     pub position: Point<Pixels>,

crates/gpui2/src/window.rs 🔗

@@ -60,6 +60,16 @@ pub enum DispatchPhase {
     Capture,
 }
 
+impl DispatchPhase {
+    pub fn bubble(self) -> bool {
+        self == DispatchPhase::Bubble
+    }
+
+    pub fn capture(self) -> bool {
+        self == DispatchPhase::Capture
+    }
+}
+
 type AnyObserver = Box<dyn FnMut(&mut WindowContext) -> bool + 'static>;
 type AnyMouseListener = Box<dyn FnMut(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>;
 type AnyFocusListener = Box<dyn Fn(&FocusEvent, &mut WindowContext) + 'static>;
@@ -859,7 +869,7 @@ impl<'a> WindowContext<'a> {
     /// same layer as the given stacking order.
     pub fn was_top_layer(&self, point: &Point<Pixels>, level: &StackingOrder) -> bool {
         for (stack, bounds) in self.window.rendered_frame.depth_map.iter() {
-            if bounds.contains_point(point) {
+            if bounds.contains(point) {
                 return level.starts_with(stack) || stack.starts_with(level);
             }
         }

crates/ui2/src/components/right_click_menu.rs 🔗

@@ -137,7 +137,7 @@ impl<M: ManagedView> Element for RightClickMenu<M> {
         cx.on_mouse_event(move |event: &MouseDownEvent, phase, cx| {
             if phase == DispatchPhase::Bubble
                 && event.button == MouseButton::Right
-                && bounds.contains_point(&event.position)
+                && bounds.contains(&event.position)
             {
                 cx.stop_propagation();
                 cx.prevent_default();

crates/workspace2/src/pane.rs 🔗

@@ -1040,10 +1040,11 @@ impl Pane {
                     {
                         pane.remove_item(item_ix, false, cx);
                     }
-                })?;
+                })
+                .ok();
             }
 
-            pane.update(&mut cx, |_, cx| cx.notify())?;
+            pane.update(&mut cx, |_, cx| cx.notify()).ok();
             Ok(())
         })
     }

crates/workspace2/src/pane_group.rs 🔗

@@ -12,7 +12,7 @@ use serde::Deserialize;
 use std::sync::Arc;
 use ui::{prelude::*, Button};
 
-const HANDLE_HITBOX_SIZE: f32 = 4.0;
+const HANDLE_HITBOX_SIZE: f32 = 10.0; //todo!(change this back to 4)
 const HORIZONTAL_MIN_SIZE: f32 = 80.;
 const VERTICAL_MIN_SIZE: f32 = 100.;
 
@@ -576,7 +576,7 @@ impl PaneAxis {
 
         for (idx, member) in self.members.iter().enumerate() {
             if let Some(coordinates) = bounding_boxes[idx] {
-                if coordinates.contains_point(&coordinate) {
+                if coordinates.contains(&coordinate) {
                     return match member {
                         Member::Pane(found) => Some(found),
                         Member::Axis(axis) => axis.pane_at_pixel_position(coordinate),
@@ -598,73 +598,41 @@ impl PaneAxis {
         cx: &mut ViewContext<Workspace>,
     ) -> gpui::AnyElement {
         debug_assert!(self.members.len() == self.flexes.lock().len());
+        let mut active_pane_ix = None;
 
-        pane_axis(self.axis, basis, self.flexes.clone())
-            .children(self.members.iter().enumerate().map(|(ix, member)| {
-                match member {
-                    Member::Axis(axis) => axis
-                        .render(
-                            project,
-                            basis,
-                            follower_states,
-                            active_pane,
-                            zoomed,
-                            app_state,
-                            cx,
-                        )
-                        .into_any_element(),
-                    Member::Pane(pane) => pane.clone().into_any_element(),
-                }
-            }))
-            .into_any_element()
-
-        // let mut pane_axis = PaneAxisElement::new(
-        //     self.axis,
-        //     basis,
-        //     self.flexes.clone(),
-        //     self.bounding_boxes.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_states,
-        //         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,
-        //         }
-
-        //         member = member.contained().with_border(border).into_any();
-        //     }
+        pane_axis(
+            self.axis,
+            basis,
+            self.flexes.clone(),
+            self.bounding_boxes.clone(),
+        )
+        .children(self.members.iter().enumerate().map(|(ix, member)| {
+            if member.contains(active_pane) {
+                active_pane_ix = Some(ix);
+            }
 
-        //     pane_axis = pane_axis.with_child(member.into_any());
-        // }
-        // pane_axis.set_active_pane(active_pane_ix);
-        // pane_axis.into_any()
+            match member {
+                Member::Axis(axis) => axis
+                    .render(
+                        project,
+                        (basis + ix) * 10,
+                        follower_states,
+                        active_pane,
+                        zoomed,
+                        app_state,
+                        cx,
+                    )
+                    .into_any_element(),
+                Member::Pane(pane) => div()
+                    .size_full()
+                    .border()
+                    .border_color(gpui::green())
+                    .child(pane.clone())
+                    .into_any_element(),
+            }
+        }))
+        .with_active_pane(active_pane_ix)
+        .into_any_element()
     }
 }
 
@@ -727,18 +695,31 @@ impl SplitDirection {
 }
 
 mod element {
-    use std::sync::Arc;
 
-    use gpui::{relative, AnyElement, Axis, Element, IntoElement, ParentElement, Style};
+    use std::{iter, sync::Arc};
+
+    use gpui::{
+        px, relative, Along, AnyElement, Axis, Bounds, CursorStyle, Element, IntoElement,
+        MouseMoveEvent, ParentElement, Pixels, Style, WindowContext,
+    };
     use parking_lot::Mutex;
     use smallvec::SmallVec;
 
-    pub fn pane_axis(axis: Axis, basis: usize, flexes: Arc<Mutex<Vec<f32>>>) -> PaneAxisElement {
+    use super::{HANDLE_HITBOX_SIZE, HORIZONTAL_MIN_SIZE, VERTICAL_MIN_SIZE};
+
+    pub fn pane_axis(
+        axis: Axis,
+        basis: usize,
+        flexes: Arc<Mutex<Vec<f32>>>,
+        bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
+    ) -> PaneAxisElement {
         PaneAxisElement {
             axis,
             basis,
             flexes,
+            bounding_boxes,
             children: SmallVec::new(),
+            active_pane_ix: None,
         }
     }
 
@@ -746,7 +727,145 @@ mod element {
         axis: Axis,
         basis: usize,
         flexes: Arc<Mutex<Vec<f32>>>,
+        bounding_boxes: Arc<Mutex<Vec<Option<Bounds<Pixels>>>>>,
         children: SmallVec<[AnyElement; 2]>,
+        active_pane_ix: Option<usize>,
+    }
+
+    impl PaneAxisElement {
+        pub fn with_active_pane(mut self, active_pane_ix: Option<usize>) -> Self {
+            self.active_pane_ix = active_pane_ix;
+            self
+        }
+
+        fn compute_resize(
+            flexes: &Arc<Mutex<Vec<f32>>>,
+            e: &MouseMoveEvent,
+            ix: usize,
+            axis: Axis,
+            axis_bounds: Bounds<Pixels>,
+            cx: &mut WindowContext,
+        ) {
+            let min_size = match axis {
+                Axis::Horizontal => px(HORIZONTAL_MIN_SIZE),
+                Axis::Vertical => px(VERTICAL_MIN_SIZE),
+            };
+            let mut flexes = flexes.lock();
+            debug_assert!(flex_values_in_bounds(flexes.as_slice()));
+
+            let size = move |ix, flexes: &[f32]| {
+                axis_bounds.size.along(axis) * (flexes[ix] / flexes.len() as f32)
+            };
+
+            // Don't allow resizing to less than the minimum size, if elements are already too small
+            if min_size - px(1.) > size(ix, flexes.as_slice()) {
+                return;
+            }
+
+            let mut proposed_current_pixel_change =
+                (e.position - axis_bounds.origin).along(axis) - size(ix, flexes.as_slice());
+
+            let flex_changes = |pixel_dx, target_ix, next: isize, flexes: &[f32]| {
+                let flex_change = pixel_dx / axis_bounds.size.along(axis);
+                let current_target_flex = flexes[target_ix] + flex_change;
+                let next_target_flex = flexes[(target_ix as isize + next) as usize] - flex_change;
+                (current_target_flex, next_target_flex)
+            };
+
+            let mut successors = iter::from_fn({
+                let forward = proposed_current_pixel_change > px(0.);
+                let mut ix_offset = 0;
+                let len = flexes.len();
+                move || {
+                    let result = if forward {
+                        (ix + 1 + ix_offset < len).then(|| ix + ix_offset)
+                    } else {
+                        (ix as isize - ix_offset as isize >= 0).then(|| ix - ix_offset)
+                    };
+
+                    ix_offset += 1;
+
+                    result
+                }
+            });
+
+            while dbg!(proposed_current_pixel_change).abs() > px(0.) {
+                let Some(current_ix) = successors.next() else {
+                    break;
+                };
+
+                dbg!(current_ix);
+
+                let next_target_size = Pixels::max(
+                    size(current_ix + 1, flexes.as_slice()) - proposed_current_pixel_change,
+                    min_size,
+                );
+
+                let current_target_size = Pixels::max(
+                    size(current_ix, flexes.as_slice()) + size(current_ix + 1, flexes.as_slice())
+                        - next_target_size,
+                    min_size,
+                );
+
+                let current_pixel_change =
+                    current_target_size - size(current_ix, flexes.as_slice());
+
+                let (current_target_flex, next_target_flex) =
+                    flex_changes(current_pixel_change, current_ix, 1, flexes.as_slice());
+
+                flexes[current_ix] = current_target_flex;
+                flexes[current_ix + 1] = next_target_flex;
+
+                proposed_current_pixel_change -= current_pixel_change;
+            }
+
+            // todo!(reserialize workspace)
+            // workspace.schedule_serialize(cx);
+            cx.notify();
+        }
+
+        fn push_handle(
+            flexes: Arc<Mutex<Vec<f32>>>,
+            axis: Axis,
+            ix: usize,
+            pane_bounds: Bounds<Pixels>,
+            axis_bounds: Bounds<Pixels>,
+            cx: &mut WindowContext,
+        ) {
+            let handle_bounds = Bounds {
+                origin: pane_bounds.origin.apply_along(axis, |o| {
+                    o + pane_bounds.size.along(axis) - Pixels(HANDLE_HITBOX_SIZE / 2.)
+                }),
+                size: pane_bounds
+                    .size
+                    .apply_along(axis, |_| Pixels(HANDLE_HITBOX_SIZE)),
+            };
+
+            cx.with_z_index(3, |cx| {
+                if handle_bounds.contains(&cx.mouse_position()) {
+                    cx.set_cursor_style(match axis {
+                        Axis::Vertical => CursorStyle::ResizeUpDown,
+                        Axis::Horizontal => CursorStyle::ResizeLeftRight,
+                    })
+                }
+
+                cx.add_opaque_layer(handle_bounds);
+
+                cx.paint_quad(
+                    handle_bounds,
+                    Default::default(),
+                    gpui::red(),
+                    Default::default(),
+                    gpui::red(),
+                );
+
+                cx.on_mouse_event(move |e: &MouseMoveEvent, phase, cx| {
+                    if phase.bubble() && e.dragging() && handle_bounds.contains(&e.position) {
+                        Self::compute_resize(&flexes, e, ix, axis, axis_bounds, cx)
+                    }
+                });
+            });
+        }
     }
 
     impl IntoElement for PaneAxisElement {
@@ -784,16 +903,47 @@ mod element {
             cx: &mut ui::prelude::WindowContext,
         ) {
             let flexes = self.flexes.lock().clone();
-            debug_assert!(flexes.len() == self.children.len());
-
-            let origin = bounds.origin;
-            let size = bounds.size;
             let len = self.children.len();
-            let child_size = size.apply_along(self.axis, |val| val / len as f32);
+            debug_assert!(flexes.len() == len);
+            debug_assert!(flex_values_in_bounds(flexes.as_slice()));
+
+            let mut origin = bounds.origin;
+            let space_per_flex = bounds.size.along(self.axis) / len as f32;
+
+            let mut bounding_boxes = self.bounding_boxes.lock();
+            bounding_boxes.clear();
+
             for (ix, child) in self.children.into_iter().enumerate() {
-                let origin =
-                    origin.apply_along(self.axis, |val| val + child_size.along(self.axis) * ix);
-                child.draw(origin, child_size.into(), cx);
+                //todo!(active_pane_magnification)
+                // If usign active pane magnification, need to switch to using
+                // 1 for all non-active panes, and then the magnification for the
+                // active pane.
+                let child_size = bounds
+                    .size
+                    .apply_along(self.axis, |_| space_per_flex * flexes[ix]);
+
+                let child_bounds = Bounds {
+                    origin,
+                    size: child_size,
+                };
+                bounding_boxes.push(Some(child_bounds));
+                cx.with_z_index(0, |cx| {
+                    child.draw(origin, child_size.into(), cx);
+                });
+                cx.with_z_index(1, |cx| {
+                    if ix < len - 1 {
+                        Self::push_handle(
+                            self.flexes.clone(),
+                            self.axis,
+                            ix,
+                            child_bounds,
+                            bounds,
+                            cx,
+                        );
+                    }
+                });
+
+                origin = origin.apply_along(self.axis, |val| val + child_size.along(self.axis));
             }
         }
     }
@@ -804,6 +954,9 @@ mod element {
         }
     }
 
+    fn flex_values_in_bounds(flexes: &[f32]) -> bool {
+        (flexes.iter().copied().sum::<f32>() - flexes.len() as f32).abs() < 0.001
+    }
     //     // use std::{cell::RefCell, iter::from_fn, ops::Range, rc::Rc};
 
     //     // use gpui::{

crates/workspace2/src/workspace2.rs 🔗

@@ -2015,7 +2015,7 @@ impl Workspace {
         };
         let cursor = self.active_pane.read(cx).pixel_position_of_cursor(cx);
         let center = match cursor {
-            Some(cursor) if bounding_box.contains_point(&cursor) => cursor,
+            Some(cursor) if bounding_box.contains(&cursor) => cursor,
             _ => bounding_box.center(),
         };