debugger: Improve drag-and-scroll in memory views (#34526)

Piotr Osiewicz created

Closes #34508

Release Notes:

- N/A

Change summary

crates/debugger_ui/src/session/running/memory_view.rs | 74 +++++++-----
1 file changed, 42 insertions(+), 32 deletions(-)

Detailed changes

crates/debugger_ui/src/session/running/memory_view.rs 🔗

@@ -8,10 +8,10 @@ use std::{
 
 use editor::{Editor, EditorElement, EditorStyle};
 use gpui::{
-    Action, AppContext, DismissEvent, Empty, Entity, FocusHandle, Focusable, MouseButton,
-    MouseMoveEvent, Point, ScrollStrategy, ScrollWheelEvent, Stateful, Subscription, Task,
-    TextStyle, UniformList, UniformListScrollHandle, WeakEntity, actions, anchored, bounds,
-    deferred, point, size, uniform_list,
+    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,
 };
 use notifications::status_toast::{StatusToast, ToastIcon};
 use project::debugger::{MemoryCell, dap_command::DataBreakpointContext, session::Session};
@@ -126,6 +126,8 @@ impl ViewState {
     }
 }
 
+struct ScrollbarDragging;
+
 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("??");
@@ -189,11 +191,14 @@ impl MemoryView {
             div()
                 .occlude()
                 .id("memory-view-vertical-scrollbar")
-                .on_mouse_move(cx.listener(|this, evt, _, cx| {
-                    this.handle_drag(evt);
+                .on_drag_move(cx.listener(|this, evt, _, cx| {
+                    let did_handle = this.handle_scroll_drag(evt);
                     cx.notify();
-                    cx.stop_propagation()
+                    if did_handle {
+                        cx.stop_propagation()
+                    }
                 }))
+                .on_drag(ScrollbarDragging, |_, _, _, cx| cx.new(|_| Empty))
                 .on_hover(|_, _, cx| {
                     cx.stop_propagation();
                 })
@@ -307,16 +312,12 @@ impl MemoryView {
         .detach();
     }
 
-    fn handle_drag(&mut self, evt: &MouseMoveEvent) {
-        if !evt.dragging() {
-            return;
-        }
-        if !self.scroll_state.is_dragging()
-            && !self
-                .view_state
-                .selection
-                .as_ref()
-                .is_some_and(|selection| selection.is_dragging())
+    fn handle_memory_drag(&mut self, evt: &DragMoveEvent<Drag>) {
+        if !self
+            .view_state
+            .selection
+            .as_ref()
+            .is_some_and(|selection| selection.is_dragging())
         {
             return;
         }
@@ -324,22 +325,31 @@ impl MemoryView {
         debug_assert!(row_count > 1);
         let scroll_handle = self.scroll_state.scroll_handle();
         let viewport = scroll_handle.viewport();
-        let (top_area, bottom_area) = {
-            let size = size(viewport.size.width, viewport.size.height / 10.);
-            (
-                bounds(viewport.origin, size),
-                bounds(
-                    point(viewport.origin.x, viewport.origin.y + size.height * 2.),
-                    size,
-                ),
-            )
-        };
 
-        if bottom_area.contains(&evt.position) {
-            //ix == row_count - 1 {
+        if viewport.bottom() < evt.event.position.y {
+            self.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();
-        } else if top_area.contains(&evt.position) {
+            true
+        } else if viewport.top() > evt.event.position.y {
             self.view_state.schedule_scroll_up();
+            true
+        } else {
+            false
         }
     }
 
@@ -955,8 +965,8 @@ impl Render for MemoryView {
             .child(
                 v_flex()
                     .size_full()
-                    .on_mouse_move(cx.listener(|this, evt: &MouseMoveEvent, _, _| {
-                        this.handle_drag(evt);
+                    .on_drag_move(cx.listener(|this, evt, _, _| {
+                        this.handle_memory_drag(&evt);
                     }))
                     .child(self.render_memory(cx).size_full())
                     .children(self.open_context_menu.as_ref().map(|(menu, position, _)| {