Wire up events and fix synthetic dragging

Nathan Sobo and Antonio Scandurra created

Co-Authored-By: Antonio Scandurra <me@as-cii.com>

Change summary

gpui/src/app.rs                 | 27 ++++++++++++-
gpui/src/executor.rs            |  1 
gpui/src/platform/mac/window.rs | 68 ++++++++++++++++++----------------
gpui/src/presenter.rs           | 21 ++++++----
4 files changed, 74 insertions(+), 43 deletions(-)

Detailed changes

gpui/src/app.rs 🔗

@@ -467,14 +467,14 @@ impl MutableAppContext {
     pub fn dispatch_action(
         &mut self,
         window_id: usize,
-        responder_chain: &[usize],
+        path: &[usize],
         name: &str,
         arg: &dyn Any,
     ) -> bool {
         self.pending_flushes += 1;
         let mut halted_dispatch = false;
 
-        for view_id in responder_chain.iter().rev() {
+        for view_id in path.iter().rev() {
             if let Some(mut view) = self
                 .ctx
                 .windows
@@ -635,6 +635,29 @@ impl MutableAppContext {
                     self,
                 )));
 
+                {
+                    let mut app = self.upgrade();
+                    let presenter = presenter.clone();
+                    window.on_event(Box::new(move |event, window| {
+                        log::info!("event {:?}", event);
+                        app.update(|ctx| {
+                            ctx.pending_flushes += 1;
+                            let actions = presenter
+                                .borrow_mut()
+                                .dispatch_event(event, ctx.downgrade());
+                            for action in actions {
+                                ctx.dispatch_action(
+                                    window_id,
+                                    &action.path,
+                                    action.name,
+                                    action.arg.as_ref(),
+                                );
+                            }
+                            ctx.flush_effects();
+                        })
+                    }));
+                }
+
                 {
                     let mut app = self.upgrade();
                     let presenter = presenter.clone();

gpui/src/executor.rs 🔗

@@ -17,6 +17,7 @@ pub enum Foreground {
     Test(smol::LocalExecutor<'static>),
 }
 
+#[must_use]
 #[pin_project(project = ForegroundTaskProject)]
 pub enum ForegroundTask<T> {
     Platform(#[pin] async_task::Task<T>),

gpui/src/platform/mac/window.rs 🔗

@@ -31,8 +31,8 @@ use std::{
     cell::RefCell,
     ffi::c_void,
     mem, ptr,
-    rc::Rc,
-    time::{Duration, Instant},
+    rc::{Rc, Weak},
+    time::Duration,
 };
 
 use super::{geometry::RectFExt, renderer::Renderer};
@@ -330,24 +330,33 @@ extern "C" fn dealloc_view(this: &Object, _: Sel) {
 
 extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
     let window_state = unsafe { get_window_state(this) };
-    let mut window_state_borrow = window_state.as_ref().borrow_mut();
+    let weak_window_state = Rc::downgrade(&window_state);
+    let mut window_state = window_state.as_ref().borrow_mut();
 
-    let event = unsafe { Event::from_native(native_event, Some(window_state_borrow.size().y())) };
+    let event = unsafe { Event::from_native(native_event, Some(window_state.size().y())) };
 
     if let Some(event) = event {
         match event {
             Event::LeftMouseDragged { position } => {
-                schedule_synthetic_drag(&&window_state, position)
+                window_state.synthetic_drag_counter += 1;
+                window_state
+                    .executor
+                    .spawn(synthetic_drag(
+                        weak_window_state,
+                        window_state.synthetic_drag_counter,
+                        position,
+                    ))
+                    .detach();
             }
             Event::LeftMouseUp { .. } => {
-                post_inc(&mut window_state_borrow.synthetic_drag_counter);
+                window_state.synthetic_drag_counter += 1;
             }
             _ => {}
         }
 
-        if let Some(mut callback) = window_state_borrow.event_callback.take() {
-            callback(event, &mut *window_state_borrow);
-            window_state_borrow.event_callback = Some(callback);
+        if let Some(mut callback) = window_state.event_callback.take() {
+            callback(event, &mut *window_state);
+            window_state.event_callback = Some(callback);
         }
     }
 }
@@ -450,30 +459,23 @@ extern "C" fn display_layer(this: &Object, _: Sel, _: id) {
     }
 }
 
-fn schedule_synthetic_drag(window_state: &Rc<RefCell<WindowState>>, position: Vector2F) {
-    let weak_window_state = Rc::downgrade(window_state);
-    let mut window_state = window_state.as_ref().borrow_mut();
-
-    let drag_id = post_inc(&mut window_state.synthetic_drag_counter);
-    let instant = Instant::now() + Duration::from_millis(16);
-
-    window_state
-        .executor
-        .spawn(async move {
-            Timer::at(instant).await;
-            if let Some(window_state) = weak_window_state.upgrade() {
-                let mut window_state_borrow = window_state.as_ref().borrow_mut();
-                if window_state_borrow.synthetic_drag_counter == drag_id {
-                    if let Some(mut callback) = window_state_borrow.event_callback.take() {
-                        schedule_synthetic_drag(&window_state, position);
-                        callback(
-                            Event::LeftMouseDragged { position },
-                            &mut *window_state_borrow,
-                        );
-                        window_state_borrow.event_callback = Some(callback);
-                    }
+async fn synthetic_drag(
+    window_state: Weak<RefCell<WindowState>>,
+    drag_id: usize,
+    position: Vector2F,
+) {
+    loop {
+        Timer::after(Duration::from_millis(16)).await;
+        if let Some(window_state) = window_state.upgrade() {
+            let mut window_state = window_state.borrow_mut();
+            if window_state.synthetic_drag_counter == drag_id {
+                if let Some(mut callback) = window_state.event_callback.take() {
+                    callback(Event::LeftMouseDragged { position }, &mut *window_state);
+                    window_state.event_callback = Some(callback);
                 }
+            } else {
+                break;
             }
-        })
-        .detach();
+        }
+    }
 }

gpui/src/presenter.rs 🔗

@@ -109,11 +109,7 @@ impl Presenter {
         })
     }
 
-    pub fn dispatch_event(
-        &self,
-        event: Event,
-        app: &AppContext,
-    ) -> Vec<(usize, &'static str, Box<dyn Any>)> {
+    pub fn dispatch_event(&self, event: Event, app: &AppContext) -> Vec<ActionToDispatch> {
         let mut event_ctx = EventContext {
             rendered_views: &self.rendered_views,
             actions: Vec::new(),
@@ -128,6 +124,12 @@ impl Presenter {
     }
 }
 
+pub struct ActionToDispatch {
+    pub path: Vec<usize>,
+    pub name: &'static str,
+    pub arg: Box<dyn Any>,
+}
+
 pub struct LayoutContext<'a> {
     rendered_views: &'a mut HashMap<usize, Box<dyn Element>>,
     parents: &'a mut HashMap<usize, usize>,
@@ -184,7 +186,7 @@ impl<'a> PaintContext<'a> {
 
 pub struct EventContext<'a> {
     rendered_views: &'a HashMap<usize, Box<dyn Element>>,
-    actions: Vec<(usize, &'static str, Box<dyn Any>)>,
+    actions: Vec<ActionToDispatch>,
     pub font_cache: &'a FontCache,
     pub text_layout_cache: &'a TextLayoutCache,
     view_stack: Vec<usize>,
@@ -208,8 +210,11 @@ impl<'a> EventContext<'a> {
     }
 
     pub fn dispatch_action<A: 'static + Any>(&mut self, name: &'static str, arg: A) {
-        self.actions
-            .push((*self.view_stack.last().unwrap(), name, Box::new(arg)));
+        self.actions.push(ActionToDispatch {
+            path: self.view_stack.clone(),
+            name,
+            arg: Box::new(arg),
+        });
     }
 }