Move from register_modals to register_workspace_action

Conrad Irwin created

Change summary

crates/command_palette2/src/command_palette.rs | 23 ++---
crates/go_to_line2/src/go_to_line.rs           | 30 ++++----
crates/workspace2/src/modal_layer.rs           | 70 ++++---------------
crates/workspace2/src/workspace2.rs            | 58 ++++++++++++---
4 files changed, 84 insertions(+), 97 deletions(-)

Detailed changes

crates/command_palette2/src/command_palette.rs 🔗

@@ -21,19 +21,7 @@ actions!(Toggle);
 
 pub fn init(cx: &mut AppContext) {
     cx.set_global(HitCounts::default());
-
-    cx.observe_new_views(
-        |workspace: &mut Workspace, _: &mut ViewContext<Workspace>| {
-            workspace.modal_layer().register_modal(Toggle, |cx| {
-                let Some(previous_focus_handle) = cx.focused() else {
-                    return None;
-                };
-
-                Some(cx.build_view(|cx| CommandPalette::new(previous_focus_handle, cx)))
-            });
-        },
-    )
-    .detach();
+    cx.observe_new_views(CommandPalette::register).detach();
 }
 
 pub struct CommandPalette {
@@ -41,6 +29,15 @@ pub struct CommandPalette {
 }
 
 impl CommandPalette {
+    fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+        workspace.register_action(|workspace, _: &Toggle, cx| {
+            let Some(previous_focus_handle) = cx.focused() else {
+                return;
+            };
+            workspace.toggle_modal(cx, move |cx| CommandPalette::new(previous_focus_handle, cx));
+        });
+    }
+
     fn new(previous_focus_handle: FocusHandle, cx: &mut ViewContext<Self>) -> Self {
         let filter = cx.try_global::<CommandPaletteFilter>();
 

crates/go_to_line2/src/go_to_line.rs 🔗

@@ -13,22 +13,7 @@ use workspace::{Modal, ModalEvent, Workspace};
 actions!(Toggle);
 
 pub fn init(cx: &mut AppContext) {
-    cx.observe_new_views(
-        |workspace: &mut Workspace, cx: &mut ViewContext<Workspace>| {
-            let handle = cx.view().downgrade();
-
-            workspace.modal_layer().register_modal(Toggle, move |cx| {
-                let workspace = handle.upgrade()?;
-                let editor = workspace
-                    .read(cx)
-                    .active_item(cx)
-                    .and_then(|active_item| active_item.downcast::<Editor>())?;
-
-                Some(cx.build_view(|cx| GoToLine::new(editor, cx)))
-            });
-        },
-    )
-    .detach();
+    cx.observe_new_views(GoToLine::register).detach();
 }
 
 pub struct GoToLine {
@@ -47,6 +32,19 @@ impl Modal for GoToLine {
 }
 
 impl GoToLine {
+    fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
+        workspace.register_action(|workspace, _: &Toggle, cx| {
+            let Some(editor) = workspace
+                .active_item(cx)
+                .and_then(|active_item| active_item.downcast::<Editor>())
+            else {
+                return;
+            };
+
+            workspace.toggle_modal(cx, move |cx| GoToLine::new(editor, cx));
+        });
+    }
+
     pub fn new(active_editor: View<Editor>, cx: &mut ViewContext<Self>) -> Self {
         let line_editor = cx.build_view(|cx| Editor::single_line(cx));
         let line_editor_change = cx.subscribe(&line_editor, Self::on_line_editor_event);

crates/workspace2/src/modal_layer.rs 🔗

@@ -2,7 +2,7 @@ use crate::Workspace;
 use gpui::{
     div, px, AnyView, Component, Div, EventEmitter, FocusHandle, ParentElement, Render,
     StatefulInteractivity, StatelessInteractive, Styled, Subscription, View, ViewContext,
-    WindowContext,
+    VisualContext, WindowContext,
 };
 use std::{any::TypeId, sync::Arc};
 use ui::v_stack;
@@ -16,14 +16,6 @@ pub struct ActiveModal {
 
 pub struct ModalLayer {
     active_modal: Option<ActiveModal>,
-    registered_modals: Vec<(
-        TypeId,
-        Box<
-            dyn Fn(
-                Div<Workspace, StatefulInteractivity<Workspace>>,
-            ) -> Div<Workspace, StatefulInteractivity<Workspace>>,
-        >,
-    )>,
 }
 
 pub trait Modal: Render + EventEmitter<ModalEvent> {
@@ -36,35 +28,13 @@ pub enum ModalEvent {
 
 impl ModalLayer {
     pub fn new() -> Self {
-        Self {
-            active_modal: None,
-            registered_modals: Vec::new(),
-        }
+        Self { active_modal: None }
     }
 
-    pub fn register_modal<A: 'static, V, B>(&mut self, action: A, build_view: B)
+    pub fn toggle_modal<V, B>(&mut self, cx: &mut ViewContext<Workspace>, build_view: B)
     where
         V: Modal,
-        B: Fn(&mut WindowContext) -> Option<View<V>> + 'static,
-    {
-        let build_view = Arc::new(build_view);
-
-        self.registered_modals.push((
-            TypeId::of::<A>(),
-            Box::new(move |mut div| {
-                let build_view = build_view.clone();
-
-                div.on_action(move |workspace, event: &A, cx| {
-                    workspace.modal_layer().toggle_modal(build_view.clone(), cx)
-                })
-            }),
-        ));
-    }
-
-    pub fn toggle_modal<V, B>(&mut self, build_view: Arc<B>, cx: &mut ViewContext<Workspace>)
-    where
-        V: Modal,
-        B: Fn(&mut WindowContext) -> Option<View<V>> + 'static,
+        B: FnOnce(&mut ViewContext<V>) -> V,
     {
         let previous_focus = cx.focused();
 
@@ -74,28 +44,23 @@ impl ModalLayer {
                 return;
             }
         }
-        let Some(new_modal) = (build_view)(cx) else {
-            return;
-        };
-        self.show_modal(previous_focus, new_modal, cx);
+        let new_modal = cx.build_view(build_view);
+        self.show_modal(new_modal, cx);
     }
 
-    pub fn show_modal<V>(
-        &mut self,
-        previous_focus: Option<FocusHandle>,
-        new_modal: View<V>,
-        cx: &mut ViewContext<Workspace>,
-    ) where
+    pub fn show_modal<V>(&mut self, new_modal: View<V>, cx: &mut ViewContext<Workspace>)
+    where
         V: Modal,
     {
         self.active_modal = Some(ActiveModal {
             modal: new_modal.clone().into(),
-            subscription: cx.subscribe(&new_modal, |this, modal, e, cx| match e {
-                ModalEvent::Dismissed => this.modal_layer().hide_modal(cx),
+            subscription: cx.subscribe(&new_modal, |workspace, modal, e, cx| match e {
+                ModalEvent::Dismissed => workspace.modal_layer.hide_modal(cx),
             }),
-            previous_focus_handle: previous_focus,
+            previous_focus_handle: cx.focused(),
             focus_handle: cx.focus_handle(),
         });
+        new_modal.update(cx, |modal, cx| modal.focus(cx));
         cx.notify();
     }
 
@@ -115,12 +80,7 @@ impl ModalLayer {
         &self,
         cx: &ViewContext<Workspace>,
     ) -> Div<Workspace, StatefulInteractivity<Workspace>> {
-        let mut parent = div().id("modal layer").relative().size_full();
-
-        for (_, action) in self.registered_modals.iter() {
-            parent = (action)(parent);
-        }
-
+        let parent = div().id("boop");
         parent.when_some(self.active_modal.as_ref(), |parent, open_modal| {
             let container1 = div()
                 .absolute()
@@ -137,8 +97,8 @@ impl ModalLayer {
                 .relative()
                 .top_20()
                 .track_focus(&open_modal.focus_handle)
-                .on_mouse_down_out(|workspace: &mut Workspace, _, cx| {
-                    workspace.modal_layer().hide_modal(cx);
+                .on_mouse_down_out(|workspace: &mut Workspace, event, cx| {
+                    workspace.modal_layer.hide_modal(cx);
                 });
 
             parent.child(container1.child(container2.child(open_modal.modal.clone())))

crates/workspace2/src/workspace2.rs 🔗

@@ -36,11 +36,12 @@ use futures::{
     Future, FutureExt, StreamExt,
 };
 use gpui::{
-    actions, div, point, rems, size, AnyModel, AnyView, AnyWeakView, AppContext, AsyncAppContext,
-    AsyncWindowContext, Bounds, Component, DispatchContext, Div, Entity, EntityId, EventEmitter,
-    FocusHandle, GlobalPixels, Model, ModelContext, ParentElement, Point, Render, Size,
-    StatefulInteractive, StatefulInteractivity, Styled, Subscription, Task, View, ViewContext,
-    VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
+    actions, div, point, rems, size, Action, AnyModel, AnyView, AnyWeakView, AppContext,
+    AsyncAppContext, AsyncWindowContext, Bounds, Component, DispatchContext, Div, Entity, EntityId,
+    EventEmitter, FocusHandle, GlobalPixels, Model, ModelContext, ParentElement, Point, Render,
+    Size, StatefulInteractive, StatefulInteractivity, StatelessInteractive, Styled, Subscription,
+    Task, View, ViewContext, VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle,
+    WindowOptions,
 };
 use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
 use itertools::Itertools;
@@ -530,6 +531,13 @@ pub enum Event {
 pub struct Workspace {
     weak_self: WeakView<Self>,
     focus_handle: FocusHandle,
+    workspace_actions: Vec<
+        Box<
+            dyn Fn(
+                Div<Workspace, StatefulInteractivity<Workspace>>,
+            ) -> Div<Workspace, StatefulInteractivity<Workspace>>,
+        >,
+    >,
     zoomed: Option<AnyWeakView>,
     zoomed_position: Option<DockPosition>,
     center: PaneGroup,
@@ -775,13 +783,10 @@ impl Workspace {
             leader_updates_tx,
             subscriptions,
             pane_history_timestamp,
+            workspace_actions: Default::default(),
         }
     }
 
-    pub fn modal_layer(&mut self) -> &mut ModalLayer {
-        &mut self.modal_layer
-    }
-
     fn new_local(
         abs_paths: Vec<PathBuf>,
         app_state: Arc<AppState>,
@@ -3495,6 +3500,34 @@ impl Workspace {
     //         )
     //     }
     // }
+    pub fn register_action<A: Action>(
+        &mut self,
+        callback: impl Fn(&mut Self, &A, &mut ViewContext<Self>) + 'static,
+    ) {
+        let callback = Arc::new(callback);
+
+        self.workspace_actions.push(Box::new(move |div| {
+            let callback = callback.clone();
+            div.on_action(move |workspace, event, cx| (callback.clone())(workspace, event, cx))
+        }));
+    }
+
+    fn add_workspace_actions_listeners(
+        &self,
+        mut div: Div<Workspace, StatefulInteractivity<Workspace>>,
+    ) -> Div<Workspace, StatefulInteractivity<Workspace>> {
+        for action in self.workspace_actions.iter() {
+            div = (action)(div)
+        }
+        div
+    }
+
+    pub fn toggle_modal<V: Modal, B>(&mut self, cx: &mut ViewContext<Self>, build: B)
+    where
+        B: FnOnce(&mut ViewContext<V>) -> V,
+    {
+        self.modal_layer.toggle_modal(cx, build)
+    }
 }
 
 fn window_bounds_env_override(cx: &AsyncAppContext) -> Option<WindowBounds> {
@@ -3706,14 +3739,13 @@ fn notify_if_database_failed(workspace: WindowHandle<Workspace>, cx: &mut AsyncA
 impl EventEmitter<Event> for Workspace {}
 
 impl Render for Workspace {
-    type Element = Div<Self, StatefulInteractivity<Self>>;
+    type Element = Div<Self>;
 
     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
         let mut context = DispatchContext::default();
         context.insert("Workspace");
         cx.with_key_dispatch_context(context, |cx| {
             div()
-                .id("workspace")
                 .relative()
                 .size_full()
                 .flex()
@@ -3727,8 +3759,7 @@ impl Render for Workspace {
                 .child(self.render_titlebar(cx))
                 .child(
                     // todo! should this be a component a view?
-                    self.modal_layer
-                        .wrapper_element(cx)
+                    self.add_workspace_actions_listeners(div().id("workspace"))
                         .relative()
                         .flex_1()
                         .w_full()
@@ -3737,6 +3768,7 @@ impl Render for Workspace {
                         .border_t()
                         .border_b()
                         .border_color(cx.theme().colors().border)
+                        .child(self.modal_layer.wrapper_element(cx))
                         // .children(
                         //     Some(
                         //         Panel::new("project-panel-outer", cx)