picker2.rs

  1use editor::Editor;
  2use gpui::{
  3    div, uniform_list, Component, Div, FocusEnabled, ParentElement, Render, StatefulInteractivity,
  4    StatelessInteractive, Styled, UniformListScrollHandle, View, ViewContext, VisualContext,
  5    WindowContext,
  6};
  7use std::cmp;
  8
  9pub struct Picker<D: PickerDelegate> {
 10    pub delegate: D,
 11    scroll_handle: UniformListScrollHandle,
 12    editor: View<Editor>,
 13}
 14
 15pub trait PickerDelegate: Sized + 'static {
 16    type ListItem: Component<Picker<Self>>;
 17
 18    fn match_count(&self) -> usize;
 19    fn selected_index(&self) -> usize;
 20    fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>);
 21
 22    // fn placeholder_text(&self) -> Arc<str>;
 23    // fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()>;
 24
 25    fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<Self>>);
 26    fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>);
 27
 28    fn render_match(
 29        &self,
 30        ix: usize,
 31        selected: bool,
 32        cx: &mut ViewContext<Picker<Self>>,
 33    ) -> Self::ListItem;
 34}
 35
 36impl<D: PickerDelegate> Picker<D> {
 37    pub fn new(delegate: D, cx: &mut ViewContext<Self>) -> Self {
 38        Self {
 39            delegate,
 40            scroll_handle: UniformListScrollHandle::new(),
 41            editor: cx.build_view(|cx| Editor::single_line(cx)),
 42        }
 43    }
 44
 45    pub fn focus(&self, cx: &mut WindowContext) {
 46        self.editor.update(cx, |editor, cx| editor.focus(cx));
 47    }
 48
 49    fn select_next(&mut self, _: &menu::SelectNext, cx: &mut ViewContext<Self>) {
 50        let count = self.delegate.match_count();
 51        if count > 0 {
 52            let index = self.delegate.selected_index();
 53            let ix = cmp::min(index + 1, count - 1);
 54            self.delegate.set_selected_index(ix, cx);
 55            self.scroll_handle.scroll_to_item(ix);
 56        }
 57    }
 58
 59    fn select_prev(&mut self, _: &menu::SelectPrev, cx: &mut ViewContext<Self>) {
 60        let count = self.delegate.match_count();
 61        if count > 0 {
 62            let index = self.delegate.selected_index();
 63            let ix = index.saturating_sub(1);
 64            self.delegate.set_selected_index(ix, cx);
 65            self.scroll_handle.scroll_to_item(ix);
 66        }
 67    }
 68
 69    fn select_first(&mut self, _: &menu::SelectFirst, cx: &mut ViewContext<Self>) {
 70        let count = self.delegate.match_count();
 71        if count > 0 {
 72            self.delegate.set_selected_index(0, cx);
 73            self.scroll_handle.scroll_to_item(0);
 74        }
 75    }
 76
 77    fn select_last(&mut self, _: &menu::SelectLast, cx: &mut ViewContext<Self>) {
 78        let count = self.delegate.match_count();
 79        if count > 0 {
 80            self.delegate.set_selected_index(count - 1, cx);
 81            self.scroll_handle.scroll_to_item(count - 1);
 82        }
 83    }
 84
 85    fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
 86        self.delegate.dismissed(cx);
 87    }
 88
 89    fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
 90        self.delegate.confirm(false, cx);
 91    }
 92
 93    fn secondary_confirm(&mut self, _: &menu::SecondaryConfirm, cx: &mut ViewContext<Self>) {
 94        self.delegate.confirm(true, cx);
 95    }
 96}
 97
 98impl<D: PickerDelegate> Render for Picker<D> {
 99    type Element = Div<Self, StatefulInteractivity<Self>, FocusEnabled<Self>>;
100
101    fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
102        div()
103            .context("picker")
104            .id("picker-container")
105            .focusable()
106            .size_full()
107            .on_action(Self::select_next)
108            .on_action(Self::select_prev)
109            .on_action(Self::select_first)
110            .on_action(Self::select_last)
111            .on_action(Self::cancel)
112            .on_action(Self::confirm)
113            .on_action(Self::secondary_confirm)
114            .child(self.editor.clone())
115            .child(
116                uniform_list("candidates", self.delegate.match_count(), {
117                    move |this: &mut Self, visible_range, cx| {
118                        let selected_ix = this.delegate.selected_index();
119                        visible_range
120                            .map(|ix| this.delegate.render_match(ix, ix == selected_ix, cx))
121                            .collect()
122                    }
123                })
124                .track_scroll(self.scroll_handle.clone())
125                .size_full(),
126            )
127    }
128}