Add temporary variables

Enables easier use of values returned by macro segments.
For example, this enables the user to extract the user name of a new
Twitch follower and change a text sources settings accordingly
This commit is contained in:
WarmUpTill 2023-11-16 20:36:26 +01:00 committed by WarmUpTill
parent c17523a476
commit 17efa82057
13 changed files with 873 additions and 17 deletions

View File

@ -315,6 +315,8 @@ target_sources(
src/utils/switch-button.hpp
src/utils/sync-helper.cpp
src/utils/sync-helper.hpp
src/utils/temp-variable.cpp
src/utils/temp-variable.hpp
src/utils/transition-selection.cpp
src/utils/transition-selection.hpp
src/utils/utility.cpp

View File

@ -325,6 +325,9 @@ void SwitcherData::SetPreconditions()
cursorPosChanged = cursorPos.first != switcher->lastCursorPos.first ||
cursorPos.second != switcher->lastCursorPos.second;
lastCursorPos = GetCursorPos();
// Macro
InvalidateMacroTempVarValues();
}
void ClearWebsocketMessages();

View File

@ -178,6 +178,7 @@ signals:
void MacroRemoved(const QString &name);
void MacroRenamed(const QString &oldName, const QString &newName);
void MacroSegmentOrderChanged();
void SegmentTempVarsChanged();
void HighlightMacrosChanged(bool value);
void HighlightActionsChanged(bool value);
void HighlightElseActionsChanged(bool value);

View File

