use ratatui::text::Line;
use super::*;
pub fn confirm<G>(msg: impl AsRef<str>, over: &impl State, ctx: &mut Context<G>) -> bool {
Confirm{ msg: msg.as_ref() }.run_over(over, ctx)
}
pub fn select<'a, T, U, G>(msg: impl AsRef<str>, actions: T, over: &impl State, ctx: &mut Context<G>)
-> usize
where
T: AsRef<[U]>,
U: AsRef<str>,
{
Select{ msg: msg.as_ref(), actions: actions.as_ref(), selected: 0 }.run_over(over, ctx)
}
pub fn info<G>(msg: impl AsRef<str>, over: &impl State, ctx: &mut Context<G>) {
message(msg.as_ref(), MessageLevel::Info, over, ctx)
}
pub fn warning<G>(msg: impl AsRef<str>, over: &impl State, ctx: &mut Context<G>) {
message(msg.as_ref(), MessageLevel::Warning, over, ctx)
}
pub fn error<G>(msg: impl AsRef<str>, over: &impl State, ctx: &mut Context<G>) {
message(msg.as_ref(), MessageLevel::Error, over, ctx)
}
pub fn fatal<G>(msg: impl AsRef<str>, ctx: &mut Context<G>) {
message(msg.as_ref(), MessageLevel::Fatal, &(), ctx)
}
fn message<G>(msg: &str, level: MessageLevel, over: &impl State, ctx: &mut Context<G>) {
Message{ msg, level }.run_over(over, ctx)
}
struct Confirm<'a> {
msg: &'a str,
}
impl Dialog for Confirm<'_> {
type Out = bool;
fn format(&self) -> DrawInfo {
DrawInfo {
title: "Confirm".into(),
color: Color::Yellow,
body: self.msg.into(),
hint: "Press (y) to confirm, (n) or (esc) to cancel...".into(),
..Default::default()
}
}
fn input(self, key: KeyEvent) -> Signal<Self> {
match key.code {
KeyCode::Char('y') |
KeyCode::Char('Y') => Signal::Return(true),
KeyCode::Esc |
KeyCode::Char('n') |
KeyCode::Char('N') => Signal::Return(false),
_ => Signal::Continue(self),
}
}
}
struct Select<'a, T> {
msg: &'a str,
actions: &'a [T],
selected: usize,
}
impl<'a, T: AsRef<str>> Dialog for Select<'a, T> {
type Out = usize;
fn format(&self) -> DrawInfo {
let format_action = |(i, action)| {
let prefix = match i == self.selected {
true => '→',
false => ' ',
};
format!("{prefix} {action}").into()
};
let actions = self.actions
.iter()
.map(AsRef::as_ref)
.enumerate()
.map(format_action);
let body: Vec<Line> = std::iter::once(self.msg.into())
.chain(actions)
.collect();
DrawInfo {
title: "Select".into(),
color: Color::Cyan,
body: body.into(),
hint: "Press (enter) to select action, (esc) to cancel...".into(),
wrap: Some(Wrap{ trim: false }),
..Default::default()
}
}
fn input(mut self, key: KeyEvent) -> Signal<Self> {
match key.code {
KeyCode::Up => {
self.selected = self.selected.saturating_sub(1);
}
KeyCode::Down => {
self.selected = usize::min(self.selected + 1, self.actions.len() - 1);
}
KeyCode::Enter => return Signal::Return(self.selected),
_ => (),
};
Signal::Continue(self)
}
}
enum MessageLevel {
Info,
Warning,
Error,
Fatal,
}
struct Message<'a> {
msg: &'a str,
level: MessageLevel,
}
impl Dialog for Message<'_> {
type Out = ();
fn format(&self) -> DrawInfo {
let (title, color) = match self.level {
MessageLevel::Info => ("Info", Color::Cyan),
MessageLevel::Warning => ("Warning", Color::Yellow),
MessageLevel::Error => ("Error", Color::Red),
MessageLevel::Fatal => ("Fatal error", Color::Red),
};
DrawInfo {
title: title.into(),
color,
body: self.msg.into(),
hint: "Press any key to close...".into(),
..Default::default()
}
}
fn input(self, _key: KeyEvent) -> Signal<Self> {
Signal::Return(())
}
}