add mouse region handler bool for adding the handler above the child

K Simmons created

Change summary

crates/drag_and_drop/src/drag_and_drop.rs       |   2 
crates/editor/src/element.rs                    |  12 
crates/gpui/src/elements/flex.rs                |   2 
crates/gpui/src/elements/mouse_event_handler.rs |  72 +++-
crates/gpui/src/elements/overlay.rs             |  35 +-
crates/gpui/src/elements/uniform_list.rs        |   2 
crates/gpui/src/presenter.rs                    |   6 
crates/workspace/src/dock.rs                    |  39 +-
crates/workspace/src/pane.rs                    | 246 +++++++++---------
9 files changed, 225 insertions(+), 191 deletions(-)

Detailed changes

crates/drag_and_drop/src/drag_and_drop.rs 🔗

@@ -125,7 +125,7 @@ impl<V: View> DragAndDrop<V> {
                             cx.defer(|cx| {
                                 cx.update_global::<Self, _, _>(|this, cx| this.stop_dragging(cx));
                             });
-                            cx.propogate_event();
+                            cx.propagate_event();
                         })
                         .on_up_out(MouseButton::Left, |_, cx| {
                             cx.defer(|cx| {

crates/editor/src/element.rs 🔗

@@ -137,7 +137,7 @@ impl EditorElement {
                             gutter_bounds,
                             cx,
                         ) {
-                            cx.propogate_event();
+                            cx.propagate_event();
                         }
                     }
                 })
@@ -150,7 +150,7 @@ impl EditorElement {
                             text_bounds,
                             cx,
                         ) {
-                            cx.propogate_event();
+                            cx.propagate_event();
                         }
                     }
                 })
@@ -167,7 +167,7 @@ impl EditorElement {
                             text_bounds,
                             cx,
                         ) {
-                            cx.propogate_event()
+                            cx.propagate_event()
                         }
                     }
                 })
@@ -182,7 +182,7 @@ impl EditorElement {
                             text_bounds,
                             cx,
                         ) {
-                            cx.propogate_event()
+                            cx.propagate_event()
                         }
                     }
                 })
@@ -190,7 +190,7 @@ impl EditorElement {
                     let position_map = position_map.clone();
                     move |e, cx| {
                         if !Self::mouse_moved(e.platform_event, &position_map, text_bounds, cx) {
-                            cx.propogate_event()
+                            cx.propagate_event()
                         }
                     }
                 })
@@ -199,7 +199,7 @@ impl EditorElement {
                     move |e, cx| {
                         if !Self::scroll(e.position, e.delta, e.precise, &position_map, bounds, cx)
                         {
-                            cx.propogate_event()
+                            cx.propagate_event()
                         }
                     }
                 }),

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

@@ -23,10 +23,14 @@ pub struct MouseEventHandler<Tag: 'static> {
     hoverable: bool,
     notify_on_hover: bool,
     notify_on_click: bool,
+    above: bool,
     padding: Padding,
     _tag: PhantomData<Tag>,
 }
 
