improved mod list
This commit is contained in:
@@ -5,13 +5,13 @@ use log::error;
|
|||||||
use ratatui::{
|
use ratatui::{
|
||||||
DefaultTerminal, Frame,
|
DefaultTerminal, Frame,
|
||||||
layout::{Constraint, Direction, Layout, Rect},
|
layout::{Constraint, Direction, Layout, Rect},
|
||||||
widgets::{StatefulWidget, TableState, Widget},
|
widgets::{StatefulWidget, Widget},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
tui::{
|
tui::{
|
||||||
instance::{InstanceSelect, InstanceSelectState},
|
instance::{InstanceSelect, InstanceSelectState},
|
||||||
mod_list::ModList,
|
mod_list::{ModList, ModListState},
|
||||||
status::StatusBar,
|
status::StatusBar,
|
||||||
},
|
},
|
||||||
types::{ModdedInstance, RootConfig},
|
types::{ModdedInstance, RootConfig},
|
||||||
@@ -30,19 +30,21 @@ struct App<'a> {
|
|||||||
|
|
||||||
exit: bool,
|
exit: bool,
|
||||||
|
|
||||||
mod_list_state: TableState,
|
mod_list_state: ModListState,
|
||||||
selected_instance_state: InstanceSelectState,
|
selected_instance_state: InstanceSelectState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> App<'a> {
|
impl<'a> App<'a> {
|
||||||
fn new(root_config: &'a mut RootConfig) -> Self {
|
fn new(root_config: &'a mut RootConfig) -> Self {
|
||||||
|
let mut mod_list_state = ModListState::new();
|
||||||
|
mod_list_state.update_list(root_config, None);
|
||||||
Self {
|
Self {
|
||||||
root_config,
|
root_config,
|
||||||
|
|
||||||
loaded_instance: None,
|
loaded_instance: None,
|
||||||
|
|
||||||
exit: false,
|
exit: false,
|
||||||
mod_list_state: TableState::default(),
|
mod_list_state,
|
||||||
selected_instance_state: InstanceSelectState::new(),
|
selected_instance_state: InstanceSelectState::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -74,7 +76,7 @@ impl<'a> App<'a> {
|
|||||||
match key_event.code {
|
match key_event.code {
|
||||||
KeyCode::Esc | KeyCode::Char('q') => self.exit(),
|
KeyCode::Esc | KeyCode::Char('q') => self.exit(),
|
||||||
KeyCode::Up | KeyCode::Char('k') => {
|
KeyCode::Up | KeyCode::Char('k') => {
|
||||||
self.mod_list_state.select_previous();
|
self.mod_list_state.select_prev();
|
||||||
}
|
}
|
||||||
KeyCode::Down | KeyCode::Char('j') => {
|
KeyCode::Down | KeyCode::Char('j') => {
|
||||||
self.mod_list_state.select_next();
|
self.mod_list_state.select_next();
|
||||||
@@ -103,6 +105,8 @@ impl<'a> App<'a> {
|
|||||||
match self.root_config.load_instance_by_id(selected) {
|
match self.root_config.load_instance_by_id(selected) {
|
||||||
Ok(instance) => {
|
Ok(instance) => {
|
||||||
self.loaded_instance = Some(instance);
|
self.loaded_instance = Some(instance);
|
||||||
|
self.mod_list_state
|
||||||
|
.update_list(self.root_config, self.loaded_instance.as_ref());
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
error!("Failed to load instance: {err}");
|
error!("Failed to load instance: {err}");
|
||||||
@@ -126,8 +130,8 @@ impl<'a> Widget for &mut App<'a> {
|
|||||||
])
|
])
|
||||||
.split(area);
|
.split(area);
|
||||||
|
|
||||||
InstanceSelect {}.render(chunks[0], buf, &mut self.selected_instance_state);
|
InstanceSelect.render(chunks[0], buf, &mut self.selected_instance_state);
|
||||||
ModList::new(self.root_config).render(chunks[1], buf, &mut self.mod_list_state);
|
ModList.render(chunks[1], buf, &mut self.mod_list_state);
|
||||||
StatusBar.render(chunks[2], buf);
|
StatusBar.render(chunks[2], buf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ impl InstanceSelectState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct InstanceSelect {}
|
pub struct InstanceSelect;
|
||||||
|
|
||||||
impl StatefulWidget for InstanceSelect {
|
impl StatefulWidget for InstanceSelect {
|
||||||
type State = InstanceSelectState;
|
type State = InstanceSelectState;
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::collections::HashSet;
|
||||||
|
|
||||||
use ratatui::{
|
use ratatui::{
|
||||||
buffer::Buffer,
|
buffer::Buffer,
|
||||||
layout::{Constraint, Rect},
|
layout::{Constraint, Rect},
|
||||||
@@ -5,37 +7,73 @@ use ratatui::{
|
|||||||
widgets::{Block, Borders, Cell, Row, StatefulWidget, Table, TableState},
|
widgets::{Block, Borders, Cell, Row, StatefulWidget, Table, TableState},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::types::{ModConfig, RootConfig};
|
use crate::types::{ModConfig, ModdedInstance, RootConfig};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ListItem<'a> {
|
pub struct ModListState {
|
||||||
mod_config: &'a ModConfig,
|
table_state: TableState,
|
||||||
id: &'a str,
|
items: Vec<ListItem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
impl ModListState {
|
||||||
pub struct ModList<'a> {
|
pub fn new() -> Self {
|
||||||
items: Vec<ListItem<'a>>,
|
Self {
|
||||||
}
|
table_state: TableState::new(),
|
||||||
|
items: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_next(&mut self) {
|
||||||
|
self.table_state.select_next();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn select_prev(&mut self) {
|
||||||
|
self.table_state.select_previous();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update_list(
|
||||||
|
&mut self,
|
||||||
|
root_config: &RootConfig,
|
||||||
|
loaded_instance: Option<&ModdedInstance>,
|
||||||
|
) {
|
||||||
|
let instance_game_type = loaded_instance
|
||||||
|
.and_then(|e| root_config.game_by_id(e.game_id()))
|
||||||
|
.map(|e| e.game_type());
|
||||||
|
|
||||||
|
let included_ids: Option<HashSet<_>> =
|
||||||
|
loaded_instance.map(|instance| instance.mods().iter().map(|m| m.mod_id()).collect());
|
||||||
|
|
||||||
impl<'a> ModList<'a> {
|
|
||||||
pub fn new(root_config: &'a RootConfig) -> Self {
|
|
||||||
let mut items: Vec<_> = root_config
|
let mut items: Vec<_> = root_config
|
||||||
.mods()
|
.mods()
|
||||||
.iter()
|
.iter()
|
||||||
|
.filter(|e| instance_game_type.clone().is_none_or(|gt| e.1.game() == gt))
|
||||||
.map(|(id, config)| ListItem {
|
.map(|(id, config)| ListItem {
|
||||||
id,
|
id: id.to_owned(),
|
||||||
mod_config: config,
|
mod_config: config.clone(),
|
||||||
|
included: included_ids
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|set| set.contains(id.as_str())),
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
items.sort_by_key(|item| item.id);
|
items.sort_by_key(|item| item.id.clone());
|
||||||
Self { items }
|
|
||||||
|
self.items = items;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> StatefulWidget for ModList<'a> {
|
#[derive(Debug)]
|
||||||
type State = TableState;
|
struct ListItem {
|
||||||
|
mod_config: ModConfig,
|
||||||
|
id: String,
|
||||||
|
included: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ModList;
|
||||||
|
|
||||||
|
impl StatefulWidget for ModList {
|
||||||
|
type State = ModListState;
|
||||||
|
|
||||||
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) {
|
||||||
let block = Block::default()
|
let block = Block::default()
|
||||||
@@ -43,27 +81,34 @@ impl<'a> StatefulWidget for ModList<'a> {
|
|||||||
.borders(Borders::ALL)
|
.borders(Borders::ALL)
|
||||||
.style(Style::default());
|
.style(Style::default());
|
||||||
|
|
||||||
let rows: Vec<Row> = self
|
let rows: Vec<Row> = state
|
||||||
.items
|
.items
|
||||||
.iter()
|
.iter()
|
||||||
.map(|item| {
|
.map(|item| {
|
||||||
Row::new(vec![
|
Row::new(vec![
|
||||||
Cell::from(item.mod_config.name().unwrap_or(item.id)),
|
Cell::from(item.mod_config.name().unwrap_or(&item.id)),
|
||||||
Cell::from(item.id),
|
Cell::from(item.id.as_str()),
|
||||||
|
Cell::from(if item.included { "" } else { "" }),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let table = Table::new(rows, [Constraint::Fill(1), Constraint::Fill(1)])
|
let table = Table::new(
|
||||||
.row_highlight_style(
|
rows,
|
||||||
Style::default()
|
[
|
||||||
.fg(Color::Yellow)
|
Constraint::Fill(1),
|
||||||
.bg(Color::DarkGray)
|
Constraint::Fill(1),
|
||||||
.add_modifier(Modifier::BOLD),
|
Constraint::Fill(1),
|
||||||
)
|
],
|
||||||
.block(block)
|
)
|
||||||
.highlight_symbol(">> ");
|
.row_highlight_style(
|
||||||
|
Style::default()
|
||||||
|
.fg(Color::Yellow)
|
||||||
|
.bg(Color::DarkGray)
|
||||||
|
.add_modifier(Modifier::BOLD),
|
||||||
|
)
|
||||||
|
.block(block);
|
||||||
|
|
||||||
StatefulWidget::render(table, area, buf, state);
|
StatefulWidget::render(table, area, buf, &mut state.table_state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user