Rework presenter dispatch_event to allow isolated propogation of individual MouseRegionEvent types Co-Authored-By: mikayla@zed.dev

K Simmons created

Change summary

crates/gpui/src/app.rs                          |  33 
crates/gpui/src/elements/mouse_event_handler.rs |  14 
crates/gpui/src/presenter.rs                    | 448 +++++++++---------
crates/gpui/src/scene/mouse_region.rs           |  35 -
crates/gpui/src/scene/mouse_region_event.rs     |  82 +-
crates/workspace/src/sidebar.rs                 |   2 
6 files changed, 298 insertions(+), 316 deletions(-)

Detailed changes

crates/gpui/src/app.rs 🔗

@@ -9,7 +9,7 @@ use crate::{
     platform::{self, KeyDownEvent, Platform, PromptLevel, WindowOptions},
     presenter::Presenter,
     util::post_inc,
-    AssetCache, AssetSource, ClipboardItem, FontCache, InputHandler, MouseRegionId,
+    AssetCache, AssetSource, ClipboardItem, FontCache, InputHandler, MouseButton, MouseRegionId,
     PathPromptOptions, TextLayoutCache,
 };
 pub use action::*;
@@ -483,6 +483,7 @@ impl TestAppContext {
                     keystroke: keystroke.clone(),
                     is_held,
                 }),
+                false,
                 cx,
             ) {
                 return true;
@@ -569,8 +570,7 @@ impl TestAppContext {
                 view_type: PhantomData,
                 titlebar_height: 0.,
                 hovered_region_ids: Default::default(),
-                clicked_region_id: None,
-                right_clicked_region_id: None,
+                clicked_region_ids: None,
                 refreshing: false,
             };
             f(view, &mut render_cx)
@@ -1278,8 +1278,7 @@ impl MutableAppContext {
                         view_id,
                         titlebar_height,
                         hovered_region_ids: Default::default(),
-                        clicked_region_id: None,
-                        right_clicked_region_id: None,
+                        clicked_region_ids: None,
                         refreshing: false,
                     })
                     .unwrap(),
@@ -1958,7 +1957,7 @@ impl MutableAppContext {
                             }
                         }
 
-                        presenter.borrow_mut().dispatch_event(event, cx)
+                        presenter.borrow_mut().dispatch_event(event, false, cx)
                     } else {
                         false
                     }
@@ -4013,8 +4012,7 @@ pub struct RenderParams {
     pub view_id: usize,
     pub titlebar_height: f32,
     pub hovered_region_ids: HashSet<MouseRegionId>,
-    pub clicked_region_id: Option<MouseRegionId>,
-    pub right_clicked_region_id: Option<MouseRegionId>,
+    pub clicked_region_ids: Option<(Vec<MouseRegionId>, MouseButton)>,
     pub refreshing: bool,
 }
 
@@ -4023,8 +4021,7 @@ pub struct RenderContext<'a, T: View> {
     pub(crate) view_id: usize,
     pub(crate) view_type: PhantomData<T>,
     pub(crate) hovered_region_ids: HashSet<MouseRegionId>,
-    pub(crate) clicked_region_id: Option<MouseRegionId>,
-    pub(crate) right_clicked_region_id: Option<MouseRegionId>,
+    pub(crate) clicked_region_ids: Option<(Vec<MouseRegionId>, MouseButton)>,
     pub app: &'a mut MutableAppContext,
     pub titlebar_height: f32,
     pub refreshing: bool,
@@ -4033,8 +4030,7 @@ pub struct RenderContext<'a, T: View> {
 #[derive(Clone, Copy, Default)]
 pub struct MouseState {
     pub hovered: bool,
-    pub clicked: bool,
-    pub right_clicked: bool,
+    pub clicked: Option<MouseButton>,
 }
 
 impl<'a, V: View> RenderContext<'a, V> {
@@ -4046,8 +4042,7 @@ impl<'a, V: View> RenderContext<'a, V> {
             view_type: PhantomData,
             titlebar_height: params.titlebar_height,
             hovered_region_ids: params.hovered_region_ids.clone(),
-            clicked_region_id: params.clicked_region_id,
-            right_clicked_region_id: params.right_clicked_region_id,
+            clicked_region_ids: params.clicked_region_ids.clone(),
             refreshing: params.refreshing,
         }
     }
@@ -4071,8 +4066,13 @@ impl<'a, V: View> RenderContext<'a, V> {
         };
         MouseState {
             hovered: self.hovered_region_ids.contains(&region_id),
-            clicked: self.clicked_region_id == Some(region_id),
-            right_clicked: self.right_clicked_region_id == Some(region_id),
+            clicked: self.clicked_region_ids.as_ref().and_then(|(ids, button)| {
+                if ids.contains(&region_id) {
+                    Some(*button)
+                } else {
+                    None
+                }
+            }),
         }
     }
 
