debugger: Add refinements to the UI (#35940)

Took a little bit of time to add just a handful of small tweaks to the
debugger UI so it looks slightly more polished. This PR includes
adjustments to size, focus styles, and more in icon buttons, overall
spacing nudges in each section pane, making tooltip labels title case
(for overall consistency), and some icon SVG iteration.

Release Notes:

- N/A
This commit is contained in:
Danilo Leal 2025-08-10 15:23:27 -03:00 committed by GitHub
parent 95e302fa68
commit f3d6deb5a3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 263 additions and 204 deletions

View file

@ -36,7 +36,7 @@ use settings::Settings;
use std::sync::{Arc, LazyLock};
use task::{DebugScenario, TaskContext};
use tree_sitter::{Query, StreamingIterator as _};
use ui::{ContextMenu, Divider, PopoverMenuHandle, Tooltip, prelude::*};
use ui::{ContextMenu, Divider, PopoverMenuHandle, Tab, Tooltip, prelude::*};
use util::{ResultExt, debug_panic, maybe};
use workspace::SplitDirection;
use workspace::item::SaveOptions;
@ -642,12 +642,14 @@ impl DebugPanel {
}
})
};
let documentation_button = || {
IconButton::new("debug-open-documentation", IconName::CircleHelp)
.icon_size(IconSize::Small)
.on_click(move |_, _, cx| cx.open_url("https://zed.dev/docs/debugger"))
.tooltip(Tooltip::text("Open Documentation"))
};
let logs_button = || {
IconButton::new("debug-open-logs", IconName::Notepad)
.icon_size(IconSize::Small)
@ -658,16 +660,18 @@ impl DebugPanel {
};
Some(
div.border_b_1()
.border_color(cx.theme().colors().border)
.p_1()
div.w_full()
.py_1()
.px_1p5()
.justify_between()
.w_full()
.border_b_1()
.border_color(cx.theme().colors().border)
.when(is_side, |this| this.gap_1())
.child(
h_flex()
.justify_between()
.child(
h_flex().gap_2().w_full().when_some(
h_flex().gap_1().w_full().when_some(
active_session
.as_ref()
.map(|session| session.read(cx).running_state()),
@ -679,6 +683,7 @@ impl DebugPanel {
let capabilities = running_state.read(cx).capabilities(cx);
let supports_detach =
running_state.read(cx).session().read(cx).is_attached();
this.map(|this| {
if thread_status == ThreadStatus::Running {
this.child(
@ -686,8 +691,7 @@ impl DebugPanel {
"debug-pause",
IconName::DebugPause,
)
.icon_size(IconSize::XSmall)
.shape(ui::IconButtonShape::Square)
.icon_size(IconSize::Small)
.on_click(window.listener_for(
&running_state,
|this, _, _window, cx| {
@ -698,7 +702,7 @@ impl DebugPanel {
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
"Pause program",
"Pause Program",
&Pause,
&focus_handle,
window,
@ -713,8 +717,7 @@ impl DebugPanel {
"debug-continue",
IconName::DebugContinue,
)
.icon_size(IconSize::XSmall)
.shape(ui::IconButtonShape::Square)
.icon_size(IconSize::Small)
.on_click(window.listener_for(
&running_state,
|this, _, _window, cx| this.continue_thread(cx),
@ -724,7 +727,7 @@ impl DebugPanel {
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
"Continue program",
"Continue Program",
&Continue,
&focus_handle,
window,
@ -737,8 +740,7 @@ impl DebugPanel {
})
.child(
IconButton::new("debug-step-over", IconName::ArrowRight)
.icon_size(IconSize::XSmall)
.shape(ui::IconButtonShape::Square)
.icon_size(IconSize::Small)
.on_click(window.listener_for(
&running_state,
|this, _, _window, cx| {
@ -750,7 +752,7 @@ impl DebugPanel {
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
"Step over",
"Step Over",
&StepOver,
&focus_handle,
window,
@ -764,8 +766,7 @@ impl DebugPanel {
"debug-step-into",
IconName::ArrowDownRight,
)
.icon_size(IconSize::XSmall)
.shape(ui::IconButtonShape::Square)
.icon_size(IconSize::Small)
.on_click(window.listener_for(
&running_state,
|this, _, _window, cx| {
@ -777,7 +778,7 @@ impl DebugPanel {
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
"Step in",
"Step In",
&StepInto,
&focus_handle,
window,
@ -789,7 +790,6 @@ impl DebugPanel {
.child(
IconButton::new("debug-step-out", IconName::ArrowUpRight)
.icon_size(IconSize::Small)
.shape(ui::IconButtonShape::Square)
.on_click(window.listener_for(
&running_state,
|this, _, _window, cx| {
@ -801,7 +801,7 @@ impl DebugPanel {
let focus_handle = focus_handle.clone();
move |window, cx| {
Tooltip::for_action_in(
"Step out",
"Step Out",
&StepOut,
&focus_handle,
window,
@ -813,7 +813,7 @@ impl DebugPanel {
.child(Divider::vertical())
.child(
IconButton::new("debug-restart", IconName::RotateCcw)
.icon_size(IconSize::XSmall)
.icon_size(IconSize::Small)
.on_click(window.listener_for(
&running_state,
|this, _, window, cx| {
@ -835,7 +835,7 @@ impl DebugPanel {
)
.child(
IconButton::new("debug-stop", IconName::Power)
.icon_size(IconSize::XSmall)
.icon_size(IconSize::Small)
.on_click(window.listener_for(
&running_state,
|this, _, _window, cx| {
@ -890,7 +890,7 @@ impl DebugPanel {
thread_status != ThreadStatus::Stopped
&& thread_status != ThreadStatus::Running,
)
.icon_size(IconSize::XSmall)
.icon_size(IconSize::Small)
.on_click(window.listener_for(
&running_state,
|this, _, _, cx| {
@ -915,7 +915,6 @@ impl DebugPanel {
},
),
)
.justify_around()
.when(is_side, |this| {
this.child(new_session_button())
.child(logs_button())
@ -924,7 +923,7 @@ impl DebugPanel {
)
.child(
h_flex()
.gap_2()
.gap_0p5()
.when(is_side, |this| this.justify_between())
.child(
h_flex().when_some(
@ -954,12 +953,15 @@ impl DebugPanel {
)
})
})
.when(!is_side, |this| this.gap_2().child(Divider::vertical()))
.when(!is_side, |this| {
this.gap_0p5().child(Divider::vertical())
})
},
),
)
.child(
h_flex()
.gap_0p5()
.children(self.render_session_menu(
self.active_session(),
self.running_state(cx),
@ -1702,6 +1704,7 @@ impl Render for DebugPanel {
this.child(active_session)
} else {
let docked_to_bottom = self.position(window, cx) == DockPosition::Bottom;
let welcome_experience = v_flex()
.when_else(
docked_to_bottom,
@ -1767,54 +1770,58 @@ impl Render for DebugPanel {
);
}),
);
let breakpoint_list =
v_flex()
.group("base-breakpoint-list")
.items_start()
.when_else(
docked_to_bottom,
|this| this.min_w_1_3().h_full(),
|this| this.w_full().h_2_3(),
)
.p_1()
.child(
h_flex()
.pl_1()
.w_full()
.justify_between()
.child(Label::new("Breakpoints").size(LabelSize::Small))
.child(h_flex().visible_on_hover("base-breakpoint-list").child(
let breakpoint_list = v_flex()
.group("base-breakpoint-list")
.when_else(
docked_to_bottom,
|this| this.min_w_1_3().h_full(),
|this| this.size_full().h_2_3(),
)
.child(
h_flex()
.track_focus(&self.breakpoint_list.focus_handle(cx))
.h(Tab::container_height(cx))
.p_1p5()
.w_full()
.justify_between()
.border_b_1()
.border_color(cx.theme().colors().border_variant)
.child(Label::new("Breakpoints").size(LabelSize::Small))
.child(
h_flex().visible_on_hover("base-breakpoint-list").child(
self.breakpoint_list.read(cx).render_control_strip(),
))
.track_focus(&self.breakpoint_list.focus_handle(cx)),
)
.child(Divider::horizontal())
.child(self.breakpoint_list.clone());
),
),
)
.child(self.breakpoint_list.clone());
this.child(
v_flex()
.h_full()
.size_full()
.gap_1()
.items_center()
.justify_center()
.child(
div()
.when_else(docked_to_bottom, Div::h_flex, Div::v_flex)
.size_full()
.map(|this| {
if docked_to_bottom {
this.items_start()
.child(breakpoint_list)
.child(Divider::vertical())
.child(welcome_experience)
.child(Divider::vertical())
} else {
this.items_end()
.child(welcome_experience)
.child(Divider::horizontal())
.child(breakpoint_list)
}
}),
),
.map(|this| {
if docked_to_bottom {
this.child(
h_flex()
.size_full()
.child(breakpoint_list)
.child(Divider::vertical())
.child(welcome_experience)
.child(Divider::vertical()),
)
} else {
this.child(
v_flex()
.size_full()
.child(welcome_experience)
.child(Divider::horizontal())
.child(breakpoint_list),
)
}
}),
)
}
})

View file

@ -48,10 +48,8 @@ use task::{
};
use terminal_view::TerminalView;
use ui::{
ActiveTheme, AnyElement, App, ButtonCommon as _, Clickable as _, Context, FluentBuilder,
IconButton, IconName, IconSize, InteractiveElement, IntoElement, Label, LabelCommon as _,
ParentElement, Render, SharedString, StatefulInteractiveElement, Styled, Tab, Tooltip,
VisibleOnHover, VisualContext, Window, div, h_flex, v_flex,
FluentBuilder, IntoElement, Render, StatefulInteractiveElement, Tab, Tooltip, VisibleOnHover,
VisualContext, prelude::*,
};
use util::ResultExt;
use variable_list::VariableList;
@ -419,13 +417,14 @@ pub(crate) fn new_debugger_pane(
.map_or(false, |item| item.read(cx).hovered);
h_flex()
.group(pane_group_id.clone())
.justify_between()
.bg(cx.theme().colors().tab_bar_background)
.border_b_1()
.px_2()
.border_color(cx.theme().colors().border)
.track_focus(&focus_handle)
.group(pane_group_id.clone())
.pl_1p5()
.pr_1()
.justify_between()
.border_b_1()
.border_color(cx.theme().colors().border)
.bg(cx.theme().colors().tab_bar_background)
.on_action(|_: &menu::Cancel, window, cx| {
if cx.stop_active_drag(window) {
return;
@ -514,6 +513,7 @@ pub(crate) fn new_debugger_pane(
)
.child({
let zoomed = pane.is_zoomed();
h_flex()
.visible_on_hover(pane_group_id)
.when(is_hovered, |this| this.visible())
@ -537,7 +537,7 @@ pub(crate) fn new_debugger_pane(
IconName::Maximize
},
)
.icon_size(IconSize::XSmall)
.icon_size(IconSize::Small)
.on_click(cx.listener(move |pane, _, _, cx| {
let is_zoomed = pane.is_zoomed();
pane.set_zoomed(!is_zoomed, cx);
@ -592,10 +592,11 @@ impl DebugTerminal {
}
impl gpui::Render for DebugTerminal {
fn render(&mut self, _window: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
div()
.size_full()
.track_focus(&self.focus_handle)
.size_full()
.bg(cx.theme().colors().editor_background)
.children(self.terminal.clone())
}
}

View file

@ -23,11 +23,8 @@ use project::{
worktree_store::WorktreeStore,
};
use ui::{
ActiveTheme, AnyElement, App, ButtonCommon, Clickable, Color, Context, Disableable, Div,
Divider, FluentBuilder as _, Icon, IconButton, IconName, IconSize, InteractiveElement,
IntoElement, Label, LabelCommon, LabelSize, ListItem, ParentElement, Render, RenderOnce,
Scrollbar, ScrollbarState, SharedString, StatefulInteractiveElement, Styled, Toggleable,
Tooltip, Window, div, h_flex, px, v_flex,
Divider, DividerColor, FluentBuilder as _, Indicator, IntoElement, ListItem, Render, Scrollbar,
ScrollbarState, StatefulInteractiveElement, Tooltip, prelude::*,
};
use workspace::Workspace;
use zed_actions::{ToggleEnableBreakpoint, UnsetBreakpoint};
@ -569,6 +566,7 @@ impl BreakpointList {
.map(|session| SupportedBreakpointProperties::from(session.read(cx).capabilities()))
.unwrap_or_else(SupportedBreakpointProperties::empty);
let strip_mode = self.strip_mode;
uniform_list(
"breakpoint-list",
self.breakpoints.len(),
@ -591,7 +589,7 @@ impl BreakpointList {
}),
)
.track_scroll(self.scroll_handle.clone())
.flex_grow()
.flex_1()
}
fn render_vertical_scrollbar(&self, cx: &mut Context<Self>) -> Stateful<Div> {
@ -630,6 +628,7 @@ impl BreakpointList {
pub(crate) fn render_control_strip(&self) -> AnyElement {
let selection_kind = self.selection_kind();
let focus_handle = self.focus_handle.clone();
let remove_breakpoint_tooltip = selection_kind.map(|(kind, _)| match kind {
SelectedBreakpointKind::Source => "Remove breakpoint from a breakpoint list",
SelectedBreakpointKind::Exception => {
@ -637,6 +636,7 @@ impl BreakpointList {
}
SelectedBreakpointKind::Data => "Remove data breakpoint from a breakpoint list",
});
let toggle_label = selection_kind.map(|(_, is_enabled)| {
if is_enabled {
(
@ -649,13 +649,12 @@ impl BreakpointList {
});
h_flex()
.gap_2()
.child(
IconButton::new(
"disable-breakpoint-breakpoint-list",
IconName::DebugDisabledBreakpoint,
)
.icon_size(IconSize::XSmall)
.icon_size(IconSize::Small)
.when_some(toggle_label, |this, (label, meta)| {
this.tooltip({
let focus_handle = focus_handle.clone();
@ -681,9 +680,8 @@ impl BreakpointList {
}),
)
.child(
IconButton::new("remove-breakpoint-breakpoint-list", IconName::Close)
.icon_size(IconSize::XSmall)
.icon_color(ui::Color::Error)
IconButton::new("remove-breakpoint-breakpoint-list", IconName::Trash)
.icon_size(IconSize::Small)
.when_some(remove_breakpoint_tooltip, |this, tooltip| {
this.tooltip({
let focus_handle = focus_handle.clone();
@ -710,7 +708,6 @@ impl BreakpointList {
}
}),
)
.mr_2()
.into_any_element()
}
}
@ -791,6 +788,7 @@ impl Render for BreakpointList {
.chain(data_breakpoints)
.chain(exception_breakpoints),
);
v_flex()
.id("breakpoint-list")
.key_context("BreakpointList")
@ -806,35 +804,33 @@ impl Render for BreakpointList {
.on_action(cx.listener(Self::next_breakpoint_property))
.on_action(cx.listener(Self::previous_breakpoint_property))
.size_full()
.m_0p5()
.child(
v_flex()
.size_full()
.child(self.render_list(cx))
.child(self.render_vertical_scrollbar(cx)),
)
.pt_1()
.child(self.render_list(cx))
.child(self.render_vertical_scrollbar(cx))
.when_some(self.strip_mode, |this, _| {
this.child(Divider::horizontal()).child(
h_flex()
// .w_full()
.m_0p5()
.p_0p5()
.border_1()
.rounded_sm()
.when(
self.input.focus_handle(cx).contains_focused(window, cx),
|this| {
let colors = cx.theme().colors();
let border = if self.input.read(cx).read_only(cx) {
colors.border_disabled
} else {
colors.border_focused
};
this.border_color(border)
},
)
.child(self.input.clone()),
)
this.child(Divider::horizontal().color(DividerColor::Border))
.child(
h_flex()
.p_1()
.rounded_sm()
.bg(cx.theme().colors().editor_background)
.border_1()
.when(
self.input.focus_handle(cx).contains_focused(window, cx),
|this| {
let colors = cx.theme().colors();
let border_color = if self.input.read(cx).read_only(cx) {
colors.border_disabled
} else {
colors.border_transparent
};
this.border_color(border_color)
},
)
.child(self.input.clone()),
)
})
}
}
@ -865,12 +861,17 @@ impl LineBreakpoint {
let path = self.breakpoint.path.clone();
let row = self.breakpoint.row;
let is_enabled = self.breakpoint.state.is_enabled();
let indicator = div()
.id(SharedString::from(format!(
"breakpoint-ui-toggle-{:?}/{}:{}",
self.dir, self.name, self.line
)))
.cursor_pointer()
.child(
Icon::new(icon_name)
.color(Color::Debugger)
.size(IconSize::XSmall),
)
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
@ -902,17 +903,14 @@ impl LineBreakpoint {
.ok();
}
})
.child(
Icon::new(icon_name)
.color(Color::Debugger)
.size(IconSize::XSmall),
)
.on_mouse_down(MouseButton::Left, move |_, _, _| {});
ListItem::new(SharedString::from(format!(
"breakpoint-ui-item-{:?}/{}:{}",
self.dir, self.name, self.line
)))
.toggle_state(is_selected)
.inset(true)
.on_click({
let weak = weak.clone();
move |_, window, cx| {
@ -922,23 +920,20 @@ impl LineBreakpoint {
.ok();
}
})
.start_slot(indicator)
.rounded()
.on_secondary_mouse_down(|_, _, cx| {
cx.stop_propagation();
})
.start_slot(indicator)
.child(
h_flex()
.w_full()
.mr_4()
.py_0p5()
.gap_1()
.min_h(px(26.))
.justify_between()
.id(SharedString::from(format!(
"breakpoint-ui-on-click-go-to-line-{:?}/{}:{}",
self.dir, self.name, self.line
)))
.w_full()
.gap_1()
.min_h(rems_from_px(26.))
.justify_between()
.on_click({
let weak = weak.clone();
move |_, window, cx| {
@ -949,9 +944,9 @@ impl LineBreakpoint {
.ok();
}
})
.cursor_pointer()
.child(
h_flex()
.id("label-container")
.gap_0p5()
.child(
Label::new(format!("{}:{}", self.name, self.line))
@ -971,11 +966,13 @@ impl LineBreakpoint {
.line_height_style(ui::LineHeightStyle::UiLabel)
.truncate(),
)
})),
}))
.when_some(self.dir.as_ref(), |this, parent_dir| {
this.tooltip(Tooltip::text(format!(
"Worktree parent path: {parent_dir}"
)))
}),
)
.when_some(self.dir.as_ref(), |this, parent_dir| {
this.tooltip(Tooltip::text(format!("Worktree parent path: {parent_dir}")))
})
.child(BreakpointOptionsStrip {
props,
breakpoint: BreakpointEntry {
@ -988,15 +985,16 @@ impl LineBreakpoint {
index: ix,
}),
)
.toggle_state(is_selected)
}
}
#[derive(Clone, Debug)]
struct ExceptionBreakpoint {
id: String,
data: ExceptionBreakpointsFilter,
is_enabled: bool,
}
#[derive(Clone, Debug)]
struct DataBreakpoint(project::debugger::session::DataBreakpointState);
@ -1017,17 +1015,24 @@ impl DataBreakpoint {
};
let is_enabled = self.0.is_enabled;
let id = self.0.dap.data_id.clone();
ListItem::new(SharedString::from(format!(
"data-breakpoint-ui-item-{}",
self.0.dap.data_id
)))
.rounded()
.toggle_state(is_selected)
.inset(true)
.start_slot(
div()
.id(SharedString::from(format!(
"data-breakpoint-ui-item-{}-click-handler",
self.0.dap.data_id
)))
.child(
Icon::new(IconName::Binary)
.color(color)
.size(IconSize::Small),
)
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
@ -1052,25 +1057,18 @@ impl DataBreakpoint {
})
.ok();
}
})
.cursor_pointer()
.child(
Icon::new(IconName::Binary)
.color(color)
.size(IconSize::Small),
),
}),
)
.child(
h_flex()
.w_full()
.mr_4()
.py_0p5()
.gap_1()
.min_h(rems_from_px(26.))
.justify_between()
.child(
v_flex()
.py_1()
.gap_1()
.min_h(px(26.))
.justify_center()
.id(("data-breakpoint-label", ix))
.child(
@ -1091,7 +1089,6 @@ impl DataBreakpoint {
index: ix,
}),
)
.toggle_state(is_selected)
}
}
@ -1113,10 +1110,13 @@ impl ExceptionBreakpoint {
let id = SharedString::from(&self.id);
let is_enabled = self.is_enabled;
let weak = list.clone();
ListItem::new(SharedString::from(format!(
"exception-breakpoint-ui-item-{}",
self.id
)))
.toggle_state(is_selected)
.inset(true)
.on_click({
let list = list.clone();
move |_, window, cx| {
@ -1124,7 +1124,6 @@ impl ExceptionBreakpoint {
.ok();
}
})
.rounded()
.on_secondary_mouse_down(|_, _, cx| {
cx.stop_propagation();
})
@ -1134,6 +1133,11 @@ impl ExceptionBreakpoint {
"exception-breakpoint-ui-item-{}-click-handler",
self.id
)))
.child(
Icon::new(IconName::Flame)
.color(color)
.size(IconSize::Small),
)
.tooltip({
let focus_handle = focus_handle.clone();
move |window, cx| {
@ -1158,25 +1162,18 @@ impl ExceptionBreakpoint {
})
.ok();
}
})
.cursor_pointer()
.child(
Icon::new(IconName::Flame)
.color(color)
.size(IconSize::Small),
),
}),
)
.child(
h_flex()
.w_full()
.mr_4()
.py_0p5()
.gap_1()
.min_h(rems_from_px(26.))
.justify_between()
.child(
v_flex()
.py_1()
.gap_1()
.min_h(px(26.))
.justify_center()
.id(("exception-breakpoint-label", ix))
.child(
@ -1200,7 +1197,6 @@ impl ExceptionBreakpoint {
index: ix,
}),
)
.toggle_state(is_selected)
}
}
#[derive(Clone, Debug)]
@ -1302,6 +1298,7 @@ impl BreakpointEntry {
}
}
}
bitflags::bitflags! {
#[derive(Clone, Copy)]
pub struct SupportedBreakpointProperties: u32 {
@ -1360,6 +1357,7 @@ impl BreakpointOptionsStrip {
fn is_toggled(&self, expected_mode: ActiveBreakpointStripMode) -> bool {
self.is_selected && self.strip_mode == Some(expected_mode)
}
fn on_click_callback(
&self,
mode: ActiveBreakpointStripMode,
@ -1379,7 +1377,8 @@ impl BreakpointOptionsStrip {
.ok();
}
}
fn add_border(
fn add_focus_styles(
&self,
kind: ActiveBreakpointStripMode,
available: bool,
@ -1388,22 +1387,25 @@ impl BreakpointOptionsStrip {
) -> impl Fn(Div) -> Div {
move |this: Div| {
// Avoid layout shifts in case there's no colored border
let this = this.border_2().rounded_sm();
let this = this.border_1().rounded_sm();
let color = cx.theme().colors();
if self.is_selected && self.strip_mode == Some(kind) {
let theme = cx.theme().colors();
if self.focus_handle.is_focused(window) {
this.border_color(theme.border_selected)
this.bg(color.editor_background)
.border_color(color.border_focused)
} else {
this.border_color(theme.border_disabled)
this.border_color(color.border)
}
} else if !available {
this.border_color(cx.theme().colors().border_disabled)
this.border_color(color.border_transparent)
} else {
this
}
}
}
}
impl RenderOnce for BreakpointOptionsStrip {
fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement {
let id = self.breakpoint.id();
@ -1426,73 +1428,117 @@ impl RenderOnce for BreakpointOptionsStrip {
};
let color_for_toggle = |is_enabled| {
if is_enabled {
ui::Color::Default
Color::Default
} else {
ui::Color::Muted
Color::Muted
}
};
h_flex()
.gap_1()
.gap_px()
.mr_3() // Space to avoid overlapping with the scrollbar
.child(
div().map(self.add_border(ActiveBreakpointStripMode::Log, supports_logs, window, cx))
div()
.map(self.add_focus_styles(
ActiveBreakpointStripMode::Log,
supports_logs,
window,
cx,
))
.child(
IconButton::new(
SharedString::from(format!("{id}-log-toggle")),
IconName::Notepad,
)
.icon_size(IconSize::XSmall)
.shape(ui::IconButtonShape::Square)
.style(style_for_toggle(ActiveBreakpointStripMode::Log, has_logs))
.icon_size(IconSize::Small)
.icon_color(color_for_toggle(has_logs))
.when(has_logs, |this| this.indicator(Indicator::dot().color(Color::Info)))
.disabled(!supports_logs)
.toggle_state(self.is_toggled(ActiveBreakpointStripMode::Log))
.on_click(self.on_click_callback(ActiveBreakpointStripMode::Log)).tooltip(|window, cx| Tooltip::with_meta("Set Log Message", None, "Set log message to display (instead of stopping) when a breakpoint is hit", window, cx))
.on_click(self.on_click_callback(ActiveBreakpointStripMode::Log))
.tooltip(|window, cx| {
Tooltip::with_meta(
"Set Log Message",
None,
"Set log message to display (instead of stopping) when a breakpoint is hit.",
window,
cx,
)
}),
)
.when(!has_logs && !self.is_selected, |this| this.invisible()),
)
.child(
div().map(self.add_border(
ActiveBreakpointStripMode::Condition,
supports_condition,
window, cx
))
div()
.map(self.add_focus_styles(
ActiveBreakpointStripMode::Condition,
supports_condition,
window,
cx,
))
.child(
IconButton::new(
SharedString::from(format!("{id}-condition-toggle")),
IconName::SplitAlt,
)
.icon_size(IconSize::XSmall)
.shape(ui::IconButtonShape::Square)
.style(style_for_toggle(
ActiveBreakpointStripMode::Condition,
has_condition
has_condition,
))
.icon_size(IconSize::Small)
.icon_color(color_for_toggle(has_condition))
.when(has_condition, |this| this.indicator(Indicator::dot().color(Color::Info)))
.disabled(!supports_condition)
.toggle_state(self.is_toggled(ActiveBreakpointStripMode::Condition))
.on_click(self.on_click_callback(ActiveBreakpointStripMode::Condition))
.tooltip(|window, cx| Tooltip::with_meta("Set Condition", None, "Set condition to evaluate when a breakpoint is hit. Program execution will stop only when the condition is met", window, cx))
.tooltip(|window, cx| {
Tooltip::with_meta(
"Set Condition",
None,
"Set condition to evaluate when a breakpoint is hit. Program execution will stop only when the condition is met.",
window,
cx,
)
}),
)
.when(!has_condition && !self.is_selected, |this| this.invisible()),
)
.child(
div().map(self.add_border(
ActiveBreakpointStripMode::HitCondition,
supports_hit_condition,window, cx
))
div()
.map(self.add_focus_styles(
ActiveBreakpointStripMode::HitCondition,
supports_hit_condition,
window,
cx,
))
.child(
IconButton::new(
SharedString::from(format!("{id}-hit-condition-toggle")),
IconName::ArrowDown10,
)
.icon_size(IconSize::XSmall)
.style(style_for_toggle(
ActiveBreakpointStripMode::HitCondition,
has_hit_condition,
))
.shape(ui::IconButtonShape::Square)
.icon_size(IconSize::Small)
.icon_color(color_for_toggle(has_hit_condition))
.when(has_hit_condition, |this| this.indicator(Indicator::dot().color(Color::Info)))
.disabled(!supports_hit_condition)
.toggle_state(self.is_toggled(ActiveBreakpointStripMode::HitCondition))
.on_click(self.on_click_callback(ActiveBreakpointStripMode::HitCondition)).tooltip(|window, cx| Tooltip::with_meta("Set Hit Condition", None, "Set expression that controls how many hits of the breakpoint are ignored.", window, cx))
.on_click(self.on_click_callback(ActiveBreakpointStripMode::HitCondition))
.tooltip(|window, cx| {
Tooltip::with_meta(
"Set Hit Condition",
None,
"Set expression that controls how many hits of the breakpoint are ignored.",
window,
cx,
)
}),
)
.when(!has_hit_condition && !self.is_selected, |this| {
this.invisible()

View file

@ -367,7 +367,7 @@ impl Console {
.when_some(keybinding_target.clone(), |el, keybinding_target| {
el.context(keybinding_target.clone())
})
.action("Watch expression", WatchExpression.boxed_clone())
.action("Watch Expression", WatchExpression.boxed_clone())
}))
})
},
@ -452,18 +452,22 @@ impl Render for Console {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let query_focus_handle = self.query_bar.focus_handle(cx);
self.update_output(window, cx);
v_flex()
.track_focus(&self.focus_handle)
.key_context("DebugConsole")
.on_action(cx.listener(Self::evaluate))
.on_action(cx.listener(Self::watch_expression))
.size_full()
.border_2()
.bg(cx.theme().colors().editor_background)
.child(self.render_console(cx))
.when(self.is_running(cx), |this| {
this.child(Divider::horizontal()).child(
h_flex()
.on_action(cx.listener(Self::previous_query))
.on_action(cx.listener(Self::next_query))
.p_1()
.gap_1()
.bg(cx.theme().colors().editor_background)
.child(self.render_query_bar(cx))
@ -474,6 +478,9 @@ impl Render for Console {
.on_click(move |_, window, cx| {
window.dispatch_action(Box::new(Confirm), cx)
})
.layer(ui::ElevationIndex::ModalSurface)
.size(ui::ButtonSize::Compact)
.child(Label::new("Evaluate"))
.tooltip({
let query_focus_handle = query_focus_handle.clone();
@ -486,10 +493,7 @@ impl Render for Console {
cx,
)
}
})
.layer(ui::ElevationIndex::ModalSurface)
.size(ui::ButtonSize::Compact)
.child(Label::new("Evaluate")),
}),
self.render_submit_menu(
ElementId::Name("split-button-right-confirm-button".into()),
Some(query_focus_handle.clone()),
@ -499,7 +503,6 @@ impl Render for Console {
)),
)
})
.border_2()
}
}

View file

@ -18,10 +18,8 @@ use project::debugger::{MemoryCell, dap_command::DataBreakpointContext, session:
use settings::Settings;
use theme::ThemeSettings;
use ui::{
ActiveTheme, AnyElement, App, Color, Context, ContextMenu, Div, Divider, DropdownMenu, Element,
FluentBuilder, Icon, IconName, InteractiveElement, IntoElement, Label, LabelCommon,
ParentElement, Pixels, PopoverMenuHandle, Render, Scrollbar, ScrollbarState, SharedString,
StatefulInteractiveElement, Styled, TextSize, Tooltip, Window, div, h_flex, px, v_flex,
ContextMenu, Divider, DropdownMenu, FluentBuilder, IntoElement, PopoverMenuHandle, Render,
Scrollbar, ScrollbarState, StatefulInteractiveElement, Tooltip, prelude::*,
};
use workspace::Workspace;