debugger: Remember focused item (#30722)

Cole Miller created

Release Notes:

- Debugger Beta: the `debug panel: toggle focus` action now preserves
the debug panel's focused item.

Change summary

crates/debugger_ui/src/debugger_panel.rs  | 31 +++++++++++++++++++++---
crates/debugger_ui/src/debugger_ui.rs     | 11 ++++++++
crates/debugger_ui/src/session/running.rs | 21 ++++++++--------
crates/workspace/src/workspace.rs         | 13 ++++++++--
4 files changed, 57 insertions(+), 19 deletions(-)

Detailed changes

crates/debugger_ui/src/debugger_panel.rs 🔗

@@ -69,15 +69,20 @@ pub struct DebugPanel {
 }
 
 impl DebugPanel {
-    pub fn new(workspace: &Workspace, cx: &mut Context<Workspace>) -> Entity<Self> {
+    pub fn new(
+        workspace: &Workspace,
+        _window: &mut Window,
+        cx: &mut Context<Workspace>,
+    ) -> Entity<Self> {
         cx.new(|cx| {
             let project = workspace.project().clone();
+            let focus_handle = cx.focus_handle();
 
             let debug_panel = Self {
                 size: px(300.),
                 sessions: vec![],
                 active_session: None,
-                focus_handle: cx.focus_handle(),
+                focus_handle,
                 project,
                 workspace: workspace.weak_handle(),
                 context_menu: None,
@@ -88,6 +93,24 @@ impl DebugPanel {
         })
     }
 
+    pub(crate) fn focus_active_item(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+        let Some(session) = self.active_session.clone() else {
+            return;
+        };
+        let Some(active_pane) = session
+            .read(cx)
+            .running_state()
+            .read(cx)
+            .active_pane()
+            .cloned()
+        else {
+            return;
+        };
+        active_pane.update(cx, |pane, cx| {
+            pane.focus_active_item(window, cx);
+        });
+    }
+
     pub(crate) fn sessions(&self) -> Vec<Entity<DebugSession>> {
         self.sessions.clone()
     }
@@ -182,8 +205,8 @@ impl DebugPanel {
         cx: &mut AsyncWindowContext,
     ) -> Task<Result<Entity<Self>>> {
         cx.spawn(async move |cx| {
-            workspace.update(cx, |workspace, cx| {
-                let debug_panel = DebugPanel::new(workspace, cx);
+            workspace.update_in(cx, |workspace, window, cx| {
+                let debug_panel = DebugPanel::new(workspace, window, cx);
 
                 workspace.register_action(|workspace, _: &ClearAllBreakpoints, _, cx| {
                     workspace.project().read(cx).breakpoint_store().update(

crates/debugger_ui/src/debugger_ui.rs 🔗

@@ -60,7 +60,16 @@ pub fn init(cx: &mut App) {
         cx.when_flag_enabled::<DebuggerFeatureFlag>(window, |workspace, _, _| {
             workspace
                 .register_action(|workspace, _: &ToggleFocus, window, cx| {
-                    workspace.toggle_panel_focus::<DebugPanel>(window, cx);
+                    let did_focus_panel = workspace.toggle_panel_focus::<DebugPanel>(window, cx);
+                    if !did_focus_panel {
+                        return;
+                    };
+                    let Some(panel) = workspace.panel::<DebugPanel>(cx) else {
+                        return;
+                    };
+                    panel.update(cx, |panel, cx| {
+                        panel.focus_active_item(window, cx);
+                    })
                 })
                 .register_action(|workspace, _: &Pause, _, cx| {
                     if let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) {

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

@@ -81,6 +81,10 @@ impl RunningState {
     pub(crate) fn thread_id(&self) -> Option<ThreadId> {
         self.thread_id
     }
+
+    pub(crate) fn active_pane(&self) -> Option<&Entity<Pane>> {
+        self.active_pane.as_ref()
+    }
 }
 
 impl Render for RunningState {
@@ -502,20 +506,15 @@ impl DebugTerminal {
 
 impl gpui::Render for DebugTerminal {
     fn render(&mut self, _window: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
-        if let Some(terminal) = self.terminal.clone() {
-            terminal.into_any_element()
-        } else {
-            div().track_focus(&self.focus_handle).into_any_element()
-        }
+        div()
+            .size_full()
+            .track_focus(&self.focus_handle)
+            .children(self.terminal.clone())
     }
 }
 impl Focusable for DebugTerminal {
-    fn focus_handle(&self, cx: &App) -> FocusHandle {
-        if let Some(terminal) = self.terminal.as_ref() {
-            return terminal.focus_handle(cx);
-        } else {
-            self.focus_handle.clone()
-        }
+    fn focus_handle(&self, _cx: &App) -> FocusHandle {
+        self.focus_handle.clone()
     }
 }
 

crates/workspace/src/workspace.rs 🔗

@@ -2776,10 +2776,17 @@ impl Workspace {
 
     /// Focus the panel of the given type if it isn't already focused. If it is
     /// already focused, then transfer focus back to the workspace center.
-    pub fn toggle_panel_focus<T: Panel>(&mut self, window: &mut Window, cx: &mut Context<Self>) {
+    pub fn toggle_panel_focus<T: Panel>(
+        &mut self,
+        window: &mut Window,
+        cx: &mut Context<Self>,
+    ) -> bool {
+        let mut did_focus_panel = false;
         self.focus_or_unfocus_panel::<T>(window, cx, |panel, window, cx| {
-            !panel.panel_focus_handle(cx).contains_focused(window, cx)
+            did_focus_panel = !panel.panel_focus_handle(cx).contains_focused(window, cx);
+            did_focus_panel
         });
+        did_focus_panel
     }
 
     pub fn activate_panel_for_proto_id(
@@ -2813,7 +2820,7 @@ impl Workspace {
         &mut self,
         window: &mut Window,
         cx: &mut Context<Self>,
-        should_focus: impl Fn(&dyn PanelHandle, &mut Window, &mut Context<Dock>) -> bool,
+        mut should_focus: impl FnMut(&dyn PanelHandle, &mut Window, &mut Context<Dock>) -> bool,
     ) -> Option<Arc<dyn PanelHandle>> {
         let mut result_panel = None;
         let mut serialize = false;