use crate::render::Cell;
use crate::style::Color;
use crate::utils::truncate_to_width;
use crate::widget::theme::PLACEHOLDER_FG;
use crate::widget::traits::{RenderContext, View};
use super::super::dropdown::{
calculate_dropdown_layout, dropdown_height, queue_or_inline_overlay, render_options,
render_status_row, DropdownColors, DropdownOption,
};
use super::Select;
impl View for Select {
fn render(&self, ctx: &mut RenderContext) {
let area = ctx.area;
if area.width < 3 || area.height < 1 {
return;
}
let width = self.display_width(area.width);
let text_width = (width - 2) as usize;
let fg = if self.disabled {
Some(PLACEHOLDER_FG)
} else if self.focused {
self.fg.or(Some(Color::CYAN))
} else {
self.fg
};
let bg = self.bg;
let display_text = if self.open && self.searchable && !self.query.is_empty() {
&self.query
} else {
self.value().unwrap_or(&self.placeholder)
};
let arrow = if self.open { "▲" } else { "▼" };
ctx.fill_row(0, width, fg, bg);
if self.focused && !self.disabled {
ctx.draw_focus_brackets(0, width, Color::CYAN);
}
let icon = if self.open && self.searchable {
"🔍".chars().next().unwrap_or('?')
} else {
arrow.chars().next().unwrap_or('▼')
};
let mut cell = Cell::new(icon);
cell.fg = fg;
cell.bg = bg;
ctx.set(0, 0, cell);
let truncated = truncate_to_width(display_text, text_width);
let mut cx: u16 = 2;
for ch in truncated.chars() {
let mut cell = Cell::new(ch);
cell.fg = fg;
cell.bg = bg;
ctx.set(cx, 0, cell);
cx += crate::utils::char_width(ch) as u16;
}
if !self.open {
return;
}
let visible_options: Vec<(usize, &String)> = if self.query.is_empty() {
self.options.iter().enumerate().collect()
} else {
self.filtered
.iter()
.filter_map(|&i| self.options.get(i).map(|opt| (i, opt)))
.collect()
};
let dropdown_h = dropdown_height(visible_options.len(), None);
let layout = calculate_dropdown_layout(ctx, dropdown_h);
let (abs_x, _) = ctx.absolute_position();
let overlay_area = crate::layout::Rect::new(abs_x, layout.overlay_y, width, dropdown_h);
let mut entry = crate::widget::traits::OverlayEntry::new(100, overlay_area);
if visible_options.is_empty() && !self.query.is_empty() {
render_status_row(
&mut entry,
"No results",
width,
self.fg,
self.bg,
Some(PLACEHOLDER_FG),
);
queue_or_inline_overlay(ctx, entry);
return;
}
let colors = DropdownColors {
fg: self.fg,
bg: self.bg,
selected_fg: self.selected_fg,
selected_bg: self.selected_bg,
highlight_fg: self.highlight_fg,
disabled_fg: None,
};
let dropdown_options: Vec<DropdownOption<'_>> = visible_options
.iter()
.take(dropdown_h as usize)
.map(|(option_idx, option)| {
let is_selected = self.selection.is_selected(*option_idx);
let indicator = if is_selected { '›' } else { ' ' };
let match_indices: std::collections::HashSet<usize> = self
.get_match(option)
.map(|m| m.indices.into_iter().collect())
.unwrap_or_default();
DropdownOption {
label: option.as_str(),
is_highlighted: is_selected,
is_disabled: false,
match_indices,
indicator,
}
})
.collect();
render_options(&mut entry, &dropdown_options, width, &colors);
queue_or_inline_overlay(ctx, entry);
}
crate::impl_view_meta!("Select");
}