Adjust the type arrangement on ManagedViews (#3354)

Mikayla Maki created

Made the trait into a blanket-trait impl if you have it's two
constituent pieces (FocusableView and EventEmitter) to remove the
duplicated method. I also changed the struct to an enum for aesthetic
reasons (EventType::EventName feels self documenting to me) and added
some new `cx` APIs utilizing our new powers of dismissal.

Release Notes:

- N/A

Change summary

crates/command_palette2/src/command_palette.rs | 11 ++++--
crates/file_finder2/src/file_finder.rs         | 15 +++++---
crates/go_to_line2/src/go_to_line.rs           | 16 +++++----
crates/gpui2/src/app/async_context.rs          | 11 ++++++
crates/gpui2/src/app/test_context.rs           | 11 +++++++
crates/gpui2/src/gpui2.rs                      |  4 ++
crates/gpui2/src/window.rs                     | 31 ++++++++++++++-----
crates/ui2/src/components/context_menu.rs      | 20 +++++++-----
8 files changed, 83 insertions(+), 36 deletions(-)

Detailed changes

crates/command_palette2/src/command_palette.rs 🔗

@@ -1,8 +1,9 @@
 use collections::{CommandPaletteFilter, HashMap};
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
-    actions, div, prelude::*, Action, AppContext, Component, Dismiss, Div, FocusHandle, Keystroke,
-    ManagedView, ParentComponent, Render, Styled, View, ViewContext, VisualContext, WeakView,
+    actions, div, prelude::*, Action, AppContext, Component, Div, EventEmitter, FocusHandle,
+    FocusableView, Keystroke, Manager, ParentComponent, Render, Styled, View, ViewContext,
+    VisualContext, WeakView,
 };
 use picker::{Picker, PickerDelegate};
 use std::{
@@ -68,7 +69,9 @@ impl CommandPalette {
     }
 }
 
-impl ManagedView for CommandPalette {
+impl EventEmitter<Manager> for CommandPalette {}
+
+impl FocusableView for CommandPalette {
     fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
@@ -265,7 +268,7 @@ impl PickerDelegate for CommandPaletteDelegate {
 
     fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
         self.command_palette
-            .update(cx, |_, cx| cx.emit(Dismiss))
+            .update(cx, |_, cx| cx.emit(Manager::Dismiss))
             .log_err();
     }
 

crates/file_finder2/src/file_finder.rs 🔗

@@ -2,9 +2,9 @@ use collections::HashMap;
 use editor::{scroll::autoscroll::Autoscroll, Bias, Editor};
 use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
 use gpui::{
-    actions, div, AppContext, Component, Dismiss, Div, FocusHandle, InteractiveComponent,
-    ManagedView, Model, ParentComponent, Render, Styled, Task, View, ViewContext, VisualContext,
-    WeakView,
+    actions, div, AppContext, Component, Div, EventEmitter, FocusHandle, FocusableView,
+    InteractiveComponent, Manager, Model, ParentComponent, Render, Styled, Task, View, ViewContext,
+    VisualContext, WeakView,
 };
 use picker::{Picker, PickerDelegate};
 use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId};
@@ -111,7 +111,8 @@ impl FileFinder {
     }
 }
 
-impl ManagedView for FileFinder {
+impl EventEmitter<Manager> for FileFinder {}
+impl FocusableView for FileFinder {
     fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
         self.picker.focus_handle(cx)
     }
@@ -688,7 +689,9 @@ impl PickerDelegate for FileFinderDelegate {
                                 .log_err();
                         }
                     }
-                    finder.update(&mut cx, |_, cx| cx.emit(Dismiss)).ok()?;
+                    finder
+                        .update(&mut cx, |_, cx| cx.emit(Manager::Dismiss))
+                        .ok()?;
 
                     Some(())
                 })
