@@ -0,0 +1,91 @@
+use std::ops::Range;
+
+use smallvec::SmallVec;
+
+use crate::{AnyElement, Component, Element, ElementId, StyleRefinement, ViewContext};
+
+// We want to support uniform and non-uniform height
+// We need to make the ID mandatory, to replace the 'state' field
+// Previous implementation measured the first element as early as possible
+
+fn list<'a, Id, V, Iter, C>(
+ id: Id,
+ f: impl 'static + FnOnce(&'a mut V, Range<usize>, &'a mut ViewContext<V>) -> Iter,
+) -> List<V>
+where
+ Id: Into<ElementId>,
+ V: 'static,
+ Iter: 'a + Iterator<Item = C>,
+ C: Component<V>,
+{
+ List {
+ id: id.into(),
+ render_items: Box::new(|view, visible_range, cx| {
+ f(view, visible_range, cx)
+ .map(|element| element.render())
+ .collect()
+ }),
+ }
+}
+
+struct List<V> {
+ id: ElementId,
+ render_items: Box<
+ dyn for<'a> FnOnce(
+ &'a mut V,
+ Range<usize>,
+ &'a mut ViewContext<V>,
+ ) -> SmallVec<[AnyElement<V>; 64]>,
+ >,
+}
+
+impl<V> List<V> {}
+
+// #[derive(Debug)]
+// pub enum ScrollTarget {
+// Show(usize),
+// Center(usize),
+// }
+
+#[derive(Default)]
+struct ListState {
+ scroll_top: f32,
+ style: StyleRefinement,
+ // todo
+ // scroll_to: Option<ScrollTarget>,
+}
+impl<V: 'static> Element<V> for List<V> {
+ type ElementState = ListState;
+
+ fn id(&self) -> Option<crate::ElementId> {
+ Some(self.id)
+ }
+
+ fn initialize(
+ &mut self,
+ _: &mut V,
+ element_state: Option<Self::ElementState>,
+ _: &mut crate::ViewContext<V>,
+ ) -> Self::ElementState {
+ let element_state = element_state.unwrap_or_default();
+ element_state
+ }
+
+ fn layout(
+ &mut self,
+ view_state: &mut V,
+ element_state: &mut Self::ElementState,
+ cx: &mut crate::ViewContext<V>,
+ ) -> crate::LayoutId {
+ todo!()
+ }
+
+ fn paint(
+ &mut self,
+ bounds: crate::Bounds<crate::Pixels>,
+ view_state: &mut V,
+ element_state: &mut Self::ElementState,
+ cx: &mut crate::ViewContext<V>,
+ ) {
+ }
+}
@@ -0,0 +1,400 @@
+// use editor::Editor;
+// use gpui::{
+// elements::*,
+// geometry::vector::{vec2f, Vector2F},
+// keymap_matcher::KeymapContext,
+// platform::{CursorStyle, MouseButton},
+// AnyElement, AnyViewHandle, AppContext, Axis, Entity, MouseState, Task, View, ViewContext,
+// ViewHandle,
+// };
+// use menu::{Cancel, Confirm, SecondaryConfirm, SelectFirst, SelectLast, SelectNext, SelectPrev};
+// use parking_lot::Mutex;
+// use std::{cmp, sync::Arc};
+// use util::ResultExt;
+// use workspace::Modal;
+
+// #[derive(Clone, Copy)]
+// pub enum PickerEvent {
+// Dismiss,
+// }
+
+use std::ops::Range;
+
+use gpui::{div, AppContext, Component, Div, Element, ParentElement, Render, ViewContext};
+
+pub struct Picker<D> {
+ delegate: D,
+ // query_editor: ViewHandle<Editor>,
+ // list_state: UniformListState,
+ // max_size: Vector2F,
+ // theme: Arc<Mutex<Box<dyn Fn(&theme::Theme) -> theme::Picker>>>,
+ // confirmed: bool,
+ // pending_update_matches: Option<Task<Option<()>>>,
+ // confirm_on_update: Option<bool>,
+ // has_focus: bool,
+}
+
+pub trait PickerDelegate: Sized + 'static {
+ type ListItem: Element<Picker<Self>>;
+ // fn placeholder_text(&self) -> Arc<str>;
+ // fn match_count(&self) -> usize;
+ // fn selected_index(&self) -> usize;
+ // fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>);
+ // fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()>;
+ // fn confirm(&mut self, secondary: bool, cx: &mut ViewContext<Picker<Self>>);
+ // fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>);
+
+ // todo!("rename to render_candidate?")
+ fn render_match(
+ &self,
+ ix: usize,
+ active: bool,
+ hovered: bool,
+ selected: bool,
+ cx: &mut ViewContext<Picker<Self>>,
+ ) -> Self::ListItem;
+
+ // fn center_selection_after_match_updates(&self) -> bool {
+ // false
+ // }
+ // fn render_header(
+ // &self,
+ // _cx: &mut ViewContext<Picker<Self>>,
+ // ) -> Option<AnyElement<Picker<Self>>> {
+ // None
+ // }
+ // fn render_footer(
+ // &self,
+ // _cx: &mut ViewContext<Picker<Self>>,
+ // ) -> Option<AnyElement<Picker<Self>>> {
+ // None
+ // }
+}
+
+// impl<D: PickerDelegate> Entity for Picker<D> {
+// type Event = PickerEvent;
+// }
+
+impl<D: PickerDelegate> Render for Picker<D> {
+ type Element = Div<Self>;
+
+ fn render(&mut self, cx: &mut gpui::ViewContext<Self>) -> Self::Element {
+ div().child(list(
+ "candidates",
+ |this: &mut Picker<D>, visible_range, cx| {
+ visible_range
+ .into_iter()
+ .map(|ix| this.delegate.render_match(ix, false, false, false, cx))
+ },
+ ))
+ }
+}
+
+fn list<'a, D: PickerDelegate, F, I>(id: &'static str, f: F) -> Div<Picker<D>>
+where
+ F: FnOnce(&'a mut Picker<D>, Range<usize>, &'a mut ViewContext<Picker<D>>) -> I,
+ I: 'a + Iterator<Item = D::ListItem>,
+{
+ todo!();
+}
+
+// impl<D: PickerDelegate> View for Picker<D> {
+// fn ui_name() -> &'static str {
+// "Picker"
+// }
+
+// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
+// let theme = (self.theme.lock())(theme::current(cx).as_ref());
+// let query = self.query(cx);
+// let match_count = self.delegate.match_count();
+
+// let container_style;
+// let editor_style;
+// if query.is_empty() && match_count == 0 {
+// container_style = theme.empty_container;
+// editor_style = theme.empty_input_editor.container;
+// } else {
+// container_style = theme.container;
+// editor_style = theme.input_editor.container;
+// };
+
+// Flex::new(Axis::Vertical)
+// .with_child(
+// ChildView::new(&self.query_editor, cx)
+// .contained()
+// .with_style(editor_style),
+// )
+// .with_children(self.delegate.render_header(cx))
+// .with_children(if match_count == 0 {
+// if query.is_empty() {
+// None
+// } else {
+// Some(
+// Label::new("No matches", theme.no_matches.label.clone())
+// .contained()
+// .with_style(theme.no_matches.container)
+// .into_any(),
+// )
+// }
+// } else {
+// Some(
+// UniformList::new(
+// self.list_state.clone(),
+// match_count,
+// cx,
+// move |this, mut range, items, cx| {
+// let selected_ix = this.delegate.selected_index();
+// range.end = cmp::min(range.end, this.delegate.match_count());
+// items.extend(range.map(move |ix| {
+// MouseEventHandler::new::<D, _>(ix, cx, |state, cx| {
+// this.delegate.render_match(ix, state, ix == selected_ix, cx)
+// })
+// // Capture mouse events
+// .on_down(MouseButton::Left, |_, _, _| {})
+// .on_up(MouseButton::Left, |_, _, _| {})
+// .on_click(MouseButton::Left, move |click, picker, cx| {
+// picker.select_index(ix, click.cmd, cx);
+// })
+// .with_cursor_style(CursorStyle::PointingHand)
+// .into_any()
+// }));
+// },
+// )
+// .contained()
+// .with_margin_top(6.0)
+// .flex(1., false)
+// .into_any(),
+// )
+// })
+// .with_children(self.delegate.render_footer(cx))
+// .contained()
+// .with_style(container_style)
+// .constrained()
+// .with_max_width(self.max_size.x())
+// .with_max_height(self.max_size.y())
+// .into_any_named("picker")
+// }
+
+// fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
+// Self::reset_to_default_keymap_context(keymap);
+// keymap.add_identifier("menu");
+// }
+
+// fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
+// self.has_focus = true;
+// if cx.is_self_focused() {
+// cx.focus(&self.query_editor);
+// }
+// }
+
+// fn focus_out(&mut self, _: AnyViewHandle, _: &mut ViewContext<Self>) {
+// self.has_focus = false;
+// }
+// }
+
+// impl<D: PickerDelegate> Modal for Picker<D> {
+// fn has_focus(&self) -> bool {
+// self.has_focus
+// }
+
+// fn dismiss_on_event(event: &Self::Event) -> bool {
+// matches!(event, PickerEvent::Dismiss)
+// }
+// }
+
+// impl<D: PickerDelegate> Picker<D> {
+// pub fn init(cx: &mut AppContext) {
+// cx.add_action(Self::select_first);
+// cx.add_action(Self::select_last);
+// cx.add_action(Self::select_next);
+// cx.add_action(Self::select_prev);
+// cx.add_action(Self::confirm);
+// cx.add_action(Self::secondary_confirm);
+// cx.add_action(Self::cancel);
+// }
+
+// pub fn new(delegate: D, cx: &mut ViewContext<Self>) -> Self {
+// let theme = Arc::new(Mutex::new(
+// Box::new(|theme: &theme::Theme| theme.picker.clone())
+// as Box<dyn Fn(&theme::Theme) -> theme::Picker>,
+// ));
+// let placeholder_text = delegate.placeholder_text();
+// let query_editor = cx.add_view({
+// let picker_theme = theme.clone();
+// |cx| {
+// let mut editor = Editor::single_line(
+// Some(Arc::new(move |theme| {
+// (picker_theme.lock())(theme).input_editor.clone()
+// })),
+// cx,
+// );
+// editor.set_placeholder_text(placeholder_text, cx);
+// editor
+// }
+// });
+// cx.subscribe(&query_editor, Self::on_query_editor_event)
+// .detach();
+// let mut this = Self {
+// query_editor,
+// list_state: Default::default(),
+// delegate,
+// max_size: vec2f(540., 420.),
+// theme,
+// confirmed: false,
+// pending_update_matches: None,
+// confirm_on_update: None,
+// has_focus: false,
+// };
+// this.update_matches(String::new(), cx);
+// this
+// }
+
+// pub fn with_max_size(mut self, width: f32, height: f32) -> Self {
+// self.max_size = vec2f(width, height);
+// self
+// }
+
+// pub fn with_theme<F>(self, theme: F) -> Self
+// where
+// F: 'static + Fn(&theme::Theme) -> theme::Picker,
+// {
+// *self.theme.lock() = Box::new(theme);
+// self
+// }
+
+// pub fn delegate(&self) -> &D {
+// &self.delegate
+// }
+
+// pub fn delegate_mut(&mut self) -> &mut D {
+// &mut self.delegate
+// }
+
+// pub fn query(&self, cx: &AppContext) -> String {
+// self.query_editor.read(cx).text(cx)
+// }
+
+// pub fn set_query(&self, query: impl Into<Arc<str>>, cx: &mut ViewContext<Self>) {
+// self.query_editor
+// .update(cx, |editor, cx| editor.set_text(query, cx));
+// }
+
+// fn on_query_editor_event(
+// &mut self,
+// _: ViewHandle<Editor>,
+// event: &editor::Event,
+// cx: &mut ViewContext<Self>,
+// ) {
+// match event {
+// editor::Event::BufferEdited { .. } => self.update_matches(self.query(cx), cx),
+// editor::Event::Blurred if !self.confirmed => {
+// self.dismiss(cx);
+// }
+// _ => {}
+// }
+// }
+
+// pub fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) {
+// let update = self.delegate.update_matches(query, cx);
+// self.matches_updated(cx);
+// self.pending_update_matches = Some(cx.spawn(|this, mut cx| async move {
+// update.await;
+// this.update(&mut cx, |this, cx| {
+// this.matches_updated(cx);
+// })
+// .log_err()
+// }));
+// }
+
+// fn matches_updated(&mut self, cx: &mut ViewContext<Self>) {
+// let index = self.delegate.selected_index();
+// let target = if self.delegate.center_selection_after_match_updates() {
+// ScrollTarget::Center(index)
+// } else {
+// ScrollTarget::Show(index)
+// };
+// self.list_state.scroll_to(target);
+// self.pending_update_matches = None;
+// if let Some(secondary) = self.confirm_on_update.take() {
+// self.confirmed = true;
+// self.delegate.confirm(secondary, cx)
+// }
+// cx.notify();
+// }
+
+// pub fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) {
+// if self.delegate.match_count() > 0 {
+// self.delegate.set_selected_index(0, cx);
+// self.list_state.scroll_to(ScrollTarget::Show(0));
+// }
+
+// cx.notify();
+// }
+
+// pub fn select_index(&mut self, index: usize, cmd: bool, cx: &mut ViewContext<Self>) {
+// if self.delegate.match_count() > 0 {
+// self.confirmed = true;
+// self.delegate.set_selected_index(index, cx);
+// self.delegate.confirm(cmd, cx);
+// }
+// }
+
+// pub fn select_last(&mut self, _: &SelectLast, cx: &mut ViewContext<Self>) {
+// let match_count = self.delegate.match_count();
+// if match_count > 0 {
+// let index = match_count - 1;
+// self.delegate.set_selected_index(index, cx);
+// self.list_state.scroll_to(ScrollTarget::Show(index));
+// }
+// cx.notify();
+// }
+
+// pub fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
+// let next_index = self.delegate.selected_index() + 1;
+// if next_index < self.delegate.match_count() {
+// self.delegate.set_selected_index(next_index, cx);
+// self.list_state.scroll_to(ScrollTarget::Show(next_index));
+// }
+
+// cx.notify();
+// }
+
+// pub fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
+// let mut selected_index = self.delegate.selected_index();
+// if selected_index > 0 {
+// selected_index -= 1;
+// self.delegate.set_selected_index(selected_index, cx);
+// self.list_state
+// .scroll_to(ScrollTarget::Show(selected_index));
+// }
+
+// cx.notify();
+// }
+
+// pub fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
+// if self.pending_update_matches.is_some() {
+// self.confirm_on_update = Some(false)
+// } else {
+// self.confirmed = true;
+// self.delegate.confirm(false, cx);
+// }
+// }
+
+// pub fn secondary_confirm(&mut self, _: &SecondaryConfirm, cx: &mut ViewContext<Self>) {
+// if self.pending_update_matches.is_some() {
+// self.confirm_on_update = Some(true)
+// } else {
+// self.confirmed = true;
+// self.delegate.confirm(true, cx);
+// }
+// }
+
+// fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
+// self.dismiss(cx);
+// }
+
+// fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
+// cx.emit(PickerEvent::Dismiss);
+// self.delegate.dismissed(cx);
+// }
+// }