@ -138,6 +138,7 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text)
_entryData->reset();
*_entryData = MacroActionFactory::Create(id, macro);
(*_entryData)->SetIndex(idx);
(*_entryData)->PostLoad();
}
auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData);
QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)),
@ -204,7 +205,7 @@ void MacroActionEdit::SetEnableAppearance(bool value)
SetDisableEffect(!value);
}
std::shared_ptr<MacroSegment> MacroActionEdit::Data()
std::shared_ptr<MacroSegment> MacroActionEdit::Data() const
{
return *_entryData;
}
@ -236,6 +237,7 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
auto data = obs_data_create();
macro->Actions().at(idx - 1)->Save(data);
macro->Actions().at(idx)->Load(data);
macro->Actions().at(idx)->PostLoad();
obs_data_release(data);
}
macro->UpdateActionIndices();
@ -525,6 +527,7 @@ void AdvSceneSwitcher::AddMacroElseAction(int idx)
macro->ElseActions().at(idx - 1)->Save(data);
macro->ElseActions().at(idx)->Load(data);
}
macro->ElseActions().at(idx)->PostLoad();
macro->UpdateElseActionIndices();
ui->elseActionsList->Insert(
idx, new MacroActionEdit(

View File

@ -50,7 +50,7 @@ private slots:
void UpdateActionState();
private:
std::shared_ptr<MacroSegment> Data();
std::shared_ptr<MacroSegment> Data() const;
void SetDisableEffect(bool);
void SetEnableAppearance(bool);

View File

@ -312,6 +312,7 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text)
*_entryData = MacroConditionFactory::Create(id, macro);
(*_entryData)->SetIndex(idx);
(*_entryData)->SetLogicType(logic);
(*_entryData)->PostLoad();
}
auto widget =
MacroConditionFactory::CreateWidget(id, this, *_entryData);
@ -342,7 +343,7 @@ void MacroConditionEdit::DurationModifierChanged(DurationModifier::Type m)
(*_entryData)->SetDurationModifier(m);
}
std::shared_ptr<MacroSegment> MacroConditionEdit::Data()
std::shared_ptr<MacroSegment> MacroConditionEdit::Data() const
{
return *_entryData;
}
@ -383,6 +384,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
macro->Conditions().at(idx)->Load(data);
obs_data_release(data);
}
macro->Conditions().at(idx)->PostLoad();
(*cond)->SetLogicType(logic);
macro->UpdateConditionIndices();
ui->conditionsList->Insert(

View File

@ -75,7 +75,7 @@ private slots:
private:
void SetLogicSelection();
std::shared_ptr<MacroSegment> Data();
std::shared_ptr<MacroSegment> Data() const;
QComboBox *_logicSelection;
FilterComboBox *_conditionSelection;

View File

@ -1,4 +1,5 @@
#include "macro-segment.hpp"
#include "macro.hpp"
#include "section.hpp"
#include "utility.hpp"
#include "mouse-wheel-guard.hpp"
@ -26,11 +27,13 @@ bool MacroSegment::Save(obs_data_t *obj) const
bool MacroSegment::Load(obs_data_t *obj)
{
_collapsed = obs_data_get_bool(obj, "collapsed");
ClearAvailableTempvars();
return true;
}
bool MacroSegment::PostLoad()
{
SetupTempVars();
return true;
}
@ -68,20 +71,111 @@ void MacroSegment::SetVariableValue(const std::string &value)
}
}
void MacroSegment::IncrementVariableRef()
void MacroSegment::SetupTempVars()
{
if (!_supportsVariableValue) {
return;
}
_variableRefs++;
ClearAvailableTempvars();
}
void MacroSegment::DecrementVariableRef()
void MacroSegment::ClearAvailableTempvars()
{
if (!_supportsVariableValue || _variableRefs == 0) {
_tempVariables.clear();
NotifyUIAboutTempVarChange();
}
static std::shared_ptr<MacroSegment>
getSharedSegmentFromMacro(Macro *macro, const MacroSegment *segment)
{
auto matches = [segment](const std::shared_ptr<MacroSegment> &s) {
return s.get() == segment;
};
auto condIt = std::find_if(macro->Conditions().begin(),
macro->Conditions().end(), matches);
if (condIt != macro->Conditions().end()) {
return *condIt;
}
auto actionIt = std::find_if(macro->Actions().begin(),
macro->Actions().end(), matches);
if (actionIt != macro->Actions().end()) {
return *actionIt;
}
auto elseIt = std::find_if(macro->ElseActions().begin(),
macro->ElseActions().end(), matches);
if (elseIt != macro->ElseActions().end()) {
return *elseIt;
}
return {};
}
void MacroSegment::AddTempvar(const std::string &id, const std::string &name,
const std::string &description)
{
auto macro = GetMacro();
if (!macro) {
return;
}
_variableRefs--;
auto sharedSegment = getSharedSegmentFromMacro(macro, this);
if (!sharedSegment) {
return;
}
TempVariable var(id, name, description, sharedSegment);
_tempVariables.emplace_back(std::move(var));
NotifyUIAboutTempVarChange();
}
void MacroSegment::SetTempVarValue(const std::string &id,
const std::string &value)
{
for (auto &var : _tempVariables) {
if (var.ID() != id) {
continue;
}
var.SetValue(value);
break;
}
}
void MacroSegment::InvalidateTempVarValues()
{
for (auto &var : _tempVariables) {
var.InvalidateValue();
}
}
std::optional<const TempVariable>
MacroSegment::GetTempVar(const std::string &id) const
{
TempVariable *result = nullptr;
for (auto &var : _tempVariables) {
if (var.ID() == id) {
return var;
}
}
vblog(LOG_INFO, "cannot get value of unknown tempvar %s", id.c_str());
return {};
}
bool SupportsVariableValue(MacroSegment *segment)
{
return segment && segment->_supportsVariableValue;
}
void IncrementVariableRef(MacroSegment *segment)
{
if (!segment || !segment->_supportsVariableValue) {
return;
}
segment->_variableRefs++;
}
void DecrementVariableRef(MacroSegment *segment)
{
if (!segment || !segment->_supportsVariableValue ||
segment->_variableRefs == 0) {
return;
}
segment->_variableRefs--;
}
MacroSegmentEdit::MacroSegmentEdit(bool highlight, QWidget *parent)

View File

@ -1,10 +1,11 @@
#pragma once
// The following helpers are used by all macro seements,
// The following helpers are used by all macro segments,
// so it makes sense to include them here:
#include "log-helper.hpp"
#include "obs-module-helper.hpp"
#include "sync-helper.hpp"
#include "temp-variable.hpp"
#include <QWidget>
#include <QFrame>
@ -34,16 +35,26 @@ public:
virtual std::string GetId() const = 0;
void SetHighlight();
bool Highlight();
bool SupportsVariableValue() const { return _supportsVariableValue; }
virtual std::string GetVariableValue() const;
void IncrementVariableRef();
void DecrementVariableRef();
protected:
friend bool SupportsVariableValue(MacroSegment *);
friend void IncrementVariableRef(MacroSegment *);
friend void DecrementVariableRef(MacroSegment *);
void SetVariableValue(const std::string &value);
bool IsReferencedInVars() { return _variableRefs != 0; }
virtual void SetupTempVars();
void AddTempvar(const std::string &id, const std::string &name,
const std::string &description = "");
void SetTempVarValue(const std::string &id, const std::string &value);
private:
void ClearAvailableTempvars();
std::optional<const TempVariable>
GetTempVar(const std::string &id) const;
void InvalidateTempVarValues();
// Macro helpers
Macro *_macro = nullptr;
int _idx = 0;
@ -56,6 +67,9 @@ private:
const bool _supportsVariableValue = false;
int _variableRefs = 0;
std::string _variableValue;
std::vector<TempVariable> _tempVariables;
friend class Macro;
};
class Section;
@ -70,6 +84,7 @@ public:
void SetFocusPolicyOfWidgets();
void SetCollapsed(bool collapsed);
void SetSelected(bool);
virtual std::shared_ptr<MacroSegment> Data() const = 0;
protected slots:
void HeaderInfoChanged(const QString &);
@ -99,7 +114,6 @@ private:
BELOW,
};
virtual std::shared_ptr<MacroSegment> Data() = 0;
void ShowDropLine(DropLineState);
// The reason for using two separate frame widget each with their own

