use crate::core::{EntityMap, Handle};
use crate::view::uniform_list::UniformListState;
use crate::widgets::text_input::{InputChanged, TextInput};
pub trait PickerDelegate: Sized + 'static {
fn placeholder(&self) -> String;
fn match_count(&self) -> usize;
fn update_matches(&mut self, query: &str, picker: Handle<Picker<Self>>, entities: &mut EntityMap);
fn render_match(&self, index: usize, selected: bool) -> crate::view::any_element::AnyElement;
fn confirm(&mut self, index: usize, entities: &mut EntityMap);
fn row_height(&self) -> f32 {
32.0
}
fn width(&self) -> f32 {
600.0
}
fn max_list_height(&self) -> f32 {
360.0
}
fn filters(&self) -> Vec<Vec<(String, bool)>> {
Vec::new()
}
fn select_filter(&mut self, _row: usize, _index: usize) {}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PickerEvent {
Confirmed,
Dismissed,
}
pub struct Picker<D: PickerDelegate> {
pub delegate: D,
pub(crate) input: Handle<TextInput>,
pub(crate) selected: usize,
pub(crate) list: UniformListState,
}
impl<D: PickerDelegate> Picker<D> {
pub fn open(delegate: D, entities: &mut EntityMap) -> Handle<Picker<D>> {
let focus = entities.focus.new_handle();
let input = entities.insert(TextInput::new(focus).placeholder(delegate.placeholder()));
let picker = entities.insert(Picker { delegate, input, selected: 0, list: UniformListState::default() });
entities.subscribe(input, picker, |picker: &mut Picker<D>, event: &InputChanged, cx| {
let handle = cx.handle();
picker.selected = 0;
picker.list.reset();
picker.delegate.update_matches(&event.0, handle, cx);
});
entities.focus.focus(&focus);
entities.update(picker, |picker, cx| {
let handle = cx.handle();
picker.delegate.update_matches("", handle, cx);
});
picker
}
pub fn query(&self, entities: &EntityMap) -> String {
entities.read(self.input).map(|input| input.text().to_owned()).unwrap_or_default()
}
pub fn selected(&self) -> usize {
self.selected
}
pub fn select(&mut self, index: usize) {
let count = self.delegate.match_count();
if count == 0 {
self.selected = 0;
return;
}
self.selected = index.min(count - 1);
self.list.scroll_to_item(self.selected, self.delegate.row_height());
}
pub fn select_relative(&mut self, delta: isize) {
let count = self.delegate.match_count() as isize;
if count > 0 {
self.select((self.selected as isize + delta).rem_euclid(count) as usize);
}
}
pub fn apply_filter(&mut self, row: usize, index: usize, handle: Handle<Picker<D>>, entities: &mut EntityMap) {
self.delegate.select_filter(row, index);
let query = self.query(entities);
self.selected = 0;
self.list.reset();
self.delegate.update_matches(&query, handle, entities);
}
pub fn cycle_filter(&mut self, delta: isize, handle: Handle<Picker<D>>, entities: &mut EntityMap) {
let filters = self.delegate.filters();
let Some(row) = filters.len().checked_sub(1) else { return };
let count = filters[row].len() as isize;
let active = filters[row].iter().position(|(_, on)| *on).unwrap_or(0) as isize;
if count > 0 {
self.apply_filter(row, (active + delta).rem_euclid(count) as usize, handle, entities);
}
}
pub fn matches_updated(&mut self) {
let index = self.selected;
self.select(index);
}
}