Qt: Add per-game settings UI

This commit is contained in:
Connor McLaughlin 2022-02-16 00:29:18 +10:00 committed by lightningterror
parent 48dabd3ca2
commit b47552c08f
24 changed files with 1396 additions and 732 deletions

View File

@ -41,10 +41,10 @@
#include "Settings/InterfaceSettingsWidget.h"
#include "svnrev.h"
static constexpr char DISC_IMAGE_FILTER[] = QT_TRANSLATE_NOOP(
"MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.elf *.irx *.m3u);;Single-Track Raw Images (*.bin "
"*.iso);;Cue Sheets (*.cue);;MAME CHD Images (*.chd);;CSO Images (*.cso);;"
"ELF Executables (*.elf);;IRX Executables (*.irx);;Playlists (*.m3u)");
static constexpr char DISC_IMAGE_FILTER[] =
QT_TRANSLATE_NOOP("MainWindow", "All File Types (*.bin *.iso *.cue *.chd *.cso *.elf *.irx *.m3u);;Single-Track Raw Images (*.bin "
"*.iso);;Cue Sheets (*.cue);;MAME CHD Images (*.chd);;CSO Images (*.cso);;"
"ELF Executables (*.elf);;IRX Executables (*.irx);;Playlists (*.m3u)");
const char* MainWindow::DEFAULT_THEME_NAME = "darkfusion";
@ -106,8 +106,7 @@ void MainWindow::setupAdditionalUi()
for (u32 scale = 0; scale <= 10; scale++)
{
QAction* action =
m_ui.menuWindowSize->addAction((scale == 0) ? tr("Internal Resolution") : tr("%1x Scale").arg(scale));
QAction* action = m_ui.menuWindowSize->addAction((scale == 0) ? tr("Internal Resolution") : tr("%1x Scale").arg(scale));
connect(action, &QAction::triggered, [scale]() { g_emu_thread->requestDisplaySize(static_cast<float>(scale)); });
}
@ -120,10 +119,8 @@ void MainWindow::connectSignals()
connect(m_ui.actionStartBios, &QAction::triggered, this, &MainWindow::onStartBIOSActionTriggered);
connect(m_ui.actionChangeDisc, &QAction::triggered, [this] { m_ui.menuChangeDisc->exec(QCursor::pos()); });
connect(m_ui.actionChangeDiscFromFile, &QAction::triggered, this, &MainWindow::onChangeDiscFromFileActionTriggered);
connect(m_ui.actionChangeDiscFromDevice, &QAction::triggered, this,
&MainWindow::onChangeDiscFromDeviceActionTriggered);
connect(m_ui.actionChangeDiscFromGameList, &QAction::triggered, this,
&MainWindow::onChangeDiscFromGameListActionTriggered);
connect(m_ui.actionChangeDiscFromDevice, &QAction::triggered, this, &MainWindow::onChangeDiscFromDeviceActionTriggered);
connect(m_ui.actionChangeDiscFromGameList, &QAction::triggered, this, &MainWindow::onChangeDiscFromGameListActionTriggered);
connect(m_ui.menuChangeDisc, &QMenu::aboutToShow, this, &MainWindow::onChangeDiscMenuAboutToShow);
connect(m_ui.menuChangeDisc, &QMenu::aboutToHide, this, &MainWindow::onChangeDiscMenuAboutToHide);
connect(m_ui.actionPowerOff, &QAction::triggered, []() { g_emu_thread->shutdownVM(); });
@ -132,29 +129,20 @@ void MainWindow::connectSignals()
connect(m_ui.actionExit, &QAction::triggered, this, &MainWindow::close);
connect(m_ui.menuLoadState, &QMenu::aboutToShow, this, &MainWindow::onLoadStateMenuAboutToShow);
connect(m_ui.menuSaveState, &QMenu::aboutToShow, this, &MainWindow::onSaveStateMenuAboutToShow);
connect(m_ui.actionSettings, &QAction::triggered, [this]() { doSettings(SettingsDialog::Category::Count); });
connect(m_ui.actionInterfaceSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::InterfaceSettings); });
connect(m_ui.actionGameListSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::GameListSettings); });
connect(m_ui.actionEmulationSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::EmulationSettings); });
connect(m_ui.actionBIOSSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::BIOSSettings); });
connect(m_ui.actionSystemSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::SystemSettings); });
connect(m_ui.actionGraphicsSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::GraphicsSettings); });
connect(m_ui.actionAudioSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::AudioSettings); });
connect(m_ui.actionMemoryCardSettings, &QAction::triggered,
[this]() { doSettings(SettingsDialog::Category::MemoryCardSettings); });
connect(m_ui.actionControllerSettings, &QAction::triggered,
[this]() { doControllerSettings(ControllerSettingsDialog::Category::GlobalSettings); });
connect(m_ui.actionHotkeySettings, &QAction::triggered,
[this]() { doControllerSettings(ControllerSettingsDialog::Category::HotkeySettings); });
connect(m_ui.actionAddGameDirectory, &QAction::triggered,
[this]() { getSettingsDialog()->getGameListSettingsWidget()->addSearchDirectory(this); });
connect(m_ui.actionSettings, &QAction::triggered, [this]() { doSettings(); });
connect(m_ui.actionInterfaceSettings, &QAction::triggered, [this]() { doSettings("Interface"); });
connect(m_ui.actionGameListSettings, &QAction::triggered, [this]() { doSettings("Game List"); });
connect(m_ui.actionEmulationSettings, &QAction::triggered, [this]() { doSettings("Emulation"); });
connect(m_ui.actionBIOSSettings, &QAction::triggered, [this]() { doSettings("BIOS"); });
connect(m_ui.actionSystemSettings, &QAction::triggered, [this]() { doSettings("System"); });
connect(m_ui.actionGraphicsSettings, &QAction::triggered, [this]() { doSettings("Graphics"); });
connect(m_ui.actionAudioSettings, &QAction::triggered, [this]() { doSettings("Audio"); });
connect(m_ui.actionMemoryCardSettings, &QAction::triggered, [this]() { doSettings("Memory Card"); });
connect(
m_ui.actionControllerSettings, &QAction::triggered, [this]() { doControllerSettings(ControllerSettingsDialog::Category::GlobalSettings); });
connect(m_ui.actionHotkeySettings, &QAction::triggered, [this]() { doControllerSettings(ControllerSettingsDialog::Category::HotkeySettings); });
connect(
m_ui.actionAddGameDirectory, &QAction::triggered, [this]() { getSettingsDialog()->getGameListSettingsWidget()->addSearchDirectory(this); });
connect(m_ui.actionScanForNewGames, &QAction::triggered, [this]() { refreshGameList(false); });
connect(m_ui.actionRescanAllGames, &QAction::triggered, [this]() { refreshGameList(true); });
connect(m_ui.actionViewToolbar, &QAction::toggled, this, &MainWindow::onViewToolbarActionToggled);
@ -180,26 +168,22 @@ void MainWindow::connectSignals()
if (isShowingGameList())
m_game_list_widget->gridZoomOut();
});
connect(m_ui.actionGridViewRefreshCovers, &QAction::triggered, m_game_list_widget,
&GameListWidget::refreshGridCovers);
connect(m_ui.actionGridViewRefreshCovers, &QAction::triggered, m_game_list_widget, &GameListWidget::refreshGridCovers);
// These need to be queued connections to stop crashing due to menus opening/closing and switching focus.
connect(m_game_list_widget, &GameListWidget::refreshProgress, this, &MainWindow::onGameListRefreshProgress);
connect(m_game_list_widget, &GameListWidget::refreshComplete, this, &MainWindow::onGameListRefreshComplete);
connect(m_game_list_widget, &GameListWidget::selectionChanged, this, &MainWindow::onGameListSelectionChanged,
Qt::QueuedConnection);
connect(m_game_list_widget, &GameListWidget::entryActivated, this, &MainWindow::onGameListEntryActivated,
Qt::QueuedConnection);
connect(m_game_list_widget, &GameListWidget::entryContextMenuRequested, this,
&MainWindow::onGameListEntryContextMenuRequested, Qt::QueuedConnection);
connect(m_game_list_widget, &GameListWidget::selectionChanged, this, &MainWindow::onGameListSelectionChanged, Qt::QueuedConnection);
connect(m_game_list_widget, &GameListWidget::entryActivated, this, &MainWindow::onGameListEntryActivated, Qt::QueuedConnection);
connect(
m_game_list_widget, &GameListWidget::entryContextMenuRequested, this, &MainWindow::onGameListEntryContextMenuRequested, Qt::QueuedConnection);
}
void MainWindow::connectVMThreadSignals(EmuThread* thread)
{
connect(thread, &EmuThread::onCreateDisplayRequested, this, &MainWindow::createDisplay, Qt::BlockingQueuedConnection);
connect(thread, &EmuThread::onUpdateDisplayRequested, this, &MainWindow::updateDisplay, Qt::BlockingQueuedConnection);
connect(thread, &EmuThread::onDestroyDisplayRequested, this, &MainWindow::destroyDisplay,
Qt::BlockingQueuedConnection);
connect(thread, &EmuThread::onDestroyDisplayRequested, this, &MainWindow::destroyDisplay, Qt::BlockingQueuedConnection);
connect(thread, &EmuThread::onResizeDisplayRequested, this, &MainWindow::displayResizeRequested);
connect(thread, &EmuThread::onVMStarting, this, &MainWindow::onVMStarting);
connect(thread, &EmuThread::onVMStarted, this, &MainWindow::onVMStarted);
@ -221,9 +205,8 @@ void MainWindow::connectVMThreadSignals(EmuThread* thread)
GSRendererType::OGL, GSRendererType::VK, GSRendererType::SW, GSRendererType::Null};
for (GSRendererType renderer : renderers)
{
connect(
m_ui.menuDebugSwitchRenderer->addAction(QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(renderer))),
&QAction::triggered, [renderer] { g_emu_thread->switchRenderer(renderer); });
connect(m_ui.menuDebugSwitchRenderer->addAction(QString::fromUtf8(Pcsx2Config::GSOptions::GetRendererName(renderer))), &QAction::triggered,
[renderer] { g_emu_thread->switchRenderer(renderer); });
}
}
@ -462,10 +445,7 @@ void MainWindow::clearProgressBar()
m_ui.statusBar->removeWidget(m_status_progress_widget);
}
bool MainWindow::isShowingGameList() const
{
return m_ui.mainContainer->currentIndex() == 0;
}
bool MainWindow::isShowingGameList() const { return m_ui.mainContainer->currentIndex() == 0; }
void MainWindow::switchToGameListView()
{
@ -498,25 +478,13 @@ void MainWindow::switchToEmulationView()
m_display_widget->setFocus();
}
void MainWindow::refreshGameList(bool invalidate_cache)
{
m_game_list_widget->refresh(invalidate_cache);
}
void MainWindow::refreshGameList(bool invalidate_cache) { m_game_list_widget->refresh(invalidate_cache); }
void MainWindow::invalidateSaveStateCache()
{
m_save_states_invalidated = true;
}
void MainWindow::invalidateSaveStateCache() { m_save_states_invalidated = true; }
void MainWindow::reportError(const QString& title, const QString& message)
{
QMessageBox::critical(this, title, message);
}
void MainWindow::reportError(const QString& title, const QString& message) { QMessageBox::critical(this, title, message); }
void Host::InvalidateSaveStateCache()
{
QMetaObject::invokeMethod(g_main_window, &MainWindow::invalidateSaveStateCache, Qt::QueuedConnection);
}
void Host::InvalidateSaveStateCache() { QMetaObject::invokeMethod(g_main_window, &MainWindow::invalidateSaveStateCache, Qt::QueuedConnection); }
void MainWindow::onGameListRefreshProgress(const QString& status, int current, int total)
{
@ -524,10 +492,7 @@ void MainWindow::onGameListRefreshProgress(const QString& status, int current, i
setProgressBar(current, total);
}
void MainWindow::onGameListRefreshComplete()
{
clearProgressBar();
}
void MainWindow::onGameListRefreshComplete() { clearProgressBar(); }
void MainWindow::onGameListSelectionChanged()
{
@ -555,8 +520,7 @@ void MainWindow::onGameListEntryActivated()
}
// only resume if the option is enabled, and we have one for this game
const bool resume =
(VMManager::ShouldSaveResumeState() && VMManager::HasSaveStateInSlot(entry->serial.c_str(), entry->crc, -1));
const bool resume = (VMManager::ShouldSaveResumeState() && VMManager::HasSaveStateInSlot(entry->serial.c_str(), entry->crc, -1));
startGameListEntry(entry, resume ? std::optional<s32>(-1) : std::optional<s32>(), std::nullopt);
}
@ -570,7 +534,9 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
if (entry)
{
QAction* action = menu.addAction(tr("Properties..."));
// connect(action, &QAction::triggered, [this, entry]() { GamePropertiesDialog::showForEntry(entry, this); });
action->setEnabled(!entry->serial.empty());
if (action->isEnabled())
connect(action, &QAction::triggered, [this, entry]() { SettingsDialog::openGamePropertiesDialog(entry, entry->crc); });
action = menu.addAction(tr("Open Containing Directory..."));
connect(action, &QAction::triggered, [this, entry]() {
@ -608,8 +574,7 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
}
menu.addSeparator();
populateLoadStateMenu(&menu, QString::fromStdString(entry->path), QString::fromStdString(entry->serial),
entry->crc);
populateLoadStateMenu(&menu, QString::fromStdString(entry->path), QString::fromStdString(entry->serial), entry->crc);
}
else
{
@ -632,8 +597,8 @@ void MainWindow::onGameListEntryContextMenuRequested(const QPoint& point)
void MainWindow::onStartFileActionTriggered()
{
QString filename = QDir::toNativeSeparators(
QFileDialog::getOpenFileName(this, tr("Select Disc Image"), QString(), tr(DISC_IMAGE_FILTER), nullptr));
QString filename =
QDir::toNativeSeparators(QFileDialog::getOpenFileName(this, tr("Select Disc Image"), QString(), tr(DISC_IMAGE_FILTER), nullptr));
if (filename.isEmpty())
return;
@ -653,18 +618,14 @@ void MainWindow::onChangeDiscFromFileActionTriggered()
{
ScopedVMPause pauser(m_vm_paused);
QString filename =
QFileDialog::getOpenFileName(this, tr("Select Disc Image"), QString(), tr(DISC_IMAGE_FILTER), nullptr);
QString filename = QFileDialog::getOpenFileName(this, tr("Select Disc Image"), QString(), tr(DISC_IMAGE_FILTER), nullptr);
if (filename.isEmpty())
return;
g_emu_thread->changeDisc(filename);
}
void MainWindow::onChangeDiscFromGameListActionTriggered()
{
switchToGameListView();
}
void MainWindow::onChangeDiscFromGameListActionTriggered() { switchToGameListView(); }
void MainWindow::onChangeDiscFromDeviceActionTriggered()
{
@ -676,9 +637,7 @@ void MainWindow::onChangeDiscMenuAboutToShow()
// TODO: This is where we would populate the playlist if there is one.
}
void MainWindow::onChangeDiscMenuAboutToHide()
{
}
void MainWindow::onChangeDiscMenuAboutToHide() {}
void MainWindow::onLoadStateMenuAboutToShow()
{
@ -732,22 +691,29 @@ void MainWindow::onViewGamePropertiesActionTriggered()
{
if (!m_vm_valid)
return;
// prefer to use a game list entry, if we have one, that way the summary is populated
if (!m_current_disc_path.isEmpty())
{
auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryForPath(m_current_disc_path.toUtf8().constData());
if (entry)
{
SettingsDialog::openGamePropertiesDialog(entry, entry->crc);
return;
}
}
// open properties for the current running file (isn't in the game list)
if (m_current_game_crc != 0)
SettingsDialog::openGamePropertiesDialog(nullptr, m_current_game_crc);
}
void MainWindow::onGitHubRepositoryActionTriggered()
{
QtUtils::OpenURL(this, AboutDialog::getGitHubRepositoryUrl());
}
void MainWindow::onGitHubRepositoryActionTriggered() { QtUtils::OpenURL(this, AboutDialog::getGitHubRepositoryUrl()); }
void MainWindow::onSupportForumsActionTriggered()
{
QtUtils::OpenURL(this, AboutDialog::getSupportForumsUrl());
}
void MainWindow::onSupportForumsActionTriggered() { QtUtils::OpenURL(this, AboutDialog::getSupportForumsUrl()); }
void MainWindow::onDiscordServerActionTriggered()
{
QtUtils::OpenURL(this, AboutDialog::getDiscordServerUrl());
}
void MainWindow::onDiscordServerActionTriggered() { QtUtils::OpenURL(this, AboutDialog::getDiscordServerUrl()); }
void MainWindow::onAboutActionTriggered()
{
@ -755,9 +721,7 @@ void MainWindow::onAboutActionTriggered()
about.exec();
}
void MainWindow::onCheckForUpdatesActionTriggered()
{
}
void MainWindow::onCheckForUpdatesActionTriggered() {}
void MainWindow::onToolsOpenDataDirectoryTriggered()
{
@ -776,7 +740,7 @@ void MainWindow::onThemeChangedFromSettings()
{
// reopen the settings dialog after recreating
onThemeChanged();
g_main_window->doSettings(SettingsDialog::Category::InterfaceSettings);
g_main_window->doSettings();
}
void MainWindow::onVMStarting()
@ -905,9 +869,8 @@ DisplayWidget* MainWindow::createDisplay(bool fullscreen, bool render_to_main)
return nullptr;
}
if (!host_display->CreateRenderDevice(wi.value(), Host::GetStringSettingValue("EmuCore/GS", "Adapter", ""),
EmuConfig.GetEffectiveVsyncMode(), Host::GetBoolSettingValue("EmuCore/GS", "ThreadedPresentation", false),
Host::GetBoolSettingValue("EmuCore/GS", "UseDebugDevice", false)))
if (!host_display->CreateRenderDevice(wi.value(), Host::GetStringSettingValue("EmuCore/GS", "Adapter", ""), EmuConfig.GetEffectiveVsyncMode(),
Host::GetBoolSettingValue("EmuCore/GS", "ThreadedPresentation", false), Host::GetBoolSettingValue("EmuCore/GS", "UseDebugDevice", false)))
{
QMessageBox::critical(this, tr("Error"), tr("Failed to create host display device context."));
destroyDisplayWidget();
@ -1042,10 +1005,7 @@ void MainWindow::displayResizeRequested(qint32 width, qint32 height)
resize(QSize(std::max<qint32>(width, 1), std::max<qint32>(height + extra_height, 1)));
}
void MainWindow::destroyDisplay()
{
destroyDisplayWidget();
}
void MainWindow::destroyDisplay() { destroyDisplayWidget(); }
void MainWindow::focusDisplayWidget()
{
@ -1127,14 +1087,14 @@ SettingsDialog* MainWindow::getSettingsDialog()
if (!m_settings_dialog)
{
m_settings_dialog = new SettingsDialog(this);
connect(m_settings_dialog->getInterfaceSettingsWidget(), &InterfaceSettingsWidget::themeChanged, this,
&MainWindow::onThemeChangedFromSettings);
connect(
m_settings_dialog->getInterfaceSettingsWidget(), &InterfaceSettingsWidget::themeChanged, this, &MainWindow::onThemeChangedFromSettings);
}
return m_settings_dialog;
}
void MainWindow::doSettings(SettingsDialog::Category category)
void MainWindow::doSettings(const char* category /* = nullptr */)
{
SettingsDialog* dlg = getSettingsDialog();
if (!dlg->isVisible())
@ -1143,7 +1103,7 @@ void MainWindow::doSettings(SettingsDialog::Category category)
dlg->show();
}
if (category != SettingsDialog::Category::Count)
if (category)
dlg->setCategory(category);
}
@ -1168,8 +1128,7 @@ void MainWindow::doControllerSettings(ControllerSettingsDialog::Category categor
dlg->setCategory(category);
}
void MainWindow::startGameListEntry(const GameList::Entry* entry, std::optional<s32> save_slot,
std::optional<bool> fast_boot)
void MainWindow::startGameListEntry(const GameList::Entry* entry, std::optional<s32> save_slot, std::optional<bool> fast_boot)
{
std::shared_ptr<VMBootParameters> params = std::make_shared<VMBootParameters>();
params->fast_boot = fast_boot;
@ -1193,15 +1152,13 @@ void MainWindow::startGameListEntry(const GameList::Entry* entry, std::optional<
void MainWindow::setGameListEntryCoverImage(const GameList::Entry* entry)
{
const QString filename(QFileDialog::getOpenFileName(this, tr("Select Cover Image"), QString(),
tr("All Cover Image Types (*.jpg *.jpeg *.png)")));
const QString filename(QFileDialog::getOpenFileName(this, tr("Select Cover Image"), QString(), tr("All Cover Image Types (*.jpg *.jpeg *.png)")));
if (filename.isEmpty())
return;
if (!GameList::GetCoverImagePathForEntry(entry).empty())
{
if (QMessageBox::question(this, tr("Cover Already Exists"),
tr("A cover image for this game already exists, do you wish to replace it?"),
if (QMessageBox::question(this, tr("Cover Already Exists"), tr("A cover image for this game already exists, do you wish to replace it?"),
QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes)
{
return;
@ -1276,8 +1233,7 @@ void MainWindow::populateLoadStateMenu(QMenu* menu, const QString& filename, con
QAction* action = menu->addAction(is_right_click_menu ? tr("Load State File...") : tr("Load From File..."));
connect(action, &QAction::triggered, [this, filename]() {
const QString path(
QFileDialog::getOpenFileName(this, tr("Select Save State File"), QString(), tr("Save States (*.p2s)")));
const QString path(QFileDialog::getOpenFileName(this, tr("Select Save State File"), QString(), tr("Save States (*.p2s)")));
if (path.isEmpty())
return;
@ -1328,8 +1284,7 @@ void MainWindow::populateSaveStateMenu(QMenu* menu, const QString& serial, quint
return;
connect(menu->addAction(tr("Save To File...")), &QAction::triggered, [this]() {
const QString path(
QFileDialog::getSaveFileName(this, tr("Select Save State File"), QString(), tr("Save States (*.p2s)")));
const QString path(QFileDialog::getSaveFileName(this, tr("Select Save State File"), QString(), tr("Save States (*.p2s)")));
if (path.isEmpty())
return;

View File

@ -136,7 +136,7 @@ private:
void setDisplayFullscreen(const std::string& fullscreen_mode);
SettingsDialog* getSettingsDialog();
void doSettings(SettingsDialog::Category category = SettingsDialog::Category::Count);
void doSettings(const char* category = nullptr);
ControllerSettingsDialog* getControllerSettingsDialog();
void doControllerSettings(ControllerSettingsDialog::Category category = ControllerSettingsDialog::Category::Count);

View File

@ -33,6 +33,7 @@
#include "EmuThread.h"
#include "QtHost.h"
#include "SettingsDialog.h"
namespace SettingWidgetBinder
{
@ -41,15 +42,27 @@ namespace SettingWidgetBinder
{
static bool getBoolValue(const T* widget);
static void setBoolValue(T* widget, bool value);
static void makeNullableBool(T* widget, bool globalValue);
static std::optional<bool> getNullableBoolValue(const T* widget);
static void setNullableBoolValue(T* widget, std::optional<bool> value);
static int getIntValue(const T* widget);
static void setIntValue(T* widget, int value);
static void setIntValue(T* widget, bool nullable, int value);
static void makeNullableInt(T* widget, int globalValue);
static std::optional<int> getNullableIntValue(const T* widget);
static void setNullableIntValue(T* widget, std::optional<int> value);
static int getFloatValue(const T* widget);
static void setFloatValue(T* widget, int value);
static void makeNullableFloat(T* widget, float globalValue);
static std::optional<float> getNullableFloatValue(const T* widget);
static void setNullableFloatValue(T* widget, std::optional<float> value);
static QString getStringValue(const T* widget);
static void setStringValue(T* widget, const QString& value);
static void makeNullableString(T* widget, const QString& globalValue);
static std::optional<QString> getNullableStringValue(const T* widget);
static void setNullableFloatValue(T* widget, std::optional<QString> value);
template <typename F>
static void connectValueChanged(T* widget, F func);
@ -59,19 +72,28 @@ namespace SettingWidgetBinder
struct SettingAccessor<QLineEdit>
{
static bool getBoolValue(const QLineEdit* widget) { return widget->text().toInt() != 0; }
static void setBoolValue(QLineEdit* widget, bool value)
{
widget->setText(value ? QStringLiteral("1") : QStringLiteral("0"));
}
static void setBoolValue(QLineEdit* widget, bool value) { widget->setText(value ? QStringLiteral("1") : QStringLiteral("0")); }
static void makeNullableBool(QLineEdit* widget, bool globalValue) { widget->setEnabled(false); }
static std::optional<bool> getNullableBoolValue(const QLineEdit* widget) { return getBoolValue(widget); }
static void setNullableBoolValue(QLineEdit* widget, std::optional<bool> value) { setBoolValue(widget, value.value_or(false)); }
static int getIntValue(const QLineEdit* widget) { return widget->text().toInt(); }
static void setIntValue(QLineEdit* widget, int value) { widget->setText(QString::number(value)); }
static void makeNullableInt(QLineEdit* widget, int globalValue) { widget->setEnabled(false); }
static std::optional<int> getNullableIntValue(const QLineEdit* widget) { return getIntValue(widget); }
static void setNullableIntValue(QLineEdit* widget, std::optional<int> value) { setIntValue(widget, value.value_or(0)); }
static float getFloatValue(const QLineEdit* widget) { return widget->text().toFloat(); }
static void setFloatValue(QLineEdit* widget, float value) { widget->setText(QString::number(value)); }
static void makeNullableFloat(QLineEdit* widget, float globalValue) { widget->setEnabled(false); }
static std::optional<float> getNullableFloatValue(const QLineEdit* widget) { return getFloatValue(widget); }
static void setNullableFloatValue(QLineEdit* widget, std::optional<float> value) { setFloatValue(widget, value.value_or(0.0f)); }
static QString getStringValue(const QLineEdit* widget) { return widget->text(); }
static void setStringValue(QLineEdit* widget, const QString& value) { widget->setText(value); }
static void makeNullableString(QLineEdit* widget, const QString& globalValue) { widget->setEnabled(false); }
static std::optional<QString> getNullableStringValue(const QLineEdit* widget) { return getStringValue(widget); }
static void setNullableStringValue(QLineEdit* widget, std::optional<QString> value) { setStringValue(widget, value.value_or(QString())); }
template <typename F>
static void connectValueChanged(QLineEdit* widget, F func)
@ -83,14 +105,49 @@ namespace SettingWidgetBinder
template <>
struct SettingAccessor<QComboBox>
{
static bool isNullValue(const QComboBox* widget) { return (widget->currentIndex() == 0); }
static bool getBoolValue(const QComboBox* widget) { return widget->currentIndex() > 0; }
static void setBoolValue(QComboBox* widget, bool value) { widget->setCurrentIndex(value ? 1 : 0); }
static void makeNullableBool(QComboBox* widget, bool globalValue)
{
widget->insertItem(0, globalValue ? qApp->translate("SettingsDialog", "Use Global Setting [Enabled]") :
qApp->translate("SettingsDialog", "Use Global Setting [Disabled]"));
}
static int getIntValue(const QComboBox* widget) { return widget->currentIndex(); }
static void setIntValue(QComboBox* widget, int value) { widget->setCurrentIndex(value); }
static void makeNullableInt(QComboBox* widget, int globalValue)
{
widget->insertItem(0, qApp->translate("SettingsDialog", "Use Global Setting [%1]")
.arg((globalValue >= 0 && globalValue < widget->count()) ? widget->itemText(globalValue) : QString()));
}
static std::optional<int> getNullableIntValue(const QComboBox* widget)
{
return isNullValue(widget) ? std::nullopt : std::optional<int>(widget->currentIndex() + 1);
}
static void setNullableIntValue(QComboBox* widget, std::optional<int> value)
{
widget->setCurrentIndex(value.has_value() ? (value.value() + 1) : 0);
}
static float getFloatValue(const QComboBox* widget) { return static_cast<float>(widget->currentIndex()); }
static void setFloatValue(QComboBox* widget, float value) { widget->setCurrentIndex(static_cast<int>(value)); }
static void makeNullableFloat(QComboBox* widget, float globalValue)
{
widget->insertItem(0,
qApp->translate("SettingsDialog", "Use Global Setting [%1]")
.arg((globalValue >= 0.0f && static_cast<int>(globalValue) < widget->count()) ? widget->itemText(static_cast<int>(globalValue)) :
QString()));
}
static std::optional<float> getNullableFloatValue(const QComboBox* widget)
{
return isNullValue(widget) ? std::nullopt : std::optional<float>(static_cast<float>(widget->currentIndex() + 1));
}
static void setNullableFloatValue(QComboBox* widget, std::optional<float> value)
{
widget->setCurrentIndex(value.has_value() ? static_cast<int>(value.value() + 1.0f) : 0);
}
static QString getStringValue(const QComboBox* widget)
{
@ -105,7 +162,6 @@ namespace SettingWidgetBinder
return widget->currentText();
}
static void setStringValue(QComboBox* widget, const QString& value)
{
const int index = widget->findData(value);
@ -117,6 +173,15 @@ namespace SettingWidgetBinder
widget->setCurrentText(value);
}
static void makeNullableString(QComboBox* widget, const QString& globalValue) { makeNullableInt(widget, widget->findData(globalValue)); }
static std::optional<QString> getNullableStringValue(const QComboBox* widget)
{
return isNullValue(widget) ? std::nullopt : std::optional<QString>(getStringValue(widget));
}
static void setNullableStringValue(QComboBox* widget, std::optional<QString> value)
{
isNullValue(widget) ? widget->setCurrentIndex(0) : setStringValue(widget, value.value());
}
template <typename F>
static void connectValueChanged(QComboBox* widget, F func)
@ -130,18 +195,53 @@ namespace SettingWidgetBinder
{
static bool getBoolValue(const QCheckBox* widget) { return widget->isChecked(); }
static void setBoolValue(QCheckBox* widget, bool value) { widget->setChecked(value); }
static void makeNullableBool(QCheckBox* widget, bool globalValue) { widget->setTristate(true); }
static std::optional<bool> getNullableBoolValue(const QCheckBox* widget)
{
return (widget->checkState() == Qt::PartiallyChecked) ? std::nullopt : std::optional<bool>(widget->isChecked());
}
static void setNullableBoolValue(QCheckBox* widget, std::optional<bool> value)
{
widget->setCheckState(value.has_value() ? (value.value() ? Qt::Checked : Qt::Unchecked) : Qt::PartiallyChecked);
}
static int getIntValue(const QCheckBox* widget) { return widget->isChecked() ? 1 : 0; }
static void setIntValue(QCheckBox* widget, int value) { widget->setChecked(value != 0); }
static void makeNullableInt(QCheckBox* widget, int globalValue) { widget->setTristate(true); }
static std::optional<int> getNullableIntValue(const QCheckBox* widget)
{
return (widget->checkState() == Qt::PartiallyChecked) ? std::nullopt : std::optional<int>(widget->isChecked() ? 1 : 0);
}
static void setNullableIntValue(QCheckBox* widget, std::optional<int> value)
{
widget->setCheckState(value.has_value() ? ((value.value() != 0) ? Qt::Checked : Qt::Unchecked) : Qt::PartiallyChecked);
}
static float getFloatValue(const QCheckBox* widget) { return widget->isChecked() ? 1.0f : 0.0f; }
static void setFloatValue(QCheckBox* widget, float value) { widget->setChecked(value != 0.0f); }
static QString getStringValue(const QCheckBox* widget)
static void makeNullableFloat(QCheckBox* widget, float globalValue) { widget->setTristate(true); }
static std::optional<float> getNullableFloatValue(const QCheckBox* widget)
{
return widget->isChecked() ? QStringLiteral("1") : QStringLiteral("0");
return (widget->checkState() == Qt::PartiallyChecked) ? std::nullopt : std::optional<float>(widget->isChecked() ? 1.0f : 0.0f);
}
static void setNullableFloatValue(QCheckBox* widget, std::optional<float> value)
{
widget->setCheckState(value.has_value() ? ((value.value() != 0.0f) ? Qt::Checked : Qt::Unchecked) : Qt::PartiallyChecked);
}
static QString getStringValue(const QCheckBox* widget) { return widget->isChecked() ? QStringLiteral("1") : QStringLiteral("0"); }
static void setStringValue(QCheckBox* widget, const QString& value) { widget->setChecked(value.toInt() != 0); }
static void makeNullableString(QCheckBox* widget, const QString& globalValue) { widget->setTristate(true); }
static std::optional<QString> getNullableStringValue(const QCheckBox* widget)
{
return (widget->checkState() == Qt::PartiallyChecked) ?
std::nullopt :
std::optional<QString>(widget->isChecked() ? QStringLiteral("1") : QStringLiteral("0"));
}
static void setNullableStringValue(QCheckBox* widget, std::optional<QString> value)
{
widget->setCheckState(value.has_value() ? ((value->toInt() != 0) ? Qt::Checked : Qt::Unchecked) : Qt::PartiallyChecked);
}
template <typename F>
static void connectValueChanged(QCheckBox* widget, F func)
@ -155,15 +255,27 @@ namespace SettingWidgetBinder
{
static bool getBoolValue(const QSlider* widget) { return widget->value() > 0; }
static void setBoolValue(QSlider* widget, bool value) { widget->setValue(value ? 1 : 0); }
static void makeNullableBool(QSlider* widget, bool globalSetting) { widget->setEnabled(false); }
static std::optional<bool> getNullableBoolValue(const QSlider* widget) { return getBoolValue(widget); }
static void setNullableBoolValue(QSlider* widget, std::optional<bool> value) { setBoolValue(widget, value.value_or(false)); }
static int getIntValue(const QSlider* widget) { return widget->value(); }
static void setIntValue(QSlider* widget, int value) { widget->setValue(value); }
static void makeNullableInt(QSlider* widget, int globalValue) { widget->setEnabled(false); }
static std::optional<int> getNullableIntValue(const QSlider* widget) { return getIntValue(widget); }
static void setNullableIntValue(QSlider* widget, std::optional<int> value) { setIntValue(widget, value.value_or(0)); }
static float getFloatValue(const QSlider* widget) { return static_cast<float>(widget->value()); }
static void setFloatValue(QSlider* widget, float value) { widget->setValue(static_cast<int>(value)); }
static void makeNullableFloat(QSlider* widget, float globalValue) { widget->setEnabled(false); }
static std::optional<float> getNullableFloatValue(const QSlider* widget) { return getFloatValue(widget); }
static void setNullableFloatValue(QSlider* widget, std::optional<float> value) { setFloatValue(widget, value.value_or(0.0f)); }
static QString getStringValue(const QSlider* widget) { return QString::number(widget->value()); }
static void setStringValue(QSlider* widget, const QString& value) { widget->setValue(value.toInt()); }
static void makeNullableString(QSlider* widget, const QString& globalValue) { widget->setEnabled(false); }
static std::optional<QString> getNullableStringValue(const QSlider* widget) { return getStringValue(widget); }
static void setNullableStringValue(QSlider* widget, std::optional<QString> value) { setStringValue(widget, value.value_or(QString())); }
template <typename F>
static void connectValueChanged(QSlider* widget, F func)
@ -177,15 +289,27 @@ namespace SettingWidgetBinder
{
static bool getBoolValue(const QSpinBox* widget) { return widget->value() > 0; }
static void setBoolValue(QSpinBox* widget, bool value) { widget->setValue(value ? 1 : 0); }
static void makeNullableBool(QSpinBox* widget, bool globalSetting) { widget->setEnabled(false); }
static std::optional<bool> getNullableBoolValue(const QSpinBox* widget) { return getBoolValue(widget); }
static void setNullableBoolValue(QSpinBox* widget, std::optional<bool> value) { setBoolValue(widget, value.value_or(false)); }
static int getIntValue(const QSpinBox* widget) { return widget->value(); }
static void setIntValue(QSpinBox* widget, int value) { widget->setValue(value); }
static void makeNullableInt(QSpinBox* widget, int globalValue) { widget->setEnabled(false); }
static std::optional<int> getNullableIntValue(const QSpinBox* widget) { return getIntValue(widget); }
static void setNullableIntValue(QSpinBox* widget, std::optional<int> value) { setIntValue(widget, value.value_or(0)); }
static float getFloatValue(const QSpinBox* widget) { return static_cast<float>(widget->value()); }
static void setFloatValue(QSpinBox* widget, float value) { widget->setValue(static_cast<int>(value)); }
static void makeNullableFloat(QSpinBox* widget, float globalValue) { widget->setEnabled(false); }
static std::optional<float> getNullableFloatValue(const QSpinBox* widget) { return getFloatValue(widget); }
static void setNullableFloatValue(QSpinBox* widget, std::optional<float> value) { setFloatValue(widget, value.value_or(0.0f)); }
static QString getStringValue(const QSpinBox* widget) { return QString::number(widget->value()); }
static void setStringValue(QSpinBox* widget, const QString& value) { widget->setValue(value.toInt()); }
static void makeNullableString(QSpinBox* widget, const QString& globalValue) { widget->setEnabled(false); }
static std::optional<QString> getNullableStringValue(const QSpinBox* widget) { return getStringValue(widget); }
static void setNullableStringValue(QSpinBox* widget, std::optional<QString> value) { setStringValue(widget, value.value_or(QString())); }
template <typename F>
static void connectValueChanged(QSpinBox* widget, F func)
@ -199,15 +323,30 @@ namespace SettingWidgetBinder
{
static bool getBoolValue(const QDoubleSpinBox* widget) { return widget->value() > 0.0; }
static void setBoolValue(QDoubleSpinBox* widget, bool value) { widget->setValue(value ? 1.0 : 0.0); }
static void makeNullableBool(QDoubleSpinBox* widget, bool globalSetting) { widget->setEnabled(false); }
static std::optional<bool> getNullableBoolValue(const QDoubleSpinBox* widget) { return getBoolValue(widget); }
static void setNullableBoolValue(QDoubleSpinBox* widget, std::optional<bool> value) { setBoolValue(widget, value.value_or(false)); }
static int getIntValue(const QDoubleSpinBox* widget) { return static_cast<int>(widget->value()); }
static void setIntValue(QDoubleSpinBox* widget, int value) { widget->setValue(static_cast<double>(value)); }
static void makeNullableInt(QDoubleSpinBox* widget, int globalValue) { widget->setEnabled(false); }
static std::optional<int> getNullableIntValue(const QDoubleSpinBox* widget) { return getIntValue(widget); }
static void setNullableIntValue(QDoubleSpinBox* widget, std::optional<int> value) { setIntValue(widget, value.value_or(0)); }
static float getFloatValue(const QDoubleSpinBox* widget) { return static_cast<float>(widget->value()); }
static void setFloatValue(QDoubleSpinBox* widget, float value) { widget->setValue(static_cast<double>(value)); }
static void makeNullableFloat(QDoubleSpinBox* widget, float globalValue) { widget->setEnabled(false); }
static std::optional<float> getNullableFloatValue(const QDoubleSpinBox* widget) { return getFloatValue(widget); }
static void setNullableFloatValue(QDoubleSpinBox* widget, std::optional<float> value) { setFloatValue(widget, value.value_or(0.0f)); }
static QString getStringValue(const QDoubleSpinBox* widget) { return QString::number(widget->value()); }
static void setStringValue(QDoubleSpinBox* widget, const QString& value) { widget->setValue(value.toDouble()); }
static void makeNullableString(QDoubleSpinBox* widget, const QString& globalValue) { widget->setEnabled(false); }
static std::optional<QString> getNullableStringValue(const QDoubleSpinBox* widget) { return getStringValue(widget); }
static void setNullableStringValue(QDoubleSpinBox* widget, std::optional<QString> value)
{
setStringValue(widget, value.value_or(QString()));
}
template <typename F>
static void connectValueChanged(QDoubleSpinBox* widget, F func)
@ -221,18 +360,27 @@ namespace SettingWidgetBinder
{
static bool getBoolValue(const QAction* widget) { return widget->isChecked(); }
static void setBoolValue(QAction* widget, bool value) { widget->setChecked(value); }
static void makeNullableBool(QAction* widget, bool globalSetting) { widget->setEnabled(false); }
static std::optional<bool> getNullableBoolValue(const QAction* widget) { return getBoolValue(widget); }
static void setNullableBoolValue(QAction* widget, std::optional<bool> value) { setBoolValue(widget, value.value_or(false)); }
static int getIntValue(const QAction* widget) { return widget->isChecked() ? 1 : 0; }
static void setIntValue(QAction* widget, int value) { widget->setChecked(value != 0); }
static void makeNullableInt(QAction* widget, int globalValue) { widget->setEnabled(false); }
static std::optional<int> getNullableIntValue(const QAction* widget) { return getIntValue(widget); }
static void setNullableIntValue(QAction* widget, std::optional<int> value) { setIntValue(widget, value.value_or(0)); }
static float getFloatValue(const QAction* widget) { return widget->isChecked() ? 1.0f : 0.0f; }
static void setFloatValue(QAction* widget, float value) { widget->setChecked(value != 0.0f); }
static void makeNullableFloat(QAction* widget, float globalValue) { widget->setEnabled(false); }
static std::optional<float> getNullableFloatValue(const QAction* widget) { return getFloatValue(widget); }
static void setNullableFloatValue(QAction* widget, std::optional<float> value) { setFloatValue(widget, value.value_or(0.0f)); }
static QString getStringValue(const QAction* widget)
{
return widget->isChecked() ? QStringLiteral("1") : QStringLiteral("0");
}
static QString getStringValue(const QAction* widget) { return widget->isChecked() ? QStringLiteral("1") : QStringLiteral("0"); }
static void setStringValue(QAction* widget, const QString& value) { widget->setChecked(value.toInt() != 0); }
static void makeNullableString(QAction* widget, const QString& globalValue) { widget->setEnabled(false); }
static std::optional<QString> getNullableStringValue(const QAction* widget) { return getStringValue(widget); }
static void setNullableStringValue(QAction* widget, std::optional<QString> value) { setStringValue(widget, value.value_or(QString())); }
template <typename F>
static void connectValueChanged(QAction* widget, F func)
@ -244,153 +392,334 @@ namespace SettingWidgetBinder
/// Binds a widget's value to a setting, updating it when the value changes.
template <typename WidgetType>
static void BindWidgetToBoolSetting(WidgetType* widget, std::string section, std::string key, bool default_value)
static void BindWidgetToBoolSetting(SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, bool default_value)
{
using Accessor = SettingAccessor<WidgetType>;
bool value = QtHost::GetBaseBoolSettingValue(section.c_str(), key.c_str(), default_value);
const bool value = QtHost::GetBaseBoolSettingValue(section.c_str(), key.c_str(), default_value);
Accessor::setBoolValue(widget, value);
if (sif)
{
Accessor::makeNullableBool(widget, value);
Accessor::connectValueChanged(widget, [widget, section, key]() {
const bool new_value = Accessor::getBoolValue(widget);
QtHost::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value);
g_emu_thread->applySettings();
});
}
template <typename WidgetType>
static void BindWidgetToIntSetting(WidgetType* widget, std::string section, std::string key, int default_value, int option_offset = 0)
{
using Accessor = SettingAccessor<WidgetType>;
s32 value = QtHost::GetBaseIntSettingValue(section.c_str(), key.c_str(), static_cast<s32>(default_value)) - option_offset;
Accessor::setIntValue(widget, static_cast<int>(value));
Accessor::connectValueChanged(widget, [widget, section, key, option_offset]() {
const int new_value = Accessor::getIntValue(widget);
QtHost::SetBaseIntSettingValue(section.c_str(), key.c_str(), new_value + option_offset);
g_emu_thread->applySettings();
});
}
template <typename WidgetType>
static void BindWidgetToFloatSetting(WidgetType* widget, std::string section, std::string key, float default_value)
{
using Accessor = SettingAccessor<WidgetType>;
float value = QtHost::GetBaseFloatSettingValue(section.c_str(), key.c_str(), default_value);
Accessor::setFloatValue(widget, value);
Accessor::connectValueChanged(widget, [widget, section, key]() {
const float new_value = Accessor::getFloatValue(widget);
QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value);
g_emu_thread->applySettings();
});
}
template <typename WidgetType>
static void BindWidgetToNormalizedSetting(WidgetType* widget, std::string section, std::string key, float range,
float default_value)
{
using Accessor = SettingAccessor<WidgetType>;
float value = QtHost::GetBaseFloatSettingValue(section.c_str(), key.c_str(), default_value);
Accessor::setIntValue(widget, static_cast<int>(value * range));
Accessor::connectValueChanged(widget, [widget, section, key, range]() {
const float new_value = (static_cast<float>(Accessor::getIntValue(widget)) / range);
QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value);
g_emu_thread->applySettings();
});
}
template <typename WidgetType>
static void BindWidgetToStringSetting(WidgetType* widget, std::string section, std::string key,
std::string default_value = std::string())
{
using Accessor = SettingAccessor<WidgetType>;
std::string value = QtHost::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value.c_str());
Accessor::setStringValue(widget, QString::fromStdString(value));
Accessor::connectValueChanged(widget, [widget, section, key]() {
const QString new_value = Accessor::getStringValue(widget);
if (!new_value.isEmpty())
QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.toUtf8().constData());
bool sif_value;
if (sif->GetBoolValue(section.c_str(), key.c_str(), &sif_value))
Accessor::setNullableBoolValue(widget, sif_value);
else
QtHost::RemoveBaseSettingValue(section.c_str(), key.c_str());
Accessor::setNullableBoolValue(widget, std::nullopt);
g_emu_thread->applySettings();
});
}
Accessor::connectValueChanged(widget, [sif, widget, section, key]() {
if (std::optional<bool> new_value = Accessor::getNullableBoolValue(widget); new_value.has_value())
sif->SetBoolValue(section.c_str(), key.c_str(), new_value.value());
else
sif->DeleteValue(section.c_str(), key.c_str());
template <typename WidgetType, typename DataType>
static void BindWidgetToEnumSetting(WidgetType* widget, std::string section, std::string key,
std::optional<DataType> (*from_string_function)(const char* str),
const char* (*to_string_function)(DataType value), DataType default_value)
{
using Accessor = SettingAccessor<WidgetType>;
using UnderlyingType = std::underlying_type_t<DataType>;
// TODO: Clean this up?
const std::string old_setting_string_value =
QtHost::GetBaseStringSettingValue(section.c_str(), key.c_str(), to_string_function(default_value));
const std::optional<DataType> old_setting_value = from_string_function(old_setting_string_value.c_str());
if (old_setting_value.has_value())
Accessor::setIntValue(widget, static_cast<int>(static_cast<UnderlyingType>(old_setting_value.value())));
sif->Save();
g_emu_thread->applySettings();
});
}
else
Accessor::setIntValue(widget, static_cast<int>(static_cast<UnderlyingType>(default_value)));
{
Accessor::setBoolValue(widget, value);
Accessor::connectValueChanged(widget, [widget, section, key, to_string_function]() {
const DataType value = static_cast<DataType>(static_cast<UnderlyingType>(Accessor::getIntValue(widget)));
const char* string_value = to_string_function(value);
QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), string_value);
g_emu_thread->applySettings();
});
Accessor::connectValueChanged(widget, [widget, section, key]() {
const bool new_value = Accessor::getBoolValue(widget);
QtHost::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value);
g_emu_thread->applySettings();
});
}
}
template <typename WidgetType>
static void BindWidgetToIntSetting(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, int default_value, int option_offset = 0)
{
using Accessor = SettingAccessor<WidgetType>;
const s32 value = QtHost::GetBaseIntSettingValue(section.c_str(), key.c_str(), static_cast<s32>(default_value)) - option_offset;
if (sif)
{
Accessor::makeNullableInt(widget, value);
int sif_value;
if (sif->GetIntValue(section.c_str(), key.c_str(), &sif_value))
Accessor::setNullableIntValue(widget, sif_value);
else
Accessor::setNullableIntValue(widget, std::nullopt);
Accessor::connectValueChanged(widget, [sif, widget, section, key]() {
if (std::optional<int> new_value = Accessor::getNullableIntValue(widget); new_value.has_value())
sif->SetIntValue(section.c_str(), key.c_str(), new_value.value());
else
sif->DeleteValue(section.c_str(), key.c_str());
sif->Save();
g_emu_thread->applySettings();
});
}
else
{
Accessor::setIntValue(widget, static_cast<int>(value));
Accessor::connectValueChanged(widget, [widget, section, key, option_offset]() {
const int new_value = Accessor::getIntValue(widget);
QtHost::SetBaseIntSettingValue(section.c_str(), key.c_str(), new_value + option_offset);
g_emu_thread->applySettings();
});
}
}
template <typename WidgetType>
static void BindWidgetToFloatSetting(SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, float default_value)
{
using Accessor = SettingAccessor<WidgetType>;
const float value = QtHost::GetBaseFloatSettingValue(section.c_str(), key.c_str(), default_value);
if (sif)
{
Accessor::makeNullableFloat(widget, value);
float sif_value;
if (sif->GetFloatValue(section.c_str(), key.c_str(), &sif_value))
Accessor::setNullableFloatValue(widget, sif_value);
else
Accessor::setNullableFloatValue(widget, std::nullopt);
Accessor::connectValueChanged(widget, [sif, widget, section, key]() {
if (std::optional<float> new_value = Accessor::getNullableFloatValue(widget); new_value.has_value())
sif->SetFloatValue(section.c_str(), key.c_str(), new_value.value());
else
sif->DeleteValue(section.c_str(), key.c_str());
sif->Save();
g_emu_thread->applySettings();
});
}
else
{
Accessor::setFloatValue(widget, value);
Accessor::connectValueChanged(widget, [widget, section, key]() {
const float new_value = Accessor::getFloatValue(widget);
QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value);
g_emu_thread->applySettings();
});
}
}
template <typename WidgetType>
static void BindWidgetToNormalizedSetting(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, float range, float default_value)
{
using Accessor = SettingAccessor<WidgetType>;
const float value = QtHost::GetBaseFloatSettingValue(section.c_str(), key.c_str(), default_value);
if (sif)
{
Accessor::makeNullableInt(widget, static_cast<int>(value * range));
float sif_value;
if (sif->GetFloatValue(section.c_str(), key.c_str(), &sif_value))
Accessor::setNullableIntValue(widget, static_cast<int>(sif_value * range));
else
Accessor::setNullableIntValue(widget, std::nullopt);
Accessor::connectValueChanged(widget, [sif, widget, section, key, range]() {
if (std::optional<int> new_value = Accessor::getNullableIntValue(widget); new_value.has_value())
sif->SetFloatValue(section.c_str(), key.c_str(), static_cast<float>(new_value.value()) / range);
else
sif->DeleteValue(section.c_str(), key.c_str());
sif->Save();
g_emu_thread->applySettings();
});
}
else
{
Accessor::setIntValue(widget, static_cast<int>(value * range));
Accessor::connectValueChanged(widget, [widget, section, key, range]() {
const float new_value = (static_cast<float>(Accessor::getIntValue(widget)) / range);
QtHost::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value);
g_emu_thread->applySettings();
});
}
}
template <typename WidgetType>
static void BindWidgetToStringSetting(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, std::string default_value = std::string())
{
using Accessor = SettingAccessor<WidgetType>;
const QString value(QString::fromStdString(QtHost::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value.c_str())));
if (sif)
{
Accessor::makeNullableString(widget, value);
std::string sif_value;
if (sif->GetStringValue(section.c_str(), key.c_str(), &sif_value))
Accessor::setNullableStringValue(widget, QString::fromStdString(sif_value));
else
Accessor::setNullableStringValue(widget, std::nullopt);
Accessor::connectValueChanged(widget, [sif, widget, section, key]() {
if (std::optional<QString> new_value = Accessor::getNullableStringValue(widget); new_value.has_value())
sif->SetStringValue(section.c_str(), key.c_str(), new_value->toUtf8().constData());
else
sif->DeleteValue(section.c_str(), key.c_str());
sif->Save();
g_emu_thread->applySettings();
});
}
else
{
Accessor::setStringValue(widget, value);
Accessor::connectValueChanged(widget, [widget, section, key]() {
const QString new_value = Accessor::getStringValue(widget);
if (!new_value.isEmpty())
QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), new_value.toUtf8().constData());
else
QtHost::RemoveBaseSettingValue(section.c_str(), key.c_str());
g_emu_thread->applySettings();
});
}
}
template <typename WidgetType, typename DataType>
static void BindWidgetToEnumSetting(WidgetType* widget, std::string section, std::string key, const char** enum_names,
DataType default_value)
static void BindWidgetToEnumSetting(SettingsInterface* sif, WidgetType* widget, std::string section, std::string key,
std::optional<DataType> (*from_string_function)(const char* str), const char* (*to_string_function)(DataType value), DataType default_value)
{
using Accessor = SettingAccessor<WidgetType>;
using UnderlyingType = std::underlying_type_t<DataType>;
const std::string old_setting_string_value = QtHost::GetBaseStringSettingValue(
section.c_str(), key.c_str(), enum_names[static_cast<UnderlyingType>(default_value)]);
const std::string value(QtHost::GetBaseStringSettingValue(section.c_str(), key.c_str(), to_string_function(default_value)));
const std::optional<DataType> typed_value = from_string_function(value.c_str());
if (sif)
{
Accessor::makeNullableInt(widget, typed_value.has_value() ? static_cast<int>(static_cast<UnderlyingType>(typed_value.value())) : 0);
std::string sif_value;
if (sif->GetStringValue(section.c_str(), key.c_str(), &sif_value))
{
const std::optional<DataType> old_setting_value = from_string_function(sif_value.c_str());
if (old_setting_value.has_value())
Accessor::setNullableIntValue(widget, static_cast<int>(static_cast<UnderlyingType>(old_setting_value.value())));
else
Accessor::setNullableIntValue(widget, std::nullopt);
}
else
{
Accessor::setNullableIntValue(widget, std::nullopt);
}
Accessor::connectValueChanged(widget, [sif, widget, section, key]() {
if (std::optional<int> new_value = Accessor::getNullableIntValue(widget); new_value.has_value())
{
const char* string_value = to_string_function(static_cast<DataType>(static_cast<UnderlyingType>(new_value.value())));
sif->SetStringValue(section.c_str(), key.c_str(), string_value);
}
else
{
sif->DeleteValue(section.c_str(), key.c_str());
}
sif->Save();
g_emu_thread->applySettings();
});
}
else
{
if (typed_value.has_value())
Accessor::setIntValue(widget, static_cast<int>(static_cast<UnderlyingType>(typed_value.value())));
else
Accessor::setIntValue(widget, static_cast<int>(static_cast<UnderlyingType>(default_value)));
Accessor::connectValueChanged(widget, [widget, section, key, to_string_function]() {
const DataType value = static_cast<DataType>(static_cast<UnderlyingType>(Accessor::getIntValue(widget)));
const char* string_value = to_string_function(value);
QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), string_value);
g_emu_thread->applySettings();
});
}
}
template <typename WidgetType, typename DataType>
static void BindWidgetToEnumSetting(
SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, const char** enum_names, DataType default_value)
{
using Accessor = SettingAccessor<WidgetType>;
using UnderlyingType = std::underlying_type_t<DataType>;
const std::string value(
QtHost::GetBaseStringSettingValue(section.c_str(), key.c_str(), enum_names[static_cast<UnderlyingType>(default_value)]));
UnderlyingType enum_index = static_cast<UnderlyingType>(default_value);
for (UnderlyingType i = 0; enum_names[i] != nullptr; i++)
{
if (old_setting_string_value == enum_names[i])
if (value == enum_names[i])
{
enum_index = i;
break;
}
}
Accessor::setIntValue(widget, static_cast<int>(enum_index));
Accessor::connectValueChanged(widget, [widget, section, key, enum_names]() {
const UnderlyingType value = static_cast<UnderlyingType>(Accessor::getIntValue(widget));
const char* string_value = enum_names[value];
QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), string_value);
g_emu_thread->applySettings();
});
if (sif)
{
Accessor::makeNullableInt(widget, static_cast<int>(enum_index));
std::string sif_value;
std::optional<int> sif_int_value;
if (sif->GetStringValue(section.c_str(), key.c_str(), &sif_value))
{
for (UnderlyingType i = 0; enum_names[i] != nullptr; i++)
{
if (sif_value == enum_names[i])
{
sif_int_value = static_cast<int>(i);
break;
}
}
}
Accessor::setNullableIntValue(widget, sif_int_value);
Accessor::connectValueChanged(widget, [sif, widget, section, key, enum_names]() {
if (std::optional<int> new_value = Accessor::getNullableIntValue(widget); new_value.has_value())
sif->SetStringValue(section.c_str(), key.c_str(), enum_names[new_value.value()]);
else
sif->DeleteValue(section.c_str(), key.c_str());
sif->Save();
g_emu_thread->applySettings();
});
}
else
{
Accessor::setIntValue(widget, static_cast<int>(enum_index));
Accessor::connectValueChanged(widget, [widget, section, key, enum_names]() {
const UnderlyingType value = static_cast<UnderlyingType>(Accessor::getIntValue(widget));
QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_names[value]);
g_emu_thread->applySettings();
});
}
}
template <typename WidgetType>
static void BindWidgetToEnumSetting(WidgetType* widget, std::string section, std::string key, const char** enum_names,
static void BindWidgetToEnumSetting(SettingsInterface* sif, WidgetType* widget, std::string section, std::string key, const char** enum_names,
const char** enum_values, const char* default_value)
{
using Accessor = SettingAccessor<WidgetType>;
const std::string old_setting_string_value =
QtHost::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value);
const std::string value = QtHost::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value);
for (int i = 0; enum_names[i] != nullptr; i++)
widget->addItem(QString::fromUtf8(enum_names[i]));
@ -398,20 +727,53 @@ namespace SettingWidgetBinder
int enum_index = -1;
for (int i = 0; enum_values[i] != nullptr; i++)
{
if (old_setting_string_value == enum_values[i])
if (value == enum_values[i])
{
enum_index = i;
break;
}
}
if (enum_index >= 0)
Accessor::setIntValue(widget, enum_index);
Accessor::connectValueChanged(widget, [widget, section, key, enum_values]() {
const int value = Accessor::getIntValue(widget);
QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_values[value]);
g_emu_thread->applySettings();
});
if (sif)
{
Accessor::makeNullableInt(widget, enum_index);
std::string sif_value;
std::optional<int> sif_int_value;
if (sif->GetStringValue(section.c_str(), key.c_str(), &sif_value))
{
for (int i = 0; enum_values[i] != nullptr; i++)
{
if (value == enum_values[i])
{
sif_int_value = i;
break;
}
}
}
Accessor::setNullableIntValue(widget, sif_int_value);
Accessor::connectValueChanged(widget, [sif, widget, section, key, enum_names]() {
if (std::optional<int> new_value = Accessor::getNullableIntValue(widget); new_value.has_value())
sif->SetStringValue(section.c_str(), key.c_str(), enum_names[new_value.value()]);
else
sif->DeleteValue(section.c_str(), key.c_str());
sif->Save();
g_emu_thread->applySettings();
});
}
else
{
if (enum_index >= 0)
Accessor::setIntValue(widget, enum_index);
Accessor::connectValueChanged(widget, [widget, section, key, enum_values]() {
const int value = Accessor::getIntValue(widget);
QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_values[value]);
g_emu_thread->applySettings();
});
}
}
} // namespace SettingWidgetBinder

View File

@ -24,27 +24,29 @@
#include "SettingWidgetBinder.h"
#include "SettingsDialog.h"
AdvancedSystemSettingsWidget::AdvancedSystemSettingsWidget(QWidget* parent, SettingsDialog* dialog)
AdvancedSystemSettingsWidget::AdvancedSystemSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent)
{
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.eeRecompiler, "EmuCore/CPU/Recompiler", "EnableEE", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.eeCache, "EmuCore/CPU/Recompiler", "EnableEECache", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.eeINTCSpinDetection, "EmuCore/Speedhacks", "IntcStat", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.eeWaitLoopDetection, "EmuCore/Speedhacks", "WaitLoop", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.eeRecompiler, "EmuCore/CPU/Recompiler", "EnableEE", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.eeCache, "EmuCore/CPU/Recompiler", "EnableEECache", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.eeINTCSpinDetection, "EmuCore/Speedhacks", "IntcStat", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.eeWaitLoopDetection, "EmuCore/Speedhacks", "WaitLoop", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.vu0Recompiler, "EmuCore/CPU/Recompiler", "EnableVU0", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.vu1Recompiler, "EmuCore/CPU/Recompiler", "EnableVU1", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.vuFlagHack, "EmuCore/Speedhacks", "vuFlagHack", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.vu0Recompiler, "EmuCore/CPU/Recompiler", "EnableVU0", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.vu1Recompiler, "EmuCore/CPU/Recompiler", "EnableVU1", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.vuFlagHack, "EmuCore/Speedhacks", "vuFlagHack", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.iopRecompiler, "EmuCore/CPU/Recompiler", "EnableIOP", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.iopRecompiler, "EmuCore/CPU/Recompiler", "EnableIOP", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.gameFixes, "", "EnableGameFixes", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.patches, "EmuCore", "EnablePatches", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.gameFixes, "", "EnableGameFixes", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.patches, "EmuCore", "EnablePatches", true);
SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.ntscFrameRate, "EmuCore/GS", "FramerateNTSC", 59.94f);
SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.palFrameRate, "EmuCore/GS", "FrameratePAL", 50.00f);
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.ntscFrameRate, "EmuCore/GS", "FramerateNTSC", 59.94f);
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.palFrameRate, "EmuCore/GS", "FrameratePAL", 50.00f);
}
AdvancedSystemSettingsWidget::~AdvancedSystemSettingsWidget() = default;

