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