SceneSwitcher/plugins/base/macro-action-sequence.cpp
WarmUpTill 7d0332dd0e Restructure library and plugins
The "core" macro conditions and actions have been extracted out to the
"base" plugin.

The library now mostly contains functionality which is required across
all plugins and (e.g. definitions for macro segments).

The goal is to reduce the complexity and cross-dependencies and group
the source files in a better way.

This should relsove the "library limit of 65535 objects exceeded" build
issue occuring in some Windows build environments.
2024-01-27 14:10:34 +01:00

433 lines
10 KiB
C++

#include "macro-action-sequence.hpp"
#include "macro-helpers.hpp"
#include "utility.hpp"
namespace advss {
const std::string MacroActionSequence::id = "sequence";
bool MacroActionSequence::_registered = MacroActionFactory::Register(
MacroActionSequence::id,
{MacroActionSequence::Create, MacroActionSequenceEdit::Create,
"AdvSceneSwitcher.action.sequence"});
static const std::map<MacroActionSequence::Action, std::string> actionTypes = {
{MacroActionSequence::Action::RUN_SEQUENCE,
"AdvSceneSwitcher.action.sequence.action.run"},
{MacroActionSequence::Action::SET_INDEX,
"AdvSceneSwitcher.action.sequence.action.setIndex"},
};
static int getNextUnpausedMacroIdx(std::vector<MacroRef> &macros, int startIdx)
{
if (startIdx < 0) {
return -1;
}
for (; (int)macros.size() > startIdx; ++startIdx) {
auto macro = macros[startIdx].GetMacro();
if (!MacroIsPaused(macro.get())) {
return startIdx;
}
}
return -1;
}
MacroRef MacroActionSequence::GetNextMacro(bool advance)
{
int idx = _lastIdx;
MacroRef res;
int nextUnpausedIdx = getNextUnpausedMacroIdx(_macros, idx + 1);
if (nextUnpausedIdx != -1) {
res = _macros[nextUnpausedIdx];
idx = nextUnpausedIdx;
} else {
if (_restart) {
idx = getNextUnpausedMacroIdx(_macros, 0);
if (idx != -1) {
res = _macros[idx];
} else {
// End of sequence reached, as all available
// macros are either paused or the macro list
// is empty
idx = _macros.size();
}
} else {
// End of sequence reached
idx = _macros.size();
}
}
if (advance) {
_lastIdx = idx;
_lastSequenceMacro = res;
}
return res;
}
bool MacroActionSequence::RunSequence()
{
if (_macros.size() == 0) {
return true;
}
auto macro = GetNextMacro().GetMacro();
if (!macro.get()) {
return true;
}
return RunMacroActions(macro.get());
}
bool MacroActionSequence::SetSequenceIndex() const
{
auto macro = _macro.GetMacro();
if (!macro) {
return true;
}
auto actions = GetMacroActions(macro.get());
if (!actions) {
return true;
}
for (const auto &action : *actions) {
if (action->GetId() != id) {
continue;
}
auto sequenceAction =
dynamic_cast<MacroActionSequence *>(action.get());
if (!sequenceAction) {
continue;
}
// -2 is needed since the _lastIndex starts at -1 and the reset
// index starts at 1
sequenceAction->_lastIdx = _resetIndex - 2;
}
return true;
}
bool MacroActionSequence::PerformAction()
{
if (_action == Action::RUN_SEQUENCE) {
return RunSequence();
} else {
return SetSequenceIndex();
}
return false;
}
void MacroActionSequence::LogAction() const
{
if (_action == Action::RUN_SEQUENCE) {
vblog(LOG_INFO, "running macro sequence");
} else {
vblog(LOG_INFO, "set sequences of macro '%s' to index %d",
_macro.Name().c_str(), _resetIndex.GetValue());
}
}
bool MacroActionSequence::Save(obs_data_t *obj) const
{
MacroAction::Save(obj);
SaveMacroList(obj, _macros);
obs_data_set_bool(obj, "restart", _restart);
_macro.Save(obj);
obs_data_set_int(obj, "action", static_cast<int>(_action));
_resetIndex.Save(obj, "resetIndex");
return true;
}
bool MacroActionSequence::Load(obs_data_t *obj)
{
MacroAction::Load(obj);
LoadMacroList(obj, _macros);
_restart = obs_data_get_bool(obj, "restart");
_macro.Load(obj);
_action = static_cast<Action>(obs_data_get_int(obj, "action"));
_resetIndex.Load(obj, "resetIndex");
return true;
}
bool MacroActionSequence::PostLoad()
{
return MacroAction::PostLoad() && MacroRefAction::PostLoad() &&
MultiMacroRefAction::PostLoad();
}
static inline void populateActionSelection(QComboBox *list)
{
for (const auto &[_, name] : actionTypes) {
list->addItem(obs_module_text(name.c_str()));
}
}
MacroActionSequenceEdit::MacroActionSequenceEdit(
QWidget *parent, std::shared_ptr<MacroActionSequence> entryData)
: QWidget(parent),
_macroList(new MacroList(this, true, true)),
_continueFrom(new QPushButton(obs_module_text(
"AdvSceneSwitcher.action.sequence.continueFrom"))),
_restart(new QCheckBox(
obs_module_text("AdvSceneSwitcher.action.sequence.restart"))),
_statusLine(new QLabel()),
_actions(new QComboBox()),
_macros(new MacroSelection(this)),
_resetIndex(new VariableSpinBox()),
_layout(new QHBoxLayout())
{
populateActionSelection(_actions);
_resetIndex->setMinimum(1);
auto line = new QFrame();
line->setFrameShape(QFrame::VLine);
line->setFrameShadow(QFrame::Sunken);
_macroList->AddControl(line);
_macroList->AddControl(_continueFrom);
QWidget::connect(_macroList, SIGNAL(Added(const std::string &)), this,
SLOT(Add(const std::string &)));
QWidget::connect(_macroList, SIGNAL(Removed(int)), this,
SLOT(Remove(int)));
QWidget::connect(_macroList, SIGNAL(MovedUp(int)), this, SLOT(Up(int)));
QWidget::connect(_macroList, SIGNAL(MovedDown(int)), this,
SLOT(Down(int)));
QWidget::connect(_macroList, SIGNAL(Replaced(int, const std::string &)),
this, SLOT(Replace(int, const std::string &)));
QWidget::connect(_continueFrom, SIGNAL(clicked()), this,
SLOT(ContinueFromClicked()));
QWidget::connect(_restart, SIGNAL(stateChanged(int)), this,
SLOT(RestartChanged(int)));
QWidget::connect(window(), SIGNAL(MacroRemoved(const QString &)), this,
SLOT(MacroRemove(const QString &)));
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
SLOT(ActionChanged(int)));
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
this, SLOT(MacroChanged(const QString &)));
QWidget::connect(
_resetIndex,
SIGNAL(NumberVariableChanged(const NumberVariable<int> &)),
this, SLOT(ResetIndexChanged(const NumberVariable<int> &)));
_layout->addWidget(_actions);
_layout->addWidget(_macros);
_layout->addWidget(_resetIndex);
auto mainLayout = new QVBoxLayout;
mainLayout->addLayout(_layout);
mainLayout->addWidget(_macroList);
mainLayout->addWidget(_restart);
mainLayout->addWidget(_statusLine);
setLayout(mainLayout);
_entryData = entryData;
UpdateEntryData();
_loading = false;
UpdateStatusLine();
connect(&_statusTimer, SIGNAL(timeout()), this,
SLOT(UpdateStatusLine()));
_statusTimer.start(1000);
}
void MacroActionSequenceEdit::UpdateEntryData()
{
if (!_entryData) {
return;
}
_macroList->SetContent(_entryData->_macros);
_restart->setChecked(_entryData->_restart);
_resetIndex->SetValue(_entryData->_resetIndex);
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
_macros->SetCurrentMacro(_entryData->_macro);
SetWidgetVisibility();
adjustSize();
}
void MacroActionSequenceEdit::MacroRemove(const QString &)
{
if (!_entryData) {
return;
}
auto it = _entryData->_macros.begin();
while (it != _entryData->_macros.end()) {
if (!it->GetMacro()) {
it = _entryData->_macros.erase(it);
} else {
++it;
}
}
adjustSize();
}
void MacroActionSequenceEdit::Add(const std::string &name)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
MacroRef macro(name);
_entryData->_macros.push_back(macro);
adjustSize();
}
void MacroActionSequenceEdit::Remove(int idx)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_macros.erase(std::next(_entryData->_macros.begin(), idx));
adjustSize();
}
void MacroActionSequenceEdit::Up(int idx)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
std::swap(_entryData->_macros[idx], _entryData->_macros[idx - 1]);
}
void MacroActionSequenceEdit::Down(int idx)
{
auto lock = LockContext();
std::swap(_entryData->_macros[idx], _entryData->_macros[idx + 1]);
}
void MacroActionSequenceEdit::Replace(int idx, const std::string &name)
{
if (_loading || !_entryData) {
return;
}
MacroRef macro(name);
auto lock = LockContext();
_entryData->_macros[idx] = macro;
adjustSize();
}
void MacroActionSequenceEdit::ContinueFromClicked()
{
if (_loading || !_entryData) {
return;
}
int idx = _macroList->CurrentRow();
if (idx == -1) {
return;
}
auto lock = LockContext();
_entryData->_lastIdx = idx - 1;
}
void MacroActionSequenceEdit::RestartChanged(int state)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_restart = state;
}
void MacroActionSequenceEdit::UpdateStatusLine()
{
QString lastMacroName =
obs_module_text("AdvSceneSwitcher.action.sequence.status.none");
QString nextMacroName =
obs_module_text("AdvSceneSwitcher.action.sequence.status.none");
if (_entryData) {
if (auto macro = _entryData->_lastSequenceMacro.GetMacro()) {
lastMacroName = QString::fromStdString(
GetMacroName(macro.get()));
}
auto next = _entryData->GetNextMacro(false).GetMacro();
if (next) {
nextMacroName = QString::fromStdString(
GetMacroName(next.get()));
}
}
QString format{
obs_module_text("AdvSceneSwitcher.action.sequence.status")};
_statusLine->setText(format.arg(lastMacroName, nextMacroName));
}
void MacroActionSequenceEdit::ActionChanged(int value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_action = static_cast<MacroActionSequence::Action>(value);
SetWidgetVisibility();
}
void MacroActionSequenceEdit::MacroChanged(const QString &text)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_macro = text;
}
void MacroActionSequenceEdit::ResetIndexChanged(const NumberVariable<int> &value)
{
if (_loading || !_entryData) {
return;
}
auto lock = LockContext();
_entryData->_resetIndex = value;
}
void MacroActionSequenceEdit::SetWidgetVisibility()
{
_layout->removeWidget(_actions);
_layout->removeWidget(_macros);
_layout->removeWidget(_resetIndex);
ClearLayout(_layout);
PlaceWidgets(
obs_module_text(
_entryData->_action ==
MacroActionSequence::Action::RUN_SEQUENCE
? "AdvSceneSwitcher.action.sequence.entry.run"
: "AdvSceneSwitcher.action.sequence.entry.setIndex"),
_layout,
{{"{{actions}}", _actions},
{"{{macros}}", _macros},
{"{{index}}", _resetIndex}});
_macroList->setVisible(_entryData->_action ==
MacroActionSequence::Action::RUN_SEQUENCE);
_restart->setVisible(_entryData->_action ==
MacroActionSequence::Action::RUN_SEQUENCE);
_statusLine->setVisible(_entryData->_action ==
MacroActionSequence::Action::RUN_SEQUENCE);
_macros->setVisible(_entryData->_action ==
MacroActionSequence::Action::SET_INDEX);
_resetIndex->setVisible(_entryData->_action ==
MacroActionSequence::Action::SET_INDEX);
adjustSize();
updateGeometry();
}
} // namespace advss