From 08df9a66e03acb614dca6d1c118818f84f1cbcfd Mon Sep 17 00:00:00 2001 From: TryTwo Date: Tue, 17 Sep 2024 23:29:13 -0700 Subject: [PATCH 1/4] DolphinQt: Refactor, add ConfigControl class This reduces code duplication in the different ConfigControls. This is helpful for the next commit, which will modify the now deduplicated code. --- Source/Core/DolphinQt/CMakeLists.txt | 1 + .../Config/ConfigControls/ConfigBool.cpp | 31 +++----- .../Config/ConfigControls/ConfigBool.h | 6 +- .../Config/ConfigControls/ConfigChoice.cpp | 62 ++++++---------- .../Config/ConfigControls/ConfigChoice.h | 20 ++++-- .../Config/ConfigControls/ConfigControl.h | 72 +++++++++++++++++++ .../ConfigControls/ConfigFloatSlider.cpp | 29 +++----- .../Config/ConfigControls/ConfigFloatSlider.h | 6 +- .../Config/ConfigControls/ConfigInteger.cpp | 26 +++---- .../Config/ConfigControls/ConfigInteger.h | 6 +- .../Config/ConfigControls/ConfigRadio.cpp | 28 +++----- .../Config/ConfigControls/ConfigRadio.h | 14 +++- .../Config/ConfigControls/ConfigSlider.cpp | 28 +++----- .../Config/ConfigControls/ConfigSlider.h | 6 +- Source/Core/DolphinQt/DolphinQt.vcxproj | 1 + 15 files changed, 189 insertions(+), 147 deletions(-) create mode 100644 Source/Core/DolphinQt/Config/ConfigControls/ConfigControl.h diff --git a/Source/Core/DolphinQt/CMakeLists.txt b/Source/Core/DolphinQt/CMakeLists.txt index bd766e0453..1da798b366 100644 --- a/Source/Core/DolphinQt/CMakeLists.txt +++ b/Source/Core/DolphinQt/CMakeLists.txt @@ -52,6 +52,7 @@ add_executable(dolphin-emu Config/ConfigControls/ConfigBool.h Config/ConfigControls/ConfigChoice.cpp Config/ConfigControls/ConfigChoice.h + Config/ConfigControls/ConfigControl.h Config/ConfigControls/ConfigInteger.cpp Config/ConfigControls/ConfigInteger.h Config/ConfigControls/ConfigRadio.cpp diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.cpp index 89ab9c15be..0f99284297 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.cpp @@ -3,31 +3,22 @@ #include "DolphinQt/Config/ConfigControls/ConfigBool.h" -#include -#include -#include - -#include "Common/Config/Config.h" - -#include "DolphinQt/Settings.h" - ConfigBool::ConfigBool(const QString& label, const Config::Info& setting, bool reverse) - : ToolTipCheckBox(label), m_setting(setting), m_reverse(reverse) + : ConfigControl(label, setting.GetLocation()), m_setting(setting), m_reverse(reverse) { + setChecked(ReadValue(setting) ^ reverse); + connect(this, &QCheckBox::toggled, this, &ConfigBool::Update); - setChecked(Config::Get(m_setting) ^ reverse); - - connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] { - QFont bf = font(); - bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base); - setFont(bf); - - const QSignalBlocker blocker(this); - setChecked(Config::Get(m_setting) ^ m_reverse); - }); } void ConfigBool::Update() { - Config::SetBaseOrCurrent(m_setting, static_cast(isChecked() ^ m_reverse)); + const bool value = static_cast(isChecked() ^ m_reverse); + + SaveValue(m_setting, value); +} + +void ConfigBool::OnConfigChanged() +{ + setChecked(ReadValue(m_setting) ^ m_reverse); } diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.h index 21f5f62352..467220e488 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.h @@ -3,6 +3,7 @@ #pragma once +#include "DolphinQt/Config/ConfigControls/ConfigControl.h" #include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h" namespace Config @@ -11,12 +12,15 @@ template class Info; } -class ConfigBool : public ToolTipCheckBox +class ConfigBool final : public ConfigControl { Q_OBJECT public: ConfigBool(const QString& label, const Config::Info& setting, bool reverse = false); +protected: + void OnConfigChanged() override; + private: void Update(); diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.cpp index ad65f0c96b..a26b87b46e 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.cpp @@ -5,85 +5,65 @@ #include -#include "Common/Config/Config.h" - -#include "DolphinQt/Settings.h" - ConfigChoice::ConfigChoice(const QStringList& options, const Config::Info& setting) - : m_setting(setting) + : ConfigControl(setting.GetLocation()), m_setting(setting) { addItems(options); + setCurrentIndex(ReadValue(setting)); + connect(this, &QComboBox::currentIndexChanged, this, &ConfigChoice::Update); - setCurrentIndex(Config::Get(m_setting)); - - connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] { - QFont bf = font(); - bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base); - setFont(bf); - - const QSignalBlocker blocker(this); - setCurrentIndex(Config::Get(m_setting)); - }); } void ConfigChoice::Update(int choice) { - Config::SetBaseOrCurrent(m_setting, choice); + SaveValue(m_setting, choice); +} + +void ConfigChoice::OnConfigChanged() +{ + setCurrentIndex(ReadValue(m_setting)); } ConfigStringChoice::ConfigStringChoice(const std::vector& options, const Config::Info& setting) - : m_setting(setting), m_text_is_data(true) + : ConfigControl(setting.GetLocation()), m_setting(setting), m_text_is_data(true) { for (const auto& op : options) addItem(QString::fromStdString(op)); - Connect(); Load(); + connect(this, &QComboBox::currentIndexChanged, this, &ConfigStringChoice::Update); } ConfigStringChoice::ConfigStringChoice(const std::vector>& options, const Config::Info& setting) - : m_setting(setting), m_text_is_data(false) + : ConfigControl(setting.GetLocation()), m_setting(setting), m_text_is_data(false) { for (const auto& [option_text, option_data] : options) addItem(option_text, option_data); - Connect(); - Load(); -} - -void ConfigStringChoice::Connect() -{ - const auto on_config_changed = [this]() { - QFont bf = font(); - bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base); - setFont(bf); - - Load(); - }; - - connect(&Settings::Instance(), &Settings::ConfigChanged, this, on_config_changed); connect(this, &QComboBox::currentIndexChanged, this, &ConfigStringChoice::Update); + Load(); } void ConfigStringChoice::Update(int index) { if (m_text_is_data) - { - Config::SetBaseOrCurrent(m_setting, itemText(index).toStdString()); - } + SaveValue(m_setting, itemText(index).toStdString()); else - { - Config::SetBaseOrCurrent(m_setting, itemData(index).toString().toStdString()); - } + SaveValue(m_setting, itemData(index).toString().toStdString()); } void ConfigStringChoice::Load() { - const QString setting_value = QString::fromStdString(Config::Get(m_setting)); + const QString setting_value = QString::fromStdString(ReadValue(m_setting)); const int index = m_text_is_data ? findText(setting_value) : findData(setting_value); const QSignalBlocker blocker(this); setCurrentIndex(index); } + +void ConfigStringChoice::OnConfigChanged() +{ + Load(); +} diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.h index 0082605604..930e7aef10 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.h @@ -7,23 +7,31 @@ #include #include +#include "DolphinQt/Config/ConfigControls/ConfigControl.h" #include "DolphinQt/Config/ToolTipControls/ToolTipComboBox.h" -#include "Common/Config/Config.h" +namespace Config +{ +template +class Info; +} -class ConfigChoice : public ToolTipComboBox +class ConfigChoice final : public ConfigControl { Q_OBJECT public: ConfigChoice(const QStringList& options, const Config::Info& setting); +protected: + void OnConfigChanged() override; + private: void Update(int choice); Config::Info m_setting; }; -class ConfigStringChoice : public ToolTipComboBox +class ConfigStringChoice final : public ConfigControl { Q_OBJECT public: @@ -32,11 +40,13 @@ public: ConfigStringChoice(const std::vector>& options, const Config::Info& setting); +protected: + void OnConfigChanged() override; + private: - void Connect(); void Update(int index); void Load(); - Config::Info m_setting; + const Config::Info& m_setting; bool m_text_is_data = false; }; diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigControl.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigControl.h new file mode 100644 index 0000000000..4100781cfe --- /dev/null +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigControl.h @@ -0,0 +1,72 @@ +// Copyright 2024 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#include "Common/Config/Enums.h" +#include "DolphinQt/Settings.h" + +namespace Config +{ +template +class Info; +struct Location; +} // namespace Config + +template +class ConfigControl : public Derived +{ +public: + ConfigControl(const Config::Location& location) : m_location(location) { ConnectConfig(); } + ConfigControl(const QString& label, const Config::Location& location) + : Derived(label), m_location(location) + { + ConnectConfig(); + } + ConfigControl(const Qt::Orientation& orient, const Config::Location& location) + : Derived(orient), m_location(location) + { + ConnectConfig(); + } + + const Config::Location GetLocation() const { return m_location; } + +protected: + void ConnectConfig() + { + Derived::connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] { + QFont bf = Derived::font(); + bf.setBold(IsConfigLocal()); + Derived::setFont(bf); + + const QSignalBlocker blocker(this); + OnConfigChanged(); + }); + } + + template + void SaveValue(const Config::Info& setting, const T& value) + { + Config::SetBaseOrCurrent(setting, value); + } + + template + const T ReadValue(const Config::Info& setting) const + { + return Config::Get(setting); + } + + virtual void OnConfigChanged() {}; + +private: + bool IsConfigLocal() const + { + return Config::GetActiveLayerForConfig(m_location) != Config::LayerType::Base; + } + + const Config::Location m_location; +}; diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.cpp index 3f7bed01c8..b8aa2c82fc 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.cpp @@ -3,20 +3,15 @@ #include "DolphinQt/Config/ConfigControls/ConfigFloatSlider.h" -#include - -#include "Common/Config/Config.h" - -#include "DolphinQt/Settings.h" - ConfigFloatSlider::ConfigFloatSlider(float minimum, float maximum, const Config::Info& setting, float step) - : ToolTipSlider(Qt::Horizontal), m_minimum(minimum), m_step(step), m_setting(setting) + : ConfigControl(Qt::Horizontal, setting.GetLocation()), m_minimum(minimum), m_step(step), + m_setting(setting) { const float range = maximum - minimum; const int steps = std::round(range / step); const int interval = std::round(range / steps); - const int current_value = std::round((Config::Get(m_setting) - minimum) / step); + const int current_value = std::round((ReadValue(setting) - minimum) / step); setMinimum(0); setMaximum(steps); @@ -24,25 +19,21 @@ ConfigFloatSlider::ConfigFloatSlider(float minimum, float maximum, setValue(current_value); connect(this, &ConfigFloatSlider::valueChanged, this, &ConfigFloatSlider::Update); - - connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] { - QFont bf = font(); - bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base); - setFont(bf); - - const QSignalBlocker blocker(this); - const int value = std::round((Config::Get(m_setting) - m_minimum) / m_step); - setValue(value); - }); } void ConfigFloatSlider::Update(int value) { const float current_value = (m_step * value) + m_minimum; - Config::SetBaseOrCurrent(m_setting, current_value); + + SaveValue(m_setting, current_value); } float ConfigFloatSlider::GetValue() const { return (m_step * value()) + m_minimum; } + +void ConfigFloatSlider::OnConfigChanged() +{ + setValue(std::round((ReadValue(m_setting) - m_minimum) / m_step)); +} diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.h index e20739cfe4..3c38e34095 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.h @@ -3,6 +3,7 @@ #pragma once +#include "DolphinQt/Config/ConfigControls/ConfigControl.h" #include "DolphinQt/Config/ToolTipControls/ToolTipSlider.h" namespace Config @@ -13,7 +14,7 @@ class Info; // Automatically converts an int slider into a float one. // Do not read the int values or ranges directly from it. -class ConfigFloatSlider : public ToolTipSlider +class ConfigFloatSlider final : public ConfigControl { Q_OBJECT public: @@ -23,6 +24,9 @@ public: // Returns the adjusted float value float GetValue() const; +protected: + void OnConfigChanged() override; + private: float m_minimum; float m_step; diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.cpp index c8c3e5074a..ba85e64617 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.cpp @@ -3,33 +3,23 @@ #include "DolphinQt/Config/ConfigControls/ConfigInteger.h" -#include - -#include "Common/Config/Config.h" - -#include "DolphinQt/Settings.h" - ConfigInteger::ConfigInteger(int minimum, int maximum, const Config::Info& setting, int step) - : ToolTipSpinBox(), m_setting(setting) + : ConfigControl(setting.GetLocation()), m_setting(setting) { setMinimum(minimum); setMaximum(maximum); setSingleStep(step); - - setValue(Config::Get(setting)); + setValue(ReadValue(setting)); connect(this, &ConfigInteger::valueChanged, this, &ConfigInteger::Update); - connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] { - QFont bf = font(); - bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base); - setFont(bf); - - const QSignalBlocker blocker(this); - setValue(Config::Get(m_setting)); - }); } void ConfigInteger::Update(int value) { - Config::SetBaseOrCurrent(m_setting, value); + SaveValue(m_setting, value); +} + +void ConfigInteger::OnConfigChanged() +{ + setValue(ReadValue(m_setting)); } diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.h index 35b25986b7..21100c9af1 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.h @@ -3,6 +3,7 @@ #pragma once +#include "DolphinQt/Config/ConfigControls/ConfigControl.h" #include "DolphinQt/Config/ToolTipControls/ToolTipSpinBox.h" namespace Config @@ -11,13 +12,16 @@ template class Info; } -class ConfigInteger : public ToolTipSpinBox +class ConfigInteger final : public ConfigControl { Q_OBJECT public: ConfigInteger(int minimum, int maximum, const Config::Info& setting, int step = 1); void Update(int value); +protected: + void OnConfigChanged() override; + private: const Config::Info& m_setting; }; diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.cpp index 11a049d910..baaf7e6f48 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.cpp @@ -3,33 +3,20 @@ #include "DolphinQt/Config/ConfigControls/ConfigRadio.h" -#include - -#include "Common/Config/Config.h" - -#include "DolphinQt/Settings.h" - ConfigRadioInt::ConfigRadioInt(const QString& label, const Config::Info& setting, int value) - : ToolTipRadioButton(label), m_setting(setting), m_value(value) + : ConfigControl(label, setting.GetLocation()), m_setting(setting), m_value(value) { - setChecked(Config::Get(m_setting) == m_value); + setChecked(ReadValue(setting) == value); + connect(this, &QRadioButton::toggled, this, &ConfigRadioInt::Update); - - connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] { - QFont bf = font(); - bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base); - setFont(bf); - - const QSignalBlocker blocker(this); - setChecked(Config::Get(m_setting) == m_value); - }); } void ConfigRadioInt::Update() { if (isChecked()) { - Config::SetBaseOrCurrent(m_setting, m_value); + SaveValue(m_setting, m_value); + emit OnSelected(m_value); } else @@ -37,3 +24,8 @@ void ConfigRadioInt::Update() emit OnDeselected(m_value); } } + +void ConfigRadioInt::OnConfigChanged() +{ + setChecked(ReadValue(m_setting) == m_value); +} diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.h index 147c6391f9..8b28f2c106 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.h @@ -3,11 +3,16 @@ #pragma once +#include "DolphinQt/Config/ConfigControls/ConfigControl.h" #include "DolphinQt/Config/ToolTipControls/ToolTipRadioButton.h" -#include "Common/Config/Config.h" +namespace Config +{ +template +class Info; +} -class ConfigRadioInt : public ToolTipRadioButton +class ConfigRadioInt final : public ConfigControl { Q_OBJECT public: @@ -19,9 +24,12 @@ signals: void OnSelected(int new_value); void OnDeselected(int old_value); +protected: + void OnConfigChanged() override; + private: void Update(); - Config::Info m_setting; + const Config::Info& m_setting; int m_value; }; diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.cpp index 9732909edc..a52b42d4f4 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.cpp @@ -3,34 +3,24 @@ #include "DolphinQt/Config/ConfigControls/ConfigSlider.h" -#include - -#include "Common/Config/Config.h" - -#include "DolphinQt/Settings.h" - ConfigSlider::ConfigSlider(int minimum, int maximum, const Config::Info& setting, int tick) - : ToolTipSlider(Qt::Horizontal), m_setting(setting) + : ConfigControl(Qt::Horizontal, setting.GetLocation()), m_setting(setting) + { setMinimum(minimum); setMaximum(maximum); setTickInterval(tick); - - setValue(Config::Get(setting)); + setValue(ReadValue(setting)); connect(this, &ConfigSlider::valueChanged, this, &ConfigSlider::Update); - - connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] { - QFont bf = font(); - bf.setBold(Config::GetActiveLayerForConfig(m_setting) != Config::LayerType::Base); - setFont(bf); - - const QSignalBlocker blocker(this); - setValue(Config::Get(m_setting)); - }); } void ConfigSlider::Update(int value) { - Config::SetBaseOrCurrent(m_setting, value); + SaveValue(m_setting, value); +} + +void ConfigSlider::OnConfigChanged() +{ + setValue(ReadValue(m_setting)); } diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.h index a14d06291e..ebb02391e8 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.h @@ -3,6 +3,7 @@ #pragma once +#include "DolphinQt/Config/ConfigControls/ConfigControl.h" #include "DolphinQt/Config/ToolTipControls/ToolTipSlider.h" namespace Config @@ -11,13 +12,16 @@ template class Info; } -class ConfigSlider : public ToolTipSlider +class ConfigSlider final : public ConfigControl { Q_OBJECT public: ConfigSlider(int minimum, int maximum, const Config::Info& setting, int tick = 0); void Update(int value); +protected: + void OnConfigChanged() override; + private: const Config::Info& m_setting; }; diff --git a/Source/Core/DolphinQt/DolphinQt.vcxproj b/Source/Core/DolphinQt/DolphinQt.vcxproj index f4bc2e0030..edcdc2516a 100644 --- a/Source/Core/DolphinQt/DolphinQt.vcxproj +++ b/Source/Core/DolphinQt/DolphinQt.vcxproj @@ -241,6 +241,7 @@ --> + From 84a937ae651aa5296535e8feffe47aa1f2b219ab Mon Sep 17 00:00:00 2001 From: TryTwo Date: Tue, 17 Sep 2024 23:29:13 -0700 Subject: [PATCH 2/4] Add GFX property tabs to game properties window, allowing them to be set to the user game ini. Additionally, refactor ConfigWidgets to reduce duplication. Refactor GameConfigWidget to use config system. Creates a layer outside the game config layer system and passes it to the created gfx widows, so as to not interfere with the global config system. Supports multiple game properties being open at once. Supports editing while a game is playing, but the options only save and update the active game when the window is closed. Right-clicking will remove a property from the game ini. --- .../Config/ConfigControls/ConfigBool.cpp | 8 +- .../Config/ConfigControls/ConfigBool.h | 2 + .../Config/ConfigControls/ConfigChoice.cpp | 20 +- .../Config/ConfigControls/ConfigChoice.h | 9 +- .../Config/ConfigControls/ConfigControl.h | 47 +- .../ConfigControls/ConfigFloatSlider.cpp | 5 +- .../Config/ConfigControls/ConfigFloatSlider.h | 3 +- .../Config/ConfigControls/ConfigInteger.cpp | 8 +- .../Config/ConfigControls/ConfigInteger.h | 3 + .../Config/ConfigControls/ConfigRadio.cpp | 5 +- .../Config/ConfigControls/ConfigRadio.h | 3 +- .../Config/ConfigControls/ConfigSlider.cpp | 8 +- .../Config/ConfigControls/ConfigSlider.h | 3 + .../DolphinQt/Config/GameConfigWidget.cpp | 442 ++++++++---------- .../Core/DolphinQt/Config/GameConfigWidget.h | 58 +-- .../Config/Graphics/AdvancedWidget.cpp | 173 ++++--- .../Config/Graphics/AdvancedWidget.h | 20 +- .../Config/Graphics/EnhancementsWidget.cpp | 48 +- .../Config/Graphics/EnhancementsWidget.h | 8 + .../Config/Graphics/GeneralWidget.cpp | 81 ++-- .../DolphinQt/Config/Graphics/GeneralWidget.h | 15 +- .../DolphinQt/Config/Graphics/HacksWidget.cpp | 53 ++- .../DolphinQt/Config/Graphics/HacksWidget.h | 9 + Source/Core/DolphinQt/GameList/GameList.cpp | 6 +- 24 files changed, 573 insertions(+), 464 deletions(-) diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.cpp index 0f99284297..4fad525e6f 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.cpp @@ -4,7 +4,13 @@ #include "DolphinQt/Config/ConfigControls/ConfigBool.h" ConfigBool::ConfigBool(const QString& label, const Config::Info& setting, bool reverse) - : ConfigControl(label, setting.GetLocation()), m_setting(setting), m_reverse(reverse) + : ConfigBool(label, setting, nullptr, reverse) +{ +} + +ConfigBool::ConfigBool(const QString& label, const Config::Info& setting, + Config::Layer* layer, bool reverse) + : ConfigControl(label, setting.GetLocation(), layer), m_setting(setting), m_reverse(reverse) { setChecked(ReadValue(setting) ^ reverse); diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.h index 467220e488..304f1a9b1f 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.h @@ -17,6 +17,8 @@ class ConfigBool final : public ConfigControl Q_OBJECT public: ConfigBool(const QString& label, const Config::Info& setting, bool reverse = false); + ConfigBool(const QString& label, const Config::Info& setting, Config::Layer* layer, + bool reverse = false); protected: void OnConfigChanged() override; diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.cpp index a26b87b46e..c2652563d1 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.cpp @@ -5,8 +5,9 @@ #include -ConfigChoice::ConfigChoice(const QStringList& options, const Config::Info& setting) - : ConfigControl(setting.GetLocation()), m_setting(setting) +ConfigChoice::ConfigChoice(const QStringList& options, const Config::Info& setting, + Config::Layer* layer) + : ConfigControl(setting.GetLocation(), layer), m_setting(setting) { addItems(options); setCurrentIndex(ReadValue(setting)); @@ -25,8 +26,9 @@ void ConfigChoice::OnConfigChanged() } ConfigStringChoice::ConfigStringChoice(const std::vector& options, - const Config::Info& setting) - : ConfigControl(setting.GetLocation()), m_setting(setting), m_text_is_data(true) + const Config::Info& setting, + Config::Layer* layer) + : ConfigControl(setting.GetLocation(), layer), m_setting(setting), m_text_is_data(true) { for (const auto& op : options) addItem(QString::fromStdString(op)); @@ -36,8 +38,9 @@ ConfigStringChoice::ConfigStringChoice(const std::vector& options, } ConfigStringChoice::ConfigStringChoice(const std::vector>& options, - const Config::Info& setting) - : ConfigControl(setting.GetLocation()), m_setting(setting), m_text_is_data(false) + const Config::Info& setting, + Config::Layer* layer) + : ConfigControl(setting.GetLocation(), layer), m_setting(setting), m_text_is_data(false) { for (const auto& [option_text, option_data] : options) addItem(option_text, option_data); @@ -57,9 +60,10 @@ void ConfigStringChoice::Update(int index) void ConfigStringChoice::Load() { const QString setting_value = QString::fromStdString(ReadValue(m_setting)); - const int index = m_text_is_data ? findText(setting_value) : findData(setting_value); - const QSignalBlocker blocker(this); + + // This can be called publicly. + const QSignalBlocker block(this); setCurrentIndex(index); } diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.h index 930e7aef10..fa2bdf6f88 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.h @@ -20,7 +20,8 @@ class ConfigChoice final : public ConfigControl { Q_OBJECT public: - ConfigChoice(const QStringList& options, const Config::Info& setting); + ConfigChoice(const QStringList& options, const Config::Info& setting, + Config::Layer* layer = nullptr); protected: void OnConfigChanged() override; @@ -36,16 +37,16 @@ class ConfigStringChoice final : public ConfigControl Q_OBJECT public: ConfigStringChoice(const std::vector& options, - const Config::Info& setting); + const Config::Info& setting, Config::Layer* layer = nullptr); ConfigStringChoice(const std::vector>& options, - const Config::Info& setting); + const Config::Info& setting, Config::Layer* layer = nullptr); + void Load(); protected: void OnConfigChanged() override; private: void Update(int index); - void Load(); const Config::Info& m_setting; bool m_text_is_data = false; diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigControl.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigControl.h index 4100781cfe..1be0a8fa19 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigControl.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigControl.h @@ -8,6 +8,7 @@ #include #include "Common/Config/Enums.h" +#include "Common/Config/Layer.h" #include "DolphinQt/Settings.h" namespace Config @@ -21,14 +22,19 @@ template class ConfigControl : public Derived { public: - ConfigControl(const Config::Location& location) : m_location(location) { ConnectConfig(); } - ConfigControl(const QString& label, const Config::Location& location) - : Derived(label), m_location(location) + ConfigControl(const Config::Location& location, Config::Layer* layer) + : m_location(location), m_layer(layer) { ConnectConfig(); } - ConfigControl(const Qt::Orientation& orient, const Config::Location& location) - : Derived(orient), m_location(location) + ConfigControl(const QString& label, const Config::Location& location, Config::Layer* layer) + : Derived(label), m_location(location), m_layer(layer) + { + ConnectConfig(); + } + ConfigControl(const Qt::Orientation& orient, const Config::Location& location, + Config::Layer* layer) + : Derived(orient), m_location(location), m_layer(layer) { ConnectConfig(); } @@ -51,22 +57,49 @@ protected: template void SaveValue(const Config::Info& setting, const T& value) { + if (m_layer != nullptr) + { + m_layer->Set(m_location, value); + Config::OnConfigChanged(); + return; + } + Config::SetBaseOrCurrent(setting, value); } template const T ReadValue(const Config::Info& setting) const { + if (m_layer != nullptr) + return m_layer->Get(setting); + return Config::Get(setting); } - virtual void OnConfigChanged() {}; + virtual void OnConfigChanged(){}; private: bool IsConfigLocal() const { - return Config::GetActiveLayerForConfig(m_location) != Config::LayerType::Base; + if (m_layer != nullptr) + return m_layer->Exists(m_location); + else + return Config::GetActiveLayerForConfig(m_location) != Config::LayerType::Base; + } + + void mousePressEvent(QMouseEvent* event) override + { + if (m_layer != nullptr && event->button() == Qt::RightButton) + { + m_layer->DeleteKey(m_location); + Config::OnConfigChanged(); + } + else + { + Derived::mousePressEvent(event); + } } const Config::Location m_location; + Config::Layer* m_layer; }; diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.cpp index b8aa2c82fc..e5211d00bb 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.cpp @@ -4,8 +4,9 @@ #include "DolphinQt/Config/ConfigControls/ConfigFloatSlider.h" ConfigFloatSlider::ConfigFloatSlider(float minimum, float maximum, - const Config::Info& setting, float step) - : ConfigControl(Qt::Horizontal, setting.GetLocation()), m_minimum(minimum), m_step(step), + const Config::Info& setting, float step, + Config::Layer* layer) + : ConfigControl(Qt::Horizontal, setting.GetLocation(), layer), m_minimum(minimum), m_step(step), m_setting(setting) { const float range = maximum - minimum; diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.h index 3c38e34095..f68b018379 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.h @@ -18,7 +18,8 @@ class ConfigFloatSlider final : public ConfigControl { Q_OBJECT public: - ConfigFloatSlider(float minimum, float maximum, const Config::Info& setting, float step); + ConfigFloatSlider(float minimum, float maximum, const Config::Info& setting, float step, + Config::Layer* layer = nullptr); void Update(int value); // Returns the adjusted float value diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.cpp index ba85e64617..8d724505e3 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.cpp @@ -4,7 +4,13 @@ #include "DolphinQt/Config/ConfigControls/ConfigInteger.h" ConfigInteger::ConfigInteger(int minimum, int maximum, const Config::Info& setting, int step) - : ConfigControl(setting.GetLocation()), m_setting(setting) + : ConfigInteger(minimum, maximum, setting, nullptr, step) +{ +} + +ConfigInteger::ConfigInteger(int minimum, int maximum, const Config::Info& setting, + Config::Layer* layer, int step) + : ConfigControl(setting.GetLocation(), layer), m_setting(setting) { setMinimum(minimum); setMaximum(maximum); diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.h index 21100c9af1..2979423f74 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.h @@ -17,6 +17,9 @@ class ConfigInteger final : public ConfigControl Q_OBJECT public: ConfigInteger(int minimum, int maximum, const Config::Info& setting, int step = 1); + ConfigInteger(int minimum, int maximum, const Config::Info& setting, Config::Layer* layer, + int step = 1); + void Update(int value); protected: diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.cpp index baaf7e6f48..eba9de283c 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.cpp @@ -3,8 +3,9 @@ #include "DolphinQt/Config/ConfigControls/ConfigRadio.h" -ConfigRadioInt::ConfigRadioInt(const QString& label, const Config::Info& setting, int value) - : ConfigControl(label, setting.GetLocation()), m_setting(setting), m_value(value) +ConfigRadioInt::ConfigRadioInt(const QString& label, const Config::Info& setting, int value, + Config::Layer* layer) + : ConfigControl(label, setting.GetLocation(), layer), m_setting(setting), m_value(value) { setChecked(ReadValue(setting) == value); diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.h index 8b28f2c106..c6b0e8835c 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.h @@ -16,7 +16,8 @@ class ConfigRadioInt final : public ConfigControl { Q_OBJECT public: - ConfigRadioInt(const QString& label, const Config::Info& setting, int value); + ConfigRadioInt(const QString& label, const Config::Info& setting, int value, + Config::Layer* layer = nullptr); signals: // Since selecting a new radio button deselects the old one, ::toggled will generate two signals. diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.cpp index a52b42d4f4..9192410e03 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.cpp @@ -4,7 +4,13 @@ #include "DolphinQt/Config/ConfigControls/ConfigSlider.h" ConfigSlider::ConfigSlider(int minimum, int maximum, const Config::Info& setting, int tick) - : ConfigControl(Qt::Horizontal, setting.GetLocation()), m_setting(setting) + : ConfigSlider(minimum, maximum, setting, nullptr, tick) +{ +} + +ConfigSlider::ConfigSlider(int minimum, int maximum, const Config::Info& setting, + Config::Layer* layer, int tick) + : ConfigControl(Qt::Horizontal, setting.GetLocation(), layer), m_setting(setting) { setMinimum(minimum); diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.h index ebb02391e8..7ac81d03d5 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.h @@ -17,6 +17,9 @@ class ConfigSlider final : public ConfigControl Q_OBJECT public: ConfigSlider(int minimum, int maximum, const Config::Info& setting, int tick = 0); + ConfigSlider(int minimum, int maximum, const Config::Info& setting, Config::Layer* layer, + int tick = 0); + void Update(int value); protected: 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 -#include +#include #include +#include +#include #include -#include -#include -#include #include +#include +#include #include #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( + ConfigLoaders::GenerateLocalGameConfigLoader(m_game_id, m_game.GetRevision())); + m_global_layer = std::make_unique( + 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 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")); + // 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); + + m_local_tab->insertTab(i, edit, QString::fromStdString(m_game_id + ".ini")); + break; + } + } + } + + // 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(); + } + + 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::ConnectWidgets() +GameConfigWidget::~GameConfigWidget() { - // Buttons - connect(m_refresh_config, &QPushButton::clicked, this, &GameConfigWidget::LoadSettings); + // Destructor saves the layer to file. + m_layer.reset(); - 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); -} - -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); - - if (m_gameini_default.GetOrCreateSection(section)->Get(key, &checked)) - return checkbox->setCheckState(checked ^ reverse ? Qt::Checked : Qt::Unchecked); - checkbox->setCheckState(Qt::PartiallyChecked); -} - -void GameConfigWidget::SaveCheckBox(QCheckBox* checkbox, const std::string& section, - const std::string& key, bool reverse) -{ - // 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. - - 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 globals + auto update_bool = [this](auto config, bool reverse = false) { + const Config::Location& setting = config->GetLocation(); - // Load game-specific settings + // Don't overwrite local with global + if (m_layer->Exists(setting) || !m_global_layer->Exists(setting)) + return; - // 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); + std::optional value = m_global_layer->Get(config->GetLocation()); - std::string determinism_mode; + if (value.has_value()) + { + const QSignalBlocker blocker(config); + config->setChecked(value.value() ^ reverse); + } + }; - int determinism_index = DETERMINISM_NOT_SET_INDEX; + auto update_int = [this](auto config) { + const Config::Location& setting = config->GetLocation(); - m_gameini_default.GetIfExists("Core", "GPUDeterminismMode", &determinism_mode); - m_gameini_local.GetIfExists("Core", "GPUDeterminismMode", &determinism_mode); + if (m_layer->Exists(setting) || !m_global_layer->Exists(setting)) + return; - if (determinism_mode == DETERMINISM_AUTO_STRING) + std::optional value = m_global_layer->Get(setting); + + if (value.has_value()) + { + const QSignalBlocker blocker(config); + config->setValue(value.value()); + } + }; + + for (ConfigBool* config : {m_enable_dual_core, m_enable_mmu, m_enable_fprf, m_sync_gpu, + m_use_dsp_hle, m_use_monoscopic_shadows}) { - 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; + update_bool(config); } - m_deterministic_dual_core->setCurrentIndex(determinism_index); + update_bool(m_emulate_disc_speed, true); - // 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"); + update_int(m_depth_slider); + update_int(m_convergence_spin); } -void GameConfigWidget::SaveSettings() +void GameConfigWidget::SetItalics() { - // 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); + // 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; - int determinism_num = m_deterministic_dual_core->currentIndex(); + QFont ifont = config->font(); + ifont.setItalic(true); + config->setFont(ifont); + }; - 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)) - { - m_gameini_local.GetOrCreateSection("Core")->Set("GPUDeterminismMode", determinism_mode); - } - } - else - { - m_gameini_local.DeleteKey("Core", "GPUDeterminismMode"); - } - - // Stereoscopy - int depth_percentage = m_depth_slider->value(); - - if (depth_percentage != 100) - { - int default_value = 0; - if (!(m_gameini_default.GetIfExists("Video_Stereoscopy", "StereoDepthPercentage", - &default_value) && - default_value == depth_percentage)) - { - m_gameini_local.GetOrCreateSection("Video_Stereoscopy") - ->Set("StereoDepthPercentage", depth_percentage); - } - } - - int convergence = m_convergence_spin->value(); - if (convergence != 0) - { - 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); - } - } - - SaveCheckBox(m_use_monoscopic_shadows, "Video_Stereoscopy", "StereoEFBMonoDepth"); - - bool success = m_gameini_local.Save(m_gameini_local_path.toStdString()); - - // 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()); + for (auto* config : findChildren()) + italics(config); + for (auto* config : findChildren()) + italics(config); + for (auto* config : findChildren()) + italics(config); + for (auto* config : findChildren()) + italics(config); + for (auto* config : findChildren()) + italics(config); + for (auto* config : findChildren()) + italics(config); } diff --git a/Source/Core/DolphinQt/Config/GameConfigWidget.h b/Source/Core/DolphinQt/Config/GameConfigWidget.h index 7ad4e64925..c0eb9eb15e 100644 --- a/Source/Core/DolphinQt/Config/GameConfigWidget.h +++ b/Source/Core/DolphinQt/Config/GameConfigWidget.h @@ -7,19 +7,21 @@ #include #include - -#include "Common/IniFile.h" - namespace UICommon { class GameFile; } -class QCheckBox; -class QComboBox; +namespace Config +{ +class Layer; +} // namespace Config + +class ConfigBool; +class ConfigInteger; +class ConfigSlider; +class ConfigStringChoice; class QPushButton; -class QSlider; -class QSpinBox; class QTabWidget; class GameConfigWidget : public QWidget @@ -27,45 +29,33 @@ class GameConfigWidget : public QWidget Q_OBJECT public: explicit GameConfigWidget(const UICommon::GameFile& game); + ~GameConfigWidget(); private: void CreateWidgets(); - void ConnectWidgets(); - void LoadSettings(); - void SaveSettings(); + void SetItalics(); - void SaveCheckBox(QCheckBox* checkbox, const std::string& section, const std::string& key, - bool reverse = false); - void LoadCheckBox(QCheckBox* checkbox, const std::string& section, const std::string& key, - bool reverse = false); - - QString m_gameini_sys_path; QString m_gameini_local_path; QTabWidget* m_default_tab; QTabWidget* m_local_tab; - QCheckBox* m_enable_dual_core; - QCheckBox* m_enable_mmu; - QCheckBox* m_enable_fprf; - QCheckBox* m_sync_gpu; - QCheckBox* m_emulate_disc_speed; - QCheckBox* m_use_dsp_hle; - QCheckBox* m_use_monoscopic_shadows; - QCheckBox* m_manual_texture_sampling; + ConfigBool* m_enable_dual_core; + ConfigBool* m_enable_mmu; + ConfigBool* m_enable_fprf; + ConfigBool* m_sync_gpu; + ConfigBool* m_emulate_disc_speed; + ConfigBool* m_use_dsp_hle; + ConfigBool* m_use_monoscopic_shadows; - QPushButton* m_refresh_config; - - QComboBox* m_deterministic_dual_core; - - QSlider* m_depth_slider; - - QSpinBox* m_convergence_spin; + ConfigStringChoice* m_deterministic_dual_core; + ConfigSlider* m_depth_slider; + ConfigInteger* m_convergence_spin; const UICommon::GameFile& m_game; std::string m_game_id; - - Common::IniFile m_gameini_local; - Common::IniFile m_gameini_default; + std::unique_ptr m_layer; + std::unique_ptr m_global_layer; + int m_prev_tab_index = 0; }; diff --git a/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp b/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp index 569b6856f2..c64c8fca40 100644 --- a/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.cpp @@ -3,11 +3,9 @@ #include "DolphinQt/Config/Graphics/AdvancedWidget.h" -#include #include #include #include -#include #include #include "Core/Config/GraphicsSettings.h" @@ -19,6 +17,7 @@ #include "DolphinQt/Config/ConfigControls/ConfigBool.h" #include "DolphinQt/Config/ConfigControls/ConfigChoice.h" #include "DolphinQt/Config/ConfigControls/ConfigInteger.h" +#include "DolphinQt/Config/GameConfigWidget.h" #include "DolphinQt/Config/Graphics/GraphicsWindow.h" #include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h" #include "DolphinQt/QtUtils/SignalBlocking.h" @@ -29,7 +28,6 @@ AdvancedWidget::AdvancedWidget(GraphicsWindow* parent) { CreateWidgets(); - LoadSettings(); ConnectWidgets(); AddDescriptions(); @@ -37,17 +35,30 @@ AdvancedWidget::AdvancedWidget(GraphicsWindow* parent) connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) { OnEmulationStateChanged(state != Core::State::Uninitialized); }); - connect(m_manual_texture_sampling, &QCheckBox::toggled, [this, parent] { - SaveSettings(); - emit parent->UseFastTextureSamplingChanged(); - }); + connect(m_manual_texture_sampling, &QCheckBox::toggled, + [this, parent] { emit parent->UseFastTextureSamplingChanged(); }); OnBackendChanged(); OnEmulationStateChanged(!Core::IsUninitialized(Core::System::GetInstance())); } +AdvancedWidget::AdvancedWidget(GameConfigWidget* parent, Config::Layer* layer) : m_game_layer(layer) +{ + CreateWidgets(); + ConnectWidgets(); + AddDescriptions(); + + connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) { + OnEmulationStateChanged(state != Core::State::Uninitialized); + }); + OnEmulationStateChanged(Core::GetState(Core::System::GetInstance()) != + Core::State::Uninitialized); +} + void AdvancedWidget::CreateWidgets() { + const bool local_edit = m_game_layer != nullptr; + auto* main_layout = new QVBoxLayout; // Performance @@ -55,17 +66,19 @@ void AdvancedWidget::CreateWidgets() auto* performance_layout = new QGridLayout(); performance_box->setLayout(performance_layout); - m_show_fps = new ConfigBool(tr("Show FPS"), Config::GFX_SHOW_FPS); - m_show_ftimes = new ConfigBool(tr("Show Frame Times"), Config::GFX_SHOW_FTIMES); - m_show_vps = new ConfigBool(tr("Show VPS"), Config::GFX_SHOW_VPS); - m_show_vtimes = new ConfigBool(tr("Show VBlank Times"), Config::GFX_SHOW_VTIMES); - m_show_graphs = new ConfigBool(tr("Show Performance Graphs"), Config::GFX_SHOW_GRAPHS); - m_show_speed = new ConfigBool(tr("Show % Speed"), Config::GFX_SHOW_SPEED); - m_show_speed_colors = new ConfigBool(tr("Show Speed Colors"), Config::GFX_SHOW_SPEED_COLORS); - m_perf_samp_window = new ConfigInteger(0, 10000, Config::GFX_PERF_SAMP_WINDOW, 100); + m_show_fps = new ConfigBool(tr("Show FPS"), Config::GFX_SHOW_FPS, m_game_layer); + m_show_ftimes = new ConfigBool(tr("Show Frame Times"), Config::GFX_SHOW_FTIMES, m_game_layer); + m_show_vps = new ConfigBool(tr("Show VPS"), Config::GFX_SHOW_VPS, m_game_layer); + m_show_vtimes = new ConfigBool(tr("Show VBlank Times"), Config::GFX_SHOW_VTIMES, m_game_layer); + m_show_graphs = + new ConfigBool(tr("Show Performance Graphs"), Config::GFX_SHOW_GRAPHS, m_game_layer); + m_show_speed = new ConfigBool(tr("Show % Speed"), Config::GFX_SHOW_SPEED, m_game_layer); + m_show_speed_colors = + new ConfigBool(tr("Show Speed Colors"), Config::GFX_SHOW_SPEED_COLORS, m_game_layer); + m_perf_samp_window = new ConfigInteger(0, 10000, Config::GFX_PERF_SAMP_WINDOW, m_game_layer, 100); m_perf_samp_window->SetTitle(tr("Performance Sample Window (ms)")); - m_log_render_time = - new ConfigBool(tr("Log Render Time to File"), Config::GFX_LOG_RENDER_TIME_TO_FILE); + m_log_render_time = new ConfigBool(tr("Log Render Time to File"), + Config::GFX_LOG_RENDER_TIME_TO_FILE, m_game_layer); performance_layout->addWidget(m_show_fps, 0, 0); performance_layout->addWidget(m_show_ftimes, 0, 1); @@ -83,14 +96,16 @@ void AdvancedWidget::CreateWidgets() auto* debugging_layout = new QGridLayout(); debugging_box->setLayout(debugging_layout); - m_enable_wireframe = new ConfigBool(tr("Enable Wireframe"), Config::GFX_ENABLE_WIREFRAME); - m_show_statistics = new ConfigBool(tr("Show Statistics"), Config::GFX_OVERLAY_STATS); - m_show_proj_statistics = - new ConfigBool(tr("Show Projection Statistics"), Config::GFX_OVERLAY_PROJ_STATS); + m_enable_wireframe = + new ConfigBool(tr("Enable Wireframe"), Config::GFX_ENABLE_WIREFRAME, m_game_layer); + m_show_statistics = + new ConfigBool(tr("Show Statistics"), Config::GFX_OVERLAY_STATS, m_game_layer); + m_show_proj_statistics = new ConfigBool(tr("Show Projection Statistics"), + Config::GFX_OVERLAY_PROJ_STATS, m_game_layer); m_enable_format_overlay = - new ConfigBool(tr("Texture Format Overlay"), Config::GFX_TEXFMT_OVERLAY_ENABLE); - m_enable_api_validation = - new ConfigBool(tr("Enable API Validation Layers"), Config::GFX_ENABLE_VALIDATION_LAYER); + new ConfigBool(tr("Texture Format Overlay"), Config::GFX_TEXFMT_OVERLAY_ENABLE, m_game_layer); + m_enable_api_validation = new ConfigBool(tr("Enable API Validation Layers"), + Config::GFX_ENABLE_VALIDATION_LAYER, m_game_layer); debugging_layout->addWidget(m_enable_wireframe, 0, 0); debugging_layout->addWidget(m_show_statistics, 0, 1); @@ -103,14 +118,25 @@ void AdvancedWidget::CreateWidgets() auto* utility_layout = new QGridLayout(); utility_box->setLayout(utility_layout); - m_load_custom_textures = new ConfigBool(tr("Load Custom Textures"), Config::GFX_HIRES_TEXTURES); - m_prefetch_custom_textures = - new ConfigBool(tr("Prefetch Custom Textures"), Config::GFX_CACHE_HIRES_TEXTURES); + m_load_custom_textures = + new ConfigBool(tr("Load Custom Textures"), Config::GFX_HIRES_TEXTURES, m_game_layer); + m_prefetch_custom_textures = new ConfigBool(tr("Prefetch Custom Textures"), + Config::GFX_CACHE_HIRES_TEXTURES, m_game_layer); + m_prefetch_custom_textures->setEnabled(m_load_custom_textures->isChecked()); m_dump_efb_target = new ConfigBool(tr("Dump EFB Target"), Config::GFX_DUMP_EFB_TARGET); m_dump_xfb_target = new ConfigBool(tr("Dump XFB Target"), Config::GFX_DUMP_XFB_TARGET); - m_disable_vram_copies = - new ConfigBool(tr("Disable EFB VRAM Copies"), Config::GFX_HACK_DISABLE_COPY_TO_VRAM); - m_enable_graphics_mods = new ToolTipCheckBox(tr("Enable Graphics Mods")); + + if (local_edit) + { + // It's hazardous to accidentally set these in a game ini. + m_dump_efb_target->setEnabled(false); + m_dump_xfb_target->setEnabled(false); + } + + m_disable_vram_copies = new ConfigBool(tr("Disable EFB VRAM Copies"), + Config::GFX_HACK_DISABLE_COPY_TO_VRAM, m_game_layer); + m_enable_graphics_mods = + new ConfigBool(tr("Enable Graphics Mods"), Config::GFX_MODS_ENABLE, m_game_layer); utility_layout->addWidget(m_load_custom_textures, 0, 0); utility_layout->addWidget(m_prefetch_custom_textures, 0, 1); @@ -128,6 +154,16 @@ void AdvancedWidget::CreateWidgets() m_dump_textures = new ConfigBool(tr("Enable"), Config::GFX_DUMP_TEXTURES); m_dump_base_textures = new ConfigBool(tr("Dump Base Textures"), Config::GFX_DUMP_BASE_TEXTURES); m_dump_mip_textures = new ConfigBool(tr("Dump Mip Maps"), Config::GFX_DUMP_MIP_TEXTURES); + m_dump_mip_textures->setEnabled(m_dump_textures->isChecked()); + m_dump_base_textures->setEnabled(m_dump_textures->isChecked()); + + if (local_edit) + { + // It's hazardous to accidentally set dumping in a game ini. + m_dump_textures->setEnabled(false); + m_dump_base_textures->setEnabled(false); + m_dump_mip_textures->setEnabled(false); + } texture_dump_layout->addWidget(m_dump_textures, 0, 0); @@ -142,18 +178,22 @@ void AdvancedWidget::CreateWidgets() m_frame_dumps_resolution_type = new ConfigChoice({tr("Window Resolution"), tr("Aspect Ratio Corrected Internal Resolution"), tr("Raw Internal Resolution")}, - Config::GFX_FRAME_DUMPS_RESOLUTION_TYPE); - m_dump_use_ffv1 = new ConfigBool(tr("Use Lossless Codec (FFV1)"), Config::GFX_USE_FFV1); - m_dump_bitrate = new ConfigInteger(0, 1000000, Config::GFX_BITRATE_KBPS, 1000); - m_png_compression_level = new ConfigInteger(0, 9, Config::GFX_PNG_COMPRESSION_LEVEL); - + Config::GFX_FRAME_DUMPS_RESOLUTION_TYPE, m_game_layer); + m_png_compression_level = + new ConfigInteger(0, 9, Config::GFX_PNG_COMPRESSION_LEVEL, m_game_layer); dump_layout->addWidget(new QLabel(tr("Resolution Type:")), 0, 0); dump_layout->addWidget(m_frame_dumps_resolution_type, 0, 1); + #if defined(HAVE_FFMPEG) + m_dump_use_ffv1 = + new ConfigBool(tr("Use Lossless Codec (FFV1)"), Config::GFX_USE_FFV1, m_game_layer); + m_dump_bitrate = new ConfigInteger(0, 1000000, Config::GFX_BITRATE_KBPS, m_game_layer, 1000); + m_dump_bitrate->setEnabled(!m_dump_use_ffv1->isChecked()); dump_layout->addWidget(m_dump_use_ffv1, 1, 0); dump_layout->addWidget(new QLabel(tr("Bitrate (kbps):")), 2, 0); dump_layout->addWidget(m_dump_bitrate, 2, 1); #endif + dump_layout->addWidget(new QLabel(tr("PNG Compression Level:")), 3, 0); m_png_compression_level->SetTitle(tr("PNG Compression Level")); dump_layout->addWidget(m_png_compression_level, 3, 1); @@ -163,14 +203,16 @@ void AdvancedWidget::CreateWidgets() auto* misc_layout = new QGridLayout(); misc_box->setLayout(misc_layout); - m_enable_cropping = new ConfigBool(tr("Crop"), Config::GFX_CROP); - m_enable_prog_scan = new ToolTipCheckBox(tr("Enable Progressive Scan")); - m_backend_multithreading = - new ConfigBool(tr("Backend Multithreading"), Config::GFX_BACKEND_MULTITHREADING); + m_enable_cropping = new ConfigBool(tr("Crop"), Config::GFX_CROP, m_game_layer); + m_enable_prog_scan = + new ConfigBool(tr("Enable Progressive Scan"), Config::SYSCONF_PROGRESSIVE_SCAN, m_game_layer); + m_backend_multithreading = new ConfigBool(tr("Backend Multithreading"), + Config::GFX_BACKEND_MULTITHREADING, m_game_layer); m_prefer_vs_for_point_line_expansion = new ConfigBool( // i18n: VS is short for vertex shaders. - tr("Prefer VS for Point/Line Expansion"), Config::GFX_PREFER_VS_FOR_LINE_POINT_EXPANSION); - m_cpu_cull = new ConfigBool(tr("Cull Vertices on the CPU"), Config::GFX_CPU_CULL); + tr("Prefer VS for Point/Line Expansion"), Config::GFX_PREFER_VS_FOR_LINE_POINT_EXPANSION, + m_game_layer); + m_cpu_cull = new ConfigBool(tr("Cull Vertices on the CPU"), Config::GFX_CPU_CULL, m_game_layer); misc_layout->addWidget(m_enable_cropping, 0, 0); misc_layout->addWidget(m_enable_prog_scan, 0, 1); @@ -179,7 +221,7 @@ void AdvancedWidget::CreateWidgets() misc_layout->addWidget(m_cpu_cull, 2, 0); #ifdef _WIN32 m_borderless_fullscreen = - new ConfigBool(tr("Borderless Fullscreen"), Config::GFX_BORDERLESS_FULLSCREEN); + new ConfigBool(tr("Borderless Fullscreen"), Config::GFX_BORDERLESS_FULLSCREEN, m_game_layer); misc_layout->addWidget(m_borderless_fullscreen, 2, 1); #endif @@ -189,10 +231,10 @@ void AdvancedWidget::CreateWidgets() auto* experimental_layout = new QGridLayout(); experimental_box->setLayout(experimental_layout); - m_defer_efb_access_invalidation = - new ConfigBool(tr("Defer EFB Cache Invalidation"), Config::GFX_HACK_EFB_DEFER_INVALIDATION); - m_manual_texture_sampling = - new ConfigBool(tr("Manual Texture Sampling"), Config::GFX_HACK_FAST_TEXTURE_SAMPLING, true); + m_defer_efb_access_invalidation = new ConfigBool( + tr("Defer EFB Cache Invalidation"), Config::GFX_HACK_EFB_DEFER_INVALIDATION, m_game_layer); + m_manual_texture_sampling = new ConfigBool( + tr("Manual Texture Sampling"), Config::GFX_HACK_FAST_TEXTURE_SAMPLING, m_game_layer, true); experimental_layout->addWidget(m_defer_efb_access_invalidation, 0, 0); experimental_layout->addWidget(m_manual_texture_sampling, 0, 1); @@ -211,34 +253,19 @@ void AdvancedWidget::CreateWidgets() void AdvancedWidget::ConnectWidgets() { - connect(m_load_custom_textures, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings); - connect(m_dump_use_ffv1, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings); - connect(m_enable_prog_scan, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings); - connect(m_dump_textures, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings); - connect(m_enable_graphics_mods, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings); -} + connect(m_load_custom_textures, &QCheckBox::toggled, this, + [this](bool checked) { m_prefetch_custom_textures->setEnabled(checked); }); + connect(m_dump_textures, &QCheckBox::toggled, this, [this](bool checked) { + m_dump_mip_textures->setEnabled(checked); + m_dump_base_textures->setEnabled(checked); + }); + connect(m_enable_graphics_mods, &QCheckBox::toggled, this, + [this](bool checked) { emit Settings::Instance().EnableGfxModsChanged(checked); }); -void AdvancedWidget::LoadSettings() -{ - m_prefetch_custom_textures->setEnabled(Config::Get(Config::GFX_HIRES_TEXTURES)); - m_dump_bitrate->setEnabled(!Config::Get(Config::GFX_USE_FFV1)); - - m_enable_prog_scan->setChecked(Config::Get(Config::SYSCONF_PROGRESSIVE_SCAN)); - m_dump_mip_textures->setEnabled(Config::Get(Config::GFX_DUMP_TEXTURES)); - m_dump_base_textures->setEnabled(Config::Get(Config::GFX_DUMP_TEXTURES)); - - SignalBlocking(m_enable_graphics_mods)->setChecked(Settings::Instance().GetGraphicModsEnabled()); -} - -void AdvancedWidget::SaveSettings() -{ - m_prefetch_custom_textures->setEnabled(Config::Get(Config::GFX_HIRES_TEXTURES)); - m_dump_bitrate->setEnabled(!Config::Get(Config::GFX_USE_FFV1)); - - Config::SetBase(Config::SYSCONF_PROGRESSIVE_SCAN, m_enable_prog_scan->isChecked()); - m_dump_mip_textures->setEnabled(Config::Get(Config::GFX_DUMP_TEXTURES)); - m_dump_base_textures->setEnabled(Config::Get(Config::GFX_DUMP_TEXTURES)); - Settings::Instance().SetGraphicModsEnabled(m_enable_graphics_mods->isChecked()); +#if defined(HAVE_FFMPEG) + connect(m_dump_use_ffv1, &QCheckBox::toggled, this, + [this](bool checked) { m_dump_bitrate->setEnabled(!checked); }); +#endif } void AdvancedWidget::OnBackendChanged() diff --git a/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.h b/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.h index da7504955c..e3b3688571 100644 --- a/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.h +++ b/Source/Core/DolphinQt/Config/Graphics/AdvancedWidget.h @@ -8,22 +8,22 @@ class ConfigBool; class ConfigChoice; class ConfigInteger; +class GameConfigWidget; class GraphicsWindow; -class QCheckBox; -class QComboBox; -class QSpinBox; -class ToolTipCheckBox; + +namespace Config +{ +class Layer; +} // namespace Config class AdvancedWidget final : public QWidget { Q_OBJECT public: explicit AdvancedWidget(GraphicsWindow* parent); + AdvancedWidget(GameConfigWidget* parent, Config::Layer* layer); private: - void LoadSettings(); - void SaveSettings(); - void CreateWidgets(); void ConnectWidgets(); void AddDescriptions(); @@ -52,7 +52,7 @@ private: ConfigBool* m_dump_xfb_target; ConfigBool* m_disable_vram_copies; ConfigBool* m_load_custom_textures; - ToolTipCheckBox* m_enable_graphics_mods; + ConfigBool* m_enable_graphics_mods; // Texture dumping ConfigBool* m_dump_textures; @@ -67,7 +67,7 @@ private: // Misc ConfigBool* m_enable_cropping; - ToolTipCheckBox* m_enable_prog_scan; + ConfigBool* m_enable_prog_scan; ConfigBool* m_backend_multithreading; ConfigBool* m_prefer_vs_for_point_line_expansion; ConfigBool* m_cpu_cull; @@ -76,4 +76,6 @@ private: // Experimental ConfigBool* m_defer_efb_access_invalidation; ConfigBool* m_manual_texture_sampling; + + Config::Layer* m_game_layer = nullptr; }; diff --git a/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.cpp b/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.cpp index 7cf67ab39a..3fd3ec42d8 100644 --- a/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.cpp @@ -18,6 +18,7 @@ #include "DolphinQt/Config/ConfigControls/ConfigChoice.h" #include "DolphinQt/Config/ConfigControls/ConfigRadio.h" #include "DolphinQt/Config/ConfigControls/ConfigSlider.h" +#include "DolphinQt/Config/GameConfigWidget.h" #include "DolphinQt/Config/Graphics/ColorCorrectionConfigWindow.h" #include "DolphinQt/Config/Graphics/GraphicsWindow.h" #include "DolphinQt/Config/Graphics/PostProcessingConfigWindow.h" @@ -45,6 +46,15 @@ EnhancementsWidget::EnhancementsWidget(GraphicsWindow* parent) : m_block_save(fa &EnhancementsWidget::LoadSettings); } +EnhancementsWidget::EnhancementsWidget(GameConfigWidget* parent, Config::Layer* layer) + : m_game_layer(layer) +{ + CreateWidgets(); + LoadSettings(); + ConnectWidgets(); + AddDescriptions(); +} + constexpr int TEXTURE_FILTERING_DEFAULT = 0; constexpr int TEXTURE_FILTERING_ANISO_2X = 1; constexpr int TEXTURE_FILTERING_ANISO_4X = 2; @@ -104,7 +114,7 @@ void EnhancementsWidget::CreateWidgets() } } - m_ir_combo = new ConfigChoice(resolution_options, Config::GFX_EFB_SCALE); + m_ir_combo = new ConfigChoice(resolution_options, Config::GFX_EFB_SCALE, m_game_layer); m_ir_combo->setMaxVisibleItems(visible_resolution_option_count); m_aa_combo = new ToolTipComboBox(); @@ -146,19 +156,22 @@ void EnhancementsWidget::CreateWidgets() m_pp_effect = new ToolTipComboBox(); m_configure_pp_effect = new NonDefaultQPushButton(tr("Configure")); - m_scaled_efb_copy = new ConfigBool(tr("Scaled EFB Copy"), Config::GFX_HACK_COPY_EFB_SCALED); + m_scaled_efb_copy = + new ConfigBool(tr("Scaled EFB Copy"), Config::GFX_HACK_COPY_EFB_SCALED, m_game_layer); m_per_pixel_lighting = - new ConfigBool(tr("Per-Pixel Lighting"), Config::GFX_ENABLE_PIXEL_LIGHTING); + new ConfigBool(tr("Per-Pixel Lighting"), Config::GFX_ENABLE_PIXEL_LIGHTING, m_game_layer); - m_widescreen_hack = new ConfigBool(tr("Widescreen Hack"), Config::GFX_WIDESCREEN_HACK); - m_disable_fog = new ConfigBool(tr("Disable Fog"), Config::GFX_DISABLE_FOG); + m_widescreen_hack = + new ConfigBool(tr("Widescreen Hack"), Config::GFX_WIDESCREEN_HACK, m_game_layer); + m_disable_fog = new ConfigBool(tr("Disable Fog"), Config::GFX_DISABLE_FOG, m_game_layer); m_force_24bit_color = - new ConfigBool(tr("Force 24-Bit Color"), Config::GFX_ENHANCE_FORCE_TRUE_COLOR); - m_disable_copy_filter = - new ConfigBool(tr("Disable Copy Filter"), Config::GFX_ENHANCE_DISABLE_COPY_FILTER); - m_arbitrary_mipmap_detection = new ConfigBool(tr("Arbitrary Mipmap Detection"), - Config::GFX_ENHANCE_ARBITRARY_MIPMAP_DETECTION); - m_hdr = new ConfigBool(tr("HDR Post-Processing"), Config::GFX_ENHANCE_HDR_OUTPUT); + new ConfigBool(tr("Force 24-Bit Color"), Config::GFX_ENHANCE_FORCE_TRUE_COLOR, m_game_layer); + m_disable_copy_filter = new ConfigBool(tr("Disable Copy Filter"), + Config::GFX_ENHANCE_DISABLE_COPY_FILTER, m_game_layer); + m_arbitrary_mipmap_detection = + new ConfigBool(tr("Arbitrary Mipmap Detection"), + Config::GFX_ENHANCE_ARBITRARY_MIPMAP_DETECTION, m_game_layer); + m_hdr = new ConfigBool(tr("HDR Post-Processing"), Config::GFX_ENHANCE_HDR_OUTPUT, m_game_layer); int row = 0; enhancements_layout->addWidget(new QLabel(tr("Internal Resolution:")), row, 0); @@ -209,15 +222,16 @@ void EnhancementsWidget::CreateWidgets() m_3d_mode = new ConfigChoice({tr("Off"), tr("Side-by-Side"), tr("Top-and-Bottom"), tr("Anaglyph"), tr("HDMI 3D"), tr("Passive")}, - Config::GFX_STEREO_MODE); - m_3d_depth = new ConfigSlider(0, Config::GFX_STEREO_DEPTH_MAXIMUM, Config::GFX_STEREO_DEPTH); + Config::GFX_STEREO_MODE, m_game_layer); + m_3d_depth = + new ConfigSlider(0, Config::GFX_STEREO_DEPTH_MAXIMUM, Config::GFX_STEREO_DEPTH, m_game_layer); m_3d_convergence = new ConfigSlider(0, Config::GFX_STEREO_CONVERGENCE_MAXIMUM, - Config::GFX_STEREO_CONVERGENCE, 100); + Config::GFX_STEREO_CONVERGENCE, m_game_layer, 100); - m_3d_swap_eyes = new ConfigBool(tr("Swap Eyes"), Config::GFX_STEREO_SWAP_EYES); + m_3d_swap_eyes = new ConfigBool(tr("Swap Eyes"), Config::GFX_STEREO_SWAP_EYES, m_game_layer); - m_3d_per_eye_resolution = - new ConfigBool(tr("Use Full Resolution Per Eye"), Config::GFX_STEREO_PER_EYE_RESOLUTION_FULL); + m_3d_per_eye_resolution = new ConfigBool( + tr("Use Full Resolution Per Eye"), Config::GFX_STEREO_PER_EYE_RESOLUTION_FULL, m_game_layer); stereoscopy_layout->addWidget(new QLabel(tr("Stereoscopic 3D Mode:")), 0, 0); stereoscopy_layout->addWidget(m_3d_mode, 0, 1); diff --git a/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.h b/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.h index 9e64d5400f..f7922ee424 100644 --- a/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.h +++ b/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.h @@ -10,6 +10,7 @@ class ConfigBool; class ConfigChoice; class ConfigSlider; +class GameConfigWidget; class GraphicsWindow; class QCheckBox; class QComboBox; @@ -19,11 +20,17 @@ class ToolTipComboBox; class ToolTipPushButton; enum class StereoMode : int; +namespace Config +{ +class Layer; +} // namespace Config + class EnhancementsWidget final : public QWidget { Q_OBJECT public: explicit EnhancementsWidget(GraphicsWindow* parent); + EnhancementsWidget(GameConfigWidget* parent, Config::Layer* layer); private: void LoadSettings(); @@ -60,6 +67,7 @@ private: ConfigBool* m_3d_swap_eyes; ConfigBool* m_3d_per_eye_resolution; + Config::Layer* m_game_layer = nullptr; int m_msaa_modes; bool m_block_save; }; diff --git a/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.cpp b/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.cpp index b5f5a6b05a..bd94cde5b8 100644 --- a/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.cpp @@ -23,6 +23,7 @@ #include "DolphinQt/Config/ConfigControls/ConfigChoice.h" #include "DolphinQt/Config/ConfigControls/ConfigInteger.h" #include "DolphinQt/Config/ConfigControls/ConfigRadio.h" +#include "DolphinQt/Config/GameConfigWidget.h" #include "DolphinQt/Config/Graphics/GraphicsWindow.h" #include "DolphinQt/Config/ToolTipControls/ToolTipComboBox.h" #include "DolphinQt/QtUtils/ModalMessageBox.h" @@ -38,7 +39,6 @@ GeneralWidget::GeneralWidget(GraphicsWindow* parent) LoadSettings(); ConnectWidgets(); AddDescriptions(); - emit BackendChanged(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND))); connect(parent, &GraphicsWindow::BackendChanged, this, &GeneralWidget::OnBackendChanged); connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) { @@ -47,6 +47,14 @@ GeneralWidget::GeneralWidget(GraphicsWindow* parent) OnEmulationStateChanged(!Core::IsUninitialized(Core::System::GetInstance())); } +GeneralWidget::GeneralWidget(GameConfigWidget* parent, Config::Layer* layer) : m_game_layer(layer) +{ + CreateWidgets(); + LoadSettings(); + ConnectWidgets(); + AddDescriptions(); +} + void GeneralWidget::CreateWidgets() { auto* main_layout = new QVBoxLayout; @@ -55,33 +63,36 @@ void GeneralWidget::CreateWidgets() auto* m_video_box = new QGroupBox(tr("Basic")); m_video_layout = new QGridLayout(); - m_backend_combo = new ToolTipComboBox(); + std::vector> options; + for (auto& backend : VideoBackendBase::GetAvailableBackends()) + { + options.push_back(std::make_pair(tr(backend->GetDisplayName().c_str()), + QString::fromStdString(backend->GetName()))); + } + m_backend_combo = new ConfigStringChoice(options, Config::MAIN_GFX_BACKEND, m_game_layer); + m_previous_backend = m_backend_combo->currentIndex(); + m_aspect_combo = new ConfigChoice({tr("Auto"), tr("Force 16:9"), tr("Force 4:3"), tr("Stretch to Window"), tr("Custom"), tr("Custom (Stretch)")}, - Config::GFX_ASPECT_RATIO); + Config::GFX_ASPECT_RATIO, m_game_layer); m_custom_aspect_label = new QLabel(tr("Custom Aspect Ratio:")); m_custom_aspect_label->setHidden(true); constexpr int MAX_CUSTOM_ASPECT_RATIO_RESOLUTION = 10000; m_custom_aspect_width = new ConfigInteger(1, MAX_CUSTOM_ASPECT_RATIO_RESOLUTION, - Config::GFX_CUSTOM_ASPECT_RATIO_WIDTH); + Config::GFX_CUSTOM_ASPECT_RATIO_WIDTH, m_game_layer); m_custom_aspect_width->setEnabled(false); m_custom_aspect_width->setHidden(true); m_custom_aspect_height = new ConfigInteger(1, MAX_CUSTOM_ASPECT_RATIO_RESOLUTION, - Config::GFX_CUSTOM_ASPECT_RATIO_HEIGHT); + Config::GFX_CUSTOM_ASPECT_RATIO_HEIGHT, m_game_layer); m_custom_aspect_height->setEnabled(false); m_custom_aspect_height->setHidden(true); m_adapter_combo = new ToolTipComboBox; - m_enable_vsync = new ConfigBool(tr("V-Sync"), Config::GFX_VSYNC); - m_enable_fullscreen = new ConfigBool(tr("Start in Fullscreen"), Config::MAIN_FULLSCREEN); + m_enable_vsync = new ConfigBool(tr("V-Sync"), Config::GFX_VSYNC, m_game_layer); + m_enable_fullscreen = + new ConfigBool(tr("Start in Fullscreen"), Config::MAIN_FULLSCREEN, m_game_layer); m_video_box->setLayout(m_video_layout); - for (auto& backend : VideoBackendBase::GetAvailableBackends()) - { - m_backend_combo->addItem(tr(backend->GetDisplayName().c_str()), - QVariant(QString::fromStdString(backend->GetName()))); - } - m_video_layout->addWidget(new QLabel(tr("Backend:")), 0, 0); m_video_layout->addWidget(m_backend_combo, 0, 1, 1, -1); @@ -102,11 +113,14 @@ void GeneralWidget::CreateWidgets() auto* m_options_box = new QGroupBox(tr("Other")); auto* m_options_layout = new QGridLayout(); - m_show_ping = new ConfigBool(tr("Show NetPlay Ping"), Config::GFX_SHOW_NETPLAY_PING); - m_autoadjust_window_size = - new ConfigBool(tr("Auto-Adjust Window Size"), Config::MAIN_RENDER_WINDOW_AUTOSIZE); - m_show_messages = new ConfigBool(tr("Show NetPlay Messages"), Config::GFX_SHOW_NETPLAY_MESSAGES); - m_render_main_window = new ConfigBool(tr("Render to Main Window"), Config::MAIN_RENDER_TO_MAIN); + m_show_ping = + new ConfigBool(tr("Show NetPlay Ping"), Config::GFX_SHOW_NETPLAY_PING, m_game_layer); + m_autoadjust_window_size = new ConfigBool(tr("Auto-Adjust Window Size"), + Config::MAIN_RENDER_WINDOW_AUTOSIZE, m_game_layer); + m_show_messages = + new ConfigBool(tr("Show NetPlay Messages"), Config::GFX_SHOW_NETPLAY_MESSAGES, m_game_layer); + m_render_main_window = + new ConfigBool(tr("Render to Main Window"), Config::MAIN_RENDER_TO_MAIN, m_game_layer); m_options_box->setLayout(m_options_layout); @@ -128,13 +142,13 @@ void GeneralWidget::CreateWidgets() }}; for (size_t i = 0; i < modes.size(); i++) { - m_shader_compilation_mode[i] = - new ConfigRadioInt(tr(modes[i]), Config::GFX_SHADER_COMPILATION_MODE, static_cast(i)); + m_shader_compilation_mode[i] = new ConfigRadioInt( + tr(modes[i]), Config::GFX_SHADER_COMPILATION_MODE, static_cast(i), m_game_layer); shader_compilation_layout->addWidget(m_shader_compilation_mode[i], static_cast(i / 2), static_cast(i % 2)); } m_wait_for_shaders = new ConfigBool(tr("Compile Shaders Before Starting"), - Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING); + Config::GFX_WAIT_FOR_SHADERS_BEFORE_STARTING, m_game_layer); shader_compilation_layout->addWidget(m_wait_for_shaders); shader_compilation_box->setLayout(shader_compilation_layout); @@ -149,7 +163,7 @@ void GeneralWidget::CreateWidgets() void GeneralWidget::ConnectWidgets() { // Video Backend - connect(m_backend_combo, &QComboBox::currentIndexChanged, this, &GeneralWidget::SaveSettings); + connect(m_backend_combo, &QComboBox::currentIndexChanged, this, &GeneralWidget::BackendWarning); connect(m_adapter_combo, &QComboBox::currentIndexChanged, this, [&](int index) { g_Config.iAdapter = index; Config::SetBaseOrCurrent(Config::GFX_ADAPTER, index); @@ -168,10 +182,6 @@ void GeneralWidget::ConnectWidgets() void GeneralWidget::LoadSettings() { - // Video Backend - m_backend_combo->setCurrentIndex(m_backend_combo->findData( - QVariant(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND))))); - const bool is_custom_aspect_ratio = (Config::Get(Config::GFX_ASPECT_RATIO) == AspectMode::Custom) || (Config::Get(Config::GFX_ASPECT_RATIO) == AspectMode::CustomStretch); @@ -182,13 +192,8 @@ void GeneralWidget::LoadSettings() m_custom_aspect_height->setHidden(!is_custom_aspect_ratio); } -void GeneralWidget::SaveSettings() +void GeneralWidget::BackendWarning() { - // Video Backend - const auto current_backend = m_backend_combo->currentData().toString().toStdString(); - if (Config::Get(Config::MAIN_GFX_BACKEND) == current_backend) - return; - if (Config::GetActiveLayerForConfig(Config::MAIN_GFX_BACKEND) == Config::LayerType::Base) { auto warningMessage = VideoBackendBase::GetAvailableBackends()[m_backend_combo->currentIndex()] @@ -205,15 +210,14 @@ void GeneralWidget::SaveSettings() SetQWidgetWindowDecorations(&confirm_sw); if (confirm_sw.exec() != QMessageBox::Yes) { - m_backend_combo->setCurrentIndex(m_backend_combo->findData( - QVariant(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND))))); + m_backend_combo->setCurrentIndex(m_previous_backend); return; } } } - Config::SetBaseOrCurrent(Config::MAIN_GFX_BACKEND, current_backend); - emit BackendChanged(QString::fromStdString(current_backend)); + m_previous_backend = m_backend_combo->currentIndex(); + emit BackendChanged(m_backend_combo->currentData().toString()); } void GeneralWidget::OnEmulationStateChanged(bool running) @@ -227,7 +231,10 @@ void GeneralWidget::OnEmulationStateChanged(bool running) std::string current_backend = m_backend_combo->currentData().toString().toStdString(); if (Config::Get(Config::MAIN_GFX_BACKEND) != current_backend) + { + m_backend_combo->Load(); emit BackendChanged(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND))); + } } void GeneralWidget::AddDescriptions() @@ -348,8 +355,6 @@ void GeneralWidget::AddDescriptions() void GeneralWidget::OnBackendChanged(const QString& backend_name) { - m_backend_combo->setCurrentIndex(m_backend_combo->findData(QVariant(backend_name))); - const QSignalBlocker blocker(m_adapter_combo); m_adapter_combo->clear(); diff --git a/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.h b/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.h index 4f908bfa30..5b4bf36441 100644 --- a/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.h +++ b/Source/Core/DolphinQt/Config/Graphics/GeneralWidget.h @@ -11,6 +11,8 @@ class ConfigBool; class ConfigChoice; class ConfigInteger; class ConfigRadioInt; +class ConfigStringChoice; +class GameConfigWidget; class GraphicsWindow; class QCheckBox; class QComboBox; @@ -19,17 +21,24 @@ class QRadioButton; class QGridLayout; class ToolTipComboBox; +namespace Config +{ +class Layer; +} // namespace Config + class GeneralWidget final : public QWidget { Q_OBJECT public: explicit GeneralWidget(GraphicsWindow* parent); + GeneralWidget(GameConfigWidget* parent, Config::Layer* layer); + signals: void BackendChanged(const QString& backend); private: void LoadSettings(); - void SaveSettings(); + void BackendWarning(); void CreateWidgets(); void ConnectWidgets(); @@ -40,7 +49,7 @@ private: // Video QGridLayout* m_video_layout; - ToolTipComboBox* m_backend_combo; + ConfigStringChoice* m_backend_combo; ToolTipComboBox* m_adapter_combo; ConfigChoice* m_aspect_combo; QLabel* m_custom_aspect_label; @@ -56,4 +65,6 @@ private: ConfigBool* m_render_main_window; std::array m_shader_compilation_mode{}; ConfigBool* m_wait_for_shaders; + int m_previous_backend = 0; + Config::Layer* m_game_layer = nullptr; }; diff --git a/Source/Core/DolphinQt/Config/Graphics/HacksWidget.cpp b/Source/Core/DolphinQt/Config/Graphics/HacksWidget.cpp index 19712e38f8..49a1c50b68 100644 --- a/Source/Core/DolphinQt/Config/Graphics/HacksWidget.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/HacksWidget.cpp @@ -15,6 +15,7 @@ #include "DolphinQt/Config/ConfigControls/ConfigBool.h" #include "DolphinQt/Config/ConfigControls/ConfigSlider.h" +#include "DolphinQt/Config/GameConfigWidget.h" #include "DolphinQt/Config/Graphics/GraphicsWindow.h" #include "DolphinQt/Config/ToolTipControls/ToolTipSlider.h" #include "DolphinQt/Settings.h" @@ -37,6 +38,14 @@ HacksWidget::HacksWidget(GraphicsWindow* parent) }); } +HacksWidget::HacksWidget(GameConfigWidget* parent, Config::Layer* layer) : m_game_layer(layer) +{ + CreateWidgets(); + LoadSettings(); + ConnectWidgets(); + AddDescriptions(); +} + void HacksWidget::CreateWidgets() { auto* main_layout = new QVBoxLayout; @@ -45,14 +54,14 @@ void HacksWidget::CreateWidgets() auto* efb_box = new QGroupBox(tr("Embedded Frame Buffer (EFB)")); auto* efb_layout = new QGridLayout(); efb_box->setLayout(efb_layout); - m_skip_efb_cpu = - new ConfigBool(tr("Skip EFB Access from CPU"), Config::GFX_HACK_EFB_ACCESS_ENABLE, true); - m_ignore_format_changes = new ConfigBool(tr("Ignore Format Changes"), - Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES, true); - m_store_efb_copies = - new ConfigBool(tr("Store EFB Copies to Texture Only"), Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM); - m_defer_efb_copies = - new ConfigBool(tr("Defer EFB Copies to RAM"), Config::GFX_HACK_DEFER_EFB_COPIES); + m_skip_efb_cpu = new ConfigBool(tr("Skip EFB Access from CPU"), + Config::GFX_HACK_EFB_ACCESS_ENABLE, m_game_layer, true); + m_ignore_format_changes = new ConfigBool( + tr("Ignore Format Changes"), Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES, m_game_layer, true); + m_store_efb_copies = new ConfigBool(tr("Store EFB Copies to Texture Only"), + Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM, m_game_layer); + m_defer_efb_copies = new ConfigBool(tr("Defer EFB Copies to RAM"), + Config::GFX_HACK_DEFER_EFB_COPIES, m_game_layer); efb_layout->addWidget(m_skip_efb_cpu, 0, 0); efb_layout->addWidget(m_ignore_format_changes, 0, 1); @@ -69,8 +78,8 @@ void HacksWidget::CreateWidgets() m_accuracy->setMaximum(2); m_accuracy->setPageStep(1); m_accuracy->setTickPosition(QSlider::TicksBelow); - m_gpu_texture_decoding = - new ConfigBool(tr("GPU Texture Decoding"), Config::GFX_ENABLE_GPU_TEXTURE_DECODING); + m_gpu_texture_decoding = new ConfigBool(tr("GPU Texture Decoding"), + Config::GFX_ENABLE_GPU_TEXTURE_DECODING, m_game_layer); auto* safe_label = new QLabel(tr("Safe")); safe_label->setAlignment(Qt::AlignRight); @@ -88,11 +97,12 @@ void HacksWidget::CreateWidgets() auto* xfb_layout = new QVBoxLayout(); xfb_box->setLayout(xfb_layout); - m_store_xfb_copies = - new ConfigBool(tr("Store XFB Copies to Texture Only"), Config::GFX_HACK_SKIP_XFB_COPY_TO_RAM); - m_immediate_xfb = new ConfigBool(tr("Immediately Present XFB"), Config::GFX_HACK_IMMEDIATE_XFB); - m_skip_duplicate_xfbs = - new ConfigBool(tr("Skip Presenting Duplicate Frames"), Config::GFX_HACK_SKIP_DUPLICATE_XFBS); + m_store_xfb_copies = new ConfigBool(tr("Store XFB Copies to Texture Only"), + Config::GFX_HACK_SKIP_XFB_COPY_TO_RAM, m_game_layer); + m_immediate_xfb = + new ConfigBool(tr("Immediately Present XFB"), Config::GFX_HACK_IMMEDIATE_XFB, m_game_layer); + m_skip_duplicate_xfbs = new ConfigBool(tr("Skip Presenting Duplicate Frames"), + Config::GFX_HACK_SKIP_DUPLICATE_XFBS, m_game_layer); xfb_layout->addWidget(m_store_xfb_copies); xfb_layout->addWidget(m_immediate_xfb); @@ -104,13 +114,14 @@ void HacksWidget::CreateWidgets() other_box->setLayout(other_layout); m_fast_depth_calculation = - new ConfigBool(tr("Fast Depth Calculation"), Config::GFX_FAST_DEPTH_CALC); + new ConfigBool(tr("Fast Depth Calculation"), Config::GFX_FAST_DEPTH_CALC, m_game_layer); m_disable_bounding_box = - new ConfigBool(tr("Disable Bounding Box"), Config::GFX_HACK_BBOX_ENABLE, true); - m_vertex_rounding = new ConfigBool(tr("Vertex Rounding"), Config::GFX_HACK_VERTEX_ROUNDING); - m_save_texture_cache_state = - new ConfigBool(tr("Save Texture Cache to State"), Config::GFX_SAVE_TEXTURE_CACHE_TO_STATE); - m_vi_skip = new ConfigBool(tr("VBI Skip"), Config::GFX_HACK_VI_SKIP); + new ConfigBool(tr("Disable Bounding Box"), Config::GFX_HACK_BBOX_ENABLE, m_game_layer, true); + m_vertex_rounding = + new ConfigBool(tr("Vertex Rounding"), Config::GFX_HACK_VERTEX_ROUNDING, m_game_layer); + m_save_texture_cache_state = new ConfigBool( + tr("Save Texture Cache to State"), Config::GFX_SAVE_TEXTURE_CACHE_TO_STATE, m_game_layer); + m_vi_skip = new ConfigBool(tr("VBI Skip"), Config::GFX_HACK_VI_SKIP, m_game_layer); other_layout->addWidget(m_fast_depth_calculation, 0, 0); other_layout->addWidget(m_disable_bounding_box, 0, 1); diff --git a/Source/Core/DolphinQt/Config/Graphics/HacksWidget.h b/Source/Core/DolphinQt/Config/Graphics/HacksWidget.h index 995a901ad4..391570278c 100644 --- a/Source/Core/DolphinQt/Config/Graphics/HacksWidget.h +++ b/Source/Core/DolphinQt/Config/Graphics/HacksWidget.h @@ -6,15 +6,22 @@ #include class ConfigBool; +class GameConfigWidget; class GraphicsWindow; class QLabel; class ToolTipSlider; +namespace Config +{ +class Layer; +} // namespace Config + class HacksWidget final : public QWidget { Q_OBJECT public: explicit HacksWidget(GraphicsWindow* parent); + HacksWidget(GameConfigWidget* parent, Config::Layer* layer); private: void LoadSettings(); @@ -45,6 +52,8 @@ private: ConfigBool* m_vi_skip; ConfigBool* m_save_texture_cache_state; + Config::Layer* m_game_layer = nullptr; + void CreateWidgets(); void ConnectWidgets(); void AddDescriptions(); diff --git a/Source/Core/DolphinQt/GameList/GameList.cpp b/Source/Core/DolphinQt/GameList/GameList.cpp index 64e9ae6774..6c276ec97b 100644 --- a/Source/Core/DolphinQt/GameList/GameList.cpp +++ b/Source/Core/DolphinQt/GameList/GameList.cpp @@ -546,13 +546,13 @@ void GameList::OpenProperties() return; PropertiesDialog* properties = new PropertiesDialog(this, *game); - // Since the properties dialog locks the game file, it's important to free it as soon as it's - // closed so that the file can be moved or deleted. - properties->setAttribute(Qt::WA_DeleteOnClose, true); connect(properties, &PropertiesDialog::OpenGeneralSettings, this, &GameList::OpenGeneralSettings); connect(properties, &PropertiesDialog::OpenGraphicsSettings, this, &GameList::OpenGraphicsSettings); + connect(properties, &PropertiesDialog::finished, this, + [properties]() { properties->deleteLater(); }); + #ifdef USE_RETRO_ACHIEVEMENTS connect(properties, &PropertiesDialog::OpenAchievementSettings, this, &GameList::OpenAchievementSettings); From ac129d318b02f6878352cb1c93d2ebf3542ace1c Mon Sep 17 00:00:00 2001 From: TryTwo Date: Mon, 21 Oct 2024 14:47:42 -0700 Subject: [PATCH 3/4] EnhancementsWidget:: Move to using ConfigControls and add new control for ComboBoxes that set two settings at once. --- .../Config/ConfigControls/ConfigChoice.cpp | 99 ++++ .../Config/ConfigControls/ConfigChoice.h | 27 ++ .../DolphinQt/Config/GameConfigWidget.cpp | 11 + .../Config/Graphics/EnhancementsWidget.cpp | 435 +++++++----------- .../Config/Graphics/EnhancementsWidget.h | 30 +- 5 files changed, 325 insertions(+), 277 deletions(-) diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.cpp index c2652563d1..b1c12a4172 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.cpp @@ -71,3 +71,102 @@ void ConfigStringChoice::OnConfigChanged() { Load(); } + +ConfigComplexChoice::ConfigComplexChoice(const InfoVariant setting1, const InfoVariant setting2, + Config::Layer* layer) + : m_setting1(setting1), m_setting2(setting2), m_layer(layer) +{ + connect(&Settings::Instance(), &Settings::ConfigChanged, this, &ConfigComplexChoice::Refresh); + connect(this, &QComboBox::currentIndexChanged, this, &ConfigComplexChoice::SaveValue); +} + +void ConfigComplexChoice::Refresh() +{ + auto& location = GetLocation(); + + QFont bf = font(); + if (m_layer != nullptr) + { + bf.setBold(m_layer->Exists(location.first) || m_layer->Exists(location.second)); + } + else + { + bf.setBold(Config::GetActiveLayerForConfig(location.first) != Config::LayerType::Base || + Config::GetActiveLayerForConfig(location.second) != Config::LayerType::Base); + } + + setFont(bf); + UpdateComboIndex(); +} + +void ConfigComplexChoice::Add(const QString& name, const OptionVariant option1, + const OptionVariant option2) +{ + const QSignalBlocker blocker(this); + addItem(name); + m_options.push_back(std::make_pair(option1, option2)); +} + +void ConfigComplexChoice::Reset() +{ + clear(); + m_options.clear(); +} + +void ConfigComplexChoice::SaveValue(int choice) +{ + auto Set = [this, choice](auto& setting, auto& value) { + if (m_layer != nullptr) + { + m_layer->Set(setting.GetLocation(), value); + Config::OnConfigChanged(); + return; + } + + Config::SetBaseOrCurrent(setting, value); + }; + + std::visit(Set, m_setting1, m_options[choice].first); + std::visit(Set, m_setting2, m_options[choice].second); +} + +void ConfigComplexChoice::UpdateComboIndex() +{ + auto Get = [this](auto& setting) { + if (m_layer != nullptr) + return static_cast(m_layer->Get(setting)); + + return static_cast(Config::Get(setting)); + }; + + std::pair values = + std::make_pair(std::visit(Get, m_setting1), std::visit(Get, m_setting2)); + + auto it = std::find(m_options.begin(), m_options.end(), values); + int index = static_cast(std::distance(m_options.begin(), it)); + + const QSignalBlocker blocker(this); + setCurrentIndex(index); +} + +const std::pair ConfigComplexChoice::GetLocation() const +{ + auto visit = [](auto& v) { return v.GetLocation(); }; + + return {std::visit(visit, m_setting1), std::visit(visit, m_setting2)}; +} + +void ConfigComplexChoice::mousePressEvent(QMouseEvent* event) +{ + if (event->button() == Qt::RightButton && m_layer != nullptr) + { + auto& location = GetLocation(); + m_layer->DeleteKey(location.first); + m_layer->DeleteKey(location.second); + Config::OnConfigChanged(); + } + else + { + QComboBox::mousePressEvent(event); + } +}; diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.h index fa2bdf6f88..8134487402 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.h @@ -51,3 +51,30 @@ private: const Config::Info& m_setting; bool m_text_is_data = false; }; + +class ConfigComplexChoice final : public ToolTipComboBox +{ + Q_OBJECT + + using InfoVariant = std::variant, Config::Info, Config::Info>; + using OptionVariant = std::variant; + +public: + ConfigComplexChoice(const InfoVariant setting1, const InfoVariant setting2, + Config::Layer* layer = nullptr); + + void Add(const QString& name, const OptionVariant option1, const OptionVariant option2); + void Refresh(); + void Reset(); + const std::pair GetLocation() const; + +private: + void SaveValue(int choice); + void UpdateComboIndex(); + void mousePressEvent(QMouseEvent* event) override; + + Config::Layer* m_layer; + const InfoVariant m_setting1; + const InfoVariant m_setting2; + std::vector> m_options; +}; diff --git a/Source/Core/DolphinQt/Config/GameConfigWidget.cpp b/Source/Core/DolphinQt/Config/GameConfigWidget.cpp index 30109f1b9c..c53e8e8379 100644 --- a/Source/Core/DolphinQt/Config/GameConfigWidget.cpp +++ b/Source/Core/DolphinQt/Config/GameConfigWidget.cpp @@ -363,4 +363,15 @@ void GameConfigWidget::SetItalics() italics(config); for (auto* config : findChildren()) italics(config); + + for (auto* config : findChildren()) + { + std::pair location = config->GetLocation(); + if (m_global_layer->Exists(location.first) || m_global_layer->Exists(location.second)) + { + QFont ifont = config->font(); + ifont.setItalic(true); + config->setFont(ifont); + } + } } diff --git a/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.cpp b/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.cpp index 3fd3ec42d8..9854a47b23 100644 --- a/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.cpp @@ -11,6 +11,8 @@ #include #include +#include "Common/CommonTypes.h" + #include "Core/Config/GraphicsSettings.h" #include "Core/ConfigManager.h" @@ -32,40 +34,43 @@ #include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoConfig.h" -EnhancementsWidget::EnhancementsWidget(GraphicsWindow* parent) : m_block_save(false) +EnhancementsWidget::EnhancementsWidget(GraphicsWindow* parent) { CreateWidgets(); - LoadSettings(); + LoadPPShaders(); ConnectWidgets(); AddDescriptions(); - connect(parent, &GraphicsWindow::BackendChanged, - [this](const QString& backend) { LoadSettings(); }); - connect(parent, &GraphicsWindow::UseFastTextureSamplingChanged, this, - &EnhancementsWidget::LoadSettings); - connect(parent, &GraphicsWindow::UseGPUTextureDecodingChanged, this, - &EnhancementsWidget::LoadSettings); + + // BackendChanged is called by parent on window creation. + connect(parent, &GraphicsWindow::BackendChanged, this, &EnhancementsWidget::OnBackendChanged); + connect(parent, &GraphicsWindow::UseFastTextureSamplingChanged, this, [this]() { + m_texture_filtering_combo->setEnabled(ReadSetting(Config::GFX_HACK_FAST_TEXTURE_SAMPLING)); + }); + connect(parent, &GraphicsWindow::UseGPUTextureDecodingChanged, this, [this]() { + m_arbitrary_mipmap_detection->setEnabled(!ReadSetting(Config::GFX_ENABLE_GPU_TEXTURE_DECODING)); + }); } EnhancementsWidget::EnhancementsWidget(GameConfigWidget* parent, Config::Layer* layer) : m_game_layer(layer) { CreateWidgets(); - LoadSettings(); + LoadPPShaders(); ConnectWidgets(); AddDescriptions(); + + connect(&Settings::Instance(), &Settings::ConfigChanged, this, + &EnhancementsWidget::OnConfigChanged); } -constexpr int TEXTURE_FILTERING_DEFAULT = 0; -constexpr int TEXTURE_FILTERING_ANISO_2X = 1; -constexpr int TEXTURE_FILTERING_ANISO_4X = 2; -constexpr int TEXTURE_FILTERING_ANISO_8X = 3; -constexpr int TEXTURE_FILTERING_ANISO_16X = 4; -constexpr int TEXTURE_FILTERING_FORCE_NEAREST = 5; -constexpr int TEXTURE_FILTERING_FORCE_LINEAR = 6; -constexpr int TEXTURE_FILTERING_FORCE_LINEAR_ANISO_2X = 7; -constexpr int TEXTURE_FILTERING_FORCE_LINEAR_ANISO_4X = 8; -constexpr int TEXTURE_FILTERING_FORCE_LINEAR_ANISO_8X = 9; -constexpr int TEXTURE_FILTERING_FORCE_LINEAR_ANISO_16X = 10; +constexpr int ANISO_DEFAULT = 0; +constexpr int ANISO_2X = 1; +constexpr int ANISO_4X = 2; +constexpr int ANISO_8X = 3; +constexpr int ANISO_16X = 4; +constexpr int FILTERING_DEFAULT = 0; +constexpr int FILTERING_NEAREST = 1; +constexpr int FILTERING_LINEAR = 2; void EnhancementsWidget::CreateWidgets() { @@ -93,7 +98,7 @@ void EnhancementsWidget::CreateWidgets() // If the current scale is greater than the max scale in the ini, add sufficient options so that // when the settings are saved we don't lose the user-modified value from the ini. const int max_efb_scale = - std::max(Config::Get(Config::GFX_EFB_SCALE), Config::Get(Config::GFX_MAX_EFB_SCALE)); + std::max(ReadSetting(Config::GFX_EFB_SCALE), ReadSetting(Config::GFX_MAX_EFB_SCALE)); for (int scale = static_cast(resolution_options.size()); scale <= max_efb_scale; scale++) { const QString scale_text = QString::number(scale); @@ -117,45 +122,40 @@ void EnhancementsWidget::CreateWidgets() m_ir_combo = new ConfigChoice(resolution_options, Config::GFX_EFB_SCALE, m_game_layer); m_ir_combo->setMaxVisibleItems(visible_resolution_option_count); - m_aa_combo = new ToolTipComboBox(); + m_aa_combo = new ConfigComplexChoice(Config::GFX_MSAA, Config::GFX_SSAA, m_game_layer); + m_aa_combo->Add(tr("None"), (u32)1, false); - m_texture_filtering_combo = new ToolTipComboBox(); - m_texture_filtering_combo->addItem(tr("Default"), TEXTURE_FILTERING_DEFAULT); - m_texture_filtering_combo->addItem(tr("2x Anisotropic"), TEXTURE_FILTERING_ANISO_2X); - m_texture_filtering_combo->addItem(tr("4x Anisotropic"), TEXTURE_FILTERING_ANISO_4X); - m_texture_filtering_combo->addItem(tr("8x Anisotropic"), TEXTURE_FILTERING_ANISO_8X); - m_texture_filtering_combo->addItem(tr("16x Anisotropic"), TEXTURE_FILTERING_ANISO_16X); - m_texture_filtering_combo->addItem(tr("Force Nearest"), TEXTURE_FILTERING_FORCE_NEAREST); - m_texture_filtering_combo->addItem(tr("Force Linear"), TEXTURE_FILTERING_FORCE_LINEAR); - m_texture_filtering_combo->addItem(tr("Force Linear and 2x Anisotropic"), - TEXTURE_FILTERING_FORCE_LINEAR_ANISO_2X); - m_texture_filtering_combo->addItem(tr("Force Linear and 4x Anisotropic"), - TEXTURE_FILTERING_FORCE_LINEAR_ANISO_4X); - m_texture_filtering_combo->addItem(tr("Force Linear and 8x Anisotropic"), - TEXTURE_FILTERING_FORCE_LINEAR_ANISO_8X); - m_texture_filtering_combo->addItem(tr("Force Linear and 16x Anisotropic"), - TEXTURE_FILTERING_FORCE_LINEAR_ANISO_16X); + m_texture_filtering_combo = + new ConfigComplexChoice(Config::GFX_ENHANCE_MAX_ANISOTROPY, + Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING, m_game_layer); - m_output_resampling_combo = new ToolTipComboBox(); - m_output_resampling_combo->addItem(tr("Default"), - static_cast(OutputResamplingMode::Default)); - m_output_resampling_combo->addItem(tr("Bilinear"), - static_cast(OutputResamplingMode::Bilinear)); - m_output_resampling_combo->addItem(tr("Bicubic: B-Spline"), - static_cast(OutputResamplingMode::BSpline)); - m_output_resampling_combo->addItem(tr("Bicubic: Mitchell-Netravali"), - static_cast(OutputResamplingMode::MitchellNetravali)); - m_output_resampling_combo->addItem(tr("Bicubic: Catmull-Rom"), - static_cast(OutputResamplingMode::CatmullRom)); - m_output_resampling_combo->addItem(tr("Sharp Bilinear"), - static_cast(OutputResamplingMode::SharpBilinear)); - m_output_resampling_combo->addItem(tr("Area Sampling"), - static_cast(OutputResamplingMode::AreaSampling)); + m_texture_filtering_combo->Add(tr("Default"), ANISO_DEFAULT, FILTERING_DEFAULT); + m_texture_filtering_combo->Add(tr("2x Anisotropic"), ANISO_2X, FILTERING_DEFAULT); + m_texture_filtering_combo->Add(tr("4x Anisotropic"), ANISO_4X, FILTERING_DEFAULT); + m_texture_filtering_combo->Add(tr("8x Anisotropic"), ANISO_8X, FILTERING_DEFAULT); + m_texture_filtering_combo->Add(tr("16x Anisotropic"), ANISO_16X, FILTERING_DEFAULT); + m_texture_filtering_combo->Add(tr("Force Nearest"), ANISO_DEFAULT, FILTERING_NEAREST); + m_texture_filtering_combo->Add(tr("Force Linear"), ANISO_DEFAULT, FILTERING_LINEAR); + m_texture_filtering_combo->Add(tr("Force Linear and 2x Anisotropic"), ANISO_2X, FILTERING_LINEAR); + m_texture_filtering_combo->Add(tr("Force Linear and 4x Anisotropic"), ANISO_4X, FILTERING_LINEAR); + m_texture_filtering_combo->Add(tr("Force Linear and 8x Anisotropic"), ANISO_8X, FILTERING_LINEAR); + m_texture_filtering_combo->Add(tr("Force Linear and 16x Anisotropic"), ANISO_16X, + FILTERING_LINEAR); + m_texture_filtering_combo->Refresh(); + m_texture_filtering_combo->setEnabled(ReadSetting(Config::GFX_HACK_FAST_TEXTURE_SAMPLING)); + + m_output_resampling_combo = new ConfigChoice( + {tr("Default"), tr("Bilinear"), tr("Bicubic: B-Spline"), tr("Bicubic: Mitchell-Netravali"), + tr("Bicubic: Catmull-Rom"), tr("Sharp Bilinear"), tr("Area Sampling")}, + Config::GFX_ENHANCE_OUTPUT_RESAMPLING, m_game_layer); m_configure_color_correction = new ToolTipPushButton(tr("Configure")); - m_pp_effect = new ToolTipComboBox(); + m_pp_effect = new ConfigStringChoice(VideoCommon::PostProcessing::GetShaderList(), + Config::GFX_ENHANCE_POST_SHADER, m_game_layer); m_configure_pp_effect = new NonDefaultQPushButton(tr("Configure")); + m_configure_pp_effect->setDisabled(true); + m_scaled_efb_copy = new ConfigBool(tr("Scaled EFB Copy"), Config::GFX_HACK_COPY_EFB_SCALED, m_game_layer); m_per_pixel_lighting = @@ -171,6 +171,7 @@ void EnhancementsWidget::CreateWidgets() m_arbitrary_mipmap_detection = new ConfigBool(tr("Arbitrary Mipmap Detection"), Config::GFX_ENHANCE_ARBITRARY_MIPMAP_DETECTION, m_game_layer); + m_arbitrary_mipmap_detection->setEnabled(!ReadSetting(Config::GFX_ENABLE_GPU_TEXTURE_DECODING)); m_hdr = new ConfigBool(tr("HDR Post-Processing"), Config::GFX_ENHANCE_HDR_OUTPUT, m_game_layer); int row = 0; @@ -242,7 +243,7 @@ void EnhancementsWidget::CreateWidgets() stereoscopy_layout->addWidget(m_3d_swap_eyes, 3, 0); stereoscopy_layout->addWidget(m_3d_per_eye_resolution, 4, 0); - auto current_stereo_mode = Config::Get(Config::GFX_STEREO_MODE); + auto current_stereo_mode = ReadSetting(Config::GFX_STEREO_MODE); if (current_stereo_mode != StereoMode::SBS && current_stereo_mode != StereoMode::TAB) m_3d_per_eye_resolution->hide(); @@ -255,58 +256,53 @@ void EnhancementsWidget::CreateWidgets() void EnhancementsWidget::ConnectWidgets() { - connect(m_aa_combo, &QComboBox::currentIndexChanged, [this](int) { SaveSettings(); }); - connect(m_texture_filtering_combo, &QComboBox::currentIndexChanged, - [this](int) { SaveSettings(); }); - connect(m_output_resampling_combo, &QComboBox::currentIndexChanged, - [this](int) { SaveSettings(); }); - connect(m_pp_effect, &QComboBox::currentIndexChanged, [this](int) { SaveSettings(); }); connect(m_3d_mode, &QComboBox::currentIndexChanged, [this] { - m_block_save = true; - m_configure_color_correction->setEnabled(g_Config.backend_info.bSupportsPostProcessing); - - auto current_stereo_mode = Config::Get(Config::GFX_STEREO_MODE); - LoadPPShaders(current_stereo_mode); + auto current_stereo_mode = ReadSetting(Config::GFX_STEREO_MODE); + LoadPPShaders(); if (current_stereo_mode == StereoMode::SBS || current_stereo_mode == StereoMode::TAB) m_3d_per_eye_resolution->show(); else m_3d_per_eye_resolution->hide(); - - m_block_save = false; - SaveSettings(); }); + + connect(m_pp_effect, &QComboBox::currentIndexChanged, this, &EnhancementsWidget::ShaderChanged); + connect(m_configure_color_correction, &QPushButton::clicked, this, &EnhancementsWidget::ConfigureColorCorrection); connect(m_configure_pp_effect, &QPushButton::clicked, this, &EnhancementsWidget::ConfigurePostProcessingShader); - - connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this] { - const QSignalBlocker blocker(this); - m_block_save = true; - LoadPPShaders(Config::Get(Config::GFX_STEREO_MODE)); - m_block_save = false; - }); } -void EnhancementsWidget::LoadPPShaders(StereoMode stereo_mode) +template +T EnhancementsWidget::ReadSetting(const Config::Info& setting) const { - std::vector shaders = VideoCommon::PostProcessing::GetShaderList(); - if (stereo_mode == StereoMode::Anaglyph) - { - shaders = VideoCommon::PostProcessing::GetAnaglyphShaderList(); - } - else if (stereo_mode == StereoMode::Passive) - { - shaders = VideoCommon::PostProcessing::GetPassiveShaderList(); - } + if (m_game_layer != nullptr) + return m_game_layer->Get(setting); + else + return Config::Get(setting); +} +void EnhancementsWidget::LoadPPShaders() +{ + auto stereo_mode = ReadSetting(Config::GFX_STEREO_MODE); + + const QSignalBlocker blocker(m_pp_effect); m_pp_effect->clear(); + // Get shader list + std::vector shaders = VideoCommon::PostProcessing::GetShaderList(); + + if (stereo_mode == StereoMode::Anaglyph) + shaders = VideoCommon::PostProcessing::GetAnaglyphShaderList(); + else if (stereo_mode == StereoMode::Passive) + shaders = VideoCommon::PostProcessing::GetPassiveShaderList(); + + // Populate widget if (stereo_mode != StereoMode::Anaglyph && stereo_mode != StereoMode::Passive) m_pp_effect->addItem(tr("(off)")); - auto selected_shader = Config::Get(Config::GFX_ENHANCE_POST_SHADER); + auto selected_shader = ReadSetting(Config::GFX_ENHANCE_POST_SHADER); bool found = false; @@ -320,6 +316,7 @@ void EnhancementsWidget::LoadPPShaders(StereoMode stereo_mode) } } + // Force a shader for StereoModes that require it. if (!found) { if (stereo_mode == StereoMode::Anaglyph) @@ -335,103 +332,20 @@ void EnhancementsWidget::LoadPPShaders(StereoMode stereo_mode) else m_pp_effect->setCurrentIndex(0); - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_POST_SHADER, selected_shader); + // Save forced shader, but avoid forcing an option into a game ini layer. + if (m_game_layer == nullptr && ReadSetting(Config::GFX_ENHANCE_POST_SHADER) != selected_shader) + Config::SetBaseOrCurrent(Config::GFX_ENHANCE_POST_SHADER, selected_shader); } - const bool supports_postprocessing = g_Config.backend_info.bSupportsPostProcessing; - m_pp_effect->setEnabled(supports_postprocessing); - - m_pp_effect->setToolTip(supports_postprocessing ? - QString{} : - tr("%1 doesn't support this feature.") - .arg(tr(g_video_backend->GetDisplayName().c_str()))); - - VideoCommon::PostProcessingConfiguration pp_shader; - if (selected_shader != "" && supports_postprocessing) - { - pp_shader.LoadShader(selected_shader); - m_configure_pp_effect->setEnabled(pp_shader.HasOptions()); - } - else - { - m_configure_pp_effect->setEnabled(false); - } + m_pp_effect->Load(); + ShaderChanged(); } -void EnhancementsWidget::LoadSettings() +void EnhancementsWidget::OnBackendChanged() { - m_block_save = true; - m_texture_filtering_combo->setEnabled(Config::Get(Config::GFX_HACK_FAST_TEXTURE_SAMPLING)); - m_arbitrary_mipmap_detection->setEnabled(!Config::Get(Config::GFX_ENABLE_GPU_TEXTURE_DECODING)); - - // Anti-Aliasing - - const u32 aa_selection = Config::Get(Config::GFX_MSAA); - const bool ssaa = Config::Get(Config::GFX_SSAA); - const int aniso = Config::Get(Config::GFX_ENHANCE_MAX_ANISOTROPY); - const TextureFilteringMode tex_filter_mode = - Config::Get(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING); - - m_aa_combo->clear(); - - for (const u32 aa_mode : g_Config.backend_info.AAModes) - { - if (aa_mode == 1) - m_aa_combo->addItem(tr("None"), 1); - else - m_aa_combo->addItem(tr("%1x MSAA").arg(aa_mode), static_cast(aa_mode)); - - if (aa_mode == aa_selection && !ssaa) - m_aa_combo->setCurrentIndex(m_aa_combo->count() - 1); - } - if (g_Config.backend_info.bSupportsSSAA) - { - for (const u32 aa_mode : g_Config.backend_info.AAModes) - { - if (aa_mode != 1) // don't show "None" twice - { - // Mark SSAA using negative values in the variant - m_aa_combo->addItem(tr("%1x SSAA").arg(aa_mode), -static_cast(aa_mode)); - if (aa_mode == aa_selection && ssaa) - m_aa_combo->setCurrentIndex(m_aa_combo->count() - 1); - } - } - } - - m_aa_combo->setEnabled(m_aa_combo->count() > 1); - - switch (tex_filter_mode) - { - case TextureFilteringMode::Default: - if (aniso >= 0 && aniso <= 4) - m_texture_filtering_combo->setCurrentIndex(aniso); - else - m_texture_filtering_combo->setCurrentIndex(TEXTURE_FILTERING_DEFAULT); - break; - case TextureFilteringMode::Nearest: - m_texture_filtering_combo->setCurrentIndex(TEXTURE_FILTERING_FORCE_NEAREST); - break; - case TextureFilteringMode::Linear: - if (aniso >= 0 && aniso <= 4) - m_texture_filtering_combo->setCurrentIndex(TEXTURE_FILTERING_FORCE_LINEAR + aniso); - else - m_texture_filtering_combo->setCurrentIndex(TEXTURE_FILTERING_FORCE_LINEAR); - break; - } - - // Resampling - const OutputResamplingMode output_resampling_mode = - Config::Get(Config::GFX_ENHANCE_OUTPUT_RESAMPLING); - m_output_resampling_combo->setCurrentIndex( - m_output_resampling_combo->findData(static_cast(output_resampling_mode))); - m_output_resampling_combo->setEnabled(g_Config.backend_info.bSupportsPostProcessing); - - // Color Correction m_configure_color_correction->setEnabled(g_Config.backend_info.bSupportsPostProcessing); - - // Post Processing Shader - LoadPPShaders(Config::Get(Config::GFX_STEREO_MODE)); + m_hdr->setEnabled(g_Config.backend_info.bSupportsHDROutput); // Stereoscopy const bool supports_stereoscopy = g_Config.backend_info.bSupportsGeometryShaders; @@ -440,105 +354,98 @@ void EnhancementsWidget::LoadSettings() m_3d_depth->setEnabled(supports_stereoscopy); m_3d_swap_eyes->setEnabled(supports_stereoscopy); - m_hdr->setEnabled(g_Config.backend_info.bSupportsHDROutput); - - m_block_save = false; -} - -void EnhancementsWidget::SaveSettings() -{ - if (m_block_save) - return; - - const u32 aa_value = static_cast(std::abs(m_aa_combo->currentData().toInt())); - const bool is_ssaa = m_aa_combo->currentData().toInt() < 0; - - Config::SetBaseOrCurrent(Config::GFX_MSAA, aa_value); - Config::SetBaseOrCurrent(Config::GFX_SSAA, is_ssaa); - - const int texture_filtering_selection = m_texture_filtering_combo->currentData().toInt(); - switch (texture_filtering_selection) + // PostProcessing + const bool supports_postprocessing = g_Config.backend_info.bSupportsPostProcessing; + if (!supports_postprocessing) { - case TEXTURE_FILTERING_DEFAULT: - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 0); - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING, - TextureFilteringMode::Default); - break; - case TEXTURE_FILTERING_ANISO_2X: - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 1); - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING, - TextureFilteringMode::Default); - break; - case TEXTURE_FILTERING_ANISO_4X: - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 2); - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING, - TextureFilteringMode::Default); - break; - case TEXTURE_FILTERING_ANISO_8X: - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 3); - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING, - TextureFilteringMode::Default); - break; - case TEXTURE_FILTERING_ANISO_16X: - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 4); - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING, - TextureFilteringMode::Default); - break; - case TEXTURE_FILTERING_FORCE_NEAREST: - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 0); - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING, - TextureFilteringMode::Nearest); - break; - case TEXTURE_FILTERING_FORCE_LINEAR: - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 0); - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING, - TextureFilteringMode::Linear); - break; - case TEXTURE_FILTERING_FORCE_LINEAR_ANISO_2X: - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 1); - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING, - TextureFilteringMode::Linear); - break; - case TEXTURE_FILTERING_FORCE_LINEAR_ANISO_4X: - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 2); - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING, - TextureFilteringMode::Linear); - break; - case TEXTURE_FILTERING_FORCE_LINEAR_ANISO_8X: - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 3); - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING, - TextureFilteringMode::Linear); - break; - case TEXTURE_FILTERING_FORCE_LINEAR_ANISO_16X: - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 4); - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING, - TextureFilteringMode::Linear); - break; + m_configure_pp_effect->setEnabled(false); + m_pp_effect->setEnabled(false); + m_pp_effect->setToolTip( + tr("%1 doesn't support this feature.").arg(tr(g_video_backend->GetDisplayName().c_str()))); + } + else if (!m_pp_effect->isEnabled() && supports_postprocessing) + { + m_configure_pp_effect->setEnabled(true); + m_pp_effect->setEnabled(true); + m_pp_effect->setToolTip(QString{}); + LoadPPShaders(); } - const int output_resampling_selection = m_output_resampling_combo->currentData().toInt(); - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_OUTPUT_RESAMPLING, - static_cast(output_resampling_selection)); + UpdateAAOptions(); +} - const bool anaglyph = g_Config.stereo_mode == StereoMode::Anaglyph; - const bool passive = g_Config.stereo_mode == StereoMode::Passive; - Config::SetBaseOrCurrent(Config::GFX_ENHANCE_POST_SHADER, - (!anaglyph && !passive && m_pp_effect->currentIndex() == 0) ? - "" : - m_pp_effect->currentText().toStdString()); +void EnhancementsWidget::ShaderChanged() +{ + auto shader = ReadSetting(Config::GFX_ENHANCE_POST_SHADER); - VideoCommon::PostProcessingConfiguration pp_shader; - if (Config::Get(Config::GFX_ENHANCE_POST_SHADER) != "") + if (shader == "(off)" || shader == "") { - pp_shader.LoadShader(Config::Get(Config::GFX_ENHANCE_POST_SHADER)); + shader = ""; + + // Setting a shader to null in a game ini could be confusing, as it won't be bolded. Remove it + // instead. + if (m_game_layer != nullptr) + m_game_layer->DeleteKey(Config::GFX_ENHANCE_POST_SHADER.GetLocation()); + else + Config::SetBaseOrCurrent(Config::GFX_ENHANCE_POST_SHADER, shader); + } + + if (shader != "" && m_pp_effect->isEnabled()) + { + VideoCommon::PostProcessingConfiguration pp_shader; + pp_shader.LoadShader(shader); m_configure_pp_effect->setEnabled(pp_shader.HasOptions()); } else { m_configure_pp_effect->setEnabled(false); } +} - LoadSettings(); +void EnhancementsWidget::OnConfigChanged() +{ + // Only used for the GameConfigWidget. Bypasses graphics window signals and backend info due to it + // being global. + m_texture_filtering_combo->setEnabled(ReadSetting(Config::GFX_HACK_FAST_TEXTURE_SAMPLING)); + m_arbitrary_mipmap_detection->setEnabled(!ReadSetting(Config::GFX_ENABLE_GPU_TEXTURE_DECODING)); + UpdateAAOptions(); + + // Needs to update after deleting a key for 3d settings. + LoadPPShaders(); +} + +void EnhancementsWidget::UpdateAAOptions() +{ + const QSignalBlocker blocker_aa(m_aa_combo); + + m_aa_combo->Reset(); + m_aa_combo->Add(tr("None"), (u32)1, false); + + std::vector aa_modes = g_Config.backend_info.AAModes; + for (const u32 aa_mode : aa_modes) + { + if (aa_mode > 1) + m_aa_combo->Add(tr("%1x MSAA").arg(aa_mode), aa_mode, false); + } + + if (g_Config.backend_info.bSupportsSSAA) + { + for (const u32 aa_mode : aa_modes) + { + if (aa_mode > 1) + m_aa_combo->Add(tr("%1x SSAA").arg(aa_mode), aa_mode, true); + } + } + + m_aa_combo->Refresh(); + + // Backend info can't be populated in the local game settings window. Only enable local game AA + // edits when the backend info is correct - global and local have the same backend. + const bool good_info = + m_game_layer == nullptr || !m_game_layer->Exists(Config::MAIN_GFX_BACKEND.GetLocation()) || + Config::Get(Config::MAIN_GFX_BACKEND) == m_game_layer->Get(Config::MAIN_GFX_BACKEND); + + m_aa_combo->setEnabled(m_aa_combo->count() > 1 && good_info); } void EnhancementsWidget::AddDescriptions() @@ -727,7 +634,7 @@ void EnhancementsWidget::ConfigureColorCorrection() void EnhancementsWidget::ConfigurePostProcessingShader() { - const std::string shader = Config::Get(Config::GFX_ENHANCE_POST_SHADER); + const std::string shader = ReadSetting(Config::GFX_ENHANCE_POST_SHADER); PostProcessingConfigWindow dialog(this, shader); SetQWidgetWindowDecorations(&dialog); dialog.exec(); diff --git a/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.h b/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.h index f7922ee424..b3eeca785b 100644 --- a/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.h +++ b/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.h @@ -9,19 +9,19 @@ class ConfigBool; class ConfigChoice; +class ConfigComplexChoice; +class ConfigStringChoice; class ConfigSlider; class GameConfigWidget; class GraphicsWindow; -class QCheckBox; -class QComboBox; class QPushButton; -class QSlider; -class ToolTipComboBox; class ToolTipPushButton; enum class StereoMode : int; namespace Config { +template +class Info; class Layer; } // namespace Config @@ -33,22 +33,28 @@ public: EnhancementsWidget(GameConfigWidget* parent, Config::Layer* layer); private: - void LoadSettings(); - void SaveSettings(); + template + T ReadSetting(const Config::Info& setting) const; void CreateWidgets(); void ConnectWidgets(); void AddDescriptions(); + + void OnBackendChanged(); + void UpdateAAOptions(); + void LoadPPShaders(); + void ShaderChanged(); + void OnConfigChanged(); + void ConfigureColorCorrection(); void ConfigurePostProcessingShader(); - void LoadPPShaders(StereoMode stereo_mode); // Enhancements ConfigChoice* m_ir_combo; - ToolTipComboBox* m_aa_combo; - ToolTipComboBox* m_texture_filtering_combo; - ToolTipComboBox* m_output_resampling_combo; - ToolTipComboBox* m_pp_effect; + ConfigComplexChoice* m_aa_combo; + ConfigComplexChoice* m_texture_filtering_combo; + ConfigChoice* m_output_resampling_combo; + ConfigStringChoice* m_pp_effect; ToolTipPushButton* m_configure_color_correction; QPushButton* m_configure_pp_effect; ConfigBool* m_scaled_efb_copy; @@ -68,6 +74,4 @@ private: ConfigBool* m_3d_per_eye_resolution; Config::Layer* m_game_layer = nullptr; - int m_msaa_modes; - bool m_block_save; }; From 9541bb6cf7f966cc9fefaaa4ef2bc4d45babed22 Mon Sep 17 00:00:00 2001 From: TryTwo Date: Sun, 29 Sep 2024 17:12:22 -0700 Subject: [PATCH 4/4] Add method to bold slider/spin labels when a user game ini setting is being used --- .../Config/ConfigControls/ConfigInteger.cpp | 10 ++++++++++ .../Config/ConfigControls/ConfigInteger.h | 14 ++++++++++++++ .../Config/ConfigControls/ConfigSlider.cpp | 13 +++++++++++++ .../DolphinQt/Config/ConfigControls/ConfigSlider.h | 14 ++++++++++++++ Source/Core/DolphinQt/Config/GameConfigWidget.cpp | 6 ++++-- .../Config/Graphics/EnhancementsWidget.cpp | 4 ++-- 6 files changed, 57 insertions(+), 4 deletions(-) diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.cpp index 8d724505e3..a81bfd7aed 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.cpp @@ -29,3 +29,13 @@ void ConfigInteger::OnConfigChanged() { setValue(ReadValue(m_setting)); } + +ConfigIntegerLabel::ConfigIntegerLabel(const QString& text, ConfigInteger* widget) + : QLabel(text), m_widget(QPointer(widget)) +{ + connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this]() { + // Label shares font changes with ConfigInteger to mark game ini settings. + if (m_widget) + setFont(m_widget->font()); + }); +} diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.h index 2979423f74..9a0e718279 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.h @@ -3,6 +3,9 @@ #pragma once +#include +#include + #include "DolphinQt/Config/ConfigControls/ConfigControl.h" #include "DolphinQt/Config/ToolTipControls/ToolTipSpinBox.h" @@ -28,3 +31,14 @@ protected: private: const Config::Info& m_setting; }; + +class ConfigIntegerLabel final : public QLabel +{ + Q_OBJECT + +public: + ConfigIntegerLabel(const QString& text, ConfigInteger* widget); + +private: + QPointer m_widget; +}; diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.cpp index 9192410e03..a296d57e86 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.cpp @@ -1,7 +1,10 @@ // Copyright 2017 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #include "DolphinQt/Config/ConfigControls/ConfigSlider.h" +#include "DolphinQt/Settings.h" ConfigSlider::ConfigSlider(int minimum, int maximum, const Config::Info& setting, int tick) : ConfigSlider(minimum, maximum, setting, nullptr, tick) @@ -30,3 +33,13 @@ void ConfigSlider::OnConfigChanged() { setValue(ReadValue(m_setting)); } + +ConfigSliderLabel::ConfigSliderLabel(const QString& text, ConfigSlider* slider) + : QLabel(text), m_slider(QPointer(slider)) +{ + connect(&Settings::Instance(), &Settings::ConfigChanged, this, [this]() { + // Label shares font changes with slider to mark game ini settings. + if (m_slider) + setFont(m_slider->font()); + }); +} diff --git a/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.h index 7ac81d03d5..7d10dcafb2 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.h @@ -3,6 +3,9 @@ #pragma once +#include +#include + #include "DolphinQt/Config/ConfigControls/ConfigControl.h" #include "DolphinQt/Config/ToolTipControls/ToolTipSlider.h" @@ -28,3 +31,14 @@ protected: private: const Config::Info& m_setting; }; + +class ConfigSliderLabel final : public QLabel +{ + Q_OBJECT + +public: + ConfigSliderLabel(const QString& text, ConfigSlider* slider); + +private: + QPointer m_slider; +}; diff --git a/Source/Core/DolphinQt/Config/GameConfigWidget.cpp b/Source/Core/DolphinQt/Config/GameConfigWidget.cpp index c53e8e8379..99d7b45cce 100644 --- a/Source/Core/DolphinQt/Config/GameConfigWidget.cpp +++ b/Source/Core/DolphinQt/Config/GameConfigWidget.cpp @@ -152,9 +152,11 @@ void GameConfigWidget::CreateWidgets() m_use_monoscopic_shadows->setToolTip( tr("Use a single depth buffer for both eyes. Needed for a few games.")); - stereoscopy_layout->addWidget(new QLabel(tr("Depth Percentage:")), 0, 0); + stereoscopy_layout->addWidget(new ConfigSliderLabel(tr("Depth Percentage:"), m_depth_slider), 0, + 0); stereoscopy_layout->addWidget(m_depth_slider, 0, 1); - stereoscopy_layout->addWidget(new QLabel(tr("Convergence:")), 1, 0); + stereoscopy_layout->addWidget(new ConfigIntegerLabel(tr("Convergence:"), m_convergence_spin), 1, + 0); stereoscopy_layout->addWidget(m_convergence_spin, 1, 1); stereoscopy_layout->addWidget(m_use_monoscopic_shadows, 2, 0); diff --git a/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.cpp b/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.cpp index 9854a47b23..f04d8fec2a 100644 --- a/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.cpp +++ b/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.cpp @@ -236,9 +236,9 @@ void EnhancementsWidget::CreateWidgets() stereoscopy_layout->addWidget(new QLabel(tr("Stereoscopic 3D Mode:")), 0, 0); stereoscopy_layout->addWidget(m_3d_mode, 0, 1); - stereoscopy_layout->addWidget(new QLabel(tr("Depth:")), 1, 0); + stereoscopy_layout->addWidget(new ConfigSliderLabel(tr("Depth:"), m_3d_depth), 1, 0); stereoscopy_layout->addWidget(m_3d_depth, 1, 1); - stereoscopy_layout->addWidget(new QLabel(tr("Convergence:")), 2, 0); + stereoscopy_layout->addWidget(new ConfigSliderLabel(tr("Convergence:"), m_3d_convergence), 2, 0); stereoscopy_layout->addWidget(m_3d_convergence, 2, 1); stereoscopy_layout->addWidget(m_3d_swap_eyes, 3, 0); stereoscopy_layout->addWidget(m_3d_per_eye_resolution, 4, 0);