diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 61f721bf..76db7a16 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -1460,6 +1460,12 @@ AdvSceneSwitcher.action.sceneLock.type.lock="lock" AdvSceneSwitcher.action.sceneLock.type.unlock="unlock" AdvSceneSwitcher.action.sceneLock.type.toggle="toggle lock of" AdvSceneSwitcher.action.sceneLock.entry="On{{scenes}}{{actions}}{{sources}}" +AdvSceneSwitcher.action.createSource="Create source" +AdvSceneSwitcher.action.createSource.scene="Scene:" +AdvSceneSwitcher.action.createSource.type="Source type:" +AdvSceneSwitcher.action.createSource.name="Name:" +AdvSceneSwitcher.action.createSource.name.default="New source" +AdvSceneSwitcher.action.createSource.name.help="If a source with this name already exists, a number will be appended to the name of the new source (e.g. \"New source 2\").\nUse the \"Source name\" temp variable to get the actual name." AdvSceneSwitcher.action.twitch="Twitch" AdvSceneSwitcher.action.twitch.type.channel.info.title.set="Set stream title" AdvSceneSwitcher.action.twitch.type.channel.info.category.set="Set stream category" @@ -2685,6 +2691,9 @@ AdvSceneSwitcher.tempVar.sequence.nextMacro.description="The name of the macro w AdvSceneSwitcher.tempVar.random.macro="Macro" AdvSceneSwitcher.tempVar.random.macro.description="The name of the macro executed by the \"Random\" action.\nIf no macro was executed the returned value is the empty string." +AdvSceneSwitcher.tempVar.createSource.name="Source name" +AdvSceneSwitcher.tempVar.createSource.name.description="The name of the created source.\nThis can differ from the configured name if a source with that name already existed." + AdvSceneSwitcher.selectScene="--select scene--" AdvSceneSwitcher.selectCanvas="--select canvas--" AdvSceneSwitcher.selectPreviousScene="Previous Scene" diff --git a/plugins/base/CMakeLists.txt b/plugins/base/CMakeLists.txt index 38df4043..4543c418 100644 --- a/plugins/base/CMakeLists.txt +++ b/plugins/base/CMakeLists.txt @@ -9,6 +9,8 @@ target_sources( macro-action-audio.hpp macro-action-clipboard.cpp macro-action-clipboard.hpp + macro-action-create-source.cpp + macro-action-create-source.hpp macro-action-file.cpp macro-action-file.hpp macro-action-filter.cpp diff --git a/plugins/base/macro-action-create-source.cpp b/plugins/base/macro-action-create-source.cpp new file mode 100644 index 00000000..f913bbe8 --- /dev/null +++ b/plugins/base/macro-action-create-source.cpp @@ -0,0 +1,200 @@ +#include "macro-action-create-source.hpp" + +#include +#include + +namespace advss { + +const std::string MacroActionCreateSource::id = "create_source"; + +bool MacroActionCreateSource::_registered = MacroActionFactory::Register( + MacroActionCreateSource::id, + {MacroActionCreateSource::Create, MacroActionCreateSourceEdit::Create, + "AdvSceneSwitcher.action.createSource"}); + +bool MacroActionCreateSource::PerformAction() +{ + OBSSourceAutoRelease sceneSource = + obs_weak_source_get_source(_scene.GetScene(false)); + auto scene = obs_scene_from_source(sceneSource); + if (!scene) { + blog(LOG_WARNING, + "cannot create source on invalid scene \"%s\"", + _scene.ToString(true).c_str()); + return true; + } + + OBSSourceAutoRelease source = obs_source_create( + _sourceType.c_str(), _sourceName.c_str(), nullptr, nullptr); + if (!source) { + blog(LOG_WARNING, "failed to create source of type \"%s\"", + _sourceType.c_str()); + return true; + } + + if (!obs_scene_add(scene, source)) { + blog(LOG_WARNING, "failed to add source \"%s\" to scene \"%s\"", + obs_source_get_name(source), + _scene.ToString(true).c_str()); + return true; + } + + SetTempVarValue("sourceName", obs_source_get_name(source)); + return true; +} + +void MacroActionCreateSource::LogAction() const +{ + ablog(LOG_INFO, "create source \"%s\" of type \"%s\" on scene \"%s\"", + _sourceName.c_str(), _sourceType.c_str(), + _scene.ToString(true).c_str()); +} + +bool MacroActionCreateSource::Save(obs_data_t *obj) const +{ + MacroAction::Save(obj); + _scene.Save(obj); + obs_data_set_string(obj, "sourceType", _sourceType.c_str()); + _sourceName.Save(obj, "sourceName"); + return true; +} + +bool MacroActionCreateSource::Load(obs_data_t *obj) +{ + MacroAction::Load(obj); + _scene.Load(obj); + _sourceType = obs_data_get_string(obj, "sourceType"); + _sourceName.Load(obj, "sourceName"); + return true; +} + +std::string MacroActionCreateSource::GetShortDesc() const +{ + return _scene.ToString() + " - " + _sourceName.UnresolvedValue(); +} + +std::shared_ptr MacroActionCreateSource::Create(Macro *m) +{ + return std::make_shared(m); +} + +std::shared_ptr MacroActionCreateSource::Copy() const +{ + return std::make_shared(*this); +} + +void MacroActionCreateSource::ResolveVariablesToFixedValues() +{ + _scene.ResolveVariables(); + _sourceName.ResolveVariables(); +} + +void MacroActionCreateSource::SetupTempVars() +{ + MacroAction::SetupTempVars(); + AddTempvar( + "sourceName", + obs_module_text("AdvSceneSwitcher.tempVar.createSource.name"), + obs_module_text( + "AdvSceneSwitcher.tempVar.createSource.name.description")); +} + +static void populateSourceTypeSelection(QComboBox *list) +{ + size_t idx = 0; + const char *type = nullptr; + const char *unversionedType = nullptr; + while (obs_enum_input_types2(idx++, &type, &unversionedType)) { + const uint32_t caps = obs_get_source_output_flags(type); + if ((caps & OBS_SOURCE_CAP_DISABLED) != 0) { + continue; + } + const char *name = obs_source_get_display_name(type); + list->addItem(name ? name : unversionedType, unversionedType); + } + list->model()->sort(0); +} + +MacroActionCreateSourceEdit::MacroActionCreateSourceEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent), + _scenes(new SceneSelectionWidget(this, true, false, true, true)), + _sourceTypes(new FilterComboBox( + this, obs_module_text("AdvSceneSwitcher.selectItem"))), + _sourceName(new VariableLineEdit(this)), + _sourceNameHelp(new HelpIcon( + obs_module_text( + "AdvSceneSwitcher.action.createSource.name.help"), + this)) +{ + populateSourceTypeSelection(_sourceTypes); + + QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)), + this, SLOT(SceneChanged(const SceneSelection &))); + QWidget::connect(_sourceTypes, SIGNAL(currentIndexChanged(int)), this, + SLOT(SourceTypeChanged(int))); + QWidget::connect(_sourceName, SIGNAL(editingFinished()), this, + SLOT(SourceNameChanged())); + + auto layout = new QGridLayout; + int row = 0; + layout->addWidget( + new QLabel(obs_module_text( + "AdvSceneSwitcher.action.createSource.scene")), + row, 0); + layout->addWidget(_scenes, row, 1); + ++row; + layout->addWidget(new QLabel(obs_module_text( + "AdvSceneSwitcher.action.createSource.type")), + row, 0); + layout->addWidget(_sourceTypes, row, 1); + ++row; + layout->addWidget(new QLabel(obs_module_text( + "AdvSceneSwitcher.action.createSource.name")), + row, 0); + layout->addWidget(_sourceName, row, 1); + layout->addWidget(_sourceNameHelp, row, 2); + layout->setColumnStretch(1, 1); + setLayout(layout); + + _entryData = entryData; + UpdateEntryData(); + _loading = false; +} + +void MacroActionCreateSourceEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + + _scenes->SetScene(_entryData->_scene); + _sourceTypes->setCurrentIndex(_sourceTypes->findData( + QString::fromStdString(_entryData->_sourceType))); + _sourceName->setText(_entryData->_sourceName); +} + +void MacroActionCreateSourceEdit::SceneChanged(const SceneSelection &scene) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_scene = scene; + emit HeaderInfoChanged( + QString::fromStdString(_entryData->GetShortDesc())); +} + +void MacroActionCreateSourceEdit::SourceTypeChanged(int idx) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_sourceType = + _sourceTypes->itemData(idx).toString().toStdString(); +} + +void MacroActionCreateSourceEdit::SourceNameChanged() +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_sourceName = _sourceName->text().toStdString(); + emit HeaderInfoChanged( + QString::fromStdString(_entryData->GetShortDesc())); +} + +} // namespace advss diff --git a/plugins/base/macro-action-create-source.hpp b/plugins/base/macro-action-create-source.hpp new file mode 100644 index 00000000..3eb81ff1 --- /dev/null +++ b/plugins/base/macro-action-create-source.hpp @@ -0,0 +1,69 @@ +#pragma once +#include "filter-combo-box.hpp" +#include "help-icon.hpp" +#include "macro-action-edit.hpp" +#include "scene-selection.hpp" +#include "variable-line-edit.hpp" +#include "variable-string.hpp" + +namespace advss { + +class MacroActionCreateSource : public MacroAction { +public: + MacroActionCreateSource(Macro *m) : MacroAction(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 Create(Macro *m); + std::shared_ptr Copy() const; + void ResolveVariablesToFixedValues(); + + SceneSelection _scene; + std::string _sourceType; + StringVariable _sourceName = obs_module_text( + "AdvSceneSwitcher.action.createSource.name.default"); + +private: + void SetupTempVars(); + + static bool _registered; + static const std::string id; +}; + +class MacroActionCreateSourceEdit : public QWidget { + Q_OBJECT + +public: + MacroActionCreateSourceEdit( + QWidget *parent, + std::shared_ptr entryData = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr action) + { + return new MacroActionCreateSourceEdit( + parent, + std::dynamic_pointer_cast( + action)); + } + +private slots: + void SceneChanged(const SceneSelection &); + void SourceTypeChanged(int); + void SourceNameChanged(); +signals: + void HeaderInfoChanged(const QString &); + +private: + SceneSelectionWidget *_scenes; + FilterComboBox *_sourceTypes; + VariableLineEdit *_sourceName; + HelpIcon *_sourceNameHelp; + std::shared_ptr _entryData; + bool _loading = true; +}; + +} // namespace advss