Add action queues

This is intended to be used to execute long running actions which shall
not be run in parallel to one another.

Multiple different action queues can be created.

The "Queue" action and condition can be used to modify and check the
status and size of action queues.
This commit is contained in:
WarmUpTill 2023-12-29 18:56:38 +01:00 committed by WarmUpTill
parent e37e35e318
commit cde4e24cc4
9 changed files with 1056 additions and 1 deletions

View File

@ -87,7 +87,9 @@ target_sources(
# Maro sources
target_sources(
${LIB_NAME}
PRIVATE src/macro-core/macro-action-audio.cpp
PRIVATE src/macro-core/action-queue.cpp
src/macro-core/action-queue.hpp
src/macro-core/macro-action-audio.cpp
src/macro-core/macro-action-audio.hpp
src/macro-core/macro-action-clipboard.cpp
src/macro-core/macro-action-clipboard.hpp
@ -115,6 +117,8 @@ target_sources(
src/macro-core/macro-action-profile.hpp
src/macro-core/macro-action-projector.cpp
src/macro-core/macro-action-projector.hpp
src/macro-core/macro-action-queue.cpp
src/macro-core/macro-action-queue.hpp
src/macro-core/macro-action-random.cpp
src/macro-core/macro-action-random.hpp
src/macro-core/macro-action-recording.cpp
@ -193,6 +197,8 @@ target_sources(
src/macro-core/macro-condition-process.hpp
src/macro-core/macro-condition-profile.cpp
src/macro-core/macro-condition-profile.hpp
src/macro-core/macro-condition-queue.cpp
src/macro-core/macro-condition-queue.hpp
src/macro-core/macro-condition-recording.cpp
src/macro-core/macro-condition-recording.hpp
src/macro-core/macro-condition-replay-buffer.cpp

View File

@ -557,6 +557,12 @@ AdvSceneSwitcher.condition.twitch.entry="Channel{{channel}}{{conditions}}{{point
AdvSceneSwitcher.condition.twitch.entry.account="Check using account{{account}}"
AdvSceneSwitcher.condition.twitch.tokenPermissionsInsufficient="Permissions of selected token are insufficient to perform selected action!"
AdvSceneSwitcher.condition.twitch.title.title="Enter title"
AdvSceneSwitcher.condition.queue="Queue"
AdvSceneSwitcher.condition.queue.type.started="Started"
AdvSceneSwitcher.condition.queue.type.stopped="Stopped"
AdvSceneSwitcher.condition.queue.type.size="Size"
AdvSceneSwitcher.condition.queue.entry.startStop="Queue{{queues}}is{{conditions}}"
AdvSceneSwitcher.condition.queue.entry.size="Queue{{queues}}{{conditions}}is less than{{size}}"
; Macro Actions
AdvSceneSwitcher.action.scene="Switch scene"
@ -897,6 +903,13 @@ AdvSceneSwitcher.action.clipboard.type.copy.image="Copy image"
AdvSceneSwitcher.action.clipboard.copy.text.text.placeholder="Enter text"
AdvSceneSwitcher.action.clipboard.copy.image.url.placeholder="Enter direct image URL"
AdvSceneSwitcher.action.clipboard.copy.image.url.tooltip="Currently supported formats: PNG, JPG/JPEG, BMP, GIF."
AdvSceneSwitcher.action.queue="Queue"
AdvSceneSwitcher.action.queue.type.add="Add"
AdvSceneSwitcher.action.queue.type.clear="Clear"
AdvSceneSwitcher.action.queue.type.start="Start"
AdvSceneSwitcher.action.queue.type.stop="Stop"
AdvSceneSwitcher.action.queue.entry.add="{{actions}}actions of macro{{macros}}to queue{{queues}}"
AdvSceneSwitcher.action.queue.entry.other="{{actions}}queue{{queues}}"
; Hotkey
AdvSceneSwitcher.hotkey.startSwitcherHotkey="Start the Advanced Scene Switcher"
@ -962,6 +975,20 @@ AdvSceneSwitcher.connection.status.connecting="Connecting"
AdvSceneSwitcher.connection.status.connected="Connected, but not authenticated"
AdvSceneSwitcher.connection.status.authenticated="Connected and authenticated"
AdvSceneSwitcher.actionQueues.select="--select queue--"
AdvSceneSwitcher.actionQueues.add="Add new action queue"
AdvSceneSwitcher.actionQueues.nameNotAvailable="Action queue name already in use"
AdvSceneSwitcher.actionQueues.configure="Configure action queue settings"
AdvSceneSwitcher.actionQueues.invalid="Invalid action queue selection"
AdvSceneSwitcher.actionQueues.name="Name:"
AdvSceneSwitcher.actionQueues.runOnStartup="Run action queue when starting the plugin"
AdvSceneSwitcher.actionQueues.running="Queue is running"
AdvSceneSwitcher.actionQueues.stopped="Queue is stopped"
AdvSceneSwitcher.actionQueues.start="Start action queue"
AdvSceneSwitcher.actionQueues.stop="Stop action queue"
AdvSceneSwitcher.actionQueues.size="Current size: %1"
AdvSceneSwitcher.actionQueues.clear="Clear action queue"
AdvSceneSwitcher.regex.enable="Enable regular expressions"
AdvSceneSwitcher.regex.configure="Configure regular expression settings"
AdvSceneSwitcher.regex.partialMatch="Allow partial match"

View File

@ -434,11 +434,14 @@ static void ResetMacros()
}
}
void AutoStartActionQueues();
void SwitcherData::Start()
{
if (!(th && th->isRunning())) {
ResetForNextInterval();
ResetMacros();
AutoStartActionQueues();
stop = false;
th = new SwitcherThread();
@ -465,6 +468,7 @@ void SwitcherData::Start()
}
bool CloseAllInputDialogs();
void StopAndClearAllActionQueues();
void SwitcherData::Stop()
{
@ -474,6 +478,7 @@ void SwitcherData::Stop()
SetMacroAbortWait(true);
GetMacroWaitCV().notify_all();
GetMacroTransitionCV().notify_all();
StopAndClearAllActionQueues();
// Not waiting if a dialog was closed is a workaround to avoid
// deadlocks when a variable input dialog is opened while Stop()
@ -750,6 +755,7 @@ QWidget *GetSettingsWindow()
void SetupConnectionManager();
void SetupWebsocketHelpers();
void SetupActionQueues();
extern "C" void InitSceneSwitcher(obs_module_t *module, translateFunc translate)
{
@ -763,6 +769,7 @@ extern "C" void InitSceneSwitcher(obs_module_t *module, translateFunc translate)
SetupDock();
SetupConnectionManager();
SetupWebsocketHelpers();
SetupActionQueues();
obs_frontend_add_save_callback(SaveSceneSwitcher, nullptr);
obs_frontend_add_event_callback(OBSEvent, switcher);

View File

@ -0,0 +1,380 @@
#include "action-queue.hpp"
#include "plugin-state-helpers.hpp"
namespace advss {
static std::deque<std::shared_ptr<Item>> queues;
void SetupActionQueues()
{
static bool done = false;
if (done) {
return;
}
AddSaveStep(SaveActionQueues);
AddLoadStep(LoadActionQueues);
done = true;
}
ActionQueue::ActionQueue() : Item() {}
ActionQueue::~ActionQueue()
{
Stop();
}
std::shared_ptr<Item> ActionQueue::Create()
{
return std::make_shared<ActionQueue>();
}
void ActionQueue::Save(obs_data_t *obj) const
{
obs_data_set_string(obj, "name", _name.c_str());
obs_data_set_bool(obj, "runOnStartup", _runOnStartup);
}
void ActionQueue::Load(obs_data_t *obj)
{
std::lock_guard<std::mutex> lock(_mutex);
_name = obs_data_get_string(obj, "name");
_runOnStartup = obs_data_get_bool(obj, "runOnStartup");
if (_runOnStartup) {
Start();
}
}
void ActionQueue::Start()
{
if (_thread.joinable()) {
return;
}
_stop = false;
_thread = std::thread(&ActionQueue::RunActions, this);
}
void ActionQueue::Stop()
{
_stop = true;
_cv.notify_all();
if (_thread.joinable()) {
_thread.join();
}
}
bool ActionQueue::IsRunning() const
{
return _thread.joinable();
}
bool ActionQueue::RunsOnStartup() const
{
return _runOnStartup;
}
void ActionQueue::Clear()
{
std::lock_guard<std::mutex> lock(_mutex);
_actions.clear();
}
void ActionQueue::Add(const std::shared_ptr<MacroAction> &actions)
{
std::lock_guard<std::mutex> lock(_mutex);
_actions.emplace_back(actions);
_cv.notify_all();
}
bool ActionQueue::IsEmpty()
{
std::lock_guard<std::mutex> lock(_mutex);
return _actions.empty();
}
size_t ActionQueue::Size()
{
std::lock_guard<std::mutex> lock(_mutex);
return _actions.size();
}
void ActionQueue::RunActions()
{
std::shared_ptr<MacroAction> action;
while (true) {
{ // Grab next action to run
std::unique_lock<std::mutex> lock(_mutex);
while (_actions.empty() && !_stop) {
_cv.wait(lock);
}
if (_stop) {
return;
}
action = _actions.front();
_actions.pop_front();
}
if (!action) {
continue;
}
vblog(LOG_INFO, "Performing action '%s' in queue '%s'",
action->GetId().c_str(), _name.c_str());
action->PerformAction();
}
}
ActionQueueSettingsDialog::ActionQueueSettingsDialog(QWidget *parent,
ActionQueue &settings)
: ItemSettingsDialog(settings, queues,
"AdvSceneSwitcher.actionQueues.select",
"AdvSceneSwitcher.actionQueues.add",
"AdvSceneSwitcher.actionQueues.nameNotAvailable",
parent),
_queueRunStatus(new QLabel()),
_startStopToggle(new QPushButton()),
_queueSize(new QLabel()),
_clear(new QPushButton(
obs_module_text("AdvSceneSwitcher.actionQueues.clear"))),
_runOnStartup(new QCheckBox()),
_queue(settings)
{
QWidget::connect(_startStopToggle, SIGNAL(clicked()), this,
SLOT(StartStopClicked()));
QWidget::connect(_clear, SIGNAL(clicked()), this, SLOT(ClearClicked()));
_runOnStartup->setChecked(settings._runOnStartup);
UpdateLabels();
auto layout = new QGridLayout();
int row = 0;
layout->addWidget(new QLabel(obs_module_text(
"AdvSceneSwitcher.actionQueues.name")),
row, 0);
auto nameLayout = new QHBoxLayout();
nameLayout->setContentsMargins(0, 0, 0, 0);
nameLayout->addWidget(_name);
nameLayout->addWidget(_nameHint);
layout->addLayout(nameLayout, row, 1);
++row;
layout->addWidget(
new QLabel(obs_module_text(
"AdvSceneSwitcher.actionQueues.runOnStartup")),
row, 0);
layout->addWidget(_runOnStartup, row, 1);
_runOnStartup->setToolTip(
obs_module_text("AdvSceneSwitcher.actionQueues.runOnStartup"));
++row;
layout->addWidget(_queueRunStatus, row, 0);
layout->addWidget(_startStopToggle, row, 1);
++row;
layout->addWidget(_queueSize, row, 0);
layout->addWidget(_clear, row, 1);
++row;
layout->addWidget(_buttonbox, row, 0, 1, -1);
layout->setSizeConstraint(QLayout::SetFixedSize);
setLayout(layout);
auto timer = new QTimer(this);
QWidget::connect(timer, &QTimer::timeout, this,
&ActionQueueSettingsDialog::UpdateLabels);
timer->start(300);
}
bool ActionQueueSettingsDialog::AskForSettings(QWidget *parent,
ActionQueue &settings)
{
ActionQueueSettingsDialog dialog(parent, settings);
dialog.setWindowTitle(obs_module_text("AdvSceneSwitcher.windowTitle"));
if (dialog.exec() != DialogCode::Accepted) {
return false;
}
settings._name = dialog._name->text().toStdString();
settings._runOnStartup = dialog._runOnStartup->isChecked();
return true;
}
void ActionQueueSettingsDialog::StartStopClicked()
{
if (_queue.IsRunning()) {
_queue.Stop();
} else {
_queue.Start();
}
UpdateLabels();
}
void ActionQueueSettingsDialog::ClearClicked()
{
_queue.Clear();
}
void ActionQueueSettingsDialog::UpdateLabels()
{
_queueRunStatus->setText(obs_module_text(
_queue.IsRunning() ? "AdvSceneSwitcher.actionQueues.running"
: "AdvSceneSwitcher.actionQueues.stopped"));
_startStopToggle->setText(
_queue.IsRunning()
? obs_module_text("AdvSceneSwitcher.actionQueues.stop")
: obs_module_text(
"AdvSceneSwitcher.actionQueues.start"));
_queueSize->setText(
QString(obs_module_text("AdvSceneSwitcher.actionQueues.size"))
.arg(QString::number(_queue.Size())));
}
static bool AskForSettingsWrapper(QWidget *parent, Item &settings)
{
ActionQueue &queue = dynamic_cast<ActionQueue &>(settings);
if (ActionQueueSettingsDialog::AskForSettings(parent, queue)) {
return true;
}
return false;
}
void ActionQueueSelection::SetActionQueue(
const std::weak_ptr<ActionQueue> &queue_)
{
const QSignalBlocker blocker(_selection);
auto queue = queue_.lock();
if (queue) {
_selection->setCurrentText(
QString::fromStdString(queue->Name()));
} else {
_selection->setCurrentIndex(-1);
}
}
ActionQueueSelection::ActionQueueSelection(QWidget *parent)
: ItemSelection(queues, ActionQueue::Create, AskForSettingsWrapper,
"AdvSceneSwitcher.actionQueues.select",
"AdvSceneSwitcher.actionQueues.add",
"AdvSceneSwitcher.item.nameNotAvailable",
"AdvSceneSwitcher.actionQueues.configure", parent)
{
// Connect to slots
QWidget::connect(ActionQueueSignalManager::Instance(),
SIGNAL(Rename(const QString &, const QString &)), this,
SLOT(RenameItem(const QString &, const QString &)));
QWidget::connect(ActionQueueSignalManager::Instance(),
SIGNAL(Add(const QString &)), this,
SLOT(AddItem(const QString &)));
QWidget::connect(ActionQueueSignalManager::Instance(),
SIGNAL(Remove(const QString &)), this,
SLOT(RemoveItem(const QString &)));
// Forward signals
QWidget::connect(this,
SIGNAL(ItemRenamed(const QString &, const QString &)),
ActionQueueSignalManager::Instance(),
SIGNAL(Rename(const QString &, const QString &)));
QWidget::connect(this, SIGNAL(ItemAdded(const QString &)),
ActionQueueSignalManager::Instance(),
SIGNAL(Add(const QString &)));
QWidget::connect(this, SIGNAL(ItemRemoved(const QString &)),
ActionQueueSignalManager::Instance(),
SIGNAL(Remove(const QString &)));
}
ActionQueueSignalManager::ActionQueueSignalManager(QObject *parent)
: QObject(parent)
{
QWidget::connect(this, SIGNAL(Add(const QString &)), this,
SLOT(StartNewQueue(const QString &)));
}
ActionQueueSignalManager *ActionQueueSignalManager::Instance()
{
static ActionQueueSignalManager manager;
return &manager;
}
void ActionQueueSignalManager::StartNewQueue(const QString &name)
{
auto weakQueue = GetWeakActionQueueByQString(name);
auto queue = weakQueue.lock();
if (!queue) {
return;
}
if (queue->RunsOnStartup()) {
queue->Start();
}
}
void SaveActionQueues(obs_data_t *obj)
{
OBSDataArrayAutoRelease array = obs_data_array_create();
for (const auto &queue : queues) {
OBSDataAutoRelease obj = obs_data_create();
queue->Save(obj);
obs_data_array_push_back(array, obj);
}
obs_data_set_array(obj, "actionQueues", array);
}
void LoadActionQueues(obs_data_t *obj)
{
queues.clear();
OBSDataArrayAutoRelease array = obs_data_get_array(obj, "actionQueues");
size_t count = obs_data_array_count(array);
for (size_t i = 0; i < count; i++) {
OBSDataAutoRelease obj = obs_data_array_item(array, i);
auto queue = ActionQueue::Create();
queues.emplace_back(queue);
queues.back()->Load(obj);
}
}
std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name)
{
for (const auto &queue : queues) {
if (queue->Name() == name) {
std::weak_ptr<ActionQueue> wp =
std::dynamic_pointer_cast<ActionQueue>(queue);
return wp;
}
}
return std::weak_ptr<ActionQueue>();
}
std::weak_ptr<ActionQueue> GetWeakActionQueueByQString(const QString &name)
{
return GetWeakActionQueueByName(name.toStdString());
}
std::string GetActionQueueName(const std::weak_ptr<ActionQueue> &queue_)
{
auto queue = queue_.lock();
if (!queue) {
return obs_module_text("AdvSceneSwitcher.actionQueues.invalid");
}
return queue->Name();
}
void AutoStartActionQueues()
{
for (auto &queue : queues) {
auto actionQueue =
std::dynamic_pointer_cast<ActionQueue>(queue);
if (actionQueue->RunsOnStartup()) {
actionQueue->Start();
}
}
}
void StopAndClearAllActionQueues()
{
for (auto &queue : queues) {
auto actionQueue =
std::dynamic_pointer_cast<ActionQueue>(queue);
actionQueue->Stop();
actionQueue->Clear();
}
}
} // namespace advss

View File

@ -0,0 +1,105 @@
#pragma once
#include "item-selection-helpers.hpp"
#include "macro-action.hpp"
#include <deque>
#include <obs-data.h>
#include <QCheckBox>
#include <thread>
#include <condition_variable>
namespace advss {
class ActionQueueSelection;
class ActionQueueSettingsDialog;
class ActionQueue : public Item {
public:
ActionQueue();
~ActionQueue();
static std::shared_ptr<Item> Create();
void Save(obs_data_t *obj) const;
void Load(obs_data_t *obj);
void Start();
void Stop();
bool IsRunning() const;
bool RunsOnStartup() const;
void Clear();
bool IsEmpty();
void Add(const std::shared_ptr<MacroAction> &);
size_t Size();
private:
void RunActions();
bool _runOnStartup = true;
std::atomic_bool _stop = {false};
std::mutex _mutex;
std::condition_variable _cv;
std::thread _thread;
std::deque<std::shared_ptr<MacroAction>> _actions;
friend ActionQueueSelection;
friend ActionQueueSettingsDialog;
};
class ActionQueueSettingsDialog : public ItemSettingsDialog {
Q_OBJECT
public:
ActionQueueSettingsDialog(QWidget *parent, ActionQueue &);
static bool AskForSettings(QWidget *parent, ActionQueue &settings);
private slots:
void StartStopClicked();
void ClearClicked();
void UpdateLabels();
private:
QLabel *_queueRunStatus;
QPushButton *_startStopToggle;
QLabel *_queueSize;
QPushButton *_clear;
QCheckBox *_runOnStartup;
ActionQueue &_queue;
};
class ActionQueueSelection : public ItemSelection {
Q_OBJECT
public:
ActionQueueSelection(QWidget *parent = 0);
void SetActionQueue(const std::weak_ptr<ActionQueue> &);
};
class ActionQueueSignalManager : public QObject {
Q_OBJECT
public:
ActionQueueSignalManager(QObject *parent = nullptr);
static ActionQueueSignalManager *Instance();
private slots:
void StartNewQueue(const QString &);
signals:
void Rename(const QString &, const QString &);
void Add(const QString &);
void Remove(const QString &);
};
void SetupActionQueues();
void SaveActionQueues(obs_data_t *);
void LoadActionQueues(obs_data_t *);
std::weak_ptr<ActionQueue> GetWeakActionQueueByName(const std::string &name);
std::weak_ptr<ActionQueue> GetWeakActionQueueByQString(const QString &name);
std::string GetActionQueueName(const std::weak_ptr<ActionQueue> &);
void AutoStartActionQueues();
void StopAndClearAllActionQueues();
} // namespace advss

View File

@ -0,0 +1,218 @@
#include "macro-action-queue.hpp"
#include "macro-helpers.hpp"
#include "utility.hpp"
namespace advss {
const std::string MacroActionQueue::id = "queue";
bool MacroActionQueue::_registered = MacroActionFactory::Register(
MacroActionQueue::id,
{MacroActionQueue::Create, MacroActionQueueEdit::Create,
"AdvSceneSwitcher.action.queue"});
const static std::map<MacroActionQueue::Action, std::string> actionTypes = {
{MacroActionQueue::Action::ADD_TO_QUEUE,
"AdvSceneSwitcher.action.queue.type.add"},
{MacroActionQueue::Action::START_QUEUE,
"AdvSceneSwitcher.action.queue.type.start"},
{MacroActionQueue::Action::STOP_QUEUE,
"AdvSceneSwitcher.action.queue.type.stop"},
{MacroActionQueue::Action::CLEAR_QUEUE,
"AdvSceneSwitcher.action.queue.type.clear"},
};
void MacroActionQueue::AddActions(ActionQueue *queue)
{
auto macro = _macro.GetMacro();
if (!macro) {
return;
}
auto actions = *GetMacroActions(macro.get());
for (const auto &action : actions) {
queue->Add(action);
}
}
bool MacroActionQueue::PerformAction()
{
auto queue = _queue.lock();
if (!queue) {
return true;
}
switch (_action) {
case Action::ADD_TO_QUEUE:
AddActions(queue.get());
break;
case Action::CLEAR_QUEUE:
queue->Clear();
break;
case Action::START_QUEUE:
queue->Start();
break;
case Action::STOP_QUEUE:
queue->Stop();
break;
default:
break;
}
return true;
}
void MacroActionQueue::LogAction() const
{
auto macro = _macro.GetMacro();
if (!macro) {
return;
}
switch (_action) {
case Action::ADD_TO_QUEUE:
vblog(LOG_INFO, "queued actions of \"%s\" to \"%s\"",
GetMacroName(macro.get()).c_str(),
GetActionQueueName(_queue).c_str());
break;
case Action::START_QUEUE:
vblog(LOG_INFO, "start queue \"%s\"",
GetActionQueueName(_queue).c_str());
break;
case Action::STOP_QUEUE:
vblog(LOG_INFO, "stop queue \"%s\"",
GetActionQueueName(_queue).c_str());
break;
case Action::CLEAR_QUEUE:
vblog(LOG_INFO, "cleared queue \"%s\"",
GetActionQueueName(_queue).c_str());
break;
default:
break;
}
}
bool MacroActionQueue::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
_macro.Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
obs_data_set_string(obj, "queue", GetActionQueueName(_queue).c_str());
return true;
}
bool MacroActionQueue::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
_macro.Load(obj);
_action = static_cast<MacroActionQueue::Action>(
obs_data_get_int(obj, "action"));
_queue = GetWeakActionQueueByName(obs_data_get_string(obj, "queue"));
return true;
}
std::string MacroActionQueue::GetShortDesc() const
{
return GetActionQueueName(_queue);
}
static inline void populateActionSelection(QComboBox *list)
{
for (const auto &[_, name] : actionTypes) {
list->addItem(obs_module_text(name.c_str()));
}
}
MacroActionQueueEdit::MacroActionQueueEdit(
QWidget *parent, std::shared_ptr<MacroActionQueue> entryData)
: QWidget(parent),
_macros(new MacroSelection(parent)),
_queues(new ActionQueueSelection()),
_actions(new QComboBox()),
_layout(new QHBoxLayout())
{
populateActionSelection(_actions);
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(MacroChanged(const QString &)));
QWidget::connect(_queues, SIGNAL(SelectionChanged(const QString &)),
this, SLOT(QueueChanged(const QString &)));
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
setLayout(_layout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroActionQueueEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_macros->SetCurrentMacro(_entryData->_macro);
_queues->SetActionQueue(_entryData->_queue);
SetWidgetVisibility();
}
void MacroActionQueueEdit::MacroChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_macro = text;
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionQueueEdit::QueueChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_queue = GetWeakActionQueueByQString(text);
}
void MacroActionQueueEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_action = static_cast<MacroActionQueue::Action>(value);
SetWidgetVisibility();
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroActionQueueEdit::SetWidgetVisibility()
{
_layout->removeWidget(_actions);
_layout->removeWidget(_queues);
_layout->removeWidget(_macros);
ClearLayout(_layout);
PlaceWidgets(
obs_module_text(
_entryData->_action ==
MacroActionQueue::Action::ADD_TO_QUEUE
? "AdvSceneSwitcher.action.queue.entry.add"
: "AdvSceneSwitcher.action.queue.entry.other"),
_layout,
{{"{{actions}}", _actions},
{"{{queues}}", _queues},
{"{{macros}}", _macros}});
_macros->setVisible(_entryData->_action ==
MacroActionQueue::Action::ADD_TO_QUEUE);
_macros->HideSelectedMacro();
}
} // namespace advss

View File

@ -0,0 +1,75 @@
#pragma once
#include "macro-action-edit.hpp"
#include "macro-selection.hpp"
#include "action-queue.hpp"
#include <QHBoxLayout>
namespace advss {
class MacroActionQueue : public MacroRefAction {
public:
MacroActionQueue(Macro *m) : MacroAction(m), MacroRefAction(m) {}
bool PerformAction();
void LogAction() const;
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetShortDesc() const;
std::string GetId() const { return id; };
static std::shared_ptr<MacroAction> Create(Macro *m)
{
return std::make_shared<MacroActionQueue>(m);
}
enum class Action {
ADD_TO_QUEUE,
START_QUEUE,
STOP_QUEUE,
CLEAR_QUEUE,
};
Action _action = Action::ADD_TO_QUEUE;
std::weak_ptr<ActionQueue> _queue;
private:
void AddActions(ActionQueue *);
static bool _registered;
static const std::string id;
};
class MacroActionQueueEdit : public QWidget {
Q_OBJECT
public:
MacroActionQueueEdit(
QWidget *parent,
std::shared_ptr<MacroActionQueue> entryData = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroAction> action)
{
return new MacroActionQueueEdit(
parent,
std::dynamic_pointer_cast<MacroActionQueue>(action));
}
private slots:
void MacroChanged(const QString &text);
void QueueChanged(const QString &);
void ActionChanged(int value);
signals:
void HeaderInfoChanged(const QString &);
private:
void SetWidgetVisibility();
MacroSelection *_macros;
ActionQueueSelection *_queues;
QComboBox *_actions;
QHBoxLayout *_layout;
std::shared_ptr<MacroActionQueue> _entryData;
bool _loading = true;
};
} // namespace advss

View File

@ -0,0 +1,163 @@
#include "macro-condition-queue.hpp"
#include "utility.hpp"
namespace advss {
const std::string MacroConditionQueue::id = "queue";
bool MacroConditionQueue::_registered = MacroConditionFactory::Register(
MacroConditionQueue::id,
{MacroConditionQueue::Create, MacroConditionQueueEdit::Create,
"AdvSceneSwitcher.condition.queue"});
const static std::map<MacroConditionQueue::Condition, std::string>
conditionTypes = {
{MacroConditionQueue::Condition::STARTED,
"AdvSceneSwitcher.condition.queue.type.started"},
{MacroConditionQueue::Condition::STOPPED,
"AdvSceneSwitcher.condition.queue.type.stopped"},
{MacroConditionQueue::Condition::SIZE,
"AdvSceneSwitcher.condition.queue.type.size"},
};
bool MacroConditionQueue::CheckCondition()
{
auto queue = _queue.lock();
if (!queue) {
return false;
}
switch (_condition) {
case Condition::STARTED:
return queue->IsRunning();
case Condition::STOPPED:
return !queue->IsRunning();
case Condition::SIZE:
return queue->Size() < _size;
default:
break;
}
return false;
}
bool MacroConditionQueue::Save(obs_data_t *obj) const
{
MacroCondition::Save(obj);
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
obs_data_set_string(obj, "queue", GetActionQueueName(_queue).c_str());
_size.Save(obj, "size");
return true;
}
bool MacroConditionQueue::Load(obs_data_t *obj)
{
MacroCondition::Load(obj);
_condition = static_cast<Condition>(obs_data_get_int(obj, "condition"));
_queue = GetWeakActionQueueByName(obs_data_get_string(obj, "queue"));
_size.Load(obj, "size");
return true;
}
static inline void populateQueueTypeSelection(QComboBox *list)
{
for (const auto &[_, name] : conditionTypes) {
list->addItem(obs_module_text(name.c_str()));
}
}
MacroConditionQueueEdit::MacroConditionQueueEdit(
QWidget *parent, std::shared_ptr<MacroConditionQueue> entryData)
: QWidget(parent),
_conditions(new QComboBox()),
_queues(new ActionQueueSelection()),
_size(new VariableSpinBox()),
_layout(new QHBoxLayout())
{
populateQueueTypeSelection(_conditions);
_size->setMinimum(1);
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ConditionChanged(int)));
QWidget::connect(_queues, SIGNAL(SelectionChanged(const QString &)),
this, SLOT(QueueChanged(const QString &)));
QWidget::connect(
_size,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(SizeChanged(const NumberVariable<int> &)));
setLayout(_layout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
}
void MacroConditionQueueEdit::ConditionChanged(int condition)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_condition =
static_cast<MacroConditionQueue::Condition>(condition);
SetWidgetVisibility();
}
void MacroConditionQueueEdit::QueueChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_queue = GetWeakActionQueueByQString(text);
emit HeaderInfoChanged(
QString::fromStdString(_entryData->GetShortDesc()));
}
void MacroConditionQueueEdit::SizeChanged(const NumberVariable<int> &value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_size = value;
}
void MacroConditionQueueEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_conditions->setCurrentIndex(static_cast<int>(_entryData->_condition));
_queues->SetActionQueue(_entryData->_queue);
SetWidgetVisibility();
}
void MacroConditionQueueEdit::SetWidgetVisibility()
{
_layout->removeWidget(_conditions);
_layout->removeWidget(_queues);
_layout->removeWidget(_size);
ClearLayout(_layout);
PlaceWidgets(
obs_module_text(
_entryData->_condition ==
MacroConditionQueue::Condition::SIZE
? "AdvSceneSwitcher.condition.queue.entry.size"
: "AdvSceneSwitcher.condition.queue.entry.startStop"),
_layout,
{{"{{conditions}}", _conditions},
{"{{queues}}", _queues},
{"{{size}}", _size}});
_size->setVisible(_entryData->_condition ==
MacroConditionQueue::Condition::SIZE);
}
} // namespace advss