@@ -6025,6 +6025,7 @@ mod tests {
                 cmd: false,
                 click_count: 1,
             }),
+            false,
             cx,
         );
         assert_eq!(mouse_down_count.load(SeqCst), 1);

crates/gpui/src/elements/mouse_event_handler.rs 🔗

@@ -6,9 +6,8 @@ use crate::{
     },
     platform::CursorStyle,
     scene::{
-        ClickRegionEvent, CursorRegion, DownOutRegionEvent, DownRegionEvent, DragOverRegionEvent,
-        DragRegionEvent, HandlerSet, HoverRegionEvent, MoveRegionEvent, UpOutRegionEvent,
-        UpRegionEvent,
+        ClickRegionEvent, CursorRegion, DownOutRegionEvent, DownRegionEvent, DragRegionEvent,
+        HandlerSet, HoverRegionEvent, MoveRegionEvent, UpOutRegionEvent, UpRegionEvent,
     },
     DebugContext, Element, ElementBox, Event, EventContext, LayoutContext, MeasurementContext,
     MouseButton, MouseRegion, MouseState, PaintContext, RenderContext, SizeConstraint, View,
@@ -107,15 +106,6 @@ impl MouseEventHandler {
         self
     }
 
-    pub fn on_drag_over(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(DragOverRegionEvent, &mut EventContext) + 'static,
-    ) -> Self {
-        self.handlers = self.handlers.on_drag_over(button, handler);
-        self
-    }
-
     pub fn on_hover(
         mut self,
         handler: impl Fn(HoverRegionEvent, &mut EventContext) + 'static,

crates/gpui/src/presenter.rs 🔗

@@ -7,15 +7,14 @@ use crate::{
     keymap::Keystroke,
     platform::{CursorStyle, Event},
     scene::{
-        ClickRegionEvent, CursorRegion, DownOutRegionEvent, DownRegionEvent, DragOverRegionEvent,
-        DragRegionEvent, HoverRegionEvent, MouseRegionEvent, MoveRegionEvent, UpOutRegionEvent,
-        UpRegionEvent,
+        ClickRegionEvent, CursorRegion, DownOutRegionEvent, DownRegionEvent, DragRegionEvent,
+        HoverRegionEvent, MouseRegionEvent, MoveRegionEvent, UpOutRegionEvent, UpRegionEvent,
     },
     text_layout::TextLayoutCache,
     Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AssetCache, ElementBox, Entity,
-    FontSystem, ModelHandle, MouseButtonEvent, MouseMovedEvent, MouseRegion, MouseRegionId,
-    ParentId, ReadModel, ReadView, RenderContext, RenderParams, Scene, UpgradeModelHandle,
-    UpgradeViewHandle, View, ViewHandle, WeakModelHandle, WeakViewHandle,
+    FontSystem, ModelHandle, MouseButton, MouseMovedEvent, MouseRegion, MouseRegionId, ParentId,
+    ReadModel, ReadView, RenderContext, RenderParams, Scene, UpgradeModelHandle, UpgradeViewHandle,
+    View, ViewHandle, WeakModelHandle, WeakViewHandle,
 };
 use collections::{HashMap, HashSet};
 use pathfinder_geometry::vector::{vec2f, Vector2F};
@@ -37,9 +36,9 @@ pub struct Presenter {
     asset_cache: Arc<AssetCache>,
     last_mouse_moved_event: Option<Event>,
     hovered_region_ids: HashSet<MouseRegionId>,
-    clicked_region: Option<MouseRegion>,
-    right_clicked_region: Option<MouseRegion>,
-    prev_drag_position: Option<Vector2F>,
+    clicked_regions: Vec<MouseRegion>,
+    clicked_button: Option<MouseButton>,
+    mouse_position: Vector2F,
     titlebar_height: f32,
 }
 
@@ -62,9 +61,9 @@ impl Presenter {
             asset_cache,
             last_mouse_moved_event: None,
             hovered_region_ids: Default::default(),
-            clicked_region: None,
-            right_clicked_region: None,
-            prev_drag_position: None,
+            clicked_regions: Vec::new(),
+            clicked_button: None,
+            mouse_position: vec2f(0., 0.),
             titlebar_height,
         }
     }
@@ -87,11 +86,15 @@ impl Presenter {
                     view_id: *view_id,
                     titlebar_height: self.titlebar_height,
                     hovered_region_ids: self.hovered_region_ids.clone(),
-                    clicked_region_id: self.clicked_region.as_ref().and_then(MouseRegion::id),
-                    right_clicked_region_id: self
-                        .right_clicked_region
-                        .as_ref()
-                        .and_then(MouseRegion::id),
+                    clicked_region_ids: self.clicked_button.map(|button| {
+                        (
+                            self.clicked_regions
+                                .iter()
+                                .filter_map(MouseRegion::id)
+                                .collect(),
+                            button,
+                        )
+                    }),
                     refreshing: false,
                 })
                 .unwrap(),
