From d779ced27f02c88eaf799f05d4de3bd0944679bc Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 11 Sep 2022 15:30:44 +0200 Subject: [PATCH] Add macro condition "Variable" --- CMakeLists.txt | 2 + data/locale/en-US.ini | 8 + src/macro-core/macro-condition-variable.cpp | 267 ++++++++++++++++++++ src/macro-core/macro-condition-variable.hpp | 84 ++++++ 4 files changed, 361 insertions(+) create mode 100644 src/macro-core/macro-condition-variable.cpp create mode 100644 src/macro-core/macro-condition-variable.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index a0977ca3..36641e35 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -205,6 +205,8 @@ target_sources( src/macro-core/macro-condition-timer.hpp src/macro-core/macro-condition-transition.cpp src/macro-core/macro-condition-transition.hpp + src/macro-core/macro-condition-variable.cpp + src/macro-core/macro-condition-variable.hpp src/macro-core/macro-condition-virtual-cam.cpp src/macro-core/macro-condition-virtual-cam.hpp src/macro-core/macro-condition-websocket.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 32986d17..9f250b99 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -344,6 +344,14 @@ AdvSceneSwitcher.condition.websocket.type.event="Event" AdvSceneSwitcher.condition.websocket.useRegex="Use regular expressions" AdvSceneSwitcher.condition.websocket.entry.request="{{type}} was received:" AdvSceneSwitcher.condition.websocket.entry.event="{{type}} was received from {{connection}}:" +AdvSceneSwitcher.condition.variable="Variable" +AdvSceneSwitcher.condition.variable.type.compare="equals" +AdvSceneSwitcher.condition.variable.type.empty="is empty" +AdvSceneSwitcher.condition.variable.type.number="is a number" +AdvSceneSwitcher.condition.variable.type.lessThan="is less than" +AdvSceneSwitcher.condition.variable.type.greaterThan="is greater than" +AdvSceneSwitcher.condition.variable.regex="Use regular expressions" +AdvSceneSwitcher.condition.variable.entry="{{variables}}{{conditions}}{{strValue}}{{numValue}}" ; Macro Actions AdvSceneSwitcher.action.switchScene="Switch scene" diff --git a/src/macro-core/macro-condition-variable.cpp b/src/macro-core/macro-condition-variable.cpp new file mode 100644 index 00000000..f553a542 --- /dev/null +++ b/src/macro-core/macro-condition-variable.cpp @@ -0,0 +1,267 @@ +#include "macro-condition-edit.hpp" +#include "macro-condition-variable.hpp" +#include "utility.hpp" +#include "advanced-scene-switcher.hpp" + +#include + +const std::string MacroConditionVariable::id = "variable"; + +bool MacroConditionVariable::_registered = MacroConditionFactory::Register( + MacroConditionVariable::id, + {MacroConditionVariable::Create, MacroConditionVariableEdit::Create, + "AdvSceneSwitcher.condition.variable"}); + +const static std::map + conditionTypes = { + {MacroConditionVariable::Type::EQUALS, + "AdvSceneSwitcher.condition.variable.type.compare"}, + {MacroConditionVariable::Type::IS_EMPTY, + "AdvSceneSwitcher.condition.variable.type.empty"}, + {MacroConditionVariable::Type::IS_NUMBER, + "AdvSceneSwitcher.condition.variable.type.number"}, + {MacroConditionVariable::Type::LESS_THAN, + "AdvSceneSwitcher.condition.variable.type.lessThan"}, + {MacroConditionVariable::Type::GREATER_THAN, + "AdvSceneSwitcher.condition.variable.type.greaterThan"}, +}; + +static bool isNumber(const std::string &str) +{ + char *end = nullptr; + double val = strtod(str.c_str(), &end); + return end != str.c_str() && *end == '\0' && val != HUGE_VAL; +} + +static bool compareNumber(const std::string &var, double value, bool less) +{ + char *end = nullptr; + double varVal = strtod(var.c_str(), &end); + if (end != var.c_str() && *end == '\0' && varVal != HUGE_VAL) { + if (less) { + return varVal < value; + } else { + return varVal > value; + } + } + return false; +} + +bool MacroConditionVariable::Compare(const Variable &var) const +{ + if (_regex) { + try { + std::regex expr(_strValue); + return std::regex_match(var.Value(), expr); + } catch (const std::regex_error &) { + return false; + } + } else { + return _strValue == var.Value(); + } +} + +bool MacroConditionVariable::CheckCondition() +{ + auto var = GetVariableByName(_variableName); + if (!var) { + return false; + } + + switch (_type) { + case MacroConditionVariable::Type::EQUALS: + return Compare(*var); + case MacroConditionVariable::Type::IS_EMPTY: + return var->Value().empty(); + case MacroConditionVariable::Type::IS_NUMBER: + return isNumber(var->Value()); + case MacroConditionVariable::Type::LESS_THAN: + return compareNumber(var->Value(), _numValue, true); + case MacroConditionVariable::Type::GREATER_THAN: + return compareNumber(var->Value(), _numValue, false); + } + + return false; +} + +bool MacroConditionVariable::Save(obs_data_t *obj) +{ + MacroCondition::Save(obj); + obs_data_set_string(obj, "variableName", _variableName.c_str()); + obs_data_set_string(obj, "strValue", _strValue.c_str()); + obs_data_set_double(obj, "numValue", _numValue); + obs_data_set_int(obj, "condition", static_cast(_type)); + obs_data_set_bool(obj, "regex", _regex); + return true; +} + +bool MacroConditionVariable::Load(obs_data_t *obj) +{ + MacroCondition::Load(obj); + _variableName = obs_data_get_string(obj, "variableName"); + _strValue = obs_data_get_string(obj, "strValue"); + _numValue = obs_data_get_double(obj, "numValue"); + _type = static_cast(obs_data_get_int(obj, "condition")); + _regex = obs_data_get_bool(obj, "regex"); + return true; +} + +std::string MacroConditionVariable::GetShortDesc() +{ + return _variableName; +} + +static inline void populateConditionSelection(QComboBox *list) +{ + for (auto entry : conditionTypes) { + list->addItem(obs_module_text(entry.second.c_str())); + } +} + +MacroConditionVariableEdit::MacroConditionVariableEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent), + _variables(new VariableSelection(this)), + _conditions(new QComboBox()), + _strValue(new QLineEdit()), + _numValue(new QDoubleSpinBox()), + _regex(new QCheckBox( + obs_module_text("AdvSceneSwitcher.condition.variable.regex"))) +{ + _numValue->setMinimum(-9999999999); + _numValue->setMaximum(9999999999); + populateConditionSelection(_conditions); + + QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)), + this, SLOT(VariableChanged(const QString &))); + QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this, + SLOT(ConditionChanged(int))); + QWidget::connect(_strValue, SIGNAL(editingFinished()), this, + SLOT(StrValueChanged())); + QWidget::connect(_numValue, SIGNAL(valueChanged(double)), this, + SLOT(NumValueChanged(double))); + QWidget::connect(_regex, SIGNAL(stateChanged(int)), this, + SLOT(RegexChanged(int))); + + std::unordered_map widgetPlaceholders = { + {"{{variables}}", _variables}, {"{{conditions}}", _conditions}, + {"{{strValue}}", _strValue}, {"{{numValue}}", _numValue}, + {"{{regex}}", _regex}, + }; + + QHBoxLayout *layout = new QHBoxLayout; + placeWidgets( + obs_module_text("AdvSceneSwitcher.condition.variable.entry"), + layout, widgetPlaceholders); + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addLayout(layout); + mainLayout->addWidget(_regex); + setLayout(mainLayout); + + _entryData = entryData; + UpdateEntryData(); + _loading = false; +} + +void MacroConditionVariableEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + + _variables->SetVariable(_entryData->_variableName); + _conditions->setCurrentIndex(static_cast(_entryData->_type)); + _strValue->setText(QString::fromStdString(_entryData->_strValue)); + _numValue->setValue(_entryData->_numValue); + _regex->setChecked(_entryData->_regex); + SetWidgetVisibility(); +} + +void MacroConditionVariableEdit::VariableChanged(const QString &text) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_variableName = text.toStdString(); +} + +void MacroConditionVariableEdit::ConditionChanged(int value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_type = static_cast(value); + SetWidgetVisibility(); +} + +void MacroConditionVariableEdit::StrValueChanged() +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_strValue = _strValue->text().toStdString(); +} + +void MacroConditionVariableEdit::NumValueChanged(double val) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_numValue = val; +} + +void MacroConditionVariableEdit::RegexChanged(int state) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_regex = state; +} + +void MacroConditionVariableEdit::SetWidgetVisibility() +{ + if (!_entryData) { + return; + } + + switch (_entryData->_type) { + case MacroConditionVariable::Type::EQUALS: + _regex->show(); + _strValue->show(); + _numValue->hide(); + break; + case MacroConditionVariable::Type::IS_EMPTY: + _regex->hide(); + _strValue->hide(); + _numValue->hide(); + break; + case MacroConditionVariable::Type::IS_NUMBER: + _regex->hide(); + _strValue->hide(); + _numValue->hide(); + break; + case MacroConditionVariable::Type::LESS_THAN: + _regex->hide(); + _strValue->hide(); + _numValue->show(); + break; + case MacroConditionVariable::Type::GREATER_THAN: + _regex->hide(); + _strValue->hide(); + _numValue->show(); + break; + } + + adjustSize(); + updateGeometry(); +} diff --git a/src/macro-core/macro-condition-variable.hpp b/src/macro-core/macro-condition-variable.hpp new file mode 100644 index 00000000..c85ce893 --- /dev/null +++ b/src/macro-core/macro-condition-variable.hpp @@ -0,0 +1,84 @@ +#pragma once +#include "macro.hpp" +#include "variable.hpp" + +#include +#include +#include +#include + +class MacroConditionVariable : public MacroCondition { +public: + MacroConditionVariable(Macro *m) : MacroCondition(m) {} + bool CheckCondition(); + bool Save(obs_data_t *obj); + bool Load(obs_data_t *obj); + std::string GetShortDesc(); + std::string GetId() { return id; }; + static std::shared_ptr Create(Macro *m) + { + return std::make_shared(m); + } + + enum class Type { + EQUALS, + IS_EMPTY, + IS_NUMBER, + LESS_THAN, + GREATER_THAN, + //... + }; + + Type _type = Type::EQUALS; + std::string _variableName = ""; + std::string _strValue = ""; + double _numValue = 0; + bool _regex = false; + +private: + bool Compare(const Variable &) const; + + static bool _registered; + static const std::string id; +}; + +class MacroConditionVariableEdit : public QWidget { + Q_OBJECT + +public: + MacroConditionVariableEdit( + QWidget *parent, + std::shared_ptr cond = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr cond) + { + return new MacroConditionVariableEdit( + parent, + std::dynamic_pointer_cast( + cond)); + } + +private slots: + void VariableChanged(const QString &); + void ConditionChanged(int); + void StrValueChanged(); + void NumValueChanged(double); + void RegexChanged(int state); + +signals: + void HeaderInfoChanged(const QString &); + +protected: + VariableSelection *_variables; + QComboBox *_conditions; + QLineEdit *_strValue; + QDoubleSpinBox *_numValue; + QCheckBox *_regex; + std::shared_ptr _entryData; + +private: + void SetWidgetVisibility(); + + bool _loading = true; +};