177 lines · 5.6 KB · Rust
RawDownload
use gpui::{div, Action, Context, Div, Entity, InteractiveElement, Window};use workspace::Workspace; use crate::BufferSearchBar; /// Registrar inverts the dependency between search and its downstream user, allowing said downstream user to register search action without knowing exactly what those actions are.pub trait SearchActionsRegistrar {    fn register_handler<A: Action>(&mut self, callback: impl ActionExecutor<A>);} type SearchBarActionCallback<A> =    fn(&mut BufferSearchBar, &A, &mut Window, &mut Context<BufferSearchBar>); type GetSearchBar<T> =    for<'a, 'b> fn(&'a T, &'a mut Window, &mut Context<'b, T>) -> Option<Entity<BufferSearchBar>>; /// Registers search actions on a div that can be taken out.pub struct DivRegistrar<'a, 'b, T: 'static> {    div: Option<Div>,    cx: &'a mut Context<'b, T>,    search_getter: GetSearchBar<T>,} impl<'a, 'b, T: 'static> DivRegistrar<'a, 'b, T> {    pub fn new(search_getter: GetSearchBar<T>, cx: &'a mut Context<'b, T>) -> Self {        Self {            div: Some(div()),            cx,            search_getter,        }    }    pub fn into_div(self) -> Div {        // This option is always Some; it's an option in the first place because we want to call methods        // on div that require ownership.        self.div.unwrap()    }} impl<T: 'static> SearchActionsRegistrar for DivRegistrar<'_, '_, T> {    fn register_handler<A: Action>(&mut self, callback: impl ActionExecutor<A>) {        let getter = self.search_getter;        self.div = self.div.take().map(|div| {            div.on_action(self.cx.listener(move |this, action, window, cx| {                let should_notify = (getter)(this, window, cx)                    .clone()                    .map(|search_bar| {                        search_bar.update(cx, |search_bar, cx| {                            callback.execute(search_bar, action, window, cx)                        })                    })                    .unwrap_or(false);                if should_notify {                    cx.notify();                } else {                    cx.propagate();                }            }))        });    }} /// Register actions for an active pane.impl SearchActionsRegistrar for Workspace {    fn register_handler<A: Action>(&mut self, callback: impl ActionExecutor<A>) {        self.register_action(move |workspace, action: &A, window, cx| {            if workspace.has_active_modal(window, cx) {                cx.propagate();                return;            }             let pane = workspace.active_pane();            let callback = callback.clone();            pane.update(cx, |this, cx| {                this.toolbar().update(cx, move |this, cx| {                    if let Some(search_bar) = this.item_of_type::<BufferSearchBar>() {                        let should_notify = search_bar.update(cx, move |search_bar, cx| {                            callback.execute(search_bar, action, window, cx)                        });                        if should_notify {                            cx.notify();                        } else {                            cx.propagate();                        }                    }                })            });        });    }} type DidHandleAction = bool;/// Potentially executes the underlying action if some preconditions are met (e.g. buffer search bar is visible)pub trait ActionExecutor<A: Action>: 'static + Clone {    fn execute(        &self,        search_bar: &mut BufferSearchBar,        action: &A,        window: &mut Window,        cx: &mut Context<BufferSearchBar>,    ) -> DidHandleAction;} /// Run an action when the search bar has been dismissed from the panel.pub struct ForDismissed<A>(pub(super) SearchBarActionCallback<A>);impl<A> Clone for ForDismissed<A> {    fn clone(&self) -> Self {        Self(self.0)    }} impl<A: Action> ActionExecutor<A> for ForDismissed<A> {    fn execute(        &self,        search_bar: &mut BufferSearchBar,        action: &A,        window: &mut Window,        cx: &mut Context<BufferSearchBar>,    ) -> DidHandleAction {        if search_bar.is_dismissed() {            self.0(search_bar, action, window, cx);            true        } else {            false        }    }} /// Run an action when the search bar is deployed.pub struct ForDeployed<A>(pub(super) SearchBarActionCallback<A>);impl<A> Clone for ForDeployed<A> {    fn clone(&self) -> Self {        Self(self.0)    }} impl<A: Action> ActionExecutor<A> for ForDeployed<A> {    fn execute(        &self,        search_bar: &mut BufferSearchBar,        action: &A,        window: &mut Window,        cx: &mut Context<BufferSearchBar>,    ) -> DidHandleAction {        if search_bar.is_dismissed() || search_bar.active_searchable_item.is_none() {            false        } else {            self.0(search_bar, action, window, cx);            true        }    }} /// Run an action when the search bar has any matches, regardless of whether it/// is visible or not.pub struct WithResults<A>(pub(super) SearchBarActionCallback<A>);impl<A> Clone for WithResults<A> {    fn clone(&self) -> Self {        Self(self.0)    }} impl<A: Action> ActionExecutor<A> for WithResults<A> {    fn execute(        &self,        search_bar: &mut BufferSearchBar,        action: &A,        window: &mut Window,        cx: &mut Context<BufferSearchBar>,    ) -> DidHandleAction {        if search_bar.active_match_index.is_some() {            self.0(search_bar, action, window, cx);            true        } else {            false        }    }}