debugger: Add horizontal scroll bars to variable list, memory view, and breakpoint list (#41594)
Closes #40360 This PR added heuristics to determine what variable/breakpoint list entry has the longest width when rendered. I added this in so the uniform list would correctly determine which item has the longest width and use that to calculate the scrollbar size. The heuristic can be off if a non-mono space font is used in the UI; in most cases, it's more than accurate enough though. Release Notes: - debugger: Add horizontal scroll bars to variable list, memory view, and breakpoint list --------- Co-authored-by: MrSubidubi <dev@bahn.sh>
This commit is contained in:
co-authored by
MrSubidubi
parent
3944234bab
commit
44e5a962e6
@@ -12,6 +12,7 @@ use gpui::{
|
||||
Action, AppContext, ClickEvent, Entity, FocusHandle, Focusable, MouseButton, ScrollStrategy,
|
||||
Task, UniformListScrollHandle, WeakEntity, actions, uniform_list,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use language::Point;
|
||||
use project::{
|
||||
Project,
|
||||
@@ -24,7 +25,7 @@ use project::{
|
||||
};
|
||||
use ui::{
|
||||
Divider, DividerColor, FluentBuilder as _, Indicator, IntoElement, ListItem, Render,
|
||||
StatefulInteractiveElement, Tooltip, WithScrollbar, prelude::*,
|
||||
ScrollAxes, StatefulInteractiveElement, Tooltip, WithScrollbar, prelude::*,
|
||||
};
|
||||
use util::rel_path::RelPath;
|
||||
use workspace::Workspace;
|
||||
@@ -55,6 +56,7 @@ pub(crate) struct BreakpointList {
|
||||
focus_handle: FocusHandle,
|
||||
scroll_handle: UniformListScrollHandle,
|
||||
selected_ix: Option<usize>,
|
||||
max_width_index: Option<usize>,
|
||||
input: Entity<Editor>,
|
||||
strip_mode: Option<ActiveBreakpointStripMode>,
|
||||
serialize_exception_breakpoints_task: Option<Task<anyhow::Result<()>>>,
|
||||
@@ -95,6 +97,7 @@ impl BreakpointList {
|
||||
dap_store,
|
||||
worktree_store,
|
||||
breakpoints: Default::default(),
|
||||
max_width_index: None,
|
||||
workspace,
|
||||
session,
|
||||
focus_handle,
|
||||
@@ -570,6 +573,8 @@ impl BreakpointList {
|
||||
.collect()
|
||||
}),
|
||||
)
|
||||
.with_horizontal_sizing_behavior(gpui::ListHorizontalSizingBehavior::Unconstrained)
|
||||
.with_width_from_item(self.max_width_index)
|
||||
.track_scroll(self.scroll_handle.clone())
|
||||
.flex_1()
|
||||
}
|
||||
@@ -732,6 +737,26 @@ impl Render for BreakpointList {
|
||||
.chain(exception_breakpoints),
|
||||
);
|
||||
|
||||
let text_pixels = ui::TextSize::Default.pixels(cx).to_f64() as f32;
|
||||
|
||||
self.max_width_index = self
|
||||
.breakpoints
|
||||
.iter()
|
||||
.map(|entry| match &entry.kind {
|
||||
BreakpointEntryKind::LineBreakpoint(line_bp) => {
|
||||
let name_and_line = format!("{}:{}", line_bp.name, line_bp.line);
|
||||
let dir_len = line_bp.dir.as_ref().map(|d| d.len()).unwrap_or(0);
|
||||
(name_and_line.len() + dir_len) as f32 * text_pixels
|
||||
}
|
||||
BreakpointEntryKind::ExceptionBreakpoint(exc_bp) => {
|
||||
exc_bp.data.label.len() as f32 * text_pixels
|
||||
}
|
||||
BreakpointEntryKind::DataBreakpoint(data_bp) => {
|
||||
data_bp.0.context.human_readable_label().len() as f32 * text_pixels
|
||||
}
|
||||
})
|
||||
.position_max_by(|left, right| left.total_cmp(right));
|
||||
|
||||
v_flex()
|
||||
.id("breakpoint-list")
|
||||
.key_context("BreakpointList")
|
||||
@@ -749,7 +774,14 @@ impl Render for BreakpointList {
|
||||
.size_full()
|
||||
.pt_1()
|
||||
.child(self.render_list(cx))
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx)
|
||||
.custom_scrollbars(
|
||||
ui::Scrollbars::new(ScrollAxes::Both)
|
||||
.tracked_scroll_handle(self.scroll_handle.clone())
|
||||
.with_track_along(ScrollAxes::Both, cx.theme().colors().panel_background)
|
||||
.tracked_entity(cx.entity_id()),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
.when_some(self.strip_mode, |this, _| {
|
||||
this.child(Divider::horizontal().color(DividerColor::Border))
|
||||
.child(
|
||||
|
||||
@@ -10,8 +10,9 @@ use std::{
|
||||
use editor::{Editor, EditorElement, EditorStyle};
|
||||
use gpui::{
|
||||
Action, Along, AppContext, Axis, DismissEvent, DragMoveEvent, Empty, Entity, FocusHandle,
|
||||
Focusable, MouseButton, Point, ScrollStrategy, ScrollWheelEvent, Subscription, Task, TextStyle,
|
||||
UniformList, UniformListScrollHandle, WeakEntity, actions, anchored, deferred, uniform_list,
|
||||
Focusable, ListHorizontalSizingBehavior, MouseButton, Point, ScrollStrategy, ScrollWheelEvent,
|
||||
Subscription, Task, TextStyle, UniformList, UniformListScrollHandle, WeakEntity, actions,
|
||||
anchored, deferred, uniform_list,
|
||||
};
|
||||
use notifications::status_toast::{StatusToast, ToastIcon};
|
||||
use project::debugger::{MemoryCell, dap_command::DataBreakpointContext, session::Session};
|
||||
@@ -229,6 +230,7 @@ impl MemoryView {
|
||||
},
|
||||
)
|
||||
.track_scroll(view_state.scroll_handle)
|
||||
.with_horizontal_sizing_behavior(ListHorizontalSizingBehavior::Unconstrained)
|
||||
.on_scroll_wheel(cx.listener(|this, evt: &ScrollWheelEvent, window, _| {
|
||||
let mut view_state = this.view_state();
|
||||
let delta = evt.delta.pixel_delta(window.line_height());
|
||||
@@ -917,7 +919,17 @@ impl Render for MemoryView {
|
||||
)
|
||||
.with_priority(1)
|
||||
}))
|
||||
.vertical_scrollbar_for(self.view_state_handle.clone(), window, cx),
|
||||
.custom_scrollbars(
|
||||
ui::Scrollbars::new(ui::ScrollAxes::Both)
|
||||
.tracked_scroll_handle(self.view_state_handle.clone())
|
||||
.with_track_along(
|
||||
ui::ScrollAxes::Both,
|
||||
cx.theme().colors().panel_background,
|
||||
)
|
||||
.tracked_entity(cx.entity_id()),
|
||||
window,
|
||||
cx,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,15 +11,18 @@ use gpui::{
|
||||
FocusHandle, Focusable, Hsla, MouseDownEvent, Point, Subscription, TextStyleRefinement,
|
||||
UniformListScrollHandle, WeakEntity, actions, anchored, deferred, uniform_list,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use menu::{SelectFirst, SelectLast, SelectNext, SelectPrevious};
|
||||
use project::debugger::{
|
||||
dap_command::DataBreakpointContext,
|
||||
session::{Session, SessionEvent, Watcher},
|
||||
};
|
||||
use std::{collections::HashMap, ops::Range, sync::Arc};
|
||||
use ui::{ContextMenu, ListItem, ScrollableHandle, Tooltip, WithScrollbar, prelude::*};
|
||||
use ui::{ContextMenu, ListItem, ScrollAxes, ScrollableHandle, Tooltip, WithScrollbar, prelude::*};
|
||||
use util::{debug_panic, maybe};
|
||||
|
||||
static INDENT_STEP_SIZE: Pixels = px(10.0);
|
||||
|
||||
actions!(
|
||||
variable_list,
|
||||
[
|
||||
@@ -185,6 +188,7 @@ struct VariableColor {
|
||||
|
||||
pub struct VariableList {
|
||||
entries: Vec<ListEntry>,
|
||||
max_width_index: Option<usize>,
|
||||
entry_states: HashMap<EntryPath, EntryState>,
|
||||
selected_stack_frame_id: Option<StackFrameId>,
|
||||
list_handle: UniformListScrollHandle,
|
||||
@@ -243,6 +247,7 @@ impl VariableList {
|
||||
disabled: false,
|
||||
edited_path: None,
|
||||
entries: Default::default(),
|
||||
max_width_index: None,
|
||||
entry_states: Default::default(),
|
||||
weak_running,
|
||||
memory_view,
|
||||
@@ -368,6 +373,26 @@ impl VariableList {
|
||||
}
|
||||
|
||||
self.entries = entries;
|
||||
|
||||
let text_pixels = ui::TextSize::Default.pixels(cx).to_f64() as f32;
|
||||
let indent_size = INDENT_STEP_SIZE.to_f64() as f32;
|
||||
|
||||
self.max_width_index = self
|
||||
.entries
|
||||
.iter()
|
||||
.map(|entry| match &entry.entry {
|
||||
DapEntry::Scope(scope) => scope.name.len() as f32 * text_pixels,
|
||||
DapEntry::Variable(variable) => {
|
||||
(variable.value.len() + variable.name.len()) as f32 * text_pixels
|
||||
+ (entry.path.indices.len() as f32 * indent_size)
|
||||
}
|
||||
DapEntry::Watcher(watcher) => {
|
||||
(watcher.value.len() + watcher.expression.len()) as f32 * text_pixels
|
||||
+ (entry.path.indices.len() as f32 * indent_size)
|
||||
}
|
||||
})
|
||||
.position_max_by(|left, right| left.total_cmp(right));
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -1244,7 +1269,7 @@ impl VariableList {
|
||||
.disabled(self.disabled)
|
||||
.selectable(false)
|
||||
.indent_level(state.depth)
|
||||
.indent_step_size(px(10.))
|
||||
.indent_step_size(INDENT_STEP_SIZE)
|
||||
.always_show_disclosure_icon(true)
|
||||
.when(var_ref > 0, |list_item| {
|
||||
list_item.toggle(state.is_expanded).on_toggle(cx.listener({
|
||||
@@ -1445,7 +1470,7 @@ impl VariableList {
|
||||
.disabled(self.disabled)
|
||||
.selectable(false)
|
||||
.indent_level(state.depth)
|
||||
.indent_step_size(px(10.))
|
||||
.indent_step_size(INDENT_STEP_SIZE)
|
||||
.always_show_disclosure_icon(true)
|
||||
.when(var_ref > 0, |list_item| {
|
||||
list_item.toggle(state.is_expanded).on_toggle(cx.listener({
|
||||
@@ -1507,7 +1532,6 @@ impl Render for VariableList {
|
||||
.key_context("VariableList")
|
||||
.id("variable-list")
|
||||
.group("variable-list")
|
||||
.overflow_y_scroll()
|
||||
.size_full()
|
||||
.on_action(cx.listener(Self::select_first))
|
||||
.on_action(cx.listener(Self::select_last))
|
||||
@@ -1533,6 +1557,9 @@ impl Render for VariableList {
|
||||
}),
|
||||
)
|
||||
.track_scroll(self.list_handle.clone())
|
||||
.with_width_from_item(self.max_width_index)
|
||||
.with_sizing_behavior(gpui::ListSizingBehavior::Auto)
|
||||
.with_horizontal_sizing_behavior(gpui::ListHorizontalSizingBehavior::Unconstrained)
|
||||
.gap_1_5()
|
||||
.size_full()
|
||||
.flex_grow(),
|
||||
@@ -1546,7 +1573,15 @@ impl Render for VariableList {
|
||||
)
|
||||
.with_priority(1)
|
||||
}))
|
||||
.vertical_scrollbar_for(self.list_handle.clone(), window, cx)
|
||||
// .vertical_scrollbar_for(self.list_handle.clone(), window, cx)
|
||||
.custom_scrollbars(
|
||||
ui::Scrollbars::new(ScrollAxes::Both)
|
||||
.tracked_scroll_handle(self.list_handle.clone())
|
||||
.with_track_along(ScrollAxes::Both, cx.theme().colors().panel_background)
|
||||
.tracked_entity(cx.entity_id()),
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -251,6 +251,8 @@ impl Element for UniformList {
|
||||
None
|
||||
}
|
||||
|
||||
// self.max_found_width = 0.0
|
||||
//
|
||||
fn request_layout(
|
||||
&mut self,
|
||||
global_id: Option<&GlobalElementId>,
|
||||
|
||||
@@ -392,7 +392,7 @@ pub struct Scrollbars<T: ScrollableHandle = ScrollHandle> {
|
||||
|
||||
impl Scrollbars {
|
||||
pub fn new(show_along: ScrollAxes) -> Self {
|
||||
Self::new_with_setting(show_along, |_| ShowScrollbar::default())
|
||||
Self::new_with_setting(show_along, |_| ShowScrollbar::Always)
|
||||
}
|
||||
|
||||
pub fn for_settings<S: ScrollbarVisibility>() -> Scrollbars {
|
||||
|
||||
@@ -144,6 +144,19 @@ impl TextSize {
|
||||
Self::Editor => rems_from_px(theme_settings.buffer_font_size(cx)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pixels(self, cx: &App) -> Pixels {
|
||||
let theme_settings = ThemeSettings::get_global(cx);
|
||||
|
||||
match self {
|
||||
Self::Large => px(16.),
|
||||
Self::Default => px(14.),
|
||||
Self::Small => px(12.),
|
||||
Self::XSmall => px(10.),
|
||||
Self::Ui => theme_settings.ui_font_size(cx),
|
||||
Self::Editor => theme_settings.buffer_font_size(cx),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The size of a [`Headline`] element
|
||||
|
||||
Reference in New Issue
Block a user