diff --git a/CMakeLists.txt b/CMakeLists.txt index def9a5e6..75b31bfa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -84,6 +84,7 @@ set(advanced-scene-switcher_HEADERS src/headers/macro-action-audio.hpp src/headers/macro-action-filter.hpp src/headers/macro-action-media.hpp + src/headers/macro-action-pause.hpp src/headers/macro-action-recording.hpp src/headers/macro-action-replay-buffer.hpp src/headers/macro-action-run.hpp @@ -145,6 +146,7 @@ set(advanced-scene-switcher_SOURCES src/macro-action-audio.cpp src/macro-action-filter.cpp src/macro-action-media.cpp + src/macro-action-pause.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 4c6a443b..c128270a 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -179,6 +179,10 @@ AdvSceneSwitcher.action.media.type.restart="Restart" AdvSceneSwitcher.action.media.type.next="Next" AdvSceneSwitcher.action.media.type.previous="Previous" AdvSceneSwitcher.action.media.entry="{{actions}} {{mediaSources}}" +AdvSceneSwitcher.action.pause="Pause" +AdvSceneSwitcher.action.pause.type.pause="Pause" +AdvSceneSwitcher.action.pause.type.unpause="Unpause" +AdvSceneSwitcher.action.pause.entry="{{actions}} {{macros}}" ; Transition Tab @@ -435,6 +439,7 @@ AdvSceneSwitcher.selectVideoSource="--select video source--" AdvSceneSwitcher.selectMediaSource="--select media source--" AdvSceneSwitcher.selectProcess="--select process--" AdvSceneSwitcher.selectFilter="--select filter--" +AdvSceneSwitcher.selectMacro="--select macro--" AdvSceneSwitcher.selectItem="--select item--" AdvSceneSwitcher.enterPath="--enter path--" AdvSceneSwitcher.enterText="--enter text--" diff --git a/src/headers/advanced-scene-switcher.hpp b/src/headers/advanced-scene-switcher.hpp index 737cd3b5..7bf44ce6 100644 --- a/src/headers/advanced-scene-switcher.hpp +++ b/src/headers/advanced-scene-switcher.hpp @@ -107,6 +107,9 @@ public: bool listMoveDown(QListWidget *list); signals: + void MacroAdded(const QString &name); + void MacroRemoved(const QString &name); + void MacroRenamed(const QString &oldName, const QString newName); void SceneGroupAdded(const QString &name); void SceneGroupRemoved(const QString &name); void SceneGroupRenamed(const QString &oldName, const QString newName); @@ -137,6 +140,7 @@ public slots: void on_macroDown_clicked(); void on_macroName_editingFinished(); void on_macros_currentRowChanged(int idx); + void on_macros_itemChanged(QListWidgetItem *); void on_conditionAdd_clicked(); void on_conditionRemove_clicked(); void on_actionAdd_clicked(); diff --git a/src/headers/macro-action-pause.hpp b/src/headers/macro-action-pause.hpp new file mode 100644 index 00000000..f2f60e77 --- /dev/null +++ b/src/headers/macro-action-pause.hpp @@ -0,0 +1,61 @@ +#pragma once +#include +#include "macro-action-edit.hpp" + +enum class PauseAction { + PAUSE, + UNPAUSE, +}; + +class MacroActionPause : public MacroAction { +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(); + } + + PauseAction _action = PauseAction::PAUSE; + Macro *_macro = nullptr; + +private: + static bool _registered; + static const std::string id; +}; + +class MacroActionPauseEdit : public QWidget { + Q_OBJECT + +public: + MacroActionPauseEdit( + QWidget *parent, + std::shared_ptr entryData = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr action) + { + return new MacroActionPauseEdit( + parent, + std::dynamic_pointer_cast(action)); + } + +private slots: + void MacroChanged(const QString &text); + void ActionChanged(int value); + void MacroAdd(const QString &name); + void MacroRemove(const QString &name); + void MacroRename(const QString &oldName, const QString &newName); + +protected: + QComboBox *_macros; + QComboBox *_actions; + std::shared_ptr _entryData; + +private: + QHBoxLayout *_mainLayout; + bool _loading = true; +}; diff --git a/src/headers/macro.hpp b/src/headers/macro.hpp index 7c0271a2..8c2719f8 100644 --- a/src/headers/macro.hpp +++ b/src/headers/macro.hpp @@ -5,6 +5,7 @@ #include #include #include +#include constexpr auto macro_func = 10; constexpr auto default_priority_10 = macro_func; @@ -68,6 +69,8 @@ public: bool Matched() { return _matched; } std::string Name() { return _name; } void SetName(std::string name) { _name = name; } + void SetPaused(bool pause = true) { _paused = pause; } + bool Paused() { return _paused; } std::deque> &Conditions() { return _conditions; @@ -85,4 +88,8 @@ private: std::deque> _conditions; std::deque> _actions; bool _matched = false; + bool _paused = false; }; + +Macro *GetMacroByName(const char *name); +Macro *GetMacroByQString(const QString &name); diff --git a/src/macro-action-pause.cpp b/src/macro-action-pause.cpp new file mode 100644 index 00000000..d69d71ef --- /dev/null +++ b/src/macro-action-pause.cpp @@ -0,0 +1,189 @@ +#include "headers/macro-action-pause.hpp" +#include "headers/advanced-scene-switcher.hpp" +#include "headers/utility.hpp" + +const std::string MacroActionPause::id = "pause"; + +bool MacroActionPause::_registered = MacroActionFactory::Register( + MacroActionPause::id, + {MacroActionPause::Create, MacroActionPauseEdit::Create, + "AdvSceneSwitcher.action.pause"}); + +const static std::map actionTypes = { + {PauseAction::PAUSE, "AdvSceneSwitcher.action.pause.type.pause"}, + {PauseAction::UNPAUSE, "AdvSceneSwitcher.action.pause.type.unpause"}, +}; + +bool MacroActionPause::PerformAction() +{ + if (!_macro) { + return true; + } + + switch (_action) { + case PauseAction::PAUSE: + _macro->SetPaused(); + break; + case PauseAction::UNPAUSE: + _macro->SetPaused(false); + break; + default: + break; + } + return true; +} + +void MacroActionPause::LogAction() +{ + if (!_macro) { + return; + } + switch (_action) { + case PauseAction::PAUSE: + vblog(LOG_INFO, "paused \"%s\"", _macro->Name().c_str()); + break; + case PauseAction::UNPAUSE: + vblog(LOG_INFO, "unpaused \"%s\"", _macro->Name().c_str()); + break; + default: + break; + } +} + +bool MacroActionPause::Save(obs_data_t *obj) +{ + MacroAction::Save(obj); + if (_macro) { + obs_data_set_string(obj, "macro", _macro->Name().c_str()); + } + obs_data_set_int(obj, "action", static_cast(_action)); + return true; +} + +bool MacroActionPause::Load(obs_data_t *obj) +{ + MacroAction::Load(obj); + _macro = GetMacroByName(obs_data_get_string(obj, "macro")); + _action = static_cast(obs_data_get_int(obj, "action")); + return true; +} + +static inline void populateActionSelection(QComboBox *list) +{ + for (auto entry : actionTypes) { + list->addItem(obs_module_text(entry.second.c_str())); + } +} + +static inline void populateMacroSelection(QComboBox *list) +{ + list->addItem(obs_module_text("AdvSceneSwitcher.selectMacro")); + for (auto &m : switcher->macros) { + list->addItem(QString::fromStdString(m.Name())); + } +} + +MacroActionPauseEdit::MacroActionPauseEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent) +{ + _macros = new QComboBox(); + _actions = new QComboBox(); + + populateActionSelection(_actions); + populateMacroSelection(_macros); + + QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)), + this, SLOT(MacroChanged(const QString &))); + QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this, + SLOT(ActionChanged(int))); + QWidget::connect(parent, SIGNAL(MacroAdded(const QString &)), this, + SLOT(MacroAdd(const QString &))); + QWidget::connect(parent, SIGNAL(MacroRemoved(const QString &)), this, + SLOT(MacroRemove(const QString &))); + QWidget::connect(parent, + SIGNAL(MacroRenamed(const QString &, const QString &)), + this, + SLOT(MacroRename(const QString &, const QString &))); + + QHBoxLayout *mainLayout = new QHBoxLayout; + std::unordered_map widgetPlaceholders = { + {"{{actions}}", _actions}, + {"{{macros}}", _macros}, + }; + placeWidgets(obs_module_text("AdvSceneSwitcher.action.pause.entry"), + mainLayout, widgetPlaceholders); + setLayout(mainLayout); + + _entryData = entryData; + UpdateEntryData(); + _loading = false; +} + +void MacroActionPauseEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + _actions->setCurrentIndex(static_cast(_entryData->_action)); + if (_entryData->_macro) { + _macros->setCurrentText( + QString::fromStdString(_entryData->_macro->Name())); + } else { + _macros->setCurrentIndex(0); + } +} + +void MacroActionPauseEdit::MacroChanged(const QString &text) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_macro = GetMacroByQString(text); +} + +void MacroActionPauseEdit::ActionChanged(int value) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_action = static_cast(value); +} + +void MacroActionPauseEdit::MacroAdd(const QString &name) +{ + _macros->addItem(name); +} + +void MacroActionPauseEdit::MacroRemove(const QString &name) +{ + int idx = _macros->findText(name); + if (idx == -1) { + return; + } + _macros->removeItem(idx); + if (_entryData && _entryData->_macro == GetMacroByQString(name)) { + std::lock_guard lock(switcher->m); + _entryData->_macro = nullptr; + } + _macros->setCurrentIndex(0); +} + +void MacroActionPauseEdit::MacroRename(const QString &oldName, + const QString &newName) +{ + bool renameSelected = _macros->currentText() == oldName; + int idx = _macros->findText(oldName); + if (idx == -1) { + return; + } + _macros->removeItem(idx); + _macros->insertItem(idx, newName); + if (renameSelected) { + _macros->setCurrentIndex(_macros->findText(newName)); + } +} diff --git a/src/macro-tab.cpp b/src/macro-tab.cpp index 67feda0f..4dc2ce58 100644 --- a/src/macro-tab.cpp +++ b/src/macro-tab.cpp @@ -13,13 +13,7 @@ const auto actionsCollapseThreshold = 2; bool macroNameExists(std::string name) { - for (auto &m : switcher->macros) { - if (m.Name() == name) { - return true; - } - } - - return false; + return !!GetMacroByName(name.c_str()); } bool AdvSceneSwitcher::addNewMacro(std::string &name) @@ -69,10 +63,14 @@ void AdvSceneSwitcher::on_macroAdd_clicked() QListWidgetItem *item = new QListWidgetItem(text, ui->macros); item->setData(Qt::UserRole, text); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Checked); ui->macros->setCurrentItem(item); ui->macroAdd->disconnect(addPulse); ui->macroHelp->setVisible(false); + + emit MacroAdded(QString::fromStdString(name)); } void AdvSceneSwitcher::on_macroRemove_clicked() @@ -81,9 +79,11 @@ void AdvSceneSwitcher::on_macroRemove_clicked() if (!item) { return; } + QString name; { std::lock_guard lock(switcher->m); int idx = ui->macros->currentRow(); + QString::fromStdString(switcher->macros[idx].Name()); switcher->macros.erase(switcher->macros.begin() + idx); } @@ -92,6 +92,7 @@ void AdvSceneSwitcher::on_macroRemove_clicked() if (ui->macros->count() == 0) { ui->macroHelp->setVisible(true); } + emit MacroRemoved(name); } void AdvSceneSwitcher::on_macroUp_clicked() @@ -147,12 +148,17 @@ void AdvSceneSwitcher::on_macroName_editingFinished() if (nameValid) { macro->SetName(newName.toUtf8().constData()); QListWidgetItem *item = ui->macros->currentItem(); - item->setData(Qt::UserRole, newName); + // Don't trigger itemChanged() + // pause state remains as is + ui->macros->blockSignals(true); item->setText(newName); + ui->macros->blockSignals(false); } else { ui->macroName->setText(oldName); } } + + emit MacroRenamed(oldName, newName); } void AdvSceneSwitcher::SetEditMacro(Macro &m) @@ -196,22 +202,14 @@ void AdvSceneSwitcher::SetEditMacro(Macro &m) Macro *AdvSceneSwitcher::getSelectedMacro() { - Macro *macro = nullptr; QListWidgetItem *item = ui->macros->currentItem(); if (!item) { - return macro; + return nullptr; } - QString name = item->data(Qt::UserRole).toString(); - for (auto &m : switcher->macros) { - if (name.compare(m.Name().c_str()) == 0) { - macro = &m; - break; - } - } - - return macro; + QString name = item->text(); + return GetMacroByQString(name); } void AdvSceneSwitcher::on_macros_currentRowChanged(int idx) @@ -226,13 +224,26 @@ void AdvSceneSwitcher::on_macros_currentRowChanged(int idx) } QListWidgetItem *item = ui->macros->item(idx); - QString macroName = item->data(Qt::UserRole).toString(); + QString macroName = item->text(); - for (auto &m : switcher->macros) { - if (macroName.compare(m.Name().c_str()) == 0) { - SetEditMacro(m); - break; - } + auto macro = GetMacroByQString(macroName); + if (macro) { + SetEditMacro(*macro); + } +} + +void AdvSceneSwitcher::on_macros_itemChanged(QListWidgetItem *item) +{ + if (loading) { + return; + } + + std::lock_guard lock(switcher->m); + QString name = item->text(); + + auto m = GetMacroByQString(name); + if (m) { + m->SetPaused(item->checkState() != Qt::Checked); } } @@ -243,6 +254,12 @@ void AdvSceneSwitcher::setupMacroTab() QListWidgetItem *item = new QListWidgetItem(text, ui->macros); item->setData(Qt::UserRole, text); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + if (m.Paused()) { + item->setCheckState(Qt::Unchecked); + } else { + item->setCheckState(Qt::Checked); + } } if (switcher->macros.size() == 0) { @@ -286,5 +303,7 @@ void AdvSceneSwitcher::copyMacro() QString text = QString::fromStdString(name); QListWidgetItem *item = new QListWidgetItem(text, ui->macros); item->setData(Qt::UserRole, text); + item->setFlags(item->flags() | Qt::ItemIsUserCheckable); + item->setCheckState(Qt::Checked); ui->macros->setCurrentItem(item); } diff --git a/src/macro.cpp b/src/macro.cpp index a98adbb8..f8a7d106 100644 --- a/src/macro.cpp +++ b/src/macro.cpp @@ -20,6 +20,10 @@ Macro::~Macro() {} bool Macro::CeckMatch() { + if (_paused) { + vblog(LOG_INFO, "Macro %s is paused", _name.c_str()); + return false; + } _matched = false; for (auto &c : _conditions) { bool cond = c->CheckCondition(); @@ -80,6 +84,7 @@ bool Macro::PerformAction() bool Macro::Save(obs_data_t *obj) { obs_data_set_string(obj, "name", _name.c_str()); + obs_data_set_bool(obj, "pause", _paused); obs_data_array_t *conditions = obs_data_array_create(); for (auto &c : _conditions) { @@ -145,6 +150,7 @@ void setValidLogic(MacroCondition *c, bool root, std::string name) bool Macro::Load(obs_data_t *obj) { _name = obs_data_get_string(obj, "name"); + _paused = obs_data_get_bool(obj, "pause"); bool root = true; obs_data_array_t *conditions = obs_data_get_array(obj, "conditions"); @@ -363,3 +369,19 @@ bool SwitcherData::runMacros() } return true; } + +Macro *GetMacroByName(const char *name) +{ + for (auto &m : switcher->macros) { + if (m.Name() == name) { + return &m; + } + } + + return nullptr; +} + +Macro *GetMacroByQString(const QString &name) +{ + return GetMacroByName(name.toUtf8().constData()); +}