@@ -109,11 +112,15 @@ impl Presenter {
                         view_id: *view_id,
                         titlebar_height: self.titlebar_height,
                         hovered_region_ids: self.hovered_region_ids.clone(),
-                        clicked_region_id: self.clicked_region.as_ref().and_then(MouseRegion::id),
-                        right_clicked_region_id: self
-                            .right_clicked_region
-                            .as_ref()
-                            .and_then(MouseRegion::id),
+                        clicked_region_ids: self.clicked_button.map(|button| {
+                            (
+                                self.clicked_regions
+                                    .iter()
+                                    .filter_map(MouseRegion::id)
+                                    .collect(),
+                                button,
+                            )
+                        }),
                         refreshing: true,
                     })
                     .unwrap();
@@ -144,11 +151,7 @@ impl Presenter {
 
             if cx.window_is_active(self.window_id) {
                 if let Some(event) = self.last_mouse_moved_event.clone() {
-                    let invalidated_views = self.handle_hover_events(&event, cx).invalidated_views;
-
-                    for view_id in invalidated_views {
-                        cx.notify_view(self.window_id, view_id);
-                    }
+                    self.dispatch_event(event, true, cx);
                 }
             }
         } else {
@@ -181,8 +184,15 @@ impl Presenter {
             view_stack: Vec::new(),
             refreshing,
             hovered_region_ids: self.hovered_region_ids.clone(),
-            clicked_region_id: self.clicked_region.as_ref().and_then(MouseRegion::id),
-            right_clicked_region_id: self.right_clicked_region.as_ref().and_then(MouseRegion::id),
+            clicked_region_ids: self.clicked_button.map(|button| {
+                (
+                    self.clicked_regions
+                        .iter()
+                        .filter_map(MouseRegion::id)
+                        .collect(),
+                    button,
+                )
+            }),
             titlebar_height: self.titlebar_height,
             window_size,
             app: cx,
@@ -217,197 +227,230 @@ impl Presenter {
         })
     }
 