View File

@ -361,6 +361,126 @@ void Macro::Stop()
}
}
std::vector<TempVariable> Macro::GetTempVars(MacroSegment *filter) const
{
std::vector<TempVariable> res;
auto addTempVars = [&res](const std::deque<std::shared_ptr<MacroSegment>>
&segments) {
for (const auto &s : segments) {
const auto &tempVars = s->_tempVariables;
res.insert(res.end(), tempVars.begin(), tempVars.end());
}
};
addTempVars({_conditions.begin(), _conditions.end()});
addTempVars({_actions.begin(), _actions.end()});
addTempVars({_elseActions.begin(), _elseActions.end()});
if (!filter) {
return res;
}
auto isCondition = [this](const MacroSegment *segment) -> bool {
return std::find_if(_conditions.begin(), _conditions.end(),
[segment](
const std::shared_ptr<MacroSegment>
&ptr) {
return ptr.get() == segment;
}) != _conditions.end();
};
auto isAction = [this](MacroSegment *segment) -> bool {
return std::find_if(_actions.begin(), _actions.end(),
[segment](
const std::shared_ptr<MacroSegment>
&ptr) {
return ptr.get() == segment;
}) != _actions.end();
};
auto isElseAction = [this](MacroSegment *segment) -> bool {
return std::find_if(_elseActions.begin(), _elseActions.end(),
[segment](
const std::shared_ptr<MacroSegment>
&ptr) {
return ptr.get() == segment;
}) != _elseActions.end();
};
const int filterIndex = filter->GetIndex();
// Remove all actions and else actions and conditions after filterIndex
if (isCondition(filter)) {
for (auto it = res.begin(); it != res.end();) {
auto segment = it->Segment().lock().get();
if (isCondition(segment) &&
segment->GetIndex() >= filterIndex) {
it = res.erase(it);
continue;
}
if (isAction(segment) || isElseAction(segment)) {
it = res.erase(it);
continue;
}
++it;
}
return res;
}
// Remove all else actions and actions after filterIndex
if (isAction(filter)) {
for (auto it = res.begin(); it != res.end();) {
auto segment = it->Segment().lock().get();
if (isAction(segment) &&
segment->GetIndex() >= filterIndex) {
it = res.erase(it);
continue;
}
if (isElseAction(segment)) {
it = res.erase(it);
continue;
}
++it;
}
return res;
}
// Remove all actions and elseActions after filterIndex
for (auto it = res.begin(); it != res.end();) {
auto segment = it->Segment().lock().get();
if (isElseAction(segment) &&
segment->GetIndex() >= filterIndex) {
it = res.erase(it);
continue;
}
if (isAction(segment)) {
it = res.erase(it);
continue;
}
++it;
}
return res;
}
std::optional<const TempVariable> Macro::GetTempVar(const MacroSegment *segment,
const std::string &id) const
{
if (!segment) {
return {};
}
return segment->GetTempVar(id);
}
void Macro::InvalidateTempVarValues() const
{
auto invalidateHelper =
[](const std::deque<std::shared_ptr<MacroSegment>> &segments) {
for (const auto &s : segments) {
s->InvalidateTempVarValues();
}
};
invalidateHelper({_conditions.begin(), _conditions.end()});
invalidateHelper({_actions.begin(), _actions.end()});
invalidateHelper({_elseActions.begin(), _elseActions.end()});
}
std::deque<std::shared_ptr<MacroCondition>> &Macro::Conditions()
{
return _conditions;
@ -1200,4 +1320,11 @@ std::weak_ptr<Macro> GetWeakMacroByName(const char *name)
return {};
}
void InvalidateMacroTempVarValues()
{
for (auto &m : switcher->macros) {
m->InvalidateTempVarValues();
}
}
} // namespace advss

