use anyhow::Result;
use crossterm::{
cursor,
event::{self, Event, KeyCode, KeyEventKind},
execute, queue,
style::{self, Color},
terminal::{self, ClearType},
};
use std::io::{self, Write};
#[allow(dead_code)]
pub fn show_help(terminal_size: (u16, u16)) -> Result<()> {
let (cols, rows) = terminal_size;
let mut help_lines =
vec!["═══════════════════════ WEDI HELP ════════════════════════".to_string()];
help_lines.push(String::new());
help_lines.extend(crate::help::get_keyboard_shortcuts());
help_lines.push(String::new());
help_lines.push("Press ESC to close this help".to_string());
help_lines.push("═══════════════════════════════════════════════════════════".to_string());
let total_lines = help_lines.len();
let max_display_lines = (rows.saturating_sub(3)) as usize;
let mut scroll_offset = 0;
loop {
execute!(io::stdout(), terminal::Clear(ClearType::All))?;
let visible_lines = total_lines.min(max_display_lines);
let end_line = (scroll_offset + visible_lines).min(total_lines);
for (i, line) in help_lines[scroll_offset..end_line].iter().enumerate() {
queue!(
io::stdout(),
cursor::MoveTo(0, i as u16),
style::SetForegroundColor(Color::Cyan),
)?;
let display_line = if line.chars().count() > cols as usize {
line.chars().take(cols as usize).collect::<String>()
} else {
line.to_string()
};
queue!(io::stdout(), style::Print(display_line))?;
}
queue!(io::stdout(), style::ResetColor)?;
if total_lines > max_display_lines {
let status_row = rows.saturating_sub(1);
queue!(
io::stdout(),
cursor::MoveTo(0, status_row),
style::SetBackgroundColor(Color::DarkBlue),
style::SetForegroundColor(Color::White),
)?;
let scroll_info = format!(
" Lines {}-{}/{} | Use ↑/↓ or PgUp/PgDn to scroll | ESC to close ",
scroll_offset + 1,
end_line,
total_lines
);
let padded = if scroll_info.len() < cols as usize {
format!(
"{}{}",
scroll_info,
" ".repeat(cols as usize - scroll_info.len())
)
} else {
scroll_info.chars().take(cols as usize).collect()
};
queue!(io::stdout(), style::Print(padded), style::ResetColor)?;
}
io::stdout().flush()?;
loop {
if let Event::Key(key_event) = event::read()? {
if key_event.kind != KeyEventKind::Press {
continue;
}
match key_event.code {
KeyCode::Esc => return Ok(()),
KeyCode::Up => {
scroll_offset = scroll_offset.saturating_sub(1);
break;
}
KeyCode::Down => {
if scroll_offset + max_display_lines < total_lines {
scroll_offset += 1;
}
break;
}
KeyCode::PageUp => {
scroll_offset = scroll_offset.saturating_sub(max_display_lines / 2);
break;
}
KeyCode::PageDown => {
scroll_offset = (scroll_offset + max_display_lines / 2)
.min(total_lines.saturating_sub(max_display_lines));
break;
}
KeyCode::Home => {
scroll_offset = 0;
break;
}
KeyCode::End => {
scroll_offset = total_lines.saturating_sub(max_display_lines);
break;
}
_ => break,
}
}
}
}
}
#[allow(dead_code)]
pub fn prompt(prompt_text: &str, terminal_size: (u16, u16)) -> Result<Option<String>> {
prompt_with_default(prompt_text, "", terminal_size)
}
#[allow(dead_code)]
pub fn prompt_with_default(
prompt_text: &str,
default: &str,
terminal_size: (u16, u16),
) -> Result<Option<String>> {
let mut input = default.to_string();
let mut cursor_pos = input.chars().count(); let (cols, rows) = terminal_size;
let dialog_row = rows.saturating_sub(2);
loop {
execute!(
io::stdout(),
cursor::MoveTo(0, dialog_row),
terminal::Clear(ClearType::CurrentLine)
)?;
queue!(
io::stdout(),
style::SetBackgroundColor(Color::DarkBlue),
style::SetForegroundColor(Color::White),
cursor::MoveTo(0, dialog_row),
)?;
let display = format!(" {} {}", prompt_text, input);
let display = if display.len() > cols as usize {
&display[..cols as usize]
} else {
&display
};
queue!(io::stdout(), style::Print(display))?;
let remaining = cols as usize - display.len();
if remaining > 0 {
queue!(io::stdout(), style::Print(" ".repeat(remaining)))?;
}
queue!(io::stdout(), style::ResetColor)?;
let cursor_x = (prompt_text.len() + 2 + cursor_pos).min(cols as usize - 1) as u16;
execute!(io::stdout(), cursor::MoveTo(cursor_x, dialog_row))?;
execute!(io::stdout(), cursor::Show)?;
io::stdout().flush()?;
loop {
if let Event::Key(key_event) = event::read()? {
if key_event.kind != KeyEventKind::Press && key_event.kind != KeyEventKind::Repeat {
continue;
}
match key_event.code {
KeyCode::Enter => {
return Ok(Some(input));
}
KeyCode::Esc => {
return Ok(None);
}
KeyCode::Char(c) => {
let byte_pos = input.chars().take(cursor_pos).collect::<String>().len();
input.insert(byte_pos, c);
cursor_pos += 1;
break;
}
KeyCode::Backspace => {
if cursor_pos > 0 {
let byte_pos =
input.chars().take(cursor_pos - 1).collect::<String>().len();
input.remove(byte_pos);
cursor_pos -= 1;
}
break;
}
KeyCode::Delete => {
if cursor_pos < input.chars().count() {
let byte_pos = input.chars().take(cursor_pos).collect::<String>().len();
input.remove(byte_pos);
}
break;
}
KeyCode::Left => {
cursor_pos = cursor_pos.saturating_sub(1);
break;
}
KeyCode::Right => {
if cursor_pos < input.chars().count() {
cursor_pos += 1;
}
break;
}
KeyCode::Home => {
cursor_pos = 0;
break;
}
KeyCode::End => {
cursor_pos = input.chars().count();
break;
}
_ => {
break;
}
}
}
}
}
}
#[allow(dead_code)]
pub fn confirm(message: &str, terminal_size: (u16, u16)) -> Result<bool> {
let (cols, rows) = terminal_size;
let dialog_row = rows.saturating_sub(2);
loop {
execute!(
io::stdout(),
cursor::MoveTo(0, dialog_row),
terminal::Clear(ClearType::CurrentLine)
)?;
queue!(
io::stdout(),
style::SetBackgroundColor(Color::DarkYellow),
style::SetForegroundColor(Color::Black),
cursor::MoveTo(0, dialog_row),
)?;
let display = format!(" {} (y/n)", message);
let display = if display.len() > cols as usize {
&display[..cols as usize]
} else {
&display
};
queue!(io::stdout(), style::Print(display))?;
let remaining = cols as usize - display.len();
if remaining > 0 {
queue!(io::stdout(), style::Print(" ".repeat(remaining)))?;
}
queue!(io::stdout(), style::ResetColor)?;
io::stdout().flush()?;
loop {
if let Event::Key(key_event) = event::read()? {
if key_event.kind != KeyEventKind::Press && key_event.kind != KeyEventKind::Repeat {
continue;
}
match key_event.code {
KeyCode::Char('y') | KeyCode::Char('Y') => return Ok(true),
KeyCode::Char('n') | KeyCode::Char('N') | KeyCode::Esc => return Ok(false),
_ => {
break;
}
}
}
}
}
}