diff --git a/CMakeLists.txt b/CMakeLists.txt index 8573a9c7..5c3bb5ea 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -215,6 +215,7 @@ set(advanced-scene-switcher_HEADERS src/headers/macro-action-media.hpp src/headers/macro-action-plugin-state.hpp src/headers/macro-action-preview-scene.hpp + src/headers/macro-action-random.hpp src/headers/macro-action-recording.hpp src/headers/macro-action-replay-buffer.hpp src/headers/macro-action-run.hpp @@ -307,6 +308,7 @@ set(advanced-scene-switcher_SOURCES src/macro-action-media.cpp src/macro-action-plugin-state.cpp src/macro-action-preview-scene.cpp + src/macro-action-random.cpp src/macro-action-recording.cpp src/macro-action-replay-buffer.cpp src/macro-action-run.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index dfae3754..82f81df6 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -353,6 +353,10 @@ AdvSceneSwitcher.action.timer.type.continue="Continue" AdvSceneSwitcher.action.timer.type.reset="Reset" AdvSceneSwitcher.action.timer.type.setTimeRemaining="Set time remaining of" AdvSceneSwitcher.action.timer.entry="{{timerAction}} timers on {{macros}} {{duration}}" +AdvSceneSwitcher.action.random="Random" +AdvSceneSwitcher.action.random.arguments="Macros:" +AdvSceneSwitcher.action.random.addArgument="Add Macro" +AdvSceneSwitcher.action.random.entry="Run {{macroSelection}}" ; Transition Tab AdvSceneSwitcher.transitionTab.title="Transition" diff --git a/src/headers/macro-action-random.hpp b/src/headers/macro-action-random.hpp new file mode 100644 index 00000000..8011d1f2 --- /dev/null +++ b/src/headers/macro-action-random.hpp @@ -0,0 +1,63 @@ +#pragma once +#include "macro-action-edit.hpp" +#include "macro-selection.hpp" + +#include +#include +#include + +class MacroActionRandom : public MultiMacroRefAction { +public: + bool PerformAction(); + void LogAction(); + bool Save(obs_data_t *obj); + bool Load(obs_data_t *obj); + std::string GetId() { return id; }; + static std::shared_ptr Create() + { + return std::make_shared(); + } + + // TODO: add weights to each macro ... + // std::unordered_map _weights; + +private: + MacroRef lastRandomMacro; + static bool _registered; + static const std::string id; +}; + +class MacroActionRandomEdit : public QWidget { + Q_OBJECT + +public: + MacroActionRandomEdit( + QWidget *parent, + std::shared_ptr entryData = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr action) + { + return new MacroActionRandomEdit( + parent, + std::dynamic_pointer_cast(action)); + } + +private slots: + void MacroRemove(const QString &name); + void MacroRename(const QString &oldName, const QString &newName); + void AddMacro(); + void RemoveMacro(); + int FindEntry(const std::string ¯o); + void MacroSelectionChanged(int idx); + +protected: + std::shared_ptr _entryData; + +private: + MacroSelection *_macroSelection; + QListWidget *_macroList; + QPushButton *_add; + QPushButton *_remove; + bool _loading = true; +}; diff --git a/src/headers/macro.hpp b/src/headers/macro.hpp index 7b8d7634..883391d5 100644 --- a/src/headers/macro.hpp +++ b/src/headers/macro.hpp @@ -145,3 +145,9 @@ public: void ResolveMacroRef(); MacroRef _macro; }; + +class MultiMacroRefAction : public MacroAction { +public: + void ResolveMacroRef(); + std::vector _macros; +}; diff --git a/src/macro-action-random.cpp b/src/macro-action-random.cpp new file mode 100644 index 00000000..235f6a27 --- /dev/null +++ b/src/macro-action-random.cpp @@ -0,0 +1,263 @@ +#include "headers/macro-action-random.hpp" +#include "headers/advanced-scene-switcher.hpp" +#include "headers/name-dialog.hpp" +#include "headers/utility.hpp" + +#include + +const std::string MacroActionRandom::id = "random"; + +bool MacroActionRandom::_registered = MacroActionFactory::Register( + MacroActionRandom::id, + {MacroActionRandom::Create, MacroActionRandomEdit::Create, + "AdvSceneSwitcher.action.random"}); + +std::vector getPossibleMacros(std::vector ¯os, + MacroRef &lastRandomMacro) +{ + std::vector res; + if (macros.size() == 1) { + if (macros[0]->Paused()) { + return res; + } else { + return macros; + } + } + + for (auto &m : macros) { + if (m.get() && !m->Paused() && + !(lastRandomMacro.get() == m.get())) { + res.push_back(m); + } + } + return res; +} + +bool MacroActionRandom::PerformAction() +{ + if (_macros.size() == 0) { + return true; + } + + auto macros = getPossibleMacros(_macros, lastRandomMacro); + if (macros.size() == 0) { + return true; + } + if (macros.size() == 1) { + lastRandomMacro = macros[0]; + return macros[0]->PerformAction(); + } + + size_t idx = std::rand() % (macros.size()); + lastRandomMacro = macros[idx]; + return macros[idx]->PerformAction(); +} + +void MacroActionRandom::LogAction() +{ + vblog(LOG_INFO, "running random macro"); +} + +bool MacroActionRandom::Save(obs_data_t *obj) +{ + MacroAction::Save(obj); + obs_data_array_t *args = obs_data_array_create(); + for (auto &m : _macros) { + obs_data_t *array_obj = obs_data_create(); + m.Save(array_obj); + obs_data_array_push_back(args, array_obj); + obs_data_release(array_obj); + } + obs_data_set_array(obj, "macros", args); + obs_data_array_release(args); + return true; +} + +bool MacroActionRandom::Load(obs_data_t *obj) +{ + MacroAction::Load(obj); + obs_data_array_t *args = obs_data_get_array(obj, "macros"); + size_t count = obs_data_array_count(args); + for (size_t i = 0; i < count; i++) { + obs_data_t *array_obj = obs_data_array_item(args, i); + MacroRef ref; + ref.Load(array_obj); + _macros.push_back(ref); + obs_data_release(array_obj); + } + obs_data_array_release(args); + return true; +} + +MacroActionRandomEdit::MacroActionRandomEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent) +{ + _macroSelection = new MacroSelection(window()); + _macroList = new QListWidget(); + _macroList->setSortingEnabled(true); + _add = new QPushButton(); + _add->setMaximumSize(QSize(22, 22)); + _add->setProperty("themeID", + QVariant(QString::fromUtf8("addIconSmall"))); + _add->setFlat(true); + _remove = new QPushButton(); + _remove->setMaximumSize(QSize(22, 22)); + _remove->setProperty("themeID", + QVariant(QString::fromUtf8("removeIconSmall"))); + _remove->setFlat(true); + + QWidget::connect(_add, SIGNAL(clicked()), this, SLOT(AddMacro())); + QWidget::connect(_remove, SIGNAL(clicked()), this, SLOT(RemoveMacro())); + QWidget::connect(_macroList, SIGNAL(currentRowChanged(int)), this, + SLOT(MacroSelectionChanged(int))); + QWidget::connect(window(), + SIGNAL(MacroRenamed(const QString &, const QString &)), + this, + SLOT(MacroRename(const QString &, const QString &))); + + auto *entryLayout = new QHBoxLayout; + std::unordered_map widgetPlaceholders = { + {"{{macroSelection}}", _macroSelection}, + }; + placeWidgets(obs_module_text("AdvSceneSwitcher.action.random.entry"), + entryLayout, widgetPlaceholders); + + auto *argButtonLayout = new QHBoxLayout; + argButtonLayout->addWidget(_add); + argButtonLayout->addWidget(_remove); + argButtonLayout->addStretch(); + + auto *mainLayout = new QVBoxLayout; + mainLayout->addLayout(entryLayout); + mainLayout->addWidget(new QLabel( + obs_module_text("AdvSceneSwitcher.action.random.arguments"))); + mainLayout->addWidget(_macroList); + mainLayout->addLayout(argButtonLayout); + setLayout(mainLayout); + + _entryData = entryData; + UpdateEntryData(); + _loading = false; +} + +void MacroActionRandomEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + + for (auto &m : _entryData->_macros) { + if (!m.get()) { + continue; + } + auto name = QString::fromStdString(m->Name()); + QListWidgetItem *item = new QListWidgetItem(name, _macroList); + item->setData(Qt::UserRole, name); + } +} + +void MacroActionRandomEdit::MacroRemove(const QString &name) +{ + if (_entryData) { + auto it = _entryData->_macros.begin(); + while (it != _entryData->_macros.end()) { + if (it->get()->Name() == name.toStdString()) { + it = _entryData->_macros.erase(it); + } else { + ++it; + } + } + } +} + +void MacroActionRandomEdit::MacroRename(const QString &oldName, + const QString &newName) +{ + auto count = _macroList->count(); + for (int idx = 0; idx < count; ++idx) { + QListWidgetItem *item = _macroList->item(idx); + QString itemString = item->data(Qt::UserRole).toString(); + if (oldName == itemString) { + item->setData(Qt::UserRole, newName); + item->setText(newName); + break; + } + } +} + +void MacroActionRandomEdit::AddMacro() +{ + if (_loading || !_entryData) { + return; + } + + auto macroName = _macroSelection->currentText(); + MacroRef macro(macroName.toStdString()); + + if (!macro.get()) { + return; + } + + if (FindEntry(macro->Name()) != -1) { + return; + } + + QVariant v = QVariant::fromValue(macroName); + QListWidgetItem *item = new QListWidgetItem(macroName, _macroList); + item->setData(Qt::UserRole, macroName); + + std::lock_guard lock(switcher->m); + _entryData->_macros.push_back(macro); +} + +void MacroActionRandomEdit::RemoveMacro() +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + auto item = _macroList->currentItem(); + if (!item) { + return; + } + std::string name = item->data(Qt::UserRole).toString().toStdString(); + for (auto it = _entryData->_macros.begin(); + it != _entryData->_macros.end(); ++it) { + auto m = *it; + if (m.get() && m->Name() == name) { + _entryData->_macros.erase(it); + break; + } + } + delete item; +} + +int MacroActionRandomEdit::FindEntry(const std::string ¯o) +{ + int count = _macroList->count(); + int idx = -1; + + for (int i = 0; i < count; i++) { + QListWidgetItem *item = _macroList->item(i); + QString itemString = item->data(Qt::UserRole).toString(); + if (QString::fromStdString(macro) == itemString) { + idx = i; + break; + } + } + + return idx; +} + +void MacroActionRandomEdit::MacroSelectionChanged(int idx) +{ + if (_loading || !_entryData || idx == -1) { + return; + } + + QListWidgetItem *item = _macroList->item(idx); + QString name = item->text(); + _macroSelection->SetCurrentMacro(GetMacroByName(name.toUtf8().data())); +} diff --git a/src/macro-tab.cpp b/src/macro-tab.cpp index 20927e34..f69d534f 100644 --- a/src/macro-tab.cpp +++ b/src/macro-tab.cpp @@ -86,6 +86,9 @@ void AdvSceneSwitcher::on_macroRemove_clicked() int idx = ui->macros->currentRow(); QString::fromStdString(switcher->macros[idx]->Name()); switcher->macros.erase(switcher->macros.begin() + idx); + for (auto &m : switcher->macros) { + m->ResolveMacroRef(); + } } delete item; diff --git a/src/macro.cpp b/src/macro.cpp index 9366c503..ae2a9696 100644 --- a/src/macro.cpp +++ b/src/macro.cpp @@ -303,13 +303,18 @@ void Macro::ResolveMacroRef() MacroRefCondition *ref = dynamic_cast(c.get()); if (ref) { - ref->_macro.UpdateRef(); + ref->ResolveMacroRef(); } } for (auto &a : _actions) { MacroRefAction *ref = dynamic_cast(a.get()); if (ref) { - ref->_macro.UpdateRef(); + ref->ResolveMacroRef(); + } + MultiMacroRefAction *ref2 = + dynamic_cast(a.get()); + if (ref2) { + ref2->ResolveMacroRef(); } } } @@ -586,3 +591,10 @@ void MacroRefAction::ResolveMacroRef() { _macro.UpdateRef(); } + +void MultiMacroRefAction::ResolveMacroRef() +{ + for (auto &m : _macros) { + m.UpdateRef(); + } +}