View File

@ -26,7 +26,7 @@ class AdvancedSystemSettingsWidget : public QWidget
Q_OBJECT
public:
AdvancedSystemSettingsWidget(QWidget* parent, SettingsDialog* dialog);
AdvancedSystemSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~AdvancedSystemSettingsWidget();
private:

View File

@ -26,12 +26,14 @@
#include "SettingWidgetBinder.h"
#include "SettingsDialog.h"
BIOSSettingsWidget::BIOSSettingsWidget(QWidget* parent, SettingsDialog* dialog)
BIOSSettingsWidget::BIOSSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent)
{
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.fastBoot, "EmuCore", "EnableFastBoot", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastBoot, "EmuCore", "EnableFastBoot", true);
dialog->registerWidgetHelp(m_ui.fastBoot, tr("Fast Boot"), tr("Unchecked"),
tr("Patches the BIOS to skip the console's boot animation."));

View File

@ -42,7 +42,7 @@ class BIOSSettingsWidget : public QWidget
Q_OBJECT
public:
BIOSSettingsWidget(QWidget* parent, SettingsDialog* dialog);
BIOSSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~BIOSSettingsWidget();
private Q_SLOTS:

View File

@ -38,7 +38,7 @@ ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSett
populateControllerTypes();
onTypeChanged();
SettingWidgetBinder::BindWidgetToStringSetting(m_ui.controllerType, m_config_section, "Type", "None");
SettingWidgetBinder::BindWidgetToStringSetting(nullptr, m_ui.controllerType, m_config_section, "Type", "None");
connect(m_ui.controllerType, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&ControllerBindingWidget::onTypeChanged);
}