@@ -699,7 +702,7 @@ impl PickerDelegate for FileFinderDelegate {
 
     fn dismissed(&mut self, cx: &mut ViewContext<Picker<FileFinderDelegate>>) {
         self.file_finder
-            .update(cx, |_, cx| cx.emit(Dismiss))
+            .update(cx, |_, cx| cx.emit(Manager::Dismiss))
             .log_err();
     }
 

crates/go_to_line2/src/go_to_line.rs 🔗

@@ -1,7 +1,8 @@
 use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor};
 use gpui::{
-    actions, div, prelude::*, AppContext, Dismiss, Div, FocusHandle, ManagedView, ParentComponent,
-    Render, SharedString, Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
+    actions, div, prelude::*, AppContext, Div, EventEmitter, FocusHandle, FocusableView, Manager,
+    ParentComponent, Render, SharedString, Styled, Subscription, View, ViewContext, VisualContext,
+    WindowContext,
 };
 use text::{Bias, Point};
 use theme::ActiveTheme;
@@ -23,11 +24,12 @@ pub struct GoToLine {
     _subscriptions: Vec<Subscription>,
 }
 
-impl ManagedView for GoToLine {
+impl FocusableView for GoToLine {
     fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
-        self.line_editor.focus_handle(cx)
+        self.active_editor.focus_handle(cx)
     }
 }
+impl EventEmitter<Manager> for GoToLine {}
 
 impl GoToLine {
     fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
@@ -87,7 +89,7 @@ impl GoToLine {
     ) {
         match event {
             // todo!() this isn't working...
-            editor::Event::Blurred => cx.emit(Dismiss),
+            editor::Event::Blurred => cx.emit(Manager::Dismiss),
             editor::Event::BufferEdited { .. } => self.highlight_current_line(cx),
             _ => {}
         }
@@ -122,7 +124,7 @@ impl GoToLine {
     }
 
     fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
-        cx.emit(Dismiss);
+        cx.emit(Manager::Dismiss);
     }
 
     fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
@@ -139,7 +141,7 @@ impl GoToLine {
             self.prev_scroll_position.take();
         }
 
-        cx.emit(Dismiss);
+        cx.emit(Manager::Dismiss);
     }
 }
 

crates/gpui2/src/app/async_context.rs 🔗

@@ -1,6 +1,6 @@
 use crate::{
     AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, Context, FocusableView,
-    ForegroundExecutor, Model, ModelContext, Render, Result, Task, View, ViewContext,
+    ForegroundExecutor, Manager, Model, ModelContext, Render, Result, Task, View, ViewContext,
     VisualContext, WindowContext, WindowHandle,
 };
 use anyhow::{anyhow, Context as _};
@@ -320,4 +320,13 @@ impl VisualContext for AsyncWindowContext {
             view.read(cx).focus_handle(cx).clone().focus(cx);
         })
     }
+
+    fn dismiss_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
+    where
+        V: crate::ManagedView,
+    {
+        self.window.update(self, |_, cx| {
+            view.update(cx, |_, cx| cx.emit(Manager::Dismiss))
+        })
+    }
 }

crates/gpui2/src/app/test_context.rs 🔗

@@ -579,6 +579,17 @@ impl<'a> VisualContext for VisualTestContext<'a> {
             })
             .unwrap()
     }
+
+    fn dismiss_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
+    where
+        V: crate::ManagedView,
+    {
+        self.window
+            .update(self.cx, |_, cx| {
+                view.update(cx, |_, cx| cx.emit(crate::Manager::Dismiss))
+            })
+            .unwrap()
+    }
 }
 
 impl AnyWindowHandle {

crates/gpui2/src/gpui2.rs 🔗

@@ -141,6 +141,10 @@ pub trait VisualContext: Context {
     fn focus_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
     where
         V: FocusableView;
+
+    fn dismiss_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
+    where
+        V: ManagedView;
 }
 
 pub trait Entity<T>: Sealed {

crates/gpui2/src/window.rs 🔗

@@ -193,17 +193,12 @@ pub trait FocusableView: Render {
 
 /// ManagedView is a view (like a Modal, Popover, Menu, etc.)
 /// where the lifecycle of the view is handled by another view.
-pub trait ManagedView: Render {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle;
-}
+pub trait ManagedView: FocusableView + EventEmitter<Manager> {}
 
-pub struct Dismiss;
-impl<T: ManagedView> EventEmitter<Dismiss> for T {}
+impl<M: FocusableView + EventEmitter<Manager>> ManagedView for M {}
 
-impl<T: ManagedView> FocusableView for T {
-    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
-        self.focus_handle(cx)
-    }
+pub enum Manager {
+    Dismiss,
 }
 
 // Holds the state for a specific window.
@@ -1582,6 +1577,13 @@ impl VisualContext for WindowContext<'_> {
             view.focus_handle(cx).clone().focus(cx);
         })
     }
