debugger: Refine session modal design (#33004)

This PR makes all footer elements in the debugger session modal more
consistent, as well as fixes some weird UI quirks with leaking borders
and whatnot. Took the opportunity to do some light style clean up and
use `prelude::*` for UI imports.

Release Notes:

- N/A
This commit is contained in:
Danilo Leal 2025-06-19 02:27:34 -03:00 committed by GitHub
parent 804b91aa8c
commit 0e94ca2a1a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 122 additions and 136 deletions

View file

@ -28,12 +28,10 @@ use settings::{Settings, initial_local_debug_tasks_content};
use task::{DebugScenario, RevealTarget, ZedDebugConfig}; use task::{DebugScenario, RevealTarget, ZedDebugConfig};
use theme::ThemeSettings; use theme::ThemeSettings;
use ui::{ use ui::{
ActiveTheme, Button, ButtonCommon, ButtonSize, CheckboxWithLabel, Clickable, Color, Context, ActiveTheme, CheckboxWithLabel, Clickable, Context, ContextMenu, Disableable, DropdownMenu,
ContextMenu, Disableable, DropdownMenu, FluentBuilder, Icon, IconName, IconSize, FluentBuilder, IconWithIndicator, Indicator, IntoElement, KeyBinding, ListItem,
IconWithIndicator, Indicator, InteractiveElement, IntoElement, KeyBinding, Label, ListItemSpacing, ParentElement, StyledExt, ToggleButton, ToggleState, Toggleable, Tooltip,
LabelCommon as _, LabelSize, ListItem, ListItemSpacing, ParentElement, RenderOnce, Window, div, prelude::*, px, relative, rems,
SharedString, Styled, StyledExt, StyledTypography, ToggleButton, ToggleState, Toggleable,
Tooltip, Window, div, h_flex, px, relative, rems, v_flex,
}; };
use util::ResultExt; use util::ResultExt;
use workspace::{ModalView, Workspace, pane}; use workspace::{ModalView, Workspace, pane};
@ -683,16 +681,16 @@ impl Render for NewProcessModal {
cx: &mut ui::Context<Self>, cx: &mut ui::Context<Self>,
) -> impl ui::IntoElement { ) -> impl ui::IntoElement {
v_flex() v_flex()
.size_full()
.w(rems(34.))
.key_context({ .key_context({
let mut key_context = KeyContext::new_with_defaults(); let mut key_context = KeyContext::new_with_defaults();
key_context.add("Pane"); key_context.add("Pane");
key_context.add("RunModal"); key_context.add("RunModal");
key_context key_context
}) })
.size_full()
.w(rems(34.))
.elevation_3(cx) .elevation_3(cx)
.bg(cx.theme().colors().elevated_surface_background) .overflow_hidden()
.on_action(cx.listener(|_, _: &menu::Cancel, _, cx| { .on_action(cx.listener(|_, _: &menu::Cancel, _, cx| {
cx.emit(DismissEvent); cx.emit(DismissEvent);
})) }))
@ -720,100 +718,93 @@ impl Render for NewProcessModal {
) )
.child( .child(
h_flex() h_flex()
.w_full()
.justify_around()
.p_2() .p_2()
.child( .w_full()
h_flex() .border_b_1()
.justify_start()
.w_full()
.child(
ToggleButton::new(
"debugger-session-ui-tasks-button",
NewProcessMode::Task.to_string(),
)
.size(ButtonSize::Default)
.toggle_state(matches!(self.mode, NewProcessMode::Task))
.style(ui::ButtonStyle::Subtle)
.on_click(cx.listener(|this, _, window, cx| {
this.mode = NewProcessMode::Task;
this.mode_focus_handle(cx).focus(window);
cx.notify();
}))
.tooltip(Tooltip::text("Run predefined task"))
.first(),
)
.child(
ToggleButton::new(
"debugger-session-ui-launch-button",
NewProcessMode::Debug.to_string(),
)
.size(ButtonSize::Default)
.style(ui::ButtonStyle::Subtle)
.toggle_state(matches!(self.mode, NewProcessMode::Debug))
.on_click(cx.listener(|this, _, window, cx| {
this.mode = NewProcessMode::Debug;
this.mode_focus_handle(cx).focus(window);
cx.notify();
}))
.tooltip(Tooltip::text("Start a predefined debug scenario"))
.middle(),
)
.child(
ToggleButton::new(
"debugger-session-ui-attach-button",
NewProcessMode::Attach.to_string(),
)
.size(ButtonSize::Default)
.toggle_state(matches!(self.mode, NewProcessMode::Attach))
.style(ui::ButtonStyle::Subtle)
.on_click(cx.listener(|this, _, window, cx| {
this.mode = NewProcessMode::Attach;
if let Some(debugger) = this.debugger.as_ref() {
Self::update_attach_picker(
&this.attach_mode,
&debugger,
window,
cx,
);
}
this.mode_focus_handle(cx).focus(window);
cx.notify();
}))
.tooltip(Tooltip::text("Attach the debugger to a running process"))
.middle(),
)
.child(
ToggleButton::new(
"debugger-session-ui-custom-button",
NewProcessMode::Launch.to_string(),
)
.size(ButtonSize::Default)
.toggle_state(matches!(self.mode, NewProcessMode::Launch))
.style(ui::ButtonStyle::Subtle)
.on_click(cx.listener(|this, _, window, cx| {
this.mode = NewProcessMode::Launch;
this.mode_focus_handle(cx).focus(window);
cx.notify();
}))
.tooltip(Tooltip::text("Launch a new process with a debugger"))
.last(),
),
)
.justify_between()
.border_color(cx.theme().colors().border_variant) .border_color(cx.theme().colors().border_variant)
.border_b_1(), .child(
ToggleButton::new(
"debugger-session-ui-tasks-button",
NewProcessMode::Task.to_string(),
)
.size(ButtonSize::Default)
.toggle_state(matches!(self.mode, NewProcessMode::Task))
.style(ui::ButtonStyle::Subtle)
.on_click(cx.listener(|this, _, window, cx| {
this.mode = NewProcessMode::Task;
this.mode_focus_handle(cx).focus(window);
cx.notify();
}))
.tooltip(Tooltip::text("Run predefined task"))
.first(),
)
.child(
ToggleButton::new(
"debugger-session-ui-launch-button",
NewProcessMode::Debug.to_string(),
)
.size(ButtonSize::Default)
.style(ui::ButtonStyle::Subtle)
.toggle_state(matches!(self.mode, NewProcessMode::Debug))
.on_click(cx.listener(|this, _, window, cx| {
this.mode = NewProcessMode::Debug;
this.mode_focus_handle(cx).focus(window);
cx.notify();
}))
.tooltip(Tooltip::text("Start a predefined debug scenario"))
.middle(),
)
.child(
ToggleButton::new(
"debugger-session-ui-attach-button",
NewProcessMode::Attach.to_string(),
)
.size(ButtonSize::Default)
.toggle_state(matches!(self.mode, NewProcessMode::Attach))
.style(ui::ButtonStyle::Subtle)
.on_click(cx.listener(|this, _, window, cx| {
this.mode = NewProcessMode::Attach;
if let Some(debugger) = this.debugger.as_ref() {
Self::update_attach_picker(
&this.attach_mode,
&debugger,
window,
cx,
);
}
this.mode_focus_handle(cx).focus(window);
cx.notify();
}))
.tooltip(Tooltip::text("Attach the debugger to a running process"))
.middle(),
)
.child(
ToggleButton::new(
"debugger-session-ui-custom-button",
NewProcessMode::Launch.to_string(),
)
.size(ButtonSize::Default)
.toggle_state(matches!(self.mode, NewProcessMode::Launch))
.style(ui::ButtonStyle::Subtle)
.on_click(cx.listener(|this, _, window, cx| {
this.mode = NewProcessMode::Launch;
this.mode_focus_handle(cx).focus(window);
cx.notify();
}))
.tooltip(Tooltip::text("Launch a new process with a debugger"))
.last(),
),
) )
.child(v_flex().child(self.render_mode(window, cx))) .child(v_flex().child(self.render_mode(window, cx)))
.map(|el| { .map(|el| {
let container = h_flex() let container = h_flex()
.justify_between() .w_full()
.p_1p5()
.gap_2() .gap_2()
.p_2() .justify_between()
.border_color(cx.theme().colors().border_variant)
.border_t_1() .border_t_1()
.w_full(); .border_color(cx.theme().colors().border_variant);
match self.mode { match self.mode {
NewProcessMode::Launch => el.child( NewProcessMode::Launch => el.child(
container container
@ -825,7 +816,7 @@ impl Render for NewProcessModal {
InteractiveText::new( InteractiveText::new(
"open-debug-json", "open-debug-json",
StyledText::new( StyledText::new(
"Open .zed/debug.json for advanced configuration", "Open .zed/debug.json for advanced configuration.",
) )
.with_highlights([( .with_highlights([(
5..20, 5..20,
@ -1003,35 +994,43 @@ impl ConfigureMode {
v_flex() v_flex()
.p_2() .p_2()
.w_full() .w_full()
.gap_3() .gap_2()
.track_focus(&self.program.focus_handle(cx)) .track_focus(&self.program.focus_handle(cx))
.child( .child(
h_flex() h_flex()
.gap_2()
.child( .child(
Label::new("Debugger") Label::new("Debugger")
.size(ui::LabelSize::Small) .size(LabelSize::Small)
.color(Color::Muted), .color(Color::Muted),
) )
.gap(ui::DynamicSpacing::Base08.rems(cx))
.child(adapter_menu), .child(adapter_menu),
) )
.child( .child(
Label::new("Program") v_flex()
.size(ui::LabelSize::Small) .gap_0p5()
.color(Color::Muted), .child(
Label::new("Program")
.size(LabelSize::Small)
.color(Color::Muted),
)
.child(render_editor(&self.program, window, cx)),
) )
.child(render_editor(&self.program, window, cx))
.child( .child(
Label::new("Working Directory") v_flex()
.size(ui::LabelSize::Small) .gap_0p5()
.color(Color::Muted), .child(
Label::new("Working Directory")
.size(LabelSize::Small)
.color(Color::Muted),
)
.child(render_editor(&self.cwd, window, cx)),
) )
.child(render_editor(&self.cwd, window, cx))
.child( .child(
CheckboxWithLabel::new( CheckboxWithLabel::new(
"debugger-stop-on-entry", "debugger-stop-on-entry",
Label::new("Stop on Entry") Label::new("Stop on Entry")
.size(ui::LabelSize::Small) .size(LabelSize::Small)
.color(Color::Muted), .color(Color::Muted),
self.stop_on_entry, self.stop_on_entry,
{ {
@ -1050,7 +1049,7 @@ impl ConfigureMode {
CheckboxWithLabel::new( CheckboxWithLabel::new(
"debugger-save-to-debug-json", "debugger-save-to-debug-json",
Label::new("Save to debug.json") Label::new("Save to debug.json")
.size(ui::LabelSize::Small) .size(LabelSize::Small)
.color(Color::Muted), .color(Color::Muted),
self.save_to_debug_json, self.save_to_debug_json,
{ {
@ -1472,17 +1471,14 @@ impl PickerDelegate for DebugDelegate {
let current_modifiers = window.modifiers(); let current_modifiers = window.modifiers();
let footer = h_flex() let footer = h_flex()
.w_full() .w_full()
.h_8() .p_1p5()
.p_2() .justify_end()
.justify_between()
.rounded_b_sm()
.bg(cx.theme().colors().ghost_element_selected)
.border_t_1() .border_t_1()
.border_color(cx.theme().colors().border_variant) .border_color(cx.theme().colors().border_variant)
.child( // .child(
// TODO: add button to open selected task in debug.json // // TODO: add button to open selected task in debug.json
h_flex().into_any_element(), // h_flex().into_any_element(),
) // )
.map(|this| { .map(|this| {
if (current_modifiers.alt || self.matches.is_empty()) && !self.prompt.is_empty() { if (current_modifiers.alt || self.matches.is_empty()) && !self.prompt.is_empty() {
let action = picker::ConfirmInput { let action = picker::ConfirmInput {
@ -1491,7 +1487,6 @@ impl PickerDelegate for DebugDelegate {
.boxed_clone(); .boxed_clone();
this.children(KeyBinding::for_action(&*action, window, cx).map(|keybind| { this.children(KeyBinding::for_action(&*action, window, cx).map(|keybind| {
Button::new("launch-custom", "Launch Custom") Button::new("launch-custom", "Launch Custom")
.label_size(LabelSize::Small)
.key_binding(keybind) .key_binding(keybind)
.on_click(move |_, window, cx| { .on_click(move |_, window, cx| {
window.dispatch_action(action.boxed_clone(), cx) window.dispatch_action(action.boxed_clone(), cx)
@ -1506,7 +1501,6 @@ impl PickerDelegate for DebugDelegate {
if is_recent_selected { "Rerun" } else { "Spawn" }; if is_recent_selected { "Rerun" } else { "Spawn" };
Button::new("spawn", run_entry_label) Button::new("spawn", run_entry_label)
.label_size(LabelSize::Small)
.key_binding(keybind) .key_binding(keybind)
.on_click(|_, window, cx| { .on_click(|_, window, cx| {
window.dispatch_action(menu::Confirm.boxed_clone(), cx); window.dispatch_action(menu::Confirm.boxed_clone(), cx);

View file

@ -13,10 +13,9 @@ use picker::{Picker, PickerDelegate, highlighted_match_with_paths::HighlightedMa
use project::{TaskSourceKind, task_store::TaskStore}; use project::{TaskSourceKind, task_store::TaskStore};
use task::{DebugScenario, ResolvedTask, RevealTarget, TaskContext, TaskTemplate}; use task::{DebugScenario, ResolvedTask, RevealTarget, TaskContext, TaskTemplate};
use ui::{ use ui::{
ActiveTheme, Button, ButtonCommon, ButtonSize, Clickable, Color, FluentBuilder as _, Icon, ActiveTheme, Clickable, FluentBuilder as _, IconButtonShape, IconWithIndicator, Indicator,
IconButton, IconButtonShape, IconName, IconSize, IconWithIndicator, Indicator, IntoElement, IntoElement, KeyBinding, ListItem, ListItemSpacing, RenderOnce, Toggleable, Tooltip, div,
KeyBinding, Label, LabelSize, ListItem, ListItemSpacing, RenderOnce, Toggleable, Tooltip, div, prelude::*,
h_flex, v_flex,
}; };
use util::{ResultExt, truncate_and_trailoff}; use util::{ResultExt, truncate_and_trailoff};
@ -660,11 +659,8 @@ impl PickerDelegate for TasksModalDelegate {
Some( Some(
h_flex() h_flex()
.w_full() .w_full()
.h_8() .p_1p5()
.p_2()
.justify_between() .justify_between()
.rounded_b_sm()
.bg(cx.theme().colors().ghost_element_selected)
.border_t_1() .border_t_1()
.border_color(cx.theme().colors().border_variant) .border_color(cx.theme().colors().border_variant)
.child( .child(
@ -673,7 +669,6 @@ impl PickerDelegate for TasksModalDelegate {
let keybind = KeyBinding::for_action(&*action, window, cx); let keybind = KeyBinding::for_action(&*action, window, cx);
Button::new("edit-current-task", label) Button::new("edit-current-task", label)
.label_size(LabelSize::Small)
.when_some(keybind, |this, keybind| this.key_binding(keybind)) .when_some(keybind, |this, keybind| this.key_binding(keybind))
.on_click(move |_, window, cx| { .on_click(move |_, window, cx| {
window.dispatch_action(action.boxed_clone(), cx); window.dispatch_action(action.boxed_clone(), cx);
@ -697,7 +692,6 @@ impl PickerDelegate for TasksModalDelegate {
}; };
Button::new("spawn-onehshot", spawn_oneshot_label) Button::new("spawn-onehshot", spawn_oneshot_label)
.label_size(LabelSize::Small)
.key_binding(keybind) .key_binding(keybind)
.on_click(move |_, window, cx| { .on_click(move |_, window, cx| {
window.dispatch_action(action.boxed_clone(), cx) window.dispatch_action(action.boxed_clone(), cx)
@ -712,15 +706,14 @@ impl PickerDelegate for TasksModalDelegate {
} else { } else {
"Spawn Without History" "Spawn Without History"
}; };
Button::new("spawn", label) Button::new("spawn", label).key_binding(keybind).on_click(
.label_size(LabelSize::Small) move |_, window, cx| {
.key_binding(keybind)
.on_click(move |_, window, cx| {
window.dispatch_action( window.dispatch_action(
menu::SecondaryConfirm.boxed_clone(), menu::SecondaryConfirm.boxed_clone(),
cx, cx,
) )
}) },
)
}, },
), ),
) )
@ -731,7 +724,6 @@ impl PickerDelegate for TasksModalDelegate {
if is_recent_selected { "Rerun" } else { "Spawn" }; if is_recent_selected { "Rerun" } else { "Spawn" };
Button::new("spawn", run_entry_label) Button::new("spawn", run_entry_label)
.label_size(LabelSize::Small)
.key_binding(keybind) .key_binding(keybind)
.on_click(|_, window, cx| { .on_click(|_, window, cx| {
window.dispatch_action(menu::Confirm.boxed_clone(), cx); window.dispatch_action(menu::Confirm.boxed_clone(), cx);