From 16633df872f45b037178ec6063585a8a22298d70 Mon Sep 17 00:00:00 2001 From: rainmakerv2 <30595646+rainmakerv3@users.noreply.github.com> Date: Sat, 18 Apr 2026 15:05:34 +0800 Subject: [PATCH] Big Picture Settings: Add game folder selection (#4271) * Add game folder selection * reuse * add enable/disable checkboxes * column width fix * Update ImGuiFileDialog submodule URL and branch --------- Co-authored-by: georgemoralis --- .gitmodules | 5 ++ CMakeLists.txt | 3 +- REUSE.toml | 1 + externals/CMakeLists.txt | 6 ++ externals/ImGuiFileDialog | 1 + src/images/big_picture/profiles.png | Bin 0 -> 1792 bytes src/imgui/big_picture.cpp | 11 +-- src/imgui/settings_dialog_imgui.cpp | 130 +++++++++++++++++++++++++--- src/imgui/settings_dialog_imgui.h | 2 + 9 files changed, 141 insertions(+), 18 deletions(-) create mode 160000 externals/ImGuiFileDialog create mode 100644 src/images/big_picture/profiles.png diff --git a/.gitmodules b/.gitmodules index 042e83586..8818cc5dc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -133,3 +133,8 @@ path = externals/minimp3 url = https://github.com/lieff/minimp3 shallow = true +[submodule "externals/ImGuiFileDialog"] + path = externals/ImGuiFileDialog + url = https://github.com/shadexternals/ImGuiFileDialog.git + branch = shadps4 + shallow = true diff --git a/CMakeLists.txt b/CMakeLists.txt index cd13a96c6..4d972e303 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1144,7 +1144,7 @@ add_executable(shadps4 create_target_directory_groups(shadps4) -target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui gcn half::half ZLIB::ZLIB PNG::PNG minimp3) +target_link_libraries(shadps4 PRIVATE magic_enum::magic_enum fmt::fmt toml11::toml11 tsl::robin_map xbyak::xbyak Tracy::TracyClient RenderDoc::API FFmpeg::ffmpeg Dear_ImGui ImGuiFileDialog gcn half::half ZLIB::ZLIB PNG::PNG minimp3) target_link_libraries(shadps4 PRIVATE Boost::headers GPUOpen::VulkanMemoryAllocator LibAtrac9 sirit Vulkan::Headers xxHash::xxhash Zydis::Zydis glslang::glslang SDL3::SDL3 pugixml::pugixml) target_link_libraries(shadps4 PRIVATE stb::headers lfreist-hwinfo::hwinfo nlohmann_json::nlohmann_json miniz::miniz fdk-aac CLI11::CLI11 OpenAL::OpenAL Cpp_Httplib) @@ -1276,6 +1276,7 @@ cmrc_add_resource_library(embedded-resources src/images/big_picture/controller.png src/images/big_picture/trophy.png src/images/big_picture/log.png + src/images/big_picture/profiles.png src/images/trophy.wav src/images/bronze.png src/images/gold.png diff --git a/REUSE.toml b/REUSE.toml index 0d1505bab..1f6aa1b05 100644 --- a/REUSE.toml +++ b/REUSE.toml @@ -84,6 +84,7 @@ path = [ "src/images/big_picture/log.png", "src/images/big_picture/settings.png", "src/images/big_picture/trophy.png", + "src/images/big_picture/profiles.png", "src/shadps4.rc", ] precedence = "aggregate" diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt index 187c2f435..9161a3d16 100644 --- a/externals/CMakeLists.txt +++ b/externals/CMakeLists.txt @@ -280,6 +280,12 @@ endif() add_library(minimp3 INTERFACE) target_include_directories(minimp3 INTERFACE minimp3) +# ImGuiFileDialog +add_library(ImGuiFileDialog ImGuiFileDialog/ImGuiFileDialog.cpp) +target_compile_definitions(ImGuiFileDialog PRIVATE USE_STD_FILESYSTEM) +target_include_directories(ImGuiFileDialog INTERFACE ImGuiFileDialog/) +target_link_libraries(ImGuiFileDialog PRIVATE Dear_ImGui) + #openal if (NOT TARGET OpenAL::OpenAL) set(ALSOFT_ENABLE_MODULES OFF CACHE BOOL "" FORCE) diff --git a/externals/ImGuiFileDialog b/externals/ImGuiFileDialog new file mode 160000 index 000000000..7f91de2d4 --- /dev/null +++ b/externals/ImGuiFileDialog @@ -0,0 +1 @@ +Subproject commit 7f91de2d4f6aa39fc29f6b3a76c15f52d148402a diff --git a/src/images/big_picture/profiles.png b/src/images/big_picture/profiles.png new file mode 100644 index 0000000000000000000000000000000000000000..ba8b9942d841759871395fe60e84b3b9906053d7 GIT binary patch literal 1792 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&F&8^|hH!9j+X{eF!j2YJJ5RzN`m}?85o&ZSlQS)xVU-v1O$bI#l$70Wn`6)6z4u@(WALDynK~8=G5N+dH~@ zdiy3$nL1^XDi&0nx+@sg#>R;^yMcHR078#iy;v1`xXeftj_K6d=X$y4XfU$}hb z>a`m;Z{5Cg|G}fj&z`?{`RdKvcke%Z{Pg+D*Y7`m{`&pr@4va)-dli4{IREtV~B+0 z+k?NIgG*)D9!!5(^dMk@_W~iw3k9uQFFrWMgamMl249)v!eq?a%CerDg|nomO&~<+ z@OS^UTaP(d-Y>RmFYL3Q^PEwJ?>KwT@0Hg6&9A?GIk)ny!BO$Fu9jK@#o|et#xps0 zPWD+idBW>7u2ScBV!$>XenuPO_=17=LI?=5CES^@HKf zk`ws>Qj1UcF~oJZiPHo|0}r#zGI$%B&t}b}>*9Rv&owT$?&vnjdG-6$-ROmyi$5GF z4>ozD^k`OQzz1*flAU~iyqaeTmQQK(xxOQzV|7|vn~dunJ1N6IhCJFbiv)iQY;R}# zlHclZOy4Ad|Iw!v`A4`Xo)LU?+Ii0GU1?m3AqLK|d4KX}RlQQU=3sl-LtZh=< zEo=T^5&5fG6CNy&V)Qw=be25l(U8vvM8vOlO`PDnwPwa7|E)PHI~IkkYSIi1>Dsj+ zXOh?T#8dnsvs{8EWc^M&Bp$-8v~qe@u&Tgs9ha?fDqDlyzs#2Dcv2&~w69EM?$Uh) z@#_<76JJ?(FLArIK+uytv$@$rYdPJK`m)?sq2byGx$t zB`rO?kL&iR@BR0xd4Z~2%vyh7aQ{g7!@QpH VmsL+8&k9h<>*?y}vd$@?2>{J=Ph|iA literal 0 HcmV?d00001 diff --git a/src/imgui/big_picture.cpp b/src/imgui/big_picture.cpp index eb7ff0b5d..a09482b95 100644 --- a/src/imgui/big_picture.cpp +++ b/src/imgui/big_picture.cpp @@ -48,14 +48,9 @@ void Launch() { } SDL_Window* window = - SDL_CreateWindow("shadPS4 Big Picture Mode", EmulatorSettings.GetWindowWidth(), - EmulatorSettings.GetWindowHeight(), SDL_WINDOW_RESIZABLE); + SDL_CreateWindow("shadPS4 Big Picture Mode", 1280, 720, SDL_WINDOW_FULLSCREEN); renderer = SDL_CreateRenderer(window, nullptr); - if (EmulatorSettings.IsFullScreen()) { - SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN); - } - // Check if window creation failed if (window == nullptr) { LOG_ERROR(ImGui, "SDL Window Creation Error: {}", SDL_GetError()); @@ -146,7 +141,8 @@ void Launch() { ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->WorkPos); ImGui::SetNextWindowSize(viewport->WorkSize); - ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoDecoration; + ImGuiWindowFlags window_flags = + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoScrollWithMouse; ImGui::Begin("Game Window", &done, window_flags); ImGui::DrawPrettyBackground(); @@ -235,6 +231,7 @@ void Launch() { if (!showSettings) { uiScale = static_cast(EmulatorSettings.GetBigPictureScale() / 1000.f); sliderScale = uiScale; + GetGameInfo(gameVec, false); } } diff --git a/src/imgui/settings_dialog_imgui.cpp b/src/imgui/settings_dialog_imgui.cpp index ed290f5d3..4a5377352 100644 --- a/src/imgui/settings_dialog_imgui.cpp +++ b/src/imgui/settings_dialog_imgui.cpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -11,9 +12,6 @@ #include "imgui/imgui_std.h" #include "settings_dialog_imgui.h" -#include "imgui_fonts/notosansjp_regular.ttf.g.cpp" -#include "imgui_fonts/proggyvector_regular.ttf.g.cpp" - CMRC_DECLARE(res); namespace BigPictureMode { @@ -116,11 +114,13 @@ SDL_Texture* graphicsTexture; SDL_Texture* inputTexture; SDL_Texture* trophyTexture; SDL_Texture* logTexture; +SDL_Texture* foldersTexture; //////////////// Gui variable const float gameImageSize = 200.f; const float settingsIconSize = 125.f; std::vector settingsProfileVec = {}; +std::vector m_GameInstallDirs = {}; float uiScale = 1.0f; SDL_Renderer* renderer; @@ -128,6 +128,7 @@ SDL_Renderer* renderer; SettingsCategory currentCategory = SettingsCategory::Profiles; std::string currentProfile = "Global"; bool closeOnSave = false; +bool showFileDialog = false; void Init() { auto languageKeys = std::views::keys(languageMap); @@ -136,18 +137,20 @@ void Init() { currentProfile = "Global"; currentCategory = SettingsCategory::Profiles; LoadSettings("Global"); + m_GameInstallDirs = EmulatorSettings.GetAllGameInstallDirs(); SDL_Window* window = SDL_GetKeyboardFocus(); renderer = SDL_GetRenderer(window); LoadEmbeddedTexture("src/images/big_picture/settings.png", generalTexture); - LoadEmbeddedTexture("src/images/big_picture/folder.png", profilesTexture); + LoadEmbeddedTexture("src/images/big_picture/profiles.png", profilesTexture); LoadEmbeddedTexture("src/images/big_picture/global-settings.png", globalSettingsTexture); LoadEmbeddedTexture("src/images/big_picture/experimental.png", experimentalTexture); LoadEmbeddedTexture("src/images/big_picture/graphics.png", graphicsTexture); LoadEmbeddedTexture("src/images/big_picture/controller.png", inputTexture); LoadEmbeddedTexture("src/images/big_picture/trophy.png", trophyTexture); LoadEmbeddedTexture("src/images/big_picture/log.png", logTexture); + LoadEmbeddedTexture("src/images/big_picture/folder.png", foldersTexture); GetGameInfo(settingsProfileVec, true, globalSettingsTexture); uiScale = static_cast(EmulatorSettings.GetBigPictureScale() / 1000.f); @@ -167,7 +170,8 @@ void DrawSettings(bool* open) { ImGui::SetNextWindowPos(viewport->WorkPos); ImGui::SetNextWindowSize(viewport->WorkSize); - if (ImGui::Begin("Settings", nullptr, ImGuiWindowFlags_NoDecoration)) { + if (ImGui::Begin("Settings", nullptr, + ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoScrollWithMouse)) { if (ImGui::IsWindowAppearing()) { Init(); closeOnSave = false; @@ -180,7 +184,10 @@ void DrawSettings(bool* open) { ImVec4 settingsColor = ImVec4(0.1f, 0.1f, 0.12f, 0.8f); // Darker gray ImGui::PushStyleColor(ImGuiCol_ChildBg, settingsColor); - ImGui::BeginChild("Categories", ImVec2(0, 0), ImGuiChildFlags_AutoResizeY, + float vertSize = (settingsIconSize * uiScale + ImGui::CalcTextSize("Profiles").y) + + ImGui::GetStyle().FramePadding.x * 2.f * uiScale + 20.0 * uiScale; + + ImGui::BeginChild("Categories", ImVec2(0, vertSize), true, child_flags | ImGuiWindowFlags_HorizontalScrollbar); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(30.0f * uiScale, 0.0f)); @@ -191,6 +198,7 @@ void DrawSettings(bool* open) { AddCategory("Graphics", graphicsTexture, SettingsCategory::Graphics); AddCategory("Input", inputTexture, SettingsCategory::Input); AddCategory("Trophy", trophyTexture, SettingsCategory::Trophy); + AddCategory("Game Folders", foldersTexture, SettingsCategory::Folders); AddCategory("Log", logTexture, SettingsCategory::Log); if (currentProfile != "Global") @@ -295,11 +303,16 @@ void DrawSettings(bool* open) { } void LoadCategory(SettingsCategory category) { - ImGui::TextColored(ImVec4(0.00f, 1.00f, 1.00f, 1.00f), "%s", - ("Selected Profile: " + currentProfile).c_str()); // Dark Blue + if (category != SettingsCategory::Folders) { + ImGui::TextColored(ImVec4(0.00f, 1.00f, 1.00f, 1.00f), "%s", + ("Selected Profile: " + currentProfile).c_str()); // Dark Blue + } ImGui::Dummy(ImVec2(0, 20.f * uiScale)); ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(4.0f * uiScale, 10.0f * uiScale)); + ImGuiWindowFlags child_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | + ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NavFlattened; + if (category == SettingsCategory::General) { if (ImGui::BeginTable("SettingsTable", 2)) { ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale); @@ -364,6 +377,44 @@ void LoadCategory(SettingsCategory category) { ImGui::EndTable(); } + } else if (category == SettingsCategory::Folders) { + if (ImGui::Button("Add Folder", ImVec2(400.f * uiScale, 0))) { + ImGuiFileDialog::Instance()->OpenDialog( + "OpenFolder", "Add shadPS4 game folder", nullptr, ".", 1, nullptr, + ImGuiFileDialogFlags_DisableCreateDirectoryButton | + ImGuiFileDialogFlags_DontShowHiddenFiles); + + ImGuiViewport* viewport = ImGui::GetMainViewport(); + ImGui::SetNextWindowPos(viewport->Pos); + ImGui::SetNextWindowSize(viewport->Size); + } + + if (ImGuiFileDialog::Instance()->Display("OpenFolder", + child_flags | ImGuiWindowFlags_NoMove)) { + if (ImGuiFileDialog::Instance()->IsOk()) { + GameInstallDir dir; + dir.path = ImGuiFileDialog::Instance()->GetCurrentPath(); + dir.enabled = true; + +#ifdef WIN32 + // replace \ with / on windows + std::string pathString = dir.path.string(); + size_t pos = 0; + while ((pos = pathString.find("\\", pos)) != std::string::npos) { + pathString.replace(pos, 1, "/"); + pos += 2; + } + + dir.path = pathString; +#endif + + m_GameInstallDirs.push_back(dir); + SaveInstallDirs(); + } + + ImGuiFileDialog::Instance()->Close(); + } + } else if (category == SettingsCategory::Log) { if (ImGui::BeginTable("SettingsTable", 2)) { ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale); @@ -405,11 +456,49 @@ void LoadCategory(SettingsCategory category) { // Child Window if Needed if (category == SettingsCategory::Profiles) { - ImGuiWindowFlags child_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | - ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NavFlattened; + ImGui::BeginChild("ProfileSelect", ImVec2(0, 0), true, child_flags); Overlay::TextCentered("Select Global or Game-Specific Settings Profile"); SetProfileIcons(settingsProfileVec); + ImGui::EndChild(); + } else if (category == SettingsCategory::Folders) { + ImGui::BeginChild("Game Folder List", ImVec2(0, 0), true, child_flags); + + ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(0, 5.0f * uiScale)); + if (ImGui::BeginTable("FoldersTable", 3)) { + ImGui::TableSetupColumn("FolderButton", ImGuiTableColumnFlags_WidthFixed, + 300.0f * uiScale); + ImGui::TableSetupColumn("FolderEnabled", ImGuiTableColumnFlags_WidthFixed, 0.0f); + ImGui::TableSetupColumn("FolderPath"); + + for (int i = 0; i < m_GameInstallDirs.size(); i++) { + std::string buttonLabel = "Remove Folder##" + std::to_string(i); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + if (ImGui::Button(buttonLabel.c_str(), ImVec2(280 * uiScale, 0))) { + m_GameInstallDirs.erase(m_GameInstallDirs.begin() + i); + SaveInstallDirs(); + } + + ImGui::TableNextColumn(); + std::string checkboxLabel = "##EnableFolder" + std::to_string(i); + bool previousState = m_GameInstallDirs[i].enabled; + ImGui::Checkbox(checkboxLabel.c_str(), &m_GameInstallDirs[i].enabled); + ImGui::SameLine(); + ImGui::Dummy(ImVec2(5.0 * uiScale, 0)); + + if (m_GameInstallDirs[i].enabled != previousState) { + SaveInstallDirs(); + } + + ImGui::TableNextColumn(); + ImGui::TextWrapped("%s", m_GameInstallDirs[i].path.string().c_str()); + } + + ImGui::EndTable(); + } + ImGui::PopStyleVar(); + ImGui::EndChild(); } } @@ -690,4 +779,25 @@ void SetProfileIcons(std::vector& games) { } } +void SaveInstallDirs() { + std::string profile; + const bool isGlobal = currentProfile == "Global"; + if (!isGlobal) { + profile = currentProfile.substr(0, 9); + } + + if (!isGlobal) { + EmulatorSettings.Load(); + } + + EmulatorSettings.SetAllGameInstallDirs(m_GameInstallDirs); + EmulatorSettings.Save(); + + if (!isGlobal) { + EmulatorSettings.Load(profile); + } + + GetGameInfo(settingsProfileVec, true, globalSettingsTexture); +} + } // namespace BigPictureMode diff --git a/src/imgui/settings_dialog_imgui.h b/src/imgui/settings_dialog_imgui.h index ab521a426..4db517b0a 100644 --- a/src/imgui/settings_dialog_imgui.h +++ b/src/imgui/settings_dialog_imgui.h @@ -18,6 +18,7 @@ enum class SettingsCategory { Graphics, Input, Trophy, + Folders, Log, Experimental, }; @@ -33,6 +34,7 @@ void DrawSettings(bool* open); void SaveSettings(std::string profile); void LoadSettings(std::string profile); void LoadCategory(SettingsCategory); +void SaveInstallDirs(); void AddSettingBool(std::string name, bool& value); void AddSettingSliderInt(std::string name, int& value, int min, int max);