SceneSwitcher/plugins/base/macro-condition-scene-transform.cpp

778 lines
21 KiB
C++

#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<MacroConditionSceneTransform::SettingsType, std::string>
settingsTypes = {
{MacroConditionSceneTransform::SettingsType::ALL,
"AdvSceneSwitcher.condition.sceneTransform.settingsType.all"},
{MacroConditionSceneTransform::SettingsType::SINGLE,
"AdvSceneSwitcher.condition.sceneTransform.settingsType.single"},
};
static const std::map<MacroConditionSceneTransform::Compare, std::string>
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<MacroConditionSceneTransform::Condition, std::string>
conditionTypes = {
{MacroConditionSceneTransform::Condition::MATCHES,
"AdvSceneSwitcher.condition.sceneTransform.condition.match"},
{MacroConditionSceneTransform::Condition::CHANGED,
"AdvSceneSwitcher.condition.sceneTransform.condition.changed"},
};
static bool doesTransformOfAnySceneItemMatch(
const std::vector<OBSSceneItem> &items, const std::string &jsonCompare,
const RegexConfig &regex, 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<OBSSceneItem> &items, const std::string &jsonCompare,
const RegexConfig &regex, 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<OBSSceneItem> &items,
std::vector<std::string> &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<OBSSceneItem> &items,
std::vector<std::string> &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<OBSSceneItem> &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<OBSSceneItem> &items)
{
bool ret = false;
if (_previousSettingValues.size() < items.size()) {
_previousSettingValues.resize(items.size());
return true;
}
std::vector<std::string> 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<OBSSceneItem> &items)
{
bool ret = false;
std::vector<std::string> 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<std::string> &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<OBSSceneItem> &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<OBSSceneItem> &items)
{
if (_previousSettingValues.size() < items.size()) {
_previousSettingValues.resize(items.size());
return false;
}
if (_setting.GetID().empty()) {
return false;
}
bool ret = true;
std::vector<std::string> 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<OBSSceneItem> &items)
{
if (_setting.GetID().empty()) {
return false;
}
bool ret = true;
std::vector<std::string> 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<int>(_settingsType));
obs_data_set_int(obj, "compare", static_cast<int>(_compare));
obs_data_set_int(obj, "condition", static_cast<int>(_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<SettingsType>(
obs_data_get_int(obj, "settingsType")));
_compare = static_cast<Compare>(obs_data_get_int(obj, "compare"));
SetCondition(
static_cast<Condition>(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<Condition>(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<class T>
static inline void populateSelection(QComboBox *list,
const std::map<T, std::string> &map)
{
for (const auto &[_, name] : map) {
list->addItem(obs_module_text(name.c_str()));
}
}
MacroConditionSceneTransformEdit::MacroConditionSceneTransformEdit(
QWidget *parent,
std::shared_ptr<MacroConditionSceneTransform> 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<std::string, QWidget *> 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<int>(_entryData->GetSettingsType()));
_compare->setCurrentIndex(static_cast<int>(_entryData->_compare));
_conditions->setCurrentIndex(
static_cast<int>(_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<MacroConditionSceneTransform::SettingsType>(type));
SetWidgetVisibility();
}
void MacroConditionSceneTransformEdit::CompareChanged(int type)
{
GUARD_LOADING_AND_LOCK();
_entryData->_compare =
static_cast<MacroConditionSceneTransform::Compare>(type);
}
void MacroConditionSceneTransformEdit::ConditionChanged(int index)
{
GUARD_LOADING_AND_LOCK();
_entryData->SetCondition(
static_cast<MacroConditionSceneTransform::Condition>(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