workspace: Fix panel resize handles leaking through zoomed panels (#9909)

Piotr Osiewicz created

Fixes #9501 

Release Notes:

- Fixed panel resize handle "leaking through" into a zoomed panel or
pane.

Change summary

crates/workspace/src/dock.rs      | 105 ++++++++++++++++++--------------
crates/workspace/src/workspace.rs |  23 ++++--
2 files changed, 74 insertions(+), 54 deletions(-)

Detailed changes

crates/workspace/src/dock.rs 🔗

@@ -1,6 +1,6 @@
 use crate::persistence::model::DockData;
-use crate::DraggedDock;
 use crate::{status_bar::StatusItemView, Workspace};
+use crate::{DraggedDock, Event};
 use gpui::{
     deferred, div, px, Action, AnchorCorner, AnyView, AppContext, Axis, ClickEvent, Entity,
     EntityId, EventEmitter, FocusHandle, FocusableView, IntoElement, KeyContext, MouseButton,
@@ -149,7 +149,8 @@ pub struct Dock {
     active_panel_index: usize,
     focus_handle: FocusHandle,
     pub(crate) serialized_dock: Option<DockData>,
-    _focus_subscription: Subscription,
+    resizeable: bool,
+    _subscriptions: [Subscription; 2],
 }
 
 impl FocusableView for Dock {
@@ -195,21 +196,28 @@ pub struct PanelButtons {
 impl Dock {
     pub fn new(position: DockPosition, cx: &mut ViewContext<Workspace>) -> View<Self> {
         let focus_handle = cx.focus_handle();
-
+        let workspace = cx.view().clone();
         let dock = cx.new_view(|cx: &mut ViewContext<Self>| {
             let focus_subscription = cx.on_focus(&focus_handle, |dock, cx| {
                 if let Some(active_entry) = dock.panel_entries.get(dock.active_panel_index) {
                     active_entry.panel.focus_handle(cx).focus(cx)
                 }
             });
+            let zoom_subscription = cx.subscribe(&workspace, |dock, workspace, e: &Event, cx| {
+                if matches!(e, Event::ZoomChanged) {
+                    let is_zoomed = workspace.read(cx).zoomed.is_some();
+                    dock.resizeable = !is_zoomed;
+                }
+            });
             Self {
                 position,
                 panel_entries: Default::default(),
                 active_panel_index: 0,
                 is_open: false,
                 focus_handle: focus_handle.clone(),
-                _focus_subscription: focus_subscription,
+                _subscriptions: [focus_subscription, zoom_subscription],
                 serialized_dock: None,
+                resizeable: true,
             }
         });
 
@@ -229,6 +237,7 @@ impl Dock {
                     workspace.zoomed = None;
                     workspace.zoomed_position = None;
                 }
+                cx.emit(Event::ZoomChanged);
                 workspace.dismiss_zoomed_items_to_reveal(Some(position), cx);
                 workspace.update_active_view_for_followers(cx)
             }
@@ -241,6 +250,7 @@ impl Dock {
                     if panel.is_zoomed(cx) {
                         workspace.zoomed = Some(panel.to_any().downgrade());
                         workspace.zoomed_position = Some(position);
+                        cx.emit(Event::ZoomChanged);
                         return;
                     }
                 }
@@ -248,6 +258,7 @@ impl Dock {
             if workspace.zoomed_position == Some(position) {
                 workspace.zoomed = None;
                 workspace.zoomed_position = None;
+                cx.emit(Event::ZoomChanged);
             }
         })
         .detach();
@@ -380,6 +391,7 @@ impl Dock {
                         .update(cx, |workspace, cx| {
                             workspace.zoomed = Some(panel.downgrade().into());
                             workspace.zoomed_position = Some(panel.read(cx).position(cx));
+                            cx.emit(Event::ZoomChanged);
                         })
                         .ok();
                 }
@@ -390,6 +402,7 @@ impl Dock {
                             if workspace.zoomed_position == Some(this.position) {
                                 workspace.zoomed = None;
                                 workspace.zoomed_position = None;
+                                cx.emit(Event::ZoomChanged);
                             }
                             cx.notify();
                         })
@@ -553,47 +566,49 @@ impl Render for Dock {
             let size = entry.panel.size(cx);
 
             let position = self.position;
-            let handle = div()
-                .id("resize-handle")
-                .on_drag(DraggedDock(position), |dock, cx| {
-                    cx.stop_propagation();
-                    cx.new_view(|_| dock.clone())
-                })
-                .on_click(cx.listener(|v, e: &ClickEvent, cx| {
-                    if e.down.button == MouseButton::Left && e.down.click_count == 2 {
-                        v.resize_active_panel(None, cx);
+            let create_resize_handle = || {
+                let handle = div()
+                    .id("resize-handle")
+                    .on_drag(DraggedDock(position), |dock, cx| {
                         cx.stop_propagation();
-                    }
-                }))
-                .occlude();
-            let handle = match self.position() {
-                DockPosition::Left => deferred(
-                    handle
-                        .absolute()
-                        .right(-RESIZE_HANDLE_SIZE / 2.)
-                        .top(px(0.))
-                        .h_full()
-                        .w(RESIZE_HANDLE_SIZE)
-                        .cursor_col_resize(),
-                ),
-                DockPosition::Bottom => deferred(
-                    handle
-                        .absolute()
-                        .top(-RESIZE_HANDLE_SIZE / 2.)
-                        .left(px(0.))
-                        .w_full()
-                        .h(RESIZE_HANDLE_SIZE)
-                        .cursor_row_resize(),
-                ),
-                DockPosition::Right => deferred(
-                    handle
-                        .absolute()
-                        .top(px(0.))
-                        .left(-RESIZE_HANDLE_SIZE / 2.)
-                        .h_full()
-                        .w(RESIZE_HANDLE_SIZE)
-                        .cursor_col_resize(),
-                ),
+                        cx.new_view(|_| dock.clone())
+                    })
+                    .on_click(cx.listener(|v, e: &ClickEvent, cx| {
+                        if e.down.button == MouseButton::Left && e.down.click_count == 2 {
+                            v.resize_active_panel(None, cx);
+                            cx.stop_propagation();
+                        }
+                    }))
+                    .occlude();
+                match self.position() {
+                    DockPosition::Left => deferred(
+                        handle
+                            .absolute()
+                            .right(-RESIZE_HANDLE_SIZE / 2.)
+                            .top(px(0.))
+                            .h_full()
+                            .w(RESIZE_HANDLE_SIZE)
+                            .cursor_col_resize(),
+                    ),
+                    DockPosition::Bottom => deferred(
+                        handle
+                            .absolute()
+                            .top(-RESIZE_HANDLE_SIZE / 2.)
+                            .left(px(0.))
+                            .w_full()
+                            .h(RESIZE_HANDLE_SIZE)
+                            .cursor_row_resize(),
+                    ),
+                    DockPosition::Right => deferred(
+                        handle
+                            .absolute()
+                            .top(px(0.))
+                            .left(-RESIZE_HANDLE_SIZE / 2.)
+                            .h_full()
+                            .w(RESIZE_HANDLE_SIZE)
+                            .cursor_col_resize(),
+                    ),
+                }
             };
 
             div()
@@ -625,7 +640,7 @@ impl Render for Dock {
                                 .cached(StyleRefinement::default().v_flex().size_full()),
                         ),
                 )
-                .child(handle)
+                .when(self.resizeable, |this| this.child(create_resize_handle()))
         } else {
             div()
                 .key_context(dispatch_context)

crates/workspace/src/workspace.rs 🔗

@@ -75,9 +75,9 @@ use theme::{ActiveTheme, SystemAppearance, ThemeSettings};
 pub use toolbar::{Toolbar, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView};
 pub use ui;
 use ui::{
-    div, Context as _, Div, Element, ElementContext, InteractiveElement as _, IntoElement, Label,
-    ParentElement as _, Pixels, SharedString, Styled as _, ViewContext, VisualContext as _,
-    WindowContext,
+    div, Context as _, Div, Element, ElementContext, FluentBuilder as _, InteractiveElement as _,
+    IntoElement, Label, ParentElement as _, Pixels, SharedString, Styled as _, ViewContext,
+    VisualContext as _, WindowContext,
 };
 use util::ResultExt;
 use uuid::Uuid;
@@ -520,6 +520,7 @@ pub enum Event {
     ContactRequestedJoin(u64),
     WorkspaceCreated(WeakView<Workspace>),
     SpawnTask(SpawnInTerminal),
+    ZoomChanged,
 }
 
 pub enum OpenVisible {
@@ -1913,6 +1914,7 @@ impl Workspace {
         if self.zoomed_position != dock_to_reveal {
             self.zoomed = None;
             self.zoomed_position = None;
+            cx.emit(Event::ZoomChanged);
         }
 
         cx.notify();
@@ -2341,6 +2343,7 @@ impl Workspace {
             self.zoomed = None;
         }
         self.zoomed_position = None;
+        cx.emit(Event::ZoomChanged);
         self.update_active_view_for_followers(cx);
 
         cx.notify();
@@ -2390,6 +2393,7 @@ impl Workspace {
                     if pane.read(cx).has_focus(cx) {
                         self.zoomed = Some(pane.downgrade().into());
                         self.zoomed_position = None;
+                        cx.emit(Event::ZoomChanged);
                     }
                     cx.notify();
                 }
@@ -2398,6 +2402,7 @@ impl Workspace {
                 pane.update(cx, |pane, cx| pane.set_zoomed(false, cx));
                 if self.zoomed_position.is_none() {
                     self.zoomed = None;
+                    cx.emit(Event::ZoomChanged);
                 }
                 cx.notify();
             }
@@ -3918,9 +3923,9 @@ impl Render for Workspace {
                         .absolute()
                         .size_full()
                     })
-                    .on_drag_move(
-                        cx.listener(|workspace, e: &DragMoveEvent<DraggedDock>, cx| {
-                            match e.drag(cx).0 {
+                    .when(self.zoomed.is_none(), |this| {
+                        this.on_drag_move(cx.listener(
+                            |workspace, e: &DragMoveEvent<DraggedDock>, cx| match e.drag(cx).0 {
                                 DockPosition::Left => {
                                     let size = workspace.bounds.left() + e.event.position.x;
                                     workspace.left_dock.update(cx, |left_dock, cx| {
@@ -3939,9 +3944,9 @@ impl Render for Workspace {
                                         bottom_dock.resize_active_panel(Some(size), cx);
                                     });
                                 }
-                            }
-                        }),
-                    )
+                            },
+                        ))
+                    })
                     .child(
                         div()
                             .flex()