View File

@ -26,9 +26,9 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent,
{
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.enableSDLSource, "InputSources", "SDL", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.enableSDLEnhancedMode, "InputSources", "SDLControllerEnhancedMode", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.enableXInputSource, "InputSources", "XInput", false);
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.enableSDLSource, "InputSources", "SDL", true);
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.enableSDLEnhancedMode, "InputSources", "SDLControllerEnhancedMode", false);
SettingWidgetBinder::BindWidgetToBoolSetting(nullptr, m_ui.enableXInputSource, "InputSources", "XInput", false);
connect(m_ui.enableSDLSource, &QCheckBox::stateChanged, this, &ControllerGlobalSettingsWidget::updateSDLOptionsEnabled);
updateSDLOptionsEnabled();

View File

@ -15,6 +15,7 @@
#include "PrecompiledHeader.h"
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMessageBox>
#include <limits>
@ -25,92 +26,45 @@
static constexpr u32 DEFAULT_FRAME_LATENCY = 2;
static void FillComboBoxWithEmulationSpeeds(QComboBox* cb)
{
// cb->addItem(qApp->translate("GeneralSettingsWidget", "Custom"),;// TODO: Make use of getInteger to get manual overrides from users
// for speed choice along with dropdown presets.
cb->addItem(qApp->translate("GeneralSettingsWidget", "Unlimited"), QVariant(0.0f));
static const int speeds[] = {1, 10, 25, 50, 75, 90, 100, 110,
120, 150, 175, 200, 300, 400, 500, 1000};
for (const int speed : speeds)
{
cb->addItem(qApp->translate("EmulationSettingsWidget", "%1% [%2 FPS (NTSC) / %3 FPS (PAL)]")
.arg(speed)
.arg((60 * speed) / 100)
.arg((50 * speed) / 100),
QVariant(static_cast<float>(speed) / 100.0f));
}
}
EmulationSettingsWidget::EmulationSettingsWidget(QWidget* parent, SettingsDialog* dialog)
EmulationSettingsWidget::EmulationSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent)
, m_dialog(dialog)
{
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this);
FillComboBoxWithEmulationSpeeds(m_ui.normalSpeed);
if (const int index =
m_ui.normalSpeed->findData(QVariant(QtHost::GetBaseFloatSettingValue("Framerate", "NominalScalar", 1.0f)));
index >= 0)
{
m_ui.normalSpeed->setCurrentIndex(index);
}
connect(m_ui.normalSpeed, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&EmulationSettingsWidget::onNormalSpeedIndexChanged);
FillComboBoxWithEmulationSpeeds(m_ui.fastForwardSpeed);
initializeSpeedCombo(m_ui.normalSpeed, "Framerate", "NominalScalar", 1.0f);
initializeSpeedCombo(m_ui.fastForwardSpeed, "Framerate", "TurboScalar", 2.0f);
initializeSpeedCombo(m_ui.slowMotionSpeed, "Framerate", "SlomoScalar", 0.5f);
if (const int index =
m_ui.fastForwardSpeed->findData(QVariant(QtHost::GetBaseFloatSettingValue("Framerate", "TurboScalar", 2.0f)));
index >= 0)
{
m_ui.fastForwardSpeed->setCurrentIndex(index);
}
connect(m_ui.fastForwardSpeed, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&EmulationSettingsWidget::onFastForwardSpeedIndexChanged);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.speedLimiter, "EmuCore/GS", "FrameLimitEnable", true);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.maxFrameLatency, "EmuCore/GS", "VsyncQueueSize", DEFAULT_FRAME_LATENCY);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.syncToHostRefreshRate, "EmuCore/GS", "SyncToHostRefreshRate", false);
connect(m_ui.optimalFramePacing, &QCheckBox::stateChanged, this, &EmulationSettingsWidget::onOptimalFramePacingChanged);
m_ui.optimalFramePacing->setTristate(dialog->isPerGameSettings());
FillComboBoxWithEmulationSpeeds(m_ui.slowMotionSpeed);
if (const int index =
m_ui.slowMotionSpeed->findData(QVariant(QtHost::GetBaseFloatSettingValue("Framerate", "SlomoScalar", 0.5f)));
index >= 0)
{
m_ui.slowMotionSpeed->setCurrentIndex(index);
}
connect(m_ui.slowMotionSpeed, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&EmulationSettingsWidget::onSlowMotionSpeedIndexChanged);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cheats, "EmuCore", "EnableCheats", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.widescreenPatches, "EmuCore", "EnableWideScreenPatches", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.perGameSettings, "EmuCore", "EnablePerGameSettings", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hostFilesystem, "EmuCore", "HostFs", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.speedLimiter, "EmuCore/GS", "FrameLimitEnable", true);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.maxFrameLatency, "EmuCore/GS", "VsyncQueueSize", DEFAULT_FRAME_LATENCY);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.syncToHostRefreshRate, "EmuCore/GS", "SyncToHostRefreshRate",
false);
connect(m_ui.optimalFramePacing, &QCheckBox::toggled, this, &EmulationSettingsWidget::onOptimalFramePacingChanged);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.cheats, "EmuCore", "EnableCheats", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.widescreenPatches, "EmuCore", "EnableWideScreenPatches", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.perGameSettings, "EmuCore", "EnablePerGameSettings", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.hostFilesystem, "EmuCore", "HostFs", false);
dialog->registerWidgetHelp(
m_ui.normalSpeed, tr("Normal Speed"), "100%",
dialog->registerWidgetHelp(m_ui.normalSpeed, tr("Normal Speed"), "100%",
tr("Sets the target emulation speed. It is not guaranteed that this speed will be reached, "
"and if not, the emulator will run as fast as it can manage."));
dialog->registerWidgetHelp(
m_ui.fastForwardSpeed, tr("Fast Forward Speed"), tr("User Preference"),
dialog->registerWidgetHelp(m_ui.fastForwardSpeed, tr("Fast Forward Speed"), tr("User Preference"),
tr("Sets the fast forward speed. This speed will be used when the fast forward hotkey is pressed/toggled."));
dialog->registerWidgetHelp(
m_ui.slowMotionSpeed, tr("Slow Motion Speed"), tr("User Preference"),
dialog->registerWidgetHelp(m_ui.slowMotionSpeed, tr("Slow Motion Speed"), tr("User Preference"),
tr("Sets the slow motion speed. This speed will be used when the slow motion hotkey is pressed/toggled."));
dialog->registerWidgetHelp(
m_ui.syncToHostRefreshRate, tr("Sync To Host Refresh Rate"), tr("Unchecked"),
dialog->registerWidgetHelp(m_ui.syncToHostRefreshRate, tr("Sync To Host Refresh Rate"), tr("Unchecked"),
tr("Adjusts the emulation speed so the console's refresh rate matches the host's refresh rate when both VSync and "
"Audio Resampling settings are enabled. This results in the smoothest animations possible, at the cost of "
"potentially increasing the emulation speed by less than 1%. Sync To Host Refresh Rate will not take effect if "
"the console's refresh rate is too far from the host's refresh rate. Users with variable refresh rate displays "
"should disable this option."));
dialog->registerWidgetHelp(m_ui.cheats, tr("Enable Cheats"), tr("Unchecked"),
tr("Automatically loads and applies cheats on game start."));
dialog->registerWidgetHelp(
m_ui.perGameSettings, tr("Enable Per-Game Settings"), tr("Checked"),
dialog->registerWidgetHelp(m_ui.cheats, tr("Enable Cheats"), tr("Unchecked"), tr("Automatically loads and applies cheats on game start."));
dialog->registerWidgetHelp(m_ui.perGameSettings, tr("Enable Per-Game Settings"), tr("Checked"),
tr("When enabled, per-game settings will be applied, and incompatible enhancements will be disabled. You should "
"leave this option enabled except when testing enhancements with incompatible games."));
@ -119,49 +73,122 @@ EmulationSettingsWidget::EmulationSettingsWidget(QWidget* parent, SettingsDialog
EmulationSettingsWidget::~EmulationSettingsWidget() = default;
void EmulationSettingsWidget::onNormalSpeedIndexChanged(int index)
void EmulationSettingsWidget::initializeSpeedCombo(QComboBox* cb, const char* section, const char* key, float default_value)
{
bool okay;
const float value = m_ui.normalSpeed->currentData().toFloat(&okay);
QtHost::SetBaseFloatSettingValue("Framerate", "NominalScalar", okay ? value : 1.0f);
g_emu_thread->applySettings();
float value = QtHost::GetBaseFloatSettingValue(section, key, default_value);
if (m_dialog->isPerGameSettings())
{
cb->addItem(tr("Use Global Setting [%1%]").arg(value * 100.0f, 0, 'f', 0));
if (!m_dialog->getSettingsInterface()->GetFloatValue(key, section, &value))
{
// set to something without data
value = -1.0f;
cb->setCurrentIndex(0);
}
}
static const int speeds[] = {1, 10, 25, 50, 75, 90, 100, 110, 120, 150, 175, 200, 300, 400, 500, 1000};
for (const int speed : speeds)
{
cb->addItem(tr("%1% [%2 FPS (NTSC) / %3 FPS (PAL)]")
.arg(speed)
.arg((60 * speed) / 100)
.arg((50 * speed) / 100),
QVariant(static_cast<float>(speed) / 100.0f));
}
cb->addItem(tr("Unlimited"), QVariant(0.0f));
const int custom_index = cb->count();
cb->addItem(tr("Custom"));
if (const int index = cb->findData(QVariant(value)); index >= 0)
{
cb->setCurrentIndex(index);
}
else if (value > 0.0f)
{
cb->setItemText(custom_index, tr("Custom [%1% / %2 FPS (NTSC) / %3 FPS (PAL)]")
.arg(value * 100)
.arg(60 * value)
.arg(50 * value));
cb->setCurrentIndex(custom_index);
}
connect(cb, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
[this, cb, section, key](int index) { handleSpeedComboChange(cb, section, key); });
}
void EmulationSettingsWidget::onFastForwardSpeedIndexChanged(int index)
void EmulationSettingsWidget::handleSpeedComboChange(QComboBox* cb, const char* section, const char* key)
{
bool okay;
const float value = m_ui.fastForwardSpeed->currentData().toFloat(&okay);
QtHost::SetBaseFloatSettingValue("Framerate", "TurboScalar", okay ? value : 1.0f);
g_emu_thread->applySettings();
const int custom_index = cb->count() - 1;
const int current_index = cb->currentIndex();
std::optional<float> new_value;
if (current_index == custom_index)
{
bool ok = false;
const double custom_value = QInputDialog::getDouble(
QtUtils::GetRootWidget(this), tr("Custom Speed"), tr("Enter Custom Speed"), cb->currentData().toFloat(), 0.0f, 5000.0f, 1, &ok);
if (!ok)
{
// we need to set back to the old value
float value = m_dialog->getEffectiveFloatValue(section, key, 1.0f);
QSignalBlocker sb(cb);
if (m_dialog->isPerGameSettings() && !m_dialog->getSettingsInterface()->GetFloatValue(section, key, &value))
cb->setCurrentIndex(0);
else if (const int index = cb->findData(QVariant(value)); index >= 0)
cb->setCurrentIndex(index);
return;
}
cb->setItemText(custom_index, tr("Custom [%1% / %2 FPS (NTSC) / %3 FPS (PAL)]")
.arg(custom_value)
.arg((60 * custom_value) / 100)
.arg((50 * custom_value) / 100));
new_value = static_cast<float>(custom_value / 100.0);
}
else if (current_index > 0 || !m_dialog->isPerGameSettings())
{
new_value = cb->currentData().toFloat();
}
m_dialog->setFloatSettingValue(section, key, new_value);
}
void EmulationSettingsWidget::onSlowMotionSpeedIndexChanged(int index)
{
bool okay;
const float value = m_ui.slowMotionSpeed->currentData().toFloat(&okay);
QtHost::SetBaseFloatSettingValue("Framerate", "SlomoScalar", okay ? value : 1.0f);
g_emu_thread->applySettings();
}
void EmulationSettingsWidget::onOptimalFramePacingChanged(bool checked)
void EmulationSettingsWidget::onOptimalFramePacingChanged()
{
const QSignalBlocker sb(m_ui.maxFrameLatency);
m_ui.maxFrameLatency->setValue(DEFAULT_FRAME_LATENCY);
m_ui.maxFrameLatency->setEnabled(!checked);
QtHost::SetBaseIntSettingValue("EmuCore/GS", "VsyncQueueSize", checked ? 0 : DEFAULT_FRAME_LATENCY);
g_emu_thread->applySettings();
std::optional<int> value;
if (m_ui.optimalFramePacing->checkState() != Qt::PartiallyChecked)
value = m_ui.optimalFramePacing->isChecked() ? 0 : DEFAULT_FRAME_LATENCY;
m_ui.maxFrameLatency->setValue(DEFAULT_FRAME_LATENCY);
m_ui.maxFrameLatency->setEnabled(!m_dialog->isPerGameSettings() && !m_ui.optimalFramePacing->isChecked());
m_dialog->setIntSettingValue("EmuCore/GS", "VsyncQueueSize", value);
}
void EmulationSettingsWidget::updateOptimalFramePacing()
{
const int value = QtHost::GetBaseIntSettingValue("EmuCore/GS", "VsyncQueueSize", DEFAULT_FRAME_LATENCY);
const bool optimal = (value == 0);
const QSignalBlocker sb(m_ui.optimalFramePacing);
m_ui.optimalFramePacing->setChecked(optimal);
const QSignalBlocker sb2(m_ui.maxFrameLatency);
m_ui.maxFrameLatency->setEnabled(!optimal);
int value = m_dialog->getEffectiveIntValue("EmuCore/GS", "VsyncQueueSize", DEFAULT_FRAME_LATENCY);
bool optimal = (value == 0);
if (m_dialog->isPerGameSettings() && !m_dialog->getSettingsInterface()->GetIntValue("EmuCore/GS", "VsyncQueueSize", &value))
{
m_ui.optimalFramePacing->setCheckState(Qt::PartiallyChecked);
m_ui.maxFrameLatency->setEnabled(false);
}
else
{
m_ui.optimalFramePacing->setChecked(optimal);
m_ui.maxFrameLatency->setEnabled(!optimal);
}
m_ui.maxFrameLatency->setValue(optimal ? DEFAULT_FRAME_LATENCY : value);
}

View File

@ -26,17 +26,18 @@ class EmulationSettingsWidget : public QWidget
Q_OBJECT
public:
EmulationSettingsWidget(QWidget* parent, SettingsDialog* dialog);
EmulationSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~EmulationSettingsWidget();
private Q_SLOTS:
void onNormalSpeedIndexChanged(int index);
void onFastForwardSpeedIndexChanged(int index);
void onSlowMotionSpeedIndexChanged(int index);
void onOptimalFramePacingChanged(bool checked);
void onOptimalFramePacingChanged();
private:
void initializeSpeedCombo(QComboBox* cb, const char* section, const char* key, float default_value);
void handleSpeedComboChange(QComboBox* cb, const char* section, const char* key);
void updateOptimalFramePacing();
SettingsDialog* m_dialog;
Ui::EmulationSettingsWidget m_ui;
};

View File

@ -24,27 +24,29 @@
#include "SettingWidgetBinder.h"
#include "SettingsDialog.h"
GameFixSettingsWidget::GameFixSettingsWidget(QWidget* parent, SettingsDialog* dialog)
GameFixSettingsWidget::GameFixSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent)
{
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.FpuMulHack, "EmuCore/Gamefixes", "FpuMulHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.FpuNegDivHack, "EmuCore/Gamefixes", "FpuNegDivHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.GoemonTlbHack, "EmuCore/Gamefixes", "GoemonTlbHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.SoftwareRendererFMVHack, "EmuCore/Gamefixes", "SoftwareRendererFMVHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.SkipMPEGHack, "EmuCore/Gamefixes", "SkipMPEGHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.OPHFlagHack, "EmuCore/Gamefixes", "OPHFlagHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.EETimingHack, "EmuCore/Gamefixes", "EETimingHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.DMABusyHack, "EmuCore/Gamefixes", "DMABusyHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.GIFFIFOHack, "EmuCore/Gamefixes", "GIFFIFOHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.VIFFIFOHack, "EmuCore/Gamefixes", "VIFFIFOHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.VIF1StallHack, "EmuCore/Gamefixes", "VIF1StallHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.VuAddSubHack, "EmuCore/Gamefixes", "VuAddSubHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.IbitHack, "EmuCore/Gamefixes", "IbitHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.VUKickstartHack, "EmuCore/Gamefixes", "VUKickstartHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.VUOverflowHack, "EmuCore/Gamefixes", "VUOverflowHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.XgKickHack, "EmuCore/Gamefixes", "XgKickHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.FpuMulHack, "EmuCore/Gamefixes", "FpuMulHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.FpuNegDivHack, "EmuCore/Gamefixes", "FpuNegDivHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.GoemonTlbHack, "EmuCore/Gamefixes", "GoemonTlbHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.SoftwareRendererFMVHack, "EmuCore/Gamefixes", "SoftwareRendererFMVHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.SkipMPEGHack, "EmuCore/Gamefixes", "SkipMPEGHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.OPHFlagHack, "EmuCore/Gamefixes", "OPHFlagHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.EETimingHack, "EmuCore/Gamefixes", "EETimingHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.DMABusyHack, "EmuCore/Gamefixes", "DMABusyHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.GIFFIFOHack, "EmuCore/Gamefixes", "GIFFIFOHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.VIFFIFOHack, "EmuCore/Gamefixes", "VIFFIFOHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.VIF1StallHack, "EmuCore/Gamefixes", "VIF1StallHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.VuAddSubHack, "EmuCore/Gamefixes", "VuAddSubHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.IbitHack, "EmuCore/Gamefixes", "IbitHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.VUKickstartHack, "EmuCore/Gamefixes", "VUKickstartHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.VUOverflowHack, "EmuCore/Gamefixes", "VUOverflowHack", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.XgKickHack, "EmuCore/Gamefixes", "XgKickHack", false);
}
GameFixSettingsWidget::~GameFixSettingsWidget() = default;

View File

@ -26,7 +26,7 @@ class GameFixSettingsWidget : public QWidget
Q_OBJECT
public:
GameFixSettingsWidget(QWidget* parent, SettingsDialog* dialog);
GameFixSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~GameFixSettingsWidget();
private:

View File

@ -31,7 +31,7 @@
#include "QtHost.h"
#include "QtUtils.h"
GameListSettingsWidget::GameListSettingsWidget(QWidget* parent, SettingsDialog* dialog)
GameListSettingsWidget::GameListSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent)
{
m_ui.setupUi(this);

View File

@ -28,7 +28,7 @@ class GameListSettingsWidget : public QWidget
Q_OBJECT
public:
GameListSettingsWidget(QWidget* parent, SettingsDialog* dialog);
GameListSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~GameListSettingsWidget();
bool addExcludedPath(const std::string& path);

View File

@ -54,69 +54,65 @@ static constexpr RendererInfo s_renderer_info[] = {
};
static const char* s_anisotropic_filtering_entries[] = {QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Off (Default)"),
QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "2x"),
QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "4x"),
QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "8x"),
QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "16x"),
nullptr};
QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "2x"), QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "4x"),
QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "8x"), QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "16x"), nullptr};
static const char* s_anisotropic_filtering_values[] = {"1", "2", "4", "8", "16", nullptr};
static constexpr int DEFAULT_INTERLACE_MODE = 7;
GraphicsSettingsWidget::GraphicsSettingsWidget(QWidget* parent, SettingsDialog* dialog)
GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent)
, m_dialog(dialog)
{
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this);
//////////////////////////////////////////////////////////////////////////
// Global Settings
//////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToStringSetting(m_ui.adapter, "EmuCore/GS", "Adapter");
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.vsync, "EmuCore/GS", "VsyncEnable", 0);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.enableHWFixes, "EmuCore/GS", "UserHacks", false);
SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.adapter, "EmuCore/GS", "Adapter");
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.vsync, "EmuCore/GS", "VsyncEnable", 0);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableHWFixes, "EmuCore/GS", "UserHacks", false);
//////////////////////////////////////////////////////////////////////////
// Game Display Settings
//////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToEnumSetting(m_ui.aspectRatio, "EmuCore/GS", "AspectRatio",
Pcsx2Config::GSOptions::AspectRatioNames, AspectRatioType::R4_3);
SettingWidgetBinder::BindWidgetToEnumSetting(m_ui.fmvAspectRatio, "EmuCore/GS", "FMVAspectRatioSwitch",
Pcsx2Config::GSOptions::FMVAspectRatioSwitchNames,
FMVAspectRatioSwitchType::Off);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.interlacing, "EmuCore/GS", "interlace", DEFAULT_INTERLACE_MODE);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.bilinearFiltering, "EmuCore/GS", "LinearPresent", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.integerScaling, "EmuCore/GS", "IntegerScaling", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.internalResolutionScreenshots, "EmuCore/GS",
"InternalResolutionScreenshots", false);
SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.zoom, "EmuCore/GS", "Zoom", 100.0f);
SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.stretchY, "EmuCore/GS", "StretchY", 100.0f);
SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.offsetX, "EmuCore/GS", "OffsetX", 0.0f);
SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.offsetY, "EmuCore/GS", "OffsetY", 0.0f);
SettingWidgetBinder::BindWidgetToEnumSetting(
sif, m_ui.aspectRatio, "EmuCore/GS", "AspectRatio", Pcsx2Config::GSOptions::AspectRatioNames, AspectRatioType::R4_3);
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.fmvAspectRatio, "EmuCore/GS", "FMVAspectRatioSwitch",
Pcsx2Config::GSOptions::FMVAspectRatioSwitchNames, FMVAspectRatioSwitchType::Off);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.interlacing, "EmuCore/GS", "interlace", DEFAULT_INTERLACE_MODE);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.bilinearFiltering, "EmuCore/GS", "LinearPresent", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.integerScaling, "EmuCore/GS", "IntegerScaling", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.internalResolutionScreenshots, "EmuCore/GS", "InternalResolutionScreenshots", false);
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.zoom, "EmuCore/GS", "Zoom", 100.0f);
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.stretchY, "EmuCore/GS", "StretchY", 100.0f);
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.offsetX, "EmuCore/GS", "OffsetX", 0.0f);
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.offsetY, "EmuCore/GS", "OffsetY", 0.0f);
connect(m_ui.integerScaling, &QCheckBox::stateChanged, this, &GraphicsSettingsWidget::onIntegerScalingChanged);
onIntegerScalingChanged();
connect(m_ui.fullscreenModes, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&GraphicsSettingsWidget::onFullscreenModeChanged);
connect(m_ui.fullscreenModes, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &GraphicsSettingsWidget::onFullscreenModeChanged);
//////////////////////////////////////////////////////////////////////////
// OSD Settings
//////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.osdScale, "EmuCore/GS", "OsdScale", 100.0f);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowMessages, "EmuCore/GS", "OsdShowMessages", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowSpeed, "EmuCore/GS", "OsdShowSpeed", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowFPS, "EmuCore/GS", "OsdShowFPS", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowCPU, "EmuCore/GS", "OsdShowCPU", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowResolution, "EmuCore/GS", "OsdShowResolution", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowGSStats, "EmuCore/GS", "OsdShowGSStats", false);
SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.osdScale, "EmuCore/GS", "OsdScale", 100.0f);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.osdShowMessages, "EmuCore/GS", "OsdShowMessages", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.osdShowSpeed, "EmuCore/GS", "OsdShowSpeed", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.osdShowFPS, "EmuCore/GS", "OsdShowFPS", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.osdShowCPU, "EmuCore/GS", "OsdShowCPU", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.osdShowResolution, "EmuCore/GS", "OsdShowResolution", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.osdShowGSStats, "EmuCore/GS", "OsdShowGSStats", false);
dialog->registerWidgetHelp(m_ui.osdShowMessages, tr("Show OSD Messages"), tr("Checked"),
tr("Shows on-screen-display messages when events occur such as save states being "
"created/loaded, screenshots being taken, etc."));
dialog->registerWidgetHelp(m_ui.osdShowFPS, tr("Show Game Frame Rate"), tr("Unchecked"),
tr("Shows the internal frame rate of the game in the top-right corner of the display."));
dialog->registerWidgetHelp(
m_ui.osdShowSpeed, tr("Show Emulation Speed"), tr("Unchecked"),
dialog->registerWidgetHelp(m_ui.osdShowSpeed, tr("Show Emulation Speed"), tr("Unchecked"),
tr("Shows the current emulation speed of the system in the top-right corner of the display as a percentage."));
dialog->registerWidgetHelp(m_ui.osdShowResolution, tr("Show Resolution"), tr("Unchecked"),
tr("Shows the resolution of the game in the top-right corner of the display."));
@ -124,86 +120,91 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(QWidget* parent, SettingsDialog*
//////////////////////////////////////////////////////////////////////////
// HW Settings
//////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.upscaleMultiplier, "EmuCore/GS", "upscale_multiplier", 1);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.textureFiltering, "EmuCore/GS", "filter",
static_cast<int>(BiFiltering::PS2));
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.trilinearFiltering, "EmuCore/GS", "UserHacks_TriFilter",
static_cast<int>(TriFiltering::Off));
SettingWidgetBinder::BindWidgetToEnumSetting(m_ui.anisotropicFiltering, "EmuCore/GS", "MaxAnisotropy",
s_anisotropic_filtering_entries, s_anisotropic_filtering_values, "1");
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.dithering, "EmuCore/GS", "dithering_ps2", 2);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.mipmapping, "EmuCore/GS", "mipmap_hw",
static_cast<int>(HWMipmapLevel::Automatic), -1);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.crcFixLevel, "EmuCore/GS", "crc_hack_level",
static_cast<int>(CRCHackLevel::Automatic), -1);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.blending, "EmuCore/GS", "accurate_blending_unit",
static_cast<int>(AccBlendLevel::Basic));
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.accurateDATE, "EmuCore/GS", "accurate_date", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.conservativeBufferAllocation, "EmuCore/GS",
"conservative_framebuffer", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.gpuPaletteConversion, "EmuCore/GS", "paltex", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.preloadTexture, "EmuCore/GS", "preload_texture", false);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.upscaleMultiplier, "EmuCore/GS", "upscale_multiplier", 1);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.textureFiltering, "EmuCore/GS", "filter", static_cast<int>(BiFiltering::PS2));
SettingWidgetBinder::BindWidgetToIntSetting(
sif, m_ui.trilinearFiltering, "EmuCore/GS", "UserHacks_TriFilter", static_cast<int>(TriFiltering::Off));
SettingWidgetBinder::BindWidgetToEnumSetting(
sif, m_ui.anisotropicFiltering, "EmuCore/GS", "MaxAnisotropy", s_anisotropic_filtering_entries, s_anisotropic_filtering_values, "1");
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.dithering, "EmuCore/GS", "dithering_ps2", 2);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.mipmapping, "EmuCore/GS", "mipmap_hw", static_cast<int>(HWMipmapLevel::Automatic), -1);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.crcFixLevel, "EmuCore/GS", "crc_hack_level", static_cast<int>(CRCHackLevel::Automatic), -1);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.blending, "EmuCore/GS", "accurate_blending_unit", static_cast<int>(AccBlendLevel::Basic));
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.accurateDATE, "EmuCore/GS", "accurate_date", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.conservativeBufferAllocation, "EmuCore/GS", "conservative_framebuffer", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.gpuPaletteConversion, "EmuCore/GS", "paltex", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.preloadTexture, "EmuCore/GS", "preload_texture", false);
//////////////////////////////////////////////////////////////////////////
// HW Renderer Fixes
//////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.halfScreenFix, "EmuCore/GS", "UserHacks_Half_Bottom_Override", -1,
-1);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.skipDrawRangeStart, "EmuCore/GS", "UserHacks_SkipDraw", 0);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.skipDrawRangeCount, "EmuCore/GS", "UserHacks_SkipDraw_Offset", 0);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.hwAutoFlush, "EmuCore/GS", "UserHacks_AutoFlush", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.frameBufferConversion, "EmuCore/GS", "UserHacks_CPU_FB_Conversion",
false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.disableDepthEmulation, "EmuCore/GS",
"UserHacks_DisableDepthSupport", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.memoryWrapping, "EmuCore/GS", "wrap_gs_mem", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.disableSafeFeatures, "EmuCore/GS",
"UserHacks_Disable_Safe_Features", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.preloadFrameData, "EmuCore/GS", "preload_frame_with_gs_data",
false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.fastTextureInvalidation, "EmuCore/GS",
"UserHacks_DisablePartialInvalidation", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.textureInsideRt, "EmuCore/GS",
"UserHacks_TextureInsideRt", false);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.halfScreenFix, "EmuCore/GS", "UserHacks_Half_Bottom_Override", -1, -1);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawRangeStart, "EmuCore/GS", "UserHacks_SkipDraw", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawRangeCount, "EmuCore/GS", "UserHacks_SkipDraw_Offset", 0);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hwAutoFlush, "EmuCore/GS", "UserHacks_AutoFlush", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.frameBufferConversion, "EmuCore/GS", "UserHacks_CPU_FB_Conversion", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableDepthEmulation, "EmuCore/GS", "UserHacks_DisableDepthSupport", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.memoryWrapping, "EmuCore/GS", "wrap_gs_mem", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableSafeFeatures, "EmuCore/GS", "UserHacks_Disable_Safe_Features", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.preloadFrameData, "EmuCore/GS", "preload_frame_with_gs_data", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastTextureInvalidation, "EmuCore/GS", "UserHacks_DisablePartialInvalidation", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.textureInsideRt, "EmuCore/GS", "UserHacks_TextureInsideRt", false);
//////////////////////////////////////////////////////////////////////////
// HW Upscaling Fixes
//////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.halfPixelOffset, "EmuCore/GS", "UserHacks_HalfPixelOffset", 0);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.roundSprite, "EmuCore/GS", "UserHacks_RoundSprite", 0);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.textureOffsetX, "EmuCore/GS", "UserHacks_TCOffsetX", 0);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.textureOffsetY, "EmuCore/GS", "UserHacks_TCOffsetY", 0);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.alignSprite, "EmuCore/GS", "UserHacks_align_sprite_X", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.mergeSprite, "EmuCore/GS", "UserHacks_merge_pp_sprite", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.wildHack, "EmuCore/GS", "UserHacks_WildHack", false);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.halfPixelOffset, "EmuCore/GS", "UserHacks_HalfPixelOffset", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.roundSprite, "EmuCore/GS", "UserHacks_RoundSprite", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.textureOffsetX, "EmuCore/GS", "UserHacks_TCOffsetX", 0);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.textureOffsetY, "EmuCore/GS", "UserHacks_TCOffsetY", 0);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.alignSprite, "EmuCore/GS", "UserHacks_align_sprite_X", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.mergeSprite, "EmuCore/GS", "UserHacks_merge_pp_sprite", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.wildHack, "EmuCore/GS", "UserHacks_WildHack", false);
//////////////////////////////////////////////////////////////////////////
// Advanced Settings
//////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.useBlitSwapChain, "EmuCore/GS", "UseBlitSwapChain", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.useDebugDevice, "EmuCore/GS", "UseDebugDevice", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useBlitSwapChain, "EmuCore/GS", "UseBlitSwapChain", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useDebugDevice, "EmuCore/GS", "UseDebugDevice", false);
//////////////////////////////////////////////////////////////////////////
// SW Settings
//////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.extraSWThreads, "EmuCore/GS", "extrathreads", 2);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.swAutoFlush, "EmuCore/GS", "autoflush_sw", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.swAA1, "EmuCore/GS", "aa1", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.swMipmap, "EmuCore/GS", "mipmap", true);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.extraSWThreads, "EmuCore/GS", "extrathreads", 2);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.swAutoFlush, "EmuCore/GS", "autoflush_sw", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.swAA1, "EmuCore/GS", "aa1", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.swMipmap, "EmuCore/GS", "mipmap", true);
//////////////////////////////////////////////////////////////////////////
// Non-trivial settings
//////////////////////////////////////////////////////////////////////////
const GSRendererType current_renderer = static_cast<GSRendererType>(
QtHost::GetBaseIntSettingValue("EmuCore/GS", "Renderer", static_cast<int>(GSRendererType::Auto)));
const int renderer = m_dialog->getEffectiveIntValue("EmuCore/GS", "Renderer", static_cast<int>(GSRendererType::Auto));
for (const RendererInfo& ri : s_renderer_info)
{
m_ui.renderer->addItem(qApp->translate("GraphicsSettingsWidget", ri.name));
if (ri.type == current_renderer)
if (renderer == static_cast<int>(ri.type))
m_ui.renderer->setCurrentIndex(m_ui.renderer->count() - 1);
}
connect(m_ui.renderer, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&GraphicsSettingsWidget::onRendererChanged);
// per-game override for renderer is slightly annoying, since we need to populate the global setting field
if (sif)
{
QString global_renderer_name;
for (const RendererInfo& ri : s_renderer_info)
{
if (renderer == static_cast<int>(ri.type))
global_renderer_name = qApp->translate("GraphicsSettingsWidget", ri.name);
}
m_ui.renderer->insertItem(0, tr("Use Global Setting [%1]").arg(global_renderer_name));
int override_renderer;
if (sif->GetIntValue("EmuCore/GS", "Renderer", &override_renderer))
m_ui.renderer->setCurrentIndex(override_renderer + 1);
else
m_ui.renderer->setCurrentIndex(0);
}
connect(m_ui.renderer, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &GraphicsSettingsWidget::onRendererChanged);
connect(m_ui.enableHWFixes, &QCheckBox::stateChanged, this, &GraphicsSettingsWidget::onEnableHardwareFixesChanged);
updateRendererDependentOptions();
@ -217,33 +218,69 @@ GraphicsSettingsWidget::~GraphicsSettingsWidget() = default;
void GraphicsSettingsWidget::onRendererChanged(int index)
{
QtHost::SetBaseIntSettingValue("EmuCore/GS", "Renderer", static_cast<int>(s_renderer_info[index].type));
if (m_dialog->isPerGameSettings())
{
if (index > 0)
m_dialog->setIntSettingValue("EmuCore/GS", "Renderer", static_cast<int>(s_renderer_info[index - 1].type));
else
m_dialog->setIntSettingValue("EmuCore/GS", "Renderer", std::nullopt);
}
else
{
m_dialog->setIntSettingValue("EmuCore/GS", "Renderer", static_cast<int>(s_renderer_info[index].type));
}
g_emu_thread->applySettings();
updateRendererDependentOptions();
}
void GraphicsSettingsWidget::onAdapterChanged(int index)
{
if (index == 0)
QtHost::RemoveBaseSettingValue("EmuCore/GS", "Adapter");
const int first_adapter = m_dialog->isPerGameSettings() ? 2 : 1;
if (index >= first_adapter)
m_dialog->setStringSettingValue("EmuCore/GS", "Adapter", m_ui.adapter->currentText().toUtf8().constData());
else if (index > 0 && m_dialog->isPerGameSettings())
m_dialog->setStringSettingValue("EmuCore/GS", "Adapter", "");
else
QtHost::SetBaseStringSettingValue("EmuCore/GS", "Adapter", m_ui.adapter->currentText().toUtf8().constData());
m_dialog->setStringSettingValue("EmuCore/GS", "Adapter", std::nullopt);
g_emu_thread->applySettings();
}
void GraphicsSettingsWidget::onFullscreenModeChanged(int index)
{
const int first_mode = m_dialog->isPerGameSettings() ? 2 : 1;
if (index >= first_mode)
m_dialog->setStringSettingValue("EmuCore/GS", "FullscreenMode", m_ui.fullscreenModes->currentText().toUtf8().constData());
else if (index > 0 && m_dialog->isPerGameSettings())
m_dialog->setStringSettingValue("EmuCore/GS", "FullscreenMode", "");
else
m_dialog->setStringSettingValue("EmuCore/GS", "FullscreenMode", std::nullopt);
g_emu_thread->applySettings();
}
void GraphicsSettingsWidget::onIntegerScalingChanged() { m_ui.bilinearFiltering->setEnabled(!m_ui.integerScaling->isChecked()); }
void GraphicsSettingsWidget::onEnableHardwareFixesChanged()
{
const bool enabled = m_ui.enableHWFixes->isChecked();
const bool enabled = (m_ui.enableHWFixes->checkState() == Qt::Checked);
m_ui.hardwareRendererGroup->setTabEnabled(2, enabled);
m_ui.hardwareRendererGroup->setTabEnabled(3, enabled);
}
GSRendererType GraphicsSettingsWidget::getEffectiveRenderer() const
{
const GSRendererType type =
static_cast<GSRendererType>(m_dialog->getEffectiveIntValue("EmuCore/GS", "Renderer", static_cast<int>(GSRendererType::Auto)));
return (type == GSRendererType::Auto) ? GSUtil::GetPreferredRenderer() : type;
}
void GraphicsSettingsWidget::updateRendererDependentOptions()
{
const int index = m_ui.renderer->currentIndex();
GSRendererType type = s_renderer_info[index].type;
if (type == GSRendererType::Auto)
type = GSUtil::GetPreferredRenderer();
const GSRendererType type = getEffectiveRenderer();
#ifdef _WIN32
const bool is_dx11 = (type == GSRendererType::DX11 || type == GSRendererType::SW);
@ -253,9 +290,7 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
const bool is_hardware = (type == GSRendererType::DX11 || type == GSRendererType::OGL || type == GSRendererType::VK);
const bool is_software = (type == GSRendererType::SW);
const int current_tab = m_hardware_renderer_visible ?
m_ui.hardwareRendererGroup->currentIndex() :
m_ui.softwareRendererGroup->currentIndex();
const int current_tab = m_hardware_renderer_visible ? m_ui.hardwareRendererGroup->currentIndex() : m_ui.softwareRendererGroup->currentIndex();
// move advanced tab to the correct parent
static constexpr std::array<const char*, 3> move_tab_names = {{"Display", "On-Screen Display", "Advanced"}};
@ -334,11 +369,26 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
// fill+select adapters
{
const std::string current_adapter = QtHost::GetBaseStringSettingValue("EmuCore/GS", "Adapter", "");
QSignalBlocker sb(m_ui.adapter);
std::string current_adapter = QtHost::GetBaseStringSettingValue("EmuCore/GS", "Adapter", "");
m_ui.adapter->clear();
m_ui.adapter->setEnabled(!modes.adapter_names.empty());
m_ui.adapter->addItem(tr("(Default)"));
m_ui.adapter->setCurrentIndex(0);
if (m_dialog->isPerGameSettings())
{
m_ui.adapter->insertItem(
0, tr("Use Global Setting [%1]").arg(current_adapter.empty() ? tr("(Default)") : QString::fromStdString(current_adapter)));
if (!m_dialog->getSettingsInterface()->GetStringValue("EmuCore/GS", "Adapter", &current_adapter))
{
// clear the adapter so we don't set it to the global value
current_adapter.clear();
m_ui.adapter->setCurrentIndex(0);
}
}
for (const std::string& adapter : modes.adapter_names)
{
m_ui.adapter->addItem(QString::fromStdString(adapter));
@ -351,11 +401,21 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
{
QSignalBlocker sb(m_ui.fullscreenModes);
const std::string current_mode(QtHost::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", ""));
std::string current_mode(QtHost::GetBaseStringSettingValue("EmuCore/GS", "FullscreenMode", ""));
m_ui.fullscreenModes->clear();
m_ui.fullscreenModes->addItem(tr("Borderless Fullscreen"));
if (current_mode.empty())
m_ui.fullscreenModes->setCurrentIndex(0);
m_ui.fullscreenModes->setCurrentIndex(0);
if (m_dialog->isPerGameSettings())
{
m_ui.fullscreenModes->insertItem(
0, tr("Use Global Setting [%1]").arg(current_mode.empty() ? tr("(Default)") : QString::fromStdString(current_mode)));
if (!m_dialog->getSettingsInterface()->GetStringValue("EmuCore/GS", "FullscreenMode", &current_mode))
{
current_mode.clear();
m_ui.fullscreenModes->setCurrentIndex(0);
}
}
for (const std::string& fs_mode : modes.fullscreen_modes)
{
@ -368,23 +428,3 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
m_ui.enableHWFixes->setEnabled(is_hardware);
onEnableHardwareFixesChanged();
}
void GraphicsSettingsWidget::onIntegerScalingChanged()
{
m_ui.bilinearFiltering->setEnabled(!m_ui.integerScaling->isChecked());
}
void GraphicsSettingsWidget::onFullscreenModeChanged(int index)
{
if (index == 0)
{
QtHost::RemoveBaseSettingValue("EmuCore/GS", "FullscreenMode");
}
else
{
QtHost::SetBaseStringSettingValue("EmuCore/GS", "FullscreenMode",
m_ui.fullscreenModes->currentText().toUtf8().constData());
}
g_emu_thread->applySettings();
}

View File

@ -19,6 +19,8 @@
#include "ui_GraphicsSettingsWidget.h"
enum class GSRendererType : s8;
class SettingsDialog;
class GraphicsSettingsWidget : public QWidget
@ -26,7 +28,7 @@ class GraphicsSettingsWidget : public QWidget
Q_OBJECT
public:
GraphicsSettingsWidget(QWidget* parent, SettingsDialog* dialog);
GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~GraphicsSettingsWidget();
void updateRendererDependentOptions();
@ -42,6 +44,10 @@ private Q_SLOTS:
void onFullscreenModeChanged(int index);
private:
GSRendererType getEffectiveRenderer() const;
SettingsDialog* m_dialog;
Ui::GraphicsSettingsWidget m_ui;
bool m_hardware_renderer_visible = true;

View File

@ -27,25 +27,27 @@ static const char* THEME_NAMES[] = {QT_TRANSLATE_NOOP("InterfaceSettingsWidget",
static const char* THEME_VALUES[] = {"", "fusion", "darkfusion", "darkfusionblue", nullptr};
InterfaceSettingsWidget::InterfaceSettingsWidget(QWidget* parent, SettingsDialog* dialog)
InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent)
{
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.inhibitScreensaver, "UI", "InhibitScreensaver", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.discordPresence, "UI", "DiscordPresence", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.confirmPowerOff, "UI", "ConfirmPowerOff", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.saveStateOnExit, "EmuCore", "AutoStateLoadSave", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.pauseOnStart, "UI", "StartPaused", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.pauseOnFocusLoss, "UI", "PauseOnFocusLoss", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.inhibitScreensaver, "UI", "InhibitScreensaver", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.discordPresence, "UI", "DiscordPresence", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.confirmPowerOff, "UI", "ConfirmPowerOff", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.saveStateOnExit, "EmuCore", "AutoStateLoadSave", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnStart, "UI", "StartPaused", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnFocusLoss, "UI", "PauseOnFocusLoss", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.startFullscreen, "UI", "StartFullscreen", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.doubleClickTogglesFullscreen, "UI", "DoubleClickTogglesFullscreen",
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.startFullscreen, "UI", "StartFullscreen", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.doubleClickTogglesFullscreen, "UI", "DoubleClickTogglesFullscreen",
true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.hideMouseCursor, "UI", "HideMouseCursor", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.renderToMainWindow, "UI", "RenderToMainWindow", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hideMouseCursor, "UI", "HideMouseCursor", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.renderToMainWindow, "UI", "RenderToMainWindow", true);
SettingWidgetBinder::BindWidgetToEnumSetting(m_ui.theme, "UI", "Theme", THEME_NAMES, THEME_VALUES,
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.theme, "UI", "Theme", THEME_NAMES, THEME_VALUES,
MainWindow::DEFAULT_THEME_NAME);
connect(m_ui.theme, QOverload<int>::of(&QComboBox::currentIndexChanged), [this]() { emit themeChanged(); });
@ -57,7 +59,7 @@ InterfaceSettingsWidget::InterfaceSettingsWidget(QWidget* parent, SettingsDialog
tr("Shows the game you are currently playing as part of your profile in Discord."));
if (true)
{
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.autoUpdateEnabled, "AutoUpdater", "CheckAtStartup", true);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.autoUpdateEnabled, "AutoUpdater", "CheckAtStartup", true);
dialog->registerWidgetHelp(m_ui.autoUpdateEnabled, tr("Enable Automatic Update Check"), tr("Checked"),
tr("Automatically checks for updates to the program on startup. Updates can be deferred "
"until later or skipped entirely."));

View File

@ -26,7 +26,7 @@ class InterfaceSettingsWidget : public QWidget
Q_OBJECT
public:
InterfaceSettingsWidget(QWidget* parent, SettingsDialog* dialog);
InterfaceSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~InterfaceSettingsWidget();
Q_SIGNALS:

View File

@ -15,7 +15,14 @@
#include "PrecompiledHeader.h"
#include "common/FileSystem.h"
#include "common/StringUtil.h"
#include "pcsx2/Frontend/GameList.h"
#include "pcsx2/Frontend/INISettingsInterface.h"
#include "EmuThread.h"
#include "QtHost.h"
#include "QtUtils.h"
#include "SettingsDialog.h"
#include "AdvancedSystemSettingsWidget.h"
@ -31,33 +38,82 @@
#include <QtWidgets/QMessageBox>
#include <QtWidgets/QTextEdit>
static constexpr char DEFAULT_SETTING_HELP_TEXT[] = "";
static QList<SettingsDialog*> s_open_game_properties_dialogs;
SettingsDialog::SettingsDialog(QWidget* parent /* = nullptr */)
SettingsDialog::SettingsDialog(QWidget* parent)
: QDialog(parent)
, m_game_crc(0)
{
m_ui.setupUi(this);
setCategoryHelpTexts();
setupUi(nullptr);
}
SettingsDialog::SettingsDialog(std::unique_ptr<SettingsInterface> sif, const GameList::Entry* game, u32 game_crc)
: QDialog()
, m_sif(std::move(sif))
, m_game_crc(game_crc)
{
setupUi(game);
s_open_game_properties_dialogs.push_back(this);
}
void SettingsDialog::setupUi(const GameList::Entry* game)
{
const bool show_advanced_settings = true;
m_ui.setupUi(this);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
m_interface_settings = new InterfaceSettingsWidget(m_ui.settingsContainer, this);
m_game_list_settings = new GameListSettingsWidget(m_ui.settingsContainer, this);
m_bios_settings = new BIOSSettingsWidget(m_ui.settingsContainer, this);
m_emulation_settings = new EmulationSettingsWidget(m_ui.settingsContainer, this);
m_system_settings = new SystemSettingsWidget(m_ui.settingsContainer, this);
m_advanced_system_settings = new AdvancedSystemSettingsWidget(m_ui.settingsContainer, this);
m_game_fix_settings_widget = new GameFixSettingsWidget(m_ui.settingsContainer, this);
m_graphics_settings = new GraphicsSettingsWidget(m_ui.settingsContainer, this);
// We don't include interface/game list/bios settings from per-game settings.
if (!isPerGameSettings())
{
addWidget(m_interface_settings = new InterfaceSettingsWidget(this, m_ui.settingsContainer), tr("Interface"),
QStringLiteral("settings-3-line"),
tr("<strong>Interface Settings</strong><hr>These options control how the software looks and behaves.<br><br>Mouse over an option for "
"additional information."));
addWidget(m_game_list_settings = new GameListSettingsWidget(this, m_ui.settingsContainer), tr("Game List"),
QStringLiteral("folder-settings-line"),
tr("<strong>Game List Settings</strong><hr>The list above shows the directories which will be searched by PCSX2 to populate the game "
"list. Search directories can be added, removed, and switched to recursive/non-recursive."));
addWidget(m_bios_settings = new BIOSSettingsWidget(this, m_ui.settingsContainer), tr("BIOS"), QStringLiteral("hard-drive-2-line"),
tr("<strong>BIOS Settings</strong><hr>"));
}
else
{
// TODO: Summary page.
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::InterfaceSettings), m_interface_settings);
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::GameListSettings), m_game_list_settings);
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::BIOSSettings), m_bios_settings);
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::EmulationSettings), m_emulation_settings);
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::SystemSettings), m_system_settings);
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::AdvancedSystemSettings), m_advanced_system_settings);
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::GameFixSettings), m_game_fix_settings_widget);
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::GraphicsSettings), m_graphics_settings);
// remove the preset buttons. but we might want to enable these in the future.
m_ui.restoreDefaultsButton->setVisible(false);
m_ui.settingsPreset->setVisible(false);
}
// Common to both per-game and global settings.
addWidget(m_emulation_settings = new EmulationSettingsWidget(this, m_ui.settingsContainer), tr("Emulation"), QStringLiteral("dashboard-line"),
tr("<strong>Emulation Settings</strong><hr>"));
addWidget(m_system_settings = new SystemSettingsWidget(this, m_ui.settingsContainer), tr("System"), QStringLiteral("artboard-2-line"),
tr("<strong>System Settings</strong><hr>These options determine the configuration of the simulated console.<br><br>Mouse over an option for "
"additional information."));
if (show_advanced_settings)
{
addWidget(m_advanced_system_settings = new AdvancedSystemSettingsWidget(this, m_ui.settingsContainer), tr("Advanced System"),
QStringLiteral("artboard-2-line"), tr("<strong>Advanced System Settings</strong><hr>"));
// Only show the game fixes for per-game settings, there's really no reason to be setting them globally.
if (isPerGameSettings())
{
addWidget(m_game_fix_settings_widget = new GameFixSettingsWidget(this, m_ui.settingsContainer), tr("Game Fix"),
QStringLiteral("close-line"), tr("<strong>Game Fix Settings</strong><hr>"));
}
}
addWidget(m_graphics_settings = new GraphicsSettingsWidget(this, m_ui.settingsContainer), tr("Graphics"), QStringLiteral("brush-line"),
tr("<strong>Graphics Settings</strong><hr>"));
addWidget(new QWidget(m_ui.settingsContainer), tr("Audio"), QStringLiteral("volume-up-line"),
tr("<strong>Audio Settings</strong><hr>These options control the audio output of the console. Mouse over an option for additional "
"information."));
addWidget(
new QWidget(m_ui.settingsContainer), tr("Memory Cards"), QStringLiteral("sd-card-line"), tr("<strong>Memory Card Settings</strong><hr>"));
m_ui.settingsCategory->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
m_ui.settingsCategory->setCurrentRow(0);
@ -68,37 +124,37 @@ SettingsDialog::SettingsDialog(QWidget* parent /* = nullptr */)
connect(m_ui.restoreDefaultsButton, &QPushButton::clicked, this, &SettingsDialog::onRestoreDefaultsClicked);
}
SettingsDialog::~SettingsDialog() = default;
void SettingsDialog::setCategoryHelpTexts()
SettingsDialog::~SettingsDialog()
{
m_category_help_text[static_cast<int>(Category::InterfaceSettings)] =
tr("<strong>Interface Settings</strong><hr>These options control how the software looks and behaves.<br><br>Mouse "
"over "
"an option for additional information.");
m_category_help_text[static_cast<int>(Category::SystemSettings)] =
tr("<strong>System Settings</strong><hr>These options determine the configuration of the simulated "
"console.<br><br>Mouse over an option for additional information.");
m_category_help_text[static_cast<int>(Category::GameListSettings)] =
tr("<strong>Game List Settings</strong><hr>The list above shows the directories which will be searched by "
"PCSX2 to populate the game list. Search directories can be added, removed, and switched to "
"recursive/non-recursive.");
m_category_help_text[static_cast<int>(Category::AudioSettings)] =
tr("<strong>Audio Settings</strong><hr>These options control the audio output of the console. Mouse over an option "
"for additional information.");
if (isPerGameSettings())
s_open_game_properties_dialogs.removeOne(this);
}
void SettingsDialog::setCategory(Category category)
void SettingsDialog::closeEvent(QCloseEvent*)
{
if (category >= Category::Count)
return;
// we need to clean up ourselves, since we're not modal
if (isPerGameSettings())
deleteLater();
}
m_ui.settingsCategory->setCurrentRow(static_cast<int>(category));
void SettingsDialog::setCategory(const char* category)
{
// the titles in the category list will be translated.
const QString translated_category(qApp->translate("SettingsDialog", category));
for (int i = 0; i < m_ui.settingsCategory->count(); i++)
{
if (translated_category == m_ui.settingsCategory->item(i)->text())
{
// will also update the visible widget
m_ui.settingsCategory->setCurrentRow(i);
break;
}
}
}
void SettingsDialog::onCategoryCurrentRowChanged(int row)
{
Q_ASSERT(row < static_cast<int>(Category::Count));
m_ui.settingsContainer->setCurrentIndex(row);
m_ui.helpText->setText(m_category_help_text[row]);
}
@ -106,8 +162,8 @@ void SettingsDialog::onCategoryCurrentRowChanged(int row)
void SettingsDialog::onRestoreDefaultsClicked()
{
if (QMessageBox::question(this, tr("Confirm Restore Defaults"),
tr("Are you sure you want to restore the default settings? Any preferences will be lost."),
QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes)
tr("Are you sure you want to restore the default settings? Any preferences will be lost."), QMessageBox::Yes,
QMessageBox::No) != QMessageBox::Yes)
{
return;
}
@ -115,6 +171,20 @@ void SettingsDialog::onRestoreDefaultsClicked()
// TODO
}
void SettingsDialog::addWidget(QWidget* widget, QString title, QString icon, QString help_text)
{
const int index = m_ui.settingsCategory->count();
QListWidgetItem* item = new QListWidgetItem(m_ui.settingsCategory);
item->setText(title);
if (!icon.isEmpty())
item->setIcon(QIcon::fromTheme(icon));
m_ui.settingsContainer->addWidget(widget);
m_category_help_text[index] = std::move(help_text);
}
void SettingsDialog::registerWidgetHelp(QObject* object, QString title, QString recommended_value, QString text)
{
// construct rich text with formatted description
@ -153,4 +223,202 @@ bool SettingsDialog::eventFilter(QObject* object, QEvent* event)
}
return QDialog::eventFilter(object, event);
}
}
bool SettingsDialog::getEffectiveBoolValue(const char* section, const char* key, bool default_value) const
{
bool value;
if (m_sif && m_sif->GetBoolValue(section, key, &value))
return value;
else
return QtHost::GetBaseBoolSettingValue(section, key, default_value);
}
int SettingsDialog::getEffectiveIntValue(const char* section, const char* key, int default_value) const
{
int value;
if (m_sif && m_sif->GetIntValue(section, key, &value))
return value;
else
return QtHost::GetBaseIntSettingValue(section, key, default_value);
}
float SettingsDialog::getEffectiveFloatValue(const char* section, const char* key, float default_value) const
{
float value;
if (m_sif && m_sif->GetFloatValue(section, key, &value))
return value;
else
return QtHost::GetBaseFloatSettingValue(section, key, default_value);
}
std::string SettingsDialog::getEffectiveStringValue(const char* section, const char* key, const char* default_value) const
{
std::string value;
if (!m_sif || !m_sif->GetStringValue(section, key, &value))
value = QtHost::GetBaseStringSettingValue(section, key, default_value);
return value;
}
std::optional<bool> SettingsDialog::getBoolValue(const char* section, const char* key, std::optional<bool> default_value) const
{
std::optional<bool> value;
if (m_sif)
{
bool bvalue;
if (m_sif->GetBoolValue(section, key, &bvalue))
value = bvalue;
else
value = default_value;
}
else
{
value = QtHost::GetBaseBoolSettingValue(section, key, default_value.value_or(false));
}
return value;
}
std::optional<int> SettingsDialog::getIntValue(const char* section, const char* key, std::optional<int> default_value) const
{
std::optional<int> value;
if (m_sif)
{
int ivalue;
if (m_sif->GetIntValue(section, key, &ivalue))
value = ivalue;
else
value = default_value;
}
else
{
value = QtHost::GetBaseIntSettingValue(section, key, default_value.value_or(0));
}
return value;
}
std::optional<float> SettingsDialog::getFloatValue(const char* section, const char* key, std::optional<float> default_value) const
{
std::optional<float> value;
if (m_sif)
{
float fvalue;
if (m_sif->GetFloatValue(section, key, &fvalue))
value = fvalue;
else
value = default_value;
}
else
{
value = QtHost::GetBaseFloatSettingValue(section, key, default_value.value_or(0.0f));
}
return value;
}
std::optional<std::string> SettingsDialog::getStringValue(const char* section, const char* key, std::optional<const char*> default_value) const
{
std::optional<std::string> value;
if (m_sif)
{
std::string svalue;
if (m_sif->GetStringValue(section, key, &svalue))
value = std::move(svalue);
else if (default_value.has_value())
value = default_value.value();
}
else
{
value = QtHost::GetBaseStringSettingValue(section, key, default_value.value_or(""));
}
return value;
}
void SettingsDialog::setBoolSettingValue(const char* section, const char* key, std::optional<bool> value)
{
if (m_sif)
{
value.has_value() ? m_sif->SetBoolValue(section, key, value.value()) : m_sif->DeleteValue(section, key);
m_sif->Save();
}
else
{
value.has_value() ? QtHost::SetBaseBoolSettingValue(section, key, value.value()) : QtHost::RemoveBaseSettingValue(section, key);
}
g_emu_thread->applySettings();
}
void SettingsDialog::setIntSettingValue(const char* section, const char* key, std::optional<int> value)
{
if (m_sif)
{
value.has_value() ? m_sif->SetIntValue(section, key, value.value()) : m_sif->DeleteValue(section, key);
m_sif->Save();
}
else
{
value.has_value() ? QtHost::SetBaseIntSettingValue(section, key, value.value()) : QtHost::RemoveBaseSettingValue(section, key);
}
g_emu_thread->applySettings();
}
void SettingsDialog::setFloatSettingValue(const char* section, const char* key, std::optional<float> value)
{
if (m_sif)
{
value.has_value() ? m_sif->SetFloatValue(section, key, value.value()) : m_sif->DeleteValue(section, key);
m_sif->Save();
}
else
{
value.has_value() ? QtHost::SetBaseFloatSettingValue(section, key, value.value()) : QtHost::RemoveBaseSettingValue(section, key);
}
g_emu_thread->applySettings();
}
void SettingsDialog::setStringSettingValue(const char* section, const char* key, std::optional<const char*> value)
{
if (m_sif)
{
value.has_value() ? m_sif->SetBoolValue(section, key, value.value()) : m_sif->DeleteValue(section, key);
m_sif->Save();
}
else
{
value.has_value() ? QtHost::SetBaseStringSettingValue(section, key, value.value()) : QtHost::RemoveBaseSettingValue(section, key);
}
g_emu_thread->applySettings();
}
void SettingsDialog::openGamePropertiesDialog(const GameList::Entry* game, u32 crc)
{
// check for an existing dialog with this crc
for (SettingsDialog* dialog : s_open_game_properties_dialogs)
{
if (dialog->m_game_crc == crc)
{
dialog->show();
dialog->setFocus();
return;
}
}
std::unique_ptr<INISettingsInterface> sif =
std::make_unique<INISettingsInterface>(Path::CombineStdString(EmuFolders::GameSettings, StringUtil::StdStringFromFormat("%08X.ini", crc)));
if (FileSystem::FileExists(sif->GetFileName().c_str()))
sif->Load();
const QString window_title(tr("%1 [%2]")
.arg(game ? QtUtils::StringViewToQString(game->title) : QStringLiteral("<UNKNOWN>"))
.arg(QtUtils::StringViewToQString(FileSystem::GetFileNameFromPath(sif->GetFileName()))));
SettingsDialog* dialog = new SettingsDialog(std::move(sif), game, crc);
dialog->setWindowTitle(window_title);
dialog->show();
}

