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

View File

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

View File

@ -33,6 +33,7 @@
#include "EmuThread.h" #include "EmuThread.h"
#include "QtHost.h" #include "QtHost.h"
#include "SettingsDialog.h"
namespace SettingWidgetBinder namespace SettingWidgetBinder
{ {
@ -41,15 +42,27 @@ namespace SettingWidgetBinder
{ {
static bool getBoolValue(const T* widget); static bool getBoolValue(const T* widget);
static void setBoolValue(T* widget, bool value); 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 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 int getFloatValue(const T* widget);
static void setFloatValue(T* widget, int value); 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 QString getStringValue(const T* widget);
static void setStringValue(T* widget, const QString& value); 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> template <typename F>
static void connectValueChanged(T* widget, F func); static void connectValueChanged(T* widget, F func);
@ -59,19 +72,28 @@ namespace SettingWidgetBinder
struct SettingAccessor<QLineEdit> struct SettingAccessor<QLineEdit>
{ {
static bool getBoolValue(const QLineEdit* widget) { return widget->text().toInt() != 0; } static bool getBoolValue(const QLineEdit* widget) { return widget->text().toInt() != 0; }
static void setBoolValue(QLineEdit* widget, bool value) static void setBoolValue(QLineEdit* widget, bool value) { widget->setText(value ? QStringLiteral("1") : QStringLiteral("0")); }
{ static void makeNullableBool(QLineEdit* widget, bool globalValue) { widget->setEnabled(false); }
widget->setText(value ? QStringLiteral("1") : QStringLiteral("0")); 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 int getIntValue(const QLineEdit* widget) { return widget->text().toInt(); }
static void setIntValue(QLineEdit* widget, int value) { widget->setText(QString::number(value)); } 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 float getFloatValue(const QLineEdit* widget) { return widget->text().toFloat(); }
static void setFloatValue(QLineEdit* widget, float value) { widget->setText(QString::number(value)); } 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 QString getStringValue(const QLineEdit* widget) { return widget->text(); }
static void setStringValue(QLineEdit* widget, const QString& value) { widget->setText(value); } 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> template <typename F>
static void connectValueChanged(QLineEdit* widget, F func) static void connectValueChanged(QLineEdit* widget, F func)
@ -83,14 +105,49 @@ namespace SettingWidgetBinder
template <> template <>
struct SettingAccessor<QComboBox> struct SettingAccessor<QComboBox>
{ {
static bool isNullValue(const QComboBox* widget) { return (widget->currentIndex() == 0); }
static bool getBoolValue(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 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 int getIntValue(const QComboBox* widget) { return widget->currentIndex(); }
static void setIntValue(QComboBox* widget, int value) { widget->setCurrentIndex(value); } 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 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 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) static QString getStringValue(const QComboBox* widget)
{ {
@ -105,7 +162,6 @@ namespace SettingWidgetBinder
return widget->currentText(); return widget->currentText();
} }
static void setStringValue(QComboBox* widget, const QString& value) static void setStringValue(QComboBox* widget, const QString& value)
{ {
const int index = widget->findData(value); const int index = widget->findData(value);
@ -117,6 +173,15 @@ namespace SettingWidgetBinder
widget->setCurrentText(value); 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> template <typename F>
static void connectValueChanged(QComboBox* widget, F func) static void connectValueChanged(QComboBox* widget, F func)
@ -130,18 +195,53 @@ namespace SettingWidgetBinder
{ {
static bool getBoolValue(const QCheckBox* widget) { return widget->isChecked(); } static bool getBoolValue(const QCheckBox* widget) { return widget->isChecked(); }
static void setBoolValue(QCheckBox* widget, bool value) { widget->setChecked(value); } 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 int getIntValue(const QCheckBox* widget) { return widget->isChecked() ? 1 : 0; }
static void setIntValue(QCheckBox* widget, int value) { widget->setChecked(value != 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 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 void setFloatValue(QCheckBox* widget, float value) { widget->setChecked(value != 0.0f); }
static void makeNullableFloat(QCheckBox* widget, float globalValue) { widget->setTristate(true); }
static QString getStringValue(const QCheckBox* widget) 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 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> template <typename F>
static void connectValueChanged(QCheckBox* widget, F func) static void connectValueChanged(QCheckBox* widget, F func)
@ -155,15 +255,27 @@ namespace SettingWidgetBinder
{ {
static bool getBoolValue(const QSlider* widget) { return widget->value() > 0; } static bool getBoolValue(const QSlider* widget) { return widget->value() > 0; }
static void setBoolValue(QSlider* widget, bool value) { widget->setValue(value ? 1 : 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 int getIntValue(const QSlider* widget) { return widget->value(); }
static void setIntValue(QSlider* widget, int value) { widget->setValue(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 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 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 QString getStringValue(const QSlider* widget) { return QString::number(widget->value()); }
static void setStringValue(QSlider* widget, const QString& value) { widget->setValue(value.toInt()); } 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> template <typename F>
static void connectValueChanged(QSlider* widget, F func) static void connectValueChanged(QSlider* widget, F func)
@ -177,15 +289,27 @@ namespace SettingWidgetBinder
{ {
static bool getBoolValue(const QSpinBox* widget) { return widget->value() > 0; } static bool getBoolValue(const QSpinBox* widget) { return widget->value() > 0; }
static void setBoolValue(QSpinBox* widget, bool value) { widget->setValue(value ? 1 : 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 int getIntValue(const QSpinBox* widget) { return widget->value(); }
static void setIntValue(QSpinBox* widget, int value) { widget->setValue(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 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 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 QString getStringValue(const QSpinBox* widget) { return QString::number(widget->value()); }
static void setStringValue(QSpinBox* widget, const QString& value) { widget->setValue(value.toInt()); } 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> template <typename F>
static void connectValueChanged(QSpinBox* widget, F func) 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 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 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 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 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 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 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 QString getStringValue(const QDoubleSpinBox* widget) { return QString::number(widget->value()); }
static void setStringValue(QDoubleSpinBox* widget, const QString& value) { widget->setValue(value.toDouble()); } 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> template <typename F>
static void connectValueChanged(QDoubleSpinBox* widget, F func) static void connectValueChanged(QDoubleSpinBox* widget, F func)
@ -221,18 +360,27 @@ namespace SettingWidgetBinder
{ {
static bool getBoolValue(const QAction* widget) { return widget->isChecked(); } static bool getBoolValue(const QAction* widget) { return widget->isChecked(); }
static void setBoolValue(QAction* widget, bool value) { widget->setChecked(value); } 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 int getIntValue(const QAction* widget) { return widget->isChecked() ? 1 : 0; }
static void setIntValue(QAction* widget, int value) { widget->setChecked(value != 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 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 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) static QString getStringValue(const QAction* widget) { return widget->isChecked() ? QStringLiteral("1") : QStringLiteral("0"); }
{
return widget->isChecked() ? QStringLiteral("1") : QStringLiteral("0");
}
static void setStringValue(QAction* widget, const QString& value) { widget->setChecked(value.toInt() != 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> template <typename F>
static void connectValueChanged(QAction* widget, F func) 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. /// Binds a widget's value to a setting, updating it when the value changes.
template <typename WidgetType> 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>; 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]() { bool sif_value;
const bool new_value = Accessor::getBoolValue(widget); if (sif->GetBoolValue(section.c_str(), key.c_str(), &sif_value))
QtHost::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value); Accessor::setNullableBoolValue(widget, sif_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());
else 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> sif->Save();
static void BindWidgetToEnumSetting(WidgetType* widget, std::string section, std::string key, g_emu_thread->applySettings();
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())));
else 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]() { Accessor::connectValueChanged(widget, [widget, section, key]() {
const DataType value = static_cast<DataType>(static_cast<UnderlyingType>(Accessor::getIntValue(widget))); const bool new_value = Accessor::getBoolValue(widget);
const char* string_value = to_string_function(value); QtHost::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value);
QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), string_value); g_emu_thread->applySettings();
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> template <typename WidgetType, typename DataType>
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,
DataType default_value) std::optional<DataType> (*from_string_function)(const char* str), const char* (*to_string_function)(DataType value), DataType default_value)
{ {
using Accessor = SettingAccessor<WidgetType>; using Accessor = SettingAccessor<WidgetType>;
using UnderlyingType = std::underlying_type_t<DataType>; using UnderlyingType = std::underlying_type_t<DataType>;
const std::string old_setting_string_value = QtHost::GetBaseStringSettingValue( const std::string value(QtHost::GetBaseStringSettingValue(section.c_str(), key.c_str(), to_string_function(default_value)));
section.c_str(), key.c_str(), enum_names[static_cast<UnderlyingType>(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); UnderlyingType enum_index = static_cast<UnderlyingType>(default_value);
for (UnderlyingType i = 0; enum_names[i] != nullptr; i++) 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; enum_index = i;
break; break;
} }
} }
Accessor::setIntValue(widget, static_cast<int>(enum_index));
Accessor::connectValueChanged(widget, [widget, section, key, enum_names]() { if (sif)
const UnderlyingType value = static_cast<UnderlyingType>(Accessor::getIntValue(widget)); {
const char* string_value = enum_names[value]; Accessor::makeNullableInt(widget, static_cast<int>(enum_index));
QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), string_value);
g_emu_thread->applySettings(); 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> 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) const char** enum_values, const char* default_value)
{ {
using Accessor = SettingAccessor<WidgetType>; using Accessor = SettingAccessor<WidgetType>;
const std::string old_setting_string_value = const std::string value = QtHost::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value);
QtHost::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value);
for (int i = 0; enum_names[i] != nullptr; i++) for (int i = 0; enum_names[i] != nullptr; i++)
widget->addItem(QString::fromUtf8(enum_names[i])); widget->addItem(QString::fromUtf8(enum_names[i]));
@ -398,20 +727,53 @@ namespace SettingWidgetBinder
int enum_index = -1; int enum_index = -1;
for (int i = 0; enum_values[i] != nullptr; i++) 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; enum_index = i;
break; break;
} }
} }
if (enum_index >= 0)
Accessor::setIntValue(widget, enum_index);
Accessor::connectValueChanged(widget, [widget, section, key, enum_values]() { if (sif)
const int value = Accessor::getIntValue(widget); {
QtHost::SetBaseStringSettingValue(section.c_str(), key.c_str(), enum_values[value]); Accessor::makeNullableInt(widget, enum_index);
g_emu_thread->applySettings();
}); 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 } // namespace SettingWidgetBinder

View File

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

View File

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

View File

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

View File

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

View File

@ -38,7 +38,7 @@ ControllerBindingWidget::ControllerBindingWidget(QWidget* parent, ControllerSett
populateControllerTypes(); populateControllerTypes();
onTypeChanged(); 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, connect(m_ui.controllerType, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&ControllerBindingWidget::onTypeChanged); &ControllerBindingWidget::onTypeChanged);
} }

View File

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

View File

@ -15,6 +15,7 @@
#include "PrecompiledHeader.h" #include "PrecompiledHeader.h"
#include <QtWidgets/QInputDialog>
#include <QtWidgets/QMessageBox> #include <QtWidgets/QMessageBox>
#include <limits> #include <limits>
@ -25,92 +26,45 @@
static constexpr u32 DEFAULT_FRAME_LATENCY = 2; static constexpr u32 DEFAULT_FRAME_LATENCY = 2;
static void FillComboBoxWithEmulationSpeeds(QComboBox* cb) EmulationSettingsWidget::EmulationSettingsWidget(SettingsDialog* dialog, QWidget* parent)
{
// 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)
: QWidget(parent) : QWidget(parent)
, m_dialog(dialog)
{ {
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this); m_ui.setupUi(this);
FillComboBoxWithEmulationSpeeds(m_ui.normalSpeed); initializeSpeedCombo(m_ui.normalSpeed, "Framerate", "NominalScalar", 1.0f);
if (const int index = initializeSpeedCombo(m_ui.fastForwardSpeed, "Framerate", "TurboScalar", 2.0f);
m_ui.normalSpeed->findData(QVariant(QtHost::GetBaseFloatSettingValue("Framerate", "NominalScalar", 1.0f))); initializeSpeedCombo(m_ui.slowMotionSpeed, "Framerate", "SlomoScalar", 0.5f);
index >= 0)
{
m_ui.normalSpeed->setCurrentIndex(index);
}
connect(m_ui.normalSpeed, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&EmulationSettingsWidget::onNormalSpeedIndexChanged);
FillComboBoxWithEmulationSpeeds(m_ui.fastForwardSpeed);
if (const int index = SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.speedLimiter, "EmuCore/GS", "FrameLimitEnable", true);
m_ui.fastForwardSpeed->findData(QVariant(QtHost::GetBaseFloatSettingValue("Framerate", "TurboScalar", 2.0f))); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.maxFrameLatency, "EmuCore/GS", "VsyncQueueSize", DEFAULT_FRAME_LATENCY);
index >= 0) SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.syncToHostRefreshRate, "EmuCore/GS", "SyncToHostRefreshRate", false);
{ connect(m_ui.optimalFramePacing, &QCheckBox::stateChanged, this, &EmulationSettingsWidget::onOptimalFramePacingChanged);
m_ui.fastForwardSpeed->setCurrentIndex(index); m_ui.optimalFramePacing->setTristate(dialog->isPerGameSettings());
}
connect(m_ui.fastForwardSpeed, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&EmulationSettingsWidget::onFastForwardSpeedIndexChanged);
FillComboBoxWithEmulationSpeeds(m_ui.slowMotionSpeed); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.cheats, "EmuCore", "EnableCheats", false);
if (const int index = SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.widescreenPatches, "EmuCore", "EnableWideScreenPatches", false);
m_ui.slowMotionSpeed->findData(QVariant(QtHost::GetBaseFloatSettingValue("Framerate", "SlomoScalar", 0.5f))); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.perGameSettings, "EmuCore", "EnablePerGameSettings", true);
index >= 0) SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hostFilesystem, "EmuCore", "HostFs", false);
{
m_ui.slowMotionSpeed->setCurrentIndex(index);
}
connect(m_ui.slowMotionSpeed, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
&EmulationSettingsWidget::onSlowMotionSpeedIndexChanged);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.speedLimiter, "EmuCore/GS", "FrameLimitEnable", true); dialog->registerWidgetHelp(m_ui.normalSpeed, tr("Normal Speed"), "100%",
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%",
tr("Sets the target emulation speed. It is not guaranteed that this speed will be reached, " 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.")); "and if not, the emulator will run as fast as it can manage."));
dialog->registerWidgetHelp( dialog->registerWidgetHelp(m_ui.fastForwardSpeed, tr("Fast Forward Speed"), tr("User Preference"),
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.")); tr("Sets the fast forward speed. This speed will be used when the fast forward hotkey is pressed/toggled."));
dialog->registerWidgetHelp( dialog->registerWidgetHelp(m_ui.slowMotionSpeed, tr("Slow Motion Speed"), tr("User Preference"),
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.")); tr("Sets the slow motion speed. This speed will be used when the slow motion hotkey is pressed/toggled."));
dialog->registerWidgetHelp( dialog->registerWidgetHelp(m_ui.syncToHostRefreshRate, tr("Sync To Host Refresh Rate"), tr("Unchecked"),
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 " 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 " "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 " "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 " "the console's refresh rate is too far from the host's refresh rate. Users with variable refresh rate displays "
"should disable this option.")); "should disable this option."));
dialog->registerWidgetHelp(m_ui.cheats, tr("Enable Cheats"), tr("Unchecked"), dialog->registerWidgetHelp(m_ui.cheats, tr("Enable Cheats"), tr("Unchecked"), tr("Automatically loads and applies cheats on game start."));
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.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 " 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.")); "leave this option enabled except when testing enhancements with incompatible games."));
@ -119,49 +73,122 @@ EmulationSettingsWidget::EmulationSettingsWidget(QWidget* parent, SettingsDialog
EmulationSettingsWidget::~EmulationSettingsWidget() = default; EmulationSettingsWidget::~EmulationSettingsWidget() = default;
void EmulationSettingsWidget::onNormalSpeedIndexChanged(int index) void EmulationSettingsWidget::initializeSpeedCombo(QComboBox* cb, const char* section, const char* key, float default_value)
{ {
bool okay; float value = QtHost::GetBaseFloatSettingValue(section, key, default_value);
const float value = m_ui.normalSpeed->currentData().toFloat(&okay); if (m_dialog->isPerGameSettings())
QtHost::SetBaseFloatSettingValue("Framerate", "NominalScalar", okay ? value : 1.0f); {
g_emu_thread->applySettings(); 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 int custom_index = cb->count() - 1;
const float value = m_ui.fastForwardSpeed->currentData().toFloat(&okay); const int current_index = cb->currentIndex();
QtHost::SetBaseFloatSettingValue("Framerate", "TurboScalar", okay ? value : 1.0f);
g_emu_thread->applySettings(); 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) void EmulationSettingsWidget::onOptimalFramePacingChanged()
{
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)
{ {
const QSignalBlocker sb(m_ui.maxFrameLatency); 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); std::optional<int> value;
g_emu_thread->applySettings(); 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() 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); const QSignalBlocker sb(m_ui.optimalFramePacing);
m_ui.optimalFramePacing->setChecked(optimal);
const QSignalBlocker sb2(m_ui.maxFrameLatency); 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); m_ui.maxFrameLatency->setValue(optimal ? DEFAULT_FRAME_LATENCY : value);
} }

View File

@ -26,17 +26,18 @@ class EmulationSettingsWidget : public QWidget
Q_OBJECT Q_OBJECT
public: public:
EmulationSettingsWidget(QWidget* parent, SettingsDialog* dialog); EmulationSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~EmulationSettingsWidget(); ~EmulationSettingsWidget();
private Q_SLOTS: private Q_SLOTS:
void onNormalSpeedIndexChanged(int index); void onOptimalFramePacingChanged();
void onFastForwardSpeedIndexChanged(int index);
void onSlowMotionSpeedIndexChanged(int index);
void onOptimalFramePacingChanged(bool checked);
private: 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(); void updateOptimalFramePacing();
SettingsDialog* m_dialog;
Ui::EmulationSettingsWidget m_ui; Ui::EmulationSettingsWidget m_ui;
}; };

View File

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

View File

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

View File

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

View File

@ -28,7 +28,7 @@ class GameListSettingsWidget : public QWidget
Q_OBJECT Q_OBJECT
public: public:
GameListSettingsWidget(QWidget* parent, SettingsDialog* dialog); GameListSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~GameListSettingsWidget(); ~GameListSettingsWidget();
bool addExcludedPath(const std::string& path); 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)"), static const char* s_anisotropic_filtering_entries[] = {QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "Off (Default)"),
QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "2x"), QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "2x"), QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "4x"),
QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "4x"), QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "8x"), QT_TRANSLATE_NOOP("GraphicsSettingsWidget", "16x"), nullptr};
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 const char* s_anisotropic_filtering_values[] = {"1", "2", "4", "8", "16", nullptr};
static constexpr int DEFAULT_INTERLACE_MODE = 7; static constexpr int DEFAULT_INTERLACE_MODE = 7;
GraphicsSettingsWidget::GraphicsSettingsWidget(QWidget* parent, SettingsDialog* dialog) GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent) : QWidget(parent)
, m_dialog(dialog)
{ {
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this); m_ui.setupUi(this);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Global Settings // Global Settings
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToStringSetting(m_ui.adapter, "EmuCore/GS", "Adapter"); SettingWidgetBinder::BindWidgetToStringSetting(sif, m_ui.adapter, "EmuCore/GS", "Adapter");
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.vsync, "EmuCore/GS", "VsyncEnable", 0); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.vsync, "EmuCore/GS", "VsyncEnable", 0);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.enableHWFixes, "EmuCore/GS", "UserHacks", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableHWFixes, "EmuCore/GS", "UserHacks", false);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Game Display Settings // Game Display Settings
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToEnumSetting(m_ui.aspectRatio, "EmuCore/GS", "AspectRatio", SettingWidgetBinder::BindWidgetToEnumSetting(
Pcsx2Config::GSOptions::AspectRatioNames, AspectRatioType::R4_3); sif, m_ui.aspectRatio, "EmuCore/GS", "AspectRatio", Pcsx2Config::GSOptions::AspectRatioNames, AspectRatioType::R4_3);
SettingWidgetBinder::BindWidgetToEnumSetting(m_ui.fmvAspectRatio, "EmuCore/GS", "FMVAspectRatioSwitch", SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.fmvAspectRatio, "EmuCore/GS", "FMVAspectRatioSwitch",
Pcsx2Config::GSOptions::FMVAspectRatioSwitchNames, Pcsx2Config::GSOptions::FMVAspectRatioSwitchNames, FMVAspectRatioSwitchType::Off);
FMVAspectRatioSwitchType::Off); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.interlacing, "EmuCore/GS", "interlace", DEFAULT_INTERLACE_MODE);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.interlacing, "EmuCore/GS", "interlace", DEFAULT_INTERLACE_MODE); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.bilinearFiltering, "EmuCore/GS", "LinearPresent", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.bilinearFiltering, "EmuCore/GS", "LinearPresent", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.integerScaling, "EmuCore/GS", "IntegerScaling", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.integerScaling, "EmuCore/GS", "IntegerScaling", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.internalResolutionScreenshots, "EmuCore/GS", "InternalResolutionScreenshots", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.internalResolutionScreenshots, "EmuCore/GS", SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.zoom, "EmuCore/GS", "Zoom", 100.0f);
"InternalResolutionScreenshots", false); SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.stretchY, "EmuCore/GS", "StretchY", 100.0f);
SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.zoom, "EmuCore/GS", "Zoom", 100.0f); SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.offsetX, "EmuCore/GS", "OffsetX", 0.0f);
SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.stretchY, "EmuCore/GS", "StretchY", 100.0f); SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.offsetY, "EmuCore/GS", "OffsetY", 0.0f);
SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.offsetX, "EmuCore/GS", "OffsetX", 0.0f);
SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.offsetY, "EmuCore/GS", "OffsetY", 0.0f);
connect(m_ui.integerScaling, &QCheckBox::stateChanged, this, &GraphicsSettingsWidget::onIntegerScalingChanged); connect(m_ui.integerScaling, &QCheckBox::stateChanged, this, &GraphicsSettingsWidget::onIntegerScalingChanged);
onIntegerScalingChanged(); onIntegerScalingChanged();
connect(m_ui.fullscreenModes, QOverload<int>::of(&QComboBox::currentIndexChanged), this, connect(m_ui.fullscreenModes, QOverload<int>::of(&QComboBox::currentIndexChanged), this, &GraphicsSettingsWidget::onFullscreenModeChanged);
&GraphicsSettingsWidget::onFullscreenModeChanged);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// OSD Settings // OSD Settings
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToFloatSetting(m_ui.osdScale, "EmuCore/GS", "OsdScale", 100.0f); SettingWidgetBinder::BindWidgetToFloatSetting(sif, m_ui.osdScale, "EmuCore/GS", "OsdScale", 100.0f);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowMessages, "EmuCore/GS", "OsdShowMessages", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.osdShowMessages, "EmuCore/GS", "OsdShowMessages", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowSpeed, "EmuCore/GS", "OsdShowSpeed", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.osdShowSpeed, "EmuCore/GS", "OsdShowSpeed", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowFPS, "EmuCore/GS", "OsdShowFPS", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.osdShowFPS, "EmuCore/GS", "OsdShowFPS", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowCPU, "EmuCore/GS", "OsdShowCPU", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.osdShowCPU, "EmuCore/GS", "OsdShowCPU", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowResolution, "EmuCore/GS", "OsdShowResolution", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.osdShowResolution, "EmuCore/GS", "OsdShowResolution", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.osdShowGSStats, "EmuCore/GS", "OsdShowGSStats", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.osdShowGSStats, "EmuCore/GS", "OsdShowGSStats", false);
dialog->registerWidgetHelp(m_ui.osdShowMessages, tr("Show OSD Messages"), tr("Checked"), 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 " tr("Shows on-screen-display messages when events occur such as save states being "
"created/loaded, screenshots being taken, etc.")); "created/loaded, screenshots being taken, etc."));
dialog->registerWidgetHelp(m_ui.osdShowFPS, tr("Show Game Frame Rate"), tr("Unchecked"), 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.")); tr("Shows the internal frame rate of the game in the top-right corner of the display."));
dialog->registerWidgetHelp( dialog->registerWidgetHelp(m_ui.osdShowSpeed, tr("Show Emulation Speed"), tr("Unchecked"),
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.")); 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"), 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.")); 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 // HW Settings
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.upscaleMultiplier, "EmuCore/GS", "upscale_multiplier", 1); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.upscaleMultiplier, "EmuCore/GS", "upscale_multiplier", 1);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.textureFiltering, "EmuCore/GS", "filter", SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.textureFiltering, "EmuCore/GS", "filter", static_cast<int>(BiFiltering::PS2));
static_cast<int>(BiFiltering::PS2)); SettingWidgetBinder::BindWidgetToIntSetting(
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.trilinearFiltering, "EmuCore/GS", "UserHacks_TriFilter", sif, m_ui.trilinearFiltering, "EmuCore/GS", "UserHacks_TriFilter", static_cast<int>(TriFiltering::Off));
static_cast<int>(TriFiltering::Off)); SettingWidgetBinder::BindWidgetToEnumSetting(
SettingWidgetBinder::BindWidgetToEnumSetting(m_ui.anisotropicFiltering, "EmuCore/GS", "MaxAnisotropy", sif, m_ui.anisotropicFiltering, "EmuCore/GS", "MaxAnisotropy", s_anisotropic_filtering_entries, s_anisotropic_filtering_values, "1");
s_anisotropic_filtering_entries, s_anisotropic_filtering_values, "1"); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.dithering, "EmuCore/GS", "dithering_ps2", 2);
SettingWidgetBinder::BindWidgetToIntSetting(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(m_ui.mipmapping, "EmuCore/GS", "mipmap_hw", SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.crcFixLevel, "EmuCore/GS", "crc_hack_level", static_cast<int>(CRCHackLevel::Automatic), -1);
static_cast<int>(HWMipmapLevel::Automatic), -1); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.blending, "EmuCore/GS", "accurate_blending_unit", static_cast<int>(AccBlendLevel::Basic));
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.crcFixLevel, "EmuCore/GS", "crc_hack_level", SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.accurateDATE, "EmuCore/GS", "accurate_date", true);
static_cast<int>(CRCHackLevel::Automatic), -1); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.conservativeBufferAllocation, "EmuCore/GS", "conservative_framebuffer", true);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.blending, "EmuCore/GS", "accurate_blending_unit", SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.gpuPaletteConversion, "EmuCore/GS", "paltex", false);
static_cast<int>(AccBlendLevel::Basic)); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.preloadTexture, "EmuCore/GS", "preload_texture", false);
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);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// HW Renderer Fixes // HW Renderer Fixes
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.halfScreenFix, "EmuCore/GS", "UserHacks_Half_Bottom_Override", -1, SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.halfScreenFix, "EmuCore/GS", "UserHacks_Half_Bottom_Override", -1, -1);
-1); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawRangeStart, "EmuCore/GS", "UserHacks_SkipDraw", 0);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.skipDrawRangeStart, "EmuCore/GS", "UserHacks_SkipDraw", 0); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.skipDrawRangeCount, "EmuCore/GS", "UserHacks_SkipDraw_Offset", 0);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.skipDrawRangeCount, "EmuCore/GS", "UserHacks_SkipDraw_Offset", 0); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hwAutoFlush, "EmuCore/GS", "UserHacks_AutoFlush", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.hwAutoFlush, "EmuCore/GS", "UserHacks_AutoFlush", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.frameBufferConversion, "EmuCore/GS", "UserHacks_CPU_FB_Conversion", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.frameBufferConversion, "EmuCore/GS", "UserHacks_CPU_FB_Conversion", SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableDepthEmulation, "EmuCore/GS", "UserHacks_DisableDepthSupport", false);
false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.memoryWrapping, "EmuCore/GS", "wrap_gs_mem", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.disableDepthEmulation, "EmuCore/GS", SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.disableSafeFeatures, "EmuCore/GS", "UserHacks_Disable_Safe_Features", false);
"UserHacks_DisableDepthSupport", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.preloadFrameData, "EmuCore/GS", "preload_frame_with_gs_data", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.memoryWrapping, "EmuCore/GS", "wrap_gs_mem", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastTextureInvalidation, "EmuCore/GS", "UserHacks_DisablePartialInvalidation", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.disableSafeFeatures, "EmuCore/GS", SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.textureInsideRt, "EmuCore/GS", "UserHacks_TextureInsideRt", false);
"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);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// HW Upscaling Fixes // HW Upscaling Fixes
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.halfPixelOffset, "EmuCore/GS", "UserHacks_HalfPixelOffset", 0); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.halfPixelOffset, "EmuCore/GS", "UserHacks_HalfPixelOffset", 0);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.roundSprite, "EmuCore/GS", "UserHacks_RoundSprite", 0); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.roundSprite, "EmuCore/GS", "UserHacks_RoundSprite", 0);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.textureOffsetX, "EmuCore/GS", "UserHacks_TCOffsetX", 0); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.textureOffsetX, "EmuCore/GS", "UserHacks_TCOffsetX", 0);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.textureOffsetY, "EmuCore/GS", "UserHacks_TCOffsetY", 0); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.textureOffsetY, "EmuCore/GS", "UserHacks_TCOffsetY", 0);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.alignSprite, "EmuCore/GS", "UserHacks_align_sprite_X", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.alignSprite, "EmuCore/GS", "UserHacks_align_sprite_X", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.mergeSprite, "EmuCore/GS", "UserHacks_merge_pp_sprite", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.mergeSprite, "EmuCore/GS", "UserHacks_merge_pp_sprite", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.wildHack, "EmuCore/GS", "UserHacks_WildHack", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.wildHack, "EmuCore/GS", "UserHacks_WildHack", false);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Advanced Settings // Advanced Settings
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.useBlitSwapChain, "EmuCore/GS", "UseBlitSwapChain", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useBlitSwapChain, "EmuCore/GS", "UseBlitSwapChain", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.useDebugDevice, "EmuCore/GS", "UseDebugDevice", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.useDebugDevice, "EmuCore/GS", "UseDebugDevice", false);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// SW Settings // SW Settings
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.extraSWThreads, "EmuCore/GS", "extrathreads", 2); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.extraSWThreads, "EmuCore/GS", "extrathreads", 2);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.swAutoFlush, "EmuCore/GS", "autoflush_sw", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.swAutoFlush, "EmuCore/GS", "autoflush_sw", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.swAA1, "EmuCore/GS", "aa1", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.swAA1, "EmuCore/GS", "aa1", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.swMipmap, "EmuCore/GS", "mipmap", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.swMipmap, "EmuCore/GS", "mipmap", true);
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Non-trivial settings // Non-trivial settings
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
const GSRendererType current_renderer = static_cast<GSRendererType>( const int renderer = m_dialog->getEffectiveIntValue("EmuCore/GS", "Renderer", static_cast<int>(GSRendererType::Auto));
QtHost::GetBaseIntSettingValue("EmuCore/GS", "Renderer", static_cast<int>(GSRendererType::Auto)));
for (const RendererInfo& ri : s_renderer_info) for (const RendererInfo& ri : s_renderer_info)
{ {
m_ui.renderer->addItem(qApp->translate("GraphicsSettingsWidget", ri.name)); 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); 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); connect(m_ui.enableHWFixes, &QCheckBox::stateChanged, this, &GraphicsSettingsWidget::onEnableHardwareFixesChanged);
updateRendererDependentOptions(); updateRendererDependentOptions();
@ -217,33 +218,69 @@ GraphicsSettingsWidget::~GraphicsSettingsWidget() = default;
void GraphicsSettingsWidget::onRendererChanged(int index) 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(); g_emu_thread->applySettings();
updateRendererDependentOptions(); updateRendererDependentOptions();
} }
void GraphicsSettingsWidget::onAdapterChanged(int index) void GraphicsSettingsWidget::onAdapterChanged(int index)
{ {
if (index == 0) const int first_adapter = m_dialog->isPerGameSettings() ? 2 : 1;
QtHost::RemoveBaseSettingValue("EmuCore/GS", "Adapter");
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 else
QtHost::SetBaseStringSettingValue("EmuCore/GS", "Adapter", m_ui.adapter->currentText().toUtf8().constData()); m_dialog->setStringSettingValue("EmuCore/GS", "Adapter", std::nullopt);
g_emu_thread->applySettings(); 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() 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(2, enabled);
m_ui.hardwareRendererGroup->setTabEnabled(3, 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() void GraphicsSettingsWidget::updateRendererDependentOptions()
{ {
const int index = m_ui.renderer->currentIndex(); const GSRendererType type = getEffectiveRenderer();
GSRendererType type = s_renderer_info[index].type;
if (type == GSRendererType::Auto)
type = GSUtil::GetPreferredRenderer();
#ifdef _WIN32 #ifdef _WIN32
const bool is_dx11 = (type == GSRendererType::DX11 || type == GSRendererType::SW); 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_hardware = (type == GSRendererType::DX11 || type == GSRendererType::OGL || type == GSRendererType::VK);
const bool is_software = (type == GSRendererType::SW); const bool is_software = (type == GSRendererType::SW);
const int current_tab = m_hardware_renderer_visible ? const int current_tab = m_hardware_renderer_visible ? m_ui.hardwareRendererGroup->currentIndex() : m_ui.softwareRendererGroup->currentIndex();
m_ui.hardwareRendererGroup->currentIndex() :
m_ui.softwareRendererGroup->currentIndex();
// move advanced tab to the correct parent // move advanced tab to the correct parent
static constexpr std::array<const char*, 3> move_tab_names = {{"Display", "On-Screen Display", "Advanced"}}; 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 // fill+select adapters
{ {
const std::string current_adapter = QtHost::GetBaseStringSettingValue("EmuCore/GS", "Adapter", "");
QSignalBlocker sb(m_ui.adapter); QSignalBlocker sb(m_ui.adapter);
std::string current_adapter = QtHost::GetBaseStringSettingValue("EmuCore/GS", "Adapter", "");
m_ui.adapter->clear(); m_ui.adapter->clear();
m_ui.adapter->setEnabled(!modes.adapter_names.empty()); m_ui.adapter->setEnabled(!modes.adapter_names.empty());
m_ui.adapter->addItem(tr("(Default)")); 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) for (const std::string& adapter : modes.adapter_names)
{ {
m_ui.adapter->addItem(QString::fromStdString(adapter)); m_ui.adapter->addItem(QString::fromStdString(adapter));
@ -351,11 +401,21 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
{ {
QSignalBlocker sb(m_ui.fullscreenModes); 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->clear();
m_ui.fullscreenModes->addItem(tr("Borderless Fullscreen")); 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) for (const std::string& fs_mode : modes.fullscreen_modes)
{ {
@ -368,23 +428,3 @@ void GraphicsSettingsWidget::updateRendererDependentOptions()
m_ui.enableHWFixes->setEnabled(is_hardware); m_ui.enableHWFixes->setEnabled(is_hardware);
onEnableHardwareFixesChanged(); 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" #include "ui_GraphicsSettingsWidget.h"
enum class GSRendererType : s8;
class SettingsDialog; class SettingsDialog;
class GraphicsSettingsWidget : public QWidget class GraphicsSettingsWidget : public QWidget
@ -26,7 +28,7 @@ class GraphicsSettingsWidget : public QWidget
Q_OBJECT Q_OBJECT
public: public:
GraphicsSettingsWidget(QWidget* parent, SettingsDialog* dialog); GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~GraphicsSettingsWidget(); ~GraphicsSettingsWidget();
void updateRendererDependentOptions(); void updateRendererDependentOptions();
@ -42,6 +44,10 @@ private Q_SLOTS:
void onFullscreenModeChanged(int index); void onFullscreenModeChanged(int index);
private: private:
GSRendererType getEffectiveRenderer() const;
SettingsDialog* m_dialog;
Ui::GraphicsSettingsWidget m_ui; Ui::GraphicsSettingsWidget m_ui;
bool m_hardware_renderer_visible = true; 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}; static const char* THEME_VALUES[] = {"", "fusion", "darkfusion", "darkfusionblue", nullptr};
InterfaceSettingsWidget::InterfaceSettingsWidget(QWidget* parent, SettingsDialog* dialog) InterfaceSettingsWidget::InterfaceSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent) : QWidget(parent)
{ {
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this); m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.inhibitScreensaver, "UI", "InhibitScreensaver", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.inhibitScreensaver, "UI", "InhibitScreensaver", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.discordPresence, "UI", "DiscordPresence", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.discordPresence, "UI", "DiscordPresence", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.confirmPowerOff, "UI", "ConfirmPowerOff", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.confirmPowerOff, "UI", "ConfirmPowerOff", true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.saveStateOnExit, "EmuCore", "AutoStateLoadSave", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.saveStateOnExit, "EmuCore", "AutoStateLoadSave", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.pauseOnStart, "UI", "StartPaused", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnStart, "UI", "StartPaused", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.pauseOnFocusLoss, "UI", "PauseOnFocusLoss", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.pauseOnFocusLoss, "UI", "PauseOnFocusLoss", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.startFullscreen, "UI", "StartFullscreen", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.startFullscreen, "UI", "StartFullscreen", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.doubleClickTogglesFullscreen, "UI", "DoubleClickTogglesFullscreen", SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.doubleClickTogglesFullscreen, "UI", "DoubleClickTogglesFullscreen",
true); true);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.hideMouseCursor, "UI", "HideMouseCursor", false); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.hideMouseCursor, "UI", "HideMouseCursor", false);
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.renderToMainWindow, "UI", "RenderToMainWindow", true); 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); MainWindow::DEFAULT_THEME_NAME);
connect(m_ui.theme, QOverload<int>::of(&QComboBox::currentIndexChanged), [this]() { emit themeChanged(); }); 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.")); tr("Shows the game you are currently playing as part of your profile in Discord."));
if (true) 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"), 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 " tr("Automatically checks for updates to the program on startup. Updates can be deferred "
"until later or skipped entirely.")); "until later or skipped entirely."));

