Merge pull request #13063 from TryTwo/PR_GameSettings

Add ability to edit game-specific GFX settings from game properties tab.
This commit is contained in:
JosJuice 2024-12-22 20:42:28 +01:00 committed by GitHub
commit 5641b83d4e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
26 changed files with 1124 additions and 872 deletions

View File

@ -52,6 +52,7 @@ add_executable(dolphin-emu
Config/ConfigControls/ConfigBool.h Config/ConfigControls/ConfigBool.h
Config/ConfigControls/ConfigChoice.cpp Config/ConfigControls/ConfigChoice.cpp
Config/ConfigControls/ConfigChoice.h Config/ConfigControls/ConfigChoice.h
Config/ConfigControls/ConfigControl.h
Config/ConfigControls/ConfigInteger.cpp Config/ConfigControls/ConfigInteger.cpp
Config/ConfigControls/ConfigInteger.h Config/ConfigControls/ConfigInteger.h
Config/ConfigControls/ConfigRadio.cpp Config/ConfigControls/ConfigRadio.cpp

View File

@ -3,31 +3,28 @@
#include "DolphinQt/Config/ConfigControls/ConfigBool.h" #include "DolphinQt/Config/ConfigControls/ConfigBool.h"
#include <QEvent>
#include <QFont>
#include <QSignalBlocker>
#include "Common/Config/Config.h"
#include "DolphinQt/Settings.h"
ConfigBool::ConfigBool(const QString& label, const Config::Info<bool>& setting, bool reverse) ConfigBool::ConfigBool(const QString& label, const Config::Info<bool>& setting, bool reverse)
: ToolTipCheckBox(label), m_setting(setting), m_reverse(reverse) : ConfigBool(label, setting, nullptr, reverse)
{ {
}
ConfigBool::ConfigBool(const QString& label, const Config::Info<bool>& 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); 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() void ConfigBool::Update()
{ {
Config::SetBaseOrCurrent(m_setting, static_cast<bool>(isChecked() ^ m_reverse)); const bool value = static_cast<bool>(isChecked() ^ m_reverse);
SaveValue(m_setting, value);
}
void ConfigBool::OnConfigChanged()
{
setChecked(ReadValue(m_setting) ^ m_reverse);
} }

View File

@ -3,6 +3,7 @@
#pragma once #pragma once
#include "DolphinQt/Config/ConfigControls/ConfigControl.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h" #include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h"
namespace Config namespace Config
@ -11,11 +12,16 @@ template <typename T>
class Info; class Info;
} }
class ConfigBool : public ToolTipCheckBox class ConfigBool final : public ConfigControl<ToolTipCheckBox>
{ {
Q_OBJECT Q_OBJECT
public: public:
ConfigBool(const QString& label, const Config::Info<bool>& setting, bool reverse = false); ConfigBool(const QString& label, const Config::Info<bool>& setting, bool reverse = false);
ConfigBool(const QString& label, const Config::Info<bool>& setting, Config::Layer* layer,
bool reverse = false);
protected:
void OnConfigChanged() override;
private: private:
void Update(); void Update();

View File

@ -5,85 +5,168 @@
#include <QSignalBlocker> #include <QSignalBlocker>
#include "Common/Config/Config.h" ConfigChoice::ConfigChoice(const QStringList& options, const Config::Info<int>& setting,
Config::Layer* layer)
#include "DolphinQt/Settings.h" : ConfigControl(setting.GetLocation(), layer), m_setting(setting)
ConfigChoice::ConfigChoice(const QStringList& options, const Config::Info<int>& setting)
: m_setting(setting)
{ {
addItems(options); addItems(options);
setCurrentIndex(ReadValue(setting));
connect(this, &QComboBox::currentIndexChanged, this, &ConfigChoice::Update); 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) 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<std::string>& options, ConfigStringChoice::ConfigStringChoice(const std::vector<std::string>& options,
const Config::Info<std::string>& setting) const Config::Info<std::string>& setting,
: m_setting(setting), m_text_is_data(true) Config::Layer* layer)
: ConfigControl(setting.GetLocation(), layer), m_setting(setting), m_text_is_data(true)
{ {
for (const auto& op : options) for (const auto& op : options)
addItem(QString::fromStdString(op)); addItem(QString::fromStdString(op));
Connect();
Load(); Load();
connect(this, &QComboBox::currentIndexChanged, this, &ConfigStringChoice::Update);
} }
ConfigStringChoice::ConfigStringChoice(const std::vector<std::pair<QString, QString>>& options, ConfigStringChoice::ConfigStringChoice(const std::vector<std::pair<QString, QString>>& options,
const Config::Info<std::string>& setting) const Config::Info<std::string>& setting,
: m_setting(setting), m_text_is_data(false) Config::Layer* layer)
: ConfigControl(setting.GetLocation(), layer), m_setting(setting), m_text_is_data(false)
{ {
for (const auto& [option_text, option_data] : options) for (const auto& [option_text, option_data] : options)
addItem(option_text, option_data); 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); connect(this, &QComboBox::currentIndexChanged, this, &ConfigStringChoice::Update);
Load();
} }
void ConfigStringChoice::Update(int index) void ConfigStringChoice::Update(int index)
{ {
if (m_text_is_data) if (m_text_is_data)
{ SaveValue(m_setting, itemText(index).toStdString());
Config::SetBaseOrCurrent(m_setting, itemText(index).toStdString());
}
else else
{ SaveValue(m_setting, itemData(index).toString().toStdString());
Config::SetBaseOrCurrent(m_setting, itemData(index).toString().toStdString());
}
} }
void ConfigStringChoice::Load() void ConfigStringChoice::Load()
{ {
const QString setting_value = QString::fromStdString(Config::Get(m_setting)); const QString setting_value = QString::fromStdString(ReadValue(m_setting));
const int index = m_text_is_data ? findText(setting_value) : findData(setting_value); const 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<OptionVariant>(m_layer->Get(setting));
return static_cast<OptionVariant>(Config::Get(setting));
};
std::pair<OptionVariant, OptionVariant> 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<int>(std::distance(m_options.begin(), it));
const QSignalBlocker blocker(this); const QSignalBlocker blocker(this);
setCurrentIndex(index); setCurrentIndex(index);
} }
const std::pair<Config::Location, Config::Location> 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);
}
};

View File

@ -7,15 +7,24 @@
#include <utility> #include <utility>
#include <vector> #include <vector>
#include "DolphinQt/Config/ConfigControls/ConfigControl.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipComboBox.h" #include "DolphinQt/Config/ToolTipControls/ToolTipComboBox.h"
#include "Common/Config/Config.h" namespace Config
{
template <typename T>
class Info;
}
class ConfigChoice : public ToolTipComboBox class ConfigChoice final : public ConfigControl<ToolTipComboBox>
{ {
Q_OBJECT Q_OBJECT
public: public:
ConfigChoice(const QStringList& options, const Config::Info<int>& setting); ConfigChoice(const QStringList& options, const Config::Info<int>& setting,
Config::Layer* layer = nullptr);
protected:
void OnConfigChanged() override;
private: private:
void Update(int choice); void Update(int choice);
@ -23,20 +32,49 @@ private:
Config::Info<int> m_setting; Config::Info<int> m_setting;
}; };
class ConfigStringChoice : public ToolTipComboBox class ConfigStringChoice final : public ConfigControl<ToolTipComboBox>
{ {
Q_OBJECT Q_OBJECT
public: public:
ConfigStringChoice(const std::vector<std::string>& options, ConfigStringChoice(const std::vector<std::string>& options,
const Config::Info<std::string>& setting); const Config::Info<std::string>& setting, Config::Layer* layer = nullptr);
ConfigStringChoice(const std::vector<std::pair<QString, QString>>& options, ConfigStringChoice(const std::vector<std::pair<QString, QString>>& options,
const Config::Info<std::string>& setting); const Config::Info<std::string>& setting, Config::Layer* layer = nullptr);
private:
void Connect();
void Update(int index);
void Load(); void Load();
Config::Info<std::string> m_setting; protected:
void OnConfigChanged() override;
private:
void Update(int index);
const Config::Info<std::string>& m_setting;
bool m_text_is_data = false; bool m_text_is_data = false;
}; };
class ConfigComplexChoice final : public ToolTipComboBox
{
Q_OBJECT
using InfoVariant = std::variant<Config::Info<u32>, Config::Info<int>, Config::Info<bool>>;
using OptionVariant = std::variant<u32, int, bool>;
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<Config::Location, Config::Location> 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<std::pair<OptionVariant, OptionVariant>> m_options;
};

View File

@ -0,0 +1,105 @@
// Copyright 2024 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <QFont>
#include <QMouseEvent>
#include <QSignalBlocker>
#include "Common/Config/Enums.h"
#include "Common/Config/Layer.h"
#include "DolphinQt/Settings.h"
namespace Config
{
template <typename T>
class Info;
struct Location;
} // namespace Config
template <class Derived>
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 <typename T>
void SaveValue(const Config::Info<T>& setting, const T& value)
{
if (m_layer != nullptr)
{
m_layer->Set(m_location, value);
Config::OnConfigChanged();
return;
}
Config::SetBaseOrCurrent(setting, value);
}
template <typename T>
const T ReadValue(const Config::Info<T>& 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;
};

View File

@ -3,20 +3,16 @@
#include "DolphinQt/Config/ConfigControls/ConfigFloatSlider.h" #include "DolphinQt/Config/ConfigControls/ConfigFloatSlider.h"
#include <QSignalBlocker>
#include "Common/Config/Config.h"
#include "DolphinQt/Settings.h"
ConfigFloatSlider::ConfigFloatSlider(float minimum, float maximum, ConfigFloatSlider::ConfigFloatSlider(float minimum, float maximum,
const Config::Info<float>& setting, float step) const Config::Info<float>& setting, float step,
: ToolTipSlider(Qt::Horizontal), m_minimum(minimum), m_step(step), m_setting(setting) Config::Layer* layer)
: ConfigControl(Qt::Horizontal, setting.GetLocation(), layer), m_minimum(minimum), m_step(step),
m_setting(setting)
{ {
const float range = maximum - minimum; const float range = maximum - minimum;
const int steps = std::round(range / step); const int steps = std::round(range / step);
const int interval = std::round(range / steps); 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); setMinimum(0);
setMaximum(steps); setMaximum(steps);
@ -24,25 +20,21 @@ ConfigFloatSlider::ConfigFloatSlider(float minimum, float maximum,
setValue(current_value); setValue(current_value);
connect(this, &ConfigFloatSlider::valueChanged, this, &ConfigFloatSlider::Update); 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) void ConfigFloatSlider::Update(int value)
{ {
const float current_value = (m_step * value) + m_minimum; const float current_value = (m_step * value) + m_minimum;
Config::SetBaseOrCurrent(m_setting, current_value);
SaveValue(m_setting, current_value);
} }
float ConfigFloatSlider::GetValue() const float ConfigFloatSlider::GetValue() const
{ {
return (m_step * value()) + m_minimum; return (m_step * value()) + m_minimum;
} }
void ConfigFloatSlider::OnConfigChanged()
{
setValue(std::round((ReadValue(m_setting) - m_minimum) / m_step));
}

View File

@ -3,6 +3,7 @@
#pragma once #pragma once
#include "DolphinQt/Config/ConfigControls/ConfigControl.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipSlider.h" #include "DolphinQt/Config/ToolTipControls/ToolTipSlider.h"
namespace Config namespace Config
@ -13,16 +14,20 @@ class Info;
// Automatically converts an int slider into a float one. // Automatically converts an int slider into a float one.
// Do not read the int values or ranges directly from it. // Do not read the int values or ranges directly from it.
class ConfigFloatSlider : public ToolTipSlider class ConfigFloatSlider final : public ConfigControl<ToolTipSlider>
{ {
Q_OBJECT Q_OBJECT
public: public:
ConfigFloatSlider(float minimum, float maximum, const Config::Info<float>& setting, float step); ConfigFloatSlider(float minimum, float maximum, const Config::Info<float>& setting, float step,
Config::Layer* layer = nullptr);
void Update(int value); void Update(int value);
// Returns the adjusted float value // Returns the adjusted float value
float GetValue() const; float GetValue() const;
protected:
void OnConfigChanged() override;
private: private:
float m_minimum; float m_minimum;
float m_step; float m_step;

View File

@ -3,33 +3,39 @@
#include "DolphinQt/Config/ConfigControls/ConfigInteger.h" #include "DolphinQt/Config/ConfigControls/ConfigInteger.h"
#include <QSignalBlocker>
#include "Common/Config/Config.h"
#include "DolphinQt/Settings.h"
ConfigInteger::ConfigInteger(int minimum, int maximum, const Config::Info<int>& setting, int step) ConfigInteger::ConfigInteger(int minimum, int maximum, const Config::Info<int>& setting, int step)
: ToolTipSpinBox(), m_setting(setting) : ConfigInteger(minimum, maximum, setting, nullptr, step)
{
}
ConfigInteger::ConfigInteger(int minimum, int maximum, const Config::Info<int>& setting,
Config::Layer* layer, int step)
: ConfigControl(setting.GetLocation(), layer), m_setting(setting)
{ {
setMinimum(minimum); setMinimum(minimum);
setMaximum(maximum); setMaximum(maximum);
setSingleStep(step); setSingleStep(step);
setValue(ReadValue(setting));
setValue(Config::Get(setting));
connect(this, &ConfigInteger::valueChanged, this, &ConfigInteger::Update); 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) 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<ConfigInteger>(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());
});
} }

