From 65a7816f2e135e17789dc777aa92727a636ea1f0 Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Sun, 20 Jun 2021 22:43:40 -0400 Subject: [PATCH] Implemented UserTitlesTab class. * LayeredErrorFrame: both the error frame and the list are now protected instead of private, letting any expanded classes take more control over the them. SetErrorFrameMessage(), AddListView() and the class destructor have been removed because of this change. * LayeredErrorFrame: removed views vector and simplified the list element focus check carried out before switching to the error frame. --- include/gamecard_tab.hpp | 2 +- include/layered_error_frame.hpp | 16 ++--- include/user_titles_tab.hpp | 47 ++++++++++++ romfs/i18n/en-US/user_titles_tab.json | 5 ++ source/gamecard_tab.cpp | 35 ++++----- source/layered_error_frame.cpp | 45 ++++-------- source/root_view.cpp | 4 +- source/user_titles_tab.cpp | 100 ++++++++++++++++++++++++++ 8 files changed, 189 insertions(+), 65 deletions(-) create mode 100644 include/user_titles_tab.hpp create mode 100644 romfs/i18n/en-US/user_titles_tab.json create mode 100644 source/user_titles_tab.cpp diff --git a/include/gamecard_tab.hpp b/include/gamecard_tab.hpp index 8602ab1..e09ff86 100644 --- a/include/gamecard_tab.hpp +++ b/include/gamecard_tab.hpp @@ -32,7 +32,7 @@ namespace nxdt::views { class GameCardTab: public LayeredErrorFrame { - typedef bool (*GameCardSizeFunc)(u64 *size); + typedef bool (*GameCardSizeFunc)(u64 *out_size); private: nxdt::tasks::GameCardTask *gc_status_task = nullptr; diff --git a/include/layered_error_frame.hpp b/include/layered_error_frame.hpp index bc480dc..f8f1f1a 100644 --- a/include/layered_error_frame.hpp +++ b/include/layered_error_frame.hpp @@ -31,22 +31,14 @@ namespace nxdt::views /* Extended class to switch between ErrorFrame and List views on demand. */ class LayeredErrorFrame: public brls::LayerView { - private: - int layer_view_index = 0; - - ErrorFrame *error_frame = nullptr; - - brls::List *list = nullptr; - std::vector list_views; - protected: + ErrorFrame *error_frame = nullptr; + brls::List *list = nullptr; + void SwitchLayerView(bool use_error_frame); - void SetErrorFrameMessage(std::string msg = ""); - void AddListView(brls::View* view); public: - LayeredErrorFrame(void); - ~LayeredErrorFrame(void); + LayeredErrorFrame(std::string msg = ""); }; } diff --git a/include/user_titles_tab.hpp b/include/user_titles_tab.hpp new file mode 100644 index 0000000..00b8c28 --- /dev/null +++ b/include/user_titles_tab.hpp @@ -0,0 +1,47 @@ +/* + * user_titles_tab.hpp + * + * Copyright (c) 2020-2021, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * nxdumptool is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#ifndef __USER_TITLES_TAB_HPP__ +#define __USER_TITLES_TAB_HPP__ + +#include "tasks.hpp" +#include "layered_error_frame.hpp" + +namespace nxdt::views +{ + class UserTitlesTab: public LayeredErrorFrame + { + private: + nxdt::tasks::TitleTask *title_task = nullptr; + nxdt::tasks::VoidEvent::Subscription title_task_sub; + nxdt::tasks::TitleApplicationMetadataVector *user_app_metadata = nullptr; + + void PopulateList(void); + + public: + UserTitlesTab(nxdt::tasks::TitleTask *title_task); + ~UserTitlesTab(void); + }; +} + +#endif /* __USER_TITLES_TAB_HPP__ */ diff --git a/romfs/i18n/en-US/user_titles_tab.json b/romfs/i18n/en-US/user_titles_tab.json new file mode 100644 index 0000000..70bfe9d --- /dev/null +++ b/romfs/i18n/en-US/user_titles_tab.json @@ -0,0 +1,5 @@ +{ + "error_frame": { + "no_titles_available": "No user titles available." + } +} diff --git a/source/gamecard_tab.cpp b/source/gamecard_tab.cpp index ebdd03e..bb6f2f4 100644 --- a/source/gamecard_tab.cpp +++ b/source/gamecard_tab.cpp @@ -41,13 +41,14 @@ namespace nxdt::views [GameCardCompatibilityType_Terra] = "Terra" }; - GameCardTab::GameCardTab(nxdt::tasks::GameCardTask *gc_status_task) : LayeredErrorFrame(), gc_status_task(gc_status_task) + GameCardTab::GameCardTab(nxdt::tasks::GameCardTask *gc_status_task) : LayeredErrorFrame("gamecard_tab/error_frame/not_inserted"_i18n), gc_status_task(gc_status_task) { - /* Error frame. */ - this->SetErrorFrameMessage("gamecard_tab/error_frame/not_inserted"_i18n); + /* Set custom spacing. */ + this->list->setSpacing(this->list->getSpacing() / 2); + this->list->setMarginBottom(20); /* Gamecard properties table. */ - this->AddListView(new brls::Header("gamecard_tab/list/properties_table/header"_i18n)); + this->list->addView(new brls::Header("gamecard_tab/list/properties_table/header"_i18n)); this->properties_table = new FocusableTable(false); this->capacity = this->properties_table->addRow(brls::TableRowType::BODY, "gamecard_tab/list/properties_table/capacity"_i18n); @@ -57,28 +58,28 @@ namespace nxdt::views this->lafw_version = this->properties_table->addRow(brls::TableRowType::BODY, "gamecard_tab/list/properties_table/lafw_version"_i18n); this->sdk_version = this->properties_table->addRow(brls::TableRowType::BODY, "gamecard_tab/list/properties_table/sdk_version"_i18n); this->compatibility_type = this->properties_table->addRow(brls::TableRowType::BODY, "gamecard_tab/list/properties_table/compatibility_type"_i18n); - this->AddListView(this->properties_table); + this->list->addView(this->properties_table); /* ListItem elements. */ - this->AddListView(new brls::Header("gamecard_tab/list/dump_options"_i18n)); + this->list->addView(new brls::Header("gamecard_tab/list/dump_options"_i18n)); this->dump_card_image = new brls::ListItem("gamecard_tab/list/dump_card_image/label"_i18n, "gamecard_tab/list/dump_card_image/description"_i18n); - this->AddListView(this->dump_card_image); + this->list->addView(this->dump_card_image); this->dump_certificate = new brls::ListItem("gamecard_tab/list/dump_certificate/label"_i18n, "gamecard_tab/list/dump_certificate/description"_i18n); - this->AddListView(this->dump_certificate); + this->list->addView(this->dump_certificate); this->dump_header = new brls::ListItem("gamecard_tab/list/dump_header/label"_i18n, "gamecard_tab/list/dump_header/description"_i18n); - this->AddListView(this->dump_header); + this->list->addView(this->dump_header); this->dump_decrypted_cardinfo = new brls::ListItem("gamecard_tab/list/dump_decrypted_cardinfo/label"_i18n, "gamecard_tab/list/dump_decrypted_cardinfo/description"_i18n); - this->AddListView(this->dump_decrypted_cardinfo); + this->list->addView(this->dump_decrypted_cardinfo); this->dump_initial_data = new brls::ListItem("gamecard_tab/list/dump_initial_data/label"_i18n, "gamecard_tab/list/dump_initial_data/description"_i18n); - this->AddListView(this->dump_initial_data); + this->list->addView(this->dump_initial_data); this->dump_hfs_partitions = new brls::ListItem("gamecard_tab/list/dump_hfs_partitions/label"_i18n, "gamecard_tab/list/dump_hfs_partitions/description"_i18n); - this->AddListView(this->dump_hfs_partitions); + this->list->addView(this->dump_hfs_partitions); /* Subscribe to gamecard status event. */ this->gc_status_task_sub = this->gc_status_task->RegisterListener([this](GameCardStatus gc_status) { @@ -87,19 +88,19 @@ namespace nxdt::views switch(gc_status) { case GameCardStatus_NotInserted: - this->SetErrorFrameMessage("gamecard_tab/error_frame/not_inserted"_i18n); + this->error_frame->SetMessage("gamecard_tab/error_frame/not_inserted"_i18n); break; case GameCardStatus_Processing: - this->SetErrorFrameMessage("gamecard_tab/error_frame/processing"_i18n); + this->error_frame->SetMessage("gamecard_tab/error_frame/processing"_i18n); break; case GameCardStatus_NoGameCardPatchEnabled: - this->SetErrorFrameMessage("gamecard_tab/error_frame/nogc_enabled"_i18n); + this->error_frame->SetMessage("gamecard_tab/error_frame/nogc_enabled"_i18n); break; case GameCardStatus_LotusAsicFirmwareUpdateRequired: - this->SetErrorFrameMessage("gamecard_tab/error_frame/lafw_update_required"_i18n); + this->error_frame->SetMessage("gamecard_tab/error_frame/lafw_update_required"_i18n); break; case GameCardStatus_InsertedAndInfoNotLoaded: - this->SetErrorFrameMessage(i18n::getStr("gamecard_tab/error_frame/info_not_loaded"_i18n, GITHUB_NEW_ISSUE_URL)); + this->error_frame->SetMessage(i18n::getStr("gamecard_tab/error_frame/info_not_loaded"_i18n, GITHUB_NEW_ISSUE_URL)); break; case GameCardStatus_InsertedAndInfoLoaded: { diff --git a/source/layered_error_frame.cpp b/source/layered_error_frame.cpp index 018f641..18672aa 100644 --- a/source/layered_error_frame.cpp +++ b/source/layered_error_frame.cpp @@ -23,56 +23,35 @@ namespace nxdt::views { - LayeredErrorFrame::LayeredErrorFrame(void) : brls::LayerView() + LayeredErrorFrame::LayeredErrorFrame(std::string msg) : brls::LayerView() { /* Error frame. */ - this->error_frame = new ErrorFrame(); + this->error_frame = new ErrorFrame(msg); this->addLayer(this->error_frame); /* List. */ this->list = new brls::List(); - this->list->setSpacing(this->list->getSpacing() / 2); - this->list->setMarginBottom(20); this->addLayer(this->list); } - LayeredErrorFrame::~LayeredErrorFrame(void) - { - /* Clear list views vector. */ - if (this->list_views.size()) this->list_views.clear(); - } - void LayeredErrorFrame::SwitchLayerView(bool use_error_frame) { - if ((use_error_frame && this->layer_view_index == 0) || (!use_error_frame && this->layer_view_index == 1)) return; + int index = this->getLayerIndex(); + int new_index = (index ^ 1); + brls::View *cur_focus = brls::Application::getCurrentFocus(); - int index = (this->layer_view_index ^ 1); - brls::View *current_focus = brls::Application::getCurrentFocus(); + /* Don't proceed if we're already at the desired view layer. */ + if (index < 0 || index > 1 || (use_error_frame && index == 0) || (!use_error_frame && index == 1)) return; - /* Focus the sidebar if we're currently focusing an element from our List. */ - for(brls::View* list_view : this->list_views) + /* Focus the sidebar if we're currently focusing an element from our List and we're about to switch to the error frame. */ + if (use_error_frame && cur_focus && cur_focus->hasParent()) { - if (current_focus == list_view) - { - brls::Application::onGamepadButtonPressed(GLFW_GAMEPAD_BUTTON_DPAD_LEFT, false); - break; - } + brls::View *cur_focus_parent = cur_focus->getParent(); + if (cur_focus_parent && cur_focus_parent->hasParent() && cur_focus_parent->getParent() == this->list) brls::Application::onGamepadButtonPressed(GLFW_GAMEPAD_BUTTON_DPAD_LEFT, false); } /* Change layer view. */ - this->changeLayer(index); + this->changeLayer(new_index); this->invalidate(true); - this->layer_view_index = index; - } - - void LayeredErrorFrame::SetErrorFrameMessage(std::string msg) - { - this->error_frame->SetMessage(msg); - } - - void LayeredErrorFrame::AddListView(brls::View* view) - { - this->list->addView(view); - this->list_views.push_back(view); } } diff --git a/source/root_view.cpp b/source/root_view.cpp index 4396fbf..11d164b 100644 --- a/source/root_view.cpp +++ b/source/root_view.cpp @@ -22,7 +22,7 @@ #include #include #include -//#include +#include //#include //#include #include @@ -73,7 +73,7 @@ namespace nxdt::views /* Add tabs. */ this->addTab("root_view/tabs/gamecard"_i18n, new GameCardTab(this->gc_status_task)); this->addSeparator(); - this->addTab("root_view/tabs/user_titles"_i18n, new brls::Rectangle(nvgRGB(0, 255, 0))); + this->addTab("root_view/tabs/user_titles"_i18n, new UserTitlesTab(this->title_task)); this->addTab("root_view/tabs/system_titles"_i18n, new brls::Rectangle(nvgRGB(0, 0, 255))); this->addSeparator(); this->addTab("root_view/tabs/options"_i18n, new brls::Rectangle(nvgRGB(255, 255, 0))); diff --git a/source/user_titles_tab.cpp b/source/user_titles_tab.cpp new file mode 100644 index 0000000..31dfe7d --- /dev/null +++ b/source/user_titles_tab.cpp @@ -0,0 +1,100 @@ +/* + * user_titles_tab.cpp + * + * Copyright (c) 2020-2021, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * nxdumptool is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include + +namespace i18n = brls::i18n; /* For getStr(). */ +using namespace i18n::literals; /* For _i18n. */ + +namespace nxdt::views +{ + UserTitlesTab::UserTitlesTab(nxdt::tasks::TitleTask *title_task) : LayeredErrorFrame("user_titles_tab/error_frame/no_titles_available"_i18n), title_task(title_task) + { + /* Populate list. */ + this->PopulateList(); + + /* Subscribe to title event. */ + this->title_task_sub = this->title_task->RegisterListener([this](void) { + /* Update list. */ + this->PopulateList(); + }); + } + + UserTitlesTab::~UserTitlesTab(void) + { + /* Unregister task listener. */ + this->title_task->UnregisterListener(this->title_task_sub); + } + + void UserTitlesTab::PopulateList(void) + { + bool refocus = false; + + this->user_app_metadata = this->title_task->GetApplicationMetadata(false); + size_t user_app_metadata_count = this->user_app_metadata->size(); + + if (user_app_metadata_count) + { + /* Determine if we need to refocus after updating the list. */ + brls::View *cur_view = brls::Application::getCurrentFocus(); + while(cur_view) + { + if (cur_view == this->list) + { + refocus = true; + break; + } + + cur_view = cur_view->getParent(); + } + } else { + /* If we need to, switch to the error frame *before* cleaning up our list. */ + this->SwitchLayerView(true); + } + + /* Clear list. */ + this->list->clear(); + this->list->invalidate(true); + + /* Immediately return if we have no user application metadata. */ + if (!user_app_metadata_count) return; + + /* Populate list. */ + for(TitleApplicationMetadata *cur_app_metadata : *(this->user_app_metadata)) + { + brls::ListItem *list_item = new brls::ListItem(std::string(cur_app_metadata->lang_entry.name), "", std::string(cur_app_metadata->lang_entry.author)); + list_item->setThumbnail(cur_app_metadata->icon, cur_app_metadata->icon_size); + this->list->addView(list_item); + } + + /* Switch to the list. */ + this->list->invalidate(true); + this->SwitchLayerView(false); + + /* Refocus, if needed. */ + if (refocus) + { + brls::Application::giveFocus(this->list->getChild(0)); + this->list->willAppear(true); + } + } +}