View File

@ -19,6 +19,14 @@
#include <QtCore/QString>
#include <QtWidgets/QDialog>
#include <array>
#include <memory>
class INISettingsInterface;
namespace GameList
{
struct Entry;
}
class InterfaceSettingsWidget;
class GameListSettingsWidget;
@ -36,50 +44,68 @@ class SettingsDialog final : public QDialog
Q_OBJECT
public:
enum class Category
{
InterfaceSettings,
GameListSettings,
BIOSSettings,
EmulationSettings,
SystemSettings,
AdvancedSystemSettings,
GameFixSettings,
GraphicsSettings,
AudioSettings,
MemoryCardSettings,
Count
};
explicit SettingsDialog(QWidget* parent = nullptr);
explicit SettingsDialog(QWidget* parent);
SettingsDialog(std::unique_ptr<SettingsInterface> sif, const GameList::Entry* game, u32 game_crc);
~SettingsDialog();
InterfaceSettingsWidget* getInterfaceSettingsWidget() const { return m_interface_settings; }
GameListSettingsWidget* getGameListSettingsWidget() const { return m_game_list_settings; }
BIOSSettingsWidget* getBIOSSettingsWidget() const { return m_bios_settings; }
EmulationSettingsWidget* getEmulationSettingsWidget() const { return m_emulation_settings; }
SystemSettingsWidget* getSystemSettingsWidget() const { return m_system_settings; }
AdvancedSystemSettingsWidget* getAdvancedSystemSettingsWidget() const { return m_advanced_system_settings; }
GameFixSettingsWidget* getGameFixSettingsWidget() const { return m_game_fix_settings_widget; }
GraphicsSettingsWidget* getGraphicsSettingsWidget() const { return m_graphics_settings; }
AudioSettingsWidget* getAudioSettingsWidget() const { return m_audio_settings; }
MemoryCardSettingsWidget* getMemoryCardSettingsWidget() const { return m_memory_card_settings; }
static void openGamePropertiesDialog(const GameList::Entry* game, u32 crc);
__fi bool isPerGameSettings() const { return static_cast<bool>(m_sif); }
__fi SettingsInterface* getSettingsInterface() const { return m_sif.get(); }
__fi InterfaceSettingsWidget* getInterfaceSettingsWidget() const { return m_interface_settings; }
__fi GameListSettingsWidget* getGameListSettingsWidget() const { return m_game_list_settings; }
__fi BIOSSettingsWidget* getBIOSSettingsWidget() const { return m_bios_settings; }
__fi EmulationSettingsWidget* getEmulationSettingsWidget() const { return m_emulation_settings; }
__fi SystemSettingsWidget* getSystemSettingsWidget() const { return m_system_settings; }
__fi AdvancedSystemSettingsWidget* getAdvancedSystemSettingsWidget() const { return m_advanced_system_settings; }
__fi GameFixSettingsWidget* getGameFixSettingsWidget() const { return m_game_fix_settings_widget; }
__fi GraphicsSettingsWidget* getGraphicsSettingsWidget() const { return m_graphics_settings; }
__fi AudioSettingsWidget* getAudioSettingsWidget() const { return m_audio_settings; }
__fi MemoryCardSettingsWidget* getMemoryCardSettingsWidget() const { return m_memory_card_settings; }
void registerWidgetHelp(QObject* object, QString title, QString recommended_value, QString text);
bool eventFilter(QObject* object, QEvent* event) override;
void setCategory(const char* category);
// Helper functions for reading effective setting values (from game -> global settings).
bool getEffectiveBoolValue(const char* section, const char* key, bool default_value) const;
int getEffectiveIntValue(const char* section, const char* key, int default_value) const;
float getEffectiveFloatValue(const char* section, const char* key, float default_value) const;
std::string getEffectiveStringValue(const char* section, const char* key, const char* default_value) const;
// Helper functions for reading setting values for this layer (game settings or global).
std::optional<bool> getBoolValue(const char* section, const char* key, std::optional<bool> default_value) const;
std::optional<int> getIntValue(const char* section, const char* key, std::optional<int> default_value) const;
std::optional<float> getFloatValue(const char* section, const char* key, std::optional<float> default_value) const;
std::optional<std::string> getStringValue(const char* section, const char* key, std::optional<const char*> default_value) const;
void setBoolSettingValue(const char* section, const char* key, std::optional<bool> value);
void setIntSettingValue(const char* section, const char* key, std::optional<int> value);
void setFloatSettingValue(const char* section, const char* key, std::optional<float> value);
void setStringSettingValue(const char* section, const char* key, std::optional<const char*> value);
Q_SIGNALS:
void settingsResetToDefaults();
public Q_SLOTS:
void setCategory(Category category);
private Q_SLOTS:
void onCategoryCurrentRowChanged(int row);
void onRestoreDefaultsClicked();
protected:
void closeEvent(QCloseEvent*);
private:
void setCategoryHelpTexts();
enum : u32
{
MAX_SETTINGS_WIDGETS = 10
};
void setupUi(const GameList::Entry* game);
void addWidget(QWidget* widget, QString title, QString icon, QString help_text);
std::unique_ptr<SettingsInterface> m_sif;
Ui::SettingsDialog m_ui;
@ -94,8 +120,10 @@ private:
AudioSettingsWidget* m_audio_settings = nullptr;
MemoryCardSettingsWidget* m_memory_card_settings = nullptr;
std::array<QString, static_cast<int>(Category::Count)> m_category_help_text;
std::array<QString, MAX_SETTINGS_WIDGETS> m_category_help_text;
QObject* m_current_help_widget = nullptr;
QMap<QObject*, QString> m_widget_help_text_map;
u32 m_game_crc;
};

