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// }