diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 188c6dbf..88091ff2 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -203,6 +203,7 @@ AdvSceneSwitcher.undo.addMacro="Add Macro '%1'" AdvSceneSwitcher.undo.removeMacro="Remove Macro '%1'" AdvSceneSwitcher.undo.addSegment="Add %1 in '%2'" AdvSceneSwitcher.undo.removeSegment="Remove %1 in '%2'" +AdvSceneSwitcher.undo.changeSegmentType="Change %1 Type in '%2'" AdvSceneSwitcher.undo.groupMacros="Group Macros '%1'" AdvSceneSwitcher.undo.ungroupMacros="Ungroup Macros '%1'" AdvSceneSwitcher.undo.removeMacroGroup="Remove Macro Group '%1'" diff --git a/lib/macro/macro-action-edit.cpp b/lib/macro/macro-action-edit.cpp index 8f31feb6..92f78453 100644 --- a/lib/macro/macro-action-edit.cpp +++ b/lib/macro/macro-action-edit.cpp @@ -1,4 +1,5 @@ #include "macro-action-edit.hpp" +#include "macro-undo-redo.hpp" #include "advanced-scene-switcher.hpp" #include "macro-helpers.hpp" #include "macro-settings.hpp" @@ -111,9 +112,24 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text) return; } + const std::string oldId = (*_entryData)->GetId(); + OBSDataAutoRelease oldData = obs_data_create(); + (*_entryData)->Save(oldData); + HeaderInfoChanged(""); auto idx = _entryData->get()->GetIndex(); auto macro = _entryData->get()->GetMacro(); + + // Determine whether this is an action or else-action before resetting. + const auto *rawPtr = (*_entryData).get(); + const auto &elseActions = macro->ElseActions(); + const bool isElse = std::any_of(elseActions.begin(), elseActions.end(), + [rawPtr](const auto &a) { + return a.get() == rawPtr; + }); + const auto segmentType = isElse ? MacroEdit::SegmentType::ELSE_ACTION + : MacroEdit::SegmentType::ACTION; + { auto lock = LockContext(); _entryData->reset(); @@ -122,6 +138,11 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text) (*_entryData)->PostLoad(); RunAndClearPostLoadSteps(); } + + OBSDataAutoRelease newData = obs_data_create(); + (*_entryData)->Save(newData); + RegisterSegmentTypeChangeUndoRedo(macro, segmentType, idx, oldId, + oldData, 0, id, newData, 0); auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData); QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)), this, SLOT(HeaderInfoChanged(const QString &))); diff --git a/lib/macro/macro-condition-edit.cpp b/lib/macro/macro-condition-edit.cpp index 885871fd..22cdd56f 100644 --- a/lib/macro/macro-condition-edit.cpp +++ b/lib/macro/macro-condition-edit.cpp @@ -1,4 +1,5 @@ #include "macro-condition-edit.hpp" +#include "macro-undo-redo.hpp" #include "advanced-scene-switcher.hpp" #include "condition-logic.hpp" @@ -250,6 +251,11 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text) return; } + const std::string oldId = (*_entryData)->GetId(); + const int oldLogic = (int)(*_entryData)->GetLogicType(); + OBSDataAutoRelease oldData = obs_data_create(); + (*_entryData)->Save(oldData); + auto temp = DurationModifier(); _dur->SetValue(temp); HeaderInfoChanged(""); @@ -267,6 +273,12 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text) (*_entryData)->PostLoad(); RunAndClearPostLoadSteps(); } + + OBSDataAutoRelease newData = obs_data_create(); + (*_entryData)->Save(newData); + RegisterSegmentTypeChangeUndoRedo( + macro, MacroEdit::SegmentType::CONDITION, idx, oldId, oldData, + oldLogic, id, newData, (int)(*_entryData)->GetLogicType()); auto widget = MacroConditionFactory::CreateWidget(id, this, *_entryData); QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)), diff --git a/lib/macro/macro-undo-redo.cpp b/lib/macro/macro-undo-redo.cpp index 763b605f..29f7bffc 100644 --- a/lib/macro/macro-undo-redo.cpp +++ b/lib/macro/macro-undo-redo.cpp @@ -534,6 +534,102 @@ static void addSegmentFromData(const char *jsonData) } } +// --------------------------------------------------------------------------- +// Segment type-change undo/redo callback +// --------------------------------------------------------------------------- + +// Same data format as addSegmentFromData: +// {"macro": "Name", "type": N, "index": N, "id": "...", "logic": N, +// "segment": {...}} +static void replaceSegmentFromData(const char *jsonData) +{ + OBSDataAutoRelease data = obs_data_create_from_json(jsonData); + if (!data) { + return; + } + + const std::string macroName = obs_data_get_string(data, "macro"); + const int type = (int)obs_data_get_int(data, "type"); + const int index = (int)obs_data_get_int(data, "index"); + const std::string id = obs_data_get_string(data, "id"); + const int logic = (int)obs_data_get_int(data, "logic"); + OBSDataAutoRelease segData = obs_data_get_obj(data, "segment"); + + const auto macro = GetWeakMacroByName(macroName.c_str()).lock(); + if (!macro) { + return; + } + + { + auto lock = LockContext(); + switch ((SegmentType)type) { + case SegmentType::ACTION: { + if (index < 0 || + index >= (int)macro->Actions().size()) { + break; + } + macro->Actions().erase(macro->Actions().begin() + + index); + macro->Actions().emplace( + macro->Actions().begin() + index, + MacroActionFactory::Create(id, macro.get())); + if (segData) { + macro->Actions().at(index)->Load(segData); + } + macro->Actions().at(index)->PostLoad(); + RunAndClearPostLoadSteps(); + macro->UpdateActionIndices(); + break; + } + case SegmentType::ELSE_ACTION: { + if (index < 0 || + index >= (int)macro->ElseActions().size()) { + break; + } + macro->ElseActions().erase( + macro->ElseActions().begin() + index); + macro->ElseActions().emplace( + macro->ElseActions().begin() + index, + MacroActionFactory::Create(id, macro.get())); + if (segData) { + macro->ElseActions().at(index)->Load(segData); + } + macro->ElseActions().at(index)->PostLoad(); + RunAndClearPostLoadSteps(); + macro->UpdateElseActionIndices(); + break; + } + case SegmentType::CONDITION: { + if (index < 0 || + index >= (int)macro->Conditions().size()) { + break; + } + macro->Conditions().erase(macro->Conditions().begin() + + index); + macro->Conditions().emplace( + macro->Conditions().begin() + index, + MacroConditionFactory::Create(id, macro.get())); + if (segData) { + macro->Conditions().at(index)->Load(segData); + } + macro->Conditions().at(index)->PostLoad(); + RunAndClearPostLoadSteps(); + macro->Conditions().at(index)->SetLogicType( + (Logic::Type)logic); + macro->UpdateConditionIndices(); + break; + } + } + } + + if (auto *window = AdvSceneSwitcher::window) { + updateMacroEditSegment(window, macroName, SegmentOp::REMOVE, + (SegmentType)type, index); + updateMacroEditSegment(window, macroName, SegmentOp::ADD, + (SegmentType)type, index); + } +} + // --------------------------------------------------------------------------- // Rename undo/redo callback // --------------------------------------------------------------------------- @@ -928,4 +1024,42 @@ void RegisterMacroRenameUndoRedo(const std::string &oldName, obs_data_get_json(redoData), false); } +void RegisterSegmentTypeChangeUndoRedo(Macro *macro, SegmentType type, + int index, const std::string &oldId, + obs_data_t *oldData, int oldLogic, + const std::string &newId, + obs_data_t *newData, int newLogic) +{ + if (!macro) { + return; + } + + const std::string macroName = macro->Name(); + + OBSDataAutoRelease undoData = obs_data_create(); + obs_data_set_string(undoData, "macro", macroName.c_str()); + obs_data_set_int(undoData, "type", (int)type); + obs_data_set_int(undoData, "index", index); + obs_data_set_string(undoData, "id", oldId.c_str()); + obs_data_set_int(undoData, "logic", oldLogic); + obs_data_set_obj(undoData, "segment", oldData); + + OBSDataAutoRelease redoData = obs_data_create(); + obs_data_set_string(redoData, "macro", macroName.c_str()); + obs_data_set_int(redoData, "type", (int)type); + obs_data_set_int(redoData, "index", index); + obs_data_set_string(redoData, "id", newId.c_str()); + obs_data_set_int(redoData, "logic", newLogic); + obs_data_set_obj(redoData, "segment", newData); + + const std::string actionName = + fmtUndoName("AdvSceneSwitcher.undo.changeSegmentType", + segmentTypeName(type), macroName); + obs_frontend_add_undo_redo_action(actionName.c_str(), + &replaceSegmentFromData, + &replaceSegmentFromData, + obs_data_get_json(undoData), + obs_data_get_json(redoData), false); +} + } // namespace advss diff --git a/lib/macro/macro-undo-redo.hpp b/lib/macro/macro-undo-redo.hpp index 45144fa1..0001ca17 100644 --- a/lib/macro/macro-undo-redo.hpp +++ b/lib/macro/macro-undo-redo.hpp @@ -12,6 +12,12 @@ void RegisterMacroAddUndoRedo(const std::string ¯oName); bool RegisterMacroRemoveUndoRedo(Macro *macro); void RegisterSegmentAddUndoRedo(Macro *macro, MacroEdit::SegmentType type, int index); +void RegisterSegmentTypeChangeUndoRedo(Macro *macro, + MacroEdit::SegmentType type, int index, + const std::string &oldId, + obs_data_t *oldData, int oldLogic, + const std::string &newId, + obs_data_t *newData, int newLogic); void RegisterSegmentRemoveUndoRedo(Macro *macro, MacroEdit::SegmentType type, int index, const std::string &segmentId, obs_data_t *segmentData, int logic);