View File

@ -3,6 +3,7 @@
#include "macro-condition.hpp"
#include "macro-ref.hpp"
#include "variable-string.hpp"
#include "temp-variable.hpp"
#include <QString>
#include <QByteArray>
@ -47,6 +48,13 @@ public:
bool GetStop() const { return _stop; }
void Stop();
// Temporary variable helpers
std::vector<TempVariable> GetTempVars(MacroSegment *filter) const;
std::optional<const TempVariable>
GetTempVar(const MacroSegment *, const std::string &id) const;
void InvalidateTempVarValues() const;
// Macro segments
std::deque<std::shared_ptr<MacroCondition>> &Conditions();
std::deque<std::shared_ptr<MacroAction>> &Actions();
std::deque<std::shared_ptr<MacroAction>> &ElseActions();
@ -121,11 +129,13 @@ private:
void SetupHotkeys();
void ClearHotkeys() const;
void SetHotkeysDesc() const;
bool RunActionsHelper(
const std::deque<std::shared_ptr<MacroAction>> &actions,
bool ignorePause);
bool RunActions(bool ignorePause);
bool RunElseActions(bool ignorePause);
bool DockIsVisible() const;
void SetDockWidgetName() const;
void SaveDockSettings(obs_data_t *obj) const;
@ -195,5 +205,6 @@ private:
Macro *GetMacroByName(const char *name);
Macro *GetMacroByQString(const QString &name);
std::weak_ptr<Macro> GetWeakMacroByName(const char *name);
void InvalidateMacroTempVarValues();
} // namespace advss

467
src/utils/temp-variable.cpp Normal file
View File

