231 lines · 6.5 KB · Rust
RawDownload
use std::ops::Deref; use futures::channel::oneshot; use crate::{    div, opaque_grey, white, AnyView, App, AppContext as _, Context, Entity, EventEmitter,    FocusHandle, Focusable, InteractiveElement, IntoElement, ParentElement, PromptLevel, Render,    StatefulInteractiveElement, Styled,}; use super::Window; /// The event emitted when a prompt's option is selected./// The usize is the index of the selected option, from the actions/// passed to the prompt.pub struct PromptResponse(pub usize); /// A prompt that can be rendered in the window.pub trait Prompt: EventEmitter<PromptResponse> + Focusable {} impl<V: EventEmitter<PromptResponse> + Focusable> Prompt for V {} /// A handle to a prompt that can be used to interact with it.pub struct PromptHandle {    sender: oneshot::Sender<usize>,} impl PromptHandle {    pub(crate) fn new(sender: oneshot::Sender<usize>) -> Self {        Self { sender }    }     /// Construct a new prompt handle from a view of the appropriate types    pub fn with_view<V: Prompt + Render>(        self,        view: Entity<V>,        window: &mut Window,        cx: &mut App,    ) -> RenderablePromptHandle {        let mut sender = Some(self.sender);        let previous_focus = window.focused(cx);        let window_handle = window.window_handle();        cx.subscribe(&view, move |_: Entity<V>, e: &PromptResponse, cx| {            if let Some(sender) = sender.take() {                sender.send(e.0).ok();                window_handle                    .update(cx, |_, window, _cx| {                        window.prompt.take();                        if let Some(previous_focus) = &previous_focus {                            window.focus(previous_focus);                        }                    })                    .ok();            }        })        .detach();         window.focus(&view.focus_handle(cx));         RenderablePromptHandle {            view: Box::new(view),        }    }} /// A prompt handle capable of being rendered in a window.pub struct RenderablePromptHandle {    pub(crate) view: Box<dyn PromptViewHandle>,} /// Use this function in conjunction with [App::set_prompt_builder] to force/// GPUI to always use the fallback prompt renderer.pub fn fallback_prompt_renderer(    level: PromptLevel,    message: &str,    detail: Option<&str>,    actions: &[&str],    handle: PromptHandle,    window: &mut Window,    cx: &mut App,) -> RenderablePromptHandle {    let renderer = cx.new(|cx| FallbackPromptRenderer {        _level: level,        message: message.to_string(),        detail: detail.map(ToString::to_string),        actions: actions.iter().map(ToString::to_string).collect(),        focus: cx.focus_handle(),    });     handle.with_view(renderer, window, cx)} /// The default GPUI fallback for rendering prompts, when the platform doesn't support it.pub struct FallbackPromptRenderer {    _level: PromptLevel,    message: String,    detail: Option<String>,    actions: Vec<String>,    focus: FocusHandle,} impl Render for FallbackPromptRenderer {    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {        let prompt = div()            .cursor_default()            .track_focus(&self.focus)            .w_72()            .bg(white())            .rounded_lg()            .overflow_hidden()            .p_3()            .child(                div()                    .w_full()                    .flex()                    .flex_row()                    .justify_around()                    .child(div().overflow_hidden().child(self.message.clone())),            )            .children(self.detail.clone().map(|detail| {                div()                    .w_full()                    .flex()                    .flex_row()                    .justify_around()                    .text_sm()                    .mb_2()                    .child(div().child(detail))            }))            .children(self.actions.iter().enumerate().map(|(ix, action)| {                div()                    .flex()                    .flex_row()                    .justify_around()                    .border_1()                    .border_color(opaque_grey(0.2, 0.5))                    .mt_1()                    .rounded_sm()                    .cursor_pointer()                    .text_sm()                    .child(action.clone())                    .id(ix)                    .on_click(cx.listener(move |_, _, _, cx| {                        cx.emit(PromptResponse(ix));                    }))            }));         div()            .size_full()            .child(                div()                    .size_full()                    .bg(opaque_grey(0.5, 0.6))                    .absolute()                    .top_0()                    .left_0(),            )            .child(                div()                    .size_full()                    .absolute()                    .top_0()                    .left_0()                    .flex()                    .flex_col()                    .justify_around()                    .child(                        div()                            .w_full()                            .flex()                            .flex_row()                            .justify_around()                            .child(prompt),                    ),            )    }} impl EventEmitter<PromptResponse> for FallbackPromptRenderer {} impl Focusable for FallbackPromptRenderer {    fn focus_handle(&self, _: &crate::App) -> FocusHandle {        self.focus.clone()    }} pub(crate) trait PromptViewHandle {    fn any_view(&self) -> AnyView;} impl<V: Prompt + Render> PromptViewHandle for Entity<V> {    fn any_view(&self) -> AnyView {        self.clone().into()    }} pub(crate) enum PromptBuilder {    Default,    Custom(        Box<            dyn Fn(                PromptLevel,                &str,                Option<&str>,                &[&str],                PromptHandle,                &mut Window,                &mut App,            ) -> RenderablePromptHandle,        >,    ),} impl Deref for PromptBuilder {    type Target = dyn Fn(        PromptLevel,        &str,        Option<&str>,        &[&str],        PromptHandle,        &mut Window,        &mut App,    ) -> RenderablePromptHandle;     fn deref(&self) -> &Self::Target {        match self {            Self::Default => &fallback_prompt_renderer,            Self::Custom(f) => f.as_ref(),        }    }}