#include "macro-condition-scene-transform.hpp" #include "layout-helpers.hpp" #include "json-helpers.hpp" #include "math-helpers.hpp" #include "text-helpers.hpp" #include "scene-item-transform-helpers.hpp" namespace advss { const std::string MacroConditionSceneTransform::id = "scene_transform"; bool MacroConditionSceneTransform::_registered = MacroConditionFactory::Register( MacroConditionSceneTransform::id, {MacroConditionSceneTransform::Create, MacroConditionSceneTransformEdit::Create, "AdvSceneSwitcher.condition.sceneTransform"}); static const std::map settingsTypes = { {MacroConditionSceneTransform::SettingsType::ALL, "AdvSceneSwitcher.condition.sceneTransform.settingsType.all"}, {MacroConditionSceneTransform::SettingsType::SINGLE, "AdvSceneSwitcher.condition.sceneTransform.settingsType.single"}, }; static const std::map compareTypes = { {MacroConditionSceneTransform::Compare::LESS, "AdvSceneSwitcher.condition.sceneTransform.compare.less"}, {MacroConditionSceneTransform::Compare::EQUAL, "AdvSceneSwitcher.condition.sceneTransform.compare.equal"}, {MacroConditionSceneTransform::Compare::MORE, "AdvSceneSwitcher.condition.sceneTransform.compare.more"}, }; static const std::map conditionTypes = { {MacroConditionSceneTransform::Condition::MATCHES, "AdvSceneSwitcher.condition.sceneTransform.condition.match"}, {MacroConditionSceneTransform::Condition::CHANGED, "AdvSceneSwitcher.condition.sceneTransform.condition.changed"}, }; static bool doesTransformOfAnySceneItemMatch( const std::vector &items, const std::string &jsonCompare, const RegexConfig ®ex, std::string &newVariable) { bool ret = false; std::string json; for (const auto &item : items) { json = GetSceneItemTransform(item); if (MatchJson(json, jsonCompare, regex)) { ret = true; } } newVariable = json; return ret; } static bool doesTransformOfAllSceneItemsMatch( const std::vector &items, const std::string &jsonCompare, const RegexConfig ®ex, std::string &newVariable) { bool ret = true; std::string json; for (const auto &item : items) { json = GetSceneItemTransform(item); if (!MatchJson(json, jsonCompare, regex)) { ret = false; } } newVariable = json; return ret; } static bool didTransformOfAnySceneItemChange(const std::vector &items, std::vector &previousTransform, std::string &newVariable) { bool ret = false; std::string json; RegexConfig regex; auto numItems = items.size(); if (previousTransform.size() < numItems) { ret = true; previousTransform.resize(numItems); } for (size_t idx = 0; idx < numItems; ++idx) { auto const &item = items[idx]; json = GetSceneItemTransform(item); if (!MatchJson(json, previousTransform[idx], regex)) { ret = true; previousTransform[idx] = json; } } newVariable = json; return ret; } static bool didTransformOfAllSceneItemsChange(const std::vector &items, std::vector &previousTransform, std::string &newVariable) { const auto numItems = items.size(); if (previousTransform.size() != numItems) { previousTransform.resize(numItems); return false; } std::string json; RegexConfig regex; bool ret = true; for (size_t idx = 0; idx < numItems; ++idx) { auto const &item = items[idx]; json = GetSceneItemTransform(item); if (MatchJson(json, previousTransform[idx], regex)) { ret = false; } else { previousTransform[idx] = json; } } newVariable = json; return ret; } bool MacroConditionSceneTransform::CheckAllSettings( const std::vector &items) { std::string newVariable = ""; const bool checkAny = _source.IsSelectionOfTypeAny(); bool ret = false; switch (_condition) { case Condition::MATCHES: ret = checkAny ? doesTransformOfAnySceneItemMatch( items, _transformString, _regex, newVariable) : doesTransformOfAllSceneItemsMatch( items, _transformString, _regex, newVariable); break; case Condition::CHANGED: ret = checkAny ? didTransformOfAnySceneItemChange( items, _previousTransform, newVariable) : didTransformOfAllSceneItemsChange( items, _previousTransform, newVariable); break; default: return false; } SetVariableValue(newVariable); SetTempVarValue("settings", newVariable); return ret; } bool MacroConditionSceneTransform::AnySceneItemTransformSettingChanged( const std::vector &items) { bool ret = false; if (_previousSettingValues.size() < items.size()) { _previousSettingValues.resize(items.size()); return true; } std::vector varValues; for (size_t i = 0; i < items.size(); i++) { const auto &item = items[i]; auto &previousValue = _previousSettingValues[i]; const auto currentValue = GetTransformSettingValue(item, _setting); if (!currentValue) { continue; } varValues.emplace_back(*currentValue); if (*currentValue == previousValue) { continue; } previousValue = *currentValue; ret = true; } SetTempVarHelper(varValues); return ret; } static bool compareValue(MacroConditionSceneTransform::Compare compareMethod, double value1, double value2) { switch (compareMethod) { case MacroConditionSceneTransform::Compare::LESS: return value1 < value2; case MacroConditionSceneTransform::Compare::EQUAL: return DoubleEquals(value1, value2, 0.00001); case MacroConditionSceneTransform::Compare::MORE: return value1 > value2; default: break; } return false; } bool MacroConditionSceneTransform::AnySceneItemTransformSettingMatches( const std::vector &items) { bool ret = false; std::vector varValues; for (const auto &item : items) { const auto currentValue = GetTransformSettingValue(item, _setting); if (!currentValue) { continue; } try { if (compareValue(_compare, std::stod(*currentValue), std::stod(_singleSetting))) { ret = true; } } catch (std::invalid_argument &) { } catch (std::out_of_range &) { } varValues.emplace_back(*currentValue); } SetTempVarHelper(varValues); return ret; } void MacroConditionSceneTransform::SetTempVarHelper( const std::vector &values) { std::string varValue; bool addSeperator = false; for (const auto &value : values) { if (addSeperator) { varValue += ";"; } varValue += value; addSeperator = true; } SetTempVarValue("setting", varValue); } bool MacroConditionSceneTransform::CheckSingleSetting( const std::vector &items) { const bool checkAny = _source.IsSelectionOfTypeAny(); switch (_condition) { case Condition::MATCHES: return checkAny ? AnySceneItemTransformSettingMatches(items) : AllSceneItemsTransformSettingMatch(items); case Condition::CHANGED: return checkAny ? AnySceneItemTransformSettingChanged(items) : AllSceneItemsTransformSettingChanged(items); break; default: break; } return false; } bool MacroConditionSceneTransform::AllSceneItemsTransformSettingChanged( const std::vector &items) { if (_previousSettingValues.size() < items.size()) { _previousSettingValues.resize(items.size()); return false; } if (_setting.GetID().empty()) { return false; } bool ret = true; std::vector varValues; for (size_t i = 0; i < items.size(); i++) { const auto &item = items[i]; auto &previousValue = _previousSettingValues[i]; const auto currentValue = GetTransformSettingValue(item, _setting); if (!currentValue) { continue; } varValues.emplace_back(*currentValue); if (*currentValue == previousValue) { ret = false; } previousValue = *currentValue; } SetTempVarHelper(varValues); return ret; } bool MacroConditionSceneTransform::AllSceneItemsTransformSettingMatch( const std::vector &items) { if (_setting.GetID().empty()) { return false; } bool ret = true; std::vector varValues; for (const auto &item : items) { const auto currentValue = GetTransformSettingValue(item, _setting); if (!currentValue) { continue; } try { if (!compareValue(_compare, std::stod(*currentValue), std::stod(_singleSetting))) { ret = false; } } catch (std::invalid_argument &) { } catch (std::out_of_range &) { } varValues.emplace_back(*currentValue); } SetTempVarHelper(varValues); return ret; } bool MacroConditionSceneTransform::CheckCondition() { auto items = _source.GetSceneItems(_scene); if (items.empty()) { _previousSettingValues.clear(); _previousTransform.clear(); return false; } switch (_settingsType) { case SettingsType::ALL: return CheckAllSettings(items); case SettingsType::SINGLE: return CheckSingleSetting(items); default: break; } return false; } bool MacroConditionSceneTransform::Save(obs_data_t *obj) const { MacroCondition::Save(obj); _scene.Save(obj); _source.Save(obj); _transformString.Save(obj, "transformString"); _singleSetting.Save(obj, "singleSetting"); _regex.Save(obj); _setting.Save(obj); obs_data_set_int(obj, "settingsType", static_cast(_settingsType)); obs_data_set_int(obj, "compare", static_cast(_compare)); obs_data_set_int(obj, "condition", static_cast(_condition)); obs_data_set_int(obj, "version", 1); return true; } bool MacroConditionSceneTransform::Load(obs_data_t *obj) { // Convert old data format // TODO: Remove in future version if (obs_data_has_user_value(obj, "source")) { auto sourceName = obs_data_get_string(obj, "source"); obs_data_set_string(obj, "sceneItem", sourceName); } MacroCondition::Load(obj); _scene.Load(obj); _source.Load(obj); _transformString.Load(obj, "transformString"); _singleSetting.Load(obj, "singleSetting"); _regex.Load(obj); _setting.Load(obj); SetSettingsType(static_cast( obs_data_get_int(obj, "settingsType"))); _compare = static_cast(obs_data_get_int(obj, "compare")); SetCondition( static_cast(obs_data_get_int(obj, "condition"))); // TOOD: remove in future version if (obs_data_has_user_value(obj, "regex")) { _regex.CreateBackwardsCompatibleRegex( obs_data_get_bool(obj, "regex")); } if (!obs_data_has_user_value(obj, "version")) { SetCondition( static_cast(obs_data_get_int(obj, "type"))); _transformString.Load(obj, "settings"); SetSettingsType(SettingsType::ALL); _compare = Compare::EQUAL; } SetupTempVars(); return true; } std::string MacroConditionSceneTransform::GetShortDesc() const { if (_source.ToString().empty()) { return ""; } return _scene.ToString() + " - " + _source.ToString(); } void MacroConditionSceneTransform::SetSettingsType(SettingsType type) { _settingsType = type; SetupTempVars(); } void MacroConditionSceneTransform::SetCondition(Condition condition) { _condition = condition; _previousTransform.clear(); _previousSettingValues.clear(); } void MacroConditionSceneTransform::SetupTempVars() { MacroCondition::SetupTempVars(); if (_settingsType == SettingsType::ALL) { AddTempvar( "settings", obs_module_text( "AdvSceneSwitcher.tempVar.transform.settings")); } else { AddTempvar( "setting", obs_module_text( "AdvSceneSwitcher.tempVar.transform.setting")); } } template static inline void populateSelection(QComboBox *list, const std::map &map) { for (const auto &[_, name] : map) { list->addItem(obs_module_text(name.c_str())); } } MacroConditionSceneTransformEdit::MacroConditionSceneTransformEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent), _scenes(new SceneSelectionWidget(this, true, false, false, true)), _sources(new SceneItemSelectionWidget( parent, { SceneItemSelection::Type::SOURCE_NAME, SceneItemSelection::Type::VARIABLE_NAME, SceneItemSelection::Type::SOURCE_NAME_PATTERN, SceneItemSelection::Type::SOURCE_GROUP, SceneItemSelection::Type::SOURCE_TYPE, SceneItemSelection::Type::INDEX, SceneItemSelection::Type::INDEX_RANGE, SceneItemSelection::Type::ALL, SceneItemSelection::Type::ANY, }, SceneItemSelectionWidget::NameClashMode::ANY_AND_ALL)), _settingsType(new QComboBox()), _compare(new QComboBox()), _conditions(new QComboBox()), _getSettings(new QPushButton(obs_module_text( "AdvSceneSwitcher.condition.sceneTransform.getTransform"))), _getCurrentValue(new QPushButton(obs_module_text( "AdvSceneSwitcher.condition.sceneTransform.getCurrentValue"))), _transformString(new VariableTextEdit(this)), _singleSettingValue(new VariableLineEdit(this)), _regex(new RegexConfigWidget(parent)), _settingSelection(new TransformSettingSelection(this)), _compareLayout(new QHBoxLayout()) { populateSelection(_settingsType, settingsTypes); populateSelection(_compare, compareTypes); populateSelection(_conditions, conditionTypes); QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)), this, SLOT(SceneChanged(const SceneSelection &))); QWidget::connect(_scenes, SIGNAL(SceneChanged(const SceneSelection &)), _sources, SLOT(SceneChanged(const SceneSelection &))); QWidget::connect(_sources, SIGNAL(SceneItemChanged(const SceneItemSelection &)), this, SLOT(SourceChanged(const SceneItemSelection &))); QWidget::connect(_settingsType, SIGNAL(currentIndexChanged(int)), this, SLOT(SettingsTypeChanged(int))); QWidget::connect(_compare, SIGNAL(currentIndexChanged(int)), this, SLOT(CompareChanged(int))); QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this, SLOT(ConditionChanged(int))); QWidget::connect( _settingSelection, SIGNAL(SelectionChanged(const TransformSetting &)), this, SLOT(SettingSelectionChanged(const TransformSetting &))); QWidget::connect(_getSettings, SIGNAL(clicked()), this, SLOT(GetSettingsClicked())); QWidget::connect(_getCurrentValue, SIGNAL(clicked()), this, SLOT(GetCurrentValueClicked())); QWidget::connect(_transformString, SIGNAL(textChanged()), this, SLOT(TransformChanged())); QWidget::connect(_singleSettingValue, SIGNAL(editingFinished()), this, SLOT(SettingValueChanged())); QWidget::connect(_regex, SIGNAL(RegexConfigChanged(const RegexConfig &)), this, SLOT(RegexChanged(const RegexConfig &))); const std::unordered_map widgetPlaceholders = { {"{{scenes}}", _scenes}, {"{{sources}}", _sources}, {"{{compare}}", _compare}, {"{{setting}}", _settingSelection}, {"{{settingsType}}", _settingsType}, {"{{conditions}}", _conditions}, {"{{transformString}}", _transformString}, {"{{singleSettingValue}}", _singleSettingValue}, {"{{settings}}", _singleSettingValue}, {"{{getSettings}}", _getSettings}, {"{{getCurrentValue}}", _getCurrentValue}, {"{{regex}}", _regex}, }; auto entryLayout = new QHBoxLayout; PlaceWidgets(obs_module_text( "AdvSceneSwitcher.condition.sceneTransform.entry"), entryLayout, widgetPlaceholders); PlaceWidgets( obs_module_text( "AdvSceneSwitcher.condition.sceneTransform.entry.singleSettingCompare"), _compareLayout, widgetPlaceholders, false); auto optionsLayout = new QHBoxLayout; PlaceWidgets( obs_module_text( "AdvSceneSwitcher.condition.sceneTransform.entry.options"), optionsLayout, widgetPlaceholders); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addLayout(entryLayout); mainLayout->addLayout(_compareLayout); mainLayout->addWidget(_transformString); mainLayout->addLayout(optionsLayout); setLayout(mainLayout); _entryData = entryData; UpdateEntryData(); _loading = false; } void MacroConditionSceneTransformEdit::UpdateEntryData() { if (!_entryData) { return; } _scenes->SetScene(_entryData->_scene); _sources->SetSceneItem(_entryData->_source); _settingsType->setCurrentIndex( static_cast(_entryData->GetSettingsType())); _compare->setCurrentIndex(static_cast(_entryData->_compare)); _conditions->setCurrentIndex( static_cast(_entryData->GetCondition())); _regex->SetRegexConfig(_entryData->_regex); UpdateSettingSelection(); _settingSelection->SetSetting(_entryData->_setting); _transformString->setPlainText(_entryData->_transformString); _singleSettingValue->setText(_entryData->_singleSetting); SetWidgetVisibility(); } void MacroConditionSceneTransformEdit::SceneChanged(const SceneSelection &s) { { GUARD_LOADING_AND_LOCK(); _entryData->_scene = s; } UpdateSettingSelection(); } void MacroConditionSceneTransformEdit::SourceChanged( const SceneItemSelection &item) { { GUARD_LOADING_AND_LOCK(); _entryData->_source = item; } UpdateSettingSelection(); emit HeaderInfoChanged( QString::fromStdString(_entryData->GetShortDesc())); adjustSize(); updateGeometry(); } void MacroConditionSceneTransformEdit::SettingsTypeChanged(int type) { GUARD_LOADING_AND_LOCK(); _entryData->SetSettingsType( static_cast(type)); SetWidgetVisibility(); } void MacroConditionSceneTransformEdit::CompareChanged(int type) { GUARD_LOADING_AND_LOCK(); _entryData->_compare = static_cast(type); } void MacroConditionSceneTransformEdit::ConditionChanged(int index) { GUARD_LOADING_AND_LOCK(); _entryData->SetCondition( static_cast(index)); SetWidgetVisibility(); } void MacroConditionSceneTransformEdit::SettingSelectionChanged( const TransformSetting &setting) { GUARD_LOADING_AND_LOCK(); _entryData->_setting = setting; } void MacroConditionSceneTransformEdit::GetSettingsClicked() { if (_loading || !_entryData || !_entryData->_scene.GetScene(false)) { return; } auto items = _entryData->_source.GetSceneItems(_entryData->_scene); if (items.empty()) { return; } auto settings = FormatJsonString(GetSceneItemTransform(items[0])); if (_entryData->_regex.Enabled()) { settings = EscapeForRegex(settings); } _transformString->setPlainText(settings); adjustSize(); updateGeometry(); } void MacroConditionSceneTransformEdit::GetCurrentValueClicked() { if (_loading || !_entryData || !_entryData->_scene.GetScene(false)) { return; } auto items = _entryData->_source.GetSceneItems(_entryData->_scene); if (items.empty()) { return; } auto value = GetTransformSettingValue(items.at(0), _entryData->_setting); if (!value) { return; } _singleSettingValue->setText(QString::fromStdString(*value)); SettingValueChanged(); } void MacroConditionSceneTransformEdit::TransformChanged() { GUARD_LOADING_AND_LOCK(); _entryData->_transformString = _transformString->toPlainText().toStdString(); adjustSize(); updateGeometry(); } void MacroConditionSceneTransformEdit::SettingValueChanged() { GUARD_LOADING_AND_LOCK(); _entryData->_singleSetting = _singleSettingValue->text().toStdString(); adjustSize(); updateGeometry(); } void MacroConditionSceneTransformEdit::RegexChanged(const RegexConfig &conf) { GUARD_LOADING_AND_LOCK(); _entryData->_regex = conf; adjustSize(); updateGeometry(); } void MacroConditionSceneTransformEdit::SetWidgetVisibility() { const auto condition = _entryData->GetCondition(); const auto settingsType = _entryData->GetSettingsType(); _transformString->setVisible( condition == MacroConditionSceneTransform::Condition::MATCHES); _regex->setVisible( condition == MacroConditionSceneTransform::Condition::MATCHES && settingsType == MacroConditionSceneTransform::SettingsType::ALL); _getSettings->setVisible( condition == MacroConditionSceneTransform::Condition::MATCHES && settingsType == MacroConditionSceneTransform::SettingsType::ALL); _getCurrentValue->setVisible( condition == MacroConditionSceneTransform::Condition::MATCHES && settingsType == MacroConditionSceneTransform::SettingsType::SINGLE); SetLayoutVisible( _compareLayout, condition == MacroConditionSceneTransform::Condition::MATCHES && settingsType == MacroConditionSceneTransform:: SettingsType::SINGLE); _settingSelection->setVisible( settingsType == MacroConditionSceneTransform::SettingsType::SINGLE); if (condition == MacroConditionSceneTransform::Condition::MATCHES && settingsType == MacroConditionSceneTransform::SettingsType::SINGLE) { RemoveStretchIfPresent(_compareLayout); } else if (condition == MacroConditionSceneTransform::Condition::CHANGED && settingsType == MacroConditionSceneTransform::SettingsType::SINGLE) { AddStretchIfNecessary(_compareLayout); } _transformString->setVisible( condition == MacroConditionSceneTransform::Condition::MATCHES && settingsType == MacroConditionSceneTransform::SettingsType::ALL); adjustSize(); updateGeometry(); } void MacroConditionSceneTransformEdit::UpdateSettingSelection() const { if (!_entryData) { _settingSelection->SetSource(nullptr); return; } auto items = _entryData->_source.GetSceneItems(_entryData->_scene); if (items.empty()) { _settingSelection->SetSource(nullptr); return; } _settingSelection->SetSource(items.at(0)); } } // namespace advss