diff --git a/CMakeLists.txt b/CMakeLists.txt index b2d3c7a2..525477ec 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,6 +184,7 @@ set(advanced-scene-switcher_HEADERS src/headers/macro-condition-recording.hpp src/headers/macro-condition-replay-buffer.hpp src/headers/macro-condition-scene-order.hpp + src/headers/macro-condition-scene-transform.hpp src/headers/macro-condition-scene.hpp src/headers/macro-condition-source.hpp src/headers/macro-condition-streaming.hpp @@ -266,6 +267,7 @@ set(advanced-scene-switcher_SOURCES src/macro-condition-recording.cpp src/macro-condition-replay-buffer.cpp src/macro-condition-scene-order.cpp + src/macro-condition-scene-transform.cpp src/macro-condition-scene.cpp src/macro-condition-source.cpp src/macro-condition-streaming.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index fd845ee7..d5f6e0b7 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -191,6 +191,12 @@ AdvSceneSwitcher.condition.date.state.before="Before" AdvSceneSwitcher.condition.date.state.between="Between" AdvSceneSwitcher.condition.date.entry.line1="{{condition}} {{dateTime}} {{dateTime2}} {{ignoreDate}} Ignore date component {{ignoreTime}} Ignore time component" AdvSceneSwitcher.condition.date.entry.line2="{{repeat}} Reschedule {{duration}} later when matching" +AdvSceneSwitcher.condition.sceneTransform="Scene item transform" +AdvSceneSwitcher.condition.sceneTransform.getTransform="Get transform" +AdvSceneSwitcher.condition.sceneTransform.regex="Use regular expressions" +AdvSceneSwitcher.condition.sceneTransform.entry.line1="On {{scenes}} {{sources}} matches transform" +AdvSceneSwitcher.condition.sceneTransform.entry.line2="{{settings}}" +AdvSceneSwitcher.condition.sceneTransform.entry.line3="{{regex}} {{getSettings}}" ; Macro Actions AdvSceneSwitcher.action.switchScene="Switch scene" diff --git a/src/headers/macro-condition-scene-transform.hpp b/src/headers/macro-condition-scene-transform.hpp new file mode 100644 index 00000000..9e393286 --- /dev/null +++ b/src/headers/macro-condition-scene-transform.hpp @@ -0,0 +1,69 @@ +#pragma once +#include "macro.hpp" +#include "scene-selection.hpp" + +#include +#include +#include + +class MacroConditionSceneTransform : public MacroCondition { +public: + bool CheckCondition(); + bool Save(obs_data_t *obj); + bool Load(obs_data_t *obj); + std::string GetShortDesc(); + std::string GetId() { return id; }; + static std::shared_ptr Create() + { + return std::make_shared(); + } + + SceneSelection _scene; + OBSWeakSource _source; + bool _regex = false; + std::string _settings = ""; + +private: + static bool _registered; + static const std::string id; +}; + +class MacroConditionSceneTransformEdit : public QWidget { + Q_OBJECT + +public: + MacroConditionSceneTransformEdit( + QWidget *parent, + std::shared_ptr entryData = + nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr cond) + { + return new MacroConditionSceneTransformEdit( + parent, + std::dynamic_pointer_cast( + cond)); + } + +private slots: + void SceneChanged(const SceneSelection &); + void SourceChanged(const QString &text); + void GetSettingsClicked(); + void SettingsChanged(); + void RegexChanged(int); +signals: + void HeaderInfoChanged(const QString &); + +protected: + SceneSelectionWidget *_scenes; + QComboBox *_sources; + QPushButton *_getSettings; + QPlainTextEdit *_settings; + QCheckBox *_regex; + + std::shared_ptr _entryData; + +private: + bool _loading = true; +}; diff --git a/src/macro-condition-scene-transform.cpp b/src/macro-condition-scene-transform.cpp new file mode 100644 index 00000000..c3efe065 --- /dev/null +++ b/src/macro-condition-scene-transform.cpp @@ -0,0 +1,225 @@ +#include "headers/macro-condition-edit.hpp" +#include "headers/macro-condition-scene-transform.hpp" +#include "headers/utility.hpp" +#include "headers/advanced-scene-switcher.hpp" + +const std::string MacroConditionSceneTransform::id = "scene_transform"; + +bool MacroConditionSceneTransform::_registered = + MacroConditionFactory::Register( + MacroConditionSceneTransform::id, + {MacroConditionSceneTransform::Create, + MacroConditionSceneTransformEdit::Create, + "AdvSceneSwitcher.condition.sceneTransform"}); + +bool MacroConditionSceneTransform::CheckCondition() +{ + bool ret = false; + auto s = obs_weak_source_get_source(_scene.GetScene(false)); + auto scene = obs_scene_from_source(s); + auto name = GetWeakSourceName(_source); + auto items = getSceneItemsWithName(scene, name); + + for (auto &item : items) { + struct obs_transform_info info; + struct obs_sceneitem_crop crop; + obs_sceneitem_get_info(item, &info); + obs_sceneitem_get_crop(item, &crop); + + auto data = obs_data_create(); + saveTransformState(data, info, crop); + auto json = obs_data_get_json(data); + + if (matchJson(json, _settings, _regex)) { + ret = true; + } + + obs_data_release(data); + obs_sceneitem_release(item); + } + + obs_source_release(s); + return ret; +} + +bool MacroConditionSceneTransform::Save(obs_data_t *obj) +{ + MacroCondition::Save(obj); + _scene.Save(obj); + obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str()); + obs_data_set_string(obj, "settings", _settings.c_str()); + obs_data_set_bool(obj, "regex", _regex); + return true; +} + +bool MacroConditionSceneTransform::Load(obs_data_t *obj) +{ + MacroCondition::Load(obj); + _scene.Load(obj); + const char *sourceName = obs_data_get_string(obj, "source"); + _source = GetWeakSourceByName(sourceName); + _settings = obs_data_get_string(obj, "settings"); + _regex = obs_data_get_bool(obj, "regex"); + return true; +} + +std::string MacroConditionSceneTransform::GetShortDesc() +{ + if (_source) { + return _scene.ToString() + " - " + GetWeakSourceName(_source); + } + return ""; +} + +MacroConditionSceneTransformEdit::MacroConditionSceneTransformEdit( + QWidget *parent, + std::shared_ptr entryData) + : QWidget(parent) +{ + _scenes = new SceneSelectionWidget(window(), false, false, true); + _sources = new QComboBox(); + _getSettings = new QPushButton(obs_module_text( + "AdvSceneSwitcher.condition.sceneTransform.getTransform")); + _settings = new QPlainTextEdit(); + _regex = new QCheckBox(obs_module_text( + "AdvSceneSwitcher.condition.sceneTransform.regex")); + + QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)), + this, SLOT(SceneChanged(const SceneSelection &))); + QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)), + this, SLOT(SourceChanged(const QString &))); + QWidget::connect(_getSettings, SIGNAL(clicked()), this, + SLOT(GetSettingsClicked())); + QWidget::connect(_settings, SIGNAL(textChanged()), this, + SLOT(SettingsChanged())); + QWidget::connect(_regex, SIGNAL(stateChanged(int)), this, + SLOT(RegexChanged(int))); + + std::unordered_map widgetPlaceholders = { + {"{{scenes}}", _scenes}, {"{{sources}}", _sources}, + {"{{settings}}", _settings}, {"{{getSettings}}", _getSettings}, + {"{{regex}}", _regex}, + }; + + QHBoxLayout *line1Layout = new QHBoxLayout; + placeWidgets( + obs_module_text( + "AdvSceneSwitcher.condition.sceneTransform.entry.line1"), + line1Layout, widgetPlaceholders); + QHBoxLayout *line2Layout = new QHBoxLayout; + placeWidgets( + obs_module_text( + "AdvSceneSwitcher.condition.sceneTransform.entry.line2"), + line2Layout, widgetPlaceholders, false); + QHBoxLayout *line3Layout = new QHBoxLayout; + placeWidgets( + obs_module_text( + "AdvSceneSwitcher.condition.sceneTransform.entry.line3"), + line3Layout, widgetPlaceholders); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addLayout(line1Layout); + mainLayout->addLayout(line2Layout); + mainLayout->addLayout(line3Layout); + setLayout(mainLayout); + + _entryData = entryData; + UpdateEntryData(); + _loading = false; +} + +void MacroConditionSceneTransformEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + + _scenes->SetScene(_entryData->_scene); + populateSceneItemSelection(_sources, _entryData->_scene); + _sources->setCurrentText( + GetWeakSourceName(_entryData->_source).c_str()); + _regex->setChecked(_entryData->_regex); + if (_entryData->_source) { + _settings->setPlainText( + QString::fromStdString(_entryData->_settings)); + } +} + +void MacroConditionSceneTransformEdit::SceneChanged(const SceneSelection &s) +{ + if (_loading || !_entryData) { + return; + } + { + std::lock_guard lock(switcher->m); + _entryData->_scene = s; + } + _sources->clear(); + populateSceneItemSelection(_sources, _entryData->_scene); +} + +void MacroConditionSceneTransformEdit::SourceChanged(const QString &text) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_source = GetWeakSourceByQString(text); + emit HeaderInfoChanged( + QString::fromStdString(_entryData->GetShortDesc())); +} + +void MacroConditionSceneTransformEdit::GetSettingsClicked() +{ + if (_loading || !_entryData || !_entryData->_scene.GetScene(false) || + !_entryData->_source) { + return; + } + + auto s = obs_weak_source_get_source(_entryData->_scene.GetScene(false)); + auto scene = obs_scene_from_source(s); + auto name = GetWeakSourceName(_entryData->_source); + auto item = obs_scene_find_source_recursive(scene, name.c_str()); + obs_source_release(s); + + if (!item) { + return; + } + + struct obs_transform_info info; + struct obs_sceneitem_crop crop; + obs_sceneitem_get_info(item, &info); + obs_sceneitem_get_crop(item, &crop); + + auto data = obs_data_create(); + saveTransformState(data, info, crop); + + QString json = formatJsonString(obs_data_get_json(data)); + if (_entryData->_regex) { + json = escapeForRegex(json); + } + + _settings->setPlainText(json); + obs_data_release(data); +} + +void MacroConditionSceneTransformEdit::SettingsChanged() +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_settings = _settings->toPlainText().toStdString(); +} + +void MacroConditionSceneTransformEdit::RegexChanged(int state) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_regex = state; +}