mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2026-05-12 16:09:42 -06:00
Imgui: make imgui emulator settings accessible with hotkey (#4345)
* put all big picture files in same folder * Fix includes * Update main.cpp * make imgui settings dialog accessible by hotkey
This commit is contained in:
parent
ea8bed1c94
commit
ad102a173a
@ -1096,10 +1096,6 @@ set(IMGUI src/imgui/imgui_config.h
|
||||
src/imgui/renderer/imgui_core.h
|
||||
src/imgui/renderer/imgui_impl_sdl3.cpp
|
||||
src/imgui/renderer/imgui_impl_sdl3.h
|
||||
src/imgui/renderer/imgui_impl_sdl3_bpm.cpp
|
||||
src/imgui/renderer/imgui_impl_sdl3_bpm.h
|
||||
src/imgui/renderer/imgui_impl_sdlrenderer3.cpp
|
||||
src/imgui/renderer/imgui_impl_sdlrenderer3.h
|
||||
src/imgui/renderer/imgui_impl_vulkan.cpp
|
||||
src/imgui/renderer/imgui_impl_vulkan.h
|
||||
src/imgui/renderer/font_data.cpp
|
||||
@ -1108,10 +1104,16 @@ set(IMGUI src/imgui/imgui_config.h
|
||||
src/imgui/renderer/font_stack.h
|
||||
src/imgui/renderer/texture_manager.cpp
|
||||
src/imgui/renderer/texture_manager.h
|
||||
src/imgui/big_picture.cpp
|
||||
src/imgui/big_picture.h
|
||||
src/imgui/settings_dialog_imgui.cpp
|
||||
src/imgui/settings_dialog_imgui.h
|
||||
src/imgui/big_picture/big_picture.cpp
|
||||
src/imgui/big_picture/big_picture.h
|
||||
src/imgui/big_picture/settings_dialog_imgui.cpp
|
||||
src/imgui/big_picture/settings_dialog_imgui.h
|
||||
src/imgui/big_picture/settings_dialog_layer.cpp
|
||||
src/imgui/big_picture/settings_dialog_layer.h
|
||||
src/imgui/big_picture/imgui_impl_sdl3_big_picture.cpp
|
||||
src/imgui/big_picture/imgui_impl_sdl3_big_picture.h
|
||||
src/imgui/big_picture/imgui_impl_sdlrenderer3.cpp
|
||||
src/imgui/big_picture/imgui_impl_sdlrenderer3.h
|
||||
)
|
||||
|
||||
set(INPUT src/input/controller.cpp
|
||||
@ -1274,7 +1276,6 @@ cmrc_add_resource_library(embedded-resources
|
||||
NAMESPACE res
|
||||
src/images/big_picture/folder.png
|
||||
src/images/big_picture/settings.png
|
||||
src/images/big_picture/global-settings.png
|
||||
src/images/big_picture/experimental.png
|
||||
src/images/big_picture/graphics.png
|
||||
src/images/big_picture/controller.png
|
||||
|
||||
@ -79,7 +79,6 @@ path = [
|
||||
"src/images/big_picture/controller.png",
|
||||
"src/images/big_picture/experimental.png",
|
||||
"src/images/big_picture/folder.png",
|
||||
"src/images/big_picture/global-settings.png",
|
||||
"src/images/big_picture/graphics.png",
|
||||
"src/images/big_picture/log.png",
|
||||
"src/images/big_picture/settings.png",
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 14 KiB |
@ -1,28 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
namespace BigPictureMode {
|
||||
|
||||
struct Game {
|
||||
SDL_Texture* iconTexture;
|
||||
std::filesystem::path ebootPath;
|
||||
std::string title;
|
||||
std::string serial;
|
||||
bool focusState;
|
||||
};
|
||||
|
||||
void Launch(char* executableName);
|
||||
void SetGameIcons(std::vector<Game>& games);
|
||||
void GetGameInfo(std::vector<Game>& games, bool AddGlobalSettings, SDL_Texture* texture = {});
|
||||
std::filesystem::path UpdateChecker(const std::string sceItem, std::filesystem::path game_folder);
|
||||
|
||||
void LoadTextureDataFromFile(std::filesystem::path filePath, SDL_Texture*& texture,
|
||||
SDL_Renderer* renderer);
|
||||
void LoadTextureData(std::vector<char> data, SDL_Texture*& texture, SDL_Renderer* renderer);
|
||||
|
||||
} // namespace BigPictureMode
|
||||
@ -2,8 +2,6 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <fstream>
|
||||
#include <cmrc/cmrc.hpp>
|
||||
#include <imgui.h>
|
||||
#include <stb_image.h>
|
||||
|
||||
#include "big_picture.h"
|
||||
@ -14,24 +12,181 @@
|
||||
#include "emulator.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
#include "imgui/renderer/font_stack.h"
|
||||
#include "imgui/renderer/imgui_impl_sdl3_bpm.h"
|
||||
#include "imgui/renderer/imgui_impl_sdlrenderer3.h"
|
||||
#include "imgui_impl_sdl3_big_picture.h"
|
||||
#include "imgui_impl_sdlrenderer3.h"
|
||||
#include "settings_dialog_imgui.h"
|
||||
|
||||
CMRC_DECLARE(res);
|
||||
|
||||
namespace BigPictureMode {
|
||||
|
||||
const float gameImageSize = 200.f;
|
||||
constexpr float gameImageSize = 200.f;
|
||||
|
||||
static bool done = false;
|
||||
static bool showSettings = false;
|
||||
bool done = false;
|
||||
bool showSettings = false;
|
||||
|
||||
static std::filesystem::path runEbootPath = "";
|
||||
static std::vector<Game> gameVec = {};
|
||||
std::filesystem::path runEbootPath = "";
|
||||
std::vector<IconInfo> gameIcons = {};
|
||||
|
||||
static float uiScale = 1.0f;
|
||||
static SDL_Renderer* renderer;
|
||||
float uiScale = 1.0f;
|
||||
SDL_Renderer* renderer;
|
||||
|
||||
namespace {
|
||||
|
||||
std::filesystem::path UpdateChecker(const std::string sceItem, std::filesystem::path game_folder) {
|
||||
std::filesystem::path outputPath;
|
||||
auto update_folder = game_folder;
|
||||
update_folder += "-UPDATE";
|
||||
|
||||
auto patch_folder = game_folder;
|
||||
patch_folder += "-patch";
|
||||
|
||||
if (std::filesystem::exists(update_folder / "sce_sys" / sceItem)) {
|
||||
outputPath = update_folder / "sce_sys" / sceItem;
|
||||
} else if (std::filesystem::exists(patch_folder / "sce_sys" / sceItem)) {
|
||||
outputPath = patch_folder / "sce_sys" / sceItem;
|
||||
} else {
|
||||
outputPath = game_folder / "sce_sys" / sceItem;
|
||||
}
|
||||
|
||||
return outputPath;
|
||||
}
|
||||
|
||||
void SetGameIcons(std::vector<IconInfo>& gameIcons) {
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
const float maxAvailableWidth = ImGui::GetContentRegionAvail().x;
|
||||
const float itemSpacing = style.ItemSpacing.x; // already scaled
|
||||
const float padding = 10.0f * uiScale;
|
||||
float rowContentWidth = gameImageSize * uiScale + itemSpacing;
|
||||
|
||||
for (int i = 0; i < gameIcons.size(); i++) {
|
||||
ImGui::BeginGroup();
|
||||
std::string ButtonName = "Button" + std::to_string(i);
|
||||
const char* ButtonNameChar = ButtonName.c_str();
|
||||
|
||||
bool buttonFocused = (ImGui::GetID(ButtonNameChar) == ImGui::GetFocusID());
|
||||
if (buttonFocused) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button,
|
||||
ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered]);
|
||||
}
|
||||
|
||||
ImTextureID id = gameIcons[i].textureId;
|
||||
if (id != nullptr) {
|
||||
if (ImGui::ImageButton(ButtonNameChar, id,
|
||||
ImVec2(gameImageSize * uiScale, gameImageSize * uiScale))) {
|
||||
done = true;
|
||||
runEbootPath = gameIcons[i].ebootPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (buttonFocused) {
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
// Scroll to item only when newly-focused
|
||||
if (ImGui::IsItemFocused() && !gameIcons[i].focusState) {
|
||||
ImGui::SetScrollHereY(0.5f);
|
||||
}
|
||||
|
||||
if (ImGui::IsWindowFocused()) {
|
||||
gameIcons[i].focusState = ImGui::IsItemFocused();
|
||||
}
|
||||
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + gameImageSize * uiScale);
|
||||
ImGui::TextWrapped("%s", gameIcons[i].title.c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndGroup();
|
||||
|
||||
// Use same line if content fits horizontally, move to next line if not
|
||||
rowContentWidth += (gameImageSize * uiScale + itemSpacing * 2 + padding);
|
||||
if (rowContentWidth < maxAvailableWidth) {
|
||||
ImGui::SameLine(0.0f, padding);
|
||||
} else {
|
||||
ImGui::Dummy(ImVec2(0.0f, padding));
|
||||
rowContentWidth = gameImageSize * uiScale + itemSpacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
SDL_Texture* LoadSdlTextureData(std::vector<u8> data) {
|
||||
int image_width = 0;
|
||||
int image_height = 0;
|
||||
int channels = 4;
|
||||
unsigned char* image_data = stbi_load_from_memory(
|
||||
(const unsigned char*)data.data(), (int)data.size(), &image_width, &image_height, NULL, 4);
|
||||
if (image_data == nullptr) {
|
||||
LOG_ERROR(ImGui, "Failed to load image: {}", stbi_failure_reason());
|
||||
}
|
||||
|
||||
SDL_Surface* surface = SDL_CreateSurfaceFrom(image_width, image_height, SDL_PIXELFORMAT_RGBA32,
|
||||
(void*)image_data, channels * image_width);
|
||||
if (surface == nullptr) {
|
||||
LOG_ERROR(ImGui, "Unable to create SDL surface: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||
if (texture == nullptr) {
|
||||
LOG_ERROR(ImGui, "Unable to create SDL texture: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_DestroySurface(surface);
|
||||
stbi_image_free(image_data);
|
||||
|
||||
return texture;
|
||||
}
|
||||
|
||||
SDL_Texture* LoadSdlTextureDataFromFile(std::filesystem::path filePath) {
|
||||
std::ifstream file(filePath, std::ios::binary);
|
||||
std::vector<u8> data =
|
||||
std::vector<u8>(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
|
||||
return LoadSdlTextureData(data);
|
||||
}
|
||||
|
||||
void GetGameIconInfo(std::vector<IconInfo>& icons) {
|
||||
icons.clear();
|
||||
|
||||
for (const auto& installLoc : EmulatorSettings.GetAllGameInstallDirs()) {
|
||||
if (installLoc.enabled && std::filesystem::exists(installLoc.path)) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(installLoc.path)) {
|
||||
if (entry.path().filename().string().ends_with("-UPDATE") ||
|
||||
entry.path().filename().string().ends_with("-patch") || !entry.is_directory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
IconInfo icon;
|
||||
PSF psf;
|
||||
const std::string sfoFileName = "param.sfo";
|
||||
std::filesystem::path sfoPath = UpdateChecker(sfoFileName, entry.path());
|
||||
|
||||
if (std::filesystem::exists(sfoPath) && psf.Open(sfoPath)) {
|
||||
if (const auto title = psf.GetString("TITLE"); title.has_value()) {
|
||||
icon.title = *title;
|
||||
}
|
||||
|
||||
if (const auto title_id = psf.GetString("TITLE_ID"); title_id.has_value()) {
|
||||
icon.serial = *title_id;
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string iconFileName = "icon0.png";
|
||||
std::filesystem::path iconPath = UpdateChecker(iconFileName, entry.path());
|
||||
|
||||
SDL_Texture* texture = LoadSdlTextureDataFromFile(iconPath);
|
||||
icon.textureId = ImTextureID(texture);
|
||||
|
||||
icon.ebootPath = entry.path() / "eboot.bin";
|
||||
icon.focusState = false;
|
||||
icons.push_back(icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(icons.begin(), icons.end(), [](const IconInfo& a, const IconInfo& b) {
|
||||
return a.title < b.title; // Alphabetical order
|
||||
});
|
||||
}
|
||||
|
||||
void Launch(char* executableName) {
|
||||
if (!SDL_Init(SDL_INIT_VIDEO)) {
|
||||
@ -42,15 +197,12 @@ void Launch(char* executableName) {
|
||||
|
||||
if (!SDL_Init(SDL_INIT_GAMEPAD)) {
|
||||
LOG_ERROR(ImGui, "SDL_INIT_GAMEPAD Error: {}", SDL_GetError());
|
||||
SDL_Quit();
|
||||
return;
|
||||
}
|
||||
|
||||
SDL_Window* window =
|
||||
SDL_CreateWindow("shadPS4 Big Picture Mode", 1280, 720, SDL_WINDOW_FULLSCREEN);
|
||||
renderer = SDL_CreateRenderer(window, nullptr);
|
||||
|
||||
// Check if window creation failed
|
||||
if (window == nullptr) {
|
||||
LOG_ERROR(ImGui, "SDL Window Creation Error: {}", SDL_GetError());
|
||||
SDL_DestroyRenderer(renderer);
|
||||
@ -66,38 +218,21 @@ void Launch(char* executableName) {
|
||||
io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;
|
||||
io.ConfigNavCursorVisibleAlways = true;
|
||||
|
||||
ImFontConfig font_cfg;
|
||||
font_cfg.OversampleH = 2;
|
||||
font_cfg.OversampleV = 1;
|
||||
ImFont* myFont = ImGui::FontStack::AddPrimaryUiFont(
|
||||
io.Fonts, 32.0f, EmulatorSettings.GetConsoleLanguage(), font_cfg, true);
|
||||
io.FontDefault = myFont;
|
||||
ImFontConfig cfgBase;
|
||||
cfgBase.OversampleH = 2;
|
||||
cfgBase.OversampleV = 1;
|
||||
|
||||
io.FontDefault = ImGui::FontStack::AddPrimaryUiFont(
|
||||
io.Fonts, 64.0f, EmulatorSettings.GetConsoleLanguage(), cfgBase, true);
|
||||
io.FontGlobalScale = 0.5f;
|
||||
io.Fonts->Build();
|
||||
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
ImVec4* colors = style.Colors;
|
||||
|
||||
colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.06f, 1.00f); // black
|
||||
colors[ImGuiCol_Header] = ImVec4(0.20f, 0.40f, 0.70f, 1.00f); // blue
|
||||
colors[ImGuiCol_HeaderHovered] = ImVec4(0.25f, 0.50f, 0.85f, 1.00f); // lighter blue
|
||||
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f); // another light blue
|
||||
colors[ImGuiCol_SliderGrab] = ImVec4(0.26f, 0.59f, 0.98f, 0.80f); // another light blue
|
||||
|
||||
style.WindowRounding = 0.0f;
|
||||
style.FrameRounding = 5.0f * uiScale;
|
||||
style.ItemSpacing = ImVec2(10.0f * uiScale, 10.0f * uiScale);
|
||||
style.FramePadding = ImVec2(10.0f * uiScale, 10.0f * uiScale);
|
||||
style.FrameBorderSize = 2.5f * uiScale;
|
||||
style.WindowBorderSize = 0.0f;
|
||||
style.WindowPadding = ImVec2(20.0f * uiScale, 20.0f * uiScale);
|
||||
style.GrabMinSize = 20.0f * uiScale;
|
||||
|
||||
ImGui_ImplSDL3_InitForSDLRenderer(window, renderer);
|
||||
ImGui_ImplSDLRenderer3_Init(renderer);
|
||||
GetGameInfo(gameVec, false);
|
||||
GetGameIconInfo(gameIcons);
|
||||
|
||||
uiScale = static_cast<float>(EmulatorSettings.GetBigPictureScale() / 1000.f);
|
||||
ImGuiEmuSettings::SettingsWindow settingsWindow(false);
|
||||
|
||||
while (!done) {
|
||||
SDL_Event event;
|
||||
@ -111,7 +246,24 @@ void Launch(char* executableName) {
|
||||
ImGui_ImplSDLRenderer3_NewFrame();
|
||||
ImGui_ImplSDL3_NewFrame();
|
||||
ImGui::NewFrame();
|
||||
ImGui::PushFont(myFont);
|
||||
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.06f, 0.06f, 0.06f, 1.00f)); // black
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.20f, 0.40f, 0.70f, 1.00f)); // blue
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered,
|
||||
ImVec4(0.25f, 0.50f, 0.85f, 1.00f)); // lighter blue
|
||||
ImGui::PushStyleColor(ImGuiCol_SliderGrabActive,
|
||||
ImVec4(0.26f, 0.59f, 0.98f, 0.80f)); // another light blue
|
||||
ImGui::PushStyleColor(ImGuiCol_SliderGrab,
|
||||
ImVec4(0.26f, 0.59f, 0.98f, 0.80f)); // another light blue
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 5.0f * uiScale);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(10.0f * uiScale, 10.0f * uiScale));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f * uiScale, 10.0f * uiScale));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 2.5f * uiScale);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20.0f * uiScale, 20.0f * uiScale));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 20.0f * uiScale);
|
||||
|
||||
ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(viewport->WorkPos);
|
||||
@ -139,7 +291,7 @@ void Launch(char* executableName) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
|
||||
SetGameIcons(gameVec);
|
||||
SetGameIcons(gameIcons);
|
||||
ImGui::EndChild();
|
||||
ImGui::Separator();
|
||||
|
||||
@ -200,17 +352,18 @@ void Launch(char* executableName) {
|
||||
if (showSettings) {
|
||||
EmulatorSettings.SetBigPictureScale(static_cast<int>(uiScale * 1000));
|
||||
EmulatorSettings.Save();
|
||||
DrawSettings(&showSettings);
|
||||
settingsWindow.DrawSettings(&showSettings);
|
||||
|
||||
// update when settings dialog closed
|
||||
if (!showSettings) {
|
||||
uiScale = static_cast<float>(EmulatorSettings.GetBigPictureScale() / 1000.f);
|
||||
sliderScale = uiScale;
|
||||
GetGameInfo(gameVec, false);
|
||||
GetGameIconInfo(gameIcons);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopFont();
|
||||
ImGui::PopStyleVar(8);
|
||||
ImGui::PopStyleColor(5);
|
||||
ImGui::End();
|
||||
ImGui::Render();
|
||||
SDL_SetRenderDrawColor(renderer, 100, 100, 100, 255);
|
||||
@ -236,163 +389,4 @@ void Launch(char* executableName) {
|
||||
}
|
||||
}
|
||||
|
||||
void SetGameIcons(std::vector<Game>& games) {
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
const float maxAvailableWidth = ImGui::GetContentRegionAvail().x;
|
||||
const float itemSpacing = style.ItemSpacing.x; // already scaled
|
||||
const float padding = 10.0f * uiScale;
|
||||
float rowContentWidth = gameImageSize * uiScale + itemSpacing;
|
||||
|
||||
for (int i = 0; i < games.size(); i++) {
|
||||
ImGui::BeginGroup();
|
||||
std::string ButtonName = "Button" + std::to_string(i);
|
||||
const char* ButtonNameChar = ButtonName.c_str();
|
||||
|
||||
bool isNextItemFocused = (ImGui::GetID(ButtonNameChar) == ImGui::GetFocusID());
|
||||
bool popColor = false;
|
||||
if (isNextItemFocused) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button,
|
||||
ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered]);
|
||||
popColor = true;
|
||||
}
|
||||
|
||||
if (ImGui::ImageButton(ButtonNameChar, (ImTextureID)games[i].iconTexture,
|
||||
ImVec2(gameImageSize * uiScale, gameImageSize * uiScale))) {
|
||||
done = true;
|
||||
runEbootPath = games[i].ebootPath;
|
||||
}
|
||||
|
||||
if (popColor) {
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
// Scroll to item only when newly-focused
|
||||
if (ImGui::IsItemFocused() && !games[i].focusState) {
|
||||
ImGui::SetScrollHereY(0.5f);
|
||||
}
|
||||
|
||||
if (ImGui::IsWindowFocused())
|
||||
games[i].focusState = ImGui::IsItemFocused();
|
||||
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + gameImageSize * uiScale);
|
||||
ImGui::TextWrapped("%s", games[i].title.c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndGroup();
|
||||
|
||||
// Use same line if content fits horizontally, move to next line if not
|
||||
rowContentWidth += (gameImageSize * uiScale + itemSpacing * 2 + padding);
|
||||
if (rowContentWidth < maxAvailableWidth) {
|
||||
ImGui::SameLine(0.0f, padding);
|
||||
} else {
|
||||
ImGui::Dummy(ImVec2(0.0f, padding));
|
||||
rowContentWidth = gameImageSize * uiScale + itemSpacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path UpdateChecker(const std::string sceItem, std::filesystem::path game_folder) {
|
||||
std::filesystem::path outputPath;
|
||||
auto update_folder = game_folder;
|
||||
update_folder += "-UPDATE";
|
||||
|
||||
auto patch_folder = game_folder;
|
||||
patch_folder += "-patch";
|
||||
|
||||
if (std::filesystem::exists(update_folder / "sce_sys" / sceItem)) {
|
||||
outputPath = update_folder / "sce_sys" / sceItem;
|
||||
} else if (std::filesystem::exists(patch_folder / "sce_sys" / sceItem)) {
|
||||
outputPath = patch_folder / "sce_sys" / sceItem;
|
||||
} else {
|
||||
outputPath = game_folder / "sce_sys" / sceItem;
|
||||
}
|
||||
|
||||
return outputPath;
|
||||
}
|
||||
|
||||
void GetGameInfo(std::vector<Game>& games, bool AddGlobalSettings, SDL_Texture* texture) {
|
||||
games.clear();
|
||||
if (AddGlobalSettings) {
|
||||
Game global;
|
||||
global.title = "Global";
|
||||
global.iconTexture = texture;
|
||||
global.focusState = false;
|
||||
games.push_back(global);
|
||||
}
|
||||
|
||||
for (const auto& installLoc : EmulatorSettings.GetAllGameInstallDirs()) {
|
||||
if (installLoc.enabled && std::filesystem::exists(installLoc.path)) {
|
||||
for (const auto& entry : std::filesystem::directory_iterator(installLoc.path)) {
|
||||
if (entry.path().filename().string().ends_with("-UPDATE") ||
|
||||
entry.path().filename().string().ends_with("-patch") || !entry.is_directory()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Game game;
|
||||
PSF psf;
|
||||
const std::string sfoFileName = "param.sfo";
|
||||
std::filesystem::path sfoPath = UpdateChecker(sfoFileName, entry.path());
|
||||
|
||||
if (psf.Open(sfoPath)) {
|
||||
if (const auto title = psf.GetString("TITLE"); title.has_value()) {
|
||||
game.title = *title;
|
||||
}
|
||||
|
||||
if (const auto title_id = psf.GetString("TITLE_ID"); title_id.has_value()) {
|
||||
game.serial = *title_id;
|
||||
}
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
const std::string iconFileName = "icon0.png";
|
||||
std::filesystem::path iconPath = UpdateChecker(iconFileName, entry.path());
|
||||
LoadTextureDataFromFile(iconPath, game.iconTexture, renderer);
|
||||
|
||||
game.ebootPath = entry.path() / "eboot.bin";
|
||||
game.focusState = false;
|
||||
games.push_back(game);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Keep global settings at the start if it's added
|
||||
auto start = AddGlobalSettings ? games.begin() + 1 : (games.begin());
|
||||
std::sort(start, games.end(), [](const Game& a, const Game& b) {
|
||||
return a.title < b.title; // Alphabetical order
|
||||
});
|
||||
}
|
||||
|
||||
void LoadTextureDataFromFile(std::filesystem::path filePath, SDL_Texture*& texture,
|
||||
SDL_Renderer* m_renderer) {
|
||||
std::ifstream file(filePath, std::ios::binary);
|
||||
std::vector<char> data =
|
||||
std::vector<char>(std::istreambuf_iterator<char>(file), std::istreambuf_iterator<char>());
|
||||
LoadTextureData(data, texture, m_renderer);
|
||||
}
|
||||
|
||||
void LoadTextureData(std::vector<char> data, SDL_Texture*& texture, SDL_Renderer* m_renderer) {
|
||||
int image_width = 0;
|
||||
int image_height = 0;
|
||||
int channels = 4;
|
||||
unsigned char* image_data = stbi_load_from_memory(
|
||||
(const unsigned char*)data.data(), (int)data.size(), &image_width, &image_height, NULL, 4);
|
||||
if (image_data == nullptr) {
|
||||
LOG_ERROR(ImGui, "Failed to load image: {}", stbi_failure_reason());
|
||||
}
|
||||
|
||||
SDL_Surface* surface = SDL_CreateSurfaceFrom(image_width, image_height, SDL_PIXELFORMAT_RGBA32,
|
||||
(void*)image_data, channels * image_width);
|
||||
if (surface == nullptr) {
|
||||
LOG_ERROR(ImGui, "Unable to create SDL surface: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
texture = SDL_CreateTextureFromSurface(m_renderer, surface);
|
||||
if (texture == nullptr) {
|
||||
LOG_ERROR(ImGui, "Unable to create SDL texture: {}", SDL_GetError());
|
||||
}
|
||||
|
||||
SDL_DestroySurface(surface);
|
||||
stbi_image_free(image_data);
|
||||
}
|
||||
|
||||
} // namespace BigPictureMode
|
||||
28
src/imgui/big_picture/big_picture.h
Normal file
28
src/imgui/big_picture/big_picture.h
Normal file
@ -0,0 +1,28 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <imgui.h>
|
||||
|
||||
#include "common/types.h"
|
||||
|
||||
namespace BigPictureMode {
|
||||
|
||||
struct IconInfo {
|
||||
ImTextureID textureId;
|
||||
std::filesystem::path ebootPath;
|
||||
std::string title;
|
||||
std::string serial;
|
||||
bool focusState;
|
||||
};
|
||||
|
||||
void Launch(char* executableName);
|
||||
void GetGameIconInfo(std::vector<IconInfo>& icons);
|
||||
SDL_Texture* LoadSdlTextureData(std::vector<u8> data);
|
||||
SDL_Texture* LoadSdlTextureDataFromFile(std::filesystem::path filePath);
|
||||
|
||||
} // namespace BigPictureMode
|
||||
@ -5,7 +5,7 @@
|
||||
|
||||
#include "imgui.h"
|
||||
#ifndef IMGUI_DISABLE
|
||||
#include "imgui_impl_sdl3_bpm.h"
|
||||
#include "imgui_impl_sdl3_big_picture.h"
|
||||
|
||||
// Clang warnings with -Weverything
|
||||
#if defined(__clang__)
|
||||
813
src/imgui/big_picture/settings_dialog_imgui.cpp
Normal file
813
src/imgui/big_picture/settings_dialog_imgui.cpp
Normal file
@ -0,0 +1,813 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <map>
|
||||
#include <ranges>
|
||||
#include <ImGuiFileDialog.h>
|
||||
#include <cmrc/cmrc.hpp>
|
||||
#include <stb_image.h>
|
||||
|
||||
#include "common/elf_info.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "common/path_util.h"
|
||||
#include "core/devtools/layer.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
#include "settings_dialog_imgui.h"
|
||||
|
||||
CMRC_DECLARE(res);
|
||||
|
||||
constexpr float gameImageSize = 200.f;
|
||||
constexpr float settingsIconSize = 125.f;
|
||||
|
||||
namespace ImGuiEmuSettings {
|
||||
|
||||
int SettingsWindow::GetComboIndex(std::string selection, std::vector<std::string> options) {
|
||||
for (int i = 0; i < options.size(); i++) {
|
||||
if (selection == options[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SettingsWindow::LoadSettings(std::string profile) {
|
||||
const bool isSpecific = currentProfile != "Global";
|
||||
isSpecific ? EmulatorSettings.Load(profile) : EmulatorSettings.Load();
|
||||
|
||||
/////////// General Tab
|
||||
int languageIndex = EmulatorSettings.GetConsoleLanguage();
|
||||
std::string language;
|
||||
for (const auto& [key, value] : languageMap) {
|
||||
if (value == languageIndex) {
|
||||
language = key;
|
||||
}
|
||||
}
|
||||
|
||||
consoleLanguageSetting = GetComboIndex(language, languageOptions);
|
||||
volumeSetting = EmulatorSettings.GetVolumeSlider();
|
||||
showSplashSetting = EmulatorSettings.IsShowSplash();
|
||||
audioBackendSetting = EmulatorSettings.GetAudioBackend();
|
||||
|
||||
/////////// Graphics Tab
|
||||
fullscreenModeSetting =
|
||||
GetComboIndex(EmulatorSettings.GetFullScreenMode(), fullscreenModeOptions);
|
||||
presentModeSetting = GetComboIndex(EmulatorSettings.GetPresentMode(), presentModeOptions);
|
||||
windowHeightSetting = EmulatorSettings.GetWindowHeight();
|
||||
windowWidthSetting = EmulatorSettings.GetWindowWidth();
|
||||
hdrAllowedSetting = EmulatorSettings.IsHdrAllowed();
|
||||
fsrEnabledSetting = EmulatorSettings.IsFsrEnabled();
|
||||
rcasEnabledSetting = EmulatorSettings.IsRcasEnabled();
|
||||
rcasAttenuationSetting = static_cast<float>(EmulatorSettings.GetRcasAttenuation() * 0.001f);
|
||||
|
||||
/////////// Input Tab
|
||||
motionControlsSetting = EmulatorSettings.IsMotionControlsEnabled();
|
||||
backgroundControllerSetting = EmulatorSettings.IsBackgroundControllerInput();
|
||||
cursorStateSetting = EmulatorSettings.GetCursorState();
|
||||
cursorTimeoutSetting = EmulatorSettings.GetCursorHideTimeout();
|
||||
|
||||
/////////// Trophy Tab
|
||||
trophyPopupDisabledSetting = EmulatorSettings.IsTrophyPopupDisabled();
|
||||
trophySideSetting =
|
||||
GetComboIndex(EmulatorSettings.GetTrophyNotificationSide(), trophySideOptions);
|
||||
trophyDurationSetting = static_cast<float>(EmulatorSettings.GetTrophyNotificationDuration());
|
||||
|
||||
/////////// Log Tab
|
||||
logEnableSetting = EmulatorSettings.IsLogEnable();
|
||||
logSeparateSetting = EmulatorSettings.IsLogSeparate();
|
||||
logSyncSetting = EmulatorSettings.IsLogSync();
|
||||
|
||||
/////////// Experimental Tab
|
||||
if (isSpecific) {
|
||||
readbacksModeSetting = EmulatorSettings.GetReadbacksMode();
|
||||
readbackLinearImagesSetting = EmulatorSettings.IsReadbackLinearImagesEnabled();
|
||||
directMemoryAccessSetting = EmulatorSettings.IsDirectMemoryAccessEnabled();
|
||||
devkitConsoleSetting = EmulatorSettings.IsDevKit();
|
||||
neoModeSetting = EmulatorSettings.IsNeo();
|
||||
shadnetEnabledSetting = EmulatorSettings.IsShadNetEnabled();
|
||||
connectedNetworkSetting = EmulatorSettings.IsConnectedToNetwork();
|
||||
pipelineCacheEnabledSetting = EmulatorSettings.IsPipelineCacheEnabled();
|
||||
pipelineCacheArchiveSetting = EmulatorSettings.IsPipelineCacheArchived();
|
||||
extraDmemSetting = EmulatorSettings.GetExtraDmemInMBytes();
|
||||
vblankFrequencySetting = EmulatorSettings.GetVblankFrequency();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWindow::SaveSettings(std::string profile) {
|
||||
const bool isSpecific = currentProfile != "Global";
|
||||
|
||||
/////////// General Tab
|
||||
EmulatorSettings.SetConsoleLanguage(languageMap.at(languageOptions.at(consoleLanguageSetting)),
|
||||
isSpecific);
|
||||
EmulatorSettings.SetVolumeSlider(volumeSetting, isSpecific);
|
||||
EmulatorSettings.SetShowSplash(showSplashSetting, isSpecific);
|
||||
EmulatorSettings.SetAudioBackend(audioBackendSetting, isSpecific);
|
||||
|
||||
/////////// Graphics Tab
|
||||
bool isFullscreen = fullscreenModeSetting != 0;
|
||||
EmulatorSettings.SetFullScreen(isFullscreen);
|
||||
EmulatorSettings.SetFullScreenMode(fullscreenModeOptions.at(fullscreenModeSetting), isSpecific);
|
||||
EmulatorSettings.SetPresentMode(presentModeOptions.at(presentModeSetting), isSpecific);
|
||||
EmulatorSettings.SetWindowHeight(windowHeightSetting, isSpecific);
|
||||
EmulatorSettings.SetWindowWidth(windowWidthSetting, isSpecific);
|
||||
EmulatorSettings.SetHdrAllowed(hdrAllowedSetting, isSpecific);
|
||||
EmulatorSettings.SetFsrEnabled(fsrEnabledSetting, isSpecific);
|
||||
EmulatorSettings.SetRcasEnabled(rcasEnabledSetting, isSpecific);
|
||||
EmulatorSettings.SetRcasAttenuation(static_cast<int>(rcasAttenuationSetting * 1000),
|
||||
isSpecific);
|
||||
|
||||
/////////// Input Tab
|
||||
EmulatorSettings.SetMotionControlsEnabled(motionControlsSetting, isSpecific);
|
||||
EmulatorSettings.SetBackgroundControllerInput(backgroundControllerSetting, isSpecific);
|
||||
EmulatorSettings.SetCursorState(cursorStateSetting, isSpecific);
|
||||
EmulatorSettings.SetCursorHideTimeout(cursorTimeoutSetting, isSpecific);
|
||||
|
||||
/////////// Trophy Tab
|
||||
EmulatorSettings.SetTrophyPopupDisabled(trophyPopupDisabledSetting, isSpecific);
|
||||
EmulatorSettings.SetTrophyNotificationSide(trophySideOptions.at(trophySideSetting), isSpecific);
|
||||
EmulatorSettings.SetTrophyNotificationDuration(static_cast<double>(trophyDurationSetting));
|
||||
|
||||
/////////// Log Tab
|
||||
EmulatorSettings.SetLogEnable(logEnableSetting, isSpecific);
|
||||
EmulatorSettings.SetLogSeparate(logSeparateSetting, isSpecific);
|
||||
EmulatorSettings.SetLogSync(logSyncSetting, isSpecific);
|
||||
|
||||
/////////// Experimental Tab
|
||||
if (isSpecific) {
|
||||
EmulatorSettings.SetReadbacksMode(readbacksModeSetting, true);
|
||||
EmulatorSettings.SetReadbackLinearImagesEnabled(readbackLinearImagesSetting, true);
|
||||
EmulatorSettings.SetDirectMemoryAccessEnabled(directMemoryAccessSetting, true);
|
||||
EmulatorSettings.SetDevKit(devkitConsoleSetting, true);
|
||||
EmulatorSettings.SetNeo(neoModeSetting, true);
|
||||
EmulatorSettings.SetShadNetEnabled(shadnetEnabledSetting, true);
|
||||
EmulatorSettings.SetConnectedToNetwork(connectedNetworkSetting, true);
|
||||
EmulatorSettings.SetPipelineCacheEnabled(pipelineCacheEnabledSetting, true);
|
||||
EmulatorSettings.SetPipelineCacheArchived(pipelineCacheArchiveSetting, true);
|
||||
EmulatorSettings.SetExtraDmemInMBytes(extraDmemSetting, true);
|
||||
EmulatorSettings.SetVblankFrequency(vblankFrequencySetting, true);
|
||||
}
|
||||
|
||||
isSpecific ? EmulatorSettings.Save(profile) : EmulatorSettings.Save();
|
||||
}
|
||||
|
||||
void SettingsWindow::SaveInstallDirs() {
|
||||
std::string profile;
|
||||
const bool isGlobal = currentProfile == "Global";
|
||||
if (!isGlobal) {
|
||||
profile = currentProfile.substr(0, 9);
|
||||
EmulatorSettings.Load();
|
||||
}
|
||||
|
||||
EmulatorSettings.SetAllGameInstallDirs(m_GameInstallDirs);
|
||||
EmulatorSettings.Save();
|
||||
|
||||
if (!isGlobal) {
|
||||
EmulatorSettings.Load(profile);
|
||||
}
|
||||
|
||||
if (!isGameRunning) {
|
||||
GetProfileInfo();
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWindow::GetProfileInfo() {
|
||||
GetGameIconInfo(profileIcons);
|
||||
|
||||
BigPictureMode::IconInfo global;
|
||||
global.title = "Global";
|
||||
profileIcons.emplace(profileIcons.begin(), global);
|
||||
}
|
||||
|
||||
SettingsWindow::SettingsWindow(bool gameRunning) : isGameRunning(gameRunning) {
|
||||
auto resource = cmrc::res::get_filesystem();
|
||||
auto loadTexture = [&](const std::string& resourcePath,
|
||||
std::variant<SDL_Texture*, ImGui::RefCountedTexture>& texture) {
|
||||
auto file = resource.open(resourcePath);
|
||||
std::vector<u8> texData = std::vector<u8>(file.begin(), file.end());
|
||||
gameRunning ? texture = ImGui::RefCountedTexture::DecodePngTexture(texData)
|
||||
: texture = BigPictureMode::LoadSdlTextureData(texData);
|
||||
};
|
||||
|
||||
loadTexture("src/images/big_picture/settings.png", generalTexture);
|
||||
loadTexture("src/images/big_picture/experimental.png", experimentalTexture);
|
||||
loadTexture("src/images/big_picture/graphics.png", graphicsTexture);
|
||||
loadTexture("src/images/big_picture/controller.png", inputTexture);
|
||||
loadTexture("src/images/big_picture/trophy.png", trophyTexture);
|
||||
loadTexture("src/images/big_picture/log.png", logTexture);
|
||||
loadTexture("src/images/big_picture/folder.png", foldersTexture);
|
||||
loadTexture("src/images/big_picture/profiles.png", profilesTexture);
|
||||
|
||||
auto languageKeys = std::views::keys(languageMap);
|
||||
languageOptions.assign(languageKeys.begin(), languageKeys.end());
|
||||
|
||||
currentProfile = "Global";
|
||||
m_GameInstallDirs = EmulatorSettings.GetAllGameInstallDirs();
|
||||
currentCategory = isGameRunning ? SettingsCategory::General : SettingsCategory::Profiles;
|
||||
uiScale = static_cast<float>(EmulatorSettings.GetBigPictureScale() / 1000.f);
|
||||
|
||||
bool customConfigFound = false;
|
||||
if (isGameRunning) {
|
||||
runningGameSerial = std::string(Common::ElfInfo::Instance().GameSerial());
|
||||
std::filesystem::path customConfigFile =
|
||||
Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) /
|
||||
(runningGameSerial + ".json");
|
||||
|
||||
if (std::filesystem::exists(customConfigFile)) {
|
||||
customConfigFound = true;
|
||||
currentProfile =
|
||||
runningGameSerial + " - " + std::string(Common::ElfInfo::Instance().Title());
|
||||
}
|
||||
} else {
|
||||
GetProfileInfo();
|
||||
}
|
||||
|
||||
customConfigFound ? LoadSettings(runningGameSerial) : LoadSettings("Global");
|
||||
}
|
||||
|
||||
void SettingsWindow::DeInit() {
|
||||
EmulatorSettings.Load();
|
||||
EmulatorSettings.SetBigPictureScale(static_cast<int>(uiScale * 1000));
|
||||
EmulatorSettings.Save();
|
||||
|
||||
if (isGameRunning && !runningGameSerial.empty()) {
|
||||
EmulatorSettings.Load(runningGameSerial);
|
||||
}
|
||||
}
|
||||
void SettingsWindow::DrawSettings(bool* open) {
|
||||
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.06f, 0.06f, 0.06f, 1.00f)); // black
|
||||
ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.20f, 0.40f, 0.70f, 1.00f)); // blue
|
||||
ImGui::PushStyleColor(ImGuiCol_HeaderHovered,
|
||||
ImVec4(0.25f, 0.50f, 0.85f, 1.00f)); // lighter blue
|
||||
ImGui::PushStyleColor(ImGuiCol_SliderGrabActive,
|
||||
ImVec4(0.26f, 0.59f, 0.98f, 0.80f)); // another light blue
|
||||
ImGui::PushStyleColor(ImGuiCol_SliderGrab,
|
||||
ImVec4(0.26f, 0.59f, 0.98f, 0.80f)); // another light blue
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 5.0f * uiScale);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(10.0f * uiScale, 10.0f * uiScale));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(10.0f * uiScale, 10.0f * uiScale));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameBorderSize, 2.5f * uiScale);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(20.0f * uiScale, 20.0f * uiScale));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_GrabMinSize, 20.0f * uiScale);
|
||||
|
||||
SetupWindow();
|
||||
DrawCategoryTabs();
|
||||
DrawMainContent(open);
|
||||
|
||||
ImGui::PopStyleVar(8);
|
||||
ImGui::PopStyleColor(5);
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void SettingsWindow::SetupWindow() {
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(viewport->WorkPos);
|
||||
ImGui::SetNextWindowSize(viewport->WorkSize);
|
||||
|
||||
ImGui::Begin("Settings", nullptr,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoScrollWithMouse);
|
||||
ImGui::DrawPrettyBackground();
|
||||
ImGui::SetWindowFontScale(uiScale);
|
||||
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
|
||||
SettingsCategory firstCategory =
|
||||
isGameRunning ? SettingsCategory::General : SettingsCategory::Profiles;
|
||||
SettingsCategory lastCategory =
|
||||
currentProfile != "Global" ? SettingsCategory::Experimental : SettingsCategory::Log;
|
||||
|
||||
// Navigate categories with Tab / R1 / L1
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_GamepadR1) || ImGui::IsKeyPressed(ImGuiKey_Tab)) {
|
||||
int currentIndex = static_cast<int>(currentCategory);
|
||||
currentCategory == lastCategory
|
||||
? currentCategory = static_cast<SettingsCategory>(firstCategory)
|
||||
: currentCategory = static_cast<SettingsCategory>(currentIndex + 1);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_GamepadL1)) {
|
||||
int currentIndex = static_cast<int>(currentCategory);
|
||||
currentIndex == static_cast<int>(firstCategory)
|
||||
? currentCategory = lastCategory
|
||||
: currentCategory = static_cast<SettingsCategory>(currentIndex - 1);
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWindow::DrawCategoryTabs() {
|
||||
ImVec4 settingsColor = ImVec4(0.1f, 0.1f, 0.12f, 0.8f); // Darker gray
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, settingsColor);
|
||||
|
||||
float vertSize = (settingsIconSize * uiScale + ImGui::CalcTextSize("Profiles").y) +
|
||||
ImGui::GetStyle().FramePadding.y * 4.f + 20.0 * uiScale;
|
||||
ImGuiWindowFlags child_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NavFlattened;
|
||||
ImGui::BeginChild("Categories", ImVec2(0, vertSize), true,
|
||||
child_flags | ImGuiWindowFlags_HorizontalScrollbar |
|
||||
ImGuiWindowFlags_NoScrollWithMouse);
|
||||
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(30.0f * uiScale, 0.0f));
|
||||
|
||||
// Must add categories in enum order for L1/R1 to work correctly
|
||||
if (!isGameRunning) {
|
||||
AddCategory("Profiles", profilesTexture, SettingsCategory::Profiles);
|
||||
}
|
||||
|
||||
AddCategory("General", generalTexture, SettingsCategory::General);
|
||||
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") {
|
||||
AddCategory("Experimental", experimentalTexture, SettingsCategory::Experimental);
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void SettingsWindow::AddCategory(std::string name,
|
||||
std::variant<SDL_Texture*, ImGui::RefCountedTexture> texture,
|
||||
SettingsCategory category) {
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginGroup();
|
||||
|
||||
// make button appear hovered as long as category is selected, otherwise dull its hovered color
|
||||
currentCategory == category
|
||||
? ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered])
|
||||
: ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.235f, 0.392f, 0.624f, 1.00f));
|
||||
|
||||
ImTextureID id = nullptr;
|
||||
if (std::holds_alternative<SDL_Texture*>(texture)) {
|
||||
id = ImTextureID(std::get<SDL_Texture*>(texture));
|
||||
} else if (std::holds_alternative<ImGui::RefCountedTexture>(texture)) {
|
||||
id = std::get<ImGui::RefCountedTexture>(texture).GetTexture().im_id;
|
||||
}
|
||||
|
||||
if (id != nullptr) {
|
||||
if (ImGui::ImageButton(name.c_str(), id,
|
||||
ImVec2(settingsIconSize * uiScale, settingsIconSize * uiScale))) {
|
||||
currentCategory = category;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SetCursorPosX(
|
||||
(ImGui::GetCursorPosX() +
|
||||
(settingsIconSize * uiScale - ImGui::CalcTextSize(name.c_str()).x) * 0.5f) +
|
||||
ImGui::GetStyle().FramePadding.x);
|
||||
ImGui::Text("%s", name.c_str());
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
|
||||
void SettingsWindow::DrawMainContent(bool* open) {
|
||||
ImVec4 settingsColor = ImVec4(0.1f, 0.1f, 0.12f, 0.8f); // Darker gray
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, settingsColor);
|
||||
ImGuiWindowFlags child_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NavFlattened;
|
||||
|
||||
std::string centeredText;
|
||||
currentCategory == SettingsCategory::Folders
|
||||
? centeredText = "Manage shadPS4 Game Folders"
|
||||
: centeredText = "Selected Profile: " + currentProfile;
|
||||
|
||||
ImGui::Separator();
|
||||
Overlay::TextCentered(centeredText.c_str());
|
||||
ImGui::Separator();
|
||||
|
||||
if (currentCategory == SettingsCategory::Profiles) {
|
||||
DrawProfileSelector();
|
||||
} else if (currentCategory == SettingsCategory::Folders) {
|
||||
DrawGameFolderManager();
|
||||
} else {
|
||||
DrawSettingsTable(currentCategory);
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::SetNextItemWidth(300.0f * uiScale);
|
||||
static float sliderScale2 = 1.0f;
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
sliderScale2 = uiScale;
|
||||
}
|
||||
|
||||
ImGui::SliderFloat("UI Scale", &sliderScale2, 0.25f, 3.0f);
|
||||
// Only update when user is not interacting with slider
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||
uiScale = sliderScale2;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
// Align buttons right
|
||||
float buttonsWidth = ImGui::CalcTextSize("Save").x + ImGui::CalcTextSize("Cancel").x +
|
||||
ImGui::CalcTextSize("Apply").x + ImGui::GetStyle().FramePadding.x * 6.0f +
|
||||
ImGui::GetStyle().ItemSpacing.x * 2;
|
||||
ImGui::SetCursorPosX(ImGui::GetWindowContentRegionMax().x - buttonsWidth);
|
||||
|
||||
if (ImGui::Button("Save")) {
|
||||
closeOnSave = true;
|
||||
ImGui::OpenPopup("Save Confirmation");
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Apply")) {
|
||||
ImGui::OpenPopup("Save Confirmation");
|
||||
}
|
||||
|
||||
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||
if (ImGui::BeginPopupModal("Save Confirmation", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("%s", ("Profile Saved:\n" + currentProfile).c_str());
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Button("OK", ImVec2(250 * uiScale, 0))) {
|
||||
std::string profile = currentProfile;
|
||||
if (currentProfile != "Global") {
|
||||
profile = currentProfile.substr(0, 9);
|
||||
}
|
||||
|
||||
SaveSettings(profile);
|
||||
if (closeOnSave) {
|
||||
DeInit();
|
||||
*open = false;
|
||||
closeOnSave = false;
|
||||
ImGui::CloseCurrentPopup();
|
||||
} else {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
DeInit();
|
||||
*open = false;
|
||||
}
|
||||
}
|
||||
|
||||
void SettingsWindow::DrawProfileSelector() {
|
||||
ImGuiWindowFlags child_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NavFlattened;
|
||||
ImVec4 settingsColor = ImVec4(0.1f, 0.1f, 0.12f, 0.8f); // Darker gray
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, settingsColor);
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ButtonTextAlign, ImVec2(0.0f, 0.5f));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_ChildBorderSize, 0.0f);
|
||||
|
||||
ImGui::BeginChild("Profile Selection", ImVec2(0, -ImGui::GetFrameHeightWithSpacing()), true,
|
||||
child_flags);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (ImGui::BeginTable("ProfilesTable", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_Borders)) {
|
||||
ImGui::TableSetupColumn("CreateDeleteButton", ImGuiTableColumnFlags_WidthFixed,
|
||||
400.0f * uiScale);
|
||||
ImGui::TableSetupColumn("Profile");
|
||||
|
||||
for (int i = 0; i < profileIcons.size(); i++) {
|
||||
const std::filesystem::path customConfigFile =
|
||||
Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) /
|
||||
(profileIcons[i].serial + ".json");
|
||||
const bool gameConfigExists = std::filesystem::exists(customConfigFile);
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
std::string deleteButtonLabel = "Delete game-specific config##" + std::to_string(i);
|
||||
|
||||
if (gameConfigExists) {
|
||||
// Different shades of red depending on button state
|
||||
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.6f, 0.0f, 0.0f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.8f, 0.0f, 0.0f, 1.0f));
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.5f, 0.0f, 0.0f, 1.0f));
|
||||
|
||||
if (ImGui::Button(deleteButtonLabel.c_str(),
|
||||
ImVec2(ImGui::GetContentRegionAvail().x, 0.0f))) {
|
||||
deleteProfileIndex = i;
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor(3);
|
||||
}
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
std::string profileLabel =
|
||||
i == 0 ? "Global" : profileIcons[i].serial + " - " + profileIcons[i].title;
|
||||
if (ImGui::Button(profileLabel.c_str(),
|
||||
ImVec2(ImGui::GetContentRegionAvail().x, 0.0f))) {
|
||||
|
||||
currentProfile = profileLabel;
|
||||
if (currentProfile == "Global") {
|
||||
LoadSettings("Global");
|
||||
} else {
|
||||
LoadSettings(profileIcons[i].serial);
|
||||
if (!gameConfigExists) {
|
||||
SaveSettings(profileIcons[i].serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
|
||||
if (deleteProfileIndex != -1) {
|
||||
ImGui::OpenPopup("Confirm Delete");
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar(3);
|
||||
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||
if (ImGui::BeginPopupModal("Confirm Delete", nullptr, ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
const std::string title = profileIcons[deleteProfileIndex].title;
|
||||
const std::string message = "Delete game-specific config file for " + title + "?";
|
||||
const std::filesystem::path path =
|
||||
Common::FS::GetUserPath(Common::FS::PathType::CustomConfigs) /
|
||||
(profileIcons[deleteProfileIndex].serial + ".json");
|
||||
|
||||
ImGui::Text("%s", message.c_str());
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Button("OK", ImVec2(120 * uiScale, 0))) {
|
||||
try {
|
||||
std::filesystem::remove(path);
|
||||
} catch (const std::exception& e) {
|
||||
LOG_ERROR(ImGui, "Could not delete config file {}: {}", path.string(), e.what());
|
||||
}
|
||||
|
||||
deleteProfileIndex = -1;
|
||||
currentProfile = "Global";
|
||||
LoadSettings("Global");
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
if (ImGui::Button("Cancel", ImVec2(120 * uiScale, 0))) {
|
||||
deleteProfileIndex = -1;
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void SettingsWindow::DrawGameFolderManager() {
|
||||
ImGuiWindowFlags child_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NavFlattened;
|
||||
ImGui::BeginChild("ContentRegion", ImVec2(0, -ImGui::GetFrameHeightWithSpacing()), true,
|
||||
child_flags);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void SettingsWindow::DrawSettingsTable(SettingsCategory category) {
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_CellPadding, ImVec2(4.0f * uiScale, 10.0f * uiScale));
|
||||
ImGuiWindowFlags child_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NavFlattened;
|
||||
|
||||
ImGui::BeginChild("ContentRegion", ImVec2(0, -ImGui::GetFrameHeightWithSpacing()), true,
|
||||
child_flags);
|
||||
|
||||
if (category == SettingsCategory::General) {
|
||||
if (ImGui::BeginTable("SettingsTable", 2)) {
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
AddSettingCombo("Console Language", consoleLanguageSetting, languageOptions);
|
||||
AddSettingSliderInt("Volume", volumeSetting, 0, 500);
|
||||
AddSettingCheckbox("Show Splash Screen When Launching Game", showSplashSetting);
|
||||
AddSettingCombo("Audio Backend", audioBackendSetting, audioBackendOptions);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
} else if (category == SettingsCategory::Graphics) {
|
||||
if (ImGui::BeginTable("SettingsTable", 2)) {
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
AddSettingCombo("Display Mode", fullscreenModeSetting, fullscreenModeOptions);
|
||||
AddSettingCombo("Present Mode", presentModeSetting, presentModeOptions);
|
||||
AddSettingSliderInt("Window Width", windowWidthSetting, 0, 8000);
|
||||
AddSettingSliderInt("Window Height", windowHeightSetting, 0, 7000);
|
||||
AddSettingCheckbox("Enable HDR", hdrAllowedSetting);
|
||||
AddSettingCheckbox("Enable FSR", fsrEnabledSetting);
|
||||
|
||||
if (fsrEnabledSetting) {
|
||||
AddSettingCheckbox("Enable RCAS", rcasEnabledSetting);
|
||||
}
|
||||
|
||||
if (rcasEnabledSetting && fsrEnabledSetting) {
|
||||
AddSettingSliderFloat("RCAS Attenuation", rcasAttenuationSetting, 0.0f, 3.0f, 3);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
} else if (category == SettingsCategory::Input) {
|
||||
if (ImGui::BeginTable("SettingsTable", 2)) {
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
AddSettingCheckbox("Enable Motion Controls", motionControlsSetting);
|
||||
AddSettingCheckbox("Enable Background Controller Input", backgroundControllerSetting);
|
||||
AddSettingCombo("Hide Cursor", cursorStateSetting, hideCursorOptions);
|
||||
|
||||
if (cursorStateSetting == 1) {
|
||||
AddSettingSliderInt("Hide Cursor Idle Timeout", cursorTimeoutSetting, 1, 10);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
} else if (category == SettingsCategory::Trophy) {
|
||||
if (ImGui::BeginTable("SettingsTable", 2)) {
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
AddSettingCheckbox("Disable Trophy Notification", trophyPopupDisabledSetting);
|
||||
if (!trophyPopupDisabledSetting) {
|
||||
AddSettingCombo("Trophy Notification Position", trophySideSetting,
|
||||
trophySideOptions);
|
||||
AddSettingSliderFloat("Trophy Notification Duration", trophyDurationSetting, 0.f,
|
||||
10.f, 1);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
} else if (category == SettingsCategory::Log) {
|
||||
if (ImGui::BeginTable("SettingsTable", 2)) {
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
AddSettingCheckbox("Enable Logging", logEnableSetting);
|
||||
if (logEnableSetting) {
|
||||
AddSettingCheckbox("Separate Log Files", logSeparateSetting);
|
||||
AddSettingCheckbox("Log Sync", logSyncSetting);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
} else if (category == SettingsCategory::Experimental) {
|
||||
if (ImGui::BeginTable("SettingsTable", 2)) {
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
AddSettingSliderInt("Additional DMem Allocation", extraDmemSetting, 0, 20000);
|
||||
AddSettingSliderInt("Vblank Frequency", vblankFrequencySetting, 30, 360);
|
||||
AddSettingCombo("Readbacks Mode", readbacksModeSetting, readbacksModeOptions);
|
||||
AddSettingCheckbox("Enable Readback Linear Images", readbackLinearImagesSetting);
|
||||
AddSettingCheckbox("Enable Direct Memory Access", directMemoryAccessSetting);
|
||||
AddSettingCheckbox("Enable Devkit Console Mode", devkitConsoleSetting);
|
||||
AddSettingCheckbox("Enable PS4 Neo Mode", neoModeSetting);
|
||||
AddSettingCheckbox("Enable ShadNet", shadnetEnabledSetting);
|
||||
AddSettingCheckbox("Set Network Connected to True", connectedNetworkSetting);
|
||||
AddSettingCheckbox("Enable Shader Cache", pipelineCacheEnabledSetting);
|
||||
|
||||
if (pipelineCacheEnabledSetting) {
|
||||
AddSettingCheckbox("Compress Shader Cache to Zip File",
|
||||
pipelineCacheArchiveSetting);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
void SettingsWindow::AddSettingCheckbox(std::string name, bool& value) {
|
||||
std::string label = "##" + name;
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::TextWrapped("%s", name.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Checkbox(label.c_str(), &value);
|
||||
}
|
||||
|
||||
void SettingsWindow::AddSettingSliderInt(std::string name, int& value, int min, int max) {
|
||||
std::string label = "##" + name;
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextWrapped("%s", name.c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SliderInt(label.c_str(), &value, min, max);
|
||||
}
|
||||
|
||||
void SettingsWindow::AddSettingSliderFloat(std::string name, float& value, int min, int max,
|
||||
int precision) {
|
||||
std::string label = "##" + name;
|
||||
std::string precisionString = "%." + std::to_string(precision) + "f";
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextWrapped("%s", name.c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SliderFloat(label.c_str(), &value, min, max, precisionString.c_str());
|
||||
}
|
||||
|
||||
void SettingsWindow::AddSettingCombo(std::string name, int& value,
|
||||
std::vector<std::string> options) {
|
||||
std::string label = "##" + name;
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextWrapped("%s", name.c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
const char* combo_value = options[value].c_str();
|
||||
if (ImGui::BeginCombo(label.c_str(), combo_value)) {
|
||||
for (int i = 0; i < options.size(); i++) {
|
||||
const bool selected = (i == value);
|
||||
if (ImGui::Selectable(options[i].c_str(), selected))
|
||||
value = i;
|
||||
|
||||
// Set the initial focus when opening the combo
|
||||
if (selected)
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace ImGuiEmuSettings
|
||||
168
src/imgui/big_picture/settings_dialog_imgui.h
Normal file
168
src/imgui/big_picture/settings_dialog_imgui.h
Normal file
@ -0,0 +1,168 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <variant>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <imgui.h>
|
||||
|
||||
#include "big_picture.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "imgui/imgui_texture.h"
|
||||
|
||||
namespace ImGuiEmuSettings {
|
||||
|
||||
class SettingsWindow {
|
||||
|
||||
public:
|
||||
SettingsWindow(bool gameRunning);
|
||||
void DrawSettings(bool* open);
|
||||
|
||||
private:
|
||||
enum class SettingsCategory {
|
||||
Profiles,
|
||||
General,
|
||||
Graphics,
|
||||
Input,
|
||||
Trophy,
|
||||
Folders,
|
||||
Log,
|
||||
Experimental,
|
||||
};
|
||||
|
||||
void SaveSettings(std::string profile);
|
||||
void LoadSettings(std::string profile);
|
||||
void SaveInstallDirs();
|
||||
|
||||
void SetupWindow();
|
||||
void DeInit();
|
||||
void GetProfileInfo();
|
||||
|
||||
void DrawMainContent(bool* open);
|
||||
void DrawSettingsTable(SettingsCategory);
|
||||
void DrawProfileSelector();
|
||||
void DrawGameFolderManager();
|
||||
void DrawCategoryTabs();
|
||||
void AddCategory(std::string name, std::variant<SDL_Texture*, ImGui::RefCountedTexture> texture,
|
||||
SettingsCategory category);
|
||||
|
||||
void AddSettingCheckbox(std::string name, bool& value);
|
||||
void AddSettingSliderInt(std::string name, int& value, int min, int max);
|
||||
void AddSettingSliderFloat(std::string name, float& value, int min, int max, int precision);
|
||||
void AddSettingCombo(std::string name, int& value, std::vector<std::string> options);
|
||||
int GetComboIndex(std::string selection, std::vector<std::string> options);
|
||||
|
||||
std::vector<BigPictureMode::IconInfo> profileIcons = {};
|
||||
std::vector<GameInstallDir> m_GameInstallDirs = {};
|
||||
|
||||
float uiScale = 1.0f;
|
||||
SettingsCategory currentCategory = SettingsCategory::Profiles;
|
||||
std::string currentProfile = "Global";
|
||||
|
||||
std::string runningGameSerial = "";
|
||||
bool isGameRunning = false;
|
||||
bool closeOnSave = false;
|
||||
int deleteProfileIndex = -1;
|
||||
|
||||
std::variant<SDL_Texture*, ImGui::RefCountedTexture> profilesTexture;
|
||||
std::variant<SDL_Texture*, ImGui::RefCountedTexture> generalTexture;
|
||||
std::variant<SDL_Texture*, ImGui::RefCountedTexture> experimentalTexture;
|
||||
std::variant<SDL_Texture*, ImGui::RefCountedTexture> graphicsTexture;
|
||||
std::variant<SDL_Texture*, ImGui::RefCountedTexture> inputTexture;
|
||||
std::variant<SDL_Texture*, ImGui::RefCountedTexture> trophyTexture;
|
||||
std::variant<SDL_Texture*, ImGui::RefCountedTexture> logTexture;
|
||||
std::variant<SDL_Texture*, ImGui::RefCountedTexture> foldersTexture;
|
||||
|
||||
//////////////////// options for comboboxes
|
||||
const std::map<std::string, int> languageMap = {{"Arabic", 21},
|
||||
{"Czech", 23},
|
||||
{"Danish", 14},
|
||||
{"Dutch", 6},
|
||||
{"English (United Kingdom)", 18},
|
||||
{"English (United States)", 1},
|
||||
{"Finnish", 12},
|
||||
{"French (Canada)", 22},
|
||||
{"French (France)", 2},
|
||||
{"German", 4},
|
||||
{"Greek", 25},
|
||||
{"Hungarian", 24},
|
||||
{"Indonesian", 29},
|
||||
{"Italian", 5},
|
||||
{"Japanese", 0},
|
||||
{"Korean", 9},
|
||||
{"Norwegian (Bokmaal)", 15},
|
||||
{"Polish", 16},
|
||||
{"Portuguese (Brazil)", 17},
|
||||
{"Portuguese (Portugal)", 7},
|
||||
{"Romanian", 26},
|
||||
{"Russian", 8},
|
||||
{"Simplified Chinese", 11},
|
||||
{"Spanish (Latin America)", 20},
|
||||
{"Spanish (Spain)", 3},
|
||||
{"Swedish", 13},
|
||||
{"Thai", 27},
|
||||
{"Traditional Chinese", 10},
|
||||
{"Turkish", 19},
|
||||
{"Ukrainian", 30},
|
||||
{"Vietnamese", 28}};
|
||||
std::vector<std::string> languageOptions; // assigned from keys above
|
||||
const std::vector<std::string> fullscreenModeOptions = {"Windowed", "Fullscreen",
|
||||
"Fullscreen (Borderless)"};
|
||||
const std::vector<std::string> audioBackendOptions = {"SDL", "OpenAL"};
|
||||
const std::vector<std::string> presentModeOptions = {"Mailbox", "Fifo", "Immediate"};
|
||||
const std::vector<std::string> hideCursorOptions = {"Never", "Idle", "Always"};
|
||||
const std::vector<std::string> trophySideOptions = {"left", "right", "top", "bottom"};
|
||||
const std::vector<std::string> readbacksModeOptions = {"Disabled", "Relaxed", "Precise"};
|
||||
|
||||
//////////////// Setting Variables
|
||||
//////////////// Note:: Use int for all comboboxes as needed by ImGui
|
||||
|
||||
// General tab
|
||||
int consoleLanguageSetting;
|
||||
int volumeSetting;
|
||||
bool showSplashSetting;
|
||||
int audioBackendSetting;
|
||||
|
||||
// Graphics tab
|
||||
int fullscreenModeSetting;
|
||||
int presentModeSetting;
|
||||
int windowWidthSetting;
|
||||
int windowHeightSetting;
|
||||
bool hdrAllowedSetting;
|
||||
bool fsrEnabledSetting;
|
||||
bool rcasEnabledSetting;
|
||||
float rcasAttenuationSetting;
|
||||
|
||||
// Input tab
|
||||
bool motionControlsSetting;
|
||||
bool backgroundControllerSetting;
|
||||
int cursorStateSetting;
|
||||
int cursorTimeoutSetting;
|
||||
|
||||
// Trophy tab
|
||||
bool trophyPopupDisabledSetting;
|
||||
int trophySideSetting;
|
||||
float trophyDurationSetting;
|
||||
|
||||
// Log tab
|
||||
bool logEnableSetting;
|
||||
bool logSeparateSetting;
|
||||
bool logSyncSetting;
|
||||
|
||||
// Experimental tab
|
||||
int readbacksModeSetting;
|
||||
bool readbackLinearImagesSetting;
|
||||
bool directMemoryAccessSetting;
|
||||
bool devkitConsoleSetting;
|
||||
bool neoModeSetting;
|
||||
bool shadnetEnabledSetting;
|
||||
bool connectedNetworkSetting;
|
||||
bool pipelineCacheEnabledSetting;
|
||||
bool pipelineCacheArchiveSetting;
|
||||
int extraDmemSetting;
|
||||
int vblankFrequencySetting;
|
||||
};
|
||||
|
||||
} // namespace ImGuiEmuSettings
|
||||
44
src/imgui/big_picture/settings_dialog_layer.cpp
Normal file
44
src/imgui/big_picture/settings_dialog_layer.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <imgui.h>
|
||||
|
||||
#include "imgui/imgui_std.h"
|
||||
#include "settings_dialog_layer.h"
|
||||
|
||||
namespace ImGuiEmuSettings {
|
||||
|
||||
static bool running = false;
|
||||
static std::optional<SettingsLayer> settingsLayer = std::nullopt;
|
||||
|
||||
SettingsLayer::SettingsLayer() {
|
||||
AddLayer(this);
|
||||
}
|
||||
|
||||
SettingsLayer::~SettingsLayer() {}
|
||||
|
||||
void SettingsLayer::Finish() {
|
||||
RemoveLayer(this);
|
||||
settingsLayer.reset();
|
||||
}
|
||||
|
||||
void SettingsLayer::Draw() {
|
||||
ImGui::PushFont(ImGui::GetIO().Fonts->Fonts[IMGUI_FONT_SETTINGS_WINDOW]);
|
||||
settingsWindow.DrawSettings(&running);
|
||||
ImGui::PopFont();
|
||||
|
||||
if (!running) {
|
||||
Finish();
|
||||
}
|
||||
}
|
||||
|
||||
void OpenInGameSettingsDialog() {
|
||||
if (settingsLayer.has_value()) {
|
||||
return;
|
||||
}
|
||||
|
||||
settingsLayer.emplace();
|
||||
running = true;
|
||||
}
|
||||
|
||||
} // namespace ImGuiEmuSettings
|
||||
27
src/imgui/big_picture/settings_dialog_layer.h
Normal file
27
src/imgui/big_picture/settings_dialog_layer.h
Normal file
@ -0,0 +1,27 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2026 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL.h>
|
||||
|
||||
#include "imgui/imgui_layer.h"
|
||||
#include "settings_dialog_imgui.h"
|
||||
|
||||
namespace ImGuiEmuSettings {
|
||||
|
||||
class SettingsLayer final : public ImGui::Layer {
|
||||
public:
|
||||
SettingsLayer();
|
||||
~SettingsLayer() override;
|
||||
|
||||
void Draw() override;
|
||||
|
||||
private:
|
||||
void Finish();
|
||||
SettingsWindow settingsWindow = SettingsWindow(true);
|
||||
};
|
||||
|
||||
void OpenInGameSettingsDialog();
|
||||
|
||||
}; // namespace ImGuiEmuSettings
|
||||
@ -15,6 +15,7 @@
|
||||
#define IMGUI_FONT_TEXT 0
|
||||
#define IMGUI_FONT_MONO 1
|
||||
#define IMGUI_FONT_TEXT_BIG 2
|
||||
#define IMGUI_FONT_SETTINGS_WINDOW 3
|
||||
|
||||
namespace ImGui {
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ constexpr int kNotoSansCjkFontIndexTc = 3;
|
||||
constexpr ImWchar kLatinExtendedRanges[] = {
|
||||
0x0100, 0x024F, // Latin Extended-A + Latin Extended-B
|
||||
0x1E00, 0x1EFF, // Latin Extended Additional
|
||||
0x2122, 0x2122, // TM symbol
|
||||
0,
|
||||
};
|
||||
|
||||
|
||||
@ -75,6 +75,11 @@ void Initialize(const ::Vulkan::Instance& instance, const Frontend::WindowSDL& w
|
||||
|
||||
// Avoid exploding atlas size on Metal/MoltenVK when CJK fallback is enabled.
|
||||
FontStack::AddPrimaryUiFont(io.Fonts, 128.0f, console_language, font_cfg, false);
|
||||
|
||||
// Big Picture
|
||||
FontStack::AddPrimaryUiFont(ImGui::GetIO().Fonts, 64.0f, EmulatorSettings.GetConsoleLanguage(),
|
||||
font_cfg, true);
|
||||
|
||||
io.Fonts->Build();
|
||||
|
||||
io.FontGlobalScale = 0.5f;
|
||||
|
||||
@ -1,802 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#include <map>
|
||||
#include <ranges>
|
||||
#include <ImGuiFileDialog.h>
|
||||
#include <cmrc/cmrc.hpp>
|
||||
#include <stb_image.h>
|
||||
|
||||
#include "core/devtools/layer.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "imgui/imgui_std.h"
|
||||
#include "settings_dialog_imgui.h"
|
||||
|
||||
CMRC_DECLARE(res);
|
||||
namespace BigPictureMode {
|
||||
|
||||
//////////////////// options for comboboxes
|
||||
const std::map<std::string, int> languageMap = {{"Arabic", 21},
|
||||
{"Czech", 23},
|
||||
{"Danish", 14},
|
||||
{"Dutch", 6},
|
||||
{"English (United Kingdom)", 18},
|
||||
{"English (United States)", 1},
|
||||
{"Finnish", 12},
|
||||
{"French (Canada)", 22},
|
||||
{"French (France)", 2},
|
||||
{"German", 4},
|
||||
{"Greek", 25},
|
||||
{"Hungarian", 24},
|
||||
{"Indonesian", 29},
|
||||
{"Italian", 5},
|
||||
{"Japanese", 0},
|
||||
{"Korean", 9},
|
||||
{"Norwegian (Bokmaal)", 15},
|
||||
{"Polish", 16},
|
||||
{"Portuguese (Brazil)", 17},
|
||||
{"Portuguese (Portugal)", 7},
|
||||
{"Romanian", 26},
|
||||
{"Russian", 8},
|
||||
{"Simplified Chinese", 11},
|
||||
{"Spanish (Latin America)", 20},
|
||||
{"Spanish (Spain)", 3},
|
||||
{"Swedish", 13},
|
||||
{"Thai", 27},
|
||||
{"Traditional Chinese", 10},
|
||||
{"Turkish", 19},
|
||||
{"Ukrainian", 30},
|
||||
{"Vietnamese", 28}};
|
||||
std::vector<std::string> languageOptions; // assigned from keys above
|
||||
const std::vector<std::string> fullscreenModeOptions = {"Windowed", "Fullscreen",
|
||||
"Fullscreen (Borderless)"};
|
||||
const std::vector<std::string> audioBackendOptions = {"SDL", "OpenAL"};
|
||||
const std::vector<std::string> presentModeOptions = {"Mailbox", "Fifo", "Immediate"};
|
||||
const std::vector<std::string> hideCursorOptions = {"Never", "Idle", "Always"};
|
||||
const std::vector<std::string> trophySideOptions = {"left", "right", "top", "bottom"};
|
||||
const std::vector<std::string> readbacksModeOptions = {"Disabled", "Relaxed", "Precise"};
|
||||
|
||||
//////////////// Setting Variables
|
||||
//////////////// Note:: Use int for all comboboxes as needed by ImGui
|
||||
|
||||
// General tab
|
||||
int consoleLanguageSetting;
|
||||
int volumeSetting;
|
||||
bool showSplashSetting;
|
||||
int audioBackendSetting;
|
||||
|
||||
// Graphics tab
|
||||
int fullscreenModeSetting;
|
||||
int presentModeSetting;
|
||||
int windowWidthSetting;
|
||||
int windowHeightSetting;
|
||||
bool hdrAllowedSetting;
|
||||
bool fsrEnabledSetting;
|
||||
bool rcasEnabledSetting;
|
||||
float rcasAttenuationSetting;
|
||||
|
||||
// Input tab
|
||||
bool motionControlsSetting;
|
||||
bool backgroundControllerSetting;
|
||||
int cursorStateSetting;
|
||||
int cursorTimeoutSetting;
|
||||
|
||||
// Trophy tab
|
||||
bool trophyPopupDisabledSetting;
|
||||
int trophySideSetting;
|
||||
float trophyDurationSetting;
|
||||
|
||||
// Log tab
|
||||
bool logEnableSetting;
|
||||
bool logSeparateSetting;
|
||||
bool logSyncSetting;
|
||||
|
||||
// Experimental tab
|
||||
int readbacksModeSetting;
|
||||
bool readbackLinearImagesSetting;
|
||||
bool directMemoryAccessSetting;
|
||||
bool devkitConsoleSetting;
|
||||
bool neoModeSetting;
|
||||
bool shadnetEnabledSetting;
|
||||
bool connectedNetworkSetting;
|
||||
bool pipelineCacheEnabledSetting;
|
||||
bool pipelineCacheArchiveSetting;
|
||||
int extraDmemSetting;
|
||||
int vblankFrequencySetting;
|
||||
|
||||
//////////////// Texture data
|
||||
SDL_Texture* profilesTexture;
|
||||
SDL_Texture* generalTexture;
|
||||
SDL_Texture* globalSettingsTexture;
|
||||
SDL_Texture* experimentalTexture;
|
||||
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<Game> settingsProfileVec = {};
|
||||
std::vector<GameInstallDir> m_GameInstallDirs = {};
|
||||
|
||||
float uiScale = 1.0f;
|
||||
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);
|
||||
languageOptions.assign(languageKeys.begin(), languageKeys.end());
|
||||
|
||||
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/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<float>(EmulatorSettings.GetBigPictureScale() / 1000.f);
|
||||
}
|
||||
|
||||
void DeInit() {
|
||||
EmulatorSettings.Load();
|
||||
EmulatorSettings.SetBigPictureScale(static_cast<int>(uiScale * 1000));
|
||||
EmulatorSettings.Save();
|
||||
}
|
||||
|
||||
void DrawSettings(bool* open) {
|
||||
if (!*open)
|
||||
return;
|
||||
|
||||
const ImGuiViewport* viewport = ImGui::GetMainViewport();
|
||||
ImGui::SetNextWindowPos(viewport->WorkPos);
|
||||
ImGui::SetNextWindowSize(viewport->WorkSize);
|
||||
|
||||
if (ImGui::Begin("Settings", nullptr,
|
||||
ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoScrollWithMouse)) {
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
Init();
|
||||
closeOnSave = false;
|
||||
}
|
||||
|
||||
ImGui::DrawPrettyBackground();
|
||||
ImGui::SetWindowFontScale(uiScale);
|
||||
ImGuiWindowFlags child_flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
|
||||
ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NavFlattened;
|
||||
|
||||
ImVec4 settingsColor = ImVec4(0.1f, 0.1f, 0.12f, 0.8f); // Darker gray
|
||||
ImGui::PushStyleColor(ImGuiCol_ChildBg, settingsColor);
|
||||
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));
|
||||
|
||||
// Must add categories in enum order for L1/R1 to work correctly, with experimental last
|
||||
AddCategory("Profiles", profilesTexture, SettingsCategory::Profiles);
|
||||
AddCategory("General", generalTexture, SettingsCategory::General);
|
||||
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")
|
||||
AddCategory("Experimental", experimentalTexture, SettingsCategory::Experimental);
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::EndChild(); // Categories
|
||||
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
ImGui::SetKeyboardFocusHere();
|
||||
}
|
||||
|
||||
ImGui::BeginChild("ContentRegion", ImVec2(0, -ImGui::GetFrameHeightWithSpacing()), true,
|
||||
child_flags);
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
LoadCategory(currentCategory);
|
||||
|
||||
ImGui::EndChild();
|
||||
ImGui::Separator();
|
||||
|
||||
ImGui::SetNextItemWidth(300.0f * uiScale);
|
||||
static float sliderScale2 = 1.0f;
|
||||
if (ImGui::IsWindowAppearing()) {
|
||||
sliderScale2 = uiScale;
|
||||
}
|
||||
|
||||
ImGui::SliderFloat("UI Scale", &sliderScale2, 0.25f, 3.0f);
|
||||
// Only update when user is not interacting with slider
|
||||
if (ImGui::IsItemDeactivatedAfterEdit()) {
|
||||
uiScale = sliderScale2;
|
||||
}
|
||||
ImGui::SameLine();
|
||||
|
||||
// Align buttons right
|
||||
float buttonsWidth = ImGui::CalcTextSize("Save").x + ImGui::CalcTextSize("Cancel").x +
|
||||
ImGui::CalcTextSize("Apply").x +
|
||||
ImGui::GetStyle().FramePadding.x * 6.0f +
|
||||
ImGui::GetStyle().ItemSpacing.x * 2;
|
||||
ImGui::SetCursorPosX(ImGui::GetWindowContentRegionMax().x - buttonsWidth);
|
||||
|
||||
if (ImGui::Button("Save")) {
|
||||
closeOnSave = true;
|
||||
ImGui::OpenPopup("Save Confirmation");
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Apply")) {
|
||||
ImGui::OpenPopup("Save Confirmation");
|
||||
}
|
||||
|
||||
ImVec2 center = ImGui::GetMainViewport()->GetCenter();
|
||||
ImGui::SetNextWindowPos(center, ImGuiCond_Appearing, ImVec2(0.5f, 0.5f));
|
||||
if (ImGui::BeginPopupModal("Save Confirmation", nullptr,
|
||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
||||
ImGui::Text("%s", ("Profile Saved:\n" + currentProfile).c_str());
|
||||
ImGui::Separator();
|
||||
|
||||
if (ImGui::Button("OK", ImVec2(250 * uiScale, 0))) {
|
||||
std::string profile = currentProfile;
|
||||
if (currentProfile != "Global") {
|
||||
profile = currentProfile.substr(0, 9);
|
||||
}
|
||||
|
||||
SaveSettings(profile);
|
||||
if (closeOnSave) {
|
||||
DeInit();
|
||||
*open = false;
|
||||
ImGui::CloseCurrentPopup();
|
||||
} else {
|
||||
ImGui::CloseCurrentPopup();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::EndPopup();
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Cancel")) {
|
||||
DeInit();
|
||||
*open = false;
|
||||
}
|
||||
|
||||
SettingsCategory lastCategory =
|
||||
currentProfile != "Global" ? SettingsCategory::Experimental : SettingsCategory::Log;
|
||||
// Navigate categories with Tab / R1 / L1
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_GamepadR1) || ImGui::IsKeyPressed(ImGuiKey_Tab)) {
|
||||
int currentIndex = static_cast<int>(currentCategory);
|
||||
currentCategory == lastCategory
|
||||
? currentCategory = static_cast<SettingsCategory>(0)
|
||||
: currentCategory = static_cast<SettingsCategory>(currentIndex + 1);
|
||||
}
|
||||
|
||||
if (ImGui::IsKeyPressed(ImGuiKey_GamepadL1)) {
|
||||
int currentIndex = static_cast<int>(currentCategory);
|
||||
currentIndex == 0 ? currentCategory = lastCategory
|
||||
: currentCategory = static_cast<SettingsCategory>(currentIndex - 1);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::End();
|
||||
}
|
||||
|
||||
void LoadCategory(SettingsCategory category) {
|
||||
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);
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
AddSettingCombo("Console Language", consoleLanguageSetting, languageOptions);
|
||||
AddSettingSliderInt("Volume", volumeSetting, 0, 500);
|
||||
AddSettingBool("Show Splash Screen When Launching Game", showSplashSetting);
|
||||
AddSettingCombo("Audio Backend", audioBackendSetting, audioBackendOptions);
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
} else if (category == SettingsCategory::Graphics) {
|
||||
if (ImGui::BeginTable("SettingsTable", 2)) {
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
AddSettingCombo("Display Mode", fullscreenModeSetting, fullscreenModeOptions);
|
||||
AddSettingCombo("Present Mode", presentModeSetting, presentModeOptions);
|
||||
AddSettingSliderInt("Window Width", windowWidthSetting, 0, 8000);
|
||||
AddSettingSliderInt("Window Height", windowHeightSetting, 0, 7000);
|
||||
AddSettingBool("Enable HDR", hdrAllowedSetting);
|
||||
AddSettingBool("Enable FSR", fsrEnabledSetting);
|
||||
|
||||
if (fsrEnabledSetting) {
|
||||
AddSettingBool("Enable RCAS", rcasEnabledSetting);
|
||||
}
|
||||
|
||||
if (rcasEnabledSetting && fsrEnabledSetting) {
|
||||
AddSettingSliderFloat("RCAS Attenuation", rcasAttenuationSetting, 0.0f, 3.0f, 3);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
} else if (category == SettingsCategory::Input) {
|
||||
if (ImGui::BeginTable("SettingsTable", 2)) {
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
AddSettingBool("Enable Motion Controls", motionControlsSetting);
|
||||
AddSettingBool("Enable Background Controller Input", backgroundControllerSetting);
|
||||
AddSettingCombo("Hide Cursor", cursorStateSetting, hideCursorOptions);
|
||||
|
||||
if (cursorStateSetting == 1) {
|
||||
AddSettingSliderInt("Hide Cursor Idle Timeout", cursorTimeoutSetting, 1, 10);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
} else if (category == SettingsCategory::Trophy) {
|
||||
if (ImGui::BeginTable("SettingsTable", 2)) {
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
AddSettingBool("Disable Trophy Notification", trophyPopupDisabledSetting);
|
||||
if (!trophyPopupDisabledSetting) {
|
||||
AddSettingCombo("Trophy Notification Position", trophySideSetting,
|
||||
trophySideOptions);
|
||||
AddSettingSliderFloat("Trophy Notification Duration", trophyDurationSetting, 0.f,
|
||||
10.f, 1);
|
||||
}
|
||||
|
||||
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);
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
AddSettingBool("Enable Logging", logEnableSetting);
|
||||
if (logEnableSetting) {
|
||||
AddSettingBool("Separate Log Files", logSeparateSetting);
|
||||
AddSettingBool("Log Sync", logSyncSetting);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
} else if (category == SettingsCategory::Experimental) {
|
||||
if (ImGui::BeginTable("SettingsTable", 2)) {
|
||||
ImGui::TableSetupColumn("Name", ImGuiTableColumnFlags_WidthFixed, 500.0f * uiScale);
|
||||
ImGui::TableSetupColumn("Value");
|
||||
|
||||
AddSettingSliderInt("Additional DMem Allocation", extraDmemSetting, 0, 20000);
|
||||
AddSettingSliderInt("Vblank Frequency", vblankFrequencySetting, 30, 360);
|
||||
AddSettingCombo("Readbacks Mode", readbacksModeSetting, readbacksModeOptions);
|
||||
AddSettingBool("Enable Readback Linear Images", readbackLinearImagesSetting);
|
||||
AddSettingBool("Enable Direct Memory Access", directMemoryAccessSetting);
|
||||
AddSettingBool("Enable Devkit Console Mode", devkitConsoleSetting);
|
||||
AddSettingBool("Enable PS4 Neo Mode", neoModeSetting);
|
||||
AddSettingBool("Enable ShadNet", shadnetEnabledSetting);
|
||||
AddSettingBool("Set Network Connected to True", connectedNetworkSetting);
|
||||
AddSettingBool("Enable Shader Cache", pipelineCacheEnabledSetting);
|
||||
|
||||
if (pipelineCacheEnabledSetting) {
|
||||
AddSettingBool("Compress Shader Cache to Zip File", pipelineCacheArchiveSetting);
|
||||
}
|
||||
|
||||
ImGui::EndTable();
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopStyleVar();
|
||||
|
||||
// Child Window if Needed
|
||||
if (category == SettingsCategory::Profiles) {
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
void SaveSettings(std::string profile) {
|
||||
const bool isSpecific = currentProfile != "Global";
|
||||
|
||||
/////////// General Tab
|
||||
EmulatorSettings.SetConsoleLanguage(languageMap.at(languageOptions.at(consoleLanguageSetting)),
|
||||
isSpecific);
|
||||
EmulatorSettings.SetVolumeSlider(volumeSetting, isSpecific);
|
||||
EmulatorSettings.SetShowSplash(showSplashSetting, isSpecific);
|
||||
EmulatorSettings.SetAudioBackend(audioBackendSetting, isSpecific);
|
||||
|
||||
/////////// Graphics Tab
|
||||
bool isFullscreen = fullscreenModeSetting != 0;
|
||||
EmulatorSettings.SetFullScreen(isFullscreen);
|
||||
EmulatorSettings.SetFullScreenMode(fullscreenModeOptions.at(fullscreenModeSetting), isSpecific);
|
||||
EmulatorSettings.SetPresentMode(presentModeOptions.at(presentModeSetting), isSpecific);
|
||||
EmulatorSettings.SetWindowHeight(windowHeightSetting, isSpecific);
|
||||
EmulatorSettings.SetWindowWidth(windowWidthSetting, isSpecific);
|
||||
EmulatorSettings.SetHdrAllowed(hdrAllowedSetting, isSpecific);
|
||||
EmulatorSettings.SetFsrEnabled(fsrEnabledSetting, isSpecific);
|
||||
EmulatorSettings.SetRcasEnabled(rcasEnabledSetting, isSpecific);
|
||||
EmulatorSettings.SetRcasAttenuation(static_cast<int>(rcasAttenuationSetting * 1000),
|
||||
isSpecific);
|
||||
|
||||
/////////// Input Tab
|
||||
EmulatorSettings.SetMotionControlsEnabled(motionControlsSetting, isSpecific);
|
||||
EmulatorSettings.SetBackgroundControllerInput(backgroundControllerSetting, isSpecific);
|
||||
EmulatorSettings.SetCursorState(cursorStateSetting, isSpecific);
|
||||
EmulatorSettings.SetCursorHideTimeout(cursorTimeoutSetting, isSpecific);
|
||||
|
||||
/////////// Trophy Tab
|
||||
EmulatorSettings.SetTrophyPopupDisabled(trophyPopupDisabledSetting, isSpecific);
|
||||
EmulatorSettings.SetTrophyNotificationSide(trophySideOptions.at(trophySideSetting), isSpecific);
|
||||
EmulatorSettings.SetTrophyNotificationDuration(static_cast<double>(trophyDurationSetting));
|
||||
|
||||
/////////// Log Tab
|
||||
EmulatorSettings.SetLogEnable(logEnableSetting, isSpecific);
|
||||
EmulatorSettings.SetLogSeparate(logSeparateSetting, isSpecific);
|
||||
EmulatorSettings.SetLogSync(logSyncSetting, isSpecific);
|
||||
|
||||
/////////// Experimental Tab
|
||||
if (isSpecific) {
|
||||
EmulatorSettings.SetReadbacksMode(readbacksModeSetting, true);
|
||||
EmulatorSettings.SetReadbackLinearImagesEnabled(readbackLinearImagesSetting, true);
|
||||
EmulatorSettings.SetDirectMemoryAccessEnabled(directMemoryAccessSetting, true);
|
||||
EmulatorSettings.SetDevKit(devkitConsoleSetting, true);
|
||||
EmulatorSettings.SetNeo(neoModeSetting, true);
|
||||
EmulatorSettings.SetShadNetEnabled(shadnetEnabledSetting, true);
|
||||
EmulatorSettings.SetConnectedToNetwork(connectedNetworkSetting, true);
|
||||
EmulatorSettings.SetPipelineCacheEnabled(pipelineCacheEnabledSetting, true);
|
||||
EmulatorSettings.SetPipelineCacheArchived(pipelineCacheArchiveSetting, true);
|
||||
EmulatorSettings.SetExtraDmemInMBytes(extraDmemSetting, true);
|
||||
EmulatorSettings.SetVblankFrequency(vblankFrequencySetting, true);
|
||||
}
|
||||
|
||||
if (!isSpecific) {
|
||||
EmulatorSettings.Save();
|
||||
} else {
|
||||
EmulatorSettings.Save(profile);
|
||||
}
|
||||
}
|
||||
|
||||
void LoadSettings(std::string profile) {
|
||||
const bool isSpecific = currentProfile != "Global";
|
||||
if (!isSpecific) {
|
||||
EmulatorSettings.Load();
|
||||
} else {
|
||||
EmulatorSettings.Load(profile);
|
||||
}
|
||||
|
||||
/////////// General Tab
|
||||
int languageIndex = EmulatorSettings.GetConsoleLanguage();
|
||||
std::string language;
|
||||
for (const auto& [key, value] : languageMap) {
|
||||
if (value == languageIndex) {
|
||||
language = key;
|
||||
}
|
||||
}
|
||||
consoleLanguageSetting = GetComboIndex(language, languageOptions);
|
||||
volumeSetting = EmulatorSettings.GetVolumeSlider();
|
||||
showSplashSetting = EmulatorSettings.IsShowSplash();
|
||||
audioBackendSetting = EmulatorSettings.GetAudioBackend();
|
||||
|
||||
/////////// Graphics Tab
|
||||
fullscreenModeSetting =
|
||||
GetComboIndex(EmulatorSettings.GetFullScreenMode(), fullscreenModeOptions);
|
||||
presentModeSetting = GetComboIndex(EmulatorSettings.GetPresentMode(), presentModeOptions);
|
||||
windowHeightSetting = EmulatorSettings.GetWindowHeight();
|
||||
windowWidthSetting = EmulatorSettings.GetWindowWidth();
|
||||
hdrAllowedSetting = EmulatorSettings.IsHdrAllowed();
|
||||
fsrEnabledSetting = EmulatorSettings.IsFsrEnabled();
|
||||
rcasEnabledSetting = EmulatorSettings.IsRcasEnabled();
|
||||
rcasAttenuationSetting = static_cast<float>(EmulatorSettings.GetRcasAttenuation() * 0.001f);
|
||||
|
||||
/////////// Input Tab
|
||||
motionControlsSetting = EmulatorSettings.IsMotionControlsEnabled();
|
||||
backgroundControllerSetting = EmulatorSettings.IsBackgroundControllerInput();
|
||||
cursorStateSetting = EmulatorSettings.GetCursorState();
|
||||
cursorTimeoutSetting = EmulatorSettings.GetCursorHideTimeout();
|
||||
|
||||
/////////// Trophy Tab
|
||||
trophyPopupDisabledSetting = EmulatorSettings.IsTrophyPopupDisabled();
|
||||
trophySideSetting =
|
||||
GetComboIndex(EmulatorSettings.GetTrophyNotificationSide(), trophySideOptions);
|
||||
trophyDurationSetting = static_cast<float>(EmulatorSettings.GetTrophyNotificationDuration());
|
||||
|
||||
/////////// Log Tab
|
||||
logEnableSetting = EmulatorSettings.IsLogEnable();
|
||||
logSeparateSetting = EmulatorSettings.IsLogSeparate();
|
||||
logSyncSetting = EmulatorSettings.IsLogSync();
|
||||
|
||||
/////////// Experimental Tab
|
||||
if (isSpecific) {
|
||||
readbacksModeSetting = EmulatorSettings.GetReadbacksMode();
|
||||
readbackLinearImagesSetting = EmulatorSettings.IsReadbackLinearImagesEnabled();
|
||||
directMemoryAccessSetting = EmulatorSettings.IsDirectMemoryAccessEnabled();
|
||||
devkitConsoleSetting = EmulatorSettings.IsDevKit();
|
||||
neoModeSetting = EmulatorSettings.IsNeo();
|
||||
shadnetEnabledSetting = EmulatorSettings.IsShadNetEnabled();
|
||||
connectedNetworkSetting = EmulatorSettings.IsConnectedToNetwork();
|
||||
pipelineCacheEnabledSetting = EmulatorSettings.IsPipelineCacheEnabled();
|
||||
pipelineCacheArchiveSetting = EmulatorSettings.IsPipelineCacheArchived();
|
||||
extraDmemSetting = EmulatorSettings.GetExtraDmemInMBytes();
|
||||
vblankFrequencySetting = EmulatorSettings.GetVblankFrequency();
|
||||
}
|
||||
}
|
||||
|
||||
void AddCategory(std::string name, SDL_Texture* texture, SettingsCategory category) {
|
||||
ImGui::SameLine();
|
||||
ImGui::BeginGroup();
|
||||
|
||||
// make button appear hovered as long as category is selected, otherwise dull it's hovered color
|
||||
currentCategory == category
|
||||
? ImGui::PushStyleColor(ImGuiCol_Button, ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered])
|
||||
: ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.235f, 0.392f, 0.624f, 1.00f));
|
||||
|
||||
if (ImGui::ImageButton(name.c_str(), ImTextureID(texture),
|
||||
ImVec2(settingsIconSize * uiScale, settingsIconSize * uiScale))) {
|
||||
currentCategory = category;
|
||||
}
|
||||
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
ImGui::SetCursorPosX(
|
||||
(ImGui::GetCursorPosX() +
|
||||
(settingsIconSize * uiScale - ImGui::CalcTextSize(name.c_str()).x) * 0.5f) +
|
||||
ImGui::GetStyle().FramePadding.x);
|
||||
ImGui::Text("%s", name.c_str());
|
||||
ImGui::EndGroup();
|
||||
}
|
||||
|
||||
void AddSettingBool(std::string name, bool& value) {
|
||||
std::string label = "##" + name;
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
|
||||
ImGui::TextWrapped("%s", name.c_str());
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::Checkbox(label.c_str(), &value);
|
||||
}
|
||||
|
||||
void AddSettingSliderInt(std::string name, int& value, int min, int max) {
|
||||
std::string label = "##" + name;
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextWrapped("%s", name.c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SliderInt(label.c_str(), &value, min, max);
|
||||
}
|
||||
|
||||
void AddSettingSliderFloat(std::string name, float& value, int min, int max, int precision) {
|
||||
std::string label = "##" + name;
|
||||
std::string precisionString = "%." + std::to_string(precision) + "f";
|
||||
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextWrapped("%s", name.c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::SliderFloat(label.c_str(), &value, min, max, precisionString.c_str());
|
||||
}
|
||||
|
||||
void AddSettingCombo(std::string name, int& value, std::vector<std::string> options) {
|
||||
std::string label = "##" + name;
|
||||
ImGui::TableNextRow();
|
||||
ImGui::TableNextColumn();
|
||||
ImGui::TextWrapped("%s", name.c_str());
|
||||
|
||||
ImGui::TableNextColumn();
|
||||
const char* combo_value = options[value].c_str();
|
||||
if (ImGui::BeginCombo(label.c_str(), combo_value)) {
|
||||
for (int i = 0; i < options.size(); i++) {
|
||||
const bool selected = (i == value);
|
||||
if (ImGui::Selectable(options[i].c_str(), selected))
|
||||
value = i;
|
||||
|
||||
// Set the initial focus when opening the combo
|
||||
if (selected)
|
||||
ImGui::SetItemDefaultFocus();
|
||||
}
|
||||
ImGui::EndCombo();
|
||||
}
|
||||
}
|
||||
|
||||
int GetComboIndex(std::string selection, std::vector<std::string> options) {
|
||||
for (int i = 0; i < options.size(); i++) {
|
||||
if (selection == options[i])
|
||||
return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LoadEmbeddedTexture(std::string resourcePath, SDL_Texture*& texture) {
|
||||
auto resource = cmrc::res::get_filesystem();
|
||||
auto file = resource.open(resourcePath);
|
||||
std::vector<char> texData = std::vector<char>(file.begin(), file.end());
|
||||
|
||||
BigPictureMode::LoadTextureData(texData, texture, renderer);
|
||||
}
|
||||
|
||||
void SetProfileIcons(std::vector<Game>& games) {
|
||||
ImGuiStyle& style = ImGui::GetStyle();
|
||||
const float maxAvailableWidth = ImGui::GetContentRegionAvail().x;
|
||||
const float itemSpacing = style.ItemSpacing.x; // already scaled
|
||||
const float padding = 10.0f * uiScale;
|
||||
float rowContentWidth = gameImageSize * uiScale + itemSpacing;
|
||||
|
||||
for (int i = 0; i < games.size(); i++) {
|
||||
ImGui::BeginGroup();
|
||||
std::string ButtonName = "Button" + std::to_string(i);
|
||||
const char* ButtonNameChar = ButtonName.c_str();
|
||||
|
||||
bool isNextItemFocused = (ImGui::GetID(ButtonNameChar) == ImGui::GetFocusID());
|
||||
bool popColor = false;
|
||||
if (isNextItemFocused) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Button,
|
||||
ImGui::GetStyle().Colors[ImGuiCol_ButtonHovered]);
|
||||
popColor = true;
|
||||
}
|
||||
|
||||
if (ImGui::ImageButton(ButtonNameChar, (ImTextureID)games[i].iconTexture,
|
||||
ImVec2(gameImageSize * uiScale, gameImageSize * uiScale))) {
|
||||
currentProfile = i == 0 ? "Global" : games[i].serial + " - " + games[i].title;
|
||||
LoadSettings(games[i].serial);
|
||||
}
|
||||
|
||||
if (popColor) {
|
||||
ImGui::PopStyleColor();
|
||||
}
|
||||
|
||||
// Scroll to item only when newly-focused
|
||||
if (ImGui::IsItemFocused() && !games[i].focusState) {
|
||||
ImGui::SetScrollHereY(0.5f);
|
||||
}
|
||||
|
||||
if (ImGui::IsWindowFocused()) {
|
||||
games[i].focusState = ImGui::IsItemFocused();
|
||||
}
|
||||
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPos().x + gameImageSize * uiScale);
|
||||
ImGui::TextWrapped("%s", games[i].title.c_str());
|
||||
ImGui::PopTextWrapPos();
|
||||
ImGui::EndGroup();
|
||||
|
||||
// Use same line if content fits horizontally, move to next line if not
|
||||
rowContentWidth += (gameImageSize * uiScale + itemSpacing * 2 + padding);
|
||||
if (rowContentWidth < maxAvailableWidth) {
|
||||
ImGui::SameLine(0.0f, padding);
|
||||
} else {
|
||||
ImGui::Dummy(ImVec2(0.0f, padding));
|
||||
rowContentWidth = gameImageSize * uiScale + itemSpacing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@ -1,45 +0,0 @@
|
||||
// SPDX-FileCopyrightText: Copyright 2025 shadPS4 Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <imgui.h>
|
||||
|
||||
#include "big_picture.h"
|
||||
|
||||
namespace BigPictureMode {
|
||||
|
||||
enum class SettingsCategory {
|
||||
Profiles,
|
||||
General,
|
||||
Graphics,
|
||||
Input,
|
||||
Trophy,
|
||||
Folders,
|
||||
Log,
|
||||
Experimental,
|
||||
};
|
||||
|
||||
void Init();
|
||||
void DeInit();
|
||||
|
||||
void SetProfileIcons(std::vector<Game>& games);
|
||||
void LoadEmbeddedTexture(std::string resourcePath, SDL_Texture*& texture);
|
||||
void AddCategory(std::string name, SDL_Texture* texture, SettingsCategory category);
|
||||
|
||||
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);
|
||||
void AddSettingSliderFloat(std::string name, float& value, int min, int max, int precision);
|
||||
void AddSettingCombo(std::string name, int& value, std::vector<std::string> options);
|
||||
int GetComboIndex(std::string selection, std::vector<std::string> options);
|
||||
|
||||
} // namespace BigPictureMode
|
||||
@ -25,6 +25,7 @@
|
||||
#include "core/devtools/layer.h"
|
||||
#include "core/emulator_settings.h"
|
||||
#include "core/emulator_state.h"
|
||||
#include "imgui/big_picture/settings_dialog_layer.h"
|
||||
#include "input/controller.h"
|
||||
#include "input/input_mouse.h"
|
||||
|
||||
@ -180,6 +181,7 @@ std::filesystem::path GetInputConfigFile(const std::string& game_id) {
|
||||
{"hotkey_quit", "lctrl, lshift, end"},
|
||||
{"hotkey_volume_up", "kpplus"},
|
||||
{"hotkey_volume_down", "kpminus"},
|
||||
{"hotkey_emulator_settings", "f3"},
|
||||
};
|
||||
std::string legacy_capture_binding;
|
||||
bool legacy_capture_binding_found = false;
|
||||
@ -799,6 +801,9 @@ void ControllerOutput::FinalizeUpdate(u8 gamepad_index) {
|
||||
case HOTKEY_QUIT:
|
||||
PushSDLEvent(SDL_EVENT_QUIT_DIALOG);
|
||||
break;
|
||||
case HOTKEY_OPEN_EMULATOR_SETTINGS:
|
||||
ImGuiEmuSettings::OpenInGameSettingsDialog();
|
||||
break;
|
||||
case KEY_TOGGLE:
|
||||
// noop
|
||||
break;
|
||||
|
||||
@ -64,6 +64,7 @@
|
||||
#define HOTKEY_ADD_VIRTUAL_USER 0xf000000c
|
||||
#define HOTKEY_REMOVE_VIRTUAL_USER 0xf000000d
|
||||
#define HOTKEY_SCREENSHOT_WITH_OVERLAYS 0xf000000e
|
||||
#define HOTKEY_OPEN_EMULATOR_SETTINGS 0xf000000f
|
||||
|
||||
#define SDL_UNMAPPED UINT32_MAX - 1
|
||||
|
||||
@ -165,6 +166,7 @@ const std::map<std::string, u32> string_to_hotkey_map = {
|
||||
{"hotkey_remove_virtual_user", HOTKEY_REMOVE_VIRTUAL_USER},
|
||||
{"hotkey_volume_up", HOTKEY_VOLUME_UP},
|
||||
{"hotkey_volume_down", HOTKEY_VOLUME_DOWN},
|
||||
{"hotkey_emulator_settings", HOTKEY_OPEN_EMULATOR_SETTINGS},
|
||||
};
|
||||
|
||||
const std::map<std::string, AxisMapping> string_to_axis_map = {
|
||||
@ -530,7 +532,7 @@ public:
|
||||
|
||||
class ControllerAllOutputs {
|
||||
public:
|
||||
static constexpr u64 output_count = 41;
|
||||
static constexpr u64 output_count = 42;
|
||||
std::array<ControllerOutput, output_count> data = {
|
||||
// Important: these have to be the first, or else they will update in the wrong order
|
||||
ControllerOutput(LEFTJOYSTICK_HALFMODE),
|
||||
@ -583,6 +585,7 @@ public:
|
||||
ControllerOutput(HOTKEY_REMOVE_VIRTUAL_USER),
|
||||
ControllerOutput(HOTKEY_VOLUME_UP),
|
||||
ControllerOutput(HOTKEY_VOLUME_DOWN),
|
||||
ControllerOutput(HOTKEY_OPEN_EMULATOR_SETTINGS),
|
||||
|
||||
ControllerOutput(SDL_GAMEPAD_BUTTON_INVALID, SDL_GAMEPAD_AXIS_INVALID),
|
||||
};
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
#include "core/file_sys/fs.h"
|
||||
#include "core/ipc/ipc.h"
|
||||
#include "emulator.h"
|
||||
#include "imgui/big_picture.h"
|
||||
#include "imgui/big_picture/big_picture.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user