mirror of
https://github.com/WarmUpTill/SceneSwitcher.git
synced 2026-08-29 12:45:28 -05:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7cc44b4470 | ||
|
|
54356d9410 | ||
|
|
defbdf8b7a | ||
|
|
a01af6cfc3 | ||
|
|
4d9c7f1054 | ||
|
|
ced36e2b5b | ||
|
|
d2749f29b0 | ||
|
|
b78c8bc1de | ||
|
|
cc4a3560d8 | ||
|
|
3ab2ea9e66 | ||
|
|
69b82ee2a3 | ||
|
|
f14e6bf252 | ||
|
|
e51ae79b4c | ||
|
|
8f55856fd5 | ||
|
|
a360d53419 | ||
|
|
d28164d02f | ||
|
|
f723212394 | ||
|
|
a0cb08d18f | ||
|
|
d18044b764 | ||
|
|
406b5d61f5 | ||
|
|
9a4eef4a83 |
@@ -128,6 +128,8 @@ target_sources(
|
||||
lib/macro/macro-ref.hpp
|
||||
lib/macro/macro-run-button.cpp
|
||||
lib/macro/macro-run-button.hpp
|
||||
lib/macro/macro-segment-copy-paste.cpp
|
||||
lib/macro/macro-segment-copy-paste.hpp
|
||||
lib/macro/macro-segment-list.cpp
|
||||
lib/macro/macro-segment-list.hpp
|
||||
lib/macro/macro-segment-selection.cpp
|
||||
|
||||
@@ -469,7 +469,6 @@ AdvSceneSwitcher.action.macro.type.unpause="Nicht mehr pausieren"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Zähler zurücksetzen"
|
||||
AdvSceneSwitcher.action.macro.type.run="Aktionen ausführen"
|
||||
AdvSceneSwitcher.action.macro.type.stop="Aktionen stoppen"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Plugin-Status"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Erweiterten Automatischen Szenenwechsler stoppen"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="Ändern des Nichtübereinstimmungsverhaltens:"
|
||||
|
||||
@@ -177,6 +177,8 @@ AdvSceneSwitcher.macroTab.minimize="Minimize"
|
||||
AdvSceneSwitcher.macroTab.segment.useCustomLabel="Use custom label"
|
||||
AdvSceneSwitcher.macroTab.segment.defaultCustomLabel="My label"
|
||||
AdvSceneSwitcher.macroTab.segment.setCustomLabel="Set label ..."
|
||||
AdvSceneSwitcher.macroTab.segment.copy="Copy"
|
||||
AdvSceneSwitcher.macroTab.segment.paste="Paste"
|
||||
AdvSceneSwitcher.macroTab.highlightSettings="Visual settings"
|
||||
AdvSceneSwitcher.macroTab.hotkeySettings="Hotkey settings"
|
||||
AdvSceneSwitcher.macroTab.generalSettings="General settings"
|
||||
@@ -681,6 +683,17 @@ AdvSceneSwitcher.condition.clipboard.condition.isImage="Clipboard contains an im
|
||||
AdvSceneSwitcher.condition.clipboard.condition.isURL="Clipboard contains an URL"
|
||||
AdvSceneSwitcher.condition.clipboard.condition.matches="Clipboard content matches"
|
||||
AdvSceneSwitcher.condition.clipboard.condition.entry="{{conditions}}{{regex}}{{urlInfo}}"
|
||||
AdvSceneSwitcher.condition.folder="Folder watch"
|
||||
AdvSceneSwitcher.condition.folder.tooltip="This condition type will allow you to monitor the contents of a folder.\nNote that the monitoring will *not* recursively scan for changes in sub directories within directories of the selected folder!\nNote that if there are several changes during a short period of time, some of the changes might not emit this signal.\nHowever, the last change in the sequence of changes always will."
|
||||
AdvSceneSwitcher.condition.folder.condition.any="Any change happened"
|
||||
AdvSceneSwitcher.condition.folder.condition.fileAdd="A file was added"
|
||||
AdvSceneSwitcher.condition.folder.condition.fileChange="A file was modified"
|
||||
AdvSceneSwitcher.condition.folder.condition.fileRemove="A file was removed"
|
||||
AdvSceneSwitcher.condition.folder.condition.folderAdd="A directory was added"
|
||||
AdvSceneSwitcher.condition.folder.condition.folderRemove="A directory was removed"
|
||||
AdvSceneSwitcher.condition.folder.entry="{{conditions}}in{{folder}}{{tooltip}}"
|
||||
AdvSceneSwitcher.condition.folder.enableFilter="Only evaluate to true, if the changed path matches a patern"
|
||||
AdvSceneSwitcher.condition.folder.entry.filter="{{filter}}{{regex}}"
|
||||
|
||||
; Macro Actions
|
||||
AdvSceneSwitcher.action.scene="Switch scene"
|
||||
@@ -812,12 +825,20 @@ AdvSceneSwitcher.action.macro="Macro"
|
||||
AdvSceneSwitcher.action.macro.type.pause="Pause"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="Unpause"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Reset counter"
|
||||
AdvSceneSwitcher.action.macro.type.run="Run actions"
|
||||
AdvSceneSwitcher.action.macro.type.run="Run macro"
|
||||
AdvSceneSwitcher.action.macro.type.run.conditions.ignore="Do not consider condition state"
|
||||
AdvSceneSwitcher.action.macro.type.run.conditions.true="Only if conditions evaluate to true"
|
||||
AdvSceneSwitcher.action.macro.type.run.conditions.false="Only if conditions evaluate to false"
|
||||
AdvSceneSwitcher.action.macro.type.run.actionType.regular="actions"
|
||||
AdvSceneSwitcher.action.macro.type.run.actionType.else="else-actions"
|
||||
AdvSceneSwitcher.action.macro.type.run.skipWhenPaused="Skip execution when macro is paused"
|
||||
AdvSceneSwitcher.action.macro.type.stop="Stop actions"
|
||||
AdvSceneSwitcher.action.macro.type.disableAction="Disable action"
|
||||
AdvSceneSwitcher.action.macro.type.enableAction="Enable action"
|
||||
AdvSceneSwitcher.action.macro.type.toggleAction="Toggle action"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.macro.entry.run="{{actions}}{{actionTypes}}of{{macros}}"
|
||||
AdvSceneSwitcher.action.macro.entry.run.condition="{{conditionBehaviors}}of{{conditionMacros}}"
|
||||
AdvSceneSwitcher.action.macro.entry.other="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Plugin state"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Stop the Advanced Scene Switcher plugin"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="Change the no-match behaviour:"
|
||||
@@ -962,6 +983,7 @@ AdvSceneSwitcher.action.variable.type.sceneItemCount="Set to scene item count of
|
||||
AdvSceneSwitcher.action.variable.type.stringLength="Set to length of string"
|
||||
AdvSceneSwitcher.action.variable.type.extractJson="Extract json field with name"
|
||||
AdvSceneSwitcher.action.variable.type.setToTempvar="Set to macro property"
|
||||
AdvSceneSwitcher.action.variable.type.setToTempvar.help="This action type will allow you to extract values out of segments of the current macro and assign those values to the selected variable.\nFor example, you can get the current scene name from a scene condition and assign this name to a variable."
|
||||
AdvSceneSwitcher.action.variable.type.sceneItemName="Set to scene item name at index"
|
||||
AdvSceneSwitcher.action.variable.type.padValue="Pad current value"
|
||||
AdvSceneSwitcher.action.variable.type.truncateValue="Truncate current value"
|
||||
@@ -978,7 +1000,7 @@ AdvSceneSwitcher.action.variable.invalidSelection="Invalid selection!"
|
||||
AdvSceneSwitcher.action.variable.actionNoVariableSupport="Getting variable values from %1 actions is not supported!"
|
||||
AdvSceneSwitcher.action.variable.conditionNoVariableSupport="Getting variable values from %1 conditions is not supported!"
|
||||
AdvSceneSwitcher.action.variable.currentSegmentValue="Current value:"
|
||||
AdvSceneSwitcher.action.variable.entry.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}"
|
||||
AdvSceneSwitcher.action.variable.entry.other="{{actions}}{{variables}}{{variables2}}{{strValue}}{{numValue}}{{segmentIndex}}{{mathExpression}}{{envVariableName}}{{scenes}}{{tempVars}}{{tempVarsHelp}}{{sceneItemIndex}}{{direction}}{{stringLength}}{{paddingCharSelection}}"
|
||||
AdvSceneSwitcher.action.variable.entry.pad="{{actions}}of{{variables}}to length{{stringLength}}by adding{{paddingCharSelection}}to the{{direction}}"
|
||||
AdvSceneSwitcher.action.variable.entry.truncate="{{actions}}of{{variables}}to length{{stringLength}}by removing characters from the{{direction}}"
|
||||
AdvSceneSwitcher.action.variable.entry.substringIndex="Substring start:{{subStringStart}}Substring size:{{subStringSize}}"
|
||||
@@ -1044,6 +1066,8 @@ AdvSceneSwitcher.action.queue.entry.add="{{actions}}actions of macro{{macros}}to
|
||||
AdvSceneSwitcher.action.queue.entry.other="{{actions}}queue{{queues}}"
|
||||
AdvSceneSwitcher.action.window="Window"
|
||||
AdvSceneSwitcher.action.window.type.setFocusWindow="Focus window"
|
||||
AdvSceneSwitcher.action.window.type.setFocusWindow.limitation="The focus action can only be performed under certain circumstances"
|
||||
AdvSceneSwitcher.action.window.type.setFocusWindow.limitation.details="At least one of the following conditions must be true:\n OBS is the foreground process.\n There is currently no foreground window, and thus no foreground process.\n OBS received the last input event."
|
||||
AdvSceneSwitcher.action.window.type.maximizeWindow="Maximize window"
|
||||
AdvSceneSwitcher.action.window.type.minimizeWindow="Minimize window"
|
||||
AdvSceneSwitcher.action.window.type.closeWindow="Close window"
|
||||
@@ -1722,6 +1746,12 @@ AdvSceneSwitcher.tempVar.clipboard.text.description="The text contained in the c
|
||||
AdvSceneSwitcher.tempVar.file.content="File content"
|
||||
AdvSceneSwitcher.tempVar.file.date="File modification date"
|
||||
|
||||
AdvSceneSwitcher.tempVar.folder.newFiles="New files"
|
||||
AdvSceneSwitcher.tempVar.folder.changedFiles="Changed files"
|
||||
AdvSceneSwitcher.tempVar.folder.removedFiles="Removed files"
|
||||
AdvSceneSwitcher.tempVar.folder.newDirs="New directories"
|
||||
AdvSceneSwitcher.tempVar.folder.removedDirs="Removed directories"
|
||||
|
||||
AdvSceneSwitcher.selectScene="--select scene--"
|
||||
AdvSceneSwitcher.selectPreviousScene="Previous Scene"
|
||||
AdvSceneSwitcher.selectCurrentScene="Current Scene"
|
||||
|
||||
@@ -386,7 +386,6 @@ AdvSceneSwitcher.action.macro.type.unpause="Reanudar"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Reiniciar contador"
|
||||
AdvSceneSwitcher.action.macro.type.run="Ejecutar"
|
||||
AdvSceneSwitcher.action.macro.type.stop="Detener"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Estado del complemento"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Detener el complemento Advanced Scene Switcher"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="Cambiar el comportamiento de no coincidencia:"
|
||||
|
||||
@@ -315,7 +315,6 @@ AdvSceneSwitcher.action.macro.type.pause="Duraklat"
|
||||
AdvSceneSwitcher.action.macro.type.unpause="Duraklatma"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="Sayacı sıfırla"
|
||||
AdvSceneSwitcher.action.macro.type.run="Çalıştır"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="Eklenti durumu"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="Advanced Scene Switcher eklentisini durdurun"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="Eşleşmeme davranışını değiştirin:"
|
||||
|
||||
@@ -524,7 +524,6 @@ AdvSceneSwitcher.action.macro.type.unpause="取消暂停"
|
||||
AdvSceneSwitcher.action.macro.type.resetCounter="复位计数器"
|
||||
AdvSceneSwitcher.action.macro.type.run="运行操作"
|
||||
AdvSceneSwitcher.action.macro.type.stop="停止操作"
|
||||
AdvSceneSwitcher.action.macro.entry="{{actions}}{{actionIndex}}{{macros}}"
|
||||
AdvSceneSwitcher.action.pluginState="插件状态"
|
||||
AdvSceneSwitcher.action.pluginState.type.stop="停止高级场景切换插件"
|
||||
AdvSceneSwitcher.action.pluginState.type.noMatch="没有匹配项时:"
|
||||
|
||||
@@ -240,7 +240,7 @@ void SwitcherData::Thread()
|
||||
if (duration.count() < 1) {
|
||||
blog(LOG_INFO,
|
||||
"detected busy loop - refusing to sleep less than 1ms");
|
||||
duration = std::chrono::milliseconds(50);
|
||||
duration = std::chrono::milliseconds(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ class MacroActionEdit;
|
||||
class MacroConditionEdit;
|
||||
class Duration;
|
||||
class SequenceWidget;
|
||||
enum class LogicType;
|
||||
struct SceneGroup;
|
||||
|
||||
/*******************************************************************************
|
||||
@@ -152,12 +153,16 @@ public slots:
|
||||
void SetElseActionsStateToVisible();
|
||||
void MacroActionSelectionChanged(int idx);
|
||||
void MacroActionReorder(int to, int target);
|
||||
void AddMacroAction(Macro *macro, int idx, const std::string &id,
|
||||
obs_data_t *data);
|
||||
void AddMacroAction(int idx);
|
||||
void RemoveMacroAction(int idx);
|
||||
void MoveMacroActionUp(int idx);
|
||||
void MoveMacroActionDown(int idx);
|
||||
void MacroElseActionSelectionChanged(int idx);
|
||||
void MacroElseActionReorder(int to, int target);
|
||||
void AddMacroElseAction(Macro *macro, int idx, const std::string &id,
|
||||
obs_data_t *data);
|
||||
void AddMacroElseAction(int idx);
|
||||
void RemoveMacroElseAction(int idx);
|
||||
void SwapElseActions(Macro *m, int pos1, int pos2);
|
||||
@@ -166,16 +171,16 @@ public slots:
|
||||
void MacroConditionSelectionChanged(int idx);
|
||||
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);
|
||||
void RemoveMacroCondition(int idx);
|
||||
void MoveMacroConditionUp(int idx);
|
||||
void MoveMacroConditionDown(int idx);
|
||||
void FadeOutActionControls();
|
||||
void FadeOutConditionControls();
|
||||
void ResetOpacityActionControls();
|
||||
void ResetOpacityConditionControls();
|
||||
void HighlightControls();
|
||||
void HighlightOnChange();
|
||||
void on_macroProperties_clicked();
|
||||
void CopyMacroSegment();
|
||||
void PasteMacroSegment();
|
||||
|
||||
signals:
|
||||
void MacroAdded(const QString &name);
|
||||
@@ -184,9 +189,6 @@ signals:
|
||||
void MacroSegmentOrderChanged();
|
||||
void SegmentTempVarsChanged();
|
||||
void HighlightMacrosChanged(bool value);
|
||||
void HighlightActionsChanged(bool value);
|
||||
void HighlightElseActionsChanged(bool value);
|
||||
void HighlightConditionsChanged(bool value);
|
||||
|
||||
void ConnectionAdded(const QString &);
|
||||
void ConnectionRenamed(const QString &oldName, const QString &newName);
|
||||
|
||||
@@ -29,8 +29,7 @@ static inline void populateActionSelection(QComboBox *list)
|
||||
MacroActionEdit::MacroActionEdit(QWidget *parent,
|
||||
std::shared_ptr<MacroAction> *entryData,
|
||||
const std::string &id)
|
||||
: MacroSegmentEdit(GetGlobalMacroProperties()._highlightActions,
|
||||
parent),
|
||||
: MacroSegmentEdit(parent),
|
||||
_actionSelection(new FilterComboBox()),
|
||||
_enable(new SwitchButton()),
|
||||
_entryData(entryData)
|
||||
@@ -40,8 +39,6 @@ MacroActionEdit::MacroActionEdit(QWidget *parent,
|
||||
SLOT(ActionSelectionChanged(const QString &)));
|
||||
QWidget::connect(_enable, SIGNAL(checked(bool)), this,
|
||||
SLOT(ActionEnableChanged(bool)));
|
||||
QWidget::connect(window(), SIGNAL(HighlightActionsChanged(bool)), this,
|
||||
SLOT(EnableHighlight(bool)));
|
||||
QWidget::connect(&_actionStateTimer, SIGNAL(timeout()), this,
|
||||
SLOT(UpdateActionState()));
|
||||
|
||||
@@ -161,33 +158,20 @@ std::shared_ptr<MacroSegment> MacroActionEdit::Data() const
|
||||
return *_entryData;
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::AddMacroAction(int idx)
|
||||
void AdvSceneSwitcher::AddMacroAction(Macro *macro, int idx,
|
||||
const std::string &id, obs_data_t *data)
|
||||
{
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (idx < 0 || idx > (int)macro->Actions().size()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string id;
|
||||
if (idx - 1 >= 0) {
|
||||
id = macro->Actions().at(idx - 1)->GetId();
|
||||
} else {
|
||||
id = MacroAction::GetDefaultID();
|
||||
}
|
||||
{
|
||||
auto lock = LockContext();
|
||||
macro->Actions().emplace(
|
||||
macro->Actions().begin() + idx,
|
||||
MacroActionFactory::Create(id, macro.get()));
|
||||
if (idx - 1 >= 0) {
|
||||
auto data = obs_data_create();
|
||||
macro->Actions().at(idx - 1)->Save(data);
|
||||
macro->Actions().emplace(macro->Actions().begin() + idx,
|
||||
MacroActionFactory::Create(id, macro));
|
||||
if (data) {
|
||||
macro->Actions().at(idx)->Load(data);
|
||||
obs_data_release(data);
|
||||
}
|
||||
macro->Actions().at(idx)->PostLoad();
|
||||
RunPostLoadSteps();
|
||||
@@ -198,9 +182,37 @@ void AdvSceneSwitcher::AddMacroAction(int idx)
|
||||
SetActionData(*macro);
|
||||
}
|
||||
HighlightAction(idx);
|
||||
ui->actionsList->SetHelpMsgVisible(false);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::AddMacroAction(int idx)
|
||||
{
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (idx < 0 || idx > (int)macro->Actions().size()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string id;
|
||||
if (idx - 1 >= 0) {
|
||||
id = macro->Actions().at(idx - 1)->GetId();
|
||||
} else {
|
||||
id = MacroAction::GetDefaultID();
|
||||
}
|
||||
|
||||
OBSDataAutoRelease data;
|
||||
if (idx - 1 >= 0) {
|
||||
data = obs_data_create();
|
||||
macro->Actions().at(idx - 1)->Save(data);
|
||||
}
|
||||
AddMacroAction(macro.get(), idx, id, data);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_actionAdd_clicked()
|
||||
{
|
||||
auto macro = GetSelectedMacro();
|
||||
@@ -216,7 +228,6 @@ void AdvSceneSwitcher::on_actionAdd_clicked()
|
||||
if (currentActionIdx != -1) {
|
||||
MacroActionSelectionChanged(currentActionIdx + 1);
|
||||
}
|
||||
ui->actionsList->SetHelpMsgVisible(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::RemoveMacroAction(int idx)
|
||||
@@ -311,7 +322,6 @@ void AdvSceneSwitcher::on_elseActionAdd_clicked()
|
||||
if (currentElseActionIdx != -1) {
|
||||
MacroElseActionSelectionChanged(currentElseActionIdx + 1);
|
||||
}
|
||||
ui->elseActionsList->SetHelpMsgVisible(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::on_elseActionRemove_clicked()
|
||||
@@ -450,31 +460,21 @@ void AdvSceneSwitcher::MacroElseActionReorder(int to, int from)
|
||||
emit(MacroSegmentOrderChanged());
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::AddMacroElseAction(int idx)
|
||||
void AdvSceneSwitcher::AddMacroElseAction(Macro *macro, int idx,
|
||||
const std::string &id,
|
||||
obs_data_t *data)
|
||||
{
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (idx < 0 || idx > (int)macro->ElseActions().size()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string id;
|
||||
if (idx - 1 >= 0) {
|
||||
id = macro->ElseActions().at(idx - 1)->GetId();
|
||||
} else {
|
||||
id = MacroAction::GetDefaultID();
|
||||
}
|
||||
{
|
||||
auto lock = LockContext();
|
||||
macro->ElseActions().emplace(
|
||||
macro->ElseActions().begin() + idx,
|
||||
MacroActionFactory::Create(id, macro.get()));
|
||||
if (idx - 1 >= 0) {
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
macro->ElseActions().at(idx - 1)->Save(data);
|
||||
macro->ElseActions().emplace(macro->ElseActions().begin() + idx,
|
||||
MacroActionFactory::Create(id,
|
||||
macro));
|
||||
if (data) {
|
||||
macro->ElseActions().at(idx)->Load(data);
|
||||
}
|
||||
macro->ElseActions().at(idx)->PostLoad();
|
||||
@@ -486,9 +486,37 @@ void AdvSceneSwitcher::AddMacroElseAction(int idx)
|
||||
SetElseActionData(*macro);
|
||||
}
|
||||
HighlightElseAction(idx);
|
||||
ui->elseActionsList->SetHelpMsgVisible(false);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::AddMacroElseAction(int idx)
|
||||
{
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (idx < 0 || idx > (int)macro->ElseActions().size()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
std::string id;
|
||||
if (idx - 1 >= 0) {
|
||||
id = macro->ElseActions().at(idx - 1)->GetId();
|
||||
} else {
|
||||
id = MacroAction::GetDefaultID();
|
||||
}
|
||||
|
||||
OBSDataAutoRelease data;
|
||||
if (idx - 1 >= 0) {
|
||||
data = obs_data_create();
|
||||
macro->ElseActions().at(idx - 1)->Save(data);
|
||||
}
|
||||
AddMacroElseAction(macro.get(), idx, id, data);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::RemoveMacroElseAction(int idx)
|
||||
{
|
||||
auto macro = GetSelectedMacro();
|
||||
@@ -507,7 +535,7 @@ void AdvSceneSwitcher::RemoveMacroElseAction(int idx)
|
||||
SetMacroAbortWait(true);
|
||||
GetMacroWaitCV().notify_all();
|
||||
macro->UpdateElseActionIndices();
|
||||
SetActionData(*macro);
|
||||
SetElseActionData(*macro);
|
||||
}
|
||||
MacroElseActionSelectionChanged(-1);
|
||||
lastInteracted = MacroSection::ELSE_ACTIONS;
|
||||
|
||||
@@ -48,9 +48,7 @@ bool MacroActionMacro::PerformAction()
|
||||
macro->ResetRunCount();
|
||||
break;
|
||||
case Action::RUN:
|
||||
if (!macro->Paused()) {
|
||||
macro->PerformActions(true, false, true);
|
||||
}
|
||||
RunActions(macro.get());
|
||||
break;
|
||||
case Action::STOP:
|
||||
macro->Stop();
|
||||
@@ -127,6 +125,7 @@ bool MacroActionMacro::Save(obs_data_t *obj) const
|
||||
_macro.Save(obj);
|
||||
_actionIndex.Save(obj, "actionIndex");
|
||||
obs_data_set_int(obj, "action", static_cast<int>(_action));
|
||||
_runOptions.Save(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -137,6 +136,15 @@ bool MacroActionMacro::Load(obs_data_t *obj)
|
||||
_actionIndex.Load(obj, "actionIndex");
|
||||
_action = static_cast<MacroActionMacro::Action>(
|
||||
obs_data_get_int(obj, "action"));
|
||||
_runOptions.Load(obj);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroActionMacro::PostLoad()
|
||||
{
|
||||
MacroRefAction::PostLoad();
|
||||
MacroAction::PostLoad();
|
||||
_runOptions.macro.PostLoad();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -160,11 +168,55 @@ void MacroActionMacro::ResolveVariablesToFixedValues()
|
||||
_actionIndex.ResolveVariables();
|
||||
}
|
||||
|
||||
static inline void populateActionSelection(QComboBox *list)
|
||||
void MacroActionMacro::RunActions(Macro *actionMacro) const
|
||||
{
|
||||
for (auto entry : actionTypes) {
|
||||
list->addItem(obs_module_text(entry.second.c_str()));
|
||||
if (_runOptions.skipWhenPaused && actionMacro->Paused()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_runOptions.logic == RunOptions::Logic::IGNORE_CONDITIONS) {
|
||||
actionMacro->PerformActions(!_runOptions.runElseActions, false,
|
||||
true);
|
||||
return;
|
||||
}
|
||||
|
||||
auto conditionMacro = _runOptions.macro.GetMacro();
|
||||
if (!conditionMacro) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((_runOptions.logic == RunOptions::Logic::CONDITIONS &&
|
||||
conditionMacro->Matched()) ||
|
||||
(_runOptions.logic == RunOptions::Logic::INVERT_CONDITIONS &&
|
||||
!conditionMacro->Matched())) {
|
||||
actionMacro->PerformActions(!_runOptions.runElseActions, false,
|
||||
true);
|
||||
}
|
||||
}
|
||||
|
||||
static void populateActionSelection(QComboBox *list)
|
||||
{
|
||||
for (const auto &[_, name] : actionTypes) {
|
||||
list->addItem(obs_module_text(name.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
static void populateConditionBehaviorSelection(QComboBox *list)
|
||||
{
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.conditions.ignore"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.conditions.true"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.conditions.false"));
|
||||
}
|
||||
|
||||
static void populateActionTypeSelection(QComboBox *list)
|
||||
{
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.actionType.regular"));
|
||||
list->addItem(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.actionType.else"));
|
||||
}
|
||||
|
||||
MacroActionMacroEdit::MacroActionMacroEdit(
|
||||
@@ -173,9 +225,20 @@ MacroActionMacroEdit::MacroActionMacroEdit(
|
||||
_macros(new MacroSelection(parent)),
|
||||
_actionIndex(new MacroSegmentSelection(
|
||||
this, MacroSegmentSelection::Type::ACTION)),
|
||||
_actions(new QComboBox())
|
||||
_actions(new QComboBox()),
|
||||
_conditionMacros(new MacroSelection(parent)),
|
||||
_conditionBehaviors(new QComboBox()),
|
||||
_actionTypes(new QComboBox()),
|
||||
_skipWhenPaused(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.type.run.skipWhenPaused"))),
|
||||
_entryLayout(new QHBoxLayout()),
|
||||
_conditionLayout(new QHBoxLayout())
|
||||
{
|
||||
populateActionSelection(_actions);
|
||||
populateConditionBehaviorSelection(_conditionBehaviors);
|
||||
populateActionTypeSelection(_actionTypes);
|
||||
|
||||
_conditionMacros->HideSelectedMacro();
|
||||
|
||||
QWidget::connect(_macros, SIGNAL(currentTextChanged(const QString &)),
|
||||
this, SLOT(MacroChanged(const QString &)));
|
||||
@@ -184,15 +247,21 @@ MacroActionMacroEdit::MacroActionMacroEdit(
|
||||
QWidget::connect(_actionIndex,
|
||||
SIGNAL(SelectionChanged(const IntVariable &)), this,
|
||||
SLOT(ActionIndexChanged(const IntVariable &)));
|
||||
QWidget::connect(_conditionMacros,
|
||||
SIGNAL(currentTextChanged(const QString &)), this,
|
||||
SLOT(ConditionMacroChanged(const QString &)));
|
||||
QWidget::connect(_conditionBehaviors, SIGNAL(currentIndexChanged(int)),
|
||||
this, SLOT(ConditionBehaviorChanged(int)));
|
||||
QWidget::connect(_actionTypes, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionTypeChanged(int)));
|
||||
QWidget::connect(_skipWhenPaused, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(SkipWhenPausedChanged(int)));
|
||||
|
||||
auto mainLayout = new QHBoxLayout;
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.macro.entry"),
|
||||
mainLayout,
|
||||
{{"{{actions}}", _actions},
|
||||
{"{{actionIndex}}", _actionIndex},
|
||||
{"{{macros}}", _macros}});
|
||||
setLayout(mainLayout);
|
||||
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->addLayout(_entryLayout);
|
||||
layout->addLayout(_conditionLayout);
|
||||
layout->addWidget(_skipWhenPaused);
|
||||
setLayout(layout);
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
@@ -207,6 +276,12 @@ void MacroActionMacroEdit::UpdateEntryData()
|
||||
_actionIndex->SetValue(_entryData->_actionIndex);
|
||||
_actionIndex->SetMacro(_entryData->_macro.GetMacro());
|
||||
_macros->SetCurrentMacro(_entryData->_macro);
|
||||
_conditionMacros->SetCurrentMacro(_entryData->_runOptions.macro);
|
||||
_conditionBehaviors->setCurrentIndex(
|
||||
static_cast<int>(_entryData->_runOptions.logic));
|
||||
_actionTypes->setCurrentIndex(
|
||||
_entryData->_runOptions.runElseActions ? 1 : 0);
|
||||
_skipWhenPaused->setChecked(_entryData->_runOptions.skipWhenPaused);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
@@ -244,8 +319,88 @@ void MacroActionMacroEdit::ActionIndexChanged(const IntVariable &value)
|
||||
_entryData->_actionIndex = value;
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::ConditionMacroChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_runOptions.macro = text;
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::ConditionBehaviorChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_runOptions.logic =
|
||||
static_cast<MacroActionMacro::RunOptions::Logic>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::ActionTypeChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_runOptions.runElseActions = value;
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::SkipWhenPausedChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_runOptions.skipWhenPaused = value;
|
||||
}
|
||||
|
||||
void MacroActionMacroEdit::SetWidgetVisibility()
|
||||
{
|
||||
_entryLayout->removeWidget(_actions);
|
||||
_entryLayout->removeWidget(_actionIndex);
|
||||
_entryLayout->removeWidget(_macros);
|
||||
_entryLayout->removeWidget(_actionTypes);
|
||||
_conditionLayout->removeWidget(_conditionBehaviors);
|
||||
_conditionLayout->removeWidget(_conditionMacros);
|
||||
|
||||
ClearLayout(_entryLayout);
|
||||
ClearLayout(_conditionLayout);
|
||||
|
||||
const std::unordered_map<std::string, QWidget *> placeholders = {
|
||||
{"{{actions}}", _actions},
|
||||
{"{{actionIndex}}", _actionIndex},
|
||||
{"{{macros}}", _macros},
|
||||
{"{{actionTypes}}", _actionTypes},
|
||||
{"{{conditionBehaviors}}", _conditionBehaviors},
|
||||
{"{{conditionMacros}}", _conditionMacros},
|
||||
|
||||
};
|
||||
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
_entryData->_action == MacroActionMacro::Action::RUN
|
||||
? "AdvSceneSwitcher.action.macro.entry.run"
|
||||
: "AdvSceneSwitcher.action.macro.entry.other"),
|
||||
_entryLayout, placeholders);
|
||||
|
||||
if (_entryData->_runOptions.logic ==
|
||||
MacroActionMacro::RunOptions::Logic::IGNORE_CONDITIONS) {
|
||||
_conditionLayout->addWidget(_conditionBehaviors);
|
||||
_conditionLayout->addStretch();
|
||||
} else {
|
||||
PlaceWidgets(
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.action.macro.entry.run.condition"),
|
||||
_conditionLayout, placeholders);
|
||||
}
|
||||
|
||||
if (_entryData->_action == MacroActionMacro::Action::RUN ||
|
||||
_entryData->_action == MacroActionMacro::Action::STOP) {
|
||||
_macros->HideSelectedMacro();
|
||||
@@ -260,6 +415,42 @@ void MacroActionMacroEdit::SetWidgetVisibility()
|
||||
MacroActionMacro::Action::ENABLE_ACTION ||
|
||||
_entryData->_action == MacroActionMacro::Action::TOGGLE_ACTION;
|
||||
_actionIndex->setVisible(isModifyingActionState);
|
||||
|
||||
SetLayoutVisible(_conditionLayout,
|
||||
_entryData->_action == MacroActionMacro::Action::RUN);
|
||||
_conditionMacros->setVisible(
|
||||
_entryData->_action == MacroActionMacro::Action::RUN &&
|
||||
_entryData->_runOptions.logic !=
|
||||
MacroActionMacro::RunOptions::Logic::IGNORE_CONDITIONS);
|
||||
_actionTypes->setVisible(_entryData->_action ==
|
||||
MacroActionMacro::Action::RUN);
|
||||
_skipWhenPaused->setVisible(_entryData->_action ==
|
||||
MacroActionMacro::Action::RUN);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionMacro::RunOptions::Save(obs_data_t *obj) const
|
||||
{
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
obs_data_set_int(data, "logic", static_cast<int>(logic));
|
||||
obs_data_set_bool(data, "runElseActions", runElseActions);
|
||||
obs_data_set_bool(data, "skipWhenPaused", skipWhenPaused);
|
||||
macro.Save(data);
|
||||
obs_data_set_obj(obj, "runOptions", data);
|
||||
}
|
||||
|
||||
void MacroActionMacro::RunOptions::Load(obs_data_t *obj)
|
||||
{
|
||||
if (!obs_data_has_user_value(obj, "runOptions")) {
|
||||
return;
|
||||
}
|
||||
OBSDataAutoRelease data = obs_data_get_obj(obj, "runOptions");
|
||||
logic = static_cast<Logic>(obs_data_get_int(data, "logic"));
|
||||
runElseActions = obs_data_get_bool(data, "runElseActions");
|
||||
skipWhenPaused = obs_data_get_bool(data, "skipWhenPaused");
|
||||
macro.Load(data);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "macro-selection.hpp"
|
||||
#include "macro-segment-selection.hpp"
|
||||
|
||||
#include <QCheckBox>
|
||||
#include <QHBoxLayout>
|
||||
|
||||
namespace advss {
|
||||
@@ -14,12 +15,27 @@ public:
|
||||
void LogAction() const;
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
bool PostLoad();
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroAction> Create(Macro *m);
|
||||
std::shared_ptr<MacroAction> Copy() const;
|
||||
void ResolveVariablesToFixedValues();
|
||||
|
||||
struct RunOptions {
|
||||
void Save(obs_data_t *obj) const;
|
||||
void Load(obs_data_t *obj);
|
||||
enum class Logic {
|
||||
IGNORE_CONDITIONS,
|
||||
CONDITIONS,
|
||||
INVERT_CONDITIONS,
|
||||
};
|
||||
Logic logic;
|
||||
bool runElseActions = false;
|
||||
bool skipWhenPaused = true;
|
||||
MacroRef macro;
|
||||
};
|
||||
|
||||
enum class Action {
|
||||
PAUSE,
|
||||
UNPAUSE,
|
||||
@@ -32,8 +48,11 @@ public:
|
||||
};
|
||||
Action _action = Action::PAUSE;
|
||||
IntVariable _actionIndex = 1;
|
||||
RunOptions _runOptions = {};
|
||||
|
||||
private:
|
||||
void RunActions(Macro *actionMacro) const;
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
@@ -58,18 +77,28 @@ private slots:
|
||||
void MacroChanged(const QString &text);
|
||||
void ActionChanged(int value);
|
||||
void ActionIndexChanged(const IntVariable &value);
|
||||
void ConditionMacroChanged(const QString &text);
|
||||
void ConditionBehaviorChanged(int value);
|
||||
void ActionTypeChanged(int value);
|
||||
void SkipWhenPausedChanged(int value);
|
||||
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
protected:
|
||||
MacroSelection *_macros;
|
||||
MacroSegmentSelection *_actionIndex;
|
||||
QComboBox *_actions;
|
||||
std::shared_ptr<MacroActionMacro> _entryData;
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
MacroSelection *_macros;
|
||||
MacroSegmentSelection *_actionIndex;
|
||||
QComboBox *_actions;
|
||||
MacroSelection *_conditionMacros;
|
||||
QComboBox *_conditionBehaviors;
|
||||
QComboBox *_actionTypes;
|
||||
QCheckBox *_skipWhenPaused;
|
||||
QHBoxLayout *_entryLayout;
|
||||
QHBoxLayout *_conditionLayout;
|
||||
|
||||
std::shared_ptr<MacroActionMacro> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include "macro.hpp"
|
||||
#include "non-modal-dialog.hpp"
|
||||
#include "source-helpers.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
namespace advss {
|
||||
@@ -630,6 +631,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
_scenes(new SceneSelectionWidget(this, true, false, true, true,
|
||||
true)),
|
||||
_tempVars(new TempVariableSelection(this)),
|
||||
_tempVarsHelp(new QLabel()),
|
||||
_sceneItemIndex(new VariableSpinBox()),
|
||||
_direction(new QComboBox()),
|
||||
_stringLength(new VariableSpinBox()),
|
||||
@@ -657,6 +659,15 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
populateTypeSelection(_actions);
|
||||
populateDirectionSelection(_direction);
|
||||
|
||||
QString path = GetThemeTypeName() == "Light"
|
||||
? ":/res/images/help.svg"
|
||||
: ":/res/images/help_light.svg";
|
||||
QIcon icon(path);
|
||||
QPixmap pixmap = icon.pixmap(QSize(16, 16));
|
||||
_tempVarsHelp->setPixmap(pixmap);
|
||||
_tempVarsHelp->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.action.variable.type.setToTempvar.help"));
|
||||
|
||||
QWidget::connect(_variables, SIGNAL(SelectionChanged(const QString &)),
|
||||
this, SLOT(VariableChanged(const QString &)));
|
||||
QWidget::connect(_variables2, SIGNAL(SelectionChanged(const QString &)),
|
||||
@@ -742,6 +753,7 @@ MacroActionVariableEdit::MacroActionVariableEdit(
|
||||
{"{{envVariableName}}", _envVariable},
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{tempVars}}", _tempVars},
|
||||
{"{{tempVarsHelp}}", _tempVarsHelp},
|
||||
{"{{sceneItemIndex}}", _sceneItemIndex},
|
||||
{"{{direction}}", _direction},
|
||||
{"{{stringLength}}", _stringLength},
|
||||
@@ -1202,6 +1214,7 @@ void MacroActionVariableEdit::SelectionChanged(const TempVariableRef &var)
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_tempVar = var;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionVariableEdit::SceneItemIndexChanged(
|
||||
@@ -1268,6 +1281,7 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
||||
{"{{envVariableName}}", _envVariable},
|
||||
{"{{scenes}}", _scenes},
|
||||
{"{{tempVars}}", _tempVars},
|
||||
{"{{tempVarsHelp}}", _tempVarsHelp},
|
||||
{"{{sceneItemIndex}}", _sceneItemIndex},
|
||||
{"{{direction}}", _direction},
|
||||
{"{{stringLength}}", _stringLength},
|
||||
@@ -1381,6 +1395,10 @@ void MacroActionVariableEdit::SetWidgetVisibility()
|
||||
MacroActionVariable::Type::SCENE_ITEM_NAME);
|
||||
_tempVars->setVisible(_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_TO_TEMPVAR);
|
||||
_tempVarsHelp->setVisible(
|
||||
_entryData->_type ==
|
||||
MacroActionVariable::Type::SET_TO_TEMPVAR &&
|
||||
!_entryData->_tempVar.HasValidID());
|
||||
_sceneItemIndex->setVisible(_entryData->_type ==
|
||||
MacroActionVariable::Type::SCENE_ITEM_NAME);
|
||||
_direction->setVisible(
|
||||
|
||||
@@ -185,6 +185,7 @@ private:
|
||||
VariableLineEdit *_envVariable;
|
||||
SceneSelectionWidget *_scenes;
|
||||
TempVariableSelection *_tempVars;
|
||||
QLabel *_tempVarsHelp;
|
||||
VariableSpinBox *_sceneItemIndex;
|
||||
QComboBox *_direction;
|
||||
VariableSpinBox *_stringLength;
|
||||
|
||||
@@ -121,8 +121,7 @@ void DurationModifierEdit::Collapse(bool collapse)
|
||||
MacroConditionEdit::MacroConditionEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroCondition> *entryData,
|
||||
const std::string &id, bool root)
|
||||
: MacroSegmentEdit(GetGlobalMacroProperties()._highlightConditions,
|
||||
parent),
|
||||
: MacroSegmentEdit(parent),
|
||||
_logicSelection(new QComboBox()),
|
||||
_conditionSelection(new FilterComboBox()),
|
||||
_dur(new DurationModifierEdit()),
|
||||
@@ -139,8 +138,6 @@ MacroConditionEdit::MacroConditionEdit(
|
||||
QWidget::connect(_dur, SIGNAL(ModifierChanged(DurationModifier::Type)),
|
||||
this,
|
||||
SLOT(DurationModifierChanged(DurationModifier::Type)));
|
||||
QWidget::connect(window(), SIGNAL(HighlightConditionsChanged(bool)),
|
||||
this, SLOT(EnableHighlight(bool)));
|
||||
|
||||
populateLogicSelection(_logicSelection, root);
|
||||
populateConditionSelection(_conditionSelection);
|
||||
@@ -299,6 +296,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
|
||||
}
|
||||
|
||||
if (idx < 0 || idx > (int)macro->Conditions().size()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -315,16 +313,31 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
|
||||
id = MacroCondition::GetDefaultID();
|
||||
logic = LogicType::ROOT_NONE;
|
||||
}
|
||||
|
||||
OBSDataAutoRelease data;
|
||||
if (idx - 1 >= 0) {
|
||||
data = obs_data_create();
|
||||
macro->Conditions().at(idx - 1)->Save(data);
|
||||
}
|
||||
AddMacroCondition(macro.get(), idx, id, data.Get(), logic);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::AddMacroCondition(Macro *macro, int idx,
|
||||
const std::string &id,
|
||||
obs_data_t *data, LogicType logic)
|
||||
{
|
||||
if (idx < 0 || idx > (int)macro->Conditions().size()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
auto lock = LockContext();
|
||||
auto cond = macro->Conditions().emplace(
|
||||
macro->Conditions().begin() + idx,
|
||||
MacroConditionFactory::Create(id, macro.get()));
|
||||
if (idx - 1 >= 0) {
|
||||
auto data = obs_data_create();
|
||||
macro->Conditions().at(idx - 1)->Save(data);
|
||||
MacroConditionFactory::Create(id, macro));
|
||||
if (data) {
|
||||
macro->Conditions().at(idx)->Load(data);
|
||||
obs_data_release(data);
|
||||
}
|
||||
macro->Conditions().at(idx)->PostLoad();
|
||||
RunPostLoadSteps();
|
||||
@@ -337,6 +350,7 @@ void AdvSceneSwitcher::AddMacroCondition(int idx)
|
||||
SetConditionData(*macro);
|
||||
}
|
||||
HighlightCondition(idx);
|
||||
ui->conditionsList->SetHelpMsgVisible(false);
|
||||
emit(MacroSegmentOrderChanged());
|
||||
}
|
||||
|
||||
@@ -355,7 +369,6 @@ void AdvSceneSwitcher::on_conditionAdd_clicked()
|
||||
if (currentConditionIdx != -1) {
|
||||
MacroConditionSelectionChanged(currentConditionIdx + 1);
|
||||
}
|
||||
ui->conditionsList->SetHelpMsgVisible(false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::RemoveMacroCondition(int idx)
|
||||
|
||||
104
lib/macro/macro-segment-copy-paste.cpp
Normal file
104
lib/macro/macro-segment-copy-paste.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
#include "advanced-scene-switcher.hpp"
|
||||
#include "macro.hpp"
|
||||
|
||||
#include <QShortcut>
|
||||
|
||||
namespace advss {
|
||||
|
||||
struct MacroSegmentCopyInfo {
|
||||
enum class Type { NONE, CONDITION, ACTION, ELSE };
|
||||
Type type = Type::NONE;
|
||||
std::shared_ptr<MacroSegment> segment;
|
||||
};
|
||||
static MacroSegmentCopyInfo copyInfo;
|
||||
|
||||
void AdvSceneSwitcher::CopyMacroSegment()
|
||||
{
|
||||
copyInfo.segment.reset();
|
||||
copyInfo.type = MacroSegmentCopyInfo::Type::NONE;
|
||||
|
||||
if (currentConditionIdx == -1 && currentActionIdx == -1 &&
|
||||
currentElseActionIdx == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentConditionIdx != -1) {
|
||||
copyInfo.type = MacroSegmentCopyInfo::Type::CONDITION;
|
||||
copyInfo.segment = macro->Conditions().at(currentConditionIdx);
|
||||
} else if (currentActionIdx != -1) {
|
||||
copyInfo.type = MacroSegmentCopyInfo::Type::ACTION;
|
||||
copyInfo.segment = macro->Actions().at(currentActionIdx);
|
||||
} else if (currentElseActionIdx != -1) {
|
||||
copyInfo.type = MacroSegmentCopyInfo::Type::ELSE;
|
||||
copyInfo.segment =
|
||||
macro->ElseActions().at(currentElseActionIdx);
|
||||
} else {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::PasteMacroSegment()
|
||||
{
|
||||
if (copyInfo.type == MacroSegmentCopyInfo::Type::NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto macro = GetSelectedMacro();
|
||||
if (!macro || !copyInfo.segment) {
|
||||
return;
|
||||
}
|
||||
|
||||
OBSDataAutoRelease data = obs_data_create();
|
||||
copyInfo.segment->Save(data);
|
||||
|
||||
switch (copyInfo.type) {
|
||||
case MacroSegmentCopyInfo::Type::CONDITION: {
|
||||
const auto condition = std::static_pointer_cast<MacroCondition>(
|
||||
copyInfo.segment);
|
||||
auto logic = condition->GetLogicType();
|
||||
if (logic > LogicType::ROOT_LAST &&
|
||||
macro->Conditions().empty()) {
|
||||
logic = LogicType::ROOT_NONE;
|
||||
}
|
||||
if (logic < LogicType::ROOT_LAST &&
|
||||
!macro->Conditions().empty()) {
|
||||
logic = LogicType::OR;
|
||||
}
|
||||
AddMacroCondition(macro.get(), macro->Conditions().size(),
|
||||
copyInfo.segment->GetId(), data.Get(), logic);
|
||||
break;
|
||||
}
|
||||
case MacroSegmentCopyInfo::Type::ACTION:
|
||||
AddMacroAction(macro.get(), macro->Actions().size(),
|
||||
copyInfo.segment->GetId(), data.Get());
|
||||
break;
|
||||
case MacroSegmentCopyInfo::Type::ELSE:
|
||||
AddMacroElseAction(macro.get(), macro->ElseActions().size(),
|
||||
copyInfo.segment->GetId(), data.Get());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool MacroSegmentIsInClipboard()
|
||||
{
|
||||
return copyInfo.type != MacroSegmentCopyInfo::Type::NONE;
|
||||
}
|
||||
|
||||
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window)
|
||||
{
|
||||
auto copyShortcut = new QShortcut(QKeySequence("Ctrl+C"), window);
|
||||
QWidget::connect(copyShortcut, &QShortcut::activated, window,
|
||||
&AdvSceneSwitcher::CopyMacroSegment);
|
||||
auto pasteShortcut = new QShortcut(QKeySequence("Ctrl+V"), window);
|
||||
QWidget::connect(pasteShortcut, &QShortcut::activated, window,
|
||||
&AdvSceneSwitcher::PasteMacroSegment);
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
8
lib/macro/macro-segment-copy-paste.hpp
Normal file
8
lib/macro/macro-segment-copy-paste.hpp
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace advss {
|
||||
|
||||
class AdvSceneSwitcher;
|
||||
|
||||
bool MacroSegmentIsInClipboard();
|
||||
void SetupSegmentCopyPasteShortcutHandlers(AdvSceneSwitcher *window);
|
||||
|
||||
} // namespace advss
|
||||
@@ -176,7 +176,8 @@ bool MacroSegmentList::eventFilter(QObject *object, QEvent *event)
|
||||
|
||||
void MacroSegmentList::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
if (event->button() == Qt::LeftButton ||
|
||||
event->button() == Qt::RightButton) {
|
||||
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
|
||||
_dragPosition = GetDragIndex(event->globalPos());
|
||||
#else
|
||||
|
||||
@@ -59,12 +59,12 @@ std::string MacroSegment::GetShortDesc() const
|
||||
return "";
|
||||
}
|
||||
|
||||
void MacroSegment::SetHighlight()
|
||||
void MacroSegment::EnableHighlight()
|
||||
{
|
||||
_highlight = true;
|
||||
}
|
||||
|
||||
bool MacroSegment::Highlight()
|
||||
bool MacroSegment::GetHighlightAndReset()
|
||||
{
|
||||
if (_highlight) {
|
||||
_highlight = false;
|
||||
@@ -194,7 +194,7 @@ void DecrementVariableRef(MacroSegment *segment)
|
||||
segment->_variableRefs--;
|
||||
}
|
||||
|
||||
MacroSegmentEdit::MacroSegmentEdit(bool highlight, QWidget *parent)
|
||||
MacroSegmentEdit::MacroSegmentEdit(QWidget *parent)
|
||||
: QWidget(parent),
|
||||
_section(new Section(300)),
|
||||
_headerInfo(new QLabel()),
|
||||
@@ -203,8 +203,7 @@ MacroSegmentEdit::MacroSegmentEdit(bool highlight, QWidget *parent)
|
||||
_noBorderframe(new QFrame),
|
||||
_borderFrame(new QFrame),
|
||||
_dropLineAbove(new QFrame),
|
||||
_dropLineBelow(new QFrame),
|
||||
_showHighlight(highlight)
|
||||
_dropLineBelow(new QFrame)
|
||||
{
|
||||
_dropLineAbove->setLineWidth(3);
|
||||
_dropLineAbove->setFixedHeight(11);
|
||||
@@ -282,10 +281,6 @@ MacroSegmentEdit::MacroSegmentEdit(bool highlight, QWidget *parent)
|
||||
|
||||
// Enable dragging while clicking on the header text
|
||||
_headerInfo->installEventFilter(this);
|
||||
|
||||
_timer.setInterval(1500);
|
||||
connect(&_timer, SIGNAL(timeout()), this, SLOT(Highlight()));
|
||||
_timer.start();
|
||||
}
|
||||
|
||||
bool MacroSegmentEdit::eventFilter(QObject *obj, QEvent *ev)
|
||||
@@ -330,22 +325,6 @@ void MacroSegmentEdit::Collapsed(bool collapsed)
|
||||
}
|
||||
}
|
||||
|
||||
void MacroSegmentEdit::Highlight()
|
||||
{
|
||||
if (!Data()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_showHighlight && Data()->Highlight()) {
|
||||
PulseWidget(this, Qt::green, QColor(0, 0, 0, 0), true);
|
||||
}
|
||||
}
|
||||
|
||||
void MacroSegmentEdit::EnableHighlight(bool value)
|
||||
{
|
||||
_showHighlight = value;
|
||||
}
|
||||
|
||||
void MacroSegmentEdit::SetFocusPolicyOfWidgets()
|
||||
{
|
||||
QList<QWidget *> widgets = this->findChildren<QWidget *>();
|
||||
|
||||
@@ -37,8 +37,8 @@ public:
|
||||
virtual bool PostLoad();
|
||||
virtual std::string GetShortDesc() const;
|
||||
virtual std::string GetId() const = 0;
|
||||
void SetHighlight();
|
||||
bool Highlight();
|
||||
void EnableHighlight();
|
||||
bool GetHighlightAndReset();
|
||||
virtual std::string GetVariableValue() const;
|
||||
|
||||
protected:
|
||||
@@ -87,7 +87,7 @@ class MacroSegmentEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroSegmentEdit(bool highlight, QWidget *parent = nullptr);
|
||||
MacroSegmentEdit(QWidget *parent = nullptr);
|
||||
// Use this function to avoid accidental edits when scrolling through
|
||||
// list of actions and conditions
|
||||
void SetFocusPolicyOfWidgets();
|
||||
@@ -100,8 +100,6 @@ public slots:
|
||||
|
||||
protected slots:
|
||||
void Collapsed(bool);
|
||||
void Highlight();
|
||||
void EnableHighlight(bool);
|
||||
signals:
|
||||
void MacroAdded(const QString &name);
|
||||
void MacroRemoved(const QString &name);
|
||||
@@ -143,9 +141,6 @@ private:
|
||||
QFrame *_dropLineAbove;
|
||||
QFrame *_dropLineBelow;
|
||||
|
||||
bool _showHighlight;
|
||||
QTimer _timer;
|
||||
|
||||
friend class MacroSegmentList;
|
||||
};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "macro-export-import-dialog.hpp"
|
||||
#include "macro-properties.hpp"
|
||||
#include "macro-segment-copy-paste.hpp"
|
||||
#include "macro-tree.hpp"
|
||||
#include "macro.hpp"
|
||||
#include "math-helpers.hpp"
|
||||
@@ -767,8 +768,6 @@ void AdvSceneSwitcher::on_macroProperties_clicked()
|
||||
}
|
||||
GetGlobalMacroProperties() = prop;
|
||||
emit HighlightMacrosChanged(prop._highlightExecuted);
|
||||
emit HighlightActionsChanged(prop._highlightActions);
|
||||
emit HighlightConditionsChanged(prop._highlightConditions);
|
||||
}
|
||||
|
||||
static void moveControlsToSplitter(QSplitter *splitter, int idx,
|
||||
@@ -783,7 +782,7 @@ static void moveControlsToSplitter(QSplitter *splitter, int idx,
|
||||
splitter->setStyleSheet("QSplitter::handle {background: transparent;}");
|
||||
}
|
||||
|
||||
bool shouldRestoreSplitter(const QList<int> &pos)
|
||||
static bool shouldRestoreSplitter(const QList<int> &pos)
|
||||
{
|
||||
if (pos.size() == 0) {
|
||||
return false;
|
||||
@@ -797,6 +796,36 @@ bool shouldRestoreSplitter(const QList<int> &pos)
|
||||
return true;
|
||||
}
|
||||
|
||||
static void runSegmentHighligtChecksHelper(MacroSegmentList *list)
|
||||
{
|
||||
MacroSegmentEdit *widget = nullptr;
|
||||
for (int i = 0; (widget = list->WidgetAt(i)); i++) {
|
||||
if (widget->Data() && widget->Data()->GetHighlightAndReset()) {
|
||||
list->Highlight(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void runSegmentHighligtChecks(AdvSceneSwitcher *ss)
|
||||
{
|
||||
if (!ss || !HighlightUIElementsEnabled()) {
|
||||
return;
|
||||
}
|
||||
auto macro = ss->GetSelectedMacro();
|
||||
if (!macro) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto &properties = GetGlobalMacroProperties();
|
||||
if (properties._highlightConditions) {
|
||||
runSegmentHighligtChecksHelper(ss->ui->conditionsList);
|
||||
}
|
||||
if (properties._highlightActions) {
|
||||
runSegmentHighligtChecksHelper(ss->ui->actionsList);
|
||||
runSegmentHighligtChecksHelper(ss->ui->elseActionsList);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::SetupMacroTab()
|
||||
{
|
||||
ui->macroElseActions->installEventFilter(this);
|
||||
@@ -891,6 +920,14 @@ void AdvSceneSwitcher::SetupMacroTab()
|
||||
switcher->macroListMacroEditSplitterPosition);
|
||||
}
|
||||
}
|
||||
|
||||
SetupSegmentCopyPasteShortcutHandlers(this);
|
||||
|
||||
// Macro segment highlight
|
||||
auto timer = new QTimer(this);
|
||||
connect(timer, &QTimer::timeout,
|
||||
[this]() { runSegmentHighligtChecks(this); });
|
||||
timer->start(1500);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ShowMacroContextMenu(const QPoint &pos)
|
||||
@@ -978,16 +1015,19 @@ static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
|
||||
MacroSegmentList *list)
|
||||
{
|
||||
QMenu menu;
|
||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"),
|
||||
ss, [ss, expand]() { expand(ss); });
|
||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"),
|
||||
ss, [ss, collapse]() { collapse(ss); });
|
||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.maximize"),
|
||||
ss, [ss, maximize]() { maximize(ss); });
|
||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.minimize"),
|
||||
ss, [ss, minimize]() { minimize(ss); });
|
||||
|
||||
auto segmentEdit = list->WidgetAt(pos);
|
||||
|
||||
auto copy = menu.addAction(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.segment.copy"), ss,
|
||||
[ss]() { ss->CopyMacroSegment(); });
|
||||
copy->setEnabled(!!segmentEdit);
|
||||
auto paste = menu.addAction(
|
||||
obs_module_text("AdvSceneSwitcher.macroTab.segment.paste"), ss,
|
||||
[ss]() { ss->PasteMacroSegment(); });
|
||||
paste->setEnabled(MacroSegmentIsInClipboard());
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
if (segmentEdit) {
|
||||
auto customLabel = menu.addAction(obs_module_text(
|
||||
"AdvSceneSwitcher.macroTab.segment.useCustomLabel"));
|
||||
@@ -999,6 +1039,21 @@ static void setupConextMenu(AdvSceneSwitcher *ss, const QPoint &pos,
|
||||
std::bind(handleCustomLabelChange, segmentEdit,
|
||||
customLabel));
|
||||
}
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.expandAll"),
|
||||
ss, [ss, expand]() { expand(ss); });
|
||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.collapseAll"),
|
||||
ss, [ss, collapse]() { collapse(ss); });
|
||||
|
||||
menu.addSeparator();
|
||||
|
||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.maximize"),
|
||||
ss, [ss, maximize]() { maximize(ss); });
|
||||
menu.addAction(obs_module_text("AdvSceneSwitcher.macroTab.minimize"),
|
||||
ss, [ss, minimize]() { minimize(ss); });
|
||||
|
||||
menu.exec(list->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
@@ -1343,58 +1398,47 @@ static void fade(QWidget *widget, bool fadeOut)
|
||||
animation->start(QPropertyAnimation::DeleteWhenStopped);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::FadeOutActionControls()
|
||||
void fadeWidgets(const std::vector<QWidget *> &widgets, bool fadeOut)
|
||||
{
|
||||
fade(ui->actionAdd, true);
|
||||
fade(ui->actionRemove, true);
|
||||
fade(ui->actionTop, true);
|
||||
fade(ui->actionUp, true);
|
||||
fade(ui->actionDown, true);
|
||||
fade(ui->actionBottom, true);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::FadeOutConditionControls()
|
||||
{
|
||||
fade(ui->conditionAdd, true);
|
||||
fade(ui->conditionRemove, true);
|
||||
fade(ui->conditionTop, true);
|
||||
fade(ui->conditionUp, true);
|
||||
fade(ui->conditionDown, true);
|
||||
fade(ui->conditionBottom, true);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ResetOpacityActionControls()
|
||||
{
|
||||
fade(ui->actionAdd, false);
|
||||
fade(ui->actionRemove, false);
|
||||
fade(ui->actionTop, false);
|
||||
fade(ui->actionUp, false);
|
||||
fade(ui->actionDown, false);
|
||||
fade(ui->actionBottom, false);
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::ResetOpacityConditionControls()
|
||||
{
|
||||
fade(ui->conditionAdd, false);
|
||||
fade(ui->conditionRemove, false);
|
||||
fade(ui->conditionTop, false);
|
||||
fade(ui->conditionUp, false);
|
||||
fade(ui->conditionDown, false);
|
||||
fade(ui->conditionBottom, false);
|
||||
for (const auto &widget : widgets) {
|
||||
fade(widget, fadeOut);
|
||||
}
|
||||
}
|
||||
|
||||
void AdvSceneSwitcher::HighlightControls()
|
||||
{
|
||||
if ((currentActionIdx == -1 && currentConditionIdx == -1) ||
|
||||
(currentActionIdx != -1 && currentConditionIdx != -1)) {
|
||||
ResetOpacityActionControls();
|
||||
ResetOpacityConditionControls();
|
||||
const std::vector<QWidget *> conditionControls{
|
||||
ui->conditionAdd, ui->conditionRemove, ui->conditionTop,
|
||||
ui->conditionUp, ui->conditionDown, ui->conditionBottom,
|
||||
};
|
||||
const std::vector<QWidget *> actionControls{
|
||||
ui->actionAdd, ui->actionRemove, ui->actionTop,
|
||||
ui->actionUp, ui->actionDown, ui->actionBottom,
|
||||
};
|
||||
const std::vector<QWidget *> elseActionControls{
|
||||
ui->elseActionAdd, ui->elseActionRemove, ui->elseActionTop,
|
||||
ui->elseActionUp, ui->elseActionDown, ui->elseActionBottom,
|
||||
};
|
||||
|
||||
if ((currentActionIdx == -1 && currentConditionIdx == -1 &&
|
||||
currentElseActionIdx == -1)) {
|
||||
fadeWidgets(conditionControls, false);
|
||||
fadeWidgets(actionControls, false);
|
||||
fadeWidgets(elseActionControls, false);
|
||||
} else if (currentConditionIdx != -1) {
|
||||
fadeWidgets(conditionControls, false);
|
||||
fadeWidgets(actionControls, true);
|
||||
fadeWidgets(elseActionControls, true);
|
||||
} else if (currentActionIdx != -1) {
|
||||
FadeOutConditionControls();
|
||||
ResetOpacityActionControls();
|
||||
fadeWidgets(conditionControls, true);
|
||||
fadeWidgets(actionControls, false);
|
||||
fadeWidgets(elseActionControls, true);
|
||||
} else if (currentElseActionIdx != -1) {
|
||||
fadeWidgets(conditionControls, true);
|
||||
fadeWidgets(actionControls, true);
|
||||
fadeWidgets(elseActionControls, false);
|
||||
} else {
|
||||
FadeOutActionControls();
|
||||
ResetOpacityConditionControls();
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -137,37 +137,37 @@ bool Macro::CeckMatch()
|
||||
case LogicType::AND:
|
||||
_matched = _matched && cond;
|
||||
if (cond) {
|
||||
c->SetHighlight();
|
||||
c->EnableHighlight();
|
||||
}
|
||||
break;
|
||||
case LogicType::OR:
|
||||
_matched = _matched || cond;
|
||||
if (cond) {
|
||||
c->SetHighlight();
|
||||
c->EnableHighlight();
|
||||
}
|
||||
break;
|
||||
case LogicType::AND_NOT:
|
||||
_matched = _matched && !cond;
|
||||
if (!cond) {
|
||||
c->SetHighlight();
|
||||
c->EnableHighlight();
|
||||
}
|
||||
break;
|
||||
case LogicType::OR_NOT:
|
||||
_matched = _matched || !cond;
|
||||
if (!cond) {
|
||||
c->SetHighlight();
|
||||
c->EnableHighlight();
|
||||
}
|
||||
break;
|
||||
case LogicType::ROOT_NONE:
|
||||
_matched = cond;
|
||||
if (cond) {
|
||||
c->SetHighlight();
|
||||
c->EnableHighlight();
|
||||
}
|
||||
break;
|
||||
case LogicType::ROOT_NOT:
|
||||
_matched = !cond;
|
||||
if (!cond) {
|
||||
c->SetHighlight();
|
||||
c->EnableHighlight();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
@@ -313,7 +313,7 @@ bool Macro::RunActionsHelper(
|
||||
break;
|
||||
}
|
||||
if (action->Enabled()) {
|
||||
action->SetHighlight();
|
||||
action->EnableHighlight();
|
||||
}
|
||||
}
|
||||
_done = true;
|
||||
@@ -819,10 +819,10 @@ void Macro::ResetUIHelpers()
|
||||
{
|
||||
_onPreventedActionExecution = false;
|
||||
for (auto c : _conditions) {
|
||||
c->Highlight();
|
||||
c->GetHighlightAndReset();
|
||||
}
|
||||
for (auto a : _actions) {
|
||||
a->Highlight();
|
||||
a->GetHighlightAndReset();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,19 +11,16 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
static bool registerTab();
|
||||
static void setupTab(QTabWidget *);
|
||||
static bool registerTabDone = registerTab();
|
||||
|
||||
static ActionQueueTable *tabWidget = nullptr;
|
||||
|
||||
static bool registerTab()
|
||||
void RegisterActionQueueTab()
|
||||
{
|
||||
AddPluginInitStep([]() {
|
||||
AddSetupTabCallback("actionQueueTab", ActionQueueTable::Create,
|
||||
setupTab);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
static void setTabVisible(QTabWidget *tabWidget, bool visible)
|
||||
@@ -70,11 +67,11 @@ static QStringList getCellLabels(ActionQueue *queue, bool addName = true)
|
||||
<< QString::fromStdString(obs_module_text(
|
||||
queue->IsRunning()
|
||||
? "AdvSceneSwitcher.actionQueueTab.yes"
|
||||
: "AdvSceneSwitcher.actionQueueTab.yes"))
|
||||
: "AdvSceneSwitcher.actionQueueTab.no"))
|
||||
<< QString::fromStdString(obs_module_text(
|
||||
queue->RunsOnStartup()
|
||||
? "AdvSceneSwitcher.actionQueueTab.yes"
|
||||
: "AdvSceneSwitcher.actionQueueTab.yes"));
|
||||
: "AdvSceneSwitcher.actionQueueTab.no"));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ std::deque<std::shared_ptr<Item>> &GetActionQueues()
|
||||
return queues;
|
||||
}
|
||||
|
||||
void RegisterActionQueueTab();
|
||||
|
||||
void SetupActionQueues()
|
||||
{
|
||||
static bool done = false;
|
||||
@@ -19,6 +21,7 @@ void SetupActionQueues()
|
||||
}
|
||||
AddSaveStep(SaveActionQueues);
|
||||
AddLoadStep(LoadActionQueues);
|
||||
RegisterActionQueueTab();
|
||||
done = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
|
||||
namespace advss {
|
||||
|
||||
EXPORT std::mutex *GetMutex();
|
||||
EXPORT std::lock_guard<std::mutex> LockContext();
|
||||
EXPORT std::unique_lock<std::mutex> *GetLoopLock();
|
||||
[[nodiscard]] EXPORT std::mutex *GetMutex();
|
||||
[[nodiscard]] EXPORT std::lock_guard<std::mutex> LockContext();
|
||||
[[nodiscard]] EXPORT std::unique_lock<std::mutex> *GetLoopLock();
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -65,6 +65,7 @@ public:
|
||||
EXPORT void Load(obs_data_t *, Macro *, const char *name = "tempVar");
|
||||
EXPORT std::optional<const TempVariable> GetTempVariable(Macro *) const;
|
||||
EXPORT bool operator==(const TempVariableRef &other) const;
|
||||
bool HasValidID() const { return !_id.empty(); }
|
||||
|
||||
private:
|
||||
enum class SegmentType { NONE, CONDITION, ACTION, ELSEACTION };
|
||||
|
||||
@@ -87,6 +87,8 @@ target_sources(
|
||||
macro-condition-file.hpp
|
||||
macro-condition-filter.cpp
|
||||
macro-condition-filter.hpp
|
||||
macro-condition-folder.cpp
|
||||
macro-condition-folder.hpp
|
||||
macro-condition-hotkey.cpp
|
||||
macro-condition-hotkey.hpp
|
||||
macro-condition-idle.cpp
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "layout-helpers.hpp"
|
||||
#include "platform-funcs.hpp"
|
||||
#include "selection-helpers.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
|
||||
namespace advss {
|
||||
|
||||
@@ -163,7 +164,8 @@ MacroActionWindowEdit::MacroActionWindowEdit(
|
||||
: QWidget(parent),
|
||||
_actions(new QComboBox()),
|
||||
_windows(new QComboBox()),
|
||||
_regex(new RegexConfigWidget(this))
|
||||
_regex(new RegexConfigWidget(this)),
|
||||
_infoLayout(new QHBoxLayout())
|
||||
{
|
||||
populateActionSelection(_actions);
|
||||
|
||||
@@ -171,6 +173,22 @@ MacroActionWindowEdit::MacroActionWindowEdit(
|
||||
_windows->setMaxVisibleItems(20);
|
||||
PopulateWindowSelection(_windows);
|
||||
|
||||
auto focusLimitation = new QLabel(obs_module_text(
|
||||
"AdvSceneSwitcher.action.window.type.setFocusWindow.limitation"));
|
||||
_infoLayout->addWidget(focusLimitation);
|
||||
|
||||
const QString path = GetThemeTypeName() == "Light"
|
||||
? ":/res/images/help.svg"
|
||||
: ":/res/images/help_light.svg";
|
||||
const QIcon icon(path);
|
||||
const QPixmap pixmap = icon.pixmap(QSize(16, 16));
|
||||
auto focusLimitationDetails = new QLabel();
|
||||
focusLimitationDetails->setPixmap(pixmap);
|
||||
focusLimitationDetails->setToolTip(obs_module_text(
|
||||
"AdvSceneSwitcher.action.window.type.setFocusWindow.limitation.details"));
|
||||
_infoLayout->addWidget(focusLimitationDetails);
|
||||
_infoLayout->addStretch();
|
||||
|
||||
QWidget::connect(_actions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ActionChanged(int)));
|
||||
QWidget::connect(_windows, SIGNAL(currentTextChanged(const QString &)),
|
||||
@@ -179,12 +197,15 @@ MacroActionWindowEdit::MacroActionWindowEdit(
|
||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||
SLOT(RegexChanged(const RegexConfig &)));
|
||||
|
||||
auto layout = new QHBoxLayout;
|
||||
auto entryLayout = new QHBoxLayout;
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.action.window.entry"),
|
||||
layout,
|
||||
entryLayout,
|
||||
{{"{{actions}}", _actions},
|
||||
{"{{windows}}", _windows},
|
||||
{"{{regex}}", _regex}});
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->addLayout(entryLayout);
|
||||
layout->addLayout(_infoLayout);
|
||||
setLayout(layout);
|
||||
|
||||
_entryData = entryData;
|
||||
@@ -200,8 +221,7 @@ void MacroActionWindowEdit::UpdateEntryData()
|
||||
_actions->setCurrentIndex(static_cast<int>(_entryData->_action));
|
||||
_windows->setCurrentText(QString::fromStdString(_entryData->_window));
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroActionWindowEdit::WindowChanged(const QString &text)
|
||||
@@ -228,6 +248,15 @@ void MacroActionWindowEdit::RegexChanged(const RegexConfig ®ex)
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionWindowEdit::SetWidgetVisibility()
|
||||
{
|
||||
SetLayoutVisible(_infoLayout,
|
||||
_entryData->_action ==
|
||||
MacroActionWindow::Action::SET_FOCUS_WINDOW);
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
void MacroActionWindowEdit::ActionChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
@@ -236,6 +265,7 @@ void MacroActionWindowEdit::ActionChanged(int value)
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_action = static_cast<MacroActionWindow::Action>(value);
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
@@ -59,9 +59,12 @@ signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QComboBox *_actions;
|
||||
QComboBox *_windows;
|
||||
RegexConfigWidget *_regex;
|
||||
QHBoxLayout *_infoLayout;
|
||||
|
||||
std::shared_ptr<MacroActionWindow> _entryData;
|
||||
bool _loading = true;
|
||||
|
||||
@@ -61,13 +61,49 @@ MacroConditionAudio::~MacroConditionAudio()
|
||||
obs_volmeter_destroy(_volmeter);
|
||||
}
|
||||
|
||||
float MacroConditionAudio::GetVolumePeak()
|
||||
{
|
||||
using namespace std::chrono_literals;
|
||||
using namespace std::chrono;
|
||||
static constexpr std::chrono::milliseconds timeout = 250ms;
|
||||
|
||||
// OBS might rarely not update _peak quickly enough when very low
|
||||
// intervals are configured on the General tab.
|
||||
// In that case _peak might be set to negative infinity still, which
|
||||
// will result in unexpected behavior, so we use the previously valid
|
||||
// peak value instead.
|
||||
//
|
||||
// If no volume update was received within a timeout window, however, it
|
||||
// is assumed, that the source no longer produces any audio output and
|
||||
// thus a peak volume value of negative infinity is used.
|
||||
|
||||
float peak;
|
||||
|
||||
std::lock_guard<std::mutex> lock(_peakMutex);
|
||||
auto msPassedSinceLastUpdate = duration_cast<milliseconds>(
|
||||
high_resolution_clock::now() - _lastPeakUpdate);
|
||||
if (_lastPeakUpdate.time_since_epoch().count() != 0 &&
|
||||
msPassedSinceLastUpdate > timeout) {
|
||||
peak = -std::numeric_limits<float>::infinity();
|
||||
} else {
|
||||
peak = _peakUpdated ? _peak : _previousPeak;
|
||||
}
|
||||
|
||||
_previousPeak = peak;
|
||||
_peak = -std::numeric_limits<float>::infinity();
|
||||
_peakUpdated = false;
|
||||
|
||||
return peak;
|
||||
}
|
||||
|
||||
bool MacroConditionAudio::CheckOutputCondition()
|
||||
{
|
||||
bool ret = false;
|
||||
OBSSourceAutoRelease source =
|
||||
obs_weak_source_get_source(_audioSource.GetSource());
|
||||
|
||||
double curVolume = _useDb ? _peak : DecibelToPercent(_peak) * 100;
|
||||
float peak = GetVolumePeak();
|
||||
double curVolume = _useDb ? peak : DecibelToPercent(peak) * 100;
|
||||
|
||||
switch (_outputCondition) {
|
||||
case OutputCondition::ABOVE:
|
||||
@@ -92,7 +128,6 @@ bool MacroConditionAudio::CheckOutputCondition()
|
||||
SetTempVarValue("output_volume", std::to_string(curVolume));
|
||||
|
||||
// Reset for next check
|
||||
_peak = -std::numeric_limits<float>::infinity();
|
||||
if (_audioSource.GetType() == SourceSelection::Type::VARIABLE) {
|
||||
ResetVolmeter();
|
||||
}
|
||||
@@ -329,11 +364,14 @@ void MacroConditionAudio::SetVolumeLevel(void *data, const float *,
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard<std::mutex> lock(c->_peakMutex);
|
||||
for (int i = 0; i < MAX_AUDIO_CHANNELS; i++) {
|
||||
if (peak[i] > c->_peak) {
|
||||
c->_peak = peak[i];
|
||||
}
|
||||
}
|
||||
c->_peakUpdated = true;
|
||||
c->_lastPeakUpdate = std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
void MacroConditionAudio::ResetVolmeter()
|
||||
|
||||
@@ -71,9 +71,14 @@ private:
|
||||
bool CheckMonitor();
|
||||
bool CheckBalance();
|
||||
void SetupTempVars();
|
||||
float GetVolumePeak();
|
||||
|
||||
Type _checkType = Type::OUTPUT_VOLUME;
|
||||
std::mutex _peakMutex;
|
||||
float _peak = -std::numeric_limits<float>::infinity();
|
||||
float _previousPeak = -std::numeric_limits<float>::infinity();
|
||||
bool _peakUpdated = false;
|
||||
std::chrono::high_resolution_clock::time_point _lastPeakUpdate = {};
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
414
plugins/base/macro-condition-folder.cpp
Normal file
414
plugins/base/macro-condition-folder.cpp
Normal file
@@ -0,0 +1,414 @@
|
||||
#include "macro-condition-folder.hpp"
|
||||
#include "macro-helpers.hpp"
|
||||
#include "layout-helpers.hpp"
|
||||
#include "ui-helpers.hpp"
|
||||
|
||||
#include <QDir>
|
||||
#include <QFileInfo>
|
||||
|
||||
namespace advss {
|
||||
|
||||
const std::string MacroConditionFolder::id = "folder";
|
||||
|
||||
bool MacroConditionFolder::_registered = MacroConditionFactory::Register(
|
||||
MacroConditionFolder::id,
|
||||
{MacroConditionFolder::Create, MacroConditionFolderEdit::Create,
|
||||
"AdvSceneSwitcher.condition.folder"});
|
||||
|
||||
static const std::map<MacroConditionFolder::Condition, std::string> conditions =
|
||||
{
|
||||
{MacroConditionFolder::Condition::ANY,
|
||||
"AdvSceneSwitcher.condition.folder.condition.any"},
|
||||
{MacroConditionFolder::Condition::FILE_ADD,
|
||||
"AdvSceneSwitcher.condition.folder.condition.fileAdd"},
|
||||
{MacroConditionFolder::Condition::FILE_CHANGE,
|
||||
"AdvSceneSwitcher.condition.folder.condition.fileChange"},
|
||||
{MacroConditionFolder::Condition::FILE_REMOVE,
|
||||
"AdvSceneSwitcher.condition.folder.condition.fileRemove"},
|
||||
{MacroConditionFolder::Condition::FOLDER_ADD,
|
||||
"AdvSceneSwitcher.condition.folder.condition.folderAdd"},
|
||||
{MacroConditionFolder::Condition::FOLDER_REMOVE,
|
||||
"AdvSceneSwitcher.condition.folder.condition.folderRemove"},
|
||||
};
|
||||
|
||||
MacroConditionFolder::MacroConditionFolder(Macro *m) : MacroCondition(m, true)
|
||||
{
|
||||
}
|
||||
|
||||
bool MacroConditionFolder::CheckCondition()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
bool ret = _matched;
|
||||
if (_lastWatchedValue != _folder.UnresolvedValue()) {
|
||||
SetupWatcher();
|
||||
}
|
||||
|
||||
SetTempVarValues();
|
||||
|
||||
_newFiles.clear();
|
||||
_changedFiles.clear();
|
||||
_removedFiles.clear();
|
||||
_newDirs.clear();
|
||||
_removedDirs.clear();
|
||||
_matched = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool MacroConditionFolder::Save(obs_data_t *obj) const
|
||||
{
|
||||
MacroCondition::Save(obj);
|
||||
_folder.Save(obj, "file");
|
||||
obs_data_set_bool(obj, "enableFilter", _enableFilter);
|
||||
_regex.Save(obj);
|
||||
_filter.Save(obj, "filter");
|
||||
obs_data_set_int(obj, "condition", static_cast<int>(_condition));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MacroConditionFolder::Load(obs_data_t *obj)
|
||||
{
|
||||
MacroCondition::Load(obj);
|
||||
_folder.Load(obj, "file");
|
||||
_enableFilter = obs_data_get_bool(obj, "enableFilter");
|
||||
_regex.Load(obj);
|
||||
_regex.SetEnabled(true); // Already controlled via _enableFilter
|
||||
_filter.Load(obj, "filter");
|
||||
_condition = static_cast<Condition>(obs_data_get_int(obj, "condition"));
|
||||
SetupWatcher();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string MacroConditionFolder::GetShortDesc() const
|
||||
{
|
||||
return _folder.UnresolvedValue();
|
||||
}
|
||||
|
||||
void MacroConditionFolder::SetFolder(const std::string &folder)
|
||||
{
|
||||
_folder = folder;
|
||||
SetupWatcher();
|
||||
}
|
||||
|
||||
static QSet<QString> getFilesInDir(const QString &path)
|
||||
{
|
||||
QSet<QString> result;
|
||||
for (const auto &file : QDir(path).entryList(QDir::Files)) {
|
||||
result << file;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static QSet<QString> getDirsInDir(const QString &path)
|
||||
{
|
||||
QSet<QString> result;
|
||||
for (const auto &dir : QDir(path).entryList(QDir::AllDirs)) {
|
||||
result << dir;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void reduceSetToPatternMatch(QSet<QString> &set,
|
||||
const RegexConfig ®ex,
|
||||
const std::string &pattern)
|
||||
{
|
||||
QSet<QString> copy = set;
|
||||
for (const auto &value : copy) {
|
||||
if (!regex.Matches(value.toStdString(), pattern)) {
|
||||
set.remove(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MacroConditionFolder::DirectoryChanged(const QString &path)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
if (MacroIsPaused(GetMacro())) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto currentFiles = getFilesInDir(path);
|
||||
auto currentDirs = getDirsInDir(path);
|
||||
|
||||
if (currentFiles.count() > _currentFiles.count()) {
|
||||
_newFiles += currentFiles - _currentFiles;
|
||||
} else {
|
||||
_removedFiles += _currentFiles - currentFiles;
|
||||
}
|
||||
|
||||
if (currentDirs.count() > _currentDirs.count()) {
|
||||
_newDirs += currentDirs - _currentDirs;
|
||||
} else {
|
||||
_removedDirs += _currentDirs - currentDirs;
|
||||
}
|
||||
|
||||
if (_enableFilter) {
|
||||
reduceSetToPatternMatch(_newFiles, _regex, _filter);
|
||||
reduceSetToPatternMatch(_removedFiles, _regex, _filter);
|
||||
reduceSetToPatternMatch(_newDirs, _regex, _filter);
|
||||
reduceSetToPatternMatch(_removedDirs, _regex, _filter);
|
||||
}
|
||||
|
||||
switch (_condition) {
|
||||
case Condition::ANY:
|
||||
_matched = _matched || _newFiles.count() > 0 ||
|
||||
_removedFiles.count() > 0 || _newDirs.count() > 0 ||
|
||||
_removedDirs.count() > 0;
|
||||
break;
|
||||
case Condition::FILE_ADD:
|
||||
_matched = _newFiles.count() > 0;
|
||||
break;
|
||||
case Condition::FILE_REMOVE:
|
||||
_matched = _removedFiles.count() > 0;
|
||||
break;
|
||||
case Condition::FOLDER_ADD:
|
||||
_matched = _newDirs.count() > 0;
|
||||
break;
|
||||
case Condition::FOLDER_REMOVE:
|
||||
_matched = _removedDirs.count() > 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
for (const auto &newFile : _newFiles) {
|
||||
_watcher->addPath(path + "/" + newFile);
|
||||
}
|
||||
|
||||
_currentFiles = currentFiles;
|
||||
_currentDirs = currentDirs;
|
||||
}
|
||||
|
||||
void MacroConditionFolder::FileChanged(const QString &path)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(_mutex);
|
||||
QFileInfo fileInfo(path);
|
||||
if (!fileInfo.exists()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto fileName = fileInfo.fileName();
|
||||
if (_enableFilter && !_regex.Matches(fileName.toStdString(), _filter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
_changedFiles << fileName;
|
||||
if (_condition == Condition::FILE_CHANGE ||
|
||||
_condition == Condition::ANY) {
|
||||
_matched = true;
|
||||
}
|
||||
}
|
||||
|
||||
static QStringList getFileWatcherList(const QSet<QString> &files,
|
||||
const QString &dirName)
|
||||
{
|
||||
QStringList list;
|
||||
for (const auto &value : files) {
|
||||
list << (dirName + "/" + value);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
void MacroConditionFolder::SetupWatcher()
|
||||
{
|
||||
_watcher = std::make_unique<QFileSystemWatcher>();
|
||||
const auto path = QString::fromStdString(_folder);
|
||||
_currentFiles = getFilesInDir(path);
|
||||
_currentDirs = getDirsInDir(path);
|
||||
_lastWatchedValue = _folder.UnresolvedValue();
|
||||
connect(_watcher.get(), SIGNAL(directoryChanged(const QString &)), this,
|
||||
SLOT(DirectoryChanged(const QString &)));
|
||||
connect(_watcher.get(), SIGNAL(fileChanged(const QString &)), this,
|
||||
SLOT(FileChanged(const QString &)));
|
||||
_watcher.get()->addPaths(getFileWatcherList(_currentFiles, path));
|
||||
_watcher->addPath(path);
|
||||
}
|
||||
|
||||
void MacroConditionFolder::SetTempVarValues()
|
||||
{
|
||||
auto setVarHelper = [this](const QSet<QString> &set,
|
||||
const std::string &id) {
|
||||
std::string result;
|
||||
for (const auto &value : set) {
|
||||
result += value.toStdString() + "\n";
|
||||
}
|
||||
if (result.size() > 0) {
|
||||
result.pop_back();
|
||||
}
|
||||
SetTempVarValue(id, result);
|
||||
};
|
||||
|
||||
setVarHelper(_newFiles, "newFiles");
|
||||
setVarHelper(_changedFiles, "changedFiles");
|
||||
setVarHelper(_removedFiles, "removedFiles");
|
||||
setVarHelper(_newDirs, "newDirs");
|
||||
setVarHelper(_removedDirs, "removedDirs");
|
||||
}
|
||||
|
||||
void MacroConditionFolder::SetupTempVars()
|
||||
{
|
||||
MacroCondition::SetupTempVars();
|
||||
AddTempvar("newFiles",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.folder.newFiles"));
|
||||
AddTempvar("changedFiles",
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.folder.changedFiles"));
|
||||
AddTempvar("removedFiles",
|
||||
obs_module_text(
|
||||
"AdvSceneSwitcher.tempVar.folder.removedFiles"));
|
||||
AddTempvar("newDirs",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.folder.newDirs"));
|
||||
AddTempvar(
|
||||
"removedDirs",
|
||||
obs_module_text("AdvSceneSwitcher.tempVar.folder.removedDirs"));
|
||||
}
|
||||
|
||||
static void populateConditions(QComboBox *list)
|
||||
{
|
||||
for (const auto &[value, name] : conditions) {
|
||||
list->addItem(obs_module_text(name.c_str()),
|
||||
static_cast<int>(value));
|
||||
}
|
||||
}
|
||||
|
||||
MacroConditionFolderEdit::MacroConditionFolderEdit(
|
||||
QWidget *parent, std::shared_ptr<MacroConditionFolder> entryData)
|
||||
: QWidget(parent),
|
||||
_conditions(new QComboBox()),
|
||||
_folder(new FileSelection(FileSelection::Type::FOLDER)),
|
||||
_enableFilter(new QCheckBox(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.folder.enableFilter"))),
|
||||
_filterLayout(new QHBoxLayout()),
|
||||
_regex(new RegexConfigWidget(this, false)),
|
||||
_filter(new VariableLineEdit(this))
|
||||
{
|
||||
QString path = GetThemeTypeName() == "Light"
|
||||
? ":/res/images/help.svg"
|
||||
: ":/res/images/help_light.svg";
|
||||
QIcon icon(path);
|
||||
QPixmap pixmap = icon.pixmap(QSize(16, 16));
|
||||
auto tooltipLabel = new QLabel(this);
|
||||
tooltipLabel->setPixmap(pixmap);
|
||||
tooltipLabel->setToolTip(
|
||||
obs_module_text("AdvSceneSwitcher.condition.folder.tooltip"));
|
||||
|
||||
populateConditions(_conditions);
|
||||
|
||||
QWidget::connect(_conditions, SIGNAL(currentIndexChanged(int)), this,
|
||||
SLOT(ConditionChanged(int)));
|
||||
QWidget::connect(_folder, SIGNAL(PathChanged(const QString &)), this,
|
||||
SLOT(PathChanged(const QString &)));
|
||||
QWidget::connect(_enableFilter, SIGNAL(stateChanged(int)), this,
|
||||
SLOT(EnableFilterChanged(int)));
|
||||
QWidget::connect(_regex,
|
||||
SIGNAL(RegexConfigChanged(const RegexConfig &)), this,
|
||||
SLOT(RegexChanged(const RegexConfig &)));
|
||||
QWidget::connect(_filter, SIGNAL(editingFinished()), this,
|
||||
SLOT(FilterChanged()));
|
||||
|
||||
const std::unordered_map<std::string, QWidget *> widgetPlaceholders = {
|
||||
{"{{conditions}}", _conditions}, {"{{folder}}", _folder},
|
||||
{"{{tooltip}}", tooltipLabel}, {"{{regex}}", _regex},
|
||||
{"{{filter}}", _filter},
|
||||
};
|
||||
|
||||
auto entryLayout = new QHBoxLayout();
|
||||
entryLayout->setContentsMargins(0, 0, 0, 0);
|
||||
PlaceWidgets(obs_module_text("AdvSceneSwitcher.condition.folder.entry"),
|
||||
entryLayout, widgetPlaceholders, false);
|
||||
_filterLayout->setContentsMargins(0, 0, 0, 0);
|
||||
PlaceWidgets(obs_module_text(
|
||||
"AdvSceneSwitcher.condition.folder.entry.filter"),
|
||||
_filterLayout, widgetPlaceholders, false);
|
||||
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->addLayout(entryLayout);
|
||||
layout->addWidget(_enableFilter);
|
||||
layout->addLayout(_filterLayout);
|
||||
setLayout(layout);
|
||||
|
||||
_entryData = entryData;
|
||||
UpdateEntryData();
|
||||
_loading = false;
|
||||
}
|
||||
|
||||
void MacroConditionFolderEdit::UpdateEntryData()
|
||||
{
|
||||
if (!_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_conditions->setCurrentIndex(_conditions->findData(
|
||||
static_cast<int>(_entryData->_condition)));
|
||||
_folder->SetPath(_entryData->GetFolder());
|
||||
_enableFilter->setChecked(_entryData->_enableFilter);
|
||||
_regex->SetRegexConfig(_entryData->_regex);
|
||||
_filter->setText(_entryData->_filter);
|
||||
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionFolderEdit::ConditionChanged(int index)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_condition = static_cast<MacroConditionFolder::Condition>(
|
||||
_conditions->itemData(index).toInt());
|
||||
}
|
||||
|
||||
void MacroConditionFolderEdit::PathChanged(const QString &text)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->SetFolder(text.toStdString());
|
||||
emit HeaderInfoChanged(
|
||||
QString::fromStdString(_entryData->GetShortDesc()));
|
||||
}
|
||||
|
||||
void MacroConditionFolderEdit::RegexChanged(const RegexConfig ®ex)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_regex = regex;
|
||||
_entryData->_regex.SetEnabled(true);
|
||||
}
|
||||
|
||||
void MacroConditionFolderEdit::FilterChanged()
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_filter = _filter->text().toStdString();
|
||||
}
|
||||
|
||||
void MacroConditionFolderEdit::EnableFilterChanged(int value)
|
||||
{
|
||||
if (_loading || !_entryData) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto lock = LockContext();
|
||||
_entryData->_enableFilter = value;
|
||||
SetWidgetVisibility();
|
||||
}
|
||||
|
||||
void MacroConditionFolderEdit::SetWidgetVisibility()
|
||||
{
|
||||
SetLayoutVisible(_filterLayout, _entryData->_enableFilter);
|
||||
|
||||
adjustSize();
|
||||
updateGeometry();
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
109
plugins/base/macro-condition-folder.hpp
Normal file
109
plugins/base/macro-condition-folder.hpp
Normal file
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
#include "macro-condition-edit.hpp"
|
||||
#include "file-selection.hpp"
|
||||
#include "regex-config.hpp"
|
||||
#include "variable-line-edit.hpp"
|
||||
|
||||
#include <QFileSystemWatcher>
|
||||
|
||||
namespace advss {
|
||||
|
||||
class MacroConditionFolder : public QObject, public MacroCondition {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionFolder(Macro *m);
|
||||
bool CheckCondition();
|
||||
bool Save(obs_data_t *obj) const;
|
||||
bool Load(obs_data_t *obj);
|
||||
std::string GetShortDesc() const;
|
||||
std::string GetId() const { return id; };
|
||||
static std::shared_ptr<MacroCondition> Create(Macro *m)
|
||||
{
|
||||
return std::make_shared<MacroConditionFolder>(m);
|
||||
}
|
||||
void SetFolder(const std::string &);
|
||||
StringVariable GetFolder() const { return _folder; }
|
||||
|
||||
enum class Condition {
|
||||
ANY,
|
||||
FILE_ADD,
|
||||
FILE_CHANGE,
|
||||
FILE_REMOVE,
|
||||
FOLDER_ADD,
|
||||
FOLDER_REMOVE,
|
||||
};
|
||||
|
||||
Condition _condition = Condition::ANY;
|
||||
bool _enableFilter = false;
|
||||
RegexConfig _regex = RegexConfig(true);
|
||||
StringVariable _filter = ".*";
|
||||
|
||||
private slots:
|
||||
void DirectoryChanged(const QString &);
|
||||
void FileChanged(const QString &);
|
||||
|
||||
private:
|
||||
void SetupWatcher();
|
||||
void SetTempVarValues();
|
||||
void SetupTempVars();
|
||||
|
||||
StringVariable _folder = obs_module_text("AdvSceneSwitcher.enterPath");
|
||||
|
||||
std::unique_ptr<QFileSystemWatcher> _watcher;
|
||||
std::string _lastWatchedValue = "";
|
||||
|
||||
std::mutex _mutex;
|
||||
bool _matched = false;
|
||||
QSet<QString> _newFiles;
|
||||
QSet<QString> _changedFiles;
|
||||
QSet<QString> _removedFiles;
|
||||
QSet<QString> _newDirs;
|
||||
QSet<QString> _removedDirs;
|
||||
QSet<QString> _currentFiles;
|
||||
QSet<QString> _currentDirs;
|
||||
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
};
|
||||
|
||||
class MacroConditionFolderEdit : public QWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MacroConditionFolderEdit(
|
||||
QWidget *parent,
|
||||
std::shared_ptr<MacroConditionFolder> cond = nullptr);
|
||||
void UpdateEntryData();
|
||||
static QWidget *Create(QWidget *parent,
|
||||
std::shared_ptr<MacroCondition> cond)
|
||||
{
|
||||
return new MacroConditionFolderEdit(
|
||||
parent,
|
||||
std::dynamic_pointer_cast<MacroConditionFolder>(cond));
|
||||
}
|
||||
|
||||
private slots:
|
||||
void ConditionChanged(int index);
|
||||
void PathChanged(const QString &text);
|
||||
void EnableFilterChanged(int value);
|
||||
void RegexChanged(const RegexConfig &);
|
||||
void FilterChanged();
|
||||
signals:
|
||||
void HeaderInfoChanged(const QString &);
|
||||
|
||||
private:
|
||||
void SetWidgetVisibility();
|
||||
|
||||
QComboBox *_conditions;
|
||||
FileSelection *_folder;
|
||||
QCheckBox *_enableFilter;
|
||||
QHBoxLayout *_filterLayout;
|
||||
RegexConfigWidget *_regex;
|
||||
VariableLineEdit *_filter;
|
||||
|
||||
std::shared_ptr<MacroConditionFolder> _entryData;
|
||||
bool _loading = true;
|
||||
};
|
||||
|
||||
} // namespace advss
|
||||
@@ -45,11 +45,13 @@ static bool setupReplayBufferEventHandler()
|
||||
|
||||
bool MacroConditionReplayBuffer::ReplayBufferWasSaved()
|
||||
{
|
||||
bool timersAreInitialized =
|
||||
_saveTime.time_since_epoch().count() != 0 &&
|
||||
replayBufferSaveTime.time_since_epoch().count() != 0;
|
||||
bool newSaveOccurred = timersAreInitialized &&
|
||||
_saveTime != replayBufferSaveTime;
|
||||
if (!_saveTimeInitialized) {
|
||||
_saveTime = replayBufferSaveTime;
|
||||
_saveTimeInitialized = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool newSaveOccurred = _saveTime != replayBufferSaveTime;
|
||||
_saveTime = replayBufferSaveTime;
|
||||
return newSaveOccurred;
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ public:
|
||||
private:
|
||||
bool ReplayBufferWasSaved();
|
||||
|
||||
bool _saveTimeInitialized = false;
|
||||
std::chrono::high_resolution_clock::time_point _saveTime = {};
|
||||
static bool _registered;
|
||||
static const std::string id;
|
||||
|
||||
@@ -175,22 +175,43 @@ void PressKeys(const std::vector<HotkeyType> keys, int duration)
|
||||
}
|
||||
}
|
||||
|
||||
static HWND getHWNDfromTitle(const std::string &title)
|
||||
static bool windowIsValid(HWND window)
|
||||
{
|
||||
HWND hwnd = NULL;
|
||||
if (!IsWindowVisible(window)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Only accept for top level windows and not controls within them
|
||||
if (window != GetAncestor(window, GA_ROOT)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static std::vector<HWND> getHWNDfromTitle(const std::string &title)
|
||||
{
|
||||
HWND hwnd = nullptr;
|
||||
HWND hPrevWnd = nullptr;
|
||||
std::vector<HWND> hwnds;
|
||||
wchar_t wTitle[512];
|
||||
os_utf8_to_wcs(title.c_str(), 0, wTitle, 512);
|
||||
hwnd = FindWindowEx(NULL, NULL, NULL, wTitle);
|
||||
return hwnd;
|
||||
while ((hwnd = FindWindowEx(NULL, hPrevWnd, NULL, wTitle)) != nullptr) {
|
||||
if (windowIsValid(hwnd)) {
|
||||
hwnds.emplace_back(hwnd);
|
||||
}
|
||||
hPrevWnd = hwnd;
|
||||
}
|
||||
return hwnds;
|
||||
}
|
||||
|
||||
std::string GetWindowClassByWindowTitle(const std::string &window)
|
||||
{
|
||||
HWND hwnd = NULL;
|
||||
hwnd = getHWNDfromTitle(window);
|
||||
if (!hwnd) {
|
||||
auto hwnds = getHWNDfromTitle(window);
|
||||
if (hwnds.empty()) {
|
||||
return "";
|
||||
}
|
||||
auto hwnd = hwnds.at(0);
|
||||
std::wstring wClass;
|
||||
wClass.resize(1024);
|
||||
if (!GetClassNameW(hwnd, &wClass[0], wClass.capacity())) {
|
||||
@@ -206,41 +227,54 @@ std::string GetWindowClassByWindowTitle(const std::string &window)
|
||||
|
||||
void SetFocusWindow(const std::string &title)
|
||||
{
|
||||
auto handle = getHWNDfromTitle(title);
|
||||
if (!handle) {
|
||||
auto hwnds = getHWNDfromTitle(title);
|
||||
if (hwnds.empty()) {
|
||||
return;
|
||||
}
|
||||
ShowWindow(handle, SW_RESTORE);
|
||||
SetForegroundWindow(handle);
|
||||
SetFocus(handle);
|
||||
SetActiveWindow(handle);
|
||||
|
||||
for (const auto &handle : hwnds) {
|
||||
const bool isMinimized = IsIconic(handle);
|
||||
ShowWindow(handle, isMinimized ? SW_RESTORE : SW_SHOW);
|
||||
SetForegroundWindow(handle);
|
||||
SetFocus(handle);
|
||||
SetActiveWindow(handle);
|
||||
}
|
||||
}
|
||||
|
||||
void CloseWindow(const std::string &title)
|
||||
{
|
||||
auto handle = getHWNDfromTitle(title);
|
||||
if (!handle) {
|
||||
auto hwnds = getHWNDfromTitle(title);
|
||||
if (hwnds.empty()) {
|
||||
return;
|
||||
}
|
||||
SendMessage(handle, WM_CLOSE, 0, 0);
|
||||
|
||||
for (const auto &handle : hwnds) {
|
||||
SendMessage(handle, WM_CLOSE, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void MaximizeWindow(const std::string &title)
|
||||
{
|
||||
auto handle = getHWNDfromTitle(title);
|
||||
if (!handle) {
|
||||
auto hwnds = getHWNDfromTitle(title);
|
||||
if (hwnds.empty()) {
|
||||
return;
|
||||
}
|
||||
ShowWindow(handle, SW_MAXIMIZE);
|
||||
|
||||
for (const auto &handle : hwnds) {
|
||||
ShowWindow(handle, SW_MAXIMIZE);
|
||||
}
|
||||
}
|
||||
|
||||
void MinimizeWindow(const std::string &title)
|
||||
{
|
||||
auto handle = getHWNDfromTitle(title);
|
||||
if (!handle) {
|
||||
auto hwnds = getHWNDfromTitle(title);
|
||||
if (hwnds.empty()) {
|
||||
return;
|
||||
}
|
||||
ShowWindow(handle, SW_MINIMIZE);
|
||||
|
||||
for (const auto &handle : hwnds) {
|
||||
ShowWindow(handle, SW_MINIMIZE);
|
||||
}
|
||||
}
|
||||
|
||||
class RawMouseInputFilter;
|
||||
|
||||
@@ -20,46 +20,56 @@ std::map<std::pair<MidiDeviceType, std::string>, MidiDeviceInstance *>
|
||||
static bool setupDeviceObservers()
|
||||
{
|
||||
static std::vector<libremidi::observer> observers;
|
||||
for (auto api : libremidi::available_apis()) {
|
||||
libremidi::observer_configuration cbs;
|
||||
cbs.input_added = [=](const libremidi::input_port &p) {
|
||||
auto dev = MidiDeviceInstance::GetDevice(p);
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
blog(LOG_INFO, "MIDI input connected: %s",
|
||||
p.port_name.c_str());
|
||||
dev->ClosePort();
|
||||
dev->OpenPort();
|
||||
};
|
||||
cbs.input_removed = [=](const libremidi::input_port &p) {
|
||||
auto dev = MidiDeviceInstance::GetDevice(p);
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
blog(LOG_INFO, "MIDI input removed: %s",
|
||||
p.port_name.c_str());
|
||||
};
|
||||
cbs.output_added = [=](const libremidi::output_port &p) {
|
||||
auto dev = MidiDeviceInstance::GetDevice(p);
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
blog(LOG_INFO, "MIDI output connected: %s",
|
||||
p.port_name.c_str());
|
||||
dev->ClosePort();
|
||||
dev->OpenPort();
|
||||
};
|
||||
cbs.output_removed = [=](const libremidi::output_port &p) {
|
||||
auto dev = MidiDeviceInstance::GetDevice(p);
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
blog(LOG_INFO, "MIDI output removed: %s",
|
||||
p.port_name.c_str());
|
||||
};
|
||||
observers.emplace_back(
|
||||
cbs, libremidi::observer_configuration_for(api));
|
||||
try {
|
||||
for (auto api : libremidi::available_apis()) {
|
||||
libremidi::observer_configuration cbs;
|
||||
cbs.input_added = [=](const libremidi::input_port &p) {
|
||||
auto dev = MidiDeviceInstance::GetDevice(p);
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
blog(LOG_INFO, "MIDI input connected: %s",
|
||||
p.port_name.c_str());
|
||||
dev->ClosePort();
|
||||
dev->OpenPort();
|
||||
};
|
||||
cbs.input_removed = [=](const libremidi::input_port &p) {
|
||||
auto dev = MidiDeviceInstance::GetDevice(p);
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
blog(LOG_INFO, "MIDI input removed: %s",
|
||||
p.port_name.c_str());
|
||||
};
|
||||
cbs.output_added = [=](const libremidi::output_port &p) {
|
||||
auto dev = MidiDeviceInstance::GetDevice(p);
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
blog(LOG_INFO, "MIDI output connected: %s",
|
||||
p.port_name.c_str());
|
||||
dev->ClosePort();
|
||||
dev->OpenPort();
|
||||
};
|
||||
cbs.output_removed =
|
||||
[=](const libremidi::output_port &p) {
|
||||
auto dev =
|
||||
MidiDeviceInstance::GetDevice(
|
||||
p);
|
||||
if (!dev) {
|
||||
return;
|
||||
}
|
||||
blog(LOG_INFO,
|
||||
"MIDI output removed: %s",
|
||||
p.port_name.c_str());
|
||||
};
|
||||
observers.emplace_back(
|
||||
cbs,
|
||||
libremidi::observer_configuration_for(api));
|
||||
}
|
||||
} catch (const libremidi::driver_error &error) {
|
||||
blog(LOG_WARNING, "Failed to setup midi device observers: %s",
|
||||
error.what());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -424,11 +434,16 @@ bool MidiDevice::SendMessge(const MidiMessage &m) const
|
||||
static std::optional<libremidi::output_port>
|
||||
getOutPortFromName(const std::string &name)
|
||||
{
|
||||
libremidi::observer obs;
|
||||
for (const libremidi::output_port &port : obs.get_output_ports()) {
|
||||
if (getNameFromPortInformation(port) == name) {
|
||||
return port;
|
||||
try {
|
||||
libremidi::observer obs;
|
||||
for (const libremidi::output_port &port :
|
||||
obs.get_output_ports()) {
|
||||
if (getNameFromPortInformation(port) == name) {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
} catch (const libremidi::driver_error &error) {
|
||||
blog(LOG_WARNING, "%s: %s", __func__, error.what());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
@@ -436,11 +451,16 @@ getOutPortFromName(const std::string &name)
|
||||
static std::optional<libremidi::input_port>
|
||||
getInPortFromName(const std::string &name)
|
||||
{
|
||||
libremidi::observer obs;
|
||||
for (const libremidi::input_port &port : obs.get_input_ports()) {
|
||||
if (getNameFromPortInformation(port) == name) {
|
||||
return port;
|
||||
try {
|
||||
libremidi::observer obs;
|
||||
for (const libremidi::input_port &port :
|
||||
obs.get_input_ports()) {
|
||||
if (getNameFromPortInformation(port) == name) {
|
||||
return port;
|
||||
}
|
||||
}
|
||||
} catch (const libremidi::driver_error &error) {
|
||||
blog(LOG_WARNING, "%s: %s", __func__, error.what());
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -29,6 +29,12 @@ static void setTabVisible(QTabWidget *tabWidget, bool visible)
|
||||
obs_module_text("AdvSceneSwitcher.twitchConnectionTab.title"));
|
||||
}
|
||||
|
||||
static bool twitchTabIsSelected(QTabWidget *tabWidget)
|
||||
{
|
||||
return tabWidget->tabText(tabWidget->currentIndex()) ==
|
||||
obs_module_text("AdvSceneSwitcher.twitchConnectionTab.title");
|
||||
}
|
||||
|
||||
TwitchConnectionsTable *TwitchConnectionsTable::Create()
|
||||
{
|
||||
tabWidget = new TwitchConnectionsTable();
|
||||
@@ -211,11 +217,11 @@ static void setupTab(QTabWidget *tab)
|
||||
}
|
||||
});
|
||||
|
||||
auto timer = new QTimer(tabWidget);
|
||||
timer->setInterval(1000);
|
||||
QWidget::connect(timer, &QTimer::timeout,
|
||||
[]() { updateConnectionStatus(tabWidget->Table()); });
|
||||
timer->start();
|
||||
QWidget::connect(tab, &QTabWidget::currentChanged, [tab]() {
|
||||
if (twitchTabIsSelected(tab)) {
|
||||
updateConnectionStatus(tabWidget->Table());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace advss
|
||||
|
||||
Reference in New Issue
Block a user