diff --git a/src/common/menu.rs b/src/common/menu.rs index d5f26ce..b12e313 100644 --- a/src/common/menu.rs +++ b/src/common/menu.rs @@ -80,6 +80,7 @@ pub struct ButtonPresses { pub a: ButtonPress, pub b: ButtonPress, pub x: ButtonPress, + pub y: ButtonPress, pub r: ButtonPress, pub l: ButtonPress, pub zr: ButtonPress, @@ -133,6 +134,11 @@ pub static mut BUTTON_PRESSES: ButtonPresses = ButtonPresses { is_pressed: false, lockout_frames: 0, }, + y: ButtonPress { + prev_frame_is_pressed: false, + is_pressed: false, + lockout_frames: 0, + }, r: ButtonPress { prev_frame_is_pressed: false, is_pressed: false, @@ -180,7 +186,6 @@ pub fn handle_get_npad_state(state: *mut NpadGcState, _controller_id: *const u32 let update_count = (*state).updateCount; let flags = (*state).Flags; if QUICK_MENU_ACTIVE { - if (*state).Buttons & (1 << 0) > 0 { BUTTON_PRESSES.a.is_pressed = true; } @@ -190,6 +195,9 @@ pub fn handle_get_npad_state(state: *mut NpadGcState, _controller_id: *const u32 if (*state).Buttons & (1 << 2) > 0 { BUTTON_PRESSES.x.is_pressed = true; } + if (*state).Buttons & (1 << 3) > 0 { + BUTTON_PRESSES.y.is_pressed = true; + } if (*state).Buttons & (1 << 6) > 0 { BUTTON_PRESSES.l.is_pressed = true; } @@ -239,6 +247,12 @@ lazy_static! { ); } +pub unsafe fn p1_controller_is_gcc() -> bool { + let p1_controller_id = crate::training::input_delay::p1_controller_id(); + let p1_style_set = GetNpadStyleSet(&p1_controller_id as *const _); + (p1_style_set.flags & (1 << 5)) > 0 +} + pub unsafe fn quick_menu_loop() { loop { std::thread::sleep(std::time::Duration::from_secs(10)); @@ -262,6 +276,8 @@ pub unsafe fn quick_menu_loop() { continue; } + let is_gcc = p1_controller_is_gcc(); + let app = &mut *QUICK_MENU_APP.data_ptr(); button_presses.a.read_press().then(|| { app.on_a(); @@ -282,23 +298,39 @@ pub unsafe fn quick_menu_loop() { } }); button_presses.x.read_press().then(|| { - app.on_x(); + app.save_defaults(); + received_input = true; + }); + button_presses.y.read_press().then(|| { + app.reset_current_submenu(); received_input = true; }); button_presses.l.read_press().then(|| { - app.on_l(); + if is_gcc { + app.previous_tab(); + } received_input = true; }); button_presses.r.read_press().then(|| { - app.on_r(); + if is_gcc { + app.next_tab(); + } else { + app.reset_all_submenus(); + } received_input = true; }); button_presses.zl.read_press().then(|| { - app.on_zl(); + if !is_gcc { + app.previous_tab(); + } received_input = true; }); button_presses.zr.read_press().then(|| { - app.on_zr(); + if !is_gcc { + app.next_tab(); + } else { + app.reset_all_submenus(); + } received_input = true; }); button_presses.left.read_press().then(|| { diff --git a/src/training/ui/menu.rs b/src/training/ui/menu.rs index 61083fb..afdddca 100644 --- a/src/training/ui/menu.rs +++ b/src/training/ui/menu.rs @@ -1,7 +1,6 @@ use std::collections::HashMap; use lazy_static::lazy_static; -use skyline::nn::hid::GetNpadStyleSet; use skyline::nn::ui2d::*; use smash::ui2d::{SmashPane, SmashTextBox}; use training_mod_tui::gauge::GaugeState; @@ -55,12 +54,18 @@ const BG_LEFT_SELECTED_WHITE_COLOR: ResColor = ResColor { }; lazy_static! { - static ref GCC_BUTTON_MAPPING: HashMap<&'static str, u16> = - HashMap::from([("L", 0xE204), ("R", 0xE205), ("X", 0xE206), ("Z", 0xE208)]); + static ref GCC_BUTTON_MAPPING: HashMap<&'static str, u16> = HashMap::from([ + ("L", 0xE204), + ("R", 0xE205), + ("X", 0xE206), + ("Y", 0xE207), + ("Z", 0xE208) + ]); static ref PROCON_BUTTON_MAPPING: HashMap<&'static str, u16> = HashMap::from([ ("L", 0xE0E4), ("R", 0xE0E5), ("X", 0xE0E2), + ("Y", 0xE0E3), ("ZL", 0xE0E6), ("ZR", 0xE0E7) ]); @@ -138,7 +143,7 @@ unsafe fn render_submenu_page(app: &App, root_pane: &mut Pane) { title_text.text_shadow_enable(false); title_text.text_outline_enable(false); - + title_text.set_color(178, 199, 211, 255); } @@ -194,11 +199,15 @@ unsafe fn render_toggle_page(app: &App, root_pane: &mut Pane) { }); title_text.set_text_string(name); - menu_button.find_pane_by_name_recursive("check") + menu_button + .find_pane_by_name_recursive("check") .unwrap() .set_visible(true); - menu_button.find_pane_by_name_recursive("Icon").unwrap().set_visible(*checked); + menu_button + .find_pane_by_name_recursive("Icon") + .unwrap() + .set_visible(*checked); let title_bg_material = &mut *title_bg.material; @@ -403,17 +412,16 @@ pub unsafe fn draw(root_pane: &mut Pane) { }; let tab_titles = [prev_tab, tab_selected, next_tab].map(|idx| app_tabs[idx]); - let p1_controller_id = crate::training::input_delay::p1_controller_id(); - let p1_style_set = GetNpadStyleSet(&p1_controller_id as *const _); - let is_gcc = (p1_style_set.flags & (1 << 5)) > 0; + let is_gcc = common::menu::p1_controller_is_gcc(); let button_mapping = if is_gcc { GCC_BUTTON_MAPPING.clone() } else { PROCON_BUTTON_MAPPING.clone() }; - let (x_key, l_key, r_key, zl_key, zr_key, z_key) = ( + let (x_key, y_key, l_key, r_key, zl_key, zr_key, z_key) = ( button_mapping.get("X"), + button_mapping.get("Y"), button_mapping.get("L"), button_mapping.get("R"), button_mapping.get("ZL"), @@ -423,9 +431,9 @@ pub unsafe fn draw(root_pane: &mut Pane) { let (left_tab_key, right_tab_key, save_defaults_key, reset_current_key, reset_all_key) = if is_gcc { - (None, z_key, x_key, l_key, r_key) + (l_key, r_key, x_key, y_key, z_key) } else { - (zl_key, zr_key, x_key, l_key, r_key) + (zl_key, zr_key, x_key, y_key, r_key) }; [ @@ -465,12 +473,12 @@ pub unsafe fn draw(root_pane: &mut Pane) { help_pane.set_text_string(tab_titles[idx]); }); [ - (save_defaults_key, "SaveDefaults"), - (reset_current_key, "ResetCurrentDefaults"), - (reset_all_key, "ResetAllDefaults"), + (save_defaults_key, "SaveDefaults", "Save Defaults"), + (reset_current_key, "ResetCurrentDefaults", "Reset Current"), + (reset_all_key, "ResetAllDefaults", "Reset All"), ] .iter() - .for_each(|(key, name)| { + .for_each(|(key, name, title)| { let key_help_pane = root_pane.find_pane_by_name_recursive(name).unwrap(); let icon_pane = key_help_pane @@ -483,26 +491,11 @@ pub unsafe fn draw(root_pane: &mut Pane) { *it = *key.unwrap(); *(it.add(1)) = 0x0; - // PascalCase to Title Case - let title_case = name - .chars() - .fold(vec![], |mut acc, ch| { - if ch.is_uppercase() { - acc.push(String::new()); - } - if let Some(last) = acc.last_mut() { - last.push(ch); - } - acc - }) - .into_iter() - .collect::>() - .join(" "); key_help_pane .find_pane_by_name_recursive("set_txt_help") .unwrap() .as_textbox() - .set_text_string(title_case.as_str()); + .set_text_string(title); }); match app.page { diff --git a/src/training/ui/mod.rs b/src/training/ui/mod.rs index 5ef6e12..7a2262c 100644 --- a/src/training/ui/mod.rs +++ b/src/training/ui/mod.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "layout_arc_from_file")] use byte_unit::MEBIBYTE; use sarc::SarcFile; use skyline::nn::ui2d::*; @@ -22,6 +23,8 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64) // InfluencedAlpha means "Should my children panes' alpha be influenced by mine, as the parent?" root_pane.flags |= 1 << PaneFlag::InfluencedAlpha as u8; root_pane.set_visible(MENU.hud == OnOff::On); + } else { + root_pane.set_visible(true); } damage::draw(root_pane, layout_name); diff --git a/training_mod_tui/src/lib.rs b/training_mod_tui/src/lib.rs index 5ad24e4..4e3362b 100644 --- a/training_mod_tui/src/lib.rs +++ b/training_mod_tui/src/lib.rs @@ -1,4 +1,6 @@ -use training_mod_consts::{MenuJsonStruct, Slider, SubMenu, SubMenuType, Toggle, UiMenu, ui_menu, TrainingModpackMenu}; +use training_mod_consts::{ + ui_menu, MenuJsonStruct, Slider, SubMenu, SubMenuType, Toggle, TrainingModpackMenu, UiMenu, +}; use tui::{ backend::Backend, layout::{Constraint, Corner, Direction, Layout, Rect}, @@ -8,8 +10,8 @@ use tui::{ Frame, }; +use serde_json::{json, Map}; use std::collections::HashMap; -use serde_json::{Map, json}; pub use tui::{backend::TestBackend, style::Color, Terminal}; pub mod gauge; @@ -25,7 +27,7 @@ pub enum AppPage { SUBMENU, TOGGLE, SLIDER, - CONFIRMATION + CONFIRMATION, } /// We should hold a list of SubMenus. @@ -58,7 +60,7 @@ impl<'a> App<'a> { selected_sub_menu_toggles: MultiStatefulList::with_items(vec![], 0), selected_sub_menu_slider: DoubleEndedGauge::new(), page: AppPage::SUBMENU, - default_menu: default_menu + default_menu: default_menu, }; app.set_sub_menu_items(); app @@ -224,15 +226,13 @@ impl<'a> App<'a> { } /// Returns information about the currently selected submenu - /// + /// /// 0: Submenu Title /// 1: Submenu Help Text /// 2: Vec(toggle checked, title) for toggles, Vec(nothing) for slider /// 3: ListState for toggles, ListState::new() for slider /// TODO: Refactor return type into a nice struct - pub fn sub_menu_strs_and_states( - &self, - ) -> (&str, &str, Vec<(Vec<(bool, &str)>, ListState)>) { + pub fn sub_menu_strs_and_states(&self) -> (&str, &str, Vec<(Vec<(bool, &str)>, ListState)>) { ( self.sub_menu_selected().submenu_title, self.sub_menu_selected().help_text, @@ -293,8 +293,7 @@ impl<'a> App<'a> { .get(tab_selected) .unwrap() .idx_to_list_idx(self.menu_items.get(tab_selected).unwrap().state); - let selected_sub_menu = self.menu_items.get_mut(tab_selected).unwrap().lists - [list_section] + let selected_sub_menu = self.menu_items.get_mut(tab_selected).unwrap().lists[list_section] .items .get_mut(list_idx) .unwrap(); @@ -305,7 +304,7 @@ impl<'a> App<'a> { self.page = AppPage::SLIDER; self.selected_sub_menu_slider.state = GaugeState::MinHover; } - SubMenuType::TOGGLE => self.page = AppPage::TOGGLE + SubMenuType::TOGGLE => self.page = AppPage::TOGGLE, } } else { match SubMenuType::from_str(selected_sub_menu._type) { @@ -323,7 +322,9 @@ impl<'a> App<'a> { if !o.checked { o.checked = true; } else { - if is_single_option { return; } + if is_single_option { + return; + } o.checked = false; } } else if is_single_option { @@ -341,7 +342,9 @@ impl<'a> App<'a> { if !o.checked { o.checked = true; } else { - if is_single_option { return; } + if is_single_option { + return; + } o.checked = false; } } else if is_single_option { @@ -358,7 +361,7 @@ impl<'a> App<'a> { } GaugeState::MinSelected => { self.selected_sub_menu_slider.state = GaugeState::MinHover; - selected_sub_menu.slider = Some(Slider{ + selected_sub_menu.slider = Some(Slider { selected_min: self.selected_sub_menu_slider.selected_min, selected_max: self.selected_sub_menu_slider.selected_max, abs_min: self.selected_sub_menu_slider.abs_min, @@ -367,7 +370,7 @@ impl<'a> App<'a> { } GaugeState::MaxSelected => { self.selected_sub_menu_slider.state = GaugeState::MaxHover; - selected_sub_menu.slider = Some(Slider{ + selected_sub_menu.slider = Some(Slider { selected_min: self.selected_sub_menu_slider.selected_min, selected_max: self.selected_sub_menu_slider.selected_max, abs_min: self.selected_sub_menu_slider.abs_min, @@ -405,7 +408,7 @@ impl<'a> App<'a> { SubMenuType::SLIDER => match self.selected_sub_menu_slider.state { GaugeState::MinSelected => { self.selected_sub_menu_slider.state = GaugeState::MinHover; - selected_sub_menu.slider = Some(Slider{ + selected_sub_menu.slider = Some(Slider { selected_min: self.selected_sub_menu_slider.selected_min, selected_max: self.selected_sub_menu_slider.selected_max, abs_min: self.selected_sub_menu_slider.abs_min, @@ -416,7 +419,7 @@ impl<'a> App<'a> { } GaugeState::MaxSelected => { self.selected_sub_menu_slider.state = GaugeState::MaxHover; - selected_sub_menu.slider = Some(Slider{ + selected_sub_menu.slider = Some(Slider { selected_min: self.selected_sub_menu_slider.selected_min, selected_max: self.selected_sub_menu_slider.selected_max, abs_min: self.selected_sub_menu_slider.abs_min, @@ -434,23 +437,27 @@ impl<'a> App<'a> { } /// Save defaults command - pub fn on_x(&mut self) { + pub fn save_defaults(&mut self) { if self.page == AppPage::SUBMENU { let json = self.to_json(); unsafe { - self.default_menu = (ui_menu(serde_json::from_str::(&json).unwrap()), json); + self.default_menu = ( + ui_menu(serde_json::from_str::(&json).unwrap()), + json, + ); } } } /// Reset current submenu to defaults - pub fn on_l(&mut self) { + pub fn reset_current_submenu(&mut self) { if self.page == AppPage::TOGGLE || self.page == AppPage::SLIDER { let json = self.to_json(); let mut json_value = serde_json::from_str::(&json).unwrap(); - let selected_sub_menu= self.sub_menu_selected(); + let selected_sub_menu = self.sub_menu_selected(); let id = selected_sub_menu.submenu_id; - let default_json_value = serde_json::from_str::(&self.default_menu.1).unwrap(); + let default_json_value = + serde_json::from_str::(&self.default_menu.1).unwrap(); *json_value.get_mut(id).unwrap() = default_json_value.get(id).unwrap().clone(); let new_menu = serde_json::from_value::(json_value).unwrap(); *self = App::new(unsafe { ui_menu(new_menu) }, self.default_menu.clone()); @@ -458,18 +465,18 @@ impl<'a> App<'a> { } /// Reset all menus to defaults - pub fn on_r(&mut self) { + pub fn reset_all_submenus(&mut self) { *self = App::new(self.default_menu.0.clone(), self.default_menu.clone()); } - pub fn on_zl(&mut self) { + pub fn previous_tab(&mut self) { if self.page == AppPage::SUBMENU { self.tabs.previous(); self.set_sub_menu_items(); } } - pub fn on_zr(&mut self) { + pub fn next_tab(&mut self) { if self.page == AppPage::SUBMENU { self.tabs.next(); self.set_sub_menu_items(); @@ -571,35 +578,40 @@ impl<'a> App<'a> { serde_json::to_string(&settings).unwrap() } - /// Returns the current menu selections and the default menu selections. pub fn get_menu_selections(&self) -> String { - serde_json::to_string( - &MenuJsonStruct { + serde_json::to_string(&MenuJsonStruct { menu: serde_json::from_str(self.to_json().as_str()).unwrap(), defaults_menu: serde_json::from_str(self.default_menu.1.clone().as_str()).unwrap(), - }).unwrap() + }) + .unwrap() } pub fn submenu_ids(&self) -> Vec<&str> { - return self.menu_items - .values() - .flat_map(|multi_stateful_list| { - multi_stateful_list - .lists - .iter() - .flat_map(|sub_stateful_list| { - sub_stateful_list - .items - .iter() - .map(|submenu| submenu.submenu_id) - }) - }) - .collect::>(); + return self + .menu_items + .values() + .flat_map(|multi_stateful_list| { + multi_stateful_list + .lists + .iter() + .flat_map(|sub_stateful_list| { + sub_stateful_list + .items + .iter() + .map(|submenu| submenu.submenu_id) + }) + }) + .collect::>(); } } -fn render_submenu_page(f: &mut Frame, app: &mut App, list_chunks: Vec, help_chunk: Rect) { +fn render_submenu_page( + f: &mut Frame, + app: &mut App, + list_chunks: Vec, + help_chunk: Rect, +) { let tab_selected = app.tab_selected(); let mut item_help = None; for (list_section, stateful_list) in app @@ -648,11 +660,16 @@ fn render_submenu_page(f: &mut Frame, app: &mut App, list_chunks: item_help.unwrap_or("").replace('\"', "") + "\nZL/ZR: Next tab | X: Save Defaults | R: Reset All Menus", ) - .style(Style::default().fg(Color::Cyan)); + .style(Style::default().fg(Color::Cyan)); f.render_widget(help_paragraph, help_chunk); } -pub fn render_toggle_page(f: &mut Frame, app: &mut App, list_chunks: Vec, help_chunk: Rect) { +pub fn render_toggle_page( + f: &mut Frame, + app: &mut App, + list_chunks: Vec, + help_chunk: Rect, +) { let (title, help_text, mut sub_menu_str_lists) = app.sub_menu_strs_and_states(); for list_section in 0..sub_menu_str_lists.len() { let sub_menu_str = sub_menu_str_lists[list_section].0.clone(); @@ -677,15 +694,17 @@ pub fn render_toggle_page(f: &mut Frame, app: &mut App, list_chun .highlight_symbol(">> "); f.render_stateful_widget(values_list, list_chunks[list_section], sub_menu_state); } - let help_paragraph = Paragraph::new( - help_text.replace('\"', "") + "\nL: Reset Current Menu", - ) + let help_paragraph = Paragraph::new(help_text.replace('\"', "") + "\nL: Reset Current Menu") .style(Style::default().fg(Color::Cyan)); f.render_widget(help_paragraph, help_chunk); } - -pub fn render_slider_page(f: &mut Frame, app: &mut App, vertical_chunk: Rect, help_chunk: Rect) { +pub fn render_slider_page( + f: &mut Frame, + app: &mut App, + vertical_chunk: Rect, + help_chunk: Rect, +) { let (_title, help_text, gauge_vals) = app.sub_menu_strs_for_slider(); let abs_min = gauge_vals.abs_min; let abs_max = gauge_vals.abs_max; @@ -693,9 +712,18 @@ pub fn render_slider_page(f: &mut Frame, app: &mut App, vertical_ let selected_max = gauge_vals.selected_max; let lbl_ratio = 0.95; // Needed so that the upper limit label is visible let constraints = [ - Constraint::Ratio((lbl_ratio * (selected_min-abs_min) as f32) as u32, abs_max-abs_min), - Constraint::Ratio((lbl_ratio * (selected_max-selected_min) as f32) as u32, abs_max-abs_min), - Constraint::Ratio((lbl_ratio * (abs_max-selected_max) as f32) as u32, abs_max-abs_min), + Constraint::Ratio( + (lbl_ratio * (selected_min - abs_min) as f32) as u32, + abs_max - abs_min, + ), + Constraint::Ratio( + (lbl_ratio * (selected_max - selected_min) as f32) as u32, + abs_max - abs_min, + ), + Constraint::Ratio( + (lbl_ratio * (abs_max - selected_max) as f32) as u32, + abs_max - abs_min, + ), Constraint::Min(3), // For upper limit label ]; let gauge_chunks = Layout::default() @@ -703,12 +731,7 @@ pub fn render_slider_page(f: &mut Frame, app: &mut App, vertical_ .constraints(constraints) .split(vertical_chunk); - let slider_lbls = [ - abs_min, - selected_min, - selected_max, - abs_max, - ]; + let slider_lbls = [abs_min, selected_min, selected_max, abs_max]; for (idx, lbl) in slider_lbls.iter().enumerate() { let mut line_set = tui::symbols::line::NORMAL; line_set.horizontal = "-"; @@ -721,24 +744,16 @@ pub fn render_slider_page(f: &mut Frame, app: &mut App, vertical_ if idx == 1 { // Slider between selected_min and selected_max match gauge_vals.state { - GaugeState::MinHover => { - gauge = gauge.style(Style::default().fg(Color::Red)) - } - GaugeState::MinSelected => { - gauge = gauge.style(Style::default().fg(Color::Green)) - } + GaugeState::MinHover => gauge = gauge.style(Style::default().fg(Color::Red)), + GaugeState::MinSelected => gauge = gauge.style(Style::default().fg(Color::Green)), _ => {} } gauge = gauge.gauge_style(Style::default().fg(Color::Yellow).bg(Color::Black)); } else if idx == 2 { // Slider between selected_max and abs_max match gauge_vals.state { - GaugeState::MaxHover => { - gauge = gauge.style(Style::default().fg(Color::Red)) - } - GaugeState::MaxSelected => { - gauge = gauge.style(Style::default().fg(Color::Green)) - } + GaugeState::MaxHover => gauge = gauge.style(Style::default().fg(Color::Red)), + GaugeState::MaxSelected => gauge = gauge.style(Style::default().fg(Color::Green)), _ => {} } } else if idx == 3 { @@ -762,9 +777,7 @@ pub fn render_slider_page(f: &mut Frame, app: &mut App, vertical_ f.render_widget(gauge, gauge_chunks[idx]); } - let help_paragraph = Paragraph::new( - help_text.replace('\"', "") + "\nL: Reset Current Menu", - ) + let help_paragraph = Paragraph::new(help_text.replace('\"', "") + "\nL: Reset Current Menu") .style(Style::default().fg(Color::Cyan)); f.render_widget(help_paragraph, help_chunk); } @@ -846,22 +859,20 @@ pub fn ui(f: &mut Frame, app: &mut App) { // is not publicly exposed, and the attribute defaults to true. // https://github.com/fdehau/tui-rs/blob/v0.19.0/src/layout.rs#L121 let vertical_chunks: Vec = vertical_chunks - .iter() - .map(|chunk| { - Layout::default() - .direction(Direction::Horizontal) - .constraints( - [ - Constraint::Length(NX_TUI_WIDTH), // Width of the TUI terminal - Constraint::Min(0), // Fill the remainder margin - ] - .as_ref(), - ) - .split(*chunk)[0] - } - ) - .collect(); - + .iter() + .map(|chunk| { + Layout::default() + .direction(Direction::Horizontal) + .constraints( + [ + Constraint::Length(NX_TUI_WIDTH), // Width of the TUI terminal + Constraint::Min(0), // Fill the remainder margin + ] + .as_ref(), + ) + .split(*chunk)[0] + }) + .collect(); let list_chunks = Layout::default() .direction(Direction::Horizontal) @@ -881,6 +892,6 @@ pub fn ui(f: &mut Frame, app: &mut App) { AppPage::SUBMENU => render_submenu_page(f, app, list_chunks, vertical_chunks[2]), AppPage::SLIDER => render_slider_page(f, app, vertical_chunks[1], vertical_chunks[2]), AppPage::TOGGLE => render_toggle_page(f, app, list_chunks, vertical_chunks[2]), - AppPage::CONFIRMATION => todo!() + AppPage::CONFIRMATION => todo!(), } -} \ No newline at end of file +} diff --git a/training_mod_tui/src/main.rs b/training_mod_tui/src/main.rs index 1e72b63..033a958 100644 --- a/training_mod_tui/src/main.rs +++ b/training_mod_tui/src/main.rs @@ -5,21 +5,28 @@ use crossterm::{ terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen}, }; -#[cfg(feature = "has_terminal")] -use tui::backend::CrosstermBackend; +use std::error::Error; #[cfg(feature = "has_terminal")] use std::{ io, time::{Duration, Instant}, }; -use std::error::Error; +#[cfg(feature = "has_terminal")] +use tui::backend::CrosstermBackend; use tui::Terminal; use training_mod_consts::*; -fn test_backend_setup<'a>(ui_menu: UiMenu<'a>, menu_defaults: (UiMenu<'a>, String)) -> Result< - (Terminal, training_mod_tui::App<'a>), - Box> { +fn test_backend_setup<'a>( + ui_menu: UiMenu<'a>, + menu_defaults: (UiMenu<'a>, String), +) -> Result< + ( + Terminal, + training_mod_tui::App<'a>, + ), + Box, +> { let app = training_mod_tui::App::<'a>::new(ui_menu, menu_defaults); let backend = tui::backend::TestBackend::new(75, 15); let terminal = Terminal::new(backend)?; @@ -103,7 +110,7 @@ fn test_save_and_reset_defaults() -> Result<(), Box> { // Return to submenu selection app.on_b(); // Save Defaults - app.on_x(); + app.save_defaults(); // Enter Mash Toggles again app.on_a(); // Unset Mash Airdodge @@ -127,7 +134,7 @@ fn test_save_and_reset_defaults() -> Result<(), Box> { ); // Reset current menu alone to defaults - app.on_l(); + app.reset_current_submenu(); let menu_json = app.get_menu_selections(); let menu_struct = serde_json::from_str::(&menu_json).unwrap(); let menu = menu_struct.menu; @@ -155,7 +162,7 @@ fn test_save_and_reset_defaults() -> Result<(), Box> { // Return to submenu selection app.on_b(); // Save defaults - app.on_x(); + app.save_defaults(); // Go back in and unset Jump app.on_a(); app.on_down(); @@ -163,7 +170,7 @@ fn test_save_and_reset_defaults() -> Result<(), Box> { // Return to submenu selection app.on_b(); // Reset all to defaults - app.on_r(); + app.reset_all_submenus(); let menu_json = app.get_menu_selections(); let menu_struct = serde_json::from_str::(&menu_json).unwrap(); let menu = menu_struct.menu; @@ -177,7 +184,6 @@ fn test_save_and_reset_defaults() -> Result<(), Box> { "The menu should have Mash Airdodge off and Followup Jump on" ); - Ok(()) } @@ -192,16 +198,19 @@ fn main() -> Result<(), Box> { menu_defaults = (ui_menu(MENU), serde_json::to_string(&MENU).unwrap()); } - #[cfg(not(feature = "has_terminal"))] { + #[cfg(not(feature = "has_terminal"))] + { let (mut terminal, mut app) = test_backend_setup(menu, menu_defaults)?; if inputs.is_some() { - inputs.unwrap().split(",").for_each(|input| { - match input.to_uppercase().as_str() { - "X" => app.on_x(), - "L" => app.on_l(), - "R" => app.on_r(), - "O" => app.on_zl(), - "P" => app.on_zr(), + inputs + .unwrap() + .split(",") + .for_each(|input| match input.to_uppercase().as_str() { + "X" => app.save_defaults(), + "Y" => app.reset_current_submenu(), + "Z" => app.reset_all_submenus(), + "L" => app.previous_tab(), + "R" => app.next_tab(), "A" => app.on_a(), "B" => app.on_b(), "UP" => app.on_up(), @@ -209,8 +218,7 @@ fn main() -> Result<(), Box> { "LEFT" => app.on_left(), "RIGHT" => app.on_right(), _ => {} - } - }) + }) } let frame_res = terminal.draw(|f| training_mod_tui::ui(f, &mut app))?; let menu_json = app.get_menu_selections(); @@ -226,7 +234,8 @@ fn main() -> Result<(), Box> { println!("Menu:\n{menu_json}"); } - #[cfg(feature = "has_terminal")] { + #[cfg(feature = "has_terminal")] + { let app = training_mod_tui::App::new(menu, menu_defaults); // setup terminal @@ -281,11 +290,11 @@ fn run_app( if let Event::Key(key) = event::read()? { match key.code { KeyCode::Char('q') => return Ok(menu_json), - KeyCode::Char('x') => app.on_x(), - KeyCode::Char('p') => app.on_zr(), - KeyCode::Char('o') => app.on_zl(), - KeyCode::Char('r') => app.on_r(), - KeyCode::Char('l') => app.on_l(), + KeyCode::Char('x') => app.save_defaults(), + KeyCode::Char('p') => app.reset_current_submenu(), + KeyCode::Char('o') => app.reset_all_submenus(), + KeyCode::Char('r') => app.next_tab(), + KeyCode::Char('l') => app.previous_tab(), KeyCode::Left => app.on_left(), KeyCode::Right => app.on_right(), KeyCode::Down => app.on_down(),