debugger: Align zoom behavior with other panels (#31901)

Cole Miller created

Release Notes:

- Debugger Beta: `shift-escape` (`workspace::ToggleZoom`) now zooms the
entire debug panel; `alt-shift-escape` (`debugger::ToggleExpandItem`)
triggers the old behavior of zooming a specific item.

Change summary

assets/keymaps/default-linux.json         |  3 +
assets/keymaps/default-macos.json         |  3 +
crates/debugger_ui/src/debugger_panel.rs  | 48 +++++++++++++++++++++++-
crates/debugger_ui/src/debugger_ui.rs     |  1 
crates/debugger_ui/src/session/running.rs | 24 ++++--------
crates/workspace/src/pane.rs              | 11 +++++
6 files changed, 69 insertions(+), 21 deletions(-)

Detailed changes

assets/keymaps/default-linux.json 🔗

@@ -884,7 +884,8 @@
     "context": "DebugPanel",
     "bindings": {
       "ctrl-t": "debugger::ToggleThreadPicker",
-      "ctrl-i": "debugger::ToggleSessionPicker"
+      "ctrl-i": "debugger::ToggleSessionPicker",
+      "shift-alt-escape": "debugger::ToggleExpandItem"
     }
   },
   {

assets/keymaps/default-macos.json 🔗

@@ -949,7 +949,8 @@
     "context": "DebugPanel",
     "bindings": {
       "cmd-t": "debugger::ToggleThreadPicker",
-      "cmd-i": "debugger::ToggleSessionPicker"
+      "cmd-i": "debugger::ToggleSessionPicker",
+      "shift-alt-escape": "debugger::ToggleExpandItem"
     }
   },
   {

crates/debugger_ui/src/debugger_panel.rs 🔗

@@ -5,8 +5,8 @@ use crate::{
     ClearAllBreakpoints, Continue, Detach, FocusBreakpointList, FocusConsole, FocusFrames,
     FocusLoadedSources, FocusModules, FocusTerminal, FocusVariables, NewProcessModal,
     NewProcessMode, Pause, Restart, ShowStackTrace, StepBack, StepInto, StepOut, StepOver, Stop,
-    ToggleIgnoreBreakpoints, ToggleSessionPicker, ToggleThreadPicker, persistence,
-    spawn_task_or_modal,
+    ToggleExpandItem, ToggleIgnoreBreakpoints, ToggleSessionPicker, ToggleThreadPicker,
+    persistence, spawn_task_or_modal,
 };
 use anyhow::Result;
 use command_palette_hooks::CommandPaletteFilter;
@@ -70,6 +70,7 @@ pub struct DebugPanel {
     pub(crate) thread_picker_menu_handle: PopoverMenuHandle<ContextMenu>,
     pub(crate) session_picker_menu_handle: PopoverMenuHandle<ContextMenu>,
     fs: Arc<dyn Fs>,
+    is_zoomed: bool,
     _subscriptions: [Subscription; 1],
 }
 
@@ -104,6 +105,7 @@ impl DebugPanel {
                 fs: workspace.app_state().fs.clone(),
                 thread_picker_menu_handle,
                 session_picker_menu_handle,
+                is_zoomed: false,
                 _subscriptions: [focus_subscription],
                 debug_scenario_scheduled_last: true,
             }
@@ -1021,6 +1023,22 @@ impl DebugPanel {
     pub(crate) fn toggle_session_picker(&mut self, window: &mut Window, cx: &mut Context<Self>) {
         self.session_picker_menu_handle.toggle(window, cx);
     }
+
+    fn toggle_zoom(
+        &mut self,
+        _: &workspace::ToggleZoom,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) {
+        if self.is_zoomed {
+            cx.emit(PanelEvent::ZoomOut);
+        } else {
+            if !self.focus_handle(cx).contains_focused(window, cx) {
+                cx.focus_self(window);
+            }
+            cx.emit(PanelEvent::ZoomIn);
+        }
+    }
 }
 
 async fn register_session_inner(
@@ -1176,6 +1194,15 @@ impl Panel for DebugPanel {
     }
 
     fn set_active(&mut self, _: bool, _: &mut Window, _: &mut Context<Self>) {}
+
+    fn is_zoomed(&self, _window: &Window, _cx: &App) -> bool {
+        self.is_zoomed
+    }
+
+    fn set_zoomed(&mut self, zoomed: bool, _window: &mut Window, cx: &mut Context<Self>) {
+        self.is_zoomed = zoomed;
+        cx.notify();
+    }
 }
 
 impl Render for DebugPanel {
@@ -1316,6 +1343,23 @@ impl Render for DebugPanel {
                     .ok();
                 }
             })
