modal_layer.rs

 1use crate::Workspace;
 2use gpui::{
 3    div, px, AnyView, Component, Div, EventEmitter, ParentElement, Render, StatelessInteractive,
 4    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<(TypeId, Box<dyn Fn(Div<Workspace>) -> Div<Workspace>>)>,
13}
14
15pub enum ModalEvent {
16    Dismissed,
17}
18
19impl ModalLayer {
20    pub fn new() -> Self {
21        Self {
22            open_modal: None,
23            subscription: None,
24            registered_modals: Vec::new(),
25        }
26    }
27
28    pub fn register_modal<A: 'static, V, B>(&mut self, action: A, build_view: B)
29    where
30        V: EventEmitter<ModalEvent> + Render,
31        B: Fn(&mut Workspace, &mut ViewContext<Workspace>) -> Option<View<V>> + 'static,
32    {
33        let build_view = Arc::new(build_view);
34
35        self.registered_modals.push((
36            TypeId::of::<A>(),
37            Box::new(move |mut div| {
38                let build_view = build_view.clone();
39
40                div.on_action(move |workspace, event: &A, cx| {
41                    let Some(new_modal) = (build_view)(workspace, cx) else {
42                        return;
43                    };
44                    workspace.modal_layer().show_modal(new_modal, cx);
45                })
46            }),
47        ));
48    }
49
50    pub fn show_modal<V>(&mut self, new_modal: View<V>, cx: &mut ViewContext<Workspace>)
51    where
52        V: EventEmitter<ModalEvent> + Render,
53    {
54        self.subscription = Some(cx.subscribe(&new_modal, |this, modal, e, cx| match e {
55            ModalEvent::Dismissed => this.modal_layer().hide_modal(cx),
56        }));
57        self.open_modal = Some(new_modal.into());
58        cx.notify();
59    }
60
61    pub fn hide_modal(&mut self, cx: &mut ViewContext<Workspace>) {
62        self.open_modal.take();
63        self.subscription.take();
64        cx.notify();
65    }
66
67    pub fn wrapper_element(&self, cx: &ViewContext<Workspace>) -> Div<Workspace> {
68        let mut parent = div().relative().size_full();
69
70        for (_, action) in self.registered_modals.iter() {
71            parent = (action)(parent);
72        }
73
74        parent.when_some(self.open_modal.as_ref(), |parent, open_modal| {
75            let container1 = div()
76                .absolute()
77                .flex()
78                .flex_col()
79                .items_center()
80                .size_full()
81                .top_0()
82                .left_0()
83                .z_index(400);
84
85            // transparent layer
86            let container2 = v_stack().h(px(0.0)).relative().top_20();
87
88            parent.child(container1.child(container2.child(open_modal.clone())))
89        })
90    }
91}