+// MouseEventHandler::new
+// MouseEventHandler::above
+
 impl<Tag> MouseEventHandler<Tag> {
     pub fn new<V, F>(region_id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self
     where
@@ -45,11 +49,22 @@ impl<Tag> MouseEventHandler<Tag> {
             notify_on_hover,
             notify_on_click,
             hoverable: true,
+            above: false,
             padding: Default::default(),
             _tag: PhantomData,
         }
     }
 
+    pub fn above<V, F>(region_id: usize, cx: &mut RenderContext<V>, render_child: F) -> Self
+    where
+        V: View,
+        F: FnOnce(&mut MouseState, &mut RenderContext<V>) -> ElementBox,
+    {
+        let mut handler = Self::new(region_id, cx, render_child);
+        handler.above = true;
+        handler
+    }
+
     pub fn with_cursor_style(mut self, cursor: CursorStyle) -> Self {
         self.cursor_style = Some(cursor);
         self
@@ -149,6 +164,29 @@ impl<Tag> MouseEventHandler<Tag> {
         )
         .round_out()
     }
+
+    fn paint_regions(&self, bounds: RectF, visible_bounds: RectF, cx: &mut PaintContext) {
+        let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
+        let hit_bounds = self.hit_bounds(visible_bounds);
+
+        if let Some(style) = self.cursor_style {
+            cx.scene.push_cursor_region(CursorRegion {
+                bounds: hit_bounds,
+                style,
+            });
+        }
+        cx.scene.push_mouse_region(
+            MouseRegion::from_handlers::<Tag>(
+                cx.current_view_id(),
+                self.region_id,
+                hit_bounds,
+                self.handlers.clone(),
+            )
+            .with_hoverable(self.hoverable)
+            .with_notify_on_hover(self.notify_on_hover)
+            .with_notify_on_click(self.notify_on_click),
+        );
+    }
 }
 
 impl<Tag> Element for MouseEventHandler<Tag> {
@@ -170,30 +208,16 @@ impl<Tag> Element for MouseEventHandler<Tag> {
         _: &mut Self::LayoutState,
         cx: &mut PaintContext,
     ) -> Self::PaintState {
-        let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
-        let hit_bounds = self.hit_bounds(visible_bounds);
-
-        self.child.paint(bounds.origin(), visible_bounds, cx);
-
-        cx.paint_stacking_context(None, |cx| {
-            if let Some(style) = self.cursor_style {
-                cx.scene.push_cursor_region(CursorRegion {
-                    bounds: hit_bounds,
-                    style,
-                });
-            }
-            cx.scene.push_mouse_region(
-                MouseRegion::from_handlers::<Tag>(
-                    cx.current_view_id(),
-                    self.region_id,
-                    hit_bounds,
-                    self.handlers.clone(),
-                )
-                .with_hoverable(self.hoverable)
-                .with_notify_on_hover(self.notify_on_hover)
-                .with_notify_on_click(self.notify_on_click),
-            );
-        });
+        if self.above {
+            self.child.paint(bounds.origin(), visible_bounds, cx);
+
+            cx.paint_layer(None, |cx| {
+                self.paint_regions(bounds, visible_bounds, cx);
+            });
+        } else {
+            self.paint_regions(bounds, visible_bounds, cx);
+            self.child.paint(bounds.origin(), visible_bounds, cx);
+        }
     }
 
     fn rect_for_text_range(

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

@@ -204,25 +204,24 @@ impl Element for Overlay {
             OverlayFitMode::None => {}
         }
 
-        cx.scene.push_stacking_context(None);
-
-        if self.hoverable {
-            enum OverlayHoverCapture {}
-            // Block hovers in lower stacking contexts
-            cx.scene
-                .push_mouse_region(MouseRegion::new::<OverlayHoverCapture>(
-                    cx.current_view_id(),
-                    cx.current_view_id(),
-                    bounds,
-                ));
-        }
+        cx.paint_stacking_context(None, |cx| {
+            if self.hoverable {
+                enum OverlayHoverCapture {}
+                // Block hovers in lower stacking contexts
+                cx.scene
+                    .push_mouse_region(MouseRegion::new::<OverlayHoverCapture>(
+                        cx.current_view_id(),
+                        cx.current_view_id(),
+                        bounds,
+                    ));
+            }
 
-        self.child.paint(
-            bounds.origin(),
-            RectF::new(Vector2F::zero(), cx.window_size),
-            cx,
-        );
-        cx.scene.pop_stacking_context();
+            self.child.paint(
+                bounds.origin(),
+                RectF::new(Vector2F::zero(), cx.window_size),
+                cx,
+            );
+        });
     }
 
     fn rect_for_text_range(

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

@@ -304,7 +304,7 @@ impl Element for UniformList {
                       },
                       cx| {
                     if !Self::scroll(state.clone(), position, delta, precise, scroll_max, cx) {
-                        cx.propogate_event();
+                        cx.propagate_event();
                     }
                 }
             }),

crates/gpui/src/presenter.rs 🔗

@@ -485,9 +485,7 @@ impl Presenter {
                     event_cx.handled = true;
                     event_cx.with_current_view(valid_region.id().view_id(), {
                         let region_event = mouse_event.clone();
-                        |cx| {
-                            callback(region_event, cx);
-                        }
+                        |cx| callback(region_event, cx)
                     });
                 }
 
@@ -804,7 +802,7 @@ impl<'a> EventContext<'a> {
         self.notify_count
     }
 
-    pub fn propogate_event(&mut self) {
+    pub fn propagate_event(&mut self) {
         self.handled = false;
     }
 }

crates/workspace/src/dock.rs 🔗

