Expose Control Expression variables to mappings UI
-add a way to reset their value (from the mappings UI) -fix "memory leak" where they would never be cleaned, one would be created every time you wrote a character after a "$" -fix ability to create variables with an empty string by just writing "$" (+added error for it) -Add $ operator to the UI operators list, to expose this functionality even more
This commit is contained in:
parent
975f8e2a25
commit
93e3e691f9
|
@ -275,6 +275,7 @@ void IOWindow::CreateMainLayout()
|
|||
m_operators_combo->addItem(tr("^ Xor"));
|
||||
}
|
||||
m_operators_combo->addItem(tr("| Or"));
|
||||
m_operators_combo->addItem(tr("$ User Variable"));
|
||||
if (m_type == Type::Input)
|
||||
{
|
||||
m_operators_combo->addItem(tr(", Comma"));
|
||||
|
@ -305,6 +306,15 @@ void IOWindow::CreateMainLayout()
|
|||
m_functions_combo->addItem(QStringLiteral("max"));
|
||||
m_functions_combo->addItem(QStringLiteral("clamp"));
|
||||
|
||||
m_variables_combo = new QComboBoxWithMouseWheelDisabled(this);
|
||||
m_variables_combo->addItem(tr("User Variables"));
|
||||
m_variables_combo->setToolTip(
|
||||
tr("User defined variables usable in the control expression.\nYou can use them to save or "
|
||||
"retrieve values between\ninputs and outputs of the same parent controller."));
|
||||
m_variables_combo->insertSeparator(m_variables_combo->count());
|
||||
m_variables_combo->addItem(tr("Reset Values"));
|
||||
m_variables_combo->insertSeparator(m_variables_combo->count());
|
||||
|
||||
// Devices
|
||||
m_main_layout->addWidget(m_devices_combo);
|
||||
|
||||
|
@ -366,6 +376,8 @@ void IOWindow::CreateMainLayout()
|
|||
button_vbox->addWidget(m_test_button);
|
||||
}
|
||||
|
||||
button_vbox->addWidget(m_variables_combo);
|
||||
|
||||
button_vbox->addWidget(m_operators_combo);
|
||||
|
||||
if (m_type == Type::Input)
|
||||
|
@ -425,8 +437,26 @@ void IOWindow::ConnectWidgets()
|
|||
connect(m_expression_text, &QPlainTextEdit::textChanged,
|
||||
[this] { UpdateExpression(m_expression_text->toPlainText().toStdString()); });
|
||||
|
||||
connect(m_variables_combo, qOverload<int>(&QComboBox::activated), [this](int index) {
|
||||
if (index == 0)
|
||||
return;
|
||||
|
||||
// Reset button. 1 and 3 are separators.
|
||||
if (index == 2)
|
||||
{
|
||||
const auto lock = ControllerEmu::EmulatedController::GetStateLock();
|
||||
m_controller->ResetExpressionVariables();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_expression_text->insertPlainText(QLatin1Char('$') + m_variables_combo->currentText());
|
||||
}
|
||||
|
||||
m_variables_combo->setCurrentIndex(0);
|
||||
});
|
||||
|
||||
connect(m_operators_combo, qOverload<int>(&QComboBox::activated), [this](int index) {
|
||||
if (0 == index)
|
||||
if (index == 0)
|
||||
return;
|
||||
|
||||
m_expression_text->insertPlainText(m_operators_combo->currentText().left(1));
|
||||
|
@ -435,7 +465,7 @@ void IOWindow::ConnectWidgets()
|
|||
});
|
||||
|
||||
connect(m_functions_combo, qOverload<int>(&QComboBox::activated), [this](int index) {
|
||||
if (0 == index)
|
||||
if (index == 0)
|
||||
return;
|
||||
|
||||
m_expression_text->insertPlainText(m_functions_combo->currentText() + QStringLiteral("()"));
|
||||
|
@ -564,6 +594,16 @@ void IOWindow::UpdateExpression(std::string new_expression, UpdateMode mode)
|
|||
const auto status = m_reference->GetParseStatus();
|
||||
m_controller->UpdateSingleControlReference(g_controller_interface, m_reference);
|
||||
|
||||
// This is the only place where we need to update the user variables. Keep the first 4 items.
|
||||
while (m_variables_combo->count() > 4)
|
||||
{
|
||||
m_variables_combo->removeItem(m_variables_combo->count() - 1);
|
||||
}
|
||||
for (const auto& expression : m_controller->GetExpressionVariables())
|
||||
{
|
||||
m_variables_combo->addItem(QString::fromStdString(expression.first));
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
m_parse_text->SetShouldPaintStateIndicator(false);
|
||||
|
|
|
@ -111,6 +111,7 @@ private:
|
|||
// Shared actions
|
||||
QPushButton* m_select_button;
|
||||
QComboBox* m_operators_combo;
|
||||
QComboBox* m_variables_combo;
|
||||
|
||||
// Input actions
|
||||
QPushButton* m_detect_button;
|
||||
|
|
|
@ -461,20 +461,24 @@ class VariableExpression : public Expression
|
|||
public:
|
||||
VariableExpression(std::string name) : m_name(name) {}
|
||||
|
||||
ControlState GetValue() const override { return *m_value_ptr; }
|
||||
ControlState GetValue() const override { return m_variable_ptr ? *m_variable_ptr : 0; }
|
||||
|
||||
void SetValue(ControlState value) override { *m_value_ptr = value; }
|
||||
void SetValue(ControlState value) override
|
||||
{
|
||||
if (m_variable_ptr)
|
||||
*m_variable_ptr = value;
|
||||
}
|
||||
|
||||
int CountNumControls() const override { return 1; }
|
||||
|
||||
void UpdateReferences(ControlEnvironment& env) override
|
||||
{
|
||||
m_value_ptr = env.GetVariablePtr(m_name);
|
||||
m_variable_ptr = env.GetVariablePtr(m_name);
|
||||
}
|
||||
|
||||
protected:
|
||||
const std::string m_name;
|
||||
ControlState* m_value_ptr{};
|
||||
std::shared_ptr<ControlState> m_variable_ptr;
|
||||
};
|
||||
|
||||
class HotkeyExpression : public Expression
|
||||
|
@ -621,9 +625,30 @@ Device::Output* ControlEnvironment::FindOutput(ControlQualifier qualifier) const
|
|||
return device->FindOutput(qualifier.control_name);
|
||||
}
|
||||
|
||||
ControlState* ControlEnvironment::GetVariablePtr(const std::string& name)
|
||||
std::shared_ptr<ControlState> ControlEnvironment::GetVariablePtr(const std::string& name)
|
||||
{
|
||||
return &m_variables[name];
|
||||
// Do not accept an empty string as key, even if the expression parser already prevents this case.
|
||||
if (name.empty())
|
||||
return nullptr;
|
||||
std::shared_ptr<ControlState>& variable = m_variables[name];
|
||||
// If new, make a shared ptr
|
||||
if (!variable)
|
||||
{
|
||||
variable = std::make_shared<ControlState>();
|
||||
}
|
||||
return variable;
|
||||
}
|
||||
|
||||
void ControlEnvironment::CleanUnusedVariables()
|
||||
{
|
||||
for (auto it = m_variables.begin(); it != m_variables.end();)
|
||||
{
|
||||
// Don't count ourselves as reference
|
||||
if (it->second.use_count() <= 1)
|
||||
m_variables.erase(it++);
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
ParseResult ParseResult::MakeEmptyResult()
|
||||
|
@ -785,7 +810,10 @@ private:
|
|||
}
|
||||
case TOK_VARIABLE:
|
||||
{
|
||||
return ParseResult::MakeSuccessfulResult(std::make_unique<VariableExpression>(tok.data));
|
||||
if (tok.data.empty())
|
||||
return ParseResult::MakeErrorResult(tok, _trans("Expected variable name."));
|
||||
else
|
||||
return ParseResult::MakeSuccessfulResult(std::make_unique<VariableExpression>(tok.data));
|
||||
}
|
||||
case TOK_LPAREN:
|
||||
{
|
||||
|
|
|
@ -143,7 +143,7 @@ public:
|
|||
class ControlEnvironment
|
||||
{
|
||||
public:
|
||||
using VariableContainer = std::map<std::string, ControlState>;
|
||||
using VariableContainer = std::map<std::string, std::shared_ptr<ControlState>>;
|
||||
|
||||
ControlEnvironment(const Core::DeviceContainer& container_, const Core::DeviceQualifier& default_,
|
||||
VariableContainer& vars)
|
||||
|
@ -154,7 +154,10 @@ public:
|
|||
std::shared_ptr<Core::Device> FindDevice(ControlQualifier qualifier) const;
|
||||
Core::Device::Input* FindInput(ControlQualifier qualifier) const;
|
||||
Core::Device::Output* FindOutput(ControlQualifier qualifier) const;
|
||||
ControlState* GetVariablePtr(const std::string& name);
|
||||
// Returns an existing variable by the specified name if already existing. Creates it otherwise.
|
||||
std::shared_ptr<ControlState> GetVariablePtr(const std::string& name);
|
||||
|
||||
void CleanUnusedVariables();
|
||||
|
||||
private:
|
||||
VariableContainer& m_variables;
|
||||
|
|
|
@ -44,6 +44,8 @@ void EmulatedController::UpdateReferences(const ControllerInterface& devi)
|
|||
ciface::ExpressionParser::ControlEnvironment env(devi, GetDefaultDevice(), m_expression_vars);
|
||||
|
||||
UpdateReferences(env);
|
||||
|
||||
env.CleanUnusedVariables();
|
||||
}
|
||||
|
||||
void EmulatedController::UpdateReferences(ciface::ExpressionParser::ControlEnvironment& env)
|
||||
|
@ -75,7 +77,27 @@ void EmulatedController::UpdateSingleControlReference(const ControllerInterface&
|
|||
ControlReference* ref)
|
||||
{
|
||||
ciface::ExpressionParser::ControlEnvironment env(devi, GetDefaultDevice(), m_expression_vars);
|
||||
|
||||
ref->UpdateReference(env);
|
||||
|
||||
env.CleanUnusedVariables();
|
||||
}
|
||||
|
||||
const ciface::ExpressionParser::ControlEnvironment::VariableContainer&
|
||||
EmulatedController::GetExpressionVariables() const
|
||||
{
|
||||
return m_expression_vars;
|
||||
}
|
||||
|
||||
void EmulatedController::ResetExpressionVariables()
|
||||
{
|
||||
for (auto& var : m_expression_vars)
|
||||
{
|
||||
if (var.second)
|
||||
{
|
||||
*var.second = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool EmulatedController::IsDefaultDeviceConnected() const
|
||||
|
|
|
@ -191,6 +191,11 @@ public:
|
|||
// which happens while handling a hotplug event because a control reference's State()
|
||||
// could be called before we have finished updating the reference.
|
||||
[[nodiscard]] static std::unique_lock<std::recursive_mutex> GetStateLock();
|
||||
const ciface::ExpressionParser::ControlEnvironment::VariableContainer&
|
||||
GetExpressionVariables() const;
|
||||
|
||||
// Resets the values while keeping the list.
|
||||
void ResetExpressionVariables();
|
||||
|
||||
std::vector<std::unique_ptr<ControlGroup>> groups;
|
||||
|
||||
|
@ -218,7 +223,8 @@ public:
|
|||
}
|
||||
|
||||
protected:
|
||||
// TODO: Wiimote attachment has its own member that isn't being used.
|
||||
// TODO: Wiimote attachments actually end up using their parent controller value for this,
|
||||
// so theirs won't be used (and thus shouldn't even exist).
|
||||
ciface::ExpressionParser::ControlEnvironment::VariableContainer m_expression_vars;
|
||||
|
||||
void UpdateReferences(ciface::ExpressionParser::ControlEnvironment& env);
|
||||
|
|
Loading…
Reference in New Issue