Qt: Thread-safe QSettings access and updating

This commit is contained in:
Connor McLaughlin 2020-01-24 14:49:49 +10:00
parent 9562cbea56
commit c5282b99e1
10 changed files with 144 additions and 93 deletions

View File

@ -6,12 +6,14 @@ ConsoleSettingsWidget::ConsoleSettingsWidget(QtHostInterface* host_interface, QW
{ {
m_ui.setupUi(this); m_ui.setupUi(this);
SettingWidgetBinder::BindWidgetToSetting(m_host_interface, m_ui.region, &Settings::region); SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.region, "Console/Region",
SettingWidgetBinder::BindWidgetToSetting(m_host_interface, m_ui.biosPath, &Settings::bios_path); &Settings::ParseConsoleRegionName, &Settings::GetConsoleRegionName);
SettingWidgetBinder::BindWidgetToSetting(m_host_interface, m_ui.enableTTYOutput, &Settings::bios_patch_tty_enable); SettingWidgetBinder::BindWidgetToStringSetting(m_host_interface, m_ui.biosPath, "BIOS/Path");
SettingWidgetBinder::BindWidgetToSetting(m_host_interface, m_ui.fastBoot, &Settings::bios_patch_fast_boot); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.enableTTYOutput, "BIOS/PatchTTYEnable");
SettingWidgetBinder::BindWidgetToSetting(m_host_interface, m_ui.enableSpeedLimiter, &Settings::speed_limiter_enabled); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.fastBoot, "BIOS/PatchFastBoot");
SettingWidgetBinder::BindWidgetToSetting(m_host_interface, m_ui.pauseOnStart, &Settings::start_paused); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.enableSpeedLimiter,
"General/SpeedLimiterEnabled");
SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.pauseOnStart, "General/StartPaused");
} }
ConsoleSettingsWidget::~ConsoleSettingsWidget() = default; ConsoleSettingsWidget::~ConsoleSettingsWidget() = default;

View File

