Position drag handle relative to cursor

Nathan Sobo created

Change summary

crates/gpui2/src/app.rs         |  7 ++++---
crates/gpui2/src/interactive.rs | 33 ++++++++++++++++++---------------
crates/gpui2/src/window.rs      |  4 ++--
3 files changed, 24 insertions(+), 20 deletions(-)

Detailed changes

crates/gpui2/src/app.rs 🔗

@@ -11,9 +11,9 @@ use smallvec::SmallVec;
 use crate::{
     current_platform, image_cache::ImageCache, Action, AnyBox, AnyView, AppMetadata, AssetSource,
     Context, DispatchPhase, DisplayId, Executor, FocusEvent, FocusHandle, FocusId, KeyBinding,
-    Keymap, LayoutId, MainThread, MainThreadOnly, Platform, SharedString, SubscriberSet,
-    Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window,
-    WindowContext, WindowHandle, WindowId,
+    Keymap, LayoutId, MainThread, MainThreadOnly, Pixels, Platform, Point, SharedString,
+    SubscriberSet, Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem,
+    View, Window, WindowContext, WindowHandle, WindowId,
 };
 use anyhow::{anyhow, Result};
 use collections::{HashMap, HashSet, VecDeque};
@@ -736,6 +736,7 @@ pub(crate) enum Effect {
 
 pub(crate) struct AnyDrag {
     pub drag_handle_view: AnyView,
+    pub cursor_offset: Point<Pixels>,
     pub state: AnyBox,
     pub state_type: TypeId,
 }

crates/gpui2/src/interactive.rs 🔗

@@ -319,19 +319,21 @@ pub trait StatefulInteractive: StatelessInteractive {
             self.stateful_interaction().drag_listener.is_none(),
             "calling on_drag more than once on the same element is not supported"
         );
-        self.stateful_interaction().drag_listener = Some(Arc::new(move |view_state, cx| {
-            let drag = listener(view_state, cx);
-            let view_handle = cx.handle().upgrade().unwrap();
-            let drag_handle_view = view(view_handle, move |view_state, cx| {
-                (drag.render_drag_handle)(view_state, cx)
-            })
-            .into_any();
-            AnyDrag {
-                drag_handle_view,
-                state: Box::new(drag.state),
-                state_type: TypeId::of::<S>(),
-            }
-        }));
+        self.stateful_interaction().drag_listener =
+            Some(Arc::new(move |view_state, cursor_offset, cx| {
+                let drag = listener(view_state, cx);
+                let view_handle = cx.handle().upgrade().unwrap();
+                let drag_handle_view = view(view_handle, move |view_state, cx| {
+                    (drag.render_drag_handle)(view_state, cx)
+                })
+                .into_any();
+                AnyDrag {
+                    drag_handle_view,
+                    cursor_offset,
+                    state: Box::new(drag.state),
+                    state_type: TypeId::of::<S>(),
+                }
+            }));
         self
     }
 }
@@ -472,7 +474,8 @@ pub trait ElementInteraction<V: 'static + Send + Sync>: 'static + Send + Sync {
                             } else if phase == DispatchPhase::Bubble
                                 && bounds.contains_point(&event.position)
                             {
-                                let any_drag = drag_listener(view_state, cx);
+                                let cursor_offset = event.position - bounds.origin;
+                                let any_drag = drag_listener(view_state, cursor_offset, cx);
                                 cx.start_drag(any_drag);
                                 cx.stop_propagation();
                             }
@@ -1030,7 +1033,7 @@ pub type ClickListener<V> =
     Arc<dyn Fn(&mut V, &ClickEvent, &mut ViewContext<V>) + Send + Sync + 'static>;
 
 pub(crate) type DragListener<V> =
-    Arc<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyDrag + Send + Sync + 'static>;
+    Arc<dyn Fn(&mut V, Point<Pixels>, &mut ViewContext<V>) -> AnyDrag + Send + Sync + 'static>;
 
 pub type KeyListener<V> = Arc<
     dyn Fn(

crates/gpui2/src/window.rs 🔗

@@ -812,8 +812,8 @@ impl<'a, 'w> WindowContext<'a, 'w> {
 
             if let Some(mut active_drag) = cx.active_drag.take() {
                 cx.stack(1, |cx| {
-                    let mouse_position = cx.mouse_position();
-                    cx.with_element_offset(Some(mouse_position), |cx| {
+                    let offset = cx.mouse_position() - active_drag.cursor_offset;
+                    cx.with_element_offset(Some(offset), |cx| {
                         let available_space =
                             size(AvailableSpace::MinContent, AvailableSpace::MinContent);
                         draw_any_view(&mut active_drag.drag_handle_view, available_space, cx);