diff --git a/crates/ui-story/src/picker_story.rs b/crates/ui-story/src/picker_story.rs index 6bfa8b54..8887e573 100644 --- a/crates/ui-story/src/picker_story.rs +++ b/crates/ui-story/src/picker_story.rs @@ -37,7 +37,7 @@ impl PickerDelegate for ListItemDeletegate { self.selected_index = index } - fn render_match( + fn render_item( &self, ix: usize, selected: bool, @@ -198,23 +198,27 @@ impl Render for PickerStory { .style(ButtonStyle::Primary) .on_click(cx.listener(|this, _, cx| { this.open = !this.open; + this.picker.focus_handle(cx).focus(cx); cx.notify(); })), ), ) .when_some(self.selected_value.clone(), |this, selected_value| { - this.child("Selected: ").child(Label::new(selected_value)) + this.child( + h_flex() + .gap_1() + .child("You have selected:") + .child(selected_value), + ) }) .when(self.open, |this| { this.child( div().absolute().size_full().top_0().left_0().child( v_flex() - // .h(px(0.0)) .top_10() .flex() .flex_col() .items_center() - .track_focus(&self.picker.focus_handle(cx)) .child(h_flex().w(px(450.)).occlude().child(self.picker.clone())), ), ) diff --git a/crates/ui/src/dropdown.rs b/crates/ui/src/dropdown.rs index bb98bb9f..8eb5285d 100644 --- a/crates/ui/src/dropdown.rs +++ b/crates/ui/src/dropdown.rs @@ -73,7 +73,7 @@ where self.selected_index = index; } - fn render_match( + fn render_item( &self, ix: usize, selected: bool, @@ -135,7 +135,7 @@ where selected_index: 0, }; - let picker = cx.new_view(|cx| Picker::uniform_list(picker_delegate, cx)); + let picker = cx.new_view(|cx| Picker::uniform_list(picker_delegate, cx).no_query()); Self { id: id.into(), focus_handle: cx.focus_handle(), diff --git a/crates/ui/src/picker.rs b/crates/ui/src/picker.rs index 9e4fd87a..b54e418f 100644 --- a/crates/ui/src/picker.rs +++ b/crates/ui/src/picker.rs @@ -1,5 +1,3 @@ -use std::time::Duration; - use anyhow::Result; use gpui::{ actions, div, list, prelude::FluentBuilder as _, px, rems, uniform_list, AppContext, @@ -12,7 +10,6 @@ use gpui::{ actions!( picker, [ - UseSelectedQuery, Cancel, Confirm, SecondaryConfirm, @@ -57,8 +54,13 @@ pub trait PickerDelegate: Sized + 'static { type ListItem: IntoElement; fn match_count(&self) -> usize; + + /// Return the index of the selected item. fn selected_index(&self) -> usize; - fn set_selected_index(&mut self, index: usize, cx: &mut ViewContext>); + + /// Update the selected index. + fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext>); + fn selected_index_changed( &self, _ix: usize, @@ -67,11 +69,18 @@ pub trait PickerDelegate: Sized + 'static { None } + /// Callback when the picker is confirmed. fn confirm(&mut self, _secondary: bool, _cx: &mut ViewContext>) {} + + /// Callback when the picker is dismissed. fn dismissed(&mut self, _cx: &mut ViewContext>) {} + + /// Determine if the picker should be dismissed, return true by default. Return false will abort the dismiss action. fn should_dismiss(&self) -> bool { true } + + /// Override this method to customize the query input header container. fn render_query(&self, input: &View, _cx: &mut ViewContext>) -> Div { v_flex() .child( @@ -84,7 +93,9 @@ pub trait PickerDelegate: Sized + 'static { ) .child(Divider::horizontal()) } - fn render_match( + + /// Render the list item at the given index. + fn render_item( &self, ix: usize, selected: bool, @@ -94,21 +105,19 @@ pub trait PickerDelegate: Sized + 'static { fn separators_after_indices(&self) -> Vec { Vec::new() } + fn update_matches(&mut self, _query: &str, _cx: &mut ViewContext>) -> Task<()> { Task::ready(()) } - fn confirm_update_query(&mut self, _cx: &mut ViewContext>) -> Option { + + fn confirm_update_query( + &mut self, + _cx: &mut ViewContext>, + ) -> Option { None } - fn finalize_update_matches( - &mut self, - _query: SharedString, - _duration: Duration, - _cx: &mut ViewContext>, - ) -> bool { - false - } - fn selected_as_query(&self) -> Option { + + fn selected_as_query(&self) -> Option { None } } @@ -137,9 +146,9 @@ pub struct Picker { width: Option, max_height: Option, is_modal: bool, + /// Just a empty view for holding the focus head: View, pending_update_matches: Option, - confirm_on_update: Option, } impl Picker { @@ -181,7 +190,6 @@ impl Picker { max_height: Some(rems(20.).into()), element_container, pending_update_matches: None, - confirm_on_update: None, } } @@ -227,6 +235,12 @@ impl Picker { self.focus_handle(cx).focus(cx); } + /// Hide the query input. + pub fn no_query(mut self) -> Self { + self.query_input = None; + self + } + pub fn set_query(&mut self, query: &str, cx: &mut ViewContext) { if let Some(input) = &self.query_input { input.update(cx, |this, cx| this.set_text(query.to_string(), cx)); @@ -292,7 +306,7 @@ impl Picker { ) .children( self.delegate - .render_match(ix, ix == self.delegate.selected_index(), cx), + .render_item(ix, ix == self.delegate.selected_index(), cx), ) .when( self.delegate.separators_after_indices().contains(&ix), @@ -313,12 +327,12 @@ impl Picker { } fn do_confirm(&mut self, secondary: bool, cx: &mut ViewContext) { - // if let Some(update_query) = self.delegate.confirm_update_query(cx) { - // self.set_query(update_query, cx); - // self.delegate.set_selected_index(0, cx); - // } else { - self.delegate.confirm(secondary, cx) - // } + if let Some(update_query) = self.delegate.confirm_update_query(cx) { + self.set_query(&update_query, cx); + self.delegate.set_selected_index(0, cx); + } else { + self.delegate.confirm(secondary, cx) + } } pub fn set_selected_index( @@ -349,16 +363,7 @@ impl Picker { } fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { - if self.pending_update_matches.is_some() - && !self - .delegate - .finalize_update_matches(self.query(cx), Duration::from_millis(16), cx) - { - self.confirm_on_update = Some(false) - } else { - self.pending_update_matches.take(); - self.do_confirm(false, cx); - } + self.do_confirm(false, cx); } pub fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext) { @@ -405,13 +410,6 @@ impl Picker { } } - fn use_selected_query(&mut self, _: &UseSelectedQuery, cx: &mut ViewContext) { - if let Some(new_query) = self.delegate.selected_as_query() { - self.set_query(&new_query, cx); - cx.stop_propagation(); - } - } - pub fn refresh(&mut self, cx: &mut ViewContext) { let query = self.query(cx); self.update_matches(&query, cx); @@ -478,9 +476,12 @@ impl Picker { impl Render for Picker { fn render(&mut self, cx: &mut ViewContext) -> impl IntoElement { + let focus_handle = self.focus_handle(cx); + v_flex() .key_context("Picker") .size_full() + .track_focus(&focus_handle) .when_some(self.width, |el, width| el.w(width)) .overflow_hidden() .when(self.is_modal, |this| this.elevation_3(cx)) @@ -490,9 +491,6 @@ impl Render for Picker { .on_action(cx.listener(Self::select_last)) .on_action(cx.listener(Self::cancel)) .on_action(cx.listener(Self::confirm)) - // .on_action(cx.listener(Self::secondary_confirm)) - .on_action(cx.listener(Self::use_selected_query)) - // .on_action(cx.listener(Self::confirm_input)) // Render Query Input header .child(match self.query_input { Some(ref input) => self.delegate.render_query(input, cx), @@ -504,7 +502,6 @@ impl Render for Picker { .flex_grow() .when_some(self.max_height, |div, max_h| div.max_h(max_h)) .overflow_hidden() - // .children(self.delegate.render_header(cx)) .child(self.render_element_container(cx)), ) })