+ManagedView

Conrad Irwin created

And some games with rust traits

Change summary

crates/command_palette2/src/command_palette.rs | 16 ++++------
crates/file_finder2/src/file_finder.rs         | 21 ++++++--------
crates/go_to_line2/src/go_to_line.rs           | 19 ++++++------
crates/gpui2/src/window.rs                     | 17 +++++++++++
crates/picker2/src/picker2.rs                  | 10 +++++-
crates/ui2/src/components/context_menu.rs      | 30 +++++++------------
crates/workspace2/src/modal_layer.rs           | 22 ++++----------
crates/workspace2/src/workspace2.rs            | 13 +++++---
8 files changed, 75 insertions(+), 73 deletions(-)

Detailed changes

crates/command_palette2/src/command_palette.rs 🔗

@@ -1,9 +1,8 @@
 use collections::{CommandPaletteFilter, HashMap};
 use fuzzy::{StringMatch, StringMatchCandidate};
 use gpui::{
-    actions, div, prelude::*, Action, AppContext, Component, Div, EventEmitter, FocusHandle,
-    Keystroke, ParentComponent, Render, Styled, View, ViewContext, VisualContext, WeakView,
-    WindowContext,
+    actions, div, prelude::*, Action, AppContext, Component, Dismiss, Div, FocusHandle, Keystroke,
+    ManagedView, ParentComponent, Render, Styled, View, ViewContext, VisualContext, WeakView,
 };
 use picker::{Picker, PickerDelegate};
 use std::{
@@ -16,7 +15,7 @@ use util::{
     channel::{parse_zed_link, ReleaseChannel, RELEASE_CHANNEL},
     ResultExt,
 };
-use workspace::{Modal, ModalEvent, Workspace};
+use workspace::Workspace;
 use zed_actions::OpenZedURL;
 
 actions!(Toggle);
@@ -69,10 +68,9 @@ impl CommandPalette {
     }
 }
 
-impl EventEmitter<ModalEvent> for CommandPalette {}
-impl Modal for CommandPalette {
-    fn focus(&self, cx: &mut WindowContext) {
-        self.picker.update(cx, |picker, cx| picker.focus(cx));
+impl ManagedView for CommandPalette {
+    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+        self.picker.focus_handle(cx)
     }
 }
 
@@ -267,7 +265,7 @@ impl PickerDelegate for CommandPaletteDelegate {
 
     fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
         self.command_palette
-            .update(cx, |_, cx| cx.emit(ModalEvent::Dismissed))
+            .update(cx, |_, cx| cx.emit(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, Div, EventEmitter, InteractiveComponent, Model,
-    ParentComponent, Render, Styled, Task, View, ViewContext, VisualContext, WeakView,
-    WindowContext,
+    actions, div, AppContext, Component, Dismiss, Div, FocusHandle, InteractiveComponent,
+    ManagedView, Model, ParentComponent, Render, Styled, Task, View, ViewContext, VisualContext,
+    WeakView,
 };
 use picker::{Picker, PickerDelegate};
 use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId};
@@ -19,7 +19,7 @@ use text::Point;
 use theme::ActiveTheme;
 use ui::{v_stack, HighlightedLabel, StyledExt};
 use util::{paths::PathLikeWithPosition, post_inc, ResultExt};
-use workspace::{Modal, ModalEvent, Workspace};
+use workspace::Workspace;
 
 actions!(Toggle);
 
@@ -111,10 +111,9 @@ impl FileFinder {
     }
 }
 
-impl EventEmitter<ModalEvent> for FileFinder {}
-impl Modal for FileFinder {
-    fn focus(&self, cx: &mut WindowContext) {
-        self.picker.update(cx, |picker, cx| picker.focus(cx))
+impl ManagedView for FileFinder {
+    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+        self.picker.focus_handle(cx)
     }
 }
 impl Render for FileFinder {
@@ -689,9 +688,7 @@ impl PickerDelegate for FileFinderDelegate {
                                 .log_err();
                         }
                     }
-                    finder
-                        .update(&mut cx, |_, cx| cx.emit(ModalEvent::Dismissed))
-                        .ok()?;
+                    finder.update(&mut cx, |_, cx| cx.emit(Dismiss)).ok()?;
 
                     Some(())
                 })
