diff --git a/src/Tools/psxreadmap/src/lib.rs b/src/Tools/psxreadmap/src/lib.rs index a65b3c43..86e02c48 100644 --- a/src/Tools/psxreadmap/src/lib.rs +++ b/src/Tools/psxreadmap/src/lib.rs @@ -4,8 +4,9 @@ use std::path::PathBuf; use tool_helper::Error; use tui::{ layout::{Alignment, Constraint, Direction, Layout, Rect}, - style::{Color, Style}, - widgets::{Block, Borders, BorderType, Paragraph} + style::{Color, Modifier, Style}, + text::{Span, Spans}, + widgets::{Block, Borders, BorderType, Paragraph, Tabs} }; pub type EventReceiver = std::sync::mpsc::Receiver>; @@ -14,34 +15,79 @@ pub type Terminal = tui::Terminal { Input(I), Tick, + ForceRender, } pub enum UIState { Alive, + Render, Terminated } +pub enum MenuSelection { + Stats, + Quit, +} + +impl MenuSelection { + fn next(&self) -> MenuSelection { + match self { + Self::Stats => Self::Quit, + Self::Quit => Self::Stats + } + } + + fn prev(&self) -> MenuSelection { + self.next() + } + + fn get_id(&self) -> usize { + match self { + MenuSelection::Stats => 0, + MenuSelection::Quit => 1, + } + } +} + pub struct ConsoleUI { - rx: EventReceiver, - terminal: Terminal, - frame: Rect, - sub_frames: Vec + rx: EventReceiver, + terminal: Terminal, + frame: Rect, + sub_frames: Vec, + menu_selection: MenuSelection, } impl ConsoleUI { pub fn new(rx: EventReceiver, terminal: Terminal) -> ConsoleUI { - ConsoleUI{rx, terminal, frame: Rect::default(), sub_frames: Vec::default()} + ConsoleUI{rx, terminal, frame: Rect::default(), sub_frames: Vec::default(), menu_selection: MenuSelection::Stats} } - pub fn update(&self) -> Result { + pub fn update(&mut self) -> Result { match self.rx.recv()? { Event::Input(event) => { match event.code { KeyCode::Char('q') => Ok(UIState::Terminated), + KeyCode::Left => { + self.menu_selection = self.menu_selection.prev(); + Ok(UIState::Render) + }, + KeyCode::Right => { + self.menu_selection = self.menu_selection.next(); + Ok(UIState::Render) + }, + KeyCode::Enter => { + if matches!(self.menu_selection, MenuSelection::Quit) { + Ok(UIState::Terminated) + } + else { + Ok(UIState::Alive) + } + }, _ => Ok(UIState::Alive) } }, - Event::Tick => Ok(UIState::Alive) + Event::Tick => Ok(UIState::Alive), + Event::ForceRender => Ok(UIState::Render) } } @@ -49,11 +95,15 @@ impl ConsoleUI { self.terminal.draw(|frame| { Self::update_sub_frames(&mut self.sub_frames, &mut self.frame, frame.size()); - frame.render_widget(Self::create_titel(), self.sub_frames[0]); - frame.render_widget(Self::create_titel(), self.sub_frames[1]); - frame.render_widget(Self::create_titel(), self.sub_frames[2]); + frame.render_widget(Self::create_titel(), self.sub_frames[0]); + frame.render_widget(Self::create_menu(&self.menu_selection), self.sub_frames[1]); + frame.render_widget({ + match self.menu_selection { + MenuSelection::Stats => Self::create_titel(), + MenuSelection::Quit => Self::create_exit_message(), + } + }, self.sub_frames[2]); })?; - Ok(()) } @@ -86,6 +136,37 @@ impl ConsoleUI { .border_type(BorderType::Plain) ) } + + fn create_menu<'a>(menu_selection: &MenuSelection) -> Tabs<'a> { + const MENU_TITLES: &'static [&'static str] = &["Stats", "Quit"]; + + let menu = MENU_TITLES.iter().map(|t| { + let (first, rest) = t.split_at(1); + Spans::from(vec![ + Span::styled(first, Style::default().fg(Color::Yellow).add_modifier(Modifier::UNDERLINED),), + Span::styled(rest, Style::default().fg(Color::White)), + ]) + }).collect(); + + Tabs::new(menu).select(menu_selection.get_id()).block( + Block::default() + .title("Menu").borders(Borders::ALL)) + .style(Style::default().fg(Color::White)) + .highlight_style(Style::default().bg(Color::LightYellow)) + .divider(Span::raw("|") + ) + } + + fn create_exit_message<'a>() -> Paragraph<'a> { + Paragraph::new("Press \"ENTER\" to exit") + .style(Style::default().fg(Color::White)) + .alignment(Alignment::Center) + .block(Block::default() + .borders(Borders::ALL) + .style(Style::default().fg(Color::White)) + .border_type(BorderType::Plain).title("QUIT") + ) + } } pub fn load_memory_map(file_path: Option) -> Result, Error> { diff --git a/src/Tools/psxreadmap/src/main.rs b/src/Tools/psxreadmap/src/main.rs index 2e1a742f..2117ea29 100644 --- a/src/Tools/psxreadmap/src/main.rs +++ b/src/Tools/psxreadmap/src/main.rs @@ -1,6 +1,6 @@ use clap::Parser; use crossterm::{event::{self, Event as CEvent}, terminal}; -use psxreadmap::{ConsoleUI, Event, EventReceiver, Terminal, load_memory_map}; +use psxreadmap::{ConsoleUI, Event, EventReceiver, Terminal, load_memory_map, UIState}; use std::{io, path::PathBuf, sync::mpsc, thread, time::{Duration, Instant}}; use tool_helper::{Error, exit_with_error}; use tui::backend::CrosstermBackend; @@ -32,8 +32,12 @@ fn run_main(cmd: CommandLine) -> Result<(), Error> { let terminal = setup_console()?; let mut console_ui = ConsoleUI::new(rx, terminal); - while matches!(console_ui.update()?, psxreadmap::UIState::Alive) { - console_ui.render()?; + loop { + match console_ui.update()? { + UIState::Alive => (), + UIState::Render => {console_ui.render()?;} + UIState::Terminated => break + } } console_ui.close() @@ -45,6 +49,8 @@ fn start_event_loop() -> EventReceiver { let tick_rate = Duration::from_millis(200); thread::spawn(move || { let mut last_tick = Instant::now(); + + tx.send(Event::ForceRender).expect("Send ForceRender command!"); loop { let timeout = tick_rate.checked_sub(last_tick.elapsed()).unwrap_or_else(|| Duration::from_secs(0));