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        dbg!("focusing");
 50        cx.focus_view(&new_modal);
 51        cx.notify();
 52    }
 53
 54    pub fn hide_modal(&mut self, cx: &mut ViewContext<Self>) {
 55        if let Some(active_modal) = self.active_modal.take() {
 56            if let Some(previous_focus) = active_modal.previous_focus_handle {
 57                if active_modal.focus_handle.contains_focused(cx) {
 58                    previous_focus.focus(cx);
 59                }
 60            }
 61        }
 62
 63        cx.notify();
 64    }
 65
 66    pub fn active_modal<V>(&self) -> Option<View<V>>
 67    where
 68        V: 'static,
 69    {
 70        let active_modal = self.active_modal.as_ref()?;
 71        active_modal.modal.clone().downcast::<V>().ok()
 72    }
 73}
 74
 75impl Render for ModalLayer {
 76    type Element = Div;
 77
 78    fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
 79        let Some(active_modal) = &self.active_modal else {
 80            return div();
 81        };
 82
 83        div()
 84            .absolute()
 85            .size_full()
 86            .top_0()
 87            .left_0()
 88            .z_index(400)
 89            .child(
 90                v_stack()
 91                    .h(px(0.0))
 92                    .top_20()
 93                    .flex()
 94                    .flex_col()
 95                    .items_center()
 96                    .track_focus(&active_modal.focus_handle)
 97                    .child(
 98                        h_stack()
 99                            // needed to prevent mouse events leaking to the
100                            // UI below. // todo! for gpui3.
101                            .on_any_mouse_down(|_, cx| cx.stop_propagation())
102                            .on_any_mouse_up(|_, cx| cx.stop_propagation())
103                            .on_mouse_down_out(cx.listener(|this, _, cx| {
104                                this.hide_modal(cx);
105                            }))
106                            .child(active_modal.modal.clone()),
107                    ),
108            )
109    }
110}