@ -138,13 +138,11 @@ public:
void loadFromSettings() void loadFromSettings()
{ {
const QSettings& qsettings = m_host_interface->getQSettings(); QStringList path_list = m_host_interface->getSettingValue(QStringLiteral("GameList/Paths")).toStringList();
QStringList path_list = qsettings.value(QStringLiteral("GameList/Paths")).toStringList();
for (QString& entry : path_list) for (QString& entry : path_list)
m_entries.push_back({std::move(entry), false}); m_entries.push_back({std::move(entry), false});
path_list = qsettings.value(QStringLiteral("GameList/RecursivePaths")).toStringList(); path_list = m_host_interface->getSettingValue(QStringLiteral("GameList/RecursivePaths")).toStringList();
for (QString& entry : path_list) for (QString& entry : path_list)
m_entries.push_back({std::move(entry), true}); m_entries.push_back({std::move(entry), true});
} }
@ -162,17 +160,15 @@ public:
paths.push_back(entry.path); paths.push_back(entry.path);
} }
QSettings& qsettings = m_host_interface->getQSettings();
if (paths.empty()) if (paths.empty())
qsettings.remove(QStringLiteral("GameList/Paths")); m_host_interface->removeSettingValue(QStringLiteral("GameList/Paths"));
else else
qsettings.setValue(QStringLiteral("GameList/Paths"), paths); m_host_interface->putSettingValue(QStringLiteral("GameList/Paths"), paths);
if (recursive_paths.empty()) if (recursive_paths.empty())
qsettings.remove(QStringLiteral("GameList/RecursivePaths")); m_host_interface->removeSettingValue(QStringLiteral("GameList/RecursivePaths"));
else else
qsettings.setValue(QStringLiteral("GameList/RecursivePaths"), recursive_paths); m_host_interface->putSettingValue(QStringLiteral("GameList/RecursivePaths"), recursive_paths);
} }
private: private:
@ -191,10 +187,8 @@ GameListSettingsWidget::GameListSettingsWidget(QtHostInterface* host_interface,
{ {
m_ui.setupUi(this); m_ui.setupUi(this);
QSettings& qsettings = host_interface->getQSettings();
m_search_directories_model = new GameListSearchDirectoriesModel(host_interface); m_search_directories_model = new GameListSearchDirectoriesModel(host_interface);
m_ui.redumpDatabasePath->setText(qsettings.value("GameList/RedumpDatabasePath").toString()); m_ui.redumpDatabasePath->setText(host_interface->getSettingValue("GameList/RedumpDatabasePath").toString());
m_ui.searchDirectoryList->setModel(m_search_directories_model); m_ui.searchDirectoryList->setModel(m_search_directories_model);
m_ui.searchDirectoryList->setSelectionMode(QAbstractItemView::SingleSelection); m_ui.searchDirectoryList->setSelectionMode(QAbstractItemView::SingleSelection);
m_ui.searchDirectoryList->setSelectionBehavior(QAbstractItemView::SelectRows); m_ui.searchDirectoryList->setSelectionBehavior(QAbstractItemView::SelectRows);
@ -280,7 +274,7 @@ void GameListSettingsWidget::onBrowseRedumpPathButtonPressed()
return; return;
m_ui.redumpDatabasePath->setText(filename); m_ui.redumpDatabasePath->setText(filename);
m_host_interface->getQSettings().setValue("GameList/RedumpDatabasePath", filename); m_host_interface->putSettingValue(QStringLiteral("GameList/RedumpDatabasePath"), filename);
m_host_interface->refreshGameList(true, true); m_host_interface->refreshGameList(true, true);
} }

View File

@ -9,17 +9,16 @@ GPUSettingsWidget::GPUSettingsWidget(QtHostInterface* host_interface, QWidget* p
m_ui.setupUi(this); m_ui.setupUi(this);
setupAdditionalUi(); setupAdditionalUi();
SettingWidgetBinder::BindWidgetToSetting(m_host_interface, m_ui.renderer, &Settings::gpu_renderer); SettingWidgetBinder::BindWidgetToEnumSetting(m_host_interface, m_ui.renderer, "GPU/Renderer",
SettingWidgetBinder::BindWidgetToSetting(m_host_interface, m_ui.fullscreen, &Settings::display_fullscreen); &Settings::ParseRendererName, &Settings::GetRendererName);
SettingWidgetBinder::BindWidgetToSetting(m_host_interface, m_ui.displayLinearFiltering, SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.fullscreen, "Display/Fullscreen");
&Settings::display_linear_filtering); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.displayLinearFiltering,
SettingWidgetBinder::BindWidgetToSetting(m_host_interface, m_ui.vsync, &Settings::video_sync_enabled); "Display/LinearFiltering");
SettingWidgetBinder::BindWidgetToSetting(m_host_interface, m_ui.resolutionScale, &Settings::gpu_resolution_scale); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.vsync, "Display/VSync");
SettingWidgetBinder::BindWidgetToSetting(m_host_interface, m_ui.trueColor, &Settings::gpu_true_color); SettingWidgetBinder::BindWidgetToIntSetting(m_host_interface, m_ui.resolutionScale, "GPU/ResolutionScale");
SettingWidgetBinder::BindWidgetToSetting(m_host_interface, m_ui.linearTextureFiltering, SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.trueColor, "GPU/TrueColor");
&Settings::gpu_texture_filtering); SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.linearTextureFiltering, "GPU/TextureFiltering");
SettingWidgetBinder::BindWidgetToSetting(m_host_interface, m_ui.forceProgressiveScan, SettingWidgetBinder::BindWidgetToBoolSetting(m_host_interface, m_ui.forceProgressiveScan, "GPU/ForceProgressiveScan");
&Settings::gpu_force_progressive_scan);
} }
GPUSettingsWidget::~GPUSettingsWidget() = default; GPUSettingsWidget::~GPUSettingsWidget() = default;

