Introduce `MouseEventHandler::on_drag`

Antonio Scandurra created

Change summary

gpui/src/elements/mouse_event_handler.rs | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)

Detailed changes

gpui/src/elements/mouse_event_handler.rs 🔗

@@ -9,12 +9,14 @@ pub struct MouseEventHandler {
     state: ValueHandle<MouseState>,
     child: ElementBox,
     click_handler: Option<Box<dyn FnMut(&mut EventContext)>>,
+    drag_handler: Option<Box<dyn FnMut(Vector2F, &mut EventContext)>>,
 }
 
 #[derive(Clone, Copy, Debug, Default)]
 pub struct MouseState {
     pub hovered: bool,
     pub clicked: bool,
+    prev_drag_position: Option<Vector2F>,
 }
 
 impl MouseEventHandler {
@@ -30,6 +32,7 @@ impl MouseEventHandler {
             state: state_handle,
             child,
             click_handler: None,
+            drag_handler: None,
         }
     }
 
@@ -37,6 +40,11 @@ impl MouseEventHandler {
         self.click_handler = Some(Box::new(handler));
         self
     }
+
+    pub fn on_drag(mut self, handler: impl FnMut(Vector2F, &mut EventContext) + 'static) -> Self {
+        self.drag_handler = Some(Box::new(handler));
+        self
+    }
 }
 
 impl Element for MouseEventHandler {
@@ -69,6 +77,7 @@ impl Element for MouseEventHandler {
         cx: &mut EventContext,
     ) -> bool {
         let click_handler = self.click_handler.as_mut();
+        let drag_handler = self.drag_handler.as_mut();
 
         let handled_in_child = self.child.dispatch_event(event, cx);
 
@@ -86,6 +95,7 @@ impl Element for MouseEventHandler {
             Event::LeftMouseDown { position, .. } => {
                 if !handled_in_child && bounds.contains_point(*position) {
                     state.clicked = true;
+                    state.prev_drag_position = Some(*position);
                     cx.notify();
                     true
                 } else {
@@ -93,6 +103,7 @@ impl Element for MouseEventHandler {
                 }
             }
             Event::LeftMouseUp { position, .. } => {
+                state.prev_drag_position = None;
                 if !handled_in_child && state.clicked {
                     state.clicked = false;
                     cx.notify();
@@ -106,6 +117,20 @@ impl Element for MouseEventHandler {
                     handled_in_child
                 }
             }
+            Event::LeftMouseDragged { position, .. } => {
+                if !handled_in_child && state.clicked {
+                    let prev_drag_position = state.prev_drag_position.replace(*position);
+                    if let Some((handler, prev_position)) = drag_handler.zip(prev_drag_position) {
+                        let delta = *position - prev_position;
+                        if !delta.is_zero() {
+                            (handler)(delta, cx);
+                        }
+                    }
+                    true
+                } else {
+                    handled_in_child
+                }
+            }
             _ => handled_in_child,
         })
     }