FullscreenUI: Rewrite popup dialog handling

Add animations, fix background dimming.
This commit is contained in:
Stenzek 2025-03-29 17:30:05 +10:00
parent 576658b6eb
commit 0687e59bda
No known key found for this signature in database
3 changed files with 1460 additions and 1315 deletions

View File

@ -82,6 +82,8 @@ using ImGuiFullscreen::FocusResetType;
using ImGuiFullscreen::LAYOUT_FOOTER_HEIGHT; using ImGuiFullscreen::LAYOUT_FOOTER_HEIGHT;
using ImGuiFullscreen::LAYOUT_HORIZONTAL_MENU_ITEM_IMAGE_SIZE; using ImGuiFullscreen::LAYOUT_HORIZONTAL_MENU_ITEM_IMAGE_SIZE;
using ImGuiFullscreen::LAYOUT_LARGE_FONT_SIZE; using ImGuiFullscreen::LAYOUT_LARGE_FONT_SIZE;
using ImGuiFullscreen::LAYOUT_LARGE_POPUP_PADDING;
using ImGuiFullscreen::LAYOUT_LARGE_POPUP_ROUNDING;
using ImGuiFullscreen::LAYOUT_MEDIUM_FONT_SIZE; using ImGuiFullscreen::LAYOUT_MEDIUM_FONT_SIZE;
using ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT; using ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT;
using ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY; using ImGuiFullscreen::LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY;
@ -89,10 +91,11 @@ using ImGuiFullscreen::LAYOUT_MENU_BUTTON_X_PADDING;
using ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING; using ImGuiFullscreen::LAYOUT_MENU_BUTTON_Y_PADDING;
using ImGuiFullscreen::LAYOUT_SCREEN_HEIGHT; using ImGuiFullscreen::LAYOUT_SCREEN_HEIGHT;
using ImGuiFullscreen::LAYOUT_SCREEN_WIDTH; using ImGuiFullscreen::LAYOUT_SCREEN_WIDTH;
using ImGuiFullscreen::LAYOUT_SMALL_POPUP_PADDING;
using ImGuiFullscreen::UIStyle; using ImGuiFullscreen::UIStyle;
using ImGuiFullscreen::AddNotification; using ImGuiFullscreen::AddNotification;
using ImGuiFullscreen::BeginFixedPopupModal; using ImGuiFullscreen::BeginFixedPopupDialog;
using ImGuiFullscreen::BeginFullscreenColumns; using ImGuiFullscreen::BeginFullscreenColumns;
using ImGuiFullscreen::BeginFullscreenColumnWindow; using ImGuiFullscreen::BeginFullscreenColumnWindow;
using ImGuiFullscreen::BeginFullscreenWindow; using ImGuiFullscreen::BeginFullscreenWindow;
@ -101,8 +104,10 @@ using ImGuiFullscreen::BeginMenuButtons;
using ImGuiFullscreen::BeginNavBar; using ImGuiFullscreen::BeginNavBar;
using ImGuiFullscreen::CancelPendingMenuClose; using ImGuiFullscreen::CancelPendingMenuClose;
using ImGuiFullscreen::CenterImage; using ImGuiFullscreen::CenterImage;
using ImGuiFullscreen::CloseFixedPopupDialog;
using ImGuiFullscreen::CloseFixedPopupDialogImmediately;
using ImGuiFullscreen::DarkerColor; using ImGuiFullscreen::DarkerColor;
using ImGuiFullscreen::EndFixedPopupModal; using ImGuiFullscreen::EndFixedPopupDialog;
using ImGuiFullscreen::EndFullscreenColumns; using ImGuiFullscreen::EndFullscreenColumns;
using ImGuiFullscreen::EndFullscreenColumnWindow; using ImGuiFullscreen::EndFullscreenColumnWindow;
using ImGuiFullscreen::EndFullscreenWindow; using ImGuiFullscreen::EndFullscreenWindow;
@ -116,6 +121,8 @@ using ImGuiFullscreen::GetCachedTexture;
using ImGuiFullscreen::GetCachedTextureAsync; using ImGuiFullscreen::GetCachedTextureAsync;
using ImGuiFullscreen::GetPlaceholderTexture; using ImGuiFullscreen::GetPlaceholderTexture;
using ImGuiFullscreen::HorizontalMenuItem; using ImGuiFullscreen::HorizontalMenuItem;
using ImGuiFullscreen::IsAnyFixedPopupDialogOpen;
using ImGuiFullscreen::IsFixedPopupDialogOpen;
using ImGuiFullscreen::IsFocusResetFromWindowChange; using ImGuiFullscreen::IsFocusResetFromWindowChange;
using ImGuiFullscreen::IsFocusResetQueued; using ImGuiFullscreen::IsFocusResetQueued;
using ImGuiFullscreen::IsGamepadInputSource; using ImGuiFullscreen::IsGamepadInputSource;
@ -137,6 +144,7 @@ using ImGuiFullscreen::NavTitle;
using ImGuiFullscreen::OpenChoiceDialog; using ImGuiFullscreen::OpenChoiceDialog;
using ImGuiFullscreen::OpenConfirmMessageDialog; using ImGuiFullscreen::OpenConfirmMessageDialog;
using ImGuiFullscreen::OpenFileSelector; using ImGuiFullscreen::OpenFileSelector;
using ImGuiFullscreen::OpenFixedPopupDialog;
using ImGuiFullscreen::OpenInfoMessageDialog; using ImGuiFullscreen::OpenInfoMessageDialog;
using ImGuiFullscreen::OpenInputStringDialog; using ImGuiFullscreen::OpenInputStringDialog;
using ImGuiFullscreen::PopPrimaryColor; using ImGuiFullscreen::PopPrimaryColor;
@ -226,7 +234,6 @@ static void DrawPauseMenu();
static void ExitFullscreenAndOpenURL(std::string_view url); static void ExitFullscreenAndOpenURL(std::string_view url);
static void CopyTextToClipboard(std::string title, std::string_view text); static void CopyTextToClipboard(std::string title, std::string_view text);
static void DrawAboutWindow(); static void DrawAboutWindow();
static void OpenAboutWindow();
static void FixStateIfPaused(); static void FixStateIfPaused();
static void GetStandardSelectionFooterText(SmallStringBase& dest, bool back_instead_of_cancel); static void GetStandardSelectionFooterText(SmallStringBase& dest, bool back_instead_of_cancel);
@ -430,7 +437,8 @@ struct SaveStateListEntry
static void InitializePlaceholderSaveStateListEntry(SaveStateListEntry* li, s32 slot, bool global); static void InitializePlaceholderSaveStateListEntry(SaveStateListEntry* li, s32 slot, bool global);
static bool InitializeSaveStateListEntryFromSerial(SaveStateListEntry* li, const std::string& serial, s32 slot, static bool InitializeSaveStateListEntryFromSerial(SaveStateListEntry* li, const std::string& serial, s32 slot,
bool global); bool global);
static bool InitializeSaveStateListEntryFromPath(SaveStateListEntry* li, std::string path, s32 slot, bool global); static bool InitializeSaveStateListEntryFromPath(SaveStateListEntry* li, std::string path, s32 slot, bool global,
std::string* media_path);
static void ClearSaveStateEntryList(); static void ClearSaveStateEntryList();
static u32 PopulateSaveStateListEntries(const std::string& serial, static u32 PopulateSaveStateListEntries(const std::string& serial,
std::optional<ExtendedSaveStateInfo> undo_save_state); std::optional<ExtendedSaveStateInfo> undo_save_state);
@ -492,6 +500,9 @@ static constexpr std::array s_theme_names = {
static constexpr std::array s_theme_values = {"", "Dark", "Light", "AMOLED", "CobaltSky", static constexpr std::array s_theme_values = {"", "Dark", "Light", "AMOLED", "CobaltSky",
"GreyMatter", "GreenGiant", "PinkyPals", "DarkRuby", "PurpleRain"}; "GreyMatter", "GreenGiant", "PinkyPals", "DarkRuby", "PurpleRain"};
static constexpr std::string_view RESUME_STATE_SELECTOR_DIALOG_NAME = "##resume_state_selector";
static constexpr std::string_view ABOUT_DIALOG_NAME = "##about_duckstation";
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// State // State
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -507,8 +518,6 @@ struct ALIGN_TO_CACHE_LINE UIState
bool tried_to_initialize = false; bool tried_to_initialize = false;
bool pause_menu_was_open = false; bool pause_menu_was_open = false;
bool was_paused_on_quick_menu_open = false; bool was_paused_on_quick_menu_open = false;
bool about_window_open = false;
bool achievements_login_window_open = false;
std::string current_game_title; std::string current_game_title;
std::string current_game_serial; std::string current_game_serial;
std::string current_game_path; std::string current_game_path;
@ -562,7 +571,6 @@ struct ALIGN_TO_CACHE_LINE UIState
s32 save_state_selector_submenu_index = -1; s32 save_state_selector_submenu_index = -1;
bool save_state_selector_open = false; bool save_state_selector_open = false;
bool save_state_selector_loading = true; bool save_state_selector_loading = true;
bool save_state_selector_resuming = false;
// Lazily populated cover images. // Lazily populated cover images.
std::unordered_map<std::string, std::string> cover_image_map; std::unordered_map<std::string, std::string> cover_image_map;
@ -755,8 +763,8 @@ bool FullscreenUI::HasActiveWindow()
bool FullscreenUI::AreAnyDialogsOpen() bool FullscreenUI::AreAnyDialogsOpen()
{ {
return (s_state.save_state_selector_open || s_state.about_window_open || return (s_state.save_state_selector_open || s_state.input_binding_type != InputBindingInfo::Type::Unknown ||
s_state.input_binding_type != InputBindingInfo::Type::Unknown || ImGuiFullscreen::IsChoiceDialogOpen() || ImGuiFullscreen::IsAnyFixedPopupDialogOpen() || ImGuiFullscreen::IsChoiceDialogOpen() ||
ImGuiFullscreen::IsInputDialogOpen() || ImGuiFullscreen::IsFileSelectorOpen() || ImGuiFullscreen::IsInputDialogOpen() || ImGuiFullscreen::IsFileSelectorOpen() ||
ImGuiFullscreen::IsMessageBoxDialogOpen()); ImGuiFullscreen::IsMessageBoxDialogOpen());
} }
@ -938,7 +946,6 @@ void FullscreenUI::Shutdown(bool clear_state)
s_state.current_pause_submenu = PauseSubMenu::None; s_state.current_pause_submenu = PauseSubMenu::None;
s_state.pause_menu_was_open = false; s_state.pause_menu_was_open = false;
s_state.was_paused_on_quick_menu_open = false; s_state.was_paused_on_quick_menu_open = false;
s_state.about_window_open = false;
Achievements::ClearUIState(); Achievements::ClearUIState();
ClearInputBindingVariables(); ClearInputBindingVariables();
@ -1027,18 +1034,13 @@ void FullscreenUI::Render()
break; break;
} }
if (s_state.save_state_selector_open) if (IsFixedPopupDialogOpen(ABOUT_DIALOG_NAME))
{
if (s_state.save_state_selector_resuming)
DrawResumeStateSelector();
else
DrawSaveStateSelector(s_state.save_state_selector_loading);
}
if (s_state.about_window_open)
DrawAboutWindow(); DrawAboutWindow();
else if (IsFixedPopupDialogOpen(RESUME_STATE_SELECTOR_DIALOG_NAME))
if (s_state.input_binding_type != InputBindingInfo::Type::Unknown) 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(); DrawInputBindingWindow();
ImGuiFullscreen::EndLayout(); ImGuiFullscreen::EndLayout();
@ -1253,17 +1255,17 @@ void FullscreenUI::DoResume()
return; return;
} }
SaveStateListEntry slentry;
if (!InitializeSaveStateListEntryFromPath(&slentry, std::move(path), -1, false))
return;
CloseSaveStateSelector(); CloseSaveStateSelector();
SaveStateListEntry slentry;
if (!InitializeSaveStateListEntryFromPath(&slentry, std::move(path), -1, false,
&s_state.save_state_selector_game_path))
{
return;
}
s_state.save_state_selector_slots.push_back(std::move(slentry)); s_state.save_state_selector_slots.push_back(std::move(slentry));
s_state.save_state_selector_game_path = {}; OpenFixedPopupDialog(RESUME_STATE_SELECTOR_DIALOG_NAME);
s_state.save_state_selector_loading = true;
s_state.save_state_selector_open = true;
s_state.save_state_selector_resuming = true;
QueueResetFocus(FocusResetType::PopupOpened);
} }
void FullscreenUI::DoStartFile() void FullscreenUI::DoStartFile()
@ -1969,7 +1971,7 @@ void FullscreenUI::DrawLandingWindow()
if (!AreAnyDialogsOpen()) if (!AreAnyDialogsOpen())
{ {
if (ImGui::IsKeyPressed(ImGuiKey_GamepadBack, false) || ImGui::IsKeyPressed(ImGuiKey_F1, false)) if (ImGui::IsKeyPressed(ImGuiKey_GamepadBack, false) || ImGui::IsKeyPressed(ImGuiKey_F1, false))
OpenAboutWindow(); OpenFixedPopupDialog(ABOUT_DIALOG_NAME);
else if (ImGui::IsKeyPressed(ImGuiKey_GamepadStart, false) || ImGui::IsKeyPressed(ImGuiKey_F3, false)) else if (ImGui::IsKeyPressed(ImGuiKey_GamepadStart, false) || ImGui::IsKeyPressed(ImGuiKey_F3, false))
DoResume(); DoResume();
else if (ImGui::IsKeyPressed(ImGuiKey_NavGamepadMenu, false) || ImGui::IsKeyPressed(ImGuiKey_F11, false)) else if (ImGui::IsKeyPressed(ImGuiKey_NavGamepadMenu, false) || ImGui::IsKeyPressed(ImGuiKey_F11, false))
@ -2323,6 +2325,9 @@ void FullscreenUI::ClearInputBindingVariables()
s_state.input_binding_display_name = {}; s_state.input_binding_display_name = {};
s_state.input_binding_new_bindings = {}; s_state.input_binding_new_bindings = {};
s_state.input_binding_value_ranges = {}; s_state.input_binding_value_ranges = {};
if (IsFixedPopupDialogOpen(FSUI_ICONSTR(ICON_FA_GAMEPAD, "Set Input Binding")))
CloseFixedPopupDialogImmediately();
} }
void FullscreenUI::BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::Type type, std::string_view section, void FullscreenUI::BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::Type type, std::string_view section,
@ -2341,6 +2346,7 @@ void FullscreenUI::BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::T
s_state.input_binding_new_bindings = {}; s_state.input_binding_new_bindings = {};
s_state.input_binding_value_ranges = {}; s_state.input_binding_value_ranges = {};
s_state.input_binding_start_time = Timer::GetCurrentValue(); s_state.input_binding_start_time = Timer::GetCurrentValue();
OpenFixedPopupDialog(FSUI_ICONSTR(ICON_FA_GAMEPAD, "Set Input Binding"));
const bool game_settings = IsEditingGameSettings(bsi); const bool game_settings = IsEditingGameSettings(bsi);
@ -2396,8 +2402,9 @@ void FullscreenUI::BeginInputBinding(SettingsInterface* bsi, InputBindingInfo::T
bsi->SetStringValue(s_state.input_binding_section.c_str(), s_state.input_binding_key.c_str(), bsi->SetStringValue(s_state.input_binding_section.c_str(), s_state.input_binding_key.c_str(),
new_binding.c_str()); new_binding.c_str());
SetSettingsChanged(bsi); SetSettingsChanged(bsi);
ClearInputBindingVariables();
QueueResetFocus(FocusResetType::PopupClosed); GPUThread::RunOnThread(&CloseFixedPopupDialog);
return InputInterceptHook::CallbackResult::RemoveHookAndStopProcessingEvent; return InputInterceptHook::CallbackResult::RemoveHookAndStopProcessingEvent;
} }
@ -2474,27 +2481,27 @@ void FullscreenUI::DrawInputBindingWindow()
INPUT_BINDING_TIMEOUT_SECONDS - INPUT_BINDING_TIMEOUT_SECONDS -
Timer::ConvertValueToSeconds(Timer::GetCurrentValue() - s_state.input_binding_start_time); Timer::ConvertValueToSeconds(Timer::GetCurrentValue() - s_state.input_binding_start_time);
if (time_remaining <= 0.0) if (time_remaining <= 0.0)
{
InputManager::RemoveHook();
CloseFixedPopupDialog();
}
if (!BeginFixedPopupDialog(LayoutScale(LAYOUT_SMALL_POPUP_PADDING), LayoutScale(LAYOUT_SMALL_POPUP_PADDING),
LayoutScale(500.0f, 0.0f)))
{ {
InputManager::RemoveHook(); InputManager::RemoveHook();
ClearInputBindingVariables(); ClearInputBindingVariables();
QueueResetFocus(FocusResetType::PopupClosed);
return; return;
} }
const char* title = FSUI_ICONSTR(ICON_FA_GAMEPAD, "Set Input Binding"); ImGui::TextWrapped("%s", SmallString::from_format(FSUI_FSTR("Setting {} binding {}."), s_state.input_binding_section,
ImGui::SetNextWindowSize(LayoutScale(500.0f, 0.0f)); s_state.input_binding_display_name)
ImGui::OpenPopup(title);
if (BeginFixedPopupModal(title))
{
ImGui::TextWrapped("%s", SmallString::from_format(FSUI_FSTR("Setting {} binding {}."),
s_state.input_binding_section, s_state.input_binding_display_name)
.c_str()); .c_str());
ImGui::TextUnformatted(FSUI_CSTR("Push a controller button or axis now.")); ImGui::TextUnformatted(FSUI_CSTR("Push a controller button or axis now."));
ImGui::NewLine(); ImGui::NewLine();
ImGui::TextUnformatted(SmallString::from_format(FSUI_FSTR("Timing out in {:.0f} seconds..."), time_remaining)); ImGui::TextUnformatted(SmallString::from_format(FSUI_FSTR("Timing out in {:.0f} seconds..."), time_remaining));
EndFixedPopupModal();
} EndFixedPopupDialog();
} }
bool FullscreenUI::DrawToggleSetting(SettingsInterface* bsi, const char* title, const char* summary, bool FullscreenUI::DrawToggleSetting(SettingsInterface* bsi, const char* title, const char* summary,
@ -2661,18 +2668,18 @@ void FullscreenUI::DrawIntRangeSetting(SettingsInterface* bsi, const char* title
value.has_value() ? SmallString::from_sprintf(format, value.value()) : SmallString(FSUI_VSTR("Use Global Setting")); value.has_value() ? SmallString::from_sprintf(format, value.value()) : SmallString(FSUI_VSTR("Use Global Setting"));
if (MenuButtonWithValue(title, summary, value_text.c_str(), enabled, height, font, summary_font)) if (MenuButtonWithValue(title, summary, value_text.c_str(), enabled, height, font, summary_font))
ImGui::OpenPopup(title); OpenFixedPopupDialog(title);
ImGui::SetNextWindowSize(LayoutScale(500.0f, 194.0f)); if (!IsFixedPopupDialogOpen(title) ||
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); !BeginFixedPopupDialog(LayoutScale(LAYOUT_SMALL_POPUP_PADDING), LayoutScale(LAYOUT_SMALL_POPUP_PADDING),
LayoutScale(500.0f, 194.0f)))
bool is_open = true;
if (BeginFixedPopupModal(title, &is_open))
{ {
return;
}
BeginMenuButtons(); BeginMenuButtons();
const float end = ImGui::GetCurrentWindow()->WorkRect.GetWidth(); ImGui::SetNextItemWidth(ImGui::GetCurrentWindow()->WorkRect.GetWidth());
ImGui::SetNextItemWidth(end);
s32 dlg_value = static_cast<s32>(value.value_or(default_value)); s32 dlg_value = static_cast<s32>(value.value_or(default_value));
if (ImGui::SliderInt("##value", &dlg_value, min_value, max_value, format, ImGuiSliderFlags_NoInput)) if (ImGui::SliderInt("##value", &dlg_value, min_value, max_value, format, ImGuiSliderFlags_NoInput))
{ {
@ -2688,12 +2695,11 @@ void FullscreenUI::DrawIntRangeSetting(SettingsInterface* bsi, const char* title
if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont,
ImVec2(0.5f, 0.0f))) ImVec2(0.5f, 0.0f)))
{ {
ImGui::CloseCurrentPopup(); CloseFixedPopupDialog();
} }
EndMenuButtons(); EndMenuButtons();
EndFixedPopupModal(); EndFixedPopupDialog();
}
} }
void FullscreenUI::DrawFloatRangeSetting(SettingsInterface* bsi, const char* title, const char* summary, void FullscreenUI::DrawFloatRangeSetting(SettingsInterface* bsi, const char* title, const char* summary,
@ -2708,13 +2714,15 @@ void FullscreenUI::DrawFloatRangeSetting(SettingsInterface* bsi, const char* tit
SmallString(FSUI_VSTR("Use Global Setting")); SmallString(FSUI_VSTR("Use Global Setting"));
if (MenuButtonWithValue(title, summary, value_text.c_str(), enabled, height, font, summary_font)) if (MenuButtonWithValue(title, summary, value_text.c_str(), enabled, height, font, summary_font))
ImGui::OpenPopup(title); OpenFixedPopupDialog(title);
ImGui::SetNextWindowSize(LayoutScale(500.0f, 194.0f)); if (!IsFixedPopupDialogOpen(title) ||
!BeginFixedPopupDialog(LayoutScale(LAYOUT_SMALL_POPUP_PADDING), LayoutScale(LAYOUT_SMALL_POPUP_PADDING),
bool is_open = true; LayoutScale(500.0f, 194.0f)))
if (BeginFixedPopupModal(title, &is_open))
{ {
return;
}
BeginMenuButtons(); BeginMenuButtons();
const float end = ImGui::GetCurrentWindow()->WorkRect.GetWidth(); const float end = ImGui::GetCurrentWindow()->WorkRect.GetWidth();
@ -2737,12 +2745,11 @@ void FullscreenUI::DrawFloatRangeSetting(SettingsInterface* bsi, const char* tit
if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont,
ImVec2(0.5f, 0.0f))) ImVec2(0.5f, 0.0f)))
{ {
ImGui::CloseCurrentPopup(); CloseFixedPopupDialog();
} }
EndMenuButtons(); EndMenuButtons();
EndFixedPopupModal(); EndFixedPopupDialog();
}
} }
void FullscreenUI::DrawFloatSpinBoxSetting(SettingsInterface* bsi, const char* title, const char* summary, void FullscreenUI::DrawFloatSpinBoxSetting(SettingsInterface* bsi, const char* title, const char* summary,
@ -2760,15 +2767,17 @@ void FullscreenUI::DrawFloatSpinBoxSetting(SettingsInterface* bsi, const char* t
if (MenuButtonWithValue(title, summary, value_text.c_str(), enabled, height, font, summary_font)) if (MenuButtonWithValue(title, summary, value_text.c_str(), enabled, height, font, summary_font))
{ {
ImGui::OpenPopup(title); OpenFixedPopupDialog(title);
manual_input = false; manual_input = false;
} }
ImGui::SetNextWindowSize(LayoutScale(500.0f, 194.0f)); if (!IsFixedPopupDialogOpen(title) ||
!BeginFixedPopupDialog(LayoutScale(LAYOUT_SMALL_POPUP_PADDING), LayoutScale(LAYOUT_SMALL_POPUP_PADDING),
bool is_open = true; LayoutScale(500.0f, 194.0f)))
if (BeginFixedPopupModal(title, &is_open))
{ {
return;
}
BeginMenuButtons(); BeginMenuButtons();
float dlg_value = value.value_or(default_value) * multiplier; float dlg_value = value.value_or(default_value) * multiplier;
@ -2837,8 +2846,7 @@ void FullscreenUI::DrawFloatSpinBoxSetting(SettingsInterface* bsi, const char* t
dlg_value_changed = true; dlg_value_changed = true;
} }
ImGui::SetCursorPosY(button_pos.y + (padding.y * 2.0f) + ImGui::SetCursorPosY(button_pos.y + (padding.y * 2.0f) + LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY + 10.0f));
LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY + 10.0f));
} }
if (dlg_value_changed) if (dlg_value_changed)
@ -2855,12 +2863,11 @@ void FullscreenUI::DrawFloatSpinBoxSetting(SettingsInterface* bsi, const char* t
if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont,
ImVec2(0.5f, 0.0f))) ImVec2(0.5f, 0.0f)))
{ {
ImGui::CloseCurrentPopup(); CloseFixedPopupDialog();
} }
EndMenuButtons(); EndMenuButtons();
EndFixedPopupModal(); EndFixedPopupDialog();
}
} }
bool FullscreenUI::DrawIntRectSetting(SettingsInterface* bsi, const char* title, const char* summary, bool FullscreenUI::DrawIntRectSetting(SettingsInterface* bsi, const char* title, const char* summary,
@ -2887,18 +2894,20 @@ bool FullscreenUI::DrawIntRectSetting(SettingsInterface* bsi, const char* title,
TinyString(FSUI_VSTR("Default"))); TinyString(FSUI_VSTR("Default")));
if (MenuButtonWithValue(title, summary, value_text.c_str(), enabled, height, font, summary_font)) if (MenuButtonWithValue(title, summary, value_text.c_str(), enabled, height, font, summary_font))
ImGui::OpenPopup(title); OpenFixedPopupDialog(title);
ImGui::SetNextWindowSize(LayoutScale(500.0f, 370.0f)); if (!IsFixedPopupDialogOpen(title) ||
!BeginFixedPopupDialog(LayoutScale(LAYOUT_SMALL_POPUP_PADDING), LayoutScale(LAYOUT_SMALL_POPUP_PADDING),
bool is_open = true; LayoutScale(500.0f, 370.0f)))
bool changed = false;
if (BeginFixedPopupModal(title, &is_open))
{ {
return false;
}
s32 dlg_left_value = static_cast<s32>(left_value.value_or(default_left)); s32 dlg_left_value = static_cast<s32>(left_value.value_or(default_left));
s32 dlg_top_value = static_cast<s32>(top_value.value_or(default_top)); s32 dlg_top_value = static_cast<s32>(top_value.value_or(default_top));
s32 dlg_right_value = static_cast<s32>(right_value.value_or(default_right)); s32 dlg_right_value = static_cast<s32>(right_value.value_or(default_right));
s32 dlg_bottom_value = static_cast<s32>(bottom_value.value_or(default_bottom)); s32 dlg_bottom_value = static_cast<s32>(bottom_value.value_or(default_bottom));
bool changed = false;
BeginMenuButtons(); BeginMenuButtons();
@ -2964,12 +2973,11 @@ bool FullscreenUI::DrawIntRectSetting(SettingsInterface* bsi, const char* title,
if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont,
ImVec2(0.5f, 0.0f))) ImVec2(0.5f, 0.0f)))
{ {
ImGui::CloseCurrentPopup(); CloseFixedPopupDialog();
} }
EndMenuButtons(); EndMenuButtons();
EndFixedPopupModal(); EndFixedPopupDialog();
}
return changed; return changed;
} }
@ -2992,15 +3000,17 @@ void FullscreenUI::DrawIntSpinBoxSetting(SettingsInterface* bsi, const char* tit
if (MenuButtonWithValue(title, summary, value_text, enabled, height, font, summary_font)) if (MenuButtonWithValue(title, summary, value_text, enabled, height, font, summary_font))
{ {
ImGui::OpenPopup(title); OpenFixedPopupDialog(title);
manual_input = false; manual_input = false;
} }
ImGui::SetNextWindowSize(LayoutScale(500.0f, 194.0f)); if (!IsFixedPopupDialogOpen(title) ||
!BeginFixedPopupDialog(LayoutScale(LAYOUT_SMALL_POPUP_PADDING), LayoutScale(LAYOUT_SMALL_POPUP_PADDING),
bool is_open = true; LayoutScale(500.0f, 194.0f)))
if (BeginFixedPopupModal(title, &is_open))
{ {
return;
}
BeginMenuButtons(); BeginMenuButtons();
s32 dlg_value = static_cast<s32>(value.value_or(default_value)); s32 dlg_value = static_cast<s32>(value.value_or(default_value));
@ -3063,8 +3073,7 @@ void FullscreenUI::DrawIntSpinBoxSetting(SettingsInterface* bsi, const char* tit
dlg_value_changed = true; dlg_value_changed = true;
} }
ImGui::SetCursorPosY(button_pos.y + (padding.y * 2.0f) + ImGui::SetCursorPosY(button_pos.y + (padding.y * 2.0f) + LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY + 10.0f));
LayoutScale(LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY + 10.0f));
} }
if (dlg_value_changed) if (dlg_value_changed)
@ -3081,12 +3090,11 @@ void FullscreenUI::DrawIntSpinBoxSetting(SettingsInterface* bsi, const char* tit
if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont,
ImVec2(0.5f, 0.0f))) ImVec2(0.5f, 0.0f)))
{ {
ImGui::CloseCurrentPopup(); CloseFixedPopupDialog();
} }
EndMenuButtons(); EndMenuButtons();
EndFixedPopupModal(); EndFixedPopupDialog();
}
} }
[[maybe_unused]] void [[maybe_unused]] void
@ -4737,7 +4745,7 @@ void FullscreenUI::DrawControllerSettingsPage()
"Determines the frequency at which the macro will toggle the buttons on and off (aka auto fire)."), "Determines the frequency at which the macro will toggle the buttons on and off (aka auto fire)."),
freq_summary, true)) freq_summary, true))
{ {
ImGui::OpenPopup(freq_label.c_str()); OpenFixedPopupDialog(freq_label);
} }
DrawFloatSpinBoxSetting(bsi, FSUI_ICONSTR(ICON_FA_ARROW_DOWN, "Pressure"), DrawFloatSpinBoxSetting(bsi, FSUI_ICONSTR(ICON_FA_ARROW_DOWN, "Pressure"),
@ -4750,13 +4758,15 @@ void FullscreenUI::DrawControllerSettingsPage()
section, TinyString::from_format("Macro{}Deadzone", macro_index + 1).c_str(), 0.0f, 0.00f, section, TinyString::from_format("Macro{}Deadzone", macro_index + 1).c_str(), 0.0f, 0.00f,
1.0f, 0.01f, 100.0f, "%.0f%%"); 1.0f, 0.01f, 100.0f, "%.0f%%");
ImGui::SetNextWindowSize(LayoutScale(500.0f, 180.0f)); if (IsFixedPopupDialogOpen(freq_label) &&
BeginFixedPopupDialog(LayoutScale(LAYOUT_SMALL_POPUP_PADDING), LayoutScale(LAYOUT_SMALL_POPUP_PADDING),
bool is_open = true; LayoutScale(500.0f, 194.0f)))
if (BeginFixedPopupModal(freq_label, &is_open))
{ {
ImGui::SetNextItemWidth(LayoutScale(450.0f)); BeginMenuButtons();
if (ImGui::SliderInt("##value", &frequency, 0, 60, FSUI_CSTR("Toggle every %d frames"),
ImGui::SetNextItemWidth(ImGui::GetCurrentWindow()->WorkRect.GetWidth());
if (ImGui::SliderInt("##value", &frequency, 0, 60,
(frequency == 0) ? FSUI_CSTR("Disabled") : FSUI_CSTR("Toggle every %d frames"),
ImGuiSliderFlags_NoInput)) ImGuiSliderFlags_NoInput))
{ {
if (frequency == 0) if (frequency == 0)
@ -4765,12 +4775,16 @@ void FullscreenUI::DrawControllerSettingsPage()
bsi->SetIntValue(section.c_str(), freq_key.c_str(), frequency); bsi->SetIntValue(section.c_str(), freq_key.c_str(), frequency);
} }
BeginMenuButtons(); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
if (MenuButton(FSUI_CSTR("OK"), nullptr, true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY)) if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont,
ImGui::CloseCurrentPopup(); ImVec2(0.5f, 0.0f)))
{
CloseFixedPopupDialog();
}
EndMenuButtons(); EndMenuButtons();
EndFixedPopupModal(); EndFixedPopupDialog();
} }
} }
@ -5573,34 +5587,16 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
str.format(FSUI_FSTR("Value: {} | Default: {} | Minimum: {} | Maximum: {}"), opt.value[0].float_value, str.format(FSUI_FSTR("Value: {} | Default: {} | Minimum: {} | Maximum: {}"), opt.value[0].float_value,
opt.default_value[0].float_value, opt.min_value[0].float_value, opt.max_value[0].float_value); opt.default_value[0].float_value, opt.min_value[0].float_value, opt.max_value[0].float_value);
if (MenuButton(tstr, str)) if (MenuButton(tstr, str))
ImGui::OpenPopup(tstr); OpenFixedPopupDialog(tstr);
ImGui::SetNextWindowSize(LayoutScale(500.0f, 194.0f)); if (IsFixedPopupDialogOpen(tstr) &&
BeginFixedPopupDialog(LayoutScale(LAYOUT_SMALL_POPUP_PADDING), LayoutScale(LAYOUT_SMALL_POPUP_PADDING),
bool is_open = true; LayoutScale(500.0f, 194.0f)))
if (BeginFixedPopupModal(tstr, &is_open))
{ {
BeginMenuButtons(); BeginMenuButtons();
const float end = ImGui::GetCurrentWindow()->WorkRect.GetWidth(); const float end = ImGui::GetCurrentWindow()->WorkRect.GetWidth();
#if 0
for (u32 i = 0; i < opt.vector_size; i++)
{
static constexpr const char* components[] = { "X", "Y", "Z", "W" };
if (opt.vector_size == 1)
tstr.Assign("##value");
else
tstr.Fmt("{}##value{}", components[i], i);
ImGui::SetNextItemWidth(end);
if (ImGui::SliderFloat(tstr, &opt.value[i].float_value, opt.min_value[i].float_value,
opt.max_value[i].float_value, "%f", ImGuiSliderFlags_NoInput))
{
SavePostProcessingChain();
}
}
#else
ImGui::SetNextItemWidth(end); ImGui::SetNextItemWidth(end);
bool changed = false; bool changed = false;
@ -5641,17 +5637,17 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
SetSettingsChanged(bsi); SetSettingsChanged(bsi);
reload_pending = true; reload_pending = true;
} }
#endif
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont,
ImVec2(0.5f, 0.0f))) ImVec2(0.5f, 0.0f)))
{ {
ImGui::CloseCurrentPopup(); CloseFixedPopupDialog();
} }
EndMenuButtons(); EndMenuButtons();
EndFixedPopupModal(); EndFixedPopupDialog();
} }
} }
break; break;
@ -5662,34 +5658,15 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
str.format(FSUI_FSTR("Value: {} | Default: {} | Minimum: {} | Maximum: {}"), opt.value[0].int_value, str.format(FSUI_FSTR("Value: {} | Default: {} | Minimum: {} | Maximum: {}"), opt.value[0].int_value,
opt.default_value[0].int_value, opt.min_value[0].int_value, opt.max_value[0].int_value); opt.default_value[0].int_value, opt.min_value[0].int_value, opt.max_value[0].int_value);
if (MenuButton(tstr, str)) if (MenuButton(tstr, str))
ImGui::OpenPopup(tstr); OpenFixedPopupDialog(tstr);
ImGui::SetNextWindowSize(LayoutScale(500.0f, 194.0f)); if (IsFixedPopupDialogOpen(tstr) &&
BeginFixedPopupDialog(LayoutScale(LAYOUT_SMALL_POPUP_PADDING), LayoutScale(LAYOUT_SMALL_POPUP_PADDING),
bool is_open = true; LayoutScale(500.0f, 194.0f)))
if (BeginFixedPopupModal(tstr, &is_open))
{ {
BeginMenuButtons(); BeginMenuButtons();
const float end = ImGui::GetCurrentWindow()->WorkRect.GetWidth(); const float end = ImGui::GetCurrentWindow()->WorkRect.GetWidth();
#if 0
for (u32 i = 0; i < opt.vector_size; i++)
{
static constexpr const char* components[] = { "X", "Y", "Z", "W" };
if (opt.vector_size == 1)
tstr.Assign("##value");
else
tstr.Fmt("{}##value{}", components[i], i);
ImGui::SetNextItemWidth(end);
if (ImGui::SliderInt(tstr, &opt.value[i].int_value, opt.min_value[i].int_value,
opt.max_value[i].int_value, "%d", ImGuiSliderFlags_NoInput))
{
SavePostProcessingChain();
}
}
#else
bool changed = false; bool changed = false;
ImGui::SetNextItemWidth(end); ImGui::SetNextItemWidth(end);
switch (opt.vector_size) switch (opt.vector_size)
@ -5729,17 +5706,16 @@ void FullscreenUI::DrawPostProcessingSettingsPage()
SetSettingsChanged(bsi); SetSettingsChanged(bsi);
reload_pending = true; reload_pending = true;
} }
#endif
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f)); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + LayoutScale(10.0f));
if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont,
ImVec2(0.5f, 0.0f))) ImVec2(0.5f, 0.0f)))
{ {
ImGui::CloseCurrentPopup(); CloseFixedPopupDialog();
} }
EndMenuButtons(); EndMenuButtons();
EndFixedPopupModal(); EndFixedPopupDialog();
} }
} }
break; break;
@ -6022,13 +5998,12 @@ void FullscreenUI::DrawAchievementsSettingsPage()
{ {
MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_USER, "Not Logged In"), false); MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_USER, "Not Logged In"), false);
if (MenuButton(FSUI_ICONSTR(ICON_FA_KEY, "Login"), FSUI_CSTR("Logs in to RetroAchievements."))) static constexpr std::string_view popup_title = "##achievements_login";
{
s_state.achievements_login_window_open = true;
QueueResetFocus(FocusResetType::PopupOpened);
}
if (s_state.achievements_login_window_open) if (MenuButton(FSUI_ICONSTR(ICON_FA_KEY, "Login"), FSUI_CSTR("Logs in to RetroAchievements.")))
OpenFixedPopupDialog(popup_title);
if (IsFixedPopupDialogOpen(popup_title))
DrawAchievementsLoginWindow(); DrawAchievementsLoginWindow();
} }
@ -6065,26 +6040,29 @@ void FullscreenUI::DrawAchievementsLoginWindow()
static char username[256] = {}; static char username[256] = {};
static char password[256] = {}; static char password[256] = {};
static std::unique_ptr<std::string> login_error;
static constexpr auto actually_close_popup = []() { if (!BeginFixedPopupDialog(LayoutScale(LAYOUT_LARGE_POPUP_PADDING), LayoutScale(LAYOUT_LARGE_POPUP_ROUNDING),
LayoutScale(600.0f, 0.0f)))
{
std::memset(username, 0, sizeof(username)); std::memset(username, 0, sizeof(username));
std::memset(password, 0, sizeof(password)); std::memset(password, 0, sizeof(password));
s_state.achievements_login_window_open = false; login_error.reset();
QueueResetFocus(FocusResetType::PopupClosed); return;
}; }
ImGui::SetNextWindowSize(LayoutScale(600.0f, 0.0f));
const char* popup_title = FSUI_ICONSTR(ICON_FA_KEY, "RetroAchievements Login");
bool popup_closed = false;
ImGui::OpenPopup(popup_title);
if (BeginFixedPopupModal(popup_title, nullptr))
{
BeginMenuButtons(); BeginMenuButtons();
if (!login_error)
{
ImGui::TextWrapped( ImGui::TextWrapped(
FSUI_CSTR("Please enter your user name and password for retroachievements.org below. Your password will " FSUI_CSTR("Please enter your user name and password for retroachievements.org below. Your password will "
"not be saved in DuckStation, an access token will be generated and used instead.")); "not be saved in DuckStation, an access token will be generated and used instead."));
}
else
{
ImGui::TextWrapped("%s", login_error->c_str());
}
ImGui::NewLine(); ImGui::NewLine();
@ -6124,40 +6102,23 @@ void FullscreenUI::DrawAchievementsLoginWindow()
if (result) if (result)
{ {
actually_close_popup(); CloseFixedPopupDialog();
return; return;
} }
// keep popup open on failure login_error = std::make_unique<std::string>(
// because of the whole popup stack thing, we need to hide the dialog while this popup is visible
s_state.achievements_login_window_open = false;
ImGuiFullscreen::OpenInfoMessageDialog(
FSUI_STR("Login Error"),
fmt::format(FSUI_FSTR("Login Failed.\nError: {}\nPlease check your username and password, and try again."), fmt::format(FSUI_FSTR("Login Failed.\nError: {}\nPlease check your username and password, and try again."),
error.GetDescription()), error.GetDescription()));
[]() {
s_state.achievements_login_window_open = true;
QueueResetFocus(FocusResetType::PopupOpened);
},
FSUI_ICONSTR(ICON_FA_TIMES, "Close"));
}); });
}); });
} }
if (MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_TIMES, "Cancel"), !is_logging_in)) if (MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_TIMES, "Cancel"), !is_logging_in))
popup_closed = true; CloseFixedPopupDialog();
popup_closed = popup_closed || (!is_logging_in && WantsToCloseMenu());
if (popup_closed)
ImGui::CloseCurrentPopup();
EndMenuButtons(); EndMenuButtons();
EndFixedPopupModal(); EndFixedPopupDialog();
}
if (popup_closed)
actually_close_popup();
} }
void FullscreenUI::DrawAdvancedSettingsPage() void FullscreenUI::DrawAdvancedSettingsPage()
@ -6324,12 +6285,11 @@ void FullscreenUI::DrawPatchesOrCheatsSettingsPage(bool cheats)
constexpr s32 step_value = 1; constexpr s32 step_value = 1;
if (MenuButtonWithValue(title.c_str(), ci.description.c_str(), visible_value.c_str())) if (MenuButtonWithValue(title.c_str(), ci.description.c_str(), visible_value.c_str()))
ImGui::OpenPopup(title); OpenFixedPopupDialog(title);
ImGui::SetNextWindowSize(LayoutScale(500.0f, 194.0f)); if (IsFixedPopupDialogOpen(title) &&
BeginFixedPopupDialog(LayoutScale(LAYOUT_SMALL_POPUP_PADDING), LayoutScale(LAYOUT_SMALL_POPUP_PADDING),
bool is_open = true; LayoutScale(500.0f, 194.0f)))
if (BeginFixedPopupModal(title, &is_open))
{ {
BeginMenuButtons(); BeginMenuButtons();
@ -6396,11 +6356,11 @@ void FullscreenUI::DrawPatchesOrCheatsSettingsPage(bool cheats)
if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont, if (MenuButtonWithoutSummary(FSUI_CSTR("OK"), true, LAYOUT_MENU_BUTTON_HEIGHT_NO_SUMMARY, UIStyle.LargeFont,
ImVec2(0.5f, 0.0f))) ImVec2(0.5f, 0.0f)))
{ {
ImGui::CloseCurrentPopup(); CloseFixedPopupDialog();
} }
EndMenuButtons(); EndMenuButtons();
EndFixedPopupModal(); EndFixedPopupDialog();
} }
} }
else else
@ -6776,7 +6736,7 @@ bool FullscreenUI::InitializeSaveStateListEntryFromSerial(SaveStateListEntry* li
{ {
const std::string path = const std::string path =
(global ? System::GetGlobalSaveStateFileName(slot) : System::GetGameSaveStateFileName(serial, slot)); (global ? System::GetGlobalSaveStateFileName(slot) : System::GetGameSaveStateFileName(serial, slot));
if (!InitializeSaveStateListEntryFromPath(li, path.c_str(), slot, global)) if (!InitializeSaveStateListEntryFromPath(li, path.c_str(), slot, global, nullptr))
{ {
InitializePlaceholderSaveStateListEntry(li, slot, global); InitializePlaceholderSaveStateListEntry(li, slot, global);
return false; return false;
@ -6785,7 +6745,8 @@ bool FullscreenUI::InitializeSaveStateListEntryFromSerial(SaveStateListEntry* li
return true; return true;
} }
bool FullscreenUI::InitializeSaveStateListEntryFromPath(SaveStateListEntry* li, std::string path, s32 slot, bool global) bool FullscreenUI::InitializeSaveStateListEntryFromPath(SaveStateListEntry* li, std::string path, s32 slot, bool global,
std::string* media_path)
{ {
std::optional<ExtendedSaveStateInfo> ssi(System::GetExtendedSaveStateInfo(path.c_str())); std::optional<ExtendedSaveStateInfo> ssi(System::GetExtendedSaveStateInfo(path.c_str()));
if (!ssi.has_value()) if (!ssi.has_value())
@ -6807,6 +6768,8 @@ bool FullscreenUI::InitializeSaveStateListEntryFromPath(SaveStateListEntry* li,
li->global = global; li->global = global;
if (ssi->screenshot.IsValid()) if (ssi->screenshot.IsValid())
li->preview_texture = g_gpu_device->FetchAndUploadTextureImage(ssi->screenshot); li->preview_texture = g_gpu_device->FetchAndUploadTextureImage(ssi->screenshot);
if (media_path)
*media_path = std::move(ssi->media_path);
return true; return true;
} }
@ -6865,7 +6828,6 @@ void FullscreenUI::OpenSaveStateSelector(const std::string& serial, const std::s
std::optional<ExtendedSaveStateInfo> undo_state = System::GetUndoSaveStateInfo(); std::optional<ExtendedSaveStateInfo> undo_state = System::GetUndoSaveStateInfo();
GPUThread::RunOnThread([serial = std::move(serial), undo_state = std::move(undo_state), is_loading]() mutable { 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_loading = is_loading;
s_state.save_state_selector_resuming = false;
s_state.save_state_selector_game_path = {}; s_state.save_state_selector_game_path = {};
if (PopulateSaveStateListEntries(serial, std::move(undo_state)) > 0) if (PopulateSaveStateListEntries(serial, std::move(undo_state)) > 0)
{ {
@ -6883,7 +6845,6 @@ void FullscreenUI::OpenSaveStateSelector(const std::string& serial, const std::s
else else
{ {
s_state.save_state_selector_loading = is_loading; s_state.save_state_selector_loading = is_loading;
s_state.save_state_selector_resuming = false;
if (PopulateSaveStateListEntries(serial, std::nullopt) > 0) if (PopulateSaveStateListEntries(serial, std::nullopt) > 0)
{ {
s_state.save_state_selector_game_path = path; s_state.save_state_selector_game_path = path;
@ -6905,8 +6866,10 @@ void FullscreenUI::CloseSaveStateSelector()
ClearSaveStateEntryList(); ClearSaveStateEntryList();
s_state.save_state_selector_open = false; s_state.save_state_selector_open = false;
s_state.save_state_selector_loading = false; s_state.save_state_selector_loading = false;
s_state.save_state_selector_resuming = false;
s_state.save_state_selector_game_path = {}; s_state.save_state_selector_game_path = {};
if (IsFixedPopupDialogOpen(RESUME_STATE_SELECTOR_DIALOG_NAME))
CloseFixedPopupDialogImmediately();
} }
void FullscreenUI::DrawSaveStateSelector(bool is_loading) void FullscreenUI::DrawSaveStateSelector(bool is_loading)
@ -7240,27 +7203,18 @@ bool FullscreenUI::OpenLoadStateSelectorForGameResume(const GameList::Entry* ent
CloseSaveStateSelector(); CloseSaveStateSelector();
s_state.save_state_selector_slots.push_back(std::move(slentry)); s_state.save_state_selector_slots.push_back(std::move(slentry));
s_state.save_state_selector_game_path = entry->path; s_state.save_state_selector_game_path = entry->path;
s_state.save_state_selector_loading = true; OpenFixedPopupDialog(RESUME_STATE_SELECTOR_DIALOG_NAME);
s_state.save_state_selector_open = true;
s_state.save_state_selector_resuming = true;
QueueResetFocus(FocusResetType::PopupOpened);
return true; return true;
} }
void FullscreenUI::DrawResumeStateSelector() void FullscreenUI::DrawResumeStateSelector()
{ {
ImGui::SetNextWindowSize(LayoutScale(820.0f, 625.0f)); if (!BeginFixedPopupDialog(LayoutScale(30.0f), LayoutScale(40.0f), LayoutScale(820.0f, 625.0f)))
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f));
ImGui::OpenPopup(FSUI_CSTR("Load Resume State"));
ImGui::PushFont(UIStyle.LargeFont);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(40.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(30.0f, 30.0f));
bool is_open = true;
if (ImGui::BeginPopupModal(FSUI_CSTR("Load Resume State"), &is_open,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize))
{ {
CloseSaveStateSelector();
return;
}
SaveStateListEntry& entry = s_state.save_state_selector_slots.front(); SaveStateListEntry& entry = s_state.save_state_selector_slots.front();
SmallString time; SmallString time;
TimeToPrintableString(&time, entry.timestamp); TimeToPrintableString(&time, entry.timestamp);
@ -7277,7 +7231,8 @@ void FullscreenUI::DrawResumeStateSelector()
const ImRect image_bb(pos, pos + ImVec2(image_width, image_height)); const ImRect image_bb(pos, pos + ImVec2(image_width, image_height));
ImGui::GetWindowDrawList()->AddImage( ImGui::GetWindowDrawList()->AddImage(
static_cast<ImTextureID>(entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get()), static_cast<ImTextureID>(entry.preview_texture ? entry.preview_texture.get() : GetPlaceholderTexture().get()),
image_bb.Min, image_bb.Max); image_bb.Min, image_bb.Max, ImVec2(0.0f, 0.0f), ImVec2(1.0f, 1.0f),
ImGui::GetColorU32(IM_COL32(255, 255, 255, 255)));
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + image_height + LayoutScale(40.0f)); ImGui::SetCursorPosY(ImGui::GetCursorPosY() + image_height + LayoutScale(40.0f));
@ -7286,22 +7241,26 @@ void FullscreenUI::DrawResumeStateSelector()
if (MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_PLAY, "Load State"))) if (MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_PLAY, "Load State")))
{ {
DoStartPath(s_state.save_state_selector_game_path, std::move(entry.path)); std::string game_path = std::move(s_state.save_state_selector_game_path);
is_open = false; std::string state_path = std::move(entry.path);
CloseSaveStateSelector();
DoStartPath(std::move(game_path), std::move(state_path));
} }
if (MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_LIGHTBULB, "Clean Boot"))) if (MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_LIGHTBULB, "Clean Boot")))
{ {
DoStartPath(s_state.save_state_selector_game_path); std::string game_path = std::move(s_state.save_state_selector_game_path);
is_open = false; CloseSaveStateSelector();
DoStartPath(std::move(game_path));
} }
if (MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_FOLDER_MINUS, "Delete State"))) if (MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_FOLDER_MINUS, "Delete State")))
{ {
if (FileSystem::DeleteFile(entry.path.c_str())) if (FileSystem::DeleteFile(entry.path.c_str()))
{ {
DoStartPath(s_state.save_state_selector_game_path); std::string game_path = std::move(s_state.save_state_selector_game_path);
is_open = false; CloseSaveStateSelector();
DoStartPath(std::move(game_path));
} }
else else
{ {
@ -7309,32 +7268,14 @@ void FullscreenUI::DrawResumeStateSelector()
} }
} }
if (MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_WINDOW_CLOSE, "Cancel")) || WantsToCloseMenu()) if (MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_WINDOW_CLOSE, "Cancel")))
{ CloseFixedPopupDialog();
ImGui::CloseCurrentPopup();
is_open = false;
}
EndMenuButtons(); EndMenuButtons();
ImGui::EndPopup();
}
ImGui::PopStyleVar(2);
ImGui::PopFont();
if (!is_open)
{
ClearSaveStateEntryList();
QueueResetFocus(FocusResetType::PopupClosed);
s_state.save_state_selector_open = false;
s_state.save_state_selector_loading = false;
s_state.save_state_selector_resuming = false;
s_state.save_state_selector_game_path = {};
}
else
{
SetStandardSelectionFooterText(false); SetStandardSelectionFooterText(false);
}
EndFixedPopupDialog();
} }
void FullscreenUI::DoLoadState(std::string path) void FullscreenUI::DoLoadState(std::string path)
@ -8409,11 +8350,6 @@ GPUTexture* FullscreenUI::GetCoverForCurrentGame()
// Overlays // Overlays
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void FullscreenUI::OpenAboutWindow()
{
s_state.about_window_open = true;
}
void FullscreenUI::ExitFullscreenAndOpenURL(std::string_view url) void FullscreenUI::ExitFullscreenAndOpenURL(std::string_view url)
{ {
Host::RunOnCPUThread([url = std::string(url)]() { Host::RunOnCPUThread([url = std::string(url)]() {
@ -8434,32 +8370,29 @@ void FullscreenUI::CopyTextToClipboard(std::string title, std::string_view text)
void FullscreenUI::DrawAboutWindow() void FullscreenUI::DrawAboutWindow()
{ {
ImGui::SetNextWindowSize(LayoutScale(1020.0f, 590.0f)); if (!BeginFixedPopupDialog(LayoutScale(LAYOUT_LARGE_POPUP_PADDING), LayoutScale(LAYOUT_LARGE_POPUP_ROUNDING),
ImGui::SetNextWindowPos(ImGui::GetIO().DisplaySize * 0.5f, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); LayoutScale(1020.0f, 590.0f)))
ImGui::OpenPopup(FSUI_CSTR("About DuckStation"));
ImGui::PushFont(UIStyle.LargeFont);
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, LayoutScale(40.0f));
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, LayoutScale(30.0f, 30.0f));
if (ImGui::BeginPopupModal(FSUI_CSTR("About DuckStation"), &s_state.about_window_open,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize))
{ {
return;
}
const ImVec2 image_size = LayoutScale(64.0f, 64.0f); const ImVec2 image_size = LayoutScale(64.0f, 64.0f);
const float indent = image_size.x + LayoutScale(8.0f); const float indent = image_size.x + LayoutScale(8.0f);
ImGui::GetWindowDrawList()->AddImage(s_state.app_icon_texture.get(), ImGui::GetCursorScreenPos(), ImGui::GetWindowDrawList()->AddImage(s_state.app_icon_texture.get(), ImGui::GetCursorScreenPos(),
ImGui::GetCursorScreenPos() + image_size); ImGui::GetCursorScreenPos() + image_size);
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + indent); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + indent);
ImGui::TextUnformatted("DuckStation"); ImGui::TextUnformatted("DuckStation");
ImGui::PushStyleColor(ImGuiCol_Text, DarkerColor(UIStyle.BackgroundTextColor));
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + indent); ImGui::SetCursorPosX(ImGui::GetCursorPosX() + indent);
ImGui::TextUnformatted(g_scm_tag_str); ImGui::TextUnformatted(g_scm_tag_str);
ImGui::PopStyleColor();
ImGui::NewLine(); ImGui::NewLine();
ImGui::TextWrapped("%s", FSUI_CSTR("DuckStation is a free simulator/emulator of the Sony PlayStation(TM) " ImGui::TextWrapped("%s", FSUI_CSTR("DuckStation is a free simulator/emulator of the Sony PlayStation(TM) "
"console, focusing on playability, speed, and long-term maintainability.")); "console, focusing on playability, speed, and long-term maintainability."));
ImGui::NewLine(); ImGui::NewLine();
ImGui::TextWrapped( ImGui::TextWrapped("%s",
"%s", FSUI_CSTR("Duck icon by icons8 (https://icons8.com/icon/74847/platforms.undefined.short-title)")); FSUI_CSTR("Duck icon by icons8 (https://icons8.com/icon/74847/platforms.undefined.short-title)"));
ImGui::NewLine(); ImGui::NewLine();
ImGui::TextWrapped( ImGui::TextWrapped(
"%s", FSUI_CSTR("\"PlayStation\" and \"PSX\" are registered trademarks of Sony Interactive Entertainment Europe " "%s", FSUI_CSTR("\"PlayStation\" and \"PSX\" are registered trademarks of Sony Interactive Entertainment Europe "
@ -8476,22 +8409,13 @@ void FullscreenUI::DrawAboutWindow()
ExitFullscreenAndOpenURL("https://github.com/stenzek/duckstation/blob/master/CONTRIBUTORS.md"); ExitFullscreenAndOpenURL("https://github.com/stenzek/duckstation/blob/master/CONTRIBUTORS.md");
if (MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_WINDOW_CLOSE, "Close")) || WantsToCloseMenu()) if (MenuButtonWithoutSummary(FSUI_ICONSTR(ICON_FA_WINDOW_CLOSE, "Close")) || WantsToCloseMenu())
{ CloseFixedPopupDialog();
ImGui::CloseCurrentPopup();
s_state.about_window_open = false;
}
else else
{
SetStandardSelectionFooterText(true); SetStandardSelectionFooterText(true);
}
EndMenuButtons(); EndMenuButtons();
ImGui::EndPopup(); EndFixedPopupDialog();
}
ImGui::PopStyleVar(2);
ImGui::PopFont();
} }
void FullscreenUI::OpenAchievementsWindow() void FullscreenUI::OpenAchievementsWindow()

File diff suppressed because it is too large Load Diff

View File

@ -44,6 +44,9 @@ static constexpr float LAYOUT_HORIZONTAL_MENU_PADDING = 30.0f;
static constexpr float LAYOUT_HORIZONTAL_MENU_ITEM_WIDTH = 250.0f; static constexpr float LAYOUT_HORIZONTAL_MENU_ITEM_WIDTH = 250.0f;
static constexpr float LAYOUT_HORIZONTAL_MENU_ITEM_IMAGE_SIZE = 150.0f; static constexpr float LAYOUT_HORIZONTAL_MENU_ITEM_IMAGE_SIZE = 150.0f;
static constexpr float LAYOUT_SHADOW_OFFSET = 1.0f; static constexpr float LAYOUT_SHADOW_OFFSET = 1.0f;
static constexpr float LAYOUT_SMALL_POPUP_PADDING = 20.0f;
static constexpr float LAYOUT_LARGE_POPUP_PADDING = 30.0f;
static constexpr float LAYOUT_LARGE_POPUP_ROUNDING = 40.0f;
struct ALIGN_TO_CACHE_LINE UIStyles struct ALIGN_TO_CACHE_LINE UIStyles
{ {
@ -185,8 +188,16 @@ void UploadAsyncTextures();
void BeginLayout(); void BeginLayout();
void EndLayout(); void EndLayout();
bool BeginFixedPopupModal(const char* name, bool* p_open = nullptr);
void EndFixedPopupModal(); bool IsAnyFixedPopupDialogOpen();
bool IsFixedPopupDialogOpen(std::string_view name);
void OpenFixedPopupDialog(std::string_view name);
void CloseFixedPopupDialog();
void CloseFixedPopupDialogImmediately();
bool BeginFixedPopupDialog(float scaled_window_padding = LayoutScale(20.0f),
float scaled_window_rounding = LayoutScale(20.0f),
const ImVec2& scaled_window_size = ImVec2(0.0f, 0.0f));
void EndFixedPopupDialog();
void RenderOverlays(); void RenderOverlays();
@ -357,7 +368,7 @@ void CloseChoiceDialog();
using InputStringDialogCallback = std::function<void(std::string text)>; using InputStringDialogCallback = std::function<void(std::string text)>;
bool IsInputDialogOpen(); bool IsInputDialogOpen();
void OpenInputStringDialog(std::string title, std::string message, std::string caption, std::string ok_button_text, void OpenInputStringDialog(std::string_view title, std::string message, std::string caption, std::string ok_button_text,
InputStringDialogCallback callback); InputStringDialogCallback callback);
void CloseInputDialog(); void CloseInputDialog();
@ -365,12 +376,12 @@ using ConfirmMessageDialogCallback = std::function<void(bool)>;
using InfoMessageDialogCallback = std::function<void()>; using InfoMessageDialogCallback = std::function<void()>;
using MessageDialogCallback = std::function<void(s32)>; using MessageDialogCallback = std::function<void(s32)>;
bool IsMessageBoxDialogOpen(); bool IsMessageBoxDialogOpen();
void OpenConfirmMessageDialog(std::string title, std::string message, ConfirmMessageDialogCallback callback, void OpenConfirmMessageDialog(std::string_view title, std::string message, ConfirmMessageDialogCallback callback,
std::string yes_button_text = ICON_FA_CHECK " Yes", std::string yes_button_text = ICON_FA_CHECK " Yes",
std::string no_button_text = ICON_FA_TIMES " No"); std::string no_button_text = ICON_FA_TIMES " No");
void OpenInfoMessageDialog(std::string title, std::string message, InfoMessageDialogCallback callback = {}, void OpenInfoMessageDialog(std::string_view title, std::string message, InfoMessageDialogCallback callback = {},
std::string button_text = ICON_FA_WINDOW_CLOSE " Close"); std::string button_text = ICON_FA_WINDOW_CLOSE " Close");
void OpenMessageDialog(std::string title, std::string message, MessageDialogCallback callback, void OpenMessageDialog(std::string_view title, std::string message, MessageDialogCallback callback,
std::string first_button_text, std::string second_button_text, std::string third_button_text); std::string first_button_text, std::string second_button_text, std::string third_button_text);
void CloseMessageDialog(); void CloseMessageDialog();
@ -405,6 +416,46 @@ void ClearToast();
void GetChoiceDialogHelpText(SmallStringBase& dest); void GetChoiceDialogHelpText(SmallStringBase& dest);
void GetFileSelectorHelpText(SmallStringBase& dest); void GetFileSelectorHelpText(SmallStringBase& dest);
void GetInputDialogHelpText(SmallStringBase& dest); void GetInputDialogHelpText(SmallStringBase& dest);
// Wrapper for an animated popup dialog.
class PopupDialog
{
public:
PopupDialog();
~PopupDialog();
ALWAYS_INLINE const std::string& GetTitle() const { return m_title; }
ALWAYS_INLINE bool IsOpen() const { return (m_state != State::Inactive); }
void StartClose();
void CloseImmediately();
void ClearState();
protected:
enum class State
{
Inactive,
ClosingTrigger,
Open,
OpeningTrigger,
Opening,
Closing,
};
static constexpr float OPEN_TIME = 0.2f;
static constexpr float CLOSE_TIME = 0.1f;
void SetTitleAndOpen(std::string title);
bool BeginRender(float scaled_window_padding = LayoutScale(20.0f), float scaled_window_rounding = LayoutScale(20.0f),
const ImVec2& scaled_window_size = ImVec2(0.0f, 0.0f));
void EndRender();
std::string m_title;
float m_animation_time_remaining = 0.0f;
State m_state = State::Inactive;
};
} // namespace ImGuiFullscreen } // namespace ImGuiFullscreen
// Host UI triggers from Big Picture mode. // Host UI triggers from Big Picture mode.