Refactor the scrollbar component (#36105)
Closes https://github.com/zed-industries/zed/issues/37621 Improves https://github.com/zed-industries/zed/issues/24623 Adding scrollbars withing Zed's UI currently is rather cumbersome, as it requires the copying of a lot of code in order for these to work. Wiring up settings for scrollbar visibilty always has to be done at the call site and the state has to be saved and maintained by the caller as well. Similarly, reserving space has to also be handled by the caller. This PR changes the way scrollbars work in Zed fundamentally by making use of the new `use_keyed_state` APIs: Instead of saving the state at the call site, the window now keeps track of the state corresponding to scrollbars. This enables us to add scrollbars with e.g. one simple call on divs: ```rust div() .vertical_scrollbar(window, cx) ``` will add a scrollbar to the corresponding container. There are some more improvements regarding tracking of scrollbar visibility settings (which is now handled by a trait for each setting that supports this) as well as reserving space. Additionally, all needed stuff for layouting, catching events and reserving space is also now managed by the scrollbar component instead. This drastically reduces the amount of event listeners and makes layouting of two scrollbars easier. Furthermore, this paves the way for more improvements to scrollbars, such as graceful auto-hide. Only downsight here is that we lose some customizability in a few areas. However, once this lands, we gain the ability to quickly follow these up without breaking stuff elsewhere. This also already fixes a few bugs: - Scrollbars no longer flicker on first render. - Auto-hide now properly works for all scrollbars. - If the content size changes, the scrollbar is updated on the same frame. Both of these happened because we were computing the scrollbar sizes too early, causing us to use the sizes from the previous frame or unitialized sizes. - The project panel no longer jumps if scrolled all the way to the bottom and the scrollbar actually auto-hides. Still TODO: - [x] Fix scrolling in the debugger memory view - [x] Clean up some more in the scrollbar component and reduce clones there - [x] Ensure we don't over-notify the entity the scrollbar is rendered within - [x] Make sure auto-hide properly works for all cases - [x] Check whether we want to implement the scrollbar trait for `UniformList`s as well - ~~ [ ] Use for uniformlist where possible~~ Postponed - [x] Improve layout for cases where we render both scrollbars. Release Notes: - N/A
This commit is contained in:
@@ -10,7 +10,7 @@ use db::kvp::KEY_VALUE_STORE;
|
||||
use editor::Editor;
|
||||
use gpui::{
|
||||
Action, AppContext, ClickEvent, Entity, FocusHandle, Focusable, MouseButton, ScrollStrategy,
|
||||
Stateful, Task, UniformListScrollHandle, WeakEntity, actions, uniform_list,
|
||||
Task, UniformListScrollHandle, WeakEntity, actions, uniform_list,
|
||||
};
|
||||
use language::Point;
|
||||
use project::{
|
||||
@@ -23,8 +23,8 @@ use project::{
|
||||
worktree_store::WorktreeStore,
|
||||
};
|
||||
use ui::{
|
||||
Divider, DividerColor, FluentBuilder as _, Indicator, IntoElement, ListItem, Render, Scrollbar,
|
||||
ScrollbarState, StatefulInteractiveElement, Tooltip, prelude::*,
|
||||
Divider, DividerColor, FluentBuilder as _, Indicator, IntoElement, ListItem, Render,
|
||||
StatefulInteractiveElement, Tooltip, WithScrollbar, prelude::*,
|
||||
};
|
||||
use workspace::Workspace;
|
||||
use zed_actions::{ToggleEnableBreakpoint, UnsetBreakpoint};
|
||||
@@ -49,7 +49,6 @@ pub(crate) struct BreakpointList {
|
||||
breakpoint_store: Entity<BreakpointStore>,
|
||||
dap_store: Entity<DapStore>,
|
||||
worktree_store: Entity<WorktreeStore>,
|
||||
scrollbar_state: ScrollbarState,
|
||||
breakpoints: Vec<BreakpointEntry>,
|
||||
session: Option<Entity<Session>>,
|
||||
focus_handle: FocusHandle,
|
||||
@@ -87,7 +86,6 @@ impl BreakpointList {
|
||||
let dap_store = project.dap_store();
|
||||
let focus_handle = cx.focus_handle();
|
||||
let scroll_handle = UniformListScrollHandle::new();
|
||||
let scrollbar_state = ScrollbarState::new(scroll_handle.clone());
|
||||
|
||||
let adapter_name = session.as_ref().map(|session| session.read(cx).adapter());
|
||||
cx.new(|cx| {
|
||||
@@ -95,7 +93,6 @@ impl BreakpointList {
|
||||
breakpoint_store,
|
||||
dap_store,
|
||||
worktree_store,
|
||||
scrollbar_state,
|
||||
breakpoints: Default::default(),
|
||||
workspace,
|
||||
session,
|
||||
@@ -576,39 +573,6 @@ impl BreakpointList {
|
||||
.flex_1()
|
||||
}
|
||||
|
||||
fn render_vertical_scrollbar(&self, cx: &mut Context<Self>) -> Stateful<Div> {
|
||||
div()
|
||||
.occlude()
|
||||
.id("breakpoint-list-vertical-scrollbar")
|
||||
.on_mouse_move(cx.listener(|_, _, _, cx| {
|
||||
cx.notify();
|
||||
cx.stop_propagation()
|
||||
}))
|
||||
.on_hover(|_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.on_any_mouse_down(|_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.on_mouse_up(
|
||||
MouseButton::Left,
|
||||
cx.listener(|_, _, _, cx| {
|
||||
cx.stop_propagation();
|
||||
}),
|
||||
)
|
||||
.on_scroll_wheel(cx.listener(|_, _, _, cx| {
|
||||
cx.notify();
|
||||
}))
|
||||
.h_full()
|
||||
.absolute()
|
||||
.right_1()
|
||||
.top_1()
|
||||
.bottom_0()
|
||||
.w(px(12.))
|
||||
.cursor_default()
|
||||
.children(Scrollbar::vertical(self.scrollbar_state.clone()).map(|s| s.auto_hide(cx)))
|
||||
}
|
||||
|
||||
pub(crate) fn render_control_strip(&self) -> AnyElement {
|
||||
let selection_kind = self.selection_kind();
|
||||
let focus_handle = self.focus_handle.clone();
|
||||
@@ -789,7 +753,7 @@ impl Render for BreakpointList {
|
||||
.size_full()
|
||||
.pt_1()
|
||||
.child(self.render_list(cx))
|
||||
.child(self.render_vertical_scrollbar(cx))
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx)
|
||||
.when_some(self.strip_mode, |this, _| {
|
||||
this.child(Divider::horizontal().color(DividerColor::Border))
|
||||
.child(
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
use std::{
|
||||
cell::LazyCell,
|
||||
cell::{LazyCell, RefCell, RefMut},
|
||||
fmt::Write,
|
||||
ops::RangeInclusive,
|
||||
rc::Rc,
|
||||
sync::{Arc, LazyLock},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use editor::{Editor, EditorElement, EditorStyle};
|
||||
use gpui::{
|
||||
Action, AppContext, DismissEvent, DragMoveEvent, Empty, Entity, FocusHandle, Focusable,
|
||||
MouseButton, Point, ScrollStrategy, ScrollWheelEvent, Stateful, Subscription, Task, TextStyle,
|
||||
UniformList, UniformListScrollHandle, WeakEntity, actions, anchored, deferred, point,
|
||||
uniform_list,
|
||||
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,
|
||||
};
|
||||
use notifications::status_toast::{StatusToast, ToastIcon};
|
||||
use project::debugger::{MemoryCell, dap_command::DataBreakpointContext, session::Session};
|
||||
@@ -19,7 +19,7 @@ use settings::Settings;
|
||||
use theme::ThemeSettings;
|
||||
use ui::{
|
||||
ContextMenu, Divider, DropdownMenu, FluentBuilder, IntoElement, PopoverMenuHandle, Render,
|
||||
Scrollbar, ScrollbarState, StatefulInteractiveElement, Tooltip, prelude::*,
|
||||
ScrollableHandle, StatefulInteractiveElement, Tooltip, WithScrollbar, prelude::*,
|
||||
};
|
||||
use workspace::Workspace;
|
||||
|
||||
@@ -29,11 +29,9 @@ actions!(debugger, [GoToSelectedAddress]);
|
||||
|
||||
pub(crate) struct MemoryView {
|
||||
workspace: WeakEntity<Workspace>,
|
||||
scroll_handle: UniformListScrollHandle,
|
||||
scroll_state: ScrollbarState,
|
||||
stack_frame_list: WeakEntity<StackFrameList>,
|
||||
focus_handle: FocusHandle,
|
||||
view_state: ViewState,
|
||||
view_state_handle: ViewStateHandle,
|
||||
query_editor: Entity<Editor>,
|
||||
session: Entity<Session>,
|
||||
width_picker_handle: PopoverMenuHandle<ContextMenu>,
|
||||
@@ -90,18 +88,29 @@ impl SelectedMemoryRange {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ViewStateHandle(Rc<RefCell<ViewState>>);
|
||||
|
||||
impl ViewStateHandle {
|
||||
fn new(base_row: u64, line_width: ViewWidth) -> Self {
|
||||
Self(Rc::new(RefCell::new(ViewState::new(base_row, line_width))))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct ViewState {
|
||||
/// Uppermost row index
|
||||
base_row: u64,
|
||||
/// How many cells per row do we have?
|
||||
line_width: ViewWidth,
|
||||
scroll_handle: UniformListScrollHandle,
|
||||
selection: Option<SelectedMemoryRange>,
|
||||
}
|
||||
|
||||
impl ViewState {
|
||||
fn new(base_row: u64, line_width: ViewWidth) -> Self {
|
||||
Self {
|
||||
scroll_handle: UniformListScrollHandle::new(),
|
||||
base_row,
|
||||
line_width,
|
||||
selection: None,
|
||||
@@ -119,13 +128,39 @@ impl ViewState {
|
||||
fn schedule_scroll_up(&mut self) {
|
||||
self.base_row = self.base_row.saturating_sub(1);
|
||||
}
|
||||
|
||||
fn set_offset(&mut self, point: Point<Pixels>) {
|
||||
if point.y >= -Pixels::ZERO {
|
||||
self.schedule_scroll_up();
|
||||
} else if point.y <= -self.scroll_handle.max_offset().height {
|
||||
self.schedule_scroll_down();
|
||||
}
|
||||
self.scroll_handle.set_offset(point);
|
||||
}
|
||||
}
|
||||
|
||||
struct ScrollbarDragging;
|
||||
impl ScrollableHandle for ViewStateHandle {
|
||||
fn max_offset(&self) -> gpui::Size<Pixels> {
|
||||
self.0.borrow().scroll_handle.max_offset()
|
||||
}
|
||||
|
||||
fn set_offset(&self, point: Point<Pixels>) {
|
||||
self.0.borrow_mut().set_offset(point);
|
||||
}
|
||||
|
||||
fn offset(&self) -> Point<Pixels> {
|
||||
self.0.borrow().scroll_handle.offset()
|
||||
}
|
||||
|
||||
fn viewport(&self) -> gpui::Bounds<Pixels> {
|
||||
self.0.borrow().scroll_handle.viewport()
|
||||
}
|
||||
}
|
||||
|
||||
static HEX_BYTES_MEMOIZED: LazyLock<[SharedString; 256]> =
|
||||
LazyLock::new(|| std::array::from_fn(|byte| SharedString::from(format!("{byte:02X}"))));
|
||||
static UNKNOWN_BYTE: SharedString = SharedString::new_static("??");
|
||||
|
||||
impl MemoryView {
|
||||
pub(crate) fn new(
|
||||
session: Entity<Session>,
|
||||
@@ -134,19 +169,15 @@ impl MemoryView {
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let view_state = ViewState::new(0, WIDTHS[4].clone());
|
||||
let scroll_handle = UniformListScrollHandle::default();
|
||||
let view_state_handle = ViewStateHandle::new(0, WIDTHS[4].clone());
|
||||
|
||||
let query_editor = cx.new(|cx| Editor::single_line(window, cx));
|
||||
|
||||
let scroll_state = ScrollbarState::new(scroll_handle.clone());
|
||||
let mut this = Self {
|
||||
workspace,
|
||||
scroll_state,
|
||||
scroll_handle,
|
||||
stack_frame_list,
|
||||
focus_handle: cx.focus_handle(),
|
||||
view_state,
|
||||
view_state_handle,
|
||||
query_editor,
|
||||
session,
|
||||
width_picker_handle: Default::default(),
|
||||
@@ -162,50 +193,17 @@ impl MemoryView {
|
||||
this
|
||||
}
|
||||
|
||||
fn render_vertical_scrollbar(&self, cx: &mut Context<Self>) -> Stateful<Div> {
|
||||
div()
|
||||
.occlude()
|
||||
.id("memory-view-vertical-scrollbar")
|
||||
.on_drag_move(cx.listener(|this, evt, _, cx| {
|
||||
let did_handle = this.handle_scroll_drag(evt);
|
||||
cx.notify();
|
||||
if did_handle {
|
||||
cx.stop_propagation()
|
||||
}
|
||||
}))
|
||||
.on_drag(ScrollbarDragging, |_, _, _, cx| cx.new(|_| Empty))
|
||||
.on_hover(|_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.on_any_mouse_down(|_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.on_mouse_up(
|
||||
MouseButton::Left,
|
||||
cx.listener(|_, _, _, cx| {
|
||||
cx.stop_propagation();
|
||||
}),
|
||||
)
|
||||
.on_scroll_wheel(cx.listener(|_, _, _, cx| {
|
||||
cx.notify();
|
||||
}))
|
||||
.h_full()
|
||||
.absolute()
|
||||
.right_1()
|
||||
.top_1()
|
||||
.bottom_0()
|
||||
.w(px(12.))
|
||||
.cursor_default()
|
||||
.children(Scrollbar::vertical(self.scroll_state.clone()).map(|s| s.auto_hide(cx)))
|
||||
fn view_state(&self) -> RefMut<'_, ViewState> {
|
||||
self.view_state_handle.0.borrow_mut()
|
||||
}
|
||||
|
||||
fn render_memory(&self, cx: &mut Context<Self>) -> UniformList {
|
||||
let weak = cx.weak_entity();
|
||||
let session = self.session.clone();
|
||||
let view_state = self.view_state.clone();
|
||||
let view_state = self.view_state_handle.0.borrow().clone();
|
||||
uniform_list(
|
||||
"debugger-memory-view",
|
||||
self.view_state.row_count() as usize,
|
||||
view_state.row_count() as usize,
|
||||
move |range, _, cx| {
|
||||
let mut line_buffer = Vec::with_capacity(view_state.line_width.width as usize);
|
||||
let memory_start =
|
||||
@@ -230,22 +228,13 @@ impl MemoryView {
|
||||
rows
|
||||
},
|
||||
)
|
||||
.track_scroll(self.scroll_handle.clone())
|
||||
.track_scroll(view_state.scroll_handle)
|
||||
.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());
|
||||
let scroll_handle = this.scroll_state.scroll_handle();
|
||||
let size = scroll_handle.content_size();
|
||||
let viewport = scroll_handle.viewport();
|
||||
let current_offset = scroll_handle.offset();
|
||||
let first_entry_offset_boundary = size.height / this.view_state.row_count() as f32;
|
||||
let last_entry_offset_boundary = size.height - first_entry_offset_boundary;
|
||||
if first_entry_offset_boundary + viewport.size.height > current_offset.y.abs() {
|
||||
// The topmost entry is visible, hence if we're scrolling up, we need to load extra lines.
|
||||
this.view_state.schedule_scroll_up();
|
||||
} else if last_entry_offset_boundary < current_offset.y.abs() + viewport.size.height {
|
||||
this.view_state.schedule_scroll_down();
|
||||
}
|
||||
scroll_handle.set_offset(current_offset + point(px(0.), delta.y));
|
||||
let current_offset = view_state.scroll_handle.offset();
|
||||
view_state
|
||||
.set_offset(current_offset.apply_along(Axis::Vertical, |offset| offset + delta.y));
|
||||
}))
|
||||
}
|
||||
fn render_query_bar(&self, cx: &Context<Self>) -> impl IntoElement {
|
||||
@@ -275,7 +264,7 @@ impl MemoryView {
|
||||
cx.spawn(async move |this, cx| {
|
||||
let access_size = access_size.await.unwrap_or(1);
|
||||
this.update(cx, |this, cx| {
|
||||
this.view_state.selection = Some(SelectedMemoryRange::DragComplete(Drag {
|
||||
this.view_state().selection = Some(SelectedMemoryRange::DragComplete(Drag {
|
||||
start_address: as_address,
|
||||
end_address: as_address + access_size - 1,
|
||||
}));
|
||||
@@ -287,43 +276,23 @@ impl MemoryView {
|
||||
}
|
||||
|
||||
fn handle_memory_drag(&mut self, evt: &DragMoveEvent<Drag>) {
|
||||
if !self
|
||||
.view_state
|
||||
let mut view_state = self.view_state();
|
||||
if !view_state
|
||||
.selection
|
||||
.as_ref()
|
||||
.is_some_and(|selection| selection.is_dragging())
|
||||
{
|
||||
return;
|
||||
}
|
||||
let row_count = self.view_state.row_count();
|
||||
let row_count = view_state.row_count();
|
||||
debug_assert!(row_count > 1);
|
||||
let scroll_handle = self.scroll_state.scroll_handle();
|
||||
let scroll_handle = &view_state.scroll_handle;
|
||||
let viewport = scroll_handle.viewport();
|
||||
|
||||
if viewport.bottom() < evt.event.position.y {
|
||||
self.view_state.schedule_scroll_down();
|
||||
view_state.schedule_scroll_down();
|
||||
} else if viewport.top() > evt.event.position.y {
|
||||
self.view_state.schedule_scroll_up();
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_scroll_drag(&mut self, evt: &DragMoveEvent<ScrollbarDragging>) -> bool {
|
||||
if !self.scroll_state.is_dragging() {
|
||||
return false;
|
||||
}
|
||||
let row_count = self.view_state.row_count();
|
||||
debug_assert!(row_count > 1);
|
||||
let scroll_handle = self.scroll_state.scroll_handle();
|
||||
let viewport = scroll_handle.viewport();
|
||||
|
||||
if viewport.bottom() < evt.event.position.y {
|
||||
self.view_state.schedule_scroll_down();
|
||||
true
|
||||
} else if viewport.top() > evt.event.position.y {
|
||||
self.view_state.schedule_scroll_up();
|
||||
true
|
||||
} else {
|
||||
false
|
||||
view_state.schedule_scroll_up();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -354,7 +323,7 @@ impl MemoryView {
|
||||
|
||||
fn render_width_picker(&self, window: &mut Window, cx: &mut Context<Self>) -> DropdownMenu {
|
||||
let weak = cx.weak_entity();
|
||||
let selected_width = self.view_state.line_width.clone();
|
||||
let selected_width = self.view_state().line_width.clone();
|
||||
DropdownMenu::new(
|
||||
"memory-view-width-picker",
|
||||
selected_width.label.clone(),
|
||||
@@ -364,24 +333,25 @@ impl MemoryView {
|
||||
let width = width.clone();
|
||||
this = this.entry(width.label.clone(), None, move |_, cx| {
|
||||
_ = weak.update(cx, |this, _| {
|
||||
let mut view_state = this.view_state();
|
||||
// Convert base ix between 2 line widths to keep the shown memory address roughly the same.
|
||||
// All widths are powers of 2, so the conversion should be lossless.
|
||||
match this.view_state.line_width.width.cmp(&width.width) {
|
||||
match view_state.line_width.width.cmp(&width.width) {
|
||||
std::cmp::Ordering::Less => {
|
||||
// We're converting up.
|
||||
let shift = width.width.trailing_zeros()
|
||||
- this.view_state.line_width.width.trailing_zeros();
|
||||
this.view_state.base_row >>= shift;
|
||||
- view_state.line_width.width.trailing_zeros();
|
||||
view_state.base_row >>= shift;
|
||||
}
|
||||
std::cmp::Ordering::Greater => {
|
||||
// We're converting down.
|
||||
let shift = this.view_state.line_width.width.trailing_zeros()
|
||||
let shift = view_state.line_width.width.trailing_zeros()
|
||||
- width.width.trailing_zeros();
|
||||
this.view_state.base_row <<= shift;
|
||||
view_state.base_row <<= shift;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
this.view_state.line_width = width.clone();
|
||||
view_state.line_width = width.clone();
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -400,18 +370,18 @@ impl MemoryView {
|
||||
}
|
||||
|
||||
fn page_down(&mut self, _: &menu::SelectLast, _: &mut Window, cx: &mut Context<Self>) {
|
||||
self.view_state.base_row = self
|
||||
.view_state
|
||||
let mut view_state = self.view_state();
|
||||
view_state.base_row = view_state
|
||||
.base_row
|
||||
.overflowing_add(self.view_state.row_count())
|
||||
.overflowing_add(view_state.row_count())
|
||||
.0;
|
||||
cx.notify();
|
||||
}
|
||||
fn page_up(&mut self, _: &menu::SelectFirst, _: &mut Window, cx: &mut Context<Self>) {
|
||||
self.view_state.base_row = self
|
||||
.view_state
|
||||
let mut view_state = self.view_state();
|
||||
view_state.base_row = view_state
|
||||
.base_row
|
||||
.overflowing_sub(self.view_state.row_count())
|
||||
.overflowing_sub(view_state.row_count())
|
||||
.0;
|
||||
cx.notify();
|
||||
}
|
||||
@@ -447,7 +417,8 @@ impl MemoryView {
|
||||
_: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let Some(SelectedMemoryRange::DragComplete(selection)) = self.view_state.selection.clone()
|
||||
let Some(SelectedMemoryRange::DragComplete(selection)) =
|
||||
self.view_state().selection.clone()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
@@ -484,7 +455,8 @@ impl MemoryView {
|
||||
}
|
||||
|
||||
fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if let Some(SelectedMemoryRange::DragComplete(drag)) = &self.view_state.selection {
|
||||
let selection = self.view_state().selection.clone();
|
||||
if let Some(SelectedMemoryRange::DragComplete(drag)) = selection {
|
||||
// Go into memory writing mode.
|
||||
if !self.is_writing_memory {
|
||||
let should_return = self.session.update(cx, |session, cx| {
|
||||
@@ -558,9 +530,11 @@ impl MemoryView {
|
||||
}
|
||||
|
||||
fn jump_to_address(&mut self, address: u64, cx: &mut Context<Self>) {
|
||||
self.view_state.base_row = (address & !0xfff) / self.view_state.line_width.width as u64;
|
||||
let line_ix = (address & 0xfff) / self.view_state.line_width.width as u64;
|
||||
self.scroll_handle
|
||||
let mut view_state = self.view_state();
|
||||
view_state.base_row = (address & !0xfff) / view_state.line_width.width as u64;
|
||||
let line_ix = (address & 0xfff) / view_state.line_width.width as u64;
|
||||
view_state
|
||||
.scroll_handle
|
||||
.scroll_to_item(line_ix as usize, ScrollStrategy::Center);
|
||||
cx.notify();
|
||||
}
|
||||
@@ -595,7 +569,7 @@ impl MemoryView {
|
||||
}
|
||||
|
||||
fn cancel(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
|
||||
self.view_state.selection = None;
|
||||
self.view_state().selection = None;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
@@ -606,7 +580,7 @@ impl MemoryView {
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
let Some(SelectedMemoryRange::DragComplete(drag)) = self.view_state.selection.clone()
|
||||
let Some(SelectedMemoryRange::DragComplete(drag)) = self.view_state().selection.clone()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
@@ -718,7 +692,7 @@ fn render_single_memory_view_line(
|
||||
weak: gpui::WeakEntity<MemoryView>,
|
||||
cx: &mut App,
|
||||
) -> AnyElement {
|
||||
let Ok(view_state) = weak.update(cx, |this, _| this.view_state.clone()) else {
|
||||
let Ok(view_state) = weak.update(cx, |this, _| this.view_state().clone()) else {
|
||||
return div().into_any();
|
||||
};
|
||||
let base_address = (view_state.base_row + ix) * view_state.line_width.width as u64;
|
||||
@@ -799,7 +773,7 @@ fn render_single_memory_view_line(
|
||||
let weak = weak.clone();
|
||||
move |drag, _, _, cx| {
|
||||
_ = weak.update(cx, |this, _| {
|
||||
this.view_state.selection =
|
||||
this.view_state().selection =
|
||||
Some(SelectedMemoryRange::DragUnderway(drag.clone()));
|
||||
});
|
||||
|
||||
@@ -811,7 +785,7 @@ fn render_single_memory_view_line(
|
||||
let weak = weak.clone();
|
||||
move |drag: &Drag, _, cx| {
|
||||
_ = weak.update(cx, |this, _| {
|
||||
this.view_state.selection =
|
||||
this.view_state().selection =
|
||||
Some(SelectedMemoryRange::DragComplete(Drag {
|
||||
start_address: drag.start_address,
|
||||
end_address: base_address + cell_ix as u64,
|
||||
@@ -821,7 +795,7 @@ fn render_single_memory_view_line(
|
||||
})
|
||||
.drag_over(move |style, drag: &Drag, _, cx| {
|
||||
_ = weak.update(cx, |this, _| {
|
||||
this.view_state.selection =
|
||||
this.view_state().selection =
|
||||
Some(SelectedMemoryRange::DragUnderway(Drag {
|
||||
start_address: drag.start_address,
|
||||
end_address: base_address + cell_ix as u64,
|
||||
@@ -943,7 +917,7 @@ impl Render for MemoryView {
|
||||
)
|
||||
.with_priority(1)
|
||||
}))
|
||||
.child(self.render_vertical_scrollbar(cx)),
|
||||
.vertical_scrollbar_for(self.view_state_handle.clone(), window, cx),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
use anyhow::anyhow;
|
||||
use dap::Module;
|
||||
use gpui::{
|
||||
AnyElement, Entity, FocusHandle, Focusable, MouseButton, ScrollStrategy, Stateful,
|
||||
Subscription, Task, UniformListScrollHandle, WeakEntity, uniform_list,
|
||||
AnyElement, Entity, FocusHandle, Focusable, ScrollStrategy, Subscription, Task,
|
||||
UniformListScrollHandle, WeakEntity, uniform_list,
|
||||
};
|
||||
use project::{
|
||||
ProjectItem as _, ProjectPath,
|
||||
debugger::session::{Session, SessionEvent},
|
||||
};
|
||||
use std::{ops::Range, path::Path, sync::Arc};
|
||||
use ui::{Scrollbar, ScrollbarState, prelude::*};
|
||||
use ui::{WithScrollbar, prelude::*};
|
||||
use workspace::Workspace;
|
||||
|
||||
pub struct ModuleList {
|
||||
@@ -18,7 +18,6 @@ pub struct ModuleList {
|
||||
session: Entity<Session>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
focus_handle: FocusHandle,
|
||||
scrollbar_state: ScrollbarState,
|
||||
entries: Vec<Module>,
|
||||
_rebuild_task: Option<Task<()>>,
|
||||
_subscription: Subscription,
|
||||
@@ -44,7 +43,6 @@ impl ModuleList {
|
||||
let scroll_handle = UniformListScrollHandle::new();
|
||||
|
||||
Self {
|
||||
scrollbar_state: ScrollbarState::new(scroll_handle.clone()),
|
||||
scroll_handle,
|
||||
session,
|
||||
workspace,
|
||||
@@ -167,38 +165,6 @@ impl ModuleList {
|
||||
self.session
|
||||
.update(cx, |session, cx| session.modules(cx).to_vec())
|
||||
}
|
||||
fn render_vertical_scrollbar(&self, cx: &mut Context<Self>) -> Stateful<Div> {
|
||||
div()
|
||||
.occlude()
|
||||
.id("module-list-vertical-scrollbar")
|
||||
.on_mouse_move(cx.listener(|_, _, _, cx| {
|
||||
cx.notify();
|
||||
cx.stop_propagation()
|
||||
}))
|
||||
.on_hover(|_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.on_any_mouse_down(|_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.on_mouse_up(
|
||||
MouseButton::Left,
|
||||
cx.listener(|_, _, _, cx| {
|
||||
cx.stop_propagation();
|
||||
}),
|
||||
)
|
||||
.on_scroll_wheel(cx.listener(|_, _, _, cx| {
|
||||
cx.notify();
|
||||
}))
|
||||
.h_full()
|
||||
.absolute()
|
||||
.right_1()
|
||||
.top_1()
|
||||
.bottom_0()
|
||||
.w(px(12.))
|
||||
.cursor_default()
|
||||
.children(Scrollbar::vertical(self.scrollbar_state.clone()))
|
||||
}
|
||||
|
||||
fn confirm(&mut self, _: &menu::Confirm, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let Some(ix) = self.selected_ix else { return };
|
||||
@@ -313,6 +279,6 @@ impl Render for ModuleList {
|
||||
.size_full()
|
||||
.p_1()
|
||||
.child(self.render_list(window, cx))
|
||||
.child(self.render_vertical_scrollbar(cx))
|
||||
.vertical_scrollbar_for(self.scroll_handle.clone(), window, cx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use dap::StackFrameId;
|
||||
use db::kvp::KEY_VALUE_STORE;
|
||||
use gpui::{
|
||||
Action, AnyElement, Entity, EventEmitter, FocusHandle, Focusable, FontWeight, ListState,
|
||||
MouseButton, Stateful, Subscription, Task, WeakEntity, list,
|
||||
Subscription, Task, WeakEntity, list,
|
||||
};
|
||||
use util::debug_panic;
|
||||
|
||||
@@ -16,7 +16,7 @@ use language::PointUtf16;
|
||||
use project::debugger::breakpoint_store::ActiveStackFrame;
|
||||
use project::debugger::session::{Session, SessionEvent, StackFrame, ThreadStatus};
|
||||
use project::{ProjectItem, ProjectPath};
|
||||
use ui::{Scrollbar, ScrollbarState, Tooltip, prelude::*};
|
||||
use ui::{Tooltip, WithScrollbar, prelude::*};
|
||||
use workspace::{ItemHandle, Workspace};
|
||||
|
||||
use super::RunningState;
|
||||
@@ -64,7 +64,6 @@ pub struct StackFrameList {
|
||||
workspace: WeakEntity<Workspace>,
|
||||
selected_ix: Option<usize>,
|
||||
opened_stack_frame_id: Option<StackFrameId>,
|
||||
scrollbar_state: ScrollbarState,
|
||||
list_state: ListState,
|
||||
list_filter: StackFrameFilter,
|
||||
filter_entries_indices: Vec<usize>,
|
||||
@@ -102,7 +101,6 @@ impl StackFrameList {
|
||||
});
|
||||
|
||||
let list_state = ListState::new(0, gpui::ListAlignment::Top, px(1000.));
|
||||
let scrollbar_state = ScrollbarState::new(list_state.clone());
|
||||
|
||||
let list_filter = KEY_VALUE_STORE
|
||||
.read_kvp(&format!(
|
||||
@@ -127,7 +125,6 @@ impl StackFrameList {
|
||||
opened_stack_frame_id: None,
|
||||
list_filter,
|
||||
list_state,
|
||||
scrollbar_state,
|
||||
_refresh_task: Task::ready(()),
|
||||
};
|
||||
this.schedule_refresh(true, window, cx);
|
||||
@@ -697,39 +694,6 @@ impl StackFrameList {
|
||||
}
|
||||
}
|
||||
|
||||
fn render_vertical_scrollbar(&self, cx: &mut Context<Self>) -> Stateful<Div> {
|
||||
div()
|
||||
.occlude()
|
||||
.id("stack-frame-list-vertical-scrollbar")
|
||||
.on_mouse_move(cx.listener(|_, _, _, cx| {
|
||||
cx.notify();
|
||||
cx.stop_propagation()
|
||||
}))
|
||||
.on_hover(|_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.on_any_mouse_down(|_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.on_mouse_up(
|
||||
MouseButton::Left,
|
||||
cx.listener(|_, _, _, cx| {
|
||||
cx.stop_propagation();
|
||||
}),
|
||||
)
|
||||
.on_scroll_wheel(cx.listener(|_, _, _, cx| {
|
||||
cx.notify();
|
||||
}))
|
||||
.h_full()
|
||||
.absolute()
|
||||
.right_1()
|
||||
.top_1()
|
||||
.bottom_0()
|
||||
.w(px(12.))
|
||||
.cursor_default()
|
||||
.children(Scrollbar::vertical(self.scrollbar_state.clone()))
|
||||
}
|
||||
|
||||
fn select_ix(&mut self, ix: Option<usize>, cx: &mut Context<Self>) {
|
||||
self.selected_ix = ix;
|
||||
cx.notify();
|
||||
@@ -941,7 +905,7 @@ impl Render for StackFrameList {
|
||||
)
|
||||
})
|
||||
.child(self.render_list(window, cx))
|
||||
.child(self.render_vertical_scrollbar(cx))
|
||||
.vertical_scrollbar_for(self.list_state.clone(), window, cx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,9 +8,8 @@ use dap::{
|
||||
use editor::Editor;
|
||||
use gpui::{
|
||||
Action, AnyElement, ClickEvent, ClipboardItem, Context, DismissEvent, Empty, Entity,
|
||||
FocusHandle, Focusable, Hsla, MouseButton, MouseDownEvent, Point, Stateful, Subscription,
|
||||
TextStyleRefinement, UniformListScrollHandle, WeakEntity, actions, anchored, deferred,
|
||||
uniform_list,
|
||||
FocusHandle, Focusable, Hsla, MouseDownEvent, Point, Subscription, TextStyleRefinement,
|
||||
UniformListScrollHandle, WeakEntity, actions, anchored, deferred, uniform_list,
|
||||
};
|
||||
use menu::{SelectFirst, SelectLast, SelectNext, SelectPrevious};
|
||||
use project::debugger::{
|
||||
@@ -18,7 +17,7 @@ use project::debugger::{
|
||||
session::{Session, SessionEvent, Watcher},
|
||||
};
|
||||
use std::{collections::HashMap, ops::Range, sync::Arc};
|
||||
use ui::{ContextMenu, ListItem, ScrollableHandle, Scrollbar, ScrollbarState, Tooltip, prelude::*};
|
||||
use ui::{ContextMenu, ListItem, ScrollableHandle, Tooltip, WithScrollbar, prelude::*};
|
||||
use util::{debug_panic, maybe};
|
||||
|
||||
actions!(
|
||||
@@ -189,7 +188,6 @@ pub struct VariableList {
|
||||
entry_states: HashMap<EntryPath, EntryState>,
|
||||
selected_stack_frame_id: Option<StackFrameId>,
|
||||
list_handle: UniformListScrollHandle,
|
||||
scrollbar_state: ScrollbarState,
|
||||
session: Entity<Session>,
|
||||
selection: Option<EntryPath>,
|
||||
open_context_menu: Option<(Entity<ContextMenu>, Point<Pixels>, Subscription)>,
|
||||
@@ -235,7 +233,6 @@ impl VariableList {
|
||||
let list_state = UniformListScrollHandle::default();
|
||||
|
||||
Self {
|
||||
scrollbar_state: ScrollbarState::new(list_state.clone()),
|
||||
list_handle: list_state,
|
||||
session,
|
||||
focus_handle,
|
||||
@@ -1500,39 +1497,6 @@ impl VariableList {
|
||||
)
|
||||
.into_any()
|
||||
}
|
||||
|
||||
fn render_vertical_scrollbar(&self, cx: &mut Context<Self>) -> Stateful<Div> {
|
||||
div()
|
||||
.occlude()
|
||||
.id("variable-list-vertical-scrollbar")
|
||||
.on_mouse_move(cx.listener(|_, _, _, cx| {
|
||||
cx.notify();
|
||||
cx.stop_propagation()
|
||||
}))
|
||||
.on_hover(|_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.on_any_mouse_down(|_, _, cx| {
|
||||
cx.stop_propagation();
|
||||
})
|
||||
.on_mouse_up(
|
||||
MouseButton::Left,
|
||||
cx.listener(|_, _, _, cx| {
|
||||
cx.stop_propagation();
|
||||
}),
|
||||
)
|
||||
.on_scroll_wheel(cx.listener(|_, _, _, cx| {
|
||||
cx.notify();
|
||||
}))
|
||||
.h_full()
|
||||
.absolute()
|
||||
.right_1()
|
||||
.top_1()
|
||||
.bottom_0()
|
||||
.w(px(12.))
|
||||
.cursor_default()
|
||||
.children(Scrollbar::vertical(self.scrollbar_state.clone()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Focusable for VariableList {
|
||||
@@ -1542,7 +1506,7 @@ impl Focusable for VariableList {
|
||||
}
|
||||
|
||||
impl Render for VariableList {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
.track_focus(&self.focus_handle)
|
||||
.key_context("VariableList")
|
||||
@@ -1587,7 +1551,7 @@ impl Render for VariableList {
|
||||
)
|
||||
.with_priority(1)
|
||||
}))
|
||||
.child(self.render_vertical_scrollbar(cx))
|
||||
.vertical_scrollbar_for(self.list_handle.clone(), window, cx)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user