Files
fomod-manager/src/tui/app.rs

134 lines
3.6 KiB
Rust

use std::io;
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
use log::error;
use ratatui::{
DefaultTerminal, Frame,
layout::{Constraint, Direction, Layout, Rect},
widgets::{StatefulWidget, TableState, Widget},
};
use crate::{
tui::{
instance::{InstanceSelect, InstanceSelectState},
mod_list::ModList,
status::StatusBar,
},
types::{ModdedInstance, RootConfig},
};
pub fn run(root_config: &mut RootConfig) -> anyhow::Result<()> {
ratatui::run(|terminal| App::new(root_config).run(terminal))?;
Ok(())
}
#[derive(Debug)]
struct App<'a> {
root_config: &'a mut RootConfig,
loaded_instance: Option<ModdedInstance>,
exit: bool,
mod_list_state: TableState,
selected_instance_state: InstanceSelectState,
}
impl<'a> App<'a> {
fn new(root_config: &'a mut RootConfig) -> Self {
Self {
root_config,
loaded_instance: None,
exit: false,
mod_list_state: TableState::default(),
selected_instance_state: InstanceSelectState::new(),
}
}
pub fn run(&mut self, terminal: &mut DefaultTerminal) -> io::Result<()> {
terminal.clear()?;
while !self.exit {
terminal.draw(|frame| self.draw(frame))?;
self.handle_events()?;
}
Ok(())
}
fn draw(&mut self, frame: &mut Frame) {
frame.render_widget(self, frame.area());
}
fn handle_events(&mut self) -> io::Result<()> {
match event::read()? {
Event::Key(key_event) if key_event.kind == KeyEventKind::Press => {
self.handle_key_event(key_event)
}
_ => {}
};
Ok(())
}
fn handle_key_event(&mut self, key_event: KeyEvent) {
match key_event.code {
KeyCode::Esc | KeyCode::Char('q') => self.exit(),
KeyCode::Up | KeyCode::Char('k') => {
self.mod_list_state.select_previous();
}
KeyCode::Down | KeyCode::Char('j') => {
self.mod_list_state.select_next();
}
KeyCode::Right | KeyCode::Char('l') => {
self.selected_instance_state.next_instance(self.root_config);
self.load_instance();
}
KeyCode::Left | KeyCode::Char('h') => {
self.selected_instance_state.prev_instance(self.root_config);
self.load_instance();
}
_ => {}
}
}
fn exit(&mut self) {
self.exit = true;
}
fn load_instance(&mut self) {
let Some(selected) = self.selected_instance_state.instance() else {
return;
};
match self.root_config.load_instance_by_id(selected) {
Ok(instance) => {
self.loaded_instance = Some(instance);
}
Err(err) => {
error!("Failed to load instance: {err}");
self.exit();
}
}
}
}
impl<'a> Widget for &mut App<'a> {
fn render(self, area: Rect, buf: &mut ratatui::prelude::Buffer)
where
Self: Sized,
{
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(3),
Constraint::Min(1),
Constraint::Length(1),
])
.split(area);
InstanceSelect {}.render(chunks[0], buf, &mut self.selected_instance_state);
ModList::new(self.root_config).render(chunks[1], buf, &mut self.mod_list_state);
StatusBar.render(chunks[2], buf);
}
}