View File

@ -9,7 +9,7 @@ InputButtonBindingWidget::InputButtonBindingWidget(QtHostInterface* host_interfa
QWidget* parent) QWidget* parent)
: QPushButton(parent), m_host_interface(host_interface), m_setting_name(std::move(setting_name)) : QPushButton(parent), m_host_interface(host_interface), m_setting_name(std::move(setting_name))
{ {
m_current_binding_value = m_host_interface->getQSettings().value(m_setting_name).toString(); m_current_binding_value = m_host_interface->getSettingValue(m_setting_name).toString();
setText(m_current_binding_value); setText(m_current_binding_value);
connect(this, &QPushButton::pressed, this, &InputButtonBindingWidget::onPressed); connect(this, &QPushButton::pressed, this, &InputButtonBindingWidget::onPressed);
@ -54,7 +54,7 @@ void InputButtonBindingWidget::setNewBinding()
if (m_new_binding_value.isEmpty()) if (m_new_binding_value.isEmpty())
return; return;
m_host_interface->getQSettings().setValue(m_setting_name, m_new_binding_value); m_host_interface->putSettingValue(m_setting_name, m_new_binding_value);
m_host_interface->updateInputMap(); m_host_interface->updateInputMap();
m_current_binding_value = std::move(m_new_binding_value); m_current_binding_value = std::move(m_new_binding_value);

View File

@ -216,9 +216,8 @@ void MainWindow::setupAdditionalUi()
action->setCheckable(true); action->setCheckable(true);
action->setChecked(m_host_interface->GetCoreSettings().gpu_renderer == renderer); action->setChecked(m_host_interface->GetCoreSettings().gpu_renderer == renderer);
connect(action, &QAction::triggered, [this, action, renderer]() { connect(action, &QAction::triggered, [this, action, renderer]() {
m_host_interface->getQSettings().setValue(QStringLiteral("GPU/Renderer"), m_host_interface->putSettingValue(QStringLiteral("GPU/Renderer"), QString(Settings::GetRendererName(renderer)));
QString(Settings::GetRendererName(renderer))); m_host_interface->applySettings();
m_host_interface->GetCoreSettings().gpu_renderer = renderer;
action->setChecked(true); action->setChecked(true);
switchRenderer(); switchRenderer();
}); });

View File

@ -30,13 +30,12 @@ void PortSettingsWidget::createUi()
void PortSettingsWidget::createPortSettingsUi(int index, PortSettingsUI* ui) void PortSettingsWidget::createPortSettingsUi(int index, PortSettingsUI* ui)
{ {
const Settings& settings = m_host_interface->GetCoreSettings();
ui->widget = new QWidget(m_tab_widget); ui->widget = new QWidget(m_tab_widget);
ui->layout = new QVBoxLayout(ui->widget); ui->layout = new QVBoxLayout(ui->widget);
QHBoxLayout* memory_card_layout = new QHBoxLayout(); QHBoxLayout* memory_card_layout = new QHBoxLayout();
ui->memory_card_path = new QLineEdit(QString::fromStdString(settings.memory_card_paths[index]), ui->widget); ui->memory_card_path = new QLineEdit(
m_host_interface->getSettingValue(QStringLiteral("MemoryCards/Card%1Path").arg(index + 1)).toString(), ui->widget);
memory_card_layout->addWidget(ui->memory_card_path); memory_card_layout->addWidget(ui->memory_card_path);
ui->memory_card_path_browse = new QPushButton(tr("Browse..."), ui->widget); ui->memory_card_path_browse = new QPushButton(tr("Browse..."), ui->widget);
memory_card_layout->addWidget(ui->memory_card_path_browse); memory_card_layout->addWidget(ui->memory_card_path_browse);
@ -49,13 +48,19 @@ void PortSettingsWidget::createPortSettingsUi(int index, PortSettingsUI* ui)
ui->controller_type->addItem( ui->controller_type->addItem(
QString::fromLocal8Bit(Settings::GetControllerTypeDisplayName(static_cast<ControllerType>(i)))); QString::fromLocal8Bit(Settings::GetControllerTypeDisplayName(static_cast<ControllerType>(i))));
} }
ui->controller_type->setCurrentIndex(static_cast<int>(settings.controller_types[index])); ControllerType ctype = Settings::ParseControllerTypeName(
m_host_interface->getSettingValue(QStringLiteral("Controller%1/Type").arg(index + 1))
.toString()
.toStdString()
.c_str())
.value_or(ControllerType::None);
ui->controller_type->setCurrentIndex(static_cast<int>(ctype));
connect(ui->controller_type, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), connect(ui->controller_type, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged),
[this, index]() { onControllerTypeChanged(index); }); [this, index]() { onControllerTypeChanged(index); });
ui->layout->addWidget(new QLabel(tr("Controller Type:"), ui->widget)); ui->layout->addWidget(new QLabel(tr("Controller Type:"), ui->widget));
ui->layout->addWidget(ui->controller_type); ui->layout->addWidget(ui->controller_type);
createPortBindingSettingsUi(index, ui); createPortBindingSettingsUi(index, ui, ctype);
ui->layout->addStretch(1); ui->layout->addStretch(1);
@ -64,12 +69,11 @@ void PortSettingsWidget::createPortSettingsUi(int index, PortSettingsUI* ui)
m_tab_widget->addTab(ui->widget, tr("Port %1").arg(index + 1)); m_tab_widget->addTab(ui->widget, tr("Port %1").arg(index + 1));
} }
void PortSettingsWidget::createPortBindingSettingsUi(int index, PortSettingsUI* ui) void PortSettingsWidget::createPortBindingSettingsUi(int index, PortSettingsUI* ui, ControllerType ctype)
{ {
QWidget* container = new QWidget(ui->widget); QWidget* container = new QWidget(ui->widget);
QGridLayout* layout = new QGridLayout(container); QGridLayout* layout = new QGridLayout(container);
layout->setContentsMargins(0, 0, 0, 0); layout->setContentsMargins(0, 0, 0, 0);
const ControllerType ctype = m_host_interface->GetCoreSettings().controller_types[index];
const auto buttons = Controller::GetButtonNames(ctype); const auto buttons = Controller::GetButtonNames(ctype);
if (!buttons.empty()) if (!buttons.empty())
@ -124,9 +128,9 @@ void PortSettingsWidget::onControllerTypeChanged(int index)
if (type_index < 0 || type_index >= static_cast<int>(ControllerType::Count)) if (type_index < 0 || type_index >= static_cast<int>(ControllerType::Count))
return; return;
m_host_interface->GetCoreSettings().controller_types[index] = static_cast<ControllerType>(type_index); m_host_interface->putSettingValue(
m_host_interface->getQSettings().setValue(
QStringLiteral("Controller%1/Type").arg(index + 1), QStringLiteral("Controller%1/Type").arg(index + 1),
QString::fromStdString(Settings::GetControllerTypeName(static_cast<ControllerType>(type_index)))); QString::fromStdString(Settings::GetControllerTypeName(static_cast<ControllerType>(type_index))));
createPortBindingSettingsUi(index, &m_port_ui[index]); m_host_interface->applySettings();
createPortBindingSettingsUi(index, &m_port_ui[index], static_cast<ControllerType>(type_index));
} }

