diff --git a/CMakeLists.txt b/CMakeLists.txt index fff481f1..b37fe52f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -113,6 +113,22 @@ target_sources( lib/macro/macro-action-macro.hpp lib/macro/macro-action-queue.cpp lib/macro/macro-action-queue.hpp + lib/macro/macro-action-variable-basic.cpp + lib/macro/macro-action-variable-basic.hpp + lib/macro/macro-action-variable-group.cpp + lib/macro/macro-action-variable-group.hpp + lib/macro/macro-action-variable-input.cpp + lib/macro/macro-action-variable-input.hpp + lib/macro/macro-action-variable-json.cpp + lib/macro/macro-action-variable-json.hpp + lib/macro/macro-action-variable-macro.cpp + lib/macro/macro-action-variable-macro.hpp + lib/macro/macro-action-variable-math.cpp + lib/macro/macro-action-variable-math.hpp + lib/macro/macro-action-variable-scene.cpp + lib/macro/macro-action-variable-scene.hpp + lib/macro/macro-action-variable-string.cpp + lib/macro/macro-action-variable-string.hpp lib/macro/macro-action-variable.cpp lib/macro/macro-action-variable.hpp lib/macro/macro-action.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 651d96ab..eb8bc662 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -1419,7 +1419,19 @@ AdvSceneSwitcher.action.variable.invalidSelection="Invalid selection!" AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!" AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!" AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:" -AdvSceneSwitcher.action.variable.layout.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}{{caseType}}{{jsonQuery}}{{jsonQueryHelp}}{{jsonIndex}}" +AdvSceneSwitcher.action.variable.layout.default="{{actions}}{{variables}}" +AdvSceneSwitcher.action.variable.layout.strValue="{{actions}}{{variables}}{{strValue}}" +AdvSceneSwitcher.action.variable.layout.variable2="{{actions}}{{variables}}{{variables2}}" +AdvSceneSwitcher.action.variable.layout.changeCase="{{actions}}{{variables}}{{caseType}}" +AdvSceneSwitcher.action.variable.layout.queryJson="{{actions}}{{variables}}{{jsonQuery}}{{jsonQueryHelp}}" +AdvSceneSwitcher.action.variable.layout.accessJsonArray="{{actions}}{{variables}}{{jsonIndex}}" +AdvSceneSwitcher.action.variable.layout.setToTempvar="{{actions}}{{variables}}{{tempVars}}{{tempVarsHelp}}" +AdvSceneSwitcher.action.variable.layout.segmentValue="{{actions}}{{variables}}{{segmentIndex}}" +AdvSceneSwitcher.action.variable.layout.mathExpression="{{actions}}{{variables}}{{mathExpression}}" +AdvSceneSwitcher.action.variable.layout.numValue="{{actions}}{{variables}}{{numValue}}" +AdvSceneSwitcher.action.variable.layout.environmentVariable="{{actions}}{{variables}}{{envVariableName}}" +AdvSceneSwitcher.action.variable.layout.sceneItemName="{{actions}}{{variables}}{{scenes}}{{sceneItemIndex}}" +AdvSceneSwitcher.action.variable.layout.sceneItemCount="{{actions}}{{variables}}{{scenes}}" AdvSceneSwitcher.action.variable.layout.copy="{{actions}}{{variables2}}to{{variables}}" AdvSceneSwitcher.action.variable.layout.pad="{{actions}}of{{variables}}to length{{stringLength}}by adding{{paddingCharSelection}}to the{{direction}}" AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}of{{variables}}to length{{stringLength}}by removing characters from the{{direction}}" diff --git a/data/locale/zh-CN.ini b/data/locale/zh-CN.ini index ffb45823..7e8c199c 100644 --- a/data/locale/zh-CN.ini +++ b/data/locale/zh-CN.ini @@ -1374,7 +1374,6 @@ AdvSceneSwitcher.action.variable.invalidSelection="无效的选择!" AdvSceneSwitcher.action.variable.actionNoVariableSupport="不支持从 %1 操作获取变量值!" AdvSceneSwitcher.action.variable.conditionNoVariableSupport="不支持从 %1 条件获取变量值!" AdvSceneSwitcher.action.variable.currentSegmentValue="当前值:" -AdvSceneSwitcher.action.variable.layout.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}{{caseType}}{{jsonQuery}}{{jsonQueryHelp}}{{jsonIndex}}" AdvSceneSwitcher.action.variable.layout.copy="{{actions}}{{variables2}}到{{variables}}" AdvSceneSwitcher.action.variable.layout.pad="{{actions}}{{variables}}到长度{{stringLength}}通过向{{direction}}添加{{paddingCharSelection}}" AdvSceneSwitcher.action.variable.layout.truncate="{{actions}}{{variables}}到长度{{stringLength}}通过从{{direction}}删除字符" diff --git a/lib/macro/macro-action-variable-basic.cpp b/lib/macro/macro-action-variable-basic.cpp new file mode 100644 index 00000000..2b37b7ff --- /dev/null +++ b/lib/macro/macro-action-variable-basic.cpp @@ -0,0 +1,99 @@ +#include "macro-action-variable-basic.hpp" + +namespace advss { + +using Action = MacroActionVariable::Action; + +static bool registered = RegisterVariableActionGroup( + {0, + { + {Action::SET_VALUE, "AdvSceneSwitcher.action.variable.type.set", + "AdvSceneSwitcher.action.variable.layout.strValue", true}, + {Action::COPY_VAR, "AdvSceneSwitcher.action.variable.type.copy", + "AdvSceneSwitcher.action.variable.layout.copy"}, + {Action::APPEND, "AdvSceneSwitcher.action.variable.type.append", + "AdvSceneSwitcher.action.variable.layout.strValue", true}, + {Action::APPEND_VAR, + "AdvSceneSwitcher.action.variable.type.appendVar", + "AdvSceneSwitcher.action.variable.layout.variable2"}, + {Action::SWAP_VALUES, + "AdvSceneSwitcher.action.variable.type.swapValues", + "AdvSceneSwitcher.action.variable.layout.variable2"}, + }, + []() { return std::make_unique(); }, + VariableActionBasicEdit::Create}); + +static void apppend(Variable &var, const std::string &value) +{ + auto currentValue = var.Value(); + var.SetValue(currentValue + value); +} + +std::unique_ptr VariableActionBasic::Copy() const +{ + return std::make_unique(*this); +} + +bool VariableActionBasic::Perform(MacroActionVariable &action, + MacroActionVariable::Action type, + Variable &var) +{ + switch (type) { + case Action::SET_VALUE: + var.SetValue(action._strValue); + break; + case Action::APPEND: + apppend(var, action._strValue); + break; + case Action::COPY_VAR: { + auto var2 = action._variable2.lock(); + if (!var2) { + return true; + } + var.SetValue(var2->Value()); + break; + } + case Action::APPEND_VAR: { + auto var2 = action._variable2.lock(); + if (!var2) { + return true; + } + apppend(var, var2->Value()); + break; + } + case Action::SWAP_VALUES: { + auto var2 = action._variable2.lock(); + if (!var2) { + return true; + } + auto tempValue = var.Value(); + var.SetValue(var2->Value()); + var2->SetValue(tempValue); + break; + } + default: + break; + } + return true; +} + +VariableActionBasicEdit::VariableActionBasicEdit( + QWidget *parent, std::shared_ptr entryData) + : VariableActionGroupEdit(parent, entryData) +{ + _loading = false; +} + +VariableActionGroupEdit * +VariableActionBasicEdit::Create(QWidget *parent, + std::shared_ptr entryData) +{ + return new VariableActionBasicEdit(parent, entryData); +} + +void VariableActionBasicEdit::SetWidgetVisibility(MacroActionVariable::Action) +{ + hide(); +} + +} // namespace advss diff --git a/lib/macro/macro-action-variable-basic.hpp b/lib/macro/macro-action-variable-basic.hpp new file mode 100644 index 00000000..a48c54d4 --- /dev/null +++ b/lib/macro/macro-action-variable-basic.hpp @@ -0,0 +1,25 @@ +#pragma once +#include "macro-action-variable-group.hpp" + +namespace advss { + +class VariableActionBasic : public VariableActionGroup { +public: + std::unique_ptr Copy() const override; + bool Perform(MacroActionVariable &, MacroActionVariable::Action, + Variable &) override; +}; + +class VariableActionBasicEdit : public VariableActionGroupEdit { + Q_OBJECT + +public: + VariableActionBasicEdit(QWidget *parent, + std::shared_ptr entryData); + static VariableActionGroupEdit * + Create(QWidget *parent, std::shared_ptr entryData); + void UpdateEntryData() override {} + void SetWidgetVisibility(MacroActionVariable::Action) override; +}; + +} // namespace advss diff --git a/lib/macro/macro-action-variable-group.cpp b/lib/macro/macro-action-variable-group.cpp new file mode 100644 index 00000000..e6ffcfcc --- /dev/null +++ b/lib/macro/macro-action-variable-group.cpp @@ -0,0 +1,83 @@ +#include "macro-action-variable-group.hpp" +#include "log-helper.hpp" + +#include + +namespace advss { + +static std::vector &getGroups() +{ + static std::vector groups; + return groups; +} + +bool RegisterVariableActionGroup(const VariableActionGroupInfo &info) +{ + for (const auto &option : info.options) { + if (GetVariableActionGroupIndex(option.action) != -1) { + blog(LOG_WARNING, + "variable action option %d already registered", + static_cast(option.action)); + return false; + } + } + + auto &groups = getGroups(); + auto it = std::upper_bound(groups.begin(), groups.end(), info, + [](const VariableActionGroupInfo &a, + const VariableActionGroupInfo &b) { + return a.order < b.order; + }); + groups.insert(it, info); + return true; +} + +const std::vector &GetVariableActionGroups() +{ + return getGroups(); +} + +int GetVariableActionGroupIndex(MacroActionVariable::Action action) +{ + const auto &groups = getGroups(); + for (size_t idx = 0; idx < groups.size(); idx++) { + for (const auto &option : groups[idx].options) { + if (option.action == action) { + return static_cast(idx); + } + } + } + return -1; +} + +const VariableActionOption * +GetVariableActionOption(MacroActionVariable::Action action) +{ + for (const auto &group : getGroups()) { + for (const auto &option : group.options) { + if (option.action == action) { + return &option; + } + } + } + return nullptr; +} + +VariableActionGroupEdit::VariableActionGroupEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent), + _entryData(entryData) +{ +} + +void VariableActionGroupEdit::UpdateSize() +{ + adjustSize(); + updateGeometry(); + if (auto parent = parentWidget()) { + parent->adjustSize(); + parent->updateGeometry(); + } +} + +} // namespace advss diff --git a/lib/macro/macro-action-variable-group.hpp b/lib/macro/macro-action-variable-group.hpp new file mode 100644 index 00000000..7a949d3e --- /dev/null +++ b/lib/macro/macro-action-variable-group.hpp @@ -0,0 +1,73 @@ +#pragma once +#include "macro-action-variable.hpp" + +#include + +#include +#include +#include +#include +#include + +namespace advss { + +struct VariableActionOption { + MacroActionVariable::Action action; + const char *label; + const char *layout = "AdvSceneSwitcher.action.variable.layout.default"; + bool expandInlineRow = false; +}; + +class VariableActionGroup { +public: + virtual ~VariableActionGroup() = default; + virtual std::unique_ptr Copy() const = 0; + virtual void Save(obs_data_t *, const MacroActionVariable &) const {} + virtual void Load(obs_data_t *, MacroActionVariable &) {} + virtual void PostLoad(MacroActionVariable &) {} + virtual void ResolveVariables() {} + virtual std::vector GetTempVarRefs() const + { + return {}; + } + // Returning false aborts the macro + virtual bool Perform(MacroActionVariable &, MacroActionVariable::Action, + Variable &) = 0; +}; + +class VariableActionGroupEdit : public QWidget { + Q_OBJECT + +public: + VariableActionGroupEdit(QWidget *parent, + std::shared_ptr entryData); + virtual std::unordered_map GetInlineWidgets() + { + return {}; + } + virtual void UpdateEntryData() = 0; + virtual void SetWidgetVisibility(MacroActionVariable::Action) = 0; + +protected: + void UpdateSize(); + + std::shared_ptr _entryData; + bool _loading = true; +}; + +struct VariableActionGroupInfo { + int order; + std::vector options; + std::function()> createData; + std::function)> + createEdit; +}; + +bool RegisterVariableActionGroup(const VariableActionGroupInfo &); +const std::vector &GetVariableActionGroups(); +int GetVariableActionGroupIndex(MacroActionVariable::Action); +const VariableActionOption * + GetVariableActionOption(MacroActionVariable::Action); + +} // namespace advss diff --git a/lib/macro/macro-action-variable-input.cpp b/lib/macro/macro-action-variable-input.cpp new file mode 100644 index 00000000..fd1e83af --- /dev/null +++ b/lib/macro/macro-action-variable-input.cpp @@ -0,0 +1,324 @@ +#include "macro-action-variable-input.hpp" +#include "layout-helpers.hpp" +#include "macro.hpp" +#include "non-modal-dialog.hpp" + +#include + +namespace advss { + +using Action = MacroActionVariable::Action; + +static bool registered = RegisterVariableActionGroup( + {50, + { + {Action::USER_INPUT, + "AdvSceneSwitcher.action.variable.type.askForValue"}, + {Action::ENV_VARIABLE, + "AdvSceneSwitcher.action.variable.type.environmentVariable", + "AdvSceneSwitcher.action.variable.layout.environmentVariable", + true}, + }, + []() { return std::make_unique(); }, + VariableActionInputEdit::Create}); + +struct AskForInputParams { + AskForInputParams(const QString &prompt_, const QString &placeholder_, + ValueInputType type_, const QStringList &options_) + : prompt(prompt_), + placeholder(placeholder_), + type(type_), + options(options_){}; + QString prompt; + QString placeholder; + ValueInputType type; + QStringList options; + std::optional result; + std::atomic_bool resultReady = {false}; +}; + +static void askForInput(void *param) +{ + auto parameters = + *static_cast *>(param); + NonModalMessageDialog *dialog = nullptr; + if (parameters->type == ValueInputType::String) { + dialog = new NonModalMessageDialog( + parameters->prompt, NonModalMessageDialog::Type::INPUT, + false); + dialog->SetInput(parameters->placeholder); + } else { + dialog = new NonModalMessageDialog( + parameters->prompt, parameters->type, + parameters->placeholder.toStdString(), + parameters->options); + } + parameters->result = dialog->GetInput(); + parameters->resultReady = true; +} + +std::unique_ptr VariableActionInput::Copy() const +{ + return std::make_unique(*this); +} + +void VariableActionInput::Save(obs_data_t *obj, + const MacroActionVariable &) const +{ + obs_data_set_bool(obj, "useCustomPrompt", _useCustomPrompt); + _inputPrompt.Save(obj, "inputPrompt"); + obs_data_set_bool(obj, "useInputPlaceholder", _useInputPlaceholder); + _inputPlaceholder.Save(obj, "inputPlaceholder"); + obs_data_set_int(obj, "inputType", static_cast(_inputType)); + _inputOptions.Save(obj, "inputOptions", "value"); + _envVariableName.Save(obj, "environmentVariableName"); +} + +void VariableActionInput::Load(obs_data_t *obj, MacroActionVariable &) +{ + _useCustomPrompt = obs_data_get_bool(obj, "useCustomPrompt"); + _inputPrompt.Load(obj, "inputPrompt"); + _useInputPlaceholder = obs_data_get_bool(obj, "useInputPlaceholder"); + _inputPlaceholder.Load(obj, "inputPlaceholder"); + obs_data_set_default_int(obj, "inputType", + static_cast(ValueInputType::String)); + _inputType = + static_cast(obs_data_get_int(obj, "inputType")); + _inputOptions.Load(obj, "inputOptions", "value"); + _envVariableName.Load(obj, "environmentVariableName"); +} + +void VariableActionInput::ResolveVariables() +{ + _inputPrompt.ResolveVariables(); + _inputPlaceholder.ResolveVariables(); + _inputOptions.ResolveVariables(); + _envVariableName.ResolveVariables(); +} + +bool VariableActionInput::AskForUserInput(MacroActionVariable &action, + Variable &var) +{ + QStringList options; + for (const auto &option : _inputOptions) { + options << QString::fromStdString(option); + } + auto params = std::make_shared( + _useCustomPrompt + ? QString::fromStdString(_inputPrompt) + : QString(obs_module_text( + "AdvSceneSwitcher.action.variable.askForValuePromptDefault")) + .arg(QString::fromStdString(var.Name())), + _useCustomPrompt && _useInputPlaceholder + ? QString::fromStdString(_inputPlaceholder) + : "", + _inputType, options); + obs_queue_task(OBS_TASK_UI, askForInput, ¶ms, false); + while (!params->resultReady) { + if (action.GetMacro()->GetStop()) { + return false; + } + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + if (!params->result.has_value()) { + return true; + } + var.SetValue(*params->result); + return true; +} + +bool VariableActionInput::Perform(MacroActionVariable &action, + MacroActionVariable::Action type, + Variable &var) +{ + switch (type) { + case Action::USER_INPUT: + return AskForUserInput(action, var); + case Action::ENV_VARIABLE: { + auto value = std::getenv(_envVariableName.c_str()); + if (value) { + var.SetValue(value); + } + return true; + } + default: + return true; + } +} + +VariableActionInputEdit::VariableActionInputEdit( + QWidget *parent, std::shared_ptr entryData) + : VariableActionGroupEdit(parent, entryData), + _data(entryData ? entryData->GetGroup() + : nullptr), + _promptLayout(new QHBoxLayout()), + _useCustomPrompt(new QCheckBox(this)), + _inputPrompt(new VariableLineEdit(this)), + _placeholderLayout(new QHBoxLayout()), + _useInputPlaceholder(new QCheckBox(this)), + _inputPlaceholder(new VariableLineEdit(this)), + _inputTypeLayout(new QHBoxLayout()), + _inputType(new QComboBox(this)), + _inputOptions(new StringListEdit(this)), + _envVariable(new VariableLineEdit(this)) +{ + _inputPrompt->setSizePolicy(QSizePolicy::MinimumExpanding, + QSizePolicy::Preferred); + _inputOptions->SetMaxStringSize(99999999); + PopulateValueInputTypeSelection(_inputType); + + QWidget::connect(_useCustomPrompt, SIGNAL(stateChanged(int)), this, + SLOT(UseCustomPromptChanged(int))); + QWidget::connect(_inputPrompt, SIGNAL(editingFinished()), this, + SLOT(InputPromptChanged())); + QWidget::connect(_useInputPlaceholder, SIGNAL(stateChanged(int)), this, + SLOT(UseInputPlaceholderChanged(int))); + QWidget::connect(_inputPlaceholder, SIGNAL(editingFinished()), this, + SLOT(InputPlaceholderChanged())); + QWidget::connect(_inputType, SIGNAL(currentIndexChanged(int)), this, + SLOT(InputTypeChanged(int))); + QWidget::connect(_inputOptions, + SIGNAL(StringListChanged(const StringList &)), this, + SLOT(InputOptionsChanged(const StringList &))); + QWidget::connect(_envVariable, SIGNAL(editingFinished()), this, + SLOT(EnvVariableChanged())); + + const std::unordered_map widgetPlaceholders = { + {"{{useCustomPrompt}}", _useCustomPrompt}, + {"{{inputPrompt}}", _inputPrompt}, + {"{{useInputPlaceholder}}", _useInputPlaceholder}, + {"{{inputPlaceholder}}", _inputPlaceholder}, + {"{{inputType}}", _inputType}, + }; + PlaceWidgets( + obs_module_text( + "AdvSceneSwitcher.action.variable.layout.userInput.type"), + _inputTypeLayout, widgetPlaceholders); + PlaceWidgets( + obs_module_text( + "AdvSceneSwitcher.action.variable.layout.userInput.customPrompt"), + _promptLayout, widgetPlaceholders); + PlaceWidgets( + obs_module_text( + "AdvSceneSwitcher.action.variable.layout.userInput.placeholder"), + _placeholderLayout, widgetPlaceholders); + + auto layout = new QVBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + layout->addLayout(_inputTypeLayout); + layout->addWidget(_inputOptions); + layout->addLayout(_promptLayout); + layout->addLayout(_placeholderLayout); + setLayout(layout); +} + +VariableActionGroupEdit * +VariableActionInputEdit::Create(QWidget *parent, + std::shared_ptr entryData) +{ + return new VariableActionInputEdit(parent, entryData); +} + +std::unordered_map +VariableActionInputEdit::GetInlineWidgets() +{ + return {{"{{envVariableName}}", _envVariable}}; +} + +void VariableActionInputEdit::UpdateEntryData() +{ + if (!_data) { + return; + } + + _useCustomPrompt->setChecked(_data->_useCustomPrompt); + _inputPrompt->setText(_data->_inputPrompt); + _useInputPlaceholder->setChecked(_data->_useInputPlaceholder); + _inputPlaceholder->setText(_data->_inputPlaceholder); + _inputType->setCurrentIndex( + _inputType->findData(static_cast(_data->_inputType))); + _inputOptions->SetStringList(_data->_inputOptions); + _envVariable->setText(_data->_envVariableName); + _loading = false; +} + +void VariableActionInputEdit::SetWidgetVisibility( + MacroActionVariable::Action action) +{ + if (!_data) { + return; + } + + const bool isUserInput = action == Action::USER_INPUT; + SetLayoutVisible(_inputTypeLayout, isUserInput); + _inputOptions->setVisible( + isUserInput && _data->_inputType == ValueInputType::Selection); + SetLayoutVisible(_promptLayout, isUserInput); + _inputPrompt->setVisible(isUserInput && _data->_useCustomPrompt); + if (_data->_useCustomPrompt) { + RemoveStretchIfPresent(_promptLayout); + } else { + AddStretchIfNecessary(_promptLayout); + } + SetLayoutVisible(_placeholderLayout, + isUserInput && _data->_useCustomPrompt); + _useInputPlaceholder->setVisible(isUserInput && + _data->_useCustomPrompt); + _inputPlaceholder->setVisible(isUserInput && _data->_useCustomPrompt && + _data->_useInputPlaceholder); + if (_data->_useInputPlaceholder) { + RemoveStretchIfPresent(_placeholderLayout); + } else { + AddStretchIfNecessary(_placeholderLayout); + } + setVisible(isUserInput); + UpdateSize(); +} + +void VariableActionInputEdit::UseCustomPromptChanged(int value) +{ + GUARD_LOADING_AND_LOCK(); + _data->_useCustomPrompt = value; + SetWidgetVisibility(_entryData->_action); +} + +void VariableActionInputEdit::InputPromptChanged() +{ + GUARD_LOADING_AND_LOCK(); + _data->_inputPrompt = _inputPrompt->text().toStdString(); +} + +void VariableActionInputEdit::UseInputPlaceholderChanged(int value) +{ + GUARD_LOADING_AND_LOCK(); + _data->_useInputPlaceholder = value; + SetWidgetVisibility(_entryData->_action); +} + +void VariableActionInputEdit::InputPlaceholderChanged() +{ + GUARD_LOADING_AND_LOCK(); + _data->_inputPlaceholder = _inputPlaceholder->text().toStdString(); +} + +void VariableActionInputEdit::InputTypeChanged(int) +{ + GUARD_LOADING_AND_LOCK(); + _data->_inputType = + static_cast(_inputType->currentData().toInt()); + SetWidgetVisibility(_entryData->_action); +} + +void VariableActionInputEdit::InputOptionsChanged(const StringList &options) +{ + GUARD_LOADING_AND_LOCK(); + _data->_inputOptions = options; +} + +void VariableActionInputEdit::EnvVariableChanged() +{ + GUARD_LOADING_AND_LOCK(); + _data->_envVariableName = _envVariable->text().toStdString(); +} + +} // namespace advss diff --git a/lib/macro/macro-action-variable-input.hpp b/lib/macro/macro-action-variable-input.hpp new file mode 100644 index 00000000..ea8cdbd5 --- /dev/null +++ b/lib/macro/macro-action-variable-input.hpp @@ -0,0 +1,72 @@ +#pragma once +#include "macro-action-variable-group.hpp" +#include "string-list.hpp" +#include "value-input.hpp" +#include "variable-line-edit.hpp" + +namespace advss { + +class VariableActionInput : public VariableActionGroup { +public: + std::unique_ptr Copy() const override; + void Save(obs_data_t *, const MacroActionVariable &) const override; + void Load(obs_data_t *, MacroActionVariable &) override; + void ResolveVariables() override; + bool Perform(MacroActionVariable &, MacroActionVariable::Action, + Variable &) override; + + bool _useCustomPrompt = false; + StringVariable _inputPrompt = obs_module_text( + "AdvSceneSwitcher.action.variable.askForValuePrompt"); + bool _useInputPlaceholder = false; + StringVariable _inputPlaceholder = + obs_module_text("AdvSceneSwitcher.enterText"); + ValueInputType _inputType = ValueInputType::String; + StringList _inputOptions = {"value1", "value2", "value3"}; +#ifdef _WIN32 + StringVariable _envVariableName = "USERPROFILE"; +#else + StringVariable _envVariableName = "HOME"; +#endif + +private: + bool AskForUserInput(MacroActionVariable &, Variable &); +}; + +class VariableActionInputEdit : public VariableActionGroupEdit { + Q_OBJECT + +public: + VariableActionInputEdit(QWidget *parent, + std::shared_ptr entryData); + static VariableActionGroupEdit * + Create(QWidget *parent, std::shared_ptr entryData); + std::unordered_map GetInlineWidgets() override; + void UpdateEntryData() override; + void SetWidgetVisibility(MacroActionVariable::Action) override; + +private slots: + void UseCustomPromptChanged(int); + void InputPromptChanged(); + void UseInputPlaceholderChanged(int); + void InputPlaceholderChanged(); + void InputTypeChanged(int); + void InputOptionsChanged(const StringList &); + void EnvVariableChanged(); + +private: + VariableActionInput *_data; + + QHBoxLayout *_promptLayout; + QCheckBox *_useCustomPrompt; + VariableLineEdit *_inputPrompt; + QHBoxLayout *_placeholderLayout; + QCheckBox *_useInputPlaceholder; + VariableLineEdit *_inputPlaceholder; + QHBoxLayout *_inputTypeLayout; + QComboBox *_inputType; + StringListEdit *_inputOptions; + VariableLineEdit *_envVariable; +}; + +} // namespace advss diff --git a/lib/macro/macro-action-variable-json.cpp b/lib/macro/macro-action-variable-json.cpp new file mode 100644 index 00000000..5900c33a --- /dev/null +++ b/lib/macro/macro-action-variable-json.cpp @@ -0,0 +1,189 @@ +#include "macro-action-variable-json.hpp" +#include "json-helpers.hpp" + +namespace advss { + +using Action = MacroActionVariable::Action; + +static bool registered = RegisterVariableActionGroup( + {20, + { + {Action::EXTRACT_JSON, + "AdvSceneSwitcher.action.variable.type.extractJsonField", + "AdvSceneSwitcher.action.variable.layout.strValue", true}, + {Action::QUERY_JSON, + "AdvSceneSwitcher.action.variable.type.queryJson", + "AdvSceneSwitcher.action.variable.layout.queryJson", true}, + {Action::ARRAY_JSON, + "AdvSceneSwitcher.action.variable.type.accessJsonArray", + "AdvSceneSwitcher.action.variable.layout.accessJsonArray"}, + }, + []() { return std::make_unique(); }, + VariableActionJsonEdit::Create}); + +std::unique_ptr VariableActionJson::Copy() const +{ + return std::make_unique(*this); +} + +void VariableActionJson::Save(obs_data_t *obj, + const MacroActionVariable &) const +{ + _jsonQuery.Save(obj, "jsonQuery"); + _jsonIndex.Save(obj, "jsonIndex"); + obs_data_set_bool(obj, "jsonQueryExtractSingle", + _jsonQueryExtractSingle); +} + +void VariableActionJson::Load(obs_data_t *obj, MacroActionVariable &) +{ + _jsonQuery.Load(obj, "jsonQuery"); + _jsonIndex.Load(obj, "jsonIndex"); + _jsonQueryExtractSingle = + obs_data_get_bool(obj, "jsonQueryExtractSingle"); +} + +void VariableActionJson::ResolveVariables() +{ + _jsonQuery.ResolveVariables(); + _jsonIndex.ResolveVariables(); +} + +bool VariableActionJson::Perform(MacroActionVariable &action, + MacroActionVariable::Action type, + Variable &var) +{ + switch (type) { + case Action::EXTRACT_JSON: { + auto value = GetJsonField(var.Value(), action._strValue); + if (!value.has_value()) { + return true; + } + var.SetValue(*value); + return true; + } + case Action::QUERY_JSON: { + auto value = QueryJson(var.Value(), _jsonQuery); + if (!value.has_value()) { + return true; + } + + if (!_jsonQueryExtractSingle) { + var.SetValue(*value); + return true; + } + + auto extracted = ExtractSingleJsonArrayElement(*value); + if (extracted.has_value()) { + var.SetValue(*extracted); + } else { + var.SetValue(*value); + } + return true; + } + case Action::ARRAY_JSON: { + auto value = AccessJsonArrayIndex(var.Value(), _jsonIndex); + if (!value.has_value()) { + return true; + } + var.SetValue(*value); + return true; + } + default: + return true; + } +} + +VariableActionJsonEdit::VariableActionJsonEdit( + QWidget *parent, std::shared_ptr entryData) + : VariableActionGroupEdit(parent, entryData), + _data(entryData ? entryData->GetGroup() + : nullptr), + _jsonQuery(new VariableLineEdit(this)), + _jsonQueryHelp(new HelpIcon( + obs_module_text( + "AdvSceneSwitcher.action.variable.type.queryJson.info"), + this)), + _jsonIndex(new VariableSpinBox(this)), + _jsonExtractSingle(new QCheckBox( + obs_module_text( + "AdvSceneSwitcher.action.variable.type.queryJson.extractSingle"), + this)), + _jsonExtractSingleHelp(new HelpIcon( + obs_module_text( + "AdvSceneSwitcher.action.variable.type.queryJson.extractSingle.info"), + this)) +{ + _jsonIndex->setMaximum(999); + + QWidget::connect(_jsonQuery, SIGNAL(editingFinished()), this, + SLOT(JsonQueryChanged())); + QWidget::connect( + _jsonIndex, + SIGNAL(NumberVariableChanged(const NumberVariable &)), + this, SLOT(JsonIndexChanged(const NumberVariable &))); + QWidget::connect(_jsonExtractSingle, SIGNAL(stateChanged(int)), this, + SLOT(JsonQueryExtractSingleChanged(int))); + + auto layout = new QHBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(_jsonExtractSingle); + layout->addWidget(_jsonExtractSingleHelp); + layout->addStretch(); + setLayout(layout); +} + +VariableActionGroupEdit * +VariableActionJsonEdit::Create(QWidget *parent, + std::shared_ptr entryData) +{ + return new VariableActionJsonEdit(parent, entryData); +} + +std::unordered_map +VariableActionJsonEdit::GetInlineWidgets() +{ + return { + {"{{jsonQuery}}", _jsonQuery}, + {"{{jsonQueryHelp}}", _jsonQueryHelp}, + {"{{jsonIndex}}", _jsonIndex}, + }; +} + +void VariableActionJsonEdit::UpdateEntryData() +{ + if (!_data) { + return; + } + + _jsonQuery->setText(_data->_jsonQuery); + _jsonIndex->SetValue(_data->_jsonIndex); + _jsonExtractSingle->setChecked(_data->_jsonQueryExtractSingle); + _loading = false; +} + +void VariableActionJsonEdit::SetWidgetVisibility( + MacroActionVariable::Action action) +{ + setVisible(action == Action::QUERY_JSON); +} + +void VariableActionJsonEdit::JsonQueryChanged() +{ + GUARD_LOADING_AND_LOCK(); + _data->_jsonQuery = _jsonQuery->text().toStdString(); +} + +void VariableActionJsonEdit::JsonIndexChanged(const NumberVariable &value) +{ + GUARD_LOADING_AND_LOCK(); + _data->_jsonIndex = value; +} + +void VariableActionJsonEdit::JsonQueryExtractSingleChanged(int value) +{ + GUARD_LOADING_AND_LOCK(); + _data->_jsonQueryExtractSingle = value; +} + +} // namespace advss diff --git a/lib/macro/macro-action-variable-json.hpp b/lib/macro/macro-action-variable-json.hpp new file mode 100644 index 00000000..c4ee1d1b --- /dev/null +++ b/lib/macro/macro-action-variable-json.hpp @@ -0,0 +1,50 @@ +#pragma once +#include "macro-action-variable-group.hpp" +#include "help-icon.hpp" +#include "variable-line-edit.hpp" +#include "variable-spinbox.hpp" + +namespace advss { + +class VariableActionJson : public VariableActionGroup { +public: + std::unique_ptr Copy() const override; + void Save(obs_data_t *, const MacroActionVariable &) const override; + void Load(obs_data_t *, MacroActionVariable &) override; + void ResolveVariables() override; + bool Perform(MacroActionVariable &, MacroActionVariable::Action, + Variable &) override; + + StringVariable _jsonQuery = "$.some.nested.value"; + IntVariable _jsonIndex = 0; + bool _jsonQueryExtractSingle = true; +}; + +class VariableActionJsonEdit : public VariableActionGroupEdit { + Q_OBJECT + +public: + VariableActionJsonEdit(QWidget *parent, + std::shared_ptr entryData); + static VariableActionGroupEdit * + Create(QWidget *parent, std::shared_ptr entryData); + std::unordered_map GetInlineWidgets() override; + void UpdateEntryData() override; + void SetWidgetVisibility(MacroActionVariable::Action) override; + +private slots: + void JsonQueryChanged(); + void JsonIndexChanged(const NumberVariable &); + void JsonQueryExtractSingleChanged(int); + +private: + VariableActionJson *_data; + + VariableLineEdit *_jsonQuery; + HelpIcon *_jsonQueryHelp; + VariableSpinBox *_jsonIndex; + QCheckBox *_jsonExtractSingle; + HelpIcon *_jsonExtractSingleHelp; +}; + +} // namespace advss diff --git a/lib/macro/macro-action-variable-macro.cpp b/lib/macro/macro-action-variable-macro.cpp new file mode 100644 index 00000000..d47fe4d1 --- /dev/null +++ b/lib/macro/macro-action-variable-macro.cpp @@ -0,0 +1,374 @@ +#include "macro-action-variable-macro.hpp" +#include "macro-condition-edit.hpp" +#include "macro-signals.hpp" +#include "macro.hpp" + +namespace advss { + +using Action = MacroActionVariable::Action; + +static bool registered = RegisterVariableActionGroup( + {30, + { + {Action::SET_TO_TEMPVAR, + "AdvSceneSwitcher.action.variable.type.setToTempvar", + "AdvSceneSwitcher.action.variable.layout.setToTempvar"}, + {Action::SET_CONDITION_VALUE, + "AdvSceneSwitcher.action.variable.type.setConditionValue", + "AdvSceneSwitcher.action.variable.layout.segmentValue"}, + {Action::SET_ACTION_VALUE, + "AdvSceneSwitcher.action.variable.type.setActionValue", + "AdvSceneSwitcher.action.variable.layout.segmentValue"}, + }, + []() { return std::make_unique(); }, + VariableActionMacroEdit::Create}); + +static bool isSegmentValueAction(MacroActionVariable::Action action) +{ + return action == Action::SET_CONDITION_VALUE || + action == Action::SET_ACTION_VALUE; +} + +VariableActionMacro::~VariableActionMacro() +{ + DecrementCurrentSegmentVariableRef(); +} + +std::unique_ptr VariableActionMacro::Copy() const +{ + return std::make_unique(*this); +} + +void VariableActionMacro::Save(obs_data_t *obj, + const MacroActionVariable &action) const +{ + obs_data_set_int(obj, "segmentIdx", GetSegmentIndexValue(action)); + _tempVar.Save(obj, action.GetMacro()); +} + +void VariableActionMacro::Load(obs_data_t *obj, MacroActionVariable &action) +{ + _segmentIdxLoadValue = obs_data_get_int(obj, "segmentIdx"); + _tempVar.Load(obj, action.GetMacro()); +} + +void VariableActionMacro::PostLoad(MacroActionVariable &action) +{ + SetSegmentIndexValue(action, _segmentIdxLoadValue); +} + +std::vector VariableActionMacro::GetTempVarRefs() const +{ + if (_tempVar.HasValidID()) { + return {_tempVar}; + } + return {}; +} + +bool VariableActionMacro::Perform(MacroActionVariable &action, + MacroActionVariable::Action type, + Variable &var) +{ + switch (type) { + case Action::SET_CONDITION_VALUE: + case Action::SET_ACTION_VALUE: { + auto m = action.GetMacro(); + if (!m) { + return true; + } + if (GetSegmentIndexValue(action) == -1) { + return true; + } + auto segment = _macroSegment.lock(); + if (!segment) { + return true; + } + var.SetValue(segment->GetVariableValue()); + return true; + } + case Action::SET_TO_TEMPVAR: { + auto tempVar = _tempVar.GetTempVariable(action.GetMacro()); + if (!tempVar) { + return true; + } + auto value = tempVar->Value(); + if (!value) { + return true; + } + var.SetValue(*value); + return true; + } + default: + return true; + } +} + +void VariableActionMacro::SetSegmentIndexValue( + const MacroActionVariable &action, int value) +{ + DecrementCurrentSegmentVariableRef(); + + auto m = action.GetMacro(); + if (!m) { + _macroSegment.reset(); + return; + } + + if (value < 0) { + _macroSegment.reset(); + return; + } + + std::shared_ptr segment; + if (action._action == Action::SET_CONDITION_VALUE) { + if (value < (int)m->Conditions().size()) { + segment = m->Conditions().at(value); + } + } else if (action._action == Action::SET_ACTION_VALUE) { + if (value < (int)m->Actions().size()) { + segment = m->Actions().at(value); + } + } + + _macroSegment = segment; + if (segment) { + IncrementVariableRef(segment.get()); + } +} + +int VariableActionMacro::GetSegmentIndexValue( + const MacroActionVariable &action) const +{ + auto m = action.GetMacro(); + if (!m) { + return -1; + } + + auto segment = _macroSegment.lock(); + if (!segment) { + return -1; + } + + if (action._action == Action::SET_CONDITION_VALUE) { + auto it = std::find(m->Conditions().begin(), + m->Conditions().end(), segment); + if (it != m->Conditions().end()) { + return std::distance(m->Conditions().begin(), it); + } + return -1; + } else if (action._action == Action::SET_ACTION_VALUE) { + auto it = std::find(m->Actions().begin(), m->Actions().end(), + segment); + if (it != m->Actions().end()) { + return std::distance(m->Actions().begin(), it); + } + return -1; + } + + return -1; +} + +void VariableActionMacro::DecrementCurrentSegmentVariableRef() +{ + auto segment = _macroSegment.lock(); + if (!segment) { + return; + } + + DecrementVariableRef(segment.get()); +} + +VariableActionMacroEdit::VariableActionMacroEdit( + QWidget *parent, std::shared_ptr entryData) + : VariableActionGroupEdit(parent, entryData), + _data(entryData ? entryData->GetGroup() + : nullptr), + _segmentIdx(new MacroSegmentSelection( + this, MacroSegmentSelection::Type::CONDITION, false)), + _segmentValueStatus(new QLabel(this)), + _segmentValue(new ResizingPlainTextEdit(this, 10, 1, 1)), + _tempVars(new TempVariableSelection(this)), + _tempVarsHelp(new HelpIcon( + obs_module_text( + "AdvSceneSwitcher.action.variable.type.setToTempvar.help"), + this)) +{ + _segmentValue->setReadOnly(true); + + QWidget::connect(_segmentIdx, + SIGNAL(SelectionChanged(const IntVariable &)), this, + SLOT(SegmentIndexChanged(const IntVariable &))); + QWidget::connect(MacroSignalManager::Instance(), + SIGNAL(SegmentOrderChanged()), this, + SLOT(MacroSegmentOrderChanged())); + QWidget::connect(_tempVars, + SIGNAL(SelectionChanged(const TempVariableRef &)), + this, SLOT(SelectionChanged(const TempVariableRef &))); + + auto layout = new QVBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(_segmentValueStatus); + layout->addWidget(_segmentValue); + setLayout(layout); + + connect(&_timer, SIGNAL(timeout()), this, + SLOT(UpdateSegmentVariableValue())); + _timer.start(1500); +} + +VariableActionGroupEdit * +VariableActionMacroEdit::Create(QWidget *parent, + std::shared_ptr entryData) +{ + return new VariableActionMacroEdit(parent, entryData); +} + +std::unordered_map +VariableActionMacroEdit::GetInlineWidgets() +{ + return { + {"{{segmentIndex}}", _segmentIdx}, + {"{{tempVars}}", _tempVars}, + {"{{tempVarsHelp}}", _tempVarsHelp}, + }; +} + +void VariableActionMacroEdit::UpdateEntryData() +{ + if (!_data) { + return; + } + + _segmentIdx->SetValue(_data->GetSegmentIndexValue(*_entryData) + 1); + _segmentIdx->SetMacro(_entryData->GetMacro()); + _tempVars->SetVariable(_data->_tempVar); + _loading = false; + UpdateSegmentVariableValue(); +} + +void VariableActionMacroEdit::SetWidgetVisibility( + MacroActionVariable::Action action) +{ + if (!_data) { + return; + } + + if (action == Action::SET_ACTION_VALUE) { + _segmentIdx->SetType(MacroSegmentSelection::Type::ACTION); + } else if (action == Action::SET_CONDITION_VALUE) { + _segmentIdx->SetType(MacroSegmentSelection::Type::CONDITION); + } + + _tempVarsHelp->setVisible(action == Action::SET_TO_TEMPVAR && + !_data->_tempVar.HasValidID()); + setVisible(isSegmentValueAction(action)); + UpdateSize(); +} + +void VariableActionMacroEdit::SegmentIndexChanged(const IntVariable &val) +{ + GUARD_LOADING_AND_LOCK(); + _data->SetSegmentIndexValue(*_entryData, val - 1); +} + +void VariableActionMacroEdit::SetSegmentValueError(const QString &text) +{ + _segmentValueStatus->setText(text); + _segmentValue->setPlainText(""); + _segmentValue->hide(); + UpdateSize(); +} + +void VariableActionMacroEdit::UpdateSegmentVariableValue() +{ + if (!_data || !isSegmentValueAction(_entryData->_action)) { + return; + } + + auto m = _entryData->GetMacro(); + if (!m) { + return; + } + + int index = _data->GetSegmentIndexValue(*_entryData); + if (index < 0) { + SetSegmentValueError(obs_module_text( + "AdvSceneSwitcher.action.variable.invalidSelection")); + const QSignalBlocker b(_segmentIdx); + _segmentIdx->SetValue(0); + return; + } + + std::shared_ptr segment; + if (_entryData->_action == Action::SET_ACTION_VALUE) { + const auto &actions = m->Actions(); + if (index < (int)actions.size()) { + segment = actions.at(index); + } + } else if (_entryData->_action == Action::SET_CONDITION_VALUE) { + const auto &conditions = m->Conditions(); + if (index < (int)conditions.size()) { + segment = conditions.at(index); + } + } + + if (!segment) { + SetSegmentValueError(obs_module_text( + "AdvSceneSwitcher.action.variable.invalidSelection")); + return; + } + + if (!SupportsVariableValue(segment.get())) { + std::string type; + QString fmt; + + if (_entryData->_action == Action::SET_ACTION_VALUE) { + type = MacroActionFactory::GetActionName( + segment->GetId()); + fmt = QString(obs_module_text( + "AdvSceneSwitcher.action.variable.actionNoVariableSupport")); + } else if (_entryData->_action == Action::SET_CONDITION_VALUE) { + type = MacroConditionFactory::GetConditionName( + segment->GetId()); + fmt = QString(obs_module_text( + "AdvSceneSwitcher.action.variable.conditionNoVariableSupport")); + } + SetSegmentValueError( + fmt.arg(QString(obs_module_text(type.c_str())))); + return; + } + + _segmentValueStatus->setText(obs_module_text( + "AdvSceneSwitcher.action.variable.currentSegmentValue")); + _segmentValue->show(); + + // Only update the text if the value changed to prevent possible text + // selections being lost + auto previousText = _segmentValue->toPlainText(); + auto newText = QString::fromStdString(segment->GetVariableValue()); + if (newText != previousText) { + _segmentValue->setPlainText(newText); + } + + UpdateSize(); +} + +void VariableActionMacroEdit::MacroSegmentOrderChanged() +{ + if (!_data) { + return; + } + + const QSignalBlocker b(_segmentIdx); + _segmentIdx->SetValue(_data->GetSegmentIndexValue(*_entryData) + 1); +} + +void VariableActionMacroEdit::SelectionChanged(const TempVariableRef &var) +{ + GUARD_LOADING_AND_LOCK(); + _data->_tempVar = var; + IncrementTempVarInUseGeneration(); + SetWidgetVisibility(_entryData->_action); +} + +} // namespace advss diff --git a/lib/macro/macro-action-variable-macro.hpp b/lib/macro/macro-action-variable-macro.hpp new file mode 100644 index 00000000..cfdaa55f --- /dev/null +++ b/lib/macro/macro-action-variable-macro.hpp @@ -0,0 +1,67 @@ +#pragma once +#include "macro-action-variable-group.hpp" +#include "help-icon.hpp" +#include "macro-segment-selection.hpp" +#include "resizing-text-edit.hpp" + +#include + +namespace advss { + +class VariableActionMacro : public VariableActionGroup { +public: + VariableActionMacro() = default; + VariableActionMacro(const VariableActionMacro &) = default; + ~VariableActionMacro(); + std::unique_ptr Copy() const override; + void Save(obs_data_t *, const MacroActionVariable &) const override; + void Load(obs_data_t *, MacroActionVariable &) override; + void PostLoad(MacroActionVariable &) override; + std::vector GetTempVarRefs() const override; + bool Perform(MacroActionVariable &, MacroActionVariable::Action, + Variable &) override; + + void SetSegmentIndexValue(const MacroActionVariable &, int); + int GetSegmentIndexValue(const MacroActionVariable &) const; + + TempVariableRef _tempVar; + +private: + void DecrementCurrentSegmentVariableRef(); + + std::weak_ptr _macroSegment; + int _segmentIdxLoadValue = -1; +}; + +class VariableActionMacroEdit : public VariableActionGroupEdit { + Q_OBJECT + +public: + VariableActionMacroEdit(QWidget *parent, + std::shared_ptr entryData); + static VariableActionGroupEdit * + Create(QWidget *parent, std::shared_ptr entryData); + std::unordered_map GetInlineWidgets() override; + void UpdateEntryData() override; + void SetWidgetVisibility(MacroActionVariable::Action) override; + +private slots: + void SegmentIndexChanged(const IntVariable &); + void UpdateSegmentVariableValue(); + void MacroSegmentOrderChanged(); + void SelectionChanged(const TempVariableRef &var); + +private: + void SetSegmentValueError(const QString &); + + VariableActionMacro *_data; + + MacroSegmentSelection *_segmentIdx; + QLabel *_segmentValueStatus; + ResizingPlainTextEdit *_segmentValue; + TempVariableSelection *_tempVars; + HelpIcon *_tempVarsHelp; + QTimer _timer; +}; + +} // namespace advss diff --git a/lib/macro/macro-action-variable-math.cpp b/lib/macro/macro-action-variable-math.cpp new file mode 100644 index 00000000..7e7e7705 --- /dev/null +++ b/lib/macro/macro-action-variable-math.cpp @@ -0,0 +1,366 @@ +#include "macro-action-variable-math.hpp" +#include "layout-helpers.hpp" +#include "log-helper.hpp" +#include "math-helpers.hpp" + +#include + +namespace advss { + +using Action = MacroActionVariable::Action; + +static bool registered = RegisterVariableActionGroup( + {40, + { + {Action::MATH_EXPRESSION, + "AdvSceneSwitcher.action.variable.type.mathExpression", + "AdvSceneSwitcher.action.variable.layout.mathExpression", + true}, + {Action::INCREMENT, + "AdvSceneSwitcher.action.variable.type.increment", + "AdvSceneSwitcher.action.variable.layout.numValue"}, + {Action::DECREMENT, + "AdvSceneSwitcher.action.variable.type.decrement", + "AdvSceneSwitcher.action.variable.layout.numValue"}, + {Action::ROUND_TO_INT, + "AdvSceneSwitcher.action.variable.type.roundToInt"}, + {Action::RANDOM_NUMBER, + "AdvSceneSwitcher.action.variable.type.randomNumber"}, + {Action::RANDOM_LIST_VALUE, + "AdvSceneSwitcher.action.variable.type.randomListValue"}, + }, + []() { return std::make_unique(); }, + VariableActionMathEdit::Create}); + +static void modifyNumValue(Variable &var, double val, const bool increment) +{ + auto current = var.DoubleValue(); + if (!current.has_value()) { + return; + } + + if (increment) { + var.SetValue(*current + val); + } else { + var.SetValue(*current - val); + } +} + +std::unique_ptr VariableActionMath::Copy() const +{ + return std::make_unique(*this); +} + +void VariableActionMath::Save(obs_data_t *obj, + const MacroActionVariable &) const +{ + _numValue.Save(obj, "numValue"); + _mathExpression.Save(obj, "mathExpression"); + _randomNumberStart.Save(obj, "randomNumberStart"); + _randomNumberEnd.Save(obj, "randomNumberEnd"); + obs_data_set_bool(obj, "generateInteger", _generateInteger); + _randomValues.Save(obj, "randomValues", "value"); + obs_data_set_bool(obj, "allowRepeatValues", _allowRepeatValues); +} + +void VariableActionMath::Load(obs_data_t *obj, MacroActionVariable &) +{ + _mathExpression.Load(obj, "mathExpression"); + + // Convert old data format + // TODO: Remove in future version + if (!obs_data_has_user_value(obj, "version")) { + _numValue = obs_data_get_double(obj, "numValue"); + } else { + _numValue.Load(obj, "numValue"); + } + + _randomNumberStart.Load(obj, "randomNumberStart"); + _randomNumberEnd.Load(obj, "randomNumberEnd"); + _generateInteger = obs_data_get_bool(obj, "generateInteger"); + _randomValues.Load(obj, "randomValues", "value"); + _allowRepeatValues = obs_data_get_bool(obj, "allowRepeatValues"); +} + +void VariableActionMath::ResolveVariables() +{ + _mathExpression.ResolveVariables(); +} + +void VariableActionMath::HandleMathExpression(Variable &var) +{ + auto result = EvalMathExpression(_mathExpression); + if (std::holds_alternative(result)) { + blog(LOG_WARNING, "%s", std::get(result).c_str()); + return; + } + var.SetValue(std::get(result)); +} + +void VariableActionMath::GenerateRandomNumber(Variable &var) +{ + static std::random_device rd; + static std::mt19937 gen(rd()); + + double start = _randomNumberStart; + double end = _randomNumberEnd; + + if (start > end) { + std::swap(start, end); + } + + if (_generateInteger) { + std::uniform_int_distribution dis(start, end); + var.SetValue(dis(gen)); + } else { + std::uniform_real_distribution dis(start, end); + var.SetValue(dis(gen)); + } +} + +void VariableActionMath::PickRandomValue(Variable &var) +{ + static std::random_device rd; + static std::mt19937 gen(rd()); + static const uint8_t maxIter = 255; + + if (_randomValues.isEmpty()) { + return; + } + + if (_randomValues.size() == 1) { + const auto &value = _randomValues.at(0); + var.SetValue(value); + _lastRandomValue = value; + } + + std::uniform_int_distribution dis(0, _randomValues.size() - 1); + StringVariable value = _randomValues.at(dis(gen)); + + uint8_t iter = 0; + for (; !_allowRepeatValues && iter < maxIter && + _lastRandomValue.has_value() && + std::string(value) == _lastRandomValue.value(); + iter++) { + value = _randomValues.at(dis(gen)); + } + + if (iter == maxIter) { + blog(LOG_INFO, + "giving up picking non-repeat random value after %d tries", + maxIter); + } + + var.SetValue(value); + _lastRandomValue = value; +} + +bool VariableActionMath::Perform(MacroActionVariable &, + MacroActionVariable::Action type, + Variable &var) +{ + switch (type) { + case Action::MATH_EXPRESSION: + HandleMathExpression(var); + break; + case Action::INCREMENT: + modifyNumValue(var, _numValue.GetValue(), true); + break; + case Action::DECREMENT: + modifyNumValue(var, _numValue.GetValue(), false); + break; + case Action::ROUND_TO_INT: { + auto curValue = var.DoubleValue(); + if (!curValue.has_value()) { + break; + } + var.SetValue(std::to_string(int(std::round(*curValue)))); + break; + } + case Action::RANDOM_NUMBER: + GenerateRandomNumber(var); + break; + case Action::RANDOM_LIST_VALUE: + PickRandomValue(var); + break; + default: + break; + } + return true; +} + +VariableActionMathEdit::VariableActionMathEdit( + QWidget *parent, std::shared_ptr entryData) + : VariableActionGroupEdit(parent, entryData), + _data(entryData ? entryData->GetGroup() + : nullptr), + _numValue(new VariableDoubleSpinBox(this)), + _mathExpression(new VariableLineEdit(this)), + _mathExpressionResult(new QLabel(this)), + _randomNumberStart(new VariableDoubleSpinBox(this)), + _randomNumberEnd(new VariableDoubleSpinBox(this)), + _generateInteger(new QCheckBox( + obs_module_text( + "AdvSceneSwitcher.action.variable.generateInteger"), + this)), + _randomNumberLayout(new QVBoxLayout()), + _randomValues(new StringListEdit(this)), + _allowRepeatValues(new QCheckBox( + obs_module_text( + "AdvSceneSwitcher.action.variable.type.allowRepeat"), + this)), + _randomValueLayout(new QVBoxLayout()) +{ + _numValue->setMinimum(-9999999999); + _numValue->setMaximum(9999999999); + _randomNumberStart->setMinimum(-9999999999); + _randomNumberStart->setMaximum(9999999999); + _randomNumberEnd->setMinimum(-9999999999); + _randomNumberEnd->setMaximum(9999999999); + _randomValues->SetMaxStringSize(99999999); + + QWidget::connect( + _numValue, + SIGNAL(NumberVariableChanged(const NumberVariable &)), + this, SLOT(NumValueChanged(const NumberVariable &))); + QWidget::connect(_mathExpression, SIGNAL(editingFinished()), this, + SLOT(MathExpressionChanged())); + QWidget::connect( + _randomNumberStart, + SIGNAL(NumberVariableChanged(const NumberVariable &)), + this, + SLOT(RandomNumberStartChanged(const NumberVariable &))); + QWidget::connect( + _randomNumberEnd, + SIGNAL(NumberVariableChanged(const NumberVariable &)), + this, + SLOT(RandomNumberEndChanged(const NumberVariable &))); + QWidget::connect(_generateInteger, SIGNAL(stateChanged(int)), this, + SLOT(GenerateIntegerChanged(int))); + QWidget::connect(_randomValues, + SIGNAL(StringListChanged(const StringList &)), this, + SLOT(RandomValueListChanged(const StringList &))); + QWidget::connect(_allowRepeatValues, SIGNAL(stateChanged(int)), this, + SLOT(AllowRepeatValuesChanged(int))); + + auto randomLayout = new QHBoxLayout(); + PlaceWidgets( + obs_module_text( + "AdvSceneSwitcher.action.variable.layout.randomNumber"), + randomLayout, + {{"{{randomNumberStart}}", _randomNumberStart}, + {"{{randomNumberEnd}}", _randomNumberEnd}}); + _randomNumberLayout->addLayout(randomLayout); + _randomNumberLayout->addWidget(_generateInteger); + + _randomValueLayout->addWidget(_randomValues); + _randomValueLayout->addWidget(_allowRepeatValues); + + auto layout = new QVBoxLayout(); + layout->setContentsMargins(0, 0, 0, 0); + layout->addWidget(_mathExpressionResult); + layout->addLayout(_randomNumberLayout); + layout->addLayout(_randomValueLayout); + setLayout(layout); +} + +VariableActionGroupEdit * +VariableActionMathEdit::Create(QWidget *parent, + std::shared_ptr entryData) +{ + return new VariableActionMathEdit(parent, entryData); +} + +std::unordered_map +VariableActionMathEdit::GetInlineWidgets() +{ + return { + {"{{numValue}}", _numValue}, + {"{{mathExpression}}", _mathExpression}, + }; +} + +void VariableActionMathEdit::UpdateEntryData() +{ + if (!_data) { + return; + } + + _numValue->SetValue(_data->_numValue); + _mathExpression->setText(_data->_mathExpression); + _randomNumberStart->SetValue(_data->_randomNumberStart); + _randomNumberEnd->SetValue(_data->_randomNumberEnd); + _generateInteger->setChecked(_data->_generateInteger); + _allowRepeatValues->setChecked(_data->_allowRepeatValues); + _randomValues->SetStringList(_data->_randomValues); + _loading = false; +} + +void VariableActionMathEdit::SetWidgetVisibility( + MacroActionVariable::Action action) +{ + const bool isRandomNumber = action == Action::RANDOM_NUMBER; + const bool isRandomListValue = action == Action::RANDOM_LIST_VALUE; + _mathExpressionResult->hide(); + SetLayoutVisible(_randomNumberLayout, isRandomNumber); + SetLayoutVisible(_randomValueLayout, isRandomListValue); + setVisible(isRandomNumber || isRandomListValue); + UpdateSize(); +} + +void VariableActionMathEdit::NumValueChanged(const NumberVariable &value) +{ + GUARD_LOADING_AND_LOCK(); + _data->_numValue = value; +} + +void VariableActionMathEdit::MathExpressionChanged() +{ + GUARD_LOADING_AND_LOCK(); + _data->_mathExpression = _mathExpression->text().toStdString(); + + // In case of invalid expression display an error + auto result = EvalMathExpression(_data->_mathExpression); + auto hasError = std::holds_alternative(result); + if (hasError) { + _mathExpressionResult->setText( + QString::fromStdString(std::get(result))); + } + _mathExpressionResult->setVisible(hasError); + setVisible(hasError); + UpdateSize(); +} + +void VariableActionMathEdit::RandomNumberStartChanged( + const NumberVariable &value) +{ + GUARD_LOADING_AND_LOCK(); + _data->_randomNumberStart = value; +} + +void VariableActionMathEdit::RandomNumberEndChanged( + const NumberVariable &value) +{ + GUARD_LOADING_AND_LOCK(); + _data->_randomNumberEnd = value; +} + +void VariableActionMathEdit::GenerateIntegerChanged(int value) +{ + GUARD_LOADING_AND_LOCK(); + _data->_generateInteger = value; +} + +void VariableActionMathEdit::RandomValueListChanged(const StringList &values) +{ + GUARD_LOADING_AND_LOCK(); + _data->_randomValues = values; +} + +void VariableActionMathEdit::AllowRepeatValuesChanged(int value) +{ + GUARD_LOADING_AND_LOCK(); + _data->_allowRepeatValues = value; +} + +} // namespace advss diff --git a/lib/macro/macro-action-variable-math.hpp b/lib/macro/macro-action-variable-math.hpp new file mode 100644 index 00000000..0df79cc2 --- /dev/null +++ b/lib/macro/macro-action-variable-math.hpp @@ -0,0 +1,73 @@ +#pragma once +#include "macro-action-variable-group.hpp" +#include "string-list.hpp" +#include "variable-line-edit.hpp" +#include "variable-spinbox.hpp" + +#include + +namespace advss { + +class VariableActionMath : public VariableActionGroup { +public: + std::unique_ptr Copy() const override; + void Save(obs_data_t *, const MacroActionVariable &) const override; + void Load(obs_data_t *, MacroActionVariable &) override; + void ResolveVariables() override; + bool Perform(MacroActionVariable &, MacroActionVariable::Action, + Variable &) override; + + DoubleVariable _numValue = 0; + StringVariable _mathExpression = obs_module_text( + "AdvSceneSwitcher.action.variable.mathExpression.example"); + DoubleVariable _randomNumberStart = 0; + DoubleVariable _randomNumberEnd = 100; + bool _generateInteger = true; + StringList _randomValues = {"value1", "value2", "value3"}; + bool _allowRepeatValues = true; + +private: + void HandleMathExpression(Variable &); + void GenerateRandomNumber(Variable &); + void PickRandomValue(Variable &); + + std::optional _lastRandomValue; +}; + +class VariableActionMathEdit : public VariableActionGroupEdit { + Q_OBJECT + +public: + VariableActionMathEdit(QWidget *parent, + std::shared_ptr entryData); + static VariableActionGroupEdit * + Create(QWidget *parent, std::shared_ptr entryData); + std::unordered_map GetInlineWidgets() override; + void UpdateEntryData() override; + void SetWidgetVisibility(MacroActionVariable::Action) override; + +private slots: + void NumValueChanged(const NumberVariable &value); + void MathExpressionChanged(); + void RandomNumberStartChanged(const NumberVariable &); + void RandomNumberEndChanged(const NumberVariable &); + void GenerateIntegerChanged(int); + void RandomValueListChanged(const StringList &); + void AllowRepeatValuesChanged(int); + +private: + VariableActionMath *_data; + + VariableDoubleSpinBox *_numValue; + VariableLineEdit *_mathExpression; + QLabel *_mathExpressionResult; + VariableDoubleSpinBox *_randomNumberStart; + VariableDoubleSpinBox *_randomNumberEnd; + QCheckBox *_generateInteger; + QVBoxLayout *_randomNumberLayout; + StringListEdit *_randomValues; + QCheckBox *_allowRepeatValues; + QVBoxLayout *_randomValueLayout; +}; + +} // namespace advss diff --git a/lib/macro/macro-action-variable-scene.cpp b/lib/macro/macro-action-variable-scene.cpp new file mode 100644 index 00000000..f7a778cd --- /dev/null +++ b/lib/macro/macro-action-variable-scene.cpp @@ -0,0 +1,169 @@ +#include "macro-action-variable-scene.hpp" +#include "source-helpers.hpp" + +namespace advss { + +using Action = MacroActionVariable::Action; + +static bool registered = RegisterVariableActionGroup( + {60, + { + {Action::SCENE_ITEM_NAME, + "AdvSceneSwitcher.action.variable.type.sceneItemName", + "AdvSceneSwitcher.action.variable.layout.sceneItemName"}, + {Action::SCENE_ITEM_COUNT, + "AdvSceneSwitcher.action.variable.type.sceneItemCount", + "AdvSceneSwitcher.action.variable.layout.sceneItemCount"}, + }, + []() { return std::make_unique(); }, + VariableActionSceneEdit::Create}); + +struct GetSceneItemNameHelper { + int curIdx = 0; + int targetIdx = 0; + std::string name = ""; +}; + +static bool getSceneItemAtIdx(obs_scene_t *, obs_sceneitem_t *item, void *ptr) +{ + auto data = reinterpret_cast(ptr); + if (obs_sceneitem_is_group(item)) { + obs_scene_t *scene = obs_sceneitem_group_get_scene(item); + obs_scene_enum_items(scene, getSceneItemAtIdx, ptr); + } + if (data->curIdx == data->targetIdx) { + data->name = + obs_source_get_name(obs_sceneitem_get_source(item)); + data->curIdx--; + return false; + } + data->curIdx--; + return true; +} + +std::unique_ptr VariableActionScene::Copy() const +{ + return std::make_unique(*this); +} + +void VariableActionScene::Save(obs_data_t *obj, + const MacroActionVariable &) const +{ + _scene.Save(obj); + _sceneItemIndex.Save(obj, "sceneItemIndex"); +} + +void VariableActionScene::Load(obs_data_t *obj, MacroActionVariable &) +{ + _scene.Load(obj); + _sceneItemIndex.Load(obj, "sceneItemIndex"); +} + +void VariableActionScene::ResolveVariables() +{ + _scene.ResolveVariables(); + _sceneItemIndex.ResolveVariables(); +} + +void VariableActionScene::SetToSceneItemName(Variable &var) +{ + auto weakSource = _scene.GetScene(); + if (!weakSource) { + var.SetValue(""); + return; + } + auto index = _sceneItemIndex.GetValue(); + if (index < 1) { + var.SetValue(""); + return; + } + auto sceneItemCount = GetSceneItemCount(weakSource); + OBSSourceAutoRelease source = obs_weak_source_get_source(weakSource); + auto scene = obs_scene_from_source(source); + GetSceneItemNameHelper data{sceneItemCount, index}; + obs_scene_enum_items(scene, getSceneItemAtIdx, &data); + var.SetValue(data.name); +} + +bool VariableActionScene::Perform(MacroActionVariable &, + MacroActionVariable::Action type, + Variable &var) +{ + switch (type) { + case Action::SCENE_ITEM_NAME: + SetToSceneItemName(var); + break; + case Action::SCENE_ITEM_COUNT: + var.SetValue(GetSceneItemCount(_scene.GetScene(false))); + break; + default: + break; + } + return true; +} + +VariableActionSceneEdit::VariableActionSceneEdit( + QWidget *parent, std::shared_ptr entryData) + : VariableActionGroupEdit(parent, entryData), + _data(entryData ? entryData->GetGroup() + : nullptr), + _scenes(new SceneSelectionWidget(this, true, false, true, true, + true)), + _sceneItemIndex(new VariableSpinBox(this)) +{ + _sceneItemIndex->setMinimum(1); + + QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)), + this, SLOT(SceneChanged(const SceneSelection &))); + QWidget::connect( + _sceneItemIndex, + SIGNAL(NumberVariableChanged(const NumberVariable &)), + this, SLOT(SceneItemIndexChanged(const NumberVariable &))); +} + +VariableActionGroupEdit * +VariableActionSceneEdit::Create(QWidget *parent, + std::shared_ptr entryData) +{ + return new VariableActionSceneEdit(parent, entryData); +} + +std::unordered_map +VariableActionSceneEdit::GetInlineWidgets() +{ + return { + {"{{scenes}}", _scenes}, + {"{{sceneItemIndex}}", _sceneItemIndex}, + }; +} + +void VariableActionSceneEdit::UpdateEntryData() +{ + if (!_data) { + return; + } + + _scenes->SetScene(_data->_scene); + _sceneItemIndex->SetValue(_data->_sceneItemIndex); + _loading = false; +} + +void VariableActionSceneEdit::SetWidgetVisibility(MacroActionVariable::Action) +{ + hide(); +} + +void VariableActionSceneEdit::SceneChanged(const SceneSelection &scene) +{ + GUARD_LOADING_AND_LOCK(); + _data->_scene = scene; +} + +void VariableActionSceneEdit::SceneItemIndexChanged( + const NumberVariable &value) +{ + GUARD_LOADING_AND_LOCK(); + _data->_sceneItemIndex = value; +} + +} // namespace advss diff --git a/lib/macro/macro-action-variable-scene.hpp b/lib/macro/macro-action-variable-scene.hpp new file mode 100644 index 00000000..95b5930e --- /dev/null +++ b/lib/macro/macro-action-variable-scene.hpp @@ -0,0 +1,47 @@ +#pragma once +#include "macro-action-variable-group.hpp" +#include "scene-selection.hpp" +#include "variable-spinbox.hpp" + +namespace advss { + +class VariableActionScene : public VariableActionGroup { +public: + std::unique_ptr Copy() const override; + void Save(obs_data_t *, const MacroActionVariable &) const override; + void Load(obs_data_t *, MacroActionVariable &) override; + void ResolveVariables() override; + bool Perform(MacroActionVariable &, MacroActionVariable::Action, + Variable &) override; + + SceneSelection _scene; + IntVariable _sceneItemIndex = 1; + +private: + void SetToSceneItemName(Variable &); +}; + +class VariableActionSceneEdit : public VariableActionGroupEdit { + Q_OBJECT + +public: + VariableActionSceneEdit(QWidget *parent, + std::shared_ptr entryData); + static VariableActionGroupEdit * + Create(QWidget *parent, std::shared_ptr entryData); + std::unordered_map GetInlineWidgets() override; + void UpdateEntryData() override; + void SetWidgetVisibility(MacroActionVariable::Action) override; + +private slots: + void SceneChanged(const SceneSelection &); + void SceneItemIndexChanged(const NumberVariable &); + +private: + VariableActionScene *_data; + + SceneSelectionWidget *_scenes; + VariableSpinBox *_sceneItemIndex; +}; + +} // namespace advss diff --git a/lib/macro/macro-action-variable-string.cpp b/lib/macro/macro-action-variable-string.cpp new file mode 100644 index 00000000..38944fe6 --- /dev/null +++ b/lib/macro/macro-action-variable-string.cpp @@ -0,0 +1,556 @@ +#include "macro-action-variable-string.hpp" +#include "layout-helpers.hpp" +#include "log-helper.hpp" +#include "utility.hpp" + +#include + +namespace advss { + +using Action = MacroActionVariable::Action; + +static bool registered = RegisterVariableActionGroup( + {10, + { + {Action::PAD, "AdvSceneSwitcher.action.variable.type.padValue", + "AdvSceneSwitcher.action.variable.layout.pad"}, + {Action::TRUNCATE, + "AdvSceneSwitcher.action.variable.type.truncateValue", + "AdvSceneSwitcher.action.variable.layout.truncate"}, + {Action::SUBSTRING, + "AdvSceneSwitcher.action.variable.type.subString"}, + {Action::FIND_AND_REPLACE, + "AdvSceneSwitcher.action.variable.type.findAndReplace"}, + {Action::STRING_LENGTH, + "AdvSceneSwitcher.action.variable.type.stringLength", + "AdvSceneSwitcher.action.variable.layout.strValue", true}, + {Action::TRIM, "AdvSceneSwitcher.action.variable.type.trim"}, + {Action::CHANGE_CASE, + "AdvSceneSwitcher.action.variable.type.changeCase", + "AdvSceneSwitcher.action.variable.layout.changeCase"}, + }, + []() { return std::make_unique(); }, + VariableActionStringEdit::Create}); + +static std::string truncate(const std::string &input, + VariableActionString::Direction direction, + size_t length) +{ + if (input.length() <= length) { + return input; + } + + if (direction == VariableActionString::Direction::RIGHT) { + return input.substr(0, length); + } else { + return input.substr(input.length() - length); + } +} + +static std::string pad(const std::string &input, + VariableActionString::Direction direction, size_t length, + char paddingChar) +{ + if (input.length() >= length) { + return input; + } + + if (direction == VariableActionString::Direction::RIGHT) { + return input + + std::string(length - input.length(), paddingChar); + } else { + return std::string(length - input.length(), paddingChar) + + input; + } +} + +std::unique_ptr VariableActionString::Copy() const +{ + return std::make_unique(*this); +} + +void VariableActionString::Save(obs_data_t *obj, + const MacroActionVariable &) const +{ + _subStringStart.Save(obj, "subStringStart"); + _subStringSize.Save(obj, "subStringSize"); + obs_data_set_string(obj, "regexPattern", _regexPattern.c_str()); + _regexMatchIdx.Save(obj, "regexMatchIdx"); + _findRegex.Save(obj, "findRegex"); + _findStr.Save(obj, "findStr"); + _replaceStr.Save(obj, "replaceStr"); + _subStringRegex.Save(obj); + obs_data_set_int(obj, "direction", static_cast(_direction)); + _stringLength.Save(obj, "stringLength"); + obs_data_set_int(obj, "paddingChar", _paddingChar); + obs_data_set_int(obj, "caseType", static_cast(_caseType)); +} + +void VariableActionString::Load(obs_data_t *obj, MacroActionVariable &) +{ + _subStringRegex.Load(obj); + _regexPattern = obs_data_get_string(obj, "regexPattern"); + _findRegex.Load(obj, "findRegex"); + _findStr.Load(obj, "findStr"); + _replaceStr.Load(obj, "replaceStr"); + _direction = static_cast(obs_data_get_int(obj, "direction")); + _stringLength.Load(obj, "stringLength"); + if (obs_data_has_user_value(obj, "paddingChar")) { + _paddingChar = obs_data_get_int(obj, "paddingChar"); + } else { + _paddingChar = ' '; + } + _caseType = static_cast(obs_data_get_int(obj, "caseType")); + + // Convert old data format + // TODO: Remove in future version + if (!obs_data_has_user_value(obj, "version")) { + _subStringStart = obs_data_get_int(obj, "subStringStart") + 1; + _subStringSize = obs_data_get_int(obj, "subStringSize"); + _regexMatchIdx = obs_data_get_int(obj, "regexMatchIdx") + 1; + } else { + _subStringStart.Load(obj, "subStringStart"); + _subStringSize.Load(obj, "subStringSize"); + _regexMatchIdx.Load(obj, "regexMatchIdx"); + } +} + +void VariableActionString::ResolveVariables() +{ + _findStr.ResolveVariables(); + _replaceStr.ResolveVariables(); +} + +void VariableActionString::HandleIndexSubString(Variable &var) +{ + const auto curValue = var.Value(); + const auto subStringSize = _subStringSize.GetValue(); + const auto subStringStart = _subStringStart.GetValue() - 1; + + try { + if (subStringSize == 0) { + var.SetValue(curValue.substr(subStringStart)); + return; + } + + var.SetValue(curValue.substr(subStringStart, subStringSize)); + } catch (const std::out_of_range &) { + vblog(LOG_WARNING, + "invalid start index \"%d\" selected for substring of \"%s\" of variable \"%s\"", + subStringStart, curValue.c_str(), var.Name().c_str()); + } +} + +void VariableActionString::HandleRegexSubString(Variable &var) +{ + const auto curValue = var.Value(); + auto regex = _subStringRegex.GetRegularExpression(_regexPattern); + if (!regex.isValid()) { + return; + } + + auto it = regex.globalMatch(QString::fromStdString(curValue)); + const auto regexMatchIndex = _regexMatchIdx.GetValue() - 1; + for (int idx = 0; idx < regexMatchIndex; idx++) { + if (!it.hasNext()) { + return; + } + it.next(); + } + + if (!it.hasNext()) { + return; + } + + auto match = it.next(); + var.SetValue(match.captured(0).toStdString()); +} + +void VariableActionString::HandleFindAndReplace(Variable &var) +{ + auto value = var.Value(); + if (!_findRegex.Enabled()) { + ReplaceAll(value, _findStr, _replaceStr); + var.SetValue(value); + return; + } + + QString replacement = QString::fromStdString(_replaceStr); + auto regex = _findRegex.GetRegularExpression(_findStr); + auto result = QString::fromStdString(value); + result = result.replace(regex, replacement); + var.SetValue(result.toStdString()); +} + +void VariableActionString::HandleCaseChange(Variable &var) +{ + auto value = QString::fromStdString(var.Value()); + + switch (_caseType) { + case CaseType::LOWER_CASE: + var.SetValue(value.toLower().toStdString()); + break; + case CaseType::UPPER_CASE: + var.SetValue(value.toUpper().toStdString()); + break; + case CaseType::CAPITALIZED: { + if (value.isEmpty()) { + return; + } + + value[0] = value[0].toUpper(); + var.SetValue(value.toStdString()); + break; + } + case CaseType::START_CASE: { + auto partList = value.split(QRegularExpression("\\b")); + + for (auto &part : partList) { + if (part.isEmpty()) { + continue; + } + + part[0] = part[0].toUpper(); + } + + var.SetValue(partList.join("").toStdString()); + + break; + } + } +} + +bool VariableActionString::Perform(MacroActionVariable &action, + MacroActionVariable::Action type, + Variable &var) +{ + switch (type) { + case Action::PAD: + var.SetValue(pad(var.Value(), _direction, _stringLength, + _paddingChar)); + break; + case Action::TRUNCATE: + var.SetValue(truncate(var.Value(), _direction, _stringLength)); + break; + case Action::SUBSTRING: + if (_subStringRegex.Enabled()) { + HandleRegexSubString(var); + break; + } + HandleIndexSubString(var); + break; + case Action::FIND_AND_REPLACE: + HandleFindAndReplace(var); + break; + case Action::STRING_LENGTH: + var.SetValue(std::string(action._strValue).length()); + break; + case Action::TRIM: + var.SetValue(QString::fromStdString(var.Value()) + .trimmed() + .toStdString()); + break; + case Action::CHANGE_CASE: + HandleCaseChange(var); + break; + default: + break; + } + return true; +} + +static void populateCaseTypeSelection(QComboBox *list) +{ + const static std::map + caseTypes = { + {VariableActionString::CaseType::LOWER_CASE, + "AdvSceneSwitcher.action.variable.case.type.lowerCase"}, + {VariableActionString::CaseType::UPPER_CASE, + "AdvSceneSwitcher.action.variable.case.type.upperCase"}, + {VariableActionString::CaseType::CAPITALIZED, + "AdvSceneSwitcher.action.variable.case.type.capitalized"}, + {VariableActionString::CaseType::START_CASE, + "AdvSceneSwitcher.action.variable.case.type.startCase"}, + }; + + for (const auto &[type, name] : caseTypes) { + list->addItem(obs_module_text(name.c_str()), + static_cast(type)); + } +} + +static void populateDirectionSelection(QComboBox *list) +{ + list->addItems( + QStringList() + << obs_module_text( + "AdvSceneSwitcher.action.variable.truncateOrPadDirection.left") + << obs_module_text( + "AdvSceneSwitcher.action.variable.truncateOrPadDirection.right")); +} + +VariableActionStringEdit::VariableActionStringEdit( + QWidget *parent, std::shared_ptr entryData) + : VariableActionGroupEdit(parent, entryData), + _data(entryData ? entryData->GetGroup() + : nullptr), + _substringLayout(new QVBoxLayout()), + _subStringControlsLayout(new QHBoxLayout()), + _subStringStart(new VariableSpinBox(this)), + _subStringSize(new VariableSpinBox(this)), + _subStringRegex(new RegexConfigWidget(this)), + _regexPattern(new ResizingPlainTextEdit(this, 10, 1, 1)), + _regexMatchIdx(new VariableSpinBox(this)), + _findReplaceLayout(new QHBoxLayout()), + _findRegex(new RegexConfigWidget(this)), + _findStr(new VariableTextEdit(this, 10, 1, 1)), + _replaceStr(new VariableTextEdit(this, 10, 1, 1)), + _direction(new QComboBox(this)), + _stringLength(new VariableSpinBox(this)), + _paddingCharSelection(new SingleCharSelection(this)), + _caseType(new FilterComboBox(this)) +{ + _subStringStart->setMinimum(1); + _subStringStart->setMaximum(99999); + _subStringStart->setSpecialValueText(obs_module_text( + "AdvSceneSwitcher.action.variable.subString.begin")); + _subStringSize->setMinimum(0); + _subStringSize->setMaximum(99999); + _subStringSize->setSpecialValueText(obs_module_text( + "AdvSceneSwitcher.action.variable.subString.all")); + _regexMatchIdx->setMinimum(1); + _regexMatchIdx->setMaximum(999); + _regexMatchIdx->setSuffix("."); + _replaceStr->setToolTip(obs_module_text( + "AdvSceneSwitcher.action.variable.findAndReplace.replace.tooltip")); + _stringLength->setMaximum(999); + populateDirectionSelection(_direction); + populateCaseTypeSelection(_caseType); + + QWidget::connect( + _subStringStart, + SIGNAL(NumberVariableChanged(const NumberVariable &)), + this, SLOT(SubStringStartChanged(const NumberVariable &))); + QWidget::connect( + _subStringSize, + SIGNAL(NumberVariableChanged(const NumberVariable &)), + this, SLOT(SubStringSizeChanged(const NumberVariable &))); + QWidget::connect(_subStringRegex, + SIGNAL(RegexConfigChanged(const RegexConfig &)), this, + SLOT(SubStringRegexChanged(const RegexConfig &))); + QWidget::connect(_regexPattern, SIGNAL(textChanged()), this, + SLOT(RegexPatternChanged())); + QWidget::connect( + _regexMatchIdx, + SIGNAL(NumberVariableChanged(const NumberVariable &)), + this, SLOT(RegexMatchIdxChanged(const NumberVariable &))); + QWidget::connect(_findRegex, + SIGNAL(RegexConfigChanged(const RegexConfig &)), this, + SLOT(FindRegexChanged(const RegexConfig &))); + QWidget::connect(_findStr, SIGNAL(textChanged()), this, + SLOT(FindStrValueChanged())); + QWidget::connect(_replaceStr, SIGNAL(textChanged()), this, + SLOT(ReplaceStrValueChanged())); + QWidget::connect(_direction, SIGNAL(currentIndexChanged(int)), this, + SLOT(DirectionChanged(int))); + QWidget::connect( + _stringLength, + SIGNAL(NumberVariableChanged(const NumberVariable &)), + this, SLOT(StringLengthChanged(const NumberVariable &))); + QWidget::connect(_paddingCharSelection, + SIGNAL(CharChanged(const QString &)), this, + SLOT(CharSelectionChanged(const QString &))); + QWidget::connect(_caseType, SIGNAL(currentIndexChanged(int)), this, + SLOT(CaseTypeChanged(int))); + + PlaceWidgets( + obs_module_text( + "AdvSceneSwitcher.action.variable.layout.findAndReplace"), + _findReplaceLayout, + {{"{{findStr}}", _findStr}, + {"{{findRegex}}", _findRegex}, + {"{{replaceStr}}", _replaceStr}}, + false); + + auto regexConfigLayout = new QHBoxLayout; + regexConfigLayout->addWidget(_subStringRegex); + regexConfigLayout->addStretch(); + + _substringLayout->addLayout(_subStringControlsLayout); + _substringLayout->addWidget(_regexPattern); + _substringLayout->addLayout(regexConfigLayout); + + auto layout = new QVBoxLayout; + layout->setContentsMargins(0, 0, 0, 0); + layout->addLayout(_substringLayout); + layout->addLayout(_findReplaceLayout); + setLayout(layout); +} + +VariableActionGroupEdit * +VariableActionStringEdit::Create(QWidget *parent, + std::shared_ptr entryData) +{ + return new VariableActionStringEdit(parent, entryData); +} + +std::unordered_map +VariableActionStringEdit::GetInlineWidgets() +{ + return { + {"{{direction}}", _direction}, + {"{{stringLength}}", _stringLength}, + {"{{paddingCharSelection}}", _paddingCharSelection}, + {"{{caseType}}", _caseType}, + }; +} + +void VariableActionStringEdit::UpdateEntryData() +{ + if (!_data) { + return; + } + + _subStringStart->SetValue(_data->_subStringStart); + _subStringSize->SetValue(_data->_subStringSize); + _subStringRegex->SetRegexConfig(_data->_subStringRegex); + _findRegex->SetRegexConfig(_data->_findRegex); + _regexPattern->setPlainText( + QString::fromStdString(_data->_regexPattern)); + _regexMatchIdx->SetValue(_data->_regexMatchIdx); + _findStr->setPlainText(_data->_findStr); + _replaceStr->setPlainText(_data->_replaceStr); + _direction->setCurrentIndex(static_cast(_data->_direction)); + _stringLength->SetValue(_data->_stringLength); + _paddingCharSelection->setText(QChar::fromLatin1(_data->_paddingChar)); + _caseType->setCurrentIndex( + _caseType->findData(static_cast(_data->_caseType))); + _loading = false; +} + +void VariableActionStringEdit::SetWidgetVisibility( + MacroActionVariable::Action action) +{ + if (!_data) { + return; + } + + const std::unordered_map substringWidgets = { + {"{{subStringRegex}}", _subStringRegex}, + {"{{subStringStart}}", _subStringStart}, + {"{{subStringSize}}", _subStringSize}, + {"{{regexMatchIdx}}", _regexMatchIdx}, + }; + for (const auto &[_, widget] : substringWidgets) { + _subStringControlsLayout->removeWidget(widget); + } + ClearLayout(_subStringControlsLayout); + + const bool showRegex = _data->_subStringRegex.Enabled(); + PlaceWidgets( + obs_module_text( + showRegex + ? "AdvSceneSwitcher.action.variable.layout.substringRegex" + : "AdvSceneSwitcher.action.variable.layout.substringIndex"), + _subStringControlsLayout, substringWidgets); + _subStringStart->setVisible(!showRegex); + _subStringSize->setVisible(!showRegex); + _regexMatchIdx->setVisible(showRegex); + + const bool isSubstring = action == Action::SUBSTRING; + const bool isFindAndReplace = action == Action::FIND_AND_REPLACE; + SetLayoutVisible(_substringLayout, isSubstring); + _regexPattern->setVisible(showRegex && isSubstring); + SetLayoutVisible(_findReplaceLayout, isFindAndReplace); + setVisible(isSubstring || isFindAndReplace); + + UpdateSize(); +} + +void VariableActionStringEdit::SubStringStartChanged( + const NumberVariable &start) +{ + GUARD_LOADING_AND_LOCK(); + _data->_subStringStart = start; +} + +void VariableActionStringEdit::SubStringSizeChanged( + const NumberVariable &size) +{ + GUARD_LOADING_AND_LOCK(); + _data->_subStringSize = size; +} + +void VariableActionStringEdit::SubStringRegexChanged(const RegexConfig &conf) +{ + GUARD_LOADING_AND_LOCK(); + _data->_subStringRegex = conf; + SetWidgetVisibility(_entryData->_action); +} + +void VariableActionStringEdit::RegexPatternChanged() +{ + GUARD_LOADING_AND_LOCK(); + _data->_regexPattern = _regexPattern->toPlainText().toStdString(); + UpdateSize(); +} + +void VariableActionStringEdit::RegexMatchIdxChanged( + const NumberVariable &index) +{ + GUARD_LOADING_AND_LOCK(); + _data->_regexMatchIdx = index; +} + +void VariableActionStringEdit::FindStrValueChanged() +{ + GUARD_LOADING_AND_LOCK(); + _data->_findStr = _findStr->toPlainText().toStdString(); + UpdateSize(); +} + +void VariableActionStringEdit::FindRegexChanged(const RegexConfig &conf) +{ + GUARD_LOADING_AND_LOCK(); + _data->_findRegex = conf; + UpdateSize(); +} + +void VariableActionStringEdit::ReplaceStrValueChanged() +{ + GUARD_LOADING_AND_LOCK(); + _data->_replaceStr = _replaceStr->toPlainText().toStdString(); + UpdateSize(); +} + +void VariableActionStringEdit::DirectionChanged(int value) +{ + GUARD_LOADING_AND_LOCK(); + _data->_direction = static_cast(value); +} + +void VariableActionStringEdit::StringLengthChanged( + const NumberVariable &value) +{ + GUARD_LOADING_AND_LOCK(); + _data->_stringLength = value; +} + +void VariableActionStringEdit::CharSelectionChanged(const QString &character) +{ + GUARD_LOADING_AND_LOCK(); + if (character.isEmpty()) { + _data->_paddingChar = ' '; + } else { + _data->_paddingChar = character.toStdString().at(0); + } +} + +void VariableActionStringEdit::CaseTypeChanged(int index) +{ + GUARD_LOADING_AND_LOCK(); + _data->_caseType = static_cast( + _caseType->itemData(index).toInt()); +} + +} // namespace advss diff --git a/lib/macro/macro-action-variable-string.hpp b/lib/macro/macro-action-variable-string.hpp new file mode 100644 index 00000000..f4f0e55f --- /dev/null +++ b/lib/macro/macro-action-variable-string.hpp @@ -0,0 +1,95 @@ +#pragma once +#include "macro-action-variable-group.hpp" +#include "regex-config.hpp" +#include "resizing-text-edit.hpp" +#include "single-char-selection.hpp" +#include "variable-spinbox.hpp" + +namespace advss { + +class VariableActionString : public VariableActionGroup { +public: + std::unique_ptr Copy() const override; + void Save(obs_data_t *, const MacroActionVariable &) const override; + void Load(obs_data_t *, MacroActionVariable &) override; + void ResolveVariables() override; + bool Perform(MacroActionVariable &, MacroActionVariable::Action, + Variable &) override; + + enum class Direction { LEFT, RIGHT }; + enum class CaseType { + LOWER_CASE, + UPPER_CASE, + CAPITALIZED, + START_CASE, + }; + + IntVariable _subStringStart = 0; + IntVariable _subStringSize = 0; + RegexConfig _subStringRegex = RegexConfig::PartialMatchRegexConfig(); + std::string _regexPattern = ".*"; + IntVariable _regexMatchIdx = 0; + RegexConfig _findRegex; + StringVariable _findStr = obs_module_text( + "AdvSceneSwitcher.action.variable.findAndReplace.find"); + StringVariable _replaceStr = obs_module_text( + "AdvSceneSwitcher.action.variable.findAndReplace.replace"); + Direction _direction = Direction::LEFT; + IntVariable _stringLength = 1; + char _paddingChar = '0'; + CaseType _caseType = CaseType::LOWER_CASE; + +private: + void HandleIndexSubString(Variable &); + void HandleRegexSubString(Variable &); + void HandleFindAndReplace(Variable &); + void HandleCaseChange(Variable &); +}; + +class VariableActionStringEdit : public VariableActionGroupEdit { + Q_OBJECT + +public: + VariableActionStringEdit(QWidget *parent, + std::shared_ptr entryData); + static VariableActionGroupEdit * + Create(QWidget *parent, std::shared_ptr entryData); + std::unordered_map GetInlineWidgets() override; + void UpdateEntryData() override; + void SetWidgetVisibility(MacroActionVariable::Action) override; + +private slots: + void SubStringStartChanged(const NumberVariable &start); + void SubStringSizeChanged(const NumberVariable &size); + void SubStringRegexChanged(const RegexConfig &conf); + void RegexPatternChanged(); + void RegexMatchIdxChanged(const NumberVariable &index); + void FindStrValueChanged(); + void FindRegexChanged(const RegexConfig &conf); + void ReplaceStrValueChanged(); + void DirectionChanged(int); + void StringLengthChanged(const NumberVariable &); + void CharSelectionChanged(const QString &); + void CaseTypeChanged(int index); + +private: + VariableActionString *_data; + + QVBoxLayout *_substringLayout; + QHBoxLayout *_subStringControlsLayout; + VariableSpinBox *_subStringStart; + VariableSpinBox *_subStringSize; + RegexConfigWidget *_subStringRegex; + ResizingPlainTextEdit *_regexPattern; + VariableSpinBox *_regexMatchIdx; + QHBoxLayout *_findReplaceLayout; + RegexConfigWidget *_findRegex; + VariableTextEdit *_findStr; + VariableTextEdit *_replaceStr; + QComboBox *_direction; + VariableSpinBox *_stringLength; + SingleCharSelection *_paddingCharSelection; + FilterComboBox *_caseType; +}; + +} // namespace advss diff --git a/lib/macro/macro-action-variable.cpp b/lib/macro/macro-action-variable.cpp index 4bcdd308..c1b3db5f 100644 --- a/lib/macro/macro-action-variable.cpp +++ b/lib/macro/macro-action-variable.cpp @@ -1,335 +1,45 @@ #include "macro-action-variable.hpp" -#include "help-icon.hpp" -#include "json-helpers.hpp" #include "layout-helpers.hpp" -#include "macro-condition-edit.hpp" -#include "macro-signals.hpp" -#include "math-helpers.hpp" -#include "macro.hpp" -#include "non-modal-dialog.hpp" -#include "source-helpers.hpp" -#include "ui-helpers.hpp" -#include "utility.hpp" - -#include +#include "macro-action-variable-group.hpp" namespace advss { const std::string MacroActionVariable::id = "variable"; -std::vector MacroActionVariable::GetTempVarRefs() const -{ - auto refs = MacroSegment::GetTempVarRefs(); - if (_tempVar.HasValidID()) { - refs.push_back(_tempVar); - } - return refs; -} - bool MacroActionVariable::_registered = MacroActionFactory::Register( MacroActionVariable::id, {MacroActionVariable::Create, MacroActionVariableEdit::Create, "AdvSceneSwitcher.action.variable"}); -static void apppend(Variable &var, const std::string &value) +MacroActionVariable::MacroActionVariable(Macro *m) : MacroAction(m) { - auto currentValue = var.Value(); - var.SetValue(currentValue + value); -} - -static void modifyNumValue(Variable &var, double val, const bool increment) -{ - auto current = var.DoubleValue(); - if (!current.has_value()) { - return; - } - - if (increment) { - var.SetValue(*current + val); - } else { - var.SetValue(*current - val); + for (const auto &group : GetVariableActionGroups()) { + _groups.emplace_back(group.createData()); } } -MacroActionVariable::~MacroActionVariable() +MacroActionVariable::MacroActionVariable(const MacroActionVariable &other) + : MacroAction(other), + _action(other._action), + _variable(other._variable), + _variable2(other._variable2), + _strValue(other._strValue) { - DecrementCurrentSegmentVariableRef(); -} - -void MacroActionVariable::HandleIndexSubString(Variable *var) -{ - const auto curValue = var->Value(); - const auto subStringSize = _subStringSize.GetValue(); - const auto subStringStart = _subStringStart.GetValue() - 1; - - try { - if (subStringSize == 0) { - var->SetValue(curValue.substr(subStringStart)); - return; - } - - var->SetValue(curValue.substr(subStringStart, subStringSize)); - } catch (const std::out_of_range &) { - vblog(LOG_WARNING, - "invalid start index \"%d\" selected for substring of \"%s\" of variable \"%s\"", - subStringStart, curValue.c_str(), var->Name().c_str()); + for (const auto &group : other._groups) { + _groups.emplace_back(group->Copy()); } } -void MacroActionVariable::HandleRegexSubString(Variable *var) +MacroActionVariable::~MacroActionVariable() = default; + +std::vector MacroActionVariable::GetTempVarRefs() const { - const auto curValue = var->Value(); - auto regex = _subStringRegex.GetRegularExpression(_regexPattern); - if (!regex.isValid()) { - return; - } - - auto it = regex.globalMatch(QString::fromStdString(curValue)); - const auto regexMatchIndex = _regexMatchIdx.GetValue() - 1; - for (int idx = 0; idx < regexMatchIndex; idx++) { - if (!it.hasNext()) { - return; - } - it.next(); - } - - if (!it.hasNext()) { - return; - } - - auto match = it.next(); - var->SetValue(match.captured(0).toStdString()); -} - -void MacroActionVariable::HandleFindAndReplace(Variable *var) -{ - auto value = var->Value(); - if (!_findRegex.Enabled()) { - ReplaceAll(value, _findStr, _replaceStr); - var->SetValue(value); - return; - } - - QString replacement = QString::fromStdString(_replaceStr); - auto regex = _findRegex.GetRegularExpression(_findStr); - auto result = QString::fromStdString(value); - result = result.replace(regex, replacement); - var->SetValue(result.toStdString()); -} - -void MacroActionVariable::HandleMathExpression(Variable *var) -{ - auto result = EvalMathExpression(_mathExpression); - if (std::holds_alternative(result)) { - blog(LOG_WARNING, "%s", std::get(result).c_str()); - return; - } - var->SetValue(std::get(result)); -} - -void MacroActionVariable::HandleCaseChange(Variable *var) -{ - auto value = QString::fromStdString(var->Value()); - - switch (_caseType) { - case CaseType::LOWER_CASE: - var->SetValue(value.toLower().toStdString()); - break; - case CaseType::UPPER_CASE: - var->SetValue(value.toUpper().toStdString()); - break; - case CaseType::CAPITALIZED: { - if (value.isEmpty()) { - return; - } - - value[0] = value[0].toUpper(); - var->SetValue(value.toStdString()); - break; - } - case CaseType::START_CASE: { - auto partList = value.split(QRegularExpression("\\b")); - - for (auto &part : partList) { - if (part.isEmpty()) { - continue; - } - - part[0] = part[0].toUpper(); - } - - var->SetValue(partList.join("").toStdString()); - - break; - } - } -} -struct GetSceneItemNameHelper { - int curIdx = 0; - int targetIdx = 0; - std::string name = ""; -}; - -static bool getSceneItemAtIdx(obs_scene_t *, obs_sceneitem_t *item, void *ptr) -{ - auto data = reinterpret_cast(ptr); - if (obs_sceneitem_is_group(item)) { - obs_scene_t *scene = obs_sceneitem_group_get_scene(item); - obs_scene_enum_items(scene, getSceneItemAtIdx, ptr); - } - if (data->curIdx == data->targetIdx) { - data->name = - obs_source_get_name(obs_sceneitem_get_source(item)); - data->curIdx--; - return false; - } - data->curIdx--; - return true; -} - -void MacroActionVariable::SetToSceneItemName(Variable *var) -{ - auto weakSource = _scene.GetScene(); - if (!weakSource) { - var->SetValue(""); - return; - } - auto index = _sceneItemIndex.GetValue(); - if (index < 1) { - var->SetValue(""); - return; - } - auto sceneItemCount = GetSceneItemCount(weakSource); - OBSSourceAutoRelease source = obs_weak_source_get_source(weakSource); - auto scene = obs_scene_from_source(source); - GetSceneItemNameHelper data{sceneItemCount, index}; - obs_scene_enum_items(scene, getSceneItemAtIdx, &data); - var->SetValue(data.name); -} - -void MacroActionVariable::GenerateRandomNumber(Variable *var) -{ - static std::random_device rd; - static std::mt19937 gen(rd()); - - double start = _randomNumberStart; - double end = _randomNumberEnd; - - if (start > end) { - std::swap(start, end); - } - - if (_generateInteger) { - std::uniform_int_distribution dis(start, end); - var->SetValue(dis(gen)); - } else { - std::uniform_real_distribution dis(start, end); - var->SetValue(dis(gen)); - } -} - -void MacroActionVariable::PickRandomValue(Variable *var) -{ - static std::random_device rd; - static std::mt19937 gen(rd()); - static const uint8_t maxIter = 255; - - if (_randomValues.isEmpty()) { - return; - } - - if (_randomValues.size() == 1) { - const auto &value = _randomValues.at(0); - var->SetValue(value); - _lastRandomValue = value; - } - - std::uniform_int_distribution dis(0, _randomValues.size() - 1); - StringVariable value = _randomValues.at(dis(gen)); - - uint8_t iter = 0; - for (; !_allowRepeatValues && iter < maxIter && - _lastRandomValue.has_value() && - std::string(value) == _lastRandomValue.value(); - iter++) { - value = _randomValues.at(dis(gen)); - } - - if (iter == maxIter) { - blog(LOG_INFO, - "giving up picking non-repeat random value after %d tries", - maxIter); - } - - var->SetValue(value); - _lastRandomValue = value; -} - -struct AskForInputParams { - AskForInputParams(const QString &prompt_, const QString &placeholder_, - ValueInputType type_, const QStringList &options_) - : prompt(prompt_), - placeholder(placeholder_), - type(type_), - options(options_){}; - QString prompt; - QString placeholder; - ValueInputType type; - QStringList options; - std::optional result; - std::atomic_bool resultReady = {false}; -}; - -static void askForInput(void *param) -{ - auto parameters = - *static_cast *>(param); - NonModalMessageDialog *dialog = nullptr; - if (parameters->type == ValueInputType::String) { - dialog = new NonModalMessageDialog( - parameters->prompt, NonModalMessageDialog::Type::INPUT, - false); - dialog->SetInput(parameters->placeholder); - } else { - dialog = new NonModalMessageDialog( - parameters->prompt, parameters->type, - parameters->placeholder.toStdString(), - parameters->options); - } - parameters->result = dialog->GetInput(); - parameters->resultReady = true; -} - -static std::string truncate(const std::string &input, - MacroActionVariable::Direction direction, - size_t length) -{ - if (input.length() <= length) { - return input; - } - - if (direction == MacroActionVariable::Direction::RIGHT) { - return input.substr(0, length); - } else { - return input.substr(input.length() - length); - } -} - -static std::string pad(const std::string &input, - MacroActionVariable::Direction direction, size_t length, - char paddingChar) -{ - if (input.length() >= length) { - return input; - } - - if (direction == MacroActionVariable::Direction::RIGHT) { - return input + - std::string(length - input.length(), paddingChar); - } else { - return std::string(length - input.length(), paddingChar) + - input; + auto refs = MacroSegment::GetTempVarRefs(); + for (const auto &group : _groups) { + auto groupRefs = group->GetTempVarRefs(); + refs.insert(refs.end(), groupRefs.begin(), groupRefs.end()); } + return refs; } bool MacroActionVariable::PerformAction() @@ -339,208 +49,12 @@ bool MacroActionVariable::PerformAction() return true; } - switch (_action) { - case Action::SET_VALUE: - var->SetValue(_strValue); - break; - case Action::APPEND: - apppend(*var, _strValue); - break; - case Action::COPY_VAR: { - auto var2 = _variable2.lock(); - if (!var2) { - return true; - } - var->SetValue(var2->Value()); - break; - } - case Action::APPEND_VAR: { - auto var2 = _variable2.lock(); - if (!var2) { - return true; - } - apppend(*var, var2->Value()); - break; - } - case Action::INCREMENT: - modifyNumValue(*var, _numValue.GetValue(), true); - break; - case Action::DECREMENT: - modifyNumValue(*var, _numValue.GetValue(), false); - break; - case Action::SET_CONDITION_VALUE: - case Action::SET_ACTION_VALUE: { - auto m = GetMacro(); - if (!m) { - return true; - } - if (GetSegmentIndexValue() == -1) { - return true; - } - auto segment = _macroSegment.lock(); - if (!segment) { - return true; - } - var->SetValue(segment->GetVariableValue()); - break; - } - case Action::ROUND_TO_INT: { - auto curValue = var->DoubleValue(); - if (!curValue.has_value()) { - return true; - } - var->SetValue(std::to_string(int(std::round(*curValue)))); - return true; - } - case Action::SUBSTRING: { - if (_subStringRegex.Enabled()) { - HandleRegexSubString(var.get()); - return true; - } - HandleIndexSubString(var.get()); - return true; - } - case Action::FIND_AND_REPLACE: { - HandleFindAndReplace(var.get()); - return true; - } - case Action::MATH_EXPRESSION: { - HandleMathExpression(var.get()); - return true; - } - case Action::USER_INPUT: { - QStringList options; - for (const auto &option : _inputOptions) { - options << QString::fromStdString(option); - } - auto params = std::make_shared( - _useCustomPrompt - ? QString::fromStdString(_inputPrompt) - : QString(obs_module_text( - "AdvSceneSwitcher.action.variable.askForValuePromptDefault")) - .arg(QString::fromStdString( - var->Name())), - _useCustomPrompt && _useInputPlaceholder - ? QString::fromStdString(_inputPlaceholder) - : "", - _inputType, options); - obs_queue_task(OBS_TASK_UI, askForInput, ¶ms, false); - while (!params->resultReady) { - if (GetMacro()->GetStop()) { - return false; - } - std::this_thread::sleep_for( - std::chrono::milliseconds(100)); - } - if (!params->result.has_value()) { - return true; - } - var->SetValue(*params->result); - return true; - } - case Action::ENV_VARIABLE: { - auto value = std::getenv(_envVariableName.c_str()); - if (value) { - var->SetValue(value); - } - return true; - } - case Action::SCENE_ITEM_COUNT: { - var->SetValue(GetSceneItemCount(_scene.GetScene(false))); - return true; - } - case Action::STRING_LENGTH: { - var->SetValue(std::string(_strValue).length()); - return true; - } - case Action::EXTRACT_JSON: { - auto value = GetJsonField(var->Value(), _strValue); - if (!value.has_value()) { - return true; - } - var->SetValue(*value); - return true; - } - case Action::QUERY_JSON: { - auto value = QueryJson(var->Value(), _jsonQuery); - if (!value.has_value()) { - return true; - } - - if (!_jsonQueryExtractSingle) { - var->SetValue(*value); - return true; - } - - auto extracted = ExtractSingleJsonArrayElement(*value); - if (extracted.has_value()) { - var->SetValue(*extracted); - } else { - var->SetValue(*value); - } - - return true; - } - case Action::ARRAY_JSON: { - auto value = AccessJsonArrayIndex(var->Value(), _jsonIndex); - if (!value.has_value()) { - return true; - } - var->SetValue(*value); - return true; - } - case Action::SET_TO_TEMPVAR: { - auto tempVar = _tempVar.GetTempVariable(GetMacro()); - if (!tempVar) { - return true; - } - auto value = tempVar->Value(); - if (!value) { - return true; - } - var->SetValue(*value); - return true; - } - case Action::SCENE_ITEM_NAME: - SetToSceneItemName(var.get()); - return true; - case Action::PAD: - var->SetValue(pad(var->Value(), _direction, _stringLength, - _paddingChar)); - return true; - case Action::TRUNCATE: - var->SetValue( - truncate(var->Value(), _direction, _stringLength)); - return true; - case Action::SWAP_VALUES: { - auto var2 = _variable2.lock(); - if (!var2) { - return true; - } - - auto tempValue = var->Value(); - var->SetValue(var2->Value()); - var2->SetValue(tempValue); - return true; - } - case Action::TRIM: { - var->SetValue(QString::fromStdString(var->Value()) - .trimmed() - .toStdString()); - return true; - } - case Action::CHANGE_CASE: - HandleCaseChange(var.get()); - return true; - case Action::RANDOM_NUMBER: - GenerateRandomNumber(var.get()); - return true; - case Action::RANDOM_LIST_VALUE: - PickRandomValue(var.get()); + const int idx = GetVariableActionGroupIndex(_action); + if (idx < 0 || idx >= (int)_groups.size()) { return true; } - return true; + return _groups[idx]->Perform(*this, _action, *var); } bool MacroActionVariable::Save(obs_data_t *obj) const @@ -551,44 +65,11 @@ bool MacroActionVariable::Save(obs_data_t *obj) const obs_data_set_string(obj, "variable2Name", GetWeakVariableName(_variable2).c_str()); _strValue.Save(obj, "strValue"); - _numValue.Save(obj, "numValue"); obs_data_set_int(obj, "condition", static_cast(_action)); - obs_data_set_int(obj, "segmentIdx", GetSegmentIndexValue()); - _subStringStart.Save(obj, "subStringStart"); - _subStringSize.Save(obj, "subStringSize"); - obs_data_set_string(obj, "regexPattern", _regexPattern.c_str()); - _regexMatchIdx.Save(obj, "regexMatchIdx"); - _findRegex.Save(obj, "findRegex"); - _findStr.Save(obj, "findStr"); - _replaceStr.Save(obj, "replaceStr"); - _subStringRegex.Save(obj); - _mathExpression.Save(obj, "mathExpression"); - obs_data_set_bool(obj, "useCustomPrompt", _useCustomPrompt); - _inputPrompt.Save(obj, "inputPrompt"); - obs_data_set_bool(obj, "useInputPlaceholder", _useInputPlaceholder); - _inputPlaceholder.Save(obj, "inputPlaceholder"); - obs_data_set_int(obj, "inputType", static_cast(_inputType)); - _inputOptions.Save(obj, "inputOptions", "value"); - _envVariableName.Save(obj, "environmentVariableName"); - _scene.Save(obj); - _tempVar.Save(obj, GetMacro()); - _sceneItemIndex.Save(obj, "sceneItemIndex"); - obs_data_set_int(obj, "direction", static_cast(_direction)); - _stringLength.Save(obj, "stringLength"); - obs_data_set_int(obj, "paddingChar", _paddingChar); - obs_data_set_int(obj, "caseType", static_cast(_caseType)); - _randomNumberStart.Save(obj, "randomNumberStart"); - _randomNumberEnd.Save(obj, "randomNumberEnd"); - obs_data_set_bool(obj, "generateInteger", _generateInteger); - _randomValues.Save(obj, "randomValues", "value"); - obs_data_set_bool(obj, "allowRepeatValues", _allowRepeatValues); - _jsonQuery.Save(obj, "jsonQuery"); - _jsonIndex.Save(obj, "jsonIndex"); - obs_data_set_bool(obj, "jsonQueryExtractSingle", - _jsonQueryExtractSingle); - + for (const auto &group : _groups) { + group->Save(obj, *this); + } obs_data_set_int(obj, "version", 1); - return true; } @@ -601,65 +82,17 @@ bool MacroActionVariable::Load(obs_data_t *obj) obs_data_get_string(obj, "variable2Name")); _strValue.Load(obj, "strValue"); _action = static_cast(obs_data_get_int(obj, "condition")); - _segmentIdxLoadValue = obs_data_get_int(obj, "segmentIdx"); - _subStringRegex.Load(obj); - _regexPattern = obs_data_get_string(obj, "regexPattern"); - _findRegex.Load(obj, "findRegex"); - _findStr.Load(obj, "findStr"); - _replaceStr.Load(obj, "replaceStr"); - _mathExpression.Load(obj, "mathExpression"); - _useCustomPrompt = obs_data_get_bool(obj, "useCustomPrompt"); - _inputPrompt.Load(obj, "inputPrompt"); - _useInputPlaceholder = obs_data_get_bool(obj, "useInputPlaceholder"); - _inputPlaceholder.Load(obj, "inputPlaceholder"); - obs_data_set_default_int(obj, "inputType", - static_cast(ValueInputType::String)); - _inputType = - static_cast(obs_data_get_int(obj, "inputType")); - _inputOptions.Load(obj, "inputOptions", "value"); - _envVariableName.Load(obj, "environmentVariableName"); - _scene.Load(obj); - _tempVar.Load(obj, GetMacro()); - _sceneItemIndex.Load(obj, "sceneItemIndex"); - _direction = static_cast(obs_data_get_int(obj, "direction")); - _stringLength.Load(obj, "stringLength"); - if (obs_data_has_user_value(obj, "paddingChar")) { - _paddingChar = obs_data_get_int(obj, "paddingChar"); - } else { - _paddingChar = ' '; + for (const auto &group : _groups) { + group->Load(obj, *this); } - _caseType = static_cast(obs_data_get_int(obj, "caseType")); - - // Convert old data format - // TODO: Remove in future version - if (!obs_data_has_user_value(obj, "version")) { - _numValue = obs_data_get_double(obj, "numValue"); - _subStringStart = obs_data_get_int(obj, "subStringStart") + 1; - _subStringSize = obs_data_get_int(obj, "subStringSize"); - _regexMatchIdx = obs_data_get_int(obj, "regexMatchIdx") + 1; - } else { - _numValue.Load(obj, "numValue"); - _subStringStart.Load(obj, "subStringStart"); - _subStringSize.Load(obj, "subStringSize"); - _regexMatchIdx.Load(obj, "regexMatchIdx"); - } - - _randomNumberStart.Load(obj, "randomNumberStart"); - _randomNumberEnd.Load(obj, "randomNumberEnd"); - _generateInteger = obs_data_get_bool(obj, "generateInteger"); - _randomValues.Load(obj, "randomValues", "value"); - _allowRepeatValues = obs_data_get_bool(obj, "allowRepeatValues"); - _jsonQuery.Load(obj, "jsonQuery"); - _jsonIndex.Load(obj, "jsonIndex"); - _jsonQueryExtractSingle = - obs_data_get_bool(obj, "jsonQueryExtractSingle"); - return true; } bool MacroActionVariable::PostLoad() { - SetSegmentIndexValue(_segmentIdxLoadValue); + for (const auto &group : _groups) { + group->PostLoad(*this); + } return true; } @@ -678,214 +111,28 @@ std::shared_ptr MacroActionVariable::Copy() const return std::make_shared(*this); } -void MacroActionVariable::SetSegmentIndexValue(int value) -{ - DecrementCurrentSegmentVariableRef(); - - auto m = GetMacro(); - if (!m) { - _macroSegment.reset(); - return; - } - - if (value < 0) { - _macroSegment.reset(); - return; - } - - std::shared_ptr segment; - if (_action == Action::SET_CONDITION_VALUE) { - if (value < (int)m->Conditions().size()) { - segment = m->Conditions().at(value); - } - } else if (_action == Action::SET_ACTION_VALUE) { - if (value < (int)m->Actions().size()) { - segment = m->Actions().at(value); - } - } - - _macroSegment = segment; - if (segment) { - IncrementVariableRef(segment.get()); - } -} - -int MacroActionVariable::GetSegmentIndexValue() const -{ - auto m = GetMacro(); - if (!m) { - return -1; - } - - auto segment = _macroSegment.lock(); - if (!segment) { - return -1; - } - - if (_action == Action::SET_CONDITION_VALUE) { - auto it = std::find(m->Conditions().begin(), - m->Conditions().end(), segment); - if (it != m->Conditions().end()) { - return std::distance(m->Conditions().begin(), it); - } - return -1; - } else if (_action == Action::SET_ACTION_VALUE) { - auto it = std::find(m->Actions().begin(), m->Actions().end(), - segment); - if (it != m->Actions().end()) { - return std::distance(m->Actions().begin(), it); - } - return -1; - } - - return -1; -} - void MacroActionVariable::ResolveVariablesToFixedValues() { _strValue.ResolveVariables(); - _findStr.ResolveVariables(); - _replaceStr.ResolveVariables(); - _mathExpression.ResolveVariables(); - _inputPrompt.ResolveVariables(); - _inputPlaceholder.ResolveVariables(); - _inputOptions.ResolveVariables(); - _envVariableName.ResolveVariables(); - _scene.ResolveVariables(); - _sceneItemIndex.ResolveVariables(); - _jsonQuery.ResolveVariables(); - _jsonIndex.ResolveVariables(); -} - -void MacroActionVariable::DecrementCurrentSegmentVariableRef() -{ - auto segment = _macroSegment.lock(); - if (!segment) { - return; + for (const auto &group : _groups) { + group->ResolveVariables(); } - - DecrementVariableRef(segment.get()); } -static inline void populateActionSelection(QComboBox *list) +static void populateActionSelection(QComboBox *list) { - static const std::vector, std::string>> - actions = { - {MacroActionVariable::Action::SET_VALUE, - "AdvSceneSwitcher.action.variable.type.set"}, - {MacroActionVariable::Action::COPY_VAR, - "AdvSceneSwitcher.action.variable.type.copy"}, - {MacroActionVariable::Action::APPEND, - "AdvSceneSwitcher.action.variable.type.append"}, - {MacroActionVariable::Action::PAD, - "AdvSceneSwitcher.action.variable.type.padValue"}, - {MacroActionVariable::Action::TRUNCATE, - "AdvSceneSwitcher.action.variable.type.truncateValue"}, - {MacroActionVariable::Action::SUBSTRING, - "AdvSceneSwitcher.action.variable.type.subString"}, - {MacroActionVariable::Action::FIND_AND_REPLACE, - "AdvSceneSwitcher.action.variable.type.findAndReplace"}, - {MacroActionVariable::Action::STRING_LENGTH, - "AdvSceneSwitcher.action.variable.type.stringLength"}, - {MacroActionVariable::Action::TRIM, - "AdvSceneSwitcher.action.variable.type.trim"}, - {MacroActionVariable::Action::CHANGE_CASE, - "AdvSceneSwitcher.action.variable.type.changeCase"}, - {true, ""}, // Separator - - {MacroActionVariable::Action::EXTRACT_JSON, - "AdvSceneSwitcher.action.variable.type.extractJsonField"}, - {MacroActionVariable::Action::QUERY_JSON, - "AdvSceneSwitcher.action.variable.type.queryJson"}, - {MacroActionVariable::Action::ARRAY_JSON, - "AdvSceneSwitcher.action.variable.type.accessJsonArray"}, - {true, ""}, // Separator - - {MacroActionVariable::Action::SET_TO_TEMPVAR, - "AdvSceneSwitcher.action.variable.type.setToTempvar"}, - {MacroActionVariable::Action::SET_CONDITION_VALUE, - "AdvSceneSwitcher.action.variable.type.setConditionValue"}, - {MacroActionVariable::Action::SET_ACTION_VALUE, - "AdvSceneSwitcher.action.variable.type.setActionValue"}, - {true, ""}, // Separator - - {MacroActionVariable::Action::MATH_EXPRESSION, - "AdvSceneSwitcher.action.variable.type.mathExpression"}, - {MacroActionVariable::Action::INCREMENT, - "AdvSceneSwitcher.action.variable.type.increment"}, - {MacroActionVariable::Action::DECREMENT, - "AdvSceneSwitcher.action.variable.type.decrement"}, - {MacroActionVariable::Action::ROUND_TO_INT, - "AdvSceneSwitcher.action.variable.type.roundToInt"}, - {MacroActionVariable::Action::RANDOM_NUMBER, - "AdvSceneSwitcher.action.variable.type.randomNumber"}, - {MacroActionVariable::Action::RANDOM_LIST_VALUE, - "AdvSceneSwitcher.action.variable.type.randomListValue"}, - {true, ""}, // Separator - - {MacroActionVariable::Action::USER_INPUT, - "AdvSceneSwitcher.action.variable.type.askForValue"}, - {MacroActionVariable::Action::ENV_VARIABLE, - "AdvSceneSwitcher.action.variable.type.environmentVariable"}, - {true, ""}, // Separator - - {MacroActionVariable::Action::SCENE_ITEM_NAME, - "AdvSceneSwitcher.action.variable.type.sceneItemName"}, - {MacroActionVariable::Action::SCENE_ITEM_COUNT, - "AdvSceneSwitcher.action.variable.type.sceneItemCount"}, - {true, ""}, // Separator - - {MacroActionVariable::Action::SWAP_VALUES, - "AdvSceneSwitcher.action.variable.type.swapValues"}, - {MacroActionVariable::Action::APPEND_VAR, - "AdvSceneSwitcher.action.variable.type.appendVar"}, - - }; - - for (const auto &[action, name] : actions) { - if (std::holds_alternative(action)) { - list->insertSeparator(actions.size()); - continue; + const auto &groups = GetVariableActionGroups(); + for (size_t idx = 0; idx < groups.size(); idx++) { + if (idx > 0) { + list->insertSeparator(list->count()); + } + for (const auto &option : groups[idx].options) { + list->addItem(obs_module_text(option.label), + static_cast(option.action)); } - - list->addItem( - obs_module_text(name.c_str()), - static_cast( - std::get(action))); } } -static inline void populateCaseTypeSelection(QComboBox *list) -{ - const static std::map - caseTypes = { - {MacroActionVariable::CaseType::LOWER_CASE, - "AdvSceneSwitcher.action.variable.case.type.lowerCase"}, - {MacroActionVariable::CaseType::UPPER_CASE, - "AdvSceneSwitcher.action.variable.case.type.upperCase"}, - {MacroActionVariable::CaseType::CAPITALIZED, - "AdvSceneSwitcher.action.variable.case.type.capitalized"}, - {MacroActionVariable::CaseType::START_CASE, - "AdvSceneSwitcher.action.variable.case.type.startCase"}, - }; - - for (const auto &[type, name] : caseTypes) { - list->addItem(obs_module_text(name.c_str()), - static_cast(type)); - } -} - -static inline void populateDirectionSelection(QComboBox *list) -{ - list->addItems( - QStringList() - << obs_module_text( - "AdvSceneSwitcher.action.variable.truncateOrPadDirection.left") - << obs_module_text( - "AdvSceneSwitcher.action.variable.truncateOrPadDirection.right")); -} - MacroActionVariableEdit::MacroActionVariableEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent), @@ -893,110 +140,9 @@ MacroActionVariableEdit::MacroActionVariableEdit( _variables2(new VariableSelection(this)), _actions(new FilterComboBox(this)), _strValue(new VariableTextEdit(this, 5, 1, 1)), - _numValue(new VariableDoubleSpinBox(this)), - _segmentIdx(new MacroSegmentSelection( - this, MacroSegmentSelection::Type::CONDITION, false)), - _segmentValueStatus(new QLabel()), - _segmentValue(new ResizingPlainTextEdit(this, 10, 1, 1)), - _substringLayout(new QVBoxLayout()), - _subStringControlsLayout(new QHBoxLayout()), - _subStringStart(new VariableSpinBox(this)), - _subStringSize(new VariableSpinBox(this)), - _subStringRegex(new RegexConfigWidget(parent)), - _regexPattern(new ResizingPlainTextEdit(this, 10, 1, 1)), - _regexMatchIdx(new VariableSpinBox(this)), - _findReplaceLayout(new QHBoxLayout()), - _findRegex(new RegexConfigWidget()), - _findStr(new VariableTextEdit(this, 10, 1, 1)), - _replaceStr(new VariableTextEdit(this, 10, 1, 1)), - _mathExpression(new VariableLineEdit(this)), - _mathExpressionResult(new QLabel()), - _promptLayout(new QHBoxLayout()), - _useCustomPrompt(new QCheckBox()), - _inputPrompt(new VariableLineEdit(this)), - _placeholderLayout(new QHBoxLayout()), - _useInputPlaceholder(new QCheckBox()), - _inputPlaceholder(new VariableLineEdit(this)), - _inputTypeLayout(new QHBoxLayout()), - _inputType(new QComboBox(this)), - _inputOptions(new StringListEdit(this)), - _envVariable(new VariableLineEdit(this)), - _scenes(new SceneSelectionWidget(this, true, false, true, true, - true)), - _tempVars(new TempVariableSelection(this)), - _tempVarsHelp(new HelpIcon(obs_module_text( - "AdvSceneSwitcher.action.variable.type.setToTempvar.help"))), - _sceneItemIndex(new VariableSpinBox(this)), - _direction(new QComboBox()), - _stringLength(new VariableSpinBox(this)), - _paddingCharSelection(new SingleCharSelection()), - _caseType(new FilterComboBox(this)), - _randomNumberStart(new VariableDoubleSpinBox(this)), - _randomNumberEnd(new VariableDoubleSpinBox(this)), - _generateInteger(new QCheckBox( - obs_module_text( - "AdvSceneSwitcher.action.variable.generateInteger"), - this)), - _randomNumberLayout(new QVBoxLayout()), - _randomValues(new StringListEdit(this)), - _allowRepeatValues(new QCheckBox( - obs_module_text( - "AdvSceneSwitcher.action.variable.type.allowRepeat"), - this)), - _randomValueLayout(new QVBoxLayout()), - _jsonQuery(new VariableLineEdit(this)), - _jsonQueryHelp(new HelpIcon( - obs_module_text( - "AdvSceneSwitcher.action.variable.type.queryJson.info"), - this)), - _jsonIndex(new VariableSpinBox(this)), - _jsonExtractSingle(new QCheckBox( - obs_module_text( - "AdvSceneSwitcher.action.variable.type.queryJson.extractSingle"), - this)), - _jsonExtractSingleHelp(new HelpIcon( - obs_module_text( - "AdvSceneSwitcher.action.variable.type.queryJson.extractSingle.info"), - this)), - _jsonQueryLayout(new QVBoxLayout()), _entryLayout(new QHBoxLayout()) { - _numValue->setMinimum(-9999999999); - _numValue->setMaximum(9999999999); - _segmentValue->setReadOnly(true); - _subStringStart->setMinimum(1); - _subStringStart->setMaximum(99999); - _subStringStart->setSpecialValueText(obs_module_text( - "AdvSceneSwitcher.action.variable.subString.begin")); - _subStringSize->setMinimum(0); - _subStringSize->setMaximum(99999); - _subStringSize->setSpecialValueText(obs_module_text( - "AdvSceneSwitcher.action.variable.subString.all")); - _regexMatchIdx->setMinimum(1); - _regexMatchIdx->setMaximum(999); - _regexMatchIdx->setSuffix("."); - _replaceStr->setToolTip(obs_module_text( - "AdvSceneSwitcher.action.variable.findAndReplace.replace.tooltip")); - _inputPrompt->setSizePolicy(QSizePolicy::MinimumExpanding, - QSizePolicy::Preferred); - _sceneItemIndex->setMinimum(1); - _stringLength->setMaximum(999); populateActionSelection(_actions); - populateDirectionSelection(_direction); - populateCaseTypeSelection(_caseType); - PopulateValueInputTypeSelection(_inputType); - _randomNumberStart->setMinimum(-9999999999); - _randomNumberStart->setMaximum(9999999999); - _randomNumberEnd->setMinimum(-9999999999); - _randomNumberEnd->setMaximum(9999999999); - _randomValues->SetMaxStringSize(99999999); - _inputOptions->SetMaxStringSize(99999999); - _jsonIndex->setMaximum(999); - auto jsonExtractSingleLayout = new QHBoxLayout(); - jsonExtractSingleLayout->addWidget(_jsonExtractSingle); - jsonExtractSingleLayout->addWidget(_jsonExtractSingleHelp); - jsonExtractSingleLayout->addStretch(); - _jsonQueryLayout->addLayout(jsonExtractSingleLayout); QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)), this, SLOT(VariableChanged(const QString &))); @@ -1006,205 +152,19 @@ MacroActionVariableEdit::MacroActionVariableEdit( SLOT(ActionChanged(int))); QWidget::connect(_strValue, SIGNAL(textChanged()), this, SLOT(StrValueChanged())); - QWidget::connect( - _numValue, - SIGNAL(NumberVariableChanged(const NumberVariable &)), - this, SLOT(NumValueChanged(const NumberVariable &))); - QWidget::connect(_segmentIdx, - SIGNAL(SelectionChanged(const IntVariable &)), this, - SLOT(SegmentIndexChanged(const IntVariable &))); - QWidget::connect(MacroSignalManager::Instance(), - SIGNAL(SegmentOrderChanged()), this, - SLOT(MacroSegmentOrderChanged())); - QWidget::connect( - _subStringStart, - SIGNAL(NumberVariableChanged(const NumberVariable &)), - this, SLOT(SubStringStartChanged(const NumberVariable &))); - QWidget::connect( - _subStringSize, - SIGNAL(NumberVariableChanged(const NumberVariable &)), - this, SLOT(SubStringSizeChanged(const NumberVariable &))); - QWidget::connect(_subStringRegex, - SIGNAL(RegexConfigChanged(const RegexConfig &)), this, - SLOT(SubStringRegexChanged(const RegexConfig &))); - QWidget::connect(_regexPattern, SIGNAL(textChanged()), this, - SLOT(RegexPatternChanged())); - QWidget::connect( - _regexMatchIdx, - SIGNAL(NumberVariableChanged(const NumberVariable &)), - this, SLOT(RegexMatchIdxChanged(const NumberVariable &))); - QWidget::connect(_findRegex, - SIGNAL(RegexConfigChanged(const RegexConfig &)), this, - SLOT(FindRegexChanged(const RegexConfig &))); - QWidget::connect(_findStr, SIGNAL(textChanged()), this, - SLOT(FindStrValueChanged())); - QWidget::connect(_replaceStr, SIGNAL(textChanged()), this, - SLOT(ReplaceStrValueChanged())); - QWidget::connect(_mathExpression, SIGNAL(editingFinished()), this, - SLOT(MathExpressionChanged())); - QWidget::connect(_useCustomPrompt, SIGNAL(stateChanged(int)), this, - SLOT(UseCustomPromptChanged(int))); - QWidget::connect(_inputPrompt, SIGNAL(editingFinished()), this, - SLOT(InputPromptChanged())); - QWidget::connect(_useInputPlaceholder, SIGNAL(stateChanged(int)), this, - SLOT(UseInputPlaceholderChanged(int))); - QWidget::connect(_inputPlaceholder, SIGNAL(editingFinished()), this, - SLOT(InputPlaceholderChanged())); - QWidget::connect(_inputType, SIGNAL(currentIndexChanged(int)), this, - SLOT(InputTypeChanged(int))); - QWidget::connect(_inputOptions, - SIGNAL(StringListChanged(const StringList &)), this, - SLOT(InputOptionsChanged(const StringList &))); - QWidget::connect(_envVariable, SIGNAL(editingFinished()), this, - SLOT(EnvVariableChanged())); - QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)), - this, SLOT(SceneChanged(const SceneSelection &))); - QWidget::connect(_tempVars, - SIGNAL(SelectionChanged(const TempVariableRef &)), - this, SLOT(SelectionChanged(const TempVariableRef &))); - QWidget::connect( - _sceneItemIndex, - SIGNAL(NumberVariableChanged(const NumberVariable &)), - this, SLOT(SceneItemIndexChanged(const NumberVariable &))); - QWidget::connect(_direction, SIGNAL(currentIndexChanged(int)), this, - SLOT(DirectionChanged(int))); - QWidget::connect( - _stringLength, - SIGNAL(NumberVariableChanged(const NumberVariable &)), - this, SLOT(StringLengthChanged(const NumberVariable &))); - QWidget::connect(_paddingCharSelection, - SIGNAL(CharChanged(const QString &)), this, - SLOT(CharSelectionChanged(const QString &))); - QWidget::connect(_caseType, SIGNAL(currentIndexChanged(int)), this, - SLOT(CaseTypeChanged(int))); - QWidget::connect( - _randomNumberStart, - SIGNAL(NumberVariableChanged(const NumberVariable &)), - this, - SLOT(RandomNumberStartChanged(const NumberVariable &))); - QWidget::connect( - _randomNumberEnd, - SIGNAL(NumberVariableChanged(const NumberVariable &)), - this, - SLOT(RandomNumberEndChanged(const NumberVariable &))); - QWidget::connect(_generateInteger, SIGNAL(stateChanged(int)), this, - SLOT(GenerateIntegerChanged(int))); - QWidget::connect(_randomValues, - SIGNAL(StringListChanged(const StringList &)), this, - SLOT(RandomValueListChanged(const StringList &))); - QWidget::connect(_allowRepeatValues, SIGNAL(stateChanged(int)), this, - SLOT(AllowRepeatValuesChanged(int))); - QWidget::connect(_jsonQuery, SIGNAL(editingFinished()), this, - SLOT(JsonQueryChanged())); - QWidget::connect( - _jsonIndex, - SIGNAL(NumberVariableChanged(const NumberVariable &)), - this, SLOT(JsonIndexChanged(const NumberVariable &))); - QWidget::connect(_jsonExtractSingle, SIGNAL(stateChanged(int)), this, - SLOT(JsonQueryExtractSingleChanged(int))); - - const std::unordered_map widgetPlaceholders = { - {"{{variables}}", _variables}, - {"{{variables2}}", _variables2}, - {"{{actions}}", _actions}, - {"{{strValue}}", _strValue}, - {"{{numValue}}", _numValue}, - {"{{segmentIndex}}", _segmentIdx}, - {"{{findRegex}}", _findRegex}, - {"{{findStr}}", _findStr}, - {"{{replaceStr}}", _replaceStr}, - {"{{mathExpression}}", _mathExpression}, - {"{{useCustomPrompt}}", _useCustomPrompt}, - {"{{inputPrompt}}", _inputPrompt}, - {"{{useInputPlaceholder}}", _useInputPlaceholder}, - {"{{inputPlaceholder}}", _inputPlaceholder}, - {"{{inputType}}", _inputType}, - {"{{envVariableName}}", _envVariable}, - {"{{scenes}}", _scenes}, - {"{{tempVars}}", _tempVars}, - {"{{tempVarsHelp}}", _tempVarsHelp}, - {"{{sceneItemIndex}}", _sceneItemIndex}, - {"{{direction}}", _direction}, - {"{{stringLength}}", _stringLength}, - {"{{paddingCharSelection}}", _paddingCharSelection}, - {"{{caseType}}", _caseType}, - {"{{randomNumberStart}}", _randomNumberStart}, - {"{{randomNumberEnd}}", _randomNumberEnd}, - {"{{generateInteger}}", _generateInteger}, - {"{{jsonQuery}}", _jsonQuery}, - {"{{jsonQueryHelp}}", _jsonQueryHelp}, - {"{{jsonIndex}}", _jsonIndex}, - }; - PlaceWidgets(obs_module_text( - "AdvSceneSwitcher.action.variable.layout.other"), - _entryLayout, widgetPlaceholders); - - PlaceWidgets( - obs_module_text( - "AdvSceneSwitcher.action.variable.layout.substringIndex"), - _subStringControlsLayout, widgetPlaceholders); - - PlaceWidgets( - obs_module_text( - "AdvSceneSwitcher.action.variable.layout.findAndReplace"), - _findReplaceLayout, widgetPlaceholders, false); - - PlaceWidgets( - obs_module_text( - "AdvSceneSwitcher.action.variable.layout.userInput.type"), - _inputTypeLayout, widgetPlaceholders); - PlaceWidgets( - obs_module_text( - "AdvSceneSwitcher.action.variable.layout.userInput.customPrompt"), - _promptLayout, widgetPlaceholders); - PlaceWidgets( - obs_module_text( - "AdvSceneSwitcher.action.variable.layout.userInput.placeholder"), - _placeholderLayout, widgetPlaceholders); - - auto randomLayout = new QHBoxLayout(); - PlaceWidgets( - obs_module_text( - "AdvSceneSwitcher.action.variable.layout.randomNumber"), - randomLayout, widgetPlaceholders); - _randomNumberLayout->addLayout(randomLayout); - _randomNumberLayout->addWidget(_generateInteger); - - _randomValueLayout->addWidget(_randomValues); - _randomValueLayout->addWidget(_allowRepeatValues); - - auto regexConfigLayout = new QHBoxLayout; - regexConfigLayout->addWidget(_subStringRegex); - regexConfigLayout->addStretch(); - - _substringLayout->addLayout(_subStringControlsLayout); - _substringLayout->addWidget(_regexPattern); - _substringLayout->addLayout(regexConfigLayout); auto layout = new QVBoxLayout; layout->addLayout(_entryLayout); - layout->addLayout(_jsonQueryLayout); - layout->addLayout(_substringLayout); - layout->addWidget(_segmentValueStatus); - layout->addWidget(_segmentValue); - layout->addLayout(_findReplaceLayout); - layout->addWidget(_mathExpressionResult); - layout->addLayout(_inputTypeLayout); - layout->addWidget(_inputOptions); - layout->addLayout(_promptLayout); - layout->addLayout(_placeholderLayout); - layout->addLayout(_randomNumberLayout); - layout->addLayout(_randomValueLayout); + for (const auto &group : GetVariableActionGroups()) { + auto groupEdit = group.createEdit(this, entryData); + _groupEdits.push_back(groupEdit); + layout->addWidget(groupEdit); + } setLayout(layout); _entryData = entryData; UpdateEntryData(); _loading = false; - - UpdateSegmentVariableValue(); - connect(&_timer, SIGNAL(timeout()), this, - SLOT(UpdateSegmentVariableValue())); - _timer.start(1500); } void MacroActionVariableEdit::UpdateEntryData() @@ -1218,49 +178,9 @@ void MacroActionVariableEdit::UpdateEntryData() _actions->setCurrentIndex( _actions->findData(static_cast(_entryData->_action))); _strValue->setPlainText(_entryData->_strValue); - _numValue->SetValue(_entryData->_numValue); - _segmentIdx->SetValue(_entryData->GetSegmentIndexValue() + 1); - _segmentIdx->SetMacro(_entryData->GetMacro()); - _segmentIdx->SetType( - _entryData->_action == - MacroActionVariable::Action::SET_CONDITION_VALUE - ? MacroSegmentSelection::Type::CONDITION - : MacroSegmentSelection::Type::ACTION); - _subStringStart->SetValue(_entryData->_subStringStart); - _subStringSize->SetValue(_entryData->_subStringSize); - _subStringRegex->SetRegexConfig(_entryData->_subStringRegex); - _findRegex->SetRegexConfig(_entryData->_findRegex); - _regexPattern->setPlainText( - QString::fromStdString(_entryData->_regexPattern)); - _regexMatchIdx->SetValue(_entryData->_regexMatchIdx); - _findStr->setPlainText(_entryData->_findStr); - _replaceStr->setPlainText(_entryData->_replaceStr); - _mathExpression->setText(_entryData->_mathExpression); - _useCustomPrompt->setChecked(_entryData->_useCustomPrompt); - _inputPrompt->setText(_entryData->_inputPrompt); - _useInputPlaceholder->setChecked(_entryData->_useInputPlaceholder); - _inputPlaceholder->setText(_entryData->_inputPlaceholder); - _inputType->setCurrentIndex( - _inputType->findData(static_cast(_entryData->_inputType))); - _inputOptions->SetStringList(_entryData->_inputOptions); - _envVariable->setText(_entryData->_envVariableName); - _scenes->SetScene(_entryData->_scene); - _tempVars->SetVariable(_entryData->_tempVar); - _sceneItemIndex->SetValue(_entryData->_sceneItemIndex); - _direction->setCurrentIndex(static_cast(_entryData->_direction)); - _stringLength->SetValue(_entryData->_stringLength); - _paddingCharSelection->setText( - QChar::fromLatin1(_entryData->_paddingChar)); - _caseType->setCurrentIndex( - _caseType->findData(static_cast(_entryData->_caseType))); - _randomNumberStart->SetValue(_entryData->_randomNumberStart); - _randomNumberEnd->SetValue(_entryData->_randomNumberEnd); - _generateInteger->setChecked(_entryData->_generateInteger); - _allowRepeatValues->setChecked(_entryData->_allowRepeatValues); - _randomValues->SetStringList(_entryData->_randomValues); - _jsonQuery->setText(_entryData->_jsonQuery); - _jsonIndex->SetValue(_entryData->_jsonIndex); - _jsonExtractSingle->setChecked(_entryData->_jsonQueryExtractSingle); + for (const auto &groupEdit : _groupEdits) { + groupEdit->UpdateEntryData(); + } SetWidgetVisibility(); } @@ -1288,14 +208,6 @@ void MacroActionVariableEdit::ActionChanged(int idx) auto lock = LockContext(); _entryData->_action = static_cast( _actions->itemData(idx).toInt()); - - if (_entryData->_action == - MacroActionVariable::Action::SET_ACTION_VALUE) { - _segmentIdx->SetType(MacroSegmentSelection::Type::ACTION); - } else if (_entryData->_action == - MacroActionVariable::Action::SET_CONDITION_VALUE) { - _segmentIdx->SetType(MacroSegmentSelection::Type::CONDITION); - } SetWidgetVisibility(); } @@ -1307,343 +219,19 @@ void MacroActionVariableEdit::StrValueChanged() updateGeometry(); } -void MacroActionVariableEdit::NumValueChanged( - const NumberVariable &value) +std::unordered_map +MacroActionVariableEdit::GetInlineWidgets() const { - GUARD_LOADING_AND_LOCK(); - _entryData->_numValue = value; -} - -void MacroActionVariableEdit::SegmentIndexChanged(const IntVariable &val) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->SetSegmentIndexValue(val - 1); -} - -void MacroActionVariableEdit::SetSegmentValueError(const QString &text) -{ - _segmentValueStatus->setText(text); - _segmentValue->setPlainText(""); - _segmentValue->hide(); - - adjustSize(); - updateGeometry(); -} - -void MacroActionVariableEdit::UpdateSegmentVariableValue() -{ - if (!_entryData || - !(_entryData->_action == - MacroActionVariable::Action::SET_CONDITION_VALUE || - _entryData->_action == - MacroActionVariable::Action::SET_ACTION_VALUE)) { - return; + std::unordered_map widgets = { + {"{{variables}}", _variables}, + {"{{variables2}}", _variables2}, + {"{{actions}}", _actions}, + {"{{strValue}}", _strValue}, + }; + for (const auto &groupEdit : _groupEdits) { + widgets.merge(groupEdit->GetInlineWidgets()); } - - auto m = _entryData->GetMacro(); - if (!m) { - return; - } - - int index = _entryData->GetSegmentIndexValue(); - if (index < 0) { - SetSegmentValueError(obs_module_text( - "AdvSceneSwitcher.action.variable.invalidSelection")); - const QSignalBlocker b(_segmentIdx); - _segmentIdx->SetValue(0); - return; - } - - std::shared_ptr segment; - if (_entryData->_action == - MacroActionVariable::Action::SET_ACTION_VALUE) { - const auto &actions = m->Actions(); - if (index < (int)actions.size()) { - segment = actions.at(index); - } - } else if (_entryData->_action == - MacroActionVariable::Action::SET_CONDITION_VALUE) { - const auto &conditions = m->Conditions(); - if (index < (int)conditions.size()) { - segment = conditions.at(index); - } - } - - if (!segment) { - SetSegmentValueError(obs_module_text( - "AdvSceneSwitcher.action.variable.invalidSelection")); - return; - } - - if (!SupportsVariableValue(segment.get())) { - std::string type; - QString fmt; - - if (_entryData->_action == - MacroActionVariable::Action::SET_ACTION_VALUE) { - type = MacroActionFactory::GetActionName( - segment->GetId()); - fmt = QString(obs_module_text( - "AdvSceneSwitcher.action.variable.actionNoVariableSupport")); - } else if (_entryData->_action == - MacroActionVariable::Action::SET_CONDITION_VALUE) { - type = MacroConditionFactory::GetConditionName( - segment->GetId()); - fmt = QString(obs_module_text( - "AdvSceneSwitcher.action.variable.conditionNoVariableSupport")); - } - SetSegmentValueError( - fmt.arg(QString(obs_module_text(type.c_str())))); - return; - } - - _segmentValueStatus->setText(obs_module_text( - "AdvSceneSwitcher.action.variable.currentSegmentValue")); - _segmentValue->show(); - - // Only update the text if the value changed to prevent possible text - // selections being lost - auto previousText = _segmentValue->toPlainText(); - auto newText = QString::fromStdString(segment->GetVariableValue()); - if (newText != previousText) { - _segmentValue->setPlainText(newText); - } - - adjustSize(); - updateGeometry(); -} - -void MacroActionVariableEdit::MacroSegmentOrderChanged() -{ - const QSignalBlocker b(_segmentIdx); - _segmentIdx->SetValue(_entryData->GetSegmentIndexValue() + 1); -} - -void MacroActionVariableEdit::SubStringStartChanged( - const NumberVariable &start) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_subStringStart = start; -} - -void MacroActionVariableEdit::SubStringSizeChanged( - const NumberVariable &size) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_subStringSize = size; -} - -void MacroActionVariableEdit::SubStringRegexChanged(const RegexConfig &conf) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_subStringRegex = conf; - - SetWidgetVisibility(); -} - -void MacroActionVariableEdit::RegexPatternChanged() -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_regexPattern = _regexPattern->toPlainText().toStdString(); - adjustSize(); - updateGeometry(); -} - -void MacroActionVariableEdit::RegexMatchIdxChanged( - const NumberVariable &index) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_regexMatchIdx = index; -} - -void MacroActionVariableEdit::FindStrValueChanged() -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_findStr = _findStr->toPlainText().toStdString(); - adjustSize(); - updateGeometry(); -} - -void MacroActionVariableEdit::FindRegexChanged(const RegexConfig &conf) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_findRegex = conf; - adjustSize(); - updateGeometry(); -} - -void MacroActionVariableEdit::ReplaceStrValueChanged() -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_replaceStr = _replaceStr->toPlainText().toStdString(); - adjustSize(); - updateGeometry(); -} - -void MacroActionVariableEdit::MathExpressionChanged() -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_mathExpression = _mathExpression->text().toStdString(); - - // In case of invalid expression display an error - auto result = EvalMathExpression(_entryData->_mathExpression); - auto hasError = std::holds_alternative(result); - if (hasError) { - _mathExpressionResult->setText( - QString::fromStdString(std::get(result))); - } - _mathExpressionResult->setVisible(hasError); - - adjustSize(); - updateGeometry(); -} - -void MacroActionVariableEdit::UseCustomPromptChanged(int value) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_useCustomPrompt = value; - SetWidgetVisibility(); -} - -void MacroActionVariableEdit::InputPromptChanged() -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_inputPrompt = _inputPrompt->text().toStdString(); -} - -void MacroActionVariableEdit::UseInputPlaceholderChanged(int value) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_useInputPlaceholder = value; - SetWidgetVisibility(); -} - -void MacroActionVariableEdit::InputPlaceholderChanged() -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_inputPlaceholder = _inputPlaceholder->text().toStdString(); -} - -void MacroActionVariableEdit::InputTypeChanged(int) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_inputType = - static_cast(_inputType->currentData().toInt()); - SetWidgetVisibility(); -} - -void MacroActionVariableEdit::InputOptionsChanged(const StringList &options) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_inputOptions = options; -} - -void MacroActionVariableEdit::EnvVariableChanged() -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_envVariableName = _envVariable->text().toStdString(); -} - -void MacroActionVariableEdit::SceneChanged(const SceneSelection &scene) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_scene = scene; -} - -void MacroActionVariableEdit::SelectionChanged(const TempVariableRef &var) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_tempVar = var; - IncrementTempVarInUseGeneration(); - SetWidgetVisibility(); -} - -void MacroActionVariableEdit::SceneItemIndexChanged( - const NumberVariable &value) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_sceneItemIndex = value; -} - -void MacroActionVariableEdit::DirectionChanged(int value) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_direction = - static_cast(value); -} - -void MacroActionVariableEdit::StringLengthChanged( - const NumberVariable &value) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_stringLength = value; -} - -void MacroActionVariableEdit::CharSelectionChanged(const QString &character) -{ - GUARD_LOADING_AND_LOCK(); - if (character.isEmpty()) { - _entryData->_paddingChar = ' '; - } else { - _entryData->_paddingChar = character.toStdString().at(0); - } -} - -void MacroActionVariableEdit::CaseTypeChanged(int index) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_caseType = static_cast( - _caseType->itemData(index).toInt()); -} - -void MacroActionVariableEdit::RandomNumberStartChanged( - const NumberVariable &value) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_randomNumberStart = value; -} - -void MacroActionVariableEdit::RandomNumberEndChanged( - const NumberVariable &value) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_randomNumberEnd = value; -} - -void MacroActionVariableEdit::GenerateIntegerChanged(int value) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_generateInteger = value; -} - -void MacroActionVariableEdit::RandomValueListChanged(const StringList &values) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_randomValues = values; -} - -void MacroActionVariableEdit::AllowRepeatValuesChanged(int value) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_allowRepeatValues = value; -} - -void MacroActionVariableEdit::JsonQueryChanged() -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_jsonQuery = _jsonQuery->text().toStdString(); -} - -void MacroActionVariableEdit::JsonIndexChanged(const NumberVariable &value) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_jsonIndex = value; -} - -void MacroActionVariableEdit::JsonQueryExtractSingleChanged(int value) -{ - GUARD_LOADING_AND_LOCK(); - _entryData->_jsonQueryExtractSingle = value; + return widgets; } void MacroActionVariableEdit::SetWidgetVisibility() @@ -1652,209 +240,34 @@ void MacroActionVariableEdit::SetWidgetVisibility() return; } - const std::unordered_map widgetPlaceholders = { - {"{{variables}}", _variables}, - {"{{variables2}}", _variables2}, - {"{{actions}}", _actions}, - {"{{strValue}}", _strValue}, - {"{{numValue}}", _numValue}, - {"{{segmentIndex}}", _segmentIdx}, - {"{{mathExpression}}", _mathExpression}, - {"{{envVariableName}}", _envVariable}, - {"{{scenes}}", _scenes}, - {"{{tempVars}}", _tempVars}, - {"{{tempVarsHelp}}", _tempVarsHelp}, - {"{{sceneItemIndex}}", _sceneItemIndex}, - {"{{direction}}", _direction}, - {"{{stringLength}}", _stringLength}, - {"{{paddingCharSelection}}", _paddingCharSelection}, - {"{{caseType}}", _caseType}, - {"{{jsonQuery}}", _jsonQuery}, - {"{{jsonQueryHelp}}", _jsonQueryHelp}, - {"{{jsonIndex}}", _jsonIndex}, - {"{{subStringRegex}}", _subStringRegex}, - {"{{subStringStart}}", _subStringStart}, - {"{{subStringSize}}", _subStringSize}, - {"{{regexMatchIdx}}", _regexMatchIdx}, - }; + const auto option = GetVariableActionOption(_entryData->_action); + if (!option) { + return; + } - for (const auto &[_, widget] : widgetPlaceholders) { + const auto widgets = GetInlineWidgets(); + for (const auto &[_, widget] : widgets) { _entryLayout->removeWidget(widget); - _subStringControlsLayout->removeWidget(widget); } - ClearLayout(_entryLayout); - ClearLayout(_subStringControlsLayout); - const char *layoutString = ""; - if (_entryData->_action == MacroActionVariable::Action::PAD) { - layoutString = obs_module_text( - "AdvSceneSwitcher.action.variable.layout.pad"); - } else if (_entryData->_action == - MacroActionVariable::Action::TRUNCATE) { - layoutString = obs_module_text( - "AdvSceneSwitcher.action.variable.layout.truncate"); - } else if (_entryData->_action == - MacroActionVariable::Action::COPY_VAR) { - layoutString = obs_module_text( - "AdvSceneSwitcher.action.variable.layout.copy"); - } else { - layoutString = obs_module_text( - "AdvSceneSwitcher.action.variable.layout.other"); + const std::string layoutString = obs_module_text(option->layout); + PlaceWidgets(layoutString, _entryLayout, widgets); + + for (const auto &[placeholder, widget] : widgets) { + widget->setVisible(layoutString.find(placeholder) != + std::string::npos); } - PlaceWidgets(layoutString, _entryLayout, widgetPlaceholders); - - if (_entryData->_action == MacroActionVariable::Action::SET_VALUE || - _entryData->_action == MacroActionVariable::Action::APPEND || - _entryData->_action == - MacroActionVariable::Action::MATH_EXPRESSION || - _entryData->_action == MacroActionVariable::Action::ENV_VARIABLE || - _entryData->_action == MacroActionVariable::Action::STRING_LENGTH || - _entryData->_action == MacroActionVariable::Action::EXTRACT_JSON || - _entryData->_action == MacroActionVariable::Action::QUERY_JSON) { + if (option->expandInlineRow) { RemoveStretchIfPresent(_entryLayout); } else { AddStretchIfNecessary(_entryLayout); } - _variables2->setVisible( - _entryData->_action == MacroActionVariable::Action::COPY_VAR || - _entryData->_action == - MacroActionVariable::Action::APPEND_VAR || - _entryData->_action == - MacroActionVariable::Action::SWAP_VALUES); - _strValue->setVisible( - _entryData->_action == MacroActionVariable::Action::SET_VALUE || - _entryData->_action == MacroActionVariable::Action::APPEND || - _entryData->_action == - MacroActionVariable::Action::STRING_LENGTH || - _entryData->_action == - MacroActionVariable::Action::EXTRACT_JSON); - _numValue->setVisible( - _entryData->_action == MacroActionVariable::Action::INCREMENT || - _entryData->_action == MacroActionVariable::Action::DECREMENT); - _segmentValueStatus->setVisible( - _entryData->_action == - MacroActionVariable::Action::SET_ACTION_VALUE || - _entryData->_action == - MacroActionVariable::Action::SET_CONDITION_VALUE); - _segmentValue->setVisible( - _entryData->_action == - MacroActionVariable::Action::SET_ACTION_VALUE || - _entryData->_action == - MacroActionVariable::Action::SET_CONDITION_VALUE); - _segmentIdx->setVisible( - _entryData->_action == - MacroActionVariable::Action::SET_ACTION_VALUE || - _entryData->_action == - MacroActionVariable::Action::SET_CONDITION_VALUE); - - bool showRegex = _entryData->_subStringRegex.Enabled(); - layoutString = - showRegex - ? "AdvSceneSwitcher.action.variable.layout.substringRegex" - : "AdvSceneSwitcher.action.variable.layout.substringIndex"; - PlaceWidgets(obs_module_text(layoutString), _subStringControlsLayout, - widgetPlaceholders); - _subStringStart->setVisible(!showRegex); - _subStringSize->setVisible(!showRegex); - _regexMatchIdx->setVisible(showRegex); - - SetLayoutVisible(_substringLayout, - _entryData->_action == - MacroActionVariable::Action::SUBSTRING); - _regexPattern->setVisible( - showRegex && - _entryData->_action == MacroActionVariable::Action::SUBSTRING); - - SetLayoutVisible(_findReplaceLayout, - _entryData->_action == - MacroActionVariable::Action::FIND_AND_REPLACE); - _mathExpression->setVisible( - _entryData->_action == - MacroActionVariable::Action::MATH_EXPRESSION); - _mathExpressionResult->hide(); - SetLayoutVisible(_inputTypeLayout, - _entryData->_action == - MacroActionVariable::Action::USER_INPUT); - _inputOptions->setVisible( - _entryData->_action == - MacroActionVariable::Action::USER_INPUT && - _entryData->_inputType == ValueInputType::Selection); - SetLayoutVisible(_promptLayout, - _entryData->_action == - MacroActionVariable::Action::USER_INPUT); - _inputPrompt->setVisible( - _entryData->_action == - MacroActionVariable::Action::USER_INPUT && - _entryData->_useCustomPrompt); - if (_entryData->_useCustomPrompt) { - RemoveStretchIfPresent(_promptLayout); - } else { - AddStretchIfNecessary(_promptLayout); + for (const auto &groupEdit : _groupEdits) { + groupEdit->SetWidgetVisibility(_entryData->_action); } - SetLayoutVisible( - _placeholderLayout, - _entryData->_action == - MacroActionVariable::Action::USER_INPUT && - _entryData->_useCustomPrompt); - _useInputPlaceholder->setVisible( - _entryData->_action == - MacroActionVariable::Action::USER_INPUT && - _entryData->_useCustomPrompt); - _inputPlaceholder->setVisible( - _entryData->_action == - MacroActionVariable::Action::USER_INPUT && - _entryData->_useCustomPrompt && - _entryData->_useInputPlaceholder); - if (_entryData->_useInputPlaceholder) { - RemoveStretchIfPresent(_placeholderLayout); - } else { - AddStretchIfNecessary(_placeholderLayout); - } - _envVariable->setVisible(_entryData->_action == - MacroActionVariable::Action::ENV_VARIABLE); - _scenes->setVisible( - _entryData->_action == - MacroActionVariable::Action::SCENE_ITEM_COUNT || - _entryData->_action == - MacroActionVariable::Action::SCENE_ITEM_NAME); - _tempVars->setVisible(_entryData->_action == - MacroActionVariable::Action::SET_TO_TEMPVAR); - _tempVarsHelp->setVisible( - _entryData->_action == - MacroActionVariable::Action::SET_TO_TEMPVAR && - !_entryData->_tempVar.HasValidID()); - _sceneItemIndex->setVisible( - _entryData->_action == - MacroActionVariable::Action::SCENE_ITEM_NAME); - _direction->setVisible( - _entryData->_action == MacroActionVariable::Action::PAD || - _entryData->_action == MacroActionVariable::Action::TRUNCATE); - _stringLength->setVisible( - _entryData->_action == MacroActionVariable::Action::PAD || - _entryData->_action == MacroActionVariable::Action::TRUNCATE); - _paddingCharSelection->setVisible(_entryData->_action == - MacroActionVariable::Action::PAD); - _caseType->setVisible(_entryData->_action == - MacroActionVariable::Action::CHANGE_CASE); - SetLayoutVisible(_randomNumberLayout, - _entryData->_action == - MacroActionVariable::Action::RANDOM_NUMBER); - SetLayoutVisible( - _randomValueLayout, - _entryData->_action == - MacroActionVariable::Action::RANDOM_LIST_VALUE); - _jsonQuery->setVisible(_entryData->_action == - MacroActionVariable::Action::QUERY_JSON); - _jsonQueryHelp->setVisible(_entryData->_action == - MacroActionVariable::Action::QUERY_JSON); - _jsonIndex->setVisible(_entryData->_action == - MacroActionVariable::Action::ARRAY_JSON); - SetLayoutVisible(_jsonQueryLayout, - _entryData->_action == - MacroActionVariable::Action::QUERY_JSON); adjustSize(); updateGeometry(); diff --git a/lib/macro/macro-action-variable.hpp b/lib/macro/macro-action-variable.hpp index ce91fd70..cccca0ad 100644 --- a/lib/macro/macro-action-variable.hpp +++ b/lib/macro/macro-action-variable.hpp @@ -1,22 +1,20 @@ #pragma once #include "macro-action-edit.hpp" -#include "help-icon.hpp" -#include "macro-segment-selection.hpp" -#include "regex-config.hpp" -#include "resizing-text-edit.hpp" -#include "scene-selection.hpp" -#include "single-char-selection.hpp" -#include "string-list.hpp" -#include "value-input.hpp" -#include "variable-line-edit.hpp" #include "variable-text-edit.hpp" -#include "variable-spinbox.hpp" +#include "variable.hpp" + +#include +#include namespace advss { +class VariableActionGroup; +class VariableActionGroupEdit; + class MacroActionVariable : public MacroAction { public: - MacroActionVariable(Macro *m) : MacroAction(m) {} + MacroActionVariable(Macro *m); + MacroActionVariable(const MacroActionVariable &); ~MacroActionVariable(); bool PerformAction(); bool Save(obs_data_t *obj) const; @@ -27,10 +25,19 @@ public: std::vector GetTempVarRefs() const; static std::shared_ptr Create(Macro *m); std::shared_ptr Copy() const; - void SetSegmentIndexValue(int); - int GetSegmentIndexValue() const; void ResolveVariablesToFixedValues(); + template T *GetGroup() const + { + for (const auto &group : _groups) { + if (auto result = dynamic_cast(group.get())) { + return result; + } + } + return nullptr; + } + + // Values are persisted, do not change existing ones enum class Action { SET_VALUE, APPEND, @@ -66,74 +73,10 @@ public: std::weak_ptr _variable; std::weak_ptr _variable2; StringVariable _strValue = ""; - DoubleVariable _numValue = 0; - IntVariable _subStringStart = 0; - IntVariable _subStringSize = 0; - RegexConfig _subStringRegex = RegexConfig::PartialMatchRegexConfig(); - std::string _regexPattern = ".*"; - IntVariable _regexMatchIdx = 0; - RegexConfig _findRegex; - StringVariable _findStr = obs_module_text( - "AdvSceneSwitcher.action.variable.findAndReplace.find"); - StringVariable _replaceStr = obs_module_text( - "AdvSceneSwitcher.action.variable.findAndReplace.replace"); - StringVariable _mathExpression = obs_module_text( - "AdvSceneSwitcher.action.variable.mathExpression.example"); - bool _useCustomPrompt = false; - StringVariable _inputPrompt = obs_module_text( - "AdvSceneSwitcher.action.variable.askForValuePrompt"); - bool _useInputPlaceholder = false; - StringVariable _inputPlaceholder = - obs_module_text("AdvSceneSwitcher.enterText"); - ValueInputType _inputType = ValueInputType::String; - StringList _inputOptions = {"value1", "value2", "value3"}; -#ifdef _WIN32 - StringVariable _envVariableName = "USERPROFILE"; -#else - StringVariable _envVariableName = "HOME"; -#endif - SceneSelection _scene; - TempVariableRef _tempVar; - IntVariable _sceneItemIndex = 1; - - enum class Direction { LEFT, RIGHT }; - Direction _direction = Direction::LEFT; - IntVariable _stringLength = 1; - char _paddingChar = '0'; - - enum class CaseType { - LOWER_CASE, - UPPER_CASE, - CAPITALIZED, - START_CASE, - }; - - CaseType _caseType = CaseType::LOWER_CASE; - DoubleVariable _randomNumberStart = 0; - DoubleVariable _randomNumberEnd = 100; - bool _generateInteger = true; - - StringList _randomValues = {"value1", "value2", "value3"}; - bool _allowRepeatValues = true; - std::optional _lastRandomValue; - - StringVariable _jsonQuery = "$.some.nested.value"; - IntVariable _jsonIndex = 0; - bool _jsonQueryExtractSingle = true; private: - void DecrementCurrentSegmentVariableRef(); - void HandleIndexSubString(Variable *); - void HandleRegexSubString(Variable *); - void HandleFindAndReplace(Variable *); - void HandleMathExpression(Variable *); - void HandleCaseChange(Variable *); - void SetToSceneItemName(Variable *); - void GenerateRandomNumber(Variable *); - void PickRandomValue(Variable *); + std::vector> _groups; - std::weak_ptr _macroSegment; - int _segmentIdxLoadValue = -1; static bool _registered; static const std::string id; }; @@ -159,105 +102,22 @@ private slots: void Variable2Changed(const QString &); void ActionChanged(int); void StrValueChanged(); - void NumValueChanged(const NumberVariable &value); - void SegmentIndexChanged(const IntVariable &); - void UpdateSegmentVariableValue(); - void MacroSegmentOrderChanged(); - void SubStringStartChanged(const NumberVariable &start); - void SubStringSizeChanged(const NumberVariable &size); - void SubStringRegexChanged(const RegexConfig &conf); - void RegexPatternChanged(); - void RegexMatchIdxChanged(const NumberVariable &index); - void FindStrValueChanged(); - void FindRegexChanged(const RegexConfig &conf); - void ReplaceStrValueChanged(); - void MathExpressionChanged(); - void UseCustomPromptChanged(int); - void InputPromptChanged(); - void UseInputPlaceholderChanged(int); - void InputPlaceholderChanged(); - void InputTypeChanged(int); - void InputOptionsChanged(const StringList &); - void EnvVariableChanged(); - void SceneChanged(const SceneSelection &); - void SelectionChanged(const TempVariableRef &var); - void SceneItemIndexChanged(const NumberVariable &); - void DirectionChanged(int); - void StringLengthChanged(const NumberVariable &); - void CharSelectionChanged(const QString &); - void CaseTypeChanged(int index); - void RandomNumberStartChanged(const NumberVariable &); - void RandomNumberEndChanged(const NumberVariable &); - void GenerateIntegerChanged(int); - void RandomValueListChanged(const StringList &); - void AllowRepeatValuesChanged(int); - void JsonQueryChanged(); - void JsonIndexChanged(const NumberVariable &); - void JsonQueryExtractSingleChanged(int); signals: void HeaderInfoChanged(const QString &); private: void SetWidgetVisibility(); - void SetSegmentValueError(const QString &); + std::unordered_map GetInlineWidgets() const; VariableSelection *_variables; VariableSelection *_variables2; FilterComboBox *_actions; VariableTextEdit *_strValue; - VariableDoubleSpinBox *_numValue; - MacroSegmentSelection *_segmentIdx; - QLabel *_segmentValueStatus; - ResizingPlainTextEdit *_segmentValue; - QVBoxLayout *_substringLayout; - QHBoxLayout *_subStringControlsLayout; - VariableSpinBox *_subStringStart; - VariableSpinBox *_subStringSize; - RegexConfigWidget *_subStringRegex; - ResizingPlainTextEdit *_regexPattern; - VariableSpinBox *_regexMatchIdx; - QHBoxLayout *_findReplaceLayout; - RegexConfigWidget *_findRegex; - VariableTextEdit *_findStr; - VariableTextEdit *_replaceStr; - VariableLineEdit *_mathExpression; - QLabel *_mathExpressionResult; - QHBoxLayout *_promptLayout; - QCheckBox *_useCustomPrompt; - VariableLineEdit *_inputPrompt; - QHBoxLayout *_placeholderLayout; - QCheckBox *_useInputPlaceholder; - VariableLineEdit *_inputPlaceholder; - QHBoxLayout *_inputTypeLayout; - QComboBox *_inputType; - StringListEdit *_inputOptions; - VariableLineEdit *_envVariable; - SceneSelectionWidget *_scenes; - TempVariableSelection *_tempVars; - HelpIcon *_tempVarsHelp; - VariableSpinBox *_sceneItemIndex; - QComboBox *_direction; - VariableSpinBox *_stringLength; - SingleCharSelection *_paddingCharSelection; - FilterComboBox *_caseType; - VariableDoubleSpinBox *_randomNumberStart; - VariableDoubleSpinBox *_randomNumberEnd; - QCheckBox *_generateInteger; - QVBoxLayout *_randomNumberLayout; - StringListEdit *_randomValues; - QCheckBox *_allowRepeatValues; - QVBoxLayout *_randomValueLayout; - VariableLineEdit *_jsonQuery; - HelpIcon *_jsonQueryHelp; - VariableSpinBox *_jsonIndex; - QCheckBox *_jsonExtractSingle; - HelpIcon *_jsonExtractSingleHelp; - QVBoxLayout *_jsonQueryLayout; QHBoxLayout *_entryLayout; + std::vector _groupEdits; std::shared_ptr _entryData; - QTimer _timer; bool _loading = true; };