use super::{Window, WndId}; use crate::tui::{Frame, GEvent, RetVal, ReturnValue, GENERAL_EVENT_CHANNEL}; use anyhow::Result as AResult; use crossterm::event::KeyCode; use std::any::Any; use tui::layout::{Alignment, Constraint, Direction, Layout, Rect}; use tui::style::{Modifier, Style}; use tui::widgets::{Block, Clear, List, ListItem, ListState, Paragraph}; #[derive(Default)] pub struct ConfirmWnd { pub id: WndId, msg: String, variants: Vec<String>, state: ListState, } impl ConfirmWnd { pub fn new_yn(msg: impl Into<String>, variants: Option<Vec<String>>) -> Self { let default = vec!["Yes".to_string(), "No".to_string()]; let variants = match variants { Some(v) if !v.is_empty() => v, _ => default, }; let mut this = Self { msg: msg.into(), variants, ..Default::default() }; this.state.select(Some(0)); this } pub fn on_right(&mut self) { let selected = self.state.selected().unwrap_or(0); self.state .select(Some((selected + 1).rem_euclid(self.variants.len()))); } pub fn on_left(&mut self) { let selected = self.state.selected().unwrap_or(0); let vars_len = self.variants.len(); self.state .select(Some((selected + vars_len - 1).rem_euclid(vars_len))); } } impl RetVal for ConfirmWnd { fn retval(&self) -> Box<dyn Any + Send> { let value = self .variants .get(self.state.selected().unwrap()) .unwrap() .to_owned(); Box::new(value) } } #[async_trait] impl Window for ConfirmWnd { async fn handle_kbd(&mut self, k: KeyCode) -> AResult<()> { match k { KeyCode::Right => self.on_right(), KeyCode::Left => self.on_left(), KeyCode::Enter | KeyCode::Esc => self.close(false).await, _ => (), } Ok(()) } async fn handle_update(&mut self) -> AResult<()> { Ok(()) } async fn handle_close(&mut self) -> bool { true } fn draw(&mut self, f: &mut Frame) { let size = f.size(); let rect = centered_rect(60, 40, size); f.render_widget(Clear, rect); let chunks = Layout::default() .direction(Direction::Vertical) .constraints(vec![Constraint::Percentage(70), Constraint::Percentage(30)]) .split(rect); let msg = Paragraph::new(self.msg.as_ref()); f.render_widget(msg, chunks[0]); let options = self .variants .iter() .map(AsRef::as_ref) .map(ListItem::new) .collect::<Vec<ListItem>>(); let list = List::new(options).highlight_style(Style::default().add_modifier(Modifier::BOLD)); f.render_stateful_widget(list, chunks[1], &mut self.state); } } pub async fn confirm_wnd(msg: impl Into<String>) -> bool { let wnd = ConfirmWnd::new_yn(msg.into(), None); GENERAL_EVENT_CHANNEL.send(GEvent::CreateWnd(wnd.into_shared())); *ReturnValue::get().await } fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect { let popup_layout = Layout::default() .direction(Direction::Vertical) .constraints( [ Constraint::Percentage((100 - percent_y) / 2), Constraint::Percentage(percent_y), Constraint::Percentage((100 - percent_y) / 2), ] .as_ref(), ) .split(r); Layout::default() .direction(Direction::Horizontal) .constraints( [ Constraint::Percentage((100 - percent_x) / 2), Constraint::Percentage(percent_x), Constraint::Percentage((100 - percent_x) / 2), ] .as_ref(), ) .split(popup_layout[1])[1] }