@@ -1,7 +1,7 @@
 use collections::HashMap;
 use gpui::{
     actions,
-    elements::{ChildView, Container, Empty, MouseEventHandler, Side, Svg},
+    elements::{ChildView, Container, Empty, MouseEventHandler, ParentElement, Side, Stack, Svg},
     impl_internal_actions, Border, CursorStyle, Element, ElementBox, Entity, MouseButton,
     MutableAppContext, RenderContext, View, ViewContext, ViewHandle, WeakViewHandle,
 };
@@ -308,25 +308,34 @@ impl Dock {
                 DockAnchor::Expanded => {
                     enum ExpandedDockWash {}
                     enum ExpandedDockPane {}
-                    Container::new(
-                        MouseEventHandler::<ExpandedDockWash>::new(0, cx, |_state, cx| {
+                    Stack::new()
+                        .with_child(
+                            // Render wash under the dock which when clicked hides it
+                            MouseEventHandler::<ExpandedDockWash>::new(0, cx, |_, _| {
+                                Empty::new()
+                                    .contained()
+                                    .with_background_color(style.wash_color)
+                                    .boxed()
+                            })
+                            .capture_all()
+                            .on_down(MouseButton::Left, |_, cx| {
+                                cx.dispatch_action(HideDock);
+                            })
+                            .with_cursor_style(CursorStyle::Arrow)
+                            .boxed(),
+                        )
+                        .with_child(
                             MouseEventHandler::<ExpandedDockPane>::new(0, cx, |_state, cx| {
                                 ChildView::new(&self.pane, cx).boxed()
                             })
+                            // Make sure all events directly under the dock pane
+                            // are captured
                             .capture_all()
                             .contained()
                             .with_style(style.maximized)
-                            .boxed()
-                        })
-                        .capture_all()
-                        .on_down(MouseButton::Left, |_, cx| {
-                            cx.dispatch_action(HideDock);
-                        })
-                        .with_cursor_style(CursorStyle::Arrow)
-                        .boxed(),
-                    )
-                    .with_background_color(style.wash_color)
-                    .boxed()
+                            .boxed(),
+                        )
+                        .boxed()
                 }
             })
     }
