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