use ratatui::{
prelude::{Alignment, Buffer, Rect, Widget},
text::{Line, Span},
widgets::{Block, Borders, Clear, List, StatefulWidget},
};
use crossterm::event::{MouseButton, MouseEventKind};
use smallvec::smallvec;
use crate::app::{Action, MouseArea};
use crate::config::Config;
use crate::object_list::ObjectList;
pub struct DropdownWidget<'a> {
object_list: &'a mut ObjectList,
dropdown_area: &'a Rect,
config: &'a Config,
}
impl<'a> DropdownWidget<'a> {
pub fn new(
object_list: &'a mut ObjectList,
dropdown_area: &'a Rect,
config: &'a Config,
) -> Self {
Self {
object_list,
dropdown_area,
config,
}
}
}
impl StatefulWidget for DropdownWidget<'_> {
type State = Vec<MouseArea>;
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
let mouse_areas = state;
let targets: Vec<_> = self
.object_list
.targets
.iter()
.map(|(_, title)| title.clone())
.collect();
let dropdown_area = self.dropdown_area.clamp(area);
mouse_areas.push((
area,
smallvec![MouseEventKind::Down(MouseButton::Left)],
smallvec![Action::CloseDropdown],
));
mouse_areas.push((
dropdown_area,
smallvec![MouseEventKind::Down(MouseButton::Left)],
smallvec![],
));
Clear.render(dropdown_area, buf);
let highlight_symbol =
format!("{} ", self.config.char_set.dropdown_selector);
let list = List::new(targets)
.block(
Block::default()
.borders(Borders::ALL)
.border_style(self.config.theme.dropdown_border)
.border_type(self.config.char_set.dropdown_border),
)
.style(self.config.theme.dropdown_item)
.highlight_symbol(&highlight_symbol)
.highlight_style(self.config.theme.dropdown_selected);
StatefulWidget::render(
&list,
dropdown_area,
buf,
&mut self.object_list.dropdown_state,
);
let first_index = self.object_list.dropdown_state.offset();
if first_index > 0 {
let top_area = Rect::new(
dropdown_area.x,
dropdown_area.y,
dropdown_area.width,
1,
);
Line::from(Span::styled(
&self.config.char_set.dropdown_more,
self.config.theme.dropdown_more,
))
.alignment(Alignment::Center)
.render(top_area, buf);
mouse_areas.push((
top_area,
smallvec![MouseEventKind::Down(MouseButton::Left)],
smallvec![Action::MoveUp],
));
}
let dropdown_area_inner_height =
(dropdown_area.height as usize).saturating_sub(2);
let last_index = first_index.saturating_add(dropdown_area_inner_height);
if last_index < self.object_list.targets.len() {
let y = dropdown_area
.y
.saturating_add(dropdown_area.height.saturating_sub(1));
let bottom_area =
Rect::new(dropdown_area.x, y, dropdown_area.width, 1);
Line::from(Span::styled(
&self.config.char_set.dropdown_more,
self.config.theme.dropdown_more,
))
.alignment(Alignment::Center)
.render(bottom_area, buf);
mouse_areas.push((
bottom_area,
smallvec![MouseEventKind::Down(MouseButton::Left)],
smallvec![Action::MoveDown],
));
}
for i in 0..(dropdown_area.height - 2) {
let target_area = Rect::new(
dropdown_area.x,
dropdown_area.y.saturating_add(1).saturating_add(i),
dropdown_area.width,
1,
);
let target = self
.object_list
.targets
.iter()
.skip(first_index)
.nth(i as usize)
.map(|(target, _)| target);
if let Some(target) = target {
mouse_areas.push((
target_area,
smallvec![MouseEventKind::Down(MouseButton::Left)],
smallvec![Action::SetTarget(*target)],
));
}
}
}
}