Merge pull request #12655 from jordan-woyak/numeric-setting-fix

NumericSetting: Stop values from binding to numbered input names.
This commit is contained in:
Admiral H. Curtiss 2024-04-13 00:42:03 +02:00 committed by GitHub
commit a44511741c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 63 additions and 34 deletions

View File

@ -332,6 +332,9 @@ MappingWidget::CreateSettingAdvancedMappingButton(ControllerEmu::NumericSettingB
if (setting.IsSimpleValue()) if (setting.IsSimpleValue())
setting.SetExpressionFromValue(); setting.SetExpressionFromValue();
// Ensure the UI has the game-controller indicator while editing the expression.
ConfigChanged();
IOWindow io(this, GetController(), &setting.GetInputReference(), IOWindow::Type::Input); IOWindow io(this, GetController(), &setting.GetInputReference(), IOWindow::Type::Input);
SetQWidgetWindowDecorations(&io); SetQWidgetWindowDecorations(&io);
io.exec(); io.exec();

View File

@ -9,6 +9,12 @@
#include "InputCommon/ControlReference/ExpressionParser.h" #include "InputCommon/ControlReference/ExpressionParser.h"
#include "InputCommon/ControllerInterface/CoreDevice.h" #include "InputCommon/ControllerInterface/CoreDevice.h"
namespace ControllerEmu
{
template <typename T>
T ControlStateCast(ControlState value);
}
// ControlReference // ControlReference
// //
// These are what you create to actually use the inputs, InputReference or OutputReference. // These are what you create to actually use the inputs, InputReference or OutputReference.
@ -31,7 +37,10 @@ public:
virtual bool IsInput() const = 0; virtual bool IsInput() const = 0;
template <typename T> template <typename T>
T GetState(); T GetState()
{
return ControllerEmu::ControlStateCast<T>(State());
}
int BoundCount() const; int BoundCount() const;
ciface::ExpressionParser::ParseStatus GetParseStatus() const; ciface::ExpressionParser::ParseStatus GetParseStatus() const;
@ -51,24 +60,27 @@ protected:
ciface::ExpressionParser::ParseStatus::EmptyExpression; ciface::ExpressionParser::ParseStatus::EmptyExpression;
}; };
namespace ControllerEmu
{
template <> template <>
inline bool ControlReference::GetState<bool>() inline bool ControlStateCast<bool>(ControlState value)
{ {
// Round to nearest of 0 or 1. // Round to nearest of 0 or 1.
return std::lround(State()) > 0; return std::lround(value) > 0;
} }
template <> template <>
inline int ControlReference::GetState<int>() inline int ControlStateCast<int>(ControlState value)
{ {
return std::lround(State()); return std::lround(value);
} }
template <> template <>
inline ControlState ControlReference::GetState<ControlState>() inline ControlState ControlStateCast<ControlState>(ControlState value)
{ {
return State(); return value;
} }
} // namespace ControllerEmu
// //
// InputReference // InputReference

View File

