modal_layer.rs

  1use crate::Workspace;
  2use gpui::{
  3    div, px, AnyView, Component, Div, EventEmitter, FocusHandle, ParentElement, Render,
  4    StatefulInteractivity, StatelessInteractive, Styled, Subscription, View, ViewContext,
  5    WindowContext,
  6};
  7use std::{any::TypeId, sync::Arc};
  8use ui::v_stack;
  9
 10pub struct ActiveModal {
 11    modal: AnyView,
 12    subscription: Subscription,
 13    previous_focus_handle: Option<FocusHandle>,
 14    focus_handle: FocusHandle,
 15}
 16
 17pub struct ModalLayer {
 18    active_modal: Option<ActiveModal>,
 19    registered_modals: Vec<(
 20        TypeId,
 21        Box<
 22            dyn Fn(
 23                Div<Workspace, StatefulInteractivity<Workspace>>,
 24            ) -> Div<Workspace, StatefulInteractivity<Workspace>>,
 25        >,
 26    )>,
 27}
 28
 29pub trait Modal: Render + EventEmitter<ModalEvent> {
 30    fn focus(&self, cx: &mut WindowContext);
 31}
 32
 33pub enum ModalEvent {
 34    Dismissed,
 35}
 36
 37impl ModalLayer {
 38    pub fn new() -> Self {
 39        Self {
 40            active_modal: None,
 41            registered_modals: Vec::new(),
 42        }
 43    }
 44
 45    pub fn register_modal<A: 'static, V, B>(&mut self, action: A, build_view: B)
 46    where
 47        V: Modal,
 48        B: Fn(&mut WindowContext) -> Option<View<V>> + 'static,
 49    {
 50        let build_view = Arc::new(build_view);
 51
 52        self.registered_modals.push((
 53            TypeId::of::<A>(),
 54            Box::new(move |mut div| {
 55                let build_view = build_view.clone();
 56
 57                div.on_action(move |workspace, event: &A, cx| {
 58                    let previous_focus = cx.focused();
 59                    if let Some(active_modal) = &workspace.modal_layer().active_modal {
 60                        if active_modal.modal.clone().downcast::<V>().is_ok() {
 61                            workspace.modal_layer().hide_modal(cx);
 62                            return;
 63                        }
 64                    }
 65                    let Some(new_modal) = (build_view)(cx) else {
 66                        return;
 67                    };
 68                    workspace
 69                        .modal_layer()
 70                        .show_modal(previous_focus, new_modal, cx);
 71                })
 72            }),
 73        ));
 74    }
 75
 76    pub fn show_modal<V>(
 77        &mut self,
 78        previous_focus: Option<FocusHandle>,
 79        new_modal: View<V>,
 80        cx: &mut ViewContext<Workspace>,
 81    ) where
 82        V: EventEmitter<ModalEvent> + Render,
 83    {
 84        self.active_modal = Some(ActiveModal {
 85            modal: new_modal.clone().into(),
 86            subscription: cx.subscribe(&new_modal, |this, modal, e, cx| match e {
 87                ModalEvent::Dismissed => this.modal_layer().hide_modal(cx),
 88            }),
 89            previous_focus_handle: previous_focus,
 90            focus_handle: cx.focus_handle(),
 91        });
 92        cx.notify();
 93    }
 94
 95    pub fn hide_modal(&mut self, cx: &mut ViewContext<Workspace>) {
 96        dbg!("hiding...");
 97        if let Some(active_modal) = self.active_modal.take() {
 98            dbg!("something");
 99            if let Some(previous_focus) = active_modal.previous_focus_handle {
100                dbg!("oohthing");
101                if active_modal.focus_handle.contains_focused(cx) {
102                    dbg!("aahthing");
103                    previous_focus.focus(cx);
104                }
105            }
106        }
107
108        cx.notify();
109    }
110
111    pub fn wrapper_element(
112        &self,
113        cx: &ViewContext<Workspace>,
114    ) -> Div<Workspace, StatefulInteractivity<Workspace>> {
115        let mut parent = div().id("modal layer").relative().size_full();
116
117        for (_, action) in self.registered_modals.iter() {
118            parent = (action)(parent);
119        }
120
121        parent.when_some(self.active_modal.as_ref(), |parent, open_modal| {
122            let container1 = div()
123                .absolute()
124                .flex()
125                .flex_col()
126                .items_center()
127                .size_full()
128                .top_0()
129                .left_0()
130                .z_index(400);
131
132            let container2 = v_stack()
133                .h(px(0.0))
134                .relative()
135                .top_20()
136                .track_focus(&open_modal.focus_handle);
137
138            parent.child(container1.child(container2.child(open_modal.modal.clone())))
139        })
140    }
141}
142
143// impl Render for ModalLayer {
144//     type Element = Div<Self>;
145
146//     fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
147//         let mut div = div();
148//         for (type_id, build_view) in cx.global::<ModalRegistry>().registered_modals {
149//             div = div.useful_on_action(
150//                 type_id,
151//                 Box::new(|this, _: dyn Any, phase, cx: &mut ViewContext<Self>| {
152//                     if phase == DispatchPhase::Capture {
153//                         return;
154//                     }
155//                     self.workspace.update(cx, |workspace, cx| {
156//                         self.open_modal = Some(build_view(workspace, cx));
157//                     });
158//                     cx.notify();
159//                 }),
160//             )
161//         }
162
163//         div
164//     }
165// }