add scrolling text to popup list, handling clipping inside scrolling text, game menu changes
- added delete entity in game menu - added list meta records to game menu
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "ui/widget.hpp"
|
||||
#include "ui/scrolling_text.hpp"
|
||||
#include "ui/list.hpp"
|
||||
#include <optional>
|
||||
|
||||
@@ -39,6 +40,7 @@ private:
|
||||
s64 m_starting_index{};
|
||||
|
||||
std::unique_ptr<List> m_list{};
|
||||
ScrollingText m_scroll_text{};
|
||||
|
||||
float m_yoff{};
|
||||
float m_line_top{};
|
||||
|
||||
@@ -31,6 +31,9 @@ union ExtendedHeader {
|
||||
NcmDataPatchMetaExtendedHeader data_patch;
|
||||
};
|
||||
|
||||
auto GetMetaTypeStr(u8 meta_type) -> const char*;
|
||||
auto GetStorageIdStr(u8 storage_id) -> const char*;
|
||||
|
||||
auto GetAppId(u8 meta_type, u64 id) -> u64;
|
||||
auto GetAppId(const NcmContentMetaKey& key) -> u64;
|
||||
auto GetAppId(const PackagedContentMeta& meta) -> u64;
|
||||
|
||||
@@ -37,7 +37,7 @@ auto ErrorBox::Draw(NVGcontext* vg, Theme* theme) -> void {
|
||||
gfx::drawTextArgs(vg, center_x, 180, 63, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, theme->GetColour(ThemeEntryID_ERROR), "\uE140");
|
||||
if (m_code.has_value()) {
|
||||
const auto code = m_code.value();
|
||||
gfx::drawTextArgs(vg, center_x, 270, 25, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, theme->GetColour(ThemeEntryID_TEXT), "Error code: 0x%X Module: %u Description: 0x%X Value: 0x%X", code, R_MODULE(code), R_DESCRIPTION(code), R_VALUE(code));
|
||||
gfx::drawTextArgs(vg, center_x, 270, 25, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, theme->GetColour(ThemeEntryID_TEXT), "Code: 0x%X Module: %u Description: 0x%X Value: 0x%X", code, R_MODULE(code), R_DESCRIPTION(code), R_VALUE(code));
|
||||
} else {
|
||||
gfx::drawTextArgs(vg, center_x, 270, 25, NVG_ALIGN_CENTER | NVG_ALIGN_TOP, theme->GetColour(ThemeEntryID_TEXT), "An error occurred"_i18n.c_str());
|
||||
}
|
||||
|
||||
@@ -1044,15 +1044,10 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
const auto text_off = 148;
|
||||
const auto text_x = x + text_off;
|
||||
const auto text_clip_w = w - 30.f - text_off;
|
||||
nvgSave(vg);
|
||||
nvgIntersectScissor(vg, text_x, y, text_clip_w, h); // clip
|
||||
{
|
||||
const float font_size = 18;
|
||||
m_scroll_name.Draw(vg, selected, text_x, y + 45, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.title.c_str());
|
||||
m_scroll_author.Draw(vg, selected, text_x, y + 80, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.author.c_str());
|
||||
m_scroll_version.Draw(vg, selected, text_x, y + 115, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.version.c_str());
|
||||
}
|
||||
nvgRestore(vg);
|
||||
const float font_size = 18;
|
||||
m_scroll_name.Draw(vg, selected, text_x, y + 45, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.title.c_str());
|
||||
m_scroll_author.Draw(vg, selected, text_x, y + 80, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.author.c_str());
|
||||
m_scroll_version.Draw(vg, selected, text_x, y + 115, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.version.c_str());
|
||||
|
||||
float i_size = 22;
|
||||
switch (e.status) {
|
||||
|
||||
@@ -6,9 +6,11 @@
|
||||
#include "ui/error_box.hpp"
|
||||
#include "ui/option_box.hpp"
|
||||
#include "ui/progress_box.hpp"
|
||||
#include "ui/popup_list.hpp"
|
||||
#include "ui/nvg_util.hpp"
|
||||
#include "defines.hpp"
|
||||
#include "i18n.hpp"
|
||||
#include "yati/nx/ncm.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <cstring>
|
||||
@@ -16,6 +18,8 @@
|
||||
namespace sphaira::ui::menu::game {
|
||||
namespace {
|
||||
|
||||
using MetaEntries = std::vector<NsApplicationContentMetaStatus>;
|
||||
|
||||
Result Notify(Result rc, const std::string& error_message) {
|
||||
if (R_FAILED(rc)) {
|
||||
App::Push(std::make_shared<ui::ErrorBox>(rc,
|
||||
@@ -28,6 +32,17 @@ Result Notify(Result rc, const std::string& error_message) {
|
||||
return rc;
|
||||
}
|
||||
|
||||
Result GetMetaEntries(const Entry& e, MetaEntries& out) {
|
||||
s32 count;
|
||||
R_TRY(nsCountApplicationContentMeta(e.app_id, &count));
|
||||
|
||||
out.resize(count);
|
||||
R_TRY(nsListApplicationContentMetaStatus(e.app_id, 0, out.data(), out.size(), &count));
|
||||
|
||||
out.resize(count);
|
||||
R_SUCCEED();
|
||||
}
|
||||
|
||||
// also sets the status to error.
|
||||
void FakeNacpEntry(Entry& e) {
|
||||
e.status = NacpLoadStatus::Error;
|
||||
@@ -89,10 +104,13 @@ Menu::Menu() : MenuBase{"Games"_i18n} {
|
||||
SetPop();
|
||||
}}),
|
||||
std::make_pair(Button::A, Action{"Launch"_i18n, [this](){
|
||||
if (m_entries.empty()) {
|
||||
return;
|
||||
}
|
||||
LaunchEntry(m_entries[m_index]);
|
||||
}}),
|
||||
std::make_pair(Button::X, Action{"Options"_i18n, [this](){
|
||||
auto options = std::make_shared<Sidebar>("Homebrew Options"_i18n, Sidebar::Side::RIGHT);
|
||||
auto options = std::make_shared<Sidebar>("Game Options"_i18n, Sidebar::Side::RIGHT);
|
||||
ON_SCOPE_EXIT(App::Push(options));
|
||||
|
||||
if (m_entries.size()) {
|
||||
@@ -117,6 +135,40 @@ Menu::Menu() : MenuBase{"Games"_i18n} {
|
||||
));
|
||||
}));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("List meta records"_i18n, [this](){
|
||||
MetaEntries meta_entries;
|
||||
const auto rc = GetMetaEntries(m_entries[m_index], meta_entries);
|
||||
if (R_FAILED(rc)) {
|
||||
App::Push(std::make_shared<ui::ErrorBox>(rc,
|
||||
i18n::get("Failed to list application meta entries")
|
||||
));
|
||||
return;
|
||||
}
|
||||
|
||||
if (meta_entries.empty()) {
|
||||
App::Notify("No meta entries found...\n");
|
||||
return;
|
||||
}
|
||||
|
||||
PopupList::Items items;
|
||||
for (auto& e : meta_entries) {
|
||||
char buf[256];
|
||||
std::snprintf(buf, sizeof(buf), "Type: %s Storage: %s [%016lX][v%u]", ncm::GetMetaTypeStr(e.meta_type), ncm::GetStorageIdStr(e.storageID), e.application_id, e.version);
|
||||
items.emplace_back(buf);
|
||||
}
|
||||
|
||||
App::Push(std::make_shared<PopupList>(
|
||||
"Entries", items, [this, meta_entries](auto op_index){
|
||||
#if 0
|
||||
if (op_index) {
|
||||
const auto& e = meta_entries[*op_index];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
));
|
||||
}));
|
||||
|
||||
// completely deletes the application record and all data.
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Delete"_i18n, [this](){
|
||||
const auto buf = "Are you sure you want to delete "_i18n + m_entries[m_index].GetName() + "?";
|
||||
App::Push(std::make_shared<OptionBox>(
|
||||
@@ -133,6 +185,20 @@ Menu::Menu() : MenuBase{"Games"_i18n} {
|
||||
}, m_entries[m_index].image
|
||||
));
|
||||
}, true));
|
||||
|
||||
// removes installed data but keeps the record, basically archiving.
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Delete entity"_i18n, [this](){
|
||||
const auto buf = "Are you sure you want to delete "_i18n + m_entries[m_index].GetName() + "?";
|
||||
App::Push(std::make_shared<OptionBox>(
|
||||
buf,
|
||||
"Back"_i18n, "Delete"_i18n, 0, [this](auto op_index){
|
||||
if (op_index && *op_index) {
|
||||
const auto rc = nsDeleteApplicationEntity(m_entries[m_index].app_id);
|
||||
Notify(rc, "Failed to delete application");
|
||||
}
|
||||
}, m_entries[m_index].image
|
||||
));
|
||||
}, true));
|
||||
}
|
||||
}})
|
||||
);
|
||||
@@ -197,15 +263,10 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
const auto text_off = 148;
|
||||
const auto text_x = x + text_off;
|
||||
const auto text_clip_w = w - 30.f - text_off;
|
||||
nvgSave(vg);
|
||||
nvgIntersectScissor(vg, text_x, y, text_clip_w, h); // clip
|
||||
{
|
||||
const float font_size = 18;
|
||||
m_scroll_name.Draw(vg, selected, text_x, y + 45, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.GetName());
|
||||
m_scroll_author.Draw(vg, selected, text_x, y + 80, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.GetAuthor());
|
||||
m_scroll_version.Draw(vg, selected, text_x, y + 115, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.GetDisplayVersion());
|
||||
}
|
||||
nvgRestore(vg);
|
||||
const float font_size = 18;
|
||||
m_scroll_name.Draw(vg, selected, text_x, y + 45, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.GetName());
|
||||
m_scroll_author.Draw(vg, selected, text_x, y + 80, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.GetAuthor());
|
||||
m_scroll_version.Draw(vg, selected, text_x, y + 115, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.GetDisplayVersion());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -249,7 +310,20 @@ void Menu::ScanHomebrew() {
|
||||
}
|
||||
|
||||
for (s32 i = 0; i < record_count; i++) {
|
||||
// log_write("ID: %016lx got type: %u\n", record_list[i].application_id, record_list[i].type);
|
||||
#if 0
|
||||
u8 unk_x09 = record_list[i].unk_x09;
|
||||
u64 unk_x0a;// = record_list[i].unk_x0a;
|
||||
u8 unk_x10 = record_list[i].unk_x10;
|
||||
u64 unk_x11;// = record_list[i].unk_x11;
|
||||
memcpy(&unk_x0a, record_list[i].unk_x0a, sizeof(record_list[i].unk_x0a));
|
||||
memcpy(&unk_x11, record_list[i].unk_x11, sizeof(record_list[i].unk_x11));
|
||||
log_write("ID: %016lx got type: %u unk_x09: %u unk_x0a: %zu unk_x10: %u unk_x11: %zu\n", record_list[i].application_id, record_list[i].type,
|
||||
unk_x09,
|
||||
unk_x0a,
|
||||
unk_x10,
|
||||
unk_x11
|
||||
);
|
||||
#endif
|
||||
m_entries.emplace_back(record_list[i].application_id);
|
||||
}
|
||||
|
||||
|
||||
@@ -176,23 +176,18 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
const auto text_off = 148;
|
||||
const auto text_x = x + text_off;
|
||||
const auto text_clip_w = w - 30.f - text_off;
|
||||
nvgSave(vg);
|
||||
nvgIntersectScissor(vg, text_x, y, text_clip_w, h); // clip
|
||||
{
|
||||
bool has_star = false;
|
||||
if (IsStarEnabled()) {
|
||||
if (!e.has_star.has_value()) {
|
||||
e.has_star = fs::FsNativeSd().FileExists(GenerateStarPath(e.path));
|
||||
}
|
||||
has_star = e.has_star.value();
|
||||
bool has_star = false;
|
||||
if (IsStarEnabled()) {
|
||||
if (!e.has_star.has_value()) {
|
||||
e.has_star = fs::FsNativeSd().FileExists(GenerateStarPath(e.path));
|
||||
}
|
||||
|
||||
const float font_size = 18;
|
||||
m_scroll_name.DrawArgs(vg, selected, text_x, y + 45, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), "%s%s", has_star ? "\u2605 " : "", e.GetName());
|
||||
m_scroll_author.Draw(vg, selected, text_x, y + 80, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.GetAuthor());
|
||||
m_scroll_version.Draw(vg, selected, text_x, y + 115, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.GetDisplayVersion());
|
||||
has_star = e.has_star.value();
|
||||
}
|
||||
nvgRestore(vg);
|
||||
|
||||
const float font_size = 18;
|
||||
m_scroll_name.DrawArgs(vg, selected, text_x, y + 45, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), "%s%s", has_star ? "\u2605 " : "", e.GetName());
|
||||
m_scroll_author.Draw(vg, selected, text_x, y + 80, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.GetAuthor());
|
||||
m_scroll_version.Draw(vg, selected, text_x, y + 115, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.GetDisplayVersion());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -610,14 +610,9 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
|
||||
const auto text_x = x + xoff;
|
||||
const auto text_clip_w = w - 30.f - xoff;
|
||||
nvgSave(vg);
|
||||
nvgIntersectScissor(vg, text_x, y, text_clip_w, h); // clip
|
||||
{
|
||||
const float font_size = 18;
|
||||
m_scroll_name.Draw(vg, selected, text_x, y + 180 + 20, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.details.name.c_str());
|
||||
m_scroll_author.Draw(vg, selected, text_x, y + 180 + 55, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.creator.display_name.c_str());
|
||||
}
|
||||
nvgRestore(vg);
|
||||
const float font_size = 18;
|
||||
m_scroll_name.Draw(vg, selected, text_x, y + 180 + 20, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.details.name.c_str());
|
||||
m_scroll_author.Draw(vg, selected, text_x, y + 180 + 55, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), e.creator.display_name.c_str());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -113,7 +113,8 @@ auto PopupList::Draw(NVGcontext* vg, Theme* theme) -> void {
|
||||
m_list->Draw(vg, theme, m_items.size(), [this](auto* vg, auto* theme, auto v, auto i) {
|
||||
const auto& [x, y, w, h] = v;
|
||||
auto colour = ThemeEntryID_TEXT;
|
||||
if (m_index == i) {
|
||||
const auto selected = m_index == i;
|
||||
if (selected) {
|
||||
gfx::drawRectOutline(vg, theme, 4.f, v);
|
||||
} else {
|
||||
if (i != m_items.size() - 1) {
|
||||
@@ -126,7 +127,9 @@ auto PopupList::Draw(NVGcontext* vg, Theme* theme) -> void {
|
||||
gfx::drawText(vg, x + w - m_text_xoffset, y + (h / 2.f), 20.f, "\uE14B", NULL, NVG_ALIGN_RIGHT | NVG_ALIGN_MIDDLE, theme->GetColour(colour));
|
||||
}
|
||||
|
||||
gfx::drawText(vg, x + m_text_xoffset, y + (h / 2.f), 20.f, m_items[i].c_str(), NULL, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, theme->GetColour(colour));
|
||||
const auto text_x = x + m_text_xoffset;
|
||||
const auto text_clip_w = w - 60.f - m_text_xoffset;
|
||||
m_scroll_text.Draw(vg, selected, text_x, y + (h / 2.f), text_clip_w, 20.f, NVG_ALIGN_LEFT | NVG_ALIGN_MIDDLE, theme->GetColour(colour), m_items[i]);
|
||||
});
|
||||
|
||||
Widget::Draw(vg, theme);
|
||||
|
||||
@@ -14,11 +14,20 @@ auto GetTextScrollSpeed() -> float {
|
||||
}
|
||||
}
|
||||
|
||||
void DrawClipped(NVGcontext* vg, const Vec4& clip, float x, float y, float size, int align, const NVGcolor& colour, const std::string& str) {
|
||||
nvgSave(vg);
|
||||
nvgIntersectScissor(vg, clip.x, clip.y, clip.w, clip.h); // clip
|
||||
gfx::drawText(vg, x, y, size, colour, str.c_str(), align);
|
||||
nvgRestore(vg);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ScrollingText::Draw(NVGcontext* vg, bool focus, float x, float y, float w, float size, int align, const NVGcolor& colour, const std::string& text_entry) {
|
||||
const Vec4 clip{x, 0, w, 720};
|
||||
|
||||
if (!focus) {
|
||||
gfx::drawText(vg, x, y, size, colour, text_entry.c_str(), align);
|
||||
DrawClipped(vg, clip, x, y, size, align, colour, text_entry);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -56,8 +65,8 @@ void ScrollingText::Draw(NVGcontext* vg, bool focus, float x, float y, float w,
|
||||
}
|
||||
}
|
||||
|
||||
const Vec2 pos{x - m_text_xoff, y};
|
||||
gfx::drawText(vg, pos, size, colour, value_str.c_str(), align);
|
||||
x -= m_text_xoff;
|
||||
DrawClipped(vg, clip, x, y, size, align, colour, value_str);
|
||||
}
|
||||
|
||||
void ScrollingText::DrawArgs(NVGcontext* vg, bool focus, float x, float y, float w, float size, int align, const NVGcolor& colour, const char* s, ...) {
|
||||
|
||||
@@ -7,6 +7,38 @@ namespace {
|
||||
|
||||
} // namespace
|
||||
|
||||
auto GetMetaTypeStr(u8 meta_type) -> const char* {
|
||||
switch (meta_type) {
|
||||
case NcmContentMetaType_Unknown: return "Unknown";
|
||||
case NcmContentMetaType_SystemProgram: return "SystemProgram";
|
||||
case NcmContentMetaType_SystemData: return "SystemData";
|
||||
case NcmContentMetaType_SystemUpdate: return "SystemUpdate";
|
||||
case NcmContentMetaType_BootImagePackage: return "BootImagePackage";
|
||||
case NcmContentMetaType_BootImagePackageSafe: return "BootImagePackageSafe";
|
||||
case NcmContentMetaType_Application: return "Application";
|
||||
case NcmContentMetaType_Patch: return "Patch";
|
||||
case NcmContentMetaType_AddOnContent: return "AddOnContent";
|
||||
case NcmContentMetaType_Delta: return "Delta";
|
||||
case NcmContentMetaType_DataPatch: return "DataPatch";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
auto GetStorageIdStr(u8 storage_id) -> const char* {
|
||||
switch (storage_id) {
|
||||
case NcmStorageId_None: return "None";
|
||||
case NcmStorageId_Host: return "Host";
|
||||
case NcmStorageId_GameCard: return "GameCard";
|
||||
case NcmStorageId_BuiltInSystem: return "BuiltInSystem";
|
||||
case NcmStorageId_BuiltInUser: return "BuiltInUser";
|
||||
case NcmStorageId_SdCard: return "SdCard";
|
||||
case NcmStorageId_Any: return "Any";
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
auto GetAppId(u8 meta_type, u64 id) -> u64 {
|
||||
if (meta_type == NcmContentMetaType_Patch) {
|
||||
return id ^ 0x800;
|
||||
|
||||
Reference in New Issue
Block a user