diff --git a/data/locale/de-DE.ini b/data/locale/de-DE.ini index 3812d4a9..e0816ebe 100644 --- a/data/locale/de-DE.ini +++ b/data/locale/de-DE.ini @@ -382,8 +382,8 @@ AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Warten, bis der Übergan AdvSceneSwitcher.action.wait="Warten" AdvSceneSwitcher.action.wait.type.fixed="fixe" AdvSceneSwitcher.action.wait.type.random="zufällig" -AdvSceneSwitcher.action.wait.entry.fixed="Warte für {{waitType}} Dauer von {{duration}}" -AdvSceneSwitcher.action.wait.entry.random="Warte für {{waitType}} Dauer von {{duration}} bis {{duration2}}" +AdvSceneSwitcher.action.wait.layout.fixed="Warte für {{waitType}} Dauer von {{duration}}" +AdvSceneSwitcher.action.wait.layout.random="Warte für {{waitType}} Dauer von {{duration}} bis {{duration2}}" AdvSceneSwitcher.action.audio="Audio" AdvSceneSwitcher.action.audio.type.mute="Stummgeschaltet" AdvSceneSwitcher.action.audio.type.unmute="Nicht stummgeschaltet" diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index b87ff2eb..35fb926a 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -899,8 +899,18 @@ AdvSceneSwitcher.action.scene.canvasNotSupported="Not supported by the currently AdvSceneSwitcher.action.wait="Wait" AdvSceneSwitcher.action.wait.type.fixed="fixed" AdvSceneSwitcher.action.wait.type.random="random" -AdvSceneSwitcher.action.wait.entry.fixed="Wait for{{waitType}}duration of{{duration}}" -AdvSceneSwitcher.action.wait.entry.random="Wait for{{waitType}}duration from{{duration}}to{{duration2}}" +AdvSceneSwitcher.action.wait.type.variableCondition="variable" +AdvSceneSwitcher.action.wait.layout.fixed="Wait for{{waitType}}duration of{{duration}}" +AdvSceneSwitcher.action.wait.layout.random="Wait for{{waitType}}duration from{{duration}}to{{duration2}}" +AdvSceneSwitcher.action.wait.condition.equals="equals" +AdvSceneSwitcher.action.wait.condition.doesNotEqual="does not equal" +AdvSceneSwitcher.action.wait.condition.isEmpty="is empty" +AdvSceneSwitcher.action.wait.condition.lessThan="is less than" +AdvSceneSwitcher.action.wait.condition.greaterThan="is greater than" +AdvSceneSwitcher.action.wait.layout.variableCondition="Wait until{{waitType}}{{variable}}{{condition}}{{strValue}}{{numValue}}{{regex}}" +AdvSceneSwitcher.action.wait.layout.timeout="Timeout:{{useTimeout}}{{conditionTimeout}}" +AdvSceneSwitcher.tempVar.wait.timedOut="Timed out" +AdvSceneSwitcher.tempVar.wait.timedOut.description="True if the wait action timed out before the condition was met, false if the condition was met in time" AdvSceneSwitcher.action.audio="Audio" AdvSceneSwitcher.action.audio.type.mute="Mute" AdvSceneSwitcher.action.audio.type.unmute="Unmute" diff --git a/data/locale/es-ES.ini b/data/locale/es-ES.ini index d7d7a49f..ed356e5f 100644 --- a/data/locale/es-ES.ini +++ b/data/locale/es-ES.ini @@ -312,8 +312,8 @@ AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Espere hasta que se comp AdvSceneSwitcher.action.wait="Esperar" AdvSceneSwitcher.action.wait.type.fixed="fijo" AdvSceneSwitcher.action.wait.type.random="aleatorio" -AdvSceneSwitcher.action.wait.entry.fixed="Espere {{waitType}} de {{duration}}" -AdvSceneSwitcher.action.wait.entry.random="Espere {{waitType}} de {{duration}} a {{duration2}}" +AdvSceneSwitcher.action.wait.layout.fixed="Espere {{waitType}} de {{duration}}" +AdvSceneSwitcher.action.wait.layout.random="Espere {{waitType}} de {{duration}} a {{duration2}}" AdvSceneSwitcher.action.audio="Audio" AdvSceneSwitcher.action.audio.type.mute="Silencio" AdvSceneSwitcher.action.audio.type.unmute="Activar silencio" diff --git a/data/locale/fr-FR.ini b/data/locale/fr-FR.ini index 7c53f311..49e35f65 100644 --- a/data/locale/fr-FR.ini +++ b/data/locale/fr-FR.ini @@ -460,8 +460,8 @@ AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Attendre que la transiti AdvSceneSwitcher.action.wait="Attendre" AdvSceneSwitcher.action.wait.type.fixed="fixe" AdvSceneSwitcher.action.wait.type.random="aléatoire" -AdvSceneSwitcher.action.wait.entry.fixed="Attendre pour{{waitType}}une durée de{{duration}}" -AdvSceneSwitcher.action.wait.entry.random="Attendre pour{{waitType}}une durée de{{duration}}à{{duration2}}" +AdvSceneSwitcher.action.wait.layout.fixed="Attendre pour{{waitType}}une durée de{{duration}}" +AdvSceneSwitcher.action.wait.layout.random="Attendre pour{{waitType}}une durée de{{duration}}à{{duration2}}" AdvSceneSwitcher.action.audio="Audio" AdvSceneSwitcher.action.audio.type.mute="Muet" AdvSceneSwitcher.action.audio.type.unmute="Activer le son" diff --git a/data/locale/ja-JP.ini b/data/locale/ja-JP.ini index 1aa60f09..ee9629bf 100644 --- a/data/locale/ja-JP.ini +++ b/data/locale/ja-JP.ini @@ -800,8 +800,8 @@ AdvSceneSwitcher.action.scene.blockUntilTransitionDone="目的のシーンへの ; AdvSceneSwitcher.action.wait="Wait" AdvSceneSwitcher.action.wait.type.fixed="指定" AdvSceneSwitcher.action.wait.type.random="ランダム" -AdvSceneSwitcher.action.wait.entry.fixed="{{waitType}}期間{{duration}}待機します" -AdvSceneSwitcher.action.wait.entry.random="{{duration}}から{{duration2}}までの{{waitType}}期間待ちます" +AdvSceneSwitcher.action.wait.layout.fixed="{{waitType}}期間{{duration}}待機します" +AdvSceneSwitcher.action.wait.layout.random="{{duration}}から{{duration2}}までの{{waitType}}期間待ちます" AdvSceneSwitcher.action.audio="音声" AdvSceneSwitcher.action.audio.type.mute="ミュート" AdvSceneSwitcher.action.audio.type.unmute="ミュート解除" diff --git a/data/locale/pt-BR.ini b/data/locale/pt-BR.ini index 2fc4f7c9..a9774121 100644 --- a/data/locale/pt-BR.ini +++ b/data/locale/pt-BR.ini @@ -718,8 +718,8 @@ AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Aguardar até que a tran AdvSceneSwitcher.action.wait="Aguardar" AdvSceneSwitcher.action.wait.type.fixed="fixo" AdvSceneSwitcher.action.wait.type.random="aleatório" -AdvSceneSwitcher.action.wait.entry.fixed="Aguardando por{{waitType}}duração de{{duration}}" -AdvSceneSwitcher.action.wait.entry.random="Aguardando por{{waitType}}duração de{{duration}}para{{duration2}}" +AdvSceneSwitcher.action.wait.layout.fixed="Aguardando por{{waitType}}duração de{{duration}}" +AdvSceneSwitcher.action.wait.layout.random="Aguardando por{{waitType}}duração de{{duration}}para{{duration2}}" AdvSceneSwitcher.action.audio="Áudio" AdvSceneSwitcher.action.audio.type.mute="Silenciar" AdvSceneSwitcher.action.audio.type.unmute="Ativar som" diff --git a/data/locale/ru-RU.ini b/data/locale/ru-RU.ini index 18ace96f..a9607983 100644 --- a/data/locale/ru-RU.ini +++ b/data/locale/ru-RU.ini @@ -114,8 +114,8 @@ AdvSceneSwitcher.action.scene="Переключить сцену" AdvSceneSwitcher.action.wait="Подождать" AdvSceneSwitcher.action.wait.type.fixed="фиксированный" AdvSceneSwitcher.action.wait.type.random="случайный" -AdvSceneSwitcher.action.wait.entry.fixed="Ожидать {{waitType}} в течении {{duration}}" -AdvSceneSwitcher.action.wait.entry.random="Ожидание {{waitType}} длительностью от {{duration}} до {{duration2}}" +AdvSceneSwitcher.action.wait.layout.fixed="Ожидать {{waitType}} в течении {{duration}}" +AdvSceneSwitcher.action.wait.layout.random="Ожидание {{waitType}} длительностью от {{duration}} до {{duration2}}" AdvSceneSwitcher.action.audio="Аудио" AdvSceneSwitcher.action.audio.type.mute="Замьютить" AdvSceneSwitcher.action.audio.type.unmute="Размьютить" diff --git a/data/locale/tr-TR.ini b/data/locale/tr-TR.ini index fd1310ea..961b89f2 100644 --- a/data/locale/tr-TR.ini +++ b/data/locale/tr-TR.ini @@ -249,8 +249,8 @@ AdvSceneSwitcher.action.scene.blockUntilTransitionDone="Hedef sahneye geçiş ta AdvSceneSwitcher.action.wait="Bekle" AdvSceneSwitcher.action.wait.type.fixed="sabit" AdvSceneSwitcher.action.wait.type.random="rastgele" -AdvSceneSwitcher.action.wait.entry.fixed="Şunu bekle {{waitType}} süre {{duration}}" -AdvSceneSwitcher.action.wait.entry.random="Şunu bekle {{waitType}} bu aralıktan {{duration}} buna {{duration2}}" +AdvSceneSwitcher.action.wait.layout.fixed="Şunu bekle {{waitType}} süre {{duration}}" +AdvSceneSwitcher.action.wait.layout.random="Şunu bekle {{waitType}} bu aralıktan {{duration}} buna {{duration2}}" AdvSceneSwitcher.action.audio="Ses" AdvSceneSwitcher.action.audio.type.mute="Sessiz" AdvSceneSwitcher.action.audio.type.unmute="Ses açmak" diff --git a/data/locale/zh-CN.ini b/data/locale/zh-CN.ini index 84a78454..c6567f94 100644 --- a/data/locale/zh-CN.ini +++ b/data/locale/zh-CN.ini @@ -778,8 +778,8 @@ AdvSceneSwitcher.action.scene.blockUntilTransitionDone="等待转场到目标场 AdvSceneSwitcher.action.wait="等待" AdvSceneSwitcher.action.wait.type.fixed="固定" AdvSceneSwitcher.action.wait.type.random="随机" -AdvSceneSwitcher.action.wait.entry.fixed="等待时间{{waitType}}{{duration}}" -AdvSceneSwitcher.action.wait.entry.random="等待时间{{waitType}}{{duration}} 到 {{duration2}}" +AdvSceneSwitcher.action.wait.layout.fixed="等待时间{{waitType}}{{duration}}" +AdvSceneSwitcher.action.wait.layout.random="等待时间{{waitType}}{{duration}} 到 {{duration2}}" AdvSceneSwitcher.action.audio="音频" AdvSceneSwitcher.action.audio.type.mute="静音" AdvSceneSwitcher.action.audio.type.unmute="取消静音" diff --git a/plugins/base/macro-action-wait.cpp b/plugins/base/macro-action-wait.cpp index 10d3541b..112438f1 100644 --- a/plugins/base/macro-action-wait.cpp +++ b/plugins/base/macro-action-wait.cpp @@ -19,6 +19,21 @@ static const std::map waitTypes = { "AdvSceneSwitcher.action.wait.type.fixed"}, {MacroActionWait::Type::RANDOM, "AdvSceneSwitcher.action.wait.type.random"}, + {MacroActionWait::Type::VARIABLE_WAIT, + "AdvSceneSwitcher.action.wait.type.variableCondition"}, +}; + +static const std::map conditionTypes = { + {MacroActionWait::Condition::EQUALS, + "AdvSceneSwitcher.action.wait.condition.equals"}, + {MacroActionWait::Condition::DOES_NOT_EQUAL, + "AdvSceneSwitcher.action.wait.condition.doesNotEqual"}, + {MacroActionWait::Condition::IS_EMPTY, + "AdvSceneSwitcher.action.wait.condition.isEmpty"}, + {MacroActionWait::Condition::LESS_THAN, + "AdvSceneSwitcher.action.wait.condition.lessThan"}, + {MacroActionWait::Condition::GREATER_THAN, + "AdvSceneSwitcher.action.wait.condition.greaterThan"}, }; static std::random_device rd; @@ -35,7 +50,52 @@ static void waitHelper(std::unique_lock *lock, Macro *macro, } } -bool MacroActionWait::PerformAction() +void MacroActionWait::SetupTempVars() +{ + MacroAction::SetupTempVars(); + if (_waitType != Type::VARIABLE_WAIT) { + return; + } + AddTempvar( + "timedOut", + obs_module_text("AdvSceneSwitcher.tempVar.wait.timedOut"), + obs_module_text( + "AdvSceneSwitcher.tempVar.wait.timedOut.description")); +} + +bool MacroActionWait::ConditionIsMet() const +{ + auto var = _variable.lock(); + if (!var) { + return false; + } + + switch (_condition) { + case Condition::EQUALS: + if (_regex.Enabled()) { + return _regex.Matches(var->Value(), _strValue); + } + return var->Value() == std::string(_strValue); + case Condition::DOES_NOT_EQUAL: + if (_regex.Enabled()) { + return !_regex.Matches(var->Value(), _strValue); + } + return var->Value() != std::string(_strValue); + case Condition::IS_EMPTY: + return var->Value().empty(); + case Condition::LESS_THAN: { + auto val = var->DoubleValue(); + return val.has_value() && *val < (double)_numValue; + } + case Condition::GREATER_THAN: { + auto val = var->DoubleValue(); + return val.has_value() && *val > (double)_numValue; + } + } + return false; +} + +bool MacroActionWait::PerformDurationWait() { double sleepDuration; if (_waitType == Type::FIXED) { @@ -66,20 +126,71 @@ bool MacroActionWait::PerformAction() return !MacroWaitShouldAbort(); } +bool MacroActionWait::PerformVariableWait() +{ + auto var = _variable.lock(); + if (!var) { + return true; + } + + auto deadline = + _useTimeout + ? std::chrono::high_resolution_clock::now() + + std::chrono::milliseconds(( + int)(_conditionTimeout.Milliseconds())) + : std::chrono::high_resolution_clock::time_point::max(); + + SetMacroAbortWait(false); + SuspendLock suspendLock(*this); + auto cvLock = var->GetCVLock(); + + while (!MacroWaitShouldAbort() && !MacroIsStopped(GetMacro())) { + if (ConditionIsMet()) { + SetTempVarValue("timedOut", false); + return true; + } + auto now = std::chrono::high_resolution_clock::now(); + if (now >= deadline) { + SetTempVarValue("timedOut", true); + return true; + } + auto wakeAt = + std::min(now + std::chrono::milliseconds(50), deadline); + var->GetCV().wait_until(cvLock, wakeAt); + } + return !MacroWaitShouldAbort(); +} + +bool MacroActionWait::PerformAction() +{ + if (_waitType == Type::VARIABLE_WAIT) { + return PerformVariableWait(); + } + return PerformDurationWait(); +} + bool MacroActionWait::Save(obs_data_t *obj) const { MacroAction::Save(obj); - _duration.Save(obj); - _duration2.Save(obj, "duration2"); obs_data_set_int(obj, "waitType", static_cast(_waitType)); obs_data_set_int(obj, "version", 1); + _duration.Save(obj); + _duration2.Save(obj, "duration2"); + obs_data_set_string(obj, "variableName", + GetWeakVariableName(_variable).c_str()); + obs_data_set_int(obj, "condition", static_cast(_condition)); + _strValue.Save(obj, "strValue"); + _numValue.Save(obj, "numValue"); + _regex.Save(obj); + obs_data_set_bool(obj, "useTimeout", _useTimeout); + _conditionTimeout.Save(obj, "conditionTimeout"); return true; } bool MacroActionWait::Load(obs_data_t *obj) { MacroAction::Load(obj); - _duration.Load(obj); + _waitType = static_cast(obs_data_get_int(obj, "waitType")); // TODO: remove this fallback if (obs_data_get_int(obj, "version") == 1) { _duration2.Load(obj, "duration2"); @@ -88,12 +199,23 @@ bool MacroActionWait::Load(obs_data_t *obj) _duration2.SetUnit(static_cast( obs_data_get_int(obj, "displayUnit2"))); } - _waitType = static_cast(obs_data_get_int(obj, "waitType")); + _duration.Load(obj); + _variable = + GetWeakVariableByName(obs_data_get_string(obj, "variableName")); + _condition = static_cast(obs_data_get_int(obj, "condition")); + _strValue.Load(obj, "strValue"); + _numValue.Load(obj, "numValue"); + _regex.Load(obj); + _useTimeout = obs_data_get_bool(obj, "useTimeout"); + _conditionTimeout.Load(obj, "conditionTimeout"); return true; } std::string MacroActionWait::GetShortDesc() const { + if (_waitType == Type::VARIABLE_WAIT) { + return GetWeakVariableName(_variable); + } if (_waitType == Type::FIXED) { return _duration.ToString(); } @@ -114,12 +236,18 @@ void MacroActionWait::ResolveVariablesToFixedValues() { _duration.ResolveVariables(); _duration2.ResolveVariables(); + _strValue.ResolveVariables(); + _numValue.ResolveVariables(); + _conditionTimeout.ResolveVariables(); } -static inline void populateTypeSelection(QComboBox *list) +template +static inline void populateSelection(QComboBox *list, + const std::map &options) { - for (const auto &[_, name] : waitTypes) { - list->addItem(obs_module_text(name.c_str())); + for (const auto &[value, name] : options) { + list->addItem(obs_module_text(name.c_str()), + static_cast(value)); } } @@ -128,10 +256,21 @@ MacroActionWaitEdit::MacroActionWaitEdit( : QWidget(parent), _duration(new DurationSelection()), _duration2(new DurationSelection()), + _variable(new VariableSelection(this)), + _variableCondition(new QComboBox()), + _strValue(new VariableTextEdit(this, 5, 1, 1)), + _numValue(new VariableDoubleSpinBox()), + _regex(new RegexConfigWidget(parent)), + _useTimeout(new QCheckBox()), + _conditionTimeout(new DurationSelection()), _waitType(new QComboBox()), - _mainLayout(new QHBoxLayout()) + _mainLayout(new QHBoxLayout()), + _timeoutLayout(new QHBoxLayout()) { - populateTypeSelection(_waitType); + _numValue->setMinimum(-9999999999); + _numValue->setMaximum(9999999999); + populateSelection(_waitType, waitTypes); + populateSelection(_variableCondition, conditionTypes); QWidget::connect(_duration, SIGNAL(DurationChanged(const Duration &)), this, SLOT(DurationChanged(const Duration &))); @@ -139,8 +278,37 @@ MacroActionWaitEdit::MacroActionWaitEdit( this, SLOT(Duration2Changed(const Duration &))); QWidget::connect(_waitType, SIGNAL(currentIndexChanged(int)), this, SLOT(TypeChanged(int))); + QWidget::connect(_variable, SIGNAL(SelectionChanged(const QString &)), + this, SLOT(VariableChanged(const QString &))); + QWidget::connect(_variableCondition, SIGNAL(currentIndexChanged(int)), + this, SLOT(ConditionChanged(int))); + QWidget::connect(_strValue, SIGNAL(textChanged()), this, + SLOT(StrValueChanged())); + QWidget::connect( + _numValue, + SIGNAL(NumberVariableChanged(const NumberVariable &)), + this, SLOT(NumValueChanged(const NumberVariable &))); + QWidget::connect(_regex, + SIGNAL(RegexConfigChanged(const RegexConfig &)), this, + SLOT(RegexChanged(const RegexConfig &))); + QWidget::connect(_useTimeout, SIGNAL(stateChanged(int)), this, + SLOT(UseTimeoutChanged(int))); + QWidget::connect(_conditionTimeout, + SIGNAL(DurationChanged(const Duration &)), this, + SLOT(ConditionTimeoutChanged(const Duration &))); - setLayout(_mainLayout); + PlaceWidgets( + obs_module_text("AdvSceneSwitcher.action.wait.layout.timeout"), + _timeoutLayout, + { + {"{{useTimeout}}", _useTimeout}, + {"{{conditionTimeout}}", _conditionTimeout}, + }); + + auto *outerLayout = new QVBoxLayout(); + outerLayout->addLayout(_mainLayout); + outerLayout->addLayout(_timeoutLayout); + setLayout(outerLayout); _entryData = entryData; UpdateEntryData(); @@ -153,15 +321,30 @@ void MacroActionWaitEdit::UpdateEntryData() return; } - if (_entryData->_waitType == MacroActionWait::Type::FIXED) { + _waitType->setCurrentIndex(static_cast(_entryData->_waitType)); + + switch (_entryData->_waitType) { + case MacroActionWait::Type::FIXED: SetupFixedDurationEdit(); - } else { + break; + case MacroActionWait::Type::RANDOM: SetupRandomDurationEdit(); + break; + case MacroActionWait::Type::VARIABLE_WAIT: + SetupVariableWaitEdit(); + break; } _duration->SetDuration(_entryData->_duration); _duration2->SetDuration(_entryData->_duration2); - _waitType->setCurrentIndex(static_cast(_entryData->_waitType)); + _variable->SetVariable(_entryData->_variable); + _variableCondition->setCurrentIndex( + static_cast(_entryData->_condition)); + _strValue->setPlainText(_entryData->_strValue); + _numValue->SetValue(_entryData->_numValue); + _regex->SetRegexConfig(_entryData->_regex); + _useTimeout->setChecked(_entryData->_useTimeout); + _conditionTimeout->SetDuration(_entryData->_conditionTimeout); } void MacroActionWaitEdit::SetupFixedDurationEdit() @@ -169,15 +352,24 @@ void MacroActionWaitEdit::SetupFixedDurationEdit() _mainLayout->removeWidget(_duration); _mainLayout->removeWidget(_duration2); _mainLayout->removeWidget(_waitType); + _mainLayout->removeWidget(_variable); + _mainLayout->removeWidget(_variableCondition); + _mainLayout->removeWidget(_strValue); + _mainLayout->removeWidget(_numValue); + _mainLayout->removeWidget(_regex); ClearLayout(_mainLayout); - std::unordered_map widgetPlaceholders = { - {"{{duration}}", _duration}, - {"{{waitType}}", _waitType}, - }; PlaceWidgets( - obs_module_text("AdvSceneSwitcher.action.wait.entry.fixed"), - _mainLayout, widgetPlaceholders); + obs_module_text("AdvSceneSwitcher.action.wait.layout.fixed"), + _mainLayout, + {{"{{duration}}", _duration}, {"{{waitType}}", _waitType}}); + _duration->show(); _duration2->hide(); + _variable->hide(); + _variableCondition->hide(); + _strValue->hide(); + _numValue->hide(); + _regex->hide(); + SetLayoutVisible(_timeoutLayout, false); } void MacroActionWaitEdit::SetupRandomDurationEdit() @@ -185,30 +377,99 @@ void MacroActionWaitEdit::SetupRandomDurationEdit() _mainLayout->removeWidget(_duration); _mainLayout->removeWidget(_duration2); _mainLayout->removeWidget(_waitType); + _mainLayout->removeWidget(_variable); + _mainLayout->removeWidget(_variableCondition); + _mainLayout->removeWidget(_strValue); + _mainLayout->removeWidget(_numValue); + _mainLayout->removeWidget(_regex); ClearLayout(_mainLayout); - std::unordered_map widgetPlaceholders = { - {"{{duration}}", _duration}, - {"{{duration2}}", _duration2}, - {"{{waitType}}", _waitType}, - }; PlaceWidgets( - obs_module_text("AdvSceneSwitcher.action.wait.entry.random"), - _mainLayout, widgetPlaceholders); + obs_module_text("AdvSceneSwitcher.action.wait.layout.random"), + _mainLayout, + {{"{{duration}}", _duration}, + {"{{duration2}}", _duration2}, + {"{{waitType}}", _waitType}}); + _duration->show(); _duration2->show(); + _variable->hide(); + _variableCondition->hide(); + _strValue->hide(); + _numValue->hide(); + _regex->hide(); + SetLayoutVisible(_timeoutLayout, false); } -void MacroActionWaitEdit::TypeChanged(int value) +void MacroActionWaitEdit::SetupVariableWaitEdit() +{ + _mainLayout->removeWidget(_duration); + _mainLayout->removeWidget(_duration2); + _mainLayout->removeWidget(_waitType); + _mainLayout->removeWidget(_variable); + _mainLayout->removeWidget(_variableCondition); + _mainLayout->removeWidget(_strValue); + _mainLayout->removeWidget(_numValue); + _mainLayout->removeWidget(_regex); + ClearLayout(_mainLayout); + std::unordered_map widgetPlaceholders = { + {"{{waitType}}", _waitType}, + {"{{variable}}", _variable}, + {"{{condition}}", _variableCondition}, + {"{{strValue}}", _strValue}, + {"{{numValue}}", _numValue}, + {"{{regex}}", _regex}, + }; + PlaceWidgets( + obs_module_text( + "AdvSceneSwitcher.action.wait.layout.variableCondition"), + _mainLayout, widgetPlaceholders); + _duration->hide(); + _duration2->hide(); + _variable->show(); + _variableCondition->show(); + SetLayoutVisible(_timeoutLayout, true); + SetVariableWaitWidgetVisibility(); +} + +void MacroActionWaitEdit::SetVariableWaitWidgetVisibility() +{ + if (!_entryData) { + return; + } + + const auto cond = _entryData->_condition; + const bool isStringComparison = + cond == MacroActionWait::Condition::EQUALS || + cond == MacroActionWait::Condition::DOES_NOT_EQUAL; + _strValue->setVisible(isStringComparison); + _regex->setVisible(isStringComparison); + _numValue->setVisible(cond == MacroActionWait::Condition::LESS_THAN || + cond == MacroActionWait::Condition::GREATER_THAN); + _conditionTimeout->setVisible(_entryData->_useTimeout); + + adjustSize(); + updateGeometry(); +} + +void MacroActionWaitEdit::TypeChanged(int idx) { GUARD_LOADING_AND_LOCK(); - auto type = static_cast(value); + auto type = static_cast( + _waitType->itemData(idx).toInt()); - if (type == MacroActionWait::Type::FIXED) { + switch (type) { + case MacroActionWait::Type::FIXED: SetupFixedDurationEdit(); - } else { + break; + case MacroActionWait::Type::RANDOM: SetupRandomDurationEdit(); + break; + case MacroActionWait::Type::VARIABLE_WAIT: + SetupVariableWaitEdit(); + break; } _entryData->_waitType = type; + _entryData->SetupTempVars(); emit HeaderInfoChanged( QString::fromStdString(_entryData->GetShortDesc())); } @@ -229,4 +490,55 @@ void MacroActionWaitEdit::Duration2Changed(const Duration &dur) QString::fromStdString(_entryData->GetShortDesc())); } +void MacroActionWaitEdit::VariableChanged(const QString &text) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_variable = GetWeakVariableByQString(text); + emit HeaderInfoChanged( + QString::fromStdString(_entryData->GetShortDesc())); +} + +void MacroActionWaitEdit::ConditionChanged(int idx) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_condition = static_cast( + _variableCondition->itemData(idx).toInt()); + SetVariableWaitWidgetVisibility(); +} + +void MacroActionWaitEdit::StrValueChanged() +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_strValue = _strValue->toPlainText().toStdString(); + adjustSize(); + updateGeometry(); +} + +void MacroActionWaitEdit::NumValueChanged(const NumberVariable &val) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_numValue = val; +} + +void MacroActionWaitEdit::RegexChanged(const RegexConfig &conf) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_regex = conf; + adjustSize(); + updateGeometry(); +} + +void MacroActionWaitEdit::UseTimeoutChanged(int state) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_useTimeout = state != Qt::Unchecked; + SetVariableWaitWidgetVisibility(); +} + +void MacroActionWaitEdit::ConditionTimeoutChanged(const Duration &dur) +{ + GUARD_LOADING_AND_LOCK(); + _entryData->_conditionTimeout = dur; +} + } // namespace advss diff --git a/plugins/base/macro-action-wait.hpp b/plugins/base/macro-action-wait.hpp index 0412b857..71276d3d 100644 --- a/plugins/base/macro-action-wait.hpp +++ b/plugins/base/macro-action-wait.hpp @@ -1,8 +1,14 @@ #pragma once #include "macro-action-edit.hpp" #include "duration-control.hpp" +#include "regex-config.hpp" +#include "variable-spinbox.hpp" +#include "variable-text-edit.hpp" +#include "variable.hpp" +#include #include +#include namespace advss { @@ -17,17 +23,40 @@ public: static std::shared_ptr Create(Macro *m); std::shared_ptr Copy() const; void ResolveVariablesToFixedValues(); - - Duration _duration; - Duration _duration2; + void SetupTempVars(); enum class Type { FIXED, RANDOM, + VARIABLE_WAIT, }; Type _waitType = Type::FIXED; + // FIXED / RANDOM + Duration _duration; + Duration _duration2; + + // VARIABLE_WAIT + enum class Condition { + EQUALS, + DOES_NOT_EQUAL, + IS_EMPTY, + LESS_THAN, + GREATER_THAN, + }; + std::weak_ptr _variable; + Condition _condition = Condition::EQUALS; + StringVariable _strValue = ""; + DoubleVariable _numValue = 0.0; + RegexConfig _regex; + bool _useTimeout = false; + Duration _conditionTimeout; + private: + bool PerformDurationWait(); + bool PerformVariableWait(); + bool ConditionIsMet() const; + static bool _registered; static const std::string id; }; @@ -42,6 +71,7 @@ public: void UpdateEntryData(); void SetupFixedDurationEdit(); void SetupRandomDurationEdit(); + void SetupVariableWaitEdit(); static QWidget *Create(QWidget *parent, std::shared_ptr action) { @@ -54,15 +84,36 @@ private slots: void DurationChanged(const Duration &value); void Duration2Changed(const Duration &value); void TypeChanged(int value); + void VariableChanged(const QString &); + void ConditionChanged(int); + void StrValueChanged(); + void NumValueChanged(const NumberVariable &); + void RegexChanged(const RegexConfig &); + void UseTimeoutChanged(int); + void ConditionTimeoutChanged(const Duration &); signals: void HeaderInfoChanged(const QString &); private: + void SetVariableWaitWidgetVisibility(); + + // Duration widgets (FIXED / RANDOM) DurationSelection *_duration; DurationSelection *_duration2; + + // Variable condition widgets (VARIABLE_WAIT) + VariableSelection *_variable; + QComboBox *_variableCondition; + VariableTextEdit *_strValue; + VariableDoubleSpinBox *_numValue; + RegexConfigWidget *_regex; + QCheckBox *_useTimeout; + DurationSelection *_conditionTimeout; + QComboBox *_waitType; QHBoxLayout *_mainLayout; + QHBoxLayout *_timeoutLayout; std::shared_ptr _entryData; bool _loading = true;