diff options
Diffstat (limited to 'Source/Core/DolphinQt/Config/GameConfigWidget.cpp')
| -rw-r--r-- | Source/Core/DolphinQt/Config/GameConfigWidget.cpp | 440 |
1 files changed, 202 insertions, 238 deletions
diff --git a/Source/Core/DolphinQt/Config/GameConfigWidget.cpp b/Source/Core/DolphinQt/Config/GameConfigWidget.cpp index ce56a357be..30109f1b9c 100644 --- a/Source/Core/DolphinQt/Config/GameConfigWidget.cpp +++ b/Source/Core/DolphinQt/Config/GameConfigWidget.cpp @@ -3,37 +3,39 @@ #include "DolphinQt/Config/GameConfigWidget.h" -#include <QCheckBox> -#include <QComboBox> +#include <QFont> #include <QGroupBox> +#include <QHBoxLayout> +#include <QIcon> #include <QLabel> -#include <QPushButton> -#include <QSlider> -#include <QSpinBox> #include <QTabWidget> +#include <QTimer> +#include <QToolTip> #include <QVBoxLayout> #include "Common/CommonPaths.h" +#include "Common/Config/Config.h" +#include "Common/Config/Layer.h" #include "Common/FileUtil.h" +#include "Core/Config/GraphicsSettings.h" +#include "Core/Config/MainSettings.h" #include "Core/ConfigLoaders/GameConfigLoader.h" #include "Core/ConfigManager.h" - +#include "DolphinQt/Config/ConfigControls/ConfigBool.h" +#include "DolphinQt/Config/ConfigControls/ConfigChoice.h" +#include "DolphinQt/Config/ConfigControls/ConfigInteger.h" +#include "DolphinQt/Config/ConfigControls/ConfigRadio.h" #include "DolphinQt/Config/ConfigControls/ConfigSlider.h" #include "DolphinQt/Config/GameConfigEdit.h" +#include "DolphinQt/Config/Graphics/AdvancedWidget.h" +#include "DolphinQt/Config/Graphics/EnhancementsWidget.h" +#include "DolphinQt/Config/Graphics/GeneralWidget.h" +#include "DolphinQt/Config/Graphics/HacksWidget.h" +#include "DolphinQt/QtUtils/WrapInScrollArea.h" #include "UICommon/GameFile.h" -constexpr int DETERMINISM_NOT_SET_INDEX = 0; -constexpr int DETERMINISM_AUTO_INDEX = 1; -constexpr int DETERMINISM_NONE_INDEX = 2; -constexpr int DETERMINISM_FAKE_COMPLETION_INDEX = 3; - -constexpr const char* DETERMINISM_NOT_SET_STRING = ""; -constexpr const char* DETERMINISM_AUTO_STRING = "auto"; -constexpr const char* DETERMINISM_NONE_STRING = "none"; -constexpr const char* DETERMINISM_FAKE_COMPLETION_STRING = "fake-completion"; - static void PopulateTab(QTabWidget* tab, const std::string& path, std::string& game_id, u16 revision, bool read_only) { @@ -55,46 +57,62 @@ GameConfigWidget::GameConfigWidget(const UICommon::GameFile& game) : m_game(game m_gameini_local_path = QString::fromStdString(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini"); + m_layer = std::make_unique<Config::Layer>( + ConfigLoaders::GenerateLocalGameConfigLoader(m_game_id, m_game.GetRevision())); + m_global_layer = std::make_unique<Config::Layer>( + ConfigLoaders::GenerateGlobalGameConfigLoader(m_game_id, m_game.GetRevision())); + CreateWidgets(); - LoadSettings(); - ConnectWidgets(); + connect(&Settings::Instance(), &Settings::ConfigChanged, this, &GameConfigWidget::LoadSettings); PopulateTab(m_default_tab, File::GetSysDirectory() + GAMESETTINGS_DIR DIR_SEP, m_game_id, m_game.GetRevision(), true); PopulateTab(m_local_tab, File::GetUserPath(D_GAMESETTINGS_IDX), m_game_id, m_game.GetRevision(), false); - // Always give the user the opportunity to create a new INI - if (m_local_tab->count() == 0) + bool game_id_tab = false; + for (int i = 0; i < m_local_tab->count(); i++) { + if (m_local_tab->tabText(i).toStdString() == m_game_id + ".ini") + game_id_tab = true; + } + + if (game_id_tab == false) + { + // Create new local game ini tab if none exists. auto* edit = new GameConfigEdit( nullptr, QString::fromStdString(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini"), false); m_local_tab->addTab(edit, QString::fromStdString(m_game_id + ".ini")); } + + // Fails to change font if it's directly called at this time. Is there a better workaround? + QTimer::singleShot(100, this, [this]() { + SetItalics(); + Config::OnConfigChanged(); + }); } void GameConfigWidget::CreateWidgets() { - // General - m_refresh_config = new QPushButton(tr("Refresh")); - + Config::Layer* layer = m_layer.get(); // Core auto* core_box = new QGroupBox(tr("Core")); auto* core_layout = new QGridLayout; core_box->setLayout(core_layout); - m_enable_dual_core = new QCheckBox(tr("Enable Dual Core")); - m_enable_mmu = new QCheckBox(tr("Enable MMU")); - m_enable_fprf = new QCheckBox(tr("Enable FPRF")); - m_sync_gpu = new QCheckBox(tr("Synchronize GPU thread")); - m_emulate_disc_speed = new QCheckBox(tr("Emulate Disc Speed")); - m_use_dsp_hle = new QCheckBox(tr("DSP HLE (fast)")); - m_manual_texture_sampling = new QCheckBox(tr("Manual Texture Sampling")); - m_deterministic_dual_core = new QComboBox; + m_enable_dual_core = new ConfigBool(tr("Enable Dual Core"), Config::MAIN_CPU_THREAD, layer); + m_enable_mmu = new ConfigBool(tr("Enable MMU"), Config::MAIN_MMU, layer); + m_enable_fprf = new ConfigBool(tr("Enable FPRF"), Config::MAIN_FPRF, layer); + m_sync_gpu = new ConfigBool(tr("Synchronize GPU thread"), Config::MAIN_SYNC_GPU, layer); + m_emulate_disc_speed = + new ConfigBool(tr("Emulate Disc Speed"), Config::MAIN_FAST_DISC_SPEED, layer, true); + m_use_dsp_hle = new ConfigBool(tr("DSP HLE (fast)"), Config::MAIN_DSP_HLE, layer); - for (const auto& item : {tr("Not Set"), tr("auto"), tr("none"), tr("fake-completion")}) - m_deterministic_dual_core->addItem(item); + const std::vector<std::string> choice{tr("auto").toStdString(), tr("none").toStdString(), + tr("fake-completion").toStdString()}; + m_deterministic_dual_core = + new ConfigStringChoice(choice, Config::MAIN_GPU_DETERMINISM_MODE, layer); m_enable_mmu->setToolTip(tr( "Enables the Memory Management Unit, needed for some games. (ON = Compatible, OFF = Fast)")); @@ -114,24 +132,18 @@ void GameConfigWidget::CreateWidgets() core_layout->addWidget(m_sync_gpu, 3, 0); core_layout->addWidget(m_emulate_disc_speed, 4, 0); core_layout->addWidget(m_use_dsp_hle, 5, 0); - core_layout->addWidget(m_manual_texture_sampling, 6, 0); - core_layout->addWidget(new QLabel(tr("Deterministic dual core:")), 7, 0); - core_layout->addWidget(m_deterministic_dual_core, 7, 1); + core_layout->addWidget(new QLabel(tr("Deterministic dual core:")), 6, 0); + core_layout->addWidget(m_deterministic_dual_core, 6, 1); // Stereoscopy auto* stereoscopy_box = new QGroupBox(tr("Stereoscopy")); auto* stereoscopy_layout = new QGridLayout; stereoscopy_box->setLayout(stereoscopy_layout); - m_depth_slider = new QSlider(Qt::Horizontal); - - m_depth_slider->setMinimum(100); - m_depth_slider->setMaximum(200); - - m_convergence_spin = new QSpinBox; - m_convergence_spin->setMinimum(0); - m_convergence_spin->setMaximum(INT32_MAX); - m_use_monoscopic_shadows = new QCheckBox(tr("Monoscopic Shadows")); + m_depth_slider = new ConfigSlider(100, 200, Config::GFX_STEREO_DEPTH_PERCENTAGE, layer); + m_convergence_spin = new ConfigInteger(0, INT32_MAX, Config::GFX_STEREO_CONVERGENCE, layer); + m_use_monoscopic_shadows = + new ConfigBool(tr("Monoscopic Shadows"), Config::GFX_STEREO_EFB_MONO_DEPTH, layer); m_depth_slider->setToolTip( tr("This value is multiplied with the depth set in the graphics configuration.")); @@ -146,31 +158,15 @@ void GameConfigWidget::CreateWidgets() stereoscopy_layout->addWidget(m_convergence_spin, 1, 1); stereoscopy_layout->addWidget(m_use_monoscopic_shadows, 2, 0); - auto* settings_box = new QGroupBox(tr("Game-Specific Settings")); - auto* settings_layout = new QVBoxLayout; - settings_box->setLayout(settings_layout); - - settings_layout->addWidget( - new QLabel(tr("These settings override core Dolphin settings.\nUndetermined means the game " - "uses Dolphin's setting."))); - settings_layout->addWidget(core_box); - settings_layout->addWidget(stereoscopy_box); - - auto* general_layout = new QGridLayout; - - general_layout->addWidget(settings_box, 0, 0, 1, -1); - - general_layout->addWidget(m_refresh_config, 1, 0, 1, -1); - - for (QCheckBox* item : - {m_enable_dual_core, m_enable_mmu, m_enable_fprf, m_sync_gpu, m_emulate_disc_speed, - m_use_dsp_hle, m_manual_texture_sampling, m_use_monoscopic_shadows}) - item->setTristate(true); + auto* general_layout = new QVBoxLayout; + general_layout->addWidget(core_box); + general_layout->addWidget(stereoscopy_box); + general_layout->addStretch(); auto* general_widget = new QWidget; general_widget->setLayout(general_layout); - // Advanced + // Editor tab auto* advanced_layout = new QVBoxLayout; auto* default_group = new QGroupBox(tr("Default Config (Read Only)")); @@ -196,207 +192,175 @@ void GameConfigWidget::CreateWidgets() auto* layout = new QVBoxLayout; auto* tab_widget = new QTabWidget; - tab_widget->addTab(general_widget, tr("General")); - tab_widget->addTab(advanced_widget, tr("Editor")); - - layout->addWidget(tab_widget); - setLayout(layout); -} + // GFX settings tabs. Placed in a QWidget for consistent margins. + auto* gfx_tab_holder = new QWidget; + auto* gfx_layout = new QVBoxLayout; + gfx_tab_holder->setLayout(gfx_layout); + tab_widget->addTab(gfx_tab_holder, tr("Graphics")); + + auto* gfx_tabs = new QTabWidget; + + gfx_tabs->addTab(GetWrappedWidget(new GeneralWidget(this, m_layer.get()), this, 125, 100), + tr("General")); + gfx_tabs->addTab(GetWrappedWidget(new EnhancementsWidget(this, m_layer.get()), this, 125, 100), + tr("Enhancements")); + gfx_tabs->addTab(GetWrappedWidget(new HacksWidget(this, m_layer.get()), this, 125, 100), + tr("Hacks")); + gfx_tabs->addTab(GetWrappedWidget(new AdvancedWidget(this, m_layer.get()), this, 125, 100), + tr("Advanced")); + const int editor_index = tab_widget->addTab(advanced_widget, tr("Editor")); + gfx_layout->addWidget(gfx_tabs); + + connect(tab_widget, &QTabWidget::currentChanged, this, [this, editor_index](int index) { + // Update the ini editor after editing other tabs. + if (index == editor_index) + { + // Layer only auto-saves when it is destroyed. + m_layer->Save(); + + // There can be multiple ini loaded for a game, only replace the one related to the game + // ini being edited. + for (int i = 0; i < m_local_tab->count(); i++) + { + if (m_local_tab->tabText(i).toStdString() == m_game_id + ".ini") + { + m_local_tab->removeTab(i); + + auto* edit = new GameConfigEdit( + nullptr, + QString::fromStdString(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini"), + false); -void GameConfigWidget::ConnectWidgets() -{ - // Buttons - connect(m_refresh_config, &QPushButton::clicked, this, &GameConfigWidget::LoadSettings); - - for (QCheckBox* box : - {m_enable_dual_core, m_enable_mmu, m_enable_fprf, m_sync_gpu, m_emulate_disc_speed, - m_use_dsp_hle, m_manual_texture_sampling, m_use_monoscopic_shadows}) - connect(box, &QCheckBox::stateChanged, this, &GameConfigWidget::SaveSettings); - - connect(m_deterministic_dual_core, &QComboBox::currentIndexChanged, this, - &GameConfigWidget::SaveSettings); - connect(m_depth_slider, &QSlider::valueChanged, this, &GameConfigWidget::SaveSettings); - connect(m_convergence_spin, &QSpinBox::valueChanged, this, &GameConfigWidget::SaveSettings); -} + m_local_tab->insertTab(i, edit, QString::fromStdString(m_game_id + ".ini")); + break; + } + } + } -void GameConfigWidget::LoadCheckBox(QCheckBox* checkbox, const std::string& section, - const std::string& key, bool reverse) -{ - bool checked; - if (m_gameini_local.GetOrCreateSection(section)->Get(key, &checked)) - return checkbox->setCheckState(checked ^ reverse ? Qt::Checked : Qt::Unchecked); + // Update other tabs after using ini editor. + if (m_prev_tab_index == editor_index) + { + // Load won't clear deleted keys, so everything is wiped before loading. + m_layer->DeleteAllKeys(); + m_layer->Load(); + Config::OnConfigChanged(); + } - if (m_gameini_default.GetOrCreateSection(section)->Get(key, &checked)) - return checkbox->setCheckState(checked ^ reverse ? Qt::Checked : Qt::Unchecked); - checkbox->setCheckState(Qt::PartiallyChecked); + m_prev_tab_index = index; + }); + + const QString help_msg = tr( + "Italics mark default game settings, bold marks user settings.\nRight-click to remove user " + "settings.\nGraphics tabs don't display the value of a default game setting.\nAnti-Aliasing " + "settings are disabled when the global graphics backend doesn't " + "match the game setting."); + + auto help_icon = style()->standardIcon(QStyle::SP_MessageBoxQuestion); + auto* help_label = new QLabel(tr("These settings override core Dolphin settings.")); + help_label->setToolTip(help_msg); + auto help_label_icon = new QLabel(); + help_label_icon->setPixmap(help_icon.pixmap(12, 12)); + help_label_icon->setToolTip(help_msg); + auto* help_layout = new QHBoxLayout(); + help_layout->addWidget(help_label); + help_layout->addWidget(help_label_icon); + help_layout->addStretch(); + + layout->addLayout(help_layout); + layout->addWidget(tab_widget); + setLayout(layout); } -void GameConfigWidget::SaveCheckBox(QCheckBox* checkbox, const std::string& section, - const std::string& key, bool reverse) +GameConfigWidget::~GameConfigWidget() { - // Delete any existing entries from the local gameini if checkbox is undetermined. - // Otherwise, write the current value to the local gameini if the value differs from the default - // gameini values. - // Delete any existing entry from the local gameini if the value does not differ from the default - // gameini value. + // Destructor saves the layer to file. + m_layer.reset(); - if (checkbox->checkState() == Qt::PartiallyChecked) + // If a game is running and the game properties window is closed, update local game layer with + // any new changes. Not sure if doing it more frequently is safe. + auto local_layer = Config::GetLayer(Config::LayerType::LocalGame); + if (local_layer && SConfig::GetInstance().GetGameID() == m_game_id) { - m_gameini_local.DeleteKey(section, key); - return; + local_layer->DeleteAllKeys(); + local_layer->Load(); + Config::OnConfigChanged(); } - bool checked = (checkbox->checkState() == Qt::Checked) ^ reverse; - - if (m_gameini_default.Exists(section, key)) - { - bool default_value; - m_gameini_default.GetOrCreateSection(section)->Get(key, &default_value); - - if (default_value != checked) - m_gameini_local.GetOrCreateSection(section)->Set(key, checked); - else - m_gameini_local.DeleteKey(section, key); - - return; - } - - m_gameini_local.GetOrCreateSection(section)->Set(key, checked); + // Delete empty configs + if (File::GetSize(m_gameini_local_path.toStdString()) == 0) + File::Delete(m_gameini_local_path.toStdString()); } void GameConfigWidget::LoadSettings() { - // Reload config - m_gameini_local = SConfig::LoadLocalGameIni(m_game_id, m_game.GetRevision()); - m_gameini_default = SConfig::LoadDefaultGameIni(m_game_id, m_game.GetRevision()); - - // Load game-specific settings - - // Core - LoadCheckBox(m_enable_dual_core, "Core", "CPUThread"); - LoadCheckBox(m_enable_mmu, "Core", "MMU"); - LoadCheckBox(m_enable_fprf, "Core", "FPRF"); - LoadCheckBox(m_sync_gpu, "Core", "SyncGPU"); - LoadCheckBox(m_emulate_disc_speed, "Core", "FastDiscSpeed", true); - LoadCheckBox(m_use_dsp_hle, "Core", "DSPHLE"); - LoadCheckBox(m_manual_texture_sampling, "Video_Hacks", "FastTextureSampling", true); + // Load globals + auto update_bool = [this](auto config, bool reverse = false) { + const Config::Location& setting = config->GetLocation(); - std::string determinism_mode; + // Don't overwrite local with global + if (m_layer->Exists(setting) || !m_global_layer->Exists(setting)) + return; - int determinism_index = DETERMINISM_NOT_SET_INDEX; + std::optional<bool> value = m_global_layer->Get<bool>(config->GetLocation()); - m_gameini_default.GetIfExists("Core", "GPUDeterminismMode", &determinism_mode); - m_gameini_local.GetIfExists("Core", "GPUDeterminismMode", &determinism_mode); - - if (determinism_mode == DETERMINISM_AUTO_STRING) - { - determinism_index = DETERMINISM_AUTO_INDEX; - } - else if (determinism_mode == DETERMINISM_NONE_STRING) - { - determinism_index = DETERMINISM_NONE_INDEX; - } - else if (determinism_mode == DETERMINISM_FAKE_COMPLETION_STRING) - { - determinism_index = DETERMINISM_FAKE_COMPLETION_INDEX; - } - - m_deterministic_dual_core->setCurrentIndex(determinism_index); - - // Stereoscopy - int depth_percentage = 100; - - m_gameini_default.GetIfExists("Video_Stereoscopy", "StereoDepthPercentage", &depth_percentage); - m_gameini_local.GetIfExists("Video_Stereoscopy", "StereoDepthPercentage", &depth_percentage); - - m_depth_slider->setValue(depth_percentage); - - int convergence = 0; - - m_gameini_default.GetIfExists("Video_Stereoscopy", "StereoConvergence", &convergence); - m_gameini_local.GetIfExists("Video_Stereoscopy", "StereoConvergence", &convergence); - - m_convergence_spin->setValue(convergence); - - LoadCheckBox(m_use_monoscopic_shadows, "Video_Stereoscopy", "StereoEFBMonoDepth"); -} - -void GameConfigWidget::SaveSettings() -{ - // Core - SaveCheckBox(m_enable_dual_core, "Core", "CPUThread"); - SaveCheckBox(m_enable_mmu, "Core", "MMU"); - SaveCheckBox(m_enable_fprf, "Core", "FPRF"); - SaveCheckBox(m_sync_gpu, "Core", "SyncGPU"); - SaveCheckBox(m_emulate_disc_speed, "Core", "FastDiscSpeed", true); - SaveCheckBox(m_use_dsp_hle, "Core", "DSPHLE"); - SaveCheckBox(m_manual_texture_sampling, "Video_Hacks", "FastTextureSampling", true); - - int determinism_num = m_deterministic_dual_core->currentIndex(); - - std::string determinism_mode = DETERMINISM_NOT_SET_STRING; - - switch (determinism_num) - { - case DETERMINISM_AUTO_INDEX: - determinism_mode = DETERMINISM_AUTO_STRING; - break; - case DETERMINISM_NONE_INDEX: - determinism_mode = DETERMINISM_NONE_STRING; - break; - case DETERMINISM_FAKE_COMPLETION_INDEX: - determinism_mode = DETERMINISM_FAKE_COMPLETION_STRING; - break; - } - - if (determinism_mode != DETERMINISM_NOT_SET_STRING) - { - std::string default_mode = DETERMINISM_NOT_SET_STRING; - if (!(m_gameini_default.GetIfExists("Core", "GPUDeterminismMode", &default_mode) && - default_mode == determinism_mode)) + if (value.has_value()) { - m_gameini_local.GetOrCreateSection("Core")->Set("GPUDeterminismMode", determinism_mode); + const QSignalBlocker blocker(config); + config->setChecked(value.value() ^ reverse); } - } - else - { - m_gameini_local.DeleteKey("Core", "GPUDeterminismMode"); - } + }; - // Stereoscopy - int depth_percentage = m_depth_slider->value(); + auto update_int = [this](auto config) { + const Config::Location& setting = config->GetLocation(); - if (depth_percentage != 100) - { - int default_value = 0; - if (!(m_gameini_default.GetIfExists("Video_Stereoscopy", "StereoDepthPercentage", - &default_value) && - default_value == depth_percentage)) + if (m_layer->Exists(setting) || !m_global_layer->Exists(setting)) + return; + + std::optional<int> value = m_global_layer->Get<int>(setting); + + if (value.has_value()) { - m_gameini_local.GetOrCreateSection("Video_Stereoscopy") - ->Set("StereoDepthPercentage", depth_percentage); + const QSignalBlocker blocker(config); + config->setValue(value.value()); } - } + }; - int convergence = m_convergence_spin->value(); - if (convergence != 0) + for (ConfigBool* config : {m_enable_dual_core, m_enable_mmu, m_enable_fprf, m_sync_gpu, + m_use_dsp_hle, m_use_monoscopic_shadows}) { - int default_value = 0; - if (!(m_gameini_default.GetIfExists("Video_Stereoscopy", "StereoConvergence", &default_value) && - default_value == convergence)) - { - m_gameini_local.GetOrCreateSection("Video_Stereoscopy") - ->Set("StereoConvergence", convergence); - } + update_bool(config); } - SaveCheckBox(m_use_monoscopic_shadows, "Video_Stereoscopy", "StereoEFBMonoDepth"); + update_bool(m_emulate_disc_speed, true); - bool success = m_gameini_local.Save(m_gameini_local_path.toStdString()); + update_int(m_depth_slider); + update_int(m_convergence_spin); +} - // If the resulting file is empty, delete it. Kind of a hack, but meh. - if (success && File::GetSize(m_gameini_local_path.toStdString()) == 0) - File::Delete(m_gameini_local_path.toStdString()); +void GameConfigWidget::SetItalics() +{ + // Mark system game settings with italics. Called once because it should never change. + auto italics = [this](auto config) { + if (!m_global_layer->Exists(config->GetLocation())) + return; + + QFont ifont = config->font(); + ifont.setItalic(true); + config->setFont(ifont); + }; + + for (auto* config : findChildren<ConfigBool*>()) + italics(config); + for (auto* config : findChildren<ConfigSlider*>()) + italics(config); + for (auto* config : findChildren<ConfigInteger*>()) + italics(config); + for (auto* config : findChildren<ConfigRadioInt*>()) + italics(config); + for (auto* config : findChildren<ConfigChoice*>()) + italics(config); + for (auto* config : findChildren<ConfigStringChoice*>()) + italics(config); } |
