add layout options to grid based menues.
This commit is contained in:
@@ -13,6 +13,7 @@
|
||||
#include "yyjson_helper.hpp"
|
||||
#include "swkbd.hpp"
|
||||
#include "i18n.hpp"
|
||||
#include "nro.hpp"
|
||||
|
||||
#include <minIni.h>
|
||||
#include <string>
|
||||
@@ -34,8 +35,6 @@ constexpr auto URL_JSON = "https://switch.cdn.fortheusers.org/repo.json";
|
||||
constexpr auto URL_POST_FEEDBACK = "http://switchbru.com/appstore/feedback";
|
||||
constexpr auto URL_GET_FEEDACK = "http://switchbru.com/appstore/feedback";
|
||||
|
||||
constexpr const char* INI_SECTION = "appstore";
|
||||
|
||||
constexpr const char* FILTER_STR[] = {
|
||||
"All",
|
||||
"Games",
|
||||
@@ -846,7 +845,7 @@ void EntryMenu::SetIndex(s64 index) {
|
||||
}
|
||||
}
|
||||
|
||||
Menu::Menu() : MenuBase{"AppStore"_i18n} {
|
||||
Menu::Menu() : grid::Menu{"AppStore"_i18n} {
|
||||
fs::FsNativeSd fs;
|
||||
fs.CreateDirectoryRecursively("/switch/sphaira/cache/appstore/icons");
|
||||
fs.CreateDirectoryRecursively("/switch/sphaira/cache/appstore/banners");
|
||||
@@ -859,7 +858,7 @@ Menu::Menu() : MenuBase{"AppStore"_i18n} {
|
||||
if (m_is_search) {
|
||||
SetSearch(m_search_term);
|
||||
} else {
|
||||
SetFilter(m_filter);
|
||||
SetFilter();
|
||||
}
|
||||
|
||||
SetIndex(m_entry_author_jump_back);
|
||||
@@ -870,7 +869,7 @@ Menu::Menu() : MenuBase{"AppStore"_i18n} {
|
||||
}
|
||||
} else if (m_is_search) {
|
||||
m_is_search = false;
|
||||
SetFilter(m_filter);
|
||||
SetFilter();
|
||||
SetIndex(m_entry_search_jump_back);
|
||||
if (m_entry_search_jump_back >= 9) {
|
||||
m_list->SetYoff(0);
|
||||
@@ -913,17 +912,30 @@ Menu::Menu() : MenuBase{"AppStore"_i18n} {
|
||||
order_items.push_back("Descending"_i18n);
|
||||
order_items.push_back("Ascending"_i18n);
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Filter"_i18n, filter_items, [this, filter_items](s64& index_out){
|
||||
SetFilter((Filter)index_out);
|
||||
}, (s64)m_filter));
|
||||
SidebarEntryArray::Items layout_items;
|
||||
layout_items.push_back("List"_i18n);
|
||||
layout_items.push_back("Icon"_i18n);
|
||||
layout_items.push_back("Grid"_i18n);
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this, sort_items](s64& index_out){
|
||||
SetSort((SortType)index_out);
|
||||
}, (s64)m_sort));
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Filter"_i18n, filter_items, [this](s64& index_out){
|
||||
m_filter.Set(index_out);
|
||||
SetFilter();
|
||||
}, m_filter.Get()));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Order"_i18n, order_items, [this, order_items](s64& index_out){
|
||||
SetOrder((OrderType)index_out);
|
||||
}, (s64)m_order));
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this](s64& index_out){
|
||||
m_sort.Set(index_out);
|
||||
SortAndFindLastFile();
|
||||
}, m_sort.Get()));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Order"_i18n, order_items, [this](s64& index_out){
|
||||
m_order.Set(index_out);
|
||||
SortAndFindLastFile();
|
||||
}, m_order.Get()));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Layout"_i18n, layout_items, [this](s64& index_out){
|
||||
m_layout.Set(index_out);
|
||||
OnLayoutChange();
|
||||
}, m_layout.Get()));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryCallback>("Search"_i18n, [this](){
|
||||
std::string out;
|
||||
@@ -953,14 +965,7 @@ Menu::Menu() : MenuBase{"AppStore"_i18n} {
|
||||
}
|
||||
});
|
||||
|
||||
m_filter = (Filter)ini_getl(INI_SECTION, "filter", m_filter, App::CONFIG_PATH);
|
||||
m_sort = (SortType)ini_getl(INI_SECTION, "sort", m_sort, App::CONFIG_PATH);
|
||||
m_order = (OrderType)ini_getl(INI_SECTION, "order", m_order, App::CONFIG_PATH);
|
||||
|
||||
const Vec4 v{75, 110, 370, 155};
|
||||
const Vec2 pad{10, 10};
|
||||
m_list = std::make_unique<List>(3, 9, m_pos, v, pad);
|
||||
Sort();
|
||||
OnLayoutChange();
|
||||
}
|
||||
|
||||
Menu::~Menu() {
|
||||
@@ -1055,42 +1060,27 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
}
|
||||
}
|
||||
|
||||
auto text_id = ThemeEntryID_TEXT;
|
||||
const auto selected = pos == m_index;
|
||||
if (selected) {
|
||||
text_id = ThemeEntryID_TEXT_SELECTED;
|
||||
gfx::drawRectOutline(vg, theme, 4.f, v);
|
||||
} else {
|
||||
DrawElement(x, y, w, h, ThemeEntryID_GRID);
|
||||
}
|
||||
const auto image_vec = DrawEntryNoImage(vg, theme, m_layout.Get(), v, selected, e.title.c_str(), e.author.c_str(), e.version.c_str());
|
||||
|
||||
constexpr double image_scale = 256.0 / 115.0;
|
||||
// const float image_size = 256 / image_scale;
|
||||
// const float image_size_h = 150 / image_scale;
|
||||
DrawIcon(vg, e.image, m_default_image, x + 20, y + 20, 115, 115, true, image_scale);
|
||||
const auto image_scale = 256.0 / image_vec.w;
|
||||
DrawIcon(vg, e.image, m_default_image, image_vec.x, image_vec.y, image_vec.w, image_vec.h, true, image_scale);
|
||||
// gfx::drawImage(vg, x + 20, y + 20, image_size, image_size_h, image.image ? image.image : m_default_image);
|
||||
|
||||
const auto text_off = 148;
|
||||
const auto text_x = x + text_off;
|
||||
const auto text_clip_w = w - 30.f - text_off;
|
||||
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());
|
||||
|
||||
// todo: fix position on non-grid layout.
|
||||
float i_size = 22;
|
||||
switch (e.status) {
|
||||
case EntryStatus::Get:
|
||||
gfx::drawImage(vg, x + w - 30.f, y + 110, i_size, i_size, m_get.image, 15);
|
||||
gfx::drawImage(vg, x + w - 30.f, y + 110, i_size, i_size, m_get.image, 0);
|
||||
break;
|
||||
case EntryStatus::Installed:
|
||||
gfx::drawImage(vg, x + w - 30.f, y + 110, i_size, i_size, m_installed.image, 15);
|
||||
gfx::drawImage(vg, x + w - 30.f, y + 110, i_size, i_size, m_installed.image, 0);
|
||||
break;
|
||||
case EntryStatus::Local:
|
||||
gfx::drawImage(vg, x + w - 30.f, y + 110, i_size, i_size, m_local.image, 15);
|
||||
gfx::drawImage(vg, x + w - 30.f, y + 110, i_size, i_size, m_local.image, 0);
|
||||
break;
|
||||
case EntryStatus::Update:
|
||||
gfx::drawImage(vg, x + w - 30.f, y + 110, i_size, i_size, m_update.image, 15);
|
||||
gfx::drawImage(vg, x + w - 30.f, y + 110, i_size, i_size, m_update.image, 0);
|
||||
break;
|
||||
}
|
||||
});
|
||||
@@ -1126,12 +1116,16 @@ void Menu::OnFocusGained() {
|
||||
|
||||
for (u32 i = 0; i < m_entries_current.size(); i++) {
|
||||
if (current_entry.name == m_entries[m_entries_current[i]].name) {
|
||||
SetIndex(i);
|
||||
if (i >= 9) {
|
||||
m_list->SetYoff((((i - 9) + 3) / 3) * m_list->GetMaxY());
|
||||
const auto index = i;
|
||||
const auto row = m_list->GetRow();
|
||||
const auto page = m_list->GetPage();
|
||||
// guesstimate where the position is
|
||||
if (index >= page) {
|
||||
m_list->SetYoff((((index - page) + row) / row) * m_list->GetMaxY());
|
||||
} else {
|
||||
m_list->SetYoff(0);
|
||||
}
|
||||
SetIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -1215,15 +1209,20 @@ void Menu::ScanHomebrew() {
|
||||
index.shrink_to_fit();
|
||||
}
|
||||
|
||||
SetFilter(Filter_All);
|
||||
SetFilter();
|
||||
SetIndex(0);
|
||||
Sort();
|
||||
}
|
||||
|
||||
void Menu::Sort() {
|
||||
// log_write("doing sort: size: %zu count: %zu\n", repo_json.size(), m_entries.size());
|
||||
|
||||
const auto sort = m_sort.Get();
|
||||
const auto order = m_order.Get();
|
||||
const auto filter = m_filter.Get();
|
||||
|
||||
// returns true if lhs should be before rhs
|
||||
const auto sorter = [this](EntryMini _lhs, EntryMini _rhs) -> bool {
|
||||
const auto sorter = [this, sort, order](EntryMini _lhs, EntryMini _rhs) -> bool {
|
||||
const auto& lhs = m_entries[_lhs];
|
||||
const auto& rhs = m_entries[_rhs];
|
||||
|
||||
@@ -1241,11 +1240,11 @@ void Menu::Sort() {
|
||||
} else if (!(lhs.status == EntryStatus::Local) && rhs.status == EntryStatus::Local) {
|
||||
return false;
|
||||
} else {
|
||||
switch (m_sort) {
|
||||
switch (sort) {
|
||||
case SortType_Updated: {
|
||||
if (lhs.updated_num == rhs.updated_num) {
|
||||
return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0;
|
||||
} else if (m_order == OrderType_Descending) {
|
||||
} else if (order == OrderType_Descending) {
|
||||
return lhs.updated_num > rhs.updated_num;
|
||||
} else {
|
||||
return lhs.updated_num < rhs.updated_num;
|
||||
@@ -1254,7 +1253,7 @@ void Menu::Sort() {
|
||||
case SortType_Downloads: {
|
||||
if (lhs.app_dls == rhs.app_dls) {
|
||||
return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0;
|
||||
} else if (m_order == OrderType_Descending) {
|
||||
} else if (order == OrderType_Descending) {
|
||||
return lhs.app_dls > rhs.app_dls;
|
||||
} else {
|
||||
return lhs.app_dls < rhs.app_dls;
|
||||
@@ -1263,14 +1262,14 @@ void Menu::Sort() {
|
||||
case SortType_Size: {
|
||||
if (lhs.extracted == rhs.extracted) {
|
||||
return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0;
|
||||
} else if (m_order == OrderType_Descending) {
|
||||
} else if (order == OrderType_Descending) {
|
||||
return lhs.extracted > rhs.extracted;
|
||||
} else {
|
||||
return lhs.extracted < rhs.extracted;
|
||||
}
|
||||
} break;
|
||||
case SortType_Alphabetical: {
|
||||
if (m_order == OrderType_Descending) {
|
||||
if (order == OrderType_Descending) {
|
||||
return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) < 0;
|
||||
} else {
|
||||
return strcasecmp(lhs.name.c_str(), rhs.name.c_str()) > 0;
|
||||
@@ -1284,33 +1283,43 @@ void Menu::Sort() {
|
||||
|
||||
|
||||
char subheader[128]{};
|
||||
std::snprintf(subheader, sizeof(subheader), "Filter: %s | Sort: %s | Order: %s"_i18n.c_str(), i18n::get(FILTER_STR[m_filter]).c_str(), i18n::get(SORT_STR[m_sort]).c_str(), i18n::get(ORDER_STR[m_order]).c_str());
|
||||
std::snprintf(subheader, sizeof(subheader), "Filter: %s | Sort: %s | Order: %s"_i18n.c_str(), i18n::get(FILTER_STR[filter]).c_str(), i18n::get(SORT_STR[sort]).c_str(), i18n::get(ORDER_STR[order]).c_str());
|
||||
SetTitleSubHeading(subheader);
|
||||
|
||||
std::sort(m_entries_current.begin(), m_entries_current.end(), sorter);
|
||||
}
|
||||
|
||||
void Menu::SetFilter(Filter filter) {
|
||||
void Menu::SortAndFindLastFile() {
|
||||
const auto name = GetEntry().name;
|
||||
Sort();
|
||||
SetIndex(0);
|
||||
|
||||
s64 index = -1;
|
||||
for (u64 i = 0; i < m_entries_current.size(); i++) {
|
||||
if (name == GetEntry(i).name) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
const auto row = m_list->GetRow();
|
||||
const auto page = m_list->GetPage();
|
||||
// guesstimate where the position is
|
||||
if (index >= page) {
|
||||
m_list->SetYoff((((index - page) + row) / row) * m_list->GetMaxY());
|
||||
} else {
|
||||
m_list->SetYoff(0);
|
||||
}
|
||||
SetIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
void Menu::SetFilter() {
|
||||
m_is_search = false;
|
||||
m_is_author = false;
|
||||
|
||||
m_filter = filter;
|
||||
m_entries_current = m_entries_index[m_filter];
|
||||
ini_putl(INI_SECTION, "filter", m_filter, App::CONFIG_PATH);
|
||||
SetIndex(0);
|
||||
Sort();
|
||||
}
|
||||
|
||||
void Menu::SetSort(SortType sort) {
|
||||
m_sort = sort;
|
||||
ini_putl(INI_SECTION, "sort", m_sort, App::CONFIG_PATH);
|
||||
SetIndex(0);
|
||||
Sort();
|
||||
}
|
||||
|
||||
void Menu::SetOrder(OrderType order) {
|
||||
m_order = order;
|
||||
ini_putl(INI_SECTION, "order", m_order, App::CONFIG_PATH);
|
||||
m_entries_current = m_entries_index[m_filter.Get()];
|
||||
SetIndex(0);
|
||||
Sort();
|
||||
}
|
||||
@@ -1359,6 +1368,11 @@ void Menu::SetAuthor() {
|
||||
Sort();
|
||||
}
|
||||
|
||||
void Menu::OnLayoutChange() {
|
||||
m_index = 0;
|
||||
grid::Menu::OnLayoutChange(m_list, m_layout.Get());
|
||||
}
|
||||
|
||||
LazyImage::~LazyImage() {
|
||||
if (image) {
|
||||
nvgDeleteImage(App::GetVg(), image);
|
||||
|
||||
@@ -113,7 +113,7 @@ void LaunchEntry(const Entry& e) {
|
||||
|
||||
} // namespace
|
||||
|
||||
Menu::Menu() : MenuBase{"Games"_i18n} {
|
||||
Menu::Menu() : grid::Menu{"Games"_i18n} {
|
||||
this->SetActions(
|
||||
std::make_pair(Button::B, Action{"Back"_i18n, [this](){
|
||||
SetPop();
|
||||
@@ -140,16 +140,26 @@ Menu::Menu() : MenuBase{"Games"_i18n} {
|
||||
order_items.push_back("Descending"_i18n);
|
||||
order_items.push_back("Ascending"_i18n);
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this, sort_items](s64& index_out){
|
||||
SidebarEntryArray::Items layout_items;
|
||||
layout_items.push_back("List"_i18n);
|
||||
layout_items.push_back("Icon"_i18n);
|
||||
layout_items.push_back("Grid"_i18n);
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this](s64& index_out){
|
||||
m_sort.Set(index_out);
|
||||
SortAndFindLastFile(false);
|
||||
}, m_sort.Get()));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Order"_i18n, order_items, [this, order_items](s64& index_out){
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Order"_i18n, order_items, [this](s64& index_out){
|
||||
m_order.Set(index_out);
|
||||
SortAndFindLastFile(false);
|
||||
}, m_order.Get()));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Layout"_i18n, layout_items, [this](s64& index_out){
|
||||
m_layout.Set(index_out);
|
||||
OnLayoutChange();
|
||||
}, m_layout.Get()));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Hide forwarders"_i18n, m_hide_forwarders.Get(), [this](bool& v_out){
|
||||
m_hide_forwarders.Set(v_out);
|
||||
m_dirty = true;
|
||||
@@ -241,9 +251,7 @@ Menu::Menu() : MenuBase{"Games"_i18n} {
|
||||
}})
|
||||
);
|
||||
|
||||
const Vec4 v{75, 110, 370, 155};
|
||||
const Vec2 pad{10, 10};
|
||||
m_list = std::make_unique<List>(3, 9, m_pos, v, pad);
|
||||
OnLayoutChange();
|
||||
|
||||
nsInitialize();
|
||||
nsGetApplicationRecordUpdateSystemEvent(&m_event);
|
||||
@@ -285,7 +293,7 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
int image_load_count = 0;
|
||||
|
||||
m_list->Draw(vg, theme, m_entries.size(), [this, &image_load_count](auto* vg, auto* theme, auto v, auto pos) {
|
||||
const auto& [x, y, w, h] = v;
|
||||
// const auto& [x, y, w, h] = v;
|
||||
auto& e = m_entries[pos];
|
||||
|
||||
if (e.status == NacpLoadStatus::None) {
|
||||
@@ -299,25 +307,8 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
}
|
||||
}
|
||||
|
||||
auto text_id = ThemeEntryID_TEXT;
|
||||
const auto selected = pos == m_index;
|
||||
if (selected) {
|
||||
text_id = ThemeEntryID_TEXT_SELECTED;
|
||||
gfx::drawRectOutline(vg, theme, 4.f, v);
|
||||
} else {
|
||||
DrawElement(v, ThemeEntryID_GRID);
|
||||
}
|
||||
|
||||
const float image_size = 115;
|
||||
gfx::drawImage(vg, x + 20, y + 20, image_size, image_size, e.image ? e.image : App::GetDefaultImage(), 5);
|
||||
|
||||
const auto text_off = 148;
|
||||
const auto text_x = x + text_off;
|
||||
const auto text_clip_w = w - 30.f - text_off;
|
||||
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());
|
||||
DrawEntry(vg, theme, m_layout.Get(), v, selected, e.image, e.GetName(), e.GetAuthor(), e.GetDisplayVersion());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -446,9 +437,11 @@ void Menu::SortAndFindLastFile(bool scan) {
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
const auto row = m_list->GetRow();
|
||||
const auto page = m_list->GetPage();
|
||||
// guesstimate where the position is
|
||||
if (index >= 9) {
|
||||
m_list->SetYoff((((index - 9) + 3) / 3) * m_list->GetMaxY());
|
||||
if (index >= page) {
|
||||
m_list->SetYoff((((index - page) + row) / row) * m_list->GetMaxY());
|
||||
} else {
|
||||
m_list->SetYoff(0);
|
||||
}
|
||||
@@ -466,4 +459,9 @@ void Menu::FreeEntries() {
|
||||
m_entries.clear();
|
||||
}
|
||||
|
||||
void Menu::OnLayoutChange() {
|
||||
m_index = 0;
|
||||
grid::Menu::OnLayoutChange(m_list, m_layout.Get());
|
||||
}
|
||||
|
||||
} // namespace sphaira::ui::menu::game
|
||||
|
||||
81
sphaira/source/ui/menus/grid_menu_base.cpp
Normal file
81
sphaira/source/ui/menus/grid_menu_base.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
#include "app.hpp"
|
||||
#include "ui/menus/grid_menu_base.hpp"
|
||||
#include "ui/nvg_util.hpp"
|
||||
|
||||
namespace sphaira::ui::menu::grid {
|
||||
|
||||
void Menu::DrawEntry(NVGcontext* vg, Theme* theme, int layout, const Vec4& v, bool selected, int image, const char* name, const char* author, const char* version) {
|
||||
DrawEntry(vg, theme, true, layout, v, selected, image, name, author, version);
|
||||
}
|
||||
|
||||
Vec4 Menu::DrawEntryNoImage(NVGcontext* vg, Theme* theme, int layout, const Vec4& v, bool selected, const char* name, const char* author, const char* version) {
|
||||
return DrawEntry(vg, theme, false, layout, v, selected, 0, name, author, version);
|
||||
}
|
||||
|
||||
Vec4 Menu::DrawEntry(NVGcontext* vg, Theme* theme, bool draw_image, int layout, const Vec4& v, bool selected, int image, const char* name, const char* author, const char* version) {
|
||||
const auto& [x, y, w, h] = v;
|
||||
|
||||
auto text_id = ThemeEntryID_TEXT;
|
||||
if (selected) {
|
||||
text_id = ThemeEntryID_TEXT_SELECTED;
|
||||
gfx::drawRectOutline(vg, theme, 4.f, v);
|
||||
} else {
|
||||
DrawElement(v, ThemeEntryID_GRID);
|
||||
}
|
||||
|
||||
Vec4 image_v = v;
|
||||
|
||||
if (layout == LayoutType_GridDetail) {
|
||||
image_v.x += 20;
|
||||
image_v.y += 20;
|
||||
image_v.w = 115;
|
||||
image_v.h = 115;
|
||||
|
||||
const auto text_off = 148;
|
||||
const auto text_x = x + text_off;
|
||||
const auto text_clip_w = w - 30.f - text_off;
|
||||
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), name);
|
||||
m_scroll_author.Draw(vg, selected, text_x, y + 80, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), author);
|
||||
m_scroll_version.Draw(vg, selected, text_x, y + 115, text_clip_w, font_size, NVG_ALIGN_LEFT, theme->GetColour(text_id), version);
|
||||
} else {
|
||||
if (selected) {
|
||||
gfx::drawAppLable(vg, theme, m_scroll_name, x, y, w, name);
|
||||
}
|
||||
}
|
||||
|
||||
if (draw_image) {
|
||||
gfx::drawImage(vg, image_v, image ?: App::GetDefaultImage(), 5);
|
||||
}
|
||||
|
||||
return image_v;
|
||||
}
|
||||
|
||||
void Menu::OnLayoutChange(std::unique_ptr<List>& list, int layout) {
|
||||
m_scroll_name.Reset();
|
||||
m_scroll_author.Reset();
|
||||
m_scroll_version.Reset();
|
||||
|
||||
switch (layout) {
|
||||
case LayoutType_List: {
|
||||
const Vec2 pad{14, 14};
|
||||
const Vec4 v{106, 194, 256, 256};
|
||||
list = std::make_unique<List>(1, 4, m_pos, v, pad);
|
||||
list->SetLayout(List::Layout::HOME);
|
||||
} break;
|
||||
|
||||
case LayoutType_Grid: {
|
||||
const Vec2 pad{10, 10};
|
||||
const Vec4 v{93, 186, 174, 174};
|
||||
list = std::make_unique<List>(6, 6*2, m_pos, v, pad);
|
||||
} break;
|
||||
|
||||
case LayoutType_GridDetail: {
|
||||
const Vec2 pad{10, 10};
|
||||
const Vec4 v{75, 110, 370, 155};
|
||||
list = std::make_unique<List>(3, 3*3, m_pos, v, pad);
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace sphaira::ui::menu::grid
|
||||
@@ -31,7 +31,7 @@ void FreeEntry(NVGcontext* vg, NroEntry& e) {
|
||||
|
||||
} // namespace
|
||||
|
||||
Menu::Menu() : MenuBase{"Homebrew"_i18n} {
|
||||
Menu::Menu() : grid::Menu{"Homebrew"_i18n} {
|
||||
this->SetActions(
|
||||
std::make_pair(Button::A, Action{"Launch"_i18n, [this](){
|
||||
nro_launch(m_entries[m_index].path);
|
||||
@@ -57,6 +57,11 @@ Menu::Menu() : MenuBase{"Homebrew"_i18n} {
|
||||
order_items.push_back("Descending"_i18n);
|
||||
order_items.push_back("Ascending"_i18n);
|
||||
|
||||
SidebarEntryArray::Items layout_items;
|
||||
layout_items.push_back("List"_i18n);
|
||||
layout_items.push_back("Icon"_i18n);
|
||||
layout_items.push_back("Grid"_i18n);
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Sort"_i18n, sort_items, [this, sort_items](s64& index_out){
|
||||
m_sort.Set(index_out);
|
||||
SortAndFindLastFile();
|
||||
@@ -67,6 +72,11 @@ Menu::Menu() : MenuBase{"Homebrew"_i18n} {
|
||||
SortAndFindLastFile();
|
||||
}, m_order.Get()));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryArray>("Layout"_i18n, layout_items, [this](s64& index_out){
|
||||
m_layout.Set(index_out);
|
||||
OnLayoutChange();
|
||||
}, m_layout.Get()));
|
||||
|
||||
options->Add(std::make_shared<SidebarEntryBool>("Hide Sphaira"_i18n, m_hide_sphaira.Get(), [this](bool& enable){
|
||||
m_hide_sphaira.Set(enable);
|
||||
}));
|
||||
@@ -114,9 +124,7 @@ Menu::Menu() : MenuBase{"Homebrew"_i18n} {
|
||||
}})
|
||||
);
|
||||
|
||||
const Vec4 v{75, 110, 370, 155};
|
||||
const Vec2 pad{10, 10};
|
||||
m_list = std::make_unique<List>(3, 9, m_pos, v, pad);
|
||||
OnLayoutChange();
|
||||
}
|
||||
|
||||
Menu::~Menu() {
|
||||
@@ -143,7 +151,6 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
int image_load_count = 0;
|
||||
|
||||
m_list->Draw(vg, theme, m_entries.size(), [this, &image_load_count](auto* vg, auto* theme, auto v, auto pos) {
|
||||
const auto& [x, y, w, h] = v;
|
||||
auto& e = m_entries[pos];
|
||||
|
||||
// lazy load image
|
||||
@@ -161,21 +168,7 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
}
|
||||
}
|
||||
|
||||
auto text_id = ThemeEntryID_TEXT;
|
||||
const auto selected = pos == m_index;
|
||||
if (selected) {
|
||||
text_id = ThemeEntryID_TEXT_SELECTED;
|
||||
gfx::drawRectOutline(vg, theme, 4.f, v);
|
||||
} else {
|
||||
DrawElement(v, ThemeEntryID_GRID);
|
||||
}
|
||||
|
||||
const float image_size = 115;
|
||||
gfx::drawImage(vg, x + 20, y + 20, image_size, image_size, e.image ? e.image : App::GetDefaultImage(), 5);
|
||||
|
||||
const auto text_off = 148;
|
||||
const auto text_x = x + text_off;
|
||||
const auto text_clip_w = w - 30.f - text_off;
|
||||
bool has_star = false;
|
||||
if (IsStarEnabled()) {
|
||||
if (!e.has_star.has_value()) {
|
||||
@@ -184,10 +177,15 @@ void Menu::Draw(NVGcontext* vg, Theme* theme) {
|
||||
has_star = e.has_star.value();
|
||||
}
|
||||
|
||||
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());
|
||||
std::string name;
|
||||
if (has_star) {
|
||||
name = std::string("\u2605 ") + e.GetName();
|
||||
} else {
|
||||
name = e.GetName();
|
||||
}
|
||||
|
||||
const auto selected = pos == m_index;
|
||||
DrawEntry(vg, theme, m_layout.Get(), v, selected, e.image, name.c_str(), e.GetAuthor(), e.GetDisplayVersion());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -386,9 +384,11 @@ void Menu::SortAndFindLastFile() {
|
||||
}
|
||||
|
||||
if (index >= 0) {
|
||||
const auto row = m_list->GetRow();
|
||||
const auto page = m_list->GetPage();
|
||||
// guesstimate where the position is
|
||||
if (index >= 9) {
|
||||
m_list->SetYoff((((index - 9) + 3) / 3) * m_list->GetMaxY());
|
||||
if (index >= page) {
|
||||
m_list->SetYoff((((index - page) + row) / row) * m_list->GetMaxY());
|
||||
} else {
|
||||
m_list->SetYoff(0);
|
||||
}
|
||||
@@ -406,6 +406,11 @@ void Menu::FreeEntries() {
|
||||
m_entries.clear();
|
||||
}
|
||||
|
||||
void Menu::OnLayoutChange() {
|
||||
m_index = 0;
|
||||
grid::Menu::OnLayoutChange(m_list, m_layout.Get());
|
||||
}
|
||||
|
||||
Result Menu::InstallHomebrew(const fs::FsPath& path, const NacpStruct& nacp, const std::vector<u8>& icon) {
|
||||
OwoConfig config{};
|
||||
config.nro_path = path.toString();
|
||||
|
||||
Reference in New Issue
Block a user