diff --git a/pcsx2-qt/SettingWidgetBinder.h b/pcsx2-qt/SettingWidgetBinder.h index 316e446357..f633141c27 100644 --- a/pcsx2-qt/SettingWidgetBinder.h +++ b/pcsx2-qt/SettingWidgetBinder.h @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,10 @@ namespace SettingWidgetBinder { + static constexpr const char* NULLABLE_PROPERTY = "SettingWidgetBinder_isNullable"; + static constexpr const char* IS_NULL_PROPERTY = "SettingWidgetBinder_isNull"; + static constexpr const char* GLOBAL_VALUE_PROPERTY = "SettingWidgetBinder_globalValue"; + template struct SettingAccessor { @@ -179,7 +184,7 @@ namespace SettingWidgetBinder } static void setNullableStringValue(QComboBox* widget, std::optional value) { - isNullValue(widget) ? widget->setCurrentIndex(0) : setStringValue(widget, value.value()); + value.has_value() ? setStringValue(widget, value.value()) : widget->setCurrentIndex(0); } template @@ -286,71 +291,256 @@ namespace SettingWidgetBinder template <> struct SettingAccessor { + static bool isNullable(const QSpinBox* widget) { return widget->property(NULLABLE_PROPERTY).toBool(); } + + static void updateNullState(QSpinBox* widget, bool isNull) + { + widget->setPrefix(isNull ? qApp->translate("SettingWidgetBinder", "Default: ") : QString()); + } + static bool getBoolValue(const QSpinBox* widget) { return widget->value() > 0; } static void setBoolValue(QSpinBox* widget, bool value) { widget->setValue(value ? 1 : 0); } - static void makeNullableBool(QSpinBox* widget, bool globalSetting) { widget->setEnabled(false); } - static std::optional getNullableBoolValue(const QSpinBox* widget) { return getBoolValue(widget); } - static void setNullableBoolValue(QSpinBox* widget, std::optional value) { setBoolValue(widget, value.value_or(false)); } + static void makeNullableBool(QSpinBox* widget, bool globalSetting) + { + widget->setProperty(NULLABLE_PROPERTY, QVariant(true)); + widget->setProperty(GLOBAL_VALUE_PROPERTY, QVariant(globalSetting)); + } + static std::optional getNullableBoolValue(const QSpinBox* widget) + { + if (widget->property(IS_NULL_PROPERTY).toBool()) + return std::nullopt; + + return getBoolValue(widget); + } + static void setNullableBoolValue(QSpinBox* widget, std::optional value) + { + widget->setProperty(IS_NULL_PROPERTY, QVariant(!value.has_value())); + setBoolValue(widget, value.has_value() ? value.value() : widget->property(GLOBAL_VALUE_PROPERTY).toBool()); + updateNullState(widget, !value.has_value()); + } static int getIntValue(const QSpinBox* widget) { return widget->value(); } static void setIntValue(QSpinBox* widget, int value) { widget->setValue(value); } - static void makeNullableInt(QSpinBox* widget, int globalValue) { widget->setEnabled(false); } - static std::optional getNullableIntValue(const QSpinBox* widget) { return getIntValue(widget); } - static void setNullableIntValue(QSpinBox* widget, std::optional value) { setIntValue(widget, value.value_or(0)); } + static void makeNullableInt(QSpinBox* widget, int globalValue) + { + widget->setProperty(NULLABLE_PROPERTY, QVariant(true)); + widget->setProperty(GLOBAL_VALUE_PROPERTY, QVariant(globalValue)); + } + static std::optional getNullableIntValue(const QSpinBox* widget) + { + if (widget->property(IS_NULL_PROPERTY).toBool()) + return std::nullopt; + + return getIntValue(widget); + } + static void setNullableIntValue(QSpinBox* widget, std::optional value) + { + widget->setProperty(IS_NULL_PROPERTY, QVariant(!value.has_value())); + setIntValue(widget, value.has_value() ? value.value() : widget->property(GLOBAL_VALUE_PROPERTY).toInt()); + updateNullState(widget, !value.has_value()); + } static float getFloatValue(const QSpinBox* widget) { return static_cast(widget->value()); } static void setFloatValue(QSpinBox* widget, float value) { widget->setValue(static_cast(value)); } - static void makeNullableFloat(QSpinBox* widget, float globalValue) { widget->setEnabled(false); } - static std::optional getNullableFloatValue(const QSpinBox* widget) { return getFloatValue(widget); } - static void setNullableFloatValue(QSpinBox* widget, std::optional value) { setFloatValue(widget, value.value_or(0.0f)); } + static void makeNullableFloat(QSpinBox* widget, float globalValue) + { + widget->setProperty(NULLABLE_PROPERTY, QVariant(true)); + widget->setProperty(GLOBAL_VALUE_PROPERTY, QVariant(globalValue)); + } + static std::optional getNullableFloatValue(const QSpinBox* widget) + { + if (widget->property(IS_NULL_PROPERTY).toBool()) + return std::nullopt; + + return getFloatValue(widget); + } + static void setNullableFloatValue(QSpinBox* widget, std::optional value) + { + widget->setProperty(IS_NULL_PROPERTY, QVariant(!value.has_value())); + setFloatValue(widget, value.has_value() ? value.value() : widget->property(GLOBAL_VALUE_PROPERTY).toFloat()); + updateNullState(widget, !value.has_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 makeNullableString(QSpinBox* widget, const QString& globalValue) { widget->setEnabled(false); } - static std::optional getNullableStringValue(const QSpinBox* widget) { return getStringValue(widget); } - static void setNullableStringValue(QSpinBox* widget, std::optional value) { setStringValue(widget, value.value_or(QString())); } + static void makeNullableString(QSpinBox* widget, const QString& globalValue) + { + widget->setProperty(NULLABLE_PROPERTY, QVariant(true)); + widget->setProperty(GLOBAL_VALUE_PROPERTY, QVariant(globalValue)); + } + static std::optional getNullableStringValue(const QSpinBox* widget) + { + if (widget->property(IS_NULL_PROPERTY).toBool()) + return std::nullopt; + + return getStringValue(widget); + } + static void setNullableStringValue(QSpinBox* widget, std::optional value) + { + widget->setProperty(IS_NULL_PROPERTY, QVariant(!value.has_value())); + setStringValue(widget, value.has_value() ? value.value() : widget->property(GLOBAL_VALUE_PROPERTY).toString()); + updateNullState(widget, !value.has_value()); + } template static void connectValueChanged(QSpinBox* widget, F func) { - widget->connect(widget, QOverload::of(&QSpinBox::valueChanged), func); + if (!isNullable(widget)) + { + widget->connect(widget, QOverload::of(&QSpinBox::valueChanged), func); + } + else + { + widget->setContextMenuPolicy(Qt::CustomContextMenu); + widget->connect(widget, &QSpinBox::customContextMenuRequested, widget, [widget, func](const QPoint& pt) { + QMenu menu(widget); + widget->connect(menu.addAction(qApp->translate("SettingWidgetBinder", "Reset")), &QAction::triggered, widget, + [widget, func = std::move(func)]() { + const bool old = widget->blockSignals(true); + setNullableIntValue(widget, std::nullopt); + widget->blockSignals(old); + updateNullState(widget, true); + func(); + }); + menu.exec(widget->mapToGlobal(pt)); + }); + widget->connect(widget, &QSpinBox::valueChanged, widget, [widget, func = std::move(func)]() { + if (widget->property(IS_NULL_PROPERTY).toBool()) + { + widget->setProperty(IS_NULL_PROPERTY, QVariant(false)); + updateNullState(widget, false); + } + func(); + }); + } } }; template <> struct SettingAccessor { + static bool isNullable(const QDoubleSpinBox* widget) { return widget->property(NULLABLE_PROPERTY).toBool(); } + + static void updateNullState(QDoubleSpinBox* widget, bool isNull) + { + widget->setPrefix(isNull ? qApp->translate("SettingWidgetBinder", "Default: ") : QString()); + } + static bool getBoolValue(const QDoubleSpinBox* widget) { return widget->value() > 0.0; } static void setBoolValue(QDoubleSpinBox* widget, bool value) { widget->setValue(value ? 1.0 : 0.0); } - static void makeNullableBool(QDoubleSpinBox* widget, bool globalSetting) { widget->setEnabled(false); } - static std::optional getNullableBoolValue(const QDoubleSpinBox* widget) { return getBoolValue(widget); } - static void setNullableBoolValue(QDoubleSpinBox* widget, std::optional value) { setBoolValue(widget, value.value_or(false)); } + static void makeNullableBool(QDoubleSpinBox* widget, bool globalSetting) + { + widget->setProperty(NULLABLE_PROPERTY, QVariant(true)); + widget->setProperty(GLOBAL_VALUE_PROPERTY, QVariant(globalSetting)); + } + static std::optional getNullableBoolValue(const QDoubleSpinBox* widget) + { + if (widget->property(IS_NULL_PROPERTY).toBool()) + return std::nullopt; + + return getBoolValue(widget); + } + static void setNullableBoolValue(QDoubleSpinBox* widget, std::optional value) + { + widget->setProperty(IS_NULL_PROPERTY, QVariant(!value.has_value())); + setBoolValue(widget, value.has_value() ? value.value() : widget->property(GLOBAL_VALUE_PROPERTY).toBool()); + updateNullState(widget, !value.has_value()); + } static int getIntValue(const QDoubleSpinBox* widget) { return static_cast(widget->value()); } static void setIntValue(QDoubleSpinBox* widget, int value) { widget->setValue(static_cast(value)); } - static void makeNullableInt(QDoubleSpinBox* widget, int globalValue) { widget->setEnabled(false); } - static std::optional getNullableIntValue(const QDoubleSpinBox* widget) { return getIntValue(widget); } - static void setNullableIntValue(QDoubleSpinBox* widget, std::optional value) { setIntValue(widget, value.value_or(0)); } + static void makeNullableInt(QDoubleSpinBox* widget, int globalValue) + { + widget->setProperty(NULLABLE_PROPERTY, QVariant(true)); + widget->setProperty(GLOBAL_VALUE_PROPERTY, QVariant(globalValue)); + } + static std::optional getNullableIntValue(const QDoubleSpinBox* widget) + { + if (widget->property(IS_NULL_PROPERTY).toBool()) + return std::nullopt; + + return getIntValue(widget); + } + static void setNullableIntValue(QDoubleSpinBox* widget, std::optional value) + { + widget->setProperty(IS_NULL_PROPERTY, QVariant(!value.has_value())); + setIntValue(widget, value.has_value() ? value.value() : widget->property(GLOBAL_VALUE_PROPERTY).toInt()); + updateNullState(widget, !value.has_value()); + } static float getFloatValue(const QDoubleSpinBox* widget) { return static_cast(widget->value()); } static void setFloatValue(QDoubleSpinBox* widget, float value) { widget->setValue(static_cast(value)); } - static void makeNullableFloat(QDoubleSpinBox* widget, float globalValue) { widget->setEnabled(false); } - static std::optional getNullableFloatValue(const QDoubleSpinBox* widget) { return getFloatValue(widget); } - static void setNullableFloatValue(QDoubleSpinBox* widget, std::optional value) { setFloatValue(widget, value.value_or(0.0f)); } + static void makeNullableFloat(QDoubleSpinBox* widget, float globalValue) + { + widget->setProperty(NULLABLE_PROPERTY, QVariant(true)); + widget->setProperty(GLOBAL_VALUE_PROPERTY, QVariant(globalValue)); + } + static std::optional getNullableFloatValue(const QDoubleSpinBox* widget) + { + if (widget->property(IS_NULL_PROPERTY).toBool()) + return std::nullopt; + + return getFloatValue(widget); + } + static void setNullableFloatValue(QDoubleSpinBox* widget, std::optional value) + { + widget->setProperty(IS_NULL_PROPERTY, QVariant(!value.has_value())); + setFloatValue(widget, value.has_value() ? value.value() : widget->property(GLOBAL_VALUE_PROPERTY).toFloat()); + updateNullState(widget, !value.has_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 makeNullableString(QDoubleSpinBox* widget, const QString& globalValue) { widget->setEnabled(false); } - static std::optional getNullableStringValue(const QDoubleSpinBox* widget) { return getStringValue(widget); } + static void makeNullableString(QDoubleSpinBox* widget, const QString& globalValue) + { + widget->setProperty(NULLABLE_PROPERTY, QVariant(true)); + widget->setProperty(GLOBAL_VALUE_PROPERTY, QVariant(globalValue)); + } + static std::optional getNullableStringValue(const QDoubleSpinBox* widget) + { + if (widget->property(IS_NULL_PROPERTY).toBool()) + return std::nullopt; + + return getStringValue(widget); + } static void setNullableStringValue(QDoubleSpinBox* widget, std::optional value) { - setStringValue(widget, value.value_or(QString())); + widget->setProperty(IS_NULL_PROPERTY, QVariant(!value.has_value())); + setStringValue(widget, value.has_value() ? value.value() : widget->property(GLOBAL_VALUE_PROPERTY).toString()); + updateNullState(widget, !value.has_value()); } template static void connectValueChanged(QDoubleSpinBox* widget, F func) { - widget->connect(widget, QOverload::of(&QDoubleSpinBox::valueChanged), func); + if (!isNullable(widget)) + { + widget->connect(widget, QOverload::of(&QDoubleSpinBox::valueChanged), func); + } + else + { + widget->setContextMenuPolicy(Qt::CustomContextMenu); + widget->connect(widget, &QDoubleSpinBox::customContextMenuRequested, widget, [widget, func](const QPoint& pt) { + QMenu menu(widget); + widget->connect(menu.addAction(qApp->translate("SettingWidgetBinder", "Reset")), &QAction::triggered, widget, + [widget, func = std::move(func)]() { + const bool old = widget->blockSignals(true); + setNullableFloatValue(widget, std::nullopt); + widget->blockSignals(old); + updateNullState(widget, true); + func(); + }); + menu.exec(widget->mapToGlobal(pt)); + }); + widget->connect(widget, QOverload::of(&QDoubleSpinBox::valueChanged), widget, [widget, func = std::move(func)]() { + if (widget->property(IS_NULL_PROPERTY).toBool()) + { + widget->setProperty(IS_NULL_PROPERTY, QVariant(false)); + updateNullState(widget, false); + } + func(); + }); + } } }; @@ -776,14 +966,15 @@ namespace SettingWidgetBinder } template - static void BindWidgetToFolderSetting(SettingsInterface* sif, WidgetType* widget, - QAbstractButton* browse_button, QAbstractButton* open_button, QAbstractButton* reset_button, - std::string section, std::string key, std::string default_value) + static void BindWidgetToFolderSetting(SettingsInterface* sif, WidgetType* widget, QAbstractButton* browse_button, QAbstractButton* open_button, + QAbstractButton* reset_button, std::string section, std::string key, std::string default_value) { using Accessor = SettingAccessor; std::string current_path(Host::GetBaseStringSettingValue(section.c_str(), key.c_str(), default_value.c_str())); - if (!Path::IsAbsolute(current_path)) + if (current_path.empty()) + current_path = default_value; + else if (!Path::IsAbsolute(current_path)) current_path = Path::Combine(EmuFolders::DataRoot, current_path); const QString value(QString::fromStdString(current_path)); @@ -836,9 +1027,8 @@ namespace SettingWidgetBinder } if (reset_button) { - QObject::connect(reset_button, &QAbstractButton::clicked, reset_button, [widget, default_value = std::move(default_value)]() { - Accessor::setStringValue(widget, QString::fromStdString(Path::Combine(EmuFolders::AppRoot, default_value))); - }); + QObject::connect(reset_button, &QAbstractButton::clicked, reset_button, + [widget, default_value = std::move(default_value)]() { Accessor::setStringValue(widget, QString::fromStdString(default_value)); }); } } } // namespace SettingWidgetBinder diff --git a/pcsx2-qt/Settings/BIOSSettingsWidget.cpp b/pcsx2-qt/Settings/BIOSSettingsWidget.cpp index 7514ce5087..e87c00768a 100644 --- a/pcsx2-qt/Settings/BIOSSettingsWidget.cpp +++ b/pcsx2-qt/Settings/BIOSSettingsWidget.cpp @@ -39,7 +39,7 @@ BIOSSettingsWidget::BIOSSettingsWidget(SettingsDialog* dialog, QWidget* parent) SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.patchRegion, "EmuCore", "PatchBios", false); SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.regionComboBox, "EmuCore", "PatchRegion", BiosZoneStrings, BiosZoneBytes, BiosZoneBytes[0]); SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.searchDirectory, m_ui.browseSearchDirectory, m_ui.openSearchDirectory, - m_ui.resetSearchDirectory, "Folders", "Bios", "bios"); + m_ui.resetSearchDirectory, "Folders", "Bios", Path::Combine(EmuFolders::DataRoot, "bios")); dialog->registerWidgetHelp(m_ui.patchRegion, tr("Patch Region"), tr("Unchecked"), tr("Patches the BIOS region byte in ROM. Not recommended unless you really know what you're doing.")); diff --git a/pcsx2-qt/Settings/FolderSettingsWidget.cpp b/pcsx2-qt/Settings/FolderSettingsWidget.cpp index 38c707c13d..457e76b957 100644 --- a/pcsx2-qt/Settings/FolderSettingsWidget.cpp +++ b/pcsx2-qt/Settings/FolderSettingsWidget.cpp @@ -29,10 +29,10 @@ FolderSettingsWidget::FolderSettingsWidget(SettingsDialog* dialog, QWidget* pare m_ui.setupUi(this); - SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.cache, m_ui.cacheBrowse, m_ui.cacheOpen, m_ui.cacheReset, "Folders", "Cache", "cache"); - SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.covers, m_ui.coversBrowse, m_ui.coversOpen, m_ui.coversReset, "Folders", "Covers", "covers"); - SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.snapshots, m_ui.snapshotsBrowse, m_ui.snapshotsOpen, m_ui.snapshotsReset, "Folders", "Snapshots", "snaps"); - SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.saveStates, m_ui.saveStatesBrowse, m_ui.saveStatesOpen, m_ui.saveStatesReset, "Folders", "SaveStates", "sstates"); + SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.cache, m_ui.cacheBrowse, m_ui.cacheOpen, m_ui.cacheReset, "Folders", "Cache", Path::Combine(EmuFolders::DataRoot, "cache")); + SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.covers, m_ui.coversBrowse, m_ui.coversOpen, m_ui.coversReset, "Folders", "Covers", Path::Combine(EmuFolders::DataRoot, "covers")); + SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.snapshots, m_ui.snapshotsBrowse, m_ui.snapshotsOpen, m_ui.snapshotsReset, "Folders", "Snapshots", Path::Combine(EmuFolders::DataRoot, "snaps")); + SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.saveStates, m_ui.saveStatesBrowse, m_ui.saveStatesOpen, m_ui.saveStatesReset, "Folders", "SaveStates", Path::Combine(EmuFolders::DataRoot, "sstates")); } FolderSettingsWidget::~FolderSettingsWidget() = default; diff --git a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp index 32d58f372a..79d7ef7a01 100644 --- a/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp +++ b/pcsx2-qt/Settings/GraphicsSettingsWidget.cpp @@ -232,7 +232,7 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget* SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.loadTextureReplacementsAsync, "EmuCore/GS", "LoadTextureReplacementsAsync", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.precacheTextureReplacements, "EmuCore/GS", "PrecacheTextureReplacements", false); SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.texturesDirectory, m_ui.texturesBrowse, m_ui.texturesOpen, m_ui.texturesReset, - "Folders", "Textures", "textures"); + "Folders", "Textures", Path::Combine(EmuFolders::DataRoot, "textures")); ////////////////////////////////////////////////////////////////////////// // Advanced Settings diff --git a/pcsx2-qt/Settings/MemoryCardSettingsWidget.cpp b/pcsx2-qt/Settings/MemoryCardSettingsWidget.cpp index 8bf96062e5..436793cc71 100644 --- a/pcsx2-qt/Settings/MemoryCardSettingsWidget.cpp +++ b/pcsx2-qt/Settings/MemoryCardSettingsWidget.cpp @@ -50,7 +50,7 @@ MemoryCardSettingsWidget::MemoryCardSettingsWidget(SettingsDialog* dialog, QWidg // since the group box hasn't been resized at that point. m_ui.cardGroupBox->installEventFilter(this); - SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.directory, m_ui.browse, m_ui.open, m_ui.reset, "Folders", "MemoryCards", "memcards"); + SettingWidgetBinder::BindWidgetToFolderSetting(sif, m_ui.directory, m_ui.browse, m_ui.open, m_ui.reset, "Folders", "MemoryCards", Path::Combine(EmuFolders::DataRoot, "memcards")); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.autoEject, "EmuCore", "McdEnableEjection", true); SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.automaticManagement, "EmuCore", "McdFolderAutoManage", true);