@ -830,6 +830,12 @@ private:
// Interpret it as a unary minus function. // Interpret it as a unary minus function.
return ParseFunctionArguments("minus", MakeFunctionExpression("minus"), tok); return ParseFunctionArguments("minus", MakeFunctionExpression("minus"), tok);
} }
case TOK_ADD:
{
// An atom was expected but we got an addition symbol.
// Interpret it as a unary plus.
return ParseFunctionArguments("plus", MakeFunctionExpression("plus"), tok);
}
default: default:
{ {
return ParseResult::MakeErrorResult(tok, _trans("Expected start of expression.")); return ParseResult::MakeErrorResult(tok, _trans("Expected start of expression."));

View File

@ -375,6 +375,22 @@ private:
} }
}; };
// usage: plus(expression)
class UnaryPlusExpression : public FunctionExpression
{
private:
ArgumentValidation
ValidateArguments(const std::vector<std::unique_ptr<Expression>>& args) override
{
if (args.size() == 1)
return ArgumentsAreValid{};
else
return ExpectedArguments{"expression"};
}
ControlState GetValue() const override { return GetArg(0).GetValue(); }
};
// usage: deadzone(input, amount) // usage: deadzone(input, amount)
class DeadzoneExpression : public FunctionExpression class DeadzoneExpression : public FunctionExpression
{ {
@ -689,6 +705,8 @@ std::unique_ptr<FunctionExpression> MakeFunctionExpression(std::string_view name
return std::make_unique<ToggleExpression>(); return std::make_unique<ToggleExpression>();
if (name == "minus") if (name == "minus")
return std::make_unique<UnaryMinusExpression>(); return std::make_unique<UnaryMinusExpression>();
if (name == "plus")
return std::make_unique<UnaryPlusExpression>();
if (name == "deadzone") if (name == "deadzone")
return std::make_unique<DeadzoneExpression>(); return std::make_unique<DeadzoneExpression>();
if (name == "smooth") if (name == "smooth")

View File

@ -3,7 +3,7 @@
#include "InputCommon/ControllerEmu/Setting/NumericSetting.h" #include "InputCommon/ControllerEmu/Setting/NumericSetting.h"
#include <sstream> #include <fmt/format.h>
namespace ControllerEmu namespace ControllerEmu
{ {
@ -11,6 +11,11 @@ NumericSettingBase::NumericSettingBase(const NumericSettingDetails& details) : m
{ {
} }
// Explicit instantiations so generic definitions can exist outside of the header.
template class NumericSetting<int>;
template class NumericSetting<double>;
template class NumericSetting<bool>;
const char* NumericSettingBase::GetININame() const const char* NumericSettingBase::GetININame() const
{ {
return m_details.ini_name; return m_details.ini_name;
@ -36,28 +41,20 @@ SettingVisibility NumericSettingBase::GetVisibility() const
return m_details.visibility; return m_details.visibility;
} }
template <> template <typename T>
void NumericSetting<int>::SetExpressionFromValue() void NumericSetting<T>::SetExpressionFromValue()
{ {
m_value.m_input.SetExpression(ValueToString(GetValue())); // Always include -/+ sign to prevent CoalesceExpression binding.
// e.g. 1 is a valid input name for keyboard devices, +1 is not.
m_value.m_input.SetExpression(fmt::format("{:+g}", ControlState(GetValue())));
} }
template <> template <typename T>
void NumericSetting<double>::SetExpressionFromValue() void NumericSetting<T>::SimplifyIfPossible()
{ {
// We must use a dot decimal separator for expression parser. ValueType value;
std::ostringstream ss; if (TryParse(std::string(StripWhitespace(m_value.m_input.GetExpression())), &value))
ss.imbue(std::locale::classic()); m_value.SetValue(value);
ss << GetValue();
m_value.m_input.SetExpression(ss.str());
}
template <>
void NumericSetting<bool>::SetExpressionFromValue()
{
// Cast bool to prevent "true"/"false" strings.
m_value.m_input.SetExpression(ValueToString(int(GetValue())));
} }
template <> template <>

View File

@ -143,14 +143,7 @@ public:
} }
bool IsSimpleValue() const override { return m_value.IsSimpleValue(); } bool IsSimpleValue() const override { return m_value.IsSimpleValue(); }
void SimplifyIfPossible() override;
void SimplifyIfPossible() override
{
ValueType value;
if (TryParse(m_value.m_input.GetExpression(), &value))
m_value.SetValue(value);
}
void SetExpressionFromValue() override; void SetExpressionFromValue() override;
InputReference& GetInputReference() override { return m_value.m_input; } InputReference& GetInputReference() override { return m_value.m_input; }
const InputReference& GetInputReference() const override { return m_value.m_input; } const InputReference& GetInputReference() const override { return m_value.m_input; }