Improve Picker details.

This commit is contained in:
Jason Lee 2024-06-29 14:39:15 +08:00
parent 3288270b49
commit ddadcda140
3 changed files with 52 additions and 51 deletions

View file

@ -37,7 +37,7 @@ impl PickerDelegate for ListItemDeletegate {
self.selected_index = index self.selected_index = index
} }
fn render_match( fn render_item(
&self, &self,
ix: usize, ix: usize,
selected: bool, selected: bool,
@ -198,23 +198,27 @@ impl Render for PickerStory {
.style(ButtonStyle::Primary) .style(ButtonStyle::Primary)
.on_click(cx.listener(|this, _, cx| { .on_click(cx.listener(|this, _, cx| {
this.open = !this.open; this.open = !this.open;
this.picker.focus_handle(cx).focus(cx);
cx.notify(); cx.notify();
})), })),
), ),
) )
.when_some(self.selected_value.clone(), |this, selected_value| { .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| { .when(self.open, |this| {
this.child( this.child(
div().absolute().size_full().top_0().left_0().child( div().absolute().size_full().top_0().left_0().child(
v_flex() v_flex()
// .h(px(0.0))
.top_10() .top_10()
.flex() .flex()
.flex_col() .flex_col()
.items_center() .items_center()
.track_focus(&self.picker.focus_handle(cx))
.child(h_flex().w(px(450.)).occlude().child(self.picker.clone())), .child(h_flex().w(px(450.)).occlude().child(self.picker.clone())),
), ),
) )

View file

@ -73,7 +73,7 @@ where
self.selected_index = index; self.selected_index = index;
} }
fn render_match( fn render_item(
&self, &self,
ix: usize, ix: usize,
selected: bool, selected: bool,
@ -135,7 +135,7 @@ where
selected_index: 0, 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 { Self {
id: id.into(), id: id.into(),
focus_handle: cx.focus_handle(), focus_handle: cx.focus_handle(),

View file

@ -1,5 +1,3 @@
use std::time::Duration;
use anyhow::Result; use anyhow::Result;
use gpui::{ use gpui::{
actions, div, list, prelude::FluentBuilder as _, px, rems, uniform_list, AppContext, actions, div, list, prelude::FluentBuilder as _, px, rems, uniform_list, AppContext,
@ -12,7 +10,6 @@ use gpui::{
actions!( actions!(
picker, picker,
[ [
UseSelectedQuery,
Cancel, Cancel,
Confirm, Confirm,
SecondaryConfirm, SecondaryConfirm,
@ -57,8 +54,13 @@ pub trait PickerDelegate: Sized + 'static {
type ListItem: IntoElement; type ListItem: IntoElement;
fn match_count(&self) -> usize; fn match_count(&self) -> usize;
/// Return the index of the selected item.
fn selected_index(&self) -> usize; fn selected_index(&self) -> usize;
fn set_selected_index(&mut self, index: usize, cx: &mut ViewContext<Picker<Self>>);
/// Update the selected index.
fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>);
fn selected_index_changed( fn selected_index_changed(
&self, &self,
_ix: usize, _ix: usize,
@ -67,11 +69,18 @@ pub trait PickerDelegate: Sized + 'static {
None None
} }
/// Callback when the picker is confirmed.
fn confirm(&mut self, _secondary: bool, _cx: &mut ViewContext<Picker<Self>>) {} fn confirm(&mut self, _secondary: bool, _cx: &mut ViewContext<Picker<Self>>) {}
/// Callback when the picker is dismissed.
fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {} fn dismissed(&mut self, _cx: &mut ViewContext<Picker<Self>>) {}
/// Determine if the picker should be dismissed, return true by default. Return false will abort the dismiss action.
fn should_dismiss(&self) -> bool { fn should_dismiss(&self) -> bool {
true true
} }
/// Override this method to customize the query input header container.
fn render_query(&self, input: &View<TextInput>, _cx: &mut ViewContext<Picker<Self>>) -> Div { fn render_query(&self, input: &View<TextInput>, _cx: &mut ViewContext<Picker<Self>>) -> Div {
v_flex() v_flex()
.child( .child(
@ -84,7 +93,9 @@ pub trait PickerDelegate: Sized + 'static {
) )
.child(Divider::horizontal()) .child(Divider::horizontal())
} }
fn render_match(
/// Render the list item at the given index.
fn render_item(
&self, &self,
ix: usize, ix: usize,
selected: bool, selected: bool,
@ -94,21 +105,19 @@ pub trait PickerDelegate: Sized + 'static {
fn separators_after_indices(&self) -> Vec<usize> { fn separators_after_indices(&self) -> Vec<usize> {
Vec::new() Vec::new()
} }
fn update_matches(&mut self, _query: &str, _cx: &mut ViewContext<Picker<Self>>) -> Task<()> { fn update_matches(&mut self, _query: &str, _cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
Task::ready(()) Task::ready(())
} }
fn confirm_update_query(&mut self, _cx: &mut ViewContext<Picker<Self>>) -> Option<String> {
fn confirm_update_query(
&mut self,
_cx: &mut ViewContext<Picker<Self>>,
) -> Option<SharedString> {
None None
} }
fn finalize_update_matches(
&mut self, fn selected_as_query(&self) -> Option<SharedString> {
_query: SharedString,
_duration: Duration,
_cx: &mut ViewContext<Picker<Self>>,
) -> bool {
false
}
fn selected_as_query(&self) -> Option<String> {
None None
} }
} }
@ -137,9 +146,9 @@ pub struct Picker<D: PickerDelegate> {
width: Option<Length>, width: Option<Length>,
max_height: Option<Length>, max_height: Option<Length>,
is_modal: bool, is_modal: bool,
/// Just a empty view for holding the focus
head: View<Empty>, head: View<Empty>,
pending_update_matches: Option<PendingUpdateMatches>, pending_update_matches: Option<PendingUpdateMatches>,
confirm_on_update: Option<bool>,
} }
impl<D: PickerDelegate> Picker<D> { impl<D: PickerDelegate> Picker<D> {
@ -181,7 +190,6 @@ impl<D: PickerDelegate> Picker<D> {
max_height: Some(rems(20.).into()), max_height: Some(rems(20.).into()),
element_container, element_container,
pending_update_matches: None, pending_update_matches: None,
confirm_on_update: None,
} }
} }
@ -227,6 +235,12 @@ impl<D: PickerDelegate> Picker<D> {
self.focus_handle(cx).focus(cx); 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<Self>) { pub fn set_query(&mut self, query: &str, cx: &mut ViewContext<Self>) {
if let Some(input) = &self.query_input { if let Some(input) = &self.query_input {
input.update(cx, |this, cx| this.set_text(query.to_string(), cx)); input.update(cx, |this, cx| this.set_text(query.to_string(), cx));
@ -292,7 +306,7 @@ impl<D: PickerDelegate> Picker<D> {
) )
.children( .children(
self.delegate self.delegate
.render_match(ix, ix == self.delegate.selected_index(), cx), .render_item(ix, ix == self.delegate.selected_index(), cx),
) )
.when( .when(
self.delegate.separators_after_indices().contains(&ix), self.delegate.separators_after_indices().contains(&ix),
@ -313,12 +327,12 @@ impl<D: PickerDelegate> Picker<D> {
} }
fn do_confirm(&mut self, secondary: bool, cx: &mut ViewContext<Self>) { fn do_confirm(&mut self, secondary: bool, cx: &mut ViewContext<Self>) {
// if let Some(update_query) = self.delegate.confirm_update_query(cx) { if let Some(update_query) = self.delegate.confirm_update_query(cx) {
// self.set_query(update_query, cx); self.set_query(&update_query, cx);
// self.delegate.set_selected_index(0, cx); self.delegate.set_selected_index(0, cx);
// } else { } else {
self.delegate.confirm(secondary, cx) self.delegate.confirm(secondary, cx)
// } }
} }
pub fn set_selected_index( pub fn set_selected_index(
@ -349,16 +363,7 @@ impl<D: PickerDelegate> Picker<D> {
} }
fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) { fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
if self.pending_update_matches.is_some() self.do_confirm(false, cx);
&& !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);
}
} }
pub fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) { pub fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
@ -405,13 +410,6 @@ impl<D: PickerDelegate> Picker<D> {
} }
} }
fn use_selected_query(&mut self, _: &UseSelectedQuery, cx: &mut ViewContext<Self>) {
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<Self>) { pub fn refresh(&mut self, cx: &mut ViewContext<Self>) {
let query = self.query(cx); let query = self.query(cx);
self.update_matches(&query, cx); self.update_matches(&query, cx);
@ -478,9 +476,12 @@ impl<D: PickerDelegate> Picker<D> {
impl<D: PickerDelegate> Render for Picker<D> { impl<D: PickerDelegate> Render for Picker<D> {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let focus_handle = self.focus_handle(cx);
v_flex() v_flex()
.key_context("Picker") .key_context("Picker")
.size_full() .size_full()
.track_focus(&focus_handle)
.when_some(self.width, |el, width| el.w(width)) .when_some(self.width, |el, width| el.w(width))
.overflow_hidden() .overflow_hidden()
.when(self.is_modal, |this| this.elevation_3(cx)) .when(self.is_modal, |this| this.elevation_3(cx))
@ -490,9 +491,6 @@ impl<D: PickerDelegate> Render for Picker<D> {
.on_action(cx.listener(Self::select_last)) .on_action(cx.listener(Self::select_last))
.on_action(cx.listener(Self::cancel)) .on_action(cx.listener(Self::cancel))
.on_action(cx.listener(Self::confirm)) .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 // Render Query Input header
.child(match self.query_input { .child(match self.query_input {
Some(ref input) => self.delegate.render_query(input, cx), Some(ref input) => self.delegate.render_query(input, cx),
@ -504,7 +502,6 @@ impl<D: PickerDelegate> Render for Picker<D> {
.flex_grow() .flex_grow()
.when_some(self.max_height, |div, max_h| div.max_h(max_h)) .when_some(self.max_height, |div, max_h| div.max_h(max_h))
.overflow_hidden() .overflow_hidden()
// .children(self.delegate.render_header(cx))
.child(self.render_element_container(cx)), .child(self.render_element_container(cx)),
) )
}) })