modal_layer.rs

  1use gpui::{
  2    div, prelude::*, px, AnyView, Div, FocusHandle, ManagedView, Render, Subscription, View,
  3    ViewContext,
  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
 18impl ModalLayer {
 19    pub fn new() -> Self {
 20        Self { active_modal: None }
 21    }
 22
 23    pub fn toggle_modal<V, B>(&mut self, cx: &mut ViewContext<Self>, build_view: B)
 24    where
 25        V: ManagedView,
 26        B: FnOnce(&mut ViewContext<V>) -> V,
 27    {
 28        if let Some(active_modal) = &self.active_modal {
 29            let is_close = active_modal.modal.clone().downcast::<V>().is_ok();
 30            self.hide_modal(cx);
 31            if is_close {
 32                return;
 33            }
 34        }
 35        let new_modal = cx.build_view(build_view);
 36        self.show_modal(new_modal, cx);
 37    }
 38
 39    pub fn show_modal<V>(&mut self, new_modal: View<V>, cx: &mut ViewContext<Self>)
 40    where
 41        V: ManagedView,
 42    {
 43        self.active_modal = Some(ActiveModal {
 44            modal: new_modal.clone().into(),
 45            subscription: cx.subscribe(&new_modal, |this, modal, e, cx| this.hide_modal(cx)),
 46            previous_focus_handle: cx.focused(),
 47            focus_handle: cx.focus_handle(),
 48        });
 49        cx.focus_view(&new_modal);
 50        cx.notify();
 51    }
 52
 53    pub fn hide_modal(&mut self, cx: &mut ViewContext<Self>) {
 54        if let Some(active_modal) = self.active_modal.take() {
 55            if let Some(previous_focus) = active_modal.previous_focus_handle {
 56                if active_modal.focus_handle.contains_focused(cx) {
 57                    previous_focus.focus(cx);
 58                }
 59            }
 60        }
 61
 62        cx.notify();
 63    }
 64
 65    pub fn active_modal<V>(&self) -> Option<View<V>>
 66    where
 67        V: 'static,
 68    {
 69        let active_modal = self.active_modal.as_ref()?;
 70        active_modal.modal.clone().downcast::<V>().ok()
 71    }
 72}
 73
 74impl Render for ModalLayer {
 75    type Element = Div;
 76
 77    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
 78        let Some(active_modal) = &self.active_modal else {
 79            return div();
 80        };
 81
 82        div()
 83            .absolute()
 84            .size_full()
 85            .top_0()
 86            .left_0()
 87            .z_index(400)
 88            .child(
 89                v_stack()
 90                    .h(px(0.0))
 91                    .top_20()
 92                    .flex()
 93                    .flex_col()
 94                    .items_center()
 95                    .track_focus(&active_modal.focus_handle)
 96                    .child(
 97                        h_stack()
 98                            .on_mouse_down_out(cx.listener(|this, _, cx| {
 99                                this.hide_modal(cx);
100                            }))
101                            .child(active_modal.modal.clone()),
102                    ),
103            )
104    }
105}