134 lines
3.6 KiB
Rust
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);
|
|
}
|
|
}
|