use std::mem;
use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};
use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, Paragraph};
use ratatui::Frame;
use super::app::{AppScreen, ScreenOutcome};
use super::main_menu::MainMenuState;
#[derive(Debug)]
pub(super) struct PlaceholderState {
pub(super) name: &'static str,
pub(super) prev: MainMenuState,
}
impl PlaceholderState {
pub(super) fn new(name: &'static str, prev: MainMenuState) -> Self {
Self { name, prev }
}
}
pub(super) fn update(state: &mut PlaceholderState, key: KeyEvent) -> ScreenOutcome {
if key.modifiers == KeyModifiers::NONE && key.code == KeyCode::Esc {
let prev = mem::take(&mut state.prev);
return ScreenOutcome::NavigateTo(AppScreen::MainMenu(prev));
}
ScreenOutcome::Stay
}
pub(super) fn view(state: &PlaceholderState, frame: &mut Frame, area: Rect) {
let block = Block::default().borders(Borders::ALL).title(Span::styled(
format!(" {} ", state.name),
Style::default().add_modifier(Modifier::BOLD),
));
let inner = block.inner(area);
frame.render_widget(block, area);
let rows = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Percentage(40),
Constraint::Length(1),
Constraint::Length(1),
Constraint::Length(1),
Constraint::Min(0),
])
.split(inner);
let body = Paragraph::new(Line::from("Coming soon.")).alignment(Alignment::Center);
frame.render_widget(body, rows[1]);
let hint = Paragraph::new(Line::from(vec![
Span::raw("Press "),
Span::styled("Esc", Style::default().add_modifier(Modifier::BOLD)),
Span::raw(" to return to the main menu, or "),
Span::styled("q", Style::default().add_modifier(Modifier::BOLD)),
Span::raw(" to quit."),
]))
.alignment(Alignment::Center);
frame.render_widget(hint, rows[3]);
}
#[cfg(test)]
mod tests {
use super::*;
fn key(code: KeyCode) -> KeyEvent {
KeyEvent::new(code, KeyModifiers::NONE)
}
fn key_mod(code: KeyCode, mods: KeyModifiers) -> KeyEvent {
KeyEvent::new(code, mods)
}
#[test]
fn esc_returns_navigate_to_main_menu() {
let mut state = PlaceholderState::new("Edit Lines", MainMenuState::default());
let outcome = update(&mut state, key(KeyCode::Esc));
assert!(matches!(
outcome,
ScreenOutcome::NavigateTo(AppScreen::MainMenu(_))
));
}
#[test]
fn other_keys_stay_on_placeholder() {
let mut state = PlaceholderState::new("Edit Lines", MainMenuState::default());
for code in [
KeyCode::Enter,
KeyCode::Up,
KeyCode::Down,
KeyCode::Char('a'),
] {
let outcome = update(&mut state, key(code));
assert!(
matches!(outcome, ScreenOutcome::Stay),
"key {code:?} should not navigate",
);
}
}
fn render_to_string(state: &PlaceholderState, width: u16, height: u16) -> String {
use ratatui::backend::TestBackend;
use ratatui::Terminal;
let backend = TestBackend::new(width, height);
let mut terminal = Terminal::new(backend).expect("backend");
terminal
.draw(|frame| view(state, frame, frame.area()))
.expect("draw");
crate::tui::buffer_to_string(terminal.backend().buffer())
}
#[test]
fn snapshot_placeholder_renders_centered_panel() {
let state = PlaceholderState::new("Edit Colors", MainMenuState::default());
insta::assert_snapshot!("placeholder_canonical", render_to_string(&state, 60, 14));
}
#[test]
fn esc_with_modifier_does_not_back_navigate() {
let mut state = PlaceholderState::new("Edit Lines", MainMenuState::default());
let outcome = update(&mut state, key_mod(KeyCode::Esc, KeyModifiers::SHIFT));
assert!(matches!(outcome, ScreenOutcome::Stay));
}
}