From d6924446870069082ad4c6c95767051e5f7f520a Mon Sep 17 00:00:00 2001 From: WarmUpTill Date: Sun, 13 Jun 2021 21:43:41 +0200 Subject: [PATCH] Add / extend macro filter condition / action --- CMakeLists.txt | 2 + data/locale/en-US.ini | 11 ++ src/headers/macro-action-filter.hpp | 9 +- src/headers/macro-action-source.hpp | 1 - src/headers/macro-condition-filter.hpp | 71 ++++++++ src/macro-action-filter.cpp | 121 ++++++------- src/macro-condition-filter.cpp | 231 +++++++++++++++++++++++++ 7 files changed, 386 insertions(+), 60 deletions(-) create mode 100644 src/headers/macro-condition-filter.hpp create mode 100644 src/macro-condition-filter.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index d6ed77ec..5c8d376f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,6 +98,7 @@ set(advanced-scene-switcher_HEADERS src/headers/macro-condition-audio.hpp src/headers/macro-condition-macro.hpp src/headers/macro-condition-file.hpp + src/headers/macro-condition-filter.hpp src/headers/macro-condition-idle.hpp src/headers/macro-condition-interval.hpp src/headers/macro-condition-media.hpp @@ -168,6 +169,7 @@ set(advanced-scene-switcher_SOURCES src/macro-condition-audio.cpp src/macro-condition-macro.cpp src/macro-condition-file.cpp + src/macro-condition-filter.cpp src/macro-condition-idle.cpp src/macro-condition-interval.cpp src/macro-condition-media.cpp diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 502dd90f..830baae7 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -150,6 +150,15 @@ AdvSceneSwitcher.condition.virtualCamera="Virtual camera" AdvSceneSwitcher.condition.virtualCamera.state.start="Virtual camera started" AdvSceneSwitcher.condition.virtualCamera.state.stop="Virtual camera stopped" AdvSceneSwitcher.condition.virtualCamera.entry="{{states}}" +AdvSceneSwitcher.condition.filter="Filter" +AdvSceneSwitcher.condition.filter.type.active="Is enabled" +AdvSceneSwitcher.condition.filter.type.showing="Is disabled" +AdvSceneSwitcher.condition.filter.type.settings="Settings match" +AdvSceneSwitcher.condition.filter.regex="Use regular expressions" +AdvSceneSwitcher.condition.filter.getSettings="Get current settings" +AdvSceneSwitcher.condition.filter.entry.line1="On {{sources}} {{filters}} {{conditions}}" +AdvSceneSwitcher.condition.filter.entry.line2="{{settings}}" +AdvSceneSwitcher.condition.filter.entry.line3="{{regex}} {{getSettings}}" ; Macro Actions AdvSceneSwitcher.action.switchScene="Switch scene" @@ -190,7 +199,9 @@ AdvSceneSwitcher.action.sceneVisibility.entry="On {{scenes}} {{actions}} {{sourc AdvSceneSwitcher.action.filter="Filter" AdvSceneSwitcher.action.filter.type.enable="Enable" AdvSceneSwitcher.action.filter.type.disable="Disable" +AdvSceneSwitcher.action.filter.type.settings="Set settings" AdvSceneSwitcher.action.filter.entry="On {{sources}} {{actions}} {{filters}}" +AdvSceneSwitcher.action.filter.getSettings="Get current settings" AdvSceneSwitcher.action.source="Source" AdvSceneSwitcher.action.source.type.enable="Enable" AdvSceneSwitcher.action.source.type.disable="Disable" diff --git a/src/headers/macro-action-filter.hpp b/src/headers/macro-action-filter.hpp index 63d07f3e..bd572baa 100644 --- a/src/headers/macro-action-filter.hpp +++ b/src/headers/macro-action-filter.hpp @@ -1,10 +1,12 @@ #pragma once #include +#include #include "macro-action-edit.hpp" enum class FilterAction { ENABLE, DISABLE, + SETTINGS, }; class MacroActionFilter : public MacroAction { @@ -22,6 +24,7 @@ public: OBSWeakSource _source; OBSWeakSource _filter; FilterAction _action = FilterAction::ENABLE; + std::string _settings = ""; private: static bool _registered; @@ -48,14 +51,18 @@ private slots: void SourceChanged(const QString &text); void FilterChanged(const QString &text); void ActionChanged(int value); + void GetSettingsClicked(); + void SettingsChanged(); protected: QComboBox *_sources; QComboBox *_filters; QComboBox *_actions; + QPushButton *_getSettings; + QPlainTextEdit *_settings; std::shared_ptr _entryData; private: - QHBoxLayout *_mainLayout; + void SetWidgetVisibility(bool); bool _loading = true; }; diff --git a/src/headers/macro-action-source.hpp b/src/headers/macro-action-source.hpp index 8b0e5178..79b64592 100644 --- a/src/headers/macro-action-source.hpp +++ b/src/headers/macro-action-source.hpp @@ -65,6 +65,5 @@ protected: private: void SetWidgetVisibility(bool); - QHBoxLayout *_mainLayout; bool _loading = true; }; diff --git a/src/headers/macro-condition-filter.hpp b/src/headers/macro-condition-filter.hpp new file mode 100644 index 00000000..ae30b35a --- /dev/null +++ b/src/headers/macro-condition-filter.hpp @@ -0,0 +1,71 @@ +#pragma once +#include "macro.hpp" +#include +#include + +enum class FilterCondition { + ENABLED, + DISABLED, + SETTINGS, +}; + +class MacroConditionFilter : public MacroCondition { +public: + bool CheckCondition(); + bool Save(obs_data_t *obj); + bool Load(obs_data_t *obj); + std::string GetId() { return id; }; + static std::shared_ptr Create() + { + return std::make_shared(); + } + + OBSWeakSource _source; + OBSWeakSource _filter; + FilterCondition _condition = FilterCondition::ENABLED; + std::string _settings = ""; + bool _regex = false; + +private: + static bool _registered; + static const std::string id; +}; + +class MacroConditionFilterEdit : public QWidget { + Q_OBJECT + +public: + MacroConditionFilterEdit( + QWidget *parent, + std::shared_ptr cond = nullptr); + void UpdateEntryData(); + static QWidget *Create(QWidget *parent, + std::shared_ptr cond) + { + return new MacroConditionFilterEdit( + parent, + std::dynamic_pointer_cast(cond)); + } + +private slots: + void SourceChanged(const QString &text); + void FilterChanged(const QString &text); + void ConditionChanged(int cond); + void GetSettingsClicked(); + void SettingsChanged(); + void RegexChanged(int); + +protected: + QComboBox *_sources; + QComboBox *_filters; + QComboBox *_conditions; + QPushButton *_getSettings; + QPlainTextEdit *_settings; + QCheckBox *_regex; + + std::shared_ptr _entryData; + +private: + void SetSettingsSelectionVisible(bool visible); + bool _loading = true; +}; diff --git a/src/macro-action-filter.cpp b/src/macro-action-filter.cpp index 81e4f95c..f4349e7b 100644 --- a/src/macro-action-filter.cpp +++ b/src/macro-action-filter.cpp @@ -12,6 +12,8 @@ bool MacroActionFilter::_registered = MacroActionFactory::Register( const static std::map actionTypes = { {FilterAction::ENABLE, "AdvSceneSwitcher.action.filter.type.enable"}, {FilterAction::DISABLE, "AdvSceneSwitcher.action.filter.type.disable"}, + {FilterAction::SETTINGS, + "AdvSceneSwitcher.action.filter.type.settings"}, }; bool MacroActionFilter::PerformAction() @@ -24,6 +26,9 @@ bool MacroActionFilter::PerformAction() case FilterAction::DISABLE: obs_source_set_enabled(s, false); break; + case FilterAction::SETTINGS: + setSourceSettings(s, _settings); + break; default: break; } @@ -51,6 +56,7 @@ bool MacroActionFilter::Save(obs_data_t *obj) obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str()); obs_data_set_string(obj, "filter", GetWeakSourceName(_filter).c_str()); obs_data_set_int(obj, "action", static_cast(_action)); + obs_data_set_string(obj, "settings", _settings.c_str()); return true; } @@ -62,6 +68,7 @@ bool MacroActionFilter::Load(obs_data_t *obj) const char *filterName = obs_data_get_string(obj, "filter"); _filter = GetWeakFilterByQString(_source, filterName); _action = static_cast(obs_data_get_int(obj, "action")); + _settings = obs_data_get_string(obj, "settings"); return true; } @@ -72,56 +79,6 @@ static inline void populateActionSelection(QComboBox *list) } } -static inline void populateFilters(QComboBox *list, - OBSWeakSource weakSource = nullptr) -{ - auto enumFilters = [](obs_source_t *, obs_source_t *filter, void *ptr) { - QComboBox *list = reinterpret_cast(ptr); - auto name = obs_source_get_name(filter); - list->addItem(name); - }; - - auto s = obs_weak_source_get_source(weakSource); - obs_source_enum_filters(s, enumFilters, list); - list->model()->sort(0); - addSelectionEntry(list, - obs_module_text("AdvSceneSwitcher.selectFilter")); - obs_source_release(s); - list->setCurrentIndex(0); -} - -static inline void hasFilterEnum(obs_source_t *, obs_source_t *filter, - void *ptr) -{ - if (!filter) { - return; - } - bool *hasFilter = reinterpret_cast(ptr); - *hasFilter = true; -} - -static inline void populateSourcesWithFilter(QComboBox *list) -{ - auto enumSourcesWithFilters = [](void *param, obs_source_t *source) { - if (!source) { - return true; - } - QComboBox *list = reinterpret_cast(param); - bool hasFilter = false; - obs_source_enum_filters(source, hasFilterEnum, &hasFilter); - if (hasFilter) { - list->addItem(obs_source_get_name(source)); - } - return true; - }; - obs_enum_sources(enumSourcesWithFilters, list); - obs_enum_scenes(enumSourcesWithFilters, list); - list->model()->sort(0); - addSelectionEntry(list, - obs_module_text("AdvSceneSwitcher.selectSource")); - list->setCurrentIndex(0); -} - MacroActionFilterEdit::MacroActionFilterEdit( QWidget *parent, std::shared_ptr entryData) : QWidget(parent) @@ -129,9 +86,12 @@ MacroActionFilterEdit::MacroActionFilterEdit( _sources = new QComboBox(); _filters = new QComboBox(); _actions = new QComboBox(); + _getSettings = new QPushButton( + obs_module_text("AdvSceneSwitcher.action.filter.getSettings")); + _settings = new QPlainTextEdit(); populateActionSelection(_actions); - populateSourcesWithFilter(_sources); + populateSourcesWithFilterSelection(_sources); QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this, SLOT(ActionChanged(int))); @@ -139,15 +99,29 @@ MacroActionFilterEdit::MacroActionFilterEdit( this, SLOT(SourceChanged(const QString &))); QWidget::connect(_filters, SIGNAL(currentTextChanged(const QString &)), this, SLOT(FilterChanged(const QString &))); + QWidget::connect(_getSettings, SIGNAL(clicked()), this, + SLOT(GetSettingsClicked())); + QWidget::connect(_settings, SIGNAL(textChanged()), this, + SLOT(SettingsChanged())); + + QHBoxLayout *entryLayout = new QHBoxLayout; - QHBoxLayout *mainLayout = new QHBoxLayout; std::unordered_map widgetPlaceholders = { - {"{{sources}}", _sources}, - {"{{filters}}", _filters}, - {"{{actions}}", _actions}, + {"{{sources}}", _sources}, {"{{filters}}", _filters}, + {"{{actions}}", _actions}, {"{{settings}}", _settings}, + {"{{getSettings}}", _getSettings}, }; placeWidgets(obs_module_text("AdvSceneSwitcher.action.filter.entry"), - mainLayout, widgetPlaceholders); + entryLayout, widgetPlaceholders); + + QHBoxLayout *buttonLayout = new QHBoxLayout; + buttonLayout->addWidget(_getSettings); + buttonLayout->addStretch(); + + QVBoxLayout *mainLayout = new QVBoxLayout; + mainLayout->addLayout(entryLayout); + mainLayout->addWidget(_settings); + mainLayout->addLayout(buttonLayout); setLayout(mainLayout); _entryData = entryData; @@ -164,9 +138,11 @@ void MacroActionFilterEdit::UpdateEntryData() _actions->setCurrentIndex(static_cast(_entryData->_action)); _sources->setCurrentText( GetWeakSourceName(_entryData->_source).c_str()); - populateFilters(_filters, _entryData->_source); + populateFilterSelection(_filters, _entryData->_source); _filters->setCurrentText( GetWeakSourceName(_entryData->_filter).c_str()); + _settings->setPlainText(QString::fromStdString(_entryData->_settings)); + SetWidgetVisibility(_entryData->_action == FilterAction::SETTINGS); } void MacroActionFilterEdit::SourceChanged(const QString &text) @@ -179,7 +155,7 @@ void MacroActionFilterEdit::SourceChanged(const QString &text) _entryData->_source = GetWeakSourceByQString(text); } _filters->clear(); - populateFilters(_filters, _entryData->_source); + populateFilterSelection(_filters, _entryData->_source); } void MacroActionFilterEdit::FilterChanged(const QString &text) @@ -200,4 +176,33 @@ void MacroActionFilterEdit::ActionChanged(int value) std::lock_guard lock(switcher->m); _entryData->_action = static_cast(value); + SetWidgetVisibility(_entryData->_action == FilterAction::SETTINGS); +} + +void MacroActionFilterEdit::GetSettingsClicked() +{ + if (_loading || !_entryData || !_entryData->_source || + !_entryData->_filter) { + return; + } + + _settings->setPlainText( + QString::fromStdString(getSourceSettings(_entryData->_filter))); +} + +void MacroActionFilterEdit::SettingsChanged() +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_settings = _settings->toPlainText().toStdString(); +} + +void MacroActionFilterEdit::SetWidgetVisibility(bool showSettings) +{ + _settings->setVisible(showSettings); + _getSettings->setVisible(showSettings); + adjustSize(); } diff --git a/src/macro-condition-filter.cpp b/src/macro-condition-filter.cpp new file mode 100644 index 00000000..3186afaf --- /dev/null +++ b/src/macro-condition-filter.cpp @@ -0,0 +1,231 @@ +#include "headers/macro-condition-edit.hpp" +#include "headers/macro-condition-filter.hpp" +#include "headers/utility.hpp" +#include "headers/advanced-scene-switcher.hpp" + +#include + +const std::string MacroConditionFilter::id = "filter"; + +bool MacroConditionFilter::_registered = MacroConditionFactory::Register( + MacroConditionFilter::id, + {MacroConditionFilter::Create, MacroConditionFilterEdit::Create, + "AdvSceneSwitcher.condition.filter"}); + +static std::map filterConditionTypes = { + {FilterCondition::ENABLED, + "AdvSceneSwitcher.condition.filter.type.active"}, + {FilterCondition::DISABLED, + "AdvSceneSwitcher.condition.filter.type.showing"}, + {FilterCondition::SETTINGS, + "AdvSceneSwitcher.condition.filter.type.settings"}, +}; + +bool MacroConditionFilter::CheckCondition() +{ + if (!_source) { + return false; + } + + bool ret = false; + auto s = obs_weak_source_get_source(_filter); + + switch (_condition) { + case FilterCondition::ENABLED: + ret = obs_source_enabled(s); + break; + case FilterCondition::DISABLED: + ret = !obs_source_enabled(s); + break; + case FilterCondition::SETTINGS: + ret = compareSourceSettings(_filter, _settings, _regex); + break; + default: + break; + } + + obs_source_release(s); + + return ret; +} + +bool MacroConditionFilter::Save(obs_data_t *obj) +{ + MacroCondition::Save(obj); + obs_data_set_string(obj, "source", GetWeakSourceName(_source).c_str()); + obs_data_set_string(obj, "filter", GetWeakSourceName(_filter).c_str()); + obs_data_set_int(obj, "condition", static_cast(_condition)); + obs_data_set_string(obj, "settings", _settings.c_str()); + obs_data_set_bool(obj, "regex", _regex); + return true; +} + +bool MacroConditionFilter::Load(obs_data_t *obj) +{ + MacroCondition::Load(obj); + const char *sourceName = obs_data_get_string(obj, "source"); + _source = GetWeakSourceByName(sourceName); + const char *filterName = obs_data_get_string(obj, "filter"); + _filter = GetWeakFilterByQString(_source, filterName); + _condition = static_cast( + obs_data_get_int(obj, "condition")); + _settings = obs_data_get_string(obj, "settings"); + _regex = obs_data_get_bool(obj, "regex"); + return true; +} + +static inline void populateConditionSelection(QComboBox *list) +{ + for (auto entry : filterConditionTypes) { + list->addItem(obs_module_text(entry.second.c_str())); + } +} + +MacroConditionFilterEdit::MacroConditionFilterEdit( + QWidget *parent, std::shared_ptr entryData) + : QWidget(parent) +{ + _sources = new QComboBox(); + _filters = new QComboBox(); + _conditions = new QComboBox(); + _getSettings = new QPushButton(obs_module_text( + "AdvSceneSwitcher.condition.filter.getSettings")); + _settings = new QPlainTextEdit(); + _regex = new QCheckBox( + obs_module_text("AdvSceneSwitcher.condition.filter.regex")); + + populateConditionSelection(_conditions); + populateSourcesWithFilterSelection(_sources); + + QWidget::connect(_sources, SIGNAL(currentTextChanged(const QString &)), + this, SLOT(SourceChanged(const QString &))); + QWidget::connect(_filters, SIGNAL(currentTextChanged(const QString &)), + this, SLOT(FilterChanged(const QString &))); + QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this, + SLOT(ConditionChanged(int))); + 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))); + + QHBoxLayout *line1Layout = new QHBoxLayout; + QHBoxLayout *line2Layout = new QHBoxLayout; + QHBoxLayout *line3Layout = new QHBoxLayout; + std::unordered_map widgetPlaceholders = { + {"{{sources}}", _sources}, {"{{filters}}", _filters}, + {"{{conditions}}", _conditions}, {"{{settings}}", _settings}, + {"{{getSettings}}", _getSettings}, {"{{regex}}", _regex}, + }; + placeWidgets(obs_module_text( + "AdvSceneSwitcher.condition.filter.entry.line1"), + line1Layout, widgetPlaceholders); + placeWidgets(obs_module_text( + "AdvSceneSwitcher.condition.filter.entry.line2"), + line2Layout, widgetPlaceholders, false); + placeWidgets(obs_module_text( + "AdvSceneSwitcher.condition.filter.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 MacroConditionFilterEdit::SourceChanged(const QString &text) +{ + if (_loading || !_entryData) { + return; + } + { + std::lock_guard lock(switcher->m); + _entryData->_source = GetWeakSourceByQString(text); + } + _filters->clear(); + populateFilterSelection(_filters, _entryData->_source); +} + +void MacroConditionFilterEdit::FilterChanged(const QString &text) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_filter = GetWeakFilterByQString(_entryData->_source, text); +} + +void MacroConditionFilterEdit::ConditionChanged(int index) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_condition = static_cast(index); + SetSettingsSelectionVisible(_entryData->_condition == + FilterCondition::SETTINGS); +} + +void MacroConditionFilterEdit::GetSettingsClicked() +{ + if (_loading || !_entryData || !_entryData->_source) { + return; + } + + _settings->setPlainText( + QString::fromStdString(getSourceSettings(_entryData->_filter))); +} + +void MacroConditionFilterEdit::SettingsChanged() +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_settings = _settings->toPlainText().toStdString(); +} + +void MacroConditionFilterEdit::RegexChanged(int state) +{ + if (_loading || !_entryData) { + return; + } + + std::lock_guard lock(switcher->m); + _entryData->_regex = state; +} + +void MacroConditionFilterEdit::SetSettingsSelectionVisible(bool visible) +{ + _settings->setVisible(visible); + _getSettings->setVisible(visible); + _regex->setVisible(visible); + adjustSize(); +} + +void MacroConditionFilterEdit::UpdateEntryData() +{ + if (!_entryData) { + return; + } + + _sources->setCurrentText( + GetWeakSourceName(_entryData->_source).c_str()); + populateFilterSelection(_filters, _entryData->_source); + _filters->setCurrentText( + GetWeakSourceName(_entryData->_filter).c_str()); + _conditions->setCurrentIndex(static_cast(_entryData->_condition)); + _settings->setPlainText(QString::fromStdString(_entryData->_settings)); + _regex->setChecked(_entryData->_regex); + SetSettingsSelectionVisible(_entryData->_condition == + FilterCondition::SETTINGS); +}