+            .on_action(cx.listener(Self::toggle_zoom))
+            .on_action(cx.listener(|panel, _: &ToggleExpandItem, _, cx| {
+                let Some(session) = panel.active_session() else {
+                    return;
+                };
+                let active_pane = session
+                    .read(cx)
+                    .running_state()
+                    .read(cx)
+                    .active_pane()
+                    .clone();
+                active_pane.update(cx, |pane, cx| {
+                    let is_zoomed = pane.is_zoomed();
+                    pane.set_zoomed(!is_zoomed, cx);
+                });
+                cx.notify();
+            }))
             .when(self.active_session.is_some(), |this| {
                 this.on_mouse_down(
                     MouseButton::Right,

crates/debugger_ui/src/session/running.rs 🔗

@@ -8,6 +8,7 @@ pub mod variable_list;
 use std::{any::Any, ops::ControlFlow, path::PathBuf, sync::Arc, time::Duration};
 
 use crate::{
+    ToggleExpandItem,
     new_process_modal::resolve_path,
     persistence::{self, DebuggerPaneItem, SerializedLayout},
 };
@@ -347,6 +348,7 @@ pub(crate) fn new_debugger_pane(
                 false
             }
         })));
+        pane.set_can_toggle_zoom(false, cx);
         pane.display_nav_history_buttons(None);
         pane.set_custom_drop_handle(cx, custom_drop_handle);
         pane.set_should_display_tab_bar(|_, _| true);
@@ -472,17 +474,19 @@ pub(crate) fn new_debugger_pane(
                                     },
                                 )
                                 .icon_size(IconSize::XSmall)
-                                .on_click(cx.listener(move |pane, _, window, cx| {
-                                    pane.toggle_zoom(&workspace::ToggleZoom, window, cx);
+                                .on_click(cx.listener(move |pane, _, _, cx| {
+                                    let is_zoomed = pane.is_zoomed();
+                                    pane.set_zoomed(!is_zoomed, cx);
+                                    cx.notify();
                                 }))
                                 .tooltip({
                                     let focus_handle = focus_handle.clone();
                                     move |window, cx| {
                                         let zoomed_text =
-                                            if zoomed { "Zoom Out" } else { "Zoom In" };
+                                            if zoomed { "Minimize" } else { "Expand" };
                                         Tooltip::for_action_in(
                                             zoomed_text,
-                                            &workspace::ToggleZoom,
+                                            &ToggleExpandItem,
                                             &focus_handle,
                                             window,
                                             cx,
@@ -1260,18 +1264,6 @@ impl RunningState {
             Event::Focus => {
                 this.active_pane = source_pane.clone();
             }
-            Event::ZoomIn => {
-                source_pane.update(cx, |pane, cx| {
-                    pane.set_zoomed(true, cx);
-                });
-                cx.notify();
-            }
-            Event::ZoomOut => {
-                source_pane.update(cx, |pane, cx| {
-                    pane.set_zoomed(false, cx);
-                });
-                cx.notify();
-            }
             _ => {}
         }
     }

crates/workspace/src/pane.rs 🔗

@@ -311,6 +311,7 @@ pub struct Pane {
     >,
     can_split_predicate:
         Option<Arc<dyn Fn(&mut Self, &dyn Any, &mut Window, &mut Context<Self>) -> bool>>,
+    can_toggle_zoom: bool,
     should_display_tab_bar: Rc<dyn Fn(&Window, &mut Context<Pane>) -> bool>,
     render_tab_bar_buttons: Rc<
         dyn Fn(
@@ -450,6 +451,7 @@ impl Pane {
             can_drop_predicate,
             custom_drop_handle: None,
             can_split_predicate: None,
+            can_toggle_zoom: true,
             should_display_tab_bar: Rc::new(|_, cx| TabBarSettings::get_global(cx).show),
             render_tab_bar_buttons: Rc::new(default_render_tab_bar_buttons),
             render_tab_bar: Rc::new(Self::render_tab_bar),
@@ -650,6 +652,11 @@ impl Pane {
         self.can_split_predicate = can_split_predicate;
     }
 
+    pub fn set_can_toggle_zoom(&mut self, can_toggle_zoom: bool, cx: &mut Context<Self>) {
+        self.can_toggle_zoom = can_toggle_zoom;
+        cx.notify();
+    }
+
     pub fn set_close_pane_if_empty(&mut self, close_pane_if_empty: bool, cx: &mut Context<Self>) {
         self.close_pane_if_empty = close_pane_if_empty;
         cx.notify();
@@ -1104,7 +1111,9 @@ impl Pane {
     }
 
     pub fn toggle_zoom(&mut self, _: &ToggleZoom, window: &mut Window, cx: &mut Context<Self>) {
-        if self.zoomed {
+        if !self.can_toggle_zoom {
+            cx.propagate();
+        } else if self.zoomed {
             cx.emit(Event::ZoomOut);
         } else if !self.items.is_empty() {
             if !self.focus_handle.contains_focused(window, cx) {