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..4fad525e6f 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigBool.cpp @@ -3,31 +3,28 @@ #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) + : 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); + 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..304f1a9b1f 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,11 +12,16 @@ 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); + ConfigBool(const QString& label, const Config::Info& setting, Config::Layer* layer, + 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..b1c12a4172 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.cpp @@ -5,85 +5,168 @@ #include -#include "Common/Config/Config.h" - -#include "DolphinQt/Settings.h" - -ConfigChoice::ConfigChoice(const QStringList& options, const Config::Info& setting) - : 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)); + 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) + 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)); - 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) + 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); - 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); + + // This can be called publicly. + const QSignalBlocker block(this); + setCurrentIndex(index); +} + +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 0082605604..8134487402 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigChoice.h @@ -7,15 +7,24 @@ #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); + ConfigChoice(const QStringList& options, const Config::Info& setting, + Config::Layer* layer = nullptr); + +protected: + void OnConfigChanged() override; private: void Update(int choice); @@ -23,20 +32,49 @@ private: Config::Info m_setting; }; -class ConfigStringChoice : public ToolTipComboBox +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); - -private: - void Connect(); - void Update(int index); + const Config::Info& setting, Config::Layer* layer = nullptr); void Load(); - Config::Info m_setting; +protected: + void OnConfigChanged() override; + +private: + void Update(int index); + + 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/ConfigControls/ConfigControl.h b/Source/Core/DolphinQt/Config/ConfigControls/ConfigControl.h new file mode 100644 index 0000000000..1be0a8fa19 --- /dev/null +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigControl.h @@ -0,0 +1,105 @@ +// Copyright 2024 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include + +#include "Common/Config/Enums.h" +#include "Common/Config/Layer.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, Config::Layer* layer) + : m_location(location), m_layer(layer) + { + ConnectConfig(); + } + 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(); + } + + 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) + { + 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(){}; + +private: + bool IsConfigLocal() const + { + 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 3f7bed01c8..e5211d00bb 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigFloatSlider.cpp @@ -3,20 +3,16 @@ #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) + 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; 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 +20,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..f68b018379 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,16 +14,20 @@ 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: - 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 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..a81bfd7aed 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.cpp @@ -3,33 +3,39 @@ #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) + : 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); 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)); +} + +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 35b25986b7..9a0e718279 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigInteger.h @@ -3,6 +3,10 @@ #pragma once +#include +#include + +#include "DolphinQt/Config/ConfigControls/ConfigControl.h" #include "DolphinQt/Config/ToolTipControls/ToolTipSpinBox.h" namespace Config @@ -11,13 +15,30 @@ 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); + ConfigInteger(int minimum, int maximum, const Config::Info& setting, Config::Layer* layer, + int step = 1); + void Update(int value); +protected: + void OnConfigChanged() override; + 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/ConfigRadio.cpp b/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.cpp index 11a049d910..eba9de283c 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.cpp @@ -3,33 +3,21 @@ #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) +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(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 +25,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..c6b0e8835c 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigRadio.h @@ -3,15 +3,21 @@ #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: - 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. @@ -19,9 +25,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..a296d57e86 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.cpp +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.cpp @@ -1,36 +1,45 @@ // Copyright 2017 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include + #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) + : 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); 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)); +} + +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 a14d06291e..7d10dcafb2 100644 --- a/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.h +++ b/Source/Core/DolphinQt/Config/ConfigControls/ConfigSlider.h @@ -3,6 +3,10 @@ #pragma once +#include +#include + +#include "DolphinQt/Config/ConfigControls/ConfigControl.h" #include "DolphinQt/Config/ToolTipControls/ToolTipSlider.h" namespace Config @@ -11,13 +15,30 @@ 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); + ConfigSlider(int minimum, int maximum, const Config::Info& setting, Config::Layer* layer, + int tick = 0); + void Update(int value); +protected: + void OnConfigChanged() override; + 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 ce56a357be..99d7b45cce 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.")); @@ -140,37 +152,23 @@ 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); - 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 +194,186 @@ 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; + 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); - switch (determinism_num) + for (auto* config : findChildren()) { - 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)) + std::pair location = config->GetLocation(); + if (m_global_layer->Exists(location.first) || m_global_layer->Exists(location.second)) { - m_gameini_local.GetOrCreateSection("Core")->Set("GPUDeterminismMode", determinism_mode); + QFont ifont = config->font(); + ifont.setItalic(true); + config->setFont(ifont); } } - 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()); } 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..f04d8fec2a 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" @@ -18,6 +20,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" @@ -31,31 +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)); + }); } -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; +EnhancementsWidget::EnhancementsWidget(GameConfigWidget* parent, Config::Layer* layer) + : m_game_layer(layer) +{ + CreateWidgets(); + LoadPPShaders(); + ConnectWidgets(); + AddDescriptions(); + + connect(&Settings::Instance(), &Settings::ConfigChanged, this, + &EnhancementsWidget::OnConfigChanged); +} + +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() { @@ -83,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); @@ -104,61 +119,60 @@ 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(); + 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_scaled_efb_copy = new ConfigBool(tr("Scaled EFB Copy"), Config::GFX_HACK_COPY_EFB_SCALED); - m_per_pixel_lighting = - new ConfigBool(tr("Per-Pixel Lighting"), Config::GFX_ENABLE_PIXEL_LIGHTING); + m_configure_pp_effect->setDisabled(true); - 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_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, m_game_layer); + + 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_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; enhancements_layout->addWidget(new QLabel(tr("Internal Resolution:")), row, 0); @@ -209,26 +223,27 @@ 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); - 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); - 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(); @@ -241,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; @@ -306,6 +316,7 @@ void EnhancementsWidget::LoadPPShaders(StereoMode stereo_mode) } } + // Force a shader for StereoModes that require it. if (!found) { if (stereo_mode == StereoMode::Anaglyph) @@ -321,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; @@ -426,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() @@ -713,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 9e64d5400f..b3eeca785b 100644 --- a/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.h +++ b/Source/Core/DolphinQt/Config/Graphics/EnhancementsWidget.h @@ -9,39 +9,52 @@ 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 + class EnhancementsWidget final : public QWidget { Q_OBJECT public: explicit EnhancementsWidget(GraphicsWindow* parent); + 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; @@ -60,6 +73,5 @@ private: ConfigBool* m_3d_swap_eyes; ConfigBool* m_3d_per_eye_resolution; - int m_msaa_modes; - bool m_block_save; + Config::Layer* m_game_layer = nullptr; }; 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/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 @@ --> + 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);