picker2.rs

  1// use editor::Editor;
  2// use gpui::{
  3//     elements::*,
  4//     geometry::vector::{vec2f, Vector2F},
  5//     keymap_matcher::KeymapContext,
  6//     platform::{CursorStyle, MouseButton},
  7//     AnyElement, AnyViewHandle, AppContext, Axis, Entity, MouseState, Task, View, ViewContext,
  8//     ViewHandle,
  9// };
 10// use menu::{Cancel, Confirm, SecondaryConfirm, SelectFirst, SelectLast, SelectNext, SelectPrev};
 11// use parking_lot::Mutex;
 12// use std::{cmp, sync::Arc};
 13// use util::ResultExt;
 14// use workspace::Modal;
 15
 16// #[derive(Clone, Copy)]
 17// pub enum PickerEvent {
 18//     Dismiss,
 19// }
 20
 21use std::cmp;
 22
 23use gpui::{
 24    div, list, Component, ElementId, FocusHandle, Focusable, ParentElement, StatelessInteractive,
 25    Styled, ViewContext,
 26};
 27
 28// pub struct Picker<D> {
 29//     delegate: D,
 30//     query_editor: ViewHandle<Editor>,
 31//     list_state: UniformListState,
 32//     max_size: Vector2F,
 33//     theme: Arc<Mutex<Box<dyn Fn(&theme::Theme) -> theme::Picker>>>,
 34//     confirmed: bool,
 35//     pending_update_matches: Option<Task<Option<()>>>,
 36//     confirm_on_update: Option<bool>,
 37//     has_focus: bool,
 38// }
 39
 40pub trait PickerDelegate: Sized + 'static {
 41    type ListItem: Component<Self>;
 42    //     fn placeholder_text(&self) -> Arc<str>;
 43
 44    fn match_count(&self, picker_id: ElementId) -> usize;
 45
 46    fn selected_index(&self, picker_id: ElementId) -> usize;
 47    fn set_selected_index(&mut self, ix: usize, picker_id: ElementId, cx: &mut ViewContext<Self>);
 48
 49    //     fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()>;
 50    //     fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<Self>>);
 51    //     fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>);
 52
 53    // todo!("rename to render_candidate?")
 54    fn render_match(
 55        &self,
 56        ix: usize,
 57        selected: bool,
 58        picker_id: ElementId,
 59        cx: &mut ViewContext<Self>,
 60    ) -> Self::ListItem;
 61
 62    //     fn center_selection_after_match_updates(&self) -> bool {
 63    //         false
 64    //     }
 65    //     fn render_header(
 66    //         &self,
 67    //         _cx: &mut ViewContext<Picker<Self>>,
 68    //     ) -> Option<AnyElement<Picker<Self>>> {
 69    //         None
 70    //     }
 71    //     fn render_footer(
 72    //         &self,
 73    //         _cx: &mut ViewContext<Picker<Self>>,
 74    //     ) -> Option<AnyElement<Picker<Self>>> {
 75    //         None
 76    //     }
 77}
 78
 79// impl<D: PickerDelegate> Entity for Picker<D> {
 80//     type Event = PickerEvent;
 81// }
 82
 83#[derive(Component)]
 84pub struct Picker<V: PickerDelegate> {
 85    id: ElementId,
 86    focus_handle: FocusHandle,
 87    phantom: std::marker::PhantomData<V>,
 88}
 89
 90impl<V: PickerDelegate> Picker<V> {
 91    pub fn new(id: impl Into<ElementId>, focus_handle: FocusHandle) -> Self {
 92        Self {
 93            id: id.into(),
 94            focus_handle,
 95            phantom: std::marker::PhantomData,
 96        }
 97    }
 98}
 99