View File

@ -49,86 +49,6 @@
<height>32</height>
</size>
</property>
<item>
<property name="text">
<string>Interface</string>
</property>
<property name="icon">
<iconset theme="settings-3-line"/>
</property>
</item>
<item>
<property name="text">
<string>Game List</string>
</property>
<property name="icon">
<iconset theme="folder-settings-line"/>
</property>
</item>
<item>
<property name="text">
<string>BIOS</string>
</property>
<property name="icon">
<iconset theme="hard-drive-2-line"/>
</property>
</item>
<item>
<property name="text">
<string>Emulation</string>
</property>
<property name="icon">
<iconset theme="dashboard-line"/>
</property>
</item>
<item>
<property name="text">
<string>System</string>
</property>
<property name="icon">
<iconset theme="artboard-2-line"/>
</property>
</item>
<item>
<property name="text">
<string>Advanced System</string>
</property>
<property name="icon">
<iconset theme="artboard-2-line"/>
</property>
</item>
<item>
<property name="text">
<string>Game Fixes</string>
</property>
<property name="icon">
<iconset theme="close-line"/>
</property>
</item>
<item>
<property name="text">
<string>Graphics</string>
</property>
<property name="icon">
<iconset theme="brush-line"/>
</property>
</item>
<item>
<property name="text">
<string>Audio</string>
</property>
<property name="icon">
<iconset theme="volume-up-line"/>
</property>
</item>
<item>
<property name="text">
<string>Memory Cards</string>
</property>
<property name="icon">
<iconset theme="sd-card-line"/>
</property>
</item>
</widget>
</item>
<item row="0" column="1">
@ -139,11 +59,6 @@
<height>0</height>
</size>
</property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page"/>
<widget class="QWidget" name="page_2"/>
</widget>
</item>
<item row="1" column="0" colspan="2">

