FullscreenUI: Make save state selector a main window

More reliable, saves messing around with popups.
This commit is contained in:
Stenzek 2025-03-30 12:38:46 +10:00
parent 3bf5ded0d2
commit 9bcd738f5c
No known key found for this signature in database
2 changed files with 184 additions and 290 deletions

View File

@ -174,6 +174,7 @@ enum class MainWindowType : u8
GameList,
Settings,
PauseMenu,
SaveStateSelector,
Achievements,
Leaderboards,
};
@ -427,7 +428,8 @@ struct SaveStateListEntry
{
std::string title;
std::string summary;
std::string path;
std::string game_path;
std::string state_path;
std::unique_ptr<GPUTexture> preview_texture;
time_t timestamp;
s32 slot;
@ -437,18 +439,14 @@ struct SaveStateListEntry
static void InitializePlaceholderSaveStateListEntry(SaveStateListEntry* li, s32 slot, bool global);
static bool InitializeSaveStateListEntryFromSerial(SaveStateListEntry* li, const std::string& serial, s32 slot,
bool global);
static bool InitializeSaveStateListEntryFromPath(SaveStateListEntry* li, std::string path, s32 slot, bool global,
std::string* media_path);
static bool InitializeSaveStateListEntryFromPath(SaveStateListEntry* li, std::string path, s32 slot, bool global);
static void ClearSaveStateEntryList();
static u32 PopulateSaveStateListEntries(const std::string& serial,
std::optional<ExtendedSaveStateInfo> undo_save_state);
static u32 PopulateSaveStateListEntries(const std::string& serial, std::optional<ExtendedSaveStateInfo> undo_save_state,
bool is_loading);
static void OpenSaveStateSelector(const std::string& serial, const std::string& path, bool is_loading);
static void CloseSaveStateSelector();
static void DrawSaveStateSelector(bool is_loading);
static void DrawSaveStateSelector();
static bool OpenLoadStateSelectorForGameResume(const GameList::Entry* entry);
static void DrawResumeStateSelector();
static void DoLoadState(std::string path);
static void DoSaveState(s32 slot, bool global);
//////////////////////////////////////////////////////////////////////////
// Game List
@ -567,9 +565,6 @@ struct ALIGN_TO_CACHE_LINE UIState
// Save State List
std::vector<SaveStateListEntry> save_state_selector_slots;
std::string save_state_selector_game_path;
s32 save_state_selector_submenu_index = -1;
bool save_state_selector_open = false;
bool save_state_selector_loading = true;
// Lazily populated cover images.
@ -762,7 +757,7 @@ bool FullscreenUI::HasActiveWindow()
bool FullscreenUI::AreAnyDialogsOpen()
{
return (s_state.save_state_selector_open || s_state.input_binding_type != InputBindingInfo::Type::Unknown ||
return (s_state.input_binding_type != InputBindingInfo::Type::Unknown ||
ImGuiFullscreen::IsAnyFixedPopupDialogOpen() || ImGuiFullscreen::IsChoiceDialogOpen() ||
ImGuiFullscreen::IsInputDialogOpen() || ImGuiFullscreen::IsFileSelectorOpen() ||
ImGuiFullscreen::IsMessageBoxDialogOpen() || ImGuiFullscreen::HasToast() ||
@ -968,7 +963,7 @@ void FullscreenUI::Shutdown(bool clear_state)
Achievements::ClearUIState();
ClearInputBindingVariables();
CloseSaveStateSelector();
ClearSaveStateEntryList();
s_state.icon_image_map.clear();
s_state.cover_image_map.clear();
std::memset(s_state.controller_macro_expanded, 0, sizeof(s_state.controller_macro_expanded));
@ -1043,6 +1038,9 @@ void FullscreenUI::Render()
case MainWindowType::PauseMenu:
DrawPauseMenu();
break;
case MainWindowType::SaveStateSelector:
DrawSaveStateSelector();
break;
case MainWindowType::Achievements:
Achievements::DrawAchievementsWindow();
break;
@ -1057,8 +1055,6 @@ void FullscreenUI::Render()
DrawAboutWindow();
else if (IsFixedPopupDialogOpen(RESUME_STATE_SELECTOR_DIALOG_NAME))
DrawResumeStateSelector();
else if (s_state.save_state_selector_open)
DrawSaveStateSelector(s_state.save_state_selector_loading);
else if (s_state.input_binding_type != InputBindingInfo::Type::Unknown)
DrawInputBindingWindow();
@ -1285,15 +1281,11 @@ void FullscreenUI::DoResume()
return;
}
CloseSaveStateSelector();
SaveStateListEntry slentry;
if (!InitializeSaveStateListEntryFromPath(&slentry, std::move(path), -1, false,
&s_state.save_state_selector_game_path))
{
if (!InitializeSaveStateListEntryFromPath(&slentry, std::move(path), -1, false))
return;
}
ClearSaveStateEntryList();
s_state.save_state_selector_slots.push_back(std::move(slentry));
OpenFixedPopupDialog(RESUME_STATE_SELECTOR_DIALOG_NAME);
}
@ -6755,7 +6747,7 @@ void FullscreenUI::InitializePlaceholderSaveStateListEntry(SaveStateListEntry* l
slot) :
FSUI_STR("Quick Save");
li->summary = FSUI_STR("No save present in this slot.");
li->path = {};
li->state_path = {};
li->timestamp = 0;
li->slot = slot;
li->preview_texture = {};
@ -6767,7 +6759,7 @@ bool FullscreenUI::InitializeSaveStateListEntryFromSerial(SaveStateListEntry* li
{
const std::string path =
(global ? System::GetGlobalSaveStateFileName(slot) : System::GetGameSaveStateFileName(serial, slot));
if (!InitializeSaveStateListEntryFromPath(li, path.c_str(), slot, global, nullptr))
if (!InitializeSaveStateListEntryFromPath(li, path.c_str(), slot, global))
{
InitializePlaceholderSaveStateListEntry(li, slot, global);
return false;
@ -6776,8 +6768,7 @@ bool FullscreenUI::InitializeSaveStateListEntryFromSerial(SaveStateListEntry* li
return true;
}
bool FullscreenUI::InitializeSaveStateListEntryFromPath(SaveStateListEntry* li, std::string path, s32 slot, bool global,
std::string* media_path)
bool FullscreenUI::InitializeSaveStateListEntryFromPath(SaveStateListEntry* li, std::string path, s32 slot, bool global)
{
std::optional<ExtendedSaveStateInfo> ssi(System::GetExtendedSaveStateInfo(path.c_str()));
if (!ssi.has_value())
@ -6795,12 +6786,11 @@ bool FullscreenUI::InitializeSaveStateListEntryFromPath(SaveStateListEntry* li,
li->summary = fmt::format(FSUI_FSTR("Saved {:%c}"), fmt::localtime(ssi->timestamp));
li->timestamp = ssi->timestamp;
li->slot = slot;
li->path = std::move(path);
li->state_path = std::move(path);
li->game_path = std::move(ssi->media_path);
li->global = global;
if (ssi->screenshot.IsValid())
li->preview_texture = g_gpu_device->FetchAndUploadTextureImage(ssi->screenshot);
if (media_path)
*media_path = std::move(ssi->media_path);
return true;
}
@ -6816,7 +6806,7 @@ void FullscreenUI::ClearSaveStateEntryList()
}
u32 FullscreenUI::PopulateSaveStateListEntries(const std::string& serial,
std::optional<ExtendedSaveStateInfo> undo_save_state)
std::optional<ExtendedSaveStateInfo> undo_save_state, bool is_loading)
{
ClearSaveStateEntryList();
@ -6835,7 +6825,7 @@ u32 FullscreenUI::PopulateSaveStateListEntries(const std::string& serial,
for (s32 i = 1; i <= System::PER_GAME_SAVE_STATE_SLOTS; i++)
{
SaveStateListEntry li;
if (InitializeSaveStateListEntryFromSerial(&li, serial, i, false) || !s_state.save_state_selector_loading)
if (InitializeSaveStateListEntryFromSerial(&li, serial, i, false) || !is_loading)
s_state.save_state_selector_slots.push_back(std::move(li));
}
}
@ -6843,7 +6833,7 @@ u32 FullscreenUI::PopulateSaveStateListEntries(const std::string& serial,
for (s32 i = 1; i <= System::GLOBAL_SAVE_STATE_SLOTS; i++)
{
SaveStateListEntry li;
if (InitializeSaveStateListEntryFromSerial(&li, serial, i, true) || !s_state.save_state_selector_loading)
if (InitializeSaveStateListEntryFromSerial(&li, serial, i, true) || !is_loading)
s_state.save_state_selector_slots.push_back(std::move(li));
}
@ -6856,15 +6846,15 @@ void FullscreenUI::OpenSaveStateSelector(const std::string& serial, const std::s
{
// need to get the undo state, if any
Host::RunOnCPUThread([serial = serial, is_loading]() {
std::optional<ExtendedSaveStateInfo> undo_state = System::GetUndoSaveStateInfo();
std::optional<ExtendedSaveStateInfo> undo_state;
if (is_loading)
undo_state = System::GetUndoSaveStateInfo();
GPUThread::RunOnThread([serial = std::move(serial), undo_state = std::move(undo_state), is_loading]() mutable {
s_state.save_state_selector_loading = is_loading;
s_state.save_state_selector_game_path = {};
if (PopulateSaveStateListEntries(serial, std::move(undo_state)) > 0)
if (PopulateSaveStateListEntries(serial, std::move(undo_state), is_loading) > 0)
{
s_state.current_main_window = MainWindowType::None;
s_state.save_state_selector_open = true;
QueueResetFocus(FocusResetType::PopupOpened);
s_state.save_state_selector_loading = is_loading;
s_state.current_main_window = MainWindowType::SaveStateSelector;
QueueResetFocus(FocusResetType::ViewChanged);
}
else
{
@ -6875,99 +6865,111 @@ void FullscreenUI::OpenSaveStateSelector(const std::string& serial, const std::s
}
else
{
s_state.save_state_selector_loading = is_loading;
if (PopulateSaveStateListEntries(serial, std::nullopt) > 0)
if (PopulateSaveStateListEntries(serial, std::nullopt, is_loading) > 0)
{
s_state.save_state_selector_game_path = path;
s_state.save_state_selector_open = true;
s_state.save_state_selector_loading = is_loading;
s_state.current_main_window = MainWindowType::SaveStateSelector;
QueueResetFocus(FocusResetType::ViewChanged);
}
else
{
s_state.save_state_selector_game_path = {};
ShowToast({}, FSUI_STR("No save states found."), Host::OSD_INFO_DURATION);
}
}
}
void FullscreenUI::CloseSaveStateSelector()
void FullscreenUI::DrawSaveStateSelector()
{
if (s_state.save_state_selector_open)
QueueResetFocus(FocusResetType::PopupClosed);
static constexpr auto do_load_state = [](std::string game_path, std::string state_path) {
ClearSaveStateEntryList();
s_state.save_state_selector_open = false;
s_state.save_state_selector_loading = false;
s_state.save_state_selector_game_path = {};
if (IsFixedPopupDialogOpen(RESUME_STATE_SELECTOR_DIALOG_NAME))
CloseFixedPopupDialogImmediately();
if (GPUThread::HasGPUBackend())
{
ReturnToMainWindow();
Host::RunOnCPUThread([game_path = std::move(game_path), state_path = std::move(state_path)]() mutable {
if (System::IsValid())
{
if (state_path.empty())
{
// Loading undo state.
if (!System::UndoLoadState())
{
GPUThread::RunOnThread(
[]() { ShowToast(std::string(), TRANSLATE_STR("System", "Failed to undo load state.")); });
}
void FullscreenUI::DrawSaveStateSelector(bool is_loading)
{
ImGuiIO& io = ImGui::GetIO();
ImGui::SetNextWindowPos(ImVec2(0.0f, 0.0f));
ImGui::SetNextWindowSize(io.DisplaySize - LayoutScale(0.0f, LAYOUT_FOOTER_HEIGHT));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0.0f, 0.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 0.0f);
ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f);
const char* window_title = is_loading ? FSUI_CSTR("Load State") : FSUI_CSTR("Save State");
ImGui::OpenPopup(window_title);
bool is_open = true;
const bool valid =
ImGui::BeginPopupModal(window_title, &is_open,
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove |
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoBackground);
if (!valid || !is_open)
{
if (valid)
ImGui::EndPopup();
ImGui::PopStyleVar(5);
if (!is_open)
{
CloseSaveStateSelector();
ReturnToPreviousWindow();
}
else
{
Error error;
if (!System::LoadState(state_path.c_str(), &error, true, false))
{
GPUThread::RunOnThread([error_desc = error.TakeDescription()]() {
ShowToast(std::string(), fmt::format(TRANSLATE_FS("System", "Failed to load state: {}"), error_desc));
});
}
}
}
});
}
else
{
DoStartPath(std::move(game_path), std::move(state_path));
}
};
static constexpr auto do_save_state = [](s32 slot, bool global) {
ClearSaveStateEntryList();
ReturnToMainWindow();
Host::RunOnCPUThread([slot, global]() {
if (!System::IsValid())
return;
}
std::string path(global ? System::GetGlobalSaveStateFileName(slot) :
System::GetGameSaveStateFileName(System::GetGameSerial(), slot));
Error error;
if (!System::SaveState(std::move(path), &error, g_settings.create_save_state_backups, false))
{
GPUThread::RunOnThread([error_desc = error.TakeDescription()]() {
ShowToast(std::string(), fmt::format(TRANSLATE_FS("System", "Failed to save state: {}"), error_desc));
});
}
});
};
ImGuiIO& io = ImGui::GetIO();
const ImVec2 heading_size =
ImVec2(io.DisplaySize.x, LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY) +
(LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING) * 2.0f) + LayoutScale(2.0f));
ImGui::PushStyleColor(ImGuiCol_ChildBg, ModAlpha(UIStyle.PrimaryColor, 0.9f));
bool closed = false;
bool was_close_not_back = false;
bool ignore_close_request = false;
if (ImGui::BeginChild("state_titlebar", heading_size, false, ImGuiWindowFlags_NavFlattened))
// last state deleted?
if (s_state.save_state_selector_slots.empty())
closed = true;
if (BeginFullscreenWindow(ImVec2(0.0f, 0.0f), heading_size, "##save_state_selector_title",
ModAlpha(UIStyle.PrimaryColor, GetBackgroundAlpha())))
{
BeginNavBar();
if (NavButton(ICON_PF_NAVIGATION_BACK, true, true))
closed = true;
NavTitle(is_loading ? FSUI_CSTR("Load State") : FSUI_CSTR("Save State"));
NavTitle(s_state.save_state_selector_loading ? FSUI_CSTR("Load State") : FSUI_CSTR("Save State"));
EndNavBar();
ImGui::EndChild();
}
ImGui::PopStyleColor();
ImGui::PushStyleColor(ImGuiCol_ChildBg, ModAlpha(UIStyle.BackgroundColor, 0.9f));
ImGui::SetCursorPos(ImVec2(0.0f, heading_size.y));
EndFullscreenWindow();
if (IsFocusResetFromWindowChange())
ImGui::SetNextWindowScroll(ImVec2(0.0f, 0.0f));
if (ImGui::BeginChild("state_list",
ImVec2(io.DisplaySize.x, io.DisplaySize.y - LayoutScale(LAYOUT_FOOTER_HEIGHT) - heading_size.y),
false, ImGuiWindowFlags_NavFlattened))
if (BeginFullscreenWindow(
ImVec2(0.0f, heading_size.y),
ImVec2(io.DisplaySize.x, io.DisplaySize.y - heading_size.y - LayoutScale(LAYOUT_FOOTER_HEIGHT)),
"##save_state_selector_list", ModAlpha(UIStyle.BackgroundColor, GetBackgroundAlpha()), 0.0f,
ImVec2(ImGuiFullscreen::LAYOUT_MENU_WINDOW_X_PADDING, 0.0f)))
{
ResetFocusHere();
BeginMenuButtons();
@ -6995,107 +6997,6 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading)
for (u32 i = 0; i < s_state.save_state_selector_slots.size();)
{
SaveStateListEntry& entry = s_state.save_state_selector_slots[i];
if (static_cast<s32>(i) == s_state.save_state_selector_submenu_index)
{
// can't use a choice dialog here, because we're already in a modal...
ImGuiFullscreen::PushResetLayout();
ImGui::PushFont(UIStyle.LargeFont);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(10.0f));
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding,
LayoutScale(LAYOUT_MENU_BUTTON_X_PADDING, LAYOUT_MENU_BUTTON_Y_PADDING));
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 0.0f);
ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.PrimaryTextColor);
ImGui::PushStyleColor(ImGuiCol_TitleBg, UIStyle.PrimaryDarkColor);
ImGui::PushStyleColor(ImGuiCol_TitleBgActive, UIStyle.PrimaryColor);
const float width = LayoutScale(600.0f);
const float title_height = UIStyle.LargeFont->FontSize + ImGui::GetStyle().FramePadding.y * 2.0f +
ImGui::GetStyle().WindowPadding.y * 2.0f;
const float height =
title_height +
((LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY) + (LayoutScale(LAYOUT_MENU_BUTTON_Y_PADDING) * 2.0f)) *
3.0f);
ImGui::SetNextWindowSize(ImVec2(width, height));
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::OpenPopup(entry.title.c_str());
bool removed = false;
if (ImGui::BeginPopupModal(entry.title.c_str(), &is_open,
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove))
{
ImGui::PushStyleColor(ImGuiCol_Text, UIStyle.BackgroundTextColor);
BeginMenuButtons();
if (MenuButtonWithoutSummary(is_loading ? FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Load State") :
FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Save State")))
{
if (is_loading)
DoLoadState(std::move(entry.path));
else
DoSaveState(entry.slot, entry.global);
closed = true;
was_close_not_back = true;
}
if (!entry.path.empty() && MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_FOLDER_MINUS, "Delete Save")))
{
if (!FileSystem::FileExists(entry.path.c_str()))
{
ShowToast({}, fmt::format(FSUI_FSTR("{} does not exist."), ImGuiFullscreen::RemoveHash(entry.title)));
is_open = true;
}
else if (FileSystem::DeleteFile(entry.path.c_str()))
{
ShowToast({}, fmt::format(FSUI_FSTR("{} deleted."), ImGuiFullscreen::RemoveHash(entry.title)));
if (is_loading)
s_state.save_state_selector_slots.erase(s_state.save_state_selector_slots.begin() + i);
else
InitializePlaceholderSaveStateListEntry(&entry, entry.slot, entry.global);
removed = true;
if (s_state.save_state_selector_slots.empty())
{
closed = true;
was_close_not_back = true;
}
else
{
is_open = false;
}
}
else
{
ShowToast({}, fmt::format(FSUI_FSTR("Failed to delete {}."), ImGuiFullscreen::RemoveHash(entry.title)));
is_open = false;
}
}
if (MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_WINDOW_CLOSE, "Close Menu")) || WantsToCloseMenu())
{
is_open = false;
ignore_close_request = true;
}
EndMenuButtons();
ImGui::PopStyleColor();
ImGui::EndPopup();
}
if (!is_open)
s_state.save_state_selector_submenu_index = -1;
ImGui::PopStyleColor(3);
ImGui::PopStyleVar(3);
ImGui::PopFont();
ImGuiFullscreen::PopResetLayout();
if (removed)
continue;
}
ImGuiWindow* window = ImGui::GetCurrentWindow();
if (window->SkipItems)
@ -7153,20 +7054,74 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading)
if (pressed)
{
if (is_loading)
DoLoadState(entry.path);
if (s_state.save_state_selector_loading)
do_load_state(std::move(entry.game_path), std::move(entry.state_path));
else
DoSaveState(entry.slot, entry.global);
do_save_state(entry.slot, entry.global);
closed = true;
was_close_not_back = true;
}
else if (hovered &&
(ImGui::IsItemClicked(ImGuiMouseButton_Right) ||
ImGui::IsKeyPressed(ImGuiKey_NavGamepadInput, false) || ImGui::IsKeyPressed(ImGuiKey_F1, false)))
{
CancelPendingMenuClose();
s_state.save_state_selector_submenu_index = static_cast<s32>(i);
std::string title;
if (s_state.save_state_selector_loading)
title = FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Load State");
else
title = FSUI_ICONSTR(ICON_FA_FOLDER_OPEN, "Save State");
ChoiceDialogOptions options;
options.reserve(3);
options.emplace_back(title, false);
if (!entry.state_path.empty())
options.emplace_back(FSUI_ICONSTR(ICON_FA_FOLDER_MINUS, "Delete Save"), false);
options.emplace_back(FSUI_ICONSTR(ICON_FA_WINDOW_CLOSE, "Close Menu"), false);
OpenChoiceDialog(
std::move(title), false, std::move(options),
[i](s32 index, const std::string& title, bool checked) mutable {
if (index < 0 || i >= s_state.save_state_selector_slots.size())
return;
SaveStateListEntry& entry = s_state.save_state_selector_slots[i];
if (index == 0)
{
// load state
if (s_state.save_state_selector_loading)
do_load_state(std::move(entry.game_path), std::move(entry.state_path));
else
do_save_state(entry.slot, entry.global);
}
else if (!entry.state_path.empty() && index == 1)
{
// delete state
if (!FileSystem::FileExists(entry.state_path.c_str()))
{
ShowToast({}, fmt::format(FSUI_FSTR("{} does not exist."), ImGuiFullscreen::RemoveHash(entry.title)));
}
else if (FileSystem::DeleteFile(entry.state_path.c_str()))
{
ShowToast({}, fmt::format(FSUI_FSTR("{} deleted."), ImGuiFullscreen::RemoveHash(entry.title)));
// need to preserve the texture, since it's going to be drawn this frame
// TODO: do this with a transition for safety
g_gpu_device->RecycleTexture(std::move(entry.preview_texture));
if (s_state.save_state_selector_loading)
s_state.save_state_selector_slots.erase(s_state.save_state_selector_slots.begin() + i);
else
InitializePlaceholderSaveStateListEntry(&entry, entry.slot, entry.global);
}
else
{
ShowToast({},
fmt::format(FSUI_FSTR("Failed to delete {}."), ImGuiFullscreen::RemoveHash(entry.title)));
}
}
});
}
}
@ -7187,20 +7142,17 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading)
EndMenuButtons();
SetWindowNavWrapping(true, true);
ImGui::EndChild();
}
ImGui::PopStyleColor();
ImGui::EndPopup();
ImGui::PopStyleVar(5);
EndFullscreenWindow();
if (IsGamepadInputSource())
{
SetFullscreenFooterText(
std::array{std::make_pair(ICON_PF_XBOX_DPAD, FSUI_VSTR("Select State")),
std::make_pair(ICON_PF_BUTTON_Y, FSUI_VSTR("Delete State")),
std::make_pair(ICON_PF_BUTTON_A, is_loading ? FSUI_VSTR("Load State") : FSUI_VSTR("Save State")),
std::make_pair(ICON_PF_BUTTON_A, s_state.save_state_selector_loading ? FSUI_VSTR("Load State") :
FSUI_VSTR("Save State")),
std::make_pair(ICON_PF_BUTTON_B, FSUI_VSTR("Cancel"))},
GetBackgroundAlpha());
}
@ -7210,17 +7162,15 @@ void FullscreenUI::DrawSaveStateSelector(bool is_loading)
std::array{std::make_pair(ICON_PF_ARROW_UP ICON_PF_ARROW_DOWN ICON_PF_ARROW_LEFT ICON_PF_ARROW_RIGHT,
FSUI_VSTR("Select State")),
std::make_pair(ICON_PF_F1, FSUI_VSTR("Delete State")),
std::make_pair(ICON_PF_ENTER, is_loading ? FSUI_VSTR("Load State") : FSUI_VSTR("Save State")),
std::make_pair(ICON_PF_ENTER, s_state.save_state_selector_loading ? FSUI_VSTR("Load State") :
FSUI_VSTR("Save State")),
std::make_pair(ICON_PF_ESC, FSUI_VSTR("Cancel"))},
GetBackgroundAlpha());
}
if ((!ignore_close_request && WantsToCloseMenu()) || closed)
if ((!AreAnyDialogsOpen() && WantsToCloseMenu()) || closed)
{
CloseSaveStateSelector();
if (was_close_not_back)
ReturnToMainWindow();
else if (s_state.current_main_window != MainWindowType::GameList)
ClearSaveStateEntryList();
ReturnToPreviousWindow();
}
}
@ -7231,9 +7181,7 @@ bool FullscreenUI::OpenLoadStateSelectorForGameResume(const GameList::Entry* ent
if (!InitializeSaveStateListEntryFromSerial(&slentry, entry->serial, -1, false))
return false;
CloseSaveStateSelector();
s_state.save_state_selector_slots.push_back(std::move(slentry));
s_state.save_state_selector_game_path = entry->path;
OpenFixedPopupDialog(RESUME_STATE_SELECTOR_DIALOG_NAME);
return true;
}
@ -7242,7 +7190,7 @@ void FullscreenUI::DrawResumeStateSelector()
{
if (!BeginFixedPopupDialog(LayoutScale(30.0f), LayoutScale(40.0f), LayoutScale(820.0f, 625.0f)))
{
CloseSaveStateSelector();
ClearSaveStateEntryList();
return;
}
@ -7272,25 +7220,28 @@ void FullscreenUI::DrawResumeStateSelector()
if (MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_PLAY, "Load State")))
{
std::string game_path = std::move(s_state.save_state_selector_game_path);
std::string state_path = std::move(entry.path);
CloseSaveStateSelector();
std::string game_path = std::move(entry.game_path);
std::string state_path = std::move(entry.state_path);
ClearSaveStateEntryList();
CloseFixedPopupDialogImmediately();
DoStartPath(std::move(game_path), std::move(state_path));
}
if (MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_LIGHTBULB, "Clean Boot")))
{
std::string game_path = std::move(s_state.save_state_selector_game_path);
CloseSaveStateSelector();
std::string game_path = std::move(entry.game_path);
ClearSaveStateEntryList();
CloseFixedPopupDialogImmediately();
DoStartPath(std::move(game_path));
}
if (MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_FOLDER_MINUS, "Delete State")))
{
if (FileSystem::DeleteFile(entry.path.c_str()))
if (FileSystem::DeleteFile(entry.state_path.c_str()))
{
std::string game_path = std::move(s_state.save_state_selector_game_path);
CloseSaveStateSelector();
std::string game_path = std::move(entry.game_path);
ClearSaveStateEntryList();
CloseFixedPopupDialogImmediately();
DoStartPath(std::move(game_path));
}
else
@ -7309,64 +7260,6 @@ void FullscreenUI::DrawResumeStateSelector()
EndFixedPopupDialog();
}
void FullscreenUI::DoLoadState(std::string path)
{
std::string boot_path = std::move(s_state.save_state_selector_game_path);
CloseSaveStateSelector();
if (GPUThread::HasGPUBackend())
{
Host::RunOnCPUThread([boot_path = std::move(boot_path), path = std::move(path)]() mutable {
if (System::IsValid())
{
if (path.empty())
{
// Loading undo state.
if (!System::UndoLoadState())
{
GPUThread::RunOnThread(
[]() { ShowToast(std::string(), TRANSLATE_STR("System", "Failed to undo load state.")); });
}
}
else
{
Error error;
if (!System::LoadState(path.c_str(), &error, true, false))
{
GPUThread::RunOnThread([error_desc = error.TakeDescription()]() {
ShowToast(std::string(), fmt::format(TRANSLATE_FS("System", "Failed to load state: {}"), error_desc));
});
}
}
}
});
}
else
{
DoStartPath(std::move(boot_path), std::move(path));
}
}
void FullscreenUI::DoSaveState(s32 slot, bool global)
{
CloseSaveStateSelector();
Host::RunOnCPUThread([slot, global]() {
if (!System::IsValid())
return;
std::string path(global ? System::GetGlobalSaveStateFileName(slot) :
System::GetGameSaveStateFileName(System::GetGameSerial(), slot));
Error error;
if (!System::SaveState(std::move(path), &error, g_settings.create_save_state_backups, false))
{
GPUThread::RunOnThread([error_desc = error.TakeDescription()]() {
ShowToast(std::string(), fmt::format(TRANSLATE_FS("System", "Failed to save state: {}"), error_desc));
});
}
});
}
void FullscreenUI::PopulateGameListEntryList()
{
const s32 sort = Host::GetBaseIntSettingValue("Main", "FullscreenUIGameSort", 0);

View File

@ -2426,6 +2426,7 @@ void ImGuiFullscreen::PopupDialog::CloseImmediately()
{
ImGui::CloseCurrentPopup();
ClearState();
QueueResetFocus(FocusResetType::PopupClosed);
}
else
{