modal_layer.rs

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