1use gpui::{
2 div, px, AnyView, Div, EventEmitter, FocusHandle, ParentElement, Render, StatelessInteractive,
3 Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
4};
5use ui::{h_stack, 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 if let Some(active_modal) = &self.active_modal {
37 let is_close = active_modal.modal.clone().downcast::<V>().is_ok();
38 self.hide_modal(cx);
39 if is_close {
40 return;
41 }
42 }
43 let new_modal = cx.build_view(build_view);
44 self.show_modal(new_modal, cx);
45 }
46
47 pub fn show_modal<V>(&mut self, new_modal: View<V>, cx: &mut ViewContext<Self>)
48 where
49 V: Modal,
50 {
51 self.active_modal = Some(ActiveModal {
52 modal: new_modal.clone().into(),
53 subscription: cx.subscribe(&new_modal, |this, modal, e, cx| match e {
54 ModalEvent::Dismissed => this.hide_modal(cx),
55 }),
56 previous_focus_handle: cx.focused(),
57 focus_handle: cx.focus_handle(),
58 });
59 new_modal.update(cx, |modal, cx| modal.focus(cx));
60 cx.notify();
61 }
62
63 pub fn hide_modal(&mut self, cx: &mut ViewContext<Self>) {
64 if let Some(active_modal) = self.active_modal.take() {
65 if let Some(previous_focus) = active_modal.previous_focus_handle {
66 if active_modal.focus_handle.contains_focused(cx) {
67 previous_focus.focus(cx);
68 }
69 }
70 }
71
72 cx.notify();
73 }
74}
75
76impl Render for ModalLayer {
77 type Element = Div<Self>;
78
79 fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
80 let Some(active_modal) = &self.active_modal else {
81 return div();
82 };
83
84 div()
85 .absolute()
86 .size_full()
87 .top_0()
88 .left_0()
89 .z_index(400)
90 .child(
91 v_stack()
92 .h(px(0.0))
93 .top_20()
94 .flex()
95 .flex_col()
96 .items_center()
97 .track_focus(&active_modal.focus_handle)
98 .child(
99 h_stack()
100 // needed to prevent mouse events leaking to the
101 // UI below. // todo! for gpui3.
102 .on_any_mouse_down(|_, _, cx| cx.stop_propagation())
103 .on_any_mouse_up(|_, _, cx| cx.stop_propagation())
104 .on_mouse_down_out(|this: &mut Self, event, cx| {
105 this.hide_modal(cx);
106 })
107 .child(active_modal.modal.clone()),
108 ),
109 )
110 }
111}