View File

@ -40,7 +40,7 @@ private:
void createUi(); void createUi();
void createPortSettingsUi(int index, PortSettingsUI* ui); void createPortSettingsUi(int index, PortSettingsUI* ui);
void createPortBindingSettingsUi(int index, PortSettingsUI* ui); void createPortBindingSettingsUi(int index, PortSettingsUI* ui, ControllerType ctype);
void onControllerTypeChanged(int index); void onControllerTypeChanged(int index);
std::array<PortSettingsUI, 2> m_port_ui = {}; std::array<PortSettingsUI, 2> m_port_ui = {};

View File

@ -48,6 +48,8 @@ void QtHostInterface::ReportMessage(const char* message)
void QtHostInterface::setDefaultSettings() void QtHostInterface::setDefaultSettings()
{ {
std::lock_guard<std::mutex> guard(m_qsettings_mutex);
m_settings.SetDefaults(); m_settings.SetDefaults();
// default input settings for Qt // default input settings for Qt
@ -67,20 +69,57 @@ void QtHostInterface::setDefaultSettings()
m_qsettings.setValue(QStringLiteral("Controller1/ButtonR1"), QStringLiteral("Keyboard/E")); m_qsettings.setValue(QStringLiteral("Controller1/ButtonR1"), QStringLiteral("Keyboard/E"));
m_qsettings.setValue(QStringLiteral("Controller1/ButtonR2"), QStringLiteral("Keyboard/3")); m_qsettings.setValue(QStringLiteral("Controller1/ButtonR2"), QStringLiteral("Keyboard/3"));
updateQSettings(); updateQSettingsFromCoreSettings();
} }
void QtHostInterface::updateQSettings() QVariant QtHostInterface::getSettingValue(const QString& name)
{
std::lock_guard<std::mutex> guard(m_qsettings_mutex);
return m_qsettings.value(name);
}
void QtHostInterface::putSettingValue(const QString& name, const QVariant& value)
{
std::lock_guard<std::mutex> guard(m_qsettings_mutex);
m_qsettings.setValue(name, value);
}
void QtHostInterface::removeSettingValue(const QString& name)
{
std::lock_guard<std::mutex> guard(m_qsettings_mutex);
m_qsettings.remove(name);
}
void QtHostInterface::updateQSettingsFromCoreSettings()
{ {
QtSettingsInterface si(m_qsettings); QtSettingsInterface si(m_qsettings);
m_settings.Save(si); m_settings.Save(si);
// m_qsettings.sync();
} }
void QtHostInterface::applySettings() void QtHostInterface::applySettings()
{ {
QtSettingsInterface si(m_qsettings); if (!isOnWorkerThread())
m_settings.Load(si); {
QMetaObject::invokeMethod(this, "applySettings", Qt::QueuedConnection);
return;
}
// TODO: Should we move this to the base class?
const bool old_vsync_enabled = m_settings.video_sync_enabled;
const bool old_audio_sync_enabled = m_settings.audio_sync_enabled;
const bool old_speed_limiter_enabled = m_settings.speed_limiter_enabled;
{
std::lock_guard<std::mutex> guard(m_qsettings_mutex);
QtSettingsInterface si(m_qsettings);
m_settings.Load(si);
}
if (m_settings.video_sync_enabled != old_vsync_enabled || m_settings.audio_sync_enabled != old_audio_sync_enabled ||
m_settings.speed_limiter_enabled != old_speed_limiter_enabled)
{
UpdateSpeedLimiterState();
}
} }
void QtHostInterface::checkSettings() void QtHostInterface::checkSettings()
@ -105,7 +144,9 @@ void QtHostInterface::checkSettings()
setDefaultSettings(); setDefaultSettings();
} }
applySettings(); // initial setting init - we don't do this locked since the thread hasn't been created yet
QtSettingsInterface si(m_qsettings);
m_settings.Load(si);
} }
void QtHostInterface::createGameList() void QtHostInterface::createGameList()
@ -446,6 +487,7 @@ void QtHostInterface::doBootSystem(QString initial_filename, QString initial_sav
wakeThread(); wakeThread();
m_audio_stream->PauseOutput(false); m_audio_stream->PauseOutput(false);
UpdateSpeedLimiterState();
emit emulationStarted(); emit emulationStarted();
} }

