uLaunch/uMenu/source/ui/ui_MenuLayout.cpp
2020-02-04 19:46:09 +01:00

1205 lines
No EOL
59 KiB
C++

#include <ui/ui_MenuLayout.hpp>
#include <os/os_Titles.hpp>
#include <os/os_Account.hpp>
#include <util/util_Convert.hpp>
#include <util/util_Misc.hpp>
#include <os/os_Misc.hpp>
#include <am/am_LibraryApplet.hpp>
#include <ui/ui_MenuApplication.hpp>
#include <os/os_HomeMenu.hpp>
#include <fs/fs_Stdio.hpp>
#include <net/net_Service.hpp>
extern ui::MenuApplication::Ref qapp;
extern cfg::TitleList list;
extern std::vector<cfg::TitleRecord> homebrew;
extern cfg::Config config;
extern cfg::Theme theme;
#define MENU_HBMENU_NRO "sdmc:/hbmenu.nro"
namespace ui
{
MenuLayout::MenuLayout(void *raw, u8 min_alpha)
{
this->susptr = raw;
this->mode = 0;
this->rawalpha = 255;
this->last_hasconn = false;
this->last_batterylvl = 0;
this->last_charge = false;
this->warnshown = false;
this->minalpha = min_alpha;
this->homebrew_mode = false;
this->select_on = false;
this->select_dir = false;
pu::ui::Color textclr = pu::ui::Color::FromHex(qapp->GetUIConfigValue<std::string>("text_color", "#e1e1e1ff"));
u32 menutextx = qapp->GetUIConfigValue<u32>("menu_folder_text_x", 30);
u32 menutexty = qapp->GetUIConfigValue<u32>("menu_folder_text_y", 200);
u32 menutextsz = qapp->GetUIConfigValue<u32>("menu_folder_text_size", 25);
this->bgSuspendedRaw = RawData::New(0, 0, raw, 1280, 720, 4);
this->Add(this->bgSuspendedRaw);
// Load banners first
this->topMenuImage = pu::ui::elm::Image::New(40, 35, cfg::GetAssetByTheme(theme, "ui/TopMenu.png"));
qapp->ApplyConfigForElement("main_menu", "top_menu_bg", this->topMenuImage);
this->Add(this->topMenuImage);
this->bannerImage = pu::ui::elm::Image::New(0, 585, cfg::GetAssetByTheme(theme, "ui/BannerInstalled.png"));
qapp->ApplyConfigForElement("main_menu", "banner_image", this->bannerImage);
this->Add(this->bannerImage);
// Then load buttons and other UI elements
this->logo = ClickableImage::New(610, 13 + 35, "romfs:/Logo.png");
this->logo->SetWidth(60);
this->logo->SetHeight(60);
this->logo->SetOnClick(std::bind(&MenuLayout::logo_Click, this));
qapp->ApplyConfigForElement("main_menu", "logo_icon", this->logo, false); // Sorry theme makers... logo must be visible, but can be moved
this->Add(this->logo);
this->connIcon = pu::ui::elm::Image::New(80, 53, cfg::GetAssetByTheme(theme, "ui/NoConnectionIcon.png"));
qapp->ApplyConfigForElement("main_menu", "connection_icon", this->connIcon);
this->Add(this->connIcon);
this->users = ClickableImage::New(270, 53, ""); // On layout creation, no user is still selected...
this->users->SetOnClick(std::bind(&MenuLayout::users_Click, this));
qapp->ApplyConfigForElement("main_menu", "user_icon", this->users);
this->Add(this->users);
this->controller = ClickableImage::New(340, 53, cfg::GetAssetByTheme(theme, "ui/ControllerIcon.png"));
this->controller->SetOnClick(std::bind(&MenuLayout::controller_Click, this));
qapp->ApplyConfigForElement("main_menu", "controller_icon", this->controller);
this->Add(this->controller);
auto curtime = util::GetCurrentTime();
this->timeText = pu::ui::elm::TextBlock::New(515, 68, curtime);
this->timeText->SetColor(textclr);
qapp->ApplyConfigForElement("main_menu", "time_text", this->timeText);
this->Add(this->timeText);
auto lvl = util::GetBatteryLevel();
auto lvlstr = std::to_string(lvl) + "%";
this->batteryText = pu::ui::elm::TextBlock::New(700, 55, lvlstr);
this->batteryText->SetColor(textclr);
qapp->ApplyConfigForElement("main_menu", "battery_text", this->batteryText);
this->Add(this->batteryText);
this->batteryIcon = pu::ui::elm::Image::New(700, 80, cfg::GetAssetByTheme(theme, "ui/BatteryNormalIcon.png"));
qapp->ApplyConfigForElement("main_menu", "battery_icon", this->batteryIcon);
this->Add(this->batteryIcon);
this->settings = ClickableImage::New(880, 53, cfg::GetAssetByTheme(theme, "ui/SettingsIcon.png"));
this->settings->SetOnClick(std::bind(&MenuLayout::settings_Click, this));
qapp->ApplyConfigForElement("main_menu", "settings_icon", this->settings);
this->Add(this->settings);
this->themes = ClickableImage::New(950, 53, cfg::GetAssetByTheme(theme, "ui/ThemesIcon.png"));
this->themes->SetOnClick(std::bind(&MenuLayout::themes_Click, this));
qapp->ApplyConfigForElement("main_menu", "themes_icon", this->themes);
this->Add(this->themes);
this->fwText = pu::ui::elm::TextBlock::New(1140, 68, os::GetFirmwareVersion());
this->fwText->SetColor(textclr);
qapp->ApplyConfigForElement("main_menu", "firmware_text", this->fwText);
this->Add(this->fwText);
this->menuToggle = ClickableImage::New(520, 200, cfg::GetAssetByTheme(theme, "ui/ToggleClick.png"));
this->menuToggle->SetOnClick(std::bind(&MenuLayout::menuToggle_Click, this));
qapp->ApplyConfigForElement("main_menu", "menu_toggle_button", this->menuToggle);
this->Add(this->menuToggle);
this->itemName = pu::ui::elm::TextBlock::New(40, 610, "", 30);
this->itemName->SetColor(textclr);
qapp->ApplyConfigForElement("main_menu", "banner_name_text", this->itemName);
this->Add(this->itemName);
this->itemAuthor = pu::ui::elm::TextBlock::New(45, 650, "", 20);
this->itemAuthor->SetColor(textclr);
qapp->ApplyConfigForElement("main_menu", "banner_author_text", this->itemAuthor);
this->Add(this->itemAuthor);
this->itemVersion = pu::ui::elm::TextBlock::New(45, 675, "", 20);
this->itemVersion->SetColor(textclr);
qapp->ApplyConfigForElement("main_menu", "banner_version_text", this->itemVersion);
this->Add(this->itemVersion);
this->itemsMenu = SideMenu::New(pu::ui::Color(0, 255, 120, 255), cfg::GetAssetByTheme(theme, "ui/Cursor.png"), cfg::GetAssetByTheme(theme, "ui/Suspended.png"), cfg::GetAssetByTheme(theme, "ui/Multiselect.png"), menutextx, menutexty, menutextsz, textclr, 294);
this->MoveFolder("", false);
this->itemsMenu->SetOnItemSelected(std::bind(&MenuLayout::menu_Click, this, std::placeholders::_1, std::placeholders::_2));
this->itemsMenu->SetOnSelectionChanged(std::bind(&MenuLayout::menu_OnSelected, this, std::placeholders::_1));
qapp->ApplyConfigForElement("main_menu", "items_menu", this->itemsMenu, false); // Main menu must be visible, and only Y is customizable here
this->Add(this->itemsMenu);
this->quickMenu = QuickMenu::New(cfg::GetAssetByTheme(theme, "ui/QuickMenuMain.png"));
this->quickMenu->SetEntry(QuickMenuDirection::Up, cfg::GetAssetByTheme(theme, "ui/UserIcon.png"), std::bind(&MenuLayout::users_Click, this));
this->quickMenu->SetEntry(QuickMenuDirection::UpLeft, cfg::GetAssetByTheme(theme, "ui/PowerIcon.png"), std::bind(&MenuLayout::HandlePowerDialog, this));
this->quickMenu->SetEntry(QuickMenuDirection::UpRight, cfg::GetAssetByTheme(theme, "ui/SettingsIcon.png"), std::bind(&MenuLayout::settings_Click, this));
this->quickMenu->SetEntry(QuickMenuDirection::Left, cfg::GetAssetByTheme(theme, "ui/ControllerIcon.png"), std::bind(&MenuLayout::controller_Click, this));
this->quickMenu->SetEntry(QuickMenuDirection::Right, cfg::GetAssetByTheme(theme, "ui/ThemesIcon.png"), std::bind(&MenuLayout::themes_Click, this));
this->quickMenu->SetEntry(QuickMenuDirection::DownLeft, cfg::GetAssetByTheme(theme, "ui/WebIcon.png"), std::bind(&MenuLayout::HandleWebPageOpen, this));
this->quickMenu->SetEntry(QuickMenuDirection::DownRight, cfg::GetAssetByTheme(theme, "ui/AlbumIcon.png"), std::bind(&MenuLayout::HandleOpenAlbum, this));
this->quickMenu->SetEntry(QuickMenuDirection::Down, cfg::GetAssetByTheme(theme, "ui/HelpIcon.png"), std::bind(&MenuLayout::HandleShowHelp, this));
this->Add(this->quickMenu);
this->tp = std::chrono::steady_clock::now();
this->sfxTitleLaunch = pu::audio::Load(cfg::GetAssetByTheme(theme, "sound/TitleLaunch.wav"));
this->sfxMenuToggle = pu::audio::Load(cfg::GetAssetByTheme(theme, "sound/MenuToggle.wav"));
this->SetBackgroundImage(cfg::GetAssetByTheme(theme, "ui/Background.png"));
this->SetOnInput(std::bind(&MenuLayout::OnInput, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4));
}
MenuLayout::~MenuLayout()
{
pu::audio::DeleteSfx(this->sfxTitleLaunch);
pu::audio::DeleteSfx(this->sfxMenuToggle);
}
void MenuLayout::menu_Click(u64 down, u32 index)
{
if((this->select_on) && (down & KEY_A))
{
if(!this->itemsMenu->IsAnyMultiselected()) this->StopMultiselect();
}
if(this->select_on)
{
if(select_dir)
{
if((down & KEY_A) || (down & KEY_Y))
{
if((!this->homebrew_mode) && this->curfolder.empty())
{
if(index < list.folders.size())
{
auto &folder = list.folders[index];
auto sopt = qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "menu_multiselect"), cfg::GetLanguageString(config.main_lang, config.default_lang, "menu_move_existing_folder_conf"), { cfg::GetLanguageString(config.main_lang, config.default_lang, "yes"), cfg::GetLanguageString(config.main_lang, config.default_lang, "no"), cfg::GetLanguageString(config.main_lang, config.default_lang, "cancel") }, true);
if(sopt == 0) this->HandleMultiselectMoveToFolder(folder.name);
else if(sopt == 1) this->StopMultiselect();
}
}
}
else if(down & KEY_B)
{
this->select_dir = false;
qapp->ShowNotification(cfg::GetLanguageString(config.main_lang, config.default_lang, "menu_move_select_folder_cancel"));
}
}
else
{
if(down & KEY_B)
{
qapp->ShowNotification(cfg::GetLanguageString(config.main_lang, config.default_lang, "menu_multiselect_cancel"));
this->StopMultiselect();
}
else if(down & KEY_Y)
{
bool selectable = false;
if(this->homebrew_mode) selectable = true;
else
{
if(this->curfolder.empty()) selectable = (index >= list.folders.size());
else selectable = true;
}
if(selectable)
{
this->itemsMenu->SetItemMultiselected(index, !this->itemsMenu->IsItemMultiselected(index));
}
}
else if(down & KEY_A)
{
if(this->homebrew_mode)
{
auto sopt = qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "menu_multiselect"), cfg::GetLanguageString(config.main_lang, config.default_lang, "hb_mode_entries_add"), { cfg::GetLanguageString(config.main_lang, config.default_lang, "yes"), cfg::GetLanguageString(config.main_lang, config.default_lang, "no"), cfg::GetLanguageString(config.main_lang, config.default_lang, "cancel") }, true);
if(sopt == 0)
{
// Get the idx of the last homebrew element.
s32 hbidx = 0;
for(auto &entry: list.root.titles)
{
if((cfg::TitleType)entry.title_type == cfg::TitleType::Installed) break;
hbidx++;
}
if(hbidx < 0) hbidx = 0;
bool any = false;
for(u32 i = 0; i < homebrew.size(); i++)
{
auto &hb = homebrew[i];
u32 idx = i + 1;
if(this->itemsMenu->IsItemMultiselected(idx))
{
if(!cfg::ExistsRecord(list, hb))
{
cfg::SaveRecord(hb);
list.root.titles.insert(list.root.titles.begin() + hbidx, hb);
any = true;
hbidx++;
}
}
}
if(any) qapp->ShowNotification(cfg::GetLanguageString(config.main_lang, config.default_lang, "hb_mode_entries_added"));
this->StopMultiselect();
}
else if(sopt == 1) this->StopMultiselect();
}
else if(this->curfolder.empty())
{
auto sopt = qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "menu_multiselect"), cfg::GetLanguageString(config.main_lang, config.default_lang, "menu_move_to_folder"), { cfg::GetLanguageString(config.main_lang, config.default_lang, "menu_move_new_folder"), cfg::GetLanguageString(config.main_lang, config.default_lang, "menu_move_existing_folder"), cfg::GetLanguageString(config.main_lang, config.default_lang, "no"), cfg::GetLanguageString(config.main_lang, config.default_lang, "cancel") }, true);
if(sopt == 0)
{
SwkbdConfig swkbd;
swkbdCreate(&swkbd, 0);
swkbdConfigSetGuideText(&swkbd, cfg::GetLanguageString(config.main_lang, config.default_lang, "swkbd_new_folder_guide").c_str());
char dir[500] = {0};
auto rc = swkbdShow(&swkbd, dir, 500);
swkbdClose(&swkbd);
if(R_SUCCEEDED(rc)) this->HandleMultiselectMoveToFolder(dir);
}
else if(sopt == 1)
{
this->select_dir = true;
qapp->ShowNotification(cfg::GetLanguageString(config.main_lang, config.default_lang, "menu_move_select_folder"));
}
else if(sopt == 2) this->StopMultiselect();
}
else
{
auto sopt = qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "menu_multiselect"), cfg::GetLanguageString(config.main_lang, config.default_lang, "menu_move_from_folder"), { cfg::GetLanguageString(config.main_lang, config.default_lang, "yes"), cfg::GetLanguageString(config.main_lang, config.default_lang, "no"), cfg::GetLanguageString(config.main_lang, config.default_lang, "cancel") }, true);
if(sopt == 0)
{
u32 rmvd = 0;
auto &folder = cfg::FindFolderByName(list, this->curfolder);
auto basesz = folder.titles.size();
for(u32 i = 0; i < basesz; i++)
{
auto &title = folder.titles[i - rmvd];
if(this->itemsMenu->IsItemMultiselected(i))
{
if(cfg::MoveRecordTo(list, title, ""))
{
rmvd++;
}
}
}
this->StopMultiselect();
this->MoveFolder(folder.titles.empty() ? "" : this->curfolder, true);
}
else if(sopt == 1) this->StopMultiselect();
}
}
}
}
else
{
if((index == 0) && this->homebrew_mode)
{
if(down & KEY_A)
{
pu::audio::Play(this->sfxTitleLaunch);
am::MenuCommandWriter writer(am::DaemonMessage::LaunchHomebrewLibraryApplet);
hb::HbTargetParams ipt = {};
strcpy(ipt.nro_path, MENU_HBMENU_NRO); // Launch normal hbmenu
strcpy(ipt.nro_argv, MENU_HBMENU_NRO);
writer.Write<hb::HbTargetParams>(ipt);
writer.FinishWrite();
qapp->StopPlayBGM();
qapp->CloseWithFadeOut();
return;
}
}
else
{
u32 realidx = index;
if(this->homebrew_mode) realidx--;
if(this->homebrew_mode)
{
auto hb = homebrew[realidx];
if(down & KEY_A)
{
bool hblaunch = true;
if(qapp->IsHomebrewSuspended())
{
if(qapp->EqualsSuspendedHomebrewPath(hb.nro_target.nro_path))
{
if(this->mode == 1) this->mode = 2;
hblaunch = false;
}
else
{
hblaunch = false;
this->HandleCloseSuspended();
hblaunch = !qapp->IsHomebrewSuspended();
}
}
if(hblaunch) this->HandleHomebrewLaunch(hb);
}
else if(down & KEY_X)
{
if(qapp->IsSuspended())
{
if(qapp->EqualsSuspendedHomebrewPath(hb.nro_target.nro_path)) this->HandleCloseSuspended();
}
}
else if(down & KEY_Y)
{
if(!this->select_on) this->select_on = true;
this->itemsMenu->SetItemMultiselected(this->itemsMenu->GetSelectedItem(), true);
}
}
else
{
auto &folder = cfg::FindFolderByName(list, this->curfolder);
s32 titleidx = realidx;
if(this->curfolder.empty())
{
if(realidx >= list.folders.size())
{
titleidx -= list.folders.size();
}
else
{
auto foldr = list.folders[realidx];
if(down & KEY_A)
{
this->MoveFolder(foldr.name, true);
}
else if(down & KEY_Y)
{
auto sopt = qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "menu_rename_folder"), cfg::GetLanguageString(config.main_lang, config.default_lang, "menu_rename_folder_conf"), { cfg::GetLanguageString(config.main_lang, config.default_lang, "yes"), cfg::GetLanguageString(config.main_lang, config.default_lang, "no") }, true);
if(sopt == 0)
{
SwkbdConfig swkbd;
swkbdCreate(&swkbd, 0);
swkbdConfigSetGuideText(&swkbd, cfg::GetLanguageString(config.main_lang, config.default_lang, "swkbd_rename_folder_guide").c_str());
char dir[500] = {0};
auto rc = swkbdShow(&swkbd, dir, 500);
swkbdClose(&swkbd);
if(R_SUCCEEDED(rc))
{
cfg::RenameFolder(list, foldr.name, dir);
this->MoveFolder(this->curfolder, true);
}
}
}
titleidx = -1;
}
}
if(titleidx >= 0)
{
auto title = folder.titles[titleidx];
if(down & KEY_A)
{
bool titlelaunch = true;
if(qapp->IsSuspended())
{
if((cfg::TitleType)title.title_type == cfg::TitleType::Homebrew)
{
if(qapp->EqualsSuspendedHomebrewPath(title.nro_target.nro_path))
{
if(this->mode == 1) this->mode = 2;
titlelaunch = false;
}
}
else if((cfg::TitleType)title.title_type == cfg::TitleType::Installed)
{
if(title.app_id == qapp->GetSuspendedApplicationId())
{
if(this->mode == 1) this->mode = 2;
titlelaunch = false;
}
}
if(titlelaunch)
{
// Homebrew launching code already does this checks later - doing this check only with installed titles
if((cfg::TitleType)title.title_type == cfg::TitleType::Installed)
{
titlelaunch = false;
this->HandleCloseSuspended();
titlelaunch = !qapp->IsSuspended();
}
}
}
if(titlelaunch)
{
if((cfg::TitleType)title.title_type == cfg::TitleType::Homebrew) this->HandleHomebrewLaunch(title);
else
{
pu::audio::Play(this->sfxTitleLaunch);
am::MenuCommandWriter writer(am::DaemonMessage::LaunchApplication);
writer.Write<u64>(title.app_id);
writer.FinishWrite();
am::MenuCommandResultReader reader;
if(reader && R_SUCCEEDED(reader.GetReadResult()))
{
qapp->StopPlayBGM();
qapp->CloseWithFadeOut();
return;
}
else
{
auto rc = reader.GetReadResult();
qapp->ShowNotification(cfg::GetLanguageString(config.main_lang, config.default_lang, "app_launch_error") + ": " + util::FormatResult(rc));
}
reader.FinishRead();
}
}
}
else if(down & KEY_X)
{
if(qapp->IsSuspended())
{
if((cfg::TitleType)title.title_type == cfg::TitleType::Homebrew)
{
if(qapp->EqualsSuspendedHomebrewPath(title.nro_target.nro_path)) this->HandleCloseSuspended();
}
else
{
if(title.app_id == qapp->GetSuspendedApplicationId()) this->HandleCloseSuspended();
}
}
}
else if(down & KEY_Y)
{
if((cfg::TitleType)title.title_type == cfg::TitleType::Homebrew)
{
auto sopt = qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "entry_options"), cfg::GetLanguageString(config.main_lang, config.default_lang, "entry_action"), { cfg::GetLanguageString(config.main_lang, config.default_lang, "entry_move"), cfg::GetLanguageString(config.main_lang, config.default_lang, "entry_remove"), cfg::GetLanguageString(config.main_lang, config.default_lang, "cancel") }, true);
if(sopt == 0)
{
if(!this->select_on) this->select_on = true;
this->itemsMenu->SetItemMultiselected(this->itemsMenu->GetSelectedItem(), true);
}
else if(sopt == 1)
{
auto sopt2 = qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "entry_remove"), cfg::GetLanguageString(config.main_lang, config.default_lang, "entry_remove_conf"), { cfg::GetLanguageString(config.main_lang, config.default_lang, "yes"), cfg::GetLanguageString(config.main_lang, config.default_lang, "no") }, true);
if(sopt2 == 0)
{
cfg::RemoveRecord(title);
folder.titles.erase(folder.titles.begin() + titleidx);
qapp->ShowNotification(cfg::GetLanguageString(config.main_lang, config.default_lang, "entry_remove_ok"));
this->MoveFolder(this->curfolder, true);
}
}
}
else
{
if(!this->select_on) this->select_on = true;
this->itemsMenu->SetItemMultiselected(this->itemsMenu->GetSelectedItem(), true);
}
}
else if(down & KEY_UP)
{
if((cfg::TitleType)title.title_type == cfg::TitleType::Installed)
{
auto sopt = qapp->CreateShowDialog("Homebrew title take-over", "Would you like to select this title for homebrew launching?\nIf selected, homebrew will be launched as an application over this title.", { "Yes", "Cancel" }, true);
if(sopt == 0)
{
config.homebrew_title_application_id = title.app_id;
cfg::SaveConfig(config);
qapp->ShowNotification("Done");
}
}
}
}
}
}
}
}
void MenuLayout::menu_OnSelected(u32 index)
{
this->itemAuthor->SetVisible(true);
this->itemVersion->SetVisible(true);
u32 realidx = index;
if(this->homebrew_mode)
{
if(index == 0)
{
this->itemAuthor->SetVisible(false);
this->itemVersion->SetVisible(false);
this->bannerImage->SetImage(cfg::GetAssetByTheme(theme, "ui/BannerHomebrew.png"));
this->itemName->SetText(cfg::GetLanguageString(config.main_lang, config.default_lang, "hbmenu_launch"));
}
else
{
realidx--;
auto hb = homebrew[realidx];
auto info = cfg::GetRecordInformation(hb);
if(info.strings.name.empty()) this->itemName->SetText(cfg::GetLanguageString(config.main_lang, config.default_lang, "unknown"));
else this->itemName->SetText(info.strings.name);
if(info.strings.author.empty()) this->itemAuthor->SetText(cfg::GetLanguageString(config.main_lang, config.default_lang, "unknown"));
else this->itemAuthor->SetText(info.strings.author);
if(info.strings.version.empty()) this->itemVersion->SetText("0");
else this->itemVersion->SetText(info.strings.version);
this->bannerImage->SetImage(cfg::GetAssetByTheme(theme, "ui/BannerHomebrew.png"));
}
}
else
{
auto &folder = cfg::FindFolderByName(list, this->curfolder);
s32 titleidx = realidx;
if(this->curfolder.empty())
{
if(realidx >= list.folders.size())
{
titleidx -= list.folders.size();
}
else
{
auto foldr = list.folders[realidx];
this->bannerImage->SetImage(cfg::GetAssetByTheme(theme, "ui/BannerFolder.png"));
auto sz = foldr.titles.size();
this->itemAuthor->SetText(std::to_string(sz) + " " + ((sz == 1) ? cfg::GetLanguageString(config.main_lang, config.default_lang, "folder_entry_single") : cfg::GetLanguageString(config.main_lang, config.default_lang, "folder_entry_mult")));
this->itemVersion->SetVisible(false);
this->itemName->SetText(foldr.name);
titleidx = -1;
}
}
if(titleidx >= 0)
{
auto title = folder.titles[titleidx];
auto info = cfg::GetRecordInformation(title);
if(info.strings.name.empty()) this->itemName->SetText(cfg::GetLanguageString(config.main_lang, config.default_lang, "unknown"));
else this->itemName->SetText(info.strings.name);
if(info.strings.author.empty()) this->itemAuthor->SetText(cfg::GetLanguageString(config.main_lang, config.default_lang, "unknown"));
else this->itemAuthor->SetText(info.strings.author);
if(info.strings.version.empty()) this->itemVersion->SetText("0");
else this->itemVersion->SetText(info.strings.version);
if((cfg::TitleType)title.title_type == cfg::TitleType::Homebrew) this->bannerImage->SetImage(cfg::GetAssetByTheme(theme, "ui/BannerHomebrew.png"));
else this->bannerImage->SetImage(cfg::GetAssetByTheme(theme, "ui/BannerInstalled.png"));
}
if(!this->curfolder.empty()) this->bannerImage->SetImage(cfg::GetAssetByTheme(theme, "ui/BannerFolder.png")); // This way user always knows he's inside a folder
}
}
void MenuLayout::MoveFolder(std::string name, bool fade)
{
if(fade) qapp->FadeOut();
if(this->homebrew_mode)
{
if(homebrew.empty()) homebrew = cfg::QueryAllHomebrew();
}
auto itm_list = homebrew;
if(!this->homebrew_mode)
{
auto &folder = cfg::FindFolderByName(list, name);
itm_list = folder.titles;
}
this->itemsMenu->ClearItems();
if(this->homebrew_mode) this->itemsMenu->AddItem(cfg::GetAssetByTheme(theme, "ui/Hbmenu.png"));
else
{
if(name.empty())
{
STL_REMOVE_IF(list.folders, fldr, (fldr.titles.empty())) // Remove empty folders
for(auto folder: list.folders) this->itemsMenu->AddItem(cfg::GetAssetByTheme(theme, "ui/Folder.png"), folder.name);
}
}
u32 tmpidx = 0;
for(auto itm: itm_list)
{
bool set_susp = false;
if((cfg::TitleType)itm.title_type == cfg::TitleType::Installed)
{
if(qapp->IsTitleSuspended()) if(qapp->GetSuspendedApplicationId() == itm.app_id) set_susp = true;
}
else
{
if(qapp->IsHomebrewSuspended()) if(qapp->EqualsSuspendedHomebrewPath(itm.nro_target.nro_path)) set_susp = true;
}
this->itemsMenu->AddItem(cfg::GetRecordIconPath(itm));
if(set_susp)
{
u32 suspidx = tmpidx;
if(this->homebrew_mode) suspidx++; // Skip initial item if homebrew mode
else if(name.empty()) suspidx += list.folders.size(); // Ignore front folders from main menu
this->itemsMenu->SetSuspendedItem(suspidx);
}
tmpidx++;
}
this->itemsMenu->UpdateBorderIcons();
if(!this->homebrew_mode) this->curfolder = name;
if(fade) qapp->FadeIn();
}
void MenuLayout::OnInput(u64 down, u64 up, u64 held, pu::ui::Touch pos)
{
qapp->CommonMenuOnLoop();
auto quickon = this->quickMenu->IsOn();
this->itemsMenu->SetEnabled(!quickon);
if(quickon) return;
bool hasconn = net::HasConnection();
if(this->last_hasconn != hasconn)
{
if(hasconn) this->connIcon->SetImage(cfg::GetAssetByTheme(theme, "ui/ConnectionIcon.png"));
else this->connIcon->SetImage(cfg::GetAssetByTheme(theme, "ui/NoConnectionIcon.png"));
this->last_hasconn = hasconn;
}
auto curtime = util::GetCurrentTime();
this->timeText->SetText(curtime);
auto lvl = util::GetBatteryLevel();
if(this->last_batterylvl != lvl)
{
this->last_batterylvl = lvl;
auto lvlstr = std::to_string(lvl) + "%";
this->batteryText->SetText(lvlstr);
}
bool ch = util::IsCharging();
if(this->last_charge != ch)
{
this->last_charge = ch;
if(ch) this->batteryIcon->SetImage(cfg::GetAssetByTheme(theme, "ui/BatteryChargingIcon.png"));
else this->batteryIcon->SetImage(cfg::GetAssetByTheme(theme, "ui/BatteryNormalIcon.png"));
}
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(cfg::GetLanguageString(config.main_lang, config.default_lang, "app_launch"), cfg::GetLanguageString(config.main_lang, config.default_lang, "app_unexpected_error"), { cfg::GetLanguageString(config.main_lang, config.default_lang, "ok") }, true);
this->warnshown = true;
}
}
if(am::MenuIsHomePressed())
{
if(qapp->IsSuspended())
{
if(this->mode == 1) this->mode = 2;
}
else while(this->itemsMenu->GetSelectedItem() > 0) this->itemsMenu->HandleMoveLeft();
}
if(this->susptr != NULL)
{
if(this->mode == 0)
{
if(this->rawalpha == this->minalpha)
{
this->bgSuspendedRaw->SetAlphaFactor(this->rawalpha);
this->mode = 1;
}
else
{
this->bgSuspendedRaw->SetAlphaFactor(this->rawalpha);
this->rawalpha -= 10;
if(this->rawalpha < this->minalpha) this->rawalpha = this->minalpha;
else
{
s32 cw = this->bgSuspendedRaw->GetWidth();
s32 ch = this->bgSuspendedRaw->GetHeight();
// 16:9 ratio
cw -= 16;
ch -= 9;
s32 x = (1280 - cw) / 2;
s32 y = (720 - ch) / 2;
this->bgSuspendedRaw->SetX(x);
this->bgSuspendedRaw->SetY(y);
this->bgSuspendedRaw->SetWidth(cw);
this->bgSuspendedRaw->SetHeight(ch);
}
}
}
else if(this->mode == 2)
{
if(this->rawalpha == 255)
{
this->bgSuspendedRaw->SetAlphaFactor(this->rawalpha);
am::MenuCommandWriter writer(am::DaemonMessage::ResumeApplication);
writer.FinishWrite();
am::MenuCommandResultReader reader;
reader.FinishRead();
}
else
{
this->bgSuspendedRaw->SetAlphaFactor(this->rawalpha);
this->rawalpha += 10;
if(this->rawalpha > 255) this->rawalpha = 255;
else
{
s32 cw = this->bgSuspendedRaw->GetWidth();
s32 ch = this->bgSuspendedRaw->GetHeight();
// 16:9 ratio
cw += 16;
ch += 9;
s32 x = (1280 - cw) / 2;
s32 y = (720 - ch) / 2;
this->bgSuspendedRaw->SetX(x);
this->bgSuspendedRaw->SetY(y);
this->bgSuspendedRaw->SetWidth(cw);
this->bgSuspendedRaw->SetHeight(ch);
}
}
}
}
if(down & KEY_B)
{
if(!this->curfolder.empty() && !this->homebrew_mode) this->MoveFolder("", true);
}
else if(down & KEY_PLUS) this->logo_Click();
else if(down & KEY_MINUS) this->menuToggle_Click();
else if((down & KEY_L) || (down & KEY_R) || (down & KEY_ZL) || (down & KEY_ZR) || (down & KEY_LSTICK) || (down & KEY_RSTICK)) this->quickMenu->Toggle();
}
void MenuLayout::SetUser(AccountUid user)
{
auto path = os::GetIconCacheImagePath(user);
this->users->SetImage(path);
this->users->SetWidth(50);
this->users->SetHeight(50);
}
void MenuLayout::menuToggle_Click()
{
pu::audio::Play(this->sfxMenuToggle);
this->homebrew_mode = !this->homebrew_mode;
if(this->select_on)
{
qapp->ShowNotification(cfg::GetLanguageString(config.main_lang, config.default_lang, "menu_multiselect_cancel"));
this->StopMultiselect();
}
this->MoveFolder("", true);
}
void MenuLayout::logo_Click()
{
qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "ulaunch_about"), "uLaunch v" + std::string(UL_VERSION) + "\n\n" + cfg::GetLanguageString(config.main_lang, config.default_lang, "ulaunch_desc") + ":\nhttps://github.com/XorTroll/uLaunch", { cfg::GetLanguageString(config.main_lang, config.default_lang, "ok") }, true, "romfs:/LogoLarge.png");
}
void MenuLayout::settings_Click()
{
this->HandleSettingsMenu();
}
void MenuLayout::themes_Click()
{
this->HandleThemesMenu();
}
void MenuLayout::users_Click()
{
this->HandleUserMenu();
}
void MenuLayout::controller_Click()
{
this->HandleControllerAppletOpen();
}
void MenuLayout::HandleCloseSuspended()
{
auto sopt = qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "suspended_app"), cfg::GetLanguageString(config.main_lang, config.default_lang, "suspended_close"), { cfg::GetLanguageString(config.main_lang, config.default_lang, "yes"), cfg::GetLanguageString(config.main_lang, config.default_lang, "no") }, true);
if(sopt == 0)
{
am::MenuCommandWriter writer(am::DaemonMessage::TerminateApplication);
writer.FinishWrite();
this->itemsMenu->UnsetSuspendedItem();
qapp->NotifyEndSuspended();
this->bgSuspendedRaw->SetAlphaFactor(0);
}
}
void MenuLayout::HandleHomebrewLaunch(cfg::TitleRecord &rec)
{
u32 launchmode = 0;
if(config.system_title_override_enabled)
{
auto sopt = qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "hb_launch"), cfg::GetLanguageString(config.main_lang, config.default_lang, "hb_launch_conf"), { cfg::GetLanguageString(config.main_lang, config.default_lang, "hb_applet"), cfg::GetLanguageString(config.main_lang, config.default_lang, "hb_app"), cfg::GetLanguageString(config.main_lang, config.default_lang, "cancel") }, true);
if(sopt == 0) launchmode = 1;
else if(sopt == 1) launchmode = 2;
}
else launchmode = 1;
if(launchmode == 1)
{
pu::audio::Play(this->sfxTitleLaunch);
hb::HbTargetParams ipt = {};
strcpy(ipt.nro_path, rec.nro_target.nro_path);
strcpy(ipt.nro_argv, rec.nro_target.nro_argv);
if(strlen(rec.nro_target.nro_argv)) sprintf(ipt.nro_argv, "%s %s", rec.nro_target.nro_path, rec.nro_target.nro_argv);
am::MenuCommandWriter writer(am::DaemonMessage::LaunchHomebrewLibraryApplet);
writer.Write<hb::HbTargetParams>(ipt);
writer.FinishWrite();
qapp->StopPlayBGM();
qapp->CloseWithFadeOut();
return;
}
else if(launchmode == 2)
{
if(config.homebrew_title_application_id != 0)
{
bool launch = true;
if(qapp->IsSuspended())
{
launch = false;
this->HandleCloseSuspended();
if(!qapp->IsSuspended()) launch = true;
}
if(launch)
{
pu::audio::Play(this->sfxTitleLaunch);
hb::HbTargetParams ipt = {};
strcpy(ipt.nro_path, rec.nro_target.nro_path);
strcpy(ipt.nro_argv, rec.nro_target.nro_argv);
if(strlen(rec.nro_target.nro_argv)) sprintf(ipt.nro_argv, "%s %s", rec.nro_target.nro_path, rec.nro_target.nro_argv);
am::MenuCommandWriter writer(am::DaemonMessage::LaunchHomebrewApplication);
writer.Write<u64>(config.homebrew_title_application_id);
writer.Write<hb::HbTargetParams>(ipt);
writer.FinishWrite();
am::MenuCommandResultReader reader;
if(reader && R_SUCCEEDED(reader.GetReadResult()))
{
qapp->StopPlayBGM();
qapp->CloseWithFadeOut();
return;
}
else
{
auto rc = reader.GetReadResult();
qapp->ShowNotification(cfg::GetLanguageString(config.main_lang, config.default_lang, "app_launch_error") + ": " + util::FormatResult(rc));
}
reader.FinishRead();
}
}
else
{
qapp->CreateShowDialog("Launch", "There is no title specified for homebrew to take over it.\nSelect one by pressing up over it.", { "Ok" }, true);
}
}
}
void MenuLayout::HandleUserMenu()
{
auto uid = qapp->GetSelectedUser();
am::MenuCommandWriter writer(am::DaemonMessage::UserHasPassword);
writer.Write<AccountUid>(uid);
writer.FinishWrite();
am::MenuCommandResultReader res;
res.FinishRead();
bool has_pass = R_SUCCEEDED(res.GetReadResult());
auto [_rc, name] = os::GetAccountName(uid);
auto sopt = qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "user_settings"), cfg::GetLanguageString(config.main_lang, config.default_lang, "user_selected") + ": " + name + "\n" + cfg::GetLanguageString(config.main_lang, config.default_lang, "user_option"), { has_pass ? cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_ch") : cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_reg"), cfg::GetLanguageString(config.main_lang, config.default_lang, "user_view_page"), cfg::GetLanguageString(config.main_lang, config.default_lang, "user_logoff"), cfg::GetLanguageString(config.main_lang, config.default_lang, "cancel") }, true, os::GetIconCacheImagePath(uid));
if(sopt == 0)
{
if(has_pass)
{
auto sopt = qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_ch"), cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_ch_option"), { cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_change"), cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_remove"), cfg::GetLanguageString(config.main_lang, config.default_lang, "cancel") }, true);
if((sopt == 0) || (sopt == 1))
{
SwkbdConfig swkbd;
swkbdCreate(&swkbd, 0);
swkbdConfigMakePresetPassword(&swkbd);
swkbdConfigSetStringLenMax(&swkbd, 15);
swkbdConfigSetGuideText(&swkbd, cfg::GetLanguageString(config.main_lang, config.default_lang, "swkbd_user_pass_guide").c_str());
char inpass[0x10] = {0};
auto rc = swkbdShow(&swkbd, inpass, 0x10);
swkbdClose(&swkbd);
if(R_SUCCEEDED(rc))
{
auto [rc2, oldpass] = db::PackPassword(uid, inpass);
rc = rc2;
if(R_SUCCEEDED(rc))
{
if(sopt == 0)
{
SwkbdConfig swkbd;
swkbdCreate(&swkbd, 0);
swkbdConfigMakePresetPassword(&swkbd);
swkbdConfigSetStringLenMax(&swkbd, 15);
swkbdConfigSetGuideText(&swkbd, cfg::GetLanguageString(config.main_lang, config.default_lang, "swkbd_user_new_pass_guide").c_str());
char pass[0x10] = {0};
auto rc = swkbdShow(&swkbd, pass, 0x10);
swkbdClose(&swkbd);
if(R_SUCCEEDED(rc))
{
auto sopt2 = qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_ch"), cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_change_conf"), { cfg::GetLanguageString(config.main_lang, config.default_lang, "yes"), cfg::GetLanguageString(config.main_lang, config.default_lang, "cancel") }, true);
if(sopt2 == 0)
{
auto [rc3, newpass] = db::PackPassword(uid, pass);
rc = rc3;
if(R_SUCCEEDED(rc))
{
am::MenuCommandWriter writer(am::DaemonMessage::ChangeUserPassword);
writer.Write<db::PassBlock>(oldpass);
writer.Write<db::PassBlock>(newpass);
writer.FinishWrite();
am::MenuCommandResultReader reader;
rc = reader.GetReadResult();
}
qapp->ShowNotification(R_SUCCEEDED(rc) ? cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_change_ok") : cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_change_error") + ": " + util::FormatResult(rc));
}
}
}
else if(sopt == 1)
{
auto sopt2 = qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_remove_full"), cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_remove_conf"), { cfg::GetLanguageString(config.main_lang, config.default_lang, "yes"), cfg::GetLanguageString(config.main_lang, config.default_lang, "cancel") }, true);
if(sopt2 == 0)
{
am::MenuCommandWriter writer(am::DaemonMessage::RemoveUserPassword);
writer.Write<db::PassBlock>(oldpass);
writer.FinishWrite();
am::MenuCommandResultReader reader;
rc = reader.GetReadResult();
qapp->ShowNotification(R_SUCCEEDED(rc) ? cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_remove_ok") : cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_remove_error") + ": " + util::FormatResult(rc));
}
}
}
}
}
}
else
{
SwkbdConfig swkbd;
swkbdCreate(&swkbd, 0);
swkbdConfigMakePresetPassword(&swkbd);
swkbdConfigSetStringLenMax(&swkbd, 15);
swkbdConfigSetGuideText(&swkbd, cfg::GetLanguageString(config.main_lang, config.default_lang, "swkbd_user_pass_guide").c_str());
char pass[0x10] = {0};
auto rc = swkbdShow(&swkbd, pass, 0x10);
swkbdClose(&swkbd);
if(R_SUCCEEDED(rc))
{
auto [rc2, newpass] = db::PackPassword(uid, pass);
rc = rc2;
if(R_SUCCEEDED(rc))
{
auto sopt = qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_reg"), cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_reg_conf"), { cfg::GetLanguageString(config.main_lang, config.default_lang, "yes"), cfg::GetLanguageString(config.main_lang, config.default_lang, "cancel") }, true);
if(sopt == 0)
{
am::MenuCommandWriter writer(am::DaemonMessage::RegisterUserPassword);
writer.Write<db::PassBlock>(newpass);
writer.FinishWrite();
am::MenuCommandResultReader reader;
rc = reader.GetReadResult();
}
}
qapp->ShowNotification(R_SUCCEEDED(rc) ? cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_reg_ok") : cfg::GetLanguageString(config.main_lang, config.default_lang, "user_pass_reg_error") + ": " + util::FormatResult(rc));
}
}
}
else if(sopt == 1)
{
// Show myPage applet for user config
u8 in[0xb0] = {0};
*(u32*)in = 7; // Type -> ShowMyProfile
memcpy((AccountUid*)(in + 0x8), &uid, sizeof(uid));
am::LibraryAppletDaemonLaunchWithSimple(AppletId_myPage, 1, in, sizeof(in), NULL, 0, [&]() -> bool
{
return !am::MenuIsHomePressed();
});
}
else if(sopt == 2)
{
u32 logoff = 0;
if(qapp->IsSuspended())
{
auto sopt = qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "suspended_app"), cfg::GetLanguageString(config.main_lang, config.default_lang, "user_logoff_app_suspended"), { cfg::GetLanguageString(config.main_lang, config.default_lang, "yes"), cfg::GetLanguageString(config.main_lang, config.default_lang, "cancel") }, true);
if(sopt == 0) logoff = 2;
}
else logoff = 1;
if(logoff > 0)
{
if(logoff == 2)
{
am::MenuCommandWriter writer(am::DaemonMessage::TerminateApplication);
writer.FinishWrite();
this->itemsMenu->UnsetSuspendedItem();
qapp->NotifyEndSuspended();
this->bgSuspendedRaw->SetAlphaFactor(0);
}
qapp->FadeOut();
this->MoveFolder("", false);
qapp->LoadStartupMenu();
qapp->FadeIn();
}
}
}
void MenuLayout::HandleWebPageOpen()
{
SwkbdConfig swkbd;
swkbdCreate(&swkbd, 0);
swkbdConfigSetGuideText(&swkbd, cfg::GetLanguageString(config.main_lang, config.default_lang, "swkbd_webpage_guide").c_str());
char url[500] = {0};
auto rc = swkbdShow(&swkbd, url, 500);
swkbdClose(&swkbd);
if(R_SUCCEEDED(rc))
{
WebCommonConfig web = {};
webPageCreate(&web, url);
webConfigSetWhitelist(&web, ".*");
am::MenuCommandWriter writer(am::DaemonMessage::OpenWebPage);
writer.Write<WebCommonConfig>(web);
writer.FinishWrite();
qapp->StopPlayBGM();
qapp->CloseWithFadeOut();
return;
}
}
void MenuLayout::HandleSettingsMenu()
{
qapp->FadeOut();
qapp->LoadSettingsMenu();
qapp->FadeIn();
}
void MenuLayout::HandleThemesMenu()
{
qapp->FadeOut();
qapp->LoadThemeMenu();
qapp->FadeIn();
}
void MenuLayout::HandleControllerAppletOpen()
{
am::controller::InitialArg arg1 = {};
am::controller::MainArg arg2 = {};
HidControllerType type;
hidGetSupportedNpadStyleSet(&type);
HidJoyHoldType htype;
hidGetNpadJoyHoldType(&htype);
arg1.npad_style_set = (u32)type;
arg1.npad_joy_hold_type = (u32)htype;
arg1.this_size = sizeof(arg1);
arg1.arg_size = sizeof(arg2);
arg1.unk2 = true;
arg1.unk3 = true;
arg2.min_player_count = 0;
arg2.max_player_count = 4;
arg2.take_over_connection = true;
arg2.left_justify = true;
am::LibraryAppletDaemonLaunchWith(AppletId_controller, 0,
[&](AppletHolder *h)
{
libappletPushInData(h, &arg1, sizeof(arg1));
libappletPushInData(h, &arg2, sizeof(arg2));
},
[&](AppletHolder *h)
{
},
[&]() -> bool
{
return !am::MenuIsHomePressed();
});
}
void MenuLayout::HandleShowHelp()
{
std::string msg;
msg += " - " + cfg::GetLanguageString(config.main_lang, config.default_lang, "help_launch") + "\n";
msg += " - " + cfg::GetLanguageString(config.main_lang, config.default_lang, "help_close") + "\n";
msg += " - " + cfg::GetLanguageString(config.main_lang, config.default_lang, "help_quick") + "\n";
msg += " - " + cfg::GetLanguageString(config.main_lang, config.default_lang, "help_multiselect") + "\n";
msg += " - " + cfg::GetLanguageString(config.main_lang, config.default_lang, "help_back") + "\n";
msg += " - " + cfg::GetLanguageString(config.main_lang, config.default_lang, "help_minus") + "\n";
msg += " - " + cfg::GetLanguageString(config.main_lang, config.default_lang, "help_plus") + "\n";
qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "help_title"), msg, { cfg::GetLanguageString(config.main_lang, config.default_lang, "ok") }, true);
}
void MenuLayout::HandleOpenAlbum()
{
am::MenuCommandWriter writer(am::DaemonMessage::OpenAlbum);
writer.FinishWrite();
qapp->StopPlayBGM();
qapp->CloseWithFadeOut();
return;
}
void MenuLayout::HandlePowerDialog()
{
auto msg = os::GeneralChannelMessage::Invalid;
auto sopt = qapp->CreateShowDialog(cfg::GetLanguageString(config.main_lang, config.default_lang, "power_dialog"), cfg::GetLanguageString(config.main_lang, config.default_lang, "power_dialog_info"), { cfg::GetLanguageString(config.main_lang, config.default_lang, "power_sleep"), cfg::GetLanguageString(config.main_lang, config.default_lang, "power_power_off"), cfg::GetLanguageString(config.main_lang, config.default_lang, "power_reboot"), cfg::GetLanguageString(config.main_lang, config.default_lang, "cancel") }, true);
if(sopt == 0) msg = os::GeneralChannelMessage::Sleep;
else if(sopt == 1) msg = os::GeneralChannelMessage::Shutdown;
else if(sopt == 2) msg = os::GeneralChannelMessage::Reboot;
if(msg != os::GeneralChannelMessage::Invalid)
{
// Fade out on all cases
qapp->FadeOut();
os::SystemAppletMessage smsg = {};
smsg.magic = os::SAMSMagic;
smsg.message = (u32)msg;
os::PushSystemAppletMessage(smsg);
svcSleepThread(1'500'000'000L);
// When we get back after sleep we will do a cool fade in, whereas wuth the other options the console will be already off/rebooted
qapp->FadeIn();
}
}
void MenuLayout::HandleMultiselectMoveToFolder(std::string folder)
{
if(this->select_on)
{
u32 rmvd = 0;
auto basesz = list.root.titles.size();
auto basefsz = list.folders.size();
for(u32 i = 0; i < basesz; i++)
{
auto &title = list.root.titles[i - rmvd];
if(this->itemsMenu->IsItemMultiselected(basefsz + i))
{
if(cfg::MoveRecordTo(list, title, folder))
{
rmvd++;
}
}
}
this->StopMultiselect();
this->MoveFolder(this->curfolder, true);
}
}
void MenuLayout::StopMultiselect()
{
this->select_on = false;
this->select_dir = false;
this->itemsMenu->ResetMultiselections();
}
}