From f723212394a8c44ddebbaa9cdc0231b86b4a58d4 Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Mon, 6 May 2024 20:07:43 +0200 Subject: [PATCH] Add copy-paste support for individual macro segments --- CMakeLists.txt | 2 + data/locale/en-US.ini | 2 + lib/advanced-scene-switcher.hpp | 9 ++ lib/macro/macro-action-edit.cpp | 109 ++++++++++++++++--------- lib/macro/macro-condition-edit.cpp | 28 +++++-- lib/macro/macro-segment-copy-paste.cpp | 104 +++++++++++++++++++++++ lib/macro/macro-segment-copy-paste.hpp | 8 ++ lib/macro/macro-tab.cpp | 53 ++++++++---- 8 files changed, 254 insertions(+), 61 deletions(-) create mode 100644 lib/macro/macro-segment-copy-paste.cpp create mode 100644 lib/macro/macro-segment-copy-paste.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index f86c94e8..45df62f0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,6 +128,8 @@ target_sources( lib/macro/macro-ref.hpp lib/macro/macro-run-button.cpp lib/macro/macro-run-button.hpp + lib/macro/macro-segment-copy-paste.cpp + lib/macro/macro-segment-copy-paste.hpp lib/macro/macro-segment-list.cpp lib/macro/macro-segment-list.hpp lib/macro/macro-segment-selection.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index dcb53fad..3ae46958 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -177,6 +177,8 @@ AdvSceneSwitcher.macroTab.minimize="Minimize" AdvSceneSwitcher.macroTab.segment.useCustomLabel="Use custom label" AdvSceneSwitcher.macroTab.segment.defaultCustomLabel="My label" AdvSceneSwitcher.macroTab.segment.setCustomLabel="Set label ..." +AdvSceneSwitcher.macroTab.segment.copy="Copy" +AdvSceneSwitcher.macroTab.segment.paste="Paste" AdvSceneSwitcher.macroTab.highlightSettings="Visual settings" AdvSceneSwitcher.macroTab.hotkeySettings="Hotkey settings" AdvSceneSwitcher.macroTab.generalSettings="General settings" diff --git a/lib/advanced-scene-switcher.hpp b/lib/advanced-scene-switcher.hpp index be35a710..e618957d 100644 --- a/lib/advanced-scene-switcher.hpp +++ b/lib/advanced-scene-switcher.hpp @@ -12,6 +12,7 @@ class MacroActionEdit; class MacroConditionEdit; class Duration; class SequenceWidget; +enum class LogicType; struct SceneGroup; /******************************************************************************* @@ -152,12 +153,16 @@ public slots: void SetElseActionsStateToVisible(); void MacroActionSelectionChanged(int idx); void MacroActionReorder(int to, int target); + void AddMacroAction(Macro *macro, int idx, const std::string &id, + obs_data_t *data); 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(Macro *macro, int idx, const std::string &id, + obs_data_t *data); void AddMacroElseAction(int idx); void RemoveMacroElseAction(int idx); void SwapElseActions(Macro *m, int pos1, int pos2); @@ -166,12 +171,16 @@ public slots: void MacroConditionSelectionChanged(int idx); void MacroConditionReorder(int to, int target); void AddMacroCondition(int idx); + void AddMacroCondition(Macro *macro, int idx, const std::string &id, + obs_data_t *data, LogicType logic); void RemoveMacroCondition(int idx); void MoveMacroConditionUp(int idx); void MoveMacroConditionDown(int idx); void HighlightControls(); void HighlightOnChange(); void on_macroProperties_clicked(); + void CopyMacroSegment(); + void PasteMacroSegment(); signals: void MacroAdded(const QString &name); diff --git a/lib/macro/macro-action-edit.cpp b/lib/macro/macro-action-edit.cpp index 5b36eac3..007bd737 100644 --- a/lib/macro/macro-action-edit.cpp +++ b/lib/macro/macro-action-edit.cpp @@ -161,33 +161,20 @@ std::shared_ptr MacroActionEdit::Data() const return *_entryData; } -void AdvSceneSwitcher::AddMacroAction(int idx) +void AdvSceneSwitcher::AddMacroAction(Macro *macro, int idx, + const std::string &id, obs_data_t *data) { - auto macro = GetSelectedMacro(); - if (!macro) { - return; - } - if (idx < 0 || idx > (int)macro->Actions().size()) { + assert(false); return; } - std::string id; - if (idx - 1 >= 0) { - id = macro->Actions().at(idx - 1)->GetId(); - } else { - id = MacroAction::GetDefaultID(); - } { auto lock = LockContext(); - macro->Actions().emplace( - macro->Actions().begin() + idx, - MacroActionFactory::Create(id, macro.get())); - if (idx - 1 >= 0) { - auto data = obs_data_create(); - macro->Actions().at(idx - 1)->Save(data); + macro->Actions().emplace(macro->Actions().begin() + idx, + MacroActionFactory::Create(id, macro)); + if (data) { macro->Actions().at(idx)->Load(data); - obs_data_release(data); } macro->Actions().at(idx)->PostLoad(); RunPostLoadSteps(); @@ -198,9 +185,37 @@ void AdvSceneSwitcher::AddMacroAction(int idx) SetActionData(*macro); } HighlightAction(idx); + ui->actionsList->SetHelpMsgVisible(false); emit(MacroSegmentOrderChanged()); } +void AdvSceneSwitcher::AddMacroAction(int idx) +{ + auto macro = GetSelectedMacro(); + if (!macro) { + return; + } + + if (idx < 0 || idx > (int)macro->Actions().size()) { + assert(false); + return; + } + + std::string id; + if (idx - 1 >= 0) { + id = macro->Actions().at(idx - 1)->GetId(); + } else { + id = MacroAction::GetDefaultID(); + } + + OBSDataAutoRelease data; + if (idx - 1 >= 0) { + data = obs_data_create(); + macro->Actions().at(idx - 1)->Save(data); + } + AddMacroAction(macro.get(), idx, id, data); +} + void AdvSceneSwitcher::on_actionAdd_clicked() { auto macro = GetSelectedMacro(); @@ -216,7 +231,6 @@ void AdvSceneSwitcher::on_actionAdd_clicked() if (currentActionIdx != -1) { MacroActionSelectionChanged(currentActionIdx + 1); } - ui->actionsList->SetHelpMsgVisible(false); } void AdvSceneSwitcher::RemoveMacroAction(int idx) @@ -311,7 +325,6 @@ void AdvSceneSwitcher::on_elseActionAdd_clicked() if (currentElseActionIdx != -1) { MacroElseActionSelectionChanged(currentElseActionIdx + 1); } - ui->elseActionsList->SetHelpMsgVisible(false); } void AdvSceneSwitcher::on_elseActionRemove_clicked() @@ -450,31 +463,21 @@ void AdvSceneSwitcher::MacroElseActionReorder(int to, int from) emit(MacroSegmentOrderChanged()); } -void AdvSceneSwitcher::AddMacroElseAction(int idx) +void AdvSceneSwitcher::AddMacroElseAction(Macro *macro, int idx, + const std::string &id, + obs_data_t *data) { - auto macro = GetSelectedMacro(); - if (!macro) { - return; - } - if (idx < 0 || idx > (int)macro->ElseActions().size()) { + assert(false); return; } - std::string id; - if (idx - 1 >= 0) { - id = macro->ElseActions().at(idx - 1)->GetId(); - } else { - id = MacroAction::GetDefaultID(); - } { auto lock = LockContext(); - 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().emplace(macro->ElseActions().begin() + idx, + MacroActionFactory::Create(id, + macro)); + if (data) { macro->ElseActions().at(idx)->Load(data); } macro->ElseActions().at(idx)->PostLoad(); @@ -486,9 +489,37 @@ void AdvSceneSwitcher::AddMacroElseAction(int idx) SetElseActionData(*macro); } HighlightElseAction(idx); + ui->elseActionsList->SetHelpMsgVisible(false); emit(MacroSegmentOrderChanged()); } +void AdvSceneSwitcher::AddMacroElseAction(int idx) +{ + auto macro = GetSelectedMacro(); + if (!macro) { + return; + } + + if (idx < 0 || idx > (int)macro->ElseActions().size()) { + assert(false); + return; + } + + std::string id; + if (idx - 1 >= 0) { + id = macro->ElseActions().at(idx - 1)->GetId(); + } else { + id = MacroAction::GetDefaultID(); + } + + OBSDataAutoRelease data; + if (idx - 1 >= 0) { + data = obs_data_create(); + macro->ElseActions().at(idx - 1)->Save(data); + } + AddMacroElseAction(macro.get(), idx, id, data); +} + void AdvSceneSwitcher::RemoveMacroElseAction(int idx) { auto macro = GetSelectedMacro(); diff --git a/lib/macro/macro-condition-edit.cpp b/lib/macro/macro-condition-edit.cpp index 7f25d490..307c6a30 100644 --- a/lib/macro/macro-condition-edit.cpp +++ b/lib/macro/macro-condition-edit.cpp @@ -299,6 +299,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx) } if (idx < 0 || idx > (int)macro->Conditions().size()) { + assert(false); return; } @@ -315,16 +316,31 @@ void AdvSceneSwitcher::AddMacroCondition(int idx) id = MacroCondition::GetDefaultID(); logic = LogicType::ROOT_NONE; } + + OBSDataAutoRelease data; + if (idx - 1 >= 0) { + data = obs_data_create(); + macro->Conditions().at(idx - 1)->Save(data); + } + AddMacroCondition(macro.get(), idx, id, data.Get(), logic); +} + +void AdvSceneSwitcher::AddMacroCondition(Macro *macro, int idx, + const std::string &id, + obs_data_t *data, LogicType logic) +{ + if (idx < 0 || idx > (int)macro->Conditions().size()) { + assert(false); + return; + } + { auto lock = LockContext(); auto cond = macro->Conditions().emplace( macro->Conditions().begin() + idx, - MacroConditionFactory::Create(id, macro.get())); - if (idx - 1 >= 0) { - auto data = obs_data_create(); - macro->Conditions().at(idx - 1)->Save(data); + MacroConditionFactory::Create(id, macro)); + if (data) { macro->Conditions().at(idx)->Load(data); - obs_data_release(data); } macro->Conditions().at(idx)->PostLoad(); RunPostLoadSteps(); @@ -337,6 +353,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx) SetConditionData(*macro); } HighlightCondition(idx); + ui->conditionsList->SetHelpMsgVisible(false); emit(MacroSegmentOrderChanged()); } @@ -355,7 +372,6 @@ void AdvSceneSwitcher::on_conditionAdd_clicked() if (currentConditionIdx != -1) { MacroConditionSelectionChanged(currentConditionIdx + 1); } - ui->conditionsList->SetHelpMsgVisible(false); } void AdvSceneSwitcher::RemoveMacroCondition(int idx) diff --git a/lib/macro/macro-segment-copy-paste.cpp b/lib/macro/macro-segment-copy-paste.cpp new file mode 100644 index 00000000..261c8dc0 --- /dev/null +++ b/lib/macro/macro-segment-copy-paste.cpp @@ -0,0 +1,104 @@ +#include "advanced-scene-switcher.hpp" +#include "macro.hpp" + +#include + +namespace advss { + +struct MacroSegmentCopyInfo { + enum class Type { NONE, CONDITION, ACTION, ELSE }; + Type type = Type::NONE; + std::shared_ptr segment; +}; +static MacroSegmentCopyInfo copyInfo; + +void AdvSceneSwitcher::CopyMacroSegment() +{ + copyInfo.segment.reset(); + copyInfo.type = MacroSegmentCopyInfo::Type::NONE; + + if (currentConditionIdx == -1 && currentActionIdx == -1 && + currentElseActionIdx == -1) { + return; + } + + auto macro = GetSelectedMacro(); + if (!macro) { + return; + } + + if (currentConditionIdx != -1) { + copyInfo.type = MacroSegmentCopyInfo::Type::CONDITION; + copyInfo.segment = macro->Conditions().at(currentConditionIdx); + } else if (currentActionIdx != -1) { + copyInfo.type = MacroSegmentCopyInfo::Type::ACTION; + copyInfo.segment = macro->Actions().at(currentActionIdx); + } else if (currentElseActionIdx != -1) { + copyInfo.type = MacroSegmentCopyInfo::Type::ELSE; + copyInfo.segment = + macro->ElseActions().at(currentElseActionIdx); + } else { + assert(false); + } +} + +void AdvSceneSwitcher::PasteMacroSegment() +{ + if (copyInfo.type == MacroSegmentCopyInfo::Type::NONE) { + return; + } + + auto macro = GetSelectedMacro(); + if (!macro || !copyInfo.segment) { + return; + } + + OBSDataAutoRelease data = obs_data_create(); + copyInfo.segment->Save(data); + + switch (copyInfo.type) { + case MacroSegmentCopyInfo::Type::CONDITION: { + const auto condition = std::static_pointer_cast( + copyInfo.segment); + auto logic = condition->GetLogicType(); + if (logic > LogicType::ROOT_LAST && + macro->Conditions().empty()) { + logic = LogicType::ROOT_NONE; + } + if (logic < LogicType::ROOT_LAST && + !macro->Conditions().empty()) { + logic = LogicType::OR; + } + AddMacroCondition(macro.get(), macro->Conditions().size(), + copyInfo.segment->GetId(), data.Get(), logic); + break; + } + case MacroSegmentCopyInfo::Type::ACTION: + AddMacroAction(macro.get(), macro->Actions().size(), + copyInfo.segment->GetId(), data.Get()); + break; + case MacroSegmentCopyInfo::Type::ELSE: + AddMacroElseAction(macro.get(), macro->ElseActions().size(), + copyInfo.segment->GetId(), data.Get()); + break; + default: + break; + } +} + +bool MacroSegmentIsInClipboard() +{ + return copyInfo.type != MacroSegmentCopyInfo::Type::NONE; +} + +void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window) +{ + auto copyShortcut = new QShortcut(QKeySequence("Ctrl+C"), window); + QWidget::connect(copyShortcut, &QShortcut::activated, window, + &AdvSceneSwitcher::CopyMacroSegment); + auto pasteShortcut = new QShortcut(QKeySequence("Ctrl+V"), window); + QWidget::connect(pasteShortcut, &QShortcut::activated, window, + &AdvSceneSwitcher::PasteMacroSegment); +} + +} // namespace advss diff --git a/lib/macro/macro-segment-copy-paste.hpp b/lib/macro/macro-segment-copy-paste.hpp new file mode 100644 index 00000000..67398350 --- /dev/null +++ b/lib/macro/macro-segment-copy-paste.hpp @@ -0,0 +1,8 @@ +namespace advss { + +class AdvSceneSwitcher; + +bool MacroSegmentIsInClipboard(); +void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window); + +} // namespace advss diff --git a/lib/macro/macro-tab.cpp b/lib/macro/macro-tab.cpp index e998202b..4cc0a310 100644 --- a/lib/macro/macro-tab.cpp +++ b/lib/macro/macro-tab.cpp @@ -4,6 +4,7 @@ #include "macro-condition-edit.hpp" #include "macro-export-import-dialog.hpp" #include "macro-properties.hpp" +#include "macro-segment-copy-paste.hpp" #include "macro-tree.hpp" #include "macro.hpp" #include "math-helpers.hpp" @@ -891,6 +892,8 @@ void AdvSceneSwitcher::SetupMacroTab() switcher->macroListMacroEditSplitterPosition); } } + + SetupSegmentCopyPasteShortcutHandlers(this); } void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos) @@ -978,16 +981,19 @@ static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos, MacroSegmentList *list) { QMenu menu; - menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"), - ss, [ss, expand]() { expand(ss); }); - menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"), - ss, [ss, collapse]() { collapse(ss); }); - menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.maximize"), - ss, [ss, maximize]() { maximize(ss); }); - menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.minimize"), - ss, [ss, minimize]() { minimize(ss); }); - auto segmentEdit = list->WidgetAt(pos); + + auto copy = menu.addAction( + obs_module_text("AdvSceneSwitcher.macroTab.segment.copy"), ss, + [ss]() { ss->CopyMacroSegment(); }); + copy->setEnabled(!!segmentEdit); + auto paste = menu.addAction( + obs_module_text("AdvSceneSwitcher.macroTab.segment.paste"), ss, + [ss]() { ss->PasteMacroSegment(); }); + paste->setEnabled(MacroSegmentIsInClipboard()); + + menu.addSeparator(); + if (segmentEdit) { auto customLabel = menu.addAction(obs_module_text( "AdvSceneSwitcher.macroTab.segment.useCustomLabel")); @@ -999,6 +1005,21 @@ static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos, std::bind(handleCustomLabelChange, segmentEdit, customLabel)); } + + menu.addSeparator(); + + menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"), + ss, [ss, expand]() { expand(ss); }); + menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"), + ss, [ss, collapse]() { collapse(ss); }); + + menu.addSeparator(); + + menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.maximize"), + ss, [ss, maximize]() { maximize(ss); }); + menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.minimize"), + ss, [ss, minimize]() { minimize(ss); }); + menu.exec(list->mapToGlobal(pos)); } @@ -1371,17 +1392,17 @@ void AdvSceneSwitcher::HighlightControls() fadeWidgets(actionControls, false); fadeWidgets(elseActionControls, false); } else if (currentConditionIdx != -1) { - fadeWidgets(conditionControls, true); - fadeWidgets(actionControls, false); - fadeWidgets(elseActionControls, false); - } else if (currentActionIdx != -1) { fadeWidgets(conditionControls, false); fadeWidgets(actionControls, true); - fadeWidgets(elseActionControls, false); - } else if (currentElseActionIdx != -1) { - fadeWidgets(conditionControls, false); + fadeWidgets(elseActionControls, true); + } else if (currentActionIdx != -1) { + fadeWidgets(conditionControls, true); fadeWidgets(actionControls, false); fadeWidgets(elseActionControls, true); + } else if (currentElseActionIdx != -1) { + fadeWidgets(conditionControls, true); + fadeWidgets(actionControls, true); + fadeWidgets(elseActionControls, false); } else { assert(false); }