Compare commits
No commits in common. "f17d128518fa4ed4e0bb4b5bb68cd89e2538eaf6" and "ccf73766371411c4d025e6735e4de8c815006c28" have entirely different histories.
f17d128518
...
ccf7376637
@ -30,6 +30,3 @@ wayfarer --path <path-to-savefile>
|
|||||||
| q | Normal | Quits the application |
|
| q | Normal | Quits the application |
|
||||||
| o | Normal | Open a new file |
|
| o | Normal | Open a new file |
|
||||||
| w | Normal | Toggle file watcher mode (requires "watch" feature) |
|
| w | Normal | Toggle file watcher mode (requires "watch" feature) |
|
||||||
| e | Normal | Enter edit mode |
|
|
||||||
| H,J,K,L | Edit | Move between sections |
|
|
||||||
| h,j,k,l | Edit | Move inside the current section |
|
|
||||||
|
@ -33,15 +33,6 @@ pub struct Args {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub enum Direction {
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
Up,
|
|
||||||
Down,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum Message {
|
pub enum Message {
|
||||||
@ -54,21 +45,7 @@ pub enum Message {
|
|||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
ToggleFileWatch,
|
ToggleFileWatch,
|
||||||
|
|
||||||
StartEditEntry,
|
|
||||||
|
|
||||||
CommitEditEntry,
|
|
||||||
|
|
||||||
CancelEditEntry,
|
|
||||||
|
|
||||||
NextEntryValue,
|
|
||||||
|
|
||||||
PreviousEntryValue,
|
|
||||||
|
|
||||||
ReloadFile,
|
ReloadFile,
|
||||||
|
|
||||||
MoveSection(Direction),
|
|
||||||
|
|
||||||
MoveCur(Direction),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -132,12 +109,12 @@ fn handle_message(
|
|||||||
message: Message,
|
message: Message,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
match message {
|
match message {
|
||||||
Message::SetMode(Mode::Edit) => state.edit_current_file(),
|
|
||||||
Message::SetMode(mode) => {
|
Message::SetMode(mode) => {
|
||||||
debug!("Setting mode to {:?}", mode);
|
debug!("Setting mode to {:?}", mode);
|
||||||
|
|
||||||
state.mode = mode;
|
state.mode = mode;
|
||||||
}
|
}
|
||||||
|
|
||||||
Message::LoadFile => {
|
Message::LoadFile => {
|
||||||
let file_path = state.file_select.value();
|
let file_path = state.file_select.value();
|
||||||
info!("Loading file {}", file_path);
|
info!("Loading file {}", file_path);
|
||||||
@ -151,9 +128,10 @@ fn handle_message(
|
|||||||
|
|
||||||
msg_tx.send(Message::SetMode(Mode::Normal))?;
|
msg_tx.send(Message::SetMode(Mode::Normal))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
Message::ToggleFileWatch => {
|
Message::ToggleFileWatch => {
|
||||||
if let Some(savefile) = &state.savefile {
|
if let Some(savefile) = state.savefile() {
|
||||||
if state.is_watching_file() {
|
if state.is_watching_file() {
|
||||||
let evq_tx = msg_tx.clone();
|
let evq_tx = msg_tx.clone();
|
||||||
let callback = move || {
|
let callback = move || {
|
||||||
@ -168,26 +146,11 @@ fn handle_message(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Message::ReloadFile => state.reload_active_savefile()?,
|
|
||||||
Message::MoveSection(direction) => state.move_section(direction),
|
Message::ReloadFile => {
|
||||||
Message::MoveCur(direction) => state.move_in_current_section(direction),
|
state.reload_active_savefile()?;
|
||||||
Message::StartEditEntry => state.start_editing_entry(),
|
|
||||||
Message::CommitEditEntry => {
|
|
||||||
if let Err(err) = state.commit_entry_edit() {
|
|
||||||
error!(%err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Message::CancelEditEntry => state.cancel_editing_entry(),
|
|
||||||
Message::NextEntryValue => {
|
|
||||||
if let Err(err) = state.next_entry_value() {
|
|
||||||
error!(%err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Message::PreviousEntryValue => {
|
|
||||||
if let Err(err) = state.previous_entry_value() {
|
|
||||||
error!(%err);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ use tracing::debug;
|
|||||||
use tui_input::backend::crossterm::EventHandler;
|
use tui_input::backend::crossterm::EventHandler;
|
||||||
use tui_input::Input;
|
use tui_input::Input;
|
||||||
|
|
||||||
use super::{Direction, Message, Mode, State};
|
use super::{Message, Mode, State};
|
||||||
|
|
||||||
|
|
||||||
pub fn handle(event_queue: &mut mpsc::Sender<Message>, state: &mut State) -> Result<()> {
|
pub fn handle(event_queue: &mut mpsc::Sender<Message>, state: &mut State) -> Result<()> {
|
||||||
@ -49,10 +49,6 @@ fn handle_keyboard_input(
|
|||||||
msg_tx.send(Message::Exit)?;
|
msg_tx.send(Message::Exit)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
(Mode::Insert, KeyCode::Esc) => {
|
|
||||||
msg_tx.send(Message::CancelEditEntry)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
(_, KeyCode::Esc) => {
|
(_, KeyCode::Esc) => {
|
||||||
msg_tx.send(Message::SetMode(Mode::Normal))?;
|
msg_tx.send(Message::SetMode(Mode::Normal))?;
|
||||||
}
|
}
|
||||||
@ -81,64 +77,6 @@ fn handle_keyboard_input(
|
|||||||
msg_tx.send(Message::ReloadFile)?;
|
msg_tx.send(Message::ReloadFile)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
(Mode::Edit, KeyCode::Char('H')) => {
|
|
||||||
msg_tx.send(Message::MoveSection(Direction::Left))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
(Mode::Edit, KeyCode::Char('J')) => {
|
|
||||||
msg_tx.send(Message::MoveSection(Direction::Down))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
(Mode::Edit, KeyCode::Char('K')) => {
|
|
||||||
msg_tx.send(Message::MoveSection(Direction::Up))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
(Mode::Edit, KeyCode::Char('L')) => {
|
|
||||||
msg_tx.send(Message::MoveSection(Direction::Right))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
(Mode::Edit, KeyCode::Char('h')) => {
|
|
||||||
msg_tx.send(Message::MoveCur(Direction::Left))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
(Mode::Edit, KeyCode::Char('j')) => {
|
|
||||||
msg_tx.send(Message::MoveCur(Direction::Down))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
(Mode::Edit, KeyCode::Char('k')) => {
|
|
||||||
msg_tx.send(Message::MoveCur(Direction::Up))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
(Mode::Edit, KeyCode::Char('l')) => {
|
|
||||||
msg_tx.send(Message::MoveCur(Direction::Right))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
(Mode::Edit, KeyCode::Enter) => {
|
|
||||||
msg_tx.send(Message::StartEditEntry)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
(Mode::Edit, KeyCode::Char('n')) => {
|
|
||||||
msg_tx.send(Message::NextEntryValue)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
(Mode::Edit, KeyCode::Char('p')) => {
|
|
||||||
msg_tx.send(Message::PreviousEntryValue)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
(Mode::Insert, KeyCode::Enter) => {
|
|
||||||
msg_tx.send(Message::CommitEditEntry)?;
|
|
||||||
}
|
|
||||||
|
|
||||||
(Mode::Insert, _) => {
|
|
||||||
if let Some(input) = &mut state.edit_input {
|
|
||||||
input.handle_event(&Event::Key(key));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(Mode::Normal, KeyCode::Char('e')) => {
|
|
||||||
msg_tx.send(Message::SetMode(Mode::Edit))?;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
(Mode::Normal, KeyCode::Char('w')) => {
|
(Mode::Normal, KeyCode::Char('w')) => {
|
||||||
msg_tx.send(Message::ToggleFileWatch)?;
|
msg_tx.send(Message::ToggleFileWatch)?;
|
||||||
|
@ -3,16 +3,11 @@ use std::io::Write;
|
|||||||
use std::os::unix::prelude::OsStrExt;
|
use std::os::unix::prelude::OsStrExt;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use anyhow::{bail, Context, Result};
|
use anyhow::Result;
|
||||||
use jrny_save::{RobeColor, Savefile, LEVEL_NAMES};
|
use jrny_save::Savefile;
|
||||||
use ratatui::widgets::TableState;
|
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
use tui_input::Input;
|
use tui_input::Input;
|
||||||
|
|
||||||
use super::view::info::glyphs::TABLE_RANGE as GLYPHS_TABLE_RANGE;
|
|
||||||
use super::view::info::murals::TABLE_RANGE as MURALS_TABLE_RANGE;
|
|
||||||
use super::view::info::stats::TABLE_RANGE as STATS_TABLE_RANGE;
|
|
||||||
use super::Direction;
|
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
use crate::watcher::FileWatcher;
|
use crate::watcher::FileWatcher;
|
||||||
use crate::DIRS;
|
use crate::DIRS;
|
||||||
@ -23,42 +18,16 @@ pub enum Mode {
|
|||||||
#[default]
|
#[default]
|
||||||
Normal,
|
Normal,
|
||||||
|
|
||||||
Edit,
|
|
||||||
|
|
||||||
Insert,
|
|
||||||
|
|
||||||
ShowError(String),
|
ShowError(String),
|
||||||
|
|
||||||
SelectFile,
|
SelectFile,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Mode {
|
|
||||||
pub fn is_editing(&self) -> bool {
|
|
||||||
self == &Self::Edit || self == &Self::Insert
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
|
|
||||||
pub enum Section {
|
|
||||||
#[default]
|
|
||||||
General,
|
|
||||||
Glyphs,
|
|
||||||
Murals,
|
|
||||||
Companions,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub savefile: Option<Savefile>,
|
savefile: Option<Savefile>,
|
||||||
original_file: Option<Savefile>,
|
|
||||||
pub active_section: Section,
|
|
||||||
pub stats_table: TableState,
|
|
||||||
pub glyphs_table: TableState,
|
|
||||||
pub murals_table: TableState,
|
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
pub edit_input: Option<Input>,
|
|
||||||
pub file_select: Input,
|
pub file_select: Input,
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
file_watcher: Option<FileWatcher>,
|
file_watcher: Option<FileWatcher>,
|
||||||
@ -77,10 +46,17 @@ impl State {
|
|||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
savefile,
|
savefile,
|
||||||
..Default::default()
|
mode: Mode::default(),
|
||||||
|
file_select: Input::default(),
|
||||||
|
#[cfg(feature = "watch")]
|
||||||
|
file_watcher: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn savefile(&self) -> Option<&Savefile> {
|
||||||
|
self.savefile.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_savefile_from_path<P>(&mut self, path: P) -> Result<()>
|
pub fn set_savefile_from_path<P>(&mut self, path: P) -> Result<()>
|
||||||
where
|
where
|
||||||
P: AsRef<Path>,
|
P: AsRef<Path>,
|
||||||
@ -109,212 +85,6 @@ impl State {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn edit_current_file(&mut self) {
|
|
||||||
if !self.mode.is_editing() {
|
|
||||||
self.original_file = self.savefile.clone();
|
|
||||||
self.select_section(self.active_section);
|
|
||||||
self.mode = Mode::Edit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn start_editing_entry(&mut self) {
|
|
||||||
self.mode = Mode::Insert;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
|
||||||
pub fn commit_entry_edit(&mut self) -> Result<()> {
|
|
||||||
debug!(section = ?self.active_section);
|
|
||||||
|
|
||||||
match self.active_section {
|
|
||||||
Section::General => self.edit_stats_section()?,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
self.mode = Mode::Edit;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn edit_stats_section(&mut self) -> Result<()> {
|
|
||||||
let Some(savefile) = &mut self.savefile else {
|
|
||||||
bail!("No savefile loaded");
|
|
||||||
};
|
|
||||||
|
|
||||||
let input = self.edit_input.take().context("no edit input")?;
|
|
||||||
let value = input.value();
|
|
||||||
|
|
||||||
match self.stats_table.selected().context("no selection")? {
|
|
||||||
0 => savefile.journey_count = value.parse()?,
|
|
||||||
1 => savefile.total_companions_met = value.parse()?,
|
|
||||||
2 => savefile.total_collected_symbols = value.parse()?,
|
|
||||||
3 => {
|
|
||||||
let level_id = LEVEL_NAMES
|
|
||||||
.iter()
|
|
||||||
.position(|&v| v == value.trim_end())
|
|
||||||
.context("invalid level name")?;
|
|
||||||
savefile.current_level = level_id as u64;
|
|
||||||
}
|
|
||||||
4 => savefile.companions_met = value.parse()?,
|
|
||||||
5 => {
|
|
||||||
let new_length = value.parse()?;
|
|
||||||
if new_length > 30 {
|
|
||||||
bail!("Max length exceeded");
|
|
||||||
}
|
|
||||||
|
|
||||||
savefile.scarf_length = new_length;
|
|
||||||
}
|
|
||||||
6 => {
|
|
||||||
let next_symbol_id = value.parse()?;
|
|
||||||
|
|
||||||
if next_symbol_id > 20 {
|
|
||||||
bail!("Symbol id out of range");
|
|
||||||
}
|
|
||||||
|
|
||||||
savefile.symbol.id = next_symbol_id;
|
|
||||||
}
|
|
||||||
7 => {
|
|
||||||
let new_color = match value {
|
|
||||||
"Red" | "red" => RobeColor::Red,
|
|
||||||
"White" | "white" => RobeColor::White,
|
|
||||||
_ => bail!("invalid robe color"),
|
|
||||||
};
|
|
||||||
savefile.set_robe_color(new_color);
|
|
||||||
}
|
|
||||||
8 => savefile.set_robe_tier(value.parse()?),
|
|
||||||
9 => {}
|
|
||||||
idx => debug!("unknown index {:?}", idx),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next_entry_value(&mut self) -> Result<()> {
|
|
||||||
match self.active_section {
|
|
||||||
Section::General => self.next_stats_entry_value()?,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn next_stats_entry_value(&mut self) -> Result<()> {
|
|
||||||
let Some(savefile) = &mut self.savefile else {
|
|
||||||
bail!("No savefile loaded");
|
|
||||||
};
|
|
||||||
|
|
||||||
match self.stats_table.selected().context("no selection")? {
|
|
||||||
0 => savefile.journey_count += 1,
|
|
||||||
1 => savefile.total_companions_met += 1,
|
|
||||||
2 => savefile.total_collected_symbols += 1,
|
|
||||||
3 => {
|
|
||||||
let next_level = savefile.current_level + 1;
|
|
||||||
savefile.current_level = if next_level >= LEVEL_NAMES.len() as u64 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
savefile.current_level + 1
|
|
||||||
};
|
|
||||||
}
|
|
||||||
4 => savefile.companions_met += 1,
|
|
||||||
5 => {
|
|
||||||
if savefile.scarf_length < 30 {
|
|
||||||
savefile.scarf_length += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
6 => {
|
|
||||||
let next_symbol = savefile.symbol.id + 1;
|
|
||||||
savefile.symbol.id = if next_symbol > 20 {
|
|
||||||
0
|
|
||||||
} else {
|
|
||||||
savefile.symbol.id + 1
|
|
||||||
};
|
|
||||||
}
|
|
||||||
7 => {
|
|
||||||
savefile.set_robe_color(match savefile.robe_color() {
|
|
||||||
RobeColor::Red => RobeColor::White,
|
|
||||||
RobeColor::White => RobeColor::Red,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
8 => {
|
|
||||||
let next_tier = savefile.robe_tier() + 1;
|
|
||||||
savefile.set_robe_tier(next_tier);
|
|
||||||
}
|
|
||||||
9 => {}
|
|
||||||
idx => debug!("unknown index {:?}", idx),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn previous_entry_value(&mut self) -> Result<()> {
|
|
||||||
match self.active_section {
|
|
||||||
Section::General => self.previous_stats_entry_value()?,
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn previous_stats_entry_value(&mut self) -> Result<()> {
|
|
||||||
let Some(savefile) = &mut self.savefile else {
|
|
||||||
bail!("No savefile loaded");
|
|
||||||
};
|
|
||||||
|
|
||||||
match self.stats_table.selected().context("no selection")? {
|
|
||||||
0 => savefile.journey_count = savefile.journey_count.saturating_sub(1),
|
|
||||||
1 => savefile.total_companions_met = savefile.total_companions_met.saturating_sub(1),
|
|
||||||
2 => {
|
|
||||||
savefile.total_collected_symbols =
|
|
||||||
savefile.total_collected_symbols.saturating_sub(1)
|
|
||||||
}
|
|
||||||
3 => {
|
|
||||||
let next_level: i64 = savefile.current_level as i64 - 1;
|
|
||||||
savefile.current_level = if next_level < 0 {
|
|
||||||
LEVEL_NAMES.len() as u64 - 1
|
|
||||||
} else {
|
|
||||||
next_level as u64
|
|
||||||
};
|
|
||||||
}
|
|
||||||
4 => savefile.companions_met = savefile.companions_met.saturating_sub(1),
|
|
||||||
5 => {
|
|
||||||
if savefile.scarf_length > 0 {
|
|
||||||
savefile.scarf_length = savefile.scarf_length.saturating_sub(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
6 => {
|
|
||||||
let next_symbol = savefile.symbol.id as i32 - 1;
|
|
||||||
savefile.symbol.id = if next_symbol < 0 {
|
|
||||||
20
|
|
||||||
} else {
|
|
||||||
next_symbol as u32
|
|
||||||
};
|
|
||||||
}
|
|
||||||
7 => {
|
|
||||||
savefile.set_robe_color(match savefile.robe_color() {
|
|
||||||
RobeColor::Red => RobeColor::White,
|
|
||||||
RobeColor::White => RobeColor::Red,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
8 => {
|
|
||||||
let next_tier = savefile.robe_tier().saturating_sub(1);
|
|
||||||
savefile.set_robe_tier(next_tier);
|
|
||||||
}
|
|
||||||
9 => {}
|
|
||||||
idx => debug!("unknown index {:?}", idx),
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cancel_editing_entry(&mut self) {
|
|
||||||
if self.mode == Mode::Insert {
|
|
||||||
self.edit_input = None;
|
|
||||||
self.mode = Mode::Edit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_savefile_loaded(&self) -> bool {
|
|
||||||
self.savefile.is_some()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
pub fn is_watching_file(&self) -> bool {
|
pub fn is_watching_file(&self) -> bool {
|
||||||
self.file_watcher.is_some()
|
self.file_watcher.is_some()
|
||||||
@ -345,69 +115,6 @@ impl State {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn move_section(&mut self, direction: Direction) {
|
|
||||||
let next_section = match (direction, self.active_section) {
|
|
||||||
(Direction::Left, Section::Companions) => Section::General,
|
|
||||||
(Direction::Left, _) => Section::Companions,
|
|
||||||
(Direction::Right, Section::Companions) => Section::General,
|
|
||||||
(Direction::Right, _) => Section::Companions,
|
|
||||||
(Direction::Down, Section::General) => Section::Glyphs,
|
|
||||||
(Direction::Down, Section::Glyphs) => Section::Murals,
|
|
||||||
(Direction::Down, Section::Murals) => Section::General,
|
|
||||||
(Direction::Down, section) => section,
|
|
||||||
(Direction::Up, Section::General) => Section::Murals,
|
|
||||||
(Direction::Up, Section::Murals) => Section::Glyphs,
|
|
||||||
(Direction::Up, Section::Glyphs) => Section::General,
|
|
||||||
(Direction::Up, section) => section,
|
|
||||||
};
|
|
||||||
|
|
||||||
self.select_section(next_section);
|
|
||||||
self.active_section = next_section;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn select_section(&mut self, section: Section) {
|
|
||||||
let table = match section {
|
|
||||||
Section::General => &mut self.stats_table,
|
|
||||||
Section::Glyphs => &mut self.glyphs_table,
|
|
||||||
Section::Murals => &mut self.murals_table,
|
|
||||||
_ => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
if table.selected().is_none() {
|
|
||||||
table.select(Some(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn move_in_current_section(&mut self, direction: Direction) {
|
|
||||||
match self.active_section {
|
|
||||||
Section::General => {
|
|
||||||
select_row_in_range(&mut self.stats_table, direction, STATS_TABLE_RANGE)
|
|
||||||
}
|
|
||||||
Section::Glyphs => {
|
|
||||||
select_row_in_range(&mut self.glyphs_table, direction, GLYPHS_TABLE_RANGE)
|
|
||||||
}
|
|
||||||
Section::Murals => {
|
|
||||||
select_row_in_range(&mut self.murals_table, direction, MURALS_TABLE_RANGE)
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fn select_row_in_range(table: &mut TableState, direction: Direction, (min, max): (usize, usize)) {
|
|
||||||
match (direction, table.selected()) {
|
|
||||||
(Direction::Up, Some(i)) if i <= min => (),
|
|
||||||
(Direction::Up, Some(i)) => {
|
|
||||||
table.select(Some(i - 1));
|
|
||||||
}
|
|
||||||
(Direction::Down, Some(i)) if i >= max => (),
|
|
||||||
(Direction::Down, Some(i)) => {
|
|
||||||
table.select(Some(i + 1));
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
pub mod info;
|
mod info;
|
||||||
pub mod status_bar;
|
mod status_bar;
|
||||||
|
|
||||||
|
|
||||||
use std::io::Stdout;
|
use std::io::Stdout;
|
||||||
@ -13,7 +13,7 @@ use super::State;
|
|||||||
type Frame<'a> = ratatui::Frame<'a, CrosstermBackend<Stdout>>;
|
type Frame<'a> = ratatui::Frame<'a, CrosstermBackend<Stdout>>;
|
||||||
|
|
||||||
|
|
||||||
pub(super) fn render(state: &mut State, frame: &mut Frame) {
|
pub fn render(state: &mut State, frame: &mut Frame) {
|
||||||
let rows = Layout::default()
|
let rows = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([Constraint::Min(40), Constraint::Length(2)])
|
.constraints([Constraint::Min(40), Constraint::Length(2)])
|
||||||
|
@ -1,21 +1,15 @@
|
|||||||
pub mod companions;
|
use jrny_save::{Savefile, LEVEL_NAMES};
|
||||||
pub mod glyphs;
|
use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};
|
||||||
pub mod murals;
|
use ratatui::widgets::{Block, Borders, Cell, Padding, Paragraph, Row, Table};
|
||||||
pub mod stats;
|
|
||||||
|
|
||||||
|
|
||||||
use ratatui::layout::{Constraint, Direction, Layout, Rect};
|
|
||||||
use ratatui::widgets::{Block, Borders, Padding, Paragraph};
|
|
||||||
|
|
||||||
use crate::tui::view::Frame;
|
use crate::tui::view::Frame;
|
||||||
use crate::tui::State;
|
use crate::tui::State;
|
||||||
|
|
||||||
|
|
||||||
pub(super) fn render(state: &mut State, frame: &mut Frame, area: Rect) {
|
pub fn render(state: &mut State, frame: &mut Frame, area: Rect) {
|
||||||
if state.is_savefile_loaded() {
|
match state.savefile() {
|
||||||
render_info(state, frame, area);
|
Some(savefile) => render_info(savefile, frame, area),
|
||||||
} else {
|
None => render_no_active_file(frame, area),
|
||||||
render_no_active_file(frame, area);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +26,7 @@ fn render_no_active_file(frame: &mut Frame, area: Rect) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn render_info(state: &mut State, mut frame: &mut Frame, area: Rect) {
|
fn render_info(savefile: &Savefile, mut frame: &mut Frame, area: Rect) {
|
||||||
let columns = Layout::default()
|
let columns = Layout::default()
|
||||||
.direction(Direction::Horizontal)
|
.direction(Direction::Horizontal)
|
||||||
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)])
|
||||||
@ -41,9 +35,9 @@ fn render_info(state: &mut State, mut frame: &mut Frame, area: Rect) {
|
|||||||
let left_column = Layout::default()
|
let left_column = Layout::default()
|
||||||
.direction(Direction::Vertical)
|
.direction(Direction::Vertical)
|
||||||
.constraints([
|
.constraints([
|
||||||
Constraint::Ratio(1, 3),
|
Constraint::Ratio(6, 12),
|
||||||
Constraint::Ratio(1, 3),
|
Constraint::Ratio(3, 12),
|
||||||
Constraint::Ratio(1, 3),
|
Constraint::Ratio(3, 12),
|
||||||
])
|
])
|
||||||
.split(columns[0]);
|
.split(columns[0]);
|
||||||
|
|
||||||
@ -52,8 +46,171 @@ fn render_info(state: &mut State, mut frame: &mut Frame, area: Rect) {
|
|||||||
.constraints([Constraint::Ratio(10, 10)])
|
.constraints([Constraint::Ratio(10, 10)])
|
||||||
.split(columns[1]);
|
.split(columns[1]);
|
||||||
|
|
||||||
stats::render(state, &mut frame, left_column[0]);
|
|
||||||
glyphs::render(state, &mut frame, left_column[1]);
|
render_stats(&savefile, &mut frame, left_column[0]);
|
||||||
murals::render(state, &mut frame, left_column[2]);
|
render_glyphs(&savefile, &mut frame, left_column[1]);
|
||||||
companions::render(state, &mut frame, right_column[0]);
|
render_murals(&savefile, &mut frame, left_column[2]);
|
||||||
|
|
||||||
|
render_companions(&savefile, &mut frame, right_column[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn render_stats<'a>(savefile: &Savefile, frame: &mut Frame, area: Rect) {
|
||||||
|
let stats_section_block = Block::default()
|
||||||
|
.padding(Padding::new(2, 2, 1, 1))
|
||||||
|
.borders(Borders::ALL);
|
||||||
|
|
||||||
|
let layout = Layout::default()
|
||||||
|
.direction(Direction::Horizontal)
|
||||||
|
.constraints([Constraint::Percentage(25), Constraint::Percentage(75)])
|
||||||
|
.split(stats_section_block.inner(area));
|
||||||
|
|
||||||
|
let stats_block = Block::default().title("Stats");
|
||||||
|
|
||||||
|
let table = Table::new([
|
||||||
|
Row::new([
|
||||||
|
"Journeys Completed".to_string(),
|
||||||
|
savefile.journey_count.to_string(),
|
||||||
|
]),
|
||||||
|
Row::new([
|
||||||
|
"Total Companions Met".to_string(),
|
||||||
|
savefile.total_companions_met.to_string(),
|
||||||
|
]),
|
||||||
|
Row::new([
|
||||||
|
"Total Symbols Collected".to_string(),
|
||||||
|
savefile.total_collected_symbols.to_string(),
|
||||||
|
]),
|
||||||
|
Row::new(["Current Level", savefile.current_level_name()]),
|
||||||
|
Row::new([
|
||||||
|
"Companions Met".to_string(),
|
||||||
|
savefile.companions_met.to_string(),
|
||||||
|
]),
|
||||||
|
Row::new([
|
||||||
|
"Scarf Length".to_string(),
|
||||||
|
savefile.scarf_length.to_string(),
|
||||||
|
]),
|
||||||
|
Row::new(["Symbol Number".to_string(), savefile.symbol.id.to_string()]),
|
||||||
|
Row::new(["Robe Color".to_string(), savefile.robe_color().to_string()]),
|
||||||
|
Row::new(["Robe Tier".to_string(), savefile.robe_tier().to_string()]),
|
||||||
|
Row::new(["Last Played".to_string(), savefile.last_played.to_string()]),
|
||||||
|
])
|
||||||
|
.widths(&[Constraint::Ratio(1, 3), Constraint::Ratio(2, 3)])
|
||||||
|
.block(stats_block);
|
||||||
|
|
||||||
|
let cur_symbol_block = Block::default();
|
||||||
|
|
||||||
|
let cur_symbol = Paragraph::new(savefile.symbol.to_string()).block(cur_symbol_block);
|
||||||
|
|
||||||
|
frame.render_widget(stats_section_block, area);
|
||||||
|
frame.render_widget(cur_symbol, layout[0]);
|
||||||
|
frame.render_widget(table, layout[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn render_companions<'a>(savefile: &Savefile, frame: &mut Frame, area: Rect) {
|
||||||
|
let companions_block = Block::default()
|
||||||
|
.title("Companions")
|
||||||
|
.padding(Padding::new(2, 2, 1, 1))
|
||||||
|
.borders(Borders::ALL);
|
||||||
|
|
||||||
|
let layout = Layout::default()
|
||||||
|
.direction(Direction::Vertical)
|
||||||
|
.constraints([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)])
|
||||||
|
.split(companions_block.inner(area));
|
||||||
|
|
||||||
|
let current_companions_block = Block::default()
|
||||||
|
.title("Current")
|
||||||
|
.borders(Borders::TOP)
|
||||||
|
.title_alignment(Alignment::Center);
|
||||||
|
|
||||||
|
let current_companions = Table::new(
|
||||||
|
savefile
|
||||||
|
.current_companions()
|
||||||
|
.map(|companion| Row::new([companion.name.clone(), companion.steam_url()])),
|
||||||
|
)
|
||||||
|
.widths(&[Constraint::Ratio(1, 3), Constraint::Ratio(2, 3)])
|
||||||
|
.block(current_companions_block);
|
||||||
|
|
||||||
|
let past_companions_block = Block::default()
|
||||||
|
.title("Past")
|
||||||
|
.borders(Borders::TOP)
|
||||||
|
.title_alignment(Alignment::Center);
|
||||||
|
|
||||||
|
let past_companions = Table::new(
|
||||||
|
savefile
|
||||||
|
.past_companions()
|
||||||
|
.map(|companion| Row::new([companion.name.clone(), companion.steam_url()])),
|
||||||
|
)
|
||||||
|
.widths(&[Constraint::Ratio(1, 3), Constraint::Ratio(2, 3)])
|
||||||
|
.block(past_companions_block);
|
||||||
|
|
||||||
|
frame.render_widget(companions_block, area);
|
||||||
|
frame.render_widget(current_companions, layout[0]);
|
||||||
|
frame.render_widget(past_companions, layout[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn render_glyphs<'a>(savefile: &Savefile, frame: &mut Frame, area: Rect) {
|
||||||
|
const FOUND_SIGN: &str = "◆";
|
||||||
|
const NOT_FOUND_SIGN: &str = "◇";
|
||||||
|
|
||||||
|
let block = Block::default()
|
||||||
|
.title("Glyphs")
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.padding(Padding::new(2, 2, 1, 1));
|
||||||
|
|
||||||
|
let table = Table::new(savefile.glyphs.all().map(|(level_number, status)| {
|
||||||
|
let status = status
|
||||||
|
.iter()
|
||||||
|
.map(|&val| Cell::from(if val { FOUND_SIGN } else { NOT_FOUND_SIGN }));
|
||||||
|
Row::new(
|
||||||
|
[Cell::from(LEVEL_NAMES[level_number])]
|
||||||
|
.into_iter()
|
||||||
|
.chain(status),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.widths(&[
|
||||||
|
Constraint::Length(20),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
])
|
||||||
|
.column_spacing(1)
|
||||||
|
.block(block);
|
||||||
|
|
||||||
|
frame.render_widget(table, area);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn render_murals<'a>(savefile: &Savefile, frame: &mut Frame, area: Rect) {
|
||||||
|
const FOUND_SIGN: &str = "▾";
|
||||||
|
const NOT_FOUND_SIGN: &str = "▿";
|
||||||
|
|
||||||
|
let block = Block::default()
|
||||||
|
.title("Murals")
|
||||||
|
.borders(Borders::ALL)
|
||||||
|
.padding(Padding::new(2, 2, 1, 1));
|
||||||
|
|
||||||
|
let table = Table::new(savefile.murals.all().map(|(level_number, status)| {
|
||||||
|
let status = status
|
||||||
|
.iter()
|
||||||
|
.map(|&val| Cell::from(if val { FOUND_SIGN } else { NOT_FOUND_SIGN }));
|
||||||
|
Row::new(
|
||||||
|
[Cell::from(LEVEL_NAMES[level_number])]
|
||||||
|
.into_iter()
|
||||||
|
.chain(status),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
.widths(&[
|
||||||
|
Constraint::Length(20),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
Constraint::Length(3),
|
||||||
|
])
|
||||||
|
.column_spacing(1)
|
||||||
|
.block(block);
|
||||||
|
|
||||||
|
frame.render_widget(table, area);
|
||||||
}
|
}
|
||||||
|
@ -1,63 +0,0 @@
|
|||||||
use ratatui::layout::{Alignment, Constraint, Direction, Layout, Rect};
|
|
||||||
use ratatui::style::Style;
|
|
||||||
use ratatui::widgets::{Block, Borders, Padding, Row, Table};
|
|
||||||
|
|
||||||
use crate::tui::state::{Mode, Section};
|
|
||||||
use crate::tui::view::Frame;
|
|
||||||
use crate::tui::State;
|
|
||||||
|
|
||||||
|
|
||||||
pub(super) fn render<'a>(state: &State, frame: &mut Frame, area: Rect) {
|
|
||||||
let Some(savefile) = &state.savefile else {
|
|
||||||
return
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_selected = state.active_section == Section::Companions && state.mode == Mode::Edit;
|
|
||||||
|
|
||||||
let border_style = if is_selected {
|
|
||||||
Style::default().fg(ratatui::style::Color::Blue)
|
|
||||||
} else {
|
|
||||||
Style::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let companions_block = Block::default()
|
|
||||||
.title("Companions")
|
|
||||||
.padding(Padding::new(2, 2, 1, 1))
|
|
||||||
.border_style(border_style)
|
|
||||||
.borders(Borders::ALL);
|
|
||||||
|
|
||||||
let layout = Layout::default()
|
|
||||||
.direction(Direction::Vertical)
|
|
||||||
.constraints([Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)])
|
|
||||||
.split(companions_block.inner(area));
|
|
||||||
|
|
||||||
let current_companions_block = Block::default()
|
|
||||||
.title("Current")
|
|
||||||
.borders(Borders::TOP)
|
|
||||||
.title_alignment(Alignment::Center);
|
|
||||||
|
|
||||||
let current_companions = Table::new(
|
|
||||||
savefile
|
|
||||||
.current_companions()
|
|
||||||
.map(|companion| Row::new([companion.name.clone(), companion.steam_url()])),
|
|
||||||
)
|
|
||||||
.widths(&[Constraint::Ratio(1, 3), Constraint::Ratio(2, 3)])
|
|
||||||
.block(current_companions_block);
|
|
||||||
|
|
||||||
let past_companions_block = Block::default()
|
|
||||||
.title("Past")
|
|
||||||
.borders(Borders::TOP)
|
|
||||||
.title_alignment(Alignment::Center);
|
|
||||||
|
|
||||||
let past_companions = Table::new(
|
|
||||||
savefile
|
|
||||||
.past_companions()
|
|
||||||
.map(|companion| Row::new([companion.name.clone(), companion.steam_url()])),
|
|
||||||
)
|
|
||||||
.widths(&[Constraint::Ratio(1, 3), Constraint::Ratio(2, 3)])
|
|
||||||
.block(past_companions_block);
|
|
||||||
|
|
||||||
frame.render_widget(companions_block, area);
|
|
||||||
frame.render_widget(current_companions, layout[0]);
|
|
||||||
frame.render_widget(past_companions, layout[1]);
|
|
||||||
}
|
|
@ -1,65 +0,0 @@
|
|||||||
use jrny_save::LEVEL_NAMES;
|
|
||||||
use ratatui::layout::{Constraint, Rect};
|
|
||||||
use ratatui::style::{Color, Style};
|
|
||||||
use ratatui::widgets::{Block, Borders, Cell, Padding, Row, Table};
|
|
||||||
|
|
||||||
use crate::tui::state::{Mode, Section};
|
|
||||||
use crate::tui::view::Frame;
|
|
||||||
use crate::tui::State;
|
|
||||||
|
|
||||||
|
|
||||||
pub const TABLE_RANGE: (usize, usize) = (0, 5);
|
|
||||||
|
|
||||||
|
|
||||||
pub(super) fn render<'a>(state: &mut State, frame: &mut Frame, area: Rect) {
|
|
||||||
const FOUND_SIGN: &str = "◆";
|
|
||||||
const NOT_FOUND_SIGN: &str = "◇";
|
|
||||||
|
|
||||||
let Some(savefile) = &state.savefile else {
|
|
||||||
return
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_selected = state.active_section == Section::Glyphs && state.mode == Mode::Edit;
|
|
||||||
|
|
||||||
|
|
||||||
let border_style = if is_selected {
|
|
||||||
Style::default().fg(Color::Blue)
|
|
||||||
} else {
|
|
||||||
Style::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let block = Block::default()
|
|
||||||
.title("Glyphs")
|
|
||||||
.border_style(border_style)
|
|
||||||
.borders(Borders::ALL)
|
|
||||||
.padding(Padding::new(2, 2, 1, 1));
|
|
||||||
|
|
||||||
let table_highlight = if is_selected {
|
|
||||||
Style::default().fg(Color::Blue)
|
|
||||||
} else {
|
|
||||||
Style::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let table = Table::new(savefile.glyphs.all().map(|(level_number, status)| {
|
|
||||||
let status = status
|
|
||||||
.iter()
|
|
||||||
.map(|&val| Cell::from(if val { FOUND_SIGN } else { NOT_FOUND_SIGN }));
|
|
||||||
Row::new(
|
|
||||||
[Cell::from(LEVEL_NAMES[level_number])]
|
|
||||||
.into_iter()
|
|
||||||
.chain(status),
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
.widths(&[
|
|
||||||
Constraint::Length(20),
|
|
||||||
Constraint::Length(3),
|
|
||||||
Constraint::Length(3),
|
|
||||||
Constraint::Length(3),
|
|
||||||
Constraint::Length(3),
|
|
||||||
])
|
|
||||||
.column_spacing(1)
|
|
||||||
.highlight_style(table_highlight)
|
|
||||||
.block(block);
|
|
||||||
|
|
||||||
frame.render_stateful_widget(table, area, &mut state.glyphs_table);
|
|
||||||
}
|
|
@ -1,64 +0,0 @@
|
|||||||
use jrny_save::LEVEL_NAMES;
|
|
||||||
use ratatui::layout::{Constraint, Rect};
|
|
||||||
use ratatui::style::{Color, Style};
|
|
||||||
use ratatui::widgets::{Block, Borders, Cell, Padding, Row, Table};
|
|
||||||
|
|
||||||
use crate::tui::state::{Mode, Section};
|
|
||||||
use crate::tui::view::Frame;
|
|
||||||
use crate::tui::State;
|
|
||||||
|
|
||||||
|
|
||||||
pub const TABLE_RANGE: (usize, usize) = (0, 6);
|
|
||||||
|
|
||||||
|
|
||||||
pub(super) fn render<'a>(state: &mut State, frame: &mut Frame, area: Rect) {
|
|
||||||
const FOUND_SIGN: &str = "▾";
|
|
||||||
const NOT_FOUND_SIGN: &str = "▿";
|
|
||||||
|
|
||||||
let Some(savefile) = &state.savefile else {
|
|
||||||
return
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_selected = state.active_section == Section::Murals && state.mode == Mode::Edit;
|
|
||||||
|
|
||||||
let border_style = if is_selected {
|
|
||||||
Style::default().fg(Color::Blue)
|
|
||||||
} else {
|
|
||||||
Style::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let block = Block::default()
|
|
||||||
.title("Murals")
|
|
||||||
.border_style(border_style)
|
|
||||||
.borders(Borders::ALL)
|
|
||||||
.padding(Padding::new(2, 2, 1, 1));
|
|
||||||
|
|
||||||
let table_highlight = if is_selected {
|
|
||||||
Style::default().fg(Color::Blue)
|
|
||||||
} else {
|
|
||||||
Style::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let table = Table::new(savefile.murals.all().map(|(level_number, status)| {
|
|
||||||
let status = status
|
|
||||||
.iter()
|
|
||||||
.map(|&val| Cell::from(if val { FOUND_SIGN } else { NOT_FOUND_SIGN }));
|
|
||||||
Row::new(
|
|
||||||
[Cell::from(LEVEL_NAMES[level_number])]
|
|
||||||
.into_iter()
|
|
||||||
.chain(status),
|
|
||||||
)
|
|
||||||
}))
|
|
||||||
.widths(&[
|
|
||||||
Constraint::Length(20),
|
|
||||||
Constraint::Length(3),
|
|
||||||
Constraint::Length(3),
|
|
||||||
Constraint::Length(3),
|
|
||||||
Constraint::Length(3),
|
|
||||||
])
|
|
||||||
.column_spacing(1)
|
|
||||||
.highlight_style(table_highlight)
|
|
||||||
.block(block);
|
|
||||||
|
|
||||||
frame.render_stateful_widget(table, area, &mut state.murals_table);
|
|
||||||
}
|
|
@ -1,112 +0,0 @@
|
|||||||
use ratatui::layout::{Constraint, Direction, Layout, Rect};
|
|
||||||
use ratatui::style::{Color, Style};
|
|
||||||
use ratatui::widgets::{Block, Borders, Cell, Padding, Paragraph, Row, Table};
|
|
||||||
use tui_input::Input;
|
|
||||||
|
|
||||||
use crate::tui::state::{Mode, Section};
|
|
||||||
use crate::tui::view::Frame;
|
|
||||||
use crate::tui::State;
|
|
||||||
|
|
||||||
|
|
||||||
pub const TABLE_RANGE: (usize, usize) = (0, 9);
|
|
||||||
|
|
||||||
|
|
||||||
pub(super) fn render<'a>(state: &mut State, frame: &mut Frame, area: Rect) {
|
|
||||||
let Some(savefile) = &state.savefile else {
|
|
||||||
return
|
|
||||||
};
|
|
||||||
|
|
||||||
let is_selected = state.active_section == Section::General && state.mode.is_editing();
|
|
||||||
|
|
||||||
let border_style = if is_selected {
|
|
||||||
Style::default().fg(Color::Blue)
|
|
||||||
} else {
|
|
||||||
Style::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let stats_section_block = Block::default()
|
|
||||||
.padding(Padding::new(2, 2, 1, 1))
|
|
||||||
.border_style(border_style)
|
|
||||||
.borders(Borders::ALL);
|
|
||||||
|
|
||||||
let layout = Layout::default()
|
|
||||||
.direction(Direction::Horizontal)
|
|
||||||
.constraints([Constraint::Percentage(25), Constraint::Percentage(75)])
|
|
||||||
.split(stats_section_block.inner(area));
|
|
||||||
|
|
||||||
let stats_block = Block::default().title("Stats");
|
|
||||||
|
|
||||||
let table_highlight = if is_selected {
|
|
||||||
Style::default().fg(Color::Blue)
|
|
||||||
} else {
|
|
||||||
Style::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let rows = [
|
|
||||||
("Journeys Completed", savefile.journey_count.to_string()),
|
|
||||||
(
|
|
||||||
"Total Companions Met",
|
|
||||||
savefile.total_companions_met.to_string(),
|
|
||||||
),
|
|
||||||
(
|
|
||||||
"Total Symbols Collected",
|
|
||||||
savefile.total_collected_symbols.to_string(),
|
|
||||||
),
|
|
||||||
("Current Level", savefile.current_level_name().to_string()),
|
|
||||||
("Companions Met", savefile.companions_met.to_string()),
|
|
||||||
("Scarf Length", savefile.scarf_length.to_string()),
|
|
||||||
("Symbol Number", savefile.symbol.id.to_string()),
|
|
||||||
("Robe Color", savefile.robe_color().to_string()),
|
|
||||||
("Robe Tier", savefile.robe_tier().to_string()),
|
|
||||||
("Last Played", savefile.last_played.to_string()),
|
|
||||||
]
|
|
||||||
.into_iter()
|
|
||||||
.enumerate()
|
|
||||||
.map(|(idx, (title, value))| {
|
|
||||||
let value = match state.stats_table.selected() {
|
|
||||||
Some(sel) if sel == idx => {
|
|
||||||
let value = match state.mode {
|
|
||||||
Mode::Insert => {
|
|
||||||
if state.edit_input.is_none() {
|
|
||||||
state.edit_input.replace(Input::new(value));
|
|
||||||
}
|
|
||||||
|
|
||||||
let input = state.edit_input.as_ref().unwrap();
|
|
||||||
input.value().to_string()
|
|
||||||
}
|
|
||||||
Mode::Edit => format!("< {} >", value),
|
|
||||||
_ => value.to_string(),
|
|
||||||
};
|
|
||||||
|
|
||||||
Cell::from(value)
|
|
||||||
}
|
|
||||||
_ => Cell::from(value.to_string()),
|
|
||||||
};
|
|
||||||
|
|
||||||
Row::new([Cell::from(title), value])
|
|
||||||
});
|
|
||||||
|
|
||||||
let table = Table::new(rows)
|
|
||||||
.highlight_style(table_highlight)
|
|
||||||
.widths(&[Constraint::Ratio(1, 3), Constraint::Ratio(2, 3)])
|
|
||||||
.block(stats_block);
|
|
||||||
|
|
||||||
let cur_symbol_block = Block::default();
|
|
||||||
|
|
||||||
let cur_symbol = Paragraph::new(savefile.symbol.to_string()).block(cur_symbol_block);
|
|
||||||
|
|
||||||
frame.render_widget(stats_section_block, area);
|
|
||||||
frame.render_widget(cur_symbol, layout[0]);
|
|
||||||
frame.render_stateful_widget(table, layout[1], &mut state.stats_table);
|
|
||||||
|
|
||||||
if state.mode == Mode::Insert {
|
|
||||||
if let Some(idx) = state.stats_table.selected() {
|
|
||||||
if let Some(input) = &state.edit_input {
|
|
||||||
frame.set_cursor(
|
|
||||||
layout[1].x + layout[1].width / 3 + (input.visual_cursor() + 1) as u16,
|
|
||||||
layout[1].y + (idx + 1 - state.stats_table.offset()) as u16,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,32 +17,24 @@ pub fn render(state: &State, mut frame: &mut Frame, area: Rect) {
|
|||||||
frame.render_widget(error_msg, area);
|
frame.render_widget(error_msg, area);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mode::Edit | Mode::Insert => {
|
|
||||||
if let Some(savefile) = &state.savefile {
|
|
||||||
let text = format!("Editing file: {}", savefile.path.display());
|
|
||||||
let status = Paragraph::new(text).block(status_block);
|
|
||||||
frame.render_widget(status, area);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "watch")]
|
#[cfg(feature = "watch")]
|
||||||
Mode::Normal if state.is_watching_file() => {
|
Mode::Normal if state.is_watching_file() => {
|
||||||
if let Some(savefile) = &state.savefile {
|
if let Some(savefile) = state.savefile() {
|
||||||
let text = format!("Watching file: {}", savefile.path.display());
|
let text = format!("Watching file: {}", savefile.path.display());
|
||||||
let status = Paragraph::new(text).block(status_block);
|
let status = Paragraph::new(text).block(status_block);
|
||||||
frame.render_widget(status, area);
|
frame.render_widget(status, area);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Mode::SelectFile => render_file_select(&state, &mut frame, status_block, area),
|
Mode::Normal => {
|
||||||
|
if let Some(savefile) = state.savefile() {
|
||||||
_ => {
|
|
||||||
if let Some(savefile) = &state.savefile {
|
|
||||||
let text = format!("Showing file: {}", savefile.path.display());
|
let text = format!("Showing file: {}", savefile.path.display());
|
||||||
let status = Paragraph::new(text).block(status_block);
|
let status = Paragraph::new(text).block(status_block);
|
||||||
frame.render_widget(status, area);
|
frame.render_widget(status, area);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Mode::SelectFile => render_file_select(&state, &mut frame, status_block, area),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user