mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-03-21 17:34:57 -05:00
Add option for short circuit evaluation of conditions
This commit is contained in:
parent
0906c6bb3f
commit
c7c48d03e9
|
|
@ -194,7 +194,10 @@ AdvSceneSwitcher.macroTab.highlightExecutedMacros="Highlight recently executed m
|
|||
AdvSceneSwitcher.macroTab.highlightTrueConditions="Highlight conditions of currently selected macro that evaluated to true recently"
|
||||
AdvSceneSwitcher.macroTab.highlightPerformedActions="Highlight recently performed actions of currently selected macro"
|
||||
AdvSceneSwitcher.macroTab.newMacroRegisterHotkey="Register hotkeys to control the pause state of new macros"
|
||||
AdvSceneSwitcher.macroTab.newMacroUseShortCircuitEvaluation="Enable short circuit evaluation of macro conditions for new macros"
|
||||
AdvSceneSwitcher.macroTab.currentDisableHotkeys="Register hotkeys to control the pause state of selected macro"
|
||||
AdvSceneSwitcher.macroTab.currentUseShortCircuitEvaluation="Enable short circuit evaluation of macro conditions for currently selected macro"
|
||||
AdvSceneSwitcher.macroTab.shortCircuit.tooltip="Enabling short circuit evaluation might improve the performance, as some condition checks are skipped, if the overall macro cannot be evaluated to \"true\" anymore.\nHowever, please note that condition checks, which are skipped over, will also not update their duration modifier checks."
|
||||
AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup="Skip execution of actions of current macro on startup"
|
||||
AdvSceneSwitcher.macroTab.currentStopActionsIfNotDone="Stop and rerun actions of the currently selected macro, if the actions are still running, when a new execution is triggered"
|
||||
AdvSceneSwitcher.macroTab.currentRegisterDock="Register dock widget to control the pause state of selected macro or run it manually"
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ void GlobalMacroSettings::Save(obs_data_t *obj) const
|
|||
obs_data_set_bool(data, "highlightActions", _highlightActions);
|
||||
obs_data_set_bool(data, "newMacroRegisterHotkey",
|
||||
_newMacroRegisterHotkeys);
|
||||
obs_data_set_bool(data, "newMacroUseShortCircuitEvaluation",
|
||||
_newMacroUseShortCircuitEvaluation);
|
||||
obs_data_set_obj(obj, "macroSettings", data);
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
|
@ -38,6 +40,8 @@ void GlobalMacroSettings::Load(obs_data_t *obj)
|
|||
_highlightActions = obs_data_get_bool(data, "highlightActions");
|
||||
_newMacroRegisterHotkeys =
|
||||
obs_data_get_bool(data, "newMacroRegisterHotkey");
|
||||
_newMacroUseShortCircuitEvaluation =
|
||||
obs_data_get_bool(data, "newMacroUseShortCircuitEvaluation");
|
||||
obs_data_release(data);
|
||||
}
|
||||
|
||||
|
|
@ -45,16 +49,20 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
|
|||
const GlobalMacroSettings &settings,
|
||||
Macro *macro)
|
||||
: QDialog(parent),
|
||||
_executed(new QCheckBox(obs_module_text(
|
||||
_highlightExecutedMacros(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.highlightExecutedMacros"))),
|
||||
_conditions(new QCheckBox(obs_module_text(
|
||||
_highlightConditions(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.highlightTrueConditions"))),
|
||||
_actions(new QCheckBox(obs_module_text(
|
||||
_highlightActions(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.highlightPerformedActions"))),
|
||||
_newMacroRegisterHotkeys(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.newMacroRegisterHotkey"))),
|
||||
_newMacroUseShortCircuitEvaluation(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.newMacroUseShortCircuitEvaluation"))),
|
||||
_currentMacroRegisterHotkeys(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentDisableHotkeys"))),
|
||||
_currentUseShortCircuitEvaluation(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentUseShortCircuitEvaluation"))),
|
||||
_currentSkipOnStartup(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.currentSkipExecutionOnStartup"))),
|
||||
_currentStopActionsIfNotDone(new QCheckBox(obs_module_text(
|
||||
|
|
@ -83,12 +91,17 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
|
|||
setWindowModality(Qt::WindowModality::WindowModal);
|
||||
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||
|
||||
_newMacroUseShortCircuitEvaluation->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.shortCircuit.tooltip"));
|
||||
_currentUseShortCircuitEvaluation->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.shortCircuit.tooltip"));
|
||||
|
||||
auto highlightOptions = new QGroupBox(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.highlightSettings"));
|
||||
auto highlightLayout = new QVBoxLayout;
|
||||
highlightLayout->addWidget(_executed);
|
||||
highlightLayout->addWidget(_conditions);
|
||||
highlightLayout->addWidget(_actions);
|
||||
highlightLayout->addWidget(_highlightExecutedMacros);
|
||||
highlightLayout->addWidget(_highlightConditions);
|
||||
highlightLayout->addWidget(_highlightActions);
|
||||
highlightOptions->setLayout(highlightLayout);
|
||||
|
||||
auto hotkeyOptions = new QGroupBox(
|
||||
|
|
@ -103,6 +116,8 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
|
|||
auto generalLayout = new QVBoxLayout;
|
||||
generalLayout->addWidget(_currentSkipOnStartup);
|
||||
generalLayout->addWidget(_currentStopActionsIfNotDone);
|
||||
generalLayout->addWidget(_currentUseShortCircuitEvaluation);
|
||||
generalLayout->addWidget(_newMacroUseShortCircuitEvaluation);
|
||||
generalOptions->setLayout(generalLayout);
|
||||
|
||||
auto inputOptions = new QGroupBox(
|
||||
|
|
@ -198,10 +213,12 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
|
|||
dialogLayout->addWidget(buttonbox);
|
||||
setLayout(dialogLayout);
|
||||
|
||||
_executed->setChecked(settings._highlightExecuted);
|
||||
_conditions->setChecked(settings._highlightConditions);
|
||||
_actions->setChecked(settings._highlightActions);
|
||||
_highlightExecutedMacros->setChecked(settings._highlightExecuted);
|
||||
_highlightConditions->setChecked(settings._highlightConditions);
|
||||
_highlightActions->setChecked(settings._highlightActions);
|
||||
_newMacroRegisterHotkeys->setChecked(settings._newMacroRegisterHotkeys);
|
||||
_newMacroUseShortCircuitEvaluation->setChecked(
|
||||
settings._newMacroUseShortCircuitEvaluation);
|
||||
|
||||
if (!macro || macro->IsGroup()) {
|
||||
hotkeyOptions->hide();
|
||||
|
|
@ -212,6 +229,8 @@ MacroSettingsDialog::MacroSettingsDialog(QWidget *parent,
|
|||
}
|
||||
|
||||
_currentMacroRegisterHotkeys->setChecked(macro->PauseHotkeysEnabled());
|
||||
_currentUseShortCircuitEvaluation->setChecked(
|
||||
macro->ShortCircuitEvaluationEnabled());
|
||||
_currentSkipOnStartup->setChecked(macro->SkipExecOnStart());
|
||||
_currentStopActionsIfNotDone->setChecked(macro->StopActionsIfNotDone());
|
||||
_currentInputs->SetInputs(macro->GetInputVariables());
|
||||
|
|
@ -322,17 +341,23 @@ bool MacroSettingsDialog::AskForSettings(QWidget *parent,
|
|||
if (dialog.exec() != DialogCode::Accepted) {
|
||||
return false;
|
||||
}
|
||||
userInput._highlightExecuted = dialog._executed->isChecked();
|
||||
userInput._highlightConditions = dialog._conditions->isChecked();
|
||||
userInput._highlightActions = dialog._actions->isChecked();
|
||||
userInput._highlightExecuted =
|
||||
dialog._highlightExecutedMacros->isChecked();
|
||||
userInput._highlightConditions =
|
||||
dialog._highlightConditions->isChecked();
|
||||
userInput._highlightActions = dialog._highlightActions->isChecked();
|
||||
userInput._newMacroRegisterHotkeys =
|
||||
dialog._newMacroRegisterHotkeys->isChecked();
|
||||
userInput._newMacroUseShortCircuitEvaluation =
|
||||
dialog._newMacroUseShortCircuitEvaluation->isChecked();
|
||||
if (!macro) {
|
||||
return true;
|
||||
}
|
||||
|
||||
macro->EnablePauseHotkeys(
|
||||
dialog._currentMacroRegisterHotkeys->isChecked());
|
||||
macro->SetShortCircuitEvaluation(
|
||||
dialog._currentUseShortCircuitEvaluation->isChecked());
|
||||
macro->SetSkipExecOnStart(dialog._currentSkipOnStartup->isChecked());
|
||||
macro->SetStopActionsIfNotDone(
|
||||
dialog._currentStopActionsIfNotDone->isChecked());
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ public:
|
|||
bool _highlightConditions = false;
|
||||
bool _highlightActions = false;
|
||||
bool _newMacroRegisterHotkeys = true;
|
||||
bool _newMacroUseShortCircuitEvaluation = false;
|
||||
};
|
||||
|
||||
// Dialog for configuring global and individual macro specific settings
|
||||
|
|
@ -44,12 +45,15 @@ private slots:
|
|||
private:
|
||||
void Resize();
|
||||
|
||||
QCheckBox *_executed;
|
||||
QCheckBox *_conditions;
|
||||
QCheckBox *_actions;
|
||||
// Global macro settings
|
||||
QCheckBox *_highlightExecutedMacros;
|
||||
QCheckBox *_highlightConditions;
|
||||
QCheckBox *_highlightActions;
|
||||
QCheckBox *_newMacroRegisterHotkeys;
|
||||
QCheckBox *_newMacroUseShortCircuitEvaluation;
|
||||
// Current macro specific settings
|
||||
QCheckBox *_currentMacroRegisterHotkeys;
|
||||
QCheckBox *_currentUseShortCircuitEvaluation;
|
||||
QCheckBox *_currentSkipOnStartup;
|
||||
QCheckBox *_currentStopActionsIfNotDone;
|
||||
MacroInputSelection *_currentInputs;
|
||||
|
|
|
|||
|
|
@ -87,7 +87,8 @@ bool AdvSceneSwitcher::AddNewMacro(std::shared_ptr<Macro> &res,
|
|||
}
|
||||
|
||||
res = std::make_shared<Macro>(
|
||||
name, GetGlobalMacroSettings()._newMacroRegisterHotkeys);
|
||||
name, GetGlobalMacroSettings()._newMacroRegisterHotkeys,
|
||||
GetGlobalMacroSettings()._newMacroUseShortCircuitEvaluation);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -76,13 +76,15 @@ namespace advss {
|
|||
|
||||
static std::deque<std::shared_ptr<Macro>> macros;
|
||||
|
||||
Macro::Macro(const std::string &name, const bool addHotkey)
|
||||
Macro::Macro(const std::string &name, const bool addHotkey,
|
||||
const bool shortCircuitEvaluation)
|
||||
{
|
||||
SetName(name);
|
||||
if (addHotkey) {
|
||||
SetupHotkeys();
|
||||
}
|
||||
_registerHotkeys = addHotkey;
|
||||
_useShortCircuitEvaluation = shortCircuitEvaluation;
|
||||
}
|
||||
|
||||
Macro::~Macro()
|
||||
|
|
@ -181,6 +183,61 @@ static bool checkCondition(const std::shared_ptr<MacroCondition> &condition)
|
|||
return conditionMatched;
|
||||
}
|
||||
|
||||
bool Macro::CheckConditionHelper(
|
||||
const std::shared_ptr<MacroCondition> &condition) const
|
||||
{
|
||||
bool conditionMatched = false;
|
||||
bool wasEvaluated = false;
|
||||
|
||||
const auto evaluateCondition = [&condition, &conditionMatched,
|
||||
&wasEvaluated]() -> bool {
|
||||
conditionMatched = checkCondition(condition);
|
||||
conditionMatched =
|
||||
condition->CheckDurationModifier(conditionMatched);
|
||||
wasEvaluated = true;
|
||||
return conditionMatched;
|
||||
};
|
||||
|
||||
const auto logicType = condition->GetLogicType();
|
||||
if (logicType == Logic::Type::NONE) {
|
||||
vblog(LOG_INFO, "ignoring condition '%s' for '%s'",
|
||||
condition->GetId().c_str(), _name.c_str());
|
||||
if (!_useShortCircuitEvaluation) {
|
||||
(void)evaluateCondition();
|
||||
}
|
||||
return _matched;
|
||||
}
|
||||
|
||||
bool result = _useShortCircuitEvaluation
|
||||
// Evaluate the condition result if needed
|
||||
? Logic::ApplyConditionLogic(logicType, _matched,
|
||||
evaluateCondition,
|
||||
_name.c_str())
|
||||
// Evaluate the condition result right away
|
||||
: Logic::ApplyConditionLogic(logicType, _matched,
|
||||
evaluateCondition(),
|
||||
_name.c_str());
|
||||
|
||||
const bool isNegativeLogicType = Logic::IsNegationType(logicType);
|
||||
if (wasEvaluated && ((conditionMatched && !isNegativeLogicType) ||
|
||||
(!conditionMatched && isNegativeLogicType))) {
|
||||
condition->EnableHighlight();
|
||||
}
|
||||
|
||||
if (VerboseLoggingEnabled()) {
|
||||
if (wasEvaluated) {
|
||||
blog(LOG_INFO, "condition %s returned %d",
|
||||
condition->GetId().c_str(), conditionMatched);
|
||||
} else {
|
||||
blog(LOG_INFO,
|
||||
"condition %s evaluation skipped (short circuit)",
|
||||
condition->GetId().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Macro::CeckMatch(bool ignorePause)
|
||||
{
|
||||
if (_isGroup) {
|
||||
|
|
@ -194,28 +251,7 @@ bool Macro::CeckMatch(bool ignorePause)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool conditionMatched = checkCondition(condition);
|
||||
conditionMatched =
|
||||
condition->CheckDurationModifier(conditionMatched);
|
||||
|
||||
const auto logicType = condition->GetLogicType();
|
||||
if (logicType == Logic::Type::NONE) {
|
||||
vblog(LOG_INFO, "ignoring condition '%s' for '%s'",
|
||||
condition->GetId().c_str(), _name.c_str());
|
||||
continue;
|
||||
}
|
||||
vblog(LOG_INFO, "condition %s returned %d",
|
||||
condition->GetId().c_str(), conditionMatched);
|
||||
|
||||
const bool isNegativeLogicType =
|
||||
Logic::IsNegationType(logicType);
|
||||
if ((conditionMatched && !isNegativeLogicType) ||
|
||||
(!conditionMatched && isNegativeLogicType)) {
|
||||
condition->EnableHighlight();
|
||||
}
|
||||
|
||||
_matched = Logic::ApplyConditionLogic(
|
||||
logicType, _matched, conditionMatched, _name.c_str());
|
||||
_matched = CheckConditionHelper(condition);
|
||||
}
|
||||
|
||||
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
|
||||
|
|
@ -394,6 +430,16 @@ void Macro::SetStopActionsIfNotDone(bool stopActionsIfNotDone)
|
|||
_stopActionsIfNotDone = stopActionsIfNotDone;
|
||||
}
|
||||
|
||||
void Macro::SetShortCircuitEvaluation(bool useShortCircuitEvaluation)
|
||||
{
|
||||
_useShortCircuitEvaluation = useShortCircuitEvaluation;
|
||||
}
|
||||
|
||||
bool Macro::ShortCircuitEvaluationEnabled() const
|
||||
{
|
||||
return _useShortCircuitEvaluation;
|
||||
}
|
||||
|
||||
void Macro::SetPaused(bool pause)
|
||||
{
|
||||
if (_paused && !pause) {
|
||||
|
|
@ -618,6 +664,8 @@ bool Macro::Save(obs_data_t *obj, bool saveForCopy) const
|
|||
obs_data_set_bool(obj, "onChange", _performActionsOnChange);
|
||||
obs_data_set_bool(obj, "skipExecOnStart", _skipExecOnStart);
|
||||
obs_data_set_bool(obj, "stopActionsIfNotDone", _stopActionsIfNotDone);
|
||||
obs_data_set_bool(obj, "useShortCircuitEvaluation",
|
||||
_useShortCircuitEvaluation);
|
||||
|
||||
obs_data_set_bool(obj, "group", _isGroup);
|
||||
if (_isGroup) {
|
||||
|
|
@ -681,6 +729,8 @@ bool Macro::Load(obs_data_t *obj)
|
|||
_performActionsOnChange = obs_data_get_bool(obj, "onChange");
|
||||
_skipExecOnStart = obs_data_get_bool(obj, "skipExecOnStart");
|
||||
_stopActionsIfNotDone = obs_data_get_bool(obj, "stopActionsIfNotDone");
|
||||
_useShortCircuitEvaluation =
|
||||
obs_data_get_bool(obj, "useShortCircuitEvaluation");
|
||||
|
||||
_isGroup = obs_data_get_bool(obj, "group");
|
||||
if (_isGroup) {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ class MacroDock;
|
|||
|
||||
class Macro {
|
||||
public:
|
||||
Macro(const std::string &name = "", const bool addHotkey = false);
|
||||
Macro(const std::string &name = "", const bool addHotkey = false,
|
||||
const bool shortCircuitEvaluation = false);
|
||||
virtual ~Macro();
|
||||
|
||||
std::string Name() const { return _name; }
|
||||
|
|
@ -54,6 +55,9 @@ public:
|
|||
void SetStopActionsIfNotDone(bool stopActionsIfNotDone);
|
||||
bool StopActionsIfNotDone() const { return _stopActionsIfNotDone; }
|
||||
|
||||
void SetShortCircuitEvaluation(bool useShortCircuitEvaluation);
|
||||
bool ShortCircuitEvaluationEnabled() const;
|
||||
|
||||
int RunCount() const { return _runCount; };
|
||||
void ResetRunCount() { _runCount = 0; };
|
||||
|
||||
|
|
@ -149,6 +153,9 @@ private:
|
|||
void ClearHotkeys() const;
|
||||
void SetHotkeysDesc() const;
|
||||
|
||||
bool
|
||||
CheckConditionHelper(const std::shared_ptr<MacroCondition> &) const;
|
||||
|
||||
bool RunActionsHelper(
|
||||
const std::deque<std::shared_ptr<MacroAction>> &actions,
|
||||
bool ignorePause);
|
||||
|
|
@ -179,6 +186,7 @@ private:
|
|||
bool _isGroup = false;
|
||||
bool _isCollapsed = false;
|
||||
|
||||
bool _useShortCircuitEvaluation = false;
|
||||
bool _runInParallel = false;
|
||||
bool _matched = false;
|
||||
bool _lastMatched = false;
|
||||
|
|
|
|||
|
|
@ -19,6 +19,15 @@ const std::map<Logic::Type, const char *> Logic::localeMap = {
|
|||
|
||||
bool Logic::ApplyConditionLogic(Type type, bool currentMatchResult,
|
||||
bool conditionMatched, const char *context)
|
||||
{
|
||||
return ApplyConditionLogic(
|
||||
type, currentMatchResult,
|
||||
[conditionMatched]() { return conditionMatched; }, context);
|
||||
}
|
||||
|
||||
bool Logic::ApplyConditionLogic(Type type, bool currentMatchResult,
|
||||
const std::function<bool()> &evaluateCondition,
|
||||
const char *context)
|
||||
{
|
||||
if (!context) {
|
||||
context = "";
|
||||
|
|
@ -26,22 +35,22 @@ bool Logic::ApplyConditionLogic(Type type, bool currentMatchResult,
|
|||
|
||||
switch (type) {
|
||||
case Type::ROOT_NONE:
|
||||
return conditionMatched;
|
||||
return evaluateCondition();
|
||||
case Type::ROOT_NOT:
|
||||
return !conditionMatched;
|
||||
return !evaluateCondition();
|
||||
case Type::ROOT_LAST:
|
||||
break;
|
||||
case Type::NONE:
|
||||
vblog(LOG_INFO, "skipping condition check for '%s'", context);
|
||||
return currentMatchResult;
|
||||
case Type::AND:
|
||||
return currentMatchResult && conditionMatched;
|
||||
return currentMatchResult && evaluateCondition();
|
||||
case Type::OR:
|
||||
return currentMatchResult || conditionMatched;
|
||||
return currentMatchResult || evaluateCondition();
|
||||
case Type::AND_NOT:
|
||||
return currentMatchResult && !conditionMatched;
|
||||
return currentMatchResult && !evaluateCondition();
|
||||
case Type::OR_NOT:
|
||||
return currentMatchResult || !conditionMatched;
|
||||
return currentMatchResult || !evaluateCondition();
|
||||
case Type::LAST:
|
||||
default:
|
||||
blog(LOG_WARNING, "ignoring invalid logic check (%s)", context);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
#pragma once
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <obs-data.h>
|
||||
#include <string>
|
||||
|
|
@ -39,6 +40,10 @@ public:
|
|||
static bool ApplyConditionLogic(Type, bool currentMatchResult,
|
||||
bool conditionMatched,
|
||||
const char *context);
|
||||
static bool
|
||||
ApplyConditionLogic(Type, bool currentMatchResult,
|
||||
const std::function<bool()> &evaluateCondition,
|
||||
const char *context);
|
||||
|
||||
static void PopulateLogicTypeSelection(QComboBox *list,
|
||||
bool isRootCondition);
|
||||
|
|
|
|||
|
|
@ -171,3 +171,20 @@ TEST_CASE("Logic", "[conditon-logic]")
|
|||
REQUIRE(advss::Logic::ApplyConditionLogic(logic, true, false, ""));
|
||||
REQUIRE(advss::Logic::ApplyConditionLogic(logic, true, true, ""));
|
||||
}
|
||||
|
||||
TEST_CASE("Short circuit", "[conditon-logic]")
|
||||
{
|
||||
bool functionWasRun = false;
|
||||
const auto testFunction = [&functionWasRun]() {
|
||||
functionWasRun = true;
|
||||
return true;
|
||||
};
|
||||
|
||||
advss::Logic::ApplyConditionLogic(advss::Logic::Type::AND, false,
|
||||
testFunction, "");
|
||||
REQUIRE_FALSE(functionWasRun);
|
||||
|
||||
advss::Logic::ApplyConditionLogic(advss::Logic::Type::OR, false,
|
||||
testFunction, "");
|
||||
REQUIRE(functionWasRun);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user