From 817de13e9d29ddb720f561f6c3955ae568a93cfa Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Thu, 26 Jun 2025 20:02:03 +0200 Subject: [PATCH] Add nested macro support --- data/locale/en-US.ini | 10 +++- lib/macro/macro-action-macro.cpp | 99 ++++++++++++++++++++++++++------ lib/macro/macro-action-macro.hpp | 21 +++---- 3 files changed, 100 insertions(+), 30 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 3cb2ec81..5dd3e046 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -150,9 +150,9 @@ AdvSceneSwitcher.macroTab.title="Macro" AdvSceneSwitcher.macroTab.macros="Macros" AdvSceneSwitcher.macroTab.priorityWarning="Note: It is recommended to configure macros to be the highest priority functionality.\nThis setting can be changed on the General tab." AdvSceneSwitcher.macroTab.help="Macros allow you to execute a string of actions depending on multiple conditions.\n\nClick on the highlighted plus symbol to add a new Macro." -AdvSceneSwitcher.macroTab.editConditionHelp="This section allows you to define Macro conditions.\n\nSelect an existing or add a new Macro on the left.\nThen click the plus button below to add a new condition." -AdvSceneSwitcher.macroTab.editActionHelp="This section allows you to define Macro actions.\n\nSelect an existing or add a new Macro on the left.\nThen click the plus button below to add a new action." -AdvSceneSwitcher.macroTab.editElseActionHelp="This section allows you to define Macro actions, which are executed if the conditions are *not* met.\n\nSelect an existing or add a new Macro on the left.\nThen click the plus button below to add a new action." +AdvSceneSwitcher.macroTab.editConditionHelp="This section allows you to define macro conditions.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new condition." +AdvSceneSwitcher.macroTab.editActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are met.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new action." +AdvSceneSwitcher.macroTab.editElseActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are *not* met.\n\nSelect an existing or add a new macro on the left.\nThen click the plus button below to add a new action." AdvSceneSwitcher.macroTab.edit="Edit macro" AdvSceneSwitcher.macroTab.edit.logic="Logic type:" AdvSceneSwitcher.macroTab.edit.condition="Condition type:" @@ -964,6 +964,10 @@ AdvSceneSwitcher.action.macro.type.stop="Stop actions" AdvSceneSwitcher.action.macro.type.disableAction="Disable action" AdvSceneSwitcher.action.macro.type.enableAction="Enable action" AdvSceneSwitcher.action.macro.type.toggleAction="Toggle action" +AdvSceneSwitcher.action.macro.type.nestedMacro="Nested macro" +AdvSceneSwitcher.action.macro.type.nestedMacro.conditionHelp="This section allows you to define macro conditions.\n\nClick the plus button below to add a new condition." +AdvSceneSwitcher.action.macro.type.nestedMacro.actionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are met.\n\nClick the plus button below to add a new action." +AdvSceneSwitcher.action.macro.type.nestedMacro.elseActionHelp="This section allows you to define macro actions.\nThe actions in this section will be performed when the conditions are *not* met.\n\nClick the plus button below to add a new action." AdvSceneSwitcher.action.macro.entry.run="{{actions}}{{actionTypes}}of{{macros}}" AdvSceneSwitcher.action.macro.entry.run.condition="{{conditionBehaviors}}of{{conditionMacros}}" AdvSceneSwitcher.action.macro.entry.other="{{actions}}{{actionIndex}}{{macros}}" diff --git a/lib/macro/macro-action-macro.cpp b/lib/macro/macro-action-macro.cpp index 6ba52584..d5bb2655 100644 --- a/lib/macro/macro-action-macro.cpp +++ b/lib/macro/macro-action-macro.cpp @@ -14,6 +14,11 @@ bool MacroActionMacro::_registered = MacroActionFactory::Register( bool MacroActionMacro::PerformAction() { + if (_action == Action::NESTED_MACRO) { + const bool conditionsMatched = _nestedMacro->CheckConditions(); + return _nestedMacro->PerformActions(conditionsMatched); + } + auto macro = _macro.GetMacro(); if (!macro) { return true; @@ -32,7 +37,7 @@ bool MacroActionMacro::PerformAction() case Action::RESET_COUNTER: macro->ResetRunCount(); break; - case Action::RUN: + case Action::RUN_ACTIONS: RunActions(macro.get()); break; case Action::STOP: @@ -80,7 +85,7 @@ void MacroActionMacro::LogAction() const ablog(LOG_INFO, "reset counter for \"%s\"", macro->Name().c_str()); break; - case Action::RUN: + case Action::RUN_ACTIONS: ablog(LOG_INFO, "run nested macro \"%s\"", macro->Name().c_str()); break; @@ -99,6 +104,9 @@ void MacroActionMacro::LogAction() const ablog(LOG_INFO, "toggled action %d of macro \"%s\"", _actionIndex.GetValue(), macro->Name().c_str()); break; + case Action::NESTED_MACRO: + ablog(LOG_INFO, "run nested macro"); + break; default: break; } @@ -111,6 +119,10 @@ bool MacroActionMacro::Save(obs_data_t *obj) const _actionIndex.Save(obj, "actionIndex"); obs_data_set_int(obj, "action", static_cast(_action)); _runOptions.Save(obj); + OBSDataAutoRelease nestedMacroData = obs_data_create(); + _nestedMacro->Save(nestedMacroData); + obs_data_set_obj(obj, "nestedMacro", nestedMacroData); + obs_data_set_int(obj, "customWidgetHeight", _customWidgetHeight); return true; } @@ -122,6 +134,16 @@ bool MacroActionMacro::Load(obs_data_t *obj) _action = static_cast( obs_data_get_int(obj, "action")); _runOptions.Load(obj); + + if (obs_data_has_user_value(obj, "nestedMacro")) { + OBSDataAutoRelease nestedMacroData = + obs_data_get_obj(obj, "nestedMacro"); + _nestedMacro->Load(nestedMacroData); + _nestedMacro->PostLoad(); + } + + _customWidgetHeight = obs_data_get_int(obj, "customWidgetHeight"); + return true; } @@ -204,7 +226,9 @@ static void populateActionSelection(QComboBox *list) "AdvSceneSwitcher.action.macro.type.togglePause"}, {MacroActionMacro::Action::RESET_COUNTER, "AdvSceneSwitcher.action.macro.type.resetCounter"}, - {MacroActionMacro::Action::RUN, + {MacroActionMacro::Action::NESTED_MACRO, + "AdvSceneSwitcher.action.macro.type.nestedMacro"}, + {MacroActionMacro::Action::RUN_ACTIONS, "AdvSceneSwitcher.action.macro.type.run"}, {MacroActionMacro::Action::STOP, "AdvSceneSwitcher.action.macro.type.stop"}, @@ -242,7 +266,7 @@ static void populateActionTypeSelection(QComboBox *list) MacroActionMacroEdit::MacroActionMacroEdit( QWidget *parent, std::shared_ptr entryData) - : QWidget(parent), + : ResizableWidget(parent), _macros(new MacroSelection(parent)), _actionIndex(new MacroSegmentSelection( this, MacroSegmentSelection::Type::ACTION)), @@ -261,7 +285,13 @@ MacroActionMacroEdit::MacroActionMacroEdit( _entryLayout(new QHBoxLayout()), _conditionLayout(new QHBoxLayout()), _reevaluateConditionStateLayout(new QHBoxLayout()), - _setInputsLayout(new QHBoxLayout()) + _setInputsLayout(new QHBoxLayout()), + _nestedMacro(new MacroEdit( + this, + QStringList() + << "AdvSceneSwitcher.action.macro.type.nestedMacro.conditionHelp" + << "AdvSceneSwitcher.action.macro.type.nestedMacro.actionHelp" + << "AdvSceneSwitcher.action.macro.type.nestedMacro.elseActionHelp")) { populateActionSelection(_actions); populateConditionBehaviorSelection(_conditionBehaviors); @@ -310,6 +340,7 @@ MacroActionMacroEdit::MacroActionMacroEdit( layout->addLayout(_setInputsLayout); layout->addWidget(_inputs); layout->addWidget(_skipWhenPaused); + layout->addWidget(_nestedMacro); setLayout(layout); _entryData = entryData; UpdateEntryData(); @@ -321,6 +352,12 @@ void HighlightMacroSettingsButton(bool enable); MacroActionMacroEdit::~MacroActionMacroEdit() { HighlightMacroSettingsButton(false); + + if (!_entryData) { + return; + } + _entryData->_customWidgetHeight = GetCustomHeight(); + _nestedMacro->SetMacro({}); // Save splitter states } void MacroActionMacroEdit::UpdateEntryData() @@ -343,9 +380,20 @@ void MacroActionMacroEdit::UpdateEntryData() _skipWhenPaused->setChecked(_entryData->_runOptions.skipWhenPaused); _setInputs->setChecked(_entryData->_runOptions.setInputs); SetupMacroInput(_entryData->_macro.GetMacro().get()); + + const auto ¯o = _entryData->_nestedMacro; + _nestedMacro->SetMacro(macro); + SetWidgetVisibility(); } +QWidget *MacroActionMacroEdit::Create(QWidget *parent, + std::shared_ptr action) +{ + return new MacroActionMacroEdit( + parent, std::dynamic_pointer_cast(action)); +} + void MacroActionMacroEdit::MacroChanged(const QString &text) { GUARD_LOADING_AND_LOCK(); @@ -444,7 +492,8 @@ void MacroActionMacroEdit::SetWidgetVisibility() PlaceWidgets( obs_module_text( - _entryData->_action == MacroActionMacro::Action::RUN + _entryData->_action == + MacroActionMacro::Action::RUN_ACTIONS ? "AdvSceneSwitcher.action.macro.entry.run" : "AdvSceneSwitcher.action.macro.entry.other"), _entryLayout, placeholders); @@ -460,7 +509,7 @@ void MacroActionMacroEdit::SetWidgetVisibility() _conditionLayout, placeholders); } - if (_entryData->_action == MacroActionMacro::Action::RUN || + if (_entryData->_action == MacroActionMacro::Action::RUN_ACTIONS || _entryData->_action == MacroActionMacro::Action::STOP) { _macros->HideSelectedMacro(); } else { @@ -476,27 +525,43 @@ void MacroActionMacroEdit::SetWidgetVisibility() _actionIndex->setVisible(isModifyingActionState); SetLayoutVisible(_conditionLayout, - _entryData->_action == MacroActionMacro::Action::RUN); + _entryData->_action == + MacroActionMacro::Action::RUN_ACTIONS); const bool needsAdditionalConditionWidgets = - _entryData->_action == MacroActionMacro::Action::RUN && + _entryData->_action == MacroActionMacro::Action::RUN_ACTIONS && _entryData->_runOptions.logic != MacroActionMacro::RunOptions::Logic::IGNORE_CONDITIONS; _conditionMacros->setVisible(needsAdditionalConditionWidgets); SetLayoutVisible(_reevaluateConditionStateLayout, needsAdditionalConditionWidgets); SetLayoutVisible(_setInputsLayout, - _entryData->_action == MacroActionMacro::Action::RUN); + _entryData->_action == + MacroActionMacro::Action::RUN_ACTIONS); _inputs->setVisible(_entryData->_action == - MacroActionMacro::Action::RUN && + MacroActionMacro::Action::RUN_ACTIONS && _entryData->_runOptions.setInputs); - HighlightMacroSettingsButton(_entryData->_action == - MacroActionMacro::Action::RUN && - _entryData->_runOptions.setInputs && - !_inputs->HasInputsToSet()); + HighlightMacroSettingsButton( + _entryData->_action == MacroActionMacro::Action::RUN_ACTIONS && + _entryData->_runOptions.setInputs && + !_inputs->HasInputsToSet()); _actionTypes->setVisible(_entryData->_action == - MacroActionMacro::Action::RUN); + MacroActionMacro::Action::RUN_ACTIONS); _skipWhenPaused->setVisible(_entryData->_action == - MacroActionMacro::Action::RUN); + MacroActionMacro::Action::RUN_ACTIONS); + + _nestedMacro->setVisible(_entryData->_action == + MacroActionMacro::Action::NESTED_MACRO); + _macros->setVisible(_entryData->_action != + MacroActionMacro::Action::NESTED_MACRO); + SetResizingEnabled(_entryData->_action == + MacroActionMacro::Action::NESTED_MACRO); + + if (_nestedMacro->IsEmpty()) { + _nestedMacro->ShowAllMacroSections(); + // TODO: find a better solution than setting a fixed height + _entryData->_customWidgetHeight = 600; + } + SetCustomHeight(_entryData->_customWidgetHeight); adjustSize(); updateGeometry(); diff --git a/lib/macro/macro-action-macro.hpp b/lib/macro/macro-action-macro.hpp index 2c7a6127..b0e47942 100644 --- a/lib/macro/macro-action-macro.hpp +++ b/lib/macro/macro-action-macro.hpp @@ -1,8 +1,11 @@ #pragma once #include "macro-action-edit.hpp" +#include "macro.hpp" +#include "macro-edit.hpp" #include "macro-input.hpp" #include "macro-selection.hpp" #include "macro-segment-selection.hpp" +#include "resizable-widget.hpp" #include #include @@ -44,16 +47,19 @@ public: PAUSE, UNPAUSE, RESET_COUNTER, - RUN, + RUN_ACTIONS, STOP, DISABLE_ACTION, ENABLE_ACTION, TOGGLE_ACTION, TOGGLE_PAUSE, + NESTED_MACRO, }; - Action _action = Action::RUN; + Action _action = Action::NESTED_MACRO; IntVariable _actionIndex = 1; RunOptions _runOptions = {}; + std::shared_ptr _nestedMacro = std::make_shared(); + int _customWidgetHeight = 0; private: void RunActions(Macro *actionMacro) const; @@ -62,7 +68,7 @@ private: static const std::string id; }; -class MacroActionMacroEdit final : public QWidget { +class MacroActionMacroEdit final : public ResizableWidget { Q_OBJECT public: @@ -71,13 +77,7 @@ public: std::shared_ptr entryData = nullptr); ~MacroActionMacroEdit(); void UpdateEntryData(); - static QWidget *Create(QWidget *parent, - std::shared_ptr action) - { - return new MacroActionMacroEdit( - parent, - std::dynamic_pointer_cast(action)); - } + static QWidget *Create(QWidget *, std::shared_ptr); private slots: void MacroChanged(const QString &text); @@ -112,6 +112,7 @@ private: QHBoxLayout *_conditionLayout; QHBoxLayout *_reevaluateConditionStateLayout; QHBoxLayout *_setInputsLayout; + MacroEdit *_nestedMacro; std::shared_ptr _entryData; bool _loading = true;