Compare commits

...

2 Commits

Author SHA1 Message Date
2322cd00d2 added selectable instance to tui 2026-03-27 12:16:08 +01:00
d746e830fd added instances function to root_config 2026-03-27 12:15:38 +01:00
5 changed files with 127 additions and 3 deletions

View File

@@ -1,4 +1,6 @@
mod app;
mod mod_list;
mod status;
mod instance;
pub use app::run;

View File

@@ -3,12 +3,15 @@ use std::io;
use crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind};
use ratatui::{
DefaultTerminal, Frame,
layout::Rect,
layout::{Constraint, Direction, Layout, Rect},
style::Style,
widgets::{Block, Borders, StatefulWidget, TableState, Widget},
};
use crate::{tui::mod_list::ModList, types::RootConfig};
use crate::{
tui::{instance::InstanceSelect, mod_list::ModList, status::StatusBar},
types::RootConfig,
};
pub fn run(root_config: &mut RootConfig) -> anyhow::Result<()> {
ratatui::run(|terminal| App::new(root_config).run(terminal))?;
@@ -21,6 +24,7 @@ struct App<'a> {
exit: bool,
mod_list_state: TableState,
selected_instance: Option<String>,
}
impl<'a> App<'a> {
@@ -31,6 +35,7 @@ impl<'a> App<'a> {
Self {
root_config,
mod_list_state: state,
selected_instance: None,
exit: false,
}
}
@@ -67,6 +72,12 @@ impl<'a> App<'a> {
KeyCode::Down | KeyCode::Char('j') => {
self.mod_list_state.select_next();
}
KeyCode::Right | KeyCode::Char('l') => {
self.next_instance();
}
KeyCode::Left | KeyCode::Char('h') => {
self.prev_instance();
}
_ => {}
}
}
@@ -74,6 +85,58 @@ impl<'a> App<'a> {
fn exit(&mut self) {
self.exit = true;
}
fn next_instance(&mut self) {
let mut instances = self.root_config.instances();
instances.sort();
if instances.is_empty() {
self.selected_instance = None;
return;
}
let next = match &self.selected_instance {
None => instances.first().cloned(),
Some(curr) => {
let idx = instances.iter().position(|x| x == curr);
match idx {
Some(i) => {
let next_index = (i + 1) % instances.len();
instances.get(next_index).cloned()
}
None => instances.first().cloned(),
}
}
};
self.selected_instance = next;
}
fn prev_instance(&mut self) {
let mut instances = self.root_config.instances();
instances.sort();
if instances.is_empty() {
self.selected_instance = None;
return;
}
let prev = match &self.selected_instance {
None => instances.last().cloned(),
Some(curr) => {
let idx = instances.iter().position(|x| x == curr);
match idx {
Some(i) => {
let prev_index = if i == 0 { instances.len() - 1 } else { i - 1 };
instances.get(prev_index).cloned()
}
None => Some(instances[instances.len() - 1].clone()),
}
}
};
self.selected_instance = prev;
}
}
impl<'a> Widget for &mut App<'a> {
@@ -81,15 +144,28 @@ impl<'a> Widget for &mut App<'a> {
where
Self: Sized,
{
let chunks = Layout::default()
.direction(Direction::Vertical)
.constraints([
Constraint::Length(3),
Constraint::Min(1),
Constraint::Length(1), // single line for keybindings
])
.split(area);
InstanceSelect::new(self.selected_instance.clone()).render(chunks[0], buf);
let list_block = Block::default()
.title("Mod list")
.borders(Borders::ALL)
.style(Style::default());
ModList::new(self.root_config).block(list_block).render(
area,
chunks[1],
buf,
&mut self.mod_list_state,
);
StatusBar.render(chunks[2], buf);
}
}

30
src/tui/instance.rs Normal file
View File

@@ -0,0 +1,30 @@
use ratatui::{
style::Style,
widgets::{Block, Borders, Paragraph, Widget},
};
pub struct InstanceSelect {
selected: Option<String>,
}
impl InstanceSelect {
pub fn new(selected: Option<String>) -> Self {
Self { selected }
}
}
impl Widget for InstanceSelect {
fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer)
where
Self: Sized,
{
let list_block = Block::default()
.title("Instance")
.borders(Borders::ALL)
.style(Style::default());
Paragraph::new(self.selected.unwrap_or("None".to_owned()))
.block(list_block)
.render(area, buf);
}
}

12
src/tui/status.rs Normal file
View File

@@ -0,0 +1,12 @@
use ratatui::{text::Line, widgets::Widget};
pub struct StatusBar;
impl Widget for StatusBar {
fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer)
where
Self: Sized {
Line::from("Up Down Left right").render(area, buf);
}
}

View File

@@ -95,6 +95,10 @@ impl RootConfig {
self.mods.insert(new_mod.id().to_owned(), new_mod.clone());
}
pub fn instances(&self) -> Vec<String> {
self.instances.keys().cloned().collect()
}
pub fn load_instance_by_id(&self, id: &str) -> Result<ModdedInstance, ConfigReadWriteError> {
debug!("Loading instance {}", id);
let conf = self