diff --git a/Cargo.toml b/Cargo.toml index 86665e7..f53a03e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -33,6 +33,7 @@ training_mod_consts = { path = "training_mod_consts" } training_mod_tui = { path = "training_mod_tui" } native-tls = { version = "0.2.11", features = ["vendored"] } log = "0.4.17" +byte-unit = "4.0.18" [patch.crates-io] native-tls = { git = "https://github.com/skyline-rs/rust-native-tls", rev = "f202fca" } diff --git a/src/common/menu.rs b/src/common/menu.rs index fcd25fd..d5f26ce 100644 --- a/src/common/menu.rs +++ b/src/common/menu.rs @@ -180,15 +180,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 { - // TODO: This should make more sense, look into. - // BUTTON_PRESSES.a.is_pressed = (*state).Buttons & (1 << 0) > 0; - // BUTTON_PRESSES.b.is_pressed = (*state).Buttons & (1 << 1) > 0; - // BUTTON_PRESSES.zl.is_pressed = (*state).Buttons & (1 << 8) > 0; - // BUTTON_PRESSES.zr.is_pressed = (*state).Buttons & (1 << 9) > 0; - // BUTTON_PRESSES.left.is_pressed = (*state).Buttons & ((1 << 12) | (1 << 16)) > 0; - // BUTTON_PRESSES.right.is_pressed = (*state).Buttons & ((1 << 14) | (1 << 18)) > 0; - // BUTTON_PRESSES.down.is_pressed = (*state).Buttons & ((1 << 15) | (1 << 19)) > 0; - // BUTTON_PRESSES.up.is_pressed = (*state).Buttons & ((1 << 13) | (1 << 17)) > 0; if (*state).Buttons & (1 << 0) > 0 { BUTTON_PRESSES.a.is_pressed = true; diff --git a/src/static/layout.arc b/src/static/layout.arc index cac5d2e..8d22f2b 100644 Binary files a/src/static/layout.arc and b/src/static/layout.arc differ diff --git a/src/training/ui/menu.rs b/src/training/ui/menu.rs index b2e1935..61083fb 100644 --- a/src/training/ui/menu.rs +++ b/src/training/ui/menu.rs @@ -1,9 +1,9 @@ +use std::collections::HashMap; + use lazy_static::lazy_static; use skyline::nn::hid::GetNpadStyleSet; use skyline::nn::ui2d::*; use smash::ui2d::{SmashPane, SmashTextBox}; -use std::collections::HashMap; - use training_mod_tui::gauge::GaugeState; use training_mod_tui::{App, AppPage}; @@ -69,6 +69,7 @@ lazy_static! { unsafe fn render_submenu_page(app: &App, root_pane: &mut Pane) { let tab_selected = app.tab_selected(); let tab = app.menu_items.get(tab_selected).unwrap(); + let submenu_ids = app.submenu_ids(); (0..NUM_MENU_TEXT_OPTIONS) // Valid options in this submenu @@ -78,42 +79,74 @@ unsafe fn render_submenu_page(app: &App, root_pane: &mut Pane) { .find_pane_by_name_recursive(format!("TrModMenuButtonRow{list_idx}").as_str()) .unwrap(); menu_button_row.set_visible(true); + let menu_button = menu_button_row .find_pane_by_name_recursive(format!("Button{list_section}").as_str()) .unwrap(); - menu_button.set_visible(true); + let title_text = menu_button .find_pane_by_name_recursive("TitleTxt") .unwrap() .as_textbox(); + let title_bg = menu_button .find_pane_by_name_recursive("TitleBg") .unwrap() .as_picture(); + let title_bg_material = &mut *title_bg.material; + let list = &tab.lists[list_section]; let submenu = &list.items[list_idx]; let is_selected = list.state.selected().filter(|s| *s == list_idx).is_some(); + title_text.set_text_string(submenu.submenu_title); - let title_bg_material = &mut *title_bg.material; + + // In the actual 'layout.arc' file, every icon image is stacked + // into a single container pane, with each image directly on top of another. + // Hide all icon images, and strategically mark the icon that + // corresponds with a particular button to be visible. + submenu_ids.iter().for_each(|id| { + menu_button + .find_pane_by_name_recursive(id) + .unwrap() + .set_visible(id == &submenu.submenu_id); + }); + + menu_button + .find_pane_by_name_recursive("check") + .unwrap() + .set_visible(false); + if is_selected { root_pane .find_pane_by_name_recursive("FooterTxt") .unwrap() .as_textbox() .set_text_string(submenu.help_text); + title_bg_material.set_white_res_color(BG_LEFT_ON_WHITE_COLOR); title_bg_material.set_black_res_color(BG_LEFT_ON_BLACK_COLOR); + title_text.text_shadow_enable(true); title_text.text_outline_enable(true); + title_text.set_color(255, 255, 255, 255); } else { title_bg_material.set_white_res_color(BG_LEFT_OFF_WHITE_COLOR); title_bg_material.set_black_res_color(BG_LEFT_OFF_BLACK_COLOR); + title_text.text_shadow_enable(false); title_text.text_outline_enable(false); + title_text.set_color(178, 199, 211, 255); } + + menu_button.set_visible(true); + menu_button + .find_pane_by_name_recursive("Icon") + .unwrap() + .set_visible(true); }); } @@ -130,6 +163,7 @@ unsafe fn render_toggle_page(app: &App, root_pane: &mut Pane) { .find_pane_by_name_recursive(format!("TrModMenuButtonRow{list_idx}").as_str()) .unwrap(); menu_button_row.set_visible(true); + let menu_button = menu_button_row .find_pane_by_name_recursive(format!("Button{list_section}").as_str()) .unwrap(); @@ -139,43 +173,52 @@ unsafe fn render_toggle_page(app: &App, root_pane: &mut Pane) { .find_pane_by_name_recursive("TitleTxt") .unwrap() .as_textbox(); + let title_bg = menu_button .find_pane_by_name_recursive("TitleBg") .unwrap() .as_picture(); - let value_text = menu_button - .find_pane_by_name_recursive("ValueTxt") - .unwrap() - .as_textbox(); let is_selected = sub_menu_state .selected() .filter(|s| *s == list_idx) .is_some(); + + let submenu_ids = app.submenu_ids(); + + submenu_ids.iter().for_each(|id| { + menu_button + .find_pane_by_name_recursive(id) + .unwrap() + .set_visible(false) + }); + title_text.set_text_string(name); + menu_button.find_pane_by_name_recursive("check") + .unwrap() + .set_visible(true); + + menu_button.find_pane_by_name_recursive("Icon").unwrap().set_visible(*checked); + + let title_bg_material = &mut *title_bg.material; + if is_selected { title_text.text_shadow_enable(true); title_text.text_outline_enable(true); - title_text.set_color(255, 255, 255, 255); - } else { - title_text.text_shadow_enable(false); - title_text.text_outline_enable(false); - title_text.set_color(178, 199, 211, 255); - } - let title_bg_material = &mut *title_bg.material; - if is_selected { + title_text.set_color(255, 255, 255, 255); + title_bg_material.set_white_res_color(BG_LEFT_ON_WHITE_COLOR); title_bg_material.set_black_res_color(BG_LEFT_ON_BLACK_COLOR); } else { + title_text.text_shadow_enable(false); + title_text.text_outline_enable(false); + + title_text.set_color(178, 199, 211, 255); + title_bg_material.set_white_res_color(BG_LEFT_OFF_WHITE_COLOR); title_bg_material.set_black_res_color(BG_LEFT_OFF_BLACK_COLOR); } - - if *checked { - value_text.set_text_string("X"); - value_text.set_visible(true); - } }); }); } @@ -282,6 +325,9 @@ unsafe fn render_slider_page(app: &App, root_pane: &mut Pane) { max_title_bg_material.set_white_res_color(max_colors.0); max_title_bg_material.set_black_res_color(max_colors.1); + + min_value_text.set_visible(true); + max_value_text.set_visible(true); } pub unsafe fn draw(root_pane: &mut Pane) { @@ -326,6 +372,7 @@ pub unsafe fn draw(root_pane: &mut Pane) { .find_pane_by_name_recursive(format!("TrModMenuButtonRow{row_idx}").as_str()) .unwrap(); menu_button_row.set_visible(false); + let menu_button = menu_button_row .find_pane_by_name_recursive(format!("Button{col_idx}").as_str()) .unwrap(); diff --git a/src/training/ui/mod.rs b/src/training/ui/mod.rs index d8a2cab..5ef6e12 100644 --- a/src/training/ui/mod.rs +++ b/src/training/ui/mod.rs @@ -1,3 +1,4 @@ +use byte_unit::MEBIBYTE; use sarc::SarcFile; use skyline::nn::ui2d::*; use training_mod_consts::{OnOff, MENU}; @@ -33,10 +34,13 @@ pub unsafe fn handle_draw(layout: *mut Layout, draw_info: u64, cmd_buffer: u64) original!()(layout, draw_info, cmd_buffer); } -// We'll keep some sane max size here; we shouldn't reach above 600KiB is the idea, -// but we can try higher if we need to. +// Allocate a static amount of memory that Smash isn't allowed to deallocate, +// in order for us to be able to swap the 'layout.arc' with the current +// version of the file in between loads of training mode. #[cfg(feature = "layout_arc_from_file")] -static mut LAYOUT_ARC: &mut [u8; 600000] = &mut [0u8; 600000]; +const LAYOUT_ARC_SIZE: usize = (2 * MEBIBYTE) as usize; +#[cfg(feature = "layout_arc_from_file")] +static mut LAYOUT_ARC: &mut [u8; LAYOUT_ARC_SIZE] = &mut [0u8; LAYOUT_ARC_SIZE]; /// We are editing the info_training/layout.arc and replacing the original file with our /// modified version from `LAYOUT_ARC_PATH` diff --git a/training_mod_tui/src/lib.rs b/training_mod_tui/src/lib.rs index 721db83..24431d6 100644 --- a/training_mod_tui/src/lib.rs +++ b/training_mod_tui/src/lib.rs @@ -578,6 +578,23 @@ impl<'a> App<'a> { defaults_menu: serde_json::from_str(self.default_menu.1.clone().as_str()).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::>(); + } } fn render_submenu_page(f: &mut Frame, app: &mut App, list_chunks: Vec, help_chunk: Rect) {