diff --git a/data/locale/en-US.ini b/data/locale/en-US.ini index 9ba1a37e..1c58b335 100644 --- a/data/locale/en-US.ini +++ b/data/locale/en-US.ini @@ -1795,6 +1795,10 @@ AdvSceneSwitcher.script.file.layout="Script file:{{path}}{{open}}" AdvSceneSwitcher.tempVar.select="--select value--" AdvSceneSwitcher.tempVar.selectionInfo.lastValues="Last values:" +AdvSceneSwitcher.tempVar.outputMappings="Save outputs to variables:" +AdvSceneSwitcher.tempVar.outputMappings.add="+ Add output mapping" +AdvSceneSwitcher.tempVar.outputMappings.toggle="Assign outputs to variables" +AdvSceneSwitcher.tempVar.outputMappings.remove="Remove output mapping" AdvSceneSwitcher.tempVar.twitch.broadcaster_user_id="Twitch broadcaster user ID" AdvSceneSwitcher.tempVar.twitch.broadcaster_user_id.description="The numerical Twitch user ID of the broadcaster." diff --git a/data/res/images/DarkVariable.svg b/data/res/images/DarkVariable.svg new file mode 100644 index 00000000..02c1091e --- /dev/null +++ b/data/res/images/DarkVariable.svg @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/data/res/images/LightVariable.svg b/data/res/images/LightVariable.svg new file mode 100644 index 00000000..8544425c --- /dev/null +++ b/data/res/images/LightVariable.svg @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/lib/macro/macro-action-edit.cpp b/lib/macro/macro-action-edit.cpp index 3156ff7b..5b5a239a 100644 --- a/lib/macro/macro-action-edit.cpp +++ b/lib/macro/macro-action-edit.cpp @@ -49,10 +49,12 @@ MacroActionEdit::MacroActionEdit(QWidget *parent, _section->AddHeaderWidget(_enable); _section->AddHeaderWidget(_actionSelection); _section->AddHeaderWidget(_headerInfo); + _section->AddHeaderWidget(_varMappingToggle); auto actionLayout = new QVBoxLayout; actionLayout->setContentsMargins({7, 7, 7, 7}); actionLayout->addWidget(_section); + actionLayout->addWidget(_outputMappings); _contentLayout->addLayout(actionLayout); auto mainLayout = new QHBoxLayout; @@ -61,7 +63,6 @@ MacroActionEdit::MacroActionEdit(QWidget *parent, mainLayout->addWidget(_frame); setLayout(mainLayout); - _entryData = entryData; SetupWidgets(true); actionStateTimer->start(300); @@ -90,7 +91,10 @@ void MacroActionEdit::SetupWidgets(bool basicSetup) auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData); QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)), this, SLOT(HeaderInfoChanged(const QString &))); + QWidget::connect(widget, SIGNAL(ShowVariableMappings(bool)), this, + SLOT(ShowVariableMappings(bool))); _section->SetContent(widget, (*_entryData)->GetCollapsed()); + SetupVarMappings((*_entryData).get()); SetFocusPolicyOfWidgets(); _allWidgetsAreSetup = true; @@ -121,7 +125,10 @@ void MacroActionEdit::ActionSelectionChanged(const QString &text) auto widget = MacroActionFactory::CreateWidget(id, this, *_entryData); QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)), this, SLOT(HeaderInfoChanged(const QString &))); + QWidget::connect(widget, SIGNAL(ShowVariableMappings(bool)), this, + SLOT(ShowVariableMappings(bool))); _section->SetContent(widget); + SetupVarMappings((*_entryData).get()); SetFocusPolicyOfWidgets(); } diff --git a/lib/macro/macro-action-variable.cpp b/lib/macro/macro-action-variable.cpp index e92329dc..57bb3495 100644 --- a/lib/macro/macro-action-variable.cpp +++ b/lib/macro/macro-action-variable.cpp @@ -19,10 +19,11 @@ const std::string MacroActionVariable::id = "variable"; std::vector MacroActionVariable::GetTempVarRefs() const { - if (!_tempVar.HasValidID()) { - return {}; + auto refs = MacroSegment::GetTempVarRefs(); + if (_tempVar.HasValidID()) { + refs.push_back(_tempVar); } - return {_tempVar}; + return refs; } bool MacroActionVariable::_registered = MacroActionFactory::Register( diff --git a/lib/macro/macro-condition-edit.cpp b/lib/macro/macro-condition-edit.cpp index 4ccc098b..85edcf62 100644 --- a/lib/macro/macro-condition-edit.cpp +++ b/lib/macro/macro-condition-edit.cpp @@ -126,10 +126,12 @@ MacroConditionEdit::MacroConditionEdit( _section->AddHeaderWidget(_conditionSelection); _section->AddHeaderWidget(_headerInfo); _section->AddHeaderWidget(_dur); + _section->AddHeaderWidget(_varMappingToggle); QVBoxLayout *conditionLayout = new QVBoxLayout; conditionLayout->setContentsMargins({7, 7, 7, 7}); conditionLayout->addWidget(_section); + conditionLayout->addWidget(_outputMappings); _contentLayout->addLayout(conditionLayout); QHBoxLayout *mainLayout = new QHBoxLayout; @@ -204,7 +206,10 @@ void MacroConditionEdit::SetupWidgets(bool basicSetup) MacroConditionFactory::CreateWidget(id, this, *_entryData); QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)), this, SLOT(HeaderInfoChanged(const QString &))); + QWidget::connect(widget, SIGNAL(ShowVariableMappings(bool)), this, + SLOT(ShowVariableMappings(bool))); _section->SetContent(widget, (*_entryData)->GetCollapsed()); + SetupVarMappings((*_entryData).get()); SetFocusPolicyOfWidgets(); _allWidgetsAreSetup = true; @@ -245,7 +250,10 @@ void MacroConditionEdit::ConditionSelectionChanged(const QString &text) MacroConditionFactory::CreateWidget(id, this, *_entryData); QWidget::connect(widget, SIGNAL(HeaderInfoChanged(const QString &)), this, SLOT(HeaderInfoChanged(const QString &))); + QWidget::connect(widget, SIGNAL(ShowVariableMappings(bool)), this, + SLOT(ShowVariableMappings(bool))); _section->SetContent(widget); + SetupVarMappings((*_entryData).get()); _dur->setVisible(MacroConditionFactory::UsesDurationModifier(id)); SetFocusPolicyOfWidgets(); } diff --git a/lib/macro/macro-condition-tempvar.cpp b/lib/macro/macro-condition-tempvar.cpp index db7ed2b3..fd6caca5 100644 --- a/lib/macro/macro-condition-tempvar.cpp +++ b/lib/macro/macro-condition-tempvar.cpp @@ -8,10 +8,11 @@ const std::string MacroConditionTempVar::id = "temp_var"; std::vector MacroConditionTempVar::GetTempVarRefs() const { - if (!_tempVar.HasValidID()) { - return {}; + auto refs = MacroSegment::GetTempVarRefs(); + if (_tempVar.HasValidID()) { + refs.push_back(_tempVar); } - return {_tempVar}; + return refs; } bool MacroConditionTempVar::_registered = MacroConditionFactory::Register( diff --git a/lib/macro/macro-segment.cpp b/lib/macro/macro-segment.cpp index f10907a8..2103bc6e 100644 --- a/lib/macro/macro-segment.cpp +++ b/lib/macro/macro-segment.cpp @@ -1,8 +1,10 @@ #include "macro-segment.hpp" #include "macro.hpp" #include "mouse-wheel-guard.hpp" +#include "path-helpers.hpp" #include "section.hpp" #include "ui-helpers.hpp" +#include "variable.hpp" #include #include @@ -15,7 +17,48 @@ namespace advss { std::vector MacroSegment::GetTempVarRefs() const { - return {}; + std::vector refs; + for (const auto &mapping : _varMappings) { + if (mapping.tempVar.HasValidID()) { + refs.push_back(mapping.tempVar); + } + } + return refs; +} + +void MacroSegment::ApplyVarMappings() +{ + auto macro = GetMacro(); + for (const auto &mapping : _varMappings) { + auto var = mapping.variable.lock(); + if (!var) { + continue; + } + const auto tempVar = mapping.tempVar.GetTempVariable(macro); + if (!tempVar) { + continue; + } + const auto value = tempVar->Value(); + if (!value) { + continue; + } + var->SetValue(*value); + } +} + +const std::vector &MacroSegment::GetVarMappings() const +{ + return _varMappings; +} + +void MacroSegment::SetVarMappings(const std::vector &mappings) +{ + _varMappings = mappings; +} + +std::vector MacroSegment::GetOwnTempVars() const +{ + return _tempVariables; } MacroSegment::MacroSegment(Macro *m, bool supportsVariableValue) @@ -24,14 +67,32 @@ MacroSegment::MacroSegment(Macro *m, bool supportsVariableValue) { } +void MacroSegment::SetVarMappingExpanded(bool expanded) +{ + _varMappingExpanded = expanded; +} + bool MacroSegment::Save(obs_data_t *obj) const { OBSDataAutoRelease data = obs_data_create(); obs_data_set_bool(data, "collapsed", _collapsed); + obs_data_set_bool(data, "varMappingExpanded", _varMappingExpanded); obs_data_set_bool(data, "useCustomLabel", _useCustomLabel); obs_data_set_string(data, "customLabel", _customLabel.c_str()); obs_data_set_bool(data, "enabled", _enabled); obs_data_set_int(data, "version", 1); + + OBSDataArrayAutoRelease mappingsArray = obs_data_array_create(); + for (const auto &mapping : _varMappings) { + OBSDataAutoRelease item = obs_data_create(); + mapping.tempVar.Save(item, GetMacro(), "tempVar"); + obs_data_set_string( + item, "variable", + GetWeakVariableName(mapping.variable).c_str()); + obs_data_array_push_back(mappingsArray, item); + } + obs_data_set_array(data, "varMappings", mappingsArray); + obs_data_set_obj(obj, "segmentSettings", data); return true; } @@ -40,6 +101,7 @@ bool MacroSegment::Load(obs_data_t *obj) { OBSDataAutoRelease data = obs_data_get_obj(obj, "segmentSettings"); _collapsed = obs_data_get_bool(data, "collapsed"); + _varMappingExpanded = obs_data_get_bool(data, "varMappingExpanded"); _useCustomLabel = obs_data_get_bool(data, "useCustomLabel"); _customLabel = obs_data_get_string(data, "customLabel"); obs_data_set_default_bool(data, "enabled", true); @@ -50,6 +112,20 @@ bool MacroSegment::Load(obs_data_t *obj) _enabled = obs_data_get_bool(obj, "enabled"); } + _varMappings.clear(); + OBSDataArrayAutoRelease mappingsArray = + obs_data_get_array(data, "varMappings"); + const size_t count = obs_data_array_count(mappingsArray); + _varMappings.reserve(count); + for (size_t i = 0; i < count; i++) { + OBSDataAutoRelease item = obs_data_array_item(mappingsArray, i); + VarMapping mapping; + mapping.variable = GetWeakVariableByName( + obs_data_get_string(item, "variable")); + _varMappings.push_back(std::move(mapping)); + _varMappings.back().tempVar.Load(item, GetMacro(), "tempVar"); + } + ClearAvailableTempvars(); return true; } @@ -226,11 +302,38 @@ MacroSegmentEdit::MacroSegmentEdit(QWidget *parent) _headerInfo(new QLabel()), _frame(new QWidget), _contentLayout(new QVBoxLayout), + _outputMappings(new TempVarOutputMappingsWidget(this)), + _varMappingToggle(new QPushButton(this)), _noBorderframe(new QFrame), _borderFrame(new QFrame), _dropLineAbove(new QFrame), _dropLineBelow(new QFrame) { + const auto iconPath = QString::fromStdString(GetDataFilePath( + "res/images/" + GetThemeTypeName() + "Variable.svg")); + _varMappingToggle->setIcon(QIcon(iconPath)); + _varMappingToggle->setMaximumWidth(22); + _varMappingToggle->setFlat(true); + _varMappingToggle->setCheckable(true); + _varMappingToggle->setVisible(false); + _varMappingToggle->setToolTip(obs_module_text( + "AdvSceneSwitcher.tempVar.outputMappings.toggle")); + + QWidget::connect(_varMappingToggle, &QPushButton::toggled, + _outputMappings, + &TempVarOutputMappingsWidget::SetPanelExpanded); + QWidget::connect(_varMappingToggle, &QPushButton::toggled, this, + [this](bool checked) { + if (Data()) { + Data()->SetVarMappingExpanded(checked); + } + }); + QWidget::connect(_outputMappings, + &TempVarOutputMappingsWidget::ExpandableChanged, + _varMappingToggle, &QPushButton::setVisible); + QWidget::connect(_section, &Section::Collapsed, _outputMappings, + &TempVarOutputMappingsWidget::SetSectionCollapsed); + _dropLineAbove->setLineWidth(3); _dropLineAbove->setFixedHeight(11); _dropLineBelow->setLineWidth(3); @@ -292,6 +395,21 @@ MacroSegmentEdit::MacroSegmentEdit(QWidget *parent) _headerInfo->installEventFilter(this); } +void MacroSegmentEdit::SetupVarMappings(MacroSegment *segment) +{ + _outputMappings->SetSegment(segment); + _varMappingToggle->setChecked(segment && + segment->GetVarMappingExpanded()); +} + +void MacroSegmentEdit::ShowVariableMappings(bool show) +{ + _varMappingToggle->setChecked(show); + if (Data()) { + Data()->SetVarMappingExpanded(show); + } +} + bool MacroSegmentEdit::eventFilter(QObject *obj, QEvent *ev) { if (obj == _headerInfo && ev->type() == QEvent::MouseMove) { diff --git a/lib/macro/macro-segment.hpp b/lib/macro/macro-segment.hpp index 803379d2..64508951 100644 --- a/lib/macro/macro-segment.hpp +++ b/lib/macro/macro-segment.hpp @@ -9,15 +9,23 @@ #include #include +#include #include #include #include +#include class QLabel; namespace advss { class Macro; +class Variable; + +struct VarMapping { + TempVariableRef tempVar; + std::weak_ptr variable; +}; class EXPORT MacroSegment : public Lockable { public: @@ -28,6 +36,8 @@ public: int GetIndex() const { return _idx; } void SetCollapsed(bool collapsed) { _collapsed = collapsed; } bool GetCollapsed() const { return _collapsed; } + void SetVarMappingExpanded(bool expanded); + bool GetVarMappingExpanded() const { return _varMappingExpanded; } void SetUseCustomLabel(bool enable) { _useCustomLabel = enable; } bool GetUseCustomLabel() const { return _useCustomLabel; } void SetCustomLabel(const std::string &label) { _customLabel = label; } @@ -43,6 +53,10 @@ public: void SetEnabled(bool); bool Enabled() const; virtual std::string GetVariableValue() const; + void ApplyVarMappings(); + const std::vector &GetVarMappings() const; + void SetVarMappings(const std::vector &mappings); + std::vector GetOwnTempVars() const; protected: friend bool SupportsVariableValue(MacroSegment *); @@ -87,6 +101,7 @@ private: // UI helper bool _highlight = false; bool _collapsed = false; + bool _varMappingExpanded = false; bool _enabled = true; // Custom header labels @@ -99,6 +114,7 @@ private: int _variableRefs = 0; std::string _variableValue; std::vector _tempVariables; + std::vector _varMappings; friend class Macro; }; @@ -118,8 +134,11 @@ public: virtual std::shared_ptr Data() const = 0; virtual void SetupWidgets(bool basicSetup = false) = 0; + void SetupVarMappings(MacroSegment *segment); + public slots: void HeaderInfoChanged(const QString &); + void ShowVariableMappings(bool show); protected slots: void Collapsed(bool) const; @@ -134,6 +153,8 @@ protected: QLabel *_headerInfo; QWidget *_frame; QVBoxLayout *_contentLayout; + TempVarOutputMappingsWidget *_outputMappings; + QPushButton *_varMappingToggle; bool _allWidgetsAreSetup = false; private: diff --git a/lib/macro/macro.cpp b/lib/macro/macro.cpp index 0e00ab17..2ae9a5c5 100644 --- a/lib/macro/macro.cpp +++ b/lib/macro/macro.cpp @@ -113,6 +113,7 @@ static bool checkCondition(const std::shared_ptr &condition) condition->WithLock([&condition, &conditionMatched]() { conditionMatched = condition->EvaluateCondition(); }); + condition->ApplyVarMappings(); const auto endTime = std::chrono::high_resolution_clock::now(); const auto timeSpent = endTime - startTime; @@ -432,6 +433,7 @@ bool Macro::RunActionsHelper( action->WithLock([&action, &actionResult]() { actionResult = action->PerformAction(); }); + action->ApplyVarMappings(); actionsExecutedSuccessfully = actionsExecutedSuccessfully && actionResult; } else { diff --git a/lib/utils/temp-variable.cpp b/lib/utils/temp-variable.cpp index f429fa73..afe63255 100644 --- a/lib/utils/temp-variable.cpp +++ b/lib/utils/temp-variable.cpp @@ -5,11 +5,20 @@ #include "macro-edit.hpp" #include "macro-segment.hpp" #include "plugin-state-helpers.hpp" +#include "sync-helpers.hpp" #include "ui-helpers.hpp" #include "utility.hpp" +#include "variable.hpp" -#include #include +#include +#include +#include +#include +#include +#include +#include + #include Q_DECLARE_METATYPE(advss::TempVariableRef); @@ -731,4 +740,293 @@ void NotifyUIAboutTempVarChange(MacroSegment *segment) segment); } +TempVarOutputMappingsWidget::TempVarOutputMappingsWidget(QWidget *parent) + : QWidget(parent), + _rowsLayout(new QVBoxLayout()), + _addButton(new QPushButton( + obs_module_text("AdvSceneSwitcher.tempVar.outputMappings.add"), + this)), + _animation(new QPropertyAnimation(this, "maximumHeight", this)) +{ + _rowsLayout->setContentsMargins(0, 0, 0, 0); + _rowsLayout->setSpacing(2); + auto rowsContainer = new QWidget(this); + rowsContainer->setLayout(_rowsLayout); + + auto label = new QLabel( + obs_module_text("AdvSceneSwitcher.tempVar.outputMappings"), + this); + + auto mainLayout = new QVBoxLayout(); + mainLayout->addWidget(label); + mainLayout->addWidget(rowsContainer); + mainLayout->addWidget(_addButton); + setLayout(mainLayout); + + _animation->setDuration(300); + _animation->setEasingCurve(QEasingCurve::InOutQuad); + connect(_animation, &QPropertyAnimation::finished, this, + &TempVarOutputMappingsWidget::AnimationFinished); + + hide(); + + connect(_addButton, &QPushButton::clicked, this, + &TempVarOutputMappingsWidget::Add); + connect(TempVarSignalManager::Instance(), + SIGNAL(SegmentTempVarsChanged(MacroSegment *)), this, + SLOT(SegmentTempVarsChanged(MacroSegment *))); +} + +void TempVarOutputMappingsWidget::SetSegment(MacroSegment *segment) +{ + _segment = segment; + _panelExpanded = false; + Rebuild(); +} + +void TempVarOutputMappingsWidget::Add() +{ + if (!_segment) { + return; + } + auto mappings = _segment->GetVarMappings(); + mappings.push_back(VarMapping{}); + { + auto lock = LockContext(); + _segment->SetVarMappings(mappings); + } + Rebuild(); +} + +void TempVarOutputMappingsWidget::Remove(int rowIdx) +{ + if (!_segment) { + return; + } + auto mappings = _segment->GetVarMappings(); + if (rowIdx < 0 || rowIdx >= (int)mappings.size()) { + return; + } + mappings.erase(mappings.begin() + rowIdx); + { + auto lock = LockContext(); + _segment->SetVarMappings(mappings); + } + Rebuild(); +} + +void TempVarOutputMappingsWidget::SegmentTempVarsChanged(MacroSegment *segment) +{ + if (segment != _segment) { + return; + } + // Re-populate each temp var combobox in case temp vars were added/removed + _loading = true; + const auto &mappings = _segment->GetVarMappings(); + for (int i = 0; i < (int)_rows.size(); i++) { + auto combo = _rows[i].tempVarCombo; + const QSignalBlocker blocker(combo); + combo->clear(); + PopulateTempVarCombo(combo); + if (i < (int)mappings.size() && + mappings[i].tempVar.HasValidID()) { + QVariant v; + v.setValue(mappings[i].tempVar); + combo->setCurrentIndex(combo->findData(v)); + } + } + _loading = false; + UpdateVisibility(); +} + +void TempVarOutputMappingsWidget::WriteBackMappings() +{ + if (_loading || !_segment) { + return; + } + std::vector mappings; + for (const auto &row : _rows) { + VarMapping m; + const int idx = row.tempVarCombo->currentIndex(); + if (idx >= 0) { + m.tempVar = row.tempVarCombo->itemData(idx) + .value(); + } + + auto item = row.varSelection->GetCurrentItem(); + if (!item) { + continue; + } + + m.variable = GetWeakVariableByName(item->Name()); + mappings.push_back(std::move(m)); + } + auto lock = LockContext(); + _segment->SetVarMappings(mappings); +} + +void TempVarOutputMappingsWidget::Rebuild() +{ + _loading = true; + + // Remove all existing row widgets from layout and delete them + for (auto &row : _rows) { + _rowsLayout->removeWidget(row.container); + delete row.container; + } + _rows.clear(); + + if (!_segment) { + _loading = false; + UpdateVisibility(); + return; + } + + const auto &mappings = _segment->GetVarMappings(); + for (int i = 0; i < (int)mappings.size(); i++) { + const auto &mapping = mappings[i]; + + auto container = new QWidget(this); + auto rowLayout = new QHBoxLayout(); + rowLayout->setContentsMargins(0, 0, 0, 0); + + auto tempVarCombo = new FilterComboBox( + container, + obs_module_text("AdvSceneSwitcher.tempVar.select")); + tempVarCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); + tempVarCombo->setMaximumWidth(350); + tempVarCombo->setDuplicatesEnabled(true); + PopulateTempVarCombo(tempVarCombo); + if (mapping.tempVar.HasValidID()) { + QVariant v; + v.setValue(mapping.tempVar); + tempVarCombo->setCurrentIndex( + tempVarCombo->findData(v)); + } + + auto arrowLabel = new QLabel(container); + { + QIcon icon; + const auto path = (GetThemeTypeName() == "Light") + ? "theme:Light/right.svg" + : "theme:Dark/right.svg"; + icon.addFile(QString::fromUtf8(path), QSize(), + QIcon::Normal, QIcon::Off); + arrowLabel->setPixmap(icon.pixmap(16, 16)); + } + + auto varSel = new VariableSelection(container); + varSel->SetVariable(mapping.variable); + + auto removeBtn = new QToolButton(container); + removeBtn->setProperty("themeID", QVariant(QString::fromUtf8( + "removeIconSmall"))); + removeBtn->setProperty( + "class", QVariant(QString::fromUtf8("icon-trash"))); + removeBtn->setToolTip(obs_module_text( + "AdvSceneSwitcher.tempVar.outputMappings.remove")); + + rowLayout->addWidget(tempVarCombo, 1); + rowLayout->addWidget(arrowLabel); + rowLayout->addWidget(varSel, 1); + rowLayout->addWidget(removeBtn); + container->setLayout(rowLayout); + + _rowsLayout->addWidget(container); + _rows.push_back({container, tempVarCombo, varSel}); + + const int capturedIdx = i; + connect(removeBtn, &QPushButton::clicked, this, + [this, capturedIdx]() { Remove(capturedIdx); }); + connect(tempVarCombo, + QOverload::of(&QComboBox::currentIndexChanged), + this, &TempVarOutputMappingsWidget::WriteBackMappings); + connect(varSel, &VariableSelection::SelectionChanged, this, + [this](const QString &) { WriteBackMappings(); }); + } + + _loading = false; + UpdateVisibility(); +} + +bool TempVarOutputMappingsWidget::IsExpandable() const +{ + return _segment && !_segment->GetOwnTempVars().empty(); +} + +void TempVarOutputMappingsWidget::SetPanelExpanded(bool expanded) +{ + _panelExpanded = expanded; + UpdateHeight(); +} + +void TempVarOutputMappingsWidget::SetSectionCollapsed(bool collapsed) +{ + _sectionCollapsed = collapsed; + UpdateHeight(); +} + +void TempVarOutputMappingsWidget::AnimateTo(int targetHeight) +{ + if (_animation->state() == QAbstractAnimation::Running) { + _animation->stop(); + } + + if (targetHeight > 0 && !isVisible()) { + setMinimumHeight(0); + setMaximumHeight(0); + show(); + } + + const int currentMax = maximumHeight(); + const int currentHeight = (currentMax >= QWIDGETSIZE_MAX) ? height() + : currentMax; + + _animationTargetHeight = targetHeight; + _animation->setStartValue(currentHeight); + _animation->setEndValue(targetHeight); + _animation->start(); +} + +void TempVarOutputMappingsWidget::AnimationFinished() +{ + if (_animationTargetHeight > 0) { + setMaximumHeight(QWIDGETSIZE_MAX); + } else { + hide(); + setMaximumHeight(QWIDGETSIZE_MAX); + } +} + +void TempVarOutputMappingsWidget::UpdateHeight() +{ + const bool shouldShow = _panelExpanded && !_sectionCollapsed && + IsExpandable(); + if (shouldShow) { + AnimateTo(sizeHint().height()); + } else { + AnimateTo(0); + } +} + +void TempVarOutputMappingsWidget::UpdateVisibility() +{ + const bool expandable = IsExpandable(); + emit ExpandableChanged(expandable); + UpdateHeight(); +} + +void TempVarOutputMappingsWidget::PopulateTempVarCombo( + FilterComboBox *combo) const +{ + if (!_segment) { + return; + } + for (const auto &var : _segment->GetOwnTempVars()) { + QVariant v; + v.setValue(var.GetRef()); + combo->addItem(QString::fromStdString(var.Name()), v); + } +} + } // namespace advss diff --git a/lib/utils/temp-variable.hpp b/lib/utils/temp-variable.hpp index 92c1edb2..ab0cc54b 100644 --- a/lib/utils/temp-variable.hpp +++ b/lib/utils/temp-variable.hpp @@ -5,11 +5,12 @@ #include #include #include -#include -#include -#include #include +class QPropertyAnimation; +class QPushButton; +class QVBoxLayout; + namespace advss { class Macro; @@ -17,6 +18,9 @@ class MacroEdit; class MacroSegment; class TempVariableRef; class TempVariableSelection; +class TempVarOutputMappingsWidget; +class VariableSelection; +struct VarMapping; // TempVariables are variables that are local to a given macro. // They can be created and used by macro segments. @@ -127,4 +131,48 @@ private: void NotifyUIAboutTempVarChange(MacroSegment *); EXPORT void IncrementTempVarInUseGeneration(); +class ADVSS_EXPORT TempVarOutputMappingsWidget : public QWidget { + Q_OBJECT + +public: + TempVarOutputMappingsWidget(QWidget *parent); + void SetSegment(MacroSegment *segment); + void SetPanelExpanded(bool expanded); + void SetSectionCollapsed(bool collapsed); + bool IsExpandable() const; + +signals: + void ExpandableChanged(bool); + +private slots: + void Add(); + void Remove(int rowIdx); + void SegmentTempVarsChanged(MacroSegment *segment); + void WriteBackMappings(); + void AnimationFinished(); + +private: + void Rebuild(); + void UpdateVisibility(); + void UpdateHeight(); + void AnimateTo(int targetHeight); + void PopulateTempVarCombo(FilterComboBox *combo) const; + + struct MappingRow { + QWidget *container; + FilterComboBox *tempVarCombo; + VariableSelection *varSelection; + }; + + MacroSegment *_segment = nullptr; + QVBoxLayout *_rowsLayout; + QPushButton *_addButton; + std::vector _rows; + bool _loading = false; + bool _panelExpanded = false; + bool _sectionCollapsed = false; + int _animationTargetHeight = 0; + QPropertyAnimation *_animation; +}; + } // namespace advss diff --git a/plugins/base/macro-action-filter.cpp b/plugins/base/macro-action-filter.cpp index 2fd6b66c..4f18f29f 100644 --- a/plugins/base/macro-action-filter.cpp +++ b/plugins/base/macro-action-filter.cpp @@ -11,10 +11,11 @@ const std::string MacroActionFilter::id = "filter"; std::vector MacroActionFilter::GetTempVarRefs() const { - if (!_tempVar.HasValidID()) { - return {}; + auto refs = MacroSegment::GetTempVarRefs(); + if (_tempVar.HasValidID()) { + refs.push_back(_tempVar); } - return {_tempVar}; + return refs; } bool MacroActionFilter::_registered = MacroActionFactory::Register( diff --git a/plugins/base/macro-action-source.cpp b/plugins/base/macro-action-source.cpp index 1c4c13f0..25fb4943 100644 --- a/plugins/base/macro-action-source.cpp +++ b/plugins/base/macro-action-source.cpp @@ -15,10 +15,11 @@ const std::string MacroActionSource::id = "source"; std::vector MacroActionSource::GetTempVarRefs() const { - if (!_tempVar.HasValidID()) { - return {}; + auto refs = MacroSegment::GetTempVarRefs(); + if (_tempVar.HasValidID()) { + refs.push_back(_tempVar); } - return {_tempVar}; + return refs; } bool MacroActionSource::_registered = MacroActionFactory::Register(