View File

@ -9,6 +9,7 @@
#include <functional> #include <functional>
#include <map> #include <map>
#include <memory> #include <memory>
#include <mutex>
#include <utility> #include <utility>
#include <vector> #include <vector>
@ -30,11 +31,12 @@ public:
void ReportError(const char* message) override; void ReportError(const char* message) override;
void ReportMessage(const char* message) override; void ReportMessage(const char* message) override;
const QSettings& getQSettings() const { return m_qsettings; }
QSettings& getQSettings() { return m_qsettings; }
void setDefaultSettings(); void setDefaultSettings();
void updateQSettings();
void applySettings(); /// Thread-safe QSettings access.
QVariant getSettingValue(const QString& name);
void putSettingValue(const QString& name, const QVariant& value);
void removeSettingValue(const QString& name);
const Settings& GetCoreSettings() const { return m_settings; } const Settings& GetCoreSettings() const { return m_settings; }
Settings& GetCoreSettings() { return m_settings; } Settings& GetCoreSettings() { return m_settings; }
@ -74,6 +76,7 @@ Q_SIGNALS:
void performanceCountersUpdated(float speed, float fps, float vps, float avg_frame_time, float worst_frame_time); void performanceCountersUpdated(float speed, float fps, float vps, float avg_frame_time, float worst_frame_time);
public Q_SLOTS: public Q_SLOTS:
void applySettings();
void powerOffSystem(); void powerOffSystem();
void resetSystem(); void resetSystem();
void pauseSystem(bool paused); void pauseSystem(bool paused);
@ -113,6 +116,8 @@ private:
}; };
void checkSettings(); void checkSettings();
void updateQSettingsFromCoreSettings();
void createGameList(); void createGameList();
void updateControllerInputMap(); void updateControllerInputMap();
void updateHotkeyInputMap(); void updateHotkeyInputMap();
@ -124,6 +129,7 @@ private:
void wakeThread(); void wakeThread();
QSettings m_qsettings; QSettings m_qsettings;
std::mutex m_qsettings_mutex;
std::unique_ptr<GameList> m_game_list; std::unique_ptr<GameList> m_game_list;