@@ -391,7 +400,7 @@ impl View for ToggleDockButton {
         .on_up(MouseButton::Left, move |_, cx| {
             let dock_pane = workspace.read(cx.app).dock_pane();
             let drop_index = dock_pane.read(cx.app).items_len() + 1;
-            Pane::handle_dropped_item(&dock_pane.downgrade(), drop_index, cx);
+            Pane::handle_dropped_item(&dock_pane.downgrade(), drop_index, false, cx);
         });
 
         if dock_position.is_visible() {

crates/workspace/src/pane.rs 🔗

@@ -1061,127 +1061,123 @@ impl Pane {
         enum Tab {}
         enum Filler {}
         let pane = cx.handle();
-        MouseEventHandler::<Tabs>::new(0, cx, |_, cx| {
-            let autoscroll = if mem::take(&mut self.autoscroll) {
-                Some(self.active_item_index)
-            } else {
-                None
-            };
-
-            let pane_active = self.is_active;
-
-            let mut row = Flex::row().scrollable::<Tabs, _>(1, autoscroll, cx);
-            for (ix, (item, detail)) in self
-                .items
-                .iter()
-                .cloned()
-                .zip(self.tab_details(cx))
-                .enumerate()
-            {
-                let detail = if detail == 0 { None } else { Some(detail) };
-                let tab_active = ix == self.active_item_index;
+        let autoscroll = if mem::take(&mut self.autoscroll) {
+            Some(self.active_item_index)
+        } else {
+            None
+        };
 
-                row.add_child({
-                    MouseEventHandler::<Tab>::new(ix, cx, {
-                        let item = item.clone();
-                        let pane = pane.clone();
-                        let detail = detail.clone();
+        let pane_active = self.is_active;
 
+        let mut row = Flex::row().scrollable::<Tabs, _>(1, autoscroll, cx);
+        for (ix, (item, detail)) in self
+            .items
+            .iter()
+            .cloned()
+            .zip(self.tab_details(cx))
+            .enumerate()
+        {
+            let detail = if detail == 0 { None } else { Some(detail) };
+            let tab_active = ix == self.active_item_index;
+
+            row.add_child({
+                MouseEventHandler::<Tab>::above(ix, cx, {
+                    let item = item.clone();
+                    let pane = pane.clone();
+                    let detail = detail.clone();
+
+                    let theme = cx.global::<Settings>().theme.clone();
+
+                    move |mouse_state, cx| {
+                        let tab_style = theme.workspace.tab_bar.tab_style(pane_active, tab_active);
+                        let hovered = mouse_state.hovered();
+                        Self::render_tab(
+                            &item,
+                            pane,
+                            ix == 0,
+                            detail,
+                            hovered,
+                            Self::tab_overlay_color(hovered, theme.as_ref(), cx),
+                            tab_style,
+                            cx,
+                        )
+                    }
+                })
+                .with_cursor_style(if pane_active && tab_active {
+                    CursorStyle::Arrow
+                } else {
+                    CursorStyle::PointingHand
+                })
+                .on_down(MouseButton::Left, move |_, cx| {
+                    cx.dispatch_action(ActivateItem(ix));
+                    cx.propagate_event();
+                })
+                .on_click(MouseButton::Middle, {
+                    let item = item.clone();
+                    let pane = pane.clone();
+                    move |_, cx: &mut EventContext| {
+                        cx.dispatch_action(CloseItem {
+                            item_id: item.id(),
+                            pane: pane.clone(),
+                        })
+                    }
+                })
+                .on_up(MouseButton::Left, {
+                    let pane = pane.clone();
+                    move |_, cx: &mut EventContext| Pane::handle_dropped_item(&pane, ix, true, cx)
+                })
+                .as_draggable(
+                    DraggedItem {
+                        item,
+                        pane: pane.clone(),
+                    },
+                    {
                         let theme = cx.global::<Settings>().theme.clone();
 
-                        move |mouse_state, cx| {
-                            let tab_style =
-                                theme.workspace.tab_bar.tab_style(pane_active, tab_active);
-                            let hovered = mouse_state.hovered();
+                        let detail = detail.clone();
+                        move |dragged_item, cx: &mut RenderContext<Workspace>| {
+                            let tab_style = &theme.workspace.tab_bar.dragged_tab;
                             Self::render_tab(
-                                &item,
-                                pane,
-                                ix == 0,
+                                &dragged_item.item,
+                                dragged_item.pane.clone(),
+                                false,
                                 detail,
-                                hovered,
-                                Self::tab_overlay_color(hovered, theme.as_ref(), cx),
-                                tab_style,
+                                false,
+                                None,
+                                &tab_style,
                                 cx,
                             )
                         }
-                    })
-                    .with_cursor_style(if pane_active && tab_active {
-                        CursorStyle::Arrow
-                    } else {
-                        CursorStyle::PointingHand
-                    })
-                    .on_down(MouseButton::Left, move |_, cx| {
-                        cx.dispatch_action(ActivateItem(ix));
-                    })
-                    .on_click(MouseButton::Middle, {
-                        let item = item.clone();
-                        let pane = pane.clone();
-                        move |_, cx: &mut EventContext| {
-                            cx.dispatch_action(CloseItem {
-                                item_id: item.id(),
-                                pane: pane.clone(),
-                            })
-                        }
-                    })
-                    .on_up(MouseButton::Left, {
-                        let pane = pane.clone();
-                        move |_, cx: &mut EventContext| Pane::handle_dropped_item(&pane, ix, cx)
-                    })
-                    .as_draggable(
-                        DraggedItem {
-                            item,
-                            pane: pane.clone(),
-                        },
-                        {
-                            let theme = cx.global::<Settings>().theme.clone();
-
-                            let detail = detail.clone();
-                            move |dragged_item, cx: &mut RenderContext<Workspace>| {
-                                let tab_style = &theme.workspace.tab_bar.dragged_tab;
-                                Self::render_tab(
-                                    &dragged_item.item,
-                                    dragged_item.pane.clone(),
-                                    false,
-                                    detail,
-                                    false,
-                                    None,
-                                    &tab_style,
-                                    cx,
-                                )
-                            }
-                        },
-                    )
-                    .boxed()
-                })
-            }
+                    },
+                )
+                .boxed()
+            })
+        }
 
-            // Use the inactive tab style along with the current pane's active status to decide how to render
-            // the filler
-            let filler_style = theme.workspace.tab_bar.tab_style(pane_active, false);
-            row.add_child(
-                MouseEventHandler::<Filler>::new(0, cx, |mouse_state, cx| {
-                    let mut filler = Empty::new()
-                        .contained()
-                        .with_style(filler_style.container)
-                        .with_border(filler_style.container.border);
-
-                    if let Some(overlay) =
-                        Self::tab_overlay_color(mouse_state.hovered(), &theme, cx)
-                    {
-                        filler = filler.with_overlay_color(overlay);
-                    }
+        // Use the inactive tab style along with the current pane's active status to decide how to render
+        // the filler
+        let filler_style = theme.workspace.tab_bar.tab_style(pane_active, false);
+        row.add_child(
+            MouseEventHandler::<Filler>::new(0, cx, |mouse_state, cx| {
+                let mut filler = Empty::new()
+                    .contained()
+                    .with_style(filler_style.container)
+                    .with_border(filler_style.container.border);
+
+                if let Some(overlay) = Self::tab_overlay_color(mouse_state.hovered(), &theme, cx) {
+                    filler = filler.with_overlay_color(overlay);
+                }
 
-                    filler.boxed()
-                })
-                .flex(1., true)
-                .named("filler"),
-            );
+                filler.boxed()
+            })
+            .on_up(MouseButton::Left, move |_, cx| {
+                Pane::handle_dropped_item(&pane, filler_index, true, cx)
+            })
+            .flex(1., true)
+            .named("filler"),
+        );
 
-            row.boxed()
-        })
-        .on_up(MouseButton::Left, move |_, cx| {
-            Pane::handle_dropped_item(&pane, filler_index, cx)
-        })
+        row
     }
 
     fn tab_details(&self, cx: &AppContext) -> Vec<usize> {
@@ -1305,7 +1301,6 @@ impl Pane {
                                 })
                             }
                         })