100impl<V: 'static + PickerDelegate> Picker<V> {
101    pub fn render(self, view: &mut V, _cx: &mut ViewContext<V>) -> impl Component<V> {
102        let id = self.id.clone();
103        div()
104            .size_full()
105            .id(self.id.clone())
106            .track_focus(&self.focus_handle)
107            .context("picker")
108            .on_focus(|v, e, cx| {
109                dbg!("FOCUSED!");
110            })
111            .on_blur(|v, e, cx| {
112                dbg!("BLURRED!");
113            })
114            .on_action({
115                let id = id.clone();
116                move |view: &mut V, _: &menu::SelectNext, cx| {
117                    let index = view.selected_index(id.clone());
118                    let count = view.match_count(id.clone());
119                    if count > 0 {
120                        view.set_selected_index(cmp::min(index + 1, count - 1), id.clone(), cx);
121                    }
122                }
123            })
124            .on_action({
125                let id = id.clone();
126                move |view, _: &menu::SelectPrev, cx| {
127                    let index = view.selected_index(id.clone());
128                    let count = view.match_count(id.clone());
129                    if count > 0 {
130                        view.set_selected_index((index + 1) % count, id.clone(), cx);
131                    }
132                }
133            })
134            .on_action(|view, _: &menu::SelectFirst, cx| {})
135            .on_action(|view, _: &menu::SelectLast, cx| {})
136            .on_action(|view, _: &menu::Cancel, cx| {})
137            .on_action(|view, _: &menu::Confirm, cx| {})
138            .on_action(|view, _: &menu::SecondaryConfirm, cx| {})
139            .child(
140                list(
141                    "candidates",
142                    view.match_count(self.id.clone()),
143                    move |view: &mut V, visible_range, cx| {
144                        let selected_ix = view.selected_index(self.id.clone());
145                        visible_range
146                            .map(|ix| view.render_match(ix, ix == selected_ix, self.id.clone(), cx))
147                            .collect()
148                    },
149                )
150                .size_full(),
151            )
152    }
153}
154
155// impl<D: PickerDelegate> View for Picker<D> {
156//     fn ui_name() -> &'static str {
157//         "Picker"
158//     }
159
160//     fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
161//         let theme = (self.theme.lock())(theme::current(cx).as_ref());
162//         let query = self.query(cx);
163//         let match_count = self.delegate.match_count();
164
165//         let container_style;
166//         let editor_style;
167//         if query.is_empty() && match_count == 0 {
168//             container_style = theme.empty_container;
169//             editor_style = theme.empty_input_editor.container;
170//         } else {
171//             container_style = theme.container;
172//             editor_style = theme.input_editor.container;
173//         };
174
175//         Flex::new(Axis::Vertical)
176//             .with_child(
177//                 ChildView::new(&self.query_editor, cx)
178//                     .contained()
179//                     .with_style(editor_style),
180//             )
181//             .with_children(self.delegate.render_header(cx))
182//             .with_children(if match_count == 0 {
183//                 if query.is_empty() {
184//                     None
185//                 } else {
186//                     Some(
187//                         Label::new("No matches", theme.no_matches.label.clone())
188//                             .contained()
189//                             .with_style(theme.no_matches.container)
190//                             .into_any(),
191//                     )
192//                 }
193//             } else {
194//                 Some(
195//                     UniformList::new(
196//                         self.list_state.clone(),
197//                         match_count,
198//                         cx,
199//                         move |this, mut range, items, cx| {
200//                             let selected_ix = this.delegate.selected_index();
201//                             range.end = cmp::min(range.end, this.delegate.match_count());
202//                             items.extend(range.map(move |ix| {
203//                                 MouseEventHandler::new::<D, _>(ix, cx, |state, cx| {
204//                                     this.delegate.render_match(ix, state, ix == selected_ix, cx)
205//                                 })
206//                                 // Capture mouse events
207//                                 .on_down(MouseButton::Left, |_, _, _| {})
208//                                 .on_up(MouseButton::Left, |_, _, _| {})
209//                                 .on_click(MouseButton::Left, move |click, picker, cx| {
210//                                     picker.select_index(ix, click.cmd, cx);
211//                                 })
212//                                 .with_cursor_style(CursorStyle::PointingHand)
213//                                 .into_any()
214//                             }));
215//                         },
216//                     )
217//                     .contained()
218//                     .with_margin_top(6.0)
219//                     .flex(1., false)
220//                     .into_any(),
221//                 )
222//             })
223//             .with_children(self.delegate.render_footer(cx))
224//             .contained()
225//             .with_style(container_style)
226//             .constrained()
227//             .with_max_width(self.max_size.x())
228//             .with_max_height(self.max_size.y())
229//             .into_any_named("picker")
230//     }
231
232//     fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
233//         Self::reset_to_default_keymap_context(keymap);
234//         keymap.add_identifier("menu");
235//     }
236
237//     fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
238//         self.has_focus = true;
239//         if cx.is_self_focused() {
240//             cx.focus(&self.query_editor);
241//         }
242//     }
243
244//     fn focus_out(&mut self, _: AnyViewHandle, _: &mut ViewContext<Self>) {
245//         self.has_focus = false;
246//     }
247// }
248
249// impl<D: PickerDelegate> Modal for Picker<D> {
250//     fn has_focus(&self) -> bool {
251//         self.has_focus
252//     }
253
254//     fn dismiss_on_event(event: &Self::Event) -> bool {
255//         matches!(event, PickerEvent::Dismiss)
256//     }
257// }
258
259// impl<D: PickerDelegate> Picker<D> {
260//     pub fn init(cx: &mut AppContext) {
261//         cx.add_action(Self::select_first);
262//         cx.add_action(Self::select_last);
263//         cx.add_action(Self::select_next);
264//         cx.add_action(Self::select_prev);
265//         cx.add_action(Self::confirm);
266//         cx.add_action(Self::secondary_confirm);
267//         cx.add_action(Self::cancel);
268//     }
269
270// pub fn new(delegate: D, cx: &mut ViewContext<Self>) -> Self {
271//         let theme = Arc::new(Mutex::new(
272//             Box::new(|theme: &theme::Theme| theme.picker.clone())
273//                 as Box<dyn Fn(&theme::Theme) -> theme::Picker>,
274//         ));
275//         let placeholder_text = delegate.placeholder_text();
276//         let query_editor = cx.add_view({
277//             let picker_theme = theme.clone();
278//             |cx| {
279//                 let mut editor = Editor::single_line(
280//                     Some(Arc::new(move |theme| {
281//                         (picker_theme.lock())(theme).input_editor.clone()
282//                     })),
283//                     cx,
284//                 );
285//                 editor.set_placeholder_text(placeholder_text, cx);
286//                 editor
287//             }
288//         });
289//         cx.subscribe(&query_editor, Self::on_query_editor_event)
290//             .detach();
291//         let mut this = Self {
292//             query_editor,
293//             list_state: Default::default(),
294//             delegate,
295//             max_size: vec2f(540., 420.),
296//             theme,
297//             confirmed: false,
298//             pending_update_matches: None,
299//             confirm_on_update: None,
300//             has_focus: false,
301//         };
302//         this.update_matches(String::new(), cx);
303//         this
304// Self { delegate }
305// }
306
307//     pub fn with_max_size(mut self, width: f32, height: f32) -> Self {
308//         self.max_size = vec2f(width, height);
309//         self
310//     }
311
312//     pub fn with_theme<F>(self, theme: F) -> Self
313//     where
314//         F: 'static + Fn(&theme::Theme) -> theme::Picker,
315//     {
316//         *self.theme.lock() = Box::new(theme);
317//         self
318//     }
319
320//     pub fn delegate(&self) -> &D {
321//         &self.delegate
322//     }
323
324//     pub fn delegate_mut(&mut self) -> &mut D {
325//         &mut self.delegate
326//     }
327
328//     pub fn query(&self, cx: &AppContext) -> String {
329//         self.query_editor.read(cx).text(cx)
330//     }
331
332//     pub fn set_query(&self, query: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
333//         self.query_editor
334//             .update(cx, |editor, cx| editor.set_text(query, cx));
335//     }
336
337//     fn on_query_editor_event(
338//         &mut self,
339//         _: ViewHandle<Editor>,
340//         event: &editor::Event,
341//         cx: &mut ViewContext<Self>,
342//     ) {
343//         match event {
344//             editor::Event::BufferEdited { .. } => self.update_matches(self.query(cx), cx),
345//             editor::Event::Blurred if !self.confirmed => {
346//                 self.dismiss(cx);
347//             }
348//             _ => {}
349//         }
350//     }
351
352//     pub fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) {
353//         let update = self.delegate.update_matches(query, cx);
354//         self.matches_updated(cx);
355//         self.pending_update_matches = Some(cx.spawn(|this, mut cx| async move {
356//             update.await;
357//             this.update(&mut cx, |this, cx| {
358//                 this.matches_updated(cx);
359//             })
360//             .log_err()
361//         }));
362//     }
363
364//     fn matches_updated(&mut self, cx: &mut ViewContext<Self>) {
365//         let index = self.delegate.selected_index();
366//         let target = if self.delegate.center_selection_after_match_updates() {
367//             ScrollTarget::Center(index)
368//         } else {
369//             ScrollTarget::Show(index)
370//         };
371//         self.list_state.scroll_to(target);
372//         self.pending_update_matches = None;
373//         if let Some(secondary) = self.confirm_on_update.take() {
374//             self.confirmed = true;
375//             self.delegate.confirm(secondary, cx)
376//         }
377//         cx.notify();
378//     }
379
380//     pub fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) {
381//         if self.delegate.match_count() > 0 {
382//             self.delegate.set_selected_index(0, cx);
383//             self.list_state.scroll_to(ScrollTarget::Show(0));
384//         }
385
386//         cx.notify();
387//     }
388
389//     pub fn select_index(&mut self, index: usize, cmd: bool, cx: &mut ViewContext<Self>) {
390//         if self.delegate.match_count() > 0 {
391//             self.confirmed = true;
392//             self.delegate.set_selected_index(index, cx);
393//             self.delegate.confirm(cmd, cx);
394//         }
395//     }
396
397//     pub fn select_last(&mut self, _: &SelectLast, cx: &mut ViewContext<Self>) {
398//         let match_count = self.delegate.match_count();
399//         if match_count > 0 {
400//             let index = match_count - 1;
401//             self.delegate.set_selected_index(index, cx);
402//             self.list_state.scroll_to(ScrollTarget::Show(index));
403//         }
404//         cx.notify();
405//     }
406
407//     pub fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
408//         let next_index = self.delegate.selected_index() + 1;
409//         if next_index < self.delegate.match_count() {
410//             self.delegate.set_selected_index(next_index, cx);
411//             self.list_state.scroll_to(ScrollTarget::Show(next_index));
412//         }
413
414//         cx.notify();
415//     }
416
417//     pub fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
418//         let mut selected_index = self.delegate.selected_index();
419//         if selected_index > 0 {
420//             selected_index -= 1;
421//             self.delegate.set_selected_index(selected_index, cx);
422//             self.list_state
423//                 .scroll_to(ScrollTarget::Show(selected_index));
424//         }
425
426//         cx.notify();
427//     }
428
429//     pub fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
430//         if self.pending_update_matches.is_some() {
431//             self.confirm_on_update = Some(false)
432//         } else {
433//             self.confirmed = true;
434//             self.delegate.confirm(false, cx);
435//         }
436//     }
437
438//     pub fn secondary_confirm(&mut self, _: &SecondaryConfirm, cx: &mut ViewContext<Self>) {
439//         if self.pending_update_matches.is_some() {
440//             self.confirm_on_update = Some(true)
441//         } else {
442//             self.confirmed = true;
443//             self.delegate.confirm(true, cx);
444//         }
445//     }
446
447//     fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
448//         self.dismiss(cx);
449//     }
450
451//     fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
452//         cx.emit(PickerEvent::Dismiss);
453//         self.delegate.dismissed(cx);
454//     }
455// }