Path: blob/master/src/duckstation-qt/controllersettingwidgetbinder.h
4802 views
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <[email protected]>1// SPDX-License-Identifier: CC-BY-NC-ND-4.023#pragma once45#include "qthost.h"6#include "settingwidgetbinder.h"78#include "core/host.h"910#include <QtCore/QtCore>11#include <QtGui/QAction>12#include <QtWidgets/QCheckBox>13#include <QtWidgets/QComboBox>14#include <QtWidgets/QDoubleSpinBox>15#include <QtWidgets/QLineEdit>16#include <QtWidgets/QSlider>17#include <QtWidgets/QSpinBox>18#include <optional>19#include <type_traits>2021/// This nastyness is required because input profiles aren't overlaid settings like the rest of them, it's22/// input profile *or* global, not both.23namespace ControllerSettingWidgetBinder {24/// Interface specific method of BindWidgetToBoolSetting().25template<typename WidgetType>26inline void BindWidgetToInputProfileBool(SettingsInterface* sif, WidgetType* widget, std::string section,27std::string key, bool default_value)28{29using Accessor = SettingWidgetBinder::SettingAccessor<WidgetType>;3031if (sif)32{33const bool value = sif->GetBoolValue(section.c_str(), key.c_str(), default_value);34Accessor::setBoolValue(widget, value);3536Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key)]() {37const bool new_value = Accessor::getBoolValue(widget);38sif->SetBoolValue(section.c_str(), key.c_str(), new_value);39QtHost::SaveGameSettings(sif, false);40g_emu_thread->reloadInputProfile();41});42}43else44{45const bool value = Host::GetBaseBoolSettingValue(section.c_str(), key.c_str(), default_value);46Accessor::setBoolValue(widget, value);4748Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() {49const bool new_value = Accessor::getBoolValue(widget);50Host::SetBaseBoolSettingValue(section.c_str(), key.c_str(), new_value);51Host::CommitBaseSettingChanges();52g_emu_thread->applySettings();53});54}55}5657/// Interface specific method of BindWidgetToIntSetting().58template<typename WidgetType>59inline void BindWidgetToInputProfileInt(SettingsInterface* sif, WidgetType* widget, std::string section,60std::string key, int default_value, int option_offset = 0)61{62using Accessor = SettingWidgetBinder::SettingAccessor<WidgetType>;6364if (sif)65{66const int value = sif->GetIntValue(section.c_str(), key.c_str(), default_value) - option_offset;67Accessor::setIntValue(widget, value);6869Accessor::connectValueChanged(widget,70[sif, widget, section = std::move(section), key = std::move(key), option_offset]() {71const int new_value = Accessor::getIntValue(widget);72sif->SetIntValue(section.c_str(), key.c_str(), new_value + option_offset);73QtHost::SaveGameSettings(sif, false);74g_emu_thread->reloadInputProfile();75});76}77else78{79const int value = Host::GetBaseIntSettingValue(section.c_str(), key.c_str(), default_value) - option_offset;80Accessor::setIntValue(widget, value);8182Accessor::connectValueChanged(83widget, [widget, section = std::move(section), key = std::move(key), option_offset]() {84const int new_value = Accessor::getIntValue(widget);85Host::SetBaseIntSettingValue(section.c_str(), key.c_str(), new_value + option_offset);86Host::CommitBaseSettingChanges();87g_emu_thread->applySettings();88});89}90}9192/// Interface specific method of BindWidgetToFloatSetting().93template<typename WidgetType>94inline void BindWidgetToInputProfileFloat(SettingsInterface* sif, WidgetType* widget, std::string section,95std::string key, float default_value)96{97using Accessor = SettingWidgetBinder::SettingAccessor<WidgetType>;9899if (sif)100{101const float value = sif->GetFloatValue(section.c_str(), key.c_str(), default_value);102Accessor::setFloatValue(widget, value);103104Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key)]() {105const float new_value = Accessor::getFloatValue(widget);106sif->SetFloatValue(section.c_str(), key.c_str(), new_value);107QtHost::SaveGameSettings(sif, false);108g_emu_thread->reloadInputProfile();109});110}111else112{113const float value = Host::GetBaseFloatSettingValue(section.c_str(), key.c_str(), default_value);114Accessor::setFloatValue(widget, value);115116Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() {117const float new_value = Accessor::getFloatValue(widget);118Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value);119Host::CommitBaseSettingChanges();120g_emu_thread->applySettings();121});122}123}124125/// Interface specific method of BindWidgetToNormalizedSetting().126template<typename WidgetType>127inline void BindWidgetToInputProfileNormalized(SettingsInterface* sif, WidgetType* widget, std::string section,128std::string key, float range, float default_value)129{130using Accessor = SettingWidgetBinder::SettingAccessor<WidgetType>;131132if (sif)133{134const float value = sif->GetFloatValue(section.c_str(), key.c_str(), default_value);135Accessor::setIntValue(widget, static_cast<int>(value * range));136137Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key), range]() {138const int new_value = Accessor::getIntValue(widget);139sif->SetFloatValue(section.c_str(), key.c_str(), static_cast<float>(new_value) / range);140QtHost::SaveGameSettings(sif, false);141g_emu_thread->reloadInputProfile();142});143}144else145{146const float value = Host::GetBaseFloatSettingValue(section.c_str(), key.c_str(), default_value);147Accessor::setIntValue(widget, static_cast<int>(value * range));148149Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key), range]() {150const float new_value = (static_cast<float>(Accessor::getIntValue(widget)) / range);151Host::SetBaseFloatSettingValue(section.c_str(), key.c_str(), new_value);152Host::CommitBaseSettingChanges();153g_emu_thread->applySettings();154});155}156}157158/// Interface specific method of BindWidgetToStringSetting().159template<typename WidgetType>160inline void BindWidgetToInputProfileString(SettingsInterface* sif, WidgetType* widget, std::string section,161std::string key, std::string default_value = std::string())162{163using Accessor = SettingWidgetBinder::SettingAccessor<WidgetType>;164165if (sif)166{167const QString value(168QString::fromStdString(sif->GetStringValue(section.c_str(), key.c_str(), default_value.c_str())));169170Accessor::setStringValue(widget, value);171172Accessor::connectValueChanged(widget, [widget, sif, section = std::move(section), key = std::move(key)]() {173sif->SetStringValue(section.c_str(), key.c_str(), Accessor::getStringValue(widget).toUtf8().constData());174QtHost::SaveGameSettings(sif, false);175g_emu_thread->reloadInputProfile();176});177}178else179{180const QString value(181QString::fromStdString(Host::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value.c_str())));182183Accessor::setStringValue(widget, value);184185Accessor::connectValueChanged(widget, [widget, section = std::move(section), key = std::move(key)]() {186Host::SetBaseStringSettingValue(section.c_str(), key.c_str(),187Accessor::getStringValue(widget).toUtf8().constData());188Host::CommitBaseSettingChanges();189g_emu_thread->applySettings();190});191}192}193194/// Interface specific method of BindWidgetToEnumSetting().195template<typename WidgetType, typename DataType, typename ValueCountType>196inline void BindWidgetToInputProfileEnumSetting(SettingsInterface* sif, WidgetType* widget, std::string section,197std::string key,198std::optional<DataType> (*from_string_function)(const char* str),199const char* (*to_string_function)(DataType value),200const char* (*to_display_name_function)(DataType value),201DataType default_value, ValueCountType value_count)202{203using Accessor = SettingWidgetBinder::SettingAccessor<WidgetType>;204using UnderlyingType = std::underlying_type_t<DataType>;205206for (UnderlyingType i = 0; i < static_cast<UnderlyingType>(value_count); i++)207Accessor::addOption(widget, to_display_name_function(static_cast<DataType>(i)));208209const std::string value =210sif ? sif->GetStringValue(section.c_str(), key.c_str(), to_string_function(default_value)) :211Host::GetBaseStringSettingValue(section.c_str(), key.c_str(), to_string_function(default_value));212const std::optional<DataType> typed_value = from_string_function(value.c_str());213if (typed_value.has_value())214Accessor::setIntValue(widget, static_cast<int>(static_cast<UnderlyingType>(typed_value.value())));215else216Accessor::setIntValue(widget, static_cast<int>(static_cast<UnderlyingType>(default_value)));217218if (sif)219{220Accessor::connectValueChanged(221widget, [sif, widget, section = std::move(section), key = std::move(key), to_string_function]() {222const DataType value = static_cast<DataType>(static_cast<UnderlyingType>(Accessor::getIntValue(widget)));223const char* string_value = to_string_function(value);224sif->SetStringValue(section.c_str(), key.c_str(), string_value);225QtHost::SaveGameSettings(sif, true);226g_emu_thread->reloadInputProfile();227});228}229else230{231Accessor::connectValueChanged(232widget, [widget, section = std::move(section), key = std::move(key), to_string_function]() {233const DataType value = static_cast<DataType>(static_cast<UnderlyingType>(Accessor::getIntValue(widget)));234const char* string_value = to_string_function(value);235Host::SetBaseStringSettingValue(section.c_str(), key.c_str(), string_value);236Host::CommitBaseSettingChanges();237g_emu_thread->applySettings();238});239}240}241} // namespace ControllerSettingWidgetBinder242243244