From 1e47dfce79d98cdd65d734faef946e1bab840cec Mon Sep 17 00:00:00 2001 From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com> Date: Sat, 26 Apr 2025 15:02:07 +0200 Subject: [PATCH] debugger: Improve focus states (#29469) Closes #ISSUE Release Notes: - N/A *or* Added/Fixed/Improved ... --- crates/debugger_ui/src/session/running.rs | 34 +++++++++++++++-------- crates/tab_switcher/src/tab_switcher.rs | 1 + crates/workspace/src/item.rs | 10 ++++++- crates/workspace/src/pane.rs | 2 ++ 4 files changed, 34 insertions(+), 13 deletions(-) diff --git a/crates/debugger_ui/src/session/running.rs b/crates/debugger_ui/src/session/running.rs index 218a20fab6..45af03623a 100644 --- a/crates/debugger_ui/src/session/running.rs +++ b/crates/debugger_ui/src/session/running.rs @@ -54,7 +54,7 @@ pub struct RunningState { loaded_sources_list: Entity, pub debug_terminal: Entity, module_list: Entity, - _console: Entity, + console: Entity, breakpoint_list: Entity, panes: PaneGroup, active_pane: Option>, @@ -171,9 +171,10 @@ impl Render for SubView { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { v_flex() .size_full() + // Add border uncoditionally to prevent layout shifts on focus changes. + .border_1() .when(self.pane_focus_handle.contains_focused(window, cx), |el| { - // TODO better way of showing focus? - el.border_1().border_color(gpui::red()) + el.border_color(cx.theme().colors().pane_focused_border) }) .child(self.inner.clone()) } @@ -315,12 +316,12 @@ pub(crate) fn new_debugger_pane( .justify_between() .bg(cx.theme().colors().tab_bar_background) .border_b_1() + .px_2() .border_color(cx.theme().colors().border) .track_focus(&focus_handle) .child( h_flex() .w_full() - .px_2() .gap_1() .h(Tab::container_height(cx)) .drag_over::(|bar, _, _, cx| { @@ -336,6 +337,7 @@ pub(crate) fn new_debugger_pane( let selected = active_pane_item .as_ref() .map_or(false, |active| active.item_id() == item.item_id()); + let deemphasized = !pane.has_focus(window, cx); let item_ = item.boxed_clone(); div() .id(SharedString::from(format!( @@ -346,10 +348,17 @@ pub(crate) fn new_debugger_pane( .rounded_md() .cursor_pointer() .map(|this| { + let theme = cx.theme(); if selected { - this.bg(cx.theme().colors().tab_active_background) + let color = theme.colors().tab_active_background; + let color = if deemphasized { + color.opacity(0.5) + } else { + color + }; + this.bg(color) } else { - let hover_color = cx.theme().colors().element_hover; + let hover_color = theme.colors().element_hover; this.hover(|style| style.bg(hover_color)) } }) @@ -362,6 +371,7 @@ pub(crate) fn new_debugger_pane( .child(item.tab_content( TabContentParams { selected, + deemphasized, ..Default::default() }, window, @@ -395,7 +405,7 @@ pub(crate) fn new_debugger_pane( IconName::Maximize }, ) - .icon_size(IconSize::Small) + .icon_size(IconSize::XSmall) .on_click(cx.listener(move |pane, _, window, cx| { pane.toggle_zoom(&workspace::ToggleZoom, window, cx); })) @@ -585,7 +595,7 @@ impl RunningState { panes, active_pane: None, module_list, - _console: console, + console, breakpoint_list, loaded_sources_list: loaded_source_list, pane_close_subscriptions, @@ -629,11 +639,11 @@ impl RunningState { ) -> Box { match item_kind { DebuggerPaneItem::Console => { - let weak_console = self._console.clone().downgrade(); + let weak_console = self.console.clone().downgrade(); Box::new(SubView::new( - self._console.focus_handle(cx), - self._console.clone().into(), + self.console.focus_handle(cx), + self.console.clone().into(), item_kind, Some(Box::new(move |cx| { weak_console @@ -862,7 +872,7 @@ impl RunningState { #[cfg(test)] pub fn console(&self) -> &Entity { - &self._console + &self.console } #[cfg(test)] diff --git a/crates/tab_switcher/src/tab_switcher.rs b/crates/tab_switcher/src/tab_switcher.rs index b8912802d2..14553c016e 100644 --- a/crates/tab_switcher/src/tab_switcher.rs +++ b/crates/tab_switcher/src/tab_switcher.rs @@ -395,6 +395,7 @@ impl PickerDelegate for TabSwitcherDelegate { detail: Some(tab_match.detail), selected: true, preview: tab_match.preview, + deemphasized: false, }; let label = tab_match.item.tab_content(params, window, cx); diff --git a/crates/workspace/src/item.rs b/crates/workspace/src/item.rs index 6fef4d10ea..26440ce1e4 100644 --- a/crates/workspace/src/item.rs +++ b/crates/workspace/src/item.rs @@ -214,12 +214,20 @@ pub struct TabContentParams { pub detail: Option, pub selected: bool, pub preview: bool, + /// Tab content should be deemphasized when active pane does not have focus. + pub deemphasized: bool, } impl TabContentParams { /// Returns the text color to be used for the tab content. pub fn text_color(&self) -> Color { - if self.selected { + if self.deemphasized { + if self.selected { + Color::Muted + } else { + Color::Hidden + } + } else if self.selected { Color::Default } else { Color::Muted diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index c53a7dea2b..e3556a5ad2 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -2147,6 +2147,7 @@ impl Pane { detail: Some(detail), selected: is_active, preview: is_preview, + deemphasized: !self.has_focus(window, cx), }, window, cx, @@ -3687,6 +3688,7 @@ impl Render for DraggedTab { detail: Some(self.detail), selected: false, preview: false, + deemphasized: false, }, window, cx,