View File

@ -29,33 +29,49 @@ static constexpr int MAXIMUM_EE_CYCLE_RATE = 3;
static constexpr int DEFAULT_EE_CYCLE_RATE = 0;
static constexpr int DEFAULT_EE_CYCLE_SKIP = 0;
SystemSettingsWidget::SystemSettingsWidget(QWidget* parent, SettingsDialog* dialog)
SystemSettingsWidget::SystemSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent)
, m_dialog(dialog)
{
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.eeRoundingMode, "EmuCore/CPU", "FPU.Roundmode", 3);
m_ui.eeClampMode->setCurrentIndex(getClampingModeIndex(false));
connect(m_ui.eeClampMode, QOverload<int>::of(&QComboBox::currentIndexChanged),
[this](int index) { setClampingMode(false, index); });
m_ui.eeCycleRate->setCurrentIndex(
std::clamp(QtHost::GetBaseIntSettingValue("EmuCore/Speedhacks", "EECycleRate", DEFAULT_EE_CYCLE_RATE),
MINIMUM_EE_CYCLE_RATE, MAXIMUM_EE_CYCLE_RATE) +
(0 - MINIMUM_EE_CYCLE_RATE));
connect(m_ui.eeCycleRate, QOverload<int>::of(&QComboBox::currentIndexChanged), [](int index) {
QtHost::SetBaseIntSettingValue("EmuCore/Speedhacks", "EECycleRate", MINIMUM_EE_CYCLE_RATE + index);
g_emu_thread->applySettings();
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.eeCycleSkipping, "EmuCore/Speedhacks", "EECycleSkip", 0);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.MTVU, "EmuCore/Speedhacks", "vuThread", false);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.instantVU1, "EmuCore/Speedhacks", "vu1Instant", true);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.eeRoundingMode, "EmuCore/CPU", "FPU.Roundmode", 3);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.vuRoundingMode, "EmuCore/CPU", "VU.Roundmode", 3);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastCDVD, "EmuCore/Speedhacks", "fastCDVD", false);
if (m_dialog->isPerGameSettings())
{
m_ui.eeCycleRate->insertItem(
0, tr("Use Global Setting [%1]")
.arg(m_ui.eeCycleRate->itemText(
std::clamp(QtHost::GetBaseIntSettingValue("EmuCore/Speedhacks", "EECycleRate", DEFAULT_EE_CYCLE_RATE) - MINIMUM_EE_CYCLE_RATE,
0, MAXIMUM_EE_CYCLE_RATE - MINIMUM_EE_CYCLE_RATE))));
m_ui.eeClampMode->insertItem(0, tr("Use Global Setting [%1]").arg(m_ui.eeClampMode->itemText(getGlobalClampingModeIndex(false))));
m_ui.vuClampMode->insertItem(0, tr("Use Global Setting [%1]").arg(m_ui.vuClampMode->itemText(getGlobalClampingModeIndex(true))));
}
const std::optional<int> cycle_rate =
m_dialog->getIntValue("EmuCore/Speedhacks", "EECycleRate", sif ? std::nullopt : std::optional<int>(DEFAULT_EE_CYCLE_RATE));
m_ui.eeCycleRate->setCurrentIndex(cycle_rate.has_value() ? (std::clamp(cycle_rate.value(), MINIMUM_EE_CYCLE_RATE, MAXIMUM_EE_CYCLE_RATE) +
(0 - MINIMUM_EE_CYCLE_RATE) + static_cast<int>(m_dialog->isPerGameSettings())) :
0);
connect(m_ui.eeCycleRate, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this](int index) {
std::optional<int> value;
if (!m_dialog->isPerGameSettings() || index > 0)
value = MINIMUM_EE_CYCLE_RATE + index - static_cast<int>(m_dialog->isPerGameSettings());
m_dialog->setIntSettingValue("EmuCore/Speedhacks", "EECycleRate", value);
});
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.eeCycleSkipping, "EmuCore/Speedhacks", "EECycleSkip", 0);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.MTVU, "EmuCore/Speedhacks", "vuThread", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.instantVU1, "EmuCore/Speedhacks", "vu1Instant", true);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.vuRoundingMode, "EmuCore/CPU", "VU.Roundmode", 3);
m_ui.eeClampMode->setCurrentIndex(getClampingModeIndex(false));
m_ui.vuClampMode->setCurrentIndex(getClampingModeIndex(true));
connect(m_ui.vuClampMode, QOverload<int>::of(&QComboBox::currentIndexChanged),
[this](int index) { setClampingMode(true, index); });
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.fastCDVD, "EmuCore/Speedhacks", "fastCDVD", false);
connect(m_ui.eeClampMode, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) { setClampingMode(false, index); });
connect(m_ui.vuClampMode, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) { setClampingMode(true, index); });
updateVU1InstantState();
connect(m_ui.MTVU, &QCheckBox::stateChanged, this, &SystemSettingsWidget::updateVU1InstantState);
@ -65,24 +81,59 @@ SystemSettingsWidget::~SystemSettingsWidget() = default;
void SystemSettingsWidget::updateVU1InstantState()
{
m_ui.instantVU1->setEnabled(!m_ui.MTVU->isChecked());
m_ui.instantVU1->setEnabled(!m_dialog->getEffectiveBoolValue("EmuCore/Speedhacks", "vuThread", false));
}
int SystemSettingsWidget::getClampingModeIndex(bool vu)
int SystemSettingsWidget::getGlobalClampingModeIndex(bool vu) const
{
if (QtHost::GetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuSignOverflow" : "fpuFullMode", false))
return 3;
if (QtHost::GetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuExtraOverflow" : "fpuExtraOverflow", false))
return 2;
if (QtHost::GetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuOverflow" : "fpuOverflow", true))
return 1;
return 0;
}
int SystemSettingsWidget::getClampingModeIndex(bool vu) const
{
// This is so messy... maybe we should just make the mode an int in the settings too...
const bool base = m_dialog->isPerGameSettings() ? 1 : 0;
std::optional<bool> default_false = m_dialog->isPerGameSettings() ? std::nullopt : std::optional<bool>(false);
std::optional<bool> default_true = m_dialog->isPerGameSettings() ? std::nullopt : std::optional<bool>(true);
std::optional<bool> third = m_dialog->getBoolValue("EmuCore/CPU/Recompiler", vu ? "vuSignOverflow" : "fpuFullMode", default_false);
std::optional<bool> second = m_dialog->getBoolValue("EmuCore/CPU/Recompiler", vu ? "vuExtraOverflow" : "fpuExtraOverflow", default_false);
std::optional<bool> first = m_dialog->getBoolValue("EmuCore/CPU/Recompiler", vu ? "vuOverflow" : "fpuOverflow", default_true);
if (third.has_value() && third.value())
return base + 3;
if (second.has_value() && second.value())
return base + 2;
if (first.has_value() && first.value())
return base + 1;
else if (first.has_value())
return base + 0; // none
else
return 0; // no per game override
}
void SystemSettingsWidget::setClampingMode(bool vu, int index)
{
QtHost::SetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuSignOverflow" : "fpuFullMode", (index >= 3));
QtHost::SetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuExtraOverflow" : "fpuExtraOverflow", (index >= 2));
QtHost::SetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuOverflow" : "fpuOverflow", (index >= 1));
g_emu_thread->applySettings();
std::optional<bool> first, second, third;
if (!m_dialog->isPerGameSettings() || index > 0)
{
const bool base = m_dialog->isPerGameSettings() ? 1 : 0;
third = (index >= (base + 3));
second = (index >= (base + 2));
first = (index >= (base + 1));
}
m_dialog->setBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuSignOverflow" : "fpuFullMode", third);
m_dialog->setBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuExtraOverflow" : "fpuExtraOverflow", second);
m_dialog->setBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuOverflow" : "fpuOverflow", first);
}

View File

@ -26,15 +26,18 @@ class SystemSettingsWidget : public QWidget
Q_OBJECT
public:
SystemSettingsWidget(QWidget* parent, SettingsDialog* dialog);
SystemSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~SystemSettingsWidget();
private Q_SLOTS:
void updateVU1InstantState();
private:
static int getClampingModeIndex(bool vu);
static void setClampingMode(bool vu, int index);
int getGlobalClampingModeIndex(bool vu) const;
int getClampingModeIndex(bool vu) const;
void setClampingMode(bool vu, int index);
SettingsDialog* m_dialog;
Ui::SystemSettingsWidget m_ui;
};