From 2322cd00d21ce592238b19b57b325b15176092f1 Mon Sep 17 00:00:00 2001 From: Niklas Kapelle Date: Fri, 27 Mar 2026 12:16:08 +0100 Subject: [PATCH] added selectable instance to tui --- src/tui.rs | 2 ++ src/tui/app.rs | 82 +++++++++++++++++++++++++++++++++++++++++++-- src/tui/instance.rs | 30 +++++++++++++++++ src/tui/status.rs | 12 +++++++ 4 files changed, 123 insertions(+), 3 deletions(-) create mode 100644 src/tui/instance.rs create mode 100644 src/tui/status.rs diff --git a/src/tui.rs b/src/tui.rs index 3b67790..df567e9 100644 --- a/src/tui.rs +++ b/src/tui.rs @@ -1,4 +1,6 @@ mod app; mod mod_list; +mod status; +mod instance; pub use app::run; diff --git a/src/tui/app.rs b/src/tui/app.rs index d88b950..c87f49d 100644 --- a/src/tui/app.rs +++ b/src/tui/app.rs @@ -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, } 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); } } diff --git a/src/tui/instance.rs b/src/tui/instance.rs new file mode 100644 index 0000000..8537ba8 --- /dev/null +++ b/src/tui/instance.rs @@ -0,0 +1,30 @@ +use ratatui::{ + style::Style, + widgets::{Block, Borders, Paragraph, Widget}, +}; + +pub struct InstanceSelect { + selected: Option, +} + +impl InstanceSelect { + pub fn new(selected: Option) -> 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); + } +} diff --git a/src/tui/status.rs b/src/tui/status.rs new file mode 100644 index 0000000..383e268 --- /dev/null +++ b/src/tui/status.rs @@ -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); + } +}