View File

@ -0,0 +1,74 @@
#pragma once
#include "macro-condition-edit.hpp"
#include "action-queue.hpp"
#include <QCheckBox>
#include <QPushButton>
#include <QComboBox>
#include <QHBoxLayout>
namespace advss {
class MacroConditionQueue : public MacroCondition {
public:
MacroConditionQueue(Macro *m) : MacroCondition(m, true) {}
bool CheckCondition();
bool Save(obs_data_t *obj) const;
bool Load(obs_data_t *obj);
std::string GetId() const { return id; };
static std::shared_ptr<MacroCondition> Create(Macro *m)
{
return std::make_shared<MacroConditionQueue>(m);
}
enum class Condition {
STARTED,
STOPPED,
SIZE,
};
Condition _condition = Condition::STARTED;
std::weak_ptr<ActionQueue> _queue;
IntVariable _size = 1;
private:
static bool _registered;
static const std::string id;
};
class MacroConditionQueueEdit : public QWidget {
Q_OBJECT
public:
MacroConditionQueueEdit(
QWidget *parent,
std::shared_ptr<MacroConditionQueue> cond = nullptr);
void UpdateEntryData();
static QWidget *Create(QWidget *parent,
std::shared_ptr<MacroCondition> cond)
{
return new MacroConditionQueueEdit(
parent,
std::dynamic_pointer_cast<MacroConditionQueue>(cond));
}
private slots:
void ConditionChanged(int);
void QueueChanged(const QString &);
void SizeChanged(const NumberVariable<int> &);
signals:
void HeaderInfoChanged(const QString &);
private:
void SetWidgetVisibility();
QComboBox *_conditions;
ActionQueueSelection *_queues;
VariableSpinBox *_size;
QHBoxLayout *_layout;
std::shared_ptr<MacroConditionQueue> _entryData;
bool _loading = true;
};
} // namespace advss