You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

133 lines
3.8 KiB

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]
}