mirror of
https://github.com/jugeeya/UltimateTrainingModpack.git
synced 2024-11-27 20:34:03 +00:00
Tabbed Web Menu (#333)
* Web menu refactor * Fix some menu items * Fixes for quick_menu, general clippy fixes * Revert small testing change * Add quick menu SVG * Fix defaults saving/loading * Log the last URL from the web menu Co-authored-by: jugeeya <jugeeya@live.com>
This commit is contained in:
parent
6da6aa41b7
commit
a6bed95de3
11 changed files with 1137 additions and 1466 deletions
|
@ -66,10 +66,10 @@ pub unsafe fn write_menu() {
|
|||
|
||||
const MENU_CONF_PATH: &str = "sd:/TrainingModpack/training_modpack_menu.conf";
|
||||
|
||||
pub fn set_menu_from_url(orig_last_url: &str) {
|
||||
let last_url = &orig_last_url.replace("&save_defaults=1", "");
|
||||
pub fn set_menu_from_url(last_url: &str) {
|
||||
unsafe {
|
||||
MENU = get_menu_from_url(MENU, last_url);
|
||||
MENU = get_menu_from_url(MENU, last_url, false);
|
||||
DEFAULTS_MENU = get_menu_from_url(MENU, last_url, true);
|
||||
|
||||
if MENU.quick_menu == OnOff::Off {
|
||||
if is_emulator() {
|
||||
|
@ -83,17 +83,6 @@ pub fn set_menu_from_url(orig_last_url: &str) {
|
|||
}
|
||||
}
|
||||
|
||||
if last_url.len() != orig_last_url.len() {
|
||||
// Save as default
|
||||
unsafe {
|
||||
DEFAULT_MENU = MENU;
|
||||
write_menu();
|
||||
}
|
||||
let menu_defaults_conf_path = "sd:/TrainingModpack/training_modpack_menu_defaults.conf";
|
||||
std::fs::write(menu_defaults_conf_path, last_url)
|
||||
.expect("Failed to write default menu conf file");
|
||||
}
|
||||
|
||||
std::fs::write(MENU_CONF_PATH, last_url).expect("Failed to write menu conf file");
|
||||
unsafe {
|
||||
EVENT_QUEUE.push(Event::menu_open(last_url.to_string()));
|
||||
|
@ -115,19 +104,22 @@ pub fn spawn_menu() {
|
|||
|
||||
if !quick_menu {
|
||||
let fname = "training_menu.html";
|
||||
let params = unsafe { MENU.to_url_params() };
|
||||
let page_response = Webpage::new()
|
||||
.background(Background::BlurredScreenshot)
|
||||
.htdocs_dir("training_modpack")
|
||||
.boot_display(BootDisplay::BlurredScreenshot)
|
||||
.boot_icon(true)
|
||||
.start_page(&format!("{}{}", fname, params))
|
||||
.open()
|
||||
.unwrap();
|
||||
unsafe {
|
||||
let params = MENU.to_url_params(false);
|
||||
let default_params = DEFAULTS_MENU.to_url_params(true);
|
||||
let page_response = Webpage::new()
|
||||
.background(Background::BlurredScreenshot)
|
||||
.htdocs_dir("training_modpack")
|
||||
.boot_display(BootDisplay::BlurredScreenshot)
|
||||
.boot_icon(true)
|
||||
.start_page(&format!("{}?{}&{}", fname, params, default_params))
|
||||
.open()
|
||||
.unwrap();
|
||||
|
||||
let orig_last_url = page_response.get_last_url().unwrap();
|
||||
|
||||
set_menu_from_url(orig_last_url);
|
||||
let last_url = page_response.get_last_url().unwrap();
|
||||
println!("Received URL from web menu: {}", last_url);
|
||||
set_menu_from_url(last_url);
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
QUICK_MENU_ACTIVE = true;
|
||||
|
|
|
@ -1,138 +1,138 @@
|
|||
pub mod consts;
|
||||
pub mod events;
|
||||
pub mod menu;
|
||||
pub mod raygun_printer;
|
||||
pub mod release;
|
||||
|
||||
use crate::common::consts::*;
|
||||
use smash::app::{self, lua_bind::*};
|
||||
use smash::lib::lua_const::*;
|
||||
|
||||
pub use crate::common::consts::MENU;
|
||||
pub static mut DEFAULT_MENU: TrainingModpackMenu = crate::common::consts::DEFAULT_MENU;
|
||||
pub static mut BASE_MENU: TrainingModpackMenu = unsafe { DEFAULT_MENU };
|
||||
pub static mut FIGHTER_MANAGER_ADDR: usize = 0;
|
||||
pub static mut STAGE_MANAGER_ADDR: usize = 0;
|
||||
|
||||
#[cfg(not(feature = "outside_training_mode"))]
|
||||
extern "C" {
|
||||
#[link_name = "\u{1}_ZN3app9smashball16is_training_modeEv"]
|
||||
pub fn is_training_mode() -> bool;
|
||||
}
|
||||
|
||||
#[cfg(feature = "outside_training_mode")]
|
||||
pub fn is_training_mode() -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn get_category(module_accessor: &mut app::BattleObjectModuleAccessor) -> i32 {
|
||||
(module_accessor.info >> 28) as u8 as i32
|
||||
}
|
||||
|
||||
pub fn is_emulator() -> bool {
|
||||
unsafe { skyline::hooks::getRegionAddress(skyline::hooks::Region::Text) as u64 == 0x8004000 }
|
||||
}
|
||||
|
||||
pub fn get_module_accessor(fighter_id: FighterId) -> *mut app::BattleObjectModuleAccessor {
|
||||
let entry_id_int = fighter_id as i32;
|
||||
let entry_id = app::FighterEntryID(entry_id_int);
|
||||
unsafe {
|
||||
let mgr = *(FIGHTER_MANAGER_ADDR as *mut *mut app::FighterManager);
|
||||
let fighter_entry =
|
||||
FighterManager::get_fighter_entry(mgr, entry_id) as *mut app::FighterEntry;
|
||||
let current_fighter_id = FighterEntry::current_fighter_id(fighter_entry);
|
||||
app::sv_battle_object::module_accessor(current_fighter_id as u32)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_fighter(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
get_category(module_accessor) == BATTLE_OBJECT_CATEGORY_FIGHTER
|
||||
}
|
||||
|
||||
pub fn is_operation_cpu(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
unsafe {
|
||||
if !is_fighter(module_accessor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let entry_id_int =
|
||||
WorkModule::get_int(module_accessor, *FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID) as i32;
|
||||
|
||||
if entry_id_int == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let entry_id = app::FighterEntryID(entry_id_int);
|
||||
let mgr = *(FIGHTER_MANAGER_ADDR as *mut *mut app::FighterManager);
|
||||
let fighter_information =
|
||||
FighterManager::get_fighter_information(mgr, entry_id) as *mut app::FighterInformation;
|
||||
|
||||
FighterInformation::is_operation_cpu(fighter_information)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_grounded(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let situation_kind = unsafe { StatusModule::situation_kind(module_accessor) as i32 };
|
||||
|
||||
situation_kind == SITUATION_KIND_GROUND
|
||||
}
|
||||
|
||||
pub fn is_airborne(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let situation_kind = unsafe { StatusModule::situation_kind(module_accessor) as i32 };
|
||||
|
||||
situation_kind == SITUATION_KIND_AIR
|
||||
}
|
||||
|
||||
pub fn is_idle(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
|
||||
|
||||
status_kind == FIGHTER_STATUS_KIND_WAIT
|
||||
}
|
||||
|
||||
pub fn is_in_hitstun(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
|
||||
|
||||
(*FIGHTER_STATUS_KIND_DAMAGE..*FIGHTER_STATUS_KIND_DAMAGE_FALL).contains(&status_kind)
|
||||
}
|
||||
pub fn is_in_footstool(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
|
||||
|
||||
(*FIGHTER_STATUS_KIND_TREAD_DAMAGE..=*FIGHTER_STATUS_KIND_TREAD_FALL).contains(&status_kind)
|
||||
}
|
||||
|
||||
pub fn is_shielding(module_accessor: *mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let status_kind = unsafe { StatusModule::status_kind(module_accessor) as i32 };
|
||||
|
||||
(*FIGHTER_STATUS_KIND_GUARD_ON..=*FIGHTER_STATUS_KIND_GUARD_DAMAGE).contains(&status_kind)
|
||||
}
|
||||
|
||||
pub fn is_in_shieldstun(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
|
||||
let prev_status = unsafe { StatusModule::prev_status_kind(module_accessor, 0) };
|
||||
|
||||
// If we are taking shield damage or we are droping shield from taking shield damage we are in hitstun
|
||||
status_kind == FIGHTER_STATUS_KIND_GUARD_DAMAGE
|
||||
|| (prev_status == FIGHTER_STATUS_KIND_GUARD_DAMAGE
|
||||
&& status_kind == FIGHTER_STATUS_KIND_GUARD_OFF)
|
||||
}
|
||||
|
||||
pub unsafe fn is_dead(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let fighter_kind = app::utility::get_kind(module_accessor);
|
||||
let fighter_is_ptrainer = [
|
||||
*FIGHTER_KIND_PZENIGAME,
|
||||
*FIGHTER_KIND_PFUSHIGISOU,
|
||||
*FIGHTER_KIND_PLIZARDON,
|
||||
]
|
||||
.contains(&fighter_kind);
|
||||
let status_kind = StatusModule::status_kind(module_accessor) as i32;
|
||||
let prev_status_kind = StatusModule::prev_status_kind(module_accessor, 0);
|
||||
// Pokemon trainer enters FIGHTER_STATUS_KIND_WAIT for one frame during their respawn animation
|
||||
// And the previous status is FIGHTER_STATUS_NONE
|
||||
if fighter_is_ptrainer {
|
||||
[*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind)
|
||||
|| (status_kind == FIGHTER_STATUS_KIND_WAIT
|
||||
&& prev_status_kind == FIGHTER_STATUS_KIND_NONE)
|
||||
} else {
|
||||
[*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind)
|
||||
}
|
||||
}
|
||||
pub mod consts;
|
||||
pub mod events;
|
||||
pub mod menu;
|
||||
pub mod raygun_printer;
|
||||
pub mod release;
|
||||
|
||||
use crate::common::consts::*;
|
||||
use smash::app::{self, lua_bind::*};
|
||||
use smash::lib::lua_const::*;
|
||||
|
||||
pub use crate::common::consts::MENU;
|
||||
pub static mut DEFAULTS_MENU: TrainingModpackMenu = crate::common::consts::DEFAULTS_MENU;
|
||||
pub static mut BASE_MENU: TrainingModpackMenu = unsafe { DEFAULTS_MENU };
|
||||
pub static mut FIGHTER_MANAGER_ADDR: usize = 0;
|
||||
pub static mut STAGE_MANAGER_ADDR: usize = 0;
|
||||
|
||||
#[cfg(not(feature = "outside_training_mode"))]
|
||||
extern "C" {
|
||||
#[link_name = "\u{1}_ZN3app9smashball16is_training_modeEv"]
|
||||
pub fn is_training_mode() -> bool;
|
||||
}
|
||||
|
||||
#[cfg(feature = "outside_training_mode")]
|
||||
pub fn is_training_mode() -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
pub fn get_category(module_accessor: &mut app::BattleObjectModuleAccessor) -> i32 {
|
||||
(module_accessor.info >> 28) as u8 as i32
|
||||
}
|
||||
|
||||
pub fn is_emulator() -> bool {
|
||||
unsafe { skyline::hooks::getRegionAddress(skyline::hooks::Region::Text) as u64 == 0x8004000 }
|
||||
}
|
||||
|
||||
pub fn get_module_accessor(fighter_id: FighterId) -> *mut app::BattleObjectModuleAccessor {
|
||||
let entry_id_int = fighter_id as i32;
|
||||
let entry_id = app::FighterEntryID(entry_id_int);
|
||||
unsafe {
|
||||
let mgr = *(FIGHTER_MANAGER_ADDR as *mut *mut app::FighterManager);
|
||||
let fighter_entry =
|
||||
FighterManager::get_fighter_entry(mgr, entry_id) as *mut app::FighterEntry;
|
||||
let current_fighter_id = FighterEntry::current_fighter_id(fighter_entry);
|
||||
app::sv_battle_object::module_accessor(current_fighter_id as u32)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_fighter(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
get_category(module_accessor) == BATTLE_OBJECT_CATEGORY_FIGHTER
|
||||
}
|
||||
|
||||
pub fn is_operation_cpu(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
unsafe {
|
||||
if !is_fighter(module_accessor) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let entry_id_int =
|
||||
WorkModule::get_int(module_accessor, *FIGHTER_INSTANCE_WORK_ID_INT_ENTRY_ID) as i32;
|
||||
|
||||
if entry_id_int == 0 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let entry_id = app::FighterEntryID(entry_id_int);
|
||||
let mgr = *(FIGHTER_MANAGER_ADDR as *mut *mut app::FighterManager);
|
||||
let fighter_information =
|
||||
FighterManager::get_fighter_information(mgr, entry_id) as *mut app::FighterInformation;
|
||||
|
||||
FighterInformation::is_operation_cpu(fighter_information)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_grounded(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let situation_kind = unsafe { StatusModule::situation_kind(module_accessor) as i32 };
|
||||
|
||||
situation_kind == SITUATION_KIND_GROUND
|
||||
}
|
||||
|
||||
pub fn is_airborne(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let situation_kind = unsafe { StatusModule::situation_kind(module_accessor) as i32 };
|
||||
|
||||
situation_kind == SITUATION_KIND_AIR
|
||||
}
|
||||
|
||||
pub fn is_idle(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
|
||||
|
||||
status_kind == FIGHTER_STATUS_KIND_WAIT
|
||||
}
|
||||
|
||||
pub fn is_in_hitstun(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
|
||||
|
||||
(*FIGHTER_STATUS_KIND_DAMAGE..*FIGHTER_STATUS_KIND_DAMAGE_FALL).contains(&status_kind)
|
||||
}
|
||||
pub fn is_in_footstool(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
|
||||
|
||||
(*FIGHTER_STATUS_KIND_TREAD_DAMAGE..=*FIGHTER_STATUS_KIND_TREAD_FALL).contains(&status_kind)
|
||||
}
|
||||
|
||||
pub fn is_shielding(module_accessor: *mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let status_kind = unsafe { StatusModule::status_kind(module_accessor) as i32 };
|
||||
|
||||
(*FIGHTER_STATUS_KIND_GUARD_ON..=*FIGHTER_STATUS_KIND_GUARD_DAMAGE).contains(&status_kind)
|
||||
}
|
||||
|
||||
pub fn is_in_shieldstun(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let status_kind = unsafe { StatusModule::status_kind(module_accessor) };
|
||||
let prev_status = unsafe { StatusModule::prev_status_kind(module_accessor, 0) };
|
||||
|
||||
// If we are taking shield damage or we are droping shield from taking shield damage we are in hitstun
|
||||
status_kind == FIGHTER_STATUS_KIND_GUARD_DAMAGE
|
||||
|| (prev_status == FIGHTER_STATUS_KIND_GUARD_DAMAGE
|
||||
&& status_kind == FIGHTER_STATUS_KIND_GUARD_OFF)
|
||||
}
|
||||
|
||||
pub unsafe fn is_dead(module_accessor: &mut app::BattleObjectModuleAccessor) -> bool {
|
||||
let fighter_kind = app::utility::get_kind(module_accessor);
|
||||
let fighter_is_ptrainer = [
|
||||
*FIGHTER_KIND_PZENIGAME,
|
||||
*FIGHTER_KIND_PFUSHIGISOU,
|
||||
*FIGHTER_KIND_PLIZARDON,
|
||||
]
|
||||
.contains(&fighter_kind);
|
||||
let status_kind = StatusModule::status_kind(module_accessor) as i32;
|
||||
let prev_status_kind = StatusModule::prev_status_kind(module_accessor, 0);
|
||||
// Pokemon trainer enters FIGHTER_STATUS_KIND_WAIT for one frame during their respawn animation
|
||||
// And the previous status is FIGHTER_STATUS_NONE
|
||||
if fighter_is_ptrainer {
|
||||
[*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind)
|
||||
|| (status_kind == FIGHTER_STATUS_KIND_WAIT
|
||||
&& prev_status_kind == FIGHTER_STATUS_KIND_NONE)
|
||||
} else {
|
||||
[*FIGHTER_STATUS_KIND_DEAD, *FIGHTER_STATUS_KIND_STANDBY].contains(&status_kind)
|
||||
}
|
||||
}
|
||||
|
|
35
src/lib.rs
35
src/lib.rs
|
@ -71,7 +71,7 @@ pub fn render_text_to_screen(s: &str) {
|
|||
pub fn main() {
|
||||
macro_rules! log {
|
||||
($($arg:tt)*) => {
|
||||
print!("{}{}", "[Training Modpack] ".green(), format!($($arg)*));
|
||||
println!("{}{}", "[Training Modpack] ".green(), format!($($arg)*));
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -105,10 +105,8 @@ pub fn main() {
|
|||
if menu_conf.starts_with(b"http://localhost") {
|
||||
log!("Previous menu found, loading from training_modpack_menu.conf");
|
||||
unsafe {
|
||||
MENU = get_menu_from_url(MENU, std::str::from_utf8(&menu_conf).unwrap());
|
||||
if is_emulator() {
|
||||
MENU.quick_menu = OnOff::On;
|
||||
}
|
||||
MENU = get_menu_from_url(MENU, std::str::from_utf8(&menu_conf).unwrap(), false);
|
||||
DEFAULTS_MENU = get_menu_from_url(DEFAULTS_MENU, std::str::from_utf8(&menu_conf).unwrap(), true);
|
||||
}
|
||||
} else {
|
||||
log!("Previous menu found but is invalid.");
|
||||
|
@ -116,32 +114,10 @@ pub fn main() {
|
|||
} else {
|
||||
log!("No previous menu file found.");
|
||||
}
|
||||
|
||||
let menu_defaults_conf_path = "sd:/TrainingModpack/training_modpack_menu_defaults.conf";
|
||||
log!("Checking for previous menu defaults in training_modpack_menu_defaults.conf...");
|
||||
if fs::metadata(menu_defaults_conf_path).is_ok() {
|
||||
let menu_defaults_conf = fs::read(menu_defaults_conf_path).unwrap();
|
||||
if menu_defaults_conf.starts_with(b"http://localhost") {
|
||||
log!("Menu defaults found, loading from training_modpack_menu_defaults.conf");
|
||||
unsafe {
|
||||
DEFAULT_MENU = get_menu_from_url(
|
||||
DEFAULT_MENU,
|
||||
std::str::from_utf8(&menu_defaults_conf).unwrap(),
|
||||
);
|
||||
if is_emulator() {
|
||||
DEFAULT_MENU.quick_menu = OnOff::On;
|
||||
}
|
||||
crate::menu::write_menu();
|
||||
}
|
||||
} else {
|
||||
log!("Previous menu defaults found but are invalid.");
|
||||
}
|
||||
} else {
|
||||
log!("No previous menu defaults found.");
|
||||
}
|
||||
|
||||
|
||||
if is_emulator() {
|
||||
unsafe {
|
||||
DEFAULTS_MENU.quick_menu = OnOff::On;
|
||||
MENU.quick_menu = OnOff::On;
|
||||
}
|
||||
}
|
||||
|
@ -195,6 +171,7 @@ pub fn main() {
|
|||
// Leave menu.
|
||||
menu::QUICK_MENU_ACTIVE = false;
|
||||
crate::menu::set_menu_from_url(url.as_str());
|
||||
println!("URL: {}", url.as_str());
|
||||
}
|
||||
});
|
||||
button_presses.zl.read_press().then(|| {
|
||||
|
|
|
@ -20,337 +20,261 @@
|
|||
}
|
||||
}
|
||||
|
||||
.answer-border-outer {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.button-icon {
|
||||
height: 53px;
|
||||
margin-left: 4px;
|
||||
width: 53px;
|
||||
}
|
||||
|
||||
.button-icon-wrapper {
|
||||
align-items: center;
|
||||
background: #000000;
|
||||
.tab-list-container {
|
||||
overflow: hidden;
|
||||
background-color: #555;
|
||||
display: flex;
|
||||
height: 58px;
|
||||
margin-left: -1px;
|
||||
width: 80px;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.button-msg-wrapper {
|
||||
width: 259px;
|
||||
.tab-list-container p {
|
||||
color: #fff;
|
||||
width: 130px;
|
||||
height: fit-content;
|
||||
margin: 0px 10px 0px 10px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.checkbox-display {
|
||||
margin: 10px 70px;
|
||||
}
|
||||
|
||||
.checkbox-display::after {
|
||||
/* Displayed Checkbox (unchecked) */
|
||||
color: white;
|
||||
content: "\E14C";
|
||||
}
|
||||
|
||||
.defaults-checkbox-container {
|
||||
/* Save Defaults Container */
|
||||
.tab-list {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
margin-top: 10px;
|
||||
position: fixed;
|
||||
right: 50px;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.flex-button {
|
||||
align-items: center;
|
||||
display: inline-flex;
|
||||
flex: 1;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
.tab-list button {
|
||||
background-color: inherit;
|
||||
float: left;
|
||||
border: none;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
padding: 14px 16px;
|
||||
color: #fff;
|
||||
margin: 5px 0px 0px 8px;
|
||||
border-radius: 8px 8px 0px 0px;
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.footer {
|
||||
align-items: center;
|
||||
background: #000000;
|
||||
.tab-list button:hover {
|
||||
background: #797979;
|
||||
}
|
||||
|
||||
.tab-list button.active {
|
||||
color: #000;
|
||||
background: #ccc;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
height: 73px;
|
||||
justify-content: center;
|
||||
position: fixed;
|
||||
z-index: 10;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
.header {
|
||||
align-items: center;
|
||||
background: #000000;
|
||||
border-bottom: 7px solid #000000;
|
||||
body {
|
||||
background: none;
|
||||
font-family: "nintendo_ext_003", "nintendo_udsgr_std_003";
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Overwrite padding from keyword stuff. */
|
||||
.l-main-content {
|
||||
padding: 0px 0px 0px;
|
||||
}
|
||||
|
||||
/* Handle alignment of items in the header */
|
||||
.l-header {
|
||||
display: flex;
|
||||
height: 65px;
|
||||
}
|
||||
|
||||
.header-decoration {
|
||||
align-items: center;
|
||||
background: #a80114;
|
||||
display: inline-flex;
|
||||
height: 65px;
|
||||
padding-left: 21px;
|
||||
width: 101px;
|
||||
}
|
||||
|
||||
.header-desc {
|
||||
color: white;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 80px;
|
||||
z-index: 100;
|
||||
background: #000;
|
||||
box-shadow: 0px 1px 1px #000;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
color: #f46264;
|
||||
font-size: 26px;
|
||||
line-height: 2.5;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
.is-appear {
|
||||
opacity: 1;
|
||||
.header-label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: end;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.is-focused .question-border::before {
|
||||
background: #000000;
|
||||
box-shadow: none;
|
||||
.header-label > p {
|
||||
color: #fff;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.is-focused .question-message {
|
||||
color: #FFFFFF;
|
||||
text-shadow: 2px 0 0 #000, -2px 0 0 #000, 0 2px 0 #000, 0 -2px 0 #000, 1px 1px #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000;
|
||||
.return-icon-container {
|
||||
width: 101px;
|
||||
height: 65px;
|
||||
padding-left: 21px;
|
||||
background: #a80114;
|
||||
border-radius: 0px 0px 15px 0px;
|
||||
}
|
||||
|
||||
.is-focused .question.scuffle-thema {
|
||||
background: #df1624;
|
||||
.return-icon {
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
padding-left: 7px;
|
||||
filter: drop-shadow(3px 5px 2px rgba(0, 0, 0, 0.8));
|
||||
}
|
||||
|
||||
.is-focused .scuffle-thema {
|
||||
background: #df1624;
|
||||
/* Center Icons */
|
||||
.question::before {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.is-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.is-opened .question {
|
||||
bottom: 11px;
|
||||
}
|
||||
|
||||
.is-opened .question-border::before {
|
||||
background: #000000;
|
||||
bottom: 5px;
|
||||
}
|
||||
|
||||
.is-opened .question-outer {
|
||||
height: 86px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.keyword-button {
|
||||
background: #d9e4ea;
|
||||
border: solid 4px #2e3c45;
|
||||
box-shadow: 1px 1px 6px rgba(24, 24, 24, 0.6);
|
||||
box-sizing: border-box;
|
||||
height: 66px;
|
||||
justify-content: flex-start;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.keyword-button-outer {
|
||||
border: 5px solid transparent;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
.keyword-message {
|
||||
color: #2b3940;
|
||||
font-size: 22px;
|
||||
padding: 0px 5px;
|
||||
}
|
||||
|
||||
.l-footer {
|
||||
/* Footer */
|
||||
.footer {
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.l-grid {
|
||||
background: #000;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.l-header {
|
||||
box-shadow: 0px 1px 1px #000000;
|
||||
display: flex;
|
||||
left: 0px;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 73px;
|
||||
width: 100%;
|
||||
color: #fff;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.l-header-title {
|
||||
align-items: center;
|
||||
/* Save Defaults Container */
|
||||
.defaults-checkbox-container {
|
||||
position: fixed;
|
||||
right: 50px;
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
height: 100%;
|
||||
justify-content: center;
|
||||
left: 0px;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
width: 1280px;
|
||||
}
|
||||
|
||||
.l-item {
|
||||
margin: 0px 13px;
|
||||
}
|
||||
|
||||
.l-main-content {
|
||||
/* Overwrite padding from keyword stuff. */
|
||||
padding: 0px 0px 0px;
|
||||
}
|
||||
|
||||
.l-qa {
|
||||
/* Column size */
|
||||
flex-basis: 33%;
|
||||
}
|
||||
|
||||
.l-qa:last-child .qa {
|
||||
/* Overwrite margin on the last child to avoid overlap with footer */
|
||||
margin-bottom: 75px;
|
||||
}
|
||||
|
||||
.l-qa:last-child .qa.is-opened {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
||||
.qa {
|
||||
display: block;
|
||||
will-change: scroll-position;
|
||||
}
|
||||
|
||||
.is-focused {
|
||||
background: linear-gradient(90deg, rgb(255, 109, 0) 0%, rgb(255, 255, 0) 65%, rgb(255, 109, 0) 70%);
|
||||
will-change: animation;
|
||||
animation: background-slide 650ms linear infinite normal;
|
||||
}
|
||||
|
||||
.question {
|
||||
align-items: center;
|
||||
background: #d9e4ea;
|
||||
bottom: 11px;
|
||||
display: flex;
|
||||
left: 11px;
|
||||
padding: 0px 30px 0px 94px;
|
||||
position: absolute;
|
||||
right: 28px;
|
||||
top: 11px;
|
||||
}
|
||||
|
||||
.question-border::before {
|
||||
background: #2e3c45;
|
||||
bottom: 6px;
|
||||
box-shadow: 3px 3px 3px rgba(24, 24, 24, 0.5);
|
||||
content: '';
|
||||
left: 6px;
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
top: 6px;
|
||||
}
|
||||
|
||||
.question-icon {
|
||||
bottom: 0px;
|
||||
height: 60px;
|
||||
left: 2px;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
transition: opacity 0.2s ease;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.question-message {
|
||||
color: #2b3940;
|
||||
font-size: 23px;
|
||||
width: 100%;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.question-message span {
|
||||
display: block;
|
||||
letter-spacing: normal;
|
||||
}
|
||||
|
||||
.question-outer {
|
||||
height: 86px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.question::before {
|
||||
width: 70px;
|
||||
background: #000000;
|
||||
bottom: 0px;
|
||||
content: '';
|
||||
left: 0px;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
.ret-icon {
|
||||
display: inline-block;
|
||||
height: 58px;
|
||||
transition: opacity 0.2s ease;
|
||||
width: 58px;
|
||||
}
|
||||
|
||||
.ret-icon-wrapper {
|
||||
margin-left: -4px;
|
||||
position: relative;
|
||||
will-change: transform;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Checkbox element (hidden) */
|
||||
#saveDefaults {
|
||||
/* Checkbox element (hidden) */
|
||||
left: -100vw;
|
||||
position: absolute;
|
||||
left: -100vw;
|
||||
}
|
||||
|
||||
.checkbox-display {
|
||||
margin: 10px 70px;
|
||||
}
|
||||
|
||||
/* Displayed Checkbox (unchecked) */
|
||||
.checkbox-display::after {
|
||||
content: "\E14C";
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Displayed Checkbox (checked) */
|
||||
#saveDefaults:checked~.checkbox-display::after {
|
||||
/* Displayed Checkbox (checked) */
|
||||
content: "\E14B";
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
.menu-item {
|
||||
/* Container div for menu link and menu list */
|
||||
width: 30%;
|
||||
margin: 6px;
|
||||
}
|
||||
|
||||
body {
|
||||
background: none;
|
||||
width: 1280px;
|
||||
}
|
||||
|
||||
body, div, th, td, p, ul, ol, dl, dt, dd, img, h1, h2, h3, h4, h5, h6, footer, header, nav, p, section, span, figure {
|
||||
margin: 0px;
|
||||
overflow-wrap: break-word;
|
||||
.menu-button, .menu-item > div button {
|
||||
/* Item styling */
|
||||
background-color: #d9e4ea;
|
||||
border: solid black;
|
||||
border-width: 3px 20px 3px 3px;
|
||||
padding: 0px;
|
||||
word-break: normal;
|
||||
font-family: "nintendo_ext_003", "nintendo_udsgr_std_003";
|
||||
box-shadow: 3px 3px 3px #18181880;
|
||||
display: flex;
|
||||
height: 60px;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
img, svg {
|
||||
opacity: 0;
|
||||
.menu-item > div button {
|
||||
z-index: 1;
|
||||
width: 22%;
|
||||
margin: 4px 17px;
|
||||
}
|
||||
|
||||
img.question-icon:not(.toggle) {
|
||||
/* Fade icons slightly */
|
||||
opacity: 0.75;
|
||||
.modal p {
|
||||
font-size: 18px !important;
|
||||
}
|
||||
|
||||
span {
|
||||
letter-spacing: 0.01px;
|
||||
.menu-icon {
|
||||
background-color: black;
|
||||
flex-basis: auto;
|
||||
width: 60px;
|
||||
height: 48px;
|
||||
border: solid black;
|
||||
border-right-width: 5px;
|
||||
}
|
||||
|
||||
.menu-icon img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.menu-item p {
|
||||
font-size: 23px;
|
||||
color: #2b3940;
|
||||
width: 100%;
|
||||
margin: 10px 0px 0px 20px;
|
||||
}
|
||||
|
||||
.hide {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.menu-item > div {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
position: fixed;
|
||||
justify-content: flex-start;
|
||||
align-content: flex-start;
|
||||
top: 80px;
|
||||
bottom: 73px;
|
||||
left: 0px;
|
||||
right: 0px;
|
||||
margin-top: 0px;
|
||||
background-color: rgba(100, 100, 100, 0.9);
|
||||
}
|
||||
|
||||
:focus {
|
||||
background: rgb(255,70,2);
|
||||
background: linear-gradient(45deg, rgba(255,70,2,1) 20%, rgba(255,215,0,1) 46%, rgba(255,215,0,1) 54%, rgba(255,70,2,1) 80%);
|
||||
background-size: 500% 100%;
|
||||
will-change: animation;
|
||||
animation: translate-anim 5s infinite linear;
|
||||
}
|
||||
|
||||
:focus > p {
|
||||
color: #fff;
|
||||
text-shadow: -1px -1px 1px #000, 1px -1px 1px #000, -1px 1px 1px #000, 1px 1px 1px #000;
|
||||
}
|
||||
|
||||
@keyframes translate-anim {
|
||||
0% {
|
||||
background-position: 0% 0%;
|
||||
}
|
||||
100% {
|
||||
background-position: 100% 0%;
|
||||
}
|
||||
}
|
||||
|
||||
ul, ol {
|
||||
list-style-type: none;
|
||||
}
|
29
src/static/img/quick_menu.svg
Normal file
29
src/static/img/quick_menu.svg
Normal file
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="80"
|
||||
height="80"
|
||||
viewBox="0 0 21.166666 21.166667"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<g
|
||||
id="layer1">
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 1.8552457,17.110952 8.469829,10.496369 1.8552457,3.8817854"
|
||||
id="path1904" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 7.22203,17.110952 13.836612,10.49637 7.22203,3.8817855"
|
||||
id="path1904-3" />
|
||||
<path
|
||||
style="fill:none;stroke:#ffffff;stroke-width:2.11667;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
d="M 12.588812,17.110952 19.203396,10.496369 12.588812,3.8817854"
|
||||
id="path1904-2" />
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.1 KiB |
|
@ -1,22 +1,23 @@
|
|||
var isNx = (typeof window.nx !== 'undefined');
|
||||
var prevQuestionMsg = null;
|
||||
var prevFocusedElm = null;
|
||||
var defaults_prefix = "__";
|
||||
|
||||
if (isNx) {
|
||||
window.nx.footer.setAssign('B', '', goBackHook, {se: ''});
|
||||
window.nx.footer.setAssign('X', '', resetSubmenu, {se: ''});
|
||||
window.nx.footer.setAssign('B', '', close_or_exit, {se: ''});
|
||||
window.nx.footer.setAssign('X', '', resetCurrentSubmenu, {se: ''});
|
||||
window.nx.footer.setAssign('L', '', resetAllSubmenus, {se: ''});
|
||||
window.nx.footer.setAssign('R', '', toggleSaveDefaults, {se: ''});
|
||||
window.nx.footer.setAssign('R', '', saveDefaults, {se: ''});
|
||||
window.nx.footer.setAssign('ZR', '', cycleNextTab, {se: ''});
|
||||
window.nx.footer.setAssign('ZL', '', cyclePrevTab, {se: ''});
|
||||
} else {
|
||||
document.getElementById("body").addEventListener('keypress', (event) => {
|
||||
document.addEventListener('keypress', (event) => {
|
||||
switch (event.key) {
|
||||
case "b":
|
||||
console.log("b");
|
||||
goBackHook();
|
||||
close_or_exit();
|
||||
break;
|
||||
case "x":
|
||||
console.log("x");
|
||||
resetSubmenu();
|
||||
resetCurrentSubmenu();
|
||||
break;
|
||||
case "l":
|
||||
console.log("l");
|
||||
|
@ -24,20 +25,87 @@ if (isNx) {
|
|||
break;
|
||||
case "r":
|
||||
console.log("r");
|
||||
toggleSaveDefaults();
|
||||
saveDefaults();
|
||||
break;
|
||||
case "p":
|
||||
console.log("p");
|
||||
cycleNextTab();
|
||||
break;
|
||||
case "o":
|
||||
console.log("o");
|
||||
cyclePrevTab();
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.onload = setSettings;
|
||||
window.onload = onLoad;
|
||||
var settings = new Map();
|
||||
var lastFocusedItem = document.querySelector(".menu-item > button");
|
||||
|
||||
function isTextNode(node) {
|
||||
return node.nodeType == Node.TEXT_NODE
|
||||
function onLoad() {
|
||||
// Activate the first tab
|
||||
openTab(document.querySelector("button.tab-button"));
|
||||
|
||||
// Extract URL params and set appropriate settings
|
||||
setSettingsFromURL();
|
||||
setSubmenusFromSettings();
|
||||
}
|
||||
|
||||
function openTab(e) {
|
||||
var tab_id = e.id.replace("button", "tab");
|
||||
var selected_tab = document.getElementById(tab_id);
|
||||
|
||||
|
||||
// Hide all content for all tabs
|
||||
closeAllItems();
|
||||
tabcontent = document.getElementsByClassName("tab-content");
|
||||
Array.from(tabcontent).forEach(element => {
|
||||
element.classList.add("hide");
|
||||
});
|
||||
|
||||
|
||||
// Get all elements with class="tablinks" and remove the class "active"
|
||||
tablinks = document.getElementsByClassName("tab-button");
|
||||
Array.from(tablinks).forEach(element => {
|
||||
element.classList.remove("active");
|
||||
});
|
||||
|
||||
// Show the current tab, and add an "active" class to the button that opened the tab
|
||||
e.classList.add("active");
|
||||
selected_tab.classList.remove("hide");
|
||||
selected_tab.querySelector("button").focus();
|
||||
}
|
||||
|
||||
function openItem(e) {
|
||||
playSound("SeWebMenuListOpen");
|
||||
var modal = e.parentElement.querySelector(".modal");
|
||||
modal.classList.toggle("hide");
|
||||
modal.querySelector("button").focus();
|
||||
lastFocusedItem = e;
|
||||
}
|
||||
|
||||
function closeAllItems() {
|
||||
var modals = document.querySelectorAll(".modal");
|
||||
Array.from(modals).forEach(element => {
|
||||
element.classList.add("hide");
|
||||
});
|
||||
lastFocusedItem.focus();
|
||||
}
|
||||
|
||||
function toggleOption(e) {
|
||||
playSound("SeSelectCheck");
|
||||
if (e.parentElement.classList.contains("single-option")) {
|
||||
selectSingleOption(e);
|
||||
} else {
|
||||
var img = e.querySelector("img");
|
||||
img.classList.toggle("hide");
|
||||
}
|
||||
}
|
||||
|
||||
function closestClass(elem, class_) {
|
||||
// Returns the closest anscestor (including self) with the given class
|
||||
// Returns the closest ancestor (including self) with the given class
|
||||
// TODO: Consider removing
|
||||
if (!elem) {
|
||||
// Reached the end of the DOM
|
||||
return null
|
||||
|
@ -49,46 +117,6 @@ function closestClass(elem, class_) {
|
|||
return closestClass(elem.parentElement, class_);
|
||||
}
|
||||
}
|
||||
|
||||
function getElementByXpath(path) {
|
||||
return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
|
||||
}
|
||||
|
||||
function focusQA(e) {
|
||||
playSound("SeSelectUncheck");
|
||||
prevFocusedElm = e;
|
||||
e.classList.add("is-focused");
|
||||
}
|
||||
|
||||
function defocusQA(e) {
|
||||
if (prevFocusedElm) {
|
||||
prevFocusedElm.classList.remove('is-focused');
|
||||
|
||||
}
|
||||
if (prevQuestionMsg) {
|
||||
prevQuestionMsg.remove();
|
||||
prevQuestionMsg = null;
|
||||
}
|
||||
}
|
||||
|
||||
function toggleAnswer(e) {
|
||||
playSound("SeToggleBtnOn");
|
||||
e.classList.toggle("is-opened");
|
||||
|
||||
// Toggle visibility of child answers
|
||||
[].forEach.call(e.childNodes, function (child) {
|
||||
if (!isTextNode(child) && child.classList.contains("answer-border-outer")) {
|
||||
child.classList.toggle("is-hidden");
|
||||
}
|
||||
});
|
||||
|
||||
// Toggle visibility of sibling answers
|
||||
var sibling = e.nextElementSibling;
|
||||
if (sibling.classList.contains("answer-border-outer")) {
|
||||
sibling.classList.toggle("is-hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function playSound(label) {
|
||||
// Valid labels:
|
||||
// SeToggleBtnFocus
|
||||
|
@ -129,164 +157,120 @@ function playSound(label) {
|
|||
}
|
||||
}
|
||||
|
||||
function goBackHook() {
|
||||
function exit() {
|
||||
playSound("SeFooterDecideBack");
|
||||
setSettingsFromMenu();
|
||||
var url = buildURLFromSettings();
|
||||
|
||||
if (isNx) {
|
||||
window.location.href = url;
|
||||
} else {
|
||||
console.log(url);
|
||||
}
|
||||
}
|
||||
|
||||
function close_or_exit() {
|
||||
// If any submenus are open, close them
|
||||
// Otherwise if all submenus are closed, exit the menu and return to the game
|
||||
|
||||
if (document.querySelectorAll(".qa.is-opened").length == 0) {
|
||||
// If all submenus are closed, exit and return through localhost
|
||||
playSound("SeFooterDecideBack");
|
||||
var url = "http://localhost/";
|
||||
|
||||
var settings = new Map();
|
||||
|
||||
// Collect settings for toggles
|
||||
|
||||
[].forEach.call(document.querySelectorAll("ul.l-grid"), function (toggle) {
|
||||
var section = toggle.id;
|
||||
var val = "";
|
||||
|
||||
[].forEach.call(toggle.childNodes, function (child) {
|
||||
if (!isTextNode(child) && child.querySelectorAll(".is-appear").length) {
|
||||
val += child.getAttribute("val") + ",";
|
||||
};
|
||||
});
|
||||
|
||||
settings.set(section,val);
|
||||
});
|
||||
|
||||
// Collect settings for OnOffs
|
||||
[].forEach.call(document.querySelectorAll("div.onoff"), function (onoff) {
|
||||
var section = onoff.id;
|
||||
var val = "";
|
||||
if (onoff.querySelectorAll(".is-appear").length) {
|
||||
val = "1";
|
||||
} else {
|
||||
val = "0";
|
||||
}
|
||||
settings.set(section,val);
|
||||
});
|
||||
|
||||
url += "?";
|
||||
settings.forEach((val, section) => { url += section + "=" + val + "&" } );
|
||||
|
||||
if (document.getElementById("saveDefaults").checked) {
|
||||
url += "save_defaults=1";
|
||||
} else {
|
||||
url = url.slice(0, -1);
|
||||
}
|
||||
|
||||
if (isNx) {
|
||||
window.location.href = url;
|
||||
} else {
|
||||
console.log(url);
|
||||
}
|
||||
} else {
|
||||
if (document.querySelector(".modal:not(.hide)")) {
|
||||
// Close any open submenus
|
||||
[].forEach.call(document.querySelectorAll(".qa.is-opened"), function (submenu) { toggleAnswer(submenu); });
|
||||
console.log("Closing Items");
|
||||
closeAllItems();
|
||||
} else {
|
||||
// If all submenus are closed, exit and return through localhost
|
||||
console.log("Exiting");
|
||||
exit();
|
||||
}
|
||||
}
|
||||
|
||||
function clickToggle(e) {
|
||||
playSound("SeCheckboxOn");
|
||||
var toggleOptions = e.querySelector(".toggle");
|
||||
if (e.querySelector(".is-single-option")) { // Single-option submenu
|
||||
// Deselect all submenu options
|
||||
closestClass(e, "l-qa").querySelector(".toggle").classList.remove("is-appear");
|
||||
closestClass(e, "l-qa").querySelector(".toggle").classList.add("is-hidden");
|
||||
// Then set the current one as the active setting
|
||||
toggleOptions.classList.add("is-appear");
|
||||
toggleOptions.classList.remove("is-hidden");
|
||||
} else { // Multi-option submenu
|
||||
toggleOptions.classList.toggle("is-appear");
|
||||
toggleOptions.classList.toggle("is-hidden");
|
||||
}
|
||||
}
|
||||
|
||||
function getParams(url) {
|
||||
function setSettingsFromURL() {
|
||||
var regex = /[?&]([^=#]+)=([^&#]*)/g,
|
||||
params = {},
|
||||
match;
|
||||
while (match = regex.exec(url)) {
|
||||
params[match[1]] = match[2];
|
||||
while (match = regex.exec(document.URL)) {
|
||||
settings.set(match[1], match[2]);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
function setSettings() {
|
||||
// Get settings from the URL GET parameters
|
||||
const settings = getParams(document.URL);
|
||||
|
||||
// Set Toggles
|
||||
[].forEach.call(document.querySelectorAll("ul.l-grid"), function (toggle) {
|
||||
var section = toggle.id;
|
||||
var section_setting = decodeURIComponent(settings[section]);
|
||||
|
||||
[].forEach.call(toggle.querySelectorAll("li"), function (child) {
|
||||
var e = child.querySelector("img.toggle");
|
||||
if (section_setting.split(",").includes(child.getAttribute("val"))) {
|
||||
e.classList.add("is-appear");
|
||||
e.classList.remove("is-hidden");
|
||||
} else {
|
||||
e.classList.remove("is-appear");
|
||||
e.classList.add("is-hidden");
|
||||
};
|
||||
});
|
||||
function setSettingsFromMenu() {
|
||||
var section;
|
||||
var mask;
|
||||
[].forEach.call(document.querySelectorAll(".menu-item"), function (menuItem) {
|
||||
section = menuItem.id;
|
||||
mask = getMaskFromSubmenu(menuItem);
|
||||
settings.set(section, mask);
|
||||
});
|
||||
}
|
||||
|
||||
// Set OnOffs
|
||||
[].forEach.call(document.querySelectorAll("div.onoff"), function (onOff) {
|
||||
var section = onOff.id;
|
||||
var section_setting = decodeURIComponent(settings[section]);
|
||||
var e = onOff.querySelector("img.toggle");
|
||||
if (section_setting == "1") {
|
||||
e.classList.add("is-appear");
|
||||
e.classList.remove("is-hidden");
|
||||
function buildURLFromSettings() {
|
||||
var url = "http://localhost/";
|
||||
url += "?";
|
||||
settings.forEach((val, key) => { url += key + "=" + String(val) + "&" } );
|
||||
return url
|
||||
}
|
||||
|
||||
function selectSingleOption(e) {
|
||||
// Deselect all options in the submenu
|
||||
parent = closestClass(e, "single-option");
|
||||
siblings = parent.querySelectorAll(".menu-icon img");
|
||||
[].forEach.call(siblings, function (sibling) {
|
||||
sibling.classList.add("hide");
|
||||
});
|
||||
e.querySelector(".menu-icon img").classList.remove("hide");
|
||||
}
|
||||
|
||||
function setSubmenusFromSettings() {
|
||||
[].forEach.call(document.querySelectorAll(".menu-item"), function (menuItem) {
|
||||
var section = menuItem.id;
|
||||
var section_mask = decodeURIComponent(settings.get(section));
|
||||
setSubmenuByMask(menuItem, section_mask)
|
||||
});
|
||||
}
|
||||
|
||||
function setSubmenuByMask(menuItem, mask) {
|
||||
[].forEach.call(menuItem.querySelectorAll(".modal .menu-icon img"), function (toggle) {
|
||||
if (isInBitmask(toggle.dataset.val, mask)) {
|
||||
toggle.classList.remove("hide");
|
||||
} else {
|
||||
e.classList.remove("is-appear");
|
||||
e.classList.add("is-hidden");
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function resetSubmenu() {
|
||||
// Resets any open or focused submenus to the default values
|
||||
playSound("SeToggleBtnOff");
|
||||
[].forEach.call(document.querySelectorAll("[default*='is-appear']"), function (item) {
|
||||
if (isSubmenuFocused(item)) {
|
||||
item.classList.add("is-appear");
|
||||
item.classList.remove("is-hidden");
|
||||
toggle.classList.add("hide");
|
||||
}
|
||||
});
|
||||
|
||||
[].forEach.call(document.querySelectorAll("[default*='is-hidden']"), function (item) {
|
||||
if (isSubmenuFocused(item)) {
|
||||
item.classList.remove("is-appear");
|
||||
item.classList.add("is-hidden");
|
||||
}
|
||||
});
|
||||
// If no setting for a Single Option is set, select the first one
|
||||
var isSingleOption = menuItem.querySelectorAll(".modal.single-option").length != 0;
|
||||
var isAllDeselected = menuItem.querySelectorAll(".modal .menu-icon img:not(.hide)").length == 0;
|
||||
if (isSingleOption & isAllDeselected) {
|
||||
selectSingleOption(menuItem.querySelector(".modal button"));
|
||||
}
|
||||
}
|
||||
|
||||
function isSubmenuFocused(elem) {
|
||||
// Return true if the element is in a submenu which is either focused or opened
|
||||
return (
|
||||
closestClass(elem, "l-qa").querySelectorAll(".is-opened, .is-focused").length
|
||||
|| closestClass(elem, "is-focused")
|
||||
)
|
||||
function getMaskFromSubmenu(menuItem) {
|
||||
var val = 0;
|
||||
[].forEach.call(menuItem.querySelectorAll(".modal img:not(.hide)"), function (toggle) {
|
||||
val += parseInt(toggle.dataset.val);
|
||||
});
|
||||
return val
|
||||
}
|
||||
|
||||
function resetCurrentSubmenu() {
|
||||
var focus = document.querySelector(".menu-item .modal:not(.hide)");
|
||||
if (!focus) {
|
||||
focus = document.querySelector(":focus");
|
||||
}
|
||||
var menuItem = closestClass(focus, "menu-item");
|
||||
|
||||
var key = defaults_prefix + menuItem.id;
|
||||
var section_mask = decodeURIComponent(settings.get(key));
|
||||
setSubmenuByMask(menuItem, section_mask);
|
||||
}
|
||||
|
||||
function resetAllSubmenus() {
|
||||
// Resets all submenus to the default values
|
||||
if (confirm("Are you sure that you want to reset all menu settings to the default?")) {
|
||||
playSound("SeToggleBtnOff");
|
||||
[].forEach.call(document.querySelectorAll("[default*='is-appear']"), function (item) {
|
||||
item.classList.add("is-appear");
|
||||
item.classList.remove("is-hidden");
|
||||
});
|
||||
|
||||
[].forEach.call(document.querySelectorAll("[default*='is-hidden']"), function (item) {
|
||||
item.classList.remove("is-appear");
|
||||
item.classList.add("is-hidden");
|
||||
[].forEach.call(document.querySelectorAll(".menu-item"), function (menuItem) {
|
||||
var key = defaults_prefix + menuItem.id;
|
||||
var mask = decodeURIComponent(settings.get(key));
|
||||
setSubmenuByMask(menuItem, mask)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -296,9 +280,42 @@ function setHelpText(text) {
|
|||
document.getElementById("help-text").innerText = text;
|
||||
}
|
||||
|
||||
function toggleSaveDefaults() {
|
||||
// Change the status of the Save Defaults checkbox
|
||||
playSound("SeCheckboxOn");
|
||||
var saveDefaultsCheckbox = document.getElementById("saveDefaults");
|
||||
saveDefaultsCheckbox.checked = !saveDefaultsCheckbox.checked;
|
||||
function saveDefaults() {
|
||||
if (confirm("Are you sure that you want to change the default menu settings to the current selections?")) {
|
||||
var key;
|
||||
var mask;
|
||||
[].forEach.call(document.querySelectorAll(".menu-item"), function (menuItem) {
|
||||
key = defaults_prefix + menuItem.id;
|
||||
mask = getMaskFromSubmenu(menuItem);
|
||||
settings.set(key, mask);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function isInBitmask(val, mask) {
|
||||
// Return true if the value is in the bitmask
|
||||
return (mask & val) != 0
|
||||
}
|
||||
|
||||
function cycleNextTab() {
|
||||
// Cycle to the next tab
|
||||
var activeTab = document.querySelector(".tab-button.active");
|
||||
var nextTab = activeTab.nextElementSibling;
|
||||
if (!nextTab) {
|
||||
// On the last tab - set the next tab as the first tab in the list
|
||||
nextTab = document.querySelector(".tab-button");
|
||||
}
|
||||
openTab(nextTab);
|
||||
}
|
||||
|
||||
function cyclePrevTab() {
|
||||
// Cycle to the previous tab
|
||||
var activeTab = document.querySelector(".tab-button.active");
|
||||
var prevTab = activeTab.previousElementSibling;
|
||||
if (!prevTab) {
|
||||
// On the first tab - set the next tab as the last tab in the list
|
||||
tabs = document.querySelectorAll(".tab-button");
|
||||
prevTab = tabs[tabs.length - 1];
|
||||
}
|
||||
openTab(prevTab);
|
||||
}
|
|
@ -1,117 +1,59 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<title>Document</title>
|
||||
<link rel="stylesheet" href="./css/training_modpack.css" />
|
||||
</head>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
|
||||
<title>Modpack Menu</title>
|
||||
<link rel="stylesheet" href="./css/training_modpack.css" />
|
||||
</head>
|
||||
|
||||
<body id="body">
|
||||
<script defer src="./js/training_modpack.js"></script>
|
||||
<div class="l-header">
|
||||
<div class="l-header-title">
|
||||
<div class="header-title"><span>Ultimate Training Modpack Menu</span></div>
|
||||
</div>
|
||||
<div class="header" style="flex-grow: 1;">
|
||||
<a id="ret-button" tabindex="-1" class="header-decoration" href="javascript:goBackHook();">
|
||||
<div class="ret-icon-wrapper">
|
||||
<img class="ret-icon is-appear" src="./img/m_retnormal.svg">
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<div class="header" style="flex-direction: column; justify-content: center; align-items: end; padding-right: 10px;">
|
||||
<p class="header-desc">Reset Current Menu: </p>
|
||||
<p class="header-desc">Reset All Menus: </p>
|
||||
</div>
|
||||
<body>
|
||||
<script defer src="./js/training_modpack.js"></script>
|
||||
<div class="l-header">
|
||||
<a id="ret-button" tabindex="-1" class="return-icon-container" href="javascript:goBackHook();">
|
||||
<img class="return-icon" src="./img/m_retnormal.svg">
|
||||
</a>
|
||||
<p class="header-title">Ultimate Training Modpack Menu</p>
|
||||
<div class="header-label">
|
||||
<p class="header-desc">Reset Current Menu: </p>
|
||||
<p class="header-desc">Reset All Menus: </p>
|
||||
<p class="header-desc">Save Defaults: </p>
|
||||
</div>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
<div class="l-grid">
|
||||
|
||||
<!--
|
||||
Script the part below via templating. Overall structure is perhaps
|
||||
[
|
||||
l-qa qa [id=qa-{{menuName}} tabindex="{{index}}"] {
|
||||
// make question for {{menuName}}
|
||||
// make answer with l-grid : l-item list for options
|
||||
},
|
||||
...
|
||||
]
|
||||
|
||||
|
||||
Remember to set make max keyword size greater than 23!
|
||||
-->
|
||||
{{#sub_menus}}
|
||||
<div class="l-qa">
|
||||
{{^onoffselector}}
|
||||
<a id="qa-{{id}}" class="qa" tabindex="{{index}}" href="javascript:void(0);" onfocus="focusQA(this);setHelpText({{help_text}})" onblur="defocusQA(this)" onclick="toggleAnswer(this)">
|
||||
<div class="question-outer">
|
||||
<div class="question-border">
|
||||
<div id="question-{{id}}" class="question scuffle-thema">
|
||||
<img class="question-icon" src="./img/{{id}}.svg" />
|
||||
<p class="question-message">
|
||||
<span>{{title}}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<div id="answer-border-{{id}}" class="answer-border-outer is-hidden">
|
||||
<div class="l-main">
|
||||
<ul class="l-grid" id="{{id}}">
|
||||
{{#toggles}}
|
||||
<li class="l-item" val="{{value}}">
|
||||
<div class="keyword-button-outer">
|
||||
<a tabindex="{{index}}" class="flex-button keyword-button scuffle-thema" href="javascript:void(0)" onclick="clickToggle(this);">
|
||||
<div class="button-icon-wrapper">
|
||||
<img class="button-icon toggle {{checked}} {{#is_single_option}}is-single-option{{/is_single_option}}" src="./img/check.svg" default="{{default}}">
|
||||
</div>
|
||||
<div class="button-msg-wrapper">
|
||||
<div class="keyword-message">
|
||||
{{title}}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</li>
|
||||
{{/toggles}}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{{/onoffselector}}
|
||||
{{#onoffselector}}
|
||||
<a id="qa-{{id}}" class="qa" tabindex="{{index}}" href="javascript:void(0);" onfocus="focusQA(this);setHelpText({{help_text}})" onblur="defocusQA(this)" onclick="clickToggle(this)">
|
||||
<div class="question-outer">
|
||||
<div class="question-border">
|
||||
<div id="question-{{id}}" class="question scuffle-thema">
|
||||
<div id="{{id}}" class="onoff">
|
||||
<img class="question-icon" style="z-index: 1;" src="./img/{{id}}.svg" />
|
||||
<div><img class="question-icon toggle {{checked}}" style="z-index: 2;" src="./img/check.svg" default="{{default}}"/></div>
|
||||
<p class="question-message">
|
||||
<span>{{title}}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
{{/onoffselector}}
|
||||
</div>
|
||||
<div class="tab-list-container">
|
||||
<p>Prev Tab: </p>
|
||||
<div class="tab-list">
|
||||
{{#tabs}}
|
||||
<button class="tab-button active" id="{{tab_id}}_button" onclick="openTab(this)" tabindex="-1">{{tab_title}}</button>
|
||||
{{/tabs}}
|
||||
</div>
|
||||
<p>Next Tab: </p>
|
||||
</div>
|
||||
<div class="tab-content-container">
|
||||
{{#tabs}}
|
||||
<div id="{{tab_id}}_tab" class="tab-content">
|
||||
{{#tab_submenus}}
|
||||
<div class="menu-item" id="{{submenu_id}}">
|
||||
<button class="menu-button" onfocus="setHelpText('{{help_text}}')" onclick="openItem(this)">
|
||||
<div class="menu-icon"><img src="./img/{{submenu_id}}.svg" /></div>
|
||||
<p>{{submenu_title}}</p>
|
||||
</button>
|
||||
<div class="hide modal{{#is_single_option}} single-option{{/is_single_option}}">
|
||||
{{#toggles}}
|
||||
<button class="menu-button" onclick="toggleOption(this)">
|
||||
<div class="menu-icon"><img class="hide" src="./img/check.svg" data-val="{{toggle_value}}"/></div>
|
||||
<p>{{toggle_title}}</p>
|
||||
</button>
|
||||
{{/toggles}}
|
||||
</div>
|
||||
{{/sub_menus}}
|
||||
</div>
|
||||
<footer id="footer" class="footer l-footer">
|
||||
<p id="help-text" class="header-desc"></p>
|
||||
<div class="defaults-checkbox-container">
|
||||
<label class="header-desc" for="saveDefaults">Save defaults: </label>
|
||||
<input type="checkbox" id="saveDefaults">
|
||||
<div class="checkbox-display"></div>
|
||||
</div>
|
||||
</footer>
|
||||
</body>
|
||||
|
||||
{{/tab_submenus}}
|
||||
</div>
|
||||
{{/tabs}}
|
||||
</div>
|
||||
<footer id="footer" class="footer">
|
||||
<p id="help-text" class="header-desc"></p>
|
||||
</footer>
|
||||
</body>
|
||||
</html>
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
|||
use training_mod_consts::{OnOffSelector, Slider, SubMenu, SubMenuType, Toggle};
|
||||
use training_mod_consts::{Slider, SubMenu, SubMenuType, Toggle, UiMenu};
|
||||
use tui::{
|
||||
backend::{Backend},
|
||||
layout::{Constraint, Corner, Direction, Layout},
|
||||
|
@ -22,81 +22,26 @@ pub struct App<'a> {
|
|||
pub tabs: StatefulList<&'a str>,
|
||||
pub menu_items: HashMap<&'a str, MultiStatefulList<SubMenu<'a>>>,
|
||||
pub selected_sub_menu_toggles: MultiStatefulList<Toggle<'a>>,
|
||||
pub selected_sub_menu_onoff_selectors: MultiStatefulList<OnOffSelector<'a>>,
|
||||
pub selected_sub_menu_sliders: MultiStatefulList<Slider>,
|
||||
pub outer_list: bool
|
||||
}
|
||||
|
||||
impl<'a> App<'a> {
|
||||
pub fn new(menu: training_mod_consts::Menu<'a>) -> App<'a> {
|
||||
let tab_specifiers = vec![
|
||||
("Mash Settings", vec![
|
||||
"Mash Toggles",
|
||||
"Followup Toggles",
|
||||
"Attack Angle",
|
||||
"Ledge Options",
|
||||
"Ledge Delay",
|
||||
"Tech Options",
|
||||
"Miss Tech Options",
|
||||
"Defensive Options",
|
||||
"Aerial Delay",
|
||||
"OoS Offset",
|
||||
"Reaction Time",
|
||||
]),
|
||||
("Defensive Settings", vec![
|
||||
"Fast Fall",
|
||||
"Fast Fall Delay",
|
||||
"Falling Aerials",
|
||||
"Full Hop",
|
||||
"Shield Tilt",
|
||||
"DI Direction",
|
||||
"SDI Direction",
|
||||
"Airdodge Direction",
|
||||
"SDI Strength",
|
||||
"Shield Toggles",
|
||||
"Mirroring",
|
||||
"Throw Options",
|
||||
"Throw Delay",
|
||||
"Pummel Delay",
|
||||
"Buff Options",
|
||||
]),
|
||||
("Other Settings", vec![
|
||||
"Input Delay",
|
||||
"Save States",
|
||||
"Save Damage",
|
||||
"Hitbox Visualization",
|
||||
"Stage Hazards",
|
||||
"Frame Advantage",
|
||||
"Mash In Neutral",
|
||||
"Quick Menu"
|
||||
])
|
||||
];
|
||||
let mut tabs: std::collections::HashMap<&str, Vec<SubMenu>> = std::collections::HashMap::new();
|
||||
tabs.insert("Mash Settings", vec![]);
|
||||
tabs.insert("Defensive Settings", vec![]);
|
||||
tabs.insert("Other Settings", vec![]);
|
||||
|
||||
for sub_menu in menu.sub_menus.iter() {
|
||||
for tab_spec in tab_specifiers.iter() {
|
||||
if tab_spec.1.contains(&sub_menu.title) {
|
||||
tabs.get_mut(tab_spec.0).unwrap().push(sub_menu.clone());
|
||||
}
|
||||
}
|
||||
};
|
||||
pub fn new(menu: UiMenu<'a>) -> App<'a> {
|
||||
let num_lists = 3;
|
||||
|
||||
let mut menu_items_stateful = HashMap::new();
|
||||
tabs.keys().for_each(|k| {
|
||||
menu.tabs.iter().for_each(|tab| {
|
||||
menu_items_stateful.insert(
|
||||
k.clone(),
|
||||
MultiStatefulList::with_items(tabs.get(k).unwrap().clone(), num_lists)
|
||||
tab.tab_title,
|
||||
MultiStatefulList::with_items(tab.tab_submenus.clone(), num_lists)
|
||||
);
|
||||
});
|
||||
|
||||
let mut app = App {
|
||||
tabs: StatefulList::with_items(tab_specifiers.iter().map(|(tab_title, _)| *tab_title).collect()),
|
||||
tabs: StatefulList::with_items(menu.tabs.iter().map(|tab| tab.tab_title).collect()),
|
||||
menu_items: menu_items_stateful,
|
||||
selected_sub_menu_toggles: MultiStatefulList::with_items(vec![], 0),
|
||||
selected_sub_menu_onoff_selectors: MultiStatefulList::with_items(vec![], 0),
|
||||
selected_sub_menu_sliders: MultiStatefulList::with_items(vec![], 0),
|
||||
outer_list: true
|
||||
};
|
||||
|
@ -109,8 +54,7 @@ impl<'a> App<'a> {
|
|||
let selected_sub_menu = &self.menu_items.get(self.tab_selected()).unwrap().lists[list_section].items.get(list_idx).unwrap();
|
||||
|
||||
let toggles = selected_sub_menu.toggles.clone();
|
||||
let sliders = selected_sub_menu.sliders.clone();
|
||||
let onoffs = selected_sub_menu.onoffselector.clone();
|
||||
// let sliders = selected_sub_menu.sliders.clone();
|
||||
match SubMenuType::from_str(self.sub_menu_selected()._type) {
|
||||
SubMenuType::TOGGLE => {
|
||||
self.selected_sub_menu_toggles = MultiStatefulList::with_items(
|
||||
|
@ -118,14 +62,9 @@ impl<'a> App<'a> {
|
|||
if selected_sub_menu.toggles.len() >= 3 { 3 } else { selected_sub_menu.toggles.len()} )
|
||||
},
|
||||
SubMenuType::SLIDER => {
|
||||
self.selected_sub_menu_sliders = MultiStatefulList::with_items(
|
||||
sliders,
|
||||
if selected_sub_menu.sliders.len() >= 3 { 3 } else { selected_sub_menu.sliders.len()} )
|
||||
},
|
||||
SubMenuType::ONOFF => {
|
||||
self.selected_sub_menu_onoff_selectors = MultiStatefulList::with_items(
|
||||
onoffs,
|
||||
if selected_sub_menu.onoffselector.len() >= 3 { 3 } else { selected_sub_menu.onoffselector.len()} )
|
||||
// self.selected_sub_menu_sliders = MultiStatefulList::with_items(
|
||||
// sliders,
|
||||
// if selected_sub_menu.sliders.len() >= 3 { 3 } else { selected_sub_menu.sliders.len()} )
|
||||
},
|
||||
};
|
||||
}
|
||||
|
@ -136,14 +75,13 @@ impl<'a> App<'a> {
|
|||
|
||||
fn sub_menu_selected(&self) -> &SubMenu {
|
||||
let (list_section, list_idx) = self.menu_items.get(self.tab_selected()).unwrap().idx_to_list_idx(self.menu_items.get(self.tab_selected()).unwrap().state);
|
||||
&self.menu_items.get(self.tab_selected()).unwrap().lists[list_section].items.get(list_idx).unwrap()
|
||||
self.menu_items.get(self.tab_selected()).unwrap().lists[list_section].items.get(list_idx).unwrap()
|
||||
}
|
||||
|
||||
pub fn sub_menu_next(&mut self) {
|
||||
match SubMenuType::from_str(self.sub_menu_selected()._type) {
|
||||
SubMenuType::TOGGLE => self.selected_sub_menu_toggles.next(),
|
||||
SubMenuType::SLIDER => self.selected_sub_menu_sliders.next(),
|
||||
SubMenuType::ONOFF => self.selected_sub_menu_onoff_selectors.next(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -151,7 +89,6 @@ impl<'a> App<'a> {
|
|||
match SubMenuType::from_str(self.sub_menu_selected()._type) {
|
||||
SubMenuType::TOGGLE => self.selected_sub_menu_toggles.next_list(),
|
||||
SubMenuType::SLIDER => self.selected_sub_menu_sliders.next_list(),
|
||||
SubMenuType::ONOFF => self.selected_sub_menu_onoff_selectors.next_list(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,7 +96,6 @@ impl<'a> App<'a> {
|
|||
match SubMenuType::from_str(self.sub_menu_selected()._type) {
|
||||
SubMenuType::TOGGLE => self.selected_sub_menu_toggles.previous(),
|
||||
SubMenuType::SLIDER => self.selected_sub_menu_sliders.previous(),
|
||||
SubMenuType::ONOFF => self.selected_sub_menu_onoff_selectors.previous(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -167,30 +103,22 @@ impl<'a> App<'a> {
|
|||
match SubMenuType::from_str(self.sub_menu_selected()._type) {
|
||||
SubMenuType::TOGGLE => self.selected_sub_menu_toggles.previous_list(),
|
||||
SubMenuType::SLIDER => self.selected_sub_menu_sliders.previous_list(),
|
||||
SubMenuType::ONOFF => self.selected_sub_menu_onoff_selectors.previous_list(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn sub_menu_strs_and_states(&mut self) -> (&str, &str, Vec<(Vec<(&str, &str)>, ListState)>) {
|
||||
(self.sub_menu_selected().title, self.sub_menu_selected().help_text,
|
||||
pub fn sub_menu_strs_and_states(&mut self) -> (&str, &str, Vec<(Vec<(bool, &str)>, ListState)>) {
|
||||
(self.sub_menu_selected().submenu_title, self.sub_menu_selected().help_text,
|
||||
match SubMenuType::from_str(self.sub_menu_selected()._type) {
|
||||
SubMenuType::TOGGLE => {
|
||||
self.selected_sub_menu_toggles.lists.iter().map(|toggle_list| {
|
||||
(toggle_list.items.iter().map(
|
||||
|toggle| (toggle.checked, toggle.title)
|
||||
|toggle| (toggle.checked, toggle.toggle_title)
|
||||
).collect(), toggle_list.state.clone())
|
||||
}).collect()
|
||||
},
|
||||
SubMenuType::SLIDER => {
|
||||
vec![(vec![], ListState::default())]
|
||||
},
|
||||
SubMenuType::ONOFF => {
|
||||
self.selected_sub_menu_onoff_selectors.lists.iter().map(|onoff_list| {
|
||||
(onoff_list.items.iter().map(
|
||||
|onoff| (onoff.checked, onoff.title)
|
||||
).collect(), onoff_list.state.clone())
|
||||
}).collect()
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -208,7 +136,7 @@ impl<'a> App<'a> {
|
|||
.items.get_mut(list_idx).unwrap();
|
||||
match SubMenuType::from_str(selected_sub_menu._type) {
|
||||
SubMenuType::TOGGLE => {
|
||||
let is_single_option = selected_sub_menu.is_single_option.is_some();
|
||||
let is_single_option = selected_sub_menu.is_single_option;
|
||||
let state = self.selected_sub_menu_toggles.state;
|
||||
self.selected_sub_menu_toggles.lists.iter_mut()
|
||||
.map(|list| (list.state.selected(), &mut list.items))
|
||||
|
@ -216,42 +144,31 @@ impl<'a> App<'a> {
|
|||
.enumerate()
|
||||
.for_each(|(i, o)|
|
||||
if state.is_some() && i == state.unwrap() {
|
||||
if o.checked != "is-appear" {
|
||||
o.checked = "is-appear";
|
||||
if !o.checked {
|
||||
o.checked = true;
|
||||
} else {
|
||||
o.checked = "is-hidden";
|
||||
o.checked = false;
|
||||
}
|
||||
} else if is_single_option {
|
||||
o.checked = "is-hidden";
|
||||
o.checked = false;
|
||||
}
|
||||
));
|
||||
selected_sub_menu.toggles.iter_mut()
|
||||
.enumerate()
|
||||
.for_each(|(i, o)| {
|
||||
if i == state {
|
||||
if o.checked != "is-appear" {
|
||||
o.checked = "is-appear";
|
||||
if !o.checked {
|
||||
o.checked = true;
|
||||
} else {
|
||||
o.checked = "is-hidden";
|
||||
o.checked = false;
|
||||
}
|
||||
} else if is_single_option {
|
||||
o.checked = "is-hidden";
|
||||
o.checked = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
SubMenuType::ONOFF => {
|
||||
let onoff = self.selected_sub_menu_onoff_selectors.selected_list_item();
|
||||
if onoff.checked != "is-appear" {
|
||||
onoff.checked = "is-appear";
|
||||
} else {
|
||||
onoff.checked = "is-hidden";
|
||||
}
|
||||
selected_sub_menu.onoffselector.iter_mut()
|
||||
.filter(|o| o.title == onoff.title)
|
||||
.for_each(|o| o.checked = onoff.checked);
|
||||
},
|
||||
SubMenuType::SLIDER => {
|
||||
// self.selected_sub_menu_sliders.selected_list_item().checked = "is-appear";
|
||||
// self.selected_sub_menu_sliders.selected_list_item().checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -352,17 +269,16 @@ pub fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) -> String {
|
|||
if app.outer_list {
|
||||
let tab_selected = app.tab_selected();
|
||||
let mut item_help = None;
|
||||
for list_section in 0..app.menu_items.get(tab_selected).unwrap().lists.len() {
|
||||
let stateful_list = &app.menu_items.get(tab_selected).unwrap().lists[list_section];
|
||||
for (list_section, stateful_list) in app.menu_items.get(tab_selected).unwrap().lists.iter().enumerate() {
|
||||
let items: Vec<ListItem> = stateful_list
|
||||
.items
|
||||
.iter()
|
||||
.map(|i| {
|
||||
let lines = vec![Spans::from(
|
||||
if stateful_list.state.selected().is_some() {
|
||||
i.title.to_owned()
|
||||
i.submenu_title.to_owned()
|
||||
} else {
|
||||
" ".to_owned() + i.title
|
||||
" ".to_owned() + i.submenu_title
|
||||
})];
|
||||
ListItem::new(lines).style(Style::default().fg(Color::White))
|
||||
})
|
||||
|
@ -389,7 +305,7 @@ pub fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) -> String {
|
|||
|
||||
// TODO: Add Save Defaults
|
||||
let help_paragraph = Paragraph::new(
|
||||
item_help.unwrap_or("").replace("\"", "") +
|
||||
item_help.unwrap_or("").replace('\"', "") +
|
||||
"\nA: Enter sub-menu | B: Exit menu | ZL/ZR: Next tab"
|
||||
).style(Style::default().fg(Color::Cyan));
|
||||
f.render_widget(help_paragraph, vertical_chunks[2]);
|
||||
|
@ -401,7 +317,7 @@ pub fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) -> String {
|
|||
let values_items: Vec<ListItem> = sub_menu_str.iter().map(|s| {
|
||||
ListItem::new(
|
||||
vec![
|
||||
Spans::from((if s.0 == "is-appear" { "X " } else { " " }).to_owned() + s.1)
|
||||
Spans::from((if s.0 { "X " } else { " " }).to_owned() + s.1)
|
||||
]
|
||||
)
|
||||
}).collect();
|
||||
|
@ -419,7 +335,7 @@ pub fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) -> String {
|
|||
}
|
||||
|
||||
let help_paragraph = Paragraph::new(
|
||||
help_text.replace("\"", "") +
|
||||
help_text.replace('\"', "") +
|
||||
"\nA: Select toggle | B: Exit submenu"
|
||||
).style(Style::default().fg(Color::Cyan));
|
||||
f.render_widget(help_paragraph, vertical_chunks[2]);
|
||||
|
@ -433,22 +349,17 @@ pub fn ui<B: Backend>(f: &mut Frame<B>, app: &mut App) -> String {
|
|||
for key in app.menu_items.keys() {
|
||||
for list in &app.menu_items.get(key).unwrap().lists {
|
||||
for sub_menu in &list.items {
|
||||
let mut val = String::new();
|
||||
sub_menu.toggles.iter()
|
||||
.filter(|t| t.checked == "is-appear")
|
||||
.for_each(|t| val.push_str(format!("{},", t.value).as_str()));
|
||||
let val : usize = sub_menu.toggles.iter()
|
||||
.filter(|t| t.checked)
|
||||
.map(|t| t.toggle_value)
|
||||
.sum();
|
||||
|
||||
sub_menu.onoffselector.iter()
|
||||
.for_each(|o| {
|
||||
val.push_str(
|
||||
format!("{}", if o.checked == "is-appear" { 1 } else { 0 }).as_str())
|
||||
});
|
||||
settings.insert(sub_menu.id, val);
|
||||
settings.insert(sub_menu.submenu_id, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
url.push_str("?");
|
||||
url.push('?');
|
||||
settings.iter()
|
||||
.for_each(|(section, val)| url.push_str(format!("{}={}&", section, val).as_str()));
|
||||
url
|
||||
|
|
|
@ -49,14 +49,14 @@ impl<T: Clone> MultiStatefulList<T> {
|
|||
state.select(Some(0));
|
||||
}
|
||||
StatefulList {
|
||||
state: state,
|
||||
state,
|
||||
items: items[list_section_min_idx..list_section_max_idx].to_vec(),
|
||||
}
|
||||
}).collect();
|
||||
let total_len = items.len();
|
||||
MultiStatefulList {
|
||||
lists: lists,
|
||||
total_len: total_len,
|
||||
lists,
|
||||
total_len,
|
||||
state: 0
|
||||
}
|
||||
}
|
||||
|
@ -68,12 +68,11 @@ impl<T: Clone> MultiStatefulList<T> {
|
|||
if list_section != next_list_section {
|
||||
self.lists[list_section].unselect();
|
||||
}
|
||||
let state;
|
||||
if self.state + 1 >= self.total_len {
|
||||
state = (0, 0);
|
||||
let state= if self.state + 1 >= self.total_len {
|
||||
(0, 0)
|
||||
} else {
|
||||
state = (next_list_section, next_list_idx);
|
||||
}
|
||||
(next_list_section, next_list_idx)
|
||||
};
|
||||
|
||||
self.lists[state.0].state.select(Some(state.1));
|
||||
self.state = self.list_idx_to_idx(state);
|
||||
|
@ -84,13 +83,12 @@ impl<T: Clone> MultiStatefulList<T> {
|
|||
let (last_list_section, last_list_idx) = (self.lists.len() - 1, self.lists[self.lists.len() - 1].items.len() - 1);
|
||||
|
||||
self.lists[list_section].unselect();
|
||||
let state;
|
||||
if self.state == 0 {
|
||||
state = (last_list_section, last_list_idx);
|
||||
let state= if self.state == 0 {
|
||||
(last_list_section, last_list_idx)
|
||||
} else {
|
||||
let (prev_list_section, prev_list_idx) = self.idx_to_list_idx(self.state - 1);
|
||||
state = (prev_list_section, prev_list_idx);
|
||||
}
|
||||
(prev_list_section, prev_list_idx)
|
||||
};
|
||||
|
||||
self.lists[state.0].state.select(Some(state.1));
|
||||
self.state = self.list_idx_to_idx(state);
|
||||
|
@ -99,12 +97,11 @@ impl<T: Clone> MultiStatefulList<T> {
|
|||
pub fn next_list(&mut self) {
|
||||
let (list_section, list_idx) = self.idx_to_list_idx(self.state);
|
||||
let next_list_section = (list_section + 1) % self.lists.len();
|
||||
let next_list_idx;
|
||||
if list_idx > self.lists[next_list_section].items.len() - 1 {
|
||||
next_list_idx = self.lists[next_list_section].items.len() - 1;
|
||||
let next_list_idx = if list_idx > self.lists[next_list_section].items.len() - 1 {
|
||||
self.lists[next_list_section].items.len() - 1
|
||||
} else {
|
||||
next_list_idx = list_idx;
|
||||
}
|
||||
list_idx
|
||||
};
|
||||
|
||||
if list_section != next_list_section {
|
||||
self.lists[list_section].unselect();
|
||||
|
@ -117,19 +114,17 @@ impl<T: Clone> MultiStatefulList<T> {
|
|||
|
||||
pub fn previous_list(&mut self) {
|
||||
let (list_section, list_idx) = self.idx_to_list_idx(self.state);
|
||||
let prev_list_section;
|
||||
if list_section == 0 {
|
||||
prev_list_section = self.lists.len() - 1;
|
||||
let prev_list_section = if list_section == 0 {
|
||||
self.lists.len() - 1
|
||||
} else {
|
||||
prev_list_section = list_section - 1;
|
||||
}
|
||||
list_section - 1
|
||||
};
|
||||
|
||||
let prev_list_idx;
|
||||
if list_idx > self.lists[prev_list_section].items.len() - 1 {
|
||||
prev_list_idx = self.lists[prev_list_section].items.len() - 1;
|
||||
let prev_list_idx= if list_idx > self.lists[prev_list_section].items.len() - 1 {
|
||||
self.lists[prev_list_section].items.len() - 1
|
||||
} else {
|
||||
prev_list_idx = list_idx;
|
||||
}
|
||||
list_idx
|
||||
};
|
||||
|
||||
if list_section != prev_list_section {
|
||||
self.lists[list_section].unselect();
|
||||
|
@ -152,7 +147,7 @@ impl<T> StatefulList<T> {
|
|||
// Enforce state as first of list
|
||||
state.select(Some(0));
|
||||
StatefulList {
|
||||
state: state,
|
||||
state,
|
||||
items,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,10 +32,10 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
let mut url = String::new();
|
||||
let frame_res = terminal.draw(|f| url = training_mod_tui::ui(f, &mut app))?;
|
||||
|
||||
for (i, cell) in frame_res.buffer.content().into_iter().enumerate() {
|
||||
for (i, cell) in frame_res.buffer.content().iter().enumerate() {
|
||||
print!("{}", cell.symbol);
|
||||
if i % frame_res.area.width as usize == frame_res.area.width as usize - 1 {
|
||||
print!("\n");
|
||||
println!();
|
||||
}
|
||||
}
|
||||
println!();
|
||||
|
|
Loading…
Reference in a new issue