View File

@ -3,6 +3,10 @@
#pragma once #pragma once
#include <QLabel>
#include <QPointer>
#include "DolphinQt/Config/ConfigControls/ConfigControl.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipSpinBox.h" #include "DolphinQt/Config/ToolTipControls/ToolTipSpinBox.h"
namespace Config namespace Config
@ -11,13 +15,30 @@ template <typename T>
class Info; class Info;
} }
class ConfigInteger : public ToolTipSpinBox class ConfigInteger final : public ConfigControl<ToolTipSpinBox>
{ {
Q_OBJECT Q_OBJECT
public: public:
ConfigInteger(int minimum, int maximum, const Config::Info<int>& setting, int step = 1); ConfigInteger(int minimum, int maximum, const Config::Info<int>& setting, int step = 1);
ConfigInteger(int minimum, int maximum, const Config::Info<int>& setting, Config::Layer* layer,
int step = 1);
void Update(int value); void Update(int value);
protected:
void OnConfigChanged() override;
private: private:
const Config::Info<int>& m_setting; const Config::Info<int>& m_setting;
}; };
class ConfigIntegerLabel final : public QLabel
{
Q_OBJECT
public:
ConfigIntegerLabel(const QString& text, ConfigInteger* widget);
private:
QPointer<ConfigInteger> m_widget;
};

View File

@ -3,33 +3,21 @@
#include "DolphinQt/Config/ConfigControls/ConfigRadio.h" #include "DolphinQt/Config/ConfigControls/ConfigRadio.h"
#include <QSignalBlocker> ConfigRadioInt::ConfigRadioInt(const QString& label, const Config::Info<int>& setting, int value,
Config::Layer* layer)
#include "Common/Config/Config.h" : ConfigControl(label, setting.GetLocation(), layer), m_setting(setting), m_value(value)
#include "DolphinQt/Settings.h"
ConfigRadioInt::ConfigRadioInt(const QString& label, const Config::Info<int>& setting, int value)
: ToolTipRadioButton(label), 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(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() void ConfigRadioInt::Update()
{ {
if (isChecked()) if (isChecked())
{ {
Config::SetBaseOrCurrent(m_setting, m_value); SaveValue(m_setting, m_value);
emit OnSelected(m_value); emit OnSelected(m_value);
} }
else else
@ -37,3 +25,8 @@ void ConfigRadioInt::Update()
emit OnDeselected(m_value); emit OnDeselected(m_value);
} }
} }
void ConfigRadioInt::OnConfigChanged()
{
setChecked(ReadValue(m_setting) == m_value);
}

View File

@ -3,15 +3,21 @@
#pragma once #pragma once
#include "DolphinQt/Config/ConfigControls/ConfigControl.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipRadioButton.h" #include "DolphinQt/Config/ToolTipControls/ToolTipRadioButton.h"
#include "Common/Config/Config.h" namespace Config
{
template <typename T>
class Info;
}
class ConfigRadioInt : public ToolTipRadioButton class ConfigRadioInt final : public ConfigControl<ToolTipRadioButton>
{ {
Q_OBJECT Q_OBJECT
public: public:
ConfigRadioInt(const QString& label, const Config::Info<int>& setting, int value); ConfigRadioInt(const QString& label, const Config::Info<int>& setting, int value,
Config::Layer* layer = nullptr);
signals: signals:
// Since selecting a new radio button deselects the old one, ::toggled will generate two 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 OnSelected(int new_value);
void OnDeselected(int old_value); void OnDeselected(int old_value);
protected:
void OnConfigChanged() override;
private: private:
void Update(); void Update();
Config::Info<int> m_setting; const Config::Info<int>& m_setting;
int m_value; int m_value;
}; };

View File

@ -1,36 +1,45 @@
// Copyright 2017 Dolphin Emulator Project // Copyright 2017 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later // SPDX-License-Identifier: GPL-2.0-or-later
#include <QFont>
#include "DolphinQt/Config/ConfigControls/ConfigSlider.h" #include "DolphinQt/Config/ConfigControls/ConfigSlider.h"
#include <QSignalBlocker>
#include "Common/Config/Config.h"
#include "DolphinQt/Settings.h" #include "DolphinQt/Settings.h"
ConfigSlider::ConfigSlider(int minimum, int maximum, const Config::Info<int>& setting, int tick) ConfigSlider::ConfigSlider(int minimum, int maximum, const Config::Info<int>& setting, int tick)
: ToolTipSlider(Qt::Horizontal), m_setting(setting) : ConfigSlider(minimum, maximum, setting, nullptr, tick)
{
}
ConfigSlider::ConfigSlider(int minimum, int maximum, const Config::Info<int>& setting,
Config::Layer* layer, int tick)
: ConfigControl(Qt::Horizontal, setting.GetLocation(), layer), m_setting(setting)
{ {
setMinimum(minimum); setMinimum(minimum);
setMaximum(maximum); setMaximum(maximum);
setTickInterval(tick); setTickInterval(tick);
setValue(ReadValue(setting));
setValue(Config::Get(setting));
connect(this, &ConfigSlider::valueChanged, this, &ConfigSlider::Update); 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) 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<ConfigSlider>(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());
});
} }

View File

@ -3,6 +3,10 @@
#pragma once #pragma once
#include <QLabel>
#include <QPointer>
#include "DolphinQt/Config/ConfigControls/ConfigControl.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipSlider.h" #include "DolphinQt/Config/ToolTipControls/ToolTipSlider.h"
namespace Config namespace Config
@ -11,13 +15,30 @@ template <typename T>
class Info; class Info;
} }
class ConfigSlider : public ToolTipSlider class ConfigSlider final : public ConfigControl<ToolTipSlider>
{ {
Q_OBJECT Q_OBJECT
public: public:
ConfigSlider(int minimum, int maximum, const Config::Info<int>& setting, int tick = 0); ConfigSlider(int minimum, int maximum, const Config::Info<int>& setting, int tick = 0);
ConfigSlider(int minimum, int maximum, const Config::Info<int>& setting, Config::Layer* layer,
int tick = 0);
void Update(int value); void Update(int value);
protected:
void OnConfigChanged() override;
private: private:
const Config::Info<int>& m_setting; const Config::Info<int>& m_setting;
}; };
class ConfigSliderLabel final : public QLabel
{
Q_OBJECT
public:
ConfigSliderLabel(const QString& text, ConfigSlider* slider);
private:
QPointer<ConfigSlider> m_slider;
};

View File