@ -0,0 +1,467 @@
#include "temp-variable.hpp"
#include "advanced-scene-switcher.hpp"
#include "macro.hpp"
#include "macro-segment.hpp"
#include "switcher-data.hpp"
#include "utility.hpp"
#include <QVariant>
#include <QAbstractItemView>
Q_DECLARE_METATYPE(advss::TempVariableRef);
namespace advss {
TempVariable::TempVariable(const std::string &id, const std::string &name,
const std::string &description,
const std::shared_ptr<MacroSegment> &segment)
: _id(id), _name(name), _description(description), _segment(segment)
{
}
TempVariable::TempVariable(const TempVariable &other) noexcept
{
_id = other._id;
_value = other._value;
_name = other._name;
_description = other._description;
_valueIsValid = other._valueIsValid;
_segment = other._segment;
std::lock_guard<std::mutex> lock(other._lastValuesMutex);
_lastValues = other._lastValues;
}
TempVariable::TempVariable(const TempVariable &&other) noexcept
{
_id = other._id;
_value = other._value;
_name = other._name;
_description = other._description;
_valueIsValid = other._valueIsValid;
_segment = other._segment;
std::lock_guard<std::mutex> lock(other._lastValuesMutex);
_lastValues = other._lastValues;
}
TempVariable &TempVariable::operator=(const TempVariable &other) noexcept
{
if (this != &other) {
_id = other._id;
_value = other._value;
_name = other._name;
_description = other._description;
_valueIsValid = other._valueIsValid;
_segment = other._segment;
std::lock_guard<std::mutex> lockOther(other._lastValuesMutex);
std::lock_guard<std::mutex> lock(_lastValuesMutex);
_lastValues = other._lastValues;
}
return *this;
}
TempVariable &TempVariable::operator=(const TempVariable &&other) noexcept
{
if (this != &other) {
_id = other._id;
_value = other._value;
_name = other._name;
_description = other._description;
_valueIsValid = other._valueIsValid;
_segment = other._segment;
std::lock_guard<std::mutex> lockOther(other._lastValuesMutex);
std::lock_guard<std::mutex> lock(_lastValuesMutex);
_lastValues = other._lastValues;
}
return *this;
}
std::optional<std::string> TempVariable::Value() const
{
if (!_valueIsValid) {
return {};
}
return _value;
}
void TempVariable::SetValue(const std::string &val)
{
_valueIsValid = true;
if (_value == val) {
return;
}
_value = val;
std::lock_guard<std::mutex> lock(_lastValuesMutex);
if (_lastValues.size() >= 3) {
_lastValues.erase(_lastValues.begin());
}
_lastValues.push_back(val);
}
void TempVariable::InvalidateValue()
{
_valueIsValid = false;
}
TempVariableRef TempVariable::GetRef() const
{
TempVariableRef ref;
ref._id = _id;
ref._segment = _segment;
return ref;
}
TempVariableRef::SegmentType TempVariableRef::GetType() const
{
auto segment = _segment.lock();
if (!segment) {
return SegmentType::NONE;
}
auto macro = segment->GetMacro();
if (!macro) {
return SegmentType::NONE;
}
if (std::find(macro->Conditions().begin(), macro->Conditions().end(),
segment) != macro->Conditions().end()) {
return SegmentType::CONDITION;
}
if (std::find(macro->Actions().begin(), macro->Actions().end(),
segment) != macro->Actions().end()) {
return SegmentType::ACTION;
}
if (std::find(macro->ElseActions().begin(), macro->ElseActions().end(),
segment) != macro->ElseActions().end()) {
return SegmentType::ELSEACTION;
}
return SegmentType::NONE;
}
int TempVariableRef::GetIdx() const
{
auto segment = _segment.lock();
if (!segment) {
return -1;
}
return segment->GetIndex();
}
void TempVariableRef::Save(obs_data_t *obj, const char *name) const
{
OBSDataAutoRelease data = obs_data_create();
auto type = GetType();
obs_data_set_int(data, "type", static_cast<int>(type));
obs_data_set_int(data, "idx", GetIdx());
obs_data_set_string(data, "id", _id.c_str());
obs_data_set_obj(obj, name, data);
}
void TempVariableRef::Load(obs_data_t *obj, Macro *macro, const char *name)
{
if (!macro) {
_segment.reset();
return;
}
OBSDataAutoRelease data = obs_data_get_obj(obj, name);
int idx = obs_data_get_int(data, "idx");
_id = obs_data_get_string(data, "id");
const auto type =
static_cast<SegmentType>(obs_data_get_int(data, "type"));
switcher->AddPostLoadStep([this, idx, type, macro]() {
this->PostLoad(idx, type, macro);
});
}
void TempVariableRef::PostLoad(int idx, SegmentType type, Macro *macro)
{
std::deque<std::shared_ptr<MacroSegment>> segments;
switch (type) {
case SegmentType::NONE:
_segment.reset();
return;
case SegmentType::CONDITION:
segments = {macro->Conditions().begin(),
macro->Conditions().end()};
break;
case SegmentType::ACTION:
segments = {macro->Actions().begin(), macro->Actions().end()};
break;
case SegmentType::ELSEACTION:
segments = {macro->ElseActions().begin(),
macro->ElseActions().end()};
break;
default:
break;
}
if (idx < 0 || idx >= (int)segments.size()) {
_segment.reset();
return;
}
_segment = segments[idx];
}
std::optional<const TempVariable>
TempVariableRef::GetTempVariable(Macro *macro) const
{
if (!macro) {
return {};
}
auto segment = _segment.lock();
if (!segment) {
return {};
}
return macro->GetTempVar(segment.get(), _id);
}
bool TempVariableRef::operator==(const TempVariableRef &other) const
{
if (_id != other._id) {
return false;
}
auto segment = _segment.lock();
if (!segment) {
return false;
}
return segment == other._segment.lock();
}
TempVariableSelection::TempVariableSelection(QWidget *parent)
: QWidget(parent),
_selection(new FilterComboBox(
this, obs_module_text("AdvSceneSwitcher.tempVar.select"))),
_info(new AutoUpdateTooltipLabel(this, [this]() {
return SetupInfoLabel();
}))
{
QString path = GetThemeTypeName() == "Light"
? ":/res/images/help.svg"
: ":/res/images/help_light.svg";
QIcon icon(path);
QPixmap pixmap = icon.pixmap(QSize(16, 16));
_info->setPixmap(pixmap);
_info->hide();
_selection->setDuplicatesEnabled(true);
PopulateSelection();
QWidget::connect(_selection, SIGNAL(currentIndexChanged(int)), this,
SLOT(SelectionIdxChanged(int)));
QWidget::connect(_selection, SIGNAL(highlighted(int)), this,
SLOT(HighlightChanged(int)));
QWidget::connect(window(), SIGNAL(MacroSegmentOrderChanged()), this,
SLOT(MacroSegmentsChanged()));
QWidget::connect(window(), SIGNAL(SegmentTempVarsChanged()), this,
SLOT(SegmentTempVarsChanged()));
auto layout = new QHBoxLayout();
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(_selection);
layout->addWidget(_info);
setLayout(layout);
}
void TempVariableSelection::SelectionIdxChanged(int idx)
{
if (idx == -1) {
return;
}
auto var = _selection->itemData(idx).value<TempVariableRef>();
emit SelectionChanged(var);
HighlightSelection(var);
SetupInfoLabel();
}
void TempVariableSelection::SetVariable(const TempVariableRef &var)
{
const QSignalBlocker b(_selection);
QVariant variant;
variant.setValue(var);
_selection->setCurrentIndex(_selection->findData(variant));
SetupInfoLabel();
}
void TempVariableSelection::MacroSegmentsChanged()
{
auto currentSelection = _selection->itemData(_selection->currentIndex())
.value<TempVariableRef>();
const QSignalBlocker b(_selection);
_selection->clear();
PopulateSelection();
SetVariable(currentSelection);
}
void TempVariableSelection::SegmentTempVarsChanged()
{
MacroSegmentsChanged();
}
void TempVariableSelection::HighlightChanged(int idx)
{
auto var = _selection->itemData(idx).value<TempVariableRef>();
HighlightSelection(var);
}
void TempVariableSelection::PopulateSelection()
{
auto advssWindow = qobject_cast<AdvSceneSwitcher *>(window());
if (!advssWindow) {
return;
}
auto macro = advssWindow->GetSelectedMacro();
if (!macro) {
return;
}
auto vars = macro->GetTempVars(GetSegment());
for (const auto &var : vars) {
QVariant variant;
variant.setValue(var.GetRef());
_selection->addItem(QString::fromStdString(var.Name()),
variant);
}
// Make sure content is readable when expanding combobox
auto view = _selection->view();
if (view) {
view->setMinimumWidth(view->sizeHintForColumn(0));
_selection->setMinimumWidth(view->sizeHintForColumn(0));
view->updateGeometry();
_selection->updateGeometry();
}
adjustSize();
updateGeometry();
}
void TempVariableSelection::HighlightSelection(const TempVariableRef &var)
{
auto advssWindow = qobject_cast<AdvSceneSwitcher *>(window());
if (!advssWindow) {
return;
}
auto type = var.GetType();
switch (type) {
case TempVariableRef::SegmentType::NONE:
return;
case TempVariableRef::SegmentType::CONDITION:
advssWindow->HighlightCondition(var.GetIdx(), Qt::white);
return;
case TempVariableRef::SegmentType::ACTION:
advssWindow->HighlightAction(var.GetIdx(), Qt::white);
return;
case TempVariableRef::SegmentType::ELSEACTION:
advssWindow->HighlightElseAction(var.GetIdx(), Qt::white);
return;
default:
break;
}
}
QString TempVariableSelection::SetupInfoLabel()
{
auto currentSelection = _selection->itemData(_selection->currentIndex())
.value<TempVariableRef>();
auto advssWindow = qobject_cast<AdvSceneSwitcher *>(window());
if (!advssWindow) {
_info->setToolTip("");
_info->hide();
return "";
}
auto macro = advssWindow->GetSelectedMacro();
if (!macro) {
_info->setToolTip("");
_info->hide();
return "";
}
auto var = currentSelection.GetTempVariable(macro.get());
if (!var) {
_info->setToolTip("");
_info->hide();
return "";
}
auto description = QString::fromStdString(var->_description);
std::lock_guard<std::mutex> lock(var->_lastValuesMutex);
if (!var->_lastValues.empty()) {
if (!description.isEmpty()) {
description += QString("\n\n");
}
description +=
QString(obs_module_text(
"AdvSceneSwitcher.tempVar.selectionInfo.lastValues")) +
"\n";
for (const auto &value : var->_lastValues) {
description += "\n" + QString::fromStdString(value);
}
}
_info->setToolTip(description);
_info->setVisible(!description.isEmpty());
return description;
}
MacroSegment *TempVariableSelection::GetSegment() const
{
const QWidget *widget = this;
{
while (widget) {
if (qobject_cast<const MacroSegmentEdit *>(widget)) {
break;
}
widget = widget->parentWidget();
}
}
if (!widget) {
return nullptr;
}
auto segmentWidget = qobject_cast<const MacroSegmentEdit *>(widget);
return segmentWidget->Data().get();
}
void NotifyUIAboutTempVarChange()
{
obs_queue_task(
OBS_TASK_UI,
[](void *) {
if (!GetSwitcher()->settingsWindowOpened) {
return;
}
AdvSceneSwitcher::window->SegmentTempVarsChanged();
},
nullptr, false);
}
AutoUpdateTooltipLabel::AutoUpdateTooltipLabel(
QWidget *parent, std::function<QString()> updateFunc)
: QLabel(parent), _updateFunc(updateFunc), _timer(new QTimer(this))
{
connect(_timer, &QTimer::timeout, this,
&AutoUpdateTooltipLabel::UpdateTooltip);
}
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
void AutoUpdateTooltipLabel::enterEvent(QEnterEvent *event)
#else
void AutoUpdateTooltipLabel::enterEvent(QEvent *event)
#endif
{
_timer->start(1000);
QLabel::enterEvent(event);
}
void AutoUpdateTooltipLabel::leaveEvent(QEvent *event)
{
_timer->stop();
QLabel::leaveEvent(event);
}
void AutoUpdateTooltipLabel::UpdateTooltip()
{
setToolTip(_updateFunc());
}
} // namespace advss

