@@ -1,7 +1,9 @@
use crate::persistence::DebuggerPaneItem;
use crate::{
- ClearAllBreakpoints, Continue, CreateDebuggingSession, Disconnect, Pause, Restart, StepBack,
- StepInto, StepOut, StepOver, Stop, ToggleIgnoreBreakpoints, persistence,
+ ClearAllBreakpoints, Continue, CreateDebuggingSession, Disconnect, FocusBreakpointList,
+ FocusConsole, FocusFrames, FocusLoadedSources, FocusModules, FocusTerminal, FocusVariables,
+ Pause, Restart, StepBack, StepInto, StepOut, StepOver, Stop, ToggleIgnoreBreakpoints,
+ persistence,
};
use crate::{new_session_modal::NewSessionModal, session::DebugSession};
use anyhow::{Result, anyhow};
@@ -38,6 +40,7 @@ use task::{
};
use terminal_view::TerminalView;
use ui::{ContextMenu, Divider, DropdownMenu, Tooltip, prelude::*};
+use workspace::SplitDirection;
use workspace::{
Pane, Workspace,
dock::{DockPosition, Panel, PanelEvent},
@@ -790,6 +793,7 @@ impl DebugPanel {
fn top_controls_strip(&self, window: &mut Window, cx: &mut Context<Self>) -> Option<Div> {
let active_session = self.active_session.clone();
+ let focus_handle = self.focus_handle.clone();
Some(
h_flex()
@@ -821,8 +825,17 @@ impl DebugPanel {
this.pause_thread(cx);
},
))
- .tooltip(move |window, cx| {
- Tooltip::text("Pause program")(window, cx)
+ .tooltip({
+ let focus_handle = focus_handle.clone();
+ move |window, cx| {
+ Tooltip::for_action_in(
+ "Pause program",
+ &Pause,
+ &focus_handle,
+ window,
+ cx,
+ )
+ }
}),
)
} else {
@@ -835,8 +848,17 @@ impl DebugPanel {
|this, _, _window, cx| this.continue_thread(cx),
))
.disabled(thread_status != ThreadStatus::Stopped)
- .tooltip(move |window, cx| {
- Tooltip::text("Continue program")(window, cx)
+ .tooltip({
+ let focus_handle = focus_handle.clone();
+ move |window, cx| {
+ Tooltip::for_action_in(
+ "Continue program",
+ &Continue,
+ &focus_handle,
+ window,
+ cx,
+ )
+ }
}),
)
}
@@ -852,8 +874,17 @@ impl DebugPanel {
},
))
.disabled(thread_status != ThreadStatus::Stopped)
- .tooltip(move |window, cx| {
- Tooltip::text("Step over")(window, cx)
+ .tooltip({
+ let focus_handle = focus_handle.clone();
+ move |window, cx| {
+ Tooltip::for_action_in(
+ "Step over",
+ &StepOver,
+ &focus_handle,
+ window,
+ cx,
+ )
+ }
}),
)
.child(
@@ -867,8 +898,17 @@ impl DebugPanel {
},
))
.disabled(thread_status != ThreadStatus::Stopped)
- .tooltip(move |window, cx| {
- Tooltip::text("Step out")(window, cx)
+ .tooltip({
+ let focus_handle = focus_handle.clone();
+ move |window, cx| {
+ Tooltip::for_action_in(
+ "Step out",
+ &StepOut,
+ &focus_handle,
+ window,
+ cx,
+ )
+ }
}),
)
.child(
@@ -882,8 +922,17 @@ impl DebugPanel {
},
))
.disabled(thread_status != ThreadStatus::Stopped)
- .tooltip(move |window, cx| {
- Tooltip::text("Step in")(window, cx)
+ .tooltip({
+ let focus_handle = focus_handle.clone();
+ move |window, cx| {
+ Tooltip::for_action_in(
+ "Step in",
+ &StepInto,
+ &focus_handle,
+ window,
+ cx,
+ )
+ }
}),
)
.child(Divider::vertical())
@@ -916,8 +965,17 @@ impl DebugPanel {
this.toggle_ignore_breakpoints(cx);
},
))
- .tooltip(move |window, cx| {
- Tooltip::text("Disable all breakpoints")(window, cx)
+ .tooltip({
+ let focus_handle = focus_handle.clone();
+ move |window, cx| {
+ Tooltip::for_action_in(
+ "Disable all breakpoints",
+ &ToggleIgnoreBreakpoints,
+ &focus_handle,
+ window,
+ cx,
+ )
+ }
}),
)
.child(Divider::vertical())
@@ -930,8 +988,17 @@ impl DebugPanel {
this.restart_session(cx);
},
))
- .tooltip(move |window, cx| {
- Tooltip::text("Restart")(window, cx)
+ .tooltip({
+ let focus_handle = focus_handle.clone();
+ move |window, cx| {
+ Tooltip::for_action_in(
+ "Restart",
+ &Restart,
+ &focus_handle,
+ window,
+ cx,
+ )
+ }
}),
)
.child(
@@ -948,15 +1015,24 @@ impl DebugPanel {
&& thread_status != ThreadStatus::Running,
)
.tooltip({
+ let focus_handle = focus_handle.clone();
let label = if capabilities
.supports_terminate_threads_request
.unwrap_or_default()
{
"Terminate Thread"
} else {
- "Terminate all Threads"
+ "Terminate All Threads"
};
- move |window, cx| Tooltip::text(label)(window, cx)
+ move |window, cx| {
+ Tooltip::for_action_in(
+ label,
+ &Stop,
+ &focus_handle,
+ window,
+ cx,
+ )
+ }
}),
)
},
@@ -1006,19 +1082,57 @@ impl DebugPanel {
});
}
})
- .tooltip(|window, cx| {
- Tooltip::for_action(
- "New Debug Session",
- &CreateDebuggingSession,
- window,
- cx,
- )
+ .tooltip({
+ let focus_handle = focus_handle.clone();
+ move |window, cx| {
+ Tooltip::for_action_in(
+ "New Debug Session",
+ &CreateDebuggingSession,
+ &focus_handle,
+ window,
+ cx,
+ )
+ }
}),
),
),
)
}
+ fn activate_pane_in_direction(
+ &mut self,
+ direction: SplitDirection,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ if let Some(session) = self.active_session() {
+ session.update(cx, |session, cx| {
+ if let Some(running) = session.mode().as_running() {
+ running.update(cx, |running, cx| {
+ running.activate_pane_in_direction(direction, window, cx);
+ })
+ }
+ })
+ }
+ }
+
+ fn activate_item(
+ &mut self,
+ item: DebuggerPaneItem,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ if let Some(session) = self.active_session() {
+ session.update(cx, |session, cx| {
+ if let Some(running) = session.mode().as_running() {
+ running.update(cx, |running, cx| {
+ running.activate_item(item, window, cx);
+ })
+ }
+ })
+ }
+ }
+
fn activate_session(
&mut self,
session_item: Entity<DebugSession>,
@@ -1111,6 +1225,7 @@ impl Panel for DebugPanel {
impl Render for DebugPanel {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let has_sessions = self.sessions.len() > 0;
+ let this = cx.weak_entity();
debug_assert_eq!(has_sessions, self.active_session.is_some());
if self
@@ -1128,6 +1243,105 @@ impl Render for DebugPanel {
.key_context("DebugPanel")
.child(h_flex().children(self.top_controls_strip(window, cx)))
.track_focus(&self.focus_handle(cx))
+ .on_action({
+ let this = this.clone();
+ move |_: &workspace::ActivatePaneLeft, window, cx| {
+ this.update(cx, |this, cx| {
+ this.activate_pane_in_direction(SplitDirection::Left, window, cx);
+ })
+ .ok();
+ }
+ })
+ .on_action({
+ let this = this.clone();
+ move |_: &workspace::ActivatePaneRight, window, cx| {
+ this.update(cx, |this, cx| {
+ this.activate_pane_in_direction(SplitDirection::Right, window, cx);
+ })
+ .ok();
+ }
+ })
+ .on_action({
+ let this = this.clone();
+ move |_: &workspace::ActivatePaneUp, window, cx| {
+ this.update(cx, |this, cx| {
+ this.activate_pane_in_direction(SplitDirection::Up, window, cx);
+ })
+ .ok();
+ }
+ })
+ .on_action({
+ let this = this.clone();
+ move |_: &workspace::ActivatePaneDown, window, cx| {
+ this.update(cx, |this, cx| {
+ this.activate_pane_in_direction(SplitDirection::Down, window, cx);
+ })
+ .ok();
+ }
+ })
+ .on_action({
+ let this = this.clone();
+ move |_: &FocusConsole, window, cx| {
+ this.update(cx, |this, cx| {
+ this.activate_item(DebuggerPaneItem::Console, window, cx);
+ })
+ .ok();
+ }
+ })
+ .on_action({
+ let this = this.clone();
+ move |_: &FocusVariables, window, cx| {
+ this.update(cx, |this, cx| {
+ this.activate_item(DebuggerPaneItem::Variables, window, cx);
+ })
+ .ok();
+ }
+ })
+ .on_action({
+ let this = this.clone();
+ move |_: &FocusBreakpointList, window, cx| {
+ this.update(cx, |this, cx| {
+ this.activate_item(DebuggerPaneItem::BreakpointList, window, cx);
+ })
+ .ok();
+ }
+ })
+ .on_action({
+ let this = this.clone();
+ move |_: &FocusFrames, window, cx| {
+ this.update(cx, |this, cx| {
+ this.activate_item(DebuggerPaneItem::Frames, window, cx);
+ })
+ .ok();
+ }
+ })
+ .on_action({
+ let this = this.clone();
+ move |_: &FocusModules, window, cx| {
+ this.update(cx, |this, cx| {
+ this.activate_item(DebuggerPaneItem::Modules, window, cx);
+ })
+ .ok();
+ }
+ })
+ .on_action({
+ let this = this.clone();
+ move |_: &FocusLoadedSources, window, cx| {
+ this.update(cx, |this, cx| {
+ this.activate_item(DebuggerPaneItem::LoadedSources, window, cx);
+ })
+ .ok();
+ }
+ })
+ .on_action({
+ let this = this.clone();
+ move |_: &FocusTerminal, window, cx| {
+ this.update(cx, |this, cx| {
+ this.activate_item(DebuggerPaneItem::Terminal, window, cx);
+ })
+ .ok();
+ }
+ })
.when(self.active_session.is_some(), |this| {
this.on_mouse_down(
MouseButton::Right,
@@ -37,8 +37,8 @@ use ui::{
use util::ResultExt;
use variable_list::VariableList;
use workspace::{
- ActivePaneDecorator, DraggedTab, Item, ItemHandle, Member, Pane, PaneGroup, Workspace,
- item::TabContentParams, move_item, pane::Event,
+ ActivePaneDecorator, DraggedTab, Item, ItemHandle, Member, Pane, PaneGroup, SplitDirection,
+ Workspace, item::TabContentParams, move_item, pane::Event,
};
pub struct RunningState {
@@ -57,6 +57,7 @@ pub struct RunningState {
_console: Entity<Console>,
breakpoint_list: Entity<BreakpointList>,
panes: PaneGroup,
+ active_pane: Option<Entity<Pane>>,
pane_close_subscriptions: HashMap<EntityId, Subscription>,
_schedule_serialize: Option<Task<()>>,
}
@@ -167,8 +168,14 @@ impl Item for SubView {
}
impl Render for SubView {
- fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
- v_flex().size_full().child(self.inner.clone())
+ fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+ v_flex()
+ .size_full()
+ .when(self.pane_focus_handle.contains_focused(window, cx), |el| {
+ // TODO better way of showing focus?
+ el.border_1().border_color(gpui::red())
+ })
+ .child(self.inner.clone())
}
}
@@ -576,6 +583,7 @@ impl RunningState {
stack_frame_list,
session_id,
panes,
+ active_pane: None,
module_list,
_console: console,
breakpoint_list,
@@ -616,7 +624,7 @@ impl RunningState {
fn create_sub_view(
&self,
item_kind: DebuggerPaneItem,
- pane: &Entity<Pane>,
+ _pane: &Entity<Pane>,
cx: &mut Context<Self>,
) -> Box<dyn ItemHandle> {
match item_kind {
@@ -624,7 +632,7 @@ impl RunningState {
let weak_console = self._console.clone().downgrade();
Box::new(SubView::new(
- pane.focus_handle(cx),
+ self._console.focus_handle(cx),
self._console.clone().into(),
item_kind,
Some(Box::new(move |cx| {
@@ -784,6 +792,9 @@ impl RunningState {
debug_assert!(_did_find_pane);
cx.notify();
}
+ Event::Focus => {
+ this.active_pane = Some(source_pane.clone());
+ }
Event::ZoomIn => {
source_pane.update(cx, |pane, cx| {
pane.set_zoomed(true, cx);
@@ -800,6 +811,27 @@ impl RunningState {
}
}
+ pub(crate) fn activate_pane_in_direction(
+ &mut self,
+ direction: SplitDirection,
+ window: &mut Window,
+ cx: &mut Context<Self>,
+ ) {
+ if let Some(pane) = self
+ .active_pane
+ .as_ref()
+ .and_then(|pane| self.panes.find_pane_in_direction(pane, direction, cx))
+ {
+ window.focus(&pane.focus_handle(cx));
+ } else {
+ self.workspace
+ .update(cx, |workspace, cx| {
+ workspace.activate_pane_in_direction(direction, window, cx)
+ })
+ .ok();
+ }
+ }
+
pub(crate) fn go_to_selected_stack_frame(&self, window: &Window, cx: &mut Context<Self>) {
if self.thread_id.is_some() {
self.stack_frame_list
@@ -838,8 +870,7 @@ impl RunningState {
&self.module_list
}
- #[cfg(test)]
- pub(crate) fn activate_modules_list(&self, window: &mut Window, cx: &mut App) {
+ pub(crate) fn activate_item(&self, item: DebuggerPaneItem, window: &mut Window, cx: &mut App) {
let (variable_list_position, pane) = self
.panes
.panes()
@@ -847,7 +878,7 @@ impl RunningState {
.find_map(|pane| {
pane.read(cx)
.items_of_type::<SubView>()
- .position(|view| view.read(cx).view_kind().to_shared_string() == *"Modules")
+ .position(|view| view.read(cx).view_kind() == item)
.map(|view| (view, pane))
})
.unwrap();
@@ -855,6 +886,7 @@ impl RunningState {
this.activate_item(variable_list_position, true, true, window, cx);
})
}
+
#[cfg(test)]
pub(crate) fn variable_list(&self) -> &Entity<VariableList> {
&self.variable_list