diff --git a/README.md b/README.md index 389deaf..3d035d2 100644 --- a/README.md +++ b/README.md @@ -51,3 +51,5 @@ see `assets/romfs/assoc/` for more examples of file assoc entries - libpulsar - minIni - gbatemp +- hb-appstore +- everyone who has contributed to this project! diff --git a/sphaira/include/nro.hpp b/sphaira/include/nro.hpp index 1f87795..d7742ab 100644 --- a/sphaira/include/nro.hpp +++ b/sphaira/include/nro.hpp @@ -14,20 +14,20 @@ struct Hbini { }; struct NroEntry { - fs::FsPath path; - s64 size; - NacpStruct nacp; + fs::FsPath path{}; + s64 size{}; + NacpStruct nacp{}; - std::vector icon; - u64 icon_size; - u64 icon_offset; + std::vector icon{}; + u64 icon_size{}; + u64 icon_offset{}; - FsTimeStampRaw timestamp; - Hbini hbini; + FsTimeStampRaw timestamp{}; + Hbini hbini{}; - int image; // nvg image - int x,y,w,h; // image - bool is_nacp_valid; + int image{}; // nvg image + int x,y,w,h{}; // image + bool is_nacp_valid{}; auto GetName() const -> const char* { return nacp.lang[0].name; diff --git a/sphaira/include/swkbd.hpp b/sphaira/include/swkbd.hpp index 3c76c9f..346204c 100644 --- a/sphaira/include/swkbd.hpp +++ b/sphaira/include/swkbd.hpp @@ -5,7 +5,7 @@ namespace sphaira::swkbd { -Result ShowText(std::string& out, const char* guide = nullptr, s64 len_min = -1, s64 len_max = -1); -Result ShowNumPad(s64& out, const char* guide = nullptr, s64 len_min = -1, s64 len_max = -1); +Result ShowText(std::string& out, const char* guide = nullptr, const char* initial = nullptr, s64 len_min = -1, s64 len_max = FS_MAX_PATH); +Result ShowNumPad(s64& out, const char* guide = nullptr, const char* initial = nullptr, s64 len_min = -1, s64 len_max = FS_MAX_PATH); } // namespace sphaira::swkbd diff --git a/sphaira/include/ui/menus/homebrew.hpp b/sphaira/include/ui/menus/homebrew.hpp index c517493..d385c30 100644 --- a/sphaira/include/ui/menus/homebrew.hpp +++ b/sphaira/include/ui/menus/homebrew.hpp @@ -36,6 +36,9 @@ struct Menu final : MenuBase { return m_entries; } + static Result InstallHomebrew(const fs::FsPath& path, const NacpStruct& nacp, const std::vector& icon); + static Result InstallHomebrewFromPath(const fs::FsPath& path); + private: static constexpr inline const char* INI_SECTION = "homebrew"; diff --git a/sphaira/include/ui/widget.hpp b/sphaira/include/ui/widget.hpp index cdef18a..381d0de 100644 --- a/sphaira/include/ui/widget.hpp +++ b/sphaira/include/ui/widget.hpp @@ -26,16 +26,6 @@ struct Widget : public Object { return m_focus; } - // void PushWidget(std::shared_ptr widget); - // void PopWidget(); - - void SetParent(Widget* parent) { - m_parent = parent; - } - auto GetParent() -> Widget* { - return m_parent; - } - auto HasAction(Button button) const -> bool; void SetAction(Button button, Action action); void SetActions(std::same_as> auto ...args) { @@ -66,8 +56,6 @@ struct Widget : public Object { using Actions = std::map; // using Actions = std::unordered_map; Actions m_actions; - Widget* m_parent{}; - // std::vector> widgets; bool m_focus{false}; bool m_pop{false}; }; diff --git a/sphaira/source/app.cpp b/sphaira/source/app.cpp index b4bb565..6eca75b 100644 --- a/sphaira/source/app.cpp +++ b/sphaira/source/app.cpp @@ -524,7 +524,7 @@ void App::Poll() { m_touch_info.finger_id = touch_state.touches[0].finger_id; m_touch_info.is_touching = true; m_touch_info.is_tap = true; - PlaySoundEffect(SoundEffect_Limit); + // PlaySoundEffect(SoundEffect_Limit); } else if (touch_state.count >= 1 && m_touch_info.is_touching && m_touch_info.finger_id == touch_state.touches[0].finger_id) { m_touch_info.prev_x = m_touch_info.cur_x; m_touch_info.prev_y = m_touch_info.cur_y; @@ -597,39 +597,6 @@ auto App::GetVg() -> NVGcontext* { return g_app->vg; } -#if 0 -void App::UpdateList() { - const auto index_copy = this->index; - const auto start_copy = this->start; - - else if (controller.down) { - // todo: replace with actual focus - PlaySoundEffect(SoundEffect_Limit); - } - - if (controller.any_direction()) { - if (index_copy != this->index) { - PlaySoundEffect(SoundEffect_Focus); - if (start_copy != this->start) { - // float r = randomGet64() % 100; - // float pitch = r / 100.0; - // plsrPlayerSetPitch(m_sound_ids[SoundEffect_Scroll], pitch); - PlaySoundEffect(SoundEffect_Scroll); - } - - if (this->index == 0 || this->index == this->nro_entries.size() || ((controller.down & HidNpadButton_AnyLeft) && this->index && this->index % 3 == 0) || ((controller.down & HidNpadButton_AnyRight) && this->index && (this->index + 1) % 3 == 0)) { - PlaySoundEffect(SoundEffect_Limit); - } - } else { - const auto mask = HidNpadButton_AnyDown | HidNpadButton_AnyUp | HidNpadButton_AnyLeft | HidNpadButton_AnyRight; - if (controller.down & mask) { - PlaySoundEffect(SoundEffect_Limit); - } - } - } -} -#endif - void DrawElement(float x, float y, float w, float h, ThemeEntryID id) { const auto& e = g_app->m_theme.elements[id]; diff --git a/sphaira/source/i18n.cpp b/sphaira/source/i18n.cpp index 57ca8c9..08527ae 100644 --- a/sphaira/source/i18n.cpp +++ b/sphaira/source/i18n.cpp @@ -78,8 +78,18 @@ bool init(long index) { default: lang_name = "en"; break; } - const fs::FsPath path = "romfs:/i18n/" + lang_name + ".json"; - if (R_SUCCEEDED(fs::FsStdio().read_entire_file(path, g_i18n_data))) { + const fs::FsPath sdmc_path = "/config/sphaira/i18n/" + lang_name + ".json"; + const fs::FsPath romfs_path = "romfs:/i18n/" + lang_name + ".json"; + fs::FsPath path = sdmc_path; + + // try and load override translation first + Result rc = fs::FsNativeSd().read_entire_file(path, g_i18n_data); + if (R_FAILED(rc)) { + path = romfs_path; + rc = fs::FsStdio().read_entire_file(path, g_i18n_data); + } + + if (R_SUCCEEDED(rc)) { json = yyjson_read((const char*)g_i18n_data.data(), g_i18n_data.size(), YYJSON_READ_ALLOW_TRAILING_COMMAS|YYJSON_READ_ALLOW_COMMENTS|YYJSON_READ_ALLOW_INVALID_UNICODE); if (json) { root = yyjson_doc_get_root(json); diff --git a/sphaira/source/nro.cpp b/sphaira/source/nro.cpp index 337407d..f6c8f1f 100644 --- a/sphaira/source/nro.cpp +++ b/sphaira/source/nro.cpp @@ -71,7 +71,6 @@ auto nro_parse_internal(fs::FsNative& fs, const fs::FsPath& path, NroEntry& entr } else { R_TRY(fsFileRead(&f, data.header.size + asset.nacp.offset, &entry.nacp, sizeof(entry.nacp), FsReadOption_None, &bytes_read)); entry.is_nacp_valid = true; - log_write("got nacp\n"); } // lazy load the icons @@ -241,7 +240,7 @@ auto nro_get_icon(const fs::FsPath& path) -> std::vector { R_TRY_RESULT(fsFileRead(&f, data.header.size, &asset, sizeof(asset), FsReadOption_None, &bytes_read), {}); R_UNLESS(asset.magic == NROASSETHEADER_MAGIC, {}); - return nro_get_icon_internal(&f, asset.icon.size, asset.icon.offset); + return nro_get_icon_internal(&f, asset.icon.size, data.header.size + asset.icon.offset); } auto nro_get_nacp(const fs::FsPath& path, NacpStruct& nacp) -> Result { diff --git a/sphaira/source/nxlink.cpp b/sphaira/source/nxlink.cpp index 9fd6b4e..d0f2f3c 100644 --- a/sphaira/source/nxlink.cpp +++ b/sphaira/source/nxlink.cpp @@ -224,7 +224,7 @@ void loop(void* args) { }; while (!g_quit) { - svcSleepThread(33'333'333); + svcSleepThread(1000000); if (poll_network_change()) { continue; @@ -267,7 +267,7 @@ void loop(void* args) { sockaddr_in sa_remote{}; while (!g_quit) { - svcSleepThread(33'333'333); + svcSleepThread(10000); if (poll_network_change()) { break; @@ -297,7 +297,7 @@ void loop(void* args) { } fs::FsPath name{}; - if (namelen > sizeof(name)) { + if (namelen >= sizeof(name)) { log_write("namelen is bigger than name: 0x%X\n", socketGetLastResult()); continue; } diff --git a/sphaira/source/swkbd.cpp b/sphaira/source/swkbd.cpp index a881f94..fb986ea 100644 --- a/sphaira/source/swkbd.cpp +++ b/sphaira/source/swkbd.cpp @@ -10,7 +10,7 @@ struct Config { bool numpad{}; }; -Result ShowInternal(Config& cfg, const char* guide, s64 len_min, s64 len_max) { +Result ShowInternal(Config& cfg, const char* guide, const char* initial, s64 len_min, s64 len_max) { SwkbdConfig c; R_TRY(swkbdCreate(&c, 0)); swkbdConfigMakePresetDefault(&c); @@ -24,6 +24,10 @@ Result ShowInternal(Config& cfg, const char* guide, s64 len_min, s64 len_max) { swkbdConfigSetGuideText(&c, guide); } + if (initial) { + swkbdConfigSetInitialText(&c, initial); + } + if (len_min >= 0) { swkbdConfigSetStringLenMin(&c, len_min); } @@ -37,16 +41,16 @@ Result ShowInternal(Config& cfg, const char* guide, s64 len_min, s64 len_max) { } // namespace -Result ShowText(std::string& out, const char* guide, s64 len_min, s64 len_max) { +Result ShowText(std::string& out, const char* guide, const char* initial, s64 len_min, s64 len_max) { Config cfg; - R_TRY(ShowInternal(cfg, guide, len_min, len_max)); + R_TRY(ShowInternal(cfg, guide, initial, len_min, len_max)); out = cfg.out_text; R_SUCCEED(); } -Result ShowNumPad(s64& out, const char* guide, s64 len_min, s64 len_max) { +Result ShowNumPad(s64& out, const char* guide, const char* initial, s64 len_min, s64 len_max) { Config cfg; - R_TRY(ShowInternal(cfg, guide, len_min, len_max)); + R_TRY(ShowInternal(cfg, guide, initial, len_min, len_max)); out = std::atoll(cfg.out_text); R_SUCCEED(); } diff --git a/sphaira/source/ui/menus/appstore.cpp b/sphaira/source/ui/menus/appstore.cpp index 5f86adb..66da279 100644 --- a/sphaira/source/ui/menus/appstore.cpp +++ b/sphaira/source/ui/menus/appstore.cpp @@ -661,11 +661,13 @@ EntryMenu::EntryMenu(Entry& entry, const LazyImage& default_icon, Menu& menu) std::make_pair(Button::DPAD_DOWN | Button::RS_DOWN, Action{[this](){ if (m_index < (m_options.size() - 1)) { SetIndex(m_index + 1); + App::PlaySoundEffect(SoundEffect_Focus); } }}), std::make_pair(Button::DPAD_UP | Button::RS_UP, Action{[this](){ if (m_index != 0) { SetIndex(m_index - 1); + App::PlaySoundEffect(SoundEffect_Focus); } }}), std::make_pair(Button::X, Action{"Options"_i18n, [this](){ diff --git a/sphaira/source/ui/menus/filebrowser.cpp b/sphaira/source/ui/menus/filebrowser.cpp index 2b536ac..0ca0031 100644 --- a/sphaira/source/ui/menus/filebrowser.cpp +++ b/sphaira/source/ui/menus/filebrowser.cpp @@ -1,4 +1,5 @@ #include "ui/menus/filebrowser.hpp" +#include "ui/menus/homebrew.hpp" #include "ui/sidebar.hpp" #include "ui/option_box.hpp" #include "ui/popup_list.hpp" @@ -303,8 +304,6 @@ auto get_collection(fs::FsNative& fs, const fs::FsPath& path, const fs::FsPath& R_SUCCEED(); } -#if 1 -// recursion auto get_collections(fs::FsNative& fs, const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollections& out) -> Result { // get a list of all the files / dirs FsDirCollection collection; @@ -323,48 +322,6 @@ auto get_collections(fs::FsNative& fs, const fs::FsPath& path, const fs::FsPath& R_SUCCEED(); } -#else -// normal -auto get_collections(fs::FsNative& fs, fs::FsPath path, fs::FsPath parent_name, FsDirCollections& out) -> Result { - // get a list of all the files / dirs - struct StoredStack { - fs::FsPath path; - fs::FsPath parent_name; - s64 index; - }; - - std::stack stack; - s64 index{}; - // std::vector indexes; - - while (true) { - FsDirCollection collection; - R_TRY(get_collection(fs, path, parent_name, collection, true, true, false)); - out.emplace_back(collection); - - if (collection.dirs.size()) { - stack.emplace(path, parent_name, index); - index = 0; - } - } - - FsDirCollection collection; - R_TRY(get_collection(fs, path, parent_name, collection, true, true, false)); - log_write("got collection: %s parent_name: %s files: %zu dirs: %zu\n", path, parent_name, collection.files.size(), collection.dirs.size()); - out.emplace_back(collection); - - // for (size_t i = 0; i < collection.dirs.size(); i++) { - for (const auto&p : collection.dirs) { - // use heap as to not explode the stack - const auto new_path = std::make_unique(Menu::GetNewPath(path, p.name)); - const auto new_parent_name = std::make_unique(Menu::GetNewPath(parent_name, p.name)); - log_write("trying to get nested collection: %s parent_name: %s\n", *new_path, *new_parent_name); - R_TRY(get_collections(fs, *new_path, *new_parent_name, out)); - } - - R_SUCCEED(); -} -#endif auto get_collections(const fs::FsPath& path, const fs::FsPath& parent_name, FsDirCollections& out) -> Result { fs::FsNativeSd fs; @@ -392,6 +349,7 @@ Menu::Menu(const std::vector& nro_entries) : MenuBase{"FileBrowser"_i1 std::make_pair(Button::DOWN, Action{[this](){ if (m_index < (m_entries_current.size() - 1)) { SetIndex(m_index + 1); + App::PlaySoundEffect(SoundEffect_Scroll); if (m_index - m_index_offset >= 8) { log_write("moved down\n"); m_index_offset++; @@ -401,6 +359,7 @@ Menu::Menu(const std::vector& nro_entries) : MenuBase{"FileBrowser"_i1 std::make_pair(Button::UP, Action{[this](){ if (m_index != 0) { SetIndex(m_index - 1); + App::PlaySoundEffect(SoundEffect_Scroll); if (m_index < m_index_offset ) { log_write("moved up\n"); m_index_offset--; @@ -571,12 +530,13 @@ Menu::Menu(const std::vector& nro_entries) : MenuBase{"FileBrowser"_i1 } // can't rename more than 1 file - if (m_entries_current.size() && m_selected_count < 2) { + if (m_entries_current.size() && !m_selected_count) { options->Add(std::make_shared("Rename"_i18n, [this](){ std::string out; - if (R_SUCCEEDED(swkbd::ShowText(out, "Set New File Name", -1, FS_MAX_PATH)) && !out.empty()) { - const auto& entry = GetEntry(); - const auto src_path = GetNewPathCurrent(); + const auto& entry = GetEntry(); + const auto name = entry.GetName(); + if (R_SUCCEEDED(swkbd::ShowText(out, "Set New File Name", name.c_str())) && !out.empty() && out != name) { + const auto src_path = GetNewPath(entry); const auto dst_path = GetNewPath(m_path, out); Result rc; @@ -603,7 +563,7 @@ Menu::Menu(const std::vector& nro_entries) : MenuBase{"FileBrowser"_i1 options->Add(std::make_shared("Create File"_i18n, [this](){ std::string out; - if (R_SUCCEEDED(swkbd::ShowText(out, "Set File Name", -1, FS_MAX_PATH)) && !out.empty()) { + if (R_SUCCEEDED(swkbd::ShowText(out, "Set File Name")) && !out.empty()) { fs::FsPath full_path; if (out[0] == '/') { full_path = out; @@ -624,7 +584,7 @@ Menu::Menu(const std::vector& nro_entries) : MenuBase{"FileBrowser"_i1 options->Add(std::make_shared("Create Folder"_i18n, [this](){ std::string out; - if (R_SUCCEEDED(swkbd::ShowText(out, "Set Folder Name", -1, FS_MAX_PATH)) && !out.empty()) { + if (R_SUCCEEDED(swkbd::ShowText(out, "Set Folder Name")) && !out.empty()) { fs::FsPath full_path; if (out[0] == '/') { full_path = out; @@ -648,18 +608,22 @@ Menu::Menu(const std::vector& nro_entries) : MenuBase{"FileBrowser"_i1 } if (m_entries_current.size()) { - if (HasTypeInSelectedEntries(FsDirEntryType_File) && m_selected_count < 2 && !FindFileAssocFor().empty()) { + if (HasTypeInSelectedEntries(FsDirEntryType_File) && !m_selected_count && (GetEntry().GetExtension() == "nro" || !FindFileAssocFor().empty())) { options->Add(std::make_shared("Install Forwarder"_i18n, [this](){; - #if 1 App::Push(std::make_shared( "WARNING: Installing forwarders will lead to a ban!"_i18n, "Back"_i18n, "Install"_i18n, 0, [this](auto op_index){ if (op_index && *op_index) { - InstallForwarder(); + if (GetEntry().GetExtension() == "nro") { + if (R_FAILED(homebrew::Menu::InstallHomebrewFromPath(GetNewPathCurrent()))) { + log_write("failed to create forwarder\n"); + } + } else { + InstallForwarder(); + } } } )); - #endif })); } @@ -910,25 +874,6 @@ void Menu::InstallForwarder() { title, items, [this, assoc_list](auto op_index){ if (op_index) { const auto assoc = assoc_list[*op_index]; - #if 1 - #if 0 - NroEntry nro{}; - log_write("parsing nro\n"); - if (R_FAILED(nro_parse(assoc.path, nro))) { - log_write("failed nro parse\n"); - return; - } - - OwoConfig config{}; - config.nro_path = nro.path.toString(); - // config.args = nro_add_arg_file(GetNewPathCurrent()); - config.name = nro.nacp.lang[0].name;// + std::string{" | "} + file_name; - // config.name = file_name; - config.nacp = nro.nacp; - // config.icon = GetRomIcon(pbox, file_name, extension, database, nro); - config.icon = nro.icon;// GetRomIcon(pbox, file_name, extension, database, nro); - App::Install(config); - #else log_write("pushing it\n"); App::Push(std::make_shared("Installing Forwarder", [assoc, this](auto pbox) -> bool { log_write("inside callback\n"); @@ -961,36 +906,6 @@ void Menu::InstallForwarder() { return R_SUCCEEDED(App::Install(pbox, config)); })); - #endif - #else - - const auto& assoc = assoc_list[*op_index]; - log_write("doing nro parse\n"); - - NroEntry nro{}; - if (R_SUCCEEDED(nro_parse(assoc.path.c_str(), nro))) { - log_write("got nro data\n"); - std::string file_name = GetEntry().GetInternalName(); - std::string extension = GetEntry().GetInternalExtension(); - - if (auto pos = file_name.find_last_of('.'); pos != std::string::npos) { - log_write("got filename\n"); - file_name = file_name.substr(0, pos); - log_write("got filename2: %s\n\n", file_name.c_str()); - } - - const auto database = GetRomDatabaseFromPath(m_path); - - OwoConfig config{}; - config.nro_path = assoc.path; - config.args = nro_add_arg_file(GetNewPathCurrent()); - config.name = nro.nacp.lang[0].name + std::string{" | "} + file_name; - // config.name = file_name; - config.nacp = nro.nacp; - config.icon = GetRomIcon(file_name, extension, database, nro); - App::Install(config); - } - #endif } else { log_write("pressed B to skip launch...\n"); } diff --git a/sphaira/source/ui/menus/homebrew.cpp b/sphaira/source/ui/menus/homebrew.cpp index 2125f5e..80eb26a 100644 --- a/sphaira/source/ui/menus/homebrew.cpp +++ b/sphaira/source/ui/menus/homebrew.cpp @@ -72,11 +72,10 @@ Menu::Menu() : MenuBase{"Homebrew"_i18n} { if (m_index < (m_entries.size() - 1)) { if (m_index < (m_entries.size() - 3)) { SetIndex(m_index + 3); - App::PlaySoundEffect(SoundEffect_Scroll); } else { SetIndex(m_entries.size() - 1); - App::PlaySoundEffect(SoundEffect_Scroll); } + App::PlaySoundEffect(SoundEffect_Scroll); if (m_index - m_start >= 9) { log_write("moved down\n"); m_start += 3; @@ -263,11 +262,7 @@ void Menu::SetIndex(std::size_t index) { void Menu::InstallHomebrew() { const auto& nro = m_entries[m_index]; - OwoConfig config{}; - config.nro_path = nro.path.toString(); - config.nacp = nro.nacp; - config.icon = nro.icon; - App::Install(config); + InstallHomebrew(nro.path, nro.nacp, nro.icon); } void Menu::ScanHomebrew() { @@ -275,22 +270,11 @@ void Menu::ScanHomebrew() { nro_scan("/switch", m_entries, m_hide_sphaira.Get()); log_write("nros found: %zu time_taken: %.2f\n", m_entries.size(), ts.GetSeconds()); - // todo: optimise this. maybe create a file per entry - // which would speed up parsing - for (auto& e : m_entries) { - if (ini_hassection(e.path, App::PLAYLOG_PATH)) { - // log_write("has section for: %s\n", e.path); - e.hbini.timestamp = ini_getl(e.path, "timestamp", 0, App::PLAYLOG_PATH); - } - e.image = 0; // images are lazy loaded - } - - #if 0 struct IniUser { std::vector& entires; Hbini* ini; std::string last_section; - } ini_user { m_entries }; + } ini_user{ m_entries }; ini_browse([](const mTCHAR *Section, const mTCHAR *Key, const mTCHAR *Value, void *UserData) -> int { auto user = static_cast(UserData); @@ -308,14 +292,16 @@ void Menu::ScanHomebrew() { } if (user->ini) { - + if (!strcmp(Key, "timestamp")) { + user->ini->timestamp = atoi(Value); + } else if (!strcmp(Key, "launch_count")) { + user->ini->launch_count = atoi(Value); + } } - // app-> - log_write("found: %s %s %s\n", Section, Key, Value); + // log_write("found: %s %s %s\n", Section, Key, Value); return 1; }, &ini_user, App::PLAYLOG_PATH); - #endif this->Sort(); SetIndex(0); @@ -374,4 +360,19 @@ void Menu::SortAndFindLastFile() { Sort(); } +Result Menu::InstallHomebrew(const fs::FsPath& path, const NacpStruct& nacp, const std::vector& icon) { + OwoConfig config{}; + config.nro_path = path.toString(); + config.nacp = nacp; + config.icon = icon; + return App::Install(config); +} + +Result Menu::InstallHomebrewFromPath(const fs::FsPath& path) { + NacpStruct nacp; + R_TRY(nro_get_nacp(path, nacp)) + const auto icon = nro_get_icon(path); + return InstallHomebrew(path, nacp, icon); +} + } // namespace sphaira::ui::menu::homebrew diff --git a/sphaira/source/ui/menus/themezer.cpp b/sphaira/source/ui/menus/themezer.cpp index e09db9b..4757b22 100644 --- a/sphaira/source/ui/menus/themezer.cpp +++ b/sphaira/source/ui/menus/themezer.cpp @@ -508,7 +508,7 @@ Menu::Menu() : MenuBase{"Themezer"_i18n} { options->Add(std::make_shared("Page"_i18n, [this](){ s64 out; - if (R_SUCCEEDED(swkbd::ShowNumPad(out, "Enter Page Number", -1, 3))) { + if (R_SUCCEEDED(swkbd::ShowNumPad(out, "Enter Page Number", nullptr, -1, 3))) { if (out < m_page_index_max) { m_page_index = out; PackListDownload(); diff --git a/sphaira/source/ui/nvg_util.cpp b/sphaira/source/ui/nvg_util.cpp index 0f25a09..54f1046 100644 --- a/sphaira/source/ui/nvg_util.cpp +++ b/sphaira/source/ui/nvg_util.cpp @@ -457,7 +457,7 @@ void drawButton(NVGcontext* vg, float x, float y, float size, Button button) { drawText(vg, x, y, size, getButton(button), nullptr, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, getColour(Colour::WHITE)); } -void drawButtons(NVGcontext* vg, const Widget::Actions& actions, const NVGcolor& c, float start_x) { +void drawButtons(NVGcontext* vg, const Widget::Actions& _actions, const NVGcolor& c, float start_x) { nvgBeginPath(vg); nvgFontSize(vg, 24.f); nvgTextAlign(vg, NVG_ALIGN_RIGHT | NVG_ALIGN_TOP); @@ -467,6 +467,22 @@ void drawButtons(NVGcontext* vg, const Widget::Actions& actions, const NVGcolor& const float y = 675.f; float bounds[4]{}; + // swaps L/R position, idc how shit this is, it's called once per frame. + std::vector> actions; + actions.reserve(_actions.size()); + + for (const auto a: _actions) { + // swap + if (a.first == Button::R && actions.size() && actions.back().first == Button::L) { + const auto s = actions.back(); + actions.back() = a; + actions.emplace_back(s); + + } else { + actions.emplace_back(a); + } + } + for (const auto& [button, action] : actions) { if (action.IsHidden() || action.m_hint.empty()) { continue; diff --git a/sphaira/source/ui/option_box.cpp b/sphaira/source/ui/option_box.cpp index 6aebd99..9e64fb4 100644 --- a/sphaira/source/ui/option_box.cpp +++ b/sphaira/source/ui/option_box.cpp @@ -123,6 +123,7 @@ auto OptionBox::Setup(std::size_t index) -> void { m_entries[m_index].Selected(false); m_index--; m_entries[m_index].Selected(true); + App::PlaySoundEffect(SoundEffect_Focus); } }}), std::make_pair(Button::RIGHT, Action{[this](){ @@ -130,6 +131,7 @@ auto OptionBox::Setup(std::size_t index) -> void { m_entries[m_index].Selected(false); m_index++; m_entries[m_index].Selected(true); + App::PlaySoundEffect(SoundEffect_Focus); } }}), std::make_pair(Button::A, Action{[this](){ diff --git a/sphaira/source/ui/popup_list.cpp b/sphaira/source/ui/popup_list.cpp index 38e8cd1..d3d6456 100644 --- a/sphaira/source/ui/popup_list.cpp +++ b/sphaira/source/ui/popup_list.cpp @@ -90,6 +90,8 @@ auto PopupList::Update(Controller* controller, TouchInfo* touch) -> void { return; } + const auto old_index = m_index; + if (controller->GotDown(Button::DOWN) && m_index < (m_items.size() - 1)) { m_index++; m_selected_y += m_block.h; @@ -98,7 +100,10 @@ auto PopupList::Update(Controller* controller, TouchInfo* touch) -> void { m_selected_y -= m_block.h; } - OnLayoutChange(); + if (old_index != m_index) { + App::PlaySoundEffect(SoundEffect_Scroll); + OnLayoutChange(); + } } auto PopupList::OnLayoutChange() -> void { diff --git a/sphaira/source/ui/scrollable_text.cpp b/sphaira/source/ui/scrollable_text.cpp index c0f157c..67c55cc 100644 --- a/sphaira/source/ui/scrollable_text.cpp +++ b/sphaira/source/ui/scrollable_text.cpp @@ -27,6 +27,7 @@ ScrollableText::ScrollableText(const std::string& text, float x, float y, float } m_y_off -= m_step; m_index++; + App::PlaySoundEffect(SoundEffect_Scroll); }}), std::make_pair(Button::LS_UP, Action{[this](){ if (m_y_off == m_y_off_base) { @@ -34,6 +35,7 @@ ScrollableText::ScrollableText(const std::string& text, float x, float y, float } m_y_off += m_step; m_index--; + App::PlaySoundEffect(SoundEffect_Scroll); }}) ); diff --git a/sphaira/source/ui/sidebar.cpp b/sphaira/source/ui/sidebar.cpp index 5a8db83..7fce7d4 100644 --- a/sphaira/source/ui/sidebar.cpp +++ b/sphaira/source/ui/sidebar.cpp @@ -234,6 +234,7 @@ auto Sidebar::Update(Controller* controller, TouchInfo* touch) -> void { // if we moved if (m_index != old_index) { + App::PlaySoundEffect(SoundEffect_Scroll); m_items[old_index]->OnFocusLost(); m_items[m_index]->OnFocusGained(); diff --git a/sphaira/source/ui/widget.cpp b/sphaira/source/ui/widget.cpp index fca569f..208d212 100644 --- a/sphaira/source/ui/widget.cpp +++ b/sphaira/source/ui/widget.cpp @@ -7,8 +7,10 @@ namespace sphaira::ui { void Widget::Update(Controller* controller, TouchInfo* touch) { for (const auto& [button, action] : m_actions) { if ((action.m_type & ActionType::DOWN) && controller->GotDown(button)) { + if (static_cast(button) & static_cast(Button::ANY_BUTTON)) { + App::PlaySoundEffect(SoundEffect_Focus); + } action.Invoke(true); - App::PlaySoundEffect(SoundEffect_Focus); } else if ((action.m_type & ActionType::UP) && controller->GotUp(button)) { action.Invoke(false);