@@ -702,7 +699,7 @@ impl PickerDelegate for FileFinderDelegate {
 
     fn dismissed(&mut self, cx: &mut ViewContext<Picker<FileFinderDelegate>>) {
         self.file_finder
-            .update(cx, |_, cx| cx.emit(ModalEvent::Dismissed))
+            .update(cx, |_, cx| cx.emit(Dismiss))
             .log_err();
     }
 

crates/go_to_line2/src/go_to_line.rs 🔗

@@ -1,13 +1,13 @@
 use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor};
 use gpui::{
-    actions, div, prelude::*, AppContext, Div, EventEmitter, ParentComponent, Render, SharedString,
-    Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
+    actions, div, prelude::*, AppContext, Dismiss, Div, FocusHandle, ManagedView, ParentComponent,
+    Render, SharedString, Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
 };
 use text::{Bias, Point};
 use theme::ActiveTheme;
 use ui::{h_stack, v_stack, Label, StyledExt, TextColor};
 use util::paths::FILE_ROW_COLUMN_DELIMITER;
-use workspace::{Modal, ModalEvent, Workspace};
+use workspace::Workspace;
 
 actions!(Toggle);
 
@@ -23,10 +23,9 @@ pub struct GoToLine {
     _subscriptions: Vec<Subscription>,
 }
 
-impl EventEmitter<ModalEvent> for GoToLine {}
-impl Modal for GoToLine {
-    fn focus(&self, cx: &mut WindowContext) {
-        self.line_editor.update(cx, |editor, cx| editor.focus(cx))
+impl ManagedView for GoToLine {
+    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+        self.line_editor.focus_handle(cx)
     }
 }
 
@@ -88,7 +87,7 @@ impl GoToLine {
     ) {
         match event {
             // todo!() this isn't working...
-            editor::Event::Blurred => cx.emit(ModalEvent::Dismissed),
+            editor::Event::Blurred => cx.emit(Dismiss),
             editor::Event::BufferEdited { .. } => self.highlight_current_line(cx),
             _ => {}
         }
@@ -123,7 +122,7 @@ impl GoToLine {
     }
 
     fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
-        cx.emit(ModalEvent::Dismissed);
+        cx.emit(Dismiss);
     }
 
     fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
@@ -140,7 +139,7 @@ impl GoToLine {
             self.prev_scroll_position.take();
         }
 
-        cx.emit(ModalEvent::Dismissed);
+        cx.emit(Dismiss);
     }
 }
 

crates/gpui2/src/window.rs 🔗

@@ -185,10 +185,27 @@ impl Drop for FocusHandle {
     }
 }
 
+/// FocusableView allows users of your view to easily
+/// focus it (using cx.focus_view(view))
 pub trait FocusableView: Render {
     fn focus_handle(&self, cx: &AppContext) -> FocusHandle;
 }
 
+/// 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 struct Dismiss;
+impl<T: ManagedView> EventEmitter<Dismiss> for T {}
+
+impl<T: ManagedView> FocusableView for T {
+    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+        self.focus_handle(cx)
+    }
+}
+
 // Holds the state for a specific window.
 pub struct Window {
     pub(crate) handle: AnyWindowHandle,

crates/picker2/src/picker2.rs 🔗

@@ -1,7 +1,7 @@
 use editor::Editor;
 use gpui::{
-    div, prelude::*, uniform_list, Component, Div, MouseButton, Render, Task,
-    UniformListScrollHandle, View, ViewContext, WindowContext,
+    div, prelude::*, uniform_list, AppContext, Component, Div, FocusHandle, FocusableView,
+    MouseButton, Render, Task, UniformListScrollHandle, View, ViewContext, WindowContext,
 };
 use std::{cmp, sync::Arc};
 use ui::{prelude::*, v_stack, Divider, Label, TextColor};
@@ -35,6 +35,12 @@ pub trait PickerDelegate: Sized + 'static {
     ) -> Self::ListItem;
 }
 