View File

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

View File

@ -15,7 +15,14 @@
#include "PrecompiledHeader.h" #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 "QtHost.h"
#include "QtUtils.h"
#include "SettingsDialog.h" #include "SettingsDialog.h"
#include "AdvancedSystemSettingsWidget.h" #include "AdvancedSystemSettingsWidget.h"
@ -31,33 +38,82 @@
#include <QtWidgets/QMessageBox> #include <QtWidgets/QMessageBox>
#include <QtWidgets/QTextEdit> #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) : QDialog(parent)
, m_game_crc(0)
{ {
m_ui.setupUi(this); setupUi(nullptr);
setCategoryHelpTexts(); }
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); setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
m_interface_settings = new InterfaceSettingsWidget(m_ui.settingsContainer, this); // We don't include interface/game list/bios settings from per-game settings.
m_game_list_settings = new GameListSettingsWidget(m_ui.settingsContainer, this); if (!isPerGameSettings())
m_bios_settings = new BIOSSettingsWidget(m_ui.settingsContainer, this); {
m_emulation_settings = new EmulationSettingsWidget(m_ui.settingsContainer, this); addWidget(m_interface_settings = new InterfaceSettingsWidget(this, m_ui.settingsContainer), tr("Interface"),
m_system_settings = new SystemSettingsWidget(m_ui.settingsContainer, this); QStringLiteral("settings-3-line"),
m_advanced_system_settings = new AdvancedSystemSettingsWidget(m_ui.settingsContainer, this); tr("<strong>Interface Settings</strong><hr>These options control how the software looks and behaves.<br><br>Mouse over an option for "
m_game_fix_settings_widget = new GameFixSettingsWidget(m_ui.settingsContainer, this); "additional information."));
m_graphics_settings = new GraphicsSettingsWidget(m_ui.settingsContainer, this); 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); // remove the preset buttons. but we might want to enable these in the future.
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::GameListSettings), m_game_list_settings); m_ui.restoreDefaultsButton->setVisible(false);
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::BIOSSettings), m_bios_settings); m_ui.settingsPreset->setVisible(false);
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); // Common to both per-game and global settings.
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::GameFixSettings), m_game_fix_settings_widget); addWidget(m_emulation_settings = new EmulationSettingsWidget(this, m_ui.settingsContainer), tr("Emulation"), QStringLiteral("dashboard-line"),
m_ui.settingsContainer->insertWidget(static_cast<int>(Category::GraphicsSettings), m_graphics_settings); 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->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum);
m_ui.settingsCategory->setCurrentRow(0); m_ui.settingsCategory->setCurrentRow(0);
@ -68,37 +124,37 @@ SettingsDialog::SettingsDialog(QWidget* parent /* = nullptr */)
connect(m_ui.restoreDefaultsButton, &QPushButton::clicked, this, &SettingsDialog::onRestoreDefaultsClicked); connect(m_ui.restoreDefaultsButton, &QPushButton::clicked, this, &SettingsDialog::onRestoreDefaultsClicked);
} }
SettingsDialog::~SettingsDialog() = default; SettingsDialog::~SettingsDialog()
void SettingsDialog::setCategoryHelpTexts()
{ {
m_category_help_text[static_cast<int>(Category::InterfaceSettings)] = if (isPerGameSettings())
tr("<strong>Interface Settings</strong><hr>These options control how the software looks and behaves.<br><br>Mouse " s_open_game_properties_dialogs.removeOne(this);
"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.");
} }
void SettingsDialog::setCategory(Category category) void SettingsDialog::closeEvent(QCloseEvent*)
{ {
if (category >= Category::Count) // we need to clean up ourselves, since we're not modal
return; 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) void SettingsDialog::onCategoryCurrentRowChanged(int row)
{ {
Q_ASSERT(row < static_cast<int>(Category::Count));
m_ui.settingsContainer->setCurrentIndex(row); m_ui.settingsContainer->setCurrentIndex(row);
m_ui.helpText->setText(m_category_help_text[row]); m_ui.helpText->setText(m_category_help_text[row]);
} }
@ -106,8 +162,8 @@ void SettingsDialog::onCategoryCurrentRowChanged(int row)
void SettingsDialog::onRestoreDefaultsClicked() void SettingsDialog::onRestoreDefaultsClicked()
{ {
if (QMessageBox::question(this, tr("Confirm Restore Defaults"), if (QMessageBox::question(this, tr("Confirm Restore Defaults"),
tr("Are you sure you want to restore the default settings? Any preferences will be lost."), tr("Are you sure you want to restore the default settings? Any preferences will be lost."), QMessageBox::Yes,
QMessageBox::Yes, QMessageBox::No) != QMessageBox::Yes) QMessageBox::No) != QMessageBox::Yes)
{ {
return; return;
} }
@ -115,6 +171,20 @@ void SettingsDialog::onRestoreDefaultsClicked()
// TODO // 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) void SettingsDialog::registerWidgetHelp(QObject* object, QString title, QString recommended_value, QString text)
{ {
// construct rich text with formatted description // construct rich text with formatted description
@ -153,4 +223,202 @@ bool SettingsDialog::eventFilter(QObject* object, QEvent* event)
} }
return QDialog::eventFilter(object, 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 <QtCore/QString>
#include <QtWidgets/QDialog> #include <QtWidgets/QDialog>
#include <array> #include <array>
#include <memory>
class INISettingsInterface;
namespace GameList
{
struct Entry;
}
class InterfaceSettingsWidget; class InterfaceSettingsWidget;
class GameListSettingsWidget; class GameListSettingsWidget;
@ -36,50 +44,68 @@ class SettingsDialog final : public QDialog
Q_OBJECT Q_OBJECT
public: public:
enum class Category explicit SettingsDialog(QWidget* parent);
{ SettingsDialog(std::unique_ptr<SettingsInterface> sif, const GameList::Entry* game, u32 game_crc);
InterfaceSettings,
GameListSettings,
BIOSSettings,
EmulationSettings,
SystemSettings,
AdvancedSystemSettings,
GameFixSettings,
GraphicsSettings,
AudioSettings,
MemoryCardSettings,
Count
};
explicit SettingsDialog(QWidget* parent = nullptr);
~SettingsDialog(); ~SettingsDialog();
InterfaceSettingsWidget* getInterfaceSettingsWidget() const { return m_interface_settings; } static void openGamePropertiesDialog(const GameList::Entry* game, u32 crc);
GameListSettingsWidget* getGameListSettingsWidget() const { return m_game_list_settings; }
BIOSSettingsWidget* getBIOSSettingsWidget() const { return m_bios_settings; } __fi bool isPerGameSettings() const { return static_cast<bool>(m_sif); }
EmulationSettingsWidget* getEmulationSettingsWidget() const { return m_emulation_settings; } __fi SettingsInterface* getSettingsInterface() const { return m_sif.get(); }
SystemSettingsWidget* getSystemSettingsWidget() const { return m_system_settings; }
AdvancedSystemSettingsWidget* getAdvancedSystemSettingsWidget() const { return m_advanced_system_settings; } __fi InterfaceSettingsWidget* getInterfaceSettingsWidget() const { return m_interface_settings; }
GameFixSettingsWidget* getGameFixSettingsWidget() const { return m_game_fix_settings_widget; } __fi GameListSettingsWidget* getGameListSettingsWidget() const { return m_game_list_settings; }
GraphicsSettingsWidget* getGraphicsSettingsWidget() const { return m_graphics_settings; } __fi BIOSSettingsWidget* getBIOSSettingsWidget() const { return m_bios_settings; }
AudioSettingsWidget* getAudioSettingsWidget() const { return m_audio_settings; } __fi EmulationSettingsWidget* getEmulationSettingsWidget() const { return m_emulation_settings; }
MemoryCardSettingsWidget* getMemoryCardSettingsWidget() const { return m_memory_card_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); void registerWidgetHelp(QObject* object, QString title, QString recommended_value, QString text);
bool eventFilter(QObject* object, QEvent* event) override; 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: Q_SIGNALS:
void settingsResetToDefaults(); void settingsResetToDefaults();
public Q_SLOTS:
void setCategory(Category category);
private Q_SLOTS: private Q_SLOTS:
void onCategoryCurrentRowChanged(int row); void onCategoryCurrentRowChanged(int row);
void onRestoreDefaultsClicked(); void onRestoreDefaultsClicked();
protected:
void closeEvent(QCloseEvent*);
private: 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; Ui::SettingsDialog m_ui;
@ -94,8 +120,10 @@ private:
AudioSettingsWidget* m_audio_settings = nullptr; AudioSettingsWidget* m_audio_settings = nullptr;
MemoryCardSettingsWidget* m_memory_card_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; QObject* m_current_help_widget = nullptr;
QMap<QObject*, QString> m_widget_help_text_map; QMap<QObject*, QString> m_widget_help_text_map;
u32 m_game_crc;
}; };

View File

@ -49,86 +49,6 @@
<height>32</height> <height>32</height>
</size> </size>
</property> </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> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1">
@ -139,11 +59,6 @@
<height>0</height> <height>0</height>
</size> </size>
</property> </property>
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="page"/>
<widget class="QWidget" name="page_2"/>
</widget> </widget>
</item> </item>
<item row="1" column="0" colspan="2"> <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_RATE = 0;
static constexpr int DEFAULT_EE_CYCLE_SKIP = 0; static constexpr int DEFAULT_EE_CYCLE_SKIP = 0;
SystemSettingsWidget::SystemSettingsWidget(QWidget* parent, SettingsDialog* dialog) SystemSettingsWidget::SystemSettingsWidget(SettingsDialog* dialog, QWidget* parent)
: QWidget(parent) : QWidget(parent)
, m_dialog(dialog)
{ {
SettingsInterface* sif = dialog->getSettingsInterface();
m_ui.setupUi(this); m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.eeRoundingMode, "EmuCore/CPU", "FPU.Roundmode", 3); SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.eeCycleSkipping, "EmuCore/Speedhacks", "EECycleSkip", 0);
m_ui.eeClampMode->setCurrentIndex(getClampingModeIndex(false));
connect(m_ui.eeClampMode, QOverload<int>::of(&QComboBox::currentIndexChanged), SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.MTVU, "EmuCore/Speedhacks", "vuThread", false);
[this](int index) { setClampingMode(false, index); }); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.instantVU1, "EmuCore/Speedhacks", "vu1Instant", true);
m_ui.eeCycleRate->setCurrentIndex( SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.eeRoundingMode, "EmuCore/CPU", "FPU.Roundmode", 3);
std::clamp(QtHost::GetBaseIntSettingValue("EmuCore/Speedhacks", "EECycleRate", DEFAULT_EE_CYCLE_RATE), SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.vuRoundingMode, "EmuCore/CPU", "VU.Roundmode", 3);
MINIMUM_EE_CYCLE_RATE, MAXIMUM_EE_CYCLE_RATE) + SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.fastCDVD, "EmuCore/Speedhacks", "fastCDVD", false);
(0 - MINIMUM_EE_CYCLE_RATE));
connect(m_ui.eeCycleRate, QOverload<int>::of(&QComboBox::currentIndexChanged), [](int index) { if (m_dialog->isPerGameSettings())
QtHost::SetBaseIntSettingValue("EmuCore/Speedhacks", "EECycleRate", MINIMUM_EE_CYCLE_RATE + index); {
g_emu_thread->applySettings(); 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); m_ui.eeClampMode->setCurrentIndex(getClampingModeIndex(false));
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.instantVU1, "EmuCore/Speedhacks", "vu1Instant", true);
SettingWidgetBinder::BindWidgetToIntSetting(m_ui.vuRoundingMode, "EmuCore/CPU", "VU.Roundmode", 3);
m_ui.vuClampMode->setCurrentIndex(getClampingModeIndex(true)); m_ui.vuClampMode->setCurrentIndex(getClampingModeIndex(true));
connect(m_ui.vuClampMode, QOverload<int>::of(&QComboBox::currentIndexChanged), connect(m_ui.eeClampMode, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) { setClampingMode(false, index); });
[this](int index) { setClampingMode(true, index); }); connect(m_ui.vuClampMode, QOverload<int>::of(&QComboBox::currentIndexChanged), [this](int index) { setClampingMode(true, index); });
SettingWidgetBinder::BindWidgetToBoolSetting(m_ui.fastCDVD, "EmuCore/Speedhacks", "fastCDVD", false);
updateVU1InstantState(); updateVU1InstantState();
connect(m_ui.MTVU, &QCheckBox::stateChanged, this, &SystemSettingsWidget::updateVU1InstantState); connect(m_ui.MTVU, &QCheckBox::stateChanged, this, &SystemSettingsWidget::updateVU1InstantState);
@ -65,24 +81,59 @@ SystemSettingsWidget::~SystemSettingsWidget() = default;
void SystemSettingsWidget::updateVU1InstantState() 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)) if (QtHost::GetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuSignOverflow" : "fpuFullMode", false))
return 3; return 3;
if (QtHost::GetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuExtraOverflow" : "fpuExtraOverflow", false)) if (QtHost::GetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuExtraOverflow" : "fpuExtraOverflow", false))
return 2; return 2;
if (QtHost::GetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuOverflow" : "fpuOverflow", true)) if (QtHost::GetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuOverflow" : "fpuOverflow", true))
return 1; return 1;
return 0; 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) void SystemSettingsWidget::setClampingMode(bool vu, int index)
{ {
QtHost::SetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuSignOverflow" : "fpuFullMode", (index >= 3)); std::optional<bool> first, second, third;
QtHost::SetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuExtraOverflow" : "fpuExtraOverflow", (index >= 2));
QtHost::SetBaseBoolSettingValue("EmuCore/CPU/Recompiler", vu ? "vuOverflow" : "fpuOverflow", (index >= 1)); if (!m_dialog->isPerGameSettings() || index > 0)
g_emu_thread->applySettings(); {
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 Q_OBJECT
public: public:
SystemSettingsWidget(QWidget* parent, SettingsDialog* dialog); SystemSettingsWidget(SettingsDialog* dialog, QWidget* parent);
~SystemSettingsWidget(); ~SystemSettingsWidget();
private Q_SLOTS: private Q_SLOTS:
void updateVU1InstantState(); void updateVU1InstantState();
private: private:
static int getClampingModeIndex(bool vu); int getGlobalClampingModeIndex(bool vu) const;
static void setClampingMode(bool vu, int index); int getClampingModeIndex(bool vu) const;
void setClampingMode(bool vu, int index);
SettingsDialog* m_dialog;
Ui::SystemSettingsWidget m_ui; Ui::SystemSettingsWidget m_ui;
}; };