@ -3,37 +3,39 @@
#include "DolphinQt/Config/GameConfigWidget.h" #include "DolphinQt/Config/GameConfigWidget.h"
#include <QCheckBox> #include <QFont>
#include <QComboBox>
#include <QGroupBox> #include <QGroupBox>
#include <QHBoxLayout>
#include <QIcon>
#include <QLabel> #include <QLabel>
#include <QPushButton>
#include <QSlider>
#include <QSpinBox>
#include <QTabWidget> #include <QTabWidget>
#include <QTimer>
#include <QToolTip>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "Common/CommonPaths.h" #include "Common/CommonPaths.h"
#include "Common/Config/Config.h"
#include "Common/Config/Layer.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
#include "Core/Config/GraphicsSettings.h"
#include "Core/Config/MainSettings.h"
#include "Core/ConfigLoaders/GameConfigLoader.h" #include "Core/ConfigLoaders/GameConfigLoader.h"
#include "Core/ConfigManager.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/ConfigControls/ConfigSlider.h"
#include "DolphinQt/Config/GameConfigEdit.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" #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, static void PopulateTab(QTabWidget* tab, const std::string& path, std::string& game_id,
u16 revision, bool read_only) u16 revision, bool read_only)
{ {
@ -55,46 +57,62 @@ GameConfigWidget::GameConfigWidget(const UICommon::GameFile& game) : m_game(game
m_gameini_local_path = m_gameini_local_path =
QString::fromStdString(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini"); QString::fromStdString(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini");
m_layer = std::make_unique<Config::Layer>(
ConfigLoaders::GenerateLocalGameConfigLoader(m_game_id, m_game.GetRevision()));
m_global_layer = std::make_unique<Config::Layer>(
ConfigLoaders::GenerateGlobalGameConfigLoader(m_game_id, m_game.GetRevision()));
CreateWidgets(); CreateWidgets();
LoadSettings(); connect(&Settings::Instance(), &Settings::ConfigChanged, this, &GameConfigWidget::LoadSettings);
ConnectWidgets();
PopulateTab(m_default_tab, File::GetSysDirectory() + GAMESETTINGS_DIR DIR_SEP, m_game_id, PopulateTab(m_default_tab, File::GetSysDirectory() + GAMESETTINGS_DIR DIR_SEP, m_game_id,
m_game.GetRevision(), true); m_game.GetRevision(), true);
PopulateTab(m_local_tab, File::GetUserPath(D_GAMESETTINGS_IDX), m_game_id, m_game.GetRevision(), PopulateTab(m_local_tab, File::GetUserPath(D_GAMESETTINGS_IDX), m_game_id, m_game.GetRevision(),
false); false);
// Always give the user the opportunity to create a new INI bool game_id_tab = false;
if (m_local_tab->count() == 0) 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( auto* edit = new GameConfigEdit(
nullptr, QString::fromStdString(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini"), nullptr, QString::fromStdString(File::GetUserPath(D_GAMESETTINGS_IDX) + m_game_id + ".ini"),
false); false);
m_local_tab->addTab(edit, QString::fromStdString(m_game_id + ".ini")); 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() void GameConfigWidget::CreateWidgets()
{ {
// General Config::Layer* layer = m_layer.get();
m_refresh_config = new QPushButton(tr("Refresh"));
// Core // Core
auto* core_box = new QGroupBox(tr("Core")); auto* core_box = new QGroupBox(tr("Core"));
auto* core_layout = new QGridLayout; auto* core_layout = new QGridLayout;
core_box->setLayout(core_layout); core_box->setLayout(core_layout);
m_enable_dual_core = new QCheckBox(tr("Enable Dual Core")); m_enable_dual_core = new ConfigBool(tr("Enable Dual Core"), Config::MAIN_CPU_THREAD, layer);
m_enable_mmu = new QCheckBox(tr("Enable MMU")); m_enable_mmu = new ConfigBool(tr("Enable MMU"), Config::MAIN_MMU, layer);
m_enable_fprf = new QCheckBox(tr("Enable FPRF")); m_enable_fprf = new ConfigBool(tr("Enable FPRF"), Config::MAIN_FPRF, layer);
m_sync_gpu = new QCheckBox(tr("Synchronize GPU thread")); m_sync_gpu = new ConfigBool(tr("Synchronize GPU thread"), Config::MAIN_SYNC_GPU, layer);
m_emulate_disc_speed = new QCheckBox(tr("Emulate Disc Speed")); m_emulate_disc_speed =
m_use_dsp_hle = new QCheckBox(tr("DSP HLE (fast)")); new ConfigBool(tr("Emulate Disc Speed"), Config::MAIN_FAST_DISC_SPEED, layer, true);
m_manual_texture_sampling = new QCheckBox(tr("Manual Texture Sampling")); m_use_dsp_hle = new ConfigBool(tr("DSP HLE (fast)"), Config::MAIN_DSP_HLE, layer);
m_deterministic_dual_core = new QComboBox;
for (const auto& item : {tr("Not Set"), tr("auto"), tr("none"), tr("fake-completion")}) const std::vector<std::string> choice{tr("auto").toStdString(), tr("none").toStdString(),
m_deterministic_dual_core->addItem(item); tr("fake-completion").toStdString()};
m_deterministic_dual_core =
new ConfigStringChoice(choice, Config::MAIN_GPU_DETERMINISM_MODE, layer);
m_enable_mmu->setToolTip(tr( m_enable_mmu->setToolTip(tr(
"Enables the Memory Management Unit, needed for some games. (ON = Compatible, OFF = Fast)")); "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_sync_gpu, 3, 0);
core_layout->addWidget(m_emulate_disc_speed, 4, 0); core_layout->addWidget(m_emulate_disc_speed, 4, 0);
core_layout->addWidget(m_use_dsp_hle, 5, 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:")), 6, 0);
core_layout->addWidget(new QLabel(tr("Deterministic dual core:")), 7, 0); core_layout->addWidget(m_deterministic_dual_core, 6, 1);
core_layout->addWidget(m_deterministic_dual_core, 7, 1);
// Stereoscopy // Stereoscopy
auto* stereoscopy_box = new QGroupBox(tr("Stereoscopy")); auto* stereoscopy_box = new QGroupBox(tr("Stereoscopy"));
auto* stereoscopy_layout = new QGridLayout; auto* stereoscopy_layout = new QGridLayout;
stereoscopy_box->setLayout(stereoscopy_layout); stereoscopy_box->setLayout(stereoscopy_layout);
m_depth_slider = new QSlider(Qt::Horizontal); 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_depth_slider->setMinimum(100); m_use_monoscopic_shadows =
m_depth_slider->setMaximum(200); new ConfigBool(tr("Monoscopic Shadows"), Config::GFX_STEREO_EFB_MONO_DEPTH, layer);
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->setToolTip( m_depth_slider->setToolTip(
tr("This value is multiplied with the depth set in the graphics configuration.")); 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( m_use_monoscopic_shadows->setToolTip(
tr("Use a single depth buffer for both eyes. Needed for a few games.")); 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(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_convergence_spin, 1, 1);
stereoscopy_layout->addWidget(m_use_monoscopic_shadows, 2, 0); stereoscopy_layout->addWidget(m_use_monoscopic_shadows, 2, 0);
auto* settings_box = new QGroupBox(tr("Game-Specific Settings")); auto* general_layout = new QVBoxLayout;
auto* settings_layout = new QVBoxLayout; general_layout->addWidget(core_box);
settings_box->setLayout(settings_layout); general_layout->addWidget(stereoscopy_box);
general_layout->addStretch();
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_widget = new QWidget; auto* general_widget = new QWidget;
general_widget->setLayout(general_layout); general_widget->setLayout(general_layout);
// Advanced // Editor tab
auto* advanced_layout = new QVBoxLayout; auto* advanced_layout = new QVBoxLayout;
auto* default_group = new QGroupBox(tr("Default Config (Read Only)")); auto* default_group = new QGroupBox(tr("Default Config (Read Only)"));
@ -196,207 +194,186 @@ void GameConfigWidget::CreateWidgets()
auto* layout = new QVBoxLayout; auto* layout = new QVBoxLayout;
auto* tab_widget = new QTabWidget; auto* tab_widget = new QTabWidget;
tab_widget->addTab(general_widget, tr("General")); 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); layout->addWidget(tab_widget);
setLayout(layout); setLayout(layout);
} }
void GameConfigWidget::ConnectWidgets() GameConfigWidget::~GameConfigWidget()
{ {
// Buttons // Destructor saves the layer to file.
connect(m_refresh_config, &QPushButton::clicked, this, &GameConfigWidget::LoadSettings); m_layer.reset();
for (QCheckBox* box : // If a game is running and the game properties window is closed, update local game layer with
{m_enable_dual_core, m_enable_mmu, m_enable_fprf, m_sync_gpu, m_emulate_disc_speed, // any new changes. Not sure if doing it more frequently is safe.
m_use_dsp_hle, m_manual_texture_sampling, m_use_monoscopic_shadows}) auto local_layer = Config::GetLayer(Config::LayerType::LocalGame);
connect(box, &QCheckBox::stateChanged, this, &GameConfigWidget::SaveSettings); if (local_layer && SConfig::GetInstance().GetGameID() == m_game_id)
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)
{ {
m_gameini_local.DeleteKey(section, key); local_layer->DeleteAllKeys();
return; local_layer->Load();
Config::OnConfigChanged();
} }
bool checked = (checkbox->checkState() == Qt::Checked) ^ reverse; // Delete empty configs
if (File::GetSize(m_gameini_local_path.toStdString()) == 0)
if (m_gameini_default.Exists(section, key)) File::Delete(m_gameini_local_path.toStdString());
{
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);
} }
void GameConfigWidget::LoadSettings() void GameConfigWidget::LoadSettings()
{ {
// Reload config // Load globals
m_gameini_local = SConfig::LoadLocalGameIni(m_game_id, m_game.GetRevision()); auto update_bool = [this](auto config, bool reverse = false) {
m_gameini_default = SConfig::LoadDefaultGameIni(m_game_id, m_game.GetRevision()); 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 std::optional<bool> value = m_global_layer->Get<bool>(config->GetLocation());
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::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); if (m_layer->Exists(setting) || !m_global_layer->Exists(setting))
m_gameini_local.GetIfExists("Core", "GPUDeterminismMode", &determinism_mode); return;
if (determinism_mode == DETERMINISM_AUTO_STRING) std::optional<int> value = m_global_layer->Get<int>(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; update_bool(config);
}
else if (determinism_mode == DETERMINISM_NONE_STRING)
{
determinism_index = DETERMINISM_NONE_INDEX;
}
else if (determinism_mode == DETERMINISM_FAKE_COMPLETION_STRING)
{
determinism_index = DETERMINISM_FAKE_COMPLETION_INDEX;
} }
m_deterministic_dual_core->setCurrentIndex(determinism_index); update_bool(m_emulate_disc_speed, true);
// Stereoscopy update_int(m_depth_slider);
int depth_percentage = 100; update_int(m_convergence_spin);
m_gameini_default.GetIfExists("Video_Stereoscopy", "StereoDepthPercentage", &depth_percentage);
m_gameini_local.GetIfExists("Video_Stereoscopy", "StereoDepthPercentage", &depth_percentage);
m_depth_slider->setValue(depth_percentage);
int convergence = 0;
m_gameini_default.GetIfExists("Video_Stereoscopy", "StereoConvergence", &convergence);
m_gameini_local.GetIfExists("Video_Stereoscopy", "StereoConvergence", &convergence);
m_convergence_spin->setValue(convergence);
LoadCheckBox(m_use_monoscopic_shadows, "Video_Stereoscopy", "StereoEFBMonoDepth");
} }
void GameConfigWidget::SaveSettings() void GameConfigWidget::SetItalics()
{ {
// Core // Mark system game settings with italics. Called once because it should never change.
SaveCheckBox(m_enable_dual_core, "Core", "CPUThread"); auto italics = [this](auto config) {
SaveCheckBox(m_enable_mmu, "Core", "MMU"); if (!m_global_layer->Exists(config->GetLocation()))
SaveCheckBox(m_enable_fprf, "Core", "FPRF"); return;
SaveCheckBox(m_sync_gpu, "Core", "SyncGPU");
SaveCheckBox(m_emulate_disc_speed, "Core", "FastDiscSpeed", true);
SaveCheckBox(m_use_dsp_hle, "Core", "DSPHLE");
SaveCheckBox(m_manual_texture_sampling, "Video_Hacks", "FastTextureSampling", true);
int determinism_num = m_deterministic_dual_core->currentIndex(); QFont ifont = config->font();
ifont.setItalic(true);
config->setFont(ifont);
};
std::string determinism_mode = DETERMINISM_NOT_SET_STRING; for (auto* config : findChildren<ConfigBool*>())
italics(config);
for (auto* config : findChildren<ConfigSlider*>())
italics(config);
for (auto* config : findChildren<ConfigInteger*>())
italics(config);
for (auto* config : findChildren<ConfigRadioInt*>())
italics(config);
for (auto* config : findChildren<ConfigChoice*>())
italics(config);
for (auto* config : findChildren<ConfigStringChoice*>())
italics(config);
switch (determinism_num) for (auto* config : findChildren<ConfigComplexChoice*>())
{ {
case DETERMINISM_AUTO_INDEX: std::pair<Config::Location, Config::Location> location = config->GetLocation();
determinism_mode = DETERMINISM_AUTO_STRING; if (m_global_layer->Exists(location.first) || m_global_layer->Exists(location.second))
break;
case DETERMINISM_NONE_INDEX:
determinism_mode = DETERMINISM_NONE_STRING;
break;
case DETERMINISM_FAKE_COMPLETION_INDEX:
determinism_mode = DETERMINISM_FAKE_COMPLETION_STRING;
break;
}
if (determinism_mode != DETERMINISM_NOT_SET_STRING)
{
std::string default_mode = DETERMINISM_NOT_SET_STRING;
if (!(m_gameini_default.GetIfExists("Core", "GPUDeterminismMode", &default_mode) &&
default_mode == determinism_mode))
{ {
m_gameini_local.GetOrCreateSection("Core")->Set("GPUDeterminismMode", determinism_mode); 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());
} }

View File

@ -7,19 +7,21 @@
#include <QString> #include <QString>
#include <QWidget> #include <QWidget>
#include "Common/IniFile.h"
namespace UICommon namespace UICommon
{ {
class GameFile; class GameFile;
} }
class QCheckBox; namespace Config
class QComboBox; {
class Layer;
} // namespace Config
class ConfigBool;
class ConfigInteger;
class ConfigSlider;
class ConfigStringChoice;
class QPushButton; class QPushButton;
class QSlider;
class QSpinBox;
class QTabWidget; class QTabWidget;
class GameConfigWidget : public QWidget class GameConfigWidget : public QWidget
@ -27,45 +29,33 @@ class GameConfigWidget : public QWidget
Q_OBJECT Q_OBJECT
public: public:
explicit GameConfigWidget(const UICommon::GameFile& game); explicit GameConfigWidget(const UICommon::GameFile& game);
~GameConfigWidget();
private: private:
void CreateWidgets(); void CreateWidgets();
void ConnectWidgets();
void LoadSettings(); 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; QString m_gameini_local_path;
QTabWidget* m_default_tab; QTabWidget* m_default_tab;
QTabWidget* m_local_tab; QTabWidget* m_local_tab;
QCheckBox* m_enable_dual_core; ConfigBool* m_enable_dual_core;
QCheckBox* m_enable_mmu; ConfigBool* m_enable_mmu;
QCheckBox* m_enable_fprf; ConfigBool* m_enable_fprf;
QCheckBox* m_sync_gpu; ConfigBool* m_sync_gpu;
QCheckBox* m_emulate_disc_speed; ConfigBool* m_emulate_disc_speed;
QCheckBox* m_use_dsp_hle; ConfigBool* m_use_dsp_hle;
QCheckBox* m_use_monoscopic_shadows; ConfigBool* m_use_monoscopic_shadows;
QCheckBox* m_manual_texture_sampling;
QPushButton* m_refresh_config; ConfigStringChoice* m_deterministic_dual_core;
ConfigSlider* m_depth_slider;
QComboBox* m_deterministic_dual_core; ConfigInteger* m_convergence_spin;
QSlider* m_depth_slider;
QSpinBox* m_convergence_spin;
const UICommon::GameFile& m_game; const UICommon::GameFile& m_game;
std::string m_game_id; std::string m_game_id;
std::unique_ptr<Config::Layer> m_layer;
Common::IniFile m_gameini_local; std::unique_ptr<Config::Layer> m_global_layer;
Common::IniFile m_gameini_default; int m_prev_tab_index = 0;
}; };

View File

@ -3,11 +3,9 @@
#include "DolphinQt/Config/Graphics/AdvancedWidget.h" #include "DolphinQt/Config/Graphics/AdvancedWidget.h"
#include <QCheckBox>
#include <QGridLayout> #include <QGridLayout>
#include <QGroupBox> #include <QGroupBox>
#include <QLabel> #include <QLabel>
#include <QSpinBox>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "Core/Config/GraphicsSettings.h" #include "Core/Config/GraphicsSettings.h"
@ -19,6 +17,7 @@
#include "DolphinQt/Config/ConfigControls/ConfigBool.h" #include "DolphinQt/Config/ConfigControls/ConfigBool.h"
#include "DolphinQt/Config/ConfigControls/ConfigChoice.h" #include "DolphinQt/Config/ConfigControls/ConfigChoice.h"
#include "DolphinQt/Config/ConfigControls/ConfigInteger.h" #include "DolphinQt/Config/ConfigControls/ConfigInteger.h"
#include "DolphinQt/Config/GameConfigWidget.h"
#include "DolphinQt/Config/Graphics/GraphicsWindow.h" #include "DolphinQt/Config/Graphics/GraphicsWindow.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h" #include "DolphinQt/Config/ToolTipControls/ToolTipCheckBox.h"
#include "DolphinQt/QtUtils/SignalBlocking.h" #include "DolphinQt/QtUtils/SignalBlocking.h"
@ -29,7 +28,6 @@
AdvancedWidget::AdvancedWidget(GraphicsWindow* parent) AdvancedWidget::AdvancedWidget(GraphicsWindow* parent)
{ {
CreateWidgets(); CreateWidgets();
LoadSettings();
ConnectWidgets(); ConnectWidgets();
AddDescriptions(); AddDescriptions();
@ -37,17 +35,30 @@ AdvancedWidget::AdvancedWidget(GraphicsWindow* parent)
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) { connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) {
OnEmulationStateChanged(state != Core::State::Uninitialized); OnEmulationStateChanged(state != Core::State::Uninitialized);
}); });
connect(m_manual_texture_sampling, &QCheckBox::toggled, [this, parent] { connect(m_manual_texture_sampling, &QCheckBox::toggled,
SaveSettings(); [this, parent] { emit parent->UseFastTextureSamplingChanged(); });
emit parent->UseFastTextureSamplingChanged();
});
OnBackendChanged(); OnBackendChanged();
OnEmulationStateChanged(!Core::IsUninitialized(Core::System::GetInstance())); 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() void AdvancedWidget::CreateWidgets()
{ {
const bool local_edit = m_game_layer != nullptr;
auto* main_layout = new QVBoxLayout; auto* main_layout = new QVBoxLayout;
// Performance // Performance
@ -55,17 +66,19 @@ void AdvancedWidget::CreateWidgets()
auto* performance_layout = new QGridLayout(); auto* performance_layout = new QGridLayout();
performance_box->setLayout(performance_layout); performance_box->setLayout(performance_layout);
m_show_fps = new ConfigBool(tr("Show FPS"), Config::GFX_SHOW_FPS); 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_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_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_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_show_graphs =
m_show_speed = new ConfigBool(tr("Show % Speed"), Config::GFX_SHOW_SPEED); new ConfigBool(tr("Show Performance Graphs"), Config::GFX_SHOW_GRAPHS, m_game_layer);
m_show_speed_colors = new ConfigBool(tr("Show Speed Colors"), Config::GFX_SHOW_SPEED_COLORS); m_show_speed = new ConfigBool(tr("Show % Speed"), Config::GFX_SHOW_SPEED, m_game_layer);
m_perf_samp_window = new ConfigInteger(0, 10000, Config::GFX_PERF_SAMP_WINDOW, 100); 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_perf_samp_window->SetTitle(tr("Performance Sample Window (ms)"));
m_log_render_time = m_log_render_time = new ConfigBool(tr("Log Render Time to File"),
new ConfigBool(tr("Log Render Time to File"), Config::GFX_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_fps, 0, 0);
performance_layout->addWidget(m_show_ftimes, 0, 1); performance_layout->addWidget(m_show_ftimes, 0, 1);
@ -83,14 +96,16 @@ void AdvancedWidget::CreateWidgets()
auto* debugging_layout = new QGridLayout(); auto* debugging_layout = new QGridLayout();
debugging_box->setLayout(debugging_layout); debugging_box->setLayout(debugging_layout);
m_enable_wireframe = new ConfigBool(tr("Enable Wireframe"), Config::GFX_ENABLE_WIREFRAME); m_enable_wireframe =
m_show_statistics = new ConfigBool(tr("Show Statistics"), Config::GFX_OVERLAY_STATS); new ConfigBool(tr("Enable Wireframe"), Config::GFX_ENABLE_WIREFRAME, m_game_layer);
m_show_proj_statistics = m_show_statistics =
new ConfigBool(tr("Show Projection Statistics"), Config::GFX_OVERLAY_PROJ_STATS); 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 = m_enable_format_overlay =
new ConfigBool(tr("Texture Format Overlay"), Config::GFX_TEXFMT_OVERLAY_ENABLE); new ConfigBool(tr("Texture Format Overlay"), Config::GFX_TEXFMT_OVERLAY_ENABLE, m_game_layer);
m_enable_api_validation = m_enable_api_validation = new ConfigBool(tr("Enable API Validation Layers"),
new ConfigBool(tr("Enable API Validation Layers"), Config::GFX_ENABLE_VALIDATION_LAYER); Config::GFX_ENABLE_VALIDATION_LAYER, m_game_layer);
debugging_layout->addWidget(m_enable_wireframe, 0, 0); debugging_layout->addWidget(m_enable_wireframe, 0, 0);
debugging_layout->addWidget(m_show_statistics, 0, 1); debugging_layout->addWidget(m_show_statistics, 0, 1);
@ -103,14 +118,25 @@ void AdvancedWidget::CreateWidgets()
auto* utility_layout = new QGridLayout(); auto* utility_layout = new QGridLayout();
utility_box->setLayout(utility_layout); utility_box->setLayout(utility_layout);
m_load_custom_textures = new ConfigBool(tr("Load Custom Textures"), Config::GFX_HIRES_TEXTURES); m_load_custom_textures =
m_prefetch_custom_textures = new ConfigBool(tr("Load Custom Textures"), Config::GFX_HIRES_TEXTURES, m_game_layer);
new ConfigBool(tr("Prefetch Custom Textures"), Config::GFX_CACHE_HIRES_TEXTURES); 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_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_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); if (local_edit)
m_enable_graphics_mods = new ToolTipCheckBox(tr("Enable Graphics Mods")); {
// 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_load_custom_textures, 0, 0);
utility_layout->addWidget(m_prefetch_custom_textures, 0, 1); 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_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_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 = 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); texture_dump_layout->addWidget(m_dump_textures, 0, 0);
@ -142,18 +178,22 @@ void AdvancedWidget::CreateWidgets()
m_frame_dumps_resolution_type = m_frame_dumps_resolution_type =
new ConfigChoice({tr("Window Resolution"), tr("Aspect Ratio Corrected Internal Resolution"), new ConfigChoice({tr("Window Resolution"), tr("Aspect Ratio Corrected Internal Resolution"),
tr("Raw Internal Resolution")}, tr("Raw Internal Resolution")},
Config::GFX_FRAME_DUMPS_RESOLUTION_TYPE); Config::GFX_FRAME_DUMPS_RESOLUTION_TYPE, m_game_layer);
m_dump_use_ffv1 = new ConfigBool(tr("Use Lossless Codec (FFV1)"), Config::GFX_USE_FFV1); m_png_compression_level =
m_dump_bitrate = new ConfigInteger(0, 1000000, Config::GFX_BITRATE_KBPS, 1000); new ConfigInteger(0, 9, Config::GFX_PNG_COMPRESSION_LEVEL, m_game_layer);
m_png_compression_level = new ConfigInteger(0, 9, Config::GFX_PNG_COMPRESSION_LEVEL);
dump_layout->addWidget(new QLabel(tr("Resolution Type:")), 0, 0); dump_layout->addWidget(new QLabel(tr("Resolution Type:")), 0, 0);
dump_layout->addWidget(m_frame_dumps_resolution_type, 0, 1); dump_layout->addWidget(m_frame_dumps_resolution_type, 0, 1);
#if defined(HAVE_FFMPEG) #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(m_dump_use_ffv1, 1, 0);
dump_layout->addWidget(new QLabel(tr("Bitrate (kbps):")), 2, 0); dump_layout->addWidget(new QLabel(tr("Bitrate (kbps):")), 2, 0);
dump_layout->addWidget(m_dump_bitrate, 2, 1); dump_layout->addWidget(m_dump_bitrate, 2, 1);
#endif #endif
dump_layout->addWidget(new QLabel(tr("PNG Compression Level:")), 3, 0); dump_layout->addWidget(new QLabel(tr("PNG Compression Level:")), 3, 0);
m_png_compression_level->SetTitle(tr("PNG Compression Level")); m_png_compression_level->SetTitle(tr("PNG Compression Level"));
dump_layout->addWidget(m_png_compression_level, 3, 1); dump_layout->addWidget(m_png_compression_level, 3, 1);
@ -163,14 +203,16 @@ void AdvancedWidget::CreateWidgets()
auto* misc_layout = new QGridLayout(); auto* misc_layout = new QGridLayout();
misc_box->setLayout(misc_layout); misc_box->setLayout(misc_layout);
m_enable_cropping = new ConfigBool(tr("Crop"), Config::GFX_CROP); m_enable_cropping = new ConfigBool(tr("Crop"), Config::GFX_CROP, m_game_layer);
m_enable_prog_scan = new ToolTipCheckBox(tr("Enable Progressive Scan")); m_enable_prog_scan =
m_backend_multithreading = new ConfigBool(tr("Enable Progressive Scan"), Config::SYSCONF_PROGRESSIVE_SCAN, m_game_layer);
new ConfigBool(tr("Backend Multithreading"), Config::GFX_BACKEND_MULTITHREADING); m_backend_multithreading = new ConfigBool(tr("Backend Multithreading"),
Config::GFX_BACKEND_MULTITHREADING, m_game_layer);
m_prefer_vs_for_point_line_expansion = new ConfigBool( m_prefer_vs_for_point_line_expansion = new ConfigBool(
// i18n: VS is short for vertex shaders. // i18n: VS is short for vertex shaders.
tr("Prefer VS for Point/Line Expansion"), Config::GFX_PREFER_VS_FOR_LINE_POINT_EXPANSION); 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); 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_cropping, 0, 0);
misc_layout->addWidget(m_enable_prog_scan, 0, 1); misc_layout->addWidget(m_enable_prog_scan, 0, 1);
@ -179,7 +221,7 @@ void AdvancedWidget::CreateWidgets()
misc_layout->addWidget(m_cpu_cull, 2, 0); misc_layout->addWidget(m_cpu_cull, 2, 0);
#ifdef _WIN32 #ifdef _WIN32
m_borderless_fullscreen = 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); misc_layout->addWidget(m_borderless_fullscreen, 2, 1);
#endif #endif
@ -189,10 +231,10 @@ void AdvancedWidget::CreateWidgets()
auto* experimental_layout = new QGridLayout(); auto* experimental_layout = new QGridLayout();
experimental_box->setLayout(experimental_layout); experimental_box->setLayout(experimental_layout);
m_defer_efb_access_invalidation = m_defer_efb_access_invalidation = new ConfigBool(
new ConfigBool(tr("Defer EFB Cache Invalidation"), Config::GFX_HACK_EFB_DEFER_INVALIDATION); tr("Defer EFB Cache Invalidation"), Config::GFX_HACK_EFB_DEFER_INVALIDATION, m_game_layer);
m_manual_texture_sampling = m_manual_texture_sampling = new ConfigBool(
new ConfigBool(tr("Manual Texture Sampling"), Config::GFX_HACK_FAST_TEXTURE_SAMPLING, true); 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_defer_efb_access_invalidation, 0, 0);
experimental_layout->addWidget(m_manual_texture_sampling, 0, 1); experimental_layout->addWidget(m_manual_texture_sampling, 0, 1);
@ -211,34 +253,19 @@ void AdvancedWidget::CreateWidgets()
void AdvancedWidget::ConnectWidgets() void AdvancedWidget::ConnectWidgets()
{ {
connect(m_load_custom_textures, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings); connect(m_load_custom_textures, &QCheckBox::toggled, this,
connect(m_dump_use_ffv1, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings); [this](bool checked) { m_prefetch_custom_textures->setEnabled(checked); });
connect(m_enable_prog_scan, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings); connect(m_dump_textures, &QCheckBox::toggled, this, [this](bool checked) {
connect(m_dump_textures, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings); m_dump_mip_textures->setEnabled(checked);
connect(m_enable_graphics_mods, &QCheckBox::toggled, this, &AdvancedWidget::SaveSettings); 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() #if defined(HAVE_FFMPEG)
{ connect(m_dump_use_ffv1, &QCheckBox::toggled, this,
m_prefetch_custom_textures->setEnabled(Config::Get(Config::GFX_HIRES_TEXTURES)); [this](bool checked) { m_dump_bitrate->setEnabled(!checked); });
m_dump_bitrate->setEnabled(!Config::Get(Config::GFX_USE_FFV1)); #endif
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());
} }
void AdvancedWidget::OnBackendChanged() void AdvancedWidget::OnBackendChanged()

View File

@ -8,22 +8,22 @@
class ConfigBool; class ConfigBool;
class ConfigChoice; class ConfigChoice;
class ConfigInteger; class ConfigInteger;
class GameConfigWidget;
class GraphicsWindow; class GraphicsWindow;
class QCheckBox;
class QComboBox; namespace Config
class QSpinBox; {
class ToolTipCheckBox; class Layer;
} // namespace Config
class AdvancedWidget final : public QWidget class AdvancedWidget final : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit AdvancedWidget(GraphicsWindow* parent); explicit AdvancedWidget(GraphicsWindow* parent);
AdvancedWidget(GameConfigWidget* parent, Config::Layer* layer);
private: private:
void LoadSettings();
void SaveSettings();
void CreateWidgets(); void CreateWidgets();
void ConnectWidgets(); void ConnectWidgets();
void AddDescriptions(); void AddDescriptions();
@ -52,7 +52,7 @@ private:
ConfigBool* m_dump_xfb_target; ConfigBool* m_dump_xfb_target;
ConfigBool* m_disable_vram_copies; ConfigBool* m_disable_vram_copies;
ConfigBool* m_load_custom_textures; ConfigBool* m_load_custom_textures;
ToolTipCheckBox* m_enable_graphics_mods; ConfigBool* m_enable_graphics_mods;
// Texture dumping // Texture dumping
ConfigBool* m_dump_textures; ConfigBool* m_dump_textures;
@ -67,7 +67,7 @@ private:
// Misc // Misc
ConfigBool* m_enable_cropping; ConfigBool* m_enable_cropping;
ToolTipCheckBox* m_enable_prog_scan; ConfigBool* m_enable_prog_scan;
ConfigBool* m_backend_multithreading; ConfigBool* m_backend_multithreading;
ConfigBool* m_prefer_vs_for_point_line_expansion; ConfigBool* m_prefer_vs_for_point_line_expansion;
ConfigBool* m_cpu_cull; ConfigBool* m_cpu_cull;
@ -76,4 +76,6 @@ private:
// Experimental // Experimental
ConfigBool* m_defer_efb_access_invalidation; ConfigBool* m_defer_efb_access_invalidation;
ConfigBool* m_manual_texture_sampling; ConfigBool* m_manual_texture_sampling;
Config::Layer* m_game_layer = nullptr;
}; };

View File

@ -11,6 +11,8 @@
#include <QPushButton> #include <QPushButton>
#include <QVBoxLayout> #include <QVBoxLayout>
#include "Common/CommonTypes.h"
#include "Core/Config/GraphicsSettings.h" #include "Core/Config/GraphicsSettings.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
@ -18,6 +20,7 @@
#include "DolphinQt/Config/ConfigControls/ConfigChoice.h" #include "DolphinQt/Config/ConfigControls/ConfigChoice.h"
#include "DolphinQt/Config/ConfigControls/ConfigRadio.h" #include "DolphinQt/Config/ConfigControls/ConfigRadio.h"
#include "DolphinQt/Config/ConfigControls/ConfigSlider.h" #include "DolphinQt/Config/ConfigControls/ConfigSlider.h"
#include "DolphinQt/Config/GameConfigWidget.h"
#include "DolphinQt/Config/Graphics/ColorCorrectionConfigWindow.h" #include "DolphinQt/Config/Graphics/ColorCorrectionConfigWindow.h"
#include "DolphinQt/Config/Graphics/GraphicsWindow.h" #include "DolphinQt/Config/Graphics/GraphicsWindow.h"
#include "DolphinQt/Config/Graphics/PostProcessingConfigWindow.h" #include "DolphinQt/Config/Graphics/PostProcessingConfigWindow.h"
@ -31,31 +34,43 @@
#include "VideoCommon/VideoCommon.h" #include "VideoCommon/VideoCommon.h"
#include "VideoCommon/VideoConfig.h" #include "VideoCommon/VideoConfig.h"
EnhancementsWidget::EnhancementsWidget(GraphicsWindow* parent) : m_block_save(false) EnhancementsWidget::EnhancementsWidget(GraphicsWindow* parent)
{ {
CreateWidgets(); CreateWidgets();
LoadSettings(); LoadPPShaders();
ConnectWidgets(); ConnectWidgets();
AddDescriptions(); AddDescriptions();
connect(parent, &GraphicsWindow::BackendChanged,
[this](const QString& backend) { LoadSettings(); }); // BackendChanged is called by parent on window creation.
connect(parent, &GraphicsWindow::UseFastTextureSamplingChanged, this, connect(parent, &GraphicsWindow::BackendChanged, this, &EnhancementsWidget::OnBackendChanged);
&EnhancementsWidget::LoadSettings); connect(parent, &GraphicsWindow::UseFastTextureSamplingChanged, this, [this]() {
connect(parent, &GraphicsWindow::UseGPUTextureDecodingChanged, this, m_texture_filtering_combo->setEnabled(ReadSetting(Config::GFX_HACK_FAST_TEXTURE_SAMPLING));
&EnhancementsWidget::LoadSettings); });
connect(parent, &GraphicsWindow::UseGPUTextureDecodingChanged, this, [this]() {
m_arbitrary_mipmap_detection->setEnabled(!ReadSetting(Config::GFX_ENABLE_GPU_TEXTURE_DECODING));
});
} }
constexpr int TEXTURE_FILTERING_DEFAULT = 0; EnhancementsWidget::EnhancementsWidget(GameConfigWidget* parent, Config::Layer* layer)
constexpr int TEXTURE_FILTERING_ANISO_2X = 1; : m_game_layer(layer)
constexpr int TEXTURE_FILTERING_ANISO_4X = 2; {
constexpr int TEXTURE_FILTERING_ANISO_8X = 3; CreateWidgets();
constexpr int TEXTURE_FILTERING_ANISO_16X = 4; LoadPPShaders();
constexpr int TEXTURE_FILTERING_FORCE_NEAREST = 5; ConnectWidgets();
constexpr int TEXTURE_FILTERING_FORCE_LINEAR = 6; AddDescriptions();
constexpr int TEXTURE_FILTERING_FORCE_LINEAR_ANISO_2X = 7;
constexpr int TEXTURE_FILTERING_FORCE_LINEAR_ANISO_4X = 8; connect(&Settings::Instance(), &Settings::ConfigChanged, this,
constexpr int TEXTURE_FILTERING_FORCE_LINEAR_ANISO_8X = 9; &EnhancementsWidget::OnConfigChanged);
constexpr int TEXTURE_FILTERING_FORCE_LINEAR_ANISO_16X = 10; }
constexpr int ANISO_DEFAULT = 0;
constexpr int ANISO_2X = 1;
constexpr int ANISO_4X = 2;
constexpr int ANISO_8X = 3;
constexpr int ANISO_16X = 4;
constexpr int FILTERING_DEFAULT = 0;
constexpr int FILTERING_NEAREST = 1;
constexpr int FILTERING_LINEAR = 2;
void EnhancementsWidget::CreateWidgets() 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 // 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. // when the settings are saved we don't lose the user-modified value from the ini.
const int max_efb_scale = 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<int>(resolution_options.size()); scale <= max_efb_scale; scale++) for (int scale = static_cast<int>(resolution_options.size()); scale <= max_efb_scale; scale++)
{ {
const QString scale_text = QString::number(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_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 =
m_texture_filtering_combo->addItem(tr("Default"), TEXTURE_FILTERING_DEFAULT); new ConfigComplexChoice(Config::GFX_ENHANCE_MAX_ANISOTROPY,
m_texture_filtering_combo->addItem(tr("2x Anisotropic"), TEXTURE_FILTERING_ANISO_2X); Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING, m_game_layer);
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_output_resampling_combo = new ToolTipComboBox(); m_texture_filtering_combo->Add(tr("Default"), ANISO_DEFAULT, FILTERING_DEFAULT);
m_output_resampling_combo->addItem(tr("Default"), m_texture_filtering_combo->Add(tr("2x Anisotropic"), ANISO_2X, FILTERING_DEFAULT);
static_cast<int>(OutputResamplingMode::Default)); m_texture_filtering_combo->Add(tr("4x Anisotropic"), ANISO_4X, FILTERING_DEFAULT);
m_output_resampling_combo->addItem(tr("Bilinear"), m_texture_filtering_combo->Add(tr("8x Anisotropic"), ANISO_8X, FILTERING_DEFAULT);
static_cast<int>(OutputResamplingMode::Bilinear)); m_texture_filtering_combo->Add(tr("16x Anisotropic"), ANISO_16X, FILTERING_DEFAULT);
m_output_resampling_combo->addItem(tr("Bicubic: B-Spline"), m_texture_filtering_combo->Add(tr("Force Nearest"), ANISO_DEFAULT, FILTERING_NEAREST);
static_cast<int>(OutputResamplingMode::BSpline)); m_texture_filtering_combo->Add(tr("Force Linear"), ANISO_DEFAULT, FILTERING_LINEAR);
m_output_resampling_combo->addItem(tr("Bicubic: Mitchell-Netravali"), m_texture_filtering_combo->Add(tr("Force Linear and 2x Anisotropic"), ANISO_2X, FILTERING_LINEAR);
static_cast<int>(OutputResamplingMode::MitchellNetravali)); m_texture_filtering_combo->Add(tr("Force Linear and 4x Anisotropic"), ANISO_4X, FILTERING_LINEAR);
m_output_resampling_combo->addItem(tr("Bicubic: Catmull-Rom"), m_texture_filtering_combo->Add(tr("Force Linear and 8x Anisotropic"), ANISO_8X, FILTERING_LINEAR);
static_cast<int>(OutputResamplingMode::CatmullRom)); m_texture_filtering_combo->Add(tr("Force Linear and 16x Anisotropic"), ANISO_16X,
m_output_resampling_combo->addItem(tr("Sharp Bilinear"), FILTERING_LINEAR);
static_cast<int>(OutputResamplingMode::SharpBilinear)); m_texture_filtering_combo->Refresh();
m_output_resampling_combo->addItem(tr("Area Sampling"), m_texture_filtering_combo->setEnabled(ReadSetting(Config::GFX_HACK_FAST_TEXTURE_SAMPLING));
static_cast<int>(OutputResamplingMode::AreaSampling));
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_configure_color_correction = new ToolTipPushButton(tr("Configure"));
m_pp_effect = new ToolTipComboBox(); m_pp_effect = new ConfigStringChoice(VideoCommon::PostProcessing::GetShaderList(),
Config::GFX_ENHANCE_POST_SHADER, m_game_layer);
m_configure_pp_effect = new NonDefaultQPushButton(tr("Configure")); m_configure_pp_effect = new NonDefaultQPushButton(tr("Configure"));
m_scaled_efb_copy = new ConfigBool(tr("Scaled EFB Copy"), Config::GFX_HACK_COPY_EFB_SCALED); m_configure_pp_effect->setDisabled(true);
m_per_pixel_lighting =
new ConfigBool(tr("Per-Pixel Lighting"), Config::GFX_ENABLE_PIXEL_LIGHTING);
m_widescreen_hack = new ConfigBool(tr("Widescreen Hack"), Config::GFX_WIDESCREEN_HACK); m_scaled_efb_copy =
m_disable_fog = new ConfigBool(tr("Disable Fog"), Config::GFX_DISABLE_FOG); 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 = m_force_24bit_color =
new ConfigBool(tr("Force 24-Bit Color"), Config::GFX_ENHANCE_FORCE_TRUE_COLOR); new ConfigBool(tr("Force 24-Bit Color"), Config::GFX_ENHANCE_FORCE_TRUE_COLOR, m_game_layer);
m_disable_copy_filter = m_disable_copy_filter = new ConfigBool(tr("Disable Copy Filter"),
new ConfigBool(tr("Disable Copy Filter"), Config::GFX_ENHANCE_DISABLE_COPY_FILTER); Config::GFX_ENHANCE_DISABLE_COPY_FILTER, m_game_layer);
m_arbitrary_mipmap_detection = new ConfigBool(tr("Arbitrary Mipmap Detection"), m_arbitrary_mipmap_detection =
Config::GFX_ENHANCE_ARBITRARY_MIPMAP_DETECTION); new ConfigBool(tr("Arbitrary Mipmap Detection"),
m_hdr = new ConfigBool(tr("HDR Post-Processing"), Config::GFX_ENHANCE_HDR_OUTPUT); 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; int row = 0;
enhancements_layout->addWidget(new QLabel(tr("Internal Resolution:")), 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"), m_3d_mode = new ConfigChoice({tr("Off"), tr("Side-by-Side"), tr("Top-and-Bottom"), tr("Anaglyph"),
tr("HDMI 3D"), tr("Passive")}, tr("HDMI 3D"), tr("Passive")},
Config::GFX_STEREO_MODE); Config::GFX_STEREO_MODE, m_game_layer);
m_3d_depth = new ConfigSlider(0, Config::GFX_STEREO_DEPTH_MAXIMUM, Config::GFX_STEREO_DEPTH); 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, 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 = m_3d_per_eye_resolution = new ConfigBool(
new ConfigBool(tr("Use Full Resolution Per Eye"), Config::GFX_STEREO_PER_EYE_RESOLUTION_FULL); 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(new QLabel(tr("Stereoscopic 3D Mode:")), 0, 0);
stereoscopy_layout->addWidget(m_3d_mode, 0, 1); 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(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_convergence, 2, 1);
stereoscopy_layout->addWidget(m_3d_swap_eyes, 3, 0); stereoscopy_layout->addWidget(m_3d_swap_eyes, 3, 0);
stereoscopy_layout->addWidget(m_3d_per_eye_resolution, 4, 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) if (current_stereo_mode != StereoMode::SBS && current_stereo_mode != StereoMode::TAB)
m_3d_per_eye_resolution->hide(); m_3d_per_eye_resolution->hide();
@ -241,58 +256,53 @@ void EnhancementsWidget::CreateWidgets()
void EnhancementsWidget::ConnectWidgets() 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] { connect(m_3d_mode, &QComboBox::currentIndexChanged, [this] {
m_block_save = true; auto current_stereo_mode = ReadSetting(Config::GFX_STEREO_MODE);
m_configure_color_correction->setEnabled(g_Config.backend_info.bSupportsPostProcessing); LoadPPShaders();
auto current_stereo_mode = Config::Get(Config::GFX_STEREO_MODE);
LoadPPShaders(current_stereo_mode);
if (current_stereo_mode == StereoMode::SBS || current_stereo_mode == StereoMode::TAB) if (current_stereo_mode == StereoMode::SBS || current_stereo_mode == StereoMode::TAB)
m_3d_per_eye_resolution->show(); m_3d_per_eye_resolution->show();
else else
m_3d_per_eye_resolution->hide(); 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, connect(m_configure_color_correction, &QPushButton::clicked, this,
&EnhancementsWidget::ConfigureColorCorrection); &EnhancementsWidget::ConfigureColorCorrection);
connect(m_configure_pp_effect, &QPushButton::clicked, this, connect(m_configure_pp_effect, &QPushButton::clicked, this,
&EnhancementsWidget::ConfigurePostProcessingShader); &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 <typename T>
T EnhancementsWidget::ReadSetting(const Config::Info<T>& setting) const
{ {
std::vector<std::string> shaders = VideoCommon::PostProcessing::GetShaderList(); if (m_game_layer != nullptr)
if (stereo_mode == StereoMode::Anaglyph) return m_game_layer->Get(setting);
{ else
shaders = VideoCommon::PostProcessing::GetAnaglyphShaderList(); return Config::Get(setting);
} }
else if (stereo_mode == StereoMode::Passive)
{
shaders = VideoCommon::PostProcessing::GetPassiveShaderList();
}
void EnhancementsWidget::LoadPPShaders()
{
auto stereo_mode = ReadSetting(Config::GFX_STEREO_MODE);
const QSignalBlocker blocker(m_pp_effect);
m_pp_effect->clear(); m_pp_effect->clear();
// Get shader list
std::vector<std::string> 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) if (stereo_mode != StereoMode::Anaglyph && stereo_mode != StereoMode::Passive)
m_pp_effect->addItem(tr("(off)")); 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; bool found = false;
@ -306,6 +316,7 @@ void EnhancementsWidget::LoadPPShaders(StereoMode stereo_mode)
} }
} }
// Force a shader for StereoModes that require it.
if (!found) if (!found)
{ {
if (stereo_mode == StereoMode::Anaglyph) if (stereo_mode == StereoMode::Anaglyph)
@ -321,103 +332,20 @@ void EnhancementsWidget::LoadPPShaders(StereoMode stereo_mode)
else else
m_pp_effect->setCurrentIndex(0); 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->Load();
m_pp_effect->setEnabled(supports_postprocessing); ShaderChanged();
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);
}
} }
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<int>(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<int>(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<int>(output_resampling_mode)));
m_output_resampling_combo->setEnabled(g_Config.backend_info.bSupportsPostProcessing); m_output_resampling_combo->setEnabled(g_Config.backend_info.bSupportsPostProcessing);
// Color Correction
m_configure_color_correction->setEnabled(g_Config.backend_info.bSupportsPostProcessing); m_configure_color_correction->setEnabled(g_Config.backend_info.bSupportsPostProcessing);
m_hdr->setEnabled(g_Config.backend_info.bSupportsHDROutput);
// Post Processing Shader
LoadPPShaders(Config::Get(Config::GFX_STEREO_MODE));
// Stereoscopy // Stereoscopy
const bool supports_stereoscopy = g_Config.backend_info.bSupportsGeometryShaders; const bool supports_stereoscopy = g_Config.backend_info.bSupportsGeometryShaders;
@ -426,105 +354,98 @@ void EnhancementsWidget::LoadSettings()
m_3d_depth->setEnabled(supports_stereoscopy); m_3d_depth->setEnabled(supports_stereoscopy);
m_3d_swap_eyes->setEnabled(supports_stereoscopy); m_3d_swap_eyes->setEnabled(supports_stereoscopy);
m_hdr->setEnabled(g_Config.backend_info.bSupportsHDROutput); // PostProcessing
const bool supports_postprocessing = g_Config.backend_info.bSupportsPostProcessing;
m_block_save = false; if (!supports_postprocessing)
}
void EnhancementsWidget::SaveSettings()
{
if (m_block_save)
return;
const u32 aa_value = static_cast<u32>(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)
{ {
case TEXTURE_FILTERING_DEFAULT: m_configure_pp_effect->setEnabled(false);
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 0); m_pp_effect->setEnabled(false);
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING, m_pp_effect->setToolTip(
TextureFilteringMode::Default); tr("%1 doesn't support this feature.").arg(tr(g_video_backend->GetDisplayName().c_str())));
break; }
case TEXTURE_FILTERING_ANISO_2X: else if (!m_pp_effect->isEnabled() && supports_postprocessing)
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_MAX_ANISOTROPY, 1); {
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_FORCE_TEXTURE_FILTERING, m_configure_pp_effect->setEnabled(true);
TextureFilteringMode::Default); m_pp_effect->setEnabled(true);
break; m_pp_effect->setToolTip(QString{});
case TEXTURE_FILTERING_ANISO_4X: LoadPPShaders();
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;
} }
const int output_resampling_selection = m_output_resampling_combo->currentData().toInt(); UpdateAAOptions();
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_OUTPUT_RESAMPLING, }
static_cast<OutputResamplingMode>(output_resampling_selection));
const bool anaglyph = g_Config.stereo_mode == StereoMode::Anaglyph; void EnhancementsWidget::ShaderChanged()
const bool passive = g_Config.stereo_mode == StereoMode::Passive; {
Config::SetBaseOrCurrent(Config::GFX_ENHANCE_POST_SHADER, auto shader = ReadSetting(Config::GFX_ENHANCE_POST_SHADER);
(!anaglyph && !passive && m_pp_effect->currentIndex() == 0) ?
"" :
m_pp_effect->currentText().toStdString());
VideoCommon::PostProcessingConfiguration pp_shader; if (shader == "(off)" || shader == "")
if (Config::Get(Config::GFX_ENHANCE_POST_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()); m_configure_pp_effect->setEnabled(pp_shader.HasOptions());
} }
else else
{ {
m_configure_pp_effect->setEnabled(false); 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<u32> 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() void EnhancementsWidget::AddDescriptions()
@ -713,7 +634,7 @@ void EnhancementsWidget::ConfigureColorCorrection()
void EnhancementsWidget::ConfigurePostProcessingShader() 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); PostProcessingConfigWindow dialog(this, shader);
SetQWidgetWindowDecorations(&dialog); SetQWidgetWindowDecorations(&dialog);
dialog.exec(); dialog.exec();

View File

@ -9,39 +9,52 @@
class ConfigBool; class ConfigBool;
class ConfigChoice; class ConfigChoice;
class ConfigComplexChoice;
class ConfigStringChoice;
class ConfigSlider; class ConfigSlider;
class GameConfigWidget;
class GraphicsWindow; class GraphicsWindow;
class QCheckBox;
class QComboBox;
class QPushButton; class QPushButton;
class QSlider;
class ToolTipComboBox;
class ToolTipPushButton; class ToolTipPushButton;
enum class StereoMode : int; enum class StereoMode : int;
namespace Config
{
template <typename T>
class Info;
class Layer;
} // namespace Config
class EnhancementsWidget final : public QWidget class EnhancementsWidget final : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit EnhancementsWidget(GraphicsWindow* parent); explicit EnhancementsWidget(GraphicsWindow* parent);
EnhancementsWidget(GameConfigWidget* parent, Config::Layer* layer);
private: private:
void LoadSettings(); template <typename T>
void SaveSettings(); T ReadSetting(const Config::Info<T>& setting) const;
void CreateWidgets(); void CreateWidgets();
void ConnectWidgets(); void ConnectWidgets();
void AddDescriptions(); void AddDescriptions();
void OnBackendChanged();
void UpdateAAOptions();
void LoadPPShaders();
void ShaderChanged();
void OnConfigChanged();
void ConfigureColorCorrection(); void ConfigureColorCorrection();
void ConfigurePostProcessingShader(); void ConfigurePostProcessingShader();
void LoadPPShaders(StereoMode stereo_mode);
// Enhancements // Enhancements
ConfigChoice* m_ir_combo; ConfigChoice* m_ir_combo;
ToolTipComboBox* m_aa_combo; ConfigComplexChoice* m_aa_combo;
ToolTipComboBox* m_texture_filtering_combo; ConfigComplexChoice* m_texture_filtering_combo;
ToolTipComboBox* m_output_resampling_combo; ConfigChoice* m_output_resampling_combo;
ToolTipComboBox* m_pp_effect; ConfigStringChoice* m_pp_effect;
ToolTipPushButton* m_configure_color_correction; ToolTipPushButton* m_configure_color_correction;
QPushButton* m_configure_pp_effect; QPushButton* m_configure_pp_effect;
ConfigBool* m_scaled_efb_copy; ConfigBool* m_scaled_efb_copy;
@ -60,6 +73,5 @@ private:
ConfigBool* m_3d_swap_eyes; ConfigBool* m_3d_swap_eyes;
ConfigBool* m_3d_per_eye_resolution; ConfigBool* m_3d_per_eye_resolution;
int m_msaa_modes; Config::Layer* m_game_layer = nullptr;
bool m_block_save;
}; };