+
+    fn dismiss_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
+    where
+        V: ManagedView,
+    {
+        self.update_view(view, |_, cx| cx.emit(Manager::Dismiss))
+    }
 }
 
 impl<'a> std::ops::Deref for WindowContext<'a> {
@@ -2275,6 +2277,13 @@ impl<'a, V: 'static> ViewContext<'a, V> {
     {
         self.defer(|view, cx| view.focus_handle(cx).focus(cx))
     }
+
+    pub fn dismiss_self(&mut self)
+    where
+        V: ManagedView,
+    {
+        self.defer(|_, cx| cx.emit(Manager::Dismiss))
+    }
 }
 
 impl<V> Context for ViewContext<'_, V> {
@@ -2354,6 +2363,10 @@ impl<V: 'static> VisualContext for ViewContext<'_, V> {
     fn focus_view<W: FocusableView>(&mut self, view: &View<W>) -> Self::Result<()> {
         self.window_cx.focus_view(view)
     }
+
+    fn dismiss_view<W: ManagedView>(&mut self, view: &View<W>) -> Self::Result<()> {
+        self.window_cx.dismiss_view(view)
+    }
 }
 
 impl<'a, V> std::ops::Deref for ViewContext<'a, V> {

crates/ui2/src/components/context_menu.rs 🔗

@@ -4,9 +4,9 @@ use std::rc::Rc;
 use crate::prelude::*;
 use crate::{v_stack, Label, List, ListEntry, ListItem, ListSeparator, ListSubHeader};
 use gpui::{
-    overlay, px, Action, AnchorCorner, AnyElement, Bounds, Dismiss, DispatchPhase, Div,
-    FocusHandle, LayoutId, ManagedView, MouseButton, MouseDownEvent, Pixels, Point, Render, View,
-    VisualContext, WeakView,
+    overlay, px, Action, AnchorCorner, AnyElement, AppContext, Bounds, DispatchPhase, Div,
+    EventEmitter, FocusHandle, FocusableView, LayoutId, ManagedView, Manager, MouseButton,
+    MouseDownEvent, Pixels, Point, Render, View, VisualContext, WeakView,
 };
 
 pub enum ContextMenuItem<V> {
@@ -24,12 +24,14 @@ pub struct ContextMenu<V> {
     handle: WeakView<V>,
 }
 
-impl<V: Render> ManagedView for ContextMenu<V> {
-    fn focus_handle(&self, cx: &gpui::AppContext) -> FocusHandle {
+impl<V: Render> FocusableView for ContextMenu<V> {
+    fn focus_handle(&self, _cx: &AppContext) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
 
+impl<V: Render> EventEmitter<Manager> for ContextMenu<V> {}
+
 impl<V: Render> ContextMenu<V> {
     pub fn build(
         cx: &mut ViewContext<V>,
@@ -76,11 +78,11 @@ impl<V: Render> ContextMenu<V> {
 
     pub fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
         // todo!()
-        cx.emit(Dismiss);
+        cx.emit(Manager::Dismiss);
     }
 
     pub fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
-        cx.emit(Dismiss);
+        cx.emit(Manager::Dismiss);
     }
 }
 
@@ -116,7 +118,7 @@ impl<V: Render> Render for ContextMenu<V> {
                                 let handle = self.handle.clone();
                                 ListItem::Entry(entry.clone().on_click(move |this, cx| {
                                     handle.update(cx, |view, cx| callback(view, cx)).ok();
-                                    cx.emit(Dismiss);
+                                    cx.emit(Manager::Dismiss);
                                 }))
                             }
                         })
@@ -276,7 +278,7 @@ impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
                 let new_menu = (builder)(view_state, cx);
                 let menu2 = menu.clone();
                 cx.subscribe(&new_menu, move |this, modal, e, cx| match e {
-                    &Dismiss => {
+                    &Manager::Dismiss => {
                         *menu2.borrow_mut() = None;
                         cx.notify();
                     }