mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-03-21 17:34:57 -05:00
Refactor condition logic to enable testing
This commit is contained in:
parent
d80df57ef8
commit
24f33fb0d2
|
|
@ -155,6 +155,8 @@ target_sources(
|
|||
lib/queue/action-queue-tab.hpp
|
||||
lib/utils/backup.cpp
|
||||
lib/utils/backup.hpp
|
||||
lib/utils/condition-logic.cpp
|
||||
lib/utils/condition-logic.hpp
|
||||
lib/utils/curl-helper.cpp
|
||||
lib/utils/curl-helper.hpp
|
||||
lib/utils/cursor-shape-changer.cpp
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include "macro-segment-list.hpp"
|
||||
#include "condition-logic.hpp"
|
||||
#include "log-helper.hpp"
|
||||
|
||||
#include <ui_advanced-scene-switcher.h>
|
||||
|
|
@ -12,7 +13,6 @@ class MacroActionEdit;
|
|||
class MacroConditionEdit;
|
||||
class Duration;
|
||||
class SequenceWidget;
|
||||
enum class LogicType;
|
||||
struct SceneGroup;
|
||||
|
||||
/*******************************************************************************
|
||||
|
|
@ -174,7 +174,7 @@ public slots:
|
|||
void MacroConditionReorder(int to, int target);
|
||||
void AddMacroCondition(int idx);
|
||||
void AddMacroCondition(Macro *macro, int idx, const std::string &id,
|
||||
obs_data_t *data, LogicType logic);
|
||||
obs_data_t *data, Logic::Type logic);
|
||||
void RemoveMacroCondition(int idx);
|
||||
void MoveMacroConditionUp(int idx);
|
||||
void MoveMacroConditionDown(int idx);
|
||||
|
|
|
|||
|
|
@ -10,26 +10,6 @@
|
|||
|
||||
namespace advss {
|
||||
|
||||
static inline void populateLogicSelection(QComboBox *list, bool root = false)
|
||||
{
|
||||
if (root) {
|
||||
for (const auto &entry : MacroCondition::logicTypes) {
|
||||
if (static_cast<int>(entry.first) < logic_root_offset) {
|
||||
list->addItem(obs_module_text(
|
||||
entry.second._name.c_str()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (const auto &entry : MacroCondition::logicTypes) {
|
||||
if (static_cast<int>(entry.first) >=
|
||||
logic_root_offset) {
|
||||
list->addItem(obs_module_text(
|
||||
entry.second._name.c_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void populateConditionSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, condition] :
|
||||
|
|
@ -120,13 +100,13 @@ void DurationModifierEdit::Collapse(bool collapse)
|
|||
|
||||
MacroConditionEdit::MacroConditionEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
|
||||
const std::string &id, bool root)
|
||||
const std::string &id, bool isRootCondition)
|
||||
: MacroSegmentEdit(parent),
|
||||
_logicSelection(new QComboBox()),
|
||||
_conditionSelection(new FilterComboBox()),
|
||||
_dur(new DurationModifierEdit()),
|
||||
_entryData(entryData),
|
||||
_isRoot(root)
|
||||
_isRoot(isRootCondition)
|
||||
{
|
||||
QWidget::connect(_logicSelection, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(LogicSelectionChanged(int)));
|
||||
|
|
@ -139,7 +119,7 @@ MacroConditionEdit::MacroConditionEdit(
|
|||
this,
|
||||
SLOT(DurationModifierChanged(DurationModifier::Type)));
|
||||
|
||||
populateLogicSelection(_logicSelection, root);
|
||||
Logic::PopulateLogicTypeSelection(_logicSelection, isRootCondition);
|
||||
populateConditionSelection(_conditionSelection);
|
||||
|
||||
_section->AddHeaderWidget(_logicSelection);
|
||||
|
|
@ -168,15 +148,10 @@ void MacroConditionEdit::LogicSelectionChanged(int idx)
|
|||
return;
|
||||
}
|
||||
|
||||
LogicType type;
|
||||
if (IsRootNode()) {
|
||||
type = static_cast<LogicType>(idx);
|
||||
} else {
|
||||
type = static_cast<LogicType>(idx + logic_root_offset);
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
(*_entryData)->SetLogicType(type);
|
||||
const auto logic = static_cast<Logic::Type>(
|
||||
_logicSelection->itemData(idx).toInt());
|
||||
(*_entryData)->SetLogicType(logic);
|
||||
}
|
||||
|
||||
bool MacroConditionEdit::IsRootNode()
|
||||
|
|
@ -186,13 +161,9 @@ bool MacroConditionEdit::IsRootNode()
|
|||
|
||||
void MacroConditionEdit::SetLogicSelection()
|
||||
{
|
||||
auto logic = (*_entryData)->GetLogicType();
|
||||
if (IsRootNode()) {
|
||||
_logicSelection->setCurrentIndex(static_cast<int>(logic));
|
||||
} else {
|
||||
_logicSelection->setCurrentIndex(static_cast<int>(logic) -
|
||||
logic_root_offset);
|
||||
}
|
||||
const auto logic = (*_entryData)->GetLogicType();
|
||||
_logicSelection->setCurrentIndex(
|
||||
_logicSelection->findData(static_cast<int>(logic)));
|
||||
}
|
||||
|
||||
void MacroConditionEdit::SetRootNode(bool root)
|
||||
|
|
@ -200,7 +171,7 @@ void MacroConditionEdit::SetRootNode(bool root)
|
|||
_isRoot = root;
|
||||
const QSignalBlocker blocker(_logicSelection);
|
||||
_logicSelection->clear();
|
||||
populateLogicSelection(_logicSelection, root);
|
||||
Logic::PopulateLogicTypeSelection(_logicSelection, root);
|
||||
SetLogicSelection();
|
||||
}
|
||||
|
||||
|
|
@ -301,17 +272,17 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
|
|||
}
|
||||
|
||||
std::string id;
|
||||
LogicType logic;
|
||||
Logic::Type logic;
|
||||
if (idx >= 1) {
|
||||
id = macro->Conditions().at(idx - 1)->GetId();
|
||||
if (idx == 1) {
|
||||
logic = LogicType::OR;
|
||||
logic = Logic::Type::OR;
|
||||
} else {
|
||||
logic = macro->Conditions().at(idx - 1)->GetLogicType();
|
||||
}
|
||||
} else {
|
||||
id = MacroCondition::GetDefaultID();
|
||||
logic = LogicType::ROOT_NONE;
|
||||
logic = Logic::Type::ROOT_NONE;
|
||||
}
|
||||
|
||||
OBSDataAutoRelease data;
|
||||
|
|
@ -324,7 +295,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
|
|||
|
||||
void AdvSceneSwitcher::AddMacroCondition(Macro *macro, int idx,
|
||||
const std::string &id,
|
||||
obs_data_t *data, LogicType logic)
|
||||
obs_data_t *data, Logic::Type logic)
|
||||
{
|
||||
if (idx < 0 || idx > (int)macro->Conditions().size()) {
|
||||
assert(false);
|
||||
|
|
@ -389,7 +360,7 @@ void AdvSceneSwitcher::RemoveMacroCondition(int idx)
|
|||
macro->UpdateConditionIndices();
|
||||
if (idx == 0 && macro->Conditions().size() > 0) {
|
||||
auto newRoot = macro->Conditions().at(0);
|
||||
newRoot->SetLogicType(LogicType::ROOT_NONE);
|
||||
newRoot->SetLogicType(Logic::Type::ROOT_NONE);
|
||||
static_cast<MacroConditionEdit *>(
|
||||
ui->conditionsList->WidgetAt(0))
|
||||
->SetRootNode(true);
|
||||
|
|
@ -540,22 +511,23 @@ void AdvSceneSwitcher::MacroConditionReorder(int to, int from)
|
|||
auto lock = LockContext();
|
||||
auto condition = macro->Conditions().at(from);
|
||||
if (to == 0) {
|
||||
condition->SetLogicType(LogicType::ROOT_NONE);
|
||||
condition->SetLogicType(Logic::Type::ROOT_NONE);
|
||||
static_cast<MacroConditionEdit *>(
|
||||
ui->conditionsList->WidgetAt(from))
|
||||
->SetRootNode(true);
|
||||
macro->Conditions().at(0)->SetLogicType(LogicType::AND);
|
||||
macro->Conditions().at(0)->SetLogicType(
|
||||
Logic::Type::AND);
|
||||
static_cast<MacroConditionEdit *>(
|
||||
ui->conditionsList->WidgetAt(0))
|
||||
->SetRootNode(false);
|
||||
}
|
||||
if (from == 0) {
|
||||
condition->SetLogicType(LogicType::AND);
|
||||
condition->SetLogicType(Logic::Type::AND);
|
||||
static_cast<MacroConditionEdit *>(
|
||||
ui->conditionsList->WidgetAt(from))
|
||||
->SetRootNode(false);
|
||||
macro->Conditions().at(1)->SetLogicType(
|
||||
LogicType::ROOT_NONE);
|
||||
Logic::Type::ROOT_NONE);
|
||||
static_cast<MacroConditionEdit *>(
|
||||
ui->conditionsList->WidgetAt(1))
|
||||
->SetRootNode(true);
|
||||
|
|
|
|||
|
|
@ -2,16 +2,6 @@
|
|||
|
||||
namespace advss {
|
||||
|
||||
const std::map<LogicType, LogicTypeInfo> MacroCondition::logicTypes = {
|
||||
{LogicType::NONE, {"AdvSceneSwitcher.logic.none"}},
|
||||
{LogicType::AND, {"AdvSceneSwitcher.logic.and"}},
|
||||
{LogicType::OR, {"AdvSceneSwitcher.logic.or"}},
|
||||
{LogicType::AND_NOT, {"AdvSceneSwitcher.logic.andNot"}},
|
||||
{LogicType::OR_NOT, {"AdvSceneSwitcher.logic.orNot"}},
|
||||
{LogicType::ROOT_NONE, {"AdvSceneSwitcher.logic.rootNone"}},
|
||||
{LogicType::ROOT_NOT, {"AdvSceneSwitcher.logic.not"}},
|
||||
};
|
||||
|
||||
void DurationModifier::Save(obs_data_t *obj, const char *condName,
|
||||
const char *duration) const
|
||||
{
|
||||
|
|
@ -84,7 +74,7 @@ bool MacroCondition::Save(obs_data_t *obj) const
|
|||
{
|
||||
MacroSegment::Save(obj);
|
||||
obs_data_set_string(obj, "id", GetId().c_str());
|
||||
obs_data_set_int(obj, "logic", static_cast<int>(_logic));
|
||||
_logic.Save(obj, "logic");
|
||||
|
||||
// To avoid conflicts with conditions which also use the Duration class
|
||||
// save the duration modifier in a separate obj
|
||||
|
|
@ -99,7 +89,7 @@ bool MacroCondition::Save(obs_data_t *obj) const
|
|||
bool MacroCondition::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroSegment::Load(obj);
|
||||
_logic = static_cast<LogicType>(obs_data_get_int(obj, "logic"));
|
||||
_logic.Load(obj, "logic");
|
||||
if (obs_data_has_user_value(obj, "durationModifier")) {
|
||||
auto durObj = obs_data_get_obj(obj, "durationModifier");
|
||||
_duration.Load(durObj);
|
||||
|
|
@ -111,6 +101,27 @@ bool MacroCondition::Load(obs_data_t *obj)
|
|||
return true;
|
||||
}
|
||||
|
||||
void MacroCondition::ValidateLogicSelection(bool isRootCondition,
|
||||
const char *context)
|
||||
{
|
||||
if (_logic.IsValidSelection(isRootCondition)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_logic.IsRootType()) {
|
||||
_logic.SetType(Logic::Type::ROOT_NONE);
|
||||
blog(LOG_WARNING,
|
||||
"setting invalid logic selection to 'if' for macro %s",
|
||||
context);
|
||||
return;
|
||||
}
|
||||
|
||||
_logic.SetType(Logic::Type::NONE);
|
||||
blog(LOG_WARNING,
|
||||
"setting invalid logic selection to 'ignore' for macro %s",
|
||||
context);
|
||||
}
|
||||
|
||||
void MacroCondition::ResetDuration()
|
||||
{
|
||||
_duration.Reset();
|
||||
|
|
|
|||
|
|
@ -1,34 +1,11 @@
|
|||
#pragma once
|
||||
#include "macro-segment.hpp"
|
||||
#include "condition-logic.hpp"
|
||||
#include "duration-control.hpp"
|
||||
#include "macro-ref.hpp"
|
||||
#include <duration-control.hpp>
|
||||
|
||||
namespace advss {
|
||||
|
||||
constexpr auto logic_root_offset = 100;
|
||||
|
||||
enum class LogicType {
|
||||
ROOT_NONE = 0,
|
||||
ROOT_NOT,
|
||||
ROOT_LAST,
|
||||
// leave some space for potential expansion
|
||||
NONE = 100,
|
||||
AND,
|
||||
OR,
|
||||
AND_NOT,
|
||||
OR_NOT,
|
||||
LAST,
|
||||
};
|
||||
|
||||
static inline bool isRootLogicType(LogicType l)
|
||||
{
|
||||
return static_cast<int>(l) < logic_root_offset;
|
||||
}
|
||||
|
||||
struct LogicTypeInfo {
|
||||
std::string _name;
|
||||
};
|
||||
|
||||
class DurationModifier {
|
||||
public:
|
||||
enum class Type {
|
||||
|
|
@ -65,9 +42,9 @@ public:
|
|||
virtual bool CheckCondition() = 0;
|
||||
virtual bool Save(obs_data_t *obj) const = 0;
|
||||
virtual bool Load(obs_data_t *obj) = 0;
|
||||
LogicType GetLogicType() { return _logic; }
|
||||
void SetLogicType(LogicType logic) { _logic = logic; }
|
||||
static const std::map<LogicType, LogicTypeInfo> logicTypes;
|
||||
Logic::Type GetLogicType() const { return _logic.GetType(); }
|
||||
void SetLogicType(const Logic::Type &logic) { _logic.SetType(logic); }
|
||||
void ValidateLogicSelection(bool isRootCondition, const char *context);
|
||||
void ResetDuration();
|
||||
void CheckDurationModifier(bool &val);
|
||||
DurationModifier GetDurationModifier() { return _duration; }
|
||||
|
|
@ -77,7 +54,7 @@ public:
|
|||
static std::string_view GetDefaultID();
|
||||
|
||||
private:
|
||||
LogicType _logic = LogicType::ROOT_NONE;
|
||||
Logic _logic = Logic(Logic::Type::ROOT_NONE);
|
||||
DurationModifier _duration;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -61,13 +61,13 @@ void AdvSceneSwitcher::PasteMacroSegment()
|
|||
const auto condition = std::static_pointer_cast<MacroCondition>(
|
||||
copyInfo.segment);
|
||||
auto logic = condition->GetLogicType();
|
||||
if (logic > LogicType::ROOT_LAST &&
|
||||
if (logic > Logic::Type::ROOT_LAST &&
|
||||
macro->Conditions().empty()) {
|
||||
logic = LogicType::ROOT_NONE;
|
||||
logic = Logic::Type::ROOT_NONE;
|
||||
}
|
||||
if (logic < LogicType::ROOT_LAST &&
|
||||
if (logic < Logic::Type::ROOT_LAST &&
|
||||
!macro->Conditions().empty()) {
|
||||
logic = LogicType::OR;
|
||||
logic = Logic::Type::OR;
|
||||
}
|
||||
AddMacroCondition(macro.get(), macro->Conditions().size(),
|
||||
copyInfo.segment->GetId(), data.Get(), logic);
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@
|
|||
|
||||
namespace advss {
|
||||
|
||||
static constexpr int perfLogThreshold = 300;
|
||||
static std::deque<std::shared_ptr<Macro>> macros;
|
||||
|
||||
Macro::Macro(const std::string &name, const bool addHotkey)
|
||||
|
|
@ -101,6 +100,30 @@ void Macro::PrepareMoveToGroup(std::shared_ptr<Macro> group,
|
|||
}
|
||||
}
|
||||
|
||||
static bool checkCondition(const std::shared_ptr<MacroCondition> &condition)
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
static constexpr auto perfLogThreshold = 300ms;
|
||||
|
||||
const auto startTime = std::chrono::high_resolution_clock::now();
|
||||
const bool conditionMatched = condition->CheckCondition();
|
||||
const auto endTime = std::chrono::high_resolution_clock::now();
|
||||
const auto timeSpent = endTime - startTime;
|
||||
|
||||
if (timeSpent >= perfLogThreshold) {
|
||||
const long int ms =
|
||||
std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
timeSpent)
|
||||
.count();
|
||||
blog(LOG_WARNING,
|
||||
"spent %ld ms in %s condition check of macro '%s'!", ms,
|
||||
condition->GetId().c_str(),
|
||||
condition->GetMacro()->Name().c_str());
|
||||
}
|
||||
|
||||
return conditionMatched;
|
||||
}
|
||||
|
||||
bool Macro::CeckMatch(bool ignorePause)
|
||||
{
|
||||
if (_isGroup) {
|
||||
|
|
@ -108,77 +131,35 @@ bool Macro::CeckMatch(bool ignorePause)
|
|||
}
|
||||
|
||||
_matched = false;
|
||||
for (auto &c : _conditions) {
|
||||
for (auto &condition : _conditions) {
|
||||
if (_paused && !ignorePause) {
|
||||
vblog(LOG_INFO, "Macro %s is paused", _name.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto startTime = std::chrono::high_resolution_clock::now();
|
||||
bool cond = c->CheckCondition();
|
||||
auto endTime = std::chrono::high_resolution_clock::now();
|
||||
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
endTime - startTime);
|
||||
if (ms.count() >= perfLogThreshold) {
|
||||
blog(LOG_WARNING,
|
||||
"spent %ld ms in %s condition check of macro '%s'!",
|
||||
(long int)ms.count(), c->GetId().c_str(),
|
||||
Name().c_str());
|
||||
}
|
||||
bool conditionMatched = checkCondition(condition);
|
||||
condition->CheckDurationModifier(conditionMatched);
|
||||
|
||||
c->CheckDurationModifier(cond);
|
||||
|
||||
switch (c->GetLogicType()) {
|
||||
case LogicType::NONE:
|
||||
vblog(LOG_INFO,
|
||||
"ignoring condition check 'none' for '%s'",
|
||||
_name.c_str());
|
||||
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;
|
||||
case LogicType::AND:
|
||||
_matched = _matched && cond;
|
||||
if (cond) {
|
||||
c->EnableHighlight();
|
||||
}
|
||||
break;
|
||||
case LogicType::OR:
|
||||
_matched = _matched || cond;
|
||||
if (cond) {
|
||||
c->EnableHighlight();
|
||||
}
|
||||
break;
|
||||
case LogicType::AND_NOT:
|
||||
_matched = _matched && !cond;
|
||||
if (!cond) {
|
||||
c->EnableHighlight();
|
||||
}
|
||||
break;
|
||||
case LogicType::OR_NOT:
|
||||
_matched = _matched || !cond;
|
||||
if (!cond) {
|
||||
c->EnableHighlight();
|
||||
}
|
||||
break;
|
||||
case LogicType::ROOT_NONE:
|
||||
_matched = cond;
|
||||
if (cond) {
|
||||
c->EnableHighlight();
|
||||
}
|
||||
break;
|
||||
case LogicType::ROOT_NOT:
|
||||
_matched = !cond;
|
||||
if (!cond) {
|
||||
c->EnableHighlight();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
blog(LOG_WARNING,
|
||||
"ignoring unknown condition check for '%s'",
|
||||
_name.c_str());
|
||||
break;
|
||||
}
|
||||
vblog(LOG_INFO, "condition %s returned %d", c->GetId().c_str(),
|
||||
cond);
|
||||
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());
|
||||
}
|
||||
|
||||
vblog(LOG_INFO, "Macro %s returned %d", _name.c_str(), _matched);
|
||||
|
||||
_conditionSateChanged = _lastMatched != _matched;
|
||||
|
|
@ -631,40 +612,6 @@ bool Macro::Save(obs_data_t *obj) const
|
|||
return true;
|
||||
}
|
||||
|
||||
bool isValidLogic(LogicType t, bool root)
|
||||
{
|
||||
bool isRoot = isRootLogicType(t);
|
||||
if (!isRoot == root) {
|
||||
return false;
|
||||
}
|
||||
if (isRoot) {
|
||||
if (t >= LogicType::ROOT_LAST) {
|
||||
return false;
|
||||
}
|
||||
} else if (t >= LogicType::LAST) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void setValidLogic(MacroCondition *c, bool root, std::string name)
|
||||
{
|
||||
if (isValidLogic(c->GetLogicType(), root)) {
|
||||
return;
|
||||
}
|
||||
if (root) {
|
||||
c->SetLogicType(LogicType::ROOT_NONE);
|
||||
blog(LOG_WARNING,
|
||||
"setting invalid logic selection to 'if' for macro %s",
|
||||
name.c_str());
|
||||
} else {
|
||||
c->SetLogicType(LogicType::NONE);
|
||||
blog(LOG_WARNING,
|
||||
"setting invalid logic selection to 'ignore' for macro %s",
|
||||
name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
bool Macro::Load(obs_data_t *obj)
|
||||
{
|
||||
_name = obs_data_get_string(obj, "name");
|
||||
|
|
@ -720,7 +667,7 @@ bool Macro::Load(obs_data_t *obj)
|
|||
_conditions.emplace_back(newEntry);
|
||||
auto c = _conditions.back().get();
|
||||
c->Load(arrayObj);
|
||||
setValidLogic(c, root, _name);
|
||||
c->ValidateLogicSelection(root, Name().c_str());
|
||||
} else {
|
||||
blog(LOG_WARNING,
|
||||
"discarding condition entry with unknown id (%s) for macro %s",
|
||||
|
|
|
|||
109
lib/utils/condition-logic.cpp
Normal file
109
lib/utils/condition-logic.cpp
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
#include "condition-logic.hpp"
|
||||
#include "obs-module-helper.hpp"
|
||||
#include "log-helper.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <QComboBox>
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::map<Logic::Type, const char *> Logic::localeMap = {
|
||||
{Logic::Type::NONE, {"AdvSceneSwitcher.logic.none"}},
|
||||
{Logic::Type::AND, {"AdvSceneSwitcher.logic.and"}},
|
||||
{Logic::Type::OR, {"AdvSceneSwitcher.logic.or"}},
|
||||
{Logic::Type::AND_NOT, {"AdvSceneSwitcher.logic.andNot"}},
|
||||
{Logic::Type::OR_NOT, {"AdvSceneSwitcher.logic.orNot"}},
|
||||
{Logic::Type::ROOT_NONE, {"AdvSceneSwitcher.logic.rootNone"}},
|
||||
{Logic::Type::ROOT_NOT, {"AdvSceneSwitcher.logic.not"}},
|
||||
};
|
||||
|
||||
bool Logic::ApplyConditionLogic(Type type, bool currentMatchResult,
|
||||
bool conditionMatched, const char *context)
|
||||
{
|
||||
if (!context) {
|
||||
context = "";
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case Type::ROOT_NONE:
|
||||
return conditionMatched;
|
||||
case Type::ROOT_NOT:
|
||||
return !conditionMatched;
|
||||
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;
|
||||
case Type::OR:
|
||||
return currentMatchResult || conditionMatched;
|
||||
case Type::AND_NOT:
|
||||
return currentMatchResult && !conditionMatched;
|
||||
case Type::OR_NOT:
|
||||
return currentMatchResult || !conditionMatched;
|
||||
case Type::LAST:
|
||||
default:
|
||||
blog(LOG_WARNING, "ignoring invalid logic check (%s)", context);
|
||||
return currentMatchResult;
|
||||
}
|
||||
|
||||
return currentMatchResult;
|
||||
}
|
||||
|
||||
void Logic::PopulateLogicTypeSelection(QComboBox *list, bool isRootCondition)
|
||||
{
|
||||
auto compare = isRootCondition
|
||||
? std::function<bool(int)>{[](int typeValue) {
|
||||
return typeValue < rootOffset;
|
||||
}}
|
||||
: std::function<bool(int)>{[](int typeValue) {
|
||||
return typeValue >= rootOffset;
|
||||
}};
|
||||
for (const auto &[type, name] : localeMap) {
|
||||
const int typeValue = static_cast<int>(type);
|
||||
if (compare(typeValue)) {
|
||||
list->addItem(obs_module_text(name), typeValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Logic::Save(obs_data_t *obj, const char *name) const
|
||||
{
|
||||
obs_data_set_int(obj, name, static_cast<int>(_type));
|
||||
}
|
||||
|
||||
void Logic::Load(obs_data_t *obj, const char *name)
|
||||
{
|
||||
_type = static_cast<Type>(obs_data_get_int(obj, name));
|
||||
}
|
||||
|
||||
bool Logic::IsRootType() const
|
||||
{
|
||||
return _type < static_cast<Type>(rootOffset);
|
||||
}
|
||||
|
||||
bool Logic::IsNegationType(Logic::Type type)
|
||||
{
|
||||
return type == Type::ROOT_NOT || type == Type::AND_NOT ||
|
||||
type == Type::OR_NOT;
|
||||
;
|
||||
}
|
||||
|
||||
bool Logic::IsValidSelection(bool isRootCondition) const
|
||||
{
|
||||
if (!IsRootType() == isRootCondition) {
|
||||
return false;
|
||||
}
|
||||
if (IsRootType() &&
|
||||
(_type < Type::ROOT_NONE || _type >= Type::ROOT_LAST)) {
|
||||
return false;
|
||||
}
|
||||
if (!IsRootType() &&
|
||||
(_type <= Type::ROOT_LAST || _type >= Type::LAST)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
51
lib/utils/condition-logic.hpp
Normal file
51
lib/utils/condition-logic.hpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
#include <map>
|
||||
#include <obs-data.h>
|
||||
#include <string>
|
||||
|
||||
class QComboBox;
|
||||
|
||||
namespace advss {
|
||||
|
||||
class Logic {
|
||||
public:
|
||||
static constexpr auto rootOffset = 100;
|
||||
|
||||
enum class Type {
|
||||
ROOT_NONE = 0,
|
||||
ROOT_NOT,
|
||||
ROOT_LAST,
|
||||
|
||||
// leave some space for potential expansion
|
||||
NONE = rootOffset,
|
||||
AND,
|
||||
OR,
|
||||
AND_NOT,
|
||||
OR_NOT,
|
||||
LAST,
|
||||
};
|
||||
|
||||
Logic(Type type) { _type = type; }
|
||||
|
||||
void Save(obs_data_t *obj, const char *name) const;
|
||||
void Load(obs_data_t *obj, const char *name);
|
||||
|
||||
Type GetType() const { return _type; }
|
||||
void SetType(const Type &type) { _type = type; }
|
||||
bool IsRootType() const;
|
||||
static bool IsNegationType(Logic::Type);
|
||||
bool IsValidSelection(bool isRootCondition) const;
|
||||
|
||||
static bool ApplyConditionLogic(Type, bool currentMatchResult,
|
||||
bool conditionMatched,
|
||||
const char *context);
|
||||
|
||||
static void PopulateLogicTypeSelection(QComboBox *list,
|
||||
bool isRootCondition);
|
||||
|
||||
private:
|
||||
Type _type = Type::NONE;
|
||||
static const std::map<Type, const char *> localeMap;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
Loading…
Reference in New Issue
Block a user