gui: Add new tab to swap page positions and span all buttons to available window width

This commit is contained in:
Joel16 2022-06-30 23:30:22 -04:00
parent c2d0028fb5
commit 562b196d7d
3 changed files with 182 additions and 17 deletions

View File

@ -43,6 +43,7 @@ struct AppEntries {
namespace AppList { namespace AppList {
int Get(AppEntries &entries); int Get(AppEntries &entries);
int Save(std::vector<AppInfoIcon> &entries); int Save(std::vector<AppInfoIcon> &entries);
int SavePages(std::vector<AppInfoPage> &entries);
bool SortAppAsc(const AppInfoIcon &entryA, const AppInfoIcon &entryB); bool SortAppAsc(const AppInfoIcon &entryA, const AppInfoIcon &entryB);
bool SortAppDesc(const AppInfoIcon &entryA, const AppInfoIcon &entryB); bool SortAppDesc(const AppInfoIcon &entryA, const AppInfoIcon &entryB);
bool SortChildAppAsc(const AppInfoChild &entryA, const AppInfoChild &entryB); bool SortChildAppAsc(const AppInfoChild &entryA, const AppInfoChild &entryB);

View File

@ -205,6 +205,87 @@ namespace AppList {
return 0; return 0;
} }
int SavePages(std::vector<AppInfoPage> &entries) {
int ret = 0;
sqlite3 *db = nullptr;
char *error = nullptr;
char db_path_backup[] = "ur0:shell/db/app.db.sort.bkp";
if (R_FAILED(ret = FS::CopyFile(db_path, db_path_backup)))
return ret;
ret = sqlite3_open_v2(db_path, &db, SQLITE_OPEN_READWRITE, nullptr);
if (ret != SQLITE_OK) {
Log::Error("sqlite3_open_v2 failed to open %s\n", db_path);
FS::RemoveFile(db_path);
return ret;
}
// Lock power and prevent auto suspend.
Power::Lock();
const char *prepare_query[] = {
"BEGIN TRANSACTION",
"PRAGMA foreign_keys = off",
"DROP TABLE IF EXISTS tbl_appinfo_page_sort",
"CREATE TABLE tbl_appinfo_page_sort AS SELECT * FROM tbl_appinfo_page"
};
for (int i = 0; i < 4; ++i) {
ret = sqlite3_exec(db, prepare_query[i], nullptr, nullptr, &error);
if (ret != SQLITE_OK) {
AppList::Error(prepare_query[i], error, db, db_path);
return ret;
}
}
// Update tbl_appinfo_page_sort with swapped pages
for (unsigned int i = 0; i < entries.size(); i++) {
std::string query = std::string("UPDATE tbl_appinfo_page_sort ")
+ "SET pageNo = " + std::to_string(entries[i].pageNo) + " WHERE pageId = "
+ std::to_string(entries[i].pageId) + ";";
ret = sqlite3_exec(db, query.c_str(), nullptr, nullptr, &error);
if (ret != SQLITE_OK) {
AppList::Error(query, error, db, db_path);
// If sorting fails, drop tbl_appinfo_page_sort.
query = std::string("DROP TABLE IF EXISTS tbl_appinfo_page_sort");
int ret_next = sqlite3_exec(db, query.c_str(), nullptr, nullptr, &error);
if (ret_next != SQLITE_OK) {
AppList::Error(query, error, db, db_path);
return ret_next;
}
return ret;
}
}
const char *finish_query[] = {
"DROP TABLE tbl_appinfo_page",
"CREATE TABLE tbl_appinfo_page(pageId INTEGER PRIMARY KEY NOT NULL, pageNo INT NOT NULL, themeFile TEXT, bgColor INT, texWidth INT, texHeight INT, imageWidth INT, imageHeight INT, reserved01, reserved02, reserved03, reserved04, reserved05)",
"CREATE INDEX idx_page_no ON tbl_appinfo_page ( pageNo )",
"INSERT INTO tbl_appinfo_page SELECT * FROM tbl_appinfo_page_sort",
"DROP TABLE tbl_appinfo_page_sort",
"CREATE TRIGGER tgr_deletePage2 AFTER DELETE ON tbl_appinfo_page WHEN OLD.pageNo >= 0 BEGIN UPDATE tbl_appinfo_page SET pageNo = pageNo - 1 WHERE tbl_appinfo_page.pageNo > OLD.pageNo; END",
"CREATE TRIGGER tgr_insertPage2 BEFORE INSERT ON tbl_appinfo_page WHEN NEW.pageNo >= 0 BEGIN UPDATE tbl_appinfo_page SET pageNo = pageNo + 1 WHERE tbl_appinfo_page.pageNo >= NEW.pageNo; END",
"PRAGMA foreign_keys = on",
"COMMIT"
};
for (int i = 0; i < 9; ++i) {
ret = sqlite3_exec(db, finish_query[i], nullptr, nullptr, &error);
if (ret != SQLITE_OK) {
AppList::Error(finish_query[i], error, db, db_path);
return ret;
}
}
Power::Unlock();
sqlite3_close(db);
return 0;
}
bool SortAppAsc(const AppInfoIcon &entryA, const AppInfoIcon &entryB) { bool SortAppAsc(const AppInfoIcon &entryA, const AppInfoIcon &entryB) {
std::string entryAname = cfg.sort_by == SortTitle? entryA.title : entryA.titleId; std::string entryAname = cfg.sort_by == SortTitle? entryA.title : entryA.titleId;
std::string entryBname = cfg.sort_by == SortTitle? entryB.title : entryB.titleId; std::string entryBname = cfg.sort_by == SortTitle? entryB.title : entryB.titleId;

View File

@ -33,7 +33,8 @@ namespace Renderer {
namespace GUI { namespace GUI {
enum State { enum State {
StateNone, StateNone,
StateConfirm, StateConfirmSort,
StateConfirmSwap,
StateRestore, StateRestore,
StateLoadoutRestore, StateLoadoutRestore,
StateWarning, StateWarning,
@ -59,6 +60,7 @@ namespace GUI {
static const ImVec2 tex_size = ImVec2(20, 20); static const ImVec2 tex_size = ImVec2(20, 20);
static const char *sort_by[] = {"Title", "Title ID"}; static const char *sort_by[] = {"Title", "Title ID"};
static const char *sort_folders[] = {"Both", "Apps only", "Folders only"}; static const char *sort_folders[] = {"Both", "Apps only", "Folders only"};
static int old_page_id = -1;
static void SetupPopup(const char *id) { static void SetupPopup(const char *id) {
ImGui::OpenPopup(id); ImGui::OpenPopup(id);
@ -96,18 +98,23 @@ namespace GUI {
} }
} }
static void Prompt(State &state, std::vector<AppInfoIcon> &entries, std::vector<SceIoDirent> &loadouts, const std::string &db_name) { static void Prompt(State &state, AppEntries &entries, std::vector<SceIoDirent> &loadouts, const std::string &db_name) {
if (state == StateNone) if (state == StateNone)
return; return;
std::string title, prompt; std::string title, prompt;
switch (state) { switch (state) {
case StateConfirm: case StateConfirmSort:
title = "Confirm"; title = "Confirm";
prompt = "Are you sure you want to apply this sorting method? This may take a minute."; prompt = "Are you sure you want to apply this sorting method? This may take a minute.";
break; break;
case StateConfirmSwap:
title = "Confirm";
prompt = "Are you sure you want to apply this change?";
break;
case StateRestore: case StateRestore:
title = "Restore Backup"; title = "Restore Backup";
prompt = "Are you sure you want to restore this backup?"; prompt = "Are you sure you want to restore this backup?";
@ -147,7 +154,7 @@ namespace GUI {
if (ImGui::BeginPopupModal(title.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) { if (ImGui::BeginPopupModal(title.c_str(), nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
ImGui::Text(prompt.c_str()); ImGui::Text(prompt.c_str());
if ((state == StateConfirm) || (state == StateRestore) || (state == StateLoadoutRestore)) { if ((state == StateConfirmSort) || (state == StateRestore) || (state == StateLoadoutRestore)) {
ImGui::Dummy(ImVec2(0.0f, 5.0f)); ImGui::Dummy(ImVec2(0.0f, 5.0f));
ImGui::Text("You must reboot your device for the changes to take effect."); ImGui::Text("You must reboot your device for the changes to take effect.");
} }
@ -160,10 +167,19 @@ namespace GUI {
if (ImGui::Button("Ok", ImVec2(120, 0))) { if (ImGui::Button("Ok", ImVec2(120, 0))) {
switch (state) { switch (state) {
case StateConfirm: case StateConfirmSort:
AppList::Backup(); AppList::Backup();
backupExists = true; backupExists = true;
if ((AppList::Save(entries)) == 0) if ((AppList::Save(entries.icons)) == 0)
state = StateDone;
else
state = StateError;
break;
case StateConfirmSwap:
AppList::Backup();
backupExists = true;
if ((AppList::SavePages(entries.pages)) == 0)
state = StateDone; state = StateDone;
else else
state = StateError; state = StateError;
@ -300,16 +316,16 @@ namespace GUI {
ImGui::SameLine(); ImGui::SameLine();
GUI::DisableButtonInit(cfg.sort_mode == SortDefault); GUI::DisableButtonInit(cfg.sort_mode == SortDefault);
if (ImGui::Button("Apply Sort")) if (ImGui::Button("Apply Sort", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 0.0f)))
state = StateConfirm; state = StateConfirmSort;
GUI::DisableButtonExit(cfg.sort_mode == SortDefault); GUI::DisableButtonExit(cfg.sort_mode == SortDefault);
if (backupExists) { ImGui::SameLine();
ImGui::SameLine();
if (ImGui::Button("Restore Backup")) GUI::DisableButtonInit(!backupExists);
state = StateRestore; if (ImGui::Button("Restore Backup", ImVec2(ImGui::GetContentRegionAvail().x * 1.0f, 0.0f)))
} state = StateRestore;
GUI::DisableButtonExit(!backupExists);
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
@ -401,6 +417,68 @@ namespace GUI {
} }
} }
static void PagesTab(AppEntries &entries, State &state) {
ImGuiTableFlags tableFlags = ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter;
if (ImGui::BeginTabItem("Pages")) {
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
if (ImGui::Button("Reset", ImVec2(ImGui::GetContentRegionAvail().x * 0.33f, 0.0f))) {
AppList::Get(entries);
old_page_id = -1;
}
ImGui::SameLine();
if (ImGui::Button("Apply Changes", ImVec2(ImGui::GetContentRegionAvail().x * 0.5f, 0.0f)))
state = StateConfirmSwap;
ImGui::SameLine();
GUI::DisableButtonInit(!backupExists);
if (ImGui::Button("Restore Backup", ImVec2(ImGui::GetContentRegionAvail().x * 1.0f, 0.0f)))
state = StateRestore;
GUI::DisableButtonExit(!backupExists);
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
if (ImGui::BeginTable("PagesList", 3, tableFlags)) {
ImGui::TableSetupColumn("", ImGuiTableColumnFlags_WidthFixed);
ImGui::TableSetupColumn("pageId");
ImGui::TableSetupColumn("pageNo");
ImGui::TableHeadersRow();
for (unsigned int i = 0; i < entries.pages.size(); i++) {
ImGui::TableNextRow();
ImGui::TableNextColumn();
ImGui::Image(reinterpret_cast<ImTextureID>(icons[DB].id), tex_size);
ImGui::TableNextColumn();
ImGui::Text("%d", entries.pages[i].pageId);
ImGui::TableNextColumn();
std::string pageNo = std::to_string(entries.pages[i].pageNo);
const bool is_selected = (old_page_id == static_cast<int>(i));
if (ImGui::Selectable(pageNo.c_str(), is_selected)) {
if (old_page_id == -1)
old_page_id = i;
else {
int temp = entries.pages[i].pageNo;
entries.pages[i].pageNo = entries.pages[old_page_id].pageNo;
entries.pages[old_page_id].pageNo = temp;
old_page_id = -1;
}
}
}
ImGui::EndTable();
}
ImGui::EndTabItem();
}
}
static void LoadoutsTab(std::vector<SceIoDirent> &loadouts, State &state, int &date_format, std::string &loadout_name) { static void LoadoutsTab(std::vector<SceIoDirent> &loadouts, State &state, int &date_format, std::string &loadout_name) {
ImGuiTableFlags tableFlags = ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter | ImGuiTableFlags tableFlags = ImGuiTableFlags_Resizable | ImGuiTableFlags_BordersInner | ImGuiTableFlags_BordersOuter |
ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_ScrollY; ImGuiTableFlags_SizingStretchProp | ImGuiTableFlags_ScrollY;
@ -408,9 +486,9 @@ namespace GUI {
if (ImGui::BeginTabItem("Loadouts")) { if (ImGui::BeginTabItem("Loadouts")) {
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
if (ImGui::Button("Backup current loadout")) { if (ImGui::Button("Backup current loadout", ImVec2(ImGui::GetContentRegionAvail().x * 1.0f, 0.0f))) {
if (R_SUCCEEDED(Loadouts::Backup())) if (R_SUCCEEDED(Loadouts::Backup()))
FS::GetDirList("ux0:data/VITAHomebrewSorter/loadouts", loadouts); FS::GetDirList("ux0:data/VITAHomebrewSorter/loadouts", loadouts);
} }
ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing ImGui::Dummy(ImVec2(0.0f, 5.0f)); // Spacing
@ -515,11 +593,15 @@ namespace GUI {
int RenderLoop(void) { int RenderLoop(void) {
bool done = false; bool done = false;
backupExists = (FS::FileExists("ux0:/data/VITAHomebrewSorter/backup/app.db") || FS::FileExists("ux0:/data/VITAHomebrewSorter/backup/app.db.bkp")); backupExists = (FS::FileExists("ux0:/data/VITAHomebrewSorter/backup/app.db") || FS::FileExists("ux0:/data/VITAHomebrewSorter/backup/app.db.bkp"));
AppEntries entries; AppEntries entries;
std::vector<SceIoDirent> loadouts; std::vector<SceIoDirent> loadouts;
AppList::Get(entries); AppList::Get(entries);
FS::GetDirList("ux0:data/VITAHomebrewSorter/loadouts", loadouts); FS::GetDirList("ux0:data/VITAHomebrewSorter/loadouts", loadouts);
int date_format = Utils::GetDateFormat(); int date_format = Utils::GetDateFormat();
std::string loadout_name; std::string loadout_name;
State state = StateNone; State state = StateNone;
SceCtrlData pad = { 0 }; SceCtrlData pad = { 0 };
@ -532,6 +614,7 @@ namespace GUI {
if (ImGui::Begin("VITA Homebrew Sorter", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) { if (ImGui::Begin("VITA Homebrew Sorter", nullptr, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse)) {
if (ImGui::BeginTabBar("VITA Homebrew Sorter tabs")) { if (ImGui::BeginTabBar("VITA Homebrew Sorter tabs")) {
GUI::SortTab(entries, state); GUI::SortTab(entries, state);
GUI::PagesTab(entries, state);
GUI::DisableButtonInit(!cfg.beta_features); GUI::DisableButtonInit(!cfg.beta_features);
GUI::LoadoutsTab(loadouts, state, date_format, loadout_name); GUI::LoadoutsTab(loadouts, state, date_format, loadout_name);
GUI::DisableButtonExit(!cfg.beta_features); GUI::DisableButtonExit(!cfg.beta_features);
@ -541,7 +624,7 @@ namespace GUI {
} }
GUI::ExitWindow(); GUI::ExitWindow();
GUI::Prompt(state, entries.icons, loadouts, loadout_name.c_str()); GUI::Prompt(state, entries, loadouts, loadout_name.c_str());
Renderer::End(true, ImVec4(0.45f, 0.55f, 0.60f, 1.00f)); Renderer::End(true, ImVec4(0.45f, 0.55f, 0.60f, 1.00f));
pad = Utils::ReadControls(); pad = Utils::ReadControls();