-    pub fn dispatch_event(&mut self, event: Event, cx: &mut MutableAppContext) -> bool {
+    pub fn dispatch_event(
+        &mut self,
+        event: Event,
+        event_reused: bool,
+        cx: &mut MutableAppContext,
+    ) -> bool {
         if let Some(root_view_id) = cx.root_view_id(self.window_id) {
             let mut events_to_send = Vec::new();
+
+            //1. Allocate the correct set of GPUI events generated from the platform events
+            // -> These are usually small: [Mouse Down] or [Mouse up, Click] or [Mouse Moved, Mouse Dragged?]
+            // -> Also moves around mouse related state
             match &event {
-                Event::MouseDown(e @ MouseButtonEvent { position, .. }) => {
-                    for (region, _) in self.mouse_regions.iter().rev() {
+                Event::MouseDown(e) => {
+                    //Click events are weird because they can be fired after a drag event.
+                    //MDN says that browsers handle this by starting from 'the most
+                    //specific ancestor element that contained both [positions]'
+                    //So we need to store the overlapping regions on mouse down.
+                    self.clicked_regions = self
+                        .mouse_regions
+                        .iter()
+                        .filter_map(|(region, _)| {
+                            region
+                                .bounds
+                                .contains_point(e.position)
+                                .then(|| region.clone())
+                        })
+                        .collect();
+                    self.clicked_button = Some(e.button);
+
+                    events_to_send.push(MouseRegionEvent::Down(DownRegionEvent {
+                        region: Default::default(),
+                        platform_event: e.clone(),
+                    }));
+                    events_to_send.push(MouseRegionEvent::DownOut(DownOutRegionEvent {
+                        region: Default::default(),
+                        platform_event: e.clone(),
+                    }));
+                }
+                Event::MouseUp(e) => {
+                    //NOTE: The order of event pushes is important! MouseUp events MUST be fired
+                    //before click events, and so the UpRegionEvent events need to be pushed before
+                    //ClickRegionEvents
+                    events_to_send.push(MouseRegionEvent::Up(UpRegionEvent {
+                        region: Default::default(),
+                        platform_event: e.clone(),
+                    }));
+                    events_to_send.push(MouseRegionEvent::UpOut(UpOutRegionEvent {
+                        region: Default::default(),
+                        platform_event: e.clone(),
+                    }));
+                    events_to_send.push(MouseRegionEvent::Click(ClickRegionEvent {
+                        region: Default::default(),
+                        platform_event: e.clone(),
+                    }));
+                }
+                Event::MouseMoved(
+                    e @ MouseMovedEvent {
+                        position,
+                        pressed_button,
+                        ..
+                    },
+                ) => {
+                    let mut style_to_assign = CursorStyle::Arrow;
+                    for region in self.cursor_regions.iter().rev() {
                         if region.bounds.contains_point(*position) {
-                            self.clicked_region = Some(region.clone());
-                            self.prev_drag_position = Some(*position);
-                            events_to_send.push((
-                                region.clone(),
-                                MouseRegionEvent::Down(DownRegionEvent {
-                                    region: region.bounds,
-                                    platform_event: e.clone(),
-                                }),
-                            ));
-                        } else {
-                            events_to_send.push((
-                                region.clone(),
-                                MouseRegionEvent::DownOut(DownOutRegionEvent {
-                                    region: region.bounds,
-                                    platform_event: e.clone(),
-                                }),
-                            ));
+                            style_to_assign = region.style;
+                            break;
                         }
                     }
+                    cx.platform().set_cursor_style(style_to_assign);
+
+                    if pressed_button.is_some() {
+                        events_to_send.push(MouseRegionEvent::Drag(DragRegionEvent {
+                            region: Default::default(),
+                            prev_mouse_position: self.mouse_position,
+                            platform_event: e.clone(),
+                        }));
+                    }
+
+                    events_to_send.push(MouseRegionEvent::Move(MoveRegionEvent {
+                        region: Default::default(),
+                        platform_event: e.clone(),
+                    }));
+
+                    events_to_send.push(MouseRegionEvent::Hover(HoverRegionEvent {
+                        region: Default::default(),
+                        platform_event: e.clone(),
+                        started: true,
+                    }));
+
+                    events_to_send.push(MouseRegionEvent::Hover(HoverRegionEvent {
+                        region: Default::default(),
+                        platform_event: e.clone(),
+                        started: false,
+                    }));
+
+                    self.last_mouse_moved_event = Some(event.clone());
                 }
-                Event::MouseUp(e @ MouseButtonEvent { position, .. }) => {
-                    for (region, _) in self.mouse_regions.iter().rev() {
-                        if region.bounds.contains_point(*position) {
-                            events_to_send.push((
-                                region.clone(),
-                                MouseRegionEvent::Up(UpRegionEvent {
-                                    region: region.bounds,
-                                    platform_event: e.clone(),
-                                }),
-                            ));
-                        } else {
-                            events_to_send.push((
-                                region.clone(),
-                                MouseRegionEvent::UpOut(UpOutRegionEvent {
-                                    region: region.bounds,
-                                    platform_event: e.clone(),
-                                }),
-                            ));
+                _ => {}
+            }
+
+            if let Some(position) = event.position() {
+                self.mouse_position = position;
+            }
+
+            let mut invalidated_views: HashSet<usize> = Default::default();
+            let mut any_event_handled = false;
+            //2. Process the raw mouse events into region events
+            for mut region_event in events_to_send {
+                let mut valid_regions = Vec::new();
+
+                //GPUI elements are arranged by depth but sibling elements can register overlapping
+                //mouse regions. As such, hover events are only fired on overlapping elements which
+                //are at the same depth as the deepest element which overlaps with the mouse.
+                if let MouseRegionEvent::Hover(_) = region_event {
+                    let mut top_most_depth = None;
+                    let mouse_position = self.mouse_position.clone();
+                    for (region, depth) in self.mouse_regions.iter().rev() {
+                        let contains_mouse = region.bounds.contains_point(mouse_position);
+
+                        if contains_mouse && top_most_depth.is_none() {
+                            top_most_depth = Some(depth);
+                        }
+
+                        if let Some(region_id) = region.id() {
+                            //This unwrap relies on short circuiting boolean expressions
+                            //The right side of the && is only executed when contains_mouse
+                            //is true, and we know above that when contains_mouse is true
+                            //top_most_depth is set
+                            if contains_mouse && depth == top_most_depth.unwrap() {
+                                //Ensure that hover entrance events aren't sent twice
+                                if self.hovered_region_ids.insert(region_id) {
+                                    valid_regions.push(region.clone());
+                                }
+                            } else {
+                                //Ensure that hover exit events aren't sent twice
+                                if self.hovered_region_ids.remove(&region_id) {
+                                    valid_regions.push(region.clone());
+                                }
+                            }
                         }
                     }
-                    self.prev_drag_position.take();
-                    if let Some(region) = self.clicked_region.take() {
-                        if region.bounds.contains_point(*position) {
-                            let bounds = region.bounds.clone();
-                            events_to_send.push((
-                                region,
-                                MouseRegionEvent::Click(ClickRegionEvent {
-                                    region: bounds,
-                                    platform_event: e.clone(),
-                                }),
-                            ));
+                } else if let MouseRegionEvent::Click(e) = &region_event {
+                    //Clear presenter state
+                    let clicked_regions = std::mem::replace(&mut self.clicked_regions, Vec::new());
+                    self.clicked_button = None;
+
+                    //Find regions which still overlap with the mouse since the last MouseDown happened
+                    for clicked_region in clicked_regions.into_iter().rev() {
+                        if clicked_region.bounds.contains_point(e.position) {
+                            valid_regions.push(clicked_region);
                         }
                     }
-                }
-                Event::MouseMoved(e @ MouseMovedEvent { position, .. }) => {
-                    if let Some(clicked_region) = self.clicked_region.as_ref() {
-                        if let Some(prev_drag_position) = self.prev_drag_position {
-                            events_to_send.push((
-                                clicked_region.clone(),
-                                MouseRegionEvent::Drag(DragRegionEvent {
-                                    region: clicked_region.bounds,
-                                    prev_drag_position,
-                                    platform_event: e.clone(),
-                                }),
-                            ));
+                } else if region_event.is_local() {
+                    for (mouse_region, _) in self.mouse_regions.iter().rev() {
+                        //Contains
+                        if mouse_region.bounds.contains_point(self.mouse_position) {
+                            valid_regions.push(mouse_region.clone());
                         }
-
-                        self.prev_drag_position = Some(*position)
                     }
-
-                    for (region, _) in self.mouse_regions.iter().rev() {
-                        if region.bounds.contains_point(*position) {
-                            events_to_send.push((
-                                region.clone(),
-                                MouseRegionEvent::Move(MoveRegionEvent {
-                                    region: region.bounds,
-                                    platform_event: e.clone(),
-                                }),
-                            ));
+                } else {
+                    for (mouse_region, _) in self.mouse_regions.iter().rev() {
+                        //NOT contains
+                        if !mouse_region.bounds.contains_point(self.mouse_position) {
+                            valid_regions.push(mouse_region.clone());
                         }
                     }
-
-                    self.last_mouse_moved_event = Some(event.clone());
                 }
-                _ => {}
-            }
 
-            let (invalidated_views, handled) = {
-                let mut event_cx = self.handle_hover_events(&event, cx);
-                event_cx.process_region_events(events_to_send);
+                //3. Fire region events
+                let hovered_region_ids = self.hovered_region_ids.clone();
+                let mut event_cx = self.build_event_context(cx);
+                for valid_region in valid_regions.into_iter() {
+                    region_event.set_region(valid_region.bounds);
+                    if let MouseRegionEvent::Hover(e) = &mut region_event {
+                        e.started = valid_region
+                            .id()
+                            .map(|region_id| hovered_region_ids.contains(&region_id))
+                            .unwrap_or(false)
+                    }
+
+                    if let Some(callback) = valid_region.handlers.get(&region_event.handler_key()) {
+                        if event_reused {
+                            invalidated_views.insert(valid_region.view_id);
+                        }
 
-                if !event_cx.handled {
-                    event_cx.handled = event_cx.dispatch_event(root_view_id, &event);
+                        event_cx.handled = true;
+                        let local = region_event.is_local();
+                        event_cx.with_current_view(valid_region.view_id, {
+                            let region_event = region_event.clone();
+                            |cx| {
+                                callback(region_event, cx);
+                            }
+                        });
+
+                        // For bubbling events, if the event was handled, don't continue dispatching
+                        // This only makes sense for local events. 'Out*' events are already
+                        if event_cx.handled && local {
+                            break;
+                        }
+                    }
                 }
 
-                (event_cx.invalidated_views, event_cx.handled)
-            };
+                invalidated_views.extend(event_cx.invalidated_views);
+                any_event_handled = any_event_handled && event_cx.handled;
+            }
+
+            if !any_event_handled {
+                let mut event_cx = self.build_event_context(cx);
+                any_event_handled = event_cx.dispatch_event(root_view_id, &event);
+                invalidated_views.extend(event_cx.invalidated_views);
+            }
 
             for view_id in invalidated_views {
                 cx.notify_view(self.window_id, view_id);
             }
 
-            handled
+            any_event_handled
         } else {
             false
         }
     }
 
-    fn handle_hover_events<'a>(
-        &'a mut self,
-        event: &Event,
-        cx: &'a mut MutableAppContext,
-    ) -> EventContext<'a> {
-        let mut events_to_send = Vec::new();
-
-        if let Event::MouseMoved(
-            e @ MouseMovedEvent {
-                position,
-                pressed_button,
-                ..
-            },
-        ) = event
-        {
-            let mut style_to_assign = CursorStyle::Arrow;
-            for region in self.cursor_regions.iter().rev() {
-                if region.bounds.contains_point(*position) {
-                    style_to_assign = region.style;
-                    break;
-                }
-            }
-            cx.platform().set_cursor_style(style_to_assign);
-
-            let mut hover_depth = None;
-            for (region, depth) in self.mouse_regions.iter().rev() {
-                if region.bounds.contains_point(*position)
-                    && hover_depth.map_or(true, |hover_depth| hover_depth == *depth)
-                {
-                    hover_depth = Some(*depth);
-                    if let Some(region_id) = region.id() {
-                        if !self.hovered_region_ids.contains(&region_id) {
-                            let region_event = if pressed_button.is_some() {
-                                MouseRegionEvent::DragOver(DragOverRegionEvent {
-                                    region: region.bounds,
-                                    started: true,
-                                    platform_event: e.clone(),
-                                })
-                            } else {
-                                MouseRegionEvent::Hover(HoverRegionEvent {
-                                    region: region.bounds,
-                                    started: true,
-                                    platform_event: e.clone(),
-                                })
-                            };
-                            events_to_send.push((region.clone(), region_event));
-                            self.hovered_region_ids.insert(region_id);
-                        }
-                    }
-                } else if let Some(region_id) = region.id() {
-                    if self.hovered_region_ids.contains(&region_id) {
-                        let region_event = if pressed_button.is_some() {
-                            MouseRegionEvent::DragOver(DragOverRegionEvent {
-                                region: region.bounds,
-                                started: false,
-                                platform_event: e.clone(),
-                            })
-                        } else {
-                            MouseRegionEvent::Hover(HoverRegionEvent {
-                                region: region.bounds,
-                                started: false,
-                                platform_event: e.clone(),
-                            })
-                        };
-                        events_to_send.push((region.clone(), region_event));
-                        self.hovered_region_ids.remove(&region_id);
-                    }
-                }
-            }
-        }
-
-        let mut event_cx = self.build_event_context(cx);
-        event_cx.process_region_events(events_to_send);
-        event_cx
-    }
-
     pub fn build_event_context<'a>(
         &'a mut self,
         cx: &'a mut MutableAppContext,
@@ -454,8 +497,7 @@ pub struct LayoutContext<'a> {
     pub window_size: Vector2F,
     titlebar_height: f32,
     hovered_region_ids: HashSet<MouseRegionId>,
-    clicked_region_id: Option<MouseRegionId>,
-    right_clicked_region_id: Option<MouseRegionId>,
+    clicked_region_ids: Option<(Vec<MouseRegionId>, MouseButton)>,
 }
 
 impl<'a> LayoutContext<'a> {
@@ -526,8 +568,7 @@ impl<'a> LayoutContext<'a> {
                 view_type: PhantomData,
                 titlebar_height: self.titlebar_height,
                 hovered_region_ids: self.hovered_region_ids.clone(),
-                clicked_region_id: self.clicked_region_id,
-                right_clicked_region_id: self.right_clicked_region_id,
+                clicked_region_ids: self.clicked_region_ids.clone(),
                 refreshing: self.refreshing,
             };
             f(view, &mut render_cx)
@@ -655,25 +696,6 @@ impl<'a> EventContext<'a> {
         }
     }
 
-    fn process_region_events(&mut self, events: Vec<(MouseRegion, MouseRegionEvent)>) {
-        for (region, event) in events {
-            if event.is_local() {
-                if self.handled {
-                    continue;
-                }
-
-                self.invalidated_views.insert(region.view_id);
-            }
-
-            if let Some(callback) = region.handlers.get(&event.handler_key()) {
-                self.handled = true;
-                self.with_current_view(region.view_id, |event_cx| {
-                    callback(event, event_cx);
-                })
-            }
-        }
-    }
-
     fn with_current_view<F, T>(&mut self, view_id: usize, f: F) -> T
     where
         F: FnOnce(&mut Self) -> T,

crates/gpui/src/scene/mouse_region.rs 🔗

@@ -6,8 +6,8 @@ use pathfinder_geometry::rect::RectF;
 use crate::{EventContext, MouseButton};
 
 use super::mouse_region_event::{
-    ClickRegionEvent, DownOutRegionEvent, DownRegionEvent, DragOverRegionEvent, DragRegionEvent,
-    HoverRegionEvent, MouseRegionEvent, MoveRegionEvent, UpOutRegionEvent, UpRegionEvent,
+    ClickRegionEvent, DownOutRegionEvent, DownRegionEvent, DragRegionEvent, HoverRegionEvent,
+    MouseRegionEvent, MoveRegionEvent, UpOutRegionEvent, UpRegionEvent,
 };
 
 #[derive(Clone, Default)]
@@ -104,15 +104,6 @@ impl MouseRegion {
         self
     }
 
-    pub fn on_drag_over(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(DragOverRegionEvent, &mut EventContext) + 'static,
-    ) -> Self {
-        self.handlers = self.handlers.on_drag_over(button, handler);
-        self
-    }
-
     pub fn on_hover(
         mut self,
         handler: impl Fn(HoverRegionEvent, &mut EventContext) + 'static,
@@ -152,10 +143,6 @@ impl HandlerSet {
                 (MouseRegionEvent::drag_disc(), Some(button)),
                 Rc::new(|_, _| {}),
             );
-            set.insert(
-                (MouseRegionEvent::drag_over_disc(), Some(button)),
-                Rc::new(|_, _| {}),
-            );
             set.insert(
                 (MouseRegionEvent::down_disc(), Some(button)),
                 Rc::new(|_, _| {}),
@@ -317,24 +304,6 @@ impl HandlerSet {
         self
     }
 
-    pub fn on_drag_over(
-        mut self,
-        button: MouseButton,
-        handler: impl Fn(DragOverRegionEvent, &mut EventContext) + 'static,
-    ) -> Self {
-        self.set.insert((MouseRegionEvent::drag_over_disc(), Some(button)),
-            Rc::new(move |region_event, cx| {
-                if let MouseRegionEvent::DragOver(e) = region_event {
-                    handler(e, cx);
-                } else {
-                    panic!(
-                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::DragOver, found {:?}", 
-                        region_event);
-                }
-            }));
-        self
-    }
-
     pub fn on_hover(
         mut self,
         handler: impl Fn(HoverRegionEvent, &mut EventContext) + 'static,

crates/gpui/src/scene/mouse_region_event.rs 🔗

@@ -7,7 +7,7 @@ use pathfinder_geometry::{rect::RectF, vector::Vector2F};
 
 use crate::{MouseButton, MouseButtonEvent, MouseMovedEvent, ScrollWheelEvent};
 
-#[derive(Debug, Default)]
+#[derive(Debug, Default, Clone)]
 pub struct MoveRegionEvent {
     pub region: RectF,
     pub platform_event: MouseMovedEvent,
@@ -21,10 +21,10 @@ impl Deref for MoveRegionEvent {
     }
 }
 
-#[derive(Debug, Default)]
+#[derive(Debug, Default, Clone)]
 pub struct DragRegionEvent {
     pub region: RectF,
-    pub prev_drag_position: Vector2F,
+    pub prev_mouse_position: Vector2F,
     pub platform_event: MouseMovedEvent,
 }
 
@@ -36,22 +36,7 @@ impl Deref for DragRegionEvent {
     }
 }
 
-#[derive(Debug, Default)]
-pub struct DragOverRegionEvent {
-    pub region: RectF,
-    pub started: bool,
-    pub platform_event: MouseMovedEvent,
-}
-
-impl Deref for DragOverRegionEvent {
-    type Target = MouseMovedEvent;
-
-    fn deref(&self) -> &Self::Target {
-        &self.platform_event
-    }
-}
-
-#[derive(Debug, Default)]
+#[derive(Debug, Default, Clone)]
 pub struct HoverRegionEvent {
     pub region: RectF,
     pub started: bool,
@@ -66,7 +51,7 @@ impl Deref for HoverRegionEvent {
     }
 }
 
-#[derive(Debug, Default)]
+#[derive(Debug, Default, Clone)]
 pub struct DownRegionEvent {
     pub region: RectF,
     pub platform_event: MouseButtonEvent,
@@ -80,7 +65,7 @@ impl Deref for DownRegionEvent {
     }
 }
 
-#[derive(Debug, Default)]
+#[derive(Debug, Default, Clone)]
 pub struct UpRegionEvent {
     pub region: RectF,
     pub platform_event: MouseButtonEvent,
@@ -94,7 +79,7 @@ impl Deref for UpRegionEvent {
     }
 }
 
-#[derive(Debug, Default)]
+#[derive(Debug, Default, Clone)]
 pub struct ClickRegionEvent {
     pub region: RectF,
     pub platform_event: MouseButtonEvent,
@@ -108,7 +93,7 @@ impl Deref for ClickRegionEvent {
     }
 }
 
-#[derive(Debug, Default)]
+#[derive(Debug, Default, Clone)]
 pub struct DownOutRegionEvent {
     pub region: RectF,
     pub platform_event: MouseButtonEvent,
@@ -122,7 +107,7 @@ impl Deref for DownOutRegionEvent {
     }
 }
 
-#[derive(Debug, Default)]
+#[derive(Debug, Default, Clone)]
 pub struct UpOutRegionEvent {
     pub region: RectF,
     pub platform_event: MouseButtonEvent,
@@ -136,7 +121,7 @@ impl Deref for UpOutRegionEvent {
     }
 }
 
-#[derive(Debug, Default)]
+#[derive(Debug, Default, Clone)]
 pub struct ScrollWheelRegionEvent {
     pub region: RectF,
     pub platform_event: ScrollWheelEvent,
@@ -150,11 +135,10 @@ impl Deref for ScrollWheelRegionEvent {
     }
 }
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub enum MouseRegionEvent {
     Move(MoveRegionEvent),
     Drag(DragRegionEvent),
-    DragOver(DragOverRegionEvent),
     Hover(HoverRegionEvent),
     Down(DownRegionEvent),
     Up(UpRegionEvent),
@@ -164,6 +148,36 @@ pub enum MouseRegionEvent {
     ScrollWheel(ScrollWheelRegionEvent),
 }
 
+impl MouseRegionEvent {
+    pub fn set_region(&mut self, region: RectF) {
+        match self {
+            MouseRegionEvent::Move(r) => r.region = region,
+            MouseRegionEvent::Drag(r) => r.region = region,
+            MouseRegionEvent::Hover(r) => r.region = region,
+            MouseRegionEvent::Down(r) => r.region = region,
+            MouseRegionEvent::Up(r) => r.region = region,
+            MouseRegionEvent::Click(r) => r.region = region,
+            MouseRegionEvent::DownOut(r) => r.region = region,
+            MouseRegionEvent::UpOut(r) => r.region = region,
+            MouseRegionEvent::ScrollWheel(r) => r.region = region,
+        }
+    }
+
+    pub fn is_local(&self) -> bool {
+        match self {
+            MouseRegionEvent::Move(_) => true,
+            MouseRegionEvent::Drag(_) => true,
+            MouseRegionEvent::Hover(_) => true,
+            MouseRegionEvent::Down(_) => true,
+            MouseRegionEvent::Up(_) => true,
+            MouseRegionEvent::Click(_) => true,
+            MouseRegionEvent::DownOut(_) => false,
+            MouseRegionEvent::UpOut(_) => false,
+            MouseRegionEvent::ScrollWheel(_) => true,
+        }
+    }
+}
+
 impl MouseRegionEvent {
     pub fn move_disc() -> Discriminant<MouseRegionEvent> {
         discriminant(&MouseRegionEvent::Move(Default::default()))
@@ -173,10 +187,6 @@ impl MouseRegionEvent {
         discriminant(&MouseRegionEvent::Drag(Default::default()))
     }
 
-    pub fn drag_over_disc() -> Discriminant<MouseRegionEvent> {
-        discriminant(&MouseRegionEvent::DragOver(Default::default()))
-    }
-
     pub fn hover_disc() -> Discriminant<MouseRegionEvent> {
         discriminant(&MouseRegionEvent::Hover(Default::default()))
     }
@@ -205,20 +215,10 @@ impl MouseRegionEvent {
         discriminant(&MouseRegionEvent::ScrollWheel(Default::default()))
     }
 
-    pub fn is_local(&self) -> bool {
-        match self {
-            MouseRegionEvent::DownOut(_)
-            | MouseRegionEvent::UpOut(_)
-            | MouseRegionEvent::DragOver(_) => false,
-            _ => true,
-        }
-    }
-
     pub fn handler_key(&self) -> (Discriminant<MouseRegionEvent>, Option<MouseButton>) {
         match self {
             MouseRegionEvent::Move(_) => (Self::move_disc(), None),
             MouseRegionEvent::Drag(e) => (Self::drag_disc(), e.pressed_button),
-            MouseRegionEvent::DragOver(e) => (Self::drag_over_disc(), e.pressed_button),
             MouseRegionEvent::Hover(_) => (Self::hover_disc(), None),
             MouseRegionEvent::Down(e) => (Self::down_disc(), Some(e.button)),
             MouseRegionEvent::Up(e) => (Self::up_disc(), Some(e.button)),

crates/workspace/src/sidebar.rs 🔗

@@ -190,7 +190,7 @@ impl Sidebar {
         .with_cursor_style(CursorStyle::ResizeLeftRight)
         .on_down(MouseButton::Left, |_, _| {}) // This prevents the mouse down event from being propagated elsewhere
         .on_drag(MouseButton::Left, move |e, cx| {
-            let delta = e.prev_drag_position.x() - e.position.x();
+            let delta = e.prev_mouse_position.x() - e.position.x();
             let prev_width = *actual_width.borrow();
             *custom_width.borrow_mut() = 0f32
                 .max(match side {