diff --git a/sphaira/include/ui/menus/gc_menu.hpp b/sphaira/include/ui/menus/gc_menu.hpp index 2c1e42c..5e35041 100644 --- a/sphaira/include/ui/menus/gc_menu.hpp +++ b/sphaira/include/ui/menus/gc_menu.hpp @@ -46,6 +46,7 @@ struct Menu final : MenuBase { auto GetShortTitle() const -> const char* override { return "GC"; }; void Update(Controller* controller, TouchInfo* touch) override; void Draw(NVGcontext* vg, Theme* theme) override; + void OnFocusGained() override; private: Result GcMount(); diff --git a/sphaira/include/ui/menus/main_menu.hpp b/sphaira/include/ui/menus/main_menu.hpp index 35178b0..c076975 100644 --- a/sphaira/include/ui/menus/main_menu.hpp +++ b/sphaira/include/ui/menus/main_menu.hpp @@ -17,6 +17,32 @@ enum class UpdateState { Error, }; +using MiscMenuFunction = std::function(void)>; + +enum MiscMenuFlag : u8 { + // can be set as the rightside menu. + MiscMenuFlag_Shortcut = 1 << 0, + // needs install option to be enabled. + MiscMenuFlag_Install = 1 << 1, +}; + +struct MiscMenuEntry { + const char* name; + const char* title; + MiscMenuFunction func; + u8 flag; + + auto IsShortcut() const -> bool { + return flag & MiscMenuFlag_Shortcut; + } + + auto IsInstall() const -> bool { + return flag & MiscMenuFlag_Install; + } +}; + +auto GetMiscMenuEntries() -> std::span; + // this holds 2 menus and allows for switching between them struct MainMenu final : Widget { MainMenu(); diff --git a/sphaira/source/app.cpp b/sphaira/source/app.cpp index 3cfa94c..9e1beff 100644 --- a/sphaira/source/app.cpp +++ b/sphaira/source/app.cpp @@ -7,14 +7,6 @@ #include "ui/error_box.hpp" #include "ui/menus/main_menu.hpp" -#include "ui/menus/irs_menu.hpp" -#include "ui/menus/themezer.hpp" -#include "ui/menus/ghdl.hpp" -#include "ui/menus/usb_menu.hpp" -#include "ui/menus/ftp_menu.hpp" -#include "ui/menus/gc_menu.hpp" -#include "ui/menus/game_menu.hpp" -#include "ui/menus/appstore.hpp" #include "app.hpp" #include "log.hpp" @@ -1520,24 +1512,19 @@ void App::DisplayMiscOptions(bool left_side) { auto options = std::make_shared("Misc Options"_i18n, left_side ? ui::Sidebar::Side::LEFT : ui::Sidebar::Side::RIGHT); ON_SCOPE_EXIT(App::Push(options)); - const auto push_if_not_same = [&](const char* name, const char* title){ - if (g_app->m_right_side_menu.Get() != name) { - options->Add(std::make_shared(i18n::get(title), [](){ - App::Push(std::make_shared()); - })); + for (auto& e : ui::menu::main::GetMiscMenuEntries()) { + if (e.name == g_app->m_right_side_menu.Get()) { + continue; } - }; - push_if_not_same.template operator()("Appstore", "Appstore"); - push_if_not_same.template operator()("Games", "Games"); - push_if_not_same.template operator()("Themezer", "Themezer"); - push_if_not_same.template operator()("GitHub", "GitHub"); - if (App::GetInstallEnable()) { - push_if_not_same.template operator()("FTP", "FTP Install"); - push_if_not_same.template operator()("USB", "USB Install"); - push_if_not_same.template operator()("GameCard", "GameCard Install"); + if (e.IsInstall() && !App::GetInstallEnable()) { + continue; + } + + options->Add(std::make_shared(i18n::get(e.title), [e](){ + App::Push(e.func()); + })); } - push_if_not_same.template operator()("IRS", "IRS (Infrared Joycon Camera)"); } void App::DisplayAdvancedOptions(bool left_side) { @@ -1549,12 +1536,18 @@ void App::DisplayAdvancedOptions(bool left_side) { text_scroll_speed_items.push_back("Normal"_i18n); text_scroll_speed_items.push_back("Fast"_i18n); - static constexpr std::array menu_names{ - "Appstore", - "Games", - "GitHub", - "IRS", - }; + std::vector menu_names; + for (auto& e : ui::menu::main::GetMiscMenuEntries()) { + if (!e.IsShortcut()) { + continue; + } + + if (e.IsInstall() && !App::GetInstallEnable()) { + continue; + } + + menu_names.emplace_back(e.name); + } ui::SidebarEntryArray::Items right_side_menu_items; for (auto& str : menu_names) { @@ -1578,7 +1571,7 @@ void App::DisplayAdvancedOptions(bool left_side) { App::SetTextScrollSpeed(index_out); }, App::GetTextScrollSpeed())); - options->Add(std::make_shared("Set right-side menu"_i18n, right_side_menu_items, [](s64& index_out){ + options->Add(std::make_shared("Set right-side menu"_i18n, right_side_menu_items, [menu_names](s64& index_out){ const auto e = menu_names[index_out]; if (g_app->m_right_side_menu.Get() != e) { g_app->m_right_side_menu.Set(e); diff --git a/sphaira/source/ui/menus/gc_menu.cpp b/sphaira/source/ui/menus/gc_menu.cpp index dd5fed7..17aa6d2 100644 --- a/sphaira/source/ui/menus/gc_menu.cpp +++ b/sphaira/source/ui/menus/gc_menu.cpp @@ -184,8 +184,6 @@ Menu::Menu() : MenuBase{"GameCard"_i18n} { fsOpenDeviceOperator(std::addressof(m_dev_op)); fsOpenGameCardDetectionEventNotifier(std::addressof(m_event_notifier)); fsEventNotifierGetEventHandle(std::addressof(m_event_notifier), std::addressof(m_event), true); - GcOnEvent(); - UpdateStorageSize(); } Menu::~Menu() { @@ -269,6 +267,13 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) { }); } +void Menu::OnFocusGained() { + MenuBase::OnFocusGained(); + + GcOnEvent(); + UpdateStorageSize(); +} + Result Menu::GcMount() { GcUnmount(); @@ -391,20 +396,18 @@ Result Menu::GcMount() { } else { App::Notify("Gc install failed!"_i18n); } - - UpdateStorageSize(); })); } } }}); if (m_entries.size() > 1) { - SetAction(Button::L, Action{"Prev"_i18n, [this](){ + SetAction(Button::L2, Action{"Prev"_i18n, [this](){ if (m_entry_index != 0) { OnChangeIndex(m_entry_index - 1); } }}); - SetAction(Button::R, Action{"Next"_i18n, [this](){ + SetAction(Button::R2, Action{"Next"_i18n, [this](){ if (m_entry_index < m_entries.size()) { OnChangeIndex(m_entry_index + 1); } @@ -424,8 +427,8 @@ void Menu::GcUnmount() { m_lang_entry = {}; FreeImage(); - RemoveAction(Button::L); - RemoveAction(Button::R); + RemoveAction(Button::L2); + RemoveAction(Button::R2); } Result Menu::GcPoll(bool* inserted) { diff --git a/sphaira/source/ui/menus/main_menu.cpp b/sphaira/source/ui/menus/main_menu.cpp index d17a5b8..9ccfb3e 100644 --- a/sphaira/source/ui/menus/main_menu.cpp +++ b/sphaira/source/ui/menus/main_menu.cpp @@ -31,6 +31,22 @@ namespace { constexpr const char* GITHUB_URL{"https://api.github.com/repos/ITotalJustice/sphaira/releases/latest"}; constexpr fs::FsPath CACHE_PATH{"/switch/sphaira/cache/sphaira_latest.json"}; +template +auto MiscMenuFuncGenerator() { + return std::make_shared(); +} + +const MiscMenuEntry MISC_MENU_ENTRIES[] = { + { .name = "Appstore", .title = "Appstore", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut }, + { .name = "Games", .title = "Games", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut }, + { .name = "Themezer", .title = "Themezer", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut }, + { .name = "GitHub", .title = "GitHub", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut }, + { .name = "FTP", .title = "FTP Install", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Install }, + { .name = "USB", .title = "USB Install", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Install }, + { .name = "GameCard", .title = "GameCard Install", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut|MiscMenuFlag_Install }, + { .name = "IRS", .title = "IRS (Infrared Joycon Camera)", .func = MiscMenuFuncGenerator, .flag = MiscMenuFlag_Shortcut }, +}; + auto InstallUpdate(ProgressBox* pbox, const std::string url, const std::string version) -> bool { static fs::FsPath zip_out{"/switch/sphaira/cache/update.zip"}; constexpr auto chunk_size = 1024 * 512; // 512KiB @@ -151,22 +167,10 @@ auto InstallUpdate(ProgressBox* pbox, const std::string url, const std::string v auto CreateRightSideMenu() -> std::shared_ptr { const auto name = App::GetApp()->m_right_side_menu.Get(); - if ("Games" == name) { - return std::make_shared(); - }/*else if ("Themezer" == name) { - return std::make_shared(); - }*/else if ("GitHub" == name) { - return std::make_shared(); - } else if ("IRS" == name) { - return std::make_shared(); - } else if (App::GetInstallEnable()) { - // if ("FTP" == name) { - // return std::make_shared(); - // } else if ("USB" == name) { - // return std::make_shared(); - // } else if ("GameCard" == name) { - // return std::make_shared(); - // } + for (auto& e : GetMiscMenuEntries()) { + if (e.name == name) { + return e.func(); + } } return std::make_shared(); @@ -174,6 +178,10 @@ auto CreateRightSideMenu() -> std::shared_ptr { } // namespace +auto GetMiscMenuEntries() -> std::span { + return MISC_MENU_ENTRIES; +} + MainMenu::MainMenu() { curl::Api().ToFileAsync( curl::Url{GITHUB_URL}, @@ -241,9 +249,7 @@ MainMenu::MainMenu() { this->SetActions( std::make_pair(Button::START, Action{App::Exit}), - std::make_pair(Button::SELECT, Action{"Misc"_i18n, [this](){ - App::DisplayMiscOptions(); - }}), + std::make_pair(Button::SELECT, Action{App::DisplayMiscOptions}), std::make_pair(Button::Y, Action{"Menu"_i18n, [this](){ auto options = std::make_shared("Menu Options"_i18n, "v" APP_VERSION_HASH, Sidebar::Side::LEFT); ON_SCOPE_EXIT(App::Push(options)); diff --git a/sphaira/source/ui/menus/themezer.cpp b/sphaira/source/ui/menus/themezer.cpp index b2be6ac..9fa0ed6 100644 --- a/sphaira/source/ui/menus/themezer.cpp +++ b/sphaira/source/ui/menus/themezer.cpp @@ -453,7 +453,7 @@ Menu::Menu() : MenuBase{"Themezer"_i18n} { } })); }}), - std::make_pair(Button::R, Action{"Next Page"_i18n, [this](){ + std::make_pair(Button::R2, Action{"Next"_i18n, [this](){ m_page_index++; if (m_page_index >= m_page_index_max) { m_page_index = m_page_index_max - 1; @@ -461,7 +461,7 @@ Menu::Menu() : MenuBase{"Themezer"_i18n} { PackListDownload(); } }}), - std::make_pair(Button::L, Action{"Prev Page"_i18n, [this](){ + std::make_pair(Button::L2, Action{"Prev"_i18n, [this](){ if (m_page_index) { m_page_index--; PackListDownload(); diff --git a/sphaira/source/ui/nvg_util.cpp b/sphaira/source/ui/nvg_util.cpp index 987cdf4..49b2a67 100644 --- a/sphaira/source/ui/nvg_util.cpp +++ b/sphaira/source/ui/nvg_util.cpp @@ -21,10 +21,8 @@ constexpr std::array buttons = { std::pair{Button::Y, "\uE0E3"}, std::pair{Button::L, "\uE0E4"}, std::pair{Button::R, "\uE0E5"}, - std::pair{Button::L, "\uE0E6"}, - std::pair{Button::R, "\uE0E7"}, - std::pair{Button::L2, "\uE0E8"}, - std::pair{Button::R2, "\uE0E9"}, + std::pair{Button::L2, "\uE0E6"}, + std::pair{Button::R2, "\uE0E7"}, std::pair{Button::UP, "\uE0EB"}, std::pair{Button::DOWN, "\uE0EC"}, std::pair{Button::LEFT, "\uE0ED"}, diff --git a/sphaira/source/ui/widget.cpp b/sphaira/source/ui/widget.cpp index 9f52010..0f159e3 100644 --- a/sphaira/source/ui/widget.cpp +++ b/sphaira/source/ui/widget.cpp @@ -86,6 +86,11 @@ auto Widget::GetUiButtons() const -> uiButtons { uiButtons draw_actions; draw_actions.reserve(m_actions.size()); + const std::pair swap_buttons[] = { + {Button::L, Button::R}, + {Button::L2, Button::R2}, + }; + // build array for (const auto& [button, action] : m_actions) { if (action.IsHidden() || action.m_hint.empty()) { @@ -94,13 +99,19 @@ auto Widget::GetUiButtons() const -> uiButtons { uiButton ui_button{button, action}; - // swap - if (button == Button::R && draw_actions.size() && draw_actions.back().m_button == Button::L) { - const auto s = draw_actions.back(); - draw_actions.back().m_button = button; - draw_actions.back().m_action = action; - draw_actions.emplace_back(s); - } else { + bool should_swap = false; + for (auto [left, right] : swap_buttons) { + if (button == right && draw_actions.size() && draw_actions.back().m_button == left) { + const auto s = draw_actions.back(); + draw_actions.back().m_button = button; + draw_actions.back().m_action = action; + draw_actions.emplace_back(s); + should_swap = true; + break; + } + } + + if (!should_swap) { draw_actions.emplace_back(ui_button); } }