mod filter;
mod input;
mod render;
mod state;
use crate::style::Color;
use crate::utils::{display_width, Selection};
use crate::widget::traits::WidgetProps;
use crate::{impl_props_builders, impl_styled_view};
#[derive(Clone, Debug)]
pub struct Select {
pub(crate) options: Vec<String>,
pub(crate) selection: Selection,
pub(crate) open: bool,
pub(crate) placeholder: String,
pub(crate) fg: Option<Color>,
pub(crate) bg: Option<Color>,
pub(crate) selected_fg: Option<Color>,
pub(crate) selected_bg: Option<Color>,
pub(crate) highlight_fg: Option<Color>,
pub(crate) width: Option<u16>,
pub(crate) cached_auto_width: Option<u16>,
pub(crate) query: String,
pub(crate) searchable: bool,
pub(crate) filtered: Vec<usize>,
pub(crate) filtered_selection: Selection,
pub(crate) focused: bool,
pub(crate) disabled: bool,
pub(crate) props: WidgetProps,
}
impl Select {
pub fn new() -> Self {
Self {
options: Vec::new(),
selection: Selection::new(0),
open: false,
placeholder: "Select...".to_string(),
fg: None,
bg: None,
selected_fg: Some(Color::WHITE),
selected_bg: Some(Color::BLUE),
highlight_fg: Some(Color::YELLOW),
width: None,
cached_auto_width: None,
query: String::new(),
searchable: false,
filtered: Vec::new(),
filtered_selection: Selection::new(0),
focused: false,
disabled: false,
props: WidgetProps::new(),
}
}
pub fn options(mut self, options: Vec<impl Into<String>>) -> Self {
self.options = options.into_iter().map(|o| o.into()).collect();
self.selection.set_len(self.options.len());
self.reset_filter();
self.update_cached_width();
self
}
pub fn option(mut self, option: impl Into<String>) -> Self {
self.options.push(option.into());
self.selection.set_len(self.options.len());
self.cached_auto_width = None; self.reset_filter();
self
}
pub fn searchable(mut self, enable: bool) -> Self {
self.searchable = enable;
self
}
pub fn highlight_fg(mut self, color: Color) -> Self {
self.highlight_fg = Some(color);
self
}
pub fn selected(mut self, index: usize) -> Self {
let index = index.min(self.options.len().saturating_sub(1));
self.selection.set(index);
self
}
pub fn placeholder(mut self, text: impl Into<String>) -> Self {
self.placeholder = text.into();
self
}
pub fn fg(mut self, color: Color) -> Self {
self.fg = Some(color);
self
}
pub fn bg(mut self, color: Color) -> Self {
self.bg = Some(color);
self
}
pub fn selected_style(mut self, fg: Color, bg: Color) -> Self {
self.selected_fg = Some(fg);
self.selected_bg = Some(bg);
self
}
pub fn width(mut self, width: u16) -> Self {
self.width = Some(width);
self
}
pub fn focused(mut self, focused: bool) -> Self {
self.focused = focused;
if !focused {
self.open = false;
self.clear_query();
}
self
}
pub fn disabled(mut self, disabled: bool) -> Self {
self.disabled = disabled;
self
}
pub(crate) fn display_width(&self, max_width: u16) -> u16 {
if let Some(w) = self.width {
return w.min(max_width);
}
if let Some(cached) = self.cached_auto_width {
return cached.min(max_width);
}
let max_option_len = self
.options
.iter()
.map(|o| display_width(o))
.max()
.unwrap_or(display_width(&self.placeholder));
((max_option_len + 4) as u16).min(max_width)
}
fn update_cached_width(&mut self) {
if self.width.is_some() {
return;
}
let max_option_len = self
.options
.iter()
.map(|o| display_width(o))
.max()
.unwrap_or(display_width(&self.placeholder));
self.cached_auto_width = Some((max_option_len + 4) as u16);
}
}
impl Default for Select {
fn default() -> Self {
Self::new()
}
}
impl_styled_view!(Select);
impl_props_builders!(Select);
pub fn select() -> Select {
Select::new()
}