From ce1d4cce570ce732727b44bf15e48f0c060e9e5e Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Wed, 30 Aug 2023 14:34:20 +0200 Subject: [PATCH] Add support for "else" action block * Changed the "on change" match behaviour Previously macros with true conditions but "on change" enabled would be considered to be evaluating to false. Now they are considered to be evaluating to true and the "on change" option only has an effect on if the actions are performed or not. This could have effects on user which use the "Macro" condition type to check what other macros' conditions evaluate to. * The splitter position separating the conditions, actions, and else-actions are not saved and restored based on which macro is being selected. --- data/locale/en-US.ini | 1 + src/advanced-scene-switcher.hpp | 33 +- src/general.cpp | 12 +- src/macro-core/macro-action-edit.cpp | 228 +++++++++++++- src/macro-core/macro-condition-edit.cpp | 17 +- src/macro-core/macro-tab.cpp | 381 +++++++++++++++++++----- src/macro-core/macro-tree.cpp | 1 + src/macro-core/macro-tree.hpp | 1 + src/macro-core/macro.cpp | 254 ++++++++++------ src/macro-core/macro.hpp | 23 +- src/switcher-data.hpp | 1 - 11 files changed, 753 insertions(+), 199 deletions(-) diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 3353cc37..8b730b92 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -71,6 +71,7 @@ AdvSceneSwitcher.macroTab.priorityWarning="Note: It is recommended to configure 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.edit="Edit macro" AdvSceneSwitcher.macroTab.edit.logic="Logic type:" AdvSceneSwitcher.macroTab.edit.condition="Condition type:" diff --git a/src/advanced-scene-switcher.hpp b/src/advanced-scene-switcher.hpp index 97087fd4..967fb9ef 100644 --- a/src/advanced-scene-switcher.hpp +++ b/src/advanced-scene-switcher.hpp @@ -84,10 +84,13 @@ public: void SetEditMacro(Macro &m); void SetMacroEditAreaDisabled(bool); void HighlightAction(int idx, QColor color = QColor(Qt::green)); + void HighlightElseAction(int idx, QColor color = QColor(Qt::green)); void HighlightCondition(int idx, QColor color = QColor(Qt::green)); void PopulateMacroActions(Macro &m, uint32_t afterIdx = 0); + void PopulateMacroElseActions(Macro &m, uint32_t afterIdx = 0); void PopulateMacroConditions(Macro &m, uint32_t afterIdx = 0); void SetActionData(Macro &m); + void SetElseActionData(Macro &m); void SetConditionData(Macro &m); void SwapActions(Macro *m, int pos1, int pos2); void SwapConditions(Macro *m, int pos1, int pos2); @@ -113,29 +116,50 @@ public slots: void on_actionUp_clicked(); void on_actionDown_clicked(); void on_actionBottom_clicked(); + void on_elseActionAdd_clicked(); + void on_elseActionRemove_clicked(); + void on_elseActionTop_clicked(); + void on_elseActionUp_clicked(); + void on_elseActionDown_clicked(); + void on_elseActionBottom_clicked(); + void MacroSelectionAboutToChange(); void MacroSelectionChanged(); void UpMacroSegementHotkey(); void DownMacroSegementHotkey(); void DeleteMacroSegementHotkey(); void ShowMacroContextMenu(const QPoint &); void ShowMacroActionsContextMenu(const QPoint &); + void ShowMacroElseActionsContextMenu(const QPoint &); void ShowMacroConditionsContextMenu(const QPoint &); void CopyMacro(); void RenameCurrentMacro(); void ExportMacros(); void ImportMacros(); void ExpandAllActions(); + void ExpandAllElseActions(); void ExpandAllConditions(); void CollapseAllActions(); + void CollapseAllElseActions(); void CollapseAllConditions(); void MinimizeActions(); + void MaximizeActions(); + void MinimizeElseActions(); + void MaximizeElseActions(); void MinimizeConditions(); + void MaximizeConditions(); void MacroActionSelectionChanged(int idx); void MacroActionReorder(int to, int target); void AddMacroAction(int idx); void RemoveMacroAction(int idx); void MoveMacroActionUp(int idx); void MoveMacroActionDown(int idx); + void MacroElseActionSelectionChanged(int idx); + void MacroElseActionReorder(int to, int target); + void AddMacroElseAction(int idx); + void RemoveMacroElseAction(int idx); + void SwapElseActions(Macro *m, int pos1, int pos2); + void MoveMacroElseActionUp(int idx); + void MoveMacroElseActionDown(int idx); void MacroConditionSelectionChanged(int idx); void MacroConditionReorder(int to, int target); void AddMacroCondition(int idx); @@ -157,6 +181,7 @@ signals: void MacroSegmentOrderChanged(); void HighlightMacrosChanged(bool value); void HighlightActionsChanged(bool value); + void HighlightElseActionsChanged(bool value); void HighlightConditionsChanged(bool value); void ConnectionAdded(const QString &); @@ -167,16 +192,16 @@ signals: void VariableRemoved(const QString &); private: + enum class MacroSection { CONDITIONS, ACTIONS, ELSE_ACTIONS }; + + void SetupMacroSegmentSelection(MacroSection type, int idx); bool ResolveMacroImportNameConflict(std::shared_ptr &); bool MacroTabIsInFocus(); - enum class MacroSection { - CONDITIONS, - ACTIONS, - }; MacroSection lastInteracted = MacroSection::CONDITIONS; int currentConditionIdx = -1; int currentActionIdx = -1; + int currentElseActionIdx = -1; /* --- End of macro tab section --- */ diff --git a/src/general.cpp b/src/general.cpp index fdfee314..e83f6653 100644 --- a/src/general.cpp +++ b/src/general.cpp @@ -132,10 +132,9 @@ void AdvSceneSwitcher::closeEvent(QCloseEvent *) } switcher->windowPos = this->pos(); switcher->windowSize = this->size(); - switcher->macroActionConditionSplitterPosition = - ui->macroActionConditionSplitter->sizes(); switcher->macroListMacroEditSplitterPosition = ui->macroListMacroEditSplitter->sizes(); + MacroSelectionAboutToChange(); // Trigger saving of splitter states obs_frontend_save(); } @@ -703,9 +702,7 @@ void SwitcherData::SaveUISettings(obs_data_t *obj) obs_data_set_int(obj, "windowWidth", windowSize.width()); obs_data_set_int(obj, "windowHeight", windowSize.height()); - saveSplitterPos(macroActionConditionSplitterPosition, obj, - "macroActionConditionSplitterPosition"); - saveSplitterPos(macroListMacroEditSplitterPosition, obj, + SaveSplitterPos(macroListMacroEditSplitterPosition, obj, "macroListMacroEditSplitterPosition"); } @@ -759,9 +756,8 @@ void SwitcherData::LoadUISettings(obs_data_t *obj) (int)obs_data_get_int(obj, "windowPosY")}; windowSize = {(int)obs_data_get_int(obj, "windowWidth"), (int)obs_data_get_int(obj, "windowHeight")}; - loadSplitterPos(macroActionConditionSplitterPosition, obj, - "macroActionConditionSplitterPosition"); - loadSplitterPos(macroListMacroEditSplitterPosition, obj, + + LoadSplitterPos(macroListMacroEditSplitterPosition, obj, "macroListMacroEditSplitterPosition"); } diff --git a/src/macro-core/macro-action-edit.cpp b/src/macro-core/macro-action-edit.cpp index 909cea7e..82cdb1be 100644 --- a/src/macro-core/macro-action-edit.cpp +++ b/src/macro-core/macro-action-edit.cpp @@ -322,6 +322,7 @@ void AdvSceneSwitcher::on_actionUp_clicked() MoveMacroActionUp(currentActionIdx); MacroActionSelectionChanged(currentActionIdx - 1); } + void AdvSceneSwitcher::on_actionDown_clicked() { if (currentActionIdx == -1 || @@ -342,6 +343,77 @@ void AdvSceneSwitcher::on_actionBottom_clicked() MacroActionSelectionChanged(newIdx); } +void AdvSceneSwitcher::on_elseActionAdd_clicked() +{ + auto macro = GetSelectedMacro(); + if (!macro) { + return; + } + + if (currentElseActionIdx == -1) { + AddMacroElseAction((int)macro->ElseActions().size()); + } else { + AddMacroElseAction(currentElseActionIdx + 1); + } + if (currentElseActionIdx != -1) { + MacroElseActionSelectionChanged(currentElseActionIdx + 1); + } + ui->elseActionsList->SetHelpMsgVisible(false); +} + +void AdvSceneSwitcher::on_elseActionRemove_clicked() +{ + if (currentElseActionIdx == -1) { + auto macro = GetSelectedMacro(); + if (!macro) { + return; + } + RemoveMacroElseAction((int)macro->Actions().size() - 1); + } else { + RemoveMacroElseAction(currentElseActionIdx); + } + MacroElseActionSelectionChanged(-1); +} + +void AdvSceneSwitcher::on_elseActionTop_clicked() +{ + if (currentElseActionIdx == -1) { + return; + } + MacroElseActionReorder(0, currentElseActionIdx); + MacroElseActionSelectionChanged(0); +} + +void AdvSceneSwitcher::on_elseActionUp_clicked() +{ + if (currentElseActionIdx == -1 || currentElseActionIdx == 0) { + return; + } + MoveMacroElseActionUp(currentElseActionIdx); + MacroElseActionSelectionChanged(currentElseActionIdx - 1); +} + +void AdvSceneSwitcher::on_elseActionDown_clicked() +{ + if (currentElseActionIdx == -1 || + currentElseActionIdx == + ui->elseActionsList->ContentLayout()->count() - 1) { + return; + } + MoveMacroElseActionDown(currentElseActionIdx); + MacroElseActionSelectionChanged(currentElseActionIdx + 1); +} + +void AdvSceneSwitcher::on_elseActionBottom_clicked() +{ + if (currentElseActionIdx == -1) { + return; + } + const int newIdx = ui->elseActionsList->ContentLayout()->count() - 1; + MacroElseActionReorder(newIdx, currentElseActionIdx); + MacroElseActionSelectionChanged(newIdx); +} + void AdvSceneSwitcher::SwapActions(Macro *m, int pos1, int pos2) { if (pos1 == pos2) { @@ -394,24 +466,158 @@ void AdvSceneSwitcher::MoveMacroActionDown(int idx) HighlightAction(idx + 1); } -void AdvSceneSwitcher::MacroActionSelectionChanged(int idx) +void AdvSceneSwitcher::MacroElseActionSelectionChanged(int idx) +{ + SetupMacroSegmentSelection(MacroSection::ELSE_ACTIONS, idx); +} + +void AdvSceneSwitcher::MacroElseActionReorder(int to, int from) { auto macro = GetSelectedMacro(); if (!macro) { return; } - ui->actionsList->SetSelection(idx); - ui->conditionsList->SetSelection(-1); - - if (idx < 0 || (unsigned)idx >= macro->Actions().size()) { - currentActionIdx = -1; - } else { - currentActionIdx = idx; - lastInteracted = MacroSection::ACTIONS; + if (to == from || from < 0 || from > (int)macro->ElseActions().size() || + to < 0 || to > (int)macro->ElseActions().size()) { + return; } - currentConditionIdx = -1; - HighlightControls(); + { + std::lock_guard lock(switcher->m); + auto action = macro->ElseActions().at(from); + macro->ElseActions().erase(macro->ElseActions().begin() + from); + macro->ElseActions().insert(macro->ElseActions().begin() + to, + action); + macro->UpdateElseActionIndices(); + ui->elseActionsList->ContentLayout()->insertItem( + to, ui->elseActionsList->ContentLayout()->takeAt(from)); + SetElseActionData(*macro); + } + HighlightElseAction(to); + emit(MacroSegmentOrderChanged()); +} + +void AdvSceneSwitcher::AddMacroElseAction(int idx) +{ + auto macro = GetSelectedMacro(); + if (!macro) { + return; + } + + if (idx < 0 || idx > (int)macro->ElseActions().size()) { + return; + } + + std::string id; + if (idx - 1 >= 0) { + id = macro->ElseActions().at(idx - 1)->GetId(); + } else { + MacroActionSwitchScene temp(nullptr); + id = temp.GetId(); + } + { + std::lock_guard lock(switcher->m); + macro->ElseActions().emplace( + macro->ElseActions().begin() + idx, + MacroActionFactory::Create(id, macro.get())); + if (idx - 1 >= 0) { + OBSDataAutoRelease data = obs_data_create(); + macro->ElseActions().at(idx - 1)->Save(data); + macro->ElseActions().at(idx)->Load(data); + } + macro->UpdateElseActionIndices(); + ui->elseActionsList->Insert( + idx, new MacroActionEdit( + this, ¯o->ElseActions()[idx], id)); + SetElseActionData(*macro); + } + HighlightElseAction(idx); + emit(MacroSegmentOrderChanged()); +} + +void AdvSceneSwitcher::RemoveMacroElseAction(int idx) +{ + auto macro = GetSelectedMacro(); + if (!macro) { + return; + } + + if (idx < 0 || idx >= (int)macro->ElseActions().size()) { + return; + } + + { + std::lock_guard lock(switcher->m); + ui->elseActionsList->Remove(idx); + macro->ElseActions().erase(macro->ElseActions().begin() + idx); + switcher->abortMacroWait = true; + switcher->macroWaitCv.notify_all(); + macro->UpdateElseActionIndices(); + SetActionData(*macro); + } + MacroElseActionSelectionChanged(-1); + lastInteracted = MacroSection::ELSE_ACTIONS; + emit(MacroSegmentOrderChanged()); +} + +void AdvSceneSwitcher::SwapElseActions(Macro *m, int pos1, int pos2) +{ + if (pos1 == pos2) { + return; + } + if (pos1 > pos2) { + std::swap(pos1, pos2); + } + + std::lock_guard lock(switcher->m); + iter_swap(m->ElseActions().begin() + pos1, + m->ElseActions().begin() + pos2); + m->UpdateElseActionIndices(); + auto widget1 = static_cast( + ui->elseActionsList->ContentLayout()->takeAt(pos1)->widget()); + auto widget2 = static_cast( + ui->elseActionsList->ContentLayout() + ->takeAt(pos2 - 1) + ->widget()); + ui->elseActionsList->Insert(pos1, widget2); + ui->elseActionsList->Insert(pos2, widget1); + SetElseActionData(*m); + emit(MacroSegmentOrderChanged()); +} + +void AdvSceneSwitcher::MoveMacroElseActionUp(int idx) +{ + auto macro = GetSelectedMacro(); + if (!macro) { + return; + } + + if (idx < 1 || idx >= (int)macro->ElseActions().size()) { + return; + } + + SwapElseActions(macro.get(), idx, idx - 1); + HighlightElseAction(idx - 1); +} + +void AdvSceneSwitcher::MoveMacroElseActionDown(int idx) +{ + auto macro = GetSelectedMacro(); + if (!macro) { + return; + } + + if (idx < 0 || idx >= (int)macro->ElseActions().size() - 1) { + return; + } + + SwapElseActions(macro.get(), idx, idx + 1); + HighlightElseAction(idx + 1); +} + +void AdvSceneSwitcher::MacroActionSelectionChanged(int idx) +{ + SetupMacroSegmentSelection(MacroSection::ACTIONS, idx); } void AdvSceneSwitcher::MacroActionReorder(int to, int from) diff --git a/src/macro-core/macro-condition-edit.cpp b/src/macro-core/macro-condition-edit.cpp index 0a325c8d..e1923687 100644 --- a/src/macro-core/macro-condition-edit.cpp +++ b/src/macro-core/macro-condition-edit.cpp @@ -564,22 +564,7 @@ void AdvSceneSwitcher::MoveMacroConditionDown(int idx) void AdvSceneSwitcher::MacroConditionSelectionChanged(int idx) { - auto macro = GetSelectedMacro(); - if (!macro) { - return; - } - - ui->conditionsList->SetSelection(idx); - ui->actionsList->SetSelection(-1); - - if (idx < 0 || (unsigned)idx >= macro->Conditions().size()) { - currentConditionIdx = -1; - } else { - currentConditionIdx = idx; - lastInteracted = MacroSection::CONDITIONS; - } - currentActionIdx = -1; - HighlightControls(); + SetupMacroSegmentSelection(MacroSection::CONDITIONS, idx); } void AdvSceneSwitcher::MacroConditionReorder(int to, int from) diff --git a/src/macro-core/macro-tab.cpp b/src/macro-core/macro-tab.cpp index 16d8aa74..ec8b6a16 100644 --- a/src/macro-core/macro-tab.cpp +++ b/src/macro-core/macro-tab.cpp @@ -253,6 +253,76 @@ void AdvSceneSwitcher::ExportMacros() MacroExportImportDialog::ExportMacros(exportString); } +static bool +isValidMacroSegmentIdx(const std::deque> &list, + int idx) +{ + return (idx > 0 || (unsigned)idx < list.size()); +} + +void AdvSceneSwitcher::SetupMacroSegmentSelection(MacroSection type, int idx) +{ + auto macro = GetSelectedMacro(); + if (!macro) { + return; + } + + MacroSegmentList *setList, *resetList1, *resetList2; + int *setIdx, *resetIdx1, *resetIdx2; + std::deque> segements; + + switch (type) { + case AdvSceneSwitcher::MacroSection::CONDITIONS: + setList = ui->conditionsList; + setIdx = ¤tConditionIdx; + segements = {macro->Conditions().begin(), + macro->Conditions().end()}; + + resetList1 = ui->actionsList; + resetList2 = ui->elseActionsList; + resetIdx1 = ¤tActionIdx; + resetIdx2 = ¤tElseActionIdx; + break; + case AdvSceneSwitcher::MacroSection::ACTIONS: + setList = ui->actionsList; + setIdx = ¤tActionIdx; + segements = {macro->Actions().begin(), macro->Actions().end()}; + + resetList1 = ui->conditionsList; + resetList2 = ui->elseActionsList; + resetIdx1 = ¤tConditionIdx; + resetIdx2 = ¤tElseActionIdx; + break; + case AdvSceneSwitcher::MacroSection::ELSE_ACTIONS: + setList = ui->elseActionsList; + setIdx = ¤tElseActionIdx; + segements = {macro->ElseActions().begin(), + macro->ElseActions().end()}; + + resetList1 = ui->actionsList; + resetList2 = ui->conditionsList; + resetIdx1 = ¤tActionIdx; + resetIdx2 = ¤tConditionIdx; + break; + default: + break; + } + + setList->SetSelection(idx); + resetList1->SetSelection(-1); + resetList2->SetSelection(-1); + if (isValidMacroSegmentIdx(segements, idx)) { + *setIdx = idx; + } else { + *setIdx = -1; + } + *resetIdx1 = -1; + *resetIdx2 = -1; + + lastInteracted = type; + HighlightControls(); +} + bool AdvSceneSwitcher::ResolveMacroImportNameConflict( std::shared_ptr ¯o) { @@ -427,6 +497,17 @@ void AdvSceneSwitcher::PopulateMacroActions(Macro &m, uint32_t afterIdx) ui->actionsList->SetHelpMsgVisible(actions.size() == 0); } +void AdvSceneSwitcher::PopulateMacroElseActions(Macro &m, uint32_t afterIdx) +{ + auto &actions = m.ElseActions(); + for (; afterIdx < actions.size(); afterIdx++) { + auto newEntry = new MacroActionEdit(this, &actions[afterIdx], + actions[afterIdx]->GetId()); + ui->elseActionsList->Add(newEntry); + } + ui->elseActionsList->SetHelpMsgVisible(actions.size() == 0); +} + void AdvSceneSwitcher::PopulateMacroConditions(Macro &m, uint32_t afterIdx) { bool root = afterIdx == 0; @@ -458,6 +539,23 @@ void AdvSceneSwitcher::SetActionData(Macro &m) } } +void AdvSceneSwitcher::SetElseActionData(Macro &m) +{ + auto &actions = m.ElseActions(); + for (int idx = 0; idx < ui->elseActionsList->ContentLayout()->count(); + idx++) { + auto item = ui->elseActionsList->ContentLayout()->itemAt(idx); + if (!item) { + continue; + } + auto widget = static_cast(item->widget()); + if (!widget) { + continue; + } + widget->SetEntryData(&*(actions.begin() + idx)); + } +} + void AdvSceneSwitcher::SetConditionData(Macro &m) { auto &conditions = m.Conditions(); @@ -475,6 +573,21 @@ void AdvSceneSwitcher::SetConditionData(Macro &m) } } +static void maximizeFirstSplitterEntry(QSplitter *splitter) +{ + QList newSizes; + newSizes << 999999; + for (int i = 0; i < splitter->sizes().size() - 1; i++) { + newSizes << 0; + } + splitter->setSizes(newSizes); +} + +static void centerSplitterPosition(QSplitter *splitter) +{ + splitter->setSizes(QList() << 999999 << 999999); +} + void AdvSceneSwitcher::SetEditMacro(Macro &m) { { @@ -487,20 +600,38 @@ void AdvSceneSwitcher::SetEditMacro(Macro &m) } ui->conditionsList->Clear(); ui->actionsList->Clear(); + ui->elseActionsList->Clear(); m.ResetUIHelpers(); PopulateMacroConditions(m); PopulateMacroActions(m); + PopulateMacroElseActions(m); SetMacroEditAreaDisabled(false); + + currentActionIdx = -1; + currentElseActionIdx = -1; + currentConditionIdx = -1; + HighlightControls(); + if (m.IsGroup()) { SetMacroEditAreaDisabled(true); ui->macroName->setEnabled(true); + centerSplitterPosition(ui->macroActionConditionSplitter); + maximizeFirstSplitterEntry(ui->macroElseActionSplitter); + return; } - currentActionIdx = -1; - currentConditionIdx = -1; - HighlightControls(); + if (!m.HasValidSplitterPositions()) { + centerSplitterPosition(ui->macroActionConditionSplitter); + maximizeFirstSplitterEntry(ui->macroElseActionSplitter); + return; + } + + ui->macroActionConditionSplitter->setSizes( + m.GetActionConditionSplitterPosition()); + ui->macroElseActionSplitter->setSizes( + m.GetElseActionSplitterPosition()); } void AdvSceneSwitcher::SetMacroEditAreaDisabled(bool disable) @@ -519,6 +650,11 @@ void AdvSceneSwitcher::HighlightAction(int idx, QColor color) ui->actionsList->Highlight(idx, color); } +void AdvSceneSwitcher::HighlightElseAction(int idx, QColor color) +{ + ui->elseActionsList->Highlight(idx, color); +} + void AdvSceneSwitcher::HighlightCondition(int idx, QColor color) { ui->conditionsList->Highlight(idx, color); @@ -534,6 +670,33 @@ std::vector> AdvSceneSwitcher::GetSelectedMacros() return ui->macros->GetCurrentMacros(); } +void AdvSceneSwitcher::MacroSelectionAboutToChange() +{ + if (loading) { + return; + } + + auto macro = GetMacroByQString(ui->macroName->text()); + if (!macro) { + return; + } + + macro->SetActionConditionSplitterPosition( + ui->macroActionConditionSplitter->sizes()); + + auto elsePos = ui->macroElseActionSplitter->sizes(); + // If only conditions are visible maximize the actions to avoid neither + // actions nor elseActions being visible when the condition <-> action + // splitter is moved + if (elsePos[0] == 0 && elsePos[1] == 0) { + macro->SetElseActionSplitterPosition(QList() + << 999999 << 0); + return; + } + macro->SetElseActionSplitterPosition( + ui->macroElseActionSplitter->sizes()); +} + void AdvSceneSwitcher::MacroSelectionChanged() { if (loading) { @@ -545,8 +708,12 @@ void AdvSceneSwitcher::MacroSelectionChanged() SetMacroEditAreaDisabled(true); ui->conditionsList->Clear(); ui->actionsList->Clear(); + ui->elseActionsList->Clear(); ui->conditionsList->SetHelpMsgVisible(true); ui->actionsList->SetHelpMsgVisible(true); + ui->elseActionsList->SetHelpMsgVisible(true); + centerSplitterPosition(ui->macroActionConditionSplitter); + maximizeFirstSplitterEntry(ui->macroElseActionSplitter); return; } SetEditMacro(*macro); @@ -584,8 +751,19 @@ void AdvSceneSwitcher::on_macroProperties_clicked() emit HighlightConditionsChanged(prop._highlightConditions); } -// Don't restore splitter pos if an element is not visible at all -bool shouldResotreSplitterPos(const QList &pos) +static void moveControlsToSplitter(QSplitter *splitter, int idx, + QLayoutItem *item) +{ + static int splitterHandleWidth = 38; + auto handle = splitter->handle(idx); + auto layout = item->layout(); + layout->setContentsMargins(7, 7, 7, 7); + handle->setLayout(layout); + splitter->setHandleWidth(splitterHandleWidth); + splitter->setStyleSheet("QSplitter::handle {background: transparent;}"); +} + +bool shouldRestoreSplitter(const QList &pos) { if (pos.size() == 0) { return false; @@ -606,6 +784,8 @@ void AdvSceneSwitcher::SetupMacroTab() } ui->macros->Reset(switcher->macros, switcher->macroProperties._highlightExecuted); + connect(ui->macros, SIGNAL(MacroSelectionAboutToChange()), this, + SLOT(MacroSelectionAboutToChange())); connect(ui->macros, SIGNAL(MacroSelectionChanged()), this, SLOT(MacroSelectionChanged())); @@ -623,12 +803,22 @@ void AdvSceneSwitcher::SetupMacroTab() connect(ui->actionsList, &MacroSegmentList::Reorder, this, &AdvSceneSwitcher::MacroActionReorder); + ui->elseActionsList->SetHelpMsg(obs_module_text( + "AdvSceneSwitcher.macroTab.editElseActionHelp")); + connect(ui->elseActionsList, &MacroSegmentList::SelectionChagned, this, + &AdvSceneSwitcher::MacroElseActionSelectionChanged); + connect(ui->elseActionsList, &MacroSegmentList::Reorder, this, + &AdvSceneSwitcher::MacroElseActionReorder); + ui->macros->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->macros, &QWidget::customContextMenuRequested, this, &AdvSceneSwitcher::ShowMacroContextMenu); ui->actionsList->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->actionsList, &QWidget::customContextMenuRequested, this, &AdvSceneSwitcher::ShowMacroActionsContextMenu); + ui->elseActionsList->setContextMenuPolicy(Qt::CustomContextMenu); + connect(ui->elseActionsList, &QWidget::customContextMenuRequested, this, + &AdvSceneSwitcher::ShowMacroElseActionsContextMenu); ui->conditionsList->setContextMenuPolicy(Qt::CustomContextMenu); connect(ui->conditionsList, &QWidget::customContextMenuRequested, this, &AdvSceneSwitcher::ShowMacroConditionsContextMenu); @@ -643,16 +833,10 @@ void AdvSceneSwitcher::SetupMacroTab() onChangeHighlightTimer.start(); // Move condition controls into splitter handle layout - auto handle = ui->macroActionConditionSplitter->handle(1); - auto item = ui->macroConditionsLayout->takeAt(1); - if (item) { - auto layout = item->layout(); - layout->setContentsMargins(7, 7, 7, 7); - handle->setLayout(layout); - ui->macroActionConditionSplitter->setHandleWidth(38); - } - ui->macroActionConditionSplitter->setStyleSheet( - "QSplitter::handle {background: transparent;}"); + moveControlsToSplitter(ui->macroActionConditionSplitter, 1, + ui->macroConditionsLayout->takeAt(1)); + moveControlsToSplitter(ui->macroElseActionSplitter, 1, + ui->macroActionsLayout->takeAt(1)); // Set action and condition control icons const std::string pathPrefix = @@ -660,6 +844,9 @@ void AdvSceneSwitcher::SetupMacroTab() SetButtonIcon(ui->actionTop, (pathPrefix + "DoubleUp.svg").c_str()); SetButtonIcon(ui->actionBottom, (pathPrefix + "DoubleDown.svg").c_str()); + SetButtonIcon(ui->elseActionTop, (pathPrefix + "DoubleUp.svg").c_str()); + SetButtonIcon(ui->elseActionBottom, + (pathPrefix + "DoubleDown.svg").c_str()); SetButtonIcon(ui->conditionTop, (pathPrefix + "DoubleUp.svg").c_str()); SetButtonIcon(ui->conditionBottom, (pathPrefix + "DoubleDown.svg").c_str()); @@ -668,13 +855,11 @@ void AdvSceneSwitcher::SetupMacroTab() ui->macroListMacroEditSplitter->setStretchFactor(0, 1); ui->macroListMacroEditSplitter->setStretchFactor(1, 4); + centerSplitterPosition(ui->macroActionConditionSplitter); + maximizeFirstSplitterEntry(ui->macroElseActionSplitter); + if (switcher->saveWindowGeo) { - if (shouldResotreSplitterPos( - switcher->macroActionConditionSplitterPosition)) { - ui->macroActionConditionSplitter->setSizes( - switcher->macroActionConditionSplitterPosition); - } - if (shouldResotreSplitterPos( + if (shouldRestoreSplitter( switcher->macroListMacroEditSplitterPosition)) { ui->macroListMacroEditSplitter->setSizes( switcher->macroListMacroEditSplitterPosition); @@ -732,34 +917,49 @@ void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos) menu.exec(globalPos); } -void AdvSceneSwitcher::ShowMacroActionsContextMenu(const QPoint &pos) +static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos, + std::function expand, + std::function collapse, + std::function maximize, + std::function minimize) { - QPoint globalPos = ui->actionsList->mapToGlobal(pos); QMenu menu; menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"), - this, &AdvSceneSwitcher::ExpandAllActions); + ss, [ss, expand]() { expand(ss); }); menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"), - this, &AdvSceneSwitcher::CollapseAllActions); + ss, [ss, collapse]() { collapse(ss); }); menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.maximize"), - this, &AdvSceneSwitcher::MinimizeConditions); + ss, [ss, maximize]() { maximize(ss); }); menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.minimize"), - this, &AdvSceneSwitcher::MinimizeActions); - menu.exec(globalPos); + ss, [ss, minimize]() { minimize(ss); }); + menu.exec(pos); +} + +void AdvSceneSwitcher::ShowMacroActionsContextMenu(const QPoint &pos) +{ + setupConextMenu(this, ui->actionsList->mapToGlobal(pos), + &AdvSceneSwitcher::ExpandAllActions, + &AdvSceneSwitcher::CollapseAllActions, + &AdvSceneSwitcher::MaximizeActions, + &AdvSceneSwitcher::MinimizeActions); +} + +void AdvSceneSwitcher::ShowMacroElseActionsContextMenu(const QPoint &pos) +{ + setupConextMenu(this, ui->elseActionsList->mapToGlobal(pos), + &AdvSceneSwitcher::ExpandAllElseActions, + &AdvSceneSwitcher::CollapseAllElseActions, + &AdvSceneSwitcher::MaximizeElseActions, + &AdvSceneSwitcher::MinimizeElseActions); } void AdvSceneSwitcher::ShowMacroConditionsContextMenu(const QPoint &pos) { - QPoint globalPos = ui->conditionsList->mapToGlobal(pos); - QMenu menu; - menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"), - this, &AdvSceneSwitcher::ExpandAllConditions); - menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"), - this, &AdvSceneSwitcher::CollapseAllConditions); - menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.maximize"), - this, &AdvSceneSwitcher::MinimizeActions); - menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.minimize"), - this, &AdvSceneSwitcher::MinimizeConditions); - menu.exec(globalPos); + setupConextMenu(this, ui->conditionsList->mapToGlobal(pos), + &AdvSceneSwitcher::ExpandAllConditions, + &AdvSceneSwitcher::CollapseAllConditions, + &AdvSceneSwitcher::MaximizeConditions, + &AdvSceneSwitcher::MinimizeConditions); } void AdvSceneSwitcher::CopyMacro() @@ -789,60 +989,103 @@ void AdvSceneSwitcher::CopyMacro() emit MacroAdded(QString::fromStdString(name)); } -void AdvSceneSwitcher::ExpandAllActions() +void setCollapsedHelper(const std::shared_ptr &m, MacroSegmentList *list, + bool collapsed) { - auto m = GetSelectedMacro(); if (!m) { return; } - ui->actionsList->SetCollapsed(false); + list->SetCollapsed(collapsed); +} + +void AdvSceneSwitcher::ExpandAllActions() +{ + setCollapsedHelper(GetSelectedMacro(), ui->actionsList, false); +} + +void AdvSceneSwitcher::ExpandAllElseActions() +{ + setCollapsedHelper(GetSelectedMacro(), ui->elseActionsList, false); } void AdvSceneSwitcher::ExpandAllConditions() { - auto m = GetSelectedMacro(); - if (!m) { - return; - } - ui->conditionsList->SetCollapsed(false); + setCollapsedHelper(GetSelectedMacro(), ui->conditionsList, false); } void AdvSceneSwitcher::CollapseAllActions() { - auto m = GetSelectedMacro(); - if (!m) { - return; - } - ui->actionsList->SetCollapsed(true); + setCollapsedHelper(GetSelectedMacro(), ui->actionsList, true); +} + +void AdvSceneSwitcher::CollapseAllElseActions() +{ + setCollapsedHelper(GetSelectedMacro(), ui->elseActionsList, true); } void AdvSceneSwitcher::CollapseAllConditions() { - auto m = GetSelectedMacro(); - if (!m) { - return; - } - ui->conditionsList->SetCollapsed(true); + setCollapsedHelper(GetSelectedMacro(), ui->conditionsList, true); +} + +static void reduceSizeOfSplitterIdx(QSplitter *splitter, int idx) +{ + auto sizes = splitter->sizes(); + int sum = sizes[0] + sizes[1]; + int reducedSize = sum / 10; + sizes[idx] = reducedSize; + sizes[(idx + 1) % 2] = sum - reducedSize; + splitter->setSizes(sizes); } void AdvSceneSwitcher::MinimizeActions() { - QList sizes = ui->macroActionConditionSplitter->sizes(); - int sum = sizes[0] + sizes[1]; - int actionsHeight = sum / 10; - sizes[1] = actionsHeight; - sizes[0] = sum - actionsHeight; - ui->macroActionConditionSplitter->setSizes(sizes); + auto macro = GetSelectedMacro(); + if (!macro) { + return; + } + if (macro->ElseActions().size() == 0) { + reduceSizeOfSplitterIdx(ui->macroActionConditionSplitter, 1); + } else { + maximizeFirstSplitterEntry(ui->macroElseActionSplitter); + reduceSizeOfSplitterIdx(ui->macroActionConditionSplitter, 1); + } +} + +void AdvSceneSwitcher::MaximizeActions() +{ + MinimizeElseActions(); + MinimizeConditions(); +} + +void AdvSceneSwitcher::MinimizeElseActions() +{ + auto macro = GetSelectedMacro(); + if (!macro) { + return; + } + if (macro->ElseActions().size() == 0) { + maximizeFirstSplitterEntry(ui->macroElseActionSplitter); + } else { + reduceSizeOfSplitterIdx(ui->macroElseActionSplitter, 1); + } +} + +void AdvSceneSwitcher::MaximizeElseActions() +{ + MinimizeConditions(); + reduceSizeOfSplitterIdx(ui->macroElseActionSplitter, 0); } void AdvSceneSwitcher::MinimizeConditions() { - QList sizes = ui->macroActionConditionSplitter->sizes(); - int sum = sizes[0] + sizes[1]; - int conditionsHeight = sum / 10; - sizes[0] = conditionsHeight; - sizes[1] = sum - conditionsHeight; - ui->macroActionConditionSplitter->setSizes(sizes); + reduceSizeOfSplitterIdx(ui->macroActionConditionSplitter, 0); +} + +void AdvSceneSwitcher::MaximizeConditions() +{ + MinimizeElseActions(); + MinimizeActions(); } bool AdvSceneSwitcher::MacroTabIsInFocus() diff --git a/src/macro-core/macro-tree.cpp b/src/macro-core/macro-tree.cpp index c284ec57..e743cb46 100644 --- a/src/macro-core/macro-tree.cpp +++ b/src/macro-core/macro-tree.cpp @@ -1260,6 +1260,7 @@ void MacroTree::UngroupSelectedGroups() void MacroTree::SelectionChangedHelper(const QItemSelection &, const QItemSelection &) { + emit MacroSelectionAboutToChange(); emit MacroSelectionChanged(); } diff --git a/src/macro-core/macro-tree.hpp b/src/macro-core/macro-tree.hpp index eb40f9e9..423a6f58 100644 --- a/src/macro-core/macro-tree.hpp +++ b/src/macro-core/macro-tree.hpp @@ -142,6 +142,7 @@ public slots: const QItemSelection &); signals: + void MacroSelectionAboutToChange(); void MacroSelectionChanged(); protected: diff --git a/src/macro-core/macro.cpp b/src/macro-core/macro.cpp index b9b5861e..1b2ae8cc 100644 --- a/src/macro-core/macro.cpp +++ b/src/macro-core/macro.cpp @@ -176,14 +176,11 @@ bool Macro::CeckMatch() } vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched); - bool matchedBeforeOnChangeCheck = _matched; - if (_matched && _matchOnChange && _lastMatched) { - vblog(LOG_INFO, "ignore match for Macro %s (on change)", - _name.c_str()); - _matched = false; - SetOnChangeHighlight(); + _conditionSateChanged = _lastMatched != _matched; + if (!_conditionSateChanged) { + _onPreventedActionExecution = true; } - _lastMatched = matchedBeforeOnChangeCheck; + _lastMatched = _matched; _lastCheckTime = std::chrono::high_resolution_clock::now(); return _matched; } @@ -223,6 +220,28 @@ bool Macro::ExecutedSince( return _lastExecutionTime > time; } +bool Macro::ShouldRunActions() const +{ + const bool hasActionsToExecute = + (_matched || _elseActions.size() > 0) && + (!_performActionsOnChange || _conditionSateChanged); + + if (VerboseLoggingEnabled() && _performActionsOnChange && + !_conditionSateChanged) { + if (_matched && _actions.size() > 0) { + blog(LOG_INFO, "skip actions for Macro %s (on change)", + _name.c_str()); + } + if (!_matched && _elseActions.size() > 0) { + blog(LOG_INFO, + "skip else actions for Macro %s (on change)", + _name.c_str()); + } + } + + return hasActionsToExecute; +} + int64_t Macro::MsSinceLastCheck() const { if (_lastCheckTime.time_since_epoch().count() == 0) { @@ -254,20 +273,24 @@ void Macro::ResetTimers() void Macro::RunActions(bool &retVal, bool ignorePause) { bool ret = true; - for (auto &a : _actions) { - if (a->Enabled()) { - a->LogAction(); - ret = ret && a->PerformAction(); + const std::deque> &actionsToExecute = + _matched ? _actions : _elseActions; + vblog(LOG_INFO, "running %sactions of %s", _matched ? "" : "else ", + _name.c_str()); + for (auto &action : actionsToExecute) { + if (action->Enabled()) { + action->LogAction(); + ret = ret && action->PerformAction(); } else { vblog(LOG_INFO, "skipping disabled action %s", - a->GetId().c_str()); + action->GetId().c_str()); } if (!ret || (_paused && !ignorePause) || _stop || _die) { retVal = ret; break; } - if (a->Enabled()) { - a->SetHighlight(); + if (action->Enabled()) { + action->SetHighlight(); } } _done = true; @@ -279,16 +302,16 @@ void Macro::RunActions(bool ignorePause) RunActions(unused, ignorePause); } -void Macro::SetOnChangeHighlight() -{ - _onChangeTriggered = true; -} - bool Macro::DockIsVisible() const { return _dock && _dockAction && _dock->isVisible(); } +void Macro::SetMatchOnChange(bool onChange) +{ + _performActionsOnChange = onChange; +} + void Macro::SetPaused(bool pause) { if (_paused && !pause) { @@ -332,22 +355,39 @@ std::deque> &Macro::Actions() return _actions; } -void Macro::UpdateActionIndices() +std::deque> &Macro::ElseActions() +{ + return _elseActions; +} + +static void updateIndicesHelper(std::deque> &list) { int idx = 0; - for (auto a : _actions) { - a->SetIndex(idx); + for (auto segment : list) { + segment->SetIndex(idx); idx++; } } +void Macro::UpdateActionIndices() +{ + std::deque> list(_actions.begin(), + _actions.end()); + updateIndicesHelper(list); +} + +void Macro::UpdateElseActionIndices() +{ + std::deque> list(_elseActions.begin(), + _elseActions.end()); + updateIndicesHelper(list); +} + void Macro::UpdateConditionIndices() { - int idx = 0; - for (auto c : _conditions) { - c->SetIndex(idx); - idx++; - } + std::deque> list(_conditions.begin(), + _conditions.end()); + updateIndicesHelper(list); } std::shared_ptr Macro::Parent() const @@ -360,56 +400,57 @@ bool Macro::Save(obs_data_t *obj) const obs_data_set_string(obj, "name", _name.c_str()); obs_data_set_bool(obj, "pause", _paused); obs_data_set_bool(obj, "parallel", _runInParallel); - obs_data_set_bool(obj, "onChange", _matchOnChange); + obs_data_set_bool(obj, "onChange", _performActionsOnChange); obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart); obs_data_set_bool(obj, "group", _isGroup); if (_isGroup) { - auto groupData = obs_data_create(); + OBSDataAutoRelease groupData = obs_data_create(); obs_data_set_bool(groupData, "collapsed", _isCollapsed); obs_data_set_int(groupData, "size", _groupSize); obs_data_set_obj(obj, "groupData", groupData); - obs_data_release(groupData); return true; } SaveDockSettings(obj); + SaveSplitterPos(_actionConditionSplitterPosition, obj, + "macroActionConditionSplitterPosition"); + SaveSplitterPos(_elseActionSplitterPosition, obj, + "macroElseActionSplitterPosition"); + obs_data_set_bool(obj, "registerHotkeys", _registerHotkeys); - obs_data_array_t *pauseHotkey = obs_hotkey_save(_pauseHotkey); + OBSDataArrayAutoRelease pauseHotkey = obs_hotkey_save(_pauseHotkey); obs_data_set_array(obj, "pauseHotkey", pauseHotkey); - obs_data_array_release(pauseHotkey); - obs_data_array_t *unpauseHotkey = obs_hotkey_save(_unpauseHotkey); + OBSDataArrayAutoRelease unpauseHotkey = obs_hotkey_save(_unpauseHotkey); obs_data_set_array(obj, "unpauseHotkey", unpauseHotkey); - obs_data_array_release(unpauseHotkey); - obs_data_array_t *togglePauseHotkey = + OBSDataArrayAutoRelease togglePauseHotkey = obs_hotkey_save(_togglePauseHotkey); obs_data_set_array(obj, "togglePauseHotkey", togglePauseHotkey); - obs_data_array_release(togglePauseHotkey); - obs_data_array_t *conditions = obs_data_array_create(); + OBSDataArrayAutoRelease conditions = obs_data_array_create(); for (auto &c : _conditions) { - obs_data_t *array_obj = obs_data_create(); - - c->Save(array_obj); - obs_data_array_push_back(conditions, array_obj); - - obs_data_release(array_obj); + OBSDataAutoRelease arrayObj = obs_data_create(); + c->Save(arrayObj); + obs_data_array_push_back(conditions, arrayObj); } obs_data_set_array(obj, "conditions", conditions); - obs_data_array_release(conditions); - obs_data_array_t *actions = obs_data_array_create(); + OBSDataArrayAutoRelease actions = obs_data_array_create(); for (auto &a : _actions) { - obs_data_t *array_obj = obs_data_create(); - - a->Save(array_obj); - obs_data_array_push_back(actions, array_obj); - - obs_data_release(array_obj); + OBSDataAutoRelease arrayObj = obs_data_create(); + a->Save(arrayObj); + obs_data_array_push_back(actions, arrayObj); } obs_data_set_array(obj, "actions", actions); - obs_data_array_release(actions); + + OBSDataArrayAutoRelease elseActions = obs_data_array_create(); + for (auto &a : _elseActions) { + OBSDataAutoRelease arrayObj = obs_data_create(); + a->Save(arrayObj); + obs_data_array_push_back(elseActions, arrayObj); + } + obs_data_set_array(obj, "elseActions", elseActions); return true; } @@ -453,73 +494,69 @@ bool Macro::Load(obs_data_t *obj) _name = obs_data_get_string(obj, "name"); _paused = obs_data_get_bool(obj, "pause"); _runInParallel = obs_data_get_bool(obj, "parallel"); - _matchOnChange = obs_data_get_bool(obj, "onChange"); + _performActionsOnChange = obs_data_get_bool(obj, "onChange"); _skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart"); _isGroup = obs_data_get_bool(obj, "group"); if (_isGroup) { - auto groupData = obs_data_get_obj(obj, "groupData"); + OBSDataAutoRelease groupData = + obs_data_get_obj(obj, "groupData"); _isCollapsed = obs_data_get_bool(groupData, "collapsed"); _groupSize = obs_data_get_int(groupData, "size"); - obs_data_release(groupData); return true; } LoadDockSettings(obj); + LoadSplitterPos(_actionConditionSplitterPosition, obj, + "macroActionConditionSplitterPosition"); + LoadSplitterPos(_elseActionSplitterPosition, obj, + "macroElseActionSplitterPosition"); + obs_data_set_default_bool(obj, "registerHotkeys", true); _registerHotkeys = obs_data_get_bool(obj, "registerHotkeys"); if (_registerHotkeys) { SetupHotkeys(); } - obs_data_array_t *pauseHotkey = obs_data_get_array(obj, "pauseHotkey"); + OBSDataArrayAutoRelease pauseHotkey = + obs_data_get_array(obj, "pauseHotkey"); obs_hotkey_load(_pauseHotkey, pauseHotkey); - obs_data_array_release(pauseHotkey); - obs_data_array_t *unpauseHotkey = + OBSDataArrayAutoRelease unpauseHotkey = obs_data_get_array(obj, "unpauseHotkey"); obs_hotkey_load(_unpauseHotkey, unpauseHotkey); - obs_data_array_release(unpauseHotkey); - obs_data_array_t *togglePauseHotkey = + OBSDataArrayAutoRelease togglePauseHotkey = obs_data_get_array(obj, "togglePauseHotkey"); obs_hotkey_load(_togglePauseHotkey, togglePauseHotkey); - obs_data_array_release(togglePauseHotkey); SetHotkeysDesc(); bool root = true; - obs_data_array_t *conditions = obs_data_get_array(obj, "conditions"); + OBSDataArrayAutoRelease conditions = + obs_data_get_array(obj, "conditions"); size_t count = obs_data_array_count(conditions); - for (size_t i = 0; i < count; i++) { - obs_data_t *array_obj = obs_data_array_item(conditions, i); - - std::string id = obs_data_get_string(array_obj, "id"); - + OBSDataAutoRelease arrayObj = + obs_data_array_item(conditions, i); + std::string id = obs_data_get_string(arrayObj, "id"); auto newEntry = MacroConditionFactory::Create(id, this); if (newEntry) { _conditions.emplace_back(newEntry); auto c = _conditions.back().get(); - c->Load(array_obj); + c->Load(arrayObj); setValidLogic(c, root, _name); } else { blog(LOG_WARNING, "discarding condition entry with unknown id (%s) for macro %s", id.c_str(), _name.c_str()); } - - obs_data_release(array_obj); root = false; } - obs_data_array_release(conditions); UpdateConditionIndices(); - obs_data_array_t *actions = obs_data_get_array(obj, "actions"); + OBSDataArrayAutoRelease actions = obs_data_get_array(obj, "actions"); count = obs_data_array_count(actions); - for (size_t i = 0; i < count; i++) { - obs_data_t *array_obj = obs_data_array_item(actions, i); - + OBSDataAutoRelease array_obj = obs_data_array_item(actions, i); std::string id = obs_data_get_string(array_obj, "id"); - auto newEntry = MacroActionFactory::Create(id, this); if (newEntry) { _actions.emplace_back(newEntry); @@ -529,11 +566,27 @@ bool Macro::Load(obs_data_t *obj) "discarding action entry with unknown id (%s) for macro %s", id.c_str(), _name.c_str()); } - - obs_data_release(array_obj); } - obs_data_array_release(actions); UpdateActionIndices(); + + OBSDataArrayAutoRelease elseActions = + obs_data_get_array(obj, "elseActions"); + count = obs_data_array_count(elseActions); + for (size_t i = 0; i < count; i++) { + OBSDataAutoRelease array_obj = + obs_data_array_item(elseActions, i); + std::string id = obs_data_get_string(array_obj, "id"); + auto newEntry = MacroActionFactory::Create(id, this); + if (newEntry) { + _elseActions.emplace_back(newEntry); + _elseActions.back()->Load(array_obj); + } else { + blog(LOG_WARNING, + "discarding elseAction entry with unknown id (%s) for macro %s", + id.c_str(), _name.c_str()); + } + } + UpdateElseActionIndices(); return true; } @@ -552,7 +605,12 @@ bool Macro::SwitchesScene() const { MacroActionSwitchScene temp(nullptr); auto sceneSwitchId = temp.GetId(); - for (auto &a : _actions) { + for (const auto &a : _actions) { + if (a->GetId() == sceneSwitchId) { + return true; + } + } + for (const auto &a : _elseActions) { if (a->GetId() == sceneSwitchId) { return true; } @@ -560,10 +618,36 @@ bool Macro::SwitchesScene() const return false; } +const QList &Macro::GetActionConditionSplitterPosition() const +{ + return _actionConditionSplitterPosition; +} + +void Macro::SetActionConditionSplitterPosition(const QList sizes) +{ + _actionConditionSplitterPosition = sizes; +} + +const QList &Macro::GetElseActionSplitterPosition() const +{ + return _elseActionSplitterPosition; +} + +void Macro::SetElseActionSplitterPosition(const QList sizes) +{ + _elseActionSplitterPosition = sizes; +} + +bool Macro::HasValidSplitterPositions() const +{ + return !_actionConditionSplitterPosition.empty() && + !_elseActionSplitterPosition.empty(); +} + bool Macro::OnChangePreventedActionsRecently() { - if (_onChangeTriggered) { - _onChangeTriggered = false; + if (_onPreventedActionExecution) { + _onPreventedActionExecution = false; return true; } return false; @@ -571,7 +655,7 @@ bool Macro::OnChangePreventedActionsRecently() void Macro::ResetUIHelpers() { - _onChangeTriggered = false; + _onPreventedActionExecution = false; for (auto c : _conditions) { c->Highlight(); } @@ -1014,7 +1098,7 @@ bool SwitcherData::CheckMacros() { bool ret = false; for (auto &m : macros) { - if (m->CeckMatch()) { + if (m->CeckMatch() || m->ElseActions().size() > 0) { ret = true; // This has to be performed here for now as actions are // not performed immediately after checking conditions. @@ -1049,7 +1133,7 @@ bool SwitcherData::RunMacros() } for (auto &m : runPhaseMacros) { - if (!m || !m->Matched()) { + if (!m || !m->ShouldRunActions()) { continue; } if (firstInterval && m->SkipExecOnStart()) { diff --git a/src/macro-core/macro.hpp b/src/macro-core/macro.hpp index 6266fa92..9dce81fc 100644 --- a/src/macro-core/macro.hpp +++ b/src/macro-core/macro.hpp @@ -28,6 +28,7 @@ public: bool PerformActions(bool forceParallel = false, bool ignorePause = false); bool Matched() const { return _matched; } + bool ShouldRunActions() const; int64_t MsSinceLastCheck() const; std::string Name() const { return _name; } void SetName(const std::string &name); @@ -35,8 +36,8 @@ public: bool RunInParallel() const { return _runInParallel; } void SetPaused(bool pause = true); bool Paused() const { return _paused; } - void SetMatchOnChange(bool onChange) { _matchOnChange = onChange; } - bool MatchOnChange() const { return _matchOnChange; } + void SetMatchOnChange(bool onChange); + bool MatchOnChange() const { return _performActionsOnChange; } void SetSkipExecOnStart(bool skip) { _skipExecOnStart = skip; } bool SkipExecOnStart() const { return _skipExecOnStart; } int RunCount() const { return _runCount; }; @@ -48,7 +49,9 @@ public: std::deque> &Conditions(); std::deque> &Actions(); + std::deque> &ElseActions(); void UpdateActionIndices(); + void UpdateElseActionIndices(); void UpdateConditionIndices(); // Group controls @@ -80,6 +83,11 @@ public: bool SwitchesScene() const; // UI helpers + const QList &GetActionConditionSplitterPosition() const; + void SetActionConditionSplitterPosition(const QList); + const QList &GetElseActionSplitterPosition() const; + void SetElseActionSplitterPosition(const QList); + bool HasValidSplitterPositions() const; bool ExecutedSince(const std::chrono::high_resolution_clock::time_point &); bool OnChangePreventedActionsRecently(); @@ -115,7 +123,6 @@ private: void SetHotkeysDesc() const; void RunActions(bool &ret, bool ignorePause); void RunActions(bool ignorePause); - void SetOnChangeHighlight(); bool DockIsVisible() const; void SetDockWidgetName() const; void SaveDockSettings(obs_data_t *obj) const; @@ -133,6 +140,7 @@ private: std::deque> _conditions; std::deque> _actions; + std::deque> _elseActions; std::weak_ptr _parent; uint32_t _groupSize = 0; @@ -142,7 +150,8 @@ private: bool _runInParallel = false; bool _matched = false; bool _lastMatched = false; - bool _matchOnChange = true; + bool _conditionSateChanged = false; + bool _performActionsOnChange = true; bool _skipExecOnStart = false; bool _paused = false; int _runCount = 0; @@ -151,7 +160,11 @@ private: obs_hotkey_id _unpauseHotkey = OBS_INVALID_HOTKEY_ID; obs_hotkey_id _togglePauseHotkey = OBS_INVALID_HOTKEY_ID; - bool _onChangeTriggered = false; + // UI helpers + bool _onPreventedActionExecution = false; + + QList _actionConditionSplitterPosition; + QList _elseActionSplitterPosition; bool _registerDock = false; bool _dockHasRunButton = true; diff --git a/src/switcher-data.hpp b/src/switcher-data.hpp index c2defd2c..bb706d97 100644 --- a/src/switcher-data.hpp +++ b/src/switcher-data.hpp @@ -214,7 +214,6 @@ public: bool saveWindowGeo = false; QPoint windowPos = {}; QSize windowSize = {}; - QList macroActionConditionSplitterPosition; QList macroListMacroEditSplitterPosition; /* --- End of UI section --- */