+impl<D: PickerDelegate> FocusableView for Picker<D> {
+    fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+        self.editor.focus_handle(cx)
+    }
+}
+
 impl<D: PickerDelegate> Picker<D> {
     pub fn new(delegate: D, cx: &mut ViewContext<Self>) -> Self {
         let editor = cx.build_view(|cx| {

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

@@ -4,8 +4,8 @@ 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, DispatchPhase, Div, EventEmitter,
-    FocusHandle, FocusableView, LayoutId, MouseButton, MouseDownEvent, Pixels, Point, Render, View,
+    overlay, px, Action, AnchorCorner, AnyElement, Bounds, Dismiss, DispatchPhase, Div,
+    FocusHandle, LayoutId, ManagedView, MouseButton, MouseDownEvent, Pixels, Point, Render, View,
 };
 
 pub struct ContextMenu {
@@ -13,17 +13,11 @@ pub struct ContextMenu {
     focus_handle: FocusHandle,
 }
 
-pub enum MenuEvent {
-    Dismissed,
-}
-
-impl EventEmitter<MenuEvent> for ContextMenu {}
-impl FocusableView for ContextMenu {
+impl ManagedView for ContextMenu {
     fn focus_handle(&self, cx: &gpui::AppContext) -> FocusHandle {
         self.focus_handle.clone()
     }
 }
-impl Menu for ContextMenu {}
 
 impl ContextMenu {
     pub fn new(cx: &mut WindowContext) -> Self {
@@ -50,11 +44,11 @@ impl ContextMenu {
 
     pub fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
         // todo!()
-        cx.emit(MenuEvent::Dismissed);
+        cx.emit(Dismiss);
     }
 
     pub fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
-        cx.emit(MenuEvent::Dismissed);
+        cx.emit(Dismiss);
     }
 }
 
@@ -82,9 +76,7 @@ impl Render for ContextMenu {
     }
 }
 
-pub trait Menu: Render + EventEmitter<MenuEvent> + FocusableView {}
-
-pub struct MenuHandle<V: 'static, M: Menu> {
+pub struct MenuHandle<V: 'static, M: ManagedView> {
     id: Option<ElementId>,
     child_builder: Option<Box<dyn FnOnce(bool) -> AnyElement<V> + 'static>>,
     menu_builder: Option<Rc<dyn Fn(&mut V, &mut ViewContext<V>) -> View<M> + 'static>>,
@@ -93,7 +85,7 @@ pub struct MenuHandle<V: 'static, M: Menu> {
     attach: Option<AnchorCorner>,
 }
 
-impl<V: 'static, M: Menu> MenuHandle<V, M> {
+impl<V: 'static, M: ManagedView> MenuHandle<V, M> {
     pub fn id(mut self, id: impl Into<ElementId>) -> Self {
         self.id = Some(id.into());
         self
@@ -123,7 +115,7 @@ impl<V: 'static, M: Menu> MenuHandle<V, M> {
     }
 }
 
-pub fn menu_handle<V: 'static, M: Menu>() -> MenuHandle<V, M> {
+pub fn menu_handle<V: 'static, M: ManagedView>() -> MenuHandle<V, M> {
     MenuHandle {
         id: None,
         child_builder: None,
@@ -140,7 +132,7 @@ pub struct MenuHandleState<V, M> {
     child_element: Option<AnyElement<V>>,
     menu_element: Option<AnyElement<V>>,
 }
-impl<V: 'static, M: Menu> Element<V> for MenuHandle<V, M> {
+impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
     type ElementState = MenuHandleState<V, M>;
 
     fn element_id(&self) -> Option<gpui::ElementId> {
@@ -234,7 +226,7 @@ impl<V: 'static, M: Menu> 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 {
-                    MenuEvent::Dismissed => {
+                    &Dismiss => {
                         *menu2.borrow_mut() = None;
                         cx.notify();
                     }
@@ -255,7 +247,7 @@ impl<V: 'static, M: Menu> Element<V> for MenuHandle<V, M> {
     }
 }
 
-impl<V: 'static, M: Menu> Component<V> for MenuHandle<V, M> {
+impl<V: 'static, M: ManagedView> Component<V> for MenuHandle<V, M> {
     fn render(self) -> AnyElement<V> {
         AnyElement::new(self)
     }

crates/workspace2/src/modal_layer.rs 🔗

@@ -1,6 +1,6 @@
 use gpui::{
-    div, prelude::*, px, AnyView, Div, EventEmitter, FocusHandle, Render, Subscription, View,
-    ViewContext, WindowContext,
+    div, prelude::*, px, AnyView, Div, FocusHandle, ManagedView, Render, Subscription, View,
+    ViewContext,
 };
 use ui::{h_stack, v_stack};
 
@@ -15,14 +15,6 @@ pub struct ModalLayer {
     active_modal: Option<ActiveModal>,
 }
 
-pub trait Modal: Render + EventEmitter<ModalEvent> {
-    fn focus(&self, cx: &mut WindowContext);
-}
-
-pub enum ModalEvent {
-    Dismissed,
-}
-
 impl ModalLayer {
     pub fn new() -> Self {
         Self { active_modal: None }
@@ -30,7 +22,7 @@ impl ModalLayer {
 
     pub fn toggle_modal<V, B>(&mut self, cx: &mut ViewContext<Self>, build_view: B)
     where
-        V: Modal,
+        V: ManagedView,
         B: FnOnce(&mut ViewContext<V>) -> V,
     {
         if let Some(active_modal) = &self.active_modal {
@@ -46,17 +38,15 @@ impl ModalLayer {
 
     pub fn show_modal<V>(&mut self, new_modal: View<V>, cx: &mut ViewContext<Self>)
     where
-        V: Modal,
+        V: ManagedView,
     {
         self.active_modal = Some(ActiveModal {
             modal: new_modal.clone().into(),
-            subscription: cx.subscribe(&new_modal, |this, modal, e, cx| match e {
-                ModalEvent::Dismissed => this.hide_modal(cx),
-            }),
+            subscription: cx.subscribe(&new_modal, |this, modal, e, cx| this.hide_modal(cx)),
             previous_focus_handle: cx.focused(),
             focus_handle: cx.focus_handle(),
         });
-        new_modal.update(cx, |modal, cx| modal.focus(cx));
+        cx.focus_view(&new_modal);
         cx.notify();
     }
 

crates/workspace2/src/workspace2.rs 🔗

@@ -31,9 +31,9 @@ use futures::{
 use gpui::{
     actions, div, point, size, Action, AnyModel, AnyView, AnyWeakView, AppContext, AsyncAppContext,
     AsyncWindowContext, Bounds, Context, Div, Entity, EntityId, EventEmitter, FocusHandle,
-    FocusableView, GlobalPixels, InteractiveComponent, KeyContext, Model, ModelContext,
-    ParentComponent, Point, Render, Size, Styled, Subscription, Task, View, ViewContext,
-    VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
+    FocusableView, GlobalPixels, InteractiveComponent, KeyContext, ManagedView, Model,
+    ModelContext, ParentComponent, Point, Render, Size, Styled, Subscription, Task, View,
+    ViewContext, VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
 };
 use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
 use itertools::Itertools;
@@ -3380,11 +3380,14 @@ impl Workspace {
         div
     }
 
-    pub fn active_modal<V: Modal + 'static>(&mut self, cx: &ViewContext<Self>) -> Option<View<V>> {
+    pub fn active_modal<V: ManagedView + 'static>(
+        &mut self,
+        cx: &ViewContext<Self>,
+    ) -> Option<View<V>> {
         self.modal_layer.read(cx).active_modal()
     }
 
-    pub fn toggle_modal<V: Modal, B>(&mut self, cx: &mut ViewContext<Self>, build: B)
+    pub fn toggle_modal<V: ManagedView, B>(&mut self, cx: &mut ViewContext<Self>, build: B)
     where
         B: FnOnce(&mut ViewContext<V>) -> V,
     {