132
src/utils/temp-variable.hpp Normal file
View File

@ -0,0 +1,132 @@
#pragma once
#include "filter-combo-box.hpp"
#include <obs.hpp>
#include <mutex>
#include <optional>
#include <QEnterEvent>
#include <QEvent>
#include <QLabel>
#include <QTimer>
#include <QStringList>
#include <string>
namespace advss {
class Macro;
class MacroSegment;
class TempVariableRef;
class TempVariableSelection;
// TempVariables are variables that are local to a given macro.
// They can be created and used by macro segments.
//
// For example, a condition could create the TempVariable holding the property
// "user name" which then might be used by a action to change the settings of a
// source in OBS.
class TempVariable {
public:
TempVariable(const std::string &id, const std::string &name,
const std::string &description,
const std::shared_ptr<MacroSegment> &);
TempVariable() = default;
~TempVariable() = default;
TempVariable(const TempVariable &) noexcept;
TempVariable(const TempVariable &&) noexcept;
TempVariable &operator=(const TempVariable &) noexcept;
TempVariable &operator=(const TempVariable &&) noexcept;
std::string ID() const { return _id; }
std::weak_ptr<MacroSegment> Segment() const { return _segment; }
std::string Name() const { return _name; }
std::optional<std::string> Value() const;
void SetValue(const std::string &val);
void InvalidateValue();
TempVariableRef GetRef() const;
private:
std::string _id = "";
std::string _value = "";
std::string _name = "";
std::string _description = "";
mutable std::mutex _lastValuesMutex;
std::vector<std::string> _lastValues;
bool _valueIsValid = false;
std::weak_ptr<MacroSegment> _segment;
friend TempVariableSelection;
friend TempVariableRef;
};
class TempVariableRef {
public:
void Save(obs_data_t *, const char *name = "tempVar") const;
void Load(obs_data_t *, Macro *, const char *name = "tempVar");
std::optional<const TempVariable> GetTempVariable(Macro *) const;
bool operator==(const TempVariableRef &other) const;
private:
enum class SegmentType { NONE, CONDITION, ACTION, ELSEACTION };
SegmentType GetType() const;
int GetIdx() const;
void PostLoad(int idx, SegmentType, Macro *);
std::string _id = "";
std::weak_ptr<MacroSegment> _segment;
friend TempVariable;
friend TempVariableSelection;
};
class AutoUpdateTooltipLabel : public QLabel {
Q_OBJECT
public:
AutoUpdateTooltipLabel(QWidget *parent,
std::function<QString()> updateFunc);
protected:
#if QT_VERSION > QT_VERSION_CHECK(6, 0, 0)
void enterEvent(QEnterEvent *event) override;
#else
void enterEvent(QEvent *event) override;
#endif
void leaveEvent(QEvent *event) override;
private slots:
void UpdateTooltip();
private:
std::function<QString()> _updateFunc;
QTimer *_timer;
};
class TempVariableSelection : public QWidget {
Q_OBJECT
public:
TempVariableSelection(QWidget *parent);
void SetVariable(const TempVariableRef &);
private slots:
void SelectionIdxChanged(int);
void MacroSegmentsChanged();
void SegmentTempVarsChanged();
void HighlightChanged(int);
signals:
void SelectionChanged(const TempVariableRef &);
private:
void PopulateSelection();
void HighlightSelection(const TempVariableRef &);
QString SetupInfoLabel();
MacroSegment *GetSegment() const;
FilterComboBox *_selection;
AutoUpdateTooltipLabel *_info;
};
void NotifyUIAboutTempVarChange();
} // namespace advss