1use gpui::{
2 div, prelude::*, px, AnyView, Div, EventEmitter, FocusHandle, Render, Subscription, View,
3 ViewContext, 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 pub fn active_modal<V>(&self) -> Option<View<V>>
76 where
77 V: 'static,
78 {
79 let active_modal = self.active_modal.as_ref()?;
80 active_modal.modal.clone().downcast::<V>().ok()
81 }
82}
83
84impl Render for ModalLayer {
85 type Element = Div<Self>;
86
87 fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
88 let Some(active_modal) = &self.active_modal else {
89 return div();
90 };
91
92 div()
93 .absolute()
94 .size_full()
95 .top_0()
96 .left_0()
97 .z_index(400)
98 .child(
99 v_stack()
100 .h(px(0.0))
101 .top_20()
102 .flex()
103 .flex_col()
104 .items_center()
105 .track_focus(&active_modal.focus_handle)
106 .child(
107 h_stack()
108 // needed to prevent mouse events leaking to the
109 // UI below. // todo! for gpui3.
110 .on_any_mouse_down(|_, _, cx| cx.stop_propagation())
111 .on_any_mouse_up(|_, _, cx| cx.stop_propagation())
112 .on_mouse_down_out(|this: &mut Self, event, cx| {
113 this.hide_modal(cx);
114 })
115 .child(active_modal.modal.clone()),
116 ),
117 )
118 }
119}