1use gpui::{
2 div, px, AnyView, Div, EventEmitter, FocusHandle, ParentElement, Render, StatelessInteractive,
3 Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
4};
5use ui::v_stack;
6
7pub struct ActiveModal {
8 modal: AnyView,
9 subscription: Subscription,
10 previous_focus_handle: Option<FocusHandle>,
11 focus_handle: FocusHandle,
12}
13
14pub struct ModalLayer {
15 active_modal: Option<ActiveModal>,
16}
17
18pub trait Modal: Render + EventEmitter<ModalEvent> {
19 fn focus(&self, cx: &mut WindowContext);
20}
21
22pub enum ModalEvent {
23 Dismissed,
24}
25
26impl ModalLayer {
27 pub fn new() -> Self {
28 Self { active_modal: None }
29 }
30
31 pub fn toggle_modal<V, B>(&mut self, cx: &mut ViewContext<Self>, build_view: B)
32 where
33 V: Modal,
34 B: FnOnce(&mut ViewContext<V>) -> V,
35 {
36 let previous_focus = cx.focused();
37
38 if let Some(active_modal) = &self.active_modal {
39 let is_close = active_modal.modal.clone().downcast::<V>().is_ok();
40 self.hide_modal(cx);
41 if is_close {
42 return;
43 }
44 }
45 let new_modal = cx.build_view(build_view);
46 self.show_modal(new_modal, cx);
47 }
48
49 pub fn show_modal<V>(&mut self, new_modal: View<V>, cx: &mut ViewContext<Self>)
50 where
51 V: Modal,
52 {
53 self.active_modal = Some(ActiveModal {
54 modal: new_modal.clone().into(),
55 subscription: cx.subscribe(&new_modal, |this, modal, e, cx| match e {
56 ModalEvent::Dismissed => this.hide_modal(cx),
57 }),
58 previous_focus_handle: cx.focused(),
59 focus_handle: cx.focus_handle(),
60 });
61 new_modal.update(cx, |modal, cx| modal.focus(cx));
62 cx.notify();
63 }
64
65 pub fn hide_modal(&mut self, cx: &mut ViewContext<Self>) {
66 if let Some(active_modal) = self.active_modal.take() {
67 if let Some(previous_focus) = active_modal.previous_focus_handle {
68 if active_modal.focus_handle.contains_focused(cx) {
69 previous_focus.focus(cx);
70 }
71 }
72 }
73
74 cx.notify();
75 }
76}
77
78impl Render for ModalLayer {
79 type Element = Div<Self>;
80
81 fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
82 let Some(active_modal) = &self.active_modal else {
83 return div();
84 };
85
86 div()
87 .absolute()
88 .flex()
89 .flex_col()
90 .items_center()
91 .size_full()
92 .top_0()
93 .left_0()
94 .z_index(400)
95 .child(
96 v_stack()
97 .h(px(0.0))
98 .top_20()
99 .track_focus(&active_modal.focus_handle)
100 .on_mouse_down_out(|this: &mut Self, event, cx| {
101 this.hide_modal(cx);
102 })
103 .child(active_modal.modal.clone()),
104 )
105 }
106}