diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 810d225d..8c152e56 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -934,6 +934,9 @@ AdvSceneSwitcher.action.source.type.deinterlaceOrder="Set deinterlace field orde AdvSceneSwitcher.action.source.type.openInteractionDialog="Open interaction dialog" AdvSceneSwitcher.action.source.type.openFilterDialog="Open filter dialog" AdvSceneSwitcher.action.source.type.openPropertiesDialog="Open properties dialog" +AdvSceneSwitcher.action.source.type.closeInteractionDialog="Close interaction dialog" +AdvSceneSwitcher.action.source.type.closeFilterDialog="Close filter dialog" +AdvSceneSwitcher.action.source.type.closePropertiesDialog="Close properties dialog" AdvSceneSwitcher.action.source.entry="{{actions}}{{sources}}{{settingsButtons}}{{deinterlaceMode}}{{deinterlaceOrder}}{{refresh}}" AdvSceneSwitcher.action.source.entry.settings="{{settings}}{{settingsInputMethod}}{{settingValue}}{{tempVar}}" AdvSceneSwitcher.action.source.warning="Warning: Enabling and disabling sources globally cannot be controlled by the OBS UI\nYou might be looking for \"Scene item visibility\"" @@ -955,6 +958,7 @@ AdvSceneSwitcher.action.source.inputMethod.individualTempvar="Set to macro prope AdvSceneSwitcher.action.source.inputMethod.json="Set setting JSON string" AdvSceneSwitcher.action.source.refresh="Refresh" AdvSceneSwitcher.action.source.refresh.tooltip="Repopulate the source settings selection with the settings of the source which's name matches the variable value." +AdvSceneSwitcher.action.source.dialog.accept="Accept changes" AdvSceneSwitcher.action.media="Media" AdvSceneSwitcher.action.media.type.play="Play" AdvSceneSwitcher.action.media.type.pause="Pause" diff --git a/plugins/base/macro-action-source.cpp b/plugins/base/macro-action-source.cpp index d16c32ea..b2e072fe 100644 --- a/plugins/base/macro-action-source.cpp +++ b/plugins/base/macro-action-source.cpp @@ -7,6 +7,8 @@ #include +#include + namespace advss { const std::string MacroActionSource::id = "source"; @@ -37,6 +39,12 @@ const static std::map actionTypes = { "AdvSceneSwitcher.action.source.type.openFilterDialog"}, {MacroActionSource::Action::OPEN_PROPERTIES_DIALOG, "AdvSceneSwitcher.action.source.type.openPropertiesDialog"}, + {MacroActionSource::Action::CLOSE_INTERACTION_DIALOG, + "AdvSceneSwitcher.action.source.type.closeInteractionDialog"}, + {MacroActionSource::Action::CLOSE_FILTER_DIALOG, + "AdvSceneSwitcher.action.source.type.closeFilterDialog"}, + {MacroActionSource::Action::CLOSE_PROPERTIES_DIALOG, + "AdvSceneSwitcher.action.source.type.closePropertiesDialog"}, }; const static std::map deinterlaceModes = { @@ -108,9 +116,81 @@ static bool isInteractable(obs_source_t *source) return (flags & OBS_SOURCE_INTERACTION) != 0; } +static void closeSourceDialog(obs_source_t *source, bool accept, + const char *className) +{ + if (!source) { + return; + } + + auto mainWindow = + reinterpret_cast(obs_frontend_get_main_window()); + if (!mainWindow) { + return; + } + + for (const auto widget : mainWindow->children()) { + auto dialog = qobject_cast(widget); + if (!dialog) { + continue; + } + + if (dialog->metaObject()->className() != QString(className)) { + continue; + } + + const char *name = obs_source_get_name(source); + if (!name) { + continue; + } + + if (!dialog->windowTitle().contains(QString(name))) { + continue; + } + + if (accept) { + dialog->accept(); + return; + } + + // dialog->reject() will ask for confirmation + // Try to avoid this by finding and pressing the cancel button + for (const auto widget : dialog->children()) { + auto buttons = qobject_cast(widget); + if (!buttons) { + continue; + } + + for (auto *button : buttons->buttons()) { + if (buttons->buttonRole(button) == + QDialogButtonBox::RejectRole) { + button->click(); + break; + } + } + } + } +} + +template void QueueUITaskLambda(F &&func) +{ + using FnType = std::decay_t; + auto *heapFunc = new FnType(std::forward(func)); + + QueueUITask( + [](void *param) { + std::unique_ptr fn( + static_cast(param)); + (*fn)(); + }, + heapFunc); +} + bool MacroActionSource::PerformAction() { - auto s = obs_weak_source_get_source(_source.GetSource()); + OBSSource s = obs_weak_source_get_source(_source.GetSource()); + obs_source_release(s); + switch (_action) { case Action::ENABLE: obs_source_set_enabled(s, true); @@ -172,10 +252,31 @@ bool MacroActionSource::PerformAction() case Action::OPEN_PROPERTIES_DIALOG: obs_frontend_open_source_properties(s); break; + case Action::CLOSE_INTERACTION_DIALOG: + if (!isInteractable(s)) { + // Nothing to do + break; + } + + QueueUITaskLambda([&]() { + closeSourceDialog(s, true, "OBSBasicInteraction"); + }); + break; + case Action::CLOSE_FILTER_DIALOG: + QueueUITaskLambda([&]() { + closeSourceDialog(s, _acceptDialog, "OBSBasicFilters"); + }); + break; + case Action::CLOSE_PROPERTIES_DIALOG: + QueueUITaskLambda([&]() { + closeSourceDialog(s, _acceptDialog, + "OBSBasicProperties"); + }); + break; default: break; } - obs_source_release(s); + return true; } @@ -207,6 +308,7 @@ bool MacroActionSource::Save(obs_data_t *obj) const static_cast(_deinterlaceMode)); obs_data_set_int(obj, "deinterlaceOrder", static_cast(_deinterlaceOrder)); + obs_data_set_bool(obj, "acceptDialog", _acceptDialog); return true; } @@ -231,6 +333,7 @@ bool MacroActionSource::Load(obs_data_t *obj) obs_data_get_int(obj, "deinterlaceMode")); _deinterlaceOrder = static_cast( obs_data_get_int(obj, "deinterlaceOrder")); + _acceptDialog = obs_data_get_bool(obj, "acceptDialog"); return true; } @@ -307,7 +410,9 @@ MacroActionSourceEdit::MacroActionSourceEdit( _warning(new QLabel( obs_module_text("AdvSceneSwitcher.action.source.warning"))), _refreshSettingSelection(new QPushButton( - obs_module_text("AdvSceneSwitcher.action.source.refresh"))) + obs_module_text("AdvSceneSwitcher.action.source.refresh"))), + _acceptDialog(new QCheckBox(obs_module_text( + "AdvSceneSwitcher.action.source.dialog.accept"))) { populateActionSelection(_actions); @@ -347,6 +452,8 @@ MacroActionSourceEdit::MacroActionSourceEdit( SLOT(SelectionChanged(const SourceSetting &))); QWidget::connect(_refreshSettingSelection, SIGNAL(clicked()), this, SLOT(RefreshVariableSourceSelectionValue())); + QWidget::connect(_acceptDialog, SIGNAL(stateChanged(int)), this, + SLOT(AcceptDialogChanged(int))); auto entryLayout = new QHBoxLayout; entryLayout->setContentsMargins(0, 0, 0, 0); @@ -374,6 +481,7 @@ MacroActionSourceEdit::MacroActionSourceEdit( mainLayout->addLayout(_settingsLayout); mainLayout->addWidget(_warning); mainLayout->addWidget(_settingsString); + mainLayout->addWidget(_acceptDialog); auto buttonLayout = new QHBoxLayout; buttonLayout->setContentsMargins(0, 0, 0, 0); buttonLayout->addWidget(_getSettings); @@ -406,6 +514,7 @@ void MacroActionSourceEdit::UpdateEntryData() static_cast(_entryData->_settingsInputMethod))); _tempVars->SetVariable(_entryData->_tempVar); _manualSettingValue->setPlainText(_entryData->_manualSettingValue); + _acceptDialog->setChecked(_entryData->_acceptDialog); SetWidgetVisibility(); } @@ -531,6 +640,12 @@ void MacroActionSourceEdit::RefreshVariableSourceSelectionValue() _sourceSettings->SetSource(_entryData->_source.GetSource()); } +void MacroActionSourceEdit::AcceptDialogChanged(int state) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_acceptDialog = state; +} + static QString GetIndividualListEntryName() { static const auto matchesInput = @@ -613,6 +728,12 @@ void MacroActionSourceEdit::SetWidgetVisibility() _entryData->_source.GetType() == SourceSelection::Type::VARIABLE); + _acceptDialog->setVisible( + _entryData->_action == + MacroActionSource::Action::CLOSE_FILTER_DIALOG || + _entryData->_action == + MacroActionSource::Action::CLOSE_PROPERTIES_DIALOG); + adjustSize(); updateGeometry(); } diff --git a/plugins/base/macro-action-source.hpp b/plugins/base/macro-action-source.hpp index 64543d33..8e17592a 100644 --- a/plugins/base/macro-action-source.hpp +++ b/plugins/base/macro-action-source.hpp @@ -5,9 +5,10 @@ #include "source-selection.hpp" #include "source-setting.hpp" +#include +#include #include #include -#include namespace advss { @@ -33,6 +34,7 @@ public: OBS_DEINTERLACE_FIELD_ORDER_TOP; TempVariableRef _tempVar; SourceSetting _setting; + bool _acceptDialog = false; enum class Action { ENABLE, @@ -45,6 +47,9 @@ public: OPEN_INTERACTION_DIALOG, OPEN_FILTER_DIALOG, OPEN_PROPERTIES_DIALOG, + CLOSE_INTERACTION_DIALOG, + CLOSE_FILTER_DIALOG, + CLOSE_PROPERTIES_DIALOG, }; Action _action = Action::SETTINGS; @@ -91,6 +96,7 @@ private slots: void SelectionChanged(const SourceSetting &); void ManualSettingsValueChanged(); void RefreshVariableSourceSelectionValue(); + void AcceptDialogChanged(int); signals: void HeaderInfoChanged(const QString &); @@ -112,6 +118,7 @@ private: QComboBox *_deinterlaceOrder; QLabel *_warning; QPushButton *_refreshSettingSelection; + QCheckBox *_acceptDialog; std::shared_ptr _entryData; bool _loading = true;