-                        .on_click(MouseButton::Middle, |_, cx| cx.propogate_event())
                         .named("close-tab-icon")
                     } else {
                         Empty::new().boxed()
@@ -1325,19 +1320,26 @@ impl Pane {
         tab.constrained().with_height(tab_style.height).boxed()
     }
 
-    pub fn handle_dropped_item(pane: &WeakViewHandle<Pane>, index: usize, cx: &mut EventContext) {
+    pub fn handle_dropped_item(
+        pane: &WeakViewHandle<Pane>,
+        index: usize,
+        allow_same_pane: bool,
+        cx: &mut EventContext,
+    ) {
         if let Some((_, dragged_item)) = cx
             .global::<DragAndDrop<Workspace>>()
             .currently_dragged::<DraggedItem>(cx.window_id)
         {
-            cx.dispatch_action(MoveItem {
-                item_id: dragged_item.item.id(),
-                from: dragged_item.pane.clone(),
-                to: pane.clone(),
-                destination_index: index,
-            })
+            if pane != &dragged_item.pane || allow_same_pane {
+                cx.dispatch_action(MoveItem {
+                    item_id: dragged_item.item.id(),
+                    from: dragged_item.pane.clone(),
+                    to: pane.clone(),
+                    destination_index: index,
+                })
+            }
         } else {
-            cx.propogate_event();
+            cx.propagate_event();
         }
     }
 
@@ -1448,7 +1450,7 @@ impl View for Pane {
                             })
                             .with_child({
                                 let drop_index = self.active_item_index + 1;
-                                MouseEventHandler::<PaneContentTabDropTarget>::new(
+                                MouseEventHandler::<PaneContentTabDropTarget>::above(
                                     0,
                                     cx,
                                     |_, cx| {
@@ -1469,7 +1471,7 @@ impl View for Pane {
                                 .on_up(MouseButton::Left, {
                                     let pane = cx.handle();
                                     move |_, cx: &mut EventContext| {
-                                        Pane::handle_dropped_item(&pane, drop_index, cx)
+                                        Pane::handle_dropped_item(&pane, drop_index, false, cx)
                                     }
                                 })
                                 .flex(1., true)
@@ -1491,7 +1493,9 @@ impl View for Pane {
                         })
                         .on_up(MouseButton::Left, {
                             let pane = this.clone();
-                            move |_, cx: &mut EventContext| Pane::handle_dropped_item(&pane, 0, cx)
+                            move |_, cx: &mut EventContext| {
+                                Pane::handle_dropped_item(&pane, 0, true, cx)
+                            }
                         })
                         .boxed()
                     }