View File

@ -23,6 +23,7 @@
#include "DolphinQt/Config/ConfigControls/ConfigChoice.h" #include "DolphinQt/Config/ConfigControls/ConfigChoice.h"
#include "DolphinQt/Config/ConfigControls/ConfigInteger.h" #include "DolphinQt/Config/ConfigControls/ConfigInteger.h"
#include "DolphinQt/Config/ConfigControls/ConfigRadio.h" #include "DolphinQt/Config/ConfigControls/ConfigRadio.h"
#include "DolphinQt/Config/GameConfigWidget.h"
#include "DolphinQt/Config/Graphics/GraphicsWindow.h" #include "DolphinQt/Config/Graphics/GraphicsWindow.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipComboBox.h" #include "DolphinQt/Config/ToolTipControls/ToolTipComboBox.h"
#include "DolphinQt/QtUtils/ModalMessageBox.h" #include "DolphinQt/QtUtils/ModalMessageBox.h"
@ -38,7 +39,6 @@ GeneralWidget::GeneralWidget(GraphicsWindow* parent)
LoadSettings(); LoadSettings();
ConnectWidgets(); ConnectWidgets();
AddDescriptions(); AddDescriptions();
emit BackendChanged(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND)));
connect(parent, &GraphicsWindow::BackendChanged, this, &GeneralWidget::OnBackendChanged); connect(parent, &GraphicsWindow::BackendChanged, this, &GeneralWidget::OnBackendChanged);
connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) { connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this](Core::State state) {
@ -47,6 +47,14 @@ GeneralWidget::GeneralWidget(GraphicsWindow* parent)
OnEmulationStateChanged(!Core::IsUninitialized(Core::System::GetInstance())); OnEmulationStateChanged(!Core::IsUninitialized(Core::System::GetInstance()));
} }
GeneralWidget::GeneralWidget(GameConfigWidget* parent, Config::Layer* layer) : m_game_layer(layer)
{
CreateWidgets();
LoadSettings();
ConnectWidgets();
AddDescriptions();
}
void GeneralWidget::CreateWidgets() void GeneralWidget::CreateWidgets()
{ {
auto* main_layout = new QVBoxLayout; auto* main_layout = new QVBoxLayout;
@ -55,33 +63,36 @@ void GeneralWidget::CreateWidgets()
auto* m_video_box = new QGroupBox(tr("Basic")); auto* m_video_box = new QGroupBox(tr("Basic"));
m_video_layout = new QGridLayout(); m_video_layout = new QGridLayout();
m_backend_combo = new ToolTipComboBox(); std::vector<std::pair<QString, QString>> 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"), m_aspect_combo = new ConfigChoice({tr("Auto"), tr("Force 16:9"), tr("Force 4:3"),
tr("Stretch to Window"), tr("Custom"), tr("Custom (Stretch)")}, 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 = new QLabel(tr("Custom Aspect Ratio:"));
m_custom_aspect_label->setHidden(true); m_custom_aspect_label->setHidden(true);
constexpr int MAX_CUSTOM_ASPECT_RATIO_RESOLUTION = 10000; constexpr int MAX_CUSTOM_ASPECT_RATIO_RESOLUTION = 10000;
m_custom_aspect_width = new ConfigInteger(1, MAX_CUSTOM_ASPECT_RATIO_RESOLUTION, 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->setEnabled(false);
m_custom_aspect_width->setHidden(true); m_custom_aspect_width->setHidden(true);
m_custom_aspect_height = new ConfigInteger(1, MAX_CUSTOM_ASPECT_RATIO_RESOLUTION, 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->setEnabled(false);
m_custom_aspect_height->setHidden(true); m_custom_aspect_height->setHidden(true);
m_adapter_combo = new ToolTipComboBox; m_adapter_combo = new ToolTipComboBox;
m_enable_vsync = new ConfigBool(tr("V-Sync"), Config::GFX_VSYNC); 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_enable_fullscreen =
new ConfigBool(tr("Start in Fullscreen"), Config::MAIN_FULLSCREEN, m_game_layer);
m_video_box->setLayout(m_video_layout); 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(new QLabel(tr("Backend:")), 0, 0);
m_video_layout->addWidget(m_backend_combo, 0, 1, 1, -1); 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_box = new QGroupBox(tr("Other"));
auto* m_options_layout = new QGridLayout(); auto* m_options_layout = new QGridLayout();
m_show_ping = new ConfigBool(tr("Show NetPlay Ping"), Config::GFX_SHOW_NETPLAY_PING); m_show_ping =
m_autoadjust_window_size = new ConfigBool(tr("Show NetPlay Ping"), Config::GFX_SHOW_NETPLAY_PING, m_game_layer);
new ConfigBool(tr("Auto-Adjust Window Size"), Config::MAIN_RENDER_WINDOW_AUTOSIZE); m_autoadjust_window_size = new ConfigBool(tr("Auto-Adjust Window Size"),
m_show_messages = new ConfigBool(tr("Show NetPlay Messages"), Config::GFX_SHOW_NETPLAY_MESSAGES); Config::MAIN_RENDER_WINDOW_AUTOSIZE, m_game_layer);
m_render_main_window = new ConfigBool(tr("Render to Main Window"), Config::MAIN_RENDER_TO_MAIN); 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); m_options_box->setLayout(m_options_layout);
@ -128,13 +142,13 @@ void GeneralWidget::CreateWidgets()
}}; }};
for (size_t i = 0; i < modes.size(); i++) for (size_t i = 0; i < modes.size(); i++)
{ {
m_shader_compilation_mode[i] = m_shader_compilation_mode[i] = new ConfigRadioInt(
new ConfigRadioInt(tr(modes[i]), Config::GFX_SHADER_COMPILATION_MODE, static_cast<int>(i)); tr(modes[i]), Config::GFX_SHADER_COMPILATION_MODE, static_cast<int>(i), m_game_layer);
shader_compilation_layout->addWidget(m_shader_compilation_mode[i], static_cast<int>(i / 2), shader_compilation_layout->addWidget(m_shader_compilation_mode[i], static_cast<int>(i / 2),
static_cast<int>(i % 2)); static_cast<int>(i % 2));
} }
m_wait_for_shaders = new ConfigBool(tr("Compile Shaders Before Starting"), 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_layout->addWidget(m_wait_for_shaders);
shader_compilation_box->setLayout(shader_compilation_layout); shader_compilation_box->setLayout(shader_compilation_layout);
@ -149,7 +163,7 @@ void GeneralWidget::CreateWidgets()
void GeneralWidget::ConnectWidgets() void GeneralWidget::ConnectWidgets()
{ {
// Video Backend // 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) { connect(m_adapter_combo, &QComboBox::currentIndexChanged, this, [&](int index) {
g_Config.iAdapter = index; g_Config.iAdapter = index;
Config::SetBaseOrCurrent(Config::GFX_ADAPTER, index); Config::SetBaseOrCurrent(Config::GFX_ADAPTER, index);
@ -168,10 +182,6 @@ void GeneralWidget::ConnectWidgets()
void GeneralWidget::LoadSettings() 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 = const bool is_custom_aspect_ratio =
(Config::Get(Config::GFX_ASPECT_RATIO) == AspectMode::Custom) || (Config::Get(Config::GFX_ASPECT_RATIO) == AspectMode::Custom) ||
(Config::Get(Config::GFX_ASPECT_RATIO) == AspectMode::CustomStretch); (Config::Get(Config::GFX_ASPECT_RATIO) == AspectMode::CustomStretch);
@ -182,13 +192,8 @@ void GeneralWidget::LoadSettings()
m_custom_aspect_height->setHidden(!is_custom_aspect_ratio); 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) if (Config::GetActiveLayerForConfig(Config::MAIN_GFX_BACKEND) == Config::LayerType::Base)
{ {
auto warningMessage = VideoBackendBase::GetAvailableBackends()[m_backend_combo->currentIndex()] auto warningMessage = VideoBackendBase::GetAvailableBackends()[m_backend_combo->currentIndex()]
@ -205,15 +210,14 @@ void GeneralWidget::SaveSettings()
SetQWidgetWindowDecorations(&confirm_sw); SetQWidgetWindowDecorations(&confirm_sw);
if (confirm_sw.exec() != QMessageBox::Yes) if (confirm_sw.exec() != QMessageBox::Yes)
{ {
m_backend_combo->setCurrentIndex(m_backend_combo->findData( m_backend_combo->setCurrentIndex(m_previous_backend);
QVariant(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND)))));
return; return;
} }
} }
} }
Config::SetBaseOrCurrent(Config::MAIN_GFX_BACKEND, current_backend); m_previous_backend = m_backend_combo->currentIndex();
emit BackendChanged(QString::fromStdString(current_backend)); emit BackendChanged(m_backend_combo->currentData().toString());
} }
void GeneralWidget::OnEmulationStateChanged(bool running) void GeneralWidget::OnEmulationStateChanged(bool running)
@ -227,7 +231,10 @@ void GeneralWidget::OnEmulationStateChanged(bool running)
std::string current_backend = m_backend_combo->currentData().toString().toStdString(); std::string current_backend = m_backend_combo->currentData().toString().toStdString();
if (Config::Get(Config::MAIN_GFX_BACKEND) != current_backend) if (Config::Get(Config::MAIN_GFX_BACKEND) != current_backend)
{
m_backend_combo->Load();
emit BackendChanged(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND))); emit BackendChanged(QString::fromStdString(Config::Get(Config::MAIN_GFX_BACKEND)));
}
} }
void GeneralWidget::AddDescriptions() void GeneralWidget::AddDescriptions()
@ -348,8 +355,6 @@ void GeneralWidget::AddDescriptions()
void GeneralWidget::OnBackendChanged(const QString& backend_name) void GeneralWidget::OnBackendChanged(const QString& backend_name)
{ {
m_backend_combo->setCurrentIndex(m_backend_combo->findData(QVariant(backend_name)));
const QSignalBlocker blocker(m_adapter_combo); const QSignalBlocker blocker(m_adapter_combo);
m_adapter_combo->clear(); m_adapter_combo->clear();

View File

@ -11,6 +11,8 @@ class ConfigBool;
class ConfigChoice; class ConfigChoice;
class ConfigInteger; class ConfigInteger;
class ConfigRadioInt; class ConfigRadioInt;
class ConfigStringChoice;
class GameConfigWidget;
class GraphicsWindow; class GraphicsWindow;
class QCheckBox; class QCheckBox;
class QComboBox; class QComboBox;
@ -19,17 +21,24 @@ class QRadioButton;
class QGridLayout; class QGridLayout;
class ToolTipComboBox; class ToolTipComboBox;
namespace Config
{
class Layer;
} // namespace Config
class GeneralWidget final : public QWidget class GeneralWidget final : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit GeneralWidget(GraphicsWindow* parent); explicit GeneralWidget(GraphicsWindow* parent);
GeneralWidget(GameConfigWidget* parent, Config::Layer* layer);
signals: signals:
void BackendChanged(const QString& backend); void BackendChanged(const QString& backend);
private: private:
void LoadSettings(); void LoadSettings();
void SaveSettings(); void BackendWarning();
void CreateWidgets(); void CreateWidgets();
void ConnectWidgets(); void ConnectWidgets();
@ -40,7 +49,7 @@ private:
// Video // Video
QGridLayout* m_video_layout; QGridLayout* m_video_layout;
ToolTipComboBox* m_backend_combo; ConfigStringChoice* m_backend_combo;
ToolTipComboBox* m_adapter_combo; ToolTipComboBox* m_adapter_combo;
ConfigChoice* m_aspect_combo; ConfigChoice* m_aspect_combo;
QLabel* m_custom_aspect_label; QLabel* m_custom_aspect_label;
@ -56,4 +65,6 @@ private:
ConfigBool* m_render_main_window; ConfigBool* m_render_main_window;
std::array<ConfigRadioInt*, 4> m_shader_compilation_mode{}; std::array<ConfigRadioInt*, 4> m_shader_compilation_mode{};
ConfigBool* m_wait_for_shaders; ConfigBool* m_wait_for_shaders;
int m_previous_backend = 0;
Config::Layer* m_game_layer = nullptr;
}; };

