WIP: Add click out event to fix context menus

Mikayla Maki created

Change summary

crates/context_menu/src/context_menu.rs | 27 +++++++++++++---------
crates/gpui/src/app/window.rs           |  6 ++++
crates/gpui/src/scene/mouse_event.rs    | 22 ++++++++++++++++++
crates/gpui/src/scene/mouse_region.rs   | 33 ++++++++++++++++++++++++++
4 files changed, 75 insertions(+), 13 deletions(-)

Detailed changes

crates/context_menu/src/context_menu.rs 🔗

@@ -301,18 +301,23 @@ impl ContextMenu {
         cx: &mut ViewContext<Self>,
     ) {
         let mut items = items.into_iter().peekable();
-        if items.peek().is_some() {
-            self.items = items.collect();
-            self.anchor_position = anchor_position;
-            self.anchor_corner = anchor_corner;
-            self.visible = true;
-            self.show_count += 1;
-            if !cx.is_self_focused() {
-                self.previously_focused_view_id = cx.focused_view_id();
-            }
-            cx.focus_self();
-        } else {
+        dbg!(self.visible);
+        if (self.visible) {
             self.visible = false;
+        } else {
+            if items.peek().is_some() {
+                self.items = items.collect();
+                self.anchor_position = anchor_position;
+                self.anchor_corner = anchor_corner;
+                self.visible = true;
+                self.show_count += 1;
+                if !cx.is_self_focused() {
+                    self.previously_focused_view_id = cx.focused_view_id();
+                }
+                cx.focus_self();
+            } else {
+                self.visible = false;
+            }
         }
         cx.notify();
     }

crates/gpui/src/app/window.rs 🔗

@@ -9,7 +9,7 @@ use crate::{
     },
     scene::{
         CursorRegion, MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseEvent, MouseHover,
-        MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut, Scene,
+        MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut, Scene, MouseClickOut,
     },
     text_layout::TextLayoutCache,
     util::post_inc,
@@ -524,6 +524,10 @@ impl<'a> WindowContext<'a> {
                     region: Default::default(),
                     platform_event: e.clone(),
                 }));
+                mouse_events.push(MouseEvent::ClickOut(MouseClickOut {
+                    region: Default::default(),
+                    platform_event: e.clone(),
+                }));
             }
 
             Event::MouseMoved(

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

@@ -99,6 +99,20 @@ impl Deref for MouseClick {
     }
 }
 
+#[derive(Debug, Default, Clone)]
+pub struct MouseClickOut {
+    pub region: RectF,
+    pub platform_event: MouseButtonEvent,
+}
+
+impl Deref for MouseClickOut {
+    type Target = MouseButtonEvent;
+
+    fn deref(&self) -> &Self::Target {
+        &self.platform_event
+    }
+}
+
 #[derive(Debug, Default, Clone)]
 pub struct MouseDownOut {
     pub region: RectF,
@@ -150,6 +164,7 @@ pub enum MouseEvent {
     Down(MouseDown),
     Up(MouseUp),
     Click(MouseClick),
+    ClickOut(MouseClickOut),
     DownOut(MouseDownOut),
     UpOut(MouseUpOut),
     ScrollWheel(MouseScrollWheel),
@@ -165,6 +180,7 @@ impl MouseEvent {
             MouseEvent::Down(r) => r.region = region,
             MouseEvent::Up(r) => r.region = region,
             MouseEvent::Click(r) => r.region = region,
+            MouseEvent::ClickOut(r) => r.region = region,
             MouseEvent::DownOut(r) => r.region = region,
             MouseEvent::UpOut(r) => r.region = region,
             MouseEvent::ScrollWheel(r) => r.region = region,
@@ -182,6 +198,7 @@ impl MouseEvent {
             MouseEvent::Down(_) => true,
             MouseEvent::Up(_) => true,
             MouseEvent::Click(_) => true,
+            MouseEvent::ClickOut(_) => true,
             MouseEvent::DownOut(_) => false,
             MouseEvent::UpOut(_) => false,
             MouseEvent::ScrollWheel(_) => true,
@@ -222,6 +239,10 @@ impl MouseEvent {
         discriminant(&MouseEvent::Click(Default::default()))
     }
 
+    pub fn click_out_disc() -> Discriminant<MouseEvent> {
+        discriminant(&MouseEvent::ClickOut(Default::default()))
+    }
+
     pub fn down_out_disc() -> Discriminant<MouseEvent> {
         discriminant(&MouseEvent::DownOut(Default::default()))
     }
@@ -239,6 +260,7 @@ impl MouseEvent {
             MouseEvent::Down(e) => HandlerKey::new(Self::down_disc(), Some(e.button)),
             MouseEvent::Up(e) => HandlerKey::new(Self::up_disc(), Some(e.button)),
             MouseEvent::Click(e) => HandlerKey::new(Self::click_disc(), Some(e.button)),
+            MouseEvent::ClickOut(e) => HandlerKey::new(Self::click_out_disc(), Some(e.button)),
             MouseEvent::UpOut(e) => HandlerKey::new(Self::up_out_disc(), Some(e.button)),
             MouseEvent::DownOut(e) => HandlerKey::new(Self::down_out_disc(), Some(e.button)),
             MouseEvent::ScrollWheel(_) => HandlerKey::new(Self::scroll_wheel_disc(), None),

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

@@ -14,7 +14,7 @@ use super::{
         MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseEvent, MouseHover, MouseMove, MouseUp,
         MouseUpOut,
     },
-    MouseMoveOut, MouseScrollWheel,
+    MouseMoveOut, MouseScrollWheel, MouseClickOut,
 };
 
 #[derive(Clone)]
@@ -89,6 +89,15 @@ impl MouseRegion {
         self
     }
 
+    pub fn on_click_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseClickOut, &mut V, &mut EventContext<V>) + 'static,
+    {
+        self.handlers = self.handlers.on_click(button, handler);
+        self
+    }
+
     pub fn on_down_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
     where
         V: View,
@@ -405,6 +414,28 @@ impl HandlerSet {
         self
     }
 
+    pub fn on_click_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
+    where
+        V: View,
+        F: Fn(MouseClickOut, &mut V, &mut EventContext<V>) + 'static,
+    {
+        self.insert(MouseEvent::click_out_disc(), Some(button),
+            Rc::new(move |region_event, view, cx, view_id| {
+                if let MouseEvent::ClickOut(e) = region_event {
+                    let view = view.downcast_mut().unwrap();
+                    let mut cx = ViewContext::mutable(cx, view_id);
+                    let mut cx = EventContext::new(&mut cx);
+                    handler(e, view, &mut cx);
+                    cx.handled
+                } else {
+                    panic!(
+                        "Mouse Region Event incorrectly called with mismatched event type. Expected MouseRegionEvent::ClickOut, found {:?}",
+                        region_event);
+                }
+            }));
+        self
+    }
+
     pub fn on_down_out<V, F>(mut self, button: MouseButton, handler: F) -> Self
     where
         V: View,