View File

@ -1,4 +1,5 @@
#pragma once #pragma once
#include <optional>
#include <type_traits> #include <type_traits>
#include "core/settings.h" #include "core/settings.h"
@ -63,7 +64,7 @@ struct SettingAccessor<QComboBox>
template<typename F> template<typename F>
static void connectValueChanged(QComboBox* widget, F func) static void connectValueChanged(QComboBox* widget, F func)
{ {
widget->connect(widget, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), func); widget->connect(widget, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), func);
} }
}; };
@ -90,64 +91,68 @@ struct SettingAccessor<QCheckBox>
}; };
/// 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, typename DataType, typename = void>
// void BindWidgetToSetting(QtHostInterface* hi, WidgetType* widget, DataType Settings::*settings_ptr);
template<typename WidgetType> template<typename WidgetType>
void BindWidgetToSetting(QtHostInterface* hi, WidgetType* widget, bool Settings::*settings_ptr) void BindWidgetToBoolSetting(QtHostInterface* hi, WidgetType* widget, const char* setting_name)
{ {
using Accessor = SettingAccessor<WidgetType>; using Accessor = SettingAccessor<WidgetType>;
Accessor::setBoolValue(widget, hi->GetCoreSettings().*settings_ptr); Accessor::setBoolValue(widget, hi->getSettingValue(setting_name).toBool());
Accessor::connectValueChanged(widget, [hi, widget, settings_ptr]() { Accessor::connectValueChanged(widget, [hi, widget, setting_name]() {
(hi->GetCoreSettings().*settings_ptr) = Accessor::getBoolValue(widget); const bool new_value = Accessor::getBoolValue(widget);
hi->updateQSettings(); hi->putSettingValue(setting_name, new_value);
hi->applySettings();
}); });
} }
template<typename WidgetType> template<typename WidgetType>
void BindWidgetToSetting(QtHostInterface* hi, WidgetType* widget, std::string Settings::*settings_ptr) void BindWidgetToIntSetting(QtHostInterface* hi, WidgetType* widget, const char* setting_name)
{ {
using Accessor = SettingAccessor<WidgetType>; using Accessor = SettingAccessor<WidgetType>;
Accessor::setStringValue(widget, QString::fromStdString(hi->GetCoreSettings().*settings_ptr)); Accessor::setIntValue(widget, hi->getSettingValue(setting_name).toInt());
Accessor::connectValueChanged(widget, [hi, widget, settings_ptr]() { Accessor::connectValueChanged(widget, [hi, widget, setting_name]() {
const QString value = Accessor::getStringValue(widget); const int new_value = Accessor::getIntValue(widget);
(hi->GetCoreSettings().*settings_ptr) = value.toStdString(); hi->putSettingValue(setting_name, new_value);
hi->updateQSettings(); hi->applySettings();
});
}
template<typename WidgetType>
void BindWidgetToStringSetting(QtHostInterface* hi, WidgetType* widget, const char* setting_name)
{
using Accessor = SettingAccessor<WidgetType>;
Accessor::setStringValue(widget, hi->getSettingValue(setting_name).toString());
Accessor::connectValueChanged(widget, [hi, widget, setting_name]() {
const QString new_value = Accessor::getStringValue(widget);
hi->putSettingValue(setting_name, new_value);
hi->applySettings();
}); });
} }
template<typename WidgetType, typename DataType> template<typename WidgetType, typename DataType>
void BindWidgetToSetting(QtHostInterface* hi, WidgetType* widget, DataType Settings::*settings_ptr, void BindWidgetToEnumSetting(QtHostInterface* hi, WidgetType* widget, const char* setting_name,
std::enable_if_t<std::is_enum_v<DataType>, int>* v = nullptr) std::optional<DataType> (*from_string_function)(const char* str),
const char* (*to_string_function)(DataType value))
{ {
using Accessor = SettingAccessor<WidgetType>; using Accessor = SettingAccessor<WidgetType>;
using UnderlyingType = std::underlying_type_t<DataType>; using UnderlyingType = std::underlying_type_t<DataType>;
Accessor::setIntValue(widget, static_cast<int>(static_cast<UnderlyingType>(hi->GetCoreSettings().*settings_ptr))); const QString old_setting_string_value = hi->getSettingValue(setting_name).toString();
const std::optional<DataType> old_setting_value =
from_string_function(old_setting_string_value.toStdString().c_str());
if (old_setting_value.has_value())
Accessor::setIntValue(widget, static_cast<int>(static_cast<UnderlyingType>(old_setting_value.value())));
Accessor::connectValueChanged(widget, [hi, widget, settings_ptr](int) { Accessor::connectValueChanged(widget, [hi, widget, setting_name, to_string_function]() {
const int value = Accessor::getIntValue(widget); const DataType value = static_cast<DataType>(static_cast<UnderlyingType>(Accessor::getIntValue(widget)));
(hi->GetCoreSettings().*settings_ptr) = static_cast<DataType>(static_cast<UnderlyingType>(value)); const char* string_value = to_string_function(value);
hi->updateQSettings(); hi->putSettingValue(setting_name, QString::fromLocal8Bit(string_value));
}); hi->applySettings();
}
template<typename WidgetType, typename DataType>
void BindWidgetToSetting(QtHostInterface* hi, WidgetType* widget, DataType Settings::*settings_ptr,
std::enable_if_t<std::is_integral_v<DataType>, int>* v = nullptr)
{
using Accessor = SettingAccessor<WidgetType>;
Accessor::setIntValue(widget, static_cast<int>(hi->GetCoreSettings().*settings_ptr));
Accessor::connectValueChanged(widget, [hi, widget, settings_ptr](int) {
const int value = Accessor::getIntValue(widget);
(hi->GetCoreSettings().*settings_ptr) = static_cast<DataType>(value);
hi->updateQSettings();
}); });
} }