View File

@ -15,6 +15,7 @@
#include "DolphinQt/Config/ConfigControls/ConfigBool.h" #include "DolphinQt/Config/ConfigControls/ConfigBool.h"
#include "DolphinQt/Config/ConfigControls/ConfigSlider.h" #include "DolphinQt/Config/ConfigControls/ConfigSlider.h"
#include "DolphinQt/Config/GameConfigWidget.h"
#include "DolphinQt/Config/Graphics/GraphicsWindow.h" #include "DolphinQt/Config/Graphics/GraphicsWindow.h"
#include "DolphinQt/Config/ToolTipControls/ToolTipSlider.h" #include "DolphinQt/Config/ToolTipControls/ToolTipSlider.h"
#include "DolphinQt/Settings.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() void HacksWidget::CreateWidgets()
{ {
auto* main_layout = new QVBoxLayout; auto* main_layout = new QVBoxLayout;
@ -45,14 +54,14 @@ void HacksWidget::CreateWidgets()
auto* efb_box = new QGroupBox(tr("Embedded Frame Buffer (EFB)")); auto* efb_box = new QGroupBox(tr("Embedded Frame Buffer (EFB)"));
auto* efb_layout = new QGridLayout(); auto* efb_layout = new QGridLayout();
efb_box->setLayout(efb_layout); efb_box->setLayout(efb_layout);
m_skip_efb_cpu = m_skip_efb_cpu = new ConfigBool(tr("Skip EFB Access from CPU"),
new ConfigBool(tr("Skip EFB Access from CPU"), Config::GFX_HACK_EFB_ACCESS_ENABLE, true); Config::GFX_HACK_EFB_ACCESS_ENABLE, m_game_layer, true);
m_ignore_format_changes = new ConfigBool(tr("Ignore Format Changes"), m_ignore_format_changes = new ConfigBool(
Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES, true); tr("Ignore Format Changes"), Config::GFX_HACK_EFB_EMULATE_FORMAT_CHANGES, m_game_layer, true);
m_store_efb_copies = m_store_efb_copies = new ConfigBool(tr("Store EFB Copies to Texture Only"),
new ConfigBool(tr("Store EFB Copies to Texture Only"), Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM); Config::GFX_HACK_SKIP_EFB_COPY_TO_RAM, m_game_layer);
m_defer_efb_copies = m_defer_efb_copies = new ConfigBool(tr("Defer EFB Copies to RAM"),
new ConfigBool(tr("Defer EFB Copies to RAM"), Config::GFX_HACK_DEFER_EFB_COPIES); Config::GFX_HACK_DEFER_EFB_COPIES, m_game_layer);
efb_layout->addWidget(m_skip_efb_cpu, 0, 0); efb_layout->addWidget(m_skip_efb_cpu, 0, 0);
efb_layout->addWidget(m_ignore_format_changes, 0, 1); efb_layout->addWidget(m_ignore_format_changes, 0, 1);
@ -69,8 +78,8 @@ void HacksWidget::CreateWidgets()
m_accuracy->setMaximum(2); m_accuracy->setMaximum(2);
m_accuracy->setPageStep(1); m_accuracy->setPageStep(1);
m_accuracy->setTickPosition(QSlider::TicksBelow); m_accuracy->setTickPosition(QSlider::TicksBelow);
m_gpu_texture_decoding = m_gpu_texture_decoding = new ConfigBool(tr("GPU Texture Decoding"),
new ConfigBool(tr("GPU Texture Decoding"), Config::GFX_ENABLE_GPU_TEXTURE_DECODING); Config::GFX_ENABLE_GPU_TEXTURE_DECODING, m_game_layer);
auto* safe_label = new QLabel(tr("Safe")); auto* safe_label = new QLabel(tr("Safe"));
safe_label->setAlignment(Qt::AlignRight); safe_label->setAlignment(Qt::AlignRight);
@ -88,11 +97,12 @@ void HacksWidget::CreateWidgets()
auto* xfb_layout = new QVBoxLayout(); auto* xfb_layout = new QVBoxLayout();
xfb_box->setLayout(xfb_layout); xfb_box->setLayout(xfb_layout);
m_store_xfb_copies = m_store_xfb_copies = new ConfigBool(tr("Store XFB Copies to Texture Only"),
new ConfigBool(tr("Store XFB Copies to Texture Only"), Config::GFX_HACK_SKIP_XFB_COPY_TO_RAM); 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_immediate_xfb =
m_skip_duplicate_xfbs = new ConfigBool(tr("Immediately Present XFB"), Config::GFX_HACK_IMMEDIATE_XFB, m_game_layer);
new ConfigBool(tr("Skip Presenting Duplicate Frames"), Config::GFX_HACK_SKIP_DUPLICATE_XFBS); 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_store_xfb_copies);
xfb_layout->addWidget(m_immediate_xfb); xfb_layout->addWidget(m_immediate_xfb);
@ -104,13 +114,14 @@ void HacksWidget::CreateWidgets()
other_box->setLayout(other_layout); other_box->setLayout(other_layout);
m_fast_depth_calculation = 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 = m_disable_bounding_box =
new ConfigBool(tr("Disable Bounding Box"), Config::GFX_HACK_BBOX_ENABLE, true); 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_vertex_rounding =
m_save_texture_cache_state = new ConfigBool(tr("Vertex Rounding"), Config::GFX_HACK_VERTEX_ROUNDING, m_game_layer);
new ConfigBool(tr("Save Texture Cache to State"), Config::GFX_SAVE_TEXTURE_CACHE_TO_STATE); m_save_texture_cache_state = new ConfigBool(
m_vi_skip = new ConfigBool(tr("VBI Skip"), Config::GFX_HACK_VI_SKIP); 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_fast_depth_calculation, 0, 0);
other_layout->addWidget(m_disable_bounding_box, 0, 1); other_layout->addWidget(m_disable_bounding_box, 0, 1);

View File

@ -6,15 +6,22 @@
#include <QWidget> #include <QWidget>
class ConfigBool; class ConfigBool;
class GameConfigWidget;
class GraphicsWindow; class GraphicsWindow;
class QLabel; class QLabel;
class ToolTipSlider; class ToolTipSlider;
namespace Config
{
class Layer;
} // namespace Config
class HacksWidget final : public QWidget class HacksWidget final : public QWidget
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit HacksWidget(GraphicsWindow* parent); explicit HacksWidget(GraphicsWindow* parent);
HacksWidget(GameConfigWidget* parent, Config::Layer* layer);
private: private:
void LoadSettings(); void LoadSettings();
@ -45,6 +52,8 @@ private:
ConfigBool* m_vi_skip; ConfigBool* m_vi_skip;
ConfigBool* m_save_texture_cache_state; ConfigBool* m_save_texture_cache_state;
Config::Layer* m_game_layer = nullptr;
void CreateWidgets(); void CreateWidgets();
void ConnectWidgets(); void ConnectWidgets();
void AddDescriptions(); void AddDescriptions();

View File

@ -241,6 +241,7 @@
--> -->
<ItemGroup> <ItemGroup>
<ClInclude Include="Config\CheatCodeEditor.h" /> <ClInclude Include="Config\CheatCodeEditor.h" />
<ClInclude Include="Config\ConfigControls\ConfigControl.h" />
<ClInclude Include="Config\GameConfigEdit.h" /> <ClInclude Include="Config\GameConfigEdit.h" />
<ClInclude Include="Config\Mapping\MappingCommon.h" /> <ClInclude Include="Config\Mapping\MappingCommon.h" />
<ClInclude Include="Config\Mapping\MappingIndicator.h" /> <ClInclude Include="Config\Mapping\MappingIndicator.h" />

View File

@ -546,13 +546,13 @@ void GameList::OpenProperties()
return; return;
PropertiesDialog* properties = new PropertiesDialog(this, *game); 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::OpenGeneralSettings, this, &GameList::OpenGeneralSettings);
connect(properties, &PropertiesDialog::OpenGraphicsSettings, this, connect(properties, &PropertiesDialog::OpenGraphicsSettings, this,
&GameList::OpenGraphicsSettings); &GameList::OpenGraphicsSettings);
connect(properties, &PropertiesDialog::finished, this,
[properties]() { properties->deleteLater(); });
#ifdef USE_RETRO_ACHIEVEMENTS #ifdef USE_RETRO_ACHIEVEMENTS
connect(properties, &PropertiesDialog::OpenAchievementSettings, this, connect(properties, &PropertiesDialog::OpenAchievementSettings, this,
&GameList::OpenAchievementSettings); &GameList::OpenAchievementSettings);