Add folders, failed launch handling and more

This commit is contained in:
XorTroll 2019-10-12 15:43:56 +02:00
parent 5eaf42fe5e
commit d1ef9de979
15 changed files with 329 additions and 118 deletions

View file

@ -9,7 +9,8 @@ namespace am
Invalid,
StartupScreen,
MenuNormal,
MenuApplicationSuspended
MenuApplicationSuspended,
MenuLaunchFailure
};
enum class QMenuMessage

View file

@ -12,6 +12,7 @@ namespace cfg
struct TitleRecord
{
std::string json_name; // Empty for non-SD, normal title records
u32 title_type;
std::string sub_folder; // Empty for root, name for a certain folder
@ -33,7 +34,9 @@ namespace cfg
ResultWith<TitleList> LoadTitleList(bool cache);
void SaveRecord(TitleRecord record);
bool MoveTitleToDirectory(TitleList &list, u64 app_id, std::string dir);
TitleFolder &FindFolderByName(TitleList &list, std::string name);
std::string GetTitleCacheIconPath(u64 app_id);
std::string GetNROCacheIconPath(std::string path);

View file

@ -6,6 +6,12 @@
namespace os
{
#define OS_MAX_TITLE_COUNT 64000
#define OS_FLOG_APP_ID 0x01008BB00013C000
inline constexpr bool IsFlogTitle(u64 app_id)
{
return (app_id == OS_FLOG_APP_ID);
}
ResultWith<std::vector<cfg::TitleRecord>> QueryInstalledTitles(bool cache);
}

View file

@ -24,8 +24,7 @@ using JSON = nlohmann::json;
#define Q_DB_MOUNT_NAME "qsave"
#define Q_DB_MOUNT_PATH Q_DB_MOUNT_NAME ":/"
#define Q_BASE_DB_DIR Q_DB_MOUNT_PATH Q_BASE_DIR
#define Q_MENU_JSON Q_BASE_SD_DIR "/menu.json"
#define Q_ENTRIES_PATH Q_BASE_SD_DIR "/entries"
// Thanks SciresM
#define R_TRY(res_expr) \

View file

@ -8,15 +8,46 @@
namespace cfg
{
void SaveRecord(TitleRecord record)
{
JSON entry = JSON::object();
entry["type"] = record.title_type;
entry["folder"] = record.sub_folder;
// Prepare JSON path
std::string basepath = Q_ENTRIES_PATH;
std::string json = basepath;
if((TitleType)record.title_type == TitleType::Homebrew)
{
json += "/" + std::to_string(fs::GetFileSize(record.nro_path)) + ".json";
entry["nro_path"] = record.nro_path;
}
else if((TitleType)record.title_type == TitleType::Installed)
{
auto strappid = util::FormatApplicationId(record.app_id);
json += "/" + strappid + ".json";
entry["application_id"] = strappid;
}
if(!record.json_name.empty()) json = basepath + "/" + record.json_name;
if(fs::ExistsFile(json)) fs::DeleteFile(json);
std::ofstream ofs(json);
ofs << entry;
ofs.close();
}
bool MoveTitleToDirectory(TitleList &list, u64 app_id, std::string folder)
{
bool title_found = false;
TitleRecord record_copy = {};
std::string recjson;
// Search in root first
auto find = STLITER_FINDWITHCONDITION(list.root.titles, tit, (tit.app_id == app_id));
if(STLITER_ISFOUND(list.root.titles, find))
{
if(folder.empty()) return true; // It is already on root...?
recjson = STLITER_UNWRAP(find).json_name;
list.root.titles.erase(find);
title_found = true;
@ -26,13 +57,15 @@ namespace cfg
{
for(auto &fld: list.folders)
{
auto find = STLITER_FINDWITHCONDITION(fld.titles, item, (item.app_id == app_id));
auto find = STLITER_FINDWITHCONDITION(fld.titles, entry, (entry.app_id == app_id));
if(STLITER_ISFOUND(fld.titles, find))
{
if(fld.name == folder) return true; // It is already on that folder...?
recjson = STLITER_UNWRAP(find).json_name;
fld.titles.erase(find);
title_found = true;
break;
}
}
}
@ -42,6 +75,8 @@ namespace cfg
TitleRecord title = {};
title.app_id = app_id;
title.title_type = (u32)TitleType::Installed;
title.json_name = recjson;
title.sub_folder = folder;
if(folder.empty()) // Add (move) it to root again
{
@ -62,11 +97,26 @@ namespace cfg
list.folders.push_back(fld);
}
}
SaveRecord(title);
}
return title_found;
}
TitleFolder &FindFolderByName(TitleList &list, std::string name)
{
if(!name.empty())
{
auto f = STLITER_FINDWITHCONDITION(list.folders, fld, (fld.name == name));
if(STLITER_ISFOUND(list.folders, f))
{
return STLITER_UNWRAP(f);
}
}
return list.root;
}
ResultWith<TitleList> LoadTitleList(bool cache)
{
TitleList list = {};
@ -82,24 +132,25 @@ namespace cfg
}
else return MakeResultWith(rc, list);
auto [rc2, menu] = util::LoadJSONFromFile(Q_MENU_JSON);
if(R_SUCCEEDED(rc2))
fs::ForEachFileIn(Q_ENTRIES_PATH, [&](std::string name, std::string path)
{
for(auto &item: menu)
auto [rc, entry] = util::LoadJSONFromFile(path);
if(R_SUCCEEDED(rc))
{
TitleType type = (TitleType)item.value("type", 0u);
TitleType type = (TitleType)entry.value("type", 0u);
if(type == TitleType::Installed)
{
std::string appidstr = item.value("application_id", "");
std::string appidstr = entry.value("application_id", "");
if(!appidstr.empty())
{
std::string folder = item.value("folder", "");
std::string folder = entry.value("folder", "");
u64 appid = util::Get64FromString(appidstr);
if(appid > 0)
{
if(!folder.empty())
{
TitleRecord rec = {};
rec.json_name = name;
rec.app_id = appid;
rec.title_type = (u32)TitleType::Installed;
@ -127,15 +178,16 @@ namespace cfg
}
else if(type == TitleType::Homebrew)
{
std::string nropath = item.value("nro_path", "");
std::string nropath = entry.value("nro_path", "");
if(!nropath.empty())
{
TitleRecord rec = {};
rec.json_name = name;
rec.title_type = (u32)type;
rec.nro_path = "sdmc:";
if(nropath.front() != '/') rec.nro_path += "/";
rec.nro_path += nropath;
std::string folder = item.value("folder", "");
std::string folder = entry.value("folder", "");
rec.sub_folder = folder;
if(cache)
{
@ -194,7 +246,8 @@ namespace cfg
}
}
}
}
});
return SuccessResultWith(list);
}

View file

@ -4,6 +4,7 @@
#include <pu/Plutonium>
#include <ui/ui_SideMenu.hpp>
#include <ui/ui_RawData.hpp>
#include <cfg/cfg_Config.hpp>
namespace ui
{
@ -13,13 +14,21 @@ namespace ui
MenuLayout(void *raw);
PU_SMART_CTOR(MenuLayout)
void menu_Click(u32 index);
void menu_Click(u64 down, u32 index);
void MoveFolder(std::string name, bool fade);
void OnInput(u64 down, u64 up, u64 held, pu::ui::Touch pos);
void SetSuspendedRawData(void *raw);
bool HandleFolderChange(cfg::TitleRecord &rec);
private:
void *susptr;
SideMenu::Ref itemsMenu;
RawData::Ref bgSuspendedRaw;
std::string curfolder;
std::chrono::steady_clock::time_point tp;
bool warnshown;
u32 root_idx;
u32 root_baseidx;
u32 mode;
s32 rawalpha;
};

View file

@ -17,6 +17,7 @@ namespace ui
void LoadMenu();
bool IsTitleSuspended();
bool LaunchFailed();
void SetTitleSuspended(bool suspended);
void SetSelectedUser(u128 user_id);
u128 GetSelectedUser();

View file

@ -1,6 +1,7 @@
#pragma once
#include <pu/Plutonium>
#include <map>
namespace ui
{
@ -11,9 +12,10 @@ namespace ui
static constexpr u32 ItemCount = 4;
static constexpr u32 FocusSize = 15;
static constexpr u32 FocusMargin = 5;
static constexpr u32 CursorSize = ItemSize + (Margin * 2);
public:
SideMenu(pu::ui::Color FocusColor, pu::ui::Color SuspendedColor);
SideMenu(pu::ui::Color SuspendedColor, std::string CursorPath);
PU_SMART_CTOR(SideMenu)
s32 GetX() override;
@ -22,8 +24,7 @@ namespace ui
s32 GetHeight() override;
void OnRender(pu::ui::render::Renderer::Ref &Drawer, s32 X, s32 Y) override;
void OnInput(u64 Down, u64 Up, u64 Held, pu::ui::Touch Touch) override;
void SetOnItemSelected(std::function<void(u32)> Fn);
void SetOnItemFocus(std::function<void(u32)> Fn);
void SetOnItemSelected(std::function<void(u64, u32)> Fn);
void ClearItems();
void AddItem(std::string Icon);
void SetSuspendedItem(u32 Index);
@ -33,20 +34,22 @@ namespace ui
void HandleMoveRight();
int GetSuspendedItem();
u32 GetSelectedItem();
private:
u32 GetBaseItemIndex();
void SetBaseItemIndex(u32 index);
void ClearBorderIcons();
void UpdateBorderIcons();
private:
s32 x;
u32 selitm;
u32 preselitm;
int suspitm;
u32 baseiconidx;
u8 movalpha;
pu::ui::Color selclr;
pu::ui::Color suspclr;
std::vector<std::string> icons;
std::function<void(u32)> onfocus;
std::function<void(u32)> onclick;
std::function<void(u64, u32)> onselect;
std::vector<pu::ui::render::NativeTexture> ricons;
pu::ui::render::NativeTexture cursoricon;
pu::ui::render::NativeTexture leftbicon;
pu::ui::render::NativeTexture rightbicon;
bool IsLeftFirst();

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -26,6 +26,7 @@ namespace qmenu
db::Mount();
fs::CreateDirectory(Q_BASE_DB_DIR);
fs::CreateDirectory(Q_BASE_SD_DIR);
fs::CreateDirectory(Q_ENTRIES_PATH);
fs::CreateDirectory(Q_BASE_SD_DIR "/title");
fs::CreateDirectory(Q_BASE_SD_DIR "/user");
fs::CreateDirectory(Q_BASE_SD_DIR "/nro");
@ -59,23 +60,22 @@ int main()
auto renderer = pu::ui::render::Renderer::New(SDL_INIT_EVERYTHING, pu::ui::render::RendererInitOptions::RendererEverything, pu::ui::render::RendererHardwareFlags);
qapp = ui::QMenuApplication::New(renderer);
u64 susappid = 0;
if(smode == am::QMenuStartMode::MenuApplicationSuspended)
{
am::QMenuCommandWriter writer(am::QDaemonMessage::GetSuspendedApplicationId);
writer.FinishWrite();
am::QMenuCommandResultReader reader;
if(reader) qapp->SetSuspendedApplicationId(reader.Read<u64>());
if(reader) susappid = reader.Read<u64>();
reader.FinishRead();
FILE *f = fopen(Q_BASE_SD_DIR "/temp-suspended.rgba", "rb");
if(f)
{
fread(app_buf, 1, 1280 * 720 * 4, f);
fclose(f);
}
bool flag;
appletGetLastApplicationCaptureImageEx(app_buf, 1280 * 720 * 4, &flag);
}
qapp->SetStartMode(smode);
qapp->SetSuspendedApplicationId(susappid);
qapp->Prepare();
if(smode == am::QMenuStartMode::MenuApplicationSuspended) qapp->Show();

View file

@ -15,76 +15,152 @@ namespace ui
this->susptr = raw;
this->mode = 0;
this->rawalpha = 255;
this->root_idx = 0;
this->warnshown = false;
this->bgSuspendedRaw = RawData::New(0, 0, raw, 1280, 720, 4);
this->Add(this->bgSuspendedRaw);
this->itemsMenu = SideMenu::New(pu::ui::Color(0, 120, 255, 255), pu::ui::Color());
this->itemsMenu = SideMenu::New(pu::ui::Color(0, 255, 120, 255), "romfs:/default/ui/Cursor.png");
this->MoveFolder("", false);
this->itemsMenu->SetOnItemSelected(std::bind(&MenuLayout::menu_Click, this, std::placeholders::_1, std::placeholders::_2));
this->Add(this->itemsMenu);
this->tp = std::chrono::steady_clock::now();
this->SetOnInput(std::bind(&MenuLayout::OnInput, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
}
void MenuLayout::menu_Click(u64 down, u32 index)
{
auto &folder = cfg::FindFolderByName(list, this->curfolder);
if(index == 0)
{
if(down & KEY_A)
{
qapp->CreateShowDialog("A", "All titles...", {"Ok"}, true);
}
}
else
{
u32 realidx = index - 1;
if(realidx < folder.titles.size())
{
auto title = folder.titles[realidx];
if(down & KEY_A)
{
if(!qapp->IsTitleSuspended())
{
if((cfg::TitleType)title.title_type == cfg::TitleType::Homebrew)
{
// TODO: Homebrew launching!
}
else
{
am::QMenuCommandWriter writer(am::QDaemonMessage::LaunchApplication);
writer.Write<u64>(title.app_id);
writer.Write<bool>(false);
writer.FinishWrite();
am::QMenuCommandResultReader reader;
if(reader && R_SUCCEEDED(reader.GetReadResult()))
{
qapp->CloseWithFadeOut();
return;
}
else
{
auto rc = reader.GetReadResult();
qapp->CreateShowDialog("Title launch", "An error ocurred attempting to launch the title:\n" + util::FormatResultDisplay(rc) + " (" + util::FormatResultHex(rc) + ")", { "Ok" }, true);
}
reader.FinishRead();
}
}
else if(qapp->GetSuspendedApplicationId() == title.app_id)
{
// Pressed A on the suspended title - return to it
if(this->mode == 1) this->mode = 2;
}
}
else if(down & KEY_X)
{
if(this->HandleFolderChange(title))
{
this->MoveFolder(this->curfolder, true);
}
}
}
else
{
auto foldr = list.folders[realidx - folder.titles.size()];
if(down & KEY_A)
{
this->MoveFolder(foldr.name, true);
}
}
}
}
void MenuLayout::MoveFolder(std::string name, bool fade)
{
if(fade) qapp->FadeOut();
if(this->curfolder.empty())
{
// Moving from root to a folder, let's save the indexes we were on
this->root_idx = itemsMenu->GetSelectedItem();
this->root_baseidx = itemsMenu->GetBaseItemIndex();
}
auto &folder = cfg::FindFolderByName(list, name);
this->itemsMenu->ClearItems();
// Add first item for all titles menu
this->itemsMenu->AddItem("romfs:/default/ui/AllTitles.png");
for(auto itm: list.root.titles)
u32 tmpidx = 0;
for(auto itm: folder.titles)
{
std::string iconpth;
if((cfg::TitleType)itm.title_type == cfg::TitleType::Installed) iconpth = cfg::GetTitleCacheIconPath(itm.app_id);
else if((cfg::TitleType)itm.title_type == cfg::TitleType::Homebrew) iconpth = cfg::GetNROCacheIconPath(itm.nro_path);
this->itemsMenu->AddItem(iconpth);
if(qapp->GetSuspendedApplicationId() == itm.app_id) this->itemsMenu->SetSuspendedItem(tmpidx + 1); // 1st item is always "all titles"!
tmpidx++;
}
for(auto folder: list.folders)
if(name.empty())
{
// TODO: folder logic
this->itemsMenu->AddItem("romfs:/default/Folder.png");
}
this->itemsMenu->SetOnItemSelected(std::bind(&MenuLayout::menu_Click, this, std::placeholders::_1));
this->Add(this->itemsMenu);
this->SetOnInput(std::bind(&MenuLayout::OnInput, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
}
void MenuLayout::menu_Click(u32 index)
{
if(index == 0)
{
qapp->CreateShowDialog("A", "All titles", {"Ok"}, true);
}
else
{
u32 realidx = index - 1;
if(realidx < list.root.titles.size())
std::vector<cfg::TitleFolder> folders;
for(auto folder: list.folders)
{
auto title = list.root.titles[realidx];
if(!qapp->IsTitleSuspended())
if(!folder.titles.empty())
{
am::QMenuCommandWriter writer(am::QDaemonMessage::LaunchApplication);
writer.Write<u64>(title.app_id);
writer.Write<bool>(false);
writer.FinishWrite();
am::QMenuCommandResultReader reader;
if(reader && R_SUCCEEDED(reader.GetReadResult()))
{
qapp->CloseWithFadeOut();
return;
}
else
{
auto rc = reader.GetReadResult();
qapp->CreateShowDialog("Title launch", "An error ocurred attempting to launch the title:\n" + util::FormatResultDisplay(rc) + " (" + util::FormatResultHex(rc) + ")", { "Ok" }, true);
}
reader.FinishRead();
folders.push_back(folder);
this->itemsMenu->AddItem("romfs:/default/ui/Folder.png");
}
}
else
{
auto folder = list.folders[realidx - list.root.titles.size()];
qapp->CreateShowDialog("Folder", folder.name, {"Ok"}, true);
}
list.folders = folders;
this->itemsMenu->SetSelectedItem(this->root_idx);
this->itemsMenu->SetBaseItemIndex(this->root_baseidx);
}
this->itemsMenu->UpdateBorderIcons();
this->curfolder = name;
if(fade) qapp->FadeIn();
}
void MenuLayout::OnInput(u64 down, u64 up, u64 held, pu::ui::Touch pos)
{
auto ctp = std::chrono::steady_clock::now();
if(std::chrono::duration_cast<std::chrono::milliseconds>(ctp - this->tp).count() >= 500)
{
if(qapp->LaunchFailed() && !this->warnshown)
{
qapp->CreateShowDialog("Title launch", "The title failed to start.\nAre you sure it can be launched? (it isn't deleted, gamecard is inserted...)", {"Ok"}, true);
this->warnshown = true;
}
}
auto [rc, msg] = am::QMenu_GetLatestQMenuMessage();
switch(msg)
{
@ -103,7 +179,6 @@ namespace ui
if(this->susptr != NULL)
{
if(this->mode == 0)
{
if(this->rawalpha == 80) this->mode = 1;
@ -161,5 +236,44 @@ namespace ui
}
}
}
if(down & KEY_B)
{
if(!this->curfolder.empty()) this->MoveFolder("", true);
}
else if(down & KEY_L)
{
qapp->CreateShowDialog("A", "Id -> " + util::FormatApplicationId(qapp->GetSuspendedApplicationId()), {"Ok"}, true);
}
}
bool MenuLayout::HandleFolderChange(cfg::TitleRecord &rec)
{
bool changedone = false;
if(this->curfolder.empty())
{
SwkbdConfig swkbd;
swkbdCreate(&swkbd, 0);
swkbdConfigSetHeaderText(&swkbd, "Select directory name");
char dir[500] = {0};
auto rc = swkbdShow(&swkbd, dir, 500);
if(R_SUCCEEDED(rc))
{
cfg::MoveTitleToDirectory(list, rec.app_id, std::string(dir));
changedone = true;
}
}
else
{
auto sopt = qapp->CreateShowDialog("Title move", "Would you like to move this entry outside the folder?", { "Yes", "Cancel" }, true);
if(sopt == 0)
{
cfg::MoveTitleToDirectory(list, rec.app_id, "");
changedone = true;
}
}
return changedone;
}
}

View file

@ -21,6 +21,9 @@ namespace ui
this->SetTitleSuspended(true);
this->LoadMenu();
break;
case am::QMenuStartMode::MenuLaunchFailure:
this->LoadMenu();
break;
default:
this->LoadLayout(this->startupLayout);
break;
@ -42,6 +45,11 @@ namespace ui
return this->tsuspended;
}
bool QMenuApplication::LaunchFailed()
{
return (this->stmode == am::QMenuStartMode::MenuLaunchFailure);
}
void QMenuApplication::SetTitleSuspended(bool suspended)
{
this->tsuspended = suspended;
@ -63,7 +71,7 @@ namespace ui
void QMenuApplication::SetSuspendedApplicationId(u64 app_id)
{
this->SetTitleSuspended(true);
this->SetTitleSuspended(app_id != 0);
this->suspended_appid = app_id;
}

View file

@ -2,18 +2,17 @@
namespace ui
{
SideMenu::SideMenu(pu::ui::Color FocusColor, pu::ui::Color SuspendedColor)
SideMenu::SideMenu(pu::ui::Color SuspendedColor, std::string CursorPath)
{
this->selitm = 0;
this->suspitm = -1;
this->selclr = FocusColor;
this->suspclr = SuspendedColor;
this->baseiconidx = 0;
this->movalpha = 0;
this->leftbicon = NULL;
this->rightbicon = NULL;
this->onfocus = [&](u32){};
this->onclick = [&](u32){};
this->cursoricon = pu::ui::render::LoadImage(CursorPath);
this->onselect = [&](u32,u64){};
}
s32 SideMenu::GetX()
@ -38,9 +37,9 @@ namespace ui
if(this->icons.empty()) return;
if(this->ricons.empty())
{
for(u32 i = 0; i < std::min((size_t)4, this->icons.size()); i++)
for(u32 i = 0; i < std::min((size_t)4, (this->icons.size() - this->baseiconidx)); i++)
{
auto icon = pu::ui::render::LoadImage(this->icons[i]);
auto icon = pu::ui::render::LoadImage(this->icons[this->baseiconidx + i]);
this->ricons.push_back(icon);
}
this->UpdateBorderIcons();
@ -52,17 +51,24 @@ namespace ui
{
auto ricon = this->ricons[i];
Drawer->RenderTexture(ricon, basex, Y, { -1, ItemSize, ItemSize, -1 });
pu::ui::Color clr = this->selclr;
clr.A = 175 - movalpha;
pu::ui::Color preclr = this->selclr;
preclr.A = movalpha;
if((this->baseiconidx + i) == selitm)
if(this->cursoricon != NULL)
{
Drawer->RenderRectangleFill(clr, basex, Y, ItemSize, ItemSize);
if((this->baseiconidx + i) == selitm)
{
Drawer->RenderTexture(this->cursoricon, basex - Margin, Y - Margin, { 255 - movalpha, CursorSize, CursorSize, -1 });
}
else if((this->baseiconidx + i) == preselitm)
{
Drawer->RenderTexture(this->cursoricon, basex - Margin, Y - Margin, { movalpha, CursorSize, CursorSize, -1 });
}
}
else if((this->baseiconidx + i) == preselitm)
if(this->suspitm >= 0)
{
Drawer->RenderRectangleFill(preclr, basex, Y, ItemSize, ItemSize);
if((this->baseiconidx + i) == (u32)suspitm)
{
Drawer->RenderRectangleFill(this->suspclr, basex, Y + ItemSize + FocusMargin, ItemSize, FocusSize);
}
}
basex += ItemSize + Margin;
}
@ -91,17 +97,12 @@ namespace ui
if(Down & KEY_LEFT) HandleMoveLeft();
else if(Down & KEY_RIGHT) HandleMoveRight();
else if(Down & KEY_A) (this->onclick)(selitm);
else (this->onselect)(Down, this->selitm);
}
void SideMenu::SetOnItemSelected(std::function<void(u32)> Fn)
void SideMenu::SetOnItemSelected(std::function<void(u64, u32)> Fn)
{
this->onclick = Fn;
}
void SideMenu::SetOnItemFocus(std::function<void(u32)> Fn)
{
this->onfocus = Fn;
this->onselect = Fn;
}
void SideMenu::ClearItems()
@ -110,7 +111,9 @@ namespace ui
for(auto &ricon: this->ricons) pu::ui::render::DeleteTexture(ricon);
this->ricons.clear();
this->selitm = 0;
this->baseiconidx = 0;
this->suspitm = -1;
this->ClearBorderIcons();
}
void SideMenu::AddItem(std::string Icon)
@ -141,8 +144,7 @@ namespace ui
preselitm = selitm;
selitm--;
if(ilf) ReloadIcons(1);
else movalpha = 175;
(this->onfocus)(selitm);
else movalpha = 255;
}
}
@ -154,8 +156,7 @@ namespace ui
preselitm = selitm;
selitm++;
if(irl) ReloadIcons(2);
else movalpha = 175;
(this->onfocus)(selitm);
else movalpha = 255;
}
}
@ -224,16 +225,31 @@ namespace ui
this->UpdateBorderIcons();
}
void SideMenu::UpdateBorderIcons()
u32 SideMenu::GetBaseItemIndex()
{
return this->baseiconidx;
}
void SideMenu::SetBaseItemIndex(u32 index)
{
this->baseiconidx = index;
}
void SideMenu::ClearBorderIcons()
{
if(leftbicon != NULL) pu::ui::render::DeleteTexture(leftbicon);
leftbicon = NULL;
if(rightbicon != NULL) pu::ui::render::DeleteTexture(rightbicon);
rightbicon = NULL;
}
void SideMenu::UpdateBorderIcons()
{
this->ClearBorderIcons();
if(baseiconidx > 0)
{
leftbicon = pu::ui::render::LoadImage(icons[baseiconidx - 1]);
}
if(rightbicon != NULL) pu::ui::render::DeleteTexture(rightbicon);
rightbicon = NULL;
if((baseiconidx + 4) < icons.size())
{
rightbicon = pu::ui::render::LoadImage(icons[baseiconidx + 4]);

View file

@ -85,15 +85,6 @@ void HandleAppletMessage()
{
if(am::ApplicationHasForeground())
{
bool flag;
appletUpdateLastForegroundCaptureImage();
appletGetLastForegroundCaptureImageEx(app_buf, 1280 * 720 * 4, &flag);
FILE *f = fopen(Q_BASE_SD_DIR "/temp-suspended.rgba", "wb");
if(f)
{
fwrite(app_buf, 1, 1280 * 720 * 4, f);
fclose(f);
}
am::HomeMenuSetForeground();
am::QDaemon_LaunchQMenu(am::QMenuStartMode::MenuApplicationSuspended);
used_to_reopen_menu = true;
@ -307,7 +298,12 @@ int main()
if(!am::LibraryAppletIsActive())
{
auto rc = am::ApplicationStart(titlelaunch_flag, titlelaunch_system, selected_uid);
if(R_FAILED(rc)) am::QDaemon_LaunchQMenu(am::QMenuStartMode::StartupScreen);
svcSleepThread(500'000'000);
if(!am::ApplicationIsActive())
{
// Title failed to launch, so we re-launch QMenu this way...
am::QDaemon_LaunchQMenu(am::QMenuStartMode::MenuLaunchFailure);
}
titlelaunch_flag = 0;
}
}

View file

@ -78,8 +78,10 @@ Can be customized via files in `/ui`.
- `ui/Font.ttf` -> TTF font used for all the UI.
- `ui/AllTitles.png` -> 256x256 icon for the "all titles" entry in the main menu.
- `ui/AllTitles.png` -> 256x256 PNG for the "all titles" entry in the main menu.
- `ui/Folder.png` -> 256x256 icon for folders in the main menu.
- `ui/Folder.png` -> 256x256 PNG for folders in the main menu.
- `ui/Cursor.png` -> 296x296 PNG for the cursor pointing at items in the main menu.
> *TODO: more customizable stuff*