mirror of
https://github.com/jugeeya/UltimateTrainingModpack.git
synced 2024-11-24 10:54:16 +00:00
Change Menu Buttons and Help Text; HUD Off Outside Training (#488)
* Avoid making layouts invisible outside training mode with HUD off * Switch menu buttons around; change defaults resetting texts * Fix unused feature import
This commit is contained in:
parent
7fb93ea309
commit
d05f8ce918
5 changed files with 206 additions and 158 deletions
|
@ -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(|| {
|
||||
|
|
|
@ -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::<Vec<String>>()
|
||||
.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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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::<TrainingModpackMenu>(&json).unwrap()), json);
|
||||
self.default_menu = (
|
||||
ui_menu(serde_json::from_str::<TrainingModpackMenu>(&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::<serde_json::Value>(&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::<serde_json::Value>(&self.default_menu.1).unwrap();
|
||||
let default_json_value =
|
||||
serde_json::from_str::<serde_json::Value>(&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::<TrainingModpackMenu>(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::<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::<Vec<&str>>();
|
||||
}
|
||||
}
|
||||
|
||||
fn render_submenu_page<B: Backend>(f: &mut Frame<B>, app: &mut App, list_chunks: Vec<Rect>, help_chunk: Rect) {
|
||||
fn render_submenu_page<B: Backend>(
|
||||
f: &mut Frame<B>,
|
||||
app: &mut App,
|
||||
list_chunks: Vec<Rect>,
|
||||
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<B: Backend>(f: &mut Frame<B>, 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<B: Backend>(f: &mut Frame<B>, app: &mut App, list_chunks: Vec<Rect>, help_chunk: Rect) {
|
||||
pub fn render_toggle_page<B: Backend>(
|
||||
f: &mut Frame<B>,
|
||||
app: &mut App,
|
||||
list_chunks: Vec<Rect>,
|
||||
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<B: Backend>(f: &mut Frame<B>, 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<B: Backend>(f: &mut Frame<B>, app: &mut App, vertical_chunk: Rect, help_chunk: Rect) {
|
||||
pub fn render_slider_page<B: Backend>(
|
||||
f: &mut Frame<B>,
|
||||
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<B: Backend>(f: &mut Frame<B>, 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<B: Backend>(f: &mut Frame<B>, 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<B: Backend>(f: &mut Frame<B>, 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<B: Backend>(f: &mut Frame<B>, 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<B: Backend>(f: &mut Frame<B>, 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<Rect> = 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<B: Backend>(f: &mut Frame<B>, 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!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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::TestBackend>, training_mod_tui::App<'a>),
|
||||
Box<dyn Error>> {
|
||||
fn test_backend_setup<'a>(
|
||||
ui_menu: UiMenu<'a>,
|
||||
menu_defaults: (UiMenu<'a>, String),
|
||||
) -> Result<
|
||||
(
|
||||
Terminal<training_mod_tui::TestBackend>,
|
||||
training_mod_tui::App<'a>,
|
||||
),
|
||||
Box<dyn Error>,
|
||||
> {
|
||||
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<dyn Error>> {
|
|||
// 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<dyn Error>> {
|
|||
);
|
||||
|
||||
// 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::<MenuJsonStruct>(&menu_json).unwrap();
|
||||
let menu = menu_struct.menu;
|
||||
|
@ -155,7 +162,7 @@ fn test_save_and_reset_defaults() -> Result<(), Box<dyn Error>> {
|
|||
// 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<dyn Error>> {
|
|||
// 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::<MenuJsonStruct>(&menu_json).unwrap();
|
||||
let menu = menu_struct.menu;
|
||||
|
@ -177,7 +184,6 @@ fn test_save_and_reset_defaults() -> Result<(), Box<dyn Error>> {
|
|||
"The menu should have Mash Airdodge off and Followup Jump on"
|
||||
);
|
||||
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -192,16 +198,19 @@ fn main() -> Result<(), Box<dyn Error>> {
|
|||
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<dyn Error>> {
|
|||
"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<dyn Error>> {
|
|||
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<B: tui::backend::Backend>(
|
|||
